/*
 * Copyright (C) 2008 Search Solution Corporation. All rights reserved by Search Solution.
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; version 2 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

/*
 * db_class.c - API functions for schema definition.
 */

#ident "$Id$"

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>

#include "system_parameter.h"
#include "storage_common.h"
#include "db.h"
#include "class_object.h"
#include "object_print.h"
#include "server_interface.h"
#include "boot_cl.h"
#include "locator_cl.h"
#include "schema_manager.h"
#include "schema_template.h"
#include "object_accessor.h"
#include "set_object.h"
#include "virtual_object.h"
#include "parser.h"
#include "execute_schema.h"

/* Error signaling macros */
static int drop_internal (MOP class_, const char *name,
			  SM_NAME_SPACE name_space);

static int add_method_internal (MOP class_, const char *name,
				const char *implementation,
				SM_NAME_SPACE name_space);

static int add_arg_domain (DB_OBJECT * class_, const char *name,
			   int class_method, int index,
			   int initial_domain, const char *domain);
/*
 * CLASS DEFINITION
 */

/*
 * db_create_class() - This function creates a new class.
 *    Returns NULL on error with error status left in global error.
 *    The most common reason for returning NULL was that a class with
 *    the given name could not be found.
 * return  : new class object pointer.
 * name(in): the name of a class
 */
DB_OBJECT *
db_create_class (const char *name)
{
  SM_TEMPLATE *def;
  MOP class_;

  CHECK_CONNECT_NULL ();
  CHECK_1ARG_NULL (name);
  CHECK_MODIFICATION_NULL ();

  class_ = NULL;
  def = smt_def_class (name);
  if (def != NULL)
    {
      if (sm_update_class (def, &class_) != NO_ERROR)
	{
	  smt_quit (def);
	}
    }
  return (class_);
}

/*
 * db_drop_class() - This function is used to completely remove a class and
 *    all of its instances from the database.  Obviously this should be used
 *    with care. Returns non-zero error status if the operation could not be
 *    performed. The most common reason for error is that the current user was
 *    not authorized to delete the specified class.
 * return   : error code
 * class(in): class object
 */
int
db_drop_class (MOP class_)
{
  int retval;

  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (class_);
  CHECK_MODIFICATION_ERROR ();

  retval = do_check_partitioned_class (class_, CHECK_PARTITION_SUBS, NULL);

  if (!retval)
    {
      retval = sm_delete_class_mop (class_);
    }

  return (retval);
}

/*
 * db_rename_class() - This function changes the name of a class in the
 *    database. Returns non-zero error if the operation could not be
 *    performed. The most common reason for rename failure is that another
 *    class with the desired name existed.  The current user may also not
 *    have ALTER class authorization.
 * return : error code
 * classop(in/out): class object
 * new_name(in)   : the new name
 */
int
db_rename_class (MOP classop, const char *new_name)
{
  int retval;
  char *old_name = NULL;

  CHECK_CONNECT_ERROR ();
  CHECK_2ARGS_ERROR (classop, new_name);
  CHECK_MODIFICATION_ERROR ();

  retval = do_check_partitioned_class (classop, CHECK_PARTITION_SUBS, NULL);
  if (!retval)
    {
      retval = sm_rename_class (classop, new_name);
    }

  if (old_name)
    {
      free_and_init (old_name);
    }

  return (retval);
}

/*
 * ATTRIBUTES
 */

/*
 * db_add_attribute_internal() - This is a generic work function for adding
 *    attributes of the various types.  It saves redundant error checking code
 *    in each of the type specific attribute routines.
 * return : error code
 * class(in/out) : class object
 * name(in)      : attribute name
 * domain(in)    : domain string
 * default_value(in): default_value
 * namespace(in) : namespace identifier
 */
int
db_add_attribute_internal (MOP class_, const char *name, const char *domain,
			   DB_VALUE * default_value, SM_NAME_SPACE name_space)
{
  int error = NO_ERROR;
  SM_TEMPLATE *def;
  MOP newmop;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();
  CHECK_3ARGS_RETURN_EXPR (class_, name, domain, ER_OBJ_INVALID_ARGUMENTS);

  def = smt_edit_class_mop (class_);
  if (def == NULL)
    {
      error = er_errid ();
    }
  else
    {
      error = smt_add_attribute_any (def, name, domain, (DB_DOMAIN *) 0,
				     name_space);
      if (error)
	{
	  smt_quit (def);
	}
      else
	{
	  if (default_value != NULL)
	    {
	      if (name_space == ID_CLASS || name_space == ID_CLASS_ATTRIBUTE)
		{
		  error =
		    smt_set_attribute_default (def, name, 1, default_value);
		}
	      else
		{
		  error =
		    smt_set_attribute_default (def, name, 0, default_value);
		}
	    }
	  if (error)
	    {
	      smt_quit (def);
	    }
	  else
	    {
	      error = sm_update_class_auto (def, &newmop);
	      if (error)
		{
		  smt_quit (def);
		}
	    }
	}
    }
  return (error);
}

/*
 * db_add_attribute() - This function adds a normal attribute to a class
 *                      definition.
 * return : error code
 * obj(in/out): class or instance (usually a class)
 * name(in)   : attribute name
 * domain(in) : domain specifier
 * default_value(in): optional default value
 */
int
db_add_attribute (MOP obj, const char *name, const char *domain,
		  DB_VALUE * default_value)
{
  int retval = 0;

  retval =
    (db_add_attribute_internal
     (obj, name, domain, default_value, ID_ATTRIBUTE));

  return (retval);
}

