Skip to content

File object_representation.c

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

Go to the documentation of this file

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

/*
 * object_representation.c: Low level functions that manipulate
 *                          the disk representation of
 */

#ident "$Id$"

#include "config.h"

#include <string.h>
#if defined (WINDOWS)
#include <winsock2.h>
#endif /* WINDOWS */
#include <setjmp.h>
#include <assert.h>

#include "db_json.hpp"
#include "dbtype.h"
#include "error_manager.h"
#include "file_io.h"
#include "log_lsa.hpp"

#include "compressor.hpp"
#include "object_primitive.h"
#include "object_representation.h"
#include "oid.h"
#include "porting_inline.hpp"
#include "query_list.h"
#include "set_object.h"
#include "access_spec.hpp"
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"

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

/*
 * Lookup to compute the MVCC header size faster:
 *    INDEX     MVCC FLAGS                        SIZE
 *      0        NO FLAGS                       REP_SIZE + CHN_SIZE
 *  1         INSID                 REP_SIZE + CHN_SIZE + MVCCID_SIZE
 *  2         DELID                 REP_SIZE + CHN_SIZE + MVCCID_SIZE
 *  3      INSID | DELID                REP_SIZE + CHN_SIZE + MVCCID_SIZE + MVCCID_SIZE
 *  4       PREV_VERSION                REP_SIZE + CHN_SIZE + PREV_VERSION_LSA_SIZE
 *  5     INSID | PREV_VERSION          REP_SIZE + CHN_SIZE + MVCCID_SIZE + PREV_VERSION_LSA_SIZE
 *      6     DELID | PREV_VERSION          REP_SIZE + CHN_SIZE + MVCCID_SIZE + PREV_VERSION_LSA_SIZE
 *      7   INSID | DELID | PREV_VERSION     REP_SIZE + CHN_SIZE + MVCCID_SIZE + MVCCID_SIZE + PREV_VERSION_LSA_SIZE
 *
 * Note:  In case that the heap record header modifies, mvcc_header_size_lookup must be updated.
 */
int mvcc_header_size_lookup[8] = {
  OR_MVCC_REP_SIZE + OR_CHN_SIZE,
  OR_MVCC_REP_SIZE + OR_CHN_SIZE + OR_MVCCID_SIZE,
  OR_MVCC_REP_SIZE + OR_CHN_SIZE + OR_MVCCID_SIZE,
  OR_MVCC_REP_SIZE + OR_CHN_SIZE + OR_MVCCID_SIZE + OR_MVCCID_SIZE,
  OR_MVCC_REP_SIZE + OR_CHN_SIZE + OR_MVCC_PREV_VERSION_LSA_SIZE,
  OR_MVCC_REP_SIZE + OR_CHN_SIZE + OR_MVCCID_SIZE + OR_MVCC_PREV_VERSION_LSA_SIZE,
  OR_MVCC_REP_SIZE + OR_CHN_SIZE + OR_MVCCID_SIZE + OR_MVCC_PREV_VERSION_LSA_SIZE,
  OR_MVCC_REP_SIZE + OR_CHN_SIZE + OR_MVCCID_SIZE + OR_MVCCID_SIZE + OR_MVCC_PREV_VERSION_LSA_SIZE
};

static TP_DOMAIN *unpack_domain (OR_BUF * buf, int *is_null);
#if defined(ENABLE_UNUSED_FUNCTION)
static char *unpack_str_array (char *buffer, char ***string_array, int count);
#endif
static int or_put_varbit_internal (OR_BUF * buf, const char *string, int bitlen, int align);
static int or_put_varchar_internal (OR_BUF * buf, char *string, int charlen, int align);
static int or_packed_json_schema_length (const char *json_schema);
static int or_packed_json_validator_length (JSON_VALIDATOR * json_validator);
static char *or_unpack_var_table_internal (char *ptr, int nvars, OR_VARINFO * vars, int offset_size);

/*
 * classobj_get_prop - searches a property list for a value with the given name
 *    return: index of the found property, 0 iff not found
 *    properties(in): property sequence
 *    name(in): property name
 *    pvalue(out): value container for property value
 *
 * Note:
 * Remember to clear the value with pr_clear_value or equivalent.
 */
int
classobj_get_prop (DB_SEQ * properties, const char *name, DB_VALUE * pvalue)
{
  int error;
  int found, max, i;
  DB_VALUE value;
  const char *tmp_str;

  error = NO_ERROR;
  found = 0;

  if (properties == NULL || name == NULL || pvalue == NULL)
    {
      goto error;
    }

  max = set_size (properties);

  for (i = 0; i < max && !found && error == NO_ERROR; i += 2)
    {
      error = set_get_element (properties, i, &value);
      if (error != NO_ERROR)
    {
      continue;
    }

      if (DB_VALUE_TYPE (&value) != DB_TYPE_STRING || db_get_string (&value) == NULL)
    {
      error = ER_SM_INVALID_PROPERTY;
    }
      else
    {
      tmp_str = db_get_string (&value);
      if (tmp_str && strcmp (name, tmp_str) == 0)
        {
          if ((i + 1) >= max)
        {
          error = ER_SM_INVALID_PROPERTY;
        }
          else
        {
          error = set_get_element (properties, i + 1, pvalue);
          if (error == NO_ERROR)
            {
              found = i + 1;
            }
        }
        }
    }
      pr_clear_value (&value);
    }

error:
  if (error)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }

  return found;
}

/*
 * classobj_decompose_property_oid - parse oid string from buffer
 *    return: zero if decompose error, 3 if successful
 *    buffer(in): buffer that contains oid string
 *    volid(out): volumn id
 *    fileid(out): file id
 *    pageid(out): pageid
 *
 */
int
classobj_decompose_property_oid (const char *buffer, int *volid, int *fileid, int *pageid)
{
  char *ptr;
  int result = 0;
  int val;

  if (buffer == NULL)
    {
      return 0;
    }

  result = str_to_int32 (&val, &ptr, buffer, 10);
  *volid = val;
  if (result != 0)
    {
      return 0;
    }
  buffer = ptr + 1;

  result = str_to_int32 (&val, &ptr, buffer, 10);
  *fileid = val;
  if (result != 0)
    {
      return 0;
    }
  buffer = ptr + 1;

  result = str_to_int32 (&val, &ptr, buffer, 10);
  *pageid = val;
  if (result != 0)
    {
      return 0;
    }

  return 3;
}

/*
 * RECDES DECODING FUNCTIONS
 */

/*
 * These are called primarily by the locator to get information
 * about the disk representation of an object stored in a disk
 * record descirptor (RECDES).
 */

/*
 * or_class_name - This is used to extract the class name from the disk
 * representation of a class object
 *    return: class name string pointer inside recode data
 *    record(in): disk record
 *
 * Note:
 *    To avoid a lot of dependencies with the schema code, we make the
 *    assumption that the name field is always maintained as the first
 *    variable attribute of class objects.
 *    This will also be true for the root class.
 *
 *    [PORTABILITY]
 *    This simply returns a pointer into the middle of the record.  We may
 *    need to copy the string out of the record for some architectures
 *    if there are weird alignment or character set problems.
 */
char *
or_class_name (RECDES * record)
{
  char *start, *name;
  int offset, len;

  /*
   * the first variable attribute for both classes and the rootclass
   * is the name - if this ever changes, we could check the class
   * OID which should be NULL for the root class and special case
   * from there
   */

  offset = OR_VAR_OFFSET (record->data, 0);
  start = &record->data[offset];

  /*
   * kludge kludge kludge
   * This is now an encoded "varchar" string, we need to skip over the length
   * before returning it.  Note that this also depends on the stored string
   * being NULL terminated.  This interface should be returning either a copy
   * or performing an extraction into a user supplied buffer !
   * Knowledge of the format of packed varchars should be in a different
   * or_ function.
   */
  len = (int) *((unsigned char *) start);
  if (len != 0xFF)
    {
      name = start + 1;
    }
  else
    {
      name = start + 1 + OR_INT_SIZE;
    }

  return name;
}

/*
 * or_rep_id - Extracts the representation id from the disk representation of
 * an object.
 *    return: representation of id of object. or NULL_REPRID for error
 *    record(in): disk record
 */
int
or_rep_id (RECDES * record)
{
  int rep = NULL_REPRID;

  assert (record != NULL && record->data != NULL);

  if (record->length < OR_HEADER_SIZE (record->data))
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_BUFFER_UNDERFLOW, 0);
    }
  else
    {
      rep = OR_GET_MVCC_REPID (record->data);
    }

  return rep;
}

/*
 * or_set_rep_id () - set representation id for record
 * return : error code or NO_ERROR
 * record (in/out): record
 * repid (in)     : new representation
 *
 * Note: This function changes the representation id of a record. It should
 * only be used in update/insert operations on partitioned tables. Each
 * partition is viewed by the heap/btree operations as a different class
 * and it might have a different representation id than the one of the root
 * table. However, when partitioning a table, we guarantee that the actual
 * disk representation is the same as the one of the root table (even though
 * the actual id might differ). Do not use this function in another context!
 */
int
or_set_rep_id (RECDES * record, int repid)
{
  OR_BUF orep, *buf;
  unsigned int new_bits = 0;

  if (record->length < OR_HEADER_SIZE (record->data))
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_BUFFER_UNDERFLOW, 0);
      return ER_FAILED;
    }

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

  new_bits = OR_GET_MVCC_REPID_AND_FLAG (record->data);

  /* Remove old repid */
  new_bits &= ~OR_MVCC_REPID_MASK;

  /* Add new repid */
  new_bits |= (repid & OR_MVCC_REPID_MASK);

  /* Set buffer pointer to the right position */
  buf->ptr = buf->buffer + OR_REP_OFFSET;

  /* write new REPR_ID to the record */
  or_put_int (buf, new_bits);

  return NO_ERROR;
}

/*
 * or_chn - extracts cache coherency number from the disk representation of an
 * object
 *    return: cache coherency number (chn), or NULL_CHN for error
 *    record(in): disk record
 */
int
or_chn (RECDES * record)
{
  if (record->length < OR_CHN_OFFSET + OR_CHN_SIZE)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TF_BUFFER_UNDERFLOW, 0);
      return NULL_CHN;
    }

  return OR_GET_MVCC_CHN (record->data);
}

/*
 * or_replace_chn () -replace chn
 *
 * return      : error
 * record(in/out)  : record
 * chn(int): chn
 *
 *   NOTE: It determines the type of record based on flag in record not system
 *     parameter.
 */
int
or_replace_chn (RECDES * record, int chn)
{
  OR_BUF orep, *buf;
  int offset;
  int error;

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

  offset = OR_CHN_OFFSET;
  buf->ptr = buf->buffer + offset;

  error = or_put_int (buf, chn);

  return error;
}

/*
 * or_mvcc_get_repid_and_flags () - Gets MVCC representation id and flags.
 *
 * return      : MVCC flags.
 * buf (in/out) : or buffer
 * error(out): NO_ERROR or error code
 */
int
or_mvcc_get_repid_and_flags (OR_BUF * buf, int *error)
{
  ASSERT_ALIGN (buf->ptr, INT_ALIGNMENT);

  int repid_and_flag_bits = 0;
  assert (buf->ptr + OR_INT_SIZE <= buf->endptr);

  repid_and_flag_bits = OR_GET_INT (buf->ptr);
  buf->ptr += OR_INT_SIZE;
  *error = NO_ERROR;
  return repid_and_flag_bits;
}

/*
 * or_mvcc_set_repid_and_flags () - Set MVCC representation id and flags.
 *
 * return      : nothing
 * buf (in/out) : or buffer
 * bound_bit(in) : bound bit
 * variable_offset_size(in); variable offset size
 * error(out): NO_ERROR or error code
 */
int
or_mvcc_set_repid_and_flags (OR_BUF * buf, int mvcc_flag, int repid, int bound_bit, int variable_offset_size)
{
  int repid_and_flags;

  /* Add repid */
  repid_and_flags = repid;

  /* Set bound bit flag -> unchanged */
  if (bound_bit)
    {
      repid_and_flags |= OR_BOUND_BIT_FLAG;
    }
  OR_SET_VAR_OFFSET_SIZE (repid_and_flags, variable_offset_size);

  repid_and_flags |= (mvcc_flag & OR_MVCC_FLAG_MASK) << OR_MVCC_FLAG_SHIFT_BITS;

  return or_put_int (buf, repid_and_flags);
}

#if !defined (SERVER_MODE)
/*
 * BOUND BIT FUNCTIONS
 */
/*
 * These manipulate the bound-bit array which can be found in the headers
 * of objects and sets.
 *
 */

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * or_get_bound_bit - Extracts the bound bit for a particular element.
 *    return: bound bit (0 or 1) for element
 *    bound_bits(in): pointer to bound bits on disk
 *    element(in): index into the bound bit table
 */
int
or_get_bound_bit (char *bound_bits, int element)
{
  return (OR_GET_BOUND_BIT (bound_bits, element));
}

/*
 * or_put_bound_bit - his sets the value of a bound bit
 *    return: void
 *    bound_bits(out): pointer to bound bit array
 *    element(in): bound bit table index
 *    bound(in): value to set in the table
 *
 * Note:
 * It assumes that the bound bit table is in memory format.
 */
void
or_put_bound_bit (char *bound_bits, int element, int bound)
{
  int mask;
  char *byte, value;

  /* this could be a macro, but its getting pretty confusing */
  mask = 1 << (element & 7);
  byte = bound_bits + (element >> 3);
  value = *byte;

  if (bound)
    {
      *byte = value | mask;
    }
  else
    {
      *byte = value & ~mask;
    }
}
#endif /* ENABLE_UNUSED_FUNCTION */
#endif /* !SERVER_MODE */



/*
 * or_put_monetary - write a DB_MONETARY value to or buffer
 *    return: NO_ERROR or error code
 *    buf(in/out): or buffer
 *    monetary(in): pointer to DB_MONETARY value
 */
int
or_put_monetary (OR_BUF * buf, DB_MONETARY * monetary)
{
  int error;

  ASSERT_ALIGN (buf->ptr, INT_ALIGNMENT);

  /* check for valid currency type don't put default case in the switch!!! */
  error = ER_INVALID_CURRENCY_TYPE;
  switch (monetary->type)
    {
    case DB_CURRENCY_DOLLAR:
    case DB_CURRENCY_YEN:
    case DB_CURRENCY_WON:
    case DB_CURRENCY_TL:
    case DB_CURRENCY_BRITISH_POUND:
    case DB_CURRENCY_CAMBODIAN_RIEL:
    case DB_CURRENCY_CHINESE_RENMINBI:
    case DB_CURRENCY_INDIAN_RUPEE:
    case DB_CURRENCY_RUSSIAN_RUBLE:
    case DB_CURRENCY_AUSTRALIAN_DOLLAR:
    case DB_CURRENCY_CANADIAN_DOLLAR:
    case DB_CURRENCY_BRASILIAN_REAL:
    case DB_CURRENCY_ROMANIAN_LEU:
    case DB_CURRENCY_EURO:
    case DB_CURRENCY_SWISS_FRANC:
    case DB_CURRENCY_DANISH_KRONE:
    case DB_CURRENCY_NORWEGIAN_KRONE:
    case DB_CURRENCY_BULGARIAN_LEV:
    case DB_CURRENCY_VIETNAMESE_DONG:
    case DB_CURRENCY_CZECH_KORUNA:
    case DB_CURRENCY_POLISH_ZLOTY:
    case DB_CURRENCY_SWEDISH_KRONA:
    case DB_CURRENCY_CROATIAN_KUNA:
    case DB_CURRENCY_SERBIAN_DINAR:
      error = NO_ERROR;     /* it's a type we expect */
      break;
    default:
      break;
    }

  if (error != NO_ERROR)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 1, monetary->type);
      return error;
    }

  assert (buf->ptr + OR_MONETARY_SIZE <= buf->endptr);
  OR_PUT_MONETARY (buf->ptr, monetary);
  buf->ptr += OR_MONETARY_SIZE;

  return error;
}

/*
 * or_get_monetary - read a DB_MONETARY from or buffer
 *    return: NO_ERROR or error code
 *    buf(in/out): or buffer
 *    monetary(out): pointer to DB_MONETARY value
 */
int
or_get_monetary (OR_BUF * buf, DB_MONETARY * monetary)
{
  ASSERT_ALIGN (buf->ptr, INT_ALIGNMENT);

  assert (buf->ptr + OR_MONETARY_SIZE <= buf->endptr);
  OR_GET_MONETARY (buf->ptr, monetary);
  buf->ptr += OR_MONETARY_SIZE;
  return NO_ERROR;
}

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * or_put_binary - Writes a binary array into the translation buffer.
 *    return: NO_ERROR or error code
 *    buf(in/out): or buffer
 *    binary(in): binary data
 *
 * Note:
 *    The length of the array is part of the binary data descriptor.
 *    This is similar to or_put_string in that it also must pad out the
 *    binary data to be on a word boundary.
 */
int
or_put_binary (OR_BUF * buf, DB_BINARY * binary)
{
  int header, len, bits, pad;
  int rc = NO_ERROR;

  if (binary != NULL && binary->length < OR_BINARY_MAX_LENGTH)
    {
      len = binary->length + OR_INT_SIZE;
      pad = 0;
      bits = len & 3;
      if (bits)
    {
      pad = 4 - bits;
    }
      header = binary->length | (pad << OR_BINARY_PAD_SHIFT);
      rc = or_put_int (buf, header);
      if (rc == NO_ERROR)
    {
      rc = or_put_data (buf, (char *) binary->data, binary->length);
      if (rc == NO_ERROR)
        {
          rc = or_pad (buf, pad);
        }
    }
    }
  return rc;
}
#endif /* ENABLE_UNUSED_FUNCTION */

/*
 * or_put_varbit - put varbit into or buffer
 *    return: NO_ERROR or error code
 *    buf(in/out): or buffer
 *    string(in): string contains varbit value
 *    bitlen(in): length of varbit
 */
extern int
or_put_varbit (OR_BUF * buf, const char *string, int bitlen)
{
  return or_put_varbit_internal (buf, string, bitlen, CHAR_ALIGNMENT);
}

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * or_get_varbit - get varbit from or buffer
 *    return: NO_ERROR or error code
 *    buf(in/out): or buffer
 *    length_ptr(out): length of varbit read
 */
char *
or_get_varbit (OR_BUF * buf, int *length_ptr)
{
  int bitlen, charlen;
  char *new_ = NULL;
  int rc = NO_ERROR;

  bitlen = or_get_varbit_length (buf, &rc);

  if (rc != NO_ERROR)
    {
      return NULL;
    }

  /* Allocate storage for the string including the kludge NULL terminator */
  charlen = BITS_TO_BYTES (bitlen);
  new_ = db_private_alloc (NULL, charlen + 1);

  if (new_ == NULL)
    {
      return NULL;
    }
  rc = or_get_data (buf, new_, charlen);

  if (rc == NO_ERROR)
    {
      /* return the length */
      if (length_ptr != NULL)
    {
      *length_ptr = bitlen;
    }

      /* round up to a word boundary */
      rc = or_get_align32 (buf);
    }
  if (rc != NO_ERROR)
    {
      if (new_)
    {
      db_private_free_and_init (NULL, new_);
    }
      return NULL;
    }

  return new_;
}
#endif /* ENABLE_UNUSED_FUNCTION */

/*
 * or_put_varchar - put varchar to or buffer
 *    return: NO_ERROR or error code
 *    buf(in/out): or buffer
 *    string(in): string to put into the or buffer
 *    charlen(in): string length
 */
int
or_put_varchar (OR_BUF * buf, char *string, int charlen)
{
  return or_put_varchar_internal (buf, string, charlen, CHAR_ALIGNMENT);
}

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * or_get_varchar - get varchar from or buffer
 *    return: varchar pointer read from the or buffer. or NULL for error
 *    buf(in/out): or buffer
 *    length_ptr(out): length of returned string
 */
char *
or_get_varchar (OR_BUF * buf, int *length_ptr)
{
  int rc = NO_ERROR;
  int charlen;
  char *new_;

  charlen = or_get_varchar_length (buf, &rc);

  if (rc != NO_ERROR)
    {
      return NULL;
    }

  /* Allocate storage for the string including the kludge NULL terminator */
  new_ = db_private_alloc (NULL, charlen + 1);

  if (new_ == NULL)
    {
      return NULL;
    }
  rc = or_get_data (buf, new_, charlen + 1);

  if (rc == NO_ERROR)
    {

      /* return the length */
      if (length_ptr != NULL)
    {
      *length_ptr = charlen;
    }

      /* round up to a word boundary */
      rc = or_get_align32 (buf);
    }
  if (rc != NO_ERROR)
    {
      db_private_free_and_init (NULL, new_);
      return NULL;
    }
  else
    {
      return new_;
    }
}
#endif /* ENABLE_UNUSED_FUNCTION */

