Skip to content

File object_template.c

File List > cubrid > src > object > object_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.
 *
 */

/*
 * object_template.c - Object template module
 *
 *      This contains code for attribute and method access, instance creation
 *      and deletion, and misc utilitities related to instances.
 *
 */

#ident "$Id$"

#include "config.h"

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

#include "db.h"
#include "dbtype.h"
#include "error_manager.h"
#include "system_parameter.h"
#include "server_interface.h"
#include "work_space.h"
#include "object_domain.h"
#include "object_primitive.h"
#include "object_representation.h"
#include "set_object.h"
#include "class_object.h"
#include "schema_manager.h"
#include "object_accessor.h"
#include "view_transform.h"
#include "authenticate.h"
#include "locator_cl.h"
#include "virtual_object.h"
#include "parser.h"
#include "transaction_cl.h"
#include "trigger_manager.h"
#include "environment_variable.h"
#include "transform.h"
#include "schema_system_catalog_constants.h"
#include "execute_statement.h"
#include "network_interface_cl.h"

#include "dbtype.h"

#define OBJ_INTERNAL_SAVEPOINT_NAME "*template-unique*"

/*
 *                  GLOBAL VARIABLES
 */
/*
 * State used when creating templates, to indicate whether unique constraint
 * checking is enabled.
 * This state can be modifed using obt_enable_unique_checking()
 */
bool obt_Check_uniques = true;

/*
 * State variable used when creating object template, to indicate whether enable
 * auto increment feature
 */
bool obt_Enable_autoincrement = true;

/*
 * State variable used when generating AUTO_INCREMENT value,
 * to set the first generated AUTO_INCREMENT value as LAST_INSERT_ID.
 * It is only for client-side insertion.
 */
bool obt_Last_insert_id_generated = false;

/*
 *                            OBJECT MANAGER AREAS
 */

/*
 * Template_area
 * Assignment_area
 *
 * Note :
 *    Areas for the allocation of object templates and assignment
 *    templates.  Since these can be referenced by the interpreter,
 *    we need to make sure that they serve as roots for the garbage
 *    collector.
 *
 */

static AREA *Template_area = NULL;
static AREA *Assignment_area = NULL;

/*
 * obj_Template_traversal
 *
 *
 */

static unsigned int obj_Template_traversal = 0;
/*
 * Must make sure template savepoints have unique names to allow for concurrent
 * or nested updates.  Could be resetting this at db_restart() time.
 */
static unsigned int template_savepoint_count = 0;


static DB_VALUE *check_att_domain (SM_ATTRIBUTE * att, DB_VALUE * proposed_value);
static int check_constraints (SM_ATTRIBUTE * att, DB_VALUE * value, unsigned force_check_not_null);
static int quick_validate (SM_VALIDATION * valid, DB_VALUE * value);
static void cache_validation (SM_VALIDATION * valid, DB_VALUE * value);
static void begin_template_traversal (void);
static OBJ_TEMPLATE *make_template (MOP object, MOP classobj);
static int validate_template (OBJ_TEMPLATE * temp);
static OBJ_TEMPASSIGN *obt_make_assignment (OBJ_TEMPLATE * template_ptr, SM_ATTRIBUTE * att);
static void obt_free_assignment (OBJ_TEMPASSIGN * assign);
static void obt_free_template (OBJ_TEMPLATE * template_ptr);
static int populate_auto_increment (OBJ_TEMPLATE * template_ptr);
static int populate_defaults (OBJ_TEMPLATE * template_ptr);
static int obt_assign_obt (OBJ_TEMPLATE * template_ptr, SM_ATTRIBUTE * att, int base_assignment, OBJ_TEMPLATE * value);
static MOP create_template_object (OBJ_TEMPLATE * template_ptr);
static int access_object (OBJ_TEMPLATE * template_ptr, MOP * object, MOBJ * objptr);
static int obt_convert_set_templates (SETREF * setref, int check_uniques);
static int obt_final_check_set (SETREF * setref, int *has_uniques);

static int obt_final_check (OBJ_TEMPLATE * template_ptr, int check_non_null, int *has_uniques);
static int obt_apply_assignment (MOP op, SM_ATTRIBUTE * att, char *mem, DB_VALUE * value, int check_uniques);
static int obt_apply_assignments (OBJ_TEMPLATE * template_ptr, int check_uniques, int level);

static MOP make_temp_object (DB_OBJECT * class_, OBJ_TEMPLATE * object);
static void free_temp_object (MOP obj);

/*
 * obt_area_init
 *    return: NO_ERROR or error code.
 *
 */

int
obt_area_init (void)
{
  Template_area = area_create ("Object templates", sizeof (OBJ_TEMPLATE), 32);
  if (Template_area == NULL)
    {
      goto error;
    }

  Assignment_area = area_create ("Assignment templates", sizeof (OBJ_TEMPASSIGN), 64);
  if (Assignment_area == NULL)
    {
      goto error;
    }

  return NO_ERROR;

error:
  obt_area_final ();

  assert (er_errid () != NO_ERROR);

  return er_errid ();
}

/*
 * obt_area_final
 *    return: NO_ERROR or error code.
 *
 */
void
obt_area_final (void)
{
  if (Template_area != NULL)
    {
      area_destroy (Template_area);
      Template_area = NULL;
    }

  if (Assignment_area != NULL)
    {
      area_destroy (Assignment_area);
      Assignment_area = NULL;
    }
}

/*
 * obt_find_attribute - locate an attribute for a template.
 *      return: error code
 *      template(in) :
 *      use_base_class(in) :
 *      name(in): attribute name
 *      attp(out): returned pointer to attribute descriptor
 *
 * Note:
 *    This is a bit simpler than the others since we have the class
 *    cached in the template.
 *
 */

int
obt_find_attribute (OBJ_TEMPLATE * template_ptr, int use_base_class, const char *name, SM_ATTRIBUTE ** attp)
{
  int error = NO_ERROR;
  MOP classobj;
  SM_CLASS *class_, *upgrade_class;
  SM_ATTRIBUTE *att;

  att = NULL;

  class_ = (use_base_class) ? template_ptr->base_class : template_ptr->class_;

  att = classobj_find_attribute (class_, name, template_ptr->is_class_update);

  if (att != NULL && att->header.name_space == ID_SHARED_ATTRIBUTE)
    {
      /*
       * Sigh, when we originally fetched the class, it was fetched
       * with a read lock since we weren't sure of the intent.  Now
       * that we know we're about to update a shared attribute, we need to
       * upgrade the lock to a write lock.  We could use AU_FETCH_WRITE
       * here rather than AU_FETCH_UPDATE and use locator_update_class later
       * when we're sure the template can be applied without error.
       */
      if (!template_ptr->write_lock)
    {
      classobj = ((use_base_class) ? template_ptr->base_classobj : template_ptr->classobj);

      error = au_fetch_class (classobj, &upgrade_class, AU_FETCH_UPDATE, AU_ALTER);
      template_ptr->write_lock = !error;

      /*
       * This better damn well not re-fetch the class.
       * If this can happen, we'll need a general "recache" function
       * for the template.
       */
      if (class_ != upgrade_class)
        {
          error = ER_OBJ_TEMPLATE_INTERNAL;
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
        }
    }
    }

  if (error == NO_ERROR && att == NULL)
    {
      ERROR1 (error, ER_OBJ_INVALID_ATTRIBUTE, name);
    }

  *attp = att;
  return error;
}

/*
 *
 *                           ASSIGNMENT VALIDATION
 *
 *
 */

/*
 * check_att_domain - This checks to see if a value is within the domain of an
 *                    attribute.
 *
 *      returns: actual value container
 *      att(in): attribute name (for error messages)
 *      proposed_value(in): original value container
 *
 * Note:
 *    It calls tp_domain_check & tp_domain_coerce to do the work, this
 *    function mostly serves to inerpret the return codes and set an
 *    appropriate error condition.
 *
 */

static DB_VALUE *
check_att_domain (SM_ATTRIBUTE * att, DB_VALUE * proposed_value)
{
  TP_DOMAIN_STATUS status;
  DB_VALUE *value;
  int is_ref = 0;

  value = proposed_value;

  is_ref = pt_is_reference_to_reusable_oid (value);
  if (is_ref < 0)
    {
      return NULL;
    }
  if (is_ref > 0)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_REFERENCE_TO_NON_REFERABLE_NOT_ALLOWED, 0);
      return NULL;
    }

  /*
   * Note that we set the "exact" match flag true to disallow "tolerance"
   * matches.  Some types (such as CHAR) may appear to overflow the domain,
   * but can be truncated during the coercion process.
   */
  status = tp_domain_check (att->domain, value, TP_EXACT_MATCH);

  if (status != DOMAIN_COMPATIBLE)
    {
      value = pr_make_ext_value ();
      if (value == NULL)
    {
      return NULL;
    }
      status = tp_value_cast (proposed_value, value, att->domain, !TP_IS_CHAR_TYPE (TP_DOMAIN_TYPE (att->domain)));
      if (status != DOMAIN_COMPATIBLE)
    {
      (void) pr_free_ext_value (value);
    }
    }

  if (status != DOMAIN_COMPATIBLE)
    {
      switch (status)
    {
    case DOMAIN_ERROR:
      /* error has already been set */
      break;
    case DOMAIN_OVERFLOW:
      if (TP_IS_BIT_TYPE (DB_VALUE_DOMAIN_TYPE (proposed_value)))
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_STRING_OVERFLOW, 2, att->header.name,
              att->domain->precision);
        }
      else
        {
          (void) tp_domain_status_er_set (status, ARG_FILE_LINE, proposed_value, att->domain);
          assert (er_errid () != NO_ERROR);
        }
      break;
    case DOMAIN_INCOMPATIBLE:
    default:
      /*
       * the default case shouldn't really be encountered, might want to
       * signal a different error.  The OVERFLOW case should only
       * be returned during coercion which wasn't requested, to be safe,
       * treat these like a domain conflict.  Probably need a more generic
       * domain conflict error that uses full printed representations
       * of the entire domain.
       */
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_DOMAIN_CONFLICT, 1, att->header.name);
      break;
    }

      /* return NULL if incompatible */
      value = NULL;
    }

  return value;
}

