Skip to content

File load_object.c

File List > cubrid > src > loaddb > load_object.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.
 *
 */

/*
 * load_object.c: simplified object descriptions.
 */

#include "config.h"

#include <stdio.h>
#include <fcntl.h>
#include <assert.h>
#if defined(WINDOWS)
#include <io.h>
#else
#include <unistd.h>
#endif
#include <ctype.h>
#include <sys/stat.h>
#include <math.h>

#include "utility.h"
#include "misc_string.h"
#include "memory_alloc.h"
#include "dbtype.h"
#include "object_representation.h"
#include "work_space.h"
#include "class_object.h"
#include "object_primitive.h"
#include "set_object.h"
#include "db.h"
#include "schema_manager.h"
#include "server_interface.h"
#include "load_object.h"
#include "db_value_printer.hpp"
#include "network_interface_cl.h"
#include "printer.hpp"

#include "message_catalog.h"
#include "string_opfunc.h"
#if defined(WINDOWS)
#include "porting.h"
#endif

#define MIGRATION_CHUNK 4096
static char migration_buffer[MIGRATION_CHUNK];

static int object_disk_size (DESC_OBJ * obj, int *offset_size_ptr);
static void put_varinfo (OR_BUF * buf, DESC_OBJ * obj, int offset_size);
static void put_attributes (OR_BUF * buf, DESC_OBJ * obj);
static void get_desc_current (OR_BUF * buf, SM_CLASS * class_, DESC_OBJ * obj, int bound_bit_flag, int offset_size,
                  bool is_unloaddb);
static SM_ATTRIBUTE *find_current_attribute (SM_CLASS * class_, int id);
static void get_desc_old (OR_BUF * buf, SM_CLASS * class_, int repid, DESC_OBJ * obj, int bound_bit_flag,
              int offset_size, bool is_unloaddb);
static void init_load_err_filter (void);
static void default_clear_err_filter (void);

#if (MAJOR_VERSION >= 11) || (MAJOR_VERSION == 10 && MINOR_VERSION >= 1)
extern int data_readval_string (OR_BUF * buf, DB_VALUE * value, TP_DOMAIN * domain, int size, bool copy, char *copy_buf,
                int copy_buf_len);
#endif

/*
 * make_desc_obj - Makes an object descriptor for a particular class.
 *    return: object descriptor
 *    class(in): class structure
 *    string_buf_size(in): Specifies the size of the memory space to copy the data of the varchar column
 */
DESC_OBJ *
make_desc_obj (SM_CLASS * class_, int pre_alloc_varchar_size)
{
  DESC_OBJ *obj;
  SM_ATTRIBUTE *att;
  int i;

  if ((obj = (DESC_OBJ *) malloc (sizeof (DESC_OBJ))) == NULL)
    {
      return NULL;
    }
  if (class_ == NULL)
    {
      return obj;
    }

  obj->classop = NULL;
  obj->class_ = class_;
  obj->updated_flag = 0;
  obj->count = class_->att_count;
  obj->atts = NULL;
  obj->values = NULL;
  obj->dbvalue_buf_ptr = NULL;

  if (class_->att_count)
    {
      obj->values = (DB_VALUE *) malloc (sizeof (DB_VALUE) * class_->att_count);
      if (obj->values == NULL)
    {
      free_and_init (obj);
      return NULL;
    }
      obj->atts = (SM_ATTRIBUTE **) malloc (sizeof (SM_ATTRIBUTE *) * class_->att_count);
      if (obj->atts == NULL)
    {
      free_and_init (obj->values);
      free_and_init (obj);
      return NULL;
    }

      // Until now, string_buf_size is set to -1 when calling from compatdb.
      if (pre_alloc_varchar_size > 0)
    {
      obj->dbvalue_buf_ptr = (DBVALUE_BUF *) calloc (class_->att_count, sizeof (DBVALUE_BUF));
    }

      for (i = 0, att = class_->attributes; i < class_->att_count; i++, att = (SM_ATTRIBUTE *) att->header.next)
    {
      db_make_null (&obj->values[i]);
      obj->atts[i] = att;

      if (obj->dbvalue_buf_ptr && att->type->get_id () == DB_TYPE_VARCHAR)
        {
          INT64 byte_sz = pre_alloc_varchar_size;
          assert (pre_alloc_varchar_size <= DB_MAX_VARCHAR_PRECISION);
          assert (att->domain != NULL);

          if (att->domain->precision > 0 && att->domain->precision <= pre_alloc_varchar_size)
        {
          byte_sz = att->domain->precision;
        }

          /* Using INTL_CODESET_MULT() causes significant memory waste. 
           * Therefore, realistic character lengths are used.
           * (byte_sz * INTL_CODESET_MULT (att->domain->codeset));
           */
          if (att->domain->codeset == INTL_CODESET_UTF8)
        byte_sz *= 3;
          else if (att->domain->codeset == INTL_CODESET_KSC5601_EUC)
        byte_sz *= 2;

          if (byte_sz < OR_MINIMUM_STRING_LENGTH_FOR_COMPRESSION)
        {
          /* When using dbvalue_buf_ptr, the PEEK method will be specified to read.
           * In the PEEK method, a buffer is needed only when the string is compressed.    
           */
          continue;
        }

          obj->dbvalue_buf_ptr[i].buf = (char *) malloc (byte_sz + 1);
          obj->dbvalue_buf_ptr[i].buf_size = (obj->dbvalue_buf_ptr[i].buf == NULL) ? 0 : byte_sz;
        }
    }
    }

  return obj;
}

