/** * Copyright (c) 2021 OceanBase * OceanBase CE is licensed under Mulan PubL v2. * You can use this software according to the terms and conditions of the Mulan PubL v2. * You may obtain a copy of Mulan PubL v2 at: * http://license.coscl.org.cn/MulanPubL-2.0 * 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 PubL v2 for more details. */ #define USING_LOG_PREFIX SQL_ENG #include "ob_array_cast.h" #include "lib/json_type/ob_json_tree.h" #include "lib/json_type/ob_json_parse.h" namespace oceanbase { namespace sql { int ObVectorDataCast::cast(common::ObIAllocator &alloc, ObIArrayType *src, const ObCollectionTypeBase *elem_type, ObIArrayType *&dst, const ObCollectionTypeBase *dst_elem_type, ObCastMode mode) { int ret = OB_SUCCESS; const ObCollectionBasicType *src_type = dynamic_cast(elem_type); const ObCollectionBasicType *dst_type = dynamic_cast(dst_elem_type); if (OB_UNLIKELY(!src_type || !dst_type)) { ret = OB_ERR_ARRAY_TYPE_MISMATCH; LOG_WARN("unexpected status: invalid argument", K(ret), KP(src_type), KP(dst_type)); } else if (dim_cnt_ != src->size()) { ret = OB_ERR_INVALID_VECTOR_DIM; LOG_WARN("invalid array size", K(ret), K(dim_cnt_), K(src->size())); } for (int64_t i = 0; i < src->size() && OB_SUCC(ret); i++) { ObObj src_elem; if (src->get_format() != ArrayFormat::Vector && src->is_null(i)) { if (OB_FAIL(dst->push_null())) { LOG_WARN("failed to add null to array", K(ret), K(i)); } } else if (OB_FAIL(ObArrayCastUtils::cast_get_element(src, src_type, i, src_elem))) { LOG_WARN("failed to get cast element", K(ret), K(i)); } else { ObObjType dst_obj_type = dst_type->basic_meta_.get_obj_type(); ObObj res; ObCastCtx cast_ctx(&alloc, NULL, mode, ObCharset::get_system_collation()); if (OB_FAIL(ObObjCaster::to_type(dst_obj_type, cast_ctx, src_elem, res))) { LOG_WARN("failed to cast number to double type", K(ret)); } else { ObVectorData *dst_arr = static_cast(dst); if (OB_FAIL(dst_arr->push_back(res.get_float()))) { LOG_WARN("failed to push back array value", K(ret)); } } } } return ret; } int ObArrayFixedSizeCast::cast(common::ObIAllocator &alloc, ObIArrayType *src, const ObCollectionTypeBase *elem_type, ObIArrayType *&dst, const ObCollectionTypeBase *dst_elem_type, ObCastMode mode) { int ret = OB_SUCCESS; const ObCollectionBasicType *src_type = dynamic_cast(elem_type); const ObCollectionBasicType *dst_type = dynamic_cast(dst_elem_type); if (OB_UNLIKELY(!src_type || !dst_type)) { ret = OB_ERR_ARRAY_TYPE_MISMATCH; LOG_WARN("unexpected status: invalid argument", K(ret), KP(src_type), KP(dst_type)); } for (int64_t i = 0; i < src->size() && OB_SUCC(ret); i++) { ObObj src_elem; if (src->get_format() != ArrayFormat::Vector && src->is_null(i)) { if (OB_FAIL(dst->push_null())) { LOG_WARN("failed to add null to array", K(ret), K(i)); } } else if (OB_FAIL(ObArrayCastUtils::cast_get_element(src, src_type, i, src_elem))) { LOG_WARN("failed to get cast element", K(ret), K(i)); } else if (OB_FAIL(ObArrayCastUtils::cast_add_element(alloc, src_elem, dst, dst_type, mode))) { LOG_WARN("failed to cast and add element", K(ret)); } } return ret; } int ObArrayCastUtils::cast_get_element(ObIArrayType *src, const ObCollectionBasicType *elem_type, uint32_t idx, ObObj &src_elem) { int ret = OB_SUCCESS; ObObjType obj_type = elem_type->basic_meta_.get_obj_type(); switch (obj_type) { case ObTinyIntType: { ObArrayFixedSize *arr = static_cast *>(src); src_elem.set_tinyint((*arr)[idx]); break; } case ObSmallIntType: { ObArrayFixedSize *arr = static_cast *>(src); src_elem.set_smallint((*arr)[idx]); break; } case ObIntType: { ObArrayFixedSize *arr = static_cast *>(src); src_elem.set_int((*arr)[idx]); break; } case ObInt32Type: { ObArrayFixedSize *arr = static_cast *>(src); src_elem.set_int32((*arr)[idx]); break; } case ObUTinyIntType: { ObArrayFixedSize *arr = static_cast *>(src); src_elem.set_utinyint((*arr)[idx]); break; } case ObUSmallIntType: { ObArrayFixedSize *arr = static_cast *>(src); src_elem.set_usmallint((*arr)[idx]); break; } case ObUInt64Type: { ObArrayFixedSize *arr = static_cast *>(src); src_elem.set_uint64((*arr)[idx]); break; } case ObUInt32Type: { ObArrayFixedSize *arr = static_cast *>(src); src_elem.set_uint32((*arr)[idx]); break; } case ObDecimalIntType: { ObPrecision prec = elem_type->basic_meta_.get_precision(); if (get_decimalint_type(prec) == DECIMAL_INT_32) { ObArrayFixedSize *arr = static_cast *>(src); src_elem.set_decimal_int(sizeof(int32_t), arr->get_scale(), arr->get_decimal_int(idx)); } else if (get_decimalint_type(prec) == DECIMAL_INT_64) { ObArrayFixedSize *arr = static_cast *>(src); src_elem.set_decimal_int(sizeof(int64_t), arr->get_scale(), arr->get_decimal_int(idx)); } else if (get_decimalint_type(prec) == DECIMAL_INT_128) { ObArrayFixedSize *arr = static_cast *>(src); src_elem.set_decimal_int(sizeof(int128_t), arr->get_scale(), arr->get_decimal_int(idx)); } else if (get_decimalint_type(prec) == DECIMAL_INT_256) { ObArrayFixedSize *arr = static_cast *>(src); src_elem.set_decimal_int(sizeof(int256_t), arr->get_scale(), arr->get_decimal_int(idx)); } else if (get_decimalint_type(prec) == DECIMAL_INT_512) { ObArrayFixedSize *arr = static_cast *>(src); src_elem.set_decimal_int(sizeof(int512_t), arr->get_scale(), arr->get_decimal_int(idx)); } else { ret = OB_ERR_UNEXPECTED; OB_LOG(WARN, "unexpected precision", K(ret), K(prec)); } break; } case ObVarcharType : { ObArrayBinary *arr = static_cast(src); src_elem.set_varchar((*arr)[idx]); break; } case ObDoubleType: { ObArrayFixedSize *arr = static_cast *>(src); src_elem.set_double((*arr)[idx]); break; } case ObFloatType: { ObArrayFixedSize *arr = static_cast *>(src); src_elem.set_float((*arr)[idx]); break; } default: { ret = OB_ERR_UNEXPECTED; OB_LOG(WARN, "unexpected element type", K(ret), K(elem_type->basic_meta_.get_obj_type())); } } return ret; } int ObArrayCastUtils::cast_add_element(common::ObIAllocator &alloc, ObObj &src_elem, ObIArrayType *dst, const ObCollectionBasicType *dst_elem_type, ObCastMode mode) { int ret = OB_SUCCESS; ObCastCtx cast_ctx(&alloc, NULL, mode, ObCharset::get_system_collation()); ObObjType dst_obj_type = dst_elem_type->basic_meta_.get_obj_type(); ObObj res; if (OB_FAIL(ObObjCaster::to_type(dst_obj_type, cast_ctx, src_elem, res))) { LOG_WARN("failed to cast number to double type", K(ret)); } else { switch (dst_obj_type) { case ObTinyIntType : { ObArrayFixedSize *dst_arr = static_cast *>(dst); if (OB_FAIL(dst_arr->push_back(res.get_tinyint()))) { LOG_WARN("failed to push back array value", K(ret)); } break; } case ObSmallIntType : { ObArrayFixedSize *dst_arr = static_cast *>(dst); if (OB_FAIL(dst_arr->push_back(res.get_smallint()))) { LOG_WARN("failed to push back array value", K(ret)); } break; } case ObIntType : { ObArrayFixedSize *dst_arr = static_cast *>(dst); if (OB_FAIL(dst_arr->push_back(res.get_int()))) { LOG_WARN("failed to push back array value", K(ret)); } break; } case ObInt32Type: { ObArrayFixedSize *dst_arr = static_cast *>(dst); if (OB_FAIL(dst_arr->push_back(res.get_int32()))) { LOG_WARN("failed to push back array value", K(ret)); } break; } case ObUTinyIntType : { ObArrayFixedSize *dst_arr = static_cast *>(dst); if (OB_FAIL(dst_arr->push_back(res.get_utinyint()))) { LOG_WARN("failed to push back array value", K(ret)); } break; } case ObUSmallIntType : { ObArrayFixedSize *dst_arr = static_cast *>(dst); if (OB_FAIL(dst_arr->push_back(res.get_usmallint()))) { LOG_WARN("failed to push back array value", K(ret)); } break; } case ObUInt64Type : { ObArrayFixedSize *dst_arr = static_cast *>(dst); if (OB_FAIL(dst_arr->push_back(res.get_uint64()))) { LOG_WARN("failed to push back array value", K(ret)); } break; } case ObUInt32Type: { ObArrayFixedSize *dst_arr = static_cast *>(dst); if (OB_FAIL(dst_arr->push_back(res.get_uint32()))) { LOG_WARN("failed to push back array value", K(ret)); } break; } case ObDecimalIntType: { ret = OB_NOT_SUPPORTED; // to do break; } case ObFloatType: { ObArrayFixedSize *dst_arr = static_cast *>(dst); if (OB_FAIL(dst_arr->push_back(res.get_float()))) { LOG_WARN("failed to push back array value", K(ret)); } break; } case ObDoubleType: { ObArrayFixedSize *dst_arr = static_cast *>(dst); if (OB_FAIL(dst_arr->push_back(res.get_double()))) { LOG_WARN("failed to push back array value", K(ret)); } break; } case ObVarcharType: { ObArrayBinary *dst_arr = static_cast(dst); if (OB_FAIL(dst_arr->push_back(res.get_varchar()))) { LOG_WARN("failed to push back array value", K(ret)); } break; } default: { ret = OB_ERR_UNEXPECTED; OB_LOG(WARN, "unexpected element type", K(ret), K(dst_obj_type)); } } } return ret; } #define ADD_SIGNED_FIXED_ARRAY_OBJ(Element_Type) \ int64_t val; \ ObArrayFixedSize *dst_arr = static_cast *>(dst); \ if (OB_FAIL(j_node.to_int(val))) { \ LOG_WARN("failed to push back array value", K(ret)); \ } else if (OB_FAIL(int_range_check(dst_obj_type, val, val))) { \ LOG_WARN("failed to check value range", K(ret), K(val)); \ } else if (OB_FAIL(dst_arr->push_back(static_cast(val)))) { \ LOG_WARN("failed to push back array value", K(ret)); \ } #define ADD_UNSIGNED_FIXED_ARRAY_OBJ(Element_Type) \ uint64_t val; \ ObArrayFixedSize *dst_arr = static_cast *>(dst); \ if (OB_FAIL(j_node.to_uint(val))) { \ LOG_WARN("failed to push back array value", K(ret)); \ } else if (OB_FAIL(uint_range_check(dst_obj_type, val, val))) { \ LOG_WARN("failed to check value range", K(ret), K(val)); \ } else if (OB_FAIL(dst_arr->push_back(static_cast(val)))) { \ LOG_WARN("failed to push back array value", K(ret)); \ } int ObArrayCastUtils::add_json_node_to_array(common::ObIAllocator &alloc, ObJsonNode &j_node, const ObCollectionTypeBase *elem_type, ObIArrayType *dst) { int ret = OB_SUCCESS; if (j_node.json_type() == ObJsonNodeType::J_NULL) { if (OB_FAIL(dst->push_null())) { LOG_WARN("failed to push null array value", K(ret)); } } else if (j_node.json_type() == ObJsonNodeType::J_ARRAY) { const ObCollectionArrayType *array_type = dynamic_cast(elem_type); ObIArrayType *child_array = nullptr; ObJsonArray *json_arr = static_cast(&j_node); if (OB_ISNULL(array_type)) { ret = OB_ERR_ARRAY_TYPE_MISMATCH; LOG_WARN("unexpected element type", K(ret), K(elem_type->type_id_)); } else if (OB_FAIL(ObArrayTypeObjFactory::construct(alloc, *array_type, child_array))) { LOG_WARN("failed to add null to array", K(ret)); } for (int i = 0; i < json_arr->element_count() && OB_SUCC(ret); i++) { if (OB_FAIL(add_json_node_to_array(alloc, *(*json_arr)[i], array_type->element_type_, child_array))) { LOG_WARN("failed to add json node to array", K(ret), K(i)); } } if (OB_SUCC(ret)) { ObArrayNested *nested_arr = static_cast(dst); if (OB_FAIL(child_array->init())) { LOG_WARN("child array init failed", K(ret)); } else if (OB_FAIL(nested_arr->push_back(*child_array))) { LOG_WARN("failed to push back array value", K(ret)); } } } else { // basic type const ObCollectionBasicType *basic_type = dynamic_cast(elem_type); if (OB_ISNULL(basic_type)) { ret = OB_ERR_ARRAY_TYPE_MISMATCH; LOG_WARN("unexpected element type", K(ret), K(elem_type->type_id_)); } else { ObObjType dst_obj_type = basic_type->basic_meta_.get_obj_type(); switch (dst_obj_type) { case ObTinyIntType: { ADD_SIGNED_FIXED_ARRAY_OBJ(int8_t); break; } case ObSmallIntType: { ADD_SIGNED_FIXED_ARRAY_OBJ(int16_t); break; } case ObInt32Type: { ADD_SIGNED_FIXED_ARRAY_OBJ(int32_t); break; } case ObIntType: { ADD_SIGNED_FIXED_ARRAY_OBJ(int64_t); break; } case ObUTinyIntType: { ADD_UNSIGNED_FIXED_ARRAY_OBJ(uint8_t); break; } case ObUSmallIntType: { ADD_UNSIGNED_FIXED_ARRAY_OBJ(uint16_t); break; } case ObUInt32Type: { ADD_UNSIGNED_FIXED_ARRAY_OBJ(uint32_t); break; } case ObUInt64Type: { ADD_SIGNED_FIXED_ARRAY_OBJ(int64_t); break; } case ObDecimalIntType: { ret = OB_NOT_SUPPORTED; LOG_WARN("not supported", K(ret)); break; } case ObFloatType: { double val; ObArrayFixedSize *dst_arr = static_cast *>(dst); if (OB_FAIL(j_node.to_double(val))) { LOG_WARN("failed to push back array value", K(ret)); } else if (OB_FAIL(real_range_check(dst_obj_type, val, val))) { LOG_WARN("failed to check value range", K(ret)); } else if (OB_FAIL(dst_arr->push_back(static_cast(val)))) { LOG_WARN("failed to push back array value", K(ret)); } break; } case ObDoubleType: { double val; ObArrayFixedSize *dst_arr = static_cast *>(dst); if (OB_FAIL(j_node.to_double(val))) { LOG_WARN("failed to push back array value", K(ret)); } else if (OB_FAIL(real_range_check(dst_obj_type, val, val))) { LOG_WARN("failed to check value range", K(ret)); } else if (OB_FAIL(dst_arr->push_back(val))) { LOG_WARN("failed to push back array value", K(ret)); } break; } case ObVarcharType: { ObArrayBinary *dst_arr = static_cast(dst); ObStringBuffer str_buf(&alloc); if (OB_FAIL(j_node.print(str_buf, false))) { LOG_WARN("failed to push back array value", K(ret)); } else if (OB_FAIL(dst_arr->push_back(str_buf.string()))) { LOG_WARN("failed to push back array value", K(ret)); } break; } default: { ret = OB_ERR_UNEXPECTED; OB_LOG(WARN, "unexpected element type", K(ret), K(dst_obj_type)); } } } } return ret; } int ObArrayCastUtils::string_cast(common::ObIAllocator &alloc, ObString &arr_text, ObIArrayType *&dst, const ObCollectionTypeBase *dst_elem_type) { int ret = OB_SUCCESS; const char *syntaxerr = NULL; uint64_t err_offset = 0; ObJsonNode *j_node = NULL; if (OB_FAIL( ObJsonParser::parse_json_text(&alloc, arr_text.ptr(), arr_text.length(), syntaxerr, &err_offset, j_node))) { LOG_WARN("failed to parse array text", K(ret), K(arr_text), KCSTRING(syntaxerr), K(err_offset)); } else if (j_node->json_type() != ObJsonNodeType::J_ARRAY) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid text. not json type", K(ret), K(arr_text), K(j_node->json_type())); } else { for (int i = 0; i < j_node->element_count() && OB_SUCC(ret); i++) { ObJsonArray *json_arr = static_cast(j_node); if (OB_FAIL(add_json_node_to_array(alloc, *(*json_arr)[i], dst_elem_type, dst))) { LOG_WARN("failed to add json node to array", K(ret), K(i)); } } } return ret; } int ObArrayBinaryCast::cast(common::ObIAllocator &alloc, ObIArrayType *src, const ObCollectionTypeBase *elem_type, ObIArrayType *&dst, const ObCollectionTypeBase *dst_elem_type, ObCastMode mode) { int ret = OB_SUCCESS; const ObCollectionBasicType *src_type = dynamic_cast(elem_type); const ObCollectionBasicType *dst_type = dynamic_cast(dst_elem_type); if (OB_UNLIKELY(!src_type || !dst_type)) { ret = OB_ERR_ARRAY_TYPE_MISMATCH; LOG_WARN("unexpected status: invalid argument", K(ret), KP(src_type), KP(dst_type)); } else { ObLength elem_len_max = dst_type->basic_meta_.get_length(); ObCollationType elem_cs_type = src_type->basic_meta_.get_collation_type(); ObCollationLevel elem_ncl_type = src_type->basic_meta_.get_collation_level(); for (int64_t i = 0; i < src->size() && OB_SUCC(ret); i++) { ObObj src_elem; if (src->get_format() != ArrayFormat::Vector && src->is_null(i)) { if (OB_FAIL(dst->push_null())) { LOG_WARN("failed to add null to array", K(ret), K(i)); } } else if (OB_FAIL(ObArrayCastUtils::cast_get_element(src, src_type, i, src_elem))) { LOG_WARN("failed to get cast element", K(ret), K(i)); } else if (FALSE_IT(src_elem.set_collation_type(elem_cs_type))) { } else if (FALSE_IT(src_elem.set_collation_level(elem_ncl_type))) { }else if (elem_len_max < src_elem.get_string_len()) { ret = OB_ERR_DATA_TOO_LONG; LOG_WARN("varchar type length is too long", K(ret), K(i), K(elem_len_max), K(src_elem.get_string_len())); } else if (OB_FAIL(ObArrayCastUtils::cast_add_element(alloc, src_elem, dst, dst_type, mode))) { LOG_WARN("failed to cast and add element", K(ret)); } } } return ret; } int ObArrayNestedCast::cast(common::ObIAllocator &alloc, ObIArrayType *src, const ObCollectionTypeBase *elem_type, ObIArrayType *&dst, const ObCollectionTypeBase *dst_elem_type, ObCastMode mode) { int ret = OB_SUCCESS; const ObCollectionArrayType *src_type = dynamic_cast(elem_type); const ObCollectionArrayType *dst_type = dynamic_cast(dst_elem_type); ObArrayNested *dst_arr = dynamic_cast(dst); if (OB_UNLIKELY(!src_type || !dst_type || !dst_arr)) { ret = OB_ERR_ARRAY_TYPE_MISMATCH; LOG_WARN("unexpected status: invalid argument", K(ret), KP(src_type), KP(dst_type), KP(dst_arr)); } else { ObIArrayType *src_elem = nullptr; ObIArrayType *dst_elem = nullptr; ObArrayTypeCast *arr_cast = nullptr; if (OB_FAIL(ObArrayTypeObjFactory::construct(alloc, *src_type, src_elem))) { LOG_WARN("failed to add null to array", K(ret)); } else if (OB_FAIL(ObArrayTypeObjFactory::construct(alloc, *dst_type, dst_elem))) { LOG_WARN("failed to add null to array", K(ret)); } else if (OB_FAIL(ObArrayTypeCastFactory::alloc(alloc, *src_type, *dst_type, arr_cast))) { LOG_WARN("alloc array cast failed", K(ret)); } for (int64_t i = 0; i < src->size() && OB_SUCC(ret); i++) { if (src->get_format() != ArrayFormat::Vector && src->is_null(i)) { if (OB_FAIL(dst->push_null())) { LOG_WARN("failed to add null to array", K(ret), K(i)); } } else if (OB_FAIL(src->at(i, *src_elem))) { LOG_WARN("failed to get elem", K(ret), K(i)); } else if (OB_FAIL(arr_cast->cast(alloc, src_elem, src_type->element_type_, dst_elem, dst_type->element_type_))) { LOG_WARN("array element cast failed", K(ret)); } else if (OB_FAIL(dst_elem->init())) { LOG_WARN("array init failed", K(ret)); } else if (OB_FAIL(dst_arr->push_back(*dst_elem))) { LOG_WARN("array push back failed", K(ret)); } else { src_elem->clear(); dst_elem->clear(); } } } return ret; } int ObArrayTypeCastFactory::alloc(ObIAllocator &alloc, const ObCollectionTypeBase &src_array_meta, const ObCollectionTypeBase &dst_array_meta, ObArrayTypeCast *&arr_cast) { int ret = OB_SUCCESS; UNUSED(src_array_meta); const ObCollectionArrayType *arr_type = dynamic_cast(&dst_array_meta); if (arr_type->element_type_->type_id_ == ObNestedType::OB_BASIC_TYPE) { ObCollectionBasicType *elem_type = static_cast(arr_type->element_type_); if (ob_is_string_tc(elem_type->basic_meta_.get_obj_type()) && ObCharType != elem_type->basic_meta_.get_obj_type()) { arr_cast = OB_NEWx(ObArrayBinaryCast, &alloc); } else if (arr_type->type_id_ == ObNestedType::OB_VECTOR_TYPE) { arr_cast = OB_NEWx(ObVectorDataCast, &alloc); static_cast(arr_cast)->dim_cnt_ = arr_type->dim_cnt_; } else { arr_cast = OB_NEWx(ObArrayFixedSizeCast, &alloc); } } else if (arr_type->element_type_->type_id_ == ObNestedType::OB_ARRAY_TYPE) { arr_cast = OB_NEWx(ObArrayNestedCast, &alloc); } else { // to do ret = OB_NOT_SUPPORTED; LOG_WARN("not supported cast type", K(ret), K(arr_type->element_type_->type_id_)); } if (OB_SUCC(ret) && OB_ISNULL(arr_cast)) { ret = OB_ALLOCATE_MEMORY_FAILED; OB_LOG(WARN, "alloc memory failed", K(ret)); } return ret; } } // namespace sql } // namespace oceanbase