/*
 * check_constraints - This function is used to check a proposed value
 *                     against the integrity constraints defined
 *                     for an attribute.
 *
 *      returns: error code
 *
 *      att(in): attribute descriptor
 *      value(in): value to verify
 *      force_check_not_null(in): force NOT NULL constraint check
 *
 * Note:
 *    If will return an error code if any of the constraints are violated.
 *
 */

static int
check_constraints (SM_ATTRIBUTE * att, DB_VALUE * value, unsigned force_check_not_null)
{
  int error = NO_ERROR;
  MOP mop;

  /* check NOT NULL constraint */
  if (value == NULL || DB_IS_NULL (value)
      || (att->domain->type == tp_Type_object && (mop = db_get_object (value)) && WS_MOP_IS_NULL (mop)))
    {
      if (att->flags & SM_ATTFLAG_NON_NULL)
    {
      if ((att->flags & SM_ATTFLAG_AUTO_INCREMENT) && !force_check_not_null)
        {
          assert (DB_IS_NULL (value));
          assert (att->domain->type != tp_Type_object);

          /* This is allowed to happen only during INSERT statements, since the next serial value will be filled in
           * at a later time. For other cases, the force_check_not_null flag should be set. */
        }
      else
        {
          ERROR1 (error, ER_OBJ_ATTRIBUTE_CANT_BE_NULL, att->header.name);
        }
    }
    }
  else
    {
      /* Check size constraints */
      if (tp_check_value_size (att->domain, value) != DOMAIN_COMPATIBLE)
    {
      /* probably need an error message that isn't specific to "string" types */
      ERROR2 (error, ER_OBJ_STRING_OVERFLOW, att->header.name, att->domain->precision);
    }
    }

  return error;
}

/*
 * quick_validate - This function is where we try to determine as fast as
 *                  possible if a value is compatible with
 *                  a certain attribute's domain.
 *      returns: non-zero if the value is known to be valid
 *      valid(in): validation cache
 *      value(in): value to ponder
 */

static int
quick_validate (SM_VALIDATION * valid, DB_VALUE * value)
{
  int is_valid;
  DB_TYPE type;

  if (valid == NULL || value == NULL)
    return 0;

  is_valid = 0;
  type = DB_VALUE_TYPE (value);

  switch (type)
    {
    case DB_TYPE_OBJECT:
      {
    DB_OBJECT *obj, *class_;

    obj = db_get_object (value);
    if (obj != NULL)
      {
        class_ = db_get_class (obj);
        if (class_ != NULL)
          {
        if (class_ == valid->last_class)
          {
            is_valid = 1;
          }
        else
          {
            /* wasn't on the first level cache, check the list */
            is_valid = ml_find (valid->validated_classes, class_);
            /* if its on the list, auto select this for the next time around */
            if (is_valid)
              {
            valid->last_class = class_;
              }
          }
          }
      }
      }
      break;

    case DB_TYPE_SET:
    case DB_TYPE_MULTISET:
    case DB_TYPE_SEQUENCE:
      {
    DB_SET *set;
    DB_DOMAIN *domain;

    set = db_get_set (value);
    domain = set_get_domain (set);
    if (domain == valid->last_setdomain)
      {
        is_valid = 1;
      }
      }
      break;

    case DB_TYPE_CHAR:

    case DB_TYPE_VARCHAR:
      if (type == valid->last_type && DB_GET_STRING_PRECISION (value) == valid->last_precision)
    {
      is_valid = 1;
    }
      break;

    case DB_TYPE_BIT:
    case DB_TYPE_VARBIT:
      if (type == valid->last_type && DB_GET_BIT_PRECISION (value) == valid->last_precision)
    {
      is_valid = 1;
    }
      break;

    case DB_TYPE_NUMERIC:
      if (type == valid->last_type && DB_GET_NUMERIC_PRECISION (value) == valid->last_precision
      && DB_GET_NUMERIC_SCALE (value) == valid->last_scale)
    {
      is_valid = 1;
    }
      break;

    default:
      if (type == valid->last_type)
    {
      is_valid = 1;
    }
      break;
    }

  return is_valid;
}

/*
 * cache_validation
 *      return : none
 *      valid(in): validation cache
 *      value(in): value known to be good
 *
 * Note:
 *    Caches information about the data value in the validation cache
 *    so hopefully we'll be quicker about validating values of this
 *    form if we find them again.
 *
 */

static void
cache_validation (SM_VALIDATION * valid, DB_VALUE * value)
{
  DB_TYPE type;

  if (valid == NULL || value == NULL)
    {
      return;
    }

  type = DB_VALUE_TYPE (value);
  switch (type)
    {
    case DB_TYPE_OBJECT:
      {
    DB_OBJECT *obj, *class_;

    obj = db_get_object (value);
    if (obj != NULL)
      {
        class_ = db_get_class (obj);
        if (class_ != NULL)
          {
        valid->last_class = class_;
        /*
         * !! note that we have to be building an external object list
         * here so these serve as GC roots.  This is kludgey, we should
         * be encapsulating structure rules inside cl_ where the
         * SM_VALIDATION is allocated.
         */
        (void) ml_ext_add (&valid->validated_classes, class_, NULL);
          }
      }
      }
      break;

    case DB_TYPE_SET:
    case DB_TYPE_MULTISET:
    case DB_TYPE_SEQUENCE:
      {
    DB_SET *set;

    set = db_get_set (value);
    valid->last_setdomain = set_get_domain (set);
      }
      break;

    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
    case DB_TYPE_BIT:
    case DB_TYPE_VARBIT:
      valid->last_type = type;
      valid->last_precision = db_value_precision (value);
      valid->last_scale = 0;
      break;

    case DB_TYPE_NUMERIC:
      valid->last_type = type;
      valid->last_precision = db_value_precision (value);
      valid->last_scale = db_value_scale (value);
      break;

    default:
      valid->last_type = type;
      valid->last_precision = 0;
      valid->last_scale = 0;
      break;
    }
}

/*
 * obt_check_assignment - This is the main validation routine
 *                    for attribute assignment.
 *      returns: value container
 *      att(in): attribute descriptor
 *      proposed_value(in): value to assign
 *      valid(in):
 *      force_check_not_null(in): force check for NOT NULL
 *                                constraints
 *
 *
 * Note:
 *    It is used both by the direct assignment function obj_set and also
 *    by object templates (which do grouped assignments).  Any other function
 *    that does attribute value assignment should also use this function
 *    or be VERY careful about the rules contained here.
 *    The check_unique flag is normally turned on only if we're building
 *    an object template because we have to check for constraint violation
 *    before allowing the rest of the template to be built.  For immediate
 *    attribute assignment (not using templates) we delay the checking for
 *    unique constraints until later (in assign_value) so we only have to
 *    do one server call instead of two.  Would be nice if templates could
 *    have a way to "batch up" their unique attribute checks.
 *    This function will return NULL if an error was detected.
 *    It will return the propsed_value pointer if the assignment is
 *    acceptable.
 *    It will return a new value container if the proposed_value wasn't
 *    acceptable but it was coerceable to a valid value.
 *    The caller must check to see if the returned value is different
 *    and if so free it with pr_free_ext_value() when done.
 *
 */

DB_VALUE *
obt_check_assignment (SM_ATTRIBUTE * att, DB_VALUE * proposed_value, SM_VALIDATION * valid,
              unsigned force_check_not_null)
{
  DB_VALUE *value;

  /* assume this will be ok */
  value = proposed_value;

  /* for simplicity, convert this into a container with a NULL type */
  if (value == NULL)
    {
      value = pr_make_ext_value ();
    }
  else
    {
      /*
       * before we make the expensive checks, see if we've got some cached
       * validation information handy
       */
      if (!quick_validate (valid, value))
    {
      value = check_att_domain (att, proposed_value);
      if (value != NULL)
        {
          if (check_constraints (att, value, force_check_not_null) != NO_ERROR)
        {
          if (value != proposed_value)
            {
              (void) pr_free_ext_value (value);
            }
          value = NULL;
        }
          else
        {
          /*
           * we're ok, if there was no coercion required, remember this for
           * next time.
           */
          if (value == proposed_value)
            {
              cache_validation (valid, proposed_value);
            }
        }
        }
    }
    }

  return value;
}

/*
 *
 *                         OBJECT TEMPLATE ASSIGNMENT
 *
 *
 */


/*
 * begin_template_traversal - This "allocates" the traversal counter
 *                            for a new template traversal.
 *      return : none
 *
 * Note :
 *    obj_Template_traversal is set to this value so it can
 *    be tested during traversal.
 *    This is in a function just so that the rules for skipping a traversal
 *    value of zero can be encapsulated.
 *
 */

static void
begin_template_traversal (void)
{
  /* increment the counter */
  obj_Template_traversal++;

  /* don't let it be zero */
  if (obj_Template_traversal == 0)
    {
      obj_Template_traversal++;
    }
}

/*
 * make_template - This initializes a new object template.
 *    return: new object template
 *    object(in): the object that the template is being created for
 *    classobj(in): the class of the object
 *
 */

