Skip to content

File transform_cl.c

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

/*
 * transform_cl.c: Functions related to the storage of instances and classes.
 */

#ident "$Id$"

#include "config.h"

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

#include "porting.h"
#include "memory_alloc.h"
#include "error_manager.h"
#include "work_space.h"
#include "oid.h"
#include "object_representation.h"
#include "object_domain.h"
#include "object_primitive.h"
#include "class_object.h"
#include "set_object.h"
#include "transform.h"
#include "transform_cl.h"
#include "schema_manager.h"
#include "locator_cl.h"
#include "object_accessor.h"
#include "trigger_manager.h"
#include "locator.h"
#include "server_interface.h"
#include "execute_statement.h"
#include "db_json.hpp"

#include "dbtype.h"

#if defined (SUPPRESS_STRLEN_WARNING)
#define strlen(s1)  ((int) strlen(s1))
#endif /* defined (SUPPRESS_STRLEN_WARNING) */

/*
 * tf_Allow_fixups
 *
 * Integration kludge.
 * The changes for initialization of the OR_BUF were rather widespread
 * and could not all be done at the same time.
 * This will be OFF until we're sure all the OR_BUF usages are initialized.
 *
 * Note, after this gets checked in, and is shown to work well enough, this
 * variable can be removed, it is only retained to allow for a quick disable
 * in the event that something ugly happens.
 */
const int tf_Allow_fixups = 1;


/*
 * OR_TEMPOID
 *    Information about a temporary OID encountered during a transformation.
 *    These will get stuck onto the LC_OIDMAP structures as they are created
 *    for the LC_OIDSET being maintained inside the OR_FIXUP structure, whew.
 *    LC_OIDSET is a structure that gets sent accross the client/server
 *    boundary so it contains only server safe things.  Unfortuantely, we
 *    need to map entries in the LC_OIDSET universe back into the DB_OBJECT*
 *    and transformation buffer locations that need to get fixed once the
 *    permanent OID is known.  Rather than keep parallel structures for this,
 *    we're allowed to stick a client/side structure on each LC_OIDMAP
 *    entry, this is it.
 *    When the LC_OIDMAP is returned by the server with permanent OIDs, it
 *    will be unpacked INTO an EXISTING LC_OIDMAP structure rather than
 *    creating a new one.  This existing structure will still have pointers
 *    to this structure so once the server has filled in the permanent OIDs
 *    we can simply walk over the LC_OIDMAP, get into the OR_TEMPOID and make
 *    the appropriate modifications.
 */
typedef struct or_tempoid
{

  int refsize;          /* number of elements in array */
  int refcount;         /* our fill pointer */
  void **references;        /* references, NULL terminated */

} OR_TEMPOID;

/*
 * OR_FIXUP
 *    State structure maintained in an OR_BUF that encapuslates a history
 *    of temporary OIDs that we encounter during transformation.
 *    Now that the LC_OIDSET is sufficiently complex, we could just replace
 *    OR_FIXUP with that, but lets keep the encapsulation for awhile in case
 *    we need to add somethign else here.
 */
typedef struct or_fixup
{
  LC_OIDSET *oidset;
} OR_FIXUP;

/*
 * Set functions
 *    These are shorthand macros for functions that are required as
 *    arguments to the variable set functions below.
 *
 */
typedef int (*LSIZER) (void *);
typedef void (*LWRITER) (void *, void *);
typedef DB_LIST *(*LREADER) (void *);
typedef void (*VREADER) (void *, void *);

/*
 * Forward declaration of local functions
 */
static int optimize_sets (SM_CLASS * class_, MOBJ volatile obj);
static OR_FIXUP *tf_make_fixup (void);
static void tf_free_fixup (OR_FIXUP * fix);
static void fixup_callback (LC_OIDMAP * oidmap);
static int tf_do_fixup (OR_FIXUP * fix);
static int put_varinfo (OR_BUF * buf, char *obj, SM_CLASS * class_, int offset_size);
static int object_size (SM_CLASS * class_, MOBJ obj, int *offset_size_ptr);
static int put_attributes (OR_BUF * buf, char *obj, SM_CLASS * class_);
static char *get_current (OR_BUF * buf, SM_CLASS * class_, MOBJ * obj_ptr, int bound_bit_flag, int offset_size);
static SM_ATTRIBUTE *find_current_attribute (SM_CLASS * class_, int id);
static void clear_new_unbound (char *obj, SM_CLASS * class_, SM_REPRESENTATION * oldrep);
static char *get_old (OR_BUF * buf, SM_CLASS * class_, MOBJ * obj_ptr, int repid, int bound_bit_flag, int offset_size);
static char *unpack_allocator (int size);
static OR_VARINFO *read_var_table (OR_BUF * buf, int nvars);
static OR_VARINFO *read_var_table_internal (OR_BUF * buf, int nvars, int offset_size);
static void free_var_table (OR_VARINFO * vars);
static int string_disk_size (const char *string);
static char *get_string (OR_BUF * buf, int length);
static void put_string (OR_BUF * buf, const char *string);
static int object_set_size (DB_OBJLIST * list);
static int put_object_set (OR_BUF * buf, DB_OBJLIST * list);
static DB_OBJLIST *get_object_set (OR_BUF * buf, int expected);
static int substructure_set_size (DB_LIST * list, LSIZER function);
static void put_substructure_set (OR_BUF * buf, DB_LIST * list, LWRITER writer, OID * class_, int repid);
static DB_LIST *get_substructure_set (OR_BUF * buf, LREADER reader, int expected);
static void install_substructure_set (OR_BUF * buf, DB_LIST * list, VREADER reader, int expected);
static int property_list_size (DB_SEQ * properties);
static void put_property_list (OR_BUF * buf, DB_SEQ * properties);
static DB_SEQ *get_property_list (OR_BUF * buf, int expected_size);
static int domain_size (TP_DOMAIN * domain);
static void domain_to_disk (OR_BUF * buf, TP_DOMAIN * domain);
static TP_DOMAIN *disk_to_domain2 (OR_BUF * buf);
static TP_DOMAIN *disk_to_domain (OR_BUF * buf);
static void metharg_to_disk (OR_BUF * buf, SM_METHOD_ARGUMENT * arg);
static int metharg_size (SM_METHOD_ARGUMENT * arg);
static SM_METHOD_ARGUMENT *disk_to_metharg (OR_BUF * buf);
static int methsig_to_disk (OR_BUF * buf, SM_METHOD_SIGNATURE * sig);
static inline void methsig_to_disk_lwriter (void *buf, void *sig);
static int methsig_size (SM_METHOD_SIGNATURE * sig);
static SM_METHOD_SIGNATURE *disk_to_methsig (OR_BUF * buf);
static int method_to_disk (OR_BUF * buf, SM_METHOD * method);
static inline void method_to_disk_lwriter (void *buf, void *method);
static int method_size (SM_METHOD * method);
static void disk_to_method (OR_BUF * buf, SM_METHOD * method);
static int methfile_to_disk (OR_BUF * buf, SM_METHOD_FILE * file);
static inline void methfile_to_disk_lwriter (void *buf, void *file);
static int methfile_size (SM_METHOD_FILE * file);
static SM_METHOD_FILE *disk_to_methfile (OR_BUF * buf);
static int query_spec_to_disk (OR_BUF * buf, SM_QUERY_SPEC * query_spec);
static inline void query_spec_to_disk_lwriter (void *buf, void *query_spec);
static int query_spec_size (SM_QUERY_SPEC * statement);
static SM_QUERY_SPEC *disk_to_query_spec (OR_BUF * buf);
static int attribute_to_disk (OR_BUF * buf, SM_ATTRIBUTE * att);
static inline void attribute_to_disk_lwriter (void *buf, void *att);
static int attribute_size (SM_ATTRIBUTE * att);
static void disk_to_attribute (OR_BUF * buf, SM_ATTRIBUTE * att);
static int resolution_to_disk (OR_BUF * buf, SM_RESOLUTION * res);
static inline void resolution_to_disk_lwriter (void *buf, void *res);
static int resolution_size (SM_RESOLUTION * res);
static SM_RESOLUTION *disk_to_resolution (OR_BUF * buf);
static int repattribute_to_disk (OR_BUF * buf, SM_REPR_ATTRIBUTE * rat);
static inline void repattribute_to_disk_lwriter (void *buf, void *rat);
static int repattribute_size (SM_REPR_ATTRIBUTE * rat);
static SM_REPR_ATTRIBUTE *disk_to_repattribute (OR_BUF * buf);
static int representation_size (SM_REPRESENTATION * rep);
static int representation_to_disk (OR_BUF * buf, SM_REPRESENTATION * rep);
static inline void representation_to_disk_lwriter (void *buf, void *rep);
static SM_REPRESENTATION *disk_to_representation (OR_BUF * buf);
static int check_class_structure (SM_CLASS * class_);
static int put_class_varinfo (OR_BUF * buf, SM_CLASS * class_);
static void put_class_attributes (OR_BUF * buf, SM_CLASS * class_);
static void class_to_disk (OR_BUF * buf, SM_CLASS * class_);
static void tag_component_namespace (SM_COMPONENT * components, SM_NAME_SPACE name_space);
static SM_CLASS *disk_to_class (OR_BUF * buf, SM_CLASS ** class_ptr);
static void root_to_disk (OR_BUF * buf, ROOT_CLASS * root);
static int root_size (MOBJ rootobj);
static int tf_class_size (MOBJ classobj);
static ROOT_CLASS *disk_to_root (OR_BUF * buf);

static int enumeration_size (const DB_ENUMERATION * enumeration);
static void put_enumeration (OR_BUF * buf, const DB_ENUMERATION * e);
static int get_enumeration (OR_BUF * buf, DB_ENUMERATION * enumeration, int expected);
static int tf_attribute_default_expr_to_property (SM_ATTRIBUTE * attr_list);

static int partition_info_to_disk (OR_BUF * buf, SM_PARTITION * partition_info);
static inline void partition_info_to_disk_lwriter (void *buf, void *partition_info);
static SM_PARTITION *disk_to_partition_info (OR_BUF * buf);
static int partition_info_size (SM_PARTITION * partition_info);
static void or_pack_mop (OR_BUF * buf, MOP mop);

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * tf_find_temporary_oids - walks over the memory representation of an
 * instance and finds all the temporary OIDs within it and adds them to the
 * oidset.
 *    return: NO_ERROR if successful, error code otherwise
 *    oidset(out): oidset to populate
 *    classobj(in): object to examine
 */
int
tf_find_temporary_oids (LC_OIDSET * oidset, MOBJ classobj, MOBJ obj)
{
  int error = NO_ERROR;
  SM_CLASS *class_;
  SM_ATTRIBUTE *att;
  DB_TYPE type;
  SETOBJ *col;

  /*
   * Do this only for instance objects.  This means that class objects
   * with temporary oids in, say, class variables won't get flushed at
   * this time, but that's probably ok.  They'll get flushed as part of
   * the ordinary class object packing business, and since they ought to
   * be few in number, that shouldn't have much of an impact.
   */
  if ((classobj != (MOBJ) (&sm_Root_class)) && (obj != NULL))
    {

      class_ = (SM_CLASS *) classobj;

      for (att = class_->attributes; att != NULL && error == NO_ERROR; att = (SM_ATTRIBUTE *) att->header.next)
    {

      type = TP_DOMAIN_TYPE (att->domain);
      if (type == DB_TYPE_OBJECT)
        {
          WS_MEMOID *mem;
          OID *oid;
          mem = (WS_MEMOID *) (obj + att->offset);
          oid = &mem->oid;

          if (OID_ISTEMP (oid) && mem->pointer != NULL && !WS_IS_DELETED (mem->pointer))
        {

          /* Make sure the ws_memoid mop is temporary. */
          if (OID_ISTEMP (WS_OID (mem->pointer)))
            {

              /* its an undeleted temporary object, promote */
              if (locator_add_oidset_object (oidset, mem->pointer) == NULL)
            {
              assert (er_errid () != NO_ERROR);
              error = er_errid ();
            }
            }
        }
        }
      else if (TP_IS_SET_TYPE (type))
        {
          col = *(SETOBJ **) (obj + att->offset);

          error = setobj_find_temporary_oids (col, oidset);
        }
    }
    }

  return error;
}
#endif /* ENABLE_UNUSED_FUNCTION */

/*
 * optimize_sets - Called before the transformation begins to ensure that
 * any sets this object contains are sorted before sending them to disk.
 *    return: NO_ERROR if successful, error code otherwise
 *    class(in): class structure
 *    obj(in/out): object attributes
 * Note:
 *    In practice, sets will be unsorted only if they contained temporary
 *    OIDs at one time in their lives.  This will have the side effect
 *    of doing a bulk OID assignment for each set that is unsorted and then
 *    sorting the set.  This may add some time to the flush process but
 *    the sort will not allocate very much memory so it should be safe in low
 *    memory situations.
 *
 *    This would be more effecient if we split this into two opeations,
 *    the first would walk over the entire object upgrading all temporary
 *    OIDs to permanent ones.  The second sorted the sets.
 *    This would result in one batch OID assignment calls rather than
 *    one for each set.  Unfortuantely, that's a little more complicated as
 *    it requires some new traversal code.  Someday . . .
 */
static int
optimize_sets (SM_CLASS * class_, MOBJ volatile obj)
{
  int error = NO_ERROR;
  SM_ATTRIBUTE *att;
  SETOBJ *col;

  for (att = class_->attributes; att != NULL && error == NO_ERROR; att = (SM_ATTRIBUTE *) att->header.next)
    {
      if (TP_IS_SET_TYPE (TP_DOMAIN_TYPE (att->domain)))
    {
      col = *((SETOBJ **) (obj + att->offset));

      if (col)
        {
          /*
           * col now has the collection pointer.
           * sort it before we flush. The sort operation will produce
           * batched permanent oid's on demand if needed.
           */
          error = setobj_sort (col);
        }
    }
    }
  return error;
}


/*
 * tf_make_fixup - This initializes a new transformer fixup structure for use.
 *    return: fixup structure
 * Note:
 *    It doesn't actually allocate the LC_OIDSET until we find that
 *    we have references.
 */
static OR_FIXUP *
tf_make_fixup (void)
{
  OR_FIXUP *fix;

  fix = (OR_FIXUP *) malloc (sizeof (OR_FIXUP));
  if (fix != NULL)
    {
      fix->oidset = NULL;
    }
  return fix;
}


/*
 * tf_free_fixup - frees a fixup structure create by tf_make_fixup.
 *    return: none
 *    fix(out): fixup structure
 * Note:
 *    This now consists of a single LC_OIDSET structure with
 *    OR_TEMPOID structure embedded within it.
 *    Since locator_free_oid_set doesn't know anything about our appendages,
 *    we have to walk the LC_OIDSET and free them first.
 *
 */
static void
tf_free_fixup (OR_FIXUP * fix)
{
  LC_CLASS_OIDSET *class_;
  LC_OIDMAP *oid;
  OR_TEMPOID *temp;

  if (fix == NULL)
    {
      return;
    }

  if (fix->oidset == NULL)
    {
      free_and_init (fix);
      return;
    }
  /* free our OR_TEMPOID warts */
  for (class_ = fix->oidset->classes; class_ != NULL; class_ = class_->next)
    {
      for (oid = class_->oids; oid != NULL; oid = oid->next)
    {
      if (oid->client_data)
        {
          temp = (OR_TEMPOID *) oid->client_data;
          free_and_init (temp->references);
          free_and_init (temp);
          oid->client_data = NULL;
        }
    }
    }

  /* now free the LC_OIDSET hierarchy */
  locator_free_oid_set (NULL, fix->oidset);
  free_and_init (fix);
}



#define TF_FIXUP_REFERENCE_QUANT 32
/*
 * tf_add_fixup - This adds a fixup "relocation" entry for a temporary OID
 * that's been encountered.
 *    return: error
 *    fix(in/out): fixup state
 *    obj(in): object with temporary OID
 *    ref(in): address of packed reference
 */
static int
tf_add_fixup (OR_FIXUP * fix, DB_OBJECT * obj, void *ref)
{
  LC_OIDMAP *o;
  OR_TEMPOID *t;
  size_t buf_size;

  /* get the oidset we're building */
  if (fix->oidset == NULL)
    {
      fix->oidset = locator_make_oid_set ();
      if (fix->oidset == NULL)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }
    }

  o = locator_add_oidset_object (fix->oidset, obj);
  if (o == NULL)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  /* check our references list */
  t = (OR_TEMPOID *) o->client_data;
  if (t == NULL)
    {
      /* this is a new OID, add our reference wart */
      t = (OR_TEMPOID *) malloc (sizeof (OR_TEMPOID));
      if (t == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (OR_TEMPOID));
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }

      buf_size = sizeof (void *) * TF_FIXUP_REFERENCE_QUANT;
      t->references = (void **) malloc (buf_size);
      if (t->references == NULL)
    {
      free_and_init (t);

      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, buf_size);
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }
      t->refsize = TF_FIXUP_REFERENCE_QUANT;
      t->references[0] = ref;
      t->refcount = 1;
      o->client_data = (void *) t;
    }
  else
    {
      /* we've already had at least one reference, add another one */
      if (t->refcount >= t->refsize)
    {
      /* its time to grow */
      t->refsize = t->refcount + TF_FIXUP_REFERENCE_QUANT;
      buf_size = t->refsize * sizeof (void *);
      t->references = (void **) realloc (t->references, buf_size);
      if (t->references == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, buf_size);
          return ER_OUT_OF_VIRTUAL_MEMORY;
        }
    }
      t->references[t->refcount] = ref;
      t->refcount++;
    }

  return NO_ERROR;
}


/*
 * tf_need_permanent_oid - called when a temporary OID is encountered during
 * various packing places to ensure permanent oid assignment
 *    return: oid to store
 *    buf(in/out): packing buffer
 *    obj(in): object to examine
 * Note:
 *    This is called by the various packing functions when they encounter
 *    a temporary OID that needs to be packed.  If this is an INSTANCE OID,
 *    and we're maintaining a deferred "fixup" buffer, then we return a NULL
 *    oid and add an entry to the fixup state.
 *
 *    If this is a CLASS OID, or if we're not maintaining a fixup buffer,
 *    then we simply call locator_assign_permanent_oid to immeidately get a
 *    permanent OID.
 *
 *    The oid returned by this function does not need to be freed but it
 *    must be used or copied immediately.
 *
 *    NULL is returned on error.
 */
