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