4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
25 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
29 #include "db_headers.h"
31 #include "db_dictionary.h"
32 #include "db_dictlog.h"
36 #include "ldap_parse.h"
38 #include "nis_hashitem.h"
39 #include "ldap_util.h"
41 #include <rpcsvc/nis.h>
53 typedef bool_t (*db_func
)(XDR
*, db_table_desc
*);
55 extern db_dictionary
*InUseDictionary
;
56 extern db_dictionary
*FreeDictionary
;
58 /* *************** dictionary version ****************** */
60 #define DB_MAGIC 0x12340000
63 #define DB_VERSION_0_9 (DB_MAGIC|(DB_MAJOR<<8)|9)
64 #define DB_ORIG_VERSION DB_VERSION_0_9
65 #define DB_CURRENT_VERSION (DB_MAGIC|DB_MAJOR<<8|DB_MINOR)
67 vers db_update_version
; /* Note 'global' for all dbs. */
69 #define INMEMORY_ONLY 1
72 * Checks for valid version. For now, there are two:
73 * DB_VERSION_ORIG was the ORIGINAL one with major = 0, minor = 9
74 * DB_CURRENT_VERSION is the latest one with changes in the database format
75 * for entry objects and the change in the dictionary format.
77 * Our current implementation can support both versions.
80 db_valid_version(u_int vers
)
82 return ((vers
== DB_CURRENT_VERSION
) || (vers
== DB_ORIG_VERSION
));
86 db_version_str(u_int vers
)
88 static char vstr
[128];
89 u_int d_major
= (vers
&0x0000ff00)>>8;
90 u_int d_minor
= (vers
&0x000000ff);
92 sprintf(vstr
, "SunSoft, SSM, Version %d.%d", d_major
, d_minor
);
97 * Special XDR version that checks for a valid version number.
98 * If we don't find an acceptable version, exit immediately instead
99 * of continuing to xdr rest of dictionary, which might lead to
100 * a core dump if the formats between versions are incompatible.
101 * In the future, there might be a switch to determine versions
102 * and their corresponding XDR routines for the rest of the dictionary.
106 xdr_db_dict_version(XDR
*xdrs
, db_dict_version
*objp
)
108 if (xdrs
->x_op
== XDR_DECODE
) {
109 if (!xdr_u_int(xdrs
, (u_int
*) objp
) ||
110 !db_valid_version(((u_int
) *objp
))) {
112 "db_dictionary: invalid dictionary format! Expecting %s",
113 db_version_str(DB_CURRENT_VERSION
));
115 "db_dictionary: invalid dictionary format! Expecting %s\n",
116 db_version_str(DB_CURRENT_VERSION
));
119 } else if (!xdr_u_int(xdrs
, (u_int
*) objp
))
134 /* ******************* dictionary data structures *************** */
136 /* Delete contents of single db_table_desc pointed to by 'current.' */
138 delete_table_desc(db_table_desc
*current
)
140 if (current
->table_name
!= NULL
) delete current
->table_name
;
141 if (current
->scheme
!= NULL
) delete current
->scheme
;
142 if (current
->database
!= NULL
) delete current
->database
;
146 /* Create new table descriptor using given table name and table_object. */
148 db_dictionary::create_table_desc(char *tab
, table_obj
* zdesc
,
149 db_table_desc
** answer
)
151 db_table_desc
*newtab
;
152 if ((newtab
= new db_table_desc
) == NULL
) {
154 "db_dictionary::add_table: could not allocate space for new table",
155 DB_MEMORY_LIMIT
, DB_MEMORY_LIMIT
);
158 newtab
->database
= NULL
;
159 newtab
->table_name
= NULL
;
162 if ((newtab
->scheme
= new db_scheme(zdesc
)) == NULL
) {
163 delete_table_desc(newtab
);
165 "db_dictionary::add_table: could not allocate space for scheme",
166 DB_MEMORY_LIMIT
, DB_MEMORY_LIMIT
);
169 if (newtab
->scheme
->numkeys() == 0) {
171 "db_dictionary::add_table: could not translate table_obj to scheme");
172 delete_table_desc(newtab
);
173 return (DB_BADOBJECT
);
176 if ((newtab
->table_name
= strdup(tab
)) == NULL
) {
177 delete_table_desc(newtab
);
179 "db_dictionary::add_table: could not allocate space for table name",
180 DB_MEMORY_LIMIT
, DB_MEMORY_LIMIT
);
189 /* Delete list of db_table_desc pointed to by 'head.' */
191 delete_bucket(db_table_desc
*head
)
193 db_table_desc
* nextone
, *current
;
195 for (current
= head
; current
!= NULL
; current
= nextone
) {
196 nextone
= current
->next
; // remember next
197 delete_table_desc(current
);
202 delete_dictionary(db_dict_desc
*dict
)
204 db_table_desc
* bucket
;
207 if (dict
->tables
.tables_val
) {
208 /* delete each bucket */
209 for (i
= 0; i
< dict
->tables
.tables_len
; i
++)
210 bucket
= dict
->tables
.tables_val
[i
];
212 delete_bucket(bucket
);
214 delete dict
->tables
.tables_val
;
216 /* delete dictionary */
221 /* Relocate bucket starting with this entry to new hashtable 'new_tab'. */
223 relocate_bucket(db_table_desc
* bucket
,
224 db_table_desc_p
*new_tab
, unsigned long hashsize
)
226 db_table_desc_p np
, next_np
, *hp
;
228 for (np
= bucket
; np
!= NULL
; np
= next_np
) {
230 hp
= &new_tab
[np
->hashval
% hashsize
];
237 * Return pointer to entry with same hash value and table_name
238 * as those supplied. Returns NULL if not found.
241 enumerate_bucket(db_table_desc
* bucket
, db_status(*func
)(db_table_desc
*))
246 for (np
= bucket
; np
!= NULL
; np
= np
->next
) {
248 if (status
!= DB_SUCCESS
)
256 * Return pointer to entry with same hash value and table_name
257 * as those supplied. Returns NULL if not found.
259 static db_table_desc_p
260 search_bucket(db_table_desc
* bucket
, unsigned long hval
, char *target
)
264 for (np
= bucket
; np
!= NULL
; np
= np
->next
) {
265 if (np
->hashval
== hval
&&
266 strcmp(np
->table_name
, target
) == 0) {
275 * Remove entry with the specified hashvalue and target table name.
276 * Returns 'TRUE' if successful, FALSE otherwise.
277 * If the entry being removed is at the head of the list, then
278 * the head is updated to reflect the removal. The storage for the
279 * entry is freed if desired.
282 remove_from_bucket(db_table_desc_p bucket
,
283 db_table_desc_p
*head
, unsigned long hval
, char *target
,
286 db_table_desc_p np
, dp
;
288 /* Search for it in the bucket */
289 for (dp
= np
= bucket
; np
!= NULL
; np
= np
->next
) {
290 if (np
->hashval
== hval
&&
291 strcmp(np
->table_name
, target
) == 0) {
299 return (FALSE
); // cannot delete if it is not there
302 *head
= np
->next
; // deleting head of bucket
304 dp
->next
= np
->next
; // deleting interior link
307 delete_table_desc(np
);
314 * Add given entry to the bucket pointed to by 'bucket'.
315 * If an entry with the same table_name is found, no addition
316 * is done. The entry is added to the head of the bucket.
319 add_to_bucket(db_table_desc_p bucket
, db_table_desc
**head
, db_table_desc_p td
)
321 db_table_desc_p curr
, prev
;
322 register char *target_name
;
323 unsigned long target_hval
;
324 target_name
= td
->table_name
;
325 target_hval
= td
->hashval
;
327 /* Search for it in the bucket */
328 for (prev
= curr
= bucket
; curr
!= NULL
; curr
= curr
->next
) {
329 if (curr
->hashval
== target_hval
&&
330 strcmp(curr
->table_name
, target_name
) == 0) {
338 return (FALSE
); /* duplicates not allowed */
347 /* Print bucket starting with this entry. */
349 print_bucket(db_table_desc
*head
)
352 for (np
= head
; np
!= NULL
; np
= np
->next
) {
353 printf("%s: %d\n", np
->table_name
, np
->hashval
);
358 print_table(db_table_desc
*tbl
)
361 return (DB_BADTABLE
);
362 printf("%s: %d\n", tbl
->table_name
, tbl
->hashval
);
367 static int hashsizes
[] = { /* hashtable sizes */
380 // prevents wrap around numbers from being passed
381 #define CALLOC_LIMIT 536870911
383 /* Returns the next size to use for the hash table */
385 get_next_hashsize(long unsigned oldsize
)
387 long unsigned newsize
, n
;
389 newsize
= hashsizes
[0];
391 for (n
= 0; newsize
= hashsizes
[n
++]; )
392 if (oldsize
== newsize
) {
393 newsize
= hashsizes
[n
]; /* get next size */
397 newsize
= oldsize
* 2 + 1; /* just double */
403 * Grow the current hashtable upto the next size.
404 * The contents of the existing hashtable is copied to the new one and
405 * relocated according to its hashvalue relative to the new size.
406 * Old table is deleted after the relocation.
409 grow_dictionary(db_dict_desc_p dd
)
411 unsigned int oldsize
, i
, new_size
;
412 db_table_desc_p
* oldtab
, *newtab
;
414 oldsize
= dd
->tables
.tables_len
;
415 oldtab
= dd
->tables
.tables_val
;
417 new_size
= get_next_hashsize(oldsize
);
419 if (new_size
> CALLOC_LIMIT
) {
420 FATAL("db_dictionary::grow: table size exceeds calloc limit",
424 if ((newtab
= (db_table_desc_p
*)
425 calloc((unsigned int) new_size
,
426 sizeof (db_table_desc_p
))) == NULL
) {
427 FATAL("db_dictionary::grow: cannot allocate space",
431 if (oldtab
!= NULL
) { // must transfer contents of old to new
432 for (i
= 0; i
< oldsize
; i
++) {
433 relocate_bucket(oldtab
[i
], newtab
, new_size
);
435 delete oldtab
; // delete old hashtable
438 dd
->tables
.tables_val
= newtab
;
439 dd
->tables
.tables_len
= new_size
;
443 #define HASHMASK 0x1f
446 get_hashval(char *value
)
452 for (i
= 0; i
< len
; i
++) {
453 hval
= ((hval
<<HASHSHIFT
)^hval
);
454 hval
+= (value
[i
] & HASHMASK
);
461 enumerate_dictionary(db_dict_desc
*dd
, db_status (*func
) (db_table_desc
*))
464 db_table_desc
*bucket
;
470 for (i
= 0; i
< dd
->tables
.tables_len
; i
++) {
471 bucket
= dd
->tables
.tables_val
[i
];
473 status
= enumerate_bucket(bucket
, func
);
474 if (status
!= DB_SUCCESS
)
484 * Look up target table_name in hashtable and return its db_table_desc.
485 * Return NULL if not found.
487 static db_table_desc
*
488 search_dictionary(db_dict_desc
*dd
, char *target
)
490 register unsigned long hval
;
491 unsigned long bucket
;
493 if (target
== NULL
|| dd
== NULL
|| dd
->tables
.tables_len
== 0)
496 hval
= get_hashval(target
);
497 bucket
= hval
% dd
->tables
.tables_len
;
499 db_table_desc_p fst
= dd
->tables
.tables_val
[bucket
];
502 return (search_bucket(fst
, hval
, target
));
508 * Remove the entry with the target table_name from the dictionary.
509 * If successful, return DB_SUCCESS; otherwise DB_NOTUNIQUE if target
510 * is null; DB_NOTFOUND if entry is not found.
511 * If successful, decrement count of number of entries in hash table.
514 remove_from_dictionary(db_dict_desc
*dd
, char *target
, bool_t remove_storage
)
516 register unsigned long hval
;
517 unsigned long bucket
;
518 register db_table_desc
*fst
;
521 return (DB_NOTUNIQUE
);
522 if (dd
== NULL
|| dd
->tables
.tables_len
== 0)
523 return (DB_NOTFOUND
);
524 hval
= get_hashval(target
);
525 bucket
= hval
% dd
->tables
.tables_len
;
526 fst
= dd
->tables
.tables_val
[bucket
];
528 return (DB_NOTFOUND
);
529 if (remove_from_bucket(fst
, &dd
->tables
.tables_val
[bucket
],
530 hval
, target
, remove_storage
)) {
534 return (DB_NOTFOUND
);
538 * Add a new db_table_desc to the dictionary.
539 * Return DB_NOTUNIQUE, if entry with identical table_name
540 * already exists. If entry is added, return DB_SUCCESS.
541 * Increment count of number of entries in index table and grow table
542 * if number of entries equals size of table.
544 * Inputs: db_dict_desc_p dd pointer to dictionary to add to.
545 * db_table_desc *td pointer to table entry to be added. The
546 * db_table_desc.next field will be altered
547 * without regard to it's current setting.
548 * This means that if next points to a list of
549 * table entries, they may be either linked into
550 * the dictionary unexpectly or cut off (leaked).
553 add_to_dictionary(db_dict_desc_p dd
, db_table_desc
*td
)
555 register unsigned long hval
;
559 return (DB_NOTFOUND
);
562 return (DB_NOTFOUND
);
563 target
= td
->table_name
;
565 return (DB_NOTUNIQUE
);
567 hval
= get_hashval(target
);
569 if (dd
->tables
.tables_val
== NULL
)
573 unsigned long bucket
;
574 bucket
= hval
% dd
->tables
.tables_len
;
575 fst
= dd
->tables
.tables_val
[bucket
];
577 if (fst
== NULL
) { /* Empty bucket */
578 dd
->tables
.tables_val
[bucket
] = td
;
579 } else if (!add_to_bucket(fst
, &dd
->tables
.tables_val
[bucket
], td
)) {
580 return (DB_NOTUNIQUE
);
583 /* increase hash table size if number of entries equals table size */
584 if (++(dd
->count
) > dd
->tables
.tables_len
)
590 /* ******************* pickling routines for dictionary ******************* */
593 /* Does the actual writing to/from file specific for db_dict_desc structure. */
595 transfer_aux(XDR
* x
, pptr tbl
)
597 return (xdr_db_dict_desc_p(x
, (db_dict_desc_p
*) tbl
));
600 class pickle_dict_desc
: public pickle_file
{
602 pickle_dict_desc(char *f
, pickle_mode m
) : pickle_file(f
, m
) {}
604 /* Transfers db_dict_desc structure pointed to by dp to/from file. */
605 int transfer(db_dict_desc_p
* dp
)
606 { return (pickle_file::transfer((pptr
) dp
, &transfer_aux
)); }
609 /* ************************ dictionary methods *************************** */
611 db_dictionary::db_dictionary()
619 logfile_opened
= FALSE
;
626 * This routine clones an entire hash bucket chain. If you clone a
627 * data dictionary entry with the ->next pointer set, you will get a
628 * clone of that entry, as well as the entire linked list. This can cause
629 * pain if you then pass the cloned bucket to routines such as
630 * add_to_dictionary(), which do not expect nor handle dictionary hash
631 * entries with the ->next pointer set. You might either get duplicate
632 * entires or lose entries. If you wish to clone the entire bucket chain
633 * and add it to a new dictionary, loop through the db_table_desc->next list
634 * and call add_to_dictionary() for each item.
637 db_dictionary::db_clone_bucket(db_table_desc
*bucket
, db_table_desc
**clone
)
643 READLOCK(this, DB_LOCK_ERROR
, "r db_dictionary::db_clone_bucket");
644 db_func use_this
= xdr_db_table_desc
;
645 size
= xdr_sizeof((xdrproc_t
) use_this
, (void *) bucket
);
646 bufin
= (char *) calloc(1, (size_t) size
* sizeof (char));
648 READUNLOCK(this, DB_MEMORY_LIMIT
,
649 "db_dictionary::insert_modified_table: out of memory");
650 FATAL3("db_dictionary::insert_modified_table: out of memory",
653 xdrmem_create(&xdrs
, bufin
, (size_t) size
, XDR_ENCODE
);
654 if (!xdr_db_table_desc(&xdrs
, bucket
)) {
657 READUNLOCK(this, DB_MEMORY_LIMIT
,
658 "db_dictionary::insert_modified_table: xdr encode failed");
660 "db_dictionary::insert_modified_table: xdr encode failed.",
663 *clone
= (db_table_desc
*) calloc(1, (size_t) size
* sizeof (char));
667 READUNLOCK(this, DB_MEMORY_LIMIT
,
668 "db_dictionary::insert_modified_table: out of memory");
669 FATAL3("db_dictionary::insert_modified_table: out of memory",
673 xdrmem_create(&xdrs
, bufin
, (size_t) size
, XDR_DECODE
);
674 if (!xdr_db_table_desc(&xdrs
, *clone
)) {
678 READUNLOCK(this, DB_MEMORY_LIMIT
,
679 "db_dictionary::insert_modified_table: xdr encode failed");
681 "db_dictionary::insert_modified_table: xdr encode failed.",
686 READUNLOCK(this, DB_LOCK_ERROR
, "ru db_dictionary::db_clone_bucket");
692 db_dictionary::change_table_name(db_table_desc
*clone
, char *tok
, char *repl
)
695 char *loc_end
, *loc_beg
;
697 WRITELOCK(this, DB_LOCK_ERROR
, "w db_dictionary::change_table_name");
700 * Special case for a tok="". This is used for the
701 * nisrestore(1M), when restoring a replica in another
702 * domain. This routine is used to change the datafile
703 * names in the data.dict (see bugid #4031273). This will not
704 * effect massage_dict(), since it never generates an empty
707 if (strlen(tok
) == 0) {
708 strcat(clone
->table_name
, repl
);
712 newname
= (char *) calloc(1, sizeof (char) *
713 strlen(clone
->table_name
) +
714 strlen(repl
) - strlen(tok
) + 1);
716 WRITEUNLOCK(this, DB_MEMORY_LIMIT
,
717 "db_dictionary::change_table_name: out of memory");
718 FATAL3("db_dictionary::change_table_name: out of memory.",
721 if (loc_beg
= strstr(clone
->table_name
, tok
)) {
722 loc_end
= loc_beg
+ strlen(tok
);
723 int s
= loc_beg
- clone
->table_name
;
724 memcpy(newname
, clone
->table_name
, s
);
725 strcat(newname
+ s
, repl
);
726 strcat(newname
, loc_end
);
727 free(clone
->table_name
);
728 clone
->table_name
= newname
;
734 WRITEUNLOCK(this, DB_LOCK_ERROR
,
735 "wu db_dictionary::change_table_name");
744 * A function to initialize the temporary dictionary from the real
748 db_dictionary::inittemp(char *dictname
, db_dictionary
& curdict
)
751 db_table_desc_p
*newtab
;
755 WRITELOCK(this, FALSE
, "w db_dictionary::inittemp");
757 /* Someone else got in between db_shutdown() and lock() */
758 WRITEUNLOCK(this, FALSE
, "wu db_dictionary::inittemp");
762 pickle_dict_desc
f(dictname
, PICKLE_READ
);
763 filename
= strdup(dictname
);
764 if (filename
== NULL
) {
765 WRITEUNLOCK(this, FALSE
,
766 "db_dictionary::inittemp: could not allocate space");
767 FATAL3("db_dictionary::inittemp: could not allocate space",
768 DB_MEMORY_LIMIT
, FALSE
);
770 int len
= strlen(filename
);
771 tmpfilename
= new char[len
+5];
772 if (tmpfilename
== NULL
) {
774 WRITEUNLOCK(this, FALSE
,
775 "db_dictionary::inittemp: could not allocate space");
776 FATAL3("db_dictionary::inittemp: could not allocate space",
777 DB_MEMORY_LIMIT
, FALSE
);
779 logfilename
= new char[len
+5];
780 if (logfilename
== NULL
) {
783 WRITEUNLOCK(this, FALSE
,
784 "db_dictionary::inittemp: cannot allocate space");
785 FATAL3("db_dictionary::inittemp: cannot allocate space",
786 DB_MEMORY_LIMIT
, FALSE
);
789 sprintf(tmpfilename
, "%s.tmp", filename
);
790 sprintf(logfilename
, "%s.log", filename
);
791 unlink(tmpfilename
); /* get rid of partial checkpoints */
794 if ((status
= f
.transfer(&dictionary
)) < 0) {
796 } else if (status
== 1) { /* no dictionary exists, create one */
797 dictionary
= new db_dict_desc
;
798 if (dictionary
== NULL
) {
799 WRITEUNLOCK(this, FALSE
,
800 "db_dictionary::inittemp: could not allocate space");
802 "db_dictionary::inittemp: could not allocate space",
803 DB_MEMORY_LIMIT
, FALSE
);
805 dictionary
->tables
.tables_len
=
806 curdict
.dictionary
->tables
.tables_len
;
807 if ((newtab
= (db_table_desc_p
*) calloc(
808 (unsigned int) dictionary
->tables
.tables_len
,
809 sizeof (db_table_desc_p
))) == NULL
) {
810 WRITEUNLOCK(this, FALSE
,
811 "db_dictionary::inittemp: cannot allocate space");
813 "db_dictionary::inittemp: cannot allocate space",
816 dictionary
->tables
.tables_val
= newtab
;
817 dictionary
->count
= 0;
818 dictionary
->impl_vers
= curdict
.dictionary
->impl_vers
;
820 } else /* dictionary loaded successfully */
823 if (initialized
== TRUE
) {
828 WRITEUNLOCK(this, FALSE
, "wu db_dictionary::inittemp");
829 return (initialized
);
834 * This method replaces the token string specified with the replacment
835 * string specified. It assumes that at least one and only one instance of
836 * the token exists. It is the responsibility of the caller to ensure that
837 * the above assumption stays valid.
840 db_dictionary::massage_dict(char *newdictname
, char *tok
, char *repl
)
845 db_table_desc
*bucket
, *np
, *clone
, *next_np
;
846 char tail
[NIS_MAXNAMELEN
];
847 db_dictionary
*tmpptr
;
849 WRITELOCK(this, DB_LOCK_ERROR
, "w db_dictionary::massage_dict");
850 if (dictionary
== NULL
) {
851 WRITEUNLOCK(this, DB_INTERNAL_ERROR
,
852 "db_dictionary::massage_dict: uninitialized dictionary file");
854 "db_dictionary::massage_dict: uninitialized dictionary file.",
855 DB_INTERNAL_ERROR
, DB_INTERNAL_ERROR
);
858 if ((tbl_count
= dictionary
->count
) == 0) {
859 WRITEUNLOCK(this, DB_SUCCESS
,
860 "wu db_dictionary::massage_dict");
864 /* First checkpoint */
865 if ((status
= checkpoint()) != DB_SUCCESS
) {
866 WRITEUNLOCK(this, status
, "wu db_dictionary::massage_dict");
871 enumerate_dictionary(dictionary
, &print_table
);
874 /* Initialize the free dictionary so that we can start populating it */
875 FreeDictionary
->inittemp(newdictname
, *this);
877 for (i
= 0; i
< dictionary
->tables
.tables_len
; i
++) {
878 bucket
= dictionary
->tables
.tables_val
[i
];
883 retval
= db_clone_bucket(np
, &clone
);
885 WRITEUNLOCK(this, DB_INTERNAL_ERROR
,
886 "wu db_dictionary::massage_dict");
887 return (DB_INTERNAL_ERROR
);
889 if (change_table_name(clone
, tok
, repl
) == -1) {
890 delete_table_desc(clone
);
891 WRITEUNLOCK(this, DB_INTERNAL_ERROR
,
892 "wu db_dictionary::massage_dict");
893 return (DB_INTERNAL_ERROR
);
896 * We know we don't have a log file, so we will
897 * just add to the in-memory database and dump
898 * all of it once we are done.
900 status
= add_to_dictionary
901 (FreeDictionary
->dictionary
,
903 if (status
!= DB_SUCCESS
) {
904 delete_table_desc(clone
);
905 WRITEUNLOCK(this, DB_INTERNAL_ERROR
,
906 "wu db_dictionary::massage_dict");
907 return (DB_INTERNAL_ERROR
);
909 status
= remove_from_dictionary(dictionary
,
910 np
->table_name
, TRUE
);
911 if (status
!= DB_SUCCESS
) {
912 delete_table_desc(clone
);
913 WRITEUNLOCK(this, DB_INTERNAL_ERROR
,
914 "wu db_dictionary::massage_dict");
915 return (DB_INTERNAL_ERROR
);
922 if (FreeDictionary
->dump() != DB_SUCCESS
) {
923 WRITEUNLOCK(this, DB_INTERNAL_ERROR
,
924 "wu db_dictionary::massage_dict");
926 "db_dictionary::massage_dict: Unable to dump new dictionary.",
927 DB_INTERNAL_ERROR
, DB_INTERNAL_ERROR
);
931 * Now, shutdown the inuse dictionary and update the FreeDictionary
932 * and InUseDictionary pointers as well. Also, delete the old dictionary
935 unlink(filename
); /* There shouldn't be a tmpfile or logfile */
937 tmpptr
= InUseDictionary
;
938 InUseDictionary
= FreeDictionary
;
939 FreeDictionary
= tmpptr
;
940 WRITEUNLOCK(this, DB_LOCK_ERROR
, "wu db_dictionary::massage_dict");
946 db_dictionary::merge_dict(db_dictionary
& tempdict
, char *tok
, char *repl
)
949 db_status dbstat
= DB_SUCCESS
;
951 db_table_desc
*tbl
= NULL
, *clone
= NULL
, *next_td
= NULL
;
954 WRITELOCK(this, DB_LOCK_ERROR
, "w db_dictionary::merge_dict");
956 for (i
= 0; i
< tempdict
.dictionary
->tables
.tables_len
; ++i
) {
957 tbl
= tempdict
.dictionary
->tables
.tables_val
[i
];
960 retval
= db_clone_bucket(tbl
, &clone
);
962 WRITEUNLOCK(this, DB_INTERNAL_ERROR
,
963 "wu db_dictionary::merge_dict");
964 return (DB_INTERNAL_ERROR
);
967 next_td
= clone
->next
;
970 (change_table_name(clone
, tok
, repl
) == -1)) {
971 delete_table_desc(clone
);
973 delete_table_desc(next_td
);
974 WRITEUNLOCK(this, DB_INTERNAL_ERROR
,
975 "wu db_dictionary::merge_dict");
976 return (DB_INTERNAL_ERROR
);
979 dbstat
= add_to_dictionary(dictionary
, clone
);
980 if (dbstat
== DB_NOTUNIQUE
) {
982 dbstat
= remove_from_dictionary(dictionary
,
983 clone
->table_name
, TRUE
);
984 if (dbstat
!= DB_SUCCESS
) {
985 WRITEUNLOCK(this, dbstat
,
986 "wu db_dictionary::merge_dict");
989 dbstat
= add_to_dictionary(dictionary
,
992 if (dbstat
!= DB_SUCCESS
) {
993 WRITEUNLOCK(this, dbstat
,
994 "wu db_dictionary::merge_dict");
1002 * If we were successful in merging the dictionaries, then mark the
1003 * dictionary changed, so that it will be properly checkpointed and
1006 if (dbstat
== DB_SUCCESS
)
1008 WRITEUNLOCK(this, DB_LOCK_ERROR
, "wu db_dictionary::merge_dict");
1013 db_dictionary::copyfile(char *infile
, char *outfile
)
1015 db_table_desc
*tbl
= NULL
;
1019 READLOCK(this, DB_LOCK_ERROR
, "r db_dictionary::copyfile");
1021 * We need to hold the read-lock until the dump() is done.
1022 * However, we must avoid the lock migration (read -> write)
1023 * that would happen in find_table() if the db must be loaded.
1024 * Hence, look first look for an already loaded db.
1026 dbase
= find_table(infile
, &tbl
, TRUE
, TRUE
, FALSE
);
1027 if (dbase
== NULL
) {
1028 /* Release the read-lock, and try again, allowing load */
1029 READUNLOCK(this, DB_LOCK_ERROR
, "ru db_dictionary::copyfile");
1030 dbase
= find_table(infile
, &tbl
, TRUE
, TRUE
, TRUE
);
1032 return (DB_NOTFOUND
);
1034 * Read-lock again, and get a 'tbl' we can use since we're
1035 * still holding the lock.
1037 READLOCK(this, DB_LOCK_ERROR
, "r db_dictionary::copyfile");
1038 dbase
= find_table(infile
, &tbl
, TRUE
, TRUE
, FALSE
);
1039 if (dbase
== NULL
) {
1040 READUNLOCK(this, DB_NOTFOUND
,
1041 "ru db_dictionary::copyfile");
1042 return (DB_NOTFOUND
);
1045 ret
= tbl
->database
->dump(outfile
) ? DB_SUCCESS
: DB_INTERNAL_ERROR
;
1046 READUNLOCK(this, ret
, "ru db_dictionary::copyfile");
1052 db_dictionary::extract_entries(db_dictionary
& tempdict
, char **fs
, int fscnt
)
1055 db_table_desc
*tbl
, *clone
;
1056 db_table_desc tbl_ent
;
1059 READLOCK(this, FALSE
, "r db_dictionary::extract_entries");
1060 for (i
= 0; i
< fscnt
; ++i
) {
1061 tbl
= find_table_desc(fs
[i
]);
1064 "extract_entries: no dictionary entry for %s",
1066 READUNLOCK(this, FALSE
,
1067 "ru db_dictionary::extract_entries");
1070 tbl_ent
.table_name
= tbl
->table_name
;
1071 tbl_ent
.hashval
= tbl
->hashval
;
1072 tbl_ent
.scheme
= tbl
->scheme
;
1073 tbl_ent
.database
= tbl
->database
;
1074 tbl_ent
.next
= NULL
;
1076 retval
= db_clone_bucket(&tbl_ent
, &clone
);
1079 "extract_entries: unable to clone entry for %s",
1081 READUNLOCK(this, FALSE
,
1082 "ru db_dictionary::extract_entries");
1085 dbstat
= add_to_dictionary(tempdict
.dictionary
, clone
);
1086 if (dbstat
!= DB_SUCCESS
) {
1087 delete_table_desc(clone
);
1088 READUNLOCK(this, FALSE
,
1089 "ru db_dictionary::extract_entries");
1093 if (tempdict
.dump() != DB_SUCCESS
) {
1094 READUNLOCK(this, FALSE
,
1095 "ru db_dictionary::extract_entries");
1098 READUNLOCK(this, FALSE
,
1099 "ru db_dictionary::extract_entries");
1105 * Initialize dictionary from contents in 'file'.
1106 * If there is already information in this dictionary, it is removed.
1107 * Therefore, regardless of whether the load from the file succeeds,
1108 * the contents of this dictionary will be altered. Returns
1109 * whether table has been initialized successfully.
1112 db_dictionary::init(char *file
)
1116 WRITELOCK(this, FALSE
, "w db_dictionary::init");
1119 pickle_dict_desc
f(file
, PICKLE_READ
);
1120 filename
= strdup(file
);
1121 if (filename
== NULL
) {
1122 WRITEUNLOCK(this, FALSE
,
1123 "db_dictionary::init: could not allocate space");
1124 FATAL3("db_dictionary::init: could not allocate space",
1125 DB_MEMORY_LIMIT
, FALSE
);
1127 int len
= strlen(filename
);
1128 tmpfilename
= new char[len
+5];
1129 if (tmpfilename
== NULL
) {
1131 WRITEUNLOCK(this, FALSE
,
1132 "db_dictionary::init: could not allocate space");
1133 FATAL3("db_dictionary::init: could not allocate space",
1134 DB_MEMORY_LIMIT
, FALSE
);
1136 logfilename
= new char[len
+5];
1137 if (logfilename
== NULL
) {
1140 WRITEUNLOCK(this, FALSE
,
1141 "db_dictionary::init: cannot allocate space");
1142 FATAL3("db_dictionary::init: cannot allocate space",
1143 DB_MEMORY_LIMIT
, FALSE
);
1146 sprintf(tmpfilename
, "%s.tmp", filename
);
1147 sprintf(logfilename
, "%s.log", filename
);
1148 unlink(tmpfilename
); /* get rid of partial checkpoints */
1151 /* load dictionary */
1152 if ((status
= f
.transfer(&dictionary
)) < 0) {
1153 initialized
= FALSE
;
1154 } else if (status
== 1) { /* no dictionary exists, create one */
1155 dictionary
= new db_dict_desc
;
1156 if (dictionary
== NULL
) {
1157 WRITEUNLOCK(this, FALSE
,
1158 "db_dictionary::init: could not allocate space");
1159 FATAL3("db_dictionary::init: could not allocate space",
1160 DB_MEMORY_LIMIT
, FALSE
);
1162 dictionary
->tables
.tables_len
= 0;
1163 dictionary
->tables
.tables_val
= NULL
;
1164 dictionary
->count
= 0;
1165 dictionary
->impl_vers
= DB_CURRENT_VERSION
;
1167 } else /* dictionary loaded successfully */
1170 if (initialized
== TRUE
) {
1171 int num_changes
= 0;
1174 if ((num_changes
= incorporate_log(logfilename
)) < 0)
1176 "incorporation of dictionary logfile '%s' failed",
1178 changed
= (num_changes
> 0);
1181 WRITEUNLOCK(this, initialized
, "wu db_dictionary::init");
1182 return (initialized
);
1186 * Execute log entry 'j' on the dictionary identified by 'dict' if the
1187 * version of j is later than that of the dictionary. If 'j' is executed,
1188 * 'count' is incremented and the dictionary's verison is updated to
1190 * Returns TRUE always for valid log entries; FALSE otherwise.
1193 apply_log_entry(db_dictlog_entry
*j
, char *dictchar
, int *count
)
1195 db_dictionary
*dict
= (db_dictionary
*) dictchar
;
1197 WRITELOCK(dict
, FALSE
, "w apply_log_entry");
1198 if (db_update_version
.earlier_than(j
->get_version())) {
1203 switch (j
->get_action()) {
1205 dict
->add_table_aux(j
->get_table_name(),
1206 j
->get_table_object(), INMEMORY_ONLY
);
1210 case DB_REMOVE_TABLE
:
1211 dict
->delete_table_aux(j
->get_table_name(),
1217 WARNING("db::apply_log_entry: unknown action_type");
1218 WRITEUNLOCK(dict
, FALSE
, "wu apply_log_entry");
1221 db_update_version
.assign(j
->get_version());
1224 WRITEUNLOCK(dict
, TRUE
, "wu apply_log_entry");
1229 db_dictionary::incorporate_log(char *file_name
)
1231 db_dictlog
f(file_name
, PICKLE_READ
);
1234 WRITELOCK(this, -1, "w db_dictionary::incorporate_log");
1235 setNoWriteThrough();
1236 ret
= f
.execute_on_log(&(apply_log_entry
), (char *) this);
1237 clearNoWriteThrough();
1238 WRITEUNLOCK(this, -1, "wu db_dictionary::incorporate_log");
1243 /* Frees memory of filename and tables. Has no effect on disk storage. */
1245 db_dictionary::db_shutdown()
1247 WRITELOCK(this, DB_LOCK_ERROR
, "w db_dictionary::db_shutdown");
1249 WRITEUNLOCK(this, DB_LOCK_ERROR
,
1250 "wu db_dictionary::db_shutdown");
1251 return (DB_SUCCESS
); /* DB_NOTFOUND? */
1267 delete_dictionary(dictionary
);
1270 initialized
= FALSE
;
1274 WRITEUNLOCK(this, DB_LOCK_ERROR
, "wu db_dictionary::db_shutdown");
1275 return (DB_SUCCESS
);
1279 * Dump contents of this dictionary (minus the database representations)
1280 * to its file. Returns 0 if operation succeeds, -1 otherwise.
1283 db_dictionary::dump()
1287 READLOCK(this, -1, "r db_dictionary::dump");
1289 READUNLOCK(this, -1, "ru db_dictionary::dump");
1293 unlink(tmpfilename
); /* get rid of partial dumps */
1294 pickle_dict_desc
f(tmpfilename
, PICKLE_WRITE
);
1296 status
= f
.transfer(&dictionary
); /* dump table descs */
1298 WARNING("db_dictionary::dump: could not write out dictionary");
1299 } else if (rename(tmpfilename
, filename
) < 0) {
1300 WARNING_M("db_dictionary::dump: could not rename temp file: ");
1304 READUNLOCK(this, -1, "ru db_dictionary::dump");
1309 * Write out in-memory copy of dictionary to file.
1310 * 1. Update major version.
1311 * 2. Dump contents to temporary file.
1312 * 3. Rename temporary file to real dictionary file.
1313 * 4. Remove log file.
1314 * A checkpoint is done only if it has changed since the previous checkpoint.
1315 * Returns DB_SUCCESS if checkpoint was successful; error code otherwise
1318 db_dictionary::checkpoint()
1320 WRITELOCK(this, DB_LOCK_ERROR
, "w db_dictionary::checkpoint");
1322 if (changed
== FALSE
) {
1323 WRITEUNLOCK(this, DB_LOCK_ERROR
,
1324 "wu db_dictionary::checkpoint");
1325 return (DB_SUCCESS
);
1328 vers
*oldv
= new vers(db_update_version
); // copy
1329 vers
* newv
= db_update_version
.nextmajor(); // get next version
1330 db_update_version
.assign(newv
); // update version
1335 "db_dictionary::checkpoint: could not dump dictionary: ");
1336 db_update_version
.assign(oldv
); // rollback
1338 WRITEUNLOCK(this, DB_INTERNAL_ERROR
,
1339 "wu db_dictionary::checkpoint");
1340 return (DB_INTERNAL_ERROR
);
1342 unlink(logfilename
); /* should do atomic rename and log delete */
1343 reset_log(); /* should check for what? */
1346 WRITEUNLOCK(this, DB_LOCK_ERROR
, "wu db_dictionary::checkpoint");
1347 return (DB_SUCCESS
);
1350 /* close existing logfile and delete its structure */
1352 db_dictionary::reset_log()
1354 WRITELOCK(this, -1, "w db_dictionary::reset_log");
1355 /* try to close old log file */
1356 /* doesnot matter since we do synchronous writes only */
1357 if (logfile
!= NULL
) {
1358 if (logfile_opened
== TRUE
) {
1359 if (logfile
->close() < 0) {
1361 "db_dictionary::reset_log: could not close log file: ");
1367 logfile_opened
= FALSE
;
1368 WRITEUNLOCK(this, -1, "wu db_dictionary::reset_log");
1372 /* close existing logfile, but leave its structure if exists */
1374 db_dictionary::close_log()
1376 WRITELOCK(this, -1, "w db_dictionary::close_log");
1377 if (logfile
!= NULL
&& logfile_opened
== TRUE
) {
1380 logfile_opened
= FALSE
;
1381 WRITEUNLOCK(this, -1, "wu db_dictionary::close_log");
1385 /* open logfile, creating its structure if it does not exist */
1387 db_dictionary::open_log()
1389 WRITELOCK(this, -1, "w db_dictionary::open_log");
1390 if (logfile
== NULL
) {
1391 if ((logfile
= new db_dictlog(logfilename
, PICKLE_APPEND
)) ==
1393 WRITEUNLOCK(this, -1, "wu db_dictionary::open_log");
1395 "db_dictionary::reset_log: cannot allocate space",
1396 DB_MEMORY_LIMIT
, -1);
1400 if (logfile_opened
== TRUE
) {
1401 WRITEUNLOCK(this, -1, "wu db_dictionary::open_log");
1405 if ((logfile
->open()) == FALSE
) {
1406 WARNING_M("db_dictionary::open_log: could not open log file: ");
1409 WRITEUNLOCK(this, -1, "wu db_dictionary::open_log");
1413 logfile_opened
= TRUE
;
1414 WRITEUNLOCK(this, -1, "wu db_dictionary::open_log");
1419 * closes any open log files for all tables in dictionary or 'tab'.
1420 * "tab" is an optional argument.
1422 static int close_standby_list();
1425 db_dictionary::db_standby(char *tab
)
1429 WRITELOCK(this, DB_LOCK_ERROR
, "w db_dictionary::db_standby");
1431 WRITEUNLOCK(this, DB_BADDICTIONARY
,
1432 "wu db_dictionary::db_standby");
1433 return (DB_BADDICTIONARY
);
1437 close_log(); // close dictionary log
1438 close_standby_list();
1439 WRITEUNLOCK(this, DB_LOCK_ERROR
, "wu db_dictionary::db_standby");
1440 return (DB_SUCCESS
);
1443 if ((tbl
= find_table_desc(tab
)) == NULL
) {
1444 WRITEUNLOCK(this, DB_LOCK_ERROR
, "wu db_dictionary::db_standby");
1445 return (DB_BADTABLE
);
1448 if (tbl
->database
!= NULL
)
1449 tbl
->database
->close_log();
1450 WRITEUNLOCK(this, DB_LOCK_ERROR
, "wu db_dictionary::db_standby");
1451 return (DB_SUCCESS
);
1455 * Returns db_table_desc of table name 'tab'. 'prev', if supplied,
1456 * is set to the entry located ahead of 'tab's entry in the dictionary.
1459 db_dictionary::find_table_desc(char *tab
)
1463 READLOCK(this, NULL
, "r db_dictionary::find_table_desc");
1465 ret
= search_dictionary(dictionary
, tab
);
1469 READUNLOCK(this, ret
, "r db_dictionary::find_table_desc");
1474 db_dictionary::find_table_desc(char *tab
, bool_t searchDeferred
) {
1475 db_table_desc
*ret
= NULL
;
1477 READLOCK(this, NULL
, "r db_dictionary::find_table_desc_d");
1479 /* If desired, look in the deferred dictionary first */
1480 if (initialized
&& searchDeferred
&& deferred
.dictionary
!= NULL
)
1481 ret
= search_dictionary(deferred
.dictionary
, tab
);
1483 /* No result yet => search the "normal" dictionary */
1485 ret
= find_table_desc(tab
);
1487 READUNLOCK(this, ret
, "r db_dictionary::find_table_desc_d");
1492 db_dictionary::find_table(char *tab
, db_table_desc
**where
) {
1493 /* Most operations should use the deferred dictionary if it exists */
1494 return (find_table(tab
, where
, TRUE
, TRUE
, TRUE
));
1498 db_dictionary::find_table(char *tab
, db_table_desc
**where
,
1499 bool_t searchDeferred
) {
1500 return (find_table(tab
, where
, searchDeferred
, TRUE
, TRUE
));
1504 db_dictionary::find_table(char *tab
, db_table_desc
**where
,
1505 bool_t searchDeferred
, bool_t doLDAP
,
1510 const char *myself
= "db_dictionary::find_table";
1512 res
= find_table_noLDAP(tab
, where
, searchDeferred
, doLoad
);
1513 /* If found, or shouldn't try LDAP, we're done */
1514 if (res
!= 0 || !doLDAP
)
1517 /* See if we can retrieve the object from LDAP */
1518 dstat
= dbCreateFromLDAP(tab
, &lstat
);
1519 if (dstat
!= DB_SUCCESS
) {
1520 if (dstat
== DB_NOTFOUND
) {
1521 if (lstat
!= LDAP_SUCCESS
) {
1522 logmsg(MSG_NOTIMECHECK
, LOG_INFO
,
1523 "%s: LDAP error for \"%s\": %s",
1525 ldap_err2string(lstat
));
1528 logmsg(MSG_NOTIMECHECK
, LOG_INFO
,
1529 "%s: DB error %d for \"%s\"",
1530 myself
, dstat
, NIL(tab
));
1535 /* Try the dictionary again */
1536 res
= find_table_noLDAP(tab
, where
, searchDeferred
, doLoad
);
1542 * Return database structure of table named by 'tab'.
1543 * If 'where' is set, set it to the table_desc of 'tab.'
1544 * If the database is loaded in from stable store if it has not been loaded.
1545 * If it cannot be loaded, it is initialized using the scheme stored in
1546 * the table_desc. NULL is returned if the initialization fails.
1549 db_dictionary::find_table_noLDAP(char *tab
, db_table_desc
**where
,
1550 bool_t searchDeferred
, bool_t doLoad
)
1559 READLOCK(this, NULL
, "r db_dictionary::find_table");
1560 tbl
= find_table_desc(tab
, searchDeferred
);
1562 READUNLOCK(this, NULL
, "ru db_dictionary::find_table");
1563 return (NULL
); // not found
1566 if (tbl
->database
!= NULL
|| !doLoad
) {
1567 if (tbl
->database
&& where
) *where
= tbl
;
1568 READUNLOCK(this, NULL
, "ru db_dictionary::find_table");
1569 return (tbl
->database
); // return handle
1572 READUNLOCK(this, NULL
, "ru db_dictionary::find_table");
1573 WRITELOCK(this, NULL
, "w db_dictionary::find_table");
1574 /* Re-check; some other thread might have loaded the db */
1575 if (tbl
->database
!= NULL
) {
1576 if (where
) *where
= tbl
;
1577 WRITEUNLOCK(this, NULL
, "wu db_dictionary::find_table");
1578 return (tbl
->database
); // return handle
1581 // need to load in/init database
1582 dbase
= new db(tab
);
1584 if (dbase
== NULL
) {
1585 WRITEUNLOCK(this, NULL
,
1586 "db_dictionary::find_table: could not allocate space");
1587 FATAL3("db_dictionary::find_table: could not allocate space",
1588 DB_MEMORY_LIMIT
, NULL
);
1592 * Lock the newly created 'dbase', so we can release the general
1593 * db_dictionary lock.
1595 WRITELOCKNR(dbase
, lret
, "w dbase db_dictionary::find_table");
1597 WRITEUNLOCK(this, NULL
,
1598 "db_dictionary::find_table: could not lock dbase");
1599 FATAL3("db_dictionary::find_table: could not lock dbase",
1600 DB_LOCK_ERROR
, NULL
);
1602 /* Assign tbl->database, and then release the 'this' lock */
1603 tbl
->database
= dbase
;
1604 WRITEUNLOCK(this, NULL
, "wu db_dictionary::find_table");
1606 if (dbase
->load()) { // try to load in database
1607 if (where
) *where
= tbl
;
1608 WRITEUNLOCK(dbase
, dbase
, "wu dbase db_dictionary::find_table");
1613 tbl
->database
= NULL
;
1614 WARNING("db_dictionary::find_table: could not load database");
1618 /* Log action to be taken on the dictionary and update db_update_version. */
1621 db_dictionary::log_action(int action
, char *tab
, table_obj
*tobj
)
1623 WRITELOCK(this, DB_LOCK_ERROR
, "w db_dictionary::log_action");
1625 vers
*newv
= db_update_version
.nextminor();
1626 db_dictlog_entry
le(action
, newv
, tab
, tobj
);
1628 if (open_log() < 0) {
1630 WRITEUNLOCK(this, DB_STORAGE_LIMIT
,
1631 "wu db_dictionary::log_action");
1632 return (DB_STORAGE_LIMIT
);
1635 if (logfile
->append(&le
) < 0) {
1636 WARNING_M("db::log_action: could not add log entry: ");
1639 WRITEUNLOCK(this, DB_STORAGE_LIMIT
,
1640 "wu db_dictionary::log_action");
1641 return (DB_STORAGE_LIMIT
);
1644 db_update_version
.assign(newv
);
1648 WRITEUNLOCK(this, DB_LOCK_ERROR
, "wu db_dictionary::log_action");
1649 return (DB_SUCCESS
);
1652 // For a complete 'delete' operation, we want the following behaviour:
1653 // 1. If there is an entry in the log, the physical table exists and is
1655 // 2. If there is no entry in the log, the physical table may or may not
1659 db_dictionary::delete_table_aux(char *tab
, int mode
)
1663 WRITELOCK(this, DB_LOCK_ERROR
, "w db_dictionary::delete_table_aux");
1665 WRITEUNLOCK(this, DB_LOCK_ERROR
,
1666 "wu db_dictionary::delete_table_aux");
1667 return (DB_BADDICTIONARY
);
1671 if ((tbl
= find_table_desc(tab
)) == NULL
) { // table not found
1672 WRITEUNLOCK(this, DB_LOCK_ERROR
,
1673 "wu db_dictionary::delete_table_aux");
1674 return (DB_NOTFOUND
);
1677 if (mode
!= INMEMORY_ONLY
) {
1681 db_status status
= log_action(DB_REMOVE_TABLE
, tab
);
1682 if (status
!= DB_SUCCESS
) {
1683 WRITEUNLOCK(this, status
,
1684 "wu db_dictionary::delete_table_aux");
1688 // Remove physical structures
1689 db
*dbase
= tbl
->database
;
1690 if (dbase
== NULL
) { // need to get desc to access files
1691 dbase
= new db(tab
);
1694 if (dbase
== NULL
) {
1696 "db_dictionary::delete_table: could not create db structure");
1697 WRITEUNLOCK(this, DB_MEMORY_LIMIT
,
1698 "wu db_dictionary::delete_table_aux");
1699 return (DB_MEMORY_LIMIT
);
1701 dbase
->remove_files(); // remove physical files
1706 // Remove in-memory structures
1707 ret
= remove_from_dictionary(dictionary
, tab
, TRUE
);
1708 WRITEUNLOCK(this, ret
, "wu db_dictionary::delete_table_aux");
1713 * Delete table with given name 'tab' from dictionary.
1714 * Returns error code if table does not exist or if dictionary has not been
1715 * initialized. Dictionary is updated to stable store if deletion is
1716 * successful. Fatal error occurs if dictionary cannot be saved.
1717 * Returns DB_SUCCESS if dictionary has been updated successfully.
1718 * Note that the files associated with the table are also removed.
1721 db_dictionary::delete_table(char *tab
)
1723 return (delete_table_aux(tab
, !INMEMORY_ONLY
));
1726 // For a complete 'add' operation, we want the following behaviour:
1727 // 1. If there is an entry in the log, then the physical table exists and
1728 // has been initialized properly.
1729 // 2. If there is no entry in the log, the physical table may or may not
1730 // exist. In this case, we don't really care because we cannot get at
1731 // it. The next time we add a table with the same name to the dictionary,
1732 // it will be initialized properly.
1733 // This mode is used when the table is first created.
1735 // For an INMEMORY_ONLY operation, only the internal structure is created and
1736 // updated. This mode is used when the database gets loaded and the internal
1737 // dictionary gets updated from the log entries.
1740 db_dictionary::add_table_aux(char *tab
, table_obj
* tobj
, int mode
)
1744 WRITELOCK(this, DB_LOCK_ERROR
, "w db_dictionary::add_table_aux");
1746 WRITEUNLOCK(this, DB_LOCK_ERROR
,
1747 "wu db_dictionary::add_table_aux");
1748 return (DB_BADDICTIONARY
);
1751 if (find_table_desc(tab
) != NULL
) {
1752 WRITEUNLOCK(this, DB_LOCK_ERROR
,
1753 "wu db_dictionary::add_table_aux");
1754 return (DB_NOTUNIQUE
); // table already exists
1757 // create data structures for table
1758 db_table_desc
*new_table
= 0;
1759 db_status status
= create_table_desc(tab
, tobj
, &new_table
);
1761 if (status
!= DB_SUCCESS
) {
1762 WRITEUNLOCK(this, DB_LOCK_ERROR
,
1763 "wu db_dictionary::add_table_aux");
1767 if (mode
!= INMEMORY_ONLY
) {
1768 // create physical structures for table
1769 new_table
->database
= new db(tab
);
1770 if (new_table
->database
== NULL
) {
1771 delete_table_desc(new_table
);
1772 WRITEUNLOCK(this, DB_MEMORY_LIMIT
,
1773 "db_dictionary::add_table: could not allocate space for db");
1775 "db_dictionary::add_table: could not allocate space for db",
1776 DB_MEMORY_LIMIT
, DB_MEMORY_LIMIT
);
1778 if (new_table
->database
->init(new_table
->scheme
) == 0) {
1780 "db_dictionary::add_table: could not initialize database from scheme");
1781 new_table
->database
->remove_files();
1782 delete_table_desc(new_table
);
1783 WRITEUNLOCK(this, DB_STORAGE_LIMIT
,
1784 "wu db_dictionary::add_table_aux");
1785 return (DB_STORAGE_LIMIT
);
1788 // update 'external' copy of dictionary
1789 status
= log_action(DB_ADD_TABLE
, tab
, tobj
);
1791 if (status
!= DB_SUCCESS
) {
1792 new_table
->database
->remove_files();
1793 delete_table_desc(new_table
);
1794 WRITEUNLOCK(this, status
,
1795 "wu db_dictionary::add_table_aux");
1800 // finally, update in-memory copy of dictionary
1801 ret
= add_to_dictionary(dictionary
, new_table
);
1802 WRITEUNLOCK(this, ret
, "wu db_dictionary::add_table_aux");
1807 * Add table with given name 'tab' and description 'zdesc' to dictionary.
1808 * Returns errror code if table already exists, or if no memory can be found
1809 * to store the descriptor, or if dictionary has not been intialized.
1810 * Dictionary is updated to stable store if addition is successful.
1811 * Fatal error occurs if dictionary cannot be saved.
1812 * Returns DB_SUCCESS if dictionary has been updated successfully.
1815 db_dictionary::add_table(char *tab
, table_obj
* tobj
)
1817 return (add_table_aux(tab
, tobj
, !INMEMORY_ONLY
));
1821 * Translate given NIS attribute list to a db_query structure.
1822 * Return FALSE if dictionary has not been initialized, or
1823 * table does not have a scheme (which should be a fatal error?).
1826 db_dictionary::translate_to_query(db_table_desc
* tbl
, int numattrs
,
1829 READLOCK(this, NULL
, "r db_dictionary::translate_to_query");
1831 tbl
->scheme
== NULL
|| numattrs
== 0 || attrlist
== NULL
) {
1832 READUNLOCK(this, NULL
, "ru db_dictionary::translate_to_query");
1836 db_query
*q
= new db_query(tbl
->scheme
, numattrs
, attrlist
);
1838 READUNLOCK(this, NULL
,
1839 "db_dictionary::translate: could not allocate space");
1840 FATAL3("db_dictionary::translate: could not allocate space",
1841 DB_MEMORY_LIMIT
, NULL
);
1844 if (q
->size() == 0) {
1846 READUNLOCK(this, NULL
, "ru db_dictionary::translate_to_query");
1849 READUNLOCK(this, NULL
, "ru db_dictionary::translate_to_query");
1853 static db_table_names gt_answer
;
1857 get_table_name(db_table_desc
* tbl
)
1860 return (DB_BADTABLE
);
1862 if (gt_posn
< gt_answer
.db_table_names_len
)
1863 gt_answer
.db_table_names_val
[gt_posn
++] =
1864 strdup(tbl
->table_name
);
1866 return (DB_BADTABLE
);
1868 return (DB_SUCCESS
);
1873 * Return the names of tables in this dictionary.
1874 * XXX This routine is used only for testing only;
1875 * if to be used for real, need to free memory sensibly, or
1876 * caller of get_table_names should have freed them.
1879 db_dictionary::get_table_names()
1881 READLOCK(this, NULL
, "r db_dictionary::get_table_names");
1882 gt_answer
.db_table_names_len
= dictionary
->count
;
1883 gt_answer
.db_table_names_val
= new db_table_namep
[dictionary
->count
];
1885 if ((gt_answer
.db_table_names_val
) == NULL
) {
1886 READUNLOCK(this, NULL
,
1887 "db_dictionary::get_table_names: could not allocate space for names");
1889 "db_dictionary::get_table_names: could not allocate space for names",
1890 DB_MEMORY_LIMIT
, NULL
);
1893 enumerate_dictionary(dictionary
, &get_table_name
);
1894 READUNLOCK(this, NULL
, "ru db_dictionary::get_table_names");
1895 return (>_answer
);
1899 db_checkpoint_aux(db_table_desc
*current
)
1904 if (current
== NULL
)
1905 return (DB_BADTABLE
);
1907 if (current
->database
== NULL
) { /* need to load it in */
1908 dbase
= new db(current
->table_name
);
1909 if (dbase
== NULL
) {
1911 "db_dictionary::db_checkpoint: could not allocate space",
1912 DB_MEMORY_LIMIT
, DB_MEMORY_LIMIT
);
1914 if (dbase
->load() == 0) {
1916 "db_dictionary::db_checkpoint: could not load table %s",
1917 current
->table_name
);
1919 return (DB_BADTABLE
);
1921 status
= dbase
->checkpoint();
1922 delete dbase
; // unload
1924 status
= current
->database
->checkpoint();
1927 return (DB_STORAGE_LIMIT
);
1928 return (DB_SUCCESS
);
1931 /* Like db_checkpoint_aux except only stops on LIMIT errors */
1933 db_checkpoint_aux_cont(db_table_desc
*current
)
1935 db_status status
= db_checkpoint_aux(current
);
1937 if (status
== DB_STORAGE_LIMIT
|| status
== DB_MEMORY_LIMIT
)
1940 return (DB_SUCCESS
);
1944 db_dictionary::db_checkpoint(char *tab
)
1950 READLOCK(this, DB_LOCK_ERROR
, "r db_dictionary::db_checkpoint");
1952 READUNLOCK(this, DB_LOCK_ERROR
, "ru db_dictionary::db_checkpoint");
1954 return (DB_BADDICTIONARY
);
1956 checkpoint(); // checkpoint dictionary first
1958 READLOCK(this, DB_LOCK_ERROR
, "r db_dictionary::db_checkpoint");
1961 ret
= enumerate_dictionary(dictionary
, &db_checkpoint_aux_cont
);
1962 READUNLOCK(this, ret
, "ru db_dictionary::db_checkpoint");
1966 if ((tbl
= find_table_desc(tab
)) == NULL
) {
1967 READUNLOCK(this, DB_LOCK_ERROR
,
1968 "ru db_dictionary::db_checkpoint");
1969 return (DB_BADTABLE
);
1972 ret
= db_checkpoint_aux(tbl
);
1973 READUNLOCK(this, ret
, "ru db_dictionary::db_checkpoint");
1977 /* *********************** db_standby **************************** */
1978 /* Deal with list of tables that need to be 'closed' */
1980 #define OPENED_DBS_CHUNK 12
1981 static db
**db_standby_list
;
1982 static uint_t db_standby_size
= 0;
1983 static uint_t db_standby_count
= 0;
1984 DECLMUTEXLOCK(db_standby_list
);
1987 * Returns 1 if all databases on the list could be closed, 0
1991 close_standby_list()
1995 const char *myself
= "close_standby_list";
1997 MUTEXLOCK(db_standby_list
, "close_standby_list");
1999 if (db_standby_count
== 0) {
2000 MUTEXUNLOCK(db_standby_list
, "close_standby_list");
2004 for (i
= 0, ret
= 0; i
< db_standby_size
; i
++) {
2005 if ((database
= db_standby_list
[i
])) {
2007 * In order to avoid a potential dead-lock, we
2008 * check to see if close_log() would be able to
2009 * lock the db; if not, just skip the db.
2013 TRYWRITELOCK(database
, lockok
,
2014 "try w db_dictionary::close_standby_list");
2017 database
->close_log(1);
2018 db_standby_list
[i
] = (db
*)NULL
;
2020 WRITEUNLOCK(database
, db_standby_count
== 0,
2021 "db_dictionary::close_standby_list");
2022 if (db_standby_count
== 0) {
2026 } else if (lockok
!= EBUSY
) {
2027 logmsg(MSG_NOTIMECHECK
, LOG_INFO
,
2028 "%s: try-lock error %d",
2030 } /* else it's EBUSY; skip to the next one */
2034 MUTEXUNLOCK(db_standby_list
, "close_standby_list");
2040 * Add given database to list of databases that have been opened for updates.
2041 * If size of list exceeds maximum, close opened databases first.
2045 add_to_standby_list(db
* database
)
2048 const char *myself
= "add_to_standby_list";
2050 MUTEXLOCK(db_standby_list
, "add_to_standby_list");
2052 if (database
== 0) {
2053 MUTEXUNLOCK(db_standby_list
, "add_to_standby_list");
2057 /* Try to keep the list below OPENED_DBS_CHUNK */
2058 if (db_standby_count
>= OPENED_DBS_CHUNK
) {
2059 MUTEXUNLOCK(db_standby_list
, "add_to_standby_list");
2060 close_standby_list();
2061 MUTEXLOCK(db_standby_list
, "add_to_standby_list");
2064 if (db_standby_count
>= db_standby_size
) {
2065 db
**ndsl
= (db
**)realloc(db_standby_list
,
2066 (db_standby_size
+OPENED_DBS_CHUNK
) *
2070 MUTEXUNLOCK(db_standby_list
, "add_to_standby_list");
2071 logmsg(MSG_NOMEM
, LOG_ERR
,
2072 "%s: realloc(%d) => NULL",
2073 myself
, (db_standby_size
+OPENED_DBS_CHUNK
) *
2078 db_standby_list
= ndsl
;
2080 for (i
= db_standby_size
; i
< db_standby_size
+OPENED_DBS_CHUNK
;
2082 db_standby_list
[i
] = 0;
2084 db_standby_size
+= OPENED_DBS_CHUNK
;
2087 for (i
= 0; i
< db_standby_size
; i
++) {
2088 if (db_standby_list
[i
] == (db
*)NULL
) {
2089 db_standby_list
[i
] = database
;
2091 MUTEXUNLOCK(db_standby_list
, "add_to_standby_list");
2096 MUTEXUNLOCK(db_standby_list
, "add_to_standby_list");
2102 remove_from_standby_list(db
* database
)
2106 MUTEXLOCK(db_standby_list
, "remove_from_standby_list");
2108 if (database
== 0) {
2109 MUTEXUNLOCK(db_standby_list
, "remove_from_standby_list");
2113 for (i
= 0; i
< db_standby_size
; i
++) {
2114 if ((database
== db_standby_list
[i
])) {
2115 db_standby_list
[i
] = (db
*)NULL
;
2117 MUTEXUNLOCK(db_standby_list
,
2118 "remove_from_standby_list");
2123 MUTEXUNLOCK(db_standby_list
, "remove_from_standby_list");
2128 /* Release space for copied dictionary */
2130 db_release_dictionary(db_dict_desc_p d
) {
2135 for (i
= 0; i
< d
->tables
.tables_len
; i
++) {
2136 db_table_desc_p n
, t
= d
->tables
.tables_val
[i
];
2139 delete_table_desc(t
);
2150 * Make a copy of the dictionary
2153 db_dictionary::db_copy_dictionary(void) {
2156 int i
, ok
= 1, count
= 0;
2158 WRITELOCK(this, NULL
, "db_dictionary::db_copy_dictionary w");
2160 if (dictionary
== NULL
) {
2161 WRITEUNLOCK(this, NULL
,
2162 "db_dictionary::db_copy_dictionary wu");
2166 tmp
= new db_dict_desc
;
2168 WRITEUNLOCK(this, NULL
,
2169 "db_dictionary::db_copy_dictionary wu: no memory");
2173 tmp
->tables
.tables_val
= (db_table_desc_p
*)calloc(
2174 tmp
->tables
.tables_len
,
2175 sizeof (tmp
->tables
.tables_val
[0]));
2176 if (tmp
->tables
.tables_val
== NULL
) {
2178 WRITEUNLOCK(this, NULL
,
2179 "db_dictionary::db_copy_dictionary wu: no memory");
2183 tmp
->impl_vers
= dictionary
->impl_vers
;
2184 tmp
->tables
.tables_len
= 0;
2187 /* For each table ... */
2188 for (i
= 0; ok
&& i
< dictionary
->tables
.tables_len
; i
++) {
2189 db_table_desc_p tbl
= NULL
,
2190 t
= dictionary
->tables
.tables_val
[i
];
2191 /* ... and each bucket in the chain ... */
2192 while (ok
&& t
!= NULL
) {
2193 db_table_desc_p n
, savenext
= t
->next
;
2195 if (db_clone_bucket(t
, &n
)) {
2199 tmp
->tables
.tables_val
[i
] = n
;
2208 tmp
->tables
.tables_len
++;
2212 #ifdef NISDB_LDAP_DEBUG
2213 if ((tmp
->tables
.tables_len
!=
2214 dictionary
->tables
.tables_len
) ||
2215 (tmp
->count
!= dictionary
->count
))
2217 #endif /* NISDB_LDAP_DEBUG */
2219 db_release_dictionary(tmp
);
2227 * Set deferred commit mode. To do this, we make a copy of the table
2228 * (structures and data), and put that on the deferred dictionary list.
2229 * This list is used for lookups during a resync, so clients continue
2230 * to see the pre-resync data. Meanwhile, any changes (including table
2231 * deletes) are done to the (temporarily hidden to clients) table in
2232 * the normal dictionary.
2235 db_dictionary::defer(char *table
) {
2236 db_status ret
= DB_SUCCESS
;
2237 WRITELOCK(this, DB_LOCK_ERROR
, "w db_dictionary::defer");
2238 db_table_desc
*tbl
= find_table_desc(table
);
2240 const char *myself
= "db_dictionary::defer";
2243 db_table_desc
*clone
, *savenext
= tbl
->next
;
2245 * Only want to clone one db_table_desc, so temporarily
2249 res
= db_clone_bucket(tbl
, &clone
);
2250 /* Restore link to tail */
2251 tbl
->next
= savenext
;
2254 if (deferred
.dictionary
== NULL
) {
2255 deferred
.dictionary
= new db_dict_desc
;
2256 if (deferred
.dictionary
== NULL
) {
2257 WRITEUNLOCK(this, DB_MEMORY_LIMIT
,
2258 "wu db_dictionary::defer");
2259 return (DB_MEMORY_LIMIT
);
2261 deferred
.dictionary
->tables
.tables_len
= 0;
2262 deferred
.dictionary
->tables
.tables_val
= NULL
;
2263 deferred
.dictionary
->count
= 0;
2264 deferred
.dictionary
->impl_vers
=
2268 /* Initialize and load the database for the clone */
2269 if (clone
->database
== 0) {
2270 clone
->database
= new db(table
);
2271 if (clone
->database
!= 0) {
2272 if (clone
->database
->load()) {
2273 logmsg(MSG_NOTIMECHECK
,
2274 #ifdef NISDB_LDAP_DEBUG
2278 #endif /* NISDB_LDAP_DEBUG */
2279 "%s: Clone DB for \"%s\" loaded",
2280 myself
, NIL(table
));
2282 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
2283 "%s: Error loading clone DB for \"%s\"",
2284 myself
, NIL(table
));
2285 delete clone
->database
;
2286 clone
->database
= 0;
2287 ret
= DB_INTERNAL_ERROR
;
2290 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
2291 "%s: Unable to clone DB for \"%s\"",
2292 myself
, NIL(table
));
2293 ret
= DB_MEMORY_LIMIT
;
2296 if (clone
->database
!= 0) {
2297 clone
->database
->markDeferred();
2298 stat
= add_to_dictionary(deferred
.dictionary
,
2301 if (stat
!= DB_SUCCESS
) {
2302 delete clone
->database
;
2303 clone
->database
= 0;
2305 if (stat
== DB_NOTUNIQUE
) {
2306 /* Already deferred */
2312 /* Return value already set above */
2315 ret
= DB_INTERNAL_ERROR
;
2320 WRITEUNLOCK(this, ret
, "wu db_dictionary::defer");
2325 * Unset deferred commit mode and roll back changes; doesn't recover the
2326 * disk data, but that's OK, since we only want to be able to continue
2327 * serving the table until we can try a full dump again.
2329 * The rollback is done by removing (and deleting) the updated table from
2330 * the dictionary, and then moving the saved table from the deferred
2331 * dictionary list to the actual one.
2334 db_dictionary::rollback(char *table
) {
2335 db_status ret
= DB_SUCCESS
;
2336 WRITELOCK(this, DB_LOCK_ERROR
, "w db_dictionary::rollback");
2337 db_table_desc
*old
= search_dictionary(deferred
.dictionary
, table
);
2338 db_table_desc
*upd
= search_dictionary(dictionary
, table
);
2341 WRITEUNLOCK(this, DB_NOTFOUND
, "wu db_dictionary::rollback");
2342 return (DB_NOTFOUND
);
2346 * Remove old incarnation from deferred dictionary. We already hold
2347 * a pointer ('old') to it, so don't delete.
2349 ret
= remove_from_dictionary(deferred
.dictionary
, table
, FALSE
);
2350 if (ret
!= DB_SUCCESS
) {
2351 #ifdef NISDB_LDAP_DEBUG
2353 #endif /* NISDB_LDAP_DEBUG */
2354 WRITEUNLOCK(this, ret
, "wu db_dictionary::rollback");
2358 if (old
->database
!= 0)
2359 old
->database
->unmarkDeferred();
2362 * Remove updated incarnation from dictionary. If 'upd' is NULL,
2363 * the table has been removed while we were in deferred mode, and
2364 * that's OK; we just need to retain the old incarnation.
2367 ret
= remove_from_dictionary(dictionary
, table
, FALSE
);
2368 if (ret
!= DB_SUCCESS
) {
2369 #ifdef NISDB_LDAP_DEBUG
2371 #endif /* NISDB_LDAP_DEBUG */
2373 * Cut our losses; delete old incarnation, and leave
2374 * updated one in place.
2376 delete_table_desc(old
);
2377 WRITEUNLOCK(this, ret
, "wu db_dictionary::rollback");
2380 /* Throw away updates */
2381 delete_table_desc(upd
);
2384 /* (Re-)insert old incarnation in the dictionary */
2385 ret
= add_to_dictionary(dictionary
, old
);
2386 if (ret
!= DB_SUCCESS
) {
2387 #ifdef NISDB_LDAP_DEBUG
2389 #endif /* NISDB_LDAP_DEBUG */
2390 /* At least avoid memory leak */
2391 delete_table_desc(old
);
2393 "db_dictionary::rollback: rollback error %d for \"%s\"", ret
, table
);
2396 WRITEUNLOCK(this, ret
, "wu db_dictionary::rollback");
2401 * Commit changes. Done by simply removing and deleting the pre-resync
2402 * data from the deferred dictionary.
2405 db_dictionary::commit(char *table
) {
2406 db_status ret
= DB_SUCCESS
;
2407 WRITELOCK(this, DB_LOCK_ERROR
, "w db_dictionary::commit");
2408 db_table_desc
*old
= search_dictionary(deferred
.dictionary
, table
);
2411 /* Fine (we hope); nothing to do */
2412 WRITEUNLOCK(this, ret
, "wu db_dictionary::commit");
2413 return (DB_SUCCESS
);
2416 ret
= remove_from_dictionary(deferred
.dictionary
, table
, FALSE
);
2417 if (ret
== DB_SUCCESS
)
2418 delete_table_desc(old
);
2419 #ifdef NISDB_LDAP_DEBUG
2422 #endif /* NISDB_LDAP_DEBUG */
2424 WRITEUNLOCK(this, ret
, "wu db_dictionary::commit");
2429 * The noWriteThrough flag is used to prevent modifies/updates to LDAP
2430 * while we're incorporating log data into the in-memory tables.
2433 db_dictionary::setNoWriteThrough(void) {
2434 ASSERTWHELD(this->dict
);
2435 noWriteThrough
.flag
++;
2439 db_dictionary::clearNoWriteThrough(void) {
2440 ASSERTWHELD(this->dict
);
2441 if (noWriteThrough
.flag
> 0)
2442 noWriteThrough
.flag
--;
2443 #ifdef NISDB_LDAP_DEBUG
2446 #endif /* NISDB_LDAP_DEBUG */