OID *
tf_need_permanent_oid (or_buf * buf, DB_OBJECT * obj)
{
  OID *oidp;

  oidp = NULL;

  /*
   * if we have a fixup buffer, and this is NOT a class object, then make
   * an entry for it.
   */
  if (tf_Allow_fixups && buf->fixups != NULL && obj->class_mop != NULL && obj->class_mop != sm_Root_class_mop)
    {

      if (tf_add_fixup (buf->fixups, obj, buf->ptr) != NO_ERROR)
    {
      // already set error code in tf_add_fixup
      return NULL;
    }
      else
    {
      /* leave the NULL oid in the buffer until we can fix it */
      oidp = (OID *) (&oid_Null_oid);
    }
    }
  else
    {
      /*
       * couldn't make a fixup buffer entry, go to the server and assign
       * it in the usual way.
       */
      if (locator_assign_permanent_oid (obj) == NULL)
    {
      if (er_errid () == NO_ERROR)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_CANT_ASSIGN_OID, 0);
        }

      /* this is serious */
      assert (false);
      return NULL;
    }
      else
    {
      oidp = WS_OID (obj);
    }
    }
  return oidp;
}


/*
 * fixup_callback - LC_OIDMAP_CALLBACK function passed to locator_assign_oidset
 * by tf_do_fixup function.
 *    return: none
 *    oidmap(in): oidmap being processed
 * Note:
 *    This function gets called for each oid that has been made permanent,
 *    here we get our client appendage out of the client_data field, then
 *    whip through the references list so that they can be properly updated.
 */
static void
fixup_callback (LC_OIDMAP * oidmap)
{
  OR_TEMPOID *t;
  int i;

  t = (OR_TEMPOID *) oidmap->client_data;
  if (t != NULL)
    {
      /* fix the packed references */
      for (i = 0; i < t->refcount; i++)
    {
      OR_PUT_OID (t->references[i], &(oidmap->oid));
    }
    }
}


/*
 * tf_do_fixup - Process the fixup operations left over after a transformation.
 *    return: error code
 *    fix(): fixup state
 */
static int
tf_do_fixup (OR_FIXUP * fix)
{
  int error = NO_ERROR;

  if (fix != NULL && fix->oidset != NULL)
    {
      error = locator_assign_oidset (fix->oidset, fixup_callback);
    }

  return error;
}


/*
 * put_varinfo - Write the variable attribute offset table for an instance.
 *    return:  NO_ERROR
 *    buf(in/out): translation buffer
 *    obj(in): instance memory
 *    class(in): class of instance
 */
static int
put_varinfo (OR_BUF * buf, char *obj, SM_CLASS * class_, int offset_size)
{
  SM_ATTRIBUTE *att;
  char *mem;
  int a, offset, len;
  int rc = NO_ERROR;

  if (class_->variable_count)
    {
      /* compute the variable offsets relative to the end of the header (beginning of variable table) */
      offset =
    OR_VAR_TABLE_SIZE_INTERNAL (class_->variable_count,
                    offset_size) + class_->fixed_size + OR_BOUND_BIT_BYTES (class_->fixed_count);

      for (a = class_->fixed_count; a < class_->att_count; a++)
    {
      att = &class_->attributes[a];
      mem = obj + att->offset;

      len = att->domain->type->get_disk_size_of_mem (mem, att->domain);

      or_put_offset_internal (buf, offset, offset_size);
      offset += len;
    }

      or_put_offset_internal (buf, offset, offset_size);
      buf->ptr = PTR_ALIGN (buf->ptr, INT_ALIGNMENT);
    }
  return rc;
}


/*
 * object_size - Calculates the amount of disk storage required for an instance.
 *    return: bytes of disk storage required
 *    class(in): class structure
 *    obj(in): instance to examine
 */
static int
object_size (SM_CLASS * class_, MOBJ obj, int *offset_size_ptr)
{
  SM_ATTRIBUTE *att;
  char *mem;
  int a, size;

  *offset_size_ptr = OR_BYTE_SIZE;

re_check:

  size = OR_MVCC_INSERT_HEADER_SIZE + class_->fixed_size + OR_BOUND_BIT_BYTES (class_->fixed_count);

  if (class_->variable_count)
    {
      size += OR_VAR_TABLE_SIZE_INTERNAL (class_->variable_count, *offset_size_ptr);
      for (a = class_->fixed_count; a < class_->att_count; a++)
    {
      att = &class_->attributes[a];
      mem = obj + att->offset;

      size += att->domain->type->get_disk_size_of_mem (mem, att->domain);
    }
    }

  if (*offset_size_ptr == OR_BYTE_SIZE && size > OR_MAX_BYTE)
    {
      *offset_size_ptr = OR_SHORT_SIZE; /* 2byte */
      goto re_check;
    }
  if (*offset_size_ptr == OR_SHORT_SIZE && size > OR_MAX_SHORT)
    {
      *offset_size_ptr = BIG_VAR_OFFSET_SIZE;   /* 4byte */
      goto re_check;
    }
  return (size);
}


/*
 * put_attributes - Write the fixed and variable attribute values.
 *    buf(in/out): translation buffer
 *    obj(in): instance memory
 *    class(in): class structure
 * Note:
 *    The object  header and offset table will already have been written.
 *    Assumes that the schema manager is smart enough to keep the
 *    attributes in their appropriate disk ordering.
 */
static int
put_attributes (OR_BUF * buf, char *obj, SM_CLASS * class_)
{
  SM_ATTRIBUTE *att;
  char *start;
  int pad;

  /*
   * write fixed attribute values, if unbound, leave zero or garbage
   * it doesn't really matter.
   */
  start = buf->ptr;
  for (att = class_->attributes; att != NULL && !att->type->variable_p; att = (SM_ATTRIBUTE *) att->header.next)
    {
      att->type->data_writemem (buf, obj + att->offset, att->domain);
    }

  /* bring the end of the fixed width block up to proper alignment */
  pad = (int) (buf->ptr - start);
  if (pad < class_->fixed_size)
    {
      or_pad (buf, class_->fixed_size - pad);
    }
  else if (pad > class_->fixed_size)
    {               /* mismatched fixed block calculations */
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SM_CORRUPTED, 0);
      return ER_SM_CORRUPTED;
    }

  /* write the bound bits */
  if (class_->fixed_count)
    {
      or_put_data (buf, obj + OBJ_HEADER_BOUND_BITS_OFFSET, OR_BOUND_BIT_BYTES (class_->fixed_count));
    }
  /* write the variable attributes */

  for (; att != NULL; att = (SM_ATTRIBUTE *) att->header.next)
    {
      att->type->data_writemem (buf, obj + att->offset, att->domain);
    }
  return NO_ERROR;
}


/*
 * tf_mem_to_disk - Translate an instance from its memory format into the
 *                  disk format.
 *    return: zero on success, non-zero on error
 *    classmop(in): class of this instance
 *    classobj(in): class structure
 *    obj(in/out): instance memory to translate
 *    record(out): destination disk record
 *    index_flag(out): set if there are indexes on any attributes
 * Note:
 *    If there was an overflow error on the given record, we determine
 *    the required size and return this size as a negative number, otherwise
 *    a zero is returned to indicate successful translation.
 *    we removed setjmp/longjmp logic, decided to check outside once.
 */
TF_STATUS
tf_mem_to_disk (MOP classmop, MOBJ classobj, MOBJ volatile obj, RECDES * record, bool * index_flag)
{
  OR_BUF orep, *buf;
  SM_CLASS *volatile class_;    /* prevent register optimization */
  int chn;
  bool has_index = false;
  unsigned int repid_bits;
  TF_STATUS status;
  int expected_size;
  int offset_size;

  buf = &orep;
  or_init (buf, record->data, record->area_size);
  if (tf_Allow_fixups)
    {
      buf->fixups = tf_make_fixup ();
      if (buf->fixups == NULL)
    {
      return TF_ERROR;
    }
    }

  class_ = (SM_CLASS *) classobj;

  if (optimize_sets (class_, obj) != NO_ERROR)
    {
      if (tf_Allow_fixups)
    {
      tf_free_fixup (buf->fixups);
    }
      return TF_ERROR;
    }

  expected_size = object_size (class_, obj, (int *) &offset_size);

  if ((expected_size + OR_MVCC_MAX_HEADER_SIZE - OR_MVCC_INSERT_HEADER_SIZE) > record->area_size)
    {
      record->length = -expected_size;

      /* make sure we free this */
      if (tf_Allow_fixups)
    {
      tf_free_fixup (buf->fixups);
    }

      *index_flag = false;
      return (TF_OUT_OF_SPACE);
    }

  status = TF_SUCCESS;

  if (OID_ISTEMP (WS_OID (classmop)))
    {
      /*
       * since this isn't a mem_oid, can't rely on write_oid to do this,
       * don't bother making this part of the deferred fixup stuff yet.
       */
      if (locator_assign_permanent_oid (classmop) == NULL)
    {
      if (er_errid () == NO_ERROR)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_CANT_ASSIGN_OID, 0);
        }
      status = TF_ERROR;
      has_index = false;
      goto exit;
    }
    }

  /* header */

  repid_bits = class_->repid;
  if (class_->fixed_count)
    {
      repid_bits |= OR_BOUND_BIT_FLAG;
    }
  /* offset size */
  OR_SET_VAR_OFFSET_SIZE (repid_bits, offset_size);

  chn = WS_CHN (obj) + 1;

  /* in most of the cases, we expect the MVCC header of a new object to have OR_MVCC_FLAG_VALID_INSID flag,
   * repid_bits, MVCC insert id and chn. This header may be changed later, at insert/update. So, we must be sure
   * that the record has enough free space. */

  repid_bits |= (OR_MVCC_FLAG_VALID_INSID << OR_MVCC_FLAG_SHIFT_BITS);
  if (buf->ptr + expected_size > buf->endptr)
    {
      status = TF_OUT_OF_SPACE;
      record->length = -expected_size;
      has_index = false;
      goto exit;
    }
  else
    {

      or_put_int (buf, repid_bits);
      or_put_int (buf, chn);    /* CHN, short size */
      or_put_bigint (buf, MVCCID_NULL); /* MVCC insert id */

      /* variable info block */
      put_varinfo (buf, obj, class_, offset_size);

      /* attributes, fixed followed by variable */
      put_attributes (buf, obj, class_);
      record->length = (int) (buf->ptr - buf->buffer);

      /* see if there are any indexes */
      has_index = classobj_class_has_indexes (class_);

      /*
       * assign permanent OID's and make the necessary adjustments in
       * the packed buffer.
       */
      if (tf_do_fixup (buf->fixups) != NO_ERROR || er_errid () == ER_OUT_OF_VIRTUAL_MEMORY)
    {
      status = TF_ERROR;
    }

      /*
       * Now that we know the object has been packed up safely, it's safe
       * to update the coherency number of the in-memory image.
       */
      WS_CHN (obj) = chn;

    }
exit:
  /* make sure we free this */
  if (tf_Allow_fixups)
    {
      tf_free_fixup (buf->fixups);
    }

  *index_flag = has_index;

  return (status);
}


/*
 * get_current - Loads an instance from disk using the latest class
 * representation.
 *    return: new instance memory
 *    buf(in/out): translation buffer
 *    class(): class of this instance
 *    obj_ptr(out): loaded object (same as return value)
 *    bound_bit_flag(in): initial status of bound bits
 */
static char *
get_current (OR_BUF * buf, SM_CLASS * class_, MOBJ * obj_ptr, int bound_bit_flag, int offset_size)
{
  SM_ATTRIBUTE *att;
  char *obj, *mem, *start;
  int *vars, rc = NO_ERROR;
  int i, j, offset, offset2, pad;

  obj = NULL;
  /* need nicer way to store these */
  vars = NULL;
  if (class_->variable_count)
    {
      vars = (int *) db_ws_alloc (sizeof (int) * class_->variable_count);
      if (vars == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
          (size_t) (class_->variable_count * sizeof (int)));
      return NULL;
    }
      else
    {
      /* get the offsets relative to the end of the header (beginning of variable table) */
      offset = or_get_offset_internal (buf, &rc, offset_size);
      for (i = 0; i < class_->variable_count; i++)
        {
          offset2 = or_get_offset_internal (buf, &rc, offset_size);
          vars[i] = offset2 - offset;
          offset = offset2;
        }
      buf->ptr = PTR_ALIGN (buf->ptr, INT_ALIGNMENT);
    }
    }

  /*
   * if there are no bound bits on disk, allocate the instance block
   * with all the bits turned on, we have to assume that the values
   * are non-null
   */
  obj = *obj_ptr = obj_alloc (class_, (bound_bit_flag) ? 0 : 1);

  if (obj == NULL)
    {
      if (vars != NULL)
    {
      db_ws_free (vars);
    }
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, class_->object_size);
      return NULL;
    }
  else
    {
      /* fixed */
      start = buf->ptr;
      for (i = 0; i < class_->fixed_count; i++)
    {
      att = &(class_->attributes[i]);
      mem = obj + att->offset;
      att->type->data_readmem (buf, mem, att->domain, -1);
    }

      /* round up to a to the end of the fixed block */
      pad = (int) (buf->ptr - start);
      if (pad < class_->fixed_size)
    {
      or_advance (buf, class_->fixed_size - pad);
    }
      /* bound bits, if not present, we have to assume everything is bound */
      if (bound_bit_flag && class_->fixed_count)
    {
      or_get_data (buf, obj + OBJ_HEADER_BOUND_BITS_OFFSET, OR_BOUND_BIT_BYTES (class_->fixed_count));
    }

      /* variable */
      if (vars != NULL)
    {
      for (i = class_->fixed_count, j = 0; i < class_->att_count && j < class_->variable_count; i++, j++)
        {
          att = &(class_->attributes[i]);
          mem = obj + att->offset;
          att->type->data_readmem (buf, mem, att->domain, vars[j]);
        }
    }
    }

  if (vars != NULL)
    {
      db_ws_free (vars);
    }

  return (obj);
}


/*
 * find_current_attribute - Given an instance attribute id, find the
 * corresponding attribute in the latest representation.
 *    return: pointer to current attribute. NULL if not found
 *    class(in): class structure
 *    id(in): attribute id
 */
static SM_ATTRIBUTE *
find_current_attribute (SM_CLASS * class_, int id)
{
  SM_ATTRIBUTE *found, *att;

  found = NULL;
  for (att = class_->attributes; att != NULL && found == NULL; att = (SM_ATTRIBUTE *) att->header.next)
    {
      if (att->id == id)
    {
      found = att;
    }
    }
  return (found);
}


/*
 * clear_new_unbound - This initializes the space created for instance
 * attributes that had no saved values on disk.
 *    return: void
 *    obj(in/out): instance memory
 *    class(in): class of this instance
 *    oldrep(in): old representation (of instance on disk)
 * Note:
 *    This happens when an object is converted to a new representation
 *    that had one or more attributes added.
 *    It might be more efficient to do this for all fields when the instance
 *    memory is first allocated ?
 *    The original_value is used if it has a value.
 */
static void
clear_new_unbound (char *obj, SM_CLASS * class_, SM_REPRESENTATION * oldrep)
{
  SM_ATTRIBUTE *att;
  SM_REPR_ATTRIBUTE *rat, *found;
  char *mem;

  for (att = class_->attributes; att != NULL; att = (SM_ATTRIBUTE *) att->header.next)
    {
      found = NULL;
      for (rat = oldrep->attributes; rat != NULL && found == NULL; rat = rat->next)
    {
      if (rat->attid == att->id)
        {
          found = rat;
        }
    }
      if (found == NULL)
    {
      mem = obj + att->offset;
      /* initialize in case there isn't an initial value */
      att->type->initmem (mem, att->domain);
      if (!DB_IS_NULL (&att->default_value.original_value))
        {
          /* assign the initial value, should check for non-existance ? */
          att->type->setmem (mem, att->domain, &att->default_value.original_value);
          if (!att->type->variable_p)
        {
          OBJ_SET_BOUND_BIT (obj, att->storage_order);
        }
        }
    }
    }
}


/*
 * get_old - This creates an instance from an obsolete disk representation.
 *    return: new instance memory
 *    buf(in/out): translation buffer
 *    class(in): class of this instance
 *    obj_ptr(out): return value
 *    repid(in): old representation id
 *    bound_bit_flag(in): initial status of bound bits
 * Note:
 *    Any new attributes that had no values on disk are given a starting
 *    value from the "original_value" field in the class.
 *    Values for deleted attributes are discarded.
 */
static char *
get_old (OR_BUF * buf, SM_CLASS * class_, MOBJ * obj_ptr, int repid, int bound_bit_flag, int offset_size)
{
  SM_REPRESENTATION *oldrep;
  SM_REPR_ATTRIBUTE *rat;
  SM_ATTRIBUTE **attmap;
  const PR_TYPE *type;
  char *obj, *bits, *start;
  int *vars = NULL, rc = NO_ERROR;
  int i, total, offset, offset2, bytes, att_index, padded_size, fixed_size;

  obj = NULL;
  oldrep = classobj_find_representation (class_, repid);

  if (oldrep == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_INVALID_REPRESENTATION, 1, sm_ch_name ((MOBJ) class_));
    }
  else
    {
      /*
       * if there are no bound bits on disk, allocate the instance block
       * with all the bits turned on, we have to assume that the values
       * are non-null
       */
      obj = *obj_ptr = obj_alloc (class_, (bound_bit_flag) ? 0 : 1);

      if (obj == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, class_->object_size);
      return NULL;
    }
      else
    {
      /*
       * read the variable offset table, can't we just leave this on
       * disk ?
       */
      vars = NULL;
      if (oldrep->variable_count)
        {
          vars = (int *) db_ws_alloc (sizeof (int) * oldrep->variable_count);
          if (vars == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
              (size_t) (oldrep->variable_count * sizeof (int)));
          return NULL;
        }
          else
        {
          /* get the offsets relative to the end of the header (beginning of variable table) */
          offset = or_get_offset_internal (buf, &rc, offset_size);
          for (i = 0; i < oldrep->variable_count; i++)
            {
              offset2 = or_get_offset_internal (buf, &rc, offset_size);
              vars[i] = offset2 - offset;
              offset = offset2;
            }
          buf->ptr = PTR_ALIGN (buf->ptr, INT_ALIGNMENT);
        }
        }

      /* calculate an attribute map */
      total = oldrep->fixed_count + oldrep->variable_count;
      attmap = NULL;
      if (total > 0)
        {
          attmap = (SM_ATTRIBUTE **) db_ws_alloc (sizeof (SM_ATTRIBUTE *) * total);
          if (attmap == NULL)
        {
          if (vars != NULL)
            {
              db_ws_free (vars);
            }
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
              (size_t) (total * sizeof (SM_ATTRIBUTE *)));
          return NULL;
        }
          else
        {
          for (rat = oldrep->attributes, i = 0; rat != NULL; rat = rat->next, i++)
            {
              attmap[i] = find_current_attribute (class_, rat->attid);
            }
        }
        }

      /* read the fixed attributes */
      rat = oldrep->attributes;
      start = buf->ptr;
      for (i = 0; i < oldrep->fixed_count && rat != NULL && attmap != NULL; i++, rat = rat->next)
        {
          type = pr_type_from_id (rat->typeid_);
          if (type == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_INVALID_REPRESENTATION, 1,
              sm_ch_name ((MOBJ) class_));

          db_ws_free (attmap);
          if (vars != NULL)
            {
              db_ws_free (vars);
            }

          return NULL;
        }

          if (attmap[i] == NULL)
        {
          type->data_readmem (buf, NULL, rat->domain, -1);
        }
          else
        {
          type->data_readmem (buf, obj + attmap[i]->offset, rat->domain, -1);
        }
        }

      fixed_size = (int) (buf->ptr - start);
      padded_size = DB_ATT_ALIGN (fixed_size);
      or_advance (buf, (padded_size - fixed_size));


      /*
       * sigh, we now have to process the bound bits in much the same way
       * as the attributes above, it would be nice if these could be done
       * in parallel but we don't have the fixed size of the old
       * representation so we can't easily sneak the buffer pointer
       * forward, work on this someday
       */

      if (bound_bit_flag && oldrep->fixed_count)
        {
          bits = buf->ptr;
          bytes = OR_BOUND_BIT_BYTES (oldrep->fixed_count);
          assert (bytes <= (buf->endptr - buf->ptr));
          rat = oldrep->attributes;
          for (i = 0; i < oldrep->fixed_count && rat != NULL && attmap != NULL; i++, rat = rat->next)
        {
          if (attmap[i] != NULL)
            {
              if (OR_GET_BOUND_BIT (bits, i))
            {
              OBJ_SET_BOUND_BIT (obj, attmap[i]->storage_order);
            }
            }
        }
          or_advance (buf, bytes);
        }

      /* variable */
      if (vars != NULL)
        {
          for (i = 0; i < oldrep->variable_count && rat != NULL && attmap != NULL; i++, rat = rat->next)
        {
          type = pr_type_from_id (rat->typeid_);
          if (type == NULL)
            {
              er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_INVALID_REPRESENTATION, 1,
                  sm_ch_name ((MOBJ) class_));

              db_ws_free (attmap);
              db_ws_free (vars);

              return NULL;
            }

          att_index = oldrep->fixed_count + i;
          if (attmap[att_index] == NULL)
            {
              type->data_readmem (buf, NULL, rat->domain, vars[i]);
            }
          else
            {
              type->data_readmem (buf, obj + attmap[att_index]->offset, rat->domain, vars[i]);
            }
        }
        }

      /* should be optimizing this operation ! */
      clear_new_unbound (obj, class_, oldrep);

      if (attmap != NULL)
        {
          db_ws_free (attmap);
        }
      if (vars != NULL)
        {
          db_ws_free (vars);
        }
    }
    }

  return (obj);
}