static int
or_put_varbit_internal (OR_BUF * buf, const char *string, int bitlen, int align)
{
  int net_bitlen;
  int bytelen;

  bytelen = BITS_TO_BYTES (bitlen);

  if (bitlen < 0xFF)
    {
      assert (buf->ptr + OR_BYTE_SIZE <= buf->endptr);
      or_put_byte (buf, bitlen);
    }
  else
    {
      assert (buf->ptr + OR_BYTE_SIZE + OR_INT_SIZE <= buf->endptr);
      or_put_byte (buf, 0xFF);
      OR_PUT_INT (&net_bitlen, bitlen);
      or_put_data (buf, (char *) &net_bitlen, OR_INT_SIZE);
    }

  /* store the string bytes */
  assert (buf->ptr + bytelen <= buf->endptr);
  or_put_data (buf, string, bytelen);

  if (align == INT_ALIGNMENT)
    {
      /* round up to a word boundary */
      or_put_align32 (buf);
    }

  return NO_ERROR;
}

static int
or_put_varchar_internal (OR_BUF * buf, char *string, int charlen, int align)
{
  int net_charlen = 0, compress_buffer_size;
  char *compressed_string = NULL;
  int rc = NO_ERROR;
  bool compressable = false;
  int compressed_length = 0;

  /* store the size prefix */
  if (charlen < OR_MINIMUM_STRING_LENGTH_FOR_COMPRESSION)
    {
      rc = or_put_byte (buf, charlen);
    }
  else
    {
      rc = or_put_byte (buf, OR_MINIMUM_STRING_LENGTH_FOR_COMPRESSION);
      compressable = true;
    }

  if (rc != NO_ERROR)
    {
      goto cleanup;
    }

  if (compressable == true)
    {
      if (!pr_Enable_string_compression || charlen > LZ4_MAX_INPUT_SIZE)    /* compression is not set */
    {
      compressed_length = 0;
      goto after_compression;
    }

      assert (OR_IS_STRING_LENGTH_COMPRESSABLE (charlen));

      /* Alloc memory for the compressed string */

      // *INDENT-OFF*
      compress_buffer_size = cubcompress::bound<cubcompress::LZ4> (charlen);
      // *INDENT-ON*
      compressed_string = (char *) malloc (compress_buffer_size);
      if (compressed_string == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) (compress_buffer_size));
      rc = ER_OUT_OF_VIRTUAL_MEMORY;
      goto cleanup;
    }

      /* Compress the string */
      // *INDENT-OFF*
      compressed_length =
    cubcompress::compress<cubcompress::LZ4> (string, charlen, compressed_string, compress_buffer_size);
      // *INDENT-ON*
      if (compressed_length <= 0)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_LZ4_COMPRESS_FAIL, 4, FILEIO_ZIP_LZ4_METHOD,
          fileio_get_zip_method_string (FILEIO_ZIP_LZ4_METHOD), FILEIO_ZIP_LZ4_DEFAULT_LEVEL,
          fileio_get_zip_level_string (FILEIO_ZIP_LZ4_DEFAULT_LEVEL));
      rc = ER_IO_LZ4_COMPRESS_FAIL;
      goto cleanup;
    }
      assert (compressed_length <= compress_buffer_size);

      if (compressed_length >= charlen - 8)
    {
      /* Compression successful but its length exceeds the original length of the string. */
      compressed_length = 0;
    }

      /* Store the compression size */
      assert (compressed_length < charlen - 8);
    after_compression:
      OR_PUT_INT (&net_charlen, compressed_length);
      rc = or_put_data (buf, (char *) &net_charlen, OR_INT_SIZE);
      if (rc != NO_ERROR)
    {
      goto cleanup;
    }

      net_charlen = 0;
      /* Store the uncompressed data size */
      OR_PUT_INT (&net_charlen, charlen);
      rc = or_put_data (buf, (char *) &net_charlen, OR_INT_SIZE);
      if (rc != NO_ERROR)
    {
      goto cleanup;
    }

      if (compressed_length == 0)
    {
      /* Compression failed. */
      /* Store the original string bytes */
      rc = or_put_data (buf, string, charlen);
      if (rc != NO_ERROR)
        {
          goto cleanup;
        }
    }
      else
    {
      /* Store the compressed string bytes */
      rc = or_put_data (buf, compressed_string, (int) compressed_length);
      if (rc != NO_ERROR)
        {
          goto cleanup;
        }
    }
    }
  else
    {
      /* No compression needed */
      /* Store the string in its raw form */
      rc = or_put_data (buf, string, charlen);
      if (rc != NO_ERROR)
    {
      goto cleanup;
    }
    }

  if (align == INT_ALIGNMENT)
    {
      /* kludge, temporary NULL terminator */
      rc = or_put_byte (buf, 0);
      if (rc != NO_ERROR)
    {
      goto cleanup;
    }

      /* round up to a word boundary */
      rc = or_put_align32 (buf);
    }

cleanup:

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


  if (buf->ptr > buf->endptr)
    {
      return ER_TF_BUFFER_OVERFLOW;
    }

  return rc;
}

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * or_length_binary - Calculates the number of bytes required for the disk
 * representaion of binary data.
 *    return: bytes required or 0 for error
 *    binary(in): binary data
 *
 * Note:
 * Included in this number are any padding bytes required to bring the data
 * up to a word boundary.
 */
int
or_length_binary (DB_BINARY * binary)
{
  int len, bits;

  len = 0;
  if (binary != NULL && binary->length < OR_BINARY_MAX_LENGTH)
    {
      len = binary->length + OR_INT_SIZE;   /* always a header word for sizes */
      bits = len & 3;
      if (bits)
    {
      len += 4 - bits;
    }
    }
  return len;
}
#endif /* ENABLE_UNUSED_FUNCTION */

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * or_length_string - returns the number of bytes required to hold the disk
 * representation of a string
 *    return: number of bytes needed or 0 for error
 *    string(in): string for calculation
 *
 * Note:
 * This will include any padding bytes up to 4 byte boundary
 */
int
or_length_string (char *string)
{
  int len, bits;

  len = 0;
  if (string != NULL)
    {
      len = strlen (string) + 1;
      bits = len & 3;
      if (bits)
    {
      len += 4 - bits;
    }
    }
  return len;
}
#endif /* ENABLE_UNUSED_FUNCTION */

/*
 * or_put_varbit - put varbit into or buffer
 *    return: NO_ERROR or error code
 *    buf(in/out): or buffer
 *    string(in): string contains varbit value
 *    bitlen(in): length of varbit
 */
int
or_packed_put_varbit (OR_BUF * buf, const char *string, int bitlen)
{
  return or_put_varbit_internal (buf, string, bitlen, INT_ALIGNMENT);
}

/*
 * or_packed_varbit_length - returns packed varbit length of or buffer encoding
 *    return: packed varbit encoding length
 *    bitlen(in): varbit length
 */
int
or_packed_varbit_length (int bitlen)
{
  return or_varbit_length_internal (bitlen, INT_ALIGNMENT);
}

/*
 * or_packed_put_varchar - put varchar to or buffer
 *    return: NO_ERROR or error code
 *    buf(in/out): or buffer
 *    string(in): string to put into the or buffer
 *    charlen(in): string length
 */
int
or_packed_put_varchar (OR_BUF * buf, char *string, int charlen)
{
  return or_put_varchar_internal (buf, string, charlen, INT_ALIGNMENT);
}

/*
 * or_packed_varchar_length - returns length of place holder that can contain
 * package varchar length. Also ajust length up to 4 byte boundary.
 *    return: length of placeholder that can contain packed varchar length
 *    charlen(in): varchar length
 */
int
or_packed_varchar_length (int charlen)
{
  return or_varchar_length_internal (charlen, INT_ALIGNMENT);
}

int
or_packed_recdesc_length (int length)
{
  return OR_INT_SIZE * 2 + or_packed_stream_length (length);
}

/*
 * or_unpack_var_table - Extracts a variable offset table from the disk
 * representation of an object and converts it into a memory structure
 *    return: advanced buffer pointer
 *    ptr(in): pointer into a disk representation
 *    nvars(in): number of variables expected
 *    vars(out): array of var table info
 *
 * Note:
 *    This is a little easier than dealing with the offset table in its raw
 *    disk format.  It assumes that you know the number of elements
 *    in the table and have previously allocated an array of OR_VARINFO
 *    structures that will be filled in.
 */
char *
or_unpack_var_table (char *ptr, int nvars, OR_VARINFO * vars)
{
  return or_unpack_var_table_internal (ptr, nvars, vars, BIG_VAR_OFFSET_SIZE);
}

/*
 * or_unpack_var_table_internal - Extracts a variable offset table from the disk
 * representation of an object and converts it into a memory structure
 *    return: advanced buffer pointer
 *    ptr(in): pointer into a disk representation
 *    nvars(in): number of variables expected
 *    vars(out): array of var table info
 *
 * Note:
 *    This is a little easier than dealing with the offset table in its raw
 *    disk format.  It assumes that you know the number of elements
 *    in the table and have previously allocated an array of OR_VARINFO
 *    structures that will be filled in.
 */
static char *
or_unpack_var_table_internal (char *ptr, int nvars, OR_VARINFO * vars, int offset_size)
{
  int i, offset, offset2;

  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  if (nvars)
    {
      offset = OR_GET_OFFSET_INTERNAL (ptr, offset_size);
      ptr += offset_size;

      for (i = 0; i < nvars; i++)
    {
      offset2 = OR_GET_OFFSET_INTERNAL (ptr, offset_size);
      ptr += offset_size;
      vars[i].offset = offset;
      vars[i].length = offset2 - offset;
      offset = offset2;
    }
      ptr = PTR_ALIGN (ptr, INT_ALIGNMENT);
    }
  return ptr;
}

/*
 * or_get_var_table - Extracts an array of OR_VARINFO structures from a
 * disk variable offset table
 *    return: array of OR_VARINFO structures
 *    buf(in/out): or buffer
 *    nvars(in): expected number of variables
 *    allocator(in): allocator for return value allocation
 */
OR_VARINFO *
or_get_var_table (OR_BUF * buf, int nvars, char *(*allocator) (int))
{
  return or_get_var_table_internal (buf, nvars, allocator, BIG_VAR_OFFSET_SIZE);
}

/*
 * or_get_var_table_internal - Extracts an array of OR_VARINFO structures from a
 * disk variable offset table
 *    return: array of OR_VARINFO structures
 *    buf(in/out): or buffer
 *    nvars(in): expected number of variables
 *    allocator(in): allocator for return value allocation
 */
OR_VARINFO *
or_get_var_table_internal (OR_BUF * buf, int nvars, char *(*allocator) (int), int offset_size)
{
  OR_VARINFO *vars;
  int length;

  vars = NULL;

  if (!nvars)
    {
      return vars;
    }

  length = DB_ALIGN (offset_size * (nvars + 1), INT_ALIGNMENT);
  assert (buf->ptr + length <= buf->endptr);

  vars = (OR_VARINFO *) (*allocator) (sizeof (OR_VARINFO) * nvars);
  if (vars == NULL && nvars)
    {
      assert (false);
    }
  else
    {
      (void) or_unpack_var_table_internal (buf->ptr, nvars, vars, offset_size);
    }
  buf->ptr += length;
  return vars;
}

/*
 * COMMUNICATION BUFFER PACK/UNPACK FUNCTIONS
 */

/*
 * or_pack_int - write int value to ptr
 *    return: advanced buffer pointer
 *    ptr(out): out buffer
 *    number(in): int value
 */
char *
or_pack_int (char *ptr, int number)
{
  if (ptr == NULL)
    {
      return NULL;
    }

  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_PUT_INT (ptr, number);
  return (ptr + OR_INT_SIZE);
}

/*
 * or_unpack_int - read a int value
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    number(out): int value
 */
char *
or_unpack_int (char *ptr, int *number)
{
  if (ptr == NULL)
    {
      *number = 0;
      return NULL;
    }

  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  *number = OR_GET_INT (ptr);
  return (ptr + OR_INT_SIZE);
}

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * or_pack_bigint - write bigint value to ptr
 *    return: advanced buffer pointer
 *    ptr(out): out buffer
 *    number(in): bigint value
 */
char *
or_pack_bigint (char *ptr, DB_BIGINT number)
{
  return or_pack_int64 (ptr, number);
}

/*
 * or_unpack_bigint - read a bigint value
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    number(out): bigint value
 */
char *
or_unpack_bigint (char *ptr, DB_BIGINT * number)
{
  return or_unpack_int64 (ptr, number);
}
#endif

/*
 * or_pack_int64 - write INT64 value to ptr
 *    return: advanced buffer pointer
 *    ptr(out): out buffer
 *    number(in): INT64 value
 */
char *
or_pack_int64 (char *ptr, INT64 number)
{
  ptr = PTR_ALIGN (ptr, MAX_ALIGNMENT);

  OR_PUT_INT64 (ptr, &number);
  return (ptr + OR_INT64_SIZE);
}

/*
 * or_unpack_int64 - read a INT64 value
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    number(out): INT64 value
 */
char *
or_unpack_int64 (char *ptr, INT64 * number)
{
  ptr = PTR_ALIGN (ptr, MAX_ALIGNMENT);

  OR_GET_INT64 (ptr, number);
  return (ptr + OR_INT64_SIZE);
}


/*
 * or_pack_short - write a short value
 *    return: advanced buffer pointer
 *    ptr(out): output buffer
 *    number(in): short value
 */
char *
or_pack_short (char *ptr, short number)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_PUT_INT (ptr, (int) number);
  return (ptr + OR_INT_SIZE);
}

/*
 * or_unpack_short - read a short value
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    number(out): short value
 */
char *
or_unpack_short (char *ptr, short *number)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  *number = (short) OR_GET_INT (ptr);
  return (ptr + OR_INT_SIZE);
}

/*
 * or_pack_errcode - write a errcode value
 *    return: advanced buffer pointer
 *    ptr(out): output buffer
 *    error(in): int value
 */
char *
or_pack_errcode (char *ptr, int error)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_PUT_INT (ptr, (int) error);
  return (ptr + OR_INT_SIZE);
}

/*
 * or_unpack_errcode - read a errcode value
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    error(out): int value
 */
char *
or_unpack_errcode (char *ptr, int *error)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  *error = (int) OR_GET_INT (ptr);
  return (ptr + OR_INT_SIZE);
}


/*
 * or_pack_lock - write a LOCK value
 *    return: advanced buffer pointer
 *    ptr(out): output buffer
 *    lock(in): LOCK value
 */
char *
or_pack_lock (char *ptr, LOCK lock)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_PUT_INT (ptr, (int) lock);
  return (ptr + OR_INT_SIZE);
}

/*
 * or_unpack_lock - read a LOCK value
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    lock(out): LOCK value
 */
char *
or_unpack_lock (char *ptr, LOCK * lock)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  *lock = (LOCK) OR_GET_INT (ptr);
  return (ptr + OR_INT_SIZE);
}

/*
 * or_pack_float - write a float value
 *    return: advanced buffer pointer
 *    ptr(out): output buffer
 *    number(in): float value
 */
char *
or_pack_float (char *ptr, float number)
{
  ASSERT_ALIGN (ptr, FLOAT_ALIGNMENT);

  OR_PUT_FLOAT (ptr, number);
  return (ptr + OR_FLOAT_SIZE);
}

/*
 * or_unpack_float - read a float value
 *    return: advanced buffer pointer
 *    ptr(in): read buffer
 *    number(out): float value
 */
char *
or_unpack_float (char *ptr, float *number)
{
  ASSERT_ALIGN (ptr, FLOAT_ALIGNMENT);

  OR_GET_FLOAT (ptr, number);
  return (ptr + OR_FLOAT_SIZE);
}


/*
 * or_pack_double - write a double value
 *    return: advanced buffer pointer
 *    ptr(out): output buffer
 *    number(in): double value
 */
char *
or_pack_double (char *ptr, double number)
{
  ptr = PTR_ALIGN (ptr, MAX_ALIGNMENT);

  OR_PUT_DOUBLE (ptr, number);
  return (ptr + OR_DOUBLE_SIZE);
}

/*
 * or_unpack_double - read a double value
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    number(out): double value
 */
char *
or_unpack_double (char *ptr, double *number)
{
  ptr = PTR_ALIGN (ptr, MAX_ALIGNMENT);

  OR_GET_DOUBLE (ptr, number);
  return (ptr + OR_DOUBLE_SIZE);
}

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * or_pack_time - write a DB_TIME value
 *    return: advanced buffer pointer
 *    ptr(out): output buffer
 *    time(in): DB_TIME value
 */
char *
or_pack_time (char *ptr, DB_TIME time)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_PUT_TIME (ptr, &time);
  return (ptr + OR_TIME_SIZE);
}

/*
 * or_unpack_time - read a DB_TIME value
 *    return:  advanced buffer pointer
 *    ptr(in): input buffer
 *    time(out): DB_TIME value
 */
char *
or_unpack_time (char *ptr, DB_TIME * time)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_GET_TIME (ptr, time);
  return (ptr + OR_TIME_SIZE);
}

/*
 * or_pack_utime - write a DB_UTIME value
 *    return: advanced buffer pointer
 *    ptr(out): output buffer
 *    utime(in): DB_UTIME value
 */
char *
or_pack_utime (char *ptr, DB_UTIME utime)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_PUT_UTIME (ptr, &utime);
  return (ptr + OR_UTIME_SIZE);
}

/*
 * or_unpack_utime - read a DB_UTIME value
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    utime(out): DB_UTIME value
 */
char *
or_unpack_utime (char *ptr, DB_UTIME * utime)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_GET_UTIME (ptr, utime);
  return (ptr + OR_UTIME_SIZE);
}

/*
 * or_pack_date - write a DB_DATE value
 *    return: advanced buffer pointer
 *    ptr(out): output buffer
 *    date(in): DB_DATE value
 */
char *
or_pack_date (char *ptr, DB_DATE date)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_PUT_DATE (ptr, &date);
  return (ptr + OR_DATE_SIZE);
}

/*
 * or_unpack_date - read a DB_DATE value
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    date(out): DB_DATE value
 */
char *
or_unpack_date (char *ptr, DB_DATE * date)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_GET_DATE (ptr, date);
  return (ptr + OR_DATE_SIZE);
}

/*
 * or_pack_monetary - write a DB_MONETARY value
 *    return: advanced buffer pointer
 *    ptr(out): output buffer
 *    money(in): DB_MONETARY value
 */
char *
or_pack_monetary (char *ptr, DB_MONETARY * money)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_PUT_MONETARY (ptr, money);
  return (ptr + OR_MONETARY_SIZE);
}

/*
 * or_unpack_monetary - read a DB_MONETARY value
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    money(out): DB_MONETARY value
 */
char *
or_unpack_monetary (char *ptr, DB_MONETARY * money)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_GET_MONETARY (ptr, money);
  return (ptr + OR_MONETARY_SIZE);
}
#endif /* ENABLE_UNUSED_FUNCTION */

/*
 * or_unpack_int_array - extracts a array of integers from a buffer
 *    return: advanced buffer pointer
 *    ptr(in): current pointer in buffer
 *    n(in): array length
 *    number_array(out): result array
 */
char *
or_unpack_int_array (char *ptr, int n, int **number_array)
{
  int i;

  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  if (n > 0)
    {
      *number_array = (int *) db_private_alloc (NULL, (n * sizeof (int)));
      if (*number_array)
    {
      for (i = 0; i < n; i++)
        {
          ptr = or_unpack_int (ptr, &(*number_array)[i]);
        }
    }
      else
    {
      ptr = NULL;
    }
    }
  else
    {
      ptr = NULL;
    }

  return ptr;
}

/*
 * DISK IDENTIFIER TRANSLATORS
 *    Translators for the disk identifiers OID, HFID, BTID, EHID.
 */


/*
 * or_pack_oid - write a OID value
 *    return: advanced buffer pointer
 *    ptr(out): output buffer
 *    oid(in): OID value
 */
char *
or_pack_oid (char *ptr, const OID * oid)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  if (oid != NULL)
    {
      OR_PUT_OID (ptr, oid);
    }
  else
    {
      OR_PUT_NULL_OID (ptr);
    }
  return (ptr + OR_OID_SIZE);
}

/*
 * or_pack_oid_array () - Pack an OID array.
 *
 * return    : Pointer in buffer after the packed OID array.
 * ptr (in)  : Pointer in buffer where OID array should be packed.
 * n (in)    : Length of OID array.
 * oids (in) : OID array.
 */
char *
or_pack_oid_array (char *ptr, int n, const OID * oids)
{
  int i;

  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  if (ptr == NULL)
    {
      return NULL;
    }

  assert (n > 0 && oids != NULL);
  for (i = 0; i < n; i++)
    {
      ptr = or_pack_oid (ptr, &oids[i]);
    }
  return ptr;
}

/*
 * or_unpack_oid - read a OID value
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    oid(out): OID value
 */
char *
or_unpack_oid (char *ptr, OID * oid)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_GET_OID (ptr, oid);
  return (ptr + OR_OID_SIZE);
}

/*
 * or_unpack_oid_array - read OID array values
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    n(in): number of OIDs to read
 *    oids(out): OID array
 */
