/** * Copyright (c) 2021 OceanBase * OceanBase CE is licensed under Mulan PubL v2. * You can use this software according to the terms and conditions of the Mulan PubL v2. * You may obtain a copy of Mulan PubL v2 at: * http://license.coscl.org.cn/MulanPubL-2.0 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. * See the Mulan PubL v2 for more details. */ #define USING_LOG_PREFIX STORAGE #include "ob_sstable_rowkey_helper.h" #include "lib/container/ob_fixed_array_iterator.h" #include "share/ob_get_compat_mode.h" #include "storage/ob_sstable.h" namespace oceanbase { using namespace common; using namespace blocksstable; using namespace share; using namespace share::schema; namespace storage { /* *ObRowkeyObjComparer */ // temporarily keep if in compare func // remove if to build more compare funcs array when perf critical int ObRowkeyObjComparer::sstable_number_cmp_func(const ObObj& obj1, const ObObj& obj2, const ObCompareCtx& cmp_ctx) { int cmp = 0; UNUSED(cmp_ctx); if (OB_UNLIKELY(!obj1.is_number() || !obj2.is_number())) { LOG_ERROR("unexpected error. mismatch function for comparison", K(obj1), K(obj2)); right_to_die_or_duty_to_live(); } else if (OB_LIKELY(((is_smallint_number(obj1) && is_smallint_number(obj2))))) { cmp = obj1.v_.nmb_digits_[0] - obj2.v_.nmb_digits_[0]; cmp = cmp > 0 ? ObObjCmpFuncs::CR_GT : cmp < 0 ? ObObjCmpFuncs::CR_LT : ObObjCmpFuncs::CR_EQ; } else { cmp = number::ObNumber::compare(obj1.nmb_desc_, obj1.v_.nmb_digits_, obj2.nmb_desc_, obj2.v_.nmb_digits_); } return cmp; } int ObRowkeyObjComparer::sstable_collation_free_cmp_func( const ObObj& obj1, const ObObj& obj2, const ObCompareCtx& cmp_ctx) { int cmp = 0; UNUSED(cmp_ctx); if (OB_UNLIKELY( CS_TYPE_COLLATION_FREE != obj2.get_collation_type() || obj1.get_collation_type() != obj2.get_collation_type() //|| obj1.get_type() != obj2.get_type() //|| !obj1.is_varchar_or_char() )) { STORAGE_LOG(ERROR, "unexpected error, invalid argument", K(obj1), K(obj2)); right_to_die_or_duty_to_live(); } else { const int32_t lhs_len = obj1.get_val_len(); const int32_t rhs_len = obj2.get_val_len(); const int32_t cmp_len = MIN(lhs_len, rhs_len); cmp = MEMCMP(obj1.get_string_ptr(), obj2.get_string_ptr(), cmp_len); if (0 == cmp) { if (lhs_len != rhs_len) { // in mysql mode, we should strip all trailing blanks before comparing two strings const int32_t left_len = (lhs_len > cmp_len) ? lhs_len - cmp_len : rhs_len - cmp_len; const char* ptr = (lhs_len > cmp_len) ? obj1.get_string_ptr() : obj2.get_string_ptr(); const unsigned char* uptr = reinterpret_cast(ptr + cmp_len); // int32_t is always capable of stroing the max lenth of varchar or char for (int32_t i = 0; i < left_len; ++i) { if (*(uptr + i) != ' ') { // special behavior in mysql mode, 'a\1 < a' and 'ab > a' if (*(uptr + i) < ' ') { cmp = lhs_len > cmp_len ? ObObjCmpFuncs::CR_LT : ObObjCmpFuncs::CR_GT; } else { cmp = lhs_len > cmp_len ? ObObjCmpFuncs::CR_GT : ObObjCmpFuncs::CR_LT; } break; } } } } else { cmp = cmp > 0 ? ObObjCmpFuncs::CR_GT : ObObjCmpFuncs::CR_LT; } } return cmp; } int ObRowkeyObjComparer::sstable_oracle_collation_free_cmp_func( const ObObj& obj1, const ObObj& obj2, const ObCompareCtx& cmp_ctx) { int cmp = 0; UNUSED(cmp_ctx); if (OB_UNLIKELY( CS_TYPE_COLLATION_FREE != obj2.get_collation_type() || obj1.get_collation_type() != obj2.get_collation_type() //|| obj1.get_type() != obj2.get_type() //|| !obj1.is_varchar_or_char() )) { STORAGE_LOG(ERROR, "unexpected error, invalid argument", K(obj1), K(obj2)); right_to_die_or_duty_to_live(); } else { const int32_t lhs_len = obj1.get_val_len(); const int32_t rhs_len = obj2.get_val_len(); const int32_t cmp_len = MIN(lhs_len, rhs_len); cmp = MEMCMP(obj1.get_string_ptr(), obj2.get_string_ptr(), cmp_len); if (0 == cmp) { // in oracle mode, we should consider the trailing blanks during comparing two varchar if (obj1.is_varying_len_char_type()) { if (lhs_len != rhs_len) { cmp = lhs_len > cmp_len ? ObObjCmpFuncs::CR_GT : ObObjCmpFuncs::CR_LT; } } else { // in oracle mode, we should strip all trailing blanks before comparing two char const int32_t left_len = (lhs_len > cmp_len) ? lhs_len - cmp_len : rhs_len - cmp_len; const char* ptr = (lhs_len > cmp_len) ? obj1.get_string_ptr() : obj2.get_string_ptr(); const unsigned char* uptr = reinterpret_cast(ptr + cmp_len); // int32_t is always capable of stroing the max lenth of varchar or char for (int32_t i = 0; i < left_len; ++i) { if (*(uptr + i) != ' ') { // mysql特殊行为:a\1 < a,但ab > a if (*(uptr + i) < ' ') { cmp = lhs_len > cmp_len ? ObObjCmpFuncs::CR_LT : ObObjCmpFuncs::CR_GT; } else { cmp = lhs_len > cmp_len ? ObObjCmpFuncs::CR_GT : ObObjCmpFuncs::CR_LT; } break; } } } } else { cmp = cmp > 0 ? ObObjCmpFuncs::CR_GT : ObObjCmpFuncs::CR_LT; } } return cmp; } ObRowkeyObjComparer::ObRowkeyObjComparer() : cmp_func_(NULL), cmp_ctx_(ObMaxType, CS_TYPE_INVALID, false, INVALID_TZ_OFF, NULL_FIRST), is_collation_free_(false), type_(COMPARER_MYSQL) {} void ObRowkeyObjComparer::reset() { cmp_func_ = NULL; cmp_ctx_.cmp_cs_type_ = CS_TYPE_INVALID; is_collation_free_ = false; } int ObRowkeyObjComparer::init_compare_func(const ObObjMeta& obj_meta) { int ret = OB_SUCCESS; if (OB_UNLIKELY(!obj_meta.is_valid())) { ret = OB_INVALID_ARGUMENT; STORAGE_LOG(WARN, "Invalid argument to init compare func", K(obj_meta), K(ret)); } else { reset(); if (obj_meta.is_number()) { cmp_func_ = sstable_number_cmp_func; } else { ObObjTypeClass obj_tc = obj_meta.get_type_class(); if (OB_FAIL(ObObjCmpFuncs::get_cmp_func(obj_tc, obj_tc, CO_CMP, cmp_func_))) { STORAGE_LOG(WARN, "Failed to find rowkey obj cmp func", K(obj_meta), K(obj_tc), K(ret)); } else if (OB_ISNULL(cmp_func_)) { ret = OB_ERR_UNEXPECTED; STORAGE_LOG(WARN, "Failed to find rowkey cmp func", K(ret)); } } if (OB_SUCC(ret)) { cmp_ctx_.cmp_cs_type_ = obj_meta.get_collation_type(); is_collation_free_ = false; } } return ret; } int ObRowkeyObjComparer::init_compare_func_collation_free(const common::ObObjMeta& obj_meta) { int ret = OB_SUCCESS; if (OB_UNLIKELY(!obj_meta.is_valid())) { ret = OB_INVALID_ARGUMENT; STORAGE_LOG(WARN, "Invalid argument to init collation free compare func", K(obj_meta), K(ret)); } else if (obj_meta.is_collation_free_compatible()) { reset(); cmp_func_ = sstable_collation_free_cmp_func; cmp_ctx_.cmp_cs_type_ = obj_meta.get_collation_type(); is_collation_free_ = true; } else if (OB_FAIL(init_compare_func(obj_meta))) { STORAGE_LOG(WARN, "Failed to init compare func", K(obj_meta), K(ret)); } return ret; } /* *ObRowkeyObjComparerOracle */ int ObRowkeyObjComparerOracle::init_compare_func_collation_free(const common::ObObjMeta& obj_meta) { int ret = OB_SUCCESS; // TODO oracle mode need add raw type future if (OB_UNLIKELY(!obj_meta.is_valid() || !obj_meta.is_collation_free_compatible())) { ret = OB_INVALID_ARGUMENT; STORAGE_LOG(WARN, "Invalid argument to init collation free compare func", K(obj_meta), K(ret)); } else { reset(); cmp_func_ = sstable_oracle_collation_free_cmp_func; cmp_ctx_.cmp_cs_type_ = obj_meta.get_collation_type(); is_collation_free_ = true; } return ret; } /* *ObSSTableRowkeyHelper */ ObSSTableRowkeyHelper::ObSSTableRowkeyHelper() : endkeys_(), collation_free_endkeys_(), cmp_funcs_(), collation_free_cmp_funcs_(), rowkey_column_cnt_(0), column_type_array_(NULL), sstable_(nullptr), exist_collation_free_(false), use_cmp_nullsafe_(false), is_oracle_mode_(false), is_inited_(false) {} void ObSSTableRowkeyHelper::reset() { endkeys_.reset(); collation_free_endkeys_.reset(); cmp_funcs_.reset(); collation_free_cmp_funcs_.reset(); rowkey_column_cnt_ = 0; column_type_array_ = NULL; sstable_ = nullptr; exist_collation_free_ = false; use_cmp_nullsafe_ = false; is_oracle_mode_ = false; is_inited_ = false; } int ObSSTableRowkeyHelper::get_macro_block_meta( const MacroBlockId& macro_id, ObFullMacroBlockMeta& macro_meta, const int64_t schema_rowkey_column_cnt) { int ret = OB_SUCCESS; if (!macro_id.is_valid() || schema_rowkey_column_cnt <= 0) { ret = OB_INVALID_ARGUMENT; STORAGE_LOG(WARN, "Invalid argument to get macro block meta", K(macro_id), K(schema_rowkey_column_cnt), K(ret)); } else if (OB_FAIL(sstable_->get_meta(macro_id, macro_meta))) { STORAGE_LOG(WARN, "fail to get meta", K(ret), K(macro_id)); } else if (!macro_meta.is_valid()) { ret = OB_ERR_SYS; STORAGE_LOG(WARN, "Unexpected null macro meta", K(ret)); } else if (OB_ISNULL(macro_meta.meta_->endkey_) || OB_ISNULL(macro_meta.schema_->column_type_array_)) { ret = OB_ERR_SYS; STORAGE_LOG(WARN, "Unexpected null data macro endkey", K(macro_meta), K(ret)); } else if (OB_UNLIKELY(macro_meta.meta_->rowkey_column_number_ < schema_rowkey_column_cnt)) { ret = OB_ERR_UNEXPECTED; STORAGE_LOG(WARN, "Unexpected mis matched rowkey column number", K(macro_meta.meta_->rowkey_column_number_), K(schema_rowkey_column_cnt), K(ret)); } return ret; } int ObSSTableRowkeyHelper::build_macro_endkeys(const ObIArray& macro_ids, ObIAllocator& allocator, const int64_t schema_rowkey_column_cnt, const bool need_build_collation_free) { int ret = OB_SUCCESS; if (OB_UNLIKELY(macro_ids.count() == 0 || schema_rowkey_column_cnt <= 0)) { ret = OB_INVALID_ARGUMENT; STORAGE_LOG(WARN, "Invalid argument to build macro endkeys", K(macro_ids), K(schema_rowkey_column_cnt), K(ret)); } else { int64_t macro_block_count = macro_ids.count(); ObFullMacroBlockMeta full_meta; ObStoreRowkey rowkey; endkeys_.set_allocator(&allocator); endkeys_.set_capacity(static_cast(macro_block_count)); for (int64_t i = 0; OB_SUCC(ret) && i < macro_block_count; i++) { if (OB_FAIL(get_macro_block_meta(macro_ids.at(i), full_meta, schema_rowkey_column_cnt))) { STORAGE_LOG(WARN, "Failed to get macro block meta", K(i), K(ret)); } else { const ObMacroBlockMetaV2* macro_meta = full_meta.meta_; rowkey.assign(macro_meta->endkey_, macro_meta->rowkey_column_number_); // TODO consider column order if (rowkey.contains_min_or_max_obj() || (!use_cmp_nullsafe_ && rowkey.contains_null_obj())) { ret = OB_ERR_SYS; STORAGE_LOG(WARN, "Unexpected max or min macro endkey", K(rowkey), K(ret)); } else if (0 == i) { rowkey_column_cnt_ = macro_meta->rowkey_column_number_; column_type_array_ = full_meta.schema_->column_type_array_; exist_collation_free_ = false; if (need_build_collation_free) { for (int64_t i = 0; !exist_collation_free_ && i < rowkey_column_cnt_; i++) { if (column_type_array_[i].is_collation_free_compatible()) { exist_collation_free_ = true; } } if (exist_collation_free_) { collation_free_endkeys_.set_allocator(&allocator); collation_free_endkeys_.set_capacity(static_cast(macro_block_count)); } } } if (OB_FAIL(ret)) { } else if (OB_FAIL(endkeys_.push_back(rowkey))) { STORAGE_LOG(WARN, "Failed to push back macroblock endkey", K(rowkey), K(ret)); } else if (exist_collation_free_) { if (OB_ISNULL(macro_meta->collation_free_endkey_)) { // defend code to check null collation free endkey for (int64_t i = 0; i < rowkey_column_cnt_; i++) { // original varchar or char should be null now if (rowkey.get_obj_ptr()[i].is_collation_free_compatible()) { ret = OB_ERR_UNEXPECTED; STORAGE_LOG(WARN, "Unexpected null collation freekey with valid rowkey", K(i), K(*macro_meta), K(ret)); } } } else { rowkey.assign(macro_meta->collation_free_endkey_, macro_meta->rowkey_column_number_); } if (OB_SUCC(ret)) { if (OB_FAIL(collation_free_endkeys_.push_back(rowkey))) { STORAGE_LOG(WARN, "Failed to push back macroblock endkey", K(rowkey), K(ret)); } } } } } if (OB_SUCC(ret)) { if (endkeys_.count() != macro_block_count || (exist_collation_free_ && collation_free_endkeys_.count() != macro_block_count)) { ret = OB_ERR_UNEXPECTED; STORAGE_LOG(WARN, "Unexpected macro block endkeys count", K(macro_block_count), K_(exist_collation_free), K(endkeys_.count()), K(collation_free_endkeys_.count()), K(ret)); } } } return ret; } template int ObSSTableRowkeyHelper::make_rowkey_cmp_funcs(ObIAllocator& allocator) { int ret = OB_SUCCESS; if (OB_ISNULL(column_type_array_) || rowkey_column_cnt_ <= 0) { ret = OB_ERR_UNEXPECTED; STORAGE_LOG(WARN, "Unexpected null column type array or rowkey column cnt", KP_(column_type_array), K_(rowkey_column_cnt), K(ret)); } else { void* buf = NULL; T* cmp_funcs = NULL; cmp_funcs_.set_allocator(&allocator); cmp_funcs_.set_capacity(rowkey_column_cnt_); if (OB_ISNULL(buf = allocator.alloc(sizeof(T) * rowkey_column_cnt_))) { ret = OB_ALLOCATE_MEMORY_FAILED; STORAGE_LOG(WARN, "Failed to allocate memory for cmopare func", K_(rowkey_column_cnt), K(ret)); } else { cmp_funcs = reinterpret_cast(new (buf) T[rowkey_column_cnt_]); for (int64_t i = 0; OB_SUCC(ret) && i < rowkey_column_cnt_; i++) { if (OB_FAIL(cmp_funcs[i].init_compare_func(column_type_array_[i]))) { STORAGE_LOG(WARN, "Failed to init compare func", K(i), K(ret)); } else if (OB_FAIL(cmp_funcs_.push_back(&cmp_funcs[i]))) { STORAGE_LOG(WARN, "Failed to push back compare func", K(i), K(ret)); } } } if (OB_SUCC(ret) && exist_collation_free_) { collation_free_cmp_funcs_.set_allocator(&allocator); collation_free_cmp_funcs_.set_capacity(rowkey_column_cnt_); if (OB_ISNULL(buf = allocator.alloc(sizeof(T) * rowkey_column_cnt_))) { ret = OB_ALLOCATE_MEMORY_FAILED; STORAGE_LOG(WARN, "Failed to allocate memory for cmopare func", K_(rowkey_column_cnt), K(ret)); } else { cmp_funcs = reinterpret_cast(new (buf) T[rowkey_column_cnt_]); for (int64_t i = 0; OB_SUCC(ret) && i < rowkey_column_cnt_; i++) { if (column_type_array_[i].is_collation_free_compatible()) { if (OB_FAIL(cmp_funcs[i].init_compare_func_collation_free(column_type_array_[i]))) { STORAGE_LOG(WARN, "Failed to init compare func", K(i), K(ret)); } } else if (OB_FAIL(cmp_funcs[i].init_compare_func(column_type_array_[i]))) { STORAGE_LOG(WARN, "Failed to init compare func", K(i), K(ret)); } if (OB_SUCC(ret) && OB_FAIL(collation_free_cmp_funcs_.push_back(&cmp_funcs[i]))) { STORAGE_LOG(WARN, "Failed to push back compare func", K(i), K(ret)); } } } } } return ret; } int ObSSTableRowkeyHelper::init(const ObIArray& macro_ids, const ObSSTableMeta& sstable_meta, const bool need_build_collation_free, ObSSTable* sstable, ObIAllocator& allocator) { int ret = OB_SUCCESS; reset(); if (OB_UNLIKELY(!sstable_meta.is_valid()) || OB_ISNULL(sstable)) { ret = OB_INVALID_ARGUMENT; STORAGE_LOG(WARN, "Invalid argument to init sstable rowkey helper", K(sstable_meta), KP(sstable), K(ret)); } else if (macro_ids.count() > 0) { sstable_ = sstable; ObWorker::CompatMode mode; if (OB_FAIL(ObCompatModeGetter::get_tenant_mode(extract_tenant_id(sstable_meta.index_id_), mode))) { STORAGE_LOG(WARN, "Failed to get compat mode", K(sstable_meta), K(ret)); // rewrite ret for caller deal with ret = OB_TENANT_NOT_EXIST; } else { is_oracle_mode_ = (mode == ObWorker::CompatMode::ORACLE) && !is_sys_table(sstable_meta.index_id_); use_cmp_nullsafe_ = ObTableSchema::is_index_table(static_cast(sstable_meta.table_type_)) || TPKM_NEW_NO_PK == sstable_meta.table_mode_.pk_mode_; if (OB_FAIL(build_macro_endkeys( macro_ids, allocator, sstable_meta.rowkey_column_count_, need_build_collation_free))) { STORAGE_LOG(WARN, "Failed to build macro endkeys", K(ret)); } else if (OB_FAIL(build_rowkey_cmp_funcs(allocator))) { STORAGE_LOG(WARN, "Failed to build endkey cmp funcs", K(ret)); } else { is_inited_ = true; } } } return ret; } int ObSSTableRowkeyHelper::build_rowkey_cmp_funcs(ObIAllocator& allocator) { int ret = OB_SUCCESS; if (OB_ISNULL(column_type_array_) || rowkey_column_cnt_ <= 0) { ret = OB_ERR_UNEXPECTED; STORAGE_LOG(WARN, "Unexpected null column type array or rowkey column cnt", KP_(column_type_array), K_(rowkey_column_cnt), K(ret)); } else if (is_oracle_mode_ && use_cmp_nullsafe_) { ret = make_rowkey_cmp_funcs(allocator); } else if (is_oracle_mode_) { ret = make_rowkey_cmp_funcs(allocator); } else if (use_cmp_nullsafe_) { ret = make_rowkey_cmp_funcs(allocator); } else { ret = make_rowkey_cmp_funcs(allocator); } return ret; } int ObSSTableRowkeyHelper::locate_block_idx_(const ObExtStoreRowkey& ext_rowkey, const int64_t cmp_rowkey_col_cnt, const bool use_lower_bound, const int64_t last_block_idx, int64_t& block_idx) { int ret = OB_SUCCESS; if (OB_UNLIKELY(cmp_rowkey_col_cnt > ext_rowkey.get_store_rowkey().get_obj_cnt())) { ret = OB_INVALID_ARGUMENT; STORAGE_LOG(WARN, "Invalid argument to locate block index", K(ext_rowkey), K(cmp_rowkey_col_cnt), K(ret)); } else { RowkeyArray* rowkey_array = NULL; const ObStoreRowkey* cmp_rowkey = NULL; RowkeyCmpFuncArray* cmp_funcs = NULL; block_idx = -1; if (!exist_collation_free_) { cmp_rowkey = &(ext_rowkey.get_store_rowkey()); rowkey_array = &endkeys_; cmp_funcs = &cmp_funcs_; } else { bool use_collation_free = false; if (OB_FAIL(ext_rowkey.check_use_collation_free(!exist_collation_free_, use_collation_free))) { STORAGE_LOG(WARN, "Fail to check use collation free, ", K(ret), K(ext_rowkey)); } else if (use_collation_free && collation_free_cmp_funcs_.count() > 0) { cmp_rowkey = &(ext_rowkey.get_collation_free_store_rowkey()); rowkey_array = &collation_free_endkeys_; cmp_funcs = &collation_free_cmp_funcs_; } else { cmp_rowkey = &(ext_rowkey.get_store_rowkey()); rowkey_array = &endkeys_; cmp_funcs = &cmp_funcs_; use_collation_free = false; } } if (OB_SUCC(ret)) { if (cmp_rowkey->get_obj_cnt() == rowkey_column_cnt_ && last_block_idx >= 0 && last_block_idx < rowkey_array->count()) { // the rowkey may be in the same macro block, so check last macro block first if (compare_nullsafe(rowkey_array->at(last_block_idx), *cmp_rowkey, *cmp_funcs) >= 0) { if (0 == last_block_idx || compare_nullsafe(rowkey_array->at(last_block_idx - 1), *cmp_rowkey, *cmp_funcs) < 0) { block_idx = last_block_idx; } } } if (-1 == block_idx) { // binary search RowkeyComparor comparor(*cmp_funcs, cmp_rowkey_col_cnt < rowkey_column_cnt_); ObStoreRowkey refine_cmp_rowkey((const_cast(cmp_rowkey))->get_obj_ptr(), cmp_rowkey_col_cnt); RowkeyArray::iterator begin = rowkey_array->begin(); RowkeyArray::iterator end = rowkey_array->end(); RowkeyArray::iterator iter; if (use_lower_bound) { iter = std::lower_bound(begin, end, &refine_cmp_rowkey, comparor); } else { iter = std::upper_bound(begin, end, &refine_cmp_rowkey, comparor); } if (iter < end) { block_idx = iter - begin; } } } } return ret; } int ObSSTableRowkeyHelper::locate_block_idx_index( const ObExtStoreRowkey& ext_rowkey, const int64_t last_block_idx, int64_t& block_idx) { int ret = OB_SUCCESS; if (OB_UNLIKELY(!is_inited_)) { ret = OB_NOT_INIT; STORAGE_LOG(WARN, "ObSSTableRowkeyHelper is not inited", K(ret)); } else if (OB_UNLIKELY(!ext_rowkey.is_range_cutoffed())) { ret = OB_INVALID_ARGUMENT; STORAGE_LOG(WARN, "ExtRowkey is not range cutted", K(ext_rowkey), K(ret)); } else if (OB_UNLIKELY(ext_rowkey.get_range_cut_pos() == 0)) { block_idx = ext_rowkey.is_range_check_min() ? 0 : -1; } else { ret = locate_block_idx_( ext_rowkey, ext_rowkey.get_range_cut_pos(), ext_rowkey.is_range_check_min(), last_block_idx, block_idx); } return ret; } int ObSSTableRowkeyHelper::locate_block_idx_table( const ObExtStoreRowkey& ext_rowkey, const int64_t last_block_idx, int64_t& block_idx) { int ret = OB_SUCCESS; if (OB_UNLIKELY(!is_inited_)) { ret = OB_NOT_INIT; STORAGE_LOG(WARN, "ObSSTableRowkeyHelper is not inited", K(ret)); } else if (OB_UNLIKELY(!ext_rowkey.is_range_cutoffed())) { ret = OB_INVALID_ARGUMENT; STORAGE_LOG(WARN, "ExtRowkey is not range cutted", K(ext_rowkey), K(ret)); } else if (OB_UNLIKELY(ext_rowkey.get_range_cut_pos() == 0)) { block_idx = ext_rowkey.is_range_check_min() ? 0 : -1; } else if (OB_UNLIKELY(ext_rowkey.get_first_null_pos() == 0)) { block_idx = is_oracle_mode_ ? -1 : 0; } else { int64_t cmp_rowkey_col_cnt = ext_rowkey.get_range_cut_pos(); bool use_lower_bound = ext_rowkey.is_range_check_min(); if (ext_rowkey.get_first_null_pos() > 0 && ext_rowkey.get_first_null_pos() < cmp_rowkey_col_cnt) { cmp_rowkey_col_cnt = ext_rowkey.get_first_null_pos(); use_lower_bound = !is_oracle_mode_; } ret = locate_block_idx_(ext_rowkey, cmp_rowkey_col_cnt, use_lower_bound, last_block_idx, block_idx); } return ret; } } // end namespace storage } // end namespace oceanbase