/*
 * tf_disk_to_mem - Interface function for transforming the disk
 * representation of an instance into the memory representation.
 *    return: MOBJ
 *    classobj(in): class structure
 *    record(in): disk record
 *    convertp(in): set to non-zero if the object had to be converted from
 *                  an obsolete disk representation.
 * Note:
 *    It is imperitive that garbage collection be disabled during this
 *    operation.
 *    This is because the structure being built may contain pointers
 *    to MOPs but it has not itself been attached to its own MOP yet so it
 *    doesn't serve as a gc root.   Make sure the caller
 *    calls ws_cache() immediately after we return so the new object
 *    gets attached as soon as possible.  We must also have already
 *    allocated the MOP in the caller so that we don't get one of the
 *    periodic gc's when we attempt to allocate a new MOP.
 *    This dependency should be isolated in a ws_ function called by
 *    the locator.
 */
MOBJ
tf_disk_to_mem (MOBJ classobj, RECDES * record, int *convertp)
{
  OR_BUF orep, *buf;
  SM_CLASS *class_ = NULL;
  char *obj = NULL;
  unsigned int repid_bits;
  int repid, convert, chn, bound_bit_flag;
  int rc = NO_ERROR;
  int offset_size;
  char mvcc_flags;

  buf = &orep;
  or_init (buf, record->data, record->length);
  class_ = (SM_CLASS *) classobj;


  obj = NULL;
  convert = 0;
  /* offset size */
  offset_size = OR_GET_OFFSET_SIZE (buf->ptr);

  /* in case of MVCC, repid_bits contains MVCC flags */
  repid_bits = or_mvcc_get_repid_and_flags (buf, &rc);
  repid = repid_bits & OR_MVCC_REPID_MASK;

  mvcc_flags = (char) ((repid_bits >> OR_MVCC_FLAG_SHIFT_BITS) & OR_MVCC_FLAG_MASK);

  chn = or_get_int (buf, &rc);

  if (mvcc_flags & OR_MVCC_FLAG_VALID_INSID)
    {
      /* skip insert id */
      or_advance (buf, OR_MVCCID_SIZE);
    }

  if (mvcc_flags & OR_MVCC_FLAG_VALID_DELID)
    {
      /* skip delete id */
      or_advance (buf, OR_MVCCID_SIZE);
    }

  if (mvcc_flags & OR_MVCC_FLAG_VALID_PREV_VERSION)
    {
      /* skip prev version lsa */
      or_advance (buf, OR_MVCC_PREV_VERSION_LSA_SIZE);
    }

  bound_bit_flag = repid_bits & OR_BOUND_BIT_FLAG;

  if (repid == class_->repid)
    {
      obj = get_current (buf, class_, &obj, bound_bit_flag, offset_size);
    }
  else
    {
      obj = get_old (buf, class_, &obj, repid, bound_bit_flag, offset_size);
      convert = 1;
    }

  /* set the chn of the object */
  if (obj != NULL)
    {
      WS_CHN (obj) = chn;
    }
  *convertp = convert;

  if (er_errid () == ER_OUT_OF_VIRTUAL_MEMORY)
    {
      return NULL;
    }
  return (obj);
}

/*
 * unpack_allocator - memory allocator
 *    return: memory allocated
 *    size(in): size to allocate
 * Note:
 *    Used in cases where an allocatino function needs to be passed
 *    to one of the transformation routines.
 */
static char *
unpack_allocator (int size)
{
  return ((char *) malloc (size));
}


/*
 * read_var_table -  extracts the variable offset table from the disk
 * representation and build a more convenient memory representation.
 *    return: array of OR_VARINFO structures
 *    buf(in/out): translation buffr
 *    nvars(in): expected number of variables
 */
static OR_VARINFO *
read_var_table (OR_BUF * buf, int nvars)
{
  return (read_var_table_internal (buf, nvars, BIG_VAR_OFFSET_SIZE));
}

/*
 * read_var_table_internal -  extracts the variable offset table from the disk
 * representation and build a more convenient memory representation.
 *    return: array of OR_VARINFO structures
 *    buf(in/out): translation buffr
 *    nvars(in): expected number of variables
 */
static OR_VARINFO *
read_var_table_internal (OR_BUF * buf, int nvars, int offset_size)
{
  return (or_get_var_table_internal (buf, nvars, unpack_allocator, offset_size));
}


/*
 * free_var_table - frees a table allocated by read_var_table
 *    return: void
 *    vars(out): variable table
 */
static void
free_var_table (OR_VARINFO * vars)
{
  if (vars != NULL)
    {
      free_and_init (vars);
    }
}

/*
 * string_disk_size - calculate the disk size of a NULL terminated ASCII
 * string that is supposed to be stored as a VARCHAR attribute in one of
 * the various class objects.
 *    return: byte size for packed "varchar" string
 *    string(in):
 */
static int
string_disk_size (const char *string)
{
  DB_VALUE value;
  int str_length = 0;
  int length = 0;

  if (string)
    {
      str_length = strlen (string);
    }

  db_make_varchar (&value, TP_FLOATING_PRECISION_VALUE, string, str_length, LANG_SYS_CODESET, LANG_SYS_COLLATION);

  length = tp_String.get_disk_size_of_value (&value);

  /* Clear the compressed_string of DB_VALUE */
  pr_clear_compressed_string (&value);

  return length;
}


/*
 * get_string - read a string out of the OR_BUF and return it as a NULL
 * terminated ASCII string
 *    return: string from value or NULL
 *    buf(in/out): translation buffer
 *    length(in): length of string or -1 if read variable length
 * Note:
 *    Shorthand macro to read a string out of the OR_BUF and return it
 *    as a NULL terminated ASCII string which everything stored as part
 *    of the class definition is.
 *
 *    A jmp_buf has previously been established.
 */
static char *
get_string (OR_BUF * buf, int length)
{
  DB_VALUE value;
  DB_DOMAIN my_domain;
  char *string = NULL;

  /*
   * Make sure this starts off initialized so "readval" won't try to free
   * any existing contents.
   */
  db_make_null (&value);

  /*
   * The domain here is always a server side VARCHAR.  Set a temporary
   * domain to reflect this.
   */
  my_domain.precision = DB_MAX_VARCHAR_PRECISION;

  my_domain.codeset = lang_charset ();
  my_domain.collation_id = LANG_SYS_COLLATION;
  my_domain.collation_flag = TP_DOMAIN_COLL_NORMAL;

  tp_String.data_readval (buf, &value, &my_domain, length, false, NULL, 0);

  if (DB_VALUE_TYPE (&value) != DB_TYPE_NULL)
    {
      assert (DB_VALUE_TYPE (&value) == DB_TYPE_VARCHAR);
      string = ws_copy_string (db_get_string (&value));
    }

  db_value_clear (&value);

  return string;
}


/*
 * put_string - Put a NULL terminated ASCII string into the disk buffer in the
 * usual way.
 *    return: void
 *    buf(in/out): translation buffer
 *    string(in): string to store
 * Note:
 *    See get_string & string_disk_size for the related functions.
 */
static void
put_string (OR_BUF * buf, const char *string)
{
  DB_VALUE value;
  int str_length = 0;

  if (string)
    {
      str_length = strlen (string);
    }
  else
    {
      str_length = 0;
    }

  db_make_varchar (&value, TP_FLOATING_PRECISION_VALUE, string, str_length, LANG_SYS_CODESET, LANG_SYS_COLLATION);

  tp_String.data_writeval (buf, &value);
  pr_clear_value (&value);
}

/*
 * SET SUPPORT FUNCTIONS
 *
 * These make it easier to read and write object and substructure sets
 * which are used often in a class's disk representation.
 *
 * SUBSTRUCTURE SET RULES
 *
 * The set header containing type and count is always present.
 * The variable offset table is present only if there are elements.
 * The substructure tag is present only if there are elements.
 *
 */

/*
 * object_set_size - Calculates the disk storage required for a set of object
 * pointers.
 *    return: byte size for a set of OIDs
 *    list(in): object list
 */
static int
object_set_size (DB_OBJLIST * list)
{
  DB_OBJLIST *l;
  int count, size;

  /* store NULL for empty set */
  if (list == NULL)
    {
      return 0;
    }

  size = 0;
  for (count = 0, l = list; l != NULL; l = l->next)
    {
      if (WS_IS_DELETED (l->op))
    {
      continue;
    }
      count++;
    }

  if (count == 0)
    {
      return 0;
    }

  size += OR_SET_HEADER_SIZE;
  size += OR_INT_SIZE;      /* size of domain word */
  size += OR_INT_SIZE;      /* size of the built-in object domain */
  size += (count * tp_Object.disksize);

  return (size);
}

/*
 * or_pack_mop - write an OID to a disk representation buffer given a MOP
 * instead of a WS_MEMOID.
 *    return:
 *    buf(): transformer buffer
 *    mop(): mop to transform
 * Note:
 *    mr_write_object can't be used because it takes a WS_MEMOID as is the
 *    case for object references in instances.
 *    This must stay in sync with mr_write_object above !
 */
static void
or_pack_mop (OR_BUF * buf, MOP mop)
{
  DB_VALUE value;

  tp_Object.initval (&value, 0, 0);
  db_make_object (&value, mop);
  tp_Object.data_writeval (buf, &value);
  tp_Object.setval (&value, NULL, false);
}

/*
 * put_object_set - Translates a list objects into the disk representation of a
 * sequence of objects
 *    buf(in/out): translation buffer
 *    list(in): object list
 */
static int
put_object_set (OR_BUF * buf, DB_OBJLIST * list)
{
  DB_OBJLIST *l;
  int count;

  /* store NULL for empty set */
  if (list == NULL)
    return ER_FAILED;

  for (count = 0, l = list; l != NULL; l = l->next)
    {
      if (WS_IS_DELETED (l->op))
    {
      continue;
    }
      count++;
    }

  if (count == 0)
    {
      return NO_ERROR;
    }

  /* w/ domain, no bound bits, no offsets, no tags, no substructure header */
  or_put_set_header (buf, DB_TYPE_SEQUENCE, count, 1, 0, 0, 0, 0);

  /* use the generic "object" domain */
  or_put_int (buf, OR_INT_SIZE);    /* size of the domain */
  or_put_domain (buf, &tp_Object_domain, 0, 0); /* actual domain */

  /* should be using something other than or_pack_mop here ! */
  for (l = list; l != NULL; l = l->next)
    {
      if (WS_IS_DELETED (l->op))
    {
      continue;
    }
      or_pack_mop (buf, l->op);
    }

  return NO_ERROR;
}

/*
 * get_object_set - Extracts a sequence of objects in a disk representation
 * and converts it into an object list in memory.
 *    return: object list  NOTE: a jmp_buf has previously been established.
 *    buf(in/out): translation buffer
 *    expected(in): expected length
 */
static DB_OBJLIST *
get_object_set (OR_BUF * buf, int expected)
{
  DB_OBJLIST *list;
  MOP op;
  DB_VALUE value;
  int count, i;

  /* handle NULL case, variable width will be zero */
  if (expected == 0)
    {
      return NULL;
    }

  list = NULL;
  count = or_skip_set_header (buf);
  for (i = 0; i < count; i++)
    {
      /*
       * Get a MOP, could assume classes mops here and make sure the resulting
       * MOP is stamped with the sm_Root_class_mop class ?
       */
      tp_Object.data_readval (buf, &value, NULL, -1, true, NULL, 0);
      op = db_get_object (&value);
      if (op != NULL)
    {
      if (ml_append (&list, op, NULL))
        {
          /* memory error */
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (DB_OBJLIST));
          return NULL;
        }
    }
    }

  return (list);
}

/*
 * substructure_set_size - Calculates the disk storage for a set created from
 * a list of memory elements.
 *    return: byte of disk storage required
 *    list(in): list of elements
 *    function(in): function to calculate element sizes
 * Note:
 *    The supplied function is used to find the size of the list elements.
 *    Even if the list is empty, there will always be at least a set header
 *    written to disk.
 */
static int
substructure_set_size (DB_LIST * list, LSIZER function)
{
  DB_LIST *l;
  int size, count;

  /* store NULL for empty list */
  if (list == NULL)
    {
      return 0;
    }

  /* header + domain length word + domain */
  size = OR_SET_HEADER_SIZE + OR_INT_SIZE + OR_SUB_DOMAIN_SIZE;

  count = 0;
  for (l = list; l != NULL; l = l->next, count++)
    {
      size += (*function) (l);
    }

  if (count)
    {
      /*
       * we have elements to store, in that case we need to add the
       * common substructure header at the front and an offset table
       */
      size += OR_VAR_TABLE_SIZE (count);
      size += OR_SUB_HEADER_SIZE;
    }

  return (size);
}

/*
 * put_substructure_set - Write the disk representation of a substructure
 * set from a linked list of structures.
 *    return:  void
 *    buf(in/out): translation buffer
 *    list(in): substructure list
 *    writer(in): translation function
 *    class(in): OID of meta class for this substructure
 *    repid(in): repid for the meta class
 * Note:
 *    The supplied functions calculate the size and write the elements.
 *    The OID/repid for the metaclass will be one of the meta classes
 *    defined in the catalog.
 */
static void
put_substructure_set (OR_BUF * buf, DB_LIST * list, LWRITER writer, OID * class_, int repid)
{
  DB_LIST *l;
  char *start;
  int count;
  char *offset_ptr;

  /* store NULL for empty list */
  if (list == NULL)
    {
      return;
    }

  count = 0;
  for (l = list; l != NULL; l = l->next, count++);

  /* with domain, no bound bits, with offsets, no tags, common sub header */
  start = buf->ptr;
  or_put_set_header (buf, DB_TYPE_SEQUENCE, count, 1, 0, 1, 0, 1);

  if (!count)
    {
      /* we must at least store the domain even though there will be no elements */
      or_put_int (buf, OR_SUB_DOMAIN_SIZE);
      or_put_sub_domain (buf);
    }
  else
    {
      /* begin an offset table */
      offset_ptr = buf->ptr;
      or_advance (buf, OR_VAR_TABLE_SIZE (count));

      /* write the domain */
      or_put_int (buf, OR_SUB_DOMAIN_SIZE);
      or_put_sub_domain (buf);

      /* write the common substructure header */
      or_put_oid (buf, class_);
      or_put_int (buf, repid);
      or_put_int (buf, 0);  /* flags */

      /* write each substructure */
      for (l = list; l != NULL; l = l->next)
    {
      /* determine the offset to the this element and put it in the table */
      OR_PUT_OFFSET (offset_ptr, (buf->ptr - start));
      offset_ptr += BIG_VAR_OFFSET_SIZE;
      /* write the substructure */
      (*writer) (buf, l);
    }
      /* write the final offset */
      OR_PUT_OFFSET (offset_ptr, (buf->ptr - start));
    }
}

/*
 * get_substructure_set - extracts a substructure set on disk and create a
 * list of memory structures.
 *    return: list of structures
 *    buf(in/out): translation buffer
 *    reader(in): function to read the elements
 *    expected(in): expected size
 * Note:
 *    It is important that the elements be APPENDED here.
 */
static DB_LIST *
get_substructure_set (OR_BUF * buf, LREADER reader, int expected)
{
  DB_LIST *list, *obj;
  int count, i;

  /* handle NULL case, variable width will be zero */
  if (expected == 0)
    {
      return NULL;
    }

  list = NULL;
  count = or_skip_set_header (buf);
  for (i = 0; i < count; i++)
    {
      obj = (*reader) (buf);
      if (obj != NULL)
    {
      WS_LIST_APPEND (&list, obj);
    }
      else
    {
      if (er_errid () != NO_ERROR)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
        }
      return NULL;
    }
    }
  return (list);
}

/*
 * install_substructure_set - loads a substructure set from disk into a list
 * of memory structures.
 *    return: void
 *    buf(in/out): translation buffer
 *    list(in): threaded array of structures
 *    reader(in): function to read elements
 *    expected(in): expected size;
 * Note:
 *    The difference is this function does not allocate storage for the
 *    elements, these have been previously allocated and are simply filled in
 *    by the reader.
 */
static void
install_substructure_set (OR_BUF * buf, DB_LIST * list, VREADER reader, int expected)
{
  DB_LIST *p;
  int count, i;

  if (expected)
    {
      count = or_skip_set_header (buf);
      for (i = 0, p = list; i < count && p != NULL; i++, p = p->next)
    {
      (*reader) (buf, p);
    }
    }
}

