aios/storage/indexlib/table/normal_table/NormalSchemaResolver.cpp (1,764 lines of code) (raw):

/* * Copyright 2014-present Alibaba Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "indexlib/table/normal_table/NormalSchemaResolver.h" #include "autil/Scope.h" #include "indexlib/config/BuildOptionConfig.h" #include "indexlib/config/FileCompressConfig.h" #include "indexlib/config/FileCompressConfigV2.h" #include "indexlib/config/IIndexConfig.h" #include "indexlib/config/MergeConfig.h" #include "indexlib/config/OfflineConfig.h" #include "indexlib/config/SingleFileCompressConfig.h" #include "indexlib/config/SortDescription.h" #include "indexlib/config/SortParam.h" #include "indexlib/config/TabletOptions.h" #include "indexlib/config/TabletSchema.h" #include "indexlib/config/UnresolvedSchema.h" #include "indexlib/config/date_index_config.h" #include "indexlib/config/index_partition_schema.h" #include "indexlib/config/package_index_config.h" #include "indexlib/config/primary_key_index_config.h" #include "indexlib/config/range_index_config.h" #include "indexlib/document/document_rewriter/DocumentInfoToAttributeRewriter.h" #include "indexlib/file_system/FileSystemCreator.h" #include "indexlib/file_system/FileSystemOptions.h" #include "indexlib/file_system/IDirectory.h" #include "indexlib/file_system/IFileSystem.h" #include "indexlib/file_system/JsonUtil.h" #include "indexlib/file_system/MountOption.h" #include "indexlib/index/ann/ANNIndexConfig.h" #include "indexlib/index/ann/Common.h" #include "indexlib/index/attribute/Common.h" #include "indexlib/index/attribute/config/AttributeConfig.h" #include "indexlib/index/deletionmap/DeletionMapConfig.h" #include "indexlib/index/field_meta/Common.h" #include "indexlib/index/field_meta/config/FieldMetaConfig.h" #include "indexlib/index/inverted_index/Common.h" #include "indexlib/index/inverted_index/config/DateIndexConfig.h" #include "indexlib/index/inverted_index/config/HighFreqVocabularyCreator.h" #include "indexlib/index/inverted_index/config/InvertedIndexConfig.h" #include "indexlib/index/inverted_index/config/PackageIndexConfig.h" #include "indexlib/index/inverted_index/config/PayloadConfig.h" #include "indexlib/index/inverted_index/config/RangeIndexConfig.h" #include "indexlib/index/inverted_index/config/SpatialIndexConfig.h" #include "indexlib/index/inverted_index/config/TruncateIndexNameMapper.h" #include "indexlib/index/inverted_index/config/TruncateProfileConfig.h" #include "indexlib/index/inverted_index/config/TruncateStrategy.h" #include "indexlib/index/operation_log/OperationLogConfig.h" #include "indexlib/index/pack_attribute/Common.h" #include "indexlib/index/pack_attribute/PackAttributeConfig.h" #include "indexlib/index/primary_key/Common.h" #include "indexlib/index/primary_key/config/PrimaryKeyIndexConfig.h" #include "indexlib/index/primary_key/config/PrimaryKeyLoadStrategyParam.h" #include "indexlib/index/source/Common.h" #include "indexlib/index/source/config/SourceIndexConfig.h" #include "indexlib/index/statistics_term/Constant.h" #include "indexlib/index/statistics_term/StatisticsTermIndexConfig.h" #include "indexlib/index/summary/Common.h" #include "indexlib/index/summary/config/SummaryIndexConfig.h" #include "indexlib/legacy/index_base/PartitionMeta.h" #include "indexlib/legacy/index_base/SchemaAdapter.h" #include "indexlib/table/BuiltinDefine.h" #include "indexlib/table/normal_table/Common.h" #include "indexlib/table/normal_table/virtual_attribute/VirtualAttributeConfig.h" namespace indexlibv2::table { AUTIL_LOG_SETUP(indexlib.table, NormalSchemaResolver); namespace { Status ResolveFileCompressConfig( const std::shared_ptr<std::vector<config::SingleFileCompressConfig>>& singleFileCompressConfigVec, std::shared_ptr<indexlibv2::config::InvertedIndexConfig> invertedIndexConfig) { // TODO: add test: no compress config in setting auto fileCompressConfig = invertedIndexConfig->GetFileCompressConfig(); if (!fileCompressConfig) { return Status::OK(); } std::string compressName = fileCompressConfig->GetCompressName(); auto fileCompressConfigV2 = std::make_shared<config::FileCompressConfigV2>(singleFileCompressConfigVec, compressName); invertedIndexConfig->SetFileCompressConfigV2(fileCompressConfigV2); return Status::OK(); } Status ResolveFileCompressConfig( const std::shared_ptr<std::vector<config::SingleFileCompressConfig>>& singleFileCompressConfigVec, std::shared_ptr<indexlibv2::index::AttributeConfig> attrConfig) { // TODO: add test: no compress config in setting auto fileCompressConfig = attrConfig->GetFileCompressConfig(); if (!fileCompressConfig) { return Status::OK(); } std::string compressName = fileCompressConfig->GetCompressName(); auto fileCompressConfigV2 = std::make_shared<config::FileCompressConfigV2>(singleFileCompressConfigVec, compressName); attrConfig->SetFileCompressConfigV2(fileCompressConfigV2); return Status::OK(); } Status ResolveFileCompressConfig( const std::shared_ptr<std::vector<config::SingleFileCompressConfig>>& singleFileCompressConfigVec, std::shared_ptr<indexlibv2::index::PackAttributeConfig> packAttrConfig) { auto fileCompressConfig = packAttrConfig->GetFileCompressConfig(); if (!fileCompressConfig) { return Status::OK(); } std::string compressName = fileCompressConfig->GetCompressName(); auto fileCompressConfigV2 = std::make_shared<config::FileCompressConfigV2>(singleFileCompressConfigVec, compressName); packAttrConfig->SetFileCompressConfigV2(fileCompressConfigV2); return Status::OK(); } Status ResolveFileCompressConfig( const std::shared_ptr<std::vector<config::SingleFileCompressConfig>>& singleFileCompressConfigVec, std::shared_ptr<indexlibv2::config::SummaryIndexConfig>& summaryIndexConfig) { for (index::summarygroupid_t groupId = 0; groupId < summaryIndexConfig->GetSummaryGroupConfigCount(); ++groupId) { auto groupConfig = summaryIndexConfig->GetSummaryGroupConfig(groupId); auto currentParam = groupConfig->GetSummaryGroupDataParam(); auto legacyCompressConfig = currentParam.GetFileCompressConfig(); if (!legacyCompressConfig) { continue; } std::string compressName = legacyCompressConfig->GetCompressName(); auto fileCompressConfigV2 = std::make_shared<config::FileCompressConfigV2>(singleFileCompressConfigVec, compressName); currentParam.SetFileCompressConfigV2(fileCompressConfigV2); groupConfig->SetSummaryGroupDataParam(currentParam); } return Status::OK(); } } // namespace Status NormalSchemaResolver::CheckStatisticsTermConfig(const config::TabletSchema& schema) const { for (auto indexConfig : schema.GetIndexConfigs(index::STATISTICS_TERM_INDEX_TYPE_STR)) { auto statConfig = std::dynamic_pointer_cast<index::StatisticsTermIndexConfig>(indexConfig); assert(statConfig != nullptr); for (auto invertedIndexName : statConfig->GetInvertedIndexNames()) { auto invertedIndexConfig = schema.GetIndexConfig(indexlib::index::INVERTED_INDEX_TYPE_STR, invertedIndexName); if (invertedIndexConfig == nullptr) { AUTIL_LOG(ERROR, "associated inverted index not exist in schema, index[%s] invert index[%s]", statConfig->GetIndexName().c_str(), invertedIndexName.c_str()); return Status::InternalError("associated inverted index not exist in schema"); } } } return Status::OK(); } Status NormalSchemaResolver::CheckIndexTruncateProfiles( const std::vector<indexlibv2::config::TruncateProfileConfig>& truncateProfileConfigs, const config::TabletSchema& schema, const std::shared_ptr<config::InvertedIndexConfig>& indexConfig) const { std::vector<std::string> profileNames = indexConfig->GetUseTruncateProfiles(); for (const auto& profileName : profileNames) { bool find = false; for (const auto& truncateProfileConfig : truncateProfileConfigs) { if (truncateProfileConfig.GetTruncateProfileName() == profileName) { find = true; auto sortParams = truncateProfileConfig.GetTruncateSortParams(); for (auto sortParam : sortParams) { std::string sortField = sortParam.GetSortField(); auto fieldConfig = schema.GetFieldConfig(sortField); if (!fieldConfig) { // may be DOC_PAYLOAD continue; } auto attributeConfig = std::dynamic_pointer_cast<index::AttributeConfig>( schema.GetIndexConfig(index::ATTRIBUTE_INDEX_TYPE_STR, sortField)); assert(attributeConfig); indexlib::config::CompressTypeOption compress = attributeConfig->GetCompressType(); if (compress.HasFp16EncodeCompress() || compress.HasInt8EncodeCompress()) { return Status::ConfigError("invalid field[%s] for truncate profile name [%s] of index [%s]", fieldConfig->GetFieldName().c_str(), profileName.c_str(), indexConfig->GetIndexName().c_str()); } } break; } } if (!find) { return Status::ConfigError("index [%s] truncate profile name [%s] is invalid", indexConfig->GetIndexName().c_str(), profileName.c_str()); } } return Status::OK(); } Status NormalSchemaResolver::CheckInvertedIndexConfig(const config::TabletSchema& schema) const { uint32_t fieldCount = schema.GetFieldConfigs().size(); std::vector<uint32_t> singleFieldIndexCounts(fieldCount, 0); std::map<indexlib::fieldid_t, std::string> fieldId2IndexName; std::map<std::string, std::set<std::string>> singleFieldIndexConfigsWithProfileNames; auto invertedIndexConfigs = schema.GetIndexConfigs(indexlib::index::INVERTED_INDEX_TYPE_STR); auto [truncateStatus, truncateProfileConfigs] = schema.GetRuntimeSettings().GetValue<std::vector<indexlibv2::config::TruncateProfileConfig>>( "truncate_profiles"); auto pkIndexConfig = schema.GetPrimaryKeyIndexConfig(); if (pkIndexConfig) { invertedIndexConfigs.push_back(pkIndexConfig); } for (const auto& indexConfig : invertedIndexConfigs) { std::string indexName = indexConfig->GetIndexName(); if (indexName == index::SUMMARY_INDEX_NAME) { return Status::ConfigError("index name can not use summary"); } try { indexConfig->Check(); } catch (const std::exception& e) { AUTIL_LOG(ERROR, "check invertedIndexConfig [%s] failed exception [%s].", indexName.c_str(), e.what()); return Status::ConfigError("check invertedIndexConfig failed"); } auto invertedIndexConfig = std::dynamic_pointer_cast<config::InvertedIndexConfig>(indexConfig); std::vector<std::string> profileNames = invertedIndexConfig->GetUseTruncateProfiles(); std::set<std::string> profileNameSet(profileNames.begin(), profileNames.end()); if (profileNames.size() != profileNameSet.size()) { return Status::ConfigError("index [%s] has duplicate profile name", indexName.c_str()); } if (invertedIndexConfig->HasTruncate()) { if (!truncateStatus.IsOK()) { return Status::ConfigError("index [%s] get truncate config failed", indexName.c_str()); } auto status = CheckIndexTruncateProfiles(truncateProfileConfigs, schema, invertedIndexConfig); RETURN_IF_STATUS_ERROR(status, "check truncate profiles for index [%s] failed", indexConfig->GetIndexName().c_str()); } InvertedIndexType indexType = invertedIndexConfig->GetInvertedIndexType(); if (indexType == it_pack || indexType == it_expack || indexType == it_customized) { RETURN_IF_STATUS_ERROR(CheckFieldsOrderInPackIndex(invertedIndexConfig, schema), "check index [%s] failed", indexName.c_str()); } else { RETURN_IF_STATUS_ERROR(CheckSingleFieldIndex(invertedIndexConfig, schema, &fieldId2IndexName, &singleFieldIndexConfigsWithProfileNames), "check index [%s] failed", indexName.c_str()); } if (indexType == it_spatial) { RETURN_IF_STATUS_ERROR(CheckSpatialIndexConfig(invertedIndexConfig, schema), "check index [%s] failed", indexName.c_str()); } } return Status::OK(); } Status NormalSchemaResolver::CheckSpatialIndexConfig(const std::shared_ptr<config::InvertedIndexConfig>& spatialIndexConfig, const config::TabletSchema& schema) const { auto fieldConfigs = spatialIndexConfig->GetFieldConfigs(); assert(fieldConfigs.size() == 1); // TODO: support line and polygon if (fieldConfigs[0]->GetFieldType() != ft_location) { return Status::OK(); } std::string fieldName = fieldConfigs[0]->GetFieldName(); auto attributeConfig = schema.GetIndexConfig(index::ATTRIBUTE_INDEX_TYPE_STR, fieldName); if (!attributeConfig) { return Status::ConfigError("field [%s] should in attributes, because in spatial index [%s]", fieldName.c_str(), spatialIndexConfig->GetIndexName().c_str()); } // TODO (zhuanghaolin.zhl) remove when support pack attribute // if (attrConf->GetPackAttributeConfig()) { // INDEXLIB_FATAL_ERROR(Schema, "field [%s] should not in pack attribute, because in spatial index [%s]", // fieldName.c_str(), spatialIndexConf->GetIndexName().c_str()); // } return Status::OK(); } // The check is intended to prevent adding redundant single field inverted index configs. // However, multiple single field index configs are necessary to support different term payload loads. // Thus multiple single field index configs with payload names are allowed. Status NormalSchemaResolver::CheckSingleFieldIndex( const std::shared_ptr<config::InvertedIndexConfig>& invertedIndexConfig, const config::TabletSchema& schema, std::map<fieldid_t, std::string>* fieldId2IndexName, std::map<std::string, std::set<std::string>>* singleFieldIndexConfigsWithProfileNames) const { for (const auto& fieldConfig : schema.GetFieldConfigs()) { fieldid_t fieldId = fieldConfig->GetFieldId(); if (!invertedIndexConfig->IsInIndex(fieldId)) { continue; } std::string fieldName = fieldConfig->GetFieldName(); std::vector<std::string> profileNames = invertedIndexConfig->GetUseTruncateProfiles(); std::set<std::string> profileNameSet(profileNames.begin(), profileNames.end()); if (singleFieldIndexConfigsWithProfileNames->find(fieldName) != singleFieldIndexConfigsWithProfileNames->end()) { for (const std::string& profileName : profileNameSet) { if (singleFieldIndexConfigsWithProfileNames->at(fieldName).find(profileName) != singleFieldIndexConfigsWithProfileNames->at(fieldName).end()) { return Status::ConfigError("single field [%s] has more than one index with the same profile [%s]", fieldName.c_str(), profileName.c_str()); } else { singleFieldIndexConfigsWithProfileNames->at(fieldName).insert(profileName); } } } else { (*singleFieldIndexConfigsWithProfileNames)[fieldName] = std::set<std::string> {profileNameSet}; } auto iter = fieldId2IndexName->find(fieldId); if (iter != fieldId2IndexName->end()) { std::string indexName = iter->second; auto lastInvertedIndexConfig = std::dynamic_pointer_cast<config::InvertedIndexConfig>( schema.GetIndexConfig(indexlib::index::INVERTED_INDEX_TYPE_STR, indexName)); assert(lastInvertedIndexConfig); auto hasEmptyNamePayLoadConfig = [](const std::shared_ptr<config::InvertedIndexConfig>& invertedIndexConfig) { if (invertedIndexConfig->GetShardingType() == config::InvertedIndexConfig::IndexShardingType::IST_NEED_SHARDING) { for (const auto& shardConfig : invertedIndexConfig->GetShardingIndexConfigs()) { auto payloadConfig = shardConfig->GetTruncatePayloadConfig(); if (!payloadConfig.IsInitialized()) { return true; } } return false; } else { auto payloadConfig = invertedIndexConfig->GetTruncatePayloadConfig(); return !payloadConfig.IsInitialized(); } }; if (hasEmptyNamePayLoadConfig(lastInvertedIndexConfig) || hasEmptyNamePayLoadConfig(invertedIndexConfig)) { return Status::ConfigError("field [%s] has more than one single field index and payload name is empty", fieldName.c_str()); } } (*fieldId2IndexName)[fieldId] = invertedIndexConfig->GetIndexName(); } return Status::OK(); } Status NormalSchemaResolver::CheckFieldsOrderInPackIndex( const std::shared_ptr<config::InvertedIndexConfig>& invertedIndexConfig, const config::TabletSchema& schema) const { // some legacy index config cannot convert auto packageConfigV2 = std::dynamic_pointer_cast<config::PackageIndexConfig>(invertedIndexConfig); auto packageConfigV1 = std::dynamic_pointer_cast<indexlib::config::PackageIndexConfig>(invertedIndexConfig); if (!packageConfigV2 && !packageConfigV1) { return Status::ConfigError("get package config failed for index [%s]", invertedIndexConfig->GetIndexName().c_str()); } int32_t lastFieldPosition = -1; fieldid_t lastFieldId = -1; for (const auto& fieldConfig : schema.GetFieldConfigs()) { fieldid_t fieldId = fieldConfig->GetFieldId(); if (packageConfigV1 && !packageConfigV1->IsInIndex(fieldId)) { continue; } if (packageConfigV2 && !packageConfigV2->IsInIndex(fieldId)) { continue; } int32_t curFieldPosition = packageConfigV2 ? packageConfigV2->GetFieldIdxInPack(fieldId) : packageConfigV1->GetFieldIdxInPack(fieldId); if (curFieldPosition < lastFieldPosition) { std::string beforeFieldName = schema.GetFieldConfig(lastFieldId)->GetFieldName(); std::string afterFieldName = schema.GetFieldConfig(fieldId)->GetFieldName(); return Status::ConfigError( "wrong field order in indexConfig [%s], expect field [%s] before field [%s], but not", invertedIndexConfig->GetIndexName().c_str(), beforeFieldName.c_str(), afterFieldName.c_str()); } lastFieldPosition = curFieldPosition; lastFieldId = fieldId; } return Status::OK(); } Status NormalSchemaResolver::CheckTruncateSortParams( const std::vector<indexlibv2::config::TruncateProfileConfig>& truncateProfileConfigs, const config::TabletSchema& schema) const { for (const auto& truncateProfileConfig : truncateProfileConfigs) { try { truncateProfileConfig.Check(); } catch (const std::exception& e) { AUTIL_LOG(ERROR, "check truncateProfileConfig [%s] failed exception [%s].", truncateProfileConfig.GetTruncateProfileName().c_str(), e.what()); return Status::ConfigError("check truncate profile config failed"); } auto sortParams = truncateProfileConfig.GetTruncateSortParams(); for (const auto& sortParam : sortParams) { std::string sortFieldName = sortParam.GetSortField(); if (indexlib::DOC_PAYLOAD_FIELD_NAME == sortFieldName) { continue; } auto fieldConfig = schema.GetFieldConfig(sortFieldName); if (!fieldConfig) { return Status::ConfigError("truncate sort field [%s] is not in schema", sortFieldName.c_str()); } auto attributeConfig = schema.GetIndexConfig(index::ATTRIBUTE_INDEX_TYPE_STR, sortFieldName); if (!attributeConfig) { return Status::ConfigError("truncate sort field [%s] has no attribute", sortFieldName.c_str()); } // TODO (zhuanghaolin.zhl) remove when support pack atrribute // if (attrConfig->GetPackAttributeConfig() != NULL) { // INDEXLIB_FATAL_ERROR(Schema, // "truncate sort field [%s] " // "should not be in pack attribute.", // fieldConfig->GetFieldName().c_str()); // } } } return Status::OK(); } // Check the case that if payload name is used in any of the sort params that uses DOC_PAYLOAD, all other sort params // that use DOC_PAYLOAD should also specify payload name. It's also valid that non of the sort params that uses // DOC_PAYLOAD specifies payload name. This is to be backward compatible. Status NormalSchemaResolver::CheckTruncateProfileSchema( const std::vector<indexlibv2::config::TruncateProfileConfig>& truncateProfileConfigs) const { bool payloadNameSpecified = false; std::vector<std::string> unspecifiedFieldProfiles; for (const auto& truncateProfileConfig : truncateProfileConfigs) { auto sortParams = truncateProfileConfig.GetTruncateSortParams(); for (const auto& sortParam : sortParams) { if (indexlib::DOC_PAYLOAD_FIELD_NAME == sortParam.GetSortField()) { if (truncateProfileConfig.GetPayloadConfig().IsInitialized()) { payloadNameSpecified = true; } else { unspecifiedFieldProfiles.emplace_back(truncateProfileConfig.GetTruncateProfileName()); } } } } if (payloadNameSpecified && !unspecifiedFieldProfiles.empty()) { std::stringstream ss; ss << "payload name specified, all truncate profiles should specify payload name, but "; for (const auto& profile : unspecifiedFieldProfiles) { ss << "[" << profile << "] "; } ss << "not specify payload name"; return Status::ConfigError("%s", ss.str().c_str()); } return Status::OK(); } Status NormalSchemaResolver::CheckSummaryConfig(const config::TabletSchema& schema) const { for (const auto& summaryConfig : schema.GetIndexConfigs(index::SUMMARY_INDEX_TYPE_STR)) { try { summaryConfig->Check(); } catch (const std::exception& e) { AUTIL_LOG(ERROR, "check summaryConfig [%s] failed exception [%s].", summaryConfig->GetIndexName().c_str(), e.what()); return Status::ConfigError("check summaryConfig failed"); } } return Status::OK(); } Status NormalSchemaResolver::CheckAttributeConfig(const config::TabletSchema& schema) const { auto attributeConfigs = schema.GetIndexConfigs(index::ATTRIBUTE_INDEX_TYPE_STR); for (const auto& attributeConfig : attributeConfigs) { std::string indexName = attributeConfig->GetIndexName(); try { attributeConfig->Check(); } catch (const std::exception& e) { AUTIL_LOG(ERROR, "check attributeConfig [%s] failed exception [%s].", indexName.c_str(), e.what()); return Status::ConfigError("check attributeConfig failed"); } // TODO (zhuanghaolin.zhl) remove when support pack attribute // if (config->SupportNull() && config->GetPackAttributeConfig() != NULL) { // INDEXLIB_FATAL_ERROR(Schema, "attribute [%s] in pack attribute [%s] not support enable null.", // config->GetAttrName().c_str(), config->GetPackAttributeConfig()->GetPackName().c_str()); // } // FieldType ft = config->GetFieldType(); // if ((ft == ft_date || ft == ft_time || ft == ft_timestamp) && config->GetPackAttributeConfig() != NULL) { // INDEXLIB_FATAL_ERROR(Schema, "attribute [%s] with field type [%s] not support in pack attribute [%s].", // config->GetAttrName().c_str(), FieldConfig::FieldTypeToStr(ft), // config->GetPackAttributeConfig()->GetPackName().c_str()); // } } return Status::OK(); } Status NormalSchemaResolver::Check(const config::TabletSchema& schema) const { RETURN_IF_STATUS_ERROR(CheckStatisticsTermConfig(schema), "check statistics term config failed."); auto fieldConfigs = schema.GetFieldConfigs(); if (fieldConfigs.empty()) { return Status::ConfigError("field config is empty"); } auto [truncateStatus, truncateProfileConfigs] = schema.GetRuntimeSettings().GetValue<std::vector<indexlibv2::config::TruncateProfileConfig>>( "truncate_profiles"); if (truncateStatus.IsOK()) { RETURN_IF_STATUS_ERROR(CheckTruncateSortParams(truncateProfileConfigs, schema), "check truncate sort param failed"); RETURN_IF_STATUS_ERROR(CheckTruncateProfileSchema(truncateProfileConfigs), "check truncate profile failed"); } if (!truncateStatus.IsNotFound() && !truncateStatus.IsOK()) { return truncateStatus; } RETURN_IF_STATUS_ERROR(CheckInvertedIndexConfig(schema), "check inverted index config failed."); RETURN_IF_STATUS_ERROR(CheckAttributeConfig(schema), "check attribute config failed"); RETURN_IF_STATUS_ERROR(CheckSummaryConfig(schema), "check summary config failed"); RETURN_IF_STATUS_ERROR(CheckFieldMetaConfig(schema), "check field meta config failed"); return Status::OK(); } Status NormalSchemaResolver::CheckFieldMetaConfig(const config::TabletSchema& schema) const { auto fieldMetaConfigs = schema.GetIndexConfigs(indexlib::index::FIELD_META_INDEX_TYPE_STR); for (const auto& fieldMetaConfig : fieldMetaConfigs) { try { fieldMetaConfig->Check(); } catch (const std::exception& e) { AUTIL_LOG(ERROR, "check fieldMetaConfig [%s] failed exception [%s].", fieldMetaConfig->GetIndexName().c_str(), e.what()); return Status::ConfigError("check fieldMetaConfig failed"); } } return Status::OK(); } Status NormalSchemaResolver::LegacySchemaToTabletSchema(const indexlib::config::IndexPartitionSchema& legacySchema, config::UnresolvedSchema* schema) { RETURN_IF_STATUS_ERROR(FillFileCompressToSettings(legacySchema, schema), "fill file compress config to settings failed"); RETURN_IF_STATUS_ERROR(FillIndexConfigs(legacySchema, schema), "fill index configs from legacy schema to tablet schema failed"); FillTTLToSettings(legacySchema, schema); RETURN_IF_STATUS_ERROR(FillTruncateProfile(legacySchema, schema), "fill truncate profile failed"); RETURN_IF_STATUS_ERROR(FillDictionaryToSettings(legacySchema, schema), "fill dictionary failed"); RETURN_IF_STATUS_ERROR(FillAdaptiveDictionaryToSettings(legacySchema, schema), "fill adaptive dictionary failed"); return Status::OK(); } Status NormalSchemaResolver::ResolveLegacySchema(const std::string& indexPath, indexlib::config::IndexPartitionSchema* schema) { RETURN_IF_STATUS_ERROR(LoadAdaptiveVocabulary(schema, indexPath), "load adaptive vocabulary failed"); return Status::OK(); } Status NormalSchemaResolver::ResolveTabletSchema(const std::string& indexPath, config::UnresolvedSchema* schema) { RETURN_IF_STATUS_ERROR(ResolveRuntimeSettings(indexPath, schema), "resolve runtime settings failed"); RETURN_IF_STATUS_ERROR(ResolveFieldMeta(schema), "resolve field meta failed"); RETURN_IF_STATUS_ERROR(AddBuiltinIndex(schema), "add built index failed"); RETURN_IF_STATUS_ERROR(ResolveInvertedIndex(schema), "resolve inverted index failed"); RETURN_IF_STATUS_ERROR(ResolveSummary(schema, _options), "resolve summary failed"); RETURN_IF_STATUS_ERROR(ResolveSource(schema, _options), "resolve source failed"); RETURN_IF_STATUS_ERROR(ResolveAttribute(schema, _options), "resolve attribute failed"); return Status::OK(); } Status NormalSchemaResolver::ResolveFieldMeta(config::UnresolvedSchema* schema) { // const auto& indexConfigs = schema->GetIndexConfigs(indexlib::index::FIELD_META_INDEX_TYPE_STR); // for (auto indexConfig : indexConfigs) { // auto fieldMetaConfig = std::dynamic_pointer_cast<indexlib::index::FieldMetaConfig>(indexConfig); // if (!fieldMetaConfig) { // assert(false); // return Status::Corruption("cast config [%s] to field meta config failed", // indexConfig->GetIndexName().c_str()); // } // if (fieldMetaConfig->GetStoreMetaSourceType() == FieldMetaConfig::MetaSourceType::MST_NONE) { // continue; // } // auto fieldConfig = fieldMetaConfig->GetFieldConfig(); // const std::string& fieldName = fieldConfig->GetFieldName(); // auto attributeConfig = schema->GetIndexConfig(index::ATTRIBUTE_INDEX_TYPE_STR, fieldName); // if (!attributeConfig) { // auto addAttrConfig = std::make_shared<index::AttributeConfig>(); // RETURN_IF_STATUS_ERROR(addAttrConfig->Init(fieldConfig), "attribute config init failed"); // addAttrConfig->SetUpdatable(true); // addAttrConfig->SetAttrId(schema->GetIndexConfigs(addAttrConfig->GetIndexType()).size()); // RETURN_IF_STATUS_ERROR(schema->AddIndexConfig(addAttrConfig), "add attribute config [%s] failed", // fieldName.c_str()); // AUTIL_LOG(INFO, "add attribute config [%s] for field meta [%s]", fieldName.c_str(), // fieldMetaConfig->GetIndexName().c_str()); // } // } auto [status, enableAllTextFieldMeta] = schema->GetRuntimeSettings().GetValue<bool>("enable_all_text_field_meta"); if (status.IsOK() && enableAllTextFieldMeta) { const auto& indexConfigs = schema->GetIndexConfigs(indexlib::index::FIELD_META_INDEX_TYPE_STR); std::set<std::string> metaFields; for (const auto& indexConfig : indexConfigs) { const auto& fieldMetaConfig = std::dynamic_pointer_cast<indexlib::index::FieldMetaConfig>(indexConfig); const auto& fieldConfig = fieldMetaConfig->GetFieldConfig(); assert(fieldConfig); metaFields.insert(fieldConfig->GetFieldName()); } const auto& fieldConfigs = schema->GetFieldConfigs(); for (const auto& fieldConfig : fieldConfigs) { const std::string& fieldName = fieldConfig->GetFieldName(); if (fieldConfig->GetFieldType() == ft_text && !metaFields.count(fieldName)) { auto addFieldMetaConfig = std::make_shared<indexlib::index::FieldMetaConfig>(); RETURN_IF_STATUS_ERROR(addFieldMetaConfig->Init(fieldConfig, fieldName), "field meta config init failed"); RETURN_IF_STATUS_ERROR(schema->AddIndexConfig(addFieldMetaConfig), "add field meta config [%s] failed", fieldName.c_str()); AUTIL_LOG(INFO, "add field meta config [%s] for enableAllTextFieldMeta", fieldName.c_str()); } } } else if (!status.IsNotFound()) { RETURN_IF_STATUS_ERROR(status, "%s", status.ToString().c_str()); } return Status::OK(); } Status NormalSchemaResolver::ResolveRuntimeSettings(const std::string& indexPath, config::UnresolvedSchema* schema) { RETURN_IF_STATUS_ERROR(LoadAdaptiveVocabulary(schema, indexPath), "load adaptive vocabulary failed"); RETURN_IF_STATUS_ERROR(ResolveTTLField(schema), "resolve ttl field failed"); RETURN_IF_STATUS_ERROR(ResolveSortDescriptions(schema, _options, indexPath), "resolve sort descs failed"); RETURN_IF_STATUS_ERROR(ResolveTruncateIndexConfigs(indexPath, schema), "resolve truncate index configs failed"); return Status::OK(); } Status NormalSchemaResolver::ResolveInvertedIndex(config::UnresolvedSchema* schema) { RETURN_IF_STATUS_ERROR(ResolveSpatialIndex(schema), "resolve spatial index failed"); RETURN_IF_STATUS_ERROR(ResolvePKIndexConfig(schema, _options), "resolve pk index config failed"); RETURN_IF_STATUS_ERROR(ResolveInvertedIndexId(schema), "resolve inverted index id failed"); RETURN_IF_STATUS_ERROR(ResolveGeneralInvertedIndex(schema, _options), "resolve inverted index failed"); return Status::OK(); } Status NormalSchemaResolver::ResolveAttribute(config::UnresolvedSchema* schema, config::TabletOptions* options) { RETURN_IF_STATUS_ERROR(ResolveGeneralValue(schema, options), "resolve generalized value failed"); return Status::OK(); } Status NormalSchemaResolver::ResolveInvertedIndexId(config::UnresolvedSchema* schema) { const auto& invertedConfigs = schema->GetIndexConfigs(indexlib::index::INVERTED_INDEX_TYPE_STR); int indexId = invertedConfigs.size(); const auto& pkIndexConfig = std::dynamic_pointer_cast<index::PrimaryKeyIndexConfig>(schema->GetPrimaryKeyIndexConfig()); if (pkIndexConfig) { if (pkIndexConfig->GetIndexId() == INVALID_INDEXID) { pkIndexConfig->SetIndexId(indexId); } indexId++; } const auto& configs = schema->GetIndexConfigs(indexlibv2::index::ANN_INDEX_TYPE_STR); for (const auto& config : configs) { const auto& annConfig = std::dynamic_pointer_cast<indexlibv2::config::ANNIndexConfig>(config); if (annConfig) { annConfig->SetIndexId(indexId++); } } return Status::OK(); } Status NormalSchemaResolver::ResolvePKIndexConfig(config::UnresolvedSchema* schema, config::TabletOptions* option) { const auto& configs = schema->GetIndexConfigs(indexlibv2::index::PRIMARY_KEY_INDEX_TYPE_STR); if (configs.empty()) { return Status::OK(); } assert(configs.size() == 1); const auto& pkIndexConfig = std::dynamic_pointer_cast<index::PrimaryKeyIndexConfig>(configs[0]); assert(pkIndexConfig); autil::ScopeGuard done([&schema, &pkIndexConfig]() { schema->SetPrimaryKeyIndexConfig(pkIndexConfig); }); if (!option) { AUTIL_LOG(WARN, "option is NULL, not resolve pk index config"); return Status::OK(); } std::string speedUpPKReaderPath = "online_index_config.build_config.speedup_primary_key_reader"; bool speedUpPrimaryKeyReader = false; std::string speedUpPKParamPath = "online_index_config.build_config.speedup_primary_key_param"; std::string speedUpPKParam = ""; if (auto status = option->GetFromRawJson(speedUpPKReaderPath, &speedUpPrimaryKeyReader); !status.IsOKOrNotFound()) { AUTIL_LOG(WARN, "get config [%s] from option failed, status[%s]", speedUpPKReaderPath.c_str(), status.ToString().c_str()); } if (speedUpPrimaryKeyReader) { if (auto status = option->GetFromRawJson(speedUpPKParamPath, &speedUpPKParam); !status.IsOKOrNotFound()) { AUTIL_LOG(WARN, "get config [%s] from option failed, status[%s]", speedUpPKParamPath.c_str(), status.ToString().c_str()); } } indexlib::config::PrimaryKeyLoadStrategyParam::PrimaryKeyLoadMode loadMode; if (speedUpPrimaryKeyReader || pkIndexConfig->GetPrimaryKeyIndexType() == pk_hash_table) { loadMode = indexlib::config::PrimaryKeyLoadStrategyParam::HASH_TABLE; } else if (pkIndexConfig->GetPrimaryKeyIndexType() == pk_block_array) { loadMode = indexlib::config::PrimaryKeyLoadStrategyParam::BLOCK_VECTOR; } else { loadMode = indexlib::config::PrimaryKeyLoadStrategyParam::SORTED_VECTOR; } auto status = pkIndexConfig->SetPrimaryKeyLoadParam(loadMode, speedUpPKParam); RETURN_IF_STATUS_ERROR(status, "set pk load param failed for param [%s]", speedUpPKParam.c_str()); std::string pkParallelLookupPath = "online_index_config.build_config.enable_build_parallel_lookup_pk"; bool pkParallelLookup = false; if (auto status = option->GetFromRawJson(pkParallelLookupPath, &pkParallelLookup); !status.IsOKOrNotFound()) { AUTIL_LOG(WARN, "get config from tablet options failed, status[%s]", status.ToString().c_str()); } if (auto status = option->GetFromRawJson(pkParallelLookupPath, &pkParallelLookup); !status.IsOKOrNotFound()) { AUTIL_LOG(WARN, "get config from tablet options failed, status[%s]", status.ToString().c_str()); } if (pkParallelLookup) { pkIndexConfig->EnableParallelLookupOnBuild(); } std::string enableBloomFilterPath = "online_index_config.build_config.enable_bloom_filter_for_primary_key_reader"; std::string bloomFilterMultipleNumPath = "online_index_config.build_config.bloom_filter_multiple_num"; bool enableBloomFilter = false; if (auto status = option->GetFromRawJson(enableBloomFilterPath, &enableBloomFilter); !status.IsOKOrNotFound()) { AUTIL_LOG(WARN, "get configfrom option failed, status[%s]", status.ToString().c_str()); } if (enableBloomFilter) { uint32_t bloomFilterMultipleNum = index::PrimaryKeyIndexConfig::DEFAULT_BLOOM_FILTER_MULTIPLE_NUM; if (auto status = option->GetFromRawJson(bloomFilterMultipleNumPath, &bloomFilterMultipleNum); !status.IsOKOrNotFound()) { AUTIL_LOG(WARN, "get config from tablet options failed, status[%s]", status.ToString().c_str()); } else { if (bloomFilterMultipleNum <= 1 || bloomFilterMultipleNum > 16) { AUTIL_LOG(WARN, "invalid bloom_filter_multiple_num [%u], use default [%u].", bloomFilterMultipleNum, index::PrimaryKeyIndexConfig::DEFAULT_BLOOM_FILTER_MULTIPLE_NUM); bloomFilterMultipleNum = index::PrimaryKeyIndexConfig::DEFAULT_BLOOM_FILTER_MULTIPLE_NUM; } } if (loadMode != indexlib::config::PrimaryKeyLoadStrategyParam::HASH_TABLE) { pkIndexConfig->EnableBloomFilterForPkReader(bloomFilterMultipleNum); } else { AUTIL_LOG(WARN, "hash table type primary key will not enable bloom filter."); } } return Status::OK(); } Status NormalSchemaResolver::FillFileCompressToSettings(const indexlib::config::IndexPartitionSchema& legacySchema, config::UnresolvedSchema* schema) { const auto& fileCompressSchema = legacySchema.GetFileCompressSchema(); if (!fileCompressSchema) { return Status::OK(); } auto [status, _] = schema->GetRuntimeSettings().GetValue<std::shared_ptr<std::vector<config::SingleFileCompressConfig>>>( config::SingleFileCompressConfig::FILE_COMPRESS_CONFIG_KEY); if (status.IsOK()) { return Status::OK(); } if (!status.IsNotFound()) { RETURN_IF_STATUS_ERROR(status, "%s", status.ToString().c_str()); } std::vector<std::shared_ptr<config::SingleFileCompressConfig>> configs; const auto& legacyConfigs = fileCompressSchema->GetFileCompressConfigs(); if (legacyConfigs.empty()) { return Status::OK(); } for (const auto& legacyConfig : legacyConfigs) { std::shared_ptr<config::SingleFileCompressConfig> config = legacyConfig->CreateSingleFileCompressConfig(); if (!config) { RETURN_IF_STATUS_ERROR(Status::InternalError(), "convert legacy file compress config to v2 single file compress config failed, [%s]", legacyConfig->GetCompressName().c_str()); } configs.push_back(std::move(config)); } bool ret = schema->SetRuntimeSetting(config::SingleFileCompressConfig::FILE_COMPRESS_CONFIG_KEY, configs, false); if (!ret) { RETURN_IF_STATUS_ERROR(Status::InternalError(), "schema set setting [%s] failed", config::SingleFileCompressConfig::FILE_COMPRESS_CONFIG_KEY.c_str()); } return Status::OK(); } Status NormalSchemaResolver::ResolveSummary(config::UnresolvedSchema* schema, config::TabletOptions* options) { const auto& config = schema->GetIndexConfig(indexlib::index::SUMMARY_INDEX_TYPE_STR, indexlib::index::SUMMARY_INDEX_NAME); if (!config) { return Status::OK(); } const auto& summaryConfig = std::dynamic_pointer_cast<config::SummaryIndexConfig>(config); assert(summaryConfig); // timestamp in summary const auto& fieldConfigs = summaryConfig->GetFieldConfigs(); for (const auto& fieldConfig : fieldConfigs) { if (fieldConfig->GetFieldType() != ft_timestamp) { continue; } const auto& fieldName = fieldConfig->GetFieldName(); if (schema->GetIndexConfig(indexlib::index::ATTRIBUTE_INDEX_TYPE_STR, fieldName)) { continue; } AUTIL_LOG(INFO, "inner add timestamp type attribute [%s] to support storage in summary", fieldName.c_str()); auto attributeConfig = std::make_shared<index::AttributeConfig>(); RETURN_IF_STATUS_ERROR(attributeConfig->Init(fieldConfig), "attribute config init failed, field [%s]", fieldName.c_str()); attributeConfig->SetAttrId(schema->GetIndexConfigs(attributeConfig->GetIndexType()).size()); RETURN_IF_STATUS_ERROR(schema->AddIndexConfig(attributeConfig), "add summary accompany attribute [%s] failed", fieldName.c_str()); } // disableSummarys std::string disableSummarys; if (auto status = options->GetFromRawJson("%index_config%.disable_fields.summarys", &disableSummarys); !status.IsOKOrNotFound()) { AUTIL_LOG(ERROR, "get %%index_config%%.disable_fields.summarys failed"); return status; } if (disableSummarys == "__SUMMARY_FIELD_ALL__") { summaryConfig->DisableAllFields(); return Status::OK(); } // SetNeedStoreSummary std::set<std::string> attributeFields; std::set<std::string> packAttributeFields; std::set<std::string> sourceFields; for (auto fieldConfig : schema->GetIndexFieldConfigs(indexlib::index::ATTRIBUTE_INDEX_TYPE_STR)) { attributeFields.insert(fieldConfig->GetFieldName()); } for (auto fieldConfig : schema->GetIndexFieldConfigs(indexlib::index::PACK_ATTRIBUTE_INDEX_TYPE_STR)) { packAttributeFields.insert(fieldConfig->GetFieldName()); } if (auto sourceIndexConfig = std::dynamic_pointer_cast<config::SourceIndexConfig>( schema->GetIndexConfig(index::SOURCE_INDEX_TYPE_STR, index::SOURCE_INDEX_NAME))) { auto [status, notReuseFields] = schema->GetRuntimeSettings().GetValue<std::vector<std::string>>("summary_not_reuse_source_fields"); if (!status.IsOK() && !status.IsNotFound()) { AUTIL_LOG(ERROR, "get summary not reuse source fields failed, error [%s]", status.ToString().c_str()); return status; } std::set<std::string> notReuseSourceFields(notReuseFields.begin(), notReuseFields.end()); for (auto fieldConfig : schema->GetIndexFieldConfigs(indexlib::index::SOURCE_INDEX_TYPE_STR)) { auto fieldName = fieldConfig->GetFieldName(); if (summaryConfig->IsInSummary(fieldConfig->GetFieldId()) && sourceIndexConfig->GetGroupIdByFieldName(fieldName) != index::INVALID_SOURCEGROUPID && !notReuseSourceFields.count(fieldName) && fieldConfig->GetFieldType() != ft_text) { // 过滤掉udf字段、禁止复用字段、TEXT字段、不在summary里的字段 sourceFields.insert(fieldName); } } std::vector<std::string> sourceFieldvec(sourceFields.begin(), sourceFields.end()); if (!sourceFieldvec.empty() && !schema->SetRuntimeSetting(NORMAL_TABLE_SUMMARY_REUSE_SOURCE_FIELDS, sourceFieldvec, true)) { RETURN_STATUS_ERROR(Corruption, "set summary reuse source runtime setting failed"); } } summaryConfig->SetNeedStoreSummary(false); for (const auto& summaryField : summaryConfig->GetFieldConfigs()) { auto fieldName = summaryField->GetFieldName(); auto fieldId = summaryField->GetFieldId(); if (!sourceFields.count(fieldName) && !attributeFields.count(fieldName) && !packAttributeFields.count(fieldName)) { summaryConfig->SetNeedStoreSummary(fieldId); } } return Status::OK(); } Status NormalSchemaResolver::ResolveSource(config::UnresolvedSchema* schema, config::TabletOptions* options) { const auto& sourceConfig = std::dynamic_pointer_cast<config::SourceIndexConfig>( schema->GetIndexConfig(indexlib::index::SOURCE_INDEX_TYPE_STR, indexlib::index::SOURCE_INDEX_NAME)); if (!sourceConfig) { return Status::OK(); } if (!schema->SetRuntimeSetting("need_original_field_value", true, true)) { assert(false); // to jsonize 按说不会抛异常 RETURN_STATUS_ERROR(InternalError, "set runtime setting faield"); } // disableSources std::string disableSources; if (auto status = options->GetFromRawJson("online_index_config.disable_fields.sources", &disableSources); !status.IsOKOrNotFound()) { AUTIL_LOG(ERROR, "get online_index_config.disable_fields.sources failed"); return status; } if (disableSources == "__SOURCE_FIELD_ALL__") { sourceConfig->DisableAllFields(); return Status::OK(); } std::string SOURCE_FIELD_GROUP = "__SOURCE_FIELD_GROUP__:"; if (disableSources.find(SOURCE_FIELD_GROUP) == 0) { std::string groupIdStr = disableSources.substr(SOURCE_FIELD_GROUP.size()); auto splitedStrs = autil::StringUtil::split(groupIdStr, ","); for (const auto& groupStr : splitedStrs) { index::sourcegroupid_t gid = 0; if (!autil::StringUtil::numberFromString(groupStr, gid)) { AUTIL_LOG(ERROR, "parse groupid str [%s] failed, original string is [%s]", groupStr.c_str(), disableSources.c_str()); return Status::ConfigError("parse groupid failed"); } sourceConfig->DisableFieldGroup(gid); } } return Status::OK(); } void NormalSchemaResolver::FillTTLToSettings(const indexlib::config::IndexPartitionSchema& legacySchema, config::UnresolvedSchema* schema) { if (legacySchema.TTLEnabled()) { auto [status, ttlEnable] = schema->GetRuntimeSettings().GetValue<bool>("enable_ttl"); auto [status1, fieldName] = schema->GetRuntimeSettings().GetValue<std::string>("ttl_field_name"); auto [status2, defaultTTL] = schema->GetRuntimeSettings().GetValue<int64_t>("default_ttl"); if (status.IsOK() || status1.IsOK() || status2.IsOK()) { // tablet schema setting has ttl config, not use legacy schema return; } bool ret = schema->SetRuntimeSetting("enable_ttl", true, false); ret = schema->SetRuntimeSetting("ttl_field_name", legacySchema.GetTTLFieldName(), false) && ret; ret = schema->SetRuntimeSetting("default_ttl", legacySchema.GetDefaultTTL(), false) && ret; assert(ret); } } Status NormalSchemaResolver::ResolveSpatialIndex(config::UnresolvedSchema* schema) { for (auto indexConfig : schema->GetIndexConfigs(indexlib::index::INVERTED_INDEX_TYPE_STR)) { if (auto spatialIndexConfig = std::dynamic_pointer_cast<config::SpatialIndexConfig>(indexConfig)) { // if (spatialIndexConfig->IsDeleted()) { // continue; // } auto fieldConfig = spatialIndexConfig->GetFieldConfig(); if (fieldConfig->GetFieldType() != FieldType::ft_location) { continue; } auto fieldName = fieldConfig->GetFieldName(); auto attributeConfig = std::dynamic_pointer_cast<index::AttributeConfig>( schema->GetIndexConfig(index::ATTRIBUTE_INDEX_TYPE_STR, fieldName)); if (!attributeConfig) { AUTIL_LOG(INFO, "add attribute [%s] to ensure spatial index precision", fieldName.c_str()); auto attributeConfig = std::make_shared<index::AttributeConfig>(); RETURN_IF_STATUS_ERROR(attributeConfig->Init(fieldConfig), "attribute [%s] config init failed", fieldName.c_str()); attributeConfig->SetFileCompressConfigV2(spatialIndexConfig->GetFileCompressConfigV2()); attributeConfig->SetAttrId(schema->GetIndexConfigs(attributeConfig->GetIndexType()).size()); RETURN_IF_STATUS_ERROR(schema->AddIndexConfig(attributeConfig), "add spatial attribute [%s] failed", fieldName.c_str()); } } } return Status::OK(); } Status NormalSchemaResolver::ResolveTTLField(config::UnresolvedSchema* schema) { auto [status, enableTTL] = schema->GetRuntimeSettings().GetValue<bool>("enable_ttl"); auto ttlAttrConfig = std::make_shared<index::AttributeConfig>(); if (status.IsOK()) { if (!enableTTL) { return Status::OK(); } // ttl field resolve auto [status, ttlFieldName] = schema->GetRuntimeSettings().GetValue<std::string>("ttl_field_name"); if (status.IsOK()) { auto attributeConfig = std::dynamic_pointer_cast<index::AttributeConfig>( schema->GetIndexConfig(index::ATTRIBUTE_INDEX_TYPE_STR, ttlFieldName)); if (!attributeConfig) { RETURN_STATUS_ERROR(ConfigError, "ttl field name [%s] is not found in attributes", ttlFieldName.c_str()); } if (attributeConfig->GetFieldType() != FieldType::ft_uint32 || attributeConfig->IsMultiValue()) { RETURN_STATUS_ERROR(ConfigError, "ttl field name [%s] in attribute is invalid, should be ft_uint32 and single value", ttlFieldName.c_str()); } ttlAttrConfig = attributeConfig; } else { if (status.IsNotFound()) { auto fieldConfig = schema->GetFieldConfig(DOC_TIME_TO_LIVE_IN_SECONDS); if (fieldConfig) { RETURN_STATUS_ERROR(ConfigError, "ttl field [%s] is in field config but not in attribute config", DOC_TIME_TO_LIVE_IN_SECONDS.c_str()); } fieldConfig = std::make_shared<config::FieldConfig>(DOC_TIME_TO_LIVE_IN_SECONDS, ft_uint32, false); fieldConfig->SetVirtual(true); RETURN_IF_STATUS_ERROR(schema->AddFieldConfig(fieldConfig), "add field config [%s] failed", DOC_TIME_TO_LIVE_IN_SECONDS.c_str()); if (!schema->SetRuntimeSetting("ttl_field_name", DOC_TIME_TO_LIVE_IN_SECONDS, false)) { RETURN_STATUS_DIRECTLY_IF_ERROR(Status::InternalError("")); } auto attributeConfig = std::make_shared<index::AttributeConfig>(); RETURN_IF_STATUS_ERROR(attributeConfig->Init(fieldConfig), "attribute config init failed"); attributeConfig->SetAttrId(schema->GetIndexConfigs(attributeConfig->GetIndexType()).size()); RETURN_IF_STATUS_ERROR(schema->AddIndexConfig(attributeConfig), "add attribute config [%s] failed", DOC_TIME_TO_LIVE_IN_SECONDS.c_str()); ttlAttrConfig = attributeConfig; } else { RETURN_IF_STATUS_ERROR(status, "get invaild ttl field name"); } } AUTIL_LOG(INFO, "disable update for field [%s] due to ttl field", ttlFieldName.c_str()); ttlAttrConfig->SetUpdatable(false); // default ttl auto [status1, defaultTTL] = schema->GetRuntimeSettings().GetValue<int64_t>("default_ttl"); if (status1.IsNotFound()) { bool ret = schema->SetRuntimeSetting("default_ttl", indexlib::DEFAULT_TIME_TO_LIVE, false); if (!ret) { RETURN_STATUS_DIRECTLY_IF_ERROR(Status::InternalError("")); } } else { if (status1.IsOK() && defaultTTL < 0) { RETURN_STATUS_ERROR(ConfigError, "default ttl [%d] is invalid, should not be negative", defaultTTL); } RETURN_IF_STATUS_ERROR(status1, "default ttl set is invalid [%ld]", defaultTTL); } } else if (!status.IsNotFound()) { return Status::ConfigError("get enable_ttl failed"); } return Status::OK(); } Status NormalSchemaResolver::ResolveSortDescriptions(config::UnresolvedSchema* schema, const config::TabletOptions* options, const std::string& indexPath) { auto [isExist, _] = schema->GetRuntimeSetting("sort_descriptions"); // legacy read old partitionMeta to set sortDesc to schema // options maybe nullptr when read a resolved schema in index path (offline case) // new sort description is store to schema if (!isExist) { indexlib::legacy::index_base::PartitionMeta partMeta; try { if (indexlib::legacy::index_base::PartitionMeta::IsExist(indexPath)) { partMeta.Load(indexPath); if (partMeta.Size() > 0) { schema->SetRuntimeSetting("sort_descriptions", autil::legacy::ToJson(partMeta.GetSortDescriptions()), /*overwrite*/ false); AUTIL_LOG(INFO, "set schema sort_descriptions [%s] from partionMeta", autil::legacy::ToJsonString(partMeta.GetSortDescriptions(), true).c_str()); } } else if (options && options->GetBuildOptionConfig().IsSortBuild()) { const auto& sortDescriptions = options->GetBuildOptionConfig().GetSortDescriptions(); if (sortDescriptions.empty()) { RETURN_STATUS_ERROR(ConfigError, "sort build desc cannot be empty when sort_build set true"); } schema->SetRuntimeSetting("sort_descriptions", autil::legacy::ToJson(sortDescriptions), /*overwrite*/ false); AUTIL_LOG(INFO, "set schema sort_descriptions [%s] from build option config", autil::legacy::ToJsonString(sortDescriptions, true).c_str()); } } catch (const autil::legacy::ExceptionBase& e) { AUTIL_LOG(ERROR, "resolve schema failed with exception [%s]", e.what()); return Status::IOError(e.what()); } } auto [status, sortDescs] = schema->GetRuntimeSettings().GetValue<config::SortDescriptions>("sort_descriptions"); if (status.IsNotFound()) { return Status::OK(); } RETURN_STATUS_DIRECTLY_IF_ERROR(status); if (!sortDescs.empty()) { DisableTFBitmap(schema); } return CheckSortDescs(schema); } void NormalSchemaResolver::DisableTFBitmap(config::UnresolvedSchema* schema) { auto indexConfigs = schema->GetIndexConfigs(indexlib::index::INVERTED_INDEX_TYPE_STR); for (auto indexConfig : indexConfigs) { auto invertedConfig = std::dynamic_pointer_cast<config::InvertedIndexConfig>(indexConfig); assert(invertedConfig); auto optionFlag = invertedConfig->GetOptionFlag(); if (optionFlag & of_tf_bitmap) { AUTIL_LOG(WARN, "tf bitmap disabled, not support sort dump"); optionFlag &= (~of_tf_bitmap); assert(!(optionFlag & of_tf_bitmap)); invertedConfig->SetOptionFlag(optionFlag); } } } Status NormalSchemaResolver::CheckSortDescs(const config::UnresolvedSchema* schema) { auto [status, sortDescs] = schema->GetRuntimeSettings().GetValue<config::SortDescriptions>("sort_descriptions"); if (status.IsNotFound()) { return Status::OK(); } RETURN_STATUS_DIRECTLY_IF_ERROR(status); if (sortDescs.empty()) { return Status::OK(); } for (const auto& desc : sortDescs) { const std::string& fieldName = desc.GetSortFieldName(); auto indexConfig = schema->GetIndexConfig(index::ATTRIBUTE_INDEX_TYPE_STR, fieldName); if (!indexConfig) { RETURN_STATUS_ERROR(ConfigError, "field [%s] is not attribute field, cannot be sort field", fieldName.c_str()); } auto attributeConfig = std::dynamic_pointer_cast<indexlibv2::index::AttributeConfig>(indexConfig); if (!attributeConfig) { RETURN_STATUS_ERROR(ConfigError, "field [%s] is not attribute field, cannot be sort field", fieldName.c_str()); } auto fieldConfig = attributeConfig->GetFieldConfig(); if (!fieldConfig || !fieldConfig->SupportSort()) { RETURN_STATUS_ERROR(ConfigError, "field [%s] is not illegal sort field", fieldName.c_str()); } indexlib::config::CompressTypeOption compressType = attributeConfig->GetCompressType(); if (compressType.HasFp16EncodeCompress() || compressType.HasInt8EncodeCompress()) { RETURN_STATUS_ERROR(ConfigError, "sort field [%s] not support fp16 or int8 compress", fieldName.c_str()); } AUTIL_LOG(INFO, "disable update for field [%s] due to sort field", fieldName.c_str()); attributeConfig->SetUpdatable(false); } return Status::OK(); } Status NormalSchemaResolver::LoadAdaptiveVocabulary(indexlib::config::IndexPartitionSchema* legacySchema, const std::string& indexPath) { if (indexPath.empty()) { return Status::OK(); } indexlib::file_system::FileSystemOptions options; options.loadConfigList.PushBack(indexlib::file_system::LoadConfigList::MakeMmapLoadConfig( {".*"}, /*warmup*/ false, /*isLock*/ true, /*adviseRandom*/ false, /*lockSlice*/ 4 * 1024 * 1024, /*lockInterval*/ 0)); auto [st, fileSystem] = indexlib::file_system::FileSystemCreator::CreateForRead("__TMP__adaptive_bitmap", indexPath, options) .StatusWith(); if (!st.IsOK()) { AUTIL_LOG(ERROR, "load adaptive vocabulary failed to create fs [%s]", st.ToString().c_str()); return st; } auto rootDir = indexlib::file_system::Directory::Get(fileSystem); try { indexlib::legacy::index_base::SchemaAdapter::LoadAdaptiveBitmapTerms(rootDir, legacySchema); } catch (const autil::legacy::ExceptionBase& e) { AUTIL_LOG(ERROR, "resolve schema failed. load adaptive vocabulary with exception [%s]", e.what()); return Status::IOError(e.what()); } return Status::OK(); } Status NormalSchemaResolver::LoadAdaptiveVocabulary(config::UnresolvedSchema* schema, const std::string& indexPath) { if (indexPath.empty()) { return Status::OK(); } indexlib::file_system::FileSystemOptions options; options.loadConfigList.PushBack(indexlib::file_system::LoadConfigList::MakeMmapLoadConfig( {".*"}, /*warmup*/ false, /*isLock*/ true, /*adviseRandom*/ false, /*lockSlice*/ 4 * 1024 * 1024, /*lockInterval*/ 0)); auto [st, fileSystem] = indexlib::file_system::FileSystemCreator::CreateForRead("__TMP__adaptive_bitmap", indexPath, options) .StatusWith(); if (!st.IsOK()) { AUTIL_LOG(ERROR, "load adaptive vocabulary failed to create fs [%s]", st.ToString().c_str()); return st; } auto rootDir = indexlib::file_system::Directory::Get(fileSystem)->GetIDirectory(); auto physicalRootDir = indexlib::file_system::IDirectory::GetPhysicalDirectory(indexPath); auto fsResult = physicalRootDir->GetDirectory(ADAPTIVE_DICT_DIR_NAME); if (fsResult.Code() != indexlib::file_system::ErrorCode::FSEC_OK) { if (fsResult.Code() == indexlib::file_system::ErrorCode::FSEC_NOENT) { return Status::OK(); } AUTIL_LOG(ERROR, "get directory [%s] failed, [%s]", ADAPTIVE_DICT_DIR_NAME, indexlib::file_system::toStatus(fsResult.Code()).ToString().c_str()); return indexlib::file_system::toStatus(fsResult.Code()); } RETURN_IF_STATUS_ERROR(rootDir->GetFileSystem() ->MountDir(/*physicalRoot=*/rootDir->GetPhysicalPath(""), /*physicalPath=*/ADAPTIVE_DICT_DIR_NAME, /*logicalPath=*/ADAPTIVE_DICT_DIR_NAME, /*MountDirOption=*/indexlib::file_system::FSMT_READ_WRITE, /*enableLazyMount=*/false) .Status(), "mount adaptive bitmap meta dir failed"); return LoadAdaptiveBitmapTerms(rootDir, schema); } Status NormalSchemaResolver::AddBuiltinIndex(config::UnresolvedSchema* schema) { const std::string& tableType = schema->GetTableType(); if (tableType != indexlib::table::TABLE_TYPE_NORMAL) { AUTIL_LOG(INFO, "no need add builtin index for table type [%s]", tableType.c_str()); return Status::OK(); } auto pkConfigs = schema->GetIndexConfigs(index::PRIMARY_KEY_INDEX_TYPE_STR); if (pkConfigs.size() > 0) { assert(1 == pkConfigs.size()); auto pkConfig = std::dynamic_pointer_cast<indexlibv2::index::PrimaryKeyIndexConfig>(pkConfigs[0]); assert(pkConfig); auto deletionMapConfig = std::dynamic_pointer_cast<indexlibv2::index::DeletionMapConfig>( schema->GetIndexConfig(index::DELETION_MAP_INDEX_TYPE_STR, index::DELETION_MAP_INDEX_NAME)); if (!deletionMapConfig) { deletionMapConfig = std::make_shared<index::DeletionMapConfig>(); RETURN_IF_STATUS_ERROR(schema->AddIndexConfig(deletionMapConfig), "add deletion map config failed"); } if (!schema->GetIndexConfig(indexlib::index::OPERATION_LOG_INDEX_TYPE_STR, indexlib::index::OPERATION_LOG_INDEX_NAME)) { auto operationLogConfig = std::make_shared<indexlib::index::OperationLogConfig>( indexlib::index::DEFAULT_MAX_OPERATION_BLOCK_SIZE, true); operationLogConfig->AddIndexConfigs(index::PRIMARY_KEY_INDEX_TYPE_STR, {pkConfig}); operationLogConfig->AddIndexConfigs(index::ATTRIBUTE_INDEX_TYPE_STR, schema->GetIndexConfigs(index::ATTRIBUTE_INDEX_TYPE_STR)); // TODO: add inverted index config to oplogconfig const auto& fields = schema->GetFieldConfigs(); operationLogConfig->SetFieldConfigs(fields); RETURN_IF_STATUS_ERROR(schema->AddIndexConfig(operationLogConfig), "add operation log config failed"); } } auto addBuiltinAttribute = [schema](const std::string fieldName, FieldType fieldType) -> Status { if (schema->GetIndexConfig(VIRTUAL_ATTRIBUTE_INDEX_TYPE_STR, fieldName)) { return Status::OK(); } auto fieldConfig = schema->GetFieldConfig(fieldName); if (fieldConfig && fieldConfig->GetFieldType() != fieldType) { AUTIL_LOG(ERROR, "field [%s] already in schema, but type not as expected", fieldName.c_str()); return Status::InvalidArgs("field schema invalid"); } if (!fieldConfig) { fieldConfig = std::make_shared<config::FieldConfig>(fieldName, fieldType, false); fieldConfig->SetVirtual(true); RETURN_STATUS_DIRECTLY_IF_ERROR(schema->AddFieldConfig(fieldConfig)); } assert(fieldConfig); auto attrConfig = std::make_shared<indexlibv2::index::AttributeConfig>(); RETURN_IF_STATUS_ERROR(attrConfig->Init(fieldConfig), "attribute config [%s] init failed", fieldName.c_str()); std::shared_ptr<config::IIndexConfig> newConfig(new VirtualAttributeConfig(attrConfig)); RETURN_IF_STATUS_ERROR(schema->AddIndexConfig(newConfig), "add builtin attribute [%s] failed", fieldName.c_str()); return Status::OK(); }; RETURN_IF_STATUS_ERROR(addBuiltinAttribute(document::DocumentInfoToAttributeRewriter::VIRTUAL_TIMESTAMP_FIELD_NAME, document::DocumentInfoToAttributeRewriter::VIRTUAL_TIMESTAMP_FIELD_TYPE), "add virtual timestamp field failed"); RETURN_IF_STATUS_ERROR(addBuiltinAttribute(document::DocumentInfoToAttributeRewriter::VIRTUAL_DOC_INFO_FIELD_NAME, document::DocumentInfoToAttributeRewriter::VIRTUAL_DOC_INFO_FIELD_TYPE), "add virtual hashid field failed"); return Status::OK(); } std::pair<Status, std::shared_ptr<config::InvertedIndexConfig>> NormalSchemaResolver::CreateInvertedIndexConfig(const std::shared_ptr<indexlib::config::IndexConfig>& indexConfig) { auto configV2 = indexConfig->ConstructConfigV2(); if (!configV2) { AUTIL_LOG(WARN, "construct index config[%s] from legacy schema failed", indexConfig->GetIndexName().c_str()); return {Status::OK(), indexConfig}; } auto invertedIndexConfig = dynamic_cast<config::InvertedIndexConfig*>(configV2.release()); assert(invertedIndexConfig); return {Status::OK(), std::shared_ptr<config::InvertedIndexConfig>(invertedIndexConfig)}; } Status NormalSchemaResolver::ResolveGeneralValue(config::UnresolvedSchema* schema, config::TabletOptions* options) { std::set<std::string> disableAttributes; if (auto status = options->GetFromRawJson("%index_config%.disable_fields.attributes", &disableAttributes); !status.IsOKOrNotFound()) { AUTIL_LOG(ERROR, "get %%index_config%%.disable_fields.attributes failed"); return status; } attrid_t attrId = 0; const auto& attrIndexConfigs = schema->GetIndexConfigs(index::ATTRIBUTE_INDEX_TYPE_STR); for (const auto& indexConfig : attrIndexConfigs) { const auto& attrConfig = std::dynamic_pointer_cast<index::AttributeConfig>(indexConfig); assert(attrConfig); assert(attrConfig->GetAttrId() != INVALID_ATTRID); if (disableAttributes.count(attrConfig->GetIndexName()) > 0) { attrConfig->Disable(); } if (!schema->GetIndexConfig(indexlib::index::GENERAL_VALUE_INDEX_TYPE_STR, attrConfig->GetIndexName())) { RETURN_IF_STATUS_ERROR(schema->AddIndexConfig(indexlib::index::GENERAL_VALUE_INDEX_TYPE_STR, attrConfig), "add attribute [%s] into general_value failed", attrConfig->GetIndexName().c_str()); } attrId = std::max(attrConfig->GetAttrId() + 1, attrId); } std::set<std::string> disablePackAttributes; if (auto status = options->GetFromRawJson("%index_config%.disable_fields.pack_attributes", &disablePackAttributes); !status.IsOKOrNotFound()) { AUTIL_LOG(ERROR, "get %%index_config%%.disable_fields.pack_attributes failed"); return status; } const auto& packAttrIndexConfigs = schema->GetIndexConfigs(index::PACK_ATTRIBUTE_INDEX_TYPE_STR); for (const auto& indexConfig : packAttrIndexConfigs) { if (schema->GetIndexConfig(index::ATTRIBUTE_INDEX_TYPE_STR, indexConfig->GetIndexName())) { auto status = Status::ConfigError("pack attribute name [%s] is duplicate with normal attribute", indexConfig->GetIndexName().c_str()); AUTIL_LOG(ERROR, "%s", status.ToString().c_str()); return status; } const auto& packAttrConfig = std::dynamic_pointer_cast<index::PackAttributeConfig>(indexConfig); assert(packAttrConfig); if (disablePackAttributes.count(packAttrConfig->GetIndexName()) > 0) { packAttrConfig->Disable(); } const auto& attrConfigs = packAttrConfig->GetAttributeConfigVec(); for (const auto& attrConfig : attrConfigs) { if (!schema->GetIndexConfig(indexlib::index::GENERAL_VALUE_INDEX_TYPE_STR, attrConfig->GetIndexName())) { attrConfig->SetAttrId(attrId++); RETURN_IF_STATUS_ERROR( schema->AddIndexConfig(indexlib::index::GENERAL_VALUE_INDEX_TYPE_STR, attrConfig), "add sub attribute [%s] in pack attribute [%s] into general_value failed", attrConfig->GetIndexName().c_str(), packAttrConfig->GetIndexName().c_str()); } } } return Status::OK(); } Status NormalSchemaResolver::ResolveGeneralInvertedIndex(config::UnresolvedSchema* schema, config::TabletOptions* options) { std::set<std::string> disableIndexes; if (auto status = options->GetFromRawJson("%index_config%.disable_fields.indexes", &disableIndexes); !status.IsOKOrNotFound()) { AUTIL_LOG(ERROR, "get %%index_config%%.disable_fields.indexes failed"); return status; } auto invertedIndexConfigs = schema->GetIndexConfigs(indexlib::index::INVERTED_INDEX_TYPE_STR); for (const auto& indexConfig : invertedIndexConfigs) { if (disableIndexes.count(indexConfig->GetIndexName())) { const auto& invertedIndexConfig = std::dynamic_pointer_cast<indexlibv2::config::InvertedIndexConfig>(indexConfig); assert(invertedIndexConfig); invertedIndexConfig->Disable(); } if (!schema->GetIndexConfig(indexlib::index::GENERAL_INVERTED_INDEX_TYPE_STR, indexConfig->GetIndexName())) { RETURN_IF_STATUS_ERROR( schema->AddIndexConfig(indexlib::index::GENERAL_INVERTED_INDEX_TYPE_STR, indexConfig), "add inverted_index [%s] into general_inverted_index failed", indexConfig->GetIndexName().c_str()); } } auto pkIndexConfig = schema->GetPrimaryKeyIndexConfig(); if (pkIndexConfig && !schema->GetIndexConfig(indexlib::index::GENERAL_INVERTED_INDEX_TYPE_STR, pkIndexConfig->GetIndexName())) { RETURN_IF_STATUS_ERROR(schema->AddIndexConfig(indexlib::index::GENERAL_INVERTED_INDEX_TYPE_STR, pkIndexConfig), "add primary key into general_inverted_index failed"); } auto annIndexConfigs = schema->GetIndexConfigs(indexlibv2::index::ANN_INDEX_TYPE_STR); for (const auto& indexConfig : annIndexConfigs) { if (disableIndexes.count(indexConfig->GetIndexName())) { const auto& invertedIndexConfig = std::dynamic_pointer_cast<indexlibv2::config::InvertedIndexConfig>(indexConfig); assert(invertedIndexConfig); invertedIndexConfig->Disable(); } if (!schema->GetIndexConfig(indexlib::index::GENERAL_INVERTED_INDEX_TYPE_STR, indexConfig->GetIndexName())) { RETURN_IF_STATUS_ERROR( schema->AddIndexConfig(indexlib::index::GENERAL_INVERTED_INDEX_TYPE_STR, indexConfig), "add ann [%s] into general_inverted_index failed", indexConfig->GetIndexName().c_str()); } } return Status::OK(); } Status NormalSchemaResolver::LoadAdaptiveBitmapTerms(const std::shared_ptr<indexlib::file_system::IDirectory>& rootDir, config::UnresolvedSchema* schema) { std::vector<std::shared_ptr<config::InvertedIndexConfig>> adaptiveDictIndexConfigs; const auto& configs = schema->GetIndexConfigs(indexlib::index::INVERTED_INDEX_TYPE_STR); for (const auto& config : configs) { const auto& invertedConfig = std::dynamic_pointer_cast<config::InvertedIndexConfig>(config); assert(invertedConfig); if (invertedConfig->GetShardingIndexConfigs().size() > 0) { // if is sharding index, don't read HFV for original index name for (const auto& shardingIndexConfig : invertedConfig->GetShardingIndexConfigs()) { if (shardingIndexConfig->GetAdaptiveDictionaryConfig()) { adaptiveDictIndexConfigs.push_back(shardingIndexConfig); } } continue; } if (invertedConfig->GetAdaptiveDictionaryConfig()) { adaptiveDictIndexConfigs.push_back(invertedConfig); } } if (adaptiveDictIndexConfigs.empty()) { return Status::OK(); } auto fsResult = rootDir->GetDirectory(ADAPTIVE_DICT_DIR_NAME); if (fsResult.Code() == indexlib::file_system::FSEC_NOENT) { return Status::OK(); } auto status = indexlib::file_system::toStatus(fsResult.Code()); RETURN_IF_STATUS_ERROR(status, "get directory [%s] failed, [%s]", ADAPTIVE_DICT_DIR_NAME, status.ToString().c_str()); auto directory = fsResult.Value(); if (directory == nullptr) { AUTIL_LOG(ERROR, "get directory [%s] failed, directory is nullptr", ADAPTIVE_DICT_DIR_NAME); return Status::OK(); } return doLoadAdaptiveBitmap(directory, adaptiveDictIndexConfigs); } Status NormalSchemaResolver::doLoadAdaptiveBitmap( const std::shared_ptr<indexlib::file_system::IDirectory>& adaptiveBitmapDir, const std::vector<std::shared_ptr<config::InvertedIndexConfig>>& adaptiveDictIndexConfigs) { // adaptiveBitmapDir might be in archive file or package format. const std::string PACKAGE_META_FILE_NAME = std::string(indexlib::file_system::PACKAGE_FILE_PREFIX) + std::string(indexlib::file_system::PACKAGE_FILE_META_SUFFIX); auto [st, isExist] = adaptiveBitmapDir->IsExist(PACKAGE_META_FILE_NAME).StatusWith(); if (!st.IsOK()) { AUTIL_LOG(INFO, "check package meta file failed, [%s]", st.ToString().c_str()); return st; } if (isExist) { for (size_t i = 0; i < adaptiveDictIndexConfigs.size(); i++) { auto indexConfig = adaptiveDictIndexConfigs[i]; auto [status, vol] = indexlib::config::HighFreqVocabularyCreator::LoadAdaptiveVocabulary( adaptiveBitmapDir, indexConfig->GetIndexName(), indexConfig->GetInvertedIndexType(), indexConfig->GetNullTermLiteralString(), indexConfig->GetDictConfig(), indexConfig->GetDictHashParams()) .StatusWith(); if (!status.IsOK()) { AUTIL_LOG(ERROR, "load adaptive vocabulary failed, [%s]", status.ToString().c_str()); return status; } if (vol != nullptr) { indexConfig->SetHighFreqVocabulary(vol); } } return Status::OK(); } auto adaptiveDictFolder = adaptiveBitmapDir->LEGACY_CreateArchiveFolder(false, ""); if (adaptiveDictFolder == nullptr) { AUTIL_LOG(ERROR, "create archiveFolder failed, dir[%s]", adaptiveBitmapDir->GetLogicalPath().c_str()); return Status::IOError("create archive folder failed, dir[%s]", adaptiveBitmapDir->GetLogicalPath().c_str()); } for (size_t i = 0; i < adaptiveDictIndexConfigs.size(); i++) { auto indexConfig = adaptiveDictIndexConfigs[i]; auto [status, vol] = indexlib::config::HighFreqVocabularyCreator::LoadAdaptiveVocabulary( adaptiveDictFolder, indexConfig->GetIndexName(), indexConfig->GetInvertedIndexType(), indexConfig->GetNullTermLiteralString(), indexConfig->GetDictConfig(), indexConfig->GetDictHashParams()) .StatusWith(); if (!status.IsOK()) { AUTIL_LOG(ERROR, "load adaptive vocabulary failed, [%s]", status.ToString().c_str()); return status; } if (vol != nullptr) { indexConfig->SetHighFreqVocabulary(vol); } } return adaptiveDictFolder->Close().Status(); } Status NormalSchemaResolver::FillIndexConfigs(const indexlib::config::IndexPartitionSchema& legacySchema, config::UnresolvedSchema* schema) { assert(schema); auto [status, singleFileCompressConfigVec] = schema->GetRuntimeSettings().GetValue<std::shared_ptr<std::vector<config::SingleFileCompressConfig>>>( config::SingleFileCompressConfig::FILE_COMPRESS_CONFIG_KEY); if (!status.IsOK() && !status.IsNotFound()) { RETURN_IF_STATUS_ERROR(status, "get file compress config from setting failed"); } if (singleFileCompressConfigVec) { RETURN_IF_STATUS_ERROR(config::SingleFileCompressConfig::ValidateConfigs(*singleFileCompressConfigVec), "file compress invalid"); } if (const auto& indexSchema = legacySchema.GetIndexSchema(); indexSchema != nullptr) { // INVERTED_INDEX_TYPE_STR auto pkConfig = indexSchema->GetPrimaryKeyIndexConfig(); for (auto it = indexSchema->Begin(); it != indexSchema->End(); it++) { auto indexConfig = *it; if (indexConfig == pkConfig) { continue; } if (indexSchema->IsDeleted(indexConfig->GetIndexId())) { continue; } auto shardingType = indexConfig->GetShardingType(); if (indexlibv2::config::InvertedIndexConfig::IndexShardingType::IST_IS_SHARDING == shardingType) { continue; } if (schema->GetIndexConfig(indexlib::index::INVERTED_INDEX_TYPE_STR, indexConfig->GetIndexName()) || schema->GetIndexConfig(indexlibv2::index::ANN_INDEX_TYPE_STR, indexConfig->GetIndexName())) { // already added // TODO: need to be continue, when support new schema continue; } auto [status, invertedIndexConfig] = CreateInvertedIndexConfig(indexConfig); RETURN_IF_STATUS_ERROR(status, "create inverted index config fail"); assert(invertedIndexConfig); RETURN_IF_STATUS_ERROR(ResolveFileCompressConfig(singleFileCompressConfigVec, invertedIndexConfig), "resolve inverted index compress config failed"); status = schema->AddIndexConfig(invertedIndexConfig); RETURN_IF_STATUS_ERROR(status, "add inverted index config fail, indexName[%s]", invertedIndexConfig->GetIndexName().c_str()); } // PRIMARY_KEY_INDEX_TYPE_STR if (schema->GetIndexConfigs(index::PRIMARY_KEY_INDEX_TYPE_STR).empty()) { if (indexSchema->HasPrimaryKeyIndex()) { if (!schema->GetIndexConfig(index::PRIMARY_KEY_INDEX_TYPE_STR, pkConfig->GetIndexName())) { // already added auto legacyConfig = std::dynamic_pointer_cast<indexlib::config::IndexConfig>(pkConfig); assert(legacyConfig); auto pkConfigV2 = legacyConfig->ConstructConfigV2(); if (!pkConfigV2) { RETURN_IF_STATUS_ERROR(Status::InternalError(), "make primary index config v2 failed"); } auto pkConfigShared = std::shared_ptr<indexlibv2::config::IIndexConfig>(std::move(pkConfigV2)); auto status = schema->AddIndexConfig(pkConfigShared); RETURN_IF_STATUS_ERROR(status, "add primary_key config fail, indexName[%s]", pkConfig->GetIndexName().c_str()); } } } } if (const auto& attrSchema = legacySchema.GetAttributeSchema(); attrSchema != nullptr) { // ATTRIBUTE_INDEX_TYPE_STR for (auto it = attrSchema->Begin(); it != attrSchema->End(); it++) { auto attrConfig = *it; if (schema->GetIndexConfig(index::ATTRIBUTE_INDEX_TYPE_STR, attrConfig->GetIndexName())) { // already added continue; } if (attrSchema->IsDeleted(attrConfig->GetAttrId())) { continue; } if (attrConfig->GetPackAttributeConfig()) { continue; } RETURN_IF_STATUS_ERROR(ResolveFileCompressConfig(singleFileCompressConfigVec, attrConfig), "resolve file compress config fail, indexName[%s]", attrConfig->GetIndexName().c_str()); auto status = schema->AddIndexConfig(attrConfig); RETURN_IF_STATUS_ERROR(status, "add attribute config fail, indexName[%s]", attrConfig->GetIndexName().c_str()); } // PACK_ATTRIBUTE_INDEX_TYPE_STR for (size_t i = 0; i < attrSchema->GetPackAttributeCount(); ++i) { const auto& packAttrConfig = attrSchema->GetPackAttributeConfig(i); if (schema->GetIndexConfig(index::PACK_ATTRIBUTE_INDEX_TYPE_STR, packAttrConfig->GetIndexName())) { // already added continue; } RETURN_IF_STATUS_ERROR(ResolveFileCompressConfig(singleFileCompressConfigVec, packAttrConfig), "resolve file compress config fail, indexName[%s]", packAttrConfig->GetIndexName().c_str()); auto status = schema->AddIndexConfig(packAttrConfig); RETURN_IF_STATUS_ERROR(status, "add pack attribute config fail, indexName[%s]", packAttrConfig->GetIndexName().c_str()); } } if (const auto& summarySchema = legacySchema.GetSummarySchema(); nullptr != summarySchema) { // SUMMARY_INDEX_TYPE_STR std::shared_ptr<indexlibv2::config::SummaryIndexConfig> summaryIndexConfig( summarySchema->MakeSummaryIndexConfigV2().release()); if (schema->GetIndexConfig(index::SUMMARY_INDEX_TYPE_STR, summaryIndexConfig->GetIndexName())) { // already added return Status::OK(); } RETURN_IF_STATUS_ERROR(ResolveFileCompressConfig(singleFileCompressConfigVec, summaryIndexConfig), "resolve summary compress config failed"); auto status = schema->AddIndexConfig(summaryIndexConfig); RETURN_IF_STATUS_ERROR(status, "add summary index config fail, indexName[%s]", summaryIndexConfig->GetIndexName().c_str()); } return Status::OK(); } void NormalSchemaResolver::ResolveEmptyProfileNamesForTruncateIndex( const std::vector<indexlibv2::config::TruncateProfileConfig>& profileConfigs, const std::vector<std::shared_ptr<config::IIndexConfig>>& indexConfigs) { std::vector<std::string> truncateProfileNames; for (const auto& profileConfig : profileConfigs) { if (profileConfig.GetPayloadConfig().IsInitialized()) { continue; } truncateProfileNames.push_back(profileConfig.GetTruncateProfileName()); } for (const auto& indexConfig : indexConfigs) { auto invertedIndexConfig = std::dynamic_pointer_cast<config::InvertedIndexConfig>(indexConfig); if (!invertedIndexConfig->HasTruncate()) { continue; } if (!invertedIndexConfig->GetUseTruncateProfilesStr().empty()) { continue; } invertedIndexConfig->SetUseTruncateProfiles(truncateProfileNames); } } Status NormalSchemaResolver::ResolveTruncateIndexConfigs(const std::string& indexPath, config::UnresolvedSchema* schema) { auto [isExist, _] = schema->GetRuntimeSetting("truncate_profiles"); if (!isExist) { AUTIL_LOG(DEBUG, "truncate profile not exist."); return Status::OK(); } auto st = LoadTruncateTermVocabulary(indexPath, schema); RETURN_IF_STATUS_ERROR(st, "load truncate term vocabulary failed."); auto [status, truncateProfileConfigs] = schema->GetRuntimeSettings().GetValue<std::vector<indexlibv2::config::TruncateProfileConfig>>( "truncate_profiles"); if (!status.IsOK() && !status.IsNotFound()) { AUTIL_LOG(ERROR, "get truncate profile config from setting failed."); return status; } auto indexConfigs = schema->GetIndexConfigs(indexlib::index::INVERTED_INDEX_TYPE_STR); ResolveEmptyProfileNamesForTruncateIndex(truncateProfileConfigs, indexConfigs); status = AppendTruncateIndexConfig(indexConfigs, truncateProfileConfigs); RETURN_IF_STATUS_ERROR(status, "append truncate index config failed"); for (auto& truncateProfileConfig : truncateProfileConfigs) { if (!IsValidTruncateSortParam(truncateProfileConfig.GetTruncateSortParams(), schema)) { AUTIL_LOG(ERROR, "invalid sort param for truncate, profileName[%s]", truncateProfileConfig.GetTruncateProfileName().c_str()); return Status::InvalidArgs("invalid sort param for truncate"); } } return Status::OK(); } Status NormalSchemaResolver::FillDictionaryToSettings(const indexlib::config::IndexPartitionSchema& legacySchema, config::UnresolvedSchema* schema) const { auto [isExist, _] = schema->GetRuntimeSetting("dictionaries"); if (isExist) { return Status::OK(); } auto dictSchema = legacySchema.GetDictSchema(); if (!dictSchema) { return Status::OK(); } try { auto any = autil::legacy::ToJson(dictSchema); auto jsonMap = autil::legacy::AnyCast<autil::legacy::json::JsonMap>(any); auto success = schema->SetRuntimeSetting("dictionaries", jsonMap["dictionaries"], /*overwrite*/ false); if (!success) { RETURN_IF_STATUS_ERROR(Status::InvalidArgs(), "set dictionaries to settings failed."); } } catch (const std::exception& e) { AUTIL_LOG(ERROR, "fill dictionary failed, exception[%s]", e.what()); return Status::InvalidArgs(); } return Status::OK(); } Status NormalSchemaResolver::FillAdaptiveDictionaryToSettings(const indexlib::config::IndexPartitionSchema& legacySchema, config::UnresolvedSchema* schema) const { auto [isExist, _] = schema->GetRuntimeSetting("adaptive_dictionaries"); if (isExist) { return Status::OK(); } auto adaptiveDictSchema = legacySchema.GetAdaptiveDictSchema(); if (!adaptiveDictSchema) { return Status::OK(); } try { auto any = autil::legacy::ToJson(adaptiveDictSchema); auto jsonMap = autil::legacy::AnyCast<autil::legacy::json::JsonMap>(any); auto success = schema->SetRuntimeSetting("adaptive_dictionaries", jsonMap["adaptive_dictionaries"], /*overwrite*/ false); if (!success) { RETURN_IF_STATUS_ERROR(Status::InvalidArgs(), "set adaptive_dictionaries to settings failed."); } } catch (const std::exception& e) { AUTIL_LOG(ERROR, "fill adaptive dictionary failed, exception[%s]", e.what()); return Status::InvalidArgs(); } return Status::OK(); } Status NormalSchemaResolver::FillTruncateProfile(const indexlib::config::IndexPartitionSchema& legacySchema, config::UnresolvedSchema* schema) const { auto [isExist, _] = schema->GetRuntimeSetting("truncate_profiles"); if (!isExist) { std::vector<indexlibv2::config::TruncateProfileConfig> truncateProfileConfigs; auto truncateSchema = legacySchema.GetTruncateProfileSchema(); if (truncateSchema == nullptr) { AUTIL_LOG(DEBUG, "truncate profile schema not exist."); return Status::OK(); } for (auto it = truncateSchema->Begin(); it != truncateSchema->End(); ++it) { std::string content; auto st = indexlib::file_system::JsonUtil::ToString(*(it->second), &content).Status(); if (!st.IsOK()) { AUTIL_LOG(ERROR, "serialize truncate profile config failed."); return st; } indexlibv2::config::TruncateProfileConfig truncateProfileConfig; st = indexlib::file_system::JsonUtil::FromString(content, &truncateProfileConfig).Status(); if (!st.IsOK()) { AUTIL_LOG(ERROR, "deserialize truncate profile config failed."); return st; } truncateProfileConfigs.emplace_back(std::move(truncateProfileConfig)); } auto success = schema->SetRuntimeSetting("truncate_profiles", autil::legacy::ToJson(truncateProfileConfigs), /*overwrite*/ false); if (!success) { AUTIL_LOG(ERROR, "set truncate profile config to schema failed."); return Status::InvalidArgs("set truncate profile config to schema failed"); } } return Status::OK(); } Status NormalSchemaResolver::LoadTruncateTermVocabulary(const std::string& indexPath, config::UnresolvedSchema* schema) const { assert(schema->GetTableType() == indexlib::table::TABLE_TYPE_NORMAL); // if option is online & need not rebuild truncate index (for FilteredMultiPartitionMerger) indexlib::file_system::FileSystemOptions options; options.loadConfigList.PushBack(indexlib::file_system::LoadConfigList::MakeMmapLoadConfig( {".*"}, /*warmup*/ false, /*isLock*/ true, /*adviseRandom*/ false, /*lockSlice*/ 4 * 1024 * 1024, /*lockInterval*/ 0)); auto [st1, fileSystem] = indexlib::file_system::FileSystemCreator::CreateForRead("__TMP__truncate", indexPath, options).StatusWith(); if (!st1.IsOK()) { AUTIL_LOG(ERROR, "load truncate term failed to create fs [%s]", st1.ToString().c_str()); return st1; } auto rootDir = indexlib::file_system::Directory::Get(fileSystem)->GetIDirectory(); if (rootDir == nullptr) { AUTIL_LOG(ERROR, "load truncate term failed to get root dir"); return Status::InternalError("load truncate term failed to get root dir"); } auto physicalRootDir = indexlib::file_system::IDirectory::GetPhysicalDirectory(indexPath); auto fsResult = physicalRootDir->GetDirectory(TRUNCATE_META_DIR_NAME); if (fsResult.Code() != indexlib::file_system::ErrorCode::FSEC_OK) { if (fsResult.Code() == indexlib::file_system::ErrorCode::FSEC_NOENT) { return Status::OK(); } AUTIL_LOG(ERROR, "get directory [%s] failed, [%s]", TRUNCATE_META_DIR_NAME, indexlib::file_system::toStatus(fsResult.Code()).ToString().c_str()); return indexlib::file_system::toStatus(fsResult.Code()); } RETURN_IF_STATUS_ERROR(rootDir->GetFileSystem() ->MountDir(/*physicalRoot=*/rootDir->GetPhysicalPath(""), /*physicalPath=*/TRUNCATE_META_DIR_NAME, /*logicalPath=*/TRUNCATE_META_DIR_NAME, /*MountDirOption=*/indexlib::file_system::FSMT_READ_WRITE, /*enableLazyMount=*/false) .Status(), "mount truncate meta dir failed"); auto pair = rootDir->GetDirectory(std::string(TRUNCATE_META_DIR_NAME)); indexlib::file_system::ErrorCode ec = pair.Code(); std::shared_ptr<indexlib::file_system::IDirectory> truncateMetaDir = pair.Value(); if (ec != indexlib::file_system::ErrorCode::FSEC_OK) { AUTIL_LOG(ERROR, "get truncate meta dir failed, [%s]", indexlib::file_system::toStatus(ec).ToString().c_str()); return indexlib::file_system::toStatus(ec); } indexlibv2::config::TruncateIndexNameMapper truncMapper(truncateMetaDir); auto st = truncMapper.Load(); RETURN_IF_STATUS_ERROR(st, "load truncate name mapper failed."); // truncate meta dir might be in archive file or package format. const std::string PACKAGE_META_FILE_NAME = std::string(indexlib::file_system::PACKAGE_FILE_PREFIX) + std::string(indexlib::file_system::PACKAGE_FILE_META_SUFFIX); auto [st2, isExist] = truncateMetaDir->IsExist(PACKAGE_META_FILE_NAME).StatusWith(); if (!st2.IsOK()) { AUTIL_LOG(INFO, "check package meta file failed, [%s]", st2.ToString().c_str()); return st2; } std::shared_ptr<indexlib::file_system::ArchiveFolder> archiveFolder = nullptr; if (!isExist) { archiveFolder = truncateMetaDir->LEGACY_CreateArchiveFolder(false, ""); if (archiveFolder == nullptr) { AUTIL_LOG(ERROR, "create archiveFolder failed, dir[%s]", truncateMetaDir->GetLogicalPath().c_str()); return Status::InternalError("create archiveFolder failed."); } } auto loadTruncaTermVocabulary = [&truncMapper, &truncateMetaDir, &archiveFolder](const std::shared_ptr<config::InvertedIndexConfig>& invertedIndexConfig) -> Status { const std::string& indexName = invertedIndexConfig->GetIndexName(); std::vector<std::string> truncNames; if (truncMapper.Lookup(indexName, truncNames)) { Status st; if (archiveFolder != nullptr) { st = invertedIndexConfig->LoadTruncateTermVocabulary(archiveFolder, truncNames); } else { st = invertedIndexConfig->LoadTruncateTermVocabulary(truncateMetaDir, truncNames); } RETURN_IF_STATUS_ERROR(st, "load truncate term vocabulary failed, index[%s].", indexName.c_str()); } return Status::OK(); }; AUTIL_LOG(INFO, "begin load truncate term info"); auto indexConfigs = schema->GetIndexConfigs(indexlib::index::INVERTED_INDEX_TYPE_STR); for (auto indexConfig : indexConfigs) { auto invertedIndexConfig = std::dynamic_pointer_cast<config::InvertedIndexConfig>(indexConfig); assert(invertedIndexConfig != nullptr); if (invertedIndexConfig->GetShardingType() == config::InvertedIndexConfig::IndexShardingType::IST_NEED_SHARDING) { for (const auto& shardConfig : invertedIndexConfig->GetShardingIndexConfigs()) { auto shardInvertedIndexConfig = std::dynamic_pointer_cast<config::InvertedIndexConfig>(shardConfig); assert(shardInvertedIndexConfig != nullptr); RETURN_IF_STATUS_ERROR(loadTruncaTermVocabulary(shardInvertedIndexConfig), "load truncate term volcabulary failed, indexName[%s]", shardInvertedIndexConfig->GetIndexName().c_str()); } } else { assert(invertedIndexConfig->GetShardingType() == config::InvertedIndexConfig::IndexShardingType::IST_NO_SHARDING); RETURN_IF_STATUS_ERROR(loadTruncaTermVocabulary(invertedIndexConfig), "load truncate term volcabulary failed, indexName[%s]", invertedIndexConfig->GetIndexName().c_str()); } } AUTIL_LOG(INFO, "end load truncate term info"); if (archiveFolder != nullptr) { st = archiveFolder->Close().Status(); if (!st.IsOK()) { AUTIL_LOG(ERROR, "close archive folder failed."); return st; } } return Status::OK(); } Status NormalSchemaResolver::AppendTruncateIndexConfig( const std::vector<std::shared_ptr<config::IIndexConfig>>& indexConfigs, const std::vector<indexlibv2::config::TruncateProfileConfig>& truncateProfileConfigs) { auto appendTruncateIndexConfig = [&truncateProfileConfigs, this](const std::shared_ptr<config::InvertedIndexConfig>& invertedConfig) -> Status { for (const auto& truncateProfileConfig : truncateProfileConfigs) { const auto truncateProfileName = truncateProfileConfig.GetTruncateProfileName(); if (invertedConfig->HasTruncateProfile(&truncateProfileConfig)) { auto truncateIndexConfig = CreateTruncateIndexConfig(invertedConfig, truncateProfileConfig); invertedConfig->AppendTruncateIndexConfig(truncateIndexConfig); auto st = UpdateIndexConfigForTruncate(invertedConfig, truncateIndexConfig); RETURN_IF_STATUS_ERROR(st, "update index config[%s] for truncate profile[%s] failed", invertedConfig->GetIndexName().c_str(), truncateProfileName.c_str()); } } return Status::OK(); }; for (const auto& indexConfig : indexConfigs) { auto invertedIndexConfig = std::dynamic_pointer_cast<config::InvertedIndexConfig>(indexConfig); assert(invertedIndexConfig != nullptr); if (!invertedIndexConfig->HasTruncate()) { continue; } if (invertedIndexConfig->GetShardingType() == config::InvertedIndexConfig::IST_NEED_SHARDING) { for (const auto& shardInvertedConfig : invertedIndexConfig->GetShardingIndexConfigs()) { RETURN_IF_STATUS_ERROR(appendTruncateIndexConfig(shardInvertedConfig), "append truncate index failed"); } } else { RETURN_IF_STATUS_ERROR(appendTruncateIndexConfig(invertedIndexConfig), "append truncate index failed"); } } return Status::OK(); } // IndexConfig needs to be modified if it has TruncateIndexConfig that uses payload. This is a compatibility change // for IndexConfig to support payload. Future design of IndexConfig should support paylaod natively. Status NormalSchemaResolver::UpdateIndexConfigForTruncate( const std::shared_ptr<config::InvertedIndexConfig>& indexConfig, const std::shared_ptr<config::InvertedIndexConfig>& truncateIndexConfig) { const std::string& existingPayloadName = indexConfig->GetTruncatePayloadConfig().GetName(); const std::string& incomingPayloadName = truncateIndexConfig->GetTruncatePayloadConfig().GetName(); if (!indexConfig->GetTruncatePayloadConfig().IsInitialized()) { indexConfig->SetTruncatePayloadConfig(truncateIndexConfig->GetTruncatePayloadConfig()); return Status::OK(); } if (truncateIndexConfig->GetTruncatePayloadConfig().IsInitialized() && existingPayloadName != incomingPayloadName) { return Status::InternalError("Index [%s] has different truncate payload [%s] and [%s]", indexConfig->GetIndexName().c_str(), existingPayloadName.c_str(), incomingPayloadName.c_str()); } return Status::OK(); } std::shared_ptr<config::InvertedIndexConfig> NormalSchemaResolver::CreateTruncateIndexConfig(const std::shared_ptr<config::InvertedIndexConfig>& invertedIndexConfig, const config::TruncateProfileConfig& truncateProfileConfig) const { const std::string& indexName = invertedIndexConfig->GetIndexName(); auto truncateIndexConfig = std::shared_ptr<config::InvertedIndexConfig>(invertedIndexConfig->Clone()); truncateIndexConfig->SetIndexName(config::InvertedIndexConfig::CreateTruncateIndexName( indexName, truncateProfileConfig.GetTruncateProfileName())); // TODO(xc & lc) 在InvertedIndexReaderImpl open阶段判断是否使用默认值逻辑中, 需要区分当前config是否是truncate // index truncateIndexConfig->SetNonTruncateIndexName(indexName); truncateIndexConfig->SetVirtual(true); truncateIndexConfig->SetFileCompressConfig(invertedIndexConfig->GetFileCompressConfig()); truncateIndexConfig->SetHasTruncateFlag(false); truncateIndexConfig->SetTruncatePayloadConfig(truncateProfileConfig.GetPayloadConfig()); return truncateIndexConfig; } bool NormalSchemaResolver::IsValidTruncateSortParam(const std::vector<indexlib::config::SortParam>& sortParams, const config::UnresolvedSchema* schema) const { for (auto& sortParam : sortParams) { auto sortField = sortParam.GetSortField(); if (sortField == indexlib::DOC_PAYLOAD_FIELD_NAME) { continue; } auto indexConfig = schema->GetIndexConfig(index::ATTRIBUTE_INDEX_TYPE_STR, sortField); if (indexConfig == nullptr) { AUTIL_LOG(ERROR, "sort field in truncate profile must be an attribute, field[%s]", sortField.c_str()); return false; } auto attributeConfig = std::dynamic_pointer_cast<indexlibv2::index::AttributeConfig>(indexConfig); assert(attributeConfig != nullptr); if (attributeConfig->IsMultiValue()) { AUTIL_LOG(ERROR, "sort field in truncate profile must be an single-value attribute, field[%s]", sortField.c_str()); return false; } // TODO(xc) ft_fp8 ft_fp16? auto fieldType = attributeConfig->GetFieldType(); if (fieldType != ft_integer && fieldType != ft_float && fieldType != ft_long && fieldType != ft_uint8 && fieldType != ft_uint16 && fieldType != ft_uint32 && fieldType != ft_uint64 && fieldType != ft_double && fieldType != ft_int8 && fieldType != ft_int16 && fieldType != ft_int32 && fieldType != ft_int64 && fieldType != ft_time && fieldType != ft_timestamp && fieldType != ft_date) { AUTIL_LOG(ERROR, "sort field in truncate profile must be a numeric field, field[%s]", sortField.c_str()); return false; } } return true; } } // namespace indexlibv2::table