/*
 * desc_free - Frees the storage for an object descriptor.
 *    return: none
 *    obj(out): object descriptor
 */
void
desc_free (DESC_OBJ * obj)
{
  int i;
  if (obj == NULL)
    {
      return;
    }

  if (obj->dbvalue_buf_ptr != NULL)
    {
      for (i = 0; i < obj->count; i++)
    {
      if (obj->dbvalue_buf_ptr[i].buf != NULL)
        {
          free (obj->dbvalue_buf_ptr[i].buf);
        }
    }
      free (obj->dbvalue_buf_ptr);
    }

  if (obj->count && obj->values != NULL)
    {
      for (i = 0; i < obj->count; i++)
    {
      pr_clear_value (&obj->values[i]);
    }
      free (obj->values);
    }
  if (obj->atts != NULL)
    {
      free (obj->atts);
    }

  free_and_init (obj);
}

/*
 * object_disk_size - Calculates the total number of bytes required for the
 * storage of an object as defined with an object descriptor structure.
 *    return: size in bytes
 *    obj(in): object descriptor
 */
static int
object_disk_size (DESC_OBJ * obj, int *offset_size_ptr)
{
  SM_ATTRIBUTE *att;
  SM_CLASS *class_;
  int a, i;
  int size;
  *offset_size_ptr = OR_BYTE_SIZE;
  class_ = obj->class_;
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];
      for (i = 0; i < obj->count; i++)
        {
          if (obj->atts[i] == att)
        {
          break;
        }
        }
      if (i < obj->count)
        {
          if (att->type->variable_p)
        {
          size += pr_data_writeval_disk_size (&obj->values[i]);
        }
          else
        {
          size += tp_domain_disk_size (att->domain);
        }
        }
      else
        {
          if (att->type->variable_p)
        {
          if (!DB_IS_NULL (&att->default_value.value))
            {
              size += pr_data_writeval_disk_size (&att->default_value.value);
            }
        }
          else
        {
          size += tp_domain_disk_size (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_varinfo - Writes the variable offset table for an object defined by
 * an object descriptor structure.
 *    return: void
 *    buf(out): transformer buffer
 *    obj(in): object
 */
static void
put_varinfo (OR_BUF * buf, DESC_OBJ * obj, int offset_size)
{
  SM_ATTRIBUTE *att;
  SM_CLASS *class_;
  int a, offset, len, i;
  class_ = obj->class_;
  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];
      for (i = 0; i < obj->count; i++)
        {
          if (obj->atts[i] == att)
        {
          break;
        }
        }
      len = 0;
      if (i < obj->count)
        {
          if (att->type->variable_p)
        {
          len = pr_data_writeval_disk_size (&obj->values[i]);
        }
          else
        {
          len = tp_domain_disk_size (att->domain);
        }
        }
      else
        {
          if (att->type->variable_p)
        {
          if (!DB_IS_NULL (&att->default_value.value))
            {
              len = pr_data_writeval_disk_size (&att->default_value.value);
            }
        }
          else
        {
          len = tp_domain_disk_size (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);
    }
}

/*
 * put_attributes - Writes the attribute values for an object defined by
 * an object descriptor structure.
 *    return: void
 *    buf(out): transformer buffer
 *    obj(in): object descriptor
 */
static void
put_attributes (OR_BUF * buf, DESC_OBJ * obj)
{
  SM_ATTRIBUTE *att;
  int i, bsize, pad;
  char *bits, *start;
  /* allocate bound bit array */
  bits = NULL;
  bsize = OR_BOUND_BIT_BYTES (obj->class_->fixed_count);
  if (bsize)
    {
      bits = (char *) malloc (bsize);
      if (bits == NULL)
    {
      goto error;
    }
      else
    {
      memset (bits, 0, bsize);
    }
    }

  /*
   * Write fixed attribute values, if unbound, leave zero or garbage
   * it doesn't matter, if the attribute is bound, set the appropriate
   * bit in the bound bit array
   */
  start = buf->ptr;
  for (att = obj->class_->attributes; att != NULL && !att->type->variable_p; att = (SM_ATTRIBUTE *) att->header.next)
    {
      for (i = 0; i < obj->count && obj->atts[i] != att; i++);
      if (i < obj->count)
    {
      if (DB_IS_NULL (&obj->values[i]))
        {
          or_pad (buf, tp_domain_disk_size (att->domain));
        }
      else
        {
          pr_data_writeval (buf, &obj->values[i]);
          if (bits != NULL)
        {
          OR_ENABLE_BOUND_BIT (bits, att->storage_order);
        }
        }
    }
      else
    {
      /* no value, use default if one exists */
      if (DB_IS_NULL (&att->default_value.value))
        {
          or_pad (buf, tp_domain_disk_size (att->domain));
        }
      else
        {
          pr_data_writeval (buf, &att->default_value.value);
          if (bits != NULL)
        {
          OR_ENABLE_BOUND_BIT (bits, att->storage_order);
        }
        }
    }
    }

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

  /* now write the bound bits if we have any */
  if (bits != NULL)
    {
      or_put_data (buf, bits, bsize);
      /*
       * We do not need the bits array anymore, lets free it now.
       * the pr_data_writeval() function can perform a longjmp()
       * back to the calling function if we get an overflow,
       * and bits will not be freed.
       */
      free_and_init (bits);
    }

  /* finaly do the variable width attributes */
  for (; att != NULL; att = (SM_ATTRIBUTE *) att->header.next)
    {
      for (i = 0; i < obj->count && obj->atts[i] != att; i++);
      if (i < obj->count)
    {
      if (!DB_IS_NULL (&obj->values[i]))
        {
          pr_data_writeval (buf, &obj->values[i]);
        }
    }
      else
    {
      if (!DB_IS_NULL (&att->default_value.value))
        {
          pr_data_writeval (buf, &att->default_value.value);
        }
    }
    }

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

/*
 * desc_obj_to_disk - transforms the object into a disk record for eventual
 * storage.
 *    return: size in bytes (negative if buffer overflow)
 *    obj(in): object descriptor
 *    record(out): disk record
 *    index_flag(in): set to non-zero if object has indexed attributes
 * Note:
 *    This is functionally similar to tf_mem_to_disk except that
 *    the object is defined using an object descriptor rather than
 *    a workspace object.
 *    If it returns a number less than zero, there was not enough room
 *    in the buffer and the transformation was aborted.
 */
int
desc_obj_to_disk (DESC_OBJ * obj, RECDES * record, bool * index_flag)
{
  OR_BUF orep, *buf;
  int error, status;
  bool has_index = false;
  unsigned int repid_bits;
  int expected_disk_size;
  int offset_size;
  buf = &orep;
  or_init (buf, record->data, record->area_size);

  /* expected disk size perfectly fits the size of the object , no need to check overflow */
  expected_disk_size = object_disk_size (obj, &offset_size);
  if (record->area_size < (expected_disk_size + (OR_MVCC_MAX_HEADER_SIZE - OR_MVCC_INSERT_HEADER_SIZE)))
    {
      record->length = -expected_disk_size;
      *index_flag = false;
      return (1);
    }

  if (OID_ISTEMP (WS_OID (obj->classop)))
    {
      fprintf (stderr, msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_MIGDB, MIGDB_MSG_TEMPORARY_CLASS_OID));
      return (1);
    }

  /* header */
  repid_bits = obj->class_->repid;
  if (obj->class_->fixed_count)
    {
      repid_bits |= OR_BOUND_BIT_FLAG;
    }

  /* offset size */
  OR_SET_VAR_OFFSET_SIZE (repid_bits, offset_size);
  repid_bits |= (OR_MVCC_FLAG_VALID_INSID << OR_MVCC_FLAG_SHIFT_BITS);
  or_put_int (buf, repid_bits);
  or_put_int (buf, 0);      /* CHN, fixed size */
  or_put_bigint (buf, MVCCID_NULL); /* MVCC insert id */
  /* variable info block */
  put_varinfo (buf, obj, offset_size);
  /* attributes, fixed followed by bound bits, followed by variable */
  put_attributes (buf, obj);
  record->length = (int) (buf->ptr - buf->buffer);
  /* see if there are any indexes */
  has_index = classobj_class_has_indexes (obj->class_);

  if (buf->ptr > buf->endptr)
    {
      assert (false);       /* impossible case */
      /*
       * error, currently can only be from buffer overflow
       * might be nice to store the "size guess" from the class
       * SHOULD BE USING TF_STATUS LIKE tf_mem_to_disk, need to
       * merge these two programs !
       */
      record->length = -expected_disk_size;
      *index_flag = false;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_BUFFER_UNDERFLOW, 0);
      return (1);
    }

  *index_flag = has_index;
  return NO_ERROR;
}

/*
 * get_desc_current - reads the disk representation of an object and constructs
 * an object descriptor.
 *    return: void
 *    buf(in/out): transformer buffer
 *    class(in): class structure
 *    obj(out): object descriptor
 *    bound_bit_flag(in): non-zero if we're using bound bits
 * Note:
 *    The most current representation of the class is expected.
 */
static void
get_desc_current (OR_BUF * buf, SM_CLASS * class_, DESC_OBJ * obj, int bound_bit_flag, int offset_size,
          bool is_unloaddb)
{
  SM_ATTRIBUTE *att;
  int *vars = NULL;
  int i, j, offset, offset2, pad;
  char *bits, *start;
  int rc = NO_ERROR;
  bool do_copy = is_unloaddb ? false : true;
  int zvar[32];

  /* need nicer way to store these */
  if (class_->variable_count)
    {
      if (class_->variable_count <= DIM (zvar))
    {
      vars = zvar;
    }
      else
    {
      vars = (int *) malloc (sizeof (int) * class_->variable_count);
      if (vars == NULL)
        {
          return;
        }
    }
      /* 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);
    }

  bits = NULL;
  if (bound_bit_flag)
    {
      /* assume that the buffer is in contiguous memory and that we can seek ahead to the bound bits.  */
      bits = (char *) buf->ptr + obj->class_->fixed_size;
    }

  att = class_->attributes;
  start = buf->ptr;
  for (i = 0; i < class_->fixed_count; i++, att = (SM_ATTRIBUTE *) att->header.next)
    {
      if (bits != NULL && !OR_GET_BOUND_BIT (bits, i))
    {
      /* its a NULL value, skip it */
      db_value_put_null (&obj->values[i]);
      or_advance (buf, tp_domain_disk_size (att->domain));
    }
      else
    {
      /* read the disk value into the db_value */
      att->type->data_readval (buf, &obj->values[i], att->domain, -1, do_copy, NULL, 0);
    }
    }

  /* round up to a to the end of the fixed block */
  pad = (int) (buf->ptr - start);
  if (pad < obj->class_->fixed_size)
    {
      or_advance (buf, obj->class_->fixed_size - pad);
    }

  /* skip over the bound bits */
  if (bound_bit_flag)
    {
      or_advance (buf, OR_BOUND_BIT_BYTES (obj->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 = (SM_ATTRIBUTE *) att->header.next)
    {
#if (MAJOR_VERSION >= 11) || (MAJOR_VERSION == 10 && MINOR_VERSION >= 1)
      if (is_unloaddb && obj->dbvalue_buf_ptr && att->type->get_id () == DB_TYPE_VARCHAR)
        {
          data_readval_string (buf, &obj->values[i], att->domain, vars[j], false, obj->dbvalue_buf_ptr[i].buf,
                   obj->dbvalue_buf_ptr[i].buf_size);
        }
      else
#endif
        {
          att->type->data_readval (buf, &obj->values[i], att->domain, vars[j], do_copy, NULL, 0);
        }
    }

      if (vars != zvar)
    {
      free (vars);
    }
    }
}

/*
 * find_current_attribute - locates an attribute definition in a class.
 *    return: attribute structure
 *    class(in): class structure
 *    id(in): attribute id
 */
static SM_ATTRIBUTE *
find_current_attribute (SM_CLASS * class_, int id)
{
  SM_ATTRIBUTE *att;
  for (att = class_->attributes; att != NULL; att = (SM_ATTRIBUTE *) att->header.next)
    {
      if (att->id == id)
    {
      return att;
    }
    }
  return NULL;
}

/*
 * get_desc_old - loads the disk representation of an object into an object
 * descriptor structure.
 *    return: void
 *    buf(in/out): transformer buffer
 *    class(in): class strcuture
 *    repid(in): repid of the stored instance
 *    obj(out): object descriptor being built
 *    bound_bit_flag(in): non-zero if using bound bits
 * Note:
 *    This loads the disk representation of an object into an object
 *    descriptor structure.  This function will handle the transformation
 *    of an obsolete representation of the object into its
 *    newest representation.
 */
static void
get_desc_old (OR_BUF * buf, SM_CLASS * class_, int repid, DESC_OBJ * obj, int bound_bit_flag, int offset_size,
          bool is_unloaddb)
{
  SM_REPRESENTATION *oldrep;
  SM_REPR_ATTRIBUTE *rat, *found;
  SM_ATTRIBUTE *att;
  const PR_TYPE *type;
  int *vars = NULL;
  int i, offset, offset2, total, bytes, att_index, padded_size, fixed_size;
  SM_ATTRIBUTE **attmap = NULL;
  char *bits, *start;
  int rc = NO_ERROR;
  int storage_order;
  bool do_copy = is_unloaddb ? false : true;
  int zvar[32];

  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_));
      return;
    }

  if (oldrep->variable_count)
    {
      /* need nicer way to store these */
      if (class_->variable_count <= DIM (zvar))
    {
      vars = zvar;
    }
      else
    {
      vars = (int *) malloc (sizeof (int) * oldrep->variable_count);
      if (vars == NULL)
        {
          goto abort_on_error;
        }
    }
      /* compute the variable 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 = (SM_ATTRIBUTE **) malloc (sizeof (SM_ATTRIBUTE *) * total);
  if (attmap == NULL)
    {
      goto abort_on_error;
    }

  memset (attmap, 0, sizeof (SM_ATTRIBUTE *) * total);
  for (rat = oldrep->attributes, i = 0; rat != NULL; rat = rat->next, i++)
    attmap[i] = find_current_attribute (class_, rat->attid);
  rat = oldrep->attributes;
  /* fixed */
  start = buf->ptr;
  for (i = 0; i < oldrep->fixed_count && rat != NULL; i++, rat = rat->next)
    {
      type = pr_type_from_id (rat->typeid_);
      if (type == NULL)
    {
      goto abort_on_error;
    }

      if (attmap[i] == NULL)
    {
      /* its gone, skip over it */
      type->data_readval (buf, NULL, rat->domain, -1, do_copy, NULL, 0);
    }
      else
    {
      /* its real, get it into the proper value */
      type->data_readval (buf, &obj->values[attmap[i]->storage_order], rat->domain, -1, do_copy, NULL, 0);
    }
    }

  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);
      if ((buf->ptr + bytes) > buf->endptr)
    {
      goto abort_on_error;
    }

      rat = oldrep->attributes;
      for (i = 0; i < oldrep->fixed_count && rat != NULL; i++, rat = rat->next)
    {
      if (attmap[i] != NULL)
        {
          if (!OR_GET_BOUND_BIT (bits, i))
        {
          DB_VALUE *v = &obj->values[attmap[i]->storage_order];
          db_value_clear (v);
          db_value_put_null (v);
        }
        }
    }
      or_advance (buf, bytes);
    }

  /* variable */
  for (i = 0; i < oldrep->variable_count && rat != NULL; i++, rat = rat->next)
    {
      type = pr_type_from_id (rat->typeid_);
      if (type == NULL)
    {
      goto abort_on_error;
    }

      att_index = i + oldrep->fixed_count;
      if (attmap[att_index] == NULL)
    {
      /* its null, skip over it */
      type->data_readval (buf, NULL, rat->domain, vars[i], do_copy, NULL, 0);
    }
      else
    {
      /* read it into the proper value */
      storage_order = attmap[att_index]->storage_order;
#if (MAJOR_VERSION >= 11) || (MAJOR_VERSION == 10 && MINOR_VERSION >= 1)
      if (is_unloaddb && obj->dbvalue_buf_ptr && type->get_id () == DB_TYPE_VARCHAR)
        {
          data_readval_string (buf, &obj->values[storage_order], rat->domain, vars[i], false,
                   obj->dbvalue_buf_ptr[storage_order].buf,
                   obj->dbvalue_buf_ptr[storage_order].buf_size);
        }
      else
#endif
        {
          type->data_readval (buf, &obj->values[storage_order], rat->domain, vars[i], do_copy, NULL, 0);
        }
    }
    }

  /*
   * initialize new values
   */
  for (i = 0, att = class_->attributes; att != NULL; i++, 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)
    {
      /*
       * formerly used copy_value which converted MOP values to OID
       * values, is this really necessary ?
       */
      pr_clone_value (&att->default_value.original_value, &obj->values[i]);
    }
    }

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

  if (vars && vars != zvar)
    {
      free (vars);
    }

  obj->updated_flag = 1;
  return;