/*
 * db_add_shared_attribute() - This function adds a shared attribute to a class
 *                             definition.
 * return : error code
 * obj(in)    : class or instance (usually a class)
 * name(in)   : attribute name
 * domain(in) : domain specifier
 * default_value(in): optional default value
 *
 */
int
db_add_shared_attribute (MOP obj, const char *name, const char *domain,
			 DB_VALUE * default_value)
{
  int retval;

  retval =
    (db_add_attribute_internal
     (obj, name, domain, default_value, ID_SHARED_ATTRIBUTE));

  return (retval);
}

/*
 * db_add_class_attribute() - This function adds a class attribute to a class
 *                            definition.
 * return : error code
 * obj(in): class or instance (usually a class)
 * name(in): attribute name
 * domain(in): domain specifier
 * default_value(in): optional default value
 *
 */
int
db_add_class_attribute (MOP obj, const char *name,
			const char *domain, DB_VALUE * default_value)
{
  int retval;

  retval =
    (db_add_attribute_internal
     (obj, name, domain, default_value, ID_CLASS_ATTRIBUTE));

  return (retval);
}

/*
 * drop_attribute_internal() - This is internal work function for removing
 *    definitions from a class. Can be used to remove any type of attribute
 *    or method. The db_ function layer currently forces the callers to
 *    recognize the difference between normal/shared/class attributes and
 *    normal/class methods when calling the drop routines.  The interpreter
 *    doesn't have this restriction since the smt_ layer offers an interface
 *    similar to this one. Consider offering the same thing at the db_ layer.
 * return : error code
 * class(in)    : class or instance
 * name(in)     : attribute or method name
 * namespace(in): namespace identifier
 */
static int
drop_internal (MOP class_, const char *name, SM_NAME_SPACE name_space)
{
  int error = NO_ERROR;
  SM_TEMPLATE *def;
  MOP newmop;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();
  CHECK_2ARGS_RETURN_EXPR (class_, name, ER_OBJ_INVALID_ARGUMENTS);

  def = smt_edit_class_mop (class_);
  if (def == NULL)
    {
      error = er_errid ();
    }
  else
    {
      error = smt_delete_any (def, name, name_space);
      if (error)
	{
	  smt_quit (def);
	}
      else
	{
	  error = sm_update_class_auto (def, &newmop);
	  if (error)
	    {
	      smt_quit (def);
	    }
	}
    }

  return (error);
}

/*
 * db_drop_attribute() - see the function db_drop_attribute_internal()
 * return : error code
 * class(in): class or instance
 * name(in) : attribute name
 */
int
db_drop_attribute (MOP class_, const char *name)
{
  int error = NO_ERROR;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();
  CHECK_2ARGS_ERROR (class_, name);

  error = do_check_partitioned_class (class_,
				      CHECK_PARTITION_SUBS, (char *) name);
  if (!error)
    {
      error = db_drop_attribute_internal (class_, name);
    }

  return error;
}

/*
 * db_drop_attribute_internal() - This function removes both instance & shared
 *    attributes from a class. The attribute is consequently dropped from any
 *    subclasses, as well.
 * return : error code
 * class(in): class or instance
 * name(in) : attribute name
 */
int
db_drop_attribute_internal (MOP class_, const char *name)
{
  int error = NO_ERROR;

  /* kludge, since instance & shared attributes are really supposed to
     be in the same logical namespace, we should allow shared attributes
     to be deleted here as well.  Unfortunately, the template function
     smt_delete_any() doesn't support this.  Instead, we try the operation
     and if it gets an error, we try again.
   */
  error = drop_internal (class_, name, ID_ATTRIBUTE);
  if (error == ER_SM_ATTRIBUTE_NOT_FOUND)
    error = drop_internal (class_, name, ID_SHARED_ATTRIBUTE);

  return (error);
}

/*
 * db_drop_shared_attribute - This function Removes the definitino of a
 *    shared attribute.
 * return: error code
 * class(in): class or instance
 * name(in) : attribute name
 *
 * note : Not necessary now that db_drop_attribute handles this.
 */
int
db_drop_shared_attribute (MOP class_, const char *name)
{
  int retval;

  retval = drop_internal (class_, name, ID_SHARED_ATTRIBUTE);

  return (retval);
}

/*
 * db_drop_class_attribute() - This function removes a class attribute
 *    from a class.
 * return : error code
 * class(in): class or instance
 * name(in) : attribute name
 */
int
db_drop_class_attribute (MOP class_, const char *name)
{
  int retval;

  retval = drop_internal (class_, name, ID_CLASS_ATTRIBUTE);

  return (retval);
}

/*
 * db_add_set_attribute_domain() - This function adds domain information to an
 *    attribute whose basic domain is one of the set types: set, multiset, or
 *    sequence. If the named attribute has one of these set domains, this
 *    function further specifies the allowed domains for the elements of the
 *    set.
 * return : error code
 * class(in): class or instance pointer
 * name(in) : attribute name
 * class_attribute(in) : 0 if this is not a class attribute
 *                       non-zero if this is a class attribute
 * domain(in): domain name
 */
