Skip to content

File schema_template.c

File List > cubrid > src > object > schema_template.c

Go to the documentation of this file

/*
 * Copyright 2008 Search Solution Corporation
 * Copyright 2016 CUBRID Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

/*
 * schema_template.c - Schema manager templates
 */

#ident "$Id$"

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>

#include "error_manager.h"
#include "object_representation.h"
#include "object_domain.h"
#include "work_space.h"
#include "object_primitive.h"
#include "class_object.h"
#include "schema_manager.h"
#include "set_object.h"
#include "locator_cl.h"
#include "authenticate.h"
#include "transform_cl.h"
#include "statistics.h"
#include "virtual_object.h"
#include "db.h"
#include "release_string.h"
#include "execute_schema.h"
#if defined(WINDOWS)
#include "misc_string.h"
#endif

#include "dbtype.h"

#define DOWNCASE_NAME(a, b) \
  do { \
    sm_downcase_name(a, b, SM_MAX_IDENTIFIER_LENGTH); \
    ws_free_string(a); \
    a = ws_copy_string(b); \
  } while(0)

static int find_method (SM_TEMPLATE * template_, const char *name, int class_method, SM_METHOD ** methodp);
static SM_COMPONENT *find_component (SM_TEMPLATE * template_, const char *name, int class_stuff);
static int find_any (SM_TEMPLATE * template_, const char *name, int class_stuff, SM_COMPONENT ** thing);
static int find_signature (SM_TEMPLATE * template_, const char *name, int class_method, const char *signame,
               SM_METHOD ** methodp, SM_METHOD_SIGNATURE ** sigp);
static int find_argument (SM_TEMPLATE * template_, const char *name, int class_method, const char *signame, int index,
              int create, SM_METHOD ** methodp, SM_METHOD_SIGNATURE ** sigp, SM_METHOD_ARGUMENT ** argp);
static int check_namespace (SM_TEMPLATE * temp, const char *name, const bool class_namespace);
static SM_RESOLUTION *find_alias (SM_RESOLUTION * reslist, const char *name, SM_NAME_SPACE name_space);
static int resolve_class_domain (SM_TEMPLATE * tmp, DB_DOMAIN * domain);
static int get_domain_internal (SM_TEMPLATE * tmp, const char *domain_string, DB_DOMAIN ** domainp, int check_internal);
static int get_domain (SM_TEMPLATE * tmp, const char *domain_string, DB_DOMAIN ** domain);
static int check_domain_class_type (SM_TEMPLATE * template_, DB_OBJECT * domain_classobj);
static SM_TEMPLATE *def_class_internal (const char *name, int class_type);
static int smt_add_constraint_to_property (SM_TEMPLATE * template_, SM_CONSTRAINT_TYPE type,
                       const char *constraint_name, SM_ATTRIBUTE ** atts, const int *asc_desc,
                       const int *attr_prefix_length, SM_FOREIGN_KEY_INFO * fk_info,
                       char *shared_cons_name, SM_PREDICATE_INFO * filter_index,
                       SM_FUNCTION_INFO * function_index, const char *comment,
                       SM_INDEX_STATUS index_status);
static int smt_set_attribute_orig_default_value (SM_ATTRIBUTE * att, DB_VALUE * new_orig_value,
                         DB_DEFAULT_EXPR * default_expr);
static int smt_drop_constraint_from_property (SM_TEMPLATE * template_, const char *constraint_name,
                          SM_ATTRIBUTE_FLAG constraint);
static int smt_check_foreign_key (SM_TEMPLATE * template_, const char *constraint_name, SM_ATTRIBUTE ** atts,
                  int n_atts, SM_FOREIGN_KEY_INFO * fk_info);
static int check_alias_delete (SM_TEMPLATE * template_, const char *name, SM_NAME_SPACE name_space, int error);
static int check_resolution_name (MOP classmop, const char *name, int class_name);
static int check_local_definition (SM_TEMPLATE * template_, const char *name, const char *alias,
                   SM_NAME_SPACE name_space);
static int add_resolution (SM_TEMPLATE * template_, MOP super_class, const char *name, const char *alias,
               SM_NAME_SPACE name_space);
static int delete_resolution (SM_TEMPLATE * template_, MOP super_class, const char *name, SM_NAME_SPACE name_space);
static int smt_add_attribute_to_list (SM_ATTRIBUTE ** att_list, SM_ATTRIBUTE * att, const bool add_first,
                      const char *add_after_attribute);
static int smt_change_attribute (SM_TEMPLATE * template_, const char *name, const char *new_name,
                 const char *new_domain_string, DB_DOMAIN * new_domain, const SM_NAME_SPACE name_space,
                 const bool change_first, const char *change_after_attribute,
                 SM_ATTRIBUTE ** found_att);
static int smt_change_attribute_pos_in_list (SM_ATTRIBUTE ** att_list, SM_ATTRIBUTE * att, const bool change_first,
                         const char *change_after_attribute);
static int smt_change_class_shared_attribute_domain (SM_ATTRIBUTE * att, DB_DOMAIN * new_domain);


#if defined (ENABLE_RENAME_CONSTRAINT)
static int rename_constraint (SM_TEMPLATE * ctemplate, SM_CLASS_CONSTRAINT * sm_cons, const char *old_name,
                  const char *new_name, SM_CONSTRAINT_FAMILY element_type);

static int rename_constraints_partitioned_class (SM_TEMPLATE * ctemplate, const char *old_name, const char *new_name,
                         SM_CONSTRAINT_FAMILY element_type);
#endif

static int change_constraints_comment_partitioned_class (MOP obj, const char *index_name, const char *comment);

static MOP smt_find_owner_of_constraint (SM_TEMPLATE * ctemplate, const char *constraint_name);

static int change_constraints_status_partitioned_class (MOP obj, const char *index_name, SM_INDEX_STATUS index_status);
static SM_CLASS_CONSTRAINT *smt_find_constraint (SM_TEMPLATE * ctemplate, const char *constraint_name);

/* TEMPLATE SEARCH FUNCTIONS */
/*
 * These are used to walk over the template structures and extract information
 * of interest, signaling errors if things don't look right.  These will
 * be called by the smt interface functions so we don't have to duplicate
 * a lot of the error checking code in every function.
*/

/*
 * smt_find_attribute() - Locate an instance, shared or class attribute
 *    in a template. Signal an error if not found.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in): schema template
 *   name(in): attribute name
 *   class_attribute(in): non-zero if looking for class attribute
 *   attp(out): returned pointer to attribute structure
 */

int
smt_find_attribute (SM_TEMPLATE * template_, const char *name, int class_attribute, SM_ATTRIBUTE ** attp)
{
  int error = NO_ERROR;
  SM_ATTRIBUTE *attr_list;

  *attp = NULL;

  if (!sm_check_name (name))
    {
      ASSERT_ERROR_AND_SET (error);
      return error;
    }

  attr_list = class_attribute ? template_->class_attributes : template_->attributes;

  *attp = (SM_ATTRIBUTE *) SM_FIND_NAME_IN_COMPONENT_LIST (attr_list, name);

  if (*attp != NULL)
    {
      // found local attr
      return NO_ERROR;
    }

  if (template_->current == NULL)
    {
      ERROR1 (error, ER_SM_ATTRIBUTE_NOT_FOUND, name);
      return error;
    }

  /* check for mistaken references to inherited attributes and give a better message */
  *attp = classobj_find_attribute (template_->current, name, class_attribute);

  if (*attp != NULL)
    {
      // found inherited attr
      ERROR2 (error, ER_SM_INHERITED_ATTRIBUTE, name, sm_get_ch_name ((*attp)->class_mop));
      return error;
    }
  else
    {
      /* wasn't inherited, give the ususal message */
      ERROR1 (error, ER_SM_ATTRIBUTE_NOT_FOUND, name);
      return error;
    }
}

/*
 * find_method() - Locate an instance or class method in a template.
 *    Signal an error if not found.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in): schema template
 *   name(in): method name
 *   class_method(in): non-zero if looking for a class method
 *   methodp(out): returned pointer to method structure
 */

static int
find_method (SM_TEMPLATE * template_, const char *name, int class_method, SM_METHOD ** methodp)
{
  int error = NO_ERROR;
  SM_METHOD *method_list;

  *methodp = NULL;

  if (!sm_check_name (name))
    {
      ASSERT_ERROR_AND_SET (error);
      return error;
    }

  method_list = (class_method ? template_->class_methods : template_->methods);

  *methodp = (SM_METHOD *) SM_FIND_NAME_IN_COMPONENT_LIST (method_list, name);

  if (*methodp != NULL)
    {
      return NO_ERROR;
    }

  if (template_->current == NULL)
    {
      ERROR1 (error, ER_SM_METHOD_NOT_FOUND, name);
      return error;
    }

  /* check for mistaken references to inherited methods and give a better message */
  *methodp = classobj_find_method (template_->current, name, class_method);

  if (*methodp != NULL)
    {
      /* inherited, indicate the source class */
      ERROR2 (error, ER_SM_INHERITED_METHOD, name, sm_get_ch_name ((*methodp)->class_mop));
      return error;
    }
  else
    {
      /* wasn't inherited, give the ususal message */
      ERROR1 (error, ER_SM_METHOD_NOT_FOUND, name);
      return error;
    }
}

/*
 * find_component() - This function will search through the various lists in
 *    the template looking for a named component.  No errors are signaled if the
 *    component isn't found.  Used by find_any and a few others that need
 *    to do this kind of search.
 *   return: component pointer
 *   template(in): class template
 *   name(in): name of attribute or method
 *   class_stuff(in): non-zero if looking in the class name_space
 */

static SM_COMPONENT *
find_component (SM_TEMPLATE * template_, const char *name, int class_stuff)
{
  SM_ATTRIBUTE *att, *attr_list;
  SM_METHOD *method, *method_list;
  SM_COMPONENT *comp;

  comp = NULL;

  /* check attributes */
  attr_list = (class_stuff ? template_->class_attributes : template_->attributes);
  att = (SM_ATTRIBUTE *) SM_FIND_NAME_IN_COMPONENT_LIST (attr_list, name);
  if (att != NULL)
    {
      return (SM_COMPONENT *) att;
    }

  /* couldn't find an attribute, look at the methods */
  method_list = (class_stuff ? template_->class_methods : template_->methods);
  method = (SM_METHOD *) SM_FIND_NAME_IN_COMPONENT_LIST (method_list, name);
  if (method != NULL)
    {
      return (SM_COMPONENT *) method;
    }

  return NULL;
}

/*
 * find_any() - This is used by smt_delete_any to locate any kind of attribute or
 *    method by name and give an appropriate error messages if not found.
 *    This is pretty much a concatentation of find_attribute and find_method
 *    except that error messages are smarter.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in): class definition template
 *   name(in): name of attribute or method
 *   class_stuff(in): non-zero if looking in the class name_space
 *   thing(out): pointer for matching attribute or method (set on return)
 */

static int
find_any (SM_TEMPLATE * template_, const char *name, int class_stuff, SM_COMPONENT ** thing)
{
  int error = NO_ERROR;
  SM_ATTRIBUTE *att;
  SM_METHOD *method;
  SM_COMPONENT *comp;

  *thing = NULL;

  if (!sm_check_name (name))
    {
      ASSERT_ERROR_AND_SET (error);
      return error;
    }

  comp = find_component (template_, name, class_stuff);
  if (comp != NULL)
    {
      *thing = comp;
      return NO_ERROR;
    }

  /* couldn't find anything, must signal an error */
  if (template_->current == NULL)
    {
      ERROR1 (error, ER_SM_ATTMETH_NOT_FOUND, name);
      return error;
    }

  /* check inherited attributes for better message */
  att = classobj_find_attribute (template_->current, name, class_stuff);
  if (att != NULL)
    {
      /* inherited, indicate the source class */
      ERROR2 (error, ER_SM_INHERITED_ATTRIBUTE, name, sm_get_ch_name (att->class_mop));
      return error;
    }

  /* check inherited methods */
  method = classobj_find_method (template_->current, name, class_stuff);
  if (method != NULL)
    {
      /* inherited, indicate the source class */
      ERROR2 (error, ER_SM_INHERITED_METHOD, name, sm_get_ch_name (method->class_mop));
      return error;
    }
  else
    {
      /* couldn't find any mistaken references to inherited things, give the usual message */
      ERROR1 (error, ER_SM_ATTMETH_NOT_FOUND, name);
      return error;
    }
}

/*
 * find_signature() - Locate the method signature for a particular method
 *    implementation function.
 *    A signature is identified by the name of the C function that
 *    implements the method.
 *    Remember that the function name stored in the template has
 *    an undersore prefix added (for the dynamic loader) so we need to
 *    ignore that in the search.
 *    If the signature name passed in is NULL, we must perform the
 *    search using the default "classname_methodname" format.
 *    NOTE: Multiple signatures for methods is not currently supported
 *    throughout the system so there will always be only a single
 *    method signature.  Support for multiple signatures in the
 *    schema manager was left in for future expansion.
 *    Since we don't actually support multiple signatures, right now
 *    we ignore the signame parameter and always return the first
 *    (and only) signature in the list.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in): schema template
 *   name(in): method name
 *   class_method(in): non-zero if looking for class methods
 *   signame(in): method function name (signature name)
 *   methodp(out): returned pointer to method structure
 *   sigp(out): returned pointer to signature structure
 */

static int
find_signature (SM_TEMPLATE * template_, const char *name, int class_method, const char *signame, SM_METHOD ** methodp,
        SM_METHOD_SIGNATURE ** sigp)
{
  int error = NO_ERROR;
  SM_METHOD *method;
  SM_METHOD_SIGNATURE *sig;

  error = find_method (template_, name, class_method, &method);
  if (error == NO_ERROR)
    {
      /* punt, can only have one signature so first one wins need to do a "real" search here if we ever support
       * multiple signatures */
      sig = method->signatures;

      if (sig == NULL)
    {
      ERROR2 (error, ER_SM_SIGNATURE_NOT_FOUND, name, signame);
    }
      else
    {
      *methodp = method;
      *sigp = sig;
    }
    }

  return error;
}

/*
 * find_argument() - Locate a method argument structure for a method
 *    in a template. See the discussion of signatures in find_signature.
 *    If the create flag is set, the argument will be created if it doesn't
 *    already exist.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in): schema template
 *   name(in): method name
 *   class_method(in): non-zero if looking for class methods
 *   signame(in): method function name (signature name, can be NULL)
 *   index(in): argument index
 *   create(in): non-zero to create argument if not found
 *   methodp(out): returned pointer to method structure
 *   sigp(out): returned pointer to signature structure
 *   argp(out): returned pointer to argument structrue
 */

static int
find_argument (SM_TEMPLATE * template_, const char *name, int class_method, const char *signame, int index, int create,
           SM_METHOD ** methodp, SM_METHOD_SIGNATURE ** sigp, SM_METHOD_ARGUMENT ** argp)
{
  int error = NO_ERROR;
  SM_METHOD_SIGNATURE *sig;
  SM_METHOD_ARGUMENT *arg;

  error = find_signature (template_, name, class_method, signame, methodp, &sig);
  if (error == NO_ERROR)
    {
      if (index)
    {
      arg = classobj_find_method_arg (&sig->args, index, create);
      if (arg == NULL && create)
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();  /* memory allocation error */
        }
      else
        {
          /* keep track of the highest argument index */
          if (create && (index > sig->num_args))
        {
          sig->num_args = index;
        }
        }
    }
      else
    {
      arg = sig->value;
      if (arg == NULL && create)
        {
          arg = classobj_make_method_arg (0);
          sig->value = arg;
        }
    }
      if (arg == NULL)
    {
      if (!error)
        {
          ERROR2 (error, ER_SM_METHOD_ARG_NOT_FOUND, name, index);
        }
    }
      else
    {
      *sigp = sig;
      *argp = arg;
    }
    }

  return error;
}

/*
 * check_namespace() - This is called when any kind of attribute or method is
 *    being added to a template. We check to see if there is already a component
 *    with that name and signal an appropriate error.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   temp(in): schema template
 *   name(in): attribute or method name
 *   class_namespace(in): true if looking in the class name_space
 */

static int
check_namespace (SM_TEMPLATE * temp, const char *name, const bool class_namespace)
{
  int error = NO_ERROR;

  if (class_namespace)
    {
      if (SM_FIND_NAME_IN_COMPONENT_LIST (temp->class_attributes, name) != NULL)
    {
      ERROR1 (error, ER_SM_NAME_RESERVED_BY_ATT, name);
    }
      else if (SM_FIND_NAME_IN_COMPONENT_LIST (temp->class_methods, name) != NULL)
    {
      ERROR1 (error, ER_SM_NAME_RESERVED_BY_METHOD, name);
    }
    }
  else
    {
      if (SM_FIND_NAME_IN_COMPONENT_LIST (temp->attributes, name) != NULL)
    {
      ERROR1 (error, ER_SM_NAME_RESERVED_BY_ATT, name);
    }
      else if (SM_FIND_NAME_IN_COMPONENT_LIST (temp->methods, name) != NULL)
    {
      ERROR1 (error, ER_SM_NAME_RESERVED_BY_METHOD, name);
    }
    }

  return error;
}

/*
 * find_alias() - Searches a resolution list for an alias resolution where the
 *    alias name matches the supplied name.
 *   return: resolution structure
 *   reslist(in): list of resolutions
 *   name(in): component name
 *   name_space(in): name_space identifier (class or instance)
 */

