Zebra version corresponds to YAZ version 1.4.
[idzebra-moved-to-github.git] / index / recindex.c
1 /*
2  * Copyright (C) 1994-1997, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: recindex.c,v $
7  * Revision 1.19  1997-09-17 12:19:16  adam
8  * Zebra version corresponds to YAZ version 1.4.
9  * Changed Zebra server so that it doesn't depend on global common_resource.
10  *
11  * Revision 1.18  1997/07/15 16:28:42  adam
12  * Bug fix: storeData didn't work with files with multiple records.
13  * Bug fix: fixed memory management with records; not really well
14  *  thought through.
15  *
16  * Revision 1.17  1997/02/12 20:39:46  adam
17  * Implemented options -f <n> that limits the log to the first <n>
18  * records.
19  * Changed some log messages also.
20  *
21  * Revision 1.16  1996/06/04 10:19:00  adam
22  * Minor changes - removed include of ctype.h.
23  *
24  * Revision 1.15  1996/05/13  14:23:06  adam
25  * Work on compaction of set/use bytes in dictionary.
26  *
27  * Revision 1.14  1996/02/01  20:48:15  adam
28  * The total size of records are always checked in rec_cache_insert to
29  * reduce memory usage.
30  *
31  * Revision 1.13  1995/12/11  09:12:49  adam
32  * The rec_get function returns NULL if record doesn't exist - will
33  * happen in the server if the result set records have been deleted since
34  * the creation of the set (i.e. the search).
35  * The server saves a result temporarily if it is 'volatile', i.e. the
36  * set is register dependent.
37  *
38  * Revision 1.12  1995/12/07  17:38:47  adam
39  * Work locking mechanisms for concurrent updates/commit.
40  *
41  * Revision 1.11  1995/12/06  13:58:26  adam
42  * Improved flushing of records - all flushes except the last one
43  * don't write the last accessed. Also flush takes place if record
44  * info occupy more than about 256k.
45  *
46  * Revision 1.10  1995/12/06  12:41:24  adam
47  * New command 'stat' for the index program.
48  * Filenames can be read from stdin by specifying '-'.
49  * Bug fix/enhancement of the transformation from terms to regular
50  * expressons in the search engine.
51  *
52  * Revision 1.9  1995/11/30  08:34:33  adam
53  * Started work on commit facility.
54  * Changed a few malloc/free to xmalloc/xfree.
55  *
56  * Revision 1.8  1995/11/28  14:26:21  adam
57  * Bug fix: recordId with constant wasn't right.
58  * Bug fix: recordId dictionary entry wasn't deleted when needed.
59  *
60  * Revision 1.7  1995/11/28  09:09:43  adam
61  * Zebra config renamed.
62  * Use setting 'recordId' to identify record now.
63  * Bug fix in recindex.c: rec_release_blocks was invokeded even
64  * though the blocks were already released.
65  * File traversal properly deletes records when needed.
66  *
67  * Revision 1.6  1995/11/25  10:24:06  adam
68  * More record fields - they are enumerated now.
69  * New options: flagStoreData flagStoreKey.
70  *
71  * Revision 1.5  1995/11/22  17:19:18  adam
72  * Record management uses the bfile system.
73  *
74  * Revision 1.4  1995/11/20  16:59:46  adam
75  * New update method: the 'old' keys are saved for each records.
76  *
77  * Revision 1.3  1995/11/16  15:34:55  adam
78  * Uses new record management system in both indexer and server.
79  *
80  * Revision 1.2  1995/11/15  19:13:08  adam
81  * Work on record management.
82  *
83  * Revision 1.1  1995/11/15  14:46:20  adam
84  * Started work on better record management system.
85  *
86  */
87 #include <stdio.h>
88 #include <assert.h>
89 #include <string.h>
90
91 #include "recindxp.h"
92
93 static void rec_write_head (Records p)
94 {
95     int r;
96
97     assert (p);
98     assert (p->index_BFile);
99
100     r = bf_write (p->index_BFile, 0, 0, sizeof(p->head), &p->head);    
101     if (r)
102     {
103         logf (LOG_FATAL|LOG_ERRNO, "write head of %s", p->index_fname);
104         exit (1);
105     }
106 }
107
108 static void rec_tmp_expand (Records p, int size, int dst_type)
109 {
110     if (p->tmp_size < size + 2048 ||
111         p->tmp_size < p->head.block_size[dst_type]*2)
112     {
113         xfree (p->tmp_buf);
114         p->tmp_size = size + p->head.block_size[dst_type]*2 + 2048;
115         p->tmp_buf = xmalloc (p->tmp_size);
116     }
117 }
118
119 static int read_indx (Records p, int sysno, void *buf, int itemsize, 
120                       int ignoreError)
121 {
122     int r;
123     int pos = (sysno-1)*itemsize;
124
125     r = bf_read (p->index_BFile, 1+pos/128, pos%128, itemsize, buf);
126     if (r != 1 && !ignoreError)
127     {
128         logf (LOG_FATAL|LOG_ERRNO, "read in %s at pos %ld",
129               p->index_fname, (long) pos);
130         abort ();
131         exit (1);
132     }
133     return r;
134 }
135
136 static void write_indx (Records p, int sysno, void *buf, int itemsize)
137 {
138     int pos = (sysno-1)*itemsize;
139
140     bf_write (p->index_BFile, 1+pos/128, pos%128, itemsize, buf);
141 }
142
143 static void rec_release_blocks (Records p, int sysno)
144 {
145     struct record_index_entry entry;
146     int freeblock, freenext;
147     int dst_type;
148
149     if (read_indx (p, sysno, &entry, sizeof(entry), 1) != 1)
150         return ;
151     p->head.total_bytes -= entry.size;
152     freeblock = entry.next;
153     assert (freeblock > 0);
154     dst_type = freeblock & 7;
155     assert (dst_type < REC_BLOCK_TYPES);
156     freeblock = freeblock / 8;
157     while (freeblock)
158     {
159         if (bf_read (p->data_BFile[dst_type], freeblock, 0, sizeof(freenext),
160                      &freenext) != 1)
161         {
162             logf (LOG_FATAL|LOG_ERRNO, "read in rec_del_single");
163             exit (1);
164         }
165         if (bf_write (p->data_BFile[dst_type], freeblock, 0, sizeof(freenext),
166                       &p->head.block_free[dst_type]))
167         {
168             logf (LOG_FATAL|LOG_ERRNO, "write in rec_del_single");
169             exit (1);
170         }
171         p->head.block_free[dst_type] = freeblock;
172         freeblock = freenext;
173         p->head.block_used[dst_type]--;
174     }
175 }
176
177 static void rec_delete_single (Records p, Record rec)
178 {
179     struct record_index_entry entry;
180
181     rec_release_blocks (p, rec->sysno);
182
183     entry.next = p->head.index_free;
184     entry.size = 0;
185     p->head.index_free = rec->sysno;
186     write_indx (p, rec->sysno, &entry, sizeof(entry));
187 }
188
189
190 static void rec_write_single (Records p, Record rec)
191 {
192     int i, size = 0;
193     char *cptr;
194     int dst_type = 0;
195     int no_written = 0;
196     int block_prev = -1, block_free;
197     struct record_index_entry entry;
198
199     for (i = 0; i < REC_NO_INFO; i++)
200         if (!rec->info[i])
201             size += sizeof(*rec->size);
202         else
203             size += sizeof(*rec->size) + rec->size[i];
204
205     for (i = 1; i<REC_BLOCK_TYPES; i++)
206         if (size >= p->head.block_move[i])
207             dst_type = i;
208
209     rec_tmp_expand (p, size, dst_type);
210
211     cptr = p->tmp_buf + sizeof(int);           /* a hack! */
212     for (i = 0; i < REC_NO_INFO; i++)
213     {
214         memcpy (cptr, &rec->size[i], sizeof(*rec->size));
215         cptr += sizeof(*rec->size);
216         if (rec->info[i])
217         {
218             memcpy (cptr, rec->info[i], rec->size[i]);
219             cptr += rec->size[i];
220         }
221     }
222     cptr = p->tmp_buf;
223     while (no_written < size)
224     {
225         block_free = p->head.block_free[dst_type];
226         if (block_free)
227         {
228             if (bf_read (p->data_BFile[dst_type],
229                          block_free, 0, sizeof(*p->head.block_free),
230                          &p->head.block_free[dst_type]) != 1)
231             {
232                 logf (LOG_FATAL|LOG_ERRNO, "read in %s at free block %d",
233                       p->data_fname[dst_type], block_free);
234             }
235         }
236         else
237             block_free = p->head.block_last[dst_type]++;
238         if (block_prev == -1)
239         {
240             entry.next = block_free*8 + dst_type;
241             entry.size = size;
242             p->head.total_bytes += size;
243             write_indx (p, rec->sysno, &entry, sizeof(entry));
244         }
245         else
246         {
247             memcpy (cptr, &block_free, sizeof(int));
248             bf_write (p->data_BFile[dst_type], block_prev, 0, 0, cptr);
249             cptr = p->tmp_buf + no_written;
250         }
251         block_prev = block_free;
252         no_written += p->head.block_size[dst_type] - sizeof(int);
253         p->head.block_used[dst_type]++;
254     }
255     assert (block_prev != -1);
256     block_free = 0;
257     memcpy (cptr, &block_free, sizeof(int));
258     bf_write (p->data_BFile[dst_type], block_prev, 0,
259               sizeof(int) + (p->tmp_buf+size) - cptr, cptr);
260 }
261
262 static void rec_update_single (Records p, Record rec)
263 {
264     rec_release_blocks (p, rec->sysno);
265     rec_write_single (p, rec);
266 }
267
268 Records rec_open (BFiles bfs, int rw)
269 {
270     Records p;
271     int i, r;
272
273     p = xmalloc (sizeof(*p));
274     p->rw = rw;
275     p->tmp_size = 1024;
276     p->tmp_buf = xmalloc (p->tmp_size);
277     p->index_fname = "recindex";
278     p->index_BFile = bf_open (bfs, p->index_fname, 128, rw);
279     if (p->index_BFile == NULL)
280     {
281         logf (LOG_FATAL|LOG_ERRNO, "open %s", p->index_fname);
282         exit (1);
283     }
284     r = bf_read (p->index_BFile, 0, 0, 0, p->tmp_buf);
285     switch (r)
286     {
287     case 0:
288         memcpy (p->head.magic, REC_HEAD_MAGIC, sizeof(p->head.magic));
289         p->head.index_free = 0;
290         p->head.index_last = 1;
291         p->head.no_records = 0;
292         p->head.total_bytes = 0;
293         for (i = 0; i<REC_BLOCK_TYPES; i++)
294         {
295             p->head.block_free[i] = 0;
296             p->head.block_last[i] = 1;
297             p->head.block_used[i] = 0;
298         }
299         p->head.block_size[0] = 128;
300         p->head.block_move[0] = 0;
301         for (i = 1; i<REC_BLOCK_TYPES; i++)
302         {
303             p->head.block_size[i] = p->head.block_size[i-1] * 4;
304             p->head.block_move[i] = p->head.block_size[i] * 3;
305         }
306         if (rw)
307             rec_write_head (p);
308         break;
309     case 1:
310         memcpy (&p->head, p->tmp_buf, sizeof(p->head));
311         if (memcmp (p->head.magic, REC_HEAD_MAGIC, sizeof(p->head.magic)))
312         {
313             logf (LOG_FATAL, "read %s. bad header", p->index_fname);
314             exit (1);
315         }
316         break;
317     }
318     for (i = 0; i<REC_BLOCK_TYPES; i++)
319     {
320         char str[80];
321         sprintf (str, "recdata%c", i + 'A');
322         p->data_fname[i] = xmalloc (strlen(str)+1);
323         strcpy (p->data_fname[i], str);
324         p->data_BFile[i] = NULL;
325     }
326     for (i = 0; i<REC_BLOCK_TYPES; i++)
327     {
328         if (!(p->data_BFile[i] = bf_open (bfs, p->data_fname[i],
329                                           p->head.block_size[i],
330                                           rw)))
331         {
332             logf (LOG_FATAL|LOG_ERRNO, "bf_open %s", p->data_fname[i]);
333             exit (1);
334         }
335     }
336     p->cache_max = 10;
337     p->cache_cur = 0;
338     p->record_cache = xmalloc (sizeof(*p->record_cache)*p->cache_max);
339     return p;
340 }
341
342 static void rec_cache_flush (Records p, int saveCount)
343 {
344     int i, j;
345
346     if (saveCount >= p->cache_cur)
347         saveCount = 0;
348     for (i = 0; i<p->cache_cur - saveCount; i++)
349     {
350         struct record_cache_entry *e = p->record_cache + i;
351         switch (e->flag)
352         {
353         case recordFlagNop:
354             break;
355         case recordFlagNew:
356             rec_write_single (p, e->rec);
357             break;
358         case recordFlagWrite:
359             rec_update_single (p, e->rec);
360             break;
361         case recordFlagDelete:
362             rec_delete_single (p, e->rec);
363             break;
364         }
365         rec_rm (&e->rec);
366     }
367     for (j = 0; j<saveCount; j++, i++)
368         memcpy (p->record_cache+j, p->record_cache+i,
369                 sizeof(*p->record_cache));
370     p->cache_cur = saveCount;
371 }
372
373 static Record *rec_cache_lookup (Records p, int sysno,
374                                  enum recordCacheFlag flag)
375 {
376     int i;
377     for (i = 0; i<p->cache_cur; i++)
378     {
379         struct record_cache_entry *e = p->record_cache + i;
380         if (e->rec->sysno == sysno)
381         {
382             if (flag != recordFlagNop && e->flag == recordFlagNop)
383                 e->flag = flag;
384             return &e->rec;
385         }
386     }
387     return NULL;
388 }
389
390 static void rec_cache_insert (Records p, Record rec, enum recordCacheFlag flag)
391 {
392     struct record_cache_entry *e;
393
394     if (p->cache_cur == p->cache_max)
395         rec_cache_flush (p, 1);
396     else if (p->cache_cur > 0)
397     {
398         int i, j;
399         int used = 0;
400         for (i = 0; i<p->cache_cur; i++)
401         {
402             Record r = (p->record_cache + i)->rec;
403             for (j = 0; j<REC_NO_INFO; j++)
404                 used += r->size[j];
405         }
406         if (used > 256000)
407             rec_cache_flush (p, 1);
408     }
409     assert (p->cache_cur < p->cache_max);
410
411     e = p->record_cache + (p->cache_cur)++;
412     e->flag = flag;
413     e->rec = rec_cp (rec);
414 }
415
416 void rec_close (Records *pp)
417 {
418     Records p = *pp;
419     int i;
420
421     assert (p);
422
423     rec_cache_flush (p, 0);
424     xfree (p->record_cache);
425
426     if (p->rw)
427         rec_write_head (p);
428
429     if (p->index_BFile)
430         bf_close (p->index_BFile);
431
432     for (i = 0; i<REC_BLOCK_TYPES; i++)
433     {
434         if (p->data_BFile[i])
435             bf_close (p->data_BFile[i]);
436         xfree (p->data_fname[i]);
437     }
438     xfree (p->tmp_buf);
439     xfree (p);
440     *pp = NULL;
441 }
442
443
444 Record rec_get (Records p, int sysno)
445 {
446     int i;
447     Record rec, *recp;
448     struct record_index_entry entry;
449     int freeblock, dst_type;
450     char *nptr, *cptr;
451
452     assert (sysno > 0);
453     assert (p);
454
455     if ((recp = rec_cache_lookup (p, sysno, recordFlagNop)))
456         return rec_cp (*recp);
457
458     if (!read_indx (p, sysno, &entry, sizeof(entry), 1))
459         return NULL;       /* record is not there! */
460
461     if (!entry.size)
462         return NULL;       /* record is deleted */
463
464     dst_type = entry.next & 7;
465     assert (dst_type < REC_BLOCK_TYPES);
466     freeblock = entry.next / 8;
467
468     assert (freeblock > 0);
469     
470     rec = xmalloc (sizeof(*rec));
471     rec_tmp_expand (p, entry.size, dst_type);
472
473     cptr = p->tmp_buf;
474     bf_read (p->data_BFile[dst_type], freeblock, 0, 0, cptr);
475     memcpy (&freeblock, cptr, sizeof(freeblock));
476
477     while (freeblock)
478     {
479         int tmp;
480
481         cptr += p->head.block_size[dst_type] - sizeof(freeblock);
482         
483         memcpy (&tmp, cptr, sizeof(tmp));
484         bf_read (p->data_BFile[dst_type], freeblock, 0, 0, cptr);
485         memcpy (&freeblock, cptr, sizeof(freeblock));
486         memcpy (cptr, &tmp, sizeof(tmp));
487     }
488
489     rec->sysno = sysno;
490     nptr = p->tmp_buf + sizeof(freeblock);
491     for (i = 0; i < REC_NO_INFO; i++)
492     {
493         memcpy (&rec->size[i], nptr, sizeof(*rec->size));
494         nptr += sizeof(*rec->size);
495         if (rec->size[i])
496         {
497             rec->info[i] = xmalloc (rec->size[i]);
498             memcpy (rec->info[i], nptr, rec->size[i]);
499             nptr += rec->size[i];
500         }
501         else
502             rec->info[i] = NULL;
503     }
504     rec_cache_insert (p, rec, recordFlagNop);
505     return rec;
506 }
507
508 Record rec_new (Records p)
509 {
510     int sysno, i;
511     Record rec;
512
513     assert (p);
514     rec = xmalloc (sizeof(*rec));
515     if (1 || p->head.index_free == 0)
516         sysno = (p->head.index_last)++;
517     else
518     {
519         struct record_index_entry entry;
520
521         read_indx (p, p->head.index_free, &entry, sizeof(entry), 0);
522         sysno = p->head.index_free;
523         p->head.index_free = entry.next;
524     }
525     (p->head.no_records)++;
526     rec->sysno = sysno;
527     for (i = 0; i < REC_NO_INFO; i++)
528     {
529         rec->info[i] = NULL;
530         rec->size[i] = 0;
531     }
532     rec_cache_insert (p, rec, recordFlagNew);
533     return rec;
534 }
535
536 void rec_del (Records p, Record *recpp)
537 {
538     Record *recp;
539
540     (p->head.no_records)--;
541     if ((recp = rec_cache_lookup (p, (*recpp)->sysno, recordFlagDelete)))
542     {
543         rec_rm (recp);
544         *recp = *recpp;
545     }
546     else
547     {
548         rec_cache_insert (p, *recpp, recordFlagDelete);
549         rec_rm (recpp);
550     }
551     *recpp = NULL;
552 }
553
554 void rec_put (Records p, Record *recpp)
555 {
556     Record *recp;
557
558     if ((recp = rec_cache_lookup (p, (*recpp)->sysno, recordFlagWrite)))
559     {
560         rec_rm (recp);
561         *recp = *recpp;
562     }
563     else
564     {
565         rec_cache_insert (p, *recpp, recordFlagWrite);
566         rec_rm (recpp);
567     }
568     *recpp = NULL;
569 }
570
571 void rec_rm (Record *recpp)
572 {
573     int i;
574
575     if (!*recpp)
576         return ;
577     for (i = 0; i < REC_NO_INFO; i++)
578         xfree ((*recpp)->info[i]);
579     xfree (*recpp);
580     *recpp = NULL;
581 }
582
583 Record rec_cp (Record rec)
584 {
585     Record n;
586     int i;
587
588     n = xmalloc (sizeof(*n));
589     n->sysno = rec->sysno;
590     for (i = 0; i < REC_NO_INFO; i++)
591         if (!rec->info[i])
592         {
593             n->info[i] = NULL;
594             n->size[i] = 0;
595         }
596         else
597         {
598             n->size[i] = rec->size[i];
599             n->info[i] = xmalloc (rec->size[i]);
600             memcpy (n->info[i], rec->info[i], rec->size[i]);
601         }
602     return n;
603 }
604
605
606 char *rec_strdup (const char *s, size_t *len)
607 {
608     char *p;
609
610     if (!s)
611     {
612         *len = 0;
613         return NULL;
614     }
615     *len = strlen(s)+1;
616     p = xmalloc (*len);
617     strcpy (p, s);
618     return p;
619 }
620