int
db_add_set_attribute_domain (MOP class_,
			     const char *name,
			     int class_attribute, const char *domain)
{
  int error = NO_ERROR;
  SM_TEMPLATE *def;
  MOP newmop;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();
  CHECK_3ARGS_RETURN_EXPR (class_, name, domain, ER_OBJ_INVALID_ARGUMENTS);

  def = smt_edit_class_mop (class_);
  if (def == NULL)
    {
      error = er_errid ();
    }
  else
    {
      /* should make sure this is a set attribute */
      error =
	smt_add_set_attribute_domain (def, name, class_attribute, domain,
				      (DB_DOMAIN *) 0);
      if (error)
	{
	  smt_quit (def);
	}
      else
	{
	  error = sm_update_class (def, &newmop);
	  if (error)
	    {
	      smt_quit (def);
	    }
	}
    }
  return (error);
}

/*
 * db_add_set_attribute_domain() - see the function db_add_set_attribute_domain
 * return : error code
 * class(in): class or instance pointer
 * name(in) : attribute name
 * domain(in): domain name
 *
 * note : This function is provided for backwards compatibility.
 */
int
db_add_element_domain (MOP class_, const char *name, const char *domain)
{
  int retval;

  retval = db_add_set_attribute_domain (class_, name, 0, domain);

  return (retval);
}

/*
 * db_drop_set_attribute_domain() - This function used to remove domain
 *    specifications for attributes whose original domain was "set",
 *    "multi_set", or "sequence". The set valued attributes can be given
 *    further domain information using db_add_element_domain which defines
 *    the allowed types for elements of the set. Use db_drop_element_domain
 *    to remove entries from the set domain list.
 * returns/side-effects: error code
 * class(in) : class or instance
 * name(in)  : attribute name
 * domain(in): domain name
 *
 */
int
db_drop_set_attribute_domain (MOP class_,
			      const char *name,
			      int class_attribute, const char *domain)
{
  int error = NO_ERROR;
  SM_TEMPLATE *def;
  MOP newmop;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();
  CHECK_3ARGS_RETURN_EXPR (class_, name, domain, ER_OBJ_INVALID_ARGUMENTS);

  def = smt_edit_class_mop (class_);
  if (def == NULL)
    {
      error = er_errid ();
    }
  else
    {
      error = smt_delete_set_attribute_domain (def, name, class_attribute,
					       domain, (DB_DOMAIN *) 0);
      if (error)
	{
	  smt_quit (def);
	}
      else
	{
	  error = sm_update_class (def, &newmop);
	  if (error)
	    {
	      smt_quit (def);
	    }
	}
    }

  return (error);
}

/*
 * db_drop_element_domain() - See the function db_drop_set_attribute_domain
 * returns/side-effects: error code
 * class(in) : class or instance
 * name(in)  : attribute name
 * domain(in): domain name
 *
 * note : This function is provided for backwards compatibility.
 */
int
db_drop_element_domain (MOP class_, const char *name, const char *domain)
{
  int retval;

  retval = db_drop_set_attribute_domain (class_, name, 0, domain);

  return (retval);
}

/*
 * db_change_default() - This function changes the default value definition of
 *    an attribute. Default values are normally established when the attribute
 *    is first defined using the db_add_attribute() function. This function
 *    can be used to change the default value after the attribute has been
 *    defined.
 * return : error code
 * class(in): class or instance pointer
 * name(in) : attribute name
 * value(in): value container with value
 *
 * note: Do not use this function to change the values of class attributes.
 *       Instead, the db_put() function is used with the class object as the
 *       first argument.
 */
int
db_change_default (MOP class_, const char *name, DB_VALUE * value)
{
  int error = NO_ERROR;
  SM_TEMPLATE *def;
  MOP newmop;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();
  CHECK_3ARGS_RETURN_EXPR (class_, name, value, ER_OBJ_INVALID_ARGUMENTS);

  def = smt_edit_class_mop (class_);
  if (def == NULL)
    {
      error = er_errid ();
    }
  else
    {
      error = smt_set_attribute_default (def, name, false, value);
      if (error)
	{
	  smt_quit (def);
	}
      else
	{
	  error = sm_update_class (def, &newmop);
	  if (error)
	    {
	      smt_quit (def);
	    }
	}
    }
  return (error);
}

/*
 * db_rename() - See the db_rename_internal function.
 * return : error code
 * class(in)   : class to alter
 * name(in)    : component name
 * class_namespace(in): class namespace flag
 * newname(in) : new component name
 */
int
db_rename (MOP class_, const char *name,
	   int class_namespace, const char *newname)
{
  int error = NO_ERROR;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();

  error = do_check_partitioned_class (class_,
				      CHECK_PARTITION_SUBS, (char *) name);
  if (!error)
    {
      error = db_rename_internal (class_, name, class_namespace, newname);
    }

  return error;
}

/*
 * db_rename_internal() - This will rename any of the various class components:
 *    attributes, class attributes, methods, class methods.
 * return : error code
 * class(in)   : class to alter
 * name(in)    : component name
 * class_namespace(in): class namespace flag
 * newname(in) : new component name
 */
int
db_rename_internal (MOP class_, const char *name,
		    int class_namespace, const char *newname)
{
  int error = NO_ERROR;
  SM_TEMPLATE *def;
  MOP newmop;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();

  def = smt_edit_class_mop (class_);
  if (def == NULL)
    {
      error = er_errid ();
    }
  else
    {
      error = smt_rename_any (def, name, class_namespace, newname);
      if (error)
	{
	  smt_quit (def);
	}
      else
	{
	  error = sm_update_class_auto (def, &newmop);
	  if (error)
	    {
	      smt_quit (def);
	    }
	}
    }

  return (error);
}

/*
 * db_rename_attribute() - This function renames an attribute and propogates
 *    the changes to the affected sub classes.
 * return: error code
 * class(in)   : class or instance
 * name(in)    : current attribute name
 * class_attribute(in): class attribute flag
 * newname(in) : new attribute name
 *
 * note : This function is obsoleted, use db_rename function
 */
