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