/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
*/
/*
* db.cc
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <string.h>
#ifdef TDRPC
#include <sysent.h>
#else
#include <unistd.h>
#endif
#include "nisdb_mt.h"
#include "db_headers.h"
#include "db.h"
extern int add_to_standby_list(db*);
extern int remove_from_standby_list(db*);
/* for db_next_desc */
struct db_next_info {
/* chained: db_next_index_desc* */
};
/* Constructor: Create a database using the given name, 'dbname.'
The database is stored in a file named 'dbname'.
The log file is stored in a file named 'dbname'.log.
A temporary file 'dbname'.tmp is also used. */
{
if (dbfilename == NULL)
if (logfilename == NULL) {
delete dbfilename;
}
if (tmpfilename == NULL) {
delete dbfilename;
delete logfilename;
}
READLOCKOK(db);
internal_db.setDbPtr(this);
}
/* destructor: note that associated files should be removed separated */
{
(void)acqexcl();
delete dbfilename;
delete logfilename;
delete tmpfilename;
close_log();
delete logfile;
(void)destroylock();
}
static void
{
desc->db_next_desc_len = 0;
FATAL("db::assign_next_desc: cannot allocate space",
}
}
static void
{
desc->db_next_desc_len = 0;
FATAL("db::assign_next_desc: cannot allocate space (2)",
}
}
static entryp
{
*next_type = 0;
return (0);
}
switch (*next_type) {
case LINEAR:
return (place);
case CHAINED:
*place2 = (db_next_index_desc*)
return (0);
default:
*next_type = 0; // invalid type
return (0);
}
}
/* Execute the specified action using the rest of the arguments as input.
Return a structure db_result containing the result. */
{
long num_answers;
int next_type;
FATAL3("db::exec_action: cannot allocate space for result",
switch (action) {
case DB_LOOKUP:
break;
case DB_ADD:
break;
case DB_REMOVE:
break;
case DB_FIRST:
} else {
&single);
}
delete res;
"db::exec_action: cannot allocate space for DB_FIRST result",
}
}
break;
case DB_NEXT:
switch (next_type) {
case LINEAR:
if (prev != 0) {
&single);
where);
} else
// invalid previous indicator
break;
case CHAINED:
&index_desc, &single);
} else
// invalid previous indicator
break;
default:
WARNING("db::exec_action: invalid previous indicator");
}
delete previous->db_next_desc_val;
previous->db_next_desc_len = 0;
}
delete res;
"db::exec_action: cannot allocate space for DB_NEXT result",
}
}
break;
case DB_RESET_NEXT:
switch (next_type) {
case LINEAR:
if (previous->db_next_desc_val) {
delete previous->db_next_desc_val;
previous->db_next_desc_len = 0;
}
break; // do nothing
case CHAINED:
if (previous->db_next_desc_val) {
delete previous->db_next_desc_val;
previous->db_next_desc_len = 0;
}
break;
default:
WARNING("db::exec_action: invalid previous indicator");
}
break;
case DB_ALL:
break;
default:
WARNING("unknown request");
return (res);
}
return (res);
}
/*
* Log the given action and execute it.
* The minor version of the database is updated after the action has
* been executed and the database is flagged as being changed.
* Return the structure db_result, or NULL if the logging failed or the
* action is unknown.
*/
{
/*
* If this is a synchronous operation on the master we should
* not copy the log for each operation. Doing so causes
* massive disk IO that hampers the performance of these operations.
* Where as on the replica these operations are not synchronous
* (batched) and don't affect the performance as much.
*/
delete v;
"wu db::log_action DB_STORAGE_LIMIT");
return (empty_result(DB_STORAGE_LIMIT));
}
close_log();
WARNING_M("db::log_action: could not add log entry: ");
delete v;
"wu db::log_action DB_STORAGE_LIMIT");
return (empty_result(DB_STORAGE_LIMIT));
}
switch (action) {
case DB_ADD_NOSYNC:
break;
case DB_REMOVE_NOSYNC:
break;
default:
close_log();
WARNING_M("db::log_action: could not add log entry: ");
delete v;
"wu db::log_action DB_STORAGE_LIMIT");
return (empty_result(DB_STORAGE_LIMIT));
}
break;
}
delete v;
return (res);
}
/*
* Execute 'action' using the rest of the arguments as input.
* Return the result of the operation in a db_result structure;
* Return NULL if the request is unknown.
* If the action involves updates (ADD and REMOVE), it is logged first.
*/
{
switch (action) {
case DB_LOOKUP:
case DB_FIRST:
case DB_NEXT:
case DB_ALL:
case DB_RESET_NEXT:
"ru db::execute");
return (res);
case DB_ADD_NOLOG:
"wu db::execute");
return (res);
case DB_ADD:
case DB_REMOVE:
case DB_ADD_NOSYNC:
case DB_REMOVE_NOSYNC:
/* log_action() will do the locking */
default:
WARNING("db::execute: unknown request");
return (empty_result(DB_INTERNAL_ERROR));
}
}
/* close existing logfile and delete its structure */
int
{
/* try to close old log file */
/* doesnot matter since we do synchronous writes only */
if (logfile_opened == TRUE) {
WARNING_M("db::reset_log: could not close log file: ");
}
remove_from_standby_list(this);
}
delete logfile;
}
return (0);
}
/* close existing logfile, but leave its structure if exists */
int
{
if (!bypass_standby)
remove_from_standby_list(this);
}
return (0);
}
/* open logfile, creating its structure if it does not exist */
int
{
== NULL)
FATAL3("db::reset_log: cannot allocate space",
DB_MEMORY_LIMIT, -1);
}
if (logfile_opened == TRUE) {
return (0);
}
WARNING_M("db::open_log: could not open log file: ");
delete logfile;
return (-1);
}
add_to_standby_list(this);
return (0);
}
/*
* Execute log entry 'j' on the database identified by 'dbchar' if the
* version of j is later than that of the database. If 'j' is executed,
* 'count' is incremented and the database's verison is updated to that of 'j'.
* Returns TRUE always for valid log entries; FALSE otherwise.
*/
static bool_t
{
++ *count;
#ifdef DEBUG
j->print();
#endif /* DEBUG */
switch (j->get_action()) {
case DB_ADD:
case DB_ADD_NOSYNC:
break;
case DB_REMOVE:
case DB_REMOVE_NOSYNC:
break;
default:
WARNING("db::apply_log_entry: unknown action_type");
return (FALSE);
}
}
return (TRUE); /* always want to TRUE if action valid ? */
}
/*
* Execute log entry 'j' on this db. 'j' is executed if its version is
* later than that of the database; if executed, the database's version
* will be changed to that of 'j', regardless of the status of the operation.
* Returns TRUE if 'j' was executed; FALSE if it was not.
* Log entry is added to this database's log if log_entry is applied.
*/
{
int count = 0;
/*
* If this is a synchronous operation on the master we should
* not copy the log for each operation. Doing so causes
* massive disk IO that hampers the performance of these operations.
* Where as on the replica these operations are not synchronous
* (batched) and don't affect the performance as much.
*/
action = j->get_action();
/*
* should really record the log entry first, but can''t do that without
* knowing whether the log entry is applicable.
*/
if (count == 1) {
return (FALSE);
}
close_log();
"db::execute_log_entry: could not add log entry: ");
return (FALSE);
}
// close_log(); /* do this asynchronously */
}
return (count == 1);
}
/* Incorporate updates in log to database already loaded.
Does not affect "logfile" */
int
{
int ret;
this);
"wu db::incorporate_log",
"wu mindex db::incorporate_log");
return (ret);
}
/* Load database and incorporate any logged updates into the loaded copy.
Return TRUE if load succeeds; FALSE otherwise. */
{
int count;
int load_status;
"WARNING: the current db '%s' has been changed but not checkpointed",
if (load_status < 0)
/* otherwise, there was just nothing to load */
return (FALSE);
}
reset_log();
WRITEUNLOCK2(this, (&internal_db),
"wu db::load", "wu internal_db db::load");
return (TRUE);
}
/*
* Initialize the database using table scheme 's'.
* Because the 'scheme' must be 'remembered' between restarts,
* after the initialization, the empty database is checkpointed to record
* the scheme. Returns TRUE if initialization succeeds; FALSE otherwise.
*/
{
internal_db.init(s);
if (internal_db.good()) {
reset_log();
ret = checkpoint();
}
return (ret);
}
/*
Write out in-memory copy of database to file.
1. Update major version.
2. Dump contents to temporary file.
3. Rename temporary file to real database file.
4. Remove log file.
A checkpoint is done only if it has changed since the previous checkpoint.
Returns TRUE if checkpoint was successful; FALSE otherwise.
*/
{
return (TRUE);
}
WARNING_M("db::checkpoint: could not dump database: ");
delete nextversion;
delete oldversion;
return (FALSE);
}
"db::checkpoint: could not rename temp file to db file: ");
delete nextversion;
delete oldversion;
return (FALSE);
}
reset_log(); /* should check for what? */
delete nextversion;
delete oldversion;
return (TRUE);
}
/* For generating log_list */
struct traverse_info {
};
/*
* For the given entry determine, if it is later than the version supplied,
* 1. increment 'count'.
* 2. add the entry to the list of log entries found.
*
* Since traversal happens on an automatic (struct traverse_info) in
* db::get_log_entries_since(), no locking is necessary.
*/
{
++ *count;
// j->print(); // debug
else {
}
}
return (TRUE);
}
/* Return structure db_log_list containing entries that are later
than the version 'v' given. */
{
int count;
FATAL3("db::get_log_entries_since: cannot allocate space",
if (count > 0) {
int i;
delete answer;
"db::get_log_entries_since: cannot allocate space for entries",
}
i++) {
}
} else
return (answer);
}
/* Delete all files associated with database. */
int
{
reset_log();
return (0);
}
if (logfile == 0) {
ret = DB_BADTABLE;
} else {
ret = DB_SUCCESS;
else
}
return (ret);
}
/* Pass configuration information to the db_mindex */
}
return (&internal_db);
}