int
db_rename_attribute (MOP class_, const char *name,
		     int class_attribute, const char *newname)
{
  int error = NO_ERROR;

  error = do_check_partitioned_class (class_,
				      CHECK_PARTITION_SUBS, (char *) name);
  if (!error)
    {
      error = db_rename_internal (class_, name, class_attribute, newname);
    }

  return error;
}

/*
 * db_rename_method() - This function renames an method and propogates the
 *    changes to the affected sub classes.
 * return: error code
 * class(in)   : class or instance
 * name(in)    : current method name
 * class_method(in): class method flag
 * newname(in) : new method name
 *
 * note : This function is obsoleted, use db_rename function
 */
int
db_rename_method (MOP class_, const char *name,
		  int class_method, const char *newname)
{
  int retval;

  retval = db_rename_internal (class_, name, class_method, newname);

  return (retval);
}

/*
 * METHODS
 */

/*
 * add_method_internal() - This is internal work function to add a method
 *                         definition.
 * return : error code
 * class(in) : class or instance
 * name(in): method name
 * implementation(in): implementation function name
 * namespace(in): attribute or class namespace identifier
 */
static int
add_method_internal (MOP class_, const char *name, const
		     char *implementation, SM_NAME_SPACE name_space)
{
  int error = NO_ERROR;
  SM_TEMPLATE *def;
  MOP newmop;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();
  CHECK_2ARGS_RETURN_EXPR (class_, name, ER_OBJ_INVALID_ARGUMENTS);

  def = smt_edit_class_mop (class_);
  if (def == NULL)
    {
      error = er_errid ();
    }
  else
    {
      error = smt_add_method_any (def, name, implementation, name_space);
      if (error)
	{
	  smt_quit (def);
	}
      else
	{
	  error = sm_update_class_auto (def, &newmop);
	  if (error)
	    {
	      smt_quit (def);
	    }
	}
    }
  return (error);
}

/*
 * db_add_method() - This function Defines a normal (instance) method.
 * return : error code
 * class(in) : class or instance
 * name(in): method name
 * implementation(in): implementation function name
 *
 * note : The implementation name argument can accept a value of NULL.
 *        This means the system uses a default name that is formed by
 *        combining the class name with the method name
 *        (for example, classname_methodname).
 */
int
db_add_method (MOP class_, const char *name, const char *implementation)
{
  int retval;

  retval = add_method_internal (class_, name, implementation, ID_METHOD);

  return (retval);
}

/*
 * db_add_class_method() - This function define a class method.
 * return : error code
 * class(in): class or instance
 * name(in) : method name
 * implementation(in) : function name
 *
 * note : The implementation name argument can accept a value that is NULL.
 *        This means the system uses a default name that is formed by combining
 *        the class name with the method name
 *        (for example, classname_methodname).
 */
int
db_add_class_method (MOP class_, const char *name, const char *implementation)
{
  int retval;

  retval =
    add_method_internal (class_, name, implementation, ID_CLASS_METHOD);

  return (retval);
}

/*
 * db_drop_method() - This function removes a method from a class.
 * return : error code
 * class(in): class or instance
 * name(in) : method name
 */
int
db_drop_method (MOP class_, const char *name)
{
  int retval;

  retval = drop_internal (class_, name, ID_METHOD);

  return (retval);
}

/*
 * db_drop_class_method() - This function removes a class method from a class.
 * return : error code
 * class(in): class or instance
 * name(in) : class method name
 *
 */
int
db_drop_class_method (MOP class_, const char *name)
{
  int retval;

  retval = drop_internal (class_, name, ID_CLASS_METHOD);

  return (retval);
}

/*
 * db_change_method_implementation() - This function changes the name of C
 *    function that is called when the method is invoked.
 * returns/side-effects: error code
 * class(in)   : class or instance
 * name(in)    : method name
 * class_method(in): class method flag
 * newname(in) : new interface function name
 */
int
db_change_method_implementation (MOP class_, const char *name,
				 int class_method, const char *newname)
{
  int error = NO_ERROR;
  SM_TEMPLATE *def;
  MOP newmop;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();
  CHECK_3ARGS_RETURN_EXPR (class_, name, newname, ER_OBJ_INVALID_ARGUMENTS);

  def = smt_edit_class_mop (class_);
  if (def == NULL)
    {
      error = er_errid ();
    }
  else
    {
      error = smt_change_method_implementation (def, name, class_method,
						newname);
      if (error)
	{
	  smt_quit (def);
	}
      else
	{
	  error = sm_update_class (def, &newmop);
	  if (error)
	    {
	      smt_quit (def);
	    }
	}
    }
  return (error);
}

/*
 * add_arg_domain() - This function describes the domain of a method argument
 *    used for run time consistency checking by the interpreter. If the index
 *    argument is 0, the domain is used as the domain of the return value of
 *    the method. Otherwise, the index references the arguments of the method
 *    from 1 to n.
 * return : error code
 * class(in): class object
 * name(in): method name
 * class_method(in): class method flag
 * index(in): argument index (zero is return value)
 * initial_domain(in): initialize flag.
 * domain(in): domain descriptor string
 *
 * note : Argument indexes can be specified that do not necessarily increase
 *        by 1, i.e., arg1, arg2, arg5 leaves arg3 and arg4 unspecified and
 *        logically void.
 *        If the supplied index is greater than any of the indexes that were
 *        previously supplied for the arguments, the argument list of the
 *        method is extended.
 */