abort_on_error:
  if (attmap != NULL)
    {
      free (attmap);
    }

  if (vars && vars != zvar)
    {
      free (vars);
    }
}

/*
 * desc_disk_to_obj - similar to tf_disk_to_mem except that it builds an
 * object descriptor structure rather than a workspace object.
 *    return: NO_ERROR if successful, error code otherwise
 *    classop(in): class MOP
 *    class(in): class structure
 *    record(in): disk record
 *    obj(out): object descriptor
 */
int
desc_disk_to_obj (MOP classop, SM_CLASS * class_, RECDES * record, DESC_OBJ * obj, bool is_unloaddb)
{
  OR_BUF orep, *buf;
  int repid;
  unsigned int repid_bits;
  int bound_bit_flag;
  int save;
  int i;
  int rc = NO_ERROR;
  int offset_size;
  if (obj == NULL)
    {
      return ER_FAILED;
    }
  else
    {
      /* clear previous values */
      for (i = 0; i < obj->count; i++)
    {
      pr_clear_value (&obj->values[i]);
    }
    }

  /* Kludge, make sure we don't upgrade objects to OID'd during the reading */
  save = pr_Inhibit_oid_promotion;
  pr_Inhibit_oid_promotion = 1;
  buf = &orep;
  or_init (buf, record->data, record->length);
  obj->classop = classop;
  char mvcc_flags;
  /* 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);

  i = OR_INT_SIZE;      /* skip chn */
  if (mvcc_flags & OR_MVCC_FLAG_VALID_INSID)
    {
      i += OR_MVCCID_SIZE;  /* skip insert id */
    }
  if (mvcc_flags & OR_MVCC_FLAG_VALID_DELID)
    {
      i += OR_MVCCID_SIZE;  /* skip delete id */
    }
  if (mvcc_flags & OR_MVCC_FLAG_VALID_PREV_VERSION)
    {
      i += OR_MVCC_PREV_VERSION_LSA_SIZE;   /* skip prev version lsa */
    }

  or_advance (buf, i);

  bound_bit_flag = repid_bits & OR_BOUND_BIT_FLAG;
  if (repid == class_->repid)
    {
      get_desc_current (buf, class_, obj, bound_bit_flag, offset_size, is_unloaddb);
    }
  else
    {
      get_desc_old (buf, class_, repid, obj, bound_bit_flag, offset_size, is_unloaddb);
    }

  pr_Inhibit_oid_promotion = save;
  if (er_errid () != NO_ERROR)
    {
      return er_errid ();
    }


  if (buf->ptr > buf->endptr)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_BUFFER_UNDERFLOW, 0);
      return ER_TF_BUFFER_UNDERFLOW;
    }

  return NO_ERROR;
}

