File string_opfunc.c¶
File List > cubrid > src > query > string_opfunc.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.
*
*/
/*
* string_opfunc.c - Routines that manipulate arbitrary strings
*/
#ident "$Id$"
/* This includes bit strings, character strings, and national character strings
*/
#include "config.h"
#include <assert.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <sys/timeb.h>
#include <assert.h>
#include "string_opfunc.h"
#include "chartype.h"
#include "system_parameter.h"
#include "intl_support.h"
#include "error_manager.h"
#include "tz_support.h"
#include "db_date.h"
#include "misc_string.h"
#include "crypt_opfunc.h"
#include "base64.h"
#include "tz_support.h"
#include "object_primitive.h"
#include "object_representation.h"
#include "dbtype.h"
#include "elo.h"
#include "es_common.h"
#include "db_elo.h"
#include "string_regex.hpp"
#include "tz_support.h"
#include "util_func.h"
#include <algorithm>
#include <string>
#include <locale>
#if !defined (SERVER_MODE)
#include "parse_tree.h"
#include "es_common.h"
#else
#include "misctype_def.h"
#endif /* !defined (SERVER_MODE) */
// 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) */
#define BYTE_SIZE (8)
#define QSTR_VALUE_PRECISION(value) \
((DB_VALUE_PRECISION(value) == TP_FLOATING_PRECISION_VALUE) \
? db_get_string_length(value) : \
DB_VALUE_PRECISION(value))
#define QSTR_MAX_PRECISION(str_type) \
(QSTR_IS_CHAR(str_type) ? DB_MAX_VARCHAR_PRECISION : DB_MAX_VARBIT_PRECISION)
#define ABS(i) ((i) >= 0 ? (i) : -(i))
#define STACK_SIZE 100
#define LEAP(y) (((y) % 400 == 0) || ((y) % 100 != 0 && (y) % 4 == 0))
#define DBL_MAX_DIGITS ((int)ceil(DBL_MAX_EXP * log10((double) FLT_RADIX)))
#define UINT64_MAX_HEX_DIGITS 16
#define UINT64_MAX_BIN_DIGITS 64
#define LOB_CHUNK_SIZE (128 * 1024)
#define DB_GET_UCHAR(dbval) (REINTERPRET_CAST (const unsigned char *, db_get_string ((dbval))))
/*
* This enumeration type is used to categorize the different
* string types into function like groups.
*
* DB_STRING and DB_CHAR become QSTR_CHAR
* DB_BIT and DB_VARBIT become QSTR_BIT
* All others become QSTR_UNKNOWN, although this
* categorizations doesn't apply to
* any other domain type.
*/
typedef enum
{
QSTR_UNKNOWN,
QSTR_CHAR,
QSTR_BIT
} QSTR_CATEGORY;
/* AM/PM position references */
enum
{ am_NAME = 0, pm_NAME, Am_NAME, Pm_NAME, AM_NAME, PM_NAME,
a_m_NAME, p_m_NAME, A_m_NAME, P_m_NAME, A_M_NAME, P_M_NAME
};
/*
* Number format
*/
typedef enum
{
N_END = -2, /* format string end */
N_INVALID = -1, /* invalid format */
N_FORMAT,
N_SPACE,
N_TEXT
} NUMBER_FORMAT;
typedef enum
{
SDT_DAY = 0,
SDT_MONTH,
SDT_DAY_SHORT,
SDT_MONTH_SHORT,
SDT_AM_PM
} STRING_DATE_TOKEN;
#define WHITE_CHARS " \r\t\n"
#define QSTR_DATE_LENGTH 10
#define QSTR_TIME_LENGTH 11
#define QSTR_TIME_STAMPLENGTH 22
/* multiplier ratio for TO_CHAR function : estimate result len/size based on
* format string len/size : maximum multiplier is given by:
* - format element : DAY (3)
* - result :Wednesday (9) */
#define QSTR_TO_CHAR_LEN_MULTIPLIER_RATIO LOC_PARSE_FRMT_TO_TOKEN_MULT
#define MAX_TOKEN_SIZE 16000
#define GUID_STANDARD_BYTES_LENGTH 16
typedef enum
{
COMPOSITE_YEAR = 0,
COMPOSITE_MONTH,
COMPOSITE_DAY,
COMPOSITE_HOUR,
COMPOSITE_MINUTE,
COMPOSITE_SECOND,
COMPOSITE_MILLISECOND,
COMPOSITE_MAX
} EN_COMPOSITE_POS;
static int db_string_prefix_compare (const DB_VALUE * string1, const DB_VALUE * string2, DB_VALUE * result);
static char db_string_escape_char (char c);
static int qstr_trim (MISC_OPERAND tr_operand, const unsigned char *trim, int trim_length, int trim_size,
const unsigned char *src_ptr, DB_TYPE src_type, int src_length, int src_size,
INTL_CODESET codeset, unsigned char **res, int *res_length, int *res_size);
static void trim_leading (const unsigned char *trim_charset_ptr, int trim_charset_size, const unsigned char *src_ptr,
DB_TYPE src_type, int src_length, int src_size, INTL_CODESET codeset,
unsigned char **lead_trimmed_ptr, int *lead_trimmed_length, int *lead_trimmed_size,
bool skip_spaces);
static int qstr_pad (MISC_OPERAND pad_operand, int pad_length, const unsigned char *pad_charset_ptr,
int pad_charset_length, int pad_charset_size, const unsigned char *src_ptr, DB_TYPE src_type,
int src_length, int src_size, INTL_CODESET codeset, unsigned char **result,
int *result_length, int *result_size);
static int qstr_eval_like (const char *tar, int tar_length, const char *expr, int expr_length, const char *escape,
INTL_CODESET codeset, int coll_id);
#if defined(ENABLE_UNUSED_FUNCTION)
static int kor_cmp (unsigned char *src, unsigned char *dest, int size);
#endif
static int qstr_replace (const unsigned char *src_buf, int src_len, int src_size, INTL_CODESET codeset, int coll_id,
const unsigned char *srch_str_buf, int srch_str_size, const unsigned char *repl_str_buf,
int repl_str_size, unsigned char **result_buf, int *result_len, int *result_size);
static int qstr_translate (const unsigned char *src_ptr, DB_TYPE src_type, int src_size, INTL_CODESET codeset,
const unsigned char *from_str_ptr, int from_str_size, const unsigned char *to_str_ptr,
int to_str_size, unsigned char **result_ptr, int *result_len, int *result_size);
static QSTR_CATEGORY qstr_get_category (const DB_VALUE * s);
#if defined (ENABLE_UNUSED_FUNCTION)
static bool is_string (const DB_VALUE * s);
#endif /* ENABLE_UNUSED_FUNCTION */
static bool is_char_string (const DB_VALUE * s);
static bool is_integer (const DB_VALUE * i);
static bool is_number (const DB_VALUE * n);
static int qstr_grow_string (DB_VALUE * src_string, DB_VALUE * result, int new_size);
#if defined (ENABLE_UNUSED_FUNCTION)
static int qstr_append (unsigned char *s1, int s1_length, int s1_precision, DB_TYPE s1_type, const unsigned char *s2,
int s2_length, int s2_precision, DB_TYPE s2_type, INTL_CODESET codeset, int *result_length,
int *result_size, DB_DATA_STATUS * data_status);
#endif
static int qstr_concatenate (const unsigned char *s1, int s1_length, int s1_size, int s1_precision, DB_TYPE s1_type,
const unsigned char *s2, int s2_length, int s2_size, int s2_precision, DB_TYPE s2_type,
INTL_CODESET codeset, unsigned char **result, int *result_length, int *result_size,
DB_DATA_STATUS * data_status);
static int qstr_bit_concatenate (const unsigned char *s1, int s1_length, int s1_precision, DB_TYPE s1_type,
const unsigned char *s2, int s2_length, int s2_precision, DB_TYPE s2_type,
unsigned char **result, int *result_length, int *result_size, DB_TYPE * result_type,
DB_DATA_STATUS * data_status);
static bool varchar_truncated (const unsigned char *s, DB_TYPE s_type, int s_length, int used_chars,
INTL_CODESET codeset);
static bool varbit_truncated (const unsigned char *s, int s_length, int used_bits);
static void bit_ncat (unsigned char *r, int offset, const unsigned char *s, int n);
static int bstring_fls (const char *s, int n);
static int qstr_bit_coerce (const unsigned char *src, int src_length, int src_precision, DB_TYPE src_type,
unsigned char **dest, int *dest_length, int dest_precision, DB_TYPE dest_type,
DB_DATA_STATUS * data_status);
static int qstr_coerce (const unsigned char *src, int src_length, int src_precision, DB_TYPE src_type,
INTL_CODESET src_codeset, INTL_CODESET dest_codeset, unsigned char **dest, int *dest_length,
int *dest_size, int dest_precision, DB_TYPE dest_type, DB_DATA_STATUS * data_status);
static int qstr_position (const char *sub_string, const int sub_size, const int sub_length, const char *src_string,
const char *src_end, const char *src_string_bound, int src_length, int coll_id,
bool is_forward_search, int *position);
static int qstr_bit_position (const unsigned char *sub_string, int sub_length, const unsigned char *src_string,
int src_length, int *position);
static int shift_left (unsigned char *bit_string, int bit_string_size);
static int qstr_substring (const unsigned char *src, int src_length, int start, int length, INTL_CODESET codeset,
unsigned char **r, int *r_length, int *r_size);
static int qstr_bit_substring (const unsigned char *src, int src_length, int start, int length, unsigned char **r,
int *r_length);
static void left_nshift (const unsigned char *bit_string, int bit_string_size, int shift_amount, unsigned char *r,
int r_size);
static int qstr_ffs (int v);
static int hextoi (char hex_char);
static int adjust_precision (char *data, int precision, int scale);
static int date_to_char (const DB_VALUE * src_value, const DB_VALUE * format_str, const DB_VALUE * date_lang,
DB_VALUE * result_str, const TP_DOMAIN * domain);
static int number_to_char (const DB_VALUE * src_value, const DB_VALUE * format_str, const DB_VALUE * number_lang,
DB_VALUE * result_str, const TP_DOMAIN * domain);
static int lob_to_bit_char (const DB_VALUE * src_value, DB_VALUE * result_value, DB_TYPE lob_type, int max_length);
static int lob_from_file (const char *path, const DB_VALUE * src_value, DB_VALUE * lob_value, DB_TYPE lob_type);
static int lob_length (const DB_VALUE * src_value, DB_VALUE * result_value);
static int make_number_to_char (const INTL_LANG lang, char *num_string, char *format_str, int *length,
DB_CURRENCY currency, char **result_str, INTL_CODESET codeset);
static int make_scientific_notation (char *src_string, int cipher);
static int roundoff (const INTL_LANG lang, char *src_string, int flag, int *cipher, char *format);
static int scientific_to_decimal_string (const INTL_LANG lang, char *src_string, char **scientific_str);
static int to_number_next_state (const int previous_state, const int input_char, const INTL_LANG number_lang_id);
static int make_number (char *src, char *last_src, INTL_CODESET codeset, char *token, int *token_length, DB_VALUE * r,
const int precision, const int scale, const INTL_LANG number_lang_id);
static int get_number_token (const INTL_LANG lang, char *fsp, int *length, char *last_position, char **next_fsp,
INTL_CODESET codeset);
static TIMESTAMP_FORMAT get_next_format (const char *sp, const INTL_CODESET codeset, DB_TYPE str_type,
int *format_length, const char **next_pos);
static int get_cur_year (void);
static int get_cur_month (void);
/* utility functions */
static int add_and_normalize_date_time (int *years, int *months, int *days, int *hours, int *minutes, int *seconds,
int *milliseconds, const DB_BIGINT * composite_values);
static int sub_and_normalize_date_time (int *years, int *months, int *days, int *hours, int *minutes, int *seconds,
int *milliseconds, const DB_BIGINT * composite_values);
static void set_time_argument (struct tm *dest, int year, int month, int day, int hour, int min, int sec);
static long calc_unix_timestamp (struct tm *time_argument);
#if defined (ENABLE_UNUSED_FUNCTION)
static int parse_for_next_int (char **ch, char *output);
#endif
static int db_date_add_sub_interval_composite_value (const char *expr_s, int unit, DB_BIGINT * values,
int *is_positive_value);
static int db_date_add_sub_interval_expr (DB_VALUE * result, const DB_VALUE * date, const DB_VALUE * expr,
const int unit, int is_add);
static int db_date_add_sub_interval_days (DB_VALUE * result, const DB_VALUE * date, const DB_VALUE * db_days,
bool is_add);
static int db_round_dbvalue_to_int (const DB_VALUE * src, int *result);
static int db_get_next_like_pattern_character (const char *const pattern, const int length, const INTL_CODESET codeset,
const bool has_escape_char, const char *escape_str, int *const position,
char **crt_char_p, bool * const is_escaped);
static bool is_safe_last_char_for_like_optimization (const char *chr, const bool is_escaped, INTL_CODESET codeset);
static int db_check_or_create_null_term_string (const DB_VALUE * str_val, char *pre_alloc_buf, int pre_alloc_buf_size,
bool ignore_prec_spaces, bool ignore_trail_spaces, char **str_out,
bool * do_alloc);
static bool is_str_valid_number (char *num_p, char *str_end, int base, INTL_CODESET codeset);
static bool is_valid_ip_slice (const char *ipslice);
/* reads cnt digits until non-digit char reached,
* returns nr of characters traversed
*/
static int parse_digits (char *s, int *nr, int cnt);
static int parse_time_string (const char *timestr, int timestr_size, int *sign, int *h, int *m, int *s, int *ms);
static int get_string_date_token_id (const STRING_DATE_TOKEN token_type, const INTL_LANG intl_lang_id, const char *cs,
const INTL_CODESET codeset, int *token_id, int *token_size);
static int print_string_date_token (const STRING_DATE_TOKEN token_type, const INTL_LANG intl_lang_id,
const INTL_CODESET codeset, int token_id, int case_mode, char *buffer,
int *token_size);
static void convert_locale_number (char *sz, const int size, const INTL_LANG src_locale, const INTL_LANG dst_locale);
static int parse_tzd (const char *str, const int max_expect_len);
#define TRIM_FORMAT_STRING(sz, n) {if (strlen(sz) > n) sz[n] = 0;}
#define WHITESPACE(c) (char_isspace2((c)))
/* same as characters in get_next_format */
#define PUNCTUATIONAL(c) ((c) == '-' || (c) == '/' || (c) == ',' || (c) == '.' \
|| (c) == ';' || (c) == ':' || (c) == ' ' \
|| (c) == '\t' || (c) == '\n')
/* character that need escaping when making Json String */
#define ESCAPE_CHAR(c) (c <= 0x1f || (c) == '"' || (c) == '\\')
#define SKIP_SPACES(ch, end) do {\
while (ch != end && char_isspace(*(ch))) (ch)++; \
} while(0)
#define TZD_DEFAULT_EXPECTED_LEN 4
#define TZD_MAX_EXPECTED_LEN TZ_DS_STRING_SIZE
typedef struct date_time_info DATE_TIME_INFO;
struct date_time_info
{
int h;
int mi;
int s;
int ms;
int year;
int month;
int day;
TZ_ID tz_id;
bool is_valid_tz;
};
static int get_date_time_info (DATE_TIME_INFO * dtzi, DB_TYPE res_type, const DB_VALUE * value_ptr, bool dateformat);
static int put_date_time_info (DATE_TIME_INFO * dtzi, const DB_VALUE * format, INTL_LANG date_lang_id,
const LANG_LOCALE_DATA * lld, bool dateformat, char **res_ptr);
/*
* Public Functions for Strings - Bit and Character
*/
/*
* db_string_compare () -
*
* Arguments:
* string1: Left side of compare.
* string2: Right side of compare
* result: Integer result of comparison.
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE :
* <string1> or <string2> are not character strings.
*
* ER_QSTR_INCOMPATIBLE_CODE_SETS:
* <string1> and <string2> have differing character code sets.
*
* ER_QSTR_INCOMPATIBLE_COLLATIONS
* <string1> and <string2> have incompatible collations.
*
*/
int
db_string_compare (const DB_VALUE * string1, const DB_VALUE * string2, DB_VALUE * result)
{
QSTR_CATEGORY string1_category, string2_category;
int cmp_result = 0;
DB_TYPE str1_type, str2_type;
bool ti = true;
static bool ignore_trailing_space = prm_get_bool_value (PRM_ID_IGNORE_TRAILING_SPACE);
/* Assert that DB_VALUE structures have been allocated. */
assert (string1 != (DB_VALUE *) NULL);
assert (string2 != (DB_VALUE *) NULL);
assert (result != (DB_VALUE *) NULL);
/* Categorize the two input parameters and check for errors. Verify that the parameters are both character strings.
* Verify that the input strings belong to compatible categories. */
string1_category = qstr_get_category (string1);
string2_category = qstr_get_category (string2);
str1_type = DB_VALUE_DOMAIN_TYPE (string1);
str2_type = DB_VALUE_DOMAIN_TYPE (string2);
if (!QSTR_IS_ANY_CHAR_OR_BIT (str1_type) || !QSTR_IS_ANY_CHAR_OR_BIT (str2_type))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INVALID_DATA_TYPE, 0);
return ER_QSTR_INVALID_DATA_TYPE;
}
if (string1_category != string2_category)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INCOMPATIBLE_CODE_SETS, 0);
return ER_QSTR_INCOMPATIBLE_CODE_SETS;
}
/* A string which is NULL (not the same as a NULL string) is ordered less than a string which is not NULL. Two
* strings which are NULL are ordered equivalently. If both strings are not NULL, then the strings themselves are
* compared. */
if (DB_IS_NULL (string1) && !DB_IS_NULL (string2))
{
cmp_result = -1;
}
else if (!DB_IS_NULL (string1) && DB_IS_NULL (string2))
{
cmp_result = 1;
}
else if (DB_IS_NULL (string1) && DB_IS_NULL (string2))
{
cmp_result = 0;
}
else if (db_get_string_codeset (string1) != db_get_string_codeset (string2))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INCOMPATIBLE_CODE_SETS, 0);
return ER_QSTR_INCOMPATIBLE_CODE_SETS;
}
else
{
int coll_id;
switch (string1_category)
{
case QSTR_CHAR:
assert (db_get_string_codeset (string1) == db_get_string_codeset (string2));
LANG_RT_COMMON_COLL (db_get_string_collation (string1), db_get_string_collation (string2), coll_id);
if (coll_id == -1)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INCOMPATIBLE_COLLATIONS, 0);
return ER_QSTR_INCOMPATIBLE_COLLATIONS;
}
coll_id = db_get_string_collation (string1);
assert (db_get_string_collation (string1) == db_get_string_collation (string2));
if (!ignore_trailing_space)
{
ti = (QSTR_IS_FIXED_LENGTH (str1_type) && QSTR_IS_FIXED_LENGTH (str2_type));
}
cmp_result = QSTR_COMPARE (coll_id, DB_GET_UCHAR (string1), (int) db_get_string_size (string1),
DB_GET_UCHAR (string2), (int) db_get_string_size (string2), ti);
break;
case QSTR_BIT:
cmp_result = varbit_compare (DB_GET_UCHAR (string1), (int) db_get_string_size (string1),
DB_GET_UCHAR (string2), (int) db_get_string_size (string2));
break;
default: /* QSTR_UNKNOWN */
assert (false);
}
}
if (cmp_result < 0)
{
cmp_result = -1;
}
else if (cmp_result > 0)
{
cmp_result = 1;
}
db_make_int (result, cmp_result);
return NO_ERROR;
}
/*
* db_string_unique_prefix () -
*
* Arguments:
* string1: (IN) Left side of compare.
* string2: (IN) Right side of compare.
* result: (OUT) string such that > string1, and <= string2.
*
* Returns: int
*
* Errors:
* (TBD)
*
* Note:
* The purpose of this routine is to find a prefix that is greater
* than or equal to the first string but strictly less than the second
* string.
*
* This routine assumes:
* a) The second string is strictly greater than the first
* (according to the ANSI SQL string comparison rules).
* b) The two strings are both of the same 'type', although one may be
* 'fixed' and the other may be 'varying'.
* c) No padding is done.
*
* Assert:
*
* 1. string1 != (DB_VALUE *)NULL
* 2. string2 != (DB_VALUE *)NULL
* 3. result != (DB_VALUE *)NULL
*
*/
#if 1
int
db_string_unique_prefix (const DB_VALUE * db_string1, const DB_VALUE * db_string2, DB_VALUE * db_result,
TP_DOMAIN * key_domain)
{
DB_TYPE result_type = (DB_TYPE) 0;
int error_status = NO_ERROR;
int precision;
DB_VALUE tmp_result;
int c;
bool ti = true;
static bool ignore_trailing_space = prm_get_bool_value (PRM_ID_IGNORE_TRAILING_SPACE);
/* Assertions */
assert (db_string1 != (DB_VALUE *) NULL);
assert (db_string2 != (DB_VALUE *) NULL);
assert (db_result != (DB_VALUE *) NULL);
error_status = db_string_compare (db_string1, db_string2, &tmp_result);
if ((error_status != NO_ERROR)
|| ((c = db_get_int (&tmp_result)) && ((!key_domain->is_desc && c > 0) || (key_domain->is_desc && c < 0))))
{
db_make_null (db_result);
#if defined(CUBRID_DEBUG)
if (error_status == ER_QSTR_INVALID_DATA_TYPE)
{
printf ("db_string_unique_prefix(): non-string type: %s and %s\n",
pr_type_name (DB_VALUE_DOMAIN_TYPE (db_string1)), pr_type_name (DB_VALUE_DOMAIN_TYPE (db_string2)));
}
if (error_status == ER_QSTR_INCOMPATIBLE_CODE_SETS)
{
printf ("db_string_unique_prefix(): incompatible types: %s and %s\n",
pr_type_name (DB_VALUE_DOMAIN_TYPE (db_string1)), pr_type_name (DB_VALUE_DOMAIN_TYPE (db_string2)));
}
if (db_get_int (&tmp_result) > 0)
{
printf ("db_string_unique_prefix(): string1 %s, greater than string2 %s\n", db_get_string (db_string1),
db_get_string (db_string2));
}
#endif
return ER_GENERIC_ERROR;
}
precision = DB_VALUE_PRECISION (db_string1);
/* Determine the result type */
result_type = DB_VALUE_DOMAIN_TYPE (db_string1);
if (QSTR_IS_BIT (result_type))
{
result_type = DB_TYPE_VARBIT;
}
else if (!QSTR_IS_CHAR (result_type))
{
db_make_null (db_result);
#if defined(CUBRID_DEBUG)
printf ("db_string_unique_prefix(): non-string type: %s and %s\n",
pr_type_name (DB_VALUE_DOMAIN_TYPE (db_string1)), pr_type_name (DB_VALUE_DOMAIN_TYPE (db_string2)));
#endif
return ER_GENERIC_ERROR;
}
/* A string which is NULL (not the same as a NULL string) is ordered less than a string which is not NULL. Since
* string2 is assumed to be strictly > string1, string2 can never be NULL. */
if (DB_IS_NULL (db_string1))
{
db_value_domain_init (db_result, result_type, precision, 0);
}
/* Find the first byte where the 2 strings differ. Set the result accordingly. */
else
{
int size1, size2, result_size, pad_size = 0;
const unsigned char *string1 = NULL, *string2 = NULL, *t = NULL, *key = NULL;
unsigned char *result, pad[2];
INTL_CODESET codeset;
int num_bits = -1;
int collation_id;
bool bit_use_str2_size = false;
string1 = DB_GET_UCHAR (db_string1);
size1 = (int) db_get_string_size (db_string1);
string2 = DB_GET_UCHAR (db_string2);
size2 = (int) db_get_string_size (db_string2);
codeset = db_get_string_codeset (db_string1);
collation_id = db_get_string_collation (db_string1);
assert (collation_id == db_get_string_collation (db_string2));
if (result_type == DB_TYPE_VARBIT)
{
collation_id = LANG_COLL_ISO_BINARY;
}
if (string1 == NULL || string2 == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_PARAMETER, 0);
return ER_QPROC_INVALID_PARAMETER;
}
intl_pad_char (codeset, pad, &pad_size);
trim_again:
/* We need to implicitly trim both strings since we don't want padding for the result (its of varying type) and
* since padding can mask the logical end of both of the strings. Trimming depends on codeset. */
if (!ignore_trailing_space)
{
ti = (db_string1->domain.char_info.type == DB_TYPE_CHAR)
&& (db_string2->domain.char_info.type == DB_TYPE_CHAR);
}
if (ti)
{
if (pad_size == 1)
{
for (t = string1 + (size1 - 1); t >= string1 && *t == pad[0]; t--, size1--)
{
;
}
for (t = string2 + (size2 - 1); t >= string2 && *t == pad[0]; t--, size2--)
{
;
}
}
else
{
assert (pad_size == 2);
for (t = string1 + (size1 - 2); t >= string1 && *t == pad[0] && *(t + 1) == pad[1];
t--, t--, size1--, size1--)
{
;
}
for (t = string2 + (size2 - 2); t >= string2 && *t == pad[0] && *(t + 1) == pad[1];
t--, t--, size2--, size2--)
{
;
}
if (codeset == INTL_CODESET_KSC5601_EUC)
{
/* trim also ASCII space */
intl_pad_char (INTL_CODESET_ISO88591, pad, &pad_size);
goto trim_again;
}
}
}
if (result_type == DB_TYPE_VARBIT)
{
int size;
const unsigned char *t2;
for (size = 1, t = string1, t2 = string2; size <= size1 && size <= size2; size++, t++, t2++)
{
if (*t != *t2)
{
size++;
break;
}
}
if (!(key_domain->is_desc))
{ /* normal index */
key = string2;
/* search until non-zero differentiating byte */
t2++;
size++;
bit_use_str2_size = false;
if (size >= size2)
{
size = size2;
bit_use_str2_size = true;
}
}
else
{
/* reverse index */
assert (key_domain->is_desc);
t++;
size++;
if (size >= size1)
{
/* str1 exhaused or at last byte, we use string2 as key */
key = string2;
size = size2;
bit_use_str2_size = true;
}
else
{
/* pos is already at the next diffentiating byte */
assert (size < size1);
key = string1;
bit_use_str2_size = false;
}
}
num_bits = bit_use_str2_size ? (db_string2->data.ch.medium.size) : (size * BYTE_SIZE);
result_size = (num_bits + 7) / 8;
}
else
{
error_status = QSTR_SPLIT_KEY (collation_id, key_domain->is_desc, string1, size1, string2, size2, &key,
&result_size, ti);
}
assert (error_status == NO_ERROR);
assert (key != NULL);
result = (unsigned char *) db_private_alloc (NULL, result_size + 1);
if (result)
{
if (result_size)
{
(void) memcpy (result, key, result_size);
}
result[result_size] = 0;
db_value_domain_init (db_result, result_type, precision, 0);
error_status = db_make_db_char (db_result, codeset, collation_id, REINTERPRET_CAST (char *, result),
(result_type == DB_TYPE_VARBIT ? num_bits : result_size));
db_result->need_clear = true;
}
else
{
/* will already be set by memory mgr */
assert (er_errid () != NO_ERROR);
error_status = er_errid ();
}
}
#if !defined(NDEBUG)
if (error_status == NO_ERROR)
{
int err_status2 = NO_ERROR;
int c1 = 1, c2 = -1;
err_status2 = db_string_prefix_compare (db_string1, db_result, &tmp_result);
if (err_status2 == NO_ERROR)
{
c1 = db_get_int (&tmp_result);
}
err_status2 = db_string_prefix_compare (db_result, db_string2, &tmp_result);
if (err_status2 == NO_ERROR)
{
c2 = db_get_int (&tmp_result);
}
if (!key_domain->is_desc)
{
assert (c1 < 0 && c2 <= 0);
}
else
{
assert (c1 > 0 && c2 >= 0);
}
}
#endif
return (error_status);
}
#else
int
db_string_unique_prefix (const DB_VALUE * db_string1, const DB_VALUE * db_string2, DB_VALUE * db_result)
{
DB_TYPE result_type = 0;
int error_status = NO_ERROR;
int precision, num_bits = -1;
DB_TYPE string_type;
/* Assertions */
assert (db_string1 != (DB_VALUE *) NULL);
assert (db_string2 != (DB_VALUE *) NULL);
assert (db_result != (DB_VALUE *) NULL);
precision = DB_VALUE_PRECISION (db_string1);
string_type = DB_VALUE_DOMAIN_TYPE (db_string1);
/* Determine the result type */
if (QSTR_IS_CHAR (string_type))
{
result_type = DB_TYPE_VARCHAR;
}
else if (QSTR_IS_BIT (string_type))
{
result_type = DB_TYPE_VARBIT;
}
else
{
result_type = DB_TYPE_NULL;
db_make_null (db_result);
#if defined(CUBRID_DEBUG)
printf ("db_string_unique_prefix called with non-string type: %s\n", pr_type_name (string_type));
#endif
return ER_GENERIC_ERROR;
}
/*
* A string which is NULL (not the same as a NULL string) is
* ordered less than a string which is not NULL. Since string2 is
* assumed to be strictly > string1, string2 can never be NULL.
*/
if (DB_IS_NULL (db_string1))
{
db_value_domain_init (db_result, result_type, precision, 0);
}
/*
* Find the first byte where the 2 strings differ. Set the result
* accordingly.
*/
else
{
int string1_size = db_get_string_size (db_string1);
int string2_size = db_get_string_size (db_string2);
const unsigned char *string1 = (const unsigned char *) db_get_string (db_string1);
const unsigned char *string2 = (const unsigned char *) db_get_string (db_string2);
unsigned char *result;
const unsigned char *key;
int result_size;
INTL_CODESET codeset = db_get_string_codeset ((DB_VALUE *) db_string1);
/* We need to implicitly trim both strings since we don't want padding for the result (its of varying type) and
* since padding can mask the logical end of both of the strings. We need to be careful how the trimming is
* done. Char and varchar can do the normal trim and bit and varbit don't want to trim at all. */
if (result_type == DB_TYPE_VARCHAR)
{
for (; string1_size && string1[string1_size - 1] == ' '; string1_size--)
{
; /* do nothing */
}
for (; string2_size && string2[string2_size - 1] == ' '; string2_size--)
{
; /* do nothing */
}
}
/* now find the first byte where the strings differ */
for (result_size = 0;
result_size < string1_size && result_size < string2_size && string1[result_size] == string2[result_size];
result_size++)
{
; /* do nothing */
}
/* Check for string2 < string1. This check can only be done when we haven't exhausted one of the strings. If
* string2 is exhausted it is an error. */
if ((result_size != string1_size)
&& ((result_size == string2_size) || (string2[result_size] < string1[result_size])))
{
#if defined(CUBRID_DEBUG)
printf ("db_string_unique_prefix called with ");
printf ("string1: %s, greater than string2: %s\n", string1, string2);
#endif
error_status = ER_GENERIC_ERROR;
}
else
{
if (result_size == string1_size || result_size == string2_size - 1)
{
key = string1;
result_size = string1_size;
/* if we have bits, we need all the string1 bits. Remember that you may not use all of the bits in the
* last byte. */
if (result_type == DB_TYPE_VARBIT)
{
num_bits = db_string1->data.ch.medium.size;
}
}
else
{
result_size += 1;
key = string2;
/* if we have bits, we will take all the bits for the differentiating byte. This is fine since in this
* branch we are guaranteed not to be at the end of either string. */
if (result_type == DB_TYPE_VARBIT)
{
num_bits = result_size * BYTE_SIZE;
}
}
result = db_private_alloc (NULL, result_size + 1);
if (result)
{
if (result_size)
{
memcpy (result, key, result_size);
}
result[result_size] = 0;
db_value_domain_init (db_result, result_type, precision, 0);
if (result_type == DB_TYPE_VARBIT)
{
error_status = db_make_db_char (db_result, codeset, (const char *) result, num_bits);
}
else
{
error_status = db_make_db_char (db_result, codeset, (const char *) result, result_size);
}
db_result->need_clear = true;
}
else
{
/* will already be set by memory mgr */
assert (er_errid () != NO_ERROR);
error_status = er_errid ();
}
}
}
return (error_status);
}
#endif
/*
* db_string_concatenate () -
*
* Arguments:
* string1: Left string to concatenate.
* string2: Right string to concatenate.
* result: Result of concatenation of both strings.
* data_status: DB_DATA_STATUS which indicates if truncation occurred.
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE :
* <string1> or <string2> not string types.
*
* ER_QSTR_INCOMPATIBLE_CODE_SETS:
* <string1> or <string2> have different character code sets
* or are not all bit strings.
*
*/
int
db_string_concatenate (const DB_VALUE * string1, const DB_VALUE * string2, DB_VALUE * result,
DB_DATA_STATUS * data_status)
{
QSTR_CATEGORY string1_code_set, string2_code_set;
int error_status = NO_ERROR;
DB_TYPE string_type1, string_type2;
bool is_inplace_concat;
/*
* Initialize status value
*/
*data_status = DATA_STATUS_OK;
/*
* Assert that DB_VALUE structures have been allocated.
*/
assert (string1 != (DB_VALUE *) NULL);
assert (string2 != (DB_VALUE *) NULL);
assert (result != (DB_VALUE *) NULL);
is_inplace_concat = false; /* init */
/* check iff is in-place update */
if (string1 == result || string2 == result)
{
is_inplace_concat = true;
}
/*
* Categorize the parameters into respective code sets.
* Verify that the parameters are both character strings.
* Verify that the input strings belong to compatible code sets.
*/
string1_code_set = qstr_get_category (string1);
string2_code_set = qstr_get_category (string2);
string_type1 = DB_VALUE_DOMAIN_TYPE (string1);
string_type2 = DB_VALUE_DOMAIN_TYPE (string2);
if (!QSTR_IS_ANY_CHAR_OR_BIT (string_type1) || !QSTR_IS_ANY_CHAR_OR_BIT (string_type2))
{
/* ORACLE7 ServerSQL Language Reference Manual 3-4; Although ORACLE treats zero-length character strings as
* nulls, concatenating a zero-length character string with another operand always results in the other operand,
* rather than a null. However, this may not continue to be true in future versions of ORACLE. To concatenate an
* expression that might be null, use the NVL function to explicitly convert the expression to a zero-length
* string. */
if (prm_get_bool_value (PRM_ID_ORACLE_STYLE_EMPTY_STRING))
{
if (DB_IS_NULL (string1) && QSTR_IS_ANY_CHAR_OR_BIT (string_type2))
{
pr_clone_value ((DB_VALUE *) string2, result);
}
else if (DB_IS_NULL (string2) && QSTR_IS_ANY_CHAR_OR_BIT (string_type1))
{
pr_clone_value ((DB_VALUE *) string1, result);
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
}
else if ((string1_code_set != string2_code_set))
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
else if (DB_IS_NULL (string1) || DB_IS_NULL (string2))
{
bool check_empty_string;
/* ORACLE7 ServerSQL Language Reference Manual 3-4; Although ORACLE treats zero-length character strings as
* nulls, concatenating a zero-length character string with another operand always results in the other operand,
* rather than a null. However, this may not continue to be true in future versions of ORACLE. To concatenate an
* expression that might be null, use the NVL function to explicitly convert the expression to a zero-length
* string. */
check_empty_string = prm_get_bool_value (PRM_ID_ORACLE_STYLE_EMPTY_STRING) ? true : false;
if (check_empty_string && DB_IS_NULL (string1) && QSTR_IS_ANY_CHAR_OR_BIT (string_type2))
{
pr_clone_value ((DB_VALUE *) string2, result);
}
else if (check_empty_string && DB_IS_NULL (string2) && QSTR_IS_ANY_CHAR_OR_BIT (string_type1))
{
pr_clone_value ((DB_VALUE *) string1, result);
}
else
{
if (QSTR_IS_CHAR (string_type1))
{
if (string_type1 == DB_TYPE_VARCHAR || string_type2 == DB_TYPE_VARCHAR)
{
db_value_domain_init (result, DB_TYPE_VARCHAR, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
}
else
{
db_value_domain_init (result, DB_TYPE_CHAR, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
}
}
else
{
if (string_type1 == DB_TYPE_VARBIT || string_type2 == DB_TYPE_VARBIT)
{
db_value_domain_init (result, DB_TYPE_VARBIT, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
}
else
{
db_value_domain_init (result, DB_TYPE_BIT, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
}
}
}
}
else
{
unsigned char *r;
int r_length, r_size;
DB_TYPE r_type;
if (string1_code_set == QSTR_BIT)
{
int result_domain_length;
error_status = qstr_bit_concatenate (DB_GET_UCHAR (string1), (int) db_get_string_length (string1),
(int) QSTR_VALUE_PRECISION (string1), DB_VALUE_DOMAIN_TYPE (string1),
DB_GET_UCHAR (string2), (int) db_get_string_length (string2),
(int) QSTR_VALUE_PRECISION (string2), DB_VALUE_DOMAIN_TYPE (string2),
&r, &r_length, &r_size, &r_type, data_status);
if (error_status == NO_ERROR)
{
if ((DB_VALUE_PRECISION (string1) == TP_FLOATING_PRECISION_VALUE)
|| (DB_VALUE_PRECISION (string2) == TP_FLOATING_PRECISION_VALUE))
{
result_domain_length = TP_FLOATING_PRECISION_VALUE;
}
else
{
result_domain_length =
MIN (DB_MAX_BIT_LENGTH, DB_VALUE_PRECISION (string1) + DB_VALUE_PRECISION (string2));
}
qstr_make_typed_string (r_type, result, result_domain_length, (char *) r, r_length,
db_get_string_codeset (string1), db_get_string_collation (string1));
result->need_clear = true;
}
}
else
{
DB_VALUE temp;
int result_domain_length;
int common_coll;
INTL_CODESET codeset = db_get_string_codeset (string1);
db_make_null (&temp);
LANG_RT_COMMON_COLL (db_get_string_collation (string1), db_get_string_collation (string2), common_coll);
if (common_coll == -1)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INCOMPATIBLE_COLLATIONS, 0);
return ER_QSTR_INCOMPATIBLE_COLLATIONS;
}
if (db_get_string_codeset (string1) != db_get_string_codeset (string2))
{
DB_DATA_STATUS data_status;
codeset = lang_get_collation (common_coll)->codeset;
if (db_get_string_codeset (string1) != codeset)
{
db_value_domain_init (&temp, string_type1, (int) QSTR_VALUE_PRECISION (string1), 0);
db_string_put_cs_and_collation (&temp, codeset, common_coll);
error_status = db_char_string_coerce (string1, &temp, &data_status);
if (error_status != NO_ERROR)
{
pr_clear_value (&temp);
return error_status;
}
assert (data_status == DATA_STATUS_OK);
string1 = &temp;
}
else
{
assert (db_get_string_codeset (string2) != codeset);
db_value_domain_init (&temp, string_type2, (int) QSTR_VALUE_PRECISION (string2), 0);
db_string_put_cs_and_collation (&temp, codeset, common_coll);
error_status = db_char_string_coerce (string2, &temp, &data_status);
if (error_status != NO_ERROR)
{
pr_clear_value (&temp);
return error_status;
}
assert (data_status == DATA_STATUS_OK);
string2 = &temp;
}
}
error_status = qstr_concatenate (DB_GET_UCHAR (string1), (int) db_get_string_length ((DB_VALUE *) string1),
(int) db_get_string_size (string1),
(int) QSTR_VALUE_PRECISION (string1), DB_VALUE_DOMAIN_TYPE (string1),
DB_GET_UCHAR (string2), (int) db_get_string_length ((DB_VALUE *) string2),
(int) db_get_string_size (string2),
(int) QSTR_VALUE_PRECISION (string2), DB_VALUE_DOMAIN_TYPE (string2),
codeset, &r, &r_length, &r_size, data_status);
pr_clear_value (&temp);
if (error_status == NO_ERROR && r != NULL)
{
if ((DB_VALUE_PRECISION (string1) == TP_FLOATING_PRECISION_VALUE)
|| (DB_VALUE_PRECISION (string2) == TP_FLOATING_PRECISION_VALUE))
{
result_domain_length = TP_FLOATING_PRECISION_VALUE;
}
else
{
result_domain_length =
MIN (DB_MAX_VARCHAR_PRECISION, DB_VALUE_PRECISION (string1) + DB_VALUE_PRECISION (string2));
}
if (is_inplace_concat)
{
/* clear value before in-place update */
(void) pr_clear_value (result);
}
qstr_make_typed_string (DB_TYPE_VARCHAR, result, result_domain_length, (char *) r, r_size, codeset,
common_coll);
r[r_size] = 0;
result->data.ch.medium.length = r_length;
result->need_clear = true;
}
}
}
return error_status;
}
/*
* db_string_chr () - take character of db_value
* return: NO_ERROR, or ER_code
* res(OUT) : resultant db_value node
* dbval1(IN) : first db_value node
* dbval2(IN) : charset name to use
*/
int
db_string_chr (DB_VALUE * res, DB_VALUE * dbval1, DB_VALUE * dbval2)
{
DB_TYPE arg_type;
DB_BIGINT temp_bigint = 0;
unsigned int temp_arg = 0, uint_arg = 0;
int itmp = 0;
DB_BIGINT bi = 0;
double dtmp = 0;
char *num_as_bytes = NULL;
char *invalid_pos = NULL;
int num_byte_count = 0;
int i, codeset = -1, collation = -1;
int err_status = NO_ERROR;
arg_type = DB_VALUE_DOMAIN_TYPE (dbval1);
assert (dbval1 != NULL && dbval2 != NULL);
if (arg_type == DB_TYPE_NULL || DB_IS_NULL (dbval1))
{
goto exit;
}
assert (DB_VALUE_DOMAIN_TYPE (dbval2) == DB_TYPE_INTEGER);
codeset = db_get_int (dbval2);
if (codeset != INTL_CODESET_UTF8 && codeset != INTL_CODESET_ISO88591 && codeset != INTL_CODESET_KSC5601_EUC
&& codeset != INTL_CODESET_BINARY)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
err_status = ER_OBJ_INVALID_ARGUMENTS;
goto exit;
}
/* Get value according to DB_TYPE */
switch (arg_type)
{
case DB_TYPE_SHORT:
itmp = db_get_short (dbval1);
break;
case DB_TYPE_INTEGER:
itmp = db_get_int (dbval1);
break;
case DB_TYPE_BIGINT:
bi = db_get_bigint (dbval1);
break;
case DB_TYPE_FLOAT:
dtmp = db_get_float (dbval1);
break;
case DB_TYPE_DOUBLE:
dtmp = db_get_double (dbval1);
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double (db_locate_numeric (dbval1), DB_VALUE_SCALE (dbval1), &dtmp);
break;
case DB_TYPE_MONETARY:
dtmp = (db_get_monetary (dbval1))->amount;
break;
default:
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
err_status = ER_GENERIC_ERROR;
goto exit;
} /* switch */
/* bi, dtmp and itmp have the default value set to 0, so temp_bigint will hold the numeric representation of the
* first argument, regardless of its type. */
temp_bigint = bi + (DB_BIGINT) round (fmod (dtmp, 0x100000000)) + itmp;
if (temp_bigint >= 0)
{
temp_arg = DB_UINT32_MAX & temp_bigint;
}
else
{
temp_arg = DB_UINT32_MAX & (-temp_bigint);
temp_arg = DB_UINT32_MAX - temp_arg + 1;
}
uint_arg = temp_arg;
if (temp_arg == 0)
{
num_byte_count = 1;
}
else
{
while (temp_arg > 0)
{
num_byte_count++;
temp_arg >>= 8;
}
}
num_as_bytes = (char *) db_private_alloc (NULL, (1 + num_byte_count) * sizeof (char));
if (num_as_bytes == NULL)
{
err_status = ER_OUT_OF_VIRTUAL_MEMORY;
goto exit;
}
temp_arg = uint_arg;
for (i = num_byte_count - 1; i >= 0; i--)
{
num_as_bytes[i] = (char) (temp_arg & 0xFF);
temp_arg >>= 8;
}
num_as_bytes[num_byte_count] = '\0';
if ((codeset == INTL_CODESET_UTF8
&& intl_check_utf8 ((const unsigned char *) num_as_bytes, num_byte_count, &invalid_pos) != INTL_UTF8_VALID)
|| (codeset == INTL_CODESET_KSC5601_EUC
&& intl_check_euckr ((const unsigned char *) num_as_bytes, num_byte_count, &invalid_pos) != INTL_UTF8_VALID))
{
db_make_null (res);
db_private_free (NULL, num_as_bytes);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_clear ();
err_status = NO_ERROR;
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
err_status = ER_OBJ_INVALID_ARGUMENTS;
}
goto exit;
}
collation = LANG_GET_BINARY_COLLATION (codeset);
db_make_varchar (res, DB_DEFAULT_PRECISION, num_as_bytes, num_byte_count, codeset, collation);
res->need_clear = true;
exit:
return err_status;
}
/*
* db_string_instr () -
*
* Arguments:
* sub_string: String fragment to search for within <src_string>.
* src_string: String to be searched.
* result: Character or bit position of the first <sub_string>
* occurance.
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE :
* <sub_string> or <src_string> are not a character strings.
* ER_QSTR_INCOMPATIBLE_CODE_SETS:
* <sub_string> and <src_string> have different character
* code sets, or are not both bit strings.
*
*/
int
db_string_instr (const DB_VALUE * src_string, const DB_VALUE * sub_string, const DB_VALUE * start_pos,
DB_VALUE * result)
{
int error_status = NO_ERROR;
DB_TYPE str1_type, str2_type;
DB_TYPE arg3_type;
/*
* Assert that DB_VALUE structures have been allocated.
*/
assert (src_string != (DB_VALUE *) NULL);
assert (sub_string != (DB_VALUE *) NULL);
assert (start_pos != (DB_VALUE *) NULL);
assert (result != (DB_VALUE *) NULL);
/*
* Categorize the parameters into respective code sets.
* Verify that the parameters are both character strings.
* Verify that the input strings belong to compatible code sets.
*/
str1_type = DB_VALUE_DOMAIN_TYPE (src_string);
str2_type = DB_VALUE_DOMAIN_TYPE (sub_string);
arg3_type = DB_VALUE_DOMAIN_TYPE (start_pos);
if (DB_IS_NULL (src_string) || DB_IS_NULL (sub_string) || DB_IS_NULL (start_pos))
{
db_make_null (result);
}
else
{
if ((str1_type != DB_TYPE_STRING && str1_type != DB_TYPE_CHAR && str1_type != DB_TYPE_VARCHAR)
|| (str2_type != DB_TYPE_STRING && str2_type != DB_TYPE_CHAR && str2_type != DB_TYPE_VARCHAR)
|| (arg3_type != DB_TYPE_INTEGER && arg3_type != DB_TYPE_SHORT && arg3_type != DB_TYPE_BIGINT))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
else if (qstr_get_category (src_string) != qstr_get_category (sub_string)
|| (db_get_string_codeset (src_string) != db_get_string_codeset (sub_string)))
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
else
{
int position = 0;
int src_str_len;
int sub_str_len;
int offset = db_get_int (start_pos);
INTL_CODESET codeset = (INTL_CODESET) db_get_string_codeset (src_string);
const char *search_from, *src_buf, *sub_str;
int coll_id;
int sub_str_size = db_get_string_size (sub_string);
int from_byte_offset;
int src_size = db_get_string_size (src_string);
LANG_RT_COMMON_COLL (db_get_string_collation (src_string), db_get_string_collation (sub_string), coll_id);
if (coll_id == -1)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INCOMPATIBLE_COLLATIONS, 0);
return ER_QSTR_INCOMPATIBLE_COLLATIONS;
}
src_str_len = db_get_string_length (src_string);
sub_str_len = db_get_string_length (sub_string);
src_buf = db_get_string (src_string);
if (src_size < 0)
{
src_size = strlen (src_buf);
}
sub_str = db_get_string (sub_string);
if (sub_str_size < 0)
{
sub_str_size = strlen (sub_str);
}
if (offset > 0)
{
offset--;
if (offset + sub_str_len > src_str_len)
{ /* out of bound */
position = 0;
}
else
{
search_from = src_buf;
intl_char_size ((unsigned char *) search_from, offset, codeset, &from_byte_offset);
search_from += from_byte_offset;
intl_char_count ((unsigned char *) search_from, src_size - from_byte_offset, codeset, &src_str_len);
/* forward search */
error_status =
qstr_position (sub_str, sub_str_size, sub_str_len, search_from, src_buf + src_size,
src_buf + src_size, src_str_len, coll_id, true, &position);
position += (position != 0) ? offset : 0;
}
}
else if (offset < 0)
{
if (src_str_len + offset + 1 < sub_str_len)
{
position = 0;
}
else
{
int real_offset = src_str_len + offset - (sub_str_len - 1);
search_from = src_buf;
intl_char_size ((unsigned char *) search_from, real_offset, codeset, &from_byte_offset);
search_from += from_byte_offset;
/* backward search */
error_status =
qstr_position (sub_str, sub_str_size, sub_str_len, search_from, src_buf + src_size, src_buf,
src_str_len + offset + 1, coll_id, false, &position);
if (position != 0)
{
position = src_str_len - (-offset - 1) - (position - 1) - (sub_str_len - 1);
}
}
}
else
{
/* offset == 0 */
position = 0;
}
if (error_status == NO_ERROR)
{
db_make_int (result, position);
}
}
}
return error_status;
}
/*
* db_string_space () -
* returns a VARCHAR string consisting of a number of space characters equals
* to the given argument
*
* Arguments:
* count: number of space characters in the returned string
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE: count is not a discrete numeric type (integer)
* .... ...
*/
int
db_string_space (DB_VALUE const *count, DB_VALUE * result)
{
assert (count != (DB_VALUE *) NULL);
assert (result != (DB_VALUE *) NULL);
if (DB_IS_NULL (count))
{
db_make_null (result);
return NO_ERROR;
}
else
{
int len = 0;
char *space_string_p = NULL;
switch (DB_VALUE_DOMAIN_TYPE (count))
{
case DB_TYPE_SMALLINT:
len = db_get_short (count);
break;
case DB_TYPE_INTEGER:
len = db_get_int (count);
break;
case DB_TYPE_BIGINT:
len = (int) db_get_bigint (count);
break;
default:
return ER_QSTR_INVALID_DATA_TYPE;
}
if (len < 0)
{
len = 0;
}
if (len > (int) prm_get_bigint_value (PRM_ID_STRING_MAX_SIZE_BYTES))
{
er_set (ER_NOTIFICATION_SEVERITY, ARG_FILE_LINE, ER_QPROC_STRING_SIZE_TOO_BIG, 2, len,
(int) prm_get_bigint_value (PRM_ID_STRING_MAX_SIZE_BYTES));
db_make_null (result);
return NO_ERROR;
}
space_string_p = (char *) db_private_alloc (NULL, len + 1);
if (space_string_p)
{
if (len > 64)
{
/* if string is longer than 64 chars use memset to initialize it */
memset (space_string_p, ' ', len);
}
else
{
int i = 0;
while (i < len)
space_string_p[i++] = ' ';
}
space_string_p[len] = '\0';
qstr_make_typed_string (DB_TYPE_VARCHAR, result, len, space_string_p, len, LANG_COERCIBLE_CODESET,
LANG_COERCIBLE_COLL);
result->need_clear = true;
return NO_ERROR;
}
else
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
}
}
/*
* db_string_position () -
*
* Arguments:
* sub_string: String fragment to search for within <src_string>.
* src_string: String to be searched.
* result: Character or bit position of the first <sub_string>
* occurance.
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE :
* <sub_string> or <src_string> are not a character strings.
* ER_QSTR_INCOMPATIBLE_CODE_SETS:
* <sub_string> and <src_string> have different character
* code sets, or are not both bit strings.
*
*/
int
db_string_position (const DB_VALUE * sub_string, const DB_VALUE * src_string, DB_VALUE * result)
{
int error_status = NO_ERROR;
DB_TYPE str1_type, str2_type;
/*
* Assert that DB_VALUE structures have been allocated.
*/
assert (sub_string != (DB_VALUE *) NULL);
assert (src_string != (DB_VALUE *) NULL);
assert (result != (DB_VALUE *) NULL);
/*
* Categorize the parameters into respective code sets.
* Verify that the parameters are both character strings.
* Verify that the input strings belong to compatible code sets.
*/
str1_type = DB_VALUE_DOMAIN_TYPE (sub_string);
str2_type = DB_VALUE_DOMAIN_TYPE (src_string);
if (DB_IS_NULL (sub_string) || DB_IS_NULL (src_string))
{
db_make_null (result);
}
else if (!QSTR_IS_ANY_CHAR_OR_BIT (str1_type) || !QSTR_IS_ANY_CHAR_OR_BIT (str2_type))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
else if ((qstr_get_category (sub_string) != qstr_get_category (src_string))
|| (db_get_string_codeset (src_string) != db_get_string_codeset (sub_string)))
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
else
{
int position;
DB_TYPE src_type = DB_VALUE_DOMAIN_TYPE (src_string);
if (QSTR_IS_CHAR (src_type))
{
const char *src_str = db_get_string (src_string);
int src_size = db_get_string_size (src_string);
const char *sub_str = db_get_string (sub_string);
int sub_size = db_get_string_size (sub_string);
int coll_id;
LANG_RT_COMMON_COLL (db_get_string_collation (src_string), db_get_string_collation (sub_string), coll_id);
if (coll_id == -1)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INCOMPATIBLE_COLLATIONS, 0);
return ER_QSTR_INCOMPATIBLE_COLLATIONS;
}
if (src_size < 0)
{
src_size = strlen (src_str);
}
if (sub_size < 0)
{
sub_size = strlen (sub_str);
}
error_status = qstr_position (sub_str, sub_size, db_get_string_length (sub_string), src_str,
src_str + src_size, src_str + src_size, db_get_string_length (src_string),
coll_id, true, &position);
}
else
{
error_status = qstr_bit_position (DB_GET_UCHAR (sub_string), db_get_string_length (sub_string),
DB_GET_UCHAR (src_string), db_get_string_length (src_string), &position);
}
if (error_status == NO_ERROR)
{
db_make_int (result, position);
}
}
return error_status;
}
/*
* db_string_substring
*
* Arguments:
* src_string: String from which extraction will occur.
* start_pos: Character position to begin extraction from.
* extraction_length: Number of characters to extract (Optional).
* sub_string: Extracted subtring is returned here.
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE :
* <src_string> is not a string type,
* <start_pos> or <extraction_length> is not an integer type
* ER_QSTR_INCOMPATIBLE_CODE_SETS:
* <src_string> have different character
* code sets or are not both bit strings.
*
*/
int
db_string_substring (const MISC_OPERAND substr_operand, const DB_VALUE * src_string, const DB_VALUE * start_position,
const DB_VALUE * extraction_length, DB_VALUE * sub_string)
{
int error_status = NO_ERROR;
int extraction_length_is_null = false;
DB_TYPE result_type;
DB_TYPE src_type;
/*
* Assert that DB_VALUE structures have been allocated.
*/
assert (src_string != (DB_VALUE *) NULL);
assert (start_position != (DB_VALUE *) NULL);
assert (sub_string != (DB_VALUE *) NULL);
src_type = DB_VALUE_DOMAIN_TYPE (src_string);
if ((extraction_length == NULL) || DB_IS_NULL (extraction_length))
{
extraction_length_is_null = true;
}
if (QSTR_IS_CHAR (src_type))
{
result_type = DB_TYPE_VARCHAR;
}
else
{
result_type = DB_TYPE_VARBIT;
}
if (DB_IS_NULL (src_string) || DB_IS_NULL (start_position))
{
#if defined(SERVER_MODE)
assert (DB_IS_NULL (sub_string));
#endif
db_make_null (sub_string);
}
else
{
if (!QSTR_IS_ANY_CHAR_OR_BIT (src_type) || !is_integer (start_position)
|| (!extraction_length_is_null && !is_integer (extraction_length)))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
else
{
unsigned char *sub;
int sub_length;
int extract_nchars = -1;
if (!extraction_length_is_null)
{
extract_nchars = (int) db_get_int (extraction_length);
}
/* Initialize the memory manager of the substring */
if (QSTR_IS_CHAR (src_type))
{
int sub_size = 0;
const unsigned char *string = DB_GET_UCHAR (src_string);
int start_offset = db_get_int (start_position);
int string_len = db_get_string_length (src_string);
if (extraction_length_is_null)
{
extract_nchars = string_len;
}
if (substr_operand == SUBSTR)
{
if (extract_nchars < 0 || string_len < ABS (start_offset))
{
return error_status;
}
if (start_offset < 0)
{
int byte_pos;
(void) intl_char_size (string, string_len + start_offset, db_get_string_codeset (src_string),
&byte_pos);
string += byte_pos;
string_len = -start_offset;
}
}
error_status = qstr_substring (string, string_len, start_offset, extract_nchars,
db_get_string_codeset (src_string), &sub, &sub_length, &sub_size);
if (error_status == NO_ERROR && sub != NULL)
{
qstr_make_typed_string (result_type, sub_string, DB_VALUE_PRECISION (src_string), (char *) sub,
sub_size, db_get_string_codeset (src_string),
db_get_string_collation (src_string));
sub[sub_size] = 0;
sub_string->need_clear = true;
}
}
else
{
error_status = qstr_bit_substring (DB_GET_UCHAR (src_string), (int) db_get_string_length (src_string),
(int) db_get_int (start_position), extract_nchars, &sub, &sub_length);
if (error_status == NO_ERROR)
{
qstr_make_typed_string (result_type, sub_string, DB_VALUE_PRECISION (src_string), (char *) sub,
sub_length, db_get_string_codeset (src_string),
db_get_string_collation (src_string));
sub_string->need_clear = true;
}
}
}
}
return error_status;
}
static char
db_string_escape_char (char c)
{
switch (c)
{
case '\b':
return 'b';
case '\f':
return 'f';
case '\n':
return 'n';
case '\r':
return 'r';
case '\t':
return 't';
default:
return c;
}
}
int
db_string_escape_str (const char *src_str, size_t src_size, char **res_string, size_t * dest_size)
{
size_t dest_crt_pos;
size_t src_last_pos;
// *INDENT-OFF*
std::vector<size_t> special_idx;
// *INDENT-ON*
for (size_t i = 0; i < src_size; ++i)
{
unsigned char uc = (unsigned char) src_str[i];
if (ESCAPE_CHAR (uc))
{
special_idx.push_back (i);
}
}
*dest_size = src_size + special_idx.size () + 2 /* quotes */ + 1 /* string terminator */ ;
char *result = (char *) db_private_alloc (NULL, *dest_size);
if (result == NULL)
{
return ER_OUT_OF_VIRTUAL_MEMORY;
}
result[0] = '"';
dest_crt_pos = 1;
src_last_pos = 0;
for (size_t i = 0; i < special_idx.size (); ++i)
{
size_t len = special_idx[i] - src_last_pos;
if (len > 0)
{
memcpy (&result[dest_crt_pos], &src_str[src_last_pos], len);
result[dest_crt_pos] = db_string_escape_char (result[dest_crt_pos]);
dest_crt_pos += len;
}
result[dest_crt_pos] = '\\';
++dest_crt_pos;
src_last_pos = special_idx[i];
}
memcpy (&result[dest_crt_pos], &src_str[src_last_pos], src_size - src_last_pos);
result[dest_crt_pos] = db_string_escape_char (result[dest_crt_pos]);
result[*dest_size - 2] = '"';
result[*dest_size - 1] = '\0';
*res_string = result;
return NO_ERROR;
}
/*
* db_string_quote - escape a string and surround it with quotes
* return: If success, return 0.
* src(in): str
* res(out): quoted string
* Note:
*/
int
db_string_quote (const DB_VALUE * str, DB_VALUE * res)
{
if (DB_IS_NULL (str))
{
return db_make_null (res);
}
else
{
char *escaped_string = NULL;
size_t escaped_string_size;
int error_code =
db_string_escape_str (db_get_string (str), db_get_string_size (str), &escaped_string, &escaped_string_size);
if (error_code)
{
return error_code;
}
db_make_null (res);
DB_TYPE result_type = DB_TYPE_VARCHAR;
qstr_make_typed_string (result_type, res, TP_FLOATING_PRECISION_VALUE, escaped_string,
(int) escaped_string_size - 1, db_get_string_codeset (str),
db_get_string_collation (str));
res->need_clear = true;
return NO_ERROR;
}
}
/*
* db_string_repeat
*
* Arguments:
* src_string: String which repeats itself.
* count: Number of repetitions.
* result: string containing the repeated original.
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE :
* <src_string> is not a string type,
* <start_pos> or <extraction_length> is not an integer type
*
*/
int
db_string_repeat (const DB_VALUE * src_string, const DB_VALUE * count, DB_VALUE * result)
{
int error_status = NO_ERROR;
int src_length, count_i = 0, src_size = 0;
DB_TYPE result_type = DB_TYPE_NULL;
DB_TYPE src_type;
INTL_CODESET codeset;
/*
* Assert that DB_VALUE structures have been allocated.
*/
assert (src_string != (DB_VALUE *) NULL);
assert (count != (DB_VALUE *) NULL);
assert (result != (DB_VALUE *) NULL);
assert (!DB_IS_NULL (src_string) && !DB_IS_NULL (count));
src_type = DB_VALUE_DOMAIN_TYPE (src_string);
src_length = (int) db_get_string_length (src_string);
count_i = db_get_int (count);
codeset = db_get_string_codeset (src_string);
if (QSTR_IS_CHAR (src_type))
{
result_type = DB_TYPE_VARCHAR;
}
src_size = db_get_string_size (src_string);
if (src_size < 0)
{
intl_char_size (DB_GET_UCHAR (result), src_length, codeset, &src_size);
}
if (!QSTR_IS_ANY_CHAR (src_type) || !is_integer (count))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
else if (count_i <= 0 || src_length <= 0)
{
error_status =
db_string_make_empty_typed_string (result, result_type, src_length, db_get_string_codeset (src_string),
db_get_string_collation (src_string));
if (error_status != NO_ERROR)
{
return error_status;
}
}
else
{
DB_VALUE dummy;
char *res_ptr;
const char *src_ptr;
DB_BIGINT new_length, expected_size;
/* init dummy */
db_make_null (&dummy);
/* create an empy string for result */
new_length = (DB_BIGINT) src_length *count_i;
if (OR_CHECK_INT_OVERFLOW (new_length))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_PRECISION_OVERFLOW, 2, new_length, DB_INT32_MAX);
return ER_PRECISION_OVERFLOW;
}
error_status =
db_string_make_empty_typed_string (&dummy, result_type, (int) new_length,
db_get_string_codeset (src_string), db_get_string_collation (src_string));
if (error_status != NO_ERROR)
{
return error_status;
}
if (prm_get_bool_value (PRM_ID_ORACLE_STYLE_EMPTY_STRING) == true && DB_IS_NULL (&dummy)
&& QSTR_IS_ANY_CHAR_OR_BIT (DB_VALUE_DOMAIN_TYPE (&dummy)))
{
/* intermediate value : clear is_null flag */
dummy.domain.general_info.is_null = 0;
}
expected_size = src_size * count_i;
if (OR_CHECK_INT_OVERFLOW (expected_size))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_STRING_SIZE_TOO_BIG, 2, expected_size,
(int) prm_get_bigint_value (PRM_ID_STRING_MAX_SIZE_BYTES));
return ER_QPROC_STRING_SIZE_TOO_BIG;
}
error_status = qstr_grow_string (&dummy, result, (int) expected_size);
if (error_status < 0)
{
pr_clear_value (&dummy);
return error_status;
}
/* qstr_grow_string may return DB_NULL if size too big */
if (DB_IS_NULL (result))
{
pr_clear_value (&dummy);
return NO_ERROR;
}
pr_clear_value (&dummy);
res_ptr = CONST_CAST (char *, db_get_string (result));
src_ptr = db_get_string (src_string);
while (count_i--)
{
memcpy (res_ptr, src_ptr, src_size);
res_ptr += src_size;
}
/* update size of string */
qstr_make_typed_string (result_type, result, DB_VALUE_PRECISION (result), db_get_string (result),
(int) expected_size, db_get_string_codeset (src_string),
db_get_string_collation (src_string));
result->need_clear = true;
}
return error_status;
}
/*
* db_string_substring_index - returns the substring from a string before
* count occurences of delimeter
*
* Arguments:
* src_string: String to search in.
* delim_string: String delimiter
* count: Number of occurences.
* result: string containing reminder.
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE :
* <str_string> or <delim_string> is not a string type,
* <count> is not an integer type
* ER_QSTR_INCOMPATIBLE_CODE_SETS:
* <str_string> or <delim_string> are not compatible
*
*/
int
db_string_substring_index (DB_VALUE * src_string, DB_VALUE * delim_string, const DB_VALUE * count, DB_VALUE * result)
{
QSTR_CATEGORY src_categ, delim_categ;
int error_status = NO_ERROR, count_i = 0;
DB_TYPE src_type, delim_type;
DB_VALUE empty_string1, empty_string2;
INTL_CODESET src_cs, delim_cs;
int src_coll, delim_coll;
/*
* Initialize status value
*/
db_make_null (result);
db_make_null (&empty_string1);
db_make_null (&empty_string2);
/*
* Assert that DB_VALUE structures have been allocated.
*/
assert (src_string != (DB_VALUE *) NULL);
assert (delim_string != (DB_VALUE *) NULL);
assert (count != (DB_VALUE *) NULL);
assert (result != (DB_VALUE *) NULL);
if (DB_IS_NULL (count))
{
db_make_null (result);
return NO_ERROR;
}
count_i = db_get_int (count);
/*
* Categorize the parameters into respective code sets.
* Verify that the parameters are both character strings.
* Verify that the input strings belong to compatible code sets.
*/
src_categ = qstr_get_category (src_string);
delim_categ = qstr_get_category (delim_string);
src_type = DB_VALUE_DOMAIN_TYPE (src_string);
delim_type = DB_VALUE_DOMAIN_TYPE (delim_string);
src_cs = DB_IS_NULL (src_string) ? LANG_SYS_CODESET : db_get_string_codeset (src_string);
src_coll = DB_IS_NULL (src_string) ? LANG_SYS_COLLATION : db_get_string_collation (src_string);
delim_cs = DB_IS_NULL (delim_string) ? LANG_SYS_CODESET : db_get_string_codeset (delim_string);
delim_coll = DB_IS_NULL (delim_string) ? LANG_SYS_COLLATION : db_get_string_collation (delim_string);
if (prm_get_bool_value (PRM_ID_ORACLE_STYLE_EMPTY_STRING) == true)
{
if (DB_IS_NULL (src_string))
{
if (DB_IS_NULL (delim_string))
{
/* both strings are NULL (or empty): result is DB_TYPE_NULL */
assert (error_status == NO_ERROR);
goto empty_string;
}
/* convert to empty string */
src_string = &empty_string1;
src_type = delim_type;
src_categ = delim_categ;
error_status =
db_string_make_empty_typed_string (src_string, src_type, TP_FLOATING_PRECISION_VALUE, delim_cs, delim_coll);
if (error_status != NO_ERROR)
{
goto exit;
}
/* intermediate value : clear is_null flag */
if (QSTR_IS_ANY_CHAR_OR_BIT (DB_VALUE_DOMAIN_TYPE (src_string)))
{
src_string->domain.general_info.is_null = 0;
}
}
if (DB_IS_NULL (delim_string))
{
/* convert to empty string */
delim_string = &empty_string2;
delim_type = src_type;
delim_categ = src_categ;
error_status =
db_string_make_empty_typed_string (delim_string, delim_type, TP_FLOATING_PRECISION_VALUE, src_cs, src_coll);
if (error_status != NO_ERROR)
{
goto exit;
}
/* intermediate value : clear is_null flag */
if (QSTR_IS_ANY_CHAR_OR_BIT (DB_VALUE_DOMAIN_TYPE (delim_string)))
{
delim_string->domain.general_info.is_null = 0;
}
}
}
else if (DB_IS_NULL (src_string) || DB_IS_NULL (delim_string))
{
goto exit;
}
if (!QSTR_IS_ANY_CHAR (src_type) || !QSTR_IS_ANY_CHAR (delim_type))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
else if ((src_categ != delim_categ) || (src_cs != delim_cs))
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
else if (count_i == 0)
{
/* return an empty string */
goto empty_string;
}
else
{
DB_VALUE offset_val, interm_pos;
int offset = 1, initial_count = 0;
bool count_from_start;
const int src_length = db_get_string_length (src_string);
const int delim_length = db_get_string_length (delim_string);
db_make_null (&interm_pos);
initial_count = count_i;
count_from_start = (count_i > 0) ? true : false;
count_i = abs (count_i);
assert (src_cs == delim_cs);
if (count_from_start)
{
while (count_i > 0)
{
db_make_int (&offset_val, offset);
error_status = db_string_instr (src_string, delim_string, &offset_val, &interm_pos);
if (error_status < 0)
{
goto exit;
}
offset = db_get_int (&interm_pos);
if (offset != 0)
{
offset += delim_length;
db_make_int (&offset_val, offset);
}
else
{
break;
}
count_i--;
}
}
else
{
while (count_i > 0)
{
/* search from end */
db_make_int (&offset_val, -offset);
error_status = db_string_instr (src_string, delim_string, &offset_val, &interm_pos);
if (error_status < 0)
{
goto exit;
}
offset = db_get_int (&interm_pos);
if (offset != 0)
{
/* adjust offset to indicate position relative to end */
offset = src_length - offset + 2;
db_make_int (&offset_val, offset);
}
else
{
break;
}
count_i--;
}
}
assert (count_i >= 0);
if (count_i == 0)
{
/* found count occurences , return the string */
DB_VALUE start_val, len_val;
int start_pos = 1, end_pos = 0;
if (count_from_start)
{
start_pos = 1;
end_pos = offset - delim_length - 1;
}
else
{
start_pos = src_length - offset + 2 + delim_length;
end_pos = src_length;
}
if (start_pos > end_pos || start_pos < 1 || end_pos > src_length)
{
/* empty string */
goto empty_string;
}
else
{
db_make_int (&start_val, start_pos);
db_make_int (&len_val, end_pos - start_pos + 1);
error_status = db_string_substring (SUBSTRING, src_string, &start_val, &len_val, result);
result->need_clear = true;
if (error_status < 0)
{
goto exit;
}
}
}
else
{
assert (count_i > 0);
/* not found at all or not enough number of occurences */
/* return the entire source string */
error_status = pr_clone_value ((DB_VALUE *) src_string, result);
if (src_type == DB_TYPE_CHAR)
{
/* convert CHARACTER(N) to CHARACTER VARYING(N) */
qstr_make_typed_string (DB_TYPE_VARCHAR, result,
DB_VALUE_PRECISION (result), db_get_string (result), db_get_string_size (result),
src_cs, src_coll);
result->need_clear = true;
}
if (error_status < 0)
{
goto exit;
}
}
}
pr_clear_value (&empty_string1);
pr_clear_value (&empty_string2);
return error_status;
empty_string:
/* the result should always be varying type string */
if (src_type == DB_TYPE_CHAR)
{
src_type = DB_TYPE_VARCHAR;
}
error_status = db_string_make_empty_typed_string (result, src_type, TP_FLOATING_PRECISION_VALUE, src_cs, src_coll);
pr_clear_value (&empty_string1);
pr_clear_value (&empty_string2);
return error_status;
exit:
pr_clear_value (&empty_string1);
pr_clear_value (&empty_string2);
return error_status;
}
/*
* db_string_shaone - sha1 encrypt function
* return: If success, return 0.
* src(in): source string
* result(out): the encrypted data.
* Note:
*/
int
db_string_sha_one (DB_VALUE const *src, DB_VALUE * result)
{
int error_status = NO_ERROR;
char *result_strp = NULL;
int result_len = 0;
assert (src != (DB_VALUE *) NULL);
assert (result != (DB_VALUE *) NULL);
if (DB_IS_NULL (src))
{
db_make_null (result); /* SH1(NULL) returns NULL */
return error_status;
}
else
{
DB_TYPE val_type = DB_VALUE_DOMAIN_TYPE (src);
if (QSTR_IS_ANY_CHAR (val_type))
{
error_status = crypt_sha_one (NULL, db_get_string (src), db_get_string_size (src), &result_strp, &result_len);
if (error_status != NO_ERROR)
{
goto error;
}
qstr_make_typed_string (DB_TYPE_CHAR, result, result_len, result_strp, result_len,
db_get_string_codeset (src), db_get_string_collation (src));
result->need_clear = true;
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
goto error;
}
}
return error_status;
error:
db_make_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;
}
}
/*
* db_string_shatwo - sha2 encrypt function
* return: If success, return 0.
* src(in): source string
* hash_len(in): the hash length
* result(out): the encrypted data.
* Note:
*/
int
db_string_sha_two (DB_VALUE const *src, DB_VALUE const *hash_len, DB_VALUE * result)
{
int error_status = NO_ERROR;
DB_TYPE src_type = DB_VALUE_DOMAIN_TYPE (src);
DB_TYPE hash_len_type = DB_VALUE_DOMAIN_TYPE (hash_len);
char *result_strp = NULL;
int result_len = 0;
int len = 0;
assert (src != (DB_VALUE *) NULL);
assert (hash_len != (DB_VALUE *) NULL);
assert (result != (DB_VALUE *) NULL);
if (DB_IS_NULL (src) || DB_IS_NULL (hash_len))
{
db_make_null (result); /* sha2(NULL, ...) or sha2(..., NULL) returns NULL */
return error_status;
}
switch (hash_len_type)
{
case DB_TYPE_SHORT:
len = db_get_short (hash_len);
break;
case DB_TYPE_INTEGER:
len = db_get_int (hash_len);
break;
case DB_TYPE_BIGINT:
len = (int) db_get_bigint (hash_len);
break;
default:
return ER_QSTR_INVALID_DATA_TYPE;
}
if (QSTR_IS_ANY_CHAR (src_type))
{
error_status =
crypt_sha_two (NULL, db_get_string (src), db_get_string_length (src), len, &result_strp, &result_len);
if (error_status != NO_ERROR)
{
goto error;
}
/* It means that the hash_len is wrong. */
if (result_strp == NULL)
{
db_make_null (result);
return error_status;
}
qstr_make_typed_string (DB_TYPE_VARCHAR, result, result_len, result_strp, result_len, db_get_string_codeset (src),
db_get_string_collation (src));
result->need_clear = true;
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
goto error;
}
return error_status;
error:
db_make_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;
}
}
/*
* db_string_aes_encrypt - aes encrypt function
* return: If success, return 0.
* src(in): source string
* key(in): the encrypt key
* result(out): the encrypted data.
* Note:
*/
int
db_string_aes_encrypt (DB_VALUE const *src, DB_VALUE const *key, DB_VALUE * result)
{
int error_status = NO_ERROR;
DB_TYPE src_type = DB_VALUE_DOMAIN_TYPE (src);
DB_TYPE key_type = DB_VALUE_DOMAIN_TYPE (key);
char *result_strp = NULL;
int result_len = 0;
assert (src != (DB_VALUE *) NULL);
assert (key != (DB_VALUE *) NULL);
assert (result != (DB_VALUE *) NULL);
if (DB_IS_NULL (src) || DB_IS_NULL (key))
{
/* aes_encypt(NULL, ...) or aes_encypt(..., NULL) returns NULL */
db_make_null (result);
return error_status;
}
if (QSTR_IS_ANY_CHAR (src_type) && QSTR_IS_ANY_CHAR (key_type))
{
error_status =
crypt_default_encrypt (NULL, db_get_string (src), db_get_string_length (src), db_get_string (key),
db_get_string_length (key), &result_strp, &result_len, AES_128_ECB);
if (error_status != NO_ERROR)
{
goto error;
}
qstr_make_typed_string (DB_TYPE_VARCHAR, result, result_len, result_strp, result_len, db_get_string_codeset (src),
db_get_string_collation (src));
result->need_clear = true;
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
goto error;
}
return error_status;
error:
db_make_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;
}
}
/*
* db_string_aes_decrypt - aes decrypt function
* return: If success, return 0.
* src(in): source string
* key(in): the encrypt key
* result(out): the decrypted data.
* Note:
*/
int
db_string_aes_decrypt (DB_VALUE const *src, DB_VALUE const *key, DB_VALUE * result)
{
int error_status = NO_ERROR;
DB_TYPE src_type = DB_VALUE_DOMAIN_TYPE (src);
DB_TYPE key_type = DB_VALUE_DOMAIN_TYPE (key);
char *result_strp = NULL;
int result_len = 0;
assert (src != (DB_VALUE *) NULL);
assert (key != (DB_VALUE *) NULL);
assert (result != (DB_VALUE *) NULL);
if (DB_IS_NULL (src) || DB_IS_NULL (key))
{
/* aes_decypt(NULL, ...) or aes_decypt(..., NULL) returns NULL */
db_make_null (result);
return error_status;
}
if (QSTR_IS_ANY_CHAR (src_type) && QSTR_IS_ANY_CHAR (key_type))
{
error_status =
crypt_default_decrypt (NULL, db_get_string (src), db_get_string_length (src), db_get_string (key),
db_get_string_length (key), &result_strp, &result_len, AES_128_ECB);
if (error_status != NO_ERROR)
{
goto error;
}
if (result_strp == NULL)
{
/* it means the src isn't aes_encrypted string, we return NULL like mysql */
db_make_null (result);
return error_status;
}
qstr_make_typed_string (DB_TYPE_VARCHAR, result, result_len, result_strp, result_len, db_get_string_codeset (src),
db_get_string_collation (src));
result->need_clear = true;
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
goto error;
}
return error_status;
error:
db_make_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;
}
}
/*
* MD5('str')
* Arguments
* val: string to compute the MD5 (message digest) for
* result: DB_VALUE to receive the computed MD5 from the val argument
*/
int
db_string_md5 (DB_VALUE const *val, DB_VALUE * result)
{
int error_status = NO_ERROR;
assert (val != (DB_VALUE *) NULL);
assert (result != (DB_VALUE *) NULL);
if (DB_IS_NULL (val))
{
db_make_null (result); /* MD5(NULL) returns NULL */
return error_status;
}
else
{
DB_TYPE val_type = DB_VALUE_DOMAIN_TYPE (val);
if (QSTR_IS_ANY_CHAR (val_type))
{
/* MD5 hash string buffer */
char hashString[32 + 1] = { '\0' };
DB_VALUE hash_string;
db_make_null (&hash_string);
error_status = crypt_md5_buffer_hex (db_get_string (val), db_get_string_length (val), hashString);
if (error_status != NO_ERROR)
{
return error_status;
}
/* dump result as hex string */
qstr_make_typed_string (DB_TYPE_CHAR, &hash_string, 32, hashString, 32, db_get_string_codeset (val),
db_get_string_collation (val));
hash_string.need_clear = false;
pr_clone_value (&hash_string, result);
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
}
}
return error_status;
}
/*
* db_string_insert_substring - insert a substring into a string replacing
* "length" characters starting at "position"
*
* Arguments:
* src_string: string to insert into. Its value will not be
* modified as the output is the "result" parameter
* position: starting position
* length: number of character to replace
* sub_string: string to be inserted
* result: string containing result.
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE :
* <str_string> or <delim_string> is not a string type,
* <count> is not an integer type
* ER_QSTR_INCOMPATIBLE_CODE_SETS:
* <str_string> or <delim_string> are not compatible
*
*/
int
db_string_insert_substring (DB_VALUE * src_string, const DB_VALUE * position, const DB_VALUE * length,
DB_VALUE * sub_string, DB_VALUE * result)
{
QSTR_CATEGORY src_categ, substr_categ;
int error_status = NO_ERROR, position_i = 0, length_i = 0;
DB_TYPE src_type, substr_type;
DB_VALUE string1, string2;
int src_length = 0;
int result_size = 0;
DB_VALUE empty_string1, empty_string2;
DB_VALUE partial_result;
INTL_CODESET src_cs, substr_cs;
int src_coll, substr_coll;
/*
* Assert that DB_VALUE structures have been allocated.
*/
assert (src_string != (DB_VALUE *) NULL);
assert (sub_string != (DB_VALUE *) NULL);
assert (position != (DB_VALUE *) NULL);
assert (length != (DB_VALUE *) NULL);
assert (result != (DB_VALUE *) NULL);
/*
* Initialize values
*/
db_make_null (result);
db_make_null (&string1);
db_make_null (&string2);
db_make_null (&empty_string1);
db_make_null (&empty_string2);
db_make_null (&partial_result);
/*
* Categorize the parameters into respective code sets.
* Verify that the parameters are both character strings.
* Verify that the input strings belong to compatible code sets.
*/
src_categ = qstr_get_category (src_string);
substr_categ = qstr_get_category (sub_string);
src_type = DB_VALUE_DOMAIN_TYPE (src_string);
substr_type = DB_VALUE_DOMAIN_TYPE (sub_string);
src_cs = DB_IS_NULL (src_string) ? LANG_SYS_CODESET : db_get_string_codeset (src_string);
src_coll = DB_IS_NULL (src_string) ? LANG_SYS_COLLATION : db_get_string_collation (src_string);
substr_cs = DB_IS_NULL (sub_string) ? LANG_SYS_CODESET : db_get_string_codeset (sub_string);
substr_coll = DB_IS_NULL (sub_string) ? LANG_SYS_COLLATION : db_get_string_collation (sub_string);
if (prm_get_bool_value (PRM_ID_ORACLE_STYLE_EMPTY_STRING) == true)
{
if (DB_IS_NULL (src_string))
{
if (DB_IS_NULL (sub_string))
{
/* both strings are NULL (or empty): result is DB_TYPE_NULL */
assert (error_status == NO_ERROR);
goto exit;
}
/* convert to empty string */
src_string = &empty_string1;
src_type = substr_type;
src_categ = substr_categ;
error_status =
db_string_make_empty_typed_string (src_string, src_type, TP_FLOATING_PRECISION_VALUE, substr_cs,
substr_coll);
if (error_status != NO_ERROR)
{
goto exit;
}
/* intermediate value : clear is_null flag */
if (QSTR_IS_ANY_CHAR_OR_BIT (DB_VALUE_DOMAIN_TYPE (src_string)))
{
src_string->domain.general_info.is_null = 0;
}
}
if (DB_IS_NULL (sub_string))
{
/* convert to empty string */
sub_string = &empty_string2;
substr_type = src_type;
substr_categ = src_categ;
error_status =
db_string_make_empty_typed_string (sub_string, substr_type, TP_FLOATING_PRECISION_VALUE, src_cs, src_coll);
if (error_status != NO_ERROR)
{
goto exit;
}
/* intermediate value : clear is_null flag */
if (QSTR_IS_ANY_CHAR_OR_BIT (DB_VALUE_DOMAIN_TYPE (sub_string)))
{
sub_string->domain.general_info.is_null = 0;
}
}
}
else if (DB_IS_NULL (src_string) || DB_IS_NULL (sub_string))
{
/* result is DB_TYPE_NULL */
assert (error_status == NO_ERROR);
goto exit;
}
if (DB_IS_NULL (position) || DB_IS_NULL (length))
{
/* result is DB_TYPE_NULL */
assert (error_status == NO_ERROR);
goto exit;
}
if (!QSTR_IS_ANY_CHAR_OR_BIT (src_type) || !QSTR_IS_ANY_CHAR_OR_BIT (substr_type))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (src_categ != substr_categ)
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
position_i = db_get_int (position);
length_i = db_get_int (length);
src_length = db_get_string_length (src_string);
if (position_i <= 0 || position_i > src_length + 1)
{
/* return the source string */
error_status = pr_clone_value ((DB_VALUE *) src_string, result);
result_size = db_get_string_size (src_string);
}
else
{
DB_DATA_STATUS data_status;
/* result = string1 + substring + string2 */
/* string1 = left(string,position) */
if (position_i > 1)
{
DB_VALUE start_val, len_val;
db_make_int (&start_val, 1);
db_make_int (&len_val, position_i - 1);
error_status = db_string_substring (SUBSTRING, src_string, &start_val, &len_val, &string1);
if (error_status != NO_ERROR)
{
goto exit;
}
}
if (DB_IS_NULL (&string1)) /* make dummy for concat */
{
error_status =
db_string_make_empty_typed_string (&string1, src_type, TP_FLOATING_PRECISION_VALUE, src_cs, src_coll);
if (error_status != NO_ERROR)
{
goto exit;
}
if (prm_get_bool_value (PRM_ID_ORACLE_STYLE_EMPTY_STRING) == true && DB_IS_NULL (&string1)
&& QSTR_IS_ANY_CHAR_OR_BIT (DB_VALUE_DOMAIN_TYPE (&string1)))
{
/* intermediate value : clear is_null flag */
string1.domain.general_info.is_null = 0;
}
}
/* string2 = susbtring(string,position+len) */
/* get string2 if the conditions are fullfilled : 1. length_i >= 0 - compatibility with MySql (if len is
* negative, no remainder is concatenated) 2. (position_i + length_i) <= src_length - check the start boundary
* for substring */
if ((length_i >= 0) && ((position_i + length_i) <= src_length))
{
DB_VALUE start_val, len_val;
db_make_int (&start_val, position_i + length_i);
db_make_int (&len_val, src_length - (position_i + length_i) + 1);
error_status = db_string_substring (SUBSTRING, src_string, &start_val, &len_val, &string2);
if (error_status != NO_ERROR)
{
goto exit;
}
}
if (DB_IS_NULL (&string2)) /* make dummy for concat */
{
error_status =
db_string_make_empty_typed_string (&string2, src_type, TP_FLOATING_PRECISION_VALUE, src_cs, src_coll);
if (error_status != NO_ERROR)
{
goto exit;
}
if (prm_get_bool_value (PRM_ID_ORACLE_STYLE_EMPTY_STRING) == true && DB_IS_NULL (&string2)
&& QSTR_IS_ANY_CHAR_OR_BIT (DB_VALUE_DOMAIN_TYPE (&string2)))
{
/* intermediate value : clear is_null flag */
string2.domain.general_info.is_null = 0;
}
}
/* partial_result = concat(string1,substring) */
error_status = db_string_concatenate (&string1, sub_string, &partial_result, &data_status);
if (error_status != NO_ERROR)
{
goto exit;
}
if (data_status != DATA_STATUS_OK)
{
/* This should never happen as the partial_result is a VAR[N]CHAR */
assert (false);
error_status = ER_FAILED;
goto exit;
}
/* result = concat(partial_result,string2) */
error_status = db_string_concatenate (&partial_result, &string2, result, &data_status);
if (error_status != NO_ERROR)
{
goto exit;
}
if (data_status != DATA_STATUS_OK)
{
/* This should never happen as the result is a VAR[N]CHAR */
assert (false);
error_status = ER_FAILED;
goto exit;
}
result_size = db_get_string_size (result);
}
/* force type to variable string */
if (src_type == DB_TYPE_CHAR)
{
/* convert CHARACTER(N) to CHARACTER VARYING(N) */
qstr_make_typed_string (DB_TYPE_VARCHAR, result,
TP_FLOATING_PRECISION_VALUE, db_get_string (result), result_size, src_cs, src_coll);
}
else if (src_type == DB_TYPE_BIT)
{
/* convert BIT to BIT VARYING */
qstr_make_typed_string (DB_TYPE_VARBIT, result, TP_FLOATING_PRECISION_VALUE, db_get_string (result), result_size,
src_cs, src_coll);
}
result->need_clear = true;
exit:
pr_clear_value (&string1);
pr_clear_value (&string2);
pr_clear_value (&empty_string1);
pr_clear_value (&empty_string2);
pr_clear_value (&partial_result);
return error_status;
}
/*
ELT(index, arg1, arg2, arg3, ...)
Clones into result the argument with the index given by the first
argument.
Returns: NO_ERROR or an error code
*/
int
db_string_elt (DB_VALUE * result, DB_VALUE * arg[], int const num_args)
{
DB_TYPE index_type = DB_VALUE_DOMAIN_TYPE (arg[0]);
DB_BIGINT index = 0;
if (num_args <= 0)
{
db_make_null (result);
return NO_ERROR;
}
if (DB_IS_NULL (arg[0]))
{
db_make_null (result);
return NO_ERROR;
}
switch (index_type)
{
case DB_TYPE_BIGINT:
index = db_get_bigint (arg[0]);
break;
case DB_TYPE_INTEGER:
index = db_get_int (arg[0]);
break;
case DB_TYPE_SMALLINT:
index = db_get_short (arg[0]);
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INVALID_DATA_TYPE, 0);
return ER_QSTR_INVALID_DATA_TYPE;
}
if (index > 0 && index < num_args)
{
pr_clone_value (arg[index], result);
}
else
{
db_make_null (result);
}
return NO_ERROR;
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* db_string_byte_length
*
* Arguments:
* string: (IN) Input string of which the byte count is desired.
* byte_count: (OUT) The number of bytes in string.
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE:
* <string> is not a string type
*
* Note:
* This function returns the number of bytes in <string>.
*
* If the NULL flag is set for <string>, then the NULL flag
* for the <byte_count> is set.
*
* Assert:
* 1. string != (DB_VALUE *) NULL
* 2. byte_count != (DB_VALUE *) NULL
*
*/
int
db_string_byte_length (const DB_VALUE * string, DB_VALUE * byte_count)
{
int error_status = NO_ERROR;
DB_TYPE str_type;
/*
* Assert that DB_VALUE structures have been allocated.
*/
assert (string != (DB_VALUE *) NULL);
assert (byte_count != (DB_VALUE *) NULL);
/*
* Verify that the input string is a valid character
* string. Bit strings are not allowed.
*
* If the input string is a NULL, then set
* the output null flag.
*
* Otherwise, calculte the byte size.
*/
str_type = DB_VALUE_DOMAIN_TYPE (string);
if (!QSTR_IS_ANY_CHAR_OR_BIT (str_type))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
}
else if (DB_IS_NULL (string))
{
db_value_domain_init (byte_count, DB_TYPE_INTEGER, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
}
else
{
db_make_int (byte_count, db_get_string_size (string));
}
return error_status;
}
#endif /* ENABLE_UNUSED_FUNCTION */
/*
* db_string_bit_length () -
*
* Arguments:
* string: Inpute string of which the bit length is desired.
* bit_count: Bit count of string.
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE:
* <string> is not a string type
*
* Note:
* This function returns the number of bits in <string>.
*
* If the NULL flag is set for <string>, then the NULL flag
* for the <bit_count> is set.
*
* Assert:
* 1. string != (DB_VALUE *) NULL
* 2. bit_count != (DB_VALUE *) NULL
*
*/
int
db_string_bit_length (const DB_VALUE * string, DB_VALUE * bit_count)
{
int error_status = NO_ERROR;
DB_TYPE str_type;
/*
* Assert that DB_VALUE structures have been allocated.
*/
assert (string != (DB_VALUE *) NULL);
assert (bit_count != (DB_VALUE *) NULL);
/*
* Verify that the input string is a valid character string.
* Bit strings are not allowed.
*
* If the input string is a NULL, then set the output null flag.
*
* If the input parameter is valid, then extract the byte length
* of the string.
*/
str_type = DB_VALUE_DOMAIN_TYPE (string);
if (!QSTR_IS_ANY_CHAR_OR_BIT (str_type))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
}
else if (DB_IS_NULL (string))
{
db_value_domain_init (bit_count, DB_TYPE_INTEGER, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
}
else
{
if (qstr_get_category (string) == QSTR_BIT)
{
db_make_int (bit_count, db_get_string_length (string));
}
else
{
db_make_int (bit_count, (db_get_string_size (string) * BYTE_SIZE));
}
}
return error_status;
}
/*
* db_string_char_length () -
*
* Arguments:
* string: String for which the number of characters is desired.
* char_count: Number of characters in string.
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE:
* <string> is not a character string
*
* Note:
* This function returns the number of characters in <string>.
*
* If the NULL flag is set for <string>, then the NULL flag
* for the <char_count> is set.
*
* Assert:
* 1. string != (DB_VALUE *) NULL
* 2. char_count != (DB_VALUE *) NULL
*
*/
int
db_string_char_length (const DB_VALUE * string, DB_VALUE * char_count)
{
int error_status = NO_ERROR;
/*
* Assert that DB_VALUE structures have been allocated.
*/
assert (string != (DB_VALUE *) NULL);
assert (char_count != (DB_VALUE *) NULL);
/*
* Verify that the input string is a valid character
* string. Bit strings are not allowed.
*
* If the input string is a NULL, then set the output null flag.
*
* If the input parameter is valid, then extract the character
* length of the string.
*/
if (!is_char_string (string))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
}
else if (DB_IS_NULL (string))
{
db_value_domain_init (char_count, DB_TYPE_INTEGER, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
}
else
{
db_make_int (char_count, db_get_string_length (string));
}
return error_status;
}
/*
* db_string_lower () -
*
* Arguments:
* string: Input string that will be converted to lower case.
* lower_string: Output converted string.
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE :
* <string> is not a character string.
*
* Note:
* This function returns a string with all uppercase ASCII
* and LATIN alphabetic characters converted to lowercase.
*
* If the NULL flag is set for <string>, then the NULL flag
* for the <lower_string> is set.
*
* The <lower_string> value structure will be cloned from <string>.
* <lower_string> should be cleared with pr_clone_value() if it has
* already been initialized or db_make_null if it has not been
* previously used by the system.
*
* Assert:
*
* 1. string != (DB_VALUE *) NULL
* 2. lower_string != (DB_VALUE *) NULL
*
*/
int
db_string_lower (const DB_VALUE * string, DB_VALUE * lower_string)
{
int error_status = NO_ERROR;
DB_TYPE str_type;
/*
* Assert that DB_VALUE structures have been allocated.
*/
assert (string != (DB_VALUE *) NULL);
assert (lower_string != (DB_VALUE *) NULL);
/*
* Categorize the two input parameters and check for errors.
* Verify that the parameters are both character strings.
*/
str_type = DB_VALUE_DOMAIN_TYPE (string);
if (DB_IS_NULL (string))
{
db_make_null (lower_string);
}
else if (!QSTR_IS_ANY_CHAR (str_type))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
}
/*
* If the input parameters have been properly validated, then
* we are ready to operate.
*/
else
{
unsigned char *lower_str;
int lower_size;
int src_length;
const ALPHABET_DATA *alphabet = lang_user_alphabet_w_coll (db_get_string_collation (string));
src_length = db_get_string_length (string);
lower_size = intl_lower_string_size (alphabet, DB_GET_UCHAR (string), db_get_string_size (string), src_length);
lower_str = (unsigned char *) db_private_alloc (NULL, lower_size + 1);
if (!lower_str)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
}
else
{
int lower_length = TP_FLOATING_PRECISION_VALUE;
intl_lower_string (alphabet, DB_GET_UCHAR (string), lower_str, src_length);
lower_str[lower_size] = 0;
if (db_value_precision (string) != TP_FLOATING_PRECISION_VALUE)
{
intl_char_count (lower_str, lower_size, db_get_string_codeset (string), &lower_length);
}
qstr_make_typed_string (str_type, lower_string, lower_length, (char *) lower_str, lower_size,
db_get_string_codeset (string), db_get_string_collation (string));
lower_string->need_clear = true;
}
}
return error_status;
}
/*
* db_string_upper () -
*
* Arguments:
* string: Input string that will be converted to upper case.
* lower_string: Output converted string.
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE :
* <string> is not a character string.
*
* Note:
*
* This function returns a string with all lowercase ASCII
* and LATIN alphabetic characters converted to uppercase.
*
* If the NULL flag is set for <string>, then the NULL flag
* for the <upper_string> is set.
*
* The <upper_string> value structure will be cloned from <string>.
* <upper_string> should be cleared with pr_clone_value() if it has
* already been initialized or db_make_null if it has not been
* previously used by the system.
*
* Assert:
*
* 1. string != (DB_VALUE *) NULL
* 2. upper_string != (DB_VALUE *) NULL
*
*/
int
db_string_upper (const DB_VALUE * string, DB_VALUE * upper_string)
{
int error_status = NO_ERROR;
DB_TYPE str_type;
/*
* Assert that DB_VALUE structures have been allocated.
*/
assert (string != (DB_VALUE *) NULL);
assert (upper_string != (DB_VALUE *) NULL);
/*
* Categorize the two input parameters and check for errors.
* Verify that the parameters are both character strings.
*/
str_type = DB_VALUE_DOMAIN_TYPE (string);
if (DB_IS_NULL (string))
{
db_make_null (upper_string);
}
else if (!QSTR_IS_ANY_CHAR (str_type))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
}
/*
* If the input parameters have been properly validated, then
* we are ready to operate.
*/
else
{
unsigned char *upper_str;
int upper_size, src_length;
const ALPHABET_DATA *alphabet = lang_user_alphabet_w_coll (db_get_string_collation (string));
src_length = db_get_string_length (string);
upper_size = intl_upper_string_size (alphabet, DB_GET_UCHAR (string), db_get_string_size (string), src_length);
upper_str = (unsigned char *) db_private_alloc (NULL, upper_size + 1);
if (!upper_str)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
}
else
{
int upper_length = TP_FLOATING_PRECISION_VALUE;
intl_upper_string (alphabet, DB_GET_UCHAR (string), upper_str, src_length);
upper_str[upper_size] = 0;
if (db_value_precision (string) != TP_FLOATING_PRECISION_VALUE)
{
intl_char_count (upper_str, upper_size, db_get_string_codeset (string), &upper_length);
}
qstr_make_typed_string (str_type, upper_string, upper_length, (char *) upper_str, upper_size,
db_get_string_codeset (string), db_get_string_collation (string));
upper_string->need_clear = true;
}
}
return error_status;
}
/*
* db_string_trim () -
*
* Arguments:
* trim_operand: Specifies whether the character to be trimmed is
* removed from the beginning, ending or both ends
* of the string.
* trim_charset: (Optional) The characters to be removed.
* src_string: String to remove trim character from.
* trimmed_string: Resultant trimmed string.
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE : <trim_char> or <src_string> are
* not character strings.
* ER_QSTR_INVALID_TRIM_OPERAND : <trim_char> has char length > 1.
* ER_QSTR_INCOMPATIBLE_CODE_SETS: <trim_char>, <src_string> and
* <trimmed_string> have different
* character code sets.
*
*/
int
db_string_trim (const MISC_OPERAND tr_operand, const DB_VALUE * trim_charset, const DB_VALUE * src_string,
DB_VALUE * trimmed_string)
{
int error_status = NO_ERROR;
int is_trim_charset_omitted = false;
unsigned char *result;
int result_length, result_size = 0, result_domain_length;
const unsigned char *trim_charset_ptr = NULL;
int trim_charset_length = 0;
int trim_charset_size = 0;
DB_TYPE src_type, trim_type;
/*
* Assert DB_VALUE structures have been allocated
*/
assert (src_string != (DB_VALUE *) NULL);
assert (trimmed_string != (DB_VALUE *) NULL);
/* if source is NULL, return NULL */
if (DB_IS_NULL (src_string))
{
db_value_domain_init (trimmed_string, DB_TYPE_VARCHAR, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
return error_status;
}
if (trim_charset == NULL)
{
is_trim_charset_omitted = true;
}
else
{
is_trim_charset_omitted = false;
trim_type = DB_VALUE_DOMAIN_TYPE (trim_charset);
if (DB_IS_NULL (trim_charset))
{
db_value_domain_init (trimmed_string, DB_TYPE_VARCHAR, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
return error_status;
}
}
/*
* Verify input parameters are all char strings and are compatible
*/
src_type = DB_VALUE_DOMAIN_TYPE (src_string);
if (!QSTR_IS_ANY_CHAR (src_type) || (!is_trim_charset_omitted && !QSTR_IS_ANY_CHAR (trim_type)))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INVALID_DATA_TYPE, 0);
return error_status;
}
if (!is_trim_charset_omitted
&& (qstr_get_category (src_string) != qstr_get_category (trim_charset)
|| db_get_string_codeset (src_string) != db_get_string_codeset (trim_charset)))
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INCOMPATIBLE_CODE_SETS, 0);
return error_status;
}
/*
* begin of main codes
*/
if (!is_trim_charset_omitted)
{
trim_charset_ptr = DB_GET_UCHAR (trim_charset);
trim_charset_length = db_get_string_length (trim_charset);
trim_charset_size = db_get_string_size (trim_charset);
}
error_status = qstr_trim (tr_operand, trim_charset_ptr, trim_charset_length, trim_charset_size,
DB_GET_UCHAR (src_string), DB_VALUE_DOMAIN_TYPE (src_string),
db_get_string_length (src_string), db_get_string_size (src_string),
db_get_string_codeset (src_string), &result, &result_length, &result_size);
if (error_status == NO_ERROR && result != NULL)
{
result_domain_length = MIN (DB_MAX_VARCHAR_PRECISION, DB_VALUE_PRECISION (src_string));
qstr_make_typed_string (DB_TYPE_VARCHAR, trimmed_string, result_domain_length, (char *) result, result_size,
db_get_string_codeset (src_string), db_get_string_collation (src_string));
result[result_size] = 0;
trimmed_string->need_clear = true;
}
return error_status;
}
/*
* db_string_prefix_compare () - this function is similar with db_string_compare.
* but if one of 2 string arguments is char-type
* they are compared by the ignore-trailing-space rule.
* Arguments:
* string1: Left side of compare.
* string2: Right side of compare
* result: Integer result of comparison.
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE :
* <string1> or <string2> are not character strings.
*
* ER_QSTR_INCOMPATIBLE_CODE_SETS:
* <string1> and <string2> have differing character code sets.
*
* ER_QSTR_INCOMPATIBLE_COLLATIONS
* <string1> and <string2> have incompatible collations.
*/
static int
db_string_prefix_compare (const DB_VALUE * string1, const DB_VALUE * string2, DB_VALUE * result)
{
QSTR_CATEGORY string1_category, string2_category;
int cmp_result = 0;
DB_TYPE str1_type, str2_type;
bool ti = true;
static bool ignore_trailing_space = prm_get_bool_value (PRM_ID_IGNORE_TRAILING_SPACE);
/* Assert that DB_VALUE structures have been allocated. */
assert (string1 != (DB_VALUE *) NULL);
assert (string2 != (DB_VALUE *) NULL);
assert (result != (DB_VALUE *) NULL);
/* Categorize the two input parameters and check for errors. Verify that the parameters are both character strings.
* Verify that the input strings belong to compatible categories. */
string1_category = qstr_get_category (string1);
string2_category = qstr_get_category (string2);
str1_type = DB_VALUE_DOMAIN_TYPE (string1);
str2_type = DB_VALUE_DOMAIN_TYPE (string2);
if (!QSTR_IS_ANY_CHAR_OR_BIT (str1_type) || !QSTR_IS_ANY_CHAR_OR_BIT (str2_type))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INVALID_DATA_TYPE, 0);
return ER_QSTR_INVALID_DATA_TYPE;
}
if (string1_category != string2_category)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INCOMPATIBLE_CODE_SETS, 0);
return ER_QSTR_INCOMPATIBLE_CODE_SETS;
}
/* A string which is NULL (not the same as a NULL string) is ordered less than a string which is not NULL. Two
* strings which are NULL are ordered equivalently. If both strings are not NULL, then the strings themselves are
* compared. */
if (DB_IS_NULL (string1) && !DB_IS_NULL (string2))
{
cmp_result = -1;
}
else if (!DB_IS_NULL (string1) && DB_IS_NULL (string2))
{
cmp_result = 1;
}
else if (DB_IS_NULL (string1) && DB_IS_NULL (string2))
{
cmp_result = 0;
}
else if (db_get_string_codeset (string1) != db_get_string_codeset (string2))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INCOMPATIBLE_CODE_SETS, 0);
return ER_QSTR_INCOMPATIBLE_CODE_SETS;
}
else
{
int coll_id;
switch (string1_category)
{
case QSTR_CHAR:
assert (db_get_string_codeset (string1) == db_get_string_codeset (string2));
LANG_RT_COMMON_COLL (db_get_string_collation (string1), db_get_string_collation (string2), coll_id);
if (coll_id == -1)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INCOMPATIBLE_COLLATIONS, 0);
return ER_QSTR_INCOMPATIBLE_COLLATIONS;
}
coll_id = db_get_string_collation (string1);
assert (db_get_string_collation (string1) == db_get_string_collation (string2));
if (!ignore_trailing_space)
{
ti = (QSTR_IS_FIXED_LENGTH (str1_type) && QSTR_IS_FIXED_LENGTH (str2_type));
}
cmp_result = QSTR_COMPARE (coll_id, DB_GET_UCHAR (string1), (int) db_get_string_size (string1),
DB_GET_UCHAR (string2), (int) db_get_string_size (string2), ti);
break;
case QSTR_BIT:
cmp_result = varbit_compare (DB_GET_UCHAR (string1), (int) db_get_string_size (string1),
DB_GET_UCHAR (string2), (int) db_get_string_size (string2));
break;
default: /* QSTR_UNKNOWN */
assert (false);
}
}
if (cmp_result < 0)
{
cmp_result = -1;
}
else if (cmp_result > 0)
{
cmp_result = 1;
}
db_make_int (result, cmp_result);
return NO_ERROR;
}
/* qstr_trim () -
*/
static int
qstr_trim (MISC_OPERAND trim_operand, const unsigned char *trim_charset, int trim_charset_length, int trim_charset_size,
const unsigned char *src_ptr, DB_TYPE src_type, int src_length, int src_size, INTL_CODESET codeset,
unsigned char **result, int *result_length, int *result_size)
{
unsigned char pad_char[2], *lead_trimmed_ptr, *trail_trimmed_ptr;
int lead_trimmed_length, trail_trimmed_length;
int lead_trimmed_size, trail_trimmed_size, pad_char_size = 0;
int error_status = NO_ERROR;
bool trim_ascii_spaces = false;
/* default case */
intl_pad_char (codeset, pad_char, &pad_char_size);
if (trim_charset_length == 0)
{
trim_charset = pad_char;
trim_charset_length = 1;
trim_charset_size = pad_char_size;
trim_ascii_spaces = true;
}
/* trim from front */
lead_trimmed_ptr = (unsigned char *) src_ptr;
lead_trimmed_length = src_length;
lead_trimmed_size = src_size;
if (trim_operand == LEADING || trim_operand == BOTH)
{
trim_leading (trim_charset, trim_charset_size, src_ptr, src_type, src_length, src_size, codeset,
&lead_trimmed_ptr, &lead_trimmed_length, &lead_trimmed_size, trim_ascii_spaces);
}
trail_trimmed_ptr = lead_trimmed_ptr;
trail_trimmed_length = lead_trimmed_length;
trail_trimmed_size = lead_trimmed_size;
if (trim_operand == TRAILING || trim_operand == BOTH)
{
qstr_trim_trailing (trim_charset, trim_charset_size, lead_trimmed_ptr, src_type, lead_trimmed_length,
lead_trimmed_size, codeset, &trail_trimmed_length, &trail_trimmed_size, trim_ascii_spaces);
}
/* setup result */
*result = (unsigned char *) db_private_alloc (NULL, (size_t) trail_trimmed_size + 1);
if (*result == NULL)
{
assert (er_errid () != NO_ERROR);
error_status = er_errid ();
return error_status;
}
(void) memcpy ((char *) (*result), (char *) trail_trimmed_ptr, trail_trimmed_size);
(*result)[trail_trimmed_size] = '\0';
*result_length = trail_trimmed_length;
*result_size = trail_trimmed_size;
return error_status;
}
/*
* trim_leading () -
*
* Arguments:
* trim_charset_ptr: (in) Single character trim string.
* trim_charset_size: (in) Size of trim string.
* src_string_ptr: (in) Source string to be trimmed.
* src_string_length: (in) Length of source string.
* codeset: (in) International codeset of source string.
* lead_trimmed_ptr: (out) Pointer to start of trimmed string.
* lead_trimmed_length: (out) Length of trimmed string.
* trim_ascii_spaces: (in) Option to trim normal spaces also.
*
* Returns: nothing
*
* Errors:
*
* Note:
* Remove trim character from the front of the source string. No
* characters are actually removed. Instead, the function returns
* a pointer to the beginning of the source string after the trim
* characters and the resultant length of the string.
*
*/
static void
trim_leading (const unsigned char *trim_charset_ptr, int trim_charset_size, const unsigned char *src_ptr,
DB_TYPE src_type, int src_length, int src_size, INTL_CODESET codeset, unsigned char **lead_trimmed_ptr,
int *lead_trimmed_length, int *lead_trimmed_size, bool trim_ascii_spaces)
{
int cur_src_char_size, cur_trim_char_size;
unsigned char *cur_src_char_ptr, *cur_trim_char_ptr;
int cmp_flag = 0;
*lead_trimmed_ptr = (unsigned char *) src_ptr;
*lead_trimmed_length = src_length;
*lead_trimmed_size = src_size;
/* iterate for source string */
for (cur_src_char_ptr = (unsigned char *) src_ptr; cur_src_char_ptr < src_ptr + src_size;)
{
if (trim_ascii_spaces && *cur_src_char_ptr == ' ')
{
cur_src_char_ptr += 1;
*lead_trimmed_length -= 1;
*lead_trimmed_size -= 1;
*lead_trimmed_ptr += 1;
continue;
}
for (cur_trim_char_ptr = (unsigned char *) trim_charset_ptr;
cur_src_char_ptr < (src_ptr + src_size) && (cur_trim_char_ptr < trim_charset_ptr + trim_charset_size);)
{
intl_char_size (cur_src_char_ptr, 1, codeset, &cur_src_char_size);
intl_char_size (cur_trim_char_ptr, 1, codeset, &cur_trim_char_size);
if (cur_src_char_size != cur_trim_char_size)
{
return;
}
cmp_flag = memcmp ((char *) cur_src_char_ptr, (char *) cur_trim_char_ptr, cur_trim_char_size);
if (cmp_flag != 0)
{
return;
}
cur_src_char_ptr += cur_src_char_size;
cur_trim_char_ptr += cur_trim_char_size;
}
if (cur_trim_char_ptr >= trim_charset_ptr + trim_charset_size)
{ /* all string matched */
*lead_trimmed_length -= trim_charset_size;
*lead_trimmed_size -= trim_charset_size;
*lead_trimmed_ptr += trim_charset_size;
}
}
}
/*
* qstr_trim_trailing () -
*
* Arguments:
* trim_charset_ptr: (in) Single character trim string.
* trim_charset_size: (in) Size of trim string.
* src_ptr: (in) Source string to be trimmed.
* src_length: (in) Length of source string.
* codeset: (in) International codeset of source string.
* trail_trimmed_length: (out) Length of trimmed string.
* trim_ascii_spaces: (in) Option to trim normal spaces also.
*
* Returns: nothing
*
* Errors:
*
* Note:
* Remove trim character from the end of the source string. No
* characters are actually removed. Instead, the function returns
* a pointer to the beginning of the source string after the trim
* characters and the resultant length of the string.
*
*/
void
qstr_trim_trailing (const unsigned char *trim_charset_ptr, int trim_charset_size, const unsigned char *src_ptr,
DB_TYPE src_type, int src_length, int src_size, INTL_CODESET codeset, int *trail_trimmed_length,
int *trail_trimmed_size, bool trim_ascii_spaces)
{
int prev_src_char_size, prev_trim_char_size;
const unsigned char *cur_src_char_ptr, *cur_trim_char_ptr;
const unsigned char *prev_src_char_ptr, *prev_trim_char_ptr;
int cmp_flag = 0;
*trail_trimmed_length = src_length;
*trail_trimmed_size = src_size;
/* iterate for source string */
for (cur_src_char_ptr = (unsigned char *) src_ptr + src_size; cur_src_char_ptr > src_ptr;)
{
if (trim_ascii_spaces && *(cur_src_char_ptr - 1) == ' ')
{
cur_src_char_ptr -= 1;
*trail_trimmed_length -= 1;
*trail_trimmed_size -= 1;
continue;
}
for (cur_trim_char_ptr = (unsigned char *) trim_charset_ptr + trim_charset_size;
cur_trim_char_ptr > trim_charset_ptr && cur_src_char_ptr > src_ptr;)
{
/* get previous letter */
prev_src_char_ptr = intl_prev_char (cur_src_char_ptr, src_ptr, codeset, &prev_src_char_size);
prev_trim_char_ptr = intl_prev_char (cur_trim_char_ptr, trim_charset_ptr, codeset, &prev_trim_char_size);
if (prev_trim_char_size != prev_src_char_size)
{
return;
}
cmp_flag = memcmp ((char *) prev_src_char_ptr, (char *) prev_trim_char_ptr, prev_trim_char_size);
if (cmp_flag != 0)
{
return;
}
cur_src_char_ptr -= prev_src_char_size;
cur_trim_char_ptr -= prev_trim_char_size;
}
if (cur_trim_char_ptr <= trim_charset_ptr)
{
*trail_trimmed_length -= trim_charset_size;
*trail_trimmed_size -= trim_charset_size;
}
}
}
/*
* db_string_pad () -
*
* Arguments:
* pad_operand: (in) Left or Right padding?
* src_string: (in) Source string to be padded.
* pad_length: (in) Length of padded string
* pad_charset: (in) Padding char set
* padded_string: (out) Padded string
*
* Returns: nothing
*/
int
db_string_pad (const MISC_OPERAND pad_operand, const DB_VALUE * src_string, const DB_VALUE * pad_length,
const DB_VALUE * pad_charset, DB_VALUE * padded_string)
{
int error_status = NO_ERROR;
int total_length;
unsigned char *result;
int result_length = 0, result_size = 0;
const unsigned char *pad_charset_ptr = NULL;
int pad_charset_length = 0;
int pad_charset_size = 0;
DB_TYPE src_type;
bool is_pad_charset_omitted = false;
assert (src_string != (DB_VALUE *) NULL);
assert (padded_string != (DB_VALUE *) NULL);
db_value_domain_init (padded_string, DB_TYPE_VARCHAR, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
/* if source is NULL, return NULL */
if (DB_IS_NULL (src_string))
{
return NO_ERROR;
}
if (pad_charset == NULL)
{
is_pad_charset_omitted = true;
}
else if (DB_IS_NULL (pad_charset))
{
return NO_ERROR;
}
if (DB_IS_NULL (pad_length) || (total_length = db_get_int (pad_length)) <= 0)
{
/* error_status = ER_QPROC_INVALID_PARAMETER; */// why is this commented??
return error_status; // this is NO_ERROR
}
src_type = DB_VALUE_DOMAIN_TYPE (src_string);
if (!QSTR_IS_ANY_CHAR (src_type) || (!is_pad_charset_omitted && !is_char_string (pad_charset)))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INVALID_DATA_TYPE, 0);
return error_status;
}
if (!is_pad_charset_omitted
&& (qstr_get_category (src_string) != qstr_get_category (pad_charset)
|| db_get_string_codeset (src_string) != db_get_string_codeset (pad_charset)))
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INCOMPATIBLE_CODE_SETS, 0);
return error_status;
}
if (!is_pad_charset_omitted)
{
pad_charset_ptr = DB_GET_UCHAR (pad_charset);
pad_charset_length = db_get_string_length (pad_charset);
pad_charset_size = db_get_string_size (pad_charset);
}
error_status = qstr_pad (pad_operand, total_length, pad_charset_ptr, pad_charset_length, pad_charset_size,
DB_GET_UCHAR (src_string), DB_VALUE_DOMAIN_TYPE (src_string),
db_get_string_length (src_string), db_get_string_size (src_string),
db_get_string_codeset (src_string), &result, &result_length, &result_size);
if (error_status != NO_ERROR)
{
assert (result == NULL);
ASSERT_ERROR ();
return error_status;
}
if (result == NULL)
{
// null result
return NO_ERROR;
}
// check length/size
if (result_length > QSTR_MAX_PRECISION (DB_VALUE_DOMAIN_TYPE (src_string)))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_PRECISION_OVERFLOW, 2, result_length,
QSTR_MAX_PRECISION (DB_VALUE_DOMAIN_TYPE (src_string)));
db_private_free_and_init (NULL, result);
return ER_PRECISION_OVERFLOW;
}
if ((UINT64) result_size > prm_get_bigint_value (PRM_ID_STRING_MAX_SIZE_BYTES))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_STRING_SIZE_TOO_BIG, 2, result_size,
(int) prm_get_bigint_value (PRM_ID_STRING_MAX_SIZE_BYTES));
db_private_free_and_init (NULL, result);
return ER_QPROC_STRING_SIZE_TOO_BIG;
}
qstr_make_typed_string (DB_TYPE_VARCHAR, padded_string, result_length, (char *) result, result_size,
db_get_string_codeset (src_string), db_get_string_collation (src_string));
result[result_size] = 0;
padded_string->need_clear = true;
return NO_ERROR;
}
/*
* qstr_pad () -
*/
static int
qstr_pad (MISC_OPERAND pad_operand, int pad_length, const unsigned char *pad_charset_ptr, int pad_charset_length,
int pad_charset_size, const unsigned char *src_ptr, DB_TYPE src_type, int src_length, int src_size,
INTL_CODESET codeset, unsigned char **result, int *result_length, int *result_size)
{
unsigned char def_pad_char[2];
unsigned char *cur_pad_char_ptr;
int def_pad_char_size = 0; /* default padding char */
int truncate_size, pad_size, alloc_size, cnt;
int length_to_be_padded; /* length that will be really padded */
int remain_length_to_be_padded; /* remained length that will be padded */
int pad_full_size = 0;
int pad_reminder_size = 0;
int error_status = NO_ERROR;
if (codeset == INTL_CODESET_KSC5601_EUC)
{
def_pad_char[0] = ' ';
def_pad_char_size = 1;
}
else
{
intl_pad_char (codeset, def_pad_char, &def_pad_char_size);
}
if (pad_charset_length == 0)
{
pad_charset_ptr = def_pad_char;
pad_charset_length = 1;
pad_charset_size = def_pad_char_size;
}
assert (pad_charset_length > 0);
if (src_length >= pad_length)
{
alloc_size = src_size;
}
else
{
pad_full_size = ((pad_length - src_length) / pad_charset_length) * pad_charset_size;
intl_char_size ((unsigned char *) pad_charset_ptr, (pad_length - src_length) % pad_charset_length, codeset,
&pad_reminder_size);
alloc_size = src_size + pad_full_size + pad_reminder_size;
}
*result = (unsigned char *) db_private_alloc (NULL, (size_t) alloc_size + 1);
if (*result == NULL)
{
assert (er_errid () != NO_ERROR);
error_status = er_errid ();
return error_status;
}
/*
* now start padding
*/
/* if source length is greater than pad_length */
if (src_length >= pad_length)
{
truncate_size = 0; /* SIZE to be cut */
intl_char_size ((unsigned char *) src_ptr, pad_length, codeset, &truncate_size);
memcpy ((char *) (*result), (char *) src_ptr, truncate_size);
*result_length = pad_length;
*result_size = truncate_size;
return error_status;
}
/*
* Get real length to be paded
* if source length is greater than pad_length
*/
length_to_be_padded = pad_length - src_length;
/* pad heading first */
cnt = 0; /* how many times copy pad_char_set */
pad_size = 0; /* SIZE of padded char */
remain_length_to_be_padded = 0;
for (; cnt < (length_to_be_padded / pad_charset_length); cnt++)
{
(void) memcpy ((char *) (*result) + pad_charset_size * cnt, (char *) pad_charset_ptr, pad_charset_size);
}
pad_size = pad_charset_size * cnt;
remain_length_to_be_padded = (pad_length - src_length) % pad_charset_length;
if (remain_length_to_be_padded != 0)
{
int remain_size_to_be_padded = 0;
assert (remain_length_to_be_padded > 0);
cur_pad_char_ptr = (unsigned char *) pad_charset_ptr;
intl_char_size (cur_pad_char_ptr, remain_length_to_be_padded, codeset, &remain_size_to_be_padded);
(void) memcpy ((char *) (*result) + pad_size, (char *) cur_pad_char_ptr, remain_size_to_be_padded);
cur_pad_char_ptr += remain_size_to_be_padded;
pad_size += remain_size_to_be_padded;
}
memcpy ((char *) (*result) + pad_size, src_ptr, src_size);
if (pad_operand == TRAILING)
{ /* switch source and padded string */
memmove ((char *) (*result) + src_size, (char *) (*result), pad_size);
memcpy ((char *) (*result), src_ptr, src_size);
}
pad_size += src_size;
*result_length = pad_length;
*result_size = pad_size;
return error_status;
}
/*
* db_string_like () -
*
* Arguments:
* src_string: (IN) Source string.
* pattern: (IN) Pattern string which can contain % and _
* characters.
* esc_char: (IN) Optional escape character.
* result: (OUT) Integer result.
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE:
* <src_string>, <pattern>, or <esc_char> (if it's not NULL)
* is not a character string.
*
* ER_QSTR_INCOMPATIBLE_CODE_SETS:
* <src_string>, <pattern>, and <esc_char> (if it's not NULL)
* have different character code sets.
*
* ER_QSTR_INVALID_ESCAPE_SEQUENCE:
* An illegal pattern is specified.
*
* ER_QSTR_INVALID_ESCAPE_CHARACTER:
* If <esc_char> is not NULL and the length of E is > 1.
*
*/
/* TODO ER_QSTR_INVALID_ESCAPE_CHARACTER is not checked for, although it
probably should be (the escape sequence string should contain a single
character)
*/
int
db_string_like (const DB_VALUE * src_string, const DB_VALUE * pattern, const DB_VALUE * esc_char, int *result)
{
QSTR_CATEGORY src_category = QSTR_UNKNOWN;
QSTR_CATEGORY pattern_category = QSTR_UNKNOWN;
int error_status = NO_ERROR;
DB_TYPE src_type = DB_TYPE_UNKNOWN;
DB_TYPE pattern_type = DB_TYPE_UNKNOWN;
const char *src_char_string_p = NULL;
const char *pattern_char_string_p = NULL;
const char *esc_char_p = NULL;
int src_length = 0, pattern_length = 0;
int coll_id;
/*
* Assert that DB_VALUE structures have been allocated.
*/
assert (src_string != NULL);
assert (pattern != NULL);
src_category = qstr_get_category (src_string);
pattern_category = qstr_get_category (pattern);
src_type = DB_VALUE_DOMAIN_TYPE (src_string);
pattern_type = DB_VALUE_DOMAIN_TYPE (pattern);
if (!QSTR_IS_ANY_CHAR (src_type) || !QSTR_IS_ANY_CHAR (pattern_type))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INVALID_DATA_TYPE, 0);
*result = V_ERROR;
return error_status;
}
if (src_category != pattern_category || (db_get_string_codeset (src_string) != db_get_string_codeset (pattern)))
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
*result = V_ERROR;
return error_status;
}
if (DB_IS_NULL (src_string))
{
*result = V_UNKNOWN;
return error_status;
}
if (DB_IS_NULL (pattern))
{
*result = V_FALSE;
return error_status;
}
LANG_RT_COMMON_COLL (db_get_string_collation (src_string), db_get_string_collation (pattern), coll_id);
if (coll_id == -1)
{
error_status = ER_QSTR_INCOMPATIBLE_COLLATIONS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
*result = V_ERROR;
return error_status;
}
if (esc_char)
{
if (DB_IS_NULL (esc_char))
{
/* The implicit escape character ('\\') is used if (a LIKE b ESCAPE NULL) is given in the syntax */
esc_char_p = "\\";
}
else
{
QSTR_CATEGORY esc_category = qstr_get_category (esc_char);
DB_TYPE esc_type = DB_VALUE_DOMAIN_TYPE (esc_char);
int esc_char_len, esc_char_size;
if (QSTR_IS_ANY_CHAR (esc_type))
{
if (src_category == esc_category)
{
esc_char_p = db_get_string (esc_char);
esc_char_size = db_get_string_size (esc_char);
intl_char_count ((unsigned char *) esc_char_p, esc_char_size, db_get_string_codeset (esc_char),
&esc_char_len);
assert (esc_char_p != NULL);
if (esc_char_len != 1)
{
error_status = ER_QSTR_INVALID_ESCAPE_SEQUENCE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
*result = V_ERROR;
return error_status;
}
}
else
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
*result = V_ERROR;
return error_status;
}
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INVALID_DATA_TYPE, 0);
*result = V_ERROR;
return error_status;
}
}
}
src_char_string_p = db_get_string (src_string);
src_length = db_get_string_size (src_string);
pattern_char_string_p = db_get_string (pattern);
pattern_length = db_get_string_size (pattern);
*result =
qstr_eval_like (src_char_string_p, src_length, pattern_char_string_p, pattern_length,
(esc_char ? esc_char_p : NULL), db_get_string_codeset (src_string), coll_id);
if (*result == V_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INVALID_ESCAPE_SEQUENCE, 0);
}
return ((*result == V_ERROR) ? ER_QSTR_INVALID_ESCAPE_SEQUENCE : error_status);
}
/*
* db_string_rlike () - check for match between string and regex
*
* Arguments:
* src: (IN) Source string.
* pattern: (IN) Regular expression.
* case_sensitive: (IN) Perform case sensitive matching when 1
* comp_regex: (IN/OUT) Compiled regex object
* comp_pattern: (IN/OUT) Compiled regex pattern
* result: (OUT) Integer result.
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE:
* <src>, <pattern> (if it's not NULL)
* is not a character string.
*
* ER_QSTR_INCOMPATIBLE_CODE_SETS:
* <src>, <pattern> (if it's not NULL)
* have different character code sets.
*
* ER_QSTR_INVALID_ESCAPE_SEQUENCE:
* An illegal pattern is specified.
*
*/
int
db_string_rlike (const DB_VALUE * src, const DB_VALUE * pattern, const DB_VALUE * case_sensitive,
cub_compiled_regex ** comp_regex, int *result)
{
int error_status = NO_ERROR;
*result = V_FALSE;
cub_compiled_regex *compiled_regex = (comp_regex != NULL) ? *comp_regex : nullptr;
{
/* check for allocated DB values */
assert (src != NULL);
assert (pattern != NULL);
assert (case_sensitive != NULL);
/* check type */
QSTR_CATEGORY src_category = qstr_get_category (src);
QSTR_CATEGORY pattern_category = qstr_get_category (pattern);
DB_TYPE src_type = DB_VALUE_DOMAIN_TYPE (src);
DB_TYPE pattern_type = DB_VALUE_DOMAIN_TYPE (pattern);
DB_TYPE case_sens_type = DB_VALUE_DOMAIN_TYPE (case_sensitive);
if (!QSTR_IS_ANY_CHAR (src_type) || !QSTR_IS_ANY_CHAR (pattern_type))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto cleanup;
}
if (DB_IS_NULL (src) || DB_IS_NULL (pattern))
{
goto cleanup;
}
if (DB_IS_NULL (case_sensitive) || case_sens_type != DB_TYPE_INTEGER)
{
error_status = ER_QPROC_INVALID_PARAMETER;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto cleanup;
}
INTL_CODESET src_codeset = db_get_string_codeset (src);
INTL_CODESET pattern_codeset = db_get_string_codeset (pattern);
/* check codeset compatible */
if ((src_category != pattern_category) || (src_codeset != pattern_codeset))
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
*result = V_ERROR;
goto cleanup;
}
/* collation compatible check */
int coll_id = -1;
LANG_RT_COMMON_COLL (db_get_string_collation (src), db_get_string_collation (pattern), coll_id);
if (coll_id == -1)
{
error_status = ER_QSTR_INCOMPATIBLE_COLLATIONS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
*result = V_ERROR;
goto cleanup;
}
if (db_get_string_size (pattern) == 0)
{
goto cleanup;
}
LANG_COLLATION *collation = lang_get_collation (coll_id);
assert (collation != NULL);
/* extract case sensitivity */
std::string match_str = (case_sensitive->data.i != 0) ? "c" : "i";
/* compile pattern if needed */
std::string pattern_string (db_get_string (pattern), db_get_string_size (pattern));
error_status = cubregex::compile (compiled_regex, pattern_string, match_str, collation);
if (error_status != NO_ERROR)
{
goto cleanup;
}
std::string src_string (db_get_string (src), db_get_string_size (src));
error_status = cubregex::search (*result, *compiled_regex, src_string);
if (error_status != NO_ERROR)
{
goto cleanup;
}
}
cleanup:
if (error_status != NO_ERROR)
{
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
/* we must not return an error code */
*result = V_UNKNOWN;
er_clear ();
error_status = NO_ERROR;
}
else
{
(error_status == ER_QSTR_BAD_SRC_CODESET) ? error_status = NO_ERROR : *result = V_ERROR;
}
}
if (comp_regex == NULL)
{
/* free memory if this function is invoked in constant folding */
delete compiled_regex;
compiled_regex = NULL;
}
else
{
*comp_regex = compiled_regex;
}
return error_status;
}
/*
* db_string_regexp_count () returns the number of times that regex pattern matched in a given string.
*
* Arguments:
* result: (IN) Result String
* args: (IN) Array of Arguments
* num_args: (IN) # of Arguments
* comp_regex: (IN/OUT) Compiled regex object
* comp_pattern: (IN/OUT) Compiled regex pattern
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE:
* <src>, <pattern> (if it's not NULL)
* is not a character string.
*
* ER_QSTR_INCOMPATIBLE_CODE_SETS:
* <src_string>, <pattern> (if it's not NULL)
* have different character code sets.
*
* ER_QSTR_INCOMPATIBLE_COLLATIONS:
* <src_string>, <pattern> (if it's not NULL)
* are incompatible collations.
*
* ER_REGEX_COMPILE_ERROR:
* An illegal regex pattern is specified.
*
* ER_REGEX_EXEC_ERROR:
* An regex pattern is too complex or insufficient memory while executing regex matching
*
* ER_QPROC_INVALID_PARAMETER:
* Invalid parameter exists
*
*/
int
db_string_regexp_count (DB_VALUE * result, DB_VALUE * args[], int const num_args, cub_compiled_regex ** comp_regex)
{
int error_status = NO_ERROR;
db_make_int (result, 0);
cub_compiled_regex *compiled_regex = (comp_regex != NULL) ? *comp_regex : nullptr;
{
for (int i = 0; i < num_args; i++)
{
DB_VALUE *arg = args[i];
/* check for allocated DB value */
assert (arg != (DB_VALUE *) NULL);
/* if any argument is NULL, return NULL */
if (DB_IS_NULL (arg))
{
db_make_null (result);
goto exit;
}
}
const DB_VALUE *src = args[0];
const DB_VALUE *pattern = args[1];
const DB_VALUE *position = (num_args >= 3) ? args[2] : NULL;
const DB_VALUE *match_type = (num_args == 4) ? args[3] : NULL;
/* check type */
if (!is_char_string (src) || !is_char_string (pattern))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (position && !is_integer (position))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (match_type && !is_char_string (match_type))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* check codeset compatible */
if (qstr_get_category (src) != qstr_get_category (pattern)
|| db_get_string_codeset (src) != db_get_string_codeset (pattern))
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* check collation compatible */
int coll_id = -1;
LANG_RT_COMMON_COLL (db_get_string_collation (src), db_get_string_collation (pattern), coll_id);
if (coll_id == -1)
{
error_status = ER_QSTR_INCOMPATIBLE_COLLATIONS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* check position argument */
int position_value = (position != NULL) ? db_get_int (position) - 1 : 0;
if (position_value < 0)
{
error_status = ER_QPROC_INVALID_PARAMETER;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* match_type argument check */
std::string match_type_str;
if (match_type)
{
match_type_str.assign (db_get_string (match_type), db_get_string_size (match_type));
}
/* check pattern string */
if (db_get_string_size (pattern) == 0 || position_value >= db_get_string_size (src))
{
goto exit;
}
LANG_COLLATION *collation = lang_get_collation (coll_id);
assert (collation != NULL);
/* compile pattern if needed */
std::string pattern_string (db_get_string (pattern), db_get_string_size (pattern));
error_status = cubregex::compile (compiled_regex, pattern_string, match_type_str, collation);
if (error_status != NO_ERROR)
{
goto exit;
}
/* perform regular expression according to the position and occurence value */
int result_value = 0;
std::string src_string (db_get_string (src), db_get_string_size (src));
error_status = cubregex::count (result_value, *compiled_regex, src_string, position_value);
if (error_status != NO_ERROR)
{
/* regex execution error */
goto exit;
}
/* make result */
db_make_int (result, result_value);
goto exit;
}
exit:
if (error_status != NO_ERROR)
{
db_make_int (result, 0);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
/* we must not return an error code */
er_clear ();
error_status = NO_ERROR;
}
else
{
error_status = (error_status == ER_QSTR_BAD_SRC_CODESET) ? NO_ERROR : error_status;
}
}
if (comp_regex == NULL)
{
/* free memory if this function is invoked in constant folding */
delete compiled_regex;
compiled_regex = NULL;
}
else
{
*comp_regex = compiled_regex;
}
return error_status;
}
/*
* db_string_regexp_instr () returns an integer indicating the position of searched sub-string by regex pattern
*
* Arguments:
* result: (IN) Result String
* args: (IN) Array of Arguments
* num_args: (IN) # of Arguments
* comp_regex: (IN/OUT) Compiled regex object
* comp_pattern: (IN/OUT) Compiled regex pattern
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE:
* <src>, <pattern>, <replace> (if it's not NULL)
* is not a character string.
*
* ER_QSTR_INCOMPATIBLE_CODE_SETS:
* <src>, <pattern> (if it's not NULL)
* have different character code sets.
*
* ER_QSTR_INCOMPATIBLE_COLLATIONS:
* <src>, <pattern>, <replace> (if it's not NULL)
* are incompatible collations.
*
* ER_REGEX_COMPILE_ERROR:
* An illegal regex pattern is specified.
*
* ER_REGEX_EXEC_ERROR:
* An regex pattern is too complex or insufficient memory while executing regex matching
*
* ER_QPROC_INVALID_PARAMETER:
* Invalid parameter exists
*
*/
int
db_string_regexp_instr (DB_VALUE * result, DB_VALUE * args[], int const num_args, cub_compiled_regex ** comp_regex)
{
int error_status = NO_ERROR;
db_make_int (result, 0);
cub_compiled_regex *compiled_regex = (comp_regex != NULL) ? *comp_regex : nullptr;
{
for (int i = 0; i < num_args; i++)
{
DB_VALUE *arg = args[i];
/* check for allocated DB value */
assert (arg != (DB_VALUE *) NULL);
/* if any argument is NULL, return NULL */
if (DB_IS_NULL (arg))
{
db_make_null (result);
goto exit;
}
}
const DB_VALUE *src = args[0];
const DB_VALUE *pattern = args[1];
const DB_VALUE *position = (num_args >= 3) ? args[2] : NULL;
const DB_VALUE *occurrence = (num_args >= 4) ? args[3] : NULL;
const DB_VALUE *return_opt = (num_args >= 5) ? args[4] : NULL;
const DB_VALUE *match_type = (num_args == 6) ? args[5] : NULL;
/* check type */
if (!is_char_string (src) || !is_char_string (pattern))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (position && !is_integer (position))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (occurrence && !is_integer (occurrence))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (return_opt && !is_integer (return_opt))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (match_type && !is_char_string (match_type))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* check codeset compatible */
if (qstr_get_category (src) != qstr_get_category (pattern)
|| db_get_string_codeset (src) != db_get_string_codeset (pattern))
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* check collation compatible */
int coll_id = -1;
LANG_RT_COMMON_COLL (db_get_string_collation (src), db_get_string_collation (pattern), coll_id);
if (coll_id == -1)
{
error_status = ER_QSTR_INCOMPATIBLE_COLLATIONS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* check position argument */
int position_value = (position != NULL) ? db_get_int (position) - 1 : 0;
if (position_value < 0)
{
error_status = ER_QPROC_INVALID_PARAMETER;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* check occurrence argument */
int occurrence_value = (occurrence != NULL) ? db_get_int (occurrence) : 1;
if (occurrence_value < 1)
{
error_status = ER_QPROC_INVALID_PARAMETER;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* check return_opt argument */
int return_opt_value = (return_opt != NULL) ? db_get_int (return_opt) : 0;
if (return_opt_value != 0 && return_opt_value != 1)
{
error_status = ER_QPROC_INVALID_PARAMETER;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* match_type argument check */
std::string match_type_str;
if (match_type)
{
match_type_str.assign (db_get_string (match_type), db_get_string_size (match_type));
}
/* check pattern string */
if (db_get_string_size (pattern) == 0 || position_value >= db_get_string_size (src))
{
goto exit;
}
LANG_COLLATION *collation = lang_get_collation (coll_id);
assert (collation != NULL);
/* compile pattern if needed */
std::string pattern_string (db_get_string (pattern), db_get_string_size (pattern));
error_status = cubregex::compile (compiled_regex, pattern_string, match_type_str, collation);
if (error_status != NO_ERROR)
{
goto exit;
}
/* perform regular expression according to the position and occurence value */
int result_value = 0;
std::string src_string (db_get_string (src), db_get_string_size (src));
error_status = cubregex::instr (result_value, *compiled_regex, src_string, position_value,
occurrence_value, return_opt_value);
if (error_status != NO_ERROR)
{
/* regex execution error */
goto exit;
}
/* make result */
db_make_int (result, result_value);
goto exit;
}
exit:
if (error_status != NO_ERROR)
{
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
/* we must not return an error code */
db_make_null (result);
er_clear ();
error_status = NO_ERROR;
}
else
{
db_make_int (result, 0);
error_status = (error_status == ER_QSTR_BAD_SRC_CODESET) ? NO_ERROR : error_status;
}
}
if (comp_regex == NULL)
{
/* free memory if this function is invoked in constant folding */
delete compiled_regex;
compiled_regex = NULL;
}
else
{
*comp_regex = compiled_regex;
}
return error_status;
}
/*
* db_string_regexp_like () checks for match between string and regex
*
* Arguments:
* result: (IN) Result String
* args: (IN) Array of Arguments
* num_args: (IN) # of Arguments
* comp_regex: (IN/OUT) Compiled regex object
* comp_pattern: (IN/OUT) Compiled regex pattern
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE:
* <src>, <pattern>, <replace> (if it's not NULL)
* is not a character string.
*
* ER_QSTR_INCOMPATIBLE_CODE_SETS:
* <src>, <pattern> (if it's not NULL)
* have different character code sets.
*
* ER_QSTR_INCOMPATIBLE_COLLATIONS:
* <src>, <pattern>, <replace> (if it's not NULL)
* are incompatible collations.
*
* ER_REGEX_COMPILE_ERROR:
* An illegal regex pattern is specified.
*
* ER_REGEX_EXEC_ERROR:
* An regex pattern is too complex or insufficient memory while executing regex matching
*
* ER_QPROC_INVALID_PARAMETER:
* Invalid parameter exists
*
*/
int
db_string_regexp_like (DB_VALUE * result, DB_VALUE * args[], int const num_args, cub_compiled_regex ** comp_regex)
{
int error_status = NO_ERROR;
db_make_null (result);
cub_compiled_regex *compiled_regex = (comp_regex != NULL) ? *comp_regex : nullptr;
{
for (int i = 0; i < num_args; i++)
{
DB_VALUE *arg = args[i];
/* check for allocated DB value */
assert (arg != (DB_VALUE *) NULL);
/* if any argument is NULL, return NULL */
if (DB_IS_NULL (arg))
{
goto exit;
}
}
const DB_VALUE *src = args[0];
const DB_VALUE *pattern = args[1];
const DB_VALUE *match_type = (num_args == 3) ? args[2] : NULL;
/* check type */
if (!is_char_string (src) || !is_char_string (pattern))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (match_type && !is_char_string (match_type))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* check codeset compatible */
if (qstr_get_category (src) != qstr_get_category (pattern)
|| db_get_string_codeset (src) != db_get_string_codeset (pattern))
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* check collation compatible */
int coll_id = -1;
LANG_RT_COMMON_COLL (db_get_string_collation (src), db_get_string_collation (pattern), coll_id);
if (coll_id == -1)
{
error_status = ER_QSTR_INCOMPATIBLE_COLLATIONS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* match_type argument check */
std::string match_type_str;
if (match_type)
{
match_type_str.assign (db_get_string (match_type), db_get_string_size (match_type));
}
/* check pattern string */
if (db_get_string_size (pattern) == 0)
{
db_make_int (result, 0);
goto exit;
}
LANG_COLLATION *collation = lang_get_collation (coll_id);
assert (collation != NULL);
/* compile pattern if needed */
std::string pattern_string (db_get_string (pattern), db_get_string_size (pattern));
error_status = cubregex::compile (compiled_regex, pattern_string, match_type_str, collation);
if (error_status != NO_ERROR)
{
goto exit;
}
/* perform regular expression according to the position and occurence value */
int result_value = 0;
std::string src_string (db_get_string (src), db_get_string_size (src));
error_status = cubregex::search (result_value, *compiled_regex, src_string);
if (error_status != NO_ERROR)
{
/* regex execution error */
goto exit;
}
/* make result */
db_make_int (result, result_value);
goto exit;
}
exit:
if (error_status != NO_ERROR)
{
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
/* we must not return an error code */
er_clear ();
error_status = NO_ERROR;
}
else
{
db_make_int (result, 0);
error_status = (error_status == ER_QSTR_BAD_SRC_CODESET) ? NO_ERROR : error_status;
}
}
if (comp_regex == NULL)
{
/* free memory if this function is invoked in constant folding */
delete compiled_regex;
compiled_regex = NULL;
}
else
{
*comp_regex = compiled_regex;
}
return error_status;
}
/*
* db_string_regexp_replace () returns replaced string by regex pattern
*
* Arguments:
* result: (IN) Result String
* args: (IN) Array of Arguments
* num_args: (IN) # of Arguments
* comp_regex: (IN/OUT) Compiled regex object
* comp_pattern: (IN/OUT) Compiled regex pattern
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE:
* <src>, <pattern>, <replace> (if it's not NULL)
* is not a character string.
*
* ER_QSTR_INCOMPATIBLE_CODE_SETS:
* <src>, <pattern> (if it's not NULL)
* have different character code sets.
*
* ER_QSTR_INCOMPATIBLE_COLLATIONS:
* <src>, <pattern>, <replace> (if it's not NULL)
* are incompatible collations.
*
* ER_REGEX_COMPILE_ERROR:
* An illegal regex pattern is specified.
*
* ER_REGEX_EXEC_ERROR:
* An regex pattern is too complex or insufficient memory while executing regex matching
*
* ER_QPROC_INVALID_PARAMETER:
* Invalid parameter exists
*
*/
int
db_string_regexp_replace (DB_VALUE * result, DB_VALUE * args[], int const num_args, cub_compiled_regex ** comp_regex)
{
int error_status = NO_ERROR;
db_make_null (result);
cub_compiled_regex *compiled_regex = (comp_regex != NULL) ? *comp_regex : nullptr;
{
for (int i = 0; i < num_args; i++)
{
DB_VALUE *arg = args[i];
/* check for allocated DB value */
assert (arg != (DB_VALUE *) NULL);
/* if any argument is NULL, return NULL */
if (DB_IS_NULL (arg))
{
goto exit;
}
}
const DB_VALUE *src = args[0];
const DB_VALUE *pattern = args[1];
const DB_VALUE *replace = args[2];
const DB_VALUE *position = (num_args >= 4) ? args[3] : NULL;
const DB_VALUE *occurrence = (num_args >= 5) ? args[4] : NULL;
const DB_VALUE *match_type = (num_args == 6) ? args[5] : NULL;
/* check type */
if (!is_char_string (src) || !is_char_string (pattern) || !is_char_string (replace))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (position && !is_integer (position))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (occurrence && !is_integer (occurrence))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (match_type && !is_char_string (match_type))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* check codeset compatible */
QSTR_CATEGORY category = qstr_get_category (src);
INTL_CODESET codeset = db_get_string_codeset (src);
if (category != qstr_get_category (pattern) || codeset != db_get_string_codeset (pattern))
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (category != qstr_get_category (replace) || codeset != db_get_string_codeset (replace))
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* check collation compatible */
int coll_id_tmp = -1, coll_id = -1;
LANG_RT_COMMON_COLL (db_get_string_collation (src), db_get_string_collation (pattern), coll_id_tmp);
if (coll_id_tmp == -1)
{
error_status = ER_QSTR_INCOMPATIBLE_COLLATIONS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
LANG_RT_COMMON_COLL (coll_id_tmp, db_get_string_collation (replace), coll_id);
if (coll_id == -1)
{
error_status = ER_QSTR_INCOMPATIBLE_COLLATIONS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* check position argument */
int position_value = (position != NULL) ? db_get_int (position) - 1 : 0;
if (position_value < 0)
{
error_status = ER_QPROC_INVALID_PARAMETER;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* check occurrence argument */
int occurrence_value = (occurrence != NULL) ? db_get_int (occurrence) : 0;
if (occurrence_value < 0)
{
error_status = ER_QPROC_INVALID_PARAMETER;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* match_type argument check */
std::string match_type_str;
if (match_type)
{
match_type_str.assign (db_get_string (match_type), db_get_string_size (match_type));
}
/* check pattern string */
if (db_get_string_size (pattern) == 0 || position_value >= db_get_string_size (src))
{
goto exit_copy;
}
LANG_COLLATION *collation = lang_get_collation (coll_id);
assert (collation != NULL);
/* compile pattern if needed */
std::string pattern_string (db_get_string (pattern), db_get_string_size (pattern));
error_status = cubregex::compile (compiled_regex, pattern_string, match_type_str, collation);
if (error_status != NO_ERROR)
{
goto exit;
}
/* perform regular expression according to the position and occurence value */
std::string result_string;
std::string src_string (db_get_string (src), db_get_string_size (src));
std::string repl_string (db_get_string (replace), db_get_string_size (replace));
error_status = cubregex::replace (result_string, *compiled_regex, src_string, repl_string, position_value,
occurrence_value);
if (error_status != NO_ERROR)
{
/* regex execution error */
if (error_status == ER_QSTR_BAD_SRC_CODESET)
{
goto exit_copy;
}
else
{
goto exit;
}
}
/* make result */
int result_char_size = result_string.size ();
char *result_char_string = (char *) db_private_alloc (NULL, result_char_size + 1);
if (result_char_string == NULL)
{
/* out of memory */
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
memcpy (result_char_string, result_string.c_str (), result_char_size);
result_char_string[result_char_size] = '\0';
qstr_make_typed_string (DB_TYPE_VARCHAR, result,
result_char_size, result_char_string, result_char_size,
db_get_string_codeset (src), coll_id);
result->need_clear = true;
goto exit;
}
exit_copy:
{
/* clear error status */
error_status = NO_ERROR;
const DB_VALUE *src = args[0];
pr_clone_value ((DB_VALUE *) src, result);
DB_TYPE src_type = DB_VALUE_DOMAIN_TYPE (src);
if (src_type == DB_TYPE_CHAR)
{
/* convert CHARACTER(N) to CHARACTER VARYING(N) */
qstr_make_typed_string (DB_TYPE_VARCHAR, result,
DB_VALUE_PRECISION (result), db_get_string (result), db_get_string_size (result),
db_get_string_codeset (src), db_get_string_collation (src));
}
result->need_clear = true;
}
exit:
if (error_status != NO_ERROR)
{
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
/* we must not return an error code */
er_clear ();
error_status = NO_ERROR;
}
else
{
error_status = (error_status == ER_QSTR_BAD_SRC_CODESET) ? NO_ERROR : error_status;
}
}
if (comp_regex == NULL)
{
/* free memory if this function is invoked in constant folding */
delete compiled_regex;
compiled_regex = NULL;
}
else
{
*comp_regex = compiled_regex;
}
return error_status;
}
/*
* db_string_regexp_substr () returns searched sub-string by regex pattern
*
* Arguments:
* result: (IN) Result String
* args: (IN) Array of Arguments
* num_args: (IN) # of Arguments
* comp_regex: (IN/OUT) Compiled regex object
* comp_pattern: (IN/OUT) Compiled regex pattern
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE:
* <src>, <pattern> (if it's not NULL)
* is not a character string.
*
* ER_QSTR_INCOMPATIBLE_CODE_SETS:
* <src_string>, <pattern> (if it's not NULL)
* have different character code sets.
*
* ER_QSTR_INCOMPATIBLE_COLLATIONS:
* <src_string>, <pattern> (if it's not NULL)
* are incompatible collations.
*
* ER_REGEX_COMPILE_ERROR:
* An illegal regex pattern is specified.
*
* ER_REGEX_EXEC_ERROR:
* An regex pattern is too complex or insufficient memory while executing regex matching
*
* ER_QPROC_INVALID_PARAMETER:
* Invalid parameter exists
*
*/
int
db_string_regexp_substr (DB_VALUE * result, DB_VALUE * args[], int const num_args, cub_compiled_regex ** comp_regex)
{
int error_status = NO_ERROR;
db_make_null (result);
cub_compiled_regex *compiled_regex = (comp_regex != NULL) ? *comp_regex : nullptr;
{
for (int i = 0; i < num_args; i++)
{
DB_VALUE *arg = args[i];
/* check for allocated DB value */
assert (arg != (DB_VALUE *) NULL);
/* if any argument is NULL, return NULL */
if (DB_IS_NULL (arg))
{
goto exit;
}
}
const DB_VALUE *src = args[0];
const DB_VALUE *pattern = args[1];
const DB_VALUE *position = (num_args >= 3) ? args[2] : NULL;
const DB_VALUE *occurrence = (num_args >= 4) ? args[3] : NULL;
const DB_VALUE *match_type = (num_args == 5) ? args[4] : NULL;
/* check type */
if (!is_char_string (src) || !is_char_string (pattern))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (position && !is_integer (position))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (occurrence && !is_integer (occurrence))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (match_type && !is_char_string (match_type))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* check codeset compatible */
if (qstr_get_category (src) != qstr_get_category (pattern)
|| db_get_string_codeset (src) != db_get_string_codeset (pattern))
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* check collation compatible */
int coll_id = -1;
LANG_RT_COMMON_COLL (db_get_string_collation (src), db_get_string_collation (pattern), coll_id);
if (coll_id == -1)
{
error_status = ER_QSTR_INCOMPATIBLE_COLLATIONS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* check position argument */
int position_value = (position != NULL) ? db_get_int (position) - 1 : 0;
if (position_value < 0)
{
error_status = ER_QPROC_INVALID_PARAMETER;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* check occurrence argument */
int occurrence_value = (occurrence != NULL) ? db_get_int (occurrence) : 1;
if (occurrence_value < 1)
{
error_status = ER_QPROC_INVALID_PARAMETER;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/* match_type argument check */
std::string match_type_str;
if (match_type)
{
match_type_str.assign (db_get_string (match_type), db_get_string_size (match_type));
}
/* check pattern string */
if (db_get_string_size (pattern) == 0 || position_value >= db_get_string_size (src))
{
goto exit;
}
LANG_COLLATION *collation = lang_get_collation (coll_id);
assert (collation != NULL);
/* compile pattern if needed */
std::string pattern_string (db_get_string (pattern), db_get_string_size (pattern));
error_status = cubregex::compile (compiled_regex, pattern_string, match_type_str, collation);
if (error_status != NO_ERROR)
{
goto exit;
}
/* perform regular expression according to the position and occurence value */
std::string result_string;
bool is_matched = false;
std::string src_string (db_get_string (src), db_get_string_size (src));
error_status = cubregex::substr (result_string, is_matched, *compiled_regex, src_string, position_value,
occurrence_value);
if (error_status != NO_ERROR)
{
/* regex execution error */
goto exit;
}
if (is_matched)
{
/* make result */
int result_char_size = result_string.size ();
char *result_char_string = (char *) db_private_alloc (NULL, result_char_size + 1);
if (result_char_string == NULL)
{
/* out of memory */
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
memcpy (result_char_string, result_string.c_str (), result_char_size);
result_char_string[result_char_size] = '\0';
qstr_make_typed_string (DB_TYPE_VARCHAR,
result, result_char_size, result_char_string, result_char_size,
db_get_string_codeset (src), coll_id);
result->need_clear = true;
}
}
exit:
if (error_status != NO_ERROR)
{
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
/* we must not return an error code */
er_clear ();
error_status = NO_ERROR;
}
else
{
error_status = (error_status == ER_QSTR_BAD_SRC_CODESET) ? NO_ERROR : error_status;
}
}
if (comp_regex == NULL)
{
/* free memory if this function is invoked in constant folding */
delete compiled_regex;
compiled_regex = NULL;
}
else
{
*comp_regex = compiled_regex;
}
return error_status;
}
/*
* db_string_limit_size_string () - limits the size of a string. It limits
* the size of value, but in case of fixed
* length values, it limits also the domain
* precision.
*
* Arguments:
* src: (IN) String variable.
* result: (OUT) Variable with new size
* new_size: (IN) New size for the string (in bytes).
* spare_bytes: (OUT) the number of bytes that could fit from last
* truncated character
*
* Returns:
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE:
* <src_string> is not CHAR, VARCHAR, BIT or VARBIT
*
* Note : result variable must already be created
* operates directly on memory buffer
* if the new size is greater than the source, it clones the input
* The truncation of domain size in case of fixed domain argument
* is needed in context of GROUP_CONCAT, when the result needs to be
* truncated.
* The full-char adjusting code in this function is specific to
* GROUP_CONCAT.
*/
int
db_string_limit_size_string (DB_VALUE * src_string, DB_VALUE * result, const int new_size, int *spare_bytes)
{
int result_size = 0, src_size = 0, src_domain_precision = 0;
unsigned char *r;
int error_status = NO_ERROR;
DB_TYPE src_type;
int char_count = 0, adj_char_size = 0;
assert (src_string != (DB_VALUE *) NULL);
assert (result != (DB_VALUE *) NULL);
assert (new_size >= 0);
assert (spare_bytes != NULL);
src_type = DB_VALUE_DOMAIN_TYPE (src_string);
if (!QSTR_IS_ANY_CHAR_OR_BIT (src_type))
{
return ER_QSTR_INVALID_DATA_TYPE;
}
src_size = db_get_string_size (src_string);
src_domain_precision = DB_VALUE_PRECISION (src_string);
if (new_size < 0)
{
return ER_QSTR_INVALID_DATA_TYPE;
}
*spare_bytes = 0;
if (src_size <= 0 || new_size >= src_size)
{
assert (error_status == NO_ERROR);
goto exit_copy;
}
result_size = new_size;
/* Adjust size to a full character. */
if (DB_VALUE_DOMAIN_TYPE (src_string) == DB_TYPE_VARBIT || DB_VALUE_DOMAIN_TYPE (src_string) == DB_TYPE_BIT)
{
char_count = result_size;
adj_char_size = result_size;
}
else
{
intl_char_count (DB_GET_UCHAR (src_string), result_size, db_get_string_codeset (src_string), &char_count);
intl_char_size (DB_GET_UCHAR (src_string), char_count, db_get_string_codeset (src_string), &adj_char_size);
}
assert (adj_char_size <= result_size);
/* Allocate storage for the result string */
r = (unsigned char *) db_private_alloc (NULL, (size_t) result_size + 1);
if (r == NULL)
{
goto mem_error;
}
memset (r, 0, (size_t) result_size + 1);
if (adj_char_size > 0)
{
memcpy (r, db_get_string (src_string), adj_char_size);
}
/* adjust also domain precision in case of fixed length types */
if (QSTR_IS_FIXED_LENGTH (src_type))
{
src_domain_precision = MIN (src_domain_precision, char_count);
}
qstr_make_typed_string (src_type, result, src_domain_precision, (char *) r, adj_char_size,
db_get_string_codeset (src_string), db_get_string_collation (src_string));
result->need_clear = true;
*spare_bytes = result_size - adj_char_size;
assert (error_status == NO_ERROR);
return error_status;
mem_error:
assert (er_errid () != NO_ERROR);
error_status = er_errid ();
return error_status;
exit_copy:
assert (error_status == NO_ERROR);
error_status = pr_clone_value (src_string, result);
return error_status;
}
/*
* db_string_fix_string_size () - fixes the size of a string according to its
* content (NULL terminator)
*
* Arguments:
* src: (IN/OUT) String variable.
*
* Returns:
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE:
* <src_string> is not CHAR, VARCHAR
*
* Note : Used in context of GROUP_CONCAT. It is complementary to
* 'db_string_limit_size_string' function
*/
int
db_string_fix_string_size (DB_VALUE * src_string)
{
int val_size = 0;
int string_size = 0;
int error_status = NO_ERROR;
DB_TYPE src_type;
bool save_need_clear;
assert (src_string != (DB_VALUE *) NULL);
src_type = DB_VALUE_DOMAIN_TYPE (src_string);
if (!QSTR_IS_ANY_CHAR (src_type))
{
return ER_QSTR_INVALID_DATA_TYPE;
}
val_size = db_get_string_size (src_string);
/* this is a system generated string; it must have the null terminator */
string_size = strlen (db_get_string (src_string));
assert (val_size >= string_size);
save_need_clear = src_string->need_clear;
qstr_make_typed_string (src_type, src_string, DB_VALUE_PRECISION (src_string), db_get_string (src_string),
string_size, db_get_string_codeset (src_string), db_get_string_collation (src_string));
src_string->need_clear = save_need_clear;
return error_status;
}
/*
* qstr_eval_like () -
*/
/* TODO this function should be modified to not rely on the special value 1
in the situation of no escape character. With the current
implementation it will incorrectly process strings containing
character 1.
*/
static int
qstr_eval_like (const char *tar, int tar_length, const char *expr, int expr_length, const char *escape,
INTL_CODESET codeset, int coll_id)
{
const int IN_CHECK = 0;
const int IN_PERCENT = 1;
int status = IN_CHECK;
const unsigned char *tarstack[STACK_SIZE], *exprstack[STACK_SIZE];
int stackp = -1;
const unsigned char *tar_ptr, *end_tar;
const unsigned char *expr_ptr, *end_expr;
bool escape_is_match_one = ((escape != NULL) && *escape == LIKE_WILDCARD_MATCH_ONE);
bool escape_is_match_many = ((escape != NULL) && *escape == LIKE_WILDCARD_MATCH_MANY);
unsigned char pad_char[2];
LANG_COLLATION *current_collation;
int pad_char_size;
current_collation = lang_get_collation (coll_id);
intl_pad_char (codeset, pad_char, &pad_char_size);
tar_ptr = REINTERPRET_CAST (const unsigned char *, tar);
expr_ptr = REINTERPRET_CAST (const unsigned char *, expr);
end_tar = tar_ptr + tar_length;
end_expr = expr_ptr + expr_length;
while (1)
{
int dummy = 1;
if (status == IN_CHECK)
{
bool go_back = true;
if (expr_ptr == end_expr)
{
go_back = false;
if (codeset != INTL_CODESET_KSC5601_EUC)
{
while (tar_ptr < end_tar && *tar_ptr == ' ')
{
tar_ptr++;
}
}
else
{
while (tar_ptr < end_tar)
{
/* EUC-KR : both ASCII space and EUC-KR padding char are ignored */
if (*tar_ptr == ' ')
{
tar_ptr++;
}
else if (tar_ptr + pad_char_size <= end_tar && memcmp (tar_ptr, pad_char, pad_char_size) == 0)
{
tar_ptr += pad_char_size;
}
else
{
break;
}
}
}
if (tar_ptr == end_tar)
{
return V_TRUE;
}
else
{
if (stackp >= 0 && stackp < STACK_SIZE)
{
tar_ptr = tarstack[stackp];
INTL_NEXT_CHAR (tar_ptr, tar_ptr, codeset, &dummy);
expr_ptr = exprstack[stackp--];
}
else
{
return V_FALSE;
}
}
}
else if (!escape_is_match_many && expr_ptr < end_expr && *expr_ptr == LIKE_WILDCARD_MATCH_MANY)
{
go_back = false;
status = IN_PERCENT;
while ((expr_ptr + 1 < end_expr) && *(expr_ptr + 1) == LIKE_WILDCARD_MATCH_MANY)
{
expr_ptr++;
}
}
else if (tar_ptr < end_tar && expr_ptr < end_expr)
{
if (!escape_is_match_one && *expr_ptr == LIKE_WILDCARD_MATCH_ONE)
{
INTL_NEXT_CHAR (tar_ptr, tar_ptr, codeset, &dummy);
expr_ptr++;
go_back = false;
}
else
{
const unsigned char *expr_seq_end = expr_ptr;
int cmp;
int tar_matched_size;
unsigned char *match_escape = NULL;
bool inescape = false;
bool has_last_escape = false;
/* build sequence to check (until wildcard) */
do
{
if (!inescape
&& (((!escape_is_match_many && *expr_seq_end == LIKE_WILDCARD_MATCH_MANY)
|| (!escape_is_match_one && *expr_seq_end == LIKE_WILDCARD_MATCH_ONE))))
{
break;
}
/* set escape for match: if remains NULL, we don't check for escape in matching function */
if (!inescape && escape != NULL
&& intl_cmp_char (expr_seq_end, (unsigned char *) escape, codeset, &dummy) == 0)
{
/* last escape character is not considered escape, but normal character */
if (expr_seq_end + 1 >= end_expr)
{
has_last_escape = true;
inescape = false;
}
else
{
inescape = true;
match_escape = (unsigned char *) escape;
}
}
else
{
inescape = false;
}
INTL_NEXT_CHAR (expr_seq_end, expr_seq_end, codeset, &dummy);
}
while (expr_seq_end < end_expr);
assert (end_tar - tar_ptr > 0);
assert (expr_seq_end - expr_ptr > 0);
/* match using collation */
cmp =
current_collation->strmatch (current_collation, true, tar_ptr, CAST_BUFLEN (end_tar - tar_ptr),
expr_ptr, CAST_BUFLEN (expr_seq_end - expr_ptr), match_escape,
has_last_escape, &tar_matched_size, false);
if (cmp == 0)
{
tar_ptr += tar_matched_size;
expr_ptr = expr_seq_end;
go_back = false;
}
assert (tar_ptr <= end_tar);
assert (expr_ptr <= end_expr);
}
}
if (go_back)
{
if (stackp >= 0 && stackp < STACK_SIZE)
{
tar_ptr = tarstack[stackp];
INTL_NEXT_CHAR (tar_ptr, tar_ptr, codeset, &dummy);
expr_ptr = exprstack[stackp--];
}
else if (stackp > STACK_SIZE)
{
return V_ERROR;
}
else
{
return V_FALSE;
}
}
}
else
{
const unsigned char *next_expr_ptr;
INTL_NEXT_CHAR (next_expr_ptr, expr_ptr, codeset, &dummy);
assert (status == IN_PERCENT);
if ((next_expr_ptr < end_expr) && (!escape_is_match_one || escape == NULL)
&& *next_expr_ptr == LIKE_WILDCARD_MATCH_ONE)
{
if (stackp >= STACK_SIZE - 1)
{
return V_ERROR;
}
tarstack[++stackp] = tar_ptr;
exprstack[stackp] = expr_ptr;
expr_ptr = next_expr_ptr;
INTL_NEXT_CHAR (next_expr_ptr, expr_ptr, codeset, &dummy);
if (stackp > STACK_SIZE)
{
return V_ERROR;
}
status = IN_CHECK;
continue;
}
if (next_expr_ptr == end_expr)
{
return V_TRUE;
}
if (tar_ptr < end_tar && next_expr_ptr < end_expr)
{
const unsigned char *expr_seq_end = next_expr_ptr;
int cmp;
int tar_matched_size;
unsigned char *match_escape = NULL;
bool inescape = false;
bool has_last_escape = false;
/* build sequence to check (until wildcard) */
do
{
if (!inescape
&& (((!escape_is_match_many && *expr_seq_end == LIKE_WILDCARD_MATCH_MANY)
|| (!escape_is_match_one && *expr_seq_end == LIKE_WILDCARD_MATCH_ONE))))
{
break;
}
/* set escape for match: if remains NULL, we don't check for escape in matching function */
if (!inescape && escape != NULL
&& intl_cmp_char (expr_seq_end, (unsigned char *) escape, codeset, &dummy) == 0)
{
/* last escape character is not considered escape, but normal character */
if (expr_seq_end + 1 >= end_expr)
{
has_last_escape = true;
inescape = false;
}
else
{
inescape = true;
match_escape = (unsigned char *) escape;
}
}
else
{
inescape = false;
}
INTL_NEXT_CHAR (expr_seq_end, expr_seq_end, codeset, &dummy);
}
while (expr_seq_end < end_expr);
assert (end_tar - tar_ptr > 0);
assert (expr_seq_end - next_expr_ptr > 0);
do
{
/* match using collation */
cmp =
current_collation->strmatch (current_collation, true, tar_ptr, CAST_BUFLEN (end_tar - tar_ptr),
next_expr_ptr, CAST_BUFLEN (expr_seq_end - next_expr_ptr),
match_escape, has_last_escape, &tar_matched_size, false);
if (cmp == 0)
{
if (stackp >= STACK_SIZE - 1)
{
return V_ERROR;
}
tarstack[++stackp] = tar_ptr;
tar_ptr += tar_matched_size;
exprstack[stackp] = expr_ptr;
expr_ptr = expr_seq_end;
if (stackp > STACK_SIZE)
{
return V_ERROR;
}
status = IN_CHECK;
break;
}
else
{
/* check starting from next char */
INTL_NEXT_CHAR (tar_ptr, tar_ptr, codeset, &dummy);
}
}
while (tar_ptr < end_tar);
}
}
if (tar_ptr == end_tar)
{
while (expr_ptr < end_expr && *expr_ptr == LIKE_WILDCARD_MATCH_MANY)
{
expr_ptr++;
}
if (expr_ptr == end_expr)
{
return V_TRUE;
}
else
{
return V_FALSE;
}
}
else if (tar_ptr > end_tar)
{
return V_FALSE;
}
}
}
/*
* db_string_replace () -
*/
int
db_string_replace (const DB_VALUE * src_string, const DB_VALUE * srch_string, const DB_VALUE * repl_string,
DB_VALUE * replaced_string)
{
int error_status = NO_ERROR;
unsigned char *result_ptr = NULL;
int result_length = 0, result_size = 0;
int coll_id, coll_id_tmp;
DB_VALUE dummy_string;
int is_repl_string_omitted = false;
const unsigned char *repl_string_ptr = NULL;
int repl_string_size = 0;
assert (src_string != (DB_VALUE *) NULL);
assert (replaced_string != (DB_VALUE *) NULL);
db_make_null (&dummy_string);
if (repl_string == NULL)
{
is_repl_string_omitted = true;
}
if (DB_IS_NULL (src_string) || DB_IS_NULL (srch_string) || (!is_repl_string_omitted && DB_IS_NULL (repl_string)))
{
if (prm_get_bool_value (PRM_ID_ORACLE_STYLE_EMPTY_STRING) == true && !DB_IS_NULL (src_string)
&& is_char_string (src_string))
/* srch_string or repl_string is null */
{
error_status =
db_string_make_empty_typed_string (&dummy_string, DB_VALUE_DOMAIN_TYPE (src_string),
TP_FLOATING_PRECISION_VALUE, db_get_string_codeset (src_string),
db_get_string_collation (src_string));
if (error_status != NO_ERROR)
{
goto exit;
}
if (DB_IS_NULL (srch_string))
{
srch_string = &dummy_string;
}
if (!is_repl_string_omitted && DB_IS_NULL (repl_string))
{
repl_string = &dummy_string;
}
}
else
{
error_status =
db_value_domain_init (replaced_string, DB_TYPE_VARCHAR, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
goto exit;
}
}
if (!is_char_string (srch_string) || (!is_repl_string_omitted && !is_char_string (repl_string))
|| !is_char_string (src_string))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INVALID_DATA_TYPE, 0);
goto exit;
}
if ((qstr_get_category (src_string) != qstr_get_category (srch_string))
|| (!is_repl_string_omitted && (qstr_get_category (src_string) != qstr_get_category (repl_string)))
|| (!is_repl_string_omitted && (qstr_get_category (srch_string) != qstr_get_category (repl_string)))
|| ((db_get_string_codeset (src_string) != db_get_string_codeset (srch_string)))
|| (!is_repl_string_omitted && (db_get_string_codeset (src_string) != db_get_string_codeset (repl_string))))
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INCOMPATIBLE_CODE_SETS, 0);
goto exit;
}
LANG_RT_COMMON_COLL (db_get_string_collation (src_string), db_get_string_collation (srch_string), coll_id_tmp);
if (coll_id_tmp == -1)
{
error_status = ER_QSTR_INCOMPATIBLE_COLLATIONS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (!is_repl_string_omitted)
{
LANG_RT_COMMON_COLL (coll_id_tmp, db_get_string_collation (repl_string), coll_id);
if (coll_id == -1)
{
error_status = ER_QSTR_INCOMPATIBLE_COLLATIONS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
}
else
{
coll_id = coll_id_tmp;
}
if (!is_repl_string_omitted)
{
repl_string_ptr = DB_GET_UCHAR (repl_string);
repl_string_size = db_get_string_size (repl_string);
}
error_status = qstr_replace (DB_GET_UCHAR (src_string), db_get_string_length (src_string),
db_get_string_size (src_string), db_get_string_codeset (src_string), coll_id,
DB_GET_UCHAR (srch_string), db_get_string_size (srch_string), repl_string_ptr,
repl_string_size, &result_ptr, &result_length, &result_size);
if (error_status == NO_ERROR && result_ptr != NULL)
{
if (result_length == 0)
{
qstr_make_typed_string (DB_TYPE_VARCHAR, replaced_string,
(db_get_string_length (src_string) == 0) ? 1 : db_get_string_length (src_string),
(char *) result_ptr, result_size, db_get_string_codeset (src_string), coll_id);
}
else
{
qstr_make_typed_string (DB_TYPE_VARCHAR, replaced_string, result_length, (char *) result_ptr, result_size,
db_get_string_codeset (src_string), coll_id);
}
result_ptr[result_size] = 0;
replaced_string->need_clear = true;
}
exit:
pr_clear_value (&dummy_string);
return error_status;
}
/* qstr_replace () -
*/
static int
qstr_replace (const unsigned char *src_buf, int src_len, int src_size, INTL_CODESET codeset, int coll_id,
const unsigned char *srch_str_buf, int srch_str_size, const unsigned char *repl_str_buf,
int repl_str_size, unsigned char **result_buf, int *result_len, int *result_size)
{
#define REPL_POS_ARRAY_EXTENT 32
int error_status = NO_ERROR;
int char_size, i;
const unsigned char *matched_ptr, *matched_ptr_end;
unsigned char *target;
int *repl_pos_array = NULL;
int repl_pos_array_size;
int repl_pos_array_cnt;
const unsigned char *src_ptr;
int repl_str_len;
assert (result_buf != NULL);
*result_buf = NULL;
/*
* if search string is NULL or is longer than source string
* copy source string as a result
*/
if (srch_str_buf == NULL || src_size < srch_str_size)
{
*result_buf = (unsigned char *) db_private_alloc (NULL, (size_t) src_size + 1);
if (*result_buf == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 1, (size_t) src_size);
goto exit;
}
(void) memcpy ((char *) (*result_buf), (char *) src_buf, src_size);
*result_len = src_len;
*result_size = src_size;
goto exit;
}
if (repl_str_buf == NULL)
{
repl_str_buf = (unsigned char *) "";
}
repl_pos_array_size = REPL_POS_ARRAY_EXTENT;
repl_pos_array = (int *) db_private_alloc (NULL, 2 * sizeof (int) * repl_pos_array_size);
if (repl_pos_array == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 1, 2 * sizeof (int) * repl_pos_array_size);
goto exit;
}
intl_char_count (repl_str_buf, repl_str_size, codeset, &repl_str_len);
repl_pos_array_cnt = 0;
for (*result_size = 0, *result_len = 0, src_ptr = src_buf;
src_size > 0 && srch_str_size > 0 && src_ptr < src_buf + src_size;)
{
int matched_size;
if (QSTR_MATCH (coll_id, src_ptr, CAST_BUFLEN (src_buf - src_ptr) + src_size, srch_str_buf, srch_str_size, NULL,
false, &matched_size) == 0)
{
/* store byte position and size of matched string */
if (repl_pos_array_cnt >= repl_pos_array_size)
{
repl_pos_array_size += REPL_POS_ARRAY_EXTENT;
repl_pos_array =
(int *) db_private_realloc (NULL, repl_pos_array, 2 * sizeof (int) * repl_pos_array_size);
if (repl_pos_array == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 1, 2 * sizeof (int) * repl_pos_array_size);
goto exit;
}
}
repl_pos_array[repl_pos_array_cnt * 2] = CAST_BUFLEN (src_ptr - src_buf);
repl_pos_array[repl_pos_array_cnt * 2 + 1] = matched_size;
src_ptr += matched_size;
repl_pos_array_cnt++;
*result_size += repl_str_size;
*result_len += repl_str_len;
}
else
{
INTL_NEXT_CHAR (src_ptr, src_ptr, codeset, &char_size);
*result_size += char_size;
*result_len += 1;
}
}
if (repl_pos_array_cnt == 0)
{
*result_size = src_size;
}
*result_buf = (unsigned char *) db_private_alloc (NULL, (size_t) (*result_size) + 1);
if (*result_buf == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 1, (size_t) (*result_size + 1));
goto exit;
}
matched_ptr = matched_ptr_end = src_buf;
target = *result_buf;
for (i = 0; i < repl_pos_array_cnt; i++)
{
/* first, copy non matched original string preceeding matched part */
matched_ptr = src_buf + repl_pos_array[2 * i];
if ((matched_ptr - matched_ptr_end) > 0)
{
(void) memcpy (target, matched_ptr_end, matched_ptr - matched_ptr_end);
target += matched_ptr - matched_ptr_end;
}
/* second, copy replacing string */
(void) memcpy (target, repl_str_buf, repl_str_size);
target += repl_str_size;
matched_ptr_end = matched_ptr + repl_pos_array[2 * i + 1];
}
/* append any trailing string (after last matched part) */
if (matched_ptr_end < src_buf + src_size)
{
(void) memcpy (target, matched_ptr_end, src_buf + src_size - matched_ptr_end);
target += src_buf + src_size - matched_ptr_end;
}
assert (target - *result_buf == *result_size);
exit:
if (repl_pos_array != NULL)
{
db_private_free (NULL, repl_pos_array);
}
return error_status;
#undef REPL_POS_ARRAY_EXTENT
}
/*
* db_string_translate () -
*/
int
db_string_translate (const DB_VALUE * src_string, const DB_VALUE * from_string, const DB_VALUE * to_string,
DB_VALUE * transed_string)
{
int error_status = NO_ERROR;
unsigned char *result_ptr = NULL;
int result_length = 0, result_size = 0;
DB_TYPE result_type = DB_TYPE_NULL;
assert (src_string != (DB_VALUE *) NULL);
assert (transed_string != (DB_VALUE *) NULL);
if (DB_IS_NULL (src_string) || DB_IS_NULL (from_string) || DB_IS_NULL (to_string))
{
error_status = db_value_domain_init (transed_string, DB_TYPE_VARCHAR, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
return error_status;
}
if (!is_char_string (from_string) || !is_char_string (to_string) || !is_char_string (src_string))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
if ((qstr_get_category (src_string) != qstr_get_category (from_string))
|| (qstr_get_category (src_string) != qstr_get_category (to_string))
|| (qstr_get_category (from_string) != qstr_get_category (to_string))
|| (db_get_string_codeset (src_string) != db_get_string_codeset (from_string))
|| (db_get_string_codeset (src_string) != db_get_string_codeset (to_string)))
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
error_status = qstr_translate (DB_GET_UCHAR (src_string), DB_VALUE_DOMAIN_TYPE (src_string),
db_get_string_size (src_string), db_get_string_codeset (src_string),
DB_GET_UCHAR (from_string), db_get_string_size (from_string),
DB_GET_UCHAR (to_string), db_get_string_size (to_string), &result_ptr,
&result_length, &result_size);
if (error_status == NO_ERROR && result_ptr != NULL)
{
if (result_length == 0)
{
qstr_make_typed_string (DB_TYPE_VARCHAR, transed_string,
(db_get_string_length (src_string) == 0) ? 1 : db_get_string_length (src_string),
(char *) result_ptr, result_size, db_get_string_codeset (src_string),
db_get_string_collation (src_string));
}
else
{
qstr_make_typed_string (DB_TYPE_VARCHAR, transed_string, result_length, (char *) result_ptr, result_size,
db_get_string_codeset (src_string), db_get_string_collation (src_string));
}
result_ptr[result_size] = 0;
transed_string->need_clear = true;
}
return error_status;
}
/*
* qstr_translate () -
*/
static int
qstr_translate (const unsigned char *src_ptr, DB_TYPE src_type, int src_size, INTL_CODESET codeset,
const unsigned char *from_str_ptr, int from_str_size, const unsigned char *to_str_ptr, int to_str_size,
unsigned char **result_ptr, int *result_len, int *result_size)
{
int error_status = NO_ERROR;
int j, offset, offset1, offset2;
int from_char_loc, to_char_cnt, to_char_loc;
const unsigned char *srcp, *fromp;
unsigned char *target = NULL;
int matched = 0, phase = 0;
if ((from_str_ptr == NULL && to_str_ptr != NULL))
{
error_status = ER_QPROC_INVALID_PARAMETER;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
if (to_str_ptr == NULL)
{
to_str_ptr = (unsigned char *) "";
}
/* check from, to string */
to_char_cnt = 0;
for (j = 0; j < to_str_size;)
{
intl_char_size (to_str_ptr + j, 1, codeset, &offset2);
j += offset2;
to_char_cnt++;
}
/* calculate total length */
*result_size = 0;
phase = 0;
loop:
srcp = src_ptr;
for (srcp = src_ptr; srcp < src_ptr + src_size;)
{
intl_char_size (srcp, 1, codeset, &offset);
matched = 0;
from_char_loc = 0;
for (fromp = from_str_ptr; fromp != NULL && fromp < from_str_ptr + from_str_size; from_char_loc++)
{
intl_char_size (fromp, 1, codeset, &offset1);
/* if source and from char are matched, translate */
if ((offset == offset1) && (memcmp (srcp, fromp, offset) == 0))
{
matched = 1;
to_char_loc = 0;
for (j = 0; j < to_str_size;)
{
intl_char_size (to_str_ptr + j, 1, codeset, &offset2);
if (to_char_loc == from_char_loc)
{ /* if matched char exist, replace */
if (phase == 0)
{
*result_size += offset2;
}
else
{
memcpy (target, to_str_ptr + j, offset2);
target += offset2;
}
break;
}
j += offset2;
to_char_loc++;
}
break;
}
fromp += offset1;
}
if (!matched)
{ /* preserve source char */
if (phase == 0)
{
*result_size += offset;
}
else
{
memcpy (target, srcp, offset);
target += offset;
}
}
srcp += offset;
}
if (phase == 1)
{
return error_status;
}
/* evaluate result string length */
*result_ptr = (unsigned char *) db_private_alloc (NULL, (size_t) * result_size + 1);
if (*result_ptr == NULL)
{
assert (er_errid () != NO_ERROR);
error_status = er_errid ();
return error_status;
}
if (phase == 0)
{
phase = 1;
target = *result_ptr;
*result_len = *result_size;
goto loop;
}
return error_status;
}
/*
* db_bit_string_coerce () -
*
* Arguments:
* src_string: (In) Source string
* dest_string: (Out) Coerced string
* data_status: (Out) Data status
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE
* <src_string> is not a bit string
* ER_QSTR_INCOMPATIBLE_CODE_SETS
* <dest_domain> is not a compatible domain type
*
* Note:
*
* This function coerces a bit string from one domain to another.
* A new DB_VALUE is created making use of the memory manager and
* domain information stored in <dest_value>, and coercing the
* data portion of <src_string>.
*
* If any loss of data due to truncation occurs, <data_status>
* is set to DATA_STATUS_TRUNCATED.
*
* The destination container should have the memory manager, precision
* and domain type initialized.
*
* Assert:
*
* 1. src_string != (DB_VALUE *) NULL
* 2. dest_value != (DB_VALUE *) NULL
* 3. data_status != (DB_DATA_STATUS *) NULL
*
*/
int
db_bit_string_coerce (const DB_VALUE * src_string, DB_VALUE * dest_string, DB_DATA_STATUS * data_status)
{
DB_TYPE src_type, dest_type;
int error_status = NO_ERROR;
/* Assert that DB_VALUE structures have been allocated. */
assert (src_string != (DB_VALUE *) NULL);
assert (dest_string != (DB_VALUE *) NULL);
assert (data_status != (DB_DATA_STATUS *) NULL);
/* Initialize status value */
*data_status = DATA_STATUS_OK;
/* Categorize the two input parameters and check for errors. Verify that the parameters are both character strings. */
src_type = DB_VALUE_DOMAIN_TYPE (src_string);
dest_type = DB_VALUE_DOMAIN_TYPE (dest_string);
if (!QSTR_IS_BIT (src_type) || !QSTR_IS_BIT (dest_type))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
else if (qstr_get_category (src_string) != qstr_get_category (dest_string))
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
else if (DB_IS_NULL (src_string))
{
db_value_domain_init (dest_string, DB_VALUE_DOMAIN_TYPE (dest_string), DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
}
else
{
unsigned char *dest;
int dest_prec;
int dest_length;
if (DB_VALUE_PRECISION (dest_string) == TP_FLOATING_PRECISION_VALUE)
{
dest_prec = db_get_string_length (src_string);
}
else
{
dest_prec = DB_VALUE_PRECISION (dest_string);
}
error_status = qstr_bit_coerce (DB_GET_UCHAR (src_string), db_get_string_length (src_string),
QSTR_VALUE_PRECISION (src_string), src_type, &dest, &dest_length, dest_prec,
dest_type, data_status);
if (error_status == NO_ERROR)
{
qstr_make_typed_string (dest_type, dest_string, DB_VALUE_PRECISION (dest_string), (char *) dest, dest_length,
INTL_CODESET_RAW_BITS, 0);
dest_string->need_clear = true;
}
}
return error_status;
}
/*
* db_char_string_coerce () -
*
* Arguments:
* src_string: (In) Source string
* dest_string: (Out) Coerced string
* data_status: (Out) Data status
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE
* <src_string> and <dest_string> are not both char strings
* ER_QSTR_INCOMPATIBLE_CODE_SETS
* <dest_domain> is not a compatible domain type
*
* Note:
*
* This function coerces a char string from one domain to
* another. A new DB_VALUE is created making use of the
* memory manager and domain information stored in
* <dest_value>, and coercing the data portion of
* <src_string>.
*
* If any loss of data due to truncation occurs, <data_status>
* is set to DATA_STATUS_TRUNCATED.
*
* Assert:
*
* 1. src_string != (DB_VALUE *) NULL
* 2. dest_value != (DB_VALUE *) NULL
* 3. data_status != (DB_DATA_STATUS *) NULL
*
*/
int
db_char_string_coerce (const DB_VALUE * src_string, DB_VALUE * dest_string, DB_DATA_STATUS * data_status)
{
int error_status = NO_ERROR;
/* Assert that DB_VALUE structures have been allocated. */
assert (src_string != (DB_VALUE *) NULL);
assert (dest_string != (DB_VALUE *) NULL);
assert (data_status != (DB_DATA_STATUS *) NULL);
/* Initialize status value */
*data_status = DATA_STATUS_OK;
/*
* Categorize the two input parameters and check for errors.
* Verify that the parameters are both character strings.
* Verify that the source and destination strings are of
* the same character code set.
* Verify that the source string is not NULL.
* Otherwise, coerce.
*/
if (!is_char_string (src_string) || !is_char_string (dest_string))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
else if (qstr_get_category (src_string) != qstr_get_category (dest_string))
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
else if (DB_IS_NULL (src_string))
{
db_value_domain_init (dest_string, DB_VALUE_DOMAIN_TYPE (dest_string), DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
}
else
{
unsigned char *dest;
int dest_prec;
int dest_length;
int dest_size;
INTL_CODESET src_codeset = db_get_string_codeset (src_string);
INTL_CODESET dest_codeset = db_get_string_codeset (dest_string);
if (!INTL_CAN_COERCE_CS (src_codeset, dest_codeset))
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
/* Initialize the memory manager of the destination */
if (DB_VALUE_PRECISION (dest_string) == TP_FLOATING_PRECISION_VALUE)
{
dest_prec = db_get_string_length (src_string);
}
else
{
dest_prec = DB_VALUE_PRECISION (dest_string);
}
error_status = qstr_coerce (DB_GET_UCHAR (src_string), db_get_string_length (src_string),
QSTR_VALUE_PRECISION (src_string), DB_VALUE_DOMAIN_TYPE (src_string), src_codeset,
dest_codeset, &dest, &dest_length, &dest_size, dest_prec,
DB_VALUE_DOMAIN_TYPE (dest_string), data_status);
if (error_status == NO_ERROR && dest != NULL)
{
qstr_make_typed_string (DB_VALUE_DOMAIN_TYPE (dest_string), dest_string, DB_VALUE_PRECISION (dest_string),
(char *) dest, dest_size, db_get_string_codeset (dest_string),
db_get_string_collation (dest_string));
dest[dest_size] = 0;
dest_string->need_clear = true;
}
}
return error_status;
}
/*
* db_string_make_empty_typed_string() -
*
* Arguments:
* db_val : (In/Out) value to make
* db_type : (In) Type of string (char,bit)
* precision : (In)
* codeset : (In)
* collation_id : (In)
*
* Returns: int
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE
* <type> is not one of (char,bit)
* ER_OUT_OF_VIRTUAL_MEMORY
* out of memory
*
*/
int
db_string_make_empty_typed_string (DB_VALUE * db_val, const DB_TYPE db_type, int precision, int codeset,
int collation_id)
{
int status = NO_ERROR;
char *buf = NULL;
/* handle bad cases */
assert (db_val != NULL);
assert (precision >= DB_DEFAULT_PRECISION);
if (db_type != DB_TYPE_BIT && db_type != DB_TYPE_VARBIT && db_type != DB_TYPE_CHAR && db_type != DB_TYPE_VARCHAR)
{
return ER_QSTR_INVALID_DATA_TYPE;
}
if (db_val == NULL)
{
return ER_QSTR_INVALID_DATA_TYPE;
}
if (DB_IS_NULL (db_val))
{
db_value_domain_init (db_val, db_type, precision, 0);
}
precision = ((precision < DB_DEFAULT_PRECISION) ? DB_DEFAULT_PRECISION : precision);
/* create an empty string DB VALUE */
buf = (char *) db_private_alloc (NULL, 1);
if (buf == NULL)
{
return ER_OUT_OF_VIRTUAL_MEMORY;
}
*buf = '\0';
/* We are sure it's a string type value. */
db_make_db_char (db_val, (INTL_CODESET) (codeset), collation_id, buf, 0);
db_val->need_clear = true;
return status;
}
/*
* db_find_string_in_in_set () - find the position of a string token in
* a string containing comma separated tokens
* return : error code or NO_ERROR
* needle (in) : the token to look for
* stack (in) : the set of tokens
* result (in/out) : will hold the position of the token
*/
int
db_find_string_in_in_set (const DB_VALUE * needle, const DB_VALUE * stack, DB_VALUE * result)
{
int err = NO_ERROR;
DB_TYPE needle_type, stack_type;
int position = 1;
int stack_size = 0, needle_size = 0;
const char *stack_str = NULL;
const char *needle_str = NULL;
int cmp, coll_id, matched_stack_size;
const char *stack_ptr, *stack_end, *elem_start;
if (DB_IS_NULL (needle) || DB_IS_NULL (stack))
{
db_make_null (result);
return NO_ERROR;
}
/*
* Categorize the parameters into respective code sets.
* Verify that the parameters are both character strings.
* Verify that the input strings belong to compatible code sets.
*/
needle_type = DB_VALUE_DOMAIN_TYPE (needle);
stack_type = DB_VALUE_DOMAIN_TYPE (stack);
if (!QSTR_IS_ANY_CHAR_OR_BIT (needle_type) || !QSTR_IS_ANY_CHAR_OR_BIT (stack_type))
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INVALID_DATA_TYPE, 0);
err = ER_QSTR_INVALID_DATA_TYPE;
goto error_return;
}
if (qstr_get_category (needle) != qstr_get_category (stack)
|| db_get_string_codeset (needle) != db_get_string_codeset (stack))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INCOMPATIBLE_CODE_SETS, 0);
err = ER_QSTR_INCOMPATIBLE_CODE_SETS;
goto error_return;
}
LANG_RT_COMMON_COLL (db_get_string_collation (stack), db_get_string_collation (needle), coll_id);
if (coll_id == -1)
{
err = ER_QSTR_INCOMPATIBLE_COLLATIONS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err, 0);
goto error_return;
}
stack_str = db_get_string (stack);
stack_size = db_get_string_size (stack);
needle_str = db_get_string (needle);
needle_size = db_get_string_size (needle);
if (stack_size == 0 && needle_size == 0)
{
/* if both are empty string, no match */
goto match_not_found;
}
for (elem_start = stack_ptr = stack_str, stack_end = stack_str + stack_size; stack_ptr <= stack_end; ++stack_ptr)
{
if (stack_ptr == stack_end || *stack_ptr == ',')
{
if (stack_ptr == elem_start)
{
if (needle_size == 0)
{
db_make_int (result, position);
return NO_ERROR;
}
}
else
{
assert (stack_ptr > elem_start);
/* check using collation */
if (needle_size > 0)
{
cmp = QSTR_MATCH (coll_id, (const unsigned char *) elem_start, CAST_BUFLEN (stack_ptr - elem_start),
(const unsigned char *) needle_str, needle_size, NULL, false, &matched_stack_size);
if (cmp == 0 && matched_stack_size == CAST_BUFLEN (stack_ptr - elem_start))
{
db_make_int (result, position);
return NO_ERROR;
}
}
}
position++;
elem_start = stack_ptr + 1;
}
}
match_not_found:
/* if we didn't find it in the loop above, then there is no match */
db_make_int (result, 0);
return NO_ERROR;
error_return:
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_clear ();
return NO_ERROR;
}
return err;
}
/*
* db_bigint_to_binary_string () - compute the string representation of a
* binary a value
* return : error code or NO_ERROR
* src_bigint (in) : the binary value
* result (out) : the string representation of the binary value
*/
int
db_bigint_to_binary_string (const DB_VALUE * src_bigint, DB_VALUE * result)
{
int error = NO_ERROR;
int i = 0;
DB_BIGINT bigint_val = 0;
int digits_count = 0;
char *binary_form = NULL;
if (DB_IS_NULL (src_bigint))
{
db_make_null (result);
return NO_ERROR;
}
if (DB_VALUE_TYPE (src_bigint) != DB_TYPE_BIGINT)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INVALID_DATA_TYPE, 0);
error = ER_QSTR_INVALID_DATA_TYPE;
goto error_return;
}
bigint_val = db_get_bigint (src_bigint);
/* count the number of digits in bigint_val */
if (bigint_val < (DB_BIGINT) 0)
{
/* MSB is the sign bit */
digits_count = sizeof (DB_BIGINT) * 8;
}
else if (bigint_val == 0)
{
digits_count = 1;
}
else
{
i = 0;
/* positive numbers have at most 8 * sizeof(DB_BIGINT) - 1 digits */
while ((DB_BIGINT) 1 << i <= bigint_val && i < (int) sizeof (DB_BIGINT) * 8 - 1)
{
i++;
}
digits_count = i;
}
binary_form = (char *) db_private_alloc (NULL, digits_count + 1);
if (binary_form == NULL)
{
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto error_return;
}
memset (binary_form, 0, digits_count + 1);
for (i = 0; i < digits_count; i++)
{
binary_form[digits_count - i - 1] = ((DB_BIGINT) 1 << i) & bigint_val ? '1' : '0';
}
db_make_varchar (result, digits_count, binary_form, digits_count, LANG_COERCIBLE_CODESET, LANG_COERCIBLE_COLL);
result->need_clear = true;
return error;
error_return:
if (binary_form != NULL)
{
db_private_free (NULL, binary_form);
}
pr_clear_value (result);
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_clear ();
return NO_ERROR;
}
return error;
}
/*
* db_add_time () - add the time represented by right to the value left
* return : error code or NO_ERROR
* left (in) : left operand
* right (in) : right operand
* result (out) : result
* domain (in) : the domain of the return type
*/
int
db_add_time (const DB_VALUE * left, const DB_VALUE * right, DB_VALUE * result, const TP_DOMAIN * domain)
{
int error = NO_ERROR;
DB_DATETIME ldatetime = { 0, 0 };
DB_DATETIME result_datetime = { 0, 0 };
bool left_is_datetime = false;
int month = 0, day = 0, year = 0;
int lsecond = 0, lminute = 0, lhour = 0, lms = 0;
bool is_time_decoded = false;
bool is_datetime_decoded = false;
int rsecond = 0, rminute = 0, rhour = 0, rms = 0;
DB_TIME ltime = 0, rtime = 0;
char *res_s = NULL;
DB_TYPE result_type = DB_TYPE_NULL;
INTL_CODESET codeset;
int collation_id;
TZ_ID tz_id = 0;
DB_DATETIMETZ ldatetimetz;
if (DB_IS_NULL (left) || DB_IS_NULL (right))
{
db_make_null (result);
return NO_ERROR;
}
switch (DB_VALUE_TYPE (left))
{
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
{
bool has_zone = false;
bool is_explicit_time = false;
error = db_string_to_datetimetz_ex (db_get_string (left), db_get_string_size (left), &ldatetimetz, &has_zone);
if (error == NO_ERROR && has_zone == true)
{
tz_id = ldatetimetz.tz_id;
ldatetime = ldatetimetz.datetime;
result_type = DB_TYPE_DATETIMETZ;
left_is_datetime = true;
break;
}
error = db_date_parse_time (db_get_string (left), db_get_string_size (left), <ime, &lms);
if (error != NO_ERROR)
{
/* left may be a date string, try it here */
error =
db_date_parse_datetime_parts (db_get_string (left), db_get_string_size (left), &ldatetime,
&is_explicit_time, NULL, NULL, NULL);
if (error != NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIME_CONVERSION, 0);
error = ER_TIME_CONVERSION;
goto error_return;
}
left_is_datetime = true;
if (has_zone == true)
{
result_type = DB_TYPE_DATETIMETZ;
}
else
{
result_type = DB_TYPE_VARCHAR;
}
break;
}
db_time_decode (<ime, &lhour, &lminute, &lsecond);
is_time_decoded = true;
error =
db_date_parse_datetime_parts (db_get_string (left), db_get_string_size (left), &ldatetime, &is_explicit_time,
NULL, NULL, NULL);
if (error != NO_ERROR || !is_explicit_time)
{
left_is_datetime = false;
}
else
{
int lsecond2 = 0, lminute2 = 0, lhour2 = 0, lms2 = 0;
left_is_datetime = true;
db_datetime_decode (&ldatetime, &month, &day, &year, &lhour2, &lminute2, &lsecond2, &lms2);
is_datetime_decoded = true;
if (lhour2 != lhour || lminute2 != lminute || lsecond2 != lsecond || lms2 != lms)
{
month = 0;
day = 0;
year = 0;
left_is_datetime = false;
}
}
result_type = DB_TYPE_VARCHAR;
break;
}
case DB_TYPE_DATETIME:
case DB_TYPE_DATETIMELTZ:
ldatetime = *(db_get_datetime (left));
left_is_datetime = true;
if (DB_VALUE_TYPE (left) == DB_TYPE_DATETIME)
{
result_type = DB_TYPE_DATETIME;
}
else
{
result_type = DB_TYPE_DATETIMELTZ;
}
break;
case DB_TYPE_DATETIMETZ:
{
DB_DATETIMETZ *dt_tz_p;
dt_tz_p = db_get_datetimetz (left);
ldatetime = dt_tz_p->datetime;
tz_id = dt_tz_p->tz_id;
left_is_datetime = true;
result_type = DB_TYPE_DATETIMETZ;
break;
}
case DB_TYPE_TIMESTAMP:
case DB_TYPE_TIMESTAMPLTZ:
db_timestamp_decode_utc (db_get_timestamp (left), &ldatetime.date, <ime);
ldatetime.time = ltime * 1000;
left_is_datetime = true;
if (DB_VALUE_TYPE (left) == DB_TYPE_TIMESTAMP)
{
result_type = DB_TYPE_DATETIME;
}
else
{
result_type = DB_TYPE_DATETIMELTZ;
}
break;
case DB_TYPE_TIMESTAMPTZ:
{
DB_TIMESTAMPTZ *ts_tz_p;
ts_tz_p = db_get_timestamptz (left);
db_timestamp_decode_utc (&ts_tz_p->timestamp, &ldatetime.date, <ime);
ldatetime.time = ltime * 1000;
left_is_datetime = true;
tz_id = ts_tz_p->tz_id;
result_type = DB_TYPE_DATETIMETZ;
break;
}
case DB_TYPE_DATE:
ldatetime.date = *(db_get_date (left));
left_is_datetime = true;
result_type = DB_TYPE_DATETIME;
break;
case DB_TYPE_TIME:
ltime = *(db_get_time (left));
left_is_datetime = false;
result_type = DB_TYPE_TIME;
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INVALID_DATA_TYPE, 0);
error = ER_QSTR_INVALID_DATA_TYPE;
goto error_return;
break;
}
if (db_get_time_from_dbvalue (right, &rhour, &rminute, &rsecond, &rms) != NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INVALID_DATA_TYPE, 0);
error = ER_QSTR_INVALID_DATA_TYPE;
goto error_return;
}
if (left_is_datetime)
{
DB_BIGINT composite_values[COMPOSITE_MAX] = { 0, 0, 0, rhour, rminute, rsecond, 0 };
/* add a datetime to a time */
if (!is_datetime_decoded)
{
db_datetime_decode (&ldatetime, &month, &day, &year, &lhour, &lminute, &lsecond, &lms);
}
if (month == 0 && day == 0 && year == 0 && lhour == 0 && lminute == 0 && lsecond == 0 && lms == 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ATTEMPT_TO_USE_ZERODATE, 0);
error = ER_ATTEMPT_TO_USE_ZERODATE;
goto error_return;
}
error = add_and_normalize_date_time (&year, &month, &day, &lhour, &lminute, &lsecond, &lms, composite_values);
if (error != NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
error = ER_DATE_CONVERSION;
goto error_return;
}
if (result_type != DB_TYPE_DATETIMETZ)
{
db_datetime_encode (&result_datetime, month, day, year, lhour, lminute, lsecond, lms);
}
}
else
{
/* add two time values */
int seconds = 0;
if (!is_time_decoded)
{
db_time_decode (<ime, &lhour, &lminute, &lsecond);
}
seconds = (lhour + rhour) * 3600 + (lminute + rminute) * 60 + lsecond + rsecond;
rhour = seconds / 3600;
if (rhour > 23)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIME_CONVERSION, 0);
error = ER_TIME_CONVERSION;
goto error_return;
}
rminute = (seconds - rhour * 3600) / 60;
rsecond = seconds % 60;
}
/* depending on the first argument, the result is either result_date or result_time */
if (domain != NULL)
{
assert (TP_DOMAIN_TYPE (domain) == result_type);
}
switch (result_type)
{
case DB_TYPE_DATETIME:
if (!left_is_datetime)
{
/* the result type can be DATETIME only if the first argument is a DATE or a DATETIME */
assert (false);
db_make_null (result);
}
if (DB_VALUE_TYPE (left) == DB_TYPE_TIMESTAMP)
{
DB_DATETIME dt_local;
error = tz_datetimeltz_to_local (&result_datetime, &dt_local);
if (error != NO_ERROR)
{
goto error_return;
}
result_datetime = dt_local;
}
db_make_datetime (result, &result_datetime);
break;
case DB_TYPE_DATETIMELTZ:
{
if (!left_is_datetime)
{
/* the result type can be DATETIME only if the first argument is a DATE or a DATETIME */
assert (false);
db_make_null (result);
}
db_make_datetimeltz (result, &result_datetime);
break;
}
case DB_TYPE_DATETIMETZ:
{
DB_DATETIMETZ dt_tz;
if (!left_is_datetime)
{
/* the result type can be DATETIME only if the first argument is a DATE or a DATETIME */
assert (false);
db_make_null (result);
}
error = tz_create_datetimetz_from_parts (month, day, year, lhour, lminute, lsecond, lms, &tz_id, &dt_tz);
if (error != NO_ERROR)
{
goto error_return;
}
db_make_datetimetz (result, &dt_tz);
break;
}
case DB_TYPE_TIME:
if (left_is_datetime)
{
/* the result type can be DATETIME only if the first argument is a TIME */
assert (false);
db_make_null (result);
}
db_make_time (result, rhour, rminute, rsecond);
break;
case DB_TYPE_VARCHAR:
codeset = TP_DOMAIN_CODESET (domain);
collation_id = TP_DOMAIN_COLLATION (domain);
if (left_is_datetime)
{
res_s = (char *) db_private_alloc (NULL, QSTR_DATETIME_LENGTH + 1);
if (res_s == NULL)
{
error = ER_DATE_CONVERSION;
goto error_return;
}
db_datetime_to_string (res_s, QSTR_DATETIME_LENGTH + 1, &result_datetime);
db_make_varchar (result, strlen (res_s), res_s, strlen (res_s), codeset, collation_id);
}
else
{
res_s = (char *) db_private_alloc (NULL, QSTR_TIME_LENGTH + 1);
if (res_s == NULL)
{
error = ER_TIME_CONVERSION;
goto error_return;
}
db_time_encode (&rtime, rhour, rminute, rsecond);
db_time_to_string (res_s, QSTR_TIME_LENGTH + 1, &rtime);
db_make_varchar (result, strlen (res_s), res_s, strlen (res_s), codeset, collation_id);
}
result->need_clear = true;
break;
default:
assert (false);
db_make_null (result);
break;
}
return NO_ERROR;
error_return:
if (res_s != NULL)
{
db_private_free (NULL, res_s);
}
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
/* clear error and return NULL */
er_clear ();
return NO_ERROR;
}
return error;
}
int
db_json_convert_to_utf8 (DB_VALUE * dbval)
{
assert (dbval != NULL && DB_IS_STRING (dbval));
DB_VALUE coerced_str;
if (db_get_string_codeset (dbval) == INTL_CODESET_UTF8)
{
return NO_ERROR;
}
int error_code = db_string_convert_to (dbval, &coerced_str, INTL_CODESET_UTF8, LANG_COLL_UTF8_BINARY);
if (error_code != NO_ERROR)
{
return error_code;
}
std::swap (coerced_str, *dbval);
pr_clear_value (&coerced_str);
return NO_ERROR;
}
int
db_json_copy_and_convert_to_utf8 (const DB_VALUE * src_dbval, DB_VALUE * dest_dbval, const DB_VALUE ** json_str_dbval)
{
assert (src_dbval != NULL && dest_dbval != NULL && json_str_dbval != NULL);
if (db_get_string_codeset (src_dbval) == INTL_CODESET_UTF8)
{
*json_str_dbval = src_dbval;
db_make_null (dest_dbval);
}
else
{
*json_str_dbval = dest_dbval;
int error_code = db_string_convert_to (src_dbval, dest_dbval, INTL_CODESET_UTF8, LANG_COLL_UTF8_BINARY);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
}
return NO_ERROR;
}
int
db_string_convert_to (const DB_VALUE * src_str_dbval, DB_VALUE * dest_str_dbval, INTL_CODESET dest_codeset,
int dest_col)
{
assert (src_str_dbval != NULL && dest_str_dbval != NULL);
DB_TYPE src_str_type = (DB_TYPE) src_str_dbval->domain.general_info.type;
int dest_precision = QSTR_VALUE_PRECISION (src_str_dbval);
db_value_domain_init (dest_str_dbval, src_str_type, dest_precision, 0);
db_string_put_cs_and_collation (dest_str_dbval, dest_codeset, dest_col);
DB_DATA_STATUS data_status;
int error_code = db_char_string_coerce (src_str_dbval, dest_str_dbval, &data_status);
if (error_code != NO_ERROR)
{
pr_clear_value (dest_str_dbval);
ASSERT_ERROR ();
return error_code;
}
assert (data_status == DATA_STATUS_OK);
return NO_ERROR;
}
/*
* qstr_pad_string () -
*
* Arguments:
* s: (IN OUT) Pointer to input string.
* length: (IN) Size of input string.
* codeset: (IN) International codeset of input string.
*
* Returns: unsigned char
*
* Errors:
*
* Note:
* This is a convenience function which will copy pad characters into
* the input string. It is assumed that the pad character will consist
* of one or two bytes (this is currently true).
*
* The address immediately after the padded string is returned. Thus,
* If a NULL terminated string was desired, then a call could be made:
*
* ptr = qstr_pad_string();
* *ptr = '\0';
*
*/
unsigned char *
qstr_pad_string (unsigned char *s, int length, INTL_CODESET codeset)
{
unsigned char pad[2];
int i, j, pad_size = 0;
if (length == 0)
{
return s;
}
assert (length > 0);
intl_pad_char (codeset, pad, &pad_size);
if (pad_size == 1)
{
(void) memset ((char *) s, (int) pad[0], length);
s = s + length;
}
else
{
for (i = 0; i < length; i++)
{
for (j = 0; j < pad_size; j++)
{
*(s++) = pad[j];
}
}
}
return s;
}
/*
* qstr_bin_to_hex () -
*
* arguments:
* dest: Pointer to destination hex buffer area
* dest_size: Size of destination buffer area in bytes
* src: Pointer to source binary buffer area
* src_size: Size of source buffer area in bytes
*
* returns/side-effects: int
* The number of converted source bytes is returned. This value will
* equal src_size if (dest_size >= 2*src_size) and less otherwise.
*
* description:
* Convert the binary data in the source buffer to ASCII hex characters
* in the destination buffer. The destination buffer should be at
* least 2 * src_size. If not, as much of the source string is processed
* as possible. The number of ASCII Hex characters in dest will
* equal two times the returned value.
*
*/
int
qstr_bin_to_hex (char *dest, int dest_size, const char *src, int src_size)
{
int i, copy_size;
if (dest_size >= (2 * src_size))
{
copy_size = src_size;
}
else
{
copy_size = dest_size / 2;
}
for (i = 0; i < copy_size; i++)
{
sprintf (&(dest[2 * i]), "%02x", (unsigned char) (src[i]));
}
return copy_size;
}
/*
* qstr_hex_to_bin () -
*
* arguments:
* dest: Pointer to destination hex buffer area
* dest_size: Size of destination buffer area in bytes
* src: Pointer to source binary buffer area
* src_size: Size of source buffer area in bytes
*
* returns/side-effects: int
* The number of converted hex characters is returned.
*
* description:
* Convert the string of hex characters to decimal values. For each two
* characters, one unsigned character value is produced. If the number
* of characters is odd, then the second nibble of the last byte will
* be 0 padded. If the destination buffer is not large enough to hold
* the converted data, as much data is converted as possible.
*
*/
int
qstr_hex_to_bin (char *dest, int dest_size, const char *src, int src_size)
{
int i, copy_size, src_index, required_size;
required_size = (src_size + 1) / 2;
if (dest_size >= required_size)
{
copy_size = required_size;
}
else
{
copy_size = dest_size;
}
src_index = 0;
for (i = 0; i < copy_size; i++)
{
int hex_digit;
hex_digit = hextoi (src[src_index++]);
if (hex_digit < 0)
{
return -1;
}
else
{
dest[i] = hex_digit << 4;
if (src_index < src_size)
{
hex_digit = hextoi (src[src_index++]);
if (hex_digit < 0)
{
return -1;
}
else
{
dest[i] += hex_digit;
}
}
}
}
return src_index;
}
/*
* qstr_bit_to_bin () -
*
* arguments:
* dest: Pointer to destination buffer area
* dest_size: Size of destination buffer area in bytes
* src: Pointer to source binary buffer area
* src_size: Size of source buffer area in bytes
*
* returns/side-effects: int
* The number of converted binary characters is returned.
*
* description:
* Convert the string of '0's and '1's to decimal values. For each 8
* characters, one unsigned character value is produced. If the number
* of characters is not a multiple of 8, the result will assume trailing
* 0 padding. If the destination buffer is not large enough to hold
* the converted data, as much data is converted as possible.
*
*/
int
qstr_bit_to_bin (char *dest, int dest_size, const char *src, int src_size)
{
int dest_byte, copy_size, src_index, required_size;
required_size = (src_size + 7) / 8;
if (dest_size >= required_size)
{
copy_size = required_size;
}
else
{
copy_size = dest_size;
}
src_index = 0;
for (dest_byte = 0; dest_byte < copy_size; dest_byte++)
{
int bit_count;
dest[dest_byte] = 0;
for (bit_count = 0; bit_count < 8; bit_count++)
{
dest[dest_byte] = dest[dest_byte] << 1;
if (src_index < src_size)
{
if (src[src_index] == '1')
{
dest[dest_byte]++;
}
else if (src[src_index] != '0')
{
return -1; /* Illegal digit */
}
src_index++;
}
}
}
return src_index;
}
/*
* qstr_bit_to_hex_coerce () -
*
* arguments:
* buffer: Pointer to destination buffer area
* buffer_size: Size of destination buffer area (in bytes, *including* null
* terminator)
* src: Pointer to source buffer area
* src_length: Length of source buffer area in bits
* pad_flag: TRUE if the buffer should be padded and FALSE otherwise
* copy_size: Number of bytes transfered from the src string to the dst
* buffer
* truncation: pointer to a int field. *outlen will equal 0 if no
* truncation occurred and will equal the size of the dst buffer
* in bytes needed to avoid truncation (not including the
* terminating NULL), otherwise.
*
* returns/side-effects: void
*
* description:
* Transfers at most buffer_size bytes to the region pointed at by dst.
* If pad_flag is TRUE, strings shorter than buffer_size will be
* blank-padded out to buffer_size-1 bytes. All strings will be
* null-terminated. If truncation is necessary (i.e., if buffer_size is
* less than or equal to src_length), *truncation is set to src_length;
* if truncation is is not necessary, *truncation is set to 0.
*
*/
void
qstr_bit_to_hex_coerce (char *buffer, int buffer_size, const char *src, int src_length, int pad_flag, int *copy_size,
int *truncation)
{
int src_size = QSTR_NUM_BYTES (src_length);
if (src == NULL)
{
buffer[0] = '\0';
return;
}
if (buffer_size > (2 * src_size))
{
/*
* No truncation; copy the data and blank pad if necessary.
*/
qstr_bin_to_hex (buffer, buffer_size, src, src_size);
/*
for (i=0; i<src_size; i++)
sprintf(&(buffer[2*i]), "%02x", (unsigned char)(src[i]));
*/
if (pad_flag != 0)
{
memset (&(buffer[2 * src_size]), '0', (buffer_size - (2 * src_size)));
*copy_size = buffer_size - 1;
}
else
{
*copy_size = 2 * src_size;
}
buffer[*copy_size] = '\0';
*truncation = 0;
}
else
{
/*
* Truncation is necessary; put as many bytes as possible into
* the receiving buffer and null-terminate it (i.e., it receives
* at most dstsize-1 bytes). If there is not outlen indicator by
* which we can indicate truncation, this is an error.
*
*/
if (buffer_size % 2)
{
src_size = buffer_size / 2;
}
else
{
src_size = (buffer_size - 1) / 2;
}
qstr_bin_to_hex (buffer, buffer_size, src, src_size);
/*
for (i=0; i<src_size; i++)
sprintf(&(buffer[2*i]), "%02x", (unsigned char)(src[i]));
*/
*copy_size = 2 * src_size;
buffer[*copy_size] = '\0';
*truncation = src_size;
}
}
/*
* db_get_string_length
*
* Arguments:
* value: Value container
*
* Returns: int
*
* Errors:
*
* Note:
* Returns the character length of the string in the container.
*
*/
int
db_get_string_length (const DB_VALUE * value)
{
DB_CONST_C_CHAR str;
int size;
INTL_CODESET codeset;
int length = 0;
#if 0
/* Currently, only the medium model is used */
switch (value->data.ch.info.style)
{
case SMALL_STRING:
str = value->data.ch.small.buf;
length = size = value->data.ch.small.size;
codeset = value->data.ch.small.codeset;
break;
case MEDIUM_STRING:
str = value->data.ch.medium.buf;
length = size = value->data.ch.medium.size;
codeset = value->data.ch.medium.codeset;
break;
case LARGE_STRING:
str = NULL;
size = 0;
break;
default:
break;
}
#endif
str = value->data.ch.medium.buf;
length = size = value->data.ch.medium.size;
codeset = (INTL_CODESET) value->data.ch.medium.codeset;
if (value->data.ch.medium.length != -1)
{
return value->data.ch.medium.length;
}
if (value->domain.general_info.type != DB_TYPE_BIT && value->domain.general_info.type != DB_TYPE_VARBIT)
{
intl_char_count ((unsigned char *) str, size, codeset, &length);
}
return length;
}
/*
* qstr_make_typed_string () -
*
* Arguments:
* db_type: value type for the result.
* value: Value container for the result.
* precision: Length of the string precision.
* src: Pointer to string.
* s_unit: Size of the string.
* codeset: codeset
* collation_id: collation
*
* Returns: void
*
* Errors:
*
* Note:
* Make a value container from the string of the given domain.
* This is a convenience function which allows for all string
* types given the proper domain type.
*
*/
void
qstr_make_typed_string (const DB_TYPE db_type, DB_VALUE * value, const int precision, DB_CONST_C_CHAR src,
const int s_unit, const int codeset, const int collation_id)
{
int error = NO_ERROR;
switch (db_type)
{
case DB_TYPE_CHAR:
error = db_make_char (value, precision, src, s_unit, codeset, collation_id);
break;
case DB_TYPE_VARCHAR:
error = db_make_varchar (value, precision, src, s_unit, codeset, collation_id);
break;
case DB_TYPE_BIT:
error = db_make_bit (value, precision, src, s_unit);
break;
case DB_TYPE_VARBIT:
error = db_make_varbit (value, precision, src, s_unit);
break;
default:
assert (false);
db_make_null (value);
break;
}
assert (error == NO_ERROR);
}
/*
* Private Functions
*/
/*
* qstr_get_category
*
* Arguments:
* s: DB_VALUE representation of a string.
*
* Returns: QSTR_CATEGORY
*
* Errors:
*
* Note:
* Returns the character code set of the string "s." The character code
* set of strings is:
*
* QSTR_CHAR, QSTR_BIT
*
* as defined in type QSTR_CATEGORY. A value of QSTR_UNKNOWN is defined
* if the string does not fit into one of these categories. This should
* never happen if is_string() returns TRUE.
*
*/
static QSTR_CATEGORY
qstr_get_category (const DB_VALUE * s)
{
QSTR_CATEGORY code_set;
switch (DB_VALUE_DOMAIN_TYPE (s))
{
case DB_TYPE_VARCHAR:
case DB_TYPE_CHAR:
code_set = QSTR_CHAR;
break;
case DB_TYPE_BIT:
case DB_TYPE_VARBIT:
code_set = QSTR_BIT;
break;
default:
code_set = QSTR_UNKNOWN;
break;
}
return code_set;
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* is_string () -
*
* Arguments:
* s: (IN) DB_VALUE variable.
*
* Returns: bool
*
* Errors:
*
* Note:
* Verifies that the value is a string. Returns TRUE if the
* domain type is one of:
*
* DB_TYPE_STRING
* DB_TYPE_CHAR
* DB_TYPE_VARCHAR
* DB_TYPE_BIT
* DB_TYPE_VARBIT
*
* Returns FALSE otherwise.
*
* This function supports the older type DB_TYPE_STRING which
* has been replaced by DB_TYPE_VARCHAR.
*
*/
static bool
is_string (const DB_VALUE * s)
{
DB_TYPE domain_type = DB_VALUE_DOMAIN_TYPE (s);
return QSTR_IS_ANY_CHAR_OR_BIT (domain_type);
}
#endif /* ENABLE_UNUSED_FUNCTION */
/*
* is_char_string () -
*
* Arguments:
* s: DB_VALUE variable.
*
* Returns: bool
*
* Errors:
*
* Note:
* Verifies that the value is a character string. Returns TRUE if the
* value is of domain type is one of:
*
* DB_TYPE_STRING
* DB_TYPE_VARCHAR
* DB_TYPE_CHAR
*
* Returns FALSE otherwise.
*
* This function supports the older type DB_TYPE_STRING which
* has been replaced by DB_TYPE_VARCHAR.
*
*/
static bool
is_char_string (const DB_VALUE * s)
{
DB_TYPE domain_type = DB_VALUE_DOMAIN_TYPE (s);
return (QSTR_IS_ANY_CHAR (domain_type));
}
/*
* is_integer () -
*
* Arguments:
* i: (IN) DB_VALUE variable.
*
* Returns: bool
*
* Errors:
*
* Note:
* Verifies that the value is an integer. Returns TRUE if the
* value is of domain type is one of:
*
* DB_TYPE_INTEGER
*
* Returns FALSE otherwise.
*
*/
static bool
is_integer (const DB_VALUE * i)
{
return (DB_VALUE_DOMAIN_TYPE (i) == DB_TYPE_INTEGER);
}
/*
* is_number () -
*
* Arguments:
* n: (IN) DB_VALUE variable.
*
* Returns: bool
*
* Errors:
*
* Note:
* Verifies that the value is an number. Returns TRUE if the
* value is of domain type is one of:
*
* DB_TYPE_NUMERIC
* DB_TYPE_INTEGER
* DB_TYPE_SMALLINT
* DB_TYPE_DOUBLE
* DB_TYPE_FLOAT
*
* Returns FALSE otherwise.
*
*/
static bool
is_number (const DB_VALUE * n)
{
DB_TYPE domain_type = DB_VALUE_DOMAIN_TYPE (n);
return ((domain_type == DB_TYPE_NUMERIC) || (domain_type == DB_TYPE_INTEGER) || (domain_type == DB_TYPE_SMALLINT)
|| (domain_type == DB_TYPE_BIGINT) || (domain_type == DB_TYPE_DOUBLE) || (domain_type == DB_TYPE_FLOAT)
|| (domain_type == DB_TYPE_MONETARY));
}
/*
* is_str_find_all () -
*
* Arguments:
* val: (IN) DB_VALUE variable. Assumed string
* find_all: (OUT) whether all/one
*
* Returns: error
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE
*
* Note:
* find_all becomes TRUE if val's lower-cased string is 'all', FALSE if it is 'one'
*
* Returns ER_QSTR_INVALID_DATA_TYPE otherwise.
*
*/
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* qstr_compare () - compare two character strings of DB_TYPE_STRING(tp_String)
*
* Arguments:
* string1: 1st character string
* size1: size of 1st string
* string2: 2nd character string
* size2: size of 2nd string
*
* Returns:
* Greater than 0 if string1 > string2
* Equal to 0 if string1 = string2
* Less than 0 if string1 < string2
*
* Errors:
*
* Note:
* This function is similar to strcmp(3) or bcmp(3). It is designed to
* follow SQL_TEXT character set collation. Padding character(space ' ') is
* the smallest character in the set. (e.g.) "ab z" < "ab\t1"
*
*/
int
qstr_compare (const unsigned char *string1, int size1, const unsigned char *string2, int size2)
{
int n, i, cmp;
unsigned char c1, c2;
#define PAD ' ' /* str_pad_char(INTL_CODESET_ISO88591, pad, &pad_size) */
#define SPACE PAD /* smallest character in the collation sequence */
#define ZERO '\0' /* space is treated as zero */
n = size1 < size2 ? size1 : size2;
for (i = 0, cmp = 0; i < n && cmp == 0; i++)
{
c1 = *string1++;
if (c1 == SPACE)
{
c1 = ZERO;
}
c2 = *string2++;
if (c2 == SPACE)
{
c2 = ZERO;
}
cmp = c1 - c2;
}
if (cmp != 0)
{
return cmp;
}
if (size1 == size2)
{
return cmp;
}
c1 = c2 = ZERO;
if (size1 < size2)
{
n = size2 - size1;
for (i = 0; i < n && cmp == 0; i++)
{
c2 = *string2++;
if (c2 == PAD)
{
c2 = ZERO;
}
cmp = c1 - c2;
}
}
else
{
n = size1 - size2;
for (i = 0; i < n && cmp == 0; i++)
{
c1 = *string1++;
if (c1 == PAD)
{
c1 = ZERO;
}
cmp = c1 - c2;
}
}
return cmp;
#undef PAD
#undef SPACE
#undef ZERO
} /* qstr_compare() */
/*
* char_compare () - compare two character strings of DB_TYPE_CHAR(tp_Char)
*
* Arguments:
* string1: 1st character string
* size1: size of 1st string
* string2: 2nd character string
* size2: size of 2nd string
*
* Returns:
* Greater than 0 if string1 > string2
* Equal to 0 if string1 = string2
* Less than 0 if string1 < string2
*
* Errors:
*
* Note:
* This function is identical to qstr_compare().
*
*/
int
char_compare (const unsigned char *string1, int size1, const unsigned char *string2, int size2)
{
int n, i, cmp;
unsigned char c1, c2;
assert (size1 >= 0 && size2 >= 0);
#define PAD ' ' /* str_pad_char(INTL_CODESET_ISO88591, pad, &pad_size) */
#define SPACE PAD /* smallest character in the collation sequence */
#define ZERO '\0' /* space is treated as zero */
n = size1 < size2 ? size1 : size2;
for (i = 0, cmp = 0; i < n && cmp == 0; i++)
{
c1 = *string1++;
if (c1 == SPACE)
{
c1 = ZERO;
}
c2 = *string2++;
if (c2 == SPACE)
{
c2 = ZERO;
}
cmp = c1 - c2;
}
if (cmp != 0)
{
return cmp;
}
if (size1 == size2)
{
return cmp;
}
c1 = c2 = ZERO;
if (size1 < size2)
{
n = size2 - size1;
for (i = 0; i < n && cmp == 0; i++)
{
c2 = *string2++;
if (c2 == PAD)
{
c2 = ZERO;
}
cmp = c1 - c2;
}
}
else
{
n = size1 - size2;
for (i = 0; i < n && cmp == 0; i++)
{
c1 = *string1++;
if (c1 == PAD)
{
c1 = ZERO;
}
cmp = c1 - c2;
}
}
return cmp;
#undef PAD
#undef SPACE
#undef ZERO
} /* char_compare() */
#endif /* ENABLE_UNUSED_FUNCTION */
/*
* bit_compare () - compare two bit strings of DB_TYPE_BIT(tp_Bit)
*
* Arguments:
* string1: 1st bit string
* size1: size of 1st string
* string2: 2nd bit string
* size2: size of 2nd string
* codeset: codeset of strings
*
* Returns:
* Greater than 0 if string1 > string2
* Equal to 0 if string1 = string2
* Less than 0 if string1 < string2
*
* Errors:
*
* Note:
* This function is identical to qstr_compare().
*
*/
int
bit_compare (const unsigned char *string1, int size1, const unsigned char *string2, int size2)
{
int n, i, cmp;
assert (size1 >= 0 && size2 >= 0);
#define PAD '\0' /* str_pad_char(INTL_CODESET_RAW_BITS, pad, &pad_size) */
n = size1 < size2 ? size1 : size2;
for (i = 0, cmp = 0; i < n && cmp == 0; i++)
{
cmp = (*string1++ - *string2++);
}
if (cmp != 0)
{
return cmp;
}
cmp = size1 - size2;
return cmp;
#undef PAD
} /* bit_compare() */
/*
* varbit_compare () - compare two bit strings of DB_TYPE_VARBIT(tp_VarBit)
*
* Arguments:
* string1: 1st bit string
* size1: size of 1st string
* string2: 2nd bit string
* size2: size of 2nd string
* codeset: codeset of strings
*
* Returns:
* Greater than 0 if string1 > string2
* Equal to 0 if string1 = string2
* Less than 0 if string1 < string2
*
* Errors:
*
* Note:
* This function is identical to qstr_compare().
*
*/
int
varbit_compare (const unsigned char *string1, int size1, const unsigned char *string2, int size2)
{
int n, i, cmp;
#define PAD '\0' /* str_pad_char(INTL_CODESET_RAW_BITS, pad, &pad_size) */
n = size1 < size2 ? size1 : size2;
for (i = 0, cmp = 0; i < n && cmp == 0; i++)
{
cmp = (*string1++ - *string2++);
}
if (cmp != 0)
{
return cmp;
}
cmp = size1 - size2;
return cmp;
#undef PAD
} /* varbit_compare() */
/*
* qstr_grow_string () - grows the memory buffer of string value
*
* Arguments:
* src: (IN) String variable.
* result: (IN/OUT) value with new size, or DB_NULL if requested size
* exceeds PRM_STRING_MAX_SIZE_BYTES system parameter
* new_size: (IN) New size to be reserved for the string (in bytes).
*
* Returns:
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE:
* <src_string> is not CHAR, VARCHAR
*
* Note : src buffer is not freed, caller should be aware of this;
* Result DB_VALUE must already be created.
* It doesn't operate on BIT strings;
* if requested size is larger than PRM_STRING_MAX_SIZE_BYTES,
* DB_VALUE_NULL is returned
*/
static int
qstr_grow_string (DB_VALUE * src_string, DB_VALUE * result, int new_size)
{
int result_size = 0, src_length = 0, result_domain_length = 0, src_size = 0;
char *r = NULL;
int error_status = NO_ERROR;
DB_TYPE src_type;
INTL_CODESET codeset;
assert (src_string != (DB_VALUE *) NULL);
assert (result != (DB_VALUE *) NULL);
src_type = DB_VALUE_DOMAIN_TYPE (src_string);
src_length = (int) db_get_string_length (src_string);
result_domain_length = DB_VALUE_PRECISION (src_string);
if (!QSTR_IS_ANY_CHAR (src_type) || DB_IS_NULL (src_string))
{
return ER_QSTR_INVALID_DATA_TYPE;
}
codeset = db_get_string_codeset (src_string);
result_size = src_length * INTL_CODESET_MULT (codeset);
src_size = db_get_string_size (src_string);
assert (new_size >= result_size);
assert (new_size >= src_size);
result_size = MAX (result_size, new_size);
result_size = MAX (result_size, src_size);
if (result_size > (int) prm_get_bigint_value (PRM_ID_STRING_MAX_SIZE_BYTES))
{
er_set (ER_NOTIFICATION_SEVERITY, ARG_FILE_LINE, ER_QPROC_STRING_SIZE_TOO_BIG, 2, result_size,
(int) prm_get_bigint_value (PRM_ID_STRING_MAX_SIZE_BYTES));
db_make_null (result);
return NO_ERROR;
}
/* Allocate storage for the result string */
r = (char *) db_private_alloc (NULL, (size_t) result_size + 1);
if (r == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
memset (r, 0, (size_t) result_size + 1);
if (src_size > 0)
{
memcpy (r, db_get_string (src_string), src_size);
}
qstr_make_typed_string (DB_TYPE_VARCHAR, result, result_domain_length, r, (int) MIN (result_size, src_size),
codeset, db_get_string_collation (src_string));
if (prm_get_bool_value (PRM_ID_ORACLE_STYLE_EMPTY_STRING) == true && DB_IS_NULL (result)
&& QSTR_IS_ANY_CHAR_OR_BIT (DB_VALUE_DOMAIN_TYPE (result)))
{
/* intermediate value : clear is_null flag */
result->domain.general_info.is_null = 0;
}
result->need_clear = true;
return error_status;
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* qstr_append () - appends a string to another string. Doesn't operate on BIT
*
* Arguments:
* s1: (IN/OUT) First string pointer.
* s1_length: (IN) Character length of <s1>.
* s1_precision: (IN) Max character length of <s1>.
* s1_type: (IN) Domain type of <s1>.
* s2: (IN) Second string pointer.
* s2_length: (IN) Character length of <s2>.
* s2_precision: (IN) Max character length of <s2>.
* s2_type: (IN) Domain type of <s2>.
* codeset: (IN) international codeset.
* result_length: (OUT) Character length of <result>.
* result_size: (OUT) Byte size of <result>.
* data_status: (OUT) status of truncation
*
* Returns:
* ER_QSTR_INVALID_DATA_TYPE:
* <s1> and <s2> are not CHAR, VARCHAR
*
* Errors:
*
*/
static int
qstr_append (unsigned char *s1, int s1_length, int s1_precision, DB_TYPE s1_type, const unsigned char *s2,
int s2_length, int s2_precision, DB_TYPE s2_type, INTL_CODESET codeset, int *result_length,
int *result_size, DB_DATA_STATUS * data_status)
{
int copy_length, copy_size;
int pad1_length, pad2_length;
int length_left, cat_length, cat_size;
int s1_logical_length, s2_logical_length;
unsigned char *cat_ptr;
int error_status = NO_ERROR;
*data_status = DATA_STATUS_OK;
/* Note : append logic is similar to concatenate, except the s1 string is already copied into the result. However,
* the concatenate logic is preserved in order to have the same type limits checking and padding. */
/* functions qstr_append & qstr_concatenate are kept separately because of different signatures and different
* purpose. However, a refactoring may be necessary for the shared code */
if (!QSTR_IS_ANY_CHAR (s1_type) || !QSTR_IS_ANY_CHAR (s2_type))
{
return ER_QSTR_INVALID_DATA_TYPE;
}
/*
* Categorize the source string into fixed and variable
* length. Variable length strings are simple. Fixed
* length strings have to be handled special since the
* strings may not have all of their pad character allocated
* yet. We have to account for this and act as if all of the
* characters are present. They all will be by the time
* we are through.
*/
if (QSTR_IS_FIXED_LENGTH (s1_type))
{
s1_logical_length = s1_precision;
}
else
{
s1_logical_length = s1_length;
}
if (QSTR_IS_FIXED_LENGTH (s2_type))
{
s2_logical_length = s2_precision;
}
else
{
s2_logical_length = s2_length;
}
/*
* If both source strings are fixed-length, the concatenated
* result will be fixed-length.
*/
if (QSTR_IS_FIXED_LENGTH (s1_type) && QSTR_IS_FIXED_LENGTH (s2_type))
{
/*
* The result will be a chararacter string of length =
* string1_precision + string2_precision. If the result
* length is greater than the maximum allowed for a fixed
* length string, the TRUNCATED exception is raised and
* the string is shortened appropriately.
*/
*result_length = s1_logical_length + s2_logical_length;
if (*result_length > QSTR_MAX_PRECISION (s1_type))
{
*result_length = QSTR_MAX_PRECISION (s1_type);
*data_status = DATA_STATUS_TRUNCATED;
}
*result_size = *result_length;
/*
* Determine how much of s1 is already copied.
* Remember that this may or may not include needed padding.
* Then determine how much padding must be added to each
* source string.
*/
copy_length = MIN (s1_length, *result_length);
intl_char_size ((unsigned char *) s1, copy_length, codeset, ©_size);
pad1_length = MIN (s1_logical_length, *result_length) - copy_length;
length_left = *result_length - copy_length - pad1_length;
/*
* Determine how much of string2 can be concatenated after
* string1. Remember that string2 is concatentated after
* the full length of string1 including any necessary pad
* characters.
*/
cat_length = MIN (s2_length, length_left);
intl_char_size ((unsigned char *) s2, cat_length, codeset, &cat_size);
pad2_length = length_left - cat_length;
/*
* Pad string s1, Copy the s2 string after the s1 string
*/
cat_ptr = qstr_pad_string ((unsigned char *) &(s1[copy_size]), pad1_length, codeset);
memcpy ((char *) cat_ptr, (char *) s2, cat_size);
(void) qstr_pad_string ((unsigned char *) &cat_ptr[cat_size], pad2_length, codeset);
}
/*
* If either source string is variable-length, the concatenated
* result will be variable-length.
*/
else
{
/*
* The result length will be the sum of the lengths of
* the two source strings. If this is greater than the
* maximum length of a variable length string, then the
* result length is adjusted appropriately. This does
* not necessarily indicate a truncation condition.
*/
*result_length = MIN ((s1_logical_length + s2_logical_length), QSTR_MAX_PRECISION (s1_type));
*result_size = *result_length;
/*
* Calculate the number of characters from string1 that are already
* into the result. If s1 string is larger than the expected entire
* string and if the portion of the string s1 contained anything but
* pad characters, then raise a truncation exception.
*/
copy_length = s1_length;
if (copy_length > *result_length)
{
copy_length = *result_length;
if (varchar_truncated ((unsigned char *) s1, s1_type, s1_length, copy_length, codeset))
{
*data_status = DATA_STATUS_TRUNCATED;
}
}
intl_char_size ((unsigned char *) s1, copy_length, codeset, ©_size);
pad1_length = MIN (s1_logical_length, *result_length) - copy_length;
length_left = *result_length - copy_length - pad1_length;
/*
* Processess string2 as we did for string1.
*/
cat_length = s2_length;
if (cat_length > (*result_length - copy_length))
{
cat_length = *result_length - copy_length;
if (varchar_truncated ((unsigned char *) s2, s2_type, s2_length, cat_length, codeset))
{
*data_status = DATA_STATUS_TRUNCATED;
}
}
intl_char_size ((unsigned char *) s2, cat_length, codeset, &cat_size);
pad2_length = length_left - cat_length;
/*
* Actually perform the copy operation.
*/
cat_ptr = qstr_pad_string ((unsigned char *) &(s1[copy_size]), pad1_length, codeset);
memcpy ((char *) cat_ptr, (char *) s2, cat_size);
(void) qstr_pad_string ((unsigned char *) &cat_ptr[cat_size], pad2_length, codeset);
}
intl_char_size (s1, *result_length, codeset, result_size);
return error_status;
}
#endif
/*
* qstr_concatenate () -
*
* Arguments:
* s1: (IN) First string pointer.
* s1_length: (IN) Character length of <s1>.
* s1_size: (IN) Byte size of <s1>.
* s1_precision: (IN) Max character length of <s1>.
* s1_type: (IN) Domain type of <s1>.
* s2: (IN) Second string pointer.
* s2_length: (IN) Character length of <s2>.
* s2_size: (IN) Byte size of <s2>.
* s2_precision: (IN) Max character length of <s2>.
* s2_type: (IN) Domain type of <s2>.
* codeset: (IN) international codeset.
* result: (OUT) Concatenated string pointer.
* result_length: (OUT) Character length of <result>.
* result_size: (OUT) Byte size of <result>.
* result_type: (OUT) Domain type of <result>
*
* Returns:
*
* Errors:
*
*/
static int
qstr_concatenate (const unsigned char *s1, int s1_length, int s1_size_, int s1_precision, DB_TYPE s1_type,
const unsigned char *s2, int s2_length, int s2_size_, int s2_precision, DB_TYPE s2_type,
INTL_CODESET codeset, unsigned char **result, int *result_length, int *result_size,
DB_DATA_STATUS * data_status)
{
int copy_length, copy_size;
int pad1_length, pad2_length;
int length_left, cat_length, cat_size;
int s1_logical_length, s2_logical_length;
int s1_size, s2_size;
unsigned char *cat_ptr;
int error_status = NO_ERROR;
*data_status = DATA_STATUS_OK;
s1_size = s1_size_;
s2_size = s2_size_;
/*
* Categorize the source string into fixed and variable
* length. Variable length strings are simple. Fixed
* length strings have to be handled special since the
* strings may not have all of their pad character allocated
* yet. We have to account for this and act as if all of the
* characters are present. They all will be by the time
* we are through.
*/
if (QSTR_IS_FIXED_LENGTH (s1_type))
{
s1_logical_length = s1_precision;
}
else
{
s1_logical_length = s1_length;
}
if (QSTR_IS_FIXED_LENGTH (s2_type))
{
s2_logical_length = s2_precision;
}
else
{
s2_logical_length = s2_length;
}
/*
* If both source strings are fixed-length, the concatenated
* result will be fixed-length.
*/
if (QSTR_IS_FIXED_LENGTH (s1_type) && QSTR_IS_FIXED_LENGTH (s2_type))
{
/*
* The only time we enter inside this if statement is
* when we are using the iso88591 codeset and a data type like char(100).
* This is because the size is not fixed in all other cases.
*/
/*
* The result will be a chararacter string of length =
* string1_precision + string2_precision. If the result
* length is greater than the maximum allowed for a fixed
* length string, the TRUNCATED exception is raised and
* the string is shortened appropriately.
*/
*result_length = s1_logical_length + s2_logical_length;
if (*result_length > QSTR_MAX_PRECISION (s1_type))
{
*result_length = QSTR_MAX_PRECISION (s1_type);
*data_status = DATA_STATUS_TRUNCATED;
}
if (s1_size == 0)
{
s1_size = s1_logical_length;
}
if (s2_size == 0)
{
s2_size = s2_logical_length;
}
*result_size = s1_size + s2_size;
if (*result_size > (int) prm_get_bigint_value (PRM_ID_STRING_MAX_SIZE_BYTES))
{
goto size_error;
}
/* Allocate storage for the result string */
*result = (unsigned char *) db_private_alloc (NULL, (size_t) (*result_size) + 1);
if (*result == NULL)
{
goto mem_error;
}
/*
* Determine how much of string1 needs to be copied.
* Remember that this may or may not include needed padding.
* Then determine how much padding must be added to each
* source string.
*/
copy_length = MIN (s1_length, *result_length);
intl_char_size ((unsigned char *) s1, copy_length, codeset, ©_size);
pad1_length = MIN (s1_logical_length, *result_length) - copy_length;
length_left = *result_length - copy_length - pad1_length;
/*
* Determine how much of string2 can be concatenated after
* string1. Remember that string2 is concatentated after
* the full length of string1 including any necessary pad
* characters.
*/
cat_length = MIN (s2_length, length_left);
intl_char_size ((unsigned char *) s2, cat_length, codeset, &cat_size);
pad2_length = length_left - cat_length;
/*
* Copy the source strings into the result string
*/
memcpy ((char *) *result, (char *) s1, copy_size);
cat_ptr = qstr_pad_string ((unsigned char *) &((*result)[copy_size]), pad1_length, codeset);
memcpy ((char *) cat_ptr, (char *) s2, cat_size);
(void) qstr_pad_string ((unsigned char *) &cat_ptr[cat_size], pad2_length, codeset);
}
/*
* If either source string is variable-length, the concatenated
* result will be variable-length.
*/
else
{
/*
* The result length will be the sum of the lengths of
* the two source strings. If this is greater than the
* maximum length of a variable length string, then the
* result length is adjusted appropriately. This does
* not necessarily indicate a truncation condition.
*/
*result_length = MIN ((s1_logical_length + s2_logical_length), QSTR_MAX_PRECISION (s1_type));
if (s1_size == 0)
{
s1_size = s1_logical_length;
}
if (s2_size == 0)
{
s2_size = s2_logical_length;
}
*result_size = s1_size + s2_size;
if (*result_size > (int) prm_get_bigint_value (PRM_ID_STRING_MAX_SIZE_BYTES))
{
goto size_error;
}
/* Allocate the result string */
*result = (unsigned char *) db_private_alloc (NULL, (size_t) (*result_size) + 1);
if (*result == NULL)
{
goto mem_error;
}
/*
* Calculate the number of characters from string1 that can
* be copied to the result. If we cannot copy the entire
* string and if the portion of the string which was not
* copied contained anything but pad characters, then raise
* a truncation exception.
*/
copy_length = s1_length;
copy_size = s1_size;
if (copy_length > *result_length)
{
copy_length = *result_length;
copy_size = *result_size;
if (varchar_truncated ((unsigned char *) s1, s1_type, s1_length, copy_length, codeset))
{
*data_status = DATA_STATUS_TRUNCATED;
}
}
pad1_length = MIN (s1_logical_length, *result_length) - copy_length;
length_left = *result_length - copy_length - pad1_length;
/*
* Processess string2 as we did for string1.
*/
cat_length = s2_length;
cat_size = s2_size;
if (cat_length > (*result_length - copy_length))
{
cat_length = *result_length - copy_length;
cat_size = *result_size - copy_size;
if (varchar_truncated ((unsigned char *) s2, s2_type, s2_length, cat_length, codeset))
{
*data_status = DATA_STATUS_TRUNCATED;
}
}
pad2_length = length_left - cat_length;
/*
* Actually perform the copy operations.
*/
memcpy ((char *) *result, (char *) s1, copy_size);
cat_ptr = qstr_pad_string ((unsigned char *) &((*result)[copy_size]), pad1_length, codeset);
memcpy ((char *) cat_ptr, (char *) s2, cat_size);
(void) qstr_pad_string ((unsigned char *) &cat_ptr[cat_size], pad2_length, codeset);
}
return error_status;
size_error:
error_status = ER_QPROC_STRING_SIZE_TOO_BIG;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 2, *result_size,
(int) prm_get_bigint_value (PRM_ID_STRING_MAX_SIZE_BYTES));
return error_status;
/*
* Error handler
*/
mem_error:
assert (er_errid () != NO_ERROR);
error_status = er_errid ();
return error_status;
}
/*
* qstr_bit_concatenate () -
*
* Arguments:
* s1: (IN) First string pointer.
* s1_length: (IN) Character length of <s1>.
* s1_precision: (IN) Max character length of <s1>.
* s1_type: (IN) Domain type of <s1>.
* s2: (IN) Second string pointer.
* s2_length: (IN) Character length of <s2>.
* s2_precision: (IN) Max character length of <s2>.
* s2_type: (IN) Domain type of <s2>.
* result: (OUT) Concatenated string pointer.
* result_length: (OUT) Character length of <result>.
* result_size: (OUT) Byte size of <result>.
* result_type: (OUT) Domain type of <result>
*
* Returns:
*
* Errors:
*
*/
static int
qstr_bit_concatenate (const unsigned char *s1, int s1_length, int s1_precision, DB_TYPE s1_type,
const unsigned char *s2, int s2_length, int s2_precision, DB_TYPE s2_type, unsigned char **result,
int *result_length, int *result_size, DB_TYPE * result_type, DB_DATA_STATUS * data_status)
{
int s1_size, s2_size;
int copy_length, cat_length;
int s1_logical_length, s2_logical_length;
int error_status = NO_ERROR;
*data_status = DATA_STATUS_OK;
/*
* Calculate the byte size of the strings.
* Calculate the bit length and byte size needed to concatenate
* the two strings without truncation.
*/
s1_size = QSTR_NUM_BYTES (s1_length);
s2_size = QSTR_NUM_BYTES (s2_length);
/*
* Categorize the source string into fixed and variable
* length. Variable length strings are simple. Fixed
* length strings have to be handled special since the
* strings may not have all of their pad character allocated
* yet. We have to account for this and act as if all of the
* characters are present. They all will be by the time
* we are through.
*/
s1_logical_length = (s1_type == DB_TYPE_CHAR) ? s1_precision : s1_length;
s2_logical_length = (s2_type == DB_TYPE_CHAR) ? s2_precision : s2_length;
if ((s1_type == DB_TYPE_BIT) && (s2_type == DB_TYPE_BIT))
{
/*
* The result will be a bit string of length =
* string1_precision + string2_precision. If the result
* length is greater than the maximum allowed for a fixed
* length string, the TRUNCATED exception is raised and
* the string is shortened appropriately.
*/
*result_type = DB_TYPE_BIT;
*result_length = s1_logical_length + s2_logical_length;
if (*result_length > DB_MAX_BIT_LENGTH)
{
*result_length = DB_MAX_BIT_LENGTH;
*data_status = DATA_STATUS_TRUNCATED;
}
*result_size = QSTR_NUM_BYTES (*result_length);
if (*result_size > (int) prm_get_bigint_value (PRM_ID_STRING_MAX_SIZE_BYTES))
{
goto size_error;
}
/* Allocate the result string */
*result = (unsigned char *) db_private_alloc (NULL, (size_t) (*result_size) + 1);
if (*result == NULL)
{
goto mem_error;
}
/*
* The source strings may not be fully padded, so
* we pre-pad the result string.
*/
(void) memset ((char *) *result, (int) 0, (int) *result_size);
/*
* Determine how much of string1 needs to be copied.
* Remember that this may or may not include needed padding
*/
copy_length = s1_length;
if (copy_length > *result_length)
{
copy_length = *result_length;
}
/*
* Determine how much of string2 can be concatenated after
* string1. Remember that string2 is concatentated after
* the full length of string1 including any necessary pad
* characters.
*/
cat_length = s2_length;
if (cat_length > (*result_length - s1_logical_length))
{
cat_length = *result_length - s1_logical_length;
}
/*
* Copy the source strings into the result string.
* We are being a bit sloppy here by performing a byte
* copy as opposed to a bit copy. But this should be OK
* since the bit strings should be bit padded with 0' s */
bit_ncat (*result, 0, (unsigned char *) s1, copy_length);
bit_ncat (*result, s1_logical_length, (unsigned char *) s2, cat_length);
}
else /* Assume DB_TYPE_VARBIT */
{
/*
* The result length will be the sum of the lengths of
* the two source strings. If this is greater than the
* maximum length of a variable length string, then the
* result length is adjusted appropriately. This does
* not necessarily indicate a truncation condition.
*/
*result_type = DB_TYPE_VARBIT;
*result_length = s1_logical_length + s2_logical_length;
if (*result_length > DB_MAX_BIT_LENGTH)
*result_length = DB_MAX_BIT_LENGTH;
*result_size = QSTR_NUM_BYTES (*result_length);
if (*result_size > (int) prm_get_bigint_value (PRM_ID_STRING_MAX_SIZE_BYTES))
{
goto size_error;
}
/* Allocate storage for the result string */
*result = (unsigned char *) db_private_alloc (NULL, (size_t) (*result_size) + 1);
if (*result == NULL)
{
goto mem_error;
}
/*
* The source strings may not be fully padded, so
* we pre-pad the result string.
*/
(void) memset ((char *) *result, (int) 0, (int) *result_size);
/*
* Calculate the number of bits from string1 that can
* be copied to the result. If we cannot copy the entire
* string and if the portion of the string which was not
* copied contained anything but 0's, then raise a
* truncation exception.
*/
copy_length = s1_length;
if (copy_length > *result_length)
{
copy_length = *result_length;
if (varbit_truncated (s1, s1_length, copy_length))
{
*data_status = DATA_STATUS_TRUNCATED;
}
}
/* Processess string2 as we did for string1. */
cat_length = s2_length;
if (cat_length > (*result_length - copy_length))
{
cat_length = *result_length - copy_length;
if (varbit_truncated (s2, s2_length, cat_length))
{
*data_status = DATA_STATUS_TRUNCATED;
}
}
/*
* Actually perform the copy operations and
* place the result string in a container.
*/
bit_ncat (*result, 0, (unsigned char *) s1, copy_length);
bit_ncat (*result, copy_length, s2, cat_length);
}
return error_status;
size_error:
error_status = ER_QPROC_STRING_SIZE_TOO_BIG;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 2, *result_size,
(int) prm_get_bigint_value (PRM_ID_STRING_MAX_SIZE_BYTES));
return error_status;
/*
* Error handling
*/
mem_error:
assert (er_errid () != NO_ERROR);
error_status = er_errid ();
return error_status;
}
/*
* varchar_truncated () -
*
* Arguments:
* s: (IN) Pointer to input string.
* s_length: (IN) Length of input string.
* used_chars: (IN) Number of characters which were used by caller.
* 0 <= <used_chars> <= <s_length>
* codeset: (IN) international codeset of input string.
*
* Returns: bool
*
* Errors:
*
* Note:
* This is a convenience function which is used by the concatenation
* function to determine if a variable length string has been
* truncated. When concatenating variable length strings, the string
* is not considered truncated if only pad characters were omitted.
*
* This function accepts a string <s>, its length <s_length>, and
* a count of characters <used_chars>. If the remaining characters
* are all pad characters, then the function returns true value.
* A False value is returned otherwise.
*
*/
static bool
varchar_truncated (const unsigned char *s, DB_TYPE s_type, int s_length, int used_chars, INTL_CODESET codeset)
{
unsigned char pad[2];
int pad_size = 0, trim_length, trim_size;
int s_size;
bool truncated = false;
intl_pad_char (codeset, pad, &pad_size);
intl_char_size ((unsigned char *) s, s_length, codeset, &s_size);
qstr_trim_trailing (pad, pad_size, s, s_type, s_length, s_size, codeset, &trim_length, &trim_size, true);
if (trim_length > used_chars)
{
truncated = true;
}
return truncated;
}
/*
* varbit_truncated () -
*
* Arguments:
* s: (IN) Pointer to input string.
* s_length: (IN) Length of input string.
* used_bits: (IN) Number of characters which were used by caller.
* 0 <= <used_chars> <= <s_length>
*
* Returns:
*
* Errors:
*
* Note:
* This is a convenience function which is used by the concatenation
* function to determine if a variable length string has been
* truncated. When concatenating variable length strings, the bit
* string is not considered truncated if only 0's were omitted.
*
* This function accepts a string <s>, its length <s_length>, and
* a count of characters <used_chars>. If the remaining characters
* are all 0's, then the function returns true value. A False value
* is returned otherwise.
*
*/
static bool
varbit_truncated (const unsigned char *s, int s_length, int used_bits)
{
int last_set_bit;
bool truncated = false;
last_set_bit = bstring_fls ((char *) s, QSTR_NUM_BYTES (s_length));
if (last_set_bit > used_bits)
{
truncated = true;
}
return truncated;
}
/*
* bit_ncat () -
*
* Arguments:
* r: Pointer to bit string 1
* offset: Number of bits in string1
* s: Pointer to bit string 2
* n: Number of bits in string 2
*
* Returns: void
*
* Errors:
*
* Note:
* Shift the bits of <s> onto the end of <r>. This is a helper
* function to str_bit_concatenate. This function shifts
* (concatenates) exactly the number of bits specified into the result
* buffer which must be preallocated to the correct size.
*
*/
static void
bit_ncat (unsigned char *r, int offset, const unsigned char *s, int n)
{
int i, copy_size, cat_size, total_size;
unsigned int remainder, shift_amount;
unsigned short tmp_shifted;
unsigned char mask;
copy_size = QSTR_NUM_BYTES (offset);
cat_size = QSTR_NUM_BYTES (n);
total_size = QSTR_NUM_BYTES (offset + n);
remainder = offset % BYTE_SIZE;
if (remainder == 0)
{
memcpy ((char *) &r[copy_size], (char *) s, cat_size);
}
else
{
int start_byte = copy_size - 1;
shift_amount = BYTE_SIZE - remainder;
mask = 0xff << shift_amount;
/*
* tmp_shifted is loaded with a byte from the source
* string and shifted into poition. The upper byte is
* used for the current destination location, while the
* lower byte is used by the next destination location.
*/
for (i = start_byte; i < total_size; i++)
{
tmp_shifted = (unsigned short) (s[i - start_byte]);
tmp_shifted = tmp_shifted << shift_amount;
r[i] = (r[i] & mask) | (tmp_shifted >> BYTE_SIZE);
if (i < (total_size - 1))
{
r[i + 1] = (unsigned char) (tmp_shifted & (unsigned short) 0xff);
}
}
}
/* Mask out the unused bits */
mask = 0xff << (BYTE_SIZE - ((offset + n) % BYTE_SIZE));
if (mask != 0)
{
r[total_size - 1] &= mask;
}
}
/*
* bstring_fls () -
*
* Arguments:
* s: Pointer to source bit string
* n: Number of bits in string1
*
* Returns: int
*
* Errors:
*
* Note:
* Find the last set bit in the bit string. The bits are numbered left
* to right starting at 1. A value of 0 indicates that no set bits were
* found in the string.
*
*/
static int
bstring_fls (const char *s, int n)
{
int byte_num, bit_num, inter_bit_num;
/*
* We are looking for the first non-zero byte (starting at the end).
*/
byte_num = n - 1;
while ((byte_num >= 0) && ((int) (s[byte_num]) == 0))
{
byte_num--;
}
/*
* If byte_num is < 0, then the string is all 0's.
* Othersize, byte_num is the index for the first byte which has
* some bits set (from the end).
*/
if (byte_num < 0)
{
bit_num = 0;
}
else
{
inter_bit_num = (int) qstr_ffs ((int) (s[byte_num]));
bit_num = (byte_num * BYTE_SIZE) + (BYTE_SIZE - inter_bit_num + 1);
}
return bit_num;
}
/*
* qstr_bit_coerce () -
*
* Arguments:
* src_string: (In) Source string
* dest_string: (Out) Coerced string
*
* Returns: DB_DATA_STATUS
*
* Errors:
*
* Note:
* This is a helper function which performs the actual coercion for
* bit strings. It is called from db_bit_string_coerce().
*
* If any loss of data due to truncation occurs DATA_STATUS_TRUNCATED
* is returned.
*
*/
static int
qstr_bit_coerce (const unsigned char *src, int src_length, int src_precision, DB_TYPE src_type, unsigned char **dest,
int *dest_length, int dest_precision, DB_TYPE dest_type, DB_DATA_STATUS * data_status)
{
int src_padded_length, copy_size, dest_size, copy_length;
int error_status = NO_ERROR;
*data_status = DATA_STATUS_OK;
/*
* <src_padded_length> is the length of the fully padded
* source string.
*/
if (QSTR_IS_FIXED_LENGTH (src_type))
{
src_padded_length = src_precision;
}
else
{
src_padded_length = src_length;
}
/*
* If there is not enough precision in the destination string,
* then some bits will be omited from the source string.
*/
if (src_padded_length > dest_precision)
{
int i, n = 0;
i = dest_precision / 8;
if (src[i] & (0x80 >> (dest_precision % 8)) || ((src[i] << (dest_precision % 8)) & 0xff))
{
*data_status = DATA_STATUS_TRUNCATED;
}
else
{
i++; /* for check reamin trailing bits */
for (; i < (src_padded_length + 4) / 8; i++)
{
if (src[i])
{
*data_status = DATA_STATUS_TRUNCATED;
break;
}
}
}
src_padded_length = dest_precision;
}
copy_length = MIN (src_length, src_padded_length);
copy_size = QSTR_NUM_BYTES (copy_length);
/*
* For fixed-length destination strings...
* Allocate the destination precision size, copy the source
* string and pad the rest.
*
* For variable-length destination strings...
* Allocate enough for a fully padded source string, copy
* the source string and pad the rest.
*/
if (QSTR_IS_FIXED_LENGTH (dest_type))
{
*dest_length = dest_precision;
}
else
{
*dest_length = MIN (src_padded_length, dest_precision);
}
dest_size = QSTR_NUM_BYTES (*dest_length);
*dest = (unsigned char *) db_private_alloc (NULL, dest_size + 1);
if (*dest == NULL)
{
assert (er_errid () != NO_ERROR);
error_status = er_errid ();
}
else
{
bit_ncat (*dest, 0, src, copy_length);
(void) memset ((char *) &((*dest)[copy_size]), (int) 0, (dest_size - copy_size));
}
return error_status;
}
/*
* qstr_coerce () -
*
* Arguments:
* src_string: (In) Source string
* dest_string: (Out) Coerced string
*
* Returns: DB_DATA_STATUS
*
* Errors:
*
* Note:
* This is a helper function which performs the actual coercion for
* character strings. It is called from db_char_string_coerce().
*
* If any loss of data due to truncation occurs DATA_STATUS_TRUNCATED
* is returned.
*
*/
static int
qstr_coerce (const unsigned char *src, int src_length, int src_precision, DB_TYPE src_type, INTL_CODESET src_codeset,
INTL_CODESET dest_codeset, unsigned char **dest, int *dest_length, int *dest_size, int dest_precision,
DB_TYPE dest_type, DB_DATA_STATUS * data_status)
{
int src_padded_length, copy_length, copy_size;
int alloc_size;
unsigned char *end_of_string;
int error_status = NO_ERROR;
*data_status = DATA_STATUS_OK;
*dest_size = 0;
/*
* <src_padded_length> is the length of the fully padded
* source string.
*/
if (QSTR_IS_FIXED_LENGTH (src_type))
{
src_padded_length = src_precision;
}
else
{
src_padded_length = src_length;
}
/*
* Some characters will be truncated if there is not enough
* precision in the destination string. If any of the
* truncated characters are non-pad characters, a truncation
* exception is raised.
*/
if (src_padded_length > dest_precision)
{
src_padded_length = dest_precision;
if ((src_length > src_padded_length)
&& (varchar_truncated (src, src_type, src_length, src_padded_length, src_codeset)))
{
*data_status = DATA_STATUS_TRUNCATED;
}
}
copy_length = MIN (src_length, src_padded_length);
/*
* For fixed-length destination strings...
* Allocate the destination precision size, copy the source
* string and pad the rest.
*
* For variable-length destination strings...
* Allocate enough for a fully padded source string, copy
* the source string and pad the rest.
*/
if (QSTR_IS_FIXED_LENGTH (dest_type))
{
*dest_length = dest_precision;
}
else
{
*dest_length = src_padded_length;
}
if (dest_codeset == INTL_CODESET_RAW_BYTES)
{
/* when coercing multibyte to binary charset, we just reinterpret each byte as one character */
if (INTL_CODESET_MULT (src_codeset) > 1)
{
assert (dest_precision != TP_FLOATING_PRECISION_VALUE);
intl_char_size ((unsigned char *) src, copy_length, src_codeset, ©_size);
if (copy_size > dest_precision)
{
*data_status = DATA_STATUS_TRUNCATED;
copy_size = dest_precision;
}
copy_length = copy_size;
if (QSTR_IS_VARIABLE_LENGTH (dest_type))
{
*dest_length = copy_length;
}
}
else
{
copy_size = copy_length;
}
}
else
{
/* copy_length = number of characters, count the bytes according to source codeset */
intl_char_size ((unsigned char *) src, copy_length, src_codeset, ©_size);
}
alloc_size = INTL_CODESET_MULT (dest_codeset) * (*dest_length);
/* fix allocation size enough to fit copy size plus pad size */
{
unsigned char pad[2];
int pad_size = 0;
intl_pad_char (dest_codeset, pad, &pad_size);
alloc_size = MAX (alloc_size, copy_size + (*dest_length - copy_length) * pad_size);
}
if (!alloc_size)
{
alloc_size = 1;
}
*dest = (unsigned char *) db_private_alloc (NULL, alloc_size + 1);
if (*dest == NULL)
{
assert (er_errid () != NO_ERROR);
error_status = er_errid ();
}
else
{
int conv_status = 0;
assert (copy_size >= 0);
if (copy_size == 0)
{
assert (alloc_size > 0);
**dest = '\0';
}
else if (src_codeset == INTL_CODESET_ISO88591 && dest_codeset == INTL_CODESET_UTF8)
{
int conv_size = 0;
assert (copy_size > 0);
conv_status = intl_fast_iso88591_to_utf8 (src, copy_size, dest, &conv_size);
copy_size = conv_size;
}
else if (src_codeset == INTL_CODESET_KSC5601_EUC && dest_codeset == INTL_CODESET_UTF8)
{
int conv_size = 0;
assert (copy_size > 0);
conv_status = intl_euckr_to_utf8 (src, copy_size, dest, &conv_size);
copy_size = conv_size;
}
else if (src_codeset == INTL_CODESET_UTF8 && dest_codeset == INTL_CODESET_KSC5601_EUC)
{
int conv_size = 0;
assert (copy_size > 0);
conv_status = intl_utf8_to_euckr (src, copy_size, dest, &conv_size);
copy_size = conv_size;
}
else if (src_codeset == INTL_CODESET_ISO88591 && dest_codeset == INTL_CODESET_KSC5601_EUC)
{
int conv_size = 0;
assert (copy_size > 0);
conv_status = intl_iso88591_to_euckr (src, copy_size, dest, &conv_size);
copy_size = conv_size;
}
else if (src_codeset == INTL_CODESET_UTF8 && dest_codeset == INTL_CODESET_ISO88591)
{
int conv_size = 0;
assert (copy_size > 0);
conv_status = intl_utf8_to_iso88591 (src, copy_size, dest, &conv_size);
copy_size = conv_size;
}
else if (src_codeset == INTL_CODESET_KSC5601_EUC && dest_codeset == INTL_CODESET_ISO88591)
{
int conv_size = 0;
assert (copy_size > 0);
conv_status = intl_euckr_to_iso88591 (src, copy_size, dest, &conv_size);
copy_size = conv_size;
}
else
{
assert (copy_size <= alloc_size);
if (src_codeset == INTL_CODESET_RAW_BYTES && (INTL_CODESET_MULT (dest_codeset) > 1))
{
int conv_size = 0;
if (dest_codeset == INTL_CODESET_UTF8)
{
intl_binary_to_utf8 (src, copy_size, dest, &conv_size);
}
else
{
assert (dest_codeset == INTL_CODESET_KSC5601_EUC);
intl_binary_to_euckr (src, copy_size, dest, &conv_size);
}
copy_size = conv_size;
}
else
{
(void) memcpy ((char *) *dest, (char *) src, (int) copy_size);
}
}
end_of_string =
qstr_pad_string ((unsigned char *) &((*dest)[copy_size]), (*dest_length - copy_length), dest_codeset);
*dest_size = CAST_STRLEN (end_of_string - (*dest));
if (conv_status != 0)
{
/* conversion error occured, re-count characters so that we comply to computed precision */
(void) intl_char_size (*dest, *dest_length, dest_codeset, dest_size);
end_of_string = (*dest) + *dest_size;
*end_of_string = '\0';
}
assert (*dest_size <= alloc_size);
}
return error_status;
}
/*
* qstr_position () -
*
* Arguments:
* sub_string: String fragment to search for within <src_string>.
* sub_length: Number of characters in sub_string.
* src_string: String to be searched.
* src_string_bound: Bound of string buffer:
* end of string buffer, if 'is_forward_search == true'
* start of string buffer, if 'is_forward_search == false'
* src_length: Number of characters in src_string.
* is_forward_search: forward search or backward search.
* codeset: Codeset of strings.
*
* Returns: int
*
* Errors:
*
* Note:
* This function accepts a source string <src_sring> and a string
* string fragment <sub_string> and returns the character position
* corresponding to the first occurrence of <sub_string> within
* <src_string>.
*
* This function works with National character strings.
*
*/
static int
qstr_position (const char *sub_string, const int sub_size, const int sub_length, const char *src_string,
const char *src_end, const char *src_string_bound, int src_length, int coll_id, bool is_forward_search,
int *position)
{
int error_status = NO_ERROR;
int dummy;
*position = 0;
if (sub_length == 0)
{
*position = 1;
}
else
{
int i, num_searches, current_position, result;
const unsigned char *ptr;
int char_size;
LANG_COLLATION *lc;
INTL_CODESET codeset;
lc = lang_get_collation (coll_id);
assert (lc != NULL);
codeset = lc->codeset;
/*
* Since the entire sub-string must be matched, a reduced
* number of compares <num_searches> are needed. A collation-based
* comparison will be used.
*/
if (lc->coll.uca_exp_num > 1 || lc->coll.count_contr > 0)
{
/* characters may not match one-by-one */
num_searches = src_length;
}
else
{
num_searches = src_length - sub_length + 1;
if (sub_length > src_length)
{
*position = 0;
return error_status;
}
}
/*
* Starting at the first position of the string, match the
* sub-string to the source string. If a match is not found,
* then increment into the source string by one character and
* try again. This is repeated until a match is found, or
* there are no more comparisons to be made.
*/
const unsigned char *usub_string = REINTERPRET_CAST (const unsigned char *, sub_string);
const unsigned char *usrc_end = REINTERPRET_CAST (const unsigned char *, src_end);
const unsigned char *usrc_string = REINTERPRET_CAST (const unsigned char *, src_string);
const unsigned char *usrc_string_bound = REINTERPRET_CAST (const unsigned char *, src_string_bound);
ptr = usrc_string;
current_position = 0;
result = 1;
for (i = 0; i < num_searches; i++)
{
result = QSTR_MATCH (coll_id, ptr, CAST_BUFLEN (usrc_end - ptr), usub_string, sub_size, NULL, false, &dummy);
current_position++;
if (result == 0)
{
break;
}
if (is_forward_search)
{
if (ptr >= usrc_string_bound)
{
break;
}
INTL_NEXT_CHAR (ptr, ptr, codeset, &char_size);
}
else
{
/* backward */
if (ptr > usrc_string_bound)
{
ptr = intl_prev_char (ptr, usrc_string_bound, codeset, &char_size);
}
else
{
break;
}
}
}
/*
* Return the position of the match, if found.
*/
if (result == 0)
{
*position = current_position;
}
}
return error_status;
}
/*
* qstr_bit_position () -
*
* Arguments:
* sub_string: String fragment to search for within <src_string>.
* sub_length: Number of characters in sub_string.
* src_string: String to be searched.
* src_length: Number of characters in src_string.
*
* Returns: int
*
* Errors:
*
* Note:
* This function accepts a source string <src_sring> and a string
* string fragment <sub_string> and returns the bit position
* corresponding to the first occurance of <sub_string> within
* <src_string>.
*
*/
static int
qstr_bit_position (const unsigned char *sub_string, int sub_length, const unsigned char *src_string, int src_length,
int *position)
{
int error_status = NO_ERROR;
*position = 0;
if (sub_length == 0)
{
*position = 1;
}
else if (sub_length > src_length)
{
*position = 0;
}
else
{
int i, num_searches, result;
int sub_size, sub_remainder, shift_amount;
unsigned char *ptr, *tmp_string, tmp_byte, mask;
num_searches = src_length - sub_length + 1;
sub_size = QSTR_NUM_BYTES (sub_length);
sub_remainder = sub_length % BYTE_SIZE;
shift_amount = BYTE_SIZE - sub_remainder;
mask = 0xff << shift_amount;
/*
* We will be manipulating the source string prior to
* comparison. So that we do not corrupt the source string,
* we'll allocate a storage area so that we can make a copy
* of the string. This copy need only be he length of the
* sub-string since that is the limit of the comparison.
*/
tmp_string = (unsigned char *) db_private_alloc (NULL, (size_t) sub_size + 1);
if (tmp_string == NULL)
{
assert (er_errid () != NO_ERROR);
error_status = er_errid ();
}
else
{
ptr = (unsigned char *) src_string;
/*
* Make a copy of the source string.
* Initialize the bit index.
*/
(void) memcpy ((char *) tmp_string, (char *) ptr, sub_size);
i = 0;
result = 1;
while ((i < num_searches) && (result != 0))
{
/* Pad the irrelevant bits of the source string with 0's */
tmp_byte = tmp_string[sub_size - 1];
tmp_string[sub_size - 1] &= mask;
/* Compare the source string with the sub-string */
result = memcmp (sub_string, tmp_string, sub_size);
/* Restore the padded byte to its original value */
tmp_string[sub_size - 1] = tmp_byte;
/* Shift the copied source string left one bit */
(void) shift_left (tmp_string, sub_size);
i++;
/*
* Every time we hit a byte boundary,
* Move on to the next byte of the source string.
*/
if ((i % BYTE_SIZE) == 0)
{
ptr++;
memcpy (tmp_string, ptr, sub_size);
}
}
db_private_free_and_init (NULL, tmp_string);
/*
* If a match was found, then return the position
* of the match.
*/
if (result == 0)
{
*position = i;
}
}
}
return error_status;
}
/*
* shift_left () -
*
* Arguments:
* bit_string: Byte array representing a bit string.
* bit_string_size: Number of bytes in the array.
*
* Returns: int
*
* Errors:
*
* Note:
* Shift the bit string left one bit. The left most bit is shifted out
* and returned. A 0 is inserted into the rightmost bit position.
* The entire array is shifted regardless of the number of significant
* bits in the array.
*
*/
static int
shift_left (unsigned char *bit_string, int bit_string_size)
{
int i, highest_bit;
highest_bit = ((bit_string[0] & 0x80) != 0);
bit_string[0] = bit_string[0] << 1;
for (i = 1; i < bit_string_size; i++)
{
if (bit_string[i] & 0x80)
{
bit_string[i - 1] |= 0x01;
}
bit_string[i] = bit_string[i] << 1;
}
return highest_bit;
}
/*
* qstr_substring () -
*
* Arguments:
* src_string: Source string.
* start_position: Starting character position of sub-string.
* extraction_length: Length of sub-string.
* sub_string: Returned sub-string.
*
* Returns: void
*
* Errors:
*
* Note:
* Extract the sub-string from the source string. The sub-string is
* specified by a starting position and length.
*
* This functions works on character and national character strings.
*
*/
static int
qstr_substring (const unsigned char *src, int src_length, int start, int length, INTL_CODESET codeset,
unsigned char **r, int *r_length, int *r_size)
{
int error_status = NO_ERROR;
const unsigned char *sub;
int src_size, leading_bytes;
*r_size = 0;
/* Get the size of the source string. */
intl_char_size ((unsigned char *) src, src_length, codeset, &src_size);
/*
* Perform some error chaecking.
* If the starting position is < 1, then set it to 1.
* If the starting position is after the end of the source string,
* then set the sub-string length to 0.
* If the sub-string length will extend beyond the end of the source string,
* then shorten the sub-string length to fit.
*/
if (start < 1)
{
start = 1;
}
if (start > src_length)
{
start = 1;
length = 0;
}
if ((length < 0) || ((start + length - 1) > src_length))
{
length = src_length - start + 1;
}
*r_length = length;
/*
* Get a pointer to the start of the sub-string and the
* size of the sub-string.
*
* Compute the starting byte of the sub-string.
* Compute the length of the sub-string in bytes.
*/
intl_char_size ((unsigned char *) src, (start - 1), codeset, &leading_bytes);
sub = &(src[leading_bytes]);
intl_char_size ((unsigned char *) sub, *r_length, codeset, r_size);
*r = (unsigned char *) db_private_alloc (NULL, (size_t) ((*r_size) + 1));
if (*r == NULL)
{
assert (er_errid () != NO_ERROR);
error_status = er_errid ();
}
else
{
(void) memcpy (*r, sub, *r_size);
}
return error_status;
}
/*
* qstr_bit_substring () -
*
* Arguments:
* src_string: Source string.
* start_position: Starting character position of sub-string.
* extraction_length: Length of sub-string.
* sub_string: Returned sub-string.
*
* Returns: void
*
* Errors:
*
* Note:
* Extract the sub-string from the source string. The sub-string is
* specified by a starting position and length.
*
* This functions works on bit strings.
*
*/
static int
qstr_bit_substring (const unsigned char *src, int src_length, int start, int length, unsigned char **r, int *r_length)
{
int src_size, sub_size, rem;
unsigned char trailing_mask;
int error_status = NO_ERROR;
src_size = QSTR_NUM_BYTES (src_length);
/*
* Perform some error checking.
* If the starting position is < 1, then set it to 1.
* If the starting position is after the end of the source
* string, then set the sub-string length to 0.
* If the sub-string length will extend beyond the end of the
* source string, then shorten the sub-string length to fit.
*/
if (start < 1)
{
start = 1;
}
if (start > src_length)
{
start = 1;
length = 0;
}
if ((length < 0) || ((start + length - 1) > src_length))
{
length = src_length - start + 1;
}
sub_size = QSTR_NUM_BYTES (length);
*r_length = length;
rem = length % BYTE_SIZE;
if (rem == 0)
{
trailing_mask = 0xff;
}
else
{
trailing_mask = 0xff << (BYTE_SIZE - rem);
}
/*
* Allocate storage for the sub-string.
* Copy the sub-string.
*/
*r = (unsigned char *) db_private_alloc (NULL, (size_t) sub_size + 1);
if (*r == NULL)
{
assert (er_errid () != NO_ERROR);
error_status = er_errid ();
}
else
{
left_nshift (src, src_size, (start - 1), *r, sub_size);
(*r)[sub_size - 1] &= trailing_mask;
}
return error_status;
}
/*
* left_nshift () -
*
* Arguments:
* bit_string: Byte array containing the bit string.
* bit_string_size: Size of the bit array in bytes.
* shift_amount: Number of bit positions to shift by.
* range: 0 <= shift_amount
* r: Pointer to result buffer where the shifted bit
* array will be stored.
* r_size: Size of the result array in bytes.
*
* Returns: void
*
* Errors:
*
* Note:
* Shift the bit string left <shift_amount> bits. The left most bits
* are shifted out. 0's are inserted into the rightmost bit positions.
* The entire array is shifted regardless of the number of significant
* bits in the array.
*
*/
static void
left_nshift (const unsigned char *bit_string, int bit_string_size, int shift_amount, unsigned char *r, int r_size)
{
int i, shift_bytes, shift_bits, adj_bit_string_size;
const unsigned char *ptr;
shift_bytes = shift_amount / BYTE_SIZE;
shift_bits = shift_amount % BYTE_SIZE;
ptr = &(bit_string[shift_bytes]);
adj_bit_string_size = bit_string_size - shift_bytes;
for (i = 0; i < r_size; i++)
{
if (i < (adj_bit_string_size - 1))
{
r[i] = ((ptr[i] << shift_bits) | (ptr[i + 1] >> (BYTE_SIZE - shift_bits)));
}
else if (i == (adj_bit_string_size - 1))
{
r[i] = (ptr[i] << shift_bits);
}
else
{
r[i] = 0;
}
}
}
/*
* The version below handles multibyte character sets by promoting all
* characters to two bytes each. Unfortunately, the current implementation
* of the regular expression package has some limitations with characters
* that are not char sized. The above version works with char sized
* sets only and therefore will not work with national character sets.
*/
/*
* qstr_ffs () -
* Returns: int
* v: (IN) Source string.
*
*
* Errors:
*
* Note:
* Finds the first bit set in the passed argument and returns
* the index of that bit. Bits are numbered starting at 1
* from the right. A return value of 0 indicates that the value
* passed is zero.
*
*/
static int
qstr_ffs (int v)
{
int nbits;
int i = 0;
int position = 0;
unsigned int uv = (unsigned int) v;
nbits = sizeof (int) * 8;
if (uv != 0)
{
while ((i < nbits) && (position == 0))
{
if (uv & 0x01)
{
position = i + 1;
}
i++;
uv >>= 1;
}
}
return position;
}
/*
* hextoi () -
*
* Arguments:
* hex_char: (IN) Character containing ASCII hex character
*
* Returns: int
*
* Errors:
*
* Note:
* Returns the decimal value associated with the ASCII hex character.
* Will return a -1 if hex_char is not a hexadecimal ASCII character.
*
*/
static int
hextoi (char hex_char)
{
if ((hex_char >= '0') && (hex_char <= '9'))
{
return (hex_char - '0');
}
else if ((hex_char >= 'A') && (hex_char <= 'F'))
{
return (hex_char - 'A' + 10);
}
else if ((hex_char >= 'a') && (hex_char <= 'f'))
{
return (hex_char - 'a' + 10);
}
else
{
return (-1);
}
}
/*
* set_time_argument() - construct struct tm
* return:
* dest(out):
* year(in):
* month(in):
* day(in):
* hour(in):
* min(in):
* sec(in):
*/
static void
set_time_argument (struct tm *dest, int year, int month, int day, int hour, int min, int sec)
{
if (year >= 1900)
{
dest->tm_year = year - 1900;
}
else
{
dest->tm_year = -1;
}
dest->tm_mon = month - 1;
dest->tm_mday = day;
dest->tm_hour = hour;
dest->tm_min = min;
dest->tm_sec = sec;
dest->tm_isdst = -1;
}
/*
* calc_unix_timestamp() - calculates UNIX timestamp
* return:
* time_argument(in):
*/
static long
calc_unix_timestamp (struct tm *time_argument)
{
time_t result;
if (time_argument != NULL)
{
/* validation for tm fields in order to cover for mktime conversion's like 40th of Sept equals 10th of Oct */
if (time_argument->tm_year < 0 || time_argument->tm_year > 9999 || time_argument->tm_mon < 0
|| time_argument->tm_mon > 11 || time_argument->tm_mday < 1 || time_argument->tm_mday > 31
|| time_argument->tm_hour < 0 || time_argument->tm_hour > 23 || time_argument->tm_min < 0
|| time_argument->tm_min > 59 || time_argument->tm_sec < 0 || time_argument->tm_sec > 59)
{
return -1L;
}
result = mktime (time_argument);
}
else
{
result = time (NULL);
}
if (result < (time_t) 0)
{
return -1L;
}
return (long) result;
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* parse_for_next_int () -
*
* Arguments:
* ch: char position from which we start parsing
* output: integer read
*
* Returns: -1 if error, 0 if success
*
* Note:
* parses a string for integers while skipping non-alpha delimitators
*/
static int
parse_for_next_int (char **ch, char *output)
{
int i;
/* we need in fact only 6 (upper bound for the integers we want) */
char buf[16];
i = 0;
memset (buf, 0, sizeof (buf));
/* trailing zeroes - accept only 2 (for year 00 which is short for 2000) */
while (**ch == '0')
{
if (i < 2)
{
buf[i++] = **ch;
}
(*ch)++;
}
while (i < 6 && char_isdigit (**ch) && **ch != 0)
{
buf[i++] = **ch;
(*ch)++;
}
if (i > 6)
{
return -1;
}
strcpy (output, buf);
/* skip all delimitators */
while (**ch != 0 && !char_isalpha (**ch) && !char_isdigit (**ch))
{
(*ch)++;
}
return 0;
}
#endif
/*
* db_unix_timestamp () -
*
* Arguments:
* src_date: datetime from which we calculate timestamp
*
* Returns: int
*
* Errors:
*
* Note:
* Returns a Unix timestamp (seconds since '1970-01-01 00:00:00' UTC)
*/
int
db_unix_timestamp (const DB_VALUE * src_date, DB_VALUE * result_timestamp)
{
DB_TYPE type = DB_TYPE_UNKNOWN;
int error_status = NO_ERROR;
time_t ts = 0;
if (DB_IS_NULL (src_date))
{
db_make_null (result_timestamp);
return error_status;
}
type = DB_VALUE_DOMAIN_TYPE (src_date);
switch (type)
{
case DB_TYPE_VARCHAR:
case DB_TYPE_CHAR:
case DB_TYPE_DATETIME:
case DB_TYPE_DATE:
{
DB_VALUE *dt_tz_p;
DB_VALUE dt;
TP_DOMAIN *tp_datetime;
TP_DOMAIN *tp_datetimetz;
DB_DATETIME datetime;
DB_TIMESTAMP timestamp;
DB_DATE date;
dt_tz_p = &dt;
if (type != DB_TYPE_DATETIME && type != DB_TYPE_DATE)
{
tp_datetimetz = db_type_to_db_domain (DB_TYPE_DATETIMETZ);
if (tp_value_cast (src_date, dt_tz_p, tp_datetimetz, false) == DOMAIN_COMPATIBLE)
{
type = DB_TYPE_DATETIMETZ;
}
else
{
tp_datetime = db_type_to_db_domain (DB_TYPE_DATETIME);
if (tp_value_cast (src_date, dt_tz_p, tp_datetime, false) == DOMAIN_COMPATIBLE)
{
type = DB_TYPE_DATETIME;
/* Clear error from cast to DB_TYPE_DATETIMETZ. */
er_clear ();
}
else
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
db_make_null (result_timestamp);
return ER_FAILED;
}
}
assert (DB_VALUE_TYPE (dt_tz_p) == type);
}
else
{
dt_tz_p = (DB_VALUE *) src_date;
}
/* The supported datetime range is '1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC */
if (type == DB_TYPE_DATETIMETZ)
{
DB_DATETIMETZ *datetimetz;
datetimetz = db_get_datetimetz (dt_tz_p);
datetime = datetimetz->datetime;
datetime.time /= 1000;
}
else if (type == DB_TYPE_DATETIME)
{
datetime = *db_get_datetime (dt_tz_p);
datetime.time /= 1000;
}
else
{
assert (type == DB_TYPE_DATE);
date = *db_get_date (dt_tz_p);
datetime.date = date;
datetime.time = 0;
}
if (type == DB_TYPE_DATE || type == DB_TYPE_DATETIME)
{
error_status = db_timestamp_encode (×tamp, &datetime.date, &datetime.time);
if (error_status != NO_ERROR)
{
return error_status;
}
}
else
{
assert (type == DB_TYPE_DATETIMETZ);
error_status = db_timestamp_encode_utc (&datetime.date, &datetime.time, ×tamp);
if (error_status != NO_ERROR)
{
return error_status;
}
}
db_make_int (result_timestamp, timestamp);
return NO_ERROR;
}
case DB_TYPE_DATETIMETZ:
case DB_TYPE_DATETIMELTZ:
{
DB_DATETIMETZ *datetimetz;
DB_DATETIME datetime;
DB_TIMESTAMP timestamp;
if (type == DB_TYPE_DATETIMETZ)
{
datetimetz = db_get_datetimetz (src_date);
datetime = datetimetz->datetime;
}
else
{
datetime = *db_get_datetime (src_date);
}
datetime.time /= 1000;
error_status = db_timestamp_encode_utc (&datetime.date, &datetime.time, ×tamp);
if (error_status != NO_ERROR)
{
return error_status;
}
db_make_int (result_timestamp, timestamp);
return NO_ERROR;
}
/* a TIMESTAMP format */
case DB_TYPE_TIMESTAMP:
case DB_TYPE_TIMESTAMPLTZ:
/* The supported timestamp range is '1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC */
ts = *db_get_timestamp (src_date);
/* supplementary conversion from long to int will be needed on 64 bit platforms. */
db_make_int (result_timestamp, (int) ts);
return NO_ERROR;
case DB_TYPE_TIMESTAMPTZ:
{
DB_TIMESTAMPTZ *timestamp_tz;
timestamp_tz = db_get_timestamptz (src_date);
ts = timestamp_tz->timestamp;
db_make_int (result_timestamp, (int) ts);
return NO_ERROR;
}
default:
db_make_null (result_timestamp);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_FAILED;
}
db_make_null (result_timestamp);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_FAILED;
}
/*
* db_datetime_to_timestamp () - create a timestamp DB_VALUE from a datetime
* DB_VALUE
*
* src_datetime(in):
* result_timestamp(in):
* return: ERROR_CODE
*/
int
db_datetime_to_timestamp (const DB_VALUE * src_datetime, DB_VALUE * result_timestamp)
{
DB_DATETIME *tmp_datetime;
DB_DATE tmp_date;
DB_TIME tmp_time;
DB_TIMESTAMP tmp_timestamp;
int error;
DB_VALUE temp, *temp_p;
bool same_argument = (src_datetime == result_timestamp);
if (DB_IS_NULL (src_datetime))
{
db_make_null (result_timestamp);
return NO_ERROR;
}
if (same_argument)
{
/* if the result argument is the same with the source argument, then use a temporary value for creating the
* timestamp */
temp_p = &temp;
}
else
{
/* the result_timestamp value can be used and no other temporary values are needed */
temp_p = result_timestamp;
}
error = db_value_domain_init (temp_p, DB_TYPE_TIMESTAMP, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
if (error != NO_ERROR)
{
/* error message has been set */
return error;
}
tmp_datetime = db_get_datetime (src_datetime);
tmp_date = tmp_datetime->date;
tmp_time = tmp_datetime->time / 1000;
error = db_timestamp_encode_ses (&tmp_date, &tmp_time, &tmp_timestamp, NULL);
if (error != NO_ERROR)
{
/* error message has been set */
return error;
}
db_make_timestamp (temp_p, tmp_timestamp);
if (same_argument)
{
/* if src_datetime was the same with result_timestamp, copy the result from temp, and release the temporary value */
pr_clone_value (temp_p, result_timestamp);
}
return NO_ERROR;
}
int
db_timestamp_to_datetime (const DB_VALUE * src_timestamp, DB_VALUE * result_datetime)
{
time_t sec;
struct tm tm_val;
DB_DATETIME datetime = DATETIME_NULL_VALUE;
sec = (time_t) * db_get_timestamp (src_timestamp);
if (sec != 0)
{
if (localtime_r (&sec, &tm_val) == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SYSTEM_DATE, 0);
return ER_SYSTEM_DATE;
}
db_datetime_encode (&datetime, tm_val.tm_mon + 1, tm_val.tm_mday,
tm_val.tm_year + 1900, tm_val.tm_hour, tm_val.tm_min, tm_val.tm_sec, 0);
}
db_make_datetime (result_datetime, &datetime);
return NO_ERROR;
}
/*
* db_get_date_dayofyear () - compute day of year from a date type value
*
* Arguments:
* src_date: datetime from which to compute the day of year
*
* Returns: int
*/
int
db_get_date_dayofyear (const DB_VALUE * src_date, DB_VALUE * result)
{
int error_status = NO_ERROR;
int month = 0, day = 0, year = 0;
int second = 0, minute = 0, hour = 0;
int ms = 0;
int day_of_year = 0;
if (DB_IS_NULL (src_date))
{
db_make_null (result);
return NO_ERROR;
}
/* get the date/time information from src_date */
error_status = db_get_datetime_from_dbvalue (src_date, &year, &month, &day, &hour, &minute, &second, &ms, NULL);
if (error_status != NO_ERROR)
{
error_status = ER_DATE_CONVERSION;
goto error_exit;
}
if (year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 && second == 0 && ms == 0)
{
error_status = ER_ATTEMPT_TO_USE_ZERODATE;
goto error_exit;
}
day_of_year = db_get_day_of_year (year, month, day);
db_make_int (result, day_of_year);
return NO_ERROR;
error_exit:
/* This function should return NULL if src_date is an invalid parameter or Zero date. Clear the error generated by
* the function call and return null. */
er_clear ();
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
return NO_ERROR;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
#if !defined (SERVER_MODE)
/*
* db_get_date_weekday () - compute day of week from a date type value
*
* Arguments:
* src_date: datetime from which to compute the week day
* mode : the mode in which week days are numbered
* 0 = Monday, ..., 6 = Sunday or
* 1 = Sunday, ..., 7 = Saturday
*
* Returns: int
*/
int
db_get_date_weekday (const DB_VALUE * src_date, const int mode, DB_VALUE * result)
{
int error_status = NO_ERROR;
int month = 0, day = 0, year = 0;
int second = 0, minute = 0, hour = 0;
int ms = 0;
int day_of_week = 0;
if (DB_IS_NULL (src_date))
{
db_make_null (result);
return NO_ERROR;
}
/* get the date/time information from src_date */
error_status = db_get_datetime_from_dbvalue (src_date, &year, &month, &day, &hour, &minute, &second, &ms, NULL);
if (error_status != NO_ERROR)
{
error_status = ER_DATE_CONVERSION;
goto error_exit;
}
if (year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 && second == 0 && ms == 0)
{
error_status = ER_ATTEMPT_TO_USE_ZERODATE;
goto error_exit;
}
/* 0 = Sunday, 1 = Monday, etc */
day_of_week = db_get_day_of_week (year, month, day);
switch (mode)
{
case PT_WEEKDAY:
/* 0 = Monday, 1 = Tuesday, ..., 6 = Sunday */
if (day_of_week == 0)
{
day_of_week = 6;
}
else
{
day_of_week--;
}
db_make_int (result, day_of_week);
break;
case PT_DAYOFWEEK:
/* 1 = Sunday, 2 = Monday, ..., 7 = Saturday */
day_of_week++;
db_make_int (result, day_of_week);
break;
default:
assert (false);
db_make_null (result);
break;
}
return NO_ERROR;
error_exit:
/* This function should return NULL if src_date is an invalid parameter or zero date. Clear the error generated by
* the function call and return null. */
er_clear ();
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
return NO_ERROR;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
#endif /* !defined (SERVER_MODE) */
/*
* db_get_date_quarter () - compute quarter from a date type value
*
* Arguments:
* src_date: datetime from which to compute the quarter
*
* Returns: int
*/
int
db_get_date_quarter (const DB_VALUE * src_date, DB_VALUE * result)
{
int month = 0, day = 0, year = 0;
int second = 0, minute = 0, hour = 0;
int ms = 0;
char const *endp = NULL;
int retval;
if (DB_IS_NULL (src_date))
{
db_make_null (result);
return NO_ERROR;
}
/* get the date/time information from src_date */
retval = db_get_datetime_from_dbvalue (src_date, &year, &month, &day, &hour, &minute, &second, &ms, &endp);
if (retval != NO_ERROR || (endp && *endp && !char_isspace (*endp)))
{
er_clear ();
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
return NO_ERROR;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
return ER_DATE_CONVERSION;
}
if (year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 && second == 0 && ms == 0)
{
/* This function should return 0 if src_date is zero date */
db_make_int (result, 0);
}
/* db_datetime_decode returned NO_ERROR so we can calculate the quarter */
else if (month == 0)
{
assert (false);
db_make_int (result, 0);
}
else if (month < 0 || month > 12)
{
assert (false);
db_make_null (result);
}
else
{
const int quarter = (month - 1) / 3 + 1;
db_make_int (result, quarter);
}
return NO_ERROR;
}
/*
* db_get_date_totaldays () - compute the number of days from the date 0 AD
* until the day represented by src_date
*
* Arguments:
* src_date: datetime from which to compute the number of days
*
* Returns: int
*/
int
db_get_date_totaldays (const DB_VALUE * src_date, DB_VALUE * result)
{
int error_status = NO_ERROR;
int month = 0, day = 0, year = 0;
int second = 0, minute = 0, hour = 0;
int ms = 0;
int leap_years = 0, total_days = 0, days_this_year = 0;
if (DB_IS_NULL (src_date))
{
db_make_null (result);
return NO_ERROR;
}
/* get the date/time information from src_date */
error_status = db_get_datetime_from_dbvalue (src_date, &year, &month, &day, &hour, &minute, &second, &ms, NULL);
if (error_status != NO_ERROR)
{
error_status = ER_DATE_CONVERSION;
goto error_exit;
}
if (year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 && second == 0 && ms == 0)
{
error_status = ER_ATTEMPT_TO_USE_ZERODATE;
goto error_exit;
}
leap_years = count_leap_years_up_to (year - 1);
days_this_year = db_get_day_of_year (year, month, day);
total_days = year * 365 + leap_years + days_this_year;
db_make_int (result, total_days);
return NO_ERROR;
error_exit:
er_clear ();
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
return NO_ERROR;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
/*
* db_get_date_from_days () - computes a date by adding the number of days
* represented by src to the year 0 AD
*
* Arguments:
* src: number from which to compute the date
*
* Returns: date
*/
int
db_get_date_from_days (const DB_VALUE * src, DB_VALUE * result)
{
int year = 0;
int month = 0;
int day = 0;
int week = 0;
int int_value = 0;
int julian_day = 0;
if (DB_IS_NULL (src))
{
db_make_null (result);
return NO_ERROR;
}
if (db_round_dbvalue_to_int (src, &int_value) != NO_ERROR)
{
int_value = 0;
}
/* The count should start from day 0001-01-01. Because year 0 is considered special, this function should return
* 0000-00-00 for less than 366 days */
if (int_value < 366)
{
db_make_date (result, 0, 0, 0);
return NO_ERROR;
}
julian_day = julian_encode (1, 1, 1);
/* Subtract 364 from the Julian Day to start counting from 0001-01-01 */
julian_day += int_value - 364;
julian_decode (julian_day, &month, &day, &year, &week);
if (year > 9999)
{
db_make_date (result, 0, 0, 0);
return NO_ERROR;
}
db_make_date (result, month, day, year);
return NO_ERROR;
}
/*
* db_add_days_to_year () - computes a date by adding the number of days
* contained in src_days to the date 01/01/src_year
*
* Arguments:
* src_year: the year to add days to
* src_days: the number of days to add
*
* Returns: date
*/
int
db_add_days_to_year (const DB_VALUE * src_year, const DB_VALUE * src_days, DB_VALUE * result)
{
int year_value = 0;
int days_value = 0;
int julian_day = 0;
int year = 0, month = 0, day = 0, week = 0;
if (DB_IS_NULL (src_year) || DB_IS_NULL (src_days))
{
db_make_null (result);
return NO_ERROR;
}
if (db_round_dbvalue_to_int (src_year, &year_value) != NO_ERROR)
{
goto error;
}
if (db_round_dbvalue_to_int (src_days, &days_value) != NO_ERROR)
{
goto error;
}
/* days<=0 or year_value <0 are invalid values */
if (days_value <= 0 || year_value < 0)
{
goto error;
}
/* correct the year value by applying the following rules: - if 0 <= year <= 69 then consider year as 20yy (e.g.: 33
* is 2033) - if 70 <= year <= 99 then consider year as 19yy (e.g.: 71 is 1971) */
if (year_value < 70)
{
year_value += 2000;
}
else if (year_value >= 70 && year_value < 100)
{
year_value += 1900;
}
julian_day = julian_encode (1, 1, year_value);
julian_day += days_value - 1;
julian_decode (julian_day, &month, &day, &year, &week);
if (year > 9999)
{
goto error;
}
db_make_date (result, month, day, year);
return NO_ERROR;
error:
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_clear ();
return NO_ERROR;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
return ER_DATE_CONVERSION;
}
/*
* db_convert_to_time () - creates a time value from the given hour, minute,
* second
*
* Arguments:
* src_hour: the hour
* src_minute: the minute
* src_second: the second
*
* Returns: date
*/
int
db_convert_to_time (const DB_VALUE * src_hour, const DB_VALUE * src_minute, const DB_VALUE * src_second,
DB_VALUE * result)
{
int hour = 0, minute = 0, second = 0;
if (DB_IS_NULL (src_hour) || DB_IS_NULL (src_minute) || DB_IS_NULL (src_second))
{
db_make_null (result);
return NO_ERROR;
}
if (db_round_dbvalue_to_int (src_hour, &hour) != NO_ERROR || db_round_dbvalue_to_int (src_minute, &minute) != NO_ERROR
|| db_round_dbvalue_to_int (src_second, &second) != NO_ERROR)
{
goto error;
}
if (minute >= 60 || minute < 0 || second >= 60 || second < 0 || hour >= 24 || hour < 0)
{
goto error;
}
db_make_time (result, hour, minute, second);
return NO_ERROR;
error:
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_clear ();
return NO_ERROR;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIME_CONVERSION, 0);
return ER_TIME_CONVERSION;
}
/*
* db_convert_sec_to_time() - convert a value that represents a number of
* seconds into a time value
* (hours:minutes:seconds)
*
* Arguments:
* src : value to be converted to the time value
*
* Returns: int
*
* Note:
* This function returns values in the interval 00:00:00, 23:59:59. If the
* value passed as argument does not fall in this interval then this function
* returns the nearest interval limit.
*
*/
int
db_convert_sec_to_time (const DB_VALUE * src, DB_VALUE * result)
{
int hours = 0;
int minutes = 0;
int seconds = 0;
int int_value = 0;
int err;
if (DB_IS_NULL (src))
{
db_make_null (result);
return NO_ERROR;
}
err = db_round_dbvalue_to_int (src, &int_value);
if (err != NO_ERROR)
{
int_value = 0;
}
if (int_value < 0 || err == ER_OUT_OF_VIRTUAL_MEMORY)
{
goto error;
}
hours = int_value / 3600;
minutes = (int_value - hours * 3600) / 60;
seconds = int_value % 60;
if (hours > 23)
{
goto error;
}
db_make_time (result, hours, minutes, seconds);
return NO_ERROR;
error:
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_clear ();
return NO_ERROR;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIME_CONVERSION, 0);
return ER_TIME_CONVERSION;
}
/*
* db_convert_time_to_sec () - compute the number of seconds that have elapsed
* since 00:00:00 to a given time
*
* Arguments:
* src_date: time from which to compute the number of seconds
*
* Returns: int
*/
int
db_convert_time_to_sec (const DB_VALUE * src_date, DB_VALUE * result)
{
int error_status = NO_ERROR;
int second = 0, minute = 0, hour = 0, millisecond = 0;
int total_seconds = 0;
if (DB_IS_NULL (src_date))
{
db_make_null (result);
return NO_ERROR;
}
error_status = db_get_time_from_dbvalue (src_date, &hour, &minute, &second, &millisecond);
if (error_status != NO_ERROR)
{
er_clear ();
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
return NO_ERROR;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIME_CONVERSION, 0);
return ER_TIME_CONVERSION;
}
total_seconds = hour * 3600 + minute * 60 + second;
db_make_int (result, total_seconds);
return NO_ERROR;
}
/*
* db_round_dbvalue_to_int() - converts a db value to an integer rounding the
* value to the nearest integer
*
* Arguments:
* src : value to be converted to int
* Return: NO_ERROR or error code.
* Note: for string source values the function will return the converted
* number or 0 otherwise.
*/
static int
db_round_dbvalue_to_int (const DB_VALUE * src, int *result)
{
DB_TYPE src_type = DB_VALUE_DOMAIN_TYPE (src);
switch (src_type)
{
case DB_TYPE_SMALLINT:
*result = db_get_short (src);
return NO_ERROR;
case DB_TYPE_INTEGER:
*result = db_get_int (src);
return NO_ERROR;
case DB_TYPE_FLOAT:
{
float x = db_get_float (src);
*result = (int) ((x) > 0 ? ((x) + .5) : ((x) - .5));
return NO_ERROR;
}
case DB_TYPE_DOUBLE:
{
double x = db_get_double (src);
*result = (int) ((x) > 0 ? ((x) + .5) : ((x) - .5));
return NO_ERROR;
}
case DB_TYPE_NUMERIC:
{
double x = 0;
numeric_coerce_num_to_double (db_locate_numeric ((DB_VALUE *) src), DB_VALUE_SCALE (src), &x);
*result = (int) ((x) > 0 ? ((x) + .5) : ((x) - .5));
return NO_ERROR;
}
case DB_TYPE_BIGINT:
*result = (int) db_get_bigint (src);
return NO_ERROR;
case DB_TYPE_MONETARY:
{
double x = (db_get_monetary (src))->amount;
*result = (int) ((x) > 0 ? ((x) + .5) : ((x) - .5));
return NO_ERROR;
}
case DB_TYPE_STRING:
case DB_TYPE_CHAR:
{
double x;
DB_VALUE val;
int error_status = tp_value_string_to_double (src, &val);
if (error_status != NO_ERROR)
{
return error_status;
}
x = db_get_double (&val);
*result = (int) ((x) > 0 ? ((x) + .5) : ((x) - .5));
return NO_ERROR;
}
case DB_TYPE_DATE:
{
/* convert the date to yyyymmdd as integer */
int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
int ms = 0;
if (db_get_datetime_from_dbvalue (src, &year, &month, &day, &hour, &second, &minute, &ms, NULL) != NO_ERROR)
{
er_clear ();
*result = 0;
}
else
{
*result = (int) (year * 10000 * month * 100 * day);
}
return NO_ERROR;
}
case DB_TYPE_DATETIME:
case DB_TYPE_TIMESTAMP:
{
/* convert the date to yyyymmddhhmmss as integer */
int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
int ms = 0;
if (db_get_datetime_from_dbvalue (src, &year, &month, &day, &hour, &second, &minute, &ms, NULL) != NO_ERROR)
{
er_clear ();
*result = 0;
return ER_FAILED;
}
*result = (int) (year * 10000000000 + month * 100000000 + day * 1000000 + hour * 10000 + minute * 100 + second);
return NO_ERROR;
}
case DB_TYPE_TIME:
{
int hour = 0, minute = 0, second = 0, millisecond = 0;
if (db_get_time_from_dbvalue (src, &hour, &minute, &second, &millisecond) != NO_ERROR)
{
er_clear ();
*result = 0;
return ER_FAILED;
}
*result = hour * 10000 + minute * 100 + second;
return NO_ERROR;
}
default:
*result = 0;
return ER_FAILED;
}
*result = 0;
return ER_FAILED;
}
/*
* db_get_date_week () - compute the week number of a given date time
*
* Arguments:
* src_date: datetime from which to compute the week number
* mode: specifies the mode in which to count the weeks
*
* Returns: int
*/
int
db_get_date_week (const DB_VALUE * src_date, const DB_VALUE * mode, DB_VALUE * result)
{
int error_status = NO_ERROR;
int month = 0, day = 0, year = 0;
int second = 0, minute = 0, hour = 0;
int ms = 0;
int calc_mode = prm_get_integer_value (PRM_ID_DEFAULT_WEEK_FORMAT);
int week_number = 0;
if (DB_IS_NULL (src_date))
{
db_make_null (result);
return NO_ERROR;
}
/* get the date/time information from src_date */
error_status = db_get_datetime_from_dbvalue (src_date, &year, &month, &day, &hour, &minute, &second, &ms, NULL);
if (error_status != NO_ERROR)
{
error_status = ER_DATE_CONVERSION;
goto error_exit;
}
if (year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 && second == 0 && ms == 0)
{
error_status = ER_ATTEMPT_TO_USE_ZERODATE;
goto error_exit;
}
if (DB_IS_NULL (mode))
{
calc_mode = prm_get_integer_value (PRM_ID_DEFAULT_WEEK_FORMAT);
}
else
{
error_status = db_round_dbvalue_to_int (mode, &calc_mode);
if (error_status != NO_ERROR)
{
error_status = ER_DATE_CONVERSION;
goto error_exit;
}
}
/* check boundaries for calc_mode */
if (calc_mode < 0 || calc_mode > 7)
{
error_status = ER_DATE_CONVERSION;
goto error_exit;
}
week_number = db_get_week_of_year (year, month, day, calc_mode);
db_make_int (result, week_number);
return NO_ERROR;
error_exit:
er_clear ();
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
return NO_ERROR;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
#if !defined (SERVER_MODE)
/*
* db_get_date_item () - compute an item from a datetime value
*
* Arguments:
* src_date: datetime from which to calculate the item
* item_type: one of year, month, day
*
* Returns: int
*/
int
db_get_date_item (const DB_VALUE * src_date, const int item_type, DB_VALUE * result)
{
int month = 0, day = 0, year = 0;
int second = 0, minute = 0, hour = 0;
int ms = 0;
if (DB_IS_NULL (src_date))
{
db_make_null (result);
return NO_ERROR;
}
/* get the date/time information from src_date */
if (db_get_datetime_from_dbvalue (src_date, &year, &month, &day, &hour, &minute, &second, &ms, NULL) != NO_ERROR)
{
/* This function should return NULL if src_date is an invalid parameter. Clear the error generated by the
* function call and return null. */
er_clear ();
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
return NO_ERROR;
}
/* set ER_DATE_CONVERSION */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
return ER_DATE_CONVERSION;
}
switch (item_type)
{
case PT_YEARF:
db_make_int (result, year);
break;
case PT_MONTHF:
db_make_int (result, month);
break;
case PT_DAYF:
db_make_int (result, day);
break;
default:
assert (false);
db_make_null (result);
break;
}
return NO_ERROR;
}
/*
* db_get_time_item () - compute an item from a datetime value
*
* Arguments:
* src_date: datetime from which to calculate the item
* item_type: one of hour, minute, second
*
* Returns: int
*/
int
db_get_time_item (const DB_VALUE * src_date, const int item_type, DB_VALUE * result)
{
int second = 0, minute = 0, hour = 0, millisecond = 0;
if (DB_IS_NULL (src_date))
{
db_make_null (result);
return NO_ERROR;
}
if (db_get_time_from_dbvalue (src_date, &hour, &minute, &second, &millisecond) != NO_ERROR)
{
er_clear ();
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
return NO_ERROR;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIME_CONVERSION, 0);
return ER_TIME_CONVERSION;
}
switch (item_type)
{
case PT_HOURF:
db_make_int (result, hour);
break;
case PT_MINUTEF:
db_make_int (result, minute);
break;
case PT_SECONDF:
db_make_int (result, second);
break;
default:
assert (false);
db_make_null (result);
break;
}
return NO_ERROR;
}
#endif /* !defined (SERVER_MODE) */
/*
* db_time_format ()
*
* Arguments:
* time_value: time from which we get the informations
* format: format specifiers string
* result: output string
* domain: domain of result
*
* Returns: int
*
* Errors:
*
* Note:
* This is used like the DATE_FORMAT() function, but the format
* string may contain format specifiers only for hours, minutes, seconds, and
* milliseconds. Other specifiers produce a NULL value or 0.
*/
int
db_time_format (const DB_VALUE * time_value, const DB_VALUE * format, const DB_VALUE * date_lang, DB_VALUE * result,
const TP_DOMAIN * domain)
{
DB_TYPE res_type;
int error_status = NO_ERROR;
DATE_TIME_INFO dtz_info;
INTL_LANG date_lang_id;
const LANG_LOCALE_DATA *lld;
bool dummy;
INTL_CODESET codeset;
int res_collation;
const DB_VALUE *value_ptr = time_value;
DB_VALUE new_time_value;
assert (date_lang != NULL);
memset (&dtz_info, 0x00, sizeof (dtz_info));
if (value_ptr == NULL || format == NULL || DB_IS_NULL (value_ptr) || DB_IS_NULL (format))
{
db_make_null (result);
return error_status;
}
#if 0
// Remains disabled unless pt_get_expression_definition() is modified.
if (!is_char_string (format))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
#else
assert (is_char_string (format)); /* In pt_get_expression_definition(), arg2 is specified as a string. */
#endif
assert (DB_VALUE_TYPE (date_lang) == DB_TYPE_INTEGER);
date_lang_id = lang_get_lang_id_from_flag (db_get_int (date_lang), &dummy, &dummy);
if (domain != NULL && domain->collation_flag != TP_DOMAIN_COLL_LEAVE)
{
codeset = TP_DOMAIN_CODESET (domain);
res_collation = TP_DOMAIN_COLLATION (domain);
}
else
{
codeset = db_get_string_codeset (format);
res_collation = db_get_string_collation (format);
}
lld = lang_get_specific_locale (date_lang_id, codeset);
if (lld == NULL)
{
error_status = ER_LANG_CODESET_NOT_AVAILABLE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 2, lang_get_lang_name_from_id (date_lang_id),
lang_charset_name (codeset));
return error_status;
}
res_type = DB_VALUE_DOMAIN_TYPE (value_ptr);
if ((res_type == DB_TYPE_DATE) || !(TP_IS_DATE_OR_TIME_TYPE (res_type) || TP_IS_CHAR_TYPE (res_type)))
{
const TP_DOMAIN *new_domain = tp_domain_resolve_default (DB_TYPE_VARCHAR);
db_make_null (&new_time_value);
TP_DOMAIN_STATUS status = tp_value_auto_cast (value_ptr, &new_time_value, new_domain);
if (status != DOMAIN_COMPATIBLE)
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
value_ptr = &new_time_value;
res_type = DB_VALUE_DOMAIN_TYPE (value_ptr);
}
/* 1. Get time values */
error_status = get_date_time_info (&dtz_info, res_type, value_ptr, false);
if (error_status != NO_ERROR)
{
if (value_ptr == &new_time_value)
{
pr_clear_value (&new_time_value);
}
return error_status;
}
/* 2. Generate the output according to the format and the values */
char *res = NULL;
error_status = put_date_time_info (&dtz_info, format, date_lang_id, lld, false, &res);
if (error_status == NO_ERROR)
{
db_make_string (result, res);
db_string_put_cs_and_collation (result, codeset, res_collation);
result->need_clear = true;
return error_status;
}
if (res)
{
db_private_free_and_init (NULL, res);
}
return error_status;
}
/*
* db_timestamp() -
*
* Arguments:
* src_datetime1: date or datetime expression
* src_time2: time expression
*
* Returns: int
*
* Errors:
*
* Note:
* This function is used in the function TIMESTAMP().
* It returns the date or datetime expression expr as a datetime value.
* With both arguments, it adds the time expression src_time2 to the date or
* datetime expression src_datetime1 and returns the result as a datetime value.
*/
int
db_timestamp (const DB_VALUE * src_datetime1, const DB_VALUE * src_time2, DB_VALUE * result_datetime)
{
int error_status = NO_ERROR;
int year, month, day, hour, minute, second, millisecond;
int h = 0, mi = 0, s = 0, ms = 0;
DB_BIGINT amount = 0;
double amount_d = 0;
DB_TYPE type;
DB_DATETIME datetime, calculated_datetime;
/* if sign is 1 then we perform a subtraction */
int sign = 0;
if (result_datetime == (DB_VALUE *) NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_PARAMETER, 0);
return ER_QPROC_INVALID_PARAMETER;
}
db_value_domain_init (result_datetime, DB_TYPE_DATETIME, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
/* Return NULL if NULL is explicitly given as the second argument. If no second argument is given, we consider it as
* 0. */
if (DB_IS_NULL (src_datetime1) || (src_time2 && DB_IS_NULL (src_time2)))
{
db_make_null (result_datetime);
return NO_ERROR;
}
year = month = day = hour = minute = second = millisecond = 0;
error_status =
db_get_datetime_from_dbvalue (src_datetime1, &year, &month, &day, &hour, &minute, &second, &millisecond, NULL);
if (error_status != NO_ERROR)
{
return error_status;
}
/* If no second argument is given, just encode the first argument. */
if (src_time2 == NULL)
{
goto encode_result;
}
type = DB_VALUE_DOMAIN_TYPE (src_time2);
switch (type)
{
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
parse_time_string (db_get_string (src_time2), db_get_string_size (src_time2), &sign, &h, &mi, &s, &ms);
break;
case DB_TYPE_TIME:
db_time_decode (db_get_time (src_time2), &h, &mi, &s);
break;
case DB_TYPE_SMALLINT:
amount = (DB_BIGINT) db_get_short (src_time2);
break;
case DB_TYPE_INTEGER:
amount = (DB_BIGINT) db_get_int (src_time2);
break;
case DB_TYPE_BIGINT:
amount = db_get_bigint (src_time2);
break;
case DB_TYPE_FLOAT:
amount_d = db_get_float (src_time2);
break;
case DB_TYPE_DOUBLE:
amount_d = db_get_double (src_time2);
break;
case DB_TYPE_MONETARY:
amount_d = db_value_get_monetary_amount_as_double (src_time2);
break;
case DB_TYPE_NUMERIC:
numeric_coerce_num_to_double ((DB_C_NUMERIC) db_locate_numeric (src_time2), DB_VALUE_SCALE (src_time2),
&amount_d);
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INVALID_DATA_TYPE, 0);
return ER_QSTR_INVALID_DATA_TYPE;
}
if (type == DB_TYPE_DOUBLE || type == DB_TYPE_FLOAT || type == DB_TYPE_MONETARY || type == DB_TYPE_NUMERIC)
{
amount = (DB_BIGINT) amount_d;
ms = ((long) (amount_d * 1000.0)) % 1000;
}
if (type != DB_TYPE_VARCHAR && type != DB_TYPE_CHAR && type != DB_TYPE_TIME)
{
if (amount < 0)
{
amount = -amount;
ms = -ms;
sign = 1;
}
s = (int) ((DB_BIGINT) amount % 100);
amount /= 100;
mi = (int) ((DB_BIGINT) amount % 100);
amount /= 100;
h = (int) amount;
}
/* validation of minute and second */
if ((mi < 0 || mi > 59) || (s < 0 || s > 59))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_PARAMETER, 0);
return ER_QPROC_INVALID_PARAMETER;
}
/* Convert time to milliseconds. */
amount = ((DB_BIGINT) h * 60 * 60 * 1000) + ((DB_BIGINT) mi * 60 * 1000) + ((DB_BIGINT) s * 1000) + (DB_BIGINT) ms;
encode_result:
db_datetime_encode (&datetime, month, day, year, hour, minute, second, millisecond);
if (amount > 0)
{
if (sign == 0)
{
error_status = db_add_int_to_datetime (&datetime, amount, &calculated_datetime);
}
else
{
error_status = db_subtract_int_from_datetime (&datetime, amount, &calculated_datetime);
}
if (error_status != NO_ERROR)
{
return error_status;
}
db_make_datetime (result_datetime, &calculated_datetime);
}
else
{
db_make_datetime (result_datetime, &datetime);
}
return error_status;
}
/*
* db_add_months () -
*/
int
db_add_months (const DB_VALUE * src_date, const DB_VALUE * nmonth, DB_VALUE * result_date)
{
int error_status = NO_ERROR;
int n;
int month, day, year;
int old_month, old_year;
DB_DATE *date_p;
assert (src_date != (DB_VALUE *) NULL);
assert (nmonth != (DB_VALUE *) NULL);
assert (result_date != (DB_VALUE *) NULL);
db_value_domain_init (result_date, DB_TYPE_DATE, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
if (DB_IS_NULL (src_date) || DB_IS_NULL (nmonth))
{
db_make_null (result_date);
return error_status;
}
assert (DB_VALUE_TYPE (nmonth) == DB_TYPE_INTEGER);
n = db_get_int (nmonth);
assert (DB_VALUE_TYPE (src_date) == DB_TYPE_DATE);
date_p = db_get_date (src_date);
db_date_decode (date_p, &month, &day, &year);
old_month = month;
old_year = year;
if ((month + n) >= 0) /* Calculate month,year */
{
year = year + (month + n) / 12;
month = (month + n) % 12;
year = (month == 0) ? year - 1 : year;
month = (month == 0) ? 12 : month;
}
else
{
year = year + (month + n - 12) / 12;
month = 12 + (month + n) % 12;
}
/* Check last day of month */
if (day == get_last_day (old_month, old_year) || day > get_last_day (month, year))
{
day = get_last_day (month, year);
}
if (0 < year && year < 10000)
{
db_make_date (result_date, month, day, year);
}
else
{
error_status = ER_DATE_EXCEED_LIMIT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
return error_status;
}
/*
* db_last_day () -
*/
int
db_last_day (const DB_VALUE * src_date, DB_VALUE * result_day)
{
int error_status = NO_ERROR;
int month, day, year;
int lastday;
assert (src_date != (DB_VALUE *) NULL);
assert (result_day != (DB_VALUE *) NULL);
if (DB_IS_NULL (src_date))
{
db_make_null (result_day);
return error_status;
}
db_date_decode ((DB_DATE *) (&src_date->data.date), &month, &day, &year);
if (month == 0 && day == 0 && year == 0)
{
db_make_null (result_day);
er_clear ();
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
return NO_ERROR;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ATTEMPT_TO_USE_ZERODATE, 0);
return ER_ATTEMPT_TO_USE_ZERODATE;
}
lastday = get_last_day (month, year);
db_make_date (result_day, month, lastday, year);
return error_status;
}
/*
* db_months_between () -
*/
int
db_months_between (const DB_VALUE * start_mon, const DB_VALUE * end_mon, DB_VALUE * result_mon)
{
int error_status = NO_ERROR;
double result_double;
int start_month, start_day, start_year;
int end_month, end_day, end_year;
DB_DATE *start_date, *end_date;
assert (start_mon != (DB_VALUE *) NULL);
assert (end_mon != (DB_VALUE *) NULL);
assert (result_mon != (DB_VALUE *) NULL);
/* now return null */
if (DB_IS_NULL (start_mon) || DB_IS_NULL (end_mon))
{
db_make_null (result_mon);
return error_status;
}
assert (DB_VALUE_TYPE (start_mon) == DB_TYPE_DATE);
assert (DB_VALUE_TYPE (end_mon) == DB_TYPE_DATE);
start_date = db_get_date (start_mon);
end_date = db_get_date (end_mon);
db_date_decode (start_date, &start_month, &start_day, &start_year);
db_date_decode (end_date, &end_month, &end_day, &end_year);
if (start_day == end_day
|| (start_day == get_last_day (start_month, start_year) && end_day == get_last_day (end_month, end_year)))
{
result_double = (double) (start_year * 12 + start_month - end_year * 12 - end_month);
}
else
{
result_double =
(double) ((start_year - end_year) * 12.0) + (double) (start_month - end_month) +
(double) ((start_day - end_day) / 31.0);
}
db_make_double (result_mon, result_double);
return error_status;
}
/*
* db_sys_date () -
*/
int
db_sys_date (DB_VALUE * result_date)
{
int error_status = NO_ERROR;
time_t tloc;
struct tm *c_time_struct, tm_val;
assert (result_date != (DB_VALUE *) NULL);
/* now return null */
db_value_domain_init (result_date, DB_TYPE_DATE, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
/* Need checking error */
if (time (&tloc) == -1)
{
error_status = ER_SYSTEM_DATE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
c_time_struct = localtime_r (&tloc, &tm_val);
if (c_time_struct == NULL)
{
error_status = ER_SYSTEM_DATE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
db_make_date (result_date, c_time_struct->tm_mon + 1, c_time_struct->tm_mday, c_time_struct->tm_year + 1900);
return error_status;
}
/*
* db_sys_time () -
*/
int
db_sys_time (DB_VALUE * result_time)
{
int error_status = NO_ERROR;
time_t tloc;
struct tm *c_time_struct, tm_val;
assert (result_time != (DB_VALUE *) NULL);
/* now return null */
db_value_domain_init (result_time, DB_TYPE_TIME, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
/* Need checking error */
if (time (&tloc) == -1)
{
error_status = ER_SYSTEM_DATE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
c_time_struct = localtime_r (&tloc, &tm_val);
if (c_time_struct == NULL)
{
error_status = ER_SYSTEM_DATE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
db_make_time (result_time, c_time_struct->tm_hour, c_time_struct->tm_min, c_time_struct->tm_sec);
return error_status;
}
/*
* db_sys_timestamp () -
*/
int
db_sys_timestamp (DB_VALUE * result_timestamp)
{
int error_status = NO_ERROR;
time_t tloc;
assert (result_timestamp != (DB_VALUE *) NULL);
/* now return null */
db_value_domain_init (result_timestamp, DB_TYPE_TIMESTAMP, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
if (time (&tloc) == -1 || OR_CHECK_INT_OVERFLOW (tloc))
{
error_status = ER_SYSTEM_DATE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
db_make_timestamp (result_timestamp, (DB_TIMESTAMP) tloc);
return error_status;
}
/*
* db_sys_datetime () -
*/
int
db_sys_datetime (DB_VALUE * result_datetime)
{
int error_status = NO_ERROR;
DB_DATETIME datetime;
time_t sec;
int millisec;
struct tm *c_time_struct, tm_val;
assert (result_datetime != (DB_VALUE *) NULL);
/* now return null */
db_value_domain_init (result_datetime, DB_TYPE_DATETIME, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
util_get_second_and_ms_since_epoch (&sec, &millisec);
c_time_struct = localtime_r (&sec, &tm_val);
if (c_time_struct == NULL)
{
error_status = ER_SYSTEM_DATE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
db_datetime_encode (&datetime, c_time_struct->tm_mon + 1, c_time_struct->tm_mday, c_time_struct->tm_year + 1900,
c_time_struct->tm_hour, c_time_struct->tm_min, c_time_struct->tm_sec, millisec);
db_make_datetime (result_datetime, &datetime);
return error_status;
}
/*
* db_sys_date_and_epoch_time () - This function returns current
* datetime and timestamp.
*
* return: status of the error
*
* dt_dbval(out): datetime
* ts_dbval(out): timestamp
*/
int
db_sys_date_and_epoch_time (DB_VALUE * dt_dbval, DB_VALUE * ts_dbval)
{
int error_status = NO_ERROR;
DB_DATETIME datetime;
time_t sec;
int millisec;
struct tm *c_time_struct, tm_val;
assert (dt_dbval != NULL);
assert (ts_dbval != NULL);
util_get_second_and_ms_since_epoch (&sec, &millisec);
c_time_struct = localtime_r (&sec, &tm_val);
if (c_time_struct == NULL)
{
error_status = ER_SYSTEM_DATE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
db_datetime_encode (&datetime, c_time_struct->tm_mon + 1, c_time_struct->tm_mday, c_time_struct->tm_year + 1900,
c_time_struct->tm_hour, c_time_struct->tm_min, c_time_struct->tm_sec, millisec);
db_make_datetime (dt_dbval, &datetime);
db_make_timestamp (ts_dbval, (DB_TIMESTAMP) sec);
return error_status;
}
/*
* This function return the current timezone , as an integer representing
* the minutes away from GMT
*/
int
db_sys_timezone (DB_VALUE * result_timezone)
{
assert (result_timezone != (DB_VALUE *) NULL);
/* now return null */
db_value_domain_init (result_timezone, DB_TYPE_INTEGER, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
db_make_int (result_timezone, tz_get_offset_in_mins ());
return NO_ERROR;
}
/*
* get_last_day () -
*/
int
get_last_day (int month, int year)
{
int lastday = 0;
if (year >= 1700)
{
switch (month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
lastday = 31;
break;
case 4:
case 6:
case 9:
case 11:
lastday = 30;
break;
case 2:
if (year % 4 == 0)
{
if (year % 100 == 0)
{
if (year % 400 == 0)
{
lastday = 29;
}
else
{
lastday = 28;
}
}
else
{
lastday = 29;
}
}
else
{
lastday = 28;
}
break;
default:
break; /* Need Error Checking */
}
}
else
{
switch (month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
lastday = 31;
break;
case 4:
case 6:
case 9:
case 11:
lastday = 30;
break;
case 2:
if (year % 4 == 0)
{
lastday = 29;
}
else
{
lastday = 28;
}
break;
default:
break; /* Need Error Checking */
}
}
return lastday;
}
/*
* db_to_char () -
*/
int
db_to_char (const DB_VALUE * src_value, const DB_VALUE * format_or_length, const DB_VALUE * lang_str,
DB_VALUE * result_str, const TP_DOMAIN * domain)
{
int error_status = NO_ERROR;
DB_TYPE type;
type = DB_VALUE_DOMAIN_TYPE (src_value);
if (type == DB_TYPE_NULL || is_number (src_value))
{
return number_to_char (src_value, format_or_length, lang_str, result_str, domain);
}
else if (TP_IS_DATE_OR_TIME_TYPE (type))
{
return date_to_char (src_value, format_or_length, lang_str, result_str, domain);
}
else if (TP_IS_CHAR_TYPE (type))
{
if (domain == NULL)
{
error_status = pr_clone_value (src_value, result_str);
if (error_status != NO_ERROR)
{
return error_status;
}
db_string_put_cs_and_collation (result_str, LANG_COERCIBLE_CODESET, LANG_COERCIBLE_COLL);
}
else
{
error_status = db_value_coerce (src_value, result_str, domain);
}
return error_status;
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
}
#define MAX_STRING_DATE_TOKEN_LEN LOC_DATA_MONTH_WIDE_SIZE
const char *Month_name_ISO[][12] = {
{"January", "February", "March", "April",
"May", "June", "July", "August", "September", "October",
"November", "December"}, /* US */
{"1wol",
"2wol",
"3wol",
"4wol",
"5wol",
"6wol",
"7wol",
"8wol",
"9wol",
"10wol",
"11wol",
"12wol"}, /* KR */
{"Ocak",
"Subat",
"Mart",
"Nisan",
"Mayis",
"Haziran",
"Temmuz",
"Agustos",
"Eylul",
"Ekim",
"Kasim",
"Aralik"} /* TR */
};
const char *Month_name_UTF8[][12] = {
{"January", "February", "March", "April",
"May", "June", "July", "August", "September", "October",
"November", "December"}, /* US */
{"1\xec\x9b\x94",
"2\xec\x9b\x94",
"3\xec\x9b\x94",
"4\xec\x9b\x94",
"5\xec\x9b\x94",
"6\xec\x9b\x94",
"7\xec\x9b\x94",
"8\xec\x9b\x94",
"9\xec\x9b\x94",
"10\xec\x9b\x94",
"11\xec\x9b\x94",
"12\xec\x9b\x94"}, /* KR */
{"Ocak",
"\xc5\x9e" "ubat",
"Mart",
"Nisan",
"May" "\xc4\xb1" "s",
"Haziran",
"Temmuz",
"A" "\xc4\x9f" "ustos",
"Eyl" "\xc3\xbc" "l",
"Ekim",
"Kas" "\xc4\xb1" "m",
"Aral" "\xc4\xb1" "k"} /* TR */
};
const char *Month_name_EUCKR[][12] = {
{"January", "February", "March", "April",
"May", "June", "July", "August", "September", "October",
"November", "December"}, /* US */
{"1\xbf\xf9",
"2\xbf\xf9",
"3\xbf\xf9",
"4\xbf\xf9",
"5\xbf\xf9",
"6\xbf\xf9",
"7\xbf\xf9",
"8\xbf\xf9",
"9\xbf\xf9",
"10\xbf\xf9",
"11\xbf\xf9",
"12\xbf\xf9"}, /* KR */
{"Ocak",
"Subat",
"Mart",
"Nisan",
"Mayis",
"Haziran",
"Temmuz",
"Agustos",
"Eylul",
"Ekim",
"Kasim",
"Aralik"} /* TR */
};
const char Month_name_parse_order[][12] = {
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
};
const char *Day_name_ISO[][7] = {
{"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}, /* US */
{"Iryoil",
"Woryoil",
"Hwayoil",
"Suyoil",
"Mogyoil",
"Geumyoil",
"Toyoil"}, /* KR */
{"Pazar", "Pazartesi", "Sali",
"Carsamba",
"Persembe", "Cuma",
"Cumartesi"} /* TR */
};
const char *Day_name_UTF8[][7] = {
{"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}, /* US */
{"\xec\x9d\xbc\xec\x9a\x94\xec\x9d\xbc",
"\xec\x9b\x94\xec\x9a\x94\xec\x9d\xbc",
"\xed\x99\x94\xec\x9a\x94\xec\x9d\xbc",
"\xec\x88\x98\xec\x9a\x94\xec\x9d\xbc",
"\xeb\xaa\xa9\xec\x9a\x94\xec\x9d\xbc",
"\xea\xb8\x88\xec\x9a\x94\xec\x9d\xbc",
"\xed\x86\xa0\xec\x9a\x94\xec\x9d\xbc"}, /* KR */
{"Pazar", "Pazartesi", "Sal\xc4\xb1",
"\xc3\x87" "ar" "\xc5\x9f" "amba",
"Per" "\xc5\x9f" "embe", "Cuma",
"Cumartesi"} /* TR */
};
const char *Day_name_EUCKR[][7] = {
{"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}, /* US */
{"\xc0\xcf\xbf\xe4\xc0\xcf",
"\xbf\xf9\xbf\xe4\xc0\xcf",
"\xc8\xad\xbf\xe4\xc0\xcf",
"\xbc\xf6\xbf\xe4\xc0\xcf",
"\xb8\xf1\xbf\xe4\xc0\xcf",
"\xb1\xdd\xbf\xe4\xc0\xcf",
"\xc5\xe4\xbf\xe4\xc0\xcf"}, /* KR */
{"Pazar", "Pazartesi", "Sali",
"Carsamba",
"Persembe", "Cuma",
"Cumartesi"} /* TR */
};
const char Day_name_parse_order[][7] = {
{0, 1, 2, 3, 4, 5, 6},
{0, 1, 2, 3, 4, 5, 6},
{1, 0, 2, 3, 4, 6, 5}
};
const char *Short_Month_name_ISO[][12] = {
{"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}, /* US */
{"1wol",
"2wol",
"3wol",
"4wol",
"5wol",
"6wol",
"7wol",
"8wol",
"9wol",
"10wol",
"11wol",
"12wol"}, /* KR */
{"Ock",
"Sbt",
"Mrt",
"Nsn",
"Mys",
"Hzr",
"Tmz",
"Ags",
"Eyl",
"Ekm",
"Ksm",
"Arl"} /* TR */
};
const char *Short_Month_name_UTF8[][12] = {
{"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}, /* US */
{"1\xec\x9b\x94",
"2\xec\x9b\x94",
"3\xec\x9b\x94",
"4\xec\x9b\x94",
"5\xec\x9b\x94",
"6\xec\x9b\x94",
"7\xec\x9b\x94",
"8\xec\x9b\x94",
"9\xec\x9b\x94",
"10\xec\x9b\x94",
"11\xec\x9b\x94",
"12\xec\x9b\x94"}, /* KR */
{"Ock",
"\xc5\x9e" "bt",
"Mrt",
"Nsn",
"Mys",
"Hzr",
"Tmz",
"A" "\xc4\x9f" "s",
"Eyl",
"Ekm",
"Ksm",
"Arl"} /* TR */
};
const char *Short_Month_name_EUCKR[][12] = {
{"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}, /* US */
{"1\xbf\xf9",
"2\xbf\xf9",
"3\xbf\xf9",
"4\xbf\xf9",
"5\xbf\xf9",
"6\xbf\xf9",
"7\xbf\xf9",
"8\xbf\xf9",
"9\xbf\xf9",
"10\xbf\xf9",
"11\xbf\xf9",
"12\xbf\xf9"}, /* KR */
{"Ock",
"Sbt",
"Mrt",
"Nsn",
"Mys",
"Hzr",
"Tmz",
"Ags",
"Eyl",
"Ekm",
"Ksm",
"Arl"} /* TR */
};
const char Short_Month_name_parse_order[][12] = {
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
};
const char *Short_Day_name_ISO[][7] = {
{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}, /* US */
{"Il",
"Wol",
"Hwa",
"Su",
"Mok",
"Geum",
"To"}, /* KR */
{"Pz", "Pt", "Sa",
"Ca",
"Pe", "Cu", "Ct"} /* TR */
};
const char *Short_Day_name_UTF8[][7] = {
{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}, /* US */
{"\xec\x9d\xbc",
"\xec\x9b\x94",
"\xed\x99\x94",
"\xec\x88\x98",
"\xeb\xaa\xa9",
"\xea\xb8\x88",
"\xed\x86\xa0"}, /* KR */
{"Pz", "Pt", "Sa",
"\xc3\x87" "a",
"Pe", "Cu", "Ct"} /* TR */
};
const char *Short_Day_name_EUCKR[][7] = {
{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}, /* US */
{"\xc0\xcf",
"\xbf\xf9",
"\xc8\xad",
"\xbc\xf6",
"\xb8\xf1",
"\xb1\xdd",
"\xc5\xe4"}, /* KR */
{"Pz", "Pt", "Sa",
"Ca",
"Pe", "Cu", "Ct"} /* TR */
};
const char Short_Day_name_parse_order[][7] = {
{0, 1, 2, 3, 4, 5, 6},
{0, 1, 2, 3, 4, 5, 6},
{0, 1, 2, 3, 4, 5, 6}
};
#define AM_NAME_KR "ojeon"
#define PM_NAME_KR "ohu"
#define AM_NAME_KR_EUC "\xbf\xc0\xc0\xfc"
#define PM_NAME_KR_EUC "\xbf\xc0\xc8\xc4"
#define AM_NAME_KR_UTF8 "\xec\x98\xa4\xec\xa0\x84"
#define PM_NAME_KR_UTF8 "\xec\x98\xa4\xed\x9b\x84"
const char AM_PM_parse_order[][12] = {
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
};
const char *Am_Pm_name_ISO[][12] = {
{"am", "pm", "Am", "Pm", "AM", "PM",
"a.m.", "p.m.", "A.m.", "P.m.", "A.M.", "P.M."}, /* US */
{AM_NAME_KR, PM_NAME_KR, AM_NAME_KR, PM_NAME_KR, AM_NAME_KR, PM_NAME_KR,
AM_NAME_KR, PM_NAME_KR, AM_NAME_KR, PM_NAME_KR, AM_NAME_KR, PM_NAME_KR},
/* KR */
{"am", "pm", "Am", "Pm", "AM", "PM",
"a.m.", "p.m.", "A.m.", "P.m.", "A.M.", "P.M."}, /* TR */
};
const char *Am_Pm_name_UTF8[][12] = {
{"am", "pm", "Am", "Pm", "AM", "PM",
"a.m.", "p.m.", "A.m.", "P.m.", "A.M.", "P.M."}, /* US */
{AM_NAME_KR_UTF8, PM_NAME_KR_UTF8, AM_NAME_KR_UTF8,
PM_NAME_KR_UTF8, AM_NAME_KR_UTF8, PM_NAME_KR_UTF8,
AM_NAME_KR_UTF8, PM_NAME_KR_UTF8, AM_NAME_KR_UTF8,
PM_NAME_KR_UTF8, AM_NAME_KR_UTF8, PM_NAME_KR_UTF8},
/* KR */
{"am", "pm", "Am", "Pm", "AM", "PM",
"a.m.", "p.m.", "A.m.", "P.m.", "A.M.", "P.M."}, /* TR */
};
const char *Am_Pm_name_EUCKR[][12] = {
{"am", "pm", "Am", "Pm", "AM", "PM",
"a.m.", "p.m.", "A.m.", "P.m.", "A.M.", "P.M."}, /* US */
{AM_NAME_KR_EUC, PM_NAME_KR_EUC, AM_NAME_KR_EUC,
PM_NAME_KR_EUC, AM_NAME_KR_EUC, PM_NAME_KR_EUC,
AM_NAME_KR_EUC, PM_NAME_KR_EUC, AM_NAME_KR_EUC,
PM_NAME_KR_EUC, AM_NAME_KR_EUC, PM_NAME_KR_EUC},
/* KR */
{"am", "pm", "Am", "Pm", "AM", "PM",
"a.m.", "p.m.", "A.m.", "P.m.", "A.M.", "P.M."}, /* TR */
};
static inline int
check_date_lang_on_prepared (const DB_VALUE * date_lang, INTL_LANG * date_lang_id, bool * has_user_fmt)
{
bool has_user_lang = false;
int flag;
switch (DB_VALUE_TYPE (date_lang))
{
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
/* We got here because we used HOST_VAR. And using HOST_VAR means we didn't omit format. */
if (lang_get_lang_id_from_name (db_get_string (date_lang), date_lang_id))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_LOCALE_LANG_NOT_AVAILABLE, 1, db_get_string (date_lang));
return ER_LOCALE_LANG_NOT_AVAILABLE;
}
*has_user_fmt = true;
return NO_ERROR;
case DB_TYPE_INTEGER:
flag = db_get_int (date_lang);
if ((flag & LANG_LOADED_LOCALES_PARITY_MASK) == LANG_LOADED_LOCALES_PARITY)
{
*date_lang_id = lang_get_lang_id_from_flag (flag, has_user_fmt, &has_user_lang);
return NO_ERROR;
}
else
{
/* We got here because we used HOST_VAR. This may be the case when the bound value is int. */
}
break;
default:
break;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INVALID_DATA_TYPE, 0);
return ER_QSTR_INVALID_DATA_TYPE;
}
/*
* db_to_date () -
*/
int
db_to_date (const DB_VALUE * src_str, const DB_VALUE * format_str, const DB_VALUE * date_lang, DB_VALUE * result_date)
{
int error_status = NO_ERROR;
const char *cur_format_str_ptr, *next_format_str_ptr;
char *cs; /* current source string pointer */
const char *last_src, *last_format;
TIMESTAMP_FORMAT cur_format;
int cur_format_size;
int month = 0, day = 0, year = 0, day_of_the_week = 0, week = -1;
int monthcount = 0, daycount = 0, yearcount = 0, day_of_the_weekcount = 0;
int i;
bool no_user_format;
INTL_LANG date_lang_id;
INTL_CODESET codeset;
INTL_CODESET frmt_codeset;
char stack_buf_str[64], stack_buf_format[64];
char *initial_buf_str = NULL, *initial_buf_format = NULL;
bool do_free_buf_str = false, do_free_buf_format = false;
DB_VALUE default_format;
bool has_user_format = false;
bool dummy;
assert (src_str != (DB_VALUE *) NULL);
assert (result_date != (DB_VALUE *) NULL);
assert (date_lang != (DB_VALUE *) NULL);
db_make_null (&default_format);
if (DB_IS_NULL (src_str))
{
db_make_null (result_date);
return error_status;
}
error_status = check_date_lang_on_prepared (date_lang, &date_lang_id, &has_user_format);
if (error_status != NO_ERROR)
{
return error_status;
}
if (false == is_char_string (src_str))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
if (db_get_string_size (src_str) == 0)
{
error_status = ER_QSTR_EMPTY_STRING;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
if (db_get_string_size (src_str) > MAX_TOKEN_SIZE)
{
error_status = ER_QSTR_SRC_TOO_LONG;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
codeset = db_get_string_codeset (src_str);
if (lang_get_specific_locale (date_lang_id, codeset) == NULL)
{
error_status = ER_LANG_CODESET_NOT_AVAILABLE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 2, lang_get_lang_name_from_id (date_lang_id),
lang_charset_name (codeset));
return error_status;
}
error_status =
db_check_or_create_null_term_string (src_str, stack_buf_str, sizeof (stack_buf_str), true, true, &initial_buf_str,
&do_free_buf_str);
if (error_status != NO_ERROR)
{
goto exit;
}
cs = initial_buf_str;
last_src = cs + strlen (cs);
last_src = (char *) intl_backskip_spaces (cs, last_src - 1, codeset);
last_src = last_src + 1;
no_user_format = (format_str == NULL) || (!has_user_format);
if (no_user_format)
{
DB_DATE date_tmp;
const char *default_format_str;
/* try default CUBRID format first */
if (NO_ERROR == db_string_to_date_ex ((char *) cs, CAST_BUFLEN (last_src - cs), &date_tmp))
{
db_value_put_encoded_date (result_date, &date_tmp);
goto exit;
}
/* error parsing CUBRID default format, try the locale format, if any */
default_format_str = lang_date_format_parse (date_lang_id, codeset, DB_TYPE_DATE, &frmt_codeset);
if (default_format_str == NULL)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
db_make_char (&default_format, strlen (default_format_str), default_format_str,
strlen (default_format_str), frmt_codeset, LANG_GET_BINARY_COLLATION (frmt_codeset));
format_str = &default_format;
}
if (DB_IS_NULL (format_str))
{
db_make_null (result_date);
goto exit;
}
if (false == is_char_string (format_str))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (db_get_string_size (format_str) > MAX_TOKEN_SIZE)
{
error_status = ER_QSTR_FORMAT_TOO_LONG;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (db_get_string_size (format_str) == 0)
{
error_status = ER_QSTR_EMPTY_STRING;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
frmt_codeset = db_get_string_codeset (format_str);
error_status =
db_check_or_create_null_term_string (format_str, stack_buf_format, sizeof (stack_buf_format), true, true,
&initial_buf_format, &do_free_buf_format);
if (error_status != NO_ERROR)
{
goto exit;
}
cur_format_str_ptr = initial_buf_format;
last_format = cur_format_str_ptr + strlen (cur_format_str_ptr);
/* Skip space, tab, CR */
while (cs < last_src && strchr (WHITE_CHARS, *cs))
{
cs++;
}
/* Skip space, tab, CR */
while (cur_format_str_ptr < last_format && strchr (WHITE_CHARS, *cur_format_str_ptr))
{
cur_format_str_ptr++;
}
while (cs < last_src)
{
int token_size, cmp, cs_byte_size;
int k;
cur_format =
get_next_format (cur_format_str_ptr, frmt_codeset, DB_TYPE_DATE, &cur_format_size, &next_format_str_ptr);
switch (cur_format)
{
case DT_YYYY:
if (yearcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
yearcount++;
}
k = parse_digits (cs, &year, 4);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
break;
case DT_YY:
if (yearcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
yearcount++;
}
k = parse_digits (cs, &year, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
i = get_cur_year ();
if (i == -1)
{
error_status = ER_SYSTEM_DATE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
year += (i / 100) * 100;
break;
case DT_MM:
if (monthcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
monthcount++;
}
k = parse_digits (cs, &month, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (month < 1 || month > 12)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_MONTH:
if (monthcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
monthcount++;
}
error_status = get_string_date_token_id (SDT_MONTH, date_lang_id, cs, codeset, &month, &token_size);
if (error_status != NO_ERROR)
{
goto exit;
}
cs += token_size;
if (month == 0)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_MON:
if (monthcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
monthcount++;
}
month = 0;
error_status = get_string_date_token_id (SDT_MONTH_SHORT, date_lang_id, cs, codeset, &month, &token_size);
if (error_status != NO_ERROR)
{
goto exit;
}
cs += token_size;
if (month == 0)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_DD:
if (daycount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
daycount++;
}
k = parse_digits (cs, &day, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (day < 0 || day > 31)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_TEXT:
if (codeset != frmt_codeset)
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cmp = intl_case_match_tok (date_lang_id, codeset, (unsigned char *) (cur_format_str_ptr + 1),
(unsigned char *) cs, cur_format_size - 2, strlen (cs), &cs_byte_size);
if (cmp != 0)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += cs_byte_size;
break;
case DT_PUNCTUATION:
if (strncasecmp ((const char *) cur_format_str_ptr, (const char *) cs, cur_format_size) != 0)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += cur_format_size;
break;
case DT_CC:
case DT_Q:
error_status = ER_QSTR_INVALID_FORMAT;
/* Does it need error message? */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
case DT_DAY:
if (day_of_the_weekcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
day_of_the_weekcount++;
}
error_status = get_string_date_token_id (SDT_DAY, date_lang_id, cs, codeset, &day_of_the_week, &token_size);
if (error_status != NO_ERROR)
{
goto exit;
}
cs += token_size;
if (day_of_the_week == 0)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_DY:
if (day_of_the_weekcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
day_of_the_weekcount++;
}
error_status =
get_string_date_token_id (SDT_DAY_SHORT, date_lang_id, cs, codeset, &day_of_the_week, &token_size);
if (error_status != NO_ERROR)
{
goto exit;
}
cs += token_size;
if (day_of_the_week == 0)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_D:
if (day_of_the_weekcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
day_of_the_weekcount++;
}
k = parse_digits (cs, &day_of_the_week, 1);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (day_of_the_week < 1 || day_of_the_week > 7)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_INVALID:
case DT_NORMAL:
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
default:
break;
}
/* Skip space, tab, CR */
while (cs < last_src && strchr (WHITE_CHARS, *cs))
{
cs++;
}
cur_format_str_ptr = next_format_str_ptr;
/* Skip space, tab, CR */
while (cur_format_str_ptr < last_format && strchr (WHITE_CHARS, *cur_format_str_ptr))
{
cur_format_str_ptr++;
}
if (last_format == next_format_str_ptr)
{
while (cs < last_src && strchr (WHITE_CHARS, *cs))
{
cs++;
}
if (cs != last_src)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
}
}
/* Both format and src should end at same time */
if (cs != last_src || cur_format_str_ptr != last_format)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
year = (yearcount == 0) ? get_cur_year () : year;
month = (monthcount == 0) ? get_cur_month () : month;
day = (daycount == 0) ? 1 : day;
week = (day_of_the_weekcount == 0) ? -1 : day_of_the_week - 1;
if (week != -1 && week != db_get_day_of_week (year, month, day))
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
db_make_date (result_date, month, day, year);
if (*(db_get_date (result_date)) == 0)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
exit:
if (do_free_buf_str)
{
assert (initial_buf_str != NULL);
db_private_free (NULL, initial_buf_str);
}
if (do_free_buf_format)
{
assert (initial_buf_format != NULL);
db_private_free (NULL, initial_buf_format);
}
return error_status;
}
/*
* db_to_time () -
*/
int
db_to_time (const DB_VALUE * src_str, const DB_VALUE * format_str, const DB_VALUE * date_lang, const DB_TYPE type,
DB_VALUE * result_time)
{
int error_status = NO_ERROR;
const char *cur_format_str_ptr, *next_format_str_ptr;
char *cs; /* current source string pointer */
const char *last_format, *last_src;
TIMESTAMP_FORMAT cur_format;
int cur_format_size;
int second = 0, minute = 0, hour = 0;
int time_count = 0;
int mil_time_count = 0;
int am = false;
int pm = false;
bool no_user_format;
INTL_LANG date_lang_id;
INTL_CODESET codeset;
INTL_CODESET frmt_codeset;
char stack_buf_str[64], stack_buf_format[64];
char *initial_buf_str = NULL, *initial_buf_format = NULL;
bool do_free_buf_str = false, do_free_buf_format = false;
DB_VALUE default_format;
bool has_user_format = false;
bool dummy;
int tzh = 0, tzm = 0;
bool set_tzh = false, set_tzm = false;
int start_tzr = -1, start_tzd = -1;
int len_tzr = -1, len_tzd = -1;
int zone_id = -1;
bool is_negative_tzd = false;
assert (src_str != (DB_VALUE *) NULL);
assert (result_time != (DB_VALUE *) NULL);
assert (date_lang != (DB_VALUE *) NULL);
assert (type == DB_TYPE_TIME);
if (DB_IS_NULL (src_str))
{
db_make_null (result_time);
return error_status;
}
error_status = check_date_lang_on_prepared (date_lang, &date_lang_id, &has_user_format);
if (error_status != NO_ERROR)
{
return error_status;
}
/* now return null */
if (false == is_char_string (src_str))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
if (db_get_string_size (src_str) == 0)
{
error_status = ER_QSTR_EMPTY_STRING;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
if (db_get_string_size (src_str) > MAX_TOKEN_SIZE)
{
error_status = ER_QSTR_SRC_TOO_LONG;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
codeset = db_get_string_codeset (src_str);
if (lang_get_specific_locale (date_lang_id, codeset) == NULL)
{
error_status = ER_LANG_CODESET_NOT_AVAILABLE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 2, lang_get_lang_name_from_id (date_lang_id),
lang_charset_name (codeset));
return error_status;
}
error_status =
db_check_or_create_null_term_string (src_str, stack_buf_str, sizeof (stack_buf_str), true, true, &initial_buf_str,
&do_free_buf_str);
if (error_status != NO_ERROR)
{
goto exit;
}
cs = initial_buf_str;
last_src = cs + strlen (cs);
last_src = (char *) intl_backskip_spaces (cs, last_src - 1, codeset);
last_src = last_src + 1;
no_user_format = (format_str == NULL) || (!has_user_format);
if (no_user_format)
{
DB_TIME time_tmp;
const char *default_format_str;
/* try default CUBRID format first */
assert (type == DB_TYPE_TIME);
if (NO_ERROR == db_string_to_time_ex ((const char *) cs, CAST_BUFLEN (last_src - cs), &time_tmp))
{
db_value_put_encoded_time (result_time, &time_tmp);
goto exit;
}
/* error parsing CUBRID default format, try the locale format, if any */
default_format_str = lang_date_format_parse (date_lang_id, codeset, type, &frmt_codeset);
if (default_format_str == NULL)
{
error_status = ER_TIME_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
db_make_char (&default_format, strlen (default_format_str), default_format_str,
strlen (default_format_str), frmt_codeset, LANG_GET_BINARY_COLLATION (frmt_codeset));
format_str = &default_format;
}
if (DB_IS_NULL (format_str))
{
db_make_null (result_time);
goto exit;
}
if (false == is_char_string (format_str))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (db_get_string_size (format_str) > MAX_TOKEN_SIZE)
{
error_status = ER_QSTR_FORMAT_TOO_LONG;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (db_get_string_size (format_str) == 0)
{
error_status = ER_QSTR_EMPTY_STRING;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
frmt_codeset = db_get_string_codeset (format_str);
error_status =
db_check_or_create_null_term_string (format_str, stack_buf_format, sizeof (stack_buf_format), true, true,
&initial_buf_format, &do_free_buf_format);
if (error_status != NO_ERROR)
{
goto exit;
}
cur_format_str_ptr = initial_buf_format;
last_format = cur_format_str_ptr + strlen (cur_format_str_ptr);
/* Skip space, tab, CR */
while (cs < last_src && strchr (WHITE_CHARS, *cs))
{
cs++;
}
/* Skip space, tab, CR */
while (cur_format_str_ptr < last_format && strchr (WHITE_CHARS, *cur_format_str_ptr))
{
cur_format_str_ptr++;
}
while (cs < last_src)
{
int cmp, cs_byte_size, token_size;
int am_pm_id;
int k;
cur_format = get_next_format (cur_format_str_ptr, frmt_codeset, type, &cur_format_size, &next_format_str_ptr);
switch (cur_format)
{
case DT_AM:
case DT_A_M:
case DT_PM:
case DT_P_M:
if (mil_time_count != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
mil_time_count++;
}
error_status = get_string_date_token_id (SDT_AM_PM, date_lang_id, cs, codeset, &am_pm_id, &token_size);
if (error_status != NO_ERROR)
{
goto exit;
}
if (am_pm_id > 0)
{
if (am_pm_id % 2)
{
am = true;
}
else
{
pm = true;
}
}
else
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += token_size;
break;
case DT_HH:
case DT_HH12:
if (time_count != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
time_count++;
}
k = parse_digits (cs, &hour, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (hour < 1 || hour > 12)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_HH24:
if (time_count != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
time_count++;
}
if (mil_time_count != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
mil_time_count++;
}
k = parse_digits (cs, &hour, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (hour < 0 || hour > 23)
{
error_status = ER_TIME_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_MI:
k = parse_digits (cs, &minute, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (minute < 0 || minute > 59)
{
error_status = ER_TIME_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_SS:
k = parse_digits (cs, &second, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (second < 0 || second > 59)
{
error_status = ER_TIME_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_TEXT:
if (codeset != frmt_codeset)
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cmp = intl_case_match_tok (date_lang_id, codeset, (unsigned char *) (cur_format_str_ptr + 1),
(unsigned char *) cs, cur_format_size - 2, strlen (cs), &cs_byte_size);
if (cmp != 0)
{
error_status = ER_TIME_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += cs_byte_size;
break;
case DT_PUNCTUATION:
if (strncasecmp ((const char *) cur_format_str_ptr, (const char *) cs, cur_format_size) != 0)
{
error_status = ER_TIME_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += cur_format_size;
break;
case DT_TZR:
{
if (type == DB_TYPE_TIME)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
start_tzr = CAST_BUFLEN (cs - initial_buf_str);
zone_id = tz_get_best_match_zone (cs, &len_tzr);
if (zone_id < 0)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += len_tzr;
}
break;
case DT_TZD:
{
if (type == DB_TYPE_TIME)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
start_tzd = CAST_BUFLEN (cs - initial_buf_str);
len_tzd = parse_tzd (cs, TZD_MAX_EXPECTED_LEN);
if (len_tzd < 0)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += len_tzd;
}
break;
case DT_TZH:
case DT_TZM:
{
int *p = NULL;
if (type == DB_TYPE_TIME)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (cur_format == DT_TZH)
{
p = &tzh;
set_tzh = true;
}
else
{
p = &tzm;
set_tzm = true;
}
/* Get the timezone hour offset */
if (*cs == '+' || *cs == '-')
{
if (cur_format == DT_TZM)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (*cs == '-')
{
is_negative_tzd = true;
}
else
{
is_negative_tzd = false;
}
cs++;
}
k = parse_digits (cs, p, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
}
break;
case DT_INVALID:
error_status = ER_TIME_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
default:
break;
}
/* Skip space, tab, CR */
while (cs < last_src && strchr (WHITE_CHARS, *cs))
{
cs++;
}
cur_format_str_ptr = next_format_str_ptr;
/* Skip space, tab, CR */
while (cur_format_str_ptr < last_format && strchr (WHITE_CHARS, *cur_format_str_ptr))
{
cur_format_str_ptr++;
}
if (last_format == next_format_str_ptr)
{
while (cs < last_src && strchr (WHITE_CHARS, *cs))
{
cs++;
}
if (cs != last_src)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
}
}
/* 2. Validations */
if (len_tzd > 0 || len_tzr > 0)
{
if (set_tzh == true || set_tzm == true)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
}
if (is_negative_tzd)
{
tzh = -tzh;
tzm = -tzm;
}
/* Both format and src should end at same time */
if (cs != last_src || cur_format_str_ptr != last_format)
{
error_status = ER_TIME_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (am != 0 && pm == 0 && hour <= 12)
{ /* If A.M. */
hour = (hour == 12) ? 0 : hour;
}
else if (am == 0 && pm != 0 && hour <= 12)
{ /* If P.M. */
hour = (hour == 12) ? hour : hour + 12;
}
else if (am == 0 && pm == 0)
{ /* If military time */
;
}
else
{
error_status = ER_TIME_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
db_make_time (result_time, hour, minute, second);
exit:
if (do_free_buf_str)
{
assert (initial_buf_str != NULL);
db_private_free (NULL, initial_buf_str);
}
if (do_free_buf_format)
{
assert (initial_buf_format != NULL);
db_private_free (NULL, initial_buf_format);
}
return error_status;
}
/*
* db_to_timestamp () -
*/
int
db_to_timestamp (const DB_VALUE * src_str, const DB_VALUE * format_str, const DB_VALUE * date_lang, const DB_TYPE type,
DB_VALUE * result_timestamp)
{
int error_status = NO_ERROR;
DB_DATE tmp_date;
DB_TIME tmp_time;
DB_TIMESTAMP tmp_timestamp;
const char *cur_format_str_ptr, *next_format_str_ptr;
char *cs; /* current source string pointer */
const char *last_format, *last_src;
int cur_format_size;
TIMESTAMP_FORMAT cur_format;
int month = 0, day = 0, year = 0, day_of_the_week = 0, week = -1;
int monthcount = 0, daycount = 0, yearcount = 0, day_of_the_weekcount = 0;
int second = 0, minute = 0, hour = 0;
int time_count = 0;
int mil_time_count = 0;
int am = false;
int pm = false;
int i;
bool no_user_format;
INTL_LANG date_lang_id;
INTL_CODESET codeset;
INTL_CODESET frmt_codeset;
char stack_buf_str[64], stack_buf_format[64];
char *initial_buf_str = NULL, *initial_buf_format = NULL;
bool do_free_buf_str = false, do_free_buf_format = false;
DB_VALUE default_format;
bool has_user_format = false;
bool dummy;
int tzh = 0, tzm = 0;
bool set_tzh = false, set_tzm = false;
int start_tzr = -1, start_tzd = -1;
int len_tzr = -1, len_tzd = -1;
int zone_id = -1;
bool is_negative_tzd = false;
assert (src_str != (DB_VALUE *) NULL);
assert (result_timestamp != (DB_VALUE *) NULL);
assert (date_lang != (DB_VALUE *) NULL);
assert (type == DB_TYPE_TIMESTAMP || type == DB_TYPE_TIMESTAMPTZ);
db_make_null (&default_format);
if (DB_IS_NULL (src_str))
{
db_make_null (result_timestamp);
return error_status;
}
error_status = check_date_lang_on_prepared (date_lang, &date_lang_id, &has_user_format);
if (error_status != NO_ERROR)
{
return error_status;
}
if (false == is_char_string (src_str))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
if (db_get_string_size (src_str) == 0)
{
error_status = ER_QSTR_EMPTY_STRING;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
if (db_get_string_size (src_str) > MAX_TOKEN_SIZE)
{
error_status = ER_QSTR_SRC_TOO_LONG;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
codeset = db_get_string_codeset (src_str);
if (lang_get_specific_locale (date_lang_id, codeset) == NULL)
{
error_status = ER_LANG_CODESET_NOT_AVAILABLE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 2, lang_get_lang_name_from_id (date_lang_id),
lang_charset_name (codeset));
return error_status;
}
error_status =
db_check_or_create_null_term_string (src_str, stack_buf_str, sizeof (stack_buf_str), true, true, &initial_buf_str,
&do_free_buf_str);
if (error_status != NO_ERROR)
{
goto exit;
}
cs = initial_buf_str;
last_src = cs + strlen (cs);
last_src = (char *) intl_backskip_spaces (cs, last_src - 1, codeset);
last_src = last_src + 1;
no_user_format = (format_str == NULL) || (!has_user_format);
if (no_user_format)
{
DB_TIMESTAMP timestamp_tmp;
DB_TIMESTAMPTZ timestamptz_tmp;
const char *default_format_str;
/* try default CUBRID format first */
if (type == DB_TYPE_TIMESTAMP)
{
if (db_string_to_timestamp_ex ((const char *) cs, CAST_BUFLEN (last_src - cs), ×tamp_tmp) == NO_ERROR)
{
db_make_timestamp (result_timestamp, timestamp_tmp);
goto exit;
}
}
else
{
bool has_zone;
if (db_string_to_timestamptz_ex ((const char *) cs, CAST_BUFLEN (last_src - cs), ×tamptz_tmp, &has_zone,
false) == NO_ERROR)
{
db_make_timestamptz (result_timestamp, ×tamptz_tmp);
goto exit;
}
}
default_format_str = lang_date_format_parse (date_lang_id, codeset, type, &frmt_codeset);
if (default_format_str == NULL)
{
error_status = ER_TIMESTAMP_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
db_make_char (&default_format, strlen (default_format_str), default_format_str,
strlen (default_format_str), frmt_codeset, LANG_GET_BINARY_COLLATION (frmt_codeset));
format_str = &default_format;
}
if (DB_IS_NULL (format_str))
{
db_make_null (result_timestamp);
goto exit;
}
if (false == is_char_string (format_str))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (db_get_string_size (format_str) > MAX_TOKEN_SIZE)
{
error_status = ER_QSTR_FORMAT_TOO_LONG;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (db_get_string_size (format_str) == 0)
{
error_status = ER_QSTR_EMPTY_STRING;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
frmt_codeset = db_get_string_codeset (format_str);
error_status =
db_check_or_create_null_term_string (format_str, stack_buf_format, sizeof (stack_buf_format), true, true,
&initial_buf_format, &do_free_buf_format);
if (error_status != NO_ERROR)
{
goto exit;
}
cur_format_str_ptr = initial_buf_format;
last_format = cur_format_str_ptr + strlen (cur_format_str_ptr);
/* Skip space, tab, CR */
while (cs < last_src && strchr (WHITE_CHARS, *cs))
{
cs++;
}
/* Skip space, tab, CR */
while (cur_format_str_ptr < last_format && strchr (WHITE_CHARS, *cur_format_str_ptr))
{
cur_format_str_ptr++;
}
while (cs < last_src)
{
int token_size, cmp, cs_byte_size;
int am_pm_id;
int k;
cur_format = get_next_format (cur_format_str_ptr, frmt_codeset, type, &cur_format_size, &next_format_str_ptr);
switch (cur_format)
{
case DT_YYYY:
if (yearcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
yearcount++;
}
k = parse_digits (cs, &year, 4);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
break;
case DT_YY:
if (yearcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
yearcount++;
}
k = parse_digits (cs, &year, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
i = get_cur_year ();
if (i == -1)
{
error_status = ER_SYSTEM_DATE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
year += (i / 100) * 100;
break;
case DT_MM:
if (monthcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
monthcount++;
}
k = parse_digits (cs, &month, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (month < 1 || month > 12)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_MONTH:
if (monthcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
monthcount++;
}
error_status = get_string_date_token_id (SDT_MONTH, date_lang_id, cs, codeset, &month, &token_size);
if (error_status != NO_ERROR)
{
goto exit;
}
cs += token_size;
if (month == 0)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_MON:
if (monthcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
monthcount++;
}
month = 0;
error_status = get_string_date_token_id (SDT_MONTH_SHORT, date_lang_id, cs, codeset, &month, &token_size);
if (error_status != NO_ERROR)
{
goto exit;
}
cs += token_size;
if (month == 0)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_DD:
if (daycount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
daycount++;
}
k = parse_digits (cs, &day, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (day < 0 || day > 31)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_AM:
case DT_A_M:
case DT_PM:
case DT_P_M:
if (mil_time_count != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
mil_time_count++;
}
error_status = get_string_date_token_id (SDT_AM_PM, date_lang_id, cs, codeset, &am_pm_id, &token_size);
if (error_status != NO_ERROR)
{
goto exit;
}
if (am_pm_id > 0)
{
if (am_pm_id % 2)
{
am = true;
}
else
{
pm = true;
}
}
else
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += token_size;
break;
case DT_HH:
case DT_HH12:
if (time_count != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
time_count++;
}
k = parse_digits (cs, &hour, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (hour < 1 || hour > 12)
{
error_status = ER_TIME_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_HH24:
if (time_count != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
time_count++;
}
if (mil_time_count != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
mil_time_count++;
}
k = parse_digits (cs, &hour, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (hour < 0 || hour > 23)
{
error_status = ER_TIME_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_MI:
k = parse_digits (cs, &minute, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (minute < 0 || minute > 59)
{
error_status = ER_TIME_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_SS:
k = parse_digits (cs, &second, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (second < 0 || second > 59)
{
error_status = ER_TIME_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_TEXT:
if (codeset != frmt_codeset)
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cmp = intl_case_match_tok (date_lang_id, codeset, (unsigned char *) (cur_format_str_ptr + 1),
(unsigned char *) cs, cur_format_size - 2, strlen (cs), &cs_byte_size);
if (cmp != 0)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += cs_byte_size;
break;
case DT_PUNCTUATION:
if (strncasecmp ((const char *) (void *) cur_format_str_ptr, (const char *) cs, cur_format_size) != 0)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += cur_format_size;
break;
case DT_CC:
case DT_Q:
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
case DT_DAY:
if (day_of_the_weekcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
day_of_the_weekcount++;
}
error_status = get_string_date_token_id (SDT_DAY, date_lang_id, cs, codeset, &day_of_the_week, &token_size);
if (error_status != NO_ERROR)
{
goto exit;
}
cs += token_size;
if (day_of_the_week == 0)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_DY:
if (day_of_the_weekcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
day_of_the_weekcount++;
}
error_status =
get_string_date_token_id (SDT_DAY_SHORT, date_lang_id, cs, codeset, &day_of_the_week, &token_size);
if (error_status != NO_ERROR)
{
goto exit;
}
cs += token_size;
if (day_of_the_week == 0)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_D:
if (day_of_the_weekcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
day_of_the_weekcount++;
}
k = parse_digits (cs, &day_of_the_week, 1);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (day_of_the_week < 1 || day_of_the_week > 7)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_TZR:
{
if (type == DB_TYPE_TIMESTAMP)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
start_tzr = CAST_BUFLEN (cs - initial_buf_str);
zone_id = tz_get_best_match_zone (cs, &len_tzr);
if (zone_id < 0)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += len_tzr;
}
break;
case DT_TZD:
{
if (type == DB_TYPE_TIMESTAMP)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
start_tzd = CAST_BUFLEN (cs - initial_buf_str);
len_tzd = parse_tzd (cs, TZD_MAX_EXPECTED_LEN);
if (len_tzd < 0)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += len_tzd;
}
break;
case DT_TZH:
case DT_TZM:
{
int *p = NULL;
if (type == DB_TYPE_TIMESTAMP)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (cur_format == DT_TZH)
{
p = &tzh;
set_tzh = true;
}
else
{
p = &tzm;
set_tzm = true;
}
/* Get the timezone hour offset */
if (*cs == '+' || *cs == '-')
{
if (cur_format == DT_TZM)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (*cs == '-')
{
is_negative_tzd = true;
}
else
{
is_negative_tzd = false;
}
cs++;
}
k = parse_digits (cs, p, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
}
break;
case DT_NORMAL:
case DT_INVALID:
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
default:
break;
}
/* Skip space, tab, CR */
while (cs < last_src && strchr (WHITE_CHARS, *cs))
{
cs++;
}
cur_format_str_ptr = next_format_str_ptr;
/* Skip space, tab, CR */
while (cur_format_str_ptr < last_format && strchr (WHITE_CHARS, *cur_format_str_ptr))
{
cur_format_str_ptr++;
}
if (last_format == next_format_str_ptr)
{
while (cs < last_src && strchr (WHITE_CHARS, *cs))
{
cs++;
}
if (cs != last_src)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
}
}
/* 2. Validations */
if (len_tzd > 0 || len_tzr > 0)
{
if (set_tzh == true || set_tzm == true)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
}
if (is_negative_tzd)
{
tzh = -tzh;
tzm = -tzm;
}
/* Both format and src should end at same time */
if (cs != last_src || cur_format_str_ptr != last_format)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/************** Check DATE ****************/
year = (yearcount == 0) ? get_cur_year () : year;
month = (monthcount == 0) ? get_cur_month () : month;
day = (daycount == 0) ? 1 : day;
week = (day_of_the_weekcount == 0) ? -1 : day_of_the_week - 1;
if (week != -1 && week != db_get_day_of_week (year, month, day))
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (db_date_encode (&tmp_date, month, day, year) != NO_ERROR)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/************** Check TIME ****************/
if (am != 0 && pm == 0 && hour <= 12)
{ /* If A.M. */
hour = (hour == 12) ? 0 : hour;
}
else if (am == 0 && pm != 0 && hour <= 12)
{ /* If P.M. */
hour = (hour == 12) ? hour : hour + 12;
}
else if (am == 0 && pm == 0)
{ /* If military time */
;
}
else
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
db_time_encode (&tmp_time, hour, minute, second);
if (type == DB_TYPE_TIMESTAMP)
{
if (db_timestamp_encode_ses (&tmp_date, &tmp_time, &tmp_timestamp, NULL) != NO_ERROR)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
db_make_timestamp (result_timestamp, tmp_timestamp);
}
else
{
DB_TIMESTAMPTZ db_timestamptz;
if (set_tzh == true || set_tzm == true)
{
error_status = tz_create_timestamptz_from_offset (&tmp_date, &tmp_time, tzh, tzm, &db_timestamptz);
if (error_status != NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
}
else
{
TZ_REGION session_tz_region;
const char *dst = NULL;
if (len_tzd > 0)
{
assert (start_tzd >= 0);
dst = initial_buf_str + start_tzd;
}
tz_get_session_tz_region (&session_tz_region);
error_status =
tz_create_timestamptz_from_zoneid_and_tzd (&tmp_date, &tmp_time, &session_tz_region, zone_id, dst, len_tzd,
&db_timestamptz);
if (error_status != NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
}
db_make_timestamptz (result_timestamp, &db_timestamptz);
}
exit:
if (do_free_buf_str)
{
assert (initial_buf_str != NULL);
db_private_free (NULL, initial_buf_str);
}
if (do_free_buf_format)
{
assert (initial_buf_format != NULL);
db_private_free (NULL, initial_buf_format);
}
return error_status;
}
/*
* db_to_datetime () -
*/
int
db_to_datetime (const DB_VALUE * src_str, const DB_VALUE * format_str, const DB_VALUE * date_lang, const DB_TYPE type,
DB_VALUE * result_datetime)
{
int error_status = NO_ERROR;
DB_DATETIME tmp_datetime;
const char *cur_format_str_ptr, *next_format_str_ptr;
char *cs; /* current source string pointer */
const char *last_format, *last_src;
int cur_format_size;
TIMESTAMP_FORMAT cur_format;
int month = 0, day = 0, year = 0, day_of_the_week = 0, week = -1;
int monthcount = 0, daycount = 0, yearcount = 0, day_of_the_weekcount = 0;
double fraction;
int millisecond = 0, second = 0, minute = 0, hour = 0;
int time_count = 0;
int mil_time_count = 0;
int am = false;
int pm = false;
int i;
bool no_user_format;
INTL_LANG date_lang_id;
INTL_CODESET codeset;
INTL_CODESET frmt_codeset;
char stack_buf_str[64], stack_buf_format[64];
char *initial_buf_str = NULL, *initial_buf_format = NULL;
bool do_free_buf_str = false, do_free_buf_format = false;
DB_VALUE default_format;
bool has_user_format = false;
bool dummy;
int tzh = 0, tzm = 0;
bool set_tzh = false, set_tzm = false;
int start_tzr = -1, start_tzd = -1;
int len_tzr = -1, len_tzd = -1;
int zone_id = -1;
bool is_negative_tzd = false;
assert (src_str != (DB_VALUE *) NULL);
assert (result_datetime != (DB_VALUE *) NULL);
assert (type == DB_TYPE_DATETIME || type == DB_TYPE_DATETIMETZ);
db_make_null (&default_format);
if (DB_IS_NULL (src_str))
{
db_make_null (result_datetime);
return error_status;
}
error_status = check_date_lang_on_prepared (date_lang, &date_lang_id, &has_user_format);
if (error_status != NO_ERROR)
{
return error_status;
}
if (false == is_char_string (src_str))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
if (db_get_string_size (src_str) == 0)
{
error_status = ER_QSTR_EMPTY_STRING;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
if (db_get_string_size (src_str) > MAX_TOKEN_SIZE)
{
error_status = ER_QSTR_SRC_TOO_LONG;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
codeset = db_get_string_codeset (src_str);
if (lang_get_specific_locale (date_lang_id, codeset) == NULL)
{
error_status = ER_LANG_CODESET_NOT_AVAILABLE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 2, lang_get_lang_name_from_id (date_lang_id),
lang_charset_name (codeset));
return error_status;
}
error_status =
db_check_or_create_null_term_string (src_str, stack_buf_str, sizeof (stack_buf_str), true, true, &initial_buf_str,
&do_free_buf_str);
if (error_status != NO_ERROR)
{
goto exit;
}
cs = initial_buf_str;
last_src = cs + strlen (cs);
last_src = (char *) intl_backskip_spaces (cs, last_src - 1, codeset);
last_src = last_src + 1;
no_user_format = (format_str == NULL) || (!has_user_format);
if (no_user_format)
{
DB_DATETIME datetime_tmp;
DB_DATETIMETZ datetimetz_tmp;
const char *default_format_str;
/* try default CUBRID format first */
if (type == DB_TYPE_DATETIME)
{
if (db_string_to_datetime_ex ((const char *) cs, CAST_BUFLEN (last_src - cs), &datetime_tmp) == NO_ERROR)
{
db_make_datetime (result_datetime, &datetime_tmp);
goto exit;
}
}
else
{
bool has_zone;
if (db_string_to_datetimetz_ex ((const char *) cs, CAST_BUFLEN (last_src - cs), &datetimetz_tmp, &has_zone)
== NO_ERROR)
{
db_make_datetimetz (result_datetime, &datetimetz_tmp);
goto exit;
}
}
default_format_str = lang_date_format_parse (date_lang_id, codeset, type, &frmt_codeset);
if (default_format_str == NULL)
{
error_status = ER_TIMESTAMP_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
db_make_char (&default_format, strlen (default_format_str), default_format_str,
strlen (default_format_str), frmt_codeset, LANG_GET_BINARY_COLLATION (frmt_codeset));
format_str = &default_format;
}
if (DB_IS_NULL (format_str))
{
db_make_null (result_datetime);
goto exit;
}
if (false == is_char_string (format_str))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (db_get_string_size (format_str) > MAX_TOKEN_SIZE)
{
error_status = ER_QSTR_FORMAT_TOO_LONG;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (db_get_string_size (format_str) == 0)
{
error_status = ER_QSTR_EMPTY_STRING;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
frmt_codeset = db_get_string_codeset (format_str);
error_status =
db_check_or_create_null_term_string (format_str, stack_buf_format, sizeof (stack_buf_format), true, true,
&initial_buf_format, &do_free_buf_format);
if (error_status != NO_ERROR)
{
goto exit;
}
cur_format_str_ptr = initial_buf_format;
last_format = cur_format_str_ptr + strlen (cur_format_str_ptr);
/* Skip space, tab, CR */
while (cs < last_src && strchr (WHITE_CHARS, *cs))
{
cs++;
}
/* Skip space, tab, CR */
while (cur_format_str_ptr < last_format && strchr (WHITE_CHARS, *cur_format_str_ptr))
{
cur_format_str_ptr++;
}
while (cs < last_src)
{
int token_size, cmp, cs_byte_size;
int am_pm_id;
int k;
cur_format = get_next_format (cur_format_str_ptr, frmt_codeset, type, &cur_format_size, &next_format_str_ptr);
switch (cur_format)
{
case DT_YYYY:
if (yearcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
yearcount++;
}
k = parse_digits (cs, &year, 4);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
break;
case DT_YY:
if (yearcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
yearcount++;
}
k = parse_digits (cs, &year, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
i = get_cur_year ();
if (i == -1)
{
error_status = ER_SYSTEM_DATE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
year += (i / 100) * 100;
break;
case DT_MM:
if (monthcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
monthcount++;
}
k = parse_digits (cs, &month, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (month < 1 || month > 12)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_MONTH:
if (monthcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
monthcount++;
}
error_status = get_string_date_token_id (SDT_MONTH, date_lang_id, cs, codeset, &month, &token_size);
if (error_status != NO_ERROR)
{
goto exit;
}
cs += token_size;
if (month == 0)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_MON:
if (monthcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
monthcount++;
}
month = 0;
error_status = get_string_date_token_id (SDT_MONTH_SHORT, date_lang_id, cs, codeset, &month, &token_size);
if (error_status != NO_ERROR)
{
goto exit;
}
cs += token_size;
if (month == 0)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_DD:
if (daycount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
daycount++;
}
k = parse_digits (cs, &day, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (day < 0 || day > 31)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_AM:
case DT_A_M:
case DT_PM:
case DT_P_M:
if (mil_time_count != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
mil_time_count++;
}
error_status = get_string_date_token_id (SDT_AM_PM, date_lang_id, cs, codeset, &am_pm_id, &token_size);
if (error_status != NO_ERROR)
{
goto exit;
}
if (am_pm_id > 0)
{
if (am_pm_id % 2)
{
am = true;
}
else
{
pm = true;
}
}
else
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += token_size;
break;
case DT_H:
if (time_count != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
time_count++;
}
k = parse_digits (cs, &hour, 1);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (hour < 1 || hour > 12)
{
error_status = ER_TIME_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_HH:
case DT_HH12:
if (time_count != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
time_count++;
}
k = parse_digits (cs, &hour, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (hour < 1 || hour > 12)
{
error_status = ER_TIME_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_HH24:
if (time_count != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
time_count++;
}
if (mil_time_count != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
mil_time_count++;
}
k = parse_digits (cs, &hour, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (hour < 0 || hour > 23)
{
error_status = ER_TIME_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_MI:
k = parse_digits (cs, &minute, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (minute < 0 || minute > 59)
{
error_status = ER_TIME_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_SS:
k = parse_digits (cs, &second, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (second < 0 || second > 59)
{
error_status = ER_TIME_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_MS:
if (!char_isdigit (*cs))
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
for (i = 0, fraction = 100; char_isdigit (*cs); cs++, i++)
{
millisecond += (int) ((*cs - '0') * fraction + 0.5);
fraction /= 10;
}
if (millisecond < 0 || millisecond > 999)
{
error_status = ER_TIME_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_TEXT:
if (codeset != frmt_codeset)
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cmp = intl_case_match_tok (date_lang_id, codeset, (unsigned char *) (cur_format_str_ptr + 1),
(unsigned char *) cs, cur_format_size - 2, strlen (cs), &cs_byte_size);
if (cmp != 0)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += cs_byte_size;
break;
case DT_PUNCTUATION:
if (strncasecmp ((const char *) (void *) cur_format_str_ptr, (const char *) cs, cur_format_size) != 0)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += cur_format_size;
break;
case DT_CC:
case DT_Q:
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
case DT_DAY:
if (day_of_the_weekcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
day_of_the_weekcount++;
}
error_status = get_string_date_token_id (SDT_DAY, date_lang_id, cs, codeset, &day_of_the_week, &token_size);
if (error_status != NO_ERROR)
{
goto exit;
}
cs += token_size;
if (day_of_the_week == 0)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_DY:
if (day_of_the_weekcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
day_of_the_weekcount++;
}
error_status =
get_string_date_token_id (SDT_DAY_SHORT, date_lang_id, cs, codeset, &day_of_the_week, &token_size);
if (error_status != NO_ERROR)
{
goto exit;
}
cs += token_size;
if (day_of_the_week == 0)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_D:
if (day_of_the_weekcount != 0)
{
error_status = ER_QSTR_FORMAT_DUPLICATION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
else
{
day_of_the_weekcount++;
}
k = parse_digits (cs, &day_of_the_week, 1);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
if (day_of_the_week < 1 || day_of_the_week > 7)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
case DT_TZR:
{
if (type == DB_TYPE_DATETIME)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
start_tzr = CAST_BUFLEN (cs - initial_buf_str);
zone_id = tz_get_best_match_zone (cs, &len_tzr);
if (zone_id < 0)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += len_tzr;
}
break;
case DT_TZD:
{
if (type == DB_TYPE_DATETIME)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
start_tzd = CAST_BUFLEN (cs - initial_buf_str);
len_tzd = parse_tzd (cs, TZD_MAX_EXPECTED_LEN);
if (len_tzd < 0)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += len_tzd;
}
break;
case DT_TZH:
case DT_TZM:
{
int *p = NULL;
if (type == DB_TYPE_DATETIME)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (cur_format == DT_TZH)
{
p = &tzh;
set_tzh = true;
}
else
{
p = &tzm;
set_tzm = true;
}
/* Get the timezone hour offset */
if (*cs == '+' || *cs == '-')
{
if (cur_format == DT_TZM)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (*cs == '-')
{
is_negative_tzd = true;
}
else
{
is_negative_tzd = false;
}
cs++;
}
k = parse_digits (cs, p, 2);
if (k <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
cs += k;
}
break;
case DT_NORMAL:
case DT_INVALID:
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
default:
break;
}
while (cs < last_src && strchr (WHITE_CHARS, *cs))
{
cs++;
}
cur_format_str_ptr = next_format_str_ptr;
/* Skip space, tab, CR */
while (cur_format_str_ptr < last_format && strchr (WHITE_CHARS, *cur_format_str_ptr))
{
cur_format_str_ptr++;
}
if (last_format == next_format_str_ptr)
{
while (cs < last_src && strchr (WHITE_CHARS, *cs))
{
cs++;
}
if (cs != last_src)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
break;
}
}
/* 2. Validations */
if (len_tzd > 0 || len_tzr > 0)
{
if (set_tzh == true || set_tzm == true)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
}
if (is_negative_tzd)
{
tzh = -tzh;
tzm = -tzm;
}
/* Both format and src should end at same time */
if (cs != last_src || cur_format_str_ptr != last_format)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/************** Check DATE ****************/
year = (yearcount == 0) ? get_cur_year () : year;
month = (monthcount == 0) ? get_cur_month () : month;
day = (daycount == 0) ? 1 : day;
week = (day_of_the_weekcount == 0) ? -1 : day_of_the_week - 1;
if (week != -1 && week != db_get_day_of_week (year, month, day))
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/************** Check TIME ****************/
if (am != 0 && pm == 0 && hour <= 12)
{ /* If A.M. */
hour = (hour == 12) ? 0 : hour;
}
else if (am == 0 && pm != 0 && hour <= 12)
{ /* If P.M. */
hour = (hour == 12) ? hour : hour + 12;
}
else if (am == 0 && pm == 0)
{ /* If military time */
;
}
else
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
/************* Make DATETIME *****************/
error_status = db_datetime_encode (&tmp_datetime, month, day, year, hour, minute, second, millisecond);
if (error_status != NO_ERROR)
{
goto exit;
}
if (type == DB_TYPE_DATETIME)
{
if (db_make_datetime (result_datetime, &tmp_datetime) != NO_ERROR)
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
}
else
{
DB_DATETIMETZ db_datetimetz;
if (set_tzh == true || set_tzm == true)
{
error_status = tz_create_datetimetz_from_offset (&tmp_datetime, tzh, tzm, &db_datetimetz);
if (error_status != NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
}
else
{
TZ_REGION session_tz_region;
const char *dst = NULL;
tz_get_session_tz_region (&session_tz_region);
if (len_tzd > 0)
{
assert (start_tzd >= 0);
dst = initial_buf_str + start_tzd;
}
error_status =
tz_create_datetimetz_from_zoneid_and_tzd (&tmp_datetime, &session_tz_region, zone_id, dst, len_tzd, false,
&db_datetimetz);
if (error_status != NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
}
db_make_datetimetz (result_datetime, &db_datetimetz);
}
exit:
if (do_free_buf_str)
{
assert (initial_buf_str != NULL);
db_private_free (NULL, initial_buf_str);
}
if (do_free_buf_format)
{
assert (initial_buf_format != NULL);
db_private_free (NULL, initial_buf_format);
}
return error_status;
}
/*
* adjust_precision () - Change representation of 'data' as of
* 'precision' and 'scale'.
* When data has invalid format, just return
* return : DOMAIN_INCOMPATIBLE, DOMAIN_OVERFLOW, NO_ERROR
*
* Note : This function is not localized in relation to fractional and digit
* grouping symbols. It assumes the default symbols ('.' for fraction
* symbol and ',' for digit grouping symbol)
*/
static int
adjust_precision (char *data, int precision, int scale)
{
char tmp_data[DB_MAX_NUMERIC_PRECISION * 2 + 1];
int scale_counter = 0;
int i = 0;
int before_dec_point = 0;
int after_dec_point = 0;
int space_started = false;
if (data == NULL || precision < 0 || precision > DB_MAX_NUMERIC_PRECISION || scale < 0
|| scale > DB_MAX_NUMERIC_PRECISION)
{
return DOMAIN_INCOMPATIBLE;
}
if (*data == '-')
{
tmp_data[0] = '-';
i++;
}
else if (*data == '+')
{
i++;
}
for (; i < DB_MAX_NUMERIC_PRECISION && *(data + i) != '\0' && *(data + i) != '.'; i++)
{
if (char_isdigit (*(data + i)))
{
tmp_data[i] = *(data + i);
before_dec_point++;
}
else if (char_isspace (*(data + i)))
{
space_started = true;
break;
}
else
{
return DOMAIN_INCOMPATIBLE;
}
}
if (space_started != 0)
{
int j = i;
while (char_isspace (*(data + j)))
{
j++;
}
if (*(data + j) != '\0')
{
return DOMAIN_INCOMPATIBLE;
}
}
if (*(data + i) == '.')
{
tmp_data[i] = '.';
i++;
while (*(data + i) != '\0' && scale_counter < scale)
{
if (char_isdigit (*(data + i)))
{
tmp_data[i] = *(data + i);
after_dec_point++;
}
else if (char_isspace (*(data + i)))
{
space_started = true;
break;
}
else
{
return DOMAIN_INCOMPATIBLE;
}
scale_counter++;
i++;
}
if (space_started != 0)
{
int j = i;
while (char_isspace (*(data + j)))
{
j++;
}
if (*(data + j) != '\0')
{
return DOMAIN_INCOMPATIBLE;
}
}
while (scale_counter < scale)
{
tmp_data[i] = '0';
scale_counter++;
i++;
}
}
else if (*(data + i) == '\0')
{
tmp_data[i] = '.';
i++;
while (scale_counter < scale)
{
tmp_data[i] = '0';
scale_counter++;
i++;
}
}
else
{
return DOMAIN_COMPATIBLE;
}
if (before_dec_point + after_dec_point > DB_MAX_NUMERIC_PRECISION || after_dec_point > DB_DEFAULT_NUMERIC_PRECISION
|| before_dec_point > precision - scale)
{
return DOMAIN_OVERFLOW;
}
tmp_data[i] = '\0';
strcpy (data, tmp_data);
return NO_ERROR;
}
/*
* db_to_number () -
*
* Note : This function is localized in relation to fractional and digit
* grouping symbols.
*/
int
db_to_number (const DB_VALUE * src_str, const DB_VALUE * format_str, const DB_VALUE * number_lang,
DB_VALUE * result_num)
{
#define DB_NUMERIC_E38_MAX "99999999999999999999999999999999999999"
/* default precision and scale is (38, 0) */
/* it is more profitable that the definition of this value is located in some header file */
const char *dflt_format_str = DB_NUMERIC_E38_MAX;
int error_status = NO_ERROR;
char *cs; /* current source string pointer */
char *last_cs;
char *format_str_ptr;
char *last_format;
char *next_fsp; /* next format string pointer */
int token_length;
int count_format = 0;
int cur_format;
int precision = 0; /* retain precision of format_str */
int scale = 0;
int loopvar, met_decptr = 0;
int use_default_precision = 0;
char *first_cs_for_error, *first_format_str_for_error;
char stack_buf_str[64], stack_buf_format[64];
char *initial_buf_str = NULL, *initial_buf_format = NULL;
bool do_free_buf_str = false, do_free_buf_format = false;
char digit_grouping_symbol;
char fraction_symbol;
bool has_user_format;
bool dummy;
int number_lang_id;
TP_DOMAIN *domain;
INTL_CODESET format_codeset = INTL_CODESET_NONE;
assert (src_str != (DB_VALUE *) NULL);
assert (result_num != (DB_VALUE *) NULL);
assert (number_lang != NULL);
assert (format_str != NULL);
if (DB_IS_NULL (src_str))
{
db_make_null (result_num);
return error_status;
}
if (false == is_char_string (src_str))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
if (db_get_string_size (src_str) == 0)
{
error_status = ER_QSTR_EMPTY_STRING;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
if (db_get_string_size (src_str) > MAX_TOKEN_SIZE)
{
error_status = ER_QSTR_SRC_TOO_LONG;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
assert (DB_VALUE_TYPE (number_lang) == DB_TYPE_INTEGER);
number_lang_id = lang_get_lang_id_from_flag (db_get_int (number_lang), &has_user_format, &dummy);
digit_grouping_symbol = lang_digit_grouping_symbol (number_lang_id);
fraction_symbol = lang_digit_fractional_symbol (number_lang_id);
error_status =
db_check_or_create_null_term_string (src_str, stack_buf_str, sizeof (stack_buf_str), true, false, &initial_buf_str,
&do_free_buf_str);
if (error_status != NO_ERROR)
{
goto exit;
}
cs = initial_buf_str;
last_cs = cs + strlen (cs);
/* If there is no format */
if (!has_user_format)
{
format_str_ptr = (char *) dflt_format_str;
last_format = format_str_ptr + strlen (dflt_format_str);
}
else /* format_str != NULL */
{
/* Format string type checking */
if (is_char_string (format_str))
{
if (db_get_string_size (format_str) > MAX_TOKEN_SIZE)
{
error_status = ER_QSTR_FORMAT_TOO_LONG;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
error_status =
db_check_or_create_null_term_string (format_str, stack_buf_format, sizeof (stack_buf_format), true, false,
&initial_buf_format, &do_free_buf_format);
if (error_status != NO_ERROR)
{
goto exit;
}
format_str_ptr = initial_buf_format;
last_format = format_str_ptr + strlen (format_str_ptr);
format_codeset = db_get_string_codeset (format_str);
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
if (db_get_string_size (format_str) == 0)
{
error_status = ER_QSTR_EMPTY_STRING;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
}
last_cs = (char *) intl_backskip_spaces (cs, last_cs - 1, db_get_string_codeset (src_str));
last_cs = last_cs + 1;
/* Skip space, tab, CR */
while (cs < last_cs && strchr (WHITE_CHARS, *cs))
{
cs++;
}
while (format_str_ptr < last_format && strchr (WHITE_CHARS, *format_str_ptr))
{
format_str_ptr++;
}
first_cs_for_error = cs;
first_format_str_for_error = format_str_ptr;
/* get precision and scale of format_str */
for (loopvar = 0; format_str_ptr + loopvar < last_format; loopvar++)
{
switch (*(format_str_ptr + loopvar))
{
case '9':
case '0':
precision++;
if (met_decptr > 0)
{
scale++;
}
break;
case 'c':
case 'C':
case 's':
case 'S':
break;
default:
if (*(format_str_ptr + loopvar) == digit_grouping_symbol)
{
break;
}
else if (*(format_str_ptr + loopvar) == fraction_symbol)
{
met_decptr++;
break;
}
precision = 0;
scale = 0;
use_default_precision = 1;
}
if (precision + scale > DB_MAX_NUMERIC_PRECISION)
{
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)));
error_status = ER_IT_DATA_OVERFLOW;
goto exit;
}
if (use_default_precision == 1)
{
/* scientific notation */
precision = DB_MAX_NUMERIC_PRECISION;
scale = DB_DEFAULT_NUMERIC_PRECISION;
break;
}
}
/* Skip space, tab, CR */
while (cs < last_cs)
{
cur_format =
get_number_token (number_lang_id, format_str_ptr, &token_length, last_format, &next_fsp, format_codeset);
switch (cur_format)
{
case N_FORMAT:
if (count_format != 0)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
error_status =
make_number (cs, last_cs, db_get_string_codeset (src_str), format_str_ptr, &token_length, result_num,
precision, scale, number_lang_id);
if (error_status == NO_ERROR)
{
count_format++;
cs += token_length;
}
else if (error_status == ER_IT_DATA_OVERFLOW)
{
domain = tp_domain_resolve_default (DB_TYPE_NUMERIC);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 1, pr_type_name (TP_DOMAIN_TYPE (domain)));
goto exit;
}
else
{
goto format_mismatch;
}
break;
case N_SPACE:
if (!strchr (WHITE_CHARS, *cs))
{
goto format_mismatch;
}
while (cs < last_cs && strchr (WHITE_CHARS, *cs))
{
cs++;
}
break;
case N_TEXT:
if (strncasecmp ((format_str_ptr + 1), cs, token_length - 2) != 0)
{
goto format_mismatch;
}
cs += token_length - 2;
break;
case N_INVALID:
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
case N_END:
/* Skip space, tab, CR */
while (cs < last_cs && strchr (WHITE_CHARS, *cs))
{
cs++;
}
if (cs != last_cs)
{
goto format_mismatch;
}
break;
}
while (cs < last_cs && strchr (WHITE_CHARS, *cs))
{
cs++;
}
format_str_ptr = next_fsp;
/* Skip space, tab, CR */
while (format_str_ptr < last_format && strchr (WHITE_CHARS, *format_str_ptr))
{
format_str_ptr++;
}
}
/* Both format and src should end at same time */
if (cs != last_cs || format_str_ptr != last_format)
{
goto format_mismatch;
}
result_num->domain.numeric_info.precision = precision;
result_num->domain.numeric_info.scale = scale;
if (do_free_buf_str)
{
assert (initial_buf_str != NULL);
db_private_free (NULL, initial_buf_str);
}
if (do_free_buf_format)
{
assert (initial_buf_format != NULL);
db_private_free (NULL, initial_buf_format);
}
return error_status;
format_mismatch:
while (strchr (WHITE_CHARS, *(last_cs - 1)))
{
last_cs--;
}
*last_cs = '\0';
error_status = ER_QSTR_TONUM_FORMAT_MISMATCH;
if (first_format_str_for_error == dflt_format_str)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 2, first_cs_for_error, "default");
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 2, first_cs_for_error, first_format_str_for_error);
}
exit:
if (do_free_buf_str)
{
assert (initial_buf_str != NULL);
db_private_free (NULL, initial_buf_str);
}
if (do_free_buf_format)
{
assert (initial_buf_format != NULL);
db_private_free (NULL, initial_buf_format);
}
return error_status;
#undef DB_NUMERIC_E38_MAX
}
/*
* date_to_char () -
*/
static int
date_to_char (const DB_VALUE * src_value, const DB_VALUE * format_str, const DB_VALUE * date_lang,
DB_VALUE * result_str, const TP_DOMAIN * domain)
{
int error_status = NO_ERROR;
DB_TYPE src_type;
const char *cur_format_str_ptr, *next_format_str_ptr;
const char *last_format_str_ptr;
int cur_format_size;
TIMESTAMP_FORMAT cur_format;
char *result_buf = NULL;
int result_len = 0;
int result_size = 0;
int month = 0, day = 0, year = 0;
int second = 0, minute = 0, hour = 0, millisecond = 0;
int i;
unsigned int tmp_int;
DB_DATE tmp_date;
DB_TIME tmp_time;
bool no_user_format;
INTL_LANG date_lang_id;
char stack_buf_format[64];
char *initial_buf_format = NULL;
bool do_free_buf_format = false;
const INTL_CODESET codeset = TP_DOMAIN_CODESET (domain);
const int collation_id = TP_DOMAIN_COLLATION (domain);
bool has_user_format = false;
bool dummy;
assert (src_value != (DB_VALUE *) NULL);
assert (result_str != (DB_VALUE *) NULL);
assert (date_lang != (DB_VALUE *) NULL);
if (DB_IS_NULL (src_value))
{
db_make_null (result_str);
return error_status;
}
src_type = DB_VALUE_DOMAIN_TYPE (src_value);
if (TP_IS_DATE_OR_TIME_TYPE (src_type) != true)
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
if (date_lang == NULL || DB_IS_NULL (date_lang))
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
error_status = check_date_lang_on_prepared (date_lang, &date_lang_id, &has_user_format);
if (error_status != NO_ERROR)
{
return error_status;
}
no_user_format = (format_str == NULL) || (!has_user_format);
if (no_user_format)
{
int retval = 0;
switch (src_type)
{
case DB_TYPE_DATE:
result_buf = (char *) db_private_alloc (NULL, QSTR_DATE_LENGTH + 1);
if (result_buf == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
return error_status;
}
result_len = QSTR_DATE_LENGTH;
retval = db_date_to_string (result_buf, QSTR_DATE_LENGTH + 1, db_get_date (src_value));
break;
case DB_TYPE_TIME:
result_buf = (char *) db_private_alloc (NULL, QSTR_TIME_LENGTH + 1);
if (result_buf == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
return error_status;
}
result_len = QSTR_TIME_LENGTH;
retval = db_time_to_string (result_buf, QSTR_TIME_LENGTH + 1, db_get_time (src_value));
break;
case DB_TYPE_TIMESTAMP:
result_buf = (char *) db_private_alloc (NULL, QSTR_TIME_STAMPLENGTH + 1);
if (result_buf == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
return error_status;
}
result_len = QSTR_TIME_STAMPLENGTH;
retval = db_timestamp_to_string (result_buf, QSTR_TIME_STAMPLENGTH + 1, db_get_timestamp (src_value));
break;
case DB_TYPE_DATETIME:
result_buf = (char *) db_private_alloc (NULL, QSTR_DATETIME_LENGTH + 1);
if (result_buf == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
return error_status;
}
result_len = QSTR_DATETIME_LENGTH;
retval = db_datetime_to_string (result_buf, QSTR_DATETIME_LENGTH + 1, db_get_datetime (src_value));
break;
case DB_TYPE_DATETIMETZ:
{
DB_DATETIMETZ dtz;
result_buf = (char *) db_private_alloc (NULL, DATETIMETZ_BUF_SIZE);
if (result_buf == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
return error_status;
}
dtz = *db_get_datetimetz (src_value);
retval = db_datetimetz_to_string (result_buf, DATETIMETZ_BUF_SIZE, &dtz.datetime, &dtz.tz_id);
result_len = retval;
}
break;
case DB_TYPE_DATETIMELTZ:
result_buf = (char *) db_private_alloc (NULL, DATETIMETZ_BUF_SIZE);
if (result_buf == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
return error_status;
}
retval = db_datetimeltz_to_string (result_buf, DATETIMETZ_BUF_SIZE, db_get_datetime (src_value));
result_len = retval;
break;
case DB_TYPE_TIMESTAMPTZ:
{
DB_TIMESTAMPTZ tsmp_tz;
result_buf = (char *) db_private_alloc (NULL, TIMESTAMPTZ_BUF_SIZE);
if (result_buf == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
return error_status;
}
tsmp_tz = *db_get_timestamptz (src_value);
retval = db_timestamptz_to_string (result_buf, TIMESTAMPTZ_BUF_SIZE, &tsmp_tz.timestamp, &tsmp_tz.tz_id);
result_len = retval;
}
break;
case DB_TYPE_TIMESTAMPLTZ:
result_buf = (char *) db_private_alloc (NULL, TIMESTAMPTZ_BUF_SIZE);
if (result_buf == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
return error_status;
}
retval = db_timestampltz_to_string (result_buf, TIMESTAMPTZ_BUF_SIZE, db_get_timestamp (src_value));
result_len = retval;
break;
default:
break;
}
if (retval == 0)
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
db_private_free_and_init (NULL, result_buf);
return error_status;
}
db_make_varchar (result_str, result_len, result_buf, result_len, codeset, collation_id);
}
else
{
INTL_CODESET frmt_codeset;
char tzr[TZR_SIZE + 1], tzd[TZ_DS_STRING_SIZE + 1];
int tzh, tzm;
int ntzr = 0, ntzd = 0;
int len_tzr = 0, len_tzd = 0;
bool has_tzh = false, has_tzm = false;
TZ_ID tz_id;
tzr[0] = '\0';
tzd[0] = '\0';
assert (!DB_IS_NULL (date_lang));
if (DB_IS_NULL (format_str))
{
db_make_null (result_str);
goto exit;
}
/* compute allocation size : trade-off exact size (and small mem usage) vs speed */
result_len = (db_get_string_length (format_str) * QSTR_TO_CHAR_LEN_MULTIPLIER_RATIO);
if (db_get_string_size (format_str) == 0)
{
error_status = ER_QSTR_EMPTY_STRING;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
frmt_codeset = db_get_string_codeset (format_str);
error_status =
db_check_or_create_null_term_string (format_str, stack_buf_format, sizeof (stack_buf_format), true, false,
&initial_buf_format, &do_free_buf_format);
if (error_status != NO_ERROR)
{
goto exit;
}
cur_format_str_ptr = initial_buf_format;
last_format_str_ptr = cur_format_str_ptr + strlen (cur_format_str_ptr);
/* First compute the number of TZR and TZD tokens if any */
cur_format = DT_NORMAL;
for (;;)
{
cur_format =
get_next_format (cur_format_str_ptr, frmt_codeset, src_type, &cur_format_size, &next_format_str_ptr);
switch (cur_format)
{
case DT_TZR:
ntzr++;
break;
case DT_TZD:
ntzd++;
break;
case DT_TZH:
has_tzh = true;
break;
case DT_TZM:
has_tzm = true;
break;
case DT_INVALID:
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
default:
break;
}
cur_format_str_ptr = next_format_str_ptr;
if (next_format_str_ptr == last_format_str_ptr)
{
break;
}
}
switch (src_type)
{
case DB_TYPE_DATE:
db_date_decode (db_get_date (src_value), &month, &day, &year);
break;
case DB_TYPE_TIME:
{
DB_TIME *tm;
tm = db_get_time (src_value);
if (ntzr != 0 || ntzd != 0 || has_tzh == true || has_tzm == true)
{
error_status = tz_create_session_tzid_for_time (tm, true, &tz_id);
if (error_status != NO_ERROR)
{
goto exit;
}
}
db_time_decode (tm, &hour, &minute, &second);
}
break;
case DB_TYPE_TIMESTAMP:
{
DB_TIMESTAMP *tsmp;
tsmp = db_get_timestamp (src_value);
if (ntzr != 0 || ntzd != 0 || has_tzh == true || has_tzm == true)
{
error_status = tz_create_session_tzid_for_timestamp (tsmp, &tz_id);
if (error_status != NO_ERROR)
{
goto exit;
}
}
(void) db_timestamp_decode_ses (tsmp, &tmp_date, &tmp_time);
db_date_decode (&tmp_date, &month, &day, &year);
db_time_decode (&tmp_time, &hour, &minute, &second);
}
break;
case DB_TYPE_DATETIME:
{
DB_DATETIME *dt;
dt = db_get_datetime (src_value);
if (ntzr != 0 || ntzd != 0 || has_tzh == true || has_tzm == true)
{
error_status = tz_create_session_tzid_for_datetime (dt, true, &tz_id);
if (error_status != NO_ERROR)
{
goto exit;
}
}
db_datetime_decode (dt, &month, &day, &year, &hour, &minute, &second, &millisecond);
}
break;
case DB_TYPE_DATETIMETZ:
{
DB_DATETIMETZ dtz;
DB_DATETIME dt_local;
dtz = *db_get_datetimetz (src_value);
tz_id = dtz.tz_id;
error_status = tz_utc_datetimetz_to_local (&dtz.datetime, &dtz.tz_id, &dt_local);
if (error_status != NO_ERROR)
{
goto exit;
}
db_datetime_decode (&dt_local, &month, &day, &year, &hour, &minute, &second, &millisecond);
}
break;
case DB_TYPE_DATETIMELTZ:
{
DB_DATETIME *dt, dt_local;
dt = db_get_datetime (src_value);
error_status = tz_create_session_tzid_for_datetime (dt, true, &tz_id);
if (error_status != NO_ERROR)
{
goto exit;
}
error_status = tz_utc_datetimetz_to_local (dt, &tz_id, &dt_local);
if (error_status != NO_ERROR)
{
goto exit;
}
db_datetime_decode (&dt_local, &month, &day, &year, &hour, &minute, &second, &millisecond);
}
break;
case DB_TYPE_TIMESTAMPTZ:
{
DB_TIMESTAMPTZ tsmp_tz;
DB_DATE date;
DB_TIME time;
tsmp_tz = *db_get_timestamptz (src_value);
tz_id = tsmp_tz.tz_id;
error_status = db_timestamp_decode_w_tz_id (&tsmp_tz.timestamp, &tsmp_tz.tz_id, &date, &time);
if (error_status != NO_ERROR)
{
goto exit;
}
db_date_decode (&date, &month, &day, &year);
db_time_decode (&time, &hour, &minute, &second);
}
break;
case DB_TYPE_TIMESTAMPLTZ:
{
DB_TIMESTAMP *tsmp;
DB_DATE date;
DB_TIME time;
tsmp = db_get_timestamp (src_value);
error_status = tz_create_session_tzid_for_timestamp (tsmp, &tz_id);
if (error_status != NO_ERROR)
{
goto exit;
}
error_status = db_timestamp_decode_w_tz_id (tsmp, &tz_id, &date, &time);
if (error_status != NO_ERROR)
{
goto exit;
}
db_date_decode (&date, &month, &day, &year);
db_time_decode (&time, &hour, &minute, &second);
}
break;
default:
break;
}
if (ntzr != 0 || ntzd != 0 || has_tzh == true || has_tzm == true)
{
int len = db_get_string_length (format_str);
int left;
error_status = tz_explain_tz_id (&tz_id, tzr, TZR_SIZE + 1, tzd, TZ_DS_STRING_SIZE + 1, &tzh, &tzm);
if (error_status != NO_ERROR)
{
goto exit;
}
left = len - 3 * ntzr - 3 * ntzd;
len_tzr = strlen (tzr);
len_tzd = strlen (tzd);
result_len = left * QSTR_TO_CHAR_LEN_MULTIPLIER_RATIO + len_tzr * ntzr + len_tzd * ntzd;
}
result_size = result_len * INTL_CODESET_MULT (codeset);
if (result_size > MAX_TOKEN_SIZE)
{
error_status = ER_QSTR_FORMAT_TOO_LONG;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto exit;
}
result_buf = (char *) db_private_alloc (NULL, result_size + 1);
if (result_buf == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
goto exit;
}
i = 0;
cur_format = DT_NORMAL;
cur_format_str_ptr = initial_buf_format;
while (i < result_size)
{
int token_case_mode;
int token_size;
cur_format =
get_next_format (cur_format_str_ptr, frmt_codeset, src_type, &cur_format_size, &next_format_str_ptr);
switch (cur_format)
{
case DT_CC:
if (month == 0 && day == 0 && year == 0)
{
goto zerodate_exit;
}
tmp_int = (year / 100) + 1;
sprintf (&result_buf[i], "%02d\n", tmp_int);
i += 2;
cur_format_str_ptr += 2;
break;
case DT_YYYY:
sprintf (&result_buf[i], "%04d\n", year);
i += 4;
break;
case DT_YY:
tmp_int = year - (year / 100) * 100;
sprintf (&result_buf[i], "%02d\n", tmp_int);
i += 2;
break;
case DT_MM:
sprintf (&result_buf[i], "%02d\n", month);
i += 2;
break;
case DT_MONTH:
case DT_MON:
if (month == 0 && day == 0 && year == 0)
{
goto zerodate_exit;
}
if (*cur_format_str_ptr == 'm')
{
token_case_mode = 1;
}
else if (*(cur_format_str_ptr + 1) == 'O')
{
token_case_mode = 2;
}
else
{
token_case_mode = 0;
}
if (cur_format == DT_MONTH)
{
error_status =
print_string_date_token (SDT_MONTH, date_lang_id, codeset, month - 1, token_case_mode,
&result_buf[i], &token_size);
}
else /* cur_format == DT_MON */
{
error_status =
print_string_date_token (SDT_MONTH_SHORT, date_lang_id, codeset, month - 1, token_case_mode,
&result_buf[i], &token_size);
}
if (error_status != NO_ERROR)
{
db_private_free_and_init (NULL, result_buf);
goto exit;
}
i += token_size;
break;
case DT_Q:
if (month == 0 && day == 0 && year == 0)
{
goto zerodate_exit;
}
result_buf[i] = '1' + ((month - 1) / 3);
i++;
break;
case DT_DD:
sprintf (&result_buf[i], "%02d\n", day);
i += 2;
break;
case DT_DAY:
case DT_DY:
if (month == 0 && day == 0 && year == 0)
{
goto zerodate_exit;
}
tmp_int = get_day (month, day, year);
if (*cur_format_str_ptr == 'd')
{
token_case_mode = 1;
}
else if (*(cur_format_str_ptr + 1) == 'A') /* "DAY" */
{
token_case_mode = 2;
}
else if (*(cur_format_str_ptr + 1) == 'Y') /* "DY" */
{
token_case_mode = 2;
}
else
{
token_case_mode = 0;
}
if (cur_format == DT_DAY)
{
error_status =
print_string_date_token (SDT_DAY, date_lang_id, codeset, tmp_int, token_case_mode, &result_buf[i],
&token_size);
}
else /* cur_format == DT_DY */
{
error_status =
print_string_date_token (SDT_DAY_SHORT, date_lang_id, codeset, tmp_int, token_case_mode,
&result_buf[i], &token_size);
}
if (error_status != NO_ERROR)
{
db_private_free_and_init (NULL, result_buf);
goto exit;
}
i += token_size;
break;
case DT_D:
if (month == 0 && day == 0 && year == 0)
{
goto zerodate_exit;
}
tmp_int = get_day (month, day, year);
result_buf[i] = '0' + tmp_int + 1; /* sun=1 */
i += 1;
break;
case DT_AM:
case DT_PM:
{
int am_pm_id = -1;
int am_pm_len = 0;
if (0 <= hour && hour <= 11)
{
if (*cur_format_str_ptr == 'a' || *cur_format_str_ptr == 'p')
{
am_pm_id = (int) am_NAME;
}
else if (*(cur_format_str_ptr + 1) == 'm')
{
am_pm_id = (int) Am_NAME;
}
else
{
am_pm_id = (int) AM_NAME;
}
}
else if (12 <= hour && hour <= 23)
{
if (*cur_format_str_ptr == 'p' || *cur_format_str_ptr == 'a')
{
am_pm_id = (int) pm_NAME;
}
else if (*(cur_format_str_ptr + 1) == 'm')
{
am_pm_id = (int) Pm_NAME;
}
else
{
am_pm_id = (int) PM_NAME;
}
}
else
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
db_private_free_and_init (NULL, result_buf);
goto exit;
}
assert (am_pm_id >= (int) am_NAME && am_pm_id <= (int) P_M_NAME);
error_status =
print_string_date_token (SDT_AM_PM, date_lang_id, codeset, am_pm_id, 0, &result_buf[i], &am_pm_len);
if (error_status != NO_ERROR)
{
db_private_free_and_init (NULL, result_buf);
goto exit;
}
i += am_pm_len;
}
break;
case DT_A_M:
case DT_P_M:
{
int am_pm_id = -1;
int am_pm_len = 0;
if (0 <= hour && hour <= 11)
{
if (*cur_format_str_ptr == 'a' || *cur_format_str_ptr == 'p')
{
am_pm_id = (int) a_m_NAME;
}
else if (*(cur_format_str_ptr + 2) == 'm')
{
am_pm_id = (int) A_m_NAME;
}
else
{
am_pm_id = (int) A_M_NAME;
}
}
else if (12 <= hour && hour <= 23)
{
if (*cur_format_str_ptr == 'p' || *cur_format_str_ptr == 'a')
{
am_pm_id = (int) p_m_NAME;
}
else if (*(cur_format_str_ptr + 2) == 'm')
{
am_pm_id = (int) P_m_NAME;
}
else
{
am_pm_id = (int) P_M_NAME;
}
}
else
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
db_private_free_and_init (NULL, result_buf);
goto exit;
}
assert (am_pm_id >= (int) am_NAME && am_pm_id <= (int) P_M_NAME);
error_status =
print_string_date_token (SDT_AM_PM, date_lang_id, codeset, am_pm_id, 0, &result_buf[i], &am_pm_len);
if (error_status != NO_ERROR)
{
db_private_free_and_init (NULL, result_buf);
goto exit;
}
i += am_pm_len;
}
break;
case DT_HH:
case DT_HH12:
tmp_int = hour % 12;
if (tmp_int == 0)
{
tmp_int = 12;
}
sprintf (&result_buf[i], "%02d\n", tmp_int);
i += 2;
break;
case DT_HH24:
sprintf (&result_buf[i], "%02d\n", hour);
i += 2;
break;
case DT_MI:
sprintf (&result_buf[i], "%02d\n", minute);
i += 2;
break;
case DT_SS:
sprintf (&result_buf[i], "%02d\n", second);
i += 2;
break;
case DT_MS:
sprintf (&result_buf[i], "%03d\n", millisecond);
i += 3;
break;
case DT_INVALID:
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
db_private_free_and_init (NULL, result_buf);
goto exit;
case DT_NORMAL:
memcpy (&result_buf[i], cur_format_str_ptr, cur_format_size);
i += cur_format_size;
break;
case DT_TEXT:
memcpy (&result_buf[i], cur_format_str_ptr + 1, cur_format_size - 2);
i += cur_format_size - 2;
break;
case DT_PUNCTUATION:
memcpy (&result_buf[i], cur_format_str_ptr, cur_format_size);
i += cur_format_size;
break;
case DT_TZR:
{
memcpy (&result_buf[i], tzr, len_tzr);
i += len_tzr;
}
break;
case DT_TZD:
{
memcpy (&result_buf[i], tzd, len_tzd);
i += len_tzd;
}
break;
case DT_TZH:
if ((tzh >= 0) && (tzm >= 0))
{
sprintf (&result_buf[i], "%c%02d\n", '+', tzh);
}
else
{
sprintf (&result_buf[i], "%c%02d\n", '-', -tzh);
}
i += 3;
break;
case DT_TZM:
sprintf (&result_buf[i], "%02d\n", ((tzm < 0) ? -tzm : tzm));
result_size--;
i += 2;
break;
default:
break;
}
cur_format_str_ptr = next_format_str_ptr;
if (next_format_str_ptr == last_format_str_ptr)
{
break;
}
}
db_make_varchar (result_str, result_len, result_buf, i, codeset, collation_id);
}
result_str->need_clear = true;
exit:
if (do_free_buf_format)
{
assert (initial_buf_format != NULL);
db_private_free_and_init (NULL, initial_buf_format);
}
return error_status;
zerodate_exit:
if (result_buf != NULL)
{
db_private_free_and_init (NULL, result_buf);
}
db_make_null (result_str);
goto exit;
}
/*
* number_to_char () -
*
* Note : This function is localized in relation to fractional and digit
* grouping symbols.
*/
static int
number_to_char (const DB_VALUE * src_value, const DB_VALUE * format_str, const DB_VALUE * number_lang,
DB_VALUE * result_str, const TP_DOMAIN * domain)
{
int error_status = NO_ERROR;
char tmp_str[NUMERIC_MAX_STRING_SIZE];
char *cs = NULL; /* current source string pointer */
char *format_str_ptr, *last_format;
char *next_fsp; /* next format string pointer */
int token_length = 0;
int cur_format;
char *res_string, *res_ptr;
int i, j;
char stack_buf_format[64];
char *initial_buf_format = NULL;
bool do_free_buf_format = false;
INTL_LANG number_lang_id;
char fraction_symbol;
char digit_grouping_symbol;
bool has_user_format = false;
bool dummy;
const INTL_CODESET codeset = TP_DOMAIN_CODESET (domain);
const int collation_id = TP_DOMAIN_COLLATION (domain);
DB_CURRENCY currency = DB_CURRENCY_NULL;
assert (src_value != (DB_VALUE *) NULL);
assert (result_str != (DB_VALUE *) NULL);
assert (number_lang != (DB_VALUE *) NULL);
if (number_lang == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
assert (number_lang != NULL);
return ER_OBJ_INVALID_ARGUMENTS;
}
/* now return null */
if (DB_IS_NULL (src_value))
{
db_make_null (result_str);
return error_status;
}
error_status = check_date_lang_on_prepared (number_lang, &number_lang_id, &has_user_format);
if (error_status != NO_ERROR)
{
return error_status;
}
fraction_symbol = lang_digit_fractional_symbol (number_lang_id);
digit_grouping_symbol = lang_digit_grouping_symbol (number_lang_id);
currency = lang_locale_currency (lang_get_lang_name_from_id (number_lang_id));
switch (DB_VALUE_TYPE (src_value))
{
case DB_TYPE_NUMERIC:
numeric_db_value_print (src_value, tmp_str);
cs = (char *) db_private_alloc (NULL, strlen (tmp_str) + 1);
if (cs == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
return error_status;
}
if (number_lang_id != INTL_LANG_ENGLISH)
{
convert_locale_number (tmp_str, strlen (tmp_str), INTL_LANG_ENGLISH, number_lang_id);
}
strcpy (cs, tmp_str);
break;
case DB_TYPE_INTEGER:
sprintf (tmp_str, "%d", db_get_int (src_value));
cs = (char *) db_private_alloc (NULL, strlen (tmp_str) + 1);
if (cs == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
return error_status;
}
strcpy (cs, tmp_str);
break;
case DB_TYPE_BIGINT:
sprintf (tmp_str, "%lld", (long long) db_get_bigint (src_value));
cs = (char *) db_private_alloc (NULL, strlen (tmp_str) + 1);
if (cs == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
return error_status;
}
strcpy (cs, tmp_str);
break;
case DB_TYPE_SMALLINT:
sprintf (tmp_str, "%d", db_get_short (src_value));
cs = (char *) db_private_alloc (NULL, strlen (tmp_str) + 1);
if (cs == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
return error_status;
}
strcpy (cs, tmp_str);
break;
case DB_TYPE_FLOAT:
sprintf (tmp_str, "%.6e", db_get_float (src_value));
if (number_lang_id != INTL_LANG_ENGLISH)
{
convert_locale_number (tmp_str, strlen (tmp_str), INTL_LANG_ENGLISH, number_lang_id);
}
if (scientific_to_decimal_string (number_lang_id, tmp_str, &cs) != NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_OBJ_INVALID_ARGUMENTS;
}
break;
case DB_TYPE_DOUBLE:
sprintf (tmp_str, "%.15e", db_get_double (src_value));
if (number_lang_id != INTL_LANG_ENGLISH)
{
convert_locale_number (tmp_str, strlen (tmp_str), INTL_LANG_ENGLISH, number_lang_id);
}
if (scientific_to_decimal_string (number_lang_id, tmp_str, &cs) != NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_OBJ_INVALID_ARGUMENTS;
}
break;
case DB_TYPE_MONETARY:
currency = (db_get_monetary (src_value))->type;
sprintf (tmp_str, "%.15e", (db_get_monetary (src_value))->amount);
if (number_lang_id != INTL_LANG_ENGLISH)
{
convert_locale_number (tmp_str, strlen (tmp_str), INTL_LANG_ENGLISH, number_lang_id);
}
if (scientific_to_decimal_string (number_lang_id, tmp_str, &cs) != NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_OBJ_INVALID_ARGUMENTS;
}
break;
default:
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
assert (cs != NULL);
/* Remove 'trailing zero' source string */
for (i = 0; cs[i] != '\0'; i++)
{
if (cs[i] == fraction_symbol)
{
i += strlen (cs + i);
i--;
while (cs[i] == '0')
{
i--;
}
if (cs[i] != fraction_symbol)
{
i++;
}
cs[i] = '\0';
break;
}
}
if (format_str == NULL || !has_user_format)
{
/* Caution: VARCHAR's Size */
db_make_varchar (result_str, (ssize_t) strlen (cs), cs, strlen (cs), codeset, collation_id);
result_str->need_clear = true;
return error_status;
}
else
{
if (DB_IS_NULL (format_str))
{
db_private_free_and_init (NULL, cs);
db_make_null (result_str);
return error_status;
}
/* Format string type checking */
if (is_char_string (format_str))
{
if (db_get_string_size (format_str) > MAX_TOKEN_SIZE)
{
error_status = ER_QSTR_FORMAT_TOO_LONG;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
db_private_free_and_init (NULL, cs);
return error_status;
}
error_status =
db_check_or_create_null_term_string (format_str, stack_buf_format, sizeof (stack_buf_format), true, false,
&initial_buf_format, &do_free_buf_format);
if (error_status != NO_ERROR)
{
db_private_free_and_init (NULL, cs);
goto exit;
}
format_str_ptr = initial_buf_format;
last_format = format_str_ptr + strlen (format_str_ptr);
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
db_private_free_and_init (NULL, cs);
return error_status;
}
if (db_get_string_size (format_str) == 0)
{
error_status = ER_QSTR_EMPTY_STRING;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
db_private_free_and_init (NULL, cs);
goto exit;
}
/* Memory allocation for result */
/* size is bigger two times than strlen(format_str_ptr) */
/* because of format 'C'(currency) */
/* 'C' can be expanded accoding to CODE_SET */
/* +1 implies minus - */
res_string = (char *) db_private_alloc (NULL, strlen (format_str_ptr) * 2 + 1);
if (res_string == NULL)
{
db_private_free_and_init (NULL, cs);
assert (er_errid () != NO_ERROR);
error_status = er_errid ();
goto exit;
}
res_ptr = res_string;
/* Skip space, tab, CR */
while (strchr (WHITE_CHARS, *cs))
{
cs++;
}
while (format_str_ptr != last_format)
{
cur_format =
get_number_token (number_lang_id, format_str_ptr, &token_length, last_format, &next_fsp, codeset);
switch (cur_format)
{
case N_FORMAT:
if (make_number_to_char (number_lang_id, cs, format_str_ptr, &token_length, currency, &res_ptr, codeset)
!= NO_ERROR)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
db_private_free_and_init (NULL, cs);
db_private_free_and_init (NULL, res_string);
goto exit;
}
/* Remove space character between sign,curerency and number */
i = 0;
j = 0;
while (i < token_length)
{
DB_CURRENCY currency = DB_CURRENCY_NULL;
int symbol_size = 0;
/* check currency symbols */
if (intl_is_currency_symbol (&(res_ptr[i]), ¤cy, &symbol_size,
(CURRENCY_CHECK_MODE) (CURRENCY_CHECK_MODE_CONSOLE |
CURRENCY_CHECK_MODE_UTF8 |
CURRENCY_CHECK_MODE_ISO88591)))
{
i += symbol_size;
}
else if (res_ptr[i] == '+' || res_ptr[i] == '-')
{
i += 1;
}
else if (res_ptr[i] == ' ')
{
while (res_ptr[i + j] == ' ')
{
j++;
}
while (i > 0)
{
i--;
res_ptr[i + j] = res_ptr[i];
res_ptr[i] = ' ';
}
break;
}
else
{
break;
}
}
res_ptr += token_length;
break;
case N_SPACE:
strncpy (res_ptr, format_str_ptr, token_length);
res_ptr += token_length;
break;
case N_TEXT:
strncpy (res_ptr, (format_str_ptr + 1), token_length - 2);
res_ptr += token_length - 2;
break;
case N_INVALID:
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
db_private_free_and_init (NULL, cs);
db_private_free_and_init (NULL, res_string);
goto exit;
case N_END:
*res_ptr = '\0';
break;
}
format_str_ptr = next_fsp;
}
*res_ptr = '\0';
}
/* Both format and src should end at same time */
if (format_str_ptr != last_format)
{
error_status = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
db_private_free_and_init (NULL, cs);
db_private_free_and_init (NULL, res_string);
goto exit;
}
db_make_varchar (result_str, (ssize_t) strlen (res_string), res_string, strlen (res_string), codeset, collation_id);
result_str->need_clear = true;
db_private_free_and_init (NULL, cs);
exit:
if (do_free_buf_format)
{
assert (initial_buf_format != NULL);
db_private_free (NULL, initial_buf_format);
}
return error_status;
}
/*
* lob_to_bit_char ()
*/
static int
lob_to_bit_char (const DB_VALUE * src_value, DB_VALUE * result_value, DB_TYPE lob_type, int max_length)
{
int error_status = NO_ERROR;
DB_ELO *elo;
char *cs = NULL; /* current source string pointer */
INT64 size = 0LL;
assert (lob_type == DB_TYPE_BLOB || lob_type == DB_TYPE_CLOB);
elo = db_get_elo (src_value);
if (elo)
{
size = db_elo_size (elo);
if (size < 0)
{
if (er_errid () == ER_ES_GENERAL)
{
/* by the spec, some lob handling functions treats the read error as a NULL value */
db_make_null (result_value);
/* clear the error set before */
er_clear ();
return NO_ERROR;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_BAD_LENGTH, 1, size);
return ER_QSTR_BAD_LENGTH;
}
if (max_length < 0 || max_length > DB_MAX_STRING_LENGTH)
{
max_length = DB_MAX_STRING_LENGTH;
}
if (lob_type == DB_TYPE_BLOB)
{
/* convert max_length, which is a number of bits, to number of bytes to read */
max_length = QSTR_NUM_BYTES (max_length);
}
if (max_length > size)
{
max_length = (int) size;
}
cs = (char *) db_private_alloc (NULL, max_length + 1);
if (cs == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
return error_status;
}
if (max_length > 0)
{
error_status = db_elo_read (elo, 0, cs, max_length, NULL);
if (error_status == ER_ES_GENERAL)
{
/* by the spec, some lob handling functions treats the read error as a NULL value */
db_make_null (result_value);
db_private_free_and_init (NULL, cs);
/* clear the error set before */
er_clear ();
return NO_ERROR;
}
else if (error_status < 0)
{
db_private_free_and_init (NULL, cs);
return error_status;
}
}
cs[max_length] = '\0';
if (lob_type == DB_TYPE_BLOB)
{
/* convert the converted max_length to number of bits */
max_length *= 8;
db_make_varbit (result_value, max_length, cs, max_length);
}
else
{
db_make_varchar (result_value, max_length, cs, max_length, LANG_COERCIBLE_CODESET, LANG_COERCIBLE_COLL);
}
result_value->need_clear = true;
}
else
{
db_make_null (result_value);
}
return error_status;
}
/*
* lob_from_file () -
*/
static int
lob_from_file (const char *path, const DB_VALUE * src_value, DB_VALUE * lob_value, DB_TYPE lob_type)
{
int error_status = NO_ERROR;
DB_ELO temp_elo, *result_elo;
INT64 size, chk_size;
off_t pos;
char lob_chunk[LOB_CHUNK_SIZE + 1];
assert (lob_type == DB_TYPE_BLOB || lob_type == DB_TYPE_CLOB);
elo_init_structure (&temp_elo);
temp_elo.type = ELO_FBO;
temp_elo.locator = (char *) path;
size = db_elo_size (&temp_elo);
if (size < 0)
{
error_status = ER_ES_INVALID_PATH;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 1, db_get_string (src_value));
return error_status;
}
error_status = db_create_fbo (lob_value, lob_type);
if (error_status != NO_ERROR)
{
return error_status;
}
result_elo = db_get_elo (lob_value);
pos = 0;
while (size > 0)
{
chk_size = (size < LOB_CHUNK_SIZE) ? size : LOB_CHUNK_SIZE;
error_status = db_elo_read (&temp_elo, pos, lob_chunk, chk_size, &chk_size);
if (error_status < 0)
{
return error_status;
}
error_status = db_elo_write (result_elo, pos, lob_chunk, chk_size, NULL);
if (error_status < 0)
{
return error_status;
}
size -= chk_size;
pos += chk_size;
}
return NO_ERROR;
}
/*
* lob_length () -
*/
static int
lob_length (const DB_VALUE * src_value, DB_VALUE * result_value)
{
int error_status = NO_ERROR;
DB_ELO *elo;
INT64 length;
elo = db_get_elo (src_value);
if (elo)
{
/*
* Hack:
* In order to check the existence of the file,
* it is required to make to invoke real file operation.
* Because elo_size() will return the cached elo->size,
* we need to reset it to -1.
*/
elo->size = -1;
length = db_elo_size (elo);
if (length < 0)
{
if (er_errid () == ER_ES_GENERAL)
{
/* by the spec, some lob handling functions treats the read error as a NULL value */
db_make_null (result_value);
/* clear the error set before */
er_clear ();
return NO_ERROR;
}
error_status = (int) length;
}
else
{
db_make_bigint (result_value, length);
}
}
else
{
db_make_null (result_value);
}
return error_status;
}
/*
* make_number_to_char () -
*
* Note : This function is localized in relation to fractional and digit
* grouping symbols.
*/
static int
make_number_to_char (const INTL_LANG lang, char *num_string, char *format_str, int *length, DB_CURRENCY currency,
char **result_str, INTL_CODESET codeset)
{
int flag_sign = 1;
int leadingzero = false;
char *res_str = *result_str;
char *num, *format, *res;
char *init_format = format_str;
char format_end_char = init_format[*length];
const char fraction_symbol = lang_digit_fractional_symbol (lang);
const char digit_grouping_symbol = lang_digit_grouping_symbol (lang);
init_format[*length] = '\0';
/* code for patch.. emm.. */
if (strlen (format_str) == 5 && !strncasecmp (format_str, "seeee", 5))
{
return ER_FAILED;
}
else if (strlen (format_str) == 5 && !strncasecmp (format_str, "ceeee", 5))
{
return ER_FAILED;
}
else if (strlen (format_str) == 6 && !strncasecmp (format_str, "sceeee", 6))
{
return ER_FAILED;
}
/* Check minus */
if (*num_string == '-')
{
*res_str = '-';
num_string++;
res_str++;
flag_sign = -1;
}
/* Check sign */
if (char_tolower (*format_str) == 's')
{
if (flag_sign == 1)
{
*res_str = '+';
res_str++;
}
format_str++;
}
if (*format_str == '\0')
{
init_format[*length] = format_end_char;
/* patch for format: '9999 s' */
*res_str = '\0';
*length = strlen (*result_str);
return NO_ERROR;
}
/* Check currency */
if (char_tolower (*format_str) == 'c')
{
const char *money_symbol = intl_get_money_symbol (currency, codeset);
strcpy (res_str, money_symbol);
res_str += strlen (money_symbol);
format_str++;
}
if (*format_str == '\0')
{
init_format[*length] = format_end_char;
/* patch for format: '9999 s' */
*res_str = '\0';
*length = strlen (*result_str);
return NO_ERROR;
}
/* So far, format:'s','c' are settled */
if (*length > 4 && !strncasecmp (&init_format[*length - 4], "eeee", 4))
{
int cipher = 0;
num = num_string;
format = format_str;
if (*num == '0')
{
num++;
if (*num == '\0')
{
while (*format == '0' || *format == '9' || *format == digit_grouping_symbol)
{
format++;
}
if (*format == fraction_symbol)
{
*res_str = '0';
res_str++;
format++;
*res_str = fraction_symbol;
res_str++;
while (1)
{
if (*format == '0' || *format == '9')
{
*res_str = '0';
res_str++;
format++;
}
else if (char_tolower (*format) == 'e')
{
*res_str = '\0';
init_format[*length] = format_end_char;
make_scientific_notation (*result_str, cipher);
*length = strlen (*result_str);
return NO_ERROR;
}
else
{
return ER_FAILED;
}
}
}
else if (*format == 'e')
{
*res_str = '0';
res_str++;
*res_str = '\0';
init_format[*length] = format_end_char;
make_scientific_notation (*result_str, cipher);
*length = strlen (*result_str);
return NO_ERROR;
}
else
{
return ER_FAILED;
}
}
else if (*num == fraction_symbol)
{
num++;
while (1)
{
if (*num == '0')
{
cipher--;
num++;
}
else if (char_isdigit (*num))
{
cipher--;
break;
}
else if (char_tolower (*num) == 'e')
{
break;
}
else if (*num == '\0')
{
return ER_FAILED;
}
else
{
return ER_FAILED;
}
}
}
else
{
return ER_FAILED;
}
}
else
{
while (1)
{
if (char_isdigit (*num))
{
cipher++;
num++;
}
else if (*num == fraction_symbol || *num == '\0')
{
cipher--;
break;
}
else
{
return ER_FAILED;
}
}
}
while (*format == '0' || *format == '9' || *format == digit_grouping_symbol)
{
format++;
}
if (*format != fraction_symbol && char_tolower (*format) != 'e')
{
return ER_FAILED;
}
num = num_string;
res = res_str;
while (1)
{
if ('0' < *num && *num <= '9')
{
*res = *num;
res++;
num++;
break;
}
else
{
num++;
}
}
if (char_tolower (*format) == 'e')
{
*res = '\0';
if (*num == fraction_symbol)
{
num++;
if (char_isdigit (*num) && *num - '0' > 4)
{
roundoff (lang, *result_str, 1, &cipher, (char *) NULL);
}
}
else if (char_isdigit (*num))
{
if (char_isdigit (*num) && *num - '0' > 4)
{
roundoff (lang, *result_str, 1, &cipher, (char *) NULL);
}
}
else if (*num == '\0')
{
/* do nothing */
}
else
{
return ER_FAILED;
}
/* emm */
init_format[*length] = format_end_char;
make_scientific_notation (*result_str, cipher);
*length = strlen (*result_str);
return NO_ERROR;
}
else
{
*res = *format;
res++;
format++;
}
while (1)
{
if (*format == '0' || *format == '9')
{
if (*num == fraction_symbol)
{
num++;
*res = *num;
}
else if (*num == '\0')
{
while (*format == '0' || *format == '9')
{
*res = '0';
format++;
res++;
}
if (char_tolower (*format) != 'e')
{
return ER_FAILED;
}
*res = '\0';
init_format[*length] = format_end_char;
make_scientific_notation (*result_str, cipher);
*length = strlen (*result_str);
return NO_ERROR;
}
else
{
*res = *num;
}
format++;
res++;
num++;
}
else if (char_tolower (*format) == 'e')
{
if (strlen (format) > 4)
{
return ER_FAILED;
}
if (*num == '\0')
{
*res = '\0';
init_format[*length] = format_end_char;
make_scientific_notation (*result_str, cipher);
*length = strlen (*result_str);
return NO_ERROR;
}
else
{
*res = '\0';
/* patch */
if (*num == fraction_symbol && *(num + 1) - '0' > 4)
{
roundoff (lang, *result_str, 1, &cipher, (char *) NULL);
}
if (*num - '0' > 4)
{
roundoff (lang, *result_str, 1, &cipher, (char *) NULL);
}
/* emm */
init_format[*length] = format_end_char;
make_scientific_notation (*result_str, cipher);
*length = strlen (*result_str);
return NO_ERROR;
}
}
else
{
return ER_FAILED;
}
}
}
/* So far, format:scientific notation are settled */
/* Check leading zero */
if (*format_str == '0')
{
leadingzero = true;
}
num = num_string;
format = format_str;
/* Scan unitl '.' or '\0' of both num or format */
while (char_isdigit (*num))
{
num++;
}
while (*format == '0' || *format == '9' || *format == digit_grouping_symbol)
{
format++;
}
if (*format != fraction_symbol && *format != '\0')
{
return ER_FAILED;
}
/* '.' or '\0' is copied into middle or last position of res_string */
*(res_str + (format - format_str)) = *format;
res = res_str + (format - format_str);
/* num: .xxx format: .xxx */
if (format == format_str && num == num_string)
{
;
}
/* num: .xxx format: xxx.xxx */
else if (format != format_str && num == num_string)
{
if (leadingzero != 0)
{
while (format != format_str)
{
format--;
if (*format == '9' || *format == '0')
{
*(res_str + (format - format_str)) = '0';
}
else if (*format == digit_grouping_symbol)
{
*(res_str + (format - format_str)) = digit_grouping_symbol;
}
else
{
return ER_FAILED;
}
}
}
else
{
while (format != format_str)
{
format--;
*(res_str + (format - format_str)) = ' ';
}
}
}
/* num: xxx.xxx format: .xxx */
else if (format == format_str && num != num_string)
{
while (num != num_string)
{
num--;
if (*num != '0')
{
/* Make num be different from num_string */
num = num_string + 1;
break;
}
}
}
/* num: xxx.xxx format: xxx.xxx */
else
{
format--;
num--;
/* if size of format string is 1 */
if (format == format_str)
{
*res_str = *num;
}
else
{
while (format != format_str)
{
if (*format == digit_grouping_symbol)
{
*(res_str + (format - format_str)) = *format;
}
else if ((*format == '9' || *format == '0') && num != num_string)
{
*(res_str + (format - format_str)) = *num;
num--;
}
else
{
*(res_str + (format - format_str)) = *num;
if (leadingzero != 0)
{
while (format != format_str)
{
format--;
if (*format == '9' || *format == '0')
{
*(res_str + (format - format_str)) = '0';
}
else if (*format == digit_grouping_symbol)
{
*(res_str + (format - format_str)) = digit_grouping_symbol;
}
else
{
return ER_FAILED;
}
}
}
else
{
while (format != format_str)
{
format--;
*(res_str + (format - format_str)) = ' ';
}
}
break;
}
format--;
if (format == format_str && num == num_string)
{
*(res_str + (format - format_str)) = *num;
}
}
}
}
if (num != num_string)
{
int i;
i = strlen (init_format) - 1;
while (init_format != &init_format[i])
{
if (init_format[i] == fraction_symbol)
{
break;
}
else if (init_format[i] != '0' && init_format[i] != '9' && init_format[i] != 's' && init_format[i] != 'c'
&& init_format[i] != digit_grouping_symbol)
{
return ER_FAILED;
}
else
{
i--;
}
}
i = 0;
while (i < *length)
{
(*result_str)[i] = '#';
i++;
}
(*result_str)[*length] = '\0';
init_format[*length] = format_end_char;
return NO_ERROR;
}
/* So far, Left side of decimal point is settled */
while (char_isdigit (*num))
{
num++;
}
while (*format == '0' || *format == '9' || *format == digit_grouping_symbol)
{
format++;
}
if (*format != fraction_symbol && *format != '\0')
{
return ER_FAILED;
}
if (*format == fraction_symbol && *num == fraction_symbol)
{
res++;
format++;
num++;
while (*format != '\0')
{
if ((*format == '9' || *format == '0') && *num != '\0')
{
*res = *num;
num++;
res++;
}
else
{
while (*format != '\0')
{
if (*format == '9' || *format == '0')
{
*res = '0';
}
else
{
return ER_FAILED;
}
format++;
res++;
}
*res = '\0';
break;
}
format++;
}
*res = '\0';
if (*num != '\0')
{
/* rounding */
if (*num - '0' > 4)
{
if (roundoff (lang, *result_str, 0, (int *) NULL, format_str) != NO_ERROR)
{
return ER_FAILED;
}
}
}
}
else if (*format == fraction_symbol && *num == '\0')
{
res++;
format++;
while (*format != '\0')
{
if (*format == '9' || *format == '0')
{
*res = '0';
}
else
{
return ER_FAILED;
}
format++;
res++;
}
*res = '\0';
}
else if (*format == '\0' && *num == fraction_symbol)
{
if (*(num + 1) - '0' > 4)
{
if (roundoff (lang, *result_str, 0, (int *) NULL, format_str) != NO_ERROR)
{
return ER_FAILED;
}
}
/* rounding */
}
else if (*format == '\0' && *num == '\0')
{
/* Nothing */
}
else
{
return ER_FAILED;
}
init_format[*length] = format_end_char;
*length = strlen (*result_str);
return NO_ERROR;
}
/*
* make_scientific_notation () -
*/
static int
make_scientific_notation (char *src_string, int cipher)
{
int leng = strlen (src_string);
src_string[leng] = 'E';
leng++;
if (cipher >= 0)
{
src_string[leng] = '+';
}
else
{
src_string[leng] = '-';
cipher *= (-1);
}
leng++;
if (cipher > 99)
{
sprintf (&src_string[leng], "%d", cipher);
}
else
{
sprintf (&src_string[leng], "%02d", cipher);
}
return NO_ERROR;
}
/*
* roundoff () -
*
* Note : This function is localized in relation to fractional and digit
* grouping symbols.
*/
static int
roundoff (const INTL_LANG lang, char *src_string, int flag, int *cipher, char *format)
{
int loop_state = true;
int is_overflow = false;
char *res = &src_string[strlen (src_string)];
char *for_ptr = NULL;
int i;
const char fraction_symbol = lang_digit_fractional_symbol (lang);
const char digit_grouping_symbol = lang_digit_grouping_symbol (lang);
if (flag == 0)
{
for_ptr = &format[strlen (format)];
}
if (*src_string == '\0')
{
return ER_FAILED;
}
if (flag == 0 && *format == '\0')
{
return ER_FAILED;
}
res--;
if (flag == 0)
{
for_ptr--;
}
while (loop_state)
{
if ('0' <= *res && *res <= '9')
{
switch (*res - '0' + 1)
{
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
*res = *res + 1;
loop_state = false;
break;
case 10:
*res = '0';
if (res == src_string)
{
loop_state = false;
is_overflow = true;
}
else
{
res--;
if (flag == 0)
{
for_ptr--;
}
}
break;
}
}
else if (*res == fraction_symbol || *res == digit_grouping_symbol)
{
if (res == src_string)
{
loop_state = false;
is_overflow = true;
}
else
{
res--;
if (flag == 0)
{
for_ptr--;
}
}
}
else if (*res == ' ')
{
if (flag == 0 && *for_ptr == digit_grouping_symbol)
{
*res = digit_grouping_symbol;
res--;
for_ptr--;
}
*res = '1';
loop_state = false;
}
else
{ /* in case of sign, currency */
loop_state = false;
is_overflow = true;
}
}
if (is_overflow)
{
if (flag == 0)
{ /* if decimal format */
i = 0;
while (src_string[i] != '\0')
{
src_string[i] = '#';
i++;
}
}
else
{ /* if scientific format */
i = 0;
res = src_string;
while (!('0' <= *res && *res <= '9'))
{
res++;
}
i = 0;
if (res[i] != '\0')
{
res[i++] = '1';
if (res[i] != '\0')
{
res[i++] = fraction_symbol;
while (res[i] != '\0')
{
res[i++] = '0';
}
}
}
(*cipher)++;
}
}
return NO_ERROR;
}
/*
* scientific_to_decimal_string () -
*
* Note : This function is localized in relation to fractional and digit
* grouping symbols.
*/
static int
scientific_to_decimal_string (const INTL_LANG lang, char *src_string, char **scientific_str)
{
#define PLUS 1
#define MINUS 0
int src_len = strlen (src_string);
int sign = PLUS, exponent_sign = PLUS, cipher = 0;
char *ptr = src_string;
char *result_str;
int i;
int tmp_digit;
const char fraction_symbol = lang_digit_fractional_symbol (lang);
while (char_isspace (*ptr))
{
ptr++;
}
if (*ptr == '+')
{
sign = PLUS;
ptr++;
}
else if (*ptr == '-')
{
sign = MINUS;
ptr++;
}
tmp_digit = 0;
while (char_isdigit (*ptr))
{
tmp_digit = tmp_digit * 10 + (*ptr - '0');
ptr++;
}
if (tmp_digit >= 10)
{
return ER_FAILED;
}
if (*ptr != fraction_symbol)
{
return ER_FAILED;
}
ptr++;
while (char_isdigit (*ptr))
{
ptr++;
}
if (*ptr == 'e' || *ptr == 'E')
{
ptr++;
}
else
{
return ER_FAILED;
}
if (*ptr == '+')
{
exponent_sign = PLUS;
}
else if (*ptr == '-')
{
exponent_sign = MINUS;
}
else
{
return ER_FAILED;
}
ptr++;
for (; char_isdigit (*ptr); ptr++)
{
cipher = cipher * 10 + (*ptr - '0');
}
/* So far, one pass */
/* Fron now, two pass */
while (char_isspace (*ptr))
{
ptr++;
}
if (*ptr != '\0')
{
return ER_FAILED;
}
ptr = src_string;
while (char_isspace (*ptr))
{
ptr++;
}
*scientific_str = (char *) db_private_alloc (NULL, src_len + cipher);
if (*scientific_str == NULL)
{
return ER_FAILED;
}
/* patch for MemoryTrash */
for (i = 0; i < src_len + cipher; i++)
{
(*scientific_str)[i] = '\0';
}
result_str = *scientific_str;
if (sign == MINUS)
{
*result_str = '-';
result_str++;
ptr++;
}
if (exponent_sign == PLUS)
{
i = 0;
while (char_isdigit (*ptr))
{
*result_str = *ptr;
(result_str)++;
ptr++;
}
*(result_str + cipher) = fraction_symbol;
ptr++;
while (i < cipher || char_isdigit (*ptr))
{
if (*result_str == fraction_symbol)
{
(result_str)++;
continue;
}
else if (char_isdigit (*ptr))
{
*result_str = *ptr;
ptr++;
}
else
{
*result_str = '0';
}
(result_str)++;
i++;
}
}
else
{
*result_str = '0';
result_str++;
*result_str = fraction_symbol;
result_str++;
i = 0;
while (i < cipher - 1)
{
*result_str = '0';
result_str++;
i++;
}
while (char_isdigit (*ptr) || *ptr == fraction_symbol)
{
if (*ptr == fraction_symbol)
{
ptr++;
}
*result_str = *ptr;
(result_str)++;
ptr++;
}
}
*result_str = '\0';
return NO_ERROR;
}
/*
* to_number_next_state () -
*
* Note : This function is localized in relation to fractional and digit
* grouping symbols.
*/
static int
to_number_next_state (const int previous_state, const int input_char, const INTL_LANG number_lang_id)
{
int state_table[7][7] = { {4, 5, 2, 3, -1, 6, -1},
{4, 5, -1, 3, -1, 6, -1},
{4, 5, -1, -1, -1, 6, -1},
{4, 4, -1, -1, 4, 6, 7},
{5, 5, -1, -1, 5, 6, 7},
{6, 6, -1, -1, 6, -1, 7},
{0, 0, 0, 0, 0, 0, 0}
};
int state;
const char fraction_symbol = lang_digit_fractional_symbol (number_lang_id);
const char digit_grouping_symbol = lang_digit_grouping_symbol (number_lang_id);
if (previous_state == -1)
{
return -1;
}
switch (char_tolower (input_char))
{
case '0':
state = state_table[previous_state - 1][0];
break;
case '9':
state = state_table[previous_state - 1][1];
break;
case 's':
state = state_table[previous_state - 1][2];
break;
case 'c':
state = state_table[previous_state - 1][3];
break;
default:
if (input_char == digit_grouping_symbol)
{
state = state_table[previous_state - 1][4];
break;
}
else if (input_char == fraction_symbol)
{
state = state_table[previous_state - 1][5];
break;
}
state = state_table[previous_state - 1][6];
break;
}
return state;
}
/*
* to_number_next_state () -
* Note: assume precision and scale are correct
* This function is localized in relation to fractional and digit
* grouping symbols.
*/
static int
make_number (char *src, char *last_src, INTL_CODESET codeset, char *token, int *token_length, DB_VALUE * r,
const int precision, const int scale, const INTL_LANG number_lang_id)
{
int error_status = NO_ERROR;
int state = 1;
int i, j, k;
char result_str[DB_MAX_NUMERIC_PRECISION + 2];
char *res_ptr;
const char fraction_symbol = lang_digit_fractional_symbol (number_lang_id);
const char digit_grouping_symbol = lang_digit_grouping_symbol (number_lang_id);
result_str[0] = '\0';
result_str[DB_MAX_NUMERIC_PRECISION] = '\0';
result_str[DB_MAX_NUMERIC_PRECISION + 1] = '\0';
*token_length = 0;
while (state != 7 && src < last_src)
{
switch (to_number_next_state (state, *token, number_lang_id))
{
case 1: /* Not reachable state */
break;
case 2:
if (*src == '-')
{
strncat (result_str, src, 1);
src++;
(*token_length)++;
token++;
state = 2;
}
else if (*src == '+')
{
src++;
(*token_length)++;
token++;
state = 2;
}
else
{
return ER_QSTR_MISMATCHING_ARGUMENTS;
}
break;
case 3:
{
DB_CURRENCY currency = DB_CURRENCY_NULL;
int symbol_size = 0;
if (intl_is_currency_symbol
(src, ¤cy, &symbol_size,
(CURRENCY_CHECK_MODE) (CURRENCY_CHECK_MODE_CONSOLE | CURRENCY_CHECK_MODE_UTF8 |
CURRENCY_CHECK_MODE_ISO88591)))
{
src += symbol_size;
(*token_length) += symbol_size;
token++;
}
}
state = 3;
break;
case 4:
case 5:
if (*src == '-')
{
strncat (result_str, src, 1);
src++;
(*token_length)++;
}
j = 0;
k = 0;
while (token[j] == '0' || token[j] == '9' || token[j] == digit_grouping_symbol)
{
j++;
}
while ((&src[k] < last_src) && (char_isdigit (src[k]) || src[k] == digit_grouping_symbol))
{
k++;
}
i = j;
if (k > DB_MAX_NUMERIC_PRECISION)
{
return ER_IT_DATA_OVERFLOW;
}
if (k > 0)
{
k--;
}
j--;
while (k > 0 && j > 0)
{
if (token[j] == digit_grouping_symbol && src[k] != digit_grouping_symbol)
{
return ER_QSTR_MISMATCHING_ARGUMENTS;
}
k--;
j--;
}
if (k != 0)
{ /* format = '99' && src = '4444' */
return ER_QSTR_MISMATCHING_ARGUMENTS;
}
/* patch select to_number('30','9,9') from dual; */
if ((src[k] == digit_grouping_symbol && token[j] != digit_grouping_symbol)
|| (token[j] == digit_grouping_symbol && src[k] != digit_grouping_symbol))
{
return ER_QSTR_MISMATCHING_ARGUMENTS;
}
if (j > 0)
{
j = 0;
}
while (src < last_src && (char_isdigit (*src) || *src == digit_grouping_symbol))
{
if (*src != digit_grouping_symbol)
{
strncat (result_str, src, 1);
}
(*token_length)++;
src++;
}
token = token + i;
state = 4;
break;
case 6:
token++;
if (*src == fraction_symbol)
{
strncat (result_str, src, 1);
src++;
(*token_length)++;
while (src < last_src && char_isdigit (*src))
{
if (*token == '0' || *token == '9')
{
strncat (result_str, src, 1);
token++;
src++;
(*token_length)++;
}
else
{
return ER_QSTR_MISMATCHING_ARGUMENTS;
}
}
}
while (*token == '0' || *token == '9')
{
token++;
}
state = 6;
break;
case 7:
state = 7;
break;
case -1:
return ER_QSTR_MISMATCHING_ARGUMENTS;
} /* switch */
} /* while */
/* For Scientific notation */
if (strlen (token) >= 4 && strncasecmp (token, "eeee", 4) == 0 && char_tolower (*src) == 'e'
&& (*(src + 1) == '+' || *(src + 1) == '-'))
{
strncat (result_str, src, 2);
src += 2;
(*token_length) += 2;
while (src < last_src && char_isdigit (*src))
{
strncat (result_str, src, 1);
src += 1;
(*token_length) += 1;
}
if (scientific_to_decimal_string (number_lang_id, result_str, &res_ptr) != NO_ERROR)
{
return ER_QSTR_MISMATCHING_ARGUMENTS;
/* This line needs to be modified to reflect appropriate error */
}
/*
* modify result_str to contain correct string value with respect to
* the given precision and scale.
*/
strncpy (result_str, res_ptr, sizeof (result_str) - 1);
db_private_free_and_init (NULL, res_ptr);
if (number_lang_id != INTL_LANG_ENGLISH)
{
convert_locale_number (result_str, strlen (result_str), number_lang_id, INTL_LANG_ENGLISH);
}
error_status = adjust_precision (result_str, precision, scale);
if (error_status == DOMAIN_OVERFLOW)
{
return ER_IT_DATA_OVERFLOW;
}
if (error_status != NO_ERROR
|| numeric_coerce_string_to_num (result_str, strlen (result_str), codeset, r) != NO_ERROR)
{
/* patch for to_number('-1.23e+03','9.99eeee') */
return ER_QSTR_MISMATCHING_ARGUMENTS;
}
/* old comment db_make_numeric(r,num,precision,scale); */
}
else
{
if (number_lang_id != INTL_LANG_ENGLISH)
{
convert_locale_number (result_str, strlen (result_str), number_lang_id, INTL_LANG_ENGLISH);
}
/*
* modify result_str to contain correct string value with respect to
* the given precision and scale.
*/
error_status = adjust_precision (result_str, precision, scale);
if (error_status == DOMAIN_OVERFLOW)
{
return ER_IT_DATA_OVERFLOW;
}
if (error_status != NO_ERROR
|| numeric_coerce_string_to_num (result_str, strlen (result_str), codeset, r) != NO_ERROR)
{
return ER_QSTR_MISMATCHING_ARGUMENTS;
}
}
return error_status;
}
/*
* get_number_token () -
*
* Note : This function is localized in relation to fractional and digit
* grouping symbols.
*/
static int
get_number_token (const INTL_LANG lang, char *fsp, int *length, char *last_position, char **next_fsp,
INTL_CODESET codeset)
{
const char fraction_symbol = lang_digit_fractional_symbol (lang);
const char digit_grouping_symbol = lang_digit_grouping_symbol (lang);
char c;
*length = 0;
if (fsp == last_position)
{
return N_END;
}
c = char_tolower (fsp[*length]);
switch (c)
{
case 'c':
case 's':
if (fsp[*length + 1] == digit_grouping_symbol)
{
return N_INVALID;
}
if ((char_tolower (fsp[*length + 1]) == 'c' || char_tolower (fsp[*length + 1]) == 's')
&& fsp[*length + 2] == digit_grouping_symbol)
{
return N_INVALID;
}
[[fallthrough]];
case '9':
case '0':
while (fsp[*length] == '9' || fsp[*length] == '0' || char_tolower (fsp[*length]) == 's'
|| char_tolower (fsp[*length]) == 'c' || fsp[*length] == fraction_symbol
|| fsp[*length] == digit_grouping_symbol)
{
*length += 1;
}
*next_fsp = &fsp[*length];
if (strlen (*next_fsp) >= 4 && !strncasecmp (*next_fsp, "eeee", 4))
{
*length += 4;
*next_fsp = &fsp[*length];
}
return N_FORMAT;
case ' ':
case '\t':
case '\n':
while (last_position != &fsp[*length] && (fsp[*length] == ' ' || fsp[*length] == '\t' || fsp[*length] == '\n'))
{
*length += 1;
}
*next_fsp = &fsp[*length];
return N_SPACE;
case (char) 0xa1:
if (codeset == INTL_CODESET_KSC5601_EUC && (&fsp[*length + 1]) < last_position && fsp[*length + 1] == (char) 0xa1)
{
while ((&fsp[*length + 1]) < last_position && fsp[*length] == (char) 0xa1 && fsp[*length + 1] == (char) 0xa1)
{
*length += 2;
}
*next_fsp = &fsp[*length];
return N_SPACE;
}
return N_INVALID;
case '"':
*length += 1;
while (fsp[*length] != '"')
{
if (&fsp[*length] == last_position)
{
return N_INVALID;
}
*length += 1;
}
*length += 1;
*next_fsp = &fsp[*length];
return N_TEXT;
default:
if (c == fraction_symbol)
{
while (fsp[*length] == '9' || fsp[*length] == '0' || char_tolower (fsp[*length]) == 's'
|| char_tolower (fsp[*length]) == 'c' || fsp[*length] == fraction_symbol
|| fsp[*length] == digit_grouping_symbol)
{
*length += 1;
}
*next_fsp = &fsp[*length];
if (strlen (*next_fsp) >= 4 && !strncasecmp (*next_fsp, "eeee", 4))
{
*length += 4;
*next_fsp = &fsp[*length];
}
return N_FORMAT;
}
return N_INVALID;
}
}
/*
* get_number_format () -
*/
static TIMESTAMP_FORMAT
get_next_format (const char *sp, const INTL_CODESET codeset, DB_TYPE str_type, int *format_length,
const char **next_pos)
{
/* sp : start position */
*format_length = 0;
switch (char_tolower (*sp))
{
case 'y':
if (str_type == DB_TYPE_TIME)
{
return DT_INVALID;
}
if (strncasecmp (sp, "yyyy", 4) == 0)
{
*format_length += 4;
*next_pos = sp + *format_length;
return DT_YYYY;
}
else if (strncasecmp (sp, "yy", 2) == 0)
{
*format_length += 2;
*next_pos = sp + *format_length;
return DT_YY;
}
else
{
return DT_INVALID;
}
case 'd':
if (str_type == DB_TYPE_TIME)
{
return DT_INVALID;
}
if (strncasecmp (sp, "dd", 2) == 0)
{
*format_length += 2;
*next_pos = sp + *format_length;
return DT_DD;
}
else if (strncasecmp (sp, "dy", 2) == 0)
{
*format_length += 2;
*next_pos = sp + *format_length;
return DT_DY;
}
else if (strncasecmp (sp, "day", 3) == 0)
{
*format_length += 3;
*next_pos = sp + *format_length;
return DT_DAY;
}
else
{
*format_length += 1;
*next_pos = sp + *format_length;
return DT_D;
}
case 'c':
if (str_type == DB_TYPE_TIME)
{
return DT_INVALID;
}
if (strncasecmp (sp, "cc", 2) == 0)
{
*format_length += 2;
*next_pos = sp + *format_length;
return DT_CC;
}
else
{
return DT_INVALID;
}
case 'q':
if (str_type == DB_TYPE_TIME)
{
return DT_INVALID;
}
*format_length += 1;
*next_pos = sp + *format_length;
return DT_Q;
case 'm':
if (str_type != DB_TYPE_TIME && strncasecmp (sp, "mm", 2) == 0)
{
*format_length += 2;
*next_pos = sp + *format_length;
return DT_MM;
}
else if (str_type != DB_TYPE_TIME && strncasecmp (sp, "month", 5) == 0)
{
*format_length += 5;
*next_pos = sp + *format_length;
return DT_MONTH;
}
else if (str_type != DB_TYPE_TIME && strncasecmp (sp, "mon", 3) == 0)
{
*format_length += 3;
*next_pos = sp + *format_length;
return DT_MON;
}
else if (str_type != DB_TYPE_DATE && strncasecmp (sp, "mi", 2) == 0)
{
*format_length += 2;
*next_pos = sp + *format_length;
return DT_MI;
}
else
{
return DT_INVALID;
}
case 'a':
if (str_type == DB_TYPE_DATE)
{
return DT_INVALID;
}
if (strncasecmp (sp, "am", 2) == 0)
{
*format_length += 2;
*next_pos = sp + *format_length;
return DT_AM;
}
else if (strncasecmp (sp, "a.m.", 4) == 0)
{
*format_length += 4;
*next_pos = sp + *format_length;
return DT_A_M;
}
else
{
return DT_INVALID;
}
case 'p':
if (str_type == DB_TYPE_DATE)
{
return DT_INVALID;
}
if (strncasecmp (sp, "pm", 2) == 0)
{
*format_length += 2;
*next_pos = sp + *format_length;
return DT_PM;
}
else if (strncasecmp (sp, "p.m.", 4) == 0)
{
*format_length += 4;
*next_pos = sp + *format_length;
return DT_P_M;
}
else
{
return DT_INVALID;
}
case 'h':
if (str_type == DB_TYPE_DATE)
{
return DT_INVALID;
}
if (strncasecmp (sp, "hh24", 4) == 0)
{
*format_length += 4;
*next_pos = sp + *format_length;
return DT_HH24;
}
else if (strncasecmp (sp, "hh12", 4) == 0)
{
*format_length += 4;
*next_pos = sp + *format_length;
return DT_HH12;
}
else if (strncasecmp (sp, "hh", 2) == 0)
{
*format_length += 2;
*next_pos = sp + *format_length;
return DT_HH;
}
else if (strncasecmp (sp, "h", 1) == 0)
{
*format_length += 1;
*next_pos = sp + *format_length;
return DT_H;
}
else
{
return DT_INVALID;
}
case 's':
if (str_type == DB_TYPE_DATE)
{
return DT_INVALID;
}
if (strncasecmp (sp, "ss", 2) == 0)
{
*format_length += 2;
*next_pos = sp + *format_length;
return DT_SS;
}
else
{
return DT_INVALID;
}
case 'f':
if ((str_type == DB_TYPE_DATETIME || str_type == DB_TYPE_DATETIMETZ || str_type == DB_TYPE_DATETIMELTZ)
&& strncasecmp (sp, "ff", 2) == 0)
{
*format_length += 2;
*next_pos = sp + *format_length;
return DT_MS;
}
else
{
return DT_INVALID;
}
case '"':
*format_length += 1;
while (sp[*format_length] != '"')
{
int char_size;
const unsigned char *ptr = (const unsigned char *) sp + (*format_length);
if (sp[*format_length] == '\0')
{
return DT_INVALID;
}
INTL_NEXT_CHAR (ptr, ptr, codeset, &char_size);
*format_length += char_size;
}
*format_length += 1;
*next_pos = &sp[*format_length];
return DT_TEXT;
case '-':
case '/':
/* this is not a numeric format: it is not necessary to localize point and comma symbols here */
case ',':
case '.':
case ';':
case ':':
case ' ':
case '\t':
case '\n':
*format_length += 1;
*next_pos = sp + *format_length;
return DT_PUNCTUATION;
case 't':
if (str_type == DB_TYPE_DATE)
{
return DT_INVALID;
}
if (strncasecmp (sp, "tzr", 3) == 0)
{
*format_length += 3;
*next_pos = sp + *format_length;
return DT_TZR;
}
else if (strncasecmp (sp, "tzd", 3) == 0)
{
*format_length += 3;
*next_pos = sp + *format_length;
return DT_TZD;
}
else if (strncasecmp (sp, "tzh", 3) == 0)
{
*format_length += 3;
*next_pos = sp + *format_length;
return DT_TZH;
}
else if (strncasecmp (sp, "tzm", 3) == 0)
{
*format_length += 3;
*next_pos = sp + *format_length;
return DT_TZM;
}
else
{
return DT_INVALID;
}
default:
return DT_INVALID;
}
}
/*
* get_cur_year () -
*/
static int
get_cur_year (void)
{
time_t tloc;
struct tm *tm, tm_val;
if (time (&tloc) == -1)
{
return -1;
}
tm = localtime_r (&tloc, &tm_val);
if (tm == NULL)
{
return -1;
}
return tm->tm_year + 1900;
}
/*
* get_cur_month () -
*/
static int
get_cur_month (void)
{
time_t tloc;
struct tm *tm, tm_val;
if (time (&tloc) == -1)
{
return -1;
}
tm = localtime_r (&tloc, &tm_val);
if (tm == NULL)
{
return -1;
}
return tm->tm_mon + 1;
}
/*
* get_day () -
*/
int
get_day (int month, int day, int year)
{
return day_of_week (julian_encode (month, day, year));
}
/*
* db_format () -
*
* Note : This function is localized in relation to fractional and digit
* grouping symbols.
*/
int
db_format (const DB_VALUE * value, const DB_VALUE * decimals, const DB_VALUE * number_lang, DB_VALUE * result,
const TP_DOMAIN * domain)
{
DB_TYPE arg1_type, arg2_type;
int error = NO_ERROR;
int ndec = 0, i, j;
const char *integer_format_max = "99,999,999,999,999,999,999,999,999,999,999,999,999";
char format[128];
DB_VALUE format_val, formatted_val, numeric_val, trimmed_val;
const DB_VALUE *num_dbval_p = NULL;
char fraction_symbol;
char digit_grouping_symbol;
bool dummy;
INTL_LANG number_lang_id;
assert (value != NULL);
assert (decimals != NULL);
assert (number_lang != NULL);
arg1_type = DB_VALUE_DOMAIN_TYPE (value);
arg2_type = DB_VALUE_DOMAIN_TYPE (decimals);
if (arg1_type == DB_TYPE_NULL || DB_IS_NULL (value) || arg2_type == DB_TYPE_NULL || DB_IS_NULL (decimals))
{
db_make_null (result);
return NO_ERROR;
}
assert (DB_VALUE_TYPE (number_lang) == DB_TYPE_INTEGER);
number_lang_id = lang_get_lang_id_from_flag (db_get_int (number_lang), &dummy, &dummy);
fraction_symbol = lang_digit_fractional_symbol (number_lang_id);
digit_grouping_symbol = lang_digit_grouping_symbol (number_lang_id);
db_make_null (&formatted_val);
db_make_null (&trimmed_val);
if (arg2_type == DB_TYPE_INTEGER)
{
ndec = db_get_int (decimals);
}
else if (arg2_type == DB_TYPE_SHORT)
{
ndec = db_get_short (decimals);
}
else if (arg2_type == DB_TYPE_BIGINT)
{
DB_BIGINT bi = db_get_bigint (decimals);
if (bi > INT_MAX || bi < 0)
{
goto invalid_argument_error;
}
ndec = (int) bi;
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_FAILED;
}
if (ndec < 0)
{
goto invalid_argument_error;
}
/* 30 is the decimal limit for formating floating points with this function, in mysql */
if (ndec > 30)
{
ndec = 30;
}
switch (arg1_type)
{
case DB_TYPE_VARCHAR:
case DB_TYPE_CHAR:
{
char *c;
int len, dot = 0;
/* Trim first because the input string can be given like below: - ' 1.1 ', '1.1 ', ' 1.1' */
error = db_string_trim (BOTH, NULL, value, &trimmed_val);
if (error != NO_ERROR)
{
return error;
}
c = CONST_CAST (char *, db_get_string (&trimmed_val));
if (c == NULL)
{
goto invalid_argument_error;
}
len = strlen (c);
for (i = 0; i < len; i++)
{
if (c[i] == fraction_symbol)
{
dot++;
continue;
}
if (!char_isdigit (c[i]))
{
goto invalid_argument_error;
}
}
if (dot > 1)
{
goto invalid_argument_error;
}
if (number_lang_id != INTL_LANG_ENGLISH)
{
convert_locale_number (c, len, number_lang_id, INTL_LANG_ENGLISH);
}
error = numeric_coerce_string_to_num (c, len, db_get_string_codeset (&trimmed_val), &numeric_val);
if (error != NO_ERROR)
{
pr_clear_value (&trimmed_val);
return error;
}
num_dbval_p = &numeric_val;
pr_clear_value (&trimmed_val);
}
break;
case DB_TYPE_MONETARY:
{
double d = db_value_get_monetary_amount_as_double (value);
db_make_double (&numeric_val, d);
num_dbval_p = &numeric_val;
}
break;
case DB_TYPE_SHORT:
case DB_TYPE_INTEGER:
case DB_TYPE_BIGINT:
case DB_TYPE_FLOAT:
case DB_TYPE_DOUBLE:
case DB_TYPE_NUMERIC:
num_dbval_p = value;
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_FAILED;
}
/* Make format string. */
i = snprintf (format, sizeof (format) - 1, "%s", integer_format_max);
if (number_lang_id != INTL_LANG_ENGLISH)
{
convert_locale_number (format, strlen (format), INTL_LANG_ENGLISH, number_lang_id);
}
if (ndec > 0)
{
format[i++] = fraction_symbol;
for (j = 0; j < ndec; j++)
{
format[i++] = '9';
}
format[i] = '\0';
}
db_make_string (&format_val, format);
error = number_to_char (num_dbval_p, &format_val, number_lang, &formatted_val, domain);
if (error == NO_ERROR)
{
/* number_to_char function returns a string with leading empty characters. So, we need to remove them. */
error = db_string_trim (LEADING, NULL, &formatted_val, result);
pr_clear_value (&formatted_val);
}
return error;
invalid_argument_error:
if (!DB_IS_NULL (&trimmed_val))
{
pr_clear_value (&trimmed_val);
}
if (!DB_IS_NULL (&formatted_val))
{
pr_clear_value (&formatted_val);
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_FAILED;
}
/*
* db_string_reverse () - reverse the source DB_VALUE string
*
* return:
* src_str(in): source DB_VALUE string
* result_str(in/out): result DB_VALUE string
*/
int
db_string_reverse (const DB_VALUE * src_str, DB_VALUE * result_str)
{
int error_status = NO_ERROR;
DB_TYPE str_type;
char *res = NULL;
/*
* Assert that DB_VALUE structures have been allocated.
*/
assert (src_str != (DB_VALUE *) NULL);
assert (result_str != (DB_VALUE *) NULL);
/*
* Categorize the two input parameters and check for errors.
* Verify that the parameters are both character strings.
*/
str_type = DB_VALUE_DOMAIN_TYPE (src_str);
if (DB_IS_NULL (src_str))
{
db_make_null (result_str);
}
else if (!QSTR_IS_ANY_CHAR (str_type))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
}
/*
* If the input parameters have been properly validated, then
* we are ready to operate.
*/
else
{
res = (char *) db_private_alloc (NULL, db_get_string_size (src_str) + 1);
if (res == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
}
if (error_status == NO_ERROR)
{
memset (res, 0, db_get_string_size (src_str) + 1);
intl_reverse_string (DB_GET_UCHAR (src_str),
REINTERPRET_CAST (unsigned char *, res), db_get_string_length (src_str),
db_get_string_size (src_str), db_get_string_codeset (src_str));
assert (QSTR_IS_CHAR (str_type));
db_make_varchar (result_str, DB_GET_STRING_PRECISION (src_str), res, db_get_string_size (src_str),
db_get_string_codeset (src_str), db_get_string_collation (src_str));
result_str->need_clear = true;
}
}
return error_status;
}
// Monthly Days and Monthly Cumulative Days Table
// The 0th position is unused and left blank for array access efficiency.
static const int days_per_month[2][13] = {
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
static const int cumulative_days_per_month[2][13] = {
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
};
/*
* add_and_normalize_date_time ()
*
* Arguments: date & time values to modify,
* date & time amounts to add
*
* Returns: NO_ERROR/ER_FAILED
*
* Errors:
*
* Note:
* transforms all values in a correct interval (h: 0..23, m: 0..59, etc)
*/
int
add_and_normalize_date_time (int *year, int *month, int *day, int *hour, int *minute, int *second, int *millisecond,
const DB_BIGINT * composite_values)
{
int days_idx;
DB_BIGINT _y, _m, _d, _h, _mi, _s, _ms;
/* just years and/or months case */
if (composite_values[COMPOSITE_MONTH] > 0 || composite_values[COMPOSITE_YEAR] > 0)
{
assert (composite_values[COMPOSITE_DAY] == 0 && composite_values[COMPOSITE_HOUR] == 0 &&
composite_values[COMPOSITE_MINUTE] == 0 && composite_values[COMPOSITE_SECOND] == 0 &&
composite_values[COMPOSITE_MILLISECOND] == 0);
_y = *year + composite_values[COMPOSITE_YEAR];
_m = *month + composite_values[COMPOSITE_MONTH];
if (_m % 12 == 0)
{
_y += (_m - 12) / 12;
_m = 12;
}
else
{
_y += _m / 12;
_m %= 12;
}
days_idx = LEAP (_y) ? 1 : 0;
if (*day > days_per_month[days_idx][_m])
{
*day = days_per_month[days_idx][_m];
}
if (_y > 9999)
{
return ER_FAILED;
}
*year = (int) _y;
*month = (int) _m;
return NO_ERROR;
}
assert (composite_values[COMPOSITE_MONTH] == 0 && composite_values[COMPOSITE_YEAR] == 0);
_y = *year;
_m = *month;
_d = *day + composite_values[COMPOSITE_DAY];
_h = *hour + composite_values[COMPOSITE_HOUR];
_mi = *minute + composite_values[COMPOSITE_MINUTE];
_s = *second + composite_values[COMPOSITE_SECOND];
_ms = *millisecond + composite_values[COMPOSITE_MILLISECOND];
/* time */
_s += _ms / 1000;
_ms %= 1000;
_mi += _s / 60;
_s %= 60;
_h += _mi / 60;
_mi %= 60;
_d += _h / 24;
_h %= 24;
days_idx = LEAP (_y) ? 1 : 0;
if (_d > days_per_month[days_idx][_m])
{
/* rewind to 1st january */
_d += cumulative_days_per_month[days_idx][_m - 1];
/* days for years */
int maxdays = cumulative_days_per_month[days_idx][12];
while (_d >= maxdays)
{
_d -= maxdays;
_y++;
if (_y > 9999)
{
goto set_and_return;
}
maxdays = LEAP (_y) ? 366 : 365;
}
/* days within a year */
days_idx = LEAP (_y) ? 1 : 0;
for (_m = (_d < cumulative_days_per_month[days_idx][7]) ? 1 : 7; /* blank */ ; _m++)
{
if (_d <= cumulative_days_per_month[days_idx][_m])
{
_d -= cumulative_days_per_month[days_idx][_m - 1];
break;
}
}
}
assert (_m > 0 && _m <= 12);
if (_d == 0)
{
assert (_m == 1);
_y--;
_m = 12;
_d = 31;
}
set_and_return:
if (_y > 9999)
{
return ER_FAILED;
}
*year = (int) _y;
*month = (int) _m;
*day = (int) _d;
*hour = (int) _h;
*minute = (int) _mi;
*second = (int) _s;
*millisecond = (int) _ms;
return NO_ERROR;
}
/*
* sub_and_normalize_date_time ()
*
* Arguments: date & time values to modify,
* date & time amounts to subtract
*
* Returns: NO_ERROR/ER_FAILED
*
* Errors:
*
* Note:
* transforms all values in a correct interval (h: 0..23, m: 0..59, etc)
*/
int
sub_and_normalize_date_time (int *year, int *month, int *day, int *hour, int *minute, int *second, int *millisecond,
const DB_BIGINT * composite_values)
{
int days_idx;
DB_BIGINT _y, _m, _d, _h, _mi, _s, _ms;
/* just years and/or months case */
if (composite_values[COMPOSITE_MONTH] > 0 || composite_values[COMPOSITE_YEAR] > 0)
{
assert (composite_values[COMPOSITE_DAY] == 0 && composite_values[COMPOSITE_HOUR] == 0 &&
composite_values[COMPOSITE_MINUTE] == 0 && composite_values[COMPOSITE_SECOND] == 0 &&
composite_values[COMPOSITE_MILLISECOND] == 0);
_y = *year - composite_values[COMPOSITE_YEAR];
_m = *month - composite_values[COMPOSITE_MONTH];
if (_m <= 0)
{
_y += (_m / 12);
_m %= 12;
if (_m <= 0)
{
_m += 12;
_y--;
}
}
if (_y < 0)
{
return ER_FAILED;
}
days_idx = LEAP (_y) ? 1 : 0;
if (*day > days_per_month[days_idx][_m])
{
*day = days_per_month[days_idx][_m];
}
*year = (int) _y;
*month = (int) _m;
return NO_ERROR;
}
assert (composite_values[COMPOSITE_MONTH] == 0 && composite_values[COMPOSITE_YEAR] == 0);
_y = *year;
_m = *month;
_d = *day - composite_values[COMPOSITE_DAY];
_h = *hour - composite_values[COMPOSITE_HOUR];
_mi = *minute - composite_values[COMPOSITE_MINUTE];
_s = *second - composite_values[COMPOSITE_SECOND];
_ms = *millisecond - composite_values[COMPOSITE_MILLISECOND];
/* time */
_s += _ms / 1000;
_ms %= 1000;
if (_ms < 0)
{
_ms += 1000;
_s--;
}
_mi += _s / 60;
_s %= 60;
if (_s < 0)
{
_s += 60;
_mi--;
}
_h += _mi / 60;
_mi %= 60;
if (_mi < 0)
{
_mi += 60;
_h--;
}
_d += _h / 24;
_h %= 24;
if (_h < 0)
{
_h += 24;
_d--;
}
days_idx = LEAP (_y) ? 1 : 0;
if (_d > days_per_month[days_idx][_m] || _d < 0)
{
/* rewind to 1st january */
_d += cumulative_days_per_month[days_idx][_m - 1];
/* days for years */
while (_d < 0)
{
_y--;
if (_y < 0)
{
goto set_and_return;
}
_d += (LEAP (_y) ? 366 : 365);
}
/* days within a year */
days_idx = LEAP (_y) ? 1 : 0;
for (_m = (_d < cumulative_days_per_month[days_idx][7]) ? 1 : 7; /* blank */ ; _m++)
{
if (_d <= cumulative_days_per_month[days_idx][_m])
{
_d -= cumulative_days_per_month[days_idx][_m - 1];
break;
}
}
}
assert (_m > 0 && _m <= 12);
if (_d == 0)
{
_m--;
if (_m == 0)
{
_y--;
_m = 12;
days_idx = LEAP (_y) ? 1 : 0;
}
_d = days_per_month[days_idx][_m];
}
set_and_return:
if (_y < 0)
{
return ER_FAILED;
}
*year = (int) _y;
*month = (int) _m;
*day = (int) _d;
*hour = (int) _h;
*minute = (int) _mi;
*second = (int) _s;
*millisecond = (int) _ms;
return NO_ERROR;
}
/* TODO : refactor with db_date_add_sub_interval_expr
* db_date_add_sub_interval_days ()
*
* Arguments:
* date: starting date
* db_days: number of days to add
*
* Returns: int
*
* Errors:
*
* Note:
* Returns date + an interval of db_days days.
*/
static int
db_date_add_sub_interval_days (DB_VALUE * result, const DB_VALUE * date, const DB_VALUE * db_days, bool is_add)
{
int error_status = NO_ERROR;
int days;
DB_DATETIME db_datetime, *dt_p = NULL;
DB_DATETIMETZ dt_tz;
DB_TIME db_time;
DB_DATE db_date, *d_p;
DB_TIMESTAMP db_timestamp, *ts_p = NULL;
int is_dt = -1, is_d = -1, is_t = -1, is_timest = -1, is_timezone = -1, is_local_timezone = -1;
DB_TYPE res_type;
char res_s[64];
int y, m, d, h, mi, s, ms;
int ret;
char *res_final;
TZ_ID tz_id;
DB_BIGINT composite_values[COMPOSITE_MAX] = { 0, 0, 0, 0, 0, 0, 0 };
res_type = DB_VALUE_DOMAIN_TYPE (date);
if (res_type == DB_TYPE_NULL || DB_IS_NULL (date))
{
db_make_null (result);
return NO_ERROR;
}
if (DB_VALUE_DOMAIN_TYPE (db_days) == DB_TYPE_NULL || DB_IS_NULL (db_days))
{
db_make_null (result);
return NO_ERROR;
}
bool is_null_on_error = prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS);
/* simple case, where just a number of days is added to date */
days = db_get_int (db_days);
composite_values[COMPOSITE_DAY] = abs (days);
switch (res_type)
{
case DB_TYPE_STRING:
case DB_TYPE_CHAR:
{
bool has_explicit_time = false;
const char *date_s = NULL;
int str_len = db_get_string_size (date);
date_s = db_get_string (date);
/* try to figure out the string format */
if (db_date_parse_datetime_parts (date_s, str_len, &db_datetime, &has_explicit_time, NULL, NULL, NULL))
{
is_dt = ER_TIMESTAMP_CONVERSION;
is_timest = ER_TIMESTAMP_CONVERSION;
is_d = ER_DATE_CONVERSION;
is_t = db_string_to_time_ex (date_s, str_len, &db_time);
}
else
{
if (has_explicit_time)
{
is_dt = NO_ERROR;
is_timest = ER_TIMESTAMP_CONVERSION;
is_d = ER_DATE_CONVERSION;
is_t = ER_TIME_CONVERSION;
}
else
{
db_date = db_datetime.date;
is_dt = ER_TIMESTAMP_CONVERSION;
is_timest = ER_TIMESTAMP_CONVERSION;
is_d = NO_ERROR;
is_t = ER_TIME_CONVERSION;
}
}
if (is_dt && is_d && is_t && is_timest)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto error;
}
/* add date stuff to a time -> error */
/* in fact, disable time operations, not available on mysql */
if (is_t == 0)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto error;
}
dt_p = &db_datetime;
d_p = &db_date;
ts_p = &db_timestamp;
/* except just TIME business, convert all to DATETIME */
}
break;
case DB_TYPE_DATE:
is_d = 1;
d_p = db_get_date (date);
break;
case DB_TYPE_DATETIMELTZ:
case DB_TYPE_DATETIME:
is_dt = 1;
dt_p = db_get_datetime (date);
if (res_type == DB_TYPE_DATETIMELTZ)
{
is_local_timezone = 1;
}
break;
case DB_TYPE_DATETIMETZ:
{
DB_DATETIMETZ *dt_tz_p;
dt_tz_p = db_get_datetimetz (date);
dt_p = &dt_tz_p->datetime;
tz_id = dt_tz_p->tz_id;
is_dt = 1;
is_timezone = 1;
break;
}
case DB_TYPE_TIMESTAMP:
case DB_TYPE_TIMESTAMPLTZ:
is_timest = 1;
ts_p = db_get_timestamp (date);
if (res_type == DB_TYPE_TIMESTAMPLTZ)
{
is_local_timezone = 1;
}
break;
case DB_TYPE_TIMESTAMPTZ:
{
DB_TIMESTAMPTZ *ts_tz_p;
ts_tz_p = db_get_timestamptz (date);
ts_p = &ts_tz_p->timestamp;
tz_id = ts_tz_p->tz_id;
is_timest = 1;
is_timezone = 1;
break;
}
case DB_TYPE_TIME:
/* should not reach here */
assert (0);
break;
default:
error_status = ER_OBJ_INVALID_ARGUMENTS;
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
goto error;
}
if (is_d >= 0)
{
y = m = d = h = mi = s = ms = 0;
db_date_decode (d_p, &m, &d, &y);
if (m == 0 && d == 0 && y == 0)
{
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ATTEMPT_TO_USE_ZERODATE, 0);
}
error_status = ER_ATTEMPT_TO_USE_ZERODATE;
goto error;
}
if (is_add)
{
if (days > 0)
{
ret = add_and_normalize_date_time (&y, &m, &d, &h, &mi, &s, &ms, composite_values);
}
else
{
ret = sub_and_normalize_date_time (&y, &m, &d, &h, &mi, &s, &ms, composite_values);
}
}
else
{
if (days > 0)
{
ret = sub_and_normalize_date_time (&y, &m, &d, &h, &mi, &s, &ms, composite_values);
}
else
{
ret = add_and_normalize_date_time (&y, &m, &d, &h, &mi, &s, &ms, composite_values);
}
}
/* year should always be greater than 1 and less than 9999 */
if (ret != NO_ERROR)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
goto error;
}
db_date_encode (&db_date, m, d, y);
if (res_type == DB_TYPE_STRING || res_type == DB_TYPE_CHAR)
{
db_date_to_string (res_s, 64, &db_date);
res_final = (char *) db_private_alloc (NULL, strlen (res_s) + 1);
if (!res_final)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
goto error;
}
strcpy (res_final, res_s);
db_make_string (result, res_final);
result->need_clear = true;
}
else
{
db_make_date (result, m, d, y);
}
}
else if (is_dt >= 0)
{
assert (dt_p != NULL);
y = m = d = h = mi = s = ms = 0;
db_datetime_decode (dt_p, &m, &d, &y, &h, &mi, &s, &ms);
if (m == 0 && d == 0 && y == 0 && h == 0 && mi == 0 && s == 0 && ms == 0)
{
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ATTEMPT_TO_USE_ZERODATE, 0);
}
error_status = ER_ATTEMPT_TO_USE_ZERODATE;
goto error;
}
if (is_add)
{
if (days > 0)
{
ret = add_and_normalize_date_time (&y, &m, &d, &h, &mi, &s, &ms, composite_values);
}
else
{
ret = sub_and_normalize_date_time (&y, &m, &d, &h, &mi, &s, &ms, composite_values);
}
}
else
{
if (days > 0)
{
ret = sub_and_normalize_date_time (&y, &m, &d, &h, &mi, &s, &ms, composite_values);
}
else
{
ret = add_and_normalize_date_time (&y, &m, &d, &h, &mi, &s, &ms, composite_values);
}
}
/* year should always be greater than 1 and less than 9999 */
if (ret != NO_ERROR)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
goto error;
}
if (is_timezone > 0)
{
error_status = tz_create_datetimetz_from_parts (m, d, y, h, mi, s, ms, &tz_id, &dt_tz);
}
else
{
error_status = db_datetime_encode (&db_datetime, m, d, y, h, mi, s, ms);
}
if (error_status != NO_ERROR)
{
goto error;
}
if (res_type == DB_TYPE_STRING || res_type == DB_TYPE_CHAR)
{
if (is_timezone > 0)
{
db_datetimetz_to_string (res_s, 64, &dt_tz.datetime, &dt_tz.tz_id);
}
else
{
db_datetime_to_string (res_s, 64, &db_datetime);
}
res_final = (char *) db_private_alloc (NULL, strlen (res_s) + 1);
if (!res_final)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
goto error;
}
strcpy (res_final, res_s);
db_make_string (result, res_final);
result->need_clear = true;
}
else
{
if (is_timezone > 0)
{
db_make_datetimetz (result, &dt_tz);
}
else if (is_local_timezone > 0)
{
db_make_datetimeltz (result, &db_datetime);
}
else
{
db_make_datetime (result, &db_datetime);
}
}
}
else if (is_timest >= 0)
{
assert (ts_p != NULL);
y = m = d = h = mi = s = ms = 0;
db_timestamp_decode_utc (ts_p, &db_date, &db_time);
db_date_decode (&db_date, &m, &d, &y);
db_time_decode (&db_time, &h, &mi, &s);
if (m == 0 && d == 0 && y == 0 && h == 0 && mi == 0 && s == 0)
{
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ATTEMPT_TO_USE_ZERODATE, 0);
}
error_status = ER_ATTEMPT_TO_USE_ZERODATE;
goto error;
}
if (is_add)
{
if (days > 0)
{
ret = add_and_normalize_date_time (&y, &m, &d, &h, &mi, &s, &ms, composite_values);
}
else
{
ret = sub_and_normalize_date_time (&y, &m, &d, &h, &mi, &s, &ms, composite_values);
}
}
else
{
if (days > 0)
{
ret = sub_and_normalize_date_time (&y, &m, &d, &h, &mi, &s, &ms, composite_values);
}
else
{
ret = add_and_normalize_date_time (&y, &m, &d, &h, &mi, &s, &ms, composite_values);
}
}
/* year should always be greater than 1 and less than 9999 */
if (ret != NO_ERROR)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
goto error;
}
if (is_local_timezone <= 0)
{
if (is_timezone > 0)
{
error_status = tz_create_datetimetz_from_parts (m, d, y, h, mi, s, ms, &tz_id, &dt_tz);
}
else
{
error_status = tz_create_datetimetz_from_parts (m, d, y, h, mi, s, ms, NULL, &dt_tz);
}
}
else
{
error_status = db_datetime_encode (&db_datetime, m, d, y, h, mi, s, ms);
dt_tz.datetime = db_datetime;
dt_tz.tz_id = tz_id;
}
if (error_status != NO_ERROR)
{
goto error;
}
if (res_type == DB_TYPE_STRING || res_type == DB_TYPE_CHAR)
{
DB_DATETIME dt_local;
error_status = tz_utc_datetimetz_to_local (&dt_tz.datetime, &dt_tz.tz_id, &dt_local);
if (error_status != NO_ERROR)
{
goto error;
}
db_datetime_to_string (res_s, 64, &dt_local);
res_final = (char *) db_private_alloc (NULL, strlen (res_s) + 1);
if (!res_final)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
goto error;
}
strcpy (res_final, res_s);
db_make_string (result, res_final);
result->need_clear = true;
}
else
{
if (is_timezone > 0)
{
db_make_datetimetz (result, &dt_tz);
}
else if (is_local_timezone > 0)
{
db_make_datetimeltz (result, &db_datetime);
}
else
{
DB_DATETIME dt_local;
error_status = tz_utc_datetimetz_to_local (&dt_tz.datetime, &dt_tz.tz_id, &dt_local);
if (error_status != NO_ERROR)
{
goto error;
}
db_make_datetime (result, &dt_local);
}
}
}
error:
if (error_status != NO_ERROR && error_status != ER_OUT_OF_VIRTUAL_MEMORY)
{
db_make_null (result);
if (is_null_on_error)
{
/* clear error and return NULL */
er_clear ();
return NO_ERROR;
}
}
return error_status;
}
int
db_date_add_interval_days (DB_VALUE * result, const DB_VALUE * date, const DB_VALUE * db_days)
{
return db_date_add_sub_interval_days (result, date, db_days, true);
}
int
db_date_sub_interval_days (DB_VALUE * result, const DB_VALUE * date, const DB_VALUE * db_days)
{
return db_date_add_sub_interval_days (result, date, db_days, false);
}
static int
db_date_add_sub_interval_composite_value (const char *expr_s, int unit, DB_BIGINT * values, int *is_positive_value)
{
int cnt = 0, base;
int max_composite;
bool millisec_flag = false;
char *endptr;
const char *ms_ptr = NULL;
assert (values);
switch (unit)
{
/* composite units */
case PT_SECOND_MILLISECOND:
base = COMPOSITE_SECOND;
max_composite = 2;
millisec_flag = true;
break;
case PT_MINUTE_MILLISECOND:
base = COMPOSITE_MINUTE;
max_composite = 3;
millisec_flag = true;
break;
case PT_HOUR_MILLISECOND:
base = COMPOSITE_HOUR;
max_composite = 4;
millisec_flag = true;
break;
case PT_DAY_MILLISECOND:
base = COMPOSITE_DAY;
max_composite = 5;
millisec_flag = true;
break;
case PT_MINUTE_SECOND:
base = COMPOSITE_MINUTE;
max_composite = 2;
break;
case PT_HOUR_SECOND:
base = COMPOSITE_HOUR;
max_composite = 3;
break;
case PT_DAY_SECOND:
base = COMPOSITE_DAY;
max_composite = 4;
break;
case PT_HOUR_MINUTE:
base = COMPOSITE_HOUR;
max_composite = 2;
break;
case PT_DAY_MINUTE:
base = COMPOSITE_DAY;
max_composite = 3;
break;
case PT_DAY_HOUR:
base = COMPOSITE_DAY;
max_composite = 2;
break;
case PT_YEAR_MONTH:
base = COMPOSITE_YEAR;
max_composite = 2;
break;
default:
assert (false);
return ER_OBJ_INVALID_ARGUMENTS;
}
while (char_isspace (*expr_s))
{
expr_s++;
}
// Like MySQL, it only accepts the first sign character, excluding spaces.
if (*expr_s == '-')
{
*is_positive_value = 0;
expr_s++;
}
else
{
*is_positive_value = 1;
}
while (*expr_s)
{
if (char_isdigit (*expr_s))
{
if (cnt >= max_composite)
{
return ER_FAILED;
}
ms_ptr = expr_s;
values[base + cnt] = strtoll (expr_s, &endptr, 10);
cnt++;
expr_s = endptr;
}
else
{
expr_s++;
}
}
if (millisec_flag && cnt > 0)
{
assert (ms_ptr);
// Adjust in milliseconds, cnt must be greater than 1 to be treated as yy.xxx format.
if (!char_isdigit (ms_ptr[1]))
{
values[base + cnt - 1] *= 100;
}
else if (!char_isdigit (ms_ptr[2]))
{
values[base + cnt - 1] *= 10;
}
}
if (cnt < max_composite)
{
memmove (values + (base + (max_composite - cnt)), values + base, sizeof (DB_BIGINT) * cnt);
memset (values + base, 0x00, sizeof (DB_BIGINT) * (max_composite - cnt));
}
return NO_ERROR;
}
/*
* db_date_add_sub_interval_expr () -
*
* Arguments:
* date: starting date
* expr: string with the amounts to add
* unit: unit(s) of the amounts
*
* Returns: int
*
* Errors:
*
* Note:
* Returns date + the amounts from expr
*/
static int
db_date_add_sub_interval_expr (DB_VALUE * result, const DB_VALUE * date, const DB_VALUE * expr, const int unit,
int is_add)
{
int is_positive_value = 0;
bool date_only = false;
DB_TYPE res_type, expr_type;
const char *expr_s = NULL, *date_s = NULL;
char res_s[64];
int error_status = NO_ERROR;
DB_DATETIME db_datetime, *dt_p = NULL;
DB_DATETIMETZ dt_tz;
DB_TIME db_time;
DB_DATE db_date, *d_p;
DB_TIMESTAMP db_timestamp, *ts_p = NULL;
int is_dt = -1, is_d = -1, is_t = -1, is_timest = -1, is_timezone = -1, is_local_timezone = -1;
DB_BIGINT unit_int_val = 0;
DB_BIGINT composite_values[COMPOSITE_MAX]; // { years, month, days, hours, minutes, seconds, millisec }
int y, m, d, h, mi, s, ms;
int ret;
char *res_final;
TZ_ID tz_id = 0;
res_type = DB_VALUE_DOMAIN_TYPE (date);
if (res_type == DB_TYPE_NULL || DB_IS_NULL (date))
{
db_make_null (result);
return NO_ERROR;
}
expr_type = DB_VALUE_DOMAIN_TYPE (expr);
if (expr_type == DB_TYPE_NULL || DB_IS_NULL (expr))
{
db_make_null (result);
return NO_ERROR;
}
bool is_null_on_error = prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS);
switch (expr_type)
{
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
expr_s = db_get_string (expr);
if (expr_s == NULL)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
goto error;
}
break;
case DB_TYPE_SMALLINT:
unit_int_val = db_get_short (expr);
break;
case DB_TYPE_INTEGER:
unit_int_val = db_get_int (expr);
break;
case DB_TYPE_BIGINT:
unit_int_val = db_get_bigint (expr);
break;
default:
assert (false);
error_status = ER_OBJ_INVALID_ARGUMENTS;
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
goto error;
}
memset (composite_values, 0x00, sizeof (composite_values));
if (expr_s == NULL)
{
assert (unit == PT_MILLISECOND || unit == PT_SECOND || unit == PT_MINUTE || unit == PT_HOUR ||
unit == PT_DAY || unit == PT_WEEK || unit == PT_MONTH || unit == PT_QUARTER || unit == PT_YEAR);
if (unit_int_val >= 0)
{
is_positive_value = 1;
}
else
{
is_positive_value = 0;
unit_int_val = -unit_int_val;
}
}
else
{
/* the big switch: according to unit, we parse expr and get amounts of ms/s/m/h/d/m/y/w/q to add or subtract */
/*
* expr is converted to char because it may contain a more complicated form
* for the multiple unit formats, for example:
* 'DAYS HOURS:MINUTES:SECONDS.MILLISECONDS'
* For the simple unit tags, expr is integer
*/
error_status = db_date_add_sub_interval_composite_value (expr_s, unit, composite_values, &is_positive_value);
if (error_status != NO_ERROR)
{
if (error_status == ER_FAILED)
{
db_make_null (result);
return NO_ERROR;
}
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
goto error;
}
}
switch (unit)
{
case PT_MILLISECOND:
composite_values[COMPOSITE_MILLISECOND] = unit_int_val;
break;
case PT_SECOND:
composite_values[COMPOSITE_SECOND] = unit_int_val;
break;
case PT_MINUTE:
composite_values[COMPOSITE_MINUTE] = unit_int_val;
break;
case PT_HOUR:
composite_values[COMPOSITE_HOUR] = unit_int_val;
break;
case PT_DAY:
composite_values[COMPOSITE_DAY] = unit_int_val;
date_only = true;
break;
case PT_WEEK:
composite_values[COMPOSITE_DAY] = unit_int_val * 7;
date_only = true;
break;
case PT_MONTH:
composite_values[COMPOSITE_MONTH] = unit_int_val;
date_only = true;
break;
case PT_QUARTER:
composite_values[COMPOSITE_MONTH] = unit_int_val * 3;
date_only = true;
break;
case PT_YEAR:
composite_values[COMPOSITE_YEAR] = unit_int_val;
date_only = true;
break;
case PT_SECOND_MILLISECOND:
case PT_MINUTE_MILLISECOND:
case PT_HOUR_MILLISECOND:
case PT_MINUTE_SECOND:
case PT_HOUR_SECOND:
case PT_HOUR_MINUTE:
assert (expr_s);
break;
case PT_DAY_MILLISECOND:
case PT_DAY_SECOND:
case PT_DAY_MINUTE:
case PT_DAY_HOUR:
assert (expr_s);
break;
case PT_YEAR_MONTH:
assert (expr_s);
date_only = true;
break;
default:
error_status = ER_OBJ_INVALID_ARGUMENTS;
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
goto error;
}
/* Convert string with date to DateTime or Time */
switch (res_type)
{
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
{
bool has_explicit_time = false;
bool has_zone = false;
int str_len = db_get_string_size (date);
date_s = db_get_string (date);
if (db_string_to_datetimetz_ex (date_s, str_len, &dt_tz, &has_zone) == NO_ERROR && has_zone == true)
{
is_dt = NO_ERROR;
is_timest = ER_TIMESTAMP_CONVERSION;
is_d = ER_DATE_CONVERSION;
is_t = ER_TIME_CONVERSION;
is_timezone = 1;
db_datetime = dt_tz.datetime;
tz_id = dt_tz.tz_id;
}
/* try to figure out the string format */
else if (db_date_parse_datetime_parts (date_s, str_len, &db_datetime, &has_explicit_time, NULL, NULL, NULL))
{
is_dt = ER_TIMESTAMP_CONVERSION;
is_timest = ER_TIMESTAMP_CONVERSION;
is_d = ER_DATE_CONVERSION;
is_t = db_string_to_time_ex (date_s, str_len, &db_time);
}
else if (has_explicit_time)
{
is_dt = NO_ERROR;
is_timest = ER_TIMESTAMP_CONVERSION;
is_d = ER_DATE_CONVERSION;
is_t = ER_TIME_CONVERSION;
}
else
{
db_date = db_datetime.date;
is_dt = ER_TIMESTAMP_CONVERSION;
is_timest = ER_TIMESTAMP_CONVERSION;
is_d = NO_ERROR;
is_t = ER_TIME_CONVERSION;
}
if (is_dt && is_d && is_t && is_timest)
{
error_status = ER_DATE_CONVERSION;
goto error;
}
/* add date stuff to a time -> error */
/* in fact, disable time operations, not available on mysql */
if (is_t == 0)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
goto error;
}
dt_p = &db_datetime;
d_p = &db_date;
ts_p = &db_timestamp;
/* except just TIME business, convert all to DATETIME */
}
break;
case DB_TYPE_DATE:
is_d = 1;
d_p = db_get_date (date);
break;
case DB_TYPE_DATETIME:
case DB_TYPE_DATETIMELTZ:
is_dt = 1;
dt_p = db_get_datetime (date);
if (res_type == DB_TYPE_DATETIMELTZ)
{
is_local_timezone = 1;
}
break;
case DB_TYPE_DATETIMETZ:
{
DB_DATETIMETZ *dt_tz_p;
dt_tz_p = db_get_datetimetz (date);
dt_p = &dt_tz_p->datetime;
tz_id = dt_tz_p->tz_id;
is_dt = 1;
is_timezone = 1;
break;
}
case DB_TYPE_TIMESTAMP:
case DB_TYPE_TIMESTAMPLTZ:
is_timest = 1;
ts_p = db_get_timestamp (date);
if (res_type == DB_TYPE_TIMESTAMPLTZ)
{
is_local_timezone = 1;
}
break;
case DB_TYPE_TIMESTAMPTZ:
{
DB_TIMESTAMPTZ *ts_tz_p;
ts_tz_p = db_get_timestamptz (date);
ts_p = &ts_tz_p->timestamp;
tz_id = ts_tz_p->tz_id;
is_timest = 1;
is_timezone = 1;
break;
}
case DB_TYPE_TIME:
/* should not reach here */
assert (0);
break;
default:
error_status = ER_OBJ_INVALID_ARGUMENTS;
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
goto error;
}
/* treat as date only if adding date units, else treat as datetime */
if (is_d >= 0)
{
y = m = d = h = mi = s = ms = 0;
db_date_decode (d_p, &m, &d, &y);
if (m == 0 && d == 0 && y == 0)
{
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
}
error_status = ER_DATE_CONVERSION;
goto error;
}
if (is_positive_value ^ is_add)
{
ret = sub_and_normalize_date_time (&y, &m, &d, &h, &mi, &s, &ms, composite_values);
}
else
{
ret = add_and_normalize_date_time (&y, &m, &d, &h, &mi, &s, &ms, composite_values);
}
/* year should always be greater than 1 and less than 9999 */
if (ret != NO_ERROR)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
goto error;
}
if (date_only)
{
db_date_encode (&db_date, m, d, y);
if (m == 0 && d == 0 && y == 0)
{
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
}
error_status = ER_DATE_CONVERSION;
goto error;
}
if (res_type == DB_TYPE_STRING || res_type == DB_TYPE_CHAR)
{
db_date_to_string (res_s, 64, &db_date);
res_final = (char *) db_private_alloc (NULL, strlen (res_s) + 1);
if (res_final == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
goto error;
}
strcpy (res_final, res_s);
db_make_string (result, res_final);
result->need_clear = true;
}
else
{
db_make_date (result, m, d, y);
}
}
else
{
db_datetime.date = db_datetime.time = 0;
db_datetime_encode (&db_datetime, m, d, y, h, mi, s, ms);
if (m == 0 && d == 0 && y == 0 && h == 0 && mi == 0 && s == 0 && ms == 0)
{
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
}
error_status = ER_DATE_CONVERSION;
goto error;
}
if (res_type == DB_TYPE_STRING || res_type == DB_TYPE_CHAR)
{
db_datetime_to_string (res_s, 64, &db_datetime);
res_final = (char *) db_private_alloc (NULL, strlen (res_s) + 1);
if (res_final == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
goto error;
}
strcpy (res_final, res_s);
db_make_string (result, res_final);
result->need_clear = true;
}
else
{
if (is_local_timezone > 0)
{
error_status = tz_create_datetimetz_from_ses (&db_datetime, &dt_tz);
if (error_status != NO_ERROR)
{
goto error;
}
db_make_datetimeltz (result, &dt_tz.datetime);
}
else if (is_timezone > 0)
{
TZ_REGION tz_region;
tz_id_to_region (&tz_id, &tz_region);
error_status = tz_create_datetimetz (&db_datetime, NULL, 0, &tz_region, &dt_tz, NULL);
if (error_status != NO_ERROR)
{
goto error;
}
db_make_datetimetz (result, &dt_tz);
}
else
{
db_make_datetime (result, &db_datetime);
}
}
}
}
else if (is_dt >= 0)
{
assert (dt_p != NULL);
y = m = d = h = mi = s = ms = 0;
db_datetime_decode (dt_p, &m, &d, &y, &h, &mi, &s, &ms);
if (m == 0 && d == 0 && y == 0 && h == 0 && mi == 0 && s == 0 && ms == 0)
{
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
}
error_status = ER_DATE_CONVERSION;
goto error;
}
if (is_positive_value ^ is_add)
{
ret = sub_and_normalize_date_time (&y, &m, &d, &h, &mi, &s, &ms, composite_values);
}
else
{
ret = add_and_normalize_date_time (&y, &m, &d, &h, &mi, &s, &ms, composite_values);
}
/* year should always be greater than 1 and less than 9999 */
if (ret != NO_ERROR)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
goto error;
}
if (is_timezone > 0)
{
error_status = tz_create_datetimetz_from_parts (m, d, y, h, mi, s, ms, &tz_id, &dt_tz);
}
else
{
error_status = db_datetime_encode (&db_datetime, m, d, y, h, mi, s, ms);
}
if (error_status != NO_ERROR)
{
goto error;
}
if (res_type == DB_TYPE_STRING || res_type == DB_TYPE_CHAR)
{
if (is_timezone > 0)
{
db_datetimetz_to_string (res_s, 64, &dt_tz.datetime, &dt_tz.tz_id);
}
else
{
db_datetime_to_string (res_s, 64, &db_datetime);
}
res_final = (char *) db_private_alloc (NULL, strlen (res_s) + 1);
if (res_final == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
goto error;
}
strcpy (res_final, res_s);
db_make_string (result, res_final);
result->need_clear = true;
}
else
{
/* datetime, date + time units, timestamp => return datetime */
if (is_local_timezone > 0)
{
db_make_datetimeltz (result, &db_datetime);
}
else if (is_timezone > 0)
{
db_make_datetimetz (result, &dt_tz);
}
else
{
db_make_datetime (result, &db_datetime);
}
}
}
else if (is_timest >= 0)
{
assert (ts_p != NULL);
y = m = d = h = mi = s = ms = 0;
db_timestamp_decode_utc (ts_p, &db_date, &db_time);
db_date_decode (&db_date, &m, &d, &y);
db_time_decode (&db_time, &h, &mi, &s);
if (m == 0 && d == 0 && y == 0 && h == 0 && mi == 0 && s == 0)
{
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIMESTAMP_CONVERSION, 0);
}
error_status = ER_TIMESTAMP_CONVERSION;
goto error;
}
if (is_positive_value ^ is_add)
{
ret = sub_and_normalize_date_time (&y, &m, &d, &h, &mi, &s, &ms, composite_values);
}
else
{
ret = add_and_normalize_date_time (&y, &m, &d, &h, &mi, &s, &ms, composite_values);
}
/* year should always be greater than 1 and less than 9999 */
if (ret != NO_ERROR)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
if (!is_null_on_error)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
goto error;
}
if (is_local_timezone <= 0)
{
if (is_timezone > 0)
{
error_status = tz_create_datetimetz_from_parts (m, d, y, h, mi, s, ms, &tz_id, &dt_tz);
}
else
{
error_status = tz_create_datetimetz_from_parts (m, d, y, h, mi, s, ms, NULL, &dt_tz);
}
}
else
{
error_status = db_datetime_encode (&db_datetime, m, d, y, h, mi, s, ms);
dt_tz.datetime = db_datetime;
dt_tz.tz_id = tz_id;
}
if (error_status != NO_ERROR)
{
goto error;
}
if (res_type == DB_TYPE_STRING || res_type == DB_TYPE_CHAR)
{
DB_DATETIME dt_local;
error_status = tz_utc_datetimetz_to_local (&dt_tz.datetime, &dt_tz.tz_id, &dt_local);
if (error_status != NO_ERROR)
{
goto error;
}
db_datetime_to_string (res_s, 64, &dt_local);
res_final = (char *) db_private_alloc (NULL, strlen (res_s) + 1);
if (res_final == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
goto error;
}
strcpy (res_final, res_s);
db_make_string (result, res_final);
result->need_clear = true;
}
else
{
/* datetime, date + time units, timestamp => return datetime */
if (is_local_timezone > 0)
{
db_make_datetimeltz (result, &db_datetime);
}
else if (is_timezone > 0)
{
db_make_datetimetz (result, &dt_tz);
}
else
{
DB_DATETIME dt_local;
error_status = tz_utc_datetimetz_to_local (&dt_tz.datetime, &dt_tz.tz_id, &dt_local);
if (error_status != NO_ERROR)
{
goto error;
}
db_make_datetime (result, &dt_local);
}
}
}
error:
if (error_status != NO_ERROR && error_status != ER_OUT_OF_VIRTUAL_MEMORY)
{
db_make_null (result);
if (is_null_on_error)
{
/* clear error and return NULL */
er_clear ();
return NO_ERROR;
}
}
return error_status;
}
/*
* db_date_add_interval_expr ()
*
* Arguments:
* result(out):
* date(in): source date
* expr(in): to be added interval
* unit(in): unit of interval expr
*
* Returns: int
*
* Note:
*/
int
db_date_add_interval_expr (DB_VALUE * result, const DB_VALUE * date, const DB_VALUE * expr, const int unit)
{
return db_date_add_sub_interval_expr (result, date, expr, unit, 1);
}
/*
* db_date_sub_interval_expr ()
*
* Arguments:
* result(out):
* date(in): source date
* expr(in): to be subtracted interval
* unit(in): unit of interval expr
*
* Returns: int
*
* Note:
*/
int
db_date_sub_interval_expr (DB_VALUE * result, const DB_VALUE * date, const DB_VALUE * expr, const int unit)
{
return db_date_add_sub_interval_expr (result, date, expr, unit, 0);
}
static bool
db_check_datetime_format (const DB_VALUE * value_ptr)
{
int len = db_get_string_size (value_ptr);
char *ps = (char *) db_get_string (value_ptr);
#define LEAST_DATE_FORMAT_CHECK_LENGTH (26) // "09:10:15.359 am 2011-04-20"
char *pe;
int df = 0;
int tf = 0;
if (*ps == '\0' || len <= 8 /* "12:34:56", "1:1:1 1-1-1" */ )
{
return false;
}
pe = ps + ((len < LEAST_DATE_FORMAT_CHECK_LENGTH) ? len : LEAST_DATE_FORMAT_CHECK_LENGTH);
while (ps < pe)
{
if (*ps == '-' || *ps == '/')
{
if (tf)
{
return true;
}
df++;
}
else if (*ps == ':')
{
if (df)
{
return true;
}
tf++;
}
ps++;
}
return false;
}
static int
get_date_time_info (DATE_TIME_INFO * dtzi, DB_TYPE res_type, const DB_VALUE * value_ptr, bool dateformat)
{
int error_status = NO_ERROR;
DB_TIME db_time;
DB_DATE db_date;
DB_DATE *date_ptr;
date_ptr = dateformat ? &db_date : NULL;
dtzi->is_valid_tz = false;
switch (res_type)
{
case DB_TYPE_TIMESTAMP:
{
DB_TIMESTAMP *ts_p = db_get_timestamp (value_ptr);
error_status = tz_create_session_tzid_for_timestamp (ts_p, &dtzi->tz_id);
if (error_status != NO_ERROR)
{
return error_status;
}
(void) db_timestamp_decode_ses (ts_p, date_ptr, &db_time);
db_time_decode (&db_time, &dtzi->h, &dtzi->mi, &dtzi->s);
if (dateformat)
{
db_date_decode (date_ptr, &dtzi->month, &dtzi->day, &dtzi->year);
}
dtzi->is_valid_tz = true;
}
break;
case DB_TYPE_TIMESTAMPTZ:
{
DB_TIMESTAMPTZ *tsmp_tz;
tsmp_tz = db_get_timestamptz (value_ptr);
error_status = db_timestamp_decode_w_tz_id (&tsmp_tz->timestamp, &tsmp_tz->tz_id, date_ptr, &db_time);
if (error_status != NO_ERROR)
{
return error_status;
}
db_time_decode (&db_time, &dtzi->h, &dtzi->mi, &dtzi->s);
if (dateformat)
{
db_date_decode (date_ptr, &dtzi->month, &dtzi->day, &dtzi->year);
}
dtzi->tz_id = tsmp_tz->tz_id;
dtzi->is_valid_tz = true;
}
break;
case DB_TYPE_TIMESTAMPLTZ:
{
DB_TIMESTAMP *tsmp;
tsmp = db_get_timestamp (value_ptr);
error_status = tz_create_session_tzid_for_timestamp (tsmp, &dtzi->tz_id);
if (error_status != NO_ERROR)
{
return error_status;
}
error_status = db_timestamp_decode_w_tz_id (tsmp, &dtzi->tz_id, date_ptr, &db_time);
if (error_status != NO_ERROR)
{
return error_status;
}
db_time_decode (&db_time, &dtzi->h, &dtzi->mi, &dtzi->s);
if (dateformat)
{
db_date_decode (date_ptr, &dtzi->month, &dtzi->day, &dtzi->year);
}
dtzi->is_valid_tz = true;
}
break;
case DB_TYPE_DATETIME:
{
DB_DATETIME *dt_p = db_get_datetime (value_ptr);
error_status = tz_create_session_tzid_for_datetime (dt_p, true, &dtzi->tz_id);
if (error_status != NO_ERROR)
{
return error_status;
}
db_datetime_decode (dt_p, &dtzi->month, &dtzi->day, &dtzi->year, &dtzi->h, &dtzi->mi, &dtzi->s, &dtzi->ms);
dtzi->is_valid_tz = true;
}
break;
case DB_TYPE_DATETIMETZ:
{
DB_DATETIME dt_local;
DB_DATETIMETZ *dt_tz;
dt_tz = db_get_datetimetz (value_ptr);
error_status = tz_utc_datetimetz_to_local (&dt_tz->datetime, &dt_tz->tz_id, &dt_local);
if (error_status != NO_ERROR)
{
return error_status;
}
db_datetime_decode (&dt_local, &dtzi->month, &dtzi->day, &dtzi->year, &dtzi->h, &dtzi->mi, &dtzi->s, &dtzi->ms);
dtzi->tz_id = dt_tz->tz_id;
dtzi->is_valid_tz = true;
}
break;
case DB_TYPE_DATETIMELTZ:
{
DB_DATETIME *dt, dt_local;
dt = db_get_datetime (value_ptr);
error_status = tz_create_session_tzid_for_datetime (dt, true, &dtzi->tz_id);
if (error_status != NO_ERROR)
{
return error_status;
}
error_status = tz_utc_datetimetz_to_local (dt, &dtzi->tz_id, &dt_local);
if (error_status != NO_ERROR)
{
return error_status;
}
db_datetime_decode (&dt_local, &dtzi->month, &dtzi->day, &dtzi->year, &dtzi->h, &dtzi->mi, &dtzi->s, &dtzi->ms);
dtzi->is_valid_tz = true;
}
break;
case DB_TYPE_VARCHAR:
case DB_TYPE_CHAR:
{
bool check_has_date = false;
if (dateformat == false)
{
if (db_check_datetime_format (value_ptr))
{
check_has_date = true;
}
}
if (dateformat || check_has_date)
{
DB_VALUE dt;
TP_DOMAIN *tp_datetime = db_type_to_db_domain (DB_TYPE_DATETIME);
TP_DOMAIN *tp_datetimetz = db_type_to_db_domain (DB_TYPE_DATETIMETZ);
if (tp_value_cast (value_ptr, &dt, tp_datetime, false) == DOMAIN_COMPATIBLE)
{
db_datetime_decode (db_get_datetime (&dt), &dtzi->month, &dtzi->day, &dtzi->year, &dtzi->h, &dtzi->mi,
&dtzi->s, &dtzi->ms);
if (tp_value_cast (value_ptr, &dt, tp_datetimetz, false) == DOMAIN_COMPATIBLE)
{
DB_DATETIMETZ dt_tz;
dt_tz = *db_get_datetimetz (&dt);
dtzi->tz_id = dt_tz.tz_id;
dtzi->is_valid_tz = true;
}
if (check_has_date)
{
dtzi->ms = 0;
}
break;
}
else if (check_has_date == false)
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
}
{
DB_VALUE tm;
TP_DOMAIN *tp_time = db_type_to_db_domain (DB_TYPE_TIME);
if (tp_value_cast (value_ptr, &tm, tp_time, false) != DOMAIN_COMPATIBLE)
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
db_time_decode (db_get_time (&tm), &dtzi->h, &dtzi->mi, &dtzi->s);
error_status = tz_create_session_tzid_for_time (db_get_time (&tm), true, &dtzi->tz_id);
if (error_status != NO_ERROR)
{
return error_status;
}
dtzi->is_valid_tz = true;
}
}
break;
case DB_TYPE_DATE:
assert (dateformat);
{
DB_DATE *d_p = db_get_date (value_ptr);
db_date_decode (d_p, &dtzi->month, &dtzi->day, &dtzi->year);
}
break;
case DB_TYPE_TIME:
if (dateformat == false)
{
DB_TIME *t_p = db_get_time (value_ptr);
error_status = tz_create_session_tzid_for_time (t_p, true, &dtzi->tz_id);
if (error_status != NO_ERROR)
{
return error_status;
}
db_time_decode (t_p, &dtzi->h, &dtzi->mi, &dtzi->s);
dtzi->is_valid_tz = true;
break;
}
else
{
[[fallthrough]];
}
default:
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
return error_status;
}
inline static void
get_week_info (DATE_TIME_INFO * dtzi, bool sunday_first, int *days, int *tu, int *tv, int *tx, bool * binit)
{
int weeks, ld_fw, days_counter;
int dow2 = db_get_day_of_week (dtzi->year, 1, 1);
int i, idx = sunday_first ? 1 : 0;
if (binit[idx])
{
return;
}
if (sunday_first)
{
/* %U Week (00..53), where Sunday is the first d of the week */
/* %V Week (01..53), where Sunday is the first d of the week; used with %X */
/* %X Year for the week where Sunday is the first day of the week, numeric, four digits; used with %V */
ld_fw = 7 - dow2;
for (days_counter = dtzi->day, i = 1; i < dtzi->month; i++)
{
days_counter += days[i];
}
if (days_counter <= ld_fw)
{
weeks = dow2 == 0 ? 1 : 0;
}
else
{
days_counter -= (dow2 == 0) ? 0 : ld_fw;
weeks = days_counter / 7 + (days_counter % 7 ? 1 : 0);
}
tu[idx] = tv[idx] = weeks;
tx[idx] = dtzi->year;
if (tv[idx] == 0)
{
dow2 = db_get_day_of_week (dtzi->year - 1, 1, 1);
days_counter = 365 + LEAP (dtzi->year - 1) - (dow2 == 0 ? 0 : 7 - dow2);
tv[idx] = days_counter / 7 + (days_counter % 7 ? 1 : 0);
tx[idx] = dtzi->year - 1;
}
}
else
{
/* %u Week (00..53), where Monday is the first d of the week */
/* %v Week (01..53), where Monday is the first d of the week; used with %x */
/* %x Year for the week, where Monday is the first day of the week, numeric, four digits; used with %v */
weeks = (dow2 >= 1 && dow2 <= 4) ? 1 : 0;
ld_fw = (dow2 == 0) ? 1 : (7 - dow2 + 1);
for (days_counter = dtzi->day, i = 1; i < dtzi->month; i++)
{
days_counter += days[i];
}
if (days_counter > ld_fw)
{
days_counter -= ld_fw;
weeks += days_counter / 7 + (days_counter % 7 ? 1 : 0);
}
tu[idx] = weeks;
tv[idx] = weeks;
tx[idx] = dtzi->year;
if (tv[idx] == 0)
{
dow2 = db_get_day_of_week (dtzi->year - 1, 1, 1);
weeks = dow2 >= 1 && dow2 <= 4 ? 1 : 0;
ld_fw = dow2 == 0 ? 1 : 7 - dow2 + 1;
days_counter = 365 + LEAP (dtzi->year - 1) - ld_fw;
tv[idx] = weeks + days_counter / 7 + (days_counter % 7 ? 1 : 0);
tx[idx] = dtzi->year - 1;
}
else if (tv[idx] == 53)
{
dow2 = db_get_day_of_week (dtzi->year + 1, 1, 1);
if (dow2 >= 1 && dow2 <= 4)
{
tv[idx] = 1;
tx[idx] = dtzi->year + 1;
}
}
}
binit[idx] = true;
}
static int
put_date_time_info (DATE_TIME_INFO * dtzi, const DB_VALUE * format, INTL_LANG date_lang_id,
const LANG_LOCALE_DATA * lld, bool dateformat, char **res_ptr)
{
const char *format_s = NULL, *format_e = NULL;
char *res;
int alloc_size, len;
int error_status = NO_ERROR;
char tzr[TZR_SIZE + 1], tzd[TZ_DS_STRING_SIZE + 1];
int tzh = 0, tzm = 0;
int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int dow = -1;
int tu[2] = { 0, 0 };
int tv[2] = { 0, 0 };
int tx[2] = { 0, 0 };
bool tinit[2];
bool is_matched;
bool try_tz_explain_tz_id = false;
tinit[0] = tinit[1] = false;
assert (dtzi->mi >= 0);
assert (dtzi->s >= 0);
assert (dtzi->ms >= 0);
assert (dtzi->h >= 0);
format_s = db_get_string (format);
//len = db_get_string_size (format);
format_e = format_s + db_get_string_size (format);
/* TODO:
* Memory wastage must be prevented.
* 1) You can calculate the required memory by reviewing the format in advance.
* 2) After allocating it large enough, you can resize it at the end.(use db_private_realloc(thrd, ptr, size))
*/
alloc_size = 128;
len = 0;
res = (char *) db_private_alloc (NULL, alloc_size);
if (res == NULL)
{
return ER_OUT_OF_VIRTUAL_MEMORY;
}
days[2] += LEAP (dtzi->year);
while (format_s < format_e)
{
/* assume we can't add at a time mode than 36 chars: 'America/Argentina/ComodRivadavia' */
if ((len + 36) >= alloc_size)
{
char *tmp;
tmp = (char *) db_private_alloc (NULL, alloc_size + 128);
if (tmp == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
goto error;
}
alloc_size += 128;
memcpy (tmp, res, len);
db_private_free_and_init (NULL, res);
res = tmp;
}
if (format_s[0] != '%')
{
res[len++] = *format_s++;
continue;
}
// meet '%'
is_matched = true;
switch (format_s[1])
{
case '%':
res[len++] = format_s[1];
break;
case 'f': /* %f Milliseconds (000..999) */
len += sprintf (res + len, "%03d", dtzi->ms);
break;
case 'H': /* %H Hour (00..23) */
len += sprintf (res + len, "%02d", dtzi->h);
break;
case 'h': /* %h Hour (01..12) */
len += sprintf (res + len, "%02d", (dtzi->h % 12 == 0) ? 12 : (dtzi->h % 12));
break;
case 'I': /* %I Hour (01..12) */
len += sprintf (res + len, "%02d", (dtzi->h % 12 == 0) ? 12 : (dtzi->h % 12));
break;
case 'i': /* %i Minutes, numeric (00..59) */
len += sprintf (res + len, "%02d", dtzi->mi);
break;
case 'k': /* %k Hour (0..23) */
len += sprintf (res + len, "%d", dtzi->h);
break;
case 'l': /* %l Hour (1..12) */
len += sprintf (res + len, "%d", (dtzi->h % 12 == 0) ? 12 : (dtzi->h % 12));
break;
case 'p': /* %p AM or PM */
len += sprintf (res + len, "%s", (dtzi->h > 11) ? lld->am_pm[PM_NAME] : lld->am_pm[AM_NAME]);
break;
case 'r': /* %r Time, 12-hour (hh:mm:ss followed by AM or PM) */
len += sprintf (res + len, "%02d:%02d:%02d %s", (dtzi->h % 12 == 0) ? 12 : (dtzi->h % 12), dtzi->mi,
dtzi->s, (dtzi->h > 11) ? lld->am_pm[PM_NAME] : lld->am_pm[AM_NAME]);
break;
case 'S': /* %S Seconds (00..59) */
len += sprintf (res + len, "%02d", dtzi->s);
break;
case 's': /* %s Seconds (00..59) */
len += sprintf (res + len, "%02d", dtzi->s);
break;
case 'T': /* %T Time, 24-hour (hh:mm:ss) or TZR/TZD/TZH/TZM */
if (format_s[2] != 'Z')
{
/* %T Time, 24-hour (hh:mm:ss) */
len += sprintf (res + len, "%02d:%02d:%02d", dtzi->h, dtzi->mi, dtzi->s);
}
else
{
switch (format_s[3])
{
case 'R':
case 'D':
case 'H':
case 'M':
if (dtzi->is_valid_tz == false)
{
if ((error_status = er_errid ()) != ER_TZ_LOAD_ERROR)
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
}
er_clear ();
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto error;
}
if (try_tz_explain_tz_id == false)
{
tzr[0] = '\0';
tzd[0] = '\0';
error_status = tz_explain_tz_id (&dtzi->tz_id, tzr, sizeof (tzr), tzd, sizeof (tzd), &tzh, &tzm);
if (error_status != NO_ERROR)
{
return error_status;
}
try_tz_explain_tz_id = true;
}
if (format_s[3] == 'H')
{
if ((tzh >= 0) && (tzm >= 0))
{
len += sprintf (res + len, "%c%02d", '+', tzh);
}
else
{
len += sprintf (res + len, "%c%02d", '-', -tzh);
}
}
else if (format_s[3] == 'M')
{
len += sprintf (res + len, "%02d", (tzm >= 0) ? tzm : -tzm);
}
else
{
len += sprintf (res + len, "%s", ((format_s[3] == 'R') ? tzr : tzd));
}
format_s += 2;
break;
default: /* %T Time, 24-hour (hh:mm:ss) */
len += sprintf (res + len, "%02d:%02d:%02d", dtzi->h, dtzi->mi, dtzi->s);
break;
}
}
break;
default:
if (dateformat == false)
{
/* ignore '%' */
res[len++] = format_s[1];
break;
}
is_matched = false;
break;
}
if (dateformat == false || is_matched)
{
format_s += 2;
continue;
}
switch (format_s[1])
{
case 'a': /* %a Abbreviated weekday name (Sun..Sat) */
if (dow == -1)
{
dow = db_get_day_of_week (dtzi->year, dtzi->month, dtzi->day);
}
len += sprintf (res + len, "%s", lld->day_short_name[dow]);
break;
case 'b': /* %b Abbreviated m name (Jan..Dec) */
if (dtzi->month > 0)
{
len += sprintf (res + len, "%s", lld->month_short_name[dtzi->month - 1]);
}
break;
case 'c': /* %c Month, numeric (0..12) - actually (1..12) */
len += sprintf (res + len, "%d", dtzi->month);
break;
case 'D': /* %D Day of the m with English suffix (0th, 1st, 2nd, 3rd,...) */
if (date_lang_id != INTL_LANG_ENGLISH)
{
len += sprintf (res + len, "%d", dtzi->day);
}
else if (dtzi->day / 10 == 1)
{
len += sprintf (res + len, "%dth", dtzi->day);
}
else
{
switch (dtzi->day % 10)
{
case 1:
len += sprintf (res + len, "%dst", dtzi->day);
break;
case 2:
len += sprintf (res + len, "%dnd", dtzi->day);
break;
case 3:
len += sprintf (res + len, "%drd", dtzi->day);
break;
default:
len += sprintf (res + len, "%dth", dtzi->day);
break;
}
}
break;
case 'd': /* %d Day of the m, numeric (00..31) */
len += sprintf (res + len, "%02d", dtzi->day);
break;
case 'e': /* %e Day of the m, numeric (0..31) - actually (1..31) */
len += sprintf (res + len, "%d", dtzi->day);
break;
case 'j': /* %j Day of year (001..366) */
int j, i;
for (j = dtzi->day, i = 1; i < dtzi->month; i++)
{
j += days[i];
}
len += sprintf (res + len, "%03d", j);
break;
case 'M': /* %M Month name (January..December) */
if (dtzi->month > 0)
{
len += sprintf (res + len, "%s", lld->month_name[dtzi->month - 1]);
}
break;
case 'm': /* %m Month, numeric (00..12) */
len += sprintf (res + len, "%02d", dtzi->month);
break;
case 'U': /* %U Week (00..53), where Sunday is the first day of the week */
get_week_info (dtzi, true, days, tu, tv, tx, tinit);
len += sprintf (res + len, "%02d", tu[1]);
break;
case 'u': /* %u Week (00..53), where Monday is the first day of the week */
get_week_info (dtzi, false, days, tu, tv, tx, tinit);
len += sprintf (res + len, "%02d", tu[0]);
break;
case 'V': /* %V Week (01..53), where Sunday is the first day of the week; used with %X */
get_week_info (dtzi, true, days, tu, tv, tx, tinit);
len += sprintf (res + len, "%02d", tv[1]);
break;
case 'v': /* %v Week (01..53), where Monday is the first day of the week; used with %x */
get_week_info (dtzi, false, days, tu, tv, tx, tinit);
len += sprintf (res + len, "%02d", tv[0]);
break;
case 'W': /* %W Weekday name (Sunday..Saturday) */
if (dow == -1)
{
dow = db_get_day_of_week (dtzi->year, dtzi->month, dtzi->day);
}
len += sprintf (res + len, "%s", lld->day_name[dow]);
break;
case 'w': /* %w Day of the week (0=Sunday..6=Saturday) */
if (dow == -1)
{
dow = db_get_day_of_week (dtzi->year, dtzi->month, dtzi->day);
}
len += sprintf (res + len, "%d", dow);
break;
case 'X': /* %X Year for the week where Sunday is the first day of the week, numeric, four digits; used with %V */
get_week_info (dtzi, true, days, tu, tv, tx, tinit);
len += sprintf (res + len, "%04d", tx[1]);
break;
case 'x': /* %x Year for the week, where Monday is the first day of the week, numeric, four digits; used with %v */
get_week_info (dtzi, false, days, tu, tv, tx, tinit);
len += sprintf (res + len, "%04d", tx[0]);
break;
case 'Y': /* %Y Year, numeric, four digits */
len += sprintf (res + len, "%04d", dtzi->year);
break;
case 'y': /* %y Year, numeric (two digits) */
len += sprintf (res + len, "%02d", dtzi->year % 100);
break;
default: /* ignore '%' */
res[len++] = format_s[1];
break;
}
format_s += 2;
} // while
res[len] = '\0';
*res_ptr = res;
return error_status;
error:
if (res != NULL)
{
db_private_free_and_init (NULL, res);
}
return error_status;
}
/*
* db_date_format ()
*
* Arguments:
* date_value: source date
* format: string with format specifiers
* result: output string
* domain: domain of result
*
* Returns: int
*
* Errors:
*
* Note:
* formats the date according to a specified format
*/
int
db_date_format (const DB_VALUE * date_value, const DB_VALUE * format, const DB_VALUE * date_lang, DB_VALUE * result,
const TP_DOMAIN * domain)
{
DB_TYPE res_type;
int error_status = NO_ERROR;
DATE_TIME_INFO dtz_info;
INTL_LANG date_lang_id;
const LANG_LOCALE_DATA *lld;
bool dummy;
INTL_CODESET codeset;
int res_collation;
assert (date_lang != NULL);
memset (&dtz_info, 0x00, sizeof (dtz_info));
if (date_value == NULL || format == NULL || DB_IS_NULL (date_value) || DB_IS_NULL (format))
{
db_make_null (result);
return error_status;
}
#if 0
// Remains disabled unless pt_get_expression_definition() is modified.
if (!is_char_string (format))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
#else
assert (is_char_string (format)); /* In pt_get_expression_definition(), arg2 is specified as a string. */
#endif
assert (DB_VALUE_TYPE (date_lang) == DB_TYPE_INTEGER);
date_lang_id = lang_get_lang_id_from_flag (db_get_int (date_lang), &dummy, &dummy);
if (domain != NULL && domain->collation_flag != TP_DOMAIN_COLL_LEAVE)
{
codeset = TP_DOMAIN_CODESET (domain);
res_collation = TP_DOMAIN_COLLATION (domain);
}
else
{
codeset = db_get_string_codeset (format);
res_collation = db_get_string_collation (format);
}
lld = lang_get_specific_locale (date_lang_id, codeset);
if (lld == NULL)
{
error_status = ER_LANG_CODESET_NOT_AVAILABLE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 2, lang_get_lang_name_from_id (date_lang_id),
lang_charset_name (codeset));
return error_status;
}
res_type = DB_VALUE_DOMAIN_TYPE (date_value);
/* 1. Get date values */
error_status = get_date_time_info (&dtz_info, res_type, date_value, true);
if (error_status != NO_ERROR)
{
return error_status;
}
/* 2. Generate the output according to the format and the values */
char *res = NULL;
error_status = put_date_time_info (&dtz_info, format, date_lang_id, lld, true, &res);
if (error_status == NO_ERROR)
{
db_make_string (result, res);
db_string_put_cs_and_collation (result, codeset, res_collation);
result->need_clear = true;
return error_status;
}
if (res)
{
db_private_free_and_init (NULL, res);
}
return error_status;
}
/*
* parse_digits ()
*
* Arguments:
* s: source string to parse
* nr: output number
* cnt: length at which we trim the number (-1 if none)
*
* Returns: int - actual number of characters read
*
* Errors:
*
* Note:
* reads cnt digits until non-digit char reached
*/
static int
parse_digits (char *s, int *nr, int cnt)
{
char *t = s;
int val = 0;
while (WHITESPACE (*t))
{
t++;
}
/* do not support negative numbers because... they are not supported :) */
while (*t != 0 && (cnt-- > 0) && char_isdigit (*t))
{
val = (val * 10) + (*t - '0');
t++;
}
*nr = val;
return (int) (t - s);
}
/*
* db_str_to_date ()
*
* Arguments:
* str: string from which we get the data
* format: format specifiers to match the str
* date_lang: id of language to use
* domain: expected domain of result, may be NULL; If NULL the domain
* output domain is determined according to format
*
* Returns: int
*
* Errors:
*
* Note:
* inverse function for date_format - compose a date/time from some format
* specifiers and some informations.
*/
int
db_str_to_date (const DB_VALUE * str, const DB_VALUE * format, const DB_VALUE * date_lang, DB_VALUE * result,
TP_DOMAIN * domain)
{
const char *format2_s = NULL;
char *sstr = NULL, *format_s = NULL;
int i, j, k, error_status = NO_ERROR;
int type, len1, len2, h24 = 0, _v, _x;
DB_TYPE res_type;
int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int y, m, d, h, mi, s, ms, am /* 0 = AM, 1 = PM */ ;
int u, U, v, V, dow, doy, w;
char stack_buf_str[64];
char *initial_buf_str = NULL;
bool do_free_buf_str = false;
INTL_CODESET codeset;
INTL_LANG date_lang_id;
bool dummy;
int tzm = 0, tzh = 0;
bool set_tzh = false, set_tzm = false;
int start_tzr = -1, len_tzr = -1, start_tzd = -1, len_tzd = -1;
int zone_id = -1;
bool is_negative_tzd = false;
if (str == NULL || format == NULL || date_lang == NULL)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto error;
}
db_make_null (result);
if (DB_IS_NULL (str) || DB_IS_NULL (format))
{
return NO_ERROR;
}
codeset = db_get_string_codeset (str);
assert (DB_VALUE_TYPE (date_lang) == DB_TYPE_INTEGER);
date_lang_id = lang_get_lang_id_from_flag (db_get_int (date_lang), &dummy, &dummy);
if (lang_get_specific_locale (date_lang_id, codeset) == NULL)
{
error_status = ER_LANG_CODESET_NOT_AVAILABLE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 2, lang_get_lang_name_from_id (date_lang_id),
lang_charset_name (codeset));
goto error;
}
y = m = d = V = v = U = u = -1;
h = mi = s = ms = 0;
dow = doy = am = -1;
_v = _x = 0;
error_status =
db_check_or_create_null_term_string (str, stack_buf_str, sizeof (stack_buf_str), true, true, &initial_buf_str,
&do_free_buf_str);
if (error_status != NO_ERROR)
{
goto error;
}
sstr = initial_buf_str;
format2_s = db_get_string (format);
len2 = db_get_string_size (format);
len2 = (len2 < 0) ? strlen (format2_s) : len2;
format_s = (char *) db_private_alloc (NULL, len2 + 1);
if (!format_s)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
goto error;
}
/* delete all whitespace from format */
k = 0;
for (i = 0; i < len2; i++)
{
if (!WHITESPACE (format2_s[i]))
{
format_s[k++] = format2_s[i];
}
/* '%' without format specifier */
else if (i > 0 && format2_s[i - 1] == '%')
{
format_s[k] = '\0';
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == false)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
goto error;
}
}
format_s[k] = '\0';
if (domain == NULL)
{
type = db_check_time_date_format (format_s);
if (type == TIME_SPECIFIER)
{
res_type = DB_TYPE_TIME;
}
else if (type == DATE_SPECIFIER)
{
res_type = DB_TYPE_DATE;
}
else if (type == DATETIME_SPECIFIER)
{
res_type = DB_TYPE_DATETIME;
}
else if (type == DATETIMETZ_SPECIFIER)
{
res_type = DB_TYPE_DATETIMETZ;
}
else
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto error;
}
}
else
{
res_type = TP_DOMAIN_TYPE (domain);
if (res_type != DB_TYPE_TIME && res_type != DB_TYPE_DATE && res_type != DB_TYPE_DATETIME
&& res_type != DB_TYPE_DATETIMETZ)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto error;
}
}
/*
* 1. Get information according to format specifiers
* iterate simultaneously through each string and sscanf when
* it is a format specifier.
* If a format specifier has more than one occurrence, get the last value.
*/
do
{
len1 = strlen (sstr);
len2 = strlen (format_s);
i = j = k = 0;
while (i < len1 && j < len2)
{
while (WHITESPACE (sstr[i]))
{
i++;
}
while (WHITESPACE (format_s[j]))
{
j++;
}
if (j > 0 && format_s[j - 1] == '%')
{
int token_size;
int am_pm_id;
/* do not accept a double % */
if (j > 1 && format_s[j - 2] == '%')
{
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == false)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
goto error;
}
/* we have a format specifier */
switch (format_s[j])
{
case 'a':
/* %a Abbreviated weekday name (Sun..Sat) */
error_status =
get_string_date_token_id (SDT_DAY_SHORT, date_lang_id, sstr + i, codeset, &dow, &token_size);
if (error_status != NO_ERROR)
{
goto conversion_error;
}
i += token_size;
if (dow == 0) /* not found - error */
{
goto conversion_error;
}
dow = dow - 1;
break;
case 'b':
/* %b Abbreviated month name (Jan..Dec) */
error_status =
get_string_date_token_id (SDT_MONTH_SHORT, date_lang_id, sstr + i, codeset, &m, &token_size);
if (error_status != NO_ERROR)
{
goto conversion_error;
}
i += token_size;
if (m == 0) /* not found - error */
{
goto conversion_error;
}
break;
case 'c':
/* %c Month, numeric (0..12) */
k = parse_digits (sstr + i, &m, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
break;
case 'D':
/* %D Day of the month with English suffix (0th, 1st, 2nd, 3rd, ...) */
k = parse_digits (sstr + i, &d, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
/* need 2 necessary characters or whitespace (!) after */
i += 2;
break;
case 'd':
/* %d Day of the month, numeric (00..31) */
k = parse_digits (sstr + i, &d, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
break;
case 'e':
/* %e Day of the month, numeric (0..31) */
k = parse_digits (sstr + i, &d, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
break;
case 'f':
/* %f Milliseconds (000..999) */
k = parse_digits (sstr + i, &ms, 3);
if (k <= 0)
{
goto conversion_error;
}
i += k;
break;
case 'H':
/* %H Hour (00..23) */
k = parse_digits (sstr + i, &h, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
h24 = 1;
break;
case 'h':
/* %h Hour (01..12) */
k = parse_digits (sstr + i, &h, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
break;
case 'I':
/* %I Hour (01..12) */
k = parse_digits (sstr + i, &h, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
break;
case 'i':
/* %i Minutes, numeric (00..59) */
k = parse_digits (sstr + i, &mi, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
break;
case 'j':
/* %j Day of year (001..366) */
k = parse_digits (sstr + i, &doy, 3);
if (k <= 0)
{
goto conversion_error;
}
i += k;
break;
case 'k':
/* %k Hour (0..23) */
k = parse_digits (sstr + i, &h, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
h24 = 1;
break;
case 'l':
/* %l Hour (1..12) */
k = parse_digits (sstr + i, &h, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
break;
case 'M':
/* %M Month name (January..December) */
error_status = get_string_date_token_id (SDT_MONTH, date_lang_id, sstr + i, codeset, &m, &token_size);
if (error_status != NO_ERROR)
{
goto conversion_error;
}
i += token_size;
if (m == 0) /* not found - error */
{
goto conversion_error;
}
break;
case 'm':
/* %m Month, numeric (00..12) */
k = parse_digits (sstr + i, &m, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
break;
case 'p':
/* %p AM or PM */
error_status =
get_string_date_token_id (SDT_AM_PM, date_lang_id, sstr + i, codeset, &am_pm_id, &token_size);
if (error_status != NO_ERROR)
{
goto conversion_error;
}
i += token_size;
if (am_pm_id > 0)
{
if (am_pm_id % 2)
{
am = 0;
}
else
{
am = 1;
}
}
else
{
goto conversion_error;
}
break;
case 'r':
/* %r Time, 12-hour (hh:mm:ss followed by AM or PM) */
k = parse_digits (sstr + i, &h, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
while (WHITESPACE (sstr[i]))
{
i++;
}
if (sstr[i] != ':')
{
goto conversion_error;
}
i++;
k = parse_digits (sstr + i, &mi, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
while (WHITESPACE (sstr[i]))
{
i++;
}
if (sstr[i] != ':')
{
goto conversion_error;
}
i++;
k = parse_digits (sstr + i, &s, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
error_status =
get_string_date_token_id (SDT_AM_PM, date_lang_id, sstr + i, codeset, &am_pm_id, &token_size);
if (error_status != NO_ERROR)
{
goto conversion_error;
}
i += token_size;
if (am_pm_id > 0)
{
if (am_pm_id % 2)
{
am = 0;
}
else
{
am = 1;
}
}
else
{
goto conversion_error;
}
break;
case 'S':
/* %S Seconds (00..59) */
k = parse_digits (sstr + i, &s, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
break;
case 's':
/* %s Seconds (00..59) */
k = parse_digits (sstr + i, &s, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
break;
case 'T':
/* %T Time, 24-hour (hh:mm:ss) */
if ((j + 1 < len2 && format_s[j + 1] != 'Z') || (j + 1 == len2))
{
k = parse_digits (sstr + i, &h, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
while (WHITESPACE (sstr[i]))
{
i++;
}
if (sstr[i] != ':')
{
goto conversion_error;
}
i++;
k = parse_digits (sstr + i, &mi, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
while (WHITESPACE (sstr[i]))
{
i++;
}
if (sstr[i] != ':')
{
goto conversion_error;
}
i++;
k = parse_digits (sstr + i, &s, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
h24 = 1;
}
else if (j + 2 < len2)
{
switch (format_s[j + 2])
{
case 'R':
{
start_tzr = i;
zone_id = tz_get_best_match_zone (sstr + i, &len_tzr);
if (zone_id < 0)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto error;
}
i += len_tzr;
j += 2;
}
break;
case 'D':
{
int frmt_len_spec = 0;
int tzd_exp_len = TZD_DEFAULT_EXPECTED_LEN;
int j_tzd = j + 3;
/* format TZD[number] */
if (j_tzd + frmt_len_spec < len2)
{
while (char_isdigit (format_s[j_tzd + frmt_len_spec]))
{
frmt_len_spec++;
}
if (frmt_len_spec > 0)
{
k = parse_digits (&format_s[j_tzd], &tzd_exp_len, frmt_len_spec);
if (k <= 0)
{
goto conversion_error;
}
}
}
if (tzd_exp_len <= 1 || tzd_exp_len > TZD_MAX_EXPECTED_LEN)
{
goto conversion_error;
}
start_tzd = i;
len_tzd = parse_tzd (sstr + i, tzd_exp_len);
if (len_tzd < 0)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto error;
}
i += len_tzd;
/* skip 'ZD[number]', leading 'T' is skipped by loop common code */
j += 2 + frmt_len_spec;
}
break;
case 'H':
case 'M':
{
int *p = NULL;
if (format_s[j + 2] == 'H')
{
p = &tzh;
set_tzh = true;
}
else
{
p = &tzm;
set_tzm = true;
}
/* Get the timezone hour offset */
if (sstr[i] == '+' || sstr[i] == '-')
{
if (format_s[j + 2] == 'M')
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto error;
}
if (sstr[i] == '-')
{
is_negative_tzd = true;
}
else
{
is_negative_tzd = false;
}
i++;
}
k = parse_digits (sstr + i, p, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
j += 2;
}
break;
default:
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto error;
}
}
break;
case 'U':
/* %U Week (00..53), where Sunday is the first day of the week */
k = parse_digits (sstr + i, &U, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
break;
case 'u':
/* %u Week (00..53), where Monday is the first day of the week */
k = parse_digits (sstr + i, &u, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
break;
case 'V':
/* %V Week (01..53), where Sunday is the first day of the week; used with %X */
k = parse_digits (sstr + i, &V, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
_v = 1;
break;
case 'v':
/* %v Week (01..53), where Monday is the first day of the week; used with %x */
k = parse_digits (sstr + i, &v, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
_v = 2;
break;
case 'W':
/* %W Weekday name (Sunday..Saturday) */
error_status = get_string_date_token_id (SDT_DAY, date_lang_id, sstr + i, codeset, &dow, &token_size);
if (error_status != NO_ERROR)
{
goto conversion_error;
}
i += token_size;
if (dow == 0) /* not found - error */
{
goto conversion_error;
}
dow = dow - 1;
break;
case 'w':
/* %w Day of the week (0=Sunday..6=Saturday) */
k = parse_digits (sstr + i, &dow, 1);
if (k <= 0)
{
goto conversion_error;
}
i += k;
break;
case 'X':
/* %X Year for the week where Sunday is the first day of the week, numeric, four digits; used with %V
*/
k = parse_digits (sstr + i, &y, 4);
if (k <= 0)
{
goto conversion_error;
}
i += k;
_x = 1;
break;
case 'x':
/* %x Year for the week, where Monday is the first day of the week, numeric, four digits; used with
* %v */
k = parse_digits (sstr + i, &y, 4);
if (k <= 0)
{
goto conversion_error;
}
i += k;
_x = 2;
break;
case 'Y':
/* %Y Year, numeric, four digits */
k = parse_digits (sstr + i, &y, 4);
if (k <= 0)
{
goto conversion_error;
}
i += k;
break;
case 'y':
/* %y Year, numeric (two digits) */
k = parse_digits (sstr + i, &y, 2);
if (k <= 0)
{
goto conversion_error;
}
i += k;
/* TODO: 70 convention always available? */
if (y < 70)
{
y = 2000 + y;
}
else
{
y = 1900 + y;
}
break;
default:
goto conversion_error;
break;
}
}
else if (sstr[i] != format_s[j] && format_s[j] != '%')
{
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS) == false)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
goto error;
}
else if (format_s[j] != '%')
{
i++;
}
/* when is a format specifier do not advance in sstr because we need the entire value */
j++;
}
}
while (0);
/* 2. Validations */
if (len_tzd > 0 || len_tzr > 0)
{
if (set_tzh == true || set_tzm == true)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto error;
}
}
if (is_negative_tzd)
{
tzh = -tzh;
tzm = -tzm;
}
if (am != -1) /* 24h time format and am/pm */
{
if (h24 == 1 || h == 0)
{
goto conversion_error;
}
if (h == 12)
{
h = 0;
}
}
if (h24 == 0 && h > 12)
{
goto conversion_error;
}
if (_x != _v && _x != -1) /* accept %v only if %x and %V only if %X */
{
goto conversion_error;
}
days[2] += LEAP (y);
/*
* validations are done here because they are done just on the last memorized
* values (ie: if you supply a month 99 then a month 12 the 99 isn't validated
* because it's overwritten by 12 which is correct).
*/
/*
* check only upper bounds, lower bounds will be checked later and
* will return error
*/
if (res_type == DB_TYPE_DATE || res_type == DB_TYPE_DATETIME || res_type == DB_TYPE_DATETIMETZ)
{
/* replace invalid initial year with default year */
y = (y == -1) ? 1 : y;
/* year is validated because it's vital for m & d */
if (y > 9999)
{
goto conversion_error;
}
if (m > 12)
{
goto conversion_error;
}
/* because we do not support invalid dates ... */
if (m != -1 && d > days[m])
{
goto conversion_error;
}
if (u > 53)
{
goto conversion_error;
}
if (v > 53)
{
goto conversion_error;
}
if (v == 0 || u > 53)
{
goto conversion_error;
}
if (V == 0 || u > 53)
{
goto conversion_error;
}
if (doy == 0 || doy > 365 + LEAP (y))
{
goto conversion_error;
}
if (dow > 6)
{
goto conversion_error;
}
}
if (res_type == DB_TYPE_TIME || res_type == DB_TYPE_DATETIME || res_type == DB_TYPE_DATETIMETZ)
{
if ((am != -1 && h > 12) || (am == -1 && h > 23))
{
goto conversion_error;
}
if (am == 1 && h != -1)
{
h += 12;
/* reset AM flag */
am = -1;
}
if (mi > 59)
{
goto conversion_error;
}
if (s > 59)
{
goto conversion_error;
}
/* mili does not need checking, it has all values from 0 to 999 */
}
/* 3. Try to compute a date according to the information from the format specifiers */
if (res_type == DB_TYPE_TIME)
{
/* --- no job to do --- */
goto write_results;
}
/* the year is fixed, compute the day and month from dow, doy, etc */
/*
* the day and month can be supplied specifically which supress all other
* informations or can be computed from dow and week or from doy
*/
/* 3.1 - we have a valid day and month */
if (m >= 1 && m <= 12 && d >= 1 && d <= days[m])
{
/* --- no job to do --- */
goto write_results;
}
w = MAX (v, MAX (V, MAX (u, U)));
/* 3.2 - we have the day of week and a week */
if (dow != -1 && w != -1)
{
int dow2 = db_get_day_of_week (y, 1, 1);
int ld_fw, save_dow, dowdiff;
if (U == w || V == w)
{
ld_fw = 7 - dow2;
if (w == 0)
{
dowdiff = dow - dow2;
d = dow2 == 0 ? 32 - (7 - dow) : dowdiff < 0 ? 32 + dowdiff : 1 + dowdiff;
m = dow2 == 0 || dowdiff < 0 ? 12 : 1;
y = dow2 == 0 || dowdiff < 0 ? y - 1 : y;
}
else
{
d = dow2 == 0 ? 1 : ld_fw + 1;
m = 1;
if (db_add_weeks_and_days_to_date (&d, &m, &y, w - 1, dow) == ER_FAILED)
{
goto conversion_error;
}
}
}
else if (u == w || v == w)
{
ld_fw = dow2 == 0 ? 1 : 7 - dow2 + 1;
if (w == 0 || w == 1)
{
save_dow = dow;
dow = dow == 0 ? 7 : dow;
dow2 = dow2 == 0 ? 7 : dow2;
dowdiff = dow - dow2;
if (dow2 >= 1 && dow2 <= 4) /* start with week 1 */
{
d = w == 0 ? 32 + dowdiff - 7 : dowdiff < 0 ? 32 + dowdiff : 1 + dowdiff;
m = w == 0 || dowdiff < 0 ? 12 : 1;
y = w == 0 || dowdiff < 0 ? y - 1 : y;
}
else
{
d = dowdiff < 0 ? (w == 0 ? 32 + dowdiff : ld_fw + dow) : (w == 0 ? 1 + dowdiff : 1 + dowdiff + 7);
m = dowdiff < 0 && w == 0 ? 12 : 1;
y = dowdiff < 0 && w == 0 ? y - 1 : y;
}
dow = save_dow;
}
else
{
d = ld_fw + 1;
m = 1;
if (db_add_weeks_and_days_to_date (&d, &m, &y, dow2 >= 1 && dow2 <= 4 ? w - 2 : w - 1,
dow == 0 ? 6 : dow - 1) == ER_FAILED)
{
goto conversion_error;
}
}
}
else
{
goto conversion_error; /* should not happen */
}
}
/* 3.3 - we have the day of year */
else if (doy != -1)
{
for (m = 1; doy > days[m] && m <= 12; m++)
{
doy -= days[m];
}
d = doy;
}
write_results:
/* last validations before writing results - we need only complete data info */
if (res_type == DB_TYPE_DATE || res_type == DB_TYPE_DATETIME || res_type == DB_TYPE_DATETIMETZ)
{
/* replace invalid initial date (-1,-1,-1) with default date (1,1,1) */
y = (y == -1) ? 1 : y;
m = (m == -1) ? 1 : m;
d = (d == -1) ? 1 : d;
if (y < 0 || m < 0 || d < 0)
{
goto conversion_error;
}
if (d > days[m])
{
goto conversion_error;
}
}
if (res_type == DB_TYPE_TIME || res_type == DB_TYPE_DATETIME || res_type == DB_TYPE_DATETIMETZ)
{
if (h < 0 || mi < 0 || s < 0)
{
goto conversion_error;
}
}
if (res_type == DB_TYPE_DATE)
{
db_make_date (result, m, d, y);
}
else if (res_type == DB_TYPE_TIME)
{
db_make_time (result, h, mi, s);
}
else if (res_type == DB_TYPE_DATETIME)
{
DB_DATETIME db_datetime;
db_datetime_encode (&db_datetime, m, d, y, h, mi, s, ms);
db_make_datetime (result, &db_datetime);
}
else if (res_type == DB_TYPE_DATETIMETZ)
{
DB_DATETIME db_datetime;
DB_DATETIMETZ db_datetimetz;
db_datetime_encode (&db_datetime, m, d, y, h, mi, s, ms);
if (set_tzh == true || set_tzm == true)
{
error_status = tz_create_datetimetz_from_offset (&db_datetime, tzh, tzm, &db_datetimetz);
if (error_status != NO_ERROR)
{
goto conversion_error;
}
}
else
{
const char *dst = NULL;
TZ_REGION session_tz_region;
if (len_tzd > 0)
{
assert (start_tzd >= 0);
dst = sstr + start_tzd;
}
tz_get_session_tz_region (&session_tz_region);
error_status = tz_create_datetimetz_from_zoneid_and_tzd (&db_datetime, &session_tz_region, zone_id, dst,
len_tzd, false, &db_datetimetz);
if (error_status != NO_ERROR)
{
goto conversion_error;
}
}
db_make_datetimetz (result, &db_datetimetz);
}
error:
if (format_s)
{
db_private_free_and_init (NULL, format_s);
}
if (do_free_buf_str)
{
assert (initial_buf_str != NULL);
db_private_free (NULL, initial_buf_str);
}
return error_status;
conversion_error:
if (do_free_buf_str)
{
assert (initial_buf_str != NULL);
db_private_free (NULL, initial_buf_str);
}
if (format_s)
{
db_private_free_and_init (NULL, format_s);
}
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_clear ();
error_status = NO_ERROR;
}
else
{
error_status = ER_DATE_CONVERSION;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
db_make_null (result);
return error_status;
}
/*
* db_time_dbval () - extract the time from input parameter.
* return: NO_ERROR, or error code
* result(out) : resultant db_value
* datetime_value(in) : time, timestamp or datetime expression
* domain(in): result domain
*/
int
db_time_dbval (DB_VALUE * result, const DB_VALUE * datetime_value, const TP_DOMAIN * domain)
{
DB_TYPE type;
char *res_s;
int hour = 0, min = 0, sec = 0, milisec = 0;
int size;
if (DB_IS_NULL (datetime_value))
{
db_make_null (result);
return NO_ERROR;
}
type = DB_VALUE_TYPE (datetime_value);
if (db_get_time_from_dbvalue (datetime_value, &hour, &min, &sec, &milisec) != NO_ERROR)
{
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_clear ();
return NO_ERROR;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIME_CONVERSION, 0);
return ER_TIME_CONVERSION;
}
if (milisec != 0)
{
size = 12 + 1; /* HH:MM:SS.MMM */
}
else
{
size = 8 + 1; /* HH:MM:SS */
}
res_s = (char *) db_private_alloc (NULL, size);
if (res_s == NULL)
{
return ER_OUT_OF_VIRTUAL_MEMORY;
}
if (milisec != 0)
{
sprintf (res_s, "%02d:%02d:%02d.%03d", hour, min, sec, milisec);
}
else
{
sprintf (res_s, "%02d:%02d:%02d", hour, min, sec);
}
switch (type)
{
case DB_TYPE_VARCHAR:
case DB_TYPE_CHAR:
db_make_varchar (result, TP_FLOATING_PRECISION_VALUE, res_s, strlen (res_s),
db_get_string_codeset (datetime_value), db_get_string_collation (datetime_value));
break;
default:
db_make_string (result, res_s);
break;
}
if (domain != NULL)
{
assert (TP_DOMAIN_TYPE (domain) == DB_VALUE_TYPE (result));
db_string_put_cs_and_collation (result, TP_DOMAIN_CODESET (domain), TP_DOMAIN_COLLATION (domain));
}
result->need_clear = true;
return NO_ERROR;
}
/*
* db_date_dbval () - extract the date from input parameter.
* return: NO_ERROR, or ER_code
* result(out) : resultant db_value
* date_value(in) : date or datetime expression
* domain: domain of result
*/
int
db_date_dbval (DB_VALUE * result, const DB_VALUE * date_value, const TP_DOMAIN * domain)
{
char *res_s;
int y, m, d, hour, min, sec, ms;
int error_status = NO_ERROR;
INTL_CODESET codeset;
int collation_id;
if (date_value == NULL || result == NULL)
{
return ER_FAILED;
}
y = m = d = 0;
if ((DB_VALUE_DOMAIN_TYPE (date_value) == DB_TYPE_NULL) || DB_IS_NULL (date_value))
{
db_make_null (result);
return NO_ERROR;
}
if (db_get_datetime_from_dbvalue (date_value, &y, &m, &d, &hour, &min, &sec, &ms, NULL) != NO_ERROR)
{
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_clear ();
return NO_ERROR;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
return ER_DATE_CONVERSION;
}
res_s = (char *) db_private_alloc (NULL, 10 + 1); /* MM/DD/YYYY */
if (res_s == NULL)
{
return ER_OUT_OF_VIRTUAL_MEMORY;
}
sprintf (res_s, "%02d/%02d/%04d", m, d, y);
if (domain != NULL)
{
codeset = TP_DOMAIN_CODESET (domain);
collation_id = TP_DOMAIN_COLLATION (domain);
}
else if (TP_IS_STRING_TYPE (DB_VALUE_TYPE (date_value)))
{
codeset = db_get_string_codeset (date_value);
collation_id = db_get_string_collation (date_value);
}
else
{
codeset = LANG_SYS_CODESET;
collation_id = LANG_SYS_COLLATION;
}
db_make_string (result, res_s);
db_string_put_cs_and_collation (result, codeset, collation_id);
result->need_clear = true;
return error_status;
}
/*
* count_leap_years_up_to - count the leap years up to year
* return: the counted value
* year(in) : the last year to evaluate
*/
int
count_leap_years_up_to (int year)
{
return (year / 4 - year / 100 + year / 400);
}
/*
* count_nonleap_years_up_to - count the non leap years up to year
* return: the counted value
* year(in) : the last year to evaluate
*/
int
count_nonleap_years_up_to (int year)
{
return (year - count_leap_years_up_to (year));
}
/*
* db_date_diff () - expr1 ?? expr2 expressed as a value in days from
* one date to the other.
* return: int
* result(out) : resultant db_value
* date_value1(in) : first date
* date_value2(in) : second date
*/
int
db_date_diff (const DB_VALUE * date_value1, const DB_VALUE * date_value2, DB_VALUE * result)
{
DB_TYPE type1, type2;
int y1 = 0, m1 = 0, d1 = 0;
int y2 = 0, m2 = 0, d2 = 0;
int hour, min, sec, ms;
int cly1, cly2, cnly1, cnly2, cdpm1, cdpm2, cdpy1, cdpy2, diff, i, cd1, cd2;
int m_days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int error_status = NO_ERROR;
int retval;
if (date_value1 == NULL || date_value2 == NULL || result == NULL)
{
error_status = ER_FAILED;
goto error;
}
type1 = DB_VALUE_DOMAIN_TYPE (date_value1);
if (type1 == DB_TYPE_NULL || DB_IS_NULL (date_value1))
{
db_make_null (result);
goto error;
}
type2 = DB_VALUE_DOMAIN_TYPE (date_value2);
if (type2 == DB_TYPE_NULL || DB_IS_NULL (date_value2))
{
db_make_null (result);
goto error;
}
retval = db_get_datetime_from_dbvalue (date_value1, &y1, &m1, &d1, &hour, &min, &sec, &ms, NULL);
if (retval != NO_ERROR)
{
error_status = ER_DATE_CONVERSION;
db_make_null (result);
goto error;
}
retval = db_get_datetime_from_dbvalue (date_value2, &y2, &m2, &d2, &hour, &min, &sec, &ms, NULL);
if (retval != NO_ERROR)
{
error_status = ER_DATE_CONVERSION;
db_make_null (result);
goto error;
}
if ((y1 == 0 && m1 == 0 && d1 == 0 && hour == 0 && min == 0 && sec == 0 && ms == 0)
|| (y2 == 0 && m2 == 0 && d2 == 0 && hour == 0 && min == 0 && sec == 0 && ms == 0))
{
er_clear ();
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
return NO_ERROR;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ATTEMPT_TO_USE_ZERODATE, 0);
return ER_ATTEMPT_TO_USE_ZERODATE;
}
cly1 = count_leap_years_up_to (y1 - 1);
cnly1 = count_nonleap_years_up_to (y1 - 1);
cdpy1 = cly1 * 366 + cnly1 * 365;
m_days[2] = LEAP (y1) ? 29 : 28;
cdpm1 = 0;
for (i = 1; i < m1; i++)
{
cdpm1 += m_days[i];
}
cly2 = count_leap_years_up_to (y2 - 1);
cnly2 = count_nonleap_years_up_to (y2 - 1);
cdpy2 = cly2 * 366 + cnly2 * 365;
m_days[2] = LEAP (y2) ? 29 : 28;
cdpm2 = 0;
for (i = 1; i < m2; i++)
{
cdpm2 += m_days[i];
}
cd1 = cdpy1 + cdpm1 + d1;
cd2 = cdpy2 + cdpm2 + d2;
diff = cd1 - cd2;
db_make_int (result, diff);
error:
return error_status;
}
int
db_from_unixtime (const DB_VALUE * src_value, const DB_VALUE * format, const DB_VALUE * date_lang,
DB_VALUE * result, const TP_DOMAIN * domain)
{
time_t unix_timestamp;
DB_TYPE format_type;
int error_status = NO_ERROR;
assert (src_value != NULL);
if (DB_IS_NULL (src_value))
{
db_make_null (result);
return NO_ERROR;
}
if (DB_VALUE_TYPE (src_value) != DB_TYPE_INTEGER)
{
error_status = ER_TIMESTAMP_CONVERSION;
goto error;
}
unix_timestamp = db_get_int (src_value);
if (unix_timestamp < 0)
{
error_status = ER_TIMESTAMP_CONVERSION;
goto error;
}
if (format == NULL)
{
/* if unix_timestamp is called without a format argument, return the timestamp */
db_make_timestamp (result, (DB_TIMESTAMP) unix_timestamp);
return NO_ERROR;
}
if (DB_IS_NULL (format))
{
db_make_null (result);
return NO_ERROR;
}
format_type = DB_VALUE_TYPE (format);
switch (format_type)
{
case DB_TYPE_VARCHAR:
case DB_TYPE_CHAR:
{
DB_VALUE ts_val;
DB_VALUE default_date_lang;
db_make_timestamp (&ts_val, (DB_TIMESTAMP) unix_timestamp);
if (date_lang == NULL || DB_IS_NULL (date_lang))
{
/* use date_lang for en_US */
db_make_int (&default_date_lang, 0);
date_lang = &default_date_lang;
}
error_status = db_date_format (&ts_val, format, date_lang, result, domain);
if (error_status != NO_ERROR)
{
goto error;
}
return NO_ERROR;
}
default:
error_status = ER_TIMESTAMP_CONVERSION;
goto error;
}
error:
db_make_null (result);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_clear ();
return NO_ERROR;
}
if (er_errid () == NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
return error_status;
}
/*
* db_time_diff () - return the difference between TIME values val1 and val2,
* expressed as a TIME value
* return: NO_ERROR or error code
* result(out) : resultant db_value
* val1(in) : first date/time value
* val2(in) : second date/time value
*/
int
db_time_diff (const DB_VALUE * val1, const DB_VALUE * val2, DB_VALUE * result)
{
int y1 = 0, m1 = 0, d1 = 0, hour1 = 0, min1 = 0, sec1 = 0;
int y2 = 0, m2 = 0, d2 = 0, hour2 = 0, min2 = 0, sec2 = 0;
int error_status = NO_ERROR;
int leap_years1, leap_years2, days_this_year1, days_this_year2;
int total_days1, total_days2;
int total_seconds1, total_seconds2, time_diff, date_diff = 0;
int min_res, sec_res, hour_res;
int ret_int, ms;
DB_TYPE val1_type = DB_TYPE_TIME, val2_type = DB_TYPE_TIME;
int hour_aux, min_aux, sec_aux, ms_aux;
assert (val1 != NULL);
assert (val2 != NULL);
if (DB_IS_NULL (val1) || DB_IS_NULL (val2))
{
db_make_null (result);
return NO_ERROR;
}
if (DB_VALUE_DOMAIN_TYPE (val1) != DB_VALUE_DOMAIN_TYPE (val2))
{
error_status = ER_QPROC_INVALID_PARAMETER;
goto error;
}
/* get date/time information from val1 */
if (db_get_time_from_dbvalue (val1, &hour1, &min1, &sec1, &ms) == NO_ERROR)
{
if (db_get_datetime_from_dbvalue (val1, &y1, &m1, &d1, &hour_aux, &min_aux, &sec_aux, &ms_aux, NULL) == NO_ERROR)
{
if (hour_aux != hour1 || min_aux != min1 || sec_aux != sec1)
{
y1 = 0;
m1 = 0;
d1 = 0;
}
else
{
val1_type = DB_TYPE_DATETIME;
}
}
else
{
/* Extracted time is used. Make sure error is not leaked. */
er_clear ();
}
}
else
{
/* val1 may be Date type, try it here */
if (db_get_datetime_from_dbvalue (val1, &y1, &m1, &d1, &hour_aux, &min_aux, &sec_aux, &ms_aux, NULL) == NO_ERROR)
{
val1_type = DB_TYPE_DATE;
/* Extracted time is used. Make sure error is not leaked. */
er_clear ();
}
else
{
error_status = ER_TIME_CONVERSION;
goto error;
}
}
/* get date/time information from val2 */
if (db_get_time_from_dbvalue (val2, &hour2, &min2, &sec2, &ms) == NO_ERROR)
{
if (db_get_datetime_from_dbvalue (val2, &y2, &m2, &d2, &hour_aux, &min_aux, &sec_aux, &ms_aux, NULL) == NO_ERROR)
{
if (hour_aux != hour2 || min_aux != min2 || sec_aux != sec2)
{
y2 = 0;
m2 = 0;
d2 = 0;
}
else
{
val2_type = DB_TYPE_DATETIME;
}
}
else
{
/* Extracted time is used. Make sure error is not leaked. */
er_clear ();
}
}
else
{
/* val2 may be Date type, try it here */
if (db_get_datetime_from_dbvalue (val2, &y2, &m2, &d2, &hour_aux, &min_aux, &sec_aux, &ms_aux, NULL) == NO_ERROR)
{
/* Extracted time is used. Make sure error is not leaked. */
er_clear ();
val2_type = DB_TYPE_DATE;
}
else
{
error_status = ER_TIME_CONVERSION;
goto error;
}
}
if (val1_type != val2_type)
{
error_status = ER_QPROC_INVALID_PARAMETER;
goto error;
}
if (val1_type != DB_TYPE_TIME)
{
/* convert dates to days */
leap_years1 = count_leap_years_up_to (y1 - 1);
days_this_year1 = db_get_day_of_year (y1, m1, d1);
total_days1 = y1 * 365 + leap_years1 + days_this_year1;
leap_years2 = count_leap_years_up_to (y2 - 1);
days_this_year2 = db_get_day_of_year (y2, m2, d2);
total_days2 = y2 * 365 + leap_years2 + days_this_year2;
date_diff = total_days1 - total_days2;
}
total_seconds1 = sec1 + min1 * 60 + hour1 * 3600;
total_seconds2 = sec2 + min2 * 60 + hour2 * 3600;
time_diff = total_seconds1 - total_seconds2;
date_diff = date_diff * 3600 * 24 + time_diff;
hour_res = (date_diff / 3600);
min_res = (date_diff % 3600) / 60;
sec_res = date_diff - 3600 * hour_res - 60 * min_res;
db_make_time (result, hour_res, min_res, sec_res);
ret_int = (int) *(db_get_time (result));
/* check time overflow on result */
if (ret_int < 0)
{
error_status = ER_TIME_CONVERSION;
goto error;
}
return NO_ERROR;
error:
db_make_null (result);
er_clear ();
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
return NO_ERROR;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
/*
* parse_time_string - parse a string given by the second argument of
* timestamp function
* return: NO_ERROR
*
* timestr(in) : input string
* timestr_size(in): input string size
* sign(out) : 0 if positive, -1 if negative
* h(out) : hours
* m(out) : minutes
* s(out) : seconds
* ms(out) : milliseconds
*/
static int
parse_time_string (const char *timestr, int timestr_size, int *sign, int *h, int *m, int *s, int *ms)
{
int args[4], num_args = 0, tmp;
const char *ch;
const char *dot = NULL, *end;
assert (sign != NULL && h != NULL && m != NULL && s != NULL && ms != NULL);
*sign = *h = *m = *s = *ms = 0;
if (!timestr || !timestr_size)
{
return NO_ERROR;
}
ch = timestr;
end = timestr + timestr_size;
SKIP_SPACES (ch, end);
if (*ch == '-')
{
*sign = 1;
ch++;
}
/* Find dot('.') to separate milli-seconds part from whole string. */
dot = ch;
while (dot != end && *dot != '.')
{
dot++;
}
if (dot != end)
{
char ms_string[4];
dot++;
tmp = CAST_BUFLEN (end - dot);
if (tmp)
{
tmp = (tmp < 3 ? tmp : 3);
strncpy (ms_string, dot, tmp);
}
ms_string[3] = '\0';
switch (tmp)
{
case 0:
*ms = 0;
break;
case 1:
ms_string[1] = '0';
[[fallthrough]];
case 2:
ms_string[2] = '0';
[[fallthrough]];
default:
*ms = atoi (ms_string);
}
}
/* First ':' character means '0:'. */
SKIP_SPACES (ch, end);
if (ch != end && *ch == ':')
{
args[num_args++] = 0;
ch++;
}
if (ch != end)
{
while (num_args < (int) (sizeof (args) / sizeof (*args)) && char_isdigit (*ch))
{
tmp = 0;
do
{
/* check for overflow */
if (tmp >= INT_MAX / 10)
{
tmp = INT_MAX;
}
else
{
tmp = tmp * 10 + *ch - '0';
}
ch++;
}
while (ch != end && char_isdigit (*ch));
args[num_args++] = tmp;
/* Digits should be separated by ':' character. If we meet other characters, stop parsing. */
if (ch == end || *ch != ':')
{
break;
}
ch++;
}
}
switch (num_args)
{
case 1:
/* Consider single value as H...HMMSS. */
*s = args[0] % 100;
args[0] /= 100;
*m = args[0] % 100;
*h = args[0] / 100;
break;
case 2:
*h = args[0];
*m = args[1];
break;
case 3:
*h = args[0];
*m = args[1];
*s = args[2];
break;
case 0:
default:
/* do nothing */
;
}
return NO_ERROR;
}
/*
* db_bit_to_blob - convert bit string value to blob value
* return: NO_ERROR or error code
* src_value(in): bit string value
* result_value(out): blob value
*/
int
db_bit_to_blob (const DB_VALUE * src_value, DB_VALUE * result_value)
{
DB_TYPE src_type;
int error_status = NO_ERROR;
DB_ELO *elo;
const char *src_str;
int src_length = 0;
assert (src_value != NULL && result_value != NULL);
src_type = DB_VALUE_DOMAIN_TYPE (src_value);
if (src_type == DB_TYPE_NULL)
{
db_make_null (result_value);
return NO_ERROR;
}
else if (QSTR_IS_BIT (src_type))
{
error_status = db_create_fbo (result_value, DB_TYPE_BLOB);
if (error_status == NO_ERROR)
{
elo = db_get_elo (result_value);
src_str = db_get_bit (src_value, &src_length);
if (src_length > 0)
{
error_status = db_elo_write (elo, 0, src_str, QSTR_NUM_BYTES (src_length), NULL);
}
}
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
return error_status;
}
/*
* db_char_to_blob - convert char string value to blob value
* return: NO_ERROR or error code
* src_value(in): char string value
* result_value(out): blob value
*/
int
db_char_to_blob (const DB_VALUE * src_value, DB_VALUE * result_value)
{
DB_TYPE src_type;
int error_status = NO_ERROR;
DB_ELO *elo;
const char *src_str;
int src_size;
assert (src_value != NULL && result_value != NULL);
src_type = DB_VALUE_DOMAIN_TYPE (src_value);
if (src_type == DB_TYPE_NULL)
{
db_make_null (result_value);
return NO_ERROR;
}
if (QSTR_IS_ANY_CHAR (src_type))
{
error_status = db_create_fbo (result_value, DB_TYPE_BLOB);
if (error_status == NO_ERROR)
{
elo = db_get_elo (result_value);
src_str = db_get_string (src_value);
src_size = db_get_string_size (src_value);
if (src_size > 0)
{
error_status = db_elo_write (elo, 0, src_str, src_size, NULL);
}
}
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
return error_status;
}
/*
* db_blob_to_bit - convert blob value to bit string value
* return: NO_ERROR or error code
* src_value(in): blob value
* length_value(in): the length to convert
* result_value(out): bit string value
*/
int
db_blob_to_bit (const DB_VALUE * src_value, const DB_VALUE * length_value, DB_VALUE * result_value)
{
int error_status = NO_ERROR;
DB_TYPE src_type, length_type;
int max_length;
assert (src_value != NULL && result_value != NULL);
src_type = DB_VALUE_DOMAIN_TYPE (src_value);
if (length_value == NULL || DB_VALUE_TYPE (length_value) == DB_TYPE_NULL)
{
length_type = DB_TYPE_INTEGER;
max_length = -1;
}
else
{
length_type = DB_VALUE_DOMAIN_TYPE (length_value);
max_length = db_get_int (length_value);
}
if (src_type == DB_TYPE_NULL)
{
db_make_null (result_value);
return NO_ERROR;
}
if (src_type == DB_TYPE_BLOB && length_type == DB_TYPE_INTEGER)
{
error_status = lob_to_bit_char (src_value, result_value, DB_TYPE_BLOB, max_length);
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
return error_status;
}
/*
* db_blob_from_file - construct blob value from the file (char string literal)
* return: NO_ERROR or error code
* src_value(in): char string literal (file path)
* result_value(out): blob value
*/
int
db_blob_from_file (const DB_VALUE * src_value, DB_VALUE * result_value)
{
DB_TYPE src_type;
int error_status = NO_ERROR;
char path_buf[PATH_MAX + 1]; /* reserve buffer for '\0' */
const char *default_prefix = ES_LOCAL_PATH_PREFIX;
assert (src_value != NULL && result_value != NULL);
path_buf[0] = '\0';
src_type = DB_VALUE_DOMAIN_TYPE (src_value);
if (src_type == DB_TYPE_NULL)
{
db_make_null (result_value);
return NO_ERROR;
}
if (QSTR_IS_CHAR (src_type))
{
int path_buf_len = 0;
int src_size = db_get_string_size (src_value);
src_size = (src_size < 0) ? strlen (db_get_string (src_value)) : src_size;
if (db_get_string_size (src_value) == 0)
{
error_status = ER_QSTR_EMPTY_STRING;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
if (es_get_type (db_get_string (src_value)) == ES_NONE)
{
/* Set default prefix, if no valid prefix was set. */
strcpy (path_buf, default_prefix);
path_buf_len = strlen (path_buf);
}
strncat (path_buf, db_get_string (src_value), MIN (src_size, PATH_MAX - path_buf_len));
path_buf[path_buf_len + MIN (src_size, PATH_MAX - path_buf_len)] = '\0';
error_status = lob_from_file (path_buf, src_value, result_value, DB_TYPE_BLOB);
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
return error_status;
}
/*
* db_blob_length - get the length of blob value
* return: NO_ERROR or error code
* src_value(in): blob value
* result_value(out): bigint value
*/
int
db_blob_length (const DB_VALUE * src_value, DB_VALUE * result_value)
{
DB_TYPE src_type;
int error_status = NO_ERROR;
assert (src_value != NULL && result_value != NULL);
src_type = DB_VALUE_DOMAIN_TYPE (src_value);
if (src_type == DB_TYPE_NULL)
{
db_make_null (result_value);
return NO_ERROR;
}
if (src_type == DB_TYPE_BLOB)
{
error_status = lob_length (src_value, result_value);
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
return error_status;
}
/*
* db_char_to_clob - convert char string value to clob value
* return: NO_ERROR or error code
* src_value(in): char string value
* result_value(out): clob value
*/
int
db_char_to_clob (const DB_VALUE * src_value, DB_VALUE * result_value)
{
DB_TYPE src_type;
int error_status = NO_ERROR;
DB_ELO *elo;
const char *src_str;
int src_size;
assert (src_value != NULL && result_value != NULL);
src_type = DB_VALUE_DOMAIN_TYPE (src_value);
if (src_type == DB_TYPE_NULL)
{
db_make_null (result_value);
return NO_ERROR;
}
if (QSTR_IS_ANY_CHAR (src_type))
{
error_status = db_create_fbo (result_value, DB_TYPE_CLOB);
if (error_status == NO_ERROR)
{
elo = db_get_elo (result_value);
src_str = db_get_string (src_value);
src_size = db_get_string_size (src_value);
if (src_size > 0)
{
error_status = db_elo_write (elo, 0, src_str, src_size, NULL);
}
}
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
return error_status;
}
/*
* db_clob_to_char - convert clob value to char string value
* return: NO_ERROR or error code
* src_value(in): clob value
* codeset_value(in): the codeset of output string
* result_value(out): char string value
*/
int
db_clob_to_char (const DB_VALUE * src_value, const DB_VALUE * codeset_value, DB_VALUE * result_value)
{
int error_status = NO_ERROR;
DB_TYPE src_type;
int max_length;
int cs = LANG_SYS_CODESET;
assert (src_value != NULL && result_value != NULL);
if (codeset_value != NULL)
{
assert (DB_VALUE_DOMAIN_TYPE (codeset_value) == DB_TYPE_INTEGER);
cs = db_get_int (codeset_value);
if (cs != INTL_CODESET_UTF8 && cs != INTL_CODESET_ISO88591 && cs != INTL_CODESET_KSC5601_EUC)
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
}
src_type = DB_VALUE_DOMAIN_TYPE (src_value);
max_length = -1;
if (src_type == DB_TYPE_NULL)
{
db_make_null (result_value);
return NO_ERROR;
}
if (src_type == DB_TYPE_CLOB)
{
error_status = lob_to_bit_char (src_value, result_value, DB_TYPE_CLOB, max_length);
if (result_value != NULL && DB_VALUE_DOMAIN_TYPE (result_value) == DB_TYPE_VARCHAR)
{
db_string_put_cs_and_collation (result_value, cs, LANG_GET_BINARY_COLLATION (cs));
}
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
return error_status;
}
/*
* db_clob_from_file - construct clob value from the file (char string literal)
* return: NO_ERROR or error code
* src_value(in): char string literal (file path)
* result_value(out): clob value
*/
int
db_clob_from_file (const DB_VALUE * src_value, DB_VALUE * result_value)
{
DB_TYPE src_type;
int error_status = NO_ERROR;
char path_buf[PATH_MAX + 1]; /* reserve buffer for '\0' */
const char *default_prefix = ES_LOCAL_PATH_PREFIX;
assert (src_value != (DB_VALUE *) NULL);
path_buf[0] = '\0';
src_type = DB_VALUE_DOMAIN_TYPE (src_value);
if (src_type == DB_TYPE_NULL)
{
db_make_null (result_value);
return NO_ERROR;
}
if (QSTR_IS_CHAR (src_type))
{
int path_buf_len = 0;
int src_size = db_get_string_size (src_value);
src_size = (src_size < 0) ? strlen (db_get_string (src_value)) : src_size;
if (db_get_string_size (src_value) == 0)
{
error_status = ER_QSTR_EMPTY_STRING;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
if (es_get_type (db_get_string (src_value)) == ES_NONE)
{
/* Set default prefix, if no valid prefix was set. */
strcpy (path_buf, default_prefix);
path_buf_len = strlen (path_buf);
}
strncat (path_buf, db_get_string (src_value), MIN (src_size, PATH_MAX - path_buf_len));
path_buf[path_buf_len + MIN (src_size, PATH_MAX - path_buf_len)] = '\0';
error_status = lob_from_file (path_buf, src_value, result_value, DB_TYPE_CLOB);
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
return error_status;
}
/*
* db_clob_length - get the length of clob value
* return: NO_ERROR or error code
* src_value(in): clob value
* result_value(out): bigint value
*/
int
db_clob_length (const DB_VALUE * src_value, DB_VALUE * result_value)
{
DB_TYPE src_type;
int error_status = NO_ERROR;
assert (src_value != NULL && result_value != NULL);
src_type = DB_VALUE_DOMAIN_TYPE (src_value);
if (src_type == DB_TYPE_NULL)
{
db_make_null (result_value);
return NO_ERROR;
}
if (src_type == DB_TYPE_CLOB)
{
error_status = lob_length (src_value, result_value);
}
else
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
return error_status;
}
/*
* db_get_datetime_from_dbvalue () - splits a generic DB_VALUE to
* year, month, day, hour, minute, second
* Arguments:
* src_date(in) : db_value to split
* year(out) : year
* month(out) : month
* day(out) : day
* hour(out) : hour
* minute(out) : minute
* second(out) : second
* millisecond(out) : millisecond
* endp(out) : end pointer into src_date after parsing string
* Returns: int
* Note: Callers should not use the global error mechanism.
* This function returns ER_FAILED without setting the global error info
*/
int
db_get_datetime_from_dbvalue (const DB_VALUE * src_date, int *year, int *month, int *day, int *hour, int *minute,
int *second, int *millisecond, const char **endp)
{
DB_TYPE arg_type = DB_TYPE_UNKNOWN;
if (DB_IS_NULL (src_date))
{
/* return error if src_date is null */
return ER_FAILED;
}
arg_type = DB_VALUE_DOMAIN_TYPE (src_date);
switch (arg_type)
{
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
{
DB_DATETIME db_datetime;
int str_len;
const char *strp;
strp = db_get_string (src_date);
str_len = db_get_string_size (src_date);
if (db_date_parse_datetime_parts (strp, str_len, &db_datetime, NULL, NULL, NULL, endp) != NO_ERROR)
{
return ER_FAILED;
}
return db_datetime_decode (&db_datetime, month, day, year, hour, minute, second, millisecond);
}
case DB_TYPE_DATE:
{
*hour = 0;
*minute = 0;
*second = 0;
*millisecond = 0;
db_date_decode (db_get_date (src_date), month, day, year);
return NO_ERROR;
}
case DB_TYPE_DATETIME:
case DB_TYPE_DATETIMELTZ:
case DB_TYPE_DATETIMETZ:
{
DB_DATETIME *dt_p;
DB_DATETIME dt_local;
DB_DATETIMETZ *dt_tz_p;
if (arg_type == DB_TYPE_DATETIMELTZ)
{
dt_p = db_get_datetime (src_date);
if (tz_datetimeltz_to_local (dt_p, &dt_local) != NO_ERROR)
{
er_clear ();
return ER_FAILED;
}
dt_p = &dt_local;
}
else if (arg_type == DB_TYPE_DATETIMETZ)
{
dt_tz_p = db_get_datetimetz (src_date);
if (tz_utc_datetimetz_to_local (&dt_tz_p->datetime, &dt_tz_p->tz_id, &dt_local) != NO_ERROR)
{
er_clear ();
return ER_FAILED;
}
dt_p = &dt_local;
}
else
{
dt_p = db_get_datetime (src_date);
}
return db_datetime_decode (dt_p, month, day, year, hour, minute, second, millisecond);
}
case DB_TYPE_TIMESTAMP:
case DB_TYPE_TIMESTAMPLTZ:
case DB_TYPE_TIMESTAMPTZ:
{
DB_DATE db_date = 0;
DB_TIME db_time = 0;
DB_TIMESTAMP *ts_p;
DB_TIMESTAMPTZ *ts_tz_p;
if (arg_type == DB_TYPE_TIMESTAMPTZ)
{
ts_tz_p = db_get_timestamptz (src_date);
if (db_timestamp_decode_w_tz_id (&ts_tz_p->timestamp, &ts_tz_p->tz_id, &db_date, &db_time) != NO_ERROR)
{
er_clear ();
return ER_FAILED;
}
}
else
{
ts_p = db_get_timestamp (src_date);
(void) db_timestamp_decode_ses (ts_p, &db_date, &db_time);
}
db_date_decode (&db_date, month, day, year);
db_time_decode (&db_time, hour, minute, second);
return NO_ERROR;
}
default:
return ER_FAILED;
}
return ER_FAILED;
}
/* TODO : refactor with db_get_datetime_from_dbvalue
* db_get_time_from_dbvalue () - splits a generic DB_VALUE to
* hour, minute, second , millisecond
* Arguments:
* src_date(in) : db_value to split
* hour(out) : hour
* minute(out) : minute
* second(out) : second
* millisecond(out) : millisecond
* Returns: int
*
* Note: Callers should not use the global error mechanism.
* This function returns ER_FAILED without setting the global error info
* For arguments of type having timezone, the returned values are in
* the local timezone (session or timezone of value).
*/
int
db_get_time_from_dbvalue (const DB_VALUE * src_date, int *hour, int *minute, int *second, int *millisecond)
{
DB_TYPE arg_type = DB_TYPE_UNKNOWN;
*millisecond = 0;
if (DB_IS_NULL (src_date))
{
return ER_FAILED;
}
arg_type = DB_VALUE_DOMAIN_TYPE (src_date);
switch (arg_type)
{
case DB_TYPE_DATE:
{
/* set all to 0 because we don't have any time information */
*hour = 0;
*minute = 0;
*second = 0;
return NO_ERROR;
}
case DB_TYPE_STRING:
case DB_TYPE_CHAR:
{
DB_TIME db_time;
int str_len;
const char *strp;
strp = db_get_string (src_date);
str_len = db_get_string_size (src_date);
if (db_date_parse_time (strp, str_len, &db_time, millisecond) != NO_ERROR)
{
return ER_FAILED;
}
db_time_decode (&db_time, hour, minute, second);
return NO_ERROR;
}
case DB_TYPE_DATETIME:
case DB_TYPE_DATETIMELTZ:
case DB_TYPE_DATETIMETZ:
{
int month = 0, day = 0, year = 0;
DB_DATETIME *dt_p;
DB_DATETIME dt_local;
DB_DATETIMETZ *dt_tz_p;
if (arg_type == DB_TYPE_DATETIMELTZ)
{
dt_p = db_get_datetime (src_date);
if (tz_datetimeltz_to_local (dt_p, &dt_local) != NO_ERROR)
{
er_clear ();
return ER_FAILED;
}
dt_p = &dt_local;
}
else if (arg_type == DB_TYPE_DATETIMETZ)
{
dt_tz_p = db_get_datetimetz (src_date);
if (tz_utc_datetimetz_to_local (&dt_tz_p->datetime, &dt_tz_p->tz_id, &dt_local) != NO_ERROR)
{
er_clear ();
return ER_FAILED;
}
dt_p = &dt_local;
}
else
{
dt_p = db_get_datetime (src_date);
}
return db_datetime_decode (dt_p, &month, &day, &year, hour, minute, second, millisecond);
}
case DB_TYPE_TIME:
{
DB_TIME *time_p;
time_p = db_get_time (src_date);
db_time_decode (time_p, hour, minute, second);
return NO_ERROR;
}
case DB_TYPE_TIMESTAMP:
case DB_TYPE_TIMESTAMPLTZ:
case DB_TYPE_TIMESTAMPTZ:
{
DB_DATE db_date = 0;
DB_TIME db_time = 0;
DB_TIMESTAMP *ts_p;
DB_TIMESTAMPTZ *ts_tz_p;
if (arg_type == DB_TYPE_TIMESTAMPTZ)
{
ts_tz_p = db_get_timestamptz (src_date);
if (db_timestamp_decode_w_tz_id (&ts_tz_p->timestamp, &ts_tz_p->tz_id, &db_date, &db_time) != NO_ERROR)
{
er_clear ();
return ER_FAILED;
}
}
else
{
ts_p = db_get_timestamp (src_date);
(void) db_timestamp_decode_ses (ts_p, &db_date, &db_time);
}
db_time_decode (&db_time, hour, minute, second);
return NO_ERROR;
}
default:
return ER_FAILED;
}
return ER_FAILED;
}
#if defined(ENABLE_UNUSED_FUNCTION)
/*
* db_null_terminate_string () - create a null terminated c string from a
* DB_VALUE of type DB_TYPE_CHAR
* return : NO_ERROR or error code
* src_value(in) : DB_VALUE containing the string
* strp(out) : pointer for output
*
* Note: the strp argument should not be allocated before calling this
* function and should be freed by the code calling this function
*/
int
db_null_terminate_string (const DB_VALUE * src_value, char **strp)
{
int src_size = 0;
DB_TYPE src_type = DB_TYPE_UNKNOWN;
if (src_value == NULL)
{
return ER_FAILED;
}
src_size = db_get_string_size (src_value);
src_type = DB_VALUE_DOMAIN_TYPE (src_value);
if (src_type != DB_TYPE_CHAR)
{
return ER_FAILED;
}
*strp = (char *) db_private_alloc (NULL, (size_t) src_size + 1);
if (*strp == NULL)
{
return ER_OUT_OF_VIRTUAL_MEMORY;
}
memcpy (*strp, db_get_string (src_value), src_size);
(*strp)[src_size] = '\0';
return NO_ERROR;
}
#endif
/*
* db_get_next_like_pattern_character () - Iterates through a LIKE pattern
*
* returns: NO_ERROR or error code
*
* pattern(in): the pattern that will be iterated upon
* length(in): the length of the pattern (bytes)
* codeset(in): codeset oof pattern string
* has_escape_char(in): whether the LIKE pattern can use an escape character
* escape_str(in): if has_escape_char is true this is the escaping character
* used in the pattern, otherwise the parameter has no
* meaning and should have the value NULL
* position(in/out): pointer to the pattern position counter. The initial
* value of the counter should be 0, meaning no characters
* have yet been iterated. When (*position == length) the
* iteration has come to an end. While iterating the pattern
* the position value should not be changed by the callers
* of this function.
* crt_char_p(out): when the function returns this is the current character in
* the pattern
* is_escaped(out): whether the current character pointed to by "character"
* is escaped in the pattern
*/
static int
db_get_next_like_pattern_character (const char *const pattern, const int length, const INTL_CODESET codeset,
const bool has_escape_char, const char *escape_str, int *const position,
char **crt_char_p, bool * const is_escaped)
{
int error_code = NO_ERROR;
int char_size = 1;
if (pattern == NULL || length < 0 || position == NULL || crt_char_p == NULL || is_escaped == NULL || *position < 0
|| *position >= length)
{
error_code = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 0);
goto error_exit;
}
assert (has_escape_char ^ (escape_str == NULL));
*crt_char_p = NULL;
*is_escaped = false;
if (has_escape_char
&& intl_cmp_char ((unsigned char *) &(pattern[*position]), (unsigned char *) escape_str, codeset,
&char_size) == 0)
{
*position += char_size;
if (*position >= length)
{
/* To keep MySQL compatibility, when the last character is escape char, do not return error. */
*crt_char_p = (char *) (&(pattern[*position - char_size]));
return error_code;
}
*is_escaped = true;
}
*crt_char_p = (char *) (&(pattern[*position]));
intl_char_size ((unsigned char *) *crt_char_p, 1, codeset, &char_size);
*position += char_size;
return error_code;
error_exit:
return error_code;
}
/*
* is_safe_last_char_for_like_optimization () -
*
* return: whether a character can be the last one in the string for LIKE
* index optimization. See db_get_info_for_like_optimization for
* details.
*
* chr(in) : the character to consider
* is_escaped(in) : whether the character is escaped in the LIKE pattern
*/
static bool
is_safe_last_char_for_like_optimization (const char *chr, const bool is_escaped, INTL_CODESET codeset)
{
assert (chr != NULL);
if (!is_escaped && QSTR_IS_LIKE_WILDCARD_CHAR (*chr))
{
return false;
}
if (intl_is_max_bound_chr (codeset, (const unsigned char *) chr)
|| intl_is_min_bound_chr (codeset, (const unsigned char *) chr))
{
return false;
}
return true;
}
/*
* db_get_info_for_like_optimization () - Gathers the information required for
* performing the LIKE index
* optimization
*
* returns: NO_ERROR or error code
*
* pattern(in): the LIKE pattern
* has_escape_char(in): whether the LIKE pattern can use an escape character
* escape_str(in): if has_escape_char is true this is the escaping character
* used in the pattern, otherwise the parameter has no
* meaning and should have the value NULL
* num_logical_chars(out): the number of logical characters in the pattern.
* This is equal to the pattern length minus the
* escaping characters.
* last_safe_logical_pos(out): the last character that can be used for the
* string in the predicate rewrite or a negative
* value if that particular rewrite cannot be
* performed
* num_match_many(out): the number of LIKE_WILDCARD_MATCH_MANY logical
* characters (not escaped '%' characters)
* num_match_one(out): the number of LIKE_WILDCARD_MATCH_ONE logical
* characters (not escaped '_' characters)
*
* Note: db_compress_like_pattern should be applied on the pattern before
* calling this function.
*
* Note: This function can be used for rewriting a LIKE predicate in order to
* maximize the chance of using an index scan. The possible rewrites for
* "expr LIKE pattern [ESCAPE escape]" are the following:
*
* 1)
* if the pattern is '%' we match any non-null value; we can rewrite to:
* expr IS NOT NULL
*
* 2)
* if the pattern has no wildcards (num_match_many == 0 && num_match_one == 0)
* and there are no comparison issues caused by trailing pattern whitespace,
* we can rewrite to a simple equality predicate:
* expr = remove_escaping (pattern [, escape])
*
* 3.1)
* in most other cases we can rewrite to:
* expr >= like_lower_bound ( like_prefix (pattern [, escape]) ) &&
* expr < like_upper_bound ( like_prefix (pattern [, escape]) ) &&
* expr LIKE pattern [ESCAPE escape]
* The first two predicates provide early filtering of possible matches and
* can be optimized through index scans. The last predicate provides an extra
* filter to ensure that the expression actually matches the original pattern.
*
* This rewrite is only possible if there exist strings S_lower and S_upper
* such that all LIKE matches are "BETWEEN S_lower GE_LT S_upper". We can
* compute these strings (see db_get_like_optimization_bounds) based on the
* longest prefix that does not contain a '%' character. The prefix itself can
* generally serve as S_lower while the prefix with the last character
* incremented by one can serve as S_upper. However, this imposes some
* restrictions on the last character in the prefix: it must have a succesor
* (it must not be the character 255), it must not cause issues during index
* scans (the space character might cause such issues because of its collation
* properties for VARCHAR). The special '_' wildcard can become the smallest
* possible character of the collation in S_lower (a space character) and the
* highest possible character in S_upper (character 255). Because of these
* properties, the '_' wildcard cannot be the last character in the prefix.
* Also see the is_safe_last_char_for_like_optimization function that codes
* this logic used to compute the last_safe_logical_pos parameter value.
*
* 3.2)
* If (pattern == like_prefix (pattern) + '%') and if the pattern does not
* contain additional wildcards ('_') then we can exclude the LIKE predicate
* and rewrite to:
* expr >= like_lower_bound ( like_prefix (pattern [, escape]) ) &&
* expr < like_upper_bound ( like_prefix (pattern [, escape]) )
*
* 3.3)
* If the rewrite 3.1 cannot be performed we can still use an index scan if
* like_lower_bound would returns negative infinity and like_upper_bound
* returns positive infinity, leading to:
* expr >= -infinity &&
* expr < +infinity &&
* expr LIKE pattern [ESCAPE escape]
* See db_get_like_optimization_bounds for details.
*
* Rewrite 3.1 (combined with the special case 3.3) is the most general,
* covering all the possible combinations, although it might result in slower
* execution than the alternatives.
*/
int
db_get_info_for_like_optimization (const DB_VALUE * const pattern, const bool has_escape_char,
const char *escape_str, int *const num_logical_chars,
int *const last_safe_logical_pos, int *const num_match_many,
int *const num_match_one)
{
int i = 0;
int error_code = NO_ERROR;
const char *pattern_str = NULL;
int pattern_size = 0;
if (pattern == NULL || num_logical_chars == NULL || last_safe_logical_pos == NULL || num_match_many == NULL
|| num_match_one == NULL)
{
error_code = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 0);
goto error_exit;
}
assert (has_escape_char ^ (escape_str == NULL));
if (DB_IS_NULL (pattern) || !QSTR_IS_CHAR (DB_VALUE_DOMAIN_TYPE (pattern)))
{
error_code = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 0);
goto error_exit;
}
*num_logical_chars = 0;
*last_safe_logical_pos = -22;
*num_match_many = 0;
*num_match_one = 0;
pattern_str = db_get_string (pattern);
pattern_size = db_get_string_size (pattern);
for (i = 0; i < pattern_size;)
{
char *crt_char_p = NULL;
bool is_escaped = false;
error_code =
db_get_next_like_pattern_character (pattern_str, pattern_size, db_get_string_codeset (pattern),
has_escape_char, escape_str, &i, &crt_char_p, &is_escaped);
if (error_code != NO_ERROR)
{
goto error_exit;
}
if (!is_escaped)
{
if (*crt_char_p == LIKE_WILDCARD_MATCH_MANY)
{
++(*num_match_many);
}
else if (*crt_char_p == LIKE_WILDCARD_MATCH_ONE)
{
++(*num_match_one);
}
}
if (*num_match_many == 0
&& is_safe_last_char_for_like_optimization (crt_char_p, is_escaped, db_get_string_codeset (pattern)))
{
*last_safe_logical_pos = *num_logical_chars;
}
++(*num_logical_chars);
}
return error_code;
error_exit:
return error_code;
}
/*
* db_get_like_optimization_bounds () - Computes the bounding limits required
* for performing the LIKE index
* optimization
*
* returns: NO_ERROR or error code
*
* pattern(in): the LIKE pattern
* bound(out): the computed upper or lower bound.
* has_escape_char(in): whether the LIKE pattern can use an escape character
* escape_str(in): if has_escape_char is true this is the escaping character
* used in the pattern, otherwise the parameter has no
* meaning and should have the value NULL
* compute_lower_bound(in): whether to compute the upper or the lower bound
* last_safe_logical_pos(in): the last character that can be used for the
* string in the predicate rewrite or a negative
* value if that particular rewrite cannot be
* performed.
*
* Note: See the comments on db_get_info_for_like_optimization for details
* on what this function computes.
*
* Note: If last_safe_logical_pos is negative the lower bound of the index
* scan is negative infinity (equivalent to the empty string or the
* string ' ' for the CHAR/VARCHAR default collation) and the upper
* bound is positive infinity (currently approximated by a string of
* one character code 255).
*/
int
db_get_like_optimization_bounds (const DB_VALUE * const pattern, DB_VALUE * bound, const bool has_escape_char,
const char *escape_str, const bool compute_lower_bound,
const int last_safe_logical_pos)
{
int error_code = NO_ERROR;
const char *original = NULL;
int original_size = 0;
char *result = NULL;
int result_length = 0;
int result_size = 0;
int i = 0;
int alloc_size;
int char_count;
INTL_CODESET codeset;
int collation_id;
if (pattern == NULL || bound == NULL)
{
error_code = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 0);
goto error_exit;
}
assert (has_escape_char ^ (escape_str == NULL));
if (DB_IS_NULL (pattern))
{
db_make_null (bound);
goto fast_exit;
}
codeset = db_get_string_codeset (pattern);
collation_id = db_get_string_collation (pattern);
if (!QSTR_IS_CHAR (DB_VALUE_DOMAIN_TYPE (pattern)))
{
error_code = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 0);
goto error_exit;
}
if (last_safe_logical_pos < 0)
{
if (compute_lower_bound)
{
error_code =
db_value_domain_min (bound, DB_TYPE_VARCHAR, DB_VALUE_PRECISION (pattern), DB_VALUE_SCALE (pattern),
codeset, collation_id, NULL);
if (error_code != NO_ERROR)
{
goto error_exit;
}
}
else
{
error_code =
db_value_domain_max (bound, DB_TYPE_VARCHAR, DB_VALUE_PRECISION (pattern), DB_VALUE_SCALE (pattern),
codeset, collation_id, NULL);
if (error_code != NO_ERROR)
{
goto error_exit;
}
}
goto fast_exit;
}
original = db_get_string (pattern);
original_size = db_get_string_size (pattern);
/* assume worst case scenario : all characters in output bound string are stored on the maximum character size */
intl_char_count ((unsigned char *) original, original_size, codeset, &char_count);
alloc_size = LOC_MAX_UCA_CHARS_SEQ * char_count * INTL_CODESET_MULT (codeset);
assert (alloc_size >= original_size);
result = (char *) db_private_alloc (NULL, alloc_size + 1);
if (result == NULL)
{
assert (er_errid () != NO_ERROR);
error_code = er_errid ();
goto error_exit;
}
assert (last_safe_logical_pos < char_count);
for (i = 0, result_length = 0, result_size = 0; result_length <= last_safe_logical_pos;)
{
char *crt_char_p = NULL;
bool is_escaped = false;
error_code =
db_get_next_like_pattern_character (original, original_size, codeset, has_escape_char, escape_str, &i,
&crt_char_p, &is_escaped);
if (error_code != NO_ERROR)
{
goto error_exit;
}
if (result_length == last_safe_logical_pos)
{
assert (is_safe_last_char_for_like_optimization (crt_char_p, is_escaped, codeset));
}
if (!is_escaped && *crt_char_p == LIKE_WILDCARD_MATCH_ONE)
{
assert (result_length < last_safe_logical_pos);
if (compute_lower_bound)
{
result_size += intl_set_min_bound_chr (codeset, result + result_size);
}
else
{
result_size += intl_set_max_bound_chr (codeset, result + result_size);
}
result_length++;
}
else
{
if (result_length == last_safe_logical_pos && !compute_lower_bound)
{
char *next_alpha_char_p = result + result_size;
int next_len = 0;
result_size +=
QSTR_NEXT_ALPHA_CHAR (collation_id, (unsigned char *) crt_char_p,
CAST_BUFLEN (original - crt_char_p) + original_size,
(unsigned char *) next_alpha_char_p, &next_len);
result_length += next_len;
}
else
{
result_size +=
intl_put_char ((unsigned char *) result + result_size, (unsigned char *) crt_char_p, codeset);
result_length++;
}
}
}
assert (result_size <= alloc_size);
qstr_make_typed_string (DB_TYPE_VARCHAR, bound, DB_VALUE_PRECISION (pattern), result, result_size, codeset,
collation_id);
result[result_size] = 0;
bound->need_clear = true;
fast_exit:
return error_code;
error_exit:
if (result != NULL)
{
db_private_free_and_init (NULL, result);
}
return error_code;
}
/*
* db_compress_like_pattern () - Optimizes a LIKE pattern for faster execution
* and easier processing.
*
* returns: NO_ERROR or error code
*
* pattern(in): the LIKE pattern to be compressed
* compressed_pattern(out): the optimized pattern (should be cleared before
* being passed to this function)
* has_escape_char(in): whether the LIKE pattern can use an escape character
* escape_str(in): if has_escape_char is true this is the escaping character
* used in the pattern, otherwise the parameter has no
* meaning and should have the value NULL
*
* Note: This function removes all the unnecessary escape characters in
* order to ease subsequent processing of the pattern. Currently there
* are no such unnecessary escape sequences, but there might be in the
* future if supporting MySQL semantics. See the comments in
* db_get_next_like_pattern_character.
*/
/* TODO This function could perform an extra optimization. The pattern
* 'a%___%b' can be compressed to either 'a___%b' or 'a%___b'. The first
* form is prefferable as it should execute faster than the second.
* Also, if 'a%___b' is initially present, it can be changed to 'a___%b'.
*/
int
db_compress_like_pattern (const DB_VALUE * const pattern, DB_VALUE * compressed_pattern, const bool has_escape_char,
const char *escape_str)
{
int error_code = NO_ERROR;
const char *original = NULL;
int original_size = 0;
char *result = NULL;
int result_length = 0;
int result_size = 0;
int i = 0;
int alloc_size;
bool in_percent_sequence = false;
INTL_CODESET codeset;
if (pattern == NULL || compressed_pattern == NULL)
{
error_code = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 0);
goto error_exit;
}
assert (has_escape_char ^ (escape_str == NULL));
if (DB_IS_NULL (pattern))
{
db_make_null (compressed_pattern);
goto fast_exit;
}
if (!QSTR_IS_ANY_CHAR (DB_VALUE_DOMAIN_TYPE (pattern)))
{
error_code = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 0);
goto error_exit;
}
codeset = db_get_string_codeset (pattern);
original = db_get_string (pattern);
original_size = db_get_string_size (pattern);
if (has_escape_char)
{
int char_count;
intl_char_count ((unsigned char *) original, original_size, codeset, &char_count);
/* assume worst case : each character in the compressed pattern is precedeed by the escape char */
alloc_size = original_size + char_count * strlen (escape_str);
}
else
{
alloc_size = original_size;
}
result = (char *) db_private_alloc (NULL, alloc_size + 1);
if (result == NULL)
{
assert (er_errid () != NO_ERROR);
error_code = er_errid ();
goto error_exit;
}
for (i = 0, result_length = 0, result_size = 0, in_percent_sequence = false; i < original_size;)
{
char *crt_char_p = NULL;
bool keep_crt_char = false;
bool needs_escape = false;
bool is_escaped = false;
error_code =
db_get_next_like_pattern_character (original, original_size, codeset, has_escape_char, escape_str, &i,
&crt_char_p, &is_escaped);
if (error_code != NO_ERROR)
{
goto error_exit;
}
if (is_escaped)
{
needs_escape = true;
}
assert (crt_char_p != NULL);
if (!is_escaped && *crt_char_p == LIKE_WILDCARD_MATCH_MANY && in_percent_sequence)
{
keep_crt_char = false;
}
else
{
keep_crt_char = true;
}
if (keep_crt_char)
{
if (needs_escape)
{
assert (has_escape_char);
result_size +=
intl_put_char ((unsigned char *) result + result_size, (unsigned char *) escape_str, codeset);
result_length++;
}
result_size += intl_put_char ((unsigned char *) result + result_size, (unsigned char *) crt_char_p, codeset);
result_length++;
}
if (!is_escaped && *crt_char_p == LIKE_WILDCARD_MATCH_MANY)
{
in_percent_sequence = true;
}
else
{
in_percent_sequence = false;
}
}
assert (result_length <= alloc_size);
result[result_size] = 0;
db_make_varchar (compressed_pattern, TP_FLOATING_PRECISION_VALUE, result, result_size, codeset,
db_get_string_collation (pattern));
compressed_pattern->need_clear = true;
fast_exit:
return error_code;
error_exit:
if (result != NULL)
{
db_private_free_and_init (NULL, result);
}
return error_code;
}
/*
* db_like_bound () - Computes the bounding limits required for performing the
* LIKE index optimization
*
* returns: NO_ERROR or error code
*
* src_pattern(in): the LIKE pattern
* src_escape(in): the escape character or NULL if there is no escaping
* result_bound(out): the computed upper or lower bound.
* compute_lower_bound(in): whether to compute the upper or the lower bound
*
* Note: See the comments on db_get_info_for_like_optimization for details
* on what this function computes.
*/
int
db_like_bound (const DB_VALUE * const src_pattern, const DB_VALUE * const src_escape, DB_VALUE * const result_bound,
const bool compute_lower_bound)
{
int error_code = NO_ERROR;
bool has_escape_char = false;
DB_VALUE compressed_pattern;
int num_logical_chars = 0;
int last_safe_logical_pos = 0;
int num_match_many = 0;
int num_match_one = 0;
const char *escape_str = NULL;
db_make_null (&compressed_pattern);
if (src_pattern == NULL || result_bound == NULL)
{
error_code = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 0);
goto error_exit;
}
if (DB_IS_NULL (src_pattern))
{
db_make_null (result_bound);
goto fast_exit;
}
if (!QSTR_IS_ANY_CHAR (DB_VALUE_DOMAIN_TYPE (src_pattern)))
{
error_code = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 0);
goto error_exit;
}
if (src_escape == NULL)
{
has_escape_char = false;
}
else
{
if (DB_IS_NULL (src_escape))
{
/* a LIKE b ESCAPE NULL means use the default escape character '\\' */
has_escape_char = true;
escape_str = "\\";
}
else
{
if (!QSTR_IS_ANY_CHAR (DB_VALUE_DOMAIN_TYPE (src_pattern)))
{
error_code = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 0);
goto error_exit;
}
escape_str = db_get_string (src_escape);
if (db_get_string_length (src_escape) != 1 || escape_str[0] == 0)
{
error_code = ER_QSTR_INVALID_ESCAPE_CHARACTER;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 0);
goto error_exit;
}
has_escape_char = true;
}
}
error_code = db_compress_like_pattern (src_pattern, &compressed_pattern, has_escape_char, escape_str);
if (error_code != NO_ERROR)
{
goto error_exit;
}
error_code =
db_get_info_for_like_optimization (&compressed_pattern, has_escape_char, escape_str, &num_logical_chars,
&last_safe_logical_pos, &num_match_many, &num_match_one);
if (error_code != NO_ERROR)
{
goto error_exit;
}
error_code =
db_get_like_optimization_bounds (&compressed_pattern, result_bound, has_escape_char, escape_str,
compute_lower_bound, last_safe_logical_pos);
if (error_code != NO_ERROR)
{
goto error_exit;
}
fast_exit:
pr_clear_value (&compressed_pattern);
return error_code;
error_exit:
pr_clear_value (&compressed_pattern);
return error_code;
}
/*
* db_check_or_create_null_term_string () - checks if the buffer associated to
* string DB_VALUE is null terminated; if it is returns it
* LIKE index optimization
*
* returns: NO_ERROR or error code
*
* str_val(in): source string DB_VALUE
* pre_alloc_buf(in): preallocated buffer to store null terminated string
* pre_alloc_buf_size(in): size of preallocated buffer
* ignore_prec_spaces(in): true if it should ignore preceding spaces
* (used only when new buffer needs to be allocated)
* ignore_trail_spaces(in): true if it should ignore trailing spaces
* (used only when new buffer needs to be allocated)
* str_out(out): pointer to null terminated string
* do_alloc(out): set to true if new buffer was allocated
*
*/
static int
db_check_or_create_null_term_string (const DB_VALUE * str_val, char *pre_alloc_buf, int pre_alloc_buf_size,
bool ignore_prec_spaces, bool ignore_trail_spaces, char **str_out, bool * do_alloc)
{
const char *val_buf;
char *new_buf;
const char *val_buf_end = NULL, *val_buf_end_non_space = NULL;
int val_size;
assert (pre_alloc_buf != NULL);
assert (pre_alloc_buf_size > 1);
assert (str_out != NULL);
assert (do_alloc != NULL);
assert (QSTR_IS_ANY_CHAR (DB_VALUE_DOMAIN_TYPE (str_val)));
*do_alloc = false;
val_buf = db_get_string (str_val);
if (val_buf == NULL)
{
*str_out = NULL;
return NO_ERROR;
}
val_size = db_get_string_size (str_val);
/* size < 0 assumes a null terminated string */
if (val_size < 0)
{
val_size = strlen (val_buf);
}
if (val_size < pre_alloc_buf_size)
{
/* use the preallocated buffer supplied to copy the content */
strncpy (pre_alloc_buf, val_buf, val_size);
pre_alloc_buf[val_size] = '\0';
*str_out = pre_alloc_buf;
return NO_ERROR;
}
/* trim preceding and trailing spaces */
val_buf_end = val_buf + val_size;
if (ignore_prec_spaces)
{
while (val_buf < val_buf_end && char_isspace2 (*val_buf))
{
val_buf++;
}
val_size = CAST_BUFLEN (val_buf_end - val_buf);
assert (val_size >= 0);
}
if (ignore_trail_spaces && val_size > 0)
{
val_buf_end_non_space = val_buf + val_size - 1;
while (val_buf < val_buf_end_non_space && char_isspace2 (*val_buf_end_non_space))
{
val_buf_end_non_space--;
}
val_size = CAST_BUFLEN (val_buf_end_non_space - val_buf) + 1;
assert (val_size >= 0);
}
if (val_size < pre_alloc_buf_size)
{
assert (ignore_prec_spaces || ignore_trail_spaces);
/* use the preallocated buffer supplied to copy the content */
strncpy (pre_alloc_buf, val_buf, val_size);
pre_alloc_buf[val_size] = '\0';
*str_out = pre_alloc_buf;
return NO_ERROR;
}
new_buf = (char *) db_private_alloc (NULL, val_size + 1);
if (new_buf == NULL)
{
return ER_OUT_OF_VIRTUAL_MEMORY;
}
strncpy (new_buf, val_buf, val_size);
new_buf[val_size] = '\0';
*str_out = new_buf;
*do_alloc = true;
return NO_ERROR;
}
/*
* get_string_date_token_id() - get the id of date token identifier
* return: NO_ERROR or error code
* token_type(in): string-to-date token type
* intl_lang_id(in):
* cs(in): input string to search for token (considered NULL terminated)
* token_id(out): id of token (if non-zero) or zero if not found;
* range begins from 1 :days 1 - 7, months 1 - 12
* token_size(out): size in bytes ocupied by token in input string 'cs'
*/
static int
get_string_date_token_id (const STRING_DATE_TOKEN token_type, const INTL_LANG intl_lang_id, const char *cs,
const INTL_CODESET codeset, int *token_id, int *token_size)
{
const char **p;
int error_status = NO_ERROR;
int search_size;
const char *parse_order;
int i;
int cs_size;
int skipped_leading_chars = 0;
const LANG_LOCALE_DATA *lld = lang_get_specific_locale (intl_lang_id, codeset);
assert (cs != NULL);
assert (token_id != NULL);
assert (token_size != NULL);
if (lld == NULL)
{
error_status = ER_LANG_CODESET_NOT_AVAILABLE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 2, lang_get_lang_name_from_id (intl_lang_id),
lang_charset_name (codeset));
return error_status;
}
#if 0
/* special case : korean short month name is read as digit */
if (intl_lang_id == INTL_LANG_KOREAN && codeset == INTL_CODESET_ISO88591 && token_type == SDT_MONTH_SHORT)
{
i = parse_digits ((char *) cs, token_id, 2);
if (i <= 0)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
if (*token_id < 1 || *token_id > 12)
{
error_status = ER_QSTR_MISMATCHING_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
*token_size = i;
return NO_ERROR;
}
#endif
switch (token_type)
{
case SDT_DAY:
p = (const char **) lld->day_name;
parse_order = lld->day_parse_order;
search_size = 7;
break;
case SDT_DAY_SHORT:
p = (const char **) lld->day_short_name;
parse_order = lld->day_short_parse_order;
search_size = 7;
break;
case SDT_MONTH:
p = (const char **) lld->month_name;
parse_order = lld->month_parse_order;
search_size = 12;
break;
case SDT_MONTH_SHORT:
p = (const char **) lld->month_short_name;
parse_order = lld->month_short_parse_order;
search_size = 12;
break;
case SDT_AM_PM:
p = (const char **) lld->am_pm;
parse_order = lld->am_pm_parse_order;
search_size = 12;
break;
default:
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return ER_GENERIC_ERROR;
}
*token_id = 0;
while (WHITESPACE (*cs))
{
cs++;
skipped_leading_chars++;
}
cs_size = strlen (cs);
for (i = 0; i < search_size; i++)
{
int cmp = 0;
int token_index = parse_order[i];
cmp =
intl_case_match_tok (intl_lang_id, codeset, (unsigned char *) p[token_index], (unsigned char *) cs,
strlen (p[token_index]), cs_size, token_size);
assert (*token_size <= cs_size);
if (cmp == 0)
{
*token_id = token_index + 1;
*token_size += skipped_leading_chars;
break;
}
}
return NO_ERROR;
}
/*
* print_string_date_token() - prints a date token to a buffer
* return: NO_ERROR or error code
* token_type(in): string-to-date token type
* intl_lang_id(in): locale identifier
* codeset(in): codeset to use for string to print; paired with intl_lang_id
* token_id(in): id of token (zero-based index)
* for days: 0 - 6, months: 0 - 11
* case_mode(in): casing for printing token:
* 0 : unchanged; 1 - force lowercase; 2 - force uppercase
* buffer(in/out) : buffer to print to
* token_size(out): size in bytes of token printed
*/
static int
print_string_date_token (const STRING_DATE_TOKEN token_type, const INTL_LANG intl_lang_id,
const INTL_CODESET codeset, int token_id, int case_mode, char *buffer, int *token_size)
{
const char *p;
int error_status = NO_ERROR;
int token_len;
int token_bytes;
int print_len = -1;
const LANG_LOCALE_DATA *lld = lang_get_specific_locale (intl_lang_id, codeset);
assert (buffer != NULL);
assert (token_id >= 0);
assert (token_size != NULL);
if (lld == NULL)
{
error_status = ER_LANG_CODESET_NOT_AVAILABLE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 2, lang_get_lang_name_from_id (intl_lang_id),
lang_charset_name (codeset));
return error_status;
}
switch (token_type)
{
case SDT_DAY:
assert (token_id < 7);
p = lld->day_name[token_id];
/* day names for all language use at most 9 chars */
print_len = 9;
break;
case SDT_DAY_SHORT:
assert (token_id < 7);
p = lld->day_short_name[token_id];
switch (intl_lang_id)
{
case INTL_LANG_ENGLISH:
print_len = 3;
break;
case INTL_LANG_TURKISH:
print_len = 2;
break;
default:
print_len = -1;
break;
}
break;
case SDT_MONTH:
assert (token_id < 12);
p = lld->month_name[token_id];
switch (intl_lang_id)
{
case INTL_LANG_ENGLISH:
print_len = 9;
break;
case INTL_LANG_TURKISH:
print_len = 7;
break;
default:
print_len = -1;
break;
}
break;
case SDT_MONTH_SHORT:
assert (token_id < 12);
p = lld->month_short_name[token_id];
/* all short names for months have 3 chars */
print_len = 3;
break;
case SDT_AM_PM:
assert (token_id < 12);
p = lld->am_pm[token_id];
/* AM/PM tokens are printed without padding */
print_len = -1;
break;
default:
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return ER_GENERIC_ERROR;
}
#if 0
if (codeset == INTL_CODESET_KSC5601_EUC && intl_lang_id == INTL_LANG_KOREAN)
{
/* korean names dot not use compatible codeset, we use specific code to print them */
switch (token_type)
{
case SDT_DAY:
sprintf (buffer, "%-6s", p);
*token_size = 6;
break;
case SDT_MONTH:
sprintf (buffer, "%-4s", p);
*token_size = 4;
break;
case SDT_DAY_SHORT:
memcpy (buffer, p, 2);
*token_size = 2;
break;
case SDT_MONTH_SHORT:
sprintf (buffer, "%d", token_id + 1);
*token_size = (token_id < 10) ? 1 : 2;
break;
case SDT_AM_PM:
sprintf (buffer, "%s", p);
*token_size = strlen (p);
break;
}
return NO_ERROR;
}
#endif
/* determine length of token */
token_bytes = strlen (p);
intl_char_count ((unsigned char *) p, token_bytes, codeset, &token_len);
if (case_mode == 2)
{
/* uppercase */
intl_upper_string (&(lld->alphabet), (unsigned char *) p, (unsigned char *) buffer, token_len);
intl_char_size ((unsigned char *) buffer, token_len, codeset, token_size);
}
else if (case_mode == 1)
{
/* lowercase */
intl_lower_string (&(lld->alphabet), (unsigned char *) p, (unsigned char *) buffer, token_len);
intl_char_size ((unsigned char *) buffer, token_len, codeset, token_size);
}
else
{
intl_char_size ((unsigned char *) p, token_len, codeset, token_size);
memcpy (buffer, p, *token_size);
}
/* padding */
if (token_len < print_len)
{
(void) qstr_pad_string ((unsigned char *) buffer + *token_size, print_len - token_len, codeset);
*token_size += intl_pad_size (codeset) * (print_len - token_len);
}
return NO_ERROR;
}
/*
* convert_locale_number() - transforms a string containing a number in a
* locale representation into another locale
* return: void
* sz(in/out): string to be transformed
* src_locale(in):
* dst_locale(in):
*
*/
static void
convert_locale_number (char *sz, const int size, const INTL_LANG src_locale, const INTL_LANG dst_locale)
{
const char src_locale_group = lang_digit_grouping_symbol (src_locale);
const char src_locale_frac = lang_digit_fractional_symbol (src_locale);
const char dst_locale_group = lang_digit_grouping_symbol (dst_locale);
const char dst_locale_frac = lang_digit_fractional_symbol (dst_locale);
char *sz_end = sz + size;
assert (src_locale != dst_locale);
if (src_locale_group == dst_locale_group)
{
assert (src_locale_frac == dst_locale_frac);
return;
}
assert (dst_locale_frac != dst_locale_group);
for (; sz < sz_end && *sz != '\0'; sz++)
{
if (*sz == src_locale_group)
{
*sz = dst_locale_group;
}
else if (*sz == src_locale_frac)
{
*sz = dst_locale_frac;
}
}
}
/*
* db_hex() - return hexadecimal representation
* returns: error code or NO_ERROR
* param(in): parameter to turn to hex
* result(out): varchar db_value with hex representation
*
* Note:
* If param is a generic string, the hex representation will be the
* concatenation of hex values of each byte.
* If param is a generic numeric, the hex representation will be that of
* param casted to bigint (64bit unsigned integer). if value exceeds UINT64
* capacity, the return value is 'FFFFFFFFFFFFFFFF'.
*/
int
db_hex (const DB_VALUE * param, DB_VALUE * result)
{
/* String length limits for numeric values of param. When param is numeric, it will be cast to BIGINT db type and
* then internally to UINT64. hex_lenght_limits[i] is the upper limit of the closed set of integers that can be
* represented in hex on i digits. */
const UINT64 hex_length_limits[UINT64_MAX_HEX_DIGITS + 1] = {
0x0, 0xF, 0xFF, 0xFFF, 0xFFFF,
0xFFFFF, 0xFFFFFF, 0xFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFFF,
0xFFFFFFFFFF, 0xFFFFFFFFFFF,
0xFFFFFFFFFFFF, 0xFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF
};
/* hex digits */
const char hex_digit[] = "0123456789ABCDEF";
/* other variables */
DB_TYPE param_type = DB_TYPE_UNKNOWN;
const char *str = NULL;
char *hexval = NULL;
int str_size = 0, hexval_len = 0, i = 0, error_code = NO_ERROR;
DB_VALUE tval, *ptval = NULL;
/* check parameters for NULL values */
if (param == NULL || result == NULL)
{
error_code = ER_OBJ_INVALID_ARGUMENTS;
goto error;
}
if (DB_IS_NULL (param))
{
db_make_null (result);
return NO_ERROR;
}
/* compute hex representation */
param_type = DB_VALUE_DOMAIN_TYPE (param);
coerce_pos:
if (TP_IS_CHAR_TYPE (param_type) || TP_IS_BIT_TYPE (param_type))
{
if (TP_IS_CHAR_TYPE (param_type))
{
/* retrieve source string */
str = db_get_string (param);
str_size = db_get_string_size (param);
/* remove padding from end of string */
if (param_type == DB_TYPE_CHAR)
{
unsigned char pad_char[2];
int pad_char_size;
intl_pad_char (db_get_string_codeset (param), pad_char, &pad_char_size);
while (str_size >= pad_char_size
&& memcmp (&(str[str_size - pad_char_size]), pad_char, pad_char_size) == 0)
{
str_size -= pad_char_size;
}
}
}
else
{
/* get bytes of bitfield */
str = db_get_bit (param, &str_size);
str_size = QSTR_NUM_BYTES (str_size);
}
/* allocate hex string */
hexval_len = str_size * 2;
hexval = (char *) db_private_alloc (NULL, hexval_len + 1);
if (hexval == NULL)
{
error_code = ER_OUT_OF_VIRTUAL_MEMORY;
goto error;
}
hexval[hexval_len] = 0;
/* compute hex representation */
for (i = 0; i < str_size; i++)
{
hexval[i * 2] = hex_digit[(str[i] >> 4) & 0xF];
hexval[i * 2 + 1] = hex_digit[str[i] & 0xF];
}
/* set return string */
db_make_string (result, hexval);
result->need_clear = true;
}
else if (TP_IS_NUMERIC_TYPE (param_type))
{
DB_VALUE param_db_bigint;
TP_DOMAIN *domain, *param_domain;
UINT64 param_bigint;
/* try to convert to bigint */
param_domain = tp_domain_resolve_default (param_type);
domain = tp_domain_resolve_default (DB_TYPE_BIGINT);
/* don't mind error code here, we need to know if param is out of range */
(void) tp_value_auto_cast (param, ¶m_db_bigint, domain);
if (DB_IS_NULL (¶m_db_bigint))
{
/* param is out of range, set it to max */
param_bigint = hex_length_limits[UINT64_MAX_HEX_DIGITS];
}
else
{
param_bigint = (UINT64) db_get_bigint (¶m_db_bigint);
}
/* compute hex representation length */
hexval_len = 1;
while (param_bigint > hex_length_limits[hexval_len] && hexval_len < UINT64_MAX_HEX_DIGITS)
{
hexval_len++;
}
/* allocate memory */
hexval = (char *) db_private_alloc (NULL, hexval_len + 1);
if (hexval == NULL)
{
error_code = ER_OUT_OF_VIRTUAL_MEMORY;
goto error;
}
hexval[hexval_len] = 0;
/* compute hex representation */
for (i = hexval_len - 1; i >= 0; --i)
{
hexval[i] = hex_digit[param_bigint & 0xF];
param_bigint >>= 4;
}
/* set return string */
db_make_string (result, hexval);
result->need_clear = true;
}
else
{
db_make_null (&tval);
ptval = &tval;
if (tp_value_cast (param, &tval, &tp_Char_domain, false) != DOMAIN_COMPATIBLE)
{
error_code = ER_QSTR_INVALID_DATA_TYPE;
goto error;
}
param = &tval;
param_type = DB_VALUE_DOMAIN_TYPE (param);
assert (TP_IS_CHAR_TYPE (param_type));
goto coerce_pos;
}
if (ptval)
{
db_value_clear (ptval);
}
/* all ok */
return NO_ERROR;
error:
if (ptval)
{
db_value_clear (ptval);
}
if (result)
{
db_make_null (result);
}
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_clear ();
return NO_ERROR;
}
if (er_errid () == NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 0);
}
return error_code;
}
#if !defined (CS_MODE)
/*
* db_guid() - Generate a type 4 (randomly generated) UUID.
* return: error code or NO_ERROR
* thread_p(in): thread context
* result(out): HEX encoded UUID string
* Note:
*/
int
db_guid (THREAD_ENTRY * thread_p, DB_VALUE * result)
{
int i = 0, error_code = NO_ERROR;
const char hex_digit[] = "0123456789ABCDEF";
char guid_bytes[GUID_STANDARD_BYTES_LENGTH];
char *guid_hex = NULL;
if (result == NULL)
{
error_code = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 0);
goto error;
}
db_make_null (result);
/* Generate random bytes */
error_code = crypt_generate_random_bytes (guid_bytes, GUID_STANDARD_BYTES_LENGTH);
if (error_code != NO_ERROR)
{
goto error;
}
/* Clear UUID version field */
guid_bytes[6] &= 0x0F;
/* Set UUID version according to UUID version 4 protocol */
guid_bytes[6] |= 0x40;
/* Clear variant field */
guid_bytes[8] &= 0x3f;
/* Set variant according to UUID version 4 protocol */
guid_bytes[8] |= 0x80;
guid_hex = (char *) db_private_alloc (thread_p, GUID_STANDARD_BYTES_LENGTH * 2 + 1);
if (guid_hex == NULL)
{
error_code = er_errid ();
goto error;
}
guid_hex[GUID_STANDARD_BYTES_LENGTH * 2] = '\0';
/* Encode the bytes to HEX */
for (i = 0; i < GUID_STANDARD_BYTES_LENGTH; i++)
{
guid_hex[i * 2] = hex_digit[(guid_bytes[i] >> 4) & 0xF];
guid_hex[i * 2 + 1] = hex_digit[(guid_bytes[i] & 0xF)];
}
db_make_string (result, guid_hex);
result->need_clear = true;
return NO_ERROR;
error:
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_clear ();
error_code = NO_ERROR;
}
return error_code;
}
#endif /* !defined (CS_MODE) */
/*
* db_ascii() - return ASCII code of first character in string
* returns: error code or NO_ERROR
* param(in): string
* result(out): smallint db_value of ASCII code
*
* Note:
* If param is a zero-length string, result should be zero.
* If param is DB null, result should be DB null
*/
int
db_ascii (const DB_VALUE * param, DB_VALUE * result)
{
/* other variables */
DB_TYPE param_type = DB_TYPE_UNKNOWN;
const char *str = NULL;
int str_size = 0, error_code = NO_ERROR;
/* check parameters for NULL values */
if (param == NULL || result == NULL)
{
error_code = ER_OBJ_INVALID_ARGUMENTS;
goto error;
}
if (DB_IS_NULL (param))
{
db_make_null (result);
return NO_ERROR;
}
/* get ASCII value */
param_type = DB_VALUE_DOMAIN_TYPE (param);
if (TP_IS_CHAR_TYPE (param_type))
{
/* get string and length */
str = db_get_string (param);
str_size = db_get_string_size (param);
/* remove padding from end of string */
if (param_type == DB_TYPE_CHAR)
{
unsigned char pad_char[2];
int pad_char_size;
intl_pad_char (db_get_string_codeset (param), pad_char, &pad_char_size);
while (str_size >= pad_char_size && memcmp (&(str[str_size - pad_char_size]), pad_char, pad_char_size) == 0)
{
str_size -= pad_char_size;
}
}
/* return first character */
if (str_size > 0)
{
db_make_short (result, (unsigned char) str[0]);
}
else
{
db_make_short (result, 0);
}
}
else if (TP_IS_BIT_TYPE (param_type))
{
/* get bitfield as char array */
str = db_get_bit (param, &str_size);
/* return first byte */
if (str_size > 0)
{
db_make_short (result, (unsigned char) str[0]);
}
else
{
db_make_short (result, 0);
}
}
else if (TP_IS_NUMERIC_TYPE (param_type) || TP_IS_DATE_OR_TIME_TYPE (param_type))
{
DB_VALUE new_value;
const TP_DOMAIN *new_domain = tp_domain_resolve_default (DB_TYPE_CHAR);
db_make_null (&new_value);
TP_DOMAIN_STATUS status = tp_value_auto_cast (param, &new_value, new_domain);
if (status != DOMAIN_COMPATIBLE)
{
error_code = ER_QSTR_INVALID_DATA_TYPE;
goto error;
}
str_size = db_get_string_size (&new_value);
if (str_size > 0)
{
str = db_get_string (&new_value);
db_make_short (result, (unsigned char) str[0]);
}
else
{
db_make_short (result, 0);
}
db_value_clear (&new_value);
}
else
{
error_code = ER_QSTR_INVALID_DATA_TYPE;
goto error;
}
/* no error */
return NO_ERROR;
error:
if (result)
{
db_make_null (result);
}
er_clear ();
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
return NO_ERROR;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 0);
return error_code;
}
/*
* db_conv() - convert number form one base to another
* returns: error code or NO_ERROR
* num(in): number to convert
* from_base(in): base of num
* to_base(in): base to convert num to
* result(out): string db_value with number in new base
*
* Note:
* From_base and to_base should satisfy 2 <= abs(base) <= 36
*/
int
db_conv (const DB_VALUE * num, const DB_VALUE * from_base, const DB_VALUE * to_base, DB_VALUE * result)
{
/* digit value lookup table vars */
const unsigned char base_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/* error handling vars */
int error_code = NO_ERROR;
/* type checking vars */
DB_TYPE num_type = DB_TYPE_UNKNOWN;
DB_TYPE from_base_type = DB_TYPE_UNKNOWN;
DB_TYPE to_base_type = DB_TYPE_UNKNOWN;
/* sign flags */
bool num_is_signed = false, res_is_signed = false;
bool res_has_minus = false;
/* string representations of input number and result; size of buffer is maximum computable value in base 2 (64
* digits) + sign (1 digit) + NULL terminator (1 byte) */
char num_str[UINT64_MAX_BIN_DIGITS + 2] = { 0 };
unsigned char res_str[UINT64_MAX_BIN_DIGITS + 2] = { 0 };
char *num_p_str = num_str, *res_p_str = NULL;
char *num_end_ptr = NULL;
char str_buf[NUMERIC_MAX_STRING_SIZE];
unsigned char swap = 0;
int num_size = 0, res_size = 0;
/* auxiliary variables */
UINT64 base10 = 0;
int from_base_int = 0, to_base_int = 0, i = 0;
/* check parameters for NULL values */
if (num == NULL || from_base == NULL || to_base == NULL || result == NULL)
{
error_code = ER_OBJ_INVALID_ARGUMENTS;
goto error;
}
if (DB_IS_NULL (num) || DB_IS_NULL (from_base) || DB_IS_NULL (to_base))
{
db_make_null (result);
return NO_ERROR;
}
/* type checking; do not check num_type here, we will do it later on */
num_type = DB_VALUE_DOMAIN_TYPE (num);
from_base_type = DB_VALUE_DOMAIN_TYPE (from_base);
to_base_type = DB_VALUE_DOMAIN_TYPE (to_base);
if (from_base_type != DB_TYPE_SMALLINT || to_base_type != DB_TYPE_SMALLINT)
{
error_code = ER_QSTR_INVALID_DATA_TYPE;
goto error;
}
/* from_base and to_base bounds checking */
from_base_int = db_get_short (from_base);
to_base_int = db_get_short (to_base);
num_is_signed = (from_base_int < 0);
res_is_signed = (to_base_int < 0);
from_base_int = ABS (from_base_int);
to_base_int = ABS (to_base_int);
if (from_base_int < 2 || from_base_int > 36 || to_base_int < 2 || to_base_int > 36)
{
db_make_null (result);
return NO_ERROR;
}
/* compute input number string from either generic NUMERIC or generic STRING types */
if (TP_IS_NUMERIC_TYPE (num_type))
{
/* generic number -> string */
switch (num_type)
{
case DB_TYPE_SMALLINT:
snprintf (num_p_str, UINT64_MAX_BIN_DIGITS + 1, "%d", db_get_short (num));
break;
case DB_TYPE_INTEGER:
snprintf (num_p_str, UINT64_MAX_BIN_DIGITS + 1, "%d", db_get_int (num));
break;
case DB_TYPE_BIGINT:
snprintf (num_p_str, UINT64_MAX_BIN_DIGITS + 1, "%lld", (long long int) db_get_bigint (num));
break;
case DB_TYPE_NUMERIC:
num_p_str = numeric_db_value_print (num, str_buf);
/* set the decimal point to '\0' to bypass end_ptr check, make it looks like we already trucated out the
* fractional part, as we do to float. */
for (i = 0; num_p_str[i] != '\0'; ++i)
{
if (num_p_str[i] == '.')
{
num_p_str[i] = '\0';
break;
}
}
break;
case DB_TYPE_FLOAT:
snprintf (num_p_str, UINT64_MAX_BIN_DIGITS + 1, "%.0f", db_get_float (num));
break;
case DB_TYPE_DOUBLE:
snprintf (num_p_str, UINT64_MAX_BIN_DIGITS + 1, "%.0f", db_get_double (num));
break;
case DB_TYPE_MONETARY:
snprintf (num_p_str, UINT64_MAX_BIN_DIGITS + 1, "%.0f", db_value_get_monetary_amount_as_double (num));
break;
default:
db_make_null (result);
return NO_ERROR;
}
}
else if (TP_IS_CHAR_TYPE (num_type))
{
/* copy into a null-terminated string */
int str_size = db_get_string_size (num);
INTL_CODESET codeset = db_get_string_codeset (num);
int prev_char_length = 0;
char *str_start = NULL, *str_end = NULL;
char *prev_char = NULL;
if (str_size >= 0)
{
str_size = MIN (str_size, sizeof (num_str) - 1);
strncpy (num_str, db_get_string (num), str_size);
str_start = num_str;
str_end = num_str + str_size;
/* trim the tailing white spaces */
do
{
prev_char =
(char *) intl_prev_char ((unsigned char *) str_end, (unsigned char *) str_start, codeset,
&prev_char_length);
assert (prev_char >= str_start);
if (intl_is_space (prev_char, str_end, codeset, NULL))
{
str_size -= prev_char_length;
assert (str_size >= 0);
str_end = prev_char;
}
else
{
break;
}
}
while (str_end > str_start);
}
else
{
str_size = 0;
}
num_str[str_size] = '\0';
num_p_str = num_str;
if (!is_str_valid_number (num_p_str, str_end, from_base_int, codeset))
{
error_code = ER_OBJ_INVALID_ARGUMENTS;
goto error;
}
}
else if (TP_IS_BIT_TYPE (num_type))
{
/* get raw bytes */
const char *num_bit_str = db_get_bit (num, &num_size);
num_size = QSTR_NUM_BYTES (num_size);
/* convert to hex; NOTE: qstr_bin_to_hex returns number of converted bytes, not the size of the hex string; also,
* we convert at most 64 digits even if we need only 16 in order to let strtoll handle overflow (weird stuff
* happens there ...) */
num_size = qstr_bin_to_hex (num_str, UINT64_MAX_BIN_DIGITS, num_bit_str, num_size);
num_str[num_size * 2] = '\0';
/* set up variables for hex -> base10 conversion */
num_p_str = num_str;
from_base_int = 16;
num_is_signed = false;
}
else
{
/* we cannot process the input in any way */
error_code = ER_QSTR_INVALID_DATA_TYPE;
goto error;
}
/* convert from string to INT64/UINT64 */
errno = 0;
if (num_is_signed)
{
base10 = (UINT64) strtoll (num_p_str, &num_end_ptr, from_base_int);
}
else
{
base10 = (UINT64) strtoull (num_p_str, &num_end_ptr, from_base_int);
}
if (errno != 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TP_CANT_COERCE_OVERFLOW, 2, pr_type_name (num_type),
pr_type_name (DB_TYPE_BIGINT));
error_code = ER_TP_CANT_COERCE_OVERFLOW;
goto error;
}
if (num_end_ptr != NULL && *num_end_ptr != '\0')
{
error_code = ER_OBJ_INVALID_ARGUMENTS;
goto error;
}
/* compute signed part of number */
if (res_is_signed && base10 > DB_BIGINT_MAX)
{
/* result should be signed and we DO have a negative INT64; compute complement and remember to add minus sign to
* string */
base10 = ~base10;
++base10;
res_has_minus = true;
}
/* convert base 10 -> to_base */
if (base10 == 0)
{
/* number is zero? display it as such */
res_str[res_size++] = '0';
res_has_minus = false;
}
while (base10 > 0)
{
/* convert another digit */
res_str[res_size++] = base_digits[base10 % to_base_int];
base10 /= to_base_int;
}
if (res_has_minus)
{
/* add minus sign to number string */
res_str[res_size++] = '-';
}
/* reverse string (duh!) */
res_str[res_size] = 0;
for (i = 0; i < res_size / 2; i++)
{
swap = res_str[i];
res_str[i] = res_str[res_size - i - 1];
res_str[res_size - i - 1] = swap;
}
/* return string */
res_p_str = (char *) db_private_alloc (NULL, res_size + 1);
if (res_p_str == NULL)
{
error_code = ER_OUT_OF_VIRTUAL_MEMORY;
goto error;
}
memcpy (res_p_str, res_str, res_size + 1);
db_make_string (result, res_p_str);
result->need_clear = true;
/* all ok */
return NO_ERROR;
error:
if (result)
{
db_make_null (result);
}
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
if (er_errid () != NO_ERROR)
{
er_clear ();
}
return NO_ERROR;
}
if (er_errid () == NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 0);
}
return error_code;
}
/*
* is_str_valid_number() - check whether the given string is a valid number.
*
* return: true if it's valid, false otherwise
* num_p(in): the number in string pointer
* str_end(in): end of string (pointer to first character after last
* character of string) or NULL if str is null terminated
* base(in): from 2 to 36 inclusive
*
*/
bool
is_str_valid_number (char *num_p, char *str_end, int base, INTL_CODESET codeset)
{
char digit_max = (char) ('0' + base);
char lower_char_max = (char) ('a' - 10 + base);
char upper_char_max = (char) ('A' - 10 + base);
bool has_decimal_point = false;
int space_length = 0;
/* skip leading space */
while (num_p < str_end)
{
if (!intl_is_space (num_p, str_end, codeset, &space_length))
{
break;
}
num_p += space_length;
assert (num_p <= str_end);
}
/* just space, no digit */
if (num_p == str_end)
{
return false;
}
/* check number sign */
if (*num_p == '-' || *num_p == '+')
{
++num_p;
}
/* check base16 prefix '0x' */
if (base == 16 && *num_p == '0' && (*(num_p + 1) == 'x' || *(num_p + 1) == 'X'))
{
num_p += 2;
/* just space, no digit */
if (num_p == str_end)
{
return false;
}
}
/* check the digits */
for (; num_p != str_end; ++num_p)
{
if (base < 10 && *num_p >= '0' && *num_p < digit_max)
{
continue;
}
if (base >= 10 && *num_p >= '0' && *num_p <= '9')
{
continue;
}
if (base > 10 && *num_p >= 'a' && *num_p < lower_char_max)
{
continue;
}
if (base > 10 && *num_p >= 'A' && *num_p < upper_char_max)
{
continue;
}
if (*num_p == '.' && !has_decimal_point)
{
/* truncate out the fractional part */
*num_p = '\0';
has_decimal_point = true;
continue;
}
return false;
}
return true;
}
/*
* init_builtin_calendar_names() - initializes builtin localizations for
* calendar names
* return: void
* lld(in/out): locale data
*
*/
void
init_builtin_calendar_names (LANG_LOCALE_DATA * lld)
{
int i;
assert (lld != NULL);
if (lld->codeset == INTL_CODESET_UTF8)
{
for (i = 0; i < 7; i++)
{
lld->day_short_name[i] = Short_Day_name_UTF8[lld->lang_id][i];
lld->day_name[i] = Day_name_UTF8[lld->lang_id][i];
}
for (i = 0; i < 12; i++)
{
lld->month_short_name[i] = Short_Month_name_UTF8[lld->lang_id][i];
lld->month_name[i] = Month_name_UTF8[lld->lang_id][i];
}
for (i = 0; i < 12; i++)
{
lld->am_pm[i] = Am_Pm_name_UTF8[lld->lang_id][i];
}
}
else if (lld->codeset == INTL_CODESET_KSC5601_EUC)
{
for (i = 0; i < 7; i++)
{
lld->day_short_name[i] = Short_Day_name_EUCKR[lld->lang_id][i];
lld->day_name[i] = Day_name_EUCKR[lld->lang_id][i];
}
for (i = 0; i < 12; i++)
{
lld->month_short_name[i] = Short_Month_name_EUCKR[lld->lang_id][i];
lld->month_name[i] = Month_name_EUCKR[lld->lang_id][i];
}
for (i = 0; i < 12; i++)
{
lld->am_pm[i] = Am_Pm_name_EUCKR[lld->lang_id][i];
}
}
else
{
for (i = 0; i < 7; i++)
{
lld->day_short_name[i] = Short_Day_name_ISO[lld->lang_id][i];
lld->day_name[i] = Day_name_ISO[lld->lang_id][i];
}
for (i = 0; i < 12; i++)
{
lld->month_short_name[i] = Short_Month_name_ISO[lld->lang_id][i];
lld->month_name[i] = Month_name_ISO[lld->lang_id][i];
}
for (i = 0; i < 12; i++)
{
lld->am_pm[i] = Am_Pm_name_ISO[lld->lang_id][i];
}
}
lld->month_parse_order = Month_name_parse_order[lld->lang_id];
lld->month_short_parse_order = Short_Month_name_parse_order[lld->lang_id];
lld->day_parse_order = Day_name_parse_order[lld->lang_id];
lld->day_short_parse_order = Short_Day_name_parse_order[lld->lang_id];
lld->am_pm_parse_order = AM_PM_parse_order[lld->lang_id];
}
/*
* db_value_to_enumeration_value () - convert a DB_VALUE to an enumeration
* value ignoring out of range values
* return : error code or NO_ERROR
* src (in) : source value
* result (in/out) : enumeration value
* enum_domain (in) : enumeration domain
*
* Note: The result value will hold the index of the enum if src has a
* corespondent in the enumeration values or 0 otherwise.
*/
int
db_value_to_enumeration_value (const DB_VALUE * src, DB_VALUE * result, const TP_DOMAIN * enum_domain)
{
TP_DOMAIN_STATUS status = DOMAIN_COMPATIBLE;
if (src == NULL || result == NULL || enum_domain == NULL)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return ER_FAILED;
}
db_make_null (result);
status = tp_value_cast (src, result, enum_domain, false);
if (status != DOMAIN_COMPATIBLE)
{
if (status == DOMAIN_ERROR)
{
return ER_FAILED;
}
db_make_enumeration (result, DB_ENUM_OVERFLOW_VAL, NULL, 0, TP_DOMAIN_CODESET (enum_domain),
TP_DOMAIN_COLLATION (enum_domain));
er_clear ();
/* continue, no error */
}
return NO_ERROR;
}
/*
* db_inet_aton () - convert a string formatted IPv4 address
* to a number formatted IPv4 address
* Arguments:
* string (in) : source ip string
* result_numbered_ip (in/out) : result number
*
* Returns: int
* error code or NO_ERROR
*
* Errors:
* ER_OBJ_INVALID_ARGUMENTS
* ER_QSTR_INVALID_DATA_TYPE
* ER_OPFUNC_INET_NTOA_ARG
* ER_OUT_OF_VIRTUAL_MEMORY
*
* Note: the ip "226.000.000.037" is 226.0.0.31, not 226.0.0.37
* support "0x3d.037.12.25" format
*/
int
db_inet_aton (DB_VALUE * result_numbered_ip, const DB_VALUE * string)
{
int error_code = NO_ERROR;
DB_BIGINT numbered_ip = (DB_BIGINT) 0;
const char *ip_string = NULL;
char *local_ipstring = NULL;
char *local_ipslice = NULL;
char *local_pivot = NULL;
int slice = 0;
int result = 0;
const int ipsegmax = 256;
DB_BIGINT ipbase;
int slice_count = 0;
int cnt;
char *temp_tok;
if (string == NULL || result_numbered_ip == NULL)
{
error_code = ER_OBJ_INVALID_ARGUMENTS;
goto error;
}
if (DB_IS_NULL (string))
{
db_make_null (result_numbered_ip);
return NO_ERROR;
}
if (!is_char_string (string))
{
error_code = ER_QSTR_INVALID_DATA_TYPE;
goto error;
}
/* ip format is controlled internally; use byte size directly */
ip_string = db_get_char (string);
cnt = db_get_string_size (string);
local_ipstring = (char *) db_private_alloc (NULL, cnt + 1);
if (local_ipstring == NULL)
{
error_code = ER_OUT_OF_VIRTUAL_MEMORY;
goto error;
}
memcpy (local_ipstring, ip_string, cnt);
local_ipstring[cnt] = '\0';
ipbase = (DB_BIGINT) ipsegmax *ipsegmax * ipsegmax;
for (temp_tok = local_ipstring;; temp_tok = NULL)
{
/* use ". \t" to be more tolerable of input format. */
local_ipslice = strtok_r (temp_tok, ". \t", &local_pivot);
if (local_ipslice == NULL)
{
break;
}
if (!is_valid_ip_slice (local_ipslice))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OPFUNC_INET_ATON_ARG, 1, ip_string);
error_code = ER_OPFUNC_INET_ATON_ARG;
goto error;
}
result = parse_int (&slice, local_ipslice, 0);
if (result != 0 || slice < 0 || slice >= ipsegmax)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OPFUNC_INET_ATON_ARG, 1, ip_string);
error_code = ER_OPFUNC_INET_ATON_ARG;
goto error;
}
numbered_ip += slice * ipbase;
ipbase /= ipsegmax;
slice_count++;
}
if (numbered_ip < 0 || numbered_ip > (DB_BIGINT) ipsegmax * ipsegmax * ipsegmax * ipsegmax || slice_count != 4)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OPFUNC_INET_ATON_ARG, 1, ip_string);
error_code = ER_OPFUNC_INET_ATON_ARG;
goto error;
}
db_private_free (NULL, local_ipstring);
db_make_bigint (result_numbered_ip, numbered_ip);
return NO_ERROR;
error:
if (local_ipstring != NULL)
{
db_private_free (NULL, local_ipstring);
}
db_make_null (result_numbered_ip);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_clear ();
return NO_ERROR;
}
return error_code;
}
/*
* db_inet_ntoa () - convert a number formatted IPv4 address
* to a string formatted IPv4 address
* Arguments:
* number (in) : source numbered ip
* result_ip_string (in/out) : result ip string
*
* Returns: int
* error code or NO_ERROR
*
* Errors:
* ER_QSTR_INVALID_DATA_TYPE
* ER_OBJ_INVALID_ARGUMENTS
* ER_OPFUNC_INET_NTOA_ARG
*
* Note:
*/
int
db_inet_ntoa (DB_VALUE * result_ip_string, const DB_VALUE * number)
{
int error_code = NO_ERROR;
DB_TYPE number_type = DB_TYPE_UNKNOWN;
DB_BIGINT ip_number = 0;
char ip_string[16] = { '\0' };
const int ip_seg_string_cnt = 4;
const DB_BIGINT ipmax = (DB_BIGINT) 256 * 256 * 256 * 256;
const unsigned int ipv4_mask[] = { 0xFF000000, 0xFF0000, 0xFF00, 0xFF };
const unsigned int ipfactor[] = { 256 * 256 * 256, 256 * 256, 256, 1 };
unsigned int slice;
int i;
int ret_string_len;
char *res_p_str;
if (number == NULL || result_ip_string == NULL)
{
error_code = ER_OBJ_INVALID_ARGUMENTS;
goto error;
}
if (DB_IS_NULL (number))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OPFUNC_INET_NTOA_ARG, 1, (long long int) ip_number);
error_code = ER_OPFUNC_INET_NTOA_ARG;
goto error;
}
number_type = DB_VALUE_DOMAIN_TYPE (number);
if (number_type != DB_TYPE_BIGINT)
{
error_code = ER_QSTR_INVALID_DATA_TYPE;
goto error;
}
ip_number = db_get_bigint (number);
if (ip_number > ipmax || ip_number < 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OPFUNC_INET_NTOA_ARG, 1, (long long int) ip_number);
error_code = ER_OPFUNC_INET_NTOA_ARG;
goto error;
}
ret_string_len = 0;
for (i = 0; i < 4; i++)
{
slice = (unsigned int) ((ip_number & ipv4_mask[i]) / ipfactor[i]);
ret_string_len += snprintf (ip_string + ret_string_len, ip_seg_string_cnt, "%u", slice);
if (i < 3)
{
ip_string[ret_string_len++] = '.';
}
}
ip_string[ret_string_len] = '\0';
/* return string */
res_p_str = (char *) db_private_alloc (NULL, ret_string_len + 1);
if (res_p_str == NULL)
{
error_code = ER_OUT_OF_VIRTUAL_MEMORY;
goto error;
}
memcpy (res_p_str, ip_string, ret_string_len + 1);
db_make_string (result_ip_string, res_p_str);
result_ip_string->need_clear = true;
return NO_ERROR;
error:
db_make_null (result_ip_string);
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_clear ();
return NO_ERROR;
}
return error_code;
}
/*
* is_valid_ip_slice () - check whether ip slice is valid
*
* Arguments:
* ipslice (in) : IP slice
*
* Returns: true or false
*/
static bool
is_valid_ip_slice (const char *ipslice)
{
int pos = 0;
int base_type = 10; /* base type can be 8(oct), 10(dec), 16(hex) */
assert (ipslice != NULL);
if (ipslice[0] == '\0')
{
return false;
}
if (ipslice[0] == '0')
{
if (char_tolower (ipslice[1]) == 'x')
{
if (ipslice[2] == '\0')
{
return false;
}
base_type = 16;
pos = 2;
}
else if (ipslice[1] != '\0')
{
base_type = 8;
pos = 1;
}
}
while (ipslice[pos] != '\0')
{
if (base_type == 10)
{
if (!char_isdigit (ipslice[pos]))
{
return false;
}
}
else if (base_type == 8)
{
if (!('0' <= ipslice[pos] && ipslice[pos] <= '7'))
{
return false;
}
}
else
{ /* base_type = 16 */
if (!char_isxdigit (ipslice[pos]))
{
return false;
}
}
pos++;
}
return true;
}
/*
* db_get_date_format () -
* Returns: error number
* format_str(in):
* format(in/out):
*
*/
int
db_get_date_format (const DB_VALUE * format_str, TIMESTAMP_FORMAT * format)
{
const char *fmt_str_ptr, *next_fmt_str_ptr, *last_fmt;
INTL_CODESET codeset;
char stack_buf_format[64];
char *initial_buf_format = NULL;
bool do_free_buf_format = false;
int format_size;
int error = NO_ERROR;
assert (format_str != NULL);
assert (format != NULL);
if (DB_IS_NULL (format_str))
{
*format = DT_INVALID;
goto end;
}
if (is_char_string (format_str) == false)
{
error = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
*format = DT_INVALID;
goto end;
}
if (db_get_string_size (format_str) == 0)
{
error = ER_QSTR_EMPTY_STRING;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
*format = DT_INVALID;
goto end;
}
if (db_get_string_size (format_str) > MAX_TOKEN_SIZE)
{
error = ER_QSTR_SRC_TOO_LONG;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
*format = DT_INVALID;
goto end;
}
codeset = db_get_string_codeset (format_str);
error =
db_check_or_create_null_term_string (format_str, stack_buf_format, sizeof (stack_buf_format), true, true,
&initial_buf_format, &do_free_buf_format);
if (error != NO_ERROR)
{
*format = DT_INVALID;
goto end;
}
fmt_str_ptr = initial_buf_format;
last_fmt = fmt_str_ptr + strlen (fmt_str_ptr);
/* Skip space, tab, CR */
while (fmt_str_ptr < last_fmt && strchr (WHITE_CHARS, *fmt_str_ptr))
{
fmt_str_ptr++;
}
next_fmt_str_ptr = NULL;
*format = get_next_format (fmt_str_ptr, codeset, DB_TYPE_DATETIME, &format_size, &next_fmt_str_ptr);
if (next_fmt_str_ptr != NULL && *next_fmt_str_ptr != 0)
{
error = ER_QSTR_INVALID_FORMAT;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
*format = DT_INVALID;
goto end;
}
end:
if (do_free_buf_format)
{
assert (initial_buf_format != NULL);
db_private_free (NULL, initial_buf_format);
}
return error;
}
/*
* db_get_cs_coll_info() - get codeset or collation from a value
*
* return: status
* result(out): result (string type)
* val(in): input value
* mode(in): 0 : get charset, 1 : get collation
*
*/
int
db_get_cs_coll_info (DB_VALUE * result, const DB_VALUE * val, const int mode)
{
int status = NO_ERROR;
assert (result != NULL);
assert (val != NULL);
if (DB_IS_NULL (val))
{
db_make_null (result);
}
else if (!TP_TYPE_HAS_COLLATION (DB_VALUE_TYPE (val)))
{
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
er_clear ();
db_make_null (result);
}
else
{
status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, status, 0);
}
}
else
{
int cs, coll;
if (TP_IS_CHAR_TYPE (DB_VALUE_TYPE (val)))
{
cs = db_get_string_codeset (val);
coll = db_get_string_collation (val);
}
else
{
assert (DB_VALUE_TYPE (val) == DB_TYPE_ENUMERATION);
cs = db_get_enum_codeset (val);
coll = db_get_enum_collation (val);
}
if (mode == 0)
{
db_make_string (result, lang_charset_cubrid_name ((INTL_CODESET) cs));
}
else
{
assert (mode == 1);
db_make_string (result, lang_get_collation_name (coll));
}
}
return status;
}
/*
* db_string_index_prefix () -
*/
int
db_string_index_prefix (const DB_VALUE * string1, const DB_VALUE * string2, const DB_VALUE * index_type,
DB_VALUE * prefix_index)
{
int error_status = NO_ERROR;
TP_DOMAIN key_domain;
DB_VALUE db_cmp_res;
int cmp_res;
assert (string1 != (DB_VALUE *) NULL);
assert (string2 != (DB_VALUE *) NULL);
assert (index_type != (DB_VALUE *) NULL);
if (DB_IS_NULL (string1) || DB_IS_NULL (string2) || DB_IS_NULL (index_type))
{
if (QSTR_IS_BIT (DB_VALUE_DOMAIN_TYPE (string1)))
{
error_status = db_value_domain_init (prefix_index, DB_TYPE_VARBIT, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
}
else
{
error_status = db_value_domain_init (prefix_index, DB_TYPE_VARCHAR, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
}
return error_status;
}
if (!QSTR_IS_ANY_CHAR_OR_BIT (DB_VALUE_DOMAIN_TYPE (string1))
|| !QSTR_IS_ANY_CHAR_OR_BIT (DB_VALUE_DOMAIN_TYPE (string2)) || !is_char_string (index_type))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
if ((qstr_get_category (string1) != qstr_get_category (string2))
|| (db_get_string_codeset (string1) != db_get_string_codeset (string2)))
{
error_status = ER_QSTR_INCOMPATIBLE_CODE_SETS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
key_domain.is_desc = false;
if (strncasecmp (db_get_string (index_type), "d", 1) == 0)
{
key_domain.is_desc = true;
}
error_status = db_string_compare (string1, string2, &db_cmp_res);
if (error_status != NO_ERROR)
{
return error_status;
}
cmp_res = db_get_int (&db_cmp_res);
if ((key_domain.is_desc && cmp_res <= 0) || (!key_domain.is_desc && cmp_res >= 0))
{
error_status = ER_OBJ_INVALID_ARGUMENTS;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
return error_status;
}
error_status = db_string_unique_prefix (string1, string2, prefix_index, &key_domain);
return error_status;
}
/*
* db_string_to_base64 () - Using base64 to encode arbitrary input
* return: int(NO_ERROR if successful, other error status if fail)
* src(in): source which holds plain-text string
* result(in/out): dest which holds encoded buffer
*
* Note: handling of special cases:
* 1. source string is NULL, result is NULL
* 2. source string is empty string, result is empty string
*/
int
db_string_to_base64 (DB_VALUE const *src, DB_VALUE * result)
{
int error_status, encode_len, src_len;
const unsigned char *src_buf = NULL;
unsigned char *encode_buf = NULL;
DB_TYPE val_type;
assert (src != (DB_VALUE *) NULL);
assert (result != (DB_VALUE *) NULL);
error_status = NO_ERROR;
if (DB_IS_NULL (src))
{
db_make_null (result);
return error_status;
}
src_buf = DB_GET_UCHAR (src);
/* length in bytes */
src_len = db_get_string_size (src);
assert (src_len >= 0);
/* if input is empty string, output is also empty string */
if (src_len == 0)
{
error_status =
db_string_make_empty_typed_string (result, DB_TYPE_VARCHAR, 0, db_get_string_codeset (src),
db_get_string_collation (src));
if (error_status != NO_ERROR)
{
assert_release (false);
return error_status;
}
return NO_ERROR;
}
val_type = DB_VALUE_DOMAIN_TYPE (src);
if (QSTR_IS_ANY_CHAR (val_type))
{
/* currently base64_encode always returns NO_ERROR except for memory buffer allocation fail */
error_status = base64_encode (src_buf, src_len, &encode_buf, &encode_len);
if (error_status == NO_ERROR)
{
qstr_make_typed_string (DB_TYPE_VARCHAR, result, encode_len, (char *) encode_buf, encode_len,
db_get_string_codeset (src), db_get_string_collation (src));
result->need_clear = true;
return NO_ERROR;
}
}
else /* val_type != QSTR_IS_ANY_CHAR */
{
error_status = ER_QSTR_INVALID_DATA_TYPE; /* reset error code */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
db_make_null (result);
er_clear (); /* forget previous error */
return NO_ERROR;
}
else
{
return error_status;
}
}
/*
* db_string_from_base64 () - Convert a buffer into plain-text by base64 decoding
* There is no assumption the input is base64 encoded,
* in this case, result is NULL
* return: int(NO_ERROR if successful, other error status if fail)
* src(in): source which holds encoded buffer
* result(in/out): dest which holds plain-text string
*
* Note: handling of special cases:
* 1. source string is NULL, result is NULL
* 2. source string is empty string, result is empty string
* 3. source string contains invalid base64 encoded character,
* result is NULL
* 4. source string has insufficient length even some bytes have been
* decoded, result is NULL
*/
int
db_string_from_base64 (DB_VALUE const *src, DB_VALUE * result)
{
int error_status, err, decode_len, src_len;
const unsigned char *src_buf = NULL;
unsigned char *decode_buf = NULL;
DB_TYPE val_type;
assert (src != (DB_VALUE *) NULL);
assert (result != (DB_VALUE *) NULL);
error_status = NO_ERROR;
/* source is NULL */
if (DB_IS_NULL (src))
{
db_make_null (result);
return NO_ERROR;
}
src_buf = DB_GET_UCHAR (src);
/* length in bytes */
src_len = db_get_string_size (src);
assert (src_len >= 0);
/* source is empty string */
if (src_len == 0)
{
error_status =
db_string_make_empty_typed_string (result, DB_TYPE_VARCHAR, 0, db_get_string_codeset (src),
db_get_string_collation (src));
if (error_status != NO_ERROR)
{
assert_release (false);
goto error_handling;
}
return NO_ERROR;
}
val_type = DB_VALUE_DOMAIN_TYPE (src);
if (QSTR_IS_ANY_CHAR (val_type))
{
err = base64_decode (src_buf, src_len, &decode_buf, &decode_len);
switch (err)
{
case BASE64_EMPTY_INPUT:
error_status =
db_string_make_empty_typed_string (result, DB_TYPE_VARCHAR, 0, db_get_string_codeset (src),
db_get_string_collation (src));
if (error_status != NO_ERROR)
{
assert_release (false);
goto error_handling;
}
break;
case NO_ERROR:
qstr_make_typed_string (DB_TYPE_VARCHAR, result, decode_len, (char *) decode_buf, decode_len,
db_get_string_codeset (src), db_get_string_collation (src));
result->need_clear = true;
break;
case BASE64_INVALID_INPUT:
error_status = ER_QSTR_INVALID_FORMAT; /* reset error code */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto error_handling;
default:
assert (er_errid () != NO_ERROR);
error_status = err;
goto error_handling;
}
}
else /* val_type != QSTR_IS_ANY_CHAR */
{
error_status = ER_QSTR_INVALID_DATA_TYPE; /* reset error code */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
goto error_handling;
}
assert (error_status == NO_ERROR);
return error_status;
error_handling:
if (prm_get_bool_value (PRM_ID_RETURN_NULL_ON_FUNCTION_ERRORS))
{
db_make_null (result);
er_clear (); /* forget previous error */
return NO_ERROR;
}
else
{
return error_status;
}
}
/*
* db_string_extract_dbval () -
* return: NO_ERROR, or ER_code
* extr_operand(in) : Specifies datetime field to be extracted
* dbval(in) : Extract source db_value node
* res(out) : Resultant db_value node
* domain(in) :
*
* Note: Extract a datetime field from db_value.
*/
int
db_string_extract_dbval (const MISC_OPERAND extr_operand, DB_VALUE * dbval_p, DB_VALUE * result_p, TP_DOMAIN * domain_p)
{
DB_TYPE dbval_type;
DB_DATE date;
DB_TIME time;
DB_UTIME *utime;
DB_DATETIME datetime, *datetime_p;
DB_TIMESTAMPTZ *ts_tz_p;
DB_DATETIMETZ *dt_tz_p;
int extvar[NUM_MISC_OPERANDS];
int err = NO_ERROR;
dbval_type = DB_VALUE_DOMAIN_TYPE (dbval_p);
if (TP_DOMAIN_TYPE (domain_p) == DB_TYPE_NULL || DB_IS_NULL (dbval_p))
{
return NO_ERROR;
}
switch (dbval_type)
{
case DB_TYPE_TIME:
time = *db_get_time (dbval_p);
db_time_decode (&time, &extvar[HOUR], &extvar[MINUTE], &extvar[SECOND]);
break;
case DB_TYPE_DATE:
date = *db_get_date (dbval_p);
db_date_decode (&date, &extvar[MONTH], &extvar[DAY], &extvar[YEAR]);
break;
case DB_TYPE_TIMESTAMP:
case DB_TYPE_TIMESTAMPLTZ:
utime = db_get_timestamp (dbval_p);
(void) db_timestamp_decode_ses (utime, &date, &time);
if (extr_operand == YEAR || extr_operand == MONTH || extr_operand == DAY)
{
db_date_decode (&date, &extvar[MONTH], &extvar[DAY], &extvar[YEAR]);
}
else
{
db_time_decode (&time, &extvar[HOUR], &extvar[MINUTE], &extvar[SECOND]);
}
break;
case DB_TYPE_TIMESTAMPTZ:
ts_tz_p = db_get_timestamptz (dbval_p);
err = db_timestamp_decode_w_tz_id (&ts_tz_p->timestamp, &ts_tz_p->tz_id, &date, &time);
if (err != NO_ERROR)
{
break;
}
if (extr_operand == YEAR || extr_operand == MONTH || extr_operand == DAY)
{
db_date_decode (&date, &extvar[MONTH], &extvar[DAY], &extvar[YEAR]);
}
else
{
db_time_decode (&time, &extvar[HOUR], &extvar[MINUTE], &extvar[SECOND]);
}
break;
case DB_TYPE_DATETIME:
datetime_p = db_get_datetime (dbval_p);
db_datetime_decode (datetime_p, &extvar[MONTH], &extvar[DAY], &extvar[YEAR], &extvar[HOUR], &extvar[MINUTE],
&extvar[SECOND], &extvar[MILLISECOND]);
break;
case DB_TYPE_DATETIMELTZ:
datetime_p = db_get_datetime (dbval_p);
err = tz_datetimeltz_to_local (datetime_p, &datetime);
if (err != NO_ERROR)
{
break;
}
db_datetime_decode (&datetime, &extvar[MONTH], &extvar[DAY], &extvar[YEAR], &extvar[HOUR], &extvar[MINUTE],
&extvar[SECOND], &extvar[MILLISECOND]);
break;
case DB_TYPE_DATETIMETZ:
dt_tz_p = db_get_datetimetz (dbval_p);
err = tz_utc_datetimetz_to_local (&dt_tz_p->datetime, &dt_tz_p->tz_id, &datetime);
if (err != NO_ERROR)
{
break;
}
db_datetime_decode (&datetime, &extvar[MONTH], &extvar[DAY], &extvar[YEAR], &extvar[HOUR], &extvar[MINUTE],
&extvar[SECOND], &extvar[MILLISECOND]);
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
{
DB_UTIME utime_s;
DB_DATETIME datetime_s;
const char *str_date = db_get_string (dbval_p);
int str_date_len = db_get_string_size (dbval_p);
switch (extr_operand)
{
case YEAR:
case MONTH:
case DAY:
if (db_string_to_date_ex (str_date, str_date_len, &date) == NO_ERROR)
{
db_date_decode (&date, &extvar[MONTH], &extvar[DAY], &extvar[YEAR]);
break;
}
if (db_string_to_timestamp_ex (str_date, str_date_len, &utime_s) == NO_ERROR)
{
(void) db_timestamp_decode_ses (&utime_s, &date, &time);
db_date_decode (&date, &extvar[MONTH], &extvar[DAY], &extvar[YEAR]);
break;
}
if (db_string_to_datetime_ex (str_date, str_date_len, &datetime_s) == NO_ERROR)
{
db_datetime_decode (&datetime_s, &extvar[MONTH], &extvar[DAY], &extvar[YEAR], &extvar[HOUR],
&extvar[MINUTE], &extvar[SECOND], &extvar[MILLISECOND]);
break;
}
/* no date/time can be extracted from string, error */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_QPROC_INVALID_DATATYPE;
case HOUR:
case MINUTE:
case SECOND:
if (db_string_to_time_ex (str_date, str_date_len, &time) == NO_ERROR)
{
db_time_decode (&time, &extvar[HOUR], &extvar[MINUTE], &extvar[SECOND]);
break;
}
if (db_string_to_timestamp_ex (str_date, str_date_len, &utime_s) == NO_ERROR)
{
(void) db_timestamp_decode_ses (&utime_s, &date, &time);
db_time_decode (&time, &extvar[HOUR], &extvar[MINUTE], &extvar[SECOND]);
break;
}
[[fallthrough]];
case MILLISECOND:
if (db_string_to_datetime_ex (str_date, str_date_len, &datetime_s) == NO_ERROR)
{
db_datetime_decode (&datetime_s, &extvar[MONTH], &extvar[DAY], &extvar[YEAR], &extvar[HOUR],
&extvar[MINUTE], &extvar[SECOND], &extvar[MILLISECOND]);
break;
}
/* no date/time can be extracted from string, error */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_QPROC_INVALID_DATATYPE;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_QPROC_INVALID_DATATYPE;
}
}
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_QPROC_INVALID_DATATYPE;
}
if (err == NO_ERROR)
{
db_make_int (result_p, extvar[extr_operand]);
}
return err;
}
/*
* db_new_time () - converts the time_val from the source_timezone into
* the destination timezone
*
* return error code or NO_ERROR
* time_val (in) : time value (datetime or time)
* tz_source (in) : source timezone string
* tz_dest (in) : dest timezone string
* result_time (out) : result
*/
int
db_new_time (DB_VALUE * time_val, DB_VALUE * tz_source, DB_VALUE * tz_dest, DB_VALUE * result_time)
{
int error = NO_ERROR, len_source, len_dest;
DB_DATETIME *datetime = NULL;
DB_TIME *time = NULL;
const char *t_source, *t_dest;
/*
* Assert that DB_VALUE structures have been allocated.
*/
assert (time_val != (DB_VALUE *) NULL);
assert (tz_source != (DB_VALUE *) NULL);
assert (tz_dest != (DB_VALUE *) NULL);
assert (result_time != (DB_VALUE *) NULL);
if (DB_IS_NULL (time_val) || DB_IS_NULL (tz_source) || DB_IS_NULL (tz_dest))
{
db_make_null (result_time);
return NO_ERROR;
}
t_source = db_get_string (tz_source);
t_dest = db_get_string (tz_dest);
len_source = db_get_string_size (tz_source);
len_dest = db_get_string_size (tz_dest);
if (len_source < 0)
{
len_source = strlen (t_source);
}
if (len_dest < 0)
{
len_dest = strlen (t_dest);
}
switch (DB_VALUE_TYPE (time_val))
{
case DB_TYPE_DATETIME:
{
DB_DATETIME result;
int month, day, year, julian_date;
datetime = db_get_datetime (time_val);
db_date_decode (&(datetime->date), &month, &day, &year);
julian_date = julian_encode (month, day, year);
datetime->date = julian_date;
error = tz_conv_tz_datetime_w_zone_name (datetime, t_source, len_source, t_dest, len_dest, &result);
if (error != NO_ERROR)
{
return error;
}
db_make_datetime (result_time, &result);
}
break;
case DB_TYPE_TIME:
{
DB_TIME time_res;
int hour, min, sec;
time = db_get_time (time_val);
error = tz_conv_tz_time_w_zone_name (time, t_source, len_source, t_dest, len_dest, &time_res);
if (error != NO_ERROR)
{
return error;
}
db_time_decode (&time_res, &hour, &min, &sec);
db_make_time (result_time, hour, min, sec);
}
break;
default:
error = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
break;
}
return error;
}
/*
* db_tz_offset () - retrieve the timezone offset for src_str source timezone
*
* return: error or no error
* src_str(in): source DB_VALUE timezone string or offset
* date_time(in): current UTC datetime
* result_str(out): result DB_VALUE string
*/
int
db_tz_offset (const DB_VALUE * src_str, DB_VALUE * result_str, DB_DATETIME * datetime)
{
int error_status = NO_ERROR;
DB_TYPE str_type;
char *res;
/*
* Assert that DB_VALUE structures have been allocated.
*/
assert (src_str != (DB_VALUE *) NULL);
assert (result_str != (DB_VALUE *) NULL);
/*
* Categorize the two input parameters and check for errors.
* Verify that the parameters are both character strings.
*/
str_type = DB_VALUE_DOMAIN_TYPE (src_str);
if (DB_IS_NULL (src_str))
{
db_make_null (result_str);
}
else if (!QSTR_IS_CHAR (str_type))
{
error_status = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 0);
}
/*
* If the input parameters have been properly validated, then
* we are ready to operate.
*/
else
{
int len = db_get_string_size (src_str);
if (len < 0)
{
len = strlen (db_get_string (src_str));
}
res = (char *) db_private_alloc (NULL, MAX_LEN_OFFSET);
if (res == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_status, 1, (size_t) 0);
return error_status;
}
error_status = tz_get_timezone_offset (db_get_string (src_str), len, res, datetime);
if (error_status == NO_ERROR)
{
db_make_varchar (result_str, TP_FLOATING_PRECISION_VALUE, res, strlen (res), LANG_SYS_CODESET,
LANG_SYS_COLLATION);
result_str->need_clear = true;
}
else
{
db_private_free (NULL, res);
}
}
return error_status;
}
/*
* db_from_tz () - adds timezone information to the time_val
* Return: error code or NO_ERROR
* time_val (in) : time value (datetime, time or timestamp)
* tz (in) : timezone string
* time_val_with_tz (out) : timeval with timezone information
*/
int
db_from_tz (DB_VALUE * time_val, DB_VALUE * tz, DB_VALUE * time_val_with_tz)
{
const char *timezone_str = NULL;
int len_timezone, error = NO_ERROR;
DB_DATETIME *datetime = NULL;
/*
* Assert that DB_VALUE structures have been allocated.
*/
assert (time_val != (DB_VALUE *) NULL);
assert (tz != (DB_VALUE *) NULL);
assert (time_val_with_tz != (DB_VALUE *) NULL);
if (DB_IS_NULL (time_val) || DB_IS_NULL (tz))
{
db_make_null (time_val_with_tz);
return NO_ERROR;
}
timezone_str = db_get_string (tz);
len_timezone = db_get_string_size (tz);
if (len_timezone < 0)
{
len_timezone = strlen (timezone_str);
}
switch (DB_VALUE_TYPE (time_val))
{
case DB_TYPE_DATETIME:
{
DB_DATETIMETZ result;
TZ_REGION region;
datetime = db_get_datetime (time_val);
error = tz_str_to_region (timezone_str, len_timezone, ®ion);
if (error != NO_ERROR)
{
return error;
}
error = tz_create_datetimetz (datetime, NULL, 0, ®ion, &result, NULL);
if (error != NO_ERROR)
{
return error;
}
db_make_datetimetz (time_val_with_tz, &result);
}
break;
default:
error = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
break;
}
return error;
}
/*
* parse_tzd() - finds the length of the daylight saving time information
* It stops either on first non alpha-numeric or when expected
* length is reached.
*
* Returns length of daylight
* str(in): string to search in
* max_expect_len(in): maximum length to expected
*/
static int
parse_tzd (const char *str, const int max_expect_len)
{
const char *p;
assert (max_expect_len > 1);
p = str;
if (*p == '-' || *p == '+')
{
p++;
}
while (p < str + max_expect_len && char_isalnum (*p))
{
p++;
}
return CAST_BUFLEN (p - str);
}
/*
* db_conv_tz () - Converts a timezone data type from one timezone library to another
*
* return ERROR or NO_ERROR
* time_val (in) : time value (datetimetz, datetimeltz, timestamptz, timestampltz)
* result_time (out) : result
*
*/
int
db_conv_tz (DB_VALUE * time_val, DB_VALUE * result_time)
{
int error = NO_ERROR;
DB_DATETIMETZ *datetimetz = NULL;
DB_DATETIME *datetime = NULL;
DB_TIMESTAMPTZ *timestamptz = NULL;
DB_TIMESTAMP *timestamp = NULL;
/*
* Assert that DB_VALUE structures have been allocated.
*/
assert (time_val != (DB_VALUE *) NULL);
assert (result_time != (DB_VALUE *) NULL);
if (DB_IS_NULL (time_val))
{
db_make_null (result_time);
return NO_ERROR;
}
switch (DB_VALUE_TYPE (time_val))
{
case DB_TYPE_DATETIMETZ:
{
DB_DATETIMETZ datetimetz_out;
datetimetz = db_get_datetimetz (time_val);
error = conv_tz (&datetimetz_out, datetimetz, DB_TYPE_DATETIMETZ);
if (error != NO_ERROR)
{
return error;
}
db_make_datetimetz (result_time, &datetimetz_out);
}
break;
case DB_TYPE_DATETIMELTZ:
{
DB_DATETIME datetime_out;
datetime = db_get_datetime (time_val);
error = conv_tz (&datetime_out, datetime, DB_TYPE_DATETIMELTZ);
if (error != NO_ERROR)
{
return error;
}
db_make_datetimeltz (result_time, &datetime_out);
}
break;
case DB_TYPE_TIMESTAMPTZ:
{
DB_TIMESTAMPTZ timestamptz_out;
timestamptz = db_get_timestamptz (time_val);
error = conv_tz (×tamptz_out, timestamptz, DB_TYPE_TIMESTAMPTZ);
if (error != NO_ERROR)
{
return error;
}
db_make_timestamptz (result_time, ×tamptz_out);
}
break;
case DB_TYPE_TIMESTAMPLTZ:
{
DB_TIMESTAMP timestamp_out;
timestamp = db_get_timestamp (time_val);
error = conv_tz (×tamp_out, timestamp, DB_TYPE_TIMESTAMPLTZ);
if (error != NO_ERROR)
{
return error;
}
db_make_timestampltz (result_time, timestamp_out);
}
break;
default:
error = ER_QSTR_INVALID_DATA_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
break;
}
return error;
}