static int
add_arg_domain (DB_OBJECT * class_, const char *name,
		int class_method, int index,
		int initial_domain, const char *domain)
{
  int error = NO_ERROR;
  SM_TEMPLATE *def;
  MOP newmop;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();
  CHECK_2ARGS_RETURN_EXPR (class_, name, ER_OBJ_INVALID_ARGUMENTS);

  def = smt_edit_class_mop (class_);
  if (def == NULL)
    {
      error = er_errid ();
    }
  else
    {
      if (domain == NULL)
	{
	  error =
	    smt_assign_argument_domain (def, name, class_method, NULL,
					index, NULL, (DB_DOMAIN *) 0);
	}
      else
	{
	  if (initial_domain)
	    {
	      error =
		smt_assign_argument_domain (def, name, class_method, NULL,
					    index, domain, (DB_DOMAIN *) 0);
	    }
	  else
	    {
	      error =
		smt_add_set_argument_domain (def, name, class_method,
					     NULL, index, domain,
					     (DB_DOMAIN *) 0);
	    }
	}
      if (error)
	{
	  smt_quit (def);
	}
      else
	{
	  error = sm_update_class (def, &newmop);
	  if (error)
	    {
	      smt_quit (def);
	    }
	}
    }
  return (error);
}

/*
 * db_add_argument() - see the add_arg_domain function.
 * return : error code
 * class(in): class object
 * name(in): method name
 * class_method(in): class method flag
 * index(in): argument index (zero is return value)
 * domain(in): domain descriptor string
 */
int
db_add_argument (DB_OBJECT * class_, const char *name,
		 int class_method, int index, const char *domain)
{
  int retval;

  retval = (add_arg_domain (class_, name, class_method, index, 1, domain));

  return (retval);
}

/*
 * db_set_method_arg_domain() -
 * return :
 * class(in)  :
 * name(in)   :
 * index(in)  :
 * domain(in) :
 */
int
db_set_method_arg_domain (DB_OBJECT * class_, const char *name,
			  int index, const char *domain)
{
  int retval;

  retval = (add_arg_domain (class_, name, 0, index, 1, domain));

  return (retval);
}

/*
 * db_set_class_method_arg_domain() -
 * return :
 * class(in)  :
 * name(in)   :
 * index(in)  :
 * domain(in) :
 */
int
db_set_class_method_arg_domain (DB_OBJECT * class_, const char *name,
				int index, const char *domain)
{
  int retval;

  retval = (add_arg_domain (class_, name, 1, index, 1, domain));

  return (retval);
}

/*
 * db_add_set_argument_domain() - This function is used to add additional
 *    domain information to a method argument whose fundamental domain is
 *    set, multiset, or sequence.
 * return : error code
 * class(in)  : class object
 * name(in)   : method name
 * class_method(in): class method flag
 * index(in)  : argument index
 * domain(in) : domain descriptor
 *
 * note : Use the db_add_argument() function first to specify the set type,
 *    then make repeated calls to the db_add_set_argument_domain()function
 *    to further specify the domains of the set elements.
 */
int
db_add_set_argument_domain (DB_OBJECT * class_, const char *name,
			    int class_method, int index, const char *domain)
{
  int retval;

  retval = (add_arg_domain (class_, name, class_method, index, 0, domain));

  return (retval);
}

/*
 * METHOD FILES & LOADER COMMNADS
 */

/*
 * db_set_loader_commands() - This function sets the dynamic linking loader
 *    command string for a class. This is usually a list of library names that
 *    are to be included when linking the methods for a particular class.
 * return : error code
 * class(in): class or instance
 * commands(in): link command string
 *
 * note : The format of this string must be suitable for insertion in the
 *    command line of the UNIX ld command (link editor), such as
 *      "-L /usr/local/lib -l utilities"
 */
int
db_set_loader_commands (MOP class_, const char *commands)
{
  int error = NO_ERROR;
  SM_TEMPLATE *def;

  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (class_);

  def = smt_edit_class_mop (class_);
  if (def == NULL)
    {
      error = er_errid ();
    }
  else
    {
      error = smt_set_loader_commands (def, commands);
      if (error)
	{
	  smt_quit (def);
	}
      else
	{
	  error = sm_update_class (def, NULL);
	  if (error)
	    {
	      smt_quit (def);
	    }
	}
    }

  return (error);
}

/*
 * db_add_method_file() - This function adds a method file to the list of
 *    method files for a class.
 * return : error code
 * class(in): class or instance
 * name(in) : file pathname
 *
 * note : The file does not have to exist at the time it is added, but it must
 *        exist before dynamic linking can occur. Linking normally occurs the
 *        first time a method is called on an instance of the class. There is
 *        no implicit ordering of method files that are added by this function.
 *        If you attempt to add a method file twice, the second addition is
 *        ignored. The name of a method file may contain an environment
 *        variable reference. This environment variable is expanded during
 *        dynamic linking, but it is not expanded at the time the file is
 *        defined.
 */
int
db_add_method_file (MOP class_, const char *name)
{
  int error = NO_ERROR;
  SM_TEMPLATE *def;
  MOP newmop;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();
  CHECK_2ARGS_RETURN_EXPR (class_, name, ER_OBJ_INVALID_ARGUMENTS);

  def = smt_edit_class_mop (class_);
  if (def == NULL)
    {
      error = er_errid ();
    }
  else
    {
      error = smt_add_method_file (def, name);
      if (error)
	{
	  smt_quit (def);
	}
      else
	{
	  error = sm_update_class (def, &newmop);
	  if (error)
	    {
	      smt_quit (def);
	    }
	}
    }
  return (error);
}

