phoenix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jamestay...@apache.org
Subject git commit: PHOENIX-1098 Support CASCADE option on DROP TABLE that drops all VIEWs (Jan Fernando)
Date Thu, 04 Sep 2014 07:14:43 GMT
Repository: phoenix
Updated Branches:
  refs/heads/3.0 be6465a89 -> 3af2450d3


PHOENIX-1098 Support CASCADE option on DROP TABLE that drops all VIEWs (Jan Fernando)


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/3af2450d
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/3af2450d
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/3af2450d

Branch: refs/heads/3.0
Commit: 3af2450d31ec53dbc84fcf3ad8208836acb1bba1
Parents: be6465a
Author: James Taylor <jtaylor@salesforce.com>
Authored: Thu Sep 4 00:18:14 2014 -0700
Committer: James Taylor <jtaylor@salesforce.com>
Committed: Thu Sep 4 00:18:14 2014 -0700

----------------------------------------------------------------------
 .../end2end/TenantSpecificTablesDDLIT.java      | 108 ++++++++++++++-
 .../java/org/apache/phoenix/end2end/ViewIT.java |  90 ++++++++++++-
 phoenix-core/src/main/antlr3/PhoenixSQL.g       |   5 +-
 .../coprocessor/MetaDataEndpointImpl.java       | 135 ++++++++++++++++---
 .../phoenix/coprocessor/MetaDataProtocol.java   |  11 +-
 .../apache/phoenix/jdbc/PhoenixStatement.java   |   8 +-
 .../phoenix/parse/DropTableStatement.java       |   9 +-
 .../apache/phoenix/parse/ParseNodeFactory.java  |   4 +-
 .../phoenix/query/ConnectionQueryServices.java  |   2 +-
 .../query/ConnectionQueryServicesImpl.java      |   4 +-
 .../query/ConnectionlessQueryServicesImpl.java  |   2 +-
 .../query/DelegateConnectionQueryServices.java  |   4 +-
 .../apache/phoenix/schema/MetaDataClient.java   |   8 +-
 13 files changed, 347 insertions(+), 43 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/3af2450d/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificTablesDDLIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificTablesDDLIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificTablesDDLIT.java
index 79aa6c1..591efe1 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificTablesDDLIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificTablesDDLIT.java
@@ -331,7 +331,7 @@ public class TenantSpecificTablesDDLIT extends BaseTenantSpecificTablesIT
{
     }
     
     @Test
