Fixed bug #736: Updates gets slower. The problem was that duplicate
[idzebra-moved-to-github.git] / index / zinfo.c
1 /* $Id: zinfo.c,v 1.74 2006-11-27 10:09:48 adam Exp $
2    Copyright (C) 1995-2006
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         zebraExplain_mergeAccessInfo(zei, node_tgtinfo,
413                                       &zei->accessInfo);
414
415         node_zebra = data1_search_tag(zei->dh, node_tgtinfo->child,
416                                        "zebraInfo");
417         np = 0;
418         if (node_zebra)
419         {
420             node_list = data1_search_tag(zei->dh, node_zebra->child,
421                                           "databaseList");
422             if (node_list)
423                 np = node_list->child;
424         }
425         for(; np; np = np->next)
426         {
427             data1_node *node_name = NULL;
428             data1_node *node_id = NULL;
429             data1_node *node_aid = NULL;
430             data1_node *np2;
431             if (np->which != DATA1N_tag || strcmp(np->u.tag.tag, "database"))
432                 continue;
433             for(np2 = np->child; np2; np2 = np2->next)
434             {
435                 if (np2->which != DATA1N_tag)
436                     continue;
437                 if (!strcmp(np2->u.tag.tag, "name"))
438                     node_name = np2->child;
439                 else if (!strcmp(np2->u.tag.tag, "id"))
440                     node_id = np2->child;
441                 else if (!strcmp(np2->u.tag.tag, "attributeDetailsId"))
442                     node_aid = np2->child;
443             }
444             assert(node_id && node_name && node_aid);
445             
446             *zdip =(struct zebDatabaseInfoB *) 
447                 nmem_malloc(zei->nmem, sizeof(**zdip));
448             (*zdip)->readFlag = 1;
449             (*zdip)->dirty = 0;
450             (*zdip)->data1_database = NULL;
451             (*zdip)->recordCount = 0;
452             (*zdip)->recordBytes = 0;
453             zebraExplain_mergeAccessInfo (zei, 0, &(*zdip)->accessInfo);
454
455             (*zdip)->databaseName = (char *)
456                 nmem_malloc (zei->nmem, 1+node_name->u.data.len);
457             memcpy((*zdip)->databaseName, node_name->u.data.data,
458                    node_name->u.data.len);
459             (*zdip)->databaseName[node_name->u.data.len] = '\0';
460             (*zdip)->sysno = atoi_zn (node_id->u.data.data,
461                                       node_id->u.data.len);
462             (*zdip)->attributeDetails = (zebAttributeDetails)
463                 nmem_malloc (zei->nmem, sizeof(*(*zdip)->attributeDetails));
464             (*zdip)->attributeDetails->sysno = atoi_zn (node_aid->u.data.data,
465                                                         node_aid->u.data.len);
466             (*zdip)->attributeDetails->readFlag = 1;
467             (*zdip)->attributeDetails->dirty = 0;
468             (*zdip)->attributeDetails->SUInfo = NULL;
469
470             zdip = &(*zdip)->next;
471         }
472         if (node_zebra)
473         {
474             np = data1_search_tag(zei->dh, node_zebra->child,
475                                   "ordinalSU");
476             np = np->child;
477             assert (np && np->which == DATA1N_data);
478             zei->ordinalSU = atoi_n(np->u.data.data, np->u.data.len);
479             
480             np = data1_search_tag(zei->dh, node_zebra->child,
481                                   "ordinalDatabase");
482             np = np->child;
483             assert (np && np->which == DATA1N_data);
484             zei->ordinalDatabase = atoi_n(np->u.data.data, np->u.data.len);
485
486             np = data1_search_tag(zei->dh, node_zebra->child,
487                                    "runNumber");
488             np = np->child;
489             assert (np && np->which == DATA1N_data);
490             zei->runNumber = atoi_zn(np->u.data.data, np->u.data.len);
491             yaz_log(YLOG_DEBUG, "read runnumber=" ZINT_FORMAT, zei->runNumber);
492             *zdip = NULL;
493         }
494         rec_free(&trec);
495     }
496     else  /* create initial targetInfo */
497     {
498         data1_node *node_tgtinfo;
499
500         *zdip = NULL;
501         if (writeFlag)
502         {
503             char *sgml_buf;
504             int sgml_len;
505
506             zei->data1_target =
507                 data1_read_sgml(zei->dh, zei->nmem,
508                                  "<explain><targetInfo>TargetInfo\n"
509                                  "<name>Zebra</>\n"
510                                  "<namedResultSets>1</>\n"
511                                  "<multipleDBSearch>1</>\n"
512                                  "<nicknames><name>Zebra</></>\n"
513                                  "</></>\n" );
514             if (!zei->data1_target)
515             {
516                 yaz_log(YLOG_FATAL, "Explain schema missing. Check profilePath");
517                 nmem_destroy(zei->nmem);
518                 return 0;
519             }
520             node_tgtinfo = data1_search_tag(zei->dh, zei->data1_target,
521                                              "/targetInfo");
522             assert(node_tgtinfo);
523
524             zebraExplain_initCommonInfo(zei, node_tgtinfo);
525             zebraExplain_initAccessInfo(zei, node_tgtinfo);
526
527             /* write now because we want to be sure about the sysno */
528             trec = rec_new(records);
529             if (!trec)
530             {
531                 yaz_log(YLOG_FATAL, "Cannot create root Explain record");
532                 nmem_destroy(zei->nmem);
533                 return 0;
534             }
535             trec->info[recInfo_fileType] =
536                 rec_strdup("grs.sgml", &trec->size[recInfo_fileType]);
537             trec->info[recInfo_databaseName] =
538                 rec_strdup("IR-Explain-1", &trec->size[recInfo_databaseName]);
539             
540             sgml_buf = data1_nodetoidsgml(dh, zei->data1_target, 0, &sgml_len);
541             trec->info[recInfo_storeData] = (char *) xmalloc(sgml_len);
542             memcpy(trec->info[recInfo_storeData], sgml_buf, sgml_len);
543             trec->size[recInfo_storeData] = sgml_len;
544                 
545             rec_put(records, &trec);
546             rec_free(&trec);
547         }
548         
549         zebraExplain_newDatabase(zei, "IR-Explain-1", 0);
550             
551         if (!zei->categoryList->dirty)
552         {
553             struct zebraCategoryListInfo *zcl = zei->categoryList;
554             data1_node *node_cl;
555             
556             zcl->dirty = 1;
557             zcl->data1_categoryList =
558                 data1_read_sgml(zei->dh, zei->nmem,
559                                  "<explain><categoryList>CategoryList\n"
560                                  "</></>\n");
561         
562             if (zcl->data1_categoryList)
563             {
564                 node_cl = data1_search_tag(zei->dh, zcl->data1_categoryList,
565                                             "/categoryList");
566                 assert(node_cl);
567                 zebraExplain_initCommonInfo(zei, node_cl);
568             }
569         }
570     }
571     return zei;
572 }
573
574 static void zebraExplain_readAttributeDetails(ZebraExplainInfo zei,
575                                                zebAttributeDetails zad)
576 {
577     Record rec;
578     struct zebSUInfoB **zsuip = &zad->SUInfo;
579     data1_node *node_adinfo, *node_zebra, *node_list, *np;
580
581     assert (zad->sysno);
582     rec = rec_get(zei->records, zad->sysno);
583
584     zad->data1_tree = read_sgml_rec(zei->dh, zei->nmem, rec);
585
586     node_adinfo = data1_search_tag(zei->dh, zad->data1_tree,
587                                     "/attributeDetails");
588     node_zebra = data1_search_tag(zei->dh, node_adinfo->child,
589                                  "zebraInfo");
590     node_list = data1_search_tag(zei->dh, node_zebra->child,
591                                   "attrlist");
592     for (np = node_list->child; np; np = np->next)
593     {
594         data1_node *node_str = NULL;
595         data1_node *node_ordinal = NULL;
596         data1_node *node_type = NULL;
597         data1_node *node_cat = NULL;
598         data1_node *node_doc_occurrences = NULL;
599         data1_node *node_term_occurrences = NULL;
600         data1_node *np2;
601
602         if (np->which != DATA1N_tag || strcmp(np->u.tag.tag, "attr"))
603             continue;
604         for (np2 = np->child; np2; np2 = np2->next)
605         {
606             if (np2->which != DATA1N_tag || !np2->child ||
607                 np2->child->which != DATA1N_data)
608                 continue;
609             if (!strcmp(np2->u.tag.tag, "str"))
610                 node_str = np2->child;
611             else if (!strcmp(np2->u.tag.tag, "ordinal"))
612                 node_ordinal = np2->child;
613             else if (!strcmp(np2->u.tag.tag, "type"))
614                 node_type = np2->child;
615             else if (!strcmp(np2->u.tag.tag, "cat"))
616                 node_cat = np2->child;
617             else if (!strcmp(np2->u.tag.tag, "dococcurrences"))
618                 node_doc_occurrences = np2->child;
619             else if (!strcmp(np2->u.tag.tag, "termoccurrences"))
620                 node_term_occurrences = np2->child;
621             else
622             {
623                 yaz_log(YLOG_LOG, "Unknown tag '%s' in attributeDetails",
624                         np2->u.tag.tag);
625             }
626         }
627         assert(node_ordinal);
628
629         *zsuip = (struct zebSUInfoB *)
630             nmem_malloc(zei->nmem, sizeof(**zsuip));
631
632         if (node_type && node_type->u.data.len > 0)
633             (*zsuip)->info.index_type =  node_type->u.data.data[0];
634         else
635         {
636             yaz_log(YLOG_WARN, "Missing attribute 'type' in attribute info");
637             (*zsuip)->info.index_type = 'w';
638         }
639         if (node_cat && node_cat->u.data.len > 0)
640         {
641             zinfo_index_category_t cat;
642
643             data1_node *np = node_cat;
644             if (!strncmp(np->u.data.data, "index", np->u.data.len))
645                 cat = zinfo_index_category_index;
646             else if (!strncmp(np->u.data.data, "sort", np->u.data.len))
647                 cat = zinfo_index_category_sort;
648             else if (!strncmp(np->u.data.data, "alwaysmatches", 
649                               np->u.data.len))
650                 cat = zinfo_index_category_alwaysmatches;
651             else if (!strncmp(np->u.data.data, "anchor", 
652                               np->u.data.len))
653                 cat = zinfo_index_category_anchor;
654             else
655             {
656                 yaz_log(YLOG_WARN, "Bad index cateogry '%.*s'",
657                         np->u.data.len, np->u.data.data);
658                 cat = zinfo_index_category_index;
659             }
660             (*zsuip)->info.cat = cat;
661         }
662         else
663             (*zsuip)->info.cat = zinfo_index_category_index;
664
665         if (node_doc_occurrences)
666         {
667             data1_node *np = node_doc_occurrences;
668             (*zsuip)->info.doc_occurrences = atoi_zn(np->u.data.data,
669                                                      np->u.data.len);
670         }
671         if (node_term_occurrences)
672         {
673             data1_node *np = node_term_occurrences;
674             (*zsuip)->info.term_occurrences = atoi_zn(np->u.data.data,
675                                                       np->u.data.len);
676         }
677         if (node_str)
678         {
679             (*zsuip)->info.which = ZEB_SU_STR;
680             
681             (*zsuip)->info.u.str = nmem_strdupn(zei->nmem,
682                                                 node_str->u.data.data,
683                                                 node_str->u.data.len);
684         }
685         else
686         {
687             yaz_log(YLOG_WARN, "Missing set/use/str in attribute info");
688             continue;
689         }
690         (*zsuip)->info.ordinal = atoi_n (node_ordinal->u.data.data,
691                                          node_ordinal->u.data.len);
692         zsuip = &(*zsuip)->next;
693     }
694     *zsuip = NULL;
695     zad->readFlag = 0;
696     rec_free(&rec);
697 }
698
699 static void zebraExplain_readDatabase (ZebraExplainInfo zei,
700                                        struct zebDatabaseInfoB *zdi)
701 {
702     Record rec;
703     data1_node *node_dbinfo, *node_zebra, *np;
704
705     assert (zdi->sysno);
706     rec = rec_get (zei->records, zdi->sysno);
707
708     zdi->data1_database = read_sgml_rec (zei->dh, zei->nmem, rec);
709     
710     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database,
711                                     "/databaseInfo");
712     assert (node_dbinfo);
713     zebraExplain_mergeAccessInfo (zei, node_dbinfo, &zdi->accessInfo);
714
715     node_zebra = data1_search_tag (zei->dh, node_dbinfo->child,
716                                  "zebraInfo");
717     if (node_zebra
718         && (np = data1_search_tag (zei->dh, node_zebra->child,
719                                    "recordBytes")) 
720         && np->child && np->child->which == DATA1N_data)
721         zdi->recordBytes = atoi_zn (np->child->u.data.data,
722                                     np->child->u.data.len);
723
724     if (node_zebra
725         && (np = data1_search_tag (zei->dh, node_zebra->child,
726                                    "ordinalDatabase")) 
727         && np->child && np->child->which == DATA1N_data)
728         zdi->ordinalDatabase = atoi_n(np->child->u.data.data,
729                                       np->child->u.data.len);
730
731     if ((np = data1_search_tag (zei->dh, node_dbinfo->child,
732                                 "recordCount")) &&
733         (np = data1_search_tag (zei->dh, np->child,
734                                 "recordCountActual")) &&
735         np->child->which == DATA1N_data)
736     {
737         zdi->recordCount = atoi_zn (np->child->u.data.data,
738                                     np->child->u.data.len);
739     }
740     zdi->readFlag = 0;
741     rec_free(&rec);
742 }
743
744 int zebraExplain_removeDatabase(ZebraExplainInfo zei, void *update_handle)
745 {
746     struct zebDatabaseInfoB **zdip = &zei->databaseInfo;
747
748     while (*zdip)
749     {
750         if (*zdip == zei->curDatabaseInfo)
751         {
752             struct zebDatabaseInfoB *zdi = *zdip;
753             Record rec;
754
755             zei->dirty = 1;
756             zei->updateHandle = update_handle;
757
758             if (zdi->attributeDetails)
759             {
760                 /* remove attribute details keys and delete it */
761                 zebAttributeDetails zad = zdi->attributeDetails;
762                 
763                 rec = rec_get(zei->records, zad->sysno);
764                 (*zei->updateFunc)(zei->updateHandle, rec, 0);
765                 rec_free(&rec);
766             }
767             /* remove database record keys and delete it */
768             rec = rec_get (zei->records, zdi->sysno);
769             (*zei->updateFunc)(zei->updateHandle, rec, 0);
770             rec_free(&rec);
771
772             /* remove from list */
773             *zdip = zdi->next;
774
775             /* current database is IR-Explain-1 */
776             return 0;
777         }
778         zdip = &(*zdip)->next;
779     }
780     return -1;
781 }
782
783 int zebraExplain_curDatabase (ZebraExplainInfo zei, const char *database)
784 {
785     struct zebDatabaseInfoB *zdi;
786     const char *database_n = strrchr (database, '/');
787
788     if (database_n)
789         database_n++;
790     else
791         database_n = database;
792     
793     assert (zei);
794     if (zei->curDatabaseInfo &&
795         !STRCASECMP (zei->curDatabaseInfo->databaseName, database))
796         return 0;
797     for (zdi = zei->databaseInfo; zdi; zdi=zdi->next)
798     {
799         if (!STRCASECMP (zdi->databaseName, database_n))
800             break;
801     }
802     if (!zdi)
803         return -1;
804 #if ZINFO_DEBUG
805     yaz_log(YLOG_LOG, "zebraExplain_curDatabase: %s", database);
806 #endif
807     if (zdi->readFlag)
808     {
809 #if ZINFO_DEBUG
810         yaz_log(YLOG_LOG, "zebraExplain_readDatabase: %s", database);
811 #endif
812         zebraExplain_readDatabase (zei, zdi);
813     }
814     if (zdi->attributeDetails->readFlag)
815     {
816 #if ZINFO_DEBUG
817         yaz_log(YLOG_LOG, "zebraExplain_readAttributeDetails: %s", database);
818 #endif
819         zebraExplain_readAttributeDetails (zei, zdi->attributeDetails);
820     }
821     zei->curDatabaseInfo = zdi;
822     return 0;
823 }
824
825 static void zebraExplain_initCommonInfo (ZebraExplainInfo zei, data1_node *n)
826 {
827     data1_node *c = data1_mk_tag (zei->dh, zei->nmem, "commonInfo", 0, n);
828     data1_mk_tag_data_text (zei->dh, c, "dateAdded", zei->date, zei->nmem);
829     data1_mk_tag_data_text (zei->dh, c, "dateChanged", zei->date, zei->nmem);
830     data1_mk_tag_data_text (zei->dh, c, "languageCode", "EN", zei->nmem);
831 }
832
833 static void zebraExplain_updateCommonInfo (ZebraExplainInfo zei, data1_node *n)
834 {
835     data1_node *c = data1_search_tag (zei->dh, n->child, "commonInfo");
836     assert (c);
837     data1_mk_tag_data_text_uni (zei->dh, c, "dateChanged", zei->date,
838                                 zei->nmem);
839 }
840
841 static void zebraExplain_initAccessInfo (ZebraExplainInfo zei, data1_node *n)
842 {
843     data1_node *c = data1_mk_tag (zei->dh, zei->nmem, "accessInfo", 0, n);
844     data1_node *d = data1_mk_tag (zei->dh, zei->nmem, "unitSystems", 0, c);
845     data1_mk_tag_data_text (zei->dh, d, "string", "ISO", zei->nmem);
846 }
847
848 static void zebraExplain_updateAccessInfo (ZebraExplainInfo zei, data1_node *n,
849                                            zebAccessInfo accessInfo)
850 {
851     data1_node *c = data1_search_tag (zei->dh, n->child, "accessInfo");
852     data1_node *d;
853     zebAccessObject p;
854     
855     if (!c)
856     {
857         data1_pr_tree (zei->dh, n, stdout);
858         zebra_exit("zebraExplain_updateAccessInfo");
859     }
860
861     if ((p = accessInfo->attributeSetIds))
862     {
863         d = data1_mk_tag_uni (zei->dh, zei->nmem, "attributeSetIds", c);
864         for (; p; p = p->next)
865             data1_mk_tag_data_oid (zei->dh, d, "oid", p->oid, zei->nmem);
866     }
867     if ((p = accessInfo->schemas))
868     {
869         d = data1_mk_tag_uni (zei->dh, zei->nmem, "schemas", c);
870         for (; p; p = p->next)
871             data1_mk_tag_data_oid (zei->dh, d, "oid", p->oid, zei->nmem);
872     }
873 }
874
875 int zebraExplain_newDatabase (ZebraExplainInfo zei, const char *database,
876                               int explain_database)
877 {
878     struct zebDatabaseInfoB *zdi;
879     data1_node *node_dbinfo, *node_adinfo;
880     const char *database_n = strrchr (database, '/');
881
882     if (database_n)
883         database_n++;
884     else
885         database_n = database;
886
887 #if ZINFO_DEBUG
888     yaz_log(YLOG_LOG, "zebraExplain_newDatabase: %s", database);
889 #endif
890     assert (zei);
891     for (zdi = zei->databaseInfo; zdi; zdi=zdi->next)
892     {
893         if (!STRCASECMP (zdi->databaseName, database_n))
894             break;
895     }
896     if (zdi)
897         return -1;
898     /* it's new really. make it */
899     zdi = (struct zebDatabaseInfoB *) nmem_malloc (zei->nmem, sizeof(*zdi));
900     zdi->next = zei->databaseInfo;
901     zei->databaseInfo = zdi;
902     zdi->sysno = 0;
903     zdi->recordCount = 0;
904     zdi->recordBytes = 0;
905     zdi->readFlag = 0;
906     zdi->databaseName = nmem_strdup (zei->nmem, database_n);
907
908     zdi->ordinalDatabase = zei->ordinalDatabase++;
909
910     zebraExplain_mergeAccessInfo (zei, 0, &zdi->accessInfo);
911     
912     assert (zei->dh);
913     assert (zei->nmem);
914
915     zdi->data1_database =
916         data1_read_sgml (zei->dh, zei->nmem, 
917                          "<explain><databaseInfo>DatabaseInfo\n"
918                          "</></>\n");
919     if (!zdi->data1_database)
920         return -2;
921
922     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database,
923                                     "/databaseInfo");
924     assert (node_dbinfo);
925
926     zebraExplain_initCommonInfo (zei, node_dbinfo);
927     zebraExplain_initAccessInfo (zei, node_dbinfo);
928
929     data1_mk_tag_data_text (zei->dh, node_dbinfo, "name",
930                                database, zei->nmem);
931     
932     if (explain_database)
933         data1_mk_tag_data_text (zei->dh, node_dbinfo, "explainDatabase",
934                                 "", zei->nmem);
935     
936     data1_mk_tag_data_text (zei->dh, node_dbinfo, "userFee",
937                             "0", zei->nmem);
938     
939     data1_mk_tag_data_text (zei->dh, node_dbinfo, "available",
940                             "1", zei->nmem);
941     
942 #if ZINFO_DEBUG
943     data1_pr_tree (zei->dh, zdi->data1_database, stderr);
944 #endif
945     zdi->dirty = 1;
946     zei->dirty = 1;
947     zei->curDatabaseInfo = zdi;
948
949     zdi->attributeDetails = (zebAttributeDetails)
950         nmem_malloc (zei->nmem, sizeof(*zdi->attributeDetails));
951     zdi->attributeDetails->readFlag = 0;
952     zdi->attributeDetails->sysno = 0;
953     zdi->attributeDetails->dirty = 1;
954     zdi->attributeDetails->SUInfo = NULL;
955     zdi->attributeDetails->data1_tree =
956         data1_read_sgml (zei->dh, zei->nmem,
957                          "<explain><attributeDetails>AttributeDetails\n"
958                          "</></>\n");
959
960     node_adinfo = data1_search_tag (zei->dh, zdi->attributeDetails->data1_tree,
961                                     "/attributeDetails");
962     assert (node_adinfo);
963
964     zebraExplain_initCommonInfo (zei, node_adinfo);
965
966     data1_mk_tag_data_text(zei->dh, node_adinfo, "name", database, zei->nmem);
967
968     return 0;
969 }
970
971
972 static void zebraExplain_writeCategoryList (ZebraExplainInfo zei,
973                                             struct zebraCategoryListInfo *zcl,
974                                             int key_flush)
975 {
976     char *sgml_buf;
977     int sgml_len;
978     int i;
979     Record drec;
980     data1_node *node_ci, *node_categoryList;
981     zint sysno = 0;
982     static char *category[] = {
983         "CategoryList",
984         "TargetInfo",
985         "DatabaseInfo",
986         "AttributeDetails",
987         NULL
988     };
989
990     assert (zcl);
991     if (!zcl->dirty)
992         return ;
993     zcl->dirty = 1;
994     node_categoryList = zcl->data1_categoryList;
995
996 #if ZINFO_DEBUG
997     yaz_log(YLOG_LOG, "zebraExplain_writeCategoryList");
998 #endif
999
1000     drec = createRecord (zei->records, &sysno);
1001     if (!drec)
1002         return;
1003     
1004     node_ci = data1_search_tag (zei->dh, node_categoryList,
1005                                 "/categoryList");
1006     assert (node_ci);
1007     node_ci = data1_mk_tag (zei->dh, zei->nmem, "categories", 0 /* attr */,
1008                             node_ci);
1009     assert (node_ci);
1010     
1011     for (i = 0; category[i]; i++)
1012     {
1013         data1_node *node_cat = data1_mk_tag (zei->dh, zei->nmem,  "category",
1014                                              0 /* attr */, node_ci);
1015
1016         data1_mk_tag_data_text (zei->dh, node_cat, "name",
1017                                 category[i], zei->nmem);
1018     }
1019     /* extract *searchable* keys from it. We do this here, because
1020        record count, etc. is affected */
1021     if (key_flush)
1022         (*zei->updateFunc)(zei->updateHandle, drec, node_categoryList);
1023
1024     /* convert to "SGML" and write it */
1025 #if ZINFO_DEBUG
1026     data1_pr_tree (zei->dh, node_categoryList, stderr);
1027 #endif
1028     sgml_buf = data1_nodetoidsgml(zei->dh, node_categoryList, 0, &sgml_len);
1029     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1030     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1031     drec->size[recInfo_storeData] = sgml_len;
1032     
1033     rec_put (zei->records, &drec);
1034 }
1035
1036 static void zebraExplain_writeAttributeDetails (ZebraExplainInfo zei,
1037                                                 zebAttributeDetails zad,
1038                                                 const char *databaseName,
1039                                                 int key_flush)
1040 {
1041     char *sgml_buf;
1042     int sgml_len;
1043     Record drec;
1044     data1_node *node_adinfo, *node_list, *node_zebra;
1045     struct zebSUInfoB *zsui;
1046     
1047     if (!zad->dirty)
1048         return;
1049     
1050     zad->dirty = 0;
1051 #if ZINFO_DEBUG
1052     yaz_log(YLOG_LOG, "zebraExplain_writeAttributeDetails");    
1053     data1_pr_tree(zei->dh, zad->data1_tree, stderr);
1054 #endif
1055
1056     drec = createRecord (zei->records, &zad->sysno);
1057     if (!drec)
1058         return;
1059     assert (zad->data1_tree);
1060
1061     node_adinfo = data1_search_tag (zei->dh, zad->data1_tree,
1062                                    "/attributeDetails");
1063     zebraExplain_updateCommonInfo (zei, node_adinfo);
1064
1065     /* extract *searchable* keys from it. We do this here, because
1066        record count, etc. is affected */
1067     if (key_flush)
1068         (*zei->updateFunc)(zei->updateHandle, drec, zad->data1_tree);
1069     /* zebra info (private) */
1070     node_zebra = data1_mk_tag_uni (zei->dh, zei->nmem,
1071                                  "zebraInfo", node_adinfo);
1072     node_list = data1_mk_tag_uni (zei->dh, zei->nmem,
1073                                  "attrlist", node_zebra);
1074     for (zsui = zad->SUInfo; zsui; zsui = zsui->next)
1075     {
1076         data1_node *node_attr;
1077         char index_type_str[2];
1078         
1079         node_attr = data1_mk_tag (zei->dh, zei->nmem, "attr", 0 /* attr */,
1080                                   node_list);
1081
1082         index_type_str[0] = zsui->info.index_type;
1083         index_type_str[1] = '\0';
1084         data1_mk_tag_data_text (zei->dh, node_attr, "type",
1085                                 index_type_str, zei->nmem);
1086         if (zsui->info.which == ZEB_SU_STR)
1087         {
1088             data1_mk_tag_data_text (zei->dh, node_attr, "str",
1089                                     zsui->info.u.str, zei->nmem);
1090         }
1091         data1_mk_tag_data_int (zei->dh, node_attr, "ordinal",
1092                                zsui->info.ordinal, zei->nmem);
1093
1094         data1_mk_tag_data_zint (zei->dh, node_attr, "dococcurrences",
1095                                 zsui->info.doc_occurrences, zei->nmem);
1096         data1_mk_tag_data_zint (zei->dh, node_attr, "termoccurrences",
1097                                 zsui->info.term_occurrences, zei->nmem);
1098         switch(zsui->info.cat)
1099         {
1100         case zinfo_index_category_index:
1101             data1_mk_tag_data_text (zei->dh, node_attr, "cat",
1102                                     "index", zei->nmem); break;
1103         case zinfo_index_category_sort:
1104             data1_mk_tag_data_text (zei->dh, node_attr, "cat",
1105                                     "sort", zei->nmem); break;
1106         case zinfo_index_category_alwaysmatches:
1107             data1_mk_tag_data_text (zei->dh, node_attr, "cat",
1108                                     "alwaysmatches", zei->nmem); break;
1109         case zinfo_index_category_anchor:
1110             data1_mk_tag_data_text (zei->dh, node_attr, "cat",
1111                                     "anchor", zei->nmem); break;
1112         }
1113     }
1114     /* convert to "SGML" and write it */
1115 #if ZINFO_DEBUG
1116     data1_pr_tree (zei->dh, zad->data1_tree, stderr);
1117 #endif
1118     sgml_buf = data1_nodetoidsgml(zei->dh, zad->data1_tree,
1119                                   0, &sgml_len);
1120     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1121     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1122     drec->size[recInfo_storeData] = sgml_len;
1123     
1124     rec_put (zei->records, &drec);
1125 }
1126
1127 static void zebraExplain_writeDatabase (ZebraExplainInfo zei,
1128                                         struct zebDatabaseInfoB *zdi,
1129                                         int key_flush)
1130 {
1131     char *sgml_buf;
1132     int sgml_len;
1133     Record drec;
1134     data1_node *node_dbinfo, *node_count, *node_zebra;
1135     
1136     if (!zdi->dirty)
1137         return;
1138
1139     zdi->dirty = 0;
1140 #if ZINFO_DEBUG
1141     yaz_log(YLOG_LOG, "zebraExplain_writeDatabase %s", zdi->databaseName);
1142 #endif
1143     drec = createRecord (zei->records, &zdi->sysno);
1144     if (!drec)
1145         return;
1146     assert (zdi->data1_database);
1147
1148     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database,
1149                                     "/databaseInfo");
1150
1151     assert (node_dbinfo);
1152     zebraExplain_updateCommonInfo (zei, node_dbinfo);
1153     zebraExplain_updateAccessInfo (zei, node_dbinfo, zdi->accessInfo);
1154
1155     /* extract *searchable* keys from it. We do this here, because
1156        record count, etc. is affected */
1157     if (key_flush)
1158         (*zei->updateFunc)(zei->updateHandle, drec, zdi->data1_database);
1159     /* record count */
1160     node_count = data1_mk_tag_uni (zei->dh, zei->nmem,
1161                                  "recordCount", node_dbinfo);
1162     data1_mk_tag_data_zint (zei->dh, node_count, "recordCountActual",
1163                             zdi->recordCount, zei->nmem);
1164
1165     /* zebra info (private) */
1166     node_zebra = data1_mk_tag_uni (zei->dh, zei->nmem,
1167                                  "zebraInfo", node_dbinfo);
1168     data1_mk_tag_data_zint (zei->dh, node_zebra,
1169                            "recordBytes", zdi->recordBytes, zei->nmem);
1170
1171     data1_mk_tag_data_zint(zei->dh, node_zebra,
1172                            "ordinalDatabase", zdi->ordinalDatabase, zei->nmem);
1173
1174     /* convert to "SGML" and write it */
1175 #if ZINFO_DEBUG
1176     data1_pr_tree (zei->dh, zdi->data1_database, stderr);
1177 #endif
1178     sgml_buf = data1_nodetoidsgml(zei->dh, zdi->data1_database,
1179                                   0, &sgml_len);
1180     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1181     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1182     drec->size[recInfo_storeData] = sgml_len;
1183     
1184     rec_put (zei->records, &drec);
1185 }
1186
1187 static void writeAttributeValues (ZebraExplainInfo zei,
1188                                   data1_node *node_values,
1189                                   data1_attset *attset)
1190 {
1191     data1_att *atts;
1192     data1_attset_child *c;
1193
1194     if (!attset)
1195         return;
1196
1197     for (c = attset->children; c; c = c->next)
1198         writeAttributeValues (zei, node_values, c->child);
1199     for (atts = attset->atts; atts; atts = atts->next)
1200     {
1201         data1_node *node_value;
1202         
1203         node_value = data1_mk_tag (zei->dh, zei->nmem, "attributeValue",
1204                                    0 /* attr */, node_values);
1205         data1_mk_tag_data_text (zei->dh, node_value, "name",
1206                                 atts->name, zei->nmem);
1207         node_value = data1_mk_tag (zei->dh, zei->nmem, "value",
1208                                    0 /* attr */, node_value);
1209         data1_mk_tag_data_int (zei->dh, node_value, "numeric",
1210                                atts->value, zei->nmem);
1211     }
1212 }
1213
1214
1215 static void zebraExplain_writeAttributeSet (ZebraExplainInfo zei,
1216                                             zebAccessObject o,
1217                                             int key_flush)
1218 {
1219     char *sgml_buf;
1220     int sgml_len;
1221     Record drec;
1222     data1_node *node_root, *node_attinfo, *node_attributes, *node_atttype;
1223     data1_node *node_values;
1224     struct oident *entp;
1225     struct data1_attset *attset = NULL;
1226     
1227     if ((entp = oid_getentbyoid (o->oid)))
1228         attset = data1_attset_search_id (zei->dh, entp->value);
1229             
1230 #if ZINFO_DEBUG
1231     yaz_log(YLOG_LOG, "zebraExplain_writeAttributeSet %s",
1232           attset ? attset->name : "<unknown>");    
1233 #endif
1234
1235     drec = createRecord (zei->records, &o->sysno);
1236     if (!drec)
1237         return;
1238     node_root =
1239         data1_read_sgml (zei->dh, zei->nmem,
1240                          "<explain><attributeSetInfo>AttributeSetInfo\n"
1241                          "</></>\n" );
1242
1243     node_attinfo = data1_search_tag (zei->dh, node_root,
1244                                    "/attributeSetInfo");
1245
1246     assert (node_attinfo);
1247     zebraExplain_initCommonInfo (zei, node_attinfo);
1248     zebraExplain_updateCommonInfo (zei, node_attinfo);
1249
1250     data1_mk_tag_data_oid (zei->dh, node_attinfo,
1251                             "oid", o->oid, zei->nmem);
1252     if (attset && attset->name)
1253         data1_mk_tag_data_text (zei->dh, node_attinfo,
1254                                 "name", attset->name, zei->nmem);
1255     
1256     node_attributes = data1_mk_tag_uni (zei->dh, zei->nmem,
1257                                       "attributes", node_attinfo);
1258     node_atttype = data1_mk_tag_uni (zei->dh, zei->nmem,
1259                                    "attributeType", node_attributes);
1260     data1_mk_tag_data_text (zei->dh, node_atttype,
1261                             "name", "Use", zei->nmem);
1262     data1_mk_tag_data_text (zei->dh, node_atttype,
1263                             "description", "Use Attribute", zei->nmem);
1264     data1_mk_tag_data_int (zei->dh, node_atttype,
1265                            "type", 1, zei->nmem);
1266     node_values = data1_mk_tag (zei->dh, zei->nmem,
1267                                 "attributeValues", 0 /* attr */, node_atttype);
1268     if (attset)
1269         writeAttributeValues (zei, node_values, attset);
1270
1271     /* extract *searchable* keys from it. We do this here, because
1272        record count, etc. is affected */
1273     if (key_flush)
1274         (*zei->updateFunc)(zei->updateHandle, drec, node_root);
1275     /* convert to "SGML" and write it */
1276 #if ZINFO_DEBUG
1277     data1_pr_tree (zei->dh, node_root, stderr);
1278 #endif
1279     sgml_buf = data1_nodetoidsgml(zei->dh, node_root, 0, &sgml_len);
1280     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1281     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1282     drec->size[recInfo_storeData] = sgml_len;
1283     
1284     rec_put (zei->records, &drec);
1285 }
1286
1287 static void zebraExplain_writeTarget (ZebraExplainInfo zei, int key_flush)
1288 {
1289     struct zebDatabaseInfoB *zdi;
1290     data1_node *node_tgtinfo, *node_list, *node_zebra;
1291     Record trec;
1292     int sgml_len;
1293     char *sgml_buf;
1294
1295     if (!zei->dirty)
1296         return;
1297     zei->dirty = 0;
1298
1299     trec = rec_get_root(zei->records);
1300     xfree (trec->info[recInfo_storeData]);
1301
1302     node_tgtinfo = data1_search_tag (zei->dh, zei->data1_target,
1303                                      "/targetInfo");
1304     assert (node_tgtinfo);
1305
1306     zebraExplain_updateCommonInfo (zei, node_tgtinfo);
1307     zebraExplain_updateAccessInfo (zei, node_tgtinfo, zei->accessInfo);
1308
1309     /* convert to "SGML" and write it */
1310     if (key_flush)
1311         (*zei->updateFunc)(zei->updateHandle, trec, zei->data1_target);
1312
1313     node_zebra = data1_mk_tag_uni (zei->dh, zei->nmem,
1314                                  "zebraInfo", node_tgtinfo);
1315     data1_mk_tag_data_text (zei->dh, node_zebra, "version",
1316                                ZEBRAVER, zei->nmem);
1317     node_list = data1_mk_tag (zei->dh, zei->nmem,
1318                               "databaseList", 0 /* attr */, node_zebra);
1319     for (zdi = zei->databaseInfo; zdi; zdi = zdi->next)
1320     {
1321         data1_node *node_db;
1322         node_db = data1_mk_tag (zei->dh, zei->nmem,
1323                                 "database", 0 /* attr */, node_list);
1324         data1_mk_tag_data_text (zei->dh, node_db, "name",
1325                                 zdi->databaseName, zei->nmem);
1326         data1_mk_tag_data_zint (zei->dh, node_db, "id",
1327                                 zdi->sysno, zei->nmem);
1328         data1_mk_tag_data_zint (zei->dh, node_db, "attributeDetailsId",
1329                                 zdi->attributeDetails->sysno, zei->nmem);
1330     }
1331     data1_mk_tag_data_int (zei->dh, node_zebra, "ordinalSU",
1332                            zei->ordinalSU, zei->nmem);
1333
1334     data1_mk_tag_data_int (zei->dh, node_zebra, "ordinalDatabase",
1335                            zei->ordinalDatabase, zei->nmem);
1336
1337     data1_mk_tag_data_zint (zei->dh, node_zebra, "runNumber",
1338                             zei->runNumber, zei->nmem);
1339
1340 #if ZINFO_DEBUG
1341     data1_pr_tree (zei->dh, zei->data1_target, stderr);
1342 #endif
1343     sgml_buf = data1_nodetoidsgml(zei->dh, zei->data1_target,
1344                                   0, &sgml_len);
1345     trec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1346     memcpy (trec->info[recInfo_storeData], sgml_buf, sgml_len);
1347     trec->size[recInfo_storeData] = sgml_len;
1348     
1349     rec_put (zei->records, &trec);
1350 }
1351
1352 int zebraExplain_lookup_attr_str(ZebraExplainInfo zei, 
1353                                  zinfo_index_category_t cat,
1354                                  int index_type,
1355                                  const char *str)
1356 {
1357     struct zebSUInfoB **zsui;
1358
1359     assert (zei->curDatabaseInfo);
1360     for (zsui = &zei->curDatabaseInfo->attributeDetails->SUInfo;
1361          *zsui; zsui = &(*zsui)->next)
1362         if ( (index_type == -1 || (*zsui)->info.index_type == index_type)
1363              && (*zsui)->info.cat == cat
1364              && (*zsui)->info.which == ZEB_SU_STR 
1365              && !yaz_matchstr((*zsui)->info.u.str, str))
1366         {
1367             struct zebSUInfoB *zsui_this = *zsui;
1368
1369             /* take it out of the list and move to front */
1370             *zsui = (*zsui)->next;
1371             zsui_this->next = zei->curDatabaseInfo->attributeDetails->SUInfo;
1372             zei->curDatabaseInfo->attributeDetails->SUInfo = zsui_this;
1373
1374             return zsui_this->info.ordinal;
1375         }
1376     return -1;
1377 }
1378
1379 int zebraExplain_trav_ord(ZebraExplainInfo zei, void *handle,
1380                           int (*f)(void *handle, int ord))
1381 {
1382     struct zebDatabaseInfoB *zdb = zei->curDatabaseInfo;
1383     if (zdb)
1384     {
1385         struct zebSUInfoB *zsui = zdb->attributeDetails->SUInfo;
1386         for ( ;zsui; zsui = zsui->next)
1387             (*f)(handle,  zsui->info.ordinal);
1388     }
1389     return 0;
1390 }
1391
1392
1393 struct zebSUInfoB *zebraExplain_get_sui_info (ZebraExplainInfo zei, int ord,
1394                                               int dirty_mark,
1395                                               const char **db)
1396 {
1397     struct zebDatabaseInfoB *zdb;
1398
1399     for (zdb = zei->databaseInfo; zdb; zdb = zdb->next)
1400     {
1401         struct zebSUInfoB **zsui;
1402
1403         if (zdb->attributeDetails->readFlag)
1404             zebraExplain_readAttributeDetails (zei, zdb->attributeDetails);
1405
1406         for (zsui = &zdb->attributeDetails->SUInfo; *zsui;
1407              zsui = &(*zsui)->next)
1408             if ((*zsui)->info.ordinal == ord)
1409             {
1410                 struct zebSUInfoB *zsui_this = *zsui;
1411                 
1412                 /* take it out of the list and move to front */
1413                 *zsui = (*zsui)->next;
1414                 zsui_this->next = zdb->attributeDetails->SUInfo;
1415                 zdb->attributeDetails->SUInfo = zsui_this;
1416
1417                 if (dirty_mark)
1418                     zdb->attributeDetails->dirty = 1;
1419                 if (db)
1420                     *db = zdb->databaseName;
1421                 return zsui_this;
1422             }
1423     }
1424     return 0;
1425 }
1426
1427
1428
1429 int zebraExplain_ord_adjust_occurrences(ZebraExplainInfo zei, int ord,
1430                                         int term_delta, int doc_delta)
1431 {
1432     struct zebSUInfoB *zsui = zebraExplain_get_sui_info(zei, ord, 1, 0);
1433     if (zsui)
1434     {
1435         zsui->info.term_occurrences += term_delta;
1436         zsui->info.doc_occurrences += doc_delta;
1437         return 0;
1438     }
1439     return -1;
1440 }
1441
1442 int zebraExplain_ord_get_occurrences(ZebraExplainInfo zei, int ord,
1443                                      zint *term_occurrences,
1444                                      zint *doc_occurrences)
1445 {
1446     struct zebSUInfoB *zsui = zebraExplain_get_sui_info(zei, ord, 0, 0);
1447     if (zsui)
1448     {
1449         *term_occurrences = zsui->info.term_occurrences;
1450         *doc_occurrences = zsui->info.doc_occurrences;
1451         return 0;
1452     }
1453     return -1;
1454 }
1455
1456 zint zebraExplain_ord_get_doc_occurrences(ZebraExplainInfo zei, int ord)
1457 {
1458     struct zebSUInfoB *zsui = zebraExplain_get_sui_info(zei, ord, 0, 0);
1459     if (zsui)
1460         return zsui->info.doc_occurrences;
1461     return 0;
1462 }
1463
1464 zint zebraExplain_ord_get_term_occurrences(ZebraExplainInfo zei, int ord)
1465 {
1466     struct zebSUInfoB *zsui = zebraExplain_get_sui_info(zei, ord, 0, 0);
1467     if (zsui)
1468         return zsui->info.term_occurrences;
1469     return 0;
1470 }
1471
1472 int zebraExplain_lookup_ord(ZebraExplainInfo zei, int ord,
1473                             int *index_type, 
1474                             const char **db,
1475                             const char **string_index)
1476 {
1477     struct zebSUInfoB *zsui;
1478
1479     if (index_type)
1480         *index_type = 0;
1481     if (string_index)
1482         *string_index = 0;
1483
1484     zsui = zebraExplain_get_sui_info(zei, ord, 0, db);
1485     if (zsui)
1486     {
1487         if (zsui->info.which == ZEB_SU_STR)
1488             if (string_index)
1489                 *string_index = zsui->info.u.str;
1490         if (index_type)
1491             *index_type = zsui->info.index_type;
1492         return 0;
1493     }
1494     return -1;
1495 }
1496
1497
1498
1499 zebAccessObject zebraExplain_announceOid (ZebraExplainInfo zei,
1500                                           zebAccessObject *op,
1501                                           Odr_oid *oid)
1502 {
1503     zebAccessObject ao;
1504     
1505     for (ao = *op; ao; ao = ao->next)
1506         if (!oid_oidcmp (oid, ao->oid))
1507             break;
1508     if (!ao)
1509     {
1510         ao = (zebAccessObject) nmem_malloc (zei->nmem, sizeof(*ao));
1511         ao->handle = NULL;
1512         ao->sysno = 0;
1513         ao->oid = odr_oiddup_nmem (zei->nmem, oid);
1514         ao->next = *op;
1515         *op = ao;
1516     }
1517     return ao;
1518 }
1519
1520 void zebraExplain_addAttributeSet (ZebraExplainInfo zei, int set)
1521 {
1522     oident oe;
1523     int oid[OID_SIZE];
1524
1525     oe.proto = PROTO_Z3950;
1526     oe.oclass = CLASS_ATTSET;
1527     oe.value = (enum oid_value) set;
1528
1529     if (oid_ent_to_oid (&oe, oid))
1530     {
1531         zebraExplain_announceOid (zei, &zei->accessInfo->attributeSetIds, oid);
1532         zebraExplain_announceOid (zei, &zei->curDatabaseInfo->
1533                                   accessInfo->attributeSetIds, oid);
1534     }
1535 }
1536
1537 struct zebSUInfoB *zebraExplain_add_sui_info(ZebraExplainInfo zei,
1538                                              zinfo_index_category_t cat,
1539                                              int index_type)
1540 {
1541     struct zebSUInfoB *zsui;
1542
1543     assert (zei->curDatabaseInfo);
1544     zsui = (struct zebSUInfoB *) nmem_malloc (zei->nmem, sizeof(*zsui));
1545     zsui->next = zei->curDatabaseInfo->attributeDetails->SUInfo;
1546     zei->curDatabaseInfo->attributeDetails->SUInfo = zsui;
1547     zei->curDatabaseInfo->attributeDetails->dirty = 1;
1548     zei->dirty = 1;
1549     zsui->info.index_type = index_type;
1550     zsui->info.cat = cat;
1551     zsui->info.doc_occurrences = 0;
1552     zsui->info.term_occurrences = 0;
1553     zsui->info.ordinal = (zei->ordinalSU)++;
1554     return zsui;
1555 }
1556
1557 int zebraExplain_add_attr_str(ZebraExplainInfo zei, 
1558                               zinfo_index_category_t cat,
1559                               int index_type,
1560                               const char *index_name)
1561 {
1562     struct zebSUInfoB *zsui = zebraExplain_add_sui_info(zei, cat, index_type);
1563
1564     zsui->info.which = ZEB_SU_STR;
1565     zsui->info.u.str = nmem_strdup(zei->nmem, index_name);
1566     return zsui->info.ordinal;
1567 }
1568
1569 void zebraExplain_addSchema (ZebraExplainInfo zei, Odr_oid *oid)
1570 {
1571     zebraExplain_announceOid (zei, &zei->accessInfo->schemas, oid);
1572     zebraExplain_announceOid (zei, &zei->curDatabaseInfo->
1573                               accessInfo->schemas, oid);
1574 }
1575
1576 void zebraExplain_recordBytesIncrement (ZebraExplainInfo zei, int adjust_num)
1577 {
1578     assert (zei->curDatabaseInfo);
1579
1580     if (adjust_num)
1581     {
1582         zei->curDatabaseInfo->recordBytes += adjust_num;
1583         zei->curDatabaseInfo->dirty = 1;
1584     }
1585 }
1586
1587 void zebraExplain_recordCountIncrement (ZebraExplainInfo zei, int adjust_num)
1588 {
1589     assert (zei->curDatabaseInfo);
1590
1591     if (adjust_num)
1592     {
1593         zei->curDatabaseInfo->recordCount += adjust_num;
1594         zei->curDatabaseInfo->dirty = 1;
1595     }
1596 }
1597
1598 zint zebraExplain_runNumberIncrement (ZebraExplainInfo zei, int adjust_num)
1599 {
1600     if (adjust_num)
1601     {
1602         zei->dirty = 1;
1603     }
1604     return zei->runNumber += adjust_num;
1605 }
1606
1607 RecordAttr *rec_init_attr (ZebraExplainInfo zei, Record rec)
1608 {
1609     RecordAttr *recordAttr;
1610
1611     if (rec->info[recInfo_attr])
1612         return (RecordAttr *) rec->info[recInfo_attr];
1613     recordAttr = (RecordAttr *) xmalloc (sizeof(*recordAttr));
1614
1615     memset(recordAttr, '\0', sizeof(*recordAttr));
1616     rec->info[recInfo_attr] = (char *) recordAttr;
1617     rec->size[recInfo_attr] = sizeof(*recordAttr);
1618     
1619     recordAttr->recordSize = 0;
1620     recordAttr->recordOffset = 0;
1621     recordAttr->runNumber = zei->runNumber;
1622     recordAttr->staticrank = 0;
1623     return recordAttr;
1624 }
1625
1626 static void att_loadset(void *p, const char *n, const char *name)
1627 {
1628     data1_handle dh = (data1_handle) p;
1629     if (!data1_get_attset (dh, name))
1630         yaz_log(YLOG_WARN, "Directive attset failed for %s", name);
1631 }
1632
1633 int zebraExplain_get_database_ord(ZebraExplainInfo zei)
1634 {
1635     if (!zei->curDatabaseInfo)
1636         return -1;
1637     return zei->curDatabaseInfo->ordinalDatabase;
1638 }
1639
1640 void zebraExplain_loadAttsets (data1_handle dh, Res res)
1641 {
1642     res_trav(res, "attset", dh, att_loadset);
1643 }
1644
1645 /*
1646      zebraExplain_addSU adds to AttributeDetails for a database and
1647      adds attributeSet (in AccessInfo area) to DatabaseInfo if it doesn't
1648      exist for the database.
1649
1650      If the database doesn't exist globally (in TargetInfo) an 
1651      AttributeSetInfo must be added (globally).
1652  */
1653 /*
1654  * Local variables:
1655  * c-basic-offset: 4
1656  * indent-tabs-mode: nil
1657  * End:
1658  * vim: shiftwidth=4 tabstop=8 expandtab
1659  */
1660