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