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