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