char *
or_unpack_oid_array (char *ptr, int n, OID ** oids)
{
  int i;

  *oids = (OID *) db_private_alloc (NULL, (n * sizeof (OID)));

  if (*oids == NULL)
    {
      ptr = NULL;
      return ptr;
    }

  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  for (i = 0; i < n; i++)
    {
      OR_GET_OID (ptr, &((*oids)[i]));
      ptr = ptr + OR_OID_SIZE;
    }

  return (ptr);
}

/*
 * or_pack_hfid - write a HFID value
 *    return: advanced buffer pointer
 *    ptr(out): output buffer
 *    hfid(in): HFID value
 */
char *
or_pack_hfid (const char *ptr, const HFID * hfid)
{
  char *new_;

  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  if (hfid != NULL)
    {
      OR_PUT_HFID (ptr, hfid);
    }
  else
    {
      OR_PUT_NULL_HFID (ptr);
    }

  /* kludge, need to have all of these accept and return const args */
  new_ = (char *) ptr + OR_HFID_SIZE;
  return new_;
}

/*
 * or_unpack_hfid - read a HFID value
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    hfid(out): HFID value
 */
char *
or_unpack_hfid (char *ptr, HFID * hfid)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_GET_HFID (ptr, hfid);
  return (ptr + OR_HFID_SIZE);
}

/*
 * or_unpack_hfid_array - read HFID array
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    n(in): number of HFIDs to read
 *    hfids(out): HFID array
 */
char *
or_unpack_hfid_array (char *ptr, int n, HFID ** hfids)
{
  int i = 0;

  *hfids = (HFID *) db_private_alloc (NULL, (n * sizeof (HFID)));

  if (*hfids == NULL)
    {
      ptr = NULL;
      return ptr;
    }

  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  for (i = 0; i < n; i++)
    {
      OR_GET_HFID (ptr, &((*hfids)[i]));
      ptr = ptr + OR_HFID_SIZE;
    }

  return ptr;
}

/*
 * or_pack_ehid - write a HFID value
 *    return: advanced buffer pointer
 *    ptr(out): output buffer
 *    ehid(in): HFID value
 */
char *
or_pack_ehid (char *ptr, EHID * ehid)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_PUT_EHID (ptr, ehid);

  return (ptr + OR_EHID_SIZE);
}

char *
or_pack_recdes (char *buf, RECDES * recdes)
{
  buf = or_pack_int (buf, recdes->length);
  buf = or_pack_short (buf, recdes->type);
  buf = or_pack_stream (buf, recdes->data, recdes->length);
  return buf;
}

/*
 * or_unpack_ehid - read a EHID value
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    ehid(out): EHID value
 */
char *
or_unpack_ehid (char *ptr, EHID * ehid)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_GET_EHID (ptr, ehid);

  return (ptr + OR_EHID_SIZE);
}

/*
 * or_pack_btid - write a BTID value
 *    return: advanced buffer pointer
 *    ptr(out): output buffer
 *    btid(in): BTID value
 */
char *
or_pack_btid (char *ptr, const BTID * btid)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  if (btid)
    {
      OR_PUT_BTID (ptr, btid);
    }
  else
    {
      OR_PUT_NULL_BTID (ptr);
    }

#if !defined(NDEBUG)
  /* to make valgrind quiet */
  OR_PUT_SHORT (ptr + OR_BTID_SIZE, 0);
#endif

  return (ptr + OR_BTID_ALIGNED_SIZE);
}

/*
 * or_unpack_btid - read a BTID value
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    btid(out): a BTID value
 */
char *
or_unpack_btid (char *ptr, BTID * btid)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_GET_BTID (ptr, btid);

  return (ptr + OR_BTID_ALIGNED_SIZE);
}

/*
 * or_pack_log_lsa - write a LOG_LSA value
 *    return: advanced buffer pointer
 *    ptr(out): output buffer
 *    lsa(in): LOG_LSA value
 */
char *
or_pack_log_lsa (const char *ptr, const log_lsa * lsa)
{
  char *new_;

  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  if (lsa != NULL)
    {
      OR_PUT_LOG_LSA (ptr, lsa);
    }
  else
    {
      OR_PUT_NULL_LOG_LSA (ptr);
    }

#if !defined(NDEBUG)
  /* to make valgrind quiet */
  OR_PUT_SHORT (ptr + OR_LOG_LSA_SIZE, 0);
#endif

  /* kludge, need to have all of these accept and return const args */
  new_ = (char *) ptr + OR_LOG_LSA_ALIGNED_SIZE;
  return new_;
}

/*
 * or_unpack_log_lsa - read a LOG_LSA value
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    lsa(out): LOG_LSA value
 */
char *
or_unpack_log_lsa (char *ptr, log_lsa * lsa)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_GET_LOG_LSA (ptr, lsa);
  return (ptr + OR_LOG_LSA_ALIGNED_SIZE);
}

/*
 * or_unpack_set - read a set
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    set(out): set value
 *    domain(in): domain of the set (can be NULL)
 */
char *
or_unpack_set (char *ptr, setobj ** set, TP_DOMAIN * domain)
{
  OR_BUF orbuf;

  or_init (&orbuf, ptr, 0);
  *set = or_get_set (&orbuf, domain);

  return orbuf.ptr;
}

/*
 * or_unpack_setref - unpack a set and get set reference for the set
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    ref(out): reference for a set
 */
char *
or_unpack_setref (char *ptr, DB_SET ** ref)
{
  SETOBJ *set = NULL;

  ptr = or_unpack_set (ptr, &set, NULL);

  if (set != NULL)
    {
      *ref = setobj_get_reference (set);
    }
  else
    {
      *ref = NULL;
    }

  return ptr;
}


/*
 * or_pack_string - Puts a string into the buffer.
 *    return: advanced buffer pointer
 *    ptr(out): current buffer pointer
 *    string(in): string to pack
 * Note:
 *    The string will be padded with extra bytes so that the ending pointer
 *    is on a word boundary.
 *    NOTE:  This differs from or_put_string in that the length of the string
 *    is stored before the string data.  If the string is NULL the length
 *    is -1.  This is because comm buffers aren't formatted as objects
 *    and therefore don't have an offset table that holds the size
 *    of the string.
 */
char *
or_pack_string (char *ptr, const char *string)
{
  int len, bits, pad;

  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  if (string == NULL)
    {
      OR_PUT_INT (ptr, -1);
      ptr += OR_INT_SIZE;
    }
  else
    {
      len = strlen (string) + 1;
      bits = len & 3;
      if (bits)
    {
      pad = 4 - bits;
    }
      else
    {
      pad = 0;
    }
      OR_PUT_INT (ptr, len + pad);
      ptr += OR_INT_SIZE;
      (void) memcpy (ptr, string, len);
      ptr += len;
      (void) memset (ptr, '\0', pad);
      ptr += pad;
    }
  return ptr;
}


/*
 * or_pack_string_with_null_padding - make stream to string and pack
 *    return: advanced buffer pointer
 *    ptr(out): current buffer pointer
 *    string(in): string to pack
 *    len(in): stream len
 */
char *
or_pack_string_with_null_padding (char *ptr, const char *string, size_t len)
{
  char *ret_ptr;

  ret_ptr = or_pack_stream (ptr, string, len + 1);

  ptr[OR_INT_SIZE + len] = '\0';    /* NULL Padding */

  return ret_ptr;
}

/*
 * or_pack_stream - Puts a stream into the buffer.
 *    return: advanced buffer pointer
 *    ptr(out): current buffer pointer
 *    stream(in): stream to pack
 */
char *
or_pack_stream (char *ptr, const char *stream, size_t len)
{
  int bits, pad;

  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  if (stream == NULL)
    {
      OR_PUT_INT (ptr, -1);
      ptr += OR_INT_SIZE;
    }
  else
    {
      bits = len & 3;
      if (bits)
    {
      pad = 4 - bits;
    }
      else
    {
      pad = 0;
    }
      OR_PUT_INT (ptr, len + pad);
      ptr += OR_INT_SIZE;
      memcpy (ptr, stream, len);
      ptr += len;
      memset (ptr, '\0', pad);
      ptr += pad;
    }
  return ptr;
}

/*
 * or_pack_string_with_length - pack a string at most given length
 *    return: advanced buffer pointer
 *    ptr(out): output buffer
 *    string(in): string
 *    length(in): length
 */
char *
or_pack_string_with_length (char *ptr, const char *string, int length)
{
  int len, bits, pad;

  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  if (string == NULL)
    {
      OR_PUT_INT (ptr, -1);
      ptr += OR_INT_SIZE;
    }
  else
    {
      len = length + 1;
      bits = len & 3;
      if (bits)
    {
      pad = 4 - bits;
    }
      else
    {
      pad = 0;
    }
      OR_PUT_INT (ptr, len + pad);
      ptr += OR_INT_SIZE;
      (void) memcpy (ptr, string, len);
      ptr += len;
      (void) memset (ptr, '\0', pad);
      ptr += pad;
    }

  return ptr;
}

/*
 * or_unpack_string - extracts a string from a buffer.
 *    return: advanced pointer
 *    ptr(in): current pointer
 *    string(out): return pointer
 */
char *
or_unpack_string (char *ptr, char **string)
{
  char *new_;
  int length;

  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  length = OR_GET_INT (ptr);
  ptr += OR_INT_SIZE;
  if (length == -1)
    {
      *string = NULL;
    }
  else
    {
      new_ = (char *) db_private_alloc (NULL, length);
      /* need to handle allocation errors */
      if (new_ == NULL)
    {
      ptr += length;
    }
      else
    {
      (void) memcpy (new_, ptr, length);
      ptr += length;
    }
      *string = new_;
    }
  return ptr;
}

/*
 * or_unpack_stream - extracts a stream from a buffer.
 *    return: advanced pointer
 *    ptr(in): current pointer
 *    stream(out): return pointer
 */
char *
or_unpack_stream (char *ptr, char *stream, size_t len)
{
  int length;

  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  length = OR_GET_INT (ptr);
  ptr += OR_INT_SIZE;

  assert_release ((size_t) length >= len);

  memcpy (stream, ptr, len);
  ptr += length;
  return ptr;
}

/*
 * or_unpack_string_alloc - extracts a string from a buffer.
 *    return: advanced pointer
 *    ptr(in): current pointer
 *    string(out): return pointer
 *
 * Note: Unlike or_unpack_string which uses db_private_alloc to allocate
 * memory for the resulting string, this function uses malloc and the string
 * has to be freed using free_and_init.
 */
char *
or_unpack_string_alloc (char *ptr, char **string)
{
  char *new_;
  int length;

  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  length = OR_GET_INT (ptr);
  ptr += OR_INT_SIZE;
  if (length == -1)
    {
      *string = NULL;
    }
  else
    {
      new_ = (char *) malloc (length * sizeof (char));
      /* need to handle allocation errors */
      if (new_ == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (length * sizeof (char)));
      ptr += length;
    }
      else
    {
      (void) memcpy (new_, ptr, length);
      ptr += length;
    }
      *string = new_;
    }
  return ptr;
}

/*
 * or_unpack_string_nocopy - extracts a string from a buffer.
 *    return: advanced pointer
 *    ptr(in): current pointer
 *    string(out): return pointer
 */
char *
or_unpack_string_nocopy (char *ptr, char **string)
{
  int length;

  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  length = OR_GET_INT (ptr);
  ptr += OR_INT_SIZE;
  if (length == -1)
    {
      *string = NULL;
    }
  else
    {
      *string = ptr;
      ptr += length;
    }
  return ptr;
}

/*
 * or_packed_string_length - Determines the number of bytes required to hold
 * the packed representation of a string.
 *    return: length of packed string
 *    string(in): string to examine
 *    strlen(out): strlen(string)
 *
 * Note: This includes padding bytes necessary to bring the length up to a
 * word boundary and also includes a word for the string length which is
 * stored at the top.
 */
int
or_packed_string_length (const char *string, int *strlenp)
{
  int total, len, bits, pad;

  /* always have a length */
  total = OR_INT_SIZE;
  if (string != NULL)
    {
      if (strlenp != NULL)
    {
      len = (*strlenp = strlen (string)) + 1;
    }
      else
    {
      len = strlen (string) + 1;
    }
      bits = len & 3;
      if (bits)
    {
      pad = 4 - bits;
    }
      else
    {
      pad = 0;
    }
      total += len + pad;
    }
  else
    {
      if (strlenp != NULL)
    {
      *strlenp = 0;
    }
    }
  return total;
}

/*
 * or_packed_json_schema_length - Determines the number of bytes required to hold
 * the packed representation of a json_schema.
 *    return: length of packed json_schema
 *    json_schema(in): json_schema
 */
static int
or_packed_json_schema_length (const char *json_schema)
{
  DB_VALUE val;
  db_make_string (&val, json_schema);

  int len = tp_String.get_disk_size_of_value (&val);

  pr_clear_value (&val);

  return len;
}

/*
 * or_packed_json_validator_length - Determines the number of bytes required to hold
 * the packed representation of a json_validator.
 *    return: length of packed json_validator
 *    json_validator(in): json_validator
 */
static int
or_packed_json_validator_length (JSON_VALIDATOR * json_validator)
{
  if (json_validator == NULL)
    {
      return 0;
    }
  return or_packed_json_schema_length (db_json_get_schema_raw_from_validator (json_validator));
}

/*
 * or_packed_stream_length - Determines the number of bytes required to hold
 * the packed representation of a stream.
 *    return: length of packed stream
 *    len(in): length of stream
 */
int
or_packed_stream_length (size_t len)
{
  int total, bits, pad;

  /* always have a length */
  total = OR_INT_SIZE;
  if (len > 0)
    {
      bits = len & 3;
      if (bits)
    {
      pad = 4 - bits;
    }
      else
    {
      pad = 0;
    }
      total += (int) len + pad;
    }
  return total;
}

/*
 * or_pack_bool_array - write a bool array to pointer
 *    return: advanced buffer pointer
 *    ptr(out): out buffer
 *    bools(in): bool array
 *    size(in): size of bool array
 */
char *
or_pack_bool_array (char *ptr, const bool * bools, int size)
{
  int bits, pad;
  if (ptr == NULL)
    {
      return NULL;
    }

  ASSERT_ALIGN (ptr, INT_ALIGNMENT);
  if (bools == NULL)
    {
      OR_PUT_INT (ptr, -1);
      ptr += OR_INT_SIZE;
    }
  else
    {
      bits = size & 3;
      if (bits)
    {
      pad = 4 - bits;
    }
      else
    {
      pad = 0;
    }
      OR_PUT_INT (ptr, size + pad);
      ptr += OR_INT_SIZE;
      (void) memcpy (ptr, bools, size);
      ptr += (size + pad);
    }
  return ptr;
}

/*
 * or_unpack_bool_array - read a bool array
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    bools(out): bool array
 */
char *
or_unpack_bool_array (char *ptr, bool ** bools)
{
  bool *new_;
  int length;

  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  length = OR_GET_INT (ptr);
  ptr += OR_INT_SIZE;
  if (length == -1)
    {
      *bools = NULL;
    }
  else
    {
      new_ = (bool *) db_private_alloc (NULL, length);
      /* need to handle allocation errors */
      if (new_ == NULL)
    {
      ptr += length;
    }
      else
    {
      (void) memcpy (new_, ptr, length);
      ptr += length;
    }
      *bools = new_;
    }

  return ptr;
}

/*
 * or_packed_bool_array_length - Determines the number of bytes required to
 *               hold the packed representation of a bool
 *               array.
 *    return: length of packed bool array
 *    bools(in): bool array
 *    size(in): the number of bool values in the array
 *
 * Note: This includes padding bytes necessary to bring the length up to a
 * word boundary and also includes a word for the array length which is
 * stored at the top.
 */
int
or_packed_bool_array_length (const bool * bools, int size)
{
  int total, bits, pad;

  /* always have a length */
  total = OR_INT_SIZE;
  if (bools != NULL)
    {
      bits = size & 3;
      if (bits)
    {
      pad = 4 - bits;
    }
      else
    {
      pad = 0;
    }
      total += (size + pad);
    }

  return total;
}

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * or_align_length - for a given length return aligned length
 *    return: aligned length
 *    length(in): given length
 */
int
or_align_length (int length)
{
  int total, len, bits, pad;

  /* always have a length */
  total = OR_INT_SIZE;

  if (length != 0)
    {
      len = length + 1;
      bits = len & 3;
      if (bits)
    {
      pad = 4 - bits;
    }
      else
    {
      pad = 0;
    }
      total += len + pad;
    }

  return total;
}
#endif /* ENABLE_UNUSED_FUNCTION */

/*
 * or_encode - Encodes the source data into the buffer so that only ascii
 * characters appear in the buffer.
 *    return: void
 *    buffer(out): buffer to encode into
 *    source(in): source data
 *    size(in): size of source data
 */
void
or_encode (char *buffer, const char *source, int size)
{
  while (size--)
    {
      *buffer++ = ((*source & 0xf0) >> 4) + '@';
      *buffer++ = ((*source++ & 0xf) + '@');
    }
  *buffer = '\0';
}

/*
 * or_decode - Decodes the data in the buffer to dest.
 *    return:
 *    buffer(in): buffer to decode from
 *    dest(out): destination data
 *    size(in): size of destination data
 * Note: The buffer is assumed to be encoded by or_encode
 */
void
or_decode (const char *buffer, char *dest, int size)
{
  char c1, c2;

  while (size--)
    {
      c1 = ((*buffer++ - '@') << 4) & 0xf0;
      c2 = (*buffer++ - '@') & 0xf;
      *dest++ = c1 | c2;
    }
}

/*
 * DOMAIN PACKING
 */

/*
 * OR_DOMAIN_
 *    Constants used to pick apart the first word of a packed domain.
 */

/* note that this leaves room for only 63 type codes */
#define OR_DOMAIN_TYPE_MASK     (0x3F)
#define OR_DOMAIN_NEXT_FLAG     (0x80)  /* domain following this one */

#define OR_DOMAIN_NULL_FLAG     (0x40)  /* is a tagged NULL value */
#define OR_DOMAIN_DESC_FLAG     (0x40)  /* asc/desc for only index key */

#define OR_DOMAIN_CLASS_OID_FLAG    (0x100) /* for object types */
#define OR_DOMAIN_SET_DOMAIN_FLAG   (0x100) /* for set types */
#define OR_DOMAIN_BUILTIN_FLAG      (0x100) /* for NULL type only */
#define OR_DOMAIN_ENUMERATION_FLAG  (0x100) /* for enumeration type only */
#define OR_DOMAIN_ENUM_COLL_FLAG    (0x200) /* for enumeration type only */
#define OR_DOMAIN_SCHEMA_FLAG       (0x400) /* for json */

#define OR_DOMAIN_SCALE_MASK        (0xFF00)
#define OR_DOMAIN_SCALE_SHIFT       (8)
#define OR_DOMAIN_SCALE_MAX     (0xFF)

#define OR_DOMAIN_CODSET_MASK       (0xFF00)
#define OR_DOMAIN_CODSET_SHIFT      (8)

#define OR_DOMAIN_PRECISION_MASK    (0xFFFF0000)
#define OR_DOMAIN_PRECISION_SHIFT   (16)
#define OR_DOMAIN_PRECISION_MAX     (0xFFFF)

#define OR_DOMAIN_COLLATION_MASK    (0x000000FF)
#define OR_DOMAIN_COLL_ENFORCE_FLAG (0x80000000)
#define OR_DOMAIN_COLL_LEAVE_FLAG   (0x40000000)

/*
 * or_packed_domain_size - calcualte the necessary buffer size required
 * to hold a packed representation of a hierarchical domain structure.
 *    return: byte size of the packed domain
 *    domain(in): domain to pack
 *    include_classoids(in): non-zero to include class OIDs in the packing
 *
 * Note: It is generally called before using or_pack_domain() to store the
 *    domain.  The include_classoids flag can be used to selectively decide
 *    whether or not to store the class OIDs with the domain.  You'd better
 *    call this function and or_pack_domain with the same include_classoids
 *    flag value.
 */
