source: tags/ms_ra2q2/ARBDB/adquery.cxx

Last change on this file was 16763, 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: 37.9 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : adquery.cxx                                       //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "gb_aci.h"
12#include "gb_comm.h"
13#include "gb_index.h"
14#include "gb_key.h"
15#include "gb_localdata.h"
16#include "gb_ta.h"
17#include <algorithm>
18
19#include <arb_strbuf.h>
20#include <arb_match.h>
21
22#include <cctype>
23
24#define GB_PATH_MAX 1024
25
26static void build_GBDATA_path(GBDATA *gbd, char **buffer) {
27    GBCONTAINER *gbc = GB_FATHER(gbd);
28    if (gbc) {
29        build_GBDATA_path(gbc, buffer);
30
31        const char *key = GB_KEY(gbd);
32        char       *bp  = *buffer;
33
34        *bp++ = '/';
35        while (*key) *bp++ = *key++;
36        *bp = 0;
37
38        *buffer = bp;
39    }
40}
41
42#define BUFFERSIZE 1024
43
44static const char *GB_get_GBDATA_path(GBDATA *gbd) {
45    static char *orgbuffer = NULp;
46    char        *buffer;
47
48    if (!orgbuffer) ARB_alloc(orgbuffer, BUFFERSIZE);
49    buffer = orgbuffer;
50
51    build_GBDATA_path(gbd, &buffer);
52    assert_or_exit((buffer-orgbuffer) < BUFFERSIZE); // buffer overflow
53
54    return orgbuffer;
55}
56
57// ----------------
58//      QUERIES
59
60static bool gb_find_value_equal(GBDATA *gb, GB_TYPES type, const char *val, GB_CASE case_sens) {
61    bool equal = false;
62
63#if defined(DEBUG)
64    GB_TYPES realtype = gb->type();
65    gb_assert(val);
66    if (type == GB_STRING) {
67        gb_assert(gb->is_a_string()); // gb_find_internal called with wrong type
68    }
69    else {
70        gb_assert(realtype == type); // gb_find_internal called with wrong type
71    }
72#endif // DEBUG
73
74    switch (type) {
75        case GB_STRING:
76            equal = GBS_string_matches(GB_read_char_pntr(gb), val, case_sens);
77            break;
78
79        case GB_INT: {
80            int i                      = GB_read_int(gb);
81            if (i == *(int*)val) equal = true;
82            break;
83        }
84        default: {
85            const char *err = GBS_global_string("Value search not supported for data type %i (%i)", gb->type(), type);
86            GB_internal_error(err);
87            break;
88        }
89    }
90
91    return equal;
92}
93
94static GBDATA *find_sub_by_quark(GBCONTAINER *father, GBQUARK key_quark, GB_TYPES type, const char *val, GB_CASE case_sens, GBDATA *after, size_t skip_over) {
95    /* search an entry with a key 'key_quark' below a container 'father'
96       after position 'after'
97
98       if 'skip_over' > 0 search skips 'skip_over' entries
99
100       if 'val' specified -> search for entry with value 'val':
101
102       GB_STRING/GB_LINK: compares string (case_sensitive or not)
103       GB_INT: compares values
104       GB_FLOAT: ditto (val MUST be a 'double*')
105       others: not implemented yet
106
107       Note: to search for non-char*-values use GB_find_int()
108             for other types write a new similar function
109
110             if key_quark<0 search everything
111    */
112
113    int             end    = father->d.nheader;
114    gb_header_list *header = GB_DATA_LIST_HEADER(father->d);
115
116    int index;
117    if (after) index = (int)after->index+1; else index = 0;
118
119    if (key_quark<0) { // unspecific key quark (i.e. search all)
120        gb_assert(!val);        // search for val not possible if searching all keys!
121        if (!val) {
122            for (; index < end; index++) {
123                if (header[index].flags.key_quark != 0) {
124                    if (header[index].flags.changed >= GB_DELETED) continue;
125                    GBDATA *gb = GB_HEADER_LIST_GBD(header[index]);
126                    if (!gb) {
127                        // @@@ DRY here versus below
128                        gb_unfold(father, 0, index);
129                        header = GB_DATA_LIST_HEADER(father->d);
130                        gb     = GB_HEADER_LIST_GBD(header[index]);
131                        if (!gb) {
132                            const char *err = GBS_global_string("Database entry #%u is missing (in '%s')", index, GB_get_GBDATA_path(father));
133                            GB_internal_error(err);
134                            continue;
135                        }
136                    }
137                    if (!skip_over--) return gb;
138                }
139            }
140        }
141    }
142    else { // specific key quark
143        for (; index < end; index++) {
144            if (key_quark == header[index].flags.key_quark) {
145                if (header[index].flags.changed >= GB_DELETED) continue;
146                GBDATA *gb = GB_HEADER_LIST_GBD(header[index]);
147                if (!gb) {
148                    // @@@ DRY here versus section above
149                    gb_unfold(father, 0, index);
150                    header = GB_DATA_LIST_HEADER(father->d);
151                    gb     = GB_HEADER_LIST_GBD(header[index]);
152                    if (!gb) {
153                        const char *err = GBS_global_string("Database entry #%u is missing (in '%s')", index, GB_get_GBDATA_path(father));
154                        GB_internal_error(err);
155                        continue;
156                    }
157                }
158                if (val) {
159                    if (!gb) {
160                        GB_internal_error("Cannot unfold data");
161                        continue;
162                    }
163                    else {
164                        if (!gb_find_value_equal(gb, type, val, case_sens)) continue;
165                    }
166                }
167                if (!skip_over--) return gb;
168            }
169        }
170    }
171    return NULp;
172}
173
174GBDATA *GB_find_sub_by_quark(GBDATA *father, GBQUARK key_quark, GBDATA *after, size_t skip_over) {
175    return find_sub_by_quark(father->expect_container(), key_quark, GB_NONE, NULp, GB_MIND_CASE, after, skip_over);
176}
177
178static GBDATA *GB_find_subcontent_by_quark(GBDATA *father, GBQUARK key_quark, GB_TYPES type, const char *val, GB_CASE case_sens, GBDATA *after, size_t skip_over) {
179    return find_sub_by_quark(father->expect_container(), key_quark, type, val, case_sens, after, skip_over);
180}
181
182static GBDATA *find_sub_sub_by_quark(GBCONTAINER *const father, const char *key, GBQUARK sub_key_quark, GB_TYPES type, const char *val, GB_CASE case_sens, GBDATA *after) {
183    gb_index_files *ifs    = NULp;
184    GB_MAIN_TYPE   *Main   = GBCONTAINER_MAIN(father);
185    int             end    = father->d.nheader;
186    gb_header_list *header = GB_DATA_LIST_HEADER(father->d);
187
188    int index;
189    if (after) index = (int)after->index+1; else index = 0;
190
191    GBDATA *res;
192    // ****** look for any hash index tables ********
193    // ****** no wildcards allowed       *******
194    if (Main->is_client()) {
195        if (father->flags2.folded_container) {
196            // do the query in the server
197            if (GB_ARRAY_FLAGS(father).changed) {
198                if (!father->flags2.update_in_server) {
199                    GB_ERROR error = Main->send_update_to_server(father);
200                    if (error) {
201                        GB_export_error(error);
202                        return NULp;
203                    }
204                }
205            }
206        }
207        if (father->d.size > GB_MAX_LOCAL_SEARCH && val) {
208            if (after) res = GBCMC_find(after,  key, type, val, case_sens, SEARCH_CHILD_OF_NEXT);
209            else       res = GBCMC_find(father, key, type, val, case_sens, SEARCH_GRANDCHILD);
210            return res;
211        }
212    }
213    if (val &&
214        (ifs=GBCONTAINER_IFS(father)) &&
215        (!strchr(val, '*')) &&
216        (!strchr(val, '?')))
217    {
218        for (; ifs; ifs = GB_INDEX_FILES_NEXT(ifs)) {
219            if (ifs->key != sub_key_quark) continue;
220            // ***** We found the index table *****
221            res = gb_index_find(father, ifs, sub_key_quark, val, case_sens, index);
222            return res;
223        }
224    }
225
226    GBDATA *gb = after ? after : NULp;
227    for (; index < end; index++) {
228        GBDATA *gbn = GB_HEADER_LIST_GBD(header[index]);
229
230        if (header[index].flags.changed >= GB_DELETED) continue;
231        if (!gbn) {
232            if (Main->is_client()) {
233                if (gb) res = GBCMC_find(gb,     key, type, val, case_sens, SEARCH_CHILD_OF_NEXT);
234                else    res = GBCMC_find(father, key, type, val, case_sens, SEARCH_GRANDCHILD);
235                return res;
236            }
237            GB_internal_error("Empty item in server");
238            continue;
239        }
240        gb = gbn;
241        if (gb->is_container()) {
242            res = GB_find_subcontent_by_quark(gb, sub_key_quark, type, val, case_sens, NULp, 0);
243            if (res) return res;
244        }
245    }
246    return NULp;
247}
248
249
250static GBDATA *gb_find_internal(GBDATA *gbd, const char *key, GB_TYPES type, const char *val, GB_CASE case_sens, GB_SEARCH_TYPE gbs) {
251    GBDATA *result = NULp;
252
253    if (gbd) {
254        GBDATA      *after = NULp;
255        GBCONTAINER *gbc   = NULp;
256
257        switch (gbs) {
258            case SEARCH_NEXT_BROTHER:
259                after = gbd;
260                FALLTHROUGH;
261            case SEARCH_BROTHER:
262                gbs = SEARCH_CHILD;
263                gbc = GB_FATHER(gbd);
264                break;
265
266            case SEARCH_CHILD:
267            case SEARCH_GRANDCHILD:
268                if (gbd->is_container()) gbc = gbd->as_container();
269                break;
270
271            case SEARCH_CHILD_OF_NEXT:
272                after = gbd;
273                gbs   = SEARCH_GRANDCHILD;
274                gbc   = GB_FATHER(gbd);
275                break;
276        }
277
278        if (gbc) {
279            GBQUARK key_quark = GB_find_existing_quark(gbd, key);
280
281            if (key_quark) { // only search if 'key' is known to db
282                if (gbs == SEARCH_CHILD) {
283                    result = GB_find_subcontent_by_quark(gbc, key_quark, type, val, case_sens, after, 0);
284                }
285                else {
286                    gb_assert(gbs == SEARCH_GRANDCHILD);
287                    result = find_sub_sub_by_quark(gbc, key, key_quark, type, val, case_sens, after);
288                }
289            }
290        }
291    }
292    return result;
293}
294
295GBDATA *GB_find(GBDATA *gbd, const char *key, GB_SEARCH_TYPE gbs) {
296    // normally you should not need to use GB_find!
297    // better use one of the replacement functions
298    // (GB_find_string, GB_find_int, GB_child, GB_nextChild, GB_entry, GB_nextEntry, GB_brother)
299    return gb_find_internal(gbd, key, GB_NONE, NULp, GB_CASE_UNDEFINED, gbs);
300}
301
302GBDATA *GB_find_string(GBDATA *gbd, const char *key, const char *str, GB_CASE case_sens, GB_SEARCH_TYPE gbs) {
303    // search for a subentry of 'gbd' that has
304    // - fieldname 'key'
305    // - type GB_STRING and
306    // - content matching 'str'
307    // if 'case_sensitive' is true, content is matched case sensitive.
308    // GBS_string_matches is used to compare (supports wildcards)
309    return gb_find_internal(gbd, key, GB_STRING, str, case_sens, gbs);
310}
311NOT4PERL GBDATA *GB_find_int(GBDATA *gbd, const char *key, long val, GB_SEARCH_TYPE gbs) {
312    // search for a subentry of 'gbd' that has
313    // - fieldname 'key'
314    // - type GB_INT
315    // - and value 'val'
316    return gb_find_internal(gbd, key, GB_INT, (const char *)&val, GB_CASE_UNDEFINED, gbs);
317}
318
319// ----------------------------------------------------
320//      iterate over ALL subentries of a container
321
322GBDATA *GB_child(GBDATA *father) {
323    // return first child (or NULp if no children)
324    return GB_find(father, NULp, SEARCH_CHILD);
325}
326GBDATA *GB_nextChild(GBDATA *child) {
327    // return next child after 'child' (or NULp if no more children)
328    return GB_find(child, NULp, SEARCH_NEXT_BROTHER);
329}
330
331// ------------------------------------------------------------------------------
332//      iterate over all subentries of a container that have a specified key
333
334GBDATA *GB_entry(GBDATA *father, const char *key) { 
335    // return first child of 'father' that has fieldname 'key'
336    // (or NULp if none found)
337    return GB_find(father, key, SEARCH_CHILD);
338}
339GBDATA *GB_nextEntry(GBDATA *entry) {
340    // return next child after 'entry', that has the same fieldname
341    // (or NULp if 'entry' is last one)
342    return GB_find_sub_by_quark(GB_FATHER(entry), GB_get_quark(entry), entry, 0);
343}
344GBDATA *GB_followingEntry(GBDATA *entry, size_t skip_over) {
345    // return following child after 'entry', that has the same fieldname
346    // (or NULp if no such entry)
347    // skips 'skip_over' entries (skip_over == 0 behaves like GB_nextEntry)
348    return GB_find_sub_by_quark(GB_FATHER(entry), GB_get_quark(entry), entry, skip_over);
349}
350
351GBDATA *GB_brother(GBDATA *entry, const char *key) {
352    // searches (first) brother (before or after) of 'entry' which has field 'key'
353    // i.e. does same as GB_entry(GB_get_father(entry), key)
354    return GB_find(entry, key, SEARCH_BROTHER);
355}
356
357GBDATA *gb_find_by_nr(GBCONTAINER *father, int index) {
358    /* get a subentry by its internal number:
359       Warning: This subentry must exists, otherwise internal error */
360
361    gb_header_list *header = GB_DATA_LIST_HEADER(father->d);
362    if (index >= father->d.nheader || index <0) {
363        GB_internal_errorf("Index '%i' out of range [%i:%i[", index, 0, father->d.nheader);
364        return NULp;
365    }
366    if (header[index].flags.changed >= GB_DELETED || !header[index].flags.key_quark) {
367        GB_internal_error("Entry already deleted");
368        return NULp;
369    }
370
371    GBDATA *gb = GB_HEADER_LIST_GBD(header[index]);
372    if (!gb) {
373        gb_unfold(father, 0, index);
374        header = GB_DATA_LIST_HEADER(father->d);
375        gb = GB_HEADER_LIST_GBD(header[index]);
376        if (!gb) {
377            GB_internal_error("Could not unfold data");
378            return NULp;
379        }
380    }
381    return gb;
382}
383
384class keychar_table {
385    bool table[256];
386public:
387    keychar_table() {
388        for (int i=0; i<256; i++) {
389            table[i] = islower(i) || isupper(i) || isdigit(i) || i=='_' || i=='@';
390        }
391    }
392    const char *first_non_key_character(const char *str) const {
393        while (1) {
394            int c = *str;
395            if (!table[c]) {
396                if (c == 0) break;
397                return str;
398            }
399            str++;
400        }
401        return NULp;
402    }
403};
404static keychar_table keychars;
405
406const char *GB_first_non_key_char(const char *str) {
407    return keychars.first_non_key_character(str);
408}
409
410inline GBDATA *find_or_create(GBCONTAINER *gb_parent, const char *key, GB_TYPES create, bool internflag) {
411    gb_assert(!keychars.first_non_key_character(key));
412
413    GBDATA *gbd = GB_entry(gb_parent, key);
414    if (create) {
415        if (gbd) {
416            GB_TYPES oldType = gbd->type();
417            if (create != oldType) { // type mismatch
418                GB_export_errorf("Inconsistent type for field '%s' (existing=%i, expected=%i)", key, oldType, create);
419                gbd = NULp;
420            }
421        }
422        else {
423            if (create == GB_CREATE_CONTAINER) {
424                gbd = internflag ? gb_create_container(gb_parent, key) : GB_create_container(gb_parent, key);
425            }
426            else {
427                gbd = gb_create(gb_parent, key, create);
428            }
429            gb_assert(gbd || GB_have_error());
430        }
431    }
432    return gbd;
433}
434
435GBDATA *gb_search(GBCONTAINER *gbc, const char *key, GB_TYPES create, int internflag) {
436    /* finds a hierarchical key,
437     * if create != GB_FIND(==0), then create the key
438     * force types if ! internflag
439     */
440
441    gb_assert(!GB_have_error()); // illegal to enter this function when an error is exported!
442
443    GB_test_transaction(gbc);
444
445    if (!key) return NULp; // was allowed in the past (and returned the 1st child). now returns NULp
446   
447    if (key[0] == '/') {
448        gbc = gb_get_root(gbc);
449        key++;
450    }
451
452    if (!key[0]) {
453        return gbc;
454    }
455
456    GBDATA     *gb_result     = NULp;
457    const char *separator     = keychars.first_non_key_character(key);
458    if (!separator) gb_result = find_or_create(gbc, key, create, internflag);
459    else {
460        int  len = separator-key;
461        char firstKey[len+1];
462        memcpy(firstKey, key, len);
463        firstKey[len] = 0;
464
465        char invalid_char = 0;
466
467        switch (separator[0]) {
468            case '/': {
469                GBDATA *gb_sub = find_or_create(gbc, firstKey, create ? GB_CREATE_CONTAINER : GB_FIND, internflag);
470                if (gb_sub) {
471                    if (gb_sub->is_container()) {
472                        if (separator[1] == '/') {
473                            GB_export_errorf("Invalid '//' in key '%s'", key);
474                        }
475                        else {
476                            gb_result = gb_search(gb_sub->as_container(), separator+1, create, internflag);
477                        }
478                    }
479                    else {
480                        GB_export_errorf("terminal entry '%s' cannot be used as container", firstKey);
481                    }
482                }
483                break;
484            }
485            case '.': {
486                if (separator[1] != '.') invalid_char = separator[0];
487                else {
488                    GBCONTAINER *gb_parent = gbc->get_father();
489                    if (gb_parent) {
490                        switch (separator[2]) {
491                            case 0:   gb_result    = gb_parent; break;
492                            case '/': gb_result    = gb_search(gb_parent, separator+3, create, internflag); break;
493                            default:
494                                GB_export_errorf("Expected '/' after '..' in key '%s'", key);
495                                break;
496                        }
497                    }
498                    else { // ".." at root-node
499                        if (create) {
500                            GB_export_error("cannot use '..' at root node");
501                        }
502                    }
503                }
504                break;
505            }
506            default:
507                invalid_char = separator[0];
508                break;
509        }
510
511        if (invalid_char) {
512            gb_assert(!gb_result);
513            GB_export_errorf("Invalid char '%c' in key '%s'", invalid_char, key);
514        }
515    }
516    gb_assert(!(gb_result && GB_have_error()));
517    return gb_result;
518}
519
520
521GBDATA *GB_search(GBDATA *gbd, const char *fieldpath, GB_TYPES create) {
522    return gb_search(gbd->expect_container(), fieldpath, create, 0);
523}
524
525static GBDATA *gb_expect_type(GBDATA *gbd, GB_TYPES expected_type, const char *fieldname) {
526    gb_assert(expected_type != GB_FIND); // impossible
527
528    GB_TYPES type = gbd->type();
529    if (type != expected_type) {
530        GB_export_errorf("Field '%s' has wrong type (found=%i, expected=%i)", fieldname, type, expected_type);
531        gbd = NULp;
532    }
533    return gbd;
534}
535
536GBDATA *GB_searchOrCreate_string(GBDATA *gb_container, const char *fieldpath, const char *default_value) {
537    gb_assert(!GB_have_error()); // illegal to enter this function when an error is exported!
538
539    GBDATA *gb_str = GB_search(gb_container, fieldpath, GB_FIND);
540    if (!gb_str) {
541        GB_clear_error();
542        gb_str = GB_search(gb_container, fieldpath, GB_STRING);
543        GB_ERROR error;
544
545        if (!gb_str) error = GB_await_error();
546        else error         = GB_write_string(gb_str, default_value);
547
548        if (error) {
549            gb_str = NULp;
550            GB_export_error(error);
551        }
552    }
553    else {
554        gb_str = gb_expect_type(gb_str, GB_STRING, fieldpath);
555    }
556    return gb_str;
557}
558
559GBDATA *GB_searchOrCreate_int(GBDATA *gb_container, const char *fieldpath, long default_value) {
560    gb_assert(!GB_have_error()); // illegal to enter this function when an error is exported!
561
562    GBDATA *gb_int = GB_search(gb_container, fieldpath, GB_FIND);
563    if (!gb_int) {
564        gb_int = GB_search(gb_container, fieldpath, GB_INT);
565        GB_ERROR error;
566
567        if (!gb_int) error = GB_await_error();
568        else error         = GB_write_int(gb_int, default_value);
569
570        if (error) { // @@@ in case GB_search returned 0, gb_int already is 0 and error is exported. just assert error is exported
571            gb_int = NULp;
572            GB_export_error(error);
573        }
574    }
575    else {
576        gb_int = gb_expect_type(gb_int, GB_INT, fieldpath);
577    }
578    return gb_int;
579}
580
581GBDATA *GB_searchOrCreate_float(GBDATA *gb_container, const char *fieldpath, float default_value) {
582    gb_assert(!GB_have_error()); // illegal to enter this function when an error is exported!
583
584    GBDATA *gb_float = GB_search(gb_container, fieldpath, GB_FIND);
585    if (!gb_float) {
586        gb_float = GB_search(gb_container, fieldpath, GB_FLOAT);
587        GB_ERROR error;
588
589        if (!gb_float) error = GB_await_error();
590        else error           = GB_write_float(gb_float, default_value);
591
592        if (error) {
593            gb_float = NULp;
594            GB_export_error(error);
595        }
596    }
597    else {
598        gb_float = gb_expect_type(gb_float, GB_FLOAT, fieldpath);
599    }
600    return gb_float;
601}
602
603static GBDATA *gb_search_marked(GBCONTAINER *gbc, GBQUARK key_quark, int firstindex, size_t skip_over) {
604    int             userbit = GBCONTAINER_MAIN(gbc)->users[0]->userbit;
605    int             index;
606    int             end     = gbc->d.nheader;
607    gb_header_list *header  = GB_DATA_LIST_HEADER(gbc->d);
608
609    for (index = firstindex; index<end; index++) {
610        GBDATA *gb;
611
612        if (! (userbit & header[index].flags.flags)) continue;
613        if ((key_quark>=0) && (header[index].flags.key_quark  != key_quark)) continue;
614        if (header[index].flags.changed >= GB_DELETED) continue;
615        if (!(gb=GB_HEADER_LIST_GBD(header[index]))) {
616            gb_unfold(gbc, 0, index);
617            header = GB_DATA_LIST_HEADER(gbc->d);
618            gb = GB_HEADER_LIST_GBD(header[index]);
619        }
620        if (!skip_over--) return gb;
621    }
622    return NULp;
623}
624
625long GB_number_of_marked_subentries(GBDATA *gbd) {
626    long count = 0;
627    if (gbd->is_container()) {
628        GBCONTAINER    *gbc    = gbd->as_container();
629        gb_header_list *header = GB_DATA_LIST_HEADER(gbc->d);
630
631        int userbit = GBCONTAINER_MAIN(gbc)->users[0]->userbit;
632        int end     = gbc->d.nheader;
633
634        for (int index = 0; index<end; index++) {
635            if (!(userbit & header[index].flags.flags)) continue;
636            if (header[index].flags.changed >= GB_DELETED) continue;
637            count++;
638        }
639    }
640    return count;
641}
642
643
644
645GBDATA *GB_first_marked(GBDATA *gbd, const char *keystring) {
646    GBCONTAINER *gbc       = gbd->expect_container();
647    GBQUARK      key_quark = GB_find_existing_quark(gbd, keystring);
648    GB_test_transaction(gbc);
649    return key_quark ? gb_search_marked(gbc, key_quark, 0, 0) : NULp;
650}
651
652
653GBDATA *GB_following_marked(GBDATA *gbd, const char *keystring, size_t skip_over) {
654    GBCONTAINER *gbc       = GB_FATHER(gbd);
655    GBQUARK      key_quark = GB_find_existing_quark(gbd, keystring);
656    GB_test_transaction(gbc);
657    return key_quark ? gb_search_marked(gbc, key_quark, (int)gbd->index+1, skip_over) : NULp;
658}
659
660GBDATA *GB_next_marked(GBDATA *gbd, const char *keystring) {
661    return GB_following_marked(gbd, keystring, 0);
662}
663
664
665#ifdef UNIT_TESTS
666#include <test_unit.h>
667
668const char *GBT_get_name(GBDATA *gb_item); 
669
670struct TestDB : virtual Noncopyable {
671    GB_shell  shell;
672    GBDATA   *gb_main;
673    GB_ERROR  error;
674
675    GBDATA *gb_cont1;
676    GBDATA *gb_cont2;
677    GBDATA *gb_cont_empty;
678    GBDATA *gb_cont_misc;
679
680    GB_ERROR create_many_items(GBDATA *gb_parent, const char **item_key_list, int item_count) {
681        int k = 0;
682        for (int i = 0; i<item_count && !error; i++) {
683            const char *item_key    = item_key_list[k++];
684            if (!item_key) { item_key = item_key_list[0]; k = 1; }
685
686            GBDATA *gb_child = GB_create_container(gb_parent, item_key);
687            if (!gb_child) {
688                error = GB_await_error();
689            }
690            else {
691                if ((i%7) == 0) GB_write_flag(gb_child, 1); // mark some
692               
693                GBDATA *gb_name = GB_create(gb_child, "name", GB_STRING);
694                error           = GB_write_string(gb_name, GBS_global_string("%s %i", item_key, i));
695            }
696        }
697        return error;
698    }
699
700    TestDB() {
701        gb_main = GB_open("nosuch.arb", "c");
702        error   = gb_main ? NULp : GB_await_error();
703
704        if (!error) {
705            GB_transaction ta(gb_main);
706            gb_cont1      = GB_create_container(gb_main, "container1");
707            gb_cont2      = GB_create_container(gb_main, "container2");
708            gb_cont_empty = GB_create_container(gb_main, "empty");
709            gb_cont_misc  = GB_create_container(gb_main, "misc");
710
711            if (!gb_cont1 || !gb_cont2) error = GB_await_error();
712
713            const char *single_key[] = { "entry", NULp };
714            const char *mixed_keys[] = { "item", "other", NULp };
715
716            if (!error) error = create_many_items(gb_cont1, single_key, 100);
717            if (!error) error = create_many_items(gb_cont2, mixed_keys, 20);
718        }
719        TEST_EXPECT_NO_ERROR(error);
720    }
721    ~TestDB() {
722        GB_close(gb_main);
723    }
724};
725
726__ATTR__REDUCED_OPTIMIZE void TEST_DB_search() {
727    TestDB db;
728    TEST_EXPECT_NO_ERROR(db.error);
729
730    {
731        GB_transaction ta(db.gb_main);
732
733        TEST_EXPECT_EQUAL(GB_number_of_subentries(db.gb_cont1), 100);
734        TEST_EXPECT_EQUAL(GB_number_of_subentries(db.gb_cont2), 20);
735
736        {
737            GBDATA *gb_any_child = GB_child(db.gb_cont1);
738            TEST_REJECT_NULL(gb_any_child);
739            TEST_EXPECT_EQUAL(gb_any_child, GB_entry(db.gb_cont1, "entry"));
740            TEST_EXPECT_EQUAL(gb_any_child, GB_search(db.gb_main, "container1/entry", GB_FIND));
741
742            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_main, "zic-zac", GB_FIND), "Invalid char '-' in key 'zic-zac'");
743
744            // check (obsolete) link-syntax (@@@ remove together with GB_OBSOLETE)
745            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_main, "->entry",                         GB_FIND),   "Invalid char '-' in key '->entry'");
746            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_main, "entry->bla",                      GB_FIND),   "Invalid char '-' in key 'entry->bla'");
747            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_main, "container1/entry->nowhere",       GB_FIND),   "Invalid char '-' in key 'entry->nowhere'");
748            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_main, "container1/nosuchentry->nowhere", GB_STRING), "Invalid char '-' in key 'nosuchentry->nowhere");
749            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_main, "entry->",                         GB_FIND),   "Invalid char '-' in key 'entry->'");
750
751            // check ..
752            TEST_EXPECT_EQUAL(GB_search(gb_any_child, "..", GB_FIND), db.gb_cont1);
753            TEST_EXPECT_EQUAL(GB_search(gb_any_child, "../..", GB_FIND), db.gb_main);
754            // above main entry
755            TEST_EXPECT_NORESULT__NOERROREXPORTED(GB_search(gb_any_child, "../../..", GB_FIND));
756            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(gb_any_child, "../../../impossible", GB_STRING), "cannot use '..' at root node");
757
758            TEST_EXPECT_EQUAL(GB_search(gb_any_child, "", GB_FIND), gb_any_child); // return self
759            TEST_EXPECT_EQUAL(GB_search(gb_any_child, "/container1/", GB_FIND), db.gb_cont1); // accept trailing slash for container ..
760            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(gb_any_child, "/container1/entry/name/", GB_FIND), "terminal entry 'name' cannot be used as container"); // .. but not for normal entries
761
762            TEST_EXPECT_EQUAL(GB_search(gb_any_child, "/", GB_FIND), db.gb_main);
763            TEST_EXPECT_EQUAL(GB_search(gb_any_child, "/container1/..", GB_FIND), db.gb_main);
764            TEST_EXPECT_NORESULT__NOERROREXPORTED(GB_search(gb_any_child, "/..", GB_FIND)); // main has no parent
765        }
766
767        {
768            GBDATA *gb_child1 = GB_child(db.gb_cont2);   TEST_REJECT_NULL(gb_child1);
769            GBDATA *gb_child2 = GB_nextChild(gb_child1); TEST_REJECT_NULL(gb_child2);
770            GBDATA *gb_child3 = GB_nextChild(gb_child2); TEST_REJECT_NULL(gb_child3);
771            GBDATA *gb_child4 = GB_nextChild(gb_child3); TEST_REJECT_NULL(gb_child4);
772
773            TEST_EXPECT_EQUAL(GB_read_key_pntr(gb_child1), "item");
774            TEST_EXPECT_EQUAL(GB_read_key_pntr(gb_child2), "other");
775            TEST_EXPECT_EQUAL(GB_read_key_pntr(gb_child3), "item");
776            TEST_EXPECT_EQUAL(GB_read_key_pntr(gb_child4), "other");
777
778            TEST_EXPECT_NORESULT__NOERROREXPORTED(GB_entry(db.gb_cont2, "entry"));
779            TEST_EXPECT_EQUAL(GB_entry(db.gb_cont2, "item"),  gb_child1);
780            TEST_EXPECT_EQUAL(GB_entry(db.gb_cont2, "other"), gb_child2);
781
782            TEST_EXPECT_EQUAL(GB_nextEntry(gb_child1), gb_child3);
783            TEST_EXPECT_EQUAL(GB_nextEntry(gb_child2), gb_child4);
784
785            // check ..
786            TEST_EXPECT_EQUAL(GB_search(gb_child3, "../item", GB_FIND), gb_child1);
787            TEST_EXPECT_EQUAL(GB_search(gb_child3, "../other", GB_FIND), gb_child2);
788            TEST_EXPECT_EQUAL(GB_search(gb_child3, "../other/../item", GB_FIND), gb_child1);
789        }
790
791        // ------------------------
792        //      single entries
793
794        {
795            GBDATA *gb_str      = GB_searchOrCreate_string(db.gb_cont_misc, "str", "bla");   TEST_REJECT_NULL(gb_str);
796            GBDATA *gb_str_same = GB_searchOrCreate_string(db.gb_cont_misc, "str", "blub");
797
798            TEST_EXPECT_EQUAL(gb_str, gb_str_same);
799            TEST_EXPECT_EQUAL(GB_read_char_pntr(gb_str), "bla");
800
801            GBDATA *gb_int      = GB_searchOrCreate_int(db.gb_cont_misc, "int", 4711);   TEST_REJECT_NULL(gb_int);
802            GBDATA *gb_int_same = GB_searchOrCreate_int(db.gb_cont_misc, "int", 2012);
803
804            TEST_EXPECT_EQUAL(gb_int, gb_int_same);
805            TEST_EXPECT_EQUAL(GB_read_int(gb_int), 4711);
806
807            GBDATA *gb_float      = GB_searchOrCreate_float(db.gb_cont_misc, "float", 0.815);   TEST_REJECT_NULL(gb_float);
808            GBDATA *gb_float_same = GB_searchOrCreate_float(db.gb_cont_misc, "float", 3.1415);
809
810            TEST_EXPECT_EQUAL(gb_float, gb_float_same);
811            TEST_EXPECT_SIMILAR(GB_read_float(gb_float), 0.815, 0.0001);
812
813            TEST_EXPECT_EQUAL  (GB_read_char_pntr(GB_searchOrCreate_string(db.gb_cont_misc, "sub1/str",    "blub")), "blub");
814            TEST_EXPECT_EQUAL  (GB_read_int      (GB_searchOrCreate_int   (db.gb_cont_misc, "sub2/int",    2012)),   2012);
815            TEST_EXPECT_SIMILAR(GB_read_float    (GB_searchOrCreate_float (db.gb_cont_misc, "sub3/float", 3.1415)), 3.1415, 0.00001);
816
817            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_searchOrCreate_float (db.gb_cont_misc, "int",   0.815), "has wrong type");
818            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_searchOrCreate_float (db.gb_cont_misc, "str",   0.815), "has wrong type");
819            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_searchOrCreate_int   (db.gb_cont_misc, "float", 4711),  "has wrong type");
820            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_searchOrCreate_int   (db.gb_cont_misc, "str",   4711),  "has wrong type");
821            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_searchOrCreate_string(db.gb_cont_misc, "float", "bla"), "has wrong type");
822            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_searchOrCreate_string(db.gb_cont_misc, "int",   "bla"), "has wrong type");
823
824            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_searchOrCreate_string(db.gb_cont_misc, "*", "bla"), "Invalid char '*' in key '*'");
825            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_searchOrCreate_string(db.gb_cont_misc, "int*", "bla"), "Invalid char '*' in key 'int*'");
826            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_searchOrCreate_string(db.gb_cont_misc, "sth_else*", "bla"), "Invalid char '*' in key 'sth_else*'");
827
828            GBDATA *gb_entry;
829            TEST_EXPECT_NORESULT__NOERROREXPORTED(GB_search(db.gb_cont_misc, "subcont/entry", GB_FIND));
830            TEST_EXPECT_RESULT__NOERROREXPORTED(gb_entry = GB_search(db.gb_cont_misc, "subcont/entry", GB_INT));
831            TEST_EXPECT_EQUAL(GB_read_int(gb_entry), 0);
832
833            GBDATA *gb_cont1;
834            TEST_EXPECT_RESULT__NOERROREXPORTED(gb_cont1 = GB_search(db.gb_cont_misc, "subcont", GB_CREATE_CONTAINER));
835            TEST_EXPECT_EQUAL(GB_child(gb_cont1), gb_entry); // test GB_search found the container created implicitely above
836
837            GBDATA *gb_cont2;
838            TEST_EXPECT_RESULT__NOERROREXPORTED(gb_cont2 = GB_search(db.gb_cont_misc, "subcont2", GB_CREATE_CONTAINER)); // create new container
839
840            // -----------------------
841            //      search values
842
843            GBDATA *gb_4711;
844            TEST_EXPECT_RESULT__NOERROREXPORTED(gb_4711 = GB_find_int(db.gb_cont_misc, "int", 4711, SEARCH_CHILD));
845            TEST_EXPECT_EQUAL(gb_4711, gb_int);
846
847            TEST_EXPECT_NULL(GB_find_int(db.gb_cont_misc, "int", 2012, SEARCH_CHILD));
848
849            GBDATA *gb_bla;
850            TEST_EXPECT_RESULT__NOERROREXPORTED(gb_bla = GB_find_string(db.gb_cont_misc, "str", "bla", GB_MIND_CASE, SEARCH_CHILD));
851            TEST_EXPECT_EQUAL(gb_bla, gb_str);
852            TEST_EXPECT_RESULT__NOERROREXPORTED(gb_bla = GB_find_string(gb_4711, "str", "bla", GB_MIND_CASE, SEARCH_BROTHER));
853            TEST_EXPECT_EQUAL(gb_bla, gb_str);
854
855            TEST_EXPECT_NULL(GB_find_string(db.gb_cont_misc, "str", "blub", GB_MIND_CASE, SEARCH_CHILD));
856
857            GBDATA *gb_name;
858            TEST_REJECT_NULL                  (GB_find_string          (db.gb_cont1, "name", "entry 77",  GB_MIND_CASE,   SEARCH_GRANDCHILD));
859            TEST_REJECT_NULL                  (GB_find_string          (db.gb_cont1, "name", "entry 99",  GB_MIND_CASE,   SEARCH_GRANDCHILD));
860            TEST_EXPECT_NORESULT__NOERROREXPORTED(GB_find_string          (db.gb_cont1, "name", "entry 100", GB_MIND_CASE,   SEARCH_GRANDCHILD));
861            TEST_EXPECT_NORESULT__NOERROREXPORTED(GB_find_string          (db.gb_cont1, "name", "ENTRY 13",  GB_MIND_CASE,   SEARCH_GRANDCHILD));
862            TEST_REJECT_NULL                  (gb_name = GB_find_string(db.gb_cont1, "name", "ENTRY 13",  GB_IGNORE_CASE, SEARCH_GRANDCHILD));
863
864            GBDATA *gb_sub;
865            TEST_REJECT_NULL(gb_sub = GB_get_father(gb_name));        TEST_EXPECT_EQUAL(GBT_get_name(gb_sub), "entry 13");
866            TEST_REJECT_NULL(gb_sub = GB_followingEntry(gb_sub, 0));  TEST_EXPECT_EQUAL(GBT_get_name(gb_sub), "entry 14");
867            TEST_REJECT_NULL(gb_sub = GB_followingEntry(gb_sub, 1));  TEST_EXPECT_EQUAL(GBT_get_name(gb_sub), "entry 16");
868            TEST_REJECT_NULL(gb_sub = GB_followingEntry(gb_sub, 10)); TEST_EXPECT_EQUAL(GBT_get_name(gb_sub), "entry 27");
869            TEST_EXPECT_NORESULT__NOERROREXPORTED(GB_followingEntry(gb_sub, -1U));
870            TEST_REJECT_NULL(gb_sub = GB_brother(gb_sub, "entry"));   TEST_EXPECT_EQUAL(GBT_get_name(gb_sub), "entry 0");
871
872            TEST_EXPECT_EQUAL(gb_bla = GB_search(gb_cont1, "/misc/str", GB_FIND), gb_str); // fullpath (ignores passed GBDATA)
873
874            // keyless search
875            TEST_EXPECT_NORESULT__NOERROREXPORTED(GB_search(db.gb_cont_misc, NULp, GB_FIND));
876
877            // ----------------------------
878            //      GB_get_GBDATA_path
879
880            TEST_EXPECT_EQUAL(GB_get_GBDATA_path(gb_int),   "/main/misc/int");
881            TEST_EXPECT_EQUAL(GB_get_GBDATA_path(gb_str),   "/main/misc/str");
882            TEST_EXPECT_EQUAL(GB_get_GBDATA_path(gb_entry), "/main/misc/subcont/entry");
883            TEST_EXPECT_EQUAL(GB_get_GBDATA_path(gb_cont2),  "/main/misc/subcont2");
884
885            // -----------------------------------------
886            //      search/create with changed type
887
888            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_cont_misc, "str", GB_INT), "Inconsistent type for field");
889            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_cont_misc, "subcont", GB_STRING), "Inconsistent type for field");
890
891            // ---------------------------------------------
892            //      search containers with trailing '/'
893
894            GBDATA *gb_cont2_slash;
895            TEST_EXPECT_RESULT__NOERROREXPORTED(gb_cont2_slash = GB_search(db.gb_cont_misc, "subcont2/", GB_FIND));
896            TEST_EXPECT_EQUAL(gb_cont2_slash, gb_cont2);
897
898            GBDATA *gb_rootcont;
899            GBDATA *gb_rootcont_slash;
900            TEST_EXPECT_RESULT__NOERROREXPORTED(gb_rootcont = GB_search(db.gb_main, "/container1", GB_FIND));
901            TEST_EXPECT_RESULT__NOERROREXPORTED(gb_rootcont_slash = GB_search(db.gb_main, "/container1/", GB_FIND));
902            TEST_EXPECT_EQUAL(gb_rootcont_slash, gb_rootcont);
903        }
904
905        {
906            // check invalid searches
907            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_cont_misc, "sub/inva*lid",   GB_INT),  "Invalid char '*' in key 'inva*lid'");
908            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_cont_misc, "sub/1 3",        GB_INT),  "Invalid char ' ' in key '1 3'");
909            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_cont_misc, "sub//sub",       GB_INT),  "Invalid '//' in key 'sub//sub'");
910            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_cont_misc, "subcont2//",     GB_FIND), "Invalid '//' in key 'subcont2//'");
911            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_cont_misc, "sub/..sub",      GB_INT),  "Expected '/' after '..' in key '..sub'");
912            TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_cont_misc, "subcont/entry/", GB_FIND), "terminal entry 'entry' cannot be used as container");
913        }
914
915        // ---------------
916        //      marks
917
918        TEST_EXPECT_EQUAL(GB_number_of_marked_subentries(db.gb_cont1), 15);
919        TEST_EXPECT_EQUAL(GB_number_of_marked_subentries(db.gb_cont2), 3);
920
921        TEST_EXPECT_NORESULT__NOERROREXPORTED(GB_first_marked(db.gb_cont2, "entry"));
922
923        GBDATA *gb_marked;
924        TEST_REJECT_NULL(gb_marked = GB_first_marked(db.gb_cont2, "item"));
925        TEST_EXPECT_EQUAL(GBT_get_name(gb_marked), "item 0");
926
927        TEST_EXPECT_NORESULT__NOERROREXPORTED(GB_following_marked(gb_marked, "item", 1)); // skip over last
928       
929        TEST_REJECT_NULL(gb_marked = GB_next_marked(gb_marked, "item")); // find last
930        TEST_EXPECT_EQUAL(GBT_get_name(gb_marked), "item 14");
931    }
932
933    // @@@ delete some species, then search again
934
935    TEST_EXPECT_NO_ERROR(db.error);
936}
937
938
939#endif // UNIT_TESTS
Note: See TracBrowser for help on using the repository browser.