/*
 * property_list_size - Calculate the disk storage requirements for a
 * property list.
 *    return: byte size of property list
 *    properties(in): property list
 */
static int
property_list_size (DB_SEQ * properties)
{
  DB_VALUE value;
  int size, max;

  size = 0;
  if (properties != NULL)
    {
      /* collapse empty property sequences to NULL values on disk */
      max = set_size (properties);
      if (max)
    {
      db_make_sequence (&value, properties);
      size = tp_Sequence.get_disk_size_of_value (&value);
    }
    }
  return size;
}

/*
 * put_property_list - Write the disk representation of a property list.
 *    return: void
 *    buf(in/out): translation buffer
 *    properties(in): property list
 */
static void
put_property_list (OR_BUF * buf, DB_SEQ * properties)
{
  DB_VALUE value;
  int max;

  if (properties == NULL)
    {
      return;
    }
  /* collapse empty property sequences to NULL values on disk */
  max = set_size (properties);
  if (max)
    {
      db_make_sequence (&value, properties);
      tp_Sequence.data_writeval (buf, &value);
    }
}

/*
 * get_property_list - This reads a property list from disk.
 *    return: a new property list
 *    buf(in/out): translation buffer
 *    expected_size(in): number of bytes on disk
 * Note:
 *    This reads a property list from disk.
 *    If either the expected size is zero, NULL is returned.
 *    If a sequence was stored on disk but it had no elements, NULL is
 *    returned and the empty sequence is freed.  This is to handle the
 *    case where old style class objects had an empty substructure set
 *    stored at this position.  Since these will be converted to property
 *    lists, we can just ignore them.
 *
 *    A jmp_buf has previously been established.
 */
static DB_SEQ *
get_property_list (OR_BUF * buf, int expected_size)
{
  DB_VALUE value;
  DB_SEQ *properties;
  int max;

  properties = NULL;
  if (expected_size)
    {
      tp_Sequence.data_readval (buf, &value, NULL, expected_size, true, NULL, 0);
      properties = db_get_set (&value);
      if (properties == NULL)
    {
      if (er_errid () != NO_ERROR)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
        }
      return NULL;
    }
      else
    {
      max = set_size (properties);
      if (!max)
        {
          /*
           * there is an empty sequence here, get rid of it so we don't
           * have to carry it around
           */
          set_free (properties);
          properties = NULL;
        }
    }
    }
  return (properties);
}

/*
 * domain_size - Calculates the number of bytes required to store a domain
 * list on disk.
 *    return: disk size of domain
 *    domain(in): domain list
 */
static int
domain_size (TP_DOMAIN * domain)
{
  int size;

  size = tf_Metaclass_domain.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_domain.mc_n_variable);

  size += enumeration_size (&DOM_GET_ENUMERATION (domain));

  size += substructure_set_size ((DB_LIST *) domain->setdomain, (LSIZER) domain_size);

  size += (domain->json_validator == NULL
       ? 0 : string_disk_size (db_json_get_schema_raw_from_validator (domain->json_validator)));

  return (size);
}


/*
 * domain_to_disk - Translates a domain list to its disk representation.
 *    return: void
 *    buf(in/out): translation buffer
 *    domain(in): domain list
 * Note:
 * Translates a domain list to its disk representation.
 */
static void
domain_to_disk (OR_BUF * buf, TP_DOMAIN * domain)
{
  char *start;
  int offset;
  DB_VALUE schema_value;

  /* safe-guard : domain collation flags should only be used for execution */
  if (TP_DOMAIN_COLLATION_FLAG (domain) != TP_DOMAIN_COLL_NORMAL)
    {
      assert (false);
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_OUT_OF_SYNC, 0);
    }

  /* VARIABLE OFFSET TABLE */
  start = buf->ptr;
  offset = tf_Metaclass_domain.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_domain.mc_n_variable);

  or_put_offset (buf, offset);
  offset += substructure_set_size ((DB_LIST *) domain->setdomain, (LSIZER) domain_size);

  or_put_offset (buf, offset);
  offset += enumeration_size (&DOM_GET_ENUMERATION (domain));

  or_put_offset (buf, offset);
  offset += (domain->json_validator == NULL
         ? 0 : string_disk_size (db_json_get_schema_raw_from_validator (domain->json_validator)));

  or_put_offset (buf, offset);
  buf->ptr = PTR_ALIGN (buf->ptr, INT_ALIGNMENT);

  /* ATTRIBUTES */
  or_put_int (buf, (int) TP_DOMAIN_TYPE (domain));
  or_put_int (buf, domain->precision);
  or_put_int (buf, domain->scale);
  or_put_int (buf, domain->codeset);
  or_put_int (buf, domain->collation_id);
  or_pack_mop (buf, domain->class_mop);

  put_substructure_set (buf, (DB_LIST *) domain->setdomain, (LWRITER) domain_to_disk, &tf_Metaclass_domain.mc_classoid,
            tf_Metaclass_domain.mc_repid);

  put_enumeration (buf, &DOM_GET_ENUMERATION (domain));

  if (domain->json_validator)
    {
      db_make_string (&schema_value, db_json_get_schema_raw_from_validator (domain->json_validator));
      tp_String.data_writeval (buf, &schema_value);
      pr_clear_value (&schema_value);
    }
  if (start + offset != buf->ptr)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_OUT_OF_SYNC, 0);
    }
}

/*
 * disk_to_domain2 - Create the memory representation for a domain list from
 * the disk representation.
 *    return: domain structure
 *    buf(in/out): translation buffer
 * Note:
 *    This builds a transient domain, it is called by disk_to_domain which
 *    just turns around and caches it.
 *
 *    A jmp_buf has previously been established.
 */
static TP_DOMAIN *
disk_to_domain2 (OR_BUF * buf)
{
  OR_VARINFO *vars;
  TP_DOMAIN *domain;
  DB_TYPE typeid_;
  OID oid;
  int rc = NO_ERROR;

  vars = read_var_table (buf, tf_Metaclass_domain.mc_n_variable);
  if (vars == NULL)
    {
      if (er_errid () != NO_ERROR)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
    }
      return NULL;
    }

  typeid_ = (DB_TYPE) or_get_int (buf, &rc);

  domain = tp_domain_new (typeid_);
  if (domain == NULL)
    {
      free_var_table (vars);
      if (er_errid () != NO_ERROR)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
    }
      return NULL;
    }

  domain->precision = or_get_int (buf, &rc);
  domain->scale = or_get_int (buf, &rc);
  domain->codeset = or_get_int (buf, &rc);
  domain->collation_id = or_get_int (buf, &rc);
  if (typeid_ == DB_TYPE_ENUMERATION && domain->codeset == 0)
    {
      assert (domain->collation_id == LANG_COLL_ISO_BINARY);
      domain->codeset = INTL_CODESET_ISO88591;
    }
  /*
   * Read the domain class OID without promoting it to a MOP.
   * Could use readval, and extract the OID out of the already swizzled
   * MOP too.
   */
  tp_Oid.data_readmem (buf, &oid, NULL, -1);
  domain->class_oid = oid;

  /* swizzle the pointer, we know we're on the client here */
  if (!OID_ISNULL (&domain->class_oid))
    {
      domain->class_mop = ws_mop (&domain->class_oid, NULL);
      if (domain->class_mop == NULL)
    {
      free_var_table (vars);
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_NO_CONNECT, 0);
      /* error set in ws_mop */
      return NULL;
    }
    }
  domain->setdomain = (TP_DOMAIN *) get_substructure_set (buf, (LREADER) disk_to_domain2,
                              vars[ORC_DOMAIN_SETDOMAIN_INDEX].length);
  domain->enumeration.collation_id = domain->collation_id;

  if (get_enumeration (buf, &DOM_GET_ENUMERATION (domain), vars[ORC_DOMAIN_ENUMERATION_INDEX].length) != NO_ERROR)
    {
      free_var_table (vars);
      tp_domain_free (domain);
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_NO_CONNECT, 0);
      /* error set in get_enumeration */
      return NULL;
    }

  if (vars[ORC_DOMAIN_SCHEMA_JSON_OFFSET].length > 0)
    {
      char *schema_raw;
      int error_code;

      or_get_json_schema (buf, schema_raw);
      if (schema_raw == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
          vars[ORC_DOMAIN_SCHEMA_JSON_OFFSET].length + 1);
      tp_domain_free (domain);
      free_var_table (vars);
      return NULL;
    }

      error_code = db_json_load_validator (schema_raw, domain->json_validator);
      if (error_code != NO_ERROR)
    {
      assert_release (false);
      tp_domain_free (domain);
      free_var_table (vars);
      db_private_free_and_init (NULL, schema_raw);
      return NULL;
    }
      db_private_free_and_init (NULL, schema_raw);
    }

  free_var_table (vars);

  return (domain);
}

/*
 * disk_to_domain - Create the memory representation for a domain list from
 * the disk representation.
 *    return: domain structure
 *    buf(in/out): translation buffer
 * Note:
 *    Calls disk_to_domain2 which builds the transient domain which we
 *    then cache when we're done.
 *    We need to separate the two operations because disk_to_domain2 is
 *    called recursively for nested domains and we don't want to
 *    cache each of those.
 */
static TP_DOMAIN *
disk_to_domain (OR_BUF * buf)
{
  TP_DOMAIN *domain;

  domain = disk_to_domain2 (buf);

  if (domain != NULL)
    {
      domain = tp_domain_cache (domain);
    }
  return domain;
}


/*
 * metharg_to_disk - Write the memory representation of a method argument to
 * disk.
 *    return: void
 *    buf(in/out): translation buffer
 *    arg(in): method argument
 * Note:
 *    Write the memory representation of a method argument to disk.
 */
static void
metharg_to_disk (OR_BUF * buf, SM_METHOD_ARGUMENT * arg)
{
  char *start;
  int offset;

  /* VARIABLE OFFSET TABLE */
  start = buf->ptr;
  offset = tf_Metaclass_metharg.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_metharg.mc_n_variable);
  or_put_offset (buf, offset);
  offset += substructure_set_size ((DB_LIST *) arg->domain, (LSIZER) domain_size);
  or_put_offset (buf, offset);
  buf->ptr = PTR_ALIGN (buf->ptr, INT_ALIGNMENT);

  /* ATTRIBUTES */
  /* note that arguments can be empty, in which case they are untyped */
  if (arg->type == NULL)
    {
      or_put_int (buf, (int) DB_TYPE_NULL);
    }
  else
    {
      or_put_int (buf, (int) arg->type->id);
    }

  or_put_int (buf, arg->index);

  put_substructure_set (buf, (DB_LIST *) arg->domain, (LWRITER) domain_to_disk, &tf_Metaclass_domain.mc_classoid,
            tf_Metaclass_domain.mc_repid);

  if (start + offset != buf->ptr)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_OUT_OF_SYNC, 0);
    }
}

/*
 * metharg_size - Calculate the byte size for the disk representation of a
 * method arg.
 *    return: disk size of method argument
 *    arg(in): argument
 */
static int
metharg_size (SM_METHOD_ARGUMENT * arg)
{
  int size;

  size = tf_Metaclass_metharg.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_metharg.mc_n_variable);
  size += substructure_set_size ((DB_LIST *) arg->domain, (LSIZER) domain_size);

  return (size);
}

/*
 * disk_to_metharg - Read the disk represenation of a method argument and
 * build the memory representation.
 *    return: new method argument
 *    buf(in/out): translation buffer
 */
static SM_METHOD_ARGUMENT *
disk_to_metharg (OR_BUF * buf)
{
  OR_VARINFO *vars;
  SM_METHOD_ARGUMENT *arg;
  DB_TYPE argtype;
  int rc = NO_ERROR;

  vars = read_var_table (buf, tf_Metaclass_metharg.mc_n_variable);
  if (vars == NULL)
    {
      if (er_errid () != NO_ERROR)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
    }
      return NULL;
    }

  arg = classobj_make_method_arg (0);
  if (arg == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (SM_METHOD_ARGUMENT));
      return NULL;
    }
  else
    {
      argtype = (DB_TYPE) or_get_int (buf, &rc);
      if (argtype == DB_TYPE_NULL)
    {
      arg->type = NULL;
    }
      else
    {
      arg->type = pr_type_from_id (argtype);
    }
      arg->index = or_get_int (buf, &rc);
      arg->domain =
    (TP_DOMAIN *) get_substructure_set (buf, (LREADER) disk_to_domain, vars[ORC_METHARG_DOMAIN_INDEX].length);
    }
  free_var_table (vars);

  return (arg);
}

/*
 * methsig_to_disk - Write the disk representation of a method signature.
 *    return: void
 *    buf(in/out): translation buffer
 *    sig(in): signature
 * Note: overflow shouldn't be handled here
 */
static int
methsig_to_disk (OR_BUF * buf, SM_METHOD_SIGNATURE * sig)
{
  char *start;
  int offset;

  start = buf->ptr;
  /* VARIABLE OFFSET TABLE */
  offset = tf_Metaclass_methsig.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_methsig.mc_n_variable);

  or_put_offset (buf, offset);
  offset += string_disk_size (sig->function_name);

  or_put_offset (buf, offset);
  offset += string_disk_size (sig->sql_definition);

  or_put_offset (buf, offset);
  offset += substructure_set_size ((DB_LIST *) sig->value, (LSIZER) metharg_size);

  or_put_offset (buf, offset);
  offset += substructure_set_size ((DB_LIST *) sig->args, (LSIZER) metharg_size);

  or_put_offset (buf, offset);
  buf->ptr = PTR_ALIGN (buf->ptr, INT_ALIGNMENT);


  /* ATTRIBUTES */
  or_put_int (buf, sig->num_args);
  put_string (buf, sig->function_name);
  put_string (buf, sig->sql_definition);

  put_substructure_set (buf, (DB_LIST *) sig->value, (LWRITER) metharg_to_disk, &tf_Metaclass_metharg.mc_classoid,
            tf_Metaclass_metharg.mc_repid);

  put_substructure_set (buf, (DB_LIST *) sig->args, (LWRITER) metharg_to_disk, &tf_Metaclass_metharg.mc_classoid,
            tf_Metaclass_metharg.mc_repid);

  if (start + offset != buf->ptr)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_OUT_OF_SYNC, 0);
    }

  return NO_ERROR;
}

static inline void
methsig_to_disk_lwriter (void *buf, void *sig)
{
  (void) methsig_to_disk (STATIC_CAST (OR_BUF *, buf), STATIC_CAST (SM_METHOD_SIGNATURE *, sig));
}

/*
 * methsig_size - Calculate the disk size of a method signature.
 *    return: disk size of signature
 *    sig(in): signature
 */
static int
methsig_size (SM_METHOD_SIGNATURE * sig)
{
  int size;

  size = tf_Metaclass_methsig.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_methsig.mc_n_variable);
  size += string_disk_size (sig->function_name);
  size += string_disk_size (sig->sql_definition);
  size += substructure_set_size ((DB_LIST *) sig->value, (LSIZER) metharg_size);
  size += substructure_set_size ((DB_LIST *) sig->args, (LSIZER) metharg_size);

  return (size);
}

/*
 * disk_to_methsig - Read the disk represenation of a method signature and
 * create the memory represenatation.
 *    return: new method signature
 *    buf(in/out): translation buffer
 */
static SM_METHOD_SIGNATURE *
disk_to_methsig (OR_BUF * buf)
{
  OR_VARINFO *vars;
  SM_METHOD_SIGNATURE *sig;
  int nargs, rc = NO_ERROR;
  const char *fname, *fix;

  sig = NULL;
  vars = read_var_table (buf, tf_Metaclass_methsig.mc_n_variable);
  if (vars == NULL)
    {
      if (er_errid () != NO_ERROR)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
    }
      return NULL;
    }
  else
    {
      nargs = or_get_int (buf, &rc);
      sig = classobj_make_method_signature (NULL);
      if (sig == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (SM_METHOD_SIGNATURE));
      return NULL;
    }
      else
    {
      fname = get_string (buf, vars[ORC_METHSIG_FUNCTION_NAME_INDEX].length);
      sig->sql_definition = get_string (buf, vars[ORC_METHSIG_SQL_DEF_INDEX].length);

      /*
       * KLUDGE: older databases have the function name string stored with
       * a prepended '_' character for the sun.  Now, since we don't do this
       * until we actually have to dynamic link the function, we have to
       * strip it off if it was stored in the old form
       */
      if (fname != NULL && fname[0] == '_')
        {
          fix = ws_copy_string (fname + 1);
          if (fix == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, strlen (fname) + 1);
          return NULL;
        }
          else
        {
          ws_free_string (fname);
          fname = fix;
        }
        }
      sig->function_name = fname;

      sig->num_args = nargs;
      sig->value =
        (SM_METHOD_ARGUMENT *) get_substructure_set (buf, (LREADER) disk_to_metharg,
                             vars[ORC_METHSIG_RETURN_VALUE_INDEX].length);
      sig->args =
        (SM_METHOD_ARGUMENT *) get_substructure_set (buf, (LREADER) disk_to_metharg,
                             vars[ORC_METHSIG_ARGUMENTS_INDEX].length);
    }
      free_var_table (vars);
    }

  return (sig);
}

/*
 * method_to_disk - Write the disk representation of a method.
 *    return:
 *    buf(in/out): translation buffer
 *    method(in): method structure
 *    Note: overflow shouldn't be handled here
 */
static int
method_to_disk (OR_BUF * buf, SM_METHOD * method)
{
  char *start;
  int offset;

  start = buf->ptr;
  /* VARIABLE OFFSET TABLE */
  /* name */
  offset = tf_Metaclass_method.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_method.mc_n_variable);

  or_put_offset (buf, offset);
  offset += string_disk_size (method->header.name);

  /* signature set */
  or_put_offset (buf, offset);
  offset += substructure_set_size ((DB_LIST *) method->signatures, (LSIZER) methsig_size);

  /* property list */

  or_put_offset (buf, offset);
  offset += property_list_size (method->properties);

  /* end of variables */
  or_put_offset (buf, offset);
  buf->ptr = PTR_ALIGN (buf->ptr, INT_ALIGNMENT);

  /* ATTRIBUTES */
  /* source class oid */
  or_pack_mop (buf, method->class_mop);
  or_put_int (buf, method->id);

  /* name */
  put_string (buf, method->header.name);

  /* signatures */
  put_substructure_set (buf, (DB_LIST *) method->signatures, methsig_to_disk_lwriter,
            &tf_Metaclass_methsig.mc_classoid, tf_Metaclass_methsig.mc_repid);

  put_property_list (buf, method->properties);

  if (start + offset != buf->ptr)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_OUT_OF_SYNC, 0);
    }

  return NO_ERROR;
}

static inline void
method_to_disk_lwriter (void *buf, void *method)
{
  (void) method_to_disk (STATIC_CAST (OR_BUF *, buf), STATIC_CAST (SM_METHOD *, method));
}