int
or_packed_domain_size (TP_DOMAIN * domain, int include_classoids)
{
  TP_DOMAIN *d;
  int size, precision, scale;
  DB_TYPE id;

  size = 0;

  /* hack, if this is a built-in domain, store a single word reference. */
  if (domain->built_in_index)
    {
      return OR_INT_SIZE;
    }

  /* now loop over the domains writing the disk representation */
  for (d = domain; d != NULL; d = d->next)
    {

      /* always have at least one word */
      size += OR_INT_SIZE;

      precision = 0;
      scale = 0;

      id = TP_DOMAIN_TYPE (d);
      switch (id)
    {

    case DB_TYPE_NUMERIC:
      precision = d->precision;
      scale = d->scale;
      /*
       * Safe guard for floating precision caused by incorrect type setting
       */
      if (precision <= TP_FLOATING_PRECISION_VALUE)
        {
          precision = DB_MAX_NUMERIC_PRECISION;
        }
      break;

    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      /* collation id */
      size += OR_INT_SIZE;
      [[fallthrough]];
    case DB_TYPE_BIT:
    case DB_TYPE_VARBIT:
      /*
       * Hack, if the precision is -1, it is a special value indicating
       * either the maximum precision for the varying types or a floating
       * precision for the fixed types.
       */
      if (d->precision != TP_FLOATING_PRECISION_VALUE)
        {
          precision = d->precision;
        }

      /*
       * Kludge, for temporary backward compatibility, treat varchar
       * types with the maximum precision as above. Need to change ourselves
       * to use -1 consistently for this after which this little
       * chunk of code can be removed.
       */
      if ((id == DB_TYPE_VARCHAR && d->precision == DB_MAX_VARCHAR_PRECISION)
          || (id == DB_TYPE_VARBIT && d->precision == DB_MAX_VARBIT_PRECISION))
        {
          precision = 0;
        }
      break;

    case DB_TYPE_OBJECT:
      if (include_classoids)
        {
          size += OR_OID_SIZE;
        }
      break;

    case DB_TYPE_ENUMERATION:
      /* collation id */
      if (d->collation_id != LANG_COLL_ISO_BINARY)
        {
          size += OR_INT_SIZE;
        }
      size += or_packed_enumeration_size (&DOM_GET_ENUMERATION (d));
      break;

    case DB_TYPE_JSON:
      size += or_packed_json_validator_length (d->json_validator);
      break;

    default:
      break;
    }

      if (precision >= OR_DOMAIN_PRECISION_MAX)
    {
      size += OR_INT_SIZE;
    }

      if (scale >= OR_DOMAIN_SCALE_MAX)
    {
      size += OR_INT_SIZE;
    }

      if (d->setdomain != NULL)
    {
      size += or_packed_domain_size (d->setdomain, include_classoids);
    }
    }

  return size;
}

/*
 * or_put_domain - creates the packed "disk" representation of a domain.
 *    return: NO_ERROR or error code
 *    buf(in/out): packing buffer
 *    domain(in): domain to pack
 *    include_classoids(in): non-zero if we're supposed to save class OIDs
 *    is_null(in): use domain tags for "NULL" value
 * Note:
 *    The is_null flag was added recently to allow domain tags for "NULL"
 *    values without having to store something in addition to the domain
 *    to indicate that the value was NULL.
 *    This should only be on if the domain is being used to tag a value
 *    and the value is logically NULL.
 */