static SM_RESOLUTION *
find_alias (SM_RESOLUTION * reslist, const char *name, SM_NAME_SPACE name_space)
{
  SM_RESOLUTION *res, *found;

  for (res = reslist, found = NULL; res != NULL && found == NULL; res = res->next)
    {
      if (name_space == res->name_space && res->alias != NULL && (SM_COMPARE_NAMES (res->alias, name) == 0))
    {
      found = res;
    }
    }

  return found;
}

/* DOMAIN DECODING */
/*
 * resolve_class_domain()
 * get_domain_internal()
 * get_domain() - Maps a domain string into a domain structure.
 */

static int
resolve_class_domain (SM_TEMPLATE * tmp, DB_DOMAIN * domain)
{
  int error = NO_ERROR;
  DB_DOMAIN *tmp_domain;

  if (domain)
    {
      switch (TP_DOMAIN_TYPE (domain))
    {
    case DB_TYPE_SET:
    case DB_TYPE_MULTISET:
    case DB_TYPE_SEQUENCE:
      tmp_domain = domain->setdomain;
      while (tmp_domain)
        {
          error = resolve_class_domain (tmp, tmp_domain);
          if (error != NO_ERROR)
        {
          return error;
        }
          tmp_domain = tmp_domain->next;
        }
      break;

    case DB_TYPE_OBJECT:
      if (domain->self_ref)
        {
          domain->type = tp_Type_null;
          /* kludge, store the template as the "class" for this special domain */
          domain->class_mop = (MOP) tmp;
        }
      break;

    default:
      break;
    }
    }

  return error;
}

static int
get_domain_internal (SM_TEMPLATE * tmp, const char *domain_string, DB_DOMAIN ** domainp, int check_internal)
{
  int error = NO_ERROR;
  DB_DOMAIN *domain = (DB_DOMAIN *) 0;
  const PR_TYPE *type;

  /* If the domain is already determined, use it */
  if (*domainp)
    {
      domain = *domainp;
    }
  else
    {
      if (domain_string == NULL)
    {
      ERROR0 (error, ER_SM_INVALID_ARGUMENTS);
    }
      else
    {
      if (domain_string[0] == '*')
        {
          if (check_internal)
        {
          ERROR1 (error, ER_SM_DOMAIN_NOT_A_CLASS, domain_string);
        }
          else
        {
          type = pr_find_type (domain_string);
          if (type)
            {
              domain = tp_domain_construct (type->id, NULL, 0, 0, NULL);
            }
        }
        }
      else
        {
          domain = pt_string_to_db_domain (domain_string, tmp->name);
        }
    }
    }

  if (domain != NULL)
    {
      error = resolve_class_domain (tmp, domain);
      if (error != NO_ERROR)
    {
      domain = NULL;
    }
    }
  else
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
    }

  *domainp = domain;

  return error;
}

static int
get_domain (SM_TEMPLATE * tmp, const char *domain_string, DB_DOMAIN ** domain)
{
  return (get_domain_internal (tmp, domain_string, domain, false));
}

/*
 * check_domain_class_type() - see if a class is of the appropriate type for
 *    an attribute. Classes can only have attributes of class domains and
 *    virtual classes can have attributes of both class and vclass domains.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in): class template
 *   domain_classobj(in): class to examine
 */

static int
check_domain_class_type (SM_TEMPLATE * template_, DB_OBJECT * domain_classobj)
{
  int error = NO_ERROR;
  SM_CLASS *class_;

  /* If its a class, the domain can only be "object" or another class */
  if (template_->class_type == SM_CLASS_CT)
    {
      if (domain_classobj != NULL && !(error = au_fetch_class_force (domain_classobj, &class_, AU_FETCH_READ))
      && template_->class_type != class_->class_type)
    {
      ERROR1 (error, ER_SM_INCOMPATIBLE_DOMAIN_CLASS_TYPE, sm_ch_name ((MOBJ) class_));
    }
    }

  return error;
}

/* SCHEMA TEMPLATE CREATION */

/*
 * def_class_internal() - Begins the definition of a new class.
 *    An empty template is created and returned.  The class name
 *    is not registed with the server at this time, that is deferred
 *    until the template is applied with sm_update_class.
 *   return: schema template
 *   name(in): new class name
 *   class_type(in): type of class
 */

static SM_TEMPLATE *
def_class_internal (const char *name, int class_type)
{
  char realname[SM_MAX_IDENTIFIER_LENGTH];
  SM_TEMPLATE *template_ = NULL;
  const PR_TYPE *type;

  if (sm_check_name (name))
    {
      const char *class_name = sm_remove_qualifier_name (name);

      type = pr_find_type (class_name);
      if (type != NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SM_CLASS_WITH_PRIM_NAME, 1, class_name);
    }
      else
    {
      sm_downcase_name (name, realname, SM_MAX_IDENTIFIER_LENGTH);
      name = realname;
      template_ = classobj_make_template (name, NULL, NULL);
      if (template_ != NULL)
        {
          template_->class_type = (SM_CLASS_TYPE) class_type;
        }
    }
    }

  return template_;
}

/*
 * smt_def_class() - Begins the definition of a normal class.
 *    See description of def_class_internal.
 *   return: template
 *   name(in): class name
 */

SM_TEMPLATE *
smt_def_class (const char *name)
{
  return (def_class_internal (name, SM_CLASS_CT));
}

/*
 * smt_edit_class_mop() - Begins the editing of an existing class.
 *    A template is created and populated with the current definition
 *    of a class.
 *    This will get a write lock on the class as well.
 *    At this time we could also get write locks on the subclasses but
 *    if we defer this until sm_update_class, we can be smarter about
 *    getting locks only on the affected subclasses.
 *   return: schema template
 *   op(in): class MOP
 */

SM_TEMPLATE *
smt_edit_class_mop (MOP op, DB_AUTH db_auth_type)
{
  SM_TEMPLATE *template_;
  SM_CLASS *class_;
  int is_class = 0;

  template_ = NULL;

  /* op should be a class */
  is_class = locator_is_class (op, DB_FETCH_WRITE);
  if (is_class < 0)
    {
      return NULL;
    }
  if (!is_class)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_NOT_A_CLASS, 0);
    }
  else
    {
      if (au_fetch_class (op, &class_, AU_FETCH_WRITE, db_auth_type) == NO_ERROR)
    {
      /* cleanup the class and flush out the run-time information prior to editing */
      if (sm_clean_class (op, class_) == NO_ERROR)
        {
          template_ = classobj_make_template (sm_get_ch_name (op), op, class_);
        }
    }
    }

  return template_;
}

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * smt_edit_class() - Begins the editing of an existing class.
 *    Behaves like smt_edit_class_mop except that the class is identified
 *    by name rather than with a MOP.
 *   return: schema template
 *   name(in): class name
 */

SM_TEMPLATE *
smt_edit_class (const char *name)
{
  SM_TEMPLATE *template_;
  MOP op;

  template_ = NULL;
  if (sm_check_name (name))
    {
      op = sm_find_class ((char *) name);
      if (op != NULL)
    {
      template_ = smt_edit_class_mop (op);
    }
    }

  return template_;
}
#endif /* ENABLE_UNUSED_FUNCTION */

/*
 * smt_copy_class_mop() - Duplicates an existing class for CREATE LIKE.
 *    A template is created and populated with a copy of the current definition
 *    of the given class.
 *   return: schema template
 *   op(in): class MOP of the class to duplicate
 *   class_(out): the current definition of the duplicated class is returned
 *                in order to be used for subsequent operations (such as
 *                duplicating indexes).
 */

SM_TEMPLATE *
smt_copy_class_mop (const char *name, MOP op, SM_CLASS ** class_)
{
  SM_TEMPLATE *template_ = NULL;
  int is_class = 0;

  assert (*class_ == NULL);

  /* op should be a class */
  is_class = locator_is_class (op, DB_FETCH_READ);
  if (is_class < 0)
    {
      return NULL;
    }
  if (!is_class)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_NOT_A_CLASS, 0);
      return NULL;
    }

  if (au_fetch_class (op, class_, AU_FETCH_READ, DB_AUTH_SELECT) == NO_ERROR)
    {
      if ((*class_)->class_type != SM_CLASS_CT)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_NOT_A_CLASS, 0);
      return NULL;
    }

      template_ = classobj_make_template_like (name, *class_);
    }

  return template_;
}

/*
 * smt_copy_class() - Duplicates an existing class for CREATE LIKE.
 *    Behaves like smt_copy_class_mop except that the class is identified
 *    by name rather than with a MOP.
 *   return: schema template
 *   new_name(in): name of the class to be created
 *   existing_name(in): name of the class to be duplicated
 *   class_(out): the current definition of the duplicated class is returned
 *                in order to be used for subsequent operations (such as
 *                duplicating indexes).
 */

SM_TEMPLATE *
smt_copy_class (const char *new_name, const char *existing_name, SM_CLASS ** class_)
{
  SM_TEMPLATE *template_ = NULL;

  if (sm_check_name (existing_name) != 0)
    {
      MOP op = sm_find_class (existing_name);
      if (op != NULL)
    {
      template_ = smt_copy_class_mop (new_name, op, class_);
    }
    }

  return template_;
}

/*
 * smt_quit() - This is called to abort the creation of a schema template.
 *    If a template cannot be applied due to errors, you must either
 *    fix the template and re-apply it or use smt_quit to throw
 *    away the template and release the storage that has been allocated.
 *   return: NO_ERROR on success, non-zero for ERROR (always NO_ERROR)
 *   template(in): schema template to destroy
 */

int
smt_quit (SM_TEMPLATE * template_)
{
  int error = NO_ERROR;

  if (template_ != NULL)
    {
      classobj_free_template (template_);
    }

  return error;
}

/* TEMPLATE ATTRIBUTE FUNCTIONS */
/*
 * smt_add_attribute_w_dflt_w_order()
 *   return: NO_ERROR on success, non-zero for ERROR
 *   def(in/out):
 *   name(in): attribute name
 *   domain_string(in): domain name string
 *   domain(in): domain
 *   default_value(in):
 *   name_space(in): attribute name_space (class, instance, or shared)
 *   add_first(in): the attribute should be added at the beginning of the attributes list
 *   add_after_attribute(in): the attribute should be added in the attributes
 *                            list after the attribute with the given name
 *   default_expr(in): default expression
 *   on_update(in): on_update default expression
 *   comment(in): attribute comment
 */
int
smt_add_attribute_w_dflt_w_order (DB_CTMPL * def, const char *name, const char *domain_string, DB_DOMAIN * domain,
                  DB_VALUE * default_value, const SM_NAME_SPACE name_space, const bool add_first,
                  const char *add_after_attribute, DB_DEFAULT_EXPR * default_expr,
                  DB_DEFAULT_EXPR_TYPE * on_update, const char *comment)
{
  int error = NO_ERROR;
  int is_class_attr;

  is_class_attr = (name_space == ID_CLASS_ATTRIBUTE);

  error = smt_add_attribute_any (def, name, domain_string, domain, name_space, add_first, add_after_attribute, comment);
  if (error != NO_ERROR)
    {
      return error;
    }

  if (default_value != NULL)
    {
      error = smt_set_attribute_default (def, name, is_class_attr, default_value, default_expr);
      if (error != NO_ERROR)
    {
      return error;
    }
    }

  if (on_update != NULL)
    {
      error = smt_set_attribute_on_update (def, name, is_class_attr, *on_update);
    }

  return error;
}

/*
 * smt_add_attribute_w_dflt()
 *   return: NO_ERROR on success, non-zero for ERROR
 *   def(in/out):
 *   name(in): attribute name
 *   domain_string(in): domain name string
 *   domain(in): domain
 *   default_value(in):
 *   name_space(in): attribute name_space (class, instance, or shared)
 *   default_expr(in): default expression
 *   on_update(in): on_update default expression
 *   comment(in): attribute comment
 */
int
smt_add_attribute_w_dflt (DB_CTMPL * def, const char *name, const char *domain_string, DB_DOMAIN * domain,
              DB_VALUE * default_value, const SM_NAME_SPACE name_space, DB_DEFAULT_EXPR * default_expr,
              DB_DEFAULT_EXPR_TYPE * on_update, const char *comment)
{
  return smt_add_attribute_w_dflt_w_order (def, name, domain_string, domain, default_value, name_space, false, NULL,
                       default_expr, on_update, comment);
}

/*
 * smt_add_attribute_any() - Adds an attribute to a template.
 *    Handles instance, class, or shared attributes as defined by
 *    the "name_space" argument. The other name_space specific attribute
 *    functions all call this to do the work.
 *    The domain may be specified either with a string or a DB_DOMAIN *.
 *    If domain is not NULL, it is used.  Otherwise domain_string is used.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   name(in): attribute name
 *   domain_string(in): domain name string
 *   domain(in): domain
 *   namespace(in): attribute name_space (class, instance, or shared)
 *   add_first(in): the attribute should be added at the beginning of the
 *                  attributes list
 *   add_after_attribute(in): the attribute should be added in the attributes
 *                            list after the attribute with the given name
 */

int
smt_add_attribute_any (SM_TEMPLATE * template_, const char *name, const char *domain_string, DB_DOMAIN * domain,
               const SM_NAME_SPACE name_space, const bool add_first, const char *add_after_attribute,
               const char *comment)
{
  int error_code = NO_ERROR;
  SM_ATTRIBUTE *att = NULL;
  SM_ATTRIBUTE **att_list = NULL;
  bool class_namespace = false;
  char real_name[SM_MAX_IDENTIFIER_LENGTH] = { 0 };
  char add_after_attribute_real_name[SM_MAX_IDENTIFIER_LENGTH] = { 0 };

  assert (template_ != NULL);

  switch (name_space)
    {
    case ID_INSTANCE:
    case ID_ATTRIBUTE:
    case ID_SHARED_ATTRIBUTE:
      att_list = &template_->attributes;
      class_namespace = false;
      break;

    case ID_CLASS:
    case ID_CLASS_ATTRIBUTE:
      att_list = &template_->class_attributes;
      class_namespace = true;
      break;

    default:
      ERROR0 (error_code, ER_SM_INVALID_ARGUMENTS);
      goto error_exit;
    }

  if (!sm_check_name (name))
    {
      assert (er_errid () != NO_ERROR);
      error_code = er_errid ();
      goto error_exit;
    }

  sm_downcase_name (name, real_name, SM_MAX_IDENTIFIER_LENGTH);
  name = real_name;

  if (add_after_attribute != NULL)
    {
      sm_downcase_name (add_after_attribute, add_after_attribute_real_name, SM_MAX_IDENTIFIER_LENGTH);
      add_after_attribute = add_after_attribute_real_name;
    }

  error_code = check_namespace (template_, name, class_namespace);
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }

  error_code = get_domain (template_, domain_string, &domain);
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }

  if (domain == NULL)
    {
      ERROR0 (error_code, ER_SM_INVALID_ARGUMENTS);
      goto error_exit;
    }

  if (TP_DOMAIN_TYPE (domain) == DB_TYPE_OBJECT)
    {
      error_code = check_domain_class_type (template_, domain->class_mop);
      if (error_code != NO_ERROR)
    {
      goto error_exit;
    }
    }

  att = classobj_make_attribute (name, domain->type, name_space);
  if (att == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error_code = er_errid ();
      goto error_exit;
    }
  att->comment = ws_copy_string (comment);

  /* Flag this attribute as new so that we can initialize the original_value properly.  Make sure this isn't saved on
   * disk ! */
  att->flags |= SM_ATTFLAG_NEW;
  att->class_mop = template_->op;
  att->domain = domain;
  att->auto_increment = NULL;

  error_code = smt_add_attribute_to_list (att_list, att, add_first, add_after_attribute);
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }

  return error_code;

error_exit:
  if (att != NULL)
    {
      classobj_free_attribute (att);
      att = NULL;
    }
  return error_code;
}

/*
 * smt_add_attribute_to_list()
 *   return: NO_ERROR on success, non-zero for ERROR
 *   att_list(in/out): the list to add to
 *   att(in): the attribute to add
 *   add_first(in): the attribute should be added at the beginning of the
 *                  attributes list
 *   add_after_attribute(in): the attribute should be added in the attributes
 *                            list after the attribute with the given name
 */

static int
smt_add_attribute_to_list (SM_ATTRIBUTE ** att_list, SM_ATTRIBUTE * att, const bool add_first,
               const char *add_after_attribute)
{
  int error_code = NO_ERROR;
  SM_ATTRIBUTE *crt_att = NULL;

  assert (att->header.next == NULL);
  assert (att_list != NULL);

  if (add_first)
    {
      assert (add_after_attribute == NULL);
      *att_list = (SM_ATTRIBUTE *) WS_LIST_NCONC (att, *att_list);
      goto end;
    }

  if (add_after_attribute == NULL)
    {
      WS_LIST_APPEND (att_list, att);
      goto end;
    }

  for (crt_att = *att_list; crt_att != NULL; crt_att = (SM_ATTRIBUTE *) crt_att->header.next)
    {
      if (intl_identifier_casecmp (crt_att->header.name, add_after_attribute) == 0)
    {
      break;
    }
    }
  if (crt_att == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_COLNAME, 1, add_after_attribute);
      error_code = ER_QPROC_INVALID_COLNAME;
      goto error_exit;
    }
  att->header.next = crt_att->header.next;
  crt_att->header.next = (SM_COMPONENT *) (att);

end:
  return error_code;

error_exit:
  return error_code;
}

/*
 * smt_add_attribute() - Adds an instance attribute to a class
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   name(in): attribute name
 *   domain_string(in): domain name string
 *   domain(in): domain structure
 */
/*
 * TODO Replace calls to this function with calls to smt_add_attribute_any ()
 *      and remove this function.
 */
