source: tags/ms_r17q2/ARBDB/ad_core.cxx

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