/*
 * db_drop_method_file() - This function removes a method file from a class.
 * returns : error code
 * class(in): class or instance
 * name(in): file pathname
 *
 * note : The effects of this function is not seen during the current session.
 *    The next time a database session is started and dynamic linking occurs
 *    for this class, the file is not included.
 *
 */
int
db_drop_method_file (MOP class_, const char *name)
{
  int error = NO_ERROR;
  SM_TEMPLATE *def;
  MOP newmop;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();
  CHECK_2ARGS_RETURN_EXPR (class_, name, ER_OBJ_INVALID_ARGUMENTS);

  def = smt_edit_class_mop (class_);
  if (def == NULL)
    {
      error = er_errid ();
    }
  else
    {
      error = smt_drop_method_file (def, name);
      if (error)
	{
	  smt_quit (def);
	}
      else
	{
	  error = sm_update_class (def, &newmop);
	  if (error)
	    {
	      smt_quit (def);
	    }
	}
    }
  return (error);
}

/*
 * db_drop_method_files() - This function removes all of the currently defined
 *    method files for a class. This can be used in cases where you want to
 *    completely respecify the file list without making multiple calls to
 *    db_drop_method_file().
 * return : error code.
 * class(in): class or instance.
 */
int
db_drop_method_files (MOP class_)
{
  int error = NO_ERROR;
  SM_TEMPLATE *def;
  MOP newmop;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();
  CHECK_1ARG_RETURN_EXPR (class_, ER_OBJ_INVALID_ARGUMENTS);

  def = smt_edit_class_mop (class_);
  if (def == NULL)
    {
      error = er_errid ();
    }
  else
    {
      error = smt_reset_method_files (def);
      if (error)
	{
	  smt_quit (def);
	}
      else
	{
	  error = sm_update_class (def, &newmop);
	  if (error)
	    {
	      smt_quit (def);
	    }
	}
    }

  return (error);
}

/*
 * CLASS HIERARCHY
 */

/*
 * db_add_super() - This function adds a super class to a class if it did not
 *    already exist. If the class is partitioned, function returns
 *    ER_NOT_ALLOWED_ACCESS_TO_PARTITION
 * return : error code
 * class(in): class or instance
 * super(in): super class
 */
int
db_add_super (MOP class_, MOP super)
{
  int error = NO_ERROR;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();

  error = do_check_partitioned_class (class_, CHECK_PARTITION_SUBS, NULL);
  if (!error)
    {
      error = do_check_partitioned_class (super, CHECK_PARTITION_SUBS, NULL);
      if (!error)
	{
	  error = db_add_super_internal (class_, super);
	}
    }

  return error;
}

/*
 * db_add_super_internal() - This function adds a super class to a class if it
 *                  did not already exist.
 * return : error code
 * class(in): class or instance
 * super(in): super class
 */
int
db_add_super_internal (MOP class_, MOP super)
{
  int error = NO_ERROR;
  SM_TEMPLATE *def;
  MOP newmop;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();
  CHECK_2ARGS_RETURN_EXPR (class_, super, ER_OBJ_INVALID_ARGUMENTS);

  def = smt_edit_class_mop (class_);
  if (def == NULL)
    {
      error = er_errid ();
    }
  else
    {
      error = smt_add_super (def, super);
      if (error)
	{
	  smt_quit (def);
	}
      else
	{
	  error = sm_update_class_auto (def, &newmop);
	  if (error)
	    {
	      smt_quit (def);
	    }
	}
    }

  return (error);
}

/*
 * db_drop_super() - This function removes a superclass link from a class.
 *    The attributes and methods that were inherited from the superclass are
 *    removed, and the changes are propagated to the subclasses. If the class
 *    is partitioned, function returns ER_NOT_ALLOWED_ACCESS_TO_PARTITION.
 * return : error code
 * class(in): class or instance pointer
 * super(in): class pointer
 *
 * note : Any resulting conflicts are resolved automatically by the system.
 *      If the system's resolution is unsatisfactory, it can be redefined
 *      with the db_add_resolution() function or the db_add_class_resolution()
 *      function, as appropriate.
 */
int
db_drop_super (MOP class_, MOP super)
{
  int error = NO_ERROR;
  SM_TEMPLATE *def;
  MOP newmop;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();
  CHECK_2ARGS_RETURN_EXPR (class_, super, ER_OBJ_INVALID_ARGUMENTS);

  error = do_check_partitioned_class (class_, CHECK_PARTITION_SUBS, NULL);
  if (!error)
    {
      def = smt_edit_class_mop (class_);
      if (def == NULL)
	{
	  error = er_errid ();
	}
      else
	{
	  error = smt_delete_super (def, super);
	  if (error)
	    {
	      smt_quit (def);
	    }
	  else
	    {
	      error = sm_update_class_auto (def, &newmop);
	      if (error)
		{
		  smt_quit (def);
		}
	    }
	}
    }

  return (error);
}

/*
 * db_drop_super_connect() - This function removes a superclass link from a
 *    class.
 * return : error code
 * class(in): class or instance pointer
 * super(in): class pointer
 *
 * note : It behaves somewhat differently from the db_drop_super () function.
 *    When you use the function db_drop_super_connect(), the superclasses of
 *    the superclass that is dropped(the super argument) are reconnected as
 *    superclasses of the class argument.
 */
