gt:         boolean compile = config.compile(project);
context: /* This file is part of VoltDB.
   * Copyright (C) 2008-2014 VoltDB Inc.
   *
   * Permission is hereby granted, free of charge, to any person obtaining
   * a copy of this software and associated documentation files (the
   * "Software"), to deal in the Software without restriction, including
   * without limitation the rights to use, copy, modify, merge, publish,
   * distribute, sublicense, and/or sell copies of the Software, and to
   * permit persons to whom the Software is furnished to do so, subject to
   * the following conditions:
   *
   * The above copyright notice and this permission notice shall be
   * included in all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
   * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
   * OTHER DEALINGS IN THE SOFTWARE.
   */
  
  package org.voltdb.regressionsuites;
  
  import java.io.File;
  import java.io.FileWriter;
  import java.io.IOException;
  import java.io.UnsupportedEncodingException;
  import java.net.URL;
  import java.net.URLDecoder;
  import java.net.URLEncoder;
  import java.util.HashSet;
  import java.util.Random;
  import java.util.concurrent.atomic.AtomicInteger;
  
  import junit.framework.Test;
  
  import org.apache.commons.lang3.RandomStringUtils;
  import org.apache.zookeeper_voltpatches.CreateMode;
  import org.apache.zookeeper_voltpatches.ZooDefs;
  import org.apache.zookeeper_voltpatches.ZooKeeper;
  import org.voltcore.zk.ZKUtil;
  import org.voltdb.BackendTarget;
  import org.voltdb.ClientResponseImpl;
  import org.voltdb.TheHashinator;
  import org.voltdb.VoltDB.Configuration;
  import org.voltdb.VoltTable;
  import org.voltdb.VoltType;
  import org.voltdb.VoltZK;
  import org.voltdb.benchmark.tpcc.TPCCProjectBuilder;
  import org.voltdb.benchmark.tpcc.procedures.InsertNewOrder;
  import org.voltdb.client.Client;
  import org.voltdb.client.ClientResponse;
  import org.voltdb.client.ProcCallException;
  import org.voltdb.client.ProcedureCallback;
  import org.voltdb.client.SyncCallback;
  import org.voltdb.compiler.VoltProjectBuilder.GroupInfo;
  import org.voltdb.compiler.VoltProjectBuilder.ProcedureInfo;
  import org.voltdb.compiler.VoltProjectBuilder.UserInfo;
  import org.voltdb.types.TimestampType;
  import org.voltdb.utils.MiscUtils;
  
  /**
   * Tests a mix of multi-partition and single partition procedures on a
   * mix of replicated and partititioned tables on a mix of single-site and
   * multi-site VoltDB instances.
   *
   */
  public class TestCatalogUpdateSuite extends RegressionSuite {
  
      static final int SITES_PER_HOST = 2;
      static final int HOSTS = 2;
      static final int K = MiscUtils.isPro() ? 1 : 0;
  
      // procedures used by these tests
      static Class<?>[] BASEPROCS = { org.voltdb.benchmark.tpcc.procedures.InsertNewOrder.class,
                                      org.voltdb.benchmark.tpcc.procedures.SelectAll.class,
                                      org.voltdb.benchmark.tpcc.procedures.delivery.class };
  
      static Class<?>[] BASEPROCS_OPROCS =  { org.voltdb.benchmark.tpcc.procedures.InsertNewOrder.class,
                                              org.voltdb.benchmark.tpcc.procedures.SelectAll.class,
                                              org.voltdb.benchmark.tpcc.procedures.delivery.class,
                                              org.voltdb_testprocs.regressionsuites.orderbyprocs.InsertO1.class};
  
  
      static Class<?>[] EXPANDEDPROCS = { org.voltdb.benchmark.tpcc.procedures.InsertNewOrder.class,
                                          org.voltdb.benchmark.tpcc.procedures.SelectAll.class,
                                          org.voltdb.benchmark.tpcc.procedures.delivery.class,
                                          org.voltdb.benchmark.tpcc.procedures.InsertOrderLineBatched.class };
  
      static Class<?>[] CONFLICTPROCS = { org.voltdb.catalog.InsertNewOrder.class,
                                          org.voltdb.benchmark.tpcc.procedures.SelectAll.class,
                                          org.voltdb.benchmark.tpcc.procedures.delivery.class };
  
      static Class<?>[] SOMANYPROCS =   { org.voltdb.benchmark.tpcc.procedures.InsertNewOrder.class,
                                          org.voltdb.benchmark.tpcc.procedures.SelectAll.class,
                                          org.voltdb.benchmark.tpcc.procedures.neworder.class,
                                          org.voltdb.benchmark.tpcc.procedures.ostatByCustomerId.class,
                                          org.voltdb.benchmark.tpcc.procedures.ostatByCustomerName.class,
                                          org.voltdb.benchmark.tpcc.procedures.paymentByCustomerId.class,
                                          org.voltdb.benchmark.tpcc.procedures.paymentByCustomerName.class,
                                          org.voltdb.benchmark.tpcc.procedures.slev.class,
                                          org.voltdb.benchmark.tpcc.procedures.delivery.class };
  
      // testUpdateHonkingBigCatalog constants and statistics. 100/100/40 makes a ~2MB jar.
      private static final int HUGE_TABLES = 100;
      private static final int HUGE_COLUMNS = 100;
      private static final int HUGE_NAME_SIZE = 40;
      private static double hugeCompileElapsed = 0.0;
      private static double hugeTestElapsed = 0.0;
      private static String hugeCatalogXMLPath;
      private static String hugeCatalogJarPath;
  
      /**
       * Constructor needed for JUnit. Should just pass on parameters to superclass.
       * @param name The name of the method to test. This is just passed to the superclass.
       */
      public TestCatalogUpdateSuite(String name) {
          super(name);
      }
  
      AtomicInteger m_outstandingCalls = new AtomicInteger(0);
  
      boolean callbackSuccess;
  
      class CatTestCallback implements ProcedureCallback {
  
          final byte m_expectedStatus;
  
          CatTestCallback(byte expectedStatus) {
              m_expectedStatus = expectedStatus;
              m_outstandingCalls.incrementAndGet();
          }
  
          @Override
          public void clientCallback(ClientResponse clientResponse) {
              m_outstandingCalls.decrementAndGet();
              if (m_expectedStatus != clientResponse.getStatus()) {
                  if (clientResponse.getStatusString() != null)
                      System.err.println(clientResponse.getStatusString());
                  callbackSuccess = false;
              }
          }
      }
  
      public void testUpdateWithNoDeploymentFile() throws Exception {
          System.out.println("\n\n-----\n testUpdateWithNoDeploymentFile \n-----\n\n");
          Client client = getClient();
          String newCatalogURL;
          CatTestCallback callback;
  
          loadSomeData(client, 0, 25);
          client.drain();
          assertTrue(callbackSuccess);
  
          negativeTests(client);
          assertTrue(callbackSuccess);
  
          // asynchronously call some random inserts
          loadSomeData(client, 25, 25);
          assertTrue(callbackSuccess);
  
          // add a procedure "InsertOrderLineBatched"
          newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-expanded.jar");
          callback = new CatTestCallback(ClientResponse.SUCCESS);
          client.updateApplicationCatalog(callback, new File(newCatalogURL), null);
  
          // don't care if this succeeds or fails.
          // calling the new proc before the cat change returns is not guaranteed to work
          // we just hope it doesn't crash anything
          int x = 3;
          SyncCallback cb = new SyncCallback();
          client.callProcedure(cb,
                  org.voltdb.benchmark.tpcc.procedures.InsertOrderLineBatched.class.getSimpleName(),
                  new long[] {x}, new long[] {x}, x, new long[] {x},
                  new long[] {x}, new long[] {x}, new TimestampType[] { new TimestampType() }, new long[] {x},
                  new double[] {x}, new String[] {"a"});
          cb.waitForResponse();
  
          // make sure the previous catalog change has completed
          client.drain();
          assertTrue(callbackSuccess);
  
          // now calling the new proc better work
          x = 2;
          client.callProcedure(org.voltdb.benchmark.tpcc.procedures.InsertOrderLineBatched.class.getSimpleName(),
                  new long[] {x}, new long[] {x}, (short)x, new long[] {x},
                  new long[] {x}, new long[] {x}, new TimestampType[] { new TimestampType() }, new long[] {x},
                  new double[] {x}, new String[] {"a"});
  
          loadSomeData(client, 50, 5);
          assertTrue(callbackSuccess);
      }
  
      /**
       * Start with snapshots disabled. Enable them to one directory, check that the snapshot files are created
       * with the correct prefix. Update the catalog to do the snapshots in a different directory with a
       * different prefix and check to make sure they start going to the right place. Update the catalog
       * to disable them and then make sure no snapshots appear.
       * @throws Exception
       */
      public void testEnableModifyDisableSnapshot() throws Exception {
          m_config.deleteDirectory(new File("/tmp/snapshotdir1"));
          m_config.deleteDirectory(new File("/tmp/snapshotdir2"));
          try {
              m_config.createDirectory(new File("/tmp/snapshotdir1"));
              m_config.createDirectory(new File("/tmp/snapshotdir2"));
              Client client = getClient();
  
              //
              // Test that we can enable snapshots
              //
              String newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-enable_snapshot.jar");
              String deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-enable_snapshot.xml");
              VoltTable[] results = client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL)).getResults();
              assertTrue(results.length == 1);
              Thread.sleep(5000);
  
              //
              // Make sure snapshot files are generated
              //
              for (File f : m_config.listFiles(new File("/tmp/snapshotdir1"))) {
                  assertTrue(f.getName().startsWith("foo1"));
              }
  
              //
              // Test that we can change settings like the path
              //
              newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-change_snapshot.jar");
              deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-change_snapshot.xml");
              results = client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL)).getResults();
              assertTrue(results.length == 1);
              Thread.sleep(5000);
  
              //
              // Check that files are made in the new path
              //
              for (File f : m_config.listFiles(new File("/tmp/snapshotdir2"))) {
                  assertTrue(f.getName().startsWith("foo2"));
              }
  
              //
              // Change the snapshot path to something that doesn't exist, no crashes
              //
              newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-change_snapshot_dir_not_exist.jar");
              deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-change_snapshot_dir_not_exist.xml");
              results = client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL)).getResults();
              assertTrue(results.length == 1);
  
              System.out.println("Waiting for failed snapshots");
              Thread.sleep(5000);
  
              //
              // Change it back
              //
              newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-base.jar");
              deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-base.xml");
              results = client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL)).getResults();
              assertTrue(results.length == 1);
              Thread.sleep(5000);
  
              //
              // Make sure snapshots resume
              //
              for (File f : m_config.listFiles(new File("/tmp/snapshotdir2"))) {
                  assertTrue(f.getName().startsWith("foo2"));
              }
  
              //
              // Make sure you can disable snapshots
              //
              newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-base.jar");
              deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-base.xml");
              results = client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL)).getResults();
              assertTrue(results.length == 1);
              for (File f : m_config.listFiles(new File("/tmp/snapshotdir2"))) {
                  f.delete();
              }
  
              Thread.sleep(5000);
  
              //
              // Make sure you can reenable snapshot files
              //
              assertEquals( 0, m_config.listFiles(new File("/tmp/snapshotdir2")).size());
  
              //
              // Test that we can enable snapshots
              //
              newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-enable_snapshot.jar");
              deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-enable_snapshot.xml");
              results = client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL)).getResults();
              assertTrue(results.length == 1);
              Thread.sleep(5000);
  
              //
              // Make sure snapshot files are generated
              //
              for (File f : m_config.listFiles(new File("/tmp/snapshotdir1"))) {
                  assertTrue(f.getName().startsWith("foo1"));
              }
  
              //
              // Turn snapshots off so that we can clean up
              //
              newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-base.jar");
              deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-base.xml");
              results = client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL)).getResults();
              assertTrue(results.length == 1);
              Thread.sleep(1000);
  
              m_config.deleteDirectory(new File("/tmp/snapshotdir1"));
              m_config.deleteDirectory(new File("/tmp/snapshotdir2"));
              m_config.createDirectory(new File("/tmp/snapshotdir1"));
              m_config.createDirectory(new File("/tmp/snapshotdir2"));
              Thread.sleep(5000);
              assertTrue(m_config.listFiles(new File("/tmp/snapshotdir1")).isEmpty());
              assertTrue(m_config.listFiles(new File("/tmp/snapshotdir2")).isEmpty());
          } finally {
              deleteDirectory(new File("/tmp/snapshotdir1"));
              deleteDirectory(new File("/tmp/snapshotdir2"));
          }
      }
  
      public void testUpdate() throws Exception {
          Client client = getClient();
          String newCatalogURL;
          String deploymentURL;
          VoltTable[] results;
          CatTestCallback callback;
  
          loadSomeData(client, 0, 25);
          client.drain();
          assertTrue(callbackSuccess);
  
          negativeTests(client);
          assertTrue(callbackSuccess);
  
          // asynchronously call some random inserts
          loadSomeData(client, 25, 25);
          assertTrue(callbackSuccess);
  
          // add a procedure "InsertOrderLineBatched"
          newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-expanded.jar");
          deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-expanded.xml");
          callback = new CatTestCallback(ClientResponse.SUCCESS);
          client.updateApplicationCatalog(callback, new File(newCatalogURL), new File(deploymentURL));
  
          // don't care if this succeeds or fails.
          // calling the new proc before the cat change returns is not guaranteed to work
          // we just hope it doesn't crash anything
          int x = 3;
          SyncCallback cb = new SyncCallback();
          client.callProcedure(cb,
                  org.voltdb.benchmark.tpcc.procedures.InsertOrderLineBatched.class.getSimpleName(),
                  new long[] {x}, new long[] {x}, x, new long[] {x},
                  new long[] {x}, new long[] {x}, new TimestampType[] { new TimestampType() }, new long[] {x},
                  new double[] {x}, new String[] {"a"});
          cb.waitForResponse();
  
          // make sure the previous catalog change has completed
          client.drain();
          assertTrue(callbackSuccess);
  
          // now calling the new proc better work
          x = 2;
          client.callProcedure(org.voltdb.benchmark.tpcc.procedures.InsertOrderLineBatched.class.getSimpleName(),
                  new long[] {x}, new long[] {x}, (short)x, new long[] {x},
                  new long[] {x}, new long[] {x}, new TimestampType[] { new TimestampType() }, new long[] {x},
                  new double[] {x}, new String[] {"a"});
  
          loadSomeData(client, 50, 5);
          assertTrue(callbackSuccess);
  
          // this is a do nothing change... shouldn't affect anything
          newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-expanded.jar");
          deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-expanded.xml");
          results = client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL)).getResults();
          assertTrue(results.length == 1);
          client.drain();
          assertTrue(callbackSuccess);
  
          // now calling the new proc better work
          x = 4;
          client.callProcedure(org.voltdb.benchmark.tpcc.procedures.InsertOrderLineBatched.class.getSimpleName(),
                  new long[] {x}, new long[] {x}, (short)x, new long[] {x},
                  new long[] {x}, new long[] {x}, new TimestampType[] { new TimestampType() }, new long[] {x},
                  new double[] {x}, new String[] {"a"});
  
          loadSomeData(client, 55, 5);
  
          // remove the procedure we just added async
          newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-base.jar");
          deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-base.xml");
          callback = new CatTestCallback(ClientResponse.SUCCESS);
          client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL));
  
          // don't care if this works now
          x = 4;
          cb = new SyncCallback();
          client.callProcedure(cb,
                  org.voltdb.benchmark.tpcc.procedures.InsertOrderLineBatched.class.getSimpleName(),
                  new long[] {x}, new long[] {x}, (short)x, new long[] {x},
                  new long[] {x}, new long[] {x}, new TimestampType[] { new TimestampType() }, new long[] {x},
                  new double[] {x}, new String[] {"a"});
          cb.waitForResponse();
  
          // make sure the previous catalog change has completed
          client.drain();
          assertTrue(callbackSuccess);
  
          // now calling the new proc better fail
          x = 5;
          cb = new SyncCallback();
          client.callProcedure(cb,
                  org.voltdb.benchmark.tpcc.procedures.InsertOrderLineBatched.class.getSimpleName(),
                  new long[] {x}, new long[] {x}, (short)x, new long[] {x},
                  new long[] {x}, new long[] {x}, new TimestampType[] { new TimestampType() }, new long[] {x},
                  new double[] {x}, new String[] {"a"});
          cb.waitForResponse();
          assertNotSame(cb.getResponse().getStatus(), ClientResponse.SUCCESS);
  
          loadSomeData(client, 60, 5);
  
          // change the insert new order procedure
          newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-conflict.jar");
          deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-conflict.xml");
          results = client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL)).getResults();
          assertTrue(results.length == 1);
  
          // call the new proc and make sure the one we want gets run
          results = client.callProcedure(InsertNewOrder.class.getSimpleName(), 100, 100, 100, 100, (short)100, 100, 1.0, "a").getResults();
          assertEquals(1, results.length);
          assertEquals(1776, results[0].asScalarLong());
  
          // load a big catalog change just to make sure nothing fails horribly
          newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-many.jar");
          deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-many.xml");
          results = client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL)).getResults();
          assertTrue(results.length == 1);
  
          loadSomeData(client, 65, 5);
  
          //Check that if a catalog update blocker exists the catalog update fails
          ZooKeeper zk = ZKUtil.getClient(((LocalCluster) m_config).zkinterface(0), 10000, new HashSet<Long>());
          final String catalogUpdateBlockerPath = zk.create(
                  VoltZK.elasticJoinActiveBlocker,
                  null,
                  ZooDefs.Ids.OPEN_ACL_UNSAFE,
                  CreateMode.EPHEMERAL_SEQUENTIAL );
          try {
              /*
               * Update the catalog and expect failure
               */
              newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-base.jar");
              deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-base.xml");
              boolean threw = false;
              try {
                  client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL));
              } catch (ProcCallException e) {
                  e.printStackTrace();
                  threw = true;
              }
              assertTrue(threw);
          }
          finally {
              zk.delete(catalogUpdateBlockerPath, -1);
          }
  
          //Expect success
          client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL));
  
          client.drain();
          assertTrue(callbackSuccess);
          assertTrue(true);
      }
  
      public void testEnableSecurityAndHeartbeatTimeoutChange()
      throws IOException, ProcCallException, InterruptedException
      {
          System.out.println("\n\n-----\n testEnabledSecurity \n-----\n\n");
          Client client = getClient();
          loadSomeData(client, 0, 10);
          client.drain();
          assertTrue(callbackSuccess);
  
          String newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-base-secure.jar");
          String deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-base-secure.xml");
          VoltTable[] results = client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL)).getResults();
          assertTrue(results.length == 1);
  
          // a new client should need a username/password other than the regression suite default.
          boolean caught = false;
          try {
              getClient();
          } catch (IOException e) {
              caught = true;
          }
          assertTrue(caught);
  
          // create a valid client and call some procedures
          this.m_username = "user1";
          this.m_password = "userpass1";
          Client client3 = getClient();
          loadSomeData(client3, 50, 10);
          client3.drain();
          assertTrue(callbackSuccess);
  
          // the old client should not work because the user has been removed.
          loadSomeData(client, 100, 10);
          client.drain();
          assertFalse(callbackSuccess);
          callbackSuccess = true;
  
          boolean found = false;
          int timeout = -1;
          VoltTable result = client3.callProcedure("@SystemInformation", "DEPLOYMENT").getResults()[0];
          while (result.advanceRow()) {
              if (result.getString("PROPERTY").equalsIgnoreCase("heartbeattimeout")) {
                  found = true;
                  timeout = Integer.valueOf(result.getString("VALUE"));
              }
          }
          assertTrue(found);
          assertEquals(6000, timeout);
      }
  
      private void loadSomeData(Client client, int start, int count) throws IOException, ProcCallException {
          for (int i = start; i < (start + count); i++) {
              CatTestCallback callback = new CatTestCallback(ClientResponse.SUCCESS);
              client.callProcedure(callback, InsertNewOrder.class.getSimpleName(), i, i, (short)i);
          }
      }
  
      public void negativeTests(Client client) throws UnsupportedEncodingException {
          // this fails because the catalog URL isn't a real thing but needs to point at
          // a file that actually exists.  Point to the compiled java class for this suite
          URL url = TestCatalogUpdateSuite.class.getResource("TestCatalogUpdateSuite.class");
          String newCatalogURL = URLDecoder.decode(url.getPath(), "UTF-8");
          String deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-addtables.xml");
          try {
              client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL));
              fail();
          }
          catch (Exception e) {
              assertTrue(e.getMessage().startsWith("Database catalog not found"));
          }
      }
  
      public static long indexEntryCountFromStats(Client client, String tableName, String indexName) throws Exception {
          ClientResponse callProcedure = client.callProcedure("@Statistics", "INDEX", 0);
          assertTrue(callProcedure.getResults().length == 1);
          assertTrue(callProcedure.getStatus() == ClientResponse.SUCCESS);
          VoltTable result = callProcedure.getResults()[0];
          long tupleCount = 0;
          while (result.advanceRow()) {
              if (result.getString("TABLE_NAME").equals(tableName) && result.getString("INDEX_NAME").equals(indexName)) {
                  tupleCount += result.getLong("ENTRY_COUNT");
              }
          }
          return tupleCount;
      }
  
      public void testAddDropIndex() throws Exception
      {
          ClientResponse callProcedure;
          String explanation;
          VoltTable result;
  
          Client client = getClient();
          loadSomeData(client, 0, 10);
          client.drain();
          assertTrue(callbackSuccess);
  
          // check that no index was used by checking the plan itself
          callProcedure = client.callProcedure("@Explain", "select * from NEW_ORDER where NO_O_ID = 5;");
          explanation = callProcedure.getResults()[0].fetchRow(0).getString(0);
          assertFalse(explanation.contains("INDEX SCAN"));
  
          // add index to NEW_ORDER
          String newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-addindex.jar");
          String deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-addindex.xml");
          VoltTable[] results = client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL)).getResults();
          assertTrue(results.length == 1);
  
          // check the index for non-zero size
  
          long tupleCount = -1;
          while (tupleCount <= 0) {
              tupleCount = indexEntryCountFromStats(client, "NEW_ORDER", "NEWINDEX");
          }
          assertTrue(tupleCount > 0);
  
          // verify that the new table(s) support an insert
          callProcedure = client.callProcedure("@AdHoc", "insert into NEW_ORDER values (-1, -1, -1);");
          assertTrue(callProcedure.getResults().length == 1);
          assertTrue(callProcedure.getStatus() == ClientResponse.SUCCESS);
  
          // do a call that uses the index
          callProcedure = client.callProcedure("@AdHoc", "select * from NEW_ORDER where NO_O_ID = 5;");
          result = callProcedure.getResults()[0];
          result.advanceRow();
          assertEquals(5, result.getLong("NO_O_ID"));
  
          // check that an index was used by checking the plan itself
          callProcedure = client.callProcedure("@Explain", "select * from NEW_ORDER where NO_O_ID = 5;");
          explanation = callProcedure.getResults()[0].fetchRow(0).getString(0);
          assertTrue(explanation.contains("INDEX SCAN"));
  
          // tables can still be accessed
          loadSomeData(client, 20, 10);
          client.drain();
          assertTrue(callbackSuccess);
  
          // check table for the right number of tuples
          callProcedure = client.callProcedure("@AdHoc", "select count(*) from NEW_ORDER;");
          long rowCount = callProcedure.getResults()[0].asScalarLong();
          // check the index for even biggerer size from stats
          long newTupleCount = indexEntryCountFromStats(client, "NEW_ORDER", "NEWINDEX");
          while (newTupleCount != (rowCount * (K + 1))) {
              newTupleCount = indexEntryCountFromStats(client, "NEW_ORDER", "NEWINDEX");
          }
          assertTrue(newTupleCount > tupleCount);
          assertEquals(newTupleCount, rowCount * (K + 1)); // index count is double for k=1
  
          // revert to the original schema
          newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-base.jar");
          deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-base.xml");
          results = client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL)).getResults();
          assertTrue(results.length == 1);
  
          // do a call that uses the index
          callProcedure = client.callProcedure("@AdHoc", "select * from NEW_ORDER where NO_O_ID = 5;");
          result = callProcedure.getResults()[0];
          result.advanceRow();
          assertEquals(5, result.getLong("NO_O_ID"));
  
          // check that no index was used by checking the plan itself
          callProcedure = client.callProcedure("@Explain", "select * from NEW_ORDER where NO_O_ID = 5;");
          explanation = callProcedure.getResults()[0].fetchRow(0).getString(0);
          assertFalse(explanation.contains("INDEX SCAN"));
  
          // and loading still succeeds
          loadSomeData(client, 30, 10);
          client.drain();
          assertTrue(callbackSuccess);
      }
  
      public void testAddDropExpressionIndex() throws Exception
      {
          ClientResponse callProcedure;
          String explanation;
          VoltTable result;
  
          Client client = getClient();
          loadSomeData(client, 0, 10);
          client.drain();
          assertTrue(callbackSuccess);
  
          // check that no index was used by checking the plan itself
          callProcedure = client.callProcedure("@Explain", "select * from NEW_ORDER where (NO_O_ID+NO_O_ID)-NO_O_ID = 5;");
          explanation = callProcedure.getResults()[0].fetchRow(0).getString(0);
          assertFalse(explanation.contains("INDEX SCAN"));
  
          // add index to NEW_ORDER
          String newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-addexpressindex.jar");
          String deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-addexpressindex.xml");
          VoltTable[] results = client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL)).getResults();
          assertTrue(results.length == 1);
  
          // check the index for non-zero size
  
          long tupleCount = -1;
          while (tupleCount <= 0) {
              tupleCount = indexEntryCountFromStats(client, "NEW_ORDER", "NEWEXPRESSINDEX");
          }
          assertTrue(tupleCount > 0);
  
          // verify that the new table(s) support an insert
          callProcedure = client.callProcedure("@AdHoc", "insert into NEW_ORDER values (-1, -1, -1);");
          assertTrue(callProcedure.getResults().length == 1);
          assertTrue(callProcedure.getStatus() == ClientResponse.SUCCESS);
  
          // do a call that uses the index
          callProcedure = client.callProcedure("@AdHoc", "select * from NEW_ORDER where (NO_O_ID+NO_O_ID)-NO_O_ID = 5;");
          result = callProcedure.getResults()[0];
          result.advanceRow();
          assertEquals(5, result.getLong("NO_O_ID"));
  
          // check that an index was used by checking the plan itself
          callProcedure = client.callProcedure("@Explain", "select * from NEW_ORDER where (NO_O_ID+NO_O_ID)-NO_O_ID = 5;");
          explanation = callProcedure.getResults()[0].fetchRow(0).getString(0);
          assertTrue(explanation.contains("INDEX SCAN"));
  
          // tables can still be accessed
          loadSomeData(client, 20, 10);
          client.drain();
          assertTrue(callbackSuccess);
  
  
          // check table for the right number of tuples
          callProcedure = client.callProcedure("@AdHoc", "select count(*) from NEW_ORDER;");
          long rowCount = callProcedure.getResults()[0].asScalarLong();
          // check the index for even biggerer size from stats
          long newTupleCount = -1;
          while (newTupleCount != (rowCount * (K + 1))) {
              newTupleCount = indexEntryCountFromStats(client, "NEW_ORDER", "NEWEXPRESSINDEX");
          }
          assertTrue(newTupleCount > tupleCount);
          assertEquals(newTupleCount, rowCount * (K + 1)); // index count is double for k=1
  
          // revert to the original schema
          newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-base.jar");
          deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-base.xml");
          results = client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL)).getResults();
          assertTrue(results.length == 1);
  
          // do a call that uses the index
          callProcedure = client.callProcedure("@AdHoc", "select * from NEW_ORDER where (NO_O_ID+NO_O_ID)-NO_O_ID = 5;");
          result = callProcedure.getResults()[0];
          result.advanceRow();
          assertEquals(5, result.getLong("NO_O_ID"));
  
          // check that no index was used by checking the plan itself
          callProcedure = client.callProcedure("@Explain", "select * from NEW_ORDER where (NO_O_ID+NO_O_ID)-NO_O_ID = 5;");
          explanation = callProcedure.getResults()[0].fetchRow(0).getString(0);
          assertFalse(explanation.contains("INDEX SCAN"));
  
          // and loading still succeeds
          loadSomeData(client, 30, 10);
          client.drain();
          assertTrue(callbackSuccess);
      }
  
      public void testAddDropTable() throws IOException, ProcCallException, InterruptedException
      {
          Client client = getClient();
          loadSomeData(client, 0, 10);
          client.drain();
          assertTrue(callbackSuccess);
  
          // verify that an insert w/o a table fails.
          try {
              client.callProcedure("@AdHoc", "insert into O1 values (1, 1, 'foo', 'foobar');");
              fail();
          }
          catch (ProcCallException e) {
          }
  
          // Also can't call this not-yet-existing stored procedure
          try {
              client.callProcedure("InsertO1", new Integer(100), new Integer(200), "foo", "bar");
              fail();
          }
          catch (ProcCallException e) {
          }
  
          // add tables O1, O2, O3
          String newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-addtables.jar");
          String deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-addtables.xml");
          VoltTable[] results = client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL)).getResults();
          assertTrue(results.length == 1);
  
          // verify that the new table(s) support an insert
          ClientResponse callProcedure = client.callProcedure("@AdHoc", "insert into O1 values (1, 1, 'foo', 'foobar');");
          assertTrue(callProcedure.getResults().length == 1);
          assertTrue(callProcedure.getStatus() == ClientResponse.SUCCESS);
  
          callProcedure = client.callProcedure("@AdHoc", "insert into O2 values (1, 1, 'foo', 'foobar');");
          assertTrue(callProcedure.getResults().length == 1);
          assertTrue(callProcedure.getStatus() == ClientResponse.SUCCESS);
  
          callProcedure = client.callProcedure("@AdHoc", "select * from O1");
          VoltTable result = callProcedure.getResults()[0];
          result.advanceRow();
          assertTrue(result.get(2, VoltType.STRING).equals("foo"));
  
          // old tables can still be accessed
          loadSomeData(client, 20, 10);
          client.drain();
          assertTrue(callbackSuccess);
  
          // and this new procedure is happy like clams
          callProcedure = client.callProcedure("InsertO1", new Integer(100), new Integer(200), "foo", "bar");
          assertTrue(callProcedure.getResults().length == 1);
          assertTrue(callProcedure.getStatus() == ClientResponse.SUCCESS);
  
          // revert to the original schema
          newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-base.jar");
          deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-base.xml");
          results = client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL)).getResults();
          assertTrue(results.length == 1);
  
          // requests to the dropped table should fail
          try {
              client.callProcedure("@AdHoc", "insert into O1 values (1, 1, 'foo', 'foobar');");
              fail();
          }
          catch (ProcCallException e) {
          }
  
          try {
              client.callProcedure("InsertO1", new Integer(100), new Integer(200), "foo", "bar");
              fail();
          }
          catch (ProcCallException e) {
          }
  
          // and other requests still succeed
          loadSomeData(client, 30, 10);
          client.drain();
          assertTrue(callbackSuccess);
      }
  
      public void testAddTableWithMatView() throws IOException, ProcCallException, InterruptedException {
          Client client = getClient();
          loadSomeData(client, 0, 10);
          client.drain();
          assertTrue(callbackSuccess);
  
          // add new tables and materialized view
          String newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-addtableswithmatview.jar");
          String deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-addtableswithmatview.xml");
          VoltTable[] results = client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL)).getResults();
          assertTrue(results.length == 1);
  
          // verify that the new table(s) support an insert
          for (int i=0; i < 10; ++i) {
              ClientResponse callProcedure = client.callProcedure("@AdHoc", "insert into O1 values (" + i + ", " + i % 2 + ", 'foo', 'foobar');");
              assertTrue(callProcedure.getResults().length == 1);
              assertTrue(callProcedure.getStatus() == ClientResponse.SUCCESS);
          }
  
          // read it - expect 10 rows
          ClientResponse callProcedure = client.callProcedure("@AdHoc", "select * from O1");
          VoltTable result = callProcedure.getResults()[0];
          assertTrue(result.getRowCount() == 10);
  
          // read the mat view. expect two rows (grouped on x % 2)
          callProcedure = client.callProcedure("@AdHoc", "select C1,NUM from MATVIEW_O1 order by C1");
          result = callProcedure.getResults()[0];
  
          System.out.println("MATVIEW:"); System.out.println(result);
          assertEquals(10, result.getRowCount());
      }
  
      private void loadSomeDataForNewTable(Client client, int start, int count) throws IOException, ProcCallException {
          for (int i = start; i < (start + count); i++) {
              CatTestCallback callback = new CatTestCallback(ClientResponse.SUCCESS);
              client.callProcedure(callback, "O1.insert", i, i, "abcdefg", "voltdb is a great startup with potential success in future");
          }
      }
  
      public void testAddDropTableRepeat() throws Exception {
          Client client = getClient();
          loadSomeData(client, 0, 10);
          client.drain();
          assertTrue(callbackSuccess);
  
          /*
           * Reduced from 100 to 30 so that it doesn't take quite as long
           * We run tests often enough that this will get plenty of fuzzing.
           */
          for (int i=0; i < 30; i++)
          {
              // add tables O1, O2, O3
              String newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-addtables.jar");
              String deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-addtables.xml");
              VoltTable[] results = client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL)).getResults();
              assertTrue(results.length == 1);
  
              // Thread.sleep(2000);
              // Load table into the new added tables
              loadSomeDataForNewTable(client, 0, 1000 *10);
              client.drain();
  
              // revert to the original schema
              newCatalogURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-base.jar");
              deploymentURL = Configuration.getPathToCatalogForTest("catalogupdate-cluster-base.xml");
              results = client.updateApplicationCatalog(new File(newCatalogURL), new File(deploymentURL)).getResults();
              assertTrue(results.length == 1);
          }
      }
  
      public void testUpdateHonkingBigCatalog() throws IOException, ProcCallException, InterruptedException {
          System.out.println("\n\n-----\n testUpdateHonkingBigCatalog\n");
          System.out.printf("jar: %s (%.2f MB)\n", hugeCatalogJarPath, new File(hugeCatalogJarPath).length() / 1048576.0);
          System.out.printf("compile: %.2f seconds (%.2f/second)\n", hugeCompileElapsed, HUGE_TABLES / hugeCompileElapsed);
          long t = System.currentTimeMillis();
          Client client = getClient();
          loadSomeData(client, 0, 10);
          client.drain();
          assertTrue(callbackSuccess);
  
          try {
              VoltTable[] results = client.updateApplicationCatalog(new File(hugeCatalogJarPath), new File(hugeCatalogXMLPath)).getResults();
              assertTrue(results.length == 1);
          }
          catch (ProcCallException e) {
              fail(String.format("@UpdateApplicationCatalog: ProcCallException: %s", e.getLocalizedMessage()));
          }
          hugeTestElapsed = (System.currentTimeMillis() - t) / 1000.0;
          System.out.printf("test: %.2f seconds (%.2f/second)\n", hugeTestElapsed, HUGE_TABLES / hugeTestElapsed);
          System.out.println("-----\n\n");
      }
  
      private void deleteDirectory(File dir) {
          if (!dir.exists() || !dir.isDirectory()) {
              return;
          }
  
          for (File f : dir.listFiles()) {
              assertTrue(f.delete());
          }
          assertTrue(dir.delete());
      }
  
      private static String generateRandomDDL(String name, int ntables, int ncols, int width)
              throws IOException {
          // Generate huge DDL file. Make it relatively uncompressible with randomness.
          File temp = File.createTempFile(name, ".sql");
          temp.deleteOnExit();
          FileWriter out = new FileWriter(temp);
          char[] charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_".toCharArray();
          Random random = new Random(99);
          for (int itable = 0; itable < ntables; itable++) {
              out.write(String.format("\nCREATE TABLE HUGE_TABLE_%d (\n", itable));
              out.write("C_FIRST INTEGER,\n");
              for (int icolumn = 0; icolumn < ncols; icolumn++) {
                  String columnID = RandomStringUtils.random(width,
                                                             0,
                                                             charset.length,
                                                             false,
                                                             false,
                                                             charset,
                                                             random);
                  out.write(String.format("C_%s INTEGER,\n", columnID));
              }
              out.write("PRIMARY KEY (C_FIRST));\n");
          }
          out.close();
          return URLEncoder.encode(temp.getAbsolutePath(), "UTF-8");
      }
  
      /**
       * Build a list of the tests that will be run when TestTPCCSuite gets run by JUnit.
       * Use helper classes that are part of the RegressionSuite framework.
       * This particular class runs all tests on the the local JNI backend with both
       * one and two partition configurations, as well as on the hsql backend.
       *
       * @return The TestSuite containing all the tests to be run.
       * @throws Exception
       */
      static public Test suite() throws Exception {
          TheHashinator.initialize(TheHashinator.getConfiguredHashinatorClass(), TheHashinator.getConfigureBytes(2));
  
          // the suite made here will all be using the tests from this class
          MultiConfigSuiteBuilder builder = new MultiConfigSuiteBuilder(TestCatalogUpdateSuite.class);
  
          /////////////////////////////////////////////////////////////
          // CONFIG #1: 1 Local Site/Partitions running on JNI backend
          /////////////////////////////////////////////////////////////
  
          // get a server config for the native backend with one sites/partitions
          VoltServerConfig config = new LocalCluster("catalogupdate-cluster-base.jar", SITES_PER_HOST, HOSTS, K, BackendTarget.NATIVE_EE_JNI);
  
          // Catalog upgrade test(s) sporadically fail if there's a local server because
          // a file pipe isn't available for grepping local server output.
          ((LocalCluster) config).setHasLocalServer(true);
  
          // build up a project builder for the workload
          TPCCProjectBuilder project = new TPCCProjectBuilder();
          project.addDefaultSchema();
          project.addDefaultPartitioning();
          project.addProcedures(BASEPROCS);
          // build the jarfile
          boolean basecompile = config.compile(project);
          assertTrue(basecompile);
          MiscUtils.copyFile(project.getPathToDeployment(), Configuration.getPathToCatalogForTest("catalogupdate-cluster-base.xml"));
  
          // add this config to the set of tests to run
          builder.addServerConfig(config);
  
          /////////////////////////////////////////////////////////////
          // DELTA CATALOGS FOR TESTING
          /////////////////////////////////////////////////////////////
  
          // As catalogupdate-cluster-base but with security enabled. This requires users and groups..
          // We piggy-back the heartbeat change here.
          GroupInfo groups[] = new GroupInfo[] {new GroupInfo("group1", false, true, false)};
          UserInfo users[] = new UserInfo[] {new UserInfo("user1", "userpass1", new String[] {"group1"})};
          ProcedureInfo procInfo = new ProcedureInfo(new String[] {"group1"}, InsertNewOrder.class);
  
          config = new LocalCluster("catalogupdate-cluster-base-secure.jar", SITES_PER_HOST, HOSTS, K, BackendTarget.NATIVE_EE_JNI);
          project = new TPCCProjectBuilder();
          project.addDefaultSchema();
          project.addDefaultPartitioning();
          project.addUsers(users);
          project.addGroups(groups);
          project.addProcedures(procInfo);
          project.setSecurityEnabled(true);
          project.setDeadHostTimeout(6000);
  