4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
24 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
33 #include <sys/param.h>
34 #include "db_headers.h"
36 #include "db_mindex.h"
37 #include "db_pickle.h"
39 #include "nisdb_ldap.h"
40 #include "ldap_nisdbquery.h"
42 #include "ldap_ruleval.h"
43 #include "ldap_scheme.h"
44 #include "ldap_parse.h"
45 #include "nis_hashitem.h"
48 * Constructor: Create new table using scheme defintion supplied.
49 * (Make copy of scheme and keep it with table.)
51 db_mindex::db_mindex(db_scheme
*how
, char *tablePath
) : rversion()
53 noWriteThrough
.flag
= 0;
58 if (tablePath
!= NULL
)
62 /* Constructor: Create empty table (no scheme, no table or indices). */
63 db_mindex::db_mindex() : rversion()
67 indices
.indices_len
= 0;
68 indices
.indices_val
= NULL
;
69 noWriteThrough
.flag
= 0;
76 db_mindex::~db_mindex()
78 reset(); /* get rid of data structures first */
83 * Initialize table using information given in scheme 'how'.
84 * Record the scheme for later use (make copy of it);
85 * create the required number of indices; and create table for storing
89 db_mindex::init(db_scheme
* how
)
91 scheme
= new db_scheme(how
); // make copy
93 FATAL("db_mindex::init: could not allocate space for scheme",
96 if (scheme
->numkeys() == 0) {
97 WARNING("db_mindex::init: empty scheme encountered");
98 /* what action should we take here? */
101 indices
.indices_len
= how
->numkeys();
102 db_key_desc
* keys
= how
->keyloc();
105 /* homogeneous indices for now */
106 indices
.indices_val
= new db_index
[indices
.indices_len
];
107 if (indices
.indices_val
== NULL
) {
109 indices
.indices_len
= 0;
111 FATAL("db_mindex::init: could not allocate space for indices",
114 for (i
= 0; i
< indices
.indices_len
; i
++) {
115 indices
.indices_val
[i
].init(&(keys
[i
]));
117 table
= new db_table();
121 delete indices
.indices_val
;
122 indices
.indices_val
= NULL
;
123 indices
.indices_len
= 0;
124 FATAL("db_mindex::init: could not allocate space for table",
132 /* empty associated tables associated */
134 db_mindex::reset_tables()
138 WRITELOCKV(this, "w db_mindex::reset_tables");
139 /* Add sanity check in case of table corruption */
140 if (indices
.indices_val
!= NULL
) {
141 for (i
= 0; i
< indices
.indices_len
; i
++) {
142 indices
.indices_val
[i
].reset();
145 if (table
) table
->reset();
146 WRITEUNLOCKV(this, "wu db_mindex::reset_tables");
151 * Return a list of index_entries that satsify the given query 'q'.
152 * Return the size of the list in 'count'. Return NULL if list is empty.
153 * Return in 'valid' FALSE if query is not well formed.
156 db_mindex::satisfy_query(db_query
*q
, long *count
, bool_t
*valid
) {
157 return (satisfy_query(q
, count
, valid
, FALSE
));
161 db_mindex::satisfy_query(db_query
*q
, long *count
, bool_t
*valid
,
163 db_index_entry_p ret
;
167 /* Make sure we have somewhere to store the "request valid" status */
169 valid
= &validRequest
;
171 /* Prepare for a failed lock */
175 READLOCK(this, NULL
, "r db_mindex::satisfy_query");
178 * Only get data from LDAP if the caller requested it,
179 * and if we're mapping for this table.
181 fromLDAP
= (fromLDAP
&& !noLDAPquery
.flag
&&
182 (table
->mapping
.fromLDAP
||
183 table
->mapping
.objType
!= NIS_TABLE_OBJ
));
186 * If we always fetch data from LDAP for query's, then do so now,
187 * before invoking the "real" satisfy_query().
189 if (fromLDAP
&& table
->mapping
.matchFetch
== mat_always
) {
192 READLOCKNR(table
, lockcode
,
193 "r db_mindex::satisfy_query table");
195 READUNLOCK(this, NULL
, "ru db_mindex::satisfy_query");
199 queryRes
= queryLDAP(q
, 0, 1);
201 READUNLOCKNR(table
, lockcode
,
202 "ru db_mindex::satisfy_query table");
204 READUNLOCK(this, NULL
, "ru db_mindex::satisfy_query");
207 if (queryRes
!= LDAP_SUCCESS
) {
208 /* queryLDAP() sets error codes etc. */
209 READUNLOCK(this, NULL
, "ru db_mindex::satisfy_query");
215 ret
= satisfy_query_dbonly(q
, count
, fromLDAP
? TRUE
: FALSE
, valid
);
217 /* If we found it, or if we're not mapping, return */
218 if (ret
!= NULL
|| !fromLDAP
) {
219 READUNLOCK(this, NULL
, "ru db_mindex::satisfy_query");
221 } else if (ret
== NULL
&& !(*valid
)) {
222 /* No result, and the request wasn't valid */
223 READUNLOCK(this, NULL
, "ru db_mindex::satisfy_query");
227 /* Get data from LDAP */
228 if (table
->mapping
.matchFetch
!= mat_never
) {
229 queryRes
= queryLDAP(q
, 0, 1);
232 * We'll now go on to check for an un-expired entry again,
233 * even though we're pretty sure that won't work (already
234 * did that, and nothing's changed). However, we accept that
235 * slight inefficiency in the interest of keeping the code
236 * simple; we expect 'mat_never' to be used very rarely.
238 queryRes
= LDAP_SUCCESS
;
241 if (queryRes
== LDAP_SUCCESS
) {
243 * Check if we've got a match now. If not, try one
244 * last time for an expired match.
246 ret
= satisfy_query_dbonly(q
, count
, TRUE
, valid
);
248 ret
= satisfy_query_dbonly(q
, count
, FALSE
, valid
);
252 * Check if we have an expired entry; if so, return
253 * it with an appropriate status.
255 ret
= satisfy_query_dbonly(q
, count
, FALSE
, valid
);
258 READUNLOCK(this, NULL
, "ru db_mindex::satisfy_query");
264 db_mindex::satisfy_query_dbonly(db_query
*q
, long *count
,
265 bool_t checkExpire
, bool_t
*valid
)
267 db_index_entry_p oldres
= NULL
, newres
;
269 long num_new
, num_old
= 0;
270 int limit
= q
->size();
271 db_qcomp
* comps
= q
->queryloc();
273 if (valid
) *valid
= TRUE
; /* True to begin with. */
275 /* Add sanity check in case table corrupted */
276 if (indices
.indices_len
!= 0 && indices
.indices_val
== NULL
) {
277 WARNING("db_mindex::satisfy_query: table has no indices");
278 if (valid
) *valid
= FALSE
;
283 for (i
= 0; i
< limit
; i
++) {
284 if ((curr_ind
= comps
[i
].which_index
) < indices
.indices_len
) {
285 newres
= indices
.indices_val
[curr_ind
].lookup(
286 comps
[i
].index_value
, &num_new
,
288 if (newres
== NULL
) {
292 if (oldres
== NULL
) {
296 oldres
= newres
->join(num_new
, num_old
,
298 if (oldres
== NULL
) {
304 WARNING("db_mindex::satisfy_query: index out of range");
305 if (valid
) *valid
= FALSE
;
315 * Returns an array of size 'count' of 'entry_object_p's, pointing to
316 * copies of entry_objects named by the result list of db_index_entries 'res'.
317 * Sets db_status 'statp' if error encountered; otherwise, leaves it unchanged.
320 db_mindex::prepare_results(int count
, db_index_entry_p res
, db_status
*statp
)
322 READLOCK(this, NULL
, "r db_mindex::prepare_results");
323 READLOCK2(table
, NULL
, "r table db_mindex::prepare_results", this);
324 entry_object_p
* entries
= new entry_object_p
[count
];
327 if (entries
== NULL
) {
328 READUNLOCK2(this, table
, NULL
, NULL
,
329 "ru db_mindex::prepare_results: could not allocate space",
330 "ru table db_mindex::prepare_results: could not allocate space");
331 FATAL3("db_mindex::prepare_results: could not allocate space",
332 DB_MEMORY_LIMIT
, NULL
);
335 for (i
= 0; i
< count
; i
++) {
338 for (j
= 0; j
< i
; j
++) // cleanup
339 free_entry(entries
[j
]);
341 "db_mindex::prepare_results: incorrect count");
342 *statp
= DB_INTERNAL_ERROR
;
345 new_entry(table
->get_entry(res
->getlocation()));
346 res
= res
->getnextresult();
349 READUNLOCK2(this, table
, entries
, entries
,
350 "ru db_mindex::prepare_results",
351 "ru db_mindex::prepare_results");
357 * Returns a newly created db_query structure containing the index values
358 * as obtained from the record named by 'recnum'. The record itself, along
359 * with information on the schema definition of this table, will determine
360 * which values are extracted from the record and placed into the result.
361 * Returns NULL if recnum is not a valid entry.
362 * Note that space is allocated for the query and the index values
363 * (i.e. do not share pointers with strings in 'obj'.)
366 db_mindex::extract_index_values_from_record(entryp recnum
)
370 ret
= extract_index_values_from_object(table
->get_entry(recnum
));
375 * Returns a newly created db_query containing the index values as
376 * obtained from the given object. The object itself,
377 * along with information on the scheme given, will determine
378 * which values are extracted from the object and placed into the query.
379 * Returns an empty query if 'obj' is not a valid entry.
380 * Note that space is allocated for the query and the index values
381 * (i.e. do not share pointers with strings in 'obj'.)
384 db_mindex::extract_index_values_from_object(entry_object_p obj
)
386 READLOCK(this, NULL
, "r db_mindex::extract_index_values_from_object");
387 if (scheme
->numkeys() != indices
.indices_len
) { // probably built wrong
389 "number of keys (%d) does not equal number of indices (%d)",
390 scheme
->numkeys(), indices
.indices_len
);
391 READUNLOCK(this, NULL
,
392 "ru db_mindex::extract_index_values_from_object");
393 return (new db_query()); // null query
394 } else if (obj
== NULL
) {
395 READUNLOCK(this, NULL
,
396 "ru db_mindex::extract_index_values_from_object");
399 db_query
* answer
= new db_query(scheme
, obj
);
402 * XXX If the unlock fails, and we return NULL,
403 * we leak 'answer'. On the other hand, if we
404 * return 'answer', the object may remain locked,
405 * but the caller doesn't know that anything
408 READUNLOCK(this, NULL
,
409 "ru db_mindex::extract_index_values_from_object");
412 FATAL3("db_mindex::extract: could not allocate space",
413 DB_MEMORY_LIMIT
, NULL
);
416 READUNLOCK(this, NULL
,
417 "ru db_mindex::extract_index_values_from_object");
422 * Returns the first entry found in the table by setting 'answer' to
423 * point to the a copy of entry_object. Returns DB_SUCCESS if found;
424 * DB_NOTFOUND otherwise.
427 db_mindex::first(entryp
*where
, entry_object
** answer
)
429 db_status ret
= DB_SUCCESS
;
432 * table->first_entry() returns a pointer into the table, so
433 * we must keep the table read locked until we've copied the
434 * entry_object. In order to maintain lock integrity, we must
435 * lock the db_mindex (this) before the db_table (table).
437 READLOCK(this, DB_LOCK_ERROR
, "r db_mindex::first");
438 READLOCK2(table
, DB_LOCK_ERROR
, "r table db_mindex::first", this);
439 if (table
->mapping
.fromLDAP
) {
441 (void) gettimeofday(&now
, NULL
);
442 if (now
.tv_sec
>= table
->mapping
.enumExpire
) {
443 int queryRes
= queryLDAP(0, 0, 1);
444 if (queryRes
== LDAP_SUCCESS
)
445 table
->mapping
.enumExpire
= now
.tv_sec
+
448 READUNLOCK2(this, table
,
449 DB_LOCK_ERROR
, DB_LOCK_ERROR
,
450 "ru db_mindex::first LDAP",
451 "ru table db_mindex::first LDAP");
452 return (DB_INTERNAL_ERROR
);
456 entry_object_p ptr
= table
->first_entry(where
);
460 *answer
= new_entry(ptr
);
461 READUNLOCK2(this, table
, ret
, ret
,
462 "ru db_mindex::first", "ru table db_mindex::first");
467 * Returns the next entry in the table after 'previous' by setting 'answer' to
468 * point to copy of the entry_object. Returns DB_SUCCESS if 'previous' is
469 * valid and next entry is found; DB_NOTFOUND otherwise. Sets 'where' to
470 * location of where entry is found for input as subsequent 'next' operation.
473 db_mindex::next(entryp previous
, entryp
*where
, entry_object
**answer
)
475 db_status ret
= DB_SUCCESS
;
477 READLOCK(this, DB_LOCK_ERROR
, "r db_mindex::next");
478 READLOCK2(table
, DB_LOCK_ERROR
, "r db_mindex::next", this);
479 if (!(table
->entry_exists_p(previous
)))
482 entry_object
* ptr
= table
->next_entry(previous
, where
);
486 *answer
= new_entry(ptr
);
488 READUNLOCK2(this, table
, ret
, ret
,
489 "ru db_mindex::next", "ru table db_mindex::next");
494 delete_result_list(db_next_index_desc
* orig
)
496 db_next_index_desc
* curr
, *save_next
;
497 for (curr
= orig
; curr
!= NULL
; 0) {
498 save_next
= curr
->next
;
505 static db_next_index_desc
*
506 copy_result_list(db_index_entry
* orig
)
508 db_next_index_desc
*head
= NULL
, *curr
;
509 db_index_entry
*current
;
511 for (current
= orig
; current
!= NULL
;
512 current
= current
->getnextresult()) {
513 curr
= new db_next_index_desc(current
->getlocation(), head
);
516 "db_mindex::copy_result_list: could not allocate space",
517 DB_MEMORY_LIMIT
, NULL
);
519 head
= curr
; // list is actually reversed
525 * Delete the given list of results; used when no longer interested in
526 * the results of the first/next query that returned this list.
529 db_mindex::reset_next(db_next_index_desc
*orig
)
532 return (DB_NOTFOUND
);
534 delete_result_list(orig
);
539 * Finds entry that satisfy the query 'q'. Returns the first answer by
540 * setting the pointer 'answer' to point to a copy of it. 'where' is set
541 * so that the other answers could be gotten by passing 'where' to 'next'
542 * successively. Note that the answer is a pointer to a copy of the entry.
543 * Returns DB_SUCCESS if search was successful; DB_NOTFOUND otherwise.
546 db_mindex::first(db_query
*q
,
547 db_next_index_desc
**where
, entry_object
** answer
)
549 READLOCK(this, DB_LOCK_ERROR
, "r db_mindex::first");
550 READLOCK2(table
, DB_LOCK_ERROR
, "r table db_mindex::first", this);
553 db_status ret
= DB_SUCCESS
;
554 db_index_entry
* rp
= satisfy_query(q
, &count
, &valid_query
, TRUE
);
556 if (valid_query
!= TRUE
)
558 else if (rp
== NULL
) {
562 *where
= copy_result_list(rp
);
564 entry_object_p ptr
= table
->get_entry((*where
)->location
);
568 *answer
= new_entry(ptr
);
570 READUNLOCK2(this, table
, ret
, ret
,
571 "ru db_mindex::first", "ru table db_mindex::first");
576 * Returns the next entry in the table after 'previous' by setting 'answer' to
577 * point to copy of the entry_object. Next is next in chain of answers found
578 * in previous first search with query. Returns DB_SUCCESS if 'previous' is
579 * valid and next entry is found; DB_NOTFOUND otherwise. Sets 'where' to
580 * location of where entry is found for input as subsequent 'next' operation.
583 db_mindex::next(db_next_index_desc
*previous
, db_next_index_desc
**where
,
584 entry_object
**answer
)
586 READLOCK(this, DB_LOCK_ERROR
, "r db_mindex::next");
587 READLOCK2(table
, DB_LOCK_ERROR
, "r table db_mindex::next", this);
588 db_status ret
= DB_SUCCESS
;
590 if (previous
== NULL
)
593 // should further check validity of 'previous' pointer
594 *where
= previous
->next
;
595 delete previous
; // delete previous entry
600 table
->get_entry((*where
)->location
);
604 *answer
= new_entry(ptr
);
609 READUNLOCK2(this, table
, ret
, ret
,
610 "ru db_mindex::next", "ru table db_mindex::next");
615 * Finds entry that satisfy the query 'q'. Returns the answer by
616 * setting the pointer 'rp' to point to the list of answers.
617 * Note that the answers are pointers to the COPIES of entries.
618 * Returns the number of answers find in 'count'.
619 * Returns DB_SUCCESS if search found at least one answer;
620 * returns DB_NOTFOUND if none is found.
623 db_mindex::lookup(db_query
*q
, long *count
, entry_object_p
**result
)
626 db_index_entry
* rp
= satisfy_query(q
, count
, &valid_query
, TRUE
);
627 db_status stat
= DB_SUCCESS
;
629 if (valid_query
!= TRUE
)
630 return (DB_BADQUERY
);
634 return (DB_NOTFOUND
);
637 *result
= prepare_results((int)*count
, rp
, &stat
);
643 * Return all entries within table. Returns the answer by
644 * setting the pointer 'rp' to point to the list of answers.
645 * Note that the answers are pointers to copies of the entries.
646 * Returns the number of answers find in 'count'.
647 * Returns DB_SUCCESS if search found at least one answer;
648 * returns DB_NOTFOUND if none is found.
651 db_mindex::all(long *count
, entry_object_p
**result
)
660 return (DB_NOTFOUND
);
663 READLOCK(this, DB_LOCK_ERROR
, "r db_mindex::all");
664 /* Read lock 'table' while we're traversing it */
665 READLOCKNR(table
, lret
, "r table db_mindex::all");
667 READUNLOCK(this, DB_LOCK_ERROR
, "ru db_mindex::all");
668 return (DB_LOCK_ERROR
);
671 if (table
->mapping
.fromLDAP
) {
673 (void) gettimeofday(&now
, NULL
);
674 if (now
.tv_sec
>= table
->mapping
.enumExpire
) {
675 int queryRes
= queryLDAP(0, 0, 1);
676 if (queryRes
!= LDAP_SUCCESS
) {
677 READUNLOCKNR(table
, lret
,
678 "ru table db_mindex::all LDAP");
679 READUNLOCK(this, DB_LOCK_ERROR
,
680 "ru db_mindex::all LDAP");
681 return (DB_INTERNAL_ERROR
);
686 if ((how_many
= table
->fullness()) <= 0) {
688 * Set '*count' so that the caller avoids putting garbage
689 * in an 'objects_len' field.
693 READUNLOCKNR(table
, lret
, "ru table db_mindex::all");
694 READUNLOCK(this, DB_NOTFOUND
, "ru db_mindex::all");
695 return (DB_NOTFOUND
);
698 entry_object_p
* answer
= new entry_object_p
[how_many
];
699 if (answer
== NULL
) {
700 READUNLOCKNR(table
, lret
, "ru table db_mindex::all");
701 READUNLOCK(this, DB_MEMORY_LIMIT
, "ru db_mindex::all");
702 FATAL3("db_mindex::all: could not allocate space",
703 DB_MEMORY_LIMIT
, DB_MEMORY_LIMIT
);
708 ptr
= table
->first_entry(&where
);
710 answer
[0] = new_entry(ptr
);
712 WARNING("db_mindex::all: null first entry found in all");
715 for (i
= 1; i
< how_many
; i
++) {
716 ptr
= table
->next_entry(where
, &where
);
718 answer
[i
] = new_entry(ptr
);
721 "db_mindex::all: null internal entry found in all");
722 answer
[i
] = NULL
; /* Answer gets null too. -CM */
726 READUNLOCKNR(table
, lret
, "ru table db_mindex::all");
729 READUNLOCK(this, DB_SUCCESS
, "ru db_mindex::all");
734 * Remove the entry identified by 'recloc' from:
735 * 1. all indices, as obtained by extracting the index values from the entry
736 * 2. table where entry is stored.
739 db_mindex::remove_aux(entryp recloc
)
742 db_status res
= DB_SUCCESS
;
744 WRITELOCK(this, DB_LOCK_ERROR
, "w db_mindex::remove_aux");
745 /* get index values of this record */
746 db_query
* cq
= extract_index_values_from_record(recloc
);
748 WRITEUNLOCK(this, DB_MEMORY_LIMIT
, "wu db_mindex::remove_aux");
749 FATAL3("db_mindex::remove_aux: could not allocate space",
750 DB_MEMORY_LIMIT
, DB_MEMORY_LIMIT
);
752 if (cq
->size() != indices
.indices_len
) { /* something is wrong */
753 delete cq
; // clean up
755 "db_mindex::remove_aux: record contains wrong number of indices");
756 WRITEUNLOCK(this, DB_INTERNAL_ERROR
,
757 "wu db_mindex::remove_aux");
758 return (DB_INTERNAL_ERROR
);
761 if (!noWriteThrough
.flag
) {
763 entry_object
*e
= table
->get_entry(recloc
);
764 int queryRes
, doingModify
;
767 * If the removal is part of a modify operation, we
768 * defer the LDAP update until the modified NIS+ object
771 if (saveOldObjForModify((entry_obj
*)e
, &doingModify
) == 0)
772 res
= DB_INTERNAL_ERROR
;
774 if (res
== DB_SUCCESS
&& !doingModify
) {
776 * If we're removing a directory entry, and the
777 * entry is LDAP-mapped, but the directory isn't,
778 * we need a copy of the entry object in order
779 * to remove if from LDAP.
781 if (e
!= 0 && e
->en_type
!= 0 &&
782 strcmp(e
->en_type
, "IN_DIRECTORY") == 0)
783 o
= unmakePseudoEntryObj(e
, 0);
784 queryRes
= removeLDAP(cq
, o
);
785 if (queryRes
!= LDAP_SUCCESS
) {
786 if (table
->mapping
.storeErrorDisp
== abandon
)
787 res
= DB_INTERNAL_ERROR
;
790 nis_destroy_object(o
);
794 if (res
== DB_SUCCESS
) {
795 db_qcomp
* comps
= cq
->queryloc();
797 /* Add sanity check in case of corrupted table */
798 if (indices
.indices_val
!= NULL
) {
800 for (i
= 0; i
< indices
.indices_len
; i
++) {
801 /* unnec. if sorted */
802 curr_ind
= comps
[i
].which_index
;
803 indices
.indices_val
[curr_ind
].remove(
804 comps
[i
].index_value
, recloc
);
808 /* update table where record is stored */
809 table
->delete_entry(recloc
);
815 WRITEUNLOCK(this, DB_SUCCESS
, "wu db_mindex::remove_aux");
821 * Removes the entry in the table named by given query 'q'.
822 * If a NULL query is supplied, all entries in table are removed.
823 * Returns DB_NOTFOUND if no entry is found.
824 * Returns DB_SUCCESS if one entry is found; this entry is removed from
825 * its record storage, and it is also removed from all the indices of the
826 * table. If more than one entry satisfying 'q' is found, all are removed.
829 db_mindex::remove(db_query
*q
)
836 WRITELOCK(this, DB_LOCK_ERROR
, "w db_mindex::remove");
837 WRITELOCK2(table
, DB_LOCK_ERROR
, "w table db_mindex::remove", this);
838 if (q
== NULL
) { /* remove all entries in table */
839 if (table
->mapping
.toLDAP
&& !noWriteThrough
.flag
) {
840 int queryRes
= removeLDAP(q
, 0);
841 #ifdef NISDB_LDAP_DEBUG
842 if (queryRes
!= LDAP_SUCCESS
)
844 #endif /* NISDB_LDAP_DEBUG */
846 if (table
!= NULL
&& table
->getsize() > 0) {
848 WRITEUNLOCK2(table
, this, DB_SUCCESS
, DB_SUCCESS
,
849 "wu table db_mindex::remove",
850 "wu db_mindex::remove");
853 WRITEUNLOCK2(table
, this, DB_NOTFOUND
, DB_NOTFOUND
,
854 "wu table db_mindex::remove",
855 "wu db_mindex::remove");
856 return (DB_NOTFOUND
);
860 rp
= satisfy_query(q
, &count
, &valid_query
, FALSE
);
862 if (valid_query
!= TRUE
) {
863 WRITEUNLOCK2(table
, this, DB_BADQUERY
, DB_BADQUERY
,
864 "wu table db_mindex::remove", "wu db_mindex::remove");
865 return (DB_BADQUERY
);
868 if (count
== 0) { /* not found */
869 WRITEUNLOCK2(table
, this, DB_NOTFOUND
, DB_NOTFOUND
,
870 "wu table db_mindex::remove", "wu db_mindex::remove");
871 return (DB_NOTFOUND
);
872 } else if (count
== 1) { /* found, update indices */
875 s
= remove_aux(rp
->getlocation());
877 WRITEUNLOCK2(table
, this, s
, s
,
878 "wu table db_mindex::remove", "wu db_mindex::remove");
880 } else { /* ambiguous, remove all entries */
882 db_index_entry
*next_entry
;
883 for (i
= 0; i
< count
; i
++) {
886 "db_mindex::remove: incorrect number of indices");
887 WRITEUNLOCK2(table
, this, DB_INTERNAL_ERROR
,
889 "wu table db_mindex::remove",
890 "wu db_mindex::remove");
891 return (DB_INTERNAL_ERROR
);
894 next_entry
= rp
->getnextresult(); // save before removal
895 rstat
= remove_aux(rp
->getlocation());
896 if (rstat
!= DB_SUCCESS
) {
897 WRITEUNLOCK2(table
, this, rstat
, rstat
,
898 "wu table db_mindex::remove",
899 "wu db_mindex::remove");
902 rp
= next_entry
; // go on to next
904 WRITEUNLOCK2(table
, this, DB_SUCCESS
, DB_SUCCESS
,
905 "wu table db_mindex::remove", "wu db_mindex::remove");
911 * Add copy of given entry to table. Entry is identified by query 'q'.
912 * The entry (if any) satisfying the query is first deleted, then
913 * added to the indices (using index values extracted form the given entry)
915 * Returns DB_NOTUNIQUE if more than one entry satisfies the query.
916 * Returns DB_NOTFOUND if query is not well-formed.
917 * Returns DB_SUCCESS if entry can be added.
920 db_mindex::add(db_query
*q
, entry_object
* obj
)
925 db_index_entry
*rp
= NULL
;
927 const char *myself
= "db_mindex::add";
930 * The argument q is only NULL when we know that there are
931 * no objects in the database that match the object.
933 WRITELOCK(this, DB_LOCK_ERROR
, "w db_mindex::add");
934 WRITELOCK2(table
, DB_LOCK_ERROR
, "w table db_mindex::add", this);
936 rp
= satisfy_query(q
, &count
, &valid
, FALSE
);
938 WRITEUNLOCK2(this, table
, DB_LOCK_ERROR
, DB_LOCK_ERROR
,
940 "wu table db_mindex::add");
941 return (DB_BADQUERY
);
944 if (count
== 1) { /* found, first delete */
945 rstat
= remove_aux(rp
->getlocation());
946 if (rstat
!= DB_SUCCESS
) {
947 WRITEUNLOCK2(this, table
, rstat
, rstat
,
949 "wu table db_mindex::add");
952 count
= 0; /* fall through to add */
955 if (count
== 0) { /* not found, insert */
956 /* add object to table */
957 entryp recloc
= table
->add_entry(obj
, initialLoad
.flag
);
958 /* get index values of this object, might be same as 'q' */
959 db_query
*cq
= extract_index_values_from_object(obj
);
961 table
->delete_entry(recloc
);
962 WRITEUNLOCK2(this, table
,
963 DB_MEMORY_LIMIT
, DB_MEMORY_LIMIT
,
964 "wu db_mindex::add DB_MEMORY_LIMIT",
965 "wu table db_mindex::add DB_MEMORY_LIMIT");
966 FATAL3("db_mindex::add: could not allocate space for",
967 DB_MEMORY_LIMIT
, DB_MEMORY_LIMIT
);
969 if (cq
->size() != indices
.indices_len
) { /* something wrong */
970 table
->delete_entry(recloc
);
971 delete cq
; // clean up
973 "db_mindex::add: record contains wrong number of indices");
974 WRITEUNLOCK2(this, table
,
975 DB_INTERNAL_ERROR
, DB_INTERNAL_ERROR
,
976 "wu db_mindex::add DB_INTERNAL_ERROR",
977 "wu table db_mindex::add DB_INTERNAL_ERROR");
978 return (DB_INTERNAL_ERROR
);
980 db_qcomp
* comps
= cq
->queryloc();
983 if (indices
.indices_val
!= NULL
) {
984 for (i
= 0; i
< indices
.indices_len
; i
++) {
985 curr_ind
= comps
[i
].which_index
;
986 indices
.indices_val
[curr_ind
].add(
987 comps
[i
].index_value
, recloc
);
990 delete cq
; // clean up
991 if (!noWriteThrough
.flag
) {
995 if (retrieveOldObjForModify((entry_obj
**)&e
) == 0) {
996 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
997 "%s: Error retrieving old object for LDAP update",
999 return (DB_INTERNAL_ERROR
);
1002 queryRes
= storeLDAP(q
, obj
, 0, e
, 0);
1003 if (queryRes
!= LDAP_SUCCESS
) {
1004 if (table
->mapping
.storeErrorDisp
== abandon
) {
1005 WRITEUNLOCK2(this, table
,
1008 "wu db_mindex::add LDAP",
1009 "wu table db_mindex::add LDAP");
1010 return (DB_INTERNAL_ERROR
);
1012 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
1013 "%s: LDAP store failed: %s",
1015 ldap_err2string(queryRes
));
1020 } else /* ambiguous */
1021 rstat
= DB_NOTUNIQUE
;
1023 WRITEUNLOCK2(this, table
, rstat
, rstat
,
1024 "wu db_mindex::add",
1025 "wu table db_mindex::add");
1029 /* ************************* pickle_mindex ********************* */
1030 /* Does the actual writing to/from file specific for db_mindex structure. */
1032 transfer_aux(XDR
* x
, pptr rp
)
1034 return (xdr_db_mindex(x
, (db_mindex
*) rp
));
1037 class pickle_mindex
: public pickle_file
{
1039 pickle_mindex(char *f
, pickle_mode m
) : pickle_file(f
, m
) {}
1041 /* Transfers db_mindex structure pointed to by dp to/from file. */
1042 int transfer(db_mindex
* dp
)
1046 WRITELOCK(dp
, -1, "w pickle_mindex::transfer");
1047 ret
= pickle_file::transfer((pptr
) dp
, &transfer_aux
);
1048 WRITEUNLOCK(dp
, ret
, "wu pickle_mindex::transfer");
1053 /* Write this structure (table, indices, scheme) into the specified file. */
1055 db_mindex::dump(char *file
)
1057 pickle_mindex
f(file
, PICKLE_WRITE
);
1058 int status
= f
.transfer(this);
1061 return (-1); /* could not open for write */
1067 * Reset the table by: deleting all the indices, table of entries, and its
1073 WRITELOCKV(this, "w db_mindex::reset");
1074 reset_tables(); /* clear table contents first */
1076 if (indices
.indices_val
) {
1077 delete [] indices
.indices_val
;
1078 indices
.indices_val
= NULL
;
1080 if (table
) { delete table
; table
= NULL
; }
1081 if (scheme
) { delete scheme
; scheme
= NULL
; }
1082 indices
.indices_len
= 0;
1084 if (objPath
.ptr
!= 0) {
1088 WRITEUNLOCKV(this, "wu db_mindex::reset");
1092 * Initialize table using information from specified file.
1093 * The table is first 'reset', then the attempt to load from the file
1094 * is made. If the load failed, the table is again reset.
1095 * Therefore, the table will be modified regardless of the success of the
1096 * load. Returns 0 if successful, 1 if DB disk file couldn't be opened,
1097 * -1 for various other failures.
1100 db_mindex::load(char *file
)
1102 pickle_mindex
f(file
, PICKLE_READ
);
1104 int init_table
= (this->table
== NULL
);
1105 int init_scheme
= (this->scheme
== NULL
);
1107 WRITELOCK(this, -1, "w db_mindex::load");
1110 /* load new mindex */
1111 if ((status
= f
.transfer(this)) != 0) {
1112 /* load failed. Reset. */
1116 /* Initialize the 'scheme' locking */
1117 if (status
== 0 && this->scheme
!= 0 && init_scheme
) {
1119 * Since we've added fields to the db_scheme that aren't
1120 * read from disk, we need to re-allocate so that the
1121 * db_scheme instance is large enough.
1123 db_scheme
*tmpscheme
= new db_scheme();
1124 if (tmpscheme
!= 0) {
1125 (void) memcpy(tmpscheme
, this->scheme
,
1126 this->scheme
->oldstructsize());
1128 this->scheme
= tmpscheme
;
1134 * If the 'table' field was NULL before the load, but not now,
1135 * initialize the table locking and mapping.
1137 if (status
== 0 && this->table
!= 0 && init_table
) {
1139 * As for the db_scheme, make sure the db_table is large
1142 db_table
*tmptable
= new db_table();
1143 if (tmptable
!= 0) {
1144 (void) memcpy(tmptable
, this->table
,
1145 this->table
->oldstructsize());
1147 this->table
= tmptable
;
1148 (void) this->configure(file
);
1154 if (status
== 0 && this->indices
.indices_val
!= NULL
) {
1156 * Recreate the db_index instance so that it is
1157 * correctly initialized.
1159 db_index
*tmp_indices
;
1160 int n_index
= this->indices
.indices_len
;
1162 tmp_indices
= new db_index
[n_index
];
1163 if (tmp_indices
!= NULL
) {
1164 for (int i
= 0; i
< n_index
; i
++) {
1165 if (tmp_indices
[i
].move_xdr_db_index
1166 (&this->indices
.indices_val
[i
]) != DB_SUCCESS
) {
1171 free(this->indices
.indices_val
);
1172 this->indices
.indices_val
= tmp_indices
;
1173 this->indices
.indices_len
= n_index
;
1179 WRITEUNLOCK(this, status
, "wu db_mindex::load");
1184 * Prints statistics of the table. This includes the size of the table,
1185 * the number of entries, and the index sizes.
1188 db_mindex::print_stats()
1190 long size
, count
, i
;
1191 long *stats
= table
->stats(TRUE
);
1193 printf("table_size = %d\n", stats
[0]);
1194 printf("last_used = %d\n", stats
[1]);
1195 printf("count = %d\n", stats
[2]);
1196 printf("free list size = %d\n", stats
[3]);
1197 printf("free list count = %d\n", stats
[4]);
1199 for (i
= 5; i
< 5+stats
[4]; i
++) {
1200 printf("%d, ", stats
[i
]);
1203 free((char *)stats
);
1205 /* Add sanity check in case of corrupted table */
1206 if (indices
.indices_val
== NULL
) {
1207 printf("No indices to print\n");
1210 for (i
= 0; i
< indices
.indices_len
; i
++) {
1211 printf("***** INDEX %d ******\n", i
);
1212 indices
.indices_val
[i
].stats(&size
, &count
);
1213 printf("index table size = %d\ncount = %d\n", size
, count
);
1217 /* Prints statistics about all indices of table. */
1219 db_mindex::print_all_indices()
1223 READLOCKV(this, "r db_mindex::print_all_indices");
1224 /* Add sanity check in case of corrupted table */
1225 if (indices
.indices_val
== NULL
) {
1226 printf("No indices to print\n");
1227 READUNLOCKV(this, "ru db_mindex::print_all_indices");
1230 for (i
= 0; i
< indices
.indices_len
; i
++) {
1231 printf("***** INDEX %d ******\n", i
);
1232 indices
.indices_val
[i
].print();
1234 READUNLOCKV(this, "ru db_mindex::print_all_indices");
1237 /* Prints statistics about indices identified by 'n'. */
1239 db_mindex::print_index(int n
)
1241 READLOCKV(this, "r db_mindex::print_index");
1242 if (n
>= 0 && n
< indices
.indices_len
)
1243 indices
.indices_val
[n
].print();
1244 READUNLOCKV(this, "ru db_mindex::print_index");