int
db_drop_super_connect (MOP class_, MOP super)
{
  int error = NO_ERROR;
  SM_TEMPLATE *def;
  MOP newmop;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();
  CHECK_2ARGS_RETURN_EXPR (class_, super, ER_OBJ_INVALID_ARGUMENTS);

  error = do_check_partitioned_class (class_, CHECK_PARTITION_SUBS, NULL);
  if (!error)
    {
      def = smt_edit_class_mop (class_);
      if (def == NULL)
	{
	  error = er_errid ();
	}
      else
	{
	  error = smt_delete_super_connect (def, super);
	  if (error)
	    {
	      smt_quit (def);
	    }
	  else
	    {
	      error = sm_update_class_auto (def, &newmop);
	      if (error)
		{
		  smt_quit (def);
		}
	    }
	}
    }
  return (error);
}

/*
 * INTEGRITY CONSTRAINTS
 */

/*
 * db_constrain_non_null() - This function sets the state of an attribute's
 *                           NON_NULL constraint.
 * return : error code
 * class(in): class or instance object
 * name(in) : attribute name
 * class_attribute(in): flag indicating class attribute status
 *        (0 if this is not a class attribute,
 *         non-zero if this is a class attribute)
 * on_or_off(in): non-zero if constraint is to be enabled
 *
 */
int
db_constrain_non_null (MOP class_, const char *name,
		       int class_attribute, int on_or_off)
{
  const char *att_names[2];
  int retval;

  att_names[0] = name;
  att_names[1] = NULL;
  if (on_or_off)
    {
      retval = db_add_constraint (class_, DB_CONSTRAINT_NOT_NULL,
				  NULL, att_names, class_attribute);
    }
  else
    {
      retval = db_drop_constraint (class_, DB_CONSTRAINT_NOT_NULL,
				   NULL, att_names, class_attribute);
    }

  return (retval);
}

/*
 * db_constrain_unique() - This function sets the state of an attribute's
 *                         UNIQUE constraint.
 * return : error code
 * class(in): class or instance object
 * name(in): attribute name
 * on_or_off(in): true if constraint is to be enabled
 */
int
db_constrain_unique (MOP class_, const char *name, int on_or_off)
{
  const char *att_names[2];
  int retval;

  att_names[0] = name;
  att_names[1] = NULL;
  if (on_or_off)
    {
      retval = db_add_constraint (class_, DB_CONSTRAINT_UNIQUE,
				  NULL, att_names, false);
    }
  else
    {
      retval = db_drop_constraint (class_, DB_CONSTRAINT_UNIQUE,
				   NULL, att_names, false);
    }

  return (retval);
}

/*
 *  CONFLICT RESOLUTIONS
 */

/*
 * db_add_resolution() - This defines a conflict resolution specification for
 *    a class. Conflict resolutions must be specified BEFORE an operation which
 *    would generate a conflict. Normally, the alias name is not given in which
 *    case this function simply selects the class from which the
 *    attribute/method will be inherited. If the alias name is given, both of
 *    the conflicting entities are inherited, one with a new name.
 * return : error code
 * class(in): class or instance pointer
 * super(in): class pointer
 * name(in) : attribute or method name
 * alias(in): optional alias name
 */
int
db_add_resolution (MOP class_, MOP super, const char *name, const char *alias)
{
  int error = NO_ERROR;
  SM_TEMPLATE *def;
  MOP newmop;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();

  def = smt_edit_class_mop (class_);
  if (def == NULL)
    {
      error = er_errid ();
    }
  else
    {
      error = smt_add_resolution (def, super, name, alias);
      if (error)
	{
	  smt_quit (def);
	}
      else
	{
	  error = sm_update_class_auto (def, &newmop);
	  if (error)
	    {
	      smt_quit (def);
	    }
	}
    }

  return (error);
}

/*
 * db_add_class_resolution() - This function adds a resolution specifier for
 *    the class attributes or class methods of a class. It functions exactly
 *    like the db_add_resolution() function, except that the namespace for
 *    the resolution is the class namespace instead of the instance namespace.
 * return : error code
 * class(in): class or instance pointer
 * super(in): super class pointer
 * name(in) : attribute or method name
 * alias(in): optional alias name
 */
int
db_add_class_resolution (MOP class_, MOP super, const char *name,
			 const char *alias)
{
  int error = NO_ERROR;
  SM_TEMPLATE *def;
  MOP newmop;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();

  def = smt_edit_class_mop (class_);
  if (def == NULL)
    {
      error = er_errid ();
    }
  else
    {
      error = smt_add_class_resolution (def, super, name, alias);
      if (error)
	{
	  smt_quit (def);
	}
      else
	{
	  error = sm_update_class_auto (def, &newmop);
	  if (error)
	    {
	      smt_quit (def);
	    }
	}
    }

  return (error);
}

/*
 * db_drop_resolution() - This function removes a previously specified
 *                        resolution.
 * return : error code
 * class(in): class pointer
 * super(in): class pointer
 * name(in): attribute/method name
 */
int
db_drop_resolution (MOP class_, MOP super, const char *name)
{
  int error = NO_ERROR;
  SM_TEMPLATE *def;
  MOP newmop;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();

  def = smt_edit_class_mop (class_);
  if (def == NULL)
    {
      error = er_errid ();
    }
  else
    {
      error = smt_delete_resolution (def, super, name);
      if (error)
	{
	  smt_quit (def);
	}
      else
	{
	  error = sm_update_class_auto (def, &newmop);
	  if (error)
	    {
	      smt_quit (def);
	    }
	}
    }

  return (error);
}

