* This file is part of the oGRAC project.
* Copyright (c) 2024 Huawei Technologies Co.,Ltd.
*
* oGRAC is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* func_hex.c
*
*
* IDENTIFICATION
* src/ogsql/function/func_hex.c
*
* -------------------------------------------------------------------------
*/
#include "func_hex.h"
#include "srv_instance.h"
status_t sql_bin2hex(sql_stmt_t *stmt, expr_node_t *func, bool32 hex_prefix, variant_t *result)
{
variant_t expr_var;
char *buf = NULL;
CM_POINTER3(stmt, func, result);
result->type = OG_TYPE_STRING;
result->is_null = OG_TRUE;
expr_node_t *expr_node = func->argument->root;
if (sql_exec_expr_node(stmt, expr_node, &expr_var) != OG_SUCCESS) {
return OG_ERROR;
}
SQL_CHECK_COLUMN_VAR(&expr_var, result);
if (expr_var.is_null) {
return OG_SUCCESS;
}
if (!OG_IS_VARLEN_TYPE(expr_var.type)) {
OG_THROW_ERROR(ERR_INVALID_FUNC_PARAMS, "parameter is not string or binary type.");
return OG_ERROR;
}
sql_keep_stack_variant(stmt, &expr_var);
result->v_text.len = expr_var.v_bin.size * 2 + 3;
if (result->v_text.len >= SIZE_K(64)) {
OG_THROW_ERROR(ERR_INVALID_FUNC_PARAMS, "length of value to bin2hex exceeds maxdium.");
return OG_ERROR;
}
OG_RETURN_IFERR(sql_push(stmt, result->v_text.len, (void **)&buf));
result->v_text.str = buf;
if (cm_bin2text(&expr_var.v_bin, hex_prefix, &result->v_text) != OG_SUCCESS) {
OGSQL_POP(stmt);
cm_set_error_loc(expr_node->loc);
return OG_ERROR;
}
result->is_null = OG_FALSE;
return OG_SUCCESS;
}
status_t sql_func_bin2hex(sql_stmt_t *stmt, expr_node_t *func, variant_t *res)
{
return sql_bin2hex(stmt, func, OG_TRUE, res);
}
status_t sql_verify_bin2hex(sql_verifier_t *verf, expr_node_t *func)
{
uint32 hex_len;
CM_POINTER2(verf, func);
if (sql_verify_func_node(verf, func, 1, 1, OG_INVALID_ID32) != OG_SUCCESS) {
return OG_ERROR;
}
func->datatype = OG_TYPE_STRING;
hex_len = cm_get_datatype_strlen(func->argument->root->datatype, func->argument->root->size) * 2 + 3;
func->size = (uint16)MIN(hex_len, SIZE_K(64) - 1);
return OG_SUCCESS;
}
static status_t sql_hex2bin(sql_stmt_t *stmt, expr_node_t *func, bool32 hex_prefix, variant_t *res)
{
expr_tree_t *arg = NULL;
variant_t var;
char *buf = NULL;
CM_POINTER3(stmt, func, res);
arg = func->argument;
CM_POINTER(arg);
SQL_EXEC_FUNC_ARG_EX(arg, &var, res);
if (!OG_IS_STRING_TYPE(var.type)) {
OG_SRC_THROW_ERROR(func->loc, ERR_INVALID_FUNC_PARAMS,
"the argument of the function must be a string variant.");
return OG_ERROR;
}
if (var.v_text.len == 0 && !g_instance->sql.enable_empty_string_null) {
res->v_bin.size = 0;
} else {
sql_keep_stack_variant(stmt, &var);
if (hex_prefix) {
if ((var.v_text.len < SQL_MIN_HEX_STR_LEN) || (cm_strcmpni(var.v_text.str, "0x", SQL_MIN_HEX_STR_LEN))) {
OG_SRC_THROW_ERROR(func->loc, ERR_VALUE_ERROR,
"the argument of the function is not a valid hex string.");
return OG_ERROR;
}
}
OG_RETURN_IFERR(sql_push(stmt, (var.v_text.len + 1) / 2, (void **)&buf));
res->v_bin.bytes = (uint8 *)buf;
if (cm_text2bin(&var.v_text, hex_prefix, &res->v_bin, (uint32)((var.v_text.len + 1) / 2)) != OG_SUCCESS) {
OGSQL_POP(stmt);
cm_set_error_loc(func->loc);
return OG_ERROR;
}
}
res->is_null = OG_FALSE;
res->type = g_instance->sql.string_as_hex_binary ? OG_TYPE_RAW : OG_TYPE_BINARY;
res->v_bin.is_hex_const = OG_FALSE;
return OG_SUCCESS;
}
static status_t sql_func_hex_real(double val_real, variant_t *res)
{
if (val_real >= ULLONG_MAX || val_real < LLONG_MIN) {
res->v_text.len = 2 * sizeof(uint64);
MEMS_RETURN_IFERR(memcpy_s(res->v_text.str, OG_MAX_INT64_STRLEN, "FFFFFFFFFFFFFFFF", res->v_text.len));
} else {
double val = cm_round_real(val_real, ROUND_HALF_UP);
int64 i64 = (int64)val;
double diff = (double)(i64) - val;
double min_diff = 0.00000001;
double min_double_for_overflow = -9.2233720368547747e+18;
double max_double_for_overflow = 9.2233720368547750e+18;
if (!(diff >= -min_diff && diff <= min_diff) || (val < min_double_for_overflow) ||
(val > max_double_for_overflow)) {
OG_THROW_ERROR(ERR_TYPE_OVERFLOW, "BIGINT");
return OG_ERROR;
}
cm_bigint2hex((uint64)(int64)val, &res->v_text);
}
return OG_SUCCESS;
}
static status_t sql_func_hex_num(sql_stmt_t *stmt, variant_t *var, variant_t *res)
{
uint64 val_uint64;
int64 val_int64;
OG_RETURN_IFERR(sql_push(stmt, OG_MAX_INT64_STRLEN, (void **)&res->v_text.str));
res->v_text.len = 0;
switch (var->type) {
case OG_TYPE_UINT32:
cm_bigint2hex((uint64)var->v_uint32, &res->v_text);
break;
case OG_TYPE_INTEGER:
cm_bigint2hex((uint64)var->v_int, &res->v_text);
break;
case OG_TYPE_BIGINT:
cm_bigint2hex((uint64)var->v_bigint, &res->v_text);
break;
case OG_TYPE_REAL:
OG_RETURN_IFERR(sql_func_hex_real(var->v_real, res));
break;
case OG_TYPE_NUMBER:
case OG_TYPE_DECIMAL:
case OG_TYPE_NUMBER2:
if (cm_dec_to_uint64(&var->v_dec, &val_uint64, ROUND_HALF_UP) == OG_SUCCESS) {
cm_bigint2hex(val_uint64, &res->v_text);
} else if (cm_dec_to_int64(&var->v_dec, &val_int64, ROUND_HALF_UP) == OG_SUCCESS) {
cm_bigint2hex((uint64)val_int64, &res->v_text);
} else {
res->v_text.len = 2 * sizeof(uint64);
MEMS_RETURN_IFERR(
memcpy_s(res->v_text.str, OG_MAX_INT64_STRLEN, "FFFFFFFFFFFFFFFF", res->v_text.len));
}
break;
default:
OG_THROW_ERROR(ERR_INVALID_FUNC_PARAMS, "Don't support the argument datatype of hex.");
return OG_ERROR;
}
return OG_SUCCESS;
}
static status_t sql_func_hex_string(sql_stmt_t *stmt, variant_t *var, variant_t *res)
{
uint32 buff_len = var->v_text.len * 2 + 1;
if (buff_len >= SIZE_K(64)) {
OG_THROW_ERROR(ERR_INVALID_FUNC_PARAMS, "length of value to hex exceeds maxdium.");
return OG_ERROR;
}
OG_RETURN_IFERR(sql_push(stmt, buff_len, (void **)&res->v_text.str));
res->v_text.len = buff_len;
return cm_bin2text(&var->v_bin, OG_FALSE, &res->v_text);
}
static inline status_t sql_func_hex_bin(sql_stmt_t *stmt, variant_t *var, variant_t *res)
{
return sql_func_hex_string(stmt, var, res);
}
status_t sql_func_hex(sql_stmt_t *stmt, expr_node_t *func, variant_t *res)
{
variant_t var;
CM_POINTER3(stmt, func, res);
expr_tree_t *arg = func->argument;
CM_POINTER(arg);
SQL_EXEC_FUNC_ARG_EX(arg, &var, res);
res->type = OG_TYPE_STRING;
res->is_null = OG_FALSE;
if (OG_IS_STRING_TYPE(var.type)) {
sql_keep_stack_variant(stmt, &var);
return sql_func_hex_string(stmt, &var, res);
} else if (OG_IS_NUMERIC_TYPE(var.type)) {
return sql_func_hex_num(stmt, &var, res);
} else if (var.type == OG_TYPE_BINARY || var.type == OG_TYPE_VARBINARY || var.type == OG_TYPE_RAW) {
sql_keep_stack_variant(stmt, &var);
return sql_func_hex_bin(stmt, &var, res);
} else {
OG_SRC_THROW_ERROR(func->loc, ERR_INVALID_FUNC_PARAMS,
"the argument of hex must be a string or number variant.");
return OG_ERROR;
}
}
status_t sql_verify_hex(sql_verifier_t *verf, expr_node_t *func)
{
uint32 hex_len;
CM_POINTER2(verf, func);
OG_RETURN_IFERR(sql_verify_func_node(verf, func, 1, 1, OG_INVALID_ID32));
func->datatype = OG_TYPE_STRING;
hex_len = cm_get_datatype_strlen(func->argument->root->datatype, func->argument->root->size) * 2;
func->size = (uint16)MIN(hex_len, SIZE_K(64) - 1);
return OG_SUCCESS;
}
status_t sql_func_hex2bin(sql_stmt_t *stmt, expr_node_t *func, variant_t *res)
{
return sql_hex2bin(stmt, func, OG_TRUE, res);
}
status_t sql_func_hextoraw(sql_stmt_t *stmt, expr_node_t *func, variant_t *res)
{
status_t status = sql_hex2bin(stmt, func, OG_FALSE, res);
if (res->type != OG_TYPE_COLUMN) {
res->type = OG_TYPE_RAW;
}
return status;
}
status_t sql_verify_hex2bin(sql_verifier_t *verf, expr_node_t *func)
{
CM_POINTER2(verf, func);
if (sql_verify_func_node(verf, func, 1, 1, OG_INVALID_ID32) != OG_SUCCESS) {
return OG_ERROR;
}
func->datatype = g_instance->sql.string_as_hex_binary ? OG_TYPE_RAW : OG_TYPE_BINARY;
func->size = func->argument->root->size;
return OG_SUCCESS;
}
status_t sql_verify_hextoraw(sql_verifier_t *verf, expr_node_t *func)
{
OG_RETURN_IFERR(sql_verify_hex2bin(verf, func));
func->datatype = OG_TYPE_RAW;
return OG_SUCCESS;
}
static status_t sql_func_unhex_text2bin(sql_stmt_t *stmt, text_t *text, variant_t *res)
{
if (text->len == 0) {
res->is_null = OG_TRUE;
return OG_SUCCESS;
}
uint8 *buffer = NULL;
uint32 bytesLen = (text->len + 1) / 2;
OG_RETURN_IFERR(sql_push(stmt, bytesLen, (void **)&buffer));
uint32 pos = 0;
uint32 i = 0;
if (text->len % 2 == 1) {
buffer[pos] = cm_hex2int8((uint8)text->str[0]);
if (buffer[pos] == 0xFF) {
OGSQL_POP(stmt);
res->is_null = OG_TRUE;
return OG_SUCCESS;
}
i++;
pos++;
}
uint8 higher_half_byte = 0;
uint8 lower_half_byte = 0;
for (; i < text->len; i += 2) {
higher_half_byte = cm_hex2int8((uint8)text->str[i]);
lower_half_byte = cm_hex2int8((uint8)text->str[i + 1]);
if ((higher_half_byte == 0xFF) || (lower_half_byte == 0xFF)) {
OGSQL_POP(stmt);
res->is_null = OG_TRUE;
return OG_SUCCESS;
}
buffer[pos] = (uint8)(higher_half_byte << 4);
buffer[pos] += lower_half_byte;
pos++;
}
res->is_null = OG_FALSE;
res->v_bin.bytes = buffer;
res->v_bin.size = pos;
res->v_bin.is_hex_const = OG_FALSE;
return OG_SUCCESS;
}
status_t sql_func_unhex(sql_stmt_t *stmt, expr_node_t *func, variant_t *res)
{
CM_POINTER3(stmt, func, res);
expr_tree_t *arg = func->argument;
CM_POINTER(arg);
variant_t var;
SQL_EXEC_FUNC_ARG_EX(arg, &var, res);
res->type = g_instance->sql.string_as_hex_binary ? OG_TYPE_RAW : OG_TYPE_BINARY;
if (OG_IS_STRING_TYPE(var.type)) {
sql_keep_stack_variant(stmt, &var);
return sql_func_unhex_text2bin(stmt, &var.v_text, res);
} else {
OG_SRC_THROW_ERROR(func->loc, ERR_INVALID_FUNC_PARAMS, "the argument of unhex must be a string variant.");
return OG_ERROR;
}
}
status_t sql_verify_unhex(sql_verifier_t *verf, expr_node_t *func)
{
CM_POINTER2(verf, func);
OG_RETURN_IFERR(sql_verify_func_node(verf, func, 1, 1, OG_INVALID_ID32));
func->datatype = g_instance->sql.string_as_hex_binary ? OG_TYPE_RAW : OG_TYPE_BINARY;
func->size = cm_get_datatype_strlen(func->argument->root->datatype, func->argument->root->size);
return OG_SUCCESS;
}