static OBJ_TEMPLATE *
make_template (MOP object, MOP classobj)
{
  OBJ_TEMPLATE *template_ptr;
  AU_FETCHMODE mode;
  AU_TYPE auth;
  SM_CLASS *class_, *base_class;
  MOP base_classobj, base_object;
  MOBJ obj;
  OBJ_TEMPASSIGN **vec;

  base_classobj = NULL;
  base_class = NULL;
  base_object = NULL;

  /* fetch & lock the class with the appropriate options */
  mode = AU_FETCH_READ;
  if (object == NULL)
    {
      auth = AU_INSERT;
    }
  else if (object != classobj)
    {
      auth = AU_UPDATE;
    }
  else
    {
      /*
       * class variable update
       * NOTE: It might be good to use AU_FETCH_WRITE here and then
       * use locator_update_class to set the dirty bit after the template
       * has been successfully applied.
       */
      mode = AU_FETCH_UPDATE;
      auth = AU_ALTER;
    }

  if (au_fetch_class (classobj, &class_, mode, auth))
    {
      return NULL;
    }


  /*
   * we only need to keep track of the base class if this is a
   * virtual class, for proxies, the instances look like usual
   */

  if (class_->class_type == SM_VCLASS_CT    /* a view, and... */
      && object != classobj /* we are not doing a meta class update */ )
    {
      /*
       * could use vid_is_updatable() if
       * the instance was supplied but since this can be NULL for
       * insert templates, use mq_is_updatable on the class object instead.
       * NOTE: Don't call this yet, try to use mq_fetch_one_real_class()
       * to perform the updatability test.
       */
      if (!mq_is_updatable (classobj))
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IT_NOT_UPDATABLE_STMT, 0);
      return NULL;
    }


      base_classobj = mq_fetch_one_real_class (classobj);
      if (base_classobj == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IT_NOT_UPDATABLE_STMT, 0);
      return NULL;
    }

      if (au_fetch_class (base_classobj, &base_class, AU_FETCH_READ, auth))
    {
      return NULL;
    }

      /* get the associated base object (if this isn't a proxy) */
      if (object != NULL && !vid_is_base_instance (object))
    {
      base_object = vid_get_referenced_mop (object);
    }
    }

  /*
   * If this is an instance update, fetch & lock the instance.
   * NOTE: It might be good to use AU_FETCH_WRITE and use locator_update_instance
   * to set the dirty bit after the template has been successfully applied.
   *
   * If this is a virtual instance on a non-proxy, could be locking
   * the associated instance as well. Is this already being done ?
   */
  if (object != NULL && object != classobj)
    {
      if (au_fetch_instance (object, &obj, AU_FETCH_UPDATE, LC_FETCH_MVCC_VERSION, AU_UPDATE))
    {
      return NULL;
    }

      /*
       * Could cache the object memory pointer this in the template as
       * well but that would require that it be pinned for a long
       * duration through code that we don't control.  Dangerous.
       */
    }

  template_ptr = (OBJ_TEMPLATE *) area_alloc (Template_area);
  if (template_ptr != NULL)
    {
      template_ptr->object = object;
      template_ptr->classobj = classobj;

      /*
       * cache the class info directly in the template, will need
       * to remember the transaction id and chn for validation
       */
      template_ptr->class_ = class_;

      /* cache the base class if this is a virtual class template */
      template_ptr->base_classobj = base_classobj;
      template_ptr->base_class = base_class;
      template_ptr->base_object = base_object;

      template_ptr->tran_id = tm_Tran_index;
      template_ptr->schema_id = sm_local_schema_version ();
      template_ptr->assignments = NULL;
      template_ptr->label = NULL;
      template_ptr->traversal = 0;
      template_ptr->write_lock = mode != AU_FETCH_READ;
      template_ptr->traversed = 0;
      template_ptr->is_old_template = 0;
      template_ptr->is_class_update = (object == classobj);
      template_ptr->check_uniques = obt_Check_uniques;
      if (TM_TRAN_ISOLATION () >= TRAN_REPEATABLE_READ)
    {
      template_ptr->check_serializable_conflict = 1;
    }
      else
    {
      template_ptr->check_serializable_conflict = 0;
    }
      template_ptr->uniques_were_modified = 0;
      template_ptr->function_key_modified = 0;

      template_ptr->shared_was_modified = 0;
      template_ptr->discard_on_finish = 1;
      template_ptr->fkeys_were_modified = 0;
      template_ptr->force_check_not_null = 0;
      template_ptr->force_flush = 0;
      template_ptr->is_autoincrement_set = 0;
      template_ptr->pruning_type = DB_NOT_PARTITIONED_CLASS;
      /*
       * Don't do this until we've initialized the other stuff;
       * OTMPL_NASSIGNS relies on the "class" attribute of the template.
       */

      if (template_ptr->is_class_update)
    {
      template_ptr->nassigns = template_ptr->class_->class_attribute_count;
    }
      else
    {
      template_ptr->nassigns = (template_ptr->class_->att_count + template_ptr->class_->shared_count);
    }

      vec = NULL;
      if (template_ptr->nassigns)
    {
      int i;

      vec = (OBJ_TEMPASSIGN **) malloc (template_ptr->nassigns * sizeof (OBJ_TEMPASSIGN *));
      if (!vec)
        {
          return NULL;
        }
      for (i = 0; i < template_ptr->nassigns; i++)
        {
          vec[i] = NULL;
        }
    }

      template_ptr->assignments = vec;
    }

  return template_ptr;
}

/*
 * validate_template - This is used to validate a template before each operation
 *      return: error code
 *      temp(in): template to validate
 *
 */

static int
validate_template (OBJ_TEMPLATE * temp)
{
  int error = NO_ERROR;

  if (temp != NULL && (temp->tran_id != tm_Tran_index || temp->schema_id != sm_local_schema_version ()))
    {
      error = ER_OBJ_INVALID_TEMPLATE;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }

  return error;
}

/*
 * obt_make_assignment - This initializes a new assignment template.
 *    return: template assignment structure
 *    template(in):
 *    att(in):
 *
 * Note:
 *    It also adds it to a containing template.
 */

static OBJ_TEMPASSIGN *
obt_make_assignment (OBJ_TEMPLATE * template_ptr, SM_ATTRIBUTE * att)
{
  OBJ_TEMPASSIGN *assign;

  assign = (OBJ_TEMPASSIGN *) area_alloc (Assignment_area);
  if (assign != NULL)
    {
      assign->obj = NULL;
      assign->variable = NULL;
      assign->att = att;
      assign->old_value = NULL;
      assign->is_default = 0;
      assign->is_auto_increment = 0;

      template_ptr->assignments[att->order] = assign;
      if (classobj_has_unique_constraint (att->constraints))
    {
      template_ptr->uniques_were_modified = 1;
    }
      if (att->header.name_space == ID_SHARED_ATTRIBUTE)
    {
      template_ptr->shared_was_modified = 1;
    }

      if (classobj_get_cached_constraint (att->constraints, SM_CONSTRAINT_FOREIGN_KEY, NULL))
    {
      template_ptr->fkeys_were_modified = 1;
    }
      if (classobj_has_function_constraint (att->constraints))
    {
      template_ptr->function_key_modified = 1;
    }
    }

  return assign;
}

/*
 * obt_free_assignment - Work function for obt_free_template.
 *      return: none
 *      assign(in): an assignment template
 *
 * Note :
 *    Frees an attribute assignment template.  If the assigment contains
 *    an object template rather than a DB_VALUE, it will be freed by
 *    recursively calling obj_free_template.
 *
 */

static void
obt_free_assignment (OBJ_TEMPASSIGN * assign)
{
  DB_VALUE *value = NULL;
  SETREF *setref;
  int i, set_size;

  if (assign != NULL)
    {
      if (assign->variable != NULL)
    {

      DB_TYPE av_type;

      /* check for nested templates */
      av_type = DB_VALUE_TYPE (assign->variable);
      if (av_type == DB_TYPE_POINTER)
        {
          obt_free_template ((OBJ_TEMPLATE *) db_get_pointer (assign->variable));
          db_make_pointer (assign->variable, NULL);
        }
      else if (TP_IS_SET_TYPE (av_type) && db_get_set (assign->variable) != NULL)
        {
          /* must go through and free any elements that may be template pointers */
          setref = db_get_set (assign->variable);
          if (setref->set != NULL)
        {
          set_size = setobj_size (setref->set);
          for (i = 0; i < set_size; i++)
            {
              setobj_get_element_ptr (setref->set, i, &value);
              if (value != NULL && DB_VALUE_TYPE (value) == DB_TYPE_POINTER)
            {
              obt_free_template ((OBJ_TEMPLATE *) db_get_pointer (value));
              db_make_pointer (value, NULL);
            }
            }
        }
        }

      (void) pr_free_ext_value (assign->variable);

      if (assign->old_value != NULL)
        {
          (void) pr_free_ext_value (assign->old_value);
        }
    }

      (void) area_free (Assignment_area, assign);
    }
}

/*
 * obt_free_template - This frees a hierarchical object template.
 *      return: none
 *      template(in): object template
 *
 * Note :
 *    It will be called by obt_update when the template has been applied
 *    or can be called by obt_quit to abort the creation of the template.
 *    Since the template can contain circular references, must be careful and
 *    use a traversal flag in each template.
 *
 */

static void
obt_free_template (OBJ_TEMPLATE * template_ptr)
{
  OBJ_TEMPASSIGN *a;
  int i;

  if (!template_ptr->traversed)
    {
      template_ptr->traversed = 1;

      for (i = 0; i < template_ptr->nassigns; i++)
    {
      a = template_ptr->assignments[i];
      if (a == NULL)
        {
          continue;
        }

      if (a->obj != NULL)
        {
          obt_free_template (a->obj);
        }

      obt_free_assignment (a);
    }

      if (template_ptr->assignments)
    {
      free_and_init (template_ptr->assignments);
    }

      (void) area_free (Template_area, template_ptr);
    }
}

/*
 * populate_auto_increment - This populates a template with the
 *                           auto_increment values for a class.
 *      return: error code
 *      template(in): template to fill out
 *
 * Note :
 *    This is necessary for INSERT templates.  The assignments are marked
 *    so that if an assignment is later made to the template with the
 *    same name. we don't generate an error because its ok to override
 *    a auto increment value.
 *    If an assignment is already found with the name, it is assumed
 *    that an initial value has already been given.
 *
 */

