File arithmetic.c¶
File List > cubrid > src > query > arithmetic.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.
*
*/
/*
* arithmetic.c - arithmetic functions
*/
#ident "$Id$"
#include "arithmetic.h"
#include "config.h"
#include "crypt_opfunc.h"
#include "db_date.h"
#include "db_json.hpp"
#include "db_json_path.hpp"
#include "dbtype.h"
#include "error_manager.h"
#include "memory_private_allocator.hpp"
#include "memory_reference_store.hpp"
#include "numeric_opfunc.h"
#include "object_primitive.h"
#include "object_representation.h"
#include "string_opfunc.h"
#include "tz_support.h"
#include <algorithm>
#include <assert.h>
#include <cctype>
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(SOLARIS)
#include <ieeefp.h>
#endif
// 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) */
static int db_mod_short (DB_VALUE * value, DB_VALUE * value1, DB_VALUE * value2);
static int db_mod_int (DB_VALUE * value, DB_VALUE * value1, DB_VALUE * value2);
static int db_mod_bigint (DB_VALUE * value, DB_VALUE * value1, DB_VALUE * value2);
static int db_mod_float (DB_VALUE * value, DB_VALUE * value1, DB_VALUE * value2);
static int db_mod_double (DB_VALUE * value, DB_VALUE * value1, DB_VALUE * value2);
static int db_mod_string (DB_VALUE * value, DB_VALUE * value1, DB_VALUE * value2);
static int db_mod_numeric (DB_VALUE * value, DB_VALUE * value1, DB_VALUE * value2);
static int db_mod_monetary (DB_VALUE * value, DB_VALUE * value1, DB_VALUE * value2);
static double round_double (double num, double integer);
static int move_n_days (int *monthp, int *dayp, int *yearp, const int interval);
static int round_date (DB_VALUE * result, DB_VALUE * value1, DB_VALUE * value2);
static double truncate_double (double num, double integer);
static DB_BIGINT truncate_bigint (DB_BIGINT num, DB_BIGINT integer);
static int truncate_date (DB_DATE * date, const DB_VALUE * format_str);
static int get_number_dbval_as_double (double *d, const DB_VALUE * value);
static int get_number_dbval_as_long_double (long double *ld, const DB_VALUE * value);
static int db_width_bucket_calculate_numeric (double *result, const DB_VALUE * value1, const DB_VALUE * value2,
const DB_VALUE * value3, const DB_VALUE * value4);
static int is_str_find_all (DB_VALUE * val, bool & find_all);
static bool is_any_arg_null (DB_VALUE * const *args, int num_args);
/*
* db_floor_dbval () - take floor of db_value
* return: NO_ERROR
* result(out) : resultant db_value
* value(in) : input db_value
*/
int
db_floor_dbval (DB_VALUE * result, DB_VALUE * value)
{
DB_TYPE res_type;
double dtmp;
int er_status = NO_ERROR;
DB_VALUE cast_value;
res_type = DB_VALUE_DOMAIN_TYPE (value);
if (res_type == DB_TYPE_NULL || DB_IS_NULL (value))
{
return er_status;
}
switch (res_type)
{
case DB_TYPE_SHORT:
db_make_short (result, db_get_short (value));
break;
case DB_TYPE_INTEGER:
db_make_int (result, db_get_int (value));
break;
case DB_TYPE_BIGINT:
db_make_bigint (result, db_get_bigint (value));
break;
case DB_TYPE_FLOAT:
dtmp = floor (db_get_float (value));
db_make_float (result, (float) dtmp);
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
db_make_null (&cast_value);
er_status = tp_value_str_auto_cast_to_number (value, &cast_value, &res_type);
if (er_status != NO_ERROR
|| (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true && res_type != DB_TYPE_DOUBLE))
{
return er_status;
}
assert (res_type == DB_TYPE_DOUBLE);
value = &cast_value;
[[fallthrough]];
case DB_TYPE_DOUBLE:
dtmp = floor (db_get_double (value));
db_make_double (result, (double) dtmp);
break;
case DB_TYPE_NUMERIC:
{
int p = DB_VALUE_PRECISION (value), s = DB_VALUE_SCALE (value);
if (s)
{
unsigned char num[DB_NUMERIC_BUF_SIZE];
char num_str[DB_MAX_NUMERIC_PRECISION * 4 + 2] = { '\0' };
char *num_str_p;
int num_str_len;
bool decrement = false;
num_str_p = num_str + 1;
numeric_coerce_num_to_dec_str (db_get_numeric (value), num_str_p);
num_str_len = strlen (num_str_p);
num_str_p += num_str_len - s;
while (*num_str_p)
{
if (*num_str_p != '0')
{
*num_str_p = '0';
decrement = true;
}
num_str_p++;
}
if (decrement && num_str[1] == '-')
{
/* To decrement a negative value, the absolute value (the digits) actually has to be incremented. */
char *num_str_digits = num_str + num_str_len - p;
bool carry = true;
num_str_p = num_str + num_str_len - s;
while (*num_str_p == '9')
{
*num_str_p-- = '0';
}
if (*num_str_p == '-')
{
num_str[0] = '-';
*num_str_p = '1';
}
else
{
(*num_str_p)++;
carry = false;
}
if (carry || num_str_p <= num_str_digits)
{
if (p < DB_MAX_NUMERIC_PRECISION)
{
p++;
}
else
{
s--;
num_str[num_str_len] = '\0';
}
}
if (num_str[0])
{
num_str_p = num_str;
}
else
{
num_str_p = num_str + 1;
}
numeric_coerce_dec_str_to_num (num_str_p, num);
db_make_numeric (result, num, p, s);
}
else
{
/* given numeric is positive or already rounded */
numeric_coerce_dec_str_to_num (num_str + 1, num);
db_make_numeric (result, num, p, s);
}
}
else
{
/* given numeric number is already of integral type */
db_make_numeric (result, db_get_numeric (value), p, 0);
}
break;
}
case DB_TYPE_MONETARY:
dtmp = (db_get_monetary (value))->amount;
dtmp = floor (dtmp);
db_make_monetary (result, (db_get_monetary (value))->type, dtmp);
break;
default:
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == false)
{
er_status = ER_QPROC_INVALID_DATATYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, er_status, 0);
}
break;
}
return er_status;
}
/*
* db_ceil_dbval () - take ceil of db_value
* return: NO_ERROR
* result(out) : resultant db_value
* value(in) : input db_value
*/
int
db_ceil_dbval (DB_VALUE * result, DB_VALUE * value)
{
DB_TYPE res_type;
double dtmp;
int er_status = NO_ERROR;
DB_VALUE cast_value;
res_type = DB_VALUE_DOMAIN_TYPE (value);
if (res_type == DB_TYPE_NULL || DB_IS_NULL (value))
{
return er_status;
}
switch (res_type)
{
case DB_TYPE_SHORT:
db_make_short (result, db_get_short (value));
break;
case DB_TYPE_INTEGER:
db_make_int (result, db_get_int (value));
break;
case DB_TYPE_BIGINT:
db_make_bigint (result, db_get_bigint (value));
break;
case DB_TYPE_FLOAT:
dtmp = ceil (db_get_float (value));
db_make_float (result, (float) dtmp);
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
db_make_null (&cast_value);
er_status = tp_value_str_auto_cast_to_number (value, &cast_value, &res_type);
if (er_status != NO_ERROR
|| (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true && res_type != DB_TYPE_DOUBLE))
{
return er_status;
}
assert (res_type == DB_TYPE_DOUBLE);
value = &cast_value;
[[fallthrough]];
case DB_TYPE_DOUBLE:
dtmp = ceil (db_get_double (value));
db_make_double (result, (double) dtmp);
break;
case DB_TYPE_NUMERIC:
{
int s = DB_VALUE_SCALE (value), p = DB_VALUE_PRECISION (value);
if (s)
{
char num_str[DB_MAX_NUMERIC_PRECISION * 4 + 2] = { '\0' };
char *num_str_p;
int num_str_len = 0;
bool increment = false;
num_str_p = num_str + 1;
numeric_coerce_num_to_dec_str (db_locate_numeric (value), num_str_p);
if (num_str_p[0] == '-')
{
num_str_p++;
}
num_str_len = strlen (num_str_p);
num_str_p += num_str_len - s;
while (*num_str_p)
{
if (*num_str_p != '0')
{
increment = true;
*num_str_p = '0';
}
num_str_p++;
}
if (increment)
{
unsigned char num[DB_NUMERIC_BUF_SIZE];
if (num_str[1] == '-')
{
/* CEIL(-3.1) is -3.0, as opposed to CEIL(+3.1) which is 4 */
numeric_coerce_dec_str_to_num (num_str + 1, num);
db_make_numeric (result, num, p, s);
}
else
{
bool carry = true;
char *num_str_digits = num_str + 1 + num_str_len - p;
/* position num_str_p one digit in front of the decimal point */
num_str_p = num_str;
num_str_p += num_str_len - s;
while (*num_str_p == '9')
{
*num_str_p-- = '0';
}
if (*num_str_p)
{
(*num_str_p)++;
carry = false;
}
if (carry || num_str_p < num_str_digits)
{
if (carry)
{
*num_str_p = '1';
}
if (p < DB_MAX_NUMERIC_PRECISION)
{
p++;
}
else
{
num_str[num_str_len] = '\0';
s--;
}
}
else
{
num_str_p = num_str + 1;
}
numeric_coerce_dec_str_to_num (num_str_p, num);
db_make_numeric (result, num, p, s);
}
}
else
{
/* the given numeric value is already an integer */
db_make_numeric (result, db_locate_numeric (value), p, s);
}
}
else
{
/* the given numeric value has a scale of 0 */
db_make_numeric (result, db_locate_numeric (value), p, 0);
}
break;
}
case DB_TYPE_MONETARY:
dtmp = (db_get_monetary (value))->amount;
dtmp = ceil (dtmp);
db_make_monetary (result, (db_get_monetary (value))->type, dtmp);
break;
default:
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == false)
{
er_status = ER_QPROC_INVALID_DATATYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, er_status, 0);
}
break;
}
return er_status;
}
/*
* db_sign_dbval - take sign of db_value
* return: NO_ERROR
* result(out) : resultant db_value
* value(in) : input db_value
*/
int
db_sign_dbval (DB_VALUE * result, DB_VALUE * value)
{
DB_TYPE res_type;
int itmp;
DB_BIGINT bitmp;
double dtmp;
int er_status = NO_ERROR;
res_type = DB_VALUE_DOMAIN_TYPE (value);
if (res_type == DB_TYPE_NULL || DB_IS_NULL (value))
{
return er_status;
}
switch (res_type)
{
case DB_TYPE_SHORT:
itmp = db_get_short (value);
if (itmp == 0)
{
db_make_int (result, 0);
}
else if (itmp < 0)
{
db_make_int (result, -1);
}
else
{
db_make_int (result, 1);
}
break;
case DB_TYPE_INTEGER:
itmp = db_get_int (value);
if (itmp == 0)
{
db_make_int (result, 0);
}
else if (itmp < 0)
{
db_make_int (result, -1);
}
else
{
db_make_int (result, 1);
}
break;
case DB_TYPE_BIGINT:
bitmp = db_get_bigint (value);
if (bitmp == 0)
{
db_make_int (result, 0);
}
else if (bitmp < 0)
{
db_make_int (result, -1);
}
else
{
db_make_int (result, 1);
}
break;
case DB_TYPE_FLOAT:
dtmp = db_get_float (value);
if (dtmp == 0)
{
db_make_int (result, 0);
}
else if (dtmp < 0)
{
db_make_int (result, -1);
}
else
{
db_make_int (result, 1);
}
break;
case DB_TYPE_DOUBLE:
dtmp = db_get_double (value);
if (dtmp == 0)
{
db_make_int (result, 0);
}
else if (dtmp < 0)
{
db_make_int (result, -1);
}
else
{
db_make_int (result, 1);
}
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double (db_locate_numeric (value), DB_VALUE_SCALE (value), &dtmp);
if (dtmp == 0)
{
db_make_int (result, 0);
}
else if (dtmp < 0)
{
db_make_int (result, -1);
}
else
{
db_make_int (result, 1);
}
break;
case DB_TYPE_MONETARY:
dtmp = (db_get_monetary (value))->amount;
if (dtmp == 0)
{
db_make_int (result, 0);
}
else if (dtmp < 0)
{
db_make_int (result, -1);
}
else
{
db_make_int (result, 1);
}
break;
default:
break;
}
return er_status;
}
/*
* db_abs_dbval () - take absolute value of db_value
* return: NO_ERROR
* result(out) : resultant db_value
* value(in) : input db_value
*/
int
db_abs_dbval (DB_VALUE * result, DB_VALUE * value)
{
DB_TYPE res_type;
short stmp;
int itmp;
DB_BIGINT bitmp;
double dtmp;
int er_status = NO_ERROR;
DB_VALUE cast_value;
res_type = DB_VALUE_DOMAIN_TYPE (value);
if (res_type == DB_TYPE_NULL || DB_IS_NULL (value))
{
return er_status;
}
switch (res_type)
{
case DB_TYPE_SHORT:
stmp = db_get_short (value);
stmp = abs (stmp);
db_make_short (result, stmp);
break;
case DB_TYPE_INTEGER:
itmp = db_get_int (value);
itmp = abs (itmp);
db_make_int (result, itmp);
break;
case DB_TYPE_BIGINT:
bitmp = db_get_bigint (value);
bitmp = llabs (bitmp);
db_make_bigint (result, bitmp);
break;
case DB_TYPE_FLOAT:
dtmp = db_get_float (value);
dtmp = fabs (dtmp);
db_make_float (result, (float) dtmp);
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
db_make_null (&cast_value);
er_status = tp_value_str_auto_cast_to_number (value, &cast_value, &res_type);
if (er_status != NO_ERROR
|| (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true && res_type != DB_TYPE_DOUBLE))
{
return er_status;
}
assert (res_type == DB_TYPE_DOUBLE);
value = &cast_value;
[[fallthrough]];
case DB_TYPE_DOUBLE:
dtmp = db_get_double (value);
dtmp = fabs (dtmp);
db_make_double (result, (double) dtmp);
break;
case DB_TYPE_NUMERIC:
{
unsigned char num[DB_NUMERIC_BUF_SIZE];
numeric_db_value_abs (db_locate_numeric (value), num);
db_make_numeric (result, num, DB_VALUE_PRECISION (value), DB_VALUE_SCALE (value));
break;
}
case DB_TYPE_MONETARY:
dtmp = (db_get_monetary (value))->amount;
dtmp = fabs (dtmp);
db_make_monetary (result, (db_get_monetary (value))->type, dtmp);
break;
default:
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == false)
{
er_status = ER_QPROC_INVALID_DATATYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, er_status, 0);
}
break;
}
return er_status;
}
/*
* db_exp_dbval () - take exponential value of db_value
* return: NO_ERROR
* result(out) : resultant db_value
* value(in) : input db_value
*/
int
db_exp_dbval (DB_VALUE * result, DB_VALUE * value)
{
DB_TYPE type;
short s;
int i;
float f;
double d;
double dtmp;
DB_BIGINT bi;
type = DB_VALUE_DOMAIN_TYPE (value);
if (type == DB_TYPE_NULL || DB_IS_NULL (value))
{
return NO_ERROR;
}
switch (type)
{
case DB_TYPE_SHORT:
s = db_get_short (value);
dtmp = exp ((double) s);
break;
case DB_TYPE_INTEGER:
i = db_get_int (value);
dtmp = exp ((double) i);
break;
case DB_TYPE_BIGINT:
bi = db_get_bigint (value);
dtmp = exp ((double) bi);
break;
case DB_TYPE_FLOAT:
f = db_get_float (value);
dtmp = exp (f);
break;
case DB_TYPE_DOUBLE:
d = db_get_double (value);
dtmp = exp (d);
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double (db_locate_numeric (value), DB_VALUE_SCALE (value), &d);
dtmp = exp (d);
break;
case DB_TYPE_MONETARY:
d = (db_get_monetary (value))->amount;
dtmp = exp (d);
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_FAILED;
}
if (OR_CHECK_DOUBLE_OVERFLOW (dtmp))
{
goto exp_overflow;
}
db_make_double (result, dtmp);
return NO_ERROR;
exp_overflow:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_OVERFLOW_EXP, 0);
return ER_FAILED;
}
/*
* db_sqrt_dbval () - take sqrt value of db_value
* return: NO_ERROR
* result(out): resultant db_value
* value(in) : input db_value
*/
int
db_sqrt_dbval (DB_VALUE * result, DB_VALUE * value)
{
DB_TYPE type;
short s;
int i;
float f;
double d;
double dtmp;
DB_BIGINT bi;
type = DB_VALUE_DOMAIN_TYPE (value);
if (type == DB_TYPE_NULL || DB_IS_NULL (value))
{
return NO_ERROR;
}
switch (type)
{
case DB_TYPE_SHORT:
s = db_get_short (value);
if (s < 0)
{
goto sqrt_error;
}
dtmp = sqrt ((double) s);
break;
case DB_TYPE_INTEGER:
i = db_get_int (value);
if (i < 0)
{
goto sqrt_error;
}
dtmp = sqrt ((double) i);
break;
case DB_TYPE_BIGINT:
bi = db_get_bigint (value);
if (bi < 0)
{
goto sqrt_error;
}
dtmp = sqrt ((double) bi);
break;
case DB_TYPE_FLOAT:
f = db_get_float (value);
if (f < 0)
{
goto sqrt_error;
}
dtmp = sqrt (f);
break;
case DB_TYPE_DOUBLE:
d = db_get_double (value);
if (d < 0)
{
goto sqrt_error;
}
dtmp = sqrt (d);
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double (db_locate_numeric (value), DB_VALUE_SCALE (value), &d);
if (d < 0)
{
goto sqrt_error;
}
dtmp = sqrt (d);
break;
case DB_TYPE_MONETARY:
d = (db_get_monetary (value))->amount;
if (d < 0)
{
goto sqrt_error;
}
dtmp = sqrt (d);
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_FAILED;
break;
}
db_make_double (result, dtmp);
return NO_ERROR;
sqrt_error:
if (prm_get_integer_value (PRM_ID_COMPAT_MODE) == COMPAT_MYSQL)
{
db_make_null (result);
return NO_ERROR;
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_FUNCTION_ARG_ERROR, 1, "sqrt()");
}
return ER_FAILED;
}
/*
* db_power_dbval () - take power value of db_value
* return: NO_ERROR, ER_FAILED
* result(out) : resultant db_value
* value1(in) : first db_value
* value2(in) : second db_value
*/
int
db_power_dbval (DB_VALUE * result, DB_VALUE * value1, DB_VALUE * value2)
{
double d1, d2;
double dtmp;
int error = NO_ERROR;
if (DB_IS_NULL (value1) || DB_IS_NULL (value2))
{
db_make_null (result);
return NO_ERROR;
}
error = get_number_dbval_as_double (&d1, value1);
if (error != NO_ERROR)
{
goto pow_error;
}
error = get_number_dbval_as_double (&d2, value2);
if (error != NO_ERROR)
{
goto pow_error;
}
if (d1 < 0 && d2 != ceil (d2))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_POWER_ERROR, 0);
goto pow_error;
}
dtmp = pow (d1, d2);
if (OR_CHECK_DOUBLE_OVERFLOW (dtmp))
{
goto pow_overflow;
}
db_make_double (result, dtmp);
return NO_ERROR;
pow_overflow:
if (prm_get_integer_value (PRM_ID_COMPAT_MODE) == COMPAT_MYSQL)
{
db_make_null (result);
return NO_ERROR;
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_OVERFLOW_POWER, 0);
return ER_FAILED;
}
pow_error:
if (prm_get_integer_value (PRM_ID_COMPAT_MODE) == COMPAT_MYSQL)
{
db_make_null (result);
return NO_ERROR;
}
else
{
return ER_FAILED;
}
}
/*
* db_mod_short () - take mod value of value1(short) with value2
* return: NO_ERROR, ER_FAILED
* result(out) : resultant db_value
* value1(in) : short db_value
* value2(in) : second db_value
*/
static int
db_mod_short (DB_VALUE * result, DB_VALUE * value1, DB_VALUE * value2)
{
#if !defined(NDEBUG)
DB_TYPE type1;
#endif
DB_TYPE type2;
short s1, s2;
int i2;
float f2;
double d2;
DB_BIGINT bi2;
double dtmp;
DB_DATA_STATUS data_stat;
unsigned char num[DB_NUMERIC_BUF_SIZE];
int p, s;
int er_status = NO_ERROR;
DB_VALUE cast_value2;
assert (result != NULL && value1 != NULL && value2 != NULL);
db_make_null (&cast_value2);
#if !defined(NDEBUG)
type1 = DB_VALUE_DOMAIN_TYPE (value1);
assert (type1 == DB_TYPE_SHORT);
#endif
s1 = db_get_short (value1);
type2 = DB_VALUE_DOMAIN_TYPE (value2);
switch (type2)
{
case DB_TYPE_SHORT:
s2 = db_get_short (value2);
if (s2 == 0)
{
db_make_short (result, s1);
}
else
{
db_make_short (result, (short) (s1 % s2));
}
break;
case DB_TYPE_INTEGER:
i2 = db_get_int (value2);
if (i2 == 0)
{
db_make_int (result, s1);
}
else
{
db_make_int (result, (int) (s1 % i2));
}
break;
case DB_TYPE_BIGINT:
bi2 = db_get_bigint (value2);
if (bi2 == 0)
{
db_make_bigint (result, s1);
}
else
{
db_make_bigint (result, (DB_BIGINT) (s1 % bi2));
}
break;
case DB_TYPE_FLOAT:
f2 = db_get_float (value2);
if (f2 == 0)
{
db_make_float (result, s1);
}
else
{
db_make_float (result, (float) fmod ((float) s1, f2));
}
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
er_status = tp_value_str_auto_cast_to_number (value2, &cast_value2, &type2);
if (er_status != NO_ERROR
|| (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true && type2 != DB_TYPE_DOUBLE))
{
goto exit;
}
assert (type2 == DB_TYPE_DOUBLE);
value2 = &cast_value2;
[[fallthrough]];
case DB_TYPE_DOUBLE:
d2 = db_get_double (value2);
if (d2 == 0)
{
db_make_double (result, s1);
}
else
{
db_make_double (result, (double) fmod ((double) s1, d2));
}
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double (db_locate_numeric (value2), DB_VALUE_SCALE (value2), &d2);
if (d2 == 0)
{
(void) numeric_db_value_coerce_to_num (value1, result, &data_stat);
}
else
{
dtmp = fmod ((double) s1, d2);
(void) numeric_internal_double_to_num (dtmp, DB_VALUE_SCALE (value2), num, &p, &s);
db_make_numeric (result, num, p, s);
}
break;
case DB_TYPE_MONETARY:
d2 = (db_get_monetary (value2))->amount;
if (d2 == 0)
{
db_make_monetary (result, (db_get_monetary (value2))->type, s1);
}
else
{
db_make_monetary (result, (db_get_monetary (value2))->type, (double) fmod (s1, d2));
}
break;
default:
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == false)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
er_status = ER_QPROC_INVALID_DATATYPE;
}
goto exit;
}
exit:
return er_status;
}
/*
* db_mod_int () - take mod value of value1(int) with value2
* return: NO_ERROR, ER_FAILED
* result(out) : resultant db_value
* value1(in) : int db_value
* value2(in) : second db_value
*/
static int
db_mod_int (DB_VALUE * result, DB_VALUE * value1, DB_VALUE * value2)
{
#if !defined(NDEBUG)
DB_TYPE type1;
#endif
DB_TYPE type2;
short s2;
int i1, i2;
float f2;
double d2;
DB_BIGINT bi2;
double dtmp;
DB_DATA_STATUS data_stat;
unsigned char num[DB_NUMERIC_BUF_SIZE];
int p, s;
int er_status = NO_ERROR;
DB_VALUE cast_value2;
assert (result != NULL && value1 != NULL && value2 != NULL);
db_make_null (&cast_value2);
#if !defined(NDEBUG)
type1 = DB_VALUE_DOMAIN_TYPE (value1);
assert (type1 == DB_TYPE_INTEGER);
#endif
i1 = db_get_int (value1);
type2 = DB_VALUE_DOMAIN_TYPE (value2);
switch (type2)
{
case DB_TYPE_SHORT:
s2 = db_get_short (value2);
if (s2 == 0)
{
db_make_int (result, i1);
}
else
{
db_make_int (result, (int) (i1 % s2));
}
break;
case DB_TYPE_INTEGER:
i2 = db_get_int (value2);
if (i2 == 0)
{
db_make_int (result, i1);
}
else
{
db_make_int (result, (int) (i1 % i2));
}
break;
case DB_TYPE_BIGINT:
bi2 = db_get_bigint (value2);
if (bi2 == 0)
{
db_make_bigint (result, i1);
}
else
{
db_make_bigint (result, (DB_BIGINT) (i1 % bi2));
}
break;
case DB_TYPE_FLOAT:
f2 = db_get_float (value2);
if (f2 == 0)
{
db_make_float (result, (float) i1);
}
else
{
db_make_float (result, (float) fmod ((float) i1, f2));
}
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
er_status = tp_value_str_auto_cast_to_number (value2, &cast_value2, &type2);
if (er_status != NO_ERROR
|| (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true && type2 != DB_TYPE_DOUBLE))
{
goto exit;
}
assert (type2 == DB_TYPE_DOUBLE);
value2 = &cast_value2;
[[fallthrough]];
case DB_TYPE_DOUBLE:
d2 = db_get_double (value2);
if (d2 == 0)
{
db_make_double (result, i1);
}
else
{
db_make_double (result, (double) fmod ((double) i1, d2));
}
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double (db_locate_numeric (value2), DB_VALUE_SCALE (value2), &d2);
if (d2 == 0)
{
(void) numeric_db_value_coerce_to_num (value1, result, &data_stat);
}
else
{
dtmp = fmod ((double) i1, d2);
(void) numeric_internal_double_to_num (dtmp, DB_VALUE_SCALE (value2), num, &p, &s);
db_make_numeric (result, num, p, s);
}
break;
case DB_TYPE_MONETARY:
d2 = (db_get_monetary (value2))->amount;
if (d2 == 0)
{
db_make_monetary (result, (db_get_monetary (value2))->type, i1);
}
else
{
db_make_monetary (result, (db_get_monetary (value2))->type, (double) fmod ((double) i1, d2));
}
break;
default:
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == false)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
er_status = ER_QPROC_INVALID_DATATYPE;
}
goto exit;
}
exit:
return er_status;
}
/*
* db_mod_bigint () - take mod value of value1(bigint) with value2
* return: NO_ERROR, ER_FAILED
* result(out) : resultant db_value
* value1(in) : bigint db_value
* value2(in) : second db_value
*/
static int
db_mod_bigint (DB_VALUE * result, DB_VALUE * value1, DB_VALUE * value2)
{
#if !defined(NDEBUG)
DB_TYPE type1;
#endif
DB_TYPE type2;
short s2;
int i2;
float f2;
double d2;
DB_BIGINT bi1, bi2;
double dtmp;
DB_DATA_STATUS data_stat;
unsigned char num[DB_NUMERIC_BUF_SIZE];
int p, s;
int er_status = NO_ERROR;
DB_VALUE cast_value2;
assert (result != NULL && value1 != NULL && value2 != NULL);
db_make_null (&cast_value2);
#if !defined(NDEBUG)
type1 = DB_VALUE_DOMAIN_TYPE (value1);
assert (type1 == DB_TYPE_BIGINT);
#endif
bi1 = db_get_bigint (value1);
type2 = DB_VALUE_DOMAIN_TYPE (value2);
switch (type2)
{
case DB_TYPE_SHORT:
s2 = db_get_short (value2);
if (s2 == 0)
{
db_make_bigint (result, bi1);
}
else
{
db_make_bigint (result, (DB_BIGINT) (bi1 % s2));
}
break;
case DB_TYPE_INTEGER:
i2 = db_get_int (value2);
if (i2 == 0)
{
db_make_bigint (result, bi1);
}
else
{
db_make_bigint (result, (DB_BIGINT) (bi1 % i2));
}
break;
case DB_TYPE_BIGINT:
bi2 = db_get_bigint (value2);
if (bi2 == 0)
{
db_make_bigint (result, bi1);
}
else
{
db_make_bigint (result, (DB_BIGINT) (bi1 % bi2));
}
break;
case DB_TYPE_FLOAT:
f2 = db_get_float (value2);
if (f2 == 0)
{
db_make_float (result, (float) bi1);
}
else
{
db_make_float (result, (float) fmod ((double) bi1, (double) f2));
}
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
er_status = tp_value_str_auto_cast_to_number (value2, &cast_value2, &type2);
if (er_status != NO_ERROR
|| (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true && type2 != DB_TYPE_DOUBLE))
{
goto exit;
}
assert (type2 == DB_TYPE_DOUBLE);
value2 = &cast_value2;
[[fallthrough]];
case DB_TYPE_DOUBLE:
d2 = db_get_double (value2);
if (d2 == 0)
{
db_make_double (result, (double) bi1);
}
else
{
db_make_double (result, (double) fmod ((double) bi1, d2));
}
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double (db_locate_numeric (value2), DB_VALUE_SCALE (value2), &d2);
if (d2 == 0)
{
(void) numeric_db_value_coerce_to_num (value1, result, &data_stat);
}
else
{
dtmp = fmod ((double) bi1, d2);
(void) numeric_internal_double_to_num (dtmp, DB_VALUE_SCALE (value2), num, &p, &s);
db_make_numeric (result, num, p, s);
}
break;
case DB_TYPE_MONETARY:
d2 = (db_get_monetary (value2))->amount;
if (d2 == 0)
{
db_make_monetary (result, (db_get_monetary (value2))->type, (double) bi1);
}
else
{
db_make_monetary (result, (db_get_monetary (value2))->type, (double) fmod ((double) bi1, d2));
}
break;
default:
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == false)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
er_status = ER_QPROC_INVALID_DATATYPE;
}
goto exit;
}
exit:
return er_status;
}
/*
* db_mod_float () - take mod value of value1(float) with value2
* return: NO_ERROR, ER_FAILED
* result(out) : resultant db_value
* value1(in) : float db_value
* value2(in) : second db_value
*/
static int
db_mod_float (DB_VALUE * result, DB_VALUE * value1, DB_VALUE * value2)
{
#if !defined(NDEBUG)
DB_TYPE type1;
#endif
DB_TYPE type2;
short s2;
int i2;
float f1, f2;
double d2;
DB_BIGINT bi2;
int er_status = NO_ERROR;
DB_VALUE cast_value2;
assert (result != NULL && value1 != NULL && value2 != NULL);
db_make_null (&cast_value2);
#if !defined(NDEBUG)
type1 = DB_VALUE_DOMAIN_TYPE (value1);
assert (type1 == DB_TYPE_FLOAT);
#endif
f1 = db_get_float (value1);
type2 = DB_VALUE_DOMAIN_TYPE (value2);
switch (type2)
{
case DB_TYPE_SHORT:
s2 = db_get_short (value2);
if (s2 == 0)
{
db_make_float (result, f1);
}
else
{
db_make_float (result, (float) fmod (f1, (float) s2));
}
break;
case DB_TYPE_INTEGER:
i2 = db_get_int (value2);
if (i2 == 0)
{
db_make_float (result, f1);
}
else
{
db_make_float (result, (float) fmod (f1, (float) i2));
}
break;
case DB_TYPE_BIGINT:
bi2 = db_get_bigint (value2);
if (bi2 == 0)
{
db_make_float (result, f1);
}
else
{
db_make_float (result, (float) fmod ((double) f1, (double) bi2));
}
break;
case DB_TYPE_FLOAT:
f2 = db_get_float (value2);
if (f2 == 0)
{
db_make_float (result, f1);
}
else
{
db_make_float (result, (float) fmod (f1, f2));
}
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
er_status = tp_value_str_auto_cast_to_number (value2, &cast_value2, &type2);
if (er_status != NO_ERROR
|| (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true && type2 != DB_TYPE_DOUBLE))
{
goto exit;
}
assert (type2 == DB_TYPE_DOUBLE);
value2 = &cast_value2;
[[fallthrough]];
case DB_TYPE_DOUBLE:
d2 = db_get_double (value2);
if (d2 == 0)
{
db_make_double (result, f1);
}
else
{
db_make_double (result, (double) fmod ((double) f1, d2));
}
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double (db_locate_numeric (value2), DB_VALUE_SCALE (value2), &d2);
/* common type of float and numeric is double. */
if (d2 == 0)
{
db_make_double (result, f1);
}
else
{
db_make_double (result, fmod ((double) f1, d2));
}
break;
case DB_TYPE_MONETARY:
d2 = (db_get_monetary (value2))->amount;
if (d2 == 0)
{
db_make_monetary (result, (db_get_monetary (value2))->type, f1);
}
else
{
db_make_monetary (result, (db_get_monetary (value2))->type, (double) fmod ((double) f1, d2));
}
break;
default:
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == false)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
er_status = ER_QPROC_INVALID_DATATYPE;
}
goto exit;
}
exit:
return er_status;
}
/*
* db_mod_double () - take mod value of value1(double) with value2
* return: NO_ERROR, ER_FAILED
* result(out) : resultant db_value
* value1(in) : double db_value
* value2(in) : second db_value
*/
static int
db_mod_double (DB_VALUE * result, DB_VALUE * value1, DB_VALUE * value2)
{
#if !defined(NDEBUG)
DB_TYPE type1;
#endif
DB_TYPE type2;
short s2;
int i2;
float f2;
double d1, d2;
DB_BIGINT bi2;
int er_status = NO_ERROR;
DB_VALUE cast_value2;
assert (result != NULL && value1 != NULL && value2 != NULL);
db_make_null (&cast_value2);
#if !defined(NDEBUG)
type1 = DB_VALUE_DOMAIN_TYPE (value1);
assert (type1 == DB_TYPE_DOUBLE);
#endif
d1 = db_get_double (value1);
type2 = DB_VALUE_DOMAIN_TYPE (value2);
switch (type2)
{
case DB_TYPE_SHORT:
s2 = db_get_short (value2);
if (s2 == 0)
{
db_make_double (result, d1);
}
else
{
db_make_double (result, (double) fmod (d1, (double) s2));
}
break;
case DB_TYPE_INTEGER:
i2 = db_get_int (value2);
if (i2 == 0)
{
db_make_double (result, d1);
}
else
{
db_make_double (result, (double) fmod (d1, (double) i2));
}
break;
case DB_TYPE_BIGINT:
bi2 = db_get_bigint (value2);
if (bi2 == 0)
{
db_make_double (result, d1);
}
else
{
db_make_double (result, (double) fmod (d1, (double) bi2));
}
break;
case DB_TYPE_FLOAT:
f2 = db_get_float (value2);
if (f2 == 0)
{
db_make_double (result, d1);
}
else
{
db_make_double (result, (double) fmod (d1, (double) f2));
}
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
er_status = tp_value_str_auto_cast_to_number (value2, &cast_value2, &type2);
if (er_status != NO_ERROR
|| (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true && type2 != DB_TYPE_DOUBLE))
{
goto exit;
}
assert (type2 == DB_TYPE_DOUBLE);
value2 = &cast_value2;
[[fallthrough]];
case DB_TYPE_DOUBLE:
d2 = db_get_double (value2);
if (d2 == 0)
{
db_make_double (result, d1);
}
else
{
db_make_double (result, (double) fmod (d1, d2));
}
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double (db_locate_numeric (value2), DB_VALUE_SCALE (value2), &d2);
if (d2 == 0)
{
db_make_double (result, d1);
}
else
{
db_make_double (result, (double) fmod (d1, d2));
}
break;
case DB_TYPE_MONETARY:
d2 = (db_get_monetary (value2))->amount;
if (d2 == 0)
{
db_make_monetary (result, (db_get_monetary (value2))->type, d1);
}
else
{
db_make_monetary (result, (db_get_monetary (value2))->type, (double) fmod (d1, d2));
}
break;
default:
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == false)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
er_status = ER_QPROC_INVALID_DATATYPE;
}
goto exit;
}
exit:
return er_status;
}
/*
* db_mod_string () - take mod value of value1(string) with value2
* return: NO_ERROR, ER_FAILED
* result(out) : resultant db_value
* value1(in) : string db_value
* value2(in) : second db_value
*/
static int
db_mod_string (DB_VALUE * result, DB_VALUE * value1, DB_VALUE * value2)
{
DB_TYPE type1;
int er_status = NO_ERROR;
DB_VALUE cast_value1;
assert (result != NULL && value1 != NULL && value2 != NULL);
db_make_null (&cast_value1);
#if !defined(NDEBUG)
type1 = DB_VALUE_DOMAIN_TYPE (value1);
assert (TP_IS_CHAR_TYPE (type1));
#endif
er_status = tp_value_str_auto_cast_to_number (value1, &cast_value1, &type1);
if (er_status != NO_ERROR
|| (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true && type1 != DB_TYPE_DOUBLE))
{
return er_status;
}
assert (type1 == DB_TYPE_DOUBLE);
value1 = &cast_value1;
return db_mod_double (result, value1, value2);
}
/*
* db_mod_numeric () - take mod value of value1(numeric) with value2
* return: NO_ERROR, ER_FAILED
* result(out) : resultant db_value
* value1(in) : numeric db_value
* value2(in) : second db_value
*/
static int
db_mod_numeric (DB_VALUE * result, DB_VALUE * value1, DB_VALUE * value2)
{
#if !defined(NDEBUG)
DB_TYPE type1;
#endif
DB_TYPE type2;
short s2;
int i2;
float f2;
double d1, d2;
DB_BIGINT bi2;
double dtmp;
DB_DATA_STATUS data_stat;
unsigned char num[DB_NUMERIC_BUF_SIZE];
int p, s;
int er_status = NO_ERROR;
DB_VALUE cast_value2;
assert (result != NULL && value1 != NULL && value2 != NULL);
db_make_null (&cast_value2);
#if !defined(NDEBUG)
type1 = DB_VALUE_DOMAIN_TYPE (value1);
assert (type1 == DB_TYPE_NUMERIC);
#endif
numeric_coerce_num_to_double (db_locate_numeric (value1), DB_VALUE_SCALE (value1), &d1);
type2 = DB_VALUE_DOMAIN_TYPE (value2);
switch (type2)
{
case DB_TYPE_SHORT:
s2 = db_get_short (value2);
if (s2 == 0)
{
(void) numeric_db_value_coerce_to_num (value1, result, &data_stat);
}
else
{
dtmp = fmod (d1, (double) s2);
(void) numeric_internal_double_to_num (dtmp, DB_VALUE_SCALE (value1), num, &p, &s);
db_make_numeric (result, num, p, s);
}
break;
case DB_TYPE_INTEGER:
i2 = db_get_int (value2);
if (i2 == 0)
{
(void) numeric_db_value_coerce_to_num (value1, result, &data_stat);
}
else
{
dtmp = fmod (d1, (double) i2);
(void) numeric_internal_double_to_num (dtmp, DB_VALUE_SCALE (value1), num, &p, &s);
db_make_numeric (result, num, p, s);
}
break;
case DB_TYPE_BIGINT:
bi2 = db_get_bigint (value2);
if (bi2 == 0)
{
(void) numeric_db_value_coerce_to_num (value1, result, &data_stat);
}
else
{
dtmp = fmod (d1, (double) bi2);
(void) numeric_internal_double_to_num (dtmp, DB_VALUE_SCALE (value1), num, &p, &s);
db_make_numeric (result, num, p, s);
}
break;
case DB_TYPE_FLOAT:
f2 = db_get_float (value2);
/* common type of float and numeric is double */
if (f2 == 0)
{
db_make_double (result, d1);
}
else
{
db_make_double (result, fmod (d1, (double) f2));
}
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
er_status = tp_value_str_auto_cast_to_number (value2, &cast_value2, &type2);
if (er_status != NO_ERROR
|| (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true && type2 != DB_TYPE_DOUBLE))
{
goto exit;
}
assert (type2 == DB_TYPE_DOUBLE);
value2 = &cast_value2;
[[fallthrough]];
case DB_TYPE_DOUBLE:
d2 = db_get_double (value2);
if (d2 == 0)
{
db_make_double (result, d1);
}
else
{
db_make_double (result, (double) fmod (d1, d2));
}
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double (db_locate_numeric (value2), DB_VALUE_SCALE (value2), &d2);
if (d2 == 0)
{
(void) numeric_db_value_coerce_to_num (value1, result, &data_stat);
}
else
{
dtmp = fmod (d1, d2);
(void) numeric_internal_double_to_num (dtmp, MAX (DB_VALUE_SCALE (value1), DB_VALUE_SCALE (value2)), num, &p,
&s);
db_make_numeric (result, num, p, s);
}
break;
case DB_TYPE_MONETARY:
d2 = (db_get_monetary (value2))->amount;
if (d2 == 0)
{
db_make_monetary (result, (db_get_monetary (value2))->type, d1);
}
else
{
db_make_monetary (result, (db_get_monetary (value2))->type, (double) fmod (d1, d2));
}
break;
default:
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == false)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
er_status = ER_QPROC_INVALID_DATATYPE;
}
goto exit;
}
exit:
return er_status;
}
/*
* db_mod_monetary () - take mod value of value1(monetary) with value2
* return: NO_ERROR, ER_FAILED
* result(out) : resultant db_value
* value1(in) : monetary db_value
* value2(in) : second db_value
*/
static int
db_mod_monetary (DB_VALUE * result, DB_VALUE * value1, DB_VALUE * value2)
{
#if !defined(NDEBUG)
DB_TYPE type1;
#endif
DB_TYPE type2;
double d1, d2;
int er_status = NO_ERROR;
DB_VALUE cast_value2;
assert (result != NULL && value1 != NULL && value2 != NULL);
db_make_null (&cast_value2);
#if !defined(NDEBUG)
type1 = DB_VALUE_DOMAIN_TYPE (value1);
assert (type1 == DB_TYPE_MONETARY);
#endif
d1 = (db_get_monetary (value1))->amount;
d2 = 0;
type2 = DB_VALUE_DOMAIN_TYPE (value2);
switch (type2)
{
case DB_TYPE_SHORT:
d2 = db_get_short (value2);
break;
case DB_TYPE_INTEGER:
d2 = db_get_int (value2);
break;
case DB_TYPE_BIGINT:
d2 = (double) db_get_bigint (value2);
break;
case DB_TYPE_FLOAT:
d2 = db_get_float (value2);
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
er_status = tp_value_str_auto_cast_to_number (value2, &cast_value2, &type2);
if (er_status != NO_ERROR
|| (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true && type2 != DB_TYPE_DOUBLE))
{
goto exit;
}
assert (type2 == DB_TYPE_DOUBLE);
value2 = &cast_value2;
[[fallthrough]];
case DB_TYPE_DOUBLE:
d2 = db_get_double (value2);
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double (db_locate_numeric (value2), DB_VALUE_SCALE (value2), &d2);
break;
case DB_TYPE_MONETARY:
d2 = (db_get_monetary (value2))->amount;
break;
default:
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == false)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
er_status = ER_QPROC_INVALID_DATATYPE;
}
goto exit;
}
if (d2 == 0)
{
db_make_monetary (result, (db_get_monetary (value1))->type, d1);
}
else
{
db_make_monetary (result, (db_get_monetary (value1))->type, (double) fmod (d1, d2));
}
exit:
return er_status;
}
/*
* db_mod_dbval () - take mod value of db_value
* return: NO_ERROR, ER_FAILED
* result(out) : resultant db_value
* value1(in) : first db_value
* value2(in) : second db_value
*/
int
db_mod_dbval (DB_VALUE * result, DB_VALUE * value1, DB_VALUE * value2)
{
DB_TYPE type1;
if (DB_IS_NULL (value1) || DB_IS_NULL (value2))
{
return NO_ERROR;
}
type1 = DB_VALUE_DOMAIN_TYPE (value1);
switch (type1)
{
case DB_TYPE_SHORT:
return db_mod_short (result, value1, value2);
case DB_TYPE_INTEGER:
return db_mod_int (result, value1, value2);
case DB_TYPE_BIGINT:
return db_mod_bigint (result, value1, value2);
case DB_TYPE_FLOAT:
return db_mod_float (result, value1, value2);
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
return db_mod_string (result, value1, value2);
case DB_TYPE_DOUBLE:
return db_mod_double (result, value1, value2);
case DB_TYPE_NUMERIC:
return db_mod_numeric (result, value1, value2);
case DB_TYPE_MONETARY:
return db_mod_monetary (result, value1, value2);
default:
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == false)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_QPROC_INVALID_DATATYPE;
}
else
{
return NO_ERROR;
}
}
}
/*
* round_double ()
* return: num rounded to integer places to the right of the decimal point
* num(in) :
* integer(in):
*/
static double
round_double (double num, double integer)
{
/*
* Under high optimization level, some optimizers (e.g, gcc -O3 on linux)
* generates a wrong result without "volatile".
*/
volatile double scale_up, result;
if (num == 0)
{
return num;
}
scale_up = pow (10, integer);
if (!FINITE (num * scale_up))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IT_DATA_OVERFLOW, 1, tp_Double_domain.type->name);
}
result = round (num * scale_up) / scale_up;
return result;
}
/*
* move_n_days () - move forward or backward n days from a given date,
*
* return: error code
* yearp(in/out):
* monthp(in/out) :
* dayp(in/out) :
* interval(in) : how many days to move, negative number means back
*/
static int
move_n_days (int *monthp, int *dayp, int *yearp, const int interval)
{
/* no need to judge if arguments are illegal as it has been done previously */
DB_DATE date;
int error;
error = db_date_encode (&date, *monthp, *dayp, *yearp);
if (error != NO_ERROR)
{
return error;
}
date += interval;
db_date_decode (&date, monthp, dayp, yearp);
return NO_ERROR;
}
/*
* db_round_date () - returns a date round by value2('year' | 'month' | 'day')
*
* return: NO_ERROR, ER_FAILED
* result(out): resultant db_value
* value1(in) : first db_value
* value2(in) : second db_value
*/
static int
round_date (DB_VALUE * result, DB_VALUE * value1, DB_VALUE * value2)
{
DB_DATETIME *pdatetime, local_dt;
DB_DATETIMETZ *pdatetimetz;
DB_TIMESTAMP *pstamp;
DB_TIMESTAMPTZ *pstamptz;
DB_DATE *pdate;
DB_DATE date;
DB_TIME time;
DB_TYPE type;
int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0, millisecond = 0;
int weekday = 0;
int error = NO_ERROR;
TIMESTAMP_FORMAT format;
/* get format */
error = db_get_date_format (value2, &format);
if (error != NO_ERROR)
{
goto end;
}
/* get all values in the date */
type = DB_VALUE_DOMAIN_TYPE (value1);
if (type == DB_TYPE_TIMESTAMP || type == DB_TYPE_TIMESTAMPLTZ)
{
pstamp = db_get_timestamp (value1);
(void) db_timestamp_decode_ses (pstamp, &date, &time);
db_date_decode (&date, &month, &day, &year);
db_time_decode (&time, &hour, &minute, &second);
}
else if (type == DB_TYPE_TIMESTAMPTZ)
{
pstamptz = db_get_timestamptz (value1);
error = db_timestamp_decode_w_tz_id (&pstamptz->timestamp, &pstamptz->tz_id, &date, &time);
if (error != NO_ERROR)
{
goto end;
}
db_date_decode (&date, &month, &day, &year);
db_time_decode (&time, &hour, &minute, &second);
}
else if (type == DB_TYPE_DATETIME)
{
pdatetime = db_get_datetime (value1);
db_datetime_decode (pdatetime, &month, &day, &year, &hour, &minute, &second, &millisecond);
}
else if (type == DB_TYPE_DATETIMELTZ)
{
pdatetime = db_get_datetime (value1);
error = tz_datetimeltz_to_local (pdatetime, &local_dt);
if (error != NO_ERROR)
{
goto end;
}
db_datetime_decode (&local_dt, &month, &day, &year, &hour, &minute, &second, &millisecond);
}
else if (type == DB_TYPE_DATETIMETZ)
{
pdatetimetz = db_get_datetimetz (value1);
error = tz_utc_datetimetz_to_local (&pdatetimetz->datetime, &pdatetimetz->tz_id, &local_dt);
if (error != NO_ERROR)
{
goto end;
}
db_datetime_decode (&local_dt, &month, &day, &year, &hour, &minute, &second, &millisecond);
}
else if (type == DB_TYPE_DATE)
{
pdate = db_get_date (value1);
db_date_decode (pdate, &month, &day, &year);
hour = minute = second = millisecond = 0;
}
else
{
error = ER_QPROC_INVALID_DATATYPE;
goto end;
}
/* apply round according to format */
switch (format)
{
case DT_YYYY:
case DT_YY:
if (month >= 7) /* rounds up on July 1 */
{
year++;
}
month = 1;
day = 1;
break;
case DT_MONTH:
case DT_MON:
case DT_MM:
if (day >= 16) /* rounds up on the 16th days */
{
if (month == 12)
{
year++;
month = 1;
}
else
{
month++;
}
}
day = 1;
break;
case DT_DD:
if (hour >= 12) /* rounds up on 12:00 AM */
{
error = move_n_days (&month, &day, &year, 1);
if (error != NO_ERROR)
{
goto end;
}
}
break;
case DT_HH24:
case DT_HH12:
case DT_HH:
case DT_H:
if (minute >= 30) /* rounds up on HH:30 */
{
if (++hour == 24) /* rounds up to next day */
{
error = move_n_days (&month, &day, &year, 1);
if (error != NO_ERROR)
{
goto end;
}
}
}
break;
case DT_MI:
if (second >= 30) /* rounds up on HH:MM:30 */
{
if (++minute == 60)
{
if (++hour == 24)
{
error = move_n_days (&month, &day, &year, 1);
if (error != NO_ERROR)
{
goto end;
}
}
}
}
break;
case DT_SS:
if (millisecond >= 500) /* rounds up on HH:MM:SS.500 */
{
if (++second == 60)
{
if (++minute == 60)
{
if (++hour == 24)
{
error = move_n_days (&month, &day, &year, 1);
if (error != NO_ERROR)
{
goto end;
}
}
}
}
}
break;
case DT_MS: /* do nothing */
break;
case DT_Q: /* quarter */
/* rounds up on the 16th day of the second month of the quarter */
if (month < 2 || (month == 2 && day < 16))
{
month = 1;
}
else if (month < 5 || (month == 5 && day < 16))
{
month = 4;
}
else if (month < 8 || (month == 8 && day < 16))
{
month = 7;
}
else if (month < 11 || (month == 11 && day < 16))
{
month = 10;
}
else
{
month = 1;
year++;
}
day = 1;
break;
case DT_DAY:
case DT_DY: /* rounds up on thursday of a week */
case DT_D:
if (hour >= 12) /* first round 'dd' */
{
error = move_n_days (&month, &day, &year, 1);
if (error != NO_ERROR)
{
goto end;
}
}
weekday = day_of_week (julian_encode (month, day, year));
if (weekday < 4)
{
error = move_n_days (&month, &day, &year, -weekday);
}
else
{
error = move_n_days (&month, &day, &year, 7 - weekday);
}
if (error != NO_ERROR)
{
goto end;
}
break;
case DT_CC:
default:
error = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
goto end;
}
/* check for boundary and throw overflow */
if (year < 0 || year > 9999)
{
error = ER_IT_DATA_OVERFLOW;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, tp_Date_domain.type->name);
goto end;
}
/* re-create new date */
error = db_make_date (result, month, day, year);
end:
if (error != NO_ERROR && prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
error = NO_ERROR;
db_make_null (result);
er_clear ();
}
return error;
}
/*
* db_round_dbval () - returns value1 rounded to value2 places right of
* the decimal point
* return: NO_ERROR, ER_FAILED
* result(out): resultant db_value
* value1(in) : first db_value
* value2(in) : second db_value
*/
int
db_round_dbval (DB_VALUE * result, DB_VALUE * value1, DB_VALUE * value2)
{
DB_TYPE type1, type2;
short s1;
int i1;
float f1;
double d1, d2 = 0.0;
DB_BIGINT bi1, bi2, bi_tmp;
double dtmp;
unsigned char num[DB_NUMERIC_BUF_SIZE];
char num_string[(2 * DB_MAX_NUMERIC_PRECISION) + 4];
char *ptr, *end;
int need_round = 0;
int p, s;
DB_VALUE cast_value, cast_format;
int er_status = NO_ERROR;
TP_DOMAIN *domain = NULL;
db_make_null (&cast_value);
db_make_null (&cast_format);
if (DB_IS_NULL (value1) || DB_IS_NULL (value2))
{
return NO_ERROR;
}
type1 = DB_VALUE_DOMAIN_TYPE (value1);
type2 = DB_VALUE_DOMAIN_TYPE (value2);
/* first check if round a date: */
if (type1 == DB_TYPE_DATETIME || type1 == DB_TYPE_DATETIMELTZ || type1 == DB_TYPE_DATETIMETZ
|| type1 == DB_TYPE_TIMESTAMP || type1 == DB_TYPE_TIMESTAMPLTZ || type1 == DB_TYPE_TIMESTAMPTZ
|| type1 == DB_TYPE_DATE)
{ /* round date */
if (QSTR_IS_ANY_CHAR (type2) && strcasecmp (DB_GET_STRING_SAFE (value2), "default") == 0)
{
db_make_string (&cast_format, "dd");
value2 = &cast_format;
}
return round_date (result, value1, value2);
}
/* cast value1 to double */
if (!TP_IS_NUMERIC_TYPE (type1))
{
type1 = DB_TYPE_UNKNOWN;
/* try type double */
domain = tp_domain_resolve_default (DB_TYPE_DOUBLE);
if (tp_value_coerce (value1, &cast_value, domain) != DOMAIN_COMPATIBLE)
{
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_clear ();
return NO_ERROR;
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_QPROC_INVALID_DATATYPE;
}
}
type1 = DB_TYPE_DOUBLE;
value1 = &cast_value;
}
/* get value2 */
if (type2 == DB_TYPE_INTEGER)
{
d2 = (double) db_get_int (value2);
}
else if (type2 == DB_TYPE_BIGINT)
{
d2 = (double) db_get_bigint (value2);
}
else if (type2 == DB_TYPE_SHORT)
{
d2 = (double) db_get_short (value2);
}
else if (type2 == DB_TYPE_DOUBLE)
{
d2 = db_get_double (value2);
}
else /* cast to INTEGER */
{
if (QSTR_IS_ANY_CHAR (type2) && strcasecmp (DB_GET_STRING_SAFE (value2), "default") == 0)
{
db_make_int (&cast_format, 0);
value2 = &cast_format;
type2 = DB_TYPE_INTEGER;
d2 = 0;
}
else
{
/* try type int */
type2 = DB_TYPE_UNKNOWN;
domain = tp_domain_resolve_default (DB_TYPE_INTEGER);
if (tp_value_coerce (value2, &cast_format, domain) != DOMAIN_COMPATIBLE)
{
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_clear ();
return NO_ERROR;
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_QPROC_INVALID_DATATYPE;
}
}
type2 = DB_TYPE_INTEGER;
value2 = &cast_format;
d2 = db_get_int (value2);
}
}
/* round double */
switch (type1)
{
case DB_TYPE_SHORT:
s1 = db_get_short (value1);
dtmp = round_double (s1, d2);
db_make_short (result, (short) dtmp);
break;
case DB_TYPE_INTEGER:
i1 = db_get_int (value1);
dtmp = round_double (i1, d2);
db_make_int (result, (int) dtmp);
break;
case DB_TYPE_BIGINT:
bi1 = db_get_bigint (value1);
dtmp = round_double ((double) bi1, d2);
bi_tmp = (DB_BIGINT) dtmp;
#if defined(AIX)
/* in AIX, double to long will not overflow, make it the same as linux. */
if (dtmp == (double) DB_BIGINT_MAX)
{
bi_tmp = DB_BIGINT_MIN;
}
#endif
db_make_bigint (result, bi_tmp);
break;
case DB_TYPE_FLOAT:
f1 = db_get_float (value1);
dtmp = round_double (f1, d2);
db_make_float (result, (float) dtmp);
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
db_make_null (&cast_value);
er_status = tp_value_str_auto_cast_to_number (value1, &cast_value, &type1);
if (er_status != NO_ERROR
|| (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true && type1 != DB_TYPE_DOUBLE))
{
return er_status;
}
assert (type1 == DB_TYPE_DOUBLE);
value1 = &cast_value;
[[fallthrough]];
case DB_TYPE_DOUBLE:
d1 = db_get_double (value1);
dtmp = round_double (d1, d2);
db_make_double (result, (double) dtmp);
break;
case DB_TYPE_NUMERIC:
memset (num_string, 0, sizeof (num_string));
numeric_coerce_num_to_dec_str (db_locate_numeric (value1), num_string);
p = DB_VALUE_PRECISION (value1);
s = DB_VALUE_SCALE (value1);
end = num_string + strlen (num_string);
if (type2 == DB_TYPE_BIGINT)
{
bi2 = db_get_bigint (value2);
}
else if (type2 == DB_TYPE_INTEGER)
{
bi2 = db_get_int (value2);
}
else if (type2 == DB_TYPE_SHORT)
{
bi2 = db_get_short (value2);
}
else /* double */
{
bi2 = (DB_BIGINT) db_get_double (value2);
}
ptr = end - s + bi2;
if (end < ptr)
{ /* no need to round, return as it is */
*result = *value1;
break;
}
else if (ptr < num_string)
{ /* return zero */
memset (num_string, 0, sizeof (num_string));
}
else
{
if (*ptr >= '5')
{
need_round = 1;
}
while (ptr < end)
{
*ptr++ = '0';
}
if (need_round)
{
/* round up */
int done = 0;
for (ptr = end - s + bi2 - 1; ptr >= num_string && !done; ptr--)
{
if (*ptr == '9')
{
*ptr = '0';
}
else
{
*ptr += 1;
done = 1;
}
}
for (ptr = num_string; ptr < end; ptr++)
{
if ('1' <= *ptr && *ptr <= '9')
{
if (strlen (ptr) > DB_MAX_NUMERIC_PRECISION)
{
/* overflow happened during round up */
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_clear ();
return NO_ERROR;
}
else
{
domain = tp_domain_resolve_default (DB_TYPE_NUMERIC);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IT_DATA_OVERFLOW, 1,
pr_type_name (TP_DOMAIN_TYPE (domain)));
return ER_IT_DATA_OVERFLOW;
}
}
break;
}
}
}
}
numeric_coerce_dec_str_to_num (num_string, num);
db_make_numeric (result, num, p, s);
break;
case DB_TYPE_MONETARY:
d1 = (db_get_monetary (value1))->amount;
dtmp = round_double (d1, d2);
db_make_monetary (result, (db_get_monetary (value1))->type, dtmp);
break;
default:
if (!prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_QPROC_INVALID_DATATYPE;
}
}
if (er_errid () != NO_ERROR && prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_clear ();
db_make_null (result);
}
return er_errid ();
}
/*
* db_log_dbval () -
* return: NO_ERROR, ER_FAILED
* result(out): resultant db_value
* value1(in) : first db_value
* value2(in) : second db_value
*/
int
db_log_dbval (DB_VALUE * result, DB_VALUE * value1, DB_VALUE * value2)
{
DB_TYPE type1, type2;
short s1, s2;
int i1, i2;
float f1, f2;
double d1, d2;
DB_BIGINT bi1, bi2;
double dtmp = 0.0;
if (DB_IS_NULL (value1) || DB_IS_NULL (value2))
{
return NO_ERROR;
}
type1 = DB_VALUE_DOMAIN_TYPE (value1);
type2 = DB_VALUE_DOMAIN_TYPE (value2);
switch (type1)
{
case DB_TYPE_SHORT:
s1 = db_get_short (value1);
if (s1 <= 1)
{
goto log_error;
}
switch (type2)
{
case DB_TYPE_SHORT:
s2 = db_get_short (value2);
if (s2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) s2) / log10 ((double) s1);
break;
case DB_TYPE_INTEGER:
i2 = db_get_int (value2);
if (i2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) i2) / log10 ((double) s1);
break;
case DB_TYPE_BIGINT:
bi2 = db_get_bigint (value2);
if (bi2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) bi2) / log10 ((double) s1);
break;
case DB_TYPE_FLOAT:
f2 = db_get_float (value2);
if (f2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) f2) / log10 ((double) s1);
break;
case DB_TYPE_DOUBLE:
d2 = db_get_double (value2);
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) d2) / log10 ((double) s1);
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double (db_locate_numeric (value2), DB_VALUE_SCALE (value2), &d2);
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) d2) / log10 ((double) s1);
break;
case DB_TYPE_MONETARY:
d2 = (db_get_monetary (value2))->amount;
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 (d2) / log10 ((double) s1);
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
break;
}
break;
case DB_TYPE_BIGINT:
bi1 = db_get_bigint (value1);
if (bi1 <= 1)
{
goto log_error;
}
switch (type2)
{
case DB_TYPE_SHORT:
s2 = db_get_short (value2);
if (s2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) s2) / log10 ((double) bi1);
break;
case DB_TYPE_INTEGER:
i2 = db_get_int (value2);
if (i2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) i2) / log10 ((double) bi1);
break;
case DB_TYPE_BIGINT:
bi2 = db_get_bigint (value2);
if (bi2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) bi2) / log10 ((double) bi1);
break;
case DB_TYPE_FLOAT:
f2 = db_get_float (value2);
if (f2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) f2) / log10 ((double) bi1);
break;
case DB_TYPE_DOUBLE:
d2 = db_get_double (value2);
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) d2) / log10 ((double) bi1);
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double (db_locate_numeric (value2), DB_VALUE_SCALE (value2), &d2);
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) d2) / log10 ((double) bi1);
break;
case DB_TYPE_MONETARY:
d2 = (db_get_monetary (value2))->amount;
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 (d2) / log10 ((double) bi1);
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
break;
}
break;
case DB_TYPE_INTEGER:
i1 = db_get_int (value1);
if (i1 <= 1)
{
goto log_error;
}
switch (type2)
{
case DB_TYPE_SHORT:
s2 = db_get_short (value2);
if (s2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) s2) / log10 ((double) i1);
break;
case DB_TYPE_INTEGER:
i2 = db_get_int (value2);
if (i2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) i2) / log10 ((double) i1);
break;
case DB_TYPE_BIGINT:
bi2 = db_get_bigint (value2);
if (bi2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) bi2) / log10 ((double) i1);
break;
case DB_TYPE_FLOAT:
f2 = db_get_float (value2);
if (f2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) f2) / log10 ((double) i1);
break;
case DB_TYPE_DOUBLE:
d2 = db_get_double (value2);
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 (d2) / log10 ((double) i1);
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double (db_locate_numeric (value2), DB_VALUE_SCALE (value2), &d2);
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 (d2) / log10 ((double) i1);
break;
case DB_TYPE_MONETARY:
d2 = (db_get_monetary (value2))->amount;
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 (d2) / log10 ((double) i1);
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
break;
}
break;
case DB_TYPE_FLOAT:
f1 = db_get_float (value1);
if (f1 <= 1)
{
goto log_error;
}
switch (type2)
{
case DB_TYPE_SHORT:
s2 = db_get_short (value2);
if (s2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) s2) / log10 ((double) f1);
break;
case DB_TYPE_INTEGER:
i2 = db_get_int (value2);
if (i2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) i2) / log10 ((double) f1);
break;
case DB_TYPE_BIGINT:
bi2 = db_get_bigint (value2);
if (bi2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) bi2) / log10 ((double) f1);
break;
case DB_TYPE_FLOAT:
f2 = db_get_float (value2);
if (f2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) f2) / log10 ((double) f1);
break;
case DB_TYPE_DOUBLE:
d2 = db_get_double (value2);
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 (d2) / log10 ((double) f1);
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double (db_locate_numeric (value2), DB_VALUE_SCALE (value2), &d2);
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 (d2) / log10 (f1);
break;
case DB_TYPE_MONETARY:
d2 = (db_get_monetary (value2))->amount;
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 (d2) / log10 (f1);
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
break;
}
break;
case DB_TYPE_DOUBLE:
d1 = db_get_double (value1);
if (d1 <= 1)
{
goto log_error;
}
switch (type2)
{
case DB_TYPE_SHORT:
s2 = db_get_short (value2);
if (s2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) s2) / log10 (d1);
break;
case DB_TYPE_INTEGER:
i2 = db_get_int (value2);
if (i2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) i2) / log10 (d1);
break;
case DB_TYPE_BIGINT:
bi2 = db_get_bigint (value2);
if (bi2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) bi2) / log10 (d1);
break;
case DB_TYPE_FLOAT:
f2 = db_get_float (value2);
if (f2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) f2) / log10 (d1);
break;
case DB_TYPE_DOUBLE:
d2 = db_get_double (value2);
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 (d2) / log10 (d1);
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double (db_locate_numeric (value2), DB_VALUE_SCALE (value2), &d2);
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 (d2) / log10 (d1);
break;
case DB_TYPE_MONETARY:
d2 = (db_get_monetary (value2))->amount;
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 (d2) / log10 (d1);
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
break;
}
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double (db_locate_numeric (value1), DB_VALUE_SCALE (value1), &d1);
if (d1 <= 1)
{
goto log_error;
}
switch (type2)
{
case DB_TYPE_SHORT:
s2 = db_get_short (value2);
if (s2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) s2) / log10 (d1);
break;
case DB_TYPE_INTEGER:
i2 = db_get_int (value2);
if (i2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) i2) / log10 (d1);
break;
case DB_TYPE_BIGINT:
bi2 = db_get_bigint (value2);
if (bi2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) bi2) / log10 (d1);
break;
case DB_TYPE_FLOAT:
f2 = db_get_float (value2);
if (f2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) f2) / log10 (d1);
break;
case DB_TYPE_DOUBLE:
d2 = db_get_double (value2);
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 (d2) / log10 (d1);
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double (db_locate_numeric (value2), DB_VALUE_SCALE (value2), &d2);
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 (d2) / log10 (d1);
break;
case DB_TYPE_MONETARY:
d2 = (db_get_monetary (value2))->amount;
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 (d2) / log10 (d1);
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
break;
}
break;
case DB_TYPE_MONETARY:
d1 = (db_get_monetary (value1))->amount;
if (d1 <= 1)
{
goto log_error;
}
switch (type2)
{
case DB_TYPE_SHORT:
s2 = db_get_short (value2);
if (s2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) s2) / log10 (d1);
break;
case DB_TYPE_INTEGER:
i2 = db_get_int (value2);
if (i2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) i2) / log10 (d1);
break;
case DB_TYPE_BIGINT:
bi2 = db_get_bigint (value2);
if (bi2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) bi2) / log10 (d1);
break;
case DB_TYPE_FLOAT:
f2 = db_get_float (value2);
if (f2 <= 0)
{
goto log_error;
}
dtmp = log10 ((double) f2) / log10 (d1);
break;
case DB_TYPE_DOUBLE:
d2 = db_get_double (value2);
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 (d2) / log10 (d1);
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double (db_locate_numeric (value2), DB_VALUE_SCALE (value2), &d2);
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 (d2) / log10 (d1);
break;
case DB_TYPE_MONETARY:
d2 = (db_get_monetary (value2))->amount;
if (d2 <= 0)
{
goto log_error;
}
dtmp = log10 (d2) / log10 (d1);
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
break;
}
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_FAILED;
}
db_make_double (result, dtmp);
return NO_ERROR;
log_error:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_FUNCTION_ARG_ERROR, 1, "log()");
return ER_FAILED;
}
/*
* truncate_double ()
* return: num truncated to integer places
* num(in) :
* integer(in):
*/
static double
truncate_double (double num, double integer)
{
/*
* Under high optimization level, some optimizers (e.g, gcc -O3 on linux)
* generates a wrong result without "volatile".
*/
double scale_up, num_scale_up, result;
if (num == 0)
{
return num;
}
scale_up = pow (10, integer);
num_scale_up = num * scale_up;
if (num > 0)
{
result = floor (num_scale_up);
}
else
{
result = ceil (num_scale_up);
}
if (num_scale_up == result) /* no need to calculate, return as it is */
{
result = num; /* to avoid possible truncation */
}
else
{
result = result / scale_up;
}
return result;
}
/*
* truncate_bigint ()
* return: num truncated to integer places
* num(in) :
* integer(in):
*/
static DB_BIGINT
truncate_bigint (DB_BIGINT num, DB_BIGINT integer)
{
if (num == 0 || integer >= 0)
{
return num;
}
integer = (DB_BIGINT) pow (10, (double) -integer);
num -= num % integer;
return num;
}
/*
* truncate_date ()
* return: error or no error
* date(in) :
* fmt(in):
*/
static int
truncate_date (DB_DATE * date, const DB_VALUE * format_str)
{
int year, month, day;
int error = NO_ERROR;
TIMESTAMP_FORMAT format;
int weekday;
int days_months[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
assert (date != NULL);
assert (format_str != NULL);
error = db_get_date_format (format_str, &format);
if (error != NO_ERROR)
{
goto end;
}
if (format == DT_INVALID)
{
error = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
goto end;
}
db_date_decode (date, &month, &day, &year);
/* truncate datetime according to format */
switch (format)
{
case DT_YYYY:
case DT_YY:
month = day = 1;
break;
case DT_MONTH:
case DT_MON:
case DT_MM:
day = 1;
break;
case DT_DD:
case DT_HH24:
case DT_HH12:
case DT_HH:
case DT_H:
case DT_MI:
case DT_SS:
case DT_MS:
/* do nothing */
break;
case DT_Q: /* quarter */
month = (month - 1) / 3 * 3 + 1;
day = 1;
break;
case DT_DAY: /* week day */
case DT_DY:
case DT_D:
weekday = day_of_week (*date);
day = day - weekday; /* Sunday is the first day of a week */
/* need adjust */
if (day < 1)
{
month = month - 1;
if (month < 1)
{
year = year - 1;
month = 12;
}
if (month == 2 && (year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)))
{
days_months[2] = 29;
}
day = day + days_months[month];
}
break;
case DT_CC: /* one greater than the first two digits of a four-digit year */
default:
error = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
goto end;
}
error = db_date_encode (date, month, day, year);
if (error != NO_ERROR)
{
goto end;
}
end:
return error;
}
/*
* db_trunc_dbval () - return dbval1 truncated to dbval2 decimal places
*
* There are four overloads
* The first one is used to truncate number
* trunc(PT_GENERIC_TYPE_NUMBER, PT_TYPE_INTEGER)
* The second,third and fourth are used to truncate date/datetime/timestamp with formart
* trunc(PT_TYPE_DATE/PT_TYPE_DATETIME/PT_TYPE_TIMESTAMP, PT_TYPE_CHAR)
*
* return: NO_ERROR, ER_FAILED
* result(out): resultant db_value
* value1(in) : first db_value
* value2(in) : second db_value
*/
int
db_trunc_dbval (DB_VALUE * result, DB_VALUE * value1, DB_VALUE * value2)
{
DB_TYPE type1, type2;
DB_BIGINT bi2;
double dtmp;
DB_VALUE cast_value, cast_format;
int er_status = NO_ERROR;
DB_DATE date;
TP_DOMAIN *domain;
TP_DOMAIN_STATUS cast_status;
db_make_null (&cast_value);
db_make_null (&cast_format);
if (DB_IS_NULL (value1) || DB_IS_NULL (value2))
{
return NO_ERROR;
}
type1 = DB_VALUE_DOMAIN_TYPE (value1);
type2 = DB_VALUE_DOMAIN_TYPE (value2);
if (type2 != DB_TYPE_SHORT && type2 != DB_TYPE_INTEGER && type2 != DB_TYPE_BIGINT && type2 != DB_TYPE_NUMERIC
&& type2 != DB_TYPE_FLOAT && type2 != DB_TYPE_DOUBLE && type2 != DB_TYPE_MONETARY && !QSTR_IS_ANY_CHAR (type2))
{
er_status = ER_QPROC_INVALID_DATATYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, er_status, 0);
goto end;
}
/* convert value1 to double when it's a string */
switch (type1)
{
case DB_TYPE_SHORT:
case DB_TYPE_INTEGER:
case DB_TYPE_BIGINT:
case DB_TYPE_NUMERIC:
case DB_TYPE_FLOAT:
case DB_TYPE_DOUBLE:
case DB_TYPE_MONETARY:
case DB_TYPE_DATE:
case DB_TYPE_DATETIME:
case DB_TYPE_DATETIMELTZ:
case DB_TYPE_DATETIMETZ:
case DB_TYPE_TIMESTAMP:
case DB_TYPE_TIMESTAMPLTZ:
case DB_TYPE_TIMESTAMPTZ:
break;
default: /* convert to double */
type1 = DB_TYPE_UNKNOWN;
/* try type double */
db_make_null (&cast_value);
domain = tp_domain_resolve_default (DB_TYPE_DOUBLE);
cast_status = tp_value_coerce (value1, &cast_value, domain);
if (cast_status == DOMAIN_COMPATIBLE)
{
type1 = DB_TYPE_DOUBLE;
value1 = &cast_value;
}
/* convert fail */
if (type1 == DB_TYPE_UNKNOWN)
{
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true)
{
db_make_null (result);
er_clear ();
er_status = NO_ERROR;
}
else
{
er_status = ER_QPROC_INVALID_DATATYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, er_status, 0);
}
goto end;
}
}
/* translate default fmt */
if (type2 == DB_TYPE_CHAR && strcasecmp (db_get_string (value2), "default") == 0)
{
if (TP_IS_DATE_TYPE (type1))
{
db_make_string (&cast_format, "dd");
}
else
{
db_make_int (&cast_format, 0);
type2 = DB_TYPE_INTEGER;
}
value2 = &cast_format;
}
if (type2 == DB_TYPE_INTEGER)
{
bi2 = db_get_int (value2);
}
else if (type2 == DB_TYPE_BIGINT)
{
bi2 = db_get_bigint (value2);
}
else if (type2 == DB_TYPE_SHORT)
{
bi2 = db_get_short (value2);
}
else if (type1 != DB_TYPE_DATE && type1 != DB_TYPE_DATETIME && type1 != DB_TYPE_DATETIMELTZ
&& type1 != DB_TYPE_DATETIMETZ && type1 != DB_TYPE_TIMESTAMP && type1 != DB_TYPE_TIMESTAMPLTZ
&& type1 != DB_TYPE_TIMESTAMPTZ)
{
domain = tp_domain_resolve_default (DB_TYPE_BIGINT);
cast_status = tp_value_coerce (value2, &cast_format, domain);
if (cast_status != DOMAIN_COMPATIBLE)
{
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true)
{
db_make_null (result);
er_clear ();
er_status = NO_ERROR;
}
else
{
er_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, er_status, 0);
}
goto end;
}
bi2 = db_get_bigint (&cast_format);
}
else
{
bi2 = 0; /* to make compiler be silent */
}
switch (type1)
{
case DB_TYPE_SHORT:
{
short s1;
s1 = db_get_short (value1);
dtmp = truncate_double (s1, (double) bi2);
db_make_short (result, (short) dtmp);
}
break;
case DB_TYPE_INTEGER:
{
int i1;
i1 = db_get_int (value1);
dtmp = truncate_double (i1, (double) bi2);
db_make_int (result, (int) dtmp);
}
break;
case DB_TYPE_BIGINT:
{
DB_BIGINT bi1;
bi1 = db_get_bigint (value1);
bi1 = truncate_bigint (bi1, bi2);
db_make_bigint (result, bi1);
}
break;
case DB_TYPE_FLOAT:
{
float f1;
f1 = db_get_float (value1);
dtmp = truncate_double (f1, (double) bi2);
db_make_float (result, (float) dtmp);
}
break;
case DB_TYPE_DOUBLE:
{
double d1;
d1 = db_get_double (value1);
dtmp = truncate_double (d1, (double) bi2);
db_make_double (result, (double) dtmp);
}
break;
case DB_TYPE_NUMERIC:
{
unsigned char num[DB_NUMERIC_BUF_SIZE];
char num_string[(2 * DB_MAX_NUMERIC_PRECISION) + 4];
char *ptr, *end;
int p, s;
memset (num_string, 0, sizeof (num_string));
numeric_coerce_num_to_dec_str (db_locate_numeric (value1), num_string);
p = DB_VALUE_PRECISION (value1);
s = DB_VALUE_SCALE (value1);
end = num_string + strlen (num_string);
ptr = end - s + bi2;
if (end < ptr)
{
/* no need to round, return as it is */
*result = *value1;
break;
}
else if (ptr < num_string)
{
/* return zero */
memset (num_string, 0, sizeof (num_string));
}
else
{
while (ptr < end)
{
*ptr++ = '0';
}
}
numeric_coerce_dec_str_to_num (num_string, num);
db_make_numeric (result, num, p, s);
}
break;
case DB_TYPE_MONETARY:
{
double d1;
d1 = (db_get_monetary (value1))->amount;
dtmp = truncate_double (d1, (double) bi2);
db_make_monetary (result, (db_get_monetary (value1))->type, dtmp);
}
break;
case DB_TYPE_DATE:
case DB_TYPE_DATETIME:
case DB_TYPE_DATETIMELTZ:
case DB_TYPE_DATETIMETZ:
case DB_TYPE_TIMESTAMP:
case DB_TYPE_TIMESTAMPLTZ:
case DB_TYPE_TIMESTAMPTZ:
if (type1 == DB_TYPE_DATE)
{
date = *(db_get_date (value1));
}
else if (type1 == DB_TYPE_DATETIME)
{
date = db_get_datetime (value1)->date;
}
else if (type1 == DB_TYPE_DATETIMELTZ)
{
DB_DATETIME local_dt, *p_dt;
p_dt = db_get_datetime (value1);
er_status = tz_datetimeltz_to_local (p_dt, &local_dt);
if (er_status != NO_ERROR)
{
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true)
{
er_clear ();
db_make_null (result);
er_status = NO_ERROR;
}
goto end;
}
date = local_dt.date;
}
else if (type1 == DB_TYPE_DATETIMETZ)
{
DB_DATETIME local_dt;
DB_DATETIMETZ *p_dt_tz;
p_dt_tz = db_get_datetimetz (value1);
er_status = tz_utc_datetimetz_to_local (&p_dt_tz->datetime, &p_dt_tz->tz_id, &local_dt);
if (er_status != NO_ERROR)
{
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true)
{
er_clear ();
db_make_null (result);
er_status = NO_ERROR;
}
goto end;
}
date = local_dt.date;
}
else if (type1 == DB_TYPE_TIMESTAMPTZ)
{
DB_TIMESTAMPTZ *p_ts_tz;
p_ts_tz = db_get_timestamptz (value1);
er_status = db_timestamp_decode_w_tz_id (&p_ts_tz->timestamp, &p_ts_tz->tz_id, &date, NULL);
if (er_status != NO_ERROR)
{
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true)
{
er_clear ();
db_make_null (result);
er_status = NO_ERROR;
}
goto end;
}
}
else
{
assert (type1 == DB_TYPE_TIMESTAMP || type1 == DB_TYPE_TIMESTAMPLTZ);
(void) db_timestamp_decode_ses (db_get_timestamp (value1), &date, NULL);
}
er_status = truncate_date (&date, value2);
if (er_status != NO_ERROR)
{
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true)
{
er_clear ();
er_status = NO_ERROR;
}
goto end;
}
else
{
db_value_put_encoded_date (result, &date);
}
break;
default:
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == false)
{
er_status = ER_QPROC_INVALID_DATATYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, er_status, 0);
goto end;
}
}
end:
pr_clear_value (&cast_value);
pr_clear_value (&cast_format);
return er_status;
}
/*
* db_random_dbval () - take random integer
* return: NO_ERROR
* result(out) : resultant db_value
*/
int
db_random_dbval (DB_VALUE * result)
{
db_make_int (result, lrand48 ());
return NO_ERROR;
}
/*
* db_drandom_dbval () - take random double
* return: NO_ERROR
* result(out) : resultant db_value
*/
int
db_drandom_dbval (DB_VALUE * result)
{
db_make_double (result, drand48 ());
return NO_ERROR;
}
/*
* get_number_dbval_as_double () -
* return: NO_ERROR/error code
* d(out) : double
* value(in) : input db_value
*/
static int
get_number_dbval_as_double (double *d, const DB_VALUE * value)
{
short s;
int i;
float f;
double dtmp;
DB_BIGINT bi;
switch (DB_VALUE_DOMAIN_TYPE (value))
{
case DB_TYPE_SHORT:
s = db_get_short (value);
dtmp = (double) s;
break;
case DB_TYPE_INTEGER:
i = db_get_int (value);
dtmp = (double) i;
break;
case DB_TYPE_BIGINT:
bi = db_get_bigint (value);
dtmp = (double) bi;
break;
case DB_TYPE_FLOAT:
f = db_get_float (value);
dtmp = (double) f;
break;
case DB_TYPE_DOUBLE:
dtmp = db_get_double (value);
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double ((DB_C_NUMERIC) db_locate_numeric (value), DB_VALUE_SCALE (value), &dtmp);
break;
case DB_TYPE_MONETARY:
dtmp = (db_get_monetary (value))->amount;
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_QPROC_INVALID_DATATYPE;
}
*d = dtmp;
return NO_ERROR;
}
/*
* db_cos_dbval () - computes cosine value of db_value
* return: NO_ERROR
* result(out): resultant db_value
* value(in) : input db_value
*/
int
db_cos_dbval (DB_VALUE * result, DB_VALUE * value)
{
DB_TYPE type;
int err;
double dtmp;
type = DB_VALUE_DOMAIN_TYPE (value);
if (type == DB_TYPE_NULL || DB_IS_NULL (value))
{
db_make_null (result);
return NO_ERROR;
}
err = get_number_dbval_as_double (&dtmp, value);
if (err != NO_ERROR)
{
return err;
}
dtmp = cos (dtmp);
db_make_double (result, dtmp);
return NO_ERROR;
}
/*
* db_sin_dbval () - computes sine value of db_value
* return: NO_ERROR
* result(out): resultant db_value
* value(in) : input db_value
*/
int
db_sin_dbval (DB_VALUE * result, DB_VALUE * value)
{
DB_TYPE type;
int err;
double dtmp;
type = DB_VALUE_DOMAIN_TYPE (value);
if (type == DB_TYPE_NULL || DB_IS_NULL (value))
{
db_make_null (result);
return NO_ERROR;
}
err = get_number_dbval_as_double (&dtmp, value);
if (err != NO_ERROR)
{
return err;
}
dtmp = sin (dtmp);
db_make_double (result, dtmp);
return NO_ERROR;
}
/*
* db_tan_dbval () - computes tangent value of db_value
* return: NO_ERROR
* result(out): resultant db_value
* value(in) : input db_value
*/
int
db_tan_dbval (DB_VALUE * result, DB_VALUE * value)
{
DB_TYPE type;
int err;
double dtmp;
type = DB_VALUE_DOMAIN_TYPE (value);
if (type == DB_TYPE_NULL || DB_IS_NULL (value))
{
db_make_null (result);
return NO_ERROR;
}
err = get_number_dbval_as_double (&dtmp, value);
if (err != NO_ERROR)
{
return err;
}
dtmp = tan (dtmp);
db_make_double (result, dtmp);
return NO_ERROR;
}
/*
* db_cot_dbval () - computes cotangent value of db_value
* return: NO_ERROR
* result(out): resultant db_value
* value(in) : input db_value
*/
int
db_cot_dbval (DB_VALUE * result, DB_VALUE * value)
{
DB_TYPE type;
int err;
double dtmp;
type = DB_VALUE_DOMAIN_TYPE (value);
if (type == DB_TYPE_NULL || DB_IS_NULL (value))
{
db_make_null (result);
return NO_ERROR;
}
err = get_number_dbval_as_double (&dtmp, value);
if (err != NO_ERROR)
{
return err;
}
if (dtmp == 0)
{
db_make_null (result);
}
else
{
dtmp = 1 / tan (dtmp);
db_make_double (result, dtmp);
}
return NO_ERROR;
}
/*
* db_acos_dbval () - computes arc cosine value of db_value
* return: NO_ERROR
* result(out): resultant db_value
* value(in) : input db_value
*/
int
db_acos_dbval (DB_VALUE * result, DB_VALUE * value)
{
DB_TYPE type;
int err;
double dtmp;
type = DB_VALUE_DOMAIN_TYPE (value);
if (type == DB_TYPE_NULL || DB_IS_NULL (value))
{
db_make_null (result);
return NO_ERROR;
}
err = get_number_dbval_as_double (&dtmp, value);
if (err != NO_ERROR)
{
return err;
}
if (dtmp < -1 || dtmp > 1)
{
goto error;
}
dtmp = acos (dtmp);
db_make_double (result, dtmp);
return NO_ERROR;
error:
if (prm_get_integer_value (PRM_ID_COMPAT_MODE) == COMPAT_MYSQL)
{
db_make_null (result);
return NO_ERROR;
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_FUNCTION_ARG_ERROR, 1, "acos()");
return ER_QPROC_FUNCTION_ARG_ERROR;
}
}
/*
* db_asin_dbval () - computes arc sine value of db_value
* return: NO_ERROR
* result(out): resultant db_value
* value(in) : input db_value
*/
int
db_asin_dbval (DB_VALUE * result, DB_VALUE * value)
{
DB_TYPE type;
int err;
double dtmp;
type = DB_VALUE_DOMAIN_TYPE (value);
if (type == DB_TYPE_NULL || DB_IS_NULL (value))
{
db_make_null (result);
return NO_ERROR;
}
err = get_number_dbval_as_double (&dtmp, value);
if (err != NO_ERROR)
{
return err;
}
if (dtmp < -1 || dtmp > 1)
{
goto error;
}
dtmp = asin (dtmp);
db_make_double (result, dtmp);
return NO_ERROR;
error:
if (prm_get_integer_value (PRM_ID_COMPAT_MODE) == COMPAT_MYSQL)
{
db_make_null (result);
return NO_ERROR;
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_FUNCTION_ARG_ERROR, 1, "asin()");
return ER_QPROC_FUNCTION_ARG_ERROR;
}
}
/*
* db_atan_dbval () - computes arc tangent value of value2 / value
* return: NO_ERROR
* result(out): resultant db_value
* value(in) : input db_value
*/
int
db_atan_dbval (DB_VALUE * result, DB_VALUE * value)
{
DB_TYPE type;
int err;
double dtmp;
type = DB_VALUE_DOMAIN_TYPE (value);
if (type == DB_TYPE_NULL || DB_IS_NULL (value))
{
db_make_null (result);
return NO_ERROR;
}
err = get_number_dbval_as_double (&dtmp, value);
if (err != NO_ERROR)
{
return err;
}
dtmp = atan (dtmp);
db_make_double (result, dtmp);
return NO_ERROR;
}
/*
* db_atan2_dbval () - computes arc tangent value of value2 / value
* return: NO_ERROR
* result(out): resultant db_value
* value(in) : input db_value
* value2(in) : second input db_value
* OBS: this should have been done like db_power_dbval, i.e. switch in switch
* but this yields in very much code so we prefered to get all values
* separated and then convert all to double. Then just one call of atan2.
*/
int
db_atan2_dbval (DB_VALUE * result, DB_VALUE * value, DB_VALUE * value2)
{
DB_TYPE type, type2;
int err;
double d, d2, dtmp;
/* arg1 */
type = DB_VALUE_DOMAIN_TYPE (value);
if (type == DB_TYPE_NULL || DB_IS_NULL (value))
{
db_make_null (result);
return NO_ERROR;
}
err = get_number_dbval_as_double (&d, value);
if (err != NO_ERROR)
{
return err;
}
/* arg2 */
type2 = DB_VALUE_DOMAIN_TYPE (value2);
if (type2 == DB_TYPE_NULL || DB_IS_NULL (value2))
{
db_make_null (result);
return NO_ERROR;
}
err = get_number_dbval_as_double (&d2, value2);
if (err != NO_ERROR)
{
return err;
}
/* function call, all is double type */
dtmp = atan2 (d, d2);
db_make_double (result, dtmp);
return NO_ERROR;
}
/*
* db_degrees_dbval () - computes radians from value in degrees
* return: NO_ERROR
* result(out): resultant db_value
* value(in) : input db_value
*/
int
db_degrees_dbval (DB_VALUE * result, DB_VALUE * value)
{
DB_TYPE type;
int err;
double dtmp;
type = DB_VALUE_DOMAIN_TYPE (value);
if (type == DB_TYPE_NULL || DB_IS_NULL (value))
{
db_make_null (result);
return NO_ERROR;
}
err = get_number_dbval_as_double (&dtmp, value);
if (err != NO_ERROR)
{
return err;
}
dtmp = dtmp * (double) 57.295779513082320876798154814105; /* 180 / PI */
db_make_double (result, dtmp);
return NO_ERROR;
}
/*
* db_radians_dbval () - converts degrees in value to radians
* return: NO_ERROR
* result(out): resultant db_value
* value(in) : input db_value
*/
int
db_radians_dbval (DB_VALUE * result, DB_VALUE * value)
{
DB_TYPE type;
int err;
double dtmp;
type = DB_VALUE_DOMAIN_TYPE (value);
if (type == DB_TYPE_NULL || DB_IS_NULL (value))
{
db_make_null (result);
return NO_ERROR;
}
err = get_number_dbval_as_double (&dtmp, value);
if (err != NO_ERROR)
{
return err;
}
dtmp = dtmp * (double) 0.017453292519943295769236907684886; /* PI / 180 */
db_make_double (result, dtmp);
return NO_ERROR;
}
/*
* db_log_generic_dbval () - computes log of db_value in base
* return: NO_ERROR
* result(out): resultant db_value
* value(in) : input db_value
*/
int
db_log_generic_dbval (DB_VALUE * result, DB_VALUE * value, long b)
{
DB_TYPE type;
int err;
double dtmp;
double base = ((b == -1) ? (2.7182818284590452353) : (double) b);
type = DB_VALUE_DOMAIN_TYPE (value);
if (type == DB_TYPE_NULL || DB_IS_NULL (value))
{
db_make_null (result);
return NO_ERROR;
}
err = get_number_dbval_as_double (&dtmp, value);
if (err != NO_ERROR)
{
return err;
}
if (dtmp > 0)
{
dtmp = log10 (dtmp) / log10 (base);
db_make_double (result, dtmp);
}
else
{
const char *log_func;
switch (b)
{
case -1:
log_func = "ln()";
break;
case 2:
log_func = "log2()";
break;
case 10:
log_func = "log10()";
break;
default:
assert (0);
log_func = "unknown";
break;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_FUNCTION_ARG_ERROR, 1, log_func);
return ER_QPROC_FUNCTION_ARG_ERROR;
}
return NO_ERROR;
}
/*
* db_bit_count_dbval () - bit count of db_value
* return:
* result(out): resultant db_value
* value(in) : input db_value
*/
int
db_bit_count_dbval (DB_VALUE * result, DB_VALUE * value)
{
DB_TYPE type;
short s;
int i, c = 0;
float f;
double d;
DB_BIGINT bi;
DB_VALUE tmpval, *tmpval_p;
if (value == NULL)
{
return ER_FAILED;
}
tmpval_p = value;
type = DB_VALUE_DOMAIN_TYPE (value);
if (DB_IS_NULL (value))
{
db_make_null (result);
}
else
{
switch (type)
{
case DB_TYPE_SHORT:
s = db_get_short (value);
for (c = 0; s; c++)
{
s &= s - 1;
}
break;
case DB_TYPE_INTEGER:
i = db_get_int (value);
for (c = 0; i; c++)
{
i &= i - 1;
}
break;
case DB_TYPE_BIGINT:
bi = db_get_bigint (value);
for (c = 0; bi; c++)
{
bi &= bi - 1;
}
break;
case DB_TYPE_FLOAT:
f = db_get_float (value);
if (f < 0)
{
i = (int) (f - 0.5f);
}
else
{
i = (int) (f + 0.5f);
}
for (c = 0; i; c++)
{
i &= i - 1;
}
break;
case DB_TYPE_MONETARY:
d = (db_get_monetary (value))->amount;
if (d < 0)
{
bi = (DB_BIGINT) (d - 0.5f);
}
else
{
bi = (DB_BIGINT) (d + 0.5f);
}
for (c = 0; bi; c++)
{
bi &= bi - 1;
}
break;
case DB_TYPE_NUMERIC:
if (tp_value_cast (value, &tmpval, &tp_Double_domain, false) != DOMAIN_COMPATIBLE)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_FAILED;
}
tmpval_p = &tmpval;
[[fallthrough]];
case DB_TYPE_DOUBLE:
d = db_get_double (tmpval_p);
if (d < 0)
{
bi = (DB_BIGINT) (d - 0.5f);
}
else
{
bi = (DB_BIGINT) (d + 0.5f);
}
for (c = 0; bi; c++)
{
bi &= bi - 1;
}
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_QPROC_INVALID_DATATYPE;
}
db_make_int (result, c);
}
return NO_ERROR;
}
/*
* db_typeof_dbval() -
* return:
* result(out):
* value(in) : input db_value
*/
int
db_typeof_dbval (DB_VALUE * result, DB_VALUE * value)
{
DB_TYPE type;
const char *type_name;
char *buf;
type = DB_VALUE_TYPE (value);
type_name = pr_type_name (type);
if (type_name == NULL)
{
db_make_null (result);
return NO_ERROR;
}
switch (type)
{
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
case DB_TYPE_BIT:
case DB_TYPE_VARBIT:
case DB_TYPE_NUMERIC:
buf = (char *) db_private_alloc (NULL, 128);
if (buf == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) 128);
return ER_OUT_OF_VIRTUAL_MEMORY;
}
if (type == DB_TYPE_NUMERIC)
{
snprintf (buf, 128, "%s (%u, %u)", type_name, value->domain.numeric_info.precision,
value->domain.numeric_info.scale);
}
else
{
snprintf (buf, 128, "%s (%d)", type_name, value->domain.char_info.length);
}
db_make_string (result, buf);
result->need_clear = true;
break;
default:
db_make_string (result, type_name);
}
return NO_ERROR;
}
/*
* get_number_dbval_as_long_double () -
* return:
* long double(out):
* value(in) :
*/
static int
get_number_dbval_as_long_double (long double *ld, const DB_VALUE * value)
{
short s;
int i;
float f;
long double dtmp;
DB_BIGINT bi;
char num_string[2 * DB_MAX_NUMERIC_PRECISION + 2];
char *tail_ptr = NULL;
switch (DB_VALUE_DOMAIN_TYPE (value))
{
case DB_TYPE_SHORT:
s = db_get_short (value);
dtmp = (long double) s;
break;
case DB_TYPE_INTEGER:
i = db_get_int (value);
dtmp = (long double) i;
break;
case DB_TYPE_BIGINT:
bi = db_get_bigint (value);
dtmp = (long double) bi;
break;
case DB_TYPE_FLOAT:
f = db_get_float (value);
dtmp = (long double) f;
break;
case DB_TYPE_DOUBLE:
dtmp = (long double) db_get_double (value);
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_dec_str ((DB_C_NUMERIC) db_locate_numeric (value), num_string);
#ifdef _ISOC99_SOURCE
dtmp = strtold (num_string, &tail_ptr) / powl (10.0, DB_VALUE_SCALE (value));
#else
dtmp = atof (num_string) / pow (10.0, DB_VALUE_SCALE (value));
#endif
break;
case DB_TYPE_MONETARY:
dtmp = (long double) (db_get_monetary (value))->amount;
break;
default:
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_QPROC_INVALID_DATATYPE;
}
*ld = dtmp;
return NO_ERROR;
}
/*
* db_width_bucket_calculate_numeric() -
* return:
* result(out):
* value1-4(in) : input db_value
*/
static int
db_width_bucket_calculate_numeric (double *result, const DB_VALUE * value1, const DB_VALUE * value2,
const DB_VALUE * value3, const DB_VALUE * value4)
{
int er_status = NO_ERROR, c;
DB_VALUE cmp_result;
DB_VALUE n1, n2, n3, n4;
double res = 0.0;
assert (value1 != NULL && value2 != NULL && value3 != NULL && value4 != NULL && result != NULL);
assert (DB_VALUE_TYPE (value1) == DB_TYPE_NUMERIC && DB_VALUE_TYPE (value2) == DB_TYPE_NUMERIC
&& DB_VALUE_TYPE (value3) == DB_TYPE_NUMERIC && DB_VALUE_TYPE (value4) == DB_TYPE_NUMERIC);
db_make_null (&cmp_result);
db_make_null (&n1);
db_make_null (&n2);
db_make_null (&n3);
db_make_null (&n4);
er_status = numeric_db_value_compare (value2, value3, &cmp_result);
if (er_status != NO_ERROR)
{
return er_status;
}
c = db_get_int (&cmp_result);
if (c == 0 || c == -1)
{
/* value2 <= value3 */
er_status = numeric_db_value_compare (value1, value2, &cmp_result);
if (er_status != NO_ERROR)
{
return er_status;
}
if (db_get_int (&cmp_result) < 0)
{
res = 0.0;
}
else
{
er_status = numeric_db_value_compare (value3, value1, &cmp_result);
if (er_status != NO_ERROR)
{
return er_status;
}
if (db_get_int (&cmp_result) < 1)
{
numeric_coerce_num_to_double ((DB_C_NUMERIC) db_get_numeric (value4), DB_VALUE_SCALE (value4), &res);
res += 1.0;
}
else
{
/* floor ((v1-v2)/((v3-v2)/v4)) + 1 */
er_status = numeric_db_value_sub (value1, value2, &n1);
if (er_status != NO_ERROR)
{
return er_status;
}
er_status = numeric_db_value_sub (value3, value2, &n2);
if (er_status != NO_ERROR)
{
return er_status;
}
er_status = numeric_db_value_div (&n2, value4, &n3);
if (er_status != NO_ERROR)
{
return er_status;
}
er_status = numeric_db_value_div (&n1, &n3, &n4);
if (er_status != NO_ERROR)
{
return er_status;
}
numeric_coerce_num_to_double (db_get_numeric (&n4), DB_VALUE_SCALE (&n4), &res);
if (OR_CHECK_DOUBLE_OVERFLOW (res))
{
return ER_IT_DATA_OVERFLOW;
}
res = floor (res) + 1.0;
}
}
}
else
{
/* value2 > value3 */
assert (c == 1);
er_status = numeric_db_value_compare (value2, value1, &cmp_result);
if (er_status != NO_ERROR)
{
return er_status;
}
if (db_get_int (&cmp_result) < 0)
{
res = 0.0;
}
else
{
er_status = numeric_db_value_compare (value2, value3, &cmp_result);
if (er_status != NO_ERROR)
{
return er_status;
}
if (db_get_int (&cmp_result) < 1)
{
numeric_coerce_num_to_double ((DB_C_NUMERIC) db_get_numeric (value4), DB_VALUE_SCALE (value4), &res);
res += 1.0;
}
else
{
/* floor ((v2-v1)/((v2-v3)/v4)) + 1 */
er_status = numeric_db_value_sub (value2, value1, &n1);
if (er_status != NO_ERROR)
{
return er_status;
}
er_status = numeric_db_value_sub (value2, value3, &n2);
if (er_status != NO_ERROR)
{
return er_status;
}
er_status = numeric_db_value_div (&n2, value4, &n3);
if (er_status != NO_ERROR)
{
return er_status;
}
er_status = numeric_db_value_div (&n1, &n3, &n4);
if (er_status != NO_ERROR)
{
return er_status;
}
numeric_coerce_num_to_double (db_get_numeric (&n4), DB_VALUE_SCALE (&n4), &res);
if (OR_CHECK_DOUBLE_OVERFLOW (res))
{
return ER_IT_DATA_OVERFLOW;
}
res = floor (res) + 1.0;
}
}
}
if (OR_CHECK_DOUBLE_OVERFLOW (res))
{
return ER_QPROC_OVERFLOW_ADDITION;
}
*result = res;
return NO_ERROR;
}
/*
* db_width_bucket() -
* return:
* result(out):
* value1-4(in) : input db_value
*/
int
db_width_bucket (DB_VALUE * result, const DB_VALUE * value1, const DB_VALUE * value2, const DB_VALUE * value3,
const DB_VALUE * value4)
{
#define RETURN_ERROR(err) \
do \
{ \
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true) \
{ \
db_make_null (result); \
er_clear (); \
return NO_ERROR; \
} \
else \
{ \
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, (err), 0); \
return (err); \
} \
} \
while (0)
#define RETURN_ERROR_WITH_ARG(err, arg) \
do \
{ \
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == true) \
{ \
db_make_null (result); \
er_clear (); \
return NO_ERROR; \
} \
else \
{ \
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, (err), 1, (arg)); \
return (err); \
} \
} \
while (0)
#define MAX_DOMAIN_NAME_SIZE 150
double d1, d2, d3, d4, d_ret;
double tmp_d1 = 0.0, tmp_d2 = 0.0, tmp_d3 = 0.0, tmp_d4 = 0.0;
DB_TYPE type, cast_type;
DB_VALUE cast_value1, cast_value2, cast_value3, cast_value4;
TP_DOMAIN *cast_domain = NULL;
TP_DOMAIN *numeric_domain = NULL;
TP_DOMAIN_STATUS cast_status;
bool is_deal_with_numeric = false;
int er_status = NO_ERROR;
char buf[MAX_DOMAIN_NAME_SIZE];
assert (result != NULL && value1 != NULL && value2 != NULL && value3 != NULL && value4 != NULL);
db_make_null (&cast_value1);
db_make_null (&cast_value2);
db_make_null (&cast_value3);
db_make_null (&cast_value4);
if (DB_VALUE_TYPE (value1) == DB_TYPE_NULL || DB_VALUE_TYPE (value2) == DB_TYPE_NULL
|| DB_VALUE_TYPE (value3) == DB_TYPE_NULL || DB_VALUE_TYPE (value4) == DB_TYPE_NULL)
{
db_make_null (result);
return NO_ERROR;
}
d4 = db_get_double (value4);
if (d4 < 1 || d4 >= DB_INT32_MAX)
{
RETURN_ERROR (ER_PROC_WIDTH_BUCKET_COUNT);
}
d4 = (int) floor (d4);
/* find the common type of value1, value2 and value3 and cast them to the common type */
type = DB_VALUE_DOMAIN_TYPE (value1);
switch (type)
{
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
/* try double */
cast_type = DB_TYPE_UNKNOWN;
cast_domain = tp_domain_resolve_default (DB_TYPE_DOUBLE);
cast_status = tp_value_coerce (value1, &cast_value1, cast_domain);
if (cast_status == DOMAIN_COMPATIBLE)
{
cast_type = DB_TYPE_DOUBLE;
}
else
{
/* try datetime date, timestamp is compatible with datetime */
cast_domain = tp_domain_resolve_default (DB_TYPE_DATETIME);
cast_status = tp_value_coerce (value1, &cast_value1, cast_domain);
if (cast_status == DOMAIN_COMPATIBLE)
{
cast_type = DB_TYPE_DATETIME;
}
else
{
/* try time */
er_clear (); // forget previous error to try datetime
cast_domain = tp_domain_resolve_default (DB_TYPE_TIME);
cast_status = tp_value_coerce (value1, &cast_value1, cast_domain);
if (cast_status == DOMAIN_COMPATIBLE)
{
cast_type = DB_TYPE_TIME;
}
else
{
RETURN_ERROR (ER_QPROC_INVALID_DATATYPE);
}
}
}
value1 = &cast_value1;
/* coerce value2 with the type of value1 */
if (cast_type != DB_VALUE_DOMAIN_TYPE (value2))
{
cast_domain = tp_domain_resolve_default (cast_type);
cast_status = tp_value_coerce (value2, &cast_value2, cast_domain);
if (cast_status != DOMAIN_COMPATIBLE)
{
RETURN_ERROR (ER_QPROC_INVALID_DATATYPE);
}
value2 = &cast_value2;
}
/* coerce value3 with the type of value1 */
if (cast_type != DB_VALUE_DOMAIN_TYPE (value3))
{
cast_domain = tp_domain_resolve_default (cast_type);
cast_status = tp_value_coerce (value3, &cast_value3, cast_domain);
if (cast_status != DOMAIN_COMPATIBLE)
{
RETURN_ERROR (ER_QPROC_INVALID_DATATYPE);
}
value3 = &cast_value3;
}
break;
default:
break;
}
/* the type of value1 is fixed */
type = DB_VALUE_DOMAIN_TYPE (value1);
switch (type)
{
case DB_TYPE_DATE:
d1 = (double) *db_get_date (value1);
d2 = (double) *db_get_date (value2);
d3 = (double) *db_get_date (value3);
break;
case DB_TYPE_DATETIME:
case DB_TYPE_DATETIMELTZ:
/* double can hold datetime type */
d1 = ((double) db_get_datetime (value1)->date) * MILLISECONDS_OF_ONE_DAY + db_get_datetime (value1)->time;
d2 = ((double) db_get_datetime (value2)->date) * MILLISECONDS_OF_ONE_DAY + db_get_datetime (value2)->time;
d3 = ((double) db_get_datetime (value3)->date) * MILLISECONDS_OF_ONE_DAY + db_get_datetime (value3)->time;
break;
case DB_TYPE_DATETIMETZ:
/* double can hold datetime type */
d1 = (((double) db_get_datetimetz (value1)->datetime.date) * MILLISECONDS_OF_ONE_DAY
+ db_get_datetimetz (value1)->datetime.time);
d2 = (((double) db_get_datetimetz (value2)->datetime.date) * MILLISECONDS_OF_ONE_DAY
+ db_get_datetimetz (value2)->datetime.time);
d3 = (((double) db_get_datetimetz (value3)->datetime.date) * MILLISECONDS_OF_ONE_DAY
+ db_get_datetimetz (value3)->datetime.time);
break;
case DB_TYPE_TIMESTAMP:
case DB_TYPE_TIMESTAMPLTZ:
d1 = (double) *db_get_timestamp (value1);
d2 = (double) *db_get_timestamp (value2);
d3 = (double) *db_get_timestamp (value3);
break;
case DB_TYPE_TIMESTAMPTZ:
d1 = (double) (db_get_timestamptz (value1)->timestamp);
d2 = (double) (db_get_timestamptz (value2)->timestamp);
d3 = (double) (db_get_timestamptz (value3)->timestamp);
break;
case DB_TYPE_TIME:
d1 = (double) *db_get_time (value1);
d2 = (double) *db_get_time (value2);
d3 = (double) *db_get_time (value3);
break;
case DB_TYPE_SHORT:
case DB_TYPE_INTEGER:
case DB_TYPE_FLOAT:
case DB_TYPE_DOUBLE:
case DB_TYPE_MONETARY:
if (get_number_dbval_as_double (&d1, value1) != NO_ERROR)
{
RETURN_ERROR (ER_QPROC_INVALID_DATATYPE);
}
if (get_number_dbval_as_double (&d2, value2) != NO_ERROR)
{
RETURN_ERROR (ER_QPROC_INVALID_DATATYPE);
}
if (get_number_dbval_as_double (&d3, value3) != NO_ERROR)
{
RETURN_ERROR (ER_QPROC_INVALID_DATATYPE);
}
break;
case DB_TYPE_BIGINT:
case DB_TYPE_NUMERIC:
d1 = d2 = d3 = 0; /* to make compiler be silent */
/* gcc fully support long double (80 or 128bits) if long double is not fully supported, do calculation with
* numeric */
numeric_domain = tp_domain_new (DB_TYPE_NUMERIC);
if (numeric_domain == NULL)
{
RETURN_ERROR (er_errid ());
}
cast_domain = numeric_domain;
cast_domain->precision = 2 * DB_BIGINT_PRECISION;
cast_domain->scale = DB_FLOAT_DECIMAL_PRECISION;
if (type == DB_TYPE_BIGINT)
{
/* cast bigint to numeric Compiler doesn't support long double (80 or 128bits), so we use numeric instead. If
* a high precision lib is introduced or long double is full supported, remove this part and use the lib or
* long double to calculate. */
/* convert value1 */
cast_status = tp_value_coerce (value1, &cast_value1, cast_domain);
if (cast_status != DOMAIN_COMPATIBLE)
{
tp_domain_name (numeric_domain, buf, MAX_DOMAIN_NAME_SIZE);
tp_domain_free (numeric_domain);
RETURN_ERROR_WITH_ARG (ER_IT_DATA_OVERFLOW, buf);
}
value1 = &cast_value1;
}
/* cast value2, value3, value4 to numeric to make the calculation */
if (DB_VALUE_DOMAIN_TYPE (value2) != DB_TYPE_NUMERIC)
{
cast_status = tp_value_coerce (value2, &cast_value2, cast_domain);
if (cast_status != DOMAIN_COMPATIBLE)
{
tp_domain_name (numeric_domain, buf, MAX_DOMAIN_NAME_SIZE);
tp_domain_free (numeric_domain);
RETURN_ERROR_WITH_ARG (ER_IT_DATA_OVERFLOW, buf);
}
value2 = &cast_value2;
}
if (DB_VALUE_DOMAIN_TYPE (value3) != DB_TYPE_NUMERIC)
{
cast_status = tp_value_coerce (value3, &cast_value3, cast_domain);
if (cast_status != DOMAIN_COMPATIBLE)
{
tp_domain_name (numeric_domain, buf, MAX_DOMAIN_NAME_SIZE);
tp_domain_free (numeric_domain);
RETURN_ERROR_WITH_ARG (ER_IT_DATA_OVERFLOW, buf);
}
value3 = &cast_value3;
}
db_make_int (&cast_value4, ((int) d4));
cast_domain->precision = DB_INTEGER_PRECISION;
cast_domain->scale = 0;
cast_status = tp_value_coerce (&cast_value4, &cast_value4, cast_domain);
if (cast_status != DOMAIN_COMPATIBLE)
{
tp_domain_free (numeric_domain);
RETURN_ERROR (ER_QPROC_OVERFLOW_ADDITION);
}
value4 = &cast_value4;
is_deal_with_numeric = true;
tp_domain_free (numeric_domain);
numeric_domain = NULL;
break;
default:
RETURN_ERROR (ER_QPROC_INVALID_DATATYPE);
}
if (is_deal_with_numeric)
{
er_status = db_width_bucket_calculate_numeric (&d_ret, value1, value2, value3, value4);
if (er_status != NO_ERROR)
{
RETURN_ERROR (er_status);
}
}
else
{
if (d2 <= d3)
{
if (d1 < d2)
{
d_ret = 0.0;
}
else if (d3 <= d1)
{
d_ret = d4 + 1.0;
}
else
{
/* d_ret = floor ((d1 - d2) / ((d3 - d2) / d4)) + 1.0 */
tmp_d1 = d1 - d2;
tmp_d2 = d3 - d2;
tmp_d3 = tmp_d2 / d4;
tmp_d4 = tmp_d1 / tmp_d3;
d_ret = floor (tmp_d4) + 1.0;
}
}
else
{
if (d2 < d1)
{
d_ret = 0.0;
}
else if (d1 <= d3)
{
d_ret = d4 + 1.0;
}
else
{
/* d_ret = floor ((d2 - d1) / ((d2 - d3) / d4)) + 1.0 */
tmp_d1 = d2 - d1;
tmp_d2 = d2 - d3;
tmp_d3 = tmp_d2 / d4;
tmp_d4 = tmp_d1 / tmp_d3;
d_ret = floor (tmp_d4) + 1.0;
}
}
}
/* check overflow */
if (OR_CHECK_DOUBLE_OVERFLOW (tmp_d1) || OR_CHECK_DOUBLE_OVERFLOW (tmp_d2))
{
RETURN_ERROR (ER_QPROC_OVERFLOW_SUBTRACTION);
}
else if (OR_CHECK_DOUBLE_OVERFLOW (tmp_d3) || OR_CHECK_DOUBLE_OVERFLOW (tmp_d4))
{
RETURN_ERROR (ER_QPROC_OVERFLOW_DIVISION);
}
else if (OR_CHECK_INT_OVERFLOW (d_ret))
{
RETURN_ERROR (ER_QPROC_OVERFLOW_ADDITION);
}
db_make_int (result, ((int) d_ret));
return er_status;
#undef RETURN_ERROR
#undef RETURN_ERROR_WITH_ARG
}
/*
* db_sleep() - sleep milli-secs
* return:
* result(out):
* value(in) : input db_value
*/
int
db_sleep (DB_VALUE * result, DB_VALUE * value)
{
int error = NO_ERROR;
long million_sec = 0;
assert (result != NULL && value != NULL);
assert (DB_VALUE_DOMAIN_TYPE (value) == DB_TYPE_NULL || DB_VALUE_DOMAIN_TYPE (value) == DB_TYPE_DOUBLE);
db_make_null (result);
if (DB_IS_NULL (value) || db_get_double (value) < 0.0)
{
error = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
goto end;
}
million_sec = (long) (db_get_double (value) * 1000L);
error = msleep (million_sec);
if (error == NO_ERROR)
{
db_make_int (result, 0);
}
else
{
db_make_int (result, 1);
error = NO_ERROR;
}
end:
return error;
}
/*
* db_crc32_dbval() - crc32
* return: error code
* result(out):
* value(in) : input db_value
*/
int
db_crc32_dbval (DB_VALUE * result, DB_VALUE * value)
{
DB_TYPE type;
int error_status = NO_ERROR;
int hash_result = 0;
assert (result != (DB_VALUE *) NULL);
if (DB_IS_NULL (value))
{
PRIM_SET_NULL (result);
return error_status;
}
else
{
type = DB_VALUE_DOMAIN_TYPE (value);
if (QSTR_IS_ANY_CHAR (type))
{
crypt_crc32 (db_get_string (value), db_get_string_size (value), &hash_result);
db_make_int (result, hash_result);
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
goto error;
}
}
return error_status;
error:
PRIM_SET_NULL (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_clear ();
return NO_ERROR;
}
else
{
if (er_errid () == NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
return error_status;
}
}
int
db_evaluate_json_contains (DB_VALUE * result, DB_VALUE * const *arg, int const num_args)
{
int error_code = NO_ERROR;
JSON_DOC_STORE source;
db_make_null (result);
if (num_args < 2)
{
assert (false);
return ER_FAILED;
}
if (is_any_arg_null (arg, num_args))
{
return NO_ERROR;
}
const DB_VALUE *json = arg[0];
const DB_VALUE *value = arg[1];
const DB_VALUE *path = num_args == 3 ? arg[2] : NULL;
error_code = db_value_to_json_doc (*json, false, source);
if (error_code != NO_ERROR)
{
return error_code;
}
if (path != NULL)
{
JSON_DOC_STORE extracted_doc;
/* *INDENT-OFF* */
std::string raw_path;
error_code = db_value_to_json_path (*path, F_JSON_CONTAINS, raw_path);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
error_code = db_json_extract_document_from_path (source.get_immutable (), raw_path, extracted_doc);
source = std::move (extracted_doc);
/* *INDENT-ON* */
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
}
else
{
//
}
if (source.get_immutable () != NULL)
{
bool has_member = false;
JSON_DOC_STORE value_doc;
int error_code = db_value_to_json_doc (*value, false, value_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
error_code = db_json_value_is_contained_in_doc (source.get_immutable (), value_doc.get_immutable (), has_member);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
db_make_int (result, has_member ? 1 : 0);
}
return NO_ERROR;
}
int
db_evaluate_json_type_dbval (DB_VALUE * result, DB_VALUE * const *arg, int const num_args)
{
db_make_null (result);
if (num_args != 1)
{
assert (false);
return ER_FAILED;
}
DB_VALUE *json = arg[0];
if (DB_IS_NULL (json))
{
return NO_ERROR;;
}
else
{
const char *type;
unsigned int length;
JSON_DOC_STORE doc;
int error_code = db_value_to_json_doc (*json, false, doc);
if (error_code != NO_ERROR)
{
return error_code;
}
type = db_json_get_type_as_str (doc.get_immutable ());
length = strlen (type);
return db_make_varchar (result, length, type, length, LANG_COERCIBLE_CODESET, LANG_COERCIBLE_COLL);
}
}
int
db_evaluate_json_valid (DB_VALUE * result, DB_VALUE * const *arg, int const num_args)
{
db_make_null (result);
if (num_args != 1)
{
assert (false);
return ER_FAILED;
}
DB_VALUE *value = arg[0];
if (DB_IS_NULL (value))
{
return NO_ERROR;
}
DB_TYPE type = db_value_domain_type (value);
bool valid;
if (type == DB_TYPE_JSON)
{
valid = true;
}
else if (TP_IS_CHAR_TYPE (type))
{
/* *INDENT-OFF* */
valid = db_json_is_valid (std::string (db_get_string (value), db_get_string_size (value)).c_str ());
/* *INDENT-ON* */
}
else
{
valid = false;
}
db_make_int (result, valid ? 1 : 0);
return NO_ERROR;
}
int
db_evaluate_json_length (DB_VALUE * result, DB_VALUE * const *arg, int const num_args)
{
JSON_DOC_STORE source_doc;
int error_code = NO_ERROR;
db_make_null (result);
if (num_args < 1 || num_args > 2)
{
assert (false);
return ER_FAILED;
}
if (is_any_arg_null (arg, num_args))
{
return NO_ERROR;
}
DB_VALUE *json = arg[0];
DB_VALUE *path = (num_args == 1) ? NULL : arg[1];
unsigned int length;
error_code = db_value_to_json_doc (*json, false, source_doc);
if (error_code != NO_ERROR)
{
return error_code;
}
if (path != NULL)
{
JSON_DOC_STORE extracted_doc;
/* *INDENT-OFF* */
std::string raw_path;
error_code = db_value_to_json_path (*path, F_JSON_LENGTH, raw_path);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
error_code = db_json_extract_document_from_path (source_doc.get_immutable (), raw_path, extracted_doc, false);
source_doc = std::move (extracted_doc);
/* *INDENT-ON* */
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
}
if (source_doc.get_immutable () != NULL)
{
length = db_json_get_length (source_doc.get_immutable ());
db_make_int (result, length);
}
return NO_ERROR;
}
int
db_evaluate_json_depth (DB_VALUE * result, DB_VALUE * const *arg, int const num_args)
{
db_make_null (result);
if (num_args != 1)
{
assert (false);
return ER_FAILED;
}
DB_VALUE *json = arg[0];
if (DB_IS_NULL (json))
{
return NO_ERROR;
}
JSON_DOC_STORE source_doc;
int error_code = db_value_to_json_doc (*json, false, source_doc);
if (error_code != NO_ERROR)
{
return error_code;
}
unsigned int depth = db_json_get_depth (source_doc.get_immutable ());
return db_make_int (result, depth);
}
int
db_evaluate_json_quote (DB_VALUE * result, DB_VALUE * const *arg, int const num_args)
{
if (num_args != 1)
{
assert (false);
return ER_FAILED;
}
return db_string_quote (arg[0], result);
}
int
db_evaluate_json_unquote (DB_VALUE * result, DB_VALUE * const *arg, int const num_args)
{
int error_code = NO_ERROR;
db_make_null (result);
if (num_args != 1)
{
assert (false);
return ER_FAILED;
}
DB_VALUE *json = arg[0];
if (DB_IS_NULL (json))
{
return NO_ERROR;
}
char *str = NULL;
JSON_DOC_STORE source_doc;
error_code = db_value_to_json_doc (*json, false, source_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
error_code = db_json_unquote (*source_doc.get_immutable (), str);
if (error_code != NO_ERROR)
{
return error_code;
}
error_code = db_make_string (result, str);
if (error_code != NO_ERROR)
{
return error_code;
}
// db_json_unquote uses strdup, therefore set need_clear flag
result->need_clear = true;
return NO_ERROR;
}
int
db_evaluate_json_pretty (DB_VALUE * result, DB_VALUE * const *arg, int const num_args)
{
int error_code = NO_ERROR;
db_make_null (result);
if (num_args != 1)
{
assert (false);
return ER_FAILED;
}
DB_VALUE *json = arg[0];
if (DB_IS_NULL (json))
{
return NO_ERROR;
}
char *str = NULL;
JSON_DOC_STORE source_doc;
error_code = db_value_to_json_doc (*json, false, source_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
db_json_pretty_func (*source_doc.get_immutable (), str);
error_code = db_make_string (result, str);
if (error_code != NO_ERROR)
{
return error_code;
}
// db_json_pretty_func uses strdup, therefore set need_clear flag
result->need_clear = true;
return error_code;
}
int
db_accumulate_json_arrayagg (const DB_VALUE * json_db_val, DB_VALUE * json_res)
{
int error_code = NO_ERROR;
JSON_DOC_STORE val_doc;
JSON_DOC_STORE result_doc;
if (DB_IS_NULL (json_db_val))
{
// this case should not be possible because we already wrapped a NULL value into a JSON with type DB_JSON_NULL
assert (false);
db_make_null (json_res);
return ER_FAILED;
}
// get the current value
error_code = db_value_to_json_value (*json_db_val, val_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
// append to existing document
// allocate only first time
if (DB_IS_NULL (json_res))
{
result_doc.create_mutable_reference ();
}
else
{
result_doc.set_mutable_reference (db_get_json_document (json_res));
}
if (result_doc.get_immutable () == NULL)
{
db_make_null (json_res);
return ER_FAILED;
}
db_json_add_element_to_array (result_doc.get_mutable (), val_doc.get_immutable ());
db_make_json_from_doc_store_and_release (*json_res, result_doc);
return error_code;
}
/*
* db_accumulate_json_objectagg () - Construct a Member (key-value pair) and add it in the result_json
*
* return : error_code
* json_key (in) : the key of the pair
* json_val (in) : the value of the pair
* json_res (in) : the DB_VALUE that contains the document where we want to insert
*/
int
db_accumulate_json_objectagg (const DB_VALUE * json_key, const DB_VALUE * json_db_val, DB_VALUE * json_res)
{
int error_code = NO_ERROR;
JSON_DOC_STORE val_doc;
JSON_DOC_STORE result_doc;
// this case should not be possible because we checked before if the key is NULL
// and wrapped the value with a JSON with DB_JSON_NULL type
if (DB_IS_NULL (json_key) || DB_IS_NULL (json_db_val))
{
assert (false);
db_make_null (json_res);
return ER_FAILED;
}
// get the current key
/* *INDENT-OFF* */
std::string key_str;
/* *INDENT-ON* */
error_code = db_value_to_json_key (*json_key, key_str);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
// get the current value
error_code = db_value_to_json_value (*json_db_val, val_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
// append to existing document
// allocate only first time
if (DB_IS_NULL (json_res))
{
result_doc.create_mutable_reference ();
}
else
{
result_doc.set_mutable_reference (db_get_json_document (json_res));
}
if (result_doc.get_immutable () == NULL)
{
db_make_null (json_res);
return ER_FAILED;
}
error_code = db_json_add_member_to_object (result_doc.get_mutable (), key_str.c_str (), val_doc.get_immutable ());
db_make_json_from_doc_store_and_release (*json_res, result_doc);
if (error_code == ER_JSON_DUPLICATE_KEY)
{
// ignore
er_clear ();
error_code = NO_ERROR;
}
return error_code;
}
//
// db_evaluate_json_extract () - extract paths from JSON and return a JSON object if there is only one path or a JSON
// array if there are multiple paths
//
// return : error code
// result (in) : result
// args[] (in) :
// num_args (in) :
//
// TODO: we need to change the args type of all JSON function to const DB_VALUE *[]
//
int
db_evaluate_json_extract (DB_VALUE * result, DB_VALUE * const *args, int num_args)
{
db_make_null (result);
if (num_args < 2)
{
// should be detected early
assert (false);
return ER_FAILED;
}
// there are multiple paths; the result of extract is a JSON_ARRAY with all extracted values
int error_code = NO_ERROR;
JSON_DOC_STORE source_doc;
if (is_any_arg_null (args, num_args))
{
return NO_ERROR;
}
error_code = db_value_to_json_doc (*args[0], false, source_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
/* *INDENT-OFF* */
std::vector<std::string> paths;
/* *INDENT-ON* */
for (int path_idx = 1; path_idx < num_args; path_idx++)
{
const DB_VALUE *path_value = args[path_idx];
paths.emplace_back ();
error_code = db_value_to_json_path (*path_value, F_JSON_EXTRACT, paths.back ());
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
}
JSON_DOC_STORE res_doc;
error_code = db_json_extract_document_from_path (source_doc.get_immutable (), paths, res_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
if (db_json_get_type (res_doc.get_immutable ()) != DB_JSON_NULL)
{
db_make_json_from_doc_store_and_release (*result, res_doc);
}
return NO_ERROR;
}
int
db_evaluate_json_object (DB_VALUE * result, DB_VALUE * const *arg, int const num_args)
{
int i;
int error_code = NO_ERROR;
JSON_DOC_STORE value_doc;
db_make_null (result);
if (num_args % 2 != 0)
{
assert (false); // should be caught earlier
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_FAILED;
}
JSON_DOC_STORE new_doc;
new_doc.set_mutable_reference (db_json_make_json_object ());
for (i = 0; i < num_args; i += 2)
{
if (DB_IS_NULL (arg[i]))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_JSON_OBJECT_NAME_IS_NULL, 0);
return ER_JSON_OBJECT_NAME_IS_NULL;
}
/* *INDENT-OFF* */
std::string value_key;
/* *INDENT-ON* */
error_code = db_value_to_json_key (*arg[i], value_key);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
error_code = db_value_to_json_value (*arg[i + 1], value_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
error_code =
db_json_add_member_to_object (new_doc.get_mutable (), value_key.c_str (), value_doc.get_immutable ());
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
}
db_make_json_from_doc_store_and_release (*result, new_doc);
return NO_ERROR;
}
int
db_evaluate_json_array (DB_VALUE * result, DB_VALUE * const *arg, int const num_args)
{
int error_code;
JSON_DOC_STORE new_doc;
new_doc.set_mutable_reference (db_json_make_json_array ());
JSON_DOC_STORE value_doc;
db_make_null (result);
for (int i = 0; i < num_args; i++)
{
error_code = db_value_to_json_value (*arg[i], value_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
db_json_add_element_to_array (new_doc.get_mutable (), value_doc.get_immutable ());
}
db_make_json_from_doc_store_and_release (*result, new_doc);
return NO_ERROR;
}
int
db_evaluate_json_insert (DB_VALUE * result, DB_VALUE * const *arg, int const num_args)
{
int i, error_code = NO_ERROR;
JSON_DOC_STORE new_doc;
JSON_DOC_STORE value_doc;
db_make_null (result);
if (num_args < 3 || num_args % 2 == 0)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_FAILED;
}
if (DB_IS_NULL (arg[0]))
{
return db_make_null (result);
}
error_code = db_value_to_json_doc (*arg[0], true, new_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
for (i = 1; i < num_args; i += 2)
{
if (DB_IS_NULL (arg[i]))
{
return db_make_null (result);
}
// extract path
/* *INDENT-OFF* */
std::string value_path;
/* *INDENT-ON* */
error_code = db_value_to_json_path (*arg[i], F_JSON_INSERT, value_path);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
// extract json value
error_code = db_value_to_json_value (*arg[i + 1], value_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
// insert into result the value at required path
error_code = db_json_insert_func (value_doc.get_immutable (), *new_doc.get_mutable (), value_path.c_str ());
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
}
db_make_json_from_doc_store_and_release (*result, new_doc);
return NO_ERROR;
}
int
db_evaluate_json_replace (DB_VALUE * result, DB_VALUE * const *arg, int const num_args)
{
int i, error_code = NO_ERROR;
JSON_DOC_STORE new_doc;
JSON_DOC_STORE value_doc;
db_make_null (result);
if (num_args < 3 || num_args % 2 == 0)
{
assert_release (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_FAILED;
}
if (DB_IS_NULL (arg[0]))
{
return NO_ERROR;
}
error_code = db_value_to_json_doc (*arg[0], true, new_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
for (i = 1; i < num_args; i += 2)
{
if (DB_IS_NULL (arg[i]))
{
return db_make_null (result);
}
// extract path
/* *INDENT-OFF* */
std::string value_path;
/* *INDENT-ON* */
error_code = db_value_to_json_path (*arg[i], F_JSON_REPLACE, value_path);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
// extract json value
error_code = db_value_to_json_value (*arg[i + 1], value_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
// insert into result the value at requred path
error_code = db_json_replace_func (value_doc.get_immutable (), *new_doc.get_mutable (), value_path.c_str ());
if (error_code != NO_ERROR)
{
return error_code;
}
}
db_make_json_from_doc_store_and_release (*result, new_doc);
return NO_ERROR;
}
int
db_evaluate_json_set (DB_VALUE * result, DB_VALUE * const *arg, int const num_args)
{
int i, error_code = NO_ERROR;
JSON_DOC_STORE new_doc;
JSON_DOC_STORE value_doc;
db_make_null (result);
if (num_args < 3 || num_args % 2 == 0)
{
assert_release (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_FAILED;
}
if (DB_IS_NULL (arg[0]))
{
return NO_ERROR;
}
error_code = db_value_to_json_doc (*arg[0], true, new_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
for (i = 1; i < num_args; i += 2)
{
if (DB_IS_NULL (arg[i]))
{
return db_make_null (result);
}
// extract path
/* *INDENT-OFF* */
std::string value_path;
/* *INDENT-ON* */
error_code = db_value_to_json_path (*arg[i], F_JSON_SET, value_path);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
// extract json value
error_code = db_value_to_json_value (*arg[i + 1], value_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
// insert into result the value at requred path
error_code = db_json_set_func (value_doc.get_immutable (), *new_doc.get_mutable (), value_path.c_str ());
if (error_code != NO_ERROR)
{
return error_code;
}
}
db_make_json_from_doc_store_and_release (*result, new_doc);
return NO_ERROR;
}
int
db_evaluate_json_keys (DB_VALUE * result, DB_VALUE * const *arg, int const num_args)
{
int error_code = NO_ERROR;
JSON_DOC_STORE new_doc;
/* *INDENT-OFF* */
std::string path;
/* *INDENT-ON* */
db_make_null (result);
if (num_args > 2)
{
assert_release (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_FAILED;
}
if (is_any_arg_null (arg, num_args))
{
return NO_ERROR;
}
if (num_args == 1)
{
path = "";
}
else
{
error_code = db_value_to_json_path (*arg[1], F_JSON_KEYS, path);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
}
error_code = db_value_to_json_doc (*arg[0], false, new_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
JSON_DOC_STORE result_json;
result_json.create_mutable_reference ();
error_code = db_json_keys_func (*new_doc.get_immutable (), *result_json.get_mutable (), path.c_str ());
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
db_make_json_from_doc_store_and_release (*result, result_json);
return NO_ERROR;
}
int
db_evaluate_json_remove (DB_VALUE * result, DB_VALUE * const *arg, int const num_args)
{
int i, error_code;
JSON_DOC_STORE new_doc;
// *INDENT-OFF*
std::string path;
// *INDENT-ON*
db_make_null (result);
if (num_args < 2)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_FAILED;
}
if (is_any_arg_null (arg, num_args))
{
return NO_ERROR;
}
error_code = db_value_to_json_doc (*arg[0], true, new_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
for (i = 1; i < num_args; i++)
{
error_code = db_value_to_json_path (*arg[i], F_JSON_REMOVE, path);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
error_code = db_json_remove_func (*new_doc.get_mutable (), path.c_str ());
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
}
db_make_json_from_doc_store_and_release (*result, new_doc);
return NO_ERROR;
}
int
db_evaluate_json_array_append (DB_VALUE * result, DB_VALUE * const *arg, int const num_args)
{
int i, error_code = NO_ERROR;
JSON_DOC_STORE new_doc;
JSON_DOC_STORE value_doc;
db_make_null (result);
if (num_args < 3 || num_args % 2 == 0)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_FAILED;
}
if (DB_IS_NULL (arg[0]))
{
return NO_ERROR;
}
error_code = db_value_to_json_doc (*arg[0], true, new_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
for (i = 1; i < num_args; i += 2)
{
if (DB_IS_NULL (arg[i]))
{
return db_make_null (result);
}
// extract path
/* *INDENT-OFF* */
std::string value_path;
/* *INDENT-ON* */
error_code = db_value_to_json_path (*arg[i], F_JSON_ARRAY_APPEND, value_path);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
// extract json value
error_code = db_value_to_json_value (*arg[i + 1], value_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
// insert into result the value at required path
error_code = db_json_array_append_func (value_doc.get_immutable (), *new_doc.get_mutable (), value_path.c_str ());
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
}
db_make_json_from_doc_store_and_release (*result, new_doc);
return NO_ERROR;
}
int
db_evaluate_json_array_insert (DB_VALUE * result, DB_VALUE * const *arg, int const num_args)
{
int i, error_code = NO_ERROR;
JSON_DOC_STORE new_doc;
JSON_DOC_STORE value_doc;
db_make_null (result);
if (num_args < 3 || num_args % 2 == 0)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_FAILED;
}
if (DB_IS_NULL (arg[0]))
{
return NO_ERROR;
}
error_code = db_value_to_json_doc (*arg[0], true, new_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
for (i = 1; i < num_args; i += 2)
{
if (DB_IS_NULL (arg[i]))
{
return db_make_null (result);
}
// extract path
/* *INDENT-OFF* */
std::string value_path;
/* *INDENT-ON* */
error_code = db_value_to_json_path (*arg[i], F_JSON_ARRAY_INSERT, value_path);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
// extract json value
error_code = db_value_to_json_value (*arg[i + 1], value_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
// insert into result the value at required path
error_code = db_json_array_insert_func (value_doc.get_immutable (), *new_doc.get_mutable (), value_path.c_str ());
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
}
db_make_json_from_doc_store_and_release (*result, new_doc);
return NO_ERROR;
}
int
db_evaluate_json_contains_path (DB_VALUE * result, DB_VALUE * const *arg, const int num_args)
{
bool exists = false;
int error_code = NO_ERROR;
JSON_DOC_STORE doc;
/* *INDENT-OFF* */
std::vector<std::string> paths;
/* *INDENT-ON* */
db_make_null (result);
if (is_any_arg_null (arg, num_args))
{
return NO_ERROR;
}
error_code = db_value_to_json_doc (*arg[0], false, doc);
if (error_code != NO_ERROR)
{
return error_code;
}
bool find_all;
error_code = is_str_find_all (arg[1], find_all);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
for (int i = 2; i < num_args; ++i)
{
/* *INDENT-OFF* */
paths.emplace_back ();
/* *INDENT-ON* */
error_code = db_value_to_json_path (*arg[i], F_JSON_CONTAINS_PATH, paths.back ());
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
}
error_code = db_json_contains_path (doc.get_immutable (), paths, find_all, exists);
if (error_code != NO_ERROR)
{
return error_code;
}
db_make_int (result, (int) exists);
return error_code;
}
/*
* db_evaluate_json_merge_preserve ()
*
* this function accumulate-merges jsons preserving members having duplicate keys
* so merge (j1, j2, j3, j4) = merge (j1, (merge (j2, merge (j3, j4))))
*
* result (out): the merge result
* arg (in): the arguments for the merge function
* num_args (in)
*/
int
db_evaluate_json_merge_preserve (DB_VALUE * result, DB_VALUE * const *arg, const int num_args)
{
int error_code;
JSON_DOC *accumulator = nullptr;
JSON_DOC_STORE accumulator_owner;
JSON_DOC_STORE doc;
if (num_args < 2)
{
db_make_null (result);
return NO_ERROR;
}
if (is_any_arg_null (arg, num_args))
{
db_make_null (result);
return NO_ERROR;
}
for (int i = 0; i < num_args; ++i)
{
error_code = db_value_to_json_doc (*arg[i], false, doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
error_code = db_json_merge_preserve_func (doc.get_immutable (), accumulator);
accumulator_owner.set_mutable_reference (accumulator);
if (error_code != NO_ERROR)
{
return error_code;
}
}
db_make_json_from_doc_store_and_release (*result, accumulator_owner);
return NO_ERROR;
}
/*
* db_evaluate_json_merge_patch ()
*
* this function accumulate-merges jsons and patches members having duplicate keys
* so merge (j1, j2, j3, j4) = merge (j1, (merge (j2, merge (j3, j4))))
*
* result (out): the merge result
* arg (in): the arguments for the merge function
* num_args (in)
*/
int
db_evaluate_json_merge_patch (DB_VALUE * result, DB_VALUE * const *arg, const int num_args)
{
int error_code;
JSON_DOC *accumulator = nullptr;
JSON_DOC_STORE accumulator_owner;
JSON_DOC_STORE doc;
if (num_args < 2)
{
db_make_null (result);
return NO_ERROR;
}
if (is_any_arg_null (arg, num_args))
{
db_make_null (result);
return NO_ERROR;
}
for (int i = 0; i < num_args; ++i)
{
error_code = db_value_to_json_doc (*arg[i], false, doc);
if (error_code != NO_ERROR)
{
return error_code;
}
error_code = db_json_merge_patch_func (doc.get_immutable (), accumulator);
accumulator_owner.set_mutable_reference (accumulator);
if (error_code != NO_ERROR)
{
return error_code;
}
}
db_make_json_from_doc_store_and_release (*result, accumulator_owner);
return NO_ERROR;
}
/* *INDENT-OFF* */
/*
* JSON_SEARCH (json_doc, one/all, pattern [, escape_char, path_1,... path_n])
*
* db_json_search_dbval ()
* function that finds paths of json_values that match the pattern argument
* result (out): json string or json array if there are more paths that match
* args (in): the arguments for the json_search function
* num_args (in)
*/
int
db_evaluate_json_search (DB_VALUE *result, DB_VALUE * const * args, const int num_args)
{
int error_code = NO_ERROR;
JSON_DOC_STORE doc;
const size_t ESCAPE_CHAR_ARG_INDEX = 3;
if (num_args < 3)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_FAILED;
}
for (int i = 0; i < num_args; ++i)
{
// only escape char might be null
if (i != ESCAPE_CHAR_ARG_INDEX && DB_IS_NULL (args[i]))
{
return db_make_null (result);
}
}
error_code = db_value_to_json_doc (*args[0], false, doc);
if (error_code != NO_ERROR)
{
return error_code;
}
bool find_all;
error_code = is_str_find_all (args[1], find_all);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
DB_VALUE *pattern = args[2];
const DB_VALUE *esc_char = nullptr;
const char * slash_str = "\\";
DB_VALUE default_slash_str_dbval;
if (num_args >= 4)
{
esc_char = args[3];
}
else
{
// No escape char arg provided
if (prm_get_bool_value (PRM_ID_NO_BACKSLASH_ESCAPES) == false)
{
// This is equivalent to compat_mode=mysql. In this mode '\\' is default escape character for LIKE pattern
db_make_string (&default_slash_str_dbval, slash_str);
esc_char = &default_slash_str_dbval;
}
}
std::vector<std::string> starting_paths;
for (int i = 4; i < num_args; ++i)
{
starting_paths.emplace_back ();
error_code = db_value_to_json_path (*args[i], F_JSON_SEARCH, starting_paths.back ());
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
}
if (starting_paths.empty ())
{
starting_paths.push_back ("$");
}
std::vector<JSON_PATH> paths;
error_code = db_json_search_func (*doc.get_immutable (), pattern, esc_char, paths, starting_paths, find_all);
if (error_code != NO_ERROR)
{
return error_code;
}
if (paths.empty ())
{
return db_make_null (result);
}
JSON_DOC *result_json = nullptr;
if (paths.size () == 1)
{
std::string path = paths[0].dump_json_path ();
error_code = db_json_path_unquote_object_keys_external (path);
if (error_code)
{
return error_code;
}
char *escaped;
size_t escaped_size;
error_code = db_string_escape_str (path.c_str (), path.size (), &escaped, &escaped_size);
cubmem::private_unique_ptr<char> escaped_unique_ptr (escaped, NULL);
if (error_code)
{
return error_code;
}
error_code = db_json_get_json_from_str (escaped, result_json, escaped_size);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
return db_make_json (result, result_json, true);
}
JSON_DOC_STORE result_json_owner;
JSON_DOC_STORE json_array_elem_owner;
result_json_owner.create_mutable_reference ();
for (std::size_t i = 0; i < paths.size (); ++i)
{
std::string path = paths[i].dump_json_path ();
error_code = db_json_path_unquote_object_keys_external (path);
if (error_code != NO_ERROR)
{
return error_code;
}
char *escaped;
size_t escaped_size;
error_code = db_string_escape_str (path.c_str (), path.size (), &escaped, &escaped_size);
cubmem::private_unique_ptr<char> escaped_unique_ptr (escaped, NULL);
if (error_code)
{
return error_code;
}
JSON_DOC *json_array_elem = nullptr;
error_code = db_json_get_json_from_str (escaped, json_array_elem, escaped_size);
json_array_elem_owner.set_mutable_reference (json_array_elem);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
db_json_add_element_to_array (result_json_owner.get_mutable (), json_array_elem_owner.get_immutable ());
}
db_make_json_from_doc_store_and_release (*result, result_json_owner);
return NO_ERROR;
}
/* *INDENT-ON* */
int
db_evaluate_json_get_all_paths (DB_VALUE * result, DB_VALUE * const *arg, int const num_args)
{
int error_code = NO_ERROR;
JSON_DOC_STORE new_doc;
db_make_null (result);
if (num_args != 1)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_FAILED;
}
if (DB_IS_NULL (arg[0]))
{
return NO_ERROR;
}
error_code = db_value_to_json_doc (*arg[0], false, new_doc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
JSON_DOC *result_json = db_json_allocate_doc ();
error_code = db_json_get_all_paths_func (*new_doc.get_immutable (), result_json);
db_make_json (result, result_json, true);
return NO_ERROR;
}
int
db_least_or_greatest (DB_VALUE * arg1, DB_VALUE * arg2, DB_VALUE * result, bool least)
{
int error_code = NO_ERROR;
bool can_compare = false;
DB_VALUE_COMPARE_RESULT cmp_result = DB_UNK;
cmp_result = tp_value_compare_with_error (arg1, arg2, 1, 0, &can_compare);
if (cmp_result == DB_EQ)
{
pr_clone_value (arg1, result);
}
else if (cmp_result == DB_GT)
{
if (least)
{
pr_clone_value (arg2, result);
}
else
{
pr_clone_value (arg1, result);
}
}
else if (cmp_result == DB_LT)
{
if (least)
{
pr_clone_value (arg1, result);
}
else
{
pr_clone_value (arg2, result);
}
}
else if (cmp_result == DB_UNK && can_compare == false)
{
return ER_FAILED;
}
else
{
assert_release (DB_IS_NULL (arg1) || DB_IS_NULL (arg2));
db_make_null (result);
return NO_ERROR;
}
return error_code;
}
static int
is_str_find_all (DB_VALUE * val, bool & find_all)
{
if (DB_IS_NULL (val))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_INVALID_ONE_ALL_ARGUMENT, 0);
return ER_INVALID_ONE_ALL_ARGUMENT;
}
if (!DB_IS_STRING (val))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_INVALID_ONE_ALL_ARGUMENT, 0);
return ER_INVALID_ONE_ALL_ARGUMENT;
}
// *INDENT-OFF*
std::string find_all_str (db_get_string (val), db_get_string_size (val));
std::transform (find_all_str.begin (), find_all_str.end (), find_all_str.begin (), [] (unsigned char c)
{
return std::tolower (c);
});
// *INDENT-ON*
find_all = false;
if (find_all_str == "all")
{
find_all = true;
}
if (!find_all && find_all_str != "one")
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_INVALID_ONE_ALL_ARGUMENT, 0);
return ER_INVALID_ONE_ALL_ARGUMENT;
}
return NO_ERROR;
}
static bool
is_any_arg_null (DB_VALUE * const *args, int num_args)
{
for (int i = 0; i < num_args; ++i)
{
if (DB_IS_NULL (args[i]))
{
return true;
}
}
return false;
}