int
or_put_domain (OR_BUF * buf, TP_DOMAIN * domain, int include_classoids, int is_null)
{
  unsigned int carrier, extended_precision, extended_scale;
  int precision, scale;
  int has_oid, has_subdomain, has_enum;
  bool has_schema;
  bool has_collation;
  TP_DOMAIN *d;
  DB_TYPE id;
  int rc = NO_ERROR;
  unsigned int collation_storage;

  /*
   * Hack, if this is a built-in domain, store a single word reference.
   * This is only allowed for the top level domain.
   * Note that or_unpack_domain is probably not going to do the right
   * thing if we try to pack a builtin domain that is "inside" a hierarchical
   * domain.  This isn't supposed to happen and can't in general because
   * two hierarchical domains are going to have potentially different "next"
   * pointers in the sub-domains.
   */
  if (domain->built_in_index)
    {
      carrier = ((DB_TYPE_NULL & OR_DOMAIN_TYPE_MASK) | OR_DOMAIN_BUILTIN_FLAG
         | (domain->built_in_index << OR_DOMAIN_PRECISION_SHIFT));
      if (is_null)
    {
      carrier |= OR_DOMAIN_NULL_FLAG;
    }
      return (or_put_int (buf, carrier));
    }

  /* must pack a full domain description */
  for (d = domain; d != NULL; d = d->next)
    {

      id = TP_DOMAIN_TYPE (d);

      /*
       * Initial word has type, precision, scale, & codeset to the extent that
       * they will fit.  High bit of the type byte is set if there
       * is another domain following this one. (e.g. for set or union domains).
       */
      carrier = id & OR_DOMAIN_TYPE_MASK;
      if (d->next != NULL)
    {
      carrier |= OR_DOMAIN_NEXT_FLAG;
    }
      if (d->is_desc)
    {
      carrier |= OR_DOMAIN_DESC_FLAG;
    }
      if (is_null)
    {
      carrier |= OR_DOMAIN_NULL_FLAG;
    }

      precision = 0;
      scale = 0;
      extended_precision = 0;
      extended_scale = 0;
      has_oid = 0;
      has_subdomain = 0;
      has_enum = 0;
      has_collation = false;
      has_schema = false;

      switch (id)
    {

    case DB_TYPE_NUMERIC:
      /* second byte contains scale, third & fourth bytes have precision */

      /* safe guard for scale */
      scale = d->scale;
      if (scale <= DB_DEFAULT_SCALE)
        {
          scale = 0;
        }

      if (scale < OR_DOMAIN_SCALE_MAX)
        {
          carrier |= scale << OR_DOMAIN_SCALE_SHIFT;
        }
      else
        {
          carrier |= OR_DOMAIN_SCALE_MAX << OR_DOMAIN_SCALE_SHIFT;
          extended_scale = d->scale;
        }
      /* handle all precisions the same way at the end */
      precision = d->precision;
      /*
       * Safe guard for floating precision caused by incorrect type setting
       */
      if (precision <= TP_FLOATING_PRECISION_VALUE)
        {
          precision = DB_MAX_NUMERIC_PRECISION;
        }
      break;

    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      has_collation = true;
      [[fallthrough]];
    case DB_TYPE_BIT:
    case DB_TYPE_VARBIT:
      carrier |= ((int) (d->codeset)) << OR_DOMAIN_CODSET_SHIFT;

      /*
       * Hack, if the precision is our special maximum/floating indicator,
       * store a zero in the precision field of the carrier.
       */
      if (d->precision != TP_FLOATING_PRECISION_VALUE)
        {
          precision = d->precision;
        }

      /*
       * Kludge, for temporary backward compatibility, treat varchar
       * types with the maximum precision as the -1 case.  See commentary
       * in or_packed_domain_size above.
       */
      if ((id == DB_TYPE_VARCHAR && d->precision == DB_MAX_VARCHAR_PRECISION)
          || (id == DB_TYPE_VARBIT && d->precision == DB_MAX_VARBIT_PRECISION))
        {
          precision = 0;
        }
      break;

    case DB_TYPE_OBJECT:
    case DB_TYPE_OID:
      /*
       * If the include_classoids argument was specified, set a flag in the
       * disk representation indicating the presence of the class oids.
       * This isn't necessary when the domain is used for value tagging
       * since the class OID can always be be gotten from the instance oid.
       */
      if (include_classoids)
        {
          carrier |= OR_DOMAIN_CLASS_OID_FLAG;
          has_oid = 1;
        }
      break;

    case DB_TYPE_SET:
    case DB_TYPE_MULTISET:
    case DB_TYPE_SEQUENCE:
    case DB_TYPE_TABLE:
    case DB_TYPE_MIDXKEY:
      /*
       * we need to recursively store the sub-domains following this one,
       * since sets can have empty domains we need a flag to indicate this.
       */
      if (d->setdomain != NULL)
        {
          carrier |= OR_DOMAIN_SET_DOMAIN_FLAG;
          has_subdomain = 1;
        }

      if (id == DB_TYPE_MIDXKEY)
        {
          assert (d->precision > 0 && d->precision == tp_domain_size (d->setdomain));
          precision = d->precision;
        }

      break;

    case DB_TYPE_ENUMERATION:
      if (d->collation_id != LANG_COLL_ISO_BINARY)
        {
          has_collation = true;
          carrier |= OR_DOMAIN_ENUM_COLL_FLAG;
        }
      else
        {
          has_collation = false;
        }
      if (DOM_GET_ENUM_ELEMENTS (d) != NULL)
        {
          carrier |= OR_DOMAIN_ENUMERATION_FLAG;
          has_enum = 1;
        }
      break;

    case DB_TYPE_JSON:
      if (d->json_validator != NULL)
        {
          carrier |= OR_DOMAIN_SCHEMA_FLAG;
          has_schema = true;
        }
      break;

    default:
      break;
    }

      /* handle the precision if this type wanted one */
      if (precision)
    {
      if (precision < OR_DOMAIN_PRECISION_MAX)
        {
          carrier |= precision << OR_DOMAIN_PRECISION_SHIFT;
        }
      else
        {
          carrier |= (unsigned int) OR_DOMAIN_PRECISION_MAX << OR_DOMAIN_PRECISION_SHIFT;
          extended_precision = precision;
        }
    }

      /* store the first word */
      rc = or_put_int (buf, carrier);
      if (rc != NO_ERROR)
    {
      return rc;
    }

      if (has_collation)
    {
      collation_storage = d->collation_id;
      if (d->collation_flag == TP_DOMAIN_COLL_ENFORCE)
        {
          collation_storage |= OR_DOMAIN_COLL_ENFORCE_FLAG;
        }
      else if (d->collation_flag == TP_DOMAIN_COLL_LEAVE)
        {
          collation_storage |= OR_DOMAIN_COLL_LEAVE_FLAG;
        }

      rc = or_put_int (buf, collation_storage);
      if (rc != NO_ERROR)
        {
          return rc;
        }
    }

      /* do we require any extended precision words ? */
      if (extended_precision)
    {
      rc = or_put_int (buf, extended_precision);
      if (rc != NO_ERROR)
        {
          return rc;
        }
    }

      if (extended_scale)
    {
      rc = or_put_int (buf, extended_scale);
      if (rc != NO_ERROR)
        {
          return rc;
        }
    }

      /* do we require a class OID ? */
      if (has_oid)
    {
      rc = or_put_oid (buf, &d->class_oid);
      if (rc != NO_ERROR)
        {
          return rc;
        }
    }

      if (has_enum)
    {
      rc = or_put_enumeration (buf, &DOM_GET_ENUMERATION (d));

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

      if (has_schema)
    {
      rc = or_put_json_validator (buf, d->json_validator);
      if (rc != NO_ERROR)
        {
          return rc;
        }
    }

      /*
       * Recurse on the sub domains if necessary, note that we don't
       * pass the NULL bit down here because that applies only to the
       * top level domain.
       */
      if (has_subdomain)
    {
      rc = or_put_domain (buf, d->setdomain, include_classoids, 0);
      if (rc != NO_ERROR)
        {
          return rc;
        }
    }
    }
  return rc;
}


/*
 * unpack_domain_2 - read a TP_DOMAIN from a buffer
 *    return: TP_DOMAIN read
 *    buf(in): input buffer
 *    is_null(out): set 1 if NULL domain
 */
static TP_DOMAIN *
unpack_domain_2 (OR_BUF * buf, int *is_null)
{
  TP_DOMAIN *domain, *last, *d;
  unsigned int carrier, precision, scale, codeset, has_classoid, has_setdomain, has_enum, collation_id,
    collation_storage;
  bool has_schema;
  bool more, auto_precision, is_desc, has_collation;
  DB_TYPE type;
  int index;
  int rc = NO_ERROR;
  unsigned char collation_flag;

  domain = last = NULL;

  more = true;
  while (more)
    {

      carrier = or_get_int (buf, &rc);
      if (rc != NO_ERROR)
    {
      goto error;
    }

      type = (DB_TYPE) (carrier & OR_DOMAIN_TYPE_MASK);

      /* check for the special NULL bit */
      if (is_null != NULL)
    {
      *is_null = ((carrier & OR_DOMAIN_NULL_FLAG) != 0);
    }

      /* Hack, check for references to built-in domains. */
      if (type == DB_TYPE_NULL && (carrier & OR_DOMAIN_BUILTIN_FLAG))
    {
      index = (carrier & OR_DOMAIN_PRECISION_MASK) >> OR_DOMAIN_PRECISION_SHIFT;
      /*
       * Recall that the builtin domain indexes are 1 based rather
       * than zero based, must adjust prior to indexing the table.
       */
      if (index < 1)
        {
          goto error;
        }
      domain = tp_domain_resolve_default ((DB_TYPE) (index - 1));
      /* stop the loop */
      more = false;
    }
      else
    {
      /* unpack a real domain */

      more = (carrier & OR_DOMAIN_NEXT_FLAG) ? true : false;
      precision = 0;
      scale = 0;
      codeset = 0;
      has_classoid = 0;
      has_setdomain = 0;
      has_enum = 0;
      has_schema = false;
      auto_precision = false;
      has_collation = false;

      if (carrier & OR_DOMAIN_DESC_FLAG)
        {
          is_desc = true;
        }
      else
        {
          is_desc = false;
        }

      switch (type)
        {
        case DB_TYPE_INTEGER:
        case DB_TYPE_SHORT:
        case DB_TYPE_BIGINT:
        case DB_TYPE_FLOAT:
        case DB_TYPE_DOUBLE:
        case DB_TYPE_DATE:
        case DB_TYPE_TIME:
        case DB_TYPE_TIMESTAMP:
        case DB_TYPE_TIMESTAMPTZ:
        case DB_TYPE_TIMESTAMPLTZ:
        case DB_TYPE_DATETIME:
        case DB_TYPE_DATETIMETZ:
        case DB_TYPE_DATETIMELTZ:
        case DB_TYPE_MONETARY:
          precision = tp_get_fixed_precision (type);
          break;

        case DB_TYPE_NUMERIC:
          precision = (carrier & OR_DOMAIN_PRECISION_MASK) >> OR_DOMAIN_PRECISION_SHIFT;
          scale = (carrier & OR_DOMAIN_SCALE_MASK) >> OR_DOMAIN_SCALE_SHIFT;
          break;

        case DB_TYPE_CHAR:
        case DB_TYPE_VARCHAR:
          has_collation = true;
          [[fallthrough]];
        case DB_TYPE_BIT:
        case DB_TYPE_VARBIT:
          codeset = (carrier & OR_DOMAIN_CODSET_MASK) >> OR_DOMAIN_CODSET_SHIFT;
          precision = (carrier & OR_DOMAIN_PRECISION_MASK) >> OR_DOMAIN_PRECISION_SHIFT;

          if (precision == 0)
        {
          precision = TP_FLOATING_PRECISION_VALUE;
          auto_precision = true;

          if (type == DB_TYPE_VARCHAR)
            {
              precision = DB_MAX_VARCHAR_PRECISION;
            }
          else if (type == DB_TYPE_VARBIT)
            {
              precision = DB_MAX_VARBIT_PRECISION;
            }
        }
          break;

        case DB_TYPE_OBJECT:
          has_classoid = carrier & OR_DOMAIN_CLASS_OID_FLAG;
          break;

        case DB_TYPE_SET:
        case DB_TYPE_MULTISET:
        case DB_TYPE_SEQUENCE:
        case DB_TYPE_TABLE:
        case DB_TYPE_MIDXKEY:
          has_setdomain = carrier & OR_DOMAIN_SET_DOMAIN_FLAG;
          if (type == DB_TYPE_MIDXKEY)
        {
          precision = (carrier & OR_DOMAIN_PRECISION_MASK) >> OR_DOMAIN_PRECISION_SHIFT;
        }
          break;

        case DB_TYPE_ENUMERATION:
          has_enum = carrier & OR_DOMAIN_ENUMERATION_FLAG;
          has_collation = ((carrier & OR_DOMAIN_ENUM_COLL_FLAG) == OR_DOMAIN_ENUM_COLL_FLAG);
          break;

        case DB_TYPE_JSON:
          has_schema = (carrier & OR_DOMAIN_SCHEMA_FLAG) != 0;
          break;

        default:
          break;
        }

      if (has_collation)
        {
          collation_storage = or_get_int (buf, &rc);
          if (rc != NO_ERROR)
        {
          goto error;
        }
          collation_id = collation_storage & OR_DOMAIN_COLLATION_MASK;

          if ((collation_storage & OR_DOMAIN_COLL_ENFORCE_FLAG) == OR_DOMAIN_COLL_ENFORCE_FLAG)
        {
          collation_flag = TP_DOMAIN_COLL_ENFORCE;
        }
          else if ((collation_storage & OR_DOMAIN_COLL_LEAVE_FLAG) == OR_DOMAIN_COLL_LEAVE_FLAG)
        {
          collation_flag = TP_DOMAIN_COLL_LEAVE;
        }
          else
        {
          collation_flag = TP_DOMAIN_COLL_NORMAL;
        }
        }
      else
        {
          collation_id = 0;
          collation_flag = TP_DOMAIN_COLL_NORMAL;
        }

      /* do we have an extra precision word ? */
      if (precision == OR_DOMAIN_PRECISION_MAX && !auto_precision)
        {
          precision = or_get_int (buf, &rc);
        }

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

      /* do we have an extra scale word ? */
      if (scale == OR_DOMAIN_SCALE_MAX)
        {
          scale = or_get_int (buf, &rc);
        }

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

      /* start building a transient domain */
      d = tp_domain_construct (type, NULL, precision, scale, NULL);
      if (d == NULL)
        {
          goto error;
        }
      if (last == NULL)
        {
          domain = last = d;
        }
      else
        {
          last->next = d;
          last = last->next;
        }

      /* do we have a class oid */
      if (has_classoid)
        {
          rc = or_get_oid (buf, &d->class_oid);
          if (rc != NO_ERROR)
        {
          goto error;
        }
#if !defined (SERVER_MODE)
          /* swizzle the pointer if we're on the client */
          d->class_mop = ws_mop (&d->class_oid, NULL);
#endif /* !SERVER_MODE */
        }

      /* store the codset if we had one */
      if (type == DB_TYPE_ENUMERATION)
        {
          if (has_collation)
        {
          /* collation id was read above, determine codeset */
          LANG_COLLATION *lc;
          lc = lang_get_collation (collation_id);
          assert (collation_id != LANG_COLL_ISO_BINARY);
          assert (lc != NULL);
          codeset = lc->codeset;
        }
          else
        {
          collation_id = LANG_COLL_ISO_BINARY;
          codeset = INTL_CODESET_ISO88591;
        }

          d->codeset = codeset;
          d->collation_id = collation_id;
          d->enumeration.collation_id = collation_id;
          d->collation_flag = (TP_DOMAIN_COLL_ACTION) collation_flag;
        }
      else
        {
          d->codeset = codeset;
          d->collation_id = collation_id;
          d->collation_flag = (TP_DOMAIN_COLL_ACTION) collation_flag;
        }

      if (has_enum)
        {
          rc = or_get_enumeration (buf, &DOM_GET_ENUMERATION (d));
          if (rc != NO_ERROR)
        {
          goto error;
        }
        }

      if (has_schema)
        {
          rc = or_get_json_validator (buf, d->json_validator);
          if (rc != NO_ERROR)
        {
          goto error;
        }
        }

      /*
       * Recurse to get set sub-domains if there are any, note that
       * we don't pass the is_null flag down here since NULLness only
       * applies to the top level domain.
       */
      if (has_setdomain)
        {
          d->setdomain = unpack_domain_2 (buf, NULL);
          if (d->setdomain == NULL)
        {
          goto error;
        }
        }

#if !defined (NDEBUG)
      if (type == DB_TYPE_MIDXKEY)
        {
          assert (d->precision > 0 && d->precision == tp_domain_size (d->setdomain));
        }
#endif /* NDEBUG */

      if (is_desc)
        {
          d->is_desc = 1;
        }
      else
        {
          d->is_desc = 0;
        }
    }
    }

  return domain;

error:
  if (domain != NULL)
    {
      TP_DOMAIN *td, *next;
      for (td = domain, next = NULL; td != NULL; td = next)
    {
      next = td->next;
      tp_domain_free (td);
    }
    }
  return NULL;
}


/*
 * unpack_domain - unpack disk representation of domain
 *    return: TP_DOMAIN structure
 *    buf(in/out): or buffer
 *    is_null(out): OR_DOMAIN_NULL_FLAG was on th packed domain?
 * Note:
 *    OR_DOMAIN_NULL_FLAG will normally only be set if this domain was
 *    used as a tag for a packed value.
 */
static TP_DOMAIN *
unpack_domain (OR_BUF * buf, int *is_null)
{
  TP_DOMAIN *domain, *last, *dom;
  TP_DOMAIN *setdomain, *td, *next;
  DB_TYPE type;
  bool more, is_desc;
  unsigned int carrier, index;
  unsigned int precision, scale, codeset = 0, collation_id;
  OID class_oid;
  struct db_object *class_mop = NULL;
  int rc = NO_ERROR;
  DB_ENUMERATION db_enum = { NULL, 0, 0 };
  unsigned int collation_storage;
  unsigned char collation_flag;

  domain = last = dom = setdomain = NULL;
  precision = scale = 0;

  char *schema_raw = NULL;

  more = true;
  while (more)
    {
      carrier = or_get_int (buf, &rc);
      if (rc != NO_ERROR)
    {
      goto error;
    }

      type = (DB_TYPE) (carrier & OR_DOMAIN_TYPE_MASK);

      /* check for the special NULL bit */
      if (is_null != NULL)
    {
      *is_null = ((carrier & OR_DOMAIN_NULL_FLAG) != 0);
    }

      /* Hack, check for references to built-in domains. */
      if (type == DB_TYPE_NULL && (carrier & OR_DOMAIN_BUILTIN_FLAG))
    {
      index = (carrier & OR_DOMAIN_PRECISION_MASK) >> OR_DOMAIN_PRECISION_SHIFT;
      /* Recall that the builtin domain indexes are 1 based rather than zero based, must adjust prior to indexing
       * the table. */
      domain = tp_domain_resolve_default ((DB_TYPE) (index - 1));
      if (domain == NULL)
        {
          goto error;
        }

      /* stop the loop */
      more = false;
    }
      else
    {
      /* unpack a real domain */
      more = (carrier & OR_DOMAIN_NEXT_FLAG) ? true : false;
      is_desc = (carrier & OR_DOMAIN_DESC_FLAG) ? true : false;

      collation_id = 0;
      collation_flag = TP_DOMAIN_COLL_NORMAL;

      switch (type)     /* try to find */
        {
        case DB_TYPE_INTEGER:
        case DB_TYPE_SHORT:
        case DB_TYPE_BIGINT:
        case DB_TYPE_FLOAT:
        case DB_TYPE_DOUBLE:
        case DB_TYPE_DATE:
        case DB_TYPE_TIME:
        case DB_TYPE_TIMESTAMP:
        case DB_TYPE_TIMESTAMPTZ:
        case DB_TYPE_TIMESTAMPLTZ:
        case DB_TYPE_DATETIME:
        case DB_TYPE_DATETIMETZ:
        case DB_TYPE_DATETIMELTZ:
        case DB_TYPE_MONETARY:
          precision = tp_get_fixed_precision (type);
          [[fallthrough]];

        case DB_TYPE_NULL:
        case DB_TYPE_BLOB:
        case DB_TYPE_CLOB:
          dom = tp_domain_find_noparam (type, is_desc);
          break;

        case DB_TYPE_NUMERIC:
          /* get precision and scale */
          precision = (carrier & OR_DOMAIN_PRECISION_MASK) >> OR_DOMAIN_PRECISION_SHIFT;
          scale = (carrier & OR_DOMAIN_SCALE_MASK) >> OR_DOMAIN_SCALE_SHIFT;
          /* do we have an extra precision word ? */
          if (precision == OR_DOMAIN_PRECISION_MAX)
        {
          precision = or_get_int (buf, &rc);
          if (rc != NO_ERROR)
            {
              goto error;
            }
        }
          /* do we have an extra scale word ? */
          if (scale == OR_DOMAIN_SCALE_MAX)
        {
          scale = or_get_int (buf, &rc);
          if (rc != NO_ERROR)
            {
              goto error;
            }
        }
          dom = tp_domain_find_numeric (type, precision, scale, is_desc);
          break;

        case DB_TYPE_CHAR:
        case DB_TYPE_VARCHAR:
          collation_storage = or_get_int (buf, &rc);
          if (rc != NO_ERROR)
        {
          goto error;
        }
          collation_id = collation_storage & OR_DOMAIN_COLLATION_MASK;

          if ((collation_storage & OR_DOMAIN_COLL_ENFORCE_FLAG) == OR_DOMAIN_COLL_ENFORCE_FLAG)
        {
          collation_flag = TP_DOMAIN_COLL_ENFORCE;
        }
          else if ((collation_storage & OR_DOMAIN_COLL_LEAVE_FLAG) == OR_DOMAIN_COLL_LEAVE_FLAG)
        {
          collation_flag = TP_DOMAIN_COLL_LEAVE;
        }
          else
        {
          collation_flag = TP_DOMAIN_COLL_NORMAL;
        }
          [[fallthrough]];

        case DB_TYPE_BIT:
        case DB_TYPE_VARBIT:
          codeset = ((carrier & OR_DOMAIN_CODSET_MASK) >> OR_DOMAIN_CODSET_SHIFT);
          precision = ((carrier & OR_DOMAIN_PRECISION_MASK) >> OR_DOMAIN_PRECISION_SHIFT);
          /* do we have an extra precision word ? */
          if (precision == OR_DOMAIN_PRECISION_MAX)
        {
          precision = or_get_int (buf, &rc);
          if (rc != NO_ERROR)
            {
              goto error;
            }
        }
          if (precision == 0)
        {
          /*
           * Kludge, restore maximum precision for the types that
           * aren't yet prepared for a -1.  This can be removed
           * eventually, see commentary in the or_put_domain.
           */
          if (type == DB_TYPE_VARCHAR)
            {
              precision = DB_MAX_VARCHAR_PRECISION;
            }
          else if (type == DB_TYPE_VARBIT)
            {
              precision = DB_MAX_VARBIT_PRECISION;
            }
          else
            {
              precision = TP_FLOATING_PRECISION_VALUE;
            }
        }
          dom = tp_domain_find_charbit (type, codeset, collation_id, collation_flag, precision, is_desc);
          break;

        case DB_TYPE_OBJECT:
          if (carrier & OR_DOMAIN_CLASS_OID_FLAG)
        {
          /* has classoid */
          rc = or_get_oid (buf, &class_oid);
          if (rc != NO_ERROR)
            {
              goto error;
            }
#if !defined (SERVER_MODE)
          /* swizzle the pointer if we're on the client */
          class_mop = ws_mop (&class_oid, NULL);
#endif /* !SERVER_MODE */
        }
          else
        {
          OID_SET_NULL (&class_oid);
          class_mop = NULL;
        }
          dom = tp_domain_find_object (type, &class_oid, class_mop, is_desc);
          break;

        case DB_TYPE_SET:
        case DB_TYPE_MULTISET:
        case DB_TYPE_SEQUENCE:
        case DB_TYPE_TABLE:
        case DB_TYPE_MIDXKEY:
          if (carrier & OR_DOMAIN_SET_DOMAIN_FLAG)
        {
          /* has setdomain */
          setdomain = unpack_domain_2 (buf, NULL);
          if (setdomain == NULL)
            {
              goto error;
            }
        }
          else
        {
          goto error;
        }

          if (type == DB_TYPE_MIDXKEY)
        {
          precision = (carrier & OR_DOMAIN_PRECISION_MASK) >> OR_DOMAIN_PRECISION_SHIFT;
        }

          dom = tp_domain_find_set (type, setdomain, is_desc);
          if (dom)
        {
          for (td = setdomain, next = NULL; td != NULL; td = next)
            {
              next = td->next;
              tp_domain_free (td);
            }
        }
          break;

        case DB_TYPE_ENUMERATION:
          if ((carrier & OR_DOMAIN_ENUM_COLL_FLAG) == OR_DOMAIN_ENUM_COLL_FLAG)
        {
          LANG_COLLATION *lc;
          collation_storage = or_get_int (buf, &rc);
          collation_id = collation_storage & OR_DOMAIN_COLLATION_MASK;

          if ((collation_storage & OR_DOMAIN_COLL_ENFORCE_FLAG) == OR_DOMAIN_COLL_ENFORCE_FLAG)
            {
              collation_flag = TP_DOMAIN_COLL_ENFORCE;
            }
          else if ((collation_storage & OR_DOMAIN_COLL_LEAVE_FLAG) == OR_DOMAIN_COLL_LEAVE_FLAG)
            {
              collation_flag = TP_DOMAIN_COLL_LEAVE;
            }
          else
            {
              collation_flag = TP_DOMAIN_COLL_NORMAL;
            }
          assert (collation_id != LANG_COLL_ISO_BINARY);
          if (rc != NO_ERROR)
            {
              goto error;
            }
          lc = lang_get_collation (collation_id);
          assert (lc != NULL);
          codeset = lc->codeset;
        }
          else
        {
          collation_id = LANG_COLL_ISO_BINARY;
          codeset = INTL_CODESET_ISO88591;
        }

          db_enum.collation_id = collation_id;

          if (carrier & OR_DOMAIN_ENUMERATION_FLAG)
        {
          rc = or_get_enumeration (buf, &db_enum);
          if (rc != NO_ERROR)
            {
              goto error;
            }
          dom = tp_domain_find_enumeration (&db_enum, is_desc);
          if (dom != NULL)
            {
              /* we have to free the memory allocated for the enum above since we already have it cached */
              tp_domain_clear_enumeration (&db_enum);
            }
        }
          break;

        case DB_TYPE_JSON:
          if ((carrier & OR_DOMAIN_SCHEMA_FLAG) != 0)
        {
          rc = or_get_json_schema (buf, schema_raw);
          if (rc != NO_ERROR)
            {
              goto error;
            }
          or_align (buf, OR_INT_SIZE);
          assert (er_errid () == NO_ERROR);
        }
          break;

        default:
          break;
        }

      if (dom == NULL)
        {
          /* not found. need to construct one */
          dom = tp_domain_construct (type, NULL, precision, scale, setdomain);
          if (dom == NULL)
        {
          goto error;
        }
          DOM_SET_ENUM_ELEMENTS (dom, db_enum.elements);
          DOM_SET_ENUM_ELEMS_COUNT (dom, db_enum.count);

          switch (type)
        {
        case DB_TYPE_JSON:
          if (schema_raw != NULL)
            {
              rc = db_json_load_validator (schema_raw, dom->json_validator);
              db_private_free_and_init (NULL, schema_raw);
              if (rc != NO_ERROR)
            {
              ASSERT_ERROR ();
              goto error;
            }
            }
          else
            {
              dom->json_validator = NULL;
            }
          break;
        case DB_TYPE_CHAR:
        case DB_TYPE_VARCHAR:
          dom->collation_id = collation_id;
          dom->collation_flag = (TP_DOMAIN_COLL_ACTION) collation_flag;
          [[fallthrough]];
        case DB_TYPE_BIT:
        case DB_TYPE_VARBIT:
          dom->codeset = codeset;
          break;
        case DB_TYPE_OBJECT:
          COPY_OID (&dom->class_oid, &class_oid);
#if !defined (SERVER_MODE)
          dom->class_mop = class_mop;
#endif /* !SERVER_MODE */
          break;
        case DB_TYPE_ENUMERATION:
          dom->collation_id = collation_id;
          dom->enumeration.collation_id = collation_id;
          dom->codeset = codeset;
          dom->collation_flag = (TP_DOMAIN_COLL_ACTION) collation_flag;
        default:
          break;
        }

#if !defined (NDEBUG)
          if (type == DB_TYPE_MIDXKEY)
        {
          assert (dom->precision > 0 && dom->precision == tp_domain_size (dom->setdomain));
        }
#endif /* NDEBUG */

          if (is_desc)
        {
          dom->is_desc = 1;
        }
          dom = tp_domain_cache (dom);
        }

#if !defined (NDEBUG)
      if (type == DB_TYPE_MIDXKEY)
        {
          assert (dom->precision > 0 && dom->precision == tp_domain_size (dom->setdomain));
        }
#endif /* NDEBUG */

      if (last == NULL)
        {
          domain = last = dom;
        }
      else
        {
          last->next = dom;
          last = last->next;
        }
    }
    }

  return domain;

error:
  if (domain != NULL)
    {
      for (td = domain, next = NULL; td != NULL; td = next)
    {
      next = td->next;
      tp_domain_free (td);
    }
    }
  return NULL;
}

/*
 * or_get_domain - unpacks a domain from a buffer and returns a cached domain.
 *    return: cached domain or NULL for error
 *    buf(in/out): or buffer
 *    caller_dom(in):
 *    is_null(out): OR_DOMAIN_NULL_FLAG was on in the packed domain?
 */
TP_DOMAIN *
or_get_domain (OR_BUF * buf, TP_DOMAIN * caller_dom, int *is_null)
{
  TP_DOMAIN *domain;

  if (caller_dom)
    {
      domain = unpack_domain_2 (buf, is_null);
      if (tp_domain_match (domain, caller_dom, TP_SET_MATCH))
    {
      tp_domain_free (domain);
      domain = caller_dom;
    }
      else if (domain != NULL && !domain->is_cached)
    {
      domain = tp_domain_cache (domain);
    }
    }
  else
    {
      domain = unpack_domain (buf, is_null);
      if (domain != NULL && !domain->is_cached)
    {
      domain = tp_domain_cache (domain);
    }
    }
  return domain;
}

/*
 * or_pack_domain - creates the packed "disk" representation of a domain
 *    return: advanced pointer
 *    ptr(out): output buffer
 *    domain(in): domain to pack
 *    include_classoids(in): non-zero if we're supposed to save class OIDs
 *    is_null(in): use domain tags for "NULL" value
 *
 * Note:
 *    Alternate interface for or_put_domain, see that function
 *    for more information.
 */
char *
or_pack_domain (char *ptr, TP_DOMAIN * domain, int include_classoids, int is_null)
{
  OR_BUF buf;
  int rc = 0;

  or_init (&buf, ptr, 0);
  rc = or_put_domain (&buf, domain, include_classoids, is_null);
  if (rc == NO_ERROR)
    {
      return buf.ptr;
    }
  else
    {
      return NULL;
    }
}

/*
 * or_unpack_domain - alternative interfaceto or_get_domain
 *    return: advanced buffer pointer
 *    ptr(in): pointer to buffer
 *    domain_ptr(out): pointer to domain
 *    is_null(out): use domain tags for "NULL" value
 */
char *
or_unpack_domain (char *ptr, struct tp_domain **domain_ptr, int *is_null)
{
  OR_BUF buf;
  TP_DOMAIN *domain;

  or_init (&buf, ptr, 0);

  domain = or_get_domain (&buf, NULL, is_null);
  if (domain_ptr != NULL)
    {
      *domain_ptr = domain;
    }

  return buf.ptr;
}

/*
 * or_put_sub_domain - put DB_TYPE_SUB field to buffer
 *    return: NO_ERROR or error code
 *    buf(in/out): or buffer
 * Note:
 *    This is a kludge until we have a more general mechanism for dealing
 *    with "substrutcure" domains.
 *    These are used in the disk representation of the class and indicate
 *    the presence of nested instances, similar in theory to the ADT concept.
 *    We've stored classes like this for some time, eventually this can
 *    be replaced by true ADT's when they come on-line but hopefully the
 *    representation will be identical.
 *    We use this function to avoid having to actually create a bunch of
 *    built-in domains for the meta classes though that wouldn't be all that
 *    hard to add to the tp_Domain array.
 */
int
or_put_sub_domain (OR_BUF * buf)
{
  unsigned int carrier;

  carrier = (DB_TYPE_SUB & OR_DOMAIN_TYPE_MASK);
  return (or_put_int (buf, carrier));
}

/*
 *  SET PACKING
 */
/*
 * or_packed_set_info - looks at the domain of the set and determines the
 * close to optimal storage configuration for it.
 *    return: void
 *    set_type(in): basic type of the set
 *    domain(in): full domain of the set (optional)
 *    include_domain(in): non-zero if the caller wants the domain in the set
 *    bound_bits(out): set to 1 if the set is homogeneous
 *    offset_table(out): set to the fixed width element size (-1 if variable)
 *    element_tags(out): set to 1 if bound bits required
 *    element_size(out): set to 1 if offset table is required
 *
 * Note:
 *    This looks at the domain of the set and determines the close to
 *    optimal storage configuration for it.  There is some room for
 *    interpretation here, and in fact, we can completely ignore the
 *    domain and store set in their most general representation all the
 *    time if there seems to be some problem dealing with compressed sets.
 *    This may not need to be a public function, it is only called by
 *    or_put_set and or_packed_set_size.
 */
void
or_packed_set_info (DB_TYPE set_type, TP_DOMAIN * domain, int include_domain, int *bound_bits, int *offset_table,
            int *element_tags, int *element_size)
{
  TP_DOMAIN *element_domain;
  int homogeneous;


  /*
   * A set can be of fixed width only if the domain is fully specified and there
   * is only one fixed width data type in the set.
   * Note that for "attached" sets that may be fixed width, the domain must
   * have been assigned by now for us to determine this, if not, we punt
   * and assume its a variable width set.
   */

  /*
   * might only need bother with offset tables if this is an indexable
   * sequence ?
   */
  homogeneous = 0;
  *element_tags = 1;
  *element_size = -1;
  *bound_bits = 0;
  *offset_table = 0;

  if (domain != NULL)
    {
      element_domain = domain->setdomain;
      if (element_domain != NULL && element_domain->next == NULL)
    {
      /* set can contain only one type of thing */
      homogeneous = 1;
      /* returns -1 if this is a variable width thing */
      *element_size = tp_domain_disk_size (element_domain);
    }
    }

  if (homogeneous)
    {
      if (*element_size >= 0)
    {
      *bound_bits = 1;
    }
      else
    {
      *offset_table = 1;
    }
    }
  else
    {
      *offset_table = 1;
    }

  /*
   * Determine if we need to tag each value with its domain.
   * Normally, one would tag the elements if the domain is being excluded
   * from the set, but we'll allow it and assume that it will be passed
   * to the or_get_set function.
   */
  *element_tags = !homogeneous; /* || !include_domain */

  /*
   * If we have to have element tags, then don't bother with a bound
   * bit array.
   */
  if (*element_tags)
    {
      *bound_bits = 0;
    }
}

/*
 * or_put_set_header - write a set header containing the indicated information.
 *    return: NO_ERROR or return code
 *    buf(in/out): or buffer
 *    set_type(in): basic type of the set
 *    size(in): number of elements in the set
 *    domain(in): non-zero if a domain will be packed
 *    bound_bits(in): non-zero if a bound bit vector will be packed
 *    offset_table(in): non-zero if an offset table will be packed
 *    element_tags(in): non-zero if elements tags will be included
 *    common_sub_header(in): non-zero if substructure tags will be included
 * Note:
 *    This hides basically just hides the implementation of the header
 *    word and flag constants so we can control who gets to see this.
 *    Common sub_header is used only for class objects currently.
 *
 */
int
or_put_set_header (OR_BUF * buf, DB_TYPE set_type, int size, int domain, int bound_bits, int offset_table,
           int element_tags, int common_sub_header)
{
  unsigned int header;
  int rc = NO_ERROR;

  header = set_type & 0xFF;

  if (offset_table)
    {
      header |= OR_SET_VARIABLE_BIT;
    }
  else if (bound_bits)
    {
      header |= OR_SET_BOUND_BIT;
    }

  if (domain)
    {
      header |= OR_SET_DOMAIN_BIT;
    }
  if (element_tags)
    {
      header |= OR_SET_TAG_BIT;
    }
  if (common_sub_header)
    {
      header |= OR_SET_COMMON_SUB_BIT;
    }
  rc = or_put_int (buf, header);

  if (rc == NO_ERROR)
    {
      rc = or_put_int (buf, size);
    }

  return rc;
}

/*
 * or_get_set_header - get set header from buffer
 *    return: ER_SUCCSSS or error code
 *    buf(in/out): or buffer
 *    set_type(out): set to the basic set type
 *    size(out): set to the element count
 *    domain(out): set non-zero if there will be a domain
 *    bound_bits(out): set non-zero if there will be bound bits
 *    offset_table(out): set non-zero if there will be an offset table
 *    element_tags(out): set non-zero if there will be element tags
 *    common_sub(out): set non-zero if there will be substructure tags
 */
int
or_get_set_header (OR_BUF * buf, DB_TYPE * set_type, int *size, int *domain, int *bound_bits, int *offset_table,
           int *element_tags, int *common_sub)
{
  unsigned int header;
  int rc = NO_ERROR;

  header = or_get_int (buf, &rc);
  if (rc == NO_ERROR)
    {
      *set_type = (DB_TYPE) (header & OR_SET_TYPE_MASK);
      *domain = ((header & OR_SET_DOMAIN_BIT) != 0);
      *bound_bits = ((header & OR_SET_BOUND_BIT) != 0);
      *offset_table = ((header & OR_SET_VARIABLE_BIT) != 0);
      *element_tags = ((header & OR_SET_TAG_BIT) != 0);
      if (common_sub != NULL)
    {
      *common_sub = ((header & OR_SET_COMMON_SUB_BIT) != 0);
    }
      *size = or_get_int (buf, &rc);
    }
  return rc;
}

/*
 * or_skip_set_header - skip over the set header
 *    return: number of elements in set
 *    buf(in/out): or buffer
 *
 * Note:
 *    Used only by the class loader since it knows what the type
 *    of the set is.
 */
int
or_skip_set_header (OR_BUF * buf)
{
  DB_TYPE set_type;
  int count, length, rc = NO_ERROR;
  int domain, bound_bits, offset_table, element_tags, sub_header;

  or_get_set_header (buf, &set_type, &count, &domain, &bound_bits, &offset_table, &element_tags, &sub_header);

  if (offset_table)
    {
      or_advance (buf, OR_VAR_TABLE_SIZE (count));
    }

  else if (bound_bits)
    {
      or_advance (buf, OR_BOUND_BIT_BYTES (count));
    }

  if (domain)
    {
      length = or_get_int (buf, &rc);
      or_advance (buf, length);
    }

  if (sub_header)
    {
      or_advance (buf, OR_SUB_HEADER_SIZE);
    }

  return count;
}

/*
 * or_packed_set_length - Calculates the disk size of a set
 *    return: disk length of the set
 *    set(in): pointer to an internal set object (not a set reference)
 *    include_domain(in): non-zero if the caller wants the domain in the set
 *
 *    Note:
 *    the length returned here will match the representation
 *    created by or_put_set() only.  There are some other set packers
 *    floating around that represent sets as arrays of packed DB_VALUES,
 *    these are not necessarily the same size.
 *
 *    The include_domain flag is set if the set is to be packed with a full
 *    domain description.  This is normally off when the set is inside
 *    an object since the domain can be determined from the class.
 *    When "free" sets are packed in list files or as elements of nested
 *    sets, the domain should be included.
 *    This distinction may be somewhat difficult to make in the presence
 *    of nested sets.  Consider instead leaving the domain specified but
 *    don't pack the class OIDs of object domains.
 */
int
or_packed_set_length (setobj * set, int include_domain)
{
  DB_VALUE *value = NULL;
  int len, element_size, bound_bits, offset_table, element_tags, i, bits;
  int set_size;
  TP_DOMAIN *set_domain;
  DB_TYPE set_type;
  int error;

  len = 0;
  if (set == NULL)
    {
      return 0;
    }

  set_size = setobj_size (set);
  set_type = setobj_type (set);
  set_domain = setobj_domain (set);

  /* Determine storage characteristics based on the domain */
  or_packed_set_info (set_type, set_domain, include_domain, &bound_bits, &offset_table, &element_tags, &element_size);

  len = OR_SET_HEADER_SIZE;

  if (offset_table)
    {
      len += OR_VAR_TABLE_SIZE (set_size);
    }
  else if (bound_bits)
    {
      len += OR_BOUND_BIT_BYTES (set_size);
    }

  if (set_domain != NULL && include_domain)
    {
      len += OR_INT_SIZE;
      len += or_packed_domain_size (set_domain, 0);
    }

  /*
   * If we have a non-tagged fixed width set, can calculate the size without
   * mapping over the values.
   */
  if (bound_bits)
    {
      len += element_size * set_size;
    }
  else
    {

      for (i = 0; i < set_size; i++)
    {
      error = setobj_get_element_ptr (set, i, &value);

      /* Second argument indicates whether to "collapse_null" values into nothing.  - can do this only if there is
       * an offset table. Third argument indicates whether or not to include the domain which - we do if the values
       * are tagged. Fourth argument indicates the desire to pack class OIDs which we never do since these are tag
       * domains. */
      len += or_packed_value_size (value, offset_table, element_tags, 0);
      if (offset_table)
        {
          bits = len & 3;
          if (bits)
        {
          len += (4 - bits);
        }
        }
    }
    }

  /* always pad out a packed set to a word boundary */
  bits = len & 3;
  if (bits)
    {
      len += (4 - bits);
    }

  return len;
}

/*
 * or_put_set - primary function for building the disk representation
 * of a set.
 *    return: void
 *    buf(in/out): or buffer
 *    set(in): set object to encode
 *    include_domain(in): non-zero to store full set domain too
 */
void
or_put_set (OR_BUF * buf, setobj * set, int include_domain)
{
  DB_VALUE *value = NULL;
  unsigned int bound_word;
  int element_tags, element_size, bound_bits, offset_table;
  char *set_start, *element_start, *offset_ptr, *bound_ptr;
  int i, offset, bit = 0, len, is_null, length, bits;
  TP_DOMAIN *set_domain;
  DB_TYPE set_type;
  int set_size;
  int error;

  if (set == NULL)
    {
      return;
    }

  /* only pay attention to this if we actually have a domain to store */
  set_domain = setobj_domain (set);
  set_type = setobj_type (set);
  set_size = setobj_size (set);

  if (set_domain == NULL)
    {
      include_domain = 0;
    }

  /* determine storage characteristics based on the domain */
  set_start = buf->ptr;
  or_packed_set_info (set_type, set_domain, include_domain, &bound_bits, &offset_table, &element_tags, &element_size);

  or_put_set_header (buf, set_domain ? TP_DOMAIN_TYPE (set_domain) : set_type, set_size, include_domain, bound_bits,
             offset_table, element_tags, 0);


  /* reserve space for the offset table or bound bit vector if necessary */
  offset_ptr = NULL;
  bound_ptr = NULL;
  bound_word = 0;
  if (set_size)
    {
      if (offset_table)
    {
      offset_ptr = buf->ptr;
      len = OR_VAR_TABLE_SIZE (set_size);
      or_advance (buf, len);
    }
      else if (bound_bits)
    {
      bound_ptr = buf->ptr;
      len = OR_BOUND_BIT_BYTES (set_size);
      or_advance (buf, len);
    }
    }

  /* write the domain if necessary, don't include the class OID */
  if (include_domain)
    {
      or_put_int (buf, or_packed_domain_size (set_domain, 0));
      or_put_domain (buf, set_domain, 0, 0);
    }

  /* stop if we don't have any elements */
  if (set_size)
    {

      /* calculate the offset to the first value (in case we're building an offset table) */
      offset = (int) (buf->ptr - set_start);

      /* iterate over the values */
      for (i = 0; i < set_size; i++)
    {
      error = setobj_get_element_ptr (set, i, &value);

      /*
       * make an entry in the offset table or bound bit array if we
       * have them
       */
      is_null = 0;
      if (offset_ptr != NULL)
        {
          /* offset table entry */
          OR_PUT_OFFSET (offset_ptr, offset);
          offset_ptr += BIG_VAR_OFFSET_SIZE;
        }
      else if (bound_ptr != NULL)
        {
          bit = i & 0x1F;
          if (value != NULL && DB_VALUE_TYPE (value) != DB_TYPE_NULL)
        {
          bound_word |= 1L << bit;
        }
          else
        {
          is_null = 1;
        }
          if (bit == 0x1F)
        {
          OR_PUT_INT (bound_ptr, bound_word);
          bound_ptr += OR_INT_SIZE;
          bound_word = 0;
        }
        }

      /*
       * Write the value.  Be careful with NULLs in fixed width sets, need
       * to leave space.
       */
      element_start = buf->ptr;

      if (bound_ptr != NULL && is_null)
        {
          /*
           * Could just use or_advance here but lets be nice and
           * zero out the space for debugging.
           */
          or_pad (buf, element_size);
        }
      else
        {
          /* Third argument indicates whether to "collapse_null" values into nothing.  - can do this only if there
           * is an offset table. Fourth argument indicates whether or not to include the domain which we do if the
           * values are tagged. Fifth argument indicates the desire to pack class OIDs which we never do since
           * these are tag domains. */
          or_put_value (buf, value, offset_table, element_tags, 0);
        }

      if (offset_table)
        {
          length = CAST_BUFLEN (buf->ptr - element_start);
          bits = length & 3;
          if (bits)
        {
          or_pad (buf, 4 - bits);
        }
        }

      offset += (int) (buf->ptr - element_start);
    }

      /* store the ending offset in the table if we're using one */
      if (offset_ptr != NULL)
    {
      OR_PUT_OFFSET (offset_ptr, offset);
    }

      if (bound_ptr != NULL && bit != 0x1f)
    {
      OR_PUT_INT (bound_ptr, bound_word);
    }
    }

  /* always pad out a packed set to a word boundary */
  length = CAST_BUFLEN (buf->ptr - set_start);
  bits = length & 3;
  if (bits)
    {
      or_pad (buf, 4 - bits);
    }
}

/*
 * or_get_set - eads the stored representation of a set and builds the
 *    corresponding memory represenation.
 *    return: internal set object
 *    buf(in/out): or buffer
 *    domain(in): expected domain (optional)
 * Note:
 *    The domain argument is required only if the domain set was packed
 *    explicitly without a domain and the elements are not tagged.  In that
 *    case, the supplied domain must be used to interpret the element
 *    format.  It better be right.  This is really only used for the
 *    stored values of attributes since we can always get the correct
 *    domain by looking in the catalog.
 */
setobj *
or_get_set (OR_BUF * buf, TP_DOMAIN * domain)
{
  SETOBJ *set;
  DB_VALUE value;
  TP_DOMAIN *element_domain;
  DB_TYPE set_type;
  int size, fixed_element_size, element_size, offset, offset2, bit, i;
  int length, bits;
  unsigned int bound_word;
  char *offset_ptr, *bound_ptr;
  char *set_start;
  int has_domain, bound_bits, offset_table, element_tags;
  TP_DOMAIN *set_domain;
  int rc = NO_ERROR;

  set_start = buf->ptr;

  /* read the set header and decompose the various flags */
  or_get_set_header (buf, &set_type, &size, &has_domain, &bound_bits, &offset_table, &element_tags, NULL);

  set = setobj_create (set_type, size);
  if (set == NULL)
    {
      ASSERT_ERROR ();
      return NULL;
    }

  /*
   * If a domain was supplied, stick it in the set, probably should do a
   * sanity check in this and the doamin stored in the set.  The domain
   * MUST be passed if the set was packed with the "include_domain" domain
   * flag at zero.
   */
  setobj_put_domain (set, domain);

  /* prepare for an offset table or bound bit array */
  offset_ptr = NULL;
  bound_ptr = NULL;
  offset = 0;
  bound_word = 0;
  if (offset_table)
    {
      offset_ptr = buf->ptr;
      or_advance (buf, OR_VAR_TABLE_SIZE (size));
    }
  else if (bound_bits)
    {
      bound_ptr = buf->ptr;
      or_advance (buf, OR_BOUND_BIT_BYTES (size));
    }

  if (has_domain)
    {
      (void) or_get_int (buf, &rc); /* skip the domain size */
      /* the domain returned here will be cached */
      setobj_put_domain (set, or_get_domain (buf, domain, NULL));
      /* If this is stored and the caller has supplied one as an argument to this function, they should be the same.
       * Might want to check this here. */
    }

  /*
   * Calculate the length of the fixed width elements if that's what we have.
   * This looks like it should be a little utilitiy function.
   */
  element_domain = NULL;
  fixed_element_size = -1;
  set_domain = setobj_domain (set);

  if (set_domain != NULL && set_domain->setdomain != NULL && set_domain->setdomain->next == NULL)
    {
      /* we only have one possible element domain */
      fixed_element_size = tp_domain_disk_size (set_domain->setdomain);
      element_domain = set_domain->setdomain;
    }

  if (size)
    {
      /* read the first offset or bound word */
      if (offset_table)
    {
      offset = OR_GET_OFFSET (offset_ptr);
      offset_ptr += BIG_VAR_OFFSET_SIZE;
    }
      else if (bound_bits)
    {
      bound_word = OR_GET_INT (bound_ptr);
    }

      /* loop over each element */
      for (i = 0; i < size; i++)
    {

      /* determine the length of the element if there is an offset table */
      element_size = -1;
      if (offset_ptr != NULL)
        {
          offset2 = OR_GET_OFFSET (offset_ptr);
          offset_ptr += BIG_VAR_OFFSET_SIZE;
          element_size = offset2 - offset;
          offset = offset2;
        }
      else if (bound_ptr != NULL)
        {
          element_size = fixed_element_size;    /* this must be fixed width! */
          bit = i & 0x1F;
          if ((bound_word & (1L << bit)) == 0)
        {
          element_size = 0; /* its NULL */
        }
          if (bit == 0x1F)
        {
          bound_ptr += OR_INT_SIZE;
          bound_word = OR_GET_INT (bound_ptr);
        }
        }

      /*
       * 8 element_size will now be 0 if NULL, the true size, or -1 if * variable or unknown. */

      /* Read the element. */
      if (element_size == 0)
        {
          /*
           * we have to initlaize the domain too, since a set can
           * have several possible domains, just pick the first one.
           * Actually,for wildcard sets, we won't have a domain to select.
           * Since I guess the NULL "domain" can logically be a part of all
           * sets, initialize the domain for NULL.
           */
          db_value_domain_init (&value, DB_TYPE_NULL, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
          db_make_null (&value);
          /*
           * if this is a fixed width element array, skip over the null
           * data
           */
          if (bound_ptr != NULL)
        {
          or_advance (buf, fixed_element_size);
        }
        }
      else
        {
          /*
           * read a packed value, pass the domain only if the
           * values are not tagged already tagged.
           */
          if (element_tags)
        {
          or_get_value (buf, &value, NULL, element_size, true);
        }
          else
        {
          or_get_value (buf, &value, element_domain, element_size, true);
        }
        }

      /*
       * This setobj interface function passes "ownership" of the memory
       * of value to the set. value need not be cleared after this call,
       * as its internal memory pointers are copied directly to the set
       * This function should only be used in construction of internal
       * setobj structure from temporary DB_VALUE's.
       */
      if (setobj_put_value (set, i, &value) != NO_ERROR)
        {
          /* if value not added to set, clear it */
          pr_clear_value (&value);
        }
    }
    }

  /* sets are always paded, cosume the padding */
  length = (int) (buf->ptr - set_start);
  bits = length & 3;
  if (bits)
    {
      or_advance (buf, 4 - bits);
    }

  return set;
}

/*
 * or_disk_set_size - determine set length.
 *    return: set length
 *    buf(in/out): or buffer
 *    set_domain(in/out):
 *    set_type(out): set type
 * Note:
 *    This routine will leave the OR_BUF unaltered (pointing to the beginning
 *    of the set.
 */
int
or_disk_set_size (OR_BUF * buf, TP_DOMAIN * set_domain, DB_TYPE * set_type)
{
  TP_DOMAIN *element_domain;
  DB_TYPE disk_set_type;
  int size, fixed_element_size, element_size, offset, offset2, bit;
  int length, bits, i, total_length;
  unsigned int bound_word;
  char *offset_ptr, *bound_ptr;
  char *set_start;
  int has_domain, bound_bits, offset_table, element_tags;
  int rc = NO_ERROR;

  total_length = 0;
  set_start = buf->ptr;

  /* read the set header and decompose the various flags */
  or_get_set_header (buf, &disk_set_type, &size, &has_domain, &bound_bits, &offset_table, &element_tags, NULL);

  if (set_type)
    {
      *set_type = disk_set_type;
    }

  /* prepare for an offset table or bound bit array */
  offset_ptr = NULL;
  bound_ptr = NULL;
  offset = 0;
  bound_word = 0;

  if (offset_table)
    {
      offset_ptr = buf->ptr;
      or_advance (buf, OR_VAR_TABLE_SIZE (size));
    }
  else if (bound_bits)
    {
      bound_ptr = buf->ptr;
      or_advance (buf, OR_BOUND_BIT_BYTES (size));
    }

  if (has_domain)
    {
      (void) or_get_int (buf, &rc); /* skip the domain size */
      /* we have to unpack the domain */
      set_domain = or_get_domain (buf, set_domain, NULL);
    }

  /*
   * Calculate the length of the fixed width elements if that's what we have.
   * This looks like it should be a little utilitiy function.
   */
  element_domain = NULL;
  fixed_element_size = -1;
  if (set_domain != NULL && set_domain->setdomain != NULL && set_domain->setdomain->next == NULL)
    {
      /* we only have one possible element domain */
      fixed_element_size = tp_domain_disk_size (set_domain->setdomain);
      element_domain = set_domain->setdomain;
    }

  if (size)
    {
      /* read the first offset or bound word */
      if (offset_table)
    {
      offset = OR_GET_OFFSET (offset_ptr);
      offset_ptr += BIG_VAR_OFFSET_SIZE;
    }
      else if (bound_bits)
    {
      bound_word = OR_GET_INT (bound_ptr);
    }

      /* loop over each element */
      for (i = 0; i < size; i++)
    {
      /* determine the length of the element if there is an offset table */
      element_size = -1;
      if (offset_ptr != NULL)
        {
          offset2 = OR_GET_OFFSET (offset_ptr);
          offset_ptr += BIG_VAR_OFFSET_SIZE;
          element_size = offset2 - offset;
          offset = offset2;
        }
      else if (bound_ptr != NULL)
        {
          element_size = fixed_element_size;    /* this must be fixed width! */
          bit = i & 0x1F;
          if ((bound_word & (1L << bit)) == 0)
        {
          element_size = 0; /* its NULL */
        }

          if (bit == 0x1F)
        {
          bound_ptr += OR_INT_SIZE;
          bound_word = OR_GET_INT (bound_ptr);
        }
        }

      /*
       * element_size will now be 0 if NULL, the true size, or -1 if
       * variable or unknown.
       */

      /*
       * Skip the element, we may have to actually unpack the element
       * to do this (if the size is variable), but no storage should be
       * allocated.
       */
      if (element_size == 0)
        {
          /*
           * if this is a fixed width element array, skip over the null
           * data
           */
          if (bound_ptr != NULL)
        {
          or_advance (buf, fixed_element_size);
        }
        }
      else if (element_size != -1)
        {
          /* in this case we can simply skip the element */
          or_advance (buf, element_size);
        }
      else
        {
          /* skip a packed value, pass the domain only if the values are not already tagged.  The NULL db_value
           * tells the routine to skip the value rather than actually unpack it into a db_value container. */
          if (element_tags)
        {
          or_get_value (buf, NULL, NULL, element_size, true);
        }
          else
        {
          or_get_value (buf, NULL, element_domain, element_size, true);
        }
        }
    }
    }

  /* sets are always paded, consume the padding */
  length = (int) (buf->ptr - set_start);
  bits = length & 3;
  if (bits)
    {
      or_advance (buf, 4 - bits);
    }

  total_length = (int) (buf->ptr - set_start);

  /* reset the OR_BUF so that it looks like we didn't do anything */
  buf->ptr = set_start;

  return total_length;
}

/*
 * VALUE PACKING
 */

/*
 * or_packed_value_size - calculating the size of the packed representation of
 * a value.
 *    return: packed size in bytes
 *    value(in): pointer to value
 *    collapse_null(in): non-zero to "collapse" null values
 *    include_domain(in): non-zero to include a domain tag
 *    include_domain_classoids(in): non-zero to include the domain class OIDs
 */
int
or_packed_value_size (const DB_VALUE * value, int collapse_null, int include_domain, int include_domain_classoids)
{
  const PR_TYPE *type;
  TP_DOMAIN *domain;
  int size = 0, bits;
  DB_TYPE dbval_type;

  if (value == NULL)
    {
      return 0;
    }

  dbval_type = DB_VALUE_DOMAIN_TYPE (value);
  type = pr_type_from_id (dbval_type);

  if (type == NULL)
    {
      return 0;
    }

  /* If the value is NULL, either pack nothing or pack the domain with the special null flag enabled. */
  if (DB_VALUE_TYPE (value) == DB_TYPE_NULL)
    {
      if (!collapse_null || include_domain)
    {
      domain = tp_domain_resolve_value (value, NULL);
      if (domain != NULL)
        {
          size = or_packed_domain_size (domain, include_domain_classoids);
        }
      else
        {
          size = or_packed_domain_size (&tp_Null_domain, 0);
        }
    }
    }
  else
    {
      if (include_domain)
    {
      domain = tp_domain_resolve_value (value, NULL);
      if (domain != NULL)
        {
          size = or_packed_domain_size (domain, include_domain_classoids);
        }
      else
        {
          /* shouldn't get here ! */
          size = or_packed_domain_size (&tp_Null_domain, 0);
          return size;
        }
    }
      size += type->get_disk_size_of_value (value);
    }

  /* Values must as a unit be aligned to a word boundary.  We can't do this inside the writeval function because that
   * may be used to place data inside disk structures that don't have alignment requirements. */
  if (include_domain)
    {
      bits = size & 3;
      if (bits)
    {
      size += (4 - bits);
    }
    }

  return size;
}


/*
 * or_put_value - pack a value
 *    return: error on overflow
 *    buf(out): packing buffer
 *    value(in): value to ponder
 *    collapse_null(in): non-zero to "collapse" null values
 *    include_domain(in): non-zero to include a domain tag
 *    include_domain_classoids(in): non-zero to include the domain class OIDs
 */
int
or_put_value (OR_BUF * buf, DB_VALUE * value, int collapse_null, int include_domain, int include_domain_classoids)
{
  const PR_TYPE *type;
  TP_DOMAIN *domain;
  char *start, length, bits;
  int rc = NO_ERROR;
  DB_TYPE dbval_type;

  if (value == NULL)
    {
      return ER_FAILED;
    }

  dbval_type = DB_VALUE_DOMAIN_TYPE (value);
  type = pr_type_from_id (dbval_type);

  if (type == NULL)
    {
      return ER_FAILED;
    }

  start = buf->ptr;

  if (DB_VALUE_TYPE (value) == DB_TYPE_NULL)
    {
      if (!collapse_null || include_domain)
    {
      domain = tp_domain_resolve_value (value, NULL);
      if (domain != NULL)
        {
          rc = or_put_domain (buf, domain, include_domain_classoids, 1);
        }
      else
        {
          /* shouldn't get here */
          rc = or_put_domain (buf, &tp_Null_domain, 0, 1);
        }
    }
    }
  else
    {
      if (include_domain)
    {
      domain = tp_domain_resolve_value (value, NULL);
      if (domain != NULL)
        {
          rc = or_put_domain (buf, domain, include_domain_classoids, 0);
        }
      else
        {
          /* shouldn't get here */
          rc = or_put_domain (buf, &tp_Null_domain, 0, 1);
          return NO_ERROR;
        }
    }
      /* probably should blow off writing the value if we couldn't determine the domain ? */
      if (rc == NO_ERROR)
    {
      rc = type->data_writeval (buf, value);
    }
    }

  /*
   * Values must as a unit be aligned to a word boundary.  We can't do this
   * inside the writeval function becaue that may be used to place data inside
   * disk structures that don't have alignment requirements.
   */
  if (rc == NO_ERROR)
    {
      if (include_domain)
    {
      length = (int) (buf->ptr - start);
      bits = length & 3;
      if (bits)
        {
          rc = or_pad (buf, 4 - bits);
        }
    }
    }
  return rc;
}


/*
 * or_get_value - extracts a packed DB_VALUE
 *    return: none
 *    buf(in/out): packing buffer
 *    value(out): value to ponder
 *    domain(out): domain to use (optional, only if the value is not tagged)
 *    expected(out): expected size of the value (optional, can be -1)
 *    copy(in):
 * Note:
 *    This extracts a packed DB_VALUE and whetever it contains.
 *    If the value is tagged with its own domain, the domain argument to
 *    this function must be NULL.  If the value is not tagged, the
 *    domain argument must be specified.
 *
 *    The expected size is probably not necessary but its been passed around
 *    for so long, I'm reluctant to yank it right now.  It can be -1 if
 *    we don't know the size but in that case we must be able to determine
 *    the packed size by looking at the domain or by the looking
 *    at the value header.
 *
 *    The value can be NULL, in which case, we will simply advance the
 *    OR_BUF past the current value.
 *
 */
int
or_get_value (OR_BUF * buf, DB_VALUE * value, TP_DOMAIN * domain, int expected, bool copy)
{
  char *start;
  int is_null, total, pad;
  int rc = NO_ERROR;

  is_null = 0;
  start = buf->ptr;

  /*
   * Always make sure this is properly initialized.
   * If the domain is given here, we could use that for further initialization ?
   */
  if (value)
    {
      db_make_null (value);
    }

  /* If size is zero, this must have been a "collapsed" NULL value. */
  if (expected == 0)
    {
      return NO_ERROR;
    }

  /*
   * If a domain was supplied, use it to decode the value, otherwise we
   * assume that the vlaues must be tagged.
   */
  if (domain == NULL)
    {
      domain = or_get_domain (buf, NULL, &is_null);
      if (expected >= 0)
    {
      /* reduce the expected size by the amount consumed with the domain tag */
      expected -= CAST_BUFLEN (buf->ptr - start);
      start = buf->ptr;
    }
    }

  if (domain == NULL)
    {
      /* problems decoding the domain */
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
      ASSERT_ERROR ();
      return ER_FAILED;
    }
  else
    {
      if (value)
    {
      tp_init_value_domain (domain, value);
    }

      if (is_null && value)
    {
      /* this was a tagged NULL value, restore the domain but set the null flag */
      db_value_put_null (value);

      if (TP_IS_CHAR_TYPE (TP_DOMAIN_TYPE (domain)))
        {
          db_string_put_cs_and_collation (value, TP_DOMAIN_CODESET (domain), TP_DOMAIN_COLLATION (domain));
        }
      else if (TP_DOMAIN_TYPE (domain) == DB_TYPE_ENUMERATION)
        {
          db_enum_put_cs_and_collation (value, TP_DOMAIN_CODESET (domain), TP_DOMAIN_COLLATION (domain));
        }
      else if (TP_DOMAIN_TYPE (domain) == DB_TYPE_JSON)
        {
          /* TODO find if schema_raw set here is ever used */
          value->data.json.schema_raw = NULL;
        }
    }
      else
    {
      if (value)
        {
          domain->type->data_readval (buf, value, domain, expected, copy, NULL, 0);
        }
      else
        {
          /* the NULL value, will cause readval to skip the value */
          domain->type->data_readval (buf, NULL, domain, expected, false, NULL, 0);
        }

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

  /* Consume any padding bytes that may be left over. If the expected size was given, use that to determine the amount
   * of padding, otherwise we'll have to assume that we just need to be brought up to a word boundary. */
  total = (int) (buf->ptr - start);
  pad = 0;
  if (expected > 0)
    {
      pad = expected - total;
    }
  else if (expected < 0)
    {
      if (total & 3)
    pad = 4 - (total & 3);
    }
  if (pad > 0)
    {
      rc = or_advance (buf, pad);
    }
  return rc;
}


/*
 * or_pack_value - alternative interface to or_put_value
 *    return: advanced pointer
 *    buf(out): buffer pointer
 *    value(in): value to store
 * Note:
 *    Alternate interface to or_put_value, see that function for more
 *    information.
 *    Doesn't accept all of the fields required by or_put_value because
 *    I don't think we need that level of control.  May need to add
 *    them later though.
 */
char *
or_pack_value (char *buf, DB_VALUE * value)
{
  OR_BUF orbuf;
  char *aligned_buf;

  aligned_buf = PTR_ALIGN (buf, MAX_ALIGNMENT);

  or_init (&orbuf, aligned_buf, 0);
  /* don't collapse nulls, include the domain, and include domain class oids */
  or_put_value (&orbuf, value, 0, 1, 1);

  return orbuf.ptr;
}

char *
or_pack_mem_value (char *ptr, DB_VALUE * value, int *packed_len_except_alignment)
{
  OR_BUF orbuf, *buf;
  const PR_TYPE *type;
  TP_DOMAIN *domain;
  char *start, length, bits;
  char *ptr_to_packed_value;
  int rc = NO_ERROR;
  DB_TYPE dbval_type;

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

  buf = &orbuf;
  or_init (buf, ptr, 0);
  start = buf->ptr;

  or_get_align64 (buf);

  /* notice that it points to real starting ptr to packed value */
  ptr_to_packed_value = buf->ptr;

  dbval_type = DB_VALUE_DOMAIN_TYPE (value);
  type = pr_type_from_id (dbval_type);
  if (type == NULL)
    {
      return NULL;
    }

  if (DB_VALUE_TYPE (value) == DB_TYPE_NULL)
    {
      domain = tp_domain_resolve_value (value, NULL);
      if (domain != NULL)
    {
      rc = or_put_domain (buf, domain, 1, 1);
    }
      else
    {
      /* shouldn't get here */
      rc = or_put_domain (buf, &tp_Null_domain, 0, 1);
    }
    }
  else
    {
      domain = tp_domain_resolve_value (value, NULL);
      if (domain != NULL)
    {
      rc = or_put_domain (buf, domain, 1, 0);
    }
      else
    {
      /* shouldn't get here */
      rc = or_put_domain (buf, &tp_Null_domain, 0, 1);
      return buf->ptr;
    }

      if (rc == NO_ERROR)
    {
      or_get_align64 (buf);
      rc = type->data_writeval (buf, value);
    }
    }

  if (rc == NO_ERROR)
    {
      if (packed_len_except_alignment)
    {
      /* it excludes both leading and trailing alignments */
      *packed_len_except_alignment = (int) (buf->ptr - ptr_to_packed_value);
    }

      length = (int) (buf->ptr - start);
      bits = length & 3;
      if (bits)
    {
      rc = or_pad (buf, 4 - bits);
    }

    }

  return buf->ptr;
}


/*
 * or_unpack_value - alternate interface to or_get_value
 *    return: advanced pointer
 *    buf(in): buffer
 *    value(out): value to unpack
 * Note:
 *    Alternate interface to or_get_value, see that function for more
 *    details.
 */
char *
or_unpack_value (const char *buf, DB_VALUE * value)
{
  OR_BUF orbuf;

  buf = PTR_ALIGN (buf, MAX_ALIGNMENT);
  or_init (&orbuf, CONST_CAST (char *, buf) /* it is for read */ , 0);
  or_get_value (&orbuf, value, NULL, -1, true);

  return orbuf.ptr;
}

char *
or_unpack_mem_value (char *ptr, DB_VALUE * value)
{
  OR_BUF orbuf, *buf;
  TP_DOMAIN *domain;
  int is_null, rc = NO_ERROR;
  int total, pad;
  char *start;

  buf = &orbuf;
  or_init (buf, ptr, 0);
  start = buf->ptr;
  is_null = 0;

  or_get_align64 (buf);

  if (value)
    {
      db_make_null (value);
    }

  domain = or_get_domain (buf, NULL, &is_null);
  if (domain == NULL)
    {
      return NULL;
    }
  else if (is_null)
    {
      return buf->ptr;
    }

  or_get_align64 (buf);
  tp_init_value_domain (domain, value);
  if (is_null)
    {
      db_value_put_null (value);
    }
  else
    {
      rc = domain->type->data_readval (buf, value, domain, -1, true, NULL, 0);
      if (rc != NO_ERROR)
    {
      return NULL;
    }
    }

  total = (int) (buf->ptr - start);
  pad = 0;
  if (total & 3)
    {
      pad = 4 - (total & 3);
    }
  if (pad > 0)
    {
      rc = or_advance (buf, pad);
    }

  if (rc != NO_ERROR)
    {
      return NULL;
    }

  return buf->ptr;
}

/*
 * LIST ID PACKING
 */

/*
 * or_pack_listid - packs a QFILE_LIST_ID descriptor
 *    return: advanced buffer pointer
 *    ptr(out): starting pointer
 *    listid_ptr(in): QFILE_LIST_ID pointer
 * Note:
 *    This packs a QFILE_LIST_ID descriptor.  The arguments are passed as void*
 *    so we can avoid unfortunate circular dependencies between query_list.h
 *    and or.h.  query_list.h is included at the top of this file so we have
 *    the information necessary for casting.
 *    The QFILE_LIST_ID doesn't have an OR_PUT macro because it is significantly
 *    more complex than the other types and may be of variable size.
 */
char *
or_pack_listid (char *ptr, void *listid_ptr)
{
  QFILE_LIST_ID *listid;
  int i;

  listid = (QFILE_LIST_ID *) listid_ptr;

  OR_PUT_PTR (ptr, listid->query_id);
  ptr += OR_PTR_SIZE;
  OR_PUT_PTR (ptr, listid->tfile_vfid);
  ptr += OR_PTR_SIZE;

  ptr = or_pack_int64 (ptr, listid->tuple_cnt);

  OR_PUT_INT (ptr, listid->page_cnt);
  ptr += OR_INT_SIZE;
  OR_PUT_INT (ptr, listid->first_vpid.pageid);
  ptr += OR_INT_SIZE;
  OR_PUT_INT (ptr, listid->first_vpid.volid);
  ptr += OR_INT_SIZE;

  OR_PUT_INT (ptr, listid->last_vpid.pageid);
  ptr += OR_INT_SIZE;
  OR_PUT_INT (ptr, listid->last_vpid.volid);
  ptr += OR_INT_SIZE;

  OR_PUT_INT (ptr, listid->last_offset);
  ptr += OR_INT_SIZE;
  OR_PUT_INT (ptr, listid->lasttpl_len);
  ptr += OR_INT_SIZE;
  OR_PUT_INT (ptr, listid->type_list.type_cnt);
  ptr += OR_INT_SIZE;

  for (i = 0; i < listid->type_list.type_cnt; i++)
    {
      /* do we want to pack the class oids?? */
      ptr = or_pack_domain (ptr, listid->type_list.domp[i], 0, 0);
    }

  return ptr;
}

char *
or_unpack_recdes (char *buf, RECDES ** recdes)
{
  RECDES *tmp_recdes = NULL;
  int length;

  if (buf == NULL)
    {
      return NULL;
    }

  buf = or_unpack_int (buf, &length);
  tmp_recdes = (RECDES *) malloc (sizeof (RECDES) + length);
  if (tmp_recdes == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (RECDES) + length);
      return NULL;
    }

  tmp_recdes->area_size = length;
  tmp_recdes->length = length;
  tmp_recdes->data = ((char *) tmp_recdes) + sizeof (RECDES);

  buf = or_unpack_short (buf, &tmp_recdes->type);
  buf = or_unpack_stream (buf, tmp_recdes->data, length);

  *recdes = tmp_recdes;
  return buf;
}

/*
 * or_unpack_listid - This unpacks a QFILE_LIST_ID descriptor from a buffer.
 *    return: advanced buffer pointer
 *    ptr(in): starting pointer
 *    listid_ptr(out):
 * Note:
 *    This unpacks a QFILE_LIST_ID descriptor from a buffer.
 *    Kludge, the arguments are passed in as void* so we can avoid
 *    unfortunate circular dependencies between query_list.h and or.h
 *    query_list.h is included at the top of this file so we have the
 *    information necessary for casting.
 */
char *
or_unpack_listid (char *ptr, void *listid_ptr)
{
  QFILE_LIST_ID *listid;

  listid = (QFILE_LIST_ID *) listid_ptr;

  QFILE_CLEAR_LIST_ID (listid);

  listid->query_id = OR_GET_PTR (ptr);
  ptr += OR_PTR_SIZE;

  listid->tfile_vfid = (struct qmgr_temp_file *) OR_GET_PTR (ptr);
  ptr += OR_PTR_SIZE;

  ptr = or_unpack_int64 (ptr, &listid->tuple_cnt);

  listid->page_cnt = OR_GET_INT (ptr);
  ptr += OR_INT_SIZE;

  listid->first_vpid.pageid = OR_GET_INT (ptr);
  ptr += OR_INT_SIZE;
  listid->first_vpid.volid = OR_GET_INT (ptr);
  ptr += OR_INT_SIZE;

  listid->last_vpid.pageid = OR_GET_INT (ptr);
  ptr += OR_INT_SIZE;
  listid->last_vpid.volid = OR_GET_INT (ptr);
  ptr += OR_INT_SIZE;

  listid->last_offset = OR_GET_INT (ptr);
  ptr += OR_INT_SIZE;
  listid->lasttpl_len = OR_GET_INT (ptr);
  ptr += OR_INT_SIZE;
  listid->type_list.type_cnt = OR_GET_INT (ptr);
  ptr += OR_INT_SIZE;

  return ptr;
}

/*
 * or_unpack_unbound_listid - This unpacks a QFILE_LIST_ID descriptor from a buffer.
 *    return: advanced buffer pointer
 *    ptr(in): starting pointer
 *    listid_ptr(out):
 * Note:
 *    This is a malloc used version for or_unpack_listid
 */
char *
or_unpack_unbound_listid (char *ptr, void **listid_ptr)
{
  QFILE_LIST_ID *listid;
  int count, i;

  /*
   * tuple_cnt 8, vfid.fileid 4, vfid.volid 2, attr_list.oid_flg 2,
   * attr_list.attr_cnt 4, attr_list.attr_id 4 * n
   */

  listid = (QFILE_LIST_ID *) malloc (sizeof (QFILE_LIST_ID));
  if (listid == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (QFILE_LIST_ID));
      goto error;
    }

  ptr = or_unpack_listid (ptr, listid);

  count = listid->type_list.type_cnt;
  if (count < 0)
    {
      goto error;
    }

  if (count > 0)
    {
      listid->type_list.domp = (TP_DOMAIN **) malloc (sizeof (TP_DOMAIN *) * count);

      if (listid->type_list.domp == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (sizeof (TP_DOMAIN *) * count));
      goto error;
    }

      for (i = 0; i < count; i++)
    {
      ptr = or_unpack_domain (ptr, &listid->type_list.domp[i], NULL);
    }
    }

  *listid_ptr = (void *) listid;
  return ptr;

error:
  if (listid)
    {
      free_and_init (listid);
    }
  return NULL;
}

/*
 * or_listid_length - Calculates the number of bytes required to store the
 * disk/comm representation of a QFILE_LIST_ID structure.
 *    return: length of the list representation in bytes
 *    listid_ptr(in): opaque pointer to QFILE_LIST_ID structure
 * Note:
 *    Calculates the number of bytes required to store the disk/comm
 *    representation of a QFILE_LIST_ID structure.  These are of variable size.
 *    Kludge, the arguments are passed in as void* so we can avoid
 *    unfortunate circular dependencies between query_list.h and or.h
 *    query_list.h is included at the top of this file so we have the
 *    information necesary for casting.
 */
int
or_listid_length (void *listid_ptr)
{
  QFILE_LIST_ID *listid;
  int length = 0;
  int i;

  listid = (QFILE_LIST_ID *) listid_ptr;

  if (listid == NULL)
    {
      return length;
    }

  length = OR_PTR_SIZE /* query_id */  + OR_PTR_SIZE;   /* tfile_vfid */

  /* aligned length for tuple_count (INT64, 8) */
  length = DB_ALIGN (length, MAX_ALIGNMENT);    // aligned offset
  length += OR_INT64_SIZE;

  /* 8 fixed item page_cnt first_vpid.pageid first_vpid.volid last_vpid.pageid last_vpid.volid
   * last_offset lasttpl_len type_list_type_cnt */
  length += OR_INT_SIZE * 8;

  for (i = 0; i < listid->type_list.type_cnt; i++)
    {
      length += or_packed_domain_size (listid->type_list.domp[i], 0);
    }

  return length;
}

/*
 * or_pack_key_val_range - packs a KEY VALUE RANGE.
 *    return: advanced buffer pointer
 *    ptr(out): starting pointer
 *    key_val_range(in):  key value range pointer
 */
char *
or_pack_key_val_range (char *ptr, const void *key_val_range_ptr)
{
  KEY_VAL_RANGE *key_val_range = (KEY_VAL_RANGE *) key_val_range_ptr;

  if (key_val_range == NULL)
    {
      return ptr;
    }

  ptr = or_pack_int (ptr, (int) key_val_range->range);
  ptr = or_pack_int (ptr, key_val_range->num_index_term);
  ptr = or_pack_int (ptr, (int) key_val_range->is_truncated);
  ptr = or_pack_db_value (ptr, &key_val_range->key1);
  ptr = or_pack_db_value (ptr, &key_val_range->key2);

  return ptr;
}

/*
 * or_unpack_key_val_array - unpacks a KEY_VAL_RANGE descriptor from a buffer.
 *    return: advanced buffer pointer
 *    ptr(in): starting pointer
 *    n(in):
 */
char *
or_unpack_key_val_range (char *ptr, void *key_val_range_ptr)
{
  KEY_VAL_RANGE *key_val_range = (KEY_VAL_RANGE *) key_val_range_ptr;

  if (key_val_range == (KEY_VAL_RANGE *) 0)
    {
      return NULL;
    }

  ptr = or_unpack_int (ptr, (int *) &key_val_range->range);
  ptr = or_unpack_int (ptr, &key_val_range->num_index_term);
  ptr = or_unpack_int (ptr, (int *) &key_val_range->is_truncated);
  ptr = or_unpack_db_value (ptr, &key_val_range->key1);
  ptr = or_unpack_db_value (ptr, &key_val_range->key2);

  return ptr;
}

/*
 * GENERIC DB_VALUE PACKING
 */

/*
 * or_pack_db_value - write a DB_VALUE
 *    return: advanced buffer pointer
 *    buffer(out): output buffer
 *    var(in): DB_VALUE
 */
char *
or_pack_db_value (char *buffer, DB_VALUE * var)
{
  return or_pack_value (buffer, var);
}

/*
 * or_db_value_size - get the packed size of DB_VALUE
 *    return: packed size
 *    var(in): DB_VALUE
 */
int
or_db_value_size (DB_VALUE * var)
{
  /* don't collapse nulls, include the domain, and include domain class oids */
  return or_packed_value_size (var, 0, 1, 1);
}

/*
 * or_unpack_db_value - read a DB_VALUE
 *    return: advanced buffer pointer
 *    buffer(in): input buffer
 *    val(out): DB_VALUE
 */
char *
or_unpack_db_value (char *buffer, DB_VALUE * val)
{
  /* new interface, hopefully compatible */
  return or_unpack_value (buffer, val);
}

/*
 * or_packed_enumeration_size () - get the packed size of an enumeration
 *    return: packed size
 *    enumeration (in): enumeration
 */
int
or_packed_enumeration_size (const DB_ENUMERATION * enumeration)
{
  int size = 0, idx;
  DB_VALUE value;
  DB_ENUM_ELEMENT *db_enum = NULL;

  if (enumeration->count == 0)
    {
      return 0;
    }
  /* an enumeration is packed as a collection of strings */
  size += OR_SET_HEADER_SIZE;

  for (idx = 0; idx < enumeration->count; idx++)
    {
      db_enum = &enumeration->elements[idx];

      db_make_varchar (&value, TP_FLOATING_PRECISION_VALUE, DB_GET_ENUM_ELEM_STRING (db_enum),
               DB_GET_ENUM_ELEM_STRING_SIZE (db_enum), DB_GET_ENUM_ELEM_CODESET (db_enum),
               LANG_GET_BINARY_COLLATION (DB_GET_ENUM_ELEM_CODESET (db_enum)));
      size += tp_String.get_disk_size_of_value (&value);
      pr_clear_value (&value);
    }

  return size;
}

/*
 * or_put_enumeration () - pack an enumeration
 *    return: error code or NO_ERROR
 *    enumeration (in): enumeration
 */
int
or_put_enumeration (OR_BUF * buf, const DB_ENUMERATION * enumeration)
{
  int rc = NO_ERROR, idx;
  DB_VALUE value;
  DB_ENUM_ELEMENT *db_enum = NULL;

  if (enumeration->count == 0)
    {
      return rc;
    }
  /* an enumeration is packed as a collection of strings */
  rc = or_put_set_header (buf, DB_TYPE_SEQUENCE, enumeration->count, 0, 0, 0, 0, 0);
  if (rc != NO_ERROR)
    {
      return rc;
    }

  for (idx = 0; idx < enumeration->count; idx++)
    {
      db_enum = &enumeration->elements[idx];
      db_make_varchar (&value, TP_FLOATING_PRECISION_VALUE, DB_GET_ENUM_ELEM_STRING (db_enum),
               DB_GET_ENUM_ELEM_STRING_SIZE (db_enum), DB_GET_ENUM_ELEM_CODESET (db_enum),
               enumeration->collation_id);
      rc = tp_String.data_writeval (buf, &value);
      pr_clear_value (&value);

      if (rc != NO_ERROR)
    {
      break;
    }
    }

  return rc;
}

/*
 * or_get_enumeration - read enumeration from input buffer
 *    return: NO_ERROR or error code
 *    buf(in): input buffer
 *    enumeration(in/out): pointer to enumeration holder
 */
int
or_get_enumeration (OR_BUF * buf, DB_ENUMERATION * enumeration)
{
  DB_ENUM_ELEMENT *enum_vals = NULL, *db_enum = NULL;
  int idx = 0, count = 0, error = NO_ERROR;
  DB_VALUE value;
  char *enum_str = NULL;
  const char *value_str = NULL;
  int str_size = 0;
  LANG_COLLATION *lc;

  db_make_null (&value);

  if (enumeration == NULL)
    {
      assert (false);
      return ER_FAILED;
    }

  count = or_skip_set_header (buf);

  if (count <= 0)
    {
      /* set header is not packed if count is 0 so this should never happen */
      enumeration->count = 0;
      enumeration->elements = NULL;
      assert (false);
      return ER_FAILED;
    }

  enum_vals = (DB_ENUM_ELEMENT *) malloc (sizeof (DB_ENUM_ELEMENT) * count);
  if (enum_vals == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (DB_ENUM_ELEMENT) * count);
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }

  for (idx = 0; idx < count; idx++)
    {
      db_enum = &enum_vals[idx];
      /* enum values are indexed starting with 1 */
      db_enum->short_val = idx + 1;

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

      error = tp_String.data_readval (buf, &value, NULL, -1, false, NULL, 0);
      if (error != NO_ERROR)
    {
      goto error_return;
    }

      DB_GET_ENUM_ELEM_DBCHAR (db_enum).info = value.data.ch.info;
      str_size = db_get_string_size (&value);
      enum_str = (char *) malloc (str_size + 1);
      if (enum_str == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
          (size_t) (db_get_string_size (&value) + 1));

      error = ER_OUT_OF_VIRTUAL_MEMORY;
      goto error_return;
    }
      value_str = db_get_string (&value);
      if (value_str)
    {
      memcpy (enum_str, value_str, str_size);
    }
      else
    {
      assert_release (str_size == 0);
    }
      enum_str[str_size] = 0;

      DB_SET_ENUM_ELEM_STRING (db_enum, enum_str);
      DB_SET_ENUM_ELEM_STRING_SIZE (db_enum, str_size);

      lc = lang_get_collation (enumeration->collation_id);
      assert (lc != NULL);
      DB_SET_ENUM_ELEM_CODESET (db_enum, lc->codeset);

      pr_clear_value (&value);
    }

  enumeration->count = count;
  enumeration->elements = enum_vals;

  return NO_ERROR;