static int
populate_auto_increment (OBJ_TEMPLATE * template_ptr)
{
  SM_ATTRIBUTE *att;
  OBJ_TEMPASSIGN *a, *exists;
  SM_CLASS *class_;
  int error = NO_ERROR;
  DB_VALUE val;
  DB_DATA_STATUS data_status;
  char auto_increment_name[AUTO_INCREMENT_SERIAL_NAME_MAX_LENGTH];
  MOP serial_class_mop = NULL, serial_mop;
  DB_IDENTIFIER serial_obj_id;
  const char *class_name;
  int cached_num;

  if (template_ptr->is_class_update)
    {
      return error;
    }

  class_ = template_ptr->class_;

  for (att = class_->ordered_attributes; att != NULL; att = att->order_link)
    {
      if (!(att->flags & SM_ATTFLAG_AUTO_INCREMENT))
    {
      continue;
    }

      if (att->auto_increment == NULL)
    {
      if (serial_class_mop == NULL)
        {
          serial_class_mop = sm_find_class (CT_SERIAL_NAME);
        }

      class_name = sm_get_ch_name (att->class_mop);
      if (class_name == NULL)
        {
          assert (er_errid () != NO_ERROR);
          goto auto_increment_error;
        }

      /* get original class's serial object */
      SET_AUTO_INCREMENT_SERIAL_NAME (auto_increment_name, class_name, att->header.name);
      serial_mop = do_get_serial_obj_id (&serial_obj_id, serial_class_mop, auto_increment_name);
      if (serial_mop == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ATTRIBUTE, 1, auto_increment_name);
          goto auto_increment_error;
        }

      att->auto_increment = serial_mop;
    }

      exists = template_ptr->assignments[att->order];
      if (exists != NULL)
    {
      if (exists->variable == NULL || !DB_IS_NULL (exists->variable))
        {
          continue;
        }
    }

      a = obt_make_assignment (template_ptr, att);
      if (a == NULL)
    {
      goto auto_increment_error;
    }

      a->is_auto_increment = 1;
      a->variable = pr_make_ext_value ();

      if (a->variable == NULL)
    {
      goto auto_increment_error;
    }

      if (do_get_serial_cached_num (&cached_num, att->auto_increment) != NO_ERROR)
    {
      goto auto_increment_error;
    }

      db_make_null (&val);
      /* Do not update LAST_INSERT_ID during executing a trigger. */
      if (do_Trigger_involved == true || obt_Last_insert_id_generated == true)
    {
      error = serial_get_next_value (&val, &att->auto_increment->oid_info.oid, cached_num, 1, GENERATE_SERIAL);
    }
      else
    {
      error =
        serial_get_next_value (&val, &att->auto_increment->oid_info.oid, cached_num, 1, GENERATE_AUTO_INCREMENT);
      if (error == NO_ERROR)
        {
          obt_Last_insert_id_generated = true;
        }
    }
      if (error != NO_ERROR)
    {
      goto auto_increment_error;
    }

      db_value_domain_init (a->variable, att->type->id, att->domain->precision, att->domain->scale);

      (void) numeric_db_value_coerce_from_num (&val, a->variable, &data_status);
      if (data_status != NO_ERROR)
    {
      goto auto_increment_error;
    }
      else
    {
      template_ptr->is_autoincrement_set = 1;
    }
    }

  return error;

auto_increment_error:
  assert (er_errid () != NO_ERROR);
  return er_errid ();
}

/*
 * populate_defaults - This populates a template with the default values
 *                      for a class.
 *      returns: error code
 *      template(in): template to fill out
 *
 * Note :
 *    This is necessary for INSERT templates.  The assignments are marked
 *    so that if an assignment is later made to the template with the
 *    same name, we don't generate an error because its ok to override
 *    a default value.
 *    If an assignment is already found with the name, it is assumed
 *    that an initial value has already been given and the default is
 *    ignored.
 *
 */

static int
populate_defaults (OBJ_TEMPLATE * template_ptr)
{
  SM_ATTRIBUTE *att, *base_att;
  OBJ_TEMPASSIGN *a, *exists;
  SM_CLASS *class_;
  DB_VALUE base_value;
  const char *base_name;

  db_make_null (&base_value);

  if (!template_ptr->is_class_update)
    {
      class_ = template_ptr->class_;

      if (template_ptr->base_class != NULL)
    {
      /*
       * first populate with the transformed default values of the
       * virtual class
       */
      for (att = template_ptr->class_->attributes; att != NULL; att = (SM_ATTRIBUTE *) att->header.next)
        {

          /* only update the attribute if it is updatable */
          if (mq_is_updatable_attribute (template_ptr->classobj, att->header.name, template_ptr->base_classobj))
        {
          if (mq_update_attribute (template_ptr->classobj, att->header.name, template_ptr->base_classobj,
                       &att->default_value.value, &base_value, &base_name, DB_AUTH_INSERT))
            {
              assert (er_errid () != NO_ERROR);
              return er_errid ();
            }

          /* find the associated attribute definition in the base class */
          if (obt_find_attribute (template_ptr, 1, base_name, &base_att))
            {
              assert (er_errid () != NO_ERROR);
              return er_errid ();
            }

          exists = template_ptr->assignments[base_att->order];
          /*
           * if the tranformed virtual default is non-NULL we use it,
           * if the underlying base default is non-NULL, we let the virtual
           * default override it to NULL
           */

          if (exists == NULL && (!DB_IS_NULL (&base_value) || !DB_IS_NULL (&base_att->default_value.value)))
            {
              /* who owns base_value ? */
              a = obt_make_assignment (template_ptr, base_att);
              if (a == NULL)
            {
              goto memory_error;
            }
              a->is_default = 1;
              a->variable = pr_make_ext_value ();
              if (a->variable == NULL)
            {
              goto memory_error;
            }
              if (pr_clone_value (&base_value, a->variable))
            {
              goto memory_error;
            }
            }
        }
        }

      /*
       * change the class pointer to reference the base class rather
       * than the virtual class
       */
      class_ = template_ptr->base_class;
    }

      /*
       * populate with the standard default values, ignore duplicate
       * assignments if the virtual class has already supplied
       * a value for these.
       */
      for (att = class_->attributes; att != NULL; att = (SM_ATTRIBUTE *) att->header.next)
    {
      /*
       * can assume that the type is compatible and does not need
       * to be coerced
       */

      if (DB_VALUE_TYPE (&att->default_value.value) != DB_TYPE_NULL)
        {

          exists = template_ptr->assignments[att->order];

          if (exists == NULL)
        {
          a = obt_make_assignment (template_ptr, att);
          if (a == NULL)
            {
              goto memory_error;
            }
          a->is_default = 1;
          a->variable = pr_make_ext_value ();
          if (a->variable == NULL)
            {
              goto memory_error;
            }
          /* would be nice if we could avoid copying here */
          if (pr_clone_value (&att->default_value.value, a->variable))
            {
              goto memory_error;
            }
        }
        }
    }
    }

  return (NO_ERROR);

memory_error:
  /*
   * Here we couldn't allocate sufficient memory for the template and its
   * values. Probably the template should be marked as invalid and
   * the caller be forced to throw it away and start again since
   * its current state is unknown.
   */
  assert (er_errid () != NO_ERROR);
  return er_errid ();
}

/*
 * obt_def_object - This initializes a new template for an instance of
 *                  the given class.
 *      return: new template
 *      class(in): class of the new object
 *
 * Note :
 *    This template can then be populated with assignments and given
 *    to obt_update to create the instances.
 *
 */

OBJ_TEMPLATE *
obt_def_object (MOP class_mop)
{
  OBJ_TEMPLATE *template_ptr = NULL;
  int is_class = locator_is_class (class_mop, DB_FETCH_CLREAD_INSTWRITE);

  if (is_class < 0)
    {
      return NULL;
    }
  if (!is_class)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_NOT_A_CLASS, 0);
    }
  else
    {
      template_ptr = make_template (NULL, class_mop);
    }

  return template_ptr;
}

/*
 * obt_edit_object - This is used to initialize an editing template
 *                   on an existing object.
 *
 *      returns: template
 *      object(in): existing instance
 *
 */

OBJ_TEMPLATE *
obt_edit_object (MOP object)
{
  OBJ_TEMPLATE *template_ptr = NULL;
  int is_class = locator_is_class (object, DB_FETCH_CLREAD_INSTWRITE);

  if (is_class < 0)
    {
      return NULL;
    }
  if (is_class)
    {
      /*
       * create a class object template, these are only allowed to
       * update class attributes
       */
      template_ptr = make_template (object, object);
    }
  else if (!object->is_temp)
    {
      DB_OBJECT *class_;
      /*
       * Need to make sure we have the class accessible, don't just
       * dereference obj->class. This gets a read lock early but that's ok
       * since we know we're dealing with an instance here.
       * Should be handling this inside make_template.
       */
      class_ = sm_get_class (object);
      if (class_ != NULL)
    {
      template_ptr = make_template (object, class_);
    }
    }

  else
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_TEMP_OBJECT, 0);
    }

  return template_ptr;
}

/*
 * obt_quit - This is used to abort the creation of an object template
 *            and release all the allocated storage.
 *      return: error code
 *      template(in): template to throw away
 *
 */

int
obt_quit (OBJ_TEMPLATE * template_ptr)
{
  if (template_ptr != NULL)
    {
      obt_free_template (template_ptr);
    }

  return NO_ERROR;
}

/*
 * obt_assign - This is used to assign a value to an attribute
 *              in an object template.
 *    return: error code
 *    template(in): object template
 *    att(in):
 *    base_assignment(in): non-zero if attribute/value are base class values.
 *    value(in): value to assign
 *    valid(in):
 *
 * Note:
 *    The usual semantic checking on assignment will be performed and
 *    an error returned if the assignment would be invalid.
 *    If the base_assignment flag is zero (normal), the name/value pair
 *    must correspond to the virtual class definition and translation
 *    will be performed if this is a template on a vclass.  If the
 *    base_assignment flag is non-zero, the name/value pair are assumed
 *    to correspond to the base class and translation is not performed.
 *    If this is not a template on a virtual class, the flag has
 *    no effect.
 */