/*
 * method_size - Calculates the disk size of a method.
 *    return: disk size of method
 *    method(in): method structure
 */
static int
method_size (SM_METHOD * method)
{
  int size;

  size = tf_Metaclass_method.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_method.mc_n_variable);
  size += string_disk_size (method->header.name);
  size += substructure_set_size ((DB_LIST *) method->signatures, (LSIZER) methsig_size);
  size += property_list_size (method->properties);

  return (size);
}

/*
 * disk_to_method - Reads the disk representation of a method and fills in
 * the supplied method structure.
 *    return: void
 *    buf(in/out): translation buffer
 *    method(out): method structure
 * Note:
 * A jmp_buf has previously been established.
 */
static void
disk_to_method (OR_BUF * buf, SM_METHOD * method)
{
  OR_VARINFO *vars;
  DB_VALUE value;
  int rc = NO_ERROR;

  vars = read_var_table (buf, tf_Metaclass_method.mc_n_variable);
  if (vars == NULL)
    {
      if (er_errid () != NO_ERROR)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
    }
      return;
    }
  else
    {
      /* this must be set later */
      method->header.name_space = ID_NULL;

      /* CLASS */
      tp_Object.data_readval (buf, &value, NULL, -1, true, NULL, 0);
      method->class_mop = db_get_object (&value);
      method->id = or_get_int (buf, &rc);
      method->function = NULL;

      /* variable attrubute 0 : name */
      method->header.name = get_string (buf, vars[ORC_METHOD_NAME_INDEX].length);

      /* variable attribute 1 : signatures */
      method->signatures =
    (SM_METHOD_SIGNATURE *) get_substructure_set (buf, (LREADER) disk_to_methsig,
                              vars[ORC_METHOD_SIGNATURE_INDEX].length);

      /* variable attribute 2 : property list */
      method->properties = get_property_list (buf, vars[ORC_METHOD_PROPERTIES_INDEX].length);

      free_var_table (vars);
    }
}

/*
 * methfile_to_disk - Write the disk representation of a method file.
 *    return: NO_ERROR, or error code
 *    buf(in/out): translation buffer
 *    file(in): method file
 * Note: overflow shouldn't be handled here
 */
static int
methfile_to_disk (OR_BUF * buf, SM_METHOD_FILE * file)
{
  char *start;
  int offset;

  start = buf->ptr;
  /* VARIABLE OFFSET TABLE */
  offset = tf_Metaclass_methfile.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_methfile.mc_n_variable);

  /* name */
  or_put_offset (buf, offset);
  offset += string_disk_size (file->name);

  /* property list */
  or_put_offset (buf, offset);
  /* currently no properties */
  offset += property_list_size (NULL);

  /* end of object */
  or_put_offset (buf, offset);
  buf->ptr = PTR_ALIGN (buf->ptr, INT_ALIGNMENT);

  /* ATTRIBUTES */

  /* class */
  or_pack_mop (buf, file->class_mop);

  /* name */
  put_string (buf, file->name);

  /* property list */
  put_property_list (buf, NULL);

  if (start + offset != buf->ptr)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_OUT_OF_SYNC, 0);
    }

  return NO_ERROR;
}

static inline void
methfile_to_disk_lwriter (void *buf, void *file)
{
  (void) methfile_to_disk (STATIC_CAST (OR_BUF *, buf), STATIC_CAST (SM_METHOD_FILE *, file));
}

/*
 * methfile_size - Calculate the disk size of a method file.
 *    return: disk size of the method file
 *    file(in): method file
 */
static int
methfile_size (SM_METHOD_FILE * file)
{
  int size;

  size = tf_Metaclass_methfile.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_methfile.mc_n_variable);
  size += string_disk_size (file->name);
  size += property_list_size (NULL);

  return (size);
}

/*
 * disk_to_methfile - Read the disk representation of a method file and
 * create a new method file structure.
 *    return: new method file structure
 *    buf(in/out): translation buffer
 * Note:
 *    A jmp_buf has previously been established.
 */
static SM_METHOD_FILE *
disk_to_methfile (OR_BUF * buf)
{
  SM_METHOD_FILE *file;
  OR_VARINFO *vars;
  DB_SET *props;
  DB_VALUE value;

  file = NULL;
  vars = read_var_table (buf, tf_Metaclass_methfile.mc_n_variable);
  if (vars == NULL)
    {
      if (er_errid () != NO_ERROR)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
    }
      return NULL;
    }
  else
    {
      file = classobj_make_method_file (NULL);
      if (file == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (SM_METHOD_FILE));
      return NULL;
    }
      else
    {
      /* class */
      tp_Object.data_readval (buf, &value, NULL, -1, true, NULL, 0);
      file->class_mop = db_get_object (&value);

      /* name */
      file->name = get_string (buf, vars[ORC_METHFILE_NAME_INDEX].length);

      /* properties */
      props = get_property_list (buf, vars[ORC_METHFILE_PROPERTIES_INDEX].length);
      /* shouldn't have any of these yet */
      if (props != NULL)
        {
          set_free (props);
        }
    }

      free_var_table (vars);
    }
  return (file);
}

/*
 * query_spec_to_disk - Write the disk representation of a virtual class
 * query_spec statement.
 *    return: NO_ERROR or error code
 *    buf(in/out): translation buffer
 *    statement(in): query_spec statement
 */
static int
query_spec_to_disk (OR_BUF * buf, SM_QUERY_SPEC * query_spec)
{
  char *start;
  int offset;

  start = buf->ptr;
  /* VARIABLE OFFSET TABLE */
  offset = tf_Metaclass_query_spec.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_query_spec.mc_n_variable);

  or_put_offset (buf, offset);
  offset += string_disk_size (query_spec->specification);

  or_put_offset (buf, offset);
  buf->ptr = PTR_ALIGN (buf->ptr, INT_ALIGNMENT);

  /* ATTRIBUTES */
  put_string (buf, query_spec->specification);

  if (start + offset != buf->ptr)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_OUT_OF_SYNC, 0);
    }

  return NO_ERROR;
}

static inline void
query_spec_to_disk_lwriter (void *buf, void *query_spec)
{
  (void) query_spec_to_disk (STATIC_CAST (OR_BUF *, buf), STATIC_CAST (SM_QUERY_SPEC *, query_spec));
}

/*
 * query_spec_size - Calculates the disk size of a query_spec statement.
 *    return: disk size of query_spec statement
 *    statement(in): query_spec statement
 */
static int
query_spec_size (SM_QUERY_SPEC * statement)
{
  int size;

  size = tf_Metaclass_query_spec.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_query_spec.mc_n_variable);
  size += string_disk_size (statement->specification);

  return (size);
}


/*
 * disk_to_query_spec - Reads the disk representation of a query_spec
 * statement and creates the memory representation.
 *    return: new query_spec structure
 *    buf(in/out): translation buffer
 */
static SM_QUERY_SPEC *
disk_to_query_spec (OR_BUF * buf)
{
  SM_QUERY_SPEC *statement;
  OR_VARINFO *vars;

  statement = NULL;
  vars = read_var_table (buf, tf_Metaclass_query_spec.mc_n_variable);
  if (vars == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_NO_CONNECT, 0);
      return NULL;
    }
  else
    {
      statement = classobj_make_query_spec (NULL);
      if (statement == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (SM_QUERY_SPEC));
      return NULL;
    }
      else
    {
      statement->specification = get_string (buf, vars[ORC_QUERY_SPEC_SPEC_INDEX].length);
    }

      free_var_table (vars);
    }
  return (statement);
}

/*
 * attribute_to_disk - Write the disk representation of an attribute.
 *    buf(in/out): translation buffer
 *    att(in): attribute
 *    Note: overflow shouldn't be handled here
 */
static int
attribute_to_disk (OR_BUF * buf, SM_ATTRIBUTE * att)
{
  char *start;
  int offset;
  DB_OBJLIST *triggers;

  start = buf->ptr;
  /* VARIABLE OFFSET TABLE */
  /* name */
  offset = tf_Metaclass_attribute.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_attribute.mc_n_variable);
  or_put_offset (buf, offset);

  offset += string_disk_size (att->header.name);

  /* initial value variable */
  or_put_offset (buf, offset);
  /* could avoid domain tag here ? */
  offset += or_packed_value_size (&att->default_value.value, 1, 1, 0);

  /* original value */
  or_put_offset (buf, offset);
  /* could avoid domain tag here ? */
  offset += or_packed_value_size (&att->default_value.original_value, 1, 1, 0);

  /* domain list */
  or_put_offset (buf, offset);
  offset += substructure_set_size ((DB_LIST *) att->domain, (LSIZER) domain_size);

  /* trigger list */
  or_put_offset (buf, offset);
  (void) tr_get_cache_objects (att->triggers, &triggers);
  offset += object_set_size (triggers);

  /* property list */
  or_put_offset (buf, offset);
  offset += property_list_size (att->properties);

  /* comment */
  or_put_offset (buf, offset);
  offset += string_disk_size (att->comment);

  /* end of object */
  or_put_offset (buf, offset);
  buf->ptr = PTR_ALIGN (buf->ptr, INT_ALIGNMENT);

  /* ATTRIBUTES */
  or_put_int (buf, att->id);
  or_put_int (buf, (int) att->type->id);
  or_put_int (buf, 0);      /* memory offsets are now calculated after loading */
  or_put_int (buf, att->order);
  or_pack_mop (buf, att->class_mop);
  or_put_int (buf, att->flags);

  /* index BTID */
  /*
   * The index member of the attribute structure has been removed.  Indexes
   * are now stored on the class property list.  We still need to store
   * something out to disk since the disk representation has not changed.
   * For now, store a NULL BTID on disk.  - JB
   */
  or_put_int (buf, NULL_FILEID);
  or_put_int (buf, NULL_PAGEID);
  or_put_int (buf, 0);

  /* name */
  put_string (buf, att->header.name);

  /* value/original value, make sure the flags match the calls to or_packed_value_size above ! */
  or_put_value (buf, &att->default_value.value, 1, 1, 0);
  or_put_value (buf, &att->default_value.original_value, 1, 1, 0);

  /* domain */
  put_substructure_set (buf, (DB_LIST *) att->domain, (LWRITER) domain_to_disk, &tf_Metaclass_domain.mc_classoid,
            tf_Metaclass_domain.mc_repid);

  /* triggers */
  put_object_set (buf, triggers);

  /* formerly att_extension_to_disk(buf, att) */
  put_property_list (buf, att->properties);

  /* comment */
  put_string (buf, att->comment);

  if (start + offset != buf->ptr)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_OUT_OF_SYNC, 0);
    }

  return NO_ERROR;
}

static inline void
attribute_to_disk_lwriter (void *buf, void *att)
{
  (void) attribute_to_disk (STATIC_CAST (OR_BUF *, buf), STATIC_CAST (SM_ATTRIBUTE *, att));
}

/*
 * attribute_size - Calculates the disk size of an attribute.
 *    return: disk size of attribute
 *    att(in): attribute
 */
static int
attribute_size (SM_ATTRIBUTE * att)
{
  DB_OBJLIST *triggers;
  int size;

  size = tf_Metaclass_attribute.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_attribute.mc_n_variable);

  size += string_disk_size (att->header.name);
  size += or_packed_value_size (&att->default_value.value, 1, 1, 0);
  size += or_packed_value_size (&att->default_value.original_value, 1, 1, 0);
  size += substructure_set_size ((DB_LIST *) att->domain, (LSIZER) domain_size);

  (void) tr_get_cache_objects (att->triggers, &triggers);
  size += object_set_size (triggers);

  /* size += att_extension_size(att); */
  size += property_list_size (att->properties);

  size += string_disk_size (att->comment);

  return (size);
}


/*
 * disk_to_attribute - Reads the disk representation of an attribute and
 * fills in the supplied attribute structure.
 *    return: void
 *    buf(in/out): translation buffer
 *    att(out): attribute structure
 * Note:
 *    A jmp_buf has previously been established.
 */
static void
disk_to_attribute (OR_BUF * buf, SM_ATTRIBUTE * att)
{
  OR_VARINFO *vars;
  int fileid;
  DB_OBJLIST *triggers;
  DB_VALUE value;
  int rc = NO_ERROR;
  DB_TYPE dbval_type;

  vars = read_var_table (buf, tf_Metaclass_attribute.mc_n_variable);
  if (vars == NULL)
    {
      if (er_errid () != NO_ERROR)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
    }
      return;
    }
  else
    {
      /*
       * must be sure to initialize these, the function classobj_make_attribute
       * should be split into creation & initialization functions so we can
       * have a single function that initializes the various fields.  As it
       * stands now, any change made to the attribute structure has to be
       * initialized in classobj_make_attribute and here as well
       */
      att->header.name_space = ID_NULL; /* must set this later ! */
      att->header.name = NULL;
      att->domain = NULL;
      att->constraints = NULL;
      att->properties = NULL;
      att->order_link = NULL;
      att->triggers = NULL;
      att->auto_increment = NULL;

      att->id = or_get_int (buf, &rc);
      dbval_type = (DB_TYPE) or_get_int (buf, &rc);
      att->type = pr_type_from_id (dbval_type);
      att->offset = or_get_int (buf, &rc);
      att->offset = 0;      /* calculated later */
      att->order = or_get_int (buf, &rc);

      tp_Object.data_readval (buf, &value, NULL, -1, true, NULL, 0);
      att->class_mop = db_get_object (&value);
      /* prevents clear on next readval call */
      db_value_put_null (&value);

      att->flags = or_get_int (buf, &rc);

      fileid = or_get_int (buf, &rc);

      /* index BTID */
      /*
       * Read the NULL BTID from disk.  There is no place to put this so
       * ignore it.  - JB
       */
      (void) or_get_int (buf, &rc);
      (void) or_get_int (buf, &rc);

      /* variable attribute 0 : name */
      att->header.name = get_string (buf, vars[ORC_ATT_NAME_INDEX].length);

      /* variable attribute 1 : value */
      or_get_value (buf, &att->default_value.value, NULL, vars[ORC_ATT_CURRENT_VALUE_INDEX].length, true);

      /* variable attribute 2 : original value */
      or_get_value (buf, &att->default_value.original_value, NULL, vars[ORC_ATT_ORIGINAL_VALUE_INDEX].length, true);

      /* variable attribute 3 : domain */
      att->domain =
    (TP_DOMAIN *) get_substructure_set (buf, (LREADER) disk_to_domain, vars[ORC_ATT_DOMAIN_INDEX].length);

      if (att->domain != NULL && att->domain->type->id == DB_TYPE_ENUMERATION)
    {
      /* Fill the default values with missing data */
      pr_complete_enum_value (&att->default_value.value, att->domain);
      pr_complete_enum_value (&att->default_value.original_value, att->domain);
    }

      /* variable attribute 4: trigger list */
      triggers = get_object_set (buf, vars[ORC_ATT_TRIGGER_INDEX].length);
      if (triggers != NULL)
    {
      att->triggers = tr_make_schema_cache (TR_CACHE_ATTRIBUTE, triggers);
    }
      /* variable attribute 5: property list */
      /* formerly disk_to_att_extension(buf, att, vars[5].length); */
      att->properties = get_property_list (buf, vars[ORC_ATT_PROPERTIES_INDEX].length);

      classobj_initialize_default_expr (&att->default_value.default_expr);
      att->on_update_default_expr = DB_DEFAULT_NONE;
      if (att->properties)
    {
      if (classobj_get_prop (att->properties, "update_default", &value) > 0)
        {
          att->on_update_default_expr = (DB_DEFAULT_EXPR_TYPE) db_get_int (&value);
        }

      if (classobj_get_prop (att->properties, "default_expr", &value) > 0)
        {
          /* We have two cases: simple and complex expressions. */
          if (DB_VALUE_TYPE (&value) == DB_TYPE_SEQUENCE)
        {
          DB_SEQ *def_expr_seq;
          DB_VALUE def_expr_op, def_expr_type, def_expr_format;
          const char *def_expr_format_str;

          assert (set_size (db_get_set (&value)) == 3);

          def_expr_seq = db_get_set (&value);

          /* get default expression operator (op of expr) */
          if (set_get_element_nocopy (def_expr_seq, 0, &def_expr_op) != NO_ERROR)
            {
              assert (false);
            }
          assert (DB_VALUE_TYPE (&def_expr_op) == DB_TYPE_INTEGER
              && db_get_int (&def_expr_op) == (int) T_TO_CHAR);
          att->default_value.default_expr.default_expr_op = db_get_int (&def_expr_op);

          /* get default expression type (arg1 of expr) */
          if (set_get_element_nocopy (def_expr_seq, 1, &def_expr_type) != NO_ERROR)
            {
              assert (false);
            }
          assert (DB_VALUE_TYPE (&def_expr_type) == DB_TYPE_INTEGER);
          att->default_value.default_expr.default_expr_type =
            (DB_DEFAULT_EXPR_TYPE) db_get_int (&def_expr_type);

          /* get default expression format (arg2 of expr) */
          if (set_get_element_nocopy (def_expr_seq, 2, &def_expr_format) != NO_ERROR)
            {
              assert (false);
            }

#if !defined (NDEBUG)
          {
            DB_TYPE db_value_type_local = db_value_type (&def_expr_format);
            assert (db_value_type_local == DB_TYPE_NULL || TP_IS_CHAR_TYPE (db_value_type_local));
          }
#endif
          if (!db_value_is_null (&def_expr_format))
            {
              def_expr_format_str = db_get_string (&def_expr_format);
              att->default_value.default_expr.default_expr_format = ws_copy_string (def_expr_format_str);
              if (att->default_value.default_expr.default_expr_format == NULL)
            {
              assert (er_errid () != NO_ERROR);
            }
            }
        }
          else
        {
          att->default_value.default_expr.default_expr_type = (DB_DEFAULT_EXPR_TYPE) db_get_int (&value);
        }

          pr_clear_value (&value);
        }
    }

      /* variable attribute 6: comment */
      att->comment = get_string (buf, vars[ORC_ATT_COMMENT_INDEX].length);

      /* THIS SHOULD BE INITIALIZING THE header.name_space field !! */

      free_var_table (vars);
    }
}


/*
 * resolution_to_disk - Writes the disk representation of a resolution.
 *    return:
 *    buf(in/out): translation buffer
 *    res(in): resolution
 */