/*
 * db_drop_class_resolution() - This function removes a class-level resolution.
 * returns : error code
 * class(in): class pointer
 * super(in): class pointer
 * name(in): attribute/method name
 */
int
db_drop_class_resolution (MOP class_, MOP super, const char *name)
{
  int error = NO_ERROR;
  SM_TEMPLATE *def;
  MOP newmop;

  CHECK_CONNECT_ERROR ();
  CHECK_MODIFICATION_ERROR ();

  def = smt_edit_class_mop (class_);
  if (def == NULL)
    {
      error = er_errid ();
    }
  else
    {
      error = smt_delete_class_resolution (def, super, name);
      if (error)
	{
	  smt_quit (def);
	}
      else
	{
	  error = sm_update_class_auto (def, &newmop);
	  if (error)
	    {
	      smt_quit (def);
	    }
	}
    }

  return (error);
}


/*
 *  INDEX CONTROL
 */

/*
 * db_add_index() - This will add an index to an attribute if one doesn't
 *                  already exist.
 * return : error code
 * classmop(in): class (or instance) pointer
 * attname(in) : attribute name
 *
 * note : This may be an expensive operation if there are a lot of previously
 *    created instances in the database since the index attributes for all of
 *    those instances must be added to the b-tree after it is created.
 */
int
db_add_index (MOP classmop, const char *attname)
{
  const char *att_names[2];
  int retval;

  CHECK_CONNECT_ERROR ();
  CHECK_2ARGS_ERROR (classmop, attname);
  CHECK_MODIFICATION_ERROR ();

  att_names[0] = attname;
  att_names[1] = NULL;
  retval = db_add_constraint (classmop, DB_CONSTRAINT_INDEX,
			      NULL, att_names, false);

  return (retval);
}

/*
 * db_drop_index() - This function drops an index for an attribute if one was
 *    defined. Multi-attribute indexes can be dropped with the
 *    db_drop_constraint() function.
 * return : error code
 * classmop(in): class (or instance) pointer
 * attname(in) : attribute name
 */
int
db_drop_index (MOP classmop, const char *attname)
{
  const char *att_names[2];
  int retval;

  CHECK_CONNECT_ERROR ();
  CHECK_2ARGS_ERROR (classmop, attname);
  CHECK_MODIFICATION_ERROR ();

  att_names[0] = attname;
  att_names[1] = NULL;
  retval = db_drop_constraint (classmop, DB_CONSTRAINT_INDEX,
			       NULL, att_names, false);

  return (retval);
}

/*
 * db_add_constraint() - This function is used to add constraints to a class.
 *    The types of constraints are defined by DB_CONSTRAINT_TYPE and currently
 *    include UNIQUE, REVERSE_UNIQUE, NOT NULL, INDEX, REVERSE_INDEX,
 *    PRIMARY_KEY.
 * return : error code
 * classmop(in): class (or instance) pointer
 * constraint_type(in): type of constraint to add(refer to DB_CONSTRAINT_TYPE).
 * constraint_name(in): constraint name.
 * att_names(in): Names of attributes to be constrained
 * class_attributes(in): flag indicating class attribute status
 *                       (0 if this is not a class attribute,
 *                        non-zero if this is a class attribute)
 *
 */
int
db_add_constraint (MOP classmop,
		   DB_CONSTRAINT_TYPE constraint_type,
		   const char *constraint_name,
		   const char **att_names, int class_attributes)
{
  int retval;
  char *name = NULL;

  CHECK_CONNECT_ERROR ();
  CHECK_2ARGS_ERROR (classmop, att_names);
  CHECK_MODIFICATION_ERROR ();

  name = sm_produce_constraint_name_mop (classmop, constraint_type,
					 att_names, constraint_name);
  if (name == NULL)
    {
      retval = er_errid ();
    }
  else
    {
      retval = sm_add_constraint (classmop, constraint_type, name,
				  att_names, NULL, class_attributes);
    }

  sm_free_constraint_name (name);

  return (retval);
}

/*
 * db_drop_constraint() - This function is used remove constraint from a class.
 *    Please refer to the header information for db_add_constraint() for basic
 *    information on classes and constraints.
 * return : error code
 * classmop: class (or instance) pointer
 * constraint_type: type of constraint to drop
 * constraint_name: constraint name
 * att_names: names of attributes to be constrained
 * class_attributes: flag indicating class attribute status
 *                  (0 if this is not a class attribute,
 *                   non-zero if this is a class attribute)
 *
 * note :
 *    If the name is known, the constraint can be dropped by name, in which
 *    case the <att_names> parameter should be NULL.
 *    If the name is not known, the constraint can be specified by the
 *    combination of class pointer and attribute names.
 *    The order of the attribute names must match the order given when the
 *    constraint was added. In this case, the <constraint_name> should be NULL.
 */
int
db_drop_constraint (MOP classmop,
		    DB_CONSTRAINT_TYPE constraint_type,
		    const char *constraint_name,
		    const char **att_names, int class_attributes)
{
  int retval;
  char *name = NULL;

  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (classmop);
  CHECK_MODIFICATION_ERROR ();

  name = sm_produce_constraint_name_mop (classmop, constraint_type,
					 att_names, constraint_name);

  if (name == NULL)
    {
      retval = er_errid ();
    }
  else
    {
      retval = sm_drop_constraint (classmop, constraint_type, name,
				   att_names, class_attributes);
    }

  sm_free_constraint_name (name);

  return (retval);
}
