source: tags/ms_r16q2/ARBDB/adindex.cxx

Last change on this file was 14054, checked in by westram, 9 years ago
  • improve error handling in adcomm.cxx
    • dont export errors (if avoidable)
    • check error-results in callers
    • define common error generators to
      • get uniform messages
      • be able to identify exact source location (using svn-rev + source line)
    • added some missing checks for read/write failures
  • remove useless parameter 'mode' from gbcm_write_bin
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.9 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : adindex.cxx                                       //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "gb_key.h"
12#include "gb_undo.h"
13#include "gb_index.h"
14#include "gb_hashindex.h"
15#include "gb_ts.h"
16
17#include <arb_strbuf.h>
18
19#include <cctype>
20
21#define GB_INDEX_FIND(gbf, ifs, quark)                                  \
22    for (ifs = GBCONTAINER_IFS(gbf);                                    \
23         ifs;                                                           \
24         ifs = GB_INDEX_FILES_NEXT(ifs))                                \
25    {                                                                   \
26        if (ifs->key == quark) break;                                   \
27    }
28
29void GBENTRY::index_check_in() {
30    // write field in index table
31
32    GBCONTAINER *gfather = GB_GRANDPA(this);
33    if (gfather) {
34        GBQUARK quark = GB_KEY_QUARK(this);
35        gb_index_files *ifs;
36        GB_INDEX_FIND(gfather, ifs, quark);
37
38        if (ifs) { // if key is indexed
39            if (is_indexable()) {
40                if (flags2.is_indexed) {
41                    GB_internal_error("Double checked in");
42                }
43                else {
44                    GB_CSTR       content = GB_read_char_pntr(this);
45                    unsigned long idx;
46                    GB_CALC_HASH_INDEX(content, idx, ifs->hash_table_size, ifs->case_sens);
47                    ifs->nr_of_elements++;
48
49                    {
50                        GB_REL_IFES   *entries = GB_INDEX_FILES_ENTRIES(ifs);
51                        gb_if_entries *ifes    = (gb_if_entries *)gbm_get_mem(sizeof(gb_if_entries), GB_GBM_INDEX(this));
52
53                        SET_GB_IF_ENTRIES_NEXT(ifes, GB_ENTRIES_ENTRY(entries, idx));
54                        SET_GB_IF_ENTRIES_GBD(ifes, this);
55                        SET_GB_ENTRIES_ENTRY(entries, idx, ifes);
56                    }
57                    flags2.should_be_indexed = 1;
58                    flags2.is_indexed        = 1;
59                }
60            }
61        }
62    }
63}
64
65void GBENTRY::index_check_out() {
66    // remove entry from index table
67    if (flags2.is_indexed) {
68        GBCONTAINER *gfather = GB_GRANDPA(this);
69        GBQUARK      quark   = GB_KEY_QUARK(this);
70
71        flags2.is_indexed = 0;
72
73        gb_index_files *ifs;
74        GB_INDEX_FIND(gfather, ifs, quark);
75
76        GB_ERROR     error;
77        if (!ifs) error = "key is not indexed";
78        else {
79            error = GB_push_transaction(this);
80            if (!error) {
81                GB_CSTR content = GB_read_char_pntr(this);
82
83                if (!content) {
84                    error = GBS_global_string("can't read key value (%s)", GB_await_error());
85                }
86                else {
87                    unsigned long idx;
88                    GB_CALC_HASH_INDEX(content, idx, ifs->hash_table_size, ifs->case_sens);
89
90                    gb_if_entries *ifes2   = 0;
91                    GB_REL_IFES   *entries = GB_INDEX_FILES_ENTRIES(ifs);
92                    gb_if_entries *ifes;
93
94                    for (ifes = GB_ENTRIES_ENTRY(entries, idx); ifes; ifes = GB_IF_ENTRIES_NEXT(ifes)) {
95                        if (this == GB_IF_ENTRIES_GBD(ifes)) { // entry found
96                            if (ifes2) SET_GB_IF_ENTRIES_NEXT(ifes2, GB_IF_ENTRIES_NEXT(ifes));
97                            else SET_GB_ENTRIES_ENTRY(entries, idx, GB_IF_ENTRIES_NEXT(ifes));
98
99                            ifs->nr_of_elements--;
100                            gbm_free_mem(ifes, sizeof(gb_if_entries), GB_GBM_INDEX(this));
101                            break;
102                        }
103                        ifes2 = ifes;
104                    }
105                }
106            }
107            error = GB_end_transaction(this, error);
108        }
109
110        if (error) {
111            error = GBS_global_string("GBENTRY::index_check_out failed for key '%s' (%s)\n", GB_KEY(this), error);
112            GB_internal_error(error);
113        }
114    }
115}
116
117GB_ERROR GB_create_index(GBDATA *gbd, const char *key, GB_CASE case_sens, long estimated_size) { // goes to header: __ATTR__USERESULT
118    /* Create an index for a database.
119     * Uses hash tables - collisions are avoided by using linked lists.
120     */
121    GB_ERROR error = 0;
122
123    if (gbd->is_entry()) {
124        error = "GB_create_index used on non CONTAINER Type";
125    }
126    else if (GB_read_clients(gbd)<0) {
127        error = "No index tables in DB clients allowed";
128    }
129    else {
130        GBCONTAINER *gbc       = gbd->as_container();
131        GBQUARK      key_quark = GB_find_or_create_quark(gbd, key);
132
133        gb_index_files *ifs;
134        GB_INDEX_FIND(gbc, ifs, key_quark);
135
136        if (!ifs) { // if not already have index (e.g. if fast-loaded)
137            ifs = (gb_index_files *)gbm_get_mem(sizeof(gb_index_files), GB_GBM_INDEX(gbc));
138            SET_GB_INDEX_FILES_NEXT(ifs, GBCONTAINER_IFS(gbc));
139            SET_GBCONTAINER_IFS(gbc, ifs);
140
141            ifs->key             = key_quark;
142            ifs->hash_table_size = gbs_get_a_prime(estimated_size);
143            ifs->nr_of_elements  = 0;
144            ifs->case_sens       = case_sens;
145
146            SET_GB_INDEX_FILES_ENTRIES(ifs, (gb_if_entries **)gbm_get_mem(sizeof(void *)*(int)ifs->hash_table_size, GB_GBM_INDEX(gbc)));
147
148            for (GBDATA *gbf = GB_find_sub_by_quark(gbd, -1, 0, 0);
149                 gbf;
150                 gbf = GB_find_sub_by_quark(gbd, -1, gbf, 0))
151            {
152                if (gbf->is_container()) {
153                    for (GBDATA *gb2 = GB_find_sub_by_quark(gbf, key_quark, 0, 0);
154                         gb2;
155                         gb2 = GB_find_sub_by_quark(gbf, key_quark, gb2, 0))
156                    {
157                        if (gb2->is_indexable()) gb2->as_entry()->index_check_in();
158                    }
159                }
160            }
161        }
162    }
163    RETURN_ERROR(error);
164}
165
166void gb_destroy_indices(GBCONTAINER *gbc) {
167    gb_index_files *ifs = GBCONTAINER_IFS(gbc);
168
169    while (ifs) {
170        GB_REL_IFES *if_entries = GB_INDEX_FILES_ENTRIES(ifs);
171
172        for (int index = 0; index<ifs->hash_table_size; index++) {
173            gb_if_entries *ifes = GB_ENTRIES_ENTRY(if_entries, index);
174
175            while (ifes) {
176                gb_if_entries *ifes_next = GB_IF_ENTRIES_NEXT(ifes);
177
178                gbm_free_mem(ifes, sizeof(*ifes), GB_GBM_INDEX(gbc));
179                ifes = ifes_next;
180            }
181        }
182        gbm_free_mem(if_entries, sizeof(void *)*(int)ifs->hash_table_size, GB_GBM_INDEX(gbc));
183
184        gb_index_files *ifs_next = GB_INDEX_FILES_NEXT(ifs);
185        gbm_free_mem(ifs, sizeof(gb_index_files), GB_GBM_INDEX(gbc));
186        ifs = ifs_next;
187    }
188}
189
190#if defined(DEBUG)
191
192NOT4PERL void GB_dump_indices(GBDATA *gbd) { // used for debugging
193    // dump indices of container
194
195    char *db_path = strdup(GB_get_db_path(gbd));
196    if (gbd->is_entry()) {
197        fprintf(stderr, "'%s' (%s) is no container.\n", db_path, GB_get_type_name(gbd));
198    }
199    else {
200        gb_index_files *ifs;
201        int             index_count = 0;
202
203        GBCONTAINER  *gbc  = gbd->as_container();
204        GB_MAIN_TYPE *Main = GBCONTAINER_MAIN(gbc);
205
206        for (ifs = GBCONTAINER_IFS(gbc); ifs; ifs = GB_INDEX_FILES_NEXT(ifs)) {
207            index_count++;
208        }
209
210        if (index_count == 0) {
211            fprintf(stderr, "Container '%s' has no index.\n", db_path);
212        }
213        else {
214            int pass;
215
216            fprintf(stderr, "Indices for '%s':\n", db_path);
217            for (pass = 1; pass <= 2; pass++) {
218                if (pass == 2) {
219                    fprintf(stderr, "\nDetailed index contents:\n\n");
220                }
221                index_count = 0;
222                for (ifs = GBCONTAINER_IFS(gbc); ifs; ifs = GB_INDEX_FILES_NEXT(ifs)) {
223                    fprintf(stderr,
224                            "* Index %i for key=%s (%i), entries=%li, %s\n",
225                            index_count,
226                            quark2key(Main, ifs->key),
227                            ifs->key,
228                            ifs->nr_of_elements,
229                            ifs->case_sens == GB_MIND_CASE
230                            ? "Case sensitive"
231                            : (ifs->case_sens == GB_IGNORE_CASE
232                               ? "Case insensitive"
233                               : "<Error in case_sens>")
234                            );
235
236                    if (pass == 2) {
237                        gb_if_entries *ifes;
238                        int            index;
239
240                        fprintf(stderr, "\n");
241                        for (index = 0; index<ifs->hash_table_size; index++) {
242                            for (ifes = GB_ENTRIES_ENTRY(GB_INDEX_FILES_ENTRIES(ifs), index);
243                                 ifes;
244                                 ifes = GB_IF_ENTRIES_NEXT(ifes))
245                            {
246                                GBDATA     *igbd = GB_IF_ENTRIES_GBD(ifes);
247                                const char *data = GB_read_char_pntr(igbd);
248
249                                fprintf(stderr, "  - '%s' (@idx=%i)\n", data, index);
250                            }
251                        }
252                        fprintf(stderr, "\n");
253                    }
254                    index_count++;
255                }
256            }
257        }
258    }
259
260    free(db_path);
261}
262
263#endif // DEBUG
264
265
266// find an entry in an hash table
267GBDATA *gb_index_find(GBCONTAINER *gbf, gb_index_files *ifs, GBQUARK quark, const char *val, GB_CASE case_sens, int after_index) {
268    unsigned long  index;
269    GB_CSTR        data;
270    gb_if_entries *ifes;
271    GBDATA        *result = 0;
272    long           min_index;
273
274    if (!ifs) {
275        GB_INDEX_FIND(gbf, ifs, quark);
276        if (!ifs) {
277            GB_internal_error("gb_index_find called, but no index table found");
278            return 0;
279        }
280    }
281
282    if (ifs->case_sens != case_sens) {
283        GB_internal_error("case mismatch between index and search");
284        return 0;
285    }
286
287    GB_CALC_HASH_INDEX(val, index, ifs->hash_table_size, ifs->case_sens);
288    min_index = gbf->d.nheader;
289
290    for (ifes = GB_ENTRIES_ENTRY(GB_INDEX_FILES_ENTRIES(ifs), index);
291            ifes;
292            ifes = GB_IF_ENTRIES_NEXT(ifes))
293    {
294        GBDATA *igbd = GB_IF_ENTRIES_GBD(ifes);
295        GBCONTAINER *ifather = GB_FATHER(igbd);
296
297        if (ifather->index < after_index) continue;
298        if (ifather->index >= min_index) continue;
299        data = GB_read_char_pntr(igbd);
300        if (GBS_string_matches(data, val, case_sens)) { // entry found
301            result    = igbd;
302            min_index = ifather->index;
303        }
304    }
305    return result;
306}
307
308
309/* UNDO functions
310 *
311 * There are three undo stacks:
312 *
313 * GB_UNDO_NONE    no undo
314 * GB_UNDO_UNDO    normal undo stack
315 * GB_UNDO_REDO    redo stack
316 */
317
318static char *gb_set_undo_type(GBDATA *gb_main, GB_UNDO_TYPE type) {
319    GB_MAIN_TYPE *Main = GB_MAIN(gb_main);
320    Main->undo_type = type;
321    return 0;
322}
323
324static void g_b_add_size_to_undo_entry(g_b_undo_entry *ue, long size) {
325    ue->sizeof_this                 += size;        // undo entry
326    ue->father->sizeof_this         += size;        // one undo
327    ue->father->father->sizeof_this += size;        // all undos
328}
329
330static g_b_undo_entry *new_g_b_undo_entry(g_b_undo_list *u) {
331    g_b_undo_entry *ue = (g_b_undo_entry *)gbm_get_mem(sizeof(g_b_undo_entry), GBM_UNDO);
332
333    ue->next   = u->entries;
334    ue->father = u;
335    u->entries = ue;
336
337    g_b_add_size_to_undo_entry(ue, sizeof(g_b_undo_entry));
338
339    return ue;
340}
341
342
343
344void gb_init_undo_stack(GB_MAIN_TYPE *Main) { // @@@ move into GB_MAIN_TYPE-ctor
345    Main->undo = (g_b_undo_mgr *)GB_calloc(sizeof(g_b_undo_mgr), 1);
346    Main->undo->max_size_of_all_undos = GB_MAX_UNDO_SIZE;
347    Main->undo->u = (g_b_undo_header *) GB_calloc(sizeof(g_b_undo_header), 1);
348    Main->undo->r = (g_b_undo_header *) GB_calloc(sizeof(g_b_undo_header), 1);
349}
350
351static void delete_g_b_undo_entry(g_b_undo_entry *entry) {
352    switch (entry->type) {
353        case GB_UNDO_ENTRY_TYPE_MODIFY:
354        case GB_UNDO_ENTRY_TYPE_MODIFY_ARRAY:
355            {
356                if (entry->d.ts) {
357                    gb_del_ref_gb_transaction_save(entry->d.ts);
358                }
359            }
360        default:
361            break;
362    }
363    gbm_free_mem(entry, sizeof(g_b_undo_entry), GBM_UNDO);
364}
365
366static void delete_g_b_undo_list(g_b_undo_list *u) {
367    g_b_undo_entry *a, *next;
368    for (a = u->entries; a; a = next) {
369        next = a->next;
370        delete_g_b_undo_entry(a);
371    }
372    free(u);
373}
374
375static void delete_g_b_undo_header(g_b_undo_header *uh) {
376    g_b_undo_list *a, *next=0;
377    for (a = uh->stack; a; a = next) {
378        next = a->next;
379        delete_g_b_undo_list(a);
380    }
381    free(uh);
382}
383
384static char *g_b_check_undo_size2(g_b_undo_header *uhs, long size, long max_cnt) {
385    long           csize = 0;
386    long           ccnt  = 0;
387    g_b_undo_list *us;
388
389    for (us = uhs->stack; us && us->next;  us = us->next) {
390        csize += us->sizeof_this;
391        ccnt ++;
392        if (((csize + us->next->sizeof_this) > size) ||
393             (ccnt >= max_cnt)) {  // delete the rest
394            g_b_undo_list *a, *next=0;
395
396            for (a = us->next; a; a = next) {
397                next = a->next;
398                delete_g_b_undo_list(a);
399            }
400            us->next = 0;
401            uhs->sizeof_this = csize;
402            break;
403        }
404    }
405    return 0;
406}
407
408static char *g_b_check_undo_size(GB_MAIN_TYPE *Main) {
409    long  maxsize     = Main->undo->max_size_of_all_undos;
410    char *error       = g_b_check_undo_size2(Main->undo->u, maxsize/2, GB_MAX_UNDO_CNT);
411    if (!error) error = g_b_check_undo_size2(Main->undo->r, maxsize/2, GB_MAX_REDO_CNT);
412    return error;
413}
414
415
416void gb_free_undo_stack(GB_MAIN_TYPE *Main) {
417    delete_g_b_undo_header(Main->undo->u);
418    delete_g_b_undo_header(Main->undo->r);
419    free(Main->undo);
420}
421
422// -------------------------
423//      real undo (redo)
424
425static GB_ERROR undo_entry(g_b_undo_entry *ue) {
426    GB_ERROR error = 0;
427    switch (ue->type) {
428        case GB_UNDO_ENTRY_TYPE_CREATED:
429            error = GB_delete(ue->source);
430            break;
431
432        case GB_UNDO_ENTRY_TYPE_DELETED: {
433            GBDATA *gbd = ue->d.gs.gbd;
434            if (gbd->is_container()) {
435                gbd = gb_make_pre_defined_container(ue->source->as_container(), gbd->as_container(), -1, ue->d.gs.key);
436            }
437            else {
438                gbd = gb_make_pre_defined_entry(ue->source->as_container(), gbd, -1, ue->d.gs.key);
439            }
440            GB_ARRAY_FLAGS(gbd).flags = ue->flag;
441            gb_touch_header(GB_FATHER(gbd));
442            gb_touch_entry(gbd, GB_CREATED);
443            break;
444        }
445        case GB_UNDO_ENTRY_TYPE_MODIFY_ARRAY:
446        case GB_UNDO_ENTRY_TYPE_MODIFY: {
447            GBDATA *gbd = ue->source;
448            if (gbd->is_entry()) {
449                GBENTRY *gbe = gbd->as_entry();
450                gb_save_extern_data_in_ts(gbe); // check out and free string
451
452                if (ue->d.ts) { // nothing to undo (e.g. if undoing GB_touch)
453                    gbe->flags              = ue->d.ts->flags;
454                    gbe->flags2.extern_data = ue->d.ts->flags2.extern_data;
455
456                    memcpy(&gbe->info, &ue->d.ts->info, sizeof(gbe->info)); // restore old information
457                    if (gbe->type() >= GB_BITS) {
458                        if (gbe->stored_external()) {
459                            gbe->info.ex.set_data(ue->d.ts->info.ex.data);
460                        }
461
462                        gb_del_ref_and_extern_gb_transaction_save(ue->d.ts);
463                        ue->d.ts = 0;
464
465                        gbe->index_re_check_in();
466                    }
467                }
468            }
469            {
470                gb_header_flags *pflags = &GB_ARRAY_FLAGS(gbd);
471                if (pflags->flags != (unsigned)ue->flag) {
472                    GBCONTAINER *gb_father = GB_FATHER(gbd);
473                    gbd->flags.saved_flags = pflags->flags;
474                    pflags->flags = ue->flag;
475                    if (GB_FATHER(gb_father)) {
476                        gb_touch_header(gb_father); // don't touch father of main
477                    }
478                }
479            }
480            gb_touch_entry(gbd, GB_NORMAL_CHANGE);
481            break;
482        }
483        default:
484            GB_internal_error("Undo stack corrupt:!!!");
485            error = GB_export_error("shit 34345");
486            break;
487    }
488
489    return error;
490}
491
492
493
494static GB_ERROR g_b_undo(GBDATA *gb_main, g_b_undo_header *uh) { // goes to header: __ATTR__USERESULT
495    GB_ERROR error = NULL;
496
497    if (!uh->stack) {
498        error = "Sorry no more undos/redos available";
499    }
500    else {
501        g_b_undo_list  *u = uh->stack;
502        g_b_undo_entry *ue, *next;
503
504        error = GB_begin_transaction(gb_main);
505
506        for (ue=u->entries; ue && !error; ue = next) {
507            next = ue->next;
508            error = undo_entry(ue);
509            delete_g_b_undo_entry(ue);
510            u->entries = next;
511        }
512        uh->sizeof_this -= u->sizeof_this;          // remove undo from list
513        uh->stack        = u->next;
514
515        delete_g_b_undo_list(u);
516        error = GB_end_transaction(gb_main, error);
517    }
518    return error;
519}
520
521static GB_CSTR g_b_read_undo_key_pntr(GB_MAIN_TYPE *Main, g_b_undo_entry *ue) {
522    return quark2key(Main, ue->d.gs.key);
523}
524
525static char *g_b_undo_info(GB_MAIN_TYPE *Main, g_b_undo_header *uh) {
526    GBS_strstruct  *res = GBS_stropen(1024);
527    g_b_undo_list  *u;
528    g_b_undo_entry *ue;
529
530    u = uh->stack;
531    if (!u) return strdup("No more undos available");
532    for (ue=u->entries; ue; ue = ue->next) {
533        switch (ue->type) {
534            case GB_UNDO_ENTRY_TYPE_CREATED:
535                GBS_strcat(res, "Delete new entry: ");
536                GBS_strcat(res, gb_read_key_pntr(ue->source));
537                break;
538            case GB_UNDO_ENTRY_TYPE_DELETED:
539                GBS_strcat(res, "Rebuild deleted entry: ");
540                GBS_strcat(res, g_b_read_undo_key_pntr(Main, ue));
541                break;
542            case GB_UNDO_ENTRY_TYPE_MODIFY_ARRAY:
543            case GB_UNDO_ENTRY_TYPE_MODIFY:
544                GBS_strcat(res, "Undo modified entry: ");
545                GBS_strcat(res, gb_read_key_pntr(ue->source));
546                break;
547            default:
548                break;
549        }
550        GBS_chrcat(res, '\n');
551    }
552    return GBS_strclose(res);
553}
554
555static char *gb_free_all_undos(GBDATA *gb_main) {
556    // Remove all existing undos/redos
557    GB_MAIN_TYPE  *Main = GB_MAIN(gb_main);
558    g_b_undo_list *a, *next;
559   
560    for (a = Main->undo->r->stack; a; a = next) {
561        next = a->next;
562        delete_g_b_undo_list(a);
563    }
564    Main->undo->r->stack = 0;
565    Main->undo->r->sizeof_this = 0;
566
567    for (a = Main->undo->u->stack; a; a = next) {
568        next = a->next;
569        delete_g_b_undo_list(a);
570    }
571    Main->undo->u->stack = 0;
572    Main->undo->u->sizeof_this = 0;
573    return 0;
574}
575
576
577char *gb_set_undo_sync(GBDATA *gb_main) {
578    // start a new undoable transaction
579    GB_MAIN_TYPE    *Main  = GB_MAIN(gb_main);
580    char            *error = g_b_check_undo_size(Main);
581    g_b_undo_header *uhs;
582
583    if (error) return error;
584    switch (Main->requested_undo_type) {    // init the target undo stack
585        case GB_UNDO_UNDO:      // that will undo but delete all redos
586            uhs         = Main->undo->u;
587            break;
588        case GB_UNDO_UNDO_REDO: uhs = Main->undo->u; break;
589        case GB_UNDO_REDO:      uhs = Main->undo->r; break;
590        case GB_UNDO_KILL:      gb_free_all_undos(gb_main);
591        default:            uhs = 0;
592    }
593    if (uhs)
594    {
595        g_b_undo_list *u = (g_b_undo_list *) GB_calloc(sizeof(g_b_undo_list),  1);
596        u->next = uhs->stack;
597        u->father = uhs;
598        uhs->stack = u;
599        Main->undo->valid_u = u;
600    }
601
602    return gb_set_undo_type(gb_main, Main->requested_undo_type);
603}
604
605char *gb_disable_undo(GBDATA *gb_main) {
606    // called to finish an undoable section, called at end of gb_commit_transaction
607    GB_MAIN_TYPE  *Main = GB_MAIN(gb_main);
608    g_b_undo_list *u    = Main->undo->valid_u;
609
610    if (!u) return 0;
611    if (!u->entries) {      // nothing to undo, just a read transaction
612        u->father->stack = u->next;
613        delete_g_b_undo_list(u);
614    }
615    else {
616        if (Main->requested_undo_type == GB_UNDO_UNDO) {    // remove all redos
617            g_b_undo_list *a, *next;
618
619            for (a = Main->undo->r->stack; a; a = next) {
620                next = a->next;
621                delete_g_b_undo_list(a);
622            }
623            Main->undo->r->stack = 0;
624            Main->undo->r->sizeof_this = 0;
625        }
626    }
627    Main->undo->valid_u = 0;
628    return gb_set_undo_type(gb_main, GB_UNDO_NONE);
629}
630
631void gb_check_in_undo_create(GB_MAIN_TYPE *Main, GBDATA *gbd) {
632    if (Main->undo->valid_u) {
633        g_b_undo_entry *ue = new_g_b_undo_entry(Main->undo->valid_u);
634
635        ue->type      = GB_UNDO_ENTRY_TYPE_CREATED;
636        ue->source    = gbd;
637        ue->gbm_index = GB_GBM_INDEX(gbd);
638        ue->flag      = 0;
639    }
640}
641
642void gb_check_in_undo_modify(GB_MAIN_TYPE *Main, GBDATA *gbd) {
643    if (!Main->undo->valid_u) {
644        GB_FREE_TRANSACTION_SAVE(gbd);
645    }
646    else {
647        gb_transaction_save *old = gbd->get_oldData();
648        g_b_undo_entry      *ue  = new_g_b_undo_entry(Main->undo->valid_u);
649
650        ue->source    = gbd;
651        ue->gbm_index = GB_GBM_INDEX(gbd);
652        ue->type      = GB_UNDO_ENTRY_TYPE_MODIFY;
653        ue->flag      = gbd->flags.saved_flags;
654
655        if (gbd->is_entry()) {
656            ue->d.ts = old;
657            if (old) {
658                gb_add_ref_gb_transaction_save(old);
659                if (gbd->type() >= GB_BITS && old->stored_external() && old->info.ex.data) {
660                    ue->type = GB_UNDO_ENTRY_TYPE_MODIFY_ARRAY;
661                    // move external array from ts to undo entry struct
662                    g_b_add_size_to_undo_entry(ue, old->info.ex.memsize);
663                }
664            }
665        }
666    }
667}
668
669void gb_check_in_undo_delete(GB_MAIN_TYPE *Main, GBDATA*& gbd) {
670    if (!Main->undo->valid_u) {
671        gb_delete_entry(gbd);
672        return;
673    }
674
675    if (gbd->is_container()) {
676        GBCONTAINER *gbc = gbd->as_container();
677        for (int index = 0; (index < gbc->d.nheader); index++) {
678            GBDATA *gbd2 = GBCONTAINER_ELEM(gbc, index);
679            if (gbd2) gb_check_in_undo_delete(Main, gbd2);
680        }
681    }
682    else {
683        gbd->as_entry()->index_check_out();
684        gbd->flags2.should_be_indexed = 0; // do not re-checkin
685    }
686    gb_abort_entry(gbd);            // get old version
687
688    g_b_undo_entry *ue = new_g_b_undo_entry(Main->undo->valid_u);
689
690    ue->type      = GB_UNDO_ENTRY_TYPE_DELETED;
691    ue->source    = GB_FATHER(gbd);
692    ue->gbm_index = GB_GBM_INDEX(gbd);
693    ue->flag      = GB_ARRAY_FLAGS(gbd).flags;
694
695    ue->d.gs.gbd = gbd;
696    ue->d.gs.key = GB_KEY_QUARK(gbd);
697
698    gb_pre_delete_entry(gbd);       // get the core of the entry
699
700    if (gbd->is_container()) {
701        g_b_add_size_to_undo_entry(ue, sizeof(GBCONTAINER));
702    }
703    else {
704        if (gbd->type() >= GB_BITS && gbd->as_entry()->stored_external()) {
705            /* we have copied the data structures, now
706               mark the old as deleted !!! */
707            g_b_add_size_to_undo_entry(ue, gbd->as_entry()->memsize());
708        }
709        g_b_add_size_to_undo_entry(ue, sizeof(GBENTRY));
710    }
711}
712
713// ----------------------------------------
714//      UNDO functions exported to USER
715
716GB_ERROR GB_request_undo_type(GBDATA *gb_main, GB_UNDO_TYPE type) { // goes to header: __ATTR__USERESULT_TODO
717    /*! Define how to undo DB changes.
718     *
719     * This function should be called just before opening a transaction,
720     * otherwise its effect will be delayed.
721     *
722     * Possible types are:
723     *      GB_UNDO_UNDO        enable undo
724     *      GB_UNDO_NONE        disable undo
725     *      GB_UNDO_KILL        disable undo and remove old undos !!
726     *
727     * Note: if GB_request_undo_type returns an error, local undo type remains unchanged
728     */
729
730    GB_MAIN_TYPE *Main  = GB_MAIN(gb_main);
731    GB_ERROR      error = NULL;
732
733    if (Main->is_client()) {
734        enum gb_undo_commands cmd = (type == GB_UNDO_NONE || type == GB_UNDO_KILL)
735            ? _GBCMC_UNDOCOM_REQUEST_NOUNDO
736            : _GBCMC_UNDOCOM_REQUEST_UNDO;
737        error = gbcmc_send_undo_commands(gb_main, cmd);
738    }
739    if (!error) Main->requested_undo_type = type;
740
741    return error;
742}
743
744GB_UNDO_TYPE GB_get_requested_undo_type(GBDATA *gb_main) {
745    GB_MAIN_TYPE *Main = GB_MAIN(gb_main);
746    return Main->requested_undo_type;
747}
748
749
750GB_ERROR GB_undo(GBDATA *gb_main, GB_UNDO_TYPE type) { // goes to header: __ATTR__USERESULT
751    // undo/redo the last transaction
752
753    GB_MAIN_TYPE *Main  = GB_MAIN(gb_main);
754    GB_ERROR      error = 0;
755
756    if (Main->is_client()) {
757        switch (type) {
758            case GB_UNDO_UNDO:
759                error = gbcmc_send_undo_commands(gb_main, _GBCMC_UNDOCOM_UNDO);
760                break;
761
762            case GB_UNDO_REDO:
763                error = gbcmc_send_undo_commands(gb_main, _GBCMC_UNDOCOM_REDO);
764                break;
765
766            default:
767                GB_internal_error("unknown undo type in GB_undo");
768                error = "Internal UNDO error";
769                break;
770        }
771    }
772    else {
773        GB_UNDO_TYPE old_type = GB_get_requested_undo_type(gb_main);
774        switch (type) {
775            case GB_UNDO_UNDO:
776                error = GB_request_undo_type(gb_main, GB_UNDO_REDO);
777                if (!error) {
778                    error = g_b_undo(gb_main, Main->undo->u);
779                    ASSERT_NO_ERROR(GB_request_undo_type(gb_main, old_type));
780                }
781                break;
782
783            case GB_UNDO_REDO:
784                error = GB_request_undo_type(gb_main, GB_UNDO_UNDO_REDO);
785                if (!error) {
786                    error = g_b_undo(gb_main, Main->undo->r);
787                    ASSERT_NO_ERROR(GB_request_undo_type(gb_main, old_type));
788                }
789                break;
790
791            default:
792                error = "GB_undo: unknown undo type specified";
793                break;
794        }
795    }
796
797    return error;
798}
799
800
801char *GB_undo_info(GBDATA *gb_main, GB_UNDO_TYPE type) {
802    // get some information about the next undo
803    // returns NULL in case of exported error
804
805    GB_MAIN_TYPE *Main = GB_MAIN(gb_main);
806    if (Main->is_client()) {
807        switch (type) {
808            case GB_UNDO_UNDO:
809                return gbcmc_send_undo_info_commands(gb_main, _GBCMC_UNDOCOM_INFO_UNDO);
810            case GB_UNDO_REDO:
811                return gbcmc_send_undo_info_commands(gb_main, _GBCMC_UNDOCOM_INFO_REDO);
812            default:
813                GB_export_error("GB_undo_info: unknown undo type specified");
814                return 0;
815        }
816    }
817    switch (type) {
818        case GB_UNDO_UNDO:
819            return g_b_undo_info(Main, Main->undo->u);
820        case GB_UNDO_REDO:
821            return g_b_undo_info(Main, Main->undo->r);
822        default:
823            GB_export_error("GB_undo_info: unknown undo type specified");
824            return 0;
825    }
826}
827
828GB_ERROR GB_set_undo_mem(GBDATA *gbd, long memsize) {
829    // set the maximum memory used for undoing
830
831    GB_MAIN_TYPE *Main = GB_MAIN(gbd);
832    if (memsize < _GBCMC_UNDOCOM_SET_MEM) {
833        return GB_export_errorf("Not enough UNDO memory specified: should be more than %i",
834                                _GBCMC_UNDOCOM_SET_MEM);
835    }
836    Main->undo->max_size_of_all_undos = memsize;
837    if (Main->is_client()) {
838        return gbcmc_send_undo_commands(gbd, (enum gb_undo_commands)memsize);
839    }
840    g_b_check_undo_size(Main);
841    return 0;
842}
843
844
845#ifdef UNIT_TESTS
846#include <test_unit.h>
847#include <map>
848#include <ad_cb_prot.h>
849
850class cb_counter : virtual Noncopyable {
851    GBDATA *gbd;
852    int deletes, changes, creates;
853    bool do_trace;
854public:
855    static void count_swapped(GBDATA* gbd, cb_counter* counter, GB_CB_TYPE t ) {
856        // CB system cannot swap parameters, we need to wrap
857        // And yes... fixed parameters are added *in the middle*, not at the end.
858        counter->count(gbd, t);
859    }
860   
861    cb_counter(GBDATA* gbd_) : gbd(gbd_), deletes(0), changes(0), creates(0), do_trace(false) {
862        GB_add_callback(gbd, GB_CB_ALL, makeDatabaseCallback(cb_counter::count_swapped, this));
863    }
864
865    ~cb_counter() {
866        // CB system cannot auto-destroy callbacks, nor are there CB handles
867        if (deletes == 0) { // (GB_delete destroys CBs I think)
868            GB_remove_callback(gbd, GB_CB_ALL, makeDatabaseCallback(cb_counter::count_swapped, this));
869            // above must be exact copy of GB_add_callback copy
870        }
871    }
872
873    void count(GBDATA*, GB_CB_TYPE t) {
874        if (t & GB_CB_DELETE) deletes ++;
875        if (t & GB_CB_SON_CREATED) creates ++;
876        if (t & GB_CB_CHANGED) changes ++;
877        if (do_trace) printf("counts: %p d=%i c=%i n=%i\n", gbd, deletes, changes, creates);
878    }
879
880    void trace(bool t) {
881        do_trace = t;
882    }
883
884    int get_deletes() {
885        int res = deletes;
886        deletes = 0;
887        return res;
888    }
889
890    int get_creates() {
891        int res = creates;
892        creates = 0;
893        return res;
894    }
895
896    int get_changes() {
897        int res = changes;
898        changes = 0;
899        return res;
900    }
901};
902
903
904
905void TEST_GB_undo__basic() {
906    GB_shell  shell;
907    // GB_ERROR err;
908
909    GBDATA   *main = GB_open("nosuch.arb", "c");
910    GB_begin_transaction(main);
911    cb_counter main_counter(main);
912    GB_commit_transaction(main);
913
914    GBDATA   *gbd;
915    cb_counter *gbd_counter;
916
917    GB_set_undo_mem(main, 10000);
918    GB_request_undo_type(main, GB_UNDO_UNDO);
919   
920
921    // Notes:
922    // - CB_CHANGED == CB_SON_CREATED
923    //   Both are called on creating sons as well as just writing strings.
924    // - GB_SON_CREATED also called if "SON_DELETED"
925    // - It's possible to get CB_SON_CREATED on GBENTRY if the CB is
926    //   registered within the running transaction.
927    // - CBs are triggered at the end of the transaction.
928
929
930    // test undo create empty string entry
931    GB_begin_transaction(main);
932    gbd = GB_create(main, "test", GB_STRING);
933    gbd_counter = new cb_counter(gbd);
934    GB_commit_transaction(main);
935
936    TEST_EXPECT_EQUAL( main_counter.get_creates(), 1);
937    TEST_EXPECT_EQUAL( main_counter.get_changes(), 1); 
938    TEST_EXPECT_EQUAL( main_counter.get_deletes(), 0); 
939    TEST_EXPECT_EQUAL( gbd_counter->get_creates(), 1);
940    TEST_EXPECT_EQUAL( gbd_counter->get_changes(), 1);
941    TEST_EXPECT_EQUAL( gbd_counter->get_deletes(), 0);
942    // string initialises as empty
943    TEST_EXPECT_EQUAL( GB_read_pntr(GB_find(main, "test", SEARCH_CHILD)), "");
944    TEST_EXPECT_NULL(  GB_undo(main, GB_UNDO_UNDO)                 ); 
945    TEST_EXPECT_EQUAL( main_counter.get_creates(), 1); // BROKEN -- should be 0
946    TEST_EXPECT_EQUAL( main_counter.get_changes(), 1); 
947    TEST_EXPECT_EQUAL( main_counter.get_deletes(), 0); // BROKEN -- should be 1
948    TEST_EXPECT_EQUAL( gbd_counter->get_creates(), 0);
949    TEST_EXPECT_EQUAL( gbd_counter->get_changes(), 0);
950    TEST_EXPECT_EQUAL( gbd_counter->get_deletes(), 1);
951    TEST_EXPECT_NULL(  GB_find(main, "test", SEARCH_CHILD)         ); 
952    TEST_EXPECT_NULL(  GB_undo(main, GB_UNDO_REDO)                 ); 
953    TEST_EXPECT_EQUAL( main_counter.get_creates(), 1);
954    TEST_EXPECT_EQUAL( main_counter.get_changes(), 1); 
955    TEST_EXPECT_EQUAL( main_counter.get_deletes(), 0); 
956    TEST_REJECT_NULL(  gbd = GB_find(main, "test", SEARCH_CHILD) ); 
957    TEST_EXPECT_EQUAL( GB_read_pntr(GB_find(main, "test", SEARCH_CHILD)), "");
958
959    // re-establish counter
960    GB_begin_transaction(main);
961    delete gbd_counter;
962    gbd_counter = new cb_counter(gbd);
963    GB_commit_transaction(main);
964
965    // test undo delete empty string
966    GB_begin_transaction(main);
967    GB_delete(gbd);
968    GB_commit_transaction(main);
969    TEST_EXPECT_EQUAL( main_counter.get_creates(), 1); // BROKEN -- should be 0
970    TEST_EXPECT_EQUAL( main_counter.get_changes(), 1); 
971    TEST_EXPECT_EQUAL( main_counter.get_deletes(), 0);
972    TEST_EXPECT_EQUAL( gbd_counter->get_creates(), 0);
973    TEST_EXPECT_EQUAL( gbd_counter->get_changes(), 0);
974    TEST_EXPECT_EQUAL( gbd_counter->get_deletes(), 1);
975    TEST_EXPECT_NULL(  GB_find(main, "test", SEARCH_CHILD)                   );
976    TEST_EXPECT_NULL(  GB_undo(main, GB_UNDO_UNDO)                           );
977    TEST_EXPECT_EQUAL( main_counter.get_creates(), 1);
978    TEST_EXPECT_EQUAL( main_counter.get_changes(), 1); 
979    TEST_EXPECT_EQUAL( main_counter.get_deletes(), 0); 
980    TEST_EXPECT_EQUAL( GB_read_pntr(GB_find(main, "test", SEARCH_CHILD)), "" );
981    TEST_EXPECT_NULL(  GB_undo(main, GB_UNDO_REDO)                           );
982    TEST_EXPECT_EQUAL( main_counter.get_creates(), 1); // BROKEN -- should be 0
983    TEST_EXPECT_EQUAL( main_counter.get_changes(), 1); 
984    TEST_EXPECT_EQUAL( main_counter.get_deletes(), 0); 
985    TEST_EXPECT_NULL(  GB_find(main, "test", SEARCH_CHILD)                   );
986    TEST_EXPECT_NULL(  GB_undo(main, GB_UNDO_UNDO)                           );
987    TEST_REJECT_NULL( gbd = GB_find(main, "test", SEARCH_CHILD) ); 
988    TEST_EXPECT_EQUAL( gbd_counter->get_creates(), 0);
989    TEST_EXPECT_EQUAL( gbd_counter->get_changes(), 0);
990    TEST_EXPECT_EQUAL( gbd_counter->get_deletes(), 0);
991
992    // re-establish counter
993    GB_begin_transaction(main);
994    delete gbd_counter;
995    gbd_counter = new cb_counter(gbd);
996    GB_commit_transaction(main);
997
998    // test undo write short string
999    const char* str = "testtest9012345";
1000    GB_begin_transaction(main);
1001    GB_write_string(gbd, str);
1002    GB_commit_transaction(main);
1003    TEST_EXPECT_EQUAL( gbd_counter->get_creates(), 1); // BROKEN?
1004    TEST_EXPECT_EQUAL( gbd_counter->get_changes(), 1);
1005    TEST_EXPECT_EQUAL( gbd_counter->get_deletes(), 0);
1006    TEST_EXPECT_EQUAL( GB_read_pntr(GB_find(main, "test", SEARCH_CHILD)), str);
1007    TEST_EXPECT_NULL(  GB_undo(main, GB_UNDO_UNDO)                           );
1008    TEST_EXPECT_EQUAL( gbd_counter->get_creates(), 1); // BROKEN?
1009    TEST_EXPECT_EQUAL( gbd_counter->get_changes(), 1);
1010    TEST_EXPECT_EQUAL( gbd_counter->get_deletes(), 0);
1011    TEST_EXPECT_EQUAL( GB_read_pntr(GB_find(main, "test", SEARCH_CHILD)), "");
1012    TEST_EXPECT_NULL(  GB_undo(main, GB_UNDO_REDO)                           );
1013    TEST_EXPECT_EQUAL( gbd_counter->get_creates(), 1); // BROKEN?
1014    TEST_EXPECT_EQUAL( gbd_counter->get_changes(), 1);
1015    TEST_EXPECT_EQUAL( gbd_counter->get_deletes(), 0);
1016    TEST_EXPECT_EQUAL( GB_read_pntr(GB_find(main, "test", SEARCH_CHILD)), str);
1017
1018    delete gbd_counter;
1019
1020    // test undo delete short string
1021    GB_begin_transaction(main);
1022    GB_delete(gbd);
1023    GB_commit_transaction(main);
1024    TEST_EXPECT_NULL(  GB_find(main, "test", SEARCH_CHILD)                   );
1025    TEST_EXPECT_NULL(  GB_undo(main, GB_UNDO_UNDO)                           );
1026
1027    //////////// THIS IS WHERE UNDO FAILS //////////////////
1028    TEST_EXPECT_EQUAL__BROKEN( GB_read_pntr(GB_find(main, "test", SEARCH_CHILD)), str, (char*)NULL);
1029    GB_close(main);
1030    return; // remainder will fail now
1031
1032
1033    TEST_EXPECT_NULL(  GB_undo(main, GB_UNDO_REDO)                           );
1034    TEST_EXPECT_NULL(  GB_find(main, "test", SEARCH_CHILD)                   );
1035    TEST_EXPECT_NULL(  GB_undo(main, GB_UNDO_UNDO)                           );
1036    TEST_REJECT_NULL( gbd = GB_find(main, "test", SEARCH_CHILD) ); 
1037
1038    // test undo write "" and delete short string
1039    GB_begin_transaction(main);
1040    GB_write_string(gbd, str);
1041    GB_delete(gbd);
1042    GB_commit_transaction(main);
1043    TEST_EXPECT_NULL(  GB_find(main, "test", SEARCH_CHILD)                   );
1044    TEST_EXPECT_NULL(  GB_undo(main, GB_UNDO_UNDO)                           );
1045    TEST_EXPECT_EQUAL( GB_read_pntr(GB_find(main, "test", SEARCH_CHILD)), str);
1046    TEST_EXPECT_NULL(  GB_undo(main, GB_UNDO_REDO)                           );
1047    TEST_EXPECT_NULL(  GB_find(main, "test", SEARCH_CHILD)                   );
1048    TEST_EXPECT_NULL(  GB_undo(main, GB_UNDO_UNDO)                           );
1049    TEST_REJECT_NULL( gbd = GB_find(main, "test", SEARCH_CHILD) ); 
1050
1051    //err = GB_write_string(gbd, "testtest9012345");
1052
1053    GB_close(main);
1054}
1055TEST_PUBLISH(TEST_GB_undo__basic);
1056
1057
1058#endif // UNIT_TESTS
Note: See TracBrowser for help on using the repository browser.