static int
resolution_to_disk (OR_BUF * buf, SM_RESOLUTION * res)
{
  char *start;
  int offset, name_space;

  start = buf->ptr;
  /* VARIABLE OFFSET TABLE */
  /* name */
  offset = tf_Metaclass_resolution.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_resolution.mc_n_variable);
  or_put_offset (buf, offset);
  offset += string_disk_size (res->name);

  /* new name */
  or_put_offset (buf, offset);
  offset += string_disk_size (res->alias);

  /* end of object */
  or_put_offset (buf, offset);
  buf->ptr = PTR_ALIGN (buf->ptr, INT_ALIGNMENT);

  /* ATTRIBUTES */
  or_pack_mop (buf, res->class_mop);
  name_space = (int) res->name_space;   /* kludge for ansi */
  or_put_int (buf, name_space);
  put_string (buf, res->name);
  put_string (buf, res->alias);

  if (start + offset != buf->ptr)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_OUT_OF_SYNC, 0);
    }

  return NO_ERROR;
}

static inline void
resolution_to_disk_lwriter (void *buf, void *res)
{
  (void) resolution_to_disk (STATIC_CAST (OR_BUF *, buf), STATIC_CAST (SM_RESOLUTION *, res));
}

/*
 * resolution_size - Calculates the disk size of a resolution.
 *    return: disk size of resolution
 *    res(in): resolution
 */
static int
resolution_size (SM_RESOLUTION * res)
{
  int size;

  size = tf_Metaclass_resolution.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_resolution.mc_n_variable);
  size += string_disk_size (res->name);
  size += string_disk_size (res->alias);

  return (size);
}


/*
 * disk_to_resolution - Reads the disk representation of a resolution and
 * creates a new structure in memory.
 *    return: new resolution structure
 *    buf(in/out): translation buffer
 * Note:
 *    Handle the case where the resolution for a deleted class was
 *    accidentally saved.  When this happens, the stored OID will be the NULL
 *    oid and ws_mop will return NULL.
 *    Shouldn't really happen but its easier to check for it here
 *    than having the sizer and writer functions check.  Seeing this
 *    probably indicates a bug in the subclass cleanup code after
 *    class deletion.
 *    This paranoia may no longer be necessary but it was put here
 *    for a reason and we should be careful about ripping it out until
 *    all the tests run without it.
 *
 *    A jmp_buf has previously been established.
 */
static SM_RESOLUTION *
disk_to_resolution (OR_BUF * buf)
{
  SM_RESOLUTION *res;
  SM_NAME_SPACE name_space;
  OR_VARINFO *vars;
  MOP class_;
  DB_VALUE value;
  int rc;

  res = NULL;
  vars = read_var_table (buf, tf_Metaclass_resolution.mc_n_variable);
  if (vars == NULL)
    {
      if (er_errid () != NO_ERROR)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
    }
      return NULL;
    }
  else
    {
      tp_Object.data_readval (buf, &value, NULL, -1, true, NULL, 0);
      class_ = db_get_object (&value);
      if (class_ == NULL)
    {
      (void) or_get_int (buf, &rc);
      tp_String.data_readval (buf, NULL, NULL, vars[ORC_RES_NAME_INDEX].length, true, NULL, 0);
      tp_String.data_readval (buf, NULL, NULL, vars[ORC_RES_ALIAS_INDEX].length, true, NULL, 0);
    }
      else
    {
      name_space = (SM_NAME_SPACE) or_get_int (buf, &rc);
      res = classobj_make_resolution (NULL, NULL, NULL, name_space);
      if (res == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (SM_RESOLUTION));
          return NULL;
        }
      else
        {
          res->class_mop = class_;
          res->name = get_string (buf, vars[ORC_RES_NAME_INDEX].length);
          res->alias = get_string (buf, vars[ORC_RES_ALIAS_INDEX].length);
        }
    }

      free_var_table (vars);
    }
  return (res);
}

/*
 * repattribute_to_disk - Writes the disk representation of a representation
 * attribute.
 *    return: NO_ERROR or error code
 *    buf(in): translation buffer
 *    rat(in): attribute
 * Note: overflow shouldn't be handled here
 */
static int
repattribute_to_disk (OR_BUF * buf, SM_REPR_ATTRIBUTE * rat)
{
  char *start;
  int offset;

  start = buf->ptr;
  /* VARIABLE OFFSET TABLE */
  offset = tf_Metaclass_repattribute.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_repattribute.mc_n_variable);

  /* domain list */
  or_put_offset (buf, offset);
  offset += substructure_set_size ((DB_LIST *) rat->domain, (LSIZER) domain_size);

  /* end of object */
  or_put_offset (buf, offset);
  buf->ptr = PTR_ALIGN (buf->ptr, INT_ALIGNMENT);
  /* fixed width attributes */
  or_put_int (buf, rat->attid);
  or_put_int (buf, (int) rat->typeid_);

  /* domain */
  put_substructure_set (buf, (DB_LIST *) rat->domain, (LWRITER) domain_to_disk, &tf_Metaclass_domain.mc_classoid,
            tf_Metaclass_domain.mc_repid);

  if (start + offset != buf->ptr)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_OUT_OF_SYNC, 0);
    }

  return NO_ERROR;
}

static inline void
repattribute_to_disk_lwriter (void *buf, void *rat)
{
  (void) repattribute_to_disk (STATIC_CAST (OR_BUF *, buf), STATIC_CAST (SM_REPR_ATTRIBUTE *, rat));
}

/*
 * repattribute_size - Calculates the disk size for the REPATTRIBUTE.
 *    return: disk size of repattribute
 *    rat(in): memory attribute
 */
static int
repattribute_size (SM_REPR_ATTRIBUTE * rat)
{
  int size;

  size = tf_Metaclass_repattribute.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_repattribute.mc_n_variable);

  size += substructure_set_size ((DB_LIST *) rat->domain, (LSIZER) domain_size);

  return (size);
}

/*
 * disk_to_repattribute - Reads the disk representation of a representation
 * attribute.
 *    return: new repattribute
 *    buf(in/out): translation buffer
 */
static SM_REPR_ATTRIBUTE *
disk_to_repattribute (OR_BUF * buf)
{
  SM_REPR_ATTRIBUTE *rat;
  OR_VARINFO *vars;
  int id, tid;
  int rc = NO_ERROR;

  vars = read_var_table (buf, tf_Metaclass_repattribute.mc_n_variable);
  if (vars == NULL)
    {
      if (er_errid () != NO_ERROR)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
    }
    }

  id = or_get_int (buf, &rc);
  tid = or_get_int (buf, &rc);
  rat = classobj_make_repattribute (id, (DB_TYPE) tid, NULL);
  if (rat == NULL)
    {
      free_var_table (vars);
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (SM_REPR_ATTRIBUTE));
      return NULL;
    }

  rat->domain =
    (TP_DOMAIN *) get_substructure_set (buf, (LREADER) disk_to_domain, vars[ORC_REPATT_DOMAIN_INDEX].length);

  free_var_table (vars);

  return rat;
}


/*
 * representation_size - Calculate the disk size for a representation.
 *    return: byte size of the representation
 *    rep(in): representation
 */
static int
representation_size (SM_REPRESENTATION * rep)
{
  int size;

  size = tf_Metaclass_representation.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_representation.mc_n_variable);

  size += substructure_set_size ((DB_LIST *) rep->attributes, (LSIZER) repattribute_size);

  size += property_list_size (NULL);

  return (size);
}

/*
 * representation_to_disk - Write the disk representation of an
 * SM_REPRESENTATION.
 *    return: NO_ERROR
 *    buf(in/out): translation buffer
 *    rep(in): representation
 *    Note: overflow shouldn't be handled here
 */
static int
representation_to_disk (OR_BUF * buf, SM_REPRESENTATION * rep)
{
  char *start;
  int offset;

  start = buf->ptr;
  offset = tf_Metaclass_representation.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_representation.mc_n_variable);

  or_put_offset (buf, offset);
  offset += substructure_set_size ((DB_LIST *) rep->attributes, (LSIZER) repattribute_size);

  or_put_offset (buf, offset);
  offset += property_list_size (NULL);
  /* end of object */
  or_put_offset (buf, offset);
  buf->ptr = PTR_ALIGN (buf->ptr, INT_ALIGNMENT);

  or_put_int (buf, rep->id);
  or_put_int (buf, rep->fixed_count);
  or_put_int (buf, rep->variable_count);

  /* no longer have the fixed_size field, leave it for future expansion */
  or_put_int (buf, 0);

  put_substructure_set (buf, (DB_LIST *) rep->attributes, repattribute_to_disk_lwriter,
            &tf_Metaclass_repattribute.mc_classoid, tf_Metaclass_repattribute.mc_repid);

  put_property_list (buf, NULL);

  if (start + offset != buf->ptr)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_OUT_OF_SYNC, 0);
    }

  return NO_ERROR;
}

static inline void
representation_to_disk_lwriter (void *buf, void *rep)
{
  (void) representation_to_disk (STATIC_CAST (OR_BUF *, buf), STATIC_CAST (SM_REPRESENTATION *, rep));
}

/*
 * disk_to_representation - Read the disk representation for an
 * SM_REPRESENTATION structure and build the memory structure.
 *    return: new representation structure
 *    buf(in/out): translation buffer
 */
static SM_REPRESENTATION *
disk_to_representation (OR_BUF * buf)
{
  SM_REPRESENTATION *rep;
  OR_VARINFO *vars;
  DB_SEQ *props;
  int rc = NO_ERROR;

  vars = read_var_table (buf, tf_Metaclass_representation.mc_n_variable);
  if (vars == NULL)
    {
      if (er_errid () != NO_ERROR)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
    }
      return NULL;
    }

  rep = classobj_make_representation ();

  if (rep == NULL)
    {
      free_var_table (vars);
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (SM_REPRESENTATION));
      return NULL;
    }
  else
    {
      rep->id = or_get_int (buf, &rc);
      rep->fixed_count = or_get_int (buf, &rc);
      rep->variable_count = or_get_int (buf, &rc);

      /* we no longer use this field, formerly fixed_size */
      (void) or_get_int (buf, &rc);

      /* variable 0 : attributes */
      rep->attributes =
    (SM_REPR_ATTRIBUTE *) get_substructure_set (buf, (LREADER) disk_to_repattribute,
                            vars[ORC_REP_ATTRIBUTES_INDEX].length);

      /* variable 1 : properties */
      props = get_property_list (buf, vars[ORC_REP_PROPERTIES_INDEX].length);
      /* shouldn't be any, we have no place for them */
      if (props != NULL)
    {
      set_free (props);
    }
    }

  free_var_table (vars);
  return (rep);
}

/*
 * check_class_structure - maps over the class prior to storage to make sure
 * that everything looks ok.
 *    return: error code
 *    class(in): class strucutre
 * Note:
 *    The point is to get errors detected early on so that the lower level
 *    translation functions don't have to worry about them.
 *    It is MANDATORY that this be called before any size or other walk of
 *    the class
 */
static int
check_class_structure (SM_CLASS * class_)
{
  int ok = 1;

  /* Currently, there is nothing to decache.  I expect have to populate the class property list here someting in the
   * near future so I'll leave the function in place.  - JB */

/*  decache_attribute_properties(class); */
  return (ok);
}

/*
 * put_class_varinfo - Writes the variable offset table for a class object.
 *    return: ending offset
 *    buf(in/out): translation buffer
 *    class(in): class structure
 * Note:
 *    This is the only meta object that includes OR_MVCC_HEADER_SIZE
 *    as part of the offset calculations.  This is because the other
 *    substructures are all stored in sets inside the class object.
 *    Returns the offset within the buffer after the offset table
 *    (offset to first fixed attribute).
 */
static int
put_class_varinfo (OR_BUF * buf, SM_CLASS * class_)
{
  DB_OBJLIST *triggers;
  int offset;

  /* compute the variable offsets relative to the end of the header (beginning of variable table) */
  offset = tf_Metaclass_class.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_class.mc_n_variable);

  /* name */
  or_put_offset (buf, offset);

  offset += string_disk_size (sm_ch_name ((MOBJ) class_));

  or_put_offset (buf, offset);

  offset += string_disk_size (class_->loader_commands);

  /* representation set */
  or_put_offset (buf, offset);

  offset += substructure_set_size ((DB_LIST *) class_->representations, (LSIZER) representation_size);
  or_put_offset (buf, offset);

  offset += object_set_size (class_->users);
  or_put_offset (buf, offset);

  offset += object_set_size (class_->inheritance);
  or_put_offset (buf, offset);

  offset += substructure_set_size ((DB_LIST *) class_->attributes, (LSIZER) attribute_size);
  or_put_offset (buf, offset);

  offset += substructure_set_size ((DB_LIST *) class_->shared, (LSIZER) attribute_size);
  or_put_offset (buf, offset);

  offset += substructure_set_size ((DB_LIST *) class_->class_attributes, (LSIZER) attribute_size);

  or_put_offset (buf, offset);

  offset += substructure_set_size ((DB_LIST *) class_->methods, (LSIZER) method_size);

  or_put_offset (buf, offset);

  offset += substructure_set_size ((DB_LIST *) class_->class_methods, (LSIZER) method_size);

  or_put_offset (buf, offset);

  offset += substructure_set_size ((DB_LIST *) class_->method_files, (LSIZER) methfile_size);

  or_put_offset (buf, offset);

  offset += substructure_set_size ((DB_LIST *) class_->resolutions, (LSIZER) resolution_size);

  or_put_offset (buf, offset);

  offset += substructure_set_size ((DB_LIST *) class_->query_spec, (LSIZER) query_spec_size);

  or_put_offset (buf, offset);

  (void) tr_get_cache_objects (class_->triggers, &triggers);
  offset += object_set_size (triggers);

  /* property list */
  or_put_offset (buf, offset);

  offset += property_list_size (class_->properties);

  or_put_offset (buf, offset);

  offset += string_disk_size (class_->comment);

  or_put_offset (buf, offset);

  offset += substructure_set_size ((DB_LIST *) class_->partition, (LSIZER) partition_info_size);

  /* end of object */
  or_put_offset (buf, offset);
  buf->ptr = PTR_ALIGN (buf->ptr, INT_ALIGNMENT);

  return (offset);
}

/*
 * put_class_attributes - Writes the fixed and variable attributes of a class.
 *    return: void
 *    buf(in/out): translation buffer
 *    class(in): class structure
 */
static void
put_class_attributes (OR_BUF * buf, SM_CLASS * class_)
{
  DB_OBJLIST *triggers;

#if !defined(NDEBUG)
  if (!HFID_IS_NULL (sm_ch_heap ((MOBJ) class_)))
    {
      assert (!OID_ISNULL (sm_ch_rep_dir ((MOBJ) class_)));
    }
#endif

  /* attribute id counters */

  /* doesn't exist yet */
  or_put_int (buf, class_->att_ids);
  or_put_int (buf, class_->method_ids);

  or_put_oid (buf, &(class_->header.ch_rep_dir));

  or_put_int (buf, class_->header.ch_heap.vfid.fileid);
  or_put_int (buf, class_->header.ch_heap.vfid.volid);
  or_put_int (buf, class_->header.ch_heap.hpgid);

  or_put_int (buf, class_->fixed_count);
  or_put_int (buf, class_->variable_count);
  or_put_int (buf, class_->fixed_size);
  or_put_int (buf, class_->att_count);
  /* object size is now calculated after loading */
  or_put_int (buf, 0);
  or_put_int (buf, class_->shared_count);
  or_put_int (buf, class_->method_count);
  or_put_int (buf, class_->class_method_count);
  or_put_int (buf, class_->class_attribute_count);
  or_put_int (buf, class_->flags);
  or_put_int (buf, (int) class_->class_type);

  /* owner object */
  or_pack_mop (buf, class_->owner);
  or_put_int (buf, (int) class_->collation_id);

  or_put_int (buf, class_->tde_algorithm);

  /* 0: NAME */
  put_string (buf, sm_ch_name ((MOBJ) class_));

  put_string (buf, class_->loader_commands);

  put_substructure_set (buf, (DB_LIST *) class_->representations, representation_to_disk_lwriter,
            &tf_Metaclass_representation.mc_classoid, tf_Metaclass_representation.mc_repid);

  put_object_set (buf, class_->users);

  put_object_set (buf, class_->inheritance);

  put_substructure_set (buf, (DB_LIST *) class_->attributes, attribute_to_disk_lwriter,
            &tf_Metaclass_attribute.mc_classoid, tf_Metaclass_attribute.mc_repid);

  put_substructure_set (buf, (DB_LIST *) class_->shared, attribute_to_disk_lwriter,
            &tf_Metaclass_attribute.mc_classoid, tf_Metaclass_attribute.mc_repid);

  put_substructure_set (buf, (DB_LIST *) class_->class_attributes, attribute_to_disk_lwriter,
            &tf_Metaclass_attribute.mc_classoid, tf_Metaclass_attribute.mc_repid);

  put_substructure_set (buf, (DB_LIST *) class_->methods, method_to_disk_lwriter, &tf_Metaclass_method.mc_classoid,
            tf_Metaclass_method.mc_repid);

  put_substructure_set (buf, (DB_LIST *) class_->class_methods, method_to_disk_lwriter,
            &tf_Metaclass_method.mc_classoid, tf_Metaclass_method.mc_repid);

  put_substructure_set (buf, (DB_LIST *) class_->method_files, methfile_to_disk_lwriter,
            &tf_Metaclass_methfile.mc_classoid, tf_Metaclass_methfile.mc_repid);

  put_substructure_set (buf, (DB_LIST *) class_->resolutions, resolution_to_disk_lwriter,
            &tf_Metaclass_resolution.mc_classoid, tf_Metaclass_resolution.mc_repid);

  put_substructure_set (buf, (DB_LIST *) class_->query_spec, query_spec_to_disk_lwriter,
            &tf_Metaclass_query_spec.mc_classoid, tf_Metaclass_query_spec.mc_repid);

  /*
   * triggers - for simplicity, convert the cache into a flattened
   * list of object id's
   */
  (void) tr_get_cache_objects (class_->triggers, &triggers);
  put_object_set (buf, triggers);

  put_property_list (buf, class_->properties);

  put_string (buf, class_->comment);

  put_substructure_set (buf, (DB_LIST *) class_->partition, partition_info_to_disk_lwriter,
            &tf_Metaclass_partition.mc_classoid, tf_Metaclass_partition.mc_repid);
}

/*
 * class_to_disk - Write the disk representation of a class.
 *    return: void
 *    buf(in/out): translation buffer
 *    class(in): class structure
 * Note:
 *    The writing of the offset table and the attributes was split into
 *    two pieces because it was getting too long.
 *    Caller must have a setup a jmpbuf (called setjmp) to handle any
 *    errors
 */
static void
class_to_disk (OR_BUF * buf, SM_CLASS * class_)
{
  char *start;
  int offset;

  /* kludge, we may have to do some last minute adj of the class structures before saving.  In particular, some of the
   * attribute fields need to be placed in the attribute property list because there are no corresponding fields in the
   * disk representation.  This may result in storage allocation which because we don't have modern computers may fail.
   * This function does all of the various checking up front so we don't have to detect it later in the substructure
   * conversion routines */
  if (!check_class_structure (class_))
    {
      if (er_errid () != NO_ERROR)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
    }
    }
  else
    {
      start = buf->ptr - OR_NON_MVCC_HEADER_SIZE;
      /* header already written */
      offset = put_class_varinfo (buf, class_);
      put_class_attributes (buf, class_);

      if (start + offset + OR_NON_MVCC_HEADER_SIZE != buf->ptr)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_OUT_OF_SYNC, 0);
      return;
    }
    }
}


