aboutsummaryrefslogtreecommitdiffstats
path: root/libsemanage/src/database_join.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsemanage/src/database_join.c')
-rw-r--r--libsemanage/src/database_join.c297
1 files changed, 297 insertions, 0 deletions
diff --git a/libsemanage/src/database_join.c b/libsemanage/src/database_join.c
new file mode 100644
index 00000000..b9b35a61
--- /dev/null
+++ b/libsemanage/src/database_join.c
@@ -0,0 +1,297 @@
+/* Copyright (C) 2005 Red Hat, Inc. */
+
+/* Object: dbase_join_t (Join)
+ * Extends: dbase_llist_t (Linked List)
+ * Implements: dbase_t (Database)
+ */
+
+struct dbase_join;
+typedef struct dbase_join dbase_t;
+#define DBASE_DEFINED
+
+#include <stdlib.h>
+
+#include "user_internal.h"
+#include "debug.h"
+#include "handle.h"
+#include "database_join.h"
+#include "database_llist.h"
+
+/* JOIN dbase */
+struct dbase_join {
+
+ /* Parent object - must always be
+ * the first field - here we are using
+ * a linked list to store the records */
+ dbase_llist_t llist;
+
+ /* Backing databases - for each
+ * thing being joined */
+ dbase_config_t *join1;
+ dbase_config_t *join2;
+
+ /* JOIN extension */
+ record_join_table_t *rjtable;
+};
+
+static int dbase_join_cache(semanage_handle_t * handle, dbase_join_t * dbase)
+{
+
+ /* Extract all the object tables information */
+ dbase_t *dbase1 = dbase->join1->dbase;
+ dbase_t *dbase2 = dbase->join2->dbase;
+ dbase_table_t *dtable1 = dbase->join1->dtable;
+ dbase_table_t *dtable2 = dbase->join2->dtable;
+ record_table_t *rtable = dbase_llist_get_rtable(&dbase->llist);
+ record_join_table_t *rjtable = dbase->rjtable;
+ record_table_t *rtable1 = dtable1->get_rtable(dbase1);
+ record_table_t *rtable2 = dtable2->get_rtable(dbase2);
+
+ record_key_t *rkey = NULL;
+ record_t *record = NULL;
+ record1_t **records1 = NULL;
+ record2_t **records2 = NULL;
+ unsigned int rcount1 = 0, rcount2 = 0, i = 0, j = 0;
+
+ /* Already cached */
+ if (!dbase_llist_needs_resync(handle, &dbase->llist))
+ return STATUS_SUCCESS;
+
+ /* Update cache serial */
+ dbase_llist_cache_init(&dbase->llist);
+ if (dbase_llist_set_serial(handle, &dbase->llist) < 0)
+ goto err;
+
+ /* First cache any child dbase, which must
+ * be the first thing done when calling dbase
+ * functions internally */
+ if (dtable1->cache(handle, dbase1) < 0)
+ goto err;
+ if (dtable2->cache(handle, dbase2) < 0)
+ goto err;
+
+ /* Fetch records */
+ if (dtable1->list(handle, dbase1, &records1, &rcount1) < 0)
+ goto err;
+ if (dtable2->list(handle, dbase2, &records2, &rcount2) < 0)
+ goto err;
+
+ /* Sort for quicker merge later */
+ qsort(records1, rcount1, sizeof(record1_t *),
+ (int (*)(const void *, const void *))rtable1->compare2_qsort);
+ qsort(records2, rcount2, sizeof(record2_t *),
+ (int (*)(const void *, const void *))rtable2->compare2_qsort);
+
+ /* Now merge into this dbase */
+ while (i < rcount1 || j < rcount2) {
+ int rc;
+
+ /* End of one list, or the other */
+ if (i == rcount1)
+ rc = -1;
+ else if (j == rcount2)
+ rc = 1;
+
+ /* Still more records to go, compare them */
+ else {
+ if (rtable1->key_extract(handle, records1[i], &rkey) <
+ 0)
+ goto err;
+
+ rc = rtable2->compare(records2[j], rkey);
+
+ rtable->key_free(rkey);
+ rkey = NULL;
+ }
+
+ /* Missing record1 data */
+ if (rc < 0) {
+ if (rjtable->join(handle, NULL,
+ records2[j], &record) < 0)
+ goto err;
+ j++;
+ }
+
+ /* Missing record2 data */
+ else if (rc > 0) {
+ if (rjtable->join(handle, records1[i],
+ NULL, &record) < 0)
+ goto err;
+ i++;
+ }
+
+ /* Both records available */
+ else {
+ if (rjtable->join(handle, records1[i],
+ records2[j], &record) < 0)
+ goto err;
+
+ i++;
+ j++;
+ }
+
+ /* Add result record to database */
+ if (dbase_llist_cache_prepend(handle, &dbase->llist, record) <
+ 0)
+ goto err;
+
+ rtable->free(record);
+ record = NULL;
+ }
+
+ /* Update cache serial */
+ if (dbase_llist_set_serial(handle, &dbase->llist) < 0)
+ goto err;
+
+ for (i = 0; i < rcount1; i++)
+ rtable1->free(records1[i]);
+ for (i = 0; i < rcount2; i++)
+ rtable2->free(records2[i]);
+ free(records1);
+ free(records2);
+ return STATUS_SUCCESS;
+
+ err:
+ ERR(handle, "could not cache join database");
+ for (i = 0; i < rcount1; i++)
+ rtable1->free(records1[i]);
+ for (i = 0; i < rcount2; i++)
+ rtable2->free(records2[i]);
+ free(records1);
+ free(records2);
+ rtable->key_free(rkey);
+ rtable->free(record);
+ dbase_llist_drop_cache(&dbase->llist);
+ return STATUS_ERR;
+}
+
+/* Flush database */
+static int dbase_join_flush(semanage_handle_t * handle, dbase_join_t * dbase)
+{
+
+ /* Extract all the object tables information */
+ dbase_t *dbase1 = dbase->join1->dbase;
+ dbase_t *dbase2 = dbase->join2->dbase;
+ dbase_table_t *dtable1 = dbase->join1->dtable;
+ dbase_table_t *dtable2 = dbase->join2->dtable;
+ record_table_t *rtable = dbase_llist_get_rtable(&dbase->llist);
+ record_join_table_t *rjtable = dbase->rjtable;
+ record_table_t *rtable1 = dtable1->get_rtable(dbase1);
+ record_table_t *rtable2 = dtable2->get_rtable(dbase2);
+
+ cache_entry_t *ptr;
+ record_key_t *rkey = NULL;
+ record1_t *record1 = NULL;
+ record2_t *record2 = NULL;
+
+ /* No effect of flush */
+ if (!dbase_llist_is_modified(&dbase->llist))
+ return STATUS_SUCCESS;
+
+ /* Then clear all records from the cache.
+ * This is *not* the same as dropping the cache - it's an explicit
+ * request to delete all current records. We need to do
+ * this because we don't store delete deltas for the join,
+ * so we must re-add all records from scratch */
+ if (dtable1->clear(handle, dbase1) < 0)
+ goto err;
+ if (dtable2->clear(handle, dbase2) < 0)
+ goto err;
+
+ /* For each record, split, and add parts into their corresponding databases */
+ for (ptr = dbase->llist.cache_tail; ptr != NULL; ptr = ptr->prev) {
+
+ if (rtable->key_extract(handle, ptr->data, &rkey) < 0)
+ goto err;
+
+ if (rjtable->split(handle, ptr->data, &record1, &record2) < 0)
+ goto err;
+
+ if (dtable1->add(handle, dbase1, rkey, record1) < 0)
+ goto err;
+
+ if (dtable2->add(handle, dbase2, rkey, record2) < 0)
+ goto err;
+
+ rtable->key_free(rkey);
+ rtable1->free(record1);
+ rtable2->free(record2);
+ rkey = NULL;
+ record1 = NULL;
+ record2 = NULL;
+ }
+
+ /* Note that this function does not flush the child databases, it
+ * leaves that decision up to higher-level code */
+
+ dbase_llist_set_modified(&dbase->llist, 0);
+ return STATUS_SUCCESS;
+
+ err:
+ ERR(handle, "could not flush join database");
+ rtable->key_free(rkey);
+ rtable1->free(record1);
+ rtable2->free(record2);
+ return STATUS_ERR;
+}
+
+int dbase_join_init(semanage_handle_t * handle,
+ record_table_t * rtable,
+ record_join_table_t * rjtable,
+ dbase_config_t * join1,
+ dbase_config_t * join2, dbase_t ** dbase)
+{
+
+ dbase_join_t *tmp_dbase = malloc(sizeof(dbase_join_t));
+
+ if (!tmp_dbase)
+ goto omem;
+
+ dbase_llist_init(&tmp_dbase->llist, rtable, &SEMANAGE_JOIN_DTABLE);
+
+ tmp_dbase->rjtable = rjtable;
+ tmp_dbase->join1 = join1;
+ tmp_dbase->join2 = join2;
+
+ *dbase = tmp_dbase;
+
+ return STATUS_SUCCESS;
+
+ omem:
+ ERR(handle, "out of memory, could not initialize join database");
+ free(tmp_dbase);
+ return STATUS_ERR;
+}
+
+/* Release dbase resources */
+void dbase_join_release(dbase_join_t * dbase)
+{
+
+ dbase_llist_drop_cache(&dbase->llist);
+ free(dbase);
+}
+
+/* JOIN dbase - method table implementation */
+dbase_table_t SEMANAGE_JOIN_DTABLE = {
+
+ /* Cache/Transactions */
+ .cache = dbase_join_cache,
+ .drop_cache = (void *)dbase_llist_drop_cache,
+ .flush = dbase_join_flush,
+ .is_modified = (void *)dbase_llist_is_modified,
+
+ /* Database API */
+ .iterate = (void *)dbase_llist_iterate,
+ .exists = (void *)dbase_llist_exists,
+ .list = (void *)dbase_llist_list,
+ .add = (void *)dbase_llist_add,
+ .set = (void *)dbase_llist_set,
+ .del = (void *)dbase_llist_del,
+ .clear = (void *)dbase_llist_clear,
+ .modify = (void *)dbase_llist_modify,
+ .query = (void *)dbase_llist_query,
+ .count = (void *)dbase_llist_count,
+
+ /* Polymorphism */
+ .get_rtable = (void *)dbase_llist_get_rtable
+};