static bool filter_ignore_errors[-ER_LAST_ERROR] = {
  false,
};

static bool filter_ignore_init = false;
/*
 * init_load_err_filter - init error filter array
 *    return: void
 */
static void
init_load_err_filter (void)
{
  int ifrom, ito;
  memset (filter_ignore_errors, false, sizeof (filter_ignore_errors));
  filter_ignore_errors[0] = true;   /* ER_WARNING_SEVERITY */
  for (ifrom = -ER_DISK_ALMOST_OUT_OF_SPACE, ito = -ER_DISK_TEMP_LAST_ALMOST_OUT_OF_SPACE; ifrom <= ito; ifrom++)
    {
      filter_ignore_errors[ifrom] = true;
    }
  filter_ignore_errors[-ER_BO_NOTIFY_AUTO_VOLEXT] = true;
  filter_ignore_errors[-ER_LOG_MAX_ARCHIVES_HAS_BEEN_EXCEEDED] = true;
  filter_ignore_init = true;
}

/*
 * default_clear_err_filter - clear error filter array
 *    return: void
 */
static void
default_clear_err_filter (void)
{
  int ifrom, ito;
  filter_ignore_errors[0] = false;  /* ER_WARNING_SEVERITY */
  for (ifrom = -ER_DISK_ALMOST_OUT_OF_SPACE, ito = -ER_DISK_TEMP_LAST_ALMOST_OUT_OF_SPACE; ifrom <= ito; ifrom++)
    {
      filter_ignore_errors[ifrom] = false;
    }
  filter_ignore_errors[-ER_BO_NOTIFY_AUTO_VOLEXT] = false;
}