int
obt_assign (OBJ_TEMPLATE * template_ptr, SM_ATTRIBUTE * att, int base_assignment, DB_VALUE * value,
        SM_VALIDATION * valid)
{
  int error = NO_ERROR;
  OBJ_TEMPASSIGN *assign;
  DB_VALUE *actual, base_value;
  const char *base_name;
  DB_AUTH auth;
  DB_OBJECT *object;

  db_make_null (&base_value);

  if ((template_ptr == NULL) || (att == NULL) || (value == NULL))
    {
      ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
      goto error_exit;
    }

  if (validate_template (template_ptr))
    {
      goto error_exit;
    }

  if (!base_assignment && template_ptr->base_class != NULL
      /* Don't translate class attributes */
      && template_ptr->object != template_ptr->classobj)
    {
      /*
       * it's virtual, we could check for assignment validity before calling
       * the value translator
       */

      auth = (template_ptr->object == NULL) ? DB_AUTH_INSERT : DB_AUTH_UPDATE;

      if (mq_update_attribute (template_ptr->classobj, att->header.name, template_ptr->base_classobj, value,
                   &base_value, &base_name, auth))
    {
      goto error_exit;
    }

      /* find the associated attribute definition in the base class */
      if (obt_find_attribute (template_ptr, 1, base_name, &att))
    {
      goto error_exit;
    }

      /* switch to the translated value, who owns this ? */
      value = &base_value;
    }

  /* check for duplicate assignments */
  assign = NULL;
  if (template_ptr->assignments)
    {
      assign = template_ptr->assignments[att->order];
    }

  if (assign)
    {
      if (template_ptr->discard_on_finish)
    {
      ERROR1 (error, ER_OBJ_DUPLICATE_ASSIGNMENT, att->header.name);
      goto error_exit;
    }
    }

  /* check assignment validity */
  object = OBT_BASE_OBJECT (template_ptr);
  actual = obt_check_assignment (att, value, valid, template_ptr->force_check_not_null);
  if (actual == NULL)
    {
      goto error_exit;
    }
  else
    {
      assign = obt_make_assignment (template_ptr, att);
      if (assign == NULL)
    {
      goto error_exit;
    }
    }

  if (actual != value)
    {
      if (assign->variable)
    {
      pr_free_ext_value (assign->variable);
    }
      assign->variable = actual;
    }
  else
    {
      if (assign->variable)
    {
      /*
       *
       * Clear the contents, but recycle the container.
       */
      (void) pr_clear_value (assign->variable);
    }
      else
    {
      assign->variable = pr_make_ext_value ();
      if (assign->variable == NULL)
        {
          goto error_exit;
        }
    }
      /*
       *
       * Note that this copies the set value, might not want to do this
       * when called by the interpreter under controlled conditions,
       *
       * !!! See about optimizing this so we don't do so much set copying !!!
       */
      error = pr_clone_value (value, assign->variable);
    }

  return error;

error_exit:
  assert (er_errid () != NO_ERROR);
  return er_errid ();
}

/*
 * obt_assign_obt - This is used to assign another object template as
 *                  the value of an attribute in an object template
 *    return: error code
 *    template(in): object template
 *    att(in):
 *    base_assignment(in): non-zero if base_class assignment
 *    value(in): nested object template to assign
 *
 * Note:
 *    This is the way that hierarchies of nested objects are specified
 *    using templates.
 *    See the description of obt_assign() for more information
 *    on the meaning of the base_assignment flag.
 *    NOTE: obt_set_obt & obt_assign_obt were split to be consistent
 *    with obt_set/obt_assign but we don't currently have a need
 *    to use obt_assign_obt with a non-zero value for base_assignment.
 *
 */

static int
obt_assign_obt (OBJ_TEMPLATE * template_ptr, SM_ATTRIBUTE * att, int base_assignment, OBJ_TEMPLATE * value)
{
  int error = NO_ERROR;
  OBJ_TEMPASSIGN *assign;
  DB_VALUE dummy_value, base_value;
  const char *base_name;
  DB_AUTH auth;

  db_make_null (&base_value);
  db_make_null (&dummy_value);

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

  if (!base_assignment && template_ptr->base_class != NULL)
    {
      auth = (template_ptr->object == NULL) ? DB_AUTH_INSERT : DB_AUTH_UPDATE;
      if (mq_update_attribute (template_ptr->classobj, att->header.name, template_ptr->base_classobj, &dummy_value,
                   &base_value, &base_name, auth))
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

      /* find the associated attribute definition in the base class */
      if (obt_find_attribute (template_ptr, 1, base_name, &att))
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }
    }

  if (att->domain->type != tp_Type_object)
    {
      ERROR3 (error, ER_OBJ_ATTRIBUTE_TYPE_CONFLICT, att->header.name, att->domain->type->name, tp_Type_object->name);
    }
  else
    {
      /* check duplicate assigmnent */
      assign = template_ptr->assignments[att->order];
      if (assign != NULL && template_ptr->discard_on_finish)
    {
      ERROR1 (error, ER_OBJ_DUPLICATE_ASSIGNMENT, att->header.name);
    }
      else
    {
      /*
       * obt_check_assignment doesn't accept templates, this is a rather
       * controled condition, the only thing we need to check for
       * is a valid class hierarchy
       */
      if (!sm_check_class_domain (att->domain, value->classobj))
        {
          /* if we don't free value now, it will leak */
          obt_free_template (value);
          ERROR1 (error, ER_OBJ_DOMAIN_CONFLICT, att->header.name);
        }
      else if (sm_is_reuse_oid_class (value->classobj))
        {
          obt_free_template (value);
          ERROR0 (error, ER_REFERENCE_TO_NON_REFERABLE_NOT_ALLOWED);
        }
      else
        {
          assign = obt_make_assignment (template_ptr, att);
          if (assign == NULL)
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();
        }
          else
        {
          assign->obj = value;
        }
        }
    }
    }

  return error;
}

/*
 * obt_set -
 *    return: error code
 *    template(in): attname
 *    attname(in): value
 *    value(in):
 *
 * Note:
 *    This is just a shell around obt_assign that doesn't
 *    make the base_assignment flag public.
 *    Recognize the value type DB_TYPE_POINTER as meaning the pointer
 *    is another template rather than an object.
 */

int
obt_set (OBJ_TEMPLATE * template_ptr, const char *attname, DB_VALUE * value)
{
  int error = NO_ERROR;
  SM_ATTRIBUTE *att;

  if ((template_ptr == NULL) || (attname == NULL) || (value == NULL))
    {
      ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
    }
  else
    {
      if (validate_template (template_ptr))
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

      if (obt_find_attribute (template_ptr, 0, attname, &att))
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

      if (DB_VALUE_TYPE (value) == DB_TYPE_POINTER)
    {
      error = obt_assign_obt (template_ptr, att, 0, (OBJ_TEMPLATE *) db_get_pointer (value));
    }
      else
    {
      error = obt_assign (template_ptr, att, 0, value, NULL);
    }
    }

  return error;
}

/* temporary backward compatibility */
/*
 * obt_set_obt -
 *    return: error code
 *    template(in):
 *    attname(in):
 *    value(in):
 *
 */
int
obt_set_obt (OBJ_TEMPLATE * template_ptr, const char *attname, OBJ_TEMPLATE * value)
{
  DB_VALUE v;

  db_make_pointer (&v, value);

  return (obt_set (template_ptr, attname, &v));
}

/*
 * obt_set_desc - This is similar to obt_set() except that
 *                the attribute is identified through a descriptor rather than
 *                an attribute name.
 *      return: error code
 *      template(in): object template
 *      desc(in): attribute descriptor
 *      value(in): value to assign
 *
 */

int
obt_desc_set (OBJ_TEMPLATE * template_ptr, SM_DESCRIPTOR * desc, DB_VALUE * value)
{
  int error = NO_ERROR;
  SM_CLASS *class_;
  SM_ATTRIBUTE *att;

  if ((template_ptr == NULL) || (desc == NULL) || (value == NULL))
    {
      ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS);
    }
  else
    {
      if (validate_template (template_ptr))
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

      /*
       * Note that we pass in the outer class MOP rather than an object
       * since we don't necessarily have an object at this point.
       */
      if (sm_get_descriptor_component (template_ptr->classobj, desc, 1, &class_, (SM_COMPONENT **) (&att)))
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

      if (DB_VALUE_TYPE (value) == DB_TYPE_POINTER)
    {
      error = obt_assign_obt (template_ptr, att, 0, (OBJ_TEMPLATE *) db_get_pointer (value));
    }
      else
    {
      error = obt_assign (template_ptr, att, 0, value, desc->valid);
    }
    }

  return error;
}


/*
 * create_template_object -
 *    return: MOP of new object
 *    template(in):
 */


static MOP
create_template_object (OBJ_TEMPLATE * template_ptr)
{
  MOP mop;
  char *obj;
  SM_CLASS *class_;

  mop = NULL;

  /* must flag this condition */
  ws_class_has_object_dependencies (template_ptr->classobj);

  class_ = template_ptr->class_;

  /*
   * NOTE: garbage collection can occur in either the call to locator_add_instance
   * or vid_add_virtual_instance (which calls locator_add_instance).  The object
   * we're caching can't contain any object references that aren't rooted
   * elsewhere.  Currently this is the case since the object is empty
   * and will be populated later with information from the template which IS
   * a GC root.
   */
  if (class_->class_type != SM_VCLASS_CT)
    {
      obj = obj_alloc (class_, 0);
      if (obj != NULL)
    {
      mop = locator_add_instance (obj, template_ptr->classobj);
    }
    }
  else
    {
      /* virtual instance, base_class must be supplied */
      obj = obj_alloc (template_ptr->base_class, 0);
      if (obj != NULL)
    {
      /* allocate 2 MOP's */
      mop =
        vid_add_virtual_instance (obj, template_ptr->classobj, template_ptr->base_classobj,
                      template_ptr->base_class);
    }
    }

  if (mop != NULL)
    {
      template_ptr->object = mop;

      /* set the label if one is defined */
      if (template_ptr->label != NULL)
    {
      db_make_object (template_ptr->label, mop);
    }

      /* if this is a virtual instance insert, cache the base instance too */
      if (template_ptr->base_class != NULL)
    {

      /* probably don't need the first test in the if at this point */
      if (mop->is_vid && !vid_is_base_instance (mop))
        {
          template_ptr->base_object = vid_get_referenced_mop (mop);
        }
      else
        {
          template_ptr->base_object = mop;
        }
    }
    }

  return mop;
}

