This is an automated email from the ASF dual-hosted git repository.
morningman pushed a commit to branch branch-2.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-2.1 by this push:
new 5b3b2cec80b [feat](metatable) support table$partitions for hive table
(#40774) (#41230)
5b3b2cec80b is described below
commit 5b3b2cec80b717956ac50050c1cbdc68f130b750
Author: Mingyu Chen <[email protected]>
AuthorDate: Wed Sep 25 09:52:07 2024 +0800
[feat](metatable) support table$partitions for hive table (#40774) (#41230)
bp #40774
and pick part of #34552, add `isPartitionedTable()` interface in `TableIf`
---
be/src/vec/exec/scan/vmeta_scanner.cpp | 170 +++++++++++--------
be/src/vec/exec/scan/vmeta_scanner.h | 2 +
.../antlr4/org/apache/doris/nereids/DorisParser.g4 | 4 +-
.../org/apache/doris/analysis/DescribeStmt.java | 28 +++-
.../doris/catalog/BuiltinTableValuedFunctions.java | 4 +-
.../main/java/org/apache/doris/catalog/Table.java | 6 -
.../java/org/apache/doris/catalog/TableIf.java | 6 +-
.../org/apache/doris/common/util/TimeUtils.java | 35 ++++
.../org/apache/doris/datasource/CatalogIf.java | 23 +++
.../doris/datasource/hive/HMSExternalCatalog.java | 92 +++++++++++
.../doris/datasource/hive/HMSExternalTable.java | 6 +
.../doris/nereids/parser/LogicalPlanBuilder.java | 7 +-
.../doris/nereids/rules/analysis/BindRelation.java | 24 ++-
.../functions/table/PartitionValues.java | 76 +++++++++
.../visitor/TableValuedFunctionVisitor.java | 5 +
.../apache/doris/nereids/util/RelationUtil.java | 6 +-
.../doris/tablefunction/MetadataGenerator.java | 143 ++++++++++++++--
.../PartitionValuesTableValuedFunction.java | 180 +++++++++++++++++++++
.../PartitionsTableValuedFunction.java | 2 +-
.../doris/tablefunction/TableValuedFunctionIf.java | 2 +
.../apache/doris/common/util/TimeUtilsTest.java | 51 ++++++
.../nereids/rules/analysis/BindRelationTest.java | 9 ++
gensrc/thrift/Data.thrift | 1 +
gensrc/thrift/FrontendService.thrift | 1 +
gensrc/thrift/PlanNodes.thrift | 8 +
gensrc/thrift/Types.thrift | 3 +-
.../hive/test_hive_partition_values_tvf.out | 120 ++++++++++++++
.../hive/test_hive_partition_values_tvf.groovy | 144 +++++++++++++++++
28 files changed, 1062 insertions(+), 96 deletions(-)
diff --git a/be/src/vec/exec/scan/vmeta_scanner.cpp
b/be/src/vec/exec/scan/vmeta_scanner.cpp
index 02cf15e1af3..64981d0f483 100644
--- a/be/src/vec/exec/scan/vmeta_scanner.cpp
+++ b/be/src/vec/exec/scan/vmeta_scanner.cpp
@@ -85,16 +85,6 @@ Status VMetaScanner::prepare(RuntimeState* state, const
VExprContextSPtrs& conju
VLOG_CRITICAL << "VMetaScanner::prepare";
RETURN_IF_ERROR(VScanner::prepare(_state, conjuncts));
_tuple_desc = state->desc_tbl().get_tuple_descriptor(_tuple_id);
- bool has_col_nullable =
- std::any_of(std::begin(_tuple_desc->slots()),
std::end(_tuple_desc->slots()),
- [](SlotDescriptor* slot_desc) { return
slot_desc->is_nullable(); });
-
- if (has_col_nullable) {
- // We do not allow any columns to be Nullable here, since FE can not
- // transmit a NULL value to BE, so we can not distinguish a empty
string
- // from a NULL value.
- return Status::InternalError("Logical error, VMetaScanner do not allow
ColumnNullable");
- }
return Status::OK();
}
@@ -157,60 +147,90 @@ Status VMetaScanner::_fill_block_with_remote_data(const
std::vector<MutableColum
}
for (int _row_idx = 0; _row_idx < _batch_data.size(); _row_idx++) {
- // No need to check nullable column since no nullable column is
- // guaranteed in VMetaScanner::prepare
vectorized::IColumn* col_ptr = columns[col_idx].get();
- switch (slot_desc->type().type) {
- case TYPE_BOOLEAN: {
- bool data =
_batch_data[_row_idx].column_value[col_idx].boolVal;
-
reinterpret_cast<vectorized::ColumnVector<vectorized::UInt8>*>(col_ptr)
- ->insert_value((uint8_t)data);
- break;
- }
- case TYPE_INT: {
- int64_t data =
_batch_data[_row_idx].column_value[col_idx].intVal;
-
reinterpret_cast<vectorized::ColumnVector<vectorized::Int32>*>(col_ptr)
- ->insert_value(data);
- break;
- }
- case TYPE_BIGINT: {
- int64_t data =
_batch_data[_row_idx].column_value[col_idx].longVal;
-
reinterpret_cast<vectorized::ColumnVector<vectorized::Int64>*>(col_ptr)
- ->insert_value(data);
- break;
- }
- case TYPE_FLOAT: {
- double data =
_batch_data[_row_idx].column_value[col_idx].doubleVal;
-
reinterpret_cast<vectorized::ColumnVector<vectorized::Float32>*>(col_ptr)
- ->insert_value(data);
- break;
- }
- case TYPE_DOUBLE: {
- double data =
_batch_data[_row_idx].column_value[col_idx].doubleVal;
-
reinterpret_cast<vectorized::ColumnVector<vectorized::Float64>*>(col_ptr)
- ->insert_value(data);
- break;
- }
- case TYPE_DATETIMEV2: {
- uint64_t data =
_batch_data[_row_idx].column_value[col_idx].longVal;
-
reinterpret_cast<vectorized::ColumnVector<vectorized::UInt64>*>(col_ptr)
- ->insert_value(data);
- break;
- }
- case TYPE_STRING:
- case TYPE_CHAR:
- case TYPE_VARCHAR: {
- std::string data =
_batch_data[_row_idx].column_value[col_idx].stringVal;
-
reinterpret_cast<vectorized::ColumnString*>(col_ptr)->insert_data(data.c_str(),
-
data.length());
- break;
- }
- default: {
- std::string error_msg =
- fmt::format("Invalid column type {} on column: {}.",
- slot_desc->type().debug_string(),
slot_desc->col_name());
- return Status::InternalError(std::string(error_msg));
- }
+ TCell& cell = _batch_data[_row_idx].column_value[col_idx];
+ if (cell.__isset.isNull && cell.isNull) {
+ DCHECK(slot_desc->is_nullable())
+ << "cell is null but column is not nullable: " <<
slot_desc->col_name();
+ auto& null_col = reinterpret_cast<ColumnNullable&>(*col_ptr);
+ null_col.get_nested_column().insert_default();
+ null_col.get_null_map_data().push_back(1);
+ } else {
+ if (slot_desc->is_nullable()) {
+ auto& null_col =
reinterpret_cast<ColumnNullable&>(*col_ptr);
+ null_col.get_null_map_data().push_back(0);
+ col_ptr = null_col.get_nested_column_ptr();
+ }
+ switch (slot_desc->type().type) {
+ case TYPE_BOOLEAN: {
+ bool data = cell.boolVal;
+
reinterpret_cast<vectorized::ColumnVector<vectorized::UInt8>*>(col_ptr)
+ ->insert_value((uint8_t)data);
+ break;
+ }
+ case TYPE_TINYINT: {
+ int8_t data = (int8_t)cell.intVal;
+
reinterpret_cast<vectorized::ColumnVector<vectorized::Int8>*>(col_ptr)
+ ->insert_value(data);
+ break;
+ }
+ case TYPE_SMALLINT: {
+ int16_t data = (int16_t)cell.intVal;
+
reinterpret_cast<vectorized::ColumnVector<vectorized::Int16>*>(col_ptr)
+ ->insert_value(data);
+ break;
+ }
+ case TYPE_INT: {
+ int32_t data = cell.intVal;
+
reinterpret_cast<vectorized::ColumnVector<vectorized::Int32>*>(col_ptr)
+ ->insert_value(data);
+ break;
+ }
+ case TYPE_BIGINT: {
+ int64_t data = cell.longVal;
+
reinterpret_cast<vectorized::ColumnVector<vectorized::Int64>*>(col_ptr)
+ ->insert_value(data);
+ break;
+ }
+ case TYPE_FLOAT: {
+ double data = cell.doubleVal;
+
reinterpret_cast<vectorized::ColumnVector<vectorized::Float32>*>(col_ptr)
+ ->insert_value(data);
+ break;
+ }
+ case TYPE_DOUBLE: {
+ double data = cell.doubleVal;
+
reinterpret_cast<vectorized::ColumnVector<vectorized::Float64>*>(col_ptr)
+ ->insert_value(data);
+ break;
+ }
+ case TYPE_DATEV2: {
+ uint32_t data = (uint32_t)cell.longVal;
+
reinterpret_cast<vectorized::ColumnVector<vectorized::UInt32>*>(col_ptr)
+ ->insert_value(data);
+ break;
+ }
+ case TYPE_DATETIMEV2: {
+ uint64_t data = cell.longVal;
+
reinterpret_cast<vectorized::ColumnVector<vectorized::UInt64>*>(col_ptr)
+ ->insert_value(data);
+ break;
+ }
+ case TYPE_STRING:
+ case TYPE_CHAR:
+ case TYPE_VARCHAR: {
+ std::string data = cell.stringVal;
+
reinterpret_cast<vectorized::ColumnString*>(col_ptr)->insert_data(
+ data.c_str(), data.length());
+ break;
+ }
+ default: {
+ std::string error_msg =
+ fmt::format("Invalid column type {} on column:
{}.",
+ slot_desc->type().debug_string(),
slot_desc->col_name());
+ return Status::InternalError(std::string(error_msg));
+ }
+ }
}
}
}
@@ -252,6 +272,9 @@ Status VMetaScanner::_fetch_metadata(const TMetaScanRange&
meta_scan_range) {
case TMetadataType::TASKS:
RETURN_IF_ERROR(_build_tasks_metadata_request(meta_scan_range,
&request));
break;
+ case TMetadataType::PARTITION_VALUES:
+
RETURN_IF_ERROR(_build_partition_values_metadata_request(meta_scan_range,
&request));
+ break;
default:
_meta_eos = true;
return Status::OK();
@@ -472,6 +495,27 @@ Status VMetaScanner::_build_tasks_metadata_request(const
TMetaScanRange& meta_sc
return Status::OK();
}
+Status VMetaScanner::_build_partition_values_metadata_request(
+ const TMetaScanRange& meta_scan_range, TFetchSchemaTableDataRequest*
request) {
+ VLOG_CRITICAL << "VMetaScanner::_build_partition_values_metadata_request";
+ if (!meta_scan_range.__isset.partition_values_params) {
+ return Status::InternalError(
+ "Can not find TPartitionValuesMetadataParams 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::PARTITION_VALUES);
+ metadata_table_params.__set_partition_values_metadata_params(
+ meta_scan_range.partition_values_params);
+
+ request->__set_metada_table_params(metadata_table_params);
+ return Status::OK();
+}
+
Status VMetaScanner::close(RuntimeState* state) {
VLOG_CRITICAL << "VMetaScanner::close";
RETURN_IF_ERROR(VScanner::close(state));
diff --git a/be/src/vec/exec/scan/vmeta_scanner.h
b/be/src/vec/exec/scan/vmeta_scanner.h
index b44511826f2..600624ee636 100644
--- a/be/src/vec/exec/scan/vmeta_scanner.h
+++ b/be/src/vec/exec/scan/vmeta_scanner.h
@@ -93,6 +93,8 @@ private:
TFetchSchemaTableDataRequest*
request);
Status _build_queries_metadata_request(const TMetaScanRange&
meta_scan_range,
TFetchSchemaTableDataRequest*
request);
+ Status _build_partition_values_metadata_request(const TMetaScanRange&
meta_scan_range,
+
TFetchSchemaTableDataRequest* request);
bool _meta_eos;
TupleId _tuple_id;
TUserIdentity _user_identity;
diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
index a0e3a4582d9..9c78ab4e997 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
@@ -538,8 +538,8 @@ relationPrimary
| LEFT_PAREN query RIGHT_PAREN tableAlias lateralView*
#aliasedQuery
| tvfName=identifier LEFT_PAREN
(properties=propertyItemList)?
- RIGHT_PAREN tableAlias
#tableValuedFunction
- | LEFT_PAREN relations RIGHT_PAREN
#relationList
+ RIGHT_PAREN tableAlias
#tableValuedFunction
+ | LEFT_PAREN relations RIGHT_PAREN
#relationList
;
materializedViewName
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/analysis/DescribeStmt.java
b/fe/fe-core/src/main/java/org/apache/doris/analysis/DescribeStmt.java
index 45883fd6f46..67155c8591e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/DescribeStmt.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/DescribeStmt.java
@@ -32,6 +32,7 @@ import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.ErrorCode;
import org.apache.doris.common.ErrorReport;
import org.apache.doris.common.FeConstants;
+import org.apache.doris.common.Pair;
import org.apache.doris.common.UserException;
import org.apache.doris.common.proc.IndexSchemaProcNode;
import org.apache.doris.common.proc.ProcNodeInterface;
@@ -44,6 +45,7 @@ import org.apache.doris.qe.ConnectContext;
import org.apache.doris.qe.ShowResultSetMetaData;
import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
@@ -55,6 +57,7 @@ import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
public class DescribeStmt extends ShowStmt {
@@ -123,6 +126,25 @@ public class DescribeStmt extends ShowStmt {
@Override
public void analyze(Analyzer analyzer) throws UserException {
+ // First handle meta table.
+ // It will convert this to corresponding table valued functions
+ // eg: DESC table$partitions -> partition_values(...)
+ if (dbTableName != null) {
+ dbTableName.analyze(analyzer);
+ CatalogIf catalog =
Env.getCurrentEnv().getCatalogMgr().getCatalogOrAnalysisException(dbTableName.getCtl());
+ Pair<String, String> sourceTableNameWithMetaName =
catalog.getSourceTableNameWithMetaTableName(
+ dbTableName.getTbl());
+ if (!Strings.isNullOrEmpty(sourceTableNameWithMetaName.second)) {
+ isTableValuedFunction = true;
+ Optional<TableValuedFunctionRef> optTvfRef =
catalog.getMetaTableFunctionRef(
+ dbTableName.getDb(), dbTableName.getTbl());
+ if (!optTvfRef.isPresent()) {
+ throw new AnalysisException("meta table not found: " +
sourceTableNameWithMetaName.second);
+ }
+ tableValuedFunctionRef = optTvfRef.get();
+ }
+ }
+
if (!isAllTables && isTableValuedFunction) {
tableValuedFunctionRef.analyze(analyzer);
List<Column> columns =
tableValuedFunctionRef.getTable().getBaseSchema();
@@ -148,8 +170,6 @@ public class DescribeStmt extends ShowStmt {
}
}
- dbTableName.analyze(analyzer);
-
if (!Env.getCurrentEnv().getAccessManager()
.checkTblPriv(ConnectContext.get(), dbTableName,
PrivPredicate.SHOW)) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_TABLEACCESS_DENIED_ERROR,
"DESCRIBE",
@@ -159,8 +179,7 @@ public class DescribeStmt extends ShowStmt {
CatalogIf catalog =
Env.getCurrentEnv().getCatalogMgr().getCatalogOrAnalysisException(dbTableName.getCtl());
DatabaseIf db = catalog.getDbOrAnalysisException(dbTableName.getDb());
- TableIf table = db.getTableOrAnalysisException(dbTableName.getTbl());
-
+ TableIf table = db.getTableOrDdlException(dbTableName.getTbl());
table.readLock();
try {
if (!isAllTables) {
@@ -387,3 +406,4 @@ public class DescribeStmt extends ShowStmt {
return emptyRow;
}
}
+
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 db66c260e56..88c98162093 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.PartitionValues;
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;
@@ -59,7 +60,8 @@ public class BuiltinTableValuedFunctions implements
FunctionHelper {
tableValued(Partitions.class, "partitions"),
tableValued(Jobs.class, "jobs"),
tableValued(Tasks.class, "tasks"),
- tableValued(Query.class, "query")
+ tableValued(Query.class, "query"),
+ tableValued(PartitionValues.class, "partition_values")
);
public static final BuiltinTableValuedFunctions INSTANCE = new
BuiltinTableValuedFunctions();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Table.java
b/fe/fe-core/src/main/java/org/apache/doris/catalog/Table.java
index 8cfe52356cf..862d6c1878e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Table.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Table.java
@@ -500,12 +500,6 @@ public abstract class Table extends MetaObject implements
Writable, TableIf {
this.createTime = in.readLong();
}
- // return if this table is partitioned.
- // For OlapTable, return true only if its partition type is RANGE or HASH
- public boolean isPartitionedTable() {
- return false;
- }
-
// return if this table is partitioned, for planner.
// For OlapTable ture when is partitioned, or distributed by hash when no
partition
public boolean isPartitionDistributed() {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/TableIf.java
b/fe/fe-core/src/main/java/org/apache/doris/catalog/TableIf.java
index be8f1fe6f94..d42a32ef8d2 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/TableIf.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/TableIf.java
@@ -66,8 +66,6 @@ public interface TableIf {
default void readUnlock() {
}
- ;
-
default void writeLock() {
}
@@ -556,4 +554,8 @@ public interface TableIf {
default Set<String> getDistributionColumnNames() {
return Sets.newHashSet();
}
+
+ default boolean isPartitionedTable() {
+ return false;
+ }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/common/util/TimeUtils.java
b/fe/fe-core/src/main/java/org/apache/doris/common/util/TimeUtils.java
index 55b2ed1c58d..e7066846c30 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/common/util/TimeUtils.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/util/TimeUtils.java
@@ -340,4 +340,39 @@ public class TimeUtils {
parts.length > 3 ? String.format(" %02d:%02d:%02d",
Integer.parseInt(parts[3]),
Integer.parseInt(parts[4]),
Integer.parseInt(parts[5])) : "");
}
+
+
+ // Refer to be/src/vec/runtime/vdatetime_value.h
+ public static long convertToDateTimeV2(
+ int year, int month, int day, int hour, int minute, int second,
int microsecond) {
+ return (long) microsecond | (long) second << 20 | (long) minute << 26
| (long) hour << 32
+ | (long) day << 37 | (long) month << 42 | (long) year << 46;
+ }
+
+ // Refer to be/src/vec/runtime/vdatetime_value.h
+ public static long convertToDateV2(
+ int year, int month, int day) {
+ return (long) day | (long) month << 5 | (long) year << 9;
+ }
+
+ public static long convertStringToDateTimeV2(String dateTimeStr, int
scale) {
+ String format = "yyyy-MM-dd HH:mm:ss";
+ if (scale > 0) {
+ format += ".";
+ for (int i = 0; i < scale; i++) {
+ format += "S";
+ }
+ }
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
+ LocalDateTime dateTime =
TimeUtils.formatDateTimeAndFullZero(dateTimeStr, formatter);
+ return convertToDateTimeV2(dateTime.getYear(),
dateTime.getMonthValue(), dateTime.getDayOfMonth(),
+ dateTime.getHour(), dateTime.getMinute(),
dateTime.getSecond(), dateTime.getNano() / 1000);
+ }
+
+ public static long convertStringToDateV2(String dateStr) {
+ DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ LocalDateTime dateTime = TimeUtils.formatDateTimeAndFullZero(dateStr,
formatter);
+ return convertToDateV2(dateTime.getYear(), dateTime.getMonthValue(),
dateTime.getDayOfMonth());
+ }
}
+
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogIf.java
b/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogIf.java
index a79897c67df..41cb44ef0b5 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogIf.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogIf.java
@@ -22,6 +22,7 @@ import org.apache.doris.analysis.CreateTableStmt;
import org.apache.doris.analysis.DropDbStmt;
import org.apache.doris.analysis.DropTableStmt;
import org.apache.doris.analysis.TableName;
+import org.apache.doris.analysis.TableValuedFunctionRef;
import org.apache.doris.analysis.TruncateTableStmt;
import org.apache.doris.catalog.DatabaseIf;
import org.apache.doris.catalog.Env;
@@ -30,7 +31,9 @@ import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.DdlException;
import org.apache.doris.common.ErrorCode;
import org.apache.doris.common.MetaNotFoundException;
+import org.apache.doris.common.Pair;
import org.apache.doris.common.UserException;
+import
org.apache.doris.nereids.trees.expressions.functions.table.TableValuedFunction;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
@@ -197,4 +200,24 @@ public interface CatalogIf<T extends DatabaseIf> {
void dropTable(DropTableStmt stmt) throws DdlException;
void truncateTable(TruncateTableStmt truncateTableStmt) throws
DdlException;
+
+ /**
+ * Try to parse meta table name from table name.
+ * Some catalog allow querying meta table like "table_name$partitions".
+ * Catalog can override this method to parse meta table name from table
name.
+ *
+ * @param tableName table name like "table_name" or "table_name$partitions"
+ * @return pair of source table name and meta table name
+ */
+ default Pair<String, String> getSourceTableNameWithMetaTableName(String
tableName) {
+ return Pair.of(tableName, "");
+ }
+
+ default Optional<TableValuedFunction> getMetaTableFunction(String dbName,
String sourceNameWithMetaName) {
+ return Optional.empty();
+ }
+
+ default Optional<TableValuedFunctionRef> getMetaTableFunctionRef(String
dbName, String sourceNameWithMetaName) {
+ return Optional.empty();
+ }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/datasource/hive/HMSExternalCatalog.java
b/fe/fe-core/src/main/java/org/apache/doris/datasource/hive/HMSExternalCatalog.java
index 5faf1f2bb6e..5173b414b8a 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/datasource/hive/HMSExternalCatalog.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/datasource/hive/HMSExternalCatalog.java
@@ -17,11 +17,13 @@
package org.apache.doris.datasource.hive;
+import org.apache.doris.analysis.TableValuedFunctionRef;
import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.HdfsResource;
import org.apache.doris.cluster.ClusterNamespace;
import org.apache.doris.common.Config;
import org.apache.doris.common.DdlException;
+import org.apache.doris.common.Pair;
import org.apache.doris.common.ThreadPoolManager;
import org.apache.doris.common.security.authentication.AuthenticationConfig;
import org.apache.doris.common.security.authentication.HadoopAuthenticator;
@@ -39,10 +41,15 @@ import
org.apache.doris.datasource.property.constants.HMSProperties;
import org.apache.doris.fs.FileSystemProvider;
import org.apache.doris.fs.FileSystemProviderImpl;
import org.apache.doris.fs.remote.dfs.DFSFileSystem;
+import org.apache.doris.nereids.exceptions.AnalysisException;
+import
org.apache.doris.nereids.trees.expressions.functions.table.PartitionValues;
+import
org.apache.doris.nereids.trees.expressions.functions.table.TableValuedFunction;
import org.apache.doris.transaction.TransactionManagerFactory;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
import lombok.Getter;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.hadoop.hive.conf.HiveConf;
@@ -52,6 +59,7 @@ import org.apache.logging.log4j.Logger;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.concurrent.ThreadPoolExecutor;
/**
@@ -277,6 +285,36 @@ public class HMSExternalCatalog extends ExternalCatalog {
}
}
+ @Override
+ public Pair<String, String> getSourceTableNameWithMetaTableName(String
tableName) {
+ for (MetaTableFunction metaFunction : MetaTableFunction.values()) {
+ if (metaFunction.containsMetaTable(tableName)) {
+ return Pair.of(metaFunction.getSourceTableName(tableName),
metaFunction.name().toLowerCase());
+ }
+ }
+ return Pair.of(tableName, "");
+ }
+
+ @Override
+ public Optional<TableValuedFunction> getMetaTableFunction(String dbName,
String sourceNameWithMetaName) {
+ for (MetaTableFunction metaFunction : MetaTableFunction.values()) {
+ if (metaFunction.containsMetaTable(sourceNameWithMetaName)) {
+ return Optional.of(metaFunction.createFunction(name, dbName,
sourceNameWithMetaName));
+ }
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional<TableValuedFunctionRef> getMetaTableFunctionRef(String
dbName, String sourceNameWithMetaName) {
+ for (MetaTableFunction metaFunction : MetaTableFunction.values()) {
+ if (metaFunction.containsMetaTable(sourceNameWithMetaName)) {
+ return Optional.of(metaFunction.createFunctionRef(name,
dbName, sourceNameWithMetaName));
+ }
+ }
+ return Optional.empty();
+ }
+
public String getHiveMetastoreUris() {
return catalogProperty.getOrDefault(HMSProperties.HIVE_METASTORE_URIS,
"");
}
@@ -292,4 +330,58 @@ public class HMSExternalCatalog extends ExternalCatalog {
public boolean isEnableHmsEventsIncrementalSync() {
return enableHmsEventsIncrementalSync;
}
+
+ /**
+ * Enum for meta tables in hive catalog.
+ * eg: tbl$partitions
+ */
+ private enum MetaTableFunction {
+ PARTITIONS("partition_values");
+
+ private final String suffix;
+ private final String tvfName;
+
+ MetaTableFunction(String tvfName) {
+ this.suffix = "$" + name().toLowerCase();
+ this.tvfName = tvfName;
+ }
+
+ boolean containsMetaTable(String tableName) {
+ return tableName.endsWith(suffix) && (tableName.length() >
suffix.length());
+ }
+
+ String getSourceTableName(String tableName) {
+ return tableName.substring(0, tableName.length() -
suffix.length());
+ }
+
+ public TableValuedFunction createFunction(String ctlName, String
dbName, String sourceNameWithMetaName) {
+ switch (this) {
+ case PARTITIONS:
+ List<String> nameParts = Lists.newArrayList(ctlName,
dbName,
+ getSourceTableName(sourceNameWithMetaName));
+ return PartitionValues.create(nameParts);
+ default:
+ throw new AnalysisException("Unsupported meta function
type: " + this);
+ }
+ }
+
+ public TableValuedFunctionRef createFunctionRef(String ctlName, String
dbName, String sourceNameWithMetaName) {
+ switch (this) {
+ case PARTITIONS:
+ Map<String, String> params = Maps.newHashMap();
+ params.put("catalog", ctlName);
+ params.put("database", dbName);
+ params.put("table",
getSourceTableName(sourceNameWithMetaName));
+ try {
+ return new TableValuedFunctionRef(tvfName, null,
params);
+ } catch (org.apache.doris.common.AnalysisException e) {
+ LOG.warn("should not happen. {}.{}.{}", ctlName,
dbName, sourceNameWithMetaName);
+ return null;
+ }
+ default:
+ throw new AnalysisException("Unsupported meta function
type: " + this);
+ }
+ }
+ }
}
+
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/datasource/hive/HMSExternalTable.java
b/fe/fe-core/src/main/java/org/apache/doris/datasource/hive/HMSExternalTable.java
index 52070953279..a486e286c6e 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/datasource/hive/HMSExternalTable.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/datasource/hive/HMSExternalTable.java
@@ -920,4 +920,10 @@ public class HMSExternalTable extends ExternalTable
implements MTMVRelatedTableI
String bindBrokerName = catalog.bindBrokerName();
return cache.getFilesByPartitionsWithoutCache(hivePartitions,
bindBrokerName);
}
+
+ @Override
+ public boolean isPartitionedTable() {
+ makeSureInitialized();
+ return !isView() && remoteTable.getPartitionKeysSize() > 0;
+ }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index fae6ddb137b..61521bd7681 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -1377,7 +1377,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
@Override
public LogicalPlan visitTableName(TableNameContext ctx) {
- List<String> tableId =
visitMultipartIdentifier(ctx.multipartIdentifier());
+ List<String> nameParts =
visitMultipartIdentifier(ctx.multipartIdentifier());
List<String> partitionNames = new ArrayList<>();
boolean isTempPart = false;
if (ctx.specifiedPartition() != null) {
@@ -1425,8 +1425,9 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
TableSample tableSample = ctx.sample() == null ? null : (TableSample)
visit(ctx.sample());
UnboundRelation relation = new
UnboundRelation(StatementScopeIdGenerator.newRelationId(),
- tableId, partitionNames, isTempPart, tabletIdLists,
relationHints,
- Optional.ofNullable(tableSample), indexName,
scanParams, Optional.ofNullable(tableSnapshot));
+ nameParts, partitionNames, isTempPart, tabletIdLists,
relationHints,
+ Optional.ofNullable(tableSample), indexName, scanParams,
Optional.ofNullable(tableSnapshot));
+
LogicalPlan checkedRelation =
LogicalPlanBuilderAssistant.withCheckPolicy(relation);
LogicalPlan plan = withTableAlias(checkedRelation, ctx.tableAlias());
for (LateralViewContext lateralViewContext : ctx.lateralView()) {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindRelation.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindRelation.java
index 4e18039c4b1..b418a33bae0 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindRelation.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindRelation.java
@@ -65,6 +65,7 @@ import
org.apache.doris.nereids.trees.expressions.functions.agg.Max;
import org.apache.doris.nereids.trees.expressions.functions.agg.Min;
import org.apache.doris.nereids.trees.expressions.functions.agg.QuantileUnion;
import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
+import
org.apache.doris.nereids.trees.expressions.functions.table.TableValuedFunction;
import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.PreAggStatus;
@@ -81,6 +82,7 @@ import
org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
import org.apache.doris.nereids.trees.plans.logical.LogicalSchemaScan;
import org.apache.doris.nereids.trees.plans.logical.LogicalSubQueryAlias;
+import org.apache.doris.nereids.trees.plans.logical.LogicalTVFRelation;
import org.apache.doris.nereids.trees.plans.logical.LogicalTestScan;
import org.apache.doris.nereids.trees.plans.logical.LogicalView;
import org.apache.doris.nereids.util.RelationUtil;
@@ -358,14 +360,32 @@ public class BindRelation extends OneAnalysisRuleFactory {
}
}
+ private Optional<LogicalPlan> handleMetaTable(TableIf table,
UnboundRelation unboundRelation,
+ List<String> qualifiedTableName) {
+ Optional<TableValuedFunction> tvf =
table.getDatabase().getCatalog().getMetaTableFunction(
+ qualifiedTableName.get(1), qualifiedTableName.get(2));
+ if (tvf.isPresent()) {
+ return Optional.of(new
LogicalTVFRelation(unboundRelation.getRelationId(), tvf.get()));
+ }
+ return Optional.empty();
+ }
+
private LogicalPlan getLogicalPlan(TableIf table, UnboundRelation
unboundRelation,
- List<String>
qualifiedTableName, CascadesContext cascadesContext) {
- // for create view stmt replace tablNname to ctl.db.tableName
+ List<String> qualifiedTableName,
CascadesContext cascadesContext) {
+ // for create view stmt replace tableName to ctl.db.tableName
unboundRelation.getIndexInSqlString().ifPresent(pair -> {
StatementContext statementContext =
cascadesContext.getStatementContext();
statementContext.addIndexInSqlToString(pair,
Utils.qualifiedNameWithBackquote(qualifiedTableName));
});
+
+ // Handle meta table like "table_name$partitions"
+ // qualifiedTableName should be like "ctl.db.tbl$partitions"
+ Optional<LogicalPlan> logicalPlan = handleMetaTable(table,
unboundRelation, qualifiedTableName);
+ if (logicalPlan.isPresent()) {
+ return logicalPlan.get();
+ }
+
List<String> qualifierWithoutTableName = Lists.newArrayList();
qualifierWithoutTableName.addAll(qualifiedTableName.subList(0,
qualifiedTableName.size() - 1));
boolean isView = false;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/PartitionValues.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/PartitionValues.java
new file mode 100644
index 00000000000..0389bb62637
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/PartitionValues.java
@@ -0,0 +1,76 @@
+// 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.PartitionValuesTableValuedFunction;
+import org.apache.doris.tablefunction.TableValuedFunctionIf;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * partition_values
+ */
+public class PartitionValues extends TableValuedFunction {
+ public PartitionValues(Properties properties) {
+ super("partition_values", properties);
+ }
+
+ /**
+ * Create PartitionValues function.
+ * @param qualifiedTableName ctl.db.tbl
+ * @return PartitionValues function
+ */
+ public static TableValuedFunction create(List<String> qualifiedTableName) {
+ Preconditions.checkArgument(qualifiedTableName != null &&
qualifiedTableName.size() == 3);
+ Map<String, String> parameters = Maps.newHashMap();
+ parameters.put(PartitionValuesTableValuedFunction.CATALOG,
qualifiedTableName.get(0));
+ parameters.put(PartitionValuesTableValuedFunction.DB,
qualifiedTableName.get(1));
+ parameters.put(PartitionValuesTableValuedFunction.TABLE,
qualifiedTableName.get(2));
+ return new PartitionValues(new Properties(parameters));
+ }
+
+ @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 PartitionValuesTableValuedFunction(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.visitPartitionValues(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 ca14edd87de..0b4b57e11dc 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.PartitionValues;
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;
@@ -59,6 +60,10 @@ public interface TableValuedFunctionVisitor<R, C> {
return visitTableValuedFunction(partitions, context);
}
+ default R visitPartitionValues(PartitionValues partitionValues, C context)
{
+ return visitTableValuedFunction(partitionValues, context);
+ }
+
default R visitJobs(Jobs jobs, C context) {
return visitTableValuedFunction(jobs, context);
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/RelationUtil.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/RelationUtil.java
index b145338ff81..b72498a2227 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/RelationUtil.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/RelationUtil.java
@@ -100,8 +100,10 @@ public class RelationUtil {
try {
DatabaseIf<TableIf> db = catalog.getDb(dbName).orElseThrow(() ->
new AnalysisException(
"Database [" + dbName + "] does not exist."));
- TableIf table = db.getTable(tableName).orElseThrow(() -> new
AnalysisException(
- "Table [" + tableName + "] does not exist in database [" +
dbName + "]."));
+ Pair<String, String> sourceTblNameWithMetaTblName =
catalog.getSourceTableNameWithMetaTableName(tableName);
+ String sourceTableName = sourceTblNameWithMetaTblName.first;
+ TableIf table = db.getTable(sourceTableName).orElseThrow(() -> new
AnalysisException(
+ "Table [" + sourceTableName + "] does not exist in
database [" + dbName + "]."));
return Pair.of(db, table);
} catch (Throwable e) {
throw new AnalysisException(e.getMessage(), e.getCause());
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 746519082ae..ac0443bbf52 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
@@ -30,10 +30,13 @@ import org.apache.doris.catalog.OlapTable;
import org.apache.doris.catalog.Partition;
import org.apache.doris.catalog.PartitionItem;
import org.apache.doris.catalog.PartitionType;
+import org.apache.doris.catalog.ScalarType;
import org.apache.doris.catalog.SchemaTable;
import org.apache.doris.catalog.Table;
import org.apache.doris.catalog.TableIf;
+import org.apache.doris.catalog.TableIf.TableType;
import org.apache.doris.catalog.TableProperty;
+import org.apache.doris.catalog.Type;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.ClientPool;
import org.apache.doris.common.Pair;
@@ -42,11 +45,14 @@ 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.common.util.Util;
import org.apache.doris.datasource.CatalogIf;
import org.apache.doris.datasource.ExternalCatalog;
import org.apache.doris.datasource.ExternalMetaCacheMgr;
import org.apache.doris.datasource.InternalCatalog;
+import org.apache.doris.datasource.TablePartitionValues;
import org.apache.doris.datasource.hive.HMSExternalCatalog;
+import org.apache.doris.datasource.hive.HMSExternalTable;
import org.apache.doris.datasource.hive.HiveMetaStoreCache;
import org.apache.doris.datasource.hudi.source.HudiCachedPartitionProcessor;
import org.apache.doris.datasource.iceberg.IcebergExternalCatalog;
@@ -78,6 +84,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.TPartitionValuesMetadataParams;
import org.apache.doris.thrift.TPartitionsMetadataParams;
import org.apache.doris.thrift.TPipelineWorkloadGroup;
import org.apache.doris.thrift.TRow;
@@ -204,7 +211,8 @@ public class MetadataGenerator {
}
TFetchSchemaTableDataResult result;
TMetadataTableRequestParams params = request.getMetadaTableParams();
- switch (request.getMetadaTableParams().getMetadataType()) {
+ TMetadataType metadataType =
request.getMetadaTableParams().getMetadataType();
+ switch (metadataType) {
case ICEBERG:
result = icebergMetadataResult(params);
break;
@@ -232,11 +240,17 @@ public class MetadataGenerator {
case TASKS:
result = taskMetadataResult(params);
break;
+ case PARTITION_VALUES:
+ result = partitionValuesMetadataResult(params);
+ break;
default:
return errorResult("Metadata table params is not set.");
}
if (result.getStatus().getStatusCode() == TStatusCode.OK) {
- filterColumns(result, params.getColumnsName(),
params.getMetadataType(), params);
+ if (metadataType != TMetadataType.PARTITION_VALUES) {
+ // partition_values' result already sorted by column names
+ filterColumns(result, params.getColumnsName(),
params.getMetadataType(), params);
+ }
}
if (LOG.isDebugEnabled()) {
LOG.debug("getMetadataTable() end.");
@@ -329,7 +343,8 @@ public class MetadataGenerator {
TRow trow = new TRow();
LocalDateTime committedAt =
LocalDateTime.ofInstant(Instant.ofEpochMilli(
snapshot.timestampMillis()),
TimeUtils.getTimeZone().toZoneId());
- long encodedDatetime =
convertToDateTimeV2(committedAt.getYear(), committedAt.getMonthValue(),
+ long encodedDatetime =
TimeUtils.convertToDateTimeV2(committedAt.getYear(),
+ committedAt.getMonthValue(),
committedAt.getDayOfMonth(),
committedAt.getHour(), committedAt.getMinute(),
committedAt.getSecond(), committedAt.getNano() /
1000);
@@ -785,12 +800,6 @@ public class MetadataGenerator {
result.setDataBatch(filterColumnsRows);
}
- private static long convertToDateTimeV2(
- int year, int month, int day, int hour, int minute, int second,
int microsecond) {
- return (long) microsecond | (long) second << 20 | (long) minute << 26
| (long) hour << 32
- | (long) day << 37 | (long) month << 42 | (long) year << 46;
- }
-
private static TFetchSchemaTableDataResult
mtmvMetadataResult(TMetadataTableRequestParams params) {
if (LOG.isDebugEnabled()) {
LOG.debug("mtmvMetadataResult() start");
@@ -1477,4 +1486,120 @@ public class MetadataGenerator {
}
}
}
+
+ private static TFetchSchemaTableDataResult
partitionValuesMetadataResult(TMetadataTableRequestParams params) {
+ if (!params.isSetPartitionValuesMetadataParams()) {
+ return errorResult("partition values metadata params is not set.");
+ }
+
+ TPartitionValuesMetadataParams metaParams =
params.getPartitionValuesMetadataParams();
+ String ctlName = metaParams.getCatalog();
+ String dbName = metaParams.getDatabase();
+ String tblName = metaParams.getTable();
+ List<TRow> dataBatch;
+ try {
+ TableIf table =
PartitionValuesTableValuedFunction.analyzeAndGetTable(ctlName, dbName, tblName,
false);
+ TableType tableType = table.getType();
+ switch (tableType) {
+ case HMS_EXTERNAL_TABLE:
+ dataBatch =
partitionValuesMetadataResultForHmsTable((HMSExternalTable) table,
+ params.getColumnsName());
+ break;
+ default:
+ return errorResult("not support table type " +
tableType.name());
+ }
+ TFetchSchemaTableDataResult result = new
TFetchSchemaTableDataResult();
+ result.setDataBatch(dataBatch);
+ result.setStatus(new TStatus(TStatusCode.OK));
+ return result;
+ } catch (Throwable t) {
+ LOG.warn("error when get partition values metadata. {}.{}.{}",
ctlName, dbName, tblName, t);
+ return errorResult("error when get partition values metadata: " +
Util.getRootCauseMessage(t));
+ }
+ }
+
+ private static List<TRow>
partitionValuesMetadataResultForHmsTable(HMSExternalTable tbl, List<String>
colNames)
+ throws AnalysisException {
+ List<Column> partitionCols = tbl.getPartitionColumns();
+ List<Integer> colIdxs = Lists.newArrayList();
+ List<Type> types = Lists.newArrayList();
+ for (String colName : colNames) {
+ for (int i = 0; i < partitionCols.size(); ++i) {
+ if (partitionCols.get(i).getName().equalsIgnoreCase(colName)) {
+ colIdxs.add(i);
+ types.add(partitionCols.get(i).getType());
+ }
+ }
+ }
+ if (colIdxs.size() != colNames.size()) {
+ throw new AnalysisException(
+ "column " + colNames + " does not match partition columns
of table " + tbl.getName());
+ }
+
+ HiveMetaStoreCache cache = Env.getCurrentEnv().getExtMetaCacheMgr()
+ .getMetaStoreCache((HMSExternalCatalog) tbl.getCatalog());
+ HiveMetaStoreCache.HivePartitionValues hivePartitionValues =
cache.getPartitionValues(
+ tbl.getDbName(), tbl.getName(), tbl.getPartitionColumnTypes());
+ Map<Long, List<String>> valuesMap =
hivePartitionValues.getPartitionValuesMap();
+ List<TRow> dataBatch = Lists.newArrayList();
+ for (Map.Entry<Long, List<String>> entry : valuesMap.entrySet()) {
+ TRow trow = new TRow();
+ List<String> values = entry.getValue();
+ if (values.size() != partitionCols.size()) {
+ continue;
+ }
+
+ for (int i = 0; i < colIdxs.size(); ++i) {
+ int idx = colIdxs.get(i);
+ String partitionValue = values.get(idx);
+ if (partitionValue == null ||
partitionValue.equals(TablePartitionValues.HIVE_DEFAULT_PARTITION)) {
+ trow.addToColumnValue(new TCell().setIsNull(true));
+ } else {
+ Type type = types.get(i);
+ switch (type.getPrimitiveType()) {
+ case BOOLEAN:
+ trow.addToColumnValue(new
TCell().setBoolVal(Boolean.valueOf(partitionValue)));
+ break;
+ case TINYINT:
+ case SMALLINT:
+ case INT:
+ trow.addToColumnValue(new
TCell().setIntVal(Integer.valueOf(partitionValue)));
+ break;
+ case BIGINT:
+ trow.addToColumnValue(new
TCell().setLongVal(Long.valueOf(partitionValue)));
+ break;
+ case FLOAT:
+ trow.addToColumnValue(new
TCell().setDoubleVal(Float.valueOf(partitionValue)));
+ break;
+ case DOUBLE:
+ trow.addToColumnValue(new
TCell().setDoubleVal(Double.valueOf(partitionValue)));
+ break;
+ case VARCHAR:
+ case CHAR:
+ case STRING:
+ trow.addToColumnValue(new
TCell().setStringVal(partitionValue));
+ break;
+ case DATE:
+ case DATEV2:
+ trow.addToColumnValue(
+ new
TCell().setLongVal(TimeUtils.convertStringToDateV2(partitionValue)));
+ break;
+ case DATETIME:
+ case DATETIMEV2:
+ trow.addToColumnValue(
+ new
TCell().setLongVal(TimeUtils.convertStringToDateTimeV2(partitionValue,
+ ((ScalarType)
type).getScalarScale())));
+ break;
+ default:
+ throw new AnalysisException(
+ "Unsupported partition column type for
$partitions sys table " + type);
+ }
+ }
+ }
+ dataBatch.add(trow);
+ }
+ return dataBatch;
+ }
+
}
+
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/tablefunction/PartitionValuesTableValuedFunction.java
b/fe/fe-core/src/main/java/org/apache/doris/tablefunction/PartitionValuesTableValuedFunction.java
new file mode 100644
index 00000000000..59efe9fcefd
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/tablefunction/PartitionValuesTableValuedFunction.java
@@ -0,0 +1,180 @@
+// 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.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.hive.HMSExternalCatalog;
+import org.apache.doris.datasource.hive.HMSExternalTable;
+import org.apache.doris.datasource.maxcompute.MaxComputeExternalCatalog;
+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.TMetadataType;
+import org.apache.doris.thrift.TPartitionValuesMetadataParams;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+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
+ * partition_values("catalog"="ctl1", "database" = "db1","table" = "table1").
+ */
+public class PartitionValuesTableValuedFunction extends
MetadataTableValuedFunction {
+ private static final Logger LOG =
LogManager.getLogger(PartitionValuesTableValuedFunction.class);
+
+ public static final String NAME = "partition_values";
+
+ public static final String CATALOG = "catalog";
+ public static final String DB = "database";
+ public static final String TABLE = "table";
+
+ private static final ImmutableSet<String> PROPERTIES_SET =
ImmutableSet.of(CATALOG, DB, TABLE);
+
+ private final String catalogName;
+ private final String databaseName;
+ private final String tableName;
+ private TableIf table;
+ private List<Column> schema;
+
+ public PartitionValuesTableValuedFunction(Map<String, String> params)
throws AnalysisException {
+ 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("catalog, database and table are
required");
+ }
+ this.table = analyzeAndGetTable(catalogName, dbName, tableName, true);
+ this.catalogName = catalogName;
+ this.databaseName = dbName;
+ this.tableName = tableName;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("PartitionsTableValuedFunction() end");
+ }
+ }
+
+ public static TableIf analyzeAndGetTable(String catalogName, String
dbName, String tableName, boolean checkAuth) {
+ if (checkAuth) {
+ // This method will be called at 2 places:
+ // One is when planing the query, which should check the privilege
of the user.
+ // the other is when BE call FE to fetch partition values, which
should not check the privilege.
+ 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;
+ 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)) {
+ throw new AnalysisException("Currently only support hive table's
partition values meta table");
+ }
+ HMSExternalTable hmsTable = (HMSExternalTable) table;
+ if (!hmsTable.isPartitionedTable()) {
+ throw new AnalysisException("Table " + tableName + " is not a
partitioned table");
+ }
+ return table;
+ }
+
+ @Override
+ public TMetadataType getMetadataType() {
+ return TMetadataType.PARTITION_VALUES;
+ }
+
+ @Override
+ public TMetaScanRange getMetaScanRange() {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("getMetaScanRange() start");
+ }
+ TMetaScanRange metaScanRange = new TMetaScanRange();
+ metaScanRange.setMetadataType(TMetadataType.PARTITION_VALUES);
+ TPartitionValuesMetadataParams partitionParam = new
TPartitionValuesMetadataParams();
+ partitionParam.setCatalog(catalogName);
+ partitionParam.setDatabase(databaseName);
+ partitionParam.setTable(tableName);
+ metaScanRange.setPartitionValuesParams(partitionParam);
+ return metaScanRange;
+ }
+
+ @Override
+ public String getTableName() {
+ return "PartitionsValuesTableValuedFunction";
+ }
+
+ @Override
+ public List<Column> getTableColumns() throws AnalysisException {
+ Preconditions.checkNotNull(table);
+ // TODO: support other type of sys tables
+ if (schema == null) {
+ List<Column> partitionColumns = ((HMSExternalTable)
table).getPartitionColumns();
+ schema = Lists.newArrayList();
+ for (Column column : partitionColumns) {
+ schema.add(new Column(column));
+ }
+ }
+ return schema;
+ }
+}
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
index 1ceddeb89cf..00169ad555f 100644
---
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
@@ -55,7 +55,7 @@ import java.util.Optional;
/**
* The Implement of table valued function
- * partitions("database" = "db1","table" = "table1").
+ * partitions("catalog"="ctl1", "database" = "db1","table" = "table1").
*/
public class PartitionsTableValuedFunction extends MetadataTableValuedFunction
{
private static final Logger LOG =
LogManager.getLogger(PartitionsTableValuedFunction.class);
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 6b6fda088a8..d4faa460195 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
@@ -77,6 +77,8 @@ public abstract class TableValuedFunctionIf {
return new GroupCommitTableValuedFunction(params);
case QueryTableValueFunction.NAME:
return
QueryTableValueFunction.createQueryTableValueFunction(params);
+ case PartitionValuesTableValuedFunction.NAME:
+ return new PartitionValuesTableValuedFunction(params);
default:
throw new AnalysisException("Could not find table function " +
funcName);
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/common/util/TimeUtilsTest.java
b/fe/fe-core/src/test/java/org/apache/doris/common/util/TimeUtilsTest.java
index 114ddada9cd..f57e8fe2fe6 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/common/util/TimeUtilsTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/common/util/TimeUtilsTest.java
@@ -22,6 +22,7 @@ import org.apache.doris.catalog.PrimitiveType;
import org.apache.doris.catalog.ScalarType;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.DdlException;
+import org.apache.doris.common.ExceptionChecker;
import org.apache.doris.common.FeConstants;
import mockit.Expectations;
@@ -31,6 +32,7 @@ import org.junit.Before;
import org.junit.Test;
import java.time.ZoneId;
+import java.time.format.DateTimeParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
@@ -201,4 +203,53 @@ public class TimeUtilsTest {
Assert.assertNull(TimeUtils.getHourAsDate("111"));
Assert.assertNull(TimeUtils.getHourAsDate("-1"));
}
+
+ @Test
+ public void testConvertToBEDateType() {
+ long result = TimeUtils.convertStringToDateV2("2021-01-01");
+ Assert.assertEquals(1034785, result);
+ result = TimeUtils.convertStringToDateV2("1900-01-01");
+ Assert.assertEquals(972833, result);
+ result = TimeUtils.convertStringToDateV2("1899-12-31");
+ Assert.assertEquals(972703, result);
+ result = TimeUtils.convertStringToDateV2("9999-12-31");
+ Assert.assertEquals(5119903, result);
+
+ ExceptionChecker.expectThrows(DateTimeParseException.class, () ->
TimeUtils.convertStringToDateV2("2021-1-1"));
+ ExceptionChecker.expectThrows(DateTimeParseException.class, () ->
TimeUtils.convertStringToDateV2("1900-01-1"));
+ ExceptionChecker.expectThrows(DateTimeParseException.class, () ->
TimeUtils.convertStringToDateV2("20210101"));
+ ExceptionChecker.expectThrows(DateTimeParseException.class, () ->
TimeUtils.convertStringToDateV2(""));
+ ExceptionChecker.expectThrows(NullPointerException.class, () ->
TimeUtils.convertStringToDateV2(null));
+ ExceptionChecker.expectThrows(DateTimeParseException.class,
+ () -> TimeUtils.convertStringToDateV2("2024:12:31"));
+ }
+
+ @Test
+ public void testConvertToBEDatetimeV2Type() {
+ long result = TimeUtils.convertStringToDateTimeV2("2021-01-01
10:10:10", 0);
+ Assert.assertEquals(142219811099770880L, result);
+ result = TimeUtils.convertStringToDateTimeV2("1900-01-01 00:00:00.12",
2);
+ Assert.assertEquals(133705149423146176L, result);
+ result = TimeUtils.convertStringToDateTimeV2("1899-12-31
23:59:59.000", 3);
+ Assert.assertEquals(133687385164611584L, result);
+ result = TimeUtils.convertStringToDateTimeV2("9999-12-31
23:59:59.123456", 6);
+ Assert.assertEquals(703674213003812984L, result);
+
+ ExceptionChecker.expectThrows(DateTimeParseException.class,
+ () -> TimeUtils.convertStringToDateTimeV2("2021-1-1", 0));
+ ExceptionChecker.expectThrows(DateTimeParseException.class,
+ () -> TimeUtils.convertStringToDateTimeV2("1900-01-1", 0));
+ ExceptionChecker.expectThrows(DateTimeParseException.class,
+ () -> TimeUtils.convertStringToDateTimeV2("20210101", 0));
+ ExceptionChecker.expectThrows(DateTimeParseException.class, () ->
TimeUtils.convertStringToDateTimeV2("", 0));
+ ExceptionChecker.expectThrows(NullPointerException.class, () ->
TimeUtils.convertStringToDateTimeV2(null, 0));
+ ExceptionChecker.expectThrows(DateTimeParseException.class,
+ () -> TimeUtils.convertStringToDateTimeV2("2024-10-10", 0));
+ ExceptionChecker.expectThrows(DateTimeParseException.class,
+ () -> TimeUtils.convertStringToDateTimeV2("2024-10-10 10", 0));
+ ExceptionChecker.expectThrows(DateTimeParseException.class,
+ () -> TimeUtils.convertStringToDateTimeV2("2024:12:31", 0));
+ ExceptionChecker.expectThrows(DateTimeParseException.class,
+ () -> TimeUtils.convertStringToDateTimeV2("2024-10-10
11:11:11", 6));
+ }
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/BindRelationTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/BindRelationTest.java
index 67115e67687..369a57017cb 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/BindRelationTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/BindRelationTest.java
@@ -18,6 +18,8 @@
package org.apache.doris.nereids.rules.analysis;
import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.Database;
+import org.apache.doris.catalog.DatabaseIf;
import org.apache.doris.catalog.KeysType;
import org.apache.doris.catalog.OlapTable;
import org.apache.doris.catalog.PartitionInfo;
@@ -98,6 +100,8 @@ class BindRelationTest extends TestWithFeService implements
GeneratedPlanPattern
new Column("name", Type.VARCHAR)
);
+ Database externalDatabase = new Database(10000, DEFAULT_CLUSTER_PREFIX
+ DB1);
+
OlapTable externalOlapTable = new OlapTable(1, tableName,
externalTableColumns, KeysType.DUP_KEYS,
new PartitionInfo(), new RandomDistributionInfo(10)) {
@Override
@@ -109,6 +113,11 @@ class BindRelationTest extends TestWithFeService
implements GeneratedPlanPattern
public boolean hasDeleteSign() {
return false;
}
+
+ @Override
+ public DatabaseIf getDatabase() {
+ return externalDatabase;
+ }
};
CustomTableResolver customTableResolver = qualifiedTable -> {
diff --git a/gensrc/thrift/Data.thrift b/gensrc/thrift/Data.thrift
index d1163821076..dc1190c6e42 100644
--- a/gensrc/thrift/Data.thrift
+++ b/gensrc/thrift/Data.thrift
@@ -54,6 +54,7 @@ struct TCell {
3: optional i64 longVal
4: optional double doubleVal
5: optional string stringVal
+ 6: optional bool isNull
// add type: date datetime
}
diff --git a/gensrc/thrift/FrontendService.thrift
b/gensrc/thrift/FrontendService.thrift
index dac90824fa2..ad3828e392e 100644
--- a/gensrc/thrift/FrontendService.thrift
+++ b/gensrc/thrift/FrontendService.thrift
@@ -981,6 +981,7 @@ struct TMetadataTableRequestParams {
10: optional PlanNodes.TTasksMetadataParams tasks_metadata_params
11: optional PlanNodes.TPartitionsMetadataParams partitions_metadata_params
12: optional PlanNodes.TMetaCacheStatsParams meta_cache_stats_params
+ 13: optional PlanNodes.TPartitionValuesMetadataParams
partition_values_metadata_params
}
struct TSchemaTableRequestParams {
diff --git a/gensrc/thrift/PlanNodes.thrift b/gensrc/thrift/PlanNodes.thrift
index d6690fe77a1..6e328a93e15 100644
--- a/gensrc/thrift/PlanNodes.thrift
+++ b/gensrc/thrift/PlanNodes.thrift
@@ -509,6 +509,12 @@ struct TPartitionsMetadataParams {
3: optional string table
}
+struct TPartitionValuesMetadataParams {
+ 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
@@ -526,6 +532,7 @@ struct TQueriesMetadataParams {
4: optional TJobsMetadataParams jobs_params
5: optional TTasksMetadataParams tasks_params
6: optional TPartitionsMetadataParams partitions_params
+ 7: optional TPartitionValuesMetadataParams partition_values_params
}
struct TMetaCacheStatsParams {
@@ -542,6 +549,7 @@ struct TMetaScanRange {
8: optional TTasksMetadataParams tasks_params
9: optional TPartitionsMetadataParams partitions_params
10: optional TMetaCacheStatsParams meta_cache_stats_params
+ 11: optional TPartitionValuesMetadataParams partition_values_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 9290768e0db..fd37adb8654 100644
--- a/gensrc/thrift/Types.thrift
+++ b/gensrc/thrift/Types.thrift
@@ -712,7 +712,8 @@ enum TMetadataType {
JOBS,
TASKS,
WORKLOAD_SCHED_POLICY,
- PARTITIONS;
+ PARTITIONS,
+ PARTITION_VALUES;
}
enum TIcebergQueryType {
diff --git
a/regression-test/data/external_table_p0/hive/test_hive_partition_values_tvf.out
b/regression-test/data/external_table_p0/hive/test_hive_partition_values_tvf.out
new file mode 100644
index 00000000000..bc69c716277
--- /dev/null
+++
b/regression-test/data/external_table_p0/hive/test_hive_partition_values_tvf.out
@@ -0,0 +1,120 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !sql01 --
+\N 0.1 test1
+\N 0.2 test2
+100 0.3 test3
+
+-- !sql02 --
+\N 0.1 test1
+\N 0.2 test2
+100 0.3 test3
+
+-- !sql03 --
+\N 0.1 test1
+\N 0.2 test2
+100 0.3 test3
+
+-- !sql11 --
+\N 0.1
+\N 0.2
+100 0.3
+
+-- !sql12 --
+0.1 \N
+0.2 \N
+0.3 100
+
+-- !sql13 --
+test1 \N
+test2 \N
+test3 100
+
+-- !sql21 --
+test3 100 0.3
+
+-- !sql22 --
+test1
+test2
+test3
+
+-- !sql22 --
+3
+
+-- !sql22 --
+3
+
+-- !sql31 --
+0.1 \N
+0.2 \N
+0.3 100
+
+-- !sql41 --
+100
+
+-- !sql42 --
+test3
+
+-- !sql51 --
+test3
+
+-- !sql61 --
+
+-- !sql71 --
+\N 0.2 test2
+100 0.3 test3
+\N 0.1 test1
+
+-- !sql72 --
+
+-- !sql73 --
+test3
+
+-- !sql81 --
+100 0.3 test3 100 0.3 test3
+
+-- !sql91 --
+t_int int Yes true \N NONE
+t_float float Yes true \N NONE
+t_string varchar(65533) Yes true \N NONE
+
+-- !sql92 --
+t_int int Yes true \N NONE
+t_float float Yes true \N NONE
+t_string varchar(65533) Yes true \N NONE
+
+-- !sql93 --
+t_int int Yes true \N NONE
+
+-- !sql94 --
+t_int int Yes true \N NONE
+
+-- !sql95 --
+\N 0.1 test1
+\N 0.2 test2
+100 0.3 test3
+
+-- !sql101 --
+k1 int Yes true \N
+
+-- !sql102 --
+
+-- !sql111 --
+p1 boolean Yes true \N NONE
+p2 tinyint Yes true \N NONE
+p3 smallint Yes true \N NONE
+p4 int Yes true \N NONE
+p5 bigint Yes true \N NONE
+p6 date Yes true \N NONE
+p7 datetime(6) Yes true \N NONE
+p8 varchar(65533) Yes true \N NONE
+
+-- !sql112 --
+1 test1 true -128 -32768 -2147483648 -9223372036854775808
1900-01-01 1899-01-01T23:59:59 \N
+2 \N false 127 32767 2147483647 9223372036854775807
9999-12-31 0001-01-01T00:00:01.321 boston
+3 \N \N \N \N \N \N \N \N
+
+-- !sql113 --
+\N \N \N \N \N \N \N \N
+false -128 -32768 -2147483648 -9223372036854775808 1900-01-01
1899-01-01T23:59:59 \N
+false 127 32767 2147483647 9223372036854775807 9999-12-31
0001-01-01T00:00:01.321 boston
+
diff --git
a/regression-test/suites/external_table_p0/hive/test_hive_partition_values_tvf.groovy
b/regression-test/suites/external_table_p0/hive/test_hive_partition_values_tvf.groovy
new file mode 100644
index 00000000000..cbdb9f8ff35
--- /dev/null
+++
b/regression-test/suites/external_table_p0/hive/test_hive_partition_values_tvf.groovy
@@ -0,0 +1,144 @@
+// 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_hive_partition_values_tvf",
"p0,external,hive,external_docker,external_docker_hive") {
+ String enabled = context.config.otherConfigs.get("enableHiveTest")
+ if (enabled == null || !enabled.equalsIgnoreCase("true")) {
+ logger.info("disable Hive test.")
+ return;
+ }
+ sql """set enable_nereids_planner=true"""
+ sql """set enable_fallback_to_original_planner=false"""
+ for (String hivePrefix : ["hive3"]) {
+ String extHiveHmsHost =
context.config.otherConfigs.get("externalEnvIp")
+ String extHiveHmsPort = context.config.otherConfigs.get(hivePrefix +
"HmsPort")
+ String catalog_name =
"${hivePrefix}_test_external_catalog_hive_partition"
+
+ sql """drop catalog if exists ${catalog_name};"""
+ sql """
+ create catalog if not exists ${catalog_name} properties (
+ 'type'='hms',
+ 'hive.metastore.uris' =
'thrift://${extHiveHmsHost}:${extHiveHmsPort}'
+ );
+ """
+
+ // 1. test qualifier
+ qt_sql01 """ select * from
${catalog_name}.multi_catalog.orc_partitioned_columns\$partitions order by
t_int, t_float, t_string"""
+ sql """ switch ${catalog_name} """
+ qt_sql02 """ select * from
multi_catalog.orc_partitioned_columns\$partitions order by t_int, t_float,
t_string"""
+ sql """ use multi_catalog"""
+ qt_sql03 """ select * from orc_partitioned_columns\$partitions order
by t_int, t_float, t_string"""
+
+ // 2. test select order
+ qt_sql11 """ select * except(t_string) from
orc_partitioned_columns\$partitions order by t_int, t_float, t_string"""
+ qt_sql12 """ select t_float, t_int from
orc_partitioned_columns\$partitions order by t_int, t_float, t_string"""
+ qt_sql13 """ select t_string, t_int from
orc_partitioned_columns\$partitions order by t_int, t_float, t_string"""
+
+ // 3. test agg
+ qt_sql21 """ select max(t_string), max(t_int), max(t_float) from
orc_partitioned_columns\$partitions"""
+ qt_sql22 """ select max(t_string) from
orc_partitioned_columns\$partitions group by t_int, t_float order by t_int,
t_float"""
+ qt_sql22 """ select count(*) from
orc_partitioned_columns\$partitions;"""
+ qt_sql22 """ select count(1) from
orc_partitioned_columns\$partitions;"""
+
+ // 4. test alias
+ qt_sql31 """ select pv.t_float, pv.t_int from
orc_partitioned_columns\$partitions as pv group by t_int, t_float order by
t_int, t_float"""
+
+ // 5. test CTE
+ qt_sql41 """ with v1 as (select t_string, t_int from
orc_partitioned_columns\$partitions order by t_int, t_float, t_string) select
max(t_int) from v1; """
+ qt_sql42 """ with v1 as (select t_string, t_int from
orc_partitioned_columns\$partitions order by t_int, t_float, t_string) select
c1 from (select max(t_string) as c1 from v1) x; """
+
+ // 6. test subquery
+ qt_sql51 """select c1 from (select max(t_string) as c1 from (select *
from multi_catalog.orc_partitioned_columns\$partitions)x)y;"""
+
+ // 7. test where
+ qt_sql61 """select * from orc_partitioned_columns\$partitions where
t_int != "__HIVE_DEFAULT_PARTITION__" order by t_int, t_float, t_string; """
+
+ // 8. test view
+ sql """drop database if exists internal.partition_values_db"""
+ sql """create database if not exists internal.partition_values_db"""
+ sql """create view internal.partition_values_db.v1 as select * from
${catalog_name}.multi_catalog.orc_partitioned_columns\$partitions"""
+ qt_sql71 """select * from internal.partition_values_db.v1"""
+ qt_sql72 """select t_string, t_int from
internal.partition_values_db.v1 where t_int != "__HIVE_DEFAULT_PARTITION__""""
+ qt_sql73 """with v1 as (select t_string, t_int from
internal.partition_values_db.v1 order by t_int, t_float, t_string) select c1
from (select max(t_string) as c1 from v1) x;"""
+
+ // 9. test join
+ qt_sql81 """select * from orc_partitioned_columns\$partitions p1 join
orc_partitioned_columns\$partitions p2 on p1.t_int = p2.t_int order by
p1.t_int, p1.t_float"""
+
+ // 10. test desc
+ qt_sql91 """desc orc_partitioned_columns\$partitions"""
+ qt_sql92 """desc function partition_values("catalog" =
"${catalog_name}", "database" = "multi_catalog", "table" =
"orc_partitioned_columns");"""
+ qt_sql93 """desc orc_partitioned_one_column\$partitions"""
+ qt_sql94 """desc function partition_values("catalog" =
"${catalog_name}", "database" = "multi_catalog", "table" =
"orc_partitioned_one_column");"""
+ qt_sql95 """select * from partition_values("catalog" =
"${catalog_name}", "database" = "multi_catalog", "table" =
"orc_partitioned_columns") order by t_int, t_float"""
+
+ // 11. test non partition table
+ test {
+ sql """select * from hive_text_complex_type\$partitions"""
+ exception "is not a partitioned table"
+ }
+ test {
+ sql """desc hive_text_complex_type\$partitions"""
+ exception "is not a partitioned table"
+ }
+
+ // 12. test inner table
+ sql """create table internal.partition_values_db.pv_inner1 (k1 int)
distributed by hash (k1) buckets 1 properties("replication_num" = "1")"""
+ qt_sql101 """desc internal.partition_values_db.pv_inner1"""
+ qt_sql102 """select * from internal.partition_values_db.pv_inner1"""
+ test {
+ sql """desc internal.partition_values_db.pv_inner1\$partitions"""
+ exception """Unknown table 'pv_inner1\$partitions'"""
+ }
+
+ test {
+ sql """select * from
internal.partition_values_db.pv_inner1\$partitions"""
+ exception """Table [pv_inner1\$partitions] does not exist in
database [partition_values_db]"""
+ }
+
+ // 13. test all types of partition columns
+ sql """switch ${catalog_name}"""
+ sql """drop database if exists partition_values_db""";
+ sql """create database partition_values_db"""
+ sql """use partition_values_db"""
+
+ sql """create table partition_values_all_types (
+ k1 int,
+ k2 string,
+ p1 boolean,
+ p2 tinyint,
+ p3 smallint,
+ p4 int,
+ p5 bigint,
+ p6 date,
+ p7 datetime,
+ p8 string
+ ) partition by list(p1, p2, p3, p4, p5, p6, p7, p8)();
+ """
+
+ qt_sql111 """desc partition_values_all_types\$partitions;"""
+
+ sql """insert into partition_values_all_types values
+ (1, "test1", true, -128, -32768, -2147483648,
-9223372036854775808, "1900-01-01", "1899-01-01 23:59:59", ""),
+ (2, null, false, 127, 32767, 2147483647, 9223372036854775807,
"9999-12-31", "0001-01-01 00:00:01.321", "boston"),
+ (3, "", null, null, null, null, null, null, null, null);
+ """
+
+ qt_sql112 """select * from partition_values_all_types order by k1;"""
+ qt_sql113 """select * from partition_values_all_types\$partitions
order by p1,p2,p3;"""
+ }
+}
+
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]