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