/*
 * access_object - This is a preprocessing function called by
 *                 obt_apply_assignments.
 *    return: error code
 *    template(in): object template
 *    object(in):
 *    objptr(out): pointer to instance (returned)
 *
 * Note:
 *    It ensures that the object associated with the template is locked
 *    and created if necessary.
 */
static int
access_object (OBJ_TEMPLATE * template_ptr, MOP * object, MOBJ * objptr)
{
  int error = NO_ERROR;
  MOP classobj, mop;
  MOBJ obj;

  /*
   * The class and instance was already locked&fetched when the template was created.
   * The class pointer was cached since they are always pinned.
   * To avoid pinning the instance through a scope we don't control,
   * they aren't pinned during make_template but rather are "fetched"
   * again and pinned during obt_apply_assignments()
   * Authorization was checked when the template was created so don't
   * do it again.
   */

  if (template_ptr->is_class_update)
    {
      /* object is the class but there is no memory pointer */
      *object = OBT_BASE_CLASSOBJ (template_ptr);
      *objptr = NULL;
      return NO_ERROR;
    }

  obj = NULL;

  /*
   * First, check to see if this is an INSERT template and if so, create
   * the new object.
   */
  if (template_ptr->object == NULL)
    {
      if (create_template_object (template_ptr) == NULL)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }
    }

  /*
   * Now, fetch/lock the instance and mark the class.
   * At this point, we want to be dealing with only the base object.
   */

  if (template_ptr->base_classobj != NULL)
    {
      classobj = template_ptr->base_classobj;
      mop = template_ptr->base_object;
    }
  else
    {
      classobj = template_ptr->classobj;
      mop = template_ptr->object;
    }

  if (mop != NULL)
    {
      error = au_fetch_instance_force (mop, &obj, AU_FETCH_UPDATE, LC_FETCH_MVCC_VERSION);
      if (error == NO_ERROR)
    {
      /* must call this when updating instances */
      ws_class_has_object_dependencies (classobj);
    }
    }

  if (obj == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
    }
  else
    {
      mop->pruning_type = template_ptr->pruning_type;
      *object = mop;
      *objptr = obj;
    }

  return error;
}

/*
 * obt_convert_set_templates - Work function for obt_apply_assignments.
 *    return: error code
 *    setref(in): set pointer from a template
 *    check_uniques(in):
 *
 * Note:
 *    This will iterate through the elements of a set (or sequence) and
 *    convert any elements that are templates in to actual instances.
 *    It will recursively call obt_apply_assignments for the templates
 *    found in the set.
 */

static int
obt_convert_set_templates (SETREF * setref, int check_uniques)
{
  int error = NO_ERROR;
  DB_VALUE *value = NULL;
  OBJ_TEMPLATE *template_ptr;
  int i, set_size;
  SETOBJ *set;

  if (setref != NULL)
    {
      set = setref->set;
      if (set != NULL)
    {
      set_size = setobj_size (set);
      for (i = 0; i < set_size && error == NO_ERROR; i++)
        {
          setobj_get_element_ptr (set, i, &value);
          if (value != NULL && DB_VALUE_TYPE (value) == DB_TYPE_POINTER)
        {
          /* apply the template for this element */
          template_ptr = (OBJ_TEMPLATE *) db_get_pointer (value);
          error = obt_apply_assignments (template_ptr, check_uniques, 1);
          /* 1 means do eager flushing of (set-nested) proxy objects */
          if (error == NO_ERROR && template_ptr != NULL)
            {
              db_make_object (value, template_ptr->object);
              obt_free_template (template_ptr);
            }
        }
        }
    }
    }

  return error;
}

/*
 * obt_final_check_set - This is called when a set value is encounterd in
 *                       a template that is in the final semantic checking phase.
 *    return: error code
 *    setref(in): object template that provked this call
 *    has_uniques(in):
 *
 * Note:
 *    We must go through the set and look for each element that is itself
 *    a template for a new object.
 *    When these are found, recursively call obt_final_check to make sure
 *    these templates look ok.
 */

static int
obt_final_check_set (SETREF * setref, int *has_uniques)
{
  int error = NO_ERROR;
  DB_VALUE *value = NULL;
  OBJ_TEMPLATE *template_ptr;
  SETOBJ *set;
  int i, set_size;

  if (setref != NULL)
    {
      set = setref->set;
      if (set != NULL)
    {
      set_size = setobj_size (set);
      for (i = 0; i < set_size && error == NO_ERROR; i++)
        {
          setobj_get_element_ptr (set, i, &value);
          if (value != NULL && DB_VALUE_TYPE (value) == DB_TYPE_POINTER)
        {
          template_ptr = (OBJ_TEMPLATE *) db_get_pointer (value);
          error = obt_final_check (template_ptr, 1, has_uniques);
        }
        }
    }
    }

  return error;
}

/*
 * obt_check_missing_assignments - This checks a list of attribute definitions
 *                             against a template and tries to locate missing
 *                             assignments in the template that are required
 *                             in order to process an insert template.
 *    return: error code
 *    template(in): template being processed
 *
 * Note:
 *    This includes missing initializers for attributes that are defined
 *    to be NON NULL.
 *    It also includes attributes defined with a VID flag.
 */

int
obt_check_missing_assignments (OBJ_TEMPLATE * template_ptr)
{
  int error = NO_ERROR;
  SM_CLASS *class_;
  SM_ATTRIBUTE *att;
  OBJ_TEMPASSIGN *ass;

  /* only do this if its an insert template */

  if (template_ptr->object == NULL)
    {
      /* use the base_class if this is a virtual class insert */
      class_ = OBT_BASE_CLASS (template_ptr);

      for (att = class_->ordered_attributes; att != NULL && error == NO_ERROR; att = att->order_link)
    {

      if (((att->flags & SM_ATTFLAG_NON_NULL) && DB_IS_NULL (&att->default_value.value)
           && att->default_value.default_expr.default_expr_type == DB_DEFAULT_NONE)
          || (att->flags & SM_ATTFLAG_VID))
        {
          ass = template_ptr->assignments[att->order];
          if (ass == NULL)
        {
          if (att->flags & SM_ATTFLAG_NON_NULL)
            {
              ERROR1 (error, ER_OBJ_MISSING_NON_NULL_ASSIGN, att->header.name);
            }
          if (att->flags & SM_ATTFLAG_VID)
            {
              ERROR1 (error, ER_SM_OBJECT_ID_NOT_SET, sm_ch_name ((MOBJ) (template_ptr->class_)));
            }
        }
        }
    }
    }

  return error;
}

/*
 * obt_final_check
 *    return: error code
 *    template(in): object template
 *    check_non_null(in):
 *    has_uniques(in):
 *
 */

static int
obt_final_check (OBJ_TEMPLATE * template_ptr, int check_non_null, int *has_uniques)
{
  int error = NO_ERROR;
  OBJ_TEMPASSIGN *a;
  int i;

  /* have we already been here ? */
  if (template_ptr->traversal == obj_Template_traversal)
    {
      return NO_ERROR;
    }
  template_ptr->traversal = obj_Template_traversal;

  if (validate_template (template_ptr))
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  if (!template_ptr->is_class_update)
    {

      /*
       * We locked the object when the template was created, this
       * should still be valid.  If not, it should have been detected
       * by validate_template above.
       * Could create the new instances here but wait for a later step.
       */

      /*
       * Check missing assignments on an insert template, should be able
       * to optimize this, particularly when checking for uninitialized
       * shared attributes.
       */
      if (template_ptr->object == NULL)
    {
      if (obt_Enable_autoincrement == true && populate_auto_increment (template_ptr))
        {
          assert (er_errid () != NO_ERROR);
          return er_errid ();
        }

      if (populate_defaults (template_ptr))
        {
          assert (er_errid () != NO_ERROR);
          return er_errid ();
        }

      if (check_non_null && obt_check_missing_assignments (template_ptr))
        {
          assert (er_errid () != NO_ERROR);
          return er_errid ();
        }
    }

      /* does this template have uniques? */
      if (template_ptr->uniques_were_modified)
    {
      *has_uniques = 1;
    }

      /* this template looks ok, recursively go through the sub templates */
      for (i = 0; i < template_ptr->nassigns && error == NO_ERROR; i++)
    {
      a = template_ptr->assignments[i];
      if (a == NULL)
        {
          continue;
        }
      if (a->obj != NULL)
        {
          /* the non-null flag is only used for the outermost template */
          error = obt_final_check (a->obj, 1, has_uniques);
        }
      else
        {
          DB_TYPE av_type;

          av_type = DB_VALUE_TYPE (a->variable);
          if (TP_IS_SET_TYPE (av_type))
        {
          error = obt_final_check_set (db_get_set (a->variable), has_uniques);
        }
        }
    }

      /* check unique_constraints, but only if not disabled */
      /*
       * test & set interface doesn't work right now, full savepoints are instead
       * being performed in obt_update_internal.
       */
    }
  return (error);
}

/*
 * obt_apply_assignment - This is used to apply the assignments in an object
 *                        template after all of the appropriate semantic
 *                        checking has taken place.
 *    return: error code
 *    op(in): class or instance pointer
 *    att(in): attribute descriptor
 *    mem(in): instance memory pointer (instance attribute only)
 *    value(in): value to assign
 *    check_uniques(in):
 *
 * Note:
 *    This used to be a lot more complicated because the translation
 *    of virtual values to base values was deferred until this step.
 *    Now, the values are translated immediately when they are added
 *    to the template.
 */

static int
obt_apply_assignment (MOP op, SM_ATTRIBUTE * att, char *mem, DB_VALUE * value, int check_uniques)
{
  int error = NO_ERROR;

  if (!TP_IS_SET_TYPE (TP_DOMAIN_TYPE (att->domain)))
    {
      error = obj_assign_value (op, att, mem, value);
    }
  else
    {
      /* for sets, first apply any templates in the set */
      error = obt_convert_set_templates (db_get_set (value), check_uniques);
      if (error == NO_ERROR)
    {

      /* BE VERY CAREFUL HERE, IN THE OLD VERSION THE SET WAS BEING COPIED ? */
      error = obj_assign_value (op, att, mem, value);
    }
    }

  return error;
}

