source: branches/stable/ARBDB/ad_core.cxx

Last change on this file was 18665, checked in by westram, 3 years ago
  • change many WARN_TODO triggered warnings into todo markers.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.4 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : ad_core.cxx                                       //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "gb_ts.h"
12#include "gb_index.h"
13#include "gb_localdata.h"
14#include "ad_hcb.h"
15
16// Copy all info + external data mem to an one step undo buffer
17// (needed to abort transactions)
18
19inline void _GB_CHECK_IN_UNDO_DELETE(GB_MAIN_TYPE *Main, GBDATA *& gbd) {
20    if (Main->undo_type) gb_check_in_undo_delete(Main, gbd);
21    else gb_delete_entry(gbd);
22}
23inline void _GB_CHECK_IN_UNDO_CREATE(GB_MAIN_TYPE *Main, GBDATA *gbd) {
24    if (Main->undo_type) gb_check_in_undo_create(Main, gbd);
25}
26inline void _GB_CHECK_IN_UNDO_MODIFY(GB_MAIN_TYPE *Main, GBDATA *gbd) {
27    if (Main->undo_type) gb_check_in_undo_modify(Main, gbd);
28}
29
30// ---------------------------
31//      trigger callbacks
32//      (i.e. add them to pending callbacks)
33
34void GB_MAIN_TYPE::callback_group::trigger(GBDATA *gbd, GB_CB_TYPE type, gb_callback_list *dataCBs) {
35    gb_assert(implicated(!gbd->ext, gbd->is_container())); // expect old data was saved in ext (for normal data entries)
36    if (hierarchy_cbs) {
37        for (gb_hierarchy_callback_list::itertype cb = hierarchy_cbs->callbacks.begin(); cb != hierarchy_cbs->callbacks.end(); ++cb) {
38            if ((cb->spec.get_type() & type) && cb->triggered_by(gbd)) {
39                pending.add_unchecked(gb_triggered_callback(gbd, gbd->get_oldData(), cb->spec));
40            }
41        }
42    }
43    if (dataCBs) {
44        for (gb_callback_list::itertype cb = dataCBs->callbacks.begin(); cb != dataCBs->callbacks.end(); ++cb) {
45            if (cb->spec.get_type() & type) {
46                pending.add_unchecked(gb_triggered_callback(gbd, gbd->get_oldData(), cb->spec));
47            }
48        }
49    }
50}
51
52inline void GB_MAIN_TYPE::trigger_change_callbacks(GBDATA *gbd, GB_CB_TYPE type) {
53    changeCBs.trigger(gbd, type, gbd->get_callbacks());
54}
55
56void GB_MAIN_TYPE::trigger_delete_callbacks(GBDATA *gbd) {
57    gb_callback_list *cbl = gbd->get_callbacks();
58    if (cbl || deleteCBs.hierarchy_cbs) {
59        gb_assert(implicated(cbl, gbd->ext)); // gbd->ext may be NULp if gbd has no callback installed
60
61        if (gbd->ext) {
62            gbd->ext->callback = NULp;
63            if (!gbd->ext->old && gbd->type() != GB_DB) {
64                gb_save_extern_data_in_ts(gbd->as_entry());
65            }
66        }
67        else if (gbd->type() != GB_DB) {
68            gbd->create_extended();
69            // gbd->ext->old always NULp here
70            gb_save_extern_data_in_ts(gbd->as_entry());
71        }
72
73        gb_assert(implicated(!gbd->ext, gbd->is_container()));
74        deleteCBs.trigger(gbd, GB_CB_DELETE, cbl);
75
76        gb_assert(implicated(gbd->ext, !gbd->ext->callback));
77        delete cbl;
78    }
79}
80// ---------------------------
81//      GB data management
82
83void gb_touch_entry(GBDATA *gbd, GB_CHANGE val) {
84    gbd->flags2.update_in_server = 0;
85    GB_ARRAY_FLAGS(gbd).inc_change(val);
86
87    GBCONTAINER *gbc = GB_FATHER(gbd);
88    gbc->set_touched_idx(gbd->index);
89
90    while (1) {
91        GBCONTAINER *gbc_father = GB_FATHER(gbc);
92        if (!gbc_father) break;
93
94        gbc_father->set_touched_idx(gbc->index);
95
96        if (gbc->flags2.update_in_server) {
97            gbc->flags2.update_in_server = 0;
98        }
99        else {
100            if (GB_ARRAY_FLAGS(gbc).changed >= GB_SON_CHANGED)
101                return;
102        }
103        GB_ARRAY_FLAGS(gbc).inc_change(GB_SON_CHANGED);
104        gbc = gbc_father;
105    }
106}
107
108void gb_touch_header(GBCONTAINER *gbc) {
109    gbc->flags2.header_changed = 1;
110    gb_touch_entry(gbc, GB_NORMAL_CHANGE);
111}
112
113
114void gb_untouch_children(GBCONTAINER *gbc) {
115    GBDATA          *gbd;
116    int             index, start, end;
117    GB_CHANGE       changed;
118    gb_header_list *header = GB_DATA_LIST_HEADER(gbc->d);
119
120    if (gbc->index_of_touched_one_son > 0) {
121        start = (int)gbc->index_of_touched_one_son-1;
122        end = start + 1;
123    }
124    else {
125        if (!gbc->index_of_touched_one_son) {
126            start = end = 0;
127        }
128        else {
129            start = 0;
130            end = gbc->d.nheader;
131        }
132    }
133
134    for (index = start; index < end; index++) {
135        if ((gbd = GB_HEADER_LIST_GBD(header[index]))) {
136            changed = (GB_CHANGE)header[index].flags.changed;
137            if (changed != GB_UNCHANGED && changed < GB_DELETED) {
138                header[index].flags.changed = GB_UNCHANGED;
139                if (gbd->is_container()) {
140                    gb_untouch_children(gbd->as_container());
141                }
142            }
143            gbd->flags2.update_in_server = 0;
144        }
145    }
146    gbc->index_of_touched_one_son = 0;
147}
148
149void gb_untouch_me(GBENTRY *gbe) {
150    GB_DATA_LIST_HEADER(GB_FATHER(gbe)->d)[gbe->index].flags.changed = GB_UNCHANGED;
151}
152inline void gb_untouch_me(GBCONTAINER *gbc) {
153    GB_DATA_LIST_HEADER(GB_FATHER(gbc)->d)[gbc->index].flags.changed = GB_UNCHANGED;
154
155    gbc->flags2.header_changed    = 0;
156    gbc->index_of_touched_one_son = 0;
157}
158
159void gb_untouch_children_and_me(GBCONTAINER *gbc) {
160    gb_untouch_children(gbc);
161    gb_untouch_me(gbc);
162}
163
164static void gb_set_update_in_server_flags(GBCONTAINER *gbc) {
165    for (int index = 0; index < gbc->d.nheader; index++) {
166        GBDATA *gbd = GBCONTAINER_ELEM(gbc, index);
167        if (gbd) {
168            if (gbd->is_container()) {
169                gb_set_update_in_server_flags(gbd->as_container());
170            }
171            gbd->flags2.update_in_server = 1;
172        }
173    }
174}
175
176void gb_create_header_array(GBCONTAINER *gbc, int size) {
177    // creates or resizes an old array to children
178    gb_header_list *nl, *ol;
179
180    if (size <= gbc->d.headermemsize) return;
181    if (!size) return;
182    if (size > 10) size++;
183    if (size > 30) size = size*3/2;
184    nl = (gb_header_list *)gbm_get_mem(sizeof(gb_header_list)*size, GBM_HEADER_INDEX);
185
186    if ((ol=GB_DATA_LIST_HEADER(gbc->d))) {
187        int idx;
188        int maxidx = gbc->d.headermemsize; // ???: oder ->d.nheader
189
190        for (idx=0; idx<maxidx; idx++) {
191            GBDATA *gbd = GB_HEADER_LIST_GBD(ol[idx]);
192            nl[idx].flags =  ol[idx].flags;
193
194            if (gbd) {
195                gb_assert(gbd->server_id==GBTUM_MAGIC_NUMBER || GB_read_clients(gbd)<0); // or I am a client
196                SET_GB_HEADER_LIST_GBD(nl[idx], gbd);
197            }
198        }
199
200        gbm_free_mem(ol, sizeof(gb_header_list)*gbc->d.headermemsize, GBM_HEADER_INDEX);
201    }
202
203    gbc->d.headermemsize = size;
204    SET_GB_DATA_LIST_HEADER(gbc->d, nl);
205}
206
207static void gb_link_entry(GBCONTAINER* father, GBDATA *gbd, long index_pos) {
208    /* if index_pos == -1 -> to end of data;
209       else special index position; error when data already exists in index pos */
210
211    SET_GB_FATHER(gbd, father);
212    if (!father) return; // do not link 'main'-entry!
213
214    if (index_pos < 0) {
215        index_pos = father->d.nheader++;
216    }
217    else {
218        if (index_pos >= father->d.nheader) {
219            father->d.nheader = (int)index_pos+1;
220        }
221    }
222
223    gb_create_header_array(father, (int)index_pos+1);
224
225    if (GBCONTAINER_ELEM(father, index_pos)) {
226        GB_internal_error("Index of Databaseentry used twice");
227        index_pos = father->d.nheader++;
228        gb_create_header_array(father, (int)index_pos+1);
229    }
230
231    /* the following code skips just-deleted index position, while searching for an unused
232       index position. I'm unsure whether this works w/o problems (ralf 2004-Oct-08) */
233
234    while (GB_DATA_LIST_HEADER(father->d)[index_pos].flags.changed >= GB_DELETED) {
235#if defined(DEBUG)
236        fprintf(stderr, "Warning: index_pos %li of father(%p) contains just-deleted entry -> using next index_pos..\n", index_pos, father);
237#endif // DEBUG
238        index_pos = father->d.nheader++;
239        gb_create_header_array(father, (int)index_pos+1);
240    }
241
242    gbd->index = index_pos;
243    SET_GBCONTAINER_ELEM(father, index_pos, gbd);
244    father->d.size++;
245}
246
247static void gb_unlink_entry(GBDATA * gbd) {
248    GBCONTAINER *father = GB_FATHER(gbd);
249
250    if (father) {
251        int             index_pos = (int)gbd->index;
252        gb_header_list *hls       = &(GB_DATA_LIST_HEADER(father->d)[index_pos]);
253
254        SET_GB_HEADER_LIST_GBD(*hls, NULp);
255        hls->flags.key_quark    = 0;
256        hls->flags.set_change(GB_DELETED);
257        father->d.size--;
258        SET_GB_FATHER(gbd, NULp);
259    }
260}
261
262GB_MAIN_TYPE::GB_MAIN_TYPE(const char *db_path)
263    : transaction_level(0),
264      aborted_transaction(0),
265      i_am_server(false),
266      c_link(NULp),
267      server_data(NULp),
268      dummy_father(NULp),
269      root_container(NULp),
270      gb_key_data(NULp),
271      path(nulldup(db_path)),
272      opentype(gb_open_all),
273      disabled_path(NULp),
274      allow_corrupt_file_recovery(0),
275      compression_mask(-1), // allow all compressions
276      keycnt(0),
277      sizeofkeys(0),
278      first_free_key(0),
279      keys(NULp),
280      key_2_index_hash(GBS_create_hash(ALLOWED_KEYS, GB_MIND_CASE)),
281      key_clock(0),
282      mapped(false),
283      last_updated(0),
284      last_saved_time(0),
285      last_saved_transaction(0),
286      last_main_saved_transaction(0),
287      requested_undo_type(GB_UNDO_NONE),
288      undo_type(GB_UNDO_NONE),
289      undo(NULp),
290      security_level(0),
291      clock(0),
292      remote_hash(NULp),
293      close_callbacks(NULp),
294      this_user(NULp)
295{
296    for (int i = 0; i<ALLOWED_DATES; ++i) dates[i] = NULp;
297    for (int i = 0; i<GB_MAX_USERS;  ++i) users[i] = NULp;
298
299    gb_init_undo_stack(this);
300    gb_local->announce_db_open(this);
301}
302
303GB_MAIN_TYPE::~GB_MAIN_TYPE() {
304    gb_assert(!dummy_father);
305    gb_assert(!root_container);
306
307    release_main_idx();
308
309    if (remote_hash) GBS_free_numhash(remote_hash);
310
311    free_all_keys();
312   
313    if (key_2_index_hash) GBS_free_hash(key_2_index_hash);
314    freenull(keys);
315
316    gb_free_undo_stack(this);
317
318    for (int j = 0; j<ALLOWED_DATES; ++j) freenull(dates[j]);
319
320    free(path);
321    free(disabled_path);
322    free(qs.quick_save_disabled);
323
324    gb_local->announce_db_close(this);
325}
326
327GBDATA *gb_make_pre_defined_entry(GBCONTAINER *father, GBDATA *gbd, long index_pos, GBQUARK keyq) {
328    // inserts an object into the dabase hierarchy
329    GB_MAIN_TYPE *Main = GBCONTAINER_MAIN(father);
330
331    SET_GB_FATHER(gbd, father);
332    if (Main->is_server()) {
333        gbd->server_id = GBTUM_MAGIC_NUMBER;
334    }
335    if (Main->clock) {
336        gbd->create_extended();
337        gbd->touch_creation(Main->clock);
338    }
339
340    gb_link_entry(father, gbd, index_pos);
341    gb_write_index_key(father, gbd->index, keyq);
342
343    return gbd;
344}
345
346static void gb_write_key(GBDATA *gbd, const char *s) {
347    GB_MAIN_TYPE *Main        = GB_MAIN(gbd);
348    GBQUARK       new_index   = GBS_read_hash(Main->key_2_index_hash, s);
349    if (!new_index) new_index = (int)gb_create_key(Main, s, true); // create new index
350    gb_write_index_key(GB_FATHER(gbd), gbd->index, new_index);
351}
352
353GBENTRY *gb_make_entry(GBCONTAINER *father, const char *key, long index_pos, GBQUARK keyq, GB_TYPES type) {
354    // creates a terminal database object
355    GB_MAIN_TYPE *Main = GBCONTAINER_MAIN(father);
356
357    if (!keyq) keyq = gb_find_or_create_quark(Main, key);
358
359    long     gbm_index = quark2gbmindex(Main, keyq);
360    GBENTRY *gbe       = (GBENTRY*)gbm_get_mem(sizeof(GBENTRY), gbm_index);
361
362    GB_GBM_INDEX(gbe) = gbm_index;
363    SET_GB_FATHER(gbe, father);
364
365    switch (type) {
366        case GB_STRING_SHRT:
367            type = GB_STRING;
368            // fall-through
369        case GB_STRING:
370            gbe->insert_data("", 0, 1);
371            break;
372        case GB_OBSOLETE:
373            gbe->flags.temporary = 1; // exclude obsolete type from next save
374            break;
375        default:
376            break;
377    }
378    gbe->flags.type = type;
379
380    if (Main->is_server()) {
381        gbe->server_id = GBTUM_MAGIC_NUMBER;
382    }
383    if (Main->clock) {
384        gbe->create_extended();
385        gbe->touch_creation(Main->clock);
386    }
387
388    gb_link_entry(father, gbe, index_pos);
389    if (key)    gb_write_key(gbe, key);
390    else        gb_write_index_key(father, gbe->index, keyq);
391
392    return gbe;
393}
394
395GBCONTAINER *gb_make_pre_defined_container(GBCONTAINER *father, GBCONTAINER *gbc, long index_pos, GBQUARK keyq) {
396    // inserts an object into the dabase hierarchy
397    GB_MAIN_TYPE *Main = GBCONTAINER_MAIN(father);
398
399    SET_GB_FATHER(gbc, father);
400    gbc->main_idx = father->main_idx;
401
402    if (Main->is_server()) gbc->server_id = GBTUM_MAGIC_NUMBER;
403    if (Main->clock) {
404        gbc->create_extended();
405        gbc->touch_creation(Main->clock);
406    }
407    gb_link_entry(father, gbc, index_pos);
408    gb_write_index_key(father, gbc->index, keyq);
409
410    return gbc;
411}
412
413
414GBCONTAINER *gb_make_container(GBCONTAINER *father, const char *key, long index_pos, GBQUARK keyq) {
415    GBCONTAINER *gbc;
416
417    if (father) {
418        GB_MAIN_TYPE *Main = GBCONTAINER_MAIN(father);
419
420        if (!keyq) keyq   = gb_find_or_create_NULL_quark(Main, key);
421        long gbm_index    = quark2gbmindex(Main, keyq);
422        gbc               = (GBCONTAINER *)gbm_get_mem(sizeof(GBCONTAINER), gbm_index);
423        GB_GBM_INDEX(gbc) = gbm_index;
424
425        SET_GB_FATHER(gbc, father);
426        gbc->flags.type = GB_DB;
427        gbc->main_idx = father->main_idx;
428        if (Main->is_server()) gbc->server_id = GBTUM_MAGIC_NUMBER;
429        if (Main->clock) {
430            gbc->create_extended();
431            gbc->touch_creation(Main->clock);
432        }
433        gb_link_entry(father, gbc, index_pos);
434        if (key)    gb_write_key(gbc, key);
435        else        gb_write_index_key(father, gbc->index, keyq);
436    }
437    else { // main entry
438        gbc = (GBCONTAINER *) gbm_get_mem(sizeof(GBCONTAINER), 0);
439        gbc->flags.type = GB_DB;
440    }
441
442    return gbc;
443}
444
445void gb_pre_delete_entry(GBDATA *gbd) {
446    // Reduce an entry to its absolute minimum and remove it from database
447    GB_MAIN_TYPE *Main = GB_MAIN_NO_FATHER(gbd);
448    GB_TYPES      type = gbd->type();
449
450    Main->trigger_delete_callbacks(gbd);
451
452    {
453        GBCONTAINER *gb_father = GB_FATHER(gbd);
454        if (gb_father) gb_write_index_key(gb_father, gbd->index, 0);
455    }
456    gb_unlink_entry(gbd);
457
458    /* as soon as an entry is deleted, there is
459     * no need to keep track of the database entry
460     * within the server at the client side
461     */
462    if (Main->is_client() && gbd->server_id) {
463        if (Main->remote_hash) GBS_write_numhash(Main->remote_hash, gbd->server_id, 0);
464    }
465
466    if (type >= GB_BITS && type < GB_DB) {
467        gb_free_cache(Main, gbd->as_entry()); // cant use gb_uncache (since entry is already unlinked!)
468    }
469    GB_FREE_TRANSACTION_SAVE(gbd);
470    gbd->destroy_extended();
471}
472
473static void gb_delete_entry(GBCONTAINER*& gbc) {
474    long gbm_index = GB_GBM_INDEX(gbc);
475
476    gb_assert(gbc->type() == GB_DB);
477
478    for (long index = 0; index < gbc->d.nheader; index++) {
479        GBDATA *gbd = GBCONTAINER_ELEM(gbc, index);
480        if (gbd) {
481            gb_delete_entry(gbd);
482            SET_GBCONTAINER_ELEM(gbc, index, NULp);
483        }
484    }
485
486    gb_pre_delete_entry(gbc);
487
488    // what is left now, is the core database entry!
489
490    gb_destroy_indices(gbc);
491    gb_header_list *hls;
492
493    if ((hls=GB_DATA_LIST_HEADER(gbc->d))) {
494        gbm_free_mem(hls, sizeof(gb_header_list) * gbc->d.headermemsize, GBM_HEADER_INDEX);
495    }
496    gbm_free_mem(gbc, sizeof(GBCONTAINER), gbm_index);
497
498    gbc = NULp; // avoid further usage
499}
500
501static void gb_delete_entry(GBENTRY*& gbe) {
502    long gbm_index = GB_GBM_INDEX(gbe);
503
504    gb_pre_delete_entry(gbe);
505    if (gbe->type() >= GB_BITS) gbe->free_data();
506    gbm_free_mem(gbe, sizeof(GBENTRY), gbm_index);
507
508    gbe = NULp; // avoid further usage
509}
510
511void gb_delete_entry(GBDATA*& gbd) {
512    if (gbd->is_container()) {
513        gb_delete_entry(reinterpret_cast<GBCONTAINER*&>(gbd));
514    }
515    else {
516        gb_delete_entry(reinterpret_cast<GBENTRY*&>(gbd));
517    }
518}
519
520static void gb_delete_main_entry(GBCONTAINER*& gb_main) {
521    GBQUARK sys_quark = key2quark(GB_MAIN(gb_main), GB_SYSTEM_FOLDER);
522
523    // Note: sys_quark may be 0 (happens when destroying client db which never established a connection).
524    // In this case no system folder/quark has been created (and we do no longer try to create it)
525    // Nothing will happen in pass 2 below.
526
527    for (int pass = 1; pass <= 2; pass++) {
528        for (int index = 0; index < gb_main->d.nheader; index++) {
529            GBDATA *gbd = GBCONTAINER_ELEM(gb_main, index);
530            if (gbd) {
531                // delay deletion of system folder to pass 2:
532                if (pass == 2 || GB_KEY_QUARK(gbd) != sys_quark) { 
533                    gb_delete_entry(gbd);
534                    SET_GBCONTAINER_ELEM(gb_main, index, NULp);
535                }
536            }
537        }
538    }
539    gb_delete_entry(gb_main);
540}
541
542void gb_delete_dummy_father(GBCONTAINER*& gbc) {
543    gb_assert(!GB_FATHER(gbc));
544
545    GB_MAIN_TYPE *Main = GB_MAIN(gbc);
546    for (int index = 0; index < gbc->d.nheader; index++) {
547        GBDATA *gbd = GBCONTAINER_ELEM(gbc, index);
548        if (gbd) {
549            // dummy fathers should only have one element (which is the root_container)
550            GBCONTAINER *gb_main = gbd->as_container();
551            gb_assert(gb_main == Main->root_container);
552
553            gb_delete_main_entry(gb_main);
554            SET_GBCONTAINER_ELEM(gbc, index, NULp);
555            Main->root_container = NULp;
556        }
557    }
558
559    gb_delete_entry(gbc);
560}
561
562// ---------------------
563//      Data Storage
564
565static gb_transaction_save *gb_new_gb_transaction_save(GBENTRY *gbe) {
566    // Note: does not increment the refcounter
567    gb_transaction_save *ts = (gb_transaction_save *)gbm_get_mem(sizeof(gb_transaction_save), GBM_CB_INDEX);
568
569    ts->flags  = gbe->flags;
570    ts->flags2 = gbe->flags2;
571
572    if (gbe->stored_external()) {
573        ts->info.ex.data    = gbe->info.ex.get_data();
574        ts->info.ex.memsize = gbe->info.ex.memsize;
575        ts->info.ex.size    = gbe->info.ex.size;
576    }
577    else {
578        memcpy(&(ts->info), &(gbe->info), sizeof(gbe->info));
579    }
580
581    ts->refcount = 1;
582
583    return ts;
584}
585
586void gb_add_ref_gb_transaction_save(gb_transaction_save *ts) {
587    if (!ts) return;
588    ts->refcount ++;
589}
590
591void gb_del_ref_gb_transaction_save(gb_transaction_save *ts) {
592    if (!ts) return;
593    ts->refcount --;
594    if (ts->refcount <= 0) {    // no more references !!!!
595        if (ts->stored_external()) {
596            if (ts->info.ex.data) {
597                gbm_free_mem(ts->info.ex.data,
598                             ts->info.ex.memsize,
599                             ts->flags2.gbm_index);
600            }
601        }
602        gbm_free_mem(ts, sizeof(gb_transaction_save), GBM_CB_INDEX);
603    }
604}
605
606void gb_del_ref_and_extern_gb_transaction_save(gb_transaction_save *ts) {
607    // remove reference to undo entry and set extern pointer to zero
608    if (ts->stored_external()) {
609        ts->info.ex.data = NULp;
610    }
611    gb_del_ref_gb_transaction_save(ts);
612}
613
614static void gb_abortdata(GBENTRY *gbe) {
615    gb_transaction_save *old;
616
617    gbe->index_check_out();
618    old = gbe->ext->old;
619    gb_assert(old);
620
621    gbe->flags = old->flags;
622    gbe->flags2 = old->flags2;
623
624    if (old->stored_external()) {
625        gbe->info.ex.set_data(old->info.ex.data);
626        gbe->info.ex.memsize = old->info.ex.memsize;
627        gbe->info.ex.size = old->info.ex.size;
628    }
629    else {
630        memcpy(&(gbe->info), &(old->info), sizeof(old->info));
631    }
632    gb_del_ref_and_extern_gb_transaction_save(old);
633    gbe->ext->old = NULp;
634
635    gbe->index_re_check_in();
636}
637
638
639void gb_save_extern_data_in_ts(GBENTRY *gbe) {
640    /* Saves gbe->info into gbe->ext->old
641     * Destroys gbe->info!
642     * Don't call with GBCONTAINER
643     */
644
645    gbe->create_extended();
646    gbe->index_check_out();
647    if (gbe->ext->old || (GB_ARRAY_FLAGS(gbe).changed == GB_CREATED)) {
648        gbe->free_data();
649    }
650    else {
651        gbe->ext->old = gb_new_gb_transaction_save(gbe);
652        gbe->info.ex.set_data(NULp);
653    }
654}
655
656
657// -----------------------
658//      Key Management
659
660void gb_write_index_key(GBCONTAINER *father, long index, GBQUARK new_index) {
661    // Set the key quark of an database field.
662    // Check for indexing data field.
663
664    GB_MAIN_TYPE   *Main      = GBCONTAINER_MAIN(father);
665    gb_header_list *hls       = GB_DATA_LIST_HEADER(father->d);
666    GBQUARK         old_index = hls[index].flags.key_quark;
667
668    Main->keys[old_index].nref--;
669    Main->keys[new_index].nref++;
670
671    if (Main->is_server()) {
672        GBDATA *gbd = GB_HEADER_LIST_GBD(hls[index]);
673
674        if (gbd && gbd->is_indexable()) {
675            GBENTRY        *gbe = gbd->as_entry();
676            gb_index_files *ifs = NULp;
677
678            gbe->index_check_out();
679            gbe->flags2.should_be_indexed = 0; // do not re-checkin
680           
681            GBCONTAINER *gfather = GB_FATHER(father);
682            if (gfather) {
683                for (ifs = GBCONTAINER_IFS(gfather); ifs; ifs = GB_INDEX_FILES_NEXT(ifs)) {
684                    if (ifs->key == new_index) break;
685                }
686            }
687            hls[index].flags.key_quark = new_index;
688            if (ifs) gbe->index_check_in();
689
690            return;
691        }
692    }
693
694    hls[index].flags.key_quark = new_index;
695}
696
697void gb_create_key_array(GB_MAIN_TYPE *Main, int index) {
698    if (index >= Main->sizeofkeys) {
699        Main->sizeofkeys = Main->keys ? index*3/2+1 : 1000;
700        ARB_recalloc(Main->keys, Main->keycnt, Main->sizeofkeys);
701        for (int i = Main->keycnt; i < Main->sizeofkeys; i++) {
702            Main->keys[i].compression_mask = -1;
703        }
704    }
705    gb_assert(index<Main->sizeofkeys);
706}
707
708long gb_create_key(GB_MAIN_TYPE *Main, const char *key, bool create_gb_key) {
709    long index;
710    if (Main->first_free_key) {
711        index = Main->first_free_key;
712        Main->first_free_key = Main->keys[index].next_free_key;
713        Main->keys[index].next_free_key = 0;
714    }
715    else {
716        index = Main->keycnt++;
717        gb_create_key_array(Main, (int)index+1);
718    }
719    if (Main->is_client()) {
720        long test_index = gbcmc_key_alloc(Main->gb_main(), key);
721        if (test_index != index) {
722            if (test_index == 0) { // comm error
723                GBK_terminatef("Allocating quark for '%s' failed (Reason: %s)", key, GB_await_error());
724            }
725            else {
726                GBK_terminatef("Database corrupt (allocating quark '%s' in server failed)", key);
727            }
728        }
729    }
730    Main->keys[index].nref = 0;
731
732    if (key) {
733        if (!key[0]) GBK_terminate("Attempt to allocate empty key");
734
735        Main->keys[index].key = ARB_strdup(key);
736        GBS_write_hash(Main->key_2_index_hash, key, index);
737        gb_assert(GBS_hash_elements(Main->key_2_index_hash) <= ALLOWED_KEYS);
738        if (Main->gb_key_data && create_gb_key) {
739            gb_load_single_key_data(Main->gb_main(), (GBQUARK)index);
740            // Warning: starts a big recursion
741            if (Main->is_client()) { // send new gb_key to server, needed for searching
742                GBK_terminate_on_error(Main->send_update_to_server(Main->gb_main()));
743            }
744        }
745    }
746
747
748    Main->key_clock = Main->clock;
749    return index;
750}
751
752void GB_MAIN_TYPE::free_all_keys() {
753    if (keys) {
754        for (long index = 1; index < keycnt; index++) {
755            if (keys[index].key) {
756                GBS_write_hash(key_2_index_hash, keys[index].key, 0);
757                freenull(keys[index].key);
758            }
759            keys[index].nref = 0;
760            keys[index].next_free_key = 0;
761        }
762        freenull(keys[0].key); // "main"
763        first_free_key = 0;
764        keycnt         = 1;
765    }
766}
767
768char *gb_abort_entry(GBDATA *gbd) { // @@@ result is always NULp -> remove return value
769    GB_ARRAY_FLAGS(gbd).flags = gbd->flags.saved_flags;
770
771    if (gbd->is_entry()) {
772        GBENTRY *gbe = gbd->as_entry();
773        if (gbe->get_oldData()) {
774            if (gbe->type() >= GB_BITS) {
775                gb_uncache(gbe);
776                gbe->free_data();
777            }
778            gb_abortdata(gbe);
779        }
780    }
781    return NULp;
782}
783
784// ---------------------
785//      Transactions
786
787void gb_abort_transaction_local_rek(GBDATA*& gbd) {
788    // delete created, undo changed
789    GB_CHANGE change = (GB_CHANGE)GB_ARRAY_FLAGS(gbd).changed;
790
791    switch (change) {
792        case GB_UNCHANGED:
793            break;
794
795        case GB_CREATED:
796            GB_PUT_SECURITY_DELETE(gbd, 0);
797            gb_delete_entry(gbd);
798            break;
799
800        case GB_DELETED:
801            GB_ARRAY_FLAGS(gbd).changed = GB_UNCHANGED;
802            // fall-through
803        default:
804            if (gbd->is_container()) {
805                GBCONTAINER    *gbc = gbd->as_container();
806                gb_header_list *hls = GB_DATA_LIST_HEADER(gbc->d);
807
808                for (int index = 0; index < gbc->d.nheader; index++) {
809                    GBDATA *gb = GB_HEADER_LIST_GBD(hls[index]);
810                    if (gb) gb_abort_transaction_local_rek(gb);
811                }
812            }
813            gb_abort_entry(gbd);
814            break;
815    }
816}
817
818GB_ERROR gb_commit_transaction_local_rek(GBDATA*& gbd, long mode, int *pson_created) {
819    // goes to header: __ATTR__USERESULT
820
821    /* commit created / delete deleted
822     *   mode   0   local     = server    or
823     *              begin trans in client or
824     *              commit_client_in_server
825     *   mode   1   remote    = client
826     *   mode   2   remote    = client (only send updated data)
827     */
828
829    GB_MAIN_TYPE *Main        = GB_MAIN(gbd);
830    GB_CHANGE     change      = (GB_CHANGE)GB_ARRAY_FLAGS(gbd).changed;
831    int           son_created = 0;
832    GB_ERROR      error;
833
834    switch (change) {
835        case GB_UNCHANGED:
836            return NULp;
837
838        case GB_DELETED:
839            GB_PUT_SECURITY_DELETE(gbd, 0);
840            if (mode) {
841                if (!gbd->flags2.update_in_server) {
842                    error = gbcmc_sendupdate_delete(gbd);
843                    if (error)
844                        return error;
845                    gbd->flags2.update_in_server = 1;
846                }
847                if (mode == 2) return NULp;
848            }
849            else {
850                gbcms_add_to_delete_list(gbd);
851                _GB_CHECK_IN_UNDO_DELETE(Main, gbd);
852                return NULp;
853            }
854            gb_delete_entry(gbd);
855            return NULp;
856
857        case GB_CREATED:
858            if (mode) {
859                if (!gbd->flags2.update_in_server) {
860                    if (gbd->server_id) goto gb_changed_label;
861                    // already created, do only a change
862                    error = gbcmc_sendupdate_create(gbd);
863                    if (gbd->is_container()) {
864                        gb_set_update_in_server_flags(gbd->as_container());
865                        // set all children update_in_server flags
866                    }
867                    gbd->flags2.update_in_server = 1;
868                    if (error)  return error;
869                }
870                if (mode == 2) return NULp;
871            }
872            else {
873                _GB_CHECK_IN_UNDO_CREATE(Main, gbd);
874            }
875            if (pson_created) {
876                *pson_created  = 1;
877            }
878
879            if (gbd->flags2.header_changed == 1) {
880                gbd->as_container()->header_update_date = Main->clock;
881            }
882            goto gb_commit_do_callbacks;
883
884        case GB_NORMAL_CHANGE:
885            if (mode) {
886                if (!gbd->flags2.update_in_server) {
887                  gb_changed_label:
888                    int send_header = gbd->flags2.header_changed ? 1 : 0;
889                    error           = gbcmc_sendupdate_update(gbd, send_header);
890                    if (error) return error;
891                    gbd->flags2.update_in_server = 1;
892                }
893            }
894            else {
895                _GB_CHECK_IN_UNDO_MODIFY(Main, gbd);
896            }
897            // fall-through
898
899        default: // means GB_SON_CHANGED + GB_NORMAL_CHANGE
900
901            if (gbd->is_container()) {
902                GBCONTAINER    *gbc = gbd->as_container();
903                gb_header_list *hls = GB_DATA_LIST_HEADER(gbc->d);
904
905                int start, end;
906
907                if (gbc->index_of_touched_one_son>0) {
908                    start = (int)gbc->index_of_touched_one_son-1;
909                    end = start+1;
910                }
911                else {
912                    if (!gbc->index_of_touched_one_son) {
913                        start = end = 0;
914                    }
915                    else {
916                        start = 0;
917                        end = gbc->d.nheader;
918                    }
919                }
920
921                for (int index = start; index < end; index++) {
922                    GBDATA *gb = GB_HEADER_LIST_GBD(hls[index]);
923                    if (gb) {
924                        if (!hls[index].flags.changed) continue;
925                        error = gb_commit_transaction_local_rek(gb, mode, &son_created);
926                        if (error) return error;
927                    }
928                }
929
930                if (mode) gbd->flags2.update_in_server = 1;
931            }
932    gb_commit_do_callbacks :
933            if (mode == 2) {    // update server; no callbacks
934                gbd->flags2.update_in_server = 1;
935            }
936            else {
937                GB_CB_TYPE cbtype = son_created ? GB_CB_CHANGED_OR_SON_CREATED : GB_CB_CHANGED;
938                gbd->create_extended();
939                gbd->touch_update(Main->clock);
940                if (gbd->flags2.header_changed) {
941                    gbd->as_container()->header_update_date = Main->clock;
942                }
943
944                Main->trigger_change_callbacks(gbd, cbtype);
945
946                GB_FREE_TRANSACTION_SAVE(gbd);
947            }
948    }
949
950    return NULp;
951}
Note: See TracBrowser for help on using the repository browser.