/*
 * tf_class_size - Calculates the disk size of a class.
 *    return: disk size of class
 *    classobj(in): pointer to class structure
 */
static int
tf_class_size (MOBJ classobj)
{
  SM_CLASS *class_;
  DB_OBJLIST *triggers;
  int size;

  class_ = (SM_CLASS *) classobj;

  /* make sure properties are decached */
  if (!check_class_structure (class_))
    {
      return (-1);
    }

  size = OR_NON_MVCC_HEADER_SIZE;

  size += tf_Metaclass_class.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_class.mc_n_variable);

  size += string_disk_size (sm_ch_name ((MOBJ) class_));
  size += string_disk_size (class_->loader_commands);

  size += substructure_set_size ((DB_LIST *) class_->representations, (LSIZER) representation_size);

  size += object_set_size (class_->users);
  size += object_set_size (class_->inheritance);

  size += substructure_set_size ((DB_LIST *) class_->attributes, (LSIZER) attribute_size);
  size += substructure_set_size ((DB_LIST *) class_->shared, (LSIZER) attribute_size);
  size += substructure_set_size ((DB_LIST *) class_->class_attributes, (LSIZER) attribute_size);

  size += substructure_set_size ((DB_LIST *) class_->methods, (LSIZER) method_size);
  size += substructure_set_size ((DB_LIST *) class_->class_methods, (LSIZER) method_size);

  size += substructure_set_size ((DB_LIST *) class_->method_files, (LSIZER) methfile_size);

  size += substructure_set_size ((DB_LIST *) class_->resolutions, (LSIZER) resolution_size);

  size += substructure_set_size ((DB_LIST *) class_->query_spec, (LSIZER) query_spec_size);

  (void) tr_get_cache_objects (class_->triggers, &triggers);
  size += object_set_size (triggers);

  size += property_list_size (class_->properties);

  size += string_disk_size (class_->comment);

  size += substructure_set_size ((DB_LIST *) class_->partition, (LSIZER) partition_info_size);

  return (size);
}

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * tf_dump_class_size - Debugging function to display disk size information
 * for a class.
 *    return:  void
 *    classobj(in): pointer to class structure
 */
void
tf_dump_class_size (MOBJ classobj)
{
  SM_CLASS *class_;
  DB_OBJLIST *triggers;
  int size, s;

  class_ = (SM_CLASS *) classobj;

  /* make sure properties are decached */
  if (!check_class_structure (class_))
    {
      return;
    }

  size = OR_NON_MVCC_HEADER_SIZE;   /* ? */
  size += tf_Metaclass_class.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_class.mc_n_variable);
  fprintf (stdout, "Fixed size %d\n", size);

  s = string_disk_size (sm_ch_name ((MOBJ) class_));
  fprintf (stdout, "Header name %d\n", s);
  size += s;

  s = string_disk_size (class_->loader_commands);
  fprintf (stdout, "Loader commands %d\n", s);
  size += s;

  s = substructure_set_size ((DB_LIST *) class_->representations, (LSIZER) representation_size);
  fprintf (stdout, "Representations %d\n", s);
  size += s;

  s = object_set_size (class_->users);
  fprintf (stdout, "Users %d\n", s);
  size += s;

  s = object_set_size (class_->inheritance);
  fprintf (stdout, "Inheritance %d\n", s);
  size += s;

  s = substructure_set_size ((DB_LIST *) class_->attributes, (LSIZER) attribute_size);
  fprintf (stdout, "Attributes %d\n", s);
  size += s;

  s = substructure_set_size ((DB_LIST *) class_->shared, (LSIZER) attribute_size);
  fprintf (stdout, "Shared attributes %d\n", s);
  size += s;

  s = substructure_set_size ((DB_LIST *) class_->class_attributes, (LSIZER) attribute_size);
  fprintf (stdout, "Class attributes %d\n", s);
  size += s;

  s = substructure_set_size ((DB_LIST *) class_->methods, (LSIZER) method_size);
  fprintf (stdout, "Methods %d\n", s);
  size += s;

  s = substructure_set_size ((DB_LIST *) class_->class_methods, (LSIZER) method_size);
  fprintf (stdout, "Class methods %d\n", s);
  size += s;

  s = substructure_set_size ((DB_LIST *) class_->method_files, (LSIZER) methfile_size);
  fprintf (stdout, "Method files %d\n", s);
  size += s;

  s = substructure_set_size ((DB_LIST *) class_->resolutions, (LSIZER) resolution_size);
  fprintf (stdout, "Resolutions %d\n", s);
  size += s;

  s = substructure_set_size ((DB_LIST *) class_->query_spec, (LSIZER) query_spec_size);
  fprintf (stdout, "Query_Spec statements %d\n", s);
  size += s;

  (void) tr_get_cache_objects (class_->triggers, &triggers);
  s = object_set_size (triggers);
  fprintf (stdout, "Triggers %d\n", s);
  size += s;

  s = property_list_size (class_->properties);
  fprintf (stdout, "Properties %d\n", s);
  size += s;

  s = string_disk_size (class_->comment);
  fprintf (stdout, "Comment %d\n", s);
  size += s;

  fprintf (stdout, "TOTAL: %d\n", size);
}
#endif /* ENABLE_UNUSED_FUNCTION */

/*
 * tag_component_namespace - restore the appropriate name_space tags for
 * class components.
 *    return: void
 *    components(in/out): component list
 *    namespace(in):
 * Note:
 *    Since the component tag is not stored with the attributes & methods
 *    it must be restored after they have been read.  We know the name_space
 *    because they have been separated into different lists in the
 *    class structure.
 */
static void
tag_component_namespace (SM_COMPONENT * components, SM_NAME_SPACE name_space)
{
  SM_COMPONENT *c;

  for (c = components; c != NULL; c = c->next)
    {
      c->name_space = name_space;
    }
}

/*
 * disk_to_class - Reads the disk representation of a class and creates the
 * memory structure.
 *    return: class structure
 *    buf(in/out): translation buffer
 *    class_ptr(out): return pointer
 * Note:
      A jmp_buf has previously been established.
 */
static SM_CLASS *
disk_to_class (OR_BUF * buf, SM_CLASS ** class_ptr)
{
  SM_CLASS *class_;
  SM_ATTRIBUTE *att;
  OR_VARINFO *vars;
  DB_OBJLIST *triggers;
  DB_VALUE value;
  int rc = NO_ERROR;
  char auto_increment_name[AUTO_INCREMENT_SERIAL_NAME_MAX_LENGTH];
  MOP serial_class_mop = NULL, serial_mop;
  DB_IDENTIFIER serial_obj_id;

  class_ = NULL;
  /* get the variable length and offsets. The offsets are relative to the end of the header (beginning of variable
   * table). */
  vars = read_var_table (buf, tf_Metaclass_class.mc_n_variable);
  if (vars == NULL)
    {
      goto on_error;
    }

  class_ = *class_ptr = classobj_make_class (NULL);
  if (class_ == NULL)
    {
      goto on_error;
    }

  class_->att_ids = or_get_int (buf, &rc);
  class_->method_ids = or_get_int (buf, &rc);

  or_get_oid (buf, &(class_->header.ch_rep_dir));

  class_->header.ch_heap.vfid.fileid = or_get_int (buf, &rc);
  class_->header.ch_heap.vfid.volid = or_get_int (buf, &rc);
  class_->header.ch_heap.hpgid = or_get_int (buf, &rc);

#if !defined(NDEBUG)
  if (!HFID_IS_NULL (sm_ch_heap ((MOBJ) class_)))
    {
      assert (!OID_ISNULL (sm_ch_rep_dir ((MOBJ) class_)));
    }
#endif

  class_->fixed_count = or_get_int (buf, &rc);
  class_->variable_count = or_get_int (buf, &rc);
  class_->fixed_size = or_get_int (buf, &rc);
  class_->att_count = or_get_int (buf, &rc);
  class_->object_size = or_get_int (buf, &rc);
  class_->object_size = 0;  /* calculated later */
  class_->shared_count = or_get_int (buf, &rc);
  class_->method_count = or_get_int (buf, &rc);
  class_->class_method_count = or_get_int (buf, &rc);
  class_->class_attribute_count = or_get_int (buf, &rc);
  class_->flags = or_get_int (buf, &rc);
  class_->class_type = (SM_CLASS_TYPE) or_get_int (buf, &rc);

  /* owner object */
  tp_Object.data_readval (buf, &value, NULL, -1, true, NULL, 0);
  class_->owner = db_get_object (&value);
  class_->collation_id = or_get_int (buf, &rc);

  class_->tde_algorithm = or_get_int (buf, &rc);

  /* variable 0 */
  class_->header.ch_name = get_string (buf, vars[ORC_NAME_INDEX].length);

  /* variable 1 */
  class_->loader_commands = get_string (buf, vars[ORC_LOADER_COMMANDS_INDEX].length);

  /* REPRESENTATIONS */
  /* variable 2 */
  class_->representations =
    (SM_REPRESENTATION *) get_substructure_set (buf, (LREADER) disk_to_representation,
                        vars[ORC_REPRESENTATIONS_INDEX].length);

  /* variable 3 */
  class_->users = get_object_set (buf, vars[ORC_SUBCLASSES_INDEX].length);
  /* variable 4 */
  class_->inheritance = get_object_set (buf, vars[ORC_SUPERCLASSES_INDEX].length);

  class_->attributes = (SM_ATTRIBUTE *) classobj_alloc_threaded_array (sizeof (SM_ATTRIBUTE), class_->att_count);
  if (class_->att_count && class_->attributes == NULL)
    {
      goto on_error;
    }
  classobj_initialize_attributes (class_->attributes);

  class_->shared = (SM_ATTRIBUTE *) classobj_alloc_threaded_array (sizeof (SM_ATTRIBUTE), class_->shared_count);
  if (class_->shared_count && class_->shared == NULL)
    {
      goto on_error;
    }
  classobj_initialize_attributes (class_->shared);

  class_->class_attributes =
    (SM_ATTRIBUTE *) classobj_alloc_threaded_array (sizeof (SM_ATTRIBUTE), class_->class_attribute_count);
  if (class_->class_attribute_count && class_->class_attributes == NULL)
    {
      goto on_error;
    }
  classobj_initialize_attributes (class_->class_attributes);

  class_->methods = (SM_METHOD *) classobj_alloc_threaded_array (sizeof (SM_METHOD), class_->method_count);
  if (class_->method_count && class_->methods == NULL)
    {
      goto on_error;
    }
  classobj_initialize_methods (class_->methods);

  class_->class_methods = (SM_METHOD *) classobj_alloc_threaded_array (sizeof (SM_METHOD), class_->class_method_count);
  if (class_->class_method_count && class_->class_methods == NULL)
    {
      goto on_error;
    }
  classobj_initialize_methods (class_->class_methods);

  /* variable 5 */
  install_substructure_set (buf, (DB_LIST *) class_->attributes, (VREADER) disk_to_attribute,
                vars[ORC_ATTRIBUTES_INDEX].length);
  /* variable 6 */
  install_substructure_set (buf, (DB_LIST *) class_->shared, (VREADER) disk_to_attribute,
                vars[ORC_SHARED_ATTRS_INDEX].length);
  /* variable 7 */
  install_substructure_set (buf, (DB_LIST *) class_->class_attributes, (VREADER) disk_to_attribute,
                vars[ORC_CLASS_ATTRS_INDEX].length);

  /* variable 8 */
  install_substructure_set (buf, (DB_LIST *) class_->methods, (VREADER) disk_to_method, vars[ORC_METHODS_INDEX].length);
  /* variable 9 */
  install_substructure_set (buf, (DB_LIST *) class_->class_methods, (VREADER) disk_to_method,
                vars[ORC_CLASS_METHODS_INDEX].length);

  /*
   * fix up the name_space tags, could do this later but easier just
   * to assume that they're set up correctly
   */
  tag_component_namespace ((SM_COMPONENT *) class_->attributes, ID_ATTRIBUTE);
  tag_component_namespace ((SM_COMPONENT *) class_->shared, ID_SHARED_ATTRIBUTE);
  tag_component_namespace ((SM_COMPONENT *) class_->class_attributes, ID_CLASS_ATTRIBUTE);
  tag_component_namespace ((SM_COMPONENT *) class_->methods, ID_METHOD);
  tag_component_namespace ((SM_COMPONENT *) class_->class_methods, ID_CLASS_METHOD);

  /* variable 10 */
  class_->method_files =
    (SM_METHOD_FILE *) get_substructure_set (buf, (LREADER) disk_to_methfile, vars[ORC_METHOD_FILES_INDEX].length);

  /* variable 11 */
  class_->resolutions =
    (SM_RESOLUTION *) get_substructure_set (buf, (LREADER) disk_to_resolution, vars[ORC_RESOLUTIONS_INDEX].length);

  /* variable 12 */
  class_->query_spec =
    (SM_QUERY_SPEC *) get_substructure_set (buf, (LREADER) disk_to_query_spec, vars[ORC_QUERY_SPEC_INDEX].length);

  /* variable 13 */
  triggers = get_object_set (buf, vars[ORC_TRIGGERS_INDEX].length);
  if (triggers != NULL)
    {
      class_->triggers = tr_make_schema_cache (TR_CACHE_CLASS, triggers);
    }

  /* variable 14 */
  class_->properties = get_property_list (buf, vars[ORC_PROPERTIES_INDEX].length);

  /* variable 15 */
  class_->comment = get_string (buf, vars[ORC_COMMENT_INDEX].length);

  /* variable 16 */
  class_->partition =
    (SM_PARTITION *) get_substructure_set (buf, (LREADER) disk_to_partition_info, vars[ORC_PARTITION_INDEX].length);

  /* build the ordered instance/shared instance list */
  classobj_fixup_loaded_class (class_);

  /* set attribute's auto_increment object if any */
  for (att = class_->attributes; att != NULL; att = (SM_ATTRIBUTE *) att->header.next)
    {
      att->auto_increment = NULL;
      if (att->flags & SM_ATTFLAG_AUTO_INCREMENT)
    {
      if (serial_class_mop == NULL)
        {
          serial_class_mop = sm_find_class (CT_SERIAL_NAME);
          if (serial_class_mop == NULL)
        {
          ASSERT_ERROR ();
          goto on_error;
        }
        }

      SET_AUTO_INCREMENT_SERIAL_NAME (auto_increment_name, sm_ch_name ((MOBJ) class_), att->header.name);
      serial_mop = do_get_serial_obj_id (&serial_obj_id, serial_class_mop, auto_increment_name);

      /* If this att is inherited from a super class, serial_mop can be NULL. In this case, att->auto_increment
       * will be set later. */
#if 0
      if (serial_mop == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ATTRIBUTE, 1, auto_increment_name);
          db_ws_free (class_);
          free_var_table (vars);
          or_abort (buf);
          return NULL;
        }
#endif
      att->auto_increment = serial_mop;
    }
    }

  free_var_table (vars);

  return (class_);

on_error:

  if (vars != NULL)
    {
      free_var_table (vars);
    }

  if (class_ != NULL)
    {
      classobj_free_class (class_);
    }

  *class_ptr = NULL;

  return NULL;
}


/*
 * root_to_disk - Write the disk representation of the root class.
 *    return: void
 *    buf(in/out): translation buffer
 *    root(in): root class object
 *    header_size(in): the size of header - variable in MVCC
 * Note:
 *    Caller must have a setup a jmpbuf (called setjmp) to handle any errors.
 *    Only the header part of the 'root' object is serialized. Please see comment on ROOT_CLASS definition.
 */
static void
root_to_disk (OR_BUF * buf, ROOT_CLASS * root)
{
  char *start;
  int offset;

  start = buf->ptr - OR_NON_MVCC_HEADER_SIZE;   /* header already written */

  /* compute the variable offsets relative to the end of the header (beginning of variable table) */
  offset = tf_Metaclass_root.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_root.mc_n_variable);

  /* name */
  or_put_offset (buf, offset);
  offset += string_disk_size (sm_ch_name ((MOBJ) root));

  /* end of object */
  or_put_offset (buf, offset);
  buf->ptr = PTR_ALIGN (buf->ptr, INT_ALIGNMENT);

  assert (OID_ISNULL (sm_ch_rep_dir ((MOBJ) root)));    /* is dummy */

  /* heap file id - see assumptions in comment above */
  or_put_int (buf, (int) root->header.ch_heap.vfid.fileid);
  or_put_int (buf, (int) root->header.ch_heap.vfid.volid);
  or_put_int (buf, (int) root->header.ch_heap.hpgid);

  put_string (buf, sm_ch_name ((MOBJ) root));

  if (start + OR_NON_MVCC_HEADER_SIZE + offset != buf->ptr)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_OUT_OF_SYNC, 0);
    }
}


/*
 * root_size - Calculates the disk size of the root class.
 *    return: disk size of root class
 *    rootobj(in): root class object
 *
 *  Note:
 *    Only the header part of the 'root' object is serialized. Please see comment on ROOT_CLASS definition.
 */
static int
root_size (MOBJ rootobj)
{
  ROOT_CLASS *root;
  int size;

  root = (ROOT_CLASS *) rootobj;

  size = OR_NON_MVCC_HEADER_SIZE;
  size += tf_Metaclass_root.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_root.mc_n_variable);

  /* name */
  size += string_disk_size (sm_ch_name ((MOBJ) root));

  return (size);
}


/*
 * disk_to_root - Reads the disk representation of the root class and builds
 * the memory rootclass.
 *    return: root class object
 *    buf(in/out): translation buffer
 * Note:
 *    We know there is only one static structure for the root class so we use it rather than allocating a structure.
 *    Only the header part of the 'root' object is serialized. Please see comment on ROOT_CLASS definition.
 */
static ROOT_CLASS *
disk_to_root (OR_BUF * buf)
{
  char *start;
  OR_VARINFO *vars;
  int rc = NO_ERROR;

  start = buf->ptr - OR_NON_MVCC_HEADER_SIZE;   /* header already read */

  /* get the variable length and offsets. The offsets are relative to the end of the header (beginning of variable
   * table). */
  vars = read_var_table (buf, tf_Metaclass_root.mc_n_variable);
  if (vars == NULL)
    {
      if (er_errid () != NO_ERROR)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
    }
      return NULL;
    }
  else
    {
      assert (OID_ISNULL (sm_ch_rep_dir ((MOBJ) & sm_Root_class))); /* is dummy */

      sm_Root_class.header.ch_heap.vfid.fileid = (FILEID) or_get_int (buf, &rc);
      sm_Root_class.header.ch_heap.vfid.volid = (VOLID) or_get_int (buf, &rc);
      sm_Root_class.header.ch_heap.hpgid = (PAGEID) or_get_int (buf, &rc);

      /* name - could make sure its the same as sm_Root_class_name */
      or_advance (buf, vars[0].length);

      if (start + OR_NON_MVCC_HEADER_SIZE + vars[0].offset + vars[0].length != buf->ptr)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_OUT_OF_SYNC, 0);
    }

      free_var_table (vars);
    }
  return (&sm_Root_class);
}


