/** * 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. * This file contains implementation for lob_access_utils. */ #define USING_LOG_PREFIX COMMON #include "share/ob_lob_access_utils.h" #include "lib/objectpool/ob_server_object_pool.h" #include "common/sql_mode/ob_sql_mode.h" #include "storage/lob/ob_lob_util.h" #include "storage/tx/ob_trans_define_v4.h" #include "storage/tx/ob_trans_service.h" #include "storage/lob/ob_lob_manager.h" namespace oceanbase { namespace common { void ObLobTextIterCtx::init(bool is_clone /* false */) { buff_byte_len_ = MAX(buff_byte_len_, OB_LOB_ITER_DEFAULT_BUFFER_LEN); if (is_clone) { // not used currently, waititng for temp lob based on temporary file is_cloned_temporary_ = is_clone; } } void ObLobTextIterCtx::reuse() { content_byte_len_ = 0; content_len_ = 0; accessed_byte_len_ = 0; accessed_len_ = 0; last_accessed_byte_len_ = 0; last_accessed_len_ = 0; iter_count_ = 0; if (OB_NOT_NULL(lob_query_iter_)) { lob_query_iter_->reset(); OB_DELETE(ObLobQueryIter, "unused", lob_query_iter_); lob_query_iter_ = NULL; } } // ----- implementations of ObTextStringIter ----- ObTextStringIter::~ObTextStringIter() { if (is_outrow_ && OB_NOT_NULL(ctx_)) { if (OB_NOT_NULL(ctx_->lob_query_iter_)) { ctx_->lob_query_iter_->reset(); OB_DELETE(ObLobQueryIter, "unused", ctx_->lob_query_iter_); ctx_->lob_query_iter_ = NULL; } } } int ObTextStringIter::init(uint32_t buffer_len, const sql::ObBasicSessionInfo *session, ObIAllocator *res_allocator, ObIAllocator *tmp_allocator) { int ret = OB_SUCCESS; if (is_init_) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: iter already initiated", K(ret)); } else if (!(is_lob_storage(type_))) { is_lob_ = false; is_outrow_ = false; } else if (datum_str_.length() == 0) { COMMON_LOG(DEBUG, "Lob: iter with null input str", K(ret), K(*this)); } else { ObLobLocatorV2 locator(datum_str_, has_lob_header_); is_lob_ = true; tmp_alloc_ = tmp_allocator; if (!has_lob_header_) { is_outrow_ = false; } else if (!locator.is_valid()) { ret = OB_ERR_UNEXPECTED; COMMON_LOG(WARN,"Lob: invalid lob", K(ret)); } else if (FALSE_IT(is_outrow_ = !locator.has_inrow_data())) { } else if (!is_outrow_ && !locator.is_delta_temp_lob()) { // inrow lob always get full data, no need ctx_ } else if (OB_ISNULL(res_allocator)) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: iter with null allocator", K(ret)); } else { // outrow lob ObIAllocator *allocator = (tmp_allocator == nullptr) ? res_allocator : tmp_allocator; char *ctx_buffer = static_cast(allocator->alloc(sizeof(ObLobTextIterCtx))); if (OB_ISNULL(ctx_buffer)) { ret = OB_ALLOCATE_MEMORY_FAILED; COMMON_LOG(WARN,"Lob: failed to alloc output buffer", K(ret), KP(ctx_buffer)); } else { ctx_ = new (ctx_buffer) ObLobTextIterCtx(locator, session, res_allocator, buffer_len); ctx_ ->init(); } } } if (OB_SUCC(ret)) { state_ = TEXTSTRING_ITER_INIT; is_init_ = true; } return ret; } static int init_lob_access_param(storage::ObLobAccessParam ¶m, ObLobTextIterCtx *lob_iter_ctx, ObCollationType cs_type, ObIAllocator *allocator = nullptr) { int ret = OB_SUCCESS; int64_t query_timeout = 0; ObMemLobTxInfo *tx_info = NULL; ObMemLobLocationInfo *location_info = NULL; ObLobCommon *disk_loc = NULL; ObString disk_loc_str; if (OB_ISNULL(lob_iter_ctx)) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: invalid lob iter ctx.", K(ret)); } else if (!lob_iter_ctx->locator_.is_persist_lob()) { ret = OB_NOT_IMPLEMENT; COMMON_LOG(WARN, "Lob: outrow temp lob is not supported", K(ret), K(lob_iter_ctx->locator_)); } else if (lob_iter_ctx->locator_.is_delta_temp_lob()) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: is delta lob", K(ret), K(lob_iter_ctx->locator_)); } else if (OB_FAIL(lob_iter_ctx->locator_.get_disk_locator(disk_loc_str))) { COMMON_LOG(WARN, "Lob: get disk locator failed.", K(ret)); } else if (FALSE_IT(disk_loc = reinterpret_cast(disk_loc_str.ptr()))){ } else if (OB_ISNULL(disk_loc) || disk_loc->is_init_ == false) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: invalid disk_locator.", K(ret), KP(disk_loc)); } else { if (OB_ISNULL(lob_iter_ctx->session_)) { query_timeout = ObTimeUtility::current_time() + 60 * USECS_PER_SEC; } else if (OB_FAIL(lob_iter_ctx->session_->get_query_timeout(query_timeout))) { COMMON_LOG(WARN, "Lob: get_query_timeout failed.", K(ret), K(*lob_iter_ctx)); } } if (OB_FAIL(ret)) { } else if (OB_FAIL(lob_iter_ctx->locator_.get_tx_info(tx_info))) { } else if (OB_FAIL(lob_iter_ctx->locator_.get_location_info(location_info))) { } else { param.tx_desc_ = NULL; param.snapshot_.core_.tx_id_ = tx_info->snapshot_tx_id_; param.snapshot_.core_.version_.convert_for_tx(tx_info->snapshot_version_); param.snapshot_.core_.scn_ = transaction::ObTxSEQ::cast_from_int(tx_info->snapshot_seq_); param.snapshot_.valid_= true; param.snapshot_.source_ = transaction::ObTxReadSnapshot::SRC::LS; param.snapshot_.snapshot_lsid_ = share::ObLSID(location_info->ls_id_); param.ls_id_ = share::ObLSID(location_info->ls_id_); param.sql_mode_ = (lob_iter_ctx->session_ == NULL) ? SMO_DEFAULT : lob_iter_ctx->session_->get_sql_mode(); param.tablet_id_ = ObTabletID(location_info->tablet_id_); if (allocator == nullptr) { allocator = lob_iter_ctx->alloc_; } param.allocator_ = allocator; int64_t disk_loc_handle_size = disk_loc_str.length(); param.lob_common_ = disk_loc; param.handle_size_ = disk_loc_handle_size; param.byte_size_ = param.lob_common_->get_byte_size(param.handle_size_); param.coll_type_ = cs_type; if (!lob_iter_ctx->locator_.is_lob_disk_locator()) { param.lob_locator_ = &lob_iter_ctx->locator_; } param.timeout_ = (lob_iter_ctx->session_ == NULL) ? query_timeout : (lob_iter_ctx->session_->get_query_start_time() + query_timeout); param.scan_backward_ = false; param.offset_ = 0; // use 0 offset when reading full lob data param.len_ = 0; } return ret; } int ObTextStringIter::get_outrow_lob_full_data(ObIAllocator *allocator /*nullptr*/ ) { int ret = OB_SUCCESS; storage::ObLobManager* lob_mngr = MTL(storage::ObLobManager*); if (!has_lob_header_ || !is_outrow_ || OB_ISNULL(ctx_) || OB_ISNULL(ctx_->alloc_)) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: error condition", K(ret), K(has_lob_header_), K(is_outrow_), KP(ctx_->session_), KP(ctx_)); } else if (OB_ISNULL(lob_mngr)) { ret = OB_ERR_UNEXPECTED; COMMON_LOG(WARN, "Lob: get lob manager failed.", K(ret)); } else { // outrow persist lob storage::ObLobAccessParam param; if (OB_SUCC(init_lob_access_param(param, ctx_, cs_type_, tmp_alloc_))) { param.len_ = (ctx_->total_access_len_ == 0 ? param.byte_size_ : ctx_->total_access_len_); if (!param.ls_id_.is_valid() || !param.tablet_id_.is_valid()) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: invalid param.", K(ret), K(param)); } else if (param.byte_size_ == 0) { // empty lob ctx_->content_byte_len_ = 0; } else if (param.byte_size_ < 0 || param.len_ == 0) { ret = OB_ERR_UNEXPECTED; COMMON_LOG(WARN,"Lob: calc byte size is negative.", K(ret), K(param)); } else if (param.byte_size_ > OB_MAX_LONGTEXT_LENGTH) { ret = OB_ERR_UNEXPECTED; COMMON_LOG(WARN,"Lob: unable to read full data over 512M lob.", K(ret), K(param)); } else { ctx_->total_byte_len_ = param.byte_size_; ctx_->buff_byte_len_ = static_cast(param.byte_size_);//TODO(gehao.wh): check convert from 64 to 32 ctx_->buff_ = static_cast(ctx_->alloc_->alloc(ctx_->buff_byte_len_)); ObString output_data; if (OB_ISNULL(ctx_->buff_)) { ret = OB_ALLOCATE_MEMORY_FAILED; COMMON_LOG(WARN,"Lob: failed to alloc output buffer", K(ret), KP(ctx_->buff_), K(ctx_->buff_byte_len_)); } else { output_data.assign_buffer(ctx_->buff_, ctx_->buff_byte_len_); if (OB_FAIL(lob_mngr->query(param, output_data))) { COMMON_LOG(WARN,"Lob: falied to query lob tablets.", K(ret), K(param)); } else { ctx_->content_byte_len_ = output_data.length(); // Notice: content_len_ (char len) is not updated! COMMON_LOG(DEBUG,"Lob: read output for obstring iter.", K(output_data)); } } } } } return ret; } int ObTextStringIter::get_delta_lob_full_data(ObLobLocatorV2& lob_locator, ObIAllocator *allocator, ObString &data_str) { int ret = OB_SUCCESS; ObLobCommon *lob_common = nullptr; ObLobDiffHeader *diff_header = nullptr; if (! ob_is_json(type_)) { ret = OB_ERR_UNEXPECTED; COMMON_LOG(WARN, "only json support", K(ret), K(type_)); } else if (OB_FAIL(lob_locator.get_disk_locator(lob_common))) { COMMON_LOG(WARN, "get disk locator failed.", K(ret), K(lob_locator)); } else if (! lob_common->in_row_) { ret = OB_ERR_UNEXPECTED; COMMON_LOG(WARN, "Unsupport out row delta tmp lob locator", K(ret), KPC(lob_common)); } else if (OB_ISNULL(diff_header = reinterpret_cast(lob_common->buffer_))) { ret = OB_ERR_UNEXPECTED; COMMON_LOG(WARN, "diff_header is null", K(ret), KPC(lob_common)); } else { char *buf = diff_header->data_; int64_t data_len = diff_header->persist_loc_size_; int64_t pos = 0; ObLobPartialData partial_data; if (OB_FAIL(partial_data.init())) { COMMON_LOG(WARN, "map create fail", K(ret)); } else if (OB_FAIL(partial_data.deserialize(buf, data_len, pos))) { COMMON_LOG(WARN, "deserialize partial data fail", K(ret), K(data_len), K(pos)); } else { storage::ObLobManager* lob_mngr = MTL(storage::ObLobManager*); storage::ObLobAccessParam param; ctx_->locator_ = partial_data.locator_; if (OB_FAIL(init_lob_access_param(param, ctx_, cs_type_, allocator))) { COMMON_LOG(WARN, "init_lob_access_param fail", K(ret)); } else if (!param.ls_id_.is_valid() || !param.tablet_id_.is_valid()) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: invalid param.", K(ret), K(param)); } else if ((param.len_ = param.byte_size_) <= 0) { ret = OB_ERR_UNEXPECTED; COMMON_LOG(WARN,"Lob: calc byte size is negative.", K(ret), K(param)); } else if (param.byte_size_ > OB_MAX_LONGTEXT_LENGTH) { ret = OB_ERR_UNEXPECTED; COMMON_LOG(WARN,"Lob: unable to read full data over 512M lob.", K(ret), K(param)); } else if (partial_data.data_length_ > OB_MAX_LONGTEXT_LENGTH) { ret = OB_ERR_UNEXPECTED; COMMON_LOG(WARN,"Lob: unable to read full data over 512M lob.", K(ret), K(param), K(partial_data)); } else { ctx_->total_byte_len_ = partial_data.data_length_; ctx_->buff_byte_len_ = static_cast(partial_data.data_length_); ctx_->buff_ = static_cast(ctx_->alloc_->alloc(ctx_->buff_byte_len_)); ObString output_data; if (OB_ISNULL(ctx_->buff_)) { ret = OB_ALLOCATE_MEMORY_FAILED; COMMON_LOG(WARN,"Lob: failed to alloc output buffer", K(ret), KP(ctx_->buff_), K(ctx_->buff_byte_len_)); } else { output_data.assign_buffer(ctx_->buff_, ctx_->buff_byte_len_); if (OB_FAIL(lob_mngr->query(param, output_data))) { COMMON_LOG(WARN,"Lob: falied to query lob tablets.", K(ret), K(param)); } else { output_data.set_length(static_cast(partial_data.data_length_)); for(int32_t i = 0; OB_SUCC(ret) && i < partial_data.index_.count(); ++i) { ObLobChunkIndex &idx = partial_data.index_[i]; if (1 == idx.is_modified_ || 1 == idx.is_add_) { ObLobChunkData &chunk_data = partial_data.data_[idx.data_idx_]; MEMCPY(output_data.ptr() + idx.offset_, chunk_data.data_.ptr() + idx.pos_, idx.byte_len_); } } ctx_->content_byte_len_ = output_data.length(); data_str = output_data; } } } } } return ret; } int ObTextStringIter::get_outrow_prefix_data(uint32_t prefix_char_len) { int ret = OB_SUCCESS; storage::ObLobManager* lob_mngr = MTL(storage::ObLobManager*); if (!has_lob_header_ || !is_outrow_ || OB_ISNULL(ctx_) || OB_ISNULL(ctx_->alloc_)) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: error condition", K(ret), K(has_lob_header_), K(is_outrow_), KP(ctx_->session_), KP(ctx_)); } else if (OB_ISNULL(lob_mngr)) { ret = OB_ERR_UNEXPECTED; COMMON_LOG(WARN, "Lob: get lob manager failed.", K(ret)); } else { // outrow persist lob storage::ObLobAccessParam param; if (OB_SUCC(init_lob_access_param(param, ctx_, cs_type_, tmp_alloc_))) { param.len_ = prefix_char_len; if (!param.ls_id_.is_valid() || !param.tablet_id_.is_valid()) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: invalid param.", K(ret), K(param)); } else if (param.byte_size_ < 0 || param.len_ == 0) { ret = OB_ERR_UNEXPECTED; COMMON_LOG(WARN,"Lob: calc byte size is negative.", K(ret), K(param)); } else { ctx_->total_byte_len_ = param.byte_size_; ctx_->buff_byte_len_ = prefix_char_len * MAX_CHAR_MULTIPLIER; ctx_->buff_ = static_cast(ctx_->alloc_->alloc(ctx_->buff_byte_len_)); ObString output_data; if (OB_ISNULL(ctx_->buff_)) { ret = OB_ALLOCATE_MEMORY_FAILED; COMMON_LOG(WARN,"Lob: failed to alloc output buffer", K(ret), KP(ctx_->buff_), K(ctx_->buff_byte_len_)); } else { output_data.assign_buffer(ctx_->buff_, ctx_->buff_byte_len_); if (OB_FAIL(lob_mngr->query(param, output_data))) { COMMON_LOG(WARN,"Lob: falied to query lob tablets.", K(ret), K(param)); } else { ctx_->content_byte_len_ = output_data.length(); // Notice: content_len_ (char len) is not updated! COMMON_LOG(DEBUG,"Lob: read output for obstring iter.", K(output_data)); } } } } } return ret; } int ObTextStringIter::get_current_block(ObString &str) { int ret = OB_SUCCESS; if (!is_init_) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: iter not initiated", K(ret)); } else if (state_ <= TEXTSTRING_ITER_INIT) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: iter not accessed", K(ret)); } else { str.assign(ctx_->buff_, static_cast(ctx_->content_byte_len_)); } return ret; } int ObTextStringIter::get_full_data(ObString &data_str) { int ret = OB_SUCCESS; ObLobLocatorV2 loc(datum_str_, has_lob_header_); if (!is_init_ || state_ != TEXTSTRING_ITER_INIT) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: iter state error", K(ret), K(is_init_), K(state_)); } else if (datum_str_.length() == 0) { // maybe from datum->is_nop/is_null data_str.assign(NULL, 0); COMMON_LOG(DEBUG, "Lob: iter with null input", K(ret), K(*this)); } else if (!is_lob_ || !has_lob_header_) { // string types or 4.0 compatiable text data_str.assign_ptr(datum_str_.ptr(), datum_str_.length()); } else if (loc.is_delta_temp_lob()) { if (OB_FAIL(get_delta_lob_full_data(loc, tmp_alloc_, data_str))) { COMMON_LOG(WARN, "get_delta_lob_full_data fail", K(ret), K(loc)); } } else if (!is_outrow_) { // inrow lob if (OB_FAIL(loc.get_inrow_data(data_str))) { COMMON_LOG(WARN, "Lob: get lob inrow data failed", K(ret)); } } else { // outrow lob, read full data into a inrow local tmp lob currently if (OB_FAIL(get_outrow_lob_full_data())) { COMMON_LOG(WARN, "Lob: get lob outrow data failed", K(ret)); } else { data_str.assign_ptr(ctx_->buff_, ctx_->content_byte_len_); } } if (OB_SUCC(ret)) { state_ = TEXTSTRING_ITER_NEXT; } COMMON_LOG(DEBUG, "Lob: get_full_data", K(ret), K(data_str), KP(data_str.ptr()), K(data_str.length())); return ret; } // Notice: always get full data for string tc & inrow lobs int ObTextStringIter::get_inrow_or_outrow_prefix_data(ObString &data_str, uint32_t prefix_char_len) { int ret = OB_SUCCESS; if (!is_init_ || state_ != TEXTSTRING_ITER_INIT) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: iter state error", K(ret), K(is_init_), K(state_)); } else if (datum_str_.length() == 0) { data_str.assign(NULL, 0); COMMON_LOG(DEBUG, "Lob: iter with null input", K(ret), K(*this)); } else if (!is_lob_ || !has_lob_header_) { // string types if (prefix_char_len == DEAFULT_LOB_PREFIX_CHAR_LEN) { data_str.assign_ptr(datum_str_.ptr(), datum_str_.length()); } else { int64_t mb_len = ObCharset::strlen_char(cs_type_, datum_str_.ptr(), datum_str_.length()); if (mb_len <= prefix_char_len) { data_str.assign_ptr(datum_str_.ptr(), datum_str_.length()); } else { int64_t truncated_str_len = ObCharset::charpos(cs_type_, datum_str_.ptr(), datum_str_.length(), prefix_char_len); data_str.assign_ptr(datum_str_.ptr(), truncated_str_len); } } } else if (!is_outrow_) { // inrow lob ObLobLocatorV2 loc(datum_str_, has_lob_header_); if (OB_FAIL(loc.get_inrow_data(data_str))) { COMMON_LOG(WARN, "Lob: get lob inrow data failed", K(ret)); } else { uint32_t max_len = ObCharset::strlen_char(cs_type_, data_str.ptr(), data_str.length()); uint32_t byte_len = (prefix_char_len > max_len) ? max_len : prefix_char_len; byte_len = ObCharset::charpos(cs_type_, data_str.ptr(), data_str.length(), byte_len); data_str.assign_ptr(data_str.ptr(), byte_len); } } else { // outrow lob, read full data into a inrow local tmp lob currently if (OB_FAIL(get_outrow_prefix_data(prefix_char_len))) { COMMON_LOG(WARN, "Lob: get lob outrow prefix failed", K(ret), K(prefix_char_len)); } else { data_str.assign_ptr(ctx_->buff_, ctx_->content_byte_len_); } } if (OB_SUCC(ret)) { state_ = TEXTSTRING_ITER_NEXT; } COMMON_LOG(DEBUG, "Lob: get_inrow_or_outrow_prefix_data", K(ret), K(data_str), KP(data_str.ptr()), K(data_str.length())); return ret; } void ObTextStringIter::reset() { if(!is_init_) { } else if (is_lob_ && OB_NOT_NULL(ctx_)) { // Notice: memory of ctx_ itself will be released when allocator destruction ctx_->reuse(); } state_ = TEXTSTRING_ITER_INIT; } int ObTextStringIter::get_first_block(ObString &str) { int ret = OB_SUCCESS; storage::ObLobManager* lob_mngr = MTL(storage::ObLobManager*); if (!is_outrow_ || OB_ISNULL(ctx_) || !has_lob_header_) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: error condition", K(ret), K(is_outrow_), KP(ctx_), K(has_lob_header_)); } else if (OB_ISNULL(lob_mngr)) { ret = OB_ERR_UNEXPECTED; COMMON_LOG(WARN, "Lob: get lob manager failed.", K(ret)); } else { storage::ObLobAccessParam param; if (OB_SUCC(init_lob_access_param(param, ctx_, cs_type_, tmp_alloc_))) { param.scan_backward_ = ctx_->is_backward_; param.offset_ = ctx_->start_offset_; param.len_ = (ctx_->total_access_len_ == 0 ? param.byte_size_ : ctx_->total_access_len_); // update buffer len according to reserve length config ctx_->total_byte_len_ = param.byte_size_; if (ctx_->reserved_byte_len_ > 0 || ctx_->reserved_len_ > 0) { int64_t max_reserved_byte = MAX(ctx_->reserved_byte_len_, ctx_->reserved_len_ * MAX_CHAR_MULTIPLIER); if (ctx_->buff_byte_len_ < max_reserved_byte) { COMMON_LOG(INFO,"Lob: buffer size changed due to configurations", K(ctx_->buff_byte_len_), K(ctx_->reserved_byte_len_), K(ctx_->reserved_len_), K(max_reserved_byte)); ctx_->buff_byte_len_ = static_cast(max_reserved_byte); } } if (!param.ls_id_.is_valid() || !param.tablet_id_.is_valid()) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: invalid param.", K(ret), K(param)); } else if (param.byte_size_ == 0) { state_ = TEXTSTRING_ITER_END; } else if (param.byte_size_ < 0 || param.len_ == 0) { ret = OB_ERR_UNEXPECTED; COMMON_LOG(WARN,"Lob: calc byte size is negative.", K(ret), K(param)); } else { if (OB_ISNULL(ctx_->buff_)) { ctx_->buff_ = static_cast(ctx_->alloc_->alloc(ctx_->buff_byte_len_)); } ObString output_data; output_data.assign_buffer(ctx_->buff_, ctx_->buff_byte_len_); // 1. start query iter, and query one time // 2. update access param if (OB_ISNULL(ctx_->buff_)) { ret = OB_ALLOCATE_MEMORY_FAILED; COMMON_LOG(WARN,"Lob: failed to alloc output buffer", K(ret), KP(ctx_->buff_), K(ctx_->buff_byte_len_)); } else if (OB_FAIL(lob_mngr->query(param, ctx_->lob_query_iter_))) { COMMON_LOG(WARN,"Lob: falied to query lob iter.", K(ret), K(param)); } else if (OB_FAIL(ctx_->lob_query_iter_->get_next_row(output_data))) { COMMON_LOG(WARN,"Lob: falied to get first block.", K(ret), K(param)); } else { ctx_->content_byte_len_ = output_data.length(); // ToDo: @gehao get char len directly from lob mngr ? ctx_->content_len_ = static_cast(ObCharset::strlen_char(cs_type_, output_data.ptr(), static_cast(output_data.length()))); ctx_->last_accessed_byte_len_ = 0; ctx_->last_accessed_len_ = 0; ctx_->accessed_byte_len_ = ctx_->content_byte_len_; ctx_->accessed_len_ = ctx_->content_len_; ctx_->iter_count_++; str.assign_ptr(ctx_->buff_, ctx_->content_byte_len_); state_ = TEXTSTRING_ITER_NEXT; } } } } return ret; } int ObTextStringIter::reserve_data() { int ret = OB_SUCCESS; if (!is_outrow_ || !has_lob_header_ || OB_ISNULL(ctx_) || OB_ISNULL(ctx_->lob_query_iter_)) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: error condition", K(ret), K(is_outrow_), K(has_lob_header_), KP(ctx_)); } else if (ctx_->reserved_len_ == 0 && ctx_->reserved_byte_len_ == 0) { // do nothing } else if (ctx_->accessed_byte_len_ == ctx_->total_byte_len_) { // 1. already the last block, should get iter end; // 2. if not the last block, this function fail if access_len_ < reserved_len_ } else if (ctx_->reserved_len_ != 0) { // reserve_char_data() if (cs_type_ == CS_TYPE_BINARY) { ctx_->reserved_byte_len_ = ctx_->reserved_len_; } else if (ctx_->reserved_len_ > ctx_->content_len_) { ret = OB_SIZE_OVERFLOW; COMMON_LOG(WARN, "Lob: reserved length oversized", K(ret), K(*ctx_)); } else if (!ctx_->is_backward_) { uint32 reserved_char_start = ctx_->content_len_ - ctx_->reserved_len_; uint32 reserved_char_pos = static_cast(ObCharset::charpos(cs_type_, ctx_->buff_, ctx_->content_byte_len_, reserved_char_start)); ctx_->reserved_byte_len_ = ctx_->content_byte_len_ - reserved_char_pos; MEMMOVE(ctx_->buff_, ctx_->buff_ + reserved_char_pos, ctx_->reserved_byte_len_); } else if (ctx_->is_backward_) { uint32 reserved_char_end = ctx_->reserved_len_ + 1; uint32 reserved_char_end_pos = static_cast(ObCharset::charpos(cs_type_, ctx_->buff_, ctx_->content_byte_len_, reserved_char_end)); ctx_->reserved_byte_len_ = reserved_char_end_pos; MEMMOVE(ctx_->buff_ + ctx_->content_byte_len_ - ctx_->reserved_byte_len_, ctx_->buff_, ctx_->reserved_byte_len_); } else {} } else if (ctx_->reserved_byte_len_ != 0) { if (OB_FAIL(reserve_byte_data())) { COMMON_LOG(WARN, "Lob: reserved byte data failed", K(ret), K(*ctx_)); } } return ret; } int ObTextStringIter::reserve_byte_data() { int ret = OB_SUCCESS; if (ctx_->reserved_byte_len_ > ctx_->content_byte_len_) { ret = OB_SIZE_OVERFLOW; COMMON_LOG(WARN, "Lob: reserved byte length oversized", K(ret), K(*ctx_)); } else if (!ctx_->is_backward_) { uint32 reserved_byte_start = ctx_->content_byte_len_ - ctx_->reserved_byte_len_; MEMMOVE(ctx_->buff_, ctx_->buff_ + reserved_byte_start, ctx_->reserved_byte_len_); } else if (ctx_->is_backward_) { MEMMOVE(ctx_->buff_ + ctx_->content_byte_len_ - ctx_->reserved_byte_len_, ctx_->buff_, ctx_->reserved_byte_len_); } else {} return ret; } int ObTextStringIter::get_next_block_inner(ObString &str) { // 1. calc reserved len and memmove // 2. update query buffer and query agin // 3. update access param int ret = OB_SUCCESS; if (!is_outrow_ || !has_lob_header_ || OB_ISNULL(ctx_) || OB_ISNULL(ctx_->lob_query_iter_)) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: error condition", K(ret), K(is_outrow_), K(has_lob_header_), KP(ctx_)); } else if (ctx_->content_byte_len_ == 0) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: no data", K(ret), K(is_outrow_), KP(ctx_)); } else if (OB_FAIL(reserve_data())) { COMMON_LOG(WARN, "Lob: reserve_data failed", K(ret), K(is_outrow_), KP(ctx_)); } else { ObString output_data; if (!ctx_->is_backward_) { output_data.assign_buffer(ctx_->buff_ + ctx_->reserved_byte_len_, ctx_->buff_byte_len_ - ctx_->reserved_byte_len_); } else { output_data.assign_buffer(ctx_->buff_, ctx_->buff_byte_len_ - ctx_->reserved_byte_len_); } if (OB_FAIL(ctx_->lob_query_iter_->get_next_row(output_data))) { if (ret == OB_ITER_END) { state_ = TEXTSTRING_ITER_END; // iter finished ctx_->lob_query_iter_->reset(); OB_DELETE(ObLobQueryIter, "unused", ctx_->lob_query_iter_); ctx_->lob_query_iter_ = NULL; ret = OB_SUCCESS; } else { COMMON_LOG(WARN,"Lob: falied to get first block.", K(ret)); } } else { // if put backward, we should compact buffer remain and move reserved part closed to the reading value if (output_data.remain() > 0 && ctx_->is_backward_ && ctx_->reserved_byte_len_ > 0) { // from :[0, output_data.length_][output_data.length_, output_data.buffer_size_][reserved_part] // to :[0, output_data.length_][reserved_part] MEMMOVE(output_data.ptr() + output_data.length(), output_data.ptr() + output_data.length() + output_data.remain(), ctx_->reserved_byte_len_); } ctx_->content_byte_len_ = ctx_->reserved_byte_len_ + output_data.length(); // ToDo: @gehao get directly from lob mngr ? uint32 cur_out_len = static_cast(ObCharset::strlen_char(cs_type_, output_data.ptr(), static_cast(output_data.length()))); ctx_->content_len_ = ctx_->reserved_len_ + cur_out_len; ctx_->last_accessed_byte_len_ = ctx_->accessed_byte_len_; ctx_->last_accessed_len_ = ctx_->accessed_len_; ctx_->accessed_byte_len_ += output_data.length(); ctx_->accessed_len_ += cur_out_len; ctx_->iter_count_++; str.assign_ptr(ctx_->buff_, ctx_->content_byte_len_); } } return ret; } ObTextStringIterState ObTextStringIter::get_next_block(ObString &str) { int ret = OB_SUCCESS; str.reset(); if (!is_init_ || state_ < TEXTSTRING_ITER_INIT) { state_ = TEXTSTRING_ITER_INVALID; COMMON_LOG(WARN, "Lob: iter not initiated", K(ret), K(*this)); } else if (!is_lob_ || !is_outrow_ || !has_lob_header_) { // if not outrow lob, get full data switch (state_) { case TEXTSTRING_ITER_INIT: { OZ(get_full_data(str)); break; } case TEXTSTRING_ITER_NEXT: { state_ = TEXTSTRING_ITER_END; break; } default: { COMMON_LOG(WARN, "Lob: error state for common string or inrow lob", K(state_)); break; } } } else if (is_outrow_) { switch (state_) { case TEXTSTRING_ITER_INIT: { OZ(get_first_block(str)); break; } case TEXTSTRING_ITER_NEXT: { OZ(get_next_block_inner(str)); break; } default: { COMMON_LOG(WARN, "Lob: error state for common string or inrow lob", K(state_)); break; } } } else { COMMON_LOG(WARN, "Lob: error case in of iter", K(ret), K(*this)); state_ = TEXTSTRING_ITER_INVALID; } if (OB_FAIL(ret)) { COMMON_LOG(WARN, "Lob: iter get_next_block failed", K(ret), K(*this)); state_ = TEXTSTRING_ITER_INVALID; err_ret_ = ret; } return state_; } void ObTextStringIter::set_start_offset(uint64_t offset) { if (is_valid_for_config()) { ctx_->start_offset_ = offset; } } void ObTextStringIter::set_access_len(int64_t char_len) { if (is_valid_for_config()) { ctx_->total_access_len_ = char_len; } } void ObTextStringIter::set_reserved_len(uint32_t reserved_len) { if (is_valid_for_config()) { ctx_->reserved_len_ = reserved_len; ctx_->reserved_byte_len_ = 0; } } void ObTextStringIter::set_reserved_byte_len(uint32_t reserved_byte_len) { if (is_valid_for_config()) { ctx_->reserved_byte_len_ = reserved_byte_len; ctx_->reserved_len_ = 0; } } void ObTextStringIter::reset_reserve_len() { if (is_valid_for_config(TEXTSTRING_ITER_NEXT)) { ctx_->reserved_byte_len_ = 0; ctx_->reserved_len_ = 0; } } void ObTextStringIter::set_backward() { if (is_valid_for_config()) { ctx_->is_backward_ = true; } } void ObTextStringIter::set_forward() { if (is_valid_for_config()) { ctx_->is_backward_ = false; } } uint64_t ObTextStringIter::get_start_offset() { uint64_t start_offset = 0; if (!is_init_ || !is_outrow_ || !has_lob_header_ || state_ <= TEXTSTRING_ITER_INIT || OB_ISNULL(ctx_)) { } else { start_offset = ctx_->start_offset_; } return start_offset; } uint32_t ObTextStringIter::get_last_accessed_len() { uint32_t last_accessed_len = 0; if (!is_init_ || !is_outrow_ || !has_lob_header_ || state_ <= TEXTSTRING_ITER_INIT || OB_ISNULL(ctx_)) { } else { last_accessed_len = ctx_->last_accessed_len_; } return last_accessed_len; } uint32_t ObTextStringIter::get_iter_count() { uint32_t iter_count = 0; if (!is_init_ || !is_outrow_ || !has_lob_header_ || state_ <= TEXTSTRING_ITER_INIT || OB_ISNULL(ctx_)) { } else { iter_count = ctx_->iter_count_; } return iter_count; } uint32_t ObTextStringIter::get_reserved_char_len() { uint32_t reserved_char_len = 0; if (!is_init_ || !is_outrow_ || !has_lob_header_ || state_ <= TEXTSTRING_ITER_INIT || OB_ISNULL(ctx_)) { } else { reserved_char_len = ctx_->reserved_len_; } return reserved_char_len; } uint32_t ObTextStringIter::get_reserved_byte_len() { uint32_t reserved_byte_len = 0; if (!is_init_ || !is_outrow_ || !has_lob_header_ || state_ <= TEXTSTRING_ITER_INIT || OB_ISNULL(ctx_)) { } else { reserved_byte_len = ctx_->reserved_byte_len_; } return reserved_byte_len; } uint32_t ObTextStringIter::get_last_accessed_byte_len() { uint32_t last_accessed_byte_len = 0; if (!is_init_ || !is_outrow_ || !has_lob_header_ || state_ <= TEXTSTRING_ITER_INIT || OB_ISNULL(ctx_)) { } else { last_accessed_byte_len = ctx_->last_accessed_byte_len_; } return last_accessed_byte_len; } uint32_t ObTextStringIter::get_accessed_len() { int32 ret = OB_SUCCESS; uint32_t accessed_len = 0; if (!is_init_ || state_ <= TEXTSTRING_ITER_INIT) { } else if (!is_lob_ || !is_outrow_ || !has_lob_header_) { // string types int64_t total_char_len = 0; if (OB_FAIL(get_char_len(total_char_len))) { COMMON_LOG(WARN, "Lob: get lob inrow data failed", K(ret)); } else { accessed_len = static_cast(total_char_len); } } else if (is_outrow_) { accessed_len = ctx_->accessed_len_; } else { // should not come here. ret = OB_UNEXPECT_INTERNAL_ERROR; COMMON_LOG(WARN, "Lob: get access len failed", K(ret), K(*this), K(lbt())); } return accessed_len; } uint32_t ObTextStringIter::get_accessed_byte_len() { int32 ret = OB_SUCCESS; uint32_t accessed_byte_len = 0; if (!is_init_ || state_ <= TEXTSTRING_ITER_INIT) { } else if (!is_lob_ || !is_outrow_ || !has_lob_header_) { // string types int64_t total_byte_len = 0; if (OB_FAIL(get_byte_len(total_byte_len))) { COMMON_LOG(WARN, "Lob: get lob inrow data failed", K(ret)); } else { accessed_byte_len = static_cast(total_byte_len); } } else if (is_outrow_) { accessed_byte_len = ctx_->accessed_byte_len_; } else { // should not come here. ret = OB_UNEXPECT_INTERNAL_ERROR; COMMON_LOG(WARN, "Lob: get access len failed", K(ret), K(*this), K(lbt())); } return accessed_byte_len; } int ObTextStringIter::get_byte_len(int64_t &byte_len) { int ret = OB_SUCCESS; if (!is_init_) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: iter state error", K(ret), K(is_init_), K(state_)); } else if (!is_lob_ || !is_outrow_ || !has_lob_header_) { ObString data_str = datum_str_; if (is_lob_ && has_lob_header_) { ObLobLocatorV2 loc(data_str, has_lob_header_); if (OB_FAIL(loc.get_inrow_data(data_str))) { COMMON_LOG(WARN, "Lob: get lob inrow data failed", K(ret)); } } byte_len = data_str.length(); } else { // outrow lob if (OB_ISNULL(ctx_)) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: iter state error", K(ret), K(is_init_), K(state_), K(ctx_)); } else if (!ctx_->locator_.is_persist_lob()) { ret = OB_NOT_IMPLEMENT; COMMON_LOG(WARN, "Lob: outrow temp lob is not implement", K(ret), K(ctx_->locator_)); } else if (OB_FAIL(ctx_->locator_.get_lob_data_byte_len(byte_len))) { // if buffer length is zero, use lob byte length COMMON_LOG(WARN, "Lob: get outrow lob byte len failed.", K(ret), K(ctx_->buff_byte_len_)); } } return ret; } int ObTextStringIter::get_char_len(int64_t &char_length) { int ret = OB_SUCCESS; if (!is_init_) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: iter state error", K(ret), K(is_init_), K(state_)); } else if (!is_lob_ || !is_outrow_ || !has_lob_header_) { ObString data_str = datum_str_; if (is_lob_ && has_lob_header_) { ObLobLocatorV2 loc(data_str, has_lob_header_); if (OB_FAIL(loc.get_inrow_data(data_str))) { COMMON_LOG(WARN, "Lob: get lob inrow data failed", K(ret)); } } char_length = ObCharset::strlen_char(cs_type_, data_str.ptr(), static_cast(data_str.length())); } else { // outrow lob ObString disk_loc_str; storage::ObLobManager* lob_mngr = MTL(storage::ObLobManager*); if (OB_ISNULL(ctx_)) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: error condition", K(ret), K(is_outrow_), KP(ctx_->session_), KP(ctx_)); } else if (OB_ISNULL(lob_mngr)) { ret = OB_ERR_UNEXPECTED; COMMON_LOG(WARN, "Lob: get lob manager failed.", K(ret)); } else { storage::ObLobAccessParam param; if (OB_SUCC(init_lob_access_param(param, ctx_, cs_type_, tmp_alloc_))) { uint64_t length = 0; if (!param.ls_id_.is_valid() || !param.tablet_id_.is_valid()) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: invalid param.", K(ret), K(param)); } else if (OB_FAIL(lob_mngr->getlength(param, length))) { COMMON_LOG(WARN,"Lob: falied to get outrow lob char len.", K(ret), K(param)); } else { char_length = static_cast(length); } } } } return ret; } // Only used to response client, append full lob data after outrow lob locator, disk locator is not changed int ObTextStringIter::append_outrow_lob_fulldata(ObObj &obj, const sql::ObBasicSessionInfo *session, ObIAllocator &allocator) { int ret = OB_SUCCESS; ObObjType type = obj.get_type(); ObString raw_string = obj.get_string(); int64_t lob_data_byte_len = 0; int64_t res_byte_len = 0; int64_t pos = 0; char *buff = NULL; if (!obj.has_lob_header()) { } else if (!is_lob_storage(type)) { } else if (obj.is_null() || obj.is_nop_value()) { } else { bool data_changed = false; ObLobLocatorV2 loc(obj.get_string(), obj.has_lob_header()); if (!loc.is_valid()) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: invalid lob locator", K(ret), K(obj)); } else if (loc.is_delta_temp_lob()) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: converting delta lob!", K(ret)); } else if (loc.has_inrow_data()) { int64_t real_loc_len = 0; if (OB_FAIL(loc.get_real_locator_len(real_loc_len))) { COMMON_LOG(WARN, "Lob: failed to get real locator len", K(loc)); } else { raw_string.assign_ptr(raw_string.ptr(), real_loc_len); data_changed = true; // should be old lob data from client, need refresh lobdata } } if (OB_FAIL(ret)) { // do noting } else if (loc.is_inrow() && !data_changed) { // do nothing } else if (OB_FAIL(loc.get_lob_data_byte_len(lob_data_byte_len))) { COMMON_LOG(WARN, "Lob: failed to get lob data byte length", K(ret), K(obj)); } else if (OB_FALSE_IT((res_byte_len = lob_data_byte_len + raw_string.length()))) { } else if (res_byte_len > UINT32_MAX) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: inrow lob data too long", K(raw_string.length()), K(lob_data_byte_len)); } else if (OB_ISNULL((buff = static_cast(allocator.alloc(res_byte_len))))) { ret = OB_ALLOCATE_MEMORY_FAILED; COMMON_LOG(WARN,"Lob: failed to alloc res buffer", K(raw_string.length()), K(lob_data_byte_len)); } else { // copy mem lob locator MEMCPY(buff, raw_string.ptr(), raw_string.length()); pos += raw_string.length(); // config mem lob locator ObMemLobCommon *mem_loc = reinterpret_cast(buff); mem_loc->set_has_inrow_data(true); // Notice: inrow flag in disk locator ObLobCommon is still outrow! if (!mem_loc->has_extern() || pos < sizeof(ObLobLocator)) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN,"Lob: invalid mem locator", K(*mem_loc), K(pos)); } else { ObMemLobExternHeader *mem_loc_extern = reinterpret_cast(mem_loc->data_); mem_loc_extern->payload_offset_ = static_cast(pos - sizeof(ObLobLocator)); mem_loc_extern->payload_size_ = static_cast(lob_data_byte_len); // copy inrow data int64_t tenant_id = (session == nullptr) ? MTL_ID() : session->get_effective_tenant_id(); ObArenaAllocator tmp_alloc("ObLobRead", OB_MALLOC_NORMAL_BLOCK_SIZE, tenant_id); ObTextStringIterState state; ObString src_block_data; ObTextStringIter instr_iter(obj); if (OB_FAIL(instr_iter.init(0, session, &allocator, &tmp_alloc))) { COMMON_LOG(WARN, "Lob: init text string iter failed", K(instr_iter)); } else { while (OB_SUCC(ret) && pos < res_byte_len && (state = instr_iter.get_next_block(src_block_data)) == TEXTSTRING_ITER_NEXT) { MEMCPY(buff + pos, src_block_data.ptr(), src_block_data.length()); pos += src_block_data.length(); } OB_ASSERT(pos == res_byte_len); obj.set_lob_value(obj.get_type(), buff, static_cast(res_byte_len)); obj.set_has_lob_header(); // must has lob header } } } } return ret; } // Notice: if input is an outrow lob, the result will be a templob! int ObTextStringIter::convert_outrow_lob_to_inrow_templob(const ObObj &in_obj, ObObj &out_obj, const sql::ObBasicSessionInfo *session, ObIAllocator *allocator, bool allow_persist_inrow, bool need_deep_copy) { int ret = OB_SUCCESS; ObObjType type = in_obj.get_type(); int64_t lob_data_byte_len = 0; int64_t pos = 0; bool is_pass_thougth = true; if (!in_obj.has_lob_header()) { } else if (!is_lob_storage(type)) { } else if (in_obj.is_null() || in_obj.is_nop_value()) { } else { ObLobLocatorV2 loc(in_obj.get_string(), in_obj.has_lob_header()); if (!loc.is_valid()) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: invalid lob loc", K(ret), K(loc), K(in_obj)); } else if ((!loc.is_persist_lob() || allow_persist_inrow) && (loc.is_inrow() || loc.is_simple())) { // do nothing } else if (loc.is_delta_temp_lob()) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: converting delta lob!", K(ret)); } else if (OB_FAIL(loc.get_lob_data_byte_len(lob_data_byte_len))) { COMMON_LOG(WARN, "Lob: failed to get lob data byte length", K(ret), K(in_obj)); } else if (lob_data_byte_len < 0 || lob_data_byte_len > UINT32_MAX) { ret = OB_INVALID_ARGUMENT; COMMON_LOG(WARN, "Lob: inrow lob data length error", K(lob_data_byte_len)); } else { is_pass_thougth = false; ObTextStringResult new_tmp_lob(in_obj.get_type(), in_obj.has_lob_header(), allocator); if (OB_FAIL(new_tmp_lob.init(lob_data_byte_len))) { COMMON_LOG(WARN, "Lob: init tmp lob failed", K(ret), K(lob_data_byte_len)); } else { // copy inrow data ObTextStringIterState state; ObString src_block_data; ObTextStringIter instr_iter(in_obj); if (OB_FAIL(ret)) { } else if (OB_FAIL(instr_iter.init(0, session, allocator))) { COMMON_LOG(WARN, "Lob: init text string iter failed", K(instr_iter)); } else { while (OB_SUCC(ret) && pos < lob_data_byte_len && (state = instr_iter.get_next_block(src_block_data)) == TEXTSTRING_ITER_NEXT) { if (OB_FAIL(new_tmp_lob.append(src_block_data))) { COMMON_LOG(WARN, "Lob: tmp lob append failed", K(ret), K(lob_data_byte_len), K(pos), K(src_block_data), K(new_tmp_lob)); } else { pos += src_block_data.length(); } } OB_ASSERT(pos == lob_data_byte_len); ObString res; new_tmp_lob.get_result_buffer(res); out_obj = in_obj; // copy meta out_obj.set_lob_value(in_obj.get_type(), res.ptr(), res.length()); out_obj.set_has_lob_header(); // must has lob header } } } if (OB_SUCC(ret) && is_pass_thougth) { if (need_deep_copy) { if (OB_FAIL(ob_write_obj(*allocator, in_obj, out_obj))) { LOG_WARN("do deepy copy obj failed.", K(ret), K(in_obj)); } } else { out_obj = in_obj; } } } return ret; } // ----- implementations of ObTextStringResult ----- int ObTextStringResult::calc_buffer_len(int64_t res_len) { int ret = OB_SUCCESS; if (!(is_lob_storage(type_))) { // tinytext no has lob header buff_len_ = res_len; } else { if (!has_lob_header_) { buff_len_ = res_len; } else if (res_len < OB_MAX_LONGTEXT_LENGTH - MAX_TMP_LOB_HEADER_LEN) { // inrow lob with lob header bool has_extern = lib::is_oracle_mode(); // even oracle may not need extern for temp data ObMemLobExternFlags extern_flags(has_extern); res_len += sizeof(ObLobCommon); buff_len_ = ObLobLocatorV2::calc_locator_full_len(extern_flags, 0, static_cast(res_len), false); } else { ret = OB_NOT_SUPPORTED; LOG_WARN("Lob: out row temp lob not implemented, not support length bigger than 512M", K(ret), K(this), K(pos_), K(buff_len_), K(res_len)); } } return ret; } int ObTextStringResult::calc_inrow_templob_len(uint32 inrow_data_len, int64_t &templob_len) { int ret = OB_SUCCESS; if (inrow_data_len < OB_MAX_LONGTEXT_LENGTH - MAX_TMP_LOB_HEADER_LEN) { bool has_extern = lib::is_oracle_mode(); ObMemLobExternFlags extern_flags(has_extern); inrow_data_len += sizeof(ObLobCommon); templob_len = ObLobLocatorV2::calc_locator_full_len(extern_flags, 0, inrow_data_len, false); } else { ret = OB_NOT_SUPPORTED; LOG_WARN("Lob: not support length bigger than 512M", K(ret), K(inrow_data_len)); } return ret; } int64_t ObTextStringResult::calc_inrow_templob_locator_len() { ObMemLobExternFlags extern_flags(lib::is_oracle_mode()); return static_cast(ObLobLocatorV2::calc_locator_full_len(extern_flags, 0, 0, false)); } int ObTextStringResult::fill_inrow_templob_header(const int64_t inrow_data_len, char *buf, int64_t buf_len) { int ret = OB_SUCCESS; if (OB_ISNULL(buf) || (buf_len == 0)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("Lob: try to fill inrow templob header with empty buffer", K(ret), K(inrow_data_len), K(buf), K(buf_len)); } else if (inrow_data_len <= OB_MAX_LONGTEXT_LENGTH - MAX_TMP_LOB_HEADER_LEN) { ObLobLocatorV2 locator(buf, static_cast(buf_len), true); // temp lob in oracle mode not need extern neither, for it does not have rowkey // However we mock extern failed in case of return it to old client ObMemLobExternFlags extern_flags(lib::is_oracle_mode()); ObString rowkey_str; ObString empty_str; ObLobCommon lob_common; if (OB_FAIL(locator.fill(TEMP_FULL_LOB, extern_flags, rowkey_str, &lob_common, static_cast(inrow_data_len + sizeof(ObLobCommon)), 0, false))) { LOG_WARN("Lob: fill temp lob locator failed", K(ret), K(inrow_data_len), K(buf), K(buf_len)); } else if (OB_FAIL((locator.set_payload_data(&lob_common, empty_str)))) { LOG_WARN("Lob: set temp lob locator payload failed", K(ret), K(inrow_data_len), K(buf), K(buf_len)); } } else { // oversized ret = OB_NOT_SUPPORTED; LOG_WARN("Lob: not support length bigger than 512M", K(ret), K(inrow_data_len), K(buf), K(buf_len)); } return ret; } int ObTextStringResult::fill_temp_lob_header(const int64_t res_len) { int ret = OB_SUCCESS; if (!has_lob_header_) { // do nothing } else if (OB_ISNULL(buffer_)) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("Lob: allocate memory for lob result failed", K(type_), K(buff_len_), K(ret)); } else if (!(is_lob_storage(type_))) { // do nothing } else if (res_len <= OB_MAX_LONGTEXT_LENGTH - MAX_TMP_LOB_HEADER_LEN) { ObLobLocatorV2 locator(buffer_, static_cast(buff_len_), has_lob_header_); // temp lob in oracle mode not need extern neither, for it does not have rowkey // However we mock extern failed in case of return it to old client ObMemLobExternFlags extern_flags(lib::is_oracle_mode()); ObString rowkey_str; ObString empty_str; ObLobCommon lob_common; if (OB_FAIL(locator.fill(TEMP_FULL_LOB, extern_flags, rowkey_str, &lob_common, static_cast(res_len + sizeof(ObLobCommon)), 0, false))) { LOG_WARN("Lob: fill temp lob locator failed", K(type_), K(ret)); } else if (OB_FAIL((locator.set_payload_data(&lob_common, empty_str)))) { LOG_WARN("Lob: set temp lob locator payload failed", K(type_), K(ret)); } pos_ = buff_len_ - res_len; // only res_len could be used later } else { // outrow ret = OB_NOT_IMPLEMENT; LOG_WARN("Lob: out row temp lob not implemented", K(this), K(pos_), K(buff_len_), K(ret)); } return ret; } int ObTextStringResult::init(int64_t res_len, ObIAllocator *allocator) { int ret = OB_SUCCESS; if (OB_NOT_NULL(buffer_) || is_init_) { ret = OB_ERR_UNEXPECTED; LOG_WARN("Lob: textstring result init already", K(ret), K(*this)); } else if (!(ob_is_string_or_lob_type(type_) || is_lob_storage(type_))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("Lob: unexpected expr result type for textstring result", K(ret), K(type_)); } else if (OB_FAIL(calc_buffer_len(res_len))) { LOG_WARN("fail to calc buffer len", K(ret), K(res_len)); } else if (buff_len_ == 0) { OB_ASSERT(has_lob_header_ == false); // empty result without header } else { buffer_ = OB_ISNULL(allocator) ? (char *)alloc_->alloc(buff_len_) : (char *)allocator->alloc(buff_len_); if (OB_ISNULL(buffer_)) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("Lob: allocation failed", K(ret), K(type_), K(buff_len_)); } else if (OB_FAIL(fill_temp_lob_header(res_len))) { // string types will not fill lob header LOG_WARN("Lob: fill_temp_lob_header failed", K(ret), K(type_)); } } if (OB_SUCC(ret)) { is_init_ = true; } return ret; } int ObTextStringResult::copy(const ObLobLocatorV2 *loc) { int ret = OB_SUCCESS; if (OB_ISNULL(loc) || !loc->is_valid()) { ret = OB_INVALID_ARGUMENT; } else if (loc->size_ == 0) { } else { buff_len_ = loc->size_; buffer_ = (char *)alloc_->alloc(buff_len_); if (OB_ISNULL(buffer_)) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("Lob: allocate memory for copy locator failed", K(loc), K(loc->size_), K(ret)); } else { MEMCPY(buffer_, loc->ptr_, buff_len_); has_lob_header_ = loc->has_lob_header_; } } return ret; } int ObTextStringResult::append(const char *buffer, int64_t len) { int ret = OB_SUCCESS; if (!is_outrow_templob_) { if (len == 0) { } else if (pos_ + len > buff_len_) { ret = OB_ERR_UNEXPECTED; LOG_WARN("Lob: append content length too long", K(pos_), K(buff_len_), K(len), K(ret)); } else { MEMCPY(buffer_ + pos_, buffer, len); pos_ += len; } } else { ret = OB_NOT_IMPLEMENT; LOG_WARN("Lob: out row temp lob not implemented", K(this), K(pos_), K(buff_len_), K(ret)); } return ret; } int ObTextStringResult::fill(int64_t pos, int c, int64_t len) { int ret = OB_SUCCESS; if (!is_outrow_templob_) { if (len == 0) { } else if (pos + len > buff_len_) { ret = OB_ERR_UNEXPECTED; LOG_WARN("Lob: append content length too long", K(this), K(pos), K(len), K(ret)); } else { MEMSET(buffer_ + pos_ + pos, c, len); } } else { ret = OB_NOT_IMPLEMENT; LOG_WARN("Lob: out row temp lob not implemented", K(this), K(pos_), K(buff_len_), K(ret)); } return ret; } int ObTextStringResult::write(const char *buffer, int64_t pos, int64_t len) { int ret = OB_SUCCESS; if (!is_outrow_templob_) { if (len == 0) { // do nothing } else if (pos + len > buff_len_) { ret = OB_ERR_UNEXPECTED; LOG_WARN("Lob: append content length too long", K(this), K(pos), K(len), K(ret)); } else { MEMCPY(buffer_ + pos, buffer, len); } } else { ret = OB_NOT_IMPLEMENT; LOG_WARN("Lob: out row temp lob not implemented", K(this), K(pos_), K(buff_len_), K(ret)); } return ret; } // ToDo: add state for curr, or beginning, or end, adjust payload size? int ObTextStringResult::lseek(int64_t offset, int state) { int ret = OB_SUCCESS; if (pos_ + offset < 0 || pos_ + offset > buff_len_) { ret = OB_ERR_UNEXPECTED; LOG_WARN("Lob: lseek to invalid position", K(this), K(pos_), K(offset), K(state), K(ret)); } else { pos_ += offset; } return ret; } // Notice: should move pos_ to new pos after write buffer (lseek) ! int ObTextStringResult::get_reserved_buffer(char *&empty_start, int64_t &empty_len) { int ret = OB_SUCCESS; if (!is_outrow_templob_) { empty_start = buffer_ + pos_; empty_len = buff_len_ - pos_; if (empty_len < 0) { ret = OB_SIZE_OVERFLOW; LOG_WARN("Lob: no remaining", K(this), K(pos_), K(buff_len_), K(ret)); } } else { ret = OB_NOT_IMPLEMENT; LOG_WARN("Lob: out row temp lob not implemented", K(this), K(pos_), K(buff_len_), K(ret)); } return ret; } int ObTextStringResult::ob_convert_obj_temporay_lob(ObObj &obj, ObIAllocator &allocator) { int ret = OB_SUCCESS; ObObjType type = obj.get_type(); if (!is_lob_storage(type)) { } else if (obj.has_lob_header()) { ret = OB_UNEXPECT_INTERNAL_ERROR; COMMON_LOG(WARN, "Lob: not support to convert obj has lob header to tempory lob", K(ret), K(obj)); } else if (obj.is_null() || obj.is_nop_value()) { } else { ObString row_str = obj.get_string(); ObTextStringResult new_tmp_lob(obj.get_type(), true, &allocator); if (OB_FAIL(new_tmp_lob.init(row_str.length()))) { COMMON_LOG(WARN, "Lob: init tmp lob failed", K(ret), K(row_str.length())); } else if (OB_FAIL(new_tmp_lob.append(row_str))) { COMMON_LOG(WARN, "Lob: append failed", K(ret), K(new_tmp_lob), K(row_str)); } else { ObString result; new_tmp_lob.get_result_buffer(result); obj.set_lob_value(type, result.ptr(), static_cast(result.length())); if (new_tmp_lob.has_lob_header()) { obj.set_has_lob_header(); } } } return ret; } int ObTextStringResult::ob_convert_datum_temporay_lob(ObDatum &datum, const ObObjMeta &in_obj_meta, const ObObjMeta &out_obj_meta, ObIAllocator &allocator) { int ret = OB_SUCCESS; ObObjType type = in_obj_meta.get_type(); if (!is_lob_storage(type)) { } else if (in_obj_meta.has_lob_header() || !out_obj_meta.has_lob_header() || (in_obj_meta.get_type_class() != out_obj_meta.get_type_class())) { ret = OB_UNEXPECT_INTERNAL_ERROR; COMMON_LOG(WARN, "Lob: not support to convert obj has lob header to tempory lob", K(ret), K(datum), K(in_obj_meta), K(out_obj_meta)); } else if (datum.is_null() || datum.is_nop()) { } else { ObString row_str = datum.get_string(); ObTextStringResult new_tmp_lob(out_obj_meta.get_type(), true, &allocator); if (OB_FAIL(new_tmp_lob.init(row_str.length()))) { COMMON_LOG(WARN, "Lob: init tmp lob failed", K(ret), K(row_str.length())); } else if (OB_FAIL(new_tmp_lob.append(row_str))) { COMMON_LOG(WARN, "Lob: append failed", K(ret), K(new_tmp_lob), K(row_str)); } else { ObString result; new_tmp_lob.get_result_buffer(result); datum.set_string(result); } } return ret; } int64_t ObDeltaLob::get_serialize_size() const { int64_t size = 0; // header size size += get_header_serialize_size(); // updated lob size size += get_partial_data_serialize_size(); // lob diff size size += get_lob_diff_serialize_size(); return size; } int64_t ObDeltaLob::get_header_serialize_size() const { int64_t size = 0; // ObMemLobCommon size += sizeof(ObMemLobCommon); // ObLobCommon; size += sizeof(ObLobCommon); // ObLobDiffHeader size += sizeof(ObLobDiffHeader); return size; } int ObDeltaLob::serialize(char* buf, const int64_t buf_len, int64_t& pos) const { int ret = OB_SUCCESS; ObLobDiffHeader *diff_header = nullptr; if (OB_FAIL(serialize_header(buf, buf_len, pos, diff_header))) { LOG_WARN("serialize_header fail", KR(ret), K(buf_len), K(pos), KP(buf)); } else if (OB_FAIL(serialize_partial_data(buf, buf_len, pos))) { LOG_WARN("serialize_partial_data fail", KR(ret), K(buf_len), K(pos), KP(buf)); } else if (OB_FAIL(serialize_lob_diffs(buf, buf_len, diff_header))) { LOG_WARN("serialize_lob_diffs fail", KR(ret), K(buf_len), K(pos), KP(buf)); } return ret; } int ObDeltaLob::serialize_header(char* buf, const int64_t buf_len, int64_t& pos, ObLobDiffHeader *&diff_header) const { int ret = OB_SUCCESS; int64_t size = get_header_serialize_size(); if (pos + size > buf_len) { ret = OB_SIZE_OVERFLOW; LOG_WARN("buffer not enough", KR(ret), K(pos), K(size), K(buf_len), KP(buf)); } else { ObMemLobCommon *mem_common = new (buf + pos) ObMemLobCommon(ObMemLobType::TEMP_DELTA_LOB, false); ObLobCommon *lob_common = new (mem_common->data_) ObLobCommon(); diff_header = new (lob_common->buffer_) ObLobDiffHeader(); diff_header->diff_cnt_ = get_lob_diff_cnt(); diff_header->persist_loc_size_ = static_cast(get_partial_data_serialize_size()); pos += size; } return ret; } int ObDeltaLob::deserialize(const ObLobLocatorV2 &lob_locator) { int ret = OB_SUCCESS; ObLobCommon *lob_common = nullptr; ObLobDiffHeader *diff_header = nullptr; if (! lob_locator.is_delta_temp_lob()) { ret = OB_ERR_UNEXPECTED; LOG_WARN("input not delta tmp lob", KR(ret), K(lob_locator)); } else if (OB_FAIL(lob_locator.get_disk_locator(lob_common))) { LOG_WARN("get disk locator failed.", K(ret), K(lob_locator)); } else if (OB_ISNULL(diff_header = reinterpret_cast(lob_common->buffer_))){ ret = OB_ERR_UNEXPECTED; } else if (OB_FAIL(deserialize_partial_data(diff_header))) { LOG_WARN("deserialize_partial_data fail", KR(ret), K(lob_locator), KPC(diff_header)); } else if (OB_FAIL(deserialize_lob_diffs(lob_locator.ptr_, lob_locator.size_, diff_header))) { LOG_WARN("deserialize_lob_diffs fail", KR(ret), K(lob_locator), KPC(diff_header)); } return ret; } int ObDeltaLob::has_diff(const ObLobLocatorV2 &locator, int64_t &res) { int ret = OB_SUCCESS; bool bres = false; if (OB_FAIL(has_diff(locator, bres))) { LOG_WARN("fail", KR(ret), K(locator)); } else { res = bres; } return ret; } int ObDeltaLob::has_diff(const ObLobLocatorV2 &locator, bool &res) { int ret = OB_SUCCESS; ObLobCommon *lob_common = nullptr; if (! locator.is_delta_temp_lob()) { ret = OB_ERR_UNEXPECTED; LOG_WARN("not delta lob", K(ret), K(locator)); } else if (OB_FAIL(locator.get_disk_locator(lob_common))) { LOG_WARN("get disk locator failed.", KR(ret), K(locator)); } else if (! lob_common->in_row_) { ret = OB_ERR_UNEXPECTED; LOG_WARN("Unsupport out row delta tmp lob locator", KR(ret), KPC(lob_common), K(locator)); } else { ObLobDiffHeader *diff_header = reinterpret_cast(lob_common->buffer_); res = diff_header->diff_cnt_ > 0; } return ret; } } }