Added notimestamps:1 option in the config file to disable
[idzebra-moved-to-github.git] / index / zinfo.c
1 /*
2  * Copyright (C) 1994-2002, Index Data
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Id: zinfo.c,v 1.32 2002-07-11 13:15:57 heikki Exp $
7  */
8
9 #include <stdlib.h>
10 #include <assert.h>
11 #include <string.h>
12 #include <time.h>
13
14 #include <zebraver.h>
15 #include "zinfo.h"
16
17 #define ZINFO_DEBUG 0
18
19 struct zebSUInfo {
20     int set;
21     int use;
22     int ordinal;
23 };
24
25 struct zebSUInfoB {
26     struct zebSUInfo info;
27     struct zebSUInfoB *next;
28 };
29
30 typedef struct zebAccessObjectB *zebAccessObject;
31 struct zebAccessObjectB {
32     void *handle;
33     int sysno;
34     Odr_oid *oid;
35     zebAccessObject next;
36 };
37
38 typedef struct zebAccessInfoB *zebAccessInfo;
39 struct zebAccessInfoB {
40     zebAccessObject attributeSetIds;
41     zebAccessObject schemas;
42 };
43
44 typedef struct {
45     struct zebSUInfoB *SUInfo;
46     int sysno;
47     int dirty;
48     int readFlag;
49     data1_node *data1_tree;
50 } *zebAttributeDetails;
51
52 struct zebDatabaseInfoB {
53     zebAttributeDetails attributeDetails;
54     char *databaseName;
55     data1_node *data1_database;
56     int recordCount;     /* records in db */
57     int recordBytes;     /* size of records */
58     int sysno;           /* sysno of database info */
59     int readFlag;        /* 1: read is needed when referenced; 0 if not */
60     int dirty;           /* 1: database is dirty: write is needed */
61     struct zebDatabaseInfoB *next;
62     zebAccessInfo accessInfo;
63 };
64
65 struct zebraExplainAttset {
66     char *name;
67     int ordinal;
68     struct zebraExplainAttset *next;
69 };
70
71 struct zebraCategoryListInfo {
72     int dirty;
73     int sysno;
74     data1_node *data1_categoryList;
75 };
76
77 struct zebraExplainInfo {
78     int  ordinalSU;
79     int  runNumber;
80     int  dirty;
81     int write_flag;
82     Records records;
83     data1_handle dh;
84     Res res;
85     struct zebraExplainAttset *attsets;
86     NMEM nmem;
87     data1_node *data1_target;
88     struct zebraCategoryListInfo *categoryList;
89     struct zebDatabaseInfoB *databaseInfo;
90     struct zebDatabaseInfoB *curDatabaseInfo;
91     zebAccessInfo accessInfo;
92     char date[15]; /* YYYY MMDD HH MM SS */
93     int (*updateFunc)(void *handle, Record drec, data1_node *n);
94     void *updateHandle;
95 };
96
97 static void zebraExplain_initCommonInfo (ZebraExplainInfo zei, data1_node *n);
98 static void zebraExplain_initAccessInfo (ZebraExplainInfo zei, data1_node *n);
99
100 static data1_node *read_sgml_rec (data1_handle dh, NMEM nmem, Record rec)
101 {
102     return data1_read_sgml (dh, nmem, rec->info[recInfo_storeData]);
103 }
104
105 static void zebraExplain_writeDatabase (ZebraExplainInfo zei,
106                                         struct zebDatabaseInfoB *zdi,
107                                         int key_flush);
108 static void zebraExplain_writeAttributeDetails (ZebraExplainInfo zei,
109                                                 zebAttributeDetails zad,
110                                                 const char *databaseName,
111                                                 int key_flush);
112 static void zebraExplain_writeTarget (ZebraExplainInfo zei, int key_flush);
113 static void zebraExplain_writeAttributeSet (ZebraExplainInfo zei,
114                                             zebAccessObject o,
115                                             int key_flush);
116 static void zebraExplain_writeCategoryList (ZebraExplainInfo zei,
117                                             struct zebraCategoryListInfo *zcl,
118                                             int key_flush);
119
120
121 static Record createRecord (Records records, int *sysno)
122 {
123     Record rec;
124     if (*sysno)
125     {
126         rec = rec_get (records, *sysno);
127         xfree (rec->info[recInfo_storeData]);
128     }
129     else
130     {
131         rec = rec_new (records);
132         *sysno = rec->sysno;
133         
134         rec->info[recInfo_fileType] =
135             rec_strdup ("grs.sgml", &rec->size[recInfo_fileType]);
136         rec->info[recInfo_databaseName] =
137             rec_strdup ("IR-Explain-1",
138                         &rec->size[recInfo_databaseName]); 
139     }
140     return rec;
141 }
142
143 void zebraExplain_flush (ZebraExplainInfo zei, void *handle)
144 {
145     if (!zei)
146         return;
147     zei->updateHandle = handle;
148     if (zei->write_flag)
149     {
150         struct zebDatabaseInfoB *zdi;
151         zebAccessObject o;
152
153         /* write each database info record */
154         for (zdi = zei->databaseInfo; zdi; zdi = zdi->next)
155         {
156             zebraExplain_writeDatabase (zei, zdi, 1);
157             zebraExplain_writeAttributeDetails (zei, zdi->attributeDetails,
158                                                 zdi->databaseName, 1);
159         }
160         zebraExplain_writeTarget (zei, 1);
161         zebraExplain_writeCategoryList (zei,
162                                         zei->categoryList,
163                                         1);
164         assert (zei->accessInfo);
165         for (o = zei->accessInfo->attributeSetIds; o; o = o->next)
166             if (!o->sysno)
167                 zebraExplain_writeAttributeSet (zei, o, 1);
168         for (o = zei->accessInfo->schemas; o; o = o->next)
169             if (!o->sysno)
170             {
171 /*              zebraExplain_writeSchema (zei, o, 1); */
172             }
173
174         for (zdi = zei->databaseInfo; zdi; zdi = zdi->next)
175         {
176             zebraExplain_writeDatabase (zei, zdi, 0);
177             zebraExplain_writeAttributeDetails (zei, zdi->attributeDetails,
178                                                 zdi->databaseName, 0);
179         }
180         zebraExplain_writeTarget (zei, 0);
181     }
182 }
183
184 void zebraExplain_close (ZebraExplainInfo zei)
185 {
186 #if ZINFO_DEBUG
187     yaz_log (LOG_LOG, "zebraExplain_close");
188 #endif
189     if (!zei)
190         return;
191     zebraExplain_flush (zei, zei->updateHandle);
192     nmem_destroy (zei->nmem);
193 }
194
195 void zebraExplain_mergeOids (ZebraExplainInfo zei, data1_node *n,
196                              zebAccessObject *op)
197 {
198     data1_node *np;
199
200     for (np = n->child; np; np = np->next)
201     {
202         char str[64];
203         int len;
204         Odr_oid *oid;
205         zebAccessObject ao;
206
207         if (np->which != DATA1N_tag || strcmp (np->u.tag.tag, "oid"))
208             continue;
209         len = np->child->u.data.len;
210         if (len > 63)
211             len = 63;
212         memcpy (str, np->child->u.data.data, len);
213         str[len] = '\0';
214         
215         oid = odr_getoidbystr_nmem (zei->nmem, str);
216
217         for (ao = *op; ao; ao = ao->next)
218             if (!oid_oidcmp (oid, ao->oid))
219             {
220                 ao->sysno = 1;
221                 break;
222             }
223         if (!ao)
224         {
225             ao = (zebAccessObject) nmem_malloc (zei->nmem, sizeof(*ao));
226             ao->handle = NULL;
227             ao->sysno = 1;
228             ao->oid = oid;
229             ao->next = *op;
230             *op = ao;
231         }
232     }
233 }
234
235 void zebraExplain_mergeAccessInfo (ZebraExplainInfo zei, data1_node *n,
236                                    zebAccessInfo *accessInfo)
237 {
238     data1_node *np;
239     
240     if (!n)
241     {
242         *accessInfo = (zebAccessInfo)
243             nmem_malloc (zei->nmem, sizeof(**accessInfo));
244         (*accessInfo)->attributeSetIds = NULL;
245         (*accessInfo)->schemas = NULL;
246     }
247     else
248     {
249         if (!(n = data1_search_tag (zei->dh, n->child, "accessInfo")))
250             return;
251         if ((np = data1_search_tag (zei->dh, n->child, "attributeSetIds")))
252             zebraExplain_mergeOids (zei, np,
253                                     &(*accessInfo)->attributeSetIds);
254         if ((np = data1_search_tag (zei->dh, n->child, "schemas")))
255             zebraExplain_mergeOids (zei, np,
256                                     &(*accessInfo)->schemas);
257     }
258 }
259
260 ZebraExplainInfo zebraExplain_open (
261     Records records, data1_handle dh,
262     Res res,
263     int writeFlag,
264     void *updateHandle,
265     int (*updateFunc)(void *handle, Record drec, data1_node *n))
266 {
267     Record trec;
268     ZebraExplainInfo zei;
269     struct zebDatabaseInfoB **zdip;
270     time_t our_time;
271     struct tm *tm;
272     NMEM nmem = nmem_create ();
273
274 #if ZINFO_DEBUG
275     logf (LOG_LOG, "zebraExplain_open wr=%d", writeFlag);
276 #endif
277     zei = (ZebraExplainInfo) nmem_malloc (nmem, sizeof(*zei));
278     zei->write_flag = writeFlag;
279     zei->updateHandle = updateHandle;
280     zei->updateFunc = updateFunc;
281     zei->dirty = 0;
282     zei->curDatabaseInfo = NULL;
283     zei->records = records;
284     zei->nmem = nmem;
285     zei->dh = dh;
286     zei->attsets = NULL;
287     zei->res = res;
288     zei->categoryList = (struct zebraCategoryListInfo *)
289         nmem_malloc (zei->nmem, sizeof(*zei->categoryList));
290     zei->categoryList->sysno = 0;
291     zei->categoryList->dirty = 0;
292     zei->categoryList->data1_categoryList = NULL;
293
294     if ( atoi (res_get_def (res, "notimestamps", "0") )== 0)
295     {
296         time (&our_time);
297         tm = localtime (&our_time);
298         sprintf (zei->date, "%04d%02d%02d%02d%02d%02d",
299                  tm->tm_year+1900, tm->tm_mon+1,  tm->tm_mday,
300                  tm->tm_hour, tm->tm_min, tm->tm_sec);
301     } else {
302         sprintf (zei->date, "%04d%02d%02d%02d%02d%02d",
303                  0, 0, 0,  0, 0, 0);
304     }
305     zdip = &zei->databaseInfo;
306     trec = rec_get (records, 1);      /* get "root" record */
307
308     zei->ordinalSU = 1;
309     zei->runNumber = 0;
310
311     zebraExplain_mergeAccessInfo (zei, 0, &zei->accessInfo);
312     if (trec)    /* targetInfo already exists ... */
313     {
314         data1_node *node_tgtinfo, *node_zebra, *node_list, *np;
315
316         zei->data1_target = read_sgml_rec (zei->dh, zei->nmem, trec);
317 #if 0
318         if (!zei->data1_target || !zei->data1_target->u.root.absyn)
319 #else
320         if (!zei->data1_target)
321 #endif
322         {
323             logf (LOG_FATAL, "Explain schema missing. Check profilePath");
324             nmem_destroy (zei->nmem);
325             return 0;
326         }
327 #if ZINFO_DEBUG
328         data1_pr_tree (zei->dh, zei->data1_target, stderr);
329 #endif
330         node_tgtinfo = data1_search_tag (zei->dh, zei->data1_target,
331                                          "/targetInfo");
332         zebraExplain_mergeAccessInfo (zei, node_tgtinfo,
333                                       &zei->accessInfo);
334
335         node_zebra = data1_search_tag (zei->dh, node_tgtinfo->child,
336                                        "zebraInfo");
337         np = 0;
338         if (node_zebra)
339         {
340             node_list = data1_search_tag (zei->dh, node_zebra->child,
341                                           "databaseList");
342             if (node_list)
343                 np = node_list->child;
344         }
345         for (; np; np = np->next)
346         {
347             data1_node *node_name = NULL;
348             data1_node *node_id = NULL;
349             data1_node *node_aid = NULL;
350             data1_node *np2;
351             if (np->which != DATA1N_tag || strcmp (np->u.tag.tag, "database"))
352                 continue;
353             for (np2 = np->child; np2; np2 = np2->next)
354             {
355                 if (np2->which != DATA1N_tag)
356                     continue;
357                 if (!strcmp (np2->u.tag.tag, "name"))
358                     node_name = np2->child;
359                 else if (!strcmp (np2->u.tag.tag, "id"))
360                     node_id = np2->child;
361                 else if (!strcmp (np2->u.tag.tag, "attributeDetailsId"))
362                     node_aid = np2->child;
363             }
364             assert (node_id && node_name && node_aid);
365             
366             *zdip = (struct zebDatabaseInfoB *) 
367                 nmem_malloc (zei->nmem, sizeof(**zdip));
368             (*zdip)->readFlag = 1;
369             (*zdip)->dirty = 0;
370             (*zdip)->data1_database = NULL;
371             (*zdip)->recordCount = 0;
372             (*zdip)->recordBytes = 0;
373             zebraExplain_mergeAccessInfo (zei, 0, &(*zdip)->accessInfo);
374
375             (*zdip)->databaseName = (char *)
376                 nmem_malloc (zei->nmem, 1+node_name->u.data.len);
377             memcpy ((*zdip)->databaseName, node_name->u.data.data,
378                     node_name->u.data.len);
379             (*zdip)->databaseName[node_name->u.data.len] = '\0';
380             (*zdip)->sysno = atoi_n (node_id->u.data.data,
381                                      node_id->u.data.len);
382             (*zdip)->attributeDetails = (zebAttributeDetails)
383                 nmem_malloc (zei->nmem, sizeof(*(*zdip)->attributeDetails));
384             (*zdip)->attributeDetails->sysno = atoi_n (node_aid->u.data.data,
385                                                        node_aid->u.data.len);
386             (*zdip)->attributeDetails->readFlag = 1;
387             (*zdip)->attributeDetails->dirty = 0;
388             (*zdip)->attributeDetails->SUInfo = NULL;
389
390             zdip = &(*zdip)->next;
391         }
392         if (node_zebra)
393         {
394             np = data1_search_tag (zei->dh, node_zebra->child,
395                                    "ordinalSU");
396             np = np->child;
397             assert (np && np->which == DATA1N_data);
398             zei->ordinalSU = atoi_n (np->u.data.data, np->u.data.len);
399             
400             np = data1_search_tag (zei->dh, node_zebra->child,
401                                    "runNumber");
402             np = np->child;
403             assert (np && np->which == DATA1N_data);
404             zei->runNumber = atoi_n (np->u.data.data, np->u.data.len);
405             yaz_log (LOG_LOG, "READ runnumber = %d", zei->runNumber);
406             *zdip = NULL;
407         }
408         rec_rm (&trec);
409     }
410     else  /* create initial targetInfo */
411     {
412         data1_node *node_tgtinfo;
413
414         *zdip = NULL;
415         if (writeFlag)
416         {
417             char *sgml_buf;
418             int sgml_len;
419
420             zei->data1_target =
421                 data1_read_sgml (zei->dh, zei->nmem,
422                                  "<explain><targetInfo>TargetInfo\n"
423                                  "<name>Zebra</>\n"
424                                  "<namedResultSets>1</>\n"
425                                  "<multipleDBSearch>1</>\n"
426                                  "<nicknames><name>Zebra</></>\n"
427                                  "</></>\n" );
428             if (!zei->data1_target)
429             {
430                 logf (LOG_FATAL, "Explain schema missing. Check profilePath");
431                 nmem_destroy (zei->nmem);
432                 return 0;
433             }
434             node_tgtinfo = data1_search_tag (zei->dh, zei->data1_target,
435                                              "/targetInfo");
436             assert (node_tgtinfo);
437
438             zebraExplain_initCommonInfo (zei, node_tgtinfo);
439             zebraExplain_initAccessInfo (zei, node_tgtinfo);
440
441             /* write now because we want to be sure about the sysno */
442             trec = rec_new (records);
443             trec->info[recInfo_fileType] =
444                 rec_strdup ("grs.sgml", &trec->size[recInfo_fileType]);
445             trec->info[recInfo_databaseName] =
446                 rec_strdup ("IR-Explain-1", &trec->size[recInfo_databaseName]);
447             
448             sgml_buf = data1_nodetoidsgml(dh, zei->data1_target, 0, &sgml_len);
449             trec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
450             memcpy (trec->info[recInfo_storeData], sgml_buf, sgml_len);
451             trec->size[recInfo_storeData] = sgml_len;
452             
453             rec_put (records, &trec);
454             rec_rm (&trec);
455
456         }
457         zebraExplain_newDatabase (zei, "IR-Explain-1", 0);
458             
459         if (!zei->categoryList->dirty)
460         {
461             struct zebraCategoryListInfo *zcl = zei->categoryList;
462             data1_node *node_cl;
463             
464             zcl->dirty = 1;
465             zcl->data1_categoryList =
466                 data1_read_sgml (zei->dh, zei->nmem,
467                                  "<explain><categoryList>CategoryList\n"
468                                  "</></>\n");
469         
470             if (zcl->data1_categoryList)
471             {
472                 node_cl = data1_search_tag (zei->dh, zcl->data1_categoryList,
473                                             "/categoryList");
474                 assert (node_cl);
475                 zebraExplain_initCommonInfo (zei, node_cl);
476             }
477         }
478     }
479     return zei;
480 }
481
482 static void zebraExplain_readAttributeDetails (ZebraExplainInfo zei,
483                                                zebAttributeDetails zad)
484 {
485     Record rec;
486     struct zebSUInfoB **zsuip = &zad->SUInfo;
487     data1_node *node_adinfo, *node_zebra, *node_list, *np;
488
489     assert (zad->sysno);
490     rec = rec_get (zei->records, zad->sysno);
491
492     zad->data1_tree = read_sgml_rec (zei->dh, zei->nmem, rec);
493
494     node_adinfo = data1_search_tag (zei->dh, zad->data1_tree,
495                                     "/attributeDetails");
496     node_zebra = data1_search_tag (zei->dh, node_adinfo->child,
497                                  "zebraInfo");
498     node_list = data1_search_tag (zei->dh, node_zebra->child,
499                                   "attrlist");
500     for (np = node_list->child; np; np = np->next)
501     {
502         data1_node *node_set = NULL;
503         data1_node *node_use = NULL;
504         data1_node *node_ordinal = NULL;
505         data1_node *np2;
506         char oid_str[128];
507         int oid_str_len;
508
509         if (np->which != DATA1N_tag || strcmp (np->u.tag.tag, "attr"))
510             continue;
511         for (np2 = np->child; np2; np2 = np2->next)
512         {
513             if (np2->which != DATA1N_tag || !np2->child ||
514                 np2->child->which != DATA1N_data)
515                 continue;
516             if (!strcmp (np2->u.tag.tag, "set"))
517                 node_set = np2->child;
518             else if (!strcmp (np2->u.tag.tag, "use"))
519                 node_use = np2->child;
520             else if (!strcmp (np2->u.tag.tag, "ordinal"))
521                 node_ordinal = np2->child;
522         }
523         assert (node_set && node_use && node_ordinal);
524
525         oid_str_len = node_set->u.data.len;
526         if (oid_str_len >= (int) sizeof(oid_str))
527             oid_str_len = sizeof(oid_str)-1;
528         memcpy (oid_str, node_set->u.data.data, oid_str_len);
529         oid_str[oid_str_len] = '\0';
530
531         *zsuip = (struct zebSUInfoB *)
532             nmem_malloc (zei->nmem, sizeof(**zsuip));
533         (*zsuip)->info.set = oid_getvalbyname (oid_str);
534
535         (*zsuip)->info.use = atoi_n (node_use->u.data.data,
536                                      node_use->u.data.len);
537         (*zsuip)->info.ordinal = atoi_n (node_ordinal->u.data.data,
538                                          node_ordinal->u.data.len);
539         logf (LOG_DEBUG, "set=%d use=%d ordinal=%d",
540               (*zsuip)->info.set, (*zsuip)->info.use, (*zsuip)->info.ordinal);
541         zsuip = &(*zsuip)->next;
542     }
543     *zsuip = NULL;
544     zad->readFlag = 0;
545     rec_rm (&rec);
546 }
547
548 static void zebraExplain_readDatabase (ZebraExplainInfo zei,
549                                        struct zebDatabaseInfoB *zdi)
550 {
551     Record rec;
552     data1_node *node_dbinfo, *node_zebra, *np;
553
554     assert (zdi->sysno);
555     rec = rec_get (zei->records, zdi->sysno);
556
557     zdi->data1_database = read_sgml_rec (zei->dh, zei->nmem, rec);
558     
559     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database,
560                                     "/databaseInfo");
561     assert (node_dbinfo);
562     zebraExplain_mergeAccessInfo (zei, node_dbinfo, &zdi->accessInfo);
563
564     node_zebra = data1_search_tag (zei->dh, node_dbinfo->child,
565                                  "zebraInfo");
566     if (node_zebra
567         && (np = data1_search_tag (zei->dh, node_zebra->child,
568                                    "recordBytes")) 
569         && np->child && np->child->which == DATA1N_data)
570         zdi->recordBytes = atoi_n (np->child->u.data.data,
571                                    np->child->u.data.len);
572     if ((np = data1_search_tag (zei->dh, node_dbinfo->child,
573                                 "recordCount")) &&
574         (np = data1_search_tag (zei->dh, np->child,
575                                 "recordCountActual")) &&
576         np->child->which == DATA1N_data)
577     {
578         zdi->recordCount = atoi_n (np->child->u.data.data,
579                                    np->child->u.data.len);
580     }
581     zdi->readFlag = 0;
582     rec_rm (&rec);
583 }
584
585 int zebraExplain_curDatabase (ZebraExplainInfo zei, const char *database)
586 {
587     struct zebDatabaseInfoB *zdi;
588     const char *database_n = strrchr (database, '/');
589
590     if (database_n)
591         database_n++;
592     else
593         database_n = database;
594     
595     assert (zei);
596     if (zei->curDatabaseInfo &&
597         !strcmp (zei->curDatabaseInfo->databaseName, database))
598         return 0;
599     for (zdi = zei->databaseInfo; zdi; zdi=zdi->next)
600     {
601         if (!strcmp (zdi->databaseName, database_n))
602             break;
603     }
604     if (!zdi)
605         return -1;
606 #if ZINFO_DEBUG
607     logf (LOG_LOG, "zebraExplain_curDatabase: %s", database);
608 #endif
609     if (zdi->readFlag)
610     {
611 #if ZINFO_DEBUG
612         logf (LOG_LOG, "zebraExplain_readDatabase: %s", database);
613 #endif
614         zebraExplain_readDatabase (zei, zdi);
615     }
616     if (zdi->attributeDetails->readFlag)
617     {
618 #if ZINFO_DEBUG
619         logf (LOG_LOG, "zebraExplain_readAttributeDetails: %s", database);
620 #endif
621         zebraExplain_readAttributeDetails (zei, zdi->attributeDetails);
622     }
623     zei->curDatabaseInfo = zdi;
624     return 0;
625 }
626
627 static void zebraExplain_initCommonInfo (ZebraExplainInfo zei, data1_node *n)
628 {
629     data1_node *c = data1_mk_tag (zei->dh, zei->nmem, "commonInfo", 0, n);
630     data1_mk_tag_data_text (zei->dh, c, "dateAdded", zei->date, zei->nmem);
631     data1_mk_tag_data_text (zei->dh, c, "dateChanged", zei->date, zei->nmem);
632     data1_mk_tag_data_text (zei->dh, c, "languageCode", "EN", zei->nmem);
633 }
634
635 static void zebraExplain_updateCommonInfo (ZebraExplainInfo zei, data1_node *n)
636 {
637     data1_node *c = data1_search_tag (zei->dh, n->child, "commonInfo");
638     assert (c);
639     data1_mk_tag_data_text_uni (zei->dh, c, "dateChanged", zei->date,
640                                 zei->nmem);
641 }
642
643 static void zebraExplain_initAccessInfo (ZebraExplainInfo zei, data1_node *n)
644 {
645     data1_node *c = data1_mk_tag (zei->dh, zei->nmem, "accessInfo", 0, n);
646     data1_node *d = data1_mk_tag (zei->dh, zei->nmem, "unitSystems", 0, c);
647     data1_mk_tag_data_text (zei->dh, d, "string", "ISO", zei->nmem);
648 }
649
650 static void zebraExplain_updateAccessInfo (ZebraExplainInfo zei, data1_node *n,
651                                            zebAccessInfo accessInfo)
652 {
653     data1_node *c = data1_search_tag (zei->dh, n->child, "accessInfo");
654     data1_node *d;
655     zebAccessObject p;
656     
657     if (!c)
658     {
659         data1_pr_tree (zei->dh, n, stdout);
660         exit (0);
661         assert (c);
662     }
663
664     if ((p = accessInfo->attributeSetIds))
665     {
666         d = data1_mk_tag_uni (zei->dh, zei->nmem, "attributeSetIds", c);
667         for (; p; p = p->next)
668             data1_mk_tag_data_oid (zei->dh, d, "oid", p->oid, zei->nmem);
669     }
670     if ((p = accessInfo->schemas))
671     {
672         d = data1_mk_tag_uni (zei->dh, zei->nmem, "schemas", c);
673         for (; p; p = p->next)
674             data1_mk_tag_data_oid (zei->dh, d, "oid", p->oid, zei->nmem);
675     }
676 }
677
678 int zebraExplain_newDatabase (ZebraExplainInfo zei, const char *database,
679                               int explain_database)
680 {
681     struct zebDatabaseInfoB *zdi;
682     data1_node *node_dbinfo, *node_adinfo;
683     const char *database_n = strrchr (database, '/');
684
685     if (database_n)
686         database_n++;
687     else
688         database_n = database;
689
690 #if ZINFO_DEBUG
691     logf (LOG_LOG, "zebraExplain_newDatabase: %s", database);
692 #endif
693     assert (zei);
694     for (zdi = zei->databaseInfo; zdi; zdi=zdi->next)
695     {
696         if (!strcmp (zdi->databaseName, database_n))
697             break;
698     }
699     if (zdi)
700         return -1;
701     /* it's new really. make it */
702     zdi = (struct zebDatabaseInfoB *) nmem_malloc (zei->nmem, sizeof(*zdi));
703     zdi->next = zei->databaseInfo;
704     zei->databaseInfo = zdi;
705     zdi->sysno = 0;
706     zdi->recordCount = 0;
707     zdi->recordBytes = 0;
708     zdi->readFlag = 0;
709     zdi->databaseName = nmem_strdup (zei->nmem, database_n);
710
711     zebraExplain_mergeAccessInfo (zei, 0, &zdi->accessInfo);
712     
713     assert (zei->dh);
714     assert (zei->nmem);
715
716     zdi->data1_database =
717         data1_read_sgml (zei->dh, zei->nmem, 
718                          "<explain><databaseInfo>DatabaseInfo\n"
719                          "</></>\n");
720     if (!zdi->data1_database)
721         return -2;
722
723     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database,
724                                     "/databaseInfo");
725     assert (node_dbinfo);
726
727     zebraExplain_initCommonInfo (zei, node_dbinfo);
728     zebraExplain_initAccessInfo (zei, node_dbinfo);
729
730     data1_mk_tag_data_text (zei->dh, node_dbinfo, "name",
731                                database, zei->nmem);
732     
733     if (explain_database)
734         data1_mk_tag_data_text (zei->dh, node_dbinfo, "explainDatabase",
735                                 "", zei->nmem);
736     
737     data1_mk_tag_data_text (zei->dh, node_dbinfo, "userFee",
738                             "0", zei->nmem);
739     
740     data1_mk_tag_data_text (zei->dh, node_dbinfo, "available",
741                             "1", zei->nmem);
742     
743 #if ZINFO_DEBUG
744     data1_pr_tree (zei->dh, zdi->data1_database, stderr);
745 #endif
746     zdi->dirty = 1;
747     zei->dirty = 1;
748     zei->curDatabaseInfo = zdi;
749
750     zdi->attributeDetails = (zebAttributeDetails)
751         nmem_malloc (zei->nmem, sizeof(*zdi->attributeDetails));
752     zdi->attributeDetails->readFlag = 0;
753     zdi->attributeDetails->sysno = 0;
754     zdi->attributeDetails->dirty = 1;
755     zdi->attributeDetails->SUInfo = NULL;
756     zdi->attributeDetails->data1_tree =
757         data1_read_sgml (zei->dh, zei->nmem,
758                          "<explain><attributeDetails>AttributeDetails\n"
759                          "</></>\n");
760
761     node_adinfo = data1_search_tag (zei->dh, zdi->attributeDetails->data1_tree,
762                                     "/attributeDetails");
763     assert (node_adinfo);
764
765     zebraExplain_initCommonInfo (zei, node_adinfo);
766
767     return 0;
768 }
769
770 static void writeAttributeValueDetails (ZebraExplainInfo zei,
771                                   zebAttributeDetails zad,
772                                   data1_node *node_atvs, data1_attset *attset)
773
774 {
775     struct zebSUInfoB *zsui;
776     int set_ordinal = attset->reference;
777     data1_attset_child *c;
778
779     for (c = attset->children; c; c = c->next)
780         writeAttributeValueDetails (zei, zad, node_atvs, c->child);
781     for (zsui = zad->SUInfo; zsui; zsui = zsui->next)
782     {
783         data1_node *node_attvalue, *node_value;
784         if (set_ordinal != zsui->info.set)
785             continue;
786         node_attvalue = data1_mk_tag (zei->dh, zei->nmem, "attributeValue",
787                                       0 /* attr */, node_atvs);
788         node_value = data1_mk_tag (zei->dh, zei->nmem, "value",
789                                    0 /* attr */, node_attvalue);
790         data1_mk_tag_data_int (zei->dh, node_value, "numeric",
791                                zsui->info.use, zei->nmem);
792     }
793 }
794
795 static void zebraExplain_writeCategoryList (ZebraExplainInfo zei,
796                                             struct zebraCategoryListInfo *zcl,
797                                             int key_flush)
798 {
799     char *sgml_buf;
800     int sgml_len;
801     int i;
802     Record drec;
803     data1_node *node_ci, *node_categoryList;
804     int sysno = 0;
805     static char *category[] = {
806         "CategoryList",
807         "TargetInfo",
808         "DatabaseInfo",
809         "AttributeDetails",
810         NULL
811     };
812
813     assert (zcl);
814     if (!zcl->dirty)
815         return ;
816     zcl->dirty = 1;
817     node_categoryList = zcl->data1_categoryList;
818
819 #if ZINFO_DEBUG
820     logf (LOG_LOG, "zebraExplain_writeCategoryList");
821 #endif
822
823     drec = createRecord (zei->records, &sysno);
824     
825     node_ci = data1_search_tag (zei->dh, node_categoryList,
826                                 "/categoryList");
827     assert (node_ci);
828     node_ci = data1_mk_tag (zei->dh, zei->nmem, "categories", 0 /* attr */,
829                             node_ci);
830     assert (node_ci);
831     
832     for (i = 0; category[i]; i++)
833     {
834         data1_node *node_cat = data1_mk_tag (zei->dh, zei->nmem,  "category",
835                                              0 /* attr */, node_ci);
836
837         data1_mk_tag_data_text (zei->dh, node_cat, "name",
838                                 category[i], zei->nmem);
839     }
840     /* extract *searchable* keys from it. We do this here, because
841        record count, etc. is affected */
842     if (key_flush)
843         (*zei->updateFunc)(zei->updateHandle, drec, node_categoryList);
844
845     /* convert to "SGML" and write it */
846 #if ZINFO_DEBUG
847     data1_pr_tree (zei->dh, node_categoryList, stderr);
848 #endif
849     sgml_buf = data1_nodetoidsgml(zei->dh, node_categoryList, 0, &sgml_len);
850     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
851     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
852     drec->size[recInfo_storeData] = sgml_len;
853     
854     rec_put (zei->records, &drec);
855 }
856
857 static void zebraExplain_writeAttributeDetails (ZebraExplainInfo zei,
858                                                 zebAttributeDetails zad,
859                                                 const char *databaseName,
860                                                 int key_flush)
861 {
862     char *sgml_buf;
863     int sgml_len;
864     Record drec;
865     data1_node *node_adinfo, *node_list, *node_zebra, *node_attributesBySet;
866     struct zebSUInfoB *zsui;
867     int set_min;
868     
869     if (!zad->dirty)
870         return;
871     
872     zad->dirty = 0;
873 #if ZINFO_DEBUG
874     logf (LOG_LOG, "zebraExplain_writeAttributeDetails");    
875 #endif
876
877     drec = createRecord (zei->records, &zad->sysno);
878     assert (zad->data1_tree);
879
880     node_adinfo = data1_search_tag (zei->dh, zad->data1_tree,
881                                    "/attributeDetails");
882     zebraExplain_updateCommonInfo (zei, node_adinfo);
883
884     data1_mk_tag_data_text (zei->dh, node_adinfo, "name",
885                             databaseName, zei->nmem);
886
887     /* extract *searchable* keys from it. We do this here, because
888        record count, etc. is affected */
889     if (key_flush)
890         (*zei->updateFunc)(zei->updateHandle, drec, zad->data1_tree);
891
892     node_attributesBySet = data1_mk_tag_uni (zei->dh, zei->nmem,
893                                            "attributesBySet", node_adinfo);
894     set_min = -1;
895     while (1)
896     {
897         data1_node *node_asd;
898         data1_attset *attset;
899         int set_ordinal = -1;
900         for (zsui = zad->SUInfo; zsui; zsui = zsui->next)
901         {
902             if ((set_ordinal < 0 || set_ordinal > zsui->info.set)
903                 && zsui->info.set > set_min)
904                 set_ordinal = zsui->info.set;
905         }
906         if (set_ordinal < 0)
907             break;
908         set_min = set_ordinal;
909         node_asd = data1_mk_tag (zei->dh, zei->nmem,
910                                  "attributeSetDetails",
911                                  0 /* attr */, node_attributesBySet);
912
913         attset = data1_attset_search_id (zei->dh, set_ordinal);
914         if (!attset)
915         {
916             zebraExplain_loadAttsets (zei->dh, zei->res);
917             attset = data1_attset_search_id (zei->dh, set_ordinal);
918         }
919         if (attset)
920         {
921             int oid[OID_SIZE];
922             oident oe;
923             
924             oe.proto = PROTO_Z3950;
925             oe.oclass = CLASS_ATTSET;
926             oe.value = (enum oid_value) set_ordinal;
927             
928             if (oid_ent_to_oid (&oe, oid))
929             {
930                 data1_node *node_abt, *node_atd, *node_atvs;
931                 data1_mk_tag_data_oid (zei->dh, node_asd, "oid",
932                                        oid, zei->nmem);
933                 
934                 node_abt = data1_mk_tag (zei->dh, zei->nmem,
935                                          "attributesByType",
936                                          0 /*attr */, node_asd);
937                 node_atd = data1_mk_tag (zei->dh, zei->nmem,
938                                          "attributeTypeDetails", 
939                                          0 /* attr */, node_abt);
940                 data1_mk_tag_data_int (zei->dh, node_atd,
941                                        "type", 1, zei->nmem);
942                 node_atvs = data1_mk_tag (zei->dh, zei->nmem, 
943                                           "attributeValues",
944                                           0 /* attr */, node_atd);
945                 writeAttributeValueDetails (zei, zad, node_atvs, attset);
946             }
947         }
948     }
949     /* zebra info (private) */
950     node_zebra = data1_mk_tag_uni (zei->dh, zei->nmem,
951                                  "zebraInfo", node_adinfo);
952     node_list = data1_mk_tag_uni (zei->dh, zei->nmem,
953                                  "attrlist", node_zebra);
954     for (zsui = zad->SUInfo; zsui; zsui = zsui->next)
955     {
956         struct oident oident;
957         int oid[OID_SIZE];
958         data1_node *node_attr;
959         
960         node_attr = data1_mk_tag (zei->dh, zei->nmem, "attr", 0 /* attr */,
961                                   node_list);
962         
963         oident.proto = PROTO_Z3950;
964         oident.oclass = CLASS_ATTSET;
965         oident.value = (enum oid_value) zsui->info.set;
966         oid_ent_to_oid (&oident, oid);
967         
968         data1_mk_tag_data_text (zei->dh, node_attr, "set",
969                                 oident.desc, zei->nmem);
970         data1_mk_tag_data_int (zei->dh, node_attr, "use",
971                                zsui->info.use, zei->nmem);
972         data1_mk_tag_data_int (zei->dh, node_attr, "ordinal",
973                                zsui->info.ordinal, zei->nmem);
974     }
975     /* convert to "SGML" and write it */
976 #if ZINFO_DEBUG
977     data1_pr_tree (zei->dh, zad->data1_tree, stderr);
978 #endif
979     sgml_buf = data1_nodetoidsgml(zei->dh, zad->data1_tree,
980                                   0, &sgml_len);
981     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
982     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
983     drec->size[recInfo_storeData] = sgml_len;
984     
985     rec_put (zei->records, &drec);
986 }
987
988 static void zebraExplain_writeDatabase (ZebraExplainInfo zei,
989                                         struct zebDatabaseInfoB *zdi,
990                                         int key_flush)
991 {
992     char *sgml_buf;
993     int sgml_len;
994     Record drec;
995     data1_node *node_dbinfo, *node_count, *node_zebra;
996     
997     if (!zdi->dirty)
998         return;
999
1000     zdi->dirty = 0;
1001 #if ZINFO_DEBUG
1002     logf (LOG_LOG, "zebraExplain_writeDatabase %s", zdi->databaseName);
1003 #endif
1004     drec = createRecord (zei->records, &zdi->sysno);
1005     assert (zdi->data1_database);
1006
1007     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database,
1008                                     "/databaseInfo");
1009
1010     assert (node_dbinfo);
1011     zebraExplain_updateCommonInfo (zei, node_dbinfo);
1012     zebraExplain_updateAccessInfo (zei, node_dbinfo, zdi->accessInfo);
1013
1014     /* extract *searchable* keys from it. We do this here, because
1015        record count, etc. is affected */
1016     if (key_flush)
1017         (*zei->updateFunc)(zei->updateHandle, drec, zdi->data1_database);
1018     /* record count */
1019     node_count = data1_mk_tag_uni (zei->dh, zei->nmem,
1020                                  "recordCount", node_dbinfo);
1021     data1_mk_tag_data_int (zei->dh, node_count, "recordCountActual",
1022                               zdi->recordCount, zei->nmem);
1023
1024     /* zebra info (private) */
1025     node_zebra = data1_mk_tag_uni (zei->dh, zei->nmem,
1026                                  "zebraInfo", node_dbinfo);
1027     data1_mk_tag_data_int (zei->dh, node_zebra,
1028                            "recordBytes", zdi->recordBytes, zei->nmem);
1029     /* convert to "SGML" and write it */
1030 #if ZINFO_DEBUG
1031     data1_pr_tree (zei->dh, zdi->data1_database, stderr);
1032 #endif
1033     sgml_buf = data1_nodetoidsgml(zei->dh, zdi->data1_database,
1034                                   0, &sgml_len);
1035     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1036     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1037     drec->size[recInfo_storeData] = sgml_len;
1038     
1039     rec_put (zei->records, &drec);
1040 }
1041
1042 static void writeAttributeValues (ZebraExplainInfo zei,
1043                                   data1_node *node_values,
1044                                   data1_attset *attset)
1045 {
1046     data1_att *atts;
1047     data1_attset_child *c;
1048
1049     if (!attset)
1050         return;
1051
1052     for (c = attset->children; c; c = c->next)
1053         writeAttributeValues (zei, node_values, c->child);
1054     for (atts = attset->atts; atts; atts = atts->next)
1055     {
1056         data1_node *node_value;
1057         
1058         node_value = data1_mk_tag (zei->dh, zei->nmem, "attributeValue",
1059                                    0 /* attr */, node_values);
1060         data1_mk_tag_data_text (zei->dh, node_value, "name",
1061                                 atts->name, zei->nmem);
1062         node_value = data1_mk_tag (zei->dh, zei->nmem, "value",
1063                                    0 /* attr */, node_value);
1064         data1_mk_tag_data_int (zei->dh, node_value, "numeric",
1065                                atts->value, zei->nmem);
1066     }
1067 }
1068
1069
1070 static void zebraExplain_writeAttributeSet (ZebraExplainInfo zei,
1071                                             zebAccessObject o,
1072                                             int key_flush)
1073 {
1074     char *sgml_buf;
1075     int sgml_len;
1076     Record drec;
1077     data1_node *node_root, *node_attinfo, *node_attributes, *node_atttype;
1078     data1_node *node_values;
1079     struct oident *entp;
1080     struct data1_attset *attset = NULL;
1081     
1082     if ((entp = oid_getentbyoid (o->oid)))
1083         attset = data1_attset_search_id (zei->dh, entp->value);
1084             
1085 #if ZINFO_DEBUG
1086     logf (LOG_LOG, "zebraExplain_writeAttributeSet %s",
1087           attset ? attset->name : "<unknown>");    
1088 #endif
1089
1090     drec = createRecord (zei->records, &o->sysno);
1091     node_root =
1092         data1_read_sgml (zei->dh, zei->nmem,
1093                          "<explain><attributeSetInfo>AttributeSetInfo\n"
1094                          "</></>\n" );
1095
1096     node_attinfo = data1_search_tag (zei->dh, node_root,
1097                                    "/attributeSetInfo");
1098
1099     assert (node_attinfo);
1100     zebraExplain_initCommonInfo (zei, node_attinfo);
1101     zebraExplain_updateCommonInfo (zei, node_attinfo);
1102
1103     data1_mk_tag_data_oid (zei->dh, node_attinfo,
1104                             "oid", o->oid, zei->nmem);
1105     if (attset && attset->name)
1106         data1_mk_tag_data_text (zei->dh, node_attinfo,
1107                                 "name", attset->name, zei->nmem);
1108     
1109     node_attributes = data1_mk_tag_uni (zei->dh, zei->nmem,
1110                                       "attributes", node_attinfo);
1111     node_atttype = data1_mk_tag_uni (zei->dh, zei->nmem,
1112                                    "attributeType", node_attributes);
1113     data1_mk_tag_data_text (zei->dh, node_atttype,
1114                             "name", "Use", zei->nmem);
1115     data1_mk_tag_data_text (zei->dh, node_atttype,
1116                             "description", "Use Attribute", zei->nmem);
1117     data1_mk_tag_data_int (zei->dh, node_atttype,
1118                            "type", 1, zei->nmem);
1119     node_values = data1_mk_tag (zei->dh, zei->nmem,
1120                                 "attributeValues", 0 /* attr */, node_atttype);
1121     if (attset)
1122         writeAttributeValues (zei, node_values, attset);
1123
1124     /* extract *searchable* keys from it. We do this here, because
1125        record count, etc. is affected */
1126     if (key_flush)
1127         (*zei->updateFunc)(zei->updateHandle, drec, node_root);
1128     /* convert to "SGML" and write it */
1129 #if ZINFO_DEBUG
1130     data1_pr_tree (zei->dh, node_root, stderr);
1131 #endif
1132     sgml_buf = data1_nodetoidsgml(zei->dh, node_root, 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_writeTarget (ZebraExplainInfo zei, int key_flush)
1141 {
1142     struct zebDatabaseInfoB *zdi;
1143     data1_node *node_tgtinfo, *node_list, *node_zebra;
1144     Record trec;
1145     int sgml_len;
1146     char *sgml_buf;
1147
1148     if (!zei->dirty)
1149         return;
1150     zei->dirty = 0;
1151
1152     trec = rec_get (zei->records, 1);
1153     xfree (trec->info[recInfo_storeData]);
1154
1155     node_tgtinfo = data1_search_tag (zei->dh, zei->data1_target,
1156                                      "/targetInfo");
1157     assert (node_tgtinfo);
1158
1159     zebraExplain_updateCommonInfo (zei, node_tgtinfo);
1160     zebraExplain_updateAccessInfo (zei, node_tgtinfo, zei->accessInfo);
1161
1162     /* convert to "SGML" and write it */
1163     if (key_flush)
1164         (*zei->updateFunc)(zei->updateHandle, trec, zei->data1_target);
1165
1166     node_zebra = data1_mk_tag_uni (zei->dh, zei->nmem,
1167                                  "zebraInfo", node_tgtinfo);
1168     data1_mk_tag_data_text (zei->dh, node_zebra, "version",
1169                                ZEBRAVER, zei->nmem);
1170     node_list = data1_mk_tag (zei->dh, zei->nmem,
1171                               "databaseList", 0 /* attr */, node_zebra);
1172     for (zdi = zei->databaseInfo; zdi; zdi = zdi->next)
1173     {
1174         data1_node *node_db;
1175         node_db = data1_mk_tag (zei->dh, zei->nmem,
1176                                 "database", 0 /* attr */, node_list);
1177         data1_mk_tag_data_text (zei->dh, node_db, "name",
1178                                 zdi->databaseName, zei->nmem);
1179         data1_mk_tag_data_int (zei->dh, node_db, "id",
1180                                zdi->sysno, zei->nmem);
1181         data1_mk_tag_data_int (zei->dh, node_db, "attributeDetailsId",
1182                                zdi->attributeDetails->sysno, zei->nmem);
1183     }
1184     data1_mk_tag_data_int (zei->dh, node_zebra, "ordinalSU",
1185                            zei->ordinalSU, zei->nmem);
1186
1187     data1_mk_tag_data_int (zei->dh, node_zebra, "runNumber",
1188                               zei->runNumber, zei->nmem);
1189
1190 #if ZINFO_DEBUG
1191     data1_pr_tree (zei->dh, zei->data1_target, stderr);
1192 #endif
1193     sgml_buf = data1_nodetoidsgml(zei->dh, zei->data1_target,
1194                                   0, &sgml_len);
1195     trec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1196     memcpy (trec->info[recInfo_storeData], sgml_buf, sgml_len);
1197     trec->size[recInfo_storeData] = sgml_len;
1198     
1199     rec_put (zei->records, &trec);
1200 }
1201
1202 int zebraExplain_lookupSU (ZebraExplainInfo zei, int set, int use)
1203 {
1204     struct zebSUInfoB *zsui;
1205
1206     assert (zei->curDatabaseInfo);
1207     for (zsui = zei->curDatabaseInfo->attributeDetails->SUInfo;
1208          zsui; zsui=zsui->next)
1209         if (zsui->info.use == use && zsui->info.set == set)
1210             return zsui->info.ordinal;
1211     return -1;
1212 }
1213
1214 int zebraExplain_lookup_ord (ZebraExplainInfo zei, int ord,
1215                              const char **db, int *set, int *use)
1216 {
1217     struct zebDatabaseInfoB *zdb;
1218     for (zdb = zei->databaseInfo; zdb; zdb = zdb->next)
1219     {
1220         struct zebSUInfoB *zsui = zdb->attributeDetails->SUInfo;
1221         for ( ;zsui; zsui = zsui->next)
1222             if (zsui->info.ordinal == ord)
1223             {
1224                 *db = zdb->databaseName;
1225                 *set = zsui->info.set;
1226                 *use = zsui->info.use;
1227                 return 0;
1228             }
1229     }
1230     return -1;
1231 }
1232
1233 zebAccessObject zebraExplain_announceOid (ZebraExplainInfo zei,
1234                                           zebAccessObject *op,
1235                                           Odr_oid *oid)
1236 {
1237     zebAccessObject ao;
1238     
1239     for (ao = *op; ao; ao = ao->next)
1240         if (!oid_oidcmp (oid, ao->oid))
1241             break;
1242     if (!ao)
1243     {
1244         ao = (zebAccessObject) nmem_malloc (zei->nmem, sizeof(*ao));
1245         ao->handle = NULL;
1246         ao->sysno = 0;
1247         ao->oid = odr_oiddup_nmem (zei->nmem, oid);
1248         ao->next = *op;
1249         *op = ao;
1250     }
1251     return ao;
1252 }
1253
1254 void zebraExplain_addAttributeSet (ZebraExplainInfo zei, int set)
1255 {
1256     oident oe;
1257     int oid[OID_SIZE];
1258
1259     oe.proto = PROTO_Z3950;
1260     oe.oclass = CLASS_ATTSET;
1261     oe.value = (enum oid_value) set;
1262
1263     if (oid_ent_to_oid (&oe, oid))
1264     {
1265         zebraExplain_announceOid (zei, &zei->accessInfo->attributeSetIds, oid);
1266         zebraExplain_announceOid (zei, &zei->curDatabaseInfo->
1267                                   accessInfo->attributeSetIds, oid);
1268     }
1269 }
1270
1271 int zebraExplain_addSU (ZebraExplainInfo zei, int set, int use)
1272 {
1273     struct zebSUInfoB *zsui;
1274
1275     assert (zei->curDatabaseInfo);
1276     for (zsui = zei->curDatabaseInfo->attributeDetails->SUInfo;
1277          zsui; zsui=zsui->next)
1278         if (zsui->info.use == use && zsui->info.set == set)
1279             return -1;
1280     zebraExplain_addAttributeSet (zei, set);
1281     zsui = (struct zebSUInfoB *) nmem_malloc (zei->nmem, sizeof(*zsui));
1282     zsui->next = zei->curDatabaseInfo->attributeDetails->SUInfo;
1283     zei->curDatabaseInfo->attributeDetails->SUInfo = zsui;
1284     zei->curDatabaseInfo->attributeDetails->dirty = 1;
1285     zei->dirty = 1;
1286     zsui->info.set = set;
1287     zsui->info.use = use;
1288     zsui->info.ordinal = (zei->ordinalSU)++;
1289     return zsui->info.ordinal;
1290 }
1291
1292 void zebraExplain_addSchema (ZebraExplainInfo zei, Odr_oid *oid)
1293 {
1294     zebraExplain_announceOid (zei, &zei->accessInfo->schemas, oid);
1295     zebraExplain_announceOid (zei, &zei->curDatabaseInfo->
1296                               accessInfo->schemas, oid);
1297 }
1298
1299 void zebraExplain_recordBytesIncrement (ZebraExplainInfo zei, int adjust_num)
1300 {
1301     assert (zei->curDatabaseInfo);
1302
1303     if (adjust_num)
1304     {
1305         zei->curDatabaseInfo->recordBytes += adjust_num;
1306         zei->curDatabaseInfo->dirty = 1;
1307     }
1308 }
1309
1310 void zebraExplain_recordCountIncrement (ZebraExplainInfo zei, int adjust_num)
1311 {
1312     assert (zei->curDatabaseInfo);
1313
1314     if (adjust_num)
1315     {
1316         zei->curDatabaseInfo->recordCount += adjust_num;
1317         zei->curDatabaseInfo->dirty = 1;
1318     }
1319 }
1320
1321 int zebraExplain_runNumberIncrement (ZebraExplainInfo zei, int adjust_num)
1322 {
1323     if (adjust_num)
1324     {
1325         zei->dirty = 1;
1326         yaz_log (LOG_LOG, "zinfo run number=%d", zei->runNumber+adjust_num);
1327     }
1328     return zei->runNumber += adjust_num;
1329 }
1330
1331 RecordAttr *rec_init_attr (ZebraExplainInfo zei, Record rec)
1332 {
1333     RecordAttr *recordAttr;
1334
1335     if (rec->info[recInfo_attr])
1336         return (RecordAttr *) rec->info[recInfo_attr];
1337     recordAttr = (RecordAttr *) xmalloc (sizeof(*recordAttr));
1338     rec->info[recInfo_attr] = (char *) recordAttr;
1339     rec->size[recInfo_attr] = sizeof(*recordAttr);
1340     
1341     recordAttr->recordSize = 0;
1342     recordAttr->recordOffset = 0;
1343     recordAttr->runNumber = zei->runNumber;
1344     return recordAttr;
1345 }
1346
1347 static void att_loadset(void *p, const char *n, const char *name)
1348 {
1349     data1_handle dh = (data1_handle) p;
1350     if (!data1_get_attset (dh, name))
1351         logf (LOG_WARN, "Directive attset failed for %s", name);
1352 }
1353
1354 void zebraExplain_loadAttsets (data1_handle dh, Res res)
1355 {
1356     res_trav(res, "attset", dh, att_loadset);
1357 }
1358
1359 /*
1360      zebraExplain_addSU adds to AttributeDetails for a database and
1361      adds attributeSet (in AccessInfo area) to DatabaseInfo if it doesn't
1362      exist for the database.
1363
1364      If the database doesn't exist globally (in TargetInfo) an 
1365      AttributeSetInfo must be added (globally).
1366  */