error_return:
  if (enum_vals != NULL)
    {
      for (--idx; idx >= 0; idx--)
    {
      free_and_init (DB_GET_ENUM_ELEM_STRING (&enum_vals[idx]));
    }
      free_and_init (enum_vals);
    }
  pr_clear_value (&value);

  enumeration->count = 0;
  enumeration->elements = NULL;
  return error;
}

/*
 * or_header_size () - Return the record header size.
 *
 * return : header size
 */
int
or_header_size (char *ptr)
{
  return mvcc_header_size_lookup[OR_GET_MVCC_FLAG (ptr)];
}

/*
 * or_pack_int_array - write a int array
 *    return: advanced buffer pointer
 *    buffer(out): output buffer
 *    count(in): array length
 *    int_array(in): int array
 */
char *
or_pack_int_array (char *buffer, int count, const int *int_array)
{
  int i;
  char *ptr;

  assert (buffer != NULL && int_array != NULL && count >= 0);

  if (count < 0 || int_array == NULL)
    {
      count = 0;
    }

  /* pack count + that many integers */
  ptr = or_pack_int (buffer, count);

  for (i = 0; i < count; i++)
    {
      ptr = or_pack_int (ptr, int_array[i]);
    }

  return ptr;
}

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * or_packed_string_array_length - get the amount of space needed to pack an
 * array of strings.
 *    return: packed string array length
 *    count(in): element count
 *    string_array(out): packed string array
 * Note:
 */
