Pluggable libheimbase interface for DBs and misc libheimbase enhancements
[Code reviewed by Love Hörnquist Åstrand <lha@kth.se>] Added heim_db_*() entry points for dealing with databases, and make krb5_aname_to_localname() use it. The following enhancements to libheimbase are included: - Add heim_data_t and heim_string_t "reference" variants to avoid memory copies of potentially large data/strings. See heim_data_ref_create() and heim_string_ref_create(). - Added enhancements to heim_array_t to allow their use for queues and stacks, and to improve performance. See heim_array_insert_value(). - Added XPath-like accessors for heim_object_t. See heim_path_get(), heim_path_copy(), heim_path_create(), and heim_path_delete(). These are used extensively in the DB framework's generic composition of ACID support and in the test_base program - Made libheimbase more consistent with Core Foundation naming conventions. See heim_{dict, array}_{get, copy}_value() and heim_path_{get, copy}(). - Added functionality to and fixed bugs in base/json.c: - heim_serialize(); - depth limit for JSON parsing (for DoS protection); - pretty-printing; - JSON compliance (see below); - flag options for parsing and serializing; these are needed because of impedance mismatches between heim_object_t and JSON (e.g., heim_dict_t allows non-string keys, but JSON does not; heimbase supports binary data, while JSON does not). - Added heim_error_enomem(). - Enhanced the test_base program to test new functionality and to use heim_path*() to better test JSON encoding. This includes some fuzz testing of JSON parsing, and running the test under valgrind. - Started to add doxygen documentation for libheimbase (but doc build for libheimbase is still incomplete). Note that there's still some incomplete JSON support: - JSON string quoting is not fully implemented; - libheimbase lacks support for real numbers, while JSON has it -- otherwise libheimbase is a superset of JSON, specifically in that any heim_object_t can be a key for an associative array. The following DB backends are supported natively: - "sorted-text", a binary search of sorted (in C locale), flat text files; - "json", a backend that stores DB contents serialized as JSON (this is intended for configuration-like contents). The DB framework supports: - multiple key/value tables per-DB - ACID transactions The DB framework also natively implements ACID transactions for any DB backends that a) do not provide transactions natively, b) do provide lock/unlock/sync methods (even on Windows). This includes autocommit of DB updates outside transactions. Future DB enhancements may include: - add backends for various DB types (BDB, CDB, MDB, ...); - make libhdb use heim_db_t; - add a command-line tool for interfacing to databases via libheimbase (e.g., to get/set/delete values, create/copy/ backup DBs, inspect history, check integrity); - framework-level transaction logging (with redo and undo logging), for generic incremental replication; - framework-level DB integrity checking. We could store a MAC of the XOR of a hash function applied to {key, value} for every entry in the DB, then use this to check DB integrity incrementally during incremental replication, as well as for the whole DB.
This commit is contained in:
687
base/test_base.c
687
base/test_base.c
@@ -33,12 +33,35 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a test of libheimbase functionality. If you make any changes
|
||||
* to libheimbase or to this test you should run it under valgrind with
|
||||
* the following options:
|
||||
*
|
||||
* -v --track-fds=yes --num-callers=30 --leak-check=full
|
||||
*
|
||||
* and make sure that there are no leaks that don't have
|
||||
* __heim_string_constant() or heim_db_register() in their stack trace.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#ifndef WIN32
|
||||
#include <sys/file.h>
|
||||
#endif
|
||||
#ifdef HAVE_IO_H
|
||||
#include <io.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "heimbase.h"
|
||||
#include "heimbasepriv.h"
|
||||
#include "baselocl.h"
|
||||
|
||||
static void
|
||||
memory_free(heim_object_t obj)
|
||||
@@ -160,72 +183,702 @@ test_error(void)
|
||||
static int
|
||||
test_json(void)
|
||||
{
|
||||
static char *j[] = {
|
||||
"{ \"k1\" : \"s1\", \"k2\" : \"s2\" }",
|
||||
"{ \"k1\" : [\"s1\", \"s2\", \"s3\"], \"k2\" : \"s3\" }",
|
||||
"{ \"k1\" : {\"k2\":\"s1\",\"k3\":\"s2\",\"k4\":\"s3\"}, \"k5\" : \"s4\" }",
|
||||
"[ \"v1\", \"v2\", [\"v3\",\"v4\",[\"v 5\",\" v 7 \"]], -123456789, "
|
||||
"null, true, false, 123456789, \"\"]",
|
||||
" -1"
|
||||
};
|
||||
char *s;
|
||||
size_t i, k;
|
||||
heim_object_t o, o2;
|
||||
heim_string_t k1 = heim_string_create("k1");
|
||||
|
||||
o = heim_json_create("\"string\"", NULL);
|
||||
o = heim_json_create("\"string\"", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "string");
|
||||
heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
|
||||
heim_assert(strcmp("string", heim_string_get_utf8(o)) == 0, "wrong string");
|
||||
heim_release(o);
|
||||
|
||||
o = heim_json_create(" \"foo\\\"bar\" ]", NULL);
|
||||
o = heim_json_create(" \"foo\\\"bar\" ]", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "string");
|
||||
heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
|
||||
heim_assert(strcmp("foo\"bar", heim_string_get_utf8(o)) == 0, "wrong string");
|
||||
heim_release(o);
|
||||
|
||||
o = heim_json_create(" { \"key\" : \"value\" }", NULL);
|
||||
o = heim_json_create(" { \"key\" : \"value\" }", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "dict");
|
||||
heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
|
||||
heim_release(o);
|
||||
|
||||
o = heim_json_create(" { \"k1\" : \"s1\", \"k2\" : \"s2\" }", NULL);
|
||||
o = heim_json_create("{ { \"k1\" : \"s1\", \"k2\" : \"s2\" } : \"s3\", "
|
||||
"{ \"k3\" : \"s4\" } : -1 }", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "dict");
|
||||
heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
|
||||
o2 = heim_dict_get_value(o, k1);
|
||||
heim_release(o);
|
||||
|
||||
o = heim_json_create("{ { \"k1\" : \"s1\", \"k2\" : \"s2\" } : \"s3\", "
|
||||
"{ \"k3\" : \"s4\" } : -1 }", 10,
|
||||
HEIM_JSON_F_STRICT_DICT, NULL);
|
||||
heim_assert(o == NULL, "dict");
|
||||
|
||||
o = heim_json_create(" { \"k1\" : \"s1\", \"k2\" : \"s2\" }", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "dict");
|
||||
heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
|
||||
o2 = heim_dict_copy_value(o, k1);
|
||||
heim_assert(heim_get_tid(o2) == heim_string_get_type_id(), "string-tid");
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
o = heim_json_create(" { \"k1\" : { \"k2\" : \"s2\" } }", NULL);
|
||||
o = heim_json_create(" { \"k1\" : { \"k2\" : \"s2\" } }", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "dict");
|
||||
heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
|
||||
o2 = heim_dict_get_value(o, k1);
|
||||
o2 = heim_dict_copy_value(o, k1);
|
||||
heim_assert(heim_get_tid(o2) == heim_dict_get_type_id(), "dict-tid");
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
o = heim_json_create("{ \"k1\" : 1 }", NULL);
|
||||
o = heim_json_create("{ \"k1\" : 1 }", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "array");
|
||||
heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
|
||||
o2 = heim_dict_get_value(o, k1);
|
||||
o2 = heim_dict_copy_value(o, k1);
|
||||
heim_assert(heim_get_tid(o2) == heim_number_get_type_id(), "number-tid");
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
o = heim_json_create("-10", NULL);
|
||||
o = heim_json_create("-10", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "number");
|
||||
heim_assert(heim_get_tid(o) == heim_number_get_type_id(), "number-tid");
|
||||
heim_release(o);
|
||||
|
||||
o = heim_json_create("99", NULL);
|
||||
o = heim_json_create("99", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "number");
|
||||
heim_assert(heim_get_tid(o) == heim_number_get_type_id(), "number-tid");
|
||||
heim_release(o);
|
||||
|
||||
o = heim_json_create(" [ 1 ]", NULL);
|
||||
o = heim_json_create(" [ 1 ]", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "array");
|
||||
heim_assert(heim_get_tid(o) == heim_array_get_type_id(), "array-tid");
|
||||
heim_release(o);
|
||||
|
||||
o = heim_json_create(" [ -1 ]", NULL);
|
||||
o = heim_json_create(" [ -1 ]", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "array");
|
||||
heim_assert(heim_get_tid(o) == heim_array_get_type_id(), "array-tid");
|
||||
heim_release(o);
|
||||
|
||||
for (i = 0; i < (sizeof (j) / sizeof (j[0])); i++) {
|
||||
o = heim_json_create(j[i], 10, 0, NULL);
|
||||
if (o == NULL) {
|
||||
fprintf(stderr, "Failed to parse this JSON: %s\n", j[i]);
|
||||
return 1;
|
||||
}
|
||||
heim_release(o);
|
||||
/* Simple fuzz test */
|
||||
for (k = strlen(j[i]) - 1; k > 0; k--) {
|
||||
o = heim_json_create_with_bytes(j[i], k, 10, 0, NULL);
|
||||
if (o != NULL) {
|
||||
fprintf(stderr, "Invalid JSON parsed: %.*s\n", k, j[i]);
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
/* Again, but this time make it so valgrind can find invalid accesses */
|
||||
for (k = strlen(j[i]) - 1; k > 0; k--) {
|
||||
s = strndup(j[i], k);
|
||||
if (s == NULL)
|
||||
return ENOMEM;
|
||||
o = heim_json_create(s, 10, 0, NULL);
|
||||
free(s);
|
||||
if (o != NULL) {
|
||||
fprintf(stderr, "Invalid JSON parsed: %s\n", j[i]);
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
/* Again, but with no NUL termination */
|
||||
for (k = strlen(j[i]) - 1; k > 0; k--) {
|
||||
s = malloc(k);
|
||||
if (s == NULL)
|
||||
return ENOMEM;
|
||||
memcpy(s, j[i], k);
|
||||
o = heim_json_create_with_bytes(s, k, 10, 0, NULL);
|
||||
free(s);
|
||||
if (o != NULL) {
|
||||
fprintf(stderr, "Invalid JSON parsed: %s\n", j[i]);
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
heim_release(k1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_path(void)
|
||||
{
|
||||
heim_dict_t dict = heim_dict_create(11);
|
||||
heim_string_t p1 = heim_string_create("abc");
|
||||
heim_string_t p2a = heim_string_create("def");
|
||||
heim_string_t p2b = heim_string_create("DEF");
|
||||
heim_number_t p3 = heim_number_create(0);
|
||||
heim_string_t p4a = heim_string_create("ghi");
|
||||
heim_string_t p4b = heim_string_create("GHI");
|
||||
heim_array_t a = heim_array_create();
|
||||
heim_number_t l1 = heim_number_create(42);
|
||||
heim_number_t l2 = heim_number_create(813);
|
||||
heim_number_t l3 = heim_number_create(1234);
|
||||
heim_string_t k1 = heim_string_create("k1");
|
||||
heim_string_t k2 = heim_string_create("k2");
|
||||
heim_string_t k3 = heim_string_create("k3");
|
||||
heim_string_t k2_1 = heim_string_create("k2-1");
|
||||
heim_string_t k2_2 = heim_string_create("k2-2");
|
||||
heim_string_t k2_3 = heim_string_create("k2-3");
|
||||
heim_string_t k2_4 = heim_string_create("k2-4");
|
||||
heim_string_t k2_5 = heim_string_create("k2-5");
|
||||
heim_string_t k2_5_1 = heim_string_create("k2-5-1");
|
||||
heim_object_t o;
|
||||
heim_object_t neg_num;
|
||||
int ret;
|
||||
|
||||
if (!dict || !p1 || !p2a || !p2b || !p4a || !p4b)
|
||||
return ENOMEM;
|
||||
|
||||
ret = heim_path_create(dict, 11, a, NULL, p1, p2a, NULL);
|
||||
heim_release(a);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = heim_path_create(dict, 11, l3, NULL, p1, p2b, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
o = heim_path_get(dict, NULL, p1, p2b, NULL);
|
||||
if (o != l3)
|
||||
return 1;
|
||||
ret = heim_path_create(dict, 11, NULL, NULL, p1, p2a, p3, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = heim_path_create(dict, 11, l1, NULL, p1, p2a, p3, p4a, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = heim_path_create(dict, 11, l2, NULL, p1, p2a, p3, p4b, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
o = heim_path_get(dict, NULL, p1, p2a, p3, p4a, NULL);
|
||||
if (o != l1)
|
||||
return 1;
|
||||
o = heim_path_get(dict, NULL, p1, p2a, p3, p4b, NULL);
|
||||
if (o != l2)
|
||||
return 1;
|
||||
|
||||
heim_release(dict);
|
||||
|
||||
/* Test that JSON parsing works right by using heim_path_get() */
|
||||
dict = heim_json_create("{\"k1\":1,"
|
||||
"\"k2\":{\"k2-1\":21,"
|
||||
"\"k2-2\":null,"
|
||||
"\"k2-3\":true,"
|
||||
"\"k2-4\":false,"
|
||||
"\"k2-5\":[1,2,3,{\"k2-5-1\":-1},-2]},"
|
||||
"\"k3\":[true,false,0,42]}", 10, 0, NULL);
|
||||
heim_assert(dict != NULL, "dict");
|
||||
o = heim_path_get(dict, NULL, k1, NULL);
|
||||
if (heim_cmp(o, heim_number_create(1))) return 1;
|
||||
o = heim_path_get(dict, NULL, k2, NULL);
|
||||
if (heim_get_tid(o) != heim_dict_get_type_id()) return 1;
|
||||
o = heim_path_get(dict, NULL, k2, k2_1, NULL);
|
||||
if (heim_cmp(o, heim_number_create(21))) return 1;
|
||||
o = heim_path_get(dict, NULL, k2, k2_2, NULL);
|
||||
if (heim_cmp(o, heim_null_create())) return 1;
|
||||
o = heim_path_get(dict, NULL, k2, k2_3, NULL);
|
||||
if (heim_cmp(o, heim_bool_create(1))) return 1;
|
||||
o = heim_path_get(dict, NULL, k2, k2_4, NULL);
|
||||
if (heim_cmp(o, heim_bool_create(0))) return 1;
|
||||
o = heim_path_get(dict, NULL, k2, k2_5, NULL);
|
||||
if (heim_get_tid(o) != heim_array_get_type_id()) return 1;
|
||||
o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(0), NULL);
|
||||
if (heim_cmp(o, heim_number_create(1))) return 1;
|
||||
o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(1), NULL);
|
||||
if (heim_cmp(o, heim_number_create(2))) return 1;
|
||||
o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(3), k2_5_1, NULL);
|
||||
if (heim_cmp(o, neg_num = heim_number_create(-1))) return 1;
|
||||
heim_release(neg_num);
|
||||
o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(4), NULL);
|
||||
if (heim_cmp(o, neg_num = heim_number_create(-2))) return 1;
|
||||
heim_release(neg_num);
|
||||
o = heim_path_get(dict, NULL, k3, heim_number_create(3), NULL);
|
||||
if (heim_cmp(o, heim_number_create(42))) return 1;
|
||||
|
||||
heim_release(dict);
|
||||
heim_release(p1);
|
||||
heim_release(p2a);
|
||||
heim_release(p2b);
|
||||
heim_release(p4a);
|
||||
heim_release(p4b);
|
||||
heim_release(k1);
|
||||
heim_release(k2);
|
||||
heim_release(k3);
|
||||
heim_release(k2_1);
|
||||
heim_release(k2_2);
|
||||
heim_release(k2_3);
|
||||
heim_release(k2_4);
|
||||
heim_release(k2_5);
|
||||
heim_release(k2_5_1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct dict_db {
|
||||
heim_dict_t dict;
|
||||
int locked;
|
||||
} *dict_db_t;
|
||||
|
||||
static int
|
||||
dict_db_open(void *plug, const char *dbtype, const char *dbname,
|
||||
heim_dict_t options, void **db, heim_error_t *error)
|
||||
{
|
||||
dict_db_t dictdb;
|
||||
heim_dict_t contents = NULL;
|
||||
|
||||
if (error)
|
||||
*error = NULL;
|
||||
if (dbtype && *dbtype && strcmp(dbtype, "dictdb"))
|
||||
return EINVAL;
|
||||
if (dbname && *dbname && strcmp(dbname, "MEMORY") != 0)
|
||||
return EINVAL;
|
||||
dictdb = heim_alloc(sizeof (*dictdb), "dict_db", NULL);
|
||||
if (dictdb == NULL)
|
||||
return ENOMEM;
|
||||
|
||||
if (contents != NULL)
|
||||
dictdb->dict = contents;
|
||||
else {
|
||||
dictdb->dict = heim_dict_create(29);
|
||||
if (dictdb->dict == NULL) {
|
||||
heim_release(dictdb);
|
||||
return ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
*db = dictdb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dict_db_close(void *db, heim_error_t *error)
|
||||
{
|
||||
dict_db_t dictdb = db;
|
||||
|
||||
if (error)
|
||||
*error = NULL;
|
||||
heim_release(dictdb->dict);
|
||||
heim_release(dictdb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dict_db_lock(void *db, int read_only, heim_error_t *error)
|
||||
{
|
||||
dict_db_t dictdb = db;
|
||||
|
||||
if (error)
|
||||
*error = NULL;
|
||||
if (dictdb->locked)
|
||||
return EWOULDBLOCK;
|
||||
dictdb->locked = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dict_db_unlock(void *db, heim_error_t *error)
|
||||
{
|
||||
dict_db_t dictdb = db;
|
||||
|
||||
if (error)
|
||||
*error = NULL;
|
||||
dictdb->locked = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static heim_data_t
|
||||
dict_db_copy_value(void *db, heim_string_t table, heim_data_t key,
|
||||
heim_error_t *error)
|
||||
{
|
||||
dict_db_t dictdb = db;
|
||||
|
||||
if (error)
|
||||
*error = NULL;
|
||||
|
||||
return heim_retain(heim_path_get(dictdb->dict, error, table, key, NULL));
|
||||
}
|
||||
|
||||
static int
|
||||
dict_db_set_value(void *db, heim_string_t table,
|
||||
heim_data_t key, heim_data_t value, heim_error_t *error)
|
||||
{
|
||||
dict_db_t dictdb = db;
|
||||
|
||||
if (error)
|
||||
*error = NULL;
|
||||
|
||||
if (table == NULL)
|
||||
table = HSTR("");
|
||||
|
||||
return heim_path_create(dictdb->dict, 29, value, error, table, key, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
dict_db_del_key(void *db, heim_string_t table, heim_data_t key,
|
||||
heim_error_t *error)
|
||||
{
|
||||
dict_db_t dictdb = db;
|
||||
|
||||
if (error)
|
||||
*error = NULL;
|
||||
|
||||
if (table == NULL)
|
||||
table = HSTR("");
|
||||
|
||||
heim_path_delete(dictdb->dict, error, table, key, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dict_db_iter_ctx {
|
||||
heim_db_iterator_f_t iter_f;
|
||||
void *iter_ctx;
|
||||
};
|
||||
|
||||
static void dict_db_iter_f(heim_object_t key, heim_object_t value, void *arg)
|
||||
{
|
||||
struct dict_db_iter_ctx *ctx = arg;
|
||||
|
||||
ctx->iter_f((heim_object_t)key, (heim_object_t)value, ctx->iter_ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
dict_db_iter(void *db, heim_string_t table, void *iter_data,
|
||||
heim_db_iterator_f_t iter_f, heim_error_t *error)
|
||||
{
|
||||
dict_db_t dictdb = db;
|
||||
struct dict_db_iter_ctx ctx;
|
||||
heim_dict_t table_dict;
|
||||
|
||||
if (error)
|
||||
*error = NULL;
|
||||
|
||||
if (table == NULL)
|
||||
table = HSTR("");
|
||||
|
||||
table_dict = heim_dict_copy_value(dictdb->dict, table);
|
||||
if (table_dict == NULL)
|
||||
return;
|
||||
|
||||
ctx.iter_ctx = iter_data;
|
||||
ctx.iter_f = iter_f;
|
||||
|
||||
heim_dict_iterate_f(table_dict, &ctx, dict_db_iter_f);
|
||||
heim_release(table_dict);
|
||||
}
|
||||
|
||||
static void
|
||||
test_db_iter(heim_data_t k, heim_data_t v, void *arg)
|
||||
{
|
||||
int *ret = arg;
|
||||
|
||||
heim_assert(heim_get_tid(k) == heim_data_get_type_id(), "...");
|
||||
|
||||
if (heim_data_get_length(k) == strlen("msg") && strncmp(heim_data_get_ptr(k), "msg", strlen("msg")) == 0 &&
|
||||
heim_data_get_length(v) == strlen("abc") && strncmp(heim_data_get_ptr(v), "abc", strlen("abc")) == 0)
|
||||
*ret &= ~(1);
|
||||
else if (heim_data_get_length(k) == strlen("msg2") && strncmp(heim_data_get_ptr(k), "msg2", strlen("msg2")) == 0 &&
|
||||
heim_data_get_length(v) == strlen("FooBar") && strncmp(heim_data_get_ptr(v), "FooBar", strlen("FooBar")) == 0)
|
||||
*ret &= ~(2);
|
||||
else
|
||||
*ret |= 4;
|
||||
}
|
||||
|
||||
static struct heim_db_type dbt = {
|
||||
1, dict_db_open, NULL, dict_db_close,
|
||||
dict_db_lock, dict_db_unlock, NULL, NULL, NULL, NULL,
|
||||
dict_db_copy_value, dict_db_set_value,
|
||||
dict_db_del_key, dict_db_iter
|
||||
};
|
||||
|
||||
static int
|
||||
test_db(const char *dbtype, const char *dbname)
|
||||
{
|
||||
heim_data_t k1, k2, v, v1, v2, v3;
|
||||
heim_db_t db;
|
||||
int ret;
|
||||
|
||||
if (dbtype == NULL) {
|
||||
ret = heim_db_register("dictdb", NULL, &dbt);
|
||||
heim_assert(!ret, "...");
|
||||
db = heim_db_create("dictdb", "foo", NULL, NULL);
|
||||
heim_assert(!db, "...");
|
||||
db = heim_db_create("foobar", "MEMORY", NULL, NULL);
|
||||
heim_assert(!db, "...");
|
||||
db = heim_db_create("dictdb", "MEMORY", NULL, NULL);
|
||||
heim_assert(db, "...");
|
||||
} else {
|
||||
heim_dict_t options;
|
||||
|
||||
options = heim_dict_create(11);
|
||||
if (options == NULL) return ENOMEM;
|
||||
if (heim_dict_set_value(options, HSTR("journal-filename"),
|
||||
HSTR("json-journal")))
|
||||
return ENOMEM;
|
||||
if (heim_dict_set_value(options, HSTR("create"), heim_null_create()))
|
||||
return ENOMEM;
|
||||
if (heim_dict_set_value(options, HSTR("truncate"), heim_null_create()))
|
||||
return ENOMEM;
|
||||
db = heim_db_create(dbtype, dbname, options, NULL);
|
||||
heim_assert(db, "...");
|
||||
heim_release(options);
|
||||
}
|
||||
|
||||
k1 = heim_data_create("msg", strlen("msg"));
|
||||
k2 = heim_data_create("msg2", strlen("msg2"));
|
||||
v1 = heim_data_create("Hello world!", strlen("Hello world!"));
|
||||
v2 = heim_data_create("FooBar", strlen("FooBar"));
|
||||
v3 = heim_data_create("abc", strlen("abc"));
|
||||
|
||||
ret = heim_db_set_value(db, NULL, k1, v1, NULL);
|
||||
heim_assert(!ret, "...");
|
||||
|
||||
v = heim_db_copy_value(db, NULL, k1, NULL);
|
||||
heim_assert(v && !heim_cmp(v, v1), "...");
|
||||
heim_release(v);
|
||||
|
||||
ret = heim_db_set_value(db, NULL, k2, v2, NULL);
|
||||
heim_assert(!ret, "...");
|
||||
|
||||
v = heim_db_copy_value(db, NULL, k2, NULL);
|
||||
heim_assert(v && !heim_cmp(v, v2), "...");
|
||||
heim_release(v);
|
||||
|
||||
ret = heim_db_set_value(db, NULL, k1, v3, NULL);
|
||||
heim_assert(!ret, "...");
|
||||
|
||||
v = heim_db_copy_value(db, NULL, k1, NULL);
|
||||
heim_assert(v && !heim_cmp(v, v3), "...");
|
||||
heim_release(v);
|
||||
|
||||
ret = 3;
|
||||
heim_db_iterate_f(db, NULL, &ret, test_db_iter, NULL);
|
||||
heim_assert(!ret, "...");
|
||||
|
||||
ret = heim_db_begin(db, 0, NULL);
|
||||
heim_assert(!ret, "...");
|
||||
|
||||
ret = heim_db_commit(db, NULL);
|
||||
heim_assert(!ret, "...");
|
||||
|
||||
ret = heim_db_begin(db, 0, NULL);
|
||||
heim_assert(!ret, "...");
|
||||
|
||||
ret = heim_db_rollback(db, NULL);
|
||||
heim_assert(!ret, "...");
|
||||
|
||||
ret = heim_db_begin(db, 0, NULL);
|
||||
heim_assert(!ret, "...");
|
||||
|
||||
ret = heim_db_set_value(db, NULL, k1, v1, NULL);
|
||||
heim_assert(!ret, "...");
|
||||
|
||||
v = heim_db_copy_value(db, NULL, k1, NULL);
|
||||
heim_assert(v && !heim_cmp(v, v1), "...");
|
||||
heim_release(v);
|
||||
|
||||
ret = heim_db_rollback(db, NULL);
|
||||
heim_assert(!ret, "...");
|
||||
|
||||
v = heim_db_copy_value(db, NULL, k1, NULL);
|
||||
heim_assert(v && !heim_cmp(v, v3), "...");
|
||||
heim_release(v);
|
||||
|
||||
ret = heim_db_begin(db, 0, NULL);
|
||||
heim_assert(!ret, "...");
|
||||
|
||||
ret = heim_db_set_value(db, NULL, k1, v1, NULL);
|
||||
heim_assert(!ret, "...");
|
||||
|
||||
v = heim_db_copy_value(db, NULL, k1, NULL);
|
||||
heim_assert(v && !heim_cmp(v, v1), "...");
|
||||
heim_release(v);
|
||||
|
||||
ret = heim_db_commit(db, NULL);
|
||||
heim_assert(!ret, "...");
|
||||
|
||||
v = heim_db_copy_value(db, NULL, k1, NULL);
|
||||
heim_assert(v && !heim_cmp(v, v1), "...");
|
||||
heim_release(v);
|
||||
|
||||
ret = heim_db_begin(db, 0, NULL);
|
||||
heim_assert(!ret, "...");
|
||||
|
||||
ret = heim_db_delete_key(db, NULL, k1, NULL);
|
||||
heim_assert(!ret, "...");
|
||||
|
||||
v = heim_db_copy_value(db, NULL, k1, NULL);
|
||||
heim_assert(v == NULL, "...");
|
||||
heim_release(v);
|
||||
|
||||
ret = heim_db_rollback(db, NULL);
|
||||
heim_assert(!ret, "...");
|
||||
|
||||
v = heim_db_copy_value(db, NULL, k1, NULL);
|
||||
heim_assert(v && !heim_cmp(v, v1), "...");
|
||||
heim_release(v);
|
||||
|
||||
if (dbtype != NULL) {
|
||||
heim_data_t k3 = heim_data_create("value-is-a-dict", strlen("value-is-a-dict"));
|
||||
heim_dict_t vdict = heim_dict_create(11);
|
||||
heim_db_t db2;
|
||||
|
||||
heim_assert(k3 && vdict, "...");
|
||||
ret = heim_dict_set_value(vdict, HSTR("vdict-k1"), heim_number_create(11));
|
||||
heim_assert(!ret, "...");
|
||||
ret = heim_dict_set_value(vdict, HSTR("vdict-k2"), heim_null_create());
|
||||
heim_assert(!ret, "...");
|
||||
ret = heim_dict_set_value(vdict, HSTR("vdict-k3"), HSTR("a value"));
|
||||
heim_assert(!ret, "...");
|
||||
ret = heim_db_set_value(db, NULL, k3, (heim_data_t)vdict, NULL);
|
||||
heim_assert(!ret, "...");
|
||||
|
||||
heim_release(vdict);
|
||||
|
||||
db2 = heim_db_create(dbtype, dbname, NULL, NULL);
|
||||
heim_assert(db2, "...");
|
||||
|
||||
vdict = (heim_dict_t)heim_db_copy_value(db2, NULL, k3, NULL);
|
||||
heim_release(db2);
|
||||
heim_release(k3);
|
||||
heim_assert(vdict, "...");
|
||||
heim_assert(heim_get_tid(vdict) == heim_dict_get_type_id(), "...");
|
||||
|
||||
v = heim_dict_copy_value(vdict, HSTR("vdict-k1"));
|
||||
heim_assert(v && !heim_cmp(v, heim_number_create(11)), "...");
|
||||
heim_release(v);
|
||||
|
||||
v = heim_dict_copy_value(vdict, HSTR("vdict-k2"));
|
||||
heim_assert(v && !heim_cmp(v, heim_null_create()), "...");
|
||||
heim_release(v);
|
||||
|
||||
v = heim_dict_copy_value(vdict, HSTR("vdict-k3"));
|
||||
heim_assert(v && !heim_cmp(v, HSTR("a value")), "...");
|
||||
heim_release(v);
|
||||
|
||||
heim_release(vdict);
|
||||
}
|
||||
|
||||
heim_release(db);
|
||||
heim_release(k1);
|
||||
heim_release(k2);
|
||||
heim_release(v1);
|
||||
heim_release(v2);
|
||||
heim_release(v3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct test_array_iter_ctx {
|
||||
char buf[256];
|
||||
};
|
||||
|
||||
static void test_array_iter(heim_object_t elt, void *arg)
|
||||
{
|
||||
struct test_array_iter_ctx *iter_ctx = arg;
|
||||
|
||||
strcat(iter_ctx->buf, heim_string_get_utf8((heim_string_t)elt));
|
||||
}
|
||||
|
||||
static int
|
||||
test_array()
|
||||
{
|
||||
struct test_array_iter_ctx iter_ctx;
|
||||
heim_string_t s1 = heim_string_create("abc");
|
||||
heim_string_t s2 = heim_string_create("def");
|
||||
heim_string_t s3 = heim_string_create("ghi");
|
||||
heim_string_t s4 = heim_string_create("jkl");
|
||||
heim_string_t s5 = heim_string_create("mno");
|
||||
heim_string_t s6 = heim_string_create("pqr");
|
||||
heim_array_t a = heim_array_create();
|
||||
|
||||
if (!s1 || !s2 || !s3 || !s4 || !s5 || !s6 || !a)
|
||||
return ENOMEM;
|
||||
|
||||
heim_array_append_value(a, s4);
|
||||
heim_array_append_value(a, s5);
|
||||
heim_array_insert_value(a, 0, s3);
|
||||
heim_array_insert_value(a, 0, s2);
|
||||
heim_array_append_value(a, s6);
|
||||
heim_array_insert_value(a, 0, s1);
|
||||
|
||||
iter_ctx.buf[0] = '\0';
|
||||
heim_array_iterate_f(a, &iter_ctx, test_array_iter);
|
||||
if (strcmp(iter_ctx.buf, "abcdefghijklmnopqr") != 0)
|
||||
return 1;
|
||||
|
||||
iter_ctx.buf[0] = '\0';
|
||||
heim_array_delete_value(a, 2);
|
||||
heim_array_iterate_f(a, &iter_ctx, test_array_iter);
|
||||
if (strcmp(iter_ctx.buf, "abcdefjklmnopqr") != 0)
|
||||
return 1;
|
||||
|
||||
iter_ctx.buf[0] = '\0';
|
||||
heim_array_delete_value(a, 2);
|
||||
heim_array_iterate_f(a, &iter_ctx, test_array_iter);
|
||||
if (strcmp(iter_ctx.buf, "abcdefmnopqr") != 0)
|
||||
return 1;
|
||||
|
||||
iter_ctx.buf[0] = '\0';
|
||||
heim_array_delete_value(a, 0);
|
||||
heim_array_iterate_f(a, &iter_ctx, test_array_iter);
|
||||
if (strcmp(iter_ctx.buf, "defmnopqr") != 0)
|
||||
return 1;
|
||||
|
||||
iter_ctx.buf[0] = '\0';
|
||||
heim_array_delete_value(a, 2);
|
||||
heim_array_iterate_f(a, &iter_ctx, test_array_iter);
|
||||
if (strcmp(iter_ctx.buf, "defmno") != 0)
|
||||
return 1;
|
||||
|
||||
heim_array_insert_value(a, 0, s1);
|
||||
iter_ctx.buf[0] = '\0';
|
||||
heim_array_iterate_f(a, &iter_ctx, test_array_iter);
|
||||
if (strcmp(iter_ctx.buf, "abcdefmno") != 0)
|
||||
return 1;
|
||||
|
||||
heim_array_insert_value(a, 0, s2);
|
||||
iter_ctx.buf[0] = '\0';
|
||||
heim_array_iterate_f(a, &iter_ctx, test_array_iter);
|
||||
if (strcmp(iter_ctx.buf, "defabcdefmno") != 0)
|
||||
return 1;
|
||||
|
||||
heim_array_append_value(a, s3);
|
||||
iter_ctx.buf[0] = '\0';
|
||||
heim_array_iterate_f(a, &iter_ctx, test_array_iter);
|
||||
if (strcmp(iter_ctx.buf, "defabcdefmnoghi") != 0)
|
||||
return 1;
|
||||
|
||||
heim_array_append_value(a, s6);
|
||||
iter_ctx.buf[0] = '\0';
|
||||
heim_array_iterate_f(a, &iter_ctx, test_array_iter);
|
||||
if (strcmp(iter_ctx.buf, "defabcdefmnoghipqr") != 0)
|
||||
return 1;
|
||||
|
||||
heim_release(s1);
|
||||
heim_release(s2);
|
||||
heim_release(s3);
|
||||
heim_release(s4);
|
||||
heim_release(s5);
|
||||
heim_release(s6);
|
||||
heim_release(a);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
@@ -238,6 +891,10 @@ main(int argc, char **argv)
|
||||
res |= test_string();
|
||||
res |= test_error();
|
||||
res |= test_json();
|
||||
res |= test_path();
|
||||
res |= test_db(NULL, NULL);
|
||||
res |= test_db("json", argc > 1 ? argv[1] : "test_db.json");
|
||||
res |= test_array();
|
||||
|
||||
return res ? 1 : 0;
|
||||
}
|
||||
|
Reference in New Issue
Block a user