source: branches/profile/ARBDB/ad_core.cxx

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