int
or_packed_string_array_length (int count, const char **string_array)
{
  int i;
  int size = OR_INT_SIZE;

  for (i = 0; i < count; i++)
    {
      size += or_packed_string_length (string_array[i]);
    }

  return size;
}

/*
 * or_packed_db_value_array_length - get the amount of space needed to pack an
 * array of db_values.
 *    return: packed db value array length
 *    count(in): array size
 *    val(in): DB_VALUE array
 */
int
or_packed_db_value_array_length (int count, DB_VALUE * val)
{
  int i;
  int size = OR_INT_SIZE;

  for (i = 0; i < count; i++)
    {
      size += or_db_value_size (val++);
    }
  return size;
}

/*
 * or_pack_string_array - write a string array
 *    return: advanced buffer pointer
 *    buffer(out): output buffer
 *    count(in): string array length
 *    string_array(in): string array
 */
char *
or_pack_string_array (char *buffer, int count, const char **string_array)
{
  int i;
  char *ptr;

  ptr = or_pack_int (buffer, count);
  for (i = 0; i < count; i++)
    {
      ptr = or_pack_string (ptr, string_array[i]);
    }
  return ptr;
}

/*
 * unpack_str_array - read array of string
 *    return: advanced buffer pointer
 *    buffer(in): input buffer
 *    string_array(out): array of string
 *    count(in): length of array
 */
