1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996,2008 Oracle.  All rights reserved.
5 *
6 * $Id: db185.c,v 12.13 2008/01/11 16:54:10 bostic Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12
13#ifndef lint
14static const char copyright[] =
15    "Copyright (c) 1996,2008 Oracle.  All rights reserved.\n";
16#endif
17
18#include "db185_int.h"
19
20static int	db185_close __P((DB185 *));
21static int	db185_compare __P((DB *, const DBT *, const DBT *));
22static int	db185_del __P((const DB185 *, const DBT185 *, u_int));
23static int	db185_fd __P((const DB185 *));
24static int	db185_get __P((const DB185 *, const DBT185 *, DBT185 *, u_int));
25static u_int32_t
26		db185_hash __P((DB *, const void *, u_int32_t));
27static size_t	db185_prefix __P((DB *, const DBT *, const DBT *));
28static int	db185_put __P((const DB185 *, DBT185 *, const DBT185 *, u_int));
29static int	db185_seq __P((const DB185 *, DBT185 *, DBT185 *, u_int));
30static int	db185_sync __P((const DB185 *, u_int));
31
32/*
33 * EXTERN: #ifdef _DB185_INT_H_
34 * EXTERN: DB185 *__db185_open
35 * EXTERN:     __P((const char *, int, int, DBTYPE, const void *));
36 * EXTERN: #else
37 * EXTERN: DB *__db185_open
38 * EXTERN:     __P((const char *, int, int, DBTYPE, const void *));
39 * EXTERN: #endif
40 */
41DB185 *
42__db185_open(file, oflags, mode, type, openinfo)
43	const char *file;
44	int oflags, mode;
45	DBTYPE type;
46	const void *openinfo;
47{
48	const BTREEINFO *bi;
49	const HASHINFO *hi;
50	const RECNOINFO *ri;
51	DB *dbp;
52	DB185 *db185p;
53	DB_FH *fhp;
54	int ret;
55
56	dbp = NULL;
57	db185p = NULL;
58
59	if ((ret = db_create(&dbp, NULL, 0)) != 0)
60		goto err;
61
62	if ((ret = __os_calloc(NULL, 1, sizeof(DB185), &db185p)) != 0)
63		goto err;
64
65	/*
66	 * !!!
67	 * The DBTYPE enum wasn't initialized in DB 185, so it's off-by-one
68	 * from DB 2.0.
69	 */
70	switch (type) {
71	case 0:					/* DB_BTREE */
72		type = DB_BTREE;
73		if ((bi = openinfo) != NULL) {
74			if (bi->flags & ~R_DUP)
75				goto einval;
76			if (bi->flags & R_DUP)
77				(void)dbp->set_flags(dbp, DB_DUP);
78			if (bi->cachesize != 0)
79				(void)dbp->set_cachesize
80				    (dbp, 0, bi->cachesize, 0);
81			if (bi->minkeypage != 0)
82				(void)dbp->set_bt_minkey(dbp, bi->minkeypage);
83			if (bi->psize != 0)
84				(void)dbp->set_pagesize(dbp, bi->psize);
85			if (bi->prefix != NULL) {
86				db185p->prefix = bi->prefix;
87				dbp->set_bt_prefix(dbp, db185_prefix);
88			}
89			if (bi->compare != NULL) {
90				db185p->compare = bi->compare;
91				dbp->set_bt_compare(dbp, db185_compare);
92			}
93			if (bi->lorder != 0)
94				dbp->set_lorder(dbp, bi->lorder);
95		}
96		break;
97	case 1:					/* DB_HASH */
98		type = DB_HASH;
99		if ((hi = openinfo) != NULL) {
100			if (hi->bsize != 0)
101				(void)dbp->set_pagesize(dbp, hi->bsize);
102			if (hi->ffactor != 0)
103				(void)dbp->set_h_ffactor(dbp, hi->ffactor);
104			if (hi->nelem != 0)
105				(void)dbp->set_h_nelem(dbp, hi->nelem);
106			if (hi->cachesize != 0)
107				(void)dbp->set_cachesize
108				    (dbp, 0, hi->cachesize, 0);
109			if (hi->hash != NULL) {
110				db185p->hash = hi->hash;
111				(void)dbp->set_h_hash(dbp, db185_hash);
112			}
113			if (hi->lorder != 0)
114				dbp->set_lorder(dbp, hi->lorder);
115		}
116
117		break;
118	case 2:					/* DB_RECNO */
119		type = DB_RECNO;
120
121		/* DB 1.85 did renumbering by default. */
122		(void)dbp->set_flags(dbp, DB_RENUMBER);
123
124		/*
125		 * !!!
126		 * The file name given to DB 1.85 recno is the name of the DB
127		 * 2.0 backing file.  If the file doesn't exist, create it if
128		 * the user has the O_CREAT flag set, DB 1.85 did it for you,
129		 * and DB 2.0 doesn't.
130		 *
131		 * !!!
132		 * Setting the file name to NULL specifies that we're creating
133		 * a temporary backing file, in DB 2.X.  If we're opening the
134		 * DB file read-only, change the flags to read-write, because
135		 * temporary backing files cannot be opened read-only, and DB
136		 * 2.X will return an error.  We are cheating here -- if the
137		 * application does a put on the database, it will succeed --
138		 * although that would be a stupid thing for the application
139		 * to do.
140		 *
141		 * !!!
142		 * Note, the file name in DB 1.85 was a const -- we don't do
143		 * that in DB 2.0, so do that cast.
144		 */
145		if (file != NULL) {
146			if (oflags & O_CREAT &&
147			    __os_exists(NULL, file, NULL) != 0)
148				if (__os_openhandle(NULL, file,
149				    oflags, mode, &fhp) == 0)
150					(void)__os_closehandle(NULL, fhp);
151			(void)dbp->set_re_source(dbp, file);
152
153			if (O_RDONLY)
154				oflags &= ~O_RDONLY;
155			oflags |= O_RDWR;
156			file = NULL;
157		}
158
159		/*
160		 * !!!
161		 * Set the O_CREAT flag in case the application didn't -- in DB
162		 * 1.85 the backing file was the file being created and it may
163		 * exist, but DB 2.X is creating a temporary Btree database and
164		 * we need the create flag to do that.
165		 */
166		oflags |= O_CREAT;
167
168		if ((ri = openinfo) != NULL) {
169			/*
170			 * !!!
171			 * We can't support the bfname field.
172			 */
173#define	BFMSG \
174	"Berkeley DB: DB 1.85's recno bfname field is not supported.\n"
175			if (ri->bfname != NULL) {
176				dbp->errx(dbp, "%s", BFMSG);
177				goto einval;
178			}
179
180			if (ri->flags & ~(R_FIXEDLEN | R_NOKEY | R_SNAPSHOT))
181				goto einval;
182			if (ri->flags & R_FIXEDLEN) {
183				if (ri->bval != 0)
184					(void)dbp->set_re_pad(dbp, ri->bval);
185				if (ri->reclen != 0)
186					(void)dbp->set_re_len(dbp, ri->reclen);
187			} else
188				if (ri->bval != 0)
189					(void)dbp->set_re_delim(dbp, ri->bval);
190
191			/*
192			 * !!!
193			 * We ignore the R_NOKEY flag, but that's okay, it was
194			 * only an optimization that was never implemented.
195			 */
196			if (ri->flags & R_SNAPSHOT)
197				(void)dbp->set_flags(dbp, DB_SNAPSHOT);
198
199			if (ri->cachesize != 0)
200				(void)dbp->set_cachesize
201				    (dbp, 0, ri->cachesize, 0);
202			if (ri->psize != 0)
203				(void)dbp->set_pagesize(dbp, ri->psize);
204			if (ri->lorder != 0)
205				dbp->set_lorder(dbp, ri->lorder);
206		}
207		break;
208	default:
209		goto einval;
210	}
211
212	db185p->close = db185_close;
213	db185p->del = db185_del;
214	db185p->fd = db185_fd;
215	db185p->get = db185_get;
216	db185p->put = db185_put;
217	db185p->seq = db185_seq;
218	db185p->sync = db185_sync;
219
220	/*
221	 * Store a reference so we can indirect from the DB 1.85 structure
222	 * to the underlying DB structure, and vice-versa.  This has to be
223	 * done BEFORE the DB::open method call because the hash callback
224	 * is exercised as part of hash database initialization.
225	 */
226	db185p->dbp = dbp;
227	dbp->api_internal = db185p;
228
229	/* Open the database. */
230	if ((ret = dbp->open(dbp, NULL,
231	    file, NULL, type, __db_openflags(oflags), mode)) != 0)
232		goto err;
233
234	/* Create the cursor used for sequential ops. */
235	if ((ret = dbp->cursor(dbp, NULL, &((DB185 *)db185p)->dbc, 0)) != 0)
236		goto err;
237
238	return (db185p);
239
240einval:	ret = EINVAL;
241
242err:	if (db185p != NULL)
243		__os_free(NULL, db185p);
244	if (dbp != NULL)
245		(void)dbp->close(dbp, 0);
246
247	__os_set_errno(ret);
248	return (NULL);
249}
250
251static int
252db185_close(db185p)
253	DB185 *db185p;
254{
255	DB *dbp;
256	int ret;
257
258	dbp = db185p->dbp;
259
260	ret = dbp->close(dbp, 0);
261
262	__os_free(NULL, db185p);
263
264	if (ret == 0)
265		return (0);
266
267	__os_set_errno(ret);
268	return (-1);
269}
270
271static int
272db185_del(db185p, key185, flags)
273	const DB185 *db185p;
274	const DBT185 *key185;
275	u_int flags;
276{
277	DB *dbp;
278	DBT key;
279	int ret;
280
281	dbp = db185p->dbp;
282
283	memset(&key, 0, sizeof(key));
284	key.data = key185->data;
285	key.size = key185->size;
286
287	if (flags & ~R_CURSOR)
288		goto einval;
289	if (flags & R_CURSOR)
290		ret = db185p->dbc->del(db185p->dbc, 0);
291	else
292		ret = dbp->del(dbp, NULL, &key, 0);
293
294	switch (ret) {
295	case 0:
296		return (0);
297	case DB_NOTFOUND:
298		return (1);
299	}
300
301	if (0) {
302einval:		ret = EINVAL;
303	}
304	__os_set_errno(ret);
305	return (-1);
306}
307
308static int
309db185_fd(db185p)
310	const DB185 *db185p;
311{
312	DB *dbp;
313	int fd, ret;
314
315	dbp = db185p->dbp;
316
317	if ((ret = dbp->fd(dbp, &fd)) == 0)
318		return (fd);
319
320	__os_set_errno(ret);
321	return (-1);
322}
323
324static int
325db185_get(db185p, key185, data185, flags)
326	const DB185 *db185p;
327	const DBT185 *key185;
328	DBT185 *data185;
329	u_int flags;
330{
331	DB *dbp;
332	DBT key, data;
333	int ret;
334
335	dbp = db185p->dbp;
336
337	memset(&key, 0, sizeof(key));
338	key.data = key185->data;
339	key.size = key185->size;
340	memset(&data, 0, sizeof(data));
341	data.data = data185->data;
342	data.size = data185->size;
343
344	if (flags)
345		goto einval;
346
347	switch (ret = dbp->get(dbp, NULL, &key, &data, 0)) {
348	case 0:
349		data185->data = data.data;
350		data185->size = data.size;
351		return (0);
352	case DB_NOTFOUND:
353		return (1);
354	}
355
356	if (0) {
357einval:		ret = EINVAL;
358	}
359	__os_set_errno(ret);
360	return (-1);
361}
362
363static int
364db185_put(db185p, key185, data185, flags)
365	const DB185 *db185p;
366	DBT185 *key185;
367	const DBT185 *data185;
368	u_int flags;
369{
370	DB *dbp;
371	DBC *dbcp_put;
372	DBT key, data;
373	int ret, t_ret;
374
375	dbp = db185p->dbp;
376
377	memset(&key, 0, sizeof(key));
378	key.data = key185->data;
379	key.size = key185->size;
380	memset(&data, 0, sizeof(data));
381	data.data = data185->data;
382	data.size = data185->size;
383
384	switch (flags) {
385	case 0:
386		ret = dbp->put(dbp, NULL, &key, &data, 0);
387		break;
388	case R_CURSOR:
389		ret = db185p->dbc->put(db185p->dbc, &key, &data, DB_CURRENT);
390		break;
391	case R_IAFTER:
392	case R_IBEFORE:
393		if (dbp->type != DB_RECNO)
394			goto einval;
395
396		if ((ret = dbp->cursor(dbp, NULL, &dbcp_put, 0)) != 0)
397			break;
398		if ((ret =
399		    dbcp_put->get(dbcp_put, &key, &data, DB_SET)) == 0) {
400			memset(&data, 0, sizeof(data));
401			data.data = data185->data;
402			data.size = data185->size;
403			ret = dbcp_put->put(dbcp_put, &key, &data,
404			    flags == R_IAFTER ? DB_AFTER : DB_BEFORE);
405		}
406		if ((t_ret = dbcp_put->close(dbcp_put)) != 0 && ret == 0)
407			ret = t_ret;
408		break;
409	case R_NOOVERWRITE:
410		ret = dbp->put(dbp, NULL, &key, &data, DB_NOOVERWRITE);
411		break;
412	case R_SETCURSOR:
413		if (dbp->type != DB_BTREE && dbp->type != DB_RECNO)
414			goto einval;
415
416		if ((ret = dbp->put(dbp, NULL, &key, &data, 0)) != 0)
417			break;
418		ret =
419		    db185p->dbc->get(db185p->dbc, &key, &data, DB_SET_RANGE);
420		break;
421	default:
422		goto einval;
423	}
424
425	switch (ret) {
426	case 0:
427		key185->data = key.data;
428		key185->size = key.size;
429		return (0);
430	case DB_KEYEXIST:
431		return (1);
432	}
433
434	if (0) {
435einval:		ret = EINVAL;
436	}
437	__os_set_errno(ret);
438	return (-1);
439}
440
441static int
442db185_seq(db185p, key185, data185, flags)
443	const DB185 *db185p;
444	DBT185 *key185, *data185;
445	u_int flags;
446{
447	DB *dbp;
448	DBT key, data;
449	int ret;
450
451	dbp = db185p->dbp;
452
453	memset(&key, 0, sizeof(key));
454	key.data = key185->data;
455	key.size = key185->size;
456	memset(&data, 0, sizeof(data));
457	data.data = data185->data;
458	data.size = data185->size;
459
460	switch (flags) {
461	case R_CURSOR:
462		flags = DB_SET_RANGE;
463		break;
464	case R_FIRST:
465		flags = DB_FIRST;
466		break;
467	case R_LAST:
468		if (dbp->type != DB_BTREE && dbp->type != DB_RECNO)
469			goto einval;
470		flags = DB_LAST;
471		break;
472	case R_NEXT:
473		flags = DB_NEXT;
474		break;
475	case R_PREV:
476		if (dbp->type != DB_BTREE && dbp->type != DB_RECNO)
477			goto einval;
478		flags = DB_PREV;
479		break;
480	default:
481		goto einval;
482	}
483	switch (ret = db185p->dbc->get(db185p->dbc, &key, &data, flags)) {
484	case 0:
485		key185->data = key.data;
486		key185->size = key.size;
487		data185->data = data.data;
488		data185->size = data.size;
489		return (0);
490	case DB_NOTFOUND:
491		return (1);
492	}
493
494	if (0) {
495einval:		ret = EINVAL;
496	}
497	__os_set_errno(ret);
498	return (-1);
499}
500
501static int
502db185_sync(db185p, flags)
503	const DB185 *db185p;
504	u_int flags;
505{
506	DB *dbp;
507	int ret;
508
509	dbp = db185p->dbp;
510
511	switch (flags) {
512	case 0:
513		break;
514	case R_RECNOSYNC:
515		/*
516		 * !!!
517		 * We can't support the R_RECNOSYNC flag.
518		 */
519#define	RSMSG \
520	"Berkeley DB: DB 1.85's R_RECNOSYNC sync flag is not supported.\n"
521		dbp->errx(dbp, "%s", RSMSG);
522		/* FALLTHROUGH */
523	default:
524		goto einval;
525	}
526
527	if ((ret = dbp->sync(dbp, 0)) == 0)
528		return (0);
529
530	if (0) {
531einval:		ret = EINVAL;
532	}
533	__os_set_errno(ret);
534	return (-1);
535}
536
537/*
538 * db185_compare --
539 *	Cutout routine to call the user's Btree comparison function.
540 */
541static int
542db185_compare(dbp, a, b)
543	DB *dbp;
544	const DBT *a, *b;
545{
546	DBT185 a185, b185;
547
548	a185.data = a->data;
549	a185.size = a->size;
550	b185.data = b->data;
551	b185.size = b->size;
552
553	return (((DB185 *)dbp->api_internal)->compare(&a185, &b185));
554}
555
556/*
557 * db185_prefix --
558 *	Cutout routine to call the user's Btree prefix function.
559 */
560static size_t
561db185_prefix(dbp, a, b)
562	DB *dbp;
563	const DBT *a, *b;
564{
565	DBT185 a185, b185;
566
567	a185.data = a->data;
568	a185.size = a->size;
569	b185.data = b->data;
570	b185.size = b->size;
571
572	return (((DB185 *)dbp->api_internal)->prefix(&a185, &b185));
573}
574
575/*
576 * db185_hash --
577 *	Cutout routine to call the user's hash function.
578 */
579static u_int32_t
580db185_hash(dbp, key, len)
581	DB *dbp;
582	const void *key;
583	u_int32_t len;
584{
585	return (((DB185 *)dbp->api_internal)->hash(key, (size_t)len));
586}
587