diff options
Diffstat (limited to 'libsemanage/src/database_join.c')
-rw-r--r-- | libsemanage/src/database_join.c | 297 |
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 +}; |