Home | History | Annotate | Download | only in src
      1 /* Copyright (C) 2005 Red Hat, Inc. */
      2 
      3 /* Object: dbase_join_t (Join)
      4  * Extends: dbase_llist_t (Linked List)
      5  * Implements: dbase_t (Database)
      6  */
      7 
      8 struct dbase_join;
      9 typedef struct dbase_join dbase_t;
     10 #define DBASE_DEFINED
     11 
     12 #include <stdlib.h>
     13 
     14 #include "user_internal.h"
     15 #include "debug.h"
     16 #include "handle.h"
     17 #include "database_join.h"
     18 #include "database_llist.h"
     19 
     20 /* JOIN dbase */
     21 struct dbase_join {
     22 
     23 	/* Parent object - must always be
     24 	 * the first field - here we are using
     25 	 * a linked list to store the records */
     26 	dbase_llist_t llist;
     27 
     28 	/* Backing databases - for each
     29 	 * thing being joined  */
     30 	dbase_config_t *join1;
     31 	dbase_config_t *join2;
     32 
     33 	/* JOIN extension */
     34 	record_join_table_t *rjtable;
     35 };
     36 
     37 static int dbase_join_cache(semanage_handle_t * handle, dbase_join_t * dbase)
     38 {
     39 
     40 	/* Extract all the object tables information */
     41 	dbase_t *dbase1 = dbase->join1->dbase;
     42 	dbase_t *dbase2 = dbase->join2->dbase;
     43 	dbase_table_t *dtable1 = dbase->join1->dtable;
     44 	dbase_table_t *dtable2 = dbase->join2->dtable;
     45 	record_table_t *rtable = dbase_llist_get_rtable(&dbase->llist);
     46 	record_join_table_t *rjtable = dbase->rjtable;
     47 	record_table_t *rtable1 = dtable1->get_rtable(dbase1);
     48 	record_table_t *rtable2 = dtable2->get_rtable(dbase2);
     49 
     50 	record_key_t *rkey = NULL;
     51 	record_t *record = NULL;
     52 	record1_t **records1 = NULL;
     53 	record2_t **records2 = NULL;
     54 	unsigned int rcount1 = 0, rcount2 = 0, i = 0, j = 0;
     55 
     56 	/* Already cached */
     57 	if (!dbase_llist_needs_resync(handle, &dbase->llist))
     58 		return STATUS_SUCCESS;
     59 
     60 	/* Update cache serial */
     61 	dbase_llist_cache_init(&dbase->llist);
     62 	if (dbase_llist_set_serial(handle, &dbase->llist) < 0)
     63 		goto err;
     64 
     65 	/* First cache any child dbase, which must
     66 	 * be the first thing done when calling dbase
     67 	 * functions internally */
     68 	if (dtable1->cache(handle, dbase1) < 0)
     69 		goto err;
     70 	if (dtable2->cache(handle, dbase2) < 0)
     71 		goto err;
     72 
     73 	/* Fetch records */
     74 	if (dtable1->list(handle, dbase1, &records1, &rcount1) < 0)
     75 		goto err;
     76 	if (dtable2->list(handle, dbase2, &records2, &rcount2) < 0)
     77 		goto err;
     78 
     79 	/* Sort for quicker merge later */
     80 	qsort(records1, rcount1, sizeof(record1_t *),
     81 	      (int (*)(const void *, const void *))rtable1->compare2_qsort);
     82 	qsort(records2, rcount2, sizeof(record2_t *),
     83 	      (int (*)(const void *, const void *))rtable2->compare2_qsort);
     84 
     85 	/* Now merge into this dbase */
     86 	while (i < rcount1 || j < rcount2) {
     87 		int rc;
     88 
     89 		/* End of one list, or the other */
     90 		if (i == rcount1)
     91 			rc = -1;
     92 		else if (j == rcount2)
     93 			rc = 1;
     94 
     95 		/* Still more records to go, compare them */
     96 		else {
     97 			if (rtable1->key_extract(handle, records1[i], &rkey) <
     98 			    0)
     99 				goto err;
    100 
    101 			rc = rtable2->compare(records2[j], rkey);
    102 
    103 			rtable->key_free(rkey);
    104 			rkey = NULL;
    105 		}
    106 
    107 		/* Missing record1 data */
    108 		if (rc < 0) {
    109 			if (rjtable->join(handle, NULL,
    110 					  records2[j], &record) < 0)
    111 				goto err;
    112 			j++;
    113 		}
    114 
    115 		/* Missing record2 data */
    116 		else if (rc > 0) {
    117 			if (rjtable->join(handle, records1[i],
    118 					  NULL, &record) < 0)
    119 				goto err;
    120 			i++;
    121 		}
    122 
    123 		/* Both records available */
    124 		else {
    125 			if (rjtable->join(handle, records1[i],
    126 					  records2[j], &record) < 0)
    127 				goto err;
    128 
    129 			i++;
    130 			j++;
    131 		}
    132 
    133 		/* Add result record to database */
    134 		if (dbase_llist_cache_prepend(handle, &dbase->llist, record) <
    135 		    0)
    136 			goto err;
    137 
    138 		rtable->free(record);
    139 		record = NULL;
    140 	}
    141 
    142 	/* Update cache serial */
    143 	if (dbase_llist_set_serial(handle, &dbase->llist) < 0)
    144 		goto err;
    145 
    146 	for (i = 0; i < rcount1; i++)
    147 		rtable1->free(records1[i]);
    148 	for (i = 0; i < rcount2; i++)
    149 		rtable2->free(records2[i]);
    150 	free(records1);
    151 	free(records2);
    152 	return STATUS_SUCCESS;
    153 
    154       err:
    155 	ERR(handle, "could not cache join database");
    156 	for (i = 0; i < rcount1; i++)
    157 		rtable1->free(records1[i]);
    158 	for (i = 0; i < rcount2; i++)
    159 		rtable2->free(records2[i]);
    160 	free(records1);
    161 	free(records2);
    162 	rtable->key_free(rkey);
    163 	rtable->free(record);
    164 	dbase_llist_drop_cache(&dbase->llist);
    165 	return STATUS_ERR;
    166 }
    167 
    168 /* Flush database */
    169 static int dbase_join_flush(semanage_handle_t * handle, dbase_join_t * dbase)
    170 {
    171 
    172 	/* Extract all the object tables information */
    173 	dbase_t *dbase1 = dbase->join1->dbase;
    174 	dbase_t *dbase2 = dbase->join2->dbase;
    175 	dbase_table_t *dtable1 = dbase->join1->dtable;
    176 	dbase_table_t *dtable2 = dbase->join2->dtable;
    177 	record_table_t *rtable = dbase_llist_get_rtable(&dbase->llist);
    178 	record_join_table_t *rjtable = dbase->rjtable;
    179 	record_table_t *rtable1 = dtable1->get_rtable(dbase1);
    180 	record_table_t *rtable2 = dtable2->get_rtable(dbase2);
    181 
    182 	cache_entry_t *ptr;
    183 	record_key_t *rkey = NULL;
    184 	record1_t *record1 = NULL;
    185 	record2_t *record2 = NULL;
    186 
    187 	/* No effect of flush */
    188 	if (!dbase_llist_is_modified(&dbase->llist))
    189 		return STATUS_SUCCESS;
    190 
    191 	/* Then clear all records from the cache.
    192 	 * This is *not* the same as dropping the cache - it's an explicit
    193 	 * request to delete all current records. We need to do
    194 	 * this because we don't store delete deltas for the join,
    195 	 * so we must re-add all records from scratch */
    196 	if (dtable1->clear(handle, dbase1) < 0)
    197 		goto err;
    198 	if (dtable2->clear(handle, dbase2) < 0)
    199 		goto err;
    200 
    201 	/* For each record, split, and add parts into their corresponding databases */
    202 	for (ptr = dbase->llist.cache_tail; ptr != NULL; ptr = ptr->prev) {
    203 
    204 		if (rtable->key_extract(handle, ptr->data, &rkey) < 0)
    205 			goto err;
    206 
    207 		if (rjtable->split(handle, ptr->data, &record1, &record2) < 0)
    208 			goto err;
    209 
    210 		if (dtable1->add(handle, dbase1, rkey, record1) < 0)
    211 			goto err;
    212 
    213 		if (dtable2->add(handle, dbase2, rkey, record2) < 0)
    214 			goto err;
    215 
    216 		rtable->key_free(rkey);
    217 		rtable1->free(record1);
    218 		rtable2->free(record2);
    219 		rkey = NULL;
    220 		record1 = NULL;
    221 		record2 = NULL;
    222 	}
    223 
    224 	/* Note that this function does not flush the child databases, it
    225 	 * leaves that decision up to higher-level code */
    226 
    227 	dbase_llist_set_modified(&dbase->llist, 0);
    228 	return STATUS_SUCCESS;
    229 
    230       err:
    231 	ERR(handle, "could not flush join database");
    232 	rtable->key_free(rkey);
    233 	rtable1->free(record1);
    234 	rtable2->free(record2);
    235 	return STATUS_ERR;
    236 }
    237 
    238 int dbase_join_init(semanage_handle_t * handle,
    239 		    record_table_t * rtable,
    240 		    record_join_table_t * rjtable,
    241 		    dbase_config_t * join1,
    242 		    dbase_config_t * join2, dbase_t ** dbase)
    243 {
    244 
    245 	dbase_join_t *tmp_dbase = malloc(sizeof(dbase_join_t));
    246 
    247 	if (!tmp_dbase)
    248 		goto omem;
    249 
    250 	dbase_llist_init(&tmp_dbase->llist, rtable, &SEMANAGE_JOIN_DTABLE);
    251 
    252 	tmp_dbase->rjtable = rjtable;
    253 	tmp_dbase->join1 = join1;
    254 	tmp_dbase->join2 = join2;
    255 
    256 	*dbase = tmp_dbase;
    257 
    258 	return STATUS_SUCCESS;
    259 
    260       omem:
    261 	ERR(handle, "out of memory, could not initialize join database");
    262 	free(tmp_dbase);
    263 	return STATUS_ERR;
    264 }
    265 
    266 /* Release dbase resources */
    267 void dbase_join_release(dbase_join_t * dbase)
    268 {
    269 
    270 	dbase_llist_drop_cache(&dbase->llist);
    271 	free(dbase);
    272 }
    273 
    274 /* JOIN dbase - method table implementation */
    275 dbase_table_t SEMANAGE_JOIN_DTABLE = {
    276 
    277 	/* Cache/Transactions */
    278 	.cache = dbase_join_cache,
    279 	.drop_cache = (void *)dbase_llist_drop_cache,
    280 	.flush = dbase_join_flush,
    281 	.is_modified = (void *)dbase_llist_is_modified,
    282 
    283 	/* Database API */
    284 	.iterate = (void *)dbase_llist_iterate,
    285 	.exists = (void *)dbase_llist_exists,
    286 	.list = (void *)dbase_llist_list,
    287 	.add = (void *)dbase_llist_add,
    288 	.set = (void *)dbase_llist_set,
    289 	.del = (void *)dbase_llist_del,
    290 	.clear = (void *)dbase_llist_clear,
    291 	.modify = (void *)dbase_llist_modify,
    292 	.query = (void *)dbase_llist_query,
    293 	.count = (void *)dbase_llist_count,
    294 
    295 	/* Polymorphism */
    296 	.get_rtable = (void *)dbase_llist_get_rtable
    297 };
    298