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