Experimental segment facility (for matching of words within one
[idzebra-moved-to-github.git] / index / extract.c
1 /* $Id: extract.c,v 1.227 2006-08-16 13:16:36 adam Exp $
2    Copyright (C) 1995-2006
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
21 */
22
23 #include <stdio.h>
24 #include <assert.h>
25 #include <ctype.h>
26 #ifdef WIN32
27 #include <io.h>
28 #endif
29 #if HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 #include <fcntl.h>
33
34 #include "index.h"
35 #include "orddict.h"
36 #include <direntz.h>
37 #include <charmap.h>
38
39 #define ENCODE_BUFLEN 768
40 struct encode_info {
41     void *encode_handle;
42     void *decode_handle;
43     char buf[ENCODE_BUFLEN];
44 };
45
46 static int log_level = 0;
47 static int log_level_initialized = 1;
48
49 static void zebra_init_log_level()
50 {
51     if (!log_level_initialized)
52     {
53         log_level = yaz_log_module_level("extract");
54         log_level_initialized = 1;
55     }
56 }
57
58 static void extract_flushRecordKeys (ZebraHandle zh, SYSNO sysno,
59                                      int cmd, zebra_rec_keys_t reckeys,
60                                      zint staticrank);
61 static void extract_flushSortKeys (ZebraHandle zh, SYSNO sysno,
62                                    int cmd, zebra_rec_keys_t skp);
63 static void extract_schema_add (struct recExtractCtrl *p, Odr_oid *oid);
64 static void extract_token_add (RecWord *p);
65
66 static void encode_key_init (struct encode_info *i);
67 static void encode_key_write (char *k, struct encode_info *i, FILE *outf);
68 static void encode_key_flush (struct encode_info *i, FILE *outf);
69
70 #define USE_SHELLSORT 0
71
72 #if USE_SHELLSORT
73 static void shellsort(void *ar, int r, size_t s,
74                       int (*cmp)(const void *a, const void *b))
75 {
76     char *a = ar;
77     char v[100];
78     int h, i, j, k;
79     static const int incs[16] = { 1391376, 463792, 198768, 86961, 33936,
80                                   13776, 4592, 1968, 861, 336, 
81                                   112, 48, 21, 7, 3, 1 };
82     for ( k = 0; k < 16; k++)
83         for (h = incs[k], i = h; i < r; i++)
84         { 
85             memcpy (v, a+s*i, s);
86             j = i;
87             while (j > h && (*cmp)(a + s*(j-h), v) > 0)
88             {
89                 memcpy (a + s*j, a + s*(j-h), s);
90                 j -= h;
91             }
92             memcpy (a+s*j, v, s);
93         } 
94 }
95 #endif
96
97 static void logRecord (ZebraHandle zh)
98 {
99     ++zh->records_processed;
100     if (!(zh->records_processed % 1000))
101     {
102         yaz_log(YLOG_LOG, "Records: "ZINT_FORMAT" i/u/d "
103                 ZINT_FORMAT"/"ZINT_FORMAT"/"ZINT_FORMAT, 
104                 zh->records_processed, zh->records_inserted, 
105                 zh->records_updated, zh->records_deleted);
106     }
107 }
108
109 static void extract_add_index_string (RecWord *p, 
110                                       zinfo_index_category_t cat,
111                                       const char *str, int length);
112
113 static void extract_set_store_data_prepare(struct recExtractCtrl *p);
114
115 static void extract_init(struct recExtractCtrl *p, RecWord *w)
116 {
117     w->seqno = 1;
118     w->index_name = "any";
119     w->index_type = 'w';
120     w->extractCtrl = p;
121     w->record_id = 0;
122     w->section_id = 0;
123     w->segment = 0;
124 }
125
126 static void searchRecordKey(ZebraHandle zh,
127                             zebra_rec_keys_t reckeys,
128                             const char *index_name,
129                             const char **ws, int ws_length)
130 {
131     int i;
132     int ch = -1;
133     zinfo_index_category_t cat = zinfo_index_category_index;
134
135     for (i = 0; i<ws_length; i++)
136         ws[i] = NULL;
137
138     if (ch < 0)
139         ch = zebraExplain_lookup_attr_str(zh->reg->zei, cat, '0', index_name);
140     if (ch < 0)
141         ch = zebraExplain_lookup_attr_str(zh->reg->zei, cat, 'p', index_name);
142     if (ch < 0)
143         ch = zebraExplain_lookup_attr_str(zh->reg->zei, cat, 'w', index_name);
144
145     if (ch < 0)
146         return ;
147
148     if (zebra_rec_keys_rewind(reckeys))
149     {
150         zint startSeq = -1;
151         const char *str;
152         size_t slen;
153         struct it_key key;
154         zint seqno;
155         while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
156         {
157             assert(key.len <= IT_KEY_LEVEL_MAX && key.len > 2);
158
159             seqno = key.mem[key.len-1];
160             
161             if (key.mem[0] == ch)
162             {
163                 zint woff;
164                 
165                 if (startSeq == -1)
166                     startSeq = seqno;
167                 woff = seqno - startSeq;
168                 if (woff >= 0 && woff < ws_length)
169                     ws[woff] = str;
170             }
171         }
172     }
173 }
174
175 struct file_read_info {
176     off_t file_max;         /* maximum offset so far */
177     off_t file_offset;      /* current offset */
178     off_t file_moffset;     /* offset of rec/rec boundary */
179     int file_more;
180     int fd;
181 };
182
183 static struct file_read_info *file_read_start (int fd)
184 {
185     struct file_read_info *fi = (struct file_read_info *)
186         xmalloc (sizeof(*fi));
187
188     fi->fd = fd;
189     fi->file_max = 0;
190     fi->file_moffset = 0;
191     fi->file_offset = 0;
192     fi->file_more = 0;
193     return fi;
194 }
195
196 static void file_read_stop (struct file_read_info *fi)
197 {
198     xfree (fi);
199 }
200
201 static off_t file_seek (void *handle, off_t offset)
202 {
203     struct file_read_info *p = (struct file_read_info *) handle;
204     p->file_offset = offset;
205     return lseek (p->fd, offset, SEEK_SET);
206 }
207
208 static off_t file_tell (void *handle)
209 {
210     struct file_read_info *p = (struct file_read_info *) handle;
211     return p->file_offset;
212 }
213
214 static int file_read (void *handle, char *buf, size_t count)
215 {
216     struct file_read_info *p = (struct file_read_info *) handle;
217     int fd = p->fd;
218     int r;
219     r = read (fd, buf, count);
220     if (r > 0)
221     {
222         p->file_offset += r;
223         if (p->file_offset > p->file_max)
224             p->file_max = p->file_offset;
225     }
226     return r;
227 }
228
229 static void file_end (void *handle, off_t offset)
230 {
231     struct file_read_info *p = (struct file_read_info *) handle;
232
233     if (offset != p->file_moffset)
234     {
235         p->file_moffset = offset;
236         p->file_more = 1;
237     }
238 }
239
240 #define FILE_MATCH_BLANK "\t "
241
242 static char *fileMatchStr (ZebraHandle zh,
243                            zebra_rec_keys_t reckeys,
244                            const char *fname, const char *spec)
245 {
246     static char dstBuf[2048];      /* static here ??? */
247     char *dst = dstBuf;
248     const char *s = spec;
249
250     while (1)
251     {
252         for (; *s && strchr(FILE_MATCH_BLANK, *s); s++)
253             ;
254         if (!*s)
255             break;
256         if (*s == '(')
257         {
258             const char *ws[32];
259             char attset_str[64], attname_str[64];
260             int i;
261             int first = 1;
262             
263             for (s++; strchr(FILE_MATCH_BLANK, *s); s++)
264                 ;
265             for (i = 0; *s && *s != ',' && *s != ')' && 
266                      !strchr(FILE_MATCH_BLANK, *s); s++)
267                 if (i+1 < sizeof(attset_str))
268                     attset_str[i++] = *s;
269             attset_str[i] = '\0';
270             
271             for (; strchr(FILE_MATCH_BLANK, *s); s++)
272                 ;
273             if (*s != ',')
274                 strcpy(attname_str, attset_str);
275             else
276             {
277                 for (s++; strchr(FILE_MATCH_BLANK, *s); s++)
278                     ;
279                 for (i = 0; *s && *s != ')' && 
280                          !strchr(FILE_MATCH_BLANK, *s); s++)
281                     if (i+1 < sizeof(attname_str))
282                         attname_str[i++] = *s;
283                 attname_str[i] = '\0';
284             }
285
286             searchRecordKey (zh, reckeys, attname_str, ws, 32);
287
288             if (*s != ')')
289             {
290                 yaz_log (YLOG_WARN, "Missing ) in match criteria %s in group %s",
291                       spec, zh->m_group ? zh->m_group : "none");
292                 return NULL;
293             }
294             s++;
295
296             for (i = 0; i<32; i++)
297                 if (ws[i])
298                 {
299                     if (first)
300                     {
301                         *dst++ = ' ';
302                         first = 0;
303                     }
304                     strcpy (dst, ws[i]);
305                     dst += strlen(ws[i]);
306                 }
307             if (first)
308             {
309                 yaz_log (YLOG_WARN, "Record didn't contain match"
310                       " fields in (%s,%s)", attset_str, attname_str);
311                 return NULL;
312             }
313         }
314         else if (*s == '$')
315         {
316             int spec_len;
317             char special[64];
318             const char *spec_src = NULL;
319             const char *s1 = ++s;
320             while (*s1 && !strchr(FILE_MATCH_BLANK, *s1))
321                 s1++;
322
323             spec_len = s1 - s;
324             if (spec_len > sizeof(special)-1)
325                 spec_len = sizeof(special)-1;
326             memcpy (special, s, spec_len);
327             special[spec_len] = '\0';
328             s = s1;
329
330             if (!strcmp (special, "group"))
331                 spec_src = zh->m_group;
332             else if (!strcmp (special, "database"))
333                 spec_src = zh->basenames[0];
334             else if (!strcmp (special, "filename")) {
335                 spec_src = fname;
336             }
337             else if (!strcmp (special, "type"))
338                 spec_src = zh->m_record_type;
339             else 
340                 spec_src = NULL;
341             if (spec_src)
342             {
343                 strcpy (dst, spec_src);
344                 dst += strlen (spec_src);
345             }
346         }
347         else if (*s == '\"' || *s == '\'')
348         {
349             int stopMarker = *s++;
350             char tmpString[64];
351             int i = 0;
352
353             while (*s && *s != stopMarker)
354             {
355                 if (i+1 < sizeof(tmpString))
356                     tmpString[i++] = *s++;
357             }
358             if (*s)
359                 s++;
360             tmpString[i] = '\0';
361             strcpy (dst, tmpString);
362             dst += strlen (tmpString);
363         }
364         else
365         {
366             yaz_log (YLOG_WARN, "Syntax error in match criteria %s in group %s",
367                   spec, zh->m_group ? zh->m_group : "none");
368             return NULL;
369         }
370         *dst++ = 1;
371     }
372     if (dst == dstBuf)
373     {
374         yaz_log (YLOG_WARN, "No match criteria for record %s in group %s",
375               fname, zh->m_group ? zh->m_group : "none");
376         return NULL;
377     }
378     *dst = '\0';
379     return dstBuf;
380 }
381
382 struct recordLogInfo {
383     const char *fname;
384     int recordOffset;
385     struct recordGroup *rGroup;
386 };
387
388 static void init_extractCtrl(ZebraHandle zh, struct recExtractCtrl *ctrl)
389 {
390     int i;
391     for (i = 0; i<256; i++)
392     {
393         if (zebra_maps_is_positioned(zh->reg->zebra_maps, i))
394             ctrl->seqno[i] = 1;
395         else
396             ctrl->seqno[i] = 0;
397     }
398     ctrl->flagShowRecords = !zh->m_flag_rw;
399 }
400
401 static void all_matches_add(struct recExtractCtrl *ctrl)
402 {
403     RecWord word;
404     extract_init(ctrl, &word);
405     word.index_name = "_ALLRECORDS";
406     word.index_type = 'w';
407     word.seqno = 1;
408     extract_add_index_string (&word, zinfo_index_category_alwaysmatches,
409                               "", 0);
410 }
411
412 static ZEBRA_RES file_extract_record(ZebraHandle zh,
413                                      SYSNO *sysno, const char *fname,
414                                      int deleteFlag,
415                                      struct file_read_info *fi,
416                                      int force_update,
417                                      RecType recType,
418                                      void *recTypeClientData)
419 {
420     const char *match_str_to_print = "";
421     RecordAttr *recordAttr;
422     int r;
423     const char *matchStr = 0;
424     SYSNO sysnotmp;
425     Record rec;
426     off_t recordOffset = 0;
427     struct recExtractCtrl extractCtrl;
428     
429     /* announce database */
430     if (zebraExplain_curDatabase (zh->reg->zei, zh->basenames[0]))
431     {
432         if (zebraExplain_newDatabase (zh->reg->zei, zh->basenames[0],
433                                       zh->m_explain_database))
434             return ZEBRA_FAIL;
435     }
436
437     if (fi->fd != -1)
438     {
439         /* we are going to read from a file, so prepare the extraction */
440         zebra_rec_keys_reset(zh->reg->keys);
441
442         zebra_rec_keys_reset(zh->reg->sortKeys);
443         recordOffset = fi->file_moffset;
444         extractCtrl.handle = zh;
445         extractCtrl.offset = fi->file_moffset;
446         extractCtrl.readf = file_read;
447         extractCtrl.seekf = file_seek;
448         extractCtrl.tellf = file_tell;
449         extractCtrl.endf = file_end;
450         extractCtrl.fh = fi;
451         extractCtrl.init = extract_init;
452         extractCtrl.tokenAdd = extract_token_add;
453         extractCtrl.schemaAdd = extract_schema_add;
454         extractCtrl.dh = zh->reg->dh;
455         extractCtrl.match_criteria[0] = '\0';
456         extractCtrl.staticrank = 0;
457         
458         extractCtrl.first_record = fi->file_offset ? 0 : 1;
459
460         extract_set_store_data_prepare(&extractCtrl);
461
462         init_extractCtrl(zh, &extractCtrl);
463
464         if (!zh->m_flag_rw)
465             printf ("File: %s " ZINT_FORMAT "\n", fname, (zint)recordOffset);
466         if (zh->m_flag_rw)
467         {
468             char msg[512];
469             sprintf (msg, "%s:" ZINT_FORMAT , fname, (zint)recordOffset);
470             yaz_log_init_prefix2 (msg);
471         }
472
473         r = (*recType->extract)(recTypeClientData, &extractCtrl);
474
475         yaz_log_init_prefix2 (0);
476         if (r == RECCTRL_EXTRACT_EOF)
477             return ZEBRA_FAIL;
478         else if (r == RECCTRL_EXTRACT_ERROR_GENERIC)
479         {
480             /* error occured during extraction ... */
481             if (zh->m_flag_rw &&
482                 zh->records_processed < zh->m_file_verbose_limit)
483             {
484                 yaz_log (YLOG_WARN, "fail %s %s " ZINT_FORMAT, 
485                          zh->m_record_type,
486                          fname, (zint) recordOffset);
487             }
488             return ZEBRA_FAIL;
489         }
490         else if (r == RECCTRL_EXTRACT_ERROR_NO_SUCH_FILTER)
491         {
492             /* error occured during extraction ... */
493             if (zh->m_flag_rw &&
494                 zh->records_processed < zh->m_file_verbose_limit)
495             {
496                 yaz_log (YLOG_WARN, "no filter for %s %s " 
497                       ZINT_FORMAT, zh->m_record_type,
498                       fname, (zint) recordOffset);
499             }
500             return ZEBRA_FAIL;
501         }
502         all_matches_add(&extractCtrl);
503         if (extractCtrl.match_criteria[0])
504             matchStr = extractCtrl.match_criteria;
505     }
506
507     /* if matchStr is set now - we assume it's printable .
508        For internal matchStr (see below) we don't print */
509     if (matchStr)
510         match_str_to_print = matchStr;
511
512     /* perform internal match if sysno not known and if match criteria is
513        specified already */
514     if (!sysno) 
515     {
516         sysnotmp = 0;
517         sysno = &sysnotmp;
518
519         if (matchStr == 0 && zh->m_record_id && *zh->m_record_id)
520         {
521             matchStr = fileMatchStr (zh, zh->reg->keys, fname, 
522                                      zh->m_record_id);
523             if (!matchStr)
524             {
525                 yaz_log(YLOG_WARN, "Bad match criteria");
526
527                 if (zebra_rec_keys_empty(zh->reg->keys))
528                 {
529                     yaz_log(YLOG_WARN, "And no index keys");
530                 }
531                 return ZEBRA_FAIL;
532             }
533         }
534         if (matchStr)
535         {
536             int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
537             char *rinfo = dict_lookup_ord(zh->reg->matchDict, db_ord,
538                                           matchStr);
539             if (rinfo)
540             {
541                 assert(*rinfo == sizeof(*sysno));
542                 memcpy (sysno, rinfo+1, sizeof(*sysno));
543             }
544         }
545     }
546     if (! *sysno && zebra_rec_keys_empty(zh->reg->keys) )
547     {
548          /* the extraction process returned no information - the record
549             is probably empty - unless flagShowRecords is in use */
550          if (!zh->m_flag_rw)
551              return ZEBRA_OK;
552   
553          if (zh->records_processed < zh->m_file_verbose_limit)
554              yaz_log(YLOG_WARN, "empty %s %s " ZINT_FORMAT, zh->m_record_type,
555                      fname, (zint)recordOffset);
556          return ZEBRA_OK;
557     }
558
559     if (! *sysno)
560     {
561         /* new record */
562         if (deleteFlag)
563         {
564             yaz_log(YLOG_LOG, "delete %s %s " ZINT_FORMAT, zh->m_record_type,
565                     fname, (zint)recordOffset);
566             yaz_log(YLOG_WARN, "cannot delete record above (seems new)");
567             return ZEBRA_OK;
568         }
569
570         rec = rec_new (zh->reg->records);
571         
572         *sysno = rec->sysno;
573         
574         if (zh->records_processed < zh->m_file_verbose_limit)
575         {
576             yaz_log(YLOG_LOG, "add %s %s " ZINT_FORMAT 
577                     " " ZINT_FORMAT " %s" ,
578                     zh->m_record_type,
579                     fname, (zint) recordOffset, *sysno, match_str_to_print);
580         }
581         recordAttr = rec_init_attr (zh->reg->zei, rec);
582         recordAttr->staticrank = extractCtrl.staticrank;
583
584         if (matchStr)
585         {
586             int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
587             dict_insert_ord(zh->reg->matchDict, db_ord, matchStr,
588                             sizeof(*sysno), sysno);
589         }
590
591
592         extract_flushSortKeys (zh, *sysno, 1, zh->reg->sortKeys);
593         extract_flushRecordKeys (zh, *sysno, 1, zh->reg->keys,
594                                  recordAttr->staticrank);
595         zh->records_inserted++;
596     }
597     else
598     {
599         /* record already exists */
600         zebra_rec_keys_t delkeys = zebra_rec_keys_open();
601
602         zebra_rec_keys_t sortKeys = zebra_rec_keys_open();
603
604         rec = rec_get (zh->reg->records, *sysno);
605         assert (rec);
606         
607         recordAttr = rec_init_attr (zh->reg->zei, rec);
608
609         zebra_rec_keys_set_buf(delkeys,
610                                rec->info[recInfo_delKeys],
611                                rec->size[recInfo_delKeys],
612                                0);
613
614         zebra_rec_keys_set_buf(sortKeys,
615                                rec->info[recInfo_sortKeys],
616                                rec->size[recInfo_sortKeys],
617                                0);
618         extract_flushSortKeys (zh, *sysno, 0, sortKeys);
619         extract_flushRecordKeys (zh, *sysno, 0, delkeys,
620                                  recordAttr->staticrank); /* old values */  
621         if (deleteFlag)
622         {
623             /* record going to be deleted */
624             if (zebra_rec_keys_empty(delkeys))
625             {
626                 yaz_log(YLOG_LOG, "delete %s %s " ZINT_FORMAT 
627                         " " ZINT_FORMAT,
628                         zh->m_record_type, fname, (zint)recordOffset, *sysno);
629                 yaz_log(YLOG_WARN, "cannot delete file above, storeKeys false (1)");
630             }
631             else
632             {
633                 if (zh->records_processed < zh->m_file_verbose_limit)
634                 {
635                     yaz_log(YLOG_LOG, "delete %s %s " ZINT_FORMAT 
636                             " " ZINT_FORMAT " %s" ,
637                             zh->m_record_type, fname, (zint) recordOffset,
638                             *sysno, match_str_to_print);
639                 }
640                 zh->records_deleted++;
641                 if (matchStr)
642                 {
643                     int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
644                     dict_delete_ord(zh->reg->matchDict, db_ord, matchStr);
645                 }
646                 rec_del (zh->reg->records, &rec);
647             }
648             rec_rm (&rec);
649             logRecord (zh);
650             return ZEBRA_OK;
651         }
652         else
653         {
654             /* flush new keys for sort&search etc */
655             if (zh->records_processed < zh->m_file_verbose_limit)
656             {
657                 yaz_log(YLOG_LOG, "update %s %s " ZINT_FORMAT 
658                         " " ZINT_FORMAT " %s" ,
659                         zh->m_record_type, fname, (zint) recordOffset, 
660                         *sysno, match_str_to_print);
661             }
662             recordAttr->staticrank = extractCtrl.staticrank;
663             extract_flushSortKeys (zh, *sysno, 1, zh->reg->sortKeys);
664             extract_flushRecordKeys (zh, *sysno, 1, zh->reg->keys,
665                                          recordAttr->staticrank);
666             zh->records_updated++;
667         }
668         zebra_rec_keys_close(delkeys);
669         zebra_rec_keys_close(sortKeys);
670     }
671     /* update file type */
672     xfree (rec->info[recInfo_fileType]);
673     rec->info[recInfo_fileType] =
674         rec_strdup (zh->m_record_type, &rec->size[recInfo_fileType]);
675
676     /* update filename */
677     xfree (rec->info[recInfo_filename]);
678     rec->info[recInfo_filename] =
679         rec_strdup (fname, &rec->size[recInfo_filename]);
680
681     /* update delete keys */
682     xfree (rec->info[recInfo_delKeys]);
683     if (!zebra_rec_keys_empty(zh->reg->keys) && zh->m_store_keys == 1)
684     {
685         zebra_rec_keys_get_buf(zh->reg->keys,
686                                &rec->info[recInfo_delKeys],
687                                &rec->size[recInfo_delKeys]);
688     }
689     else
690     {
691         rec->info[recInfo_delKeys] = NULL;
692         rec->size[recInfo_delKeys] = 0;
693     }
694
695     /* update sort keys */
696     xfree (rec->info[recInfo_sortKeys]);
697
698     zebra_rec_keys_get_buf(zh->reg->sortKeys,
699                            &rec->info[recInfo_sortKeys],
700                            &rec->size[recInfo_sortKeys]);
701
702     /* save file size of original record */
703     zebraExplain_recordBytesIncrement (zh->reg->zei,
704                                        - recordAttr->recordSize);
705     recordAttr->recordSize = fi->file_moffset - recordOffset;
706     if (!recordAttr->recordSize)
707         recordAttr->recordSize = fi->file_max - recordOffset;
708     zebraExplain_recordBytesIncrement (zh->reg->zei,
709                                        recordAttr->recordSize);
710
711     /* set run-number for this record */
712     recordAttr->runNumber = zebraExplain_runNumberIncrement (zh->reg->zei,
713                                                              0);
714
715     /* update store data */
716     xfree (rec->info[recInfo_storeData]);
717     if (zh->store_data_buf)
718     {
719         rec->size[recInfo_storeData] = zh->store_data_size;
720         rec->info[recInfo_storeData] = zh->store_data_buf;
721         zh->store_data_buf = 0;
722     }
723     else if (zh->m_store_data)
724     {
725         rec->size[recInfo_storeData] = recordAttr->recordSize;
726         rec->info[recInfo_storeData] = (char *)
727             xmalloc (recordAttr->recordSize);
728         if (lseek (fi->fd, recordOffset, SEEK_SET) < 0)
729         {
730             yaz_log(YLOG_ERRNO|YLOG_FATAL, "seek to " ZINT_FORMAT " in %s",
731                     (zint)recordOffset, fname);
732             exit (1);
733         }
734         if (read (fi->fd, rec->info[recInfo_storeData], recordAttr->recordSize)
735             < recordAttr->recordSize)
736         {
737             yaz_log (YLOG_ERRNO|YLOG_FATAL, "read %d bytes of %s",
738                   recordAttr->recordSize, fname);
739             exit (1);
740         }
741     }
742     else
743     {
744         rec->info[recInfo_storeData] = NULL;
745         rec->size[recInfo_storeData] = 0;
746     }
747     /* update database name */
748     xfree (rec->info[recInfo_databaseName]);
749     rec->info[recInfo_databaseName] =
750         rec_strdup (zh->basenames[0], &rec->size[recInfo_databaseName]); 
751
752     /* update offset */
753     recordAttr->recordOffset = recordOffset;
754     
755     /* commit this record */
756     rec_put (zh->reg->records, &rec);
757     logRecord (zh);
758     return ZEBRA_OK;
759 }
760
761 ZEBRA_RES zebra_extract_file(ZebraHandle zh, SYSNO *sysno, const char *fname, 
762                              int deleteFlag)
763 {
764     ZEBRA_RES r = ZEBRA_OK;
765     int i, fd;
766     char gprefix[128];
767     char ext[128];
768     char ext_res[128];
769     struct file_read_info *fi;
770     const char *original_record_type = 0;
771     RecType recType;
772     void *recTypeClientData;
773
774     zebra_init_log_level();
775
776     if (!zh->m_group || !*zh->m_group)
777         *gprefix = '\0';
778     else
779         sprintf (gprefix, "%s.", zh->m_group);
780     
781     yaz_log(log_level, "zebra_extract_file %s", fname);
782
783     /* determine file extension */
784     *ext = '\0';
785     for (i = strlen(fname); --i >= 0; )
786         if (fname[i] == '/')
787             break;
788         else if (fname[i] == '.')
789         {
790             strcpy (ext, fname+i+1);
791             break;
792         }
793     /* determine file type - depending on extension */
794     original_record_type = zh->m_record_type;
795     if (!zh->m_record_type)
796     {
797         sprintf (ext_res, "%srecordType.%s", gprefix, ext);
798         zh->m_record_type = res_get (zh->res, ext_res);
799     }
800     if (!zh->m_record_type)
801     {
802         if (zh->records_processed < zh->m_file_verbose_limit)
803             yaz_log (YLOG_LOG, "? %s", fname);
804         return 0;
805     }
806     /* determine match criteria */
807     if (!zh->m_record_id)
808     {
809         sprintf (ext_res, "%srecordId.%s", gprefix, ext);
810         zh->m_record_id = res_get (zh->res, ext_res);
811     }
812
813     if (!(recType =
814           recType_byName (zh->reg->recTypes, zh->res, zh->m_record_type,
815                           &recTypeClientData)))
816     {
817         yaz_log(YLOG_WARN, "No such record type: %s", zh->m_record_type);
818         return ZEBRA_FAIL;
819     }
820
821     switch(recType->version)
822     {
823     case 0:
824         break;
825     default:
826         yaz_log(YLOG_WARN, "Bad filter version: %s", zh->m_record_type);
827     }
828     if (sysno && deleteFlag)
829         fd = -1;
830     else
831     {
832         char full_rep[1024];
833
834         if (zh->path_reg && !yaz_is_abspath (fname))
835         {
836             strcpy (full_rep, zh->path_reg);
837             strcat (full_rep, "/");
838             strcat (full_rep, fname);
839         }
840         else
841             strcpy (full_rep, fname);
842         
843         if ((fd = open (full_rep, O_BINARY|O_RDONLY)) == -1)
844         {
845             yaz_log (YLOG_WARN|YLOG_ERRNO, "open %s", full_rep);
846             zh->m_record_type = original_record_type;
847             return ZEBRA_FAIL;
848         }
849     }
850     fi = file_read_start (fd);
851     while(1)
852     {
853         fi->file_moffset = fi->file_offset;
854         fi->file_more = 0;  /* file_end not called (yet) */
855         r = file_extract_record (zh, sysno, fname, deleteFlag, fi, 1,
856                                  recType, recTypeClientData);
857         if (fi->file_more)
858         {   /* file_end has been called so reset offset .. */
859             fi->file_offset = fi->file_moffset;
860             lseek(fi->fd, fi->file_moffset, SEEK_SET);
861         }
862         if (r != ZEBRA_OK)
863         {
864             break;
865         }
866         if (sysno)
867         {
868             break;
869         }
870     }
871     file_read_stop (fi);
872     if (fd != -1)
873         close (fd);
874     zh->m_record_type = original_record_type;
875     return r;
876 }
877
878 /*
879   If sysno is provided, then it's used to identify the reocord.
880   If not, and match_criteria is provided, then sysno is guessed
881   If not, and a record is provided, then sysno is got from there
882   
883  */
884 ZEBRA_RES zebra_buffer_extract_record(ZebraHandle zh, 
885                                       const char *buf, size_t buf_size,
886                                       int delete_flag,
887                                       int test_mode, 
888                                       const char *recordType,
889                                       SYSNO *sysno,
890                                       const char *match_criteria,
891                                       const char *fname,
892                                       int force_update,
893                                       int allow_update)
894 {
895     SYSNO sysno0 = 0;
896     RecordAttr *recordAttr;
897     struct recExtractCtrl extractCtrl;
898     int r;
899     const char *matchStr = 0;
900     RecType recType = NULL;
901     void *clientData;
902     Record rec;
903     long recordOffset = 0;
904     struct zebra_fetch_control fc;
905     const char *pr_fname = fname;  /* filename to print .. */
906     int show_progress = zh->records_processed < zh->m_file_verbose_limit ? 1:0;
907
908     zebra_init_log_level();
909
910     if (!pr_fname)
911         pr_fname = "<no file>";  /* make it printable if file is omitted */
912
913     fc.fd = -1;
914     fc.record_int_buf = buf;
915     fc.record_int_len = buf_size;
916     fc.record_int_pos = 0;
917     fc.offset_end = 0;
918     fc.record_offset = 0;
919
920     extractCtrl.offset = 0;
921     extractCtrl.readf = zebra_record_int_read;
922     extractCtrl.seekf = zebra_record_int_seek;
923     extractCtrl.tellf = zebra_record_int_tell;
924     extractCtrl.endf = zebra_record_int_end;
925     extractCtrl.first_record = 1;
926     extractCtrl.fh = &fc;
927
928     zebra_rec_keys_reset(zh->reg->keys);
929     zebra_rec_keys_reset(zh->reg->sortKeys);
930
931     if (zebraExplain_curDatabase (zh->reg->zei, zh->basenames[0]))
932     {
933         if (zebraExplain_newDatabase (zh->reg->zei, zh->basenames[0], 
934                                       zh->m_explain_database))
935             return ZEBRA_FAIL;
936     }
937     
938     if (recordType && *recordType)
939     {
940         yaz_log(log_level, "Record type explicitly specified: %s", recordType);
941         recType = recType_byName (zh->reg->recTypes, zh->res, recordType,
942                                   &clientData);
943     } 
944     else
945     {
946         if (!(zh->m_record_type))
947         {
948             yaz_log (YLOG_WARN, "No such record type defined");
949             return ZEBRA_FAIL;
950         }
951         yaz_log(log_level, "Get record type from rgroup: %s",
952                 zh->m_record_type);
953         recType = recType_byName (zh->reg->recTypes, zh->res,
954                                   zh->m_record_type, &clientData);
955         recordType = zh->m_record_type;
956     }
957     
958     if (!recType)
959     {
960         yaz_log (YLOG_WARN, "No such record type: %s", recordType);
961         return ZEBRA_FAIL;
962     }
963     
964     extractCtrl.init = extract_init;
965     extractCtrl.tokenAdd = extract_token_add;
966     extractCtrl.schemaAdd = extract_schema_add;
967     extractCtrl.dh = zh->reg->dh;
968     extractCtrl.handle = zh;
969     extractCtrl.match_criteria[0] = '\0';
970     extractCtrl.staticrank = 0;
971     
972     init_extractCtrl(zh, &extractCtrl);
973
974     extract_set_store_data_prepare(&extractCtrl);
975
976     r = (*recType->extract)(clientData, &extractCtrl);
977
978     if (r == RECCTRL_EXTRACT_EOF)
979         return ZEBRA_FAIL;
980     else if (r == RECCTRL_EXTRACT_ERROR_GENERIC)
981     {
982         /* error occured during extraction ... */
983         yaz_log (YLOG_WARN, "extract error: generic");
984         return ZEBRA_FAIL;
985     }
986     else if (r == RECCTRL_EXTRACT_ERROR_NO_SUCH_FILTER)
987     {
988         /* error occured during extraction ... */
989         yaz_log (YLOG_WARN, "extract error: no such filter");
990         return ZEBRA_FAIL;
991     }
992
993     all_matches_add(&extractCtrl);
994         
995     if (extractCtrl.match_criteria[0])
996         match_criteria = extractCtrl.match_criteria;
997
998     if (!sysno) {
999
1000         sysno = &sysno0;
1001
1002         if (match_criteria && *match_criteria) {
1003             matchStr = match_criteria;
1004         } else {
1005             if (zh->m_record_id && *zh->m_record_id) {
1006                 matchStr = fileMatchStr (zh, zh->reg->keys, pr_fname, 
1007                                          zh->m_record_id);
1008                 if (!matchStr)
1009                 {
1010                     yaz_log (YLOG_WARN, "Bad match criteria (recordID)");
1011                     return ZEBRA_FAIL;
1012                 }
1013             }
1014         }
1015         if (matchStr) 
1016         {
1017             int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
1018             char *rinfo = dict_lookup_ord(zh->reg->matchDict, db_ord,
1019                                           matchStr);
1020             if (rinfo)
1021             {
1022                 assert(*rinfo == sizeof(*sysno));
1023                 memcpy (sysno, rinfo+1, sizeof(*sysno));
1024             }
1025         }
1026     }
1027     if (zebra_rec_keys_empty(zh->reg->keys))
1028     {
1029         /* the extraction process returned no information - the record
1030            is probably empty - unless flagShowRecords is in use */
1031         if (test_mode)
1032             return ZEBRA_OK;
1033     }
1034
1035     if (! *sysno)
1036     {
1037         /* new record */
1038         if (delete_flag)
1039         {
1040             yaz_log (YLOG_LOG, "delete %s %s %ld", recordType,
1041                          pr_fname, (long) recordOffset);
1042             yaz_log (YLOG_WARN, "cannot delete record above (seems new)");
1043             return ZEBRA_FAIL;
1044         }
1045         if (show_progress)
1046             yaz_log (YLOG_LOG, "add %s %s %ld", recordType, pr_fname,
1047                      (long) recordOffset);
1048         rec = rec_new (zh->reg->records);
1049
1050         *sysno = rec->sysno;
1051
1052         recordAttr = rec_init_attr (zh->reg->zei, rec);
1053         recordAttr->staticrank = extractCtrl.staticrank;
1054
1055         if (matchStr)
1056         {
1057             int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
1058             dict_insert_ord(zh->reg->matchDict, db_ord, matchStr,
1059                             sizeof(*sysno), sysno);
1060         }
1061
1062
1063         extract_flushSortKeys (zh, *sysno, 1, zh->reg->sortKeys);
1064         extract_flushRecordKeys (zh, *sysno, 1, zh->reg->keys,
1065                          recordAttr->staticrank);
1066         zh->records_inserted++;
1067     } 
1068     else
1069     {
1070         /* record already exists */
1071         zebra_rec_keys_t delkeys = zebra_rec_keys_open();
1072         zebra_rec_keys_t sortKeys = zebra_rec_keys_open();
1073         if (!allow_update)
1074         {
1075             yaz_log (YLOG_LOG, "skipped %s %s %ld", 
1076                          recordType, pr_fname, (long) recordOffset);
1077             logRecord(zh);
1078             return ZEBRA_FAIL;
1079         }
1080
1081         rec = rec_get (zh->reg->records, *sysno);
1082         assert (rec);
1083         
1084         recordAttr = rec_init_attr (zh->reg->zei, rec);
1085
1086         zebra_rec_keys_set_buf(delkeys,
1087                                rec->info[recInfo_delKeys],
1088                                rec->size[recInfo_delKeys],
1089                                0);
1090         zebra_rec_keys_set_buf(sortKeys,
1091                                rec->info[recInfo_sortKeys],
1092                                rec->size[recInfo_sortKeys],
1093                                0);
1094
1095         extract_flushSortKeys (zh, *sysno, 0, sortKeys);
1096         extract_flushRecordKeys (zh, *sysno, 0, delkeys,
1097                                  recordAttr->staticrank);
1098         if (delete_flag)
1099         {
1100             /* record going to be deleted */
1101             if (zebra_rec_keys_empty(delkeys))
1102             {
1103                 yaz_log(YLOG_LOG, "delete %s %s %ld", recordType,
1104                         pr_fname, (long) recordOffset);
1105                 yaz_log(YLOG_WARN, "cannot delete file above, "
1106                         "storeKeys false (3)");
1107             }
1108             else
1109             {
1110                 if (show_progress)
1111                     yaz_log(YLOG_LOG, "delete %s %s %ld", recordType,
1112                             pr_fname, (long) recordOffset);
1113                 zh->records_deleted++;
1114                 if (matchStr)
1115                 {
1116                     int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
1117                     dict_delete_ord(zh->reg->matchDict, db_ord, matchStr);
1118                 }
1119                 rec_del (zh->reg->records, &rec);
1120             }
1121             rec_rm (&rec);
1122             logRecord(zh);
1123             return ZEBRA_OK;
1124         }
1125         else
1126         {
1127             if (show_progress)
1128                     yaz_log(YLOG_LOG, "update %s %s %ld", recordType,
1129                             pr_fname, (long) recordOffset);
1130             recordAttr->staticrank = extractCtrl.staticrank;
1131             extract_flushSortKeys (zh, *sysno, 1, zh->reg->sortKeys);
1132             extract_flushRecordKeys (zh, *sysno, 1, zh->reg->keys, 
1133                                          recordAttr->staticrank);
1134             zh->records_updated++;
1135         }
1136         zebra_rec_keys_close(delkeys);
1137         zebra_rec_keys_close(sortKeys);
1138     }
1139     /* update file type */
1140     xfree (rec->info[recInfo_fileType]);
1141     rec->info[recInfo_fileType] =
1142         rec_strdup (recordType, &rec->size[recInfo_fileType]);
1143
1144     /* update filename */
1145     xfree (rec->info[recInfo_filename]);
1146     rec->info[recInfo_filename] =
1147         rec_strdup (fname, &rec->size[recInfo_filename]);
1148
1149     /* update delete keys */
1150     xfree (rec->info[recInfo_delKeys]);
1151     if (!zebra_rec_keys_empty(zh->reg->keys) && zh->m_store_keys == 1)
1152     {
1153         zebra_rec_keys_get_buf(zh->reg->keys,
1154                                &rec->info[recInfo_delKeys],
1155                                &rec->size[recInfo_delKeys]);
1156     }
1157     else
1158     {
1159         rec->info[recInfo_delKeys] = NULL;
1160         rec->size[recInfo_delKeys] = 0;
1161     }
1162     /* update sort keys */
1163     xfree (rec->info[recInfo_sortKeys]);
1164
1165     zebra_rec_keys_get_buf(zh->reg->sortKeys,
1166                            &rec->info[recInfo_sortKeys],
1167                            &rec->size[recInfo_sortKeys]);
1168
1169     /* save file size of original record */
1170     zebraExplain_recordBytesIncrement (zh->reg->zei,
1171                                        - recordAttr->recordSize);
1172 #if 0
1173     recordAttr->recordSize = fi->file_moffset - recordOffset;
1174     if (!recordAttr->recordSize)
1175         recordAttr->recordSize = fi->file_max - recordOffset;
1176 #else
1177     recordAttr->recordSize = buf_size;
1178 #endif
1179     zebraExplain_recordBytesIncrement (zh->reg->zei,
1180                                        recordAttr->recordSize);
1181
1182     /* set run-number for this record */
1183     recordAttr->runNumber =
1184         zebraExplain_runNumberIncrement (zh->reg->zei, 0);
1185
1186     /* update store data */
1187     xfree (rec->info[recInfo_storeData]);
1188
1189     /* update store data */
1190     if (zh->store_data_buf)
1191     {
1192         rec->size[recInfo_storeData] = zh->store_data_size;
1193         rec->info[recInfo_storeData] = zh->store_data_buf;
1194         zh->store_data_buf = 0;
1195     }
1196     else if (zh->m_store_data)
1197     {
1198         rec->size[recInfo_storeData] = recordAttr->recordSize;
1199         rec->info[recInfo_storeData] = (char *)
1200             xmalloc (recordAttr->recordSize);
1201         memcpy (rec->info[recInfo_storeData], buf, recordAttr->recordSize);
1202     }
1203     else
1204     {
1205         rec->info[recInfo_storeData] = NULL;
1206         rec->size[recInfo_storeData] = 0;
1207     }
1208     /* update database name */
1209     xfree (rec->info[recInfo_databaseName]);
1210     rec->info[recInfo_databaseName] =
1211         rec_strdup (zh->basenames[0], &rec->size[recInfo_databaseName]); 
1212
1213     /* update offset */
1214     recordAttr->recordOffset = recordOffset;
1215     
1216     /* commit this record */
1217     rec_put (zh->reg->records, &rec);
1218     logRecord(zh);
1219     return ZEBRA_OK;
1220 }
1221
1222 ZEBRA_RES zebra_extract_explain(void *handle, Record rec, data1_node *n)
1223 {
1224     ZebraHandle zh = (ZebraHandle) handle;
1225     struct recExtractCtrl extractCtrl;
1226
1227     if (zebraExplain_curDatabase (zh->reg->zei,
1228                                   rec->info[recInfo_databaseName]))
1229     {
1230         abort();
1231         if (zebraExplain_newDatabase (zh->reg->zei,
1232                                       rec->info[recInfo_databaseName], 0))
1233             abort ();
1234     }
1235
1236     zebra_rec_keys_reset(zh->reg->keys);
1237     zebra_rec_keys_reset(zh->reg->sortKeys);
1238
1239     extractCtrl.init = extract_init;
1240     extractCtrl.tokenAdd = extract_token_add;
1241     extractCtrl.schemaAdd = extract_schema_add;
1242     extractCtrl.dh = zh->reg->dh;
1243
1244     init_extractCtrl(zh, &extractCtrl);
1245
1246     extractCtrl.flagShowRecords = 0;
1247     extractCtrl.match_criteria[0] = '\0';
1248     extractCtrl.staticrank = 0;
1249     extractCtrl.handle = handle;
1250     extractCtrl.first_record = 1;
1251     
1252     extract_set_store_data_prepare(&extractCtrl);
1253
1254     if (n)
1255         grs_extract_tree(&extractCtrl, n);
1256
1257     if (rec->size[recInfo_delKeys])
1258     {
1259         zebra_rec_keys_t delkeys = zebra_rec_keys_open();
1260         
1261         zebra_rec_keys_t sortkeys = zebra_rec_keys_open();
1262
1263         zebra_rec_keys_set_buf(delkeys, rec->info[recInfo_delKeys],
1264                                rec->size[recInfo_delKeys],
1265                                0);
1266         extract_flushRecordKeys (zh, rec->sysno, 0, delkeys, 0);
1267         zebra_rec_keys_close(delkeys);
1268
1269         zebra_rec_keys_set_buf(sortkeys, rec->info[recInfo_sortKeys],
1270                                rec->size[recInfo_sortKeys],
1271                                0);
1272
1273         extract_flushSortKeys (zh, rec->sysno, 0, sortkeys);
1274         zebra_rec_keys_close(sortkeys);
1275     }
1276     extract_flushRecordKeys (zh, rec->sysno, 1, zh->reg->keys, 0);
1277     extract_flushSortKeys (zh, rec->sysno, 1, zh->reg->sortKeys);
1278
1279     xfree (rec->info[recInfo_delKeys]);
1280     zebra_rec_keys_get_buf(zh->reg->keys,
1281                            &rec->info[recInfo_delKeys], 
1282                            &rec->size[recInfo_delKeys]);
1283
1284     xfree (rec->info[recInfo_sortKeys]);
1285     zebra_rec_keys_get_buf(zh->reg->sortKeys,
1286                            &rec->info[recInfo_sortKeys],
1287                            &rec->size[recInfo_sortKeys]);
1288     return ZEBRA_OK;
1289 }
1290
1291 void extract_rec_keys_adjust(ZebraHandle zh, int is_insert,
1292                              zebra_rec_keys_t reckeys)
1293 {
1294     ZebraExplainInfo zei = zh->reg->zei;
1295     struct ord_stat {
1296         int no;
1297         int ord;
1298         struct ord_stat *next;
1299     };
1300
1301     if (zebra_rec_keys_rewind(reckeys))
1302     {
1303         struct ord_stat *ord_list = 0;
1304         struct ord_stat *p;
1305         size_t slen;
1306         const char *str;
1307         struct it_key key_in;
1308         while(zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
1309         {
1310             int ord = CAST_ZINT_TO_INT(key_in.mem[0]);
1311
1312             for (p = ord_list; p ; p = p->next)
1313                 if (p->ord == ord)
1314                 {
1315                     p->no++;
1316                     break;
1317                 }
1318             if (!p)
1319             {
1320                 p = xmalloc(sizeof(*p));
1321                 p->no = 1;
1322                 p->ord = ord;
1323                 p->next = ord_list;
1324                 ord_list = p;
1325             }
1326         }
1327
1328         p = ord_list;
1329         while (p)
1330         {
1331             struct ord_stat *p1 = p;
1332
1333             if (is_insert)
1334                 zebraExplain_ord_adjust_occurrences(zei, p->ord, p->no, 1);
1335             else
1336                 zebraExplain_ord_adjust_occurrences(zei, p->ord, - p->no, -1);
1337             p = p->next;
1338             xfree(p1);
1339         }
1340     }
1341 }
1342
1343 void extract_flushRecordKeys (ZebraHandle zh, SYSNO sysno,
1344                               int cmd,
1345                               zebra_rec_keys_t reckeys,
1346                               zint staticrank)
1347 {
1348     ZebraExplainInfo zei = zh->reg->zei;
1349
1350     extract_rec_keys_adjust(zh, cmd, reckeys);
1351
1352     if (!zh->reg->key_buf)
1353     {
1354         int mem= 1024*1024* atoi( res_get_def( zh->res, "memmax", "8"));
1355         if (mem <= 0)
1356         {
1357             yaz_log(YLOG_WARN, "Invalid memory setting, using default 8 MB");
1358             mem= 1024*1024*8;
1359         }
1360         /* FIXME: That "8" should be in a default settings include */
1361         /* not hard-coded here! -H */
1362         zh->reg->key_buf = (char**) xmalloc (mem);
1363         zh->reg->ptr_top = mem/sizeof(char*);
1364         zh->reg->ptr_i = 0;
1365         zh->reg->key_buf_used = 0;
1366         zh->reg->key_file_no = 0;
1367     }
1368     zebraExplain_recordCountIncrement (zei, cmd ? 1 : -1);
1369
1370     if (zebra_rec_keys_rewind(reckeys))
1371     {
1372         size_t slen;
1373         const char *str;
1374         struct it_key key_in;
1375         while(zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
1376         {
1377             int ch = 0;
1378             int i, j = 0;
1379             struct it_key key_out;
1380
1381             assert(key_in.len >= 2);
1382             assert(key_in.len <= IT_KEY_LEVEL_MAX);
1383             
1384             /* check for buffer overflow */
1385             if (zh->reg->key_buf_used + 1024 > 
1386                 (zh->reg->ptr_top -zh->reg->ptr_i)*sizeof(char*))
1387                 extract_flushWriteKeys (zh, 0);
1388             
1389             ++(zh->reg->ptr_i);
1390             assert(zh->reg->ptr_i > 0);
1391             (zh->reg->key_buf)[zh->reg->ptr_top - zh->reg->ptr_i] =
1392                 (char*)zh->reg->key_buf + zh->reg->key_buf_used;
1393
1394             /* key_in.mem[0] ord/ch */
1395             /* key_in.mem[1] filter specified record ID */
1396
1397             /* encode the ordinal value (field/use/attribute) .. */
1398             ch = CAST_ZINT_TO_INT(key_in.mem[0]);
1399             zh->reg->key_buf_used +=
1400                 key_SU_encode(ch, (char*)zh->reg->key_buf +
1401                               zh->reg->key_buf_used);
1402
1403             /* copy the 0-terminated stuff from str to output */
1404             memcpy((char*)zh->reg->key_buf + zh->reg->key_buf_used, str, slen);
1405             zh->reg->key_buf_used += slen;
1406             ((char*)zh->reg->key_buf)[(zh->reg->key_buf_used)++] = '\0';
1407
1408             /* the delete/insert indicator */
1409             ((char*)zh->reg->key_buf)[(zh->reg->key_buf_used)++] = cmd;
1410
1411             if (zh->m_staticrank) /* rank config enabled ? */
1412             {
1413                 if (staticrank < 0)
1414                 {
1415                     yaz_log(YLOG_WARN, "staticrank = %ld. Setting to 0",
1416                             (long) staticrank);
1417                     staticrank = 0;
1418                 }
1419                 key_out.mem[j++] = staticrank;
1420             }
1421             
1422             if (key_in.mem[1]) /* filter specified record ID */
1423                 key_out.mem[j++] = key_in.mem[1];
1424             else
1425                 key_out.mem[j++] = sysno;
1426             for (i = 2; i < key_in.len; i++)
1427                 key_out.mem[j++] = key_in.mem[i];
1428             key_out.len = j;
1429
1430             memcpy((char*)zh->reg->key_buf + zh->reg->key_buf_used,
1431                    &key_out, sizeof(key_out));
1432             (zh->reg->key_buf_used) += sizeof(key_out);
1433         }
1434     }
1435 }
1436
1437 void extract_flushWriteKeys (ZebraHandle zh, int final)
1438         /* optimizing: if final=1, and no files written yet */
1439         /* push the keys directly to merge, sidestepping the */
1440         /* temp file altogether. Speeds small updates */
1441 {
1442     FILE *outf;
1443     char out_fname[200];
1444     char *prevcp, *cp;
1445     struct encode_info encode_info;
1446     int ptr_i = zh->reg->ptr_i;
1447     int temp_policy;
1448 #if SORT_EXTRA
1449     int i;
1450 #endif
1451     if (!zh->reg->key_buf || ptr_i <= 0)
1452     {
1453         yaz_log(log_level, "  nothing to flush section=%d buf=%p i=%d",
1454                zh->reg->key_file_no, zh->reg->key_buf, ptr_i);
1455         return;
1456     }
1457
1458     (zh->reg->key_file_no)++;
1459     yaz_log (YLOG_LOG, "sorting section %d", (zh->reg->key_file_no));
1460     yaz_log(log_level, "  sort_buff at %p n=%d",
1461                     zh->reg->key_buf + zh->reg->ptr_top - ptr_i,ptr_i);
1462 #if !SORT_EXTRA
1463     qsort (zh->reg->key_buf + zh->reg->ptr_top - ptr_i, ptr_i,
1464                sizeof(char*), key_qsort_compare);
1465
1466     /* zebra.cfg: tempfiles:  
1467        Y: always use temp files (old way) 
1468        A: use temp files, if more than one (auto) 
1469           = if this is both the last and the first 
1470        N: never bother with temp files (new) */
1471
1472     temp_policy=toupper(res_get_def(zh->res,"tempfiles","auto")[0]);
1473     if (temp_policy != 'Y' && temp_policy != 'N' && temp_policy != 'A') {
1474         yaz_log (YLOG_WARN, "Illegal tempfiles setting '%c'. using 'Auto' ", 
1475                         temp_policy);
1476         temp_policy='A';
1477     }
1478
1479     if (   ( temp_policy =='N' )   ||     /* always from memory */
1480          ( ( temp_policy =='A' ) &&       /* automatic */
1481              (zh->reg->key_file_no == 1) &&  /* this is first time */
1482              (final) ) )                     /* and last (=only) time */
1483     { /* go directly from memory */
1484         zh->reg->key_file_no =0; /* signal not to read files */
1485         zebra_index_merge(zh); 
1486         zh->reg->ptr_i = 0;
1487         zh->reg->key_buf_used = 0; 
1488         return; 
1489     }
1490
1491     /* Not doing directly from memory, write into a temp file */
1492     extract_get_fname_tmp (zh, out_fname, zh->reg->key_file_no);
1493
1494     if (!(outf = fopen (out_fname, "wb")))
1495     {
1496         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fopen %s", out_fname);
1497         exit (1);
1498     }
1499     yaz_log (YLOG_LOG, "writing section %d", zh->reg->key_file_no);
1500     prevcp = cp = (zh->reg->key_buf)[zh->reg->ptr_top - ptr_i];
1501     
1502     encode_key_init (&encode_info);
1503     encode_key_write (cp, &encode_info, outf);
1504     
1505     while (--ptr_i > 0)
1506     {
1507         cp = (zh->reg->key_buf)[zh->reg->ptr_top - ptr_i];
1508         if (strcmp (cp, prevcp))
1509         {
1510             encode_key_flush ( &encode_info, outf);
1511             encode_key_init (&encode_info);
1512             encode_key_write (cp, &encode_info, outf);
1513             prevcp = cp;
1514         }
1515         else
1516             encode_key_write (cp + strlen(cp), &encode_info, outf);
1517     }
1518     encode_key_flush ( &encode_info, outf);
1519 #else
1520     qsort (key_buf + ptr_top-ptr_i, ptr_i, sizeof(char*), key_x_compare);
1521     extract_get_fname_tmp (out_fname, key_file_no);
1522
1523     if (!(outf = fopen (out_fname, "wb")))
1524     {
1525         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fopen %s", out_fname);
1526         exit (1);
1527     }
1528     yaz_log (YLOG_LOG, "writing section %d", key_file_no);
1529     i = ptr_i;
1530     prevcp =  key_buf[ptr_top-i];
1531     while (1)
1532         if (!--i || strcmp (prevcp, key_buf[ptr_top-i]))
1533         {
1534             key_y_len = strlen(prevcp)+1;
1535 #if 0
1536             yaz_log (YLOG_LOG, "key_y_len: %2d %02x %02x %s",
1537                       key_y_len, prevcp[0], prevcp[1], 2+prevcp);
1538 #endif
1539             qsort (key_buf + ptr_top-ptr_i, ptr_i - i,
1540                                    sizeof(char*), key_y_compare);
1541             cp = key_buf[ptr_top-ptr_i];
1542             --key_y_len;
1543             encode_key_init (&encode_info);
1544             encode_key_write (cp, &encode_info, outf);
1545             while (--ptr_i > i)
1546             {
1547                 cp = key_buf[ptr_top-ptr_i];
1548                 encode_key_write (cp+key_y_len, &encode_info, outf);
1549             }
1550             encode_key_flush ( &encode_info, outf);
1551             if (!i)
1552                 break;
1553             prevcp = key_buf[ptr_top-ptr_i];
1554         }
1555 #endif
1556     if (fclose (outf))
1557     {
1558         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fclose %s", out_fname);
1559         exit (1);
1560     }
1561     yaz_log (YLOG_LOG, "finished section %d", zh->reg->key_file_no);
1562     zh->reg->ptr_i = 0;
1563     zh->reg->key_buf_used = 0;
1564 }
1565
1566 ZEBRA_RES zebra_snippets_rec_keys(ZebraHandle zh,
1567                                   zebra_rec_keys_t reckeys,
1568                                   zebra_snippets *snippets)
1569 {
1570     NMEM nmem = nmem_create();
1571     if (zebra_rec_keys_rewind(reckeys)) 
1572     {
1573         const char *str;
1574         size_t slen;
1575         struct it_key key;
1576         while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
1577         {
1578             char dst_buf[IT_MAX_WORD];
1579             char *dst_term = dst_buf;
1580             int ord;
1581             zint seqno;
1582             int index_type;
1583
1584             assert(key.len <= IT_KEY_LEVEL_MAX && key.len > 2);
1585             seqno = key.mem[key.len-1];
1586             ord = CAST_ZINT_TO_INT(key.mem[0]);
1587             
1588             zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type,
1589                                     0/* db */, 0 /* string_index */);
1590             assert(index_type);
1591             zebra_term_untrans_iconv(zh, nmem, index_type,
1592                                      &dst_term, str);
1593             zebra_snippets_append(snippets, seqno, ord, dst_term);
1594             nmem_reset(nmem);
1595         }
1596     }
1597     nmem_destroy(nmem);
1598     return ZEBRA_OK;
1599 }
1600
1601 void print_rec_keys(ZebraHandle zh, zebra_rec_keys_t reckeys)
1602 {
1603     yaz_log(YLOG_LOG, "print_rec_keys");
1604     if (zebra_rec_keys_rewind(reckeys))
1605     {
1606         const char *str;
1607         size_t slen;
1608         struct it_key key;
1609         while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
1610         {
1611             char dst_buf[IT_MAX_WORD];
1612             zint seqno;
1613             int index_type;
1614             int ord = CAST_ZINT_TO_INT(key.mem[0]);
1615             const char *db = 0;
1616             assert(key.len <= IT_KEY_LEVEL_MAX && key.len > 2);
1617
1618             zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type, &db, 0);
1619             
1620             seqno = key.mem[key.len-1];
1621             
1622             zebra_term_untrans(zh, index_type, dst_buf, str);
1623             
1624             yaz_log(YLOG_LOG, "ord=%d seqno=" ZINT_FORMAT 
1625                     " term=%s", ord, seqno, dst_buf); 
1626         }
1627     }
1628 }
1629
1630 static void extract_add_index_string(RecWord *p, zinfo_index_category_t cat,
1631                                      const char *str, int length)
1632 {
1633     struct it_key key;
1634     ZebraHandle zh = p->extractCtrl->handle;
1635     ZebraExplainInfo zei = zh->reg->zei;
1636     int ch, i;
1637
1638     ch = zebraExplain_lookup_attr_str(zei, cat, p->index_type, p->index_name);
1639     if (ch < 0)
1640         ch = zebraExplain_add_attr_str(zei, cat, p->index_type, p->index_name);
1641
1642     i = 0;
1643     key.mem[i++] = ch;
1644     key.mem[i++] = p->record_id;
1645     key.mem[i++] = p->section_id;
1646
1647     if (zh->m_segment_indexing)
1648         key.mem[i++] = p->segment;
1649     key.mem[i++] = p->seqno;
1650     key.len = i;
1651
1652     zebra_rec_keys_write(zh->reg->keys, str, length, &key);
1653 }
1654
1655 static void extract_add_sort_string(RecWord *p, const char *str, int length)
1656 {
1657     struct it_key key;
1658     ZebraHandle zh = p->extractCtrl->handle;
1659     ZebraExplainInfo zei = zh->reg->zei;
1660     int ch;
1661     zinfo_index_category_t cat = zinfo_index_category_sort;
1662
1663     ch = zebraExplain_lookup_attr_str(zei, cat, p->index_type, p->index_name);
1664     if (ch < 0)
1665         ch = zebraExplain_add_attr_str(zei, cat, p->index_type, p->index_name);
1666     key.len = 2;
1667     key.mem[0] = ch;
1668     key.mem[1] = p->record_id;
1669
1670     zebra_rec_keys_write(zh->reg->sortKeys, str, length, &key);
1671 }
1672
1673 static void extract_add_string(RecWord *p, const char *string, int length)
1674 {
1675     ZebraHandle zh = p->extractCtrl->handle;
1676     assert (length > 0);
1677
1678     if (!p->index_name)
1679         return;
1680
1681     if (zebra_maps_is_sort(zh->reg->zebra_maps, p->index_type))
1682         extract_add_sort_string(p, string, length);
1683     else
1684     {
1685         extract_add_index_string(p, zinfo_index_category_index,
1686                                  string, length);
1687         if (zebra_maps_is_alwaysmatches(zh->reg->zebra_maps, p->index_type))
1688         {
1689             RecWord word;
1690             memcpy(&word, p, sizeof(word));
1691
1692             word.seqno = 1;
1693             extract_add_index_string(
1694                 &word, zinfo_index_category_alwaysmatches, "", 0);
1695         }
1696     }
1697 }
1698
1699 static void extract_add_incomplete_field (RecWord *p)
1700 {
1701     ZebraHandle zh = p->extractCtrl->handle;
1702     const char *b = p->term_buf;
1703     int remain = p->term_len;
1704     const char **map = 0;
1705     
1706     if (remain > 0)
1707         map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b, remain, 0);
1708
1709     while (map)
1710     {
1711         char buf[IT_MAX_WORD+1];
1712         int i, remain;
1713
1714         /* Skip spaces */
1715         while (map && *map && **map == *CHR_SPACE)
1716         {
1717             remain = p->term_len - (b - p->term_buf);
1718             if (remain > 0)
1719                 map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b,
1720                                        remain, 0);
1721             else
1722                 map = 0;
1723         }
1724         if (!map)
1725             break;
1726         i = 0;
1727         while (map && *map && **map != *CHR_SPACE)
1728         {
1729             const char *cp = *map;
1730
1731             while (i < IT_MAX_WORD && *cp)
1732                 buf[i++] = *(cp++);
1733             remain = p->term_len - (b - p->term_buf);
1734             if (remain > 0)
1735                 map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b, remain, 0);
1736             else
1737                 map = 0;
1738         }
1739         if (!i)
1740             return;
1741         extract_add_string (p, buf, i);
1742         p->seqno++;
1743     }
1744 }
1745
1746 static void extract_add_complete_field (RecWord *p)
1747 {
1748     ZebraHandle zh = p->extractCtrl->handle;
1749     const char *b = p->term_buf;
1750     char buf[IT_MAX_WORD+1];
1751     const char **map = 0;
1752     int i = 0, remain = p->term_len;
1753
1754     if (remain > 0)
1755         map = zebra_maps_input (zh->reg->zebra_maps, p->index_type, &b, remain, 1);
1756
1757     while (remain > 0 && i < IT_MAX_WORD)
1758     {
1759         while (map && *map && **map == *CHR_SPACE)
1760         {
1761             remain = p->term_len - (b - p->term_buf);
1762
1763             if (remain > 0)
1764             {
1765                 int first = i ? 0 : 1;  /* first position */
1766                 map = zebra_maps_input(zh->reg->zebra_maps, p->index_type, &b, remain, first);
1767             }
1768             else
1769                 map = 0;
1770         }
1771         if (!map)
1772             break;
1773
1774         if (i && i < IT_MAX_WORD)
1775             buf[i++] = *CHR_SPACE;
1776         while (map && *map && **map != *CHR_SPACE)
1777         {
1778             const char *cp = *map;
1779
1780             if (**map == *CHR_CUT)
1781             {
1782                 i = 0;
1783             }
1784             else
1785             {
1786                 if (i >= IT_MAX_WORD)
1787                     break;
1788                 while (i < IT_MAX_WORD && *cp)
1789                     buf[i++] = *(cp++);
1790             }
1791             remain = p->term_len  - (b - p->term_buf);
1792             if (remain > 0)
1793             {
1794                 map = zebra_maps_input (zh->reg->zebra_maps, p->index_type, &b,
1795                                         remain, 0);
1796             }
1797             else
1798                 map = 0;
1799         }
1800     }
1801     if (!i)
1802         return;
1803     extract_add_string (p, buf, i);
1804 }
1805
1806 static void extract_token_add(RecWord *p)
1807 {
1808     ZebraHandle zh = p->extractCtrl->handle;
1809     WRBUF wrbuf;
1810     if (log_level)
1811         yaz_log(log_level, "extract_token_add "
1812                 "type=%c index=%s seqno=" ZINT_FORMAT " s=%.*s",
1813                 p->index_type, p->index_name, 
1814                 p->seqno, p->term_len, p->term_buf);
1815     if ((wrbuf = zebra_replace(zh->reg->zebra_maps, p->index_type, 0,
1816                                p->term_buf, p->term_len)))
1817     {
1818         p->term_buf = wrbuf_buf(wrbuf);
1819         p->term_len = wrbuf_len(wrbuf);
1820     }
1821     if (zebra_maps_is_complete (zh->reg->zebra_maps, p->index_type))
1822         extract_add_complete_field (p);
1823     else
1824         extract_add_incomplete_field(p);
1825 }
1826
1827 static void extract_set_store_data_cb(struct recExtractCtrl *p,
1828                                       void *buf, size_t sz)
1829 {
1830     ZebraHandle zh = (ZebraHandle) p->handle;
1831
1832     xfree(zh->store_data_buf);
1833     zh->store_data_buf = 0;
1834     zh->store_data_size = 0;
1835     if (buf && sz)
1836     {
1837         zh->store_data_buf = xmalloc(sz);
1838         zh->store_data_size = sz;
1839         memcpy(zh->store_data_buf, buf, sz);
1840     }
1841 }
1842
1843 static void extract_set_store_data_prepare(struct recExtractCtrl *p)
1844 {
1845     ZebraHandle zh = (ZebraHandle) p->handle;
1846     xfree(zh->store_data_buf);
1847     zh->store_data_buf = 0;
1848     zh->store_data_size = 0;
1849     p->setStoreData = extract_set_store_data_cb;
1850 }
1851
1852 static void extract_schema_add (struct recExtractCtrl *p, Odr_oid *oid)
1853 {
1854     ZebraHandle zh = (ZebraHandle) p->handle;
1855     zebraExplain_addSchema (zh->reg->zei, oid);
1856 }
1857
1858 void extract_flushSortKeys (ZebraHandle zh, SYSNO sysno,
1859                             int cmd, zebra_rec_keys_t reckeys)
1860 {
1861     if (zebra_rec_keys_rewind(reckeys))
1862     {
1863         SortIdx sortIdx = zh->reg->sortIdx;
1864         size_t slen;
1865         const char *str;
1866         struct it_key key_in;
1867
1868         sortIdx_sysno (sortIdx, sysno);
1869
1870         while (zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
1871         {
1872             int ord = CAST_ZINT_TO_INT(key_in.mem[0]);
1873             
1874             sortIdx_type(sortIdx, ord);
1875             if (cmd == 1)
1876                 sortIdx_add(sortIdx, str, slen);
1877             else
1878                 sortIdx_add(sortIdx, "", 1);
1879         }
1880     }
1881 }
1882
1883 static void encode_key_init(struct encode_info *i)
1884 {
1885     i->encode_handle = iscz1_start();
1886     i->decode_handle = iscz1_start();
1887 }
1888
1889 static void encode_key_write (char *k, struct encode_info *i, FILE *outf)
1890 {
1891     struct it_key key;
1892     char *bp = i->buf, *bp0;
1893     const char *src = (char *) &key;
1894
1895     /* copy term to output buf */
1896     while ((*bp++ = *k++))
1897         ;
1898     /* and copy & align key so we can mangle */
1899     memcpy (&key, k+1, sizeof(struct it_key));  /* *k is insert/delete */
1900
1901 #if 0
1902     /* debugging */
1903     key_logdump_txt(YLOG_LOG, &key, *k ? "i" : "d");
1904 #endif
1905     assert(key.mem[0] >= 0);
1906
1907     bp0 = bp++;
1908     iscz1_encode(i->encode_handle, &bp, &src);
1909
1910     *bp0 = (*k * 128) + bp - bp0 - 1; /* length and insert/delete combined */
1911     if (fwrite (i->buf, bp - i->buf, 1, outf) != 1)
1912     {
1913         yaz_log (YLOG_FATAL|YLOG_ERRNO, "fwrite");
1914         exit (1);
1915     }
1916
1917 #if 0
1918     /* debugging */
1919     if (1)
1920     {
1921         struct it_key key2;
1922         const char *src = bp0+1;
1923         char *dst = (char*) &key2;
1924         iscz1_decode(i->decode_handle, &dst, &src);
1925
1926         key_logdump_txt(YLOG_LOG, &key2, *k ? "i" : "d");
1927
1928         assert(key2.mem[1]);
1929     }
1930 #endif
1931 }
1932
1933 static void encode_key_flush (struct encode_info *i, FILE *outf)
1934
1935     iscz1_stop(i->encode_handle);
1936     iscz1_stop(i->decode_handle);
1937 }
1938
1939 /*
1940  * Local variables:
1941  * c-basic-offset: 4
1942  * indent-tabs-mode: nil
1943  * End:
1944  * vim: shiftwidth=4 tabstop=8 expandtab
1945  */
1946