/*
 * obt_apply_assignments -
 *    return: error code
 *    template(in): object template
 *    check_uniques(in): true iff check unique constraints
 *    level(in): level of recursion (0 for outermost call)
 *
 * Note:
 *    This is used to apply the assignments in an object template after all
 *    of the appropriate semantic checking has taken place.  Technically,
 *    there shouldn't be any errors here.  If errors do occurr, they will
 *    not cause a rollback of any partially applied assignments.  The only
 *    place this is likely to happen is if there are problems updating
 *    the unique constraint table but even this would represent a serious
 *    internal error that may have other consequences as well.
 *    Update triggers on the individual instances are fired here.
 *    If level==0 then do lazy flushing of proxy objects. If level > 0 then
 *    do eager flushing of proxy objects because it's a nested proxy insert.
 */

static int
obt_apply_assignments (OBJ_TEMPLATE * template_ptr, int check_uniques, int level)
{
  int error = NO_ERROR;
  OBJ_TEMPASSIGN *a;
  DB_VALUE val;
  int pin, trigstate;
  TR_STATE *trstate;
  DB_OBJECT *temp;
  DB_TRIGGER_EVENT event;
  SM_CLASS *class_;
  DB_OBJECT *object = NULL;
  MOBJ mobj = NULL;
  char *mem;
  int i;

  /* have we already been here ? */
  if (template_ptr->traversal == obj_Template_traversal)
    {
      return NO_ERROR;
    }
  template_ptr->traversal = obj_Template_traversal;

  /* make sure we have a good template */
  if (validate_template (template_ptr))
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  /* perform all operations on the base class */
  class_ = OBT_BASE_CLASS (template_ptr);

  /*
   * figure out what kind of triggers to fire here, only do this
   * if the class indicates that there are active triggers
   */
  trigstate = sm_active_triggers (OBT_BASE_CLASSOBJ (template_ptr), class_, TR_EVENT_ALL);
  if (trigstate < 0)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  event = TR_EVENT_NULL;
  if (trigstate)
    {
      if (template_ptr->object == NULL)
    {
      event = TR_EVENT_INSERT;
    }
      else
    {
      event = TR_EVENT_UPDATE;
    }
    }

  /* Collect triggers */
  trstate = NULL;
  temp = NULL;
  if (event != TR_EVENT_NULL)
    {
      if (tr_prepare_class (&trstate, class_->triggers, OBT_BASE_CLASSOBJ (template_ptr), event))
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }
      if (event == TR_EVENT_UPDATE)
    {
      for (i = 0; i < template_ptr->nassigns; i++)
        {
          a = template_ptr->assignments[i];
          if (a == NULL)
        {
          continue;
        }
          if (tr_prepare_class (&trstate, a->att->triggers, OBT_BASE_CLASSOBJ (template_ptr), event))
        {
          tr_abort (trstate);

          assert (er_errid () != NO_ERROR);
          return er_errid ();
        }
        }
    }
    }

  /* Evaluate BEFORE triggers */
  pin = -1;
  if (trstate == NULL)
    {
      /* no triggers, lock/create the object */
      error = access_object (template_ptr, &object, &mobj);
      if (error == NO_ERROR)
    {
      pin = ws_pin (object, 1);
    }
    }
  else
    {
      /* make the temporary object for the template */
      temp = make_temp_object (OBT_BASE_CLASSOBJ (template_ptr), template_ptr);
      if (temp == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
    }
      else
    {
      if (event == TR_EVENT_INSERT)
        {
          /* evaluate triggers before creating the object */
          if (!(error = tr_before_object (trstate, NULL, temp)))
        {
          /* create the new object */
          if (!(error = access_object (template_ptr, &object, &mobj)))
            {
              pin = ws_pin (object, 1);
            }
        }
          else
        trstate = NULL;
        }
      else
        {
          /* lock the object first, then evaluate the triggers */
          if (!(error = access_object (template_ptr, &object, &mobj)))
        {
          if ((error = tr_before_object (trstate, object, temp)))
            {
              trstate = NULL;
            }
        }

          /* in some cases, the object has been decached in before trigger. we need fetch it again. */
          if (error == NO_ERROR && object->decached)
        {
          error = au_fetch_instance_force (object, &mobj, AU_FETCH_UPDATE, LC_FETCH_MVCC_VERSION);
          if (error != NO_ERROR)
            {
              if (trstate != NULL)
            {
              tr_abort (trstate);
            }
              if (temp != NULL)
            {
              free_temp_object (temp);
            }

              if (WS_IS_DELETED (object))
            {
              return NO_ERROR;
            }

              return error;
            }
        }
          /* set pin after before trigger */
          pin = ws_pin (object, 1);
        }
    }
    }

  /* Apply the assignments */
  for (i = 0; i < template_ptr->nassigns && error == NO_ERROR; i++)
    {
      a = template_ptr->assignments[i];
      if (a == NULL)
    continue;

      /* find memory pointer if this is an instance attribute */
      mem = NULL;
      if (a->att->header.name_space == ID_ATTRIBUTE && mobj != NULL)
    {
      mem = (char *) mobj + a->att->offset;
    }

      /* save old value for AFTER triggers */
      if (trstate != NULL && trstate->triggers != NULL && event == TR_EVENT_UPDATE)
    {
      a->old_value = pr_make_ext_value ();
      if (a->old_value == NULL)
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();
        }
      else
        {
          /*
           * this will copy the value which is unfortunate since
           * we're just going to throw it away later
           */
          error = obj_get_value (object, a->att, mem, NULL, a->old_value);
        }
    }

      /*
       * The following code block is for handling LOB type.
       * If the client is the log applier, it doesn't care LOB type.
       */
      if (db_get_client_type () != DB_CLIENT_TYPE_LOG_APPLIER)
    {

      if (a->att->type->id == DB_TYPE_BLOB || a->att->type->id == DB_TYPE_CLOB)
        {
          DB_VALUE old;
          DB_TYPE value_type;

          db_value_domain_init (&old, a->att->type->id, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
          error = obj_get_value (object, a->att, mem, NULL, &old);
          if (error == NO_ERROR && !db_value_is_null (&old))
        {
          DB_ELO *elo;

          value_type = db_value_type (&old);
          assert (value_type == DB_TYPE_BLOB || value_type == DB_TYPE_CLOB);
          elo = db_get_elo (&old);
          if (elo)
            {
              error = db_elo_delete (elo);
            }
          db_value_clear (&old);
          error = (error >= 0 ? NO_ERROR : error);
        }
          if (error == NO_ERROR && !db_value_is_null (a->variable))
        {
          DB_ELO dest_elo, *elo_p;
          char *save_meta_data;

          value_type = db_value_type (a->variable);
          assert (value_type == DB_TYPE_BLOB || value_type == DB_TYPE_CLOB);
          elo_p = db_get_elo (a->variable);

          assert (sm_ch_name ((MOBJ) class_) != NULL);
          save_meta_data = elo_p->meta_data;
          elo_p->meta_data = (char *) sm_ch_name ((MOBJ) class_);
          error = db_elo_copy (db_get_elo (a->variable), &dest_elo);
          elo_p->meta_data = save_meta_data;

          error = (error >= 0 ? NO_ERROR : error);
          if (error == NO_ERROR)
            {
              db_value_clear (a->variable);
              db_make_elo (a->variable, value_type, &dest_elo);
              (a->variable)->need_clear = true;
            }
        }
        }           /* if (a->att->type->id == DB_TYPE_BLOB) || */
    }           /* if (db_get_client_type () != */

      if (error == NO_ERROR)
    {
      /* check for template assignment that needs to be expanded */
      if (a->obj != NULL)
        {
          /* this is a template assignment, recurse on this template */
          error = obt_apply_assignments (a->obj, check_uniques, level + 1);
          if (error == NO_ERROR)
        {
          db_make_object (&val, a->obj->object);
          error = obt_apply_assignment (object, a->att, mem, &val, check_uniques);
        }
        }
      else
        {
          /* non-template assignment */
          error = obt_apply_assignment (object, a->att, mem, a->variable, check_uniques);
        }
    }
    }

  if ((error == NO_ERROR) && (object != NULL))
    {
      ws_dirty (object);
    }

  /* if we updated any shared attributes, we need to mark the class dirty */
  if (template_ptr->shared_was_modified)
    {
      ws_dirty (OBT_BASE_CLASSOBJ (template_ptr));
    }

  /* unpin the object */
  if (pin != -1)
    {
      (void) ws_pin (object, pin);
    }

  /* run after triggers */
  if (trstate != NULL)
    {
      if (error)
    {
      tr_abort (trstate);
    }
      else
    {
      if (event == TR_EVENT_INSERT)
        {
          error = tr_after_object (trstate, object, NULL);
        }
      else
        {
          /* mark the template as an "old" object */
          template_ptr->is_old_template = 1;
          error = tr_after_object (trstate, object, temp);
        }
    }
    }

  if (temp != NULL)
    {
      /* free this after both before and after triggers have run */
      free_temp_object (temp);
    }

  /*
   * If this is a virtual instance, we used to flush it back to the server
   * at this point.  But that early flushing is too expensive.  Consider, for
   * example, that all db_template-based proxy inserts go thru this code and
   * experience a 25-30 fold performance slowdown.  Therefore, we delay
   * flushing dirty proxy mops for non-nested proxy inserts.  It's not clear
   * under what conditions we can safely delay flushing of nested proxy
   * inserts, so we don't.
   */
  if (level > 0 && error == NO_ERROR && object && object->is_vid && vid_is_base_instance (object))
    {
      error = vid_flush_and_rehash (object);
    }
  else if (error != NO_ERROR && object && object->is_vid && vid_is_base_instance (object))
    {
      /*
       * if an error occurred in a nested proxy insert such as this
       *   insert into c_h_employee values ('new_e', 123456789,
       *     (insert into c_h_department (dept_no) values (11)),NULL)
       * we must decache the outer proxy object, otherwise a later flush
       * will generate incorrect results. Note that vid_flush_and_rehash
       * already decaches any offending inner nested proxy inserts.
       */
      ws_decache (object);
    }

  /*
   * check for unique constraint violations.
   * if the object has uniques and this is an insert, we must
   * flush the object to ensure that the btrees for the uniques
   * are updated correctly.
   * NOTE: Performed for updates now too since test & set doesn't work.
   */
  if (error == NO_ERROR)
    {
      if ((check_uniques && template_ptr->uniques_were_modified) || template_ptr->fkeys_were_modified
      || template_ptr->function_key_modified || template_ptr->force_flush
      || (template_ptr->check_serializable_conflict && template_ptr->object
          && !OID_ISTEMP (&(template_ptr->object->oid_info.oid))))
    {
      if ((locator_flush_class (OBT_BASE_CLASSOBJ (template_ptr)) != NO_ERROR)
          || (locator_flush_instance (OBT_BASE_OBJECT (template_ptr)) != NO_ERROR))
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();
        }
      /* update template object if this was a partitioned class */
      object = OBT_BASE_OBJECT (template_ptr);
    }
    }

  return error;
}

