http://git-wip-us.apache.org/repos/asf/hive/blob/651e7950/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestMetaStoreEventListener.java ---------------------------------------------------------------------- diff --cc standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestMetaStoreEventListener.java index 0000000,fb4a761..fe64a91 mode 000000,100644..100644 --- a/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestMetaStoreEventListener.java +++ b/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestMetaStoreEventListener.java @@@ -1,0 -1,471 +1,472 @@@ + /* + * 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.metastore; + + import java.util.ArrayList; + import java.util.Arrays; + import java.util.Collections; + import java.util.HashMap; + import java.util.Iterator; + import java.util.List; + import java.util.Map; + + import org.apache.hadoop.conf.Configuration; + import org.apache.hadoop.hive.metastore.annotation.MetastoreUnitTest; + import org.apache.hadoop.hive.metastore.api.Database; + import org.apache.hadoop.hive.metastore.api.FieldSchema; + import org.apache.hadoop.hive.metastore.api.Partition; + import org.apache.hadoop.hive.metastore.api.PartitionEventType; + import org.apache.hadoop.hive.metastore.api.Table; + import org.apache.hadoop.hive.metastore.client.builder.DatabaseBuilder; + import org.apache.hadoop.hive.metastore.client.builder.PartitionBuilder; + import org.apache.hadoop.hive.metastore.client.builder.TableBuilder; + import org.apache.hadoop.hive.metastore.conf.MetastoreConf; + import org.apache.hadoop.hive.metastore.conf.MetastoreConf.ConfVars; + import org.apache.hadoop.hive.metastore.events.AddPartitionEvent; + import org.apache.hadoop.hive.metastore.events.AlterPartitionEvent; + import org.apache.hadoop.hive.metastore.events.AlterTableEvent; + import org.apache.hadoop.hive.metastore.events.ConfigChangeEvent; + import org.apache.hadoop.hive.metastore.events.CreateDatabaseEvent; + import org.apache.hadoop.hive.metastore.events.CreateTableEvent; + import org.apache.hadoop.hive.metastore.events.DropDatabaseEvent; + import org.apache.hadoop.hive.metastore.events.DropPartitionEvent; + import org.apache.hadoop.hive.metastore.events.DropTableEvent; + import org.apache.hadoop.hive.metastore.events.ListenerEvent; + import org.apache.hadoop.hive.metastore.events.LoadPartitionDoneEvent; + import org.apache.hadoop.hive.metastore.events.PreAddPartitionEvent; + import org.apache.hadoop.hive.metastore.events.PreAlterPartitionEvent; + import org.apache.hadoop.hive.metastore.events.PreAlterTableEvent; + import org.apache.hadoop.hive.metastore.events.PreCreateDatabaseEvent; + import org.apache.hadoop.hive.metastore.events.PreCreateTableEvent; + import org.apache.hadoop.hive.metastore.events.PreDropDatabaseEvent; + import org.apache.hadoop.hive.metastore.events.PreDropPartitionEvent; + import org.apache.hadoop.hive.metastore.events.PreDropTableEvent; + import org.apache.hadoop.hive.metastore.events.PreEventContext; + import org.apache.hadoop.hive.metastore.events.PreLoadPartitionDoneEvent; + import org.apache.hadoop.hive.metastore.security.HadoopThriftAuthBridge; + import org.junit.Assert; + import org.junit.Before; + import org.junit.Test; + + import static org.junit.Assert.assertEquals; + import static org.junit.Assert.assertFalse; + import static org.junit.Assert.assertNotSame; + import static org.junit.Assert.assertTrue; + + import com.google.common.collect.Lists; + + import org.junit.experimental.categories.Category; + + /** + * TestMetaStoreEventListener. Test case for + * {@link org.apache.hadoop.hive.metastore.MetaStoreEventListener} and + * {@link org.apache.hadoop.hive.metastore.MetaStorePreEventListener} + */ + @Category(MetastoreUnitTest.class) + public class TestMetaStoreEventListener { + private Configuration conf; + private HiveMetaStoreClient msc; + + private static final String dbName = "hive2038"; + private static final String tblName = "tmptbl"; + private static final String renamed = "tmptbl2"; + private static final String metaConfKey = "metastore.partition.name.whitelist.pattern"; + private static final String metaConfVal = ""; + + @Before + public void setUp() throws Exception { + System.setProperty("hive.metastore.event.listeners", + DummyListener.class.getName()); + System.setProperty("hive.metastore.pre.event.listeners", + DummyPreListener.class.getName()); + + conf = MetastoreConf.newMetastoreConf(); + + MetastoreConf.setVar(conf, ConfVars.PARTITION_NAME_WHITELIST_PATTERN, metaConfVal); + MetastoreConf.setLongVar(conf, ConfVars.THRIFT_CONNECTION_RETRIES, 3); + MetastoreConf.setBoolVar(conf, ConfVars.HIVE_SUPPORT_CONCURRENCY, false); + MetaStoreTestUtils.setConfForStandloneMode(conf); + MetaStoreTestUtils.startMetaStoreWithRetry(HadoopThriftAuthBridge.getBridge(), conf); + + msc = new HiveMetaStoreClient(conf); + + msc.dropDatabase(dbName, true, true, true); + DummyListener.notifyList.clear(); + DummyPreListener.notifyList.clear(); + } + + private void validateCreateDb(Database expectedDb, Database actualDb) { + assertEquals(expectedDb.getName(), actualDb.getName()); + assertEquals(expectedDb.getLocationUri(), actualDb.getLocationUri()); + } + + private void validateTable(Table expectedTable, Table actualTable) { + assertEquals(expectedTable.getTableName(), actualTable.getTableName()); + assertEquals(expectedTable.getDbName(), actualTable.getDbName()); + assertEquals(expectedTable.getSd().getLocation(), actualTable.getSd().getLocation()); + } + + private void validateCreateTable(Table expectedTable, Table actualTable) { + validateTable(expectedTable, actualTable); + } + + private void validateAddPartition(Partition expectedPartition, Partition actualPartition) { + assertEquals(expectedPartition, actualPartition); + } + + private void validateTableInAddPartition(Table expectedTable, Table actualTable) { + assertEquals(expectedTable, actualTable); + } + + private void validatePartition(Partition expectedPartition, Partition actualPartition) { + assertEquals(expectedPartition.getValues(), actualPartition.getValues()); + assertEquals(expectedPartition.getDbName(), actualPartition.getDbName()); + assertEquals(expectedPartition.getTableName(), actualPartition.getTableName()); + } + + private void validateAlterPartition(Partition expectedOldPartition, + Partition expectedNewPartition, String actualOldPartitionDbName, + String actualOldPartitionTblName,List<String> actualOldPartitionValues, + Partition actualNewPartition) { + assertEquals(expectedOldPartition.getValues(), actualOldPartitionValues); + assertEquals(expectedOldPartition.getDbName(), actualOldPartitionDbName); + assertEquals(expectedOldPartition.getTableName(), actualOldPartitionTblName); + + validatePartition(expectedNewPartition, actualNewPartition); + } + + private void validateAlterTable(Table expectedOldTable, Table expectedNewTable, + Table actualOldTable, Table actualNewTable) { + validateTable(expectedOldTable, actualOldTable); + validateTable(expectedNewTable, actualNewTable); + } + + private void validateAlterTableColumns(Table expectedOldTable, Table expectedNewTable, + Table actualOldTable, Table actualNewTable) { + validateAlterTable(expectedOldTable, expectedNewTable, actualOldTable, actualNewTable); + + assertEquals(expectedOldTable.getSd().getCols(), actualOldTable.getSd().getCols()); + assertEquals(expectedNewTable.getSd().getCols(), actualNewTable.getSd().getCols()); + } + + private void validateLoadPartitionDone(String expectedTableName, + Map<String,String> expectedPartitionName, String actualTableName, + Map<String,String> actualPartitionName) { + assertEquals(expectedPartitionName, actualPartitionName); + assertEquals(expectedTableName, actualTableName); + } + + private void validateDropPartition(Iterator<Partition> expectedPartitions, Iterator<Partition> actualPartitions) { + while (expectedPartitions.hasNext()){ + assertTrue(actualPartitions.hasNext()); + validatePartition(expectedPartitions.next(), actualPartitions.next()); + } + assertFalse(actualPartitions.hasNext()); + } + + private void validateTableInDropPartition(Table expectedTable, Table actualTable) { + validateTable(expectedTable, actualTable); + } + + private void validateDropTable(Table expectedTable, Table actualTable) { + validateTable(expectedTable, actualTable); + } + + private void validateDropDb(Database expectedDb, Database actualDb) { + assertEquals(expectedDb, actualDb); + } + + @Test + public void testListener() throws Exception { + int listSize = 0; + + List<ListenerEvent> notifyList = DummyListener.notifyList; + List<PreEventContext> preNotifyList = DummyPreListener.notifyList; + assertEquals(notifyList.size(), listSize); + assertEquals(preNotifyList.size(), listSize); + + new DatabaseBuilder() + .setName(dbName) + .create(msc, conf); + listSize++; + PreCreateDatabaseEvent preDbEvent = (PreCreateDatabaseEvent)(preNotifyList.get(preNotifyList.size() - 1)); + Database db = msc.getDatabase(dbName); + assertEquals(listSize, notifyList.size()); + assertEquals(listSize + 1, preNotifyList.size()); + validateCreateDb(db, preDbEvent.getDatabase()); + + CreateDatabaseEvent dbEvent = (CreateDatabaseEvent)(notifyList.get(listSize - 1)); + Assert.assertTrue(dbEvent.getStatus()); + validateCreateDb(db, dbEvent.getDatabase()); + + Table table = new TableBuilder() + .inDb(db) + .setTableName(tblName) + .addCol("a", "string") + .addPartCol("b", "string") + .create(msc, conf); + PreCreateTableEvent preTblEvent = (PreCreateTableEvent)(preNotifyList.get(preNotifyList.size() - 1)); + listSize++; + Table tbl = msc.getTable(dbName, tblName); + validateCreateTable(tbl, preTblEvent.getTable()); + assertEquals(notifyList.size(), listSize); + + CreateTableEvent tblEvent = (CreateTableEvent)(notifyList.get(listSize - 1)); + Assert.assertTrue(tblEvent.getStatus()); + validateCreateTable(tbl, tblEvent.getTable()); + + + new PartitionBuilder() + .inTable(table) + .addValue("2011") + .addToTable(msc, conf); + listSize++; + assertEquals(notifyList.size(), listSize); + PreAddPartitionEvent prePartEvent = (PreAddPartitionEvent)(preNotifyList.get(preNotifyList.size() - 1)); + + AddPartitionEvent partEvent = (AddPartitionEvent)(notifyList.get(listSize-1)); + Assert.assertTrue(partEvent.getStatus()); + Partition part = msc.getPartition("hive2038", "tmptbl", "b=2011"); + Partition partAdded = partEvent.getPartitionIterator().next(); ++ partAdded.setWriteId(part.getWriteId()); + validateAddPartition(part, partAdded); + validateTableInAddPartition(tbl, partEvent.getTable()); + validateAddPartition(part, prePartEvent.getPartitions().get(0)); + + // Test adding multiple partitions in a single partition-set, atomically. + int currentTime = (int)System.currentTimeMillis(); + HiveMetaStoreClient hmsClient = new HiveMetaStoreClient(conf); + table = hmsClient.getTable(dbName, "tmptbl"); + Partition partition1 = new Partition(Arrays.asList("20110101"), dbName, "tmptbl", currentTime, + currentTime, table.getSd(), table.getParameters()); + Partition partition2 = new Partition(Arrays.asList("20110102"), dbName, "tmptbl", currentTime, + currentTime, table.getSd(), table.getParameters()); + Partition partition3 = new Partition(Arrays.asList("20110103"), dbName, "tmptbl", currentTime, + currentTime, table.getSd(), table.getParameters()); + hmsClient.add_partitions(Arrays.asList(partition1, partition2, partition3)); + ++listSize; + AddPartitionEvent multiplePartitionEvent = (AddPartitionEvent)(notifyList.get(listSize-1)); + assertEquals("Unexpected table value.", table, multiplePartitionEvent.getTable()); + List<Partition> multiParts = Lists.newArrayList(multiplePartitionEvent.getPartitionIterator()); + assertEquals("Unexpected number of partitions in event!", 3, multiParts.size()); + assertEquals("Unexpected partition value.", partition1.getValues(), multiParts.get(0).getValues()); + assertEquals("Unexpected partition value.", partition2.getValues(), multiParts.get(1).getValues()); + assertEquals("Unexpected partition value.", partition3.getValues(), multiParts.get(2).getValues()); + + part.setLastAccessTime((int)(System.currentTimeMillis()/1000)); + msc.alter_partition(dbName, tblName, part); + listSize++; + assertEquals(notifyList.size(), listSize); + PreAlterPartitionEvent preAlterPartEvent = + (PreAlterPartitionEvent)preNotifyList.get(preNotifyList.size() - 1); + + //the partition did not change, + // so the new partition should be similar to the original partition + Partition origP = msc.getPartition(dbName, tblName, "b=2011"); + + AlterPartitionEvent alterPartEvent = (AlterPartitionEvent)notifyList.get(listSize - 1); + Assert.assertTrue(alterPartEvent.getStatus()); + validateAlterPartition(origP, origP, alterPartEvent.getOldPartition().getDbName(), + alterPartEvent.getOldPartition().getTableName(), + alterPartEvent.getOldPartition().getValues(), alterPartEvent.getNewPartition()); + + + validateAlterPartition(origP, origP, preAlterPartEvent.getDbName(), + preAlterPartEvent.getTableName(), preAlterPartEvent.getNewPartition().getValues(), + preAlterPartEvent.getNewPartition()); + + List<String> part_vals = new ArrayList<>(); + part_vals.add("c=2012"); + int preEventListSize; + preEventListSize = preNotifyList.size() + 1; + Partition newPart = msc.appendPartition(dbName, tblName, part_vals); + + listSize++; + assertEquals(notifyList.size(), listSize); + assertEquals(preNotifyList.size(), preEventListSize); + + AddPartitionEvent appendPartEvent = + (AddPartitionEvent)(notifyList.get(listSize-1)); + Partition partAppended = appendPartEvent.getPartitionIterator().next(); + validateAddPartition(newPart, partAppended); + + PreAddPartitionEvent preAppendPartEvent = + (PreAddPartitionEvent)(preNotifyList.get(preNotifyList.size() - 1)); + validateAddPartition(newPart, preAppendPartEvent.getPartitions().get(0)); + + Table renamedTable = new Table(table); + renamedTable.setTableName(renamed); + msc.alter_table(dbName, tblName, renamedTable); + listSize++; + assertEquals(notifyList.size(), listSize); + PreAlterTableEvent preAlterTableE = (PreAlterTableEvent) preNotifyList.get(preNotifyList.size() - 1); + + renamedTable = msc.getTable(dbName, renamed); + + AlterTableEvent alterTableE = (AlterTableEvent) notifyList.get(listSize-1); + Assert.assertTrue(alterTableE.getStatus()); + validateAlterTable(tbl, renamedTable, alterTableE.getOldTable(), alterTableE.getNewTable()); + validateAlterTable(tbl, renamedTable, preAlterTableE.getOldTable(), + preAlterTableE.getNewTable()); + + //change the table name back + table = new Table(renamedTable); + table.setTableName(tblName); + msc.alter_table(dbName, renamed, table); + listSize++; + assertEquals(notifyList.size(), listSize); + + table = msc.getTable(dbName, tblName); + table.getSd().addToCols(new FieldSchema("c", "int", "")); + msc.alter_table(dbName, tblName, table); + listSize++; + assertEquals(notifyList.size(), listSize); + preAlterTableE = (PreAlterTableEvent) preNotifyList.get(preNotifyList.size() - 1); + + Table altTable = msc.getTable(dbName, tblName); + + alterTableE = (AlterTableEvent) notifyList.get(listSize-1); + Assert.assertTrue(alterTableE.getStatus()); + validateAlterTableColumns(tbl, altTable, alterTableE.getOldTable(), alterTableE.getNewTable()); + validateAlterTableColumns(tbl, altTable, preAlterTableE.getOldTable(), + preAlterTableE.getNewTable()); + + Map<String,String> kvs = new HashMap<>(1); + kvs.put("b", "2011"); + msc.markPartitionForEvent("hive2038", "tmptbl", kvs, PartitionEventType.LOAD_DONE); + listSize++; + assertEquals(notifyList.size(), listSize); + + LoadPartitionDoneEvent partMarkEvent = (LoadPartitionDoneEvent)notifyList.get(listSize - 1); + Assert.assertTrue(partMarkEvent.getStatus()); + validateLoadPartitionDone("tmptbl", kvs, partMarkEvent.getTable().getTableName(), + partMarkEvent.getPartitionName()); + + PreLoadPartitionDoneEvent prePartMarkEvent = + (PreLoadPartitionDoneEvent)preNotifyList.get(preNotifyList.size() - 1); + validateLoadPartitionDone("tmptbl", kvs, prePartMarkEvent.getTableName(), + prePartMarkEvent.getPartitionName()); + + msc.dropPartition(dbName, tblName, Collections.singletonList("2011")); + listSize++; + assertEquals(notifyList.size(), listSize); + PreDropPartitionEvent preDropPart = (PreDropPartitionEvent) preNotifyList.get(preNotifyList + .size() - 1); + + DropPartitionEvent dropPart = (DropPartitionEvent)notifyList.get(listSize - 1); + Assert.assertTrue(dropPart.getStatus()); + validateDropPartition(Collections.singletonList(part).iterator(), dropPart.getPartitionIterator()); + validateTableInDropPartition(tbl, dropPart.getTable()); + + validateDropPartition(Collections.singletonList(part).iterator(), preDropPart.getPartitionIterator()); + validateTableInDropPartition(tbl, preDropPart.getTable()); + + msc.dropTable(dbName, tblName); + listSize++; + assertEquals(notifyList.size(), listSize); + PreDropTableEvent preDropTbl = (PreDropTableEvent)preNotifyList.get(preNotifyList.size() - 1); + + DropTableEvent dropTbl = (DropTableEvent)notifyList.get(listSize-1); + Assert.assertTrue(dropTbl.getStatus()); + validateDropTable(tbl, dropTbl.getTable()); + validateDropTable(tbl, preDropTbl.getTable()); + + msc.dropDatabase(dbName); + listSize++; + assertEquals(notifyList.size(), listSize); + PreDropDatabaseEvent preDropDB = (PreDropDatabaseEvent)preNotifyList.get(preNotifyList.size() - 1); + + DropDatabaseEvent dropDB = (DropDatabaseEvent)notifyList.get(listSize-1); + Assert.assertTrue(dropDB.getStatus()); + validateDropDb(db, dropDB.getDatabase()); + validateDropDb(db, preDropDB.getDatabase()); + + msc.setMetaConf("metastore.try.direct.sql", "false"); + ConfigChangeEvent event = (ConfigChangeEvent) notifyList.get(notifyList.size() - 1); + assertEquals("metastore.try.direct.sql", event.getKey()); + assertEquals("true", event.getOldValue()); + assertEquals("false", event.getNewValue()); + } + + @Test + public void testMetaConfNotifyListenersClosingClient() throws Exception { + HiveMetaStoreClient closingClient = new HiveMetaStoreClient(conf, null); + closingClient.setMetaConf(metaConfKey, "[test pattern modified]"); + ConfigChangeEvent event = (ConfigChangeEvent) DummyListener.getLastEvent(); + assertEquals(event.getOldValue(), metaConfVal); + assertEquals(event.getNewValue(), "[test pattern modified]"); + closingClient.close(); + + Thread.sleep(2 * 1000); + + event = (ConfigChangeEvent) DummyListener.getLastEvent(); + assertEquals(event.getOldValue(), "[test pattern modified]"); + assertEquals(event.getNewValue(), metaConfVal); + } + + @Test + public void testMetaConfNotifyListenersNonClosingClient() throws Exception { + HiveMetaStoreClient nonClosingClient = new HiveMetaStoreClient(conf, null); + nonClosingClient.setMetaConf(metaConfKey, "[test pattern modified]"); + ConfigChangeEvent event = (ConfigChangeEvent) DummyListener.getLastEvent(); + assertEquals(event.getOldValue(), metaConfVal); + assertEquals(event.getNewValue(), "[test pattern modified]"); + // This should also trigger meta listener notification via TServerEventHandler#deleteContext + nonClosingClient.getTTransport().close(); + + Thread.sleep(2 * 1000); + + event = (ConfigChangeEvent) DummyListener.getLastEvent(); + assertEquals(event.getOldValue(), "[test pattern modified]"); + assertEquals(event.getNewValue(), metaConfVal); + } + + @Test + public void testMetaConfDuplicateNotification() throws Exception { + HiveMetaStoreClient closingClient = new HiveMetaStoreClient(conf, null); + closingClient.setMetaConf(metaConfKey, metaConfVal); + int beforeCloseNotificationEventCounts = DummyListener.notifyList.size(); + closingClient.close(); + + Thread.sleep(2 * 1000); + + int afterCloseNotificationEventCounts = DummyListener.notifyList.size(); + // Setting key to same value, should not trigger configChange event during shutdown + assertEquals(beforeCloseNotificationEventCounts, afterCloseNotificationEventCounts); + } + + @Test + public void testMetaConfSameHandler() throws Exception { + HiveMetaStoreClient closingClient = new HiveMetaStoreClient(conf, null); + closingClient.setMetaConf(metaConfKey, "[test pattern modified]"); + ConfigChangeEvent event = (ConfigChangeEvent) DummyListener.getLastEvent(); + int beforeCloseNotificationEventCounts = DummyListener.notifyList.size(); + IHMSHandler beforeHandler = event.getIHMSHandler(); + closingClient.close(); + + Thread.sleep(2 * 1000); + event = (ConfigChangeEvent) DummyListener.getLastEvent(); + int afterCloseNotificationEventCounts = DummyListener.notifyList.size(); + IHMSHandler afterHandler = event.getIHMSHandler(); + // Meta-conf cleanup should trigger an event to listener + assertNotSame(beforeCloseNotificationEventCounts, afterCloseNotificationEventCounts); + // Both the handlers should be same + assertEquals(beforeHandler, afterHandler); + } + }
http://git-wip-us.apache.org/repos/asf/hive/blob/651e7950/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestObjectStore.java ---------------------------------------------------------------------- diff --cc standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestObjectStore.java index 0000000,833e2bd..995271a mode 000000,100644..100644 --- a/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestObjectStore.java +++ b/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestObjectStore.java @@@ -1,0 -1,904 +1,904 @@@ + /* + * 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.metastore; + + import com.codahale.metrics.Counter; + import com.google.common.base.Supplier; + import com.google.common.collect.ImmutableList; + import org.apache.hadoop.hive.metastore.ObjectStore.RetryingExecutor; + import org.apache.hadoop.conf.Configuration; + import org.apache.hadoop.hive.metastore.annotation.MetastoreUnitTest; + import org.apache.hadoop.hive.metastore.api.BooleanColumnStatsData; + import org.apache.hadoop.hive.metastore.api.Catalog; + import org.apache.hadoop.hive.metastore.api.ColumnStatistics; + import org.apache.hadoop.hive.metastore.api.ColumnStatisticsData; + import org.apache.hadoop.hive.metastore.api.ColumnStatisticsDesc; + import org.apache.hadoop.hive.metastore.api.ColumnStatisticsObj; + import org.apache.hadoop.hive.metastore.api.CurrentNotificationEventId; + import org.apache.hadoop.hive.metastore.api.Database; + import org.apache.hadoop.hive.metastore.api.FieldSchema; + import org.apache.hadoop.hive.metastore.api.Function; + import org.apache.hadoop.hive.metastore.api.HiveObjectPrivilege; + import org.apache.hadoop.hive.metastore.api.HiveObjectRef; + import org.apache.hadoop.hive.metastore.api.InvalidInputException; + import org.apache.hadoop.hive.metastore.api.InvalidObjectException; + import org.apache.hadoop.hive.metastore.api.MetaException; + import org.apache.hadoop.hive.metastore.api.NoSuchObjectException; + import org.apache.hadoop.hive.metastore.api.NotificationEvent; + import org.apache.hadoop.hive.metastore.api.NotificationEventRequest; + import org.apache.hadoop.hive.metastore.api.NotificationEventResponse; + import org.apache.hadoop.hive.metastore.api.Partition; + import org.apache.hadoop.hive.metastore.api.PrincipalType; + import org.apache.hadoop.hive.metastore.api.PrivilegeBag; + import org.apache.hadoop.hive.metastore.api.PrivilegeGrantInfo; + import org.apache.hadoop.hive.metastore.api.Role; + import org.apache.hadoop.hive.metastore.api.SQLForeignKey; + import org.apache.hadoop.hive.metastore.api.SQLPrimaryKey; + import org.apache.hadoop.hive.metastore.api.SerDeInfo; + import org.apache.hadoop.hive.metastore.api.StorageDescriptor; + import org.apache.hadoop.hive.metastore.api.Table; + import org.apache.hadoop.hive.metastore.client.builder.CatalogBuilder; + import org.apache.hadoop.hive.metastore.client.builder.DatabaseBuilder; + import org.apache.hadoop.hive.metastore.client.builder.PartitionBuilder; + import org.apache.hadoop.hive.metastore.client.builder.HiveObjectPrivilegeBuilder; + import org.apache.hadoop.hive.metastore.client.builder.HiveObjectRefBuilder; + import org.apache.hadoop.hive.metastore.client.builder.PrivilegeGrantInfoBuilder; + import org.apache.hadoop.hive.metastore.client.builder.TableBuilder; + import org.apache.hadoop.hive.metastore.conf.MetastoreConf; + import org.apache.hadoop.hive.metastore.messaging.EventMessage; + import org.apache.hadoop.hive.metastore.metrics.Metrics; + import org.apache.hadoop.hive.metastore.metrics.MetricsConstants; + import org.apache.hadoop.hive.metastore.model.MNotificationLog; + import org.apache.hadoop.hive.metastore.model.MNotificationNextId; + import org.junit.Assert; + import org.junit.Assume; + import org.junit.Before; + import org.junit.Ignore; + import org.junit.Test; + import org.junit.experimental.categories.Category; + import org.mockito.Mockito; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + + import javax.jdo.Query; + import java.sql.Connection; + import java.sql.DriverManager; + import java.sql.ResultSet; + import java.sql.SQLException; + import java.sql.Statement; + import java.util.ArrayList; + import java.util.Arrays; + import java.util.HashMap; + import java.util.HashSet; + import java.util.List; + import java.util.Set; + import java.util.concurrent.BrokenBarrierException; + import java.util.concurrent.CyclicBarrier; + import java.util.concurrent.ExecutionException; + import java.util.concurrent.ExecutorService; + import java.util.concurrent.Executors; + import java.util.concurrent.TimeUnit; + + import static org.apache.hadoop.hive.metastore.Warehouse.DEFAULT_CATALOG_NAME; + + @Category(MetastoreUnitTest.class) + public class TestObjectStore { + private ObjectStore objectStore = null; + private Configuration conf; + + private static final String DB1 = "testobjectstoredb1"; + private static final String DB2 = "testobjectstoredb2"; + private static final String TABLE1 = "testobjectstoretable1"; + private static final String KEY1 = "testobjectstorekey1"; + private static final String KEY2 = "testobjectstorekey2"; + private static final String OWNER = "testobjectstoreowner"; + private static final String USER1 = "testobjectstoreuser1"; + private static final String ROLE1 = "testobjectstorerole1"; + private static final String ROLE2 = "testobjectstorerole2"; + private static final Logger LOG = LoggerFactory.getLogger(TestObjectStore.class.getName()); + + private static final class LongSupplier implements Supplier<Long> { + public long value = 0; + + @Override + public Long get() { + return value; + } + } + + @Before + public void setUp() throws Exception { + conf = MetastoreConf.newMetastoreConf(); + MetastoreConf.setBoolVar(conf, MetastoreConf.ConfVars.HIVE_IN_TEST, true); + MetaStoreTestUtils.setConfForStandloneMode(conf); + + objectStore = new ObjectStore(); + objectStore.setConf(conf); + dropAllStoreObjects(objectStore); + HiveMetaStore.HMSHandler.createDefaultCatalog(objectStore, new Warehouse(conf)); + } + + @Test + public void catalogs() throws MetaException, NoSuchObjectException { + final String names[] = {"cat1", "cat2"}; + final String locations[] = {"loc1", "loc2"}; + final String descriptions[] = {"description 1", "description 2"}; + + for (int i = 0; i < names.length; i++) { + Catalog cat = new CatalogBuilder() + .setName(names[i]) + .setLocation(locations[i]) + .setDescription(descriptions[i]) + .build(); + objectStore.createCatalog(cat); + } + + List<String> fetchedNames = objectStore.getCatalogs(); + Assert.assertEquals(3, fetchedNames.size()); + for (int i = 0; i < names.length - 1; i++) { + Assert.assertEquals(names[i], fetchedNames.get(i)); + Catalog cat = objectStore.getCatalog(fetchedNames.get(i)); + Assert.assertEquals(names[i], cat.getName()); + Assert.assertEquals(descriptions[i], cat.getDescription()); + Assert.assertEquals(locations[i], cat.getLocationUri()); + } + Catalog cat = objectStore.getCatalog(fetchedNames.get(2)); + Assert.assertEquals(DEFAULT_CATALOG_NAME, cat.getName()); + Assert.assertEquals(Warehouse.DEFAULT_CATALOG_COMMENT, cat.getDescription()); + // Location will vary by system. + + for (int i = 0; i < names.length; i++) objectStore.dropCatalog(names[i]); + fetchedNames = objectStore.getCatalogs(); + Assert.assertEquals(1, fetchedNames.size()); + } + + @Test(expected = NoSuchObjectException.class) + public void getNoSuchCatalog() throws MetaException, NoSuchObjectException { + objectStore.getCatalog("no_such_catalog"); + } + + @Test(expected = NoSuchObjectException.class) + public void dropNoSuchCatalog() throws MetaException, NoSuchObjectException { + objectStore.dropCatalog("no_such_catalog"); + } + + // TODO test dropping non-empty catalog + + /** + * Test database operations + */ + @Test + public void testDatabaseOps() throws MetaException, InvalidObjectException, + NoSuchObjectException { + String catName = "tdo1_cat"; + createTestCatalog(catName); + Database db1 = new Database(DB1, "description", "locationurl", null); + Database db2 = new Database(DB2, "description", "locationurl", null); + db1.setCatalogName(catName); + db2.setCatalogName(catName); + objectStore.createDatabase(db1); + objectStore.createDatabase(db2); + + List<String> databases = objectStore.getAllDatabases(catName); + LOG.info("databases: " + databases); + Assert.assertEquals(2, databases.size()); + Assert.assertEquals(DB1, databases.get(0)); + Assert.assertEquals(DB2, databases.get(1)); + + objectStore.dropDatabase(catName, DB1); + databases = objectStore.getAllDatabases(catName); + Assert.assertEquals(1, databases.size()); + Assert.assertEquals(DB2, databases.get(0)); + + objectStore.dropDatabase(catName, DB2); + } + + /** + * Test table operations + */ + @Test + public void testTableOps() throws MetaException, InvalidObjectException, NoSuchObjectException, + InvalidInputException { + Database db1 = new DatabaseBuilder() + .setName(DB1) + .setDescription("description") + .setLocation("locationurl") + .build(conf); + objectStore.createDatabase(db1); + StorageDescriptor sd1 = + new StorageDescriptor(ImmutableList.of(new FieldSchema("pk_col", "double", null)), + "location", null, null, false, 0, new SerDeInfo("SerDeName", "serializationLib", null), + null, null, null); + HashMap<String, String> params = new HashMap<>(); + params.put("EXTERNAL", "false"); + Table tbl1 = + new Table(TABLE1, DB1, "owner", 1, 2, 3, sd1, null, params, null, null, "MANAGED_TABLE"); + objectStore.createTable(tbl1); + + List<String> tables = objectStore.getAllTables(DEFAULT_CATALOG_NAME, DB1); + Assert.assertEquals(1, tables.size()); + Assert.assertEquals(TABLE1, tables.get(0)); + + StorageDescriptor sd2 = + new StorageDescriptor(ImmutableList.of(new FieldSchema("fk_col", "double", null)), + "location", null, null, false, 0, new SerDeInfo("SerDeName", "serializationLib", null), + null, null, null); + Table newTbl1 = new Table("new" + TABLE1, DB1, "owner", 1, 2, 3, sd2, null, params, null, null, + "MANAGED_TABLE"); + + // Change different fields and verify they were altered + newTbl1.setOwner("role1"); + newTbl1.setOwnerType(PrincipalType.ROLE); + - objectStore.alterTable(DEFAULT_CATALOG_NAME, DB1, TABLE1, newTbl1); ++ objectStore.alterTable(DEFAULT_CATALOG_NAME, DB1, TABLE1, newTbl1, -1, null); + tables = objectStore.getTables(DEFAULT_CATALOG_NAME, DB1, "new*"); + Assert.assertEquals(1, tables.size()); + Assert.assertEquals("new" + TABLE1, tables.get(0)); + + // Verify fields were altered during the alterTable operation + Table alteredTable = objectStore.getTable(DEFAULT_CATALOG_NAME, DB1, "new" + TABLE1); + Assert.assertEquals("Owner of table was not altered", newTbl1.getOwner(), alteredTable.getOwner()); + Assert.assertEquals("Owner type of table was not altered", newTbl1.getOwnerType(), alteredTable.getOwnerType()); + + objectStore.createTable(tbl1); + tables = objectStore.getAllTables(DEFAULT_CATALOG_NAME, DB1); + Assert.assertEquals(2, tables.size()); + + List<SQLForeignKey> foreignKeys = objectStore.getForeignKeys(DEFAULT_CATALOG_NAME, DB1, TABLE1, null, null); + Assert.assertEquals(0, foreignKeys.size()); + + SQLPrimaryKey pk = new SQLPrimaryKey(DB1, TABLE1, "pk_col", 1, + "pk_const_1", false, false, false); + pk.setCatName(DEFAULT_CATALOG_NAME); + objectStore.addPrimaryKeys(ImmutableList.of(pk)); + SQLForeignKey fk = new SQLForeignKey(DB1, TABLE1, "pk_col", + DB1, "new" + TABLE1, "fk_col", 1, + 0, 0, "fk_const_1", "pk_const_1", false, false, false); + objectStore.addForeignKeys(ImmutableList.of(fk)); + + // Retrieve from PK side + foreignKeys = objectStore.getForeignKeys(DEFAULT_CATALOG_NAME, null, null, DB1, "new" + TABLE1); + Assert.assertEquals(1, foreignKeys.size()); + + List<SQLForeignKey> fks = objectStore.getForeignKeys(DEFAULT_CATALOG_NAME, null, null, DB1, "new" + TABLE1); + if (fks != null) { + for (SQLForeignKey fkcol : fks) { + objectStore.dropConstraint(fkcol.getCatName(), fkcol.getFktable_db(), fkcol.getFktable_name(), + fkcol.getFk_name()); + } + } + // Retrieve from FK side + foreignKeys = objectStore.getForeignKeys(DEFAULT_CATALOG_NAME, DB1, TABLE1, null, null); + Assert.assertEquals(0, foreignKeys.size()); + // Retrieve from PK side + foreignKeys = objectStore.getForeignKeys(DEFAULT_CATALOG_NAME, null, null, DB1, "new" + TABLE1); + Assert.assertEquals(0, foreignKeys.size()); + + objectStore.dropTable(DEFAULT_CATALOG_NAME, DB1, TABLE1); + tables = objectStore.getAllTables(DEFAULT_CATALOG_NAME, DB1); + Assert.assertEquals(1, tables.size()); + + objectStore.dropTable(DEFAULT_CATALOG_NAME, DB1, "new" + TABLE1); + tables = objectStore.getAllTables(DEFAULT_CATALOG_NAME, DB1); + Assert.assertEquals(0, tables.size()); + + objectStore.dropDatabase(db1.getCatalogName(), DB1); + } + + private StorageDescriptor createFakeSd(String location) { + return new StorageDescriptor(null, location, null, null, false, 0, + new SerDeInfo("SerDeName", "serializationLib", null), null, null, null); + } + + + /** + * Tests partition operations + */ + @Test + public void testPartitionOps() throws MetaException, InvalidObjectException, + NoSuchObjectException, InvalidInputException { + Database db1 = new DatabaseBuilder() + .setName(DB1) + .setDescription("description") + .setLocation("locationurl") + .build(conf); + objectStore.createDatabase(db1); + StorageDescriptor sd = createFakeSd("location"); + HashMap<String, String> tableParams = new HashMap<>(); + tableParams.put("EXTERNAL", "false"); + FieldSchema partitionKey1 = new FieldSchema("Country", ColumnType.STRING_TYPE_NAME, ""); + FieldSchema partitionKey2 = new FieldSchema("State", ColumnType.STRING_TYPE_NAME, ""); + Table tbl1 = + new Table(TABLE1, DB1, "owner", 1, 2, 3, sd, Arrays.asList(partitionKey1, partitionKey2), + tableParams, null, null, "MANAGED_TABLE"); + objectStore.createTable(tbl1); + HashMap<String, String> partitionParams = new HashMap<>(); + partitionParams.put("PARTITION_LEVEL_PRIVILEGE", "true"); + List<String> value1 = Arrays.asList("US", "CA"); + Partition part1 = new Partition(value1, DB1, TABLE1, 111, 111, sd, partitionParams); + part1.setCatName(DEFAULT_CATALOG_NAME); + objectStore.addPartition(part1); + List<String> value2 = Arrays.asList("US", "MA"); + Partition part2 = new Partition(value2, DB1, TABLE1, 222, 222, sd, partitionParams); + part2.setCatName(DEFAULT_CATALOG_NAME); + objectStore.addPartition(part2); + + Deadline.startTimer("getPartition"); + List<Partition> partitions = objectStore.getPartitions(DEFAULT_CATALOG_NAME, DB1, TABLE1, 10); + Assert.assertEquals(2, partitions.size()); + Assert.assertEquals(111, partitions.get(0).getCreateTime()); + Assert.assertEquals(222, partitions.get(1).getCreateTime()); + + int numPartitions = objectStore.getNumPartitionsByFilter(DEFAULT_CATALOG_NAME, DB1, TABLE1, ""); + Assert.assertEquals(partitions.size(), numPartitions); + + numPartitions = objectStore.getNumPartitionsByFilter(DEFAULT_CATALOG_NAME, DB1, TABLE1, "country = \"US\""); + Assert.assertEquals(2, numPartitions); + + objectStore.dropPartition(DEFAULT_CATALOG_NAME, DB1, TABLE1, value1); + partitions = objectStore.getPartitions(DEFAULT_CATALOG_NAME, DB1, TABLE1, 10); + Assert.assertEquals(1, partitions.size()); + Assert.assertEquals(222, partitions.get(0).getCreateTime()); + + objectStore.dropPartition(DEFAULT_CATALOG_NAME, DB1, TABLE1, value2); + objectStore.dropTable(DEFAULT_CATALOG_NAME, DB1, TABLE1); + objectStore.dropDatabase(db1.getCatalogName(), DB1); + } + + /** + * Checks if the JDO cache is able to handle directSQL partition drops in one session. + * @throws MetaException + * @throws InvalidObjectException + * @throws NoSuchObjectException + * @throws SQLException + */ + @Test + public void testDirectSQLDropPartitionsCacheInSession() + throws MetaException, InvalidObjectException, NoSuchObjectException, InvalidInputException { + createPartitionedTable(false, false); + // query the partitions with JDO + Deadline.startTimer("getPartition"); + List<Partition> partitions = objectStore.getPartitionsInternal(DEFAULT_CATALOG_NAME, DB1, TABLE1, + 10, false, true); + Assert.assertEquals(3, partitions.size()); + + // drop partitions with directSql + objectStore.dropPartitionsInternal(DEFAULT_CATALOG_NAME, DB1, TABLE1, + Arrays.asList("test_part_col=a0", "test_part_col=a1"), true, false); + + // query the partitions with JDO, checking the cache is not causing any problem + partitions = objectStore.getPartitionsInternal(DEFAULT_CATALOG_NAME, DB1, TABLE1, + 10, false, true); + Assert.assertEquals(1, partitions.size()); + } + + /** + * Checks if the JDO cache is able to handle directSQL partition drops cross sessions. + * @throws MetaException + * @throws InvalidObjectException + * @throws NoSuchObjectException + * @throws SQLException + */ + @Test + public void testDirectSQLDropPartitionsCacheCrossSession() + throws MetaException, InvalidObjectException, NoSuchObjectException, InvalidInputException { + ObjectStore objectStore2 = new ObjectStore(); + objectStore2.setConf(conf); + + createPartitionedTable(false, false); + // query the partitions with JDO in the 1st session + Deadline.startTimer("getPartition"); + List<Partition> partitions = objectStore.getPartitionsInternal(DEFAULT_CATALOG_NAME, DB1, TABLE1, + 10, false, true); + Assert.assertEquals(3, partitions.size()); + + // query the partitions with JDO in the 2nd session + partitions = objectStore2.getPartitionsInternal(DEFAULT_CATALOG_NAME, DB1, TABLE1, 10, + false, true); + Assert.assertEquals(3, partitions.size()); + + // drop partitions with directSql in the 1st session + objectStore.dropPartitionsInternal(DEFAULT_CATALOG_NAME, DB1, TABLE1, + Arrays.asList("test_part_col=a0", "test_part_col=a1"), true, false); + + // query the partitions with JDO in the 2nd session, checking the cache is not causing any + // problem + partitions = objectStore2.getPartitionsInternal(DEFAULT_CATALOG_NAME, DB1, TABLE1, + 10, false, true); + Assert.assertEquals(1, partitions.size()); + } + + /** + * Checks if the directSQL partition drop removes every connected data from the RDBMS tables. + * @throws MetaException + * @throws InvalidObjectException + * @throws NoSuchObjectException + * @throws SQLException + */ + @Test + public void testDirectSQLDropParitionsCleanup() throws MetaException, InvalidObjectException, + NoSuchObjectException, SQLException, InvalidInputException { + + createPartitionedTable(true, true); + + // Check, that every table in the expected state before the drop + checkBackendTableSize("PARTITIONS", 3); + checkBackendTableSize("PART_PRIVS", 3); + checkBackendTableSize("PART_COL_PRIVS", 3); + checkBackendTableSize("PART_COL_STATS", 3); + checkBackendTableSize("PARTITION_PARAMS", 3); + checkBackendTableSize("PARTITION_KEY_VALS", 3); + checkBackendTableSize("SD_PARAMS", 3); + checkBackendTableSize("BUCKETING_COLS", 3); + checkBackendTableSize("SKEWED_COL_NAMES", 3); + checkBackendTableSize("SDS", 4); // Table has an SDS + checkBackendTableSize("SORT_COLS", 3); + checkBackendTableSize("SERDE_PARAMS", 3); + checkBackendTableSize("SERDES", 4); // Table has a serde + + // drop the partitions + Deadline.startTimer("dropPartitions"); + objectStore.dropPartitionsInternal(DEFAULT_CATALOG_NAME, DB1, TABLE1, + Arrays.asList("test_part_col=a0", "test_part_col=a1", "test_part_col=a2"), true, false); + + // Check, if every data is dropped connected to the partitions + checkBackendTableSize("PARTITIONS", 0); + checkBackendTableSize("PART_PRIVS", 0); + checkBackendTableSize("PART_COL_PRIVS", 0); + checkBackendTableSize("PART_COL_STATS", 0); + checkBackendTableSize("PARTITION_PARAMS", 0); + checkBackendTableSize("PARTITION_KEY_VALS", 0); + checkBackendTableSize("SD_PARAMS", 0); + checkBackendTableSize("BUCKETING_COLS", 0); + checkBackendTableSize("SKEWED_COL_NAMES", 0); + checkBackendTableSize("SDS", 1); // Table has an SDS + checkBackendTableSize("SORT_COLS", 0); + checkBackendTableSize("SERDE_PARAMS", 0); + checkBackendTableSize("SERDES", 1); // Table has a serde + } + + /** + * Creates DB1 database, TABLE1 table with 3 partitions. + * @param withPrivileges Should we create privileges as well + * @param withStatistics Should we create statitics as well + * @throws MetaException + * @throws InvalidObjectException + */ + private void createPartitionedTable(boolean withPrivileges, boolean withStatistics) + throws MetaException, InvalidObjectException, NoSuchObjectException, InvalidInputException { + Database db1 = new DatabaseBuilder() + .setName(DB1) + .setDescription("description") + .setLocation("locationurl") + .build(conf); + objectStore.createDatabase(db1); + Table tbl1 = + new TableBuilder() + .setDbName(DB1) + .setTableName(TABLE1) + .addCol("test_col1", "int") + .addCol("test_col2", "int") + .addPartCol("test_part_col", "int") + .addCol("test_bucket_col", "int", "test bucket col comment") + .addCol("test_skewed_col", "int", "test skewed col comment") + .addCol("test_sort_col", "int", "test sort col comment") + .build(conf); + objectStore.createTable(tbl1); + + PrivilegeBag privilegeBag = new PrivilegeBag(); + // Create partitions for the partitioned table + for(int i=0; i < 3; i++) { + Partition part = new PartitionBuilder() + .inTable(tbl1) + .addValue("a" + i) + .addSerdeParam("serdeParam", "serdeParamValue") + .addStorageDescriptorParam("sdParam", "sdParamValue") + .addBucketCol("test_bucket_col") + .addSkewedColName("test_skewed_col") + .addSortCol("test_sort_col", 1) + .build(conf); + objectStore.addPartition(part); + + if (withPrivileges) { + HiveObjectRef partitionReference = new HiveObjectRefBuilder().buildPartitionReference(part); + HiveObjectRef partitionColumnReference = new HiveObjectRefBuilder() + .buildPartitionColumnReference(tbl1, "test_part_col", part.getValues()); + PrivilegeGrantInfo privilegeGrantInfo = new PrivilegeGrantInfoBuilder() + .setPrivilege("a") + .build(); + HiveObjectPrivilege partitionPriv = new HiveObjectPrivilegeBuilder() + .setHiveObjectRef(partitionReference) + .setPrincipleName("a") + .setPrincipalType(PrincipalType.USER) + .setGrantInfo(privilegeGrantInfo) + .build(); + privilegeBag.addToPrivileges(partitionPriv); + HiveObjectPrivilege partitionColPriv = new HiveObjectPrivilegeBuilder() + .setHiveObjectRef(partitionColumnReference) + .setPrincipleName("a") + .setPrincipalType(PrincipalType.USER) + .setGrantInfo(privilegeGrantInfo) + .build(); + privilegeBag.addToPrivileges(partitionColPriv); + } + + if (withStatistics) { + ColumnStatistics stats = new ColumnStatistics(); + ColumnStatisticsDesc desc = new ColumnStatisticsDesc(); + desc.setCatName(tbl1.getCatName()); + desc.setDbName(tbl1.getDbName()); + desc.setTableName(tbl1.getTableName()); + desc.setPartName("test_part_col=a" + i); + stats.setStatsDesc(desc); + + List<ColumnStatisticsObj> statsObjList = new ArrayList<>(1); + stats.setStatsObj(statsObjList); + + ColumnStatisticsData data = new ColumnStatisticsData(); + BooleanColumnStatsData boolStats = new BooleanColumnStatsData(); + boolStats.setNumTrues(0); + boolStats.setNumFalses(0); + boolStats.setNumNulls(0); + data.setBooleanStats(boolStats); + + ColumnStatisticsObj partStats = new ColumnStatisticsObj("test_part_col", "int", data); + statsObjList.add(partStats); + - objectStore.updatePartitionColumnStatistics(stats, part.getValues()); ++ objectStore.updatePartitionColumnStatistics(stats, part.getValues(), -1, null, -1); + } + } + if (withPrivileges) { + objectStore.grantPrivileges(privilegeBag); + } + } + + /** + * Checks if the HMS backend db row number is as expected. If they are not, an + * {@link AssertionError} is thrown. + * @param tableName The table in which we count the rows + * @param size The expected row number + * @throws SQLException If there is a problem connecting to / querying the backend DB + */ + private void checkBackendTableSize(String tableName, int size) throws SQLException { + String connectionStr = MetastoreConf.getVar(conf, MetastoreConf.ConfVars.CONNECT_URL_KEY); + Connection conn = DriverManager.getConnection(connectionStr); + Statement stmt = conn.createStatement(); + + ResultSet rs = stmt.executeQuery("SELECT COUNT(1) FROM " + tableName); + rs.next(); + Assert.assertEquals(tableName + " table should contain " + size + " rows", size, + rs.getLong(1)); + } + + /** + * Test master keys operation + */ + @Test + public void testMasterKeyOps() throws MetaException, NoSuchObjectException { + int id1 = objectStore.addMasterKey(KEY1); + int id2 = objectStore.addMasterKey(KEY2); + + String[] keys = objectStore.getMasterKeys(); + Assert.assertEquals(2, keys.length); + Assert.assertEquals(KEY1, keys[0]); + Assert.assertEquals(KEY2, keys[1]); + + objectStore.updateMasterKey(id1, "new" + KEY1); + objectStore.updateMasterKey(id2, "new" + KEY2); + keys = objectStore.getMasterKeys(); + Assert.assertEquals(2, keys.length); + Assert.assertEquals("new" + KEY1, keys[0]); + Assert.assertEquals("new" + KEY2, keys[1]); + + objectStore.removeMasterKey(id1); + keys = objectStore.getMasterKeys(); + Assert.assertEquals(1, keys.length); + Assert.assertEquals("new" + KEY2, keys[0]); + + objectStore.removeMasterKey(id2); + } + + /** + * Test role operation + */ + @Test + public void testRoleOps() throws InvalidObjectException, MetaException, NoSuchObjectException { + objectStore.addRole(ROLE1, OWNER); + objectStore.addRole(ROLE2, OWNER); + List<String> roles = objectStore.listRoleNames(); + Assert.assertEquals(2, roles.size()); + Assert.assertEquals(ROLE2, roles.get(1)); + Role role1 = objectStore.getRole(ROLE1); + Assert.assertEquals(OWNER, role1.getOwnerName()); + objectStore.grantRole(role1, USER1, PrincipalType.USER, OWNER, PrincipalType.ROLE, true); + objectStore.revokeRole(role1, USER1, PrincipalType.USER, false); + objectStore.removeRole(ROLE1); + } + + @Test + public void testDirectSqlErrorMetrics() throws Exception { + Configuration conf = MetastoreConf.newMetastoreConf(); + MetastoreConf.setBoolVar(conf, MetastoreConf.ConfVars.METRICS_ENABLED, true); + Metrics.initialize(conf); + MetastoreConf.setVar(conf, MetastoreConf.ConfVars.HIVE_CODAHALE_METRICS_REPORTER_CLASSES, + "org.apache.hadoop.hive.common.metrics.metrics2.JsonFileMetricsReporter, " + + "org.apache.hadoop.hive.common.metrics.metrics2.JmxMetricsReporter" + ); + + // recall setup so that we get an object store with the metrics initalized + setUp(); + Counter directSqlErrors = + Metrics.getRegistry().getCounters().get(MetricsConstants.DIRECTSQL_ERRORS); + + objectStore.new GetDbHelper(DEFAULT_CATALOG_NAME, "foo", true, true) { + @Override + protected Database getSqlResult(ObjectStore.GetHelper<Database> ctx) throws MetaException { + return null; + } + + @Override + protected Database getJdoResult(ObjectStore.GetHelper<Database> ctx) throws MetaException, + NoSuchObjectException { + return null; + } + }.run(false); + + Assert.assertEquals(0, directSqlErrors.getCount()); + + objectStore.new GetDbHelper(DEFAULT_CATALOG_NAME, "foo", true, true) { + @Override + protected Database getSqlResult(ObjectStore.GetHelper<Database> ctx) throws MetaException { + throw new RuntimeException(); + } + + @Override + protected Database getJdoResult(ObjectStore.GetHelper<Database> ctx) throws MetaException, + NoSuchObjectException { + return null; + } + }.run(false); + + Assert.assertEquals(1, directSqlErrors.getCount()); + } + + private static void dropAllStoreObjects(RawStore store) + throws MetaException, InvalidObjectException, InvalidInputException { + try { + Deadline.registerIfNot(100000); + List<Function> functions = store.getAllFunctions(DEFAULT_CATALOG_NAME); + for (Function func : functions) { + store.dropFunction(DEFAULT_CATALOG_NAME, func.getDbName(), func.getFunctionName()); + } + for (String catName : store.getCatalogs()) { + List<String> dbs = store.getAllDatabases(catName); + for (String db : dbs) { + List<String> tbls = store.getAllTables(DEFAULT_CATALOG_NAME, db); + for (String tbl : tbls) { + Deadline.startTimer("getPartition"); + List<Partition> parts = store.getPartitions(DEFAULT_CATALOG_NAME, db, tbl, 100); + for (Partition part : parts) { + store.dropPartition(DEFAULT_CATALOG_NAME, db, tbl, part.getValues()); + } + // Find any constraints and drop them + Set<String> constraints = new HashSet<>(); + List<SQLPrimaryKey> pk = store.getPrimaryKeys(DEFAULT_CATALOG_NAME, db, tbl); + if (pk != null) { + for (SQLPrimaryKey pkcol : pk) { + constraints.add(pkcol.getPk_name()); + } + } + List<SQLForeignKey> fks = store.getForeignKeys(DEFAULT_CATALOG_NAME, null, null, db, tbl); + if (fks != null) { + for (SQLForeignKey fkcol : fks) { + constraints.add(fkcol.getFk_name()); + } + } + for (String constraint : constraints) { + store.dropConstraint(DEFAULT_CATALOG_NAME, db, tbl, constraint); + } + store.dropTable(DEFAULT_CATALOG_NAME, db, tbl); + } + store.dropDatabase(catName, db); + } + store.dropCatalog(catName); + } + List<String> roles = store.listRoleNames(); + for (String role : roles) { + store.removeRole(role); + } + } catch (NoSuchObjectException e) { + } + } + + @Test + public void testQueryCloseOnError() throws Exception { + ObjectStore spy = Mockito.spy(objectStore); + spy.getAllDatabases(DEFAULT_CATALOG_NAME); + spy.getAllFunctions(DEFAULT_CATALOG_NAME); + spy.getAllTables(DEFAULT_CATALOG_NAME, DB1); + spy.getPartitionCount(); + Mockito.verify(spy, Mockito.times(3)) + .rollbackAndCleanup(Mockito.anyBoolean(), Mockito.<Query>anyObject()); + } + + @Test + public void testRetryingExecutorSleep() throws Exception { + RetryingExecutor re = new ObjectStore.RetryingExecutor(MetastoreConf.newMetastoreConf(), null); + Assert.assertTrue("invalid sleep value", re.getSleepInterval() >= 0); + } + + @Ignore // See comment in ObjectStore.getDataSourceProps + @Test + public void testNonConfDatanucleusValueSet() { + String key = "datanucleus.no.such.key"; + String value = "test_value"; + String key1 = "blabla.no.such.key"; + String value1 = "another_value"; + Assume.assumeTrue(System.getProperty(key) == null); + Configuration localConf = MetastoreConf.newMetastoreConf(); + MetaStoreTestUtils.setConfForStandloneMode(localConf); + localConf.set(key, value); + localConf.set(key1, value1); + objectStore = new ObjectStore(); + objectStore.setConf(localConf); + Assert.assertEquals(value, objectStore.getProp().getProperty(key)); + Assert.assertNull(objectStore.getProp().getProperty(key1)); + } + + /** + * Test notification operations + */ + // TODO MS-SPLIT uncomment once we move EventMessage over + @Test + public void testNotificationOps() throws InterruptedException, MetaException { + final int NO_EVENT_ID = 0; + final int FIRST_EVENT_ID = 1; + final int SECOND_EVENT_ID = 2; + + NotificationEvent event = + new NotificationEvent(0, 0, EventMessage.EventType.CREATE_DATABASE.toString(), ""); + NotificationEventResponse eventResponse; + CurrentNotificationEventId eventId; + + // Verify that there is no notifications available yet + eventId = objectStore.getCurrentNotificationEventId(); + Assert.assertEquals(NO_EVENT_ID, eventId.getEventId()); + + // Verify that addNotificationEvent() updates the NotificationEvent with the new event ID + objectStore.addNotificationEvent(event); + Assert.assertEquals(FIRST_EVENT_ID, event.getEventId()); + objectStore.addNotificationEvent(event); + Assert.assertEquals(SECOND_EVENT_ID, event.getEventId()); + + // Verify that objectStore fetches the latest notification event ID + eventId = objectStore.getCurrentNotificationEventId(); + Assert.assertEquals(SECOND_EVENT_ID, eventId.getEventId()); + + // Verify that getNextNotification() returns all events + eventResponse = objectStore.getNextNotification(new NotificationEventRequest()); + Assert.assertEquals(2, eventResponse.getEventsSize()); + Assert.assertEquals(FIRST_EVENT_ID, eventResponse.getEvents().get(0).getEventId()); + Assert.assertEquals(SECOND_EVENT_ID, eventResponse.getEvents().get(1).getEventId()); + + // Verify that getNextNotification(last) returns events after a specified event + eventResponse = objectStore.getNextNotification(new NotificationEventRequest(FIRST_EVENT_ID)); + Assert.assertEquals(1, eventResponse.getEventsSize()); + Assert.assertEquals(SECOND_EVENT_ID, eventResponse.getEvents().get(0).getEventId()); + + // Verify that getNextNotification(last) returns zero events if there are no more notifications available + eventResponse = objectStore.getNextNotification(new NotificationEventRequest(SECOND_EVENT_ID)); + Assert.assertEquals(0, eventResponse.getEventsSize()); + + // Verify that cleanNotificationEvents() cleans up all old notifications + Thread.sleep(1); + objectStore.cleanNotificationEvents(1); + eventResponse = objectStore.getNextNotification(new NotificationEventRequest()); + Assert.assertEquals(0, eventResponse.getEventsSize()); + } + + @Ignore( + "This test is here to allow testing with other databases like mysql / postgres etc\n" + + " with user changes to the code. This cannot be run on apache derby because of\n" + + " https://db.apache.org/derby/docs/10.10/devguide/cdevconcepts842385.html" + ) + @Test + public void testConcurrentAddNotifications() throws ExecutionException, InterruptedException, MetaException { + + final int NUM_THREADS = 10; + CyclicBarrier cyclicBarrier = new CyclicBarrier(NUM_THREADS, + () -> LoggerFactory.getLogger("test") + .debug(NUM_THREADS + " threads going to add notification")); + + Configuration conf = MetastoreConf.newMetastoreConf(); + MetaStoreTestUtils.setConfForStandloneMode(conf); + /* + Below are the properties that need to be set based on what database this test is going to be run + */ + + // conf.setVar(HiveConf.ConfVars.METASTORE_CONNECTION_DRIVER, "com.mysql.jdbc.Driver"); + // conf.setVar(HiveConf.ConfVars.METASTORECONNECTURLKEY, + // "jdbc:mysql://localhost:3306/metastore_db"); + // conf.setVar(HiveConf.ConfVars.METASTORE_CONNECTION_USER_NAME, ""); + // conf.setVar(HiveConf.ConfVars.METASTOREPWD, ""); + + /* + we have to add this one manually as for tests the db is initialized via the metastoreDiretSQL + and we don't run the schema creation sql that includes the an insert for notification_sequence + which can be locked. the entry in notification_sequence happens via notification_event insertion. + */ + objectStore.getPersistenceManager().newQuery(MNotificationLog.class, "eventType==''").execute(); + objectStore.getPersistenceManager().newQuery(MNotificationNextId.class, "nextEventId==-1").execute(); + + objectStore.addNotificationEvent( + new NotificationEvent(0, 0, + EventMessage.EventType.CREATE_DATABASE.toString(), + "CREATE DATABASE DB initial")); + + ExecutorService executorService = Executors.newFixedThreadPool(NUM_THREADS); + for (int i = 0; i < NUM_THREADS; i++) { + final int n = i; + + executorService.execute( + () -> { + ObjectStore store = new ObjectStore(); + store.setConf(conf); + + String eventType = EventMessage.EventType.CREATE_DATABASE.toString(); + NotificationEvent dbEvent = + new NotificationEvent(0, 0, eventType, + "CREATE DATABASE DB" + n); + System.out.println("ADDING NOTIFICATION"); + + try { + cyclicBarrier.await(); + store.addNotificationEvent(dbEvent); + } catch (InterruptedException | BrokenBarrierException | MetaException e) { + throw new RuntimeException(e); + } + System.out.println("FINISH NOTIFICATION"); + }); + } + executorService.shutdown(); + Assert.assertTrue(executorService.awaitTermination(15, TimeUnit.SECONDS)); + + // we have to setup this again as the underlying PMF keeps getting reinitialized with original + // reference closed + ObjectStore store = new ObjectStore(); + store.setConf(conf); + + NotificationEventResponse eventResponse = store.getNextNotification( + new NotificationEventRequest()); + Assert.assertEquals(NUM_THREADS + 1, eventResponse.getEventsSize()); + long previousId = 0; + for (NotificationEvent event : eventResponse.getEvents()) { + Assert.assertTrue("previous:" + previousId + " current:" + event.getEventId(), + previousId < event.getEventId()); + Assert.assertTrue(previousId + 1 == event.getEventId()); + previousId = event.getEventId(); + } + } + + private void createTestCatalog(String catName) throws MetaException { + Catalog cat = new CatalogBuilder() + .setName(catName) + .setLocation("/tmp") + .build(); + objectStore.createCatalog(cat); + } + } + http://git-wip-us.apache.org/repos/asf/hive/blob/651e7950/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestOldSchema.java ---------------------------------------------------------------------- diff --cc standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestOldSchema.java index 0000000,717c5ee..01a8f81 mode 000000,100644..100644 --- a/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestOldSchema.java +++ b/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestOldSchema.java @@@ -1,0 -1,233 +1,233 @@@ + /* + * 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.metastore; + + import java.util.ArrayList; + import java.util.Arrays; + import java.util.Collections; + import java.util.List; + + import org.apache.hadoop.conf.Configuration; + import org.apache.hadoop.hive.common.ndv.hll.HyperLogLog; + import org.apache.hadoop.hive.metastore.annotation.MetastoreUnitTest; + import org.apache.hadoop.hive.metastore.api.AggrStats; + import org.apache.hadoop.hive.metastore.api.Catalog; + import org.apache.hadoop.hive.metastore.api.ColumnStatistics; + import org.apache.hadoop.hive.metastore.api.ColumnStatisticsData; + import org.apache.hadoop.hive.metastore.api.ColumnStatisticsDesc; + import org.apache.hadoop.hive.metastore.api.ColumnStatisticsObj; + import org.apache.hadoop.hive.metastore.api.Database; + import org.apache.hadoop.hive.metastore.api.FieldSchema; + import org.apache.hadoop.hive.metastore.api.FileMetadataExprType; + import org.apache.hadoop.hive.metastore.api.InvalidInputException; + import org.apache.hadoop.hive.metastore.api.InvalidObjectException; + import org.apache.hadoop.hive.metastore.api.LongColumnStatsData; + import org.apache.hadoop.hive.metastore.api.MetaException; + import org.apache.hadoop.hive.metastore.api.NoSuchObjectException; + import org.apache.hadoop.hive.metastore.api.Partition; + import org.apache.hadoop.hive.metastore.api.SerDeInfo; + import org.apache.hadoop.hive.metastore.api.StorageDescriptor; + import org.apache.hadoop.hive.metastore.api.Table; + import org.apache.hadoop.hive.metastore.client.builder.DatabaseBuilder; + import org.apache.hadoop.hive.metastore.conf.MetastoreConf; + import org.apache.hadoop.hive.ql.io.sarg.SearchArgument; + import org.junit.After; + import org.junit.Assert; + import org.junit.Before; + import org.junit.Ignore; + import org.junit.Test; + import org.junit.experimental.categories.Category; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + + import static org.apache.hadoop.hive.metastore.Warehouse.DEFAULT_CATALOG_NAME; + + @Category(MetastoreUnitTest.class) + public class TestOldSchema { + private ObjectStore store = null; + private Configuration conf; + + private static final Logger LOG = LoggerFactory.getLogger(TestOldSchema.class.getName()); + + public static class MockPartitionExpressionProxy implements PartitionExpressionProxy { + @Override + public String convertExprToFilter(byte[] expr) throws MetaException { + return null; + } + + @Override + public boolean filterPartitionsByExpr(List<FieldSchema> partColumns, byte[] expr, + String defaultPartitionName, + List<String> partitionNames) throws MetaException { + return false; + } + + @Override + public FileMetadataExprType getMetadataType(String inputFormat) { + return null; + } + + @Override + public SearchArgument createSarg(byte[] expr) { + return null; + } + + @Override + public FileFormatProxy getFileFormatProxy(FileMetadataExprType type) { + return null; + } + } + + private byte bitVectors[][] = new byte[2][]; + + @Before + public void setUp() throws Exception { + conf = MetastoreConf.newMetastoreConf(); + MetastoreConf.setBoolVar(conf, MetastoreConf.ConfVars.STATS_FETCH_BITVECTOR, false); + MetaStoreTestUtils.setConfForStandloneMode(conf); + + store = new ObjectStore(); + store.setConf(conf); + dropAllStoreObjects(store); + HiveMetaStore.HMSHandler.createDefaultCatalog(store, new Warehouse(conf)); + + HyperLogLog hll = HyperLogLog.builder().build(); + hll.addLong(1); + bitVectors[1] = hll.serialize(); + hll = HyperLogLog.builder().build(); + hll.addLong(2); + hll.addLong(3); + hll.addLong(3); + hll.addLong(4); + bitVectors[0] = hll.serialize(); + } + + @After + public void tearDown() { + } + + /** + * Tests partition operations + */ + @Ignore("HIVE-19509: Disable tests that are failing continuously") + @Test + public void testPartitionOps() throws Exception { + String dbName = "default"; + String tableName = "snp"; + Database db1 = new DatabaseBuilder() + .setName(dbName) + .setDescription("description") + .setLocation("locationurl") + .build(conf); + store.createDatabase(db1); + long now = System.currentTimeMillis(); + List<FieldSchema> cols = new ArrayList<>(); + cols.add(new FieldSchema("col1", "long", "nocomment")); + SerDeInfo serde = new SerDeInfo("serde", "seriallib", null); + StorageDescriptor sd = new StorageDescriptor(cols, "file:/tmp", "input", "output", false, 0, + serde, null, null, Collections.emptyMap()); + List<FieldSchema> partCols = new ArrayList<>(); + partCols.add(new FieldSchema("ds", "string", "")); + Table table = new Table(tableName, dbName, "me", (int) now, (int) now, 0, sd, partCols, + Collections.emptyMap(), null, null, null); + store.createTable(table); + + Deadline.startTimer("getPartition"); + for (int i = 0; i < 10; i++) { + List<String> partVal = new ArrayList<>(); + partVal.add(String.valueOf(i)); + StorageDescriptor psd = new StorageDescriptor(sd); + psd.setLocation("file:/tmp/default/hit/ds=" + partVal); + Partition part = new Partition(partVal, dbName, tableName, (int) now, (int) now, psd, + Collections.emptyMap()); + part.setCatName(DEFAULT_CATALOG_NAME); + store.addPartition(part); + ColumnStatistics cs = new ColumnStatistics(); + ColumnStatisticsDesc desc = new ColumnStatisticsDesc(false, dbName, tableName); + desc.setLastAnalyzed(now); + desc.setPartName("ds=" + String.valueOf(i)); + cs.setStatsDesc(desc); + ColumnStatisticsObj obj = new ColumnStatisticsObj(); + obj.setColName("col1"); + obj.setColType("bigint"); + ColumnStatisticsData data = new ColumnStatisticsData(); + LongColumnStatsData dcsd = new LongColumnStatsData(); + dcsd.setHighValue(1000 + i); + dcsd.setLowValue(-1000 - i); + dcsd.setNumNulls(i); + dcsd.setNumDVs(10 * i + 1); + dcsd.setBitVectors(bitVectors[0]); + data.setLongStats(dcsd); + obj.setStatsData(data); + cs.addToStatsObj(obj); - store.updatePartitionColumnStatistics(cs, partVal); ++ store.updatePartitionColumnStatistics(cs, partVal, -1, null, -1); + + } + + Checker statChecker = new Checker() { + @Override + public void checkStats(AggrStats aggrStats) throws Exception { + Assert.assertEquals(10, aggrStats.getPartsFound()); + Assert.assertEquals(1, aggrStats.getColStatsSize()); + ColumnStatisticsObj cso = aggrStats.getColStats().get(0); + Assert.assertEquals("col1", cso.getColName()); + Assert.assertEquals("bigint", cso.getColType()); + LongColumnStatsData lcsd = cso.getStatsData().getLongStats(); + Assert.assertEquals(1009, lcsd.getHighValue(), 0.01); + Assert.assertEquals(-1009, lcsd.getLowValue(), 0.01); + Assert.assertEquals(45, lcsd.getNumNulls()); + Assert.assertEquals(91, lcsd.getNumDVs()); + } + }; + List<String> partNames = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + partNames.add("ds=" + i); + } + AggrStats aggrStats = store.get_aggr_stats_for(DEFAULT_CATALOG_NAME, dbName, tableName, partNames, + Arrays.asList("col1")); + statChecker.checkStats(aggrStats); + + } + + private interface Checker { + void checkStats(AggrStats aggrStats) throws Exception; + } + + private static void dropAllStoreObjects(RawStore store) throws MetaException, + InvalidObjectException, InvalidInputException { + try { + Deadline.registerIfNot(100000); + Deadline.startTimer("getPartition"); + List<String> dbs = store.getAllDatabases(DEFAULT_CATALOG_NAME); + for (int i = 0; i < dbs.size(); i++) { + String db = dbs.get(i); + List<String> tbls = store.getAllTables(DEFAULT_CATALOG_NAME, db); + for (String tbl : tbls) { + List<Partition> parts = store.getPartitions(DEFAULT_CATALOG_NAME, db, tbl, 100); + for (Partition part : parts) { + store.dropPartition(DEFAULT_CATALOG_NAME, db, tbl, part.getValues()); + } + store.dropTable(DEFAULT_CATALOG_NAME, db, tbl); + } + store.dropDatabase(DEFAULT_CATALOG_NAME, db); + } + } catch (NoSuchObjectException e) { + } + } + + }