static char *
unpack_str_array (char *buffer, char ***string_array, int count)
{
  int i;
  char *ptr, **array_p;

  ptr = buffer;
  if (count <= 0)
    {
      if (string_array)
    {
      *string_array = NULL;
    }
    }
  else
    {
      *string_array = (char **) db_private_alloc (NULL, (sizeof (char *) * count));
      if (*string_array == NULL)
    {
      ptr = NULL;
    }
      else
    {
      for (array_p = *string_array, i = 0; i < count; i++, array_p++)
        {
          ptr = or_unpack_string (ptr, array_p);
        }
    }
    }
  return ptr;
}

/*
 * or_unpack_string_array -
 *    return:
 *    buffer():
 *    string_array():
 *    cnt():
 */
char *
or_unpack_string_array (char *buffer, char ***string_array, int *cnt)
{
  char *ptr = or_unpack_int (buffer, cnt);
  return unpack_str_array (ptr, string_array, *cnt);
}

/*
 * or_pack_db_value_array - write a DB_VALUE array
 *    return: advanced buffer pointer
 *    buffer(out): output buffer
 *    count(in): array length
 *    val(in): DB_VALUE array
 */
char *
or_pack_db_value_array (char *buffer, int count, DB_VALUE * val)
{
  int i;
  char *ptr;

  if (!buffer)
    {
      return NULL;
    }

  ptr = or_pack_int (buffer, count);
  for (i = 0; i < count; i++)
    {
      ptr = or_pack_db_value (ptr, val++);
    }
  return ptr;
}

/*
 * or_unpack_db_value_array - write a DB_VALUE array
 *    return: advanced buffer pointer
 *    buffer(in): input buffer
 *    val(out): DB_VALUE array
 *    count(in): array length
 */
char *
or_unpack_db_value_array (char *buffer, DB_VALUE ** val, int *count)
{
  int i;
  char *ptr;
  DB_VALUE *local_val;

  if (!buffer)
    {
      return NULL;
    }

  ptr = or_unpack_int (buffer, count);
  if (*count)
    {
      *val = (DB_VALUE *) db_private_alloc (NULL, sizeof (DB_VALUE) * (*count));
      if (*val == NULL)
    {
      ptr = NULL;
    }
      else
    {
      local_val = *val;
      for (i = 0; i < *count; i++)
        {
          ptr = (char *) or_unpack_db_value (ptr, local_val++);
        }
    }
    }

  return ptr;
}
#endif /* ENABLE_UNUSED_FUNCTION */

/*
 * or_pack_ptr - write pointer value to ptr
 *    return: advanced buffer pointer
 *    ptr(out): out buffer
 *    ptrval(in): pointer value
 */
char *
or_pack_ptr (char *ptr, UINTPTR ptrval)
{
  ptr = PTR_ALIGN (ptr, PTR_ALIGNMENT);

  OR_PUT_PTR (ptr, ptrval);
  return (ptr + OR_PTR_SIZE);
}

/*
 * or_unpack_ptr - read a pointer value
 *    return: advanced buffer pointer
 *    ptr(in): input buffer
 *    ptrval(out): pointer value
 */
char *
or_unpack_ptr (char *ptr, UINTPTR * ptrval)
{
  ptr = PTR_ALIGN (ptr, PTR_ALIGNMENT);

  *ptrval = OR_GET_PTR (ptr);
  return (ptr + OR_PTR_SIZE);
}

/*
 * MVCCID:
 */

/*
 * or_pack_mvccid () - Pack an MVCCID at the give pointer.
 *
 * return      : Pointer after the packed MVCCID.
 * ptr (in)    : Pointer where to pack the MVCCID.
 * mvccid (in) : MVCCID to pack.
 */
char *
or_pack_mvccid (char *ptr, const MVCCID mvccid)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_PUT_MVCCID (ptr, &mvccid);
  return (((char *) ptr) + OR_MVCCID_SIZE);
}

/*
 * or_unpack_mvccid () - Unpack an MVCCID from the give pointer.
 *
 * return   : Pointer after the packed MVCCID.
 * ptr (in) : Pointer where the MVCCID is packed.
 * mvccid (out) : MVCCID value.
 */
char *
or_unpack_mvccid (char *ptr, MVCCID * mvccid)
{
  ASSERT_ALIGN (ptr, INT_ALIGNMENT);
  assert (mvccid != NULL);

  OR_GET_MVCCID (ptr, mvccid);
  return (((char *) ptr) + OR_MVCCID_SIZE);
}

/* SHA-1 */


/*
 * or_pack_sha1 () - Pack SHA-1 hash.
 *
 * return    : Pointer after packed.
 * ptr (in)  : Pointer where to pack.
 * sha1 (in) : Value to pack.
 */
char *
or_pack_sha1 (char *ptr, const SHA1Hash * sha1)
{
  assert (sha1 != NULL);

  if (ptr == NULL)
    {
      return NULL;
    }

  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_PUT_SHA1 (ptr, sha1);
  return ptr + OR_SHA1_SIZE;
}

/*
 * or_unpack_sha1 () - Unpack SHA-1 hash.
 *
 * return     : Pointer after unpacked.
 * ptr (in)   : Pointer where to unpack.
 * sha1 (out) : Value to unpack.
 */
char *
or_unpack_sha1 (char *ptr, SHA1Hash * sha1)
{
  assert (sha1 != NULL);

  if (ptr == NULL)
    {
      memset (sha1, 0, sizeof (*sha1));
      return NULL;
    }

  ASSERT_ALIGN (ptr, INT_ALIGNMENT);

  OR_GET_SHA1 (ptr, sha1);
  return ptr + OR_SHA1_SIZE;
}

/*
 * or_packed_spacedb_size () - compute the size required to pack all space info
 *
 * return     : total required size
 * all (in)   : aggregated space information
 * vols (in)  : space information for each volume (may be NULL)
 * files (in) : space information for files (may be NULL)
 */
int
or_packed_spacedb_size (const SPACEDB_ALL * all, const SPACEDB_ONEVOL * vols, const SPACEDB_FILES * files)
{
  int size_total = 0;

  /* for each type without any, pack nvols, npage_used, npage_free */
  size_total = SPACEDB_ALL_COUNT * 3 * OR_INT_SIZE;

  if (vols != NULL)
    {
      /* we need to pack information on each volume. */
      int size_onevol = 5 * OR_INT_SIZE;    /* we have 5 int values without the name */
      int i;

      size_total += size_onevol * all[SPACEDB_TOTAL_ALL].nvols;

      /* also volume names */
      for (i = 0; i < all[SPACEDB_TOTAL_ALL].nvols; i++)
    {
      size_total += or_packed_string_length (vols[i].name, NULL);
    }
    }

  if (files != NULL)
    {
      /* for each type without any, pack nfile, npage_used, npage_ftab and npage_reserved */
      size_total += SPACEDB_FILE_COUNT * 4 * OR_INT_SIZE;
    }

  return size_total;
}

/*
 * or_pack_spacedb () - pack space info
 *
 * return     : new pointer after packed space info
 * ptr (in)   : destination pointer for packed space info
 * all (in)   : aggregated space information
 * vols (in)  : space information for each volume (may be NULL)
 * files (in) : space information for files (may be NULL)
 */
char *
or_pack_spacedb (char *ptr, const SPACEDB_ALL * all, const SPACEDB_ONEVOL * vols, const SPACEDB_FILES * files)
{
  int i;

  /* all */
  for (i = 0; i < SPACEDB_ALL_COUNT; i++)
    {
      ptr = or_pack_int (ptr, (int) all[i].nvols);
      ptr = or_pack_int (ptr, (int) all[i].npage_used);
      ptr = or_pack_int (ptr, (int) all[i].npage_free);
    }

  /* vols */
  if (vols != NULL)
    {
      int iter_vol = 0;
      for (iter_vol = 0; iter_vol < all[SPACEDB_TOTAL_ALL].nvols; iter_vol++)
    {
      ptr = or_pack_int (ptr, (int) vols[iter_vol].volid);
      ptr = or_pack_int (ptr, (int) vols[iter_vol].type);
      ptr = or_pack_int (ptr, (int) vols[iter_vol].purpose);
      ptr = or_pack_int (ptr, (int) vols[iter_vol].npage_used);
      ptr = or_pack_int (ptr, (int) vols[iter_vol].npage_free);
      ptr = or_pack_string (ptr, vols[iter_vol].name);
    }
    }

  if (files != NULL)
    {
      for (i = 0; i < SPACEDB_FILE_COUNT; i++)
    {
      ptr = or_pack_int (ptr, files[i].nfile);
      ptr = or_pack_int (ptr, (int) files[i].npage_user);
      ptr = or_pack_int (ptr, (int) files[i].npage_ftab);
      ptr = or_pack_int (ptr, (int) files[i].npage_reserved);
    }
    }

  return ptr;
}

/*
 * or_unpack_spacedb () - document me!
 *
 * return :
 * char * ptr (in) :
 * SPACEDB_ALL * all (in) :
 * SPACEDB_ONEVOL * * vols (in) :
 * SPACEDB_FILES * files (in) :
 */
char *
or_unpack_spacedb (char *ptr, SPACEDB_ALL * all, SPACEDB_ONEVOL ** vols, SPACEDB_FILES * files)
{
  int i;
  int unpacked_value;
  char *volname;

  assert (all != NULL);
  assert (vols == NULL || *vols == NULL);

  /* note: for all/files, total values are not packed. we'll compute them here, while unpacking */

  /* all */
  for (i = 0; i < SPACEDB_ALL_COUNT; i++)
    {
      ptr = or_unpack_int (ptr, &unpacked_value);
      all[i].nvols = (DKNVOLS) unpacked_value;
      ptr = or_unpack_int (ptr, &unpacked_value);
      all[i].npage_used = (DKNPAGES) unpacked_value;
      ptr = or_unpack_int (ptr, &unpacked_value);
      all[i].npage_free = (DKNPAGES) unpacked_value;
    }

  /* vols */
  if (vols != NULL)
    {
      int iter_vol = 0;

      *vols = (SPACEDB_ONEVOL *) malloc (all[SPACEDB_TOTAL_ALL].nvols * sizeof (SPACEDB_ONEVOL));
      if (*vols == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
          all[SPACEDB_TOTAL_ALL].nvols * sizeof (SPACEDB_ONEVOL));
      return NULL;
    }

      for (iter_vol = 0; iter_vol < all[SPACEDB_TOTAL_ALL].nvols; iter_vol++)
    {
      ptr = or_unpack_int (ptr, &unpacked_value);
      (*vols)[iter_vol].volid = (VOLID) unpacked_value;
      ptr = or_unpack_int (ptr, &unpacked_value);
      (*vols)[iter_vol].type = (DB_VOLTYPE) unpacked_value;
      ptr = or_unpack_int (ptr, &unpacked_value);
      (*vols)[iter_vol].purpose = (DB_VOLPURPOSE) unpacked_value;
      ptr = or_unpack_int (ptr, &unpacked_value);
      (*vols)[iter_vol].npage_used = (DKNPAGES) unpacked_value;
      ptr = or_unpack_int (ptr, &unpacked_value);
      (*vols)[iter_vol].npage_free = (DKNSECTS) unpacked_value;
      ptr = or_unpack_string_nocopy (ptr, &volname);
      if (volname == NULL)
        {
          return NULL;
        }
      strncpy ((*vols)[iter_vol].name, volname, DB_MAX_PATH_LENGTH);
    }
    }

  /* files */
  if (files != NULL)
    {
      for (i = 0; i < SPACEDB_FILE_COUNT; i++)
    {
      ptr = or_unpack_int (ptr, &files[i].nfile);
      ptr = or_unpack_int (ptr, &unpacked_value);
      files[i].npage_user = (DKNPAGES) unpacked_value;
      ptr = or_unpack_int (ptr, &unpacked_value);
      files[i].npage_ftab = (DKNPAGES) unpacked_value;
      ptr = or_unpack_int (ptr, &unpacked_value);
      files[i].npage_reserved = (DKNPAGES) unpacked_value;
    }
    }

  return ptr;
}

/*
 * classobj_initialize_default_expr() - Initializes default expression
 *   return: nothing
 *
 *   default_expr(out): default expression
 */
void
classobj_initialize_default_expr (DB_DEFAULT_EXPR * default_expr)
{
  assert (default_expr != NULL);

  default_expr->default_expr_type = DB_DEFAULT_NONE;
  default_expr->default_expr_format = NULL;
  default_expr->default_expr_op = NULL_DEFAULT_EXPRESSION_OPERATOR;
}

int
or_get_json_validator (OR_BUF * buf, REFPTR (JSON_VALIDATOR, validator))
{
  int rc;
  char *str = NULL;

  rc = or_get_json_schema (buf, str);
  if (rc != NO_ERROR)
    {
      validator = NULL;
      goto exit;
    }

  if (str == NULL || strlen (str) == 0)
    {
      rc = NO_ERROR;
      validator = NULL;
      goto exit;
    }
  else
    {
      rc = db_json_load_validator (str, validator);
      if (rc != NO_ERROR)
    {
      ASSERT_ERROR ();
      assert (validator == NULL);
      goto exit;
    }
      rc = NO_ERROR;
    }

exit:
  if (str != NULL)
    {
      db_private_free_and_init (NULL, str);
    }
  return rc;
}

int
or_put_json_validator (OR_BUF * buf, JSON_VALIDATOR * validator)
{
  return or_put_json_schema (buf, db_json_get_schema_raw_from_validator (validator));
}

int
or_get_json_schema (OR_BUF * buf, REFPTR (char, schema))
{
  DB_VALUE schema_value;
  int rc;

  ASSERT_ALIGN (buf->ptr, INT_ALIGNMENT);

  rc = tp_String.data_readval (buf, &schema_value, NULL, -1, false, NULL, 0);
  if (rc != NO_ERROR)
    {
      return rc;
    }

  if (db_get_string_size (&schema_value) == 0)
    {
      schema = NULL;
    }
  else
    {
      schema = db_private_strdup (NULL, db_get_string (&schema_value));
    }

  pr_clear_value (&schema_value);
  return NO_ERROR;
}

int
or_put_json_schema (OR_BUF * buf, const char *schema)
{
  int rc = NO_ERROR;
  DB_VALUE schema_raw;

  ASSERT_ALIGN (buf->ptr, INT_ALIGNMENT);

  if (schema == NULL)
    {
      db_make_string (&schema_raw, "");
    }
  else
    {
      db_make_string (&schema_raw, schema);
    }

  rc = tp_String.data_writeval (buf, &schema_raw);
  if (rc != NO_ERROR)
    {
      goto exit;
    }

exit:
  pr_clear_value (&schema_raw);
  return rc;
}