This is an automated email from the ASF dual-hosted git repository. morningman pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push: new d39b8e1be51 [enhance](mtmv)support partition tvf (#36479) d39b8e1be51 is described below commit d39b8e1be510ec0257415d24c6df95f5904a6bc7 Author: zhangdong <493738...@qq.com> AuthorDate: Wed Jun 26 18:56:12 2024 +0800 [enhance](mtmv)support partition tvf (#36479) The displayed content is consistent with the show partitions ``` mysql> select * from partitions("catalog"="internal","database"="zd","table"="ss")\G *************************** 1. row *************************** PartitionId: 14004 PartitionName: p1 VisibleVersion: 1 VisibleVersionTime: 2024-06-18 17:41:07 State: NORMAL PartitionKey: k3 Range: [types: [INT]; keys: [1]; ] DistributionKey: k2 Buckets: 2 ReplicationNum: 1 StorageMedium: HDD CooldownTime: 9999-12-31 23:59:59 RemoteStoragePolicy: LastConsistencyCheckTime: \N DataSize: 0.000 IsInMemory: 0 ReplicaAllocation: tag.location.default: 1 IsMutable: 1 SyncWithBaseTables: 1 UnsyncTables: \N 1 row in set (0.02 sec) ``` --- be/src/vec/exec/scan/vmeta_scanner.cpp | 23 ++ be/src/vec/exec/scan/vmeta_scanner.h | 2 + .../doris/catalog/BuiltinTableValuedFunctions.java | 2 + .../doris/common/proc/PartitionsProcDir.java | 77 +++++-- .../expressions/functions/table/Partitions.java | 58 +++++ .../visitor/TableValuedFunctionVisitor.java | 5 + .../doris/tablefunction/MetadataGenerator.java | 114 +++++++++- .../tablefunction/MetadataTableValuedFunction.java | 2 + .../PartitionsTableValuedFunction.java | 243 +++++++++++++++++++++ .../doris/tablefunction/TableValuedFunctionIf.java | 2 + gensrc/thrift/FrontendService.thrift | 1 + gensrc/thrift/PlanNodes.thrift | 8 + gensrc/thrift/Types.thrift | 3 +- .../tvf/test_hms_partitions_tvf.out | 8 + .../external_table_p0/tvf/test_partitions_tvf.out | 23 ++ .../tvf/test_hms_partitions_tvf.groovy | 46 ++++ .../tvf/test_partitions_tvf.groovy | 78 +++++++ 17 files changed, 675 insertions(+), 20 deletions(-) diff --git a/be/src/vec/exec/scan/vmeta_scanner.cpp b/be/src/vec/exec/scan/vmeta_scanner.cpp index 44a8624f405..64819cfaaa2 100644 --- a/be/src/vec/exec/scan/vmeta_scanner.cpp +++ b/be/src/vec/exec/scan/vmeta_scanner.cpp @@ -233,6 +233,9 @@ Status VMetaScanner::_fetch_metadata(const TMetaScanRange& meta_scan_range) { case TMetadataType::MATERIALIZED_VIEWS: RETURN_IF_ERROR(_build_materialized_views_metadata_request(meta_scan_range, &request)); break; + case TMetadataType::PARTITIONS: + RETURN_IF_ERROR(_build_partitions_metadata_request(meta_scan_range, &request)); + break; case TMetadataType::JOBS: RETURN_IF_ERROR(_build_jobs_metadata_request(meta_scan_range, &request)); break; @@ -401,6 +404,26 @@ Status VMetaScanner::_build_materialized_views_metadata_request( return Status::OK(); } +Status VMetaScanner::_build_partitions_metadata_request(const TMetaScanRange& meta_scan_range, + TFetchSchemaTableDataRequest* request) { + VLOG_CRITICAL << "VMetaScanner::_build_partitions_metadata_request"; + if (!meta_scan_range.__isset.partitions_params) { + return Status::InternalError( + "Can not find TPartitionsMetadataParams from meta_scan_range."); + } + + // create request + request->__set_schema_table_name(TSchemaTableName::METADATA_TABLE); + + // create TMetadataTableRequestParams + TMetadataTableRequestParams metadata_table_params; + metadata_table_params.__set_metadata_type(TMetadataType::PARTITIONS); + metadata_table_params.__set_partitions_metadata_params(meta_scan_range.partitions_params); + + request->__set_metada_table_params(metadata_table_params); + return Status::OK(); +} + Status VMetaScanner::_build_jobs_metadata_request(const TMetaScanRange& meta_scan_range, TFetchSchemaTableDataRequest* request) { VLOG_CRITICAL << "VMetaScanner::_build_jobs_metadata_request"; diff --git a/be/src/vec/exec/scan/vmeta_scanner.h b/be/src/vec/exec/scan/vmeta_scanner.h index 7bf308cd56a..5936130069c 100644 --- a/be/src/vec/exec/scan/vmeta_scanner.h +++ b/be/src/vec/exec/scan/vmeta_scanner.h @@ -81,6 +81,8 @@ private: TFetchSchemaTableDataRequest* request); Status _build_materialized_views_metadata_request(const TMetaScanRange& meta_scan_range, TFetchSchemaTableDataRequest* request); + Status _build_partitions_metadata_request(const TMetaScanRange& meta_scan_range, + TFetchSchemaTableDataRequest* request); Status _build_jobs_metadata_request(const TMetaScanRange& meta_scan_range, TFetchSchemaTableDataRequest* request); Status _build_tasks_metadata_request(const TMetaScanRange& meta_scan_range, diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinTableValuedFunctions.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinTableValuedFunctions.java index 3becd2e102b..db66c260e56 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinTableValuedFunctions.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinTableValuedFunctions.java @@ -29,6 +29,7 @@ import org.apache.doris.nereids.trees.expressions.functions.table.Jobs; import org.apache.doris.nereids.trees.expressions.functions.table.Local; import org.apache.doris.nereids.trees.expressions.functions.table.MvInfos; import org.apache.doris.nereids.trees.expressions.functions.table.Numbers; +import org.apache.doris.nereids.trees.expressions.functions.table.Partitions; import org.apache.doris.nereids.trees.expressions.functions.table.Query; import org.apache.doris.nereids.trees.expressions.functions.table.S3; import org.apache.doris.nereids.trees.expressions.functions.table.Tasks; @@ -55,6 +56,7 @@ public class BuiltinTableValuedFunctions implements FunctionHelper { tableValued(Numbers.class, "numbers"), tableValued(S3.class, "s3"), tableValued(MvInfos.class, "mv_infos"), + tableValued(Partitions.class, "partitions"), tableValued(Jobs.class, "jobs"), tableValued(Tasks.class, "tasks"), tableValued(Query.class, "query") diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/proc/PartitionsProcDir.java b/fe/fe-core/src/main/java/org/apache/doris/common/proc/PartitionsProcDir.java index b36bc5eb022..48e406b66d9 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/common/proc/PartitionsProcDir.java +++ b/fe/fe-core/src/main/java/org/apache/doris/common/proc/PartitionsProcDir.java @@ -46,6 +46,8 @@ import org.apache.doris.common.util.ListComparator; import org.apache.doris.common.util.OrderByPair; import org.apache.doris.common.util.TimeUtils; import org.apache.doris.mtmv.MTMVPartitionUtil; +import org.apache.doris.thrift.TCell; +import org.apache.doris.thrift.TRow; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; @@ -222,12 +224,22 @@ public class PartitionsProcDir implements ProcDirInterface { } private List<List<Comparable>> getPartitionInfos() throws AnalysisException { + List<Pair<List<Comparable>, TRow>> partitionInfosInrernal = getPartitionInfosInrernal(); + return partitionInfosInrernal.stream().map(pair -> pair.first).collect(Collectors.toList()); + } + + public List<TRow> getPartitionInfosForTvf() throws AnalysisException { + List<Pair<List<Comparable>, TRow>> partitionInfosInrernal = getPartitionInfosInrernal(); + return partitionInfosInrernal.stream().map(pair -> pair.second).collect(Collectors.toList()); + } + + private List<Pair<List<Comparable>, TRow>> getPartitionInfosInrernal() throws AnalysisException { Preconditions.checkNotNull(db); Preconditions.checkNotNull(olapTable); Preconditions.checkState(olapTable.isManagedTable()); // get info - List<List<Comparable>> partitionInfos = new ArrayList<List<Comparable>>(); + List<Pair<List<Comparable>, TRow>> partitionInfos = new ArrayList<Pair<List<Comparable>, TRow>>(); olapTable.readLock(); try { List<Long> partitionIds; @@ -259,13 +271,19 @@ public class PartitionsProcDir implements ProcDirInterface { Partition partition = olapTable.getPartition(partitionId); List<Comparable> partitionInfo = new ArrayList<Comparable>(); + TRow trow = new TRow(); String partitionName = partition.getName(); partitionInfo.add(partitionId); + trow.addToColumnValue(new TCell().setLongVal(partitionId)); partitionInfo.add(partitionName); + trow.addToColumnValue(new TCell().setStringVal(partitionName)); partitionInfo.add(partition.getVisibleVersion()); - partitionInfo.add(TimeUtils.longToTimeString(partition.getVisibleVersionTime())); + trow.addToColumnValue(new TCell().setLongVal(partition.getVisibleVersion())); + String visibleTime = TimeUtils.longToTimeString(partition.getVisibleVersionTime()); + partitionInfo.add(visibleTime); + trow.addToColumnValue(new TCell().setStringVal(visibleTime)); partitionInfo.add(partition.getState()); - + trow.addToColumnValue(new TCell().setStringVal(partition.getState().toString())); if (tblPartitionInfo.getType() == PartitionType.RANGE || tblPartitionInfo.getType() == PartitionType.LIST) { List<Column> partitionColumns = tblPartitionInfo.getPartitionColumns(); @@ -273,11 +291,17 @@ public class PartitionsProcDir implements ProcDirInterface { for (Column column : partitionColumns) { colNames.add(column.getName()); } - partitionInfo.add(joiner.join(colNames)); - partitionInfo.add(tblPartitionInfo.getItem(partitionId).getItems().toString()); + String colNamesStr = joiner.join(colNames); + partitionInfo.add(colNamesStr); + trow.addToColumnValue(new TCell().setStringVal(colNamesStr)); + String itemStr = tblPartitionInfo.getItem(partitionId).getItems().toString(); + partitionInfo.add(itemStr); + trow.addToColumnValue(new TCell().setStringVal(itemStr)); } else { partitionInfo.add(""); + trow.addToColumnValue(new TCell().setStringVal("")); partitionInfo.add(""); + trow.addToColumnValue(new TCell().setStringVal("")); } // distribution @@ -293,47 +317,70 @@ public class PartitionsProcDir implements ProcDirInterface { sb.append(distributionColumns.get(i).getName()); } partitionInfo.add(sb.toString()); + trow.addToColumnValue(new TCell().setStringVal(sb.toString())); } else { partitionInfo.add("RANDOM"); + trow.addToColumnValue(new TCell().setStringVal("RANDOM")); } partitionInfo.add(distributionInfo.getBucketNum()); + trow.addToColumnValue(new TCell().setIntVal(distributionInfo.getBucketNum())); // replica num - partitionInfo.add(tblPartitionInfo.getReplicaAllocation(partitionId).getTotalReplicaNum()); + short totalReplicaNum = tblPartitionInfo.getReplicaAllocation(partitionId).getTotalReplicaNum(); + partitionInfo.add(totalReplicaNum); + trow.addToColumnValue(new TCell().setIntVal(totalReplicaNum)); DataProperty dataProperty = tblPartitionInfo.getDataProperty(partitionId); partitionInfo.add(dataProperty.getStorageMedium().name()); - partitionInfo.add(TimeUtils.longToTimeString(dataProperty.getCooldownTimeMs())); + trow.addToColumnValue(new TCell().setStringVal(dataProperty.getStorageMedium().name())); + String cooldownTimeStr = TimeUtils.longToTimeString(dataProperty.getCooldownTimeMs()); + partitionInfo.add(cooldownTimeStr); + trow.addToColumnValue(new TCell().setStringVal(cooldownTimeStr)); partitionInfo.add(dataProperty.getStoragePolicy()); - - partitionInfo.add(TimeUtils.longToTimeString(partition.getLastCheckTime())); - + trow.addToColumnValue(new TCell().setStringVal(dataProperty.getStoragePolicy())); + String lastCheckTime = TimeUtils.longToTimeString(partition.getLastCheckTime()); + partitionInfo.add(lastCheckTime); + trow.addToColumnValue(new TCell().setStringVal(lastCheckTime)); long dataSize = partition.getDataSize(false); Pair<Double, String> sizePair = DebugUtil.getByteUint(dataSize); String readableSize = DebugUtil.DECIMAL_FORMAT_SCALE_3.format(sizePair.first) + " " + sizePair.second; partitionInfo.add(readableSize); - partitionInfo.add(tblPartitionInfo.getIsInMemory(partitionId)); + trow.addToColumnValue(new TCell().setStringVal(readableSize)); + boolean isInMemory = tblPartitionInfo.getIsInMemory(partitionId); + partitionInfo.add(isInMemory); + trow.addToColumnValue(new TCell().setBoolVal(isInMemory)); // replica allocation - partitionInfo.add(tblPartitionInfo.getReplicaAllocation(partitionId).toCreateStmt()); + String replica = tblPartitionInfo.getReplicaAllocation(partitionId).toCreateStmt(); + partitionInfo.add(replica); + trow.addToColumnValue(new TCell().setStringVal(replica)); - partitionInfo.add(tblPartitionInfo.getIsMutable(partitionId)); + boolean isMutable = tblPartitionInfo.getIsMutable(partitionId); + partitionInfo.add(isMutable); + trow.addToColumnValue(new TCell().setBoolVal(isMutable)); if (olapTable instanceof MTMV) { if (StringUtils.isEmpty(mtmvPartitionSyncErrorMsg)) { List<String> partitionUnSyncTables = partitionsUnSyncTables.getOrDefault(partitionId, Lists.newArrayList()); - partitionInfo.add(CollectionUtils.isEmpty(partitionUnSyncTables)); + boolean isSync = CollectionUtils.isEmpty(partitionUnSyncTables); + partitionInfo.add(isSync); + trow.addToColumnValue(new TCell().setBoolVal(isSync)); partitionInfo.add(partitionUnSyncTables.toString()); + trow.addToColumnValue(new TCell().setStringVal(partitionUnSyncTables.toString())); } else { partitionInfo.add(false); + trow.addToColumnValue(new TCell().setBoolVal(false)); partitionInfo.add(mtmvPartitionSyncErrorMsg); + trow.addToColumnValue(new TCell().setStringVal(mtmvPartitionSyncErrorMsg)); } } else { partitionInfo.add(true); + trow.addToColumnValue(new TCell().setBoolVal(true)); partitionInfo.add(FeConstants.null_string); + trow.addToColumnValue(new TCell().setStringVal(FeConstants.null_string)); } - partitionInfos.add(partitionInfo); + partitionInfos.add(Pair.of(partitionInfo, trow)); } } finally { olapTable.readUnlock(); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/Partitions.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/Partitions.java new file mode 100644 index 00000000000..6dcd31a9651 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/Partitions.java @@ -0,0 +1,58 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +package org.apache.doris.nereids.trees.expressions.functions.table; + +import org.apache.doris.catalog.FunctionSignature; +import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.nereids.trees.expressions.Properties; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.coercion.AnyDataType; +import org.apache.doris.tablefunction.PartitionsTableValuedFunction; +import org.apache.doris.tablefunction.TableValuedFunctionIf; + +import java.util.Map; + +/** + * partitions + */ +public class Partitions extends TableValuedFunction { + public Partitions(Properties properties) { + super("partitions", properties); + } + + @Override + public FunctionSignature customSignature() { + return FunctionSignature.of(AnyDataType.INSTANCE_WITHOUT_INDEX, getArgumentsTypes()); + } + + @Override + protected TableValuedFunctionIf toCatalogFunction() { + try { + Map<String, String> arguments = getTVFProperties().getMap(); + return new PartitionsTableValuedFunction(arguments); + } catch (Throwable t) { + throw new AnalysisException("Can not build PartitionsTableValuedFunction by " + + this + ": " + t.getMessage(), t); + } + } + + @Override + public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) { + return visitor.visitPartitions(this, context); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/TableValuedFunctionVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/TableValuedFunctionVisitor.java index bd09a81b011..ca14edd87de 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/TableValuedFunctionVisitor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/TableValuedFunctionVisitor.java @@ -29,6 +29,7 @@ import org.apache.doris.nereids.trees.expressions.functions.table.Jobs; import org.apache.doris.nereids.trees.expressions.functions.table.Local; import org.apache.doris.nereids.trees.expressions.functions.table.MvInfos; import org.apache.doris.nereids.trees.expressions.functions.table.Numbers; +import org.apache.doris.nereids.trees.expressions.functions.table.Partitions; import org.apache.doris.nereids.trees.expressions.functions.table.Query; import org.apache.doris.nereids.trees.expressions.functions.table.S3; import org.apache.doris.nereids.trees.expressions.functions.table.TableValuedFunction; @@ -54,6 +55,10 @@ public interface TableValuedFunctionVisitor<R, C> { return visitTableValuedFunction(mvInfos, context); } + default R visitPartitions(Partitions partitions, C context) { + return visitTableValuedFunction(partitions, context); + } + default R visitJobs(Jobs jobs, C context) { return visitTableValuedFunction(jobs, context); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/tablefunction/MetadataGenerator.java b/fe/fe-core/src/main/java/org/apache/doris/tablefunction/MetadataGenerator.java index b3ed195a935..91690e23b95 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/tablefunction/MetadataGenerator.java +++ b/fe/fe-core/src/main/java/org/apache/doris/tablefunction/MetadataGenerator.java @@ -19,6 +19,7 @@ package org.apache.doris.tablefunction; import org.apache.doris.analysis.UserIdentity; import org.apache.doris.catalog.Column; +import org.apache.doris.catalog.Database; import org.apache.doris.catalog.DatabaseIf; import org.apache.doris.catalog.DistributionInfo; import org.apache.doris.catalog.DistributionInfo.DistributionInfoType; @@ -35,11 +36,14 @@ import org.apache.doris.common.ClientPool; import org.apache.doris.common.Pair; import org.apache.doris.common.UserException; import org.apache.doris.common.proc.FrontendsProcNode; +import org.apache.doris.common.proc.PartitionsProcDir; import org.apache.doris.common.util.NetUtils; import org.apache.doris.common.util.TimeUtils; import org.apache.doris.datasource.CatalogIf; import org.apache.doris.datasource.InternalCatalog; +import org.apache.doris.datasource.hive.HMSExternalCatalog; import org.apache.doris.datasource.iceberg.IcebergMetadataCache; +import org.apache.doris.datasource.maxcompute.MaxComputeExternalCatalog; import org.apache.doris.job.common.JobType; import org.apache.doris.job.extensions.mtmv.MTMVJob; import org.apache.doris.job.task.AbstractTask; @@ -67,6 +71,7 @@ import org.apache.doris.thrift.TMaterializedViewsMetadataParams; import org.apache.doris.thrift.TMetadataTableRequestParams; import org.apache.doris.thrift.TMetadataType; import org.apache.doris.thrift.TNetworkAddress; +import org.apache.doris.thrift.TPartitionsMetadataParams; import org.apache.doris.thrift.TPipelineWorkloadGroup; import org.apache.doris.thrift.TRow; import org.apache.doris.thrift.TSchemaTableRequestParams; @@ -175,6 +180,9 @@ public class MetadataGenerator { case MATERIALIZED_VIEWS: result = mtmvMetadataResult(params); break; + case PARTITIONS: + result = partitionMetadataResult(params); + break; case JOBS: result = jobMetadataResult(params); break; @@ -775,6 +783,104 @@ public class MetadataGenerator { return result; } + private static TFetchSchemaTableDataResult partitionMetadataResult(TMetadataTableRequestParams params) { + if (LOG.isDebugEnabled()) { + LOG.debug("partitionMetadataResult() start"); + } + if (!params.isSetPartitionsMetadataParams()) { + if (LOG.isDebugEnabled()) { + LOG.debug("Partitions metadata params is not set."); + } + return errorResult("Partitions metadata params is not set."); + } + + TPartitionsMetadataParams partitionsMetadataParams = params.getPartitionsMetadataParams(); + String catalogName = partitionsMetadataParams.getCatalog(); + if (LOG.isDebugEnabled()) { + LOG.debug("catalogName: " + catalogName); + } + String dbName = partitionsMetadataParams.getDatabase(); + if (LOG.isDebugEnabled()) { + LOG.debug("dbName: " + dbName); + } + String tableName = partitionsMetadataParams.getTable(); + if (LOG.isDebugEnabled()) { + LOG.debug("tableName: " + tableName); + } + + CatalogIf catalog; + TableIf table; + DatabaseIf db; + try { + catalog = Env.getCurrentEnv().getCatalogMgr() + .getCatalogOrAnalysisException(catalogName); + db = catalog.getDbOrAnalysisException(dbName); + table = db.getTableOrAnalysisException(tableName); + } catch (AnalysisException e) { + LOG.warn(e.getMessage()); + return errorResult(e.getMessage()); + } + + if (catalog instanceof InternalCatalog) { + return dealInternalCatalog((Database) db, table); + } else if (catalog instanceof MaxComputeExternalCatalog) { + return dealMaxComputeCatalog((MaxComputeExternalCatalog) catalog, dbName, tableName); + } else if (catalog instanceof HMSExternalCatalog) { + return dealHMSCatalog((HMSExternalCatalog) catalog, dbName, tableName); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("partitionMetadataResult() end"); + } + return errorResult("not support catalog: " + catalogName); + } + + private static TFetchSchemaTableDataResult dealHMSCatalog(HMSExternalCatalog catalog, String dbName, + String tableName) { + List<TRow> dataBatch = Lists.newArrayList(); + List<String> partitionNames = catalog.getClient().listPartitionNames(dbName, tableName); + for (String partition : partitionNames) { + TRow trow = new TRow(); + trow.addToColumnValue(new TCell().setStringVal(partition)); + dataBatch.add(trow); + } + TFetchSchemaTableDataResult result = new TFetchSchemaTableDataResult(); + result.setDataBatch(dataBatch); + result.setStatus(new TStatus(TStatusCode.OK)); + return result; + } + + private static TFetchSchemaTableDataResult dealMaxComputeCatalog(MaxComputeExternalCatalog catalog, String dbName, + String tableName) { + List<TRow> dataBatch = Lists.newArrayList(); + List<String> partitionNames = catalog.listPartitionNames(dbName, tableName); + for (String partition : partitionNames) { + TRow trow = new TRow(); + trow.addToColumnValue(new TCell().setStringVal(partition)); + dataBatch.add(trow); + } + TFetchSchemaTableDataResult result = new TFetchSchemaTableDataResult(); + result.setDataBatch(dataBatch); + result.setStatus(new TStatus(TStatusCode.OK)); + return result; + } + + private static TFetchSchemaTableDataResult dealInternalCatalog(Database db, TableIf table) { + TFetchSchemaTableDataResult result = new TFetchSchemaTableDataResult(); + if (!(table instanceof OlapTable)) { + return errorResult("not olap table"); + } + PartitionsProcDir dir = new PartitionsProcDir(db, (OlapTable) table, false); + try { + List<TRow> dataBatch = dir.getPartitionInfosForTvf(); + result.setDataBatch(dataBatch); + } catch (AnalysisException e) { + return errorResult(e.getMessage()); + } + result.setStatus(new TStatus(TStatusCode.OK)); + return result; + } + private static TFetchSchemaTableDataResult jobMetadataResult(TMetadataTableRequestParams params) { if (!params.isSetJobsMetadataParams()) { return errorResult("Jobs metadata params is not set."); @@ -917,9 +1023,9 @@ public class MetadataGenerator { trow.addToColumnValue(new TCell().setStringVal(catalog.getName())); // TABLE_CATALOG trow.addToColumnValue(new TCell().setStringVal(database.getFullName())); // TABLE_SCHEMA trow.addToColumnValue( - new TCell().setStringVal(olapTable.getKeysType().toMetadata())); //TABLE_MODEL + new TCell().setStringVal(olapTable.getKeysType().toMetadata())); // TABLE_MODEL trow.addToColumnValue( - new TCell().setStringVal(olapTable.getKeyColAsString())); // key columTypes + new TCell().setStringVal(olapTable.getKeyColAsString())); // key columTypes DistributionInfo distributionInfo = olapTable.getDefaultDistributionInfo(); if (distributionInfo.getType() == DistributionInfoType.HASH) { @@ -936,7 +1042,7 @@ public class MetadataGenerator { trow.addToColumnValue(new TCell().setStringVal("")); } else { trow.addToColumnValue( - new TCell().setStringVal(distributeKey.toString())); + new TCell().setStringVal(distributeKey.toString())); } trow.addToColumnValue(new TCell().setStringVal("HASH")); // DISTRIBUTE_TYPE } else { @@ -951,7 +1057,7 @@ public class MetadataGenerator { } else { try { trow.addToColumnValue( - new TCell().setStringVal(property.getPropertiesString())); // PROPERTIES + new TCell().setStringVal(property.getPropertiesString())); // PROPERTIES } catch (IOException e) { return errorResult(e.getMessage()); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/tablefunction/MetadataTableValuedFunction.java b/fe/fe-core/src/main/java/org/apache/doris/tablefunction/MetadataTableValuedFunction.java index af37bcd10e4..a7e25bc7f82 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/tablefunction/MetadataTableValuedFunction.java +++ b/fe/fe-core/src/main/java/org/apache/doris/tablefunction/MetadataTableValuedFunction.java @@ -44,6 +44,8 @@ public abstract class MetadataTableValuedFunction extends TableValuedFunctionIf return CatalogsTableValuedFunction.getColumnIndexFromColumnName(columnName); case MATERIALIZED_VIEWS: return MvInfosTableValuedFunction.getColumnIndexFromColumnName(columnName); + case PARTITIONS: + return PartitionsTableValuedFunction.getColumnIndexFromColumnName(columnName, params); case JOBS: return JobsTableValuedFunction.getColumnIndexFromColumnName(columnName, params); case TASKS: diff --git a/fe/fe-core/src/main/java/org/apache/doris/tablefunction/PartitionsTableValuedFunction.java b/fe/fe-core/src/main/java/org/apache/doris/tablefunction/PartitionsTableValuedFunction.java new file mode 100644 index 00000000000..1ceddeb89cf --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/tablefunction/PartitionsTableValuedFunction.java @@ -0,0 +1,243 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +package org.apache.doris.tablefunction; + +import org.apache.doris.catalog.Column; +import org.apache.doris.catalog.DatabaseIf; +import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.PrimitiveType; +import org.apache.doris.catalog.ScalarType; +import org.apache.doris.catalog.TableIf; +import org.apache.doris.catalog.TableIf.TableType; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.common.MetaNotFoundException; +import org.apache.doris.datasource.CatalogIf; +import org.apache.doris.datasource.InternalCatalog; +import org.apache.doris.datasource.hive.HMSExternalCatalog; +import org.apache.doris.datasource.hive.HMSExternalTable; +import org.apache.doris.datasource.maxcompute.MaxComputeExternalCatalog; +import org.apache.doris.datasource.maxcompute.MaxComputeExternalTable; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.thrift.TMetaScanRange; +import org.apache.doris.thrift.TMetadataTableRequestParams; +import org.apache.doris.thrift.TMetadataType; +import org.apache.doris.thrift.TPartitionsMetadataParams; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * The Implement of table valued function + * partitions("database" = "db1","table" = "table1"). + */ +public class PartitionsTableValuedFunction extends MetadataTableValuedFunction { + private static final Logger LOG = LogManager.getLogger(PartitionsTableValuedFunction.class); + + public static final String NAME = "partitions"; + + private static final String CATALOG = "catalog"; + private static final String DB = "database"; + private static final String TABLE = "table"; + + private static final ImmutableSet<String> PROPERTIES_SET = ImmutableSet.of(CATALOG, DB, TABLE); + + private static final ImmutableList<Column> SCHEMA_FOR_OLAP_TABLE = ImmutableList.of( + new Column("PartitionId", ScalarType.createType(PrimitiveType.BIGINT)), + new Column("PartitionName", ScalarType.createStringType()), + new Column("VisibleVersion", ScalarType.createType(PrimitiveType.BIGINT)), + new Column("VisibleVersionTime", ScalarType.createStringType()), + new Column("State", ScalarType.createStringType()), + new Column("PartitionKey", ScalarType.createStringType()), + new Column("Range", ScalarType.createStringType()), + new Column("DistributionKey", ScalarType.createStringType()), + new Column("Buckets", ScalarType.createType(PrimitiveType.INT)), + new Column("ReplicationNum", ScalarType.createType(PrimitiveType.INT)), + new Column("StorageMedium", ScalarType.createStringType()), + new Column("CooldownTime", ScalarType.createStringType()), + new Column("RemoteStoragePolicy", ScalarType.createStringType()), + new Column("LastConsistencyCheckTime", ScalarType.createStringType()), + new Column("DataSize", ScalarType.createStringType()), + new Column("IsInMemory", ScalarType.createType(PrimitiveType.BOOLEAN)), + new Column("ReplicaAllocation", ScalarType.createStringType()), + new Column("IsMutable", ScalarType.createType(PrimitiveType.BOOLEAN)), + new Column("SyncWithBaseTables", ScalarType.createType(PrimitiveType.BOOLEAN)), + new Column("UnsyncTables", ScalarType.createStringType())); + + private static final ImmutableList<Column> SCHEMA_FOR_EXTERNAL_TABLE = ImmutableList.of( + new Column("Partition", ScalarType.createStringType())); + + private static final ImmutableMap<String, Integer> OLAP_TABLE_COLUMN_TO_INDEX; + private static final ImmutableMap<String, Integer> EXTERNAL_TABLE_COLUMN_TO_INDEX; + + static { + ImmutableMap.Builder<String, Integer> builder = new ImmutableMap.Builder(); + for (int i = 0; i < SCHEMA_FOR_OLAP_TABLE.size(); i++) { + builder.put(SCHEMA_FOR_OLAP_TABLE.get(i).getName().toLowerCase(), i); + } + OLAP_TABLE_COLUMN_TO_INDEX = builder.build(); + + ImmutableMap.Builder<String, Integer> otherBuilder = new ImmutableMap.Builder(); + for (int i = 0; i < SCHEMA_FOR_EXTERNAL_TABLE.size(); i++) { + otherBuilder.put(SCHEMA_FOR_EXTERNAL_TABLE.get(i).getName().toLowerCase(), i); + } + EXTERNAL_TABLE_COLUMN_TO_INDEX = otherBuilder.build(); + } + + public static Integer getColumnIndexFromColumnName(String columnName, TMetadataTableRequestParams params) + throws org.apache.doris.common.AnalysisException { + if (!params.isSetPartitionsMetadataParams()) { + throw new org.apache.doris.common.AnalysisException("Partitions metadata params is not set."); + } + TPartitionsMetadataParams partitionsMetadataParams = params.getPartitionsMetadataParams(); + String catalogName = partitionsMetadataParams.getCatalog(); + if (InternalCatalog.INTERNAL_CATALOG_NAME.equals(catalogName)) { + return OLAP_TABLE_COLUMN_TO_INDEX.get(columnName.toLowerCase()); + } else { + return EXTERNAL_TABLE_COLUMN_TO_INDEX.get(columnName.toLowerCase()); + } + } + + private final String catalogName; + private final String databaseName; + private final String tableName; + + public PartitionsTableValuedFunction(Map<String, String> params) throws AnalysisException { + if (LOG.isDebugEnabled()) { + LOG.debug("PartitionsTableValuedFunction() start"); + } + Map<String, String> validParams = Maps.newHashMap(); + for (String key : params.keySet()) { + if (!PROPERTIES_SET.contains(key.toLowerCase())) { + throw new AnalysisException("'" + key + "' is invalid property"); + } + // check ctl, db, tbl + validParams.put(key.toLowerCase(), params.get(key)); + } + String catalogName = validParams.get(CATALOG); + String dbName = validParams.get(DB); + String tableName = validParams.get(TABLE); + if (StringUtils.isEmpty(catalogName) || StringUtils.isEmpty(dbName) || StringUtils.isEmpty(tableName)) { + throw new AnalysisException("Invalid partitions metadata query"); + } + analyze(catalogName, dbName, tableName); + this.catalogName = catalogName; + this.databaseName = dbName; + this.tableName = tableName; + if (LOG.isDebugEnabled()) { + LOG.debug("PartitionsTableValuedFunction() end"); + } + } + + private void analyze(String catalogName, String dbName, String tableName) { + if (!Env.getCurrentEnv().getAccessManager() + .checkTblPriv(ConnectContext.get(), catalogName, dbName, + tableName, PrivPredicate.SHOW)) { + String message = ErrorCode.ERR_TABLEACCESS_DENIED_ERROR.formatErrorMsg("SHOW PARTITIONS", + ConnectContext.get().getQualifiedUser(), ConnectContext.get().getRemoteIP(), + catalogName + ": " + dbName + ": " + tableName); + throw new AnalysisException(message); + } + CatalogIf catalog = Env.getCurrentEnv().getCatalogMgr().getCatalog(catalogName); + if (catalog == null) { + throw new AnalysisException("can not find catalog: " + catalogName); + } + // disallow unsupported catalog + if (!(catalog.isInternalCatalog() || catalog instanceof HMSExternalCatalog + || catalog instanceof MaxComputeExternalCatalog)) { + throw new AnalysisException(String.format("Catalog of type '%s' is not allowed in ShowPartitionsStmt", + catalog.getType())); + } + + Optional<DatabaseIf> db = catalog.getDb(dbName); + if (!db.isPresent()) { + throw new AnalysisException("can not find database: " + dbName); + } + TableIf table = null; + try { + table = db.get().getTableOrMetaException(tableName, TableType.OLAP, + TableType.HMS_EXTERNAL_TABLE, TableType.MAX_COMPUTE_EXTERNAL_TABLE); + } catch (MetaNotFoundException e) { + throw new AnalysisException(e.getMessage(), e); + } + + if (table instanceof HMSExternalTable) { + if (((HMSExternalTable) table).isView()) { + throw new AnalysisException("Table " + tableName + " is not a partitioned table"); + } + if (CollectionUtils.isEmpty(((HMSExternalTable) table).getPartitionColumns())) { + throw new AnalysisException("Table " + tableName + " is not a partitioned table"); + } + return; + } + + if (table instanceof MaxComputeExternalTable) { + if (((MaxComputeExternalTable) table).getOdpsTable().getPartitions().isEmpty()) { + throw new AnalysisException("Table " + tableName + " is not a partitioned table"); + } + } + } + + @Override + public TMetadataType getMetadataType() { + return TMetadataType.PARTITIONS; + } + + @Override + public TMetaScanRange getMetaScanRange() { + if (LOG.isDebugEnabled()) { + LOG.debug("getMetaScanRange() start"); + } + TMetaScanRange metaScanRange = new TMetaScanRange(); + metaScanRange.setMetadataType(TMetadataType.PARTITIONS); + TPartitionsMetadataParams partitionParam = new TPartitionsMetadataParams(); + partitionParam.setCatalog(catalogName); + partitionParam.setDatabase(databaseName); + partitionParam.setTable(tableName); + metaScanRange.setPartitionsParams(partitionParam); + if (LOG.isDebugEnabled()) { + LOG.debug("getMetaScanRange() end"); + } + return metaScanRange; + } + + @Override + public String getTableName() { + return "PartitionsTableValuedFunction"; + } + + @Override + public List<Column> getTableColumns() throws AnalysisException { + if (InternalCatalog.INTERNAL_CATALOG_NAME.equals(catalogName)) { + return SCHEMA_FOR_OLAP_TABLE; + } else { + return SCHEMA_FOR_EXTERNAL_TABLE; + } + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/tablefunction/TableValuedFunctionIf.java b/fe/fe-core/src/main/java/org/apache/doris/tablefunction/TableValuedFunctionIf.java index c99da94de0f..5d9e807c11f 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/tablefunction/TableValuedFunctionIf.java +++ b/fe/fe-core/src/main/java/org/apache/doris/tablefunction/TableValuedFunctionIf.java @@ -66,6 +66,8 @@ public abstract class TableValuedFunctionIf { return new CatalogsTableValuedFunction(params); case MvInfosTableValuedFunction.NAME: return new MvInfosTableValuedFunction(params); + case PartitionsTableValuedFunction.NAME: + return new PartitionsTableValuedFunction(params); case JobsTableValuedFunction.NAME: return new JobsTableValuedFunction(params); case TasksTableValuedFunction.NAME: diff --git a/gensrc/thrift/FrontendService.thrift b/gensrc/thrift/FrontendService.thrift index 96ec7222b44..9a57abc9b61 100644 --- a/gensrc/thrift/FrontendService.thrift +++ b/gensrc/thrift/FrontendService.thrift @@ -991,6 +991,7 @@ struct TMetadataTableRequestParams { 8: optional PlanNodes.TMaterializedViewsMetadataParams materialized_views_metadata_params 9: optional PlanNodes.TJobsMetadataParams jobs_metadata_params 10: optional PlanNodes.TTasksMetadataParams tasks_metadata_params + 11: optional PlanNodes.TPartitionsMetadataParams partitions_metadata_params } struct TSchemaTableRequestParams { diff --git a/gensrc/thrift/PlanNodes.thrift b/gensrc/thrift/PlanNodes.thrift index 6405b8f6689..1281a7fba49 100644 --- a/gensrc/thrift/PlanNodes.thrift +++ b/gensrc/thrift/PlanNodes.thrift @@ -528,6 +528,12 @@ struct TMaterializedViewsMetadataParams { 2: optional Types.TUserIdentity current_user_ident } +struct TPartitionsMetadataParams { + 1: optional string catalog + 2: optional string database + 3: optional string table +} + struct TJobsMetadataParams { 1: optional string type 2: optional Types.TUserIdentity current_user_ident @@ -544,6 +550,7 @@ struct TQueriesMetadataParams { 3: optional TMaterializedViewsMetadataParams materialized_views_params 4: optional TJobsMetadataParams jobs_params 5: optional TTasksMetadataParams tasks_params + 6: optional TPartitionsMetadataParams partitions_params } struct TMetaScanRange { @@ -555,6 +562,7 @@ struct TMetaScanRange { 6: optional TMaterializedViewsMetadataParams materialized_views_params 7: optional TJobsMetadataParams jobs_params 8: optional TTasksMetadataParams tasks_params + 9: optional TPartitionsMetadataParams partitions_params } // Specification of an individual data range which is held in its entirety diff --git a/gensrc/thrift/Types.thrift b/gensrc/thrift/Types.thrift index a13e5c03ce2..e947dfc27c9 100644 --- a/gensrc/thrift/Types.thrift +++ b/gensrc/thrift/Types.thrift @@ -731,7 +731,8 @@ enum TMetadataType { MATERIALIZED_VIEWS, JOBS, TASKS, - WORKLOAD_SCHED_POLICY + WORKLOAD_SCHED_POLICY, + PARTITIONS } enum TIcebergQueryType { diff --git a/regression-test/data/external_table_p0/tvf/test_hms_partitions_tvf.out b/regression-test/data/external_table_p0/tvf/test_hms_partitions_tvf.out new file mode 100644 index 00000000000..8a0f5c5521b --- /dev/null +++ b/regression-test/data/external_table_p0/tvf/test_hms_partitions_tvf.out @@ -0,0 +1,8 @@ +-- This file is automatically generated. You should know what you did if you want to edit this +-- !desc -- +Partition TEXT No false \N NONE + +-- !partitions -- +part_col=20230101 +part_col=20230102 + diff --git a/regression-test/data/external_table_p0/tvf/test_partitions_tvf.out b/regression-test/data/external_table_p0/tvf/test_partitions_tvf.out new file mode 100644 index 00000000000..e68f6bf2bee --- /dev/null +++ b/regression-test/data/external_table_p0/tvf/test_partitions_tvf.out @@ -0,0 +1,23 @@ +-- This file is automatically generated. You should know what you did if you want to edit this +-- !desc -- +Buckets INT No false \N NONE +CooldownTime TEXT No false \N NONE +DataSize TEXT No false \N NONE +DistributionKey TEXT No false \N NONE +IsInMemory BOOLEAN No false \N NONE +IsMutable BOOLEAN No false \N NONE +LastConsistencyCheckTime TEXT No false \N NONE +PartitionId BIGINT No false \N NONE +PartitionKey TEXT No false \N NONE +PartitionName TEXT No false \N NONE +Range TEXT No false \N NONE +RemoteStoragePolicy TEXT No false \N NONE +ReplicaAllocation TEXT No false \N NONE +ReplicationNum INT No false \N NONE +State TEXT No false \N NONE +StorageMedium TEXT No false \N NONE +SyncWithBaseTables BOOLEAN No false \N NONE +UnsyncTables TEXT No false \N NONE +VisibleVersion BIGINT No false \N NONE +VisibleVersionTime TEXT No false \N NONE + diff --git a/regression-test/suites/external_table_p0/tvf/test_hms_partitions_tvf.groovy b/regression-test/suites/external_table_p0/tvf/test_hms_partitions_tvf.groovy new file mode 100644 index 00000000000..1f14fd9b4a3 --- /dev/null +++ b/regression-test/suites/external_table_p0/tvf/test_hms_partitions_tvf.groovy @@ -0,0 +1,46 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +suite("test_hms_partitions_tvf","p0,external,tvf,external_docker") { + String suiteName = "test_hms_partitions_tvf" + String tableName = "mtmv_base1" + String dbName = "default" + String enabled = context.config.otherConfigs.get("enableHiveTest") + if (enabled == null || !enabled.equalsIgnoreCase("true")) { + logger.info("diable Hive test.") + return; + } + + for (String hivePrefix : ["hive3"]) { + try { + String hms_port = context.config.otherConfigs.get(hivePrefix + "HmsPort") + String catalogName = "${suiteName}_${hivePrefix}_catalog" + String externalEnvIp = context.config.otherConfigs.get("externalEnvIp") + + sql """drop catalog if exists ${catalogName}""" + sql """create catalog if not exists ${catalogName} properties ( + "type"="hms", + 'hive.metastore.uris' = 'thrift://${externalEnvIp}:${hms_port}' + );""" + order_qt_desc "desc function partitions('catalog'='${catalogName}','database'='${dbName}','table'='${tableName}');" + order_qt_partitions "select * from partitions('catalog'='${catalogName}','database'='${dbName}','table'='${tableName}');" + + sql """drop catalog if exists ${catalogName}""" + } finally { + } + } +} diff --git a/regression-test/suites/external_table_p0/tvf/test_partitions_tvf.groovy b/regression-test/suites/external_table_p0/tvf/test_partitions_tvf.groovy new file mode 100644 index 00000000000..f61ccf6d224 --- /dev/null +++ b/regression-test/suites/external_table_p0/tvf/test_partitions_tvf.groovy @@ -0,0 +1,78 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +suite("test_partitions_tvf","p0,external,tvf,external_docker") { + String suiteName = "test_partitions_tvf" + String tableName = "${suiteName}_table" + sql """drop table if exists `${tableName}`""" + String dbName = context.config.getDbNameByFile(context.file) + + sql """ + CREATE TABLE ${tableName} + ( + k2 TINYINT, + k3 INT not null + ) + COMMENT "my first table" + PARTITION BY LIST(`k3`) + ( + PARTITION `p1` VALUES IN ('1') + ) + DISTRIBUTED BY HASH(k2) BUCKETS 2 + PROPERTIES ( + "replication_num" = "1" + ); + """ + order_qt_desc "desc function partitions('catalog'='internal','database'='${dbName}','table'='${tableName}');" + List<List<Object>> res = sql """ select * from partitions('catalog'='internal',"database"="${dbName}","table"="${tableName}"); """ + logger.info("res: " + res.toString()) + + assertEquals(1, res.size()); + // PartitionName + assertEquals("p1", res[0][1]); + // State + assertEquals("NORMAL", res[0][4]); + // PartitionKey + assertEquals("k3", res[0][5]); + // Buckets + assertEquals(2, res[0][8]); + // ReplicationNum + assertEquals(1, res[0][9]); + // StorageMedium + assertEquals("HDD", res[0][10]); + // ReplicaAllocation + assertEquals("tag.location.default: 1", res[0][16]); + // IsMutable + assertEquals(true, res[0][17]); + // SyncWithBaseTables + assertEquals(true, res[0][18]); + + + // test exception + test { + sql """ select * from partitions("catalog"="internal","database"="${dbName}","table"="xxx"); """ + // check exception + exception "xxx" + } + test { + sql """ select * from partitions("database"="${dbName}"); """ + // check exception + exception "Invalid" + } + + sql """drop table if exists `${tableName}`""" +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org