/*
 * init_load_err_filter - loads error codes from an external file.
 *    return: number of error filter set
 */
int
er_filter_fileset (FILE * ef)
{
  int i, num_ignore_error_list;
  int set_count = 0, errcode;
  char rdbuf[32];
  int ignore_error_list[-ER_LAST_ERROR];
  if (ef == NULL)
    {
      return set_count;
    }
  if (!filter_ignore_init)
    {
      init_load_err_filter ();
    }
  memset (rdbuf, 0, sizeof (rdbuf));
  while (feof (ef) == 0)
    {
      if (fgets (rdbuf, 30, ef) == NULL)
    {
      break;
    }
      if (rdbuf[0] == '#')
    {
      continue;
    }
      if (rdbuf[0] != '-' && rdbuf[0] != '+')
    {
      continue;
    }
      if (strncmp (rdbuf, "+DEFAULT", 8) == 0)
    {
      default_clear_err_filter ();
    }
      else
    {
      errcode = atoi (&rdbuf[1]);
      if (errcode <= 0 || errcode >= -ER_LAST_ERROR)
        {
          continue;
        }
      if (rdbuf[0] == '-')
        {
          filter_ignore_errors[errcode] = true;
        }
      else
        {
          filter_ignore_errors[errcode] = false;
        }
      set_count++;
    }
    }

  /* set ws_ignore_error_list */
  for (i = 1, num_ignore_error_list = 0; i < -ER_LAST_ERROR; i++)
    {
      if (filter_ignore_errors[i] == true)
    {
      ignore_error_list[num_ignore_error_list] = -i;
      num_ignore_error_list++;
    }
    }

  if (num_ignore_error_list > 0)
    {
      ws_set_ignore_error_list_for_mflush (num_ignore_error_list, ignore_error_list);
    }

  return set_count;
}