int
smt_add_attribute (SM_TEMPLATE * template_, const char *name, const char *domain_string, DB_DOMAIN * domain)
{
  return (smt_add_attribute_any (template_, name, domain_string, domain, ID_ATTRIBUTE, false, NULL, NULL));
}

/*
 * smt_add_set_attribute_domain() - Adds a domain to an attribute whose
 *    basic type is one of the set types.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   name(in): attribute name
 *   class_attribute(in): non-zero if using class name_space
 *   domain_string(in): domain name string
 *   domain(in): domain structure
 */

int
smt_add_set_attribute_domain (SM_TEMPLATE * template_, const char *name, int class_attribute, const char *domain_string,
                  DB_DOMAIN * domain)
{
  int error = NO_ERROR;
  SM_ATTRIBUTE *att;
  TP_DOMAIN *newdomain;

  error = smt_find_attribute (template_, name, class_attribute, &att);
  if (error != NO_ERROR)
    {
      return error;
    }

  if ((att->domain == NULL) || !pr_is_set_type (TP_DOMAIN_TYPE (att->domain)))
    {
      ERROR1 (error, ER_SM_DOMAIN_NOT_A_SET, name);
    }
  else
    {
      error = get_domain (template_, domain_string, &domain);
      if (error == NO_ERROR && domain != NULL)
    {
      if (pr_is_set_type (TP_DOMAIN_TYPE (domain)))
        {
          ERROR1 (error, ER_SM_NO_NESTED_SETS, name);
        }
      else
        {
          /* We need to make sure that we don't update a cached domain since we may not be the only one pointing to
           * it.  If the domain is cached, make a copy of it, update it, then cache it. */
          if (att->domain->is_cached)
        {
          newdomain = tp_domain_copy (att->domain, false);
          if (newdomain)
            {
              error = tp_domain_add (&newdomain->setdomain, domain);
              if (error != NO_ERROR)
            {
              tp_domain_free (newdomain);
            }
              else
            {
              newdomain = tp_domain_cache (newdomain);
              att->domain = newdomain;
            }
            }
          else
            {
              assert (er_errid () != NO_ERROR);
              error = er_errid ();
            }
        }
          else
        {
          error = tp_domain_add (&att->domain->setdomain, domain);
        }
        }
    }
    }

  return error;
}

/*
 * smt_delete_set_attribute_domain() - Remove a domain entry from the domain
 *    list of an attribute whose basic type is one of the set types.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   name(in): attribute name
 *   class_attribute(in): non-zero if looking at class attriutes
 *   domain_string(in): domain name string
 *   domain(in): domain structure
 */

int
smt_delete_set_attribute_domain (SM_TEMPLATE * template_, const char *name, int class_attribute,
                 const char *domain_string, DB_DOMAIN * domain)
{
  int error = NO_ERROR;
  SM_ATTRIBUTE *att;

  error = smt_find_attribute (template_, name, class_attribute, &att);
  if (error == NO_ERROR)
    {
      if ((att->domain == NULL) || !pr_is_set_type (TP_DOMAIN_TYPE (att->domain)))
    {
      ERROR1 (error, ER_SM_DOMAIN_NOT_A_SET, name);
    }
      else
    {
      error = get_domain (template_, domain_string, &domain);
      if (error == NO_ERROR)
        {
          assert (domain != NULL);
          if (domain == NULL || !tp_domain_drop (&att->domain->setdomain, domain))
        {
          ERROR2 (error, ER_SM_DOMAIN_NOT_FOUND, name, (domain_string ? domain_string : "unknown"));
        }
        }
    }
    }

  return error;
}

/*
 * smt_set_attribute_default() - Assigns the default value for an attribute.
 *    If this is a shared or class attribute, it sets the current value
 *    since these only have one value.
 *    Need to have domain checking and constraint checking at this
 *    level similar to that done in object.c when attribute values
 *    are being assigned.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   name(in): attribute name
 *   class_attribute(in): non-zero if looking at class attributes
 *   proposed_value(in): default value to assign
 *   default_expr(in): default expression
 */

int
smt_set_attribute_default (SM_TEMPLATE * template_, const char *name, int class_attribute, DB_VALUE * proposed_value,
               DB_DEFAULT_EXPR * default_expr)
{
  int error = NO_ERROR;
  SM_ATTRIBUTE *att;
  DB_VALUE *value;
  TP_DOMAIN_STATUS status;
  char real_name[SM_MAX_IDENTIFIER_LENGTH] = { 0 };

  sm_downcase_name (name, real_name, SM_MAX_IDENTIFIER_LENGTH);
  name = real_name;

  error = smt_find_attribute (template_, name, class_attribute, &att);
  if (error == NO_ERROR)
    {
      if ((att->type->id == DB_TYPE_BLOB || att->type->id == DB_TYPE_CLOB) && proposed_value
      && !DB_IS_NULL (proposed_value))
    {
      ERROR1 (error, ER_SM_DEFAULT_NOT_ALLOWED, att->type->name);
      return error;
    }
      else if (proposed_value && DB_IS_NULL (proposed_value)
           && (default_expr == NULL || default_expr->default_expr_type == DB_DEFAULT_NONE)
           && (att->flags & SM_ATTFLAG_PRIMARY_KEY))
    {
      ERROR1 (error, ER_CANNOT_HAVE_PK_DEFAULT_NULL, name);
      return error;
    }

      value = proposed_value;
      status = tp_domain_check (att->domain, value, TP_EXACT_MATCH);
      if (status != DOMAIN_COMPATIBLE)
    {
      /* coerce it if we can */
      value = pr_make_ext_value ();
      if (value == NULL)
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();
          goto end;
        }

      status = tp_value_cast (proposed_value, value, att->domain, false);
      /* value is freed at the bottom */
    }
      if (status != DOMAIN_COMPATIBLE)
    {
      ERROR1 (error, ER_OBJ_DOMAIN_CONFLICT, att->header.name);
    }
      else
    {
      /* check a subset of the integrity constraints, we can't check for NOT NULL or unique here */

      if (value != NULL && tp_check_value_size (att->domain, value) != DOMAIN_COMPATIBLE)
        {
          /* need an error message that isn't specific to "string" types */
          ERROR2 (error, ER_OBJ_STRING_OVERFLOW, att->header.name, att->domain->precision);
        }
      else
        {
          pr_clear_value (&att->default_value.value);
          pr_clone_value (value, &att->default_value.value);
          if (default_expr == NULL)
        {
          classobj_initialize_default_expr (&att->default_value.default_expr);
        }
          else
        {
          classobj_copy_default_expr (&att->default_value.default_expr, default_expr);
        }

          /* if there wasn't an previous original value, take this one. This can only happen for new templates OR
           * if this is a new attribute that was added during this template OR if this is the first time setting a
           * default value to the attribute. This should be handled by using candidates in the template and storing
           * an extra bit field in the candidate structure. See the comment above sm_attribute for more information
           * about "original_value". */
          if (att->flags & SM_ATTFLAG_NEW)
        {
          error = smt_set_attribute_orig_default_value (att, value, default_expr);
        }
        }
    }

      /* free the coerced value if any */
      if (value != proposed_value)
    {
      pr_free_ext_value (value);
    }
    }

end:
  return error;
}

/*
 * smt_set_attribute_orig_default_value() - Sets the original default value of the attribute.
 *                      No domain checking is performed.
 *   return: void
 *   att(in/out): attribute
 *   new_orig_value(in): original value to set
 *   default_expr(in): default expression
 *
 *  Note : This function modifies the initial default value of the attribute.
 *     The initial default value is the default value assigned when adding
 *     the attribute. The default value of attribute may change after its
 *     creation (or after it was added), but the initial value remains
 *     unchanged (until attribute is dropped).
 *     The (current) default value is stored as att->value; the initial
 *     default value is stored as att->original_value.
 */

static int
smt_set_attribute_orig_default_value (SM_ATTRIBUTE * att, DB_VALUE * new_orig_value, DB_DEFAULT_EXPR * default_expr)
{
  assert (att != NULL);
  assert (new_orig_value != NULL);

  pr_clear_value (&att->default_value.original_value);
  pr_clone_value (new_orig_value, &att->default_value.original_value);

  if (default_expr == NULL)
    {
      classobj_initialize_default_expr (&att->default_value.default_expr);
      return NO_ERROR;
    }

  return classobj_copy_default_expr (&att->default_value.default_expr, default_expr);
}

/*
 * smt_set_attribute_on_update() - Sets the on update default expr of an attribute.
 *                 No domain checking is performed.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   name(in): attribute name
 *   class_attribute(in): non-zero if looking at class attributes
 *   on_update(in): on update default expression
 */
int
smt_set_attribute_on_update (SM_TEMPLATE * template_, const char *name, int class_attribute,
                 DB_DEFAULT_EXPR_TYPE on_update)
{
  int error = NO_ERROR;
  SM_ATTRIBUTE *att;

  error = smt_find_attribute (template_, name, class_attribute, &att);
  if (error != NO_ERROR)
    {
      return error;
    }

  att->on_update_default_expr = on_update;
  return NO_ERROR;
}

/*
 * smt_drop_constraint_from_property() - Drop the named constraint from the
 *                                       template property list.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   constraint_name(in): constraint name
 *   constraint(in):
 */

static int
smt_drop_constraint_from_property (SM_TEMPLATE * template_, const char *constraint_name, SM_ATTRIBUTE_FLAG constraint)
{
  int error = NO_ERROR;
  DB_VALUE oldval, newval;
  DB_SEQ *seq = NULL;
  const char *prop_type;

  if (!SM_IS_ATTFLAG_UNIQUE_FAMILY_OR_FOREIGN_KEY (constraint))
    {
      return NO_ERROR;
    }

  db_make_null (&oldval);
  db_make_null (&newval);

  prop_type = SM_MAP_CONSTRAINT_ATTFLAG_TO_PROPERTY (constraint);

  if (classobj_get_prop (template_->properties, prop_type, &oldval) > 0)
    {
      seq = db_get_set (&oldval);

      if (!classobj_drop_prop (seq, constraint_name))
    {
      ERROR1 (error, ER_SM_CONSTRAINT_NOT_FOUND, constraint_name);
    }

      db_make_sequence (&newval, seq);
      classobj_put_prop (template_->properties, prop_type, &newval);
    }
  else
    {
      ERROR1 (error, ER_SM_CONSTRAINT_NOT_FOUND, constraint_name);
    }

  pr_clear_value (&oldval);
  pr_clear_value (&newval);

  return error;
}

/*
 * smt_add_constraint_to_property() - Add the named constraint to the
 *                                    template property list
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   type(in):
 *   constraint_name(in): constraint name
 *   atts(in):
 *   asc_desc(in): asc/desc info list
 *   attr_prefix_length(in):
 *   fk_info(in):
 *   shared_cons_name(in):
 *   filter_index(in):
 *   function_index(in)
 *   comment(in):
 */
static int
smt_add_constraint_to_property (SM_TEMPLATE * template_, SM_CONSTRAINT_TYPE type, const char *constraint_name,
                SM_ATTRIBUTE ** atts, const int *asc_desc, const int *attr_prefix_length,
                SM_FOREIGN_KEY_INFO * fk_info, char *shared_cons_name, SM_PREDICATE_INFO * filter_index,
                SM_FUNCTION_INFO * function_index, int options, const char *comment,
                SM_INDEX_STATUS index_status)
{
  int error = NO_ERROR;
  DB_VALUE cnstr_val, current_datetime;
  const char *constraint = classobj_map_constraint_to_property (type);

  db_make_null (&cnstr_val);

  /*
   *  Check if the constraint already exists. Skip it if we have an online index building done.
   */
  if (classobj_find_prop_constraint (template_->properties, constraint, constraint_name, &cnstr_val))
    {
      ERROR1 (error, ER_SM_CONSTRAINT_EXISTS, constraint_name);
      goto end;
    }

  if (db_sys_datetime (&current_datetime) != NO_ERROR)
    {
      ERROR1 (error, ER_SM_CONSTRAINT_EXISTS, constraint_name);
      goto end;
    }

  // Declare and use a temporary variable to match the prototype of the classobj_put_index() function.
  SM_CLASS_CONSTRAINT con;

  con.type = type;
  con.name = constraint_name;
  con.attributes = atts;
  con.asc_desc = (int *) asc_desc;
  con.attrs_prefix_length = (int *) attr_prefix_length;
  con.filter_predicate = filter_index;
  con.func_index_info = function_index;
  con.comment = comment;
  con.index_status = index_status;
  con.index_btid = BTID_INITIALIZER;
  con.fk_info = NULL;
  con.shared_cons_name = NULL;
  con.index_type = SM_BTREE_TYPE;
  con.options = options;

  if (classobj_put_index (&template_->properties, &con, NULL, fk_info, shared_cons_name, true) != NO_ERROR)
    {
      ASSERT_ERROR_AND_SET (error);
    }

end:
  pr_clear_value (&cnstr_val);
  pr_clear_value (&current_datetime);

  return error;
}


static int
smt_check_type_collation_match_4_fk (SM_ATTRIBUTE * attr1, SM_ATTRIBUTE * attr2)
{
  int error = NO_ERROR;

  if (attr1->type->id != attr2->type->id
      || (TP_TYPE_HAS_COLLATION (attr1->type->id)
      && TP_DOMAIN_COLLATION (attr1->domain) != TP_DOMAIN_COLLATION (attr2->domain)))
    {
      char *tp_col_nm1, *tp_col_nm2;
      char *ekind;
      if (attr1->type->id != attr2->type->id)
    {
      tp_col_nm1 = (char *) pr_type_from_id (attr1->type->id)->get_name ();
      tp_col_nm2 = (char *) pr_type_from_id (attr2->type->id)->get_name ();
      ekind = (char *) "data type";
    }
      else
    {
      tp_col_nm1 = lang_get_collation (attr1->domain->collation_id)->coll.coll_name;
      tp_col_nm2 = lang_get_collation (attr2->domain->collation_id)->coll.coll_name;
      ekind = (char *) "collation";
    }

      ERROR5 (error, ER_FK_HAS_DEFFERENT_TYPE_WITH_PK, attr1->header.name, attr2->header.name, ekind, tp_col_nm1,
          tp_col_nm2);
    }

  return error;
}


/*
 * smt_check_foreign_key()
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in): schema template
 *   constraint_name(in): constraint name
 *   atts(in):
 *   n_atts(in):
 *   fk_info(in/out):
 */
static int
smt_check_foreign_key (SM_TEMPLATE * template_, const char *constraint_name, SM_ATTRIBUTE ** atts, int n_atts,
               SM_FOREIGN_KEY_INFO * fk_info)
{
  int error = NO_ERROR;
  MOP ref_clsop = NULL;
  SM_CLASS *ref_cls;
  SM_CLASS_CONSTRAINT *pk, *temp_cons = NULL;
  SM_ATTRIBUTE *tmp_attr, *ref_attr;
  int n_ref_atts, i, j;
  bool found;
  const char *tmp = NULL;

  if (template_->op == NULL && intl_identifier_casecmp (template_->name, fk_info->ref_class) == 0)
    {
      error = classobj_make_class_constraints (template_->properties, template_->attributes, &temp_cons);
      if (error != NO_ERROR)
    {
      return error;
    }

      pk = classobj_find_cons_primary_key (temp_cons);
      if (pk == NULL)
    {
      ERROR1 (error, ER_FK_REF_CLASS_HAS_NOT_PK, fk_info->ref_class);
      goto err;
    }

      OID_SET_NULL (&fk_info->ref_class_oid);
      BTID_SET_NULL (&fk_info->ref_class_pk_btid);
    }
  else
    {
      ref_clsop = sm_find_class (fk_info->ref_class);

      if (ref_clsop == NULL)
    {
      ERROR1 (error, ER_FK_UNKNOWN_REF_CLASSNAME, fk_info->ref_class);
      return error;
    }

      error = au_fetch_class (ref_clsop, &ref_cls, AU_FETCH_READ, AU_SELECT);
      if (error != NO_ERROR)
    {
      return error;
    }

      pk = classobj_find_class_primary_key (ref_cls);
      if (pk == NULL)
    {
      ERROR1 (error, ER_FK_REF_CLASS_HAS_NOT_PK, fk_info->ref_class);
      return error;
    }

      fk_info->ref_class_oid = *(ws_oid (ref_clsop));
      fk_info->ref_class_pk_btid = pk->index_btid;
    }

  /* check pk'size and fk's size */
  for (i = 0; pk->attributes[i]; i++)
    {
      ;
    }

  if (i != n_atts)
    {
      ERROR2 (error, ER_FK_NOT_MATCH_KEY_COUNT, constraint_name, pk->name);
      goto err;
    }

  if (fk_info->ref_attrs)
    {
      n_ref_atts = 0;

      while (fk_info->ref_attrs[n_ref_atts] != NULL)
    {
      n_ref_atts++;
    }

      if (n_ref_atts != n_atts)
    {
      ERROR2 (error, ER_FK_NOT_MATCH_KEY_COUNT, constraint_name, pk->name);
      goto err;
    }
    }

  for (i = 0; pk->attributes[i]; i++)
    {
      found = false;

      if (fk_info->ref_attrs != NULL)
    {
      for (j = 0; fk_info->ref_attrs[j]; j++)
        {
          if (template_->op == NULL && temp_cons)
        {
          error = smt_find_attribute (template_, fk_info->ref_attrs[j], false, &ref_attr);
          if (error != NO_ERROR)
            {
              goto err;
            }
        }
          else
        {
          ref_attr = classobj_find_attribute (ref_cls, fk_info->ref_attrs[j], 0);
          if (ref_attr == NULL)
            {
              ERROR1 (error, ER_SM_ATTRIBUTE_NOT_FOUND, fk_info->ref_attrs[j]);
              goto err;
            }
        }

          if (pk->attributes[i]->id == ref_attr->id)
        {
          found = true;
          break;
        }
        }

      if (!found)
        {
          ERROR2 (error, ER_FK_NOT_HAVE_PK_MEMBER, constraint_name, pk->attributes[i]->header.name);
          goto err;
        }

      if ((error = smt_check_type_collation_match_4_fk (atts[j], ref_attr)) != NO_ERROR)
        {
          goto err;
        }

      if (i != j)
        {
          tmp_attr = atts[i];
          atts[i] = atts[j];
          atts[j] = tmp_attr;

          tmp = fk_info->ref_attrs[i];
          fk_info->ref_attrs[i] = fk_info->ref_attrs[j];
          fk_info->ref_attrs[j] = tmp;
        }
    }
      else
    {
      /*  This is the case where there is only a referenced table name and a specific column name is omitted.
       **  ex) create table tbl (id char(3) not null  PRIMARY KEY);
       **      create table tf_tbl (f_id int references tbl); 
       **  In this case, the PK column name is used.
       */
      if ((error = smt_check_type_collation_match_4_fk (atts[i], pk->attributes[i])) != NO_ERROR)
        {
          goto err;
        }
    }
    }

  fk_info->name = (char *) constraint_name;

err:
  if (temp_cons)
    {
      classobj_free_class_constraints (temp_cons);
    }

  return error;
}

/*
 * smt_drop_constraint() - Drops the integrity constraint flags for an attribute.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   att_names(in): array of attribute names
 *   constraint_name(in): Constraint name.
 *   class_attribute(in): non-zero if we're looking for class attributes
 *   constraint(in): constraint identifier
 */

int
smt_drop_constraint (SM_TEMPLATE * template_, const char **att_names, const char *constraint_name, int class_attribute,
             SM_ATTRIBUTE_FLAG constraint)
{
  int error = NO_ERROR;
  SM_ATTRIBUTE *not_null_attr[1], *pk_attr;
  SM_CLASS_CONSTRAINT *pk;
  int n_atts;
  MOP owner;

  if (!(SM_IS_ATTFLAG_UNIQUE_FAMILY_OR_FOREIGN_KEY (constraint) || constraint == SM_ATTFLAG_NON_NULL))
    {
      ERROR0 (error, ER_SM_INVALID_ARGUMENTS);
      return error;
    }

  if (constraint == SM_ATTFLAG_PRIMARY_KEY)
    {
      char *fk_name = NULL;

      pk = classobj_find_cons_primary_key (template_->current->constraints);
      if (pk->fk_info && classobj_is_pk_referred (template_->op, pk->fk_info, true, &fk_name))
    {
      ERROR2 (error, ER_FK_CANT_DROP_PK_REFERRED, pk->name, fk_name);
      return error;
    }
    }
  else if (constraint == SM_ATTFLAG_NON_NULL)
    {
      n_atts = 0;
      if (att_names != NULL)
    {
      while (att_names[n_atts] != NULL)
        {
          n_atts++;
        }
    }

      if (n_atts != 1)
    {
      ERROR0 (error, ER_SM_NOT_NULL_WRONG_NUM_ATTS);
      return error;
    }
    }

  owner = smt_find_owner_of_constraint (template_, constraint_name);
  if (owner == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      return error;
    }

  if (owner != template_->op && template_->partition == NULL)
    {
      /* it is inherited. */
      ERROR2 (error, ER_SM_INHERITED, constraint_name, sm_get_ch_name (owner));
      return error;
    }

  error = smt_drop_constraint_from_property (template_, constraint_name, constraint);

  if (error == NO_ERROR)
    {
      if (constraint == SM_ATTFLAG_PRIMARY_KEY)
    {
      for (pk_attr = template_->attributes; pk_attr != NULL; pk_attr = (SM_ATTRIBUTE *) pk_attr->header.next)
        {
          if (pk_attr->flags & SM_ATTFLAG_PRIMARY_KEY)
        {
          pk_attr->flags &= ~SM_ATTFLAG_PRIMARY_KEY;
          pk_attr->flags &= ~SM_ATTFLAG_NON_NULL;
        }
        }
    }
      else if (constraint == SM_ATTFLAG_NON_NULL)
    {
      error = smt_find_attribute (template_, att_names[0], class_attribute, &not_null_attr[0]);

      if (error == NO_ERROR)
        {
          if (not_null_attr[0]->flags & constraint)
        {
          not_null_attr[0]->flags &= ~constraint;
        }
          else
        {
          ERROR1 (error, ER_SM_CONSTRAINT_NOT_FOUND, "NON_NULL");
        }
        }
    }
    }

  return error;
}

/*
 * smt_check_index_exist() - Check index is duplicated.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   out_shared_cons_name(out):
 *   constraint_type: constraint type
 *   constraint_name(in): Constraint name.
 *   att_names(in): array of attribute names
 *   asc_desc(in): asc/desc info list
 *   filter_index(in): filter index info
 *   function_index(in): function index info
 */
int
smt_check_index_exist (SM_TEMPLATE * template_, char **out_shared_cons_name, DB_CONSTRAINT_TYPE constraint_type,
               const char *constraint_name, const char **att_names, const int *asc_desc,
               const SM_PREDICATE_INFO * filter_index, const SM_FUNCTION_INFO * function_index)
{
  int error = NO_ERROR;
  SM_CLASS *class_;
  SM_CLASS_CONSTRAINT *temp_cons = NULL;
  SM_CLASS_CONSTRAINT *check_cons;

  if (!DB_IS_CONSTRAINT_INDEX_FAMILY (constraint_type))
    {
      return NO_ERROR;
    }

  if (template_->op != NULL)
    {
      error = au_fetch_class (template_->op, &class_, AU_FETCH_READ, AU_INDEX);
      if (error != NO_ERROR)
    {
      return error;
    }

      check_cons = class_->constraints;
    }
  else
    {
      error = classobj_make_class_constraints (template_->properties, template_->attributes, &check_cons);
      if (error != NO_ERROR)
    {
      return error;
    }

      temp_cons = check_cons;
    }

  error =
    classobj_check_index_exist (check_cons, out_shared_cons_name, template_->name, constraint_type, constraint_name,
                att_names, asc_desc, filter_index, function_index);

  if (temp_cons != NULL)
    {
      classobj_free_class_constraints (temp_cons);
    }

  return error;
}

/*
 * smt_add_constraint() - Adds the integrity constraint flags for an attribute.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   constraint_type(in): constraint type
 *   constraint_name(in): Constraint name.
 *   att_names(in): array of attribute names
 *   attrs_prefix_length(in): prefix length for each of the index attributes
 *   asc_desc(in): asc/desc info list
 *   class_attribute(in): non-zero if we're looking for class attributes
 *   fk_info(in): foreign key information
 *   filter_index(in): filter index info
 *   function_index(in): function index info
 *   comment(in): constraint comment
 *   index_status(in):
 */
int
smt_add_constraint (SM_TEMPLATE * template_, DB_CONSTRAINT_TYPE constraint_type, const char *constraint_name,
            const char **att_names, const int *asc_desc, const int *attrs_prefix_length, int class_attribute,
            SM_FOREIGN_KEY_INFO * fk_info, SM_PREDICATE_INFO * filter_index, SM_FUNCTION_INFO * function_index,
            const char *comment, SM_INDEX_STATUS index_status)
{
  int error = NO_ERROR;
  SM_ATTRIBUTE **atts = NULL;
  int i, j, n_atts, atts_size;
  char *shared_cons_name = NULL;
  SM_ATTRIBUTE_FLAG constraint;
  bool has_nulls = false;
  bool is_secondary_index = false;
  int deduplicate_key_col_pos = -1;
  int options = 0;
  int deduplicate_key_level = 0;

  assert (template_ != NULL);

  error = smt_check_index_exist (template_, &shared_cons_name, constraint_type, constraint_name, att_names,
                 asc_desc, filter_index, function_index);
  if (error != NO_ERROR)
    {
      goto error_return;
    }

  constraint = SM_MAP_CONSTRAINT_TO_ATTFLAG (constraint_type);
  is_secondary_index = (constraint_type == DB_CONSTRAINT_INDEX || constraint_type == DB_CONSTRAINT_REVERSE_INDEX);

  n_atts = 0;
  if (att_names != NULL)
    {
      while (att_names[n_atts] != NULL)
    {
      if (IS_DEDUPLICATE_KEY_ATTR_NAME (att_names[n_atts]))
        {
          deduplicate_key_col_pos = n_atts;
          GET_DEDUPLICATE_KEY_ATTR_LEVEL_FROM_NAME (att_names[n_atts], deduplicate_key_level);
          SET_OPTION_DEDUPLICATE (options, deduplicate_key_level);
        }
      n_atts++;
    }
    }

  if ((n_atts == 0) || ((n_atts == 1) && (deduplicate_key_col_pos != -1)))
    {
      ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
      goto error_return;
    }

  /* if primary key shares index with other constraint, it is neccessary to check whether the attributs do not have
   * null value. e.g. primary key shares index with unique constraint. Because unique constraint allows null value, we
   * can not just use the index simply. template_->op == NULL, it means this is a create statement, the class has not
   * yet existed. Obviously, there is no data in the class at that time! So we skip to test NULL value for primary
   * key. */
  if (constraint_type == DB_CONSTRAINT_PRIMARY_KEY && shared_cons_name != NULL && template_->op != NULL)
    {
      for (i = 0; att_names[i] != NULL; i++)
    {
      assert (att_names[i] != NULL);
      error = do_check_rows_for_null (template_->op, att_names[i], &has_nulls);
      if (error != NO_ERROR)
        {
          goto error_return;
        }

      if (has_nulls)
        {
          error = ER_SM_ATTR_NOT_NULL;
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, att_names[i]);
          goto error_return;
        }
    }
    }

  atts_size = (n_atts + 1) * (int) sizeof (SM_ATTRIBUTE *);
  atts = (SM_ATTRIBUTE **) malloc (atts_size);
  if (atts == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) atts_size);

      error = ER_OUT_OF_VIRTUAL_MEMORY;
      goto error_return;
    }

  for (i = 0; i < n_atts && error == NO_ERROR; i++)
    {
      if (deduplicate_key_col_pos == i)
    {
      atts[i] = dk_find_sm_deduplicate_key_attribute (-1, att_names[i]);
      continue;
    }

      error = smt_find_attribute (template_, att_names[i], class_attribute, &atts[i]);
      if (error == ER_SM_INHERITED_ATTRIBUTE)
    {
      if (is_secondary_index)
        {
          // secondary indexes are allowed on an inherited column
          assert (atts[i] != NULL);

          er_clear ();
          error = NO_ERROR;
        }
    }
#if defined (ENABLE_UNUSED_FUNCTION)    /* to disable TEXT */
      else if (error == NO_ERROR && SM_IS_ATTFLAG_INDEX_FAMILY (constraint))
    {
      /* prevent to create index on TEXT attribute */
      if (sm_has_text_domain (atts[i], 0))
        {
          if (strstr (constraint_name, TEXT_CONSTRAINT_PREFIX))
        {
          ERROR1 (error, ER_REGU_NOT_IMPLEMENTED, rel_major_release_string ());
        }
        }
    }
#endif /* ENABLE_UNUSED_FUNCTION */
    }
  atts[i] = NULL;

  if (error != NO_ERROR)
    {
      goto error_return;
    }

  /* check that there are no duplicate attr defs in given list */
  for (i = 0; i < n_atts && error == NO_ERROR; i++)
    {
      for (j = i + 1; j < n_atts; j++)
    {
      /* can not check attr-id, because is not yet assigned */
      if (intl_identifier_casecmp (atts[i]->header.name, atts[j]->header.name) == 0)
        {
          ERROR1 (error, ER_SM_INDEX_ATTR_DUPLICATED, atts[i]->header.name);
        }
    }
    }

  if (error != NO_ERROR)
    {
      goto error_return;
    }

  if (is_secondary_index)
    {
      for (i = 0; atts[i] != NULL; i++)
    {
      DB_TYPE type = atts[i]->type->id;

      if (!tp_valid_indextype (type))
        {
          error = ER_SM_INVALID_INDEX_TYPE;
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, pr_type_name (type));
          break;
        }
      else if (attrs_prefix_length && attrs_prefix_length[i] >= 0)
        {
          if (!TP_IS_CHAR_TYPE (type) && !TP_IS_BIT_TYPE (type))
        {
          error = ER_SM_INVALID_INDEX_WITH_PREFIX_TYPE;
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, pr_type_name (type));
          break;
        }
          else if (((long) atts[i]->domain->precision) < attrs_prefix_length[i])
        {
          error = ER_SM_INVALID_PREFIX_LENGTH;
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SM_INVALID_PREFIX_LENGTH, 1, attrs_prefix_length[i]);
          break;
        }
        }
    }
    }

  if (error != NO_ERROR)
    {
      goto error_return;
    }

  /*
   *  Process constraint
   */
  if (SM_IS_ATTFLAG_INDEX_FAMILY (constraint))
    {
      /* Check possible errors:
       *   - We do not allow UNIQUE constraints/INDEXES on any attribute of a virtual class.
       *   - We do not allow UNIQUE constraints/INDEXES on class|shared attributes.
       *   - We only allow unique constraints on indexable data types.
       */
      if (template_->class_type != SM_CLASS_CT)
    {
      ERROR0 (error, ER_SM_UNIQUE_ON_VCLASS);
      goto error_return;
    }

      for (i = 0; i < n_atts; i++)
    {
      if (atts[i]->header.name_space == ID_SHARED_ATTRIBUTE || class_attribute)
        {
          if (constraint == SM_ATTFLAG_FOREIGN_KEY)
        {
          ERROR2 (error, ER_FK_CANT_ON_SHARED_ATTRIBUTE, constraint_name, atts[i]->header.name);
        }
          else
        {
          ERROR1 (error, ER_SM_INDEX_ON_SHARED, att_names[i]);
        }
          goto error_return;
        }

      if (!tp_valid_indextype (atts[i]->type->id))
        {
          if (SM_IS_ATTFLAG_UNIQUE_FAMILY (constraint))
        {
          ERROR2 (error, ER_SM_INVALID_UNIQUE_TYPE, atts[i]->type->name,
              SM_GET_CONSTRAINT_STRING (constraint_type));
        }
          else
        {
          assert (constraint == SM_ATTFLAG_INDEX || constraint == SM_ATTFLAG_REVERSE_INDEX
              || constraint == SM_ATTFLAG_FOREIGN_KEY);
          ERROR1 (error, ER_SM_INVALID_INDEX_TYPE, atts[i]->type->name);
        }
          goto error_return;
        }
    }

      if (constraint == SM_ATTFLAG_FOREIGN_KEY)
    {
      error =
        smt_check_foreign_key (template_, constraint_name, atts,
                   ((deduplicate_key_col_pos == -1) ? n_atts : (n_atts - 1)), fk_info);
      if (error != NO_ERROR)
        {
          goto error_return;
        }
    }
      else
    {
      assert (fk_info == NULL);
    }

      /* Add the constraint. */
      error = smt_add_constraint_to_property (template_, SM_MAP_INDEX_ATTFLAG_TO_CONSTRAINT (constraint),
                          constraint_name, atts, asc_desc, attrs_prefix_length, fk_info,
                          shared_cons_name, filter_index, function_index, options, comment,
                          index_status);
      if (error != NO_ERROR)
    {
      goto error_return;
    }

      if (constraint == SM_ATTFLAG_PRIMARY_KEY)
    {
      for (i = 0; i < n_atts; i++)
        {
          atts[i]->flags |= SM_ATTFLAG_PRIMARY_KEY;
          atts[i]->flags |= SM_ATTFLAG_NON_NULL;
        }
    }
    }
  else if (constraint == SM_ATTFLAG_NON_NULL)
    {
      /*
       *  We do not support NOT NULL constraints for;
       *    - normal (not class and shared) attributes of virtual classes
       *    - multiple attributes
       *    - class attributes without default value
       */
      if (n_atts != 1)
    {
      ERROR0 (error, ER_SM_NOT_NULL_WRONG_NUM_ATTS);
    }
      else if (template_->class_type != SM_CLASS_CT && atts[0]->header.name_space == ID_ATTRIBUTE)
    {
      ERROR0 (error, ER_SM_NOT_NULL_ON_VCLASS);
    }
      else if (class_attribute && DB_IS_NULL (&(atts[0]->default_value.value)))
    {
      ERROR0 (error, ER_SM_INVALID_CONSTRAINT);
    }
      else if (atts[0]->type->id == DB_TYPE_BLOB || atts[0]->type->id == DB_TYPE_CLOB)
    {
      ERROR1 (error, ER_SM_NOT_NULL_NOT_ALLOWED, atts[0]->type->name);
    }
      else
    {
      if (atts[0]->flags & constraint)
        {
          ERROR1 (error, ER_SM_CONSTRAINT_EXISTS, "NON_NULL");
        }
      else
        {
          atts[0]->flags |= constraint;
        }
    }
    }
  else
    {
      /* Unknown constraint type */
      ERROR0 (error, ER_SM_INVALID_ARGUMENTS);
    }

error_return:
  if (atts != NULL)
    {
      free_and_init (atts);
    }

  if (shared_cons_name != NULL)
    {
      free_and_init (shared_cons_name);
    }

  return (error);
}

/* TEMPLATE METHOD FUNCTIONS */
/*
 * smt_add_method_any() - This will add an instance method or class method to
 *    a template. It would be nice to merge this with smt_add_attribyte_any but
 *    the argument lists are slightly different so keep them separate
 *    for now.
 *    NOTE: The original implementation was designed to allow
 *    multiple method signatures each with their own unique function
 *    name.  This feature has been postponed indefinately because of the
 *    complexity added to the interpreter.  Because of this, the method
 *    structures are a bit more complicated than necessary but the partial
 *    support for multiple signatures has been left in for future
 *    expansion.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   name(in): method name
 *   function(in): implementation function name
 *   namespace(in): class or insance name_space identifier
 */

int
smt_add_method_any (SM_TEMPLATE * template_, const char *name, const char *function, SM_NAME_SPACE name_space)
{
  int error = NO_ERROR;
  SM_METHOD *method;
  SM_METHOD_SIGNATURE *sig;
  char iname[SM_MAX_IDENTIFIER_LENGTH * 2 + 2];
  SM_METHOD **methlist = NULL;
  char realname[SM_MAX_IDENTIFIER_LENGTH];

  if (!sm_check_name (name))
    {
      ASSERT_ERROR_AND_SET (error);
      return error;
    }

  sm_downcase_name (name, realname, SM_MAX_IDENTIFIER_LENGTH);
  name = realname;

  if (name_space == ID_CLASS || name_space == ID_CLASS_METHOD)
    {
      methlist = &template_->class_methods;
      error = check_namespace (template_, name, true);
    }
  else
    {
      methlist = &template_->methods;
      error = check_namespace (template_, name, false);
    }

  if (error != NO_ERROR)
    {
      return error;
    }

  if (methlist != NULL)
    {
      method = (SM_METHOD *) SM_FIND_NAME_IN_COMPONENT_LIST (*methlist, name);
      if (method == NULL)
    {
      method = classobj_make_method (name, name_space);
      if (method == NULL)
        {
          ASSERT_ERROR_AND_SET (error);
          return error;
        }

      method->class_mop = template_->op;
      WS_LIST_APPEND (methlist, method);
    }

      /* THESE FOUR LINES ENFORCE THE SINGLE SIGNATURE RESTRICTION */
      if (method->signatures != NULL)
    {
      ERROR2 (error, ER_SM_SIGNATURE_EXISTS, name, function);
      return error;
    }

      /* NEED TO CHECK FOR IDENTIFIER LENGTH OVERFLOW !!! */

      if (function != NULL)
    {
      sprintf (iname, "%s", function);
    }
      else
    {
      if (template_->name != NULL)
        {
          sprintf (iname, "%s_%s", sm_remove_qualifier_name (template_->name), name);
        }
      else if (template_->op != NULL)
        {
          sprintf (iname, "%s_%s", sm_remove_qualifier_name (sm_get_ch_name (template_->op)), name);
        }
      else
        {
          /* this should be an error */
          sprintf (iname, "%s_%s", "unknown_class", name);
        }
    }

      /* implementation names are case sensitive */
      sig = (SM_METHOD_SIGNATURE *) NLIST_FIND (method->signatures, iname);
      if (sig != NULL)
    {
      ERROR2 (error, ER_SM_SIGNATURE_EXISTS, name, function);
      return error;
    }

      sig = classobj_make_method_signature (iname);
      if (sig == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      return error;
    }
      WS_LIST_APPEND (&method->signatures, sig);
    }

  return error;
}

/*
 * smt_add_method()
 * smt_add_class_method() - These are type specific functions
 *   for adding methods. I would prefer callers convert to the
 *   smt_add_method_any style but this will be a gradual change.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   name(in): method name
 *   function(in): method implementation function
 */

int
smt_add_method (SM_TEMPLATE * template_, const char *name, const char *function)
{
  return (smt_add_method_any (template_, name, function, ID_METHOD));
}

int
smt_add_class_method (SM_TEMPLATE * template_, const char *name, const char *function)
{
  return (smt_add_method_any (template_, name, function, ID_CLASS_METHOD));
}

/*
 * smt_change_method_implementation() - This changes the name of the function
 *    that implements a method.
 *    NOTE: This is written with the assumption that methods do NOT
 *    have multiple signatures.  This is currently the case but if
 *    we extend this in the future, we will need another
 *    mechanism to identify the implementation that needs to change.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   name(in): method name
 *   class_method(in): non-zero if looking at class methods
 *   function(in): method implementation function
 */

int
smt_change_method_implementation (SM_TEMPLATE * template_, const char *name, int class_method, const char *function)
{
  int error = NO_ERROR;
  SM_METHOD *method, *method_list;
  const char *current;

  method_list = (class_method ? template_->class_methods : template_->methods);
  method = (SM_METHOD *) SM_FIND_NAME_IN_COMPONENT_LIST (method_list, name);

  if (method == NULL)
    {
      ERROR1 (error, ER_SM_METHOD_NOT_FOUND, name);
      return error;
    }

  if (method->signatures == NULL)
    {
      ERROR2 (error, ER_SM_SIGNATURE_NOT_FOUND, name, function);
      return error;
    }

  if (method->signatures->next != NULL)
    {
      ERROR1 (error, ER_SM_MULTIPLE_SIGNATURES, name);
      return error;
    }

  current = method->signatures->function_name;
  method->signatures->function_name = ws_copy_string (function);
  if (method->signatures->function_name == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      // fall through
    }
  ws_free_string (current);

  /* If this method has been called, we need to invalidate it so that dynamic linking will be invoked to
   * get the new resolution.  Remember to do both the "real" one and the cache. */
  method->function = NULL;
  method->signatures->function = NULL;

  return error;
}

/*
 * smt_assign_argument_domain() - This is used to assign a basic domain to a
 *    method argument. If there is already a domain specified for the argument
 *    it will be replaced.  If the domain argument is NULL, any existing
 *    domain information for the argument will be removed.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   name(in): method name
 *   class_method(in): non-zero if operating on class method
 *   implementation(in): method implementation function
 *   index(in): argument index
 *   domain_string(in): argument domain name
 *   domain(in): domain structure
 */

int
smt_assign_argument_domain (SM_TEMPLATE * template_, const char *name, int class_method, const char *implementation,
                int index, const char *domain_string, DB_DOMAIN * domain)
{
  int error = NO_ERROR;
  SM_METHOD *method;
  SM_METHOD_SIGNATURE *sig;
  SM_METHOD_ARGUMENT *arg;

  error = find_argument (template_, name, class_method, implementation, index, true, &method, &sig, &arg);
  if (error != NO_ERROR)
    {
      return error;
    }

  if (domain_string == NULL && domain == NULL)
    {
      /* no domain given, reset the domain list */
      arg->domain = NULL;
    }
  else
    {
      error = get_domain (template_, domain_string, &domain);
      if (error != NO_ERROR)
    {
      return error;
    }

      if (domain != NULL)
    {
      if (arg->type != NULL && arg->type != domain->type)
        {
          /* changing the domain, automatically reset the domain list */
          arg->domain = NULL;
        }
      arg->type = domain->type;
      arg->domain = domain;
    }
    }

  return error;
}

/*
 * smt_add_set_argument_domain() - This adds domain information to a method
 *    argument whose basic type is one of the set types.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   name(in): method name
 *   class_method(in): non-zero if operating on class method
 *   implementation(in): method implementation function
 *   index(in): argument index
 *   domain_string(in): domain name of set element
 *   domain(in): domain structure
 */

int
smt_add_set_argument_domain (SM_TEMPLATE * template_, const char *name, int class_method, const char *implementation,
                 int index, const char *domain_string, DB_DOMAIN * domain)
{
  int error = NO_ERROR;
  SM_METHOD *method;
  SM_METHOD_SIGNATURE *sig;
  SM_METHOD_ARGUMENT *arg;

  error = get_domain (template_, domain_string, &domain);
  if (error != NO_ERROR)
    {
      return error;
    }

  if (domain == NULL)
    {
      ERROR2 (error, ER_SM_DOMAIN_NOT_FOUND, name, (domain_string ? domain_string : "unknown"));
      return error;
    }

  error = find_argument (template_, name, class_method, implementation, index, false, &method, &sig, &arg);
  if (error != NO_ERROR)
    {
      return error;
    }

  if (arg->domain == NULL || !pr_is_set_type (TP_DOMAIN_TYPE (arg->domain)))
    {
      ERROR2 (error, ER_SM_ARG_DOMAIN_NOT_A_SET, name, index);
      return error;
    }

  error = tp_domain_add (&arg->domain->setdomain, domain);

  return error;
}

/* TEMPLATE RENAME FUNCTIONS */

/*
 * smt_rename_any() - Renames a component (attribute or method).
 *    This is semantically different than just dropping the component
 *    and re-adding it since the internal ID number assigned
 *    to the component must remain the same after the rename.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   name(in): attribute or method name
 *   class_namespace(in): non-zero of looking for class components
 *   new_name(in): new name of component
 */

int
smt_rename_any (SM_TEMPLATE * template_, const char *name, const bool class_namespace, const char *new_name)
{
  int error = NO_ERROR;
  SM_COMPONENT *comp;
  char real_new_name[SM_MAX_IDENTIFIER_LENGTH];

  if (!sm_check_name (name) || !sm_check_name (new_name))
    {
      ASSERT_ERROR_AND_SET (error);
      return error;
    }

  sm_downcase_name (new_name, real_new_name, SM_MAX_IDENTIFIER_LENGTH);
  new_name = real_new_name;

  /* find the named component */
  error = find_any (template_, name, class_namespace, &comp);
  if (error != NO_ERROR)
    {
      return error;
    }

  if (comp->name_space == ID_ATTRIBUTE || comp->name_space == ID_SHARED_ATTRIBUTE
      || comp->name_space == ID_CLASS_ATTRIBUTE)
    {
      SM_ATTRIBUTE *att;
#if defined (ENABLE_UNUSED_FUNCTION)    /* to disable TEXT */
      error = smt_find_attribute (template_, comp->name, (comp->name_space == ID_CLASS_ATTRIBUTE ? 1 : 0), &att);
      if (error == NO_ERROR)
    {
      if (sm_has_text_domain (att, 0))
        {
          /* prevent to rename attribute */
          ERROR1 (error, ER_REGU_NOT_IMPLEMENTED, rel_major_release_string ());
        }
    }
#else /* ENABLE_UNUSED_FUNCTION */
      error = smt_find_attribute (template_, comp->name, (comp->name_space == ID_CLASS_ATTRIBUTE ? 1 : 0), &att);
#endif /* ENABLE_UNUSED_FUNCTION */
      if (error != NO_ERROR)
    {
      return error;
    }
    }

  /* check for collisions on the new name */
  error = check_namespace (template_, new_name, class_namespace);
  if (error != NO_ERROR)
    {
      return error;
    }

  ws_free_string (comp->name);
  comp->name = ws_copy_string (new_name);
  if (comp->name == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      return error;
    }

  return error;
}

#if defined (ENABLE_RENAME_CONSTRAINT)
/*
 * rename_constraint() - Renames a constraint.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   ctemplate(in/out): schema template
 *   sm_cons(in/out) : list of constraints
 *   old_name(in): old name of constraint
 *   new_name(in): new name of constraint
 *   element_type(in): type of constraint
 */

static int
rename_constraint (SM_TEMPLATE * ctemplate, SM_CLASS_CONSTRAINT * sm_cons, const char *old_name, const char *new_name,
           SM_CONSTRAINT_FAMILY element_type)
{
  int error = NO_ERROR;
  DB_CONSTRAINT_TYPE ctype;
  SM_CLASS_CONSTRAINT *sm_constraint = NULL;
  SM_CLASS_CONSTRAINT *existing_con = NULL;
  const char *property_type = NULL;
  char *norm_new_name = NULL;
  MOP ref_clsop;
  BTID *btid = NULL;

  assert (ctemplate != NULL);
  assert (sm_cons != NULL);

  sm_constraint = classobj_find_constraint_by_name (sm_cons, old_name);
  if (sm_constraint == NULL)
    {
      error = ER_SM_CONSTRAINT_NOT_FOUND;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, old_name);
      goto error_exit;
    }
  btid = &sm_constraint->index_btid;

  switch (element_type)
    {
    case SM_CONSTRAINT_NAME:    /* "*U", "*RU", "*P", "*FK" */
      if (!SM_IS_CONSTRAINT_EXCEPT_INDEX_FAMILY (sm_constraint->type))
    {
      error = ER_SM_CONSTRAINT_HAS_DIFFERENT_TYPE;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, old_name);
      goto error_exit;
    }
      break;
    case SM_INDEX_NAME: /* "*U", "*I", "*RU", "*RI" */
      if (!SM_IS_INDEX_FAMILY (sm_constraint->type))
    {
      error = ER_SM_CONSTRAINT_HAS_DIFFERENT_TYPE;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, old_name);
      goto error_exit;
    }
      break;
    default:
      error = ER_SM_CONSTRAINT_HAS_DIFFERENT_TYPE;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, old_name);
      goto error_exit;
      break;
    }

  ctype = db_constraint_type (sm_constraint);

  norm_new_name = sm_produce_constraint_name (ctemplate->name, ctype, NULL, NULL, new_name);
  if (norm_new_name == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      assert (error != NO_ERROR);
      goto error_exit;
    }

  /* check norm_new_name uniqueness */
  existing_con = classobj_find_constraint_by_name (sm_cons, norm_new_name);
  if (existing_con)
    {
      ERROR2 (error, ER_SM_INDEX_EXISTS, ctemplate->name, existing_con->name);
      goto error_exit;
    }

  property_type = classobj_map_constraint_to_property (sm_constraint->type);

  error = classobj_rename_constraint (ctemplate->properties, property_type, old_name, norm_new_name);
  if (error != NO_ERROR)
    {
      goto error_exit;
    }

  /* Rename foreign key ref in owner table. */
  if (sm_constraint->type == SM_CONSTRAINT_FOREIGN_KEY)
    {
      ref_clsop = ws_mop (&(sm_constraint->fk_info->ref_class_oid), NULL);
      if (ctemplate->op == ref_clsop)
    {
      /* Class references to itself. The below rename FK ref in properties of this class. */
      error = classobj_rename_foreign_key_ref (&(ctemplate->properties), btid, old_name, new_name);
    }
      else
    {
      /* Class references to another one (owner class). The below rename FK ref in owner class and update the owner
       * class. */
      error = sm_rename_foreign_key_ref (ref_clsop, btid, old_name, new_name);
    }

      if (error != NO_ERROR)
    {
      goto error_exit;
    }
    }

end:
  if (norm_new_name)
    {
      free_and_init (norm_new_name);
    }
  return error;

error_exit:
  goto end;
}

/*
 * rename_constraints_partitioned_class () - This function renames
 *                   constraints in sub-classes(partition classes).
 *   return: NO_ERROR on success, non-zero for ERROR
 *   ctemplate(in): sm_template of the super class (partition class)
 *   old_name(in): old name of constraint
 *   new_name(in): new name of constraint
 *   element_type(in): type of constraint
 */
static int
rename_constraints_partitioned_class (SM_TEMPLATE * ctemplate, const char *old_name, const char *new_name,
                      SM_CONSTRAINT_FAMILY element_type)
{
  int error = NO_ERROR;
  int i, is_partition = 0;
  MOP *sub_partitions = NULL;
  SM_TEMPLATE *sub_ctemplate = NULL;
  SM_CLASS_CONSTRAINT *sm_cons = NULL;

  assert (ctemplate != NULL);

  error = sm_partitioned_class_type (ctemplate->op, &is_partition, NULL, &sub_partitions);
  if (error != NO_ERROR)
    {
      goto error_exit;
    }

  if (is_partition == DB_PARTITION_CLASS)
    {
      error = ER_NOT_ALLOWED_ACCESS_TO_PARTITION;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
      goto error_exit;
    }
  else if (is_partition == DB_NOT_PARTITIONED_CLASS)
    {
      goto end;
    }

  assert (is_partition == DB_PARTITIONED_CLASS);

  for (i = 0; sub_partitions[i]; i++)
    {
      if (sm_exist_index (sub_partitions[i], old_name, NULL) != NO_ERROR)
    {
      continue;
    }

      sub_ctemplate = smt_edit_class_mop (sub_partitions[i], AU_INDEX);
      if (sub_ctemplate == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      assert (error != NO_ERROR);
      goto error_exit;
    }

      /* make a list of constraints that is included in the partitioned class. */
      error = classobj_make_class_constraints (sub_ctemplate->properties, sub_ctemplate->attributes, &sm_cons);
      if (error != NO_ERROR)
    {
      goto error_exit;
    }

      if (sm_cons == NULL)
    {
      error = ER_SM_CONSTRAINT_NOT_FOUND;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, old_name);
      goto error_exit;
    }

      error = rename_constraint (sub_ctemplate, sm_cons, old_name, new_name, element_type);
      if (error != NO_ERROR)
    {
      goto error_exit;
    }

      if (sm_cons)
    {
      classobj_free_class_constraints (sm_cons);
      sm_cons = NULL;
    }

      /* classobj_free_template() is included in sm_update_class() */
      error = sm_update_class (sub_ctemplate, NULL);
      if (error != NO_ERROR)
    {
      /* Even though sm_update() did not return NO_ERROR, sub_ctemplate is already freed */
      sub_ctemplate = NULL;
      goto error_exit;
    }
    }

end:
  if (sub_partitions != NULL)
    {
      free_and_init (sub_partitions);
    }
  return error;

error_exit:
  if (sm_cons)
    {
      classobj_free_class_constraints (sm_cons);
    }
  if (sub_ctemplate != NULL)
    {
      /* smt_quit() always returns NO_ERROR */
      smt_quit (sub_ctemplate);
    }
  goto end;
}

/*
 * smt_rename_constraint() - This function renames constraints in
 *                           sm_template.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   ctemplate(in): sm_template of the class
 *   old_name(in): old name of constraint
 *   new_name(in): new name of constraint
 *   element_type(in): type of constraint
 */

int
smt_rename_constraint (SM_TEMPLATE * ctemplate, const char *old_name, const char *new_name,
               SM_CONSTRAINT_FAMILY element_type)
{
  int error = NO_ERROR;
  SM_CLASS_CONSTRAINT *sm_cons = NULL;
  SM_CLASS_CONSTRAINT *sm_constraint = NULL;
  int is_global = 0;
  MOP owner;

  assert (ctemplate != NULL);

  owner = smt_find_owner_of_constraint (ctemplate, old_name);
  if (owner == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      return error;
    }

  if (owner != ctemplate->op)
    {
      /* it is inherited. */
      ERROR2 (error, ER_SM_INHERITED, old_name, sm_get_ch_name (owner));
      return error;
    }

  error = classobj_make_class_constraints (ctemplate->properties, ctemplate->attributes, &sm_cons);
  if (error != NO_ERROR)
    {
      goto error_exit;
    }

  if (sm_cons == NULL)
    {
      error = ER_SM_CONSTRAINT_NOT_FOUND;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, old_name);
      goto error_exit;
    }

  sm_constraint = classobj_find_constraint_by_name (sm_cons, old_name);
  if (sm_constraint == NULL)
    {
      error = ER_SM_CONSTRAINT_NOT_FOUND;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, old_name);
      goto error_exit;
    }

  error = sm_is_global_only_constraint (ctemplate->op, sm_constraint, &is_global, ctemplate);
  if (error != NO_ERROR)
    {
      goto error_exit;
    }

  /* The global constraint is not included in the partitioned class. */
  if (is_global == 0)
    {
      error = rename_constraints_partitioned_class (ctemplate, old_name, new_name, element_type);
      if (error != NO_ERROR)
    {
      goto error_exit;
    }
    }

  error = rename_constraint (ctemplate, sm_cons, old_name, new_name, element_type);
  if (error != NO_ERROR)
    {
      goto error_exit;
    }

end:
  if (sm_cons)
    {
      classobj_free_class_constraints (sm_cons);
    }

  return error;

  /* in order to show explicitly the error */
error_exit:
  goto end;
}
#endif /* ENABLE_RENAME_CONSTRAINT */

/*
 * change_constraints_comment_partitioned_class ()
 * - This function changes constraints comment in sub-classes(partition classes).
 *   return: NO_ERROR on success, non-zero for ERROR
 *   obj(in): database object of the super class (partition class)
 *   index_name(in): then name of constraint
 *   comment(in): the comment of constraint
 */
static int
change_constraints_comment_partitioned_class (MOP obj, const char *index_name, const char *comment)
{
  int error = NO_ERROR;
  int i, is_partition = 0;
  MOP *sub_partitions = NULL;
  SM_TEMPLATE *ctemplate = NULL;
  SM_CLASS_CONSTRAINT *cons;

  error = sm_partitioned_class_type (obj, &is_partition, NULL, &sub_partitions);
  if (error != NO_ERROR)
    {
      goto error_exit;
    }

  if (is_partition == DB_PARTITION_CLASS)
    {
      error = ER_NOT_ALLOWED_ACCESS_TO_PARTITION;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
      goto error_exit;
    }
  else if (is_partition == DB_NOT_PARTITIONED_CLASS)
    {
      goto end;
    }

  assert (is_partition == DB_PARTITIONED_CLASS);

  for (i = 0; sub_partitions[i]; i++)
    {
      ctemplate = smt_edit_class_mop (sub_partitions[i], AU_INDEX);
      if (ctemplate == NULL)
    {
      error = er_errid ();
      assert (error != NO_ERROR);
      goto error_exit;
    }

      cons = smt_find_constraint (ctemplate, index_name);
      if (cons == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto error_exit;
    }

      error = classobj_change_constraint_comment (ctemplate->properties, cons, comment);
      if (error != NO_ERROR)
    {
      goto error_exit;
    }

      /* classobj_free_template() is included in sm_update_class() */
      error = sm_update_class (ctemplate, NULL);
      if (error != NO_ERROR)
    {
      /* Even though sm_update() did not return NO_ERROR, ctemplate is already freed */
      ctemplate = NULL;
      goto error_exit;
    }
    }

end:
  if (sub_partitions != NULL)
    {
      free_and_init (sub_partitions);
    }
  return error;

error_exit:
  if (ctemplate != NULL)
    {
      /* smt_quit() always returns NO_ERROR */
      smt_quit (ctemplate);
    }
  goto end;
}

/*
 * smt_change_constraint_comment() - This function change comment of index/constraints in sm_template.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   ctemplate(in): sm_template of the class
 *   index_name(in): the name of constraint
 *   comment(in): new comment of constraint
 */
int
smt_change_constraint_comment (SM_TEMPLATE * ctemplate, const char *index_name, const char *comment)
{
  SM_CLASS_CONSTRAINT *cons = NULL;
  int error = NO_ERROR;

  assert (ctemplate != NULL && ctemplate->op != NULL);

  cons = smt_find_constraint (ctemplate, index_name);
  if (cons == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      return error;
    }

  error = change_constraints_comment_partitioned_class (ctemplate->op, index_name, comment);
  if (error != NO_ERROR)
    {
      return error;
    }

  error = classobj_change_constraint_comment (ctemplate->properties, cons, comment);
  if (error != NO_ERROR)
    {
      return error;
    }

  return NO_ERROR;
}

/* TEMPLATE DELETION FUNCTIONS */

/*
 * check_alias_delete() - Work function for smt_delete_any.
 *    Here when a component of the given name could not be found.
 *    Check to see if there are any alias resolutions with the
 *    name and if so, remove the resolution.  Since an alias cannot have
 *    the same name as a normal component, this means that smt_delete_any
 *    can be used to remove resolution aliases as well.
 *    The error code will already have been set here, if an alias is found,
 *    set it back to NO_ERROR.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in): schema template
 *   name(in): component name
 *   name_space(in): class or instance name_space identifier
 *   error(in): current error code (may be changed to NO_ERROR)
 */

static int
check_alias_delete (SM_TEMPLATE * template_, const char *name, SM_NAME_SPACE name_space, int error)
{
  SM_RESOLUTION **reslist, *res;

  if (name_space == ID_INSTANCE)
    {
      reslist = &template_->resolutions;
    }
  else
    {
      reslist = &template_->class_resolutions;
    }

  res = find_alias (*reslist, name, name_space);
  if (res != NULL)
    {
      WS_LIST_REMOVE (reslist, res);
      classobj_free_resolution (res);
      /* reset the error since we'll drop the alias instead */
      error = NO_ERROR;
    }

  return error;
}

/*
 * smt_delete_any() - This is the primary function for deletion of all types of
 *    attributes, methods and resolution aliases from a template.
 *    It can be used to delete specific component types by passing
 *    the specific name_space identifiers (ID_ATTRIBUTE etc.) or it can
 *    be used for the broader namespaces (ID_INSTANCE, ID_CLASS) to delete
 *    any sort of component. The attribute that is a member of primary key
 *    can't be deleted.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   name(in): component name
 *   name_space(in): component name_space identifier
 */

int
smt_delete_any (SM_TEMPLATE * template_, const char *name, SM_NAME_SPACE name_space)
{
  int error = NO_ERROR;
  SM_ATTRIBUTE *att;
  SM_METHOD *method;
  SM_COMPONENT *thing;

  switch (name_space)
    {
    case ID_ATTRIBUTE:
      error = smt_find_attribute (template_, name, false, &att);
      if (error == NO_ERROR)
    {
      if (att->header.name_space == ID_SHARED_ATTRIBUTE)
        {
          ERROR1 (error, ER_SM_ATTRIBUTE_NOT_FOUND, name);
        }
      else
        {
          if (att->flags & SM_ATTFLAG_PRIMARY_KEY)
        {
          ERROR1 (error, ER_SM_ATTRIBUTE_PRIMARY_KEY_MEMBER, name);
        }
          else
        {
          WS_LIST_REMOVE (&template_->attributes, att);
          classobj_free_attribute (att);
        }
        }
    }
      else
    {
      error = check_alias_delete (template_, name, ID_INSTANCE, error);
    }
      break;

    case ID_SHARED_ATTRIBUTE:
      error = smt_find_attribute (template_, name, false, &att);
      if (error == NO_ERROR)
    {
      if (att->header.name_space != ID_SHARED_ATTRIBUTE)
        {
          ERROR1 (error, ER_SM_ATTRIBUTE_NOT_FOUND, name);
        }
      else
        {
          WS_LIST_REMOVE (&template_->attributes, att);
          classobj_free_attribute (att);
        }
    }
      else
    {
      error = check_alias_delete (template_, name, ID_INSTANCE, error);
    }
      break;

    case ID_CLASS_ATTRIBUTE:
      error = smt_find_attribute (template_, name, true, &att);
      if (error == NO_ERROR)
    {
      WS_LIST_REMOVE (&template_->class_attributes, att);
      classobj_free_attribute (att);
    }
      else
    {
      error = check_alias_delete (template_, name, ID_CLASS, error);
    }
      break;

    case ID_METHOD:
      error = find_method (template_, name, false, &method);
      if (error == NO_ERROR)
    {
      WS_LIST_REMOVE (&template_->methods, method);
      classobj_free_method (method);
    }
      else
    {
      error = check_alias_delete (template_, name, ID_INSTANCE, error);
    }
      break;

    case ID_CLASS_METHOD:
      error = find_method (template_, name, true, &method);
      if (error == NO_ERROR)
    {
      WS_LIST_REMOVE (&template_->class_methods, method);
      classobj_free_method (method);
    }
      else
    {
      error = check_alias_delete (template_, name, ID_CLASS, error);
    }
      break;

    case ID_INSTANCE:
      /* look at both attributes and methods for a name match */
      error = find_any (template_, name, false, &thing);
      if (error == NO_ERROR)
    {
      if (thing->name_space == ID_METHOD)
        {
          method = (SM_METHOD *) thing;
          WS_LIST_REMOVE (&template_->methods, method);
          classobj_free_method (method);
        }
      else if (thing->name_space == ID_ATTRIBUTE || thing->name_space == ID_SHARED_ATTRIBUTE)
        {
          att = (SM_ATTRIBUTE *) thing;
          if (att->flags & SM_ATTFLAG_PRIMARY_KEY)
        {
          ERROR1 (error, ER_SM_ATTRIBUTE_PRIMARY_KEY_MEMBER, name);
        }
          else
        {
          WS_LIST_REMOVE (&template_->attributes, att);
          classobj_free_attribute (att);
        }
        }
    }
      else
    {
      error = check_alias_delete (template_, name, ID_INSTANCE, error);
    }
      break;

    case ID_CLASS:
      /* look at both attributes and methods for a name match */
      error = find_any (template_, name, true, &thing);
      if (error == NO_ERROR)
    {
      if (thing->name_space == ID_CLASS_METHOD)
        {
          method = (SM_METHOD *) thing;
          WS_LIST_REMOVE (&template_->class_methods, method);
          classobj_free_method (method);
        }
      else if (thing->name_space == ID_CLASS_ATTRIBUTE)
        {
          att = (SM_ATTRIBUTE *) thing;
          WS_LIST_REMOVE (&template_->class_attributes, att);
          classobj_free_attribute (att);
        }
    }
      else
    {
      error = check_alias_delete (template_, name, ID_CLASS, error);
    }
      break;

    default:
      ERROR0 (error, ER_SM_INVALID_ARGUMENTS);
      break;
    }

  return error;
}

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * smt_delete()
 * smt_class_delete() - These are type specific deletion functions.
 *    They all call smt_delete_any with appropriate arguments.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   name(in): component name
 */

int
smt_delete (SM_TEMPLATE * template_, const char *name)
{
  return (smt_delete_any (template_, name, ID_INSTANCE));
}

int
smt_class_delete (SM_TEMPLATE * template_, const char *name)
{
  return (smt_delete_any (template_, name, ID_CLASS));
}
#endif /* ENABLE_UNUSED_FUNCTION */

/* TEMPLATE SUPERCLASS FUNCTIONS */
/*
 * smt_add_super() - Adds a super class to the class being edited.
 *    The checking for complex hierarchy cycles is not done here but deferred
 *    until sm_update_class.  This is because the check may be fairly
 *    complex and require a lot of locks.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   super_class(in): super class to add
 */

int
smt_add_super (SM_TEMPLATE * template_, MOP super_class)
{
  int error = NO_ERROR;

  if (ml_find (template_->inheritance, super_class))
    {
      ERROR0 (error, ER_SM_SUPER_CLASS_EXISTS);
    }
  else if ((template_->op != NULL) && (template_->op == super_class))
    {
      ERROR0 (error, ER_SM_SUPER_CAUSES_CYCLES);
    }
  else
    {
      if (template_->class_type == SM_CLASS_CT)
    {
      int is_class = 0;

      is_class = db_is_class (super_class);
      if (is_class < 0)
        {
          error = is_class;
        }
      else if (!is_class)
        {
          ERROR2 (error, ER_SM_INCOMPATIBLE_SUPER_CLASS, db_get_class_name (super_class), template_->name);
        }
    }
      if (error == NO_ERROR)
    {
      if (template_->class_type == SM_VCLASS_CT)
        {
          int is_vclass = 0;

          is_vclass = db_is_vclass (super_class);
          if (is_vclass < 0)
        {
          error = is_vclass;
        }
          if (!is_vclass)
        {
          ERROR2 (error, ER_SM_INCOMPATIBLE_SUPER_CLASS, db_get_class_name (super_class), template_->name);
        }
        }
      if (error == NO_ERROR)
        {
          error = ml_append (&template_->inheritance, super_class, NULL);
        }
    }
    }

  return error;
}

/*
 * smt_delete_super() - Remove a super class from the class being edited.
 *    The class loses the definitions of the superclass and any super classes
 *    of the dropped superclass.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   super_class(in): super class to drop
 */

int
smt_delete_super (SM_TEMPLATE * template_, MOP super_class)
{
  int error = NO_ERROR;

  if (!ml_remove (&template_->inheritance, super_class))
    {
      ERROR0 (error, ER_SM_SUPER_NOT_FOUND);
    }

  return error;
}

/*
 * smt_delete_super_connect() - This removes a super class from the class being
 *    edited but in addition automatically connects any super classes of
 *    the dropped class to the class being edited.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   super_class(in): super class to drop
 */

int
smt_delete_super_connect (SM_TEMPLATE * template_, MOP super_class)
{
  int error = NO_ERROR;
  SM_CLASS *class_;
  DB_OBJLIST *s;

  if (!ml_remove (&template_->inheritance, super_class))
    {
      ERROR0 (error, ER_SM_SUPER_NOT_FOUND);
    }
  else
    {
      /* connect all of the super supers and install its resolutions */
      if ((error = au_fetch_class (super_class, &class_, AU_FETCH_WRITE, AU_SELECT)) == NO_ERROR)
    {
      /* add super supers */
      for (s = class_->inheritance; s != NULL && !error; s = s->next)
        {
          error = ml_append (&template_->inheritance, s->op, NULL);
        }

      /* It is unclear what the semantics of inheriting resolutions are force the user to respecify resolutions for
       * conflicts on super supers */
    }
    }

  return error;
}

/* TEMPLATE METHOD FILE FUNCTIONS */

/*
 * smt_add_method_file() - Adds a method file name to a template.
 *    The name must be a valid operating system path name and will be
 *    passed to the "ld" function by the dymanic linker.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   filename(in): method file name
 */

int
smt_add_method_file (SM_TEMPLATE * template_, const char *filename)
{
  int error = NO_ERROR;
  SM_METHOD_FILE *mfile;

  /* should be detecting use of an inherited file and return an error ! */

  /* method file names are case sensitive */
  if (NLIST_FIND (template_->method_files, filename) == NULL)
    {
      mfile = classobj_make_method_file (filename);
      if (mfile == NULL)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }
      mfile->class_mop = template_->op;
      WS_LIST_APPEND (&template_->method_files, mfile);
    }

  return error;
}

/*
 * smt_reset_method_files() - Clear the method file list of a template.
 *    Useful if the method file list is to be completely re-defined.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 */

int
smt_reset_method_files (SM_TEMPLATE * template_)
{
  int error = NO_ERROR;

  WS_LIST_FREE (template_->method_files, classobj_free_method_file);
  template_->method_files = NULL;

  return error;
}

/*
 * smt_drop_method_file() - Removes a method file from a template.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   name(in): method file name
 */

int
smt_drop_method_file (SM_TEMPLATE * template_, const char *name)
{
  int error = NO_ERROR;
  SM_METHOD_FILE *file, *prev, *found;

  for (file = template_->method_files, prev = NULL, found = NULL; file != NULL && found == NULL; file = file->next)
    {
      if (strcmp (file->name, name) == 0)
    {
      found = file;
    }
      else
    {
      prev = file;
    }
    }

  if (found == NULL)
    {
      error = ER_SM_METHOD_FILE_NOT_FOUND;
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 1, name);
    }
  else
    {
      if (prev == NULL)
    {
      template_->method_files = found->next;
    }
      else
    {
      prev->next = found->next;
    }
      classobj_free_method_file (found);
    }

  return error;
}

/*
 * smt_rename_method_file() - Drops the old file name and adds new in its place
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   old_name(in): old method file name
 *   new_name(in): new method file name
 */

int
smt_rename_method_file (SM_TEMPLATE * template_, const char *old_name, const char *new_name)
{
  int error = NO_ERROR;
  SM_METHOD_FILE *file, *prev, *found;

  for (file = template_->method_files, prev = NULL, found = NULL; file != NULL && found == NULL; file = file->next)
    {
      if (strcmp (file->name, old_name) == 0)
    {
      found = file;
    }
      else
    {
      prev = file;
    }
    }

  if (found == NULL)
    {
      error = ER_SM_METHOD_FILE_NOT_FOUND;
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 1, old_name);
    }
  else
    {
      ws_free_string (found->name);
      found->name = ws_copy_string (new_name);
      if (found->name == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
    }
    }

  return error;
}

/*
 * smt_set_loader_commands() - This is used to add a "command" string for the
 *    dynamic loader. This is passed in the command line to the call to "ld".
 *    It is intended to hold things like common library lists or other
 *    linker flags necessary for linking with the method files for a
 *    class.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   commands(in): string of commands to the dynamic loader
 */

int
smt_set_loader_commands (SM_TEMPLATE * template_, const char *commands)
{
  ws_free_string (template_->loader_commands);
  template_->loader_commands = ws_copy_string (commands);

  return NO_ERROR;
}

/* TEMPLATE RESOLUTION FUNCTIONS */
/*
 * check_resolution_name() - Work function for add_resolution, could be used
 *    in other places as well. This makes sure that a class has an attribute
 *    or method with the given name.
 *   return: non-zero if name was found
 *   classmop(in): class that must have named thing
 *   name(in): the name to look for
 *   class_name(in): identifies class names or instance names
 */

static int
check_resolution_name (MOP classmop, const char *name, int class_name)
{
  SM_CLASS *class_;
  SM_COMPONENT *thing;

  thing = NULL;
  if (au_fetch_class (classmop, &class_, AU_FETCH_READ, AU_SELECT) == NO_ERROR)
    {
      thing = classobj_find_component (class_, name, class_name);
    }

  return ((thing == NULL) ? 0 : 1);
}

/*
 * check_local_definition() - Work function for add_resolution.  Check to see
 *    if there are any locally defined components (attributes or methods)
 *    that must override the use of any resolution specifiers that reference
 *    an inherited component with the same name.
 *    e.g. you can't say "inherit x from foo"
 *    if there is already a definition for attribute x in subclass being
 *    editined.  Local definitions always take precidence over inherited
 *    definitions.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in): class definition template
 *   name(in): component name
 *   alias(in): optional alias name
 *   namespace(in): component name_space
 */

static int
check_local_definition (SM_TEMPLATE * template_, const char *name, const char *alias, SM_NAME_SPACE name_space)
{
  int error = NO_ERROR;
  SM_COMPONENT *comp;
  SM_NAME_SPACE rspace;
  int class_stuff;

  rspace = sm_resolution_space (name_space);
  class_stuff = (rspace == ID_CLASS) ? 1 : 0;

  if (alias == NULL)
    {
      comp = find_component (template_, name, class_stuff);
      if (comp != NULL)
    {
      /* Can't request inheritance of "name", it is defined locally in the class */
      ERROR1 (error, ER_SM_RESOLUTION_COMPONENT_EXISTS, name);
      return error;
    }
    }
  else
    {
      comp = find_component (template_, alias, class_stuff);
      if (comp != NULL)
    {
      /* Can't use "alias" as an alias for inherited component "name", there is already a locally defined component
       * with that name */
      ERROR2 (error, ER_SM_ALIAS_COMPONENT_EXISTS, alias, name);
      return error;
    }
    }

  return NO_ERROR;
}

/*
 * add_resolution() - Add a resolution specifier to a template.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   super_class(in): super_class with conflicting component
 *   name(in): name of conflicting component
 *   alias(in): optional alias name (can be NULL)
 *   namespace(in): class or instance name_space identifier
 */

static int
add_resolution (SM_TEMPLATE * template_, MOP super_class, const char *name, const char *alias, SM_NAME_SPACE name_space)
{
  int error = NO_ERROR;
  SM_RESOLUTION **reslist, *res, *chosen, *prev_alias;
  char realname[SM_MAX_IDENTIFIER_LENGTH];
  char realalias[SM_MAX_IDENTIFIER_LENGTH];

  if (name == NULL)
    {
      ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
      return error;
    }

  /* hack, if the alias name is the same as the component name, get rid of it.  The system won't allow aliases that
   * have the same name as an inherited component (i.e. you can't shadow with an alias). */
  if (alias != NULL && SM_COMPARE_NAMES (name, alias) == 0)
    {
      alias = NULL;
    }

  if (alias != NULL && !sm_check_name (alias))
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
    }
  else
    {
      sm_downcase_name (name, realname, SM_MAX_IDENTIFIER_LENGTH);
      name = realname;

      if (alias != NULL)
    {
      sm_downcase_name (alias, realalias, SM_MAX_IDENTIFIER_LENGTH);
      alias = realalias;
    }

      error = check_local_definition (template_, name, alias, name_space);
      if (error == NO_ERROR)
    {

      /* make sure the super class actually has a component with this name */
      if (!check_resolution_name (super_class, name, (name_space == ID_CLASS) ? 1 : 0))
        {
          /* need "attribute or method" not found */
          ERROR1 (error, ER_SM_ATTRIBUTE_NOT_FOUND, name);
        }
      else
        {
          if (name_space == ID_INSTANCE)
        {
          reslist = &template_->resolutions;
        }
          else
        {
          reslist = &template_->class_resolutions;
        }

          /* look for an explicit resolution from a this or a different class, place this in "chosen". also look
           * for an already existing alias on this component */
          chosen = NULL;
          prev_alias = NULL;
          for (res = *reslist; res != NULL; res = res->next)
        {
          if (SM_COMPARE_NAMES (res->name, name) == 0)
            {
              if (res->alias == NULL)
            {
              chosen = res;
            }
              else if (res->class_mop == super_class)
            {
              prev_alias = res;
            }
            }
        }

          if (alias != NULL)
        {
          /* we're trying to set up an alias */
          if (chosen != NULL && chosen->class_mop == super_class)
            {
              /* a resolution on this class previously existed without an alias, give it the specified alias */
              chosen->alias = ws_copy_string (alias);
              if (chosen->alias == NULL)
            {
              assert (er_errid () != NO_ERROR);
              error = er_errid ();
            }
            }
          else if (prev_alias != NULL)
            {
              /* a resolution on this class previously existed with an alias, replace the old alias with the
               * new one */
              db_ws_free ((char *) prev_alias->alias);
              prev_alias->alias = ws_copy_string (alias);
              if (prev_alias->alias == NULL)
            {
              assert (er_errid () != NO_ERROR);
              error = er_errid ();
            }
            }
          else
            {
              SM_RESOLUTION *res;
              res = classobj_make_resolution (super_class, name, alias, name_space);
              if (res == NULL)
            {
              assert (er_errid () != NO_ERROR);
              error = er_errid ();
            }
              /* we need to add a new entry with the alias */
              WS_LIST_APPEND (reslist, res);
            }
        }
          else
        {
          /* we're trying to make a specific attribute selection */
          if (chosen == NULL)
            {
              if (prev_alias == NULL)
            {
              SM_RESOLUTION *res;
              res = classobj_make_resolution (super_class, name, alias, name_space);
              if (res == NULL)
                {
                  assert (er_errid () != NO_ERROR);
                  error = er_errid ();
                }
              /* we need to add a new entry */
              WS_LIST_APPEND (reslist, res);
            }
              else
            {
              /* remove the old alias */
              db_ws_free ((char *) prev_alias->alias);
              prev_alias->alias = NULL;
            }
            }
          else
            {
              /* change the chosen class */
              chosen->class_mop = super_class;
              if (prev_alias != NULL)
            {
              /* free the old alias */
              WS_LIST_REMOVE (reslist, prev_alias);
              classobj_free_resolution (prev_alias);
            }
            }
        }
        }
    }
    }

  return error;
}

/*
 * delete_resolution() - Removes a resolution from a template that matches
 *    the supplied parameters.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   super_class(in): class with resolution
 *   name(in): component name of resolution
 *   namespace(in): class or instance name_space identifier
 */

static int
delete_resolution (SM_TEMPLATE * template_, MOP super_class, const char *name, SM_NAME_SPACE name_space)
{
  int error = NO_ERROR;
  SM_RESOLUTION **reslist, *res;

  if (name_space == ID_INSTANCE)
    {
      reslist = &template_->resolutions;
    }
  else
    {
      reslist = &template_->class_resolutions;
    }

  res = classobj_find_resolution (*reslist, super_class, name, name_space);
  if (res == NULL)
    {
      ERROR1 (error, ER_SM_RESOLUTION_NOT_FOUND, name);
    }
  else
    {
      WS_LIST_REMOVE (reslist, res);
      classobj_free_resolution (res);
    }
  return (error);
}

/*
 * smt_add_resolution()
 * smt_add_class_resolution() - Add a resolution to a template.
 *    These are name_space specific functions that call add_resolution
 *    to do the actual work.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   super_class(in): super class with conflicting component
 *   name(in): name of conflicting component
 *   alias(in): optional alias name (can be NULL)
 */

int
smt_add_resolution (SM_TEMPLATE * template_, MOP super_class, const char *name, const char *alias)
{
  return (add_resolution (template_, super_class, name, alias, ID_INSTANCE));
}

int
smt_add_class_resolution (SM_TEMPLATE * template_, MOP super_class, const char *name, const char *alias)
{
  return (add_resolution (template_, super_class, name, alias, ID_CLASS));
}

/*
 * smt_delete_resolution()
 * smt_delete_class_resolution() - Removes the resolution specifier for a
 *    component of a particular super class.
 *    These are name_space specific functions that call delete_resolution
 *    to do the actual work.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   super_class(in): super class with conflicting component
 *   name(in): name of conflicting component
 */

int
smt_delete_resolution (SM_TEMPLATE * template_, MOP super_class, const char *name)
{
  return delete_resolution (template_, super_class, name, ID_INSTANCE);
}

int
smt_delete_class_resolution (SM_TEMPLATE * template_, MOP super_class, const char *name)
{
  return delete_resolution (template_, super_class, name, ID_CLASS);
}

/* TEMPLATE POPULATE FUNCTIONS */
/*
 * smt_add_query_spec() - Adds a query specification to a template.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   specification(in): query specification
 */

int
smt_add_query_spec (SM_TEMPLATE * template_, const char *specification)
{
  int error = NO_ERROR;
  SM_QUERY_SPEC *query_spec;
  SM_CLASS_TYPE ct;

  query_spec = classobj_make_query_spec (specification);

  if (query_spec == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
    }
  else
    {
      ct = template_->class_type;
      if (ct == SM_VCLASS_CT)
    {
      WS_LIST_APPEND (&template_->query_spec, query_spec);
    }
      else
    {
      db_ws_free (query_spec);
      error = ER_SM_INVALID_CLASS;
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 0);
    }
    }

  return error;
}

/*
 * smt_reset_query_spec() - Clears the query_spec list of a template.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 */

int
smt_reset_query_spec (SM_TEMPLATE * template_)
{
  int error = NO_ERROR;

  WS_LIST_FREE (template_->query_spec, classobj_free_query_spec);
  template_->query_spec = NULL;

  return error;
}

/*
 * smt_drop_query_spec() - Removes a query_spec from a template.
 *   return: NO_ERROR on success, non-zero for ERROR
 *   def(in/out): schema template
 *   index(in): 1 relative index of query_spec specification to drop
 */

int
smt_drop_query_spec (SM_TEMPLATE * def, const int index)
{
  int error = NO_ERROR;
  SM_QUERY_SPEC *file, *prev, *found;
  int i;
  char indexname[20];

  for (file = def->query_spec, prev = NULL, found = NULL, i = 1; file != NULL && found == NULL; file = file->next, i++)
    {
      if (index == i)
    {
      found = file;
    }
      else
    {
      prev = file;
    }
    }

  if (found == NULL)
    {
      error = ER_SM_QUERY_SPEC_NOT_FOUND;
      sprintf (indexname, "%d", index);
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 1, indexname);
    }
  else
    {
      if (prev == NULL)
    {
      def->query_spec = found->next;
    }
      else
    {
      prev->next = found->next;
    }
      classobj_free_query_spec (found);
    }

  return error;
}

/* VIRTUAL SCHEMA OPERATION TEMPLATE FUNCTIONS */
/*
 * smt_def_typed_class() - Begin the definition of a new virtual class.
 * Creates an empty template.  The class name is not registered at this
 * time.  It will be registered during sm_update_class
 *   return: template
 *   name(in):
 *   ct(in):
 */

SM_TEMPLATE *
smt_def_typed_class (const char *name, SM_CLASS_TYPE ct)
{
  return def_class_internal (name, ct);
}

/*
 * smt_get_class_type() - Return the type of a class template
 *   return: class type
 *   template(in):
 */

SM_CLASS_TYPE
smt_get_class_type (SM_TEMPLATE * template_)
{
  return template_->class_type;
}

/*
 * smt_get_class_type() - Convenience function to return the type of class,
 *   that is whether, a virtual class, component class or a view
 *   return: class type
 *   class(in):
 */

SM_CLASS_TYPE
sm_get_class_type (SM_CLASS * class_)
{
  return class_->class_type;
}

/*
 * smt_change_query_spec()
 *   return: NO_ERROR on success, non-zero for ERROR
 *   def(in/out):
 *   query(in):
 *   index(in):
 */

int
smt_change_query_spec (SM_TEMPLATE * def, const char *query, const int index)
{
  int error = NO_ERROR;
  SM_QUERY_SPEC *file, *prev, *found;
  int i;
  char indexname[20];

  for (file = def->query_spec, prev = NULL, found = NULL, i = 1; file != NULL && found == NULL; file = file->next, i++)
    {
      if (index == i)
    {
      found = file;
    }
      else
    {
      prev = file;
    }
    }

  if (found == NULL)
    {
      error = ER_SM_QUERY_SPEC_NOT_FOUND;
      sprintf (indexname, "%d", index);
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 1, indexname);
    }
  else
    {
      if (prev == NULL)
    {
      def->query_spec = classobj_make_query_spec (query);
      if (def->query_spec == NULL)
        {
          assert (er_errid () != NO_ERROR);
          return er_errid ();
        }
      def->query_spec->next = found->next;
    }
      else
    {
      prev->next = classobj_make_query_spec (query);
      if (prev->next == NULL)
        {
          assert (er_errid () != NO_ERROR);
          return er_errid ();
        }
      prev->next->next = found->next;
    }

      classobj_free_query_spec (found);
    }

  return (error);
}

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * smt_downcase_all_class_info()
 *   return: none
 */

void
smt_downcase_all_class_info (void)
{
  SM_CLASS *class_;
  SM_ATTRIBUTE *a;
  SM_METHOD *m;
  SM_RESOLUTION *r;
  LIST_MOPS *lmops;
  int c;
  char name_buf[SM_MAX_IDENTIFIER_LENGTH];

  lmops = locator_get_all_mops (sm_Root_class_mop, DB_FETCH_QUERY_WRITE, NULL);
  if (lmops != NULL)
    {

      for (c = 0; c < lmops->num; c++)
    {
      class_ = (SM_CLASS *) locator_fetch_class (lmops->mops[c], DB_FETCH_WRITE);

      if (class_ == NULL)
        {
          return;
        }

      FOR_ATTRIBUTES (class_->attributes, a) DOWNCASE_NAME (a->header.name, name_buf);

      FOR_ATTRIBUTES (class_->shared, a) DOWNCASE_NAME (a->header.name, name_buf);

      FOR_ATTRIBUTES (class_->class_attributes, a) DOWNCASE_NAME (a->header.name, name_buf);

      FOR_METHODS (class_->methods, m) DOWNCASE_NAME (m->header.name, name_buf);

      FOR_METHODS (class_->class_methods, m) DOWNCASE_NAME (m->header.name, name_buf);

      for (r = class_->resolutions; r != NULL; r = r->next)
        {
          DOWNCASE_NAME (r->name, name_buf);
          DOWNCASE_NAME (r->alias, name_buf);
        }
      ws_dirty (lmops->mops[c]);
    }
      locator_free_list_mops (lmops);
    }
}
#endif

/*
 * smt_change_attribute() - Changes an attribute of a template (name, domain
 *    and ordering).
 *    For class and shared atribute the value is changed according to new
 *    domain. For normal attribute, the instance values are not changed, only
 *    the schema modification is performed.
 *    The new domain may be specified either with a string or a DB_DOMAIN *.
 *    If new_domain is not NULL, it is used.  Otherwise new_domain_string is
 *    used.
 *    The attribute ordering may be changed if either the "change_first"
 *    argument is "true" or "change_after_attribute" is non-null and contains
 *    the name of an existing attribute.
 *    If all operations are successful, the changed attribute is returned in
 *    "found_att".
 *
 *   return: NO_ERROR on success, non-zero for ERROR
 *   template(in/out): schema template
 *   name(in): attribute current name
 *   new_name(in): attribute new name (may be NULL if unchanged)
 *   new_domain_string(in): new domain name string
 *   new_domain(in): new domain
 *   name_space(in): class, share or normal attribute
 *   change_first(in): the attribute will become the first in the attributes
 *                     list
 *   change_after_attribute(in): the attribute will be repositioned
 *                               after the attribute with the given name
 *   found_att(out) : the new attribute if successfully changed
 */
static int
smt_change_attribute (SM_TEMPLATE * template_, const char *name, const char *new_name, const char *new_domain_string,
              DB_DOMAIN * new_domain, const SM_NAME_SPACE name_space, const bool change_first,
              const char *change_after_attribute, SM_ATTRIBUTE ** found_att)
{
  int error_code = NO_ERROR;
  SM_ATTRIBUTE *att = NULL;
  SM_ATTRIBUTE **att_list = NULL;
  char real_name[SM_MAX_IDENTIFIER_LENGTH] = { 0 };
  char real_new_name[SM_MAX_IDENTIFIER_LENGTH] = { 0 };
  char change_after_attribute_real_name[SM_MAX_IDENTIFIER_LENGTH] = { 0 };

  assert (template_ != NULL);

  if (name_space == ID_CLASS_ATTRIBUTE)
    {
      att_list = &template_->class_attributes;
    }
  else
    {
      att_list = &template_->attributes;
    }

  sm_downcase_name (name, real_name, SM_MAX_IDENTIFIER_LENGTH);
  name = real_name;
  if (!sm_check_name (name))
    {
      assert (er_errid () != NO_ERROR);
      error_code = er_errid ();
      goto error_exit;
    }

  if (new_name != NULL)
    {
      sm_downcase_name (new_name, real_new_name, SM_MAX_IDENTIFIER_LENGTH);
      new_name = real_new_name;

      if (!sm_check_name (new_name))
    {
      assert (er_errid () != NO_ERROR);
      error_code = er_errid ();
      goto error_exit;
    }
    }

  if (change_after_attribute != NULL)
    {
      sm_downcase_name (change_after_attribute, change_after_attribute_real_name, SM_MAX_IDENTIFIER_LENGTH);
      change_after_attribute = change_after_attribute_real_name;
    }

  if (new_name != NULL)
    {
      error_code = check_namespace (template_, new_name, (name_space == ID_CLASS_ATTRIBUTE) ? true : false);
      if (error_code != NO_ERROR)
    {
      goto error_exit;
    }
    }

  error_code = get_domain (template_, new_domain_string, &new_domain);
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }

  if (new_domain == NULL)
    {
      ERROR0 (error_code, ER_SM_INVALID_ARGUMENTS);
      goto error_exit;
    }

  if (TP_DOMAIN_TYPE (new_domain) == DB_TYPE_OBJECT)
    {
      error_code = check_domain_class_type (template_, new_domain->class_mop);
      if (error_code != NO_ERROR)
    {
      goto error_exit;
    }
    }

  error_code = smt_find_attribute (template_, name, (name_space == ID_CLASS_ATTRIBUTE) ? 1 : 0, &att);
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }

  assert (att != NULL);
  *found_att = att;


  if (name_space == ID_CLASS_ATTRIBUTE || name_space == ID_SHARED_ATTRIBUTE)
    {
      /* change the value according to new domain */
      error_code = smt_change_class_shared_attribute_domain (att, new_domain);
      if (error_code != NO_ERROR)
    {
      goto error_exit;
    }
    }
  else
    {
      assert (name_space == ID_ATTRIBUTE);
    }

  att->type = new_domain->type;
  att->domain = new_domain;

  /* change name */
  if (new_name != NULL)
    {
      error_code = check_namespace (template_, new_name, (name_space == ID_CLASS_ATTRIBUTE) ? true : false);
      if (error_code != NO_ERROR)
    {
      goto error_exit;
    }

      ws_free_string (att->header.name);
      att->header.name = ws_copy_string (new_name);
      if (att->header.name == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error_code = er_errid ();
      goto error_exit;
    }
    }
  /* change order */
  if (change_first || change_after_attribute != NULL)
    {
      error_code = smt_change_attribute_pos_in_list (att_list, att, change_first, change_after_attribute);
    }
  return error_code;

error_exit:

  return error_code;
}

/*
 * smt_change_attribute_w_dflt_w_order()
 *   return: NO_ERROR on success, non-zero for ERROR
 *   def(in/out):
 *   name(in): attribute name
 *   new_name(in): attribute's new name, otherwise NULL
 *   domain_string(in): domain name string
 *   domain(in): domain
 *   name_space(in): class, shared or normal attribute
 *   new_default_value(in): default value
 *   new_default_expr(in): default expression
 *   new_on_update_expr(in): on_update default expression
 *   change_first(in): the attribute should be added at the beginning of the attributes list
 *   change_after_attribute(in): the attribute should be added in the attributes list
 *                               after the attribute with the given name
 *   found_att(out) : the new attribute if successfully changed
 */
int
smt_change_attribute_w_dflt_w_order (DB_CTMPL * def, const char *name, const char *new_name,
                     const char *new_domain_string, DB_DOMAIN * new_domain,
                     const SM_NAME_SPACE name_space, DB_VALUE * new_default_value,
                     DB_DEFAULT_EXPR * new_default_expr, DB_DEFAULT_EXPR_TYPE on_update_expr,
                     const bool change_first, const char *change_after_attribute,
                     SM_ATTRIBUTE ** found_att)
{
  int error = NO_ERROR;
  int is_class_attr;
  DB_VALUE *orig_value = NULL;
  DB_VALUE *new_orig_value = NULL;
  DB_VALUE default_value;
  DB_DEFAULT_EXPR default_expr;
  TP_DOMAIN_STATUS status;

  *found_att = NULL;
  error = smt_change_attribute (def, name, new_name, new_domain_string, new_domain, name_space, change_first,
                change_after_attribute, found_att);
  if (error != NO_ERROR)
    {
      return error;
    }
  if (*found_att == NULL)
    {
      assert (false);
      ERROR1 (error, ER_UNEXPECTED, "Attribute not found.");
      return error;
    }

  /* 
     The default value's domain should be checked even though the new default is not specified
   */
  db_make_null (&default_value);
  if (new_default_value == NULL && new_default_expr->default_expr_type == DB_DEFAULT_NONE)
    {
      pr_clone_value (&(*found_att)->default_value.value, &default_value);
      default_expr = (*found_att)->default_value.default_expr;

      if (!DB_IS_NULL (&default_value))
    {
      new_default_value = &default_value;
    }

      if (default_expr.default_expr_type != DB_DEFAULT_NONE)
    {
      new_default_expr = &default_expr;
    }
    }

  is_class_attr = (name_space == ID_CLASS_ATTRIBUTE);
  if (new_default_value != NULL || (new_default_expr != NULL && new_default_expr->default_expr_type != DB_DEFAULT_NONE))
    {
      assert (((*found_att)->flags & SM_ATTFLAG_NEW) == 0);
      error = smt_set_attribute_default (def, ((new_name != NULL) ? new_name : name), is_class_attr, new_default_value,
                     new_default_expr);

      db_value_clear (&default_value);
      if (error != NO_ERROR)
    {
      return error;
    }
    }

  if (on_update_expr != DB_DEFAULT_NONE)
    {
      error = smt_set_attribute_on_update (def, ((new_name != NULL) ? new_name : name), is_class_attr, on_update_expr);
      if (error != NO_ERROR)
    {
      return error;
    }
    }

  /* change original default : continue only for normal attributes */
  if (name_space == ID_CLASS_ATTRIBUTE || name_space == ID_SHARED_ATTRIBUTE)
    {
      assert (error == NO_ERROR);
      return error;
    }

  assert (name_space == ID_ATTRIBUTE);

  orig_value = &((*found_att)->default_value.original_value);
  if (DB_IS_NULL (orig_value))
    {
      /* the attribute has not set a default original value, so need to continue */
      assert (error == NO_ERROR);
      return error;
    }

  /* adjust original_value domain to new attribute domain */
  status = tp_domain_check ((*found_att)->domain, orig_value, TP_EXACT_MATCH);

  if (status == DOMAIN_COMPATIBLE)
    {
      /* the attribute's current default original value has the same domain, no need to change it */
      assert (error == NO_ERROR);
      return error;
    }

  /* cast the value to new one : explicit cast */
  new_orig_value = pr_make_ext_value ();
  error = db_value_coerce (orig_value, new_orig_value, (*found_att)->domain);
  if (error == NO_ERROR)
    {
      smt_set_attribute_orig_default_value (*found_att, new_orig_value, new_default_expr);
    }
  else
    {
      ERROR1 (error, ER_OBJ_DOMAIN_CONFLICT, (*found_att)->header.name);
    }

  pr_free_ext_value (new_orig_value);

  return error;
}

/*
 * smt_change_attribute_pos_in_list()
 *   return: NO_ERROR on success, non-zero for ERROR
 *   att_list(in/out): the list to add to
 *   att(in): the attribute to add
 *   add_first(in): the attribute should be added at the beginning of the
 *                  attributes list
 *   add_after_attribute(in): the attribute should be added in the attributes
 *                            list after the attribute with the given name
 */

static int
smt_change_attribute_pos_in_list (SM_ATTRIBUTE ** att_list, SM_ATTRIBUTE * att, const bool change_first,
                  const char *change_after_attribute)
{
  int error_code = NO_ERROR;

  /* we must change the position : either to first or after another element */
  assert ((change_first && change_after_attribute == NULL) || (!change_first && change_after_attribute != NULL));

  assert (att != NULL);
  assert (att_list != NULL);

  /* first remove the attribute from list */
  if (WS_LIST_REMOVE (att_list, att) != 1)
    {
      error_code = ER_SM_ATTRIBUTE_NOT_FOUND;
      return error_code;
    }

  att->header.next = NULL;
  error_code = smt_add_attribute_to_list (att_list, att, change_first, change_after_attribute);
  /* error code already set */
  return error_code;
}

/*
 * smt_change_class_shared_attribute_domain() - changes the value domain of a
 *   shared or class attribute
 *
 *   return: NO_ERROR on success, non-zero for ERROR
 *   att(in/out): attribute to change
 *   new_domain(in): new domain of attribute
 */

static int
smt_change_class_shared_attribute_domain (SM_ATTRIBUTE * att, DB_DOMAIN * new_domain)
{
  int error = NO_ERROR;
  TP_DOMAIN_STATUS status;
  int cast_status = NO_ERROR;
  DB_VALUE *new_value = NULL;
  DB_VALUE *current_value = &(att->default_value.value);

  if (DB_IS_NULL (current_value))
    {
      /* the attribute has not been set with a value, set only new domain */
      assert (error == NO_ERROR);
      return error;
    }

  /* adjust original_value domain to new attribute domain */
  status = tp_domain_check (new_domain, current_value, TP_EXACT_MATCH);

  if (status == DOMAIN_COMPATIBLE)
    {
      /* the attribute's current value has the same domain, no need to change it */
      assert (error == NO_ERROR);
      return error;
    }

  /* cast the value to new domain : explicit cast */
  new_value = pr_make_ext_value ();
  cast_status = tp_value_cast (current_value, new_value, new_domain, false);
  if (cast_status == DOMAIN_COMPATIBLE)
    {
      pr_clear_value (&att->default_value.value);
      pr_clone_value (new_value, &att->default_value.value);

      att->type = new_domain->type;
      att->domain = new_domain;
    }
  else
    {
      ERROR1 (error, ER_OBJ_DOMAIN_CONFLICT, att->header.name);
    }

  pr_free_ext_value (new_value);

  return error;
}

/*
 * smt_find_owner_of_constraint() - Find the owner mop of the given constraint.
 *
 *   return: MOP on success, NULL for ERROR
 *   ctemplate(in): class template
 *   constrant_name(in):
 *
 *   Note: This function requires that the given constraint must exist in the
 *         class of ctemplate.
 */
static MOP
smt_find_owner_of_constraint (SM_TEMPLATE * ctemplate, const char *constraint_name)
{
  int error = NO_ERROR;
  SM_CLASS_CONSTRAINT *super_cons = NULL;
  SM_CLASS_CONSTRAINT *cons = NULL;
  DB_OBJLIST *super;
  SM_CLASS *class_;

  if (ctemplate->inheritance == NULL)
    {
      return ctemplate->op;
    }

  for (super = ctemplate->inheritance; super != NULL; super = super->next)
    {
      error = au_fetch_class_force (super->op, &class_, AU_FETCH_READ);
      if (error != NO_ERROR)
    {
      return NULL;
    }

      error = classobj_make_class_constraints (class_->properties, class_->attributes, &super_cons);
      if (error != NO_ERROR)
    {
      return NULL;
    }

      for (cons = super_cons; cons != NULL; cons = cons->next)
    {
      if (constraint_name != NULL && cons->name != NULL && SM_COMPARE_NAMES (constraint_name, cons->name) == 0)
        {
          classobj_free_class_constraints (super_cons);
          super_cons = NULL;

          return super->op;
        }
    }

      if (super_cons != NULL)
    {
      classobj_free_class_constraints (super_cons);
      super_cons = NULL;
    }
    }

  return ctemplate->op;
}

static int
change_constraints_status_partitioned_class (MOP obj, const char *index_name, SM_INDEX_STATUS index_status)
{
  int error = NO_ERROR;
  int i, is_partition = 0;
  MOP *sub_partitions = NULL;
  SM_TEMPLATE *ctemplate = NULL;
  SM_CLASS_CONSTRAINT *cons;

  error = sm_partitioned_class_type (obj, &is_partition, NULL, &sub_partitions);
  if (error != NO_ERROR)
    {
      goto error_exit;
    }

  if (is_partition == DB_PARTITION_CLASS)
    {
      error = ER_NOT_ALLOWED_ACCESS_TO_PARTITION;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
      goto error_exit;
    }
  else if (is_partition == DB_NOT_PARTITIONED_CLASS)
    {
      goto end;
    }

  assert (is_partition == DB_PARTITIONED_CLASS);

  for (i = 0; sub_partitions[i]; i++)
    {
      ctemplate = smt_edit_class_mop (sub_partitions[i], AU_INDEX);
      if (ctemplate == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto error_exit;
    }

      cons = smt_find_constraint (ctemplate, index_name);
      if (cons == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto error_exit;
    }

      error = classobj_change_constraint_status (ctemplate->properties, cons, index_status);
      if (error != NO_ERROR)
    {
      goto error_exit;
    }

      /* classobj_free_template() is included in sm_update_class() */
      error = sm_update_class (ctemplate, NULL);
      if (error != NO_ERROR)
    {
      /* Even though sm_update() did not return NO_ERROR, ctemplate is already freed */
      ctemplate = NULL;
      goto error_exit;
    }
    }

end:
  if (sub_partitions != NULL)
    {
      free_and_init (sub_partitions);
    }
  return error;

error_exit:
  if (ctemplate != NULL)
    {
      /* smt_quit() always returns NO_ERROR */
      smt_quit (ctemplate);
    }
  goto end;
}

static SM_CLASS_CONSTRAINT *
smt_find_constraint (SM_TEMPLATE * ctemplate, const char *constraint_name)
{
  SM_CLASS_CONSTRAINT *cons_list = NULL, *cons = NULL;
  SM_CLASS *class_;

  assert (ctemplate != NULL && ctemplate->op != NULL);

  if (au_fetch_class (ctemplate->op, &class_, AU_FETCH_READ, AU_INDEX) != NO_ERROR)
    {
      ASSERT_ERROR ();
      return NULL;
    }

  cons_list = class_->constraints;
  if (cons_list == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SM_CONSTRAINT_NOT_FOUND, 1, constraint_name);
      return NULL;
    }

  cons = classobj_find_constraint_by_name (cons_list, constraint_name);
  if (cons == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SM_CONSTRAINT_NOT_FOUND, 1, constraint_name);
      return NULL;
    }

  return cons;
}

static int
smt_is_change_status_allowed (SM_TEMPLATE * ctemplate, const char *index_name)
{
  int error = NO_ERROR;
  SM_CLASS_CONSTRAINT *constraint;
  int partition_type;

  /* Check if this class is a partitioned class. We do not allow index status change on partitions indexes. */
  error = sm_partitioned_class_type (ctemplate->op, &partition_type, NULL, NULL);
  if (partition_type == DB_PARTITION_CLASS)
    {
      error = ER_SM_INDEX_STATUS_CHANGE_NOT_ALLOWED;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 3, sm_ch_name ((MOBJ) ctemplate->current), index_name,
          "local index on a partition");
      return error;
    }

  constraint = smt_find_constraint (ctemplate, index_name);
  if (constraint == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      return error;
    }

  switch (constraint->type)
    {
    case SM_CONSTRAINT_FOREIGN_KEY:
      error = ER_SM_INDEX_STATUS_CHANGE_NOT_ALLOWED;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 3, sm_ch_name ((MOBJ) ctemplate->current), constraint->name,
          "foreign key");
      return error;

    case SM_CONSTRAINT_PRIMARY_KEY:
      error = ER_SM_INDEX_STATUS_CHANGE_NOT_ALLOWED;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 3, sm_ch_name ((MOBJ) ctemplate->current), constraint->name,
          "primary key");
      return error;

    case SM_CONSTRAINT_NOT_NULL:
      error = ER_SM_INDEX_STATUS_CHANGE_NOT_ALLOWED;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 3, sm_ch_name ((MOBJ) ctemplate->current), constraint->name,
          "NOT NULL constraint");
      return error;

    default:
      break;
    }

  return NO_ERROR;
}

int
smt_change_constraint_status (SM_TEMPLATE * ctemplate, const char *index_name, SM_INDEX_STATUS index_status)
{
  SM_CLASS_CONSTRAINT *cons = NULL;
  int error = NO_ERROR;

  assert (ctemplate != NULL && ctemplate->op != NULL);

  error = smt_is_change_status_allowed (ctemplate, index_name);
  if (error != NO_ERROR)
    {
      return error;
    }

  error = change_constraints_status_partitioned_class (ctemplate->op, index_name, index_status);
  if (error != NO_ERROR)
    {
      return error;
    }

  cons = smt_find_constraint (ctemplate, index_name);
  if (cons == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      return error;
    }

  error = classobj_change_constraint_status (ctemplate->properties, cons, index_status);
  if (error != NO_ERROR)
    {
      return error;
    }

  return NO_ERROR;
}