Skip to content

File object_domain.c

File List > cubrid > src > object > object_domain.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_domain.c: type, domain and value operations.
 * This module primarily defines support for domain structures.
 */

#ident "$Id$"


#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <float.h>
#include <math.h>
#include <errno.h>
#include <assert.h>

#include "area_alloc.h"
#include "deduplicate_key.h"
#include "object_domain.h"
#include "object_primitive.h"
#include "object_representation.h"
#include "numeric_opfunc.h"
#include "tz_support.h"
#include "db_date.h"
#include "mprec.h"
#include "porting_inline.hpp"
#include "set_object.h"
#include "string_opfunc.h"
#include "tz_support.h"
#include "chartype.h"
#include "db_json.hpp"
#include "string_buffer.hpp"
#include "db_value_printer.hpp"

#if !defined (SERVER_MODE)
#include "work_space.h"
#include "virtual_object.h"
#include "schema_manager.h"
#include "locator_cl.h"
#include "object_template.h"
#include "dbi.h"
#endif /* !defined (SERVER_MODE) */

#include "dbtype.h"
#include "error_manager.h"
// 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) */

/*
 * used by versant_driver to avoid doing foolish things
 * like au_fetch_instance on DB_TYPE_OBJECT values that
 * contain versant (not CUBRID) mops.
 */

#define ARE_COMPARABLE(typ1, typ2)                        \
    ((typ1 == typ2) || (QSTR_IS_CHAR(typ1) && QSTR_IS_CHAR(typ2)))

#define DBL_MAX_DIGITS    ((int)ceil(DBL_MAX_EXP * log10((double) FLT_RADIX)))

#define TP_NEAR_MATCH(t1, t2)                                           \
          (((t1) == (t2)) ||                                            \
           (((t1) == DB_TYPE_CHAR) ? ((t2) == DB_TYPE_VARCHAR) :        \
            ((t1) == DB_TYPE_VARCHAR) ? ((t2) == DB_TYPE_CHAR) :        \
            ((t1) == DB_TYPE_BIT) ? ((t2) == DB_TYPE_VARBIT) :          \
            ((t1) == DB_TYPE_VARBIT) ? ((t2) == DB_TYPE_BIT) : false)   \
          )


#define TP_NUM_MIDXKEY_DOMAIN_LIST      (10)

#define DB_DATETIMETZ_INITIALIZER { {0, 0}, 0 }

typedef enum tp_coersion_mode
{
  TP_EXPLICIT_COERCION = 0,
  TP_IMPLICIT_COERCION,
  TP_FORCE_COERCION
} TP_COERCION_MODE;

/*
 * These are arranged to get relative types for symmetrical
 * coercion selection. The absolute position is not critical.
 * If two types are mutually coercible, the more general
 * should appear later. Eg. Float should appear after integer.
 */

static const DB_TYPE db_type_rank[] = { DB_TYPE_NULL,
  DB_TYPE_SHORT,
  DB_TYPE_INTEGER,
  DB_TYPE_BIGINT,
  DB_TYPE_NUMERIC,
  DB_TYPE_FLOAT,
  DB_TYPE_DOUBLE,
  DB_TYPE_MONETARY,
  DB_TYPE_SET,
  DB_TYPE_SEQUENCE,
  DB_TYPE_MULTISET,
  DB_TYPE_TIME,
  DB_TYPE_DATE,
  DB_TYPE_TIMESTAMP,
  DB_TYPE_TIMESTAMPLTZ,
  DB_TYPE_TIMESTAMPTZ,
  DB_TYPE_DATETIME,
  DB_TYPE_DATETIMELTZ,
  DB_TYPE_DATETIMETZ,
  DB_TYPE_OID,
  DB_TYPE_VOBJ,
  DB_TYPE_OBJECT,
  DB_TYPE_CHAR,
  DB_TYPE_VARCHAR,

  /* TODO:
   * DB_TYPE_NCHAR and DB_TYPE_VARNCHAR will no longer be used(NCHAR was deprecated).
   * However, to maintain compatibility with previous versions, the enum list will be preserved.       
   */
  DB_TYPE_NCHAR_DEPRECATED,
  DB_TYPE_VARNCHAR_DEPRECATED,

  DB_TYPE_BIT,
  DB_TYPE_VARBIT,
  DB_TYPE_ELO,
  DB_TYPE_BLOB,
  DB_TYPE_CLOB,
  DB_TYPE_VARIABLE,
  DB_TYPE_SUB,
  DB_TYPE_POINTER,
  DB_TYPE_ERROR,
  DB_TYPE_DB_VALUE,
  (DB_TYPE) (DB_TYPE_LAST + 1)
};
static int db_type_rank_order[DB_TYPE_LAST + 1] = { 0, };

AREA *tp_Domain_area = NULL;
static bool tp_Initialized = false;

extern unsigned int db_on_server;


/*
 * Shorthand to initialize a bunch of fields without duplication.
 * Initializes the fields from precision to the end.
 */
#define DOMAIN_INIT                                    \
  0,            /* precision */                        \
  0,            /* scale */                            \
  NULL,         /* class */                            \
  NULL,         /* set domain */                       \
  {NULL, 0, 0}, /* enumeration */              \
  {-1, -1, -1}, /* class OID */                        \
  1,            /* built_in_index (set in tp_init) */  \
  0,            /* codeset */                          \
  0,            /* collation id */             \
  TP_DOMAIN_COLL_NORMAL,  /* collation flag */         \
  0,            /* self_ref */                         \
  1,            /* is_cached */                        \
  0,            /* is_parameterized */                 \
  false,        /* is_desc */                          \
  0,        /* is_visited */               \
  NULL      /* json_validator */                   \

/* Same as above, but leaves off the prec and scale, and sets the codeset */
#define DOMAIN_INIT2(codeset, coll)                    \
  NULL,         /* class */                            \
  NULL,         /* set domain */                       \
  {NULL, 0, 0}, /* enumeration */              \
  {-1, -1, -1}, /* class OID */                        \
  1,            /* built_in_index (set in tp_init) */  \
  (codeset),    /* codeset */                          \
  (coll),   /* collation id */             \
  TP_DOMAIN_COLL_NORMAL,  /* collation flag */         \
  0,            /* self_ref */                         \
  1,            /* is_cached */                        \
  1,            /* is_parameterized */                 \
  false,        /* is_desc */                          \
  0,        /* is_visited */               \
  NULL      /* json_validator */               \
                /*
                 * Same as DOMAIN_INIT but it sets the is_parameterized flag.
                 * Used for things that don't have a precision but which are parameterized in
                 * other ways.
                 */
#define DOMAIN_INIT3                                   \
  0,            /* precision */                        \
  0,            /* scale */                            \
  NULL,         /* class */                            \
  NULL,         /* set domain */                       \
  {NULL, 0, 0}, /* enumeration */              \
  {-1, -1, -1}, /* class OID */                        \
  1,            /* built_in_index (set in tp_init) */  \
  0,            /* codeset */                          \
  0,            /* collation id */             \
  TP_DOMAIN_COLL_NORMAL,  /* collation flag */         \
  0,            /* self_ref */                         \
  1,            /* is_cached */                        \
  1,            /* is_parameterized */                 \
  false,        /* is_desc */                          \
  0,        /* is_visited */               \
  NULL      /* json_validator */                   \

/* Same as DOMAIN_INIT but set the prec and scale. */
#define DOMAIN_INIT4(prec, scale)                      \
  (prec),       /* precision */                        \
  (scale),      /* scale */                            \
  NULL,         /* class */                            \
  NULL,         /* set domain */                       \
  {NULL, 0, 0},    /* enumeration */                   \
  {-1, -1, -1}, /* class OID */                        \
  1,            /* built_in_index (set in tp_init) */  \
  0,            /* codeset */                          \
  0,            /* collation id */                     \
  TP_DOMAIN_COLL_NORMAL,  /* collation flag */         \
  0,            /* self_ref */                         \
  1,            /* is_cached */                        \
  0,            /* is_parameterized */                 \
  false,        /* is_desc */                          \
  0,        /* is_visited */               \
  NULL      /* json_validator */               \

TP_DOMAIN tp_Null_domain = { NULL, NULL, &tp_Null, DOMAIN_INIT };
TP_DOMAIN tp_Short_domain = { NULL, NULL, &tp_Short, DOMAIN_INIT4 (DB_SHORT_PRECISION, 0) };
TP_DOMAIN tp_Integer_domain = { NULL, NULL, &tp_Integer, DOMAIN_INIT4 (DB_INTEGER_PRECISION, 0) };
TP_DOMAIN tp_Bigint_domain = { NULL, NULL, &tp_Bigint, DOMAIN_INIT4 (DB_BIGINT_PRECISION, 0) };
TP_DOMAIN tp_Float_domain = { NULL, NULL, &tp_Float, DOMAIN_INIT4 (DB_FLOAT_DECIMAL_PRECISION, 0) };
TP_DOMAIN tp_Double_domain = { NULL, NULL, &tp_Double, DOMAIN_INIT4 (DB_DOUBLE_DECIMAL_PRECISION, 0) };

TP_DOMAIN tp_Monetary_domain = { NULL, NULL, &tp_Monetary, DOMAIN_INIT4 (DB_MONETARY_DECIMAL_PRECISION, 0) };

TP_DOMAIN tp_String_domain = { NULL, NULL, &tp_String, DB_MAX_VARCHAR_PRECISION, 0,
  DOMAIN_INIT2 (INTL_CODESET_ISO88591, LANG_COLL_ISO_BINARY)
};

TP_DOMAIN tp_Object_domain = { NULL, NULL, &tp_Object, DOMAIN_INIT3 };
TP_DOMAIN tp_Set_domain = { NULL, NULL, &tp_Set, DOMAIN_INIT3 };

TP_DOMAIN tp_Multiset_domain = { NULL, NULL, &tp_Multiset, DOMAIN_INIT3 };

TP_DOMAIN tp_Sequence_domain = { NULL, NULL, &tp_Sequence, DOMAIN_INIT3 };

TP_DOMAIN tp_Midxkey_domain_list_heads[TP_NUM_MIDXKEY_DOMAIN_LIST] = {
  {NULL, NULL, &tp_Midxkey, DOMAIN_INIT3},
  {NULL, NULL, &tp_Midxkey, DOMAIN_INIT3},
  {NULL, NULL, &tp_Midxkey, DOMAIN_INIT3},
  {NULL, NULL, &tp_Midxkey, DOMAIN_INIT3},
  {NULL, NULL, &tp_Midxkey, DOMAIN_INIT3},
  {NULL, NULL, &tp_Midxkey, DOMAIN_INIT3},
  {NULL, NULL, &tp_Midxkey, DOMAIN_INIT3},
  {NULL, NULL, &tp_Midxkey, DOMAIN_INIT3},
  {NULL, NULL, &tp_Midxkey, DOMAIN_INIT3},
  {NULL, NULL, &tp_Midxkey, DOMAIN_INIT3}
};
TP_DOMAIN tp_Elo_domain = { NULL, NULL, &tp_Elo, DOMAIN_INIT }; /* todo: remove me */
TP_DOMAIN tp_Blob_domain = { NULL, NULL, &tp_Blob, DOMAIN_INIT };
TP_DOMAIN tp_Clob_domain = { NULL, NULL, &tp_Clob, DOMAIN_INIT };
TP_DOMAIN tp_Time_domain = { NULL, NULL, &tp_Time, DOMAIN_INIT4 (DB_TIME_PRECISION, 0) };
TP_DOMAIN tp_Utime_domain = { NULL, NULL, &tp_Utime, DOMAIN_INIT4 (DB_TIMESTAMP_PRECISION, 0) };
TP_DOMAIN tp_Timestamptz_domain = { NULL, NULL, &tp_Timestamptz, DOMAIN_INIT4 (DB_TIMESTAMPTZ_PRECISION, 0) };
TP_DOMAIN tp_Timestampltz_domain = { NULL, NULL, &tp_Timestampltz, DOMAIN_INIT4 (DB_TIMESTAMP_PRECISION, 0) };
TP_DOMAIN tp_Date_domain = { NULL, NULL, &tp_Date, DOMAIN_INIT4 (DB_DATE_PRECISION, 0) };

TP_DOMAIN tp_Datetime_domain = { NULL, NULL, &tp_Datetime,
  DOMAIN_INIT4 (DB_DATETIME_PRECISION, DB_DATETIME_DECIMAL_SCALE)
};

TP_DOMAIN tp_Datetimetz_domain = { NULL, NULL, &tp_Datetimetz,
  DOMAIN_INIT4 (DB_DATETIMETZ_PRECISION, DB_DATETIME_DECIMAL_SCALE)
};

TP_DOMAIN tp_Datetimeltz_domain = { NULL, NULL, &tp_Datetimeltz,
  DOMAIN_INIT4 (DB_DATETIME_PRECISION, DB_DATETIME_DECIMAL_SCALE)
};

TP_DOMAIN tp_Variable_domain = { NULL, NULL, &tp_Variable, DOMAIN_INIT3 };

TP_DOMAIN tp_Substructure_domain = { NULL, NULL, &tp_Substructure, DOMAIN_INIT3 };
TP_DOMAIN tp_Pointer_domain = { NULL, NULL, &tp_Pointer, DOMAIN_INIT };
TP_DOMAIN tp_Error_domain = { NULL, NULL, &tp_Error, DOMAIN_INIT };
TP_DOMAIN tp_Vobj_domain = { NULL, NULL, &tp_Vobj, DOMAIN_INIT3 };
TP_DOMAIN tp_Oid_domain = { NULL, NULL, &tp_Oid, DOMAIN_INIT3 };

TP_DOMAIN tp_Enumeration_domain = { NULL, NULL, &tp_Enumeration, 0, 0,
  DOMAIN_INIT2 (INTL_CODESET_ISO88591, LANG_COLL_ISO_BINARY)
};

TP_DOMAIN tp_Numeric_domain = { NULL, NULL, &tp_Numeric, DB_DEFAULT_NUMERIC_PRECISION, DB_DEFAULT_NUMERIC_SCALE,
  DOMAIN_INIT2 (0, 0)
};

TP_DOMAIN tp_Bit_domain = { NULL, NULL, &tp_Bit, TP_FLOATING_PRECISION_VALUE, 0,
  DOMAIN_INIT2 (INTL_CODESET_RAW_BITS, 0)
};

TP_DOMAIN tp_VarBit_domain = { NULL, NULL, &tp_VarBit, DB_MAX_VARBIT_PRECISION, 0,
  DOMAIN_INIT2 (INTL_CODESET_RAW_BITS, 0)
};

TP_DOMAIN tp_Char_domain = { NULL, NULL, &tp_Char, TP_FLOATING_PRECISION_VALUE, 0,
  DOMAIN_INIT2 (INTL_CODESET_ISO88591, LANG_COLL_ISO_BINARY)
};

TP_DOMAIN tp_Json_domain = { NULL, NULL, &tp_Json, 0, 0,
  DOMAIN_INIT2 (INTL_CODESET_UTF8, LANG_COLL_UTF8_BINARY)
};

TP_DOMAIN tp_Resultset_domain = { NULL, NULL, &tp_ResultSet, DOMAIN_INIT4 (DB_BIGINT_PRECISION, 0) };

/* These must be in DB_TYPE order */
#define tp_NChar_domain     tp_Char_domain
#define tp_VarNChar_domain  tp_String_domain
static TP_DOMAIN *tp_Domains[] = {
  &tp_Null_domain,
  &tp_Integer_domain,
  &tp_Float_domain,
  &tp_Double_domain,
  &tp_String_domain,
  &tp_Object_domain,
  &tp_Set_domain,
  &tp_Multiset_domain,
  &tp_Sequence_domain,
  &tp_Elo_domain,
  &tp_Time_domain,
  &tp_Utime_domain,
  &tp_Date_domain,
  &tp_Monetary_domain,
  &tp_Variable_domain,
  &tp_Substructure_domain,
  &tp_Pointer_domain,
  &tp_Error_domain,
  &tp_Short_domain,
  &tp_Vobj_domain,
  &tp_Oid_domain,       /* does this make sense? shouldn't we share tp_Object_domain */
  &tp_Null_domain,      /* current position of DB_TYPE_DB_VALUE */
  &tp_Numeric_domain,
  &tp_Bit_domain,
  &tp_VarBit_domain,
  &tp_Char_domain,

  /* TODO:
   * DB_TYPE_NCHAR and DB_TYPE_VARNCHAR will no longer be used(NCHAR was deprecated).
   * However, to maintain compatibility with previous versions, the enum list will be preserved.       
   */
  &tp_NChar_domain,
  &tp_VarNChar_domain,

  &tp_Resultset_domain,     /* result set */
  &tp_Midxkey_domain_list_heads[0],
  &tp_Null_domain,
  &tp_Bigint_domain,
  &tp_Datetime_domain,

  /* beginning of some "padding" built-in domains that can be used as expansion space when new primitive data types are
   * added. */
  &tp_Blob_domain,
  &tp_Clob_domain,
  &tp_Enumeration_domain,
  &tp_Timestamptz_domain,
  &tp_Timestampltz_domain,
  &tp_Datetimetz_domain,
  &tp_Datetimeltz_domain,
  &tp_Json_domain,
  &tp_Null_domain,
  &tp_Null_domain,
  &tp_Null_domain,
  &tp_Null_domain,
  &tp_Null_domain,
  &tp_Null_domain,
  &tp_Null_domain,

  /* beginning of the built-in, complex domains */


  /* end of built-in domain marker */
  NULL
};

/*
 * Lists for MIDXKEY domain cache.
 * They are hashed by the length of setdomain to decrease congestion on
 * the list (See the function tp_domain_get_list). tp_Midxkey_domain should
 * be located on the head of tp_Midxkey_domains[0] because it is one of
 * built-in domains and is used to make XASL.
 */
static TP_DOMAIN *tp_Midxkey_domains[TP_NUM_MIDXKEY_DOMAIN_LIST + 1] = {
  &tp_Midxkey_domain_list_heads[0],
  &tp_Midxkey_domain_list_heads[1],
  &tp_Midxkey_domain_list_heads[2],
  &tp_Midxkey_domain_list_heads[3],
  &tp_Midxkey_domain_list_heads[4],
  &tp_Midxkey_domain_list_heads[5],
  &tp_Midxkey_domain_list_heads[6],
  &tp_Midxkey_domain_list_heads[7],
  &tp_Midxkey_domain_list_heads[8],
  &tp_Midxkey_domain_list_heads[9],

  /* end of built-in domain marker */
  NULL
};

static TP_DOMAIN *tp_Bigint_conv[] = {
  &tp_Bigint_domain, &tp_Integer_domain, &tp_Short_domain, &tp_Float_domain,
  &tp_Double_domain, &tp_Numeric_domain, &tp_Monetary_domain, &tp_Time_domain,
  NULL
};

static TP_DOMAIN *tp_Integer_conv[] = {
  &tp_Integer_domain, &tp_Bigint_domain, &tp_Short_domain, &tp_Float_domain,
  &tp_Double_domain, &tp_Numeric_domain, &tp_Monetary_domain,
  &tp_Time_domain, NULL
};

static TP_DOMAIN *tp_Short_conv[] = {
  &tp_Short_domain, &tp_Integer_domain, &tp_Bigint_domain, &tp_Float_domain,
  &tp_Double_domain, &tp_Numeric_domain, &tp_Monetary_domain,
  &tp_Time_domain, NULL
};

static TP_DOMAIN *tp_Float_conv[] = {
  &tp_Float_domain, &tp_Double_domain, &tp_Numeric_domain, &tp_Bigint_domain,
  &tp_Integer_domain, &tp_Short_domain, &tp_Monetary_domain,
  &tp_Time_domain, NULL
};

static TP_DOMAIN *tp_Double_conv[] = {
  &tp_Double_domain, &tp_Float_domain, &tp_Numeric_domain, &tp_Bigint_domain,
  &tp_Integer_domain, &tp_Short_domain, &tp_Monetary_domain,
  &tp_Time_domain, NULL
};

static TP_DOMAIN *tp_Numeric_conv[] = {
  &tp_Numeric_domain, &tp_Double_domain, &tp_Float_domain, &tp_Bigint_domain,
  &tp_Integer_domain, &tp_Short_domain, &tp_Monetary_domain,
  &tp_Time_domain, NULL
};

static TP_DOMAIN *tp_Monetary_conv[] = {
  &tp_Monetary_domain, &tp_Double_domain, &tp_Float_domain,
  &tp_Bigint_domain, &tp_Integer_domain,
  &tp_Short_domain, &tp_Time_domain, NULL
};

static TP_DOMAIN *tp_String_conv[] = {
  &tp_String_domain, &tp_Char_domain,
  &tp_Datetime_domain, &tp_Utime_domain, &tp_Time_domain,
  &tp_Date_domain, &tp_Datetimetz_domain, &tp_Timestamptz_domain,
  NULL
};

static TP_DOMAIN *tp_Char_conv[] = {
  &tp_Char_domain, &tp_String_domain,
  &tp_Datetime_domain, &tp_Utime_domain, &tp_Time_domain,
  &tp_Date_domain, &tp_Datetimetz_domain, &tp_Timestamptz_domain,
  NULL
};

static TP_DOMAIN *tp_Bit_conv[] = {
  &tp_Bit_domain, &tp_VarBit_domain, NULL
};

static TP_DOMAIN *tp_VarBit_conv[] = {
  &tp_VarBit_domain, &tp_Bit_domain, NULL
};

static TP_DOMAIN *tp_Set_conv[] = {
  &tp_Set_domain, &tp_Multiset_domain, &tp_Sequence_domain, NULL
};

static TP_DOMAIN *tp_Multiset_conv[] = {
  &tp_Multiset_domain, &tp_Sequence_domain, NULL
};

static TP_DOMAIN *tp_Sequence_conv[] = {
  &tp_Sequence_domain, &tp_Multiset_domain, NULL
};

/*
 * tp_Domain_conversion_matrix
 *    This is the matrix of conversion rules.  It is used primarily
 *    in the coercion of sets.
 */
#define tp_NChar_conv      tp_Char_conv
#define tp_VarNChar_conv   tp_String_conv
TP_DOMAIN **tp_Domain_conversion_matrix[] = {
  NULL,             /* DB_TYPE_NULL */
  tp_Integer_conv,
  tp_Float_conv,
  tp_Double_conv,
  tp_String_conv,
  NULL,             /* DB_TYPE_OBJECT */
  tp_Set_conv,
  tp_Multiset_conv,
  tp_Sequence_conv,
  NULL,             /* DB_TYPE_ELO */
  NULL,             /* DB_TYPE_TIME */
  NULL,             /* DB_TYPE_TIMESTAMP */
  NULL,             /* DB_TYPE_DATE */
  tp_Monetary_conv,
  NULL,             /* DB_TYPE_VARIABLE */
  NULL,             /* DB_TYPE_SUBSTRUCTURE */
  NULL,             /* DB_TYPE_POINTER */
  NULL,             /* DB_TYPE_ERROR */
  tp_Short_conv,
  NULL,             /* DB_TYPE_VOBJ */
  NULL,             /* DB_TYPE_OID */
  NULL,             /* DB_TYPE_DB_VALUE */
  tp_Numeric_conv,      /* DB_TYPE_NUMERIC */
  tp_Bit_conv,          /* DB_TYPE_BIT */
  tp_VarBit_conv,       /* DB_TYPE_VARBIT */
  tp_Char_conv,         /* DB_TYPE_CHAR */

  /* TODO:
   * DB_TYPE_NCHAR and DB_TYPE_VARNCHAR will no longer be used(NCHAR was deprecated).
   * However, to maintain compatibility with previous versions, the enum list will be preserved.       
   */
  tp_NChar_conv,        /* DB_TYPE_NCHAR */
  tp_VarNChar_conv,     /* DB_TYPE_VARNCHAR */

  NULL,             /* DB_TYPE_RESULTSET */
  NULL,             /* DB_TYPE_MIDXKEY */
  NULL,             /* DB_TYPE_TABLE */
  tp_Bigint_conv,       /* DB_TYPE_BIGINT */
  NULL,             /* DB_TYPE_DATETIME */
  NULL,             /* DB_TYPE_BLOB */
  NULL,             /* DB_TYPE_CLOB */
  NULL,             /* DB_TYPE_ENUMERATION */
  NULL,             /* DB_TYPE_TIMESTAMPTZ */
  NULL,             /* DB_TYPE_TIMESTAMPLTZ */
  NULL,             /* DB_TYPE_DATETIMETZ */
  NULL,             /* DB_TYPE_DATETIMELTZ */
  NULL              /* DB_TYPE_JSON */
};

#if defined (SERVER_MODE)
/* lock for domain list cache */
static pthread_mutex_t tp_domain_cache_lock = PTHREAD_MUTEX_INITIALIZER;
#endif /* SERVER_MODE */


#ifdef __cplusplus
/* Notice)
 * The constructor of this class is used solely to initialize global variable(db_type_rank_order).
 */
class type_rank_order_initializer
{
public:
  type_rank_order_initializer ()
  {
    memset (db_type_rank_order, 0x00, sizeof (db_type_rank_order));
    for (int i = 0; db_type_rank[i] < (DB_TYPE_LAST + 1); i++)
      {
    db_type_rank_order[db_type_rank[i]] = i;
      }
  }
};
static volatile class type_rank_order_initializer tro_instance;
#else
__attribute__ ((constructor))
     static void tp_init_db_type_rank_order (void)
{
  memset (db_type_rank_order, 0x00, sizeof (db_type_rank_order));
  for (int i = 0; db_type_rank[i] < (DB_TYPE_LAST + 1); i++)
    {
      db_type_rank_order[db_type_rank[i]] = i;
    }
}
#endif


static int tp_domain_size_internal (const TP_DOMAIN * domain);
static void tp_value_slam_domain (DB_VALUE * value, const DB_DOMAIN * domain);
static TP_DOMAIN *tp_is_domain_cached (TP_DOMAIN * dlist, TP_DOMAIN * transient, TP_MATCH exact, TP_DOMAIN ** ins_pos);
#if !defined (SERVER_MODE)
static void tp_swizzle_oid (TP_DOMAIN * domain);
#endif /* SERVER_MODE */
static int tp_domain_check_class (TP_DOMAIN * domain, int *change);
static const TP_DOMAIN *tp_domain_find_compatible (const TP_DOMAIN * src, const TP_DOMAIN * dest);
#if defined(ENABLE_UNUSED_FUNCTION)
static int tp_null_terminate (const DB_VALUE * src, char **strp, int str_len, bool * do_alloc);
#endif
static int tp_atotime (const DB_VALUE * src, DB_TIME * temp);
static int tp_atodate (const DB_VALUE * src, DB_DATE * temp);
static int tp_atoutime (const DB_VALUE * src, DB_UTIME * temp);
static int tp_atotimestamptz (const DB_VALUE * src, DB_TIMESTAMPTZ * temp);
static int tp_atoudatetime (const DB_VALUE * src, DB_DATETIME * temp);
static int tp_atodatetimetz (const DB_VALUE * src, DB_DATETIMETZ * temp);
static int tp_atonumeric (const DB_VALUE * src, DB_VALUE * temp);
static int tp_atof (const DB_VALUE * src, double *num_value, DB_DATA_STATUS * data_stat);
static int tp_atobi (const DB_VALUE * src, DB_BIGINT * num_value, DB_DATA_STATUS * data_stat);
#if defined(ENABLE_UNUSED_FUNCTION)
static char *tp_itoa (int value, char *string, int radix);
#endif
static char *tp_ltoa (DB_BIGINT value, char *string, int radix);
static void format_floating_point (char *new_string, char *rve, int ndigits, int decpt, int sign);
static void tp_ftoa (DB_VALUE const *src, DB_VALUE * result);
static void tp_dtoa (DB_VALUE const *src, DB_VALUE * result);
static int bfmt_print (int bfmt, const DB_VALUE * the_db_bit, char *string, int max_size);
static TP_DOMAIN_STATUS tp_value_cast_internal (const DB_VALUE * src, DB_VALUE * dest, const TP_DOMAIN * desired_domain,
                        TP_COERCION_MODE coercion_mode, bool do_domain_select,
                        bool preserve_domain);
static DB_VALUE_COMPARE_RESULT oidcmp (OID * oid1, OID * oid2);
static int tp_domain_match_internal (const TP_DOMAIN * dom1, const TP_DOMAIN * dom2, TP_MATCH exact, bool match_order);
#if defined(CUBRID_DEBUG)
static void fprint_domain (FILE * fp, TP_DOMAIN * domain);
#endif
static INLINE TP_DOMAIN **tp_domain_get_list_ptr (DB_TYPE type, TP_DOMAIN * setdomain) __attribute__ ((ALWAYS_INLINE));
static INLINE TP_DOMAIN *tp_domain_get_list (DB_TYPE type, TP_DOMAIN * setdomain) __attribute__ ((ALWAYS_INLINE));

static int tp_enumeration_match (const DB_ENUMERATION * db_enum1, const DB_ENUMERATION * db_enum2);
static int tp_digit_number_str_to_bi (const char *start, const char *end, INTL_CODESET codeset, bool is_negative,
                      DB_BIGINT * num_value, DB_DATA_STATUS * data_stat);
static int tp_hex_str_to_bi (const char *start, const char *end, INTL_CODESET codeset, bool is_negative,
                 DB_BIGINT * num_value, DB_DATA_STATUS * data_stat);
static int tp_scientific_str_to_bi (const char *start, const char *end, INTL_CODESET codeset, bool is_negative,
                    DB_BIGINT * num_value, DB_DATA_STATUS * data_stat);
static DB_BIGINT tp_ubi_to_bi_with_args (UINT64 ubi, bool is_negative, bool truncated, bool round,
                     DB_DATA_STATUS * data_stat);

static UINT64 tp_ubi_times_ten (UINT64 ubi, bool * truncated);

/*
 * tp_init - Global initialization for this module.
 *    return: NO_ERROR or error code
 */
int
tp_init (void)
{
  TP_DOMAIN *d;
  int i;

  if (tp_Initialized)
    {
      assert (tp_Domain_area != NULL);
      return NO_ERROR;
    }

  /* create our allocation area */
  tp_Domain_area = area_create ("Domains", sizeof (TP_DOMAIN), 1024);
  if (tp_Domain_area == NULL)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  /*
   * Make sure the next pointer on all the built-in domains is clear.
   * Also make sure the built-in domain numbers are assigned consistently.
   * Assign the builtin indexes starting from 1 so we can use zero to mean
   * that the domain isn't built-in.
   */
  for (i = 0; tp_Domains[i] != NULL; i++)
    {
      d = tp_Domains[i];
      d->next_list = NULL;
      d->class_mop = NULL;
      d->self_ref = 0;
      d->setdomain = NULL;
      DOM_SET_ENUM (d, NULL, 0);
      d->class_oid.volid = d->class_oid.pageid = d->class_oid.slotid = -1;
      d->is_cached = 1;
      d->built_in_index = i + 1;
      d->is_desc = false;

      /* ! need to be adding this to the corresponding list */
    }

  /* tp_Midxkey_domains[0] was already initialized by the above codes. */
  for (i = 1; tp_Midxkey_domains[i] != NULL; i++)
    {
      d = tp_Midxkey_domains[i];
      d->next_list = NULL;
      d->class_mop = NULL;
      d->self_ref = 0;
      d->setdomain = NULL;
      DOM_SET_ENUM (d, NULL, 0);
      d->class_oid.volid = d->class_oid.pageid = d->class_oid.slotid = -1;
      d->is_cached = 1;
      d->built_in_index = tp_Midxkey_domains[0]->built_in_index;
      d->is_desc = false;
    }

  tp_Initialized = true;

  dk_deduplicate_key_attribute_initialized ();

  return NO_ERROR;
}

/*
 * tp_apply_sys_charset - applies system charset to string domains
 *    return: none
 */
void
tp_apply_sys_charset (void)
{
  if (tp_Domain_area == NULL)
    {
      return;
    }

  /* update string domains with current codeset */
  tp_String_domain.codeset = LANG_SYS_CODESET;
  tp_String_domain.collation_id = LANG_SYS_COLLATION;
  tp_Char_domain.codeset = LANG_SYS_CODESET;
  tp_Char_domain.collation_id = LANG_SYS_COLLATION;

  tp_Enumeration_domain.codeset = LANG_SYS_CODESET;
  tp_Enumeration_domain.collation_id = LANG_SYS_COLLATION;
}

/*
 * tp_final - Global shutdown for this module.
 *    return: none
 * Note:
 *    Frees all the cached domains.  It isn't absolutely necessary
 *    that we do this since area_final() will destroy the areas but when
 *    leak statistics are enabled, it will dump a bunch of messages
 *    about dangling domain allocations.
 */
void
tp_final (void)
{
  TP_DOMAIN *dlist, *d, *next, *prev;
  int i;

  if (!tp_Initialized)
    {
      assert (tp_Domain_area == NULL);
      return;
    }

  /*
   * Make sure the next pointer on all the built-in domains is clear.
   * Also make sure the built-in domain numbers are assigned consistently.
   */
  for (i = 0; tp_Domains[i] != NULL; i++)
    {
      dlist = tp_Domains[i];
      /*
       * The first element in the domain array is always a built-in, there
       * can potentially be other built-ins in the list mixed in with
       * allocated domains.
       */
      for (d = dlist->next_list, prev = dlist, next = NULL; d != NULL; d = next)
    {
      next = d->next_list;
      if (d->built_in_index)
        {
          prev = d;
        }
      else
        {
          prev->next_list = next;

          /*
           * Make sure to turn off the cache bit or else tp_domain_free
           * will ignore the request.
           */
          d->is_cached = 0;
          tp_domain_free (d);
        }
    }
    }

  /*
   * tp_Midxkey_domains[0] was cleared by the above for-loop.
   * It holds a pointer of tp_Midxkey_domain_list_heads[0] on its head.
   * The pointer is also stored on tp_Domains[DB_TYPE_MIDXKEY].
   */
  for (i = 1; tp_Midxkey_domains[i] != NULL; i++)
    {
      dlist = tp_Midxkey_domains[i];

      for (d = dlist->next_list, prev = dlist, next = NULL; d != NULL; d = next)
    {
      next = d->next_list;
      if (d->built_in_index)
        {
          prev = d;
        }
      else
        {
          prev->next_list = next;

          d->is_cached = 0;
          tp_domain_free (d);
        }
    }
    }

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

  tp_Initialized = false;
}

/*
 * tp_domain_clear_enumeration () - free memory allocated for an enumeration type
 * return : void
 * enumeration (in/out): enumeration
 */
void
tp_domain_clear_enumeration (DB_ENUMERATION * enumeration)
{
  int i = 0;

  if (enumeration == NULL || enumeration->count == 0)
    {
      return;
    }

  for (i = 0; i < enumeration->count; i++)
    {
      if (DB_GET_ENUM_ELEM_STRING (&enumeration->elements[i]) != NULL)
    {
      free_and_init (DB_GET_ENUM_ELEM_STRING (&enumeration->elements[i]));
    }
    }

  free_and_init (enumeration->elements);
}

/*
 * tp_enumeration_match () check if two enumerations match
 * return : 1 if the two enums match, 0 otherwise
 * db_enum1 (in):
 * db_enum2 (in);
 */
static int
tp_enumeration_match (const DB_ENUMERATION * db_enum1, const DB_ENUMERATION * db_enum2)
{
  int i;
  DB_ENUM_ELEMENT *enum1 = NULL, *enum2 = NULL;

  if (db_enum1 == db_enum2)
    {
      return 1;
    }

  if (db_enum1 == NULL || db_enum2 == NULL)
    {
      return 0;
    }

  if (db_enum1->count != db_enum2->count)
    {
      return 0;
    }
  if (db_enum1->collation_id != db_enum2->collation_id)
    {
      return 0;
    }

  for (i = 0; i < db_enum1->count; i++)
    {
      enum1 = &db_enum1->elements[i];
      enum2 = &db_enum2->elements[i];

      if (DB_GET_ENUM_ELEM_STRING_SIZE (enum1) != DB_GET_ENUM_ELEM_STRING_SIZE (enum2))
    {
      return 0;
    }

      /*
       * memcmp is used here because it is necessary for domains like
       * ENUM('a', 'b') COLLATE utf8_en_ci and
       * ENUM('A', 'B') COLLATE utf8_en_ci to be regarded as different
       * domains, despite their common case-insensitive collation.
       * Thus, collation-based comparison is not correct here.
       */
      if (memcmp (DB_GET_ENUM_ELEM_STRING (enum1), DB_GET_ENUM_ELEM_STRING (enum2),
          DB_GET_ENUM_ELEM_STRING_SIZE (enum1)) != 0)
    {
      return 0;
    }
    }

  return 1;
}

/*
 * tp_get_fixed_precision - return the fixed precision of the given type.
 *    return: the fixed precision for the fixed types, otherwise -1.
 *    domain_type(in): The type of the domain
 */
int
tp_get_fixed_precision (DB_TYPE domain_type)
{
  int precision;

  switch (domain_type)
    {
    case DB_TYPE_INTEGER:
      precision = DB_INTEGER_PRECISION;
      break;
    case DB_TYPE_SHORT:
      precision = DB_SHORT_PRECISION;
      break;
    case DB_TYPE_BIGINT:
      precision = DB_BIGINT_PRECISION;
      break;
    case DB_TYPE_FLOAT:
      precision = DB_FLOAT_DECIMAL_PRECISION;
      break;
    case DB_TYPE_DOUBLE:
      precision = DB_DOUBLE_DECIMAL_PRECISION;
      break;
    case DB_TYPE_DATE:
      precision = DB_DATE_PRECISION;
      break;
    case DB_TYPE_TIME:
      precision = DB_TIME_PRECISION;
      break;
    case DB_TYPE_TIMESTAMP:
    case DB_TYPE_TIMESTAMPLTZ:
      precision = DB_TIMESTAMP_PRECISION;
      break;
    case DB_TYPE_TIMESTAMPTZ:
      precision = DB_TIMESTAMPTZ_PRECISION;
      break;
    case DB_TYPE_DATETIME:
    case DB_TYPE_DATETIMELTZ:
      precision = DB_DATETIME_PRECISION;
      break;
    case DB_TYPE_DATETIMETZ:
      precision = DB_DATETIMETZ_PRECISION;
      break;
    case DB_TYPE_MONETARY:
      precision = DB_MONETARY_DECIMAL_PRECISION;
      break;
    default:
      precision = DB_DEFAULT_PRECISION;
      break;
    }

  return precision;
}

/*
 * tp_domain_free - free a hierarchical domain structure.
 *    return: none
 *    dom(out): domain to free
 * Note:
 *    This routine can be called for a transient or cached domain.  If
 *    the domain has been cached, the request is ignored.
 *    Note that you can only call this on the root of a domain hierarchy,
 *    you are not allowed to grab pointers into the middle of a hierarchical
 *    domain and free that.
 */
void
tp_domain_free (TP_DOMAIN * dom)
{
  TP_DOMAIN *d, *next;

  if (dom != NULL && !dom->is_cached)
    {
      /* NULL things that might be problems for garbage collection */
      dom->class_mop = NULL;

      if (dom->type->id == DB_TYPE_JSON && dom->json_validator != NULL)
    {
      db_json_delete_validator (dom->json_validator);
    }

      /*
       * sub-domains are always completely owned by their root domain,
       * they cannot be cached anywhere else.
       */
      for (d = dom->setdomain, next = NULL; d != NULL; d = next)
    {
      next = d->next;
      tp_domain_free (d);
    }

      if (dom->type->id == DB_TYPE_ENUMERATION)
    {
      tp_domain_clear_enumeration (&DOM_GET_ENUMERATION (dom));
    }

      (void) area_free (tp_Domain_area, dom);
    }
}

/*
 * tp_domain_init - initializes a domain structure to contain reasonable "default"
 * values.
 *    return: none
 *    domain(out): domain structure to initialize
 *    type_id(in): basic type of the domain
 * Note:
 *    Used by tp_domain_new and also in some other places
 *    where we need to quickly synthesize some transient domain structures.
 */
void
tp_domain_init (TP_DOMAIN * domain, DB_TYPE type_id)
{
  assert (type_id <= DB_TYPE_LAST);

  domain->next = NULL;
  domain->next_list = NULL;
  domain->type = pr_type_from_id (type_id);
  domain->precision = 0;
  domain->scale = 0;
  domain->class_mop = NULL;
  domain->self_ref = 0;
  domain->setdomain = NULL;
  domain->json_validator = NULL;
  DOM_SET_ENUM (domain, NULL, 0);
  OID_SET_NULL (&domain->class_oid);

  domain->collation_flag = TP_DOMAIN_COLL_NORMAL;
  if (TP_TYPE_HAS_COLLATION (type_id))
    {
      domain->codeset = LANG_SYS_CODESET;
      domain->collation_id = LANG_SYS_COLLATION;
      if (type_id == DB_TYPE_ENUMERATION)
    {
      domain->enumeration.collation_id = LANG_SYS_COLLATION;
    }
    }
  else if (TP_IS_BIT_TYPE (type_id))
    {
      domain->codeset = INTL_CODESET_RAW_BITS;
      domain->collation_id = 0;
    }
  else
    {
      domain->codeset = 0;
      domain->collation_id = 0;
    }
  domain->is_cached = 0;
  domain->built_in_index = 0;

  /* use the built-in domain template to see if we're parameterized or not */
  domain->is_parameterized = tp_Domains[type_id]->is_parameterized;

  domain->is_desc = 0;
}

/*
 * tp_domain_new - returns a new initialized transient domain.
 *    return: new transient domain
 *    type(in): type id
 * Note:
 *    It is intended for use in places where domains are being created
 *    incrementally for eventual passing to tp_domain_cache.
 *    Only the type id is passed here since that is the only common
 *    piece of information shared by all domains.
 *    The contents of the domain can be filled in by the caller assuming
 *    they obey the rules.
 */
TP_DOMAIN *
tp_domain_new (DB_TYPE type)
{
  TP_DOMAIN *new_dm;

  new_dm = (TP_DOMAIN *) area_alloc (tp_Domain_area);
  if (new_dm != NULL)
    {
      tp_domain_init (new_dm, type);
    }

  return new_dm;
}


/*
 * tp_domain_construct - create a transient domain object with type, class,
 * precision, scale and setdomain.
 *    return:
 *    domain_type(in): The basic type of the domain
 *    class_obj(in): The class of the domain (for DB_TYPE_OBJECT)
 *    precision(in): The precision of the domain
 *    scale(in): The class of the domain
 *    setdomain(in): The setdomain of the domain
 * Note:
 *    Used in a few places, callers must be aware that there may be more
 *    initializations to do since not all of the domain parameters are
 *    arguments to this function.
 *
 *    The setdomain must also be a transient domain list.
 */
TP_DOMAIN *
tp_domain_construct (DB_TYPE domain_type, DB_OBJECT * class_obj, int precision, int scale, TP_DOMAIN * setdomain)
{
  TP_DOMAIN *new_dm;
  int fixed_precision;

  new_dm = tp_domain_new (domain_type);
  if (new_dm)
    {
      fixed_precision = tp_get_fixed_precision (domain_type);
      if (fixed_precision != DB_DEFAULT_PRECISION)
    {
#if !defined (NDEBUG)
      if (precision != fixed_precision)
        {
          assert (false);
        }
#endif
      precision = fixed_precision;
    }

      new_dm->precision = precision;
      new_dm->scale = scale;
      new_dm->setdomain = setdomain;
      new_dm->json_validator = NULL;

#if !defined (NDEBUG)
      if (domain_type == DB_TYPE_MIDXKEY)
    {
      assert ((new_dm->setdomain && new_dm->precision == tp_domain_size (new_dm->setdomain))
          || (new_dm->setdomain == NULL && new_dm->precision == 0));

      {
        TP_DOMAIN *d;
        for (d = new_dm->setdomain; d != NULL; d = d->next)
          {
        assert (d->is_cached == 0);
          }
      }
    }
#endif /* NDEBUG */

      if (class_obj == (DB_OBJECT *) TP_DOMAIN_SELF_REF)
    {
      new_dm->class_mop = NULL;
      new_dm->self_ref = 1;
    }
      else
    {
      new_dm->class_mop = class_obj;
      new_dm->self_ref = 0;
      /*
       * For compatibility on the server side, class objects must have
       * the oid in the domain match the oid in the class object.
       */
      if (class_obj)
        {
#if defined (SERVER_MODE)
          assert_release (false);
#else /* !defined (SERVER_MODE) */
          new_dm->class_oid = class_obj->oid_info.oid;
#endif /* !SERVER_MODE */
        }
    }

      /*
       * have to leave the class OID uninitialized because we don't know how
       * to get an OID out of a DB_OBJECT on the server.
       * That shouldn't matter since the server side unpackers will use
       * tp_domain_new and set the domain fields directly.
       */
    }
  return new_dm;
}

/*
 * tp_domain_copy_enumeration () - copy an enumeration
 * return: error code or NO_ERROR
 * dest (in/out): destination enumeration
 * src (in) : source enumeration
 */
int
tp_domain_copy_enumeration (DB_ENUMERATION * dest, const DB_ENUMERATION * src)
{
  int error = NO_ERROR, i;
  DB_ENUM_ELEMENT *dest_elem = NULL, *src_elem = NULL;
  char *dest_str = NULL;

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

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

  dest->collation_id = src->collation_id;
  dest->count = 0;
  dest->elements = NULL;
  if (src->count == 0 && src->elements == NULL)
    {
      /* nothing else to do */
      return NO_ERROR;
    }

  /* validate source enumeration */
  if (src->count == 0 && src->elements != NULL)
    {
      assert (false);
      return ER_FAILED;
    }
  else if (src->count != 0 && src->elements == NULL)
    {
      assert (false);
      return ER_FAILED;
    }

  dest->count = src->count;

  dest->elements = (DB_ENUM_ELEMENT *) malloc (src->count * sizeof (DB_ENUM_ELEMENT));
  if (dest->elements == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, src->count * sizeof (DB_ENUM_ELEMENT));
      return ER_FAILED;
    }

  for (i = 0; i < src->count; i++)
    {
      src_elem = &src->elements[i];
      dest_elem = &dest->elements[i];

      DB_SET_ENUM_ELEM_SHORT (dest_elem, DB_GET_ENUM_ELEM_SHORT (src_elem));

      if (DB_GET_ENUM_ELEM_STRING (src_elem) != NULL)
    {
      dest_str = (char *) malloc (DB_GET_ENUM_ELEM_STRING_SIZE (src_elem) + 1);
      if (dest_str == NULL)
        {
          er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
              (size_t) (DB_GET_ENUM_ELEM_STRING_SIZE (src_elem) + 1));
          error = ER_OUT_OF_VIRTUAL_MEMORY;
          goto error_return;
        }
      memcpy (dest_str, DB_GET_ENUM_ELEM_STRING (src_elem), DB_GET_ENUM_ELEM_STRING_SIZE (src_elem));
      dest_str[DB_GET_ENUM_ELEM_STRING_SIZE (src_elem)] = 0;
      DB_SET_ENUM_ELEM_STRING (dest_elem, dest_str);
      DB_SET_ENUM_ELEM_STRING_SIZE (dest_elem, DB_GET_ENUM_ELEM_STRING_SIZE (src_elem));
    }
      else
    {
      DB_SET_ENUM_ELEM_STRING (dest_elem, NULL);
    }
      DB_SET_ENUM_ELEM_CODESET (dest_elem, DB_GET_ENUM_ELEM_CODESET (src_elem));
    }

  return NO_ERROR;

error_return:
  if (dest->elements != NULL)
    {
      for (--i; i >= 0; i--)
    {
      if (DB_GET_ENUM_ELEM_STRING (&dest->elements[i]) != NULL)
        {
          free_and_init (DB_GET_ENUM_ELEM_STRING (&dest->elements[i]));
        }
    }
      free_and_init (dest->elements);
    }

  return error;
}

/*
 * tp_domain_copy - copy a hierarcical domain structure
 *    return: new domain
 *    dom(in): domain to copy
 *    check_cache(in): if set, return cached instance
 * Note:
 *    If the domain was cached, we simply return a handle to the cached
 *    domain, otherwise we make a full structure copy.
 *    This should only be used in a few places in the schema manager which
 *    maintains separate copies of all the attribute domains during
 *    flattening.  Could be converted to used cached domains perhaps.
 *    But the "self referencing" domain is the problem.
 *
 *    New functionality:  If the check_cache parameter is false, we make
 *    a NEW copy of the parameter domain whether it is cached or not.   This
 *    is used for updating fields of a cached domain.  We don't want to
 *    update a domain that has already been cached because multiple structures
 *    may be pointing to it.
 */
TP_DOMAIN *
tp_domain_copy (const TP_DOMAIN * domain, bool check_cache)
{
  TP_DOMAIN *new_domain, *first, *last;
  const TP_DOMAIN *d;

  if (check_cache && domain->is_cached)
    {
      return (TP_DOMAIN *) domain;
    }

  first = NULL;
  if (domain != NULL)
    {
      last = NULL;

      for (d = domain; d != NULL; d = d->next)
    {
      new_domain = tp_domain_new (TP_DOMAIN_TYPE (d));
      if (new_domain == NULL)
        {
          goto error;
        }
      else
        {
          /* copy over the domain parameters */
          new_domain->class_mop = d->class_mop;
          new_domain->class_oid = d->class_oid;
          new_domain->precision = d->precision;
          new_domain->scale = d->scale;
          new_domain->codeset = d->codeset;
          new_domain->collation_id = d->collation_id;
          new_domain->self_ref = d->self_ref;
          new_domain->is_parameterized = d->is_parameterized;
          new_domain->is_desc = d->is_desc;
          new_domain->json_validator = NULL;

          if (d->type->id == DB_TYPE_JSON)
        {
          if (d->json_validator != NULL)
            {
              new_domain->json_validator = db_json_copy_validator (d->json_validator);
            }
        }

          if (d->type->id == DB_TYPE_ENUMERATION)
        {
          int error;
          error = tp_domain_copy_enumeration (&DOM_GET_ENUMERATION (new_domain), &DOM_GET_ENUMERATION (d));
          if (error != NO_ERROR)
            {
              goto error;
            }
        }

          if (d->setdomain != NULL)
        {
          new_domain->setdomain = tp_domain_copy (d->setdomain, true);
          if (new_domain->setdomain == NULL)
            {
              goto error;
            }
        }

          if (first == NULL)
        {
          first = new_domain;
        }
          else
        {
          last->next = new_domain;
        }
          last = new_domain;
        }
    }
    }

  return first;

error:
  for (d = first; d != NULL; d = d->next)
    {
      tp_domain_free (CONST_CAST (TP_DOMAIN *, d));
    }
  return NULL;
}


/*
 * tp_domain_size_internal - count the number of domains in a domain list
 *    return: number of domains in a domain list
 *    domain(in): a domain list
 */
static int
tp_domain_size_internal (const TP_DOMAIN * domain)
{
  int size = 0;

  while (domain)
    {
      ++size;
      domain = domain->next;
    }

  return size;
}

/*
 * tp_domain_size - count the number of domains in a domain list
 *    return: number of domains in a domain list
 *    domain(in): domain
 */
int
tp_domain_size (const TP_DOMAIN * domain)
{
  return tp_domain_size_internal (domain);
}

/*
 * tp_setdomain_size - count the number of domains in a setdomain list
 *    return: number of domains in a setdomain list
 *    domain(in): domain
 */
int
tp_setdomain_size (const TP_DOMAIN * domain)
{
  if (TP_DOMAIN_TYPE (domain) == DB_TYPE_MIDXKEY)
    {
      assert ((domain->setdomain && domain->precision == tp_domain_size (domain->setdomain))
          || (domain->setdomain == NULL && domain->precision == 0));
      return domain->precision;
    }
  else
    {
      return tp_domain_size_internal (domain->setdomain);
    }
}

/*
 * tp_value_slam_domain - alter the domain of an existing DB_VALUE
 *    return: nothing
 *    value(out): value whose domain is to be altered
 *    domain(in): domain descriptor
 * Note:
 * used usually in a context like tp_value_cast where we know that we
 * have a perfectly good fixed-length string that we want tocast as a varchar.
 *
 * This is a dangerous function and should not be exported to users.  use
 * only if you know exactly what you're doing!!!!
 */
static void
tp_value_slam_domain (DB_VALUE * value, const DB_DOMAIN * domain)
{
  switch (TP_DOMAIN_TYPE (domain))
    {
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      if (domain->collation_flag == TP_DOMAIN_COLL_ENFORCE)
    {
      db_string_put_cs_and_collation (value, TP_DOMAIN_CODESET (domain), TP_DOMAIN_COLLATION (domain));
      /* don't apply precision and type */
      break;
    }
      if (domain->collation_flag == TP_DOMAIN_COLL_NORMAL)
    {
      db_string_put_cs_and_collation (value, TP_DOMAIN_CODESET (domain), TP_DOMAIN_COLLATION (domain));
    }
      [[fallthrough]];
    case DB_TYPE_BIT:
    case DB_TYPE_VARBIT:
      value->domain.char_info.type = TP_DOMAIN_TYPE (domain);
      value->domain.char_info.length = domain->precision;
      break;

    case DB_TYPE_NUMERIC:
      value->domain.numeric_info.type = TP_DOMAIN_TYPE (domain);
      value->domain.numeric_info.precision = domain->precision;
      value->domain.numeric_info.scale = domain->scale;
      break;

    default:
      value->domain.general_info.type = TP_DOMAIN_TYPE (domain);
      break;
    }
}

/*
 * tp_domain_match - examins two domains to see if they are logically same
 *    return: non-zero if the domains are the same
 *    dom1(in): first domain
 *    dom2(in): second domain
 *    exact(in): how tolerant we are of mismatches
 */
int
tp_domain_match (const TP_DOMAIN * dom1, const TP_DOMAIN * dom2, TP_MATCH exact)
{
  return tp_domain_match_internal (dom1, dom2, exact, true);
}

/*
 * tp_domain_match_ignore_order - examins two domains to see if they are logically same
 *    return: non-zero if the domains are the same
 *    dom1(in): first domain
 *    dom2(in): second domain
 *    exact(in): how tolerant we are of mismatches
 */
int
tp_domain_match_ignore_order (const TP_DOMAIN * dom1, const TP_DOMAIN * dom2, TP_MATCH exact)
{
  return tp_domain_match_internal (dom1, dom2, exact, false);
}

/*
 * tp_domain_match_internal - examins two domains to see if they are logically same
 *    return: non-zero if the domains are the same
 *    dom1(in): first domain
 *    dom2(in): second domain
 *    exact(in): how tolerant we are of mismatches
 *    match_order(in): check for asc/desc
 */
static int
tp_domain_match_internal (const TP_DOMAIN * dom1, const TP_DOMAIN * dom2, TP_MATCH exact, bool match_order)
{
  int match = 0;

  if (dom1 == NULL || dom2 == NULL)
    {
      return 0;
    }

  /* in the case where their both cached */
  if (dom1 == dom2)
    {
      return 1;
    }

  if ((TP_DOMAIN_TYPE (dom1) != TP_DOMAIN_TYPE (dom2))
      && (exact != TP_STR_MATCH || !TP_NEAR_MATCH (TP_DOMAIN_TYPE (dom1), TP_DOMAIN_TYPE (dom2))))
    {
      return 0;
    }

  /*
   * At this point, either dom1 and dom2 have exactly the same type, or
   * exact_match is TP_STR_MATCH and dom1 and dom2 are a char/varchar
   * (bit/varbit) pair.
   */

  /* check for asc/desc */
  if (TP_DOMAIN_TYPE (dom1) == TP_DOMAIN_TYPE (dom2) && tp_valid_indextype (TP_DOMAIN_TYPE (dom1))
      && match_order == true && dom1->is_desc != dom2->is_desc)
    {
      return 0;
    }

  /* could use the new is_parameterized flag to avoid the switch ? */

  switch (TP_DOMAIN_TYPE (dom1))
    {
    case DB_TYPE_NULL:
    case DB_TYPE_INTEGER:
    case DB_TYPE_BIGINT:
    case DB_TYPE_FLOAT:
    case DB_TYPE_DOUBLE:
    case DB_TYPE_BLOB:
    case DB_TYPE_CLOB:
    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_DATE:
    case DB_TYPE_MONETARY:
    case DB_TYPE_SHORT:
      /*
       * these domains have no parameters, they match if the types are the
       * same.
       */
      match = 1;
      break;

    case DB_TYPE_JSON:
      match = (int) db_json_are_validators_equal (dom1->json_validator, dom2->json_validator);
      break;

    case DB_TYPE_VOBJ:
    case DB_TYPE_OBJECT:
    case DB_TYPE_SUB:

#if defined (SERVER_MODE)
      match = OID_EQ (&dom1->class_oid, &dom2->class_oid);
#else /* !defined (SERVER_MODE) */
      /*
       * if "exact" is zero, we should be checking the subclass hierarchy of
       * dom1 to see id dom2 is in it !
       */

      /* Always prefer comparison of MOPs */
      if (dom1->class_mop != NULL && dom2->class_mop != NULL)
    {
      match = (dom1->class_mop == dom2->class_mop);
    }
      else if (dom1->class_mop == NULL && dom2->class_mop == NULL)
    {
      match = OID_EQ (&dom1->class_oid, &dom2->class_oid);
    }
      else
    {
      /*
       * We have a mixture of OID & MOPS, it probably isn't necessary to
       * be this general but try to avoid assuming the class OIDs have
       * been set when there is a MOP present.
       */
      if (dom1->class_mop == NULL)
        {
          match = OID_EQ (&dom1->class_oid, WS_OID (dom2->class_mop));
        }
      else
        {
          match = OID_EQ (WS_OID (dom1->class_mop), &dom2->class_oid);
        }
    }
#endif /* defined (SERVER_MODE) */

      if (match == 0 && exact == TP_SET_MATCH && dom1->class_mop == NULL && OID_ISNULL (&dom1->class_oid))
    {
      match = 1;
    }
      break;

    case DB_TYPE_VARIABLE:
    case DB_TYPE_SET:
    case DB_TYPE_MULTISET:
    case DB_TYPE_SEQUENCE:
#if 1
      /* >>>>> NEED MORE CONSIDERATION <<<<< do not check order must be rollback with tp_domain_add() */
      if (dom1->setdomain == dom2->setdomain)
    {
      match = 1;
    }
      else
    {
      int dsize;

      /* don't bother comparing the lists unless the sizes are the same */
      dsize = tp_domain_size (dom1->setdomain);
      if (dsize == tp_domain_size (dom2->setdomain))
        {
          /* handle the simple single domain case quickly */
          if (dsize == 1)
        {
          match = tp_domain_match (dom1->setdomain, dom2->setdomain, exact);
        }
          else
        {
          TP_DOMAIN *d1, *d2;

          match = 1;
          for (d1 = dom1->setdomain, d2 = dom2->setdomain; d1 != NULL && d2 != NULL;
               d1 = d1->next, d2 = d2->next)
            {
              if (!tp_domain_match (d1, d2, exact))
            {
              match = 0;
              break;    /* immediately exit for loop */
            }
            }
        }
        }
    }
#else /* 0 */
      if (dom1->setdomain == dom2->setdomain)
    {
      match = 1;
    }
      else
    {
      int dsize;

      /* don't bother comparing the lists unless the sizes are the same */
      dsize = tp_domain_size (dom1->setdomain);
      if (dsize == tp_domain_size (dom2->setdomain))
        {

          /* handle the simple single domain case quickly */
          if (dsize == 1)
        {
          match = tp_domain_match (dom1->setdomain, dom2->setdomain, exact);
        }
          else
        {
          TP_DOMAIN *d1, *d2;

          /* clear the visited flag in the second subdomain list */
          for (d2 = dom2->setdomain; d2 != NULL; d2 = d2->next)
            {
              d2->is_visited = 0;
            }

          match = 1;
          for (d1 = dom1->setdomain; d1 != NULL && match; d1 = d1->next)
            {
              for (d2 = dom2->setdomain; d2 != NULL; d2 = d2->next)
            {
              if (!d2->is_visited && tp_domain_match (d1, d2, exact))
                {
                  break;
                }
            }
              /* did we find the domain in the other list ? */
              if (d2 != NULL)
            {
              d2->is_visited = 1;
            }
              else
            {
              match = 0;
            }
            }
        }
        }
    }
#endif /* 1 */
      break;

    case DB_TYPE_MIDXKEY:
      if (dom1->setdomain == dom2->setdomain)
    {
      match = 1;
    }
      else
    {
      int i, dsize1, dsize2;
      TP_DOMAIN *element_dom1;
      TP_DOMAIN *element_dom2;

      dsize1 = tp_domain_size (dom1->setdomain);
      dsize2 = tp_domain_size (dom2->setdomain);

      if (dsize1 == dsize2)
        {
          match = 1;
          element_dom1 = dom1->setdomain;
          element_dom2 = dom2->setdomain;

          for (i = 0; i < dsize1; i++)
        {
          match = tp_domain_match (element_dom1, element_dom2, exact);
          if (match == 0)
            {
              break;
            }
          element_dom1 = element_dom1->next;
          element_dom2 = element_dom2->next;
        }
        }
    }
      break;

    case DB_TYPE_VARCHAR:
      if (dom1->collation_id != dom2->collation_id)
    {
      match = 0;
      break;
    }
      [[fallthrough]];
    case DB_TYPE_VARBIT:
      if (exact == TP_EXACT_MATCH || exact == TP_SET_MATCH)
    {
      match = dom1->precision == dom2->precision;
    }
      else if (exact == TP_STR_MATCH)
    {
      /*
       * Allow the match if the precisions would allow us to reuse the
       * string without modification.
       */
      match = (dom1->precision >= dom2->precision);
    }
      else
    {
      /*
       * Allow matches regardless of precision, let the actual length of the
       * value determine if it can be assigned.  This is important for
       * literal strings as their precision will be the maximum but they
       * can still be assigned to domains with a smaller precision
       * provided the actual value is within the destination domain
       * tolerance.
       */
      match = 1;
    }
      break;

    case DB_TYPE_CHAR:
      if (dom1->collation_id != dom2->collation_id)
    {
      match = 0;
      break;
    }
      [[fallthrough]];
    case DB_TYPE_BIT:
      /*
       * Unlike varchar, we have to be a little tighter on domain matches for
       * fixed width char.  Not as much of a problem since these won't be
       * used for literal strings.
       */
      if (exact == TP_EXACT_MATCH || exact == TP_STR_MATCH || exact == TP_SET_MATCH)
    {
      match = (dom1->precision == dom2->precision);
    }
      else
    {
      /* Recognize a precision of TP_FLOATING_PRECISION_VALUE to indicate a precision whose coercability must be
       * determined by examing the value.  This is used primarily by db_coerce() since it must pick a reasonable
       * CHAR domain for the representation of a literal string. Accept zero here too since it seems to creep into
       * domains sometimes. */
      match = (dom2->precision == 0 || dom2->precision == TP_FLOATING_PRECISION_VALUE
           || dom1->precision >= dom2->precision);
    }
      break;

    case DB_TYPE_NUMERIC:
      /*
       * note that we never allow inexact matches here because the
       * mr_setmem_numeric function is not currently able to perform the
       * deferred coercion.
       */
      match = ((dom1->precision == dom2->precision) && (dom1->scale == dom2->scale));
      break;

    case DB_TYPE_POINTER:
    case DB_TYPE_ERROR:
    case DB_TYPE_OID:
    case DB_TYPE_DB_VALUE:
      /*
       * These are internal domains, they shouldn't be seen, in case they are,
       * just let them match without parameters.
       */
      match = 1;
      break;

    case DB_TYPE_ENUMERATION:
      match = tp_enumeration_match (&DOM_GET_ENUMERATION (dom1), &DOM_GET_ENUMERATION (dom2));
      break;

    case DB_TYPE_RESULTSET:
    case DB_TYPE_TABLE:
      break;
    case DB_TYPE_ELO:
    default:
      assert (false);
      break;
      /* don't have a default so we make sure to add clauses for all types */
    }

#if !defined (NDEBUG)
  if (match && TP_TYPE_HAS_COLLATION (TP_DOMAIN_TYPE (dom1)))
    {
      assert (dom1->codeset == dom2->codeset);
    }
#endif

  return match;
}

/*
 * tp_domain_get_list_ptr - get the pointer of the head of domain list
 *    return: pointer of the head of the list
 *    type(in): type of value
 *    setdomain(in): used to find appropriate list of MIDXKEY
 */
STATIC_INLINE TP_DOMAIN **
tp_domain_get_list_ptr (DB_TYPE type, TP_DOMAIN * setdomain)
{
  int list_index;

  if (type == DB_TYPE_MIDXKEY)
    {
      list_index = tp_domain_size (setdomain);
      list_index %= TP_NUM_MIDXKEY_DOMAIN_LIST;
      return &(tp_Midxkey_domains[list_index]);
    }
  else
    {
      return &(tp_Domains[type]);
    }
}

/*
 * tp_domain_get_list - get the head of domain list
 *    return: the head of the list
 *    type(in): type of value
 *    setdomain(in): used to find appropriate list of MIDXKEY
 */
STATIC_INLINE TP_DOMAIN *
tp_domain_get_list (DB_TYPE type, TP_DOMAIN * setdomain)
{
  TP_DOMAIN **dlist;

  dlist = tp_domain_get_list_ptr (type, setdomain);
  return *dlist;
}

/*
 * tp_is_domain_cached - find matching domain from domain list
 *    return: matched domain
 *    dlist(in): domain list
 *    transient(in): transient domain
 *    exact(in): matching level
 *    ins_pos(out): domain found
 * Note:
 * DB_TYPE_VARCHAR, DB_TYPE_VARBIT, DB_TYPE_VARNCHAR : precision's desc order
 *                                             others: precision's asc order
 */
static TP_DOMAIN *
tp_is_domain_cached (TP_DOMAIN * dlist, TP_DOMAIN * transient, TP_MATCH exact, TP_DOMAIN ** ins_pos)
{
  TP_DOMAIN *domain = dlist;
  int match = 0;

  /* in the case where their both cached */
  if (domain == transient)
    {
      return domain;
    }

  if ((TP_DOMAIN_TYPE (domain) != TP_DOMAIN_TYPE (transient))
      && (exact != TP_STR_MATCH || !TP_NEAR_MATCH (TP_DOMAIN_TYPE (domain), TP_DOMAIN_TYPE (transient))))
    {
      return NULL;
    }

  *ins_pos = domain;

  /*
   * At this point, either domain and transient have exactly the same type, or
   * exact_match is TP_STR_MATCH and domain and transient are a char/varchar
   * (bit/varbit) pair.
   */

  /* could use the new is_parameterized flag to avoid the switch ? */
  switch (TP_DOMAIN_TYPE (domain))
    {

    case DB_TYPE_NULL:
    case DB_TYPE_INTEGER:
    case DB_TYPE_BIGINT:
    case DB_TYPE_FLOAT:
    case DB_TYPE_DOUBLE:
    case DB_TYPE_BLOB:
    case DB_TYPE_CLOB:
    case DB_TYPE_TIME:
    case DB_TYPE_TIMESTAMP:
    case DB_TYPE_TIMESTAMPLTZ:
    case DB_TYPE_TIMESTAMPTZ:
    case DB_TYPE_DATETIME:
    case DB_TYPE_DATETIMELTZ:
    case DB_TYPE_DATETIMETZ:
    case DB_TYPE_DATE:
    case DB_TYPE_MONETARY:
    case DB_TYPE_SHORT:
      /*
       * these domains have no parameters, they match if asc/desc are the
       * same
       */
      while (domain)
    {
      if (domain->is_desc == transient->is_desc)
        {
          match = 1;
          break;
        }

      *ins_pos = domain;
      domain = domain->next_list;
    }
      break;

    case DB_TYPE_VOBJ:
    case DB_TYPE_OBJECT:
    case DB_TYPE_SUB:

#if defined (SERVER_MODE)
      /* not match for these types on server... fall through. */
#else /* !defined (SERVER_MODE) */

      while (domain)
    {
      /*
       * if "exact" is zero, we should be checking the subclass hierarchy
       * of domain to see id transient is in it !
       */

      /* Always prefer comparison of MOPs */
      if (domain->class_mop != NULL && transient->class_mop != NULL)
        {
          match = (domain->class_mop == transient->class_mop);
        }
      else if (domain->class_mop == NULL && transient->class_mop == NULL)
        {
          match = OID_EQ (&domain->class_oid, &transient->class_oid);
        }
      else
        {
          /*
           * We have a mixture of OID & MOPS, it probably isn't necessary
           * to be this general but try to avoid assuming the class OIDs
           * have been set when there is a MOP present.
           */
          if (domain->class_mop != NULL)
        {
          match = OID_EQ (WS_OID (domain->class_mop), &transient->class_oid);
        }
        }

      if (match == 0 && exact == TP_SET_MATCH && domain->class_mop == NULL && OID_ISNULL (&domain->class_oid))
        {
          /* check for asc/desc */
          if (domain->is_desc == transient->is_desc)
        {
          match = 1;
        }
        }

      if (match)
        {
          break;
        }

      *ins_pos = domain;
      domain = domain->next_list;
    }
#endif /* !defined (SERVER_MODE) */
      break;

    case DB_TYPE_VARIABLE:
    case DB_TYPE_SET:
    case DB_TYPE_MULTISET:
    case DB_TYPE_SEQUENCE:
      {
    int dsize2;

    dsize2 = tp_domain_size (transient->setdomain);
    while (domain)
      {
#if 1
        /* >>>>> NEED MORE CONSIDERATION <<<<< do not check order must be rollback with tp_domain_add() */
        if (domain->setdomain == transient->setdomain)
          {
        match = 1;
          }
        else
          {
        int dsize1;

        /*
         * don't bother comparing the lists unless the sizes are the
         * same
         */
        dsize1 = tp_domain_size (domain->setdomain);
        if (dsize1 > dsize2)
          {
            break;
          }

        if (dsize1 == dsize2)
          {

            /* handle the simple single domain case quickly */
            if (dsize1 == 1)
              {
            match = tp_domain_match (domain->setdomain, transient->setdomain, exact);
              }
            else
              {
            TP_DOMAIN *d1, *d2;

            match = 1;
            for (d1 = domain->setdomain, d2 = transient->setdomain; d1 != NULL && d2 != NULL;
                 d1 = d1->next, d2 = d2->next)
              {
                if (!tp_domain_match (d1, d2, exact))
                  {
                match = 0;
                break;  /* immediately exit for loop */
                  }
              } /* for */
              }
          }     /* if (dsize1 == dsize2) */
          }
#else /* #if 1 */
        if (domain->setdomain == transient->setdomain)
          {
        match = 1;
          }
        else
          {
        int dsize;

        /*
         * don't bother comparing the lists unless the sizes are the
         * same
         */
        dsize = tp_domain_size (domain->setdomain);
        if (dsize == tp_domain_size (transient->setdomain))
          {

            /* handle the simple single domain case quickly */
            if (dsize == 1)
              {
            match = tp_domain_match (domain->setdomain, transient->setdomain, exact);
              }
            else
              {
            TP_DOMAIN *d1, *d2;

            /* clear the visited flag of second subdomain list */
            for (d2 = transient->setdomain; d2 != NULL; d2 = d2->next)
              {
                d2->is_visited = 0;
              }

            match = 1;
            for (d1 = domain->setdomain; d1 != NULL && match; d1 = d1->next)
              {
                for (d2 = transient->setdomain; d2 != NULL; d2 = d2->next)
                  {
                if (!d2->is_visited && tp_domain_match (d1, d2, exact))
                  {
                    break;
                  }
                  }
                /* did we find the domain in the other list ? */
                if (d2 != NULL)
                  {
                d2->is_visited = 1;
                  }
                else
                  {
                match = 0;
                  }
              }
              }
          }
          }
#endif /* #if 1 */

        if (match)
          {
        break;
          }

        *ins_pos = domain;
        domain = domain->next_list;
      }
      }
      break;

    case DB_TYPE_MIDXKEY:
      {
    int dsize2;

    dsize2 = tp_setdomain_size (transient);
    while (domain)
      {
        if (domain->setdomain == transient->setdomain)
          {
        match = 1;
          }
        else
          {
        int i, dsize1;
        TP_DOMAIN *element_dom1;
        TP_DOMAIN *element_dom2;

        dsize1 = tp_setdomain_size (domain);
        if (dsize1 > dsize2)
          {
            break;
          }

        if (dsize1 == dsize2)
          {
            match = 1;
            element_dom1 = domain->setdomain;
            element_dom2 = transient->setdomain;

            for (i = 0; i < dsize1; i++)
              {
            match = tp_domain_match (element_dom1, element_dom2, exact);
            if (match == 0)
              {
                break;
              }

            element_dom1 = element_dom1->next;
            element_dom2 = element_dom2->next;
              }
          }
          }
        if (match)
          {
        break;
          }

        *ins_pos = domain;
        domain = domain->next_list;
      }
      }
      break;

    case DB_TYPE_JSON:
      while (domain)
    {
      match = (int) db_json_are_validators_equal (transient->json_validator, domain->json_validator);

      if (match)
        {
          break;
        }
      *ins_pos = domain;
      domain = domain->next_list;
    }
      break;

    case DB_TYPE_VARCHAR:
      while (domain)
    {
      if (exact == TP_EXACT_MATCH || exact == TP_SET_MATCH)
        {
          /* check for descending order */
          if (domain->precision < transient->precision)
        {
          break;
        }

          match = ((domain->precision == transient->precision) && (domain->collation_id == transient->collation_id)
               && (domain->codeset == transient->codeset)
               && (domain->is_desc == transient->is_desc)
               && (domain->collation_flag == transient->collation_flag));
        }
      else if (exact == TP_STR_MATCH)
        {
          /*
           * Allow the match if the precisions would allow us to reuse the
           * string without modification.
           */
          match = ((domain->precision >= transient->precision) && (domain->collation_id == transient->collation_id)
               && (domain->codeset == transient->codeset)
               && (domain->is_desc == transient->is_desc)
               && (domain->collation_flag == transient->collation_flag));
        }
      else
        {
          /*
           * Allow matches regardless of precision, let the actual length
           * of the value determine if it can be assigned.  This is
           * important for literal strings as their precision will be the
           * maximum but they can still be assigned to domains with a
           * smaller precision provided the actual value is within the
           * destination domain tolerance.
           */
          match = ((domain->collation_id == transient->collation_id) && (domain->is_desc == transient->is_desc)
               && (domain->codeset == transient->codeset)
               && (domain->collation_flag == transient->collation_flag));
        }

      if (match)
        {
          assert (domain->codeset == transient->codeset);
          break;
        }

      *ins_pos = domain;
      domain = domain->next_list;
    }
      break;

    case DB_TYPE_VARBIT:
      while (domain)
    {
      if (exact == TP_EXACT_MATCH || exact == TP_SET_MATCH)
        {
          /* check for descending order */
          if (domain->precision < transient->precision)
        {
          break;
        }

          match = ((domain->precision == transient->precision) && (domain->is_desc == transient->is_desc));
        }
      else if (exact == TP_STR_MATCH)
        {
          /*
           * Allow the match if the precisions would allow us to reuse the
           * string without modification.
           */
          match = ((domain->precision >= transient->precision) && (domain->is_desc == transient->is_desc));
        }
      else
        {
          /*
           * Allow matches regardless of precision, let the actual length
           * of the value determine if it can be assigned.  This is
           * important for literal strings as their precision will be the
           * maximum but they can still be assigned to domains with a
           * smaller precision provided the actual value is within the
           * destination domain tolerance.
           */
          match = (domain->is_desc == transient->is_desc);
        }

      if (match)
        {
          break;
        }

      *ins_pos = domain;
      domain = domain->next_list;
    }
      break;

    case DB_TYPE_BIT:
      while (domain)
    {
      /*
       * Unlike varchar, we have to be a little tighter on domain matches
       * for fixed width char.  Not as much of a problem since these won't
       * be used for literal strings.
       */
      if (exact == TP_EXACT_MATCH || exact == TP_STR_MATCH || exact == TP_SET_MATCH)
        {
          if (domain->precision > transient->precision)
        {
          break;
        }

          match = ((domain->precision == transient->precision) && (domain->is_desc == transient->is_desc));
        }
      else
        {
          /*
           * Recognize a precision of TP_FLOATING_PRECISION_VALUE to
           * indicate a precision whose coercability must be determined
           * by examing the value.  This is used primarily by db_coerce()
           * since it must pick a reasonable CHAR domain for the
           * representation of a literal string.
           * Accept zero here too since it seems to creep into domains
           * sometimes.
           */
          match =
        ((transient->precision == 0 || transient->precision == TP_FLOATING_PRECISION_VALUE
          || domain->precision >= transient->precision) && (domain->is_desc == transient->is_desc));
        }

      if (match)
        {
          break;
        }

      *ins_pos = domain;
      domain = domain->next_list;
    }
      break;

    case DB_TYPE_CHAR:
      while (domain)
    {
      if (exact == TP_EXACT_MATCH || exact == TP_STR_MATCH || exact == TP_SET_MATCH)
        {
          if (domain->precision > transient->precision)
        {
          break;
        }

          match = ((domain->precision == transient->precision) && (domain->collation_id == transient->collation_id)
               && (domain->codeset == transient->codeset)
               && (domain->is_desc == transient->is_desc)
               && (domain->collation_flag == transient->collation_flag));
        }
      else
        {
          /*
           * see discussion of special domain precision values
           * in the DB_TYPE_CHAR case above.
           */
          match = ((domain->collation_id == transient->collation_id)
               && (domain->codeset == transient->codeset)
               && (transient->precision == 0 || (transient->precision == TP_FLOATING_PRECISION_VALUE)
               || domain->precision >= transient->precision)
               && (domain->is_desc == transient->is_desc)
               && (domain->collation_flag == transient->collation_flag));
        }

      if (match)
        {
          assert (domain->codeset == transient->codeset);
          break;
        }

      *ins_pos = domain;
      domain = domain->next_list;
    }

      break;

    case DB_TYPE_NUMERIC:
      /*
       * The first domain is a default domain for numeric type,
       * actually NUMERIC(15,0). We try to match it first.
       */
      if (transient->precision == domain->precision && transient->scale == domain->scale
      && transient->is_desc == domain->is_desc)
    {
      match = 1;
      break;
    }

      domain = domain->next_list;
      while (domain)
    {
      /*
       * The other domains for numeric values are sorted
       * by descending order of precision and scale.
       */
      if ((domain->precision < transient->precision)
          || ((domain->precision == transient->precision) && (domain->scale < transient->scale)))
        {
          break;
        }

      /*
       * note that we never allow inexact matches here because
       * the mr_setmem_numeric function is not currently able
       * to perform the deferred coercion.
       */
      match = ((domain->precision == transient->precision) && (domain->scale == transient->scale)
           && (domain->is_desc == transient->is_desc));
      if (match)
        {
          break;
        }

      *ins_pos = domain;
      domain = domain->next_list;
    }
      break;

    case DB_TYPE_POINTER:
    case DB_TYPE_ERROR:
    case DB_TYPE_OID:
    case DB_TYPE_DB_VALUE:
      /*
       * These are internal domains, they shouldn't be seen, in case they are,
       * just let them match without parameters.
       */

      while (domain)
    {
      if (domain->is_desc == transient->is_desc)
        {
          match = 1;
          break;
        }

      *ins_pos = domain;
      domain = domain->next_list;
    }
      break;

    case DB_TYPE_ENUMERATION:
      while (domain != NULL)
    {
      if (tp_enumeration_match (&DOM_GET_ENUMERATION (domain), &DOM_GET_ENUMERATION (transient)) != 0)
        {
          match = 1;
          break;
        }
      domain = domain->next_list;
    }
      break;

    case DB_TYPE_RESULTSET:
    case DB_TYPE_TABLE:
      break;
    case DB_TYPE_ELO:
    default:
      assert (false);
      break;
      /* don't have a default so we make sure to add clauses for all types */
    }

  return (match ? domain : NULL);
}

#if !defined (SERVER_MODE)
/*
 * tp_swizzle_oid - swizzle oid of a domain class recursively
 *    return: void
 *    domain(in): domain to swizzle
 * Note:
 *   If the code caching the domain was written for the server, we will
 *   only have the OID of the class here if this is an object domain.  If
 *   the domain table is being shared by the client and server (e.g. in
 *   standalone mode), it is important that we "swizzle" the OID into
 *   a corresponding workspace MOP during the cache.  This ensures that we
 *   never get an object domain entered into the client's domain table that
 *   doesn't have a real DB_OBJECT pointer for the domain class.  There is
 *   a lot of code that expects this to be the case.
 */
static void
tp_swizzle_oid (TP_DOMAIN * domain)
{
  TP_DOMAIN *d;
  DB_TYPE type;

  type = TP_DOMAIN_TYPE (domain);

  if ((type == DB_TYPE_OBJECT || type == DB_TYPE_OID || type == DB_TYPE_VOBJ) && domain->class_mop == NULL
      && !OID_ISNULL (&domain->class_oid))
    {
      /* swizzle the pointer if we're on the client */
      domain->class_mop = ws_mop (&domain->class_oid, NULL);
    }
  else if (TP_IS_SET_TYPE (type))
    {
      for (d = domain->setdomain; d != NULL; d = d->next)
    {
      tp_swizzle_oid (d);
    }
    }
}
#endif /* !SERVER_MODE */

/*
 * tp_domain_find_noparam - get domain for give type
 *    return: domain
 *    type(in): domain type
 *    is_desc(in): desc order for index key_type
 */
TP_DOMAIN *
tp_domain_find_noparam (DB_TYPE type, bool is_desc)
{
  TP_DOMAIN *dom;

  /* tp_domain_find_with_no_param */
  /* type : DB_TYPE_NULL DB_TYPE_INTEGER DB_TYPE_FLOAT DB_TYPE_DOUBLE DB_TYPE_ELO DB_TYPE_TIME DB_TYPE_BLOB
   * DB_TYPE_CLOB DB_TYPE_TIMESTAMP DB_TYPE_DATE DB_TYPE_DATETIME DB_TYPE_MONETARY DB_TYPE_SHORT DB_TYPE_BIGINT
   * DB_TYPE_TIMESTAMPTZ DB_TYPE_TIMESTAMPLTZ DB_TYPE_DATETIMETZ DB_TYPE_DATETIMELTZ */

  for (dom = tp_domain_get_list (type, NULL); dom != NULL; dom = dom->next_list)
    {
      if (dom->is_desc == is_desc)
    {
      break;        /* found */
    }
    }

  return dom;
}

/*
 * tp_domain_find_numeric - find domain for given type, precision and scale
 *    return: domain that matches
 *    type(in): DB_TYPE
 *    precision(in): precision
 *    scale(in): scale
 *    is_desc(in): desc order for index key_type
 */
TP_DOMAIN *
tp_domain_find_numeric (DB_TYPE type, int precision, int scale, bool is_desc)
{
  TP_DOMAIN *dom;

  /* tp_domain_find_with_precision_scale */
  /* type : DB_TYPE_NUMERIC */
  assert (type == DB_TYPE_NUMERIC);

  /*
   * The first domain is a default domain for numeric type,
   * actually NUMERIC(15,0). We try to match it first.
   */
  dom = tp_domain_get_list (type, NULL);
  if (precision == dom->precision && scale == dom->scale && is_desc == dom->is_desc)
    {
      return dom;
    }

  /* search the list for a domain that matches */
  for (dom = dom->next_list; dom != NULL; dom = dom->next_list)
    {
      if ((precision > dom->precision) || ((precision == dom->precision) && (scale > dom->scale)))
    {
      return NULL;      /* not exist */
    }

      /* we MUST perform exact matches here */
      if (dom->precision == precision && dom->scale == scale && dom->is_desc == is_desc)
    {
      break;        /* found */
    }
    }

  return dom;
}

/*
 * tp_domain_find_charbit - find domain for given codeset and precision
 *    return: domain that matches
 *    type(in): DB_TYPE
 *    codeset(in): code set
 *    collation_id(in): collation id
 *    precision(in): precision
 *    is_desc(in): desc order for index key_type
 */
TP_DOMAIN *
tp_domain_find_charbit (DB_TYPE type, int codeset, int collation_id, unsigned char collation_flag, int precision,
            bool is_desc)
{
  TP_DOMAIN *dom;

  /* tp_domain_find_with_codeset_precision */
  /*
   * type : DB_TYPE_CHAR    DB_TYPE_VARCHAR
   * DB_TYPE_BIT     DB_TYPE_VARBIT
   */
  assert (type == DB_TYPE_CHAR || type == DB_TYPE_VARCHAR || type == DB_TYPE_BIT || type == DB_TYPE_VARBIT);

  if (type == DB_TYPE_VARCHAR || type == DB_TYPE_VARBIT)
    {
      /* search the list for a domain that matches */
      for (dom = tp_domain_get_list (type, NULL); dom != NULL; dom = dom->next_list)
    {
      /* Variable character/bit is sorted in descending order of precision. */
      if (precision > dom->precision)
        {
          return NULL;  /* not exist */
        }

      /* we MUST perform exact matches here */
      if (dom->precision == precision && dom->is_desc == is_desc)
        {
          if (type == DB_TYPE_VARBIT)
        {
          break;    /* found */
        }
          else if (dom->collation_id == collation_id && dom->collation_flag == collation_flag
               && dom->codeset == codeset)
        {
          /* codeset should be the same if collations are equal */
          assert (dom->codeset == codeset);
          break;
        }
        }
    }
    }
  else
    {
      /* search the list for a domain that matches */
      for (dom = tp_domain_get_list (type, NULL); dom != NULL; dom = dom->next_list)
    {
      /* Fixed character/bit is sorted in ascending order of precision. */
      if (precision < dom->precision)
        {
          return NULL;  /* not exist */
        }

      /* we MUST perform exact matches here */
      if (dom->precision == precision && dom->is_desc == is_desc)
        {
          if (type == DB_TYPE_BIT)
        {
          break;    /* found */
        }
          else if (dom->collation_id == collation_id && dom->collation_flag == collation_flag)
        {
          /* codeset should be the same if collations are equal */
          assert (dom->codeset == codeset);
          break;
        }
        }
    }
    }

  return dom;
}

/*
 * tp_domain_find_object - find domain for given class OID and class
 *    return: domain that matches
 *    type(in): DB_TYPE
 *    class_oid(in): class oid
 *    class_mop(in): class structure
 *    is_desc(in): desc order for index key_type
 */
TP_DOMAIN *
tp_domain_find_object (DB_TYPE type, OID * class_oid, struct db_object * class_mop, bool is_desc)
{
  TP_DOMAIN *dom;

  /* tp_domain_find_with_classinfo */

  /* search the list for a domain that matches */
  for (dom = tp_domain_get_list (type, NULL); dom != NULL; dom = dom->next_list)
    {
      /* we MUST perform exact matches here */

      /* Always prefer comparison of MOPs */
      if (dom->class_mop != NULL && class_mop != NULL)
    {
      if (dom->class_mop == class_mop && dom->is_desc == is_desc)
        {
          break;        /* found */
        }
    }
      else if (dom->class_mop == NULL && class_mop == NULL)
    {
      if (OID_EQ (&dom->class_oid, class_oid) && dom->is_desc == is_desc)
        {
          break;        /* found */
        }
    }
      else
    {
#if defined (SERVER_MODE)
      assert_release (false);
#else /* defined (SERVER_MODE) */
      /*
       * We have a mixture of OID & MOPS, it probably isn't necessary to be
       * this general but try to avoid assuming the class OIDs have been set
       * when there is a MOP present.
       */
      if (dom->class_mop == NULL)
        {
          if (OID_EQ (&dom->class_oid, WS_OID (class_mop)) && dom->is_desc == is_desc)
        {
          break;    /* found */
        }
        }
      else
        {
          if (OID_EQ (WS_OID (dom->class_mop), class_oid) && dom->is_desc == is_desc)
        {
          break;    /* found */
        }
        }
#endif /* defined (SERVER_MODE) */
    }
    }

  return dom;
}

/*
 * tp_domain_find_set - find domain that matches for given set domain
 *    return: domain that matches
 *    type(in): DB_TYPE
 *    setdomain(in): set domain
 *    is_desc(in): desc order for index key_type
 */
TP_DOMAIN *
tp_domain_find_set (DB_TYPE type, TP_DOMAIN * setdomain, bool is_desc)
{
  TP_DOMAIN *dom;
  int dsize;
  int src_dsize;

  src_dsize = tp_domain_size (setdomain);

  /* search the list for a domain that matches */
  for (dom = tp_domain_get_list (type, setdomain); dom != NULL; dom = dom->next_list)
    {
      /* we MUST perform exact matches here */
      if (dom->setdomain == setdomain)
    {
      break;
    }

      if (dom->is_desc == is_desc)
    {
      /* don't bother comparing the lists unless the sizes are the same */
      dsize = tp_setdomain_size (dom);
      if (dsize == src_dsize)
        {
          /* handle the simple single domain case quickly */
          if (dsize == 1)
        {
          if (tp_domain_match (dom->setdomain, setdomain, TP_EXACT_MATCH))
            {
              break;
            }
        }
          else
        {
          TP_DOMAIN *d1, *d2;
          int match, i;

          if (type == DB_TYPE_SEQUENCE || type == DB_TYPE_MIDXKEY)
            {
              if (dsize == src_dsize)
            {
              match = 1;
              d1 = dom->setdomain;
              d2 = setdomain;

              for (i = 0; i < dsize; i++)
                {
                  match = tp_domain_match (d1, d2, TP_EXACT_MATCH);
                  if (match == 0)
                {
                  break;
                }
                  d1 = d1->next;
                  d2 = d2->next;
                }
              if (match == 1)
                {
                  break;
                }
            }
            }
          else
            {
              /* clear the visited flag in the second subdomain list */
              for (d2 = setdomain; d2 != NULL; d2 = d2->next)
            {
              d2->is_visited = 0;
            }

              match = 1;
              for (d1 = dom->setdomain; d1 != NULL && match; d1 = d1->next)
            {
              for (d2 = setdomain; d2 != NULL; d2 = d2->next)
                {
                  if (!d2->is_visited && tp_domain_match (d1, d2, TP_EXACT_MATCH))
                {
                  break;
                }
                }
              /* did we find the domain in the other list ? */
              if (d2 != NULL)
                {
                  d2->is_visited = 1;
                }
              else
                {
                  match = 0;
                }
            }

              if (match == 1)
            {
              break;
            }
            }
        }
        }
    }
    }

  return dom;
}

/*
 * tp_domain_find_enumeration () - Find a chached domain with this enumeration
 * enumeration(in): enumeration to look for
 * is_desc(in): true if desc, false if asc (for only index key)
 */
TP_DOMAIN *
tp_domain_find_enumeration (const DB_ENUMERATION * enumeration, bool is_desc)
{
  TP_DOMAIN *dom = NULL;

  /* search the list for a domain that matches */
  for (dom = tp_domain_get_list (DB_TYPE_ENUMERATION, NULL); dom != NULL; dom = dom->next_list)
    {
      if (dom->is_desc == is_desc && tp_enumeration_match (&DOM_GET_ENUMERATION (dom), enumeration))
    {
      return dom;
    }
    }

  return NULL;
}

/*
 * tp_domain_cache - caches a transient domain
 *    return: cached domain
 *    transient(in/out): transient domain
 * Note:
 *    If the domain has already been cached, it is located and returned.
 *    Otherwise, a new domain is cached and returned.
 *    In either case, the transient domain may be freed so you should never
 *    depend on it being valid after this function returns.
 *
 *    Note that if a new domain is added to the list, it is always appended
 *    to the end.  It is vital that the deafult "built-in" domain be
 *    at the head of the domain lists in tp_Domains.
 */
TP_DOMAIN *
tp_domain_cache (TP_DOMAIN * transient)
{
  TP_DOMAIN *domain, **dlist;
  TP_DOMAIN *ins_pos = NULL;
#if defined (SERVER_MODE)
  int rv;
#endif /* SERVER_MODE */

  /* guard against a bad transient domain */
  if (transient == NULL || transient->type == NULL)
    {
      return NULL;
    }

  /* return this domain if its already cached */
  if (transient->is_cached)
    {
      return transient;
    }

#if !defined (SERVER_MODE)
  /* see comments for tp_swizzle_oid */
  tp_swizzle_oid (transient);
#endif /* !SERVER_MODE */

  /*
   * first search stage: NO LOCK
   */
  /* locate the root of the cache list for domains of this type */
  dlist = tp_domain_get_list_ptr (TP_DOMAIN_TYPE (transient), transient->setdomain);

  /* search the list for a domain that matches */
  if (*dlist != NULL)
    {
      ins_pos = NULL;
      domain = tp_is_domain_cached (*dlist, transient, TP_EXACT_MATCH, &ins_pos);
      if (domain != NULL)
    {
      /*
       * We found one in the cache, free the supplied domain and return
       * the cached one
       */
      tp_domain_free (transient);
      return domain;
    }
    }

  /*
   * second search stage: LOCK
   */
#if defined (SERVER_MODE)
  rv = pthread_mutex_lock (&tp_domain_cache_lock);  /* LOCK */

  /* locate the root of the cache list for domains of this type */
  dlist = tp_domain_get_list_ptr (TP_DOMAIN_TYPE (transient), transient->setdomain);

  /* search the list for a domain that matches */
  if (*dlist != NULL)
    {
      ins_pos = NULL;
      domain = tp_is_domain_cached (*dlist, transient, TP_EXACT_MATCH, &ins_pos);
      if (domain != NULL)
    {
      /*
       * We found one in the cache, free the supplied domain and return
       * the cached one
       */
      tp_domain_free (transient);
      pthread_mutex_unlock (&tp_domain_cache_lock);
      return domain;
    }
    }
#endif /* SERVER_MODE */

  /*
   * We couldn't find one, install the transient domain that was passed in.
   * Since by far the most common domain match is going to be the built-in
   * domain at the head of the list, append new domains to the end of the
   * list as they are encountered.
   */
  transient->is_cached = 1;

  if (*dlist)
    {
      if (ins_pos)
    {
      TP_DOMAIN *tmp;

      tmp = ins_pos->next_list;
      ins_pos->next_list = transient;
      transient->next_list = tmp;
    }
    }
  else
    {
      *dlist = transient;
    }

  domain = transient;

#if defined (SERVER_MODE)
  pthread_mutex_unlock (&tp_domain_cache_lock);
#endif /* SERVER_MODE */

  return domain;
}

/*
 * tp_domain_resolve - Find a domain object that matches the type, class,
 * precision, scale and setdomain.
 *    return: domain found
 *    domain_type(in): The basic type of the domain
 *    class_obj(in): The class of the domain (for DB_TYPE_OBJECT)
 *    precision(in): The precision of the domain
 *    scale(in): The class of the domain
 *    setdomain(in): The setdomain of the domain
 *    collation(in): The collation of domain
 * Note:
 *    Current implementation just creates a new one then returns it.
 */
TP_DOMAIN *
tp_domain_resolve (DB_TYPE domain_type, DB_OBJECT * class_obj, int precision, int scale, TP_DOMAIN * setdomain,
           int collation)
{
  TP_DOMAIN *d;

  d = tp_domain_new (domain_type);
  if (d != NULL)
    {
      d->precision = precision;
      d->scale = scale;
      d->class_mop = class_obj;
      d->setdomain = setdomain;
      if (TP_TYPE_HAS_COLLATION (domain_type))
    {
      LANG_COLLATION *lc;
      d->collation_id = collation;

      lc = lang_get_collation (collation);
      d->codeset = lc->codeset;
    }

      d = tp_domain_cache (d);
    }

  return d;
}

/*
 * tp_domain_resolve_default - returns the built-in "default" domain for a
 * given primitive type
 *    return: cached domain
 *    type(in): type id
 * Note:
 *    This is used only in special cases where we need to get quickly to
 *    a built-in domain without worrying about domain parameters.
 *    Note that this relies on the fact that the built-in domain is at
 *    the head of our domain lists.
 */
TP_DOMAIN *
tp_domain_resolve_default (DB_TYPE type)
{
  if (type >= sizeof (tp_Domains) / sizeof (tp_Domains[0]))
    {
      assert_release (false);
      return NULL;
    }

  return tp_Domains[type];
}

/*
 * tp_domain_resolve_default_w_coll -
 *
 *    return: cached domain
 *    type(in): type id
 *    coll_id(in): collation
 *    coll_flag(in): collation flag
 * Note:
 *  It returns a special domain having the desired collation and collation
 *  mode flag. Use this in context of type inference for argument coercion
 */
TP_DOMAIN *
tp_domain_resolve_default_w_coll (DB_TYPE type, int coll_id, TP_DOMAIN_COLL_ACTION coll_flag)
{
  TP_DOMAIN *default_dom;
  TP_DOMAIN *resolved_dom;

  default_dom = tp_domain_resolve_default (type);

  if (TP_TYPE_HAS_COLLATION (type) && coll_flag != TP_DOMAIN_COLL_NORMAL)
    {
      resolved_dom = tp_domain_copy (default_dom, false);

      if (coll_flag == TP_DOMAIN_COLL_ENFORCE)
    {
      LANG_COLLATION *lc = lang_get_collation (coll_id);

      resolved_dom->collation_id = coll_id;
      resolved_dom->codeset = lc->codeset;
    }
      else
    {
      assert (coll_flag == TP_DOMAIN_COLL_LEAVE);
    }

      resolved_dom->collation_flag = coll_flag;

      resolved_dom = tp_domain_cache (resolved_dom);
    }
  else
    {
      resolved_dom = default_dom;
    }

  return resolved_dom;
}


/*
 * tp_domain_resolve_value - Find a domain object that describes the type info
 * in the DB_VALUE.
 *    return: domain found
 *    val(in): A DB_VALUE for which we need to obtain a domain
 *    dbuf(out): if not NULL, founded domain initialized on dbuf
 */
TP_DOMAIN *
tp_domain_resolve_value (const DB_VALUE * val, TP_DOMAIN * dbuf)
{
  TP_DOMAIN *domain;
  DB_TYPE value_type;

  domain = NULL;
  value_type = DB_VALUE_DOMAIN_TYPE (val);

  if (TP_IS_SET_TYPE (value_type))
    {
      DB_SET *set;
      /*
       * For sets, just return the domain attached to the set since it
       * will already have been cached.
       */
      set = db_get_set (val);
      if (set != NULL)
    {
      domain = set_get_domain (set);
      /* handle case of incomplete set domain: build full domain */
      if (domain->setdomain == NULL || tp_domain_check (domain, val, TP_EXACT_MATCH) != DOMAIN_COMPATIBLE)
        {
          if (domain->is_cached)
        {
          domain = tp_domain_new (value_type);
        }

          if (domain != NULL)
        {
          int err_status;

          err_status = setobj_build_domain_from_col (set->set, &(domain->setdomain));
          if (err_status != NO_ERROR && !domain->is_cached)
            {
              tp_domain_free (domain);
              domain = NULL;
            }
          else
            {
              /* cache this new domain */
              domain = tp_domain_cache (domain);
            }
        }
        }
    }
      else
    {
      /* we need to synthesize a wildcard set domain for this value */
      domain = tp_domain_resolve_default (value_type);
    }
    }
  else if (value_type == DB_TYPE_MIDXKEY)
    {
      DB_MIDXKEY *midxkey;

      /* For midxkey type, return the domain attached to the value */
      midxkey = db_get_midxkey (val);
      if (midxkey != NULL)
    {
      domain = midxkey->domain;
    }
      else
    {
      assert (DB_VALUE_TYPE (val) == DB_TYPE_NULL);
      domain = tp_domain_resolve_default (value_type);
    }
    }
  else
    {
      switch (value_type)
    {
    case DB_TYPE_NULL:
    case DB_TYPE_INTEGER:
    case DB_TYPE_BIGINT:
    case DB_TYPE_FLOAT:
    case DB_TYPE_DOUBLE:
    case DB_TYPE_BLOB:
    case DB_TYPE_CLOB:
    case DB_TYPE_TIME:
    case DB_TYPE_TIMESTAMP:
    case DB_TYPE_TIMESTAMPTZ:
    case DB_TYPE_TIMESTAMPLTZ:
    case DB_TYPE_DATE:
    case DB_TYPE_DATETIME:
    case DB_TYPE_DATETIMETZ:
    case DB_TYPE_DATETIMELTZ:
    case DB_TYPE_MONETARY:
    case DB_TYPE_SHORT:
      /* domains without parameters, return the built-in domain */
      domain = tp_domain_resolve_default (value_type);
      break;

    case DB_TYPE_OBJECT:
      {
#if !defined (SERVER_MODE)
        DB_OBJECT *mop;

        domain = &tp_Object_domain;
        mop = db_get_object (val);
        if ((mop == NULL) || (WS_IS_DELETED (mop)))
          {
        /* just let the oid thing stand?, this is a NULL anyway */
          }
        else
          {
        /* this is a virtual mop */
        if (WS_ISVID (mop))
          {
            domain = &tp_Vobj_domain;
          }
          }
#else
        /* We don't have mops on server */
        assert (false);
#endif
      }
      break;

    case DB_TYPE_OID:
      domain = &tp_Object_domain;
      break;

    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:   /* new name for DB_TYPE_STRING */
    case DB_TYPE_BIT:
    case DB_TYPE_VARBIT:
      /* must find one with a matching precision */
      if (dbuf == NULL)
        {
          domain = tp_domain_new (value_type);
          if (domain == NULL)
        {
          return NULL;
        }
        }
      else
        {
          domain = dbuf;
          tp_domain_init (domain, value_type);
        }
      domain->codeset = db_get_string_codeset (val);
      domain->collation_id = db_get_string_collation (val);
      domain->precision = db_value_precision (val);

      /*
       * Convert references to the "floating" precisions to actual
       * precisions. This may not be necessary or desirable?
       * Zero seems to pop up occasionally in DB_VALUE precisions, until
       * this is fixed, treat it as the floater for the variable width
       * types.
       */
      if (TP_DOMAIN_TYPE (domain) == DB_TYPE_VARCHAR)
        {
          if (domain->precision == 0 || domain->precision == TP_FLOATING_PRECISION_VALUE
          || domain->precision > DB_MAX_VARCHAR_PRECISION)
        {
          domain->precision = DB_MAX_VARCHAR_PRECISION;
        }
        }
      else if (TP_DOMAIN_TYPE (domain) == DB_TYPE_VARBIT)
        {
          if (domain->precision == 0 || domain->precision == TP_FLOATING_PRECISION_VALUE
          || domain->precision > DB_MAX_VARBIT_PRECISION)
        {
          domain->precision = DB_MAX_VARBIT_PRECISION;
        }
        }

      if (dbuf == NULL)
        {
          domain = tp_domain_cache (domain);
        }
      break;
    case DB_TYPE_ENUMERATION:
      /*
       * We have no choice but to return the default enumeration domain
       * because we cannot construct the domain from a DB_VALUE
       */
      domain = tp_domain_resolve_default (value_type);
      break;
    case DB_TYPE_NUMERIC:
      /* must find one with a matching precision and scale */
      if (dbuf == NULL)
        {
          domain = tp_domain_new (value_type);
          if (domain == NULL)
        {
          return NULL;
        }
        }
      else
        {
          domain = dbuf;
          tp_domain_init (dbuf, value_type);
        }
      domain->precision = db_value_precision (val);
      domain->scale = db_value_scale (val);

      /*
       * Hack, precision seems to be commonly -1 DB_VALUES, turn this into
       * the default "maximum" precision.
       * This may not be necessary any more.
       */
      if (domain->precision == -1)
        {
          domain->precision = DB_DEFAULT_NUMERIC_PRECISION;
        }

      if (domain->scale == -1)
        {
          domain->scale = DB_DEFAULT_NUMERIC_SCALE;
        }

      if (dbuf == NULL)
        {
          domain = tp_domain_cache (domain);
        }
      break;

    case DB_TYPE_POINTER:
    case DB_TYPE_ERROR:
    case DB_TYPE_VOBJ:
    case DB_TYPE_SUB:
    case DB_TYPE_VARIABLE:
    case DB_TYPE_DB_VALUE:
    case DB_TYPE_RESULTSET:
      /*
       * These are internal domains, they shouldn't be seen, in case they
       * are, match to a built-in
       */
      domain = tp_domain_resolve_default (value_type);
      break;

    case DB_TYPE_JSON:
      if (dbuf == NULL)
        {
          domain = tp_domain_new (value_type);
          if (domain == NULL)
        {
          return NULL;
        }
        }
      else
        {
          domain = dbuf;
          tp_domain_init (domain, value_type);
        }

      if (db_get_json_schema (val) != NULL)
        {
          int error_code;

          error_code = db_json_load_validator (db_get_json_schema (val), domain->json_validator);
          if (error_code != NO_ERROR)
        {
          assert (false);
          tp_domain_free (domain);
          return NULL;
        }
        }
      else
        {
          domain->json_validator = NULL;
        }

      if (dbuf == NULL)
        {
          domain = tp_domain_cache (domain);
        }
      break;

      /*
       * things handled in logic outside the switch, shuts up compiler
       * warnings
       */
    case DB_TYPE_SET:
    case DB_TYPE_MULTISET:
    case DB_TYPE_SEQUENCE:
    case DB_TYPE_MIDXKEY:
      break;
    case DB_TYPE_TABLE:
      break;
    case DB_TYPE_ELO:
    default:
      assert (false);
      break;
    }
    }

  return domain;
}

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * tp_create_domain_resolve_value - adjust domain of a DB_VALUE with respect to
 * the primitive value of the value
 *    return: domain
 *    val(in): DB_VALUE
 *    domain(in): domain
 * Note: val->domain changes
 */
TP_DOMAIN *
tp_create_domain_resolve_value (DB_VALUE * val, TP_DOMAIN * domain)
{
  DB_TYPE value_type;

  value_type = DB_VALUE_DOMAIN_TYPE (val);

  switch (value_type)
    {
    case DB_TYPE_INTEGER:
    case DB_TYPE_BIGINT:
    case DB_TYPE_FLOAT:
    case DB_TYPE_DOUBLE:
    case DB_TYPE_TIME:
    case DB_TYPE_TIMESTAMP:
    case DB_TYPE_DATE:
    case DB_TYPE_DATETIME:
    case DB_TYPE_MONETARY:
    case DB_TYPE_SHORT:
    case DB_TYPE_OBJECT:
    case DB_TYPE_OID:
      break;

    case DB_TYPE_CHAR:
    case DB_TYPE_BIT:
    case DB_TYPE_VARCHAR:   /* new name for DB_TYPE_STRING */
    case DB_TYPE_VARBIT:
      if (db_value_precision (val) == TP_FLOATING_PRECISION_VALUE)
    {
      /* Check for floating precision. */
      val->domain.char_info.length = domain->precision;
    }
      else
    {
      if (domain->precision == TP_FLOATING_PRECISION_VALUE)
        {
          ;         /* nop */
        }
      else
        {
          if (db_value_precision (val) > domain->precision)
        {
          val->domain.char_info.length = domain->precision;
        }
        }
    }
      break;

    case DB_TYPE_NUMERIC:
      break;

    case DB_TYPE_NULL:      /* for midxkey elements */
      break;

    default:
      return NULL;
    }

  /* if(domain) return tp_domain_cache(domain); */
  return domain;
}
#endif /* ENABLE_UNUSED_FUNCTION */

/*
 * tp_domain_add - Adds a domain structure to a domain list if it doesn't
 * already exist.
 *    return: error code
 *    dlist(in/out): domain list
 *    domain(in): domain structure
 * Note:
 *    This routine should only be used to construct a transient domain.
 *    Note that there are no error messages if a duplicate isn't added.
 */
int
tp_domain_add (TP_DOMAIN ** dlist, TP_DOMAIN * domain)
{
  int error = NO_ERROR;
  TP_DOMAIN *d, *found, *last;
  DB_TYPE type_id;

  last = NULL;
  type_id = TP_DOMAIN_TYPE (domain);

  for (d = *dlist, found = NULL; d != NULL && found == NULL; d = d->next)
    {
#if 1
      /* >>>>> NEED MORE CONSIDERATION <<<<< do not check duplication must be rollback with tp_domain_match() */
#else /* 0 */
      if (TP_DOMAIN_TYPE (d) == type_id)
    {
      switch (type_id)
        {
        case DB_TYPE_INTEGER:
        case DB_TYPE_BIGINT:
        case DB_TYPE_FLOAT:
        case DB_TYPE_DOUBLE:
        case DB_TYPE_BLOB:
        case DB_TYPE_CLOB:
        case DB_TYPE_TIME:
        case DB_TYPE_TIMESTAMP:
        case DB_TYPE_TIMESTAMPTZ:
        case DB_TYPE_TIMESTAMPLTZ:
        case DB_TYPE_DATE:
        case DB_TYPE_DATETIME:
        case DB_TYPE_DATETIMETZ:
        case DB_TYPE_DATETIMELTZ:
        case DB_TYPE_MONETARY:
        case DB_TYPE_SUB:
        case DB_TYPE_POINTER:
        case DB_TYPE_ERROR:
        case DB_TYPE_SHORT:
        case DB_TYPE_VOBJ:
        case DB_TYPE_OID:
        case DB_TYPE_NULL:
        case DB_TYPE_VARIABLE:
        case DB_TYPE_SET:
        case DB_TYPE_MULTISET:
        case DB_TYPE_SEQUENCE:
        case DB_TYPE_DB_VALUE:
        case DB_TYPE_VARCHAR:
        case DB_TYPE_VARBIT:
          found = d;
          break;

        case DB_TYPE_NUMERIC:
          if ((d->precision == domain->precision) && (d->scale == domain->scale))
        {
          found = d;
        }
          break;

        case DB_TYPE_CHAR:
        case DB_TYPE_BIT:
          /*
           * PR)  1.deficient character related with CHAR & VARCHAR in set.
           * ==> distinguishing VARCHAR from CHAR.
           * 2. core dumped & deficient character related with
           * CONST CHAR & CHAR in set.
           * ==> In case of CHAR, BIT  cosidering precision.
           */
          if (d->precision == domain->precision)
        {
          found = d;
        }
          break;

        case DB_TYPE_OBJECT:
          if (d->class_mop == domain->class_mop)
        {
          found = d;
        }
          break;

        default:
          break;
        }
    }
#endif /* 1 */

      last = d;
    }

  if (found == NULL)
    {
      if (last == NULL)
    {
      *dlist = domain;
    }
      else
    {
      last->next = domain;
    }
    }
  else
    {
      /* the domain already existed, free the supplied domain */
      tp_domain_free (domain);
    }

  return error;
}

#if !defined (SERVER_MODE)
/*
 * tp_domain_attach - concatenate two domains
 *    return: concatenated domain
 *    dlist(out): domain 1
 *    domain(in): domain 2
 */
int
tp_domain_attach (TP_DOMAIN ** dlist, TP_DOMAIN * domain)
{
  int error = NO_ERROR;
  TP_DOMAIN *d;

  d = *dlist;

  if (*dlist == NULL)
    {
      *dlist = domain;
    }
  else
    {
      while (d->next)
    d = d->next;

      d->next = domain;
    }

  return error;
}

/*
 * tp_domain_drop - Removes a domain from a list if it was found.
 *    return: non-zero if domain was dropped
 *    dlist(in/out): domain list
 *    domain(in/out):  domain class
 * Note:
 *    This routine should only be used to modify a transient domain.
 */
int
tp_domain_drop (TP_DOMAIN ** dlist, TP_DOMAIN * domain)
{
  TP_DOMAIN *d, *found, *prev;
  int dropped = 0;
  DB_TYPE type_id;

  type_id = TP_DOMAIN_TYPE (domain);
  for (d = *dlist, prev = NULL, found = NULL; d != NULL && found == NULL; d = d->next)
    {
      if (TP_DOMAIN_TYPE (d) == type_id)
    {
      switch (type_id)
        {
        case DB_TYPE_INTEGER:
        case DB_TYPE_BIGINT:
        case DB_TYPE_FLOAT:
        case DB_TYPE_DOUBLE:
        case DB_TYPE_BLOB:
        case DB_TYPE_CLOB:
        case DB_TYPE_TIME:
        case DB_TYPE_TIMESTAMP:
        case DB_TYPE_TIMESTAMPLTZ:
        case DB_TYPE_TIMESTAMPTZ:
        case DB_TYPE_DATE:
        case DB_TYPE_DATETIME:
        case DB_TYPE_DATETIMELTZ:
        case DB_TYPE_DATETIMETZ:
        case DB_TYPE_MONETARY:
        case DB_TYPE_SUB:
        case DB_TYPE_POINTER:
        case DB_TYPE_ERROR:
        case DB_TYPE_SHORT:
        case DB_TYPE_VOBJ:
        case DB_TYPE_OID:
        case DB_TYPE_NULL:
        case DB_TYPE_VARIABLE:
        case DB_TYPE_SET:
        case DB_TYPE_MULTISET:
        case DB_TYPE_SEQUENCE:
        case DB_TYPE_DB_VALUE:
        case DB_TYPE_VARCHAR:
        case DB_TYPE_VARBIT:
          found = d;
          break;

        case DB_TYPE_NUMERIC:
          if (d->precision == domain->precision && d->scale == domain->scale)
        {
          found = d;
        }
          break;

        case DB_TYPE_CHAR:
        case DB_TYPE_BIT:
          /* 1.deficient character related with CHAR & VARCHAR in set. ==> distinguishing VARCHAR from CHAR. 2.
           * core dumped & deficient character related with CONST CHAR & CHAR in set. ==> In case of
           * CHAR, BIT cosidering precision. */
          if (d->precision == domain->precision)
        {
          found = d;
        }
          break;

        case DB_TYPE_OBJECT:
          if (d->class_mop == domain->class_mop)
        {
          found = d;
        }
          break;

        default:
          break;
        }
    }

      if (found == NULL)
    {
      prev = d;
    }
    }

  if (found != NULL)
    {
      if (prev == NULL)
    {
      *dlist = found->next;
    }
      else
    {
      prev->next = found->next;
    }

      found->next = NULL;
      tp_domain_free (found);

      dropped = 1;
    }

  return dropped;
}
#endif /* !SERVER_MODE */


/*
 * tp_domain_check_class - Work function for tp_domain_filter_list and
 * sm_filter_domain.
 *    return: error code
 *    domain(in): domain to examine
 *    change(out): non-zero if the domain was modified
 * Note:
 *    Check the class in a domain and if it was deleted, downgrade the
 *    domain to "object".
 */
static int
tp_domain_check_class (TP_DOMAIN * domain, int *change)
{
  int error = NO_ERROR;
#if !defined (SERVER_MODE)
  int status;
#endif /* !SERVER_MODE */

  if (change != NULL)
    {
      *change = 0;
    }

#if !defined (SERVER_MODE)
  if (!db_on_server)
    {
      if (domain != NULL && domain->type == tp_Type_object && domain->class_mop != NULL)
    {
      /* check for deletion of the domain class, assume just one for now */
      status = locator_does_exist_object (domain->class_mop, DB_FETCH_READ);

      if (status == LC_DOESNOT_EXIST)
        {
          WS_SET_DELETED (domain->class_mop);
          domain->class_mop = NULL;
          if (change != NULL)
        {
          *change = 1;
        }
        }
      else if (status == LC_ERROR)
        {
          ASSERT_ERROR ();
          error = er_errid ();
        }
    }
    }
#endif /* !SERVER_MODE */

  return error;
}


/*
 * tp_domain_filter_list - filter out any domain references to classes that
 * have been deleted or are otherwise invalid from domain list
 *    return: error code
 *    dlist(in):  domain list
 *    list_changes(out) : non-zero if changes were made
 * Note:
 *    The semantic for deleted classes is that the domain reverts
 *    to the root "object" domain, thereby allowing all object references.
 *    This could become more sophisticated but not without a lot of extra
 *    bookkeeping in the database.   If a domain is downgraded to "object",
 *    be sure to remove it from the list entirely if there is already an
 *    "object" domain present.
 */
int
tp_domain_filter_list (TP_DOMAIN * dlist, int *list_changes)
{
  TP_DOMAIN *d, *prev, *next;
  int has_object, changes, set_changes, domain_change;
  int error;

  has_object = changes = 0;
  if (list_changes != NULL)
    {
      *list_changes = 0;
    }

  for (d = dlist, prev = NULL, next = NULL; d != NULL; d = next)
    {
      next = d->next;
      error = tp_domain_check_class (d, &domain_change);
      if (error != NO_ERROR)
    {
      return error;
    }

      if (domain_change)
    {
      /* domain reverted to "object" */
      if (!has_object)
        {
          has_object = 1;
          prev = d;
        }
      else
        {
          /*
           * redundant "object" domain, remove, prev can't be NULL here,
           * will always have at least one domain structure at the head of
           * the list
           */
          prev->next = next;
          d->next = NULL;
          tp_domain_free (d);
          changes |= 1;
        }
    }
      else
    {
      /* domain is still valid, see if its "object" */
      if (d->type == tp_Type_object && d->class_mop == NULL)
        {
          has_object = 1;
        }
      else if (pr_is_set_type (TP_DOMAIN_TYPE (d)) && d->setdomain != NULL)
        {
          /* recurse on set domain list */
          error = tp_domain_filter_list (d->setdomain, &set_changes);
          if (error != NO_ERROR)
        {
          return error;
        }

          changes |= set_changes;
        }
      prev = d;
    }
    }

  if (list_changes != NULL)
    {
      *list_changes = changes;
    }

  return NO_ERROR;
}

/*
 * tp_domain_name - generate a printed representation for the given domain.
 *    return: non-zero if buffer overflow, -1 for error
 *    domain(in): domain structure
 *    buffer(out): output buffer
 *    maxlen(in): maximum length of buffer
 */
int
tp_domain_name (const TP_DOMAIN * domain, char *buffer, int maxlen)
{
  /*
   * need to get more sophisticated here, do full name decomposition and
   * check maxlen
   */
  strncpy (buffer, domain->type->name, maxlen);
  buffer[maxlen - 1] = '\0';
  return (0);
}

/*
 * tp_value_domain_name - generates printed representation of the domain for a
 * given DB_VALUE.
 *    return: non-zero if buffer overflow, -1 if error
 *    value(in): value to examine
 *    buffer(out): output buffer
 *    maxlen(in): maximum length of buffer
 */
int
tp_value_domain_name (const DB_VALUE * value, char *buffer, int maxlen)
{
  /* need to get more sophisticated here */

  strncpy (buffer, pr_type_name (DB_VALUE_TYPE (value)), maxlen);
  buffer[maxlen - 1] = '\0';
  return (0);
}

/*
 * tp_domain_find_compatible - two domains are compatible for the purposes of
 * assignment of values.
 *    return: non-zero if domains are compatible
 *    src(in): domain we're wanting to assign
 *    dest(in): domain we're trying to go into
 * Note:
 *    Domains are compatible if they are equal.
 *    Further, domain 1 is compatible with domain 2 if domain 2 is more
 *    general.
 *
 *    This will not properly detect of the domains are compatible due
 *    to a proper subclass superclass relationship between object domains.
 *    It will only check to see if the class matches exactly.
 *
 *    This is the function used to test to see if a particular set domain
 *    is "within" another set domain during assignment of set values to
 *    attributes.  src in this case will be the domain of the set we were
 *    given and dest will be the domain of the attribute.
 *    All of the sub-domains in src must also be found in dest.
 *
 *    This is somewhat different than tp_domain_match because the comparison
 *    of set domains is more of an "is it a subset" operation rather than
 *    an "is it equal to" operation.
 */
static const TP_DOMAIN *
tp_domain_find_compatible (const TP_DOMAIN * src, const TP_DOMAIN * dest)
{
  const TP_DOMAIN *d, *found;

  found = NULL;

  /*
   * If we have a hierarchical domain, perform a lenient "superset" comparison
   * rather than an exact match.
   */
  if (TP_IS_SET_TYPE (TP_DOMAIN_TYPE (src)) || TP_DOMAIN_TYPE (src) == DB_TYPE_VARIABLE)
    {
      for (d = dest; d != NULL && found == NULL; d = d->next)
    {
      if (TP_DOMAIN_TYPE (src) == TP_DOMAIN_TYPE (d) && tp_domain_compatible (src->setdomain, dest->setdomain))
        {
          found = d;
        }
    }
    }
  else
    {

      for (d = dest; d != NULL && found == NULL; d = d->next)
    {
      if (tp_domain_match ((TP_DOMAIN *) src, (TP_DOMAIN *) d, TP_EXACT_MATCH))
        {
          /* exact match flag is on */
          found = d;
        }
    }

    }

  return found;
}

/*
 * tp_domain_compatible - check compatibility of src domain w.r.t dest
 *    return: 1 if compatible, 0 otherwise
 *    src(in): src domain
 *    dest(in): dest domain
 */
int
tp_domain_compatible (const TP_DOMAIN * src, const TP_DOMAIN * dest)
{
  const TP_DOMAIN *d;
  int equal = 0;

  if (src != NULL && dest != NULL)
    {
      equal = 1;
      if (src != dest)
    {
      /*
       * for every domain in src, make sure we have a compatible one in
       * dest
       */
      for (d = src; equal && d != NULL; d = d->next)
        {
          if (tp_domain_find_compatible (d, dest) == NULL)
        {
          equal = 0;
        }
        }
    }
    }

  return equal;
}


/*
 * tp_domain_select - select a domain from a list of possible domains that is
 * the exact match (or closest, depending on the value of exact_match) to the
 * supplied value.
 *    return: domain
 *    domain_list(in): list of possible domains
 *    value(in): value of interest
 *    allow_coercion(in): non-zero if coercion will be allowed
 *    exact_match(in): controls tolerance permitted during match
 * Note:
 *    This operation is used for basic domain compatibility checking
 *    as well as value coercion.
 *    If the allow_coercion flag is on, the tp_Domain_conversion_matrix
 *    will be consulted to find an appropriate domain in the case
 *    where there is no exact match.
 *    If an appropriate domain could not be found, NULL is returned.
 *
 *    This is known not to work correctly for nested set domains.  In order
 *    for the best domain to be selected, we must recursively check the
 *    complete set domains here.
 *
 *    The exact_match flag determines if we allow "tolerance" matches when
 *    checking domains for attributes.  See commentary in tp_domain_match
 *    for more information.
 */
TP_DOMAIN *
tp_domain_select (const TP_DOMAIN * domain_list, const DB_VALUE * value, int allow_coercion, TP_MATCH exact_match)
{
  TP_DOMAIN *best = NULL, *d = NULL;
  TP_DOMAIN **others = NULL;
  DB_TYPE vtype;
  int i;

  DB_VALUE temp;
  db_make_null (&temp);

  best = NULL;

  bool ti = true;
  static bool ignore_trailing_space = prm_get_bool_value (PRM_ID_IGNORE_TRAILING_SPACE);

  /*
   * NULL values are allowed in any domain, a NULL domain means that any value
   * is allowed, return the first thing on the list.
   */
  if (value == NULL || domain_list == NULL || (vtype = DB_VALUE_TYPE (value)) == DB_TYPE_NULL)
    {
      return (TP_DOMAIN *) domain_list;
    }


  if (vtype == DB_TYPE_OID)
    {
      if (db_on_server)
    {
      /*
       * On the server, just make sure that we have any object domain in
       * the list.
       */
      for (d = (TP_DOMAIN *) domain_list; d != NULL && best == NULL; d = d->next)
        {
          if (TP_DOMAIN_TYPE (d) == DB_TYPE_OBJECT)
        {
          best = d;
        }
        }
    }
#if !defined (SERVER_MODE)
      else
    {
      /*
       * On the client, swizzle to an object and fall in to the next
       * clause
       */
      OID *oid;
      DB_OBJECT *mop;

      oid = (OID *) db_get_oid (value);
      if (oid)
        {
          if (OID_ISNULL (oid))
        {
          /* this is the same as the NULL case above */
          return (TP_DOMAIN *) domain_list;
        }
          else
        {
          mop = ws_mop (oid, NULL);
          db_make_object (&temp, mop);
          /*
           * we don't have to worry about clearing this since its an
           * object
           */
          value = (const DB_VALUE *) &temp;
          vtype = DB_TYPE_OBJECT;
        }
        }
    }
#endif /* !SERVER_MODE */
    }

  /*
   * Handling of object domains is more complex than just comparing the
   * types and parameters.  We have to see if the instance's class is
   * somewhere in the subclass hierarchy of the domain class.
   * This can't be done on the server yet though presumably we could
   * implement something like this using OID chasing.
   */

  if (vtype == DB_TYPE_OBJECT)
    {
      if (db_on_server)
    {
      /*
       * we really shouldn't get here but if we do, handle it like the
       * OID case above, just return the first object domain that we find.
       */
      for (d = (TP_DOMAIN *) domain_list; d != NULL && best == NULL; d = d->next)
        {
          if (TP_DOMAIN_TYPE (d) == DB_TYPE_OBJECT)
        {
          best = d;
        }
        }
      return best;
    }
#if !defined (SERVER_MODE)
      else
    {
      /*
       * On the client, check to see if the instance is within the subclass
       * hierarchy of the object domains.  If there are more than one
       * acceptable domains, we just pick the first one.
       */
      DB_OBJECT *obj = db_get_object (value);

      for (d = (TP_DOMAIN *) domain_list; d != NULL && best == NULL; d = d->next)
        {
          if (TP_DOMAIN_TYPE (d) == DB_TYPE_OBJECT && sm_check_object_domain (d, obj))
        {
          best = d;
        }
        }
    }
#endif /* !SERVER_MODE */
    }

#if !defined (SERVER_MODE)
  else if (vtype == DB_TYPE_POINTER)
    {
      /*
       * This is necessary in order to correctly choose an object domain from
       * the domain list when doing an insert nested inside a heterogeneous
       * set, e.g.:
       * create class foo (a int);
       * create class bar (b set_of(string, integer, foo));
       * insert into bar (b) values ({insert into foo values (1)});
       */
      DB_OTMPL *val_tmpl;

      val_tmpl = (DB_OTMPL *) db_get_pointer (value);
      if (val_tmpl)
    {
      for (d = (TP_DOMAIN *) domain_list; d != NULL && best == NULL; d = d->next)
        {
          if (TP_DOMAIN_TYPE (d) == DB_TYPE_OBJECT && sm_check_class_domain (d, val_tmpl->classobj))
        {
          best = d;
        }
        }
    }
    }
#endif /* !SERVER_MODE */

  else if (TP_IS_SET_TYPE (vtype))
    {
      /*
       * Now that we cache set domains, there might be a faster way to do
       * this !
       */
      DB_SET *set;

      set = db_get_set (value);
      for (d = (TP_DOMAIN *) domain_list; d != NULL && best == NULL; d = d->next)
    {
      if (TP_DOMAIN_TYPE (d) == vtype)
        {
          if (set_check_domain (set, d) == DOMAIN_COMPATIBLE)
        {
          best = d;
        }
        }
    }
    }
  else if (vtype == DB_TYPE_ENUMERATION)
    {
      int val_idx, dom_size, val_size;
      const char *dom_str = NULL, *val_str = NULL;

      if (db_get_enum_short (value) == 0 && db_get_enum_string (value) != NULL)
    {
      /* An enumeration should be NULL or should at least have an index */
      assert (false);
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
      return NULL;
    }

      val_idx = db_get_enum_short (value);

      val_str = db_get_enum_string (value);
      val_size = db_get_enum_string_size (value);

      for (d = (TP_DOMAIN *) domain_list; d != NULL && best == NULL; d = d->next)
    {
      if (TP_DOMAIN_TYPE (d) != DB_TYPE_ENUMERATION)
        {
          continue;
        }

      if (val_idx == 0)
        {
          /* this is an invalid enum value so any domain matches */
          best = d;
          break;
        }
      if (DOM_GET_ENUM_ELEMS_COUNT (d) == 0 && best == NULL)
        {
          /* this is the default enum domain and we haven't found any matching domain yet. This is our best
           * candidate so far */
          best = d;
          continue;
        }
      if (DOM_GET_ENUM_ELEMS_COUNT (d) < val_idx)
        {
          continue;
        }
      if (val_str == NULL)
        {
          /* The enumeration string value is not specified. This means that the domain we have so far is the best
           * match because there is no way of deciding between other domains based only on the short value */
          best = d;
          break;
        }

      if (db_get_enum_collation (value) != d->collation_id)
        {
          continue;
        }

      dom_str = DB_GET_ENUM_ELEM_STRING (&DOM_GET_ENUM_ELEM (d, val_idx));
      dom_size = DB_GET_ENUM_ELEM_STRING_SIZE (&DOM_GET_ENUM_ELEM (d, val_idx));

      /* We have already checked that val_str is not null */
      if (dom_str == NULL)
        {
          assert (false);
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
          return NULL;
        }

      if (!ignore_trailing_space)
        {
          ti = (d->type->id == DB_TYPE_CHAR);
        }
      if (QSTR_COMPARE (d->collation_id, (const unsigned char *) dom_str, dom_size,
                (const unsigned char *) val_str, val_size, ti) == 0)
        {
          if (best == NULL)
        {
          best = d;
        }
          else if (DOM_GET_ENUM_ELEMS_COUNT (best) < DOM_GET_ENUM_ELEMS_COUNT (d))
        {
          /* The best match is the domain that has the largest element count. We're not interested in the value
           * of the exact_match argument since we cannot find an exact enumeration match based on a DB_VALUE */
          best = d;
        }
        }
    }
    }
  else
    {
      /*
       * synthesize a domain for the value and look for a match.
       * Could we be doing this for the set values too ?
       * Hack, since this will be used only for comparison purposes,
       * don't go through the overhead of caching the domain every time,
       * especially for numeric types.  This will be a lot simpler if we
       * store the domain
       * pointer directly in the DB_VALUE.
       */
      TP_DOMAIN temp_domain, *val_domain;

      val_domain = tp_domain_resolve_value ((DB_VALUE *) value, &temp_domain);

      for (d = (TP_DOMAIN *) domain_list; d != NULL && best == NULL; d = d->next)
    {
      /* hack, try allowing "tolerance" matches of the domain ! */
      if (tp_domain_match (d, val_domain, exact_match))
        {
          best = d;
        }
    }
    }

  if (best == NULL && allow_coercion)
    {
      others = tp_Domain_conversion_matrix[vtype];
      if (others != NULL)
    {
      for (i = 0; others[i] != NULL && best == NULL; i++)
        {
          for (d = (TP_DOMAIN *) domain_list; d != NULL && best == NULL; d = d->next)
        {
          if (d->type == others[i]->type)
            {
              best = d;
            }
        }
        }
    }
    }

  return best;
}

#if !defined (SERVER_MODE)
/*
 * tp_domain_select_type - similar to tp_domain_select except that it does not
 * require the existance of an actual DB_VALUE containing a proposed value.
 *    return: best domain from the list, NULL if none
 *    domain_list(in): domain lis t
 *    type(in): basic data type
 *    class(in): class if type == DB_TYPE_OBJECT
 *    allow_coercion(in): flag to enable coercions
 * Note:
 *    this cannot be used for checking set domains.
 */
TP_DOMAIN *
tp_domain_select_type (const TP_DOMAIN * domain_list, DB_TYPE type, DB_OBJECT * class_mop, int allow_coercion)
{
  const TP_DOMAIN *best, *d;
  TP_DOMAIN **others;
  int i;

  /*
   * NULL values are allowed in any domain, a NULL domain means that any value
   * is allowed, return the first thing on the list
   */
  if (type == DB_TYPE_NULL || domain_list == NULL)
    {
      best = domain_list;
    }
  else
    {
      best = NULL;
      /*
       * loop through the domain elements looking for one the fits,
       * rather than make type comparisons for each element in the loop,
       * do them out here and duplicate the loop
       */

      if (type == DB_TYPE_OBJECT)
    {
      for (d = domain_list; d != NULL && best == NULL; d = d->next)
        {
          if (TP_DOMAIN_TYPE (d) == DB_TYPE_OBJECT && sm_check_class_domain ((TP_DOMAIN *) d, class_mop))
        {
          best = d;
        }
        }
    }
      else if (TP_IS_SET_TYPE (type))
    {
      for (d = domain_list; d != NULL && best == NULL; d = d->next)
        {
          if (TP_DOMAIN_TYPE (d) == type)
        {
          /* can't check the actual set domain here, assume its ok */
          best = d;
        }
        }
    }
      else
    {
      for (d = domain_list; d != NULL && best == NULL; d = d->next)
        {
          if (TP_DOMAIN_TYPE (d) == type || TP_DOMAIN_TYPE (d) == DB_TYPE_VARIABLE)
        {
          best = d;
        }
        }
    }

      if (best == NULL && allow_coercion)
    {
      others = tp_Domain_conversion_matrix[type];
      if (others != NULL)
        {
          /*
           * loop through the allowable conversions until we find
           * one that appears in the supplied domain list, the
           * array is ordered in terms of priority,
           * THIS WILL NOT WORK CORRECTLY FOR NESTED SETS
           */
          for (i = 0; others[i] != NULL && best == NULL; i++)
        {
          for (d = domain_list; d != NULL && best == NULL; d = d->next)
            {
              if (d->type == others[i]->type)
            {
              best = d;
            }
            }
        }
        }
    }
    }

  return ((TP_DOMAIN *) best);
}
#endif /* !SERVER_MODE */


/*
 * tp_domain_check - does basic validation of a value against a domain.
 *    return: domain status
 *    domain(in): destination domain
 *    value(in): value to look at
 *    exact_match(in): controls the tolerance permitted for the match
 * Note:
 *    It does NOT do coercion.  If the intention is to perform coercion,
 *    them tp_domain_select should be used.
 *    Exact match is used to request a deferred coercion of values that
 *    are within "tolerance" of the destination domain.  This is currently
 *    only specified for assignment of attribute values and will be
 *    recognized only by those types whose "setmem" and "writeval" functions
 *    are able to perform delayed coercion.  Examples are the CHAR types
 *    which will do truncation or blank padding as the values are being
 *    assigned.  See commentary in tp_domain_match for more information.
 */
TP_DOMAIN_STATUS
tp_domain_check (const TP_DOMAIN * domain, const DB_VALUE * value, TP_MATCH exact_match)
{
  TP_DOMAIN_STATUS status;
  TP_DOMAIN *d;

  if (domain == NULL)
    {
      status = DOMAIN_COMPATIBLE;
    }
  else
    {
      d = tp_domain_select (domain, value, 0, exact_match);
      if (d == NULL)
    {
      status = DOMAIN_INCOMPATIBLE;
    }
      else
    {
      status = DOMAIN_COMPATIBLE;
    }
    }

  return status;
}

/*
 * COERCION
 */


/*
 * tp_can_steal_string - check if the string currently held in "val" can be
 * safely reused
 *    WITHOUT copying.
 *    return: error code
 *    val(in): source (and destination) value
 *    desired_domain(in): desired domain for coerced value
 * Note:
 *    Basically, this holds if
 *       1. the dest precision is "floating", or
 *       2. the dest type is varying and the length of the string is less
 *          than or equal to the dest precision, or
 *       3. the dest type is fixed and the length of the string is exactly
 *          equal to the dest precision.
 *    Since the desired domain is often a varying char, this wins often.
 */
int
tp_can_steal_string (const DB_VALUE * val, const DB_DOMAIN * desired_domain)
{
  DB_TYPE original_type, desired_type;
  int original_length, original_size, desired_precision;

  original_type = DB_VALUE_DOMAIN_TYPE (val);
  if (!TP_IS_CHAR_BIT_TYPE (original_type))
    {
      return 0;
    }

  original_length = db_get_string_length (val);
  original_size = db_get_string_size (val);
  desired_type = TP_DOMAIN_TYPE (desired_domain);
  desired_precision = desired_domain->precision;

  /* this condition covers both the cases when string conversion is needed, and when byte reinterpretation will be
   * performed (destination charset = BINARY) */
  if (original_size > desired_precision)
    {
      return 0;
    }

  if (TP_IS_CHAR_TYPE (original_type) && TP_IS_CHAR_TYPE (TP_DOMAIN_TYPE (desired_domain)))
    {
      if (desired_domain->collation_flag != TP_DOMAIN_COLL_LEAVE
      && db_get_string_collation (val) != TP_DOMAIN_COLLATION (desired_domain)
      && !LANG_IS_COERCIBLE_COLL (db_get_string_collation (val)))
    {
      return 0;
    }

      if (desired_domain->collation_flag != TP_DOMAIN_COLL_LEAVE
      && !INTL_CAN_STEAL_CS (db_get_string_codeset (val), TP_DOMAIN_CODESET (desired_domain)))
    {
      return 0;
    }
    }

  if (desired_domain->collation_flag == TP_DOMAIN_COLL_ENFORCE)
    {
      return 1;
    }

  if (desired_precision == TP_FLOATING_PRECISION_VALUE)
    {
      desired_precision = original_length;
    }

  switch (desired_type)
    {
    case DB_TYPE_CHAR:
      return (desired_precision == original_length
          && (original_type == DB_TYPE_CHAR || original_type == DB_TYPE_VARCHAR)
          && DB_GET_COMPRESSED_STRING (val) == NULL);
    case DB_TYPE_VARCHAR:
      return (desired_precision >= original_length
          && (original_type == DB_TYPE_CHAR || original_type == DB_TYPE_VARCHAR));
    case DB_TYPE_BIT:
      return (desired_precision == original_length
          && (original_type == DB_TYPE_BIT || original_type == DB_TYPE_VARBIT));
    case DB_TYPE_VARBIT:
      return (desired_precision >= original_length
          && (original_type == DB_TYPE_BIT || original_type == DB_TYPE_VARBIT));
    default:
      return 0;
    }
}

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * tp_null_terminate - NULL terminate the given DB_VALUE string.
 *    return: NO_ERROR or error code
 *    src(in): string to null terminate
 *    strp(out): pointer for output
 *    str_len(in): length of 'str'
 *    do_alloc(out): set true if allocation occurred
 * Note:
 *    Don't call this unless src is a string db_value.
 */
static int
tp_null_terminate (const DB_VALUE * src, char **strp, int str_len, bool * do_alloc)
{
  char *str;
  int str_size;

  *do_alloc = false;        /* init */

  str = db_get_string (src);
  if (str == NULL)
    {
      return ER_FAILED;
    }

  str_size = db_get_string_size (src);

  if (str[str_size] == '\0')
    {
      /* already NULL terminated */
      *strp = str;

      return NO_ERROR;
    }

  if (str_size >= str_len)
    {
      *strp = (char *) malloc (str_size + 1);
      if (*strp == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) (str_size + 1));
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }

      *do_alloc = true;     /* mark as alloced */
    }

  memcpy (*strp, str, str_size);
  (*strp)[str_size] = '\0';

  return NO_ERROR;
}
#endif /* ENABLE_UNUSED_FUNCTION */

/*
 * tp_atotime - coerce a string to a time
 *    return: NO_ERROR or error code
 *    src(in): string DB_VALUE
 *    temp(out): time container
 * Note:
 *    Accepts strings that are not null terminated. Don't call this unless
 *    src is a string db_value.
 */
static int
tp_atotime (const DB_VALUE * src, DB_TIME * temp)
{
  int milisec;
  const char *strp = db_get_string (src);
  int str_len = db_get_string_size (src);
  int status = NO_ERROR;

  if (db_date_parse_time (strp, str_len, temp, &milisec) != NO_ERROR)
    {
      status = ER_FAILED;
    }

  return status;
}

/*
 * tp_atodate - coerce a string to a date
 *    return: NO_ERROR or error code
 *    src(in): string DB_VALUE
 *    temp(out): date container
 * Note:
 *    Accepts strings that are not null terminated. Don't call this unless
 *    src is a string db_value.
 */
static int
tp_atodate (const DB_VALUE * src, DB_DATE * temp)
{
  const char *strp = db_get_string (src);
  int str_len = db_get_string_size (src);
  int status = NO_ERROR;

  if (db_date_parse_date (strp, str_len, temp) != NO_ERROR)
    {
      status = ER_FAILED;
    }

  return status;
}

/*
 * tp_atoutime - coerce a string to a utime.
 *    return: NO_ERROR or error code
 *    src(in): string DB_VALUE
 *    temp(out): utime container
 * Note:
 *    Accepts strings that are not null terminated. Don't call this unless
 *    src is a string db_value.
 */
static int
tp_atoutime (const DB_VALUE * src, DB_UTIME * temp)
{
  const char *strp = db_get_string (src);
  int str_len = db_get_string_size (src);
  int status = NO_ERROR;

  if (db_date_parse_utime (strp, str_len, temp) != NO_ERROR)
    {
      status = ER_FAILED;
    }

  return status;
}

/*
 * tp_atotimestamptz - coerce a string to a timestamp with time zone.
 *    return: NO_ERROR or error code
 *    src(in): string DB_VALUE
 *    temp(out): timestamp with TZ info container
 * Note:
 *    Accepts strings that are not null terminated. Don't call this unless
 *    src is a string db_value.
 */
static int
tp_atotimestamptz (const DB_VALUE * src, DB_TIMESTAMPTZ * temp)
{
  const char *strp = db_get_string (src);
  int str_len = db_get_string_size (src);
  int status = NO_ERROR;
  bool dummy_has_zone;

  if (db_string_to_timestamptz_ex (strp, str_len, temp, &dummy_has_zone, true) != NO_ERROR)
    {
      status = ER_FAILED;
    }

  return status;
}

/*
 * tp_atoudatetime - coerce a string to a datetime.
 *    return: NO_ERROR or error code
 *    src(in): string DB_VALUE
 *    temp(out): datetime container
 * Note:
 *    Accepts strings that are not null terminated. Don't call this unless
 *    src is a string db_value.
 */
static int
tp_atoudatetime (const DB_VALUE * src, DB_DATETIME * temp)
{
  const char *strp = db_get_string (src);
  int str_len = db_get_string_size (src);
  int status = NO_ERROR;

  if (db_date_parse_datetime (strp, str_len, temp) != NO_ERROR)
    {
      status = ER_FAILED;
    }

  return status;
}

/*
 * tp_atoudatetimetz - coerce a string to a datetime with time zone.
 *    return: NO_ERROR or error code
 *    src(in): string DB_VALUE
 *    temp(out): datetime with time zone container
 * Note:
 *    Accepts strings that are not null terminated. Don't call this unless
 *    src is a string db_value.
 */
static int
tp_atodatetimetz (const DB_VALUE * src, DB_DATETIMETZ * temp)
{
  const char *strp = db_get_string (src);
  int str_len = db_get_string_size (src);
  int status = NO_ERROR;
  bool dummy_has_zone;

  if (db_string_to_datetimetz_ex (strp, str_len, temp, &dummy_has_zone) != NO_ERROR)
    {
      status = ER_FAILED;
    }

  return status;
}

/*
 * tp_atonumeric - Coerce a string to a numeric.
 *    return: NO_ERROR or error code
 *    src(in): string DB_VALUE
 *    temp(out): numeirc container
 * Note:
 *    Accepts strings that are not null terminated. Don't call this unless
 *    src is a string db_value.
 */
static int
tp_atonumeric (const DB_VALUE * src, DB_VALUE * temp)
{
  const char *strp;
  int status = NO_ERROR;
  int str_len;

  strp = db_get_string (src);
  if (strp == NULL)
    {
      return ER_FAILED;
    }

  str_len = db_get_string_size (src);

  if (numeric_coerce_string_to_num (strp, str_len, db_get_string_codeset (src), temp) != NO_ERROR)
    {
      status = ER_FAILED;
    }

  return status;
}

/*
 * tp_atof - Coerce a string to a double.
 *    return: NO_ERROR or error code.
 *    src(in): string DB_VALUE
 *    num_value(out): float container
 *    data_stat(out): if overflow is detected, this is set to
 *            DATA_STATUS_TRUNCATED. If there exists some characters
 *            that are not numeric codes or spaces then it is set to
 *            DATA_STATUS_NOT_CONSUMED.
 * Note:
 *    Accepts strings that are not null terminated. Don't call this unless
 *    src is a string db_value.
 */
static int
tp_atof (const DB_VALUE * src, double *num_value, DB_DATA_STATUS * data_stat)
{
  char str[NUM_BUF_SIZE];
  const char *strp = NULL;
  bool do_alloc = false;
  double d;
  const char *p, *end;
  int status = NO_ERROR;
  unsigned int size;
  INTL_CODESET codeset;

  *data_stat = DATA_STATUS_OK;

  p = db_get_string (src);
  size = db_get_string_size (src);
  codeset = db_get_string_codeset (src);
  end = p + size - 1;

  if (size > 0 && *end)
    {
      while (p <= end && char_isspace (*p))
    {
      p++;
    }

      while (p < end && char_isspace (*end))
    {
      end--;
    }

      char *strp_tmp = NULL;
      size = CAST_BUFLEN (end - p) + 1;

      if (size > sizeof (str) - 1)
    {
      strp_tmp = (char *) malloc (size + 1);
      if (strp_tmp == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) (size + 1));
          return ER_OUT_OF_VIRTUAL_MEMORY;
        }
      do_alloc = true;
    }
      else
    {
      strp_tmp = str;
    }

      if (size)
    {
      memcpy (strp_tmp, p, size);
    }
      strp_tmp[size] = '\0';

      strp = strp_tmp;
    }
  else
    {
      strp = p;
    }

  /* don't use atof() which cannot detect the error. */
  errno = 0;
  char *end_ptr = NULL;
  d = string_to_double (strp, &end_ptr);

  if (errno == ERANGE)
    {
      if (d != 0)
    {
      /* overflow */
      *data_stat = DATA_STATUS_TRUNCATED;
    }
      /* d == 0 is underflow, we don't have an error for this */
    }

  /* ignore trailing spaces */
  end_ptr = (char *) intl_skip_spaces (end_ptr, NULL, codeset);
  if (*end_ptr)         /* all input does not consumed */
    {
      *data_stat = DATA_STATUS_NOT_CONSUMED;
    }
  *num_value = d;

  if (do_alloc)
    {
      free_and_init (strp);
    }

  return status;
}

/*
 * tp_atobi - Coerce a string to a bigint.
 *    return: NO_ERROR or error code
 *    src(in): string DB_VALUE
 *    num_value(out): bigint container
 *    data_stat(out): if overflow is detected, this is set to
 *            DATA_STATUS_TRUNCATED
 * Note:
 *    Accepts strings that are not null terminated. Don't call this unless
 *    src is a string db_value.
 *    If string contains decimal part, performs rounding.
 *
 */
static int
tp_atobi (const DB_VALUE * src, DB_BIGINT * num_value, DB_DATA_STATUS * data_stat)
{
  const char *strp = db_get_string (src);
  const char *stre = NULL;
  const char *p = NULL, *old_p = NULL;
  int status = NO_ERROR;
  bool is_negative = false;
  INTL_CODESET codeset = db_get_string_codeset (src);
  bool is_hex = false, is_scientific = false;
  bool has_leading_zero = false;

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

  stre = strp + db_get_string_size (src);

  /* skip leading spaces */
  while (strp != stre && char_isspace (*strp))
    {
      strp++;
    }

  /* read sign if any */
  if (strp != stre && (*strp == '-' || *strp == '+'))
    {
      is_negative = (*strp == '-');
      strp++;
    }
  else
    {
      is_negative = false;
    }

  /* 0x or 0X */
  if (strp != stre && *strp == '0' && (strp + 1) != stre && (*(strp + 1) == 'x' || *(strp + 1) == 'X'))
    {
      is_hex = true;
      strp += 2;
    }

  /* skip leading zeros */
  while (strp != stre && *strp == '0')
    {
      strp++;

      if (!has_leading_zero)
    {
      has_leading_zero = true;
    }
    }

  /* See CBRD-24780.
   * For backwards compatibility, casting from empty string to bigint is allowed. */
  if (!is_hex && *strp != '\0')
    {
      /* check whether is scientific format */
      p = strp;

      /* check first part */
      while (p != stre && char_isdigit (*p))
    {
      ++p;
    }

      if (p != stre && *p == '.')
    {
      ++p;

      while (p != stre && char_isdigit (*p))
        {
          ++p;
        }
    }

      /* no first part */
      if (!has_leading_zero && p == strp)
    {
      return ER_FAILED;
    }

      /* skip trailing white spaces of first part */
      p = (char *) intl_skip_spaces (p, stre, codeset);

      /* check exponent part */
      if (p != stre)
    {
      if (*p == 'e' || *p == 'E')
        {
          ++p;

          /* check second part */
          if (p != stre && (*p == '+' || *p == '-'))
        {
          ++p;
        }

          old_p = p;
          while (p != stre && char_isdigit (*p))
        {
          ++p;
        }

          if (p == old_p)
        {
          return ER_FAILED;
        }

          /* skip trailing white spaces of second part */
          p = (char *) intl_skip_spaces (p, stre, codeset);
          if (p == stre)
        {
          is_scientific = true;
        }
        }
      else
        {
          return ER_FAILED;
        }
    }
    }

  /* convert to bigint */
  if (is_hex)
    {
      status = tp_hex_str_to_bi (strp, stre, codeset, is_negative, num_value, data_stat);
    }
  else if (is_scientific)
    {
      status = tp_scientific_str_to_bi (strp, stre, codeset, is_negative, num_value, data_stat);
    }
  else
    {
      status = tp_digit_number_str_to_bi (strp, stre, codeset, is_negative, num_value, data_stat);
    }

  return status;
}

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * tp_itoa - int to string representation for given radix
 *    return: string pointer (given or malloc'd)
 *    value(in): int value
 *    string(in/out): dest buffer or NULL
 *    radix(in): int value between 2 and 36
 */
static char *
tp_itoa (int value, char *string, int radix)
{
  char tmp[33];
  char *tp = tmp;
  int i;
  unsigned v;
  int sign;
  char *sp;

  if (radix > 36 || radix <= 1)
    {
      return 0;
    }

  sign = (radix == 10 && value < 0);

  if (sign)
    {
      v = -value;
    }
  else
    {
      v = (unsigned) value;
    }

  while (v || tp == tmp)
    {
      i = v % radix;
      v = v / radix;
      if (i < 10)
    {
      *tp++ = i + '0';
    }
      else
    {
      *tp++ = i + 'a' - 10;
    }
    }

  if (string == NULL)
    {
      string = (char *) malloc ((tp - tmp) + sign + 1);
      if (string == NULL)
    {
      return string;
    }
    }
  sp = string;

  if (sign)
    {
      *sp++ = '-';
    }
  while (tp > tmp)
    {
      *sp++ = *--tp;
    }
  *sp = '\0';
  return string;
}
#endif /* ENABLE_UNUSED_FUNCTION */

/*
 * tp_ltoa - bigint to string representation for given radix
 *    return: string pointer (given or malloc'd)
 *    value(in): bigint value
 *    string(in/out): dest buffer or NULL
 *    radix(in): int value between 2 and 36
 */
static char *
tp_ltoa (DB_BIGINT value, char *string, int radix)
{
  char tmp[33];
  char *tp = tmp;
  int i;
  UINT64 v;
  int sign;
  char *sp;

  if (radix > 36 || radix <= 1)
    {
      return 0;
    }

  sign = (radix == 10 && value < 0);

  if (sign)
    {
      v = -value;
    }
  else
    {
      v = (UINT64) value;
    }

  while (v || tp == tmp)
    {
      i = v % radix;
      v = v / radix;
      if (i < 10)
    {
      *tp++ = i + '0';
    }
      else
    {
      *tp++ = i + 'a' - 10;
    }
    }

  if (string == NULL)
    {
      string = (char *) malloc ((tp - tmp) + sign + 1);
      if (string == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) ((tp - tmp) + sign + 1));
      return NULL;
    }
    }
  sp = string;

  if (sign)
    {
      *sp++ = '-';
    }
  while (tp > tmp)
    {
      *sp++ = *--tp;
    }
  *sp = '\0';

  return string;
}

/*
 * format_floating_point() - formats a digits sequence and an integer exponent
 *               for a floating-point number (from dtoa) to the
 *               printable representation
 *
 *  return:
 *  new_string(out):    the sequence of decimal digits for the floating-point
 *          number mantisa, to be reformated into a character
 *          sequence for a printable floating point number.
 *          the buffer is assumed to be large enought for the
 *          printable sequence
 *  rve(out):       end of sequence of digits
 *  ndigits(in):    floating number precision to be used for printing
 *  decpt(in):      decimal point position in the digits sequence
 *          (similar to the exponent)
 *  sign(in):       sign of the floating-point number
 */
static void
format_floating_point (char *new_string, char *rve, int ndigits, int decpt, int sign)
{
  assert (new_string && rve);

  if (decpt != 9999)
    {
      if (ndigits >= decpt && decpt > -4)   /* as in the C 2005 standard for printf conversion specification */
    {
      /* print as a fractional number */
      if (decpt > rve - new_string)
        {
          /* append with zeros until the decimal point is encountered */
          while (new_string + decpt > rve)
        {
          *rve++ = '0';
        }
          *rve = '\0';
          /* no decimal point needed */
        }
      else if (decpt <= 0)
        {
          /* prepend zeroes until the decimal point is encountered */
          /* if decpt is -3, insert 3 zeroes between the decimal point and first non-zero digit */
          size_t n_left_pad = +2 - decpt;

          char *p = new_string + n_left_pad;

          rve += n_left_pad;
          do
        {
          *rve = *(rve - n_left_pad);
          rve--;
        }
          while (rve != p);

          *rve = *(rve - n_left_pad);

          p--;
          while (p != new_string + 1)
        {
          *p-- = '0';
        }
          *p-- = '.';
          *p = '0';
        }
      else if (decpt != rve - new_string)
        {
          /* insert decimal point within the digits sequence at position indicated by decpt */
          rve++;

          while (rve != new_string + decpt)
        {
          *rve = *(rve - 1);
          rve--;
        }
          *rve = '.';
        }
    }
      else
    {
      /* print as mantisa followed by exponent */
      if (rve > new_string + 1)
        {
          /* insert the decimal point before the second digit, if any */
          char *p = rve;

          while (p != new_string)
        {
          *p = *(p - 1);
          p--;
        }

          p[1] = '.';
          rve++;
        }
      *rve++ = 'e';

      decpt--;      /* convert from 0.432e12 to 4.32e11 */
      if (decpt < 0)
        {
          *rve++ = '-';
          decpt = -decpt;
        }
      else
        {
          *rve++ = '+';
        }

      if (decpt > 99)
        {
          *rve++ = '0' + decpt / 100;
          *rve++ = '0' + decpt % 100 / 10;
          *rve++ = '0' + decpt % 10;
        }
      else if (decpt > 9)
        {
          *rve++ = '0' + decpt / 10;
          *rve++ = '0' + decpt % 10;
        }
      else
        *rve++ = '0' + decpt;

      *rve = '\0';
    }
    }

  /* prepend '-' sign if number is negative */
  if (sign)
    {
      char ch = *new_string;

      rve = new_string + 1;

      while (*rve)
    {
      /* swap(ch, *rve); */
      ch ^= *rve;
      *rve = ch ^ *rve;
      ch ^= *rve++;
    }

      /* swap(ch, *rve); */
      ch ^= *rve;
      *rve = ch ^ *rve;
      ch ^= *rve++;

      rve[0] = '\0';
      *new_string = '-';
    }
}

/*
 * tp_ftoa() - convert a float DB_VALUE to a string DB_VALUE.
 *         Only the decimal representation is preserved by
 *         by the conversion, in order to avoid printing
 *         inexact digits. This means that if the number is
 *         read back from string into a float, the binary
 *         representation for the float might be different
 *         than the original, but the printed number shall
 *             always be the same.
 *
 * return:
 * src(in):     float DB_VALUE to be converted to string
 * result(in/out):  string DB_VALUE of the desired [VAR][N]CHAR
 *          domain type and null value, that receives
 *          the string resulting from conversion.
 */
void
tp_ftoa (DB_VALUE const *src, DB_VALUE * result)
{
  /* dtoa() appears to ignore the requested number of digits... */
  const int ndigits = TP_FLOAT_MANTISA_DECIMAL_PRECISION;
  char *str_float, *rve;
  int decpt, sign;

  assert (DB_VALUE_TYPE (src) == DB_TYPE_FLOAT);
  assert (DB_VALUE_TYPE (result) == DB_TYPE_NULL);

  rve = str_float = (char *) db_private_alloc (NULL, TP_FLOAT_AS_CHAR_LENGTH + 1);
  if (str_float == NULL)
    {
      db_make_null (result);
      return;
    }

  /* _dtoa just returns the digits sequence and the exponent as for a number in the form 0.4321344e+14 */
  _dtoa (db_get_float (src), 0, ndigits, &decpt, &sign, &rve, str_float, 1);

  /* rounding should also be performed here */
  str_float[ndigits] = '\0';    /* _dtoa() disregards ndigits */

  format_floating_point (str_float, str_float + strlen (str_float), ndigits, decpt, sign);

  switch (DB_VALUE_DOMAIN_TYPE (result))
    {
    case DB_TYPE_CHAR:
      db_make_char (result, DB_VALUE_PRECISION (result), str_float, strlen (str_float), db_get_string_codeset (result),
            db_get_string_collation (result));
      result->need_clear = true;
      break;

    case DB_TYPE_VARCHAR:
      db_make_varchar (result, DB_VALUE_PRECISION (result), str_float, strlen (str_float),
               db_get_string_codeset (result), db_get_string_collation (result));
      result->need_clear = true;
      break;

    default:
      db_private_free_and_init (NULL, str_float);
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TP_CANT_COERCE, 2, pr_type_name (DB_VALUE_DOMAIN_TYPE (src)),
          pr_type_name (DB_VALUE_DOMAIN_TYPE (result)));
      db_make_null (result);
      break;
    }
}

/*
 *  tp_dtoa():      converts a double DB_VALUE to a string DB_VALUE.
 *          Only as many digits as can be computed exactly are
 *          written in the resulting string.
 *
 * return:
 * src(in):     double DB_VALUE to be converted to string
 * result(in/out):  string DB_VALUE of the desired [VAR][N]CHAR domain
 *          type and null value, to receive the converted float
 */
void
tp_dtoa (DB_VALUE const *src, DB_VALUE * result)
{
  /* dtoa() appears to ignore the requested number of digits... */
  const int ndigits = TP_DOUBLE_MANTISA_DECIMAL_PRECISION;
  char *str_double, *rve;
  int decpt, sign;

  assert (DB_VALUE_TYPE (src) == DB_TYPE_DOUBLE);
  assert (DB_VALUE_TYPE (result) == DB_TYPE_NULL);

  rve = str_double = (char *) db_private_alloc (NULL, TP_DOUBLE_AS_CHAR_LENGTH + 1);
  if (str_double == NULL)
    {
      db_make_null (result);
      return;
    }

  _dtoa (db_get_double (src), 0, ndigits, &decpt, &sign, &rve, str_double, 0);
  /* rounding should also be performed here */
  str_double[ndigits] = '\0';   /* _dtoa() disregards ndigits */

  format_floating_point (str_double, str_double + strlen (str_double), ndigits, decpt, sign);

  switch (DB_VALUE_DOMAIN_TYPE (result))
    {
    case DB_TYPE_CHAR:
      db_make_char (result, DB_VALUE_PRECISION (result), str_double, strlen (str_double),
            db_get_string_codeset (result), db_get_string_collation (result));
      result->need_clear = true;
      break;

    case DB_TYPE_VARCHAR:
      db_make_varchar (result, DB_VALUE_PRECISION (result), str_double, strlen (str_double),
               db_get_string_codeset (result), db_get_string_collation (result));
      result->need_clear = true;
      break;

    default:
      db_private_free_and_init (NULL, str_double);
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TP_CANT_COERCE, 2, pr_type_name (DB_VALUE_DOMAIN_TYPE (src)),
          pr_type_name (DB_VALUE_DOMAIN_TYPE (result)));
      db_make_null (result);
      break;
    }
}

/*
 * tp_enumeration_to_varchar  - Get the value of an enumeration as a varchar.
 *    return: error code or NO_ERROR
 *    src(in): enumeration
 *    result(in/out): varchar
 * Note:
 *    The string value of the varchar value is not a copy of the enumeration
 *    value, it just points to it
 */
int
tp_enumeration_to_varchar (const DB_VALUE * src, DB_VALUE * result)
{
  int error = NO_ERROR;

  if (src == NULL || result == NULL)
    {
      assert (false);
      return ER_FAILED;
    }

  if (db_get_enum_string (src) == NULL)
    {
      db_make_varchar (result, DB_DEFAULT_PRECISION, "", 0, db_get_enum_codeset (src), db_get_enum_collation (src));
    }
  else
    {
      db_make_varchar (result, DB_DEFAULT_PRECISION, db_get_enum_string (src), db_get_enum_string_size (src),
               db_get_enum_codeset (src), db_get_enum_collation (src));
    }

  return error;
}

#define BITS_IN_BYTE            8
#define HEX_IN_BYTE             2
#define BITS_IN_HEX             4

#define BYTE_COUNT(bit_cnt)     (((bit_cnt)+BITS_IN_BYTE-1)/BITS_IN_BYTE)
#define BYTE_COUNT_HEX(bit_cnt) (((bit_cnt)+BITS_IN_HEX-1)/BITS_IN_HEX)

/*
 * bfmt_print - Change the given string to a representation of the given
 * bit string value in the given format.
 *    return: NO_ERROR or -1 if max_size is too small
 *    bfmt(in): 0: for binary representation or 1: for hex representation
 *    the_db_bit(in): DB_VALUE
 *    string(out): output buffer
 *    max_size(in): size of output buffer
 */
static int
bfmt_print (int bfmt, const DB_VALUE * the_db_bit, char *string, int max_size)
{
  int length = 0;
  int string_index = 0;
  int byte_index;
  int bit_index;
  const char *bstring;
  int error = NO_ERROR;
  static const char digits[16] = {
    '0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
  };

  /* Get the buffer and the length from the_db_bit */
  bstring = db_get_bit (the_db_bit, &length);

  switch (bfmt)
    {
    case 0:         /* BIT_STRING_BINARY */
      if (length + 1 > max_size)
    {
      error = -1;
    }
      else
    {
      for (byte_index = 0; byte_index < BYTE_COUNT (length); byte_index++)
        {
          for (bit_index = 7; bit_index >= 0 && string_index < length; bit_index--)
        {
          *string = digits[((bstring[byte_index] >> bit_index) & 0x1)];
          string++;
          string_index++;
        }
        }
      *string = '\0';
    }
      break;

    case 1:         /* BIT_STRING_HEX */
      if (BYTE_COUNT_HEX (length) + 1 > max_size)
    {
      error = -1;
    }
      else
    {
      for (byte_index = 0; byte_index < BYTE_COUNT (length); byte_index++)
        {
          *string = digits[((bstring[byte_index] >> BITS_IN_HEX) & 0x0f)];
          string++;
          string_index++;
          if (string_index < BYTE_COUNT_HEX (length))
        {
          *string = digits[((bstring[byte_index] & 0x0f))];
          string++;
          string_index++;
        }
        }
      *string = '\0';
    }
      break;

    default:
      break;
    }

  return error;
}


#define ROUND(x)          ((x) > 0 ? ((x) + .5) : ((x) - .5))
#define SECONDS_IN_A_DAY      (long)(86400) /* 24L * 60L * 60L */
#define TP_IS_CHAR_STRING(db_val_type)                  \
    (db_val_type == DB_TYPE_CHAR || db_val_type == DB_TYPE_VARCHAR)

#define TP_IS_LOB(db_val_type)                                          \
    (db_val_type == DB_TYPE_BLOB || db_val_type == DB_TYPE_CLOB)

#define TP_IS_DATETIME_TYPE(db_val_type) TP_IS_DATE_OR_TIME_TYPE (db_val_type)

#define TP_IMPLICIT_COERCION_NOT_ALLOWED(src_type, dest_type)       \
   ((TP_IS_CHAR_STRING(src_type) && !(TP_IS_CHAR_STRING(dest_type) ||   \
                      TP_IS_DATETIME_TYPE(dest_type) || \
                      TP_IS_NUMERIC_TYPE(dest_type) ||  \
                      dest_type == DB_TYPE_ENUMERATION)) ||\
    (!TP_IS_CHAR_STRING(src_type) && src_type != DB_TYPE_ENUMERATION && \
     TP_IS_CHAR_STRING(dest_type)) ||                   \
    (TP_IS_LOB(src_type) || TP_IS_LOB(dest_type)))

/*
 * tp_value_string_to_double - Coerce a string to a double.
 *    return: NO_ERROR, ER_OUT_OF_VIRTUAL_MEMORY or ER_FAILED.
 *    src(in): string DB_VALUE
 *    result(in/out): float container
 * Note:
 *    Accepts strings that are not null terminated.
 */
int
tp_value_string_to_double (const DB_VALUE * value, DB_VALUE * result)
{
  DB_DATA_STATUS data_stat;
  double dbl;
  int ret;
  DB_TYPE type = DB_VALUE_TYPE (value);

  if (!TP_IS_CHAR_STRING (type))
    {
      db_make_double (result, 0);
      return ER_FAILED;
    }

  ret = tp_atof (value, &dbl, &data_stat);
  if (ret != NO_ERROR)
    {
      db_make_double (result, 0);
    }
  else
    {
      db_make_double (result, dbl);
    }

  return ret;
}

static void
make_desired_string_db_value (DB_TYPE desired_type, const TP_DOMAIN * desired_domain, const char *new_string,
                  DB_VALUE * target, TP_DOMAIN_STATUS * status, DB_DATA_STATUS * data_stat)
{
  DB_VALUE temp;

  assert (desired_domain->collation_flag == TP_DOMAIN_COLL_NORMAL
      || desired_domain->collation_flag == TP_DOMAIN_COLL_LEAVE);

  *status = DOMAIN_COMPATIBLE;
  switch (desired_type)
    {
    case DB_TYPE_CHAR:
      db_make_char (&temp, desired_domain->precision, new_string, strlen (new_string),
            TP_DOMAIN_CODESET (desired_domain), TP_DOMAIN_COLLATION (desired_domain));
      break;

    case DB_TYPE_VARCHAR:
      db_make_varchar (&temp, desired_domain->precision, new_string, strlen (new_string),
               TP_DOMAIN_CODESET (desired_domain), TP_DOMAIN_COLLATION (desired_domain));
      break;

    default:            /* Can't get here.  This just quiets the compiler */
      break;
    }

  temp.need_clear = true;
  if (db_char_string_coerce (&temp, target, data_stat) != NO_ERROR)
    {
      *status = DOMAIN_INCOMPATIBLE;
    }
  else
    {
      *status = DOMAIN_COMPATIBLE;
    }
  pr_clear_value (&temp);
}

/*
 * tp_value_coerce - Coerce a value into one of another domain.
 *    return: TP_DOMAIN_STATUS
 *    src(in): source value
 *    dest(out): destination value
 *    desired_domain(in): destination domain
 */
TP_DOMAIN_STATUS
tp_value_coerce (const DB_VALUE * src, DB_VALUE * dest, const TP_DOMAIN * desired_domain)
{
  return tp_value_cast (src, dest, desired_domain, true);
}

/*
 * tp_value_coerce_strict () - convert a value to desired domain without loss
 *                 of precision
 * return : error code or NO_ERROR
 * src (in)   : source value
 * dest (out) : destination value
 * desired_domain (in) : destination domain
 */
int
tp_value_coerce_strict (const DB_VALUE * src, DB_VALUE * dest, const TP_DOMAIN * desired_domain)
{
  DB_TYPE desired_type, original_type;
  int err = NO_ERROR;
  DB_VALUE temp, *target;

  /* A NULL src is allowed but destination remains NULL, not desired_domain */
  if (src == NULL || (original_type = DB_VALUE_TYPE (src)) == DB_TYPE_NULL)
    {
      db_make_null (dest);
      return err;
    }

  if (desired_domain == NULL)
    {
      db_make_null (dest);
      return ER_FAILED;
    }

  desired_type = TP_DOMAIN_TYPE (desired_domain);

  if (!TP_IS_NUMERIC_TYPE (desired_type) && !TP_IS_DATETIME_TYPE (desired_type))
    {
      db_make_null (dest);
      return ER_FAILED;
    }

  if (desired_type == original_type)
    {
      if (src != dest)
    {
      pr_clone_value ((DB_VALUE *) src, dest);
      return NO_ERROR;
    }
    }

  /*
   * If src == dest, coerce into a temporary variable and
   * handle the conversion before returning.
   */
  if (src == dest)
    {
      target = &temp;
    }
  else
    {
      target = dest;
    }

  /*
   * Initialize the destination domain, important for the
   * nm_ coercion functions which take domain information inside the
   * destination db value.
   */
  db_value_domain_init (target, desired_type, desired_domain->precision, desired_domain->scale);

  switch (desired_type)
    {
    case DB_TYPE_SHORT:
      switch (original_type)
    {
    case DB_TYPE_MONETARY:
      {
        double i = 0;
        const double val = db_get_monetary (src)->amount;
        if (OR_CHECK_SHORT_OVERFLOW (val))
          {
        err = ER_FAILED;
        break;
          }
        if (modf (val, &i) != 0)
          {
        err = ER_FAILED;
        break;
          }
        db_make_short (target, (short) i);
        break;
      }
    case DB_TYPE_INTEGER:
      if (OR_CHECK_SHORT_OVERFLOW (db_get_int (src)))
        {
          err = ER_FAILED;
          break;
        }
      db_make_short (target, (short) db_get_int (src));
      break;
    case DB_TYPE_BIGINT:
      if (OR_CHECK_SHORT_OVERFLOW (db_get_bigint (src)))
        {
          err = ER_FAILED;
          break;
        }
      db_make_short (target, (short) db_get_bigint (src));
      break;
    case DB_TYPE_FLOAT:
      {
        float i = 0;
        const float val = db_get_float (src);
        if (OR_CHECK_SHORT_OVERFLOW (val))
          {
        err = ER_FAILED;
        break;
          }
        if (modff (val, &i) != 0)
          {
        err = ER_FAILED;
        break;
          }
        db_make_short (target, (short) i);
        break;
      }
    case DB_TYPE_DOUBLE:
      {
        double i = 0;
        const double val = db_get_double (src);
        if (OR_CHECK_SHORT_OVERFLOW (val))
          {
        err = ER_FAILED;
        break;
          }
        if (modf (val, &i) != 0)
          {
        err = ER_FAILED;
        break;
          }
        db_make_short (target, (short) i);
        break;
      }
    case DB_TYPE_NUMERIC:
      err = numeric_db_value_coerce_from_num_strict ((DB_VALUE *) src, target);
      break;
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      {
        double num_value = 0.0, i = 0.0;
        DB_DATA_STATUS data_stat = DATA_STATUS_OK;
        if (tp_atof (src, &num_value, &data_stat) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        if (data_stat != DATA_STATUS_OK || OR_CHECK_SHORT_OVERFLOW (num_value))
          {
        err = ER_FAILED;
        break;
          }
        if (modf (num_value, &i) != 0)
          {
        err = ER_FAILED;
        break;
          }
        db_make_short (target, (short) i);
        break;
      }
    default:
      err = ER_FAILED;
      break;
    }
      break;

    case DB_TYPE_INTEGER:
      switch (original_type)
    {
    case DB_TYPE_SHORT:
      db_make_int (target, (int) db_get_short (src));
      err = NO_ERROR;
      break;
    case DB_TYPE_MONETARY:
      {
        double i = 0;
        const double val = db_get_monetary (src)->amount;
        if (OR_CHECK_INT_OVERFLOW (val))
          {
        err = ER_FAILED;
        break;
          }
        if (modf (val, &i) != 0)
          {
        err = ER_FAILED;
        break;
          }
        db_make_int (target, (int) i);
        break;
      }
    case DB_TYPE_BIGINT:
      if (OR_CHECK_INT_OVERFLOW (db_get_bigint (src)))
        {
          err = ER_FAILED;
          break;
        }
      db_make_int (target, (int) db_get_bigint (src));
      break;
    case DB_TYPE_FLOAT:
      {
        float i = 0;
        const float val = db_get_float (src);
        if (OR_CHECK_INT_OVERFLOW (val))
          {
        err = ER_FAILED;
        break;
          }
        if (modff (val, &i) != 0)
          {
        err = ER_FAILED;
        break;
          }
        db_make_int (target, (int) i);
        break;
      }
    case DB_TYPE_DOUBLE:
      {
        double i = 0;
        const double val = db_get_double (src);
        if (OR_CHECK_INT_OVERFLOW (val))
          {
        err = ER_FAILED;
        break;
          }
        if (modf (val, &i) != 0)
          {
        err = ER_FAILED;
        break;
          }
        db_make_int (target, (int) i);
        break;
      }
    case DB_TYPE_NUMERIC:
      err = numeric_db_value_coerce_from_num_strict ((DB_VALUE *) src, target);
      break;
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      {
        double num_value = 0.0, i = 0.0;
        DB_DATA_STATUS data_stat = DATA_STATUS_OK;
        if (tp_atof (src, &num_value, &data_stat) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        if (data_stat != DATA_STATUS_OK || OR_CHECK_INT_OVERFLOW (num_value))
          {
        err = ER_FAILED;
        break;
          }
        if (modf (num_value, &i) != 0)
          {
        err = ER_FAILED;
        break;
          }
        db_make_int (target, (int) i);
        break;
      }
    default:
      err = ER_FAILED;
      break;
    }
      break;

    case DB_TYPE_BIGINT:
      switch (original_type)
    {
    case DB_TYPE_SHORT:
      db_make_bigint (target, db_get_short (src));
      break;
    case DB_TYPE_INTEGER:
      db_make_bigint (target, db_get_int (src));
      break;
    case DB_TYPE_MONETARY:
      {
        double i = 0;
        const double val = db_get_monetary (src)->amount;
        if (OR_CHECK_BIGINT_OVERFLOW (val))
          {
        err = ER_FAILED;
        break;
          }
        if (modf (val, &i) != 0)
          {
        err = ER_FAILED;
        break;
          }
        db_make_bigint (target, (DB_BIGINT) i);
        break;
      }
      break;
    case DB_TYPE_FLOAT:
      {
        float i = 0;
        const float val = db_get_float (src);
        if (OR_CHECK_BIGINT_OVERFLOW (val))
          {
        err = ER_FAILED;
        break;
          }
        if (modff (val, &i) != 0)
          {
        err = ER_FAILED;
        break;
          }
        db_make_bigint (target, (DB_BIGINT) i);
        break;
      }
    case DB_TYPE_DOUBLE:
      {
        double i = 0;
        const double val = db_get_double (src);
        if (OR_CHECK_BIGINT_OVERFLOW (val))
          {
        err = ER_FAILED;
        break;
          }
        if (modf (val, &i) != 0)
          {
        err = ER_FAILED;
        break;
          }
        db_make_bigint (target, (DB_BIGINT) i);
        break;
      }
    case DB_TYPE_NUMERIC:
      err = numeric_db_value_coerce_from_num_strict ((DB_VALUE *) src, target);
      break;
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      {
        double num_value = 0.0, i = 0.0;
        DB_DATA_STATUS data_stat = DATA_STATUS_OK;
        if (tp_atof (src, &num_value, &data_stat) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        if (data_stat != DATA_STATUS_OK || OR_CHECK_BIGINT_OVERFLOW (num_value))
          {
        err = ER_FAILED;
        break;
          }
        if (modf (num_value, &i) != 0)
          {
        err = ER_FAILED;
        break;
          }
        db_make_bigint (target, (DB_BIGINT) i);
        break;
      }
    default:
      err = ER_FAILED;
      break;
    }
      break;

    case DB_TYPE_FLOAT:
      switch (original_type)
    {
    case DB_TYPE_SHORT:
      db_make_float (target, (float) db_get_short (src));
      break;
    case DB_TYPE_INTEGER:
      db_make_float (target, (float) db_get_int (src));
      break;
    case DB_TYPE_BIGINT:
      db_make_float (target, (float) db_get_bigint (src));
      break;
    case DB_TYPE_NUMERIC:
      err = numeric_db_value_coerce_from_num_strict ((DB_VALUE *) src, target);
      break;
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      {
        double num_value = 0.0;
        DB_DATA_STATUS data_stat = DATA_STATUS_OK;

        if (tp_atof (src, &num_value, &data_stat) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }

        if (data_stat != DATA_STATUS_OK || OR_CHECK_FLOAT_OVERFLOW (num_value))
          {
        err = ER_FAILED;
        break;
          }
        db_make_float (target, (float) num_value);
        break;
      }
    default:
      err = ER_FAILED;
      break;
    }
      break;

    case DB_TYPE_DOUBLE:
      switch (original_type)
    {
    case DB_TYPE_SHORT:
      db_make_double (target, (double) db_get_short (src));
      break;
    case DB_TYPE_INTEGER:
      db_make_double (target, (double) db_get_int (src));
      break;
    case DB_TYPE_BIGINT:
      db_make_double (target, (double) db_get_bigint (src));
      break;
    case DB_TYPE_FLOAT:
      db_make_double (target, (double) db_get_float (src));
      break;
    case DB_TYPE_MONETARY:
      db_make_double (target, db_get_monetary (src)->amount);
      break;
    case DB_TYPE_NUMERIC:
      err = numeric_db_value_coerce_from_num_strict ((DB_VALUE *) src, target);
      break;
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      {
        DB_DATA_STATUS data_stat = DATA_STATUS_OK;
        double num_value = 0.0;
        if (tp_atof (src, &num_value, &data_stat) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        if (data_stat != DATA_STATUS_OK)
          {
        err = ER_FAILED;
        break;
          }
        db_make_double (target, num_value);
        break;
      }
    default:
      err = ER_FAILED;
      break;
    }
      break;

    case DB_TYPE_NUMERIC:
      switch (original_type)
    {
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      {
        DB_VALUE temp;

        if (tp_atonumeric (src, &temp) != NO_ERROR)
          {
        if (er_errid () != NO_ERROR)
          {
            err = DOMAIN_ERROR;
          }
        else
          {
            err = DOMAIN_INCOMPATIBLE;
          }
          }
        else
          {
        err = tp_value_coerce (&temp, target, desired_domain);
          }
        break;
      }
    case DB_TYPE_SHORT:
    case DB_TYPE_INTEGER:
    case DB_TYPE_BIGINT:
    case DB_TYPE_NUMERIC:
      {
        DB_DATA_STATUS data_stat = DATA_STATUS_OK;
        err = numeric_db_value_coerce_to_num ((DB_VALUE *) src, target, &data_stat);
        if (data_stat != DATA_STATUS_OK)
          {
        err = ER_FAILED;
        break;
          }
        break;
      }
    default:
      err = ER_FAILED;
      break;
    }
      break;

    case DB_TYPE_MONETARY:
      switch (original_type)
    {
    case DB_TYPE_SHORT:
      db_make_monetary (target, DB_CURRENCY_DEFAULT, (double) db_get_short (src));
      break;
    case DB_TYPE_INTEGER:
      db_make_monetary (target, DB_CURRENCY_DEFAULT, (double) db_get_int (src));
      break;
    case DB_TYPE_BIGINT:
      db_make_monetary (target, DB_CURRENCY_DEFAULT, (double) db_get_bigint (src));
      break;
    case DB_TYPE_FLOAT:
      db_make_monetary (target, DB_CURRENCY_DEFAULT, (double) db_get_float (src));
      break;
    case DB_TYPE_DOUBLE:
      db_make_monetary (target, DB_CURRENCY_DEFAULT, db_get_double (src));
      break;
    case DB_TYPE_NUMERIC:
      err = numeric_db_value_coerce_from_num_strict ((DB_VALUE *) src, target);
      break;
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      {
        double num_value = 0.0;
        DB_DATA_STATUS data_stat = DATA_STATUS_OK;
        if (tp_atof (src, &num_value, &data_stat) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        if (data_stat == DATA_STATUS_TRUNCATED || data_stat == DATA_STATUS_NOT_CONSUMED)
          {
        err = ER_FAILED;
        break;
          }
        db_make_monetary (target, DB_CURRENCY_DEFAULT, num_value);
        break;
      }
    default:
      err = ER_FAILED;
      break;
    }
      break;

    case DB_TYPE_TIME:
      switch (original_type)
    {
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      {
        DB_TIME time = 0;
        if (tp_atotime (src, &time) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        db_value_put_encoded_time (target, &time);
        break;
      }
    default:
      err = ER_FAILED;
      break;
    }
      break;

    case DB_TYPE_DATE:
      switch (original_type)
    {
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      {
        DB_DATE date = 0;

        if (tp_atodate (src, &date) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        db_value_put_encoded_date (target, &date);
        break;
      }
    case DB_TYPE_DATETIME:
      {
        DB_DATETIME *src_dt = NULL;

        src_dt = db_get_datetime (src);
        if (src_dt->time != 0)
          {
        /* only "downcast" if time is 0 */
        err = ER_FAILED;
        break;
          }
        db_value_put_encoded_date (target, (DB_DATE *) (&src_dt->date));
        break;
      }
    case DB_TYPE_DATETIMELTZ:
    case DB_TYPE_DATETIMETZ:
      {
        DB_DATETIME *utc_dt_p;
        DB_DATETIMETZ *dt_tz_p;
        DB_DATETIME local_dt;
        TZ_ID tz_id;

        /* DATETIMELTZ and DATETIMETZ store in UTC, convert to session */
        if (original_type == DB_TYPE_DATETIMELTZ)
          {
        utc_dt_p = db_get_datetime (src);
        if (tz_create_session_tzid_for_datetime (utc_dt_p, true, &tz_id) != NO_ERROR)
          {
            err = ER_FAILED;
            break;
          }
          }
        else
          {
        dt_tz_p = db_get_datetimetz (src);
        utc_dt_p = &dt_tz_p->datetime;
        tz_id = dt_tz_p->tz_id;
          }

        if (tz_utc_datetimetz_to_local (utc_dt_p, &tz_id, &local_dt) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }

        if (local_dt.time != 0)
          {
        /* only "downcast" if time is 0 */
        err = ER_FAILED;
        break;
          }

        db_value_put_encoded_date (target, (DB_DATE *) (&local_dt.date));
        break;
      }
    case DB_TYPE_TIMESTAMP:
    case DB_TYPE_TIMESTAMPLTZ:
      {
        DB_DATE date = 0;
        DB_TIME time = 0;
        DB_TIMESTAMP *ts = NULL;

        ts = db_get_timestamp (src);
        (void) db_timestamp_decode_ses (ts, &date, &time);
        if (time != 0)
          {
        /* only "downcast" if time is 0 */
        err = ER_FAILED;
        break;
          }
        db_value_put_encoded_date (target, &date);
        break;
      }
    case DB_TYPE_TIMESTAMPTZ:
      {
        DB_DATE date = 0;
        DB_TIME time = 0;
        DB_TIMESTAMPTZ *ts_tz = NULL;

        ts_tz = db_get_timestamptz (src);
        err = db_timestamp_decode_w_tz_id (&ts_tz->timestamp, &ts_tz->tz_id, &date, &time);
        if (err != NO_ERROR || time != 0)
          {
        /* only "downcast" if time is 0 */
        err = ER_FAILED;
        break;
          }
        db_value_put_encoded_date (target, &date);
        break;
      }
    default:
      err = ER_FAILED;
      break;
    }
      break;

    case DB_TYPE_DATETIME:
      switch (original_type)
    {
    case DB_TYPE_DATE:
      {
        DB_DATETIME datetime = { 0, 0 };
        datetime.date = *db_get_date (src);
        datetime.time = 0;
        db_make_datetime (target, &datetime);
        break;
      }
    case DB_TYPE_DATETIMETZ:
      {
        DB_DATETIMETZ *dt_tz = db_get_datetimetz (src);
        db_make_datetime (target, &dt_tz->datetime);
        break;
      }
    case DB_TYPE_DATETIMELTZ:
      {
        DB_DATETIME *dt = db_get_datetime (src);
        db_make_datetime (target, dt);
        break;
      }
    case DB_TYPE_VARCHAR:
    case DB_TYPE_CHAR:
      {
        DB_DATETIME datetime = { 0, 0 };
        if (tp_atoudatetime (src, &datetime) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        db_make_datetime (target, &datetime);
        break;
      }
    case DB_TYPE_TIMESTAMP:
    case DB_TYPE_TIMESTAMPLTZ:
      {
        DB_DATETIME datetime = { 0, 0 };
        DB_TIMESTAMP *utime = db_get_timestamp (src);
        DB_DATE date;
        DB_TIME time;

        if (db_timestamp_decode_ses (utime, &date, &time) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        datetime.time = time * 1000;
        datetime.date = date;
        db_make_datetime (target, &datetime);
        break;
      }
    case DB_TYPE_TIMESTAMPTZ:
      {
        DB_DATETIME datetime = { 0, 0 };
        DB_DATE date;
        DB_TIME time;
        DB_TIMESTAMPTZ *ts_tz = db_get_timestamptz (src);

        if (db_timestamp_decode_w_tz_id (&ts_tz->timestamp, &ts_tz->tz_id, &date, &time) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }

        datetime.time = time * 1000;
        datetime.date = date;
        db_make_datetime (target, &datetime);
        break;
      }
    default:
      err = ER_FAILED;
      break;
    }
      break;

    case DB_TYPE_DATETIMETZ:
      switch (original_type)
    {
    case DB_TYPE_DATE:
    case DB_TYPE_DATETIME:
      {
        DB_DATETIMETZ dt_tz = DB_DATETIMETZ_INITIALIZER;

        if (original_type == DB_TYPE_DATE)
          {
        dt_tz.datetime.date = *db_get_date (src);
        dt_tz.datetime.time = 0;
          }
        else
          {
        dt_tz.datetime = *db_get_datetime (src);
          }

        err = tz_create_datetimetz_from_ses (&(dt_tz.datetime), &dt_tz);
        if (err == NO_ERROR)
          {
        db_make_datetimetz (target, &dt_tz);
          }
        break;
      }

    case DB_TYPE_DATETIMELTZ:
      {
        DB_DATETIMETZ dt_tz = DB_DATETIMETZ_INITIALIZER;
        DB_DATETIME *dt = db_get_datetime (src);

        dt_tz.datetime = *dt;
        err = tz_create_session_tzid_for_datetime (dt, false, &dt_tz.tz_id);
        if (err == NO_ERROR)
          {
        db_make_datetimetz (target, &dt_tz);
          }
        break;
      }
    case DB_TYPE_VARCHAR:
    case DB_TYPE_CHAR:
      {
        DB_DATETIMETZ dt_tz = DB_DATETIMETZ_INITIALIZER;

        if (tp_atodatetimetz (src, &dt_tz) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }

        db_make_datetimetz (target, &dt_tz);
      }
      break;
    case DB_TYPE_TIMESTAMP:
    case DB_TYPE_TIMESTAMPLTZ:
      {
        DB_DATETIMETZ dt_tz = DB_DATETIMETZ_INITIALIZER;
        DB_TIMESTAMP *utime = db_get_timestamp (src);
        DB_DATE date;
        DB_TIME time;

        /* convert DT to TS in UTC reference */
        db_timestamp_decode_utc (utime, &date, &time);
        dt_tz.datetime.date = date;
        dt_tz.datetime.time = time * 1000;
        err = tz_create_session_tzid_for_datetime (&dt_tz.datetime, true, &(dt_tz.tz_id));
        if (err == NO_ERROR)
          {
        db_make_datetimetz (target, &dt_tz);
          }
        break;
      }
    case DB_TYPE_TIMESTAMPTZ:
      {
        DB_DATETIMETZ dt_tz = DB_DATETIMETZ_INITIALIZER;
        DB_TIMESTAMPTZ *ts_tz = db_get_timestamptz (src);
        DB_DATE date;
        DB_TIME time;

        (void) db_timestamp_decode_utc (&ts_tz->timestamp, &date, &time);
        dt_tz.datetime.time = time * 1000;
        dt_tz.datetime.date = date;
        dt_tz.tz_id = ts_tz->tz_id;
        db_make_datetimetz (target, &dt_tz);
        break;
      }
    default:
      err = ER_FAILED;
      break;
    }
      break;

    case DB_TYPE_DATETIMELTZ:
      switch (original_type)
    {
    case DB_TYPE_DATE:
    case DB_TYPE_DATETIME:
      {
        DB_DATETIME datetime;
        DB_DATETIMETZ dt_tz;

        if (original_type == DB_TYPE_DATE)
          {
        datetime.date = *db_get_date (src);
        datetime.time = 0;
          }
        else
          {
        datetime = *db_get_datetime (src);
          }

        err = tz_create_datetimetz_from_ses (&datetime, &dt_tz);
        if (err != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }

        db_make_datetimeltz (target, &dt_tz.datetime);
        break;
      }
    case DB_TYPE_DATETIMETZ:
      {
        DB_DATETIMETZ *dt_tz = db_get_datetimetz (src);

        /* copy datetime (UTC) */
        db_make_datetimeltz (target, &dt_tz->datetime);
        break;
      }
    case DB_TYPE_VARCHAR:
    case DB_TYPE_CHAR:
      {
        DB_DATETIMETZ dt_tz = DB_DATETIMETZ_INITIALIZER;

        if (tp_atodatetimetz (src, &dt_tz) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        db_make_datetimeltz (target, &dt_tz.datetime);
        break;
      }
    case DB_TYPE_TIMESTAMP:
    case DB_TYPE_TIMESTAMPLTZ:
      {
        DB_DATETIME datetime = { 0, 0 };
        DB_TIMESTAMP *utime = db_get_timestamp (src);
        DB_DATE date;
        DB_TIME time;

        (void) db_timestamp_decode_utc (utime, &date, &time);
        datetime.time = time * 1000;
        datetime.date = date;
        db_make_datetimeltz (target, &datetime);
        break;
      }
    case DB_TYPE_TIMESTAMPTZ:
      {
        DB_DATETIME datetime = { 0, 0 };
        DB_TIMESTAMPTZ *ts_tz = db_get_timestamptz (src);
        DB_DATE date;
        DB_TIME time;

        (void) db_timestamp_decode_utc (&ts_tz->timestamp, &date, &time);
        datetime.time = time * 1000;
        datetime.date = date;
        db_make_datetimeltz (target, &datetime);
        break;
      }
    default:
      err = ER_FAILED;
      break;
    }
      break;

    case DB_TYPE_TIMESTAMP:
      switch (original_type)
    {
    case DB_TYPE_VARCHAR:
    case DB_TYPE_CHAR:
      {
        DB_TIMESTAMP ts = 0;

        if (tp_atoutime (src, &ts) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        db_make_timestamp (target, ts);
        break;
      }
    case DB_TYPE_DATETIME:
      {
        DB_DATETIME dt = *db_get_datetime (src);
        DB_DATE date = dt.date;
        DB_TIME time = dt.time / 1000;
        DB_TIMESTAMP ts = 0;

        if (db_timestamp_encode_ses (&date, &time, &ts, NULL) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        db_make_timestamp (target, ts);
        break;
      }
    case DB_TYPE_DATETIMELTZ:
      {
        DB_DATETIME dt = *db_get_datetime (src);
        DB_DATE date = dt.date;
        DB_TIME time = dt.time / 1000;
        DB_TIMESTAMP ts = 0;

        if (db_timestamp_encode_utc (&date, &time, &ts) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        db_make_timestamp (target, ts);
        break;
      }

    case DB_TYPE_DATETIMETZ:
      {
        DB_DATETIMETZ *dt_tz = db_get_datetimetz (src);
        DB_DATE date = dt_tz->datetime.date;
        DB_TIME time = dt_tz->datetime.time / 1000;
        DB_TIMESTAMP ts = 0;

        if (db_timestamp_encode_utc (&date, &time, &ts) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        db_make_timestamp (target, ts);
        break;
      }

    case DB_TYPE_DATE:
      {
        DB_TIME tm = 0;
        DB_DATE date = *db_get_date (src);
        DB_TIMESTAMP ts = 0;

        db_time_encode (&tm, 0, 0, 0);
        if (db_timestamp_encode_ses (&date, &tm, &ts, NULL) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        db_make_timestamp (target, ts);
        break;
      }

    case DB_TYPE_TIMESTAMPTZ:
      {
        DB_TIMESTAMPTZ *ts_tz = db_get_timestamptz (src);

        /* copy timestamp value (UTC) */
        db_make_timestamp (target, ts_tz->timestamp);
        break;
      }

    case DB_TYPE_TIMESTAMPLTZ:
      {
        DB_TIMESTAMP *ts = db_get_timestamp (src);

        /* copy timestamp value (UTC) */
        db_make_timestamp (target, *ts);
        break;
      }

    default:
      err = ER_FAILED;
      break;
    }
      break;

    case DB_TYPE_TIMESTAMPLTZ:
      switch (original_type)
    {
    case DB_TYPE_VARCHAR:
    case DB_TYPE_CHAR:
      {
        DB_TIMESTAMPTZ ts_tz = { 0, 0 };

        if (tp_atotimestamptz (src, &ts_tz) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        db_make_timestampltz (target, ts_tz.timestamp);
        break;
      }
    case DB_TYPE_DATETIME:
      {
        DB_DATETIME dt = *db_get_datetime (src);
        DB_DATE date = dt.date;
        DB_TIME time = dt.time / 1000;
        DB_TIMESTAMP ts = 0;

        if (db_timestamp_encode_ses (&date, &time, &ts, NULL) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        db_make_timestampltz (target, ts);
        break;
      }
    case DB_TYPE_DATETIMELTZ:
      {
        DB_DATETIME dt = *db_get_datetime (src);
        DB_DATE date = dt.date;
        DB_TIME time = dt.time / 1000;
        DB_TIMESTAMP ts = 0;

        if (db_timestamp_encode_utc (&date, &time, &ts) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        db_make_timestampltz (target, ts);
        break;
      }

    case DB_TYPE_DATETIMETZ:
      {
        DB_DATETIMETZ *dt_tz = db_get_datetimetz (src);
        DB_DATE date = dt_tz->datetime.date;
        DB_TIME time = dt_tz->datetime.time / 1000;
        DB_TIMESTAMP ts = 0;

        if (db_timestamp_encode_utc (&date, &time, &ts) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        db_make_timestampltz (target, ts);
        break;
      }

    case DB_TYPE_DATE:
      {
        DB_TIME tm = 0;
        DB_DATE date = *db_get_date (src);
        DB_TIMESTAMP ts = 0;

        db_time_encode (&tm, 0, 0, 0);
        if (db_timestamp_encode_ses (&date, &tm, &ts, NULL) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        db_make_timestampltz (target, ts);
        break;
      }

    case DB_TYPE_TIMESTAMP:
      {
        DB_TIMESTAMP *ts = db_get_timestamp (src);

        /* copy val timestamp value (UTC) */
        db_make_timestampltz (target, *ts);
        break;
      }

    case DB_TYPE_TIMESTAMPTZ:
      {
        DB_TIMESTAMPTZ *ts_tz = db_get_timestamptz (src);

        /* copy val timestamp value (UTC) */
        db_make_timestampltz (target, ts_tz->timestamp);
        break;
      }

    default:
      err = ER_FAILED;
      break;
    }
      break;

    case DB_TYPE_TIMESTAMPTZ:
      switch (original_type)
    {
    case DB_TYPE_VARCHAR:
    case DB_TYPE_CHAR:
      {
        DB_TIMESTAMPTZ ts_tz = { 0, 0 };

        if (tp_atotimestamptz (src, &ts_tz) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        db_make_timestamptz (target, &ts_tz);
        break;
      }
    case DB_TYPE_DATETIME:
      {
        DB_TIMESTAMPTZ ts_tz = { 0, 0 };
        DB_DATETIME dt = *db_get_datetime (src);
        DB_DATE date = dt.date;
        DB_TIME time = dt.time / 1000;

        if (db_timestamp_encode_ses (&date, &time, &ts_tz.timestamp, &ts_tz.tz_id) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        db_make_timestamptz (target, &ts_tz);
        break;
      }

    case DB_TYPE_DATETIMELTZ:
      {
        DB_TIMESTAMPTZ ts_tz = { 0, 0 };
        DB_DATETIME dt = *db_get_datetime (src);
        DB_DATE date = dt.date;
        DB_TIME time = dt.time / 1000;

        if (db_timestamp_encode_utc (&date, &time, &ts_tz.timestamp) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        ts_tz.tz_id = *tz_get_utc_tz_id ();
        db_make_timestamptz (target, &ts_tz);
        break;
      }

    case DB_TYPE_DATETIMETZ:
      {
        DB_TIMESTAMPTZ ts_tz = { 0, 0 };
        DB_DATETIMETZ *dt_tz = db_get_datetimetz (src);
        DB_DATE date = dt_tz->datetime.date;
        DB_TIME time = dt_tz->datetime.time / 1000;

        if (db_timestamp_encode_utc (&date, &time, &ts_tz.timestamp) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        ts_tz.tz_id = dt_tz->tz_id;
        db_make_timestamptz (target, &ts_tz);
        break;
      }

    case DB_TYPE_DATE:
      {
        DB_TIMESTAMPTZ ts_tz = { 0, 0 };
        DB_TIME tm = 0;
        DB_DATE date = *db_get_date (src);

        db_time_encode (&tm, 0, 0, 0);
        if (db_timestamp_encode_ses (&date, &tm, &ts_tz.timestamp, &ts_tz.tz_id) != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        db_make_timestamptz (target, &ts_tz);
        break;
      }

    case DB_TYPE_TIMESTAMP:
    case DB_TYPE_TIMESTAMPLTZ:
      {
        DB_TIMESTAMPTZ ts_tz = { 0, 0 };

        ts_tz.timestamp = *db_get_timestamp (src);

        err = tz_create_session_tzid_for_timestamp (&(ts_tz.timestamp), &(ts_tz.tz_id));

        if (err != NO_ERROR)
          {
        err = ER_FAILED;
        break;
          }
        db_make_timestamptz (target, &ts_tz);
        break;
      }

    default:
      err = ER_FAILED;
      break;
    }
      break;
    default:
      err = ER_FAILED;
      break;
    }

  if (err == ER_FAILED)
    {
      /* the above code might have set an error message but we don't want to propagate it in this context */
      er_clear ();
    }

  return err;
}

/*
 * tp_value_coerce_internal - Coerce a value into one of another domain.
 *    return: error code
 *    src(in): source value
 *    dest(out): destination value
 *    desired_domain(in): destination domain
 *    coercion_mode(in): flag for the coercion mode
 *    do_domain_select(in): flag for select appropriate domain from
 *                          'desired_domain'
 *    preserve_domain(in): flag to preserve dest's domain
 */
static TP_DOMAIN_STATUS
tp_value_cast_internal (const DB_VALUE * src, DB_VALUE * dest, const TP_DOMAIN * desired_domain,
            const TP_COERCION_MODE coercion_mode, bool do_domain_select, bool preserve_domain)
{
  DB_TYPE desired_type, original_type;
  int err;
  TP_DOMAIN_STATUS status;
  TP_DOMAIN *best, *p_tmp_desired_domain;
  TP_DOMAIN tmp_desired_domain;
  const DB_MONETARY *v_money;
  DB_UTIME v_utime;
  DB_TIMESTAMPTZ v_timestamptz;
  DB_DATETIME v_datetime;
  DB_DATETIMETZ v_datetimetz;
  DB_TIME v_time;
  DB_DATE v_date;
  DB_DATA_STATUS data_stat;
  DB_VALUE temp, *target;
  int hour, minute, second, millisecond;
  int year, month, day;
  TZ_ID ses_tz_id;

  bool ti = true;
  static bool ignore_trailing_space = prm_get_bool_value (PRM_ID_IGNORE_TRAILING_SPACE);

  DB_VALUE src_replacement;

  db_make_null (&src_replacement);

  err = NO_ERROR;
  status = DOMAIN_COMPATIBLE;

  if (desired_domain == NULL)
    {
      db_make_null (dest);
      return DOMAIN_INCOMPATIBLE;
    }

  /* If more than one destination domain, select the most appropriate */
  if (do_domain_select)
    {
      if (desired_domain->next != NULL)
    {
      best = tp_domain_select (desired_domain, src, 1, TP_ANY_MATCH);
      if (best != NULL)
        {
          desired_domain = best;
        }
    }
    }
  desired_type = TP_DOMAIN_TYPE (desired_domain);

  /* A NULL src is allowed but destination remains NULL, not desired_domain */
  if (src == NULL || (original_type = DB_VALUE_TYPE (src)) == DB_TYPE_NULL)
    {
      if (preserve_domain)
    {
      db_value_domain_init (dest, desired_type, desired_domain->precision, desired_domain->scale);
      db_value_put_null (dest);
    }
      else
    {
      db_make_null (dest);
    }
      return status;
    }

  if (desired_type != original_type && original_type == DB_TYPE_JSON)
    {
      /* TODO this is very hackish,
       * we really need to split this function up
       */
      DB_JSON_TYPE json_type = db_json_get_type (db_get_json_document (src));
      JSON_DOC *src_doc = db_get_json_document (src);
      bool use_replacement = true;

      switch (json_type)
    {
    case DB_JSON_DOUBLE:
      db_make_double (&src_replacement, db_json_get_double_from_document (src_doc));
      break;
    case DB_JSON_INT:
      db_make_int (&src_replacement, db_json_get_int_from_document (src_doc));
      break;
    case DB_JSON_BIGINT:
      db_make_bigint (&src_replacement, db_json_get_bigint_from_document (src_doc));
      break;
    case DB_JSON_BOOL:
      switch (desired_type)
        {
        case DB_TYPE_CHAR:
        case DB_TYPE_VARCHAR:
          db_make_string (&src_replacement, db_json_get_bool_as_str_from_document (src_doc));
          src_replacement.need_clear = true;
          break;
        default:
          db_make_int (&src_replacement, db_json_get_bool_from_document (src_doc) ? 1 : 0);
        }
      break;
    case DB_JSON_STRING:
      db_make_string_copy (&src_replacement, db_json_get_string_from_document (src_doc));
      break;
    default:
      use_replacement = false;
      /* do nothing */
      break;
    }

      if (use_replacement)
    {
      if (src == dest)
        {
          // if src is equal to dest then JSON_DOC can be deleted after required information was extracted from it
          pr_clear_value (dest);
        }

      original_type = DB_VALUE_TYPE (&src_replacement);
      src = &src_replacement;
    }
    }
  else if (original_type == DB_TYPE_MIDXKEY && (desired_type == DB_TYPE_CHAR || desired_type == DB_TYPE_VARCHAR))
    {
      string_buffer sb;

      sb.clear ();
      db_sprint_value (src, sb);
      db_make_string_copy (&src_replacement, sb.get_buffer ());
      sb.clear ();

      if (src == dest)
    {
      // if src is equal to dest then JSON_DOC can be deleted after required information was extracted from it
      pr_clear_value (dest);
    }

      original_type = DB_VALUE_TYPE (&src_replacement);
      src = &src_replacement;
    }


  if (desired_type == original_type)
    {
      /*
       * If there is an easy to check exact match on a non-parameterized
       * domain, just do a simple clone of the value.
       */
      if (!desired_domain->is_parameterized)
    {
      if (src != dest)
        {
          pr_clone_value ((DB_VALUE *) src, dest);
          pr_clear_value (&src_replacement);
        }
      return status;
    }
      else
    {           /* is parameterized domain */
      switch (desired_type)
        {
        case DB_TYPE_NUMERIC:
          if (desired_domain->precision == DB_VALUE_PRECISION (src)
          && desired_domain->scale == DB_VALUE_SCALE (src))
        {
          if (src != dest)
            {
              pr_clone_value ((DB_VALUE *) src, dest);
            }
          return (status);
        }
          break;
        case DB_TYPE_OID:
          if (src != dest)
        {
          pr_clone_value ((DB_VALUE *) src, dest);
        }
          return (status);
        case DB_TYPE_JSON:
          if (desired_domain->json_validator != NULL
          && db_json_validate_doc (desired_domain->json_validator, src->data.json.document) != NO_ERROR)
        {
          pr_clear_value (&src_replacement);
          ASSERT_ERROR ();
          return DOMAIN_ERROR;
        }
          if (src != dest)
        {
          pr_clone_value (src, dest);
        }
          pr_clear_value (&src_replacement);
          return (status);
        default:
          /* pr_is_string_type(desired_type) - NEED MORE CONSIDERATION */
          break;
        }
    }
    }

  /*
   * If the coercion_mode is TP_IMPLICIT_COERCION, check to see if the original
   * type can be implicitly coerced to the desired_type.
   *
   * (Note: This macro only picks up only coercions that are not allowed
   *        implicitly but are allowed explicitly.)
   */
  if (coercion_mode == TP_IMPLICIT_COERCION)
    {
      if (TP_IMPLICIT_COERCION_NOT_ALLOWED (original_type, desired_type))
    {
      if (preserve_domain)
        {
          db_value_domain_init (dest, desired_type, desired_domain->precision, desired_domain->scale);
          db_value_put_null (dest);
        }
      else
        {
          db_make_null (dest);
        }

      pr_clear_value (&src_replacement);
      return DOMAIN_INCOMPATIBLE;
    }
    }

  /*
   * If src == dest, coerce into a temporary variable and
   * handle the conversion before returning.
   */
  if (src == dest)
    {
      target = &temp;
    }
  else
    {
      target = dest;
    }

  /*
   * Initialize the destination domain, important for the
   * nm_ coercion functions thich take domain information inside the
   * destination db value.
   */
  db_value_domain_init (target, desired_type, desired_domain->precision, desired_domain->scale);

  if (TP_IS_CHAR_TYPE (desired_type))
    {
      if (desired_domain->collation_flag == TP_DOMAIN_COLL_ENFORCE)
    {
      if (TP_IS_CHAR_TYPE (original_type))
        {
          db_string_put_cs_and_collation (target, TP_DOMAIN_CODESET (desired_domain),
                          TP_DOMAIN_COLLATION (desired_domain));

          /* create a domain from source value */
          p_tmp_desired_domain = tp_domain_resolve_value ((DB_VALUE *) src, &tmp_desired_domain);
          p_tmp_desired_domain->codeset = TP_DOMAIN_CODESET (desired_domain);
          p_tmp_desired_domain->collation_id = TP_DOMAIN_COLLATION (desired_domain);

          desired_domain = p_tmp_desired_domain;
        }
      else if (TP_IS_SET_TYPE (original_type))
        {
          TP_DOMAIN *curr_set_dom;
          TP_DOMAIN *elem_dom;
          TP_DOMAIN *new_elem_dom;
          TP_DOMAIN *save_elem_dom_next;

          /* source value already exists, we expect that a collection domain already exists and is cached */
          curr_set_dom = tp_domain_resolve_value ((DB_VALUE *) src, NULL);
          elem_dom = curr_set_dom->setdomain;
          curr_set_dom->setdomain = NULL;

          /* copy only parent collection domain */
          p_tmp_desired_domain = tp_domain_copy (curr_set_dom, false);
          curr_set_dom->setdomain = elem_dom;
          while (elem_dom != NULL)
        {
          /* create a new domain from this */
          save_elem_dom_next = elem_dom->next;
          elem_dom->next = NULL;
          new_elem_dom = tp_domain_copy (elem_dom, false);
          elem_dom->next = save_elem_dom_next;

          if (TP_IS_CHAR_TYPE (TP_DOMAIN_TYPE (elem_dom)))
            {
              /* for string domains overwrite collation */
              new_elem_dom->collation_id = TP_DOMAIN_COLLATION (desired_domain);
              new_elem_dom->codeset = TP_DOMAIN_CODESET (desired_domain);
            }

          tp_domain_add (&(p_tmp_desired_domain->setdomain), new_elem_dom);
          elem_dom = elem_dom->next;
        }

          desired_domain = p_tmp_desired_domain;
        }
      else
        {
          /* ENUM values are included here (cannot be HV) */
          /* source value does not have collation, we leave it as it is */
          if (src != dest)
        {
          pr_clone_value ((DB_VALUE *) src, dest);
        }

          pr_clear_value (&src_replacement);
          return DOMAIN_COMPATIBLE;
        }
      desired_type = TP_DOMAIN_TYPE (desired_domain);
    }
      else if (desired_domain->collation_flag == TP_DOMAIN_COLL_NORMAL)
    {
      db_string_put_cs_and_collation (target, TP_DOMAIN_CODESET (desired_domain),
                      TP_DOMAIN_COLLATION (desired_domain));
    }
      else
    {
      assert (desired_domain->collation_flag == TP_DOMAIN_COLL_LEAVE);
      if (TP_IS_CHAR_TYPE (original_type))
        {
          db_string_put_cs_and_collation (target, db_get_string_codeset (src), db_get_string_collation (src));
        }
    }
    }

  switch (desired_type)
    {
    case DB_TYPE_SHORT:
      switch (original_type)
    {
    case DB_TYPE_MONETARY:
      v_money = db_get_monetary (src);
      if (OR_CHECK_SHORT_OVERFLOW (v_money->amount))
        {
          status = DOMAIN_OVERFLOW;
        }
      else
        {
          db_make_short (target, (short) ROUND (v_money->amount));
        }
      break;
    case DB_TYPE_INTEGER:
      if (OR_CHECK_SHORT_OVERFLOW (db_get_int (src)))
        {
          status = DOMAIN_OVERFLOW;
        }
      else
        {
          db_make_short (target, db_get_int (src));
        }
      break;
    case DB_TYPE_BIGINT:
      if (OR_CHECK_SHORT_OVERFLOW (db_get_bigint (src)))
        {
          status = DOMAIN_OVERFLOW;
        }
      else
        {
          db_make_short (target, (short) db_get_bigint (src));
        }
      break;
    case DB_TYPE_FLOAT:
      if (OR_CHECK_SHORT_OVERFLOW (db_get_float (src)))
        {
          status = DOMAIN_OVERFLOW;
        }
      else
        {
          db_make_short (target, (short) ROUND (db_get_float (src)));
        }
      break;
    case DB_TYPE_DOUBLE:
      if (OR_CHECK_SHORT_OVERFLOW (db_get_double (src)))
        {
          status = DOMAIN_OVERFLOW;
        }
      else
        {
          db_make_short (target, (short) ROUND (db_get_double (src)));
        }
      break;
    case DB_TYPE_NUMERIC:
      status = (TP_DOMAIN_STATUS) numeric_db_value_coerce_from_num ((DB_VALUE *) src, target, &data_stat);
      if (status != NO_ERROR)
        {
          status = DOMAIN_OVERFLOW;
        }
      break;
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      {
        double num_value = 0.0;

        if (tp_atof (src, &num_value, &data_stat) != NO_ERROR || data_stat == DATA_STATUS_NOT_CONSUMED)
          {
        if (er_errid () != NO_ERROR)    /* i.e, malloc failure */
          {
            status = DOMAIN_ERROR;
          }
        else
          {
            status = DOMAIN_INCOMPATIBLE;   /* conversion error */
          }
        break;
          }

        if (data_stat == DATA_STATUS_TRUNCATED || OR_CHECK_SHORT_OVERFLOW (num_value))
          {
        status = DOMAIN_OVERFLOW;
          }
        else
          {
        db_make_short (target, (short) ROUND (num_value));
          }
        break;
      }
    case DB_TYPE_ENUMERATION:
      db_make_short (target, db_get_enum_short (src));
      break;
    default:
      status = DOMAIN_INCOMPATIBLE;
      break;
    }
      break;

    case DB_TYPE_INTEGER:
      switch (original_type)
    {
    case DB_TYPE_SHORT:
      db_make_int (target, db_get_short (src));
      break;
    case DB_TYPE_BIGINT:
      if (OR_CHECK_INT_OVERFLOW (db_get_bigint (src)))
        {
          status = DOMAIN_OVERFLOW;
        }
      else
        {
          db_make_int (target, (int) db_get_bigint (src));
        }
      break;
    case DB_TYPE_MONETARY:
      v_money = db_get_monetary (src);
      if (OR_CHECK_INT_OVERFLOW (v_money->amount))
        {
          status = DOMAIN_OVERFLOW;
        }
      else
        {
          db_make_int (target, (int) ROUND (v_money->amount));
        }
      break;
    case DB_TYPE_FLOAT:
      {
        int tmp_int;
        float tmp_float;

        if (OR_CHECK_INT_OVERFLOW (db_get_float (src)))
          {
        status = DOMAIN_OVERFLOW;
          }
        else
          {
        tmp_float = db_get_float (src);
        tmp_int = (int) ROUND (tmp_float);

#if defined(AIX)
        /* in AIX, float/double to int will not overflow, make it the same as linux. */
        if (tmp_float == (float) DB_INT32_MAX)
          {
            tmp_int = DB_INT32_MIN;
          }
#endif

        if (OR_CHECK_ASSIGN_OVERFLOW (tmp_int, tmp_float))
          {
            status = DOMAIN_OVERFLOW;
          }
        else
          {
            db_make_int (target, tmp_int);
          }
          }
      }
      break;
    case DB_TYPE_DOUBLE:
      if (OR_CHECK_INT_OVERFLOW (db_get_double (src)))
        {
          status = DOMAIN_OVERFLOW;
        }
      else
        {
          db_make_int (target, (int) ROUND (db_get_double (src)));
        }
      break;
    case DB_TYPE_NUMERIC:
      status = (TP_DOMAIN_STATUS) numeric_db_value_coerce_from_num ((DB_VALUE *) src, target, &data_stat);
      if (status != NO_ERROR)
        {
          status = DOMAIN_OVERFLOW;
        }
      break;
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      {
        double num_value = 0.0;

        if (tp_atof (src, &num_value, &data_stat) != NO_ERROR || data_stat == DATA_STATUS_NOT_CONSUMED)
          {
        if (er_errid () != NO_ERROR)    /* i.e, malloc failure */
          {
            status = DOMAIN_ERROR;
          }
        else
          {
            status = DOMAIN_INCOMPATIBLE;   /* conversion error */
          }
        break;
          }

        if (data_stat == DATA_STATUS_TRUNCATED || OR_CHECK_INT_OVERFLOW (num_value))
          {
        status = DOMAIN_OVERFLOW;
          }
        else
          {
        db_make_int (target, (int) ROUND (num_value));
          }
        break;
      }
    case DB_TYPE_ENUMERATION:
      db_make_int (target, db_get_enum_short (src));
      break;
    default:
      status = DOMAIN_INCOMPATIBLE;
      break;
    }
      break;

    case DB_TYPE_BIGINT:
      switch (original_type)
    {
    case DB_TYPE_SHORT:
      db_make_bigint (target, db_get_short (src));
      break;
    case DB_TYPE_INTEGER:
      db_make_bigint (target, db_get_int (src));
      break;
    case DB_TYPE_MONETARY:
      {
        DB_BIGINT tmp_bi;

        v_money = db_get_monetary (src);
        if (OR_CHECK_BIGINT_OVERFLOW (v_money->amount))
          {
        status = DOMAIN_OVERFLOW;
          }
        else
          {
        tmp_bi = (DB_BIGINT) ROUND (v_money->amount);
        if (OR_CHECK_ASSIGN_OVERFLOW (tmp_bi, v_money->amount))
          {
            status = DOMAIN_OVERFLOW;
          }
        else
          {
            db_make_bigint (target, tmp_bi);
          }
          }
      }
      break;
    case DB_TYPE_FLOAT:
      {
        float tmp_float;
        DB_BIGINT tmp_bi;

        if (OR_CHECK_BIGINT_OVERFLOW (db_get_float (src)))
          {
        status = DOMAIN_OVERFLOW;
          }
        else
          {
        tmp_float = db_get_float (src);
        tmp_bi = (DB_BIGINT) ROUND (tmp_float);

#if defined(AIX)
        /* in AIX, float/double to int64 will not overflow, make it the same as linux. */
        if (tmp_float == (float) DB_BIGINT_MAX)
          {
            tmp_bi = DB_BIGINT_MIN;
          }
#endif
        if (OR_CHECK_ASSIGN_OVERFLOW (tmp_bi, tmp_float))
          {
            status = DOMAIN_OVERFLOW;
          }
        else
          {
            db_make_bigint (target, tmp_bi);
          }
          }
      }
      break;
    case DB_TYPE_DOUBLE:
      {
        double tmp_double;
        DB_BIGINT tmp_bi;

        if (OR_CHECK_BIGINT_OVERFLOW (db_get_double (src)))
          {
        status = DOMAIN_OVERFLOW;
          }
        else
          {
        tmp_double = db_get_double (src);
        tmp_bi = (DB_BIGINT) ROUND (tmp_double);

#if defined(AIX)
        /* in AIX, float/double to int64 will not overflow, make it the same as linux. */
        if (tmp_double == (double) DB_BIGINT_MAX)
          {
            tmp_bi = DB_BIGINT_MIN;
          }
#endif
        if (OR_CHECK_ASSIGN_OVERFLOW (tmp_bi, tmp_double))
          {
            status = DOMAIN_OVERFLOW;
          }
        else
          {
            db_make_bigint (target, tmp_bi);
          }
          }
      }
      break;
    case DB_TYPE_NUMERIC:
      status = (TP_DOMAIN_STATUS) numeric_db_value_coerce_from_num ((DB_VALUE *) src, target, &data_stat);
      if (status != NO_ERROR)
        {
          status = DOMAIN_OVERFLOW;
        }
      break;
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      {
        DB_BIGINT num_value = 0;

        if (tp_atobi (src, &num_value, &data_stat) != NO_ERROR)
          {
        if (er_errid () != NO_ERROR)    /* i.e, malloc failure */
          {
            status = DOMAIN_ERROR;
          }
        else
          {
            status = DOMAIN_INCOMPATIBLE;   /* conversion error */
          }
        break;
          }

        if (data_stat == DATA_STATUS_TRUNCATED)
          {
        status = DOMAIN_OVERFLOW;
        break;
          }
        db_make_bigint (target, num_value);
        break;
      }
    case DB_TYPE_ENUMERATION:
      db_make_bigint (target, db_get_enum_short (src));
      break;
    default:
      status = DOMAIN_INCOMPATIBLE;
      break;
    }
      break;

    case DB_TYPE_FLOAT:
      switch (original_type)
    {
    case DB_TYPE_SHORT:
      db_make_float (target, (float) db_get_short (src));
      break;
    case DB_TYPE_INTEGER:
      db_make_float (target, (float) db_get_int (src));
      break;
    case DB_TYPE_BIGINT:
      db_make_float (target, (float) db_get_bigint (src));
      break;
    case DB_TYPE_DOUBLE:
      if (OR_CHECK_FLOAT_OVERFLOW (db_get_double (src)))
        {
          status = DOMAIN_OVERFLOW;
        }
      else
        {
          db_make_float (target, (float) db_get_double (src));
        }
      break;
    case DB_TYPE_MONETARY:
      v_money = db_get_monetary (src);
      if (OR_CHECK_FLOAT_OVERFLOW (v_money->amount))
        {
          status = DOMAIN_OVERFLOW;
        }
      else
        {
          db_make_float (target, (float) v_money->amount);
        }
      break;
    case DB_TYPE_NUMERIC:
      status = (TP_DOMAIN_STATUS) numeric_db_value_coerce_from_num ((DB_VALUE *) src, target, &data_stat);
      if (status != NO_ERROR)
        {
          status = DOMAIN_OVERFLOW;
        }
      break;
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      {
        double num_value = 0.0;

        if (tp_atof (src, &num_value, &data_stat) != NO_ERROR || data_stat == DATA_STATUS_NOT_CONSUMED)
          {
        if (er_errid () != NO_ERROR)    /* i.e, malloc failure */
          {
            status = DOMAIN_ERROR;
          }
        else
          {
            status = DOMAIN_INCOMPATIBLE;   /* conversion error */
          }
        break;
          }

        if (data_stat == DATA_STATUS_TRUNCATED || OR_CHECK_FLOAT_OVERFLOW (num_value))
          {
        status = DOMAIN_OVERFLOW;
          }
        else
          {
        db_make_float (target, (float) num_value);
          }
        break;
      }
    case DB_TYPE_ENUMERATION:
      db_make_float (target, (float) db_get_enum_short (src));
      break;
    default:
      status = DOMAIN_INCOMPATIBLE;
      break;
    }
      break;

    case DB_TYPE_DOUBLE:
      switch (original_type)
    {
    case DB_TYPE_SHORT:
      db_make_double (target, (double) db_get_short (src));
      break;
    case DB_TYPE_INTEGER:
      db_make_double (target, (double) db_get_int (src));
      break;
    case DB_TYPE_BIGINT:
      db_make_double (target, (double) db_get_bigint (src));
      break;
    case DB_TYPE_FLOAT:
      db_make_double (target, (double) db_get_float (src));
      break;
    case DB_TYPE_MONETARY:
      v_money = db_get_monetary (src);
      db_make_double (target, (double) v_money->amount);
      break;
    case DB_TYPE_NUMERIC:
      status = (TP_DOMAIN_STATUS) numeric_db_value_coerce_from_num ((DB_VALUE *) src, target, &data_stat);
      if (status != NO_ERROR)
        {
          status = DOMAIN_OVERFLOW;
        }
      break;
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      {
        double num_value = 0.0;

        if (tp_atof (src, &num_value, &data_stat) != NO_ERROR || data_stat == DATA_STATUS_NOT_CONSUMED)
          {
        if (er_errid () != NO_ERROR)    /* i.e, malloc failure */
          {
            status = DOMAIN_ERROR;
          }
        else
          {
            status = DOMAIN_INCOMPATIBLE;   /* conversion error */
          }
        break;
          }

        if (data_stat == DATA_STATUS_TRUNCATED)
          {
        status = DOMAIN_OVERFLOW;
          }
        else
          {
        db_make_double (target, num_value);
          }

        break;
      }
    case DB_TYPE_ENUMERATION:
      db_make_double (target, (double) db_get_enum_short (src));
      break;
    default:
      status = DOMAIN_INCOMPATIBLE;
      break;
    }
      break;

    case DB_TYPE_NUMERIC:
      /*
       * Numeric-to-numeric coercion will be handled in the nm_ module.
       * The desired precision & scale is communicated through the destination
       * value.
       */
      switch (original_type)
    {
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      {
        DB_VALUE temp;

        if (tp_atonumeric (src, &temp) != NO_ERROR)
          {
        if (er_errid () != NO_ERROR)
          {
            status = DOMAIN_ERROR;
          }
        else
          {
            status = DOMAIN_INCOMPATIBLE;
          }
          }
        else
          {
        status = tp_value_coerce (&temp, target, desired_domain);
          }
        break;
      }
    default:
      {
        int error_code = numeric_db_value_coerce_to_num ((DB_VALUE *) src, target, &data_stat);

        if (error_code == ER_IT_DATA_OVERFLOW || data_stat == DATA_STATUS_TRUNCATED)
          {
        status = DOMAIN_OVERFLOW;
          }
        else if (error_code != NO_ERROR)
          {
        status = DOMAIN_INCOMPATIBLE;
          }
        else
          {
        status = DOMAIN_COMPATIBLE;
          }
      }
      break;
    }
      break;

    case DB_TYPE_MONETARY:
      switch (original_type)
    {
    case DB_TYPE_SHORT:
      db_make_monetary (target, DB_CURRENCY_DEFAULT, (double) db_get_short (src));
      break;
    case DB_TYPE_INTEGER:
      db_make_monetary (target, DB_CURRENCY_DEFAULT, (double) db_get_int (src));
      break;
    case DB_TYPE_BIGINT:
      db_make_monetary (target, DB_CURRENCY_DEFAULT, (double) db_get_bigint (src));
      break;
    case DB_TYPE_FLOAT:
      db_make_monetary (target, DB_CURRENCY_DEFAULT, (double) db_get_float (src));
      break;
    case DB_TYPE_DOUBLE:
      db_make_monetary (target, DB_CURRENCY_DEFAULT, db_get_double (src));
      break;
    case DB_TYPE_NUMERIC:
      status = (TP_DOMAIN_STATUS) numeric_db_value_coerce_from_num ((DB_VALUE *) src, target, &data_stat);
      if (status != NO_ERROR)
        {
          status = DOMAIN_OVERFLOW;
        }
      break;
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      {
        double num_value = 0.0;

        if (tp_atof (src, &num_value, &data_stat) != NO_ERROR || data_stat == DATA_STATUS_NOT_CONSUMED)
          {
        if (er_errid () != NO_ERROR)    /* i.e, malloc failure */
          {
            status = DOMAIN_ERROR;
          }
        else
          {
            status = DOMAIN_INCOMPATIBLE;   /* conversion error */
          }
        break;
          }

        if (data_stat == DATA_STATUS_TRUNCATED)
          {
        status = DOMAIN_OVERFLOW;
          }
        else
          {
        db_make_monetary (target, DB_CURRENCY_DEFAULT, num_value);
          }
        break;
      }
    case DB_TYPE_ENUMERATION:
      db_make_monetary (target, DB_CURRENCY_DEFAULT, db_get_enum_short (src));
      break;
    default:
      status = DOMAIN_INCOMPATIBLE;
      break;
    }
      break;

    case DB_TYPE_TIMESTAMP:
      switch (original_type)
    {
    case DB_TYPE_VARCHAR:
    case DB_TYPE_CHAR:
      if (tp_atoutime (src, &v_utime) != NO_ERROR)
        {
          status = DOMAIN_ERROR;
        }
      else
        {
          db_make_timestamp (target, v_utime);
        }
      break;

    case DB_TYPE_ENUMERATION:
      {
        DB_VALUE varchar_val;
        if (tp_enumeration_to_varchar (src, &varchar_val) != NO_ERROR)
          {
        status = DOMAIN_ERROR;
        break;
          }
        status =
          tp_value_cast_internal (&varchar_val, target, desired_domain, coercion_mode, do_domain_select, false);
        break;
      }

    case DB_TYPE_DATETIME:
    case DB_TYPE_DATE:
      {
        if (original_type == DB_TYPE_DATE)
          {
        v_date = *db_get_date (src);
        db_time_encode (&v_time, 0, 0, 0);
          }
        else
          {
        v_datetime = *db_get_datetime (src);
        v_date = v_datetime.date;
        v_time = v_datetime.time / 1000;
          }

        if (db_timestamp_encode_ses (&v_date, &v_time, &v_utime, NULL) == NO_ERROR)
          {
        db_make_timestamp (target, v_utime);
          }
        else
          {
        status = DOMAIN_OVERFLOW;
          }
        break;
      }

    case DB_TYPE_DATETIMELTZ:
      v_datetime = *db_get_datetime (src);
      v_date = v_datetime.date;
      v_time = v_datetime.time / 1000;

      if (db_timestamp_encode_utc (&v_date, &v_time, &v_utime) == NO_ERROR)
        {
          db_make_timestamp (target, v_utime);
        }
      else
        {
          status = DOMAIN_OVERFLOW;
        }
      break;

    case DB_TYPE_DATETIMETZ:
      v_datetimetz = *db_get_datetimetz (src);
      v_date = v_datetimetz.datetime.date;
      v_time = v_datetimetz.datetime.time / 1000;

      if (db_timestamp_encode_utc (&v_date, &v_time, &v_utime) == NO_ERROR)
        {
          db_make_timestamp (target, v_utime);
        }
      else
        {
          status = DOMAIN_OVERFLOW;
        }
      break;

    case DB_TYPE_TIMESTAMPLTZ:
      /* copy timestamp (UTC) */
      db_make_timestamp (target, *db_get_timestamp (src));
      break;

    case DB_TYPE_TIMESTAMPTZ:
      v_timestamptz = *db_get_timestamptz (src);
      /* copy timestamp (UTC) */
      db_make_timestamp (target, v_timestamptz.timestamp);
      break;

    default:
      status = tp_value_coerce ((DB_VALUE *) src, target, &tp_Integer_domain);
      if (status == DOMAIN_COMPATIBLE)
        {
          int tmpint;
          tmpint = db_get_int (target);
          if (tmpint >= 0)
        {
          db_make_timestamp (target, (DB_UTIME) tmpint);
        }
          else
        {
          status = DOMAIN_INCOMPATIBLE;
        }
        }
      break;
    }
      break;

    case DB_TYPE_TIMESTAMPTZ:
      switch (original_type)
    {
    case DB_TYPE_VARCHAR:
    case DB_TYPE_CHAR:
      if (tp_atotimestamptz (src, &v_timestamptz) != NO_ERROR)
        {
          status = DOMAIN_ERROR;
        }
      else
        {
          db_make_timestamptz (target, &v_timestamptz);
        }
      break;

    case DB_TYPE_ENUMERATION:
      {
        DB_VALUE varchar_val;
        if (tp_enumeration_to_varchar (src, &varchar_val) != NO_ERROR)
          {
        status = DOMAIN_ERROR;
        break;
          }
        status =
          tp_value_cast_internal (&varchar_val, target, desired_domain, coercion_mode, do_domain_select, false);
        break;
      }

    case DB_TYPE_DATETIME:
    case DB_TYPE_DATE:
      /* convert from session to UTC */
      if (original_type == DB_TYPE_DATETIME)
        {
          v_datetime = *db_get_datetime (src);
          v_date = v_datetime.date;
          v_time = v_datetime.time / 1000;
        }
      else
        {
          assert (original_type == DB_TYPE_DATE);
          v_date = *db_get_date (src);
          v_time = 0;
        }

      if (db_timestamp_encode_ses (&v_date, &v_time, &v_timestamptz.timestamp, &v_timestamptz.tz_id) != NO_ERROR)
        {
          status = DOMAIN_ERROR;
        }
      else
        {
          db_make_timestamptz (target, &v_timestamptz);
        }
      break;

    case DB_TYPE_DATETIMELTZ:
      v_datetime = *db_get_datetime (src);
      v_date = v_datetime.date;
      v_time = v_datetime.time / 1000;

      /* encode DT as UTC and the TZ of session */
      if (db_timestamp_encode_utc (&v_date, &v_time, &v_timestamptz.timestamp) != NO_ERROR)
        {
          status = DOMAIN_ERROR;
          break;
        }
      if (tz_create_session_tzid_for_datetime (&v_datetime, true, &(v_timestamptz.tz_id)) != NO_ERROR)
        {
          status = DOMAIN_ERROR;
        }
      else
        {
          db_make_timestamptz (target, &v_timestamptz);
        }
      break;

    case DB_TYPE_DATETIMETZ:
      v_datetimetz = *db_get_datetimetz (src);
      v_date = v_datetimetz.datetime.date;
      v_time = v_datetimetz.datetime.time / 1000;

      /* encode TS to DT (UTC) and copy TZ from DT_TZ */
      if (db_timestamp_encode_utc (&v_date, &v_time, &v_timestamptz.timestamp) == NO_ERROR)
        {
          v_timestamptz.tz_id = v_datetimetz.tz_id;
          db_make_timestamptz (target, &v_timestamptz);
        }
      else
        {
          status = DOMAIN_OVERFLOW;
        }
      break;

    case DB_TYPE_TIMESTAMP:
    case DB_TYPE_TIMESTAMPLTZ:
      /* copy TS value and create TZ_ID for system TZ */
      v_timestamptz.timestamp = *db_get_timestamp (src);

      if (tz_create_session_tzid_for_timestamp (&v_timestamptz.timestamp, &(v_timestamptz.tz_id)) != NO_ERROR)
        {
          status = DOMAIN_ERROR;
          break;
        }

      db_make_timestamptz (target, &v_timestamptz);
      break;

    default:
      status = tp_value_coerce ((DB_VALUE *) src, target, &tp_Integer_domain);
      if (status == DOMAIN_COMPATIBLE)
        {
          int tmpint;

          tmpint = db_get_int (target);
          if (tmpint < 0)
        {
          status = DOMAIN_INCOMPATIBLE;
          break;
        }
          v_timestamptz.timestamp = (DB_UTIME) tmpint;

          if (tz_create_session_tzid_for_timestamp (&v_timestamptz.timestamp, &v_timestamptz.tz_id) != NO_ERROR)
        {
          status = DOMAIN_INCOMPATIBLE;
          break;
        }

          db_make_timestamptz (target, &v_timestamptz);
        }
      break;
    }
      break;

    case DB_TYPE_TIMESTAMPLTZ:
      switch (original_type)
    {
    case DB_TYPE_VARCHAR:
    case DB_TYPE_CHAR:
      /* read as DATETIMETZ */
      if (tp_atotimestamptz (src, &v_timestamptz) != NO_ERROR)
        {
          status = DOMAIN_ERROR;
          break;
        }
      else
        {
          db_make_timestampltz (target, v_timestamptz.timestamp);
        }
      break;

    case DB_TYPE_ENUMERATION:
      {
        DB_VALUE varchar_val;
        if (tp_enumeration_to_varchar (src, &varchar_val) != NO_ERROR)
          {
        status = DOMAIN_ERROR;
        break;
          }
        status =
          tp_value_cast_internal (&varchar_val, target, desired_domain, coercion_mode, do_domain_select, false);
        break;
      }

    case DB_TYPE_DATETIME:
    case DB_TYPE_DATE:
      {
        if (original_type == DB_TYPE_DATETIME)
          {
        v_datetime = *db_get_datetime (src);
        v_date = v_datetime.date;
        v_time = v_datetime.time / 1000;
          }
        else
          {
        assert (original_type == DB_TYPE_DATE);
        v_date = *db_get_date (src);
        v_time = 0;
          }

        if (db_timestamp_encode_ses (&v_date, &v_time, &v_utime, NULL) != NO_ERROR)
          {
        status = DOMAIN_OVERFLOW;
        break;
          }

        db_make_timestampltz (target, v_utime);
      }
      break;

    case DB_TYPE_DATETIMELTZ:
    case DB_TYPE_DATETIMETZ:
      if (original_type == DB_TYPE_DATETIMELTZ)
        {
          v_datetime = *db_get_datetime (src);
          v_date = v_datetime.date;
          v_time = v_datetime.time / 1000;
        }
      else
        {
          assert (original_type == DB_TYPE_DATETIMETZ);
          v_datetimetz = *db_get_datetimetz (src);
          v_date = v_datetimetz.datetime.date;
          v_time = v_datetimetz.datetime.time / 1000;
        }

      /* both values are in UTC */
      if (db_timestamp_encode_utc (&v_date, &v_time, &v_utime) == NO_ERROR)
        {
          db_make_timestampltz (target, v_utime);
        }
      else
        {
          status = DOMAIN_OVERFLOW;
        }
      break;

    case DB_TYPE_TIMESTAMP:
      /* original value stored in UTC, copy it */
      db_make_timestampltz (target, *db_get_timestamp (src));
      break;

    case DB_TYPE_TIMESTAMPTZ:
      v_timestamptz = *db_get_timestamptz (src);
      /* original value stored in UTC, copy it */
      db_make_timestampltz (target, v_timestamptz.timestamp);
      break;

    default:
      status = tp_value_coerce ((DB_VALUE *) src, target, &tp_Integer_domain);
      if (status == DOMAIN_COMPATIBLE)
        {
          int tmpint;
          tmpint = db_get_int (target);
          if (tmpint >= 0)
        {
          db_make_timestampltz (target, (DB_UTIME) tmpint);
        }
          else
        {
          status = DOMAIN_INCOMPATIBLE;
        }
        }
      break;
    }
      break;

    case DB_TYPE_DATETIME:
      switch (original_type)
    {
    case DB_TYPE_VARCHAR:
    case DB_TYPE_CHAR:
      if (tp_atoudatetime (src, &v_datetime) != NO_ERROR)
        {
          status = DOMAIN_ERROR;
        }
      else
        {
          db_make_datetime (target, &v_datetime);
        }
      break;

    case DB_TYPE_ENUMERATION:
      {
        DB_VALUE varchar_val;
        if (tp_enumeration_to_varchar (src, &varchar_val) != NO_ERROR)
          {
        status = DOMAIN_ERROR;
        break;
          }
        status =
          tp_value_cast_internal (&varchar_val, target, desired_domain, coercion_mode, do_domain_select, false);
        break;
      }

    case DB_TYPE_TIMESTAMP:
    case DB_TYPE_TIMESTAMPLTZ:
      v_utime = *db_get_timestamp (src);
      if (db_timestamp_decode_ses (&v_utime, &v_date, &v_time) != NO_ERROR)
        {
          status = DOMAIN_ERROR;
          break;
        }
      v_datetime.date = v_date;
      v_datetime.time = v_time * 1000;
      db_make_datetime (target, &v_datetime);
      break;

    case DB_TYPE_TIMESTAMPTZ:
      v_timestamptz = *db_get_timestamptz (src);
      if (db_timestamp_decode_w_tz_id (&v_timestamptz.timestamp, &v_timestamptz.tz_id, &v_date, &v_time) !=
          NO_ERROR)
        {
          status = DOMAIN_ERROR;
          break;
        }
      v_datetime.date = v_date;
      v_datetime.time = v_time * 1000;
      db_make_datetime (target, &v_datetime);
      break;

    case DB_TYPE_DATE:
      v_datetime.date = *db_get_date (src);
      v_datetime.time = 0;
      db_make_datetime (target, &v_datetime);
      break;

    case DB_TYPE_DATETIMELTZ:
      {
        DB_DATETIME utc_dt;

        /* DATETIMELTZ store in UTC, DATETIME in session TZ */
        utc_dt = *db_get_datetime (src);
        if (tz_datetimeltz_to_local (&utc_dt, &v_datetime) != NO_ERROR)
          {
        status = DOMAIN_ERROR;
        break;
          }

        db_make_datetime (target, &v_datetime);
      }
      break;

    case DB_TYPE_DATETIMETZ:
      /* DATETIMETZ store in UTC, DATETIME in session TZ */
      v_datetimetz = *db_get_datetimetz (src);
      if (tz_utc_datetimetz_to_local (&v_datetimetz.datetime, &v_datetimetz.tz_id, &v_datetime) == NO_ERROR)
        {
          db_make_datetime (target, &v_datetime);
        }
      else
        {
          status = DOMAIN_ERROR;
        }
      break;

    default:
      status = DOMAIN_INCOMPATIBLE;
      break;
    }
      break;

    case DB_TYPE_DATETIMELTZ:
      switch (original_type)
    {
    case DB_TYPE_VARCHAR:
    case DB_TYPE_CHAR:
      if (tp_atodatetimetz (src, &v_datetimetz) != NO_ERROR)
        {
          status = DOMAIN_ERROR;
        }
      else
        {
          db_make_datetimeltz (target, &v_datetimetz.datetime);
        }
      break;

    case DB_TYPE_ENUMERATION:
      {
        DB_VALUE varchar_val;
        if (tp_enumeration_to_varchar (src, &varchar_val) != NO_ERROR)
          {
        status = DOMAIN_ERROR;
        break;
          }
        status =
          tp_value_cast_internal (&varchar_val, target, desired_domain, coercion_mode, do_domain_select, false);
        break;
      }

    case DB_TYPE_TIMESTAMP:
    case DB_TYPE_TIMESTAMPLTZ:
      v_utime = *db_get_timestamp (src);

      (void) db_timestamp_decode_utc (&v_utime, &v_date, &v_time);
      v_datetime.time = v_time * 1000;
      v_datetime.date = v_date;
      db_make_datetimeltz (target, &v_datetime);
      break;

    case DB_TYPE_TIMESTAMPTZ:
      v_timestamptz = *db_get_timestamptz (src);
      (void) db_timestamp_decode_utc (&v_timestamptz.timestamp, &v_date, &v_time);
      v_datetime.time = v_time * 1000;
      v_datetime.date = v_date;
      db_make_datetimeltz (target, &v_datetime);
      break;

    case DB_TYPE_DATE:
    case DB_TYPE_DATETIME:
      if (original_type == DB_TYPE_DATE)
        {
          v_datetime.date = *db_get_date (src);
          v_datetime.time = 0;
        }
      else
        {
          v_datetime = *db_get_datetime (src);
        }

      if (tz_create_datetimetz_from_ses (&v_datetime, &v_datetimetz) != NO_ERROR)
        {
          status = DOMAIN_ERROR;
          break;
        }
      db_make_datetimeltz (target, &v_datetimetz.datetime);
      break;

    case DB_TYPE_DATETIMETZ:
      /* copy (UTC) */
      v_datetimetz = *db_get_datetimetz (src);
      db_make_datetimeltz (target, &v_datetimetz.datetime);
      break;

    case DB_TYPE_TIME:
      status = DOMAIN_INCOMPATIBLE;
      break;

    default:
      status = DOMAIN_INCOMPATIBLE;
      break;
    }
      break;

    case DB_TYPE_DATETIMETZ:
      switch (original_type)
    {
    case DB_TYPE_VARCHAR:
    case DB_TYPE_CHAR:
      {
        if (tp_atodatetimetz (src, &v_datetimetz) != NO_ERROR)
          {
        status = DOMAIN_ERROR;
        break;
          }

        db_make_datetimetz (target, &v_datetimetz);
      }

      break;

    case DB_TYPE_ENUMERATION:
      {
        DB_VALUE varchar_val;
        if (tp_enumeration_to_varchar (src, &varchar_val) != NO_ERROR)
          {
        status = DOMAIN_ERROR;
        break;
          }
        status =
          tp_value_cast_internal (&varchar_val, target, desired_domain, coercion_mode, do_domain_select, false);
        break;
      }

    case DB_TYPE_TIMESTAMP:
    case DB_TYPE_TIMESTAMPLTZ:
      {
        v_utime = *db_get_timestamp (src);
        db_timestamp_decode_utc (&v_utime, &v_date, &v_time);
        v_datetimetz.datetime.time = v_time * 1000;
        v_datetimetz.datetime.date = v_date;

        if (tz_create_session_tzid_for_datetime (&v_datetimetz.datetime, true, &v_datetimetz.tz_id) != NO_ERROR)
          {
        status = DOMAIN_ERROR;
        break;
          }

        db_make_datetimetz (target, &v_datetimetz);
      }
      break;

    case DB_TYPE_TIMESTAMPTZ:
      v_timestamptz = *db_get_timestamptz (src);
      (void) db_timestamp_decode_utc (&v_timestamptz.timestamp, &v_date, &v_time);
      v_datetimetz.datetime.time = v_time * 1000;
      v_datetimetz.datetime.date = v_date;
      v_datetimetz.tz_id = v_timestamptz.tz_id;
      db_make_datetimetz (target, &v_datetimetz);
      break;

    case DB_TYPE_DATE:
      v_datetime.date = *db_get_date (src);
      v_datetime.time = 0;

      if (tz_create_datetimetz_from_ses (&v_datetime, &v_datetimetz) != NO_ERROR)
        {
          status = DOMAIN_ERROR;
          break;
        }
      db_make_datetimetz (target, &v_datetimetz);
      break;

    case DB_TYPE_DATETIME:
      if (tz_create_datetimetz_from_ses (db_get_datetime (src), &v_datetimetz) != NO_ERROR)
        {
          status = DOMAIN_ERROR;
          break;
        }
      db_make_datetimetz (target, &v_datetimetz);
      break;

    case DB_TYPE_DATETIMELTZ:
      v_datetimetz.datetime = *db_get_datetime (src);
      if (tz_create_session_tzid_for_datetime (&v_datetimetz.datetime, true, &v_datetimetz.tz_id) != NO_ERROR)
        {
          status = DOMAIN_ERROR;
          break;
        }
      db_make_datetimetz (target, &v_datetimetz);
      break;

    case DB_TYPE_TIME:
      status = DOMAIN_INCOMPATIBLE;
      break;

    default:
      status = DOMAIN_INCOMPATIBLE;
      break;
    }
      break;

    case DB_TYPE_DATE:
      switch (original_type)
    {
    case DB_TYPE_VARCHAR:
    case DB_TYPE_CHAR:
      if (tp_atodate (src, &v_date) == NO_ERROR)
        {
          db_date_decode (&v_date, &month, &day, &year);
        }
      else
        {
          status = DOMAIN_ERROR;
          break;
        }

      if (db_make_date (target, month, day, year) != NO_ERROR)
        {
          status = DOMAIN_ERROR;
        }
      break;

    case DB_TYPE_ENUMERATION:
      {
        DB_VALUE varchar_val;
        if (tp_enumeration_to_varchar (src, &varchar_val) != NO_ERROR)
          {
        status = DOMAIN_ERROR;
        break;
          }
        status =
          tp_value_cast_internal (&varchar_val, target, desired_domain, coercion_mode, do_domain_select, false);
        break;
      }

    case DB_TYPE_TIMESTAMP:
    case DB_TYPE_TIMESTAMPLTZ:
      (void) db_timestamp_decode_ses (db_get_timestamp (src), &v_date, NULL);
      db_date_decode (&v_date, &month, &day, &year);
      db_make_date (target, month, day, year);
      break;

    case DB_TYPE_TIMESTAMPTZ:
      v_timestamptz = *db_get_timestamptz (src);
      if (db_timestamp_decode_w_tz_id (&v_timestamptz.timestamp, &v_timestamptz.tz_id, &v_date, NULL) != NO_ERROR)
        {
          status = DOMAIN_ERROR;
          break;
        }
      db_date_decode (&v_date, &month, &day, &year);
      db_make_date (target, month, day, year);
      break;

    case DB_TYPE_DATETIME:
      db_datetime_decode ((DB_DATETIME *) db_get_datetime (src), &month, &day, &year, &hour, &minute, &second,
                  &millisecond);
      db_make_date (target, month, day, year);
      break;

    case DB_TYPE_DATETIMELTZ:
    case DB_TYPE_DATETIMETZ:
      {
        DB_DATETIME *utc_dt_p;
        DB_DATETIMETZ *dt_tz_p;
        TZ_ID tz_id;

        /* DATETIMELTZ and DATETIMETZ store in UTC, convert to session */
        if (original_type == DB_TYPE_DATETIMELTZ)
          {
        utc_dt_p = db_get_datetime (src);
        if (tz_create_session_tzid_for_datetime (utc_dt_p, true, &tz_id) != NO_ERROR)
          {
            status = DOMAIN_ERROR;
            break;
          }
          }
        else
          {
        dt_tz_p = db_get_datetimetz (src);
        utc_dt_p = &dt_tz_p->datetime;
        tz_id = dt_tz_p->tz_id;
          }

        if (tz_utc_datetimetz_to_local (utc_dt_p, &tz_id, &v_datetime) != NO_ERROR)
          {
        status = DOMAIN_ERROR;
        break;
          }

        db_datetime_decode (&v_datetime, &month, &day, &year, &hour, &minute, &second, &millisecond);

        db_make_date (target, month, day, year);
        break;
      }

    default:
      status = DOMAIN_INCOMPATIBLE;
      break;
    }
      break;

    case DB_TYPE_TIME:
      switch (original_type)
    {
    case DB_TYPE_TIMESTAMP:
    case DB_TYPE_TIMESTAMPLTZ:
      if (db_timestamp_decode_ses (db_get_timestamp (src), NULL, &v_time) != NO_ERROR)
        {
          status = DOMAIN_ERROR;
          break;
        }
      db_value_put_encoded_time (target, &v_time);
      break;

    case DB_TYPE_TIMESTAMPTZ:
      /* convert TS from UTC to value TZ */
      v_timestamptz = *db_get_timestamptz (src);
      if (db_timestamp_decode_w_tz_id (&v_timestamptz.timestamp, &v_timestamptz.tz_id, NULL, &v_time) != NO_ERROR)
        {
          status = DOMAIN_ERROR;
          break;
        }
      db_value_put_encoded_time (target, &v_time);
      break;
    case DB_TYPE_DATETIME:
      db_datetime_decode ((DB_DATETIME *) db_get_datetime (src), &month, &day, &year, &hour, &minute, &second,
                  &millisecond);
      db_make_time (target, hour, minute, second);
      break;
    case DB_TYPE_DATETIMELTZ:
      {
        DB_DATETIME dt_local;

        v_datetime = *db_get_datetime (src);

        if (tz_datetimeltz_to_local (&v_datetime, &dt_local) != NO_ERROR)
          {
        status = DOMAIN_ERROR;
        break;
          }

        db_datetime_decode (&dt_local, &month, &day, &year, &hour, &minute, &second, &millisecond);
        db_make_time (target, hour, minute, second);
        break;
      }
    case DB_TYPE_DATETIMETZ:
      {
        DB_DATETIME dt_local;

        v_datetimetz = *db_get_datetimetz (src);
        if (tz_utc_datetimetz_to_local (&v_datetimetz.datetime, &v_datetimetz.tz_id, &dt_local) != NO_ERROR)
          {
        status = DOMAIN_ERROR;
        break;
          }
        db_datetime_decode (&dt_local, &month, &day, &year, &hour, &minute, &second, &millisecond);
        db_make_time (target, hour, minute, second);
        break;
      }
    case DB_TYPE_SHORT:
      v_time = db_get_short (src) % SECONDS_IN_A_DAY;
      db_time_decode (&v_time, &hour, &minute, &second);
      db_make_time (target, hour, minute, second);
      break;
    case DB_TYPE_INTEGER:
      v_time = db_get_int (src) % SECONDS_IN_A_DAY;
      db_time_decode (&v_time, &hour, &minute, &second);
      db_make_time (target, hour, minute, second);
      break;
    case DB_TYPE_BIGINT:
      v_time = db_get_bigint (src) % SECONDS_IN_A_DAY;
      db_time_decode (&v_time, &hour, &minute, &second);
      db_make_time (target, hour, minute, second);
      break;
    case DB_TYPE_MONETARY:
      v_money = db_get_monetary (src);
      if (OR_CHECK_INT_OVERFLOW (v_money->amount))
        {
          status = DOMAIN_OVERFLOW;
        }
      else
        {
          v_time = (int) ROUND (v_money->amount) % SECONDS_IN_A_DAY;
          db_time_decode (&v_time, &hour, &minute, &second);
          db_make_time (target, hour, minute, second);
        }
      break;
    case DB_TYPE_FLOAT:
      {
        float ftmp = db_get_float (src);
        if (OR_CHECK_INT_OVERFLOW (ftmp))
          {
        status = DOMAIN_OVERFLOW;
          }
        else
          {
        v_time = ((int) ROUND (ftmp)) % SECONDS_IN_A_DAY;
        db_time_decode (&v_time, &hour, &minute, &second);
        db_make_time (target, hour, minute, second);
          }
        break;
      }
    case DB_TYPE_DOUBLE:
      {
        double dtmp = db_get_double (src);
        if (OR_CHECK_INT_OVERFLOW (dtmp))
          {
        status = DOMAIN_OVERFLOW;
          }
        else
          {
        v_time = ((int) ROUND (dtmp)) % SECONDS_IN_A_DAY;
        db_time_decode (&v_time, &hour, &minute, &second);
        db_make_time (target, hour, minute, second);
          }
        break;
      }
    case DB_TYPE_VARCHAR:
    case DB_TYPE_CHAR:
      if (tp_atotime (src, &v_time) == NO_ERROR)
        {
          db_time_decode (&v_time, &hour, &minute, &second);
        }
      else
        {
          status = DOMAIN_ERROR;
          break;
        }

      if (db_make_time (target, hour, minute, second) != NO_ERROR)
        {
          status = DOMAIN_ERROR;
        }
      break;
    case DB_TYPE_ENUMERATION:
      {
        DB_VALUE varchar_val;
        if (tp_enumeration_to_varchar (src, &varchar_val) != NO_ERROR)
          {
        status = DOMAIN_ERROR;
        break;
          }
        status =
          tp_value_cast_internal (&varchar_val, target, desired_domain, coercion_mode, do_domain_select, false);
        break;
      }
    default:
      status = DOMAIN_INCOMPATIBLE;
      break;
    }
      break;

#if !defined (SERVER_MODE)
    case DB_TYPE_OBJECT:
      {
    DB_OBJECT *v_obj = NULL;
    int is_vclass = 0;

    /* Make sure the domains are compatible.  Coerce view objects to real objects. */
    switch (original_type)
      {
      case DB_TYPE_OBJECT:
        if (!sm_coerce_object_domain ((TP_DOMAIN *) desired_domain, db_get_object (src), &v_obj))
          {
        status = DOMAIN_INCOMPATIBLE;
          }
        break;
      case DB_TYPE_POINTER:
        if (!sm_check_class_domain ((TP_DOMAIN *) desired_domain, ((DB_OTMPL *) db_get_pointer (src))->classobj))
          {
        status = DOMAIN_INCOMPATIBLE;
        break;
          }
        db_make_pointer (target, db_get_pointer (src));
        break;
      case DB_TYPE_OID:
        vid_oid_to_object (src, &v_obj);
        break;

      case DB_TYPE_VOBJ:
        vid_vobj_to_object (src, &v_obj);
        is_vclass = db_is_vclass (desired_domain->class_mop);
        if (is_vclass < 0)
          {
        status = DOMAIN_ERROR;
        break;
          }
        if (!is_vclass)
          {
        v_obj = db_real_instance (v_obj);
          }
        break;

      default:
        status = DOMAIN_INCOMPATIBLE;
      }
    if (original_type != DB_TYPE_POINTER)
      {
        /* check we got an object in a proper class */
        if (v_obj && desired_domain->class_mop)
          {
        DB_OBJECT *obj_class;

        obj_class = db_get_class (v_obj);
        if (obj_class == desired_domain->class_mop)
          {
            /* everything is fine */
          }
        else if (db_is_subclass (obj_class, desired_domain->class_mop) > 0)
          {
            /* everything is also ok */
          }
        else
          {
            is_vclass = db_is_vclass (desired_domain->class_mop);
            if (is_vclass < 0)
              {
            status = DOMAIN_ERROR;
            break;
              }
            if (is_vclass)
              {
            /*
             * This should still be an error, and the above
             * code should have constructed a virtual mop.
             * I'm not sure the rest of the code is consistent
             * in this regard.
             */
              }
            else
              {
            status = DOMAIN_INCOMPATIBLE;
              }
          }
          }
        db_make_object (target, v_obj);
      }
      }
      break;
#endif /* !SERVER_MODE */

    case DB_TYPE_SET:
    case DB_TYPE_MULTISET:
    case DB_TYPE_SEQUENCE:
      if (!TP_IS_SET_TYPE (original_type))
    {
      status = DOMAIN_INCOMPATIBLE;
    }
      else
    {
      SETREF *setref;

      setref = db_get_set (src);
      if (setref)
        {
          TP_DOMAIN *set_domain;

          set_domain = setobj_domain (setref->set);
          if (src == dest && tp_domain_compatible (set_domain, desired_domain))
        {
          /*
           * We know that this is a "coerce-in-place" operation, and
           * we know that no coercion is necessary, so do nothing: we
           * can use the exact same set without any conversion.
           * Setting "src" to NULL prevents the wrapup code from
           * clearing the set; that's important since we haven't made
           * a copy.
           */
          setobj_put_domain (setref->set, (TP_DOMAIN *) desired_domain);
          src = NULL;
        }
          else
        {
          if (tp_domain_compatible (set_domain, desired_domain))
            {
              /*
               * Well, we can't use the exact same set, but we don't
               * have to do the whole hairy coerce thing either: we
               * can just make a copy and then take the more general
               * domain.  setobj_put_domain() guards against null
               * pointers, there's no need to check first.
               */
              setref = set_copy (setref);
              if (setref)
            {
              setobj_put_domain (setref->set, (TP_DOMAIN *) desired_domain);
            }
            }
          else
            {
              /*
               * Well, now we have to use the whole hairy coercion
               * thing.  Too bad...
               *
               * This case will crop up when someone tries to cast a
               * "set of int" as a "set of float", for example.
               */
              setref =
            set_coerce (setref, (TP_DOMAIN *) desired_domain, (coercion_mode == TP_IMPLICIT_COERCION));
            }

          if (setref == NULL)
            {
              assert (er_errid () != NO_ERROR);
              err = er_errid ();
            }
          else if (desired_type == DB_TYPE_SET)
            {
              err = db_make_set (target, setref);
            }
          else if (desired_type == DB_TYPE_MULTISET)
            {
              err = db_make_multiset (target, setref);
            }
          else
            {
              err = db_make_sequence (target, setref);
            }
        }
          if (!setref || err < 0)
        {
          status = DOMAIN_INCOMPATIBLE;
        }
        }
    }
      break;

    case DB_TYPE_VOBJ:
      if (original_type == DB_TYPE_VOBJ)
    {
      SETREF *setref;
      /*
       * We should try and convert the view of the src to match
       * the view of the desired_domain. However, the desired
       * domain generally does not contain this information.
       * We will detect domain incompatibly later on assignment,
       * so we treat casting any DB_TYPE_VOBJ to DB_TYPE_VOBJ
       * as success.
       */
      status = DOMAIN_COMPATIBLE;
      setref = db_get_set (src);
      if (src != dest || !setref)
        {
          pr_clone_value ((DB_VALUE *) src, target);
        }
      else
        {
          /*
           * this is a "coerce-in-place", and no coercion is necessary,
           * so do nothing: use the same vobj without any conversion. set
           * "src" to NULL to prevent the wrapup code from clearing dest.
           */
          setobj_put_domain (setref->set, (TP_DOMAIN *) desired_domain);
          src = NULL;
        }
    }
      else
#if !defined (SERVER_MODE)
      if (original_type == DB_TYPE_OBJECT)
    {
      if (vid_object_to_vobj (db_get_object (src), target) < 0)
        {
          status = DOMAIN_INCOMPATIBLE;
        }
      else
        {
          status = DOMAIN_COMPATIBLE;
        }
      break;
    }
      else
#endif /* !SERVER_MODE */
      if (original_type == DB_TYPE_OID || original_type == DB_TYPE_OBJECT)
    {
      DB_VALUE view_oid;
      DB_VALUE class_oid;
      DB_VALUE keys;
      OID nulloid;
      DB_SEQ *seq;

      OID_SET_NULL (&nulloid);
      db_make_oid (&class_oid, &nulloid);
      db_make_oid (&view_oid, &nulloid);
      seq = db_seq_create (NULL, NULL, 3);
      keys = *src;

      /*
       * if we are on the server, and get a DB_TYPE_OBJECT,
       * then its only possible representation is a DB_TYPE_OID,
       * and it may be treated that way. However, this should
       * not really be a case that can happen. It may still
       * for historical reasons, so is not falgged as an error.
       * On the client, a worskapce based scheme must be used,
       * which is just above in a conditional compiled section.
       */

      if ((db_seq_put (seq, 0, &view_oid) != NO_ERROR) || (db_seq_put (seq, 1, &class_oid) != NO_ERROR)
          || (db_seq_put (seq, 2, &keys) != NO_ERROR))
        {
          status = DOMAIN_INCOMPATIBLE;
        }
      else
        {
          db_make_sequence (target, seq);
          db_value_alter_type (target, DB_TYPE_VOBJ);
          status = DOMAIN_COMPATIBLE;
        }
    }
      else
    {
      status = DOMAIN_INCOMPATIBLE;
    }
      break;

    case DB_TYPE_BIT:
    case DB_TYPE_VARBIT:
      switch (original_type)
    {
    case DB_TYPE_VARCHAR:
    case DB_TYPE_CHAR:
      {
        DB_VALUE temp;
        char *bit_char_string;
        int src_size = db_get_string_size (src);
        int dst_size = (src_size + 1) / 2;

        bit_char_string = (char *) db_private_alloc (NULL, dst_size + 1);
        if (bit_char_string)
          {
        if (qstr_hex_to_bin (bit_char_string, dst_size, db_get_string (src), src_size) != src_size)
          {
            status = DOMAIN_ERROR;
            db_private_free_and_init (NULL, bit_char_string);
          }
        else
          {
            db_make_bit (&temp, TP_FLOATING_PRECISION_VALUE, bit_char_string, src_size * 4);
            temp.need_clear = true;
            if (db_bit_string_coerce (&temp, target, &data_stat) != NO_ERROR)
              {
            status = DOMAIN_INCOMPATIBLE;
              }
            else if (data_stat == DATA_STATUS_TRUNCATED && coercion_mode != TP_FORCE_COERCION &&
                 (prm_get_bool_value (PRM_ID_ALLOW_TRUNCATED_STRING) == false
                  || coercion_mode == TP_IMPLICIT_COERCION))
              {
            status = DOMAIN_OVERFLOW;
            pr_clear_value (target);
              }
            else
              {
            status = DOMAIN_COMPATIBLE;
              }
            pr_clear_value (&temp);
          }
          }
        else
          {
        /* Couldn't allocate space for bit_char_string */
        status = DOMAIN_INCOMPATIBLE;
          }
      }
      break;

    case DB_TYPE_ENUMERATION:
      {
        DB_VALUE varchar_val;

        if (tp_enumeration_to_varchar (src, &varchar_val) != NO_ERROR)
          {
        status = DOMAIN_ERROR;
        break;
          }
        status =
          tp_value_cast_internal (&varchar_val, target, desired_domain, coercion_mode, do_domain_select, false);
      }
      break;

    case DB_TYPE_BLOB:
      {
        DB_VALUE tmpval;

        db_make_null (&tmpval);

        err = db_blob_to_bit (src, NULL, &tmpval);
        if (err == NO_ERROR)
          {
        err = tp_value_cast_internal (&tmpval, target, desired_domain, coercion_mode, do_domain_select, false);
          }
        (void) pr_clear_value (&tmpval);
      }
      break;

    default:
      if (src == dest && tp_can_steal_string (src, desired_domain))
        {
          tp_value_slam_domain (dest, desired_domain);
          /*
           * Set "src" to NULL to prevent the wrapup code from undoing
           * our work; since we haven't actually made a copy, we don't
           * want to clear the original.
           */
          src = NULL;
        }
      else if (db_bit_string_coerce (src, target, &data_stat) != NO_ERROR)
        {
          status = DOMAIN_INCOMPATIBLE;
        }
      else if (data_stat == DATA_STATUS_TRUNCATED && coercion_mode != TP_FORCE_COERCION &&
           (prm_get_bool_value (PRM_ID_ALLOW_TRUNCATED_STRING) == false
            || coercion_mode == TP_IMPLICIT_COERCION))
        {
          status = DOMAIN_OVERFLOW;
          pr_clear_value (target);
        }
      else
        {
          status = DOMAIN_COMPATIBLE;
        }
      break;
    }
      break;

    case DB_TYPE_VARCHAR:
    case DB_TYPE_CHAR:
      switch (original_type)
    {
    case DB_TYPE_VARCHAR:
    case DB_TYPE_CHAR:
      if (src == dest && tp_can_steal_string (src, desired_domain))
        {
          tp_value_slam_domain (dest, desired_domain);
          /*
           * Set "src" to NULL to prevent the wrapup code from undoing
           * our work; since we haven't actually made a copy, we don't
           * want to clear the original.
           */
          src = NULL;
        }
      else if (db_char_string_coerce (src, target, &data_stat) != NO_ERROR)
        {
          status = DOMAIN_INCOMPATIBLE;
        }
      else if (data_stat == DATA_STATUS_TRUNCATED && coercion_mode != TP_FORCE_COERCION &&
           (prm_get_bool_value (PRM_ID_ALLOW_TRUNCATED_STRING) == false
            || coercion_mode == TP_IMPLICIT_COERCION))
        {
          status = DOMAIN_OVERFLOW;
          pr_clear_value (target);
        }
      else if (desired_domain->collation_flag != TP_DOMAIN_COLL_LEAVE)
        {
          db_string_put_cs_and_collation (target, TP_DOMAIN_CODESET (desired_domain),
                          TP_DOMAIN_COLLATION (desired_domain));
          status = DOMAIN_COMPATIBLE;
        }
      break;

    case DB_TYPE_ENUMERATION:
      {
        DB_VALUE varchar_val;

        if (tp_enumeration_to_varchar (src, &varchar_val) != NO_ERROR)
          {
        status = DOMAIN_ERROR;
          }
        else
          {
        status =
          tp_value_cast_internal (&varchar_val, target, desired_domain, coercion_mode, do_domain_select, false);
          }
      }
      break;

    case DB_TYPE_BIGINT:
    case DB_TYPE_INTEGER:
    case DB_TYPE_SMALLINT:
      {
        int max_size = TP_BIGINT_PRECISION + 2 + 1;
        char *new_string;
        DB_BIGINT num;

        new_string = (char *) db_private_alloc (NULL, max_size);
        if (!new_string)
          {
        status = DOMAIN_ERROR;
        break;
          }

        if (original_type == DB_TYPE_BIGINT)
          {
        num = db_get_bigint (src);
          }
        else if (original_type == DB_TYPE_INTEGER)
          {
        num = (DB_BIGINT) db_get_int (src);
          }
        else        /* DB_TYPE_SHORT */
          {
        num = (DB_BIGINT) db_get_short (src);
          }

        if (tp_ltoa (num, new_string, 10))
          {
        if (db_value_precision (target) != TP_FLOATING_PRECISION_VALUE
            && db_value_precision (target) < (int) strlen (new_string))
          {
            status = DOMAIN_OVERFLOW;
            db_private_free_and_init (NULL, new_string);
          }
        else
          {
            make_desired_string_db_value (desired_type, desired_domain, new_string, target, &status,
                          &data_stat);
          }
          }
        else
          {
        status = DOMAIN_ERROR;
        db_private_free_and_init (NULL, new_string);
          }
      }
      break;

    case DB_TYPE_DOUBLE:
    case DB_TYPE_FLOAT:
      {
        if (original_type == DB_TYPE_FLOAT)
          {
        tp_ftoa (src, target);
          }
        else
          {
        tp_dtoa (src, target);
          }

        if (DB_IS_NULL (target))
          {
        if (er_errid () == ER_OUT_OF_VIRTUAL_MEMORY)
          {
            /* no way to report "out of memory" from tp_value_cast_internal() ?? */
            status = DOMAIN_ERROR;
          }
        else
          {
            status = DOMAIN_INCOMPATIBLE;
          }
          }
        else if (DB_VALUE_PRECISION (target) != TP_FLOATING_PRECISION_VALUE
             && (db_get_string_length (target) > DB_VALUE_PRECISION (target)))
          {
        status = DOMAIN_OVERFLOW;
        pr_clear_value (target);
          }
      }
      break;

    case DB_TYPE_NUMERIC:
      {
        char str_buf[NUMERIC_MAX_STRING_SIZE];
        char *new_string;
        int max_size;

        numeric_db_value_print (src, str_buf);

        max_size = strlen (str_buf) + 1;
        new_string = (char *) db_private_alloc (NULL, max_size);
        if (new_string == NULL)
          {
        status = DOMAIN_ERROR;
        break;
          }

        strcpy (new_string, str_buf);

        if (db_value_precision (target) != TP_FLOATING_PRECISION_VALUE
        && db_value_precision (target) < max_size - 1)
          {
        status = DOMAIN_OVERFLOW;
        db_private_free_and_init (NULL, new_string);
          }
        else
          {
        make_desired_string_db_value (desired_type, desired_domain, new_string, target, &status, &data_stat);
          }
      }
      break;

    case DB_TYPE_MONETARY:
      {
        /* monetary symbol = 3 sign = 1 dot = 1 fraction digits = 2 NUL terminator = 1 */
        int max_size = DBL_MAX_DIGITS + 3 + 1 + 1 + 2 + 1;
        char *new_string;
        char *p;

        new_string = (char *) db_private_alloc (NULL, max_size);
        if (!new_string)
          {
        status = DOMAIN_ERROR;
        break;
          }

        snprintf (new_string, max_size - 1, "%s%.*f", lang_currency_symbol (db_get_monetary (src)->type), 2,
              db_get_monetary (src)->amount);
        new_string[max_size - 1] = '\0';

        p = new_string + strlen (new_string);
        for (--p; p >= new_string && *p == '0'; p--)
          {         /* remove trailing zeros */
        *p = '\0';
          }
        if (*p == '.')  /* remove point */
          {
        *p = '\0';
          }

        if (db_value_precision (target) != TP_FLOATING_PRECISION_VALUE
        && db_value_precision (target) < (int) strlen (new_string))
          {
        status = DOMAIN_OVERFLOW;
        db_private_free_and_init (NULL, new_string);
          }
        else
          {
        make_desired_string_db_value (desired_type, desired_domain, new_string, target, &status, &data_stat);
          }
      }
      break;

    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:
      {
        int max_size = DATETIMETZ_BUF_SIZE;
        char *new_string;

        new_string = (char *) db_private_alloc (NULL, max_size);
        if (!new_string)
          {
        status = DOMAIN_ERROR;
        break;
          }

        err = NO_ERROR;

        switch (original_type)
          {
          case DB_TYPE_DATE:
        db_date_to_string (new_string, max_size, (DB_DATE *) db_get_date (src));
        break;
          case DB_TYPE_TIME:
        db_time_to_string (new_string, max_size, (DB_TIME *) db_get_time (src));
        break;
          case DB_TYPE_TIMESTAMP:
        db_timestamp_to_string (new_string, max_size, (DB_TIMESTAMP *) db_get_timestamp (src));
        break;
          case DB_TYPE_TIMESTAMPLTZ:
        v_utime = *db_get_timestamp (src);
        err = tz_create_session_tzid_for_timestamp (&v_utime, &ses_tz_id);
        if (err != NO_ERROR)
          {
            break;
          }
        db_timestamptz_to_string (new_string, max_size, &v_utime, &ses_tz_id);
        break;
          case DB_TYPE_TIMESTAMPTZ:
        v_timestamptz = *db_get_timestamptz (src);
        db_timestamptz_to_string (new_string, max_size, &v_timestamptz.timestamp, &v_timestamptz.tz_id);
        break;
          case DB_TYPE_DATETIMELTZ:
        v_datetime = *db_get_datetime (src);
        err = tz_create_session_tzid_for_datetime (&v_datetime, true, &ses_tz_id);
        if (err != NO_ERROR)
          {
            break;
          }
        db_datetimetz_to_string (new_string, max_size, &v_datetime, &ses_tz_id);
        break;
          case DB_TYPE_DATETIMETZ:
        v_datetimetz = *db_get_datetimetz (src);
        db_datetimetz_to_string (new_string, max_size, &v_datetimetz.datetime, &v_datetimetz.tz_id);
        break;
          case DB_TYPE_DATETIME:
          default:
        db_datetime_to_string (new_string, max_size, (DB_DATETIME *) db_get_datetime (src));
        break;
          }

        if (err != NO_ERROR)
          {
        status = DOMAIN_ERROR;
        break;
          }

        if (db_value_precision (target) != TP_FLOATING_PRECISION_VALUE
        && db_value_precision (target) < (int) strlen (new_string))
          {
        status = DOMAIN_OVERFLOW;
        db_private_free_and_init (NULL, new_string);
          }
        else
          {
        make_desired_string_db_value (desired_type, desired_domain, new_string, target, &status, &data_stat);
          }
      }
      break;

    case DB_TYPE_BIT:
    case DB_TYPE_VARBIT:
      {
        int max_size;
        char *new_string;
        int convert_error;

        max_size = ((db_get_string_length (src) + 3) / 4) + 1;
        new_string = (char *) db_private_alloc (NULL, max_size);
        if (!new_string)
          {
        status = DOMAIN_ERROR;
        break;
          }

        convert_error = bfmt_print (1 /* BIT_STRING_HEX */ , src,
                    new_string, max_size);

        if (convert_error == NO_ERROR)
          {
        if (db_value_precision (target) != TP_FLOATING_PRECISION_VALUE
            && (db_value_precision (target) < (int) strlen (new_string)))
          {
            status = DOMAIN_OVERFLOW;
            db_private_free_and_init (NULL, new_string);
          }
        else
          {
            make_desired_string_db_value (desired_type, desired_domain, new_string, target, &status,
                          &data_stat);
          }
          }
        else if (convert_error == -1)
          {
        status = DOMAIN_OVERFLOW;
        db_private_free_and_init (NULL, new_string);
          }
        else
          {
        status = DOMAIN_ERROR;
        db_private_free_and_init (NULL, new_string);
          }
      }
      break;

    case DB_TYPE_CLOB:
      {
        DB_VALUE tmpval;
        DB_VALUE cs;

        db_make_null (&tmpval);
        /* convert directly from CLOB into charset of desired domain string */
        db_make_int (&cs, desired_domain->codeset);
        err = db_clob_to_char (src, &cs, &tmpval);
        if (err == NO_ERROR)
          {
        err = tp_value_cast_internal (&tmpval, dest, desired_domain, coercion_mode, do_domain_select, false);
          }

        pr_clear_value (&tmpval);
      }
      break;

    case DB_TYPE_JSON:
      {
        char *json_str;
        int len;

        json_str = db_json_get_raw_json_body_from_document (db_get_json_document (src));
        len = strlen (json_str);

        if (db_value_precision (target) != TP_FLOATING_PRECISION_VALUE && db_value_precision (target) < len)
          {
        status = DOMAIN_OVERFLOW;
        db_private_free_and_init (NULL, json_str);
          }
        else
          {
        make_desired_string_db_value (desired_type, desired_domain, json_str, target, &status, &data_stat);
        target->need_clear = true;
          }
      }
      break;

    default:
      status = DOMAIN_INCOMPATIBLE;
      break;
    }
      break;

    case DB_TYPE_BLOB:
      switch (original_type)
    {
    case DB_TYPE_BLOB:
      err = db_value_clone ((DB_VALUE *) src, target);
      break;
    case DB_TYPE_BIT:
    case DB_TYPE_VARBIT:
      err = db_bit_to_blob (src, target);
      break;
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      err = db_char_to_blob (src, target);
      break;
    case DB_TYPE_ENUMERATION:
      {
        DB_VALUE varchar_val;

        if (tp_enumeration_to_varchar (src, &varchar_val) != NO_ERROR)
          {
        status = DOMAIN_ERROR;
        break;
          }
        status =
          tp_value_cast_internal (&varchar_val, target, desired_domain, coercion_mode, do_domain_select, false);
      }
      break;
    default:
      status = DOMAIN_INCOMPATIBLE;
      break;
    }
      break;

    case DB_TYPE_CLOB:
      switch (original_type)
    {
    case DB_TYPE_CLOB:
      err = db_value_clone ((DB_VALUE *) src, target);
      break;
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      err = db_char_to_clob (src, target);
      break;
    case DB_TYPE_ENUMERATION:
      {
        DB_VALUE varchar_val;
        if (tp_enumeration_to_varchar (src, &varchar_val) != NO_ERROR)
          {
        status = DOMAIN_ERROR;
        break;
          }
        status =
          tp_value_cast_internal (&varchar_val, target, desired_domain, coercion_mode, do_domain_select, false);
        break;
      }
    default:
      status = DOMAIN_INCOMPATIBLE;
      break;
    }
      break;

    case DB_TYPE_ENUMERATION:
      {
    unsigned short val_idx = 0;
    int val_str_size = 0;
    const char *val_str = NULL;
    bool exit = false, alloc_string = true;
    DB_VALUE conv_val;

    db_make_null (&conv_val);

    if (src->domain.general_info.is_null)
      {
        db_make_null (target);
        break;
      }

    switch (original_type)
      {
      case DB_TYPE_SHORT:
        val_idx = (unsigned short) db_get_short (src);
        break;
      case DB_TYPE_INTEGER:
        if (OR_CHECK_USHRT_OVERFLOW (db_get_int (src)))
          {
        status = DOMAIN_INCOMPATIBLE;
          }
        else
          {
        val_idx = (unsigned short) db_get_int (src);
          }
        break;
      case DB_TYPE_BIGINT:
        if (OR_CHECK_USHRT_OVERFLOW (db_get_bigint (src)))
          {
        status = DOMAIN_INCOMPATIBLE;
          }
        else
          {
        val_idx = (unsigned short) db_get_bigint (src);
          }
        break;
      case DB_TYPE_FLOAT:
        if (OR_CHECK_USHRT_OVERFLOW (floor (db_get_float (src))))
          {
        status = DOMAIN_INCOMPATIBLE;
          }
        else
          {
        val_idx = (unsigned short) floor (db_get_float (src));
          }
        break;
      case DB_TYPE_DOUBLE:
        if (OR_CHECK_USHRT_OVERFLOW (floor (db_get_double (src))))
          {
        status = DOMAIN_INCOMPATIBLE;
          }
        else
          {
        val_idx = (unsigned short) floor (db_get_double (src));
          }
        break;
      case DB_TYPE_NUMERIC:
        {
          DB_VALUE val;
          DB_DATA_STATUS stat = DATA_STATUS_OK;

          db_make_double (&val, 0);
          err = numeric_db_value_coerce_from_num ((DB_VALUE *) src, &val, &stat);
          if (err != NO_ERROR)
        {
          status = DOMAIN_ERROR;
        }
          else
        {
          if (OR_CHECK_USHRT_OVERFLOW (floor (db_get_double (&val))))
            {
              status = DOMAIN_INCOMPATIBLE;
            }
          else
            {
              val_idx = (unsigned short) floor (db_get_double (&val));
            }
        }
          break;
        }
      case DB_TYPE_MONETARY:
        v_money = db_get_monetary (src);
        if (OR_CHECK_USHRT_OVERFLOW (floor (v_money->amount)))
          {
        status = DOMAIN_INCOMPATIBLE;
          }
        else
          {
        val_idx = (unsigned short) floor (v_money->amount);
          }
        break;
      case DB_TYPE_TIMESTAMP:
      case DB_TYPE_TIMESTAMPLTZ:
      case DB_TYPE_TIMESTAMPTZ:
      case DB_TYPE_DATETIME:
      case DB_TYPE_DATETIMETZ:
      case DB_TYPE_DATETIMELTZ:
      case DB_TYPE_DATE:
      case DB_TYPE_TIME:
      case DB_TYPE_BIT:
      case DB_TYPE_VARBIT:
      case DB_TYPE_BLOB:
      case DB_TYPE_CLOB:
        {
          status =
        tp_value_cast_internal (src, &conv_val, tp_domain_resolve_default (DB_TYPE_STRING), coercion_mode,
                    do_domain_select, false);
          if (status == DOMAIN_COMPATIBLE)
        {
          val_str = db_get_string (&conv_val);
          val_str_size = db_get_string_size (&conv_val);
        }
        }
        break;
      case DB_TYPE_CHAR:
      case DB_TYPE_VARCHAR:
        if (db_get_string_codeset (src) != TP_DOMAIN_CODESET (desired_domain))
          {
        DB_DATA_STATUS data_status = DATA_STATUS_OK;

        if (TP_DOMAIN_CODESET (desired_domain) == INTL_CODESET_RAW_BYTES)
          {
            /* avoid data truncation when converting to binary charset */
            db_value_domain_init (&conv_val, DB_VALUE_TYPE (src), db_get_string_size (src), 0);
          }
        else
          {
            db_value_domain_init (&conv_val, DB_VALUE_TYPE (src), DB_VALUE_PRECISION (src), 0);
          }

        db_string_put_cs_and_collation (&conv_val, TP_DOMAIN_CODESET (desired_domain),
                        TP_DOMAIN_COLLATION (desired_domain));

        if (db_char_string_coerce (src, &conv_val, &data_status) != NO_ERROR || data_status != DATA_STATUS_OK)
          {
            status = DOMAIN_ERROR;
            pr_clear_value (&conv_val);
          }
        else
          {
            val_str = db_get_string (&conv_val);
            val_str_size = db_get_string_size (&conv_val);
          }
          }
        else
          {
        val_str = db_get_string (src);
        val_str_size = db_get_string_size (src);
          }
        break;
      case DB_TYPE_ENUMERATION:
        if (DOM_GET_ENUM_ELEMS_COUNT (desired_domain) == 0)
          {
        pr_clone_value (src, target);
        exit = true;
        break;
          }
        val_str = db_get_enum_string (src);
        val_str_size = db_get_enum_string_size (src);
        if (val_str == NULL)
          {
        /* src has a short value or a string value or both. We prefer to use the string value when matching
         * against the desired domain but, if this is not set, we will use the value index */
        val_idx = db_get_enum_short (src);
          }
        else
          {
        if (db_get_enum_codeset (src) != TP_DOMAIN_CODESET (desired_domain))
          {
            /* first convert charset of the original value to charset of destination domain */
            DB_VALUE tmp;
            DB_DATA_STATUS data_status = DATA_STATUS_OK;

            /* charset conversion can handle only CHAR/VARCHAR DB_VALUEs, create a STRING value with max
             * precision (so that no truncation occurs) from the ENUM source string */
            db_make_varchar (&tmp, DB_MAX_STRING_LENGTH, val_str, val_str_size, db_get_enum_codeset (src),
                     db_get_enum_collation (src));

            /* initialize destination value of conversion */
            db_value_domain_init (&conv_val, DB_TYPE_STRING, DB_MAX_STRING_LENGTH, 0);
            db_string_put_cs_and_collation (&conv_val, TP_DOMAIN_CODESET (desired_domain),
                            TP_DOMAIN_COLLATION (desired_domain));

            if (db_char_string_coerce (&tmp, &conv_val, &data_status) != NO_ERROR
            || data_status != DATA_STATUS_OK)
              {
            status = DOMAIN_ERROR;
            pr_clear_value (&conv_val);
            val_str = NULL;
            val_idx = 0;
              }
            else
              {
            val_str = db_get_string (&conv_val);
            val_str_size = db_get_string_size (&conv_val);
              }
            pr_clear_value (&tmp);
          }
          }
        break;
      default:
        status = DOMAIN_INCOMPATIBLE;
        break;
      }

    if (exit)
      {
        break;
      }

    if (status == DOMAIN_COMPATIBLE)
      {
        if (val_str != NULL)
          {
        /* We have to search through the elements of the desired domain to find the index for val_str. */
        int i, size;
        DB_ENUM_ELEMENT *db_enum = NULL;
        int elem_count = DOM_GET_ENUM_ELEMS_COUNT (desired_domain);

        for (i = 1; i <= elem_count; i++)
          {
            db_enum = &DOM_GET_ENUM_ELEM (desired_domain, i);
            size = DB_GET_ENUM_ELEM_STRING_SIZE (db_enum);

            if (!ignore_trailing_space)
              {
            ti = (desired_domain->type->id == DB_TYPE_CHAR);
              }

            /* use collation from the PT_TYPE_ENUMERATION */
            if (QSTR_COMPARE (desired_domain->collation_id, (const unsigned char *) val_str, val_str_size,
                      (const unsigned char *) DB_GET_ENUM_ELEM_STRING (db_enum), size, ti) == 0)
              {
            break;
              }
          }

        val_idx = i;
        if (i > elem_count)
          {
            if (val_str[0] == 0)
              {
            /* The source value is string with length 0 and can be matched with enum "special error value"
             * if it's not a valid ENUM value */
            db_make_enumeration (target, 0, NULL, 0, TP_DOMAIN_CODESET (desired_domain),
                         TP_DOMAIN_COLLATION (desired_domain));
            break;
              }
            else
              {
            status = DOMAIN_INCOMPATIBLE;
              }
          }
          }
        else
          {
        /* We have the index, we need to get the actual string value from the desired domain */
        if (val_idx > DOM_GET_ENUM_ELEMS_COUNT (desired_domain))
          {
            status = DOMAIN_INCOMPATIBLE;
          }
        else if (val_idx == 0)
          {
            /* ENUM Special error value */
            db_make_enumeration (target, 0, NULL, 0, TP_DOMAIN_CODESET (desired_domain),
                     TP_DOMAIN_COLLATION (desired_domain));
            break;
          }
        else
          {
            val_str_size = DB_GET_ENUM_ELEM_STRING_SIZE (&DOM_GET_ENUM_ELEM (desired_domain, val_idx));
            val_str = DB_GET_ENUM_ELEM_STRING (&DOM_GET_ENUM_ELEM (desired_domain, val_idx));
          }
          }

        if (status == DOMAIN_COMPATIBLE)
          {
        const char *enum_str;

        assert (val_str != NULL);

        if (!DB_IS_NULL (&conv_val))
          {
            /* if charset conversion, than use the converted value buffer to avoid an additional copy */
            alloc_string = false;
            conv_val.need_clear = false;
          }

        if (alloc_string)
          {
            char *enum_str_tmp = (char *) db_private_alloc (NULL, val_str_size + 1);
            if (enum_str_tmp == NULL)
              {
            status = DOMAIN_ERROR;
            pr_clear_value (&conv_val);
            break;
              }
            else
              {
            memcpy (enum_str_tmp, val_str, val_str_size);
            enum_str_tmp[val_str_size] = 0;
              }

            enum_str = enum_str_tmp;
          }
        else
          {
            enum_str = val_str;
          }

        db_make_enumeration (target, val_idx, enum_str, val_str_size, TP_DOMAIN_CODESET (desired_domain),
                     TP_DOMAIN_COLLATION (desired_domain));
        target->need_clear = true;
          }
      }
    pr_clear_value (&conv_val);
      }
      break;
    case DB_TYPE_JSON:
      {
    JSON_DOC *doc = NULL;

    switch (original_type)
      {
      case DB_TYPE_CHAR:
      case DB_TYPE_VARCHAR:
        {
          DB_VALUE utf8_str;
          const DB_VALUE *json_str_val = &utf8_str;
          int error_code = db_json_copy_and_convert_to_utf8 (src, &utf8_str, &json_str_val);
          if (error_code != NO_ERROR)
        {
          ASSERT_ERROR ();
          status = DOMAIN_ERROR;
          break;
        }

          unsigned int str_size = db_get_string_size (json_str_val);
          const char *original_str = db_get_string (json_str_val);

          error_code = db_json_get_json_from_str (original_str, doc, str_size);
          if (error_code != NO_ERROR)
        {
          pr_clear_value (&utf8_str);
          assert (doc == NULL);
          status = DOMAIN_ERROR;
          break;
        }

          if (desired_domain->json_validator
          && db_json_validate_doc (desired_domain->json_validator, doc) != NO_ERROR)
        {
          ASSERT_ERROR ();
          pr_clear_value (&utf8_str);
          db_json_delete_doc (doc);
          status = DOMAIN_ERROR;
          break;
        }
          pr_clear_value (&utf8_str);
        }
        break;
      case DB_TYPE_SHORT:
        doc = db_json_allocate_doc ();
        db_json_set_int_to_doc (doc, db_get_short (src));
        break;
      case DB_TYPE_INTEGER:
        doc = db_json_allocate_doc ();
        db_json_set_int_to_doc (doc, db_get_int (src));
        break;
      case DB_TYPE_BIGINT:
        doc = db_json_allocate_doc ();
        db_json_set_bigint_to_doc (doc, db_get_bigint (src));
        break;
      case DB_TYPE_DOUBLE:
        doc = db_json_allocate_doc ();
        db_json_set_double_to_doc (doc, db_get_double (src));
        break;
      case DB_TYPE_FLOAT:
      case DB_TYPE_NUMERIC:
        {
          DB_VALUE double_value;

          doc = db_json_allocate_doc ();
          db_value_coerce (src, &double_value, db_type_to_db_domain (DB_TYPE_DOUBLE));
          db_json_set_double_to_doc (doc, db_get_double (&double_value));
          pr_clear_value (&double_value);
        }
        break;
      default:
        status = DOMAIN_INCOMPATIBLE;
        break;
      }

    if (status == DOMAIN_COMPATIBLE)
      {
        db_make_json (target, doc, true);
      }
    else
      {
        if (doc != NULL)
          {
        db_json_delete_doc (doc);
          }
      }
      }
      break;
    default:
      status = DOMAIN_INCOMPATIBLE;
      break;
    }

  if (err < 0)
    {
      status = DOMAIN_ERROR;
    }

  if (status != DOMAIN_COMPATIBLE)
    {
      if (src != dest)
    {
#if 0               /* TODO - */
      pr_clear_value (dest);
#endif

      /* make sure this doesn't have any partial results */
      if (preserve_domain)
        {
          db_value_domain_init (dest, desired_type, desired_domain->precision, desired_domain->scale);
          db_value_put_null (dest);
        }
      else
        {
          db_make_null (dest);
        }
    }
    }
  else if (src == dest)
    {
      /* coercsion successful, transfer the value if src == dest */
      pr_clear_value (dest);
      *dest = temp;
    }

  pr_clear_value (&src_replacement);

  return status;
}

/*
 * tp_value_cast - Coerce a value into one of another domain.
 *    return: TP_DOMAIN_STATUS
 *    src(in): src DB_VALUE
 *    dest(out): dest DB_VALUE
 *    desired_domain(in):
 *    implicit_coercion(in): flag for the coercion is implicit
 * Note:
 *    This function does select domain from desired_domain
 */
TP_DOMAIN_STATUS
tp_value_cast (const DB_VALUE * src, DB_VALUE * dest, const TP_DOMAIN * desired_domain, bool implicit_coercion)
{
  TP_COERCION_MODE mode;

  mode = (implicit_coercion ? TP_IMPLICIT_COERCION : TP_EXPLICIT_COERCION);
  return tp_value_cast_internal (src, dest, desired_domain, mode, true, false);
}

TP_DOMAIN_STATUS
tp_value_cast_force (const DB_VALUE * src, DB_VALUE * dest, const TP_DOMAIN * desired_domain, bool implicit_coercion)
{
  TP_COERCION_MODE mode;

  mode = TP_FORCE_COERCION;
  return tp_value_cast_internal (src, dest, desired_domain, mode, true, false);
}

/*
 * tp_value_cast_preserve_domain - Coerce a value into one of another domain.
 *    return: TP_DOMAIN_STATUS
 *    src(in): src DB_VALUE
 *    dest(out): dest DB_VALUE
 *    desired_domain(in):
 *    implicit_coercion(in): flag for the coercion is implicit
 *    preserve_domain(in): flag to preserve dest's domain
 * Note:
 *    This function dose not change the domain type of dest to a DB_NULL_TYPE.
 */
TP_DOMAIN_STATUS
tp_value_cast_preserve_domain (const DB_VALUE * src, DB_VALUE * dest, const TP_DOMAIN * desired_domain,
                   bool implicit_coercion, bool preserve_domain)
{
  TP_COERCION_MODE mode;

  mode = (implicit_coercion ? TP_IMPLICIT_COERCION : TP_EXPLICIT_COERCION);
  return tp_value_cast_internal (src, dest, desired_domain, mode, true, true);
}

/*
 * tp_value_cast_no_domain_select - Coerce a value into one of another domain.
 *    return: TP_DOMAIN_STATUS
 *    src(in): src DB_VALUE
 *    dest(out): dest DB_VALUE
 *    desired_domain(in):
 *    implicit_coercion(in): flag for the coercion is implicit
 * Note:
 *    This function does not select domain from desired_domain
 */
TP_DOMAIN_STATUS
tp_value_cast_no_domain_select (const DB_VALUE * src, DB_VALUE * dest, const TP_DOMAIN * desired_domain,
                bool implicit_coercion)
{
  TP_COERCION_MODE mode;

  mode = (implicit_coercion ? TP_IMPLICIT_COERCION : TP_EXPLICIT_COERCION);
  return tp_value_cast_internal (src, dest, desired_domain, mode, false, false);
}

/*
 * tp_value_change_coll_and_codeset () - change the collation and codeset of a
 *                                       value
 *   returns: cast operation result
 *   src(in): source DB_VALUE
 *   dest(in): destination DB_VALUE where src will be copied/adjusted to
 *   coll_id(in): destination collation id
 *   codeset(in): destination codeset
 */
TP_DOMAIN_STATUS
tp_value_change_coll_and_codeset (DB_VALUE * src, DB_VALUE * dest, int coll_id, int codeset)
{
  TP_DOMAIN *temp_domain;

  assert (src != NULL && dest != NULL);
  assert (TP_IS_STRING_TYPE (DB_VALUE_TYPE (src)));

  if (db_get_string_collation (src) == coll_id && db_get_string_codeset (src) == codeset)
    {
      /* early exit scenario */
      return DOMAIN_COMPATIBLE;
    }

  /* create new domain and adjust collation and codeset */
  temp_domain = tp_domain_resolve_value (src, NULL);
  if (temp_domain != NULL && temp_domain->is_cached)
    {
      temp_domain = tp_domain_copy (temp_domain, false);
    }
  if (temp_domain == NULL)
    {
      /* not exactly a relevant error code, but should serve it's purpose */
      assert (false);
      return DOMAIN_ERROR;
    }

  temp_domain->collation_id = coll_id;
  temp_domain->codeset = codeset;

  /* cache domain */
  temp_domain = tp_domain_cache (temp_domain);

  /* cast the value */
  return tp_value_cast (src, dest, temp_domain, true);
}

/*
 * VALUE COMPARISON
 */

/*
 * oidcmp - Compares two OIDs and returns a DB_ style status code.
 *    return: DB_ comparison status code
 *    oid1(in): first oid
 *    oid2(in): second oid
 * Note:
 *    The underlying oid_compare should be using these so we can avoid
 *    an extra level of indirection.
 */
static DB_VALUE_COMPARE_RESULT
oidcmp (OID * oid1, OID * oid2)
{
  int c = oid_compare (oid1, oid2);
  if (c < 0)
    {
      return DB_LT;
    }
  else if (c > 0)
    {
      return DB_GT;
    }

  return DB_EQ;
}

/*
 * tp_more_general_type - compares two type with respect to generality
 *    return: 0 if same type,
 *           <0 if type1 less general then type2,
 *           >0 otherwise
 *    type1(in): first type
 *    type2(in): second type
 */
int
tp_more_general_type (const DB_TYPE type1, const DB_TYPE type2)
{
  if (type1 == type2)
    {
      return 0;
    }
  if ((unsigned) type1 > DB_TYPE_LAST)
    {
#if defined (CUBRID_DEBUG)
      printf ("tp_more_general_type: DB type 1 out of range: %d\n", type1);
#endif /* CUBRID_DEBUG */
      return 0;
    }
  if ((unsigned) type2 > DB_TYPE_LAST)
    {
#if defined (CUBRID_DEBUG)
      printf ("tp_more_general_type: DB type 2 out of range: %d\n", type2);
#endif /* CUBRID_DEBUG */
      return 0;
    }

  return db_type_rank_order[type1] - db_type_rank_order[type2];
}

/*
 * tp_set_compare - compare two collection
 *    return: zero if equal, <0 if less, >0 if greater
 *    value1(in): first collection value
 *    value2(in): second collection value
 *    do_coercion(in): coercion flag
 *    total_order(in): total order flag
 * Note:
 *    If the total_order flag is set, it will return one of DB_LT, DB_GT, or
 *    SB_SUBSET, DB_SUPERSET, or DB_EQ, it will not return DB_UNK.
 */
DB_VALUE_COMPARE_RESULT
tp_set_compare (const DB_VALUE * value1, const DB_VALUE * value2, int do_coercion, int total_order)
{
  DB_VALUE temp;
  int coercion;
  DB_VALUE *v1, *v2;
  DB_TYPE vtype1, vtype2;
  DB_SET *s1, *s2;
  DB_VALUE_COMPARE_RESULT result = DB_UNK;
  TP_DOMAIN_STATUS status = DOMAIN_COMPATIBLE;

  coercion = 0;
  if (DB_IS_NULL (value1))
    {
      if (DB_IS_NULL (value2))
    {
      result = (total_order ? DB_EQ : DB_UNK);
    }
      else
    {
      result = (total_order ? DB_LT : DB_UNK);
    }
    }
  else if (DB_IS_NULL (value2))
    {
      result = (total_order ? DB_GT : DB_UNK);
    }
  else
    {
      v1 = (DB_VALUE *) value1;
      v2 = (DB_VALUE *) value2;

      vtype1 = DB_VALUE_DOMAIN_TYPE (v1);
      vtype2 = DB_VALUE_DOMAIN_TYPE (v2);
      if (vtype1 != DB_TYPE_SET && vtype1 != DB_TYPE_MULTISET && vtype1 != DB_TYPE_SEQUENCE)
    {
      return DB_NE;
    }

      if (vtype2 != DB_TYPE_SET && vtype2 != DB_TYPE_MULTISET && vtype2 != DB_TYPE_SEQUENCE)
    {
      return DB_NE;
    }

      if (vtype1 != vtype2)
    {
      if (!do_coercion)
        {
          /* types are not comparable */
          return DB_NE;
        }
      else
        {
          db_make_null (&temp);
          coercion = 1;
          if (tp_more_general_type (vtype1, vtype2) > 0)
        {
          /* vtype1 is more general, coerce value 2 */
          status = tp_value_coerce (v2, &temp, tp_domain_resolve_default (vtype1));
          if (status != DOMAIN_COMPATIBLE)
            {
              /*
               * This is arguably an error condition
               * but Not Equal is as close as we can come
               * to reporting it.
               */
              pr_clear_value (&temp);
              return DB_NE;
            }
          else
            {
              v2 = &temp;
              vtype2 = DB_VALUE_TYPE (v2);
            }
        }
          else
        {
          /* coerce value1 to value2's type */
          status = tp_value_coerce (v1, &temp, tp_domain_resolve_default (vtype2));
          if (status != DOMAIN_COMPATIBLE)
            {
              /*
               * This is arguably an error condition
               * but Not Equal is as close as we can come
               * to reporting it.
               */
              pr_clear_value (&temp);
              return DB_NE;
            }
          else
            {
              v1 = &temp;
              vtype1 = DB_VALUE_TYPE (v1);
            }
        }
        }
    }
      /* Here, we have two collections of the same type */

      s1 = db_get_set (v1);
      s2 = db_get_set (v2);

      /*
       * there may ba a call for set_compare returning a total
       * ordering some day.
       */
      if (s1 && s2)
    {
      result = set_compare (s1, s2, do_coercion);
    }
      else
    {
      result = DB_UNK;
    }

      if (coercion)
    {
      pr_clear_value (&temp);
    }
    }

  return result;
}

/*
 * tp_value_compare - calls tp_value_compare_with_error, but does not log error
 *    return: zero if equal, <0 if less, >0 if greater
 *    value1(in): first value
 *    value2(in): second value
 *    do_coercion(in): coercion flag
 *    total_order(in): total order flag
 */
DB_VALUE_COMPARE_RESULT
tp_value_compare (const DB_VALUE * value1, const DB_VALUE * value2, int allow_coercion, int total_order)
{
  return tp_value_compare_with_error (value1, value2, allow_coercion, total_order, NULL);
}

/*
 * tp_value_compare_with_error - compares two values
 *    return: zero if equal, <0 if less, >0 if greater
 *    value1(in): first value
 *    value2(in): second value
 *    do_coercion(in): coercion flag
 *    total_order(in): total order flag
 *    can_compare(out): set if values are comparable
 * Note:
 *    There is some implicit conversion going on here, not sure if this
 *    is a good idea because it gives the impression that these have
 *    compatible domains.
 *
 *    If the total_order flag is set, it will return one of DB_LT, DB_GT, or
 *    DB_EQ, it will not return DB_UNK.  For the purposes of the total
 *    ordering, two NULL values are DB_EQ and if only one value is NULL, that
 *    value is less than the non-null value.
 *
 *    If "can_compare" is not null, in the event of incomparable values an
 *    error will be logged and the boolean that is pointed by "can_compare"
 *    will be set to false.
 */
DB_VALUE_COMPARE_RESULT
tp_value_compare_with_error (const DB_VALUE * value1, const DB_VALUE * value2, int do_coercion, int total_order,
                 bool * can_compare)
{
  DB_VALUE temp1, temp2, tmp_char_conv;
  int coercion, char_conv;
  DB_VALUE *v1, *v2;
  DB_TYPE vtype1, vtype2;
#if !defined (SERVER_MODE)
  DB_OBJECT *mop;
  DB_IDENTIFIER *oid1, *oid2;
#endif /* !defined (SERVER_MODE) */
  bool use_collation_of_v1 = false;
  bool use_collation_of_v2 = false;
  DB_VALUE_COMPARE_RESULT result = DB_UNK;
  TP_DOMAIN_STATUS status = DOMAIN_COMPATIBLE;

  coercion = 0;
  char_conv = 0;

  if (can_compare != NULL)
    {
      *can_compare = true;
    }

  if (DB_IS_NULL (value1))
    {
      if (DB_IS_NULL (value2))
    {
      result = (total_order ? DB_EQ : DB_UNK);
    }
      else
    {
      result = (total_order ? DB_LT : DB_UNK);
    }
    }
  else if (DB_IS_NULL (value2))
    {
      result = (total_order ? DB_GT : DB_UNK);
    }
  else
    {
      int common_coll = -1;
      v1 = (DB_VALUE *) value1;
      v2 = (DB_VALUE *) value2;

      vtype1 = DB_VALUE_DOMAIN_TYPE (v1);
      vtype2 = DB_VALUE_DOMAIN_TYPE (v2);

      /*
       * Hack, DB_TYPE_OID & DB_TYPE_OBJECT are logically the same domain
       * although their physical representations are different.
       * If we see a pair of those, handle it up front before we
       * fall in and try to perform coercion.  Avoid "coercion" between
       * OIDs and OBJECTs because we usually try to keep OIDs unswizzled
       * as long as possible.
       */
      if (vtype1 != vtype2)
    {
#if defined (SERVER_MODE)
      if (vtype1 == DB_TYPE_OBJECT || vtype2 == DB_TYPE_OBJECT)
        {
          assert_release (false);
          return DB_UNK;
        }
#else /* !defined (SERVER_MODE) */
      if (vtype1 == DB_TYPE_OBJECT)
        {
          if (vtype2 == DB_TYPE_OID)
        {
          mop = db_get_object (v1);
          oid1 = mop ? WS_OID (mop) : NULL;
          oid2 = db_get_oid (v2);
          if (oid1 && oid2)
            {
              return oidcmp (oid1, oid2);
            }
          else
            {
              return DB_UNK;
            }
        }
        }
      else if (vtype2 == DB_TYPE_OBJECT)
        {
          if (vtype1 == DB_TYPE_OID)
        {
          oid1 = db_get_oid (v1);
          mop = db_get_object (v2);
          oid2 = mop ? WS_OID (mop) : NULL;

          if (oid1 && oid2)
            {
              return oidcmp (oid1, oid2);
            }
          else
            {
              return DB_UNK;
            }
        }
        }
#endif /* !defined (SERVER_MODE) */

      /*
       * If value types aren't exact, try coercion.
       * May need to be using the domain returned by
       * tp_domain_resolve_value here ?
       */
      if (do_coercion && !ARE_COMPARABLE (vtype1, vtype2))
        {
          db_make_null (&temp1);
          db_make_null (&temp2);
          coercion = 1;

          if (TP_IS_CHAR_TYPE (vtype1) && TP_IS_NUMERIC_TYPE (vtype2))
        {
          /* coerce v1 to double */
          status = tp_value_coerce (v1, &temp1, tp_domain_resolve_default (DB_TYPE_DOUBLE));
          if (status == DOMAIN_COMPATIBLE)
            {
              v1 = &temp1;
              vtype1 = DB_VALUE_TYPE (v1);

              if (vtype2 != DB_TYPE_DOUBLE)
            {
              status = tp_value_coerce (v2, &temp2, tp_domain_resolve_default (DB_TYPE_DOUBLE));

              if (status == DOMAIN_COMPATIBLE)
                {
                  v2 = &temp2;
                  vtype2 = DB_VALUE_TYPE (v2);
                }
            }
            }
        }
          else if (TP_IS_NUMERIC_TYPE (vtype1) && TP_IS_CHAR_TYPE (vtype2))
        {
          /* coerce v2 to double */
          status = tp_value_coerce (v2, &temp2, tp_domain_resolve_default (DB_TYPE_DOUBLE));
          if (status == DOMAIN_COMPATIBLE)
            {
              v2 = &temp2;
              vtype2 = DB_VALUE_TYPE (v2);

              if (vtype1 != DB_TYPE_DOUBLE)
            {
              status = tp_value_coerce (v1, &temp1, tp_domain_resolve_default (DB_TYPE_DOUBLE));

              if (status == DOMAIN_COMPATIBLE)
                {
                  v1 = &temp1;
                  vtype1 = DB_VALUE_TYPE (v1);
                }
            }
            }
        }
          else if (TP_IS_CHAR_TYPE (vtype1) && TP_IS_DATE_OR_TIME_TYPE (vtype2))
        {
          /* vtype2 is the date or time type, coerce value 1 */
          TP_DOMAIN *d2 = tp_domain_resolve_default (vtype2);
          status = tp_value_coerce (v1, &temp1, d2);
          if (status == DOMAIN_COMPATIBLE)
            {
              v1 = &temp1;
              vtype1 = DB_VALUE_TYPE (v1);
            }
        }
          else if (TP_IS_DATE_OR_TIME_TYPE (vtype1) && TP_IS_CHAR_TYPE (vtype2))
        {
          /* vtype1 is the date or time type, coerce value 2 */
          TP_DOMAIN *d1 = tp_domain_resolve_default (vtype1);
          status = tp_value_coerce (v2, &temp2, d1);
          if (status == DOMAIN_COMPATIBLE)
            {
              v2 = &temp2;
              vtype2 = DB_VALUE_TYPE (v2);
            }
        }
          else if (tp_more_general_type (vtype1, vtype2) > 0)
        {
          /* vtype1 is more general, coerce value 2 */
          TP_DOMAIN *d1 = tp_domain_resolve_default (vtype1);

          if (TP_TYPE_HAS_COLLATION (vtype2) && TP_IS_CHAR_TYPE (vtype1))
            {
              /* create a new domain with type of v1 */
              d1 = tp_domain_copy (d1, false);
              if (TP_IS_CHAR_TYPE (vtype2))
            {
              /* keep the codeset and collation from original value v2 */
              d1->codeset = db_get_string_codeset (v2);
              d1->collation_id = db_get_string_collation (v2);
            }
              else
            {
              /* v2 is ENUM, and is coerced to string this should happend when the other operand is a HV;
               * in this case we remember to use collation and charset from ENUM (v2) */
              use_collation_of_v2 = true;
              d1->codeset = db_get_enum_codeset (v2);
              d1->collation_id = db_get_enum_collation (v2);
              common_coll = d1->collation_id;
            }

              d1 = tp_domain_cache (d1);
            }

          status = tp_value_coerce (v2, &temp2, d1);
          if (status != DOMAIN_COMPATIBLE)
            {
              /*
               * This is arguably an error condition
               * but Not Equal is as close as we can come
               * to reporting it.
               */

              // WARNING: forget any error for coercion.
              er_clear ();
            }
          else
            {
              v2 = &temp2;
              vtype2 = DB_VALUE_TYPE (v2);
            }
        }
          else
        {
          /* coerce value1 to value2's type */
          TP_DOMAIN *d2 = tp_domain_resolve_default (vtype2);

          if (TP_TYPE_HAS_COLLATION (vtype1) && TP_IS_CHAR_TYPE (vtype2))
            {
              /* create a new domain with type of v2 */
              d2 = tp_domain_copy (d2, false);
              if (TP_IS_CHAR_TYPE (vtype1))
            {
              /* keep the codeset and collation from original value v1 */
              d2->codeset = db_get_string_codeset (v1);
              d2->collation_id = db_get_string_collation (v1);
            }
              else
            {
              /* v1 is ENUM, and is coerced to string this should happend when the other operand is a HV;
               * in this case we remember to use collation and charset from ENUM (v1) */
              use_collation_of_v1 = true;
              d2->codeset = db_get_enum_codeset (v1);
              d2->collation_id = db_get_enum_collation (v1);
              common_coll = d2->collation_id;
            }

              d2 = tp_domain_cache (d2);
            }

          status = tp_value_coerce (v1, &temp1, d2);
          if (status != DOMAIN_COMPATIBLE)
            {
              /*
               * This is arguably an error condition
               * but Not Equal is as close as we can come
               * to reporting it.
               */

              // WARNING: forget any error for coercion.
              er_clear ();
            }
          else
            {
              v1 = &temp1;
              vtype1 = DB_VALUE_TYPE (v1);
            }
        }
        }
    }

      if (!ARE_COMPARABLE (vtype1, vtype2))
    {
      /*
       * Default result for mismatched types.
       * Not correct but will be consistent.
       */
      if (tp_more_general_type (vtype1, vtype2) > 0)
        {
          result = DB_GT;
        }
      else
        {
          result = DB_LT;
        }

      /* set incompatibility flag */
      if (can_compare != NULL)
        {
          *can_compare = false;

          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TP_CANT_COERCE, 2, pr_type_name (vtype1),
              pr_type_name (vtype2));
        }
    }
      else
    {
      const PR_TYPE *pr_type;

      pr_type = pr_type_from_id (vtype1);
      assert (pr_type != NULL);

      if (pr_type)
        {
          /* Either both arguments are enums, or both are not. If one is enum and one is not, it means that
           * tp_value_cast_internal failed somewhere */
          assert ((vtype1 == DB_TYPE_ENUMERATION && vtype1 == vtype2)
              || (vtype1 != DB_TYPE_ENUMERATION && vtype2 != DB_TYPE_ENUMERATION));

          if (!TP_IS_CHAR_TYPE (vtype1))
        {
          common_coll = 0;
        }
          else if (db_get_string_collation (v1) == db_get_string_collation (v2))
        {
          common_coll = db_get_string_collation (v1);
        }
          else if (TP_IS_CHAR_TYPE (vtype1) && (use_collation_of_v1 || use_collation_of_v2))
        {
          INTL_CODESET codeset;
          DB_DATA_STATUS data_status;
          int error_status;

          db_make_null (&tmp_char_conv);
          char_conv = 1;

          if (use_collation_of_v1)
            {
              assert (!use_collation_of_v2);
              common_coll = db_get_string_collation (v1);
            }
          else
            {
              assert (use_collation_of_v2 == true);
              common_coll = db_get_string_collation (v2);
            }

          codeset = lang_get_collation (common_coll)->codeset;

          if (db_get_string_codeset (v1) != codeset)
            {
              db_value_domain_init (&tmp_char_conv, vtype1, DB_VALUE_PRECISION (v1), 0);

              db_string_put_cs_and_collation (&tmp_char_conv, codeset, common_coll);
              error_status = db_char_string_coerce (v1, &tmp_char_conv, &data_status);

              if (error_status != NO_ERROR)
            {
              pr_clear_value (&tmp_char_conv);
              if (coercion)
                {
                  pr_clear_value (&temp1);
                  pr_clear_value (&temp2);
                }
              return DB_UNK;
            }

              assert (data_status == DATA_STATUS_OK);

              v1 = &tmp_char_conv;
            }
          else if (db_get_string_codeset (v2) != codeset)
            {
              db_value_domain_init (&tmp_char_conv, vtype2, DB_VALUE_PRECISION (v2), 0);

              db_string_put_cs_and_collation (&tmp_char_conv, codeset, common_coll);
              error_status = db_char_string_coerce (v2, &tmp_char_conv, &data_status);

              if (error_status != NO_ERROR)
            {
              pr_clear_value (&tmp_char_conv);
              if (coercion)
                {
                  pr_clear_value (&temp1);
                  pr_clear_value (&temp2);
                }
              return DB_UNK;
            }

              assert (data_status == DATA_STATUS_OK);

              v2 = &tmp_char_conv;
            }
        }
          else if (TP_IS_CHAR_TYPE (vtype1) && db_get_string_codeset (v1) == db_get_string_codeset (v2))
        {
          LANG_RT_COMMON_COLL (db_get_string_collation (v1), db_get_string_collation (v2), common_coll);
        }

          if (common_coll == -1)
        {
          result = DB_UNK;
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INCOMPATIBLE_COLLATIONS, 0);
          if (can_compare != NULL)
            {
              *can_compare = false;
            }
        }
          else
        {
          result = pr_type->cmpval (v1, v2, do_coercion, total_order, NULL, common_coll);
        }

          if (result == DB_UNK)
        {
          /* safe guard */
          if (pr_type->id == DB_TYPE_MIDXKEY)
            {
              er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_MR_NULL_DOMAIN, 0);
              assert (false);
            }
        }
        }
      else
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_MR_NULL_DOMAIN, 0);
          result = DB_UNK;
        }
    }

      if (coercion)
    {
      pr_clear_value (&temp1);
      pr_clear_value (&temp2);
    }
      if (char_conv)
    {
      pr_clear_value (&tmp_char_conv);
    }
    }

  return result;
}

/*
 * tp_value_equal - compares the contents of two DB_VALUE structures and
 * determines if they are equal
 *    return: non-zero if the values are equal
 *    value1(in): first value
 *    value2(in): second value
 *    do_coercion(): coercion flag
 * Note:
 *    determines if they are equal.  This is a boolean comparison, you
 *    cannot use this for sorting.
 *
 *    This used to be fully implemented, since this got a lot more complicated
 *    with the introduction of parameterized types, and it is doubtfull that
 *    it saved much in performance anyway, it has been reimplemented to simply
 *    call tp_value_compare.  The old function is commented out below in case
 *    this causes problems.  After awhile, it can be removed.
 *
 */
int
tp_value_equal (const DB_VALUE * value1, const DB_VALUE * value2, int do_coercion)
{
  return tp_value_compare (value1, value2, do_coercion, 0) == DB_EQ;
}

/*
 * DOMAIN INFO FUNCTIONS
 */


/*
 * tp_domain_disk_size - Calculate the disk size necessary to store a value
 * for a particular domain.
 *    return: disk size in bytes. -1 if this is a variable width domain or
 *            floating precision in fixed domain.
 *    domain(in): domain to consider
 * Note:
 *    This is here because it takes a domain handle.
 *    Since this is going to get called a lot, we might want to just add
 *    this to the TP_DOMAIN structure and calculate it internally when
 *    it is cached.
 */
int
tp_domain_disk_size (TP_DOMAIN * domain)
{
  if (domain->type->is_always_variable ())
    {
      return -1;
    }

  if ((domain->type->get_id () == DB_TYPE_CHAR || domain->type->get_id () == DB_TYPE_BIT)
      && domain->precision == TP_FLOATING_PRECISION_VALUE)
    {
      return -1;
    }

  assert (domain->precision != TP_FLOATING_PRECISION_VALUE);

  return domain->type->get_disk_size_of_mem (NULL, domain);
}


/*
 * tp_domain_memory_size - Calculates the "instance memory" size required
 * to hold a value for a particular domain.
 *    return: bytes size
 *    domain(in): domain to consider
 */
int
tp_domain_memory_size (TP_DOMAIN * domain)
{
  if ((domain->type->get_id () == DB_TYPE_CHAR || domain->type->get_id () == DB_TYPE_BIT)
      && domain->precision == TP_FLOATING_PRECISION_VALUE)
    {
      return -1;
    }

  return domain->type->get_mem_size_of_mem (NULL, domain);
}

/*
 * tp_init_value_domain - initializes the domain information in a DB_VALUE to
 * correspond to the information from a TP_DOMAIN structure.
 *    return: none
 *    domain(out): domain information
 *    value(in): value to initialize
 * Note:
 *    Used primarily by the value unpacking functions.
 *    It uses the "initval" type function.  This needs to be changed
 *    to take a full domain rather than just precision/scale but the
 *    currently behavior will work for now.
 *
 *    Think about the need for "initval" all it really does is call
 *    db_value_domain_init() with the supplied arguments.
 */
void
tp_init_value_domain (TP_DOMAIN * domain, DB_VALUE * value)
{
  if (domain == NULL)
    {
      /* shouldn't happen ? */
      db_value_domain_init (value, DB_TYPE_NULL, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
    }
  else
    {
      domain->type->initval (value, domain->precision, domain->scale);
    }
}


/*
 * tp_check_value_size - check a particular variable sized value (e.g.
 * varchar, char, bit) against a destination domain.
 *    return: domain status (ok or overflow)
 *    domain(in): target domain
 *    value(in): value to be assigned
 * Note:
 *    It is assumed that basic domain compatibility has already been
 *    performed and that the supplied domain will match with what is
 *    in the value.
 *    This is used primarily for character data that is allowed to fit
 *    within a domain if the byte size is within tolerance.
 */
TP_DOMAIN_STATUS
tp_check_value_size (TP_DOMAIN * domain, DB_VALUE * value)
{
  TP_DOMAIN_STATUS status;
  int src_precision, src_length;
  DB_TYPE dbtype;
  const char *src;

  status = DOMAIN_COMPATIBLE;

  /* if target domain is "floating", its always ok */
  if (domain->precision != TP_FLOATING_PRECISION_VALUE)
    {

      dbtype = TP_DOMAIN_TYPE (domain);
      switch (dbtype)
    {
    case DB_TYPE_CHAR:
    case DB_TYPE_BIT:
      /*
       * The compatibility will be determined by the precision.
       * A floating precision is determined by the length of the string
       * value.
       */
      src = db_get_string (value);
      if (src != NULL)
        {
          src_precision = db_value_precision (value);
          if (!TP_IS_BIT_TYPE (dbtype))
        {
          src_length = db_get_string_length (value);
          assert (src_length >= 0);
        }
          else
        {
          src_length = db_get_string_size (value);
        }

          /* Check for floating precision. */
          if (src_precision == TP_FLOATING_PRECISION_VALUE)
        {
          src_precision = src_length;
        }

          if (src_precision > domain->precision)
        {
          status = DOMAIN_OVERFLOW;
        }
        }
      break;

    case DB_TYPE_VARCHAR:
    case DB_TYPE_VARBIT:
      /*
       * The compatibility of the value is always determined by the
       * actual length of the value, not the destination precision.
       */
      src = db_get_string (value);
      if (src != NULL)
        {
          if (!TP_IS_BIT_TYPE (dbtype))
        {
          src_length = db_get_string_length (value);
          assert (src_length >= 0);
        }
          else
        {
          src_length = db_get_string_size (value);
        }

          /*
           * Work backwards from the source length into a minimum precision.
           * This feels like it should be a nice packed utility
           * function somewhere.
           */
          src_precision = src_length;

          if (src_precision > domain->precision)
        {
          status = DOMAIN_OVERFLOW;
        }
        }
      break;

    default:
      /*
       * None of the other types require this form of value dependent domain
       * precision checking.
       */
      break;
    }
    }

  return status;
}

#if defined(CUBRID_DEBUG)
/*
 * fprint_domain - print information of a domain
 *    return: void
 *    fp(out): FILE pointer
 *    domain(in): domain to print
 */
static void
fprint_domain (FILE * fp, TP_DOMAIN * domain)
{
  TP_DOMAIN *d;

  for (d = domain; d != NULL; d = d->next)
    {

      switch (TP_DOMAIN_TYPE (d))
    {

    case DB_TYPE_OBJECT:
    case DB_TYPE_OID:
    case DB_TYPE_SUB:
      if (TP_DOMAIN_TYPE (d) == DB_TYPE_SUB)
        {
          fprintf (fp, "sub(");
        }
#if !defined (SERVER_MODE)
      if (d->class_mop != NULL)
        {
          fprintf (fp, "%s", db_get_class_name (d->class_mop));
        }
      else if (OID_ISNULL (&d->class_oid))
        {
          fprintf (fp, "object");
        }
      else
#endif /* !SERVER_MODE */
        {
          fprintf (fp, "object(%d,%d,%d)", d->class_oid.volid, d->class_oid.pageid, d->class_oid.slotid);
        }
      if (TP_DOMAIN_TYPE (d) == DB_TYPE_SUB)
        {
          fprintf (fp, ")");
        }
      break;

    case DB_TYPE_VARIABLE:
      fprintf (fp, "union(");
      fprint_domain (fp, d->setdomain);
      fprintf (fp, ")");
      break;

    case DB_TYPE_SET:
      fprintf (fp, "set(");
      fprint_domain (fp, d->setdomain);
      fprintf (fp, ")");
      break;
    case DB_TYPE_MULTISET:
      fprintf (fp, "multiset(");
      fprint_domain (fp, d->setdomain);
      fprintf (fp, ")");
      break;
    case DB_TYPE_SEQUENCE:
      fprintf (fp, "sequence(");
      fprint_domain (fp, d->setdomain);
      fprintf (fp, ")");
      break;

    case DB_TYPE_BIT:
    case DB_TYPE_VARBIT:
      fprintf (fp, "%s(%d)", d->type->name, d->precision);
      break;

    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      fprintf (fp, "%s(%d) collate %s", d->type->name, d->precision, lang_get_collation_name (d->collation_id));
      break;

    case DB_TYPE_NUMERIC:
      fprintf (fp, "%s(%d,%d)", d->type->name, d->precision, d->scale);
      break;

    default:
      fprintf (fp, "%s", d->type->name);
      break;
    }

      if (d->next != NULL)
    {
      fprintf (fp, ",");
    }
    }
}

/*
 * tp_dump_domain - fprint_domain to stdout
 *    return: void
 *    domain(in): domain to print
 */
void
tp_dump_domain (TP_DOMAIN * domain)
{
  fprint_domain (stdout, domain);
  fprintf (stdout, "\n");
}

/*
 * tp_domain_print - fprint_domain to stdout
 *    return: void
 *    domain(in): domain to print
 */
void
tp_domain_print (TP_DOMAIN * domain)
{
  fprint_domain (stdout, domain);
}

/*
 * tp_domain_fprint - fprint_domain to stdout
 *    return: void
 *    fp(out): FILE pointer
 *    domain(in): domain to print
 */
void
tp_domain_fprint (FILE * fp, TP_DOMAIN * domain)
{
  fprint_domain (fp, domain);
}
#endif

/*
 * tp_valid_indextype - check for valid index type
 *    return: 1 if type is a valid index type, 0 otherwise.
 *    type(in): a database type constant
 */
int
tp_valid_indextype (DB_TYPE type)
{
  switch (type)
    {
    case DB_TYPE_INTEGER:
    case DB_TYPE_FLOAT:
    case DB_TYPE_DOUBLE:
    case DB_TYPE_STRING:
    case DB_TYPE_OBJECT:
    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_DATE:
    case DB_TYPE_MONETARY:
    case DB_TYPE_SHORT:
    case DB_TYPE_BIGINT:
    case DB_TYPE_OID:
    case DB_TYPE_NUMERIC:
    case DB_TYPE_BIT:
    case DB_TYPE_VARBIT:
    case DB_TYPE_CHAR:
    case DB_TYPE_ENUMERATION:
      return 1;
    default:
      return 0;
    }
}


/*
 * tp_domain_references_objects - check if domain is an object domain or a
 * collection domain that might include objects.
 *    return: int (true or false)
 *    dom(in): the domain to be inspected
 */
bool
tp_domain_references_objects (const TP_DOMAIN * dom)
{
  switch (TP_DOMAIN_TYPE (dom))
    {
    case DB_TYPE_OBJECT:
    case DB_TYPE_OID:
    case DB_TYPE_VOBJ:
      return true;
    case DB_TYPE_SET:
    case DB_TYPE_MULTISET:
    case DB_TYPE_SEQUENCE:
      dom = dom->setdomain;
      if (dom)
    {
      /*
       * If domains are specified, we can assume that the upper levels
       * have enforced the rule that no value in the collection has a
       * domain that isn't included in this list.  If this list has no
       * object domain, then no collection with this domain can include
       * an object reference.
       */
      for (; dom; dom = dom->next)
        {
          if (tp_domain_references_objects (dom))
        {
          return true;
        }
        }

      return false;
    }
      else
    {
      /*
       * We've got hold of one of our fabulous "collection of anything"
       * attributes.  We've got no choice but to assume that it might
       * have objects in it.
       */
      return true;
    }
    default:
      return false;
    }
}

/*
 * tp_value_auto_cast_with_precsion_check
 *       Cast a value into one of another domain like tp_value_auto_cast.
 *           It checks the precision in case of casting discrete number type to numeric type
 *    return: TP_DOMAIN_STATUS
 *    src(in): src DB_VALUE
 *    dest(out): dest DB_VALUE
 *    desired_domain(in): destion domain
 */
TP_DOMAIN_STATUS
tp_value_auto_cast_with_precision_check (const DB_VALUE * src, DB_VALUE * dest, const TP_DOMAIN * desired_domain)
{
  TP_DOMAIN_STATUS dom_status = DOMAIN_COMPATIBLE;
  static const INT64 max_value[DB_BIGINT_PRECISION] = {
    1LL,
    10LL,
    100LL,
    1000LL,
    10000LL,
    100000LL,
    1000000LL,
    10000000LL,
    100000000LL,
    1000000000LL,
    10000000000LL,
    100000000000LL,
    1000000000000LL,
    10000000000000LL,
    100000000000000LL,
    1000000000000000LL,
    10000000000000000LL,
    100000000000000000LL,
    1000000000000000000LL
  };                /* max precision of a big integer is 19 */

#if defined(SA_MODE) && !defined(NDEBUG)
  static int dbg_check_initialize = 0;
  if (dbg_check_initialize == 0)
    {
      for (int i = 1; i < DB_BIGINT_PRECISION; i++)
    {
      assert (max_value[i] == (max_value[i - 1] * 10));
    }
      dbg_check_initialize = 1;
    }
#endif

  if (TP_IS_DISCRETE_NUMBER_TYPE (src->domain.general_info.type))
    {
      /* if the numeric's precision is 19 or more, then it can get the bigint enough */
      if (desired_domain->type->id == DB_TYPE_NUMERIC && desired_domain->precision < DB_BIGINT_PRECISION)
    {
      INT64 bigint;

      assert (desired_domain->precision >= 0);

      switch (src->domain.general_info.type)
        {
        case DB_TYPE_BIGINT:
          bigint = db_get_bigint (src);
          break;
        case DB_TYPE_INTEGER:
          bigint = db_get_int (src);
          break;
        case DB_TYPE_SHORT:
          bigint = db_get_short (src);
          break;
        default:
          /* never here */
          assert (false);
          bigint = 0;
          break;
        }

      if ((bigint > 0 && (bigint >= max_value[desired_domain->precision]))
          || ((bigint < 0) && ((-bigint) >= max_value[desired_domain->precision])))
        {
          /* can not coerce for overflow */
          dom_status = DOMAIN_OVERFLOW;
        }
    }
    }

  if (dom_status != DOMAIN_OVERFLOW)
    {
      dom_status = tp_value_auto_cast (src, dest, desired_domain);
    }

  return dom_status;
}

/*
 * tp_value_auto_cast - Cast a value into one of another domain, returning an
 *          error only
 *    return: TP_DOMAIN_STATUS
 *    src(in): src DB_VALUE
 *    dest(out): dest DB_VALUE
 *    desired_domain(in): destion domain
 *
 *  Note : this function is used at execution stage, by operators performing
 *     late binding on Host Variables; the automatic cast is a replacement
 *     for implicit cast performed at type-checking for operators that do
 *     not require late binding.
 */
TP_DOMAIN_STATUS
tp_value_auto_cast (const DB_VALUE * src, DB_VALUE * dest, const TP_DOMAIN * desired_domain)
{
  TP_DOMAIN_STATUS status;

  status = tp_value_cast (src, dest, desired_domain, false);
  if (status != DOMAIN_COMPATIBLE)
    {
      if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true)
    {
      status = DOMAIN_COMPATIBLE;
      pr_clear_value (dest);
      db_make_null (dest);
      er_clear ();
    }
    }

  return status;
}

/*
 * tp_value_str_auto_cast_to_number () - checks if the original value
 *    is of type string, and cast it to a DOUBLE type domain.
 *   return: error code.
 *   src(in): source DB_VALUE
 *   dest(out): destination DB_VALUE
 *   val_type(in/out): db type of value; modified if the cast is performed
 *
 *  Note : this is a helper function used by arithmetic functions to accept
 *     string arguments.
 */
int
tp_value_str_auto_cast_to_number (DB_VALUE * src, DB_VALUE * dest, DB_TYPE * val_type)
{
  TP_DOMAIN *cast_dom = NULL;
  TP_DOMAIN_STATUS dom_status;
  int er_status = NO_ERROR;

  assert (src != NULL);
  assert (dest != NULL);
  assert (val_type != NULL);
  assert (TP_IS_CHAR_TYPE (*val_type));
  assert (src != dest);

  db_make_null (dest);

  /* cast string to DOUBLE */
  cast_dom = tp_domain_resolve_default (DB_TYPE_DOUBLE);
  if (cast_dom == NULL)
    {
      return ER_FAILED;
    }

  dom_status = tp_value_auto_cast (src, dest, cast_dom);
  if (dom_status != DOMAIN_COMPATIBLE)
    {
      er_status = tp_domain_status_er_set (dom_status, ARG_FILE_LINE, src, cast_dom);

      pr_clear_value (dest);
      return er_status;
    }

  *val_type = DB_VALUE_DOMAIN_TYPE (dest);

  return NO_ERROR;
}

/*
 * tp_infer_common_domain () -
 *   return:
 *
 *   arg1(in):
 *   arg2(in):
 *
 *  Note :
 */
TP_DOMAIN *
tp_infer_common_domain (TP_DOMAIN * arg1, TP_DOMAIN * arg2)
{
  TP_DOMAIN *target_domain;
  DB_TYPE arg1_type, arg2_type, common_type;
  bool need_to_domain_update = false;

  assert (arg1 && arg2);

  arg1_type = arg1->type->id;
  arg2_type = arg2->type->id;

  if (arg1_type == arg2_type)
    {
      common_type = arg1_type;
      target_domain = tp_domain_copy (arg1, false);
      need_to_domain_update = true;
    }
  else if (arg1_type == DB_TYPE_NULL)
    {
      common_type = arg2_type;
      target_domain = tp_domain_copy (arg2, false);
    }
  else if (arg2_type == DB_TYPE_NULL)
    {
      common_type = arg1_type;
      target_domain = tp_domain_copy (arg1, false);
    }
  else if ((TP_IS_BIT_TYPE (arg1_type) && TP_IS_BIT_TYPE (arg2_type))
       || (TP_IS_CHAR_TYPE (arg1_type) && TP_IS_CHAR_TYPE (arg2_type))
       || (TP_IS_DATE_TYPE (arg1_type) && TP_IS_DATE_TYPE (arg2_type))
       || (TP_IS_SET_TYPE (arg1_type) && TP_IS_SET_TYPE (arg2_type))
       || (TP_IS_NUMERIC_TYPE (arg1_type) && TP_IS_NUMERIC_TYPE (arg2_type)))
    {
      if (tp_more_general_type (arg1_type, arg2_type) > 0)
    {
      common_type = arg1_type;
      target_domain = tp_domain_copy (arg1, false);
    }
      else
    {
      common_type = arg2_type;
      target_domain = tp_domain_copy (arg2, false);
    }
      need_to_domain_update = true;
    }
  else
    {
      common_type = DB_TYPE_VARCHAR;
      target_domain = db_type_to_db_domain (common_type);
    }

  if (need_to_domain_update)
    {
      int arg1_prec, arg2_prec, arg1_scale, arg2_scale;

      arg1_prec = arg1->precision;
      arg1_scale = arg1->scale;

      arg2_prec = arg2->precision;
      arg2_scale = arg2->scale;

      if (arg1_prec == TP_FLOATING_PRECISION_VALUE || arg2_prec == TP_FLOATING_PRECISION_VALUE)
    {
      target_domain->precision = TP_FLOATING_PRECISION_VALUE;
      target_domain->scale = 0;
    }
      else if (common_type == DB_TYPE_NUMERIC)
    {
      int integral_digits1, integral_digits2;

      integral_digits1 = arg1_prec - arg1_scale;
      integral_digits2 = arg2_prec - arg2_scale;
      target_domain->scale = MAX (arg1_scale, arg2_scale);
      target_domain->precision = (target_domain->scale + MAX (integral_digits1, integral_digits2));
      target_domain->precision = MIN (target_domain->precision, DB_MAX_NUMERIC_PRECISION);
    }
      else
    {
      target_domain->precision = MAX (arg1_prec, arg2_prec);
      target_domain->scale = 0;
    }
    }

  target_domain = tp_domain_cache (target_domain);
  return target_domain;
}

/*
 * tp_domain_status_er_set () -
 *   return:
 *
 *  Note :
 */
int
tp_domain_status_er_set (TP_DOMAIN_STATUS status, const char *file_name, const int line_no, const DB_VALUE * src,
             const TP_DOMAIN * domain)
{
  int error = NO_ERROR;

  assert (src != NULL);
  assert (domain != NULL);

  /* prefer to change error code; hide internal errors from users view */
  if (status == DOMAIN_ERROR)
    {
#if 0               /* TODO */
      assert (er_errid () != NO_ERROR);
#endif
      error = er_errid ();

      if (error == ER_JSON_INVALID_JSON)
    {
      // We keep invalid json errors that happend during cast since they are more descriptive
      return error;
    }

      if (error == ER_IT_DATA_OVERFLOW)
    {
      status = DOMAIN_OVERFLOW;
    }
      else
    {
      status = DOMAIN_INCOMPATIBLE;
    }
    }

  assert (status != DOMAIN_ERROR);

  switch (status)
    {
    case DOMAIN_INCOMPATIBLE:
      error = ER_TP_CANT_COERCE;
      er_set (ER_ERROR_SEVERITY, file_name, line_no, error, 2, pr_type_name (DB_VALUE_DOMAIN_TYPE (src)),
          pr_type_name (TP_DOMAIN_TYPE (domain)));
      break;

    case DOMAIN_OVERFLOW:
      error = ER_IT_DATA_OVERFLOW;
      er_set (ER_ERROR_SEVERITY, file_name, line_no, error, 1, pr_type_name (TP_DOMAIN_TYPE (domain)));
      break;

    case DOMAIN_ERROR:
      assert (false);       /* is impossible */
      break;

    default:
      assert (false);       /* is impossible */
      break;
    }

  return error;
}

/*
 * tp_digit_number_str_to_bi - Coerce a digit number string to a bigint.
 *    return: NO_ERROR or error code
 *    start(in): string start pos
 *    end(in): string end pos
 *    codeset(in):
 *    is_negative(in):
 *    num_value(out): bigint container
 *    data_stat(out): if overflow is detected, this is set to
 *                    DATA_STATUS_TRUNCATED
 */
int
tp_digit_number_str_to_bi (const char *start, const char *end, INTL_CODESET codeset, bool is_negative,
               DB_BIGINT * num_value, DB_DATA_STATUS * data_stat)
{
  char str[64] = { 0 };
  const char *p = NULL;
  const char *strp = NULL, *stre = NULL;
  size_t n_digits = 0;
  DB_BIGINT bigint = 0;
  bool round = false;
  bool truncated = false;

  assert (start != NULL && end != NULL && num_value != NULL && data_stat != NULL);

  strp = start;
  stre = end;

  /* count number of significant digits */
  p = strp;

  while (p != stre && char_isdigit (*p))
    {
      p++;
    }

  n_digits = p - strp;
  if (n_digits > 62)
    {
      /* more than 62 significant digits in the input number (63 chars with sign) */
      truncated = true;
    }

  /* skip decimal point and the digits after, keep the round flag */
  if (p != stre && *p == '.')
    {
      p++;

      if (p != stre)
    {
      if (char_isdigit (*p))
        {
          if (*p >= '5')
        {
          round = true;
        }

          /* skip all digits after decimal point */
          do
        {
          p++;
        }
          while (p != stre && char_isdigit (*p));
        }
    }
    }

  /* skip trailing whitespace characters */
  p = (char *) intl_skip_spaces (p, stre, codeset);

  if (p != stre)
    {
      /* trailing characters in string */
      return ER_FAILED;
    }

  if (truncated)
    {
      *data_stat = DATA_STATUS_TRUNCATED;
      if (is_negative)
    {
      bigint = DB_BIGINT_MIN;
    }
      else
    {
      bigint = DB_BIGINT_MAX;
    }
    }
  else
    {
      /* Copy the number to str, excluding leading spaces and '0's and trailing spaces. Anything other than leading and
       * trailing spaces already resulted in an error. */
      if (is_negative)
    {
      str[0] = '-';
      strncpy (str + 1, strp, n_digits);
      str[n_digits + 1] = '\0';
      strp = str;
    }
      else
    {
      strp = strncpy (str, strp, n_digits);
      str[n_digits] = '\0';
    }

      errno = 0;
      bigint = strtoll (strp, NULL, 10);

      if (errno == ERANGE)
    {
      *data_stat = DATA_STATUS_TRUNCATED;
    }
      else
    {
      *data_stat = DATA_STATUS_OK;
    }

      /* round number if a '5' or greater digit was found after the decimal point */
      if (round)
    {
      if (is_negative)
        {
          if (bigint > DB_BIGINT_MIN)
        {
          bigint--;
        }
          else
        {
          *data_stat = DATA_STATUS_TRUNCATED;
        }
        }
      else
        {
          if (bigint < DB_BIGINT_MAX)
        {
          bigint++;
        }
          else
        {
          *data_stat = DATA_STATUS_TRUNCATED;
        }
        }
    }
    }

  *num_value = bigint;

  return NO_ERROR;
}

/*
 * tp_hex_str_to_bi - Coerce a hex string to a bigint.
 *    return: NO_ERROR or error code
 *    start(in): string start pos
 *    end(in): string end pos
 *    codeset(in):
 *    is_negative(in):
 *    num_value(out): bigint container
 *    data_stat(out): if overflow is detected, this is set to
 *                    DATA_STATUS_TRUNCATED
 */
int
tp_hex_str_to_bi (const char *start, const char *end, INTL_CODESET codeset, bool is_negative, DB_BIGINT * num_value,
          DB_DATA_STATUS * data_stat)
{
#define HIGHEST_4BITS_OF_UBI 0xF000000000000000

  int error = NO_ERROR;
  const char *p = NULL;
  UINT64 ubi = 0;
  unsigned int tmp_ui = 0;
  bool round = false;
  bool truncated = false;

  assert (start != NULL && end != NULL && num_value != NULL && data_stat != NULL);

  *data_stat = DATA_STATUS_OK;

  /* convert */
  p = start;
  while (p != end)
    {
      /* convert chars one by one */
      if (char_isdigit (*p))
    {
      tmp_ui = *p - '0';
    }
      else if (*p >= 'a' && *p <= 'f')
    {
      tmp_ui = *p - 'a' + 10;
    }
      else if (*p >= 'A' && *p <= 'F')
    {
      tmp_ui = *p - 'A' + 10;
    }
      else if (*p == '.')
    {
      if (++p != end)
        {
          if (char_isxdigit (*p))
        {
          if (*p >= '0' && *p < '8')
            {
              round = false;
            }
          else
            {
              round = true;
            }

          /* check the rest chars */
          while (++p != end && char_isxdigit (*p))
            ;

          /* skip trailing whitespace characters */
          p = (char *) intl_skip_spaces (p, end, codeset);
          if (p != end)
            {
              error = ER_FAILED;
              goto end;
            }
        }
          else
        {
          /* skip trailing whitespace characters */
          p = (char *) intl_skip_spaces (p, end, codeset);
          if (p != end)
            {
              error = ER_FAILED;
              goto end;
            }
        }
        }

      break;
    }
      else
    {
      /* skip trailing whitespace characters */
      p = (char *) intl_skip_spaces (p, end, codeset);
      if (p != end)
        {
          error = ER_FAILED;
          goto end;
        }

      break;
    }

      if (ubi & HIGHEST_4BITS_OF_UBI)
    {
      truncated = true;
      break;
    }

      ubi = ubi << 4;

      /* never overflow */
      ubi += tmp_ui;

      ++p;
    }

  *num_value = tp_ubi_to_bi_with_args (ubi, is_negative, truncated, round, data_stat);

end:

  return error;

#undef HIGHEST_4BITS_OF_UBI
}

/*
 * tp_scientific_str_to_bi - Coerce a scientific string to a bigint.
 *    return: NO_ERROR or error code
 *    start(in): string start pos
 *    end(in): string end pos
 *    codeset(in):
 *    is_negative(in):
 *    num_value(out): bigint container
 *    data_stat(out): if overflow is detected, this is set to
 *                    DATA_STATUS_TRUNCATED
 *
 *    NOTE: format check has been done by caller already
 *          see tp_atobi
 */
int
tp_scientific_str_to_bi (const char *start, const char *end, INTL_CODESET codeset, bool is_negative,
             DB_BIGINT * num_value, DB_DATA_STATUS * data_stat)
{
  int error = NO_ERROR;
  UINT64 ubi = 0;
  bool truncated = false;
  bool round = false;
  const char *p = NULL;
  const char *base_int_start = NULL, *base_int_end = NULL;
  const char *base_float_start = NULL, *base_float_end = NULL;
  const char *exp_start = NULL, *exp_end = NULL;
  bool is_exp_negative = false;
  int exp = 0;          /* at most 308 */

  assert (start != NULL && end != NULL && num_value != NULL && data_stat != NULL);

  *data_stat = DATA_STATUS_OK;

  base_int_start = start;
  p = base_int_start;
  while (p != end && char_isdigit (*p))
    {
      ++p;
    }

  base_int_end = p;

  /* no int part */
  if (base_int_start == base_int_end)
    {
      base_int_start = NULL;
      base_int_end = NULL;
    }

  if (p != end && *p == '.')
    {
      ++p;
      if (p != end)
    {
      base_float_start = p;
      while (p != end && char_isdigit (*p))
        {
          ++p;
        }

      base_float_end = p;

      /* no float part */
      if (base_float_start == base_float_end)
        {
          base_float_start = NULL;
          base_float_end = NULL;
        }
    }
    }

  /* this is an error */
  if (base_int_start == NULL && base_float_start == NULL)
    {
      error = ER_FAILED;
      goto end;
    }

  if (p != end && (*p == 'e' || *p == 'E'))
    {
      ++p;
      if (p != end)
    {
      if (*p == '-')
        {
          is_exp_negative = true;
          ++p;
        }
      else if (*p == '+')
        {
          ++p;
        }

      exp_start = p;
      while (p != end && char_isdigit (*p))
        {
          ++p;
        }

      exp_end = p;

      /* no exp part */
      if (exp_start == exp_end)
        {
          error = ER_FAILED;
          goto end;
        }
    }
    }

  if (exp_start == NULL)
    {
      error = ER_FAILED;
      goto end;
    }

  /* start to calculate */

  /* exponent */
  p = exp_start;
  while (p != exp_end)
    {
      exp = exp * 10 + (*p - '0');
      if (exp > 308)
    {
      error = ER_FAILED;
      goto end;
    }

      ++p;
    }

  if (is_exp_negative)
    {
      exp = -exp;
    }

  /* calculate int part */
  if (base_int_start != NULL)
    {
      assert (base_int_end != NULL);

      if (exp < 0)
    {
      if (base_int_end - base_int_start >= -exp)
        {
          base_int_end += exp;
        }
      else
        {
          base_int_end = base_int_start - 1;
        }
    }

      p = base_int_start;
      while (p < base_int_end)
    {
      ubi = tp_ubi_times_ten (ubi, &truncated);
      if (truncated)
        {
          break;
        }

      /* never overflow */
      ubi = ubi + *p - '0';

      ++p;
    }

      /* need round ? */
      if (exp < 0 && base_int_end >= base_int_start && *base_int_end >= '5')
    {
      round = true;
    }
    }

  /* calculate float part */
  if (!truncated)
    {
      if (exp > 0)
    {
      if (base_float_start != NULL)
        {
          assert (base_float_end != NULL);

          p = base_float_start;
          while (p != base_float_end && exp > 0)
        {
          ubi = tp_ubi_times_ten (ubi, &truncated);
          if (truncated)
            {
              break;
            }

          /* never overflow */
          ubi = ubi + *p - '0';

          ++p;
          --exp;
        }

          /* need round ? */
          if (p != base_float_end && *p >= '5')
        {
          round = true;
        }
        }

      /* exp */
      if (!truncated)
        {
          while (exp > 0)
        {
          ubi = tp_ubi_times_ten (ubi, &truncated);
          if (truncated)
            {
              break;
            }

          --exp;
        }
        }
    }
      else if (exp == 0 && base_float_start != NULL && *base_float_start >= '5')
    {
      round = true;
    }
    }


  *num_value = tp_ubi_to_bi_with_args (ubi, is_negative, truncated, round, data_stat);

end:

  return error;
}

/*
 * tp_ubi_to_bi_with_args -
 *    return: bigint
 *    ubi(in): unsigned bigint
 *    is_negative(in):
 *    truncated(in):
 *    round(in):
 *    data_stat(out): if overflow is detected, this is set to
 *                    DATA_STATUS_TRUNCATED
 *
 *    NOTE: This is an internal function for convert string to bigint
 */
DB_BIGINT
tp_ubi_to_bi_with_args (UINT64 ubi, bool is_negative, bool truncated, bool round, DB_DATA_STATUS * data_stat)
{
#define HIGHEST_BIT_OF_UINT64 0x8000000000000000

  DB_BIGINT bigint = 0;

  assert (data_stat != NULL);

  if (!truncated)
    {
      if (is_negative)
    {
      if (ubi == HIGHEST_BIT_OF_UINT64)
        {
          bigint = DB_BIGINT_MIN;
        }
      else if (ubi & HIGHEST_BIT_OF_UINT64)
        {
          truncated = true;
        }
      else
        {
          bigint = (DB_BIGINT) ubi;
          bigint = -bigint;
        }
    }
      else
    {
      if (ubi & HIGHEST_BIT_OF_UINT64)
        {
          truncated = true;
        }
      else
        {
          bigint = (DB_BIGINT) ubi;
        }
    }
    }

  if (truncated)
    {
      *data_stat = DATA_STATUS_TRUNCATED;
      if (is_negative)
    {
      bigint = DB_BIGINT_MIN;
    }
      else
    {
      bigint = DB_BIGINT_MAX;
    }
    }
  else if (round)
    {
      if (is_negative)
    {
      if (bigint > DB_BIGINT_MIN)
        {
          --bigint;
        }
      else
        {
          *data_stat = DATA_STATUS_TRUNCATED;
          bigint = DB_BIGINT_MIN;
        }
    }
      else
    {
      if (bigint < DB_BIGINT_MAX)
        {
          ++bigint;
        }
      else
        {
          *data_stat = DATA_STATUS_TRUNCATED;
          bigint = DB_BIGINT_MAX;
        }
    }
    }

  return bigint;

#undef HIGHEST_BIT_OF_UINT64
}

/*
 * tp_ubi_times_ten -
 *    return: bigint
 *    ubi(in): unsigned bigint
 *    truncated(in/out): set to true if truncated
 *
 */
UINT64
tp_ubi_times_ten (UINT64 ubi, bool * truncated)
{
#define HIGHEST_3BITS_OF_UBI 0xE000000000000000

  UINT64 tmp_ubi = 0;

  assert (truncated != NULL);

  if (ubi & HIGHEST_3BITS_OF_UBI)
    {
      *truncated = true;

      goto end;
    }

  /* ubi*10 = ubi*8 + ubi*2 = ubi<<3 + ubi<<1 */
  tmp_ubi = ubi << 3;
  ubi = (ubi << 1) + tmp_ubi;
  if (ubi < tmp_ubi)
    {
      *truncated = true;

      goto end;
    }

end:

  return ubi;

#undef HIGHEST_3BITS_OF_UBI
}