blob: b9b35a61d7ce89fc09a756d393a71ad0d104fd07 [file] [log] [blame]
Joshua Brindle13cd4c82008-08-19 15:30:36 -04001/* 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
8struct dbase_join;
9typedef 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 */
21struct 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
37static 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 */
169static 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
238int 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 */
267void 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 */
275dbase_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};