/*
 * tf_disk_to_class - transforming the disk representation of a class.
 *    return: class structure
 *    oid(in):
 *    record(in): disk record
 * Note:
 *    It is imperitive that garbage collection be disabled during this
 *    operation.
 *    This is because the structure being built may contain pointers
 *    to MOPs but it has not itself been attached to its own MOP yet so it
 *    doesn't serve as a gc root.   Make sure the caller
 *    calls ws_cache() immediately after we return so the new object
 *    gets attached as soon as possible.  We must also have already
 *    allocated the MOP in the caller so that we don't get one of the
 *    periodic gc's when we attempt to allocate a new MOP.
 *    This dependency should be isolated in a ws_ function called by
 *    the locator.
 */
MOBJ
tf_disk_to_class (OID * oid, RECDES * record)
{
  OR_BUF orep, *buf;
  unsigned int repid, chn;
  /* declare volatile to prevent reg optimization */
  MOBJ volatile class_;
  int rc = NO_ERROR;

  assert (oid != NULL && !OID_ISNULL (oid));

  /* should we assume this ? */
  if (!tf_Metaclass_class.mc_n_variable)
    {
      tf_compile_meta_classes ();
    }

  class_ = NULL;

  buf = &orep;
  or_init (buf, record->data, record->length);


  assert (OR_GET_OFFSET_SIZE (buf->ptr) == BIG_VAR_OFFSET_SIZE);

  repid = or_get_int (buf, &rc);
  repid = repid & ~OR_OFFSET_SIZE_FLAG;
  assert (((char) (repid >> OR_MVCC_FLAG_SHIFT_BITS) & OR_MVCC_FLAG_MASK) == 0);
  chn = or_get_int (buf, &rc);

  if (oid_is_root (oid))
    {
      class_ = (MOBJ) disk_to_root (buf);
    }
  else
    {
      class_ = (MOBJ) disk_to_class (buf, (SM_CLASS **) (&class_));
      if (class_ != NULL)
    {
      ((SM_CLASS *) class_)->repid = repid;
    }
    }

  if (class_ != NULL)
    {
      ((SM_CLASS *) class_)->header.ch_obj_header.chn = chn;
    }

  sm_bump_global_schema_version ();

  if (buf->ptr > buf->endptr)
    {
      /*
       * make sure to clear the class that was being created,
       * an appropriate error will have been set
       */
      classobj_free_class ((SM_CLASS *) class_);
      class_ = NULL;
    }

  return (class_);
}


/*
 * tf_class_to_disk - creates the disk representation of a class.
 *    return: zero for success, non-zero if errors
 *    classobj(in): pointer to class structure
 *    record(out): disk record
 * Note:
 *    If the record was not large enough for the class, resulting in
 *    the or_overflow error, this function returns the required size of the
 *    record as a negative number.
 */
TF_STATUS
tf_class_to_disk (MOBJ classobj, RECDES * record)
{
  OR_BUF orep, *buf;
  /* prevent reg optimization which hoses longmp */
  SM_CLASS *class_;
  int expected_size;
  SM_CLASS_HEADER *header;
  int chn;
  TF_STATUS status;
  int rc = 0;
  int prop_free = 0;
  int repid;

  /* should we assume this ? */
  if (!tf_Metaclass_class.mc_n_variable)
    {
      tf_compile_meta_classes ();
    }

  /*
   * don't worry about deferred fixup for classes, we don't usually have
   * many temporary OIDs in classes.
   */
  buf = &orep;
  or_init (buf, record->data, record->area_size);

  class_ = (SM_CLASS *) classobj;

  header = (SM_CLASS_HEADER *) classobj;
  if (header->ch_type != SM_META_ROOT)
    {
      /* put all default_expr values in attribute properties */
      rc = tf_attribute_default_expr_to_property (class_->attributes);
    }

  /*
   * test - this isn't necessary but we've been having a class size related
   * bug that I want to try to catch - take this out when we're sure
   */
  if (class_ == (SM_CLASS *) (&sm_Root_class))
    {
      expected_size = root_size (classobj);
    }
  else
    {
      expected_size = tf_class_size (classobj);
    }

  if (buf->ptr + expected_size > buf->endptr)
    {
      status = TF_OUT_OF_SPACE;
      record->length = -expected_size;
      goto exit;
    }
  else
    {
      status = TF_SUCCESS;

      /* representation id, offset size */
      repid = class_->repid;
      assert (((char) (repid >> OR_MVCC_FLAG_SHIFT_BITS) & OR_MVCC_FLAG_MASK) == 0);
      OR_SET_VAR_OFFSET_SIZE (repid, BIG_VAR_OFFSET_SIZE);  /* 4byte */

      chn = class_->header.ch_obj_header.chn + 1;
      class_->header.ch_obj_header.chn = chn;

      /* The header size of a class record in the same like non-MVCC case. */
      or_put_int (buf, repid);
      or_put_int (buf, chn);

      if (header->ch_type == SM_META_ROOT)
    {
      root_to_disk (buf, (ROOT_CLASS *) class_);
    }
      else
    {
      assert (class_->repid == (repid & ~OR_OFFSET_SIZE_FLAG));
      class_to_disk (buf, (SM_CLASS *) class_);
    }

      record->length = CAST_BUFLEN (buf->ptr - buf->buffer);

      /* sanity test, this sets an error only so we can see it if it happens */
      if (record->length != expected_size)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_SIZE_MISMATCH, 2, expected_size, record->length);
      status = TF_ERROR;
      goto exit;
    }
    }

exit:
  /* restore properties */
  if (((SM_CLASS_HEADER *) classobj)->ch_type != SM_META_ROOT && class_->partition)
    {
      if (prop_free)
    {
      classobj_free_prop (class_->properties);
      class_->properties = NULL;
    }
    }

  return (status);
}

/*
 * tf_object_size - Determines the number of byte required to store an object
 * on disk.
 *    return: byte size of object on disk
 *    classobj(in): class of instance
 *    obj(in): instance to examine
 * Note:
 *    This will work for any object; classes, instances, or the rootclass.
 */
int
tf_object_size (MOBJ classobj, MOBJ obj)
{
  int size = 0;

  if (classobj != (MOBJ) (&sm_Root_class))
    {
      int dummy;
      size = object_size ((SM_CLASS *) classobj, obj, &dummy);
    }
  else if (obj == (MOBJ) (&sm_Root_class))
    {
      size = root_size (obj);
    }
  else
    {
      size = tf_class_size (obj);
    }

  return size;
}

/*
 * enumeration_size () - calculates size of enumeration
 * return : size of enumeration
 * enumeration (in) : enumeration
 */
static int
enumeration_size (const DB_ENUMERATION * enumeration)
{
  return or_packed_enumeration_size (enumeration);
}

/*
 * put_enumeration () - pack an enumeration
 * return: error code or NO_ERROR
 * enumeration (in): enumeration
 */
static void
put_enumeration (OR_BUF * buf, const DB_ENUMERATION * enumeration)
{
  (void) or_put_enumeration (buf, enumeration);
}

/*
 * get_enumeration - read enumeration from input buffer
 * return: NO_ERROR or error code
 * buf(in): input buffer
 * enumeration(in/out): pointer to enumeration holder
 * expected(in): expected length
 */
static int
get_enumeration (OR_BUF * buf, DB_ENUMERATION * enumeration, int expected)
{
  if (expected == 0)
    {
      enumeration->count = 0;
      enumeration->elements = NULL;
      return NO_ERROR;
    }
  return or_get_enumeration (buf, enumeration);
}


/*
 * tf_attribute_default_expr_to_property - transfer default_expr flag to a
 *                                         disk stored property
 *  returns: error code or NO_ERROR
 *  attr_list(in): attribute list to process
 */
static int
tf_attribute_default_expr_to_property (SM_ATTRIBUTE * attr_list)
{
  SM_ATTRIBUTE *attr = NULL;
  DB_DEFAULT_EXPR *default_expr;
  DB_VALUE default_expr_value;

  if (attr_list == NULL)
    {
      /* nothing to do */
      return NO_ERROR;
    }

  for (attr = attr_list; attr; attr = (SM_ATTRIBUTE *) attr->header.next)
    {
      default_expr = &attr->default_value.default_expr;
      if (default_expr->default_expr_type != DB_DEFAULT_NONE)
    {
      /* attr has default expression as default value */
      if (attr->properties == NULL)
        {
          /* allocate new property sequence */
          attr->properties = classobj_make_prop ();

          if (attr->properties == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, sizeof (DB_SEQ));
          return er_errid ();
        }
        }

      if (default_expr->default_expr_op != NULL_DEFAULT_EXPRESSION_OPERATOR)
        {
          DB_SEQ *default_expr_sequence = NULL;
          DB_VALUE value;

          default_expr_sequence = set_create_sequence (3);
          if (default_expr_sequence == NULL)
        {
          if (attr->properties)
            {
              classobj_free_prop (attr->properties);
              attr->properties = NULL;
            }
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, sizeof (DB_SEQ));
          return er_errid ();
        }

          /* currently, only T_TO_CHAR operator is allowed  */
          assert (default_expr->default_expr_op == T_TO_CHAR);
          db_make_int (&value, (int) T_TO_CHAR);
          set_put_element (default_expr_sequence, 0, &value);

          /* default expression type */
          db_make_int (&value, default_expr->default_expr_type);
          set_put_element (default_expr_sequence, 1, &value);

          /* default expression format */
          if (default_expr->default_expr_format)
        {
          db_make_string (&value, default_expr->default_expr_format);
        }
          else
        {
          db_make_null (&value);
        }

          set_put_element (default_expr_sequence, 2, &value);

          /* create and put sequence */
          db_make_sequence (&default_expr_value, default_expr_sequence);
          default_expr_sequence = NULL;
          classobj_put_prop (attr->properties, "default_expr", &default_expr_value);
          pr_clear_value (&default_expr_value);

        }
      else
        {
          /* add default_expr property to sequence */
          db_make_int (&default_expr_value, default_expr->default_expr_type);
          classobj_put_prop (attr->properties, "default_expr", &default_expr_value);
        }
    }
      else if (attr->properties != NULL)
    {
      /* make sure property is unset for existing attributes */
      classobj_drop_prop (attr->properties, "default_expr");
    }

      DB_DEFAULT_EXPR_TYPE update_default = attr->on_update_default_expr;
      if (update_default != DB_DEFAULT_NONE)
    {
      if (attr->properties == NULL)
        {
          attr->properties = classobj_make_prop ();

          if (attr->properties == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, sizeof (DB_SEQ));
          return er_errid ();
        }
        }

      db_make_int (&default_expr_value, update_default);
      classobj_put_prop (attr->properties, "update_default", &default_expr_value);
    }
      else if (attr->properties != NULL)
    {
      /* make sure property is unset for existing attributes */
      classobj_drop_prop (attr->properties, "update_default");
    }
    }

  /* all ok */
  return NO_ERROR;
}


#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * tf_set_size - calculates the number of bytes that are required to store
 * the disk representation of a set.
 *    return: size in bytes (-1 if error)
 *    set(in): set to examine
 * Note:
 *    If -1 is returned, an error was detected while calculating the
 *    size and er_errid() must be consulted to determine the cause.
 *    In practice, errors should not happen since we're simply
 *    mapping over the elements without fetching any objects.
 */
int
tf_set_size (DB_SET * set)
{
  DB_VALUE value;
  int size;

  /* won't handle references to "attached" sets that have been swapped out */
  if (set == NULL || set->set == NULL)
    {
      return 0;
    }

  /*
   * Doesn't matter which set function we call, they all use the
   * Don't have to synthesize a domain, we can get the one out
   * of the set itself.
   */
  db_make_set (&value, set);
  size = (*(tp_Set.lengthval)) (&value, 1);

  return size;
}


/*
 * tf_pack_set - transforms a DB_SET into its disk representation.
 *    return: error code
 *    set(in): set to pack
 *    buffer(out): destination buffer
 *    buffer_size(in): maximum length of buffer
 *    actual_bytes(out): number of bytes used or required
 * Note:
 *    If the set will fit within the buffer_size, no error is returned and
 *    actual_bytes is set to the number of bytes used.
 *    If the set will not fit in the buffer, ER_TF_BUFFER_OVERFLOW is returned
 *    and actual_bytes is set to the number of bytes that are required
 *    to pack this set.
 *    Other errors may occur as the set is examined.
 */
int
tf_pack_set (DB_SET * set, char *buffer, int buffer_size, int *actual_bytes)
{
  OR_BUF orep, *buf;
  int error;
  DB_VALUE value;

  if (set == NULL || set->set == NULL)
    {
      if (actual_bytes != NULL)
    {
      *actual_bytes = 0;
    }
      return NO_ERROR;
    }

  /* set up a transformation buffer, may want to do deferred fixup here ? */
  buf = &orep;
  or_init (buf, buffer, buffer_size);

  switch (_setjmp (buf->env))
    {
    case 0:
      error = NO_ERROR;

      /*
       * Doesn't matter which set function we pick, all the types are the same.
       * Don't have to pass in domain either since the type will be
       * self-describing.
       */
      db_make_set (&value, set);
      (*(tp_Set.writeval)) (buf, &value);

      if (actual_bytes != NULL)
    {
      *actual_bytes = (int) (buf->ptr - buf->buffer);
    }
      break;

      /*
       * something happened, if it was ER_TF_BUFFER_OVERFLOW, return
       * the desired size as a negative number
       */

    case ER_TF_BUFFER_OVERFLOW:
      error = ER_TF_BUFFER_OVERFLOW;
      if (actual_bytes != NULL)
    {
      DB_VALUE value;
      db_make_set (&value, set);
      *actual_bytes = (*(tp_Set.lengthval)) (&value, 1);
    }
      break;

    default:
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      break;
    }

  return (error);
}
#endif /* ENABLE_UNUSED_FUNCTION */


/*
 * partition_to_disk - Write the disk representation of partition information
 *    return: NO_ERROR or error code
 *    buf(in/out): translation buffer
 *    partition_info(in):
 */
static int
partition_info_to_disk (OR_BUF * buf, SM_PARTITION * partition_info)
{
  char *start;
  int offset;
  DB_VALUE val;

  start = buf->ptr;
  /* VARIABLE OFFSET TABLE */
  offset = tf_Metaclass_partition.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_partition.mc_n_variable);

  db_make_sequence (&val, partition_info->values);

  or_put_offset (buf, offset);
  offset += string_disk_size (partition_info->pname);

  or_put_offset (buf, offset);
  offset += string_disk_size (partition_info->expr);

  or_put_offset (buf, offset);
  offset += or_packed_value_size (&val, 1, 1, 0);

  or_put_offset (buf, offset);
  offset += string_disk_size (partition_info->comment);

  or_put_offset (buf, offset);
  buf->ptr = PTR_ALIGN (buf->ptr, INT_ALIGNMENT);

  /* ATTRIBUTES */
  or_put_int (buf, partition_info->partition_type);
  or_put_int (buf, partition_info->class_partition_type);

  put_string (buf, partition_info->pname);
  put_string (buf, partition_info->expr);

  or_put_value (buf, &val, 1, 1, 0);

  put_string (buf, partition_info->comment);

  if (start + offset != buf->ptr)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_OUT_OF_SYNC, 0);
    }

  return NO_ERROR;
}

static inline void
partition_info_to_disk_lwriter (void *buf, void *partition_info)
{
  (void) partition_info_to_disk (STATIC_CAST (OR_BUF *, buf), STATIC_CAST (SM_PARTITION *, partition_info));
}

/*
 * partition_info_size - Calculates the disk size of a sm_partition structure.
 *    return: disk size
 *    partition_info(in):
 */
static int
partition_info_size (SM_PARTITION * partition_info)
{
  int size;
  DB_VALUE val;

  size = tf_Metaclass_partition.mc_fixed_size + OR_VAR_TABLE_SIZE (tf_Metaclass_partition.mc_n_variable);
  size += string_disk_size (partition_info->pname);
  size += string_disk_size (partition_info->expr);
  size += string_disk_size (partition_info->comment);

  db_make_sequence (&val, partition_info->values);
  size += or_packed_value_size (&val, 1, 1, 0);

  return (size);
}


/*
 * disk_to_partition_info - Reads the disk representation of partition
 * information and creates the memory representation.
 *    return: new sm_partition structure
 *    buf(in/out): translation buffer
 */
static SM_PARTITION *
disk_to_partition_info (OR_BUF * buf)
{
  SM_PARTITION *partition_info = NULL;
  OR_VARINFO *vars;
  DB_VALUE val;
  int error = NO_ERROR;

  vars = read_var_table (buf, tf_Metaclass_partition.mc_n_variable);
  if (vars == NULL)
    {
      if (er_errid () != NO_ERROR)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
    }
      return NULL;
    }

  partition_info = classobj_make_partition_info ();
  if (partition_info == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (SM_PARTITION));
      return NULL;
    }
  else
    {
      partition_info->partition_type = or_get_int (buf, &error);
      if (error != NO_ERROR)
    {
      free_var_table (vars);
      classobj_free_partition_info (partition_info);
      return NULL;
    }

      partition_info->class_partition_type = (DB_CLASS_PARTITION_TYPE) or_get_int (buf, &error);
      if (error != NO_ERROR)
    {
      free_var_table (vars);
      classobj_free_partition_info (partition_info);
      return NULL;
    }
      assert (partition_info->class_partition_type >= DB_PARTITIONED_CLASS
          && partition_info->class_partition_type <= DB_PARTITION_CLASS);

      partition_info->pname = get_string (buf, vars[ORC_PARTITION_NAME_INDEX].length);
      partition_info->expr = get_string (buf, vars[ORC_PARTITION_EXPR_INDEX].length);

      error = or_get_value (buf, &val, NULL, vars[ORC_PARTITION_VALUES_INDEX].length, true);
      if (error != NO_ERROR)
    {
      free_var_table (vars);
      classobj_free_partition_info (partition_info);
      return NULL;
    }
      partition_info->values = db_seq_copy (db_get_set (&val));
      if (partition_info->values == NULL)
    {
      free_var_table (vars);
      pr_clear_value (&val);
      classobj_free_partition_info (partition_info);
      return NULL;
    }

      partition_info->comment = get_string (buf, vars[ORC_PARTITION_COMMENT_INDEX].length);
    }

  pr_clear_value (&val);
  free_var_table (vars);
  return (partition_info);
}