This is an automated email from the ASF dual-hosted git repository.
difin pushed a commit to branch iceberg_native_views
in repository https://gitbox.apache.org/repos/asf/hive.git
The following commit(s) were added to refs/heads/iceberg_native_views by this
push:
new a97f705895b fixes
a97f705895b is described below
commit a97f705895bd9ada4a7548dd304703e9906d4a26
Author: Dmitriy Fingerman <[email protected]>
AuthorDate: Tue Apr 28 13:06:24 2026 -0400
fixes
---
.../iceberg/hive/NativeIcebergViewSupport.java | 45 +++++++-
.../iceberg/hive/TestNativeIcebergViewSupport.java | 3 +
.../iceberg/mr/hive/HiveIcebergStorageHandler.java | 31 +++++
.../ql/ddl/view/create/CreateViewAnalyzer.java | 50 +++++---
.../hive/ql/ddl/view/create/CreateViewDesc.java | 26 +++--
.../ql/ddl/view/create/CreateViewOperation.java | 127 +++++++++------------
.../hive/ql/metadata/CreateNativeViewRequest.java | 92 +++++++++++++++
.../hive/ql/metadata/HiveStorageHandler.java | 48 ++++++++
.../apache/hadoop/hive/ql/parse/StorageFormat.java | 17 +++
9 files changed, 339 insertions(+), 100 deletions(-)
diff --git
a/iceberg/iceberg-catalog/src/main/java/org/apache/iceberg/hive/NativeIcebergViewSupport.java
b/iceberg/iceberg-catalog/src/main/java/org/apache/iceberg/hive/NativeIcebergViewSupport.java
index ace4ea703b8..e7a0ad158b0 100644
---
a/iceberg/iceberg-catalog/src/main/java/org/apache/iceberg/hive/NativeIcebergViewSupport.java
+++
b/iceberg/iceberg-catalog/src/main/java/org/apache/iceberg/hive/NativeIcebergViewSupport.java
@@ -30,6 +30,7 @@
import org.apache.iceberg.catalog.Namespace;
import org.apache.iceberg.catalog.TableIdentifier;
import org.apache.iceberg.catalog.ViewCatalog;
+import org.apache.iceberg.relocated.com.google.common.collect.Maps;
import org.apache.iceberg.view.ViewBuilder;
/**
@@ -38,9 +39,30 @@
*/
public final class NativeIcebergViewSupport {
- /** HMS parameter aligned with Hive's {@code
CreateViewDesc#ICEBERG_NATIVE_VIEW_PROPERTY}. */
+ /** HMS / Iceberg view property marking a native Iceberg catalog view
(handler-specific). */
public static final String ICEBERG_NATIVE_VIEW_PROPERTY =
"hive.iceberg.native.view";
+ /**
+ * HMS table parameter recording the storage handler FQCN for native views;
must match the constant
+ * {@code NATIVE_VIEW_STORAGE_HANDLER_CLASS_PARAM} on {@code
org.apache.hadoop.hive.ql.metadata.HiveStorageHandler}.
+ */
+ public static final String NATIVE_VIEW_STORAGE_HANDLER_CLASS_PARAM =
"hive.storage.native.view.handler";
+
+ /** Value stored with {@link #NATIVE_VIEW_STORAGE_HANDLER_CLASS_PARAM} for
Iceberg native views. */
+ public static final String NATIVE_ICEBERG_VIEW_HANDLER_FQCN =
"org.apache.iceberg.mr.hive.HiveIcebergStorageHandler";
+
+ /**
+ * HMS / Iceberg view marker entries for a native Iceberg catalog view (same
map as
+ * {@code HiveIcebergStorageHandler#getNativeViewHmsTableProperties()}).
+ */
+ public static Map<String, String> defaultNativeViewMarkerTableProperties() {
+ return Map.of(
+ ICEBERG_NATIVE_VIEW_PROPERTY,
+ "true",
+ NATIVE_VIEW_STORAGE_HANDLER_CLASS_PARAM,
+ NATIVE_ICEBERG_VIEW_HANDLER_FQCN);
+ }
+
private NativeIcebergViewSupport() {
}
@@ -64,7 +86,7 @@ public static boolean createOrReplaceNativeView(Configuration
conf, String datab
}
ViewBuilder builder = startViewBuilder(viewCatalog, identifier,
fieldSchemas, viewSql);
- builder = applyCommentAndTblProps(builder, tblProperties, comment);
+ builder = applyCommentAndTblProps(builder,
mergeDefaultNativeViewTableProperties(tblProperties), comment);
commitView(builder, replace);
return true;
} finally {
@@ -94,8 +116,23 @@ private static ViewBuilder startViewBuilder(
.buildView(identifier)
.withSchema(HiveSchemaUtil.convert(fieldSchemas,
Collections.emptyMap(), true))
.withDefaultNamespace(Namespace.of(identifier.namespace().level(0)))
- .withQuery("hive", viewSql)
- .withProperty(ICEBERG_NATIVE_VIEW_PROPERTY, "true");
+ .withQuery("hive", viewSql);
+ }
+
+ /**
+ * Fills Iceberg native-view HMS / view marker properties when absent (e.g.
direct catalog callers).
+ * Handlers that delegate here after {@code
HiveStorageHandler#getNativeViewHmsTableProperties()} already
+ * supplied markers get the same result.
+ */
+ public static Map<String, String>
mergeDefaultNativeViewTableProperties(Map<String, String> tblProperties) {
+ Map<String, String> merged = Maps.newHashMap();
+ if (tblProperties != null) {
+ merged.putAll(tblProperties);
+ }
+ for (Map.Entry<String, String> e :
defaultNativeViewMarkerTableProperties().entrySet()) {
+ merged.putIfAbsent(e.getKey(), e.getValue());
+ }
+ return merged;
}
private static ViewBuilder applyCommentAndTblProps(
diff --git
a/iceberg/iceberg-catalog/src/test/java/org/apache/iceberg/hive/TestNativeIcebergViewSupport.java
b/iceberg/iceberg-catalog/src/test/java/org/apache/iceberg/hive/TestNativeIcebergViewSupport.java
index daf187b5c09..5f628b414ef 100644
---
a/iceberg/iceberg-catalog/src/test/java/org/apache/iceberg/hive/TestNativeIcebergViewSupport.java
+++
b/iceberg/iceberg-catalog/src/test/java/org/apache/iceberg/hive/TestNativeIcebergViewSupport.java
@@ -98,6 +98,9 @@ public void testCreateCommitsNativeViewWithMarkerProperty()
throws Exception {
View view = cat.loadView(id);
assertThat(view.properties().get(NativeIcebergViewSupport.ICEBERG_NATIVE_VIEW_PROPERTY))
.isEqualTo("true");
+ assertThat(
+
view.properties().get(NativeIcebergViewSupport.NATIVE_VIEW_STORAGE_HANDLER_CLASS_PARAM))
+ .isEqualTo(NativeIcebergViewSupport.NATIVE_ICEBERG_VIEW_HANDLER_FQCN);
assertThat(view.properties().get("comment")).isEqualTo("hello-view");
assertThat(view.properties().get("k1")).isEqualTo("v1");
HiveViewOperations ops = (HiveViewOperations) ((BaseView)
view).operations();
diff --git
a/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/HiveIcebergStorageHandler.java
b/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/HiveIcebergStorageHandler.java
index 65852f1a855..06a0a97c4e4 100644
---
a/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/HiveIcebergStorageHandler.java
+++
b/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/HiveIcebergStorageHandler.java
@@ -88,6 +88,7 @@
import
org.apache.hadoop.hive.ql.io.parquet.vector.VectorizedParquetRecordReader;
import org.apache.hadoop.hive.ql.io.sarg.ConvertAstToSearchArg;
import org.apache.hadoop.hive.ql.io.sarg.SearchArgument;
+import org.apache.hadoop.hive.ql.metadata.CreateNativeViewRequest;
import org.apache.hadoop.hive.ql.metadata.DefaultStorageHandler;
import org.apache.hadoop.hive.ql.metadata.DummyPartition;
import org.apache.hadoop.hive.ql.metadata.HiveException;
@@ -183,6 +184,7 @@
import org.apache.iceberg.hive.HiveTableOperations;
import org.apache.iceberg.hive.IcebergCatalogProperties;
import org.apache.iceberg.hive.MetastoreUtil;
+import org.apache.iceberg.hive.NativeIcebergViewSupport;
import org.apache.iceberg.io.CloseableIterable;
import org.apache.iceberg.mr.Catalogs;
import org.apache.iceberg.mr.InputFormatConfig;
@@ -383,6 +385,35 @@ public boolean supportsPartitioning() {
return true;
}
+ @Override
+ public boolean supportsNativeViewCatalog() {
+ return true;
+ }
+
+ @Override
+ public Map<String, String> getNativeViewHmsTableProperties() {
+ return NativeIcebergViewSupport.defaultNativeViewMarkerTableProperties();
+ }
+
+ @Override
+ public boolean createOrReplaceNativeView(Configuration conf,
CreateNativeViewRequest request)
+ throws HiveException {
+ try {
+ return NativeIcebergViewSupport.createOrReplaceNativeView(
+ conf,
+ request.getDatabaseName(),
+ request.getViewName(),
+ request.getSchema(),
+ request.getExpandedText(),
+ request.getProperties(),
+ request.getComment(),
+ request.isReplace(),
+ request.isIfNotExists());
+ } catch (Exception e) {
+ throw new HiveException(e);
+ }
+ }
+
/**
* @param jobConf Job configuration for InputFormat to access
* @param deserializer Deserializer
diff --git
a/ql/src/java/org/apache/hadoop/hive/ql/ddl/view/create/CreateViewAnalyzer.java
b/ql/src/java/org/apache/hadoop/hive/ql/ddl/view/create/CreateViewAnalyzer.java
index d750801172a..fdfdb469900 100644
---
a/ql/src/java/org/apache/hadoop/hive/ql/ddl/view/create/CreateViewAnalyzer.java
+++
b/ql/src/java/org/apache/hadoop/hive/ql/ddl/view/create/CreateViewAnalyzer.java
@@ -35,6 +35,7 @@
import org.apache.hadoop.hive.ql.ddl.DDLUtils;
import org.apache.hadoop.hive.ql.exec.TaskFactory;
import org.apache.hadoop.hive.ql.metadata.HiveException;
+import org.apache.hadoop.hive.ql.metadata.HiveStorageHandler;
import org.apache.hadoop.hive.ql.metadata.HiveUtils;
import org.apache.hadoop.hive.ql.metadata.Table;
import org.apache.hadoop.hive.ql.parse.ASTNode;
@@ -42,13 +43,13 @@
import org.apache.hadoop.hive.ql.parse.ParseUtils;
import org.apache.hadoop.hive.ql.parse.SemanticAnalyzer;
import org.apache.hadoop.hive.ql.parse.SemanticException;
+import org.apache.hadoop.hive.ql.parse.StorageFormat;
/**
* Analyzer for create view commands.
*/
@DDLType(types = HiveParser.TOK_CREATEVIEW)
public class CreateViewAnalyzer extends AbstractCreateViewAnalyzer {
- private static final String ICEBERG_STORAGE_HANDLER_CLASS =
"org.apache.iceberg.mr.hive.HiveIcebergStorageHandler";
public CreateViewAnalyzer(QueryState queryState) throws SemanticException {
super(queryState);
@@ -79,7 +80,7 @@ public void analyzeInternal(ASTNode root) throws
SemanticException {
getColumnNames((ASTNode)
children.remove(HiveParser.TOK_VIEWPARTCOLS).getChild(0)) : null;
ASTNode viewMetadataFormat =
children.remove(HiveParser.TOK_VIEWMETADATAFORMAT);
- boolean icebergNativeView = resolveNativeIcebergView(viewMetadataFormat);
+ String nativeViewStorageHandlerClass =
resolveNativeViewStorageHandlerClass(viewMetadataFormat);
assert children.isEmpty();
@@ -100,7 +101,7 @@ public void analyzeInternal(ASTNode root) throws
SemanticException {
List<FieldSchema> partitionColumns =
getPartitionColumns(partitionColumnNames);
setColumnAccessInfo(analyzer.getColumnAccessInfo());
CreateViewDesc desc = new CreateViewDesc(fqViewName, schema, comment,
properties, partitionColumnNames,
- ifNotExists, orReplace, originalText, expandedText, partitionColumns,
icebergNativeView);
+ ifNotExists, orReplace, originalText, expandedText, partitionColumns,
nativeViewStorageHandlerClass);
validateCreateView(desc, analyzer);
rootTasks.add(TaskFactory.get(new DDLWork(getInputs(), getOutputs(),
desc)));
@@ -198,26 +199,45 @@ private List<FieldSchema>
getPartitionColumns(List<String> partitionColumnNames)
}
/**
- * Native Iceberg view when {@code STORED BY iceberg} is present, or when it
is omitted and
- * {@code hive.default.storage.handler.class} is the Iceberg storage handler
class.
+ * Returns the FQCN of the storage handler that should own native-catalog
view metadata, or {@code null} for a
+ * classic HMS virtual view. Uses {@link
HiveStorageHandler#supportsNativeViewCatalog()} on the resolved handler.
*/
- private boolean resolveNativeIcebergView(ASTNode viewMetadataFormat) throws
SemanticException {
+ private String resolveNativeViewStorageHandlerClass(ASTNode
viewMetadataFormat) throws SemanticException {
+ String handlerClass;
if (viewMetadataFormat != null) {
if (viewMetadataFormat.getChildCount() != 1) {
throw new SemanticException("Internal error: expected single handler
identifier in view metadata");
}
- String handler = ((ASTNode) viewMetadataFormat.getChild(0)).getText();
- if (handler == null || !handler.equalsIgnoreCase("iceberg")) {
- throw new
SemanticException(ErrorMsg.VIEW_STORAGE_HANDLER_UNSUPPORTED.getMsg(
- "Only STORED BY ICEBERG is supported for native views, got: " +
handler));
+ String identifier = ((ASTNode) viewMetadataFormat.getChild(0)).getText();
+ handlerClass =
StorageFormat.resolveStorageHandlerClassNameForView(identifier);
+ } else {
+ handlerClass = HiveConf.getVar(conf,
HiveConf.ConfVars.HIVE_DEFAULT_STORAGE_HANDLER);
+ if (handlerClass != null) {
+ handlerClass = handlerClass.trim();
+ if (handlerClass.isEmpty()) {
+ handlerClass = null;
+ }
+ }
+ }
+ if (handlerClass == null || handlerClass.isEmpty()) {
+ return null;
+ }
+ try {
+ HiveStorageHandler handler = HiveUtils.getStorageHandler(conf,
handlerClass);
+ if (handler != null && handler.supportsNativeViewCatalog()) {
+ return handlerClass;
+ }
+ } catch (HiveException e) {
+ if (viewMetadataFormat != null) {
+ throw new
SemanticException(ErrorMsg.VIEW_STORAGE_HANDLER_UNSUPPORTED.getMsg(e.getMessage()),
e);
}
- return true;
+ return null;
}
- String defaultHandler = HiveConf.getVar(conf,
HiveConf.ConfVars.HIVE_DEFAULT_STORAGE_HANDLER);
- if (defaultHandler == null || defaultHandler.isEmpty()) {
- return false;
+ if (viewMetadataFormat != null) {
+ throw new
SemanticException(ErrorMsg.VIEW_STORAGE_HANDLER_UNSUPPORTED.getMsg(
+ "Native view metadata is not supported for storage handler: " +
handlerClass));
}
- return ICEBERG_STORAGE_HANDLER_CLASS.equals(defaultHandler.trim());
+ return null;
}
private void validateCreateView(CreateViewDesc desc, SemanticAnalyzer
analyzer) throws SemanticException {
diff --git
a/ql/src/java/org/apache/hadoop/hive/ql/ddl/view/create/CreateViewDesc.java
b/ql/src/java/org/apache/hadoop/hive/ql/ddl/view/create/CreateViewDesc.java
index bbdf0df0da2..3210d3d988d 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/ddl/view/create/CreateViewDesc.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/ddl/view/create/CreateViewDesc.java
@@ -34,16 +34,14 @@
public class CreateViewDesc extends AbstractCreateViewDesc {
private static final long serialVersionUID = 1L;
- /** HMS table property set when the view is declared with {@code STORED BY
ICEBERG} (native Iceberg view). */
- public static final String ICEBERG_NATIVE_VIEW_PROPERTY =
"hive.iceberg.native.view";
-
private final String comment;
private final Map<String, String> properties;
private final List<String> partitionColumnNames;
private final boolean ifNotExists;
private final boolean replace;
private final List<FieldSchema> partitionColumns;
- private final boolean icebergNativeView;
+ /** FQCN of storage handler for native-catalog view, or {@code null} for a
classic HMS virtual view. */
+ private final String nativeViewStorageHandlerClass;
private ReplicationSpec replicationSpec = null;
private String ownerName = null;
@@ -52,12 +50,12 @@ public CreateViewDesc(String viewName, List<FieldSchema>
schema, String comment,
List<String> partitionColumnNames, boolean ifNotExists, boolean replace,
String originalText,
String expandedText, List<FieldSchema> partitionColumns) {
this(viewName, schema, comment, properties, partitionColumnNames,
ifNotExists, replace, originalText,
- expandedText, partitionColumns, false);
+ expandedText, partitionColumns, null);
}
public CreateViewDesc(String viewName, List<FieldSchema> schema, String
comment, Map<String, String> properties,
List<String> partitionColumnNames, boolean ifNotExists, boolean replace,
String originalText,
- String expandedText, List<FieldSchema> partitionColumns, boolean
icebergNativeView) {
+ String expandedText, List<FieldSchema> partitionColumns, String
nativeViewStorageHandlerClass) {
super(viewName, schema, originalText, expandedText);
this.comment = comment;
this.properties = properties;
@@ -65,7 +63,7 @@ public CreateViewDesc(String viewName, List<FieldSchema>
schema, String comment,
this.ifNotExists = ifNotExists;
this.replace = replace;
this.partitionColumns = partitionColumns;
- this.icebergNativeView = icebergNativeView;
+ this.nativeViewStorageHandlerClass = nativeViewStorageHandlerClass;
}
@Explain(displayName = "partition columns")
@@ -101,9 +99,17 @@ public boolean isReplace() {
return replace;
}
- @Explain(displayName = "iceberg native view", displayOnlyOnTrue = true)
- public boolean isIcebergNativeView() {
- return icebergNativeView;
+ /**
+ * @return FQCN of the {@link HiveStorageHandler} that stores view metadata
in an external catalog, or
+ * {@code null} for a classic HMS-only virtual view.
+ */
+ @Explain(displayName = "native view storage handler", displayOnlyOnTrue =
true)
+ public String getNativeViewStorageHandlerClass() {
+ return nativeViewStorageHandlerClass;
+ }
+
+ public boolean usesNativeViewCatalog() {
+ return nativeViewStorageHandlerClass != null &&
!nativeViewStorageHandlerClass.trim().isEmpty();
}
/**
diff --git
a/ql/src/java/org/apache/hadoop/hive/ql/ddl/view/create/CreateViewOperation.java
b/ql/src/java/org/apache/hadoop/hive/ql/ddl/view/create/CreateViewOperation.java
index f8eed31cf1e..c971cfdc30b 100644
---
a/ql/src/java/org/apache/hadoop/hive/ql/ddl/view/create/CreateViewOperation.java
+++
b/ql/src/java/org/apache/hadoop/hive/ql/ddl/view/create/CreateViewOperation.java
@@ -29,27 +29,36 @@
import org.apache.hadoop.hive.ql.ddl.DDLUtils;
import org.apache.hadoop.hive.ql.hooks.WriteEntity;
import org.apache.hadoop.hive.ql.hooks.LineageInfo.DataContainer;
+import org.apache.hadoop.hive.ql.metadata.CreateNativeViewRequest;
import org.apache.hadoop.hive.ql.metadata.HiveException;
+import org.apache.hadoop.hive.ql.metadata.HiveStorageHandler;
+import org.apache.hadoop.hive.ql.metadata.HiveUtils;
import org.apache.hadoop.hive.ql.metadata.Table;
import org.apache.hadoop.hive.ql.parse.HiveTableName;
import org.apache.hadoop.hive.ql.parse.StorageFormat;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
+import java.util.HashMap;
import java.util.Map;
/**
* Operation process of creating a view.
*/
public class CreateViewOperation extends DDLOperation<CreateViewDesc> {
+ /**
+ * Legacy HMS marker for Iceberg native views created before {@link
+ * HiveStorageHandler#NATIVE_VIEW_STORAGE_HANDLER_CLASS_PARAM} was added;
cleared when altering to a classic view
+ * if the tracking parameter is absent.
+ */
+ private static final String LEGACY_ICEBERG_NATIVE_VIEW_HMS_KEY =
"hive.iceberg.native.view";
+
public CreateViewOperation(DDLOperationContext context, CreateViewDesc desc)
{
super(context, desc);
}
@Override
public int execute() throws HiveException {
- if (desc.isIcebergNativeView()) {
- return executeIcebergNativeView();
+ if (desc.usesNativeViewCatalog()) {
+ return executeNativeCatalogView();
}
Table oldview = context.getDb().getTable(desc.getViewName(), false);
@@ -88,11 +97,7 @@ public int execute() throws HiveException {
if (desc.getProperties() != null) {
oldview.getTTable().getParameters().putAll(desc.getProperties());
}
- if (desc.isIcebergNativeView()) {
- oldview.setProperty(CreateViewDesc.ICEBERG_NATIVE_VIEW_PROPERTY,
"true");
- } else {
-
oldview.getParameters().remove(CreateViewDesc.ICEBERG_NATIVE_VIEW_PROPERTY);
- }
+ clearNativeViewCatalogMarkersIfPresent(oldview);
oldview.setPartCols(desc.getPartitionColumns());
oldview.checkValidity(null);
@@ -117,10 +122,9 @@ public int execute() throws HiveException {
}
/**
- * Creates a native Iceberg view via the configured default Iceberg catalog
(HiveCatalog, REST
- * catalog with {@link org.apache.iceberg.view.ViewCatalog}, etc.).
+ * Creates a native-catalog view via the {@link HiveStorageHandler} selected
at compile time.
*/
- private int executeIcebergNativeView() throws HiveException {
+ private int executeNativeCatalogView() throws HiveException {
Table oldview = context.getDb().getTable(desc.getViewName(), false);
if (oldview != null) {
@@ -131,7 +135,7 @@ private int executeIcebergNativeView() throws HiveException
{
if (desc.getReplicationSpec().allowEventReplacementInto(dbParams)) {
isReplace = true;
} else {
- LOG.debug("DDLTask: Create Iceberg native view is skipped as view {}
is newer than update",
+ LOG.debug("DDLTask: Create native-catalog view is skipped as view {}
is newer than update",
desc.getViewName());
return 0;
}
@@ -149,15 +153,32 @@ private int executeIcebergNativeView() throws
HiveException {
boolean replace = oldview != null || desc.isReplace();
try {
- boolean created = invokeNativeIcebergViewSupport(
- name.getDb(),
- name.getTable(),
- replace,
- desc.getIfNotExists());
+ HiveStorageHandler handler =
+ HiveUtils.getStorageHandler(context.getConf(),
desc.getNativeViewStorageHandlerClass());
+ Map<String, String> viewProperties = new HashMap<>();
+ if (desc.getProperties() != null) {
+ viewProperties.putAll(desc.getProperties());
+ }
+ viewProperties.putAll(handler.getNativeViewHmsTableProperties());
+ CreateNativeViewRequest request =
+ new CreateNativeViewRequest(
+ name.getDb(),
+ name.getTable(),
+ desc.getSchema(),
+ desc.getExpandedText(),
+ viewProperties,
+ desc.getComment(),
+ replace,
+ desc.getIfNotExists());
+ boolean created = handler.createOrReplaceNativeView(context.getConf(),
request);
if (!created) {
return 0;
}
} catch (HiveException e) {
+ Throwable cause = e.getCause();
+ if (cause != null &&
cause.getClass().getName().endsWith("AlreadyExistsException")) {
+ throw new
HiveException(ErrorMsg.TABLE_ALREADY_EXISTS.getMsg(desc.getViewName()), cause);
+ }
throw e;
} catch (Exception e) {
throw new HiveException(e);
@@ -173,58 +194,25 @@ private int executeIcebergNativeView() throws
HiveException {
return 0;
}
- /**
- * Delegates to {@code org.apache.iceberg.hive.NativeIcebergViewSupport}
when the Iceberg catalog
- * module is on the classpath (avoids a Maven reactor cycle between {@code
hive-exec} and
- * {@code hive-iceberg-catalog}).
- */
- private boolean invokeNativeIcebergViewSupport(
- String databaseName, String viewName, boolean replace, boolean
ifNotExists)
- throws Exception {
- Class<?> supportClass;
- Method method;
- try {
- supportClass =
Class.forName("org.apache.iceberg.hive.NativeIcebergViewSupport");
- method =
- supportClass.getMethod(
- "createOrReplaceNativeView",
- org.apache.hadoop.conf.Configuration.class,
- String.class,
- String.class,
- java.util.List.class,
- String.class,
- Map.class,
- String.class,
- boolean.class,
- boolean.class);
- } catch (ClassNotFoundException | NoSuchMethodException e) {
- throw new HiveException(
- "Native Iceberg views require hive-iceberg-catalog on the classpath "
- + "(org.apache.iceberg.hive.NativeIcebergViewSupport is
missing).",
- e);
+ private void clearNativeViewCatalogMarkersIfPresent(Table oldview) throws
HiveException {
+ Map<String, String> params = oldview.getParameters();
+ if (params == null) {
+ return;
}
- try {
- return (Boolean)
- method.invoke(
- null,
- context.getConf(),
- databaseName,
- viewName,
- desc.getSchema(),
- desc.getExpandedText(),
- desc.getProperties(),
- desc.getComment(),
- replace,
- ifNotExists);
- } catch (InvocationTargetException e) {
- Throwable cause = e.getCause() == null ? e : e.getCause();
- if (cause.getClass().getName().endsWith("AlreadyExistsException")) {
- throw new
HiveException(ErrorMsg.TABLE_ALREADY_EXISTS.getMsg(desc.getViewName()), cause);
+ String fqcn =
params.get(HiveStorageHandler.NATIVE_VIEW_STORAGE_HANDLER_CLASS_PARAM);
+ if (fqcn != null) {
+ try {
+ HiveStorageHandler handler =
HiveUtils.getStorageHandler(context.getConf(), fqcn);
+ handler.clearNativeViewHmsTableProperties(params);
+ } catch (HiveException e) {
+ LOG.warn(
+ "Could not load storage handler {} to clear native view HMS
markers: {}",
+ fqcn,
+ e.getMessage());
+ params.remove(LEGACY_ICEBERG_NATIVE_VIEW_HMS_KEY);
}
- if (cause instanceof Exception) {
- throw (Exception) cause;
- }
- throw new HiveException(cause);
+ } else {
+ params.remove(LEGACY_ICEBERG_NATIVE_VIEW_HMS_KEY);
}
}
@@ -244,9 +232,6 @@ private Table createViewObject() throws HiveException {
if (desc.getProperties() != null) {
view.getParameters().putAll(desc.getProperties());
}
- if (desc.isIcebergNativeView()) {
- view.setProperty(CreateViewDesc.ICEBERG_NATIVE_VIEW_PROPERTY, "true");
- }
if (!CollectionUtils.isEmpty(desc.getPartitionColumns())) {
view.setPartCols(desc.getPartitionColumns());
diff --git
a/ql/src/java/org/apache/hadoop/hive/ql/metadata/CreateNativeViewRequest.java
b/ql/src/java/org/apache/hadoop/hive/ql/metadata/CreateNativeViewRequest.java
new file mode 100644
index 00000000000..f45f697ecca
--- /dev/null
+++
b/ql/src/java/org/apache/hadoop/hive/ql/metadata/CreateNativeViewRequest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.hadoop.hive.ql.metadata;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.apache.hadoop.hive.metastore.api.FieldSchema;
+
+/**
+ * Parameters for {@link HiveStorageHandler#createOrReplaceNativeView} (native
catalog view DDL).
+ */
+public final class CreateNativeViewRequest implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final String databaseName;
+ private final String viewName;
+ private final List<FieldSchema> schema;
+ private final String expandedText;
+ private final Map<String, String> properties;
+ private final String comment;
+ private final boolean replace;
+ private final boolean ifNotExists;
+
+ public CreateNativeViewRequest(
+ String databaseName,
+ String viewName,
+ List<FieldSchema> schema,
+ String expandedText,
+ Map<String, String> properties,
+ String comment,
+ boolean replace,
+ boolean ifNotExists) {
+ this.databaseName = databaseName;
+ this.viewName = viewName;
+ this.schema = schema;
+ this.expandedText = expandedText;
+ this.properties = properties == null ? null :
Collections.unmodifiableMap(properties);
+ this.comment = comment;
+ this.replace = replace;
+ this.ifNotExists = ifNotExists;
+ }
+
+ public String getDatabaseName() {
+ return databaseName;
+ }
+
+ public String getViewName() {
+ return viewName;
+ }
+
+ public List<FieldSchema> getSchema() {
+ return schema;
+ }
+
+ public String getExpandedText() {
+ return expandedText;
+ }
+
+ public Map<String, String> getProperties() {
+ return properties;
+ }
+
+ public String getComment() {
+ return comment;
+ }
+
+ public boolean isReplace() {
+ return replace;
+ }
+
+ public boolean isIfNotExists() {
+ return ifNotExists;
+ }
+}
diff --git
a/ql/src/java/org/apache/hadoop/hive/ql/metadata/HiveStorageHandler.java
b/ql/src/java/org/apache/hadoop/hive/ql/metadata/HiveStorageHandler.java
index 520c52a24a8..563ef5c35a3 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/metadata/HiveStorageHandler.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/metadata/HiveStorageHandler.java
@@ -27,6 +27,7 @@
import java.util.concurrent.ExecutorService;
import com.google.common.collect.Maps;
+import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configurable;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.hive.common.classification.InterfaceAudience;
@@ -1028,6 +1029,53 @@ default void setMergeTaskDeleteProperties(TableDesc
tableDesc) {
throw new UnsupportedOperationException("Storage handler does not support
getting custom delete merge schema.");
}
+ /**
+ * HMS table parameter recording the FQCN of the {@link HiveStorageHandler}
that created a native-catalog view,
+ * used when altering back to a classic view to clear handler-specific HMS
markers.
+ */
+ String NATIVE_VIEW_STORAGE_HANDLER_CLASS_PARAM =
"hive.storage.native.view.handler";
+
+ /**
+ * @return {@code true} if this handler may store CREATE VIEW text and
column metadata in an external catalog
+ * (native view) rather than only as a classic HMS virtual view.
+ */
+ default boolean supportsNativeViewCatalog() {
+ return false;
+ }
+
+ /**
+ * HMS table-level parameters to set on the metastore stub for a
native-catalog view (markers, etc.).
+ * Keys should be removed when {@linkplain
#clearNativeViewHmsTableProperties(Map)} is invoked for the same
+ * handler class recorded under {@link
#NATIVE_VIEW_STORAGE_HANDLER_CLASS_PARAM}.
+ */
+ default Map<String, String> getNativeViewHmsTableProperties() {
+ return Collections.emptyMap();
+ }
+
+ /**
+ * Removes entries added for native-catalog views (see {@link
#getNativeViewHmsTableProperties()}).
+ */
+ default void clearNativeViewHmsTableProperties(Map<String, String>
tableParameters) {
+ if (tableParameters == null) {
+ return;
+ }
+ for (String k : getNativeViewHmsTableProperties().keySet()) {
+ tableParameters.remove(k);
+ }
+ tableParameters.remove(NATIVE_VIEW_STORAGE_HANDLER_CLASS_PARAM);
+ }
+
+ /**
+ * Creates or replaces a view in the external catalog backing this handler.
+ *
+ * @return {@code false} if the operation was skipped (e.g. IF NOT EXISTS
and the view already exists)
+ */
+ default boolean createOrReplaceNativeView(Configuration conf,
CreateNativeViewRequest request)
+ throws HiveException {
+ throw new HiveException(
+ "createOrReplaceNativeView is not supported by storage handler " +
getClass().getName());
+ }
+
default boolean supportsDefaultColumnValues(Map<String, String> tblProps) {
return false;
}
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/parse/StorageFormat.java
b/ql/src/java/org/apache/hadoop/hive/ql/parse/StorageFormat.java
index 2472ad44ad0..4f69e1173f5 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/parse/StorageFormat.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/parse/StorageFormat.java
@@ -89,6 +89,23 @@ public String outputFormat() {
}
}
+ /**
+ * Resolves {@code STORED BY <identifier>} for CREATE VIEW (short
names such as {@code ICEBERG} or an FQCN).
+ */
+ public static String resolveStorageHandlerClassNameForView(String
identifierText) throws SemanticException {
+ if (StringUtils.isBlank(identifierText)) {
+ throw new SemanticException("Storage handler identifier in CREATE VIEW
cannot be empty");
+ }
+ String text = identifierText.trim();
+ for (StorageHandlerTypes type : StorageHandlerTypes.NON_DEFAULT_TYPES) {
+ if (type.name().equalsIgnoreCase(text)) {
+ Objects.requireNonNull(type.className());
+ return
ensureClassExists(BaseSemanticAnalyzer.unescapeSQLString(type.className()));
+ }
+ }
+ return ensureClassExists(BaseSemanticAnalyzer.unescapeSQLString(text));
+ }
+
public StorageFormat(Configuration conf) {
this.conf = conf;
this.serdeProps = new HashMap<String, String>();