// TODO: this is specific to loader. I don't think it belongs here.
/*
 * er_filter_errid - check for ignorable errid
 *    return: NO_ERROR if ignorable errid. otherwise, error-code clear
 *    ignorable errid
 *    ignore_warning(in): true to filter out warnings. otherwise, false
 */
int
er_filter_errid (bool ignore_warning)
{
  int errcode = er_errid (), erridx;
  if (errcode == NO_ERROR)
    {               /* don't have to check */
      return NO_ERROR;
    }

  if (!filter_ignore_init)
    {               /* need to init */
      init_load_err_filter ();
    }

  erridx = (errcode < 0) ? -errcode : errcode;
  if (filter_ignore_errors[erridx])
    {               /* ignore error if ignorable */
      goto clear_errid;
    }

  if (filter_ignore_errors[0])
    {
      if (ignore_warning && er_get_severity () == ER_WARNING_SEVERITY)
    {
      goto clear_errid;
    }
    }

exit_on_end:
  return errcode;
clear_errid:
  if (errcode != NO_ERROR)
    {               /* clear ignorable errid */
      er_clearid ();
      errcode = NO_ERROR;
    }
  goto exit_on_end;
}

/* *INDENT-OFF* */
void
get_ignored_errors (std::vector<int> &vec)
{
  for (int i = 0; i < -ER_LAST_ERROR; i++)
    {
      if (filter_ignore_errors[i])
    {
      vec.push_back (-i);
    }
    }
}
/* *INDENT-ON* */