Co-authored-by: Carrot-77 <1012982871@qq.com> Co-authored-by: wu-xingying <729224612@qq.com>
		
			
				
	
	
		
			1538 lines
		
	
	
		
			58 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1538 lines
		
	
	
		
			58 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/**
 | 
						|
 * 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<char *>(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<ObLobCommon *>(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<uint32_t>(param.byte_size_);//TODO(gehao.wh): check convert from 64 to 32
 | 
						|
        ctx_->buff_ = static_cast<char *>(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<ObLobDiffHeader*>(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<uint32_t>(partial_data.data_length_);
 | 
						|
        ctx_->buff_ = static_cast<char *>(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<int32_t>(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<char *>(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<int32_t>(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<uint32_t>(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<char *>(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<uint32_t>(ObCharset::strlen_char(cs_type_,
 | 
						|
                                                    output_data.ptr(),
 | 
						|
                                                    static_cast<int64_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(ObCharset::strlen_char(cs_type_,
 | 
						|
                                                  output_data.ptr(),
 | 
						|
                                                  static_cast<int64_t>(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<uint32_t>(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<uint32_t>(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<int64_t>(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<int64_t>(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<char *>(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<ObMemLobCommon *>(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<ObMemLobExternHeader *>(mem_loc->data_);
 | 
						|
        mem_loc_extern->payload_offset_ = static_cast<uint32_t>(pos - sizeof(ObLobLocator));
 | 
						|
        mem_loc_extern->payload_size_ = static_cast<uint32_t>(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<int32_t>(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<uint32_t>(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<int64_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<int32_t>(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<uint32_t>(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<ObLobDiffHeader*>(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<ObLobDiffHeader*>(lob_common->buffer_);
 | 
						|
    res = diff_header->diff_cnt_ > 0;
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
}
 | 
						|
}
 |