/*
 * obt_set_label - This is called by the interpreter when a certain template
 *                 is referenced by a interpreter variable (label).
 *      return: none
 *      template(in): object template
 *      label(in): pointer to MOP pointer
 *
 * Note :
 *    In this case, when the template is converted into a MOP, the pointer
 *    supplied is also set to the value of this mop.
 *
 */

void
obt_set_label (OBJ_TEMPLATE * template_ptr, DB_VALUE * label)
{
  template_ptr->label = label;
}

/*
 * obt_disable_unique_checking
 *      return: none
 *      template(in): object template
 *
 * Note :
 *    This is called by the interpreter when doing a bulk update to disable
 *    unique constraint checking on a per instance basis.  It is the
 *    interpreter's responsibility to check for constraints.
 *
 */

void
obt_disable_unique_checking (OBJ_TEMPLATE * template_ptr)
{
  if (template_ptr)
    {
      template_ptr->check_uniques = 0;
    }
}

/*
 * obt_disable_serializable_conflict_checking : disable SERIALIZABLE conflicts
 *                        checking
 *      return: none
 *      template(in): object template
 */
void
obt_disable_serializable_conflict_checking (OBJ_TEMPLATE * template_ptr)
{
  if (template_ptr)
    {
      template_ptr->check_serializable_conflict = 0;
    }
}

/*
 * obt_enable_unique_checking - This is used by the loader to disable unique
 *                              constraint checking for all templates created.
 *                              When templates are created this state is
 *                              incorporated in the template, see make_template()
 *    return:   The previous state is returned.
 *              TRUE  : global unique checking is enabled.
 *              FALSE : global unique checking is disabled.
 *    new_state(in):
 *
 */
bool
obt_enable_unique_checking (bool new_state)
{
  bool old_state = obt_Check_uniques;

  obt_Check_uniques = new_state;
  return (old_state);
}

/*
 * obj_set_force_flush - set force_flush flag of the template
 *
 * return : void
 * template_ptr (in/out)
 */
void
obt_set_force_flush (OBJ_TEMPLATE * template_ptr)
{
  assert (template_ptr != NULL);

  template_ptr->force_flush = 1;
}

/*
 * obj_reset_force_flush - reset force_flush flag of the template
 *
 * return : void
 * template_ptr (in/out)
 */
void
obt_reset_force_flush (OBJ_TEMPLATE * template_ptr)
{
  assert (template_ptr != NULL);

  template_ptr->force_flush = 0;
}

/*
 * obt_retain_after_finish
 *    return: none
 *    template(in):
 *
 */
void
obt_retain_after_finish (OBJ_TEMPLATE * template_ptr)
{
  assert (template_ptr != NULL);

  template_ptr->discard_on_finish = 0;
}

/*
 * obt_update_internal
 *    return: error code
 *    template(in): object template
 *    newobj(in): return pointer to mop of new instance
 *    check_non_null(in): set if this is an internally defined template
 *
 */
int
obt_update_internal (OBJ_TEMPLATE * template_ptr, MOP * newobj, int check_non_null)
{
  int error = NO_ERROR;
  char savepoint_name[80];
  int has_uniques = 0;
  int savepoint_used = 0;

  if (template_ptr != NULL)
    {
      error = validate_template (template_ptr);
      if (error == NO_ERROR)
    {
      /* allocate a new traversal counter for the check pass */
      begin_template_traversal ();
      error = obt_final_check (template_ptr, check_non_null, &has_uniques);
      if (error == NO_ERROR)
        {

          if (db_get_client_type () == DB_CLIENT_TYPE_LOG_APPLIER)
        {
          /*
           * Only one of the log_applier can apply replication
           * logs at the same time.
           * Therefore, log_applier don't need to perform
           * savepoint at this time to maintain unique indexes.
           */

          /* do nothing */
          ;
        }
          else if ((template_ptr->check_uniques && has_uniques) || template_ptr->fkeys_were_modified
               || template_ptr->function_key_modified || template_ptr->force_flush)
        {
          /* Must perform savepoint to handle unique maintenance until the time when test & set will work
           * correctly. We must do a savepoint if this template or any sub template has uniques.  The actual
           * unique tests will be done in obt_apply_assignments(). */

          sprintf (savepoint_name, "%s-%d", OBJ_INTERNAL_SAVEPOINT_NAME, template_savepoint_count++);
          if (tran_system_savepoint (savepoint_name) != NO_ERROR)
            {
              assert (er_errid () != NO_ERROR);
              return er_errid ();
            }
          savepoint_used = 1;
        }

          /* allocate another traversal counter for the assignment pass */
          begin_template_traversal ();
          error = obt_apply_assignments (template_ptr, template_ptr->check_uniques, 0);
          if (error == NO_ERROR)
        {
          if (newobj != NULL)
            {
              *newobj = template_ptr->object;
            }

          /* When discard_on_finish is false, caller should explictly free template */
          if (template_ptr->discard_on_finish)
            {
              obt_free_template (template_ptr);
            }
        }
        }
    }
    }

  /*
   * do we need to rollback due to failure?  We don't rollback if the
   * trans has already been aborted.
   */
  if (error != NO_ERROR && savepoint_used && error != ER_LK_UNILATERALLY_ABORTED)
    {
      (void) tran_abort_upto_system_savepoint (savepoint_name);
    }

  return error;
}

/*
 * Don't change the external interface to allow setting the check_non_null
 * flag.
 */
/*
 * obt_update - This will take an object template and apply all of
 *              the assignments, creating new objects as necessary
 *    return: error code
 *    template(in): object template
 *    newobj(in): return pointer to mop of new instance
 *
 * Note:
 *    If the top level template is for a new object, the mop will be returned
 *    through the "newobj" parameter.
 *    Note that the template will be freed here if successful
 *    so the caller must not asusme that it can be reused.
 *    The check_non_null flag is set in the case where the template
 *    is being created in response to the obj_create() function which
 *    implements the db_create() and db_create_by_name API functions.
 *    Unfortunately, as the functions are defined, there is no way
 *    to supply initial values.  If the class has attributes that are
 *    defined with the NON NULL constraint, the usual template processing
 *    refuses to create the object until the missing values
 *    are supplied.  This means that it is impossible for the "atomic"
 *    functions like db_create() to make an object whose attributes
 *    have the constraint.  This is arguably the correct behavior but
 *    it hoses 4GE since it isn't currently prepared to go check for
 *    creation dependencies and use full templates instead.
 */
int
obt_update (OBJ_TEMPLATE * template_ptr, MOP * newobj)
{
  return obt_update_internal (template_ptr, newobj, 1);
}

/*
 * make_temp_object - This is used to create a temporary object for use
 *                    in trigger processing.
 *    return: temporary object MOP
 *    class(in):
 *    object(in): object template with values
 *
 */
static MOP
make_temp_object (DB_OBJECT * class_, OBJ_TEMPLATE * object)
{
  MOP obj = NULL;

  if (class_ == NULL || object == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_TEMP_OBJECT, 0);
    }
  else
    {
      obj = ws_make_temp_mop ();
      if (obj != NULL)
    {
      obj->class_mop = class_;
      obj->object = (void *) object;
      obj->pruning_type = object->pruning_type;
      /*
       * We have to be very careful here - we need to mimick the old oid
       * for "old" to behave correctly.
       */
      if (object->object)
        {
          obj->oid_info.oid = object->object->oid_info.oid;
        }
    }
    }

  return obj;
}

/*
 * free_temp_object - This frees the temporary object created by
 *                    make_temp_object. It does NOT free the template,
 *                    only the MOP.
 *    return: none
 *    obj(in): temporary object
 *
 */
static void
free_temp_object (MOP obj)
{
  if (obj != NULL)
    {
      obj->class_mop = NULL;
      obj->object = NULL;
      ws_free_temp_mop (obj);
    }
}

/*
 * obt_populate_known_arguments - Populate default and auto_increment
 *                arguments of template_ptr
 *    return: error code if unsuccessful
 *
 *    template_ptr(in): temporary object
 *
 * Note :
 *    This is necessary for INSERT templates.  The assignments are marked
 *    so that if an assignment is later made to the template with the
 *    same name, we don't generate an error because its ok to override
 *    a default value or an auto_increment value.
 *    If an assignment is already found with the name, it is assumed
 *    that an initial value has already been given and the default or
 *    auto_increment value is ignored.
 *
 */
int
obt_populate_known_arguments (OBJ_TEMPLATE * template_ptr)
{
  if (validate_template (template_ptr))
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  if (template_ptr->is_class_update)
    {
      return NO_ERROR;
    }

  if (populate_defaults (template_ptr) != NO_ERROR)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  if (obt_Enable_autoincrement != true)
    {
      return NO_ERROR;
    }

  if (populate_auto_increment (template_ptr) != NO_ERROR)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  return NO_ERROR;
}

/*
 * obt_begin_insert_values -
 *
 *    return: none
 *
 */
void
obt_begin_insert_values (void)
{
  obt_Last_insert_id_generated = false;
}