xref: /illumos-gate/usr/src/cmd/sendmail/db/db/db_am.c (revision 2a8bcb4efb45d99ac41c94a75c396b362c414f7f)
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1998
5  *	Sleepycat Software.  All rights reserved.
6  */
7 
8 #include "config.h"
9 
10 #ifndef lint
11 static const char sccsid[] = "@(#)db_am.c	10.15 (Sleepycat) 12/30/98";
12 #endif /* not lint */
13 
14 #ifndef NO_SYSTEM_INCLUDES
15 #include <sys/types.h>
16 
17 #include <errno.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #endif
21 
22 #include "db_int.h"
23 #include "shqueue.h"
24 #include "db_page.h"
25 #include "db_shash.h"
26 #include "mp.h"
27 #include "btree.h"
28 #include "hash.h"
29 #include "db_am.h"
30 #include "db_ext.h"
31 
32 static int __db_c_close __P((DBC *));
33 static int __db_cursor __P((DB *, DB_TXN *, DBC **, u_int32_t));
34 static int __db_fd __P((DB *, int *));
35 static int __db_get __P((DB *, DB_TXN *, DBT *, DBT *, u_int32_t));
36 static int __db_put __P((DB *, DB_TXN *, DBT *, DBT *, u_int32_t));
37 
38 /*
39  * __db_init_wrapper --
40  *	Wrapper layer to implement generic DB functions.
41  *
42  * PUBLIC: int __db_init_wrapper __P((DB *));
43  */
44 int
__db_init_wrapper(dbp)45 __db_init_wrapper(dbp)
46 	DB *dbp;
47 {
48 	dbp->close = __db_close;
49 	dbp->cursor = __db_cursor;
50 	dbp->del = NULL;		/* !!! Must be set by access method. */
51 	dbp->fd = __db_fd;
52 	dbp->get = __db_get;
53 	dbp->join = __db_join;
54 	dbp->put = __db_put;
55 	dbp->stat = NULL;		/* !!! Must be set by access method. */
56 	dbp->sync = __db_sync;
57 
58 	return (0);
59 }
60 
61 /*
62  * __db_cursor --
63  *	Allocate and return a cursor.
64  */
65 static int
__db_cursor(dbp,txn,dbcp,flags)66 __db_cursor(dbp, txn, dbcp, flags)
67 	DB *dbp;
68 	DB_TXN *txn;
69 	DBC **dbcp;
70 	u_int32_t flags;
71 {
72 	DBC *dbc, *adbc;
73 	int ret;
74 	db_lockmode_t mode;
75 	u_int32_t op;
76 
77 	DB_PANIC_CHECK(dbp);
78 
79 	/* Take one from the free list if it's available. */
80 	DB_THREAD_LOCK(dbp);
81 	if ((dbc = TAILQ_FIRST(&dbp->free_queue)) != NULL)
82 		TAILQ_REMOVE(&dbp->free_queue, dbc, links);
83 	else {
84 		DB_THREAD_UNLOCK(dbp);
85 
86 		if ((ret = __os_calloc(1, sizeof(DBC), &dbc)) != 0)
87 			return (ret);
88 
89 		dbc->dbp = dbp;
90 		dbc->c_close = __db_c_close;
91 
92 		/* Set up locking information. */
93 		if (F_ISSET(dbp, DB_AM_LOCKING | DB_AM_CDB)) {
94  			/*
95  			 * If we are not threaded, then there is no need to
96  			 * create new locker ids.  We know that no one else
97  			 * is running concurrently using this DB, so we can
98  			 * take a peek at any cursors on the active queue.
99  			 */
100  			if (!F_ISSET(dbp, DB_AM_THREAD) &&
101  			    (adbc = TAILQ_FIRST(&dbp->active_queue)) != NULL)
102  				dbc->lid = adbc->lid;
103  			else
104  				if ((ret = lock_id(dbp->dbenv->lk_info,
105  				    &dbc->lid)) != 0)
106  					goto err;
107 
108 			memcpy(dbc->lock.fileid, dbp->fileid, DB_FILE_ID_LEN);
109 			if (F_ISSET(dbp, DB_AM_CDB)) {
110 				dbc->lock_dbt.size = DB_FILE_ID_LEN;
111 				dbc->lock_dbt.data = dbc->lock.fileid;
112 			} else {
113 				dbc->lock_dbt.size = sizeof(dbc->lock);
114 				dbc->lock_dbt.data = &dbc->lock;
115 			}
116 		}
117 
118 		switch (dbp->type) {
119 		case DB_BTREE:
120 		case DB_RECNO:
121 			if ((ret = __bam_c_init(dbc)) != 0)
122 				goto err;
123 			break;
124 		case DB_HASH:
125 			if ((ret = __ham_c_init(dbc)) != 0)
126 				goto err;
127 			break;
128 		default:
129 			ret = EINVAL;
130 			goto err;
131 		}
132 
133 		DB_THREAD_LOCK(dbp);
134 	}
135 
136 	if ((dbc->txn = txn) == NULL)
137 		dbc->locker = dbc->lid;
138 	else
139 		dbc->locker = txn->txnid;
140 
141 	TAILQ_INSERT_TAIL(&dbp->active_queue, dbc, links);
142 	DB_THREAD_UNLOCK(dbp);
143 
144 	/*
145 	 * If this is the concurrent DB product, then we do all locking
146 	 * in the interface, which is right here.
147 	 */
148 	if (F_ISSET(dbp, DB_AM_CDB)) {
149 		op = LF_ISSET(DB_OPFLAGS_MASK);
150 		mode = (op == DB_WRITELOCK) ? DB_LOCK_WRITE :
151 		    (LF_ISSET(DB_RMW) ? DB_LOCK_IWRITE : DB_LOCK_READ);
152 		if ((ret = lock_get(dbp->dbenv->lk_info, dbc->locker, 0,
153 		    &dbc->lock_dbt, mode, &dbc->mylock)) != 0) {
154 			(void)__db_c_close(dbc);
155 			return (EAGAIN);
156 		}
157 		if (LF_ISSET(DB_RMW))
158 			F_SET(dbc, DBC_RMW);
159 		if (op == DB_WRITELOCK)
160 			F_SET(dbc, DBC_WRITER);
161 	}
162 
163 	*dbcp = dbc;
164 	return (0);
165 
166 err:	__os_free(dbc, sizeof(*dbc));
167 	return (ret);
168 }
169 
170 /*
171  * __db_c_close --
172  *	Close the cursor (recycle for later use).
173  */
174 static int
__db_c_close(dbc)175 __db_c_close(dbc)
176 	DBC *dbc;
177 {
178 	DB *dbp;
179 	int ret, t_ret;
180 
181 	dbp = dbc->dbp;
182 
183 	DB_PANIC_CHECK(dbp);
184 
185 	ret = 0;
186 
187 	/*
188 	 * We cannot release the lock until after we've called the
189 	 * access method specific routine, since btrees may have pending
190 	 * deletes.
191 	 */
192 
193 	/* Remove the cursor from the active queue. */
194 	DB_THREAD_LOCK(dbp);
195 	TAILQ_REMOVE(&dbp->active_queue, dbc, links);
196 	DB_THREAD_UNLOCK(dbp);
197 
198 	/* Call the access specific cursor close routine. */
199 	if ((t_ret = dbc->c_am_close(dbc)) != 0 && ret == 0)
200 		t_ret = ret;
201 
202 	/* Release the lock. */
203 	if (F_ISSET(dbc->dbp, DB_AM_CDB) && dbc->mylock != LOCK_INVALID) {
204 		ret = lock_put(dbc->dbp->dbenv->lk_info, dbc->mylock);
205 		dbc->mylock = LOCK_INVALID;
206 	}
207 
208 	/* Clean up the cursor. */
209 	dbc->flags = 0;
210 
211 #ifdef DEBUG
212 	/*
213 	 * Check for leftover locks, unless we're running with transactions.
214 	 *
215 	 * If we're running tests, display any locks currently held.  It's
216 	 * possible that some applications may hold locks for long periods,
217 	 * e.g., conference room locks, but the DB tests should never close
218 	 * holding locks.
219 	 */
220 	if (F_ISSET(dbp, DB_AM_LOCKING) && dbc->lid == dbc->locker) {
221 		DB_LOCKREQ request;
222 
223 		request.op = DB_LOCK_DUMP;
224 		if ((t_ret = lock_vec(dbp->dbenv->lk_info,
225 		    dbc->locker, 0, &request, 1, NULL)) != 0 && ret == 0)
226 			ret = EAGAIN;
227 	}
228 #endif
229 	/* Move the cursor to the free queue. */
230 	DB_THREAD_LOCK(dbp);
231 	TAILQ_INSERT_TAIL(&dbp->free_queue, dbc, links);
232 	DB_THREAD_UNLOCK(dbp);
233 
234 	return (ret);
235 }
236 
237 #ifdef DEBUG
238 /*
239  * __db_cprint --
240  *	Display the current cursor list.
241  *
242  * PUBLIC: int __db_cprint __P((DB *));
243  */
244 int
__db_cprint(dbp)245 __db_cprint(dbp)
246 	DB *dbp;
247 {
248 	static const FN fn[] = {
249 		{ DBC_RECOVER, 	"recover" },
250 		{ DBC_RMW, 	"read-modify-write" },
251 		{ 0 },
252 	};
253 	DBC *dbc;
254 
255 	DB_THREAD_LOCK(dbp);
256 	for (dbc = TAILQ_FIRST(&dbp->active_queue);
257 	    dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) {
258 		fprintf(stderr,
259 		    "%#0x: dbp: %#0x txn: %#0x lid: %lu locker: %lu",
260 		    (u_int)dbc, (u_int)dbc->dbp, (u_int)dbc->txn,
261 		    (u_long)dbc->lid, (u_long)dbc->locker);
262 		__db_prflags(dbc->flags, fn, stderr);
263 		fprintf(stderr, "\n");
264 	}
265 	DB_THREAD_UNLOCK(dbp);
266 
267 	return (0);
268 }
269 #endif /* DEBUG */
270 
271 /*
272  * __db_c_destroy --
273  *	Destroy the cursor.
274  *
275  * PUBLIC: int __db_c_destroy __P((DBC *));
276  */
277 int
__db_c_destroy(dbc)278 __db_c_destroy(dbc)
279 	DBC *dbc;
280 {
281 	DB *dbp;
282 	int ret;
283 
284 	dbp = dbc->dbp;
285 
286 	/* Remove the cursor from the free queue. */
287 	DB_THREAD_LOCK(dbp);
288 	TAILQ_REMOVE(&dbp->free_queue, dbc, links);
289 	DB_THREAD_UNLOCK(dbp);
290 
291 	/* Call the access specific cursor destroy routine. */
292 	ret = dbc->c_am_destroy == NULL ? 0 : dbc->c_am_destroy(dbc);
293 
294 	/* Free up allocated memory. */
295 	if (dbc->rkey.data != NULL)
296 		__os_free(dbc->rkey.data, dbc->rkey.ulen);
297 	if (dbc->rdata.data != NULL)
298 		__os_free(dbc->rdata.data, dbc->rdata.ulen);
299 	__os_free(dbc, sizeof(*dbc));
300 
301 	return (0);
302 }
303 
304 /*
305  * db_fd --
306  *	Return a file descriptor for flock'ing.
307  */
308 static int
__db_fd(dbp,fdp)309 __db_fd(dbp, fdp)
310         DB *dbp;
311 	int *fdp;
312 {
313 	DB_PANIC_CHECK(dbp);
314 
315 	/*
316 	 * XXX
317 	 * Truly spectacular layering violation.
318 	 */
319 	return (__mp_xxx_fd(dbp->mpf, fdp));
320 }
321 
322 /*
323  * __db_get --
324  *	Return a key/data pair.
325  */
326 static int
__db_get(dbp,txn,key,data,flags)327 __db_get(dbp, txn, key, data, flags)
328 	DB *dbp;
329 	DB_TXN *txn;
330 	DBT *key, *data;
331 	u_int32_t flags;
332 {
333 	DBC *dbc;
334 	int ret, t_ret;
335 
336 	DB_PANIC_CHECK(dbp);
337 
338 	if ((ret = __db_getchk(dbp, key, data, flags)) != 0)
339 		return (ret);
340 
341 	if ((ret = dbp->cursor(dbp, txn, &dbc, 0)) != 0)
342 		return (ret);
343 
344 	DEBUG_LREAD(dbc, txn, "__db_get", key, NULL, flags);
345 
346 	ret = dbc->c_get(dbc, key, data,
347 	    flags == 0 || flags == DB_RMW ? flags | DB_SET : flags);
348 
349 	if ((t_ret = __db_c_close(dbc)) != 0 && ret == 0)
350 		ret = t_ret;
351 
352 	return (ret);
353 }
354 
355 /*
356  * __db_put --
357  *	Store a key/data pair.
358  */
359 static int
__db_put(dbp,txn,key,data,flags)360 __db_put(dbp, txn, key, data, flags)
361 	DB *dbp;
362 	DB_TXN *txn;
363 	DBT *key, *data;
364 	u_int32_t flags;
365 {
366 	DBC *dbc;
367 	DBT tdata;
368 	int ret, t_ret;
369 
370 	DB_PANIC_CHECK(dbp);
371 
372 	if ((ret = __db_putchk(dbp, key, data,
373 	    flags, F_ISSET(dbp, DB_AM_RDONLY), F_ISSET(dbp, DB_AM_DUP))) != 0)
374 		return (ret);
375 
376 	if ((ret = dbp->cursor(dbp, txn, &dbc, DB_WRITELOCK)) != 0)
377 		return (ret);
378 
379 	DEBUG_LWRITE(dbc, txn, "__db_put", key, data, flags);
380 
381 	if (flags == DB_NOOVERWRITE) {
382 		/*
383 		 * Set DB_DBT_USERMEM, this might be a threaded application and
384 		 * the flags checking will catch us.  We don't want the actual
385 		 * data, so request a partial of length 0.
386 		 */
387 		memset(&tdata, 0, sizeof(tdata));
388 		F_SET(&tdata, DB_DBT_USERMEM | DB_DBT_PARTIAL);
389 		if ((ret = dbc->c_get(dbc, key, &tdata, DB_SET | DB_RMW)) == 0)
390 			ret = DB_KEYEXIST;
391 		else if (ret == DB_NOTFOUND)
392 			ret = 0;
393 	}
394 	if (ret == 0)
395 		ret = dbc->c_put(dbc, key, data, DB_KEYLAST);
396 
397 	if ((t_ret = __db_c_close(dbc)) != 0 && ret == 0)
398 		ret = t_ret;
399 
400 	return (ret);
401 }
402 
403 /*
404  * __db_sync --
405  *	Flush the database cache.
406  *
407  * PUBLIC: int __db_sync __P((DB *, u_int32_t));
408  */
409 int
__db_sync(dbp,flags)410 __db_sync(dbp, flags)
411 	DB *dbp;
412 	u_int32_t flags;
413 {
414 	int ret;
415 
416 	DB_PANIC_CHECK(dbp);
417 
418 	if ((ret = __db_syncchk(dbp, flags)) != 0)
419 		return (ret);
420 
421 	/* If it wasn't possible to modify the file, we're done. */
422 	if (F_ISSET(dbp, DB_AM_INMEM | DB_AM_RDONLY))
423 		return (0);
424 
425 	/* Flush any dirty pages from the cache to the backing file. */
426 	if ((ret = memp_fsync(dbp->mpf)) == DB_INCOMPLETE)
427 		ret = 0;
428 
429 	return (ret);
430 }
431