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