xref: /titanic_41/usr/src/cmd/sendmail/libsmdb/smdb2.c (revision e9af4bc0b1cc30cea75d6ad4aa2fde97d985e9be)
1 /*
2 ** Copyright (c) 1999-2003, 2009 Sendmail, Inc. and its suppliers.
3 **	All rights reserved.
4 **
5 ** By using this file, you agree to the terms and conditions set
6 ** forth in the LICENSE file which can be found at the top level of
7 ** the sendmail distribution.
8 */
9 
10 #include <sm/gen.h>
11 SM_RCSID("@(#)$Id: smdb2.c,v 8.80 2009/11/12 23:07:49 ca Exp $")
12 
13 #include <fcntl.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 
17 
18 #include <sendmail/sendmail.h>
19 #include <libsmdb/smdb.h>
20 
21 #if (DB_VERSION_MAJOR >= 2)
22 
23 # define SMDB2_FILE_EXTENSION "db"
24 
25 struct smdb_db2_database
26 {
27 	DB	*smdb2_db;
28 	int	smdb2_lock_fd;
29 };
30 typedef struct smdb_db2_database SMDB_DB2_DATABASE;
31 
32 /*
33 **  SMDB_TYPE_TO_DB2_TYPE -- Translates smdb database type to db2 type.
34 **
35 **	Parameters:
36 **		type -- The type to translate.
37 **
38 **	Returns:
39 **		The DB2 type that corresponsds to the passed in SMDB type.
40 **		Returns -1 if there is no equivalent type.
41 **
42 */
43 
44 DBTYPE
smdb_type_to_db2_type(type)45 smdb_type_to_db2_type(type)
46 	SMDB_DBTYPE type;
47 {
48 	if (type == SMDB_TYPE_DEFAULT)
49 		return DB_HASH;
50 
51 	if (strncmp(type, SMDB_TYPE_HASH, SMDB_TYPE_HASH_LEN) == 0)
52 		return DB_HASH;
53 
54 	if (strncmp(type, SMDB_TYPE_BTREE, SMDB_TYPE_BTREE_LEN) == 0)
55 		return DB_BTREE;
56 
57 	return DB_UNKNOWN;
58 }
59 /*
60 **  DB2_ERROR_TO_SMDB -- Translates db2 errors to smdbe errors
61 **
62 **	Parameters:
63 **		error -- The error to translate.
64 **
65 **	Returns:
66 **		The SMDBE error corresponding to the db2 error.
67 **		If we don't have a corresponding error, it returs errno.
68 **
69 */
70 
71 int
db2_error_to_smdb(error)72 db2_error_to_smdb(error)
73 	int error;
74 {
75 	int result;
76 
77 	switch (error)
78 	{
79 # ifdef DB_INCOMPLETE
80 		case DB_INCOMPLETE:
81 			result = SMDBE_INCOMPLETE;
82 			break;
83 # endif /* DB_INCOMPLETE */
84 
85 # ifdef DB_NOTFOUND
86 		case DB_NOTFOUND:
87 			result = SMDBE_NOT_FOUND;
88 			break;
89 # endif /* DB_NOTFOUND */
90 
91 # ifdef DB_KEYEMPTY
92 		case DB_KEYEMPTY:
93 			result = SMDBE_KEY_EMPTY;
94 			break;
95 # endif /* DB_KEYEMPTY */
96 
97 # ifdef DB_KEYEXIST
98 		case DB_KEYEXIST:
99 			result = SMDBE_KEY_EXIST;
100 			break;
101 # endif /* DB_KEYEXIST */
102 
103 # ifdef DB_LOCK_DEADLOCK
104 		case DB_LOCK_DEADLOCK:
105 			result = SMDBE_LOCK_DEADLOCK;
106 			break;
107 # endif /* DB_LOCK_DEADLOCK */
108 
109 # ifdef DB_LOCK_NOTGRANTED
110 		case DB_LOCK_NOTGRANTED:
111 			result = SMDBE_LOCK_NOT_GRANTED;
112 			break;
113 # endif /* DB_LOCK_NOTGRANTED */
114 
115 # ifdef DB_LOCK_NOTHELD
116 		case DB_LOCK_NOTHELD:
117 			result = SMDBE_LOCK_NOT_HELD;
118 			break;
119 # endif /* DB_LOCK_NOTHELD */
120 
121 # ifdef DB_RUNRECOVERY
122 		case DB_RUNRECOVERY:
123 			result = SMDBE_RUN_RECOVERY;
124 			break;
125 # endif /* DB_RUNRECOVERY */
126 
127 # ifdef DB_OLD_VERSION
128 		case DB_OLD_VERSION:
129 			result = SMDBE_OLD_VERSION;
130 			break;
131 # endif /* DB_OLD_VERSION */
132 
133 		case 0:
134 			result = SMDBE_OK;
135 			break;
136 
137 		default:
138 			result = error;
139 	}
140 	return result;
141 }
142 /*
143 **  SMDB_PUT_FLAGS_TO_DB2_FLAGS -- Translates smdb put flags to db2 put flags.
144 **
145 **	Parameters:
146 **		flags -- The flags to translate.
147 **
148 **	Returns:
149 **		The db2 flags that are equivalent to the smdb flags.
150 **
151 **	Notes:
152 **		Any invalid flags are ignored.
153 **
154 */
155 
156 unsigned int
smdb_put_flags_to_db2_flags(flags)157 smdb_put_flags_to_db2_flags(flags)
158 	SMDB_FLAG flags;
159 {
160 	int return_flags;
161 
162 	return_flags = 0;
163 
164 	if (bitset(SMDBF_NO_OVERWRITE, flags))
165 		return_flags |= DB_NOOVERWRITE;
166 
167 	return return_flags;
168 }
169 /*
170 **  SMDB_CURSOR_GET_FLAGS_TO_DB2 -- Translates smdb cursor get flags to db2
171 **	getflags.
172 **
173 **	Parameters:
174 **		flags -- The flags to translate.
175 **
176 **	Returns:
177 **		The db2 flags that are equivalent to the smdb flags.
178 **
179 **	Notes:
180 **		-1 is returned if flag is unknown.
181 **
182 */
183 
184 int
smdb_cursor_get_flags_to_db2(flags)185 smdb_cursor_get_flags_to_db2(flags)
186 	SMDB_FLAG flags;
187 {
188 	switch (flags)
189 	{
190 		case SMDB_CURSOR_GET_FIRST:
191 			return DB_FIRST;
192 
193 		case SMDB_CURSOR_GET_LAST:
194 			return DB_LAST;
195 
196 		case SMDB_CURSOR_GET_NEXT:
197 			return DB_NEXT;
198 
199 		case SMDB_CURSOR_GET_RANGE:
200 			return DB_SET_RANGE;
201 
202 		default:
203 			return -1;
204 	}
205 }
206 
207 /*
208 **  Except for smdb_db_open, the rest of these functions correspond to the
209 **  interface laid out in smdb.h.
210 */
211 
212 SMDB_DB2_DATABASE *
smdb2_malloc_database()213 smdb2_malloc_database()
214 {
215 	SMDB_DB2_DATABASE *db2;
216 
217 	db2 = (SMDB_DB2_DATABASE *) malloc(sizeof(SMDB_DB2_DATABASE));
218 	if (db2 != NULL)
219 		db2->smdb2_lock_fd = -1;
220 
221 	return db2;
222 }
223 
224 int
smdb2_close(database)225 smdb2_close(database)
226 	SMDB_DATABASE *database;
227 {
228 	int result;
229 	SMDB_DB2_DATABASE *db2 = (SMDB_DB2_DATABASE *) database->smdb_impl;
230 	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
231 
232 	result = db2_error_to_smdb(db->close(db, 0));
233 	if (db2->smdb2_lock_fd != -1)
234 		close(db2->smdb2_lock_fd);
235 
236 	free(db2);
237 	database->smdb_impl = NULL;
238 
239 	return result;
240 }
241 
242 int
smdb2_del(database,key,flags)243 smdb2_del(database, key, flags)
244 	SMDB_DATABASE *database;
245 	SMDB_DBENT *key;
246 	unsigned int flags;
247 {
248 	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
249 	DBT dbkey;
250 
251 	(void) memset(&dbkey, '\0', sizeof dbkey);
252 	dbkey.data = key->data;
253 	dbkey.size = key->size;
254 	return db2_error_to_smdb(db->del(db, NULL, &dbkey, flags));
255 }
256 
257 int
smdb2_fd(database,fd)258 smdb2_fd(database, fd)
259 	SMDB_DATABASE *database;
260 	int *fd;
261 {
262 	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
263 
264 	return db2_error_to_smdb(db->fd(db, fd));
265 }
266 
267 int
smdb2_lockfd(database)268 smdb2_lockfd(database)
269 	SMDB_DATABASE *database;
270 {
271 	SMDB_DB2_DATABASE *db2 = (SMDB_DB2_DATABASE *) database->smdb_impl;
272 
273 	return db2->smdb2_lock_fd;
274 }
275 
276 int
smdb2_get(database,key,data,flags)277 smdb2_get(database, key, data, flags)
278 	SMDB_DATABASE *database;
279 	SMDB_DBENT *key;
280 	SMDB_DBENT *data;
281 	unsigned int flags;
282 {
283 	int result;
284 	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
285 	DBT dbkey, dbdata;
286 
287 	(void) memset(&dbdata, '\0', sizeof dbdata);
288 	(void) memset(&dbkey, '\0', sizeof dbkey);
289 	dbkey.data = key->data;
290 	dbkey.size = key->size;
291 
292 	result = db->get(db, NULL, &dbkey, &dbdata, flags);
293 	data->data = dbdata.data;
294 	data->size = dbdata.size;
295 	return db2_error_to_smdb(result);
296 }
297 
298 int
smdb2_put(database,key,data,flags)299 smdb2_put(database, key, data, flags)
300 	SMDB_DATABASE *database;
301 	SMDB_DBENT *key;
302 	SMDB_DBENT *data;
303 	unsigned int flags;
304 {
305 	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
306 	DBT dbkey, dbdata;
307 
308 	(void) memset(&dbdata, '\0', sizeof dbdata);
309 	(void) memset(&dbkey, '\0', sizeof dbkey);
310 	dbkey.data = key->data;
311 	dbkey.size = key->size;
312 	dbdata.data = data->data;
313 	dbdata.size = data->size;
314 
315 	return db2_error_to_smdb(db->put(db, NULL, &dbkey, &dbdata,
316 					 smdb_put_flags_to_db2_flags(flags)));
317 }
318 
319 
320 int
smdb2_set_owner(database,uid,gid)321 smdb2_set_owner(database, uid, gid)
322 	SMDB_DATABASE *database;
323 	uid_t uid;
324 	gid_t gid;
325 {
326 # if HASFCHOWN
327 	int fd;
328 	int result;
329 	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
330 
331 	result = db->fd(db, &fd);
332 	if (result != 0)
333 		return result;
334 
335 	result = fchown(fd, uid, gid);
336 	if (result < 0)
337 		return errno;
338 # endif /* HASFCHOWN */
339 
340 	return SMDBE_OK;
341 }
342 
343 int
smdb2_sync(database,flags)344 smdb2_sync(database, flags)
345 	SMDB_DATABASE *database;
346 	unsigned int flags;
347 {
348 	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
349 
350 	return db2_error_to_smdb(db->sync(db, flags));
351 }
352 
353 int
smdb2_cursor_close(cursor)354 smdb2_cursor_close(cursor)
355 	SMDB_CURSOR *cursor;
356 {
357 	int ret;
358 	DBC *dbc = (DBC *) cursor->smdbc_impl;
359 
360 	ret = db2_error_to_smdb(dbc->c_close(dbc));
361 	free(cursor);
362 	return ret;
363 }
364 
365 int
smdb2_cursor_del(cursor,flags)366 smdb2_cursor_del(cursor, flags)
367 	SMDB_CURSOR *cursor;
368 	SMDB_FLAG flags;
369 {
370 	DBC *dbc = (DBC *) cursor->smdbc_impl;
371 
372 	return db2_error_to_smdb(dbc->c_del(dbc, 0));
373 }
374 
375 int
smdb2_cursor_get(cursor,key,value,flags)376 smdb2_cursor_get(cursor, key, value, flags)
377 	SMDB_CURSOR *cursor;
378 	SMDB_DBENT *key;
379 	SMDB_DBENT *value;
380 	SMDB_FLAG flags;
381 {
382 	int db2_flags;
383 	int result;
384 	DBC *dbc = (DBC *) cursor->smdbc_impl;
385 	DBT dbkey, dbdata;
386 
387 	(void) memset(&dbdata, '\0', sizeof dbdata);
388 	(void) memset(&dbkey, '\0', sizeof dbkey);
389 
390 	db2_flags = smdb_cursor_get_flags_to_db2(flags);
391 	result = dbc->c_get(dbc, &dbkey, &dbdata, db2_flags);
392 	if (result == DB_NOTFOUND)
393 		return SMDBE_LAST_ENTRY;
394 	key->data = dbkey.data;
395 	key->size = dbkey.size;
396 	value->data = dbdata.data;
397 	value->size = dbdata.size;
398 	return db2_error_to_smdb(result);
399 }
400 
401 int
smdb2_cursor_put(cursor,key,value,flags)402 smdb2_cursor_put(cursor, key, value, flags)
403 	SMDB_CURSOR *cursor;
404 	SMDB_DBENT *key;
405 	SMDB_DBENT *value;
406 	SMDB_FLAG flags;
407 {
408 	DBC *dbc = (DBC *) cursor->smdbc_impl;
409 	DBT dbkey, dbdata;
410 
411 	(void) memset(&dbdata, '\0', sizeof dbdata);
412 	(void) memset(&dbkey, '\0', sizeof dbkey);
413 	dbkey.data = key->data;
414 	dbkey.size = key->size;
415 	dbdata.data = value->data;
416 	dbdata.size = value->size;
417 
418 	return db2_error_to_smdb(dbc->c_put(dbc, &dbkey, &dbdata, 0));
419 }
420 
421 int
smdb2_cursor(database,cursor,flags)422 smdb2_cursor(database, cursor, flags)
423 	SMDB_DATABASE *database;
424 	SMDB_CURSOR **cursor;
425 	SMDB_FLAG flags;
426 {
427 	int result;
428 	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
429 	DBC *db2_cursor;
430 
431 # if DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6
432 	result = db->cursor(db, NULL, &db2_cursor, 0);
433 # else /* DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6 */
434 	result = db->cursor(db, NULL, &db2_cursor);
435 # endif /* DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6 */
436 	if (result != 0)
437 		return db2_error_to_smdb(result);
438 
439 	*cursor = (SMDB_CURSOR *) malloc(sizeof(SMDB_CURSOR));
440 	if (*cursor == NULL)
441 		return SMDBE_MALLOC;
442 
443 	(*cursor)->smdbc_close = smdb2_cursor_close;
444 	(*cursor)->smdbc_del = smdb2_cursor_del;
445 	(*cursor)->smdbc_get = smdb2_cursor_get;
446 	(*cursor)->smdbc_put = smdb2_cursor_put;
447 	(*cursor)->smdbc_impl = db2_cursor;
448 
449 	return SMDBE_OK;
450 }
451 
452 # if DB_VERSION_MAJOR == 2
453 static int
smdb_db_open_internal(db_name,db_type,db_flags,db_params,db)454 smdb_db_open_internal(db_name, db_type, db_flags, db_params, db)
455 	char *db_name;
456 	DBTYPE db_type;
457 	int db_flags;
458 	SMDB_DBPARAMS *db_params;
459 	DB **db;
460 {
461 	void *params;
462 	DB_INFO db_info;
463 
464 	params = NULL;
465 	(void) memset(&db_info, '\0', sizeof db_info);
466 	if (db_params != NULL)
467 	{
468 		db_info.db_cachesize = db_params->smdbp_cache_size;
469 		if (db_type == DB_HASH)
470 			db_info.h_nelem = db_params->smdbp_num_elements;
471 		if (db_params->smdbp_allow_dup)
472 			db_info.flags |= DB_DUP;
473 		params = &db_info;
474 	}
475 	return db_open(db_name, db_type, db_flags, DBMMODE, NULL, params, db);
476 }
477 # endif /* DB_VERSION_MAJOR == 2 */
478 
479 # if DB_VERSION_MAJOR > 2
480 static int
smdb_db_open_internal(db_name,db_type,db_flags,db_params,db)481 smdb_db_open_internal(db_name, db_type, db_flags, db_params, db)
482 	char *db_name;
483 	DBTYPE db_type;
484 	int db_flags;
485 	SMDB_DBPARAMS *db_params;
486 	DB **db;
487 {
488 	int result;
489 
490 	result = db_create(db, NULL, 0);
491 	if (result != 0 || *db == NULL)
492 		return result;
493 
494 	if (db_params != NULL)
495 	{
496 		result = (*db)->set_cachesize(*db, 0,
497 					      db_params->smdbp_cache_size, 0);
498 		if (result != 0)
499 		{
500 			(void) (*db)->close((*db), 0);
501 			*db = NULL;
502 			return db2_error_to_smdb(result);
503 		}
504 		if (db_type == DB_HASH)
505 		{
506 			result = (*db)->set_h_nelem(*db, db_params->smdbp_num_elements);
507 			if (result != 0)
508 			{
509 				(void) (*db)->close(*db, 0);
510 				*db = NULL;
511 				return db2_error_to_smdb(result);
512 			}
513 		}
514 		if (db_params->smdbp_allow_dup)
515 		{
516 			result = (*db)->set_flags(*db, DB_DUP);
517 			if (result != 0)
518 			{
519 				(void) (*db)->close(*db, 0);
520 				*db = NULL;
521 				return db2_error_to_smdb(result);
522 			}
523 		}
524 	}
525 
526 	result = (*db)->open(*db,
527 			     DBTXN	/* transaction for DB 4.1 */
528 			     db_name, NULL, db_type, db_flags, DBMMODE);
529 	if (result != 0)
530 	{
531 		(void) (*db)->close(*db, 0);
532 		*db = NULL;
533 	}
534 	return db2_error_to_smdb(result);
535 }
536 # endif /* DB_VERSION_MAJOR > 2 */
537 /*
538 **  SMDB_DB_OPEN -- Opens a db database.
539 **
540 **	Parameters:
541 **		database -- An unallocated database pointer to a pointer.
542 **		db_name -- The name of the database without extension.
543 **		mode -- File permisions for a created database.
544 **		mode_mask -- Mode bits that must match on an opened database.
545 **		sff -- Flags for safefile.
546 **		type -- The type of database to open
547 **			See smdb_type_to_db2_type for valid types.
548 **		user_info -- User information for file permissions.
549 **		db_params --
550 **			An SMDB_DBPARAMS struct including params. These
551 **			are processed according to the type of the
552 **			database. Currently supported params (only for
553 **			HASH type) are:
554 **			   num_elements
555 **			   cache_size
556 **
557 **	Returns:
558 **		SMDBE_OK -- Success, other errno:
559 **		SMDBE_MALLOC -- Cannot allocate memory.
560 **		SMDBE_BAD_OPEN -- db_open didn't return an error, but
561 **				 somehow the DB pointer is NULL.
562 **		Anything else: translated error from db2
563 */
564 
565 int
smdb_db_open(database,db_name,mode,mode_mask,sff,type,user_info,db_params)566 smdb_db_open(database, db_name, mode, mode_mask, sff, type, user_info, db_params)
567 	SMDB_DATABASE **database;
568 	char *db_name;
569 	int mode;
570 	int mode_mask;
571 	long sff;
572 	SMDB_DBTYPE type;
573 	SMDB_USER_INFO *user_info;
574 	SMDB_DBPARAMS *db_params;
575 {
576 	bool lockcreated = false;
577 	int result;
578 	int db_flags;
579 	int lock_fd;
580 	int db_fd;
581 	int major_v, minor_v, patch_v;
582 	SMDB_DATABASE *smdb_db;
583 	SMDB_DB2_DATABASE *db2;
584 	DB *db;
585 	DBTYPE db_type;
586 	struct stat stat_info;
587 	char db_file_name[MAXPATHLEN];
588 
589 	(void) db_version(&major_v, &minor_v, &patch_v);
590 	if (major_v != DB_VERSION_MAJOR || minor_v != DB_VERSION_MINOR)
591 		return SMDBE_VERSION_MISMATCH;
592 
593 	*database = NULL;
594 
595 	result = smdb_add_extension(db_file_name, sizeof db_file_name,
596 				    db_name, SMDB2_FILE_EXTENSION);
597 	if (result != SMDBE_OK)
598 		return result;
599 
600 	result = smdb_setup_file(db_name, SMDB2_FILE_EXTENSION,
601 				 mode_mask, sff, user_info, &stat_info);
602 	if (result != SMDBE_OK)
603 		return result;
604 
605 	lock_fd = -1;
606 
607 	if (stat_info.st_mode == ST_MODE_NOFILE &&
608 	    bitset(mode, O_CREAT))
609 		lockcreated = true;
610 
611 	result = smdb_lock_file(&lock_fd, db_name, mode, sff,
612 				SMDB2_FILE_EXTENSION);
613 	if (result != SMDBE_OK)
614 		return result;
615 
616 	if (lockcreated)
617 	{
618 		mode |= O_TRUNC;
619 		mode &= ~(O_CREAT|O_EXCL);
620 	}
621 
622 	smdb_db = smdb_malloc_database();
623 	db2 = smdb2_malloc_database();
624 	if (db2 == NULL || smdb_db == NULL)
625 	{
626 		smdb_unlock_file(lock_fd);
627 		smdb_free_database(smdb_db);	/* ok to be NULL */
628 		return SMDBE_MALLOC;
629 	}
630 
631 	db2->smdb2_lock_fd = lock_fd;
632 
633 	db_type = smdb_type_to_db2_type(type);
634 
635 	db = NULL;
636 
637 	db_flags = 0;
638 	if (bitset(O_CREAT, mode))
639 		db_flags |= DB_CREATE;
640 	if (bitset(O_TRUNC, mode))
641 		db_flags |= DB_TRUNCATE;
642 	if (mode == O_RDONLY)
643 		db_flags |= DB_RDONLY;
644 	SM_DB_FLAG_ADD(db_flags);
645 
646 	result = smdb_db_open_internal(db_file_name, db_type,
647 				       db_flags, db_params, &db);
648 
649 	if (result == 0 && db != NULL)
650 	{
651 		result = db->fd(db, &db_fd);
652 		if (result == 0)
653 			result = SMDBE_OK;
654 	}
655 	else
656 	{
657 		/* Try and narrow down on the problem */
658 		if (result != 0)
659 			result = db2_error_to_smdb(result);
660 		else
661 			result = SMDBE_BAD_OPEN;
662 	}
663 
664 	if (result == SMDBE_OK)
665 		result = smdb_filechanged(db_name, SMDB2_FILE_EXTENSION, db_fd,
666 					  &stat_info);
667 
668 	if (result == SMDBE_OK)
669 	{
670 		/* Everything is ok. Setup driver */
671 		db2->smdb2_db = db;
672 
673 		smdb_db->smdb_close = smdb2_close;
674 		smdb_db->smdb_del = smdb2_del;
675 		smdb_db->smdb_fd = smdb2_fd;
676 		smdb_db->smdb_lockfd = smdb2_lockfd;
677 		smdb_db->smdb_get = smdb2_get;
678 		smdb_db->smdb_put = smdb2_put;
679 		smdb_db->smdb_set_owner = smdb2_set_owner;
680 		smdb_db->smdb_sync = smdb2_sync;
681 		smdb_db->smdb_cursor = smdb2_cursor;
682 		smdb_db->smdb_impl = db2;
683 
684 		*database = smdb_db;
685 
686 		return SMDBE_OK;
687 	}
688 
689 	if (db != NULL)
690 		db->close(db, 0);
691 
692 	smdb_unlock_file(db2->smdb2_lock_fd);
693 	free(db2);
694 	smdb_free_database(smdb_db);
695 
696 	return result;
697 }
698 
699 #endif /* (DB_VERSION_MAJOR >= 2) */
700