2007.
[idzebra-moved-to-github.git] / index / zinfo.c
1 /* $Id: zinfo.c,v 1.76 2007-01-15 15:10:17 adam Exp $
2    Copyright (C) 1995-2007
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
21 */
22
23 #include <sys/types.h>
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28
29 #include <idzebra/version.h>
30 #include "zinfo.h"
31
32 #define ZINFO_DEBUG 0
33
34 struct zebSUInfo {
35     int index_type;
36     zinfo_index_category_t cat;
37 #define ZEB_SU_SET_USE 1
38 #define ZEB_SU_STR 2
39     int which;
40     union {
41         char *str;
42     } u;
43     int ordinal;
44     zint doc_occurrences;
45     zint term_occurrences;
46 };
47
48 struct zebSUInfoB {
49     struct zebSUInfo info;
50     struct zebSUInfoB *next;
51 };
52
53 typedef struct zebAccessObjectB *zebAccessObject;
54 struct zebAccessObjectB {
55     void *handle;
56     zint sysno;
57     Odr_oid *oid;
58     zebAccessObject next;
59 };
60
61 typedef struct zebAccessInfoB *zebAccessInfo;
62 struct zebAccessInfoB {
63     zebAccessObject attributeSetIds;
64     zebAccessObject schemas;
65 };
66
67 typedef struct {
68     struct zebSUInfoB *SUInfo;
69     zint sysno;
70     int dirty;
71     int readFlag;
72     data1_node *data1_tree;
73 } *zebAttributeDetails;
74
75 struct zebDatabaseInfoB {
76     zebAttributeDetails attributeDetails;
77     int ordinalDatabase;
78     char *databaseName;
79     data1_node *data1_database;
80     zint recordCount;    /* records in db */
81     zint recordBytes;    /* size of records */
82     zint sysno;          /* sysno of database info */
83     int readFlag;        /* 1: read is needed when referenced; 0 if not */
84     int dirty;           /* 1: database is dirty: write is needed */
85     struct zebDatabaseInfoB *next;
86     zebAccessInfo accessInfo;
87 };
88
89 struct zebraExplainAttset {
90     char *name;
91     int ordinal;
92     struct zebraExplainAttset *next;
93 };
94
95 struct zebraCategoryListInfo {
96     int dirty;
97     zint sysno;
98     data1_node *data1_categoryList;
99 };
100
101 struct zebraExplainInfo {
102     int ordinalSU;
103     int ordinalDatabase;
104     zint runNumber;
105     int dirty;
106     int write_flag;
107     Records records;
108     data1_handle dh;
109     Res res;
110     struct zebraExplainAttset *attsets;
111     NMEM nmem;
112     data1_node *data1_target;
113     struct zebraCategoryListInfo *categoryList;
114     struct zebDatabaseInfoB *databaseInfo;
115     struct zebDatabaseInfoB *curDatabaseInfo;
116     zebAccessInfo accessInfo;
117     char date[15]; /* YYYY MMDD HH MM SS */
118     ZebraExplainUpdateFunc *updateFunc;
119     void *updateHandle;
120 };
121
122 static void zebraExplain_initCommonInfo(ZebraExplainInfo zei, data1_node *n);
123 static void zebraExplain_initAccessInfo(ZebraExplainInfo zei, data1_node *n);
124
125 static data1_node *read_sgml_rec(data1_handle dh, NMEM nmem, Record rec)
126 {
127     return data1_read_sgml(dh, nmem, rec->info[recInfo_storeData]);
128 }
129
130 static void zebraExplain_writeDatabase(ZebraExplainInfo zei,
131                                         struct zebDatabaseInfoB *zdi,
132                                         int key_flush);
133 static void zebraExplain_writeAttributeDetails(ZebraExplainInfo zei,
134                                                 zebAttributeDetails zad,
135                                                 const char *databaseName,
136                                                 int key_flush);
137 static void zebraExplain_writeTarget(ZebraExplainInfo zei, int key_flush);
138 static void zebraExplain_writeAttributeSet(ZebraExplainInfo zei,
139                                             zebAccessObject o,
140                                             int key_flush);
141 static void zebraExplain_writeCategoryList(ZebraExplainInfo zei,
142                                             struct zebraCategoryListInfo *zcl,
143                                             int key_flush);
144
145
146 static Record createRecord(Records records, zint *sysno)
147 {
148     Record rec;
149     if (*sysno)
150     {
151         rec = rec_get(records, *sysno);
152         if (!rec)
153             return 0;
154         xfree(rec->info[recInfo_storeData]);
155     }
156     else
157     {
158         rec = rec_new(records);
159         if (!rec)
160             return 0;
161         *sysno = rec->sysno;
162         
163         rec->info[recInfo_fileType] =
164             rec_strdup("grs.sgml", &rec->size[recInfo_fileType]);
165         rec->info[recInfo_databaseName] =
166             rec_strdup("IR-Explain-1",
167                         &rec->size[recInfo_databaseName]); 
168     }
169     return rec;
170 }
171
172 void zebraExplain_flush(ZebraExplainInfo zei, void *handle)
173 {
174     if (!zei)
175         return;
176     zei->updateHandle = handle;
177     if (zei->write_flag)
178     {
179         struct zebDatabaseInfoB *zdi;
180         zebAccessObject o;
181
182         /* write each database info record */
183         for (zdi = zei->databaseInfo; zdi; zdi = zdi->next)
184         {
185             zebraExplain_writeDatabase(zei, zdi, 1);
186             zebraExplain_writeAttributeDetails(zei, zdi->attributeDetails,
187                                                 zdi->databaseName, 1);
188         }
189         zebraExplain_writeTarget(zei, 1);
190         zebraExplain_writeCategoryList(zei,
191                                         zei->categoryList,
192                                         1);
193         assert(zei->accessInfo);
194         for (o = zei->accessInfo->attributeSetIds; o; o = o->next)
195             if (!o->sysno)
196                 zebraExplain_writeAttributeSet(zei, o, 1);
197         for (o = zei->accessInfo->schemas; o; o = o->next)
198             if (!o->sysno)
199             {
200 /*              zebraExplain_writeSchema(zei, o, 1); */
201             }
202
203         for (zdi = zei->databaseInfo; zdi; zdi = zdi->next)
204         {
205             zebraExplain_writeDatabase(zei, zdi, 0);
206             zebraExplain_writeAttributeDetails(zei, zdi->attributeDetails,
207                                                 zdi->databaseName, 0);
208         }
209         zebraExplain_writeTarget(zei, 0);
210     }
211 }
212
213 void zebraExplain_close(ZebraExplainInfo zei)
214 {
215 #if ZINFO_DEBUG
216     yaz_log(YLOG_LOG, "zebraExplain_close");
217 #endif
218     if (!zei)
219         return;
220     zebraExplain_flush(zei, zei->updateHandle);
221     nmem_destroy(zei->nmem);
222 }
223
224 void zebraExplain_mergeOids (ZebraExplainInfo zei, data1_node *n,
225                              zebAccessObject *op)
226 {
227     data1_node *np;
228
229     for (np = n->child; np; np = np->next)
230     {
231         char str[64];
232         int len;
233         Odr_oid *oid;
234         zebAccessObject ao;
235
236         if (np->which != DATA1N_tag || strcmp(np->u.tag.tag, "oid"))
237             continue;
238         len = np->child->u.data.len;
239         if (len > 63)
240             len = 63;
241         memcpy(str, np->child->u.data.data, len);
242         str[len] = '\0';
243         
244         oid = odr_getoidbystr_nmem(zei->nmem, str);
245
246         for (ao = *op; ao; ao = ao->next)
247             if (!oid_oidcmp(oid, ao->oid))
248             {
249                 ao->sysno = 1;
250                 break;
251             }
252         if (!ao)
253         {
254             ao = (zebAccessObject) nmem_malloc(zei->nmem, sizeof(*ao));
255             ao->handle = NULL;
256             ao->sysno = 1;
257             ao->oid = oid;
258             ao->next = *op;
259             *op = ao;
260         }
261     }
262 }
263
264 void zebraExplain_mergeAccessInfo(ZebraExplainInfo zei, data1_node *n,
265                                    zebAccessInfo *accessInfo)
266 {
267     data1_node *np;
268     
269     if (!n)
270     {
271         *accessInfo = (zebAccessInfo)
272             nmem_malloc(zei->nmem, sizeof(**accessInfo));
273         (*accessInfo)->attributeSetIds = NULL;
274         (*accessInfo)->schemas = NULL;
275     }
276     else
277     {
278         if (!(n = data1_search_tag(zei->dh, n->child, "accessInfo")))
279             return;
280         if ((np = data1_search_tag(zei->dh, n->child, "attributeSetIds")))
281             zebraExplain_mergeOids(zei, np,
282                                     &(*accessInfo)->attributeSetIds);
283         if ((np = data1_search_tag(zei->dh, n->child, "schemas")))
284             zebraExplain_mergeOids(zei, np,
285                                     &(*accessInfo)->schemas);
286     }
287 }
288
289 /* Explain structure
290     root record
291       of type targetInfo
292       and has sysno = 1
293
294     databaseList (list of databases)
295 */
296 /*
297 Example root:
298 explain:
299   targetInfo: TargetInfo
300     name: Zebra
301     namedResultSets: 1
302     multipleDbSearch: 1
303     nicknames:
304       name: Zebra
305     commonInfo:
306       dateAdded: 20030630190601
307       dateChanged: 20030630190601
308       languageCode: EN
309     accessinfo:
310       unitSystems:
311         string: ISO
312       attributeSetIds:
313         oid: 1.2.840.10003.3.2
314         oid: 1.2.840.10003.3.5
315         oid: 1.2.840.10003.3.1
316       schemas:
317         oid: 1.2.840.10003.13.1000.81.2
318         oid: 1.2.840.10003.13.2
319     zebraInfo:
320       version: 1.3.12
321       databaseList:
322         database:
323           name: Default
324           id: 50
325           attributeDetailsId: 51
326         database:
327           name: IR-Explain-1
328           id: 52
329           attributeDetailsId: 53
330       ordinalSU: 38
331       runNumber: 1
332 nextResultSetPosition = 2
333 */
334
335 ZebraExplainInfo zebraExplain_open(
336     Records records, data1_handle dh,
337     Res res,
338     int writeFlag,
339     void *updateHandle,
340     ZebraExplainUpdateFunc *updateFunc)
341 {
342     Record trec;
343     ZebraExplainInfo zei;
344     struct zebDatabaseInfoB **zdip;
345     time_t our_time;
346     struct tm *tm;
347     NMEM nmem = nmem_create();
348
349 #if ZINFO_DEBUG
350     yaz_log(YLOG_LOG, "zebraExplain_open wr=%d", writeFlag);
351 #endif
352     zei = (ZebraExplainInfo) nmem_malloc(nmem, sizeof(*zei));
353     zei->databaseInfo = 0;
354     zei->write_flag = writeFlag;
355     zei->updateHandle = updateHandle;
356     zei->updateFunc = updateFunc;
357     zei->dirty = 0;
358     zei->ordinalDatabase = 1;
359     zei->curDatabaseInfo = NULL;
360     zei->records = records;
361     zei->nmem = nmem;
362     zei->dh = dh;
363     
364     data1_get_absyn (zei->dh, "explain", DATA1_XPATH_INDEXING_DISABLE);
365
366     zei->attsets = NULL;
367     zei->res = res;
368     zei->categoryList = (struct zebraCategoryListInfo *)
369         nmem_malloc(zei->nmem, sizeof(*zei->categoryList));
370     zei->categoryList->sysno = 0;
371     zei->categoryList->dirty = 0;
372     zei->categoryList->data1_categoryList = NULL;
373
374     if ( atoi(res_get_def(res, "notimestamps", "0") )== 0)
375     {
376         time(&our_time);
377         tm = localtime(&our_time);
378         sprintf(zei->date, "%04d%02d%02d%02d%02d%02d",
379                  tm->tm_year+1900, tm->tm_mon+1,  tm->tm_mday,
380                  tm->tm_hour, tm->tm_min, tm->tm_sec);
381     } else {
382         sprintf(zei->date, "%04d%02d%02d%02d%02d%02d",
383                  0, 0, 0,  0, 0, 0);
384     }
385     zdip = &zei->databaseInfo;
386     trec = rec_get_root(records);      /* get "root" record */
387
388     zei->ordinalSU = 1;
389     zei->runNumber = 0;
390
391     zebraExplain_mergeAccessInfo(zei, 0, &zei->accessInfo);
392     if (trec)    /* targetInfo already exists ... */
393     {
394         data1_node *node_tgtinfo, *node_zebra, *node_list, *np;
395
396         zei->data1_target = read_sgml_rec(zei->dh, zei->nmem, trec);
397 #if 0
398         if (!zei->data1_target || !zei->data1_target->u.root.absyn)
399 #else
400         if (!zei->data1_target)
401 #endif
402         {
403             yaz_log(YLOG_FATAL, "Explain schema missing. Check profilePath");
404             nmem_destroy(zei->nmem);
405             return 0;
406         }
407 #if ZINFO_DEBUG
408         data1_pr_tree(zei->dh, zei->data1_target, stderr);
409 #endif
410         node_tgtinfo = data1_search_tag(zei->dh, zei->data1_target,
411                                          "/targetInfo");
412         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     /* zebra info (private) .. no children yet.. so se don't index zebraInfo */
1066     node_zebra = data1_mk_tag_uni (zei->dh, zei->nmem,
1067                                  "zebraInfo", node_adinfo);
1068
1069     /* extract *searchable* keys from it. We do this here, because
1070        record count, etc. is affected */
1071     if (key_flush)
1072         (*zei->updateFunc)(zei->updateHandle, drec, zad->data1_tree);
1073     node_list = data1_mk_tag_uni (zei->dh, zei->nmem,
1074                                  "attrlist", node_zebra);
1075     for (zsui = zad->SUInfo; zsui; zsui = zsui->next)
1076     {
1077         data1_node *node_attr;
1078         char index_type_str[2];
1079         
1080         node_attr = data1_mk_tag (zei->dh, zei->nmem, "attr", 0 /* attr */,
1081                                   node_list);
1082
1083         index_type_str[0] = zsui->info.index_type;
1084         index_type_str[1] = '\0';
1085         data1_mk_tag_data_text (zei->dh, node_attr, "type",
1086                                 index_type_str, zei->nmem);
1087         if (zsui->info.which == ZEB_SU_STR)
1088         {
1089             data1_mk_tag_data_text (zei->dh, node_attr, "str",
1090                                     zsui->info.u.str, zei->nmem);
1091         }
1092         data1_mk_tag_data_int (zei->dh, node_attr, "ordinal",
1093                                zsui->info.ordinal, zei->nmem);
1094
1095         data1_mk_tag_data_zint (zei->dh, node_attr, "dococcurrences",
1096                                 zsui->info.doc_occurrences, zei->nmem);
1097         data1_mk_tag_data_zint (zei->dh, node_attr, "termoccurrences",
1098                                 zsui->info.term_occurrences, zei->nmem);
1099         switch(zsui->info.cat)
1100         {
1101         case zinfo_index_category_index:
1102             data1_mk_tag_data_text (zei->dh, node_attr, "cat",
1103                                     "index", zei->nmem); break;
1104         case zinfo_index_category_sort:
1105             data1_mk_tag_data_text (zei->dh, node_attr, "cat",
1106                                     "sort", zei->nmem); break;
1107         case zinfo_index_category_alwaysmatches:
1108             data1_mk_tag_data_text (zei->dh, node_attr, "cat",
1109                                     "alwaysmatches", zei->nmem); break;
1110         case zinfo_index_category_anchor:
1111             data1_mk_tag_data_text (zei->dh, node_attr, "cat",
1112                                     "anchor", zei->nmem); break;
1113         }
1114     }
1115     /* convert to "SGML" and write it */
1116 #if ZINFO_DEBUG
1117     data1_pr_tree (zei->dh, zad->data1_tree, stderr);
1118 #endif
1119     sgml_buf = data1_nodetoidsgml(zei->dh, zad->data1_tree,
1120                                   0, &sgml_len);
1121     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1122     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1123     drec->size[recInfo_storeData] = sgml_len;
1124     
1125     rec_put (zei->records, &drec);
1126 }
1127
1128 static void zebraExplain_writeDatabase (ZebraExplainInfo zei,
1129                                         struct zebDatabaseInfoB *zdi,
1130                                         int key_flush)
1131 {
1132     char *sgml_buf;
1133     int sgml_len;
1134     Record drec;
1135     data1_node *node_dbinfo, *node_count, *node_zebra;
1136     
1137     if (!zdi->dirty)
1138         return;
1139
1140     zdi->dirty = 0;
1141 #if ZINFO_DEBUG
1142     yaz_log(YLOG_LOG, "zebraExplain_writeDatabase %s", zdi->databaseName);
1143 #endif
1144     drec = createRecord (zei->records, &zdi->sysno);
1145     if (!drec)
1146         return;
1147     assert (zdi->data1_database);
1148
1149     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database,
1150                                     "/databaseInfo");
1151
1152     assert (node_dbinfo);
1153     zebraExplain_updateCommonInfo (zei, node_dbinfo);
1154     zebraExplain_updateAccessInfo (zei, node_dbinfo, zdi->accessInfo);
1155
1156     /* record count */
1157     node_count = data1_mk_tag_uni (zei->dh, zei->nmem,
1158                                  "recordCount", node_dbinfo);
1159     data1_mk_tag_data_zint (zei->dh, node_count, "recordCountActual",
1160                             zdi->recordCount, zei->nmem);
1161
1162     /* zebra info (private) */
1163     node_zebra = data1_mk_tag_uni (zei->dh, zei->nmem,
1164                                  "zebraInfo", node_dbinfo);
1165
1166     /* extract *searchable* keys from it. We do this here, because
1167        record count, etc. is affected */
1168     if (key_flush)
1169         (*zei->updateFunc)(zei->updateHandle, drec, zdi->data1_database);
1170     data1_mk_tag_data_zint (zei->dh, node_zebra,
1171                            "recordBytes", zdi->recordBytes, zei->nmem);
1172
1173     data1_mk_tag_data_zint(zei->dh, node_zebra,
1174                            "ordinalDatabase", zdi->ordinalDatabase, zei->nmem);
1175
1176     /* convert to "SGML" and write it */
1177 #if ZINFO_DEBUG
1178     data1_pr_tree (zei->dh, zdi->data1_database, stderr);
1179 #endif
1180     sgml_buf = data1_nodetoidsgml(zei->dh, zdi->data1_database,
1181                                   0, &sgml_len);
1182     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1183     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1184     drec->size[recInfo_storeData] = sgml_len;
1185     
1186     rec_put (zei->records, &drec);
1187 }
1188
1189 static void writeAttributeValues (ZebraExplainInfo zei,
1190                                   data1_node *node_values,
1191                                   data1_attset *attset)
1192 {
1193     data1_att *atts;
1194     data1_attset_child *c;
1195
1196     if (!attset)
1197         return;
1198
1199     for (c = attset->children; c; c = c->next)
1200         writeAttributeValues (zei, node_values, c->child);
1201     for (atts = attset->atts; atts; atts = atts->next)
1202     {
1203         data1_node *node_value;
1204         
1205         node_value = data1_mk_tag (zei->dh, zei->nmem, "attributeValue",
1206                                    0 /* attr */, node_values);
1207         data1_mk_tag_data_text (zei->dh, node_value, "name",
1208                                 atts->name, zei->nmem);
1209         node_value = data1_mk_tag (zei->dh, zei->nmem, "value",
1210                                    0 /* attr */, node_value);
1211         data1_mk_tag_data_int (zei->dh, node_value, "numeric",
1212                                atts->value, zei->nmem);
1213     }
1214 }
1215
1216
1217 static void zebraExplain_writeAttributeSet (ZebraExplainInfo zei,
1218                                             zebAccessObject o,
1219                                             int key_flush)
1220 {
1221     char *sgml_buf;
1222     int sgml_len;
1223     Record drec;
1224     data1_node *node_root, *node_attinfo, *node_attributes, *node_atttype;
1225     data1_node *node_values;
1226     struct oident *entp;
1227     struct data1_attset *attset = NULL;
1228     
1229     if ((entp = oid_getentbyoid (o->oid)))
1230         attset = data1_attset_search_id (zei->dh, entp->value);
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                                  int 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 == -1 || (*zsui)->info.index_type == index_type)
1365              && (*zsui)->info.cat == cat
1366              && (*zsui)->info.which == ZEB_SU_STR 
1367              && !yaz_matchstr((*zsui)->info.u.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                             int *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 (zsui->info.which == ZEB_SU_STR)
1490             if (string_index)
1491                 *string_index = zsui->info.u.str;
1492         if (index_type)
1493             *index_type = zsui->info.index_type;
1494         return 0;
1495     }
1496     return -1;
1497 }
1498
1499
1500
1501 zebAccessObject zebraExplain_announceOid (ZebraExplainInfo zei,
1502                                           zebAccessObject *op,
1503                                           Odr_oid *oid)
1504 {
1505     zebAccessObject ao;
1506     
1507     for (ao = *op; ao; ao = ao->next)
1508         if (!oid_oidcmp (oid, ao->oid))
1509             break;
1510     if (!ao)
1511     {
1512         ao = (zebAccessObject) nmem_malloc (zei->nmem, sizeof(*ao));
1513         ao->handle = NULL;
1514         ao->sysno = 0;
1515         ao->oid = odr_oiddup_nmem (zei->nmem, oid);
1516         ao->next = *op;
1517         *op = ao;
1518     }
1519     return ao;
1520 }
1521
1522 void zebraExplain_addAttributeSet (ZebraExplainInfo zei, int set)
1523 {
1524     oident oe;
1525     int oid[OID_SIZE];
1526
1527     oe.proto = PROTO_Z3950;
1528     oe.oclass = CLASS_ATTSET;
1529     oe.value = (enum oid_value) set;
1530
1531     if (oid_ent_to_oid (&oe, oid))
1532     {
1533         zebraExplain_announceOid (zei, &zei->accessInfo->attributeSetIds, oid);
1534         zebraExplain_announceOid (zei, &zei->curDatabaseInfo->
1535                                   accessInfo->attributeSetIds, oid);
1536     }
1537 }
1538
1539 struct zebSUInfoB *zebraExplain_add_sui_info(ZebraExplainInfo zei,
1540                                              zinfo_index_category_t cat,
1541                                              int index_type)
1542 {
1543     struct zebSUInfoB *zsui;
1544
1545     assert (zei->curDatabaseInfo);
1546     zsui = (struct zebSUInfoB *) nmem_malloc (zei->nmem, sizeof(*zsui));
1547     zsui->next = zei->curDatabaseInfo->attributeDetails->SUInfo;
1548     zei->curDatabaseInfo->attributeDetails->SUInfo = zsui;
1549     zei->curDatabaseInfo->attributeDetails->dirty = 1;
1550     zei->dirty = 1;
1551     zsui->info.index_type = index_type;
1552     zsui->info.cat = cat;
1553     zsui->info.doc_occurrences = 0;
1554     zsui->info.term_occurrences = 0;
1555     zsui->info.ordinal = (zei->ordinalSU)++;
1556     return zsui;
1557 }
1558
1559 int zebraExplain_add_attr_str(ZebraExplainInfo zei, 
1560                               zinfo_index_category_t cat,
1561                               int index_type,
1562                               const char *index_name)
1563 {
1564     struct zebSUInfoB *zsui = zebraExplain_add_sui_info(zei, cat, index_type);
1565
1566     zsui->info.which = ZEB_SU_STR;
1567     zsui->info.u.str = nmem_strdup(zei->nmem, index_name);
1568     return zsui->info.ordinal;
1569 }
1570
1571 void zebraExplain_addSchema (ZebraExplainInfo zei, Odr_oid *oid)
1572 {
1573     zebraExplain_announceOid (zei, &zei->accessInfo->schemas, oid);
1574     zebraExplain_announceOid (zei, &zei->curDatabaseInfo->
1575                               accessInfo->schemas, oid);
1576 }
1577
1578 void zebraExplain_recordBytesIncrement (ZebraExplainInfo zei, int adjust_num)
1579 {
1580     assert (zei->curDatabaseInfo);
1581
1582     if (adjust_num)
1583     {
1584         zei->curDatabaseInfo->recordBytes += adjust_num;
1585         zei->curDatabaseInfo->dirty = 1;
1586     }
1587 }
1588
1589 void zebraExplain_recordCountIncrement (ZebraExplainInfo zei, int adjust_num)
1590 {
1591     assert (zei->curDatabaseInfo);
1592
1593     if (adjust_num)
1594     {
1595         zei->curDatabaseInfo->recordCount += adjust_num;
1596         zei->curDatabaseInfo->dirty = 1;
1597     }
1598 }
1599
1600 zint zebraExplain_runNumberIncrement (ZebraExplainInfo zei, int adjust_num)
1601 {
1602     if (adjust_num)
1603     {
1604         zei->dirty = 1;
1605     }
1606     return zei->runNumber += adjust_num;
1607 }
1608
1609 RecordAttr *rec_init_attr (ZebraExplainInfo zei, Record rec)
1610 {
1611     RecordAttr *recordAttr;
1612
1613     if (rec->info[recInfo_attr])
1614         return (RecordAttr *) rec->info[recInfo_attr];
1615     recordAttr = (RecordAttr *) xmalloc (sizeof(*recordAttr));
1616
1617     memset(recordAttr, '\0', sizeof(*recordAttr));
1618     rec->info[recInfo_attr] = (char *) recordAttr;
1619     rec->size[recInfo_attr] = sizeof(*recordAttr);
1620     
1621     recordAttr->recordSize = 0;
1622     recordAttr->recordOffset = 0;
1623     recordAttr->runNumber = zei->runNumber;
1624     recordAttr->staticrank = 0;
1625     return recordAttr;
1626 }
1627
1628 static void att_loadset(void *p, const char *n, const char *name)
1629 {
1630     data1_handle dh = (data1_handle) p;
1631     if (!data1_get_attset (dh, name))
1632         yaz_log(YLOG_WARN, "Directive attset failed for %s", name);
1633 }
1634
1635 int zebraExplain_get_database_ord(ZebraExplainInfo zei)
1636 {
1637     if (!zei->curDatabaseInfo)
1638         return -1;
1639     return zei->curDatabaseInfo->ordinalDatabase;
1640 }
1641
1642 void zebraExplain_loadAttsets (data1_handle dh, Res res)
1643 {
1644     res_trav(res, "attset", dh, att_loadset);
1645 }
1646
1647 /*
1648      zebraExplain_addSU adds to AttributeDetails for a database and
1649      adds attributeSet (in AccessInfo area) to DatabaseInfo if it doesn't
1650      exist for the database.
1651
1652      If the database doesn't exist globally (in TargetInfo) an 
1653      AttributeSetInfo must be added (globally).
1654  */
1655 /*
1656  * Local variables:
1657  * c-basic-offset: 4
1658  * indent-tabs-mode: nil
1659  * End:
1660  * vim: shiftwidth=4 tabstop=8 expandtab
1661  */
1662