Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2016-2021 Expedia, Inc.
* Copyright (C) 2016-2026 Expedia, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -90,4 +90,8 @@ void createDatabase(Database database)
throws AlreadyExistsException, InvalidObjectException, MetaException, TException;

long getLatency();

default boolean isGlueBackend() {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2016-2023 Expedia, Inc.
* Copyright (C) 2016-2026 Expedia, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -109,4 +109,9 @@ public long getLatency() {
return metaStoreMapping.getLatency();
}

@Override
public boolean isGlueBackend() {
return metaStoreMapping.isGlueBackend();
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2016-2025 Expedia, Inc.
* Copyright (C) 2016-2026 Expedia, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -77,7 +77,7 @@ public MetaStoreMapping newInstance(AbstractMetaStore metaStore) {
metaStore.getRemoteMetaStoreUris());
MetaStoreMapping metaStoreMapping = new MetaStoreMappingImpl(prefixNameFor(metaStore), metaStore.getName(),
createClient(metaStore), accessControlHandlerFactory.newInstance(metaStore), metaStore.getConnectionType(),
metaStore.getLatency(), loadMetastoreFilterHook(metaStore));
metaStore.getLatency(), loadMetastoreFilterHook(metaStore), metaStore.getGlueConfig() != null);
if (waggleDanceConfiguration.getDatabaseResolution() == DatabaseResolution.PREFIXED) {
return new DatabaseNameMapping(new PrefixMapping(metaStoreMapping), metaStore.getDatabaseNameBiMapping());
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2016-2025 Expedia, Inc.
* Copyright (C) 2016-2026 Expedia, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -57,6 +57,7 @@ class MetaStoreMappingImpl implements MetaStoreMapping {
private final ConnectionType connectionType;
private final long latency;
private final MetaStoreFilterHook metastoreFilter;
private final boolean glueBackend;
private final ExecutorService executor = Executors.newSingleThreadExecutor();

MetaStoreMappingImpl(
Expand All @@ -66,14 +67,16 @@ class MetaStoreMappingImpl implements MetaStoreMapping {
AccessControlHandler accessControlHandler,
ConnectionType connectionType,
long latency,
MetaStoreFilterHook metastoreFilter) {
MetaStoreFilterHook metastoreFilter,
boolean glueBackend) {
this.databasePrefix = databasePrefix;
this.name = name;
this.client = client;
this.accessControlHandler = accessControlHandler;
this.connectionType = connectionType;
this.latency = latency;
this.metastoreFilter = metastoreFilter;
this.glueBackend = glueBackend;
}

@Override
Expand Down Expand Up @@ -177,4 +180,9 @@ public long getLatency() {
return latency;
}

@Override
public boolean isGlueBackend() {
return glueBackend;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2016-2025 Expedia, Inc.
* Copyright (C) 2016-2026 Expedia, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -364,8 +364,23 @@ public Database get_database(String name) throws NoSuchObjectException, MetaExce
log.debug("Fetching database {}", name);
DatabaseMapping mapping = databaseMappingService.databaseMapping(name);
log.debug("Mapping is '{}'", mapping.getDatabasePrefix());
Database result = mapping.getClient().get_database(mapping.transformInboundDatabaseName(name));
return mapping.transformOutboundDatabase(mapping.getMetastoreFilter().filterDatabase(result));
try {
Database result = mapping.getClient().get_database(mapping.transformInboundDatabaseName(name));
return mapping.transformOutboundDatabase(mapping.getMetastoreFilter().filterDatabase(result));
} catch (MetaException e) {
if (mapping.isGlueBackend() && isLakeFormationAccessDenied(e)) {
log.debug("Lake Formation returned AccessDeniedException for database '{}', translating to NoSuchObjectException", name, e);
throw new NoSuchObjectException(name);
}
throw e;
}
}

private static boolean isLakeFormationAccessDenied(MetaException e) {
String message = e.getMessage();
return message != null
&& message.contains("AccessDeniedException")
&& message.contains("Lake Formation permission");
}

@Override
Expand Down Expand Up @@ -523,8 +538,16 @@ public List<String> get_all_tables(String db_name) throws MetaException, TExcept
@Loggable(value = Loggable.DEBUG, skipResult = true, name = INVOCATION_LOG_NAME)
public Table get_table(String dbname, String tbl_name) throws MetaException, NoSuchObjectException, TException {
DatabaseMapping mapping = getDbMappingAndCheckTableAllowed(dbname, tbl_name);
Table table = mapping.getClient().get_table(mapping.transformInboundDatabaseName(dbname), tbl_name);
return mapping.transformOutboundTable(mapping.getMetastoreFilter().filterTable(table));
try {
Table table = mapping.getClient().get_table(mapping.transformInboundDatabaseName(dbname), tbl_name);
return mapping.transformOutboundTable(mapping.getMetastoreFilter().filterTable(table));
} catch (MetaException e) {
if (mapping.isGlueBackend() && isLakeFormationAccessDenied(e)) {
log.debug("Lake Formation returned AccessDeniedException for table '{}.{}', translating to NoSuchObjectException", dbname, tbl_name, e);
throw new NoSuchObjectException(dbname + "." + tbl_name);
}
throw e;
}
}

@Override
Expand Down Expand Up @@ -2188,9 +2211,17 @@ public List<String> get_materialized_views_for_rewriting(String dbName) throws M
@Loggable(value = Loggable.DEBUG, skipResult = true, name = INVOCATION_LOG_NAME)
public GetTableResult get_table_req(GetTableRequest req) throws MetaException, NoSuchObjectException, TException {
DatabaseMapping mapping = getDbMappingAndCheckTableAllowed(req.getDbName(), req.getTblName());
GetTableResult result = mapping.getClient().get_table_req(mapping.transformInboundGetTableRequest(req));
result.setTable(mapping.getMetastoreFilter().filterTable(result.getTable()));
return mapping.transformOutboundGetTableResult(result);
try {
GetTableResult result = mapping.getClient().get_table_req(mapping.transformInboundGetTableRequest(req));
result.setTable(mapping.getMetastoreFilter().filterTable(result.getTable()));
return mapping.transformOutboundGetTableResult(result);
} catch (MetaException e) {
if (mapping.isGlueBackend() && isLakeFormationAccessDenied(e)) {
log.debug("Lake Formation returned AccessDeniedException for table '{}.{}', translating to NoSuchObjectException", req.getDbName(), req.getTblName(), e);
throw new NoSuchObjectException(req.getDbName() + "." + req.getTblName());
}
throw e;
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2016-2025 Expedia, Inc.
* Copyright (C) 2016-2026 Expedia, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -39,7 +39,7 @@ public class ASTQueryMappingTest {
@Before
public void setUp() {
metaStoreMapping = new PrefixMapping(new MetaStoreMappingImpl(PREFIX, "mapping", null, null, DIRECT, LATENCY,
new DefaultMetaStoreFilterHookImpl(new HiveConf())));
new DefaultMetaStoreFilterHookImpl(new HiveConf()), false));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2016-2021 Expedia, Inc.
* Copyright (C) 2016-2026 Expedia, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -35,7 +35,7 @@ public class DatabaseNameMappingTest {

private final MetaStoreMapping metaStoreMapping = new PrefixMapping(
new MetaStoreMappingImpl("pre_", "mapping", null, null, DIRECT,
0L, new DefaultMetaStoreFilterHookImpl(new HiveConf())));
0L, new DefaultMetaStoreFilterHookImpl(new HiveConf()), false));

@Test
public void mapNames() throws Exception {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2016-2025 Expedia, Inc.
* Copyright (C) 2016-2026 Expedia, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -61,9 +61,9 @@ public class MetaStoreMappingImplTest {
@Before
public void init() {
metaStoreMapping = new MetaStoreMappingImpl(DATABASE_PREFIX, NAME, client, accessControlHandler, DIRECT, LATENCY,
new DefaultMetaStoreFilterHookImpl(new HiveConf()));
new DefaultMetaStoreFilterHookImpl(new HiveConf()), false);
tunneledMetaStoreMapping = new MetaStoreMappingImpl(DATABASE_PREFIX, NAME, client, accessControlHandler, TUNNELED,
LATENCY, new DefaultMetaStoreFilterHookImpl(new HiveConf()));
LATENCY, new DefaultMetaStoreFilterHookImpl(new HiveConf()), false);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2016-2021 Expedia, Inc.
* Copyright (C) 2016-2026 Expedia, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,7 +28,7 @@
public class PrefixMappingTest {

private final MetaStoreMapping metaStoreMapping = new MetaStoreMappingImpl("prefix_", "mapping", null, null, DIRECT,
0L, new DefaultMetaStoreFilterHookImpl(new HiveConf()));
0L, new DefaultMetaStoreFilterHookImpl(new HiveConf()), false);

@Test
public void mapNames() throws Exception {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2016-2025 Expedia, Inc.
* Copyright (C) 2016-2026 Expedia, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -40,6 +40,7 @@
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

Expand Down Expand Up @@ -144,6 +145,7 @@
import org.apache.hadoop.hive.metastore.api.LockResponse;
import org.apache.hadoop.hive.metastore.api.LockType;
import org.apache.hadoop.hive.metastore.api.MapSchemaVersionToSerdeRequest;
import org.apache.hadoop.hive.metastore.api.MetaException;
import org.apache.hadoop.hive.metastore.api.NoSuchObjectException;
import org.apache.hadoop.hive.metastore.api.NotNullConstraintsRequest;
import org.apache.hadoop.hive.metastore.api.NotNullConstraintsResponse;
Expand Down Expand Up @@ -330,6 +332,32 @@ public void get_database() throws Exception {
assertThat(result, is(outboundDB));
}

@Test(expected = NoSuchObjectException.class)
public void get_database_glueBackendLakeFormationAccessDenied_throwsNoSuchObjectException() throws Exception {
when(primaryMapping.isGlueBackend()).thenReturn(true);
when(primaryMapping.transformInboundDatabaseName(DB_P)).thenReturn("inbound");
when(primaryClient.get_database("inbound")).thenThrow(new MetaException(
"An error occurred (AccessDeniedException) when calling the GetDatabase operation: "
+ "Insufficient Lake Formation permission(s): Required Describe on " + DB_P));
handler.get_database(DB_P);
}

@Test
public void get_database_nonGlueBackend_lakeFormationLikeMessage_rethrowsMetaException() throws Exception {
MetaException expected = new MetaException(
"An error occurred (AccessDeniedException) when calling the GetDatabase operation: "
+ "Insufficient Lake Formation permission(s): Required Describe on " + DB_P);
when(primaryMapping.isGlueBackend()).thenReturn(false);
when(primaryMapping.transformInboundDatabaseName(DB_P)).thenReturn("inbound");
when(primaryClient.get_database("inbound")).thenThrow(expected);
try {
handler.get_database(DB_P);
fail("Expected MetaException");
} catch (MetaException e) {
assertThat(e, is(sameInstance(expected)));
}
}

@Test
public void drop_database() throws TException {
when(primaryMapping.transformInboundDatabaseName(DB_P)).thenReturn("inbound");
Expand Down Expand Up @@ -486,6 +514,32 @@ public void get_table() throws TException {
assertThat(result, is(outbound));
}

@Test(expected = NoSuchObjectException.class)
public void get_table_glueBackendLakeFormationAccessDenied_throwsNoSuchObjectException() throws Exception {
when(primaryMapping.isGlueBackend()).thenReturn(true);
when(primaryMapping.transformInboundDatabaseName(DB_P)).thenReturn("inbound");
when(primaryClient.get_table("inbound", "table")).thenThrow(new MetaException(
"An error occurred (AccessDeniedException) when calling the GetTable operation: "
+ "Insufficient Lake Formation permission(s): Required Describe on table"));
handler.get_table(DB_P, "table");
}

@Test
public void get_table_nonGlueBackend_lakeFormationLikeMessage_rethrowsMetaException() throws Exception {
MetaException expected = new MetaException(
"An error occurred (AccessDeniedException) when calling the GetTable operation: "
+ "Insufficient Lake Formation permission(s): Required Describe on table");
when(primaryMapping.isGlueBackend()).thenReturn(false);
when(primaryMapping.transformInboundDatabaseName(DB_P)).thenReturn("inbound");
when(primaryClient.get_table("inbound", "table")).thenThrow(expected);
try {
handler.get_table(DB_P, "table");
fail("Expected MetaException");
} catch (MetaException e) {
assertThat(e, is(sameInstance(expected)));
}
}

@Test
public void get_table_objects_by_name() throws TException {
when(primaryMapping.transformInboundDatabaseName(DB_P)).thenReturn("inbound");
Expand Down Expand Up @@ -1001,6 +1055,40 @@ public void get_table_req() throws TException {
assertThat(result.getTable().getTableName(), is("table"));
}

@Test(expected = NoSuchObjectException.class)
public void get_table_req_glueBackendLakeFormationAccessDenied_throwsNoSuchObjectException() throws Exception {
when(primaryMapping.isGlueBackend()).thenReturn(true);
Table table = new Table();
table.setDbName(DB_P);
table.setTableName("table");
GetTableRequest request = new GetTableRequest(table.getDbName(), table.getTableName());
when(primaryMapping.transformInboundGetTableRequest(request)).thenReturn(request);
when(primaryClient.get_table_req(request)).thenThrow(new MetaException(
"An error occurred (AccessDeniedException) when calling the GetTable operation: "
+ "Insufficient Lake Formation permission(s): Required Describe on table"));
handler.get_table_req(request);
}

@Test
public void get_table_req_nonGlueBackend_lakeFormationLikeMessage_rethrowsMetaException() throws Exception {
MetaException expected = new MetaException(
"An error occurred (AccessDeniedException) when calling the GetTable operation: "
+ "Insufficient Lake Formation permission(s): Required Describe on table");
when(primaryMapping.isGlueBackend()).thenReturn(false);
Table table = new Table();
table.setDbName(DB_P);
table.setTableName("table");
GetTableRequest request = new GetTableRequest(table.getDbName(), table.getTableName());
when(primaryMapping.transformInboundGetTableRequest(request)).thenReturn(request);
when(primaryClient.get_table_req(request)).thenThrow(expected);
try {
handler.get_table_req(request);
fail("Expected MetaException");
} catch (MetaException e) {
assertThat(e, is(sameInstance(expected)));
}
}

@Test
public void get_table_objects_by_name_req() throws TException {
Table table0 = new Table();
Expand Down
Loading