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