-    public void testDropParentTableWithExistingTenantTable() throws Exception {
+    public void testDisallowDropParentTableWithExistingTenantTable() throws Exception {
         Properties props = new Properties();
         props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(nextTimestamp()));
         Connection conn = DriverManager.getConnection(getUrl(), props);
@@ -348,6 +348,112 @@ public class TenantSpecificTablesDDLIT extends BaseTenantSpecificTablesIT
{
     }
     
     @Test
+    public void testAllowDropParentTableWithCascadeAndSingleTenantTable() throws Exception
{
+	    long ts = nextTimestamp();
+	    Properties props = new Properties();
+	    props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts));
+	    Connection conn = DriverManager.getConnection(getUrl(), props);
+	    Connection connTenant = null;
+    
+		try {
+			// Drop Parent Table 
+			conn.createStatement().executeUpdate("DROP TABLE " + PARENT_TABLE_NAME + " CASCADE");
+			conn.close();
+		      
+			props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 10));
+			connTenant = DriverManager.getConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL, props);
+			
+	        validateTenantViewIsDropped(conn);		
+	    } finally {
+	    	if (conn != null) {
+	    		conn.close();
+	    	}
+	    	if (connTenant != null) {
+	    		connTenant.close();
+	    	}
+	    }
+    }
+    
+    
+    @Test
+    public void testAllDropParentTableWithCascadeWithMultipleTenantTablesAndIndexes() throws
Exception {
+        // Create a second tenant table
+    	createTestTable(PHOENIX_JDBC_TENANT_SPECIFIC_URL2, TENANT_TABLE_DDL, null, nextTimestamp());
+    	//TODO Create some tenant specific table indexes
+        
+	    long ts = nextTimestamp();
+	    Properties props = new Properties();
+	    props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts));
+	    Connection conn = null;
+	    Connection connTenant1 = null;
+	    Connection connTenant2 = null;
+    
+		try {
+			conn = DriverManager.getConnection(getUrl(), props);
+	        DatabaseMetaData meta = conn.getMetaData();
+            ResultSet rs = meta.getSuperTables(null, null, StringUtil.escapeLike(TENANT_TABLE_NAME)
+ "%");
+            assertTrue(rs.next());
+            assertEquals(TENANT_ID2, rs.getString(PhoenixDatabaseMetaData.TABLE_CAT));
+            assertEquals(TENANT_TABLE_NAME, rs.getString(PhoenixDatabaseMetaData.TABLE_NAME));
+            assertEquals(PARENT_TABLE_NAME, rs.getString(PhoenixDatabaseMetaData.SUPERTABLE_NAME));
+            assertTrue(rs.next());
+            assertEquals(TENANT_ID, rs.getString(PhoenixDatabaseMetaData.TABLE_CAT));
+            assertEquals(TENANT_TABLE_NAME, rs.getString(PhoenixDatabaseMetaData.TABLE_NAME));
+            assertEquals(PARENT_TABLE_NAME, rs.getString(PhoenixDatabaseMetaData.SUPERTABLE_NAME));
+            assertTrue(rs.next());
+            assertEquals(TENANT_ID, rs.getString(PhoenixDatabaseMetaData.TABLE_CAT));
+            assertEquals(TENANT_TABLE_NAME_NO_TENANT_TYPE_ID, rs.getString(PhoenixDatabaseMetaData.TABLE_NAME));
+            assertEquals(PARENT_TABLE_NAME_NO_TENANT_TYPE_ID, rs.getString(PhoenixDatabaseMetaData.SUPERTABLE_NAME));
+            assertFalse(rs.next());
+            rs.close();
+            conn.close();
+            
+			// Drop Parent Table 
+			conn.createStatement().executeUpdate("DROP TABLE " + PARENT_TABLE_NAME + " CASCADE");
+		  
+			// Validate Tenant Views are dropped
+			props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 10));
+			connTenant1 = DriverManager.getConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL, props);
+	        validateTenantViewIsDropped(connTenant1);
+			connTenant2 = DriverManager.getConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL2, props);
+	        validateTenantViewIsDropped(connTenant2);
+	        
+	        // Validate Tenant Metadata is gone for the Tenant Table TENANT_TABLE_NAME
+			conn = DriverManager.getConnection(getUrl(), props);
+	        meta = conn.getMetaData();
+            rs = meta.getSuperTables(null, null, StringUtil.escapeLike(TENANT_TABLE_NAME)
+ "%");
+            assertTrue(rs.next());
+            assertEquals(TENANT_ID, rs.getString(PhoenixDatabaseMetaData.TABLE_CAT));
+            assertEquals(TENANT_TABLE_NAME_NO_TENANT_TYPE_ID, rs.getString(PhoenixDatabaseMetaData.TABLE_NAME));
+            assertEquals(PARENT_TABLE_NAME_NO_TENANT_TYPE_ID, rs.getString(PhoenixDatabaseMetaData.SUPERTABLE_NAME));
+            assertFalse(rs.next());
+            rs.close();
+	        
+	    } finally {
+	    	if (conn != null) {
+	    		conn.close();
+	    	}
+	    	if (connTenant1 != null) {
+	    		connTenant1.close();
+	    	}
+	    	if (connTenant2 != null) {
+	    		connTenant2.close();
+	    	}
+	    }
+    }
+
+	private void validateTenantViewIsDropped(Connection connTenant)	throws SQLException {
+		// Try and drop tenant view, should throw TableNotFoundException
+		try {
+			String ddl = "DROP VIEW " + TENANT_TABLE_NAME;
+		    connTenant.createStatement().execute(ddl);
+		    fail("Tenant specific view " + TENANT_TABLE_NAME + " should have been dropped when
parent was dropped");
+		} catch (TableNotFoundException e) {
+			//Expected
+		}
+	}
+    
+    @Test
     public void testTableMetadataScan() throws Exception {
         // create a tenant table with same name for a different tenant to make sure we are
not picking it up in metadata scans for TENANT_ID
         String tenantId2 = "tenant2";

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3af2450d/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
index 394fa04..8ef1024 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
@@ -259,15 +259,99 @@ public class ViewIT extends BaseViewIT {
         } catch (TableNotFoundException ignore) {
         }
         ddl = "DROP TABLE s1.t";
+        validateCannotDropTableWithChildViewsWithoutCascade(conn, "s1.t");
+        ddl = "DROP VIEW v2";
+        conn.createStatement().execute(ddl);
+        ddl = "DROP TABLE s1.t";
+        conn.createStatement().execute(ddl);
+    }
+
+    
+    @Test
+    public void testDisallowDropOfColumnOnParentTable() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        String ddl = "CREATE TABLE tp (k1 INTEGER NOT NULL, k2 INTEGER NOT NULL, v1 DECIMAL,
CONSTRAINT pk PRIMARY KEY (k1, k2))";
+        conn.createStatement().execute(ddl);
+        ddl = "CREATE VIEW v1(v2 VARCHAR, v3 VARCHAR) AS SELECT * FROM tp WHERE v1 = 1.0";
+        conn.createStatement().execute(ddl);
+        
         try {
-            conn.createStatement().execute(ddl);
+            conn.createStatement().execute("ALTER TABLE tp DROP COLUMN v1");
             fail();
         } catch (SQLException e) {
             assertEquals(SQLExceptionCode.CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
         }
-        ddl = "DROP VIEW v2";
+    }
+   
+    @Test
+    public void testViewAndTableAndDropCascade() throws Exception {
+    	// Setup
+        Connection conn = DriverManager.getConnection(getUrl());
+        String ddl = "CREATE TABLE s2.t (k INTEGER NOT NULL PRIMARY KEY, v1 DATE)";
         conn.createStatement().execute(ddl);
-        ddl = "DROP TABLE s1.t";
+        ddl = "CREATE VIEW s2.v1 (v2 VARCHAR) AS SELECT * FROM s2.t WHERE k > 5";
+        conn.createStatement().execute(ddl);
+        ddl = "CREATE VIEW s2.v2 (v2 VARCHAR) AS SELECT * FROM s2.t WHERE k > 10";
+        conn.createStatement().execute(ddl);
+
+        validateCannotDropTableWithChildViewsWithoutCascade(conn, "s2.t");
+        
+        // Execute DROP...CASCADE
+        conn.createStatement().execute("DROP TABLE s2.t CASCADE");
+        
+        validateViewDoesNotExist(conn, "s2.v1");
+        validateViewDoesNotExist(conn, "s2.v2");
+    }
+    
+    @Test
+    public void testViewAndTableAndDropCascadeWithIndexes() throws Exception {
+        
+    	// Setup - Tables and Views with Indexes
+    	Connection conn = DriverManager.getConnection(getUrl());
+    	
+        String ddl = "CREATE TABLE s3.t (k INTEGER NOT NULL PRIMARY KEY, v1 DATE) IMMUTABLE_ROWS=true";
+        conn.createStatement().execute(ddl);
+        ddl = "CREATE INDEX IDX1 ON s3.t (v1)";
+        conn.createStatement().execute(ddl);
+        ddl = "CREATE VIEW s3.v1 (v2 VARCHAR) AS SELECT * FROM s3.t WHERE k > 5";
+        conn.createStatement().execute(ddl);
+        ddl = "CREATE INDEX IDX2 ON s3.v1 (v2)";
+        conn.createStatement().execute(ddl);
+        ddl = "CREATE VIEW s3.v2 (v2 VARCHAR) AS SELECT * FROM s3.t WHERE k > 10";
+        conn.createStatement().execute(ddl);
+        ddl = "CREATE INDEX IDX3 ON s3.v2 (v2)";
         conn.createStatement().execute(ddl);
+
+        validateCannotDropTableWithChildViewsWithoutCascade(conn, "s3.t");
+        
+        // Execute DROP...CASCADE
+        conn.createStatement().execute("DROP TABLE s3.t CASCADE");
+        
+        // Validate Views were deleted - Try and delete child views, should throw TableNotFoundException
+        validateViewDoesNotExist(conn, "s3.v1");
+        validateViewDoesNotExist(conn, "s3.v2");
     }
+
+
+	private void validateCannotDropTableWithChildViewsWithoutCascade(Connection conn, String
tableName) throws SQLException {
+		String ddl;
+		try {
+	        ddl = "DROP TABLE " + tableName;
+	        conn.createStatement().execute(ddl);
+	        fail("Should not be able to drop table " + tableName + " with child views without
explictly specifying CASCADE");
+        }  catch (SQLException e) {
+            assertEquals(SQLExceptionCode.CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+        }
+	}
+
+
+	private void validateViewDoesNotExist(Connection conn, String viewName)	throws SQLException
{
+		try {
+        	String ddl1 = "DROP VIEW " + viewName;
+            conn.createStatement().execute(ddl1);
+            fail("View s3.v1 should have been deleted when parent was dropped");
+        } catch (TableNotFoundException e) {
+        	//Expected
+        }
+	}
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3af2450d/phoenix-core/src/main/antlr3/PhoenixSQL.g
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/antlr3/PhoenixSQL.g b/phoenix-core/src/main/antlr3/PhoenixSQL.g
index d1b69b8..6cc8d57 100644
--- a/phoenix-core/src/main/antlr3/PhoenixSQL.g
+++ b/phoenix-core/src/main/antlr3/PhoenixSQL.g
@@ -104,6 +104,7 @@ tokens
     MINVALUE='minvalue';
     MAXVALUE='maxvalue';
     CYCLE='cycle';
+    CASCADE='cascade';
 }
 
 
@@ -469,8 +470,8 @@ column_names returns [List<ColumnName> ret]
 	
 // Parse a drop table statement.
 drop_table_node returns [DropTableStatement ret]
-    :   DROP (v=VIEW | TABLE) (IF ex=EXISTS)? t=from_table_name
-        {ret = factory.dropTable(t, v==null ? PTableType.TABLE : PTableType.VIEW, ex!=null);
}
+    :   DROP (v=VIEW | TABLE) (IF ex=EXISTS)? t=from_table_name (c=CASCADE)?
+        {ret = factory.dropTable(t, v==null ? PTableType.TABLE : PTableType.VIEW, ex!=null,
c!=null); }
     ;
 
 // Parse a drop index statement

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3af2450d/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
index b7343d1..9af606e 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
@@ -77,10 +77,12 @@ import org.apache.hadoop.hbase.filter.Filter;
 import org.apache.hadoop.hbase.filter.FilterList;
 import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
 import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.hadoop.hbase.regionserver.HRegion;
 import org.apache.hadoop.hbase.regionserver.RegionScanner;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
+import org.apache.hadoop.io.WritableUtils;
 import org.apache.phoenix.cache.GlobalCache;
 import org.apache.phoenix.hbase.index.util.GenericKeyValueBuilder;
 import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
@@ -104,6 +106,7 @@ import org.apache.phoenix.schema.PTableImpl;
 import org.apache.phoenix.schema.PTableType;
 import org.apache.phoenix.schema.SortOrder;
 import org.apache.phoenix.schema.TableNotFoundException;
+import org.apache.phoenix.schema.tuple.ResultTuple;
 import org.apache.phoenix.util.ByteUtil;
 import org.apache.phoenix.util.IndexUtil;
 import org.apache.phoenix.util.KeyValueUtil;
@@ -597,10 +600,10 @@ public class MetaDataEndpointImpl extends BaseEndpointCoprocessor implements
Met
     protected static final byte[] PHYSICAL_TABLE_BYTES = new byte[] {PTable.LinkType.PHYSICAL_TABLE.getSerializedValue()};
     /**
      * @param tableName parent table's name
-     * @return true if there exist a table that use this table as their base table.
+     * Looks for whether child views exist for the table specified by table.
      * TODO: should we pass a timestamp here?
      */
-    private boolean hasViews(HRegion region, byte[] tenantId, PTable table) throws IOException
{
+    private TableViewFinderResult findChildViews(HRegion region, byte[] tenantId, PTable
table) throws IOException {
         byte[] schemaName = table.getSchemaName().getBytes();
         byte[] tableName = table.getTableName().getBytes();
         boolean isMultiTenant = table.isMultiTenant();
@@ -622,22 +625,42 @@ public class MetaDataEndpointImpl extends BaseEndpointCoprocessor implements
Met
         scan.setFilter(filter);
         scan.addColumn(TABLE_FAMILY_BYTES, LINK_TYPE_BYTES);
         HTableInterface hTable = getEnvironment().getTable(PhoenixDatabaseMetaData.SYSTEM_CATALOG_NAME_BYTES);
+        ResultScanner scanner = hTable.getScanner(scan);
+
+        boolean allViewsInCurrentRegion = true;
+        int numOfChildViews = 0;
+        List<Result> results = Lists.newArrayList();
         try {
-            ResultScanner scanner = hTable.getScanner(scan);
-            try {
-                Result result = scanner.next();
-                return result != null;
-            }
-            finally {
-                scanner.close();
+            for (Result result = scanner.next(); (result != null); result = scanner.next())
{
+                numOfChildViews++;
+                ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+                ResultTuple resultTuple = new ResultTuple(result);
+                resultTuple.getKey(ptr);
+                byte[] key = ptr.copyBytes();
+                if (checkTableKeyInRegion(key, region) != null) {
+                    allViewsInCurrentRegion = false;
+                }
+                results.add(result);
             }
         } finally {
+            scanner.close();
             hTable.close();
         }
+        TableViewFinderResult tableViewFinderResult = new TableViewFinderResult(results);
+        if (numOfChildViews > 0 && !allViewsInCurrentRegion) {
+            tableViewFinderResult.setAllViewsNotInSingleRegion();
+        }
+        return tableViewFinderResult;
+
     }
-    
+
     @Override
     public MetaDataMutationResult dropTable(List<Mutation> tableMetadata, String tableType)
throws IOException {
+        return dropTable(tableMetadata, tableType, false);
+    }
+    
+    @Override
+    public MetaDataMutationResult dropTable(List<Mutation> tableMetadata, String tableType,
boolean isCascade) throws IOException {
         byte[][] rowKeyMetaData = new byte[3][];
         MetaDataUtil.getTenantIdAndSchemaAndTableName(tableMetadata,rowKeyMetaData);
         byte[] tenantIdBytes = rowKeyMetaData[PhoenixDatabaseMetaData.TENANT_ID_INDEX];
@@ -667,7 +690,7 @@ public class MetaDataEndpointImpl extends BaseEndpointCoprocessor implements
Met
                     acquireLock(region, key, lids);
                 }
                 List<ImmutableBytesPtr> invalidateList = new ArrayList<ImmutableBytesPtr>();
-                result = doDropTable(key, tenantIdBytes, schemaName, tableName, parentTableName,
PTableType.fromSerializedValue(tableType), tableMetadata, invalidateList, lids, tableNamesToDelete);
+                result = doDropTable(key, tenantIdBytes, schemaName, tableName, parentTableName,
PTableType.fromSerializedValue(tableType), tableMetadata, invalidateList, lids, tableNamesToDelete,
isCascade);
                 if (result.getMutationCode() != MutationCode.TABLE_ALREADY_EXISTS) {
                     return result;
                 }
@@ -693,7 +716,8 @@ public class MetaDataEndpointImpl extends BaseEndpointCoprocessor implements
Met
     }
 
     private MetaDataMutationResult doDropTable(byte[] key, byte[] tenantId, byte[] schemaName,
byte[] tableName, byte[] parentTableName, 
-            PTableType tableType, List<Mutation> rowsToDelete, List<ImmutableBytesPtr>
invalidateList, List<Integer> lids, List<byte[]> tableNamesToDelete) throws IOException,
SQLException {
+            PTableType tableType, List<Mutation> rowsToDelete, List<ImmutableBytesPtr>
invalidateList, List<Integer> lids, List<byte[]> tableNamesToDelete,
+            boolean isCascade) throws IOException, SQLException {
         long clientTimeStamp = MetaDataUtil.getClientTimeStamp(rowsToDelete);
 
         RegionCoprocessorEnvironment env = getEnvironment();
@@ -702,7 +726,7 @@ public class MetaDataEndpointImpl extends BaseEndpointCoprocessor implements
Met
         
         Cache<ImmutableBytesPtr,PTable> metaDataCache = GlobalCache.getInstance(this.getEnvironment()).getMetaDataCache();
         PTable table = metaDataCache.getIfPresent(cacheKey);
-        
+       
         // We always cache the latest version - fault in if not in cache
         if (table != null || (table = buildTable(key, cacheKey, region, HConstants.LATEST_TIMESTAMP))
!= null) {
             if (table.getTimeStamp() < clientTimeStamp) {
@@ -736,9 +760,41 @@ public class MetaDataEndpointImpl extends BaseEndpointCoprocessor implements
Met
         if (results.isEmpty()) { // Should not be possible
             return new MetaDataMutationResult(MutationCode.TABLE_NOT_FOUND, EnvironmentEdgeManager.currentTimeMillis(),
null);
         }
-        if (hasViews(region, tenantId, table)) {
-            return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION, EnvironmentEdgeManager.currentTimeMillis(),
null);
-        }
+
+        // Handle any child views that exist
+        TableViewFinderResult tableViewFinderResult = findChildViews(region, tenantId, table);
+        if (tableViewFinderResult.hasViews()) {
+        	if (isCascade) {
+		        if (tableViewFinderResult.allViewsInMultipleRegions()) {
+		            // We don't yet support deleting a table with views where SYSTEM.CATALOG has
split and the 
+		        	// view metadata spans multiple regions
+		        	return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION, EnvironmentEdgeManager.currentTimeMillis(),
null);
+		        } else if (tableViewFinderResult.allViewsInSingleRegion()) {
+		        	// Recursively delete views - safe as all the views as all in the same region
+		        	for (Result viewResult : tableViewFinderResult.getResults()) {
+		                byte[][] rowKeyMetaData = new byte[3][];
+		                getVarChars(viewResult.getRow(), 3, rowKeyMetaData);
+		                byte[] viewTenantId = rowKeyMetaData[PhoenixDatabaseMetaData.TENANT_ID_INDEX];
+		                byte[] viewSchemaName = rowKeyMetaData[PhoenixDatabaseMetaData.SCHEMA_NAME_INDEX];
+		                byte[] viewName = rowKeyMetaData[PhoenixDatabaseMetaData.TABLE_NAME_INDEX];
+		            	byte[] viewKey = SchemaUtil.getTableKey(viewTenantId, viewSchemaName, viewName);
+		                Delete delete = new Delete(viewKey, clientTimeStamp);
+		                rowsToDelete.add(delete);
+		                acquireLock(region, viewKey, lids);
+		                MetaDataMutationResult result =
+		                        doDropTable(viewKey, viewTenantId, viewSchemaName, viewName, null,
PTableType.VIEW,
+		                            rowsToDelete, invalidateList, lids, tableNamesToDelete, false);
+		                if (result.getMutationCode() != MutationCode.TABLE_ALREADY_EXISTS) {
+		                    return result;
+		                }
+					}
+		        }	
+        	} else {
+            	// DROP without CASCADE on tables with child views is not permitted
+            	return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION, EnvironmentEdgeManager.currentTimeMillis(),
null);
+            }
+        } 
+        
         if (tableType != PTableType.VIEW) { // Add to list of HTables to delete, unless it's
a view
             tableNamesToDelete.add(table.getName().getBytes());
         }
@@ -775,7 +831,7 @@ public class MetaDataEndpointImpl extends BaseEndpointCoprocessor implements
Met
             Delete delete = new Delete(indexKey, clientTimeStamp, null);
             rowsToDelete.add(delete);
             acquireLock(region, indexKey, lids);
-            MetaDataMutationResult result = doDropTable(indexKey, tenantId, schemaName, indexName,
tableName, PTableType.INDEX, rowsToDelete, invalidateList, lids, tableNamesToDelete);
+            MetaDataMutationResult result = doDropTable(indexKey, tenantId, schemaName, indexName,
tableName, PTableType.INDEX, rowsToDelete, invalidateList, lids, tableNamesToDelete, false);
             if (result.getMutationCode() != MutationCode.TABLE_ALREADY_EXISTS) {
                 return result;
             }
@@ -854,7 +910,7 @@ public class MetaDataEndpointImpl extends BaseEndpointCoprocessor implements
Met
                     if (type != expectedType) {
                         return new MetaDataMutationResult(MutationCode.TABLE_NOT_FOUND, EnvironmentEdgeManager.currentTimeMillis(),
null);
                     }
-                    if (hasViews(region, tenantId, table)) {
+                    if (findChildViews(region, tenantId, table).hasViews()) {
                         // Disallow any column mutations for parents of tenant tables
                         return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION,
EnvironmentEdgeManager.currentTimeMillis(), null);
                     }
@@ -977,7 +1033,7 @@ public class MetaDataEndpointImpl extends BaseEndpointCoprocessor implements
Met
                                             byte[] linkKey = MetaDataUtil.getParentLinkKey(tenantId,
schemaName, tableName, index.getTableName().getBytes());
                                             // Drop the link between the data table and the
index table
                                             additionalTableMetaData.add(new Delete(linkKey,
clientTimeStamp, null));
-                                            doDropTable(indexKey, tenantId, index.getSchemaName().getBytes(),
index.getTableName().getBytes(), tableName, index.getType(), additionalTableMetaData, invalidateList,
lids, tableNamesToDelete);
+                                            doDropTable(indexKey, tenantId, index.getSchemaName().getBytes(),
index.getTableName().getBytes(), tableName, index.getType(), additionalTableMetaData, invalidateList,
lids, tableNamesToDelete, false);
                                             // TODO: return in result?
                                         } else {
                                             invalidateList.add(new ImmutableBytesPtr(indexKey));
@@ -1247,4 +1303,45 @@ public class MetaDataEndpointImpl extends BaseEndpointCoprocessor implements
Met
         return new MetaDataMutationResult(MutationCode.TABLE_NOT_IN_REGION,
                 EnvironmentEdgeManager.currentTimeMillis(), null);
     }
+    
+    /**
+     * Certain operations, such as DROP TABLE are not allowed if there a table has child
views.
+     * This class wraps the Results of a scanning the Phoenix Metadata for child views for
a specific table
+     * and stores an additional flag for whether whether SYSTEM.CATALOG has split across
multiple regions.
+     */
+    private static class TableViewFinderResult {
+        
+        private List<Result> results = Lists.newArrayList();
+        private boolean allViewsNotInSingleRegion = false;
+        
+        private TableViewFinderResult(List<Result> results) {
+            this.results = results;
+        }
+        
+        public boolean hasViews() {
+            return results.size() > 0;
+        }
+
+        private void setAllViewsNotInSingleRegion() {
+            allViewsNotInSingleRegion = true;
+        }
+        
+        private List<Result> getResults() {
+            return results;
+        }
+        
+        /**
+         * Returns true is the table has views and they are all in the same HBase region.
+         */
+        private boolean allViewsInSingleRegion() {
+            return results.size() > 0 && !allViewsNotInSingleRegion;
+        }
+        
+        /**
+         * Returns true is the table has views and they are all NOT in the same HBase region.
+         */
+        private boolean allViewsInMultipleRegions() {
+            return results.size() > 0 && allViewsNotInSingleRegion;
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3af2450d/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
index a303b95..351662b 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
@@ -226,13 +226,22 @@ public interface MetaDataProtocol extends CoprocessorProtocol {
     MetaDataMutationResult createTable(List<Mutation> tableMetadata) throws IOException;
 
     /**
-     * Drop an existing Phoenix table
+     * Drop an existing Phoenix table. This is for backwards compatibility after adding option
to CASCADE.
      * @param tableMetadata
      * @param tableType
      * @return MetaDataMutationResult
      * @throws IOException
      */
     MetaDataMutationResult dropTable(List<Mutation> tableMetadata, String tableType)
throws IOException;
+    
+    /**
+     * Drop an existing Phoenix table
+     * @param tableMetadata
+     * @param tableType
+     * @return MetaDataMutationResult
+     * @throws IOException
+     */
+    MetaDataMutationResult dropTable(List<Mutation> tableMetadata, String tableType,
boolean isCascade) throws IOException;
 
     /**
      * Add a column to an existing Phoenix table

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3af2450d/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
index 3265206..45e6973 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
@@ -524,8 +524,8 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
 
     private static class ExecutableDropTableStatement extends DropTableStatement implements
CompilableStatement {
 
-        ExecutableDropTableStatement(TableName tableName, PTableType tableType, boolean ifExists)
{
-            super(tableName, tableType, ifExists);
+        ExecutableDropTableStatement(TableName tableName, PTableType tableType, boolean ifExists,
boolean cascade) {
+            super(tableName, tableType, ifExists, cascade);
         }
 
         @SuppressWarnings("unchecked")
@@ -781,8 +781,8 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
         }
         
         @Override
-        public DropTableStatement dropTable(TableName tableName, PTableType tableType, boolean
ifExists) {
-            return new ExecutableDropTableStatement(tableName, tableType, ifExists);
+        public DropTableStatement dropTable(TableName tableName, PTableType tableType, boolean
ifExists, boolean cascade) {
+            return new ExecutableDropTableStatement(tableName, tableType, ifExists, cascade);
         }
         
         @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3af2450d/phoenix-core/src/main/java/org/apache/phoenix/parse/DropTableStatement.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/DropTableStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/DropTableStatement.java
index 2945d36..997b695 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/DropTableStatement.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/DropTableStatement.java
@@ -24,11 +24,14 @@ public class DropTableStatement extends MutableStatement {
     private final TableName tableName;
     private final boolean ifExists;
     private final PTableType tableType;
+    private final boolean cascade;
+    
 
-    protected DropTableStatement(TableName tableName, PTableType tableType, boolean ifExists)
{
+    protected DropTableStatement(TableName tableName, PTableType tableType, boolean ifExists,
boolean cascade) {
         this.tableName = tableName;
         this.tableType = tableType;
         this.ifExists = ifExists;
+        this.cascade = cascade;
     }
     
     @Override
@@ -48,6 +51,10 @@ public class DropTableStatement extends MutableStatement {
         return ifExists;
     }
     
+    public boolean cascade() {
+    	return cascade;
+    }
+    
     @Override
     public Operation getOperation() {
         return Operation.DELETE;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3af2450d/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
index 4fd5ab8..f1e39eb 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
@@ -299,8 +299,8 @@ public class ParseNodeFactory {
         return new DropColumnStatement(table, tableType, columnNodes, ifExists);
     }
     
-    public DropTableStatement dropTable(TableName tableName, PTableType tableType, boolean
ifExists) {
-        return new DropTableStatement(tableName, tableType, ifExists);
+    public DropTableStatement dropTable(TableName tableName, PTableType tableType, boolean
ifExists, boolean cascade) {
+        return new DropTableStatement(tableName, tableType, ifExists, cascade);
     }
     
     public DropIndexStatement dropIndex(NamedNode indexName, TableName tableName, boolean
ifExists) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3af2450d/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServices.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServices.java
b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServices.java
index fd44d6b..5f43a63 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServices.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServices.java
@@ -71,7 +71,7 @@ public interface ConnectionQueryServices extends QueryServices, MetaDataMutated
 
     public MetaDataMutationResult getTable(PName tenantId, byte[] schemaName, byte[] tableName,
long tableTimestamp, long clientTimetamp) throws SQLException;
     public MetaDataMutationResult createTable(List<Mutation> tableMetaData, byte[]
tableName, PTableType tableType, Map<String,Object> tableProps, List<Pair<byte[],Map<String,Object>>>
families, byte[][] splits) throws SQLException;
-    public MetaDataMutationResult dropTable(List<Mutation> tableMetadata, PTableType
tableType) throws SQLException;
+    public MetaDataMutationResult dropTable(List<Mutation> tableMetadata, PTableType
tableType, boolean cascade) throws SQLException;
     public MetaDataMutationResult addColumn(List<Mutation> tableMetaData, List<Pair<byte[],Map<String,Object>>>
families, PTable table) throws SQLException;
     public MetaDataMutationResult dropColumn(List<Mutation> tableMetadata, PTableType
tableType) throws SQLException;
     public MetaDataMutationResult updateIndexState(List<Mutation> tableMetadata, String
parentTableName) throws SQLException;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3af2450d/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
index f811c6e..3633724 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
@@ -1026,7 +1026,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices
implement
     }
 
     @Override
-    public MetaDataMutationResult dropTable(final List<Mutation> tableMetaData, final
PTableType tableType) throws SQLException {
+    public MetaDataMutationResult dropTable(final List<Mutation> tableMetaData, final
PTableType tableType, final boolean cascade) throws SQLException {
         byte[][] rowKeyMetadata = new byte[3][];
         SchemaUtil.getVarChars(tableMetaData.get(0).getRow(), rowKeyMetadata);
         byte[] tenantIdBytes = rowKeyMetadata[PhoenixDatabaseMetaData.TENANT_ID_INDEX];
@@ -1037,7 +1037,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices
implement
                 new Batch.Call<MetaDataProtocol, MetaDataMutationResult>() {
                     @Override
                     public MetaDataMutationResult call(MetaDataProtocol instance) throws
IOException {
-                      return instance.dropTable(tableMetaData, tableType.getSerializedValue());
+                      return instance.dropTable(tableMetaData, tableType.getSerializedValue(),
cascade);
                     }
                 });
         

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3af2450d/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
index 6552355..09f42aa 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
@@ -193,7 +193,7 @@ public class ConnectionlessQueryServicesImpl extends DelegateQueryServices
imple
     }
 
     @Override
-    public MetaDataMutationResult dropTable(List<Mutation> tableMetadata, PTableType
tableType) throws SQLException {
+    public MetaDataMutationResult dropTable(List<Mutation> tableMetadata, PTableType
tableType, boolean cascade) throws SQLException {
         return new MetaDataMutationResult(MutationCode.TABLE_ALREADY_EXISTS, 0, null);
     }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3af2450d/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
b/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
index 306d536..0b6a399 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
@@ -115,8 +115,8 @@ public class DelegateConnectionQueryServices extends DelegateQueryServices
imple
     }
 
     @Override
-    public MetaDataMutationResult dropTable(List<Mutation> tabeMetaData, PTableType
tableType) throws SQLException {
-        return getDelegate().dropTable(tabeMetaData, tableType);
+    public MetaDataMutationResult dropTable(List<Mutation> tabeMetaData, PTableType
tableType, boolean cascade) throws SQLException {
+        return getDelegate().dropTable(tabeMetaData, tableType, cascade);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3af2450d/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
index a462a5d..fee329d 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
@@ -1308,17 +1308,17 @@ public class MetaDataClient {
     public MutationState dropTable(DropTableStatement statement) throws SQLException {
         String schemaName = statement.getTableName().getSchemaName();
         String tableName = statement.getTableName().getTableName();
-        return dropTable(schemaName, tableName, null, statement.getTableType(), statement.ifExists());
+        return dropTable(schemaName, tableName, null, statement.getTableType(), statement.ifExists(),
statement.cascade());
     }
 
     public MutationState dropIndex(DropIndexStatement statement) throws SQLException {
         String schemaName = statement.getTableName().getSchemaName();
         String tableName = statement.getIndexName().getName();
         String parentTableName = statement.getTableName().getTableName();
-        return dropTable(schemaName, tableName, parentTableName, PTableType.INDEX, statement.ifExists());
+        return dropTable(schemaName, tableName, parentTableName, PTableType.INDEX, statement.ifExists(),
false);
     }
 
-    private MutationState dropTable(String schemaName, String tableName, String parentTableName,
PTableType tableType, boolean ifExists) throws SQLException {
+    private MutationState dropTable(String schemaName, String tableName, String parentTableName,
PTableType tableType, boolean ifExists, boolean cascade) throws SQLException {
         connection.rollback();
         boolean wasAutoCommit = connection.getAutoCommit();
         try {
@@ -1338,7 +1338,7 @@ public class MetaDataClient {
                 tableMetaData.add(linkDelete);
             }
 
-            MetaDataMutationResult result = connection.getQueryServices().dropTable(tableMetaData,
tableType);
+            MetaDataMutationResult result = connection.getQueryServices().dropTable(tableMetaData,
tableType, cascade);
             MutationCode code = result.getMutationCode();
             switch(code) {
                 case TABLE_NOT_FOUND:


Mime
View raw message