db_mindex.cc revision 36e852a172cba914383d7341c988128b2c667fbd
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <malloc.h>
#include <strings.h>
#include <string.h>
#include "db_headers.h"
#include "db.h"
#include "db_mindex.h"
#include "db_pickle.h"
#include "nisdb_mt.h"
#include "nisdb_ldap.h"
#include "ldap_nisdbquery.h"
#include "ldap_map.h"
#include "ldap_ruleval.h"
#include "ldap_scheme.h"
#include "ldap_parse.h"
#include "nis_hashitem.h"
/*
* Constructor: Create new table using scheme defintion supplied.
* (Make copy of scheme and keep it with table.)
*/
{
noWriteThrough.flag = 0;
noLDAPquery.flag = 0;
initialLoad.flag = 0;
}
/* Constructor: Create empty table (no scheme, no table or indices). */
{
indices.indices_len = 0;
noWriteThrough.flag = 0;
noLDAPquery.flag = 0;
initialLoad.flag = 0;
}
{
reset(); /* get rid of data structures first */
}
/*
* Initialize table using information given in scheme 'how'.
* Record the scheme for later use (make copy of it);
* create the required number of indices; and create table for storing
* entries.
*/
void
{
FATAL("db_mindex::init: could not allocate space for scheme",
WARNING("db_mindex::init: empty scheme encountered");
/* what action should we take here? */
}
int i;
/* homogeneous indices for now */
delete scheme;
indices.indices_len = 0;
FATAL("db_mindex::init: could not allocate space for indices",
}
for (i = 0; i < indices.indices_len; i++) {
}
delete scheme;
delete indices.indices_val;
indices.indices_len = 0;
FATAL("db_mindex::init: could not allocate space for table",
}
}
/* empty associated tables associated */
void
{
int i;
WRITELOCKV(this, "w db_mindex::reset_tables");
/* Add sanity check in case of table corruption */
for (i = 0; i < indices.indices_len; i++) {
}
}
WRITEUNLOCKV(this, "wu db_mindex::reset_tables");
}
/*
* Return a list of index_entries that satsify the given query 'q'.
* Return the size of the list in 'count'. Return NULL if list is empty.
* Return in 'valid' FALSE if query is not well formed.
*/
}
int queryRes;
/* Make sure we have somewhere to store the "request valid" status */
valid = &validRequest;
/* Prepare for a failed lock */
*count = 0;
/*
* Only get data from LDAP if the caller requested it,
* and if we're mapping for this table.
*/
/*
* If we always fetch data from LDAP for query's, then do so now,
* before invoking the "real" satisfy_query().
*/
int lockcode = 0;
"r db_mindex::satisfy_query table");
if (lockcode != 0) {
return (NULL);
}
"ru db_mindex::satisfy_query table");
if (lockcode != 0) {
return (NULL);
}
if (queryRes != LDAP_SUCCESS) {
/* queryLDAP() sets error codes etc. */
return (NULL);
}
}
/* If we found it, or if we're not mapping, return */
return (ret);
/* No result, and the request wasn't valid */
return (NULL);
}
/* Get data from LDAP */
} else {
/*
* We'll now go on to check for an un-expired entry again,
* even though we're pretty sure that won't work (already
* did that, and nothing's changed). However, we accept that
* slight inefficiency in the interest of keeping the code
* simple; we expect 'mat_never' to be used very rarely.
*/
}
if (queryRes == LDAP_SUCCESS) {
/*
* Check if we've got a match now. If not, try one
* last time for an expired match.
*/
}
} else {
/*
* Check if we have an expired entry; if so, return
* it with an appropriate status.
*/
}
return (ret);
}
{
int i, curr_ind;
/* Add sanity check in case table corrupted */
WARNING("db_mindex::satisfy_query: table has no indices");
*count = 0;
return (NULL);
}
for (i = 0; i < limit; i++) {
table, checkExpire);
*count = 0;
return (NULL);
}
} else {
*count = 0;
return (NULL);
}
}
} else {
WARNING("db_mindex::satisfy_query: index out of range");
*count = 0;
return (NULL);
}
}
return (oldres);
}
/*
* Returns an array of size 'count' of 'entry_object_p's, pointing to
* copies of entry_objects named by the result list of db_index_entries 'res'.
* Sets db_status 'statp' if error encountered; otherwise, leaves it unchanged.
*/
{
int i;
"ru db_mindex::prepare_results: could not allocate space",
"ru table db_mindex::prepare_results: could not allocate space");
FATAL3("db_mindex::prepare_results: could not allocate space",
}
for (i = 0; i < count; i++) {
int j;
for (j = 0; j < i; j++) // cleanup
free_entry(entries[j]);
"db_mindex::prepare_results: incorrect count");
} else {
entries[i] =
}
}
"ru db_mindex::prepare_results",
"ru db_mindex::prepare_results");
return (entries);
}
/*
* Returns a newly created db_query structure containing the index values
* as obtained from the record named by 'recnum'. The record itself, along
* with information on the schema definition of this table, will determine
* which values are extracted from the record and placed into the result.
* Returns NULL if recnum is not a valid entry.
* Note that space is allocated for the query and the index values
* (i.e. do not share pointers with strings in 'obj'.)
*/
db_query *
{
return (ret);
}
/*
* Returns a newly created db_query containing the index values as
* obtained from the given object. The object itself,
* along with information on the scheme given, will determine
* which values are extracted from the object and placed into the query.
* Returns an empty query if 'obj' is not a valid entry.
* Note that space is allocated for the query and the index values
* (i.e. do not share pointers with strings in 'obj'.)
*/
db_query *
{
"number of keys (%d) does not equal number of indices (%d)",
READUNLOCK(this, NULL,
"ru db_mindex::extract_index_values_from_object");
return (new db_query()); // null query
READUNLOCK(this, NULL,
"ru db_mindex::extract_index_values_from_object");
return (NULL);
} else {
if (answer) {
/*
* XXX If the unlock fails, and we return NULL,
* we leak 'answer'. On the other hand, if we
* return 'answer', the object may remain locked,
* but the caller doesn't know that anything
* went wrong.
*/
READUNLOCK(this, NULL,
"ru db_mindex::extract_index_values_from_object");
return (answer);
} else {
FATAL3("db_mindex::extract: could not allocate space",
}
}
READUNLOCK(this, NULL,
"ru db_mindex::extract_index_values_from_object");
return (NULL);
}
/*
* Returns the first entry found in the table by setting 'answer' to
* point to the a copy of entry_object. Returns DB_SUCCESS if found;
* DB_NOTFOUND otherwise.
*/
{
/*
* table->first_entry() returns a pointer into the table, so
* we must keep the table read locked until we've copied the
* entry_object. In order to maintain lock integrity, we must
* lock the db_mindex (this) before the db_table (table).
*/
if (queryRes == LDAP_SUCCESS)
else {
READUNLOCK2(this, table,
"ru db_mindex::first LDAP",
"ru table db_mindex::first LDAP");
return (DB_INTERNAL_ERROR);
}
}
}
ret = DB_NOTFOUND;
else
"ru db_mindex::first", "ru table db_mindex::first");
return (ret);
}
/*
* Returns the next entry in the table after 'previous' by setting 'answer' to
* point to copy of the entry_object. Returns DB_SUCCESS if 'previous' is
* valid and next entry is found; DB_NOTFOUND otherwise. Sets 'where' to
* location of where entry is found for input as subsequent 'next' operation.
*/
{
ret = DB_NOTFOUND;
else {
ret = DB_NOTFOUND;
else
}
"ru db_mindex::next", "ru table db_mindex::next");
return (ret);
}
static void
{
delete curr;
}
}
static db_next_index_desc *
{
"db_mindex::copy_result_list: could not allocate space",
}
}
return (head);
}
/*
* Delete the given list of results; used when no longer interested in
*/
{
return (DB_NOTFOUND);
return (DB_SUCCESS);
}
/*
* Finds entry that satisfy the query 'q'. Returns the first answer by
* setting the pointer 'answer' to point to a copy of it. 'where' is set
* so that the other answers could be gotten by passing 'where' to 'next'
* successively. Note that the answer is a pointer to a copy of the entry.
* Returns DB_SUCCESS if search was successful; DB_NOTFOUND otherwise.
*/
{
long count;
if (valid_query != TRUE)
ret = DB_BADQUERY;
ret = DB_NOTFOUND;
} else {
ret = DB_NOTFOUND;
else
}
"ru db_mindex::first", "ru table db_mindex::first");
return (ret);
}
/*
* Returns the next entry in the table after 'previous' by setting 'answer' to
* point to copy of the entry_object. Next is next in chain of answers found
* in previous first search with query. Returns DB_SUCCESS if 'previous' is
* valid and next entry is found; DB_NOTFOUND otherwise. Sets 'where' to
* location of where entry is found for input as subsequent 'next' operation.
*/
{
ret = DB_NOTFOUND;
else {
// should further check validity of 'previous' pointer
delete previous; // delete previous entry
ret = DB_NOTFOUND;
else {
entry_object * ptr =
ret = DB_NOTFOUND;
else {
ret = DB_SUCCESS;
}
}
}
"ru db_mindex::next", "ru table db_mindex::next");
return (ret);
}
/*
* Finds entry that satisfy the query 'q'. Returns the answer by
* setting the pointer 'rp' to point to the list of answers.
* Note that the answers are pointers to the COPIES of entries.
* Returns the number of answers find in 'count'.
* Returns DB_SUCCESS if search found at least one answer;
* returns DB_NOTFOUND if none is found.
*/
{
if (valid_query != TRUE)
return (DB_BADQUERY);
return (DB_NOTFOUND);
}
return (stat);
}
/*
* Return all entries within table. Returns the answer by
* setting the pointer 'rp' to point to the list of answers.
* Note that the answers are pointers to copies of the entries.
* Returns the number of answers find in 'count'.
* Returns DB_SUCCESS if search found at least one answer;
* returns DB_NOTFOUND if none is found.
*/
{
long how_many, i;
int lret = 0;
return (DB_NOTFOUND);
}
/* Read lock 'table' while we're traversing it */
if (lret != 0) {
return (DB_LOCK_ERROR);
}
if (queryRes != LDAP_SUCCESS) {
"ru table db_mindex::all LDAP");
READUNLOCK(this, DB_LOCK_ERROR,
"ru db_mindex::all LDAP");
return (DB_INTERNAL_ERROR);
}
}
}
/*
* Set '*count' so that the caller avoids putting garbage
* in an 'objects_len' field.
*/
*count = 0;
return (DB_NOTFOUND);
}
FATAL3("db_mindex::all: could not allocate space",
}
else {
WARNING("db_mindex::all: null first entry found in all");
}
for (i = 1; i < how_many; i++) {
else {
"db_mindex::all: null internal entry found in all");
}
}
return (DB_SUCCESS);
}
/*
* Remove the entry identified by 'recloc' from:
* 1. all indices, as obtained by extracting the index values from the entry
* 2. table where entry is stored.
*/
{
int i, curr_ind;
/* get index values of this record */
FATAL3("db_mindex::remove_aux: could not allocate space",
}
delete cq; // clean up
"db_mindex::remove_aux: record contains wrong number of indices");
WRITEUNLOCK(this, DB_INTERNAL_ERROR,
"wu db_mindex::remove_aux");
return (DB_INTERNAL_ERROR);
}
if (!noWriteThrough.flag) {
nis_object *o = 0;
int queryRes, doingModify;
/*
* If the removal is part of a modify operation, we
* defer the LDAP update until the modified NIS+ object
* is added back.
*/
/*
* If we're removing a directory entry, and the
* entry is LDAP-mapped, but the directory isn't,
* we need a copy of the entry object in order
* to remove if from LDAP.
*/
if (e != 0 && e->en_type != 0 &&
o = unmakePseudoEntryObj(e, 0);
if (queryRes != LDAP_SUCCESS) {
}
if (o != 0)
}
}
if (res == DB_SUCCESS) {
/* Add sanity check in case of corrupted table */
/* update indices */
for (i = 0; i < indices.indices_len; i++) {
/* unnec. if sorted */
}
}
/* update table where record is stored */
}
/* delete query */
delete cq;
return (res);
}
/*
* Removes the entry in the table named by given query 'q'.
* If a NULL query is supplied, all entries in table are removed.
* Returns DB_NOTFOUND if no entry is found.
* Returns DB_SUCCESS if one entry is found; this entry is removed from
* its record storage, and it is also removed from all the indices of the
* table. If more than one entry satisfying 'q' is found, all are removed.
*/
{
long count = 0;
if (q == NULL) { /* remove all entries in table */
int queryRes = removeLDAP(q, 0);
#ifdef NISDB_LDAP_DEBUG
if (queryRes != LDAP_SUCCESS)
abort();
#endif /* NISDB_LDAP_DEBUG */
}
reset_tables();
"wu table db_mindex::remove",
"wu db_mindex::remove");
return (DB_SUCCESS);
} else {
"wu table db_mindex::remove",
"wu db_mindex::remove");
return (DB_NOTFOUND);
}
}
if (valid_query != TRUE) {
"wu table db_mindex::remove", "wu db_mindex::remove");
return (DB_BADQUERY);
}
if (count == 0) { /* not found */
"wu table db_mindex::remove", "wu db_mindex::remove");
return (DB_NOTFOUND);
db_status s;
WRITEUNLOCK2(table, this, s, s,
"wu table db_mindex::remove", "wu db_mindex::remove");
return (s);
} else { /* ambiguous, remove all entries */
int i;
for (i = 0; i < count; i++) {
"db_mindex::remove: incorrect number of indices");
"wu table db_mindex::remove",
"wu db_mindex::remove");
return (DB_INTERNAL_ERROR);
}
if (rstat != DB_SUCCESS) {
"wu table db_mindex::remove",
"wu db_mindex::remove");
return (rstat);
}
}
"wu table db_mindex::remove", "wu db_mindex::remove");
return (DB_SUCCESS);
}
}
/*
* Add copy of given entry to table. Entry is identified by query 'q'.
* The entry (if any) satisfying the query is first deleted, then
* added to the indices (using index values extracted form the given entry)
* and the table.
* Returns DB_NOTUNIQUE if more than one entry satisfies the query.
* Returns DB_NOTFOUND if query is not well-formed.
* Returns DB_SUCCESS if entry can be added.
*/
{
long count = 0;
int i, curr_ind;
char *myself = "db_mindex::add";
/*
* The argument q is only NULL when we know that there are
* no objects in the database that match the object.
*/
if (q) {
if (!valid) {
"wu db_mindex::add",
"wu table db_mindex::add");
return (DB_BADQUERY);
}
}
if (rstat != DB_SUCCESS) {
"wu db_mindex::add",
"wu table db_mindex::add");
return (rstat);
}
count = 0; /* fall through to add */
}
if (count == 0) { /* not found, insert */
/* add object to table */
/* get index values of this object, might be same as 'q' */
WRITEUNLOCK2(this, table,
"wu db_mindex::add DB_MEMORY_LIMIT",
"wu table db_mindex::add DB_MEMORY_LIMIT");
FATAL3("db_mindex::add: could not allocate space for",
}
delete cq; // clean up
"db_mindex::add: record contains wrong number of indices");
WRITEUNLOCK2(this, table,
"wu db_mindex::add DB_INTERNAL_ERROR",
"wu table db_mindex::add DB_INTERNAL_ERROR");
return (DB_INTERNAL_ERROR);
}
/* update indices */
for (i = 0; i < indices.indices_len; i++) {
}
}
delete cq; // clean up
if (!noWriteThrough.flag) {
int queryRes;
entry_object *e = 0;
if (retrieveOldObjForModify((entry_obj **)&e) == 0) {
"%s: Error retrieving old object for LDAP update",
myself);
return (DB_INTERNAL_ERROR);
}
if (queryRes != LDAP_SUCCESS) {
WRITEUNLOCK2(this, table,
"wu db_mindex::add LDAP",
"wu table db_mindex::add LDAP");
return (DB_INTERNAL_ERROR);
} else {
"%s: LDAP store failed: %s",
}
}
}
rstat = DB_SUCCESS;
} else /* ambiguous */
"wu db_mindex::add",
"wu table db_mindex::add");
return (rstat);
}
/* ************************* pickle_mindex ********************* */
static bool_t
{
}
class pickle_mindex: public pickle_file {
public:
{
int ret;
return (ret);
}
};
/* Write this structure (table, indices, scheme) into the specified file. */
int
{
if (status == 1)
return (-1); /* could not open for write */
else
return (status);
}
/*
* Reset the table by: deleting all the indices, table of entries, and its
* scheme.
*/
void
{
WRITELOCKV(this, "w db_mindex::reset");
reset_tables(); /* clear table contents first */
if (indices.indices_val) {
delete [] indices.indices_val;
}
indices.indices_len = 0;
}
WRITEUNLOCKV(this, "wu db_mindex::reset");
}
/*
* Initialize table using information from specified file.
* The table is first 'reset', then the attempt to load from the file
* is made. If the load failed, the table is again reset.
* Therefore, the table will be modified regardless of the success of the
* load. Returns 0 if successful, 1 if DB disk file couldn't be opened,
* -1 for various other failures.
*/
int
{
int status;
reset();
/* load new mindex */
/* load failed. Reset. */
reset();
}
/* Initialize the 'scheme' locking */
/*
* Since we've added fields to the db_scheme that aren't
* read from disk, we need to re-allocate so that the
* db_scheme instance is large enough.
*/
if (tmpscheme != 0) {
this->scheme->oldstructsize());
} else {
status = -1;
}
}
/*
* If the 'table' field was NULL before the load, but not now,
* initialize the table locking and mapping.
*/
/*
* As for the db_scheme, make sure the db_table is large
* enough.
*/
if (tmptable != 0) {
this->table->oldstructsize());
} else {
status = -1;
}
}
/*
* Recreate the db_index instance so that it is
* correctly initialized.
*/
if (tmp_indices != NULL) {
for (int i = 0; i < n_index; i++) {
if (tmp_indices[i].move_xdr_db_index
status = -1;
break;
}
}
} else {
status = -1;
}
}
return (status);
}
/*
* Prints statistics of the table. This includes the size of the table,
* the number of entries, and the index sizes.
*/
void
{
}
printf("\n");
/* Add sanity check in case of corrupted table */
printf("No indices to print\n");
return;
}
for (i = 0; i < indices.indices_len; i++) {
printf("***** INDEX %d ******\n", i);
}
}
/* Prints statistics about all indices of table. */
void
{
int i;
READLOCKV(this, "r db_mindex::print_all_indices");
/* Add sanity check in case of corrupted table */
printf("No indices to print\n");
READUNLOCKV(this, "ru db_mindex::print_all_indices");
return;
}
for (i = 0; i < indices.indices_len; i++) {
printf("***** INDEX %d ******\n", i);
}
READUNLOCKV(this, "ru db_mindex::print_all_indices");
}
/* Prints statistics about indices identified by 'n'. */
void
db_mindex::print_index(int n)
{
READLOCKV(this, "r db_mindex::print_index");
if (n >= 0 && n < indices.indices_len)
READUNLOCKV(this, "ru db_mindex::print_index");
}