phoenix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jamestay...@apache.org
Subject [28/41] PHOENIX-130 Separate execution of slow (integration) tests from fast unit tests (Gabriel Reid)
Date Wed, 12 Mar 2014 21:31:55 GMT
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/8d6e2a58/phoenix-core/src/it/java/org/apache/phoenix/end2end/SpillableGroupByIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SpillableGroupByIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SpillableGroupByIT.java
new file mode 100644
index 0000000..ebbd9af
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SpillableGroupByIT.java
@@ -0,0 +1,134 @@
+/*
+ * 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.phoenix.end2end;
+
+import static org.apache.phoenix.util.TestUtil.GROUPBYTEST_NAME;
+import static org.apache.phoenix.util.TestUtil.PHOENIX_JDBC_URL;
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Map;
+import java.util.Properties;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.google.common.collect.Maps;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.util.PhoenixRuntime;
+import org.apache.phoenix.util.ReadOnlyProps;
+
+public class SpillableGroupByIT extends BaseConnectedQueryIT {
+
+    private static final int NUM_ROWS_INSERTED = 1000;
+    
+    // covers: COUNT, COUNT(DISTINCT) SUM, AVG, MIN, MAX 
+    private static String GROUPBY1 = "select "
+            + "count(*), count(distinct uri), sum(appcpu), avg(appcpu), uri, min(id), max(id) from "
+            + GROUPBYTEST_NAME + " group by uri";
+    
+    private int id;
+
+    @BeforeClass
+    public static void doSetup() throws Exception {
+        Map<String, String> props = Maps.newHashMapWithExpectedSize(1);
+        // Set a very small cache size to force plenty of spilling
+        props.put(QueryServices.GROUPBY_MAX_CACHE_SIZE_ATTRIB,
+                Integer.toString(1));
+        props.put(QueryServices.GROUPBY_SPILLABLE_ATTRIB, String.valueOf(true));
+        props.put(QueryServices.GROUPBY_SPILL_FILES_ATTRIB,
+                Integer.toString(1));
+
+        // Must update config before starting server
+        startServer(getUrl(), new ReadOnlyProps(props.entrySet().iterator()));
+    }
+
+    private long createTable() throws Exception {
+        long ts = nextTimestamp();
+        ensureTableCreated(getUrl(), GROUPBYTEST_NAME, null, ts - 2);
+        return ts;
+    }
+
+    private void loadData(long ts) throws SQLException {
+        Properties props = new Properties(TEST_PROPERTIES);
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts));
+        Connection conn = DriverManager.getConnection(PHOENIX_JDBC_URL, props);
+        int groupFactor = NUM_ROWS_INSERTED / 2;
+        for (int i = 0; i < NUM_ROWS_INSERTED; i++) {
+            insertRow(conn, Integer.toString(i % (groupFactor)), 10);
+
+            if ((i % 1000) == 0) {
+                conn.commit();
+            }
+        }
+        conn.commit();
+        conn.close();
+    }
+
+    private void insertRow(Connection conn, String uri, int appcpu)
+            throws SQLException {
+        PreparedStatement statement = conn.prepareStatement("UPSERT INTO "
+                + GROUPBYTEST_NAME + "(id, uri, appcpu) values (?,?,?)");
+        statement.setString(1, String.valueOf(id));
+        statement.setString(2, uri);
+        statement.setInt(3, appcpu);
+        statement.executeUpdate();
+        id++;
+    }
+
+    
+    @Test
+    public void testScanUri() throws Exception {
+        SpillableGroupByIT spGpByT = new SpillableGroupByIT();
+        long ts = spGpByT.createTable();
+        spGpByT.loadData(ts);
+        Properties props = new Properties(TEST_PROPERTIES);
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB,
+                Long.toString(ts + 1));
+        Connection conn = DriverManager.getConnection(PHOENIX_JDBC_URL, props);
+        try {
+            Statement stmt = conn.createStatement();
+            ResultSet rs = stmt.executeQuery(GROUPBY1);
+
+            int count = 0;
+            while (rs.next()) {
+                String uri = rs.getString(5);
+                assertEquals(2, rs.getInt(1));
+                assertEquals(1, rs.getInt(2));
+                assertEquals(20, rs.getInt(3));
+                assertEquals(10, rs.getInt(4));
+                int a = Integer.valueOf(rs.getString(6)).intValue();
+                int b = Integer.valueOf(rs.getString(7)).intValue();
+                assertEquals(Integer.valueOf(uri).intValue(), Math.min(a, b));
+                assertEquals(NUM_ROWS_INSERTED / 2 + Integer.valueOf(uri), Math.max(a, b));
+                count++;
+            }
+            assertEquals(NUM_ROWS_INSERTED / 2, count);
+            
+        } finally {
+            conn.close();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/8d6e2a58/phoenix-core/src/it/java/org/apache/phoenix/end2end/SpooledOrderByIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SpooledOrderByIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SpooledOrderByIT.java
new file mode 100644
index 0000000..ca743c4
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SpooledOrderByIT.java
@@ -0,0 +1,38 @@
+/*
+ * 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.phoenix.end2end;
+
+import java.util.Map;
+
+import org.junit.BeforeClass;
+
+import com.google.common.collect.Maps;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.util.ReadOnlyProps;
+
+public class SpooledOrderByIT extends OrderByIT {
+
+    @BeforeClass
+    public static void doSetup() throws Exception {
+        Map<String,String> props = Maps.newHashMapWithExpectedSize(3);
+        props.put(QueryServices.SPOOL_THRESHOLD_BYTES_ATTRIB, Integer.toString(100));
+        // Must update config before starting server
+        startServer(getUrl(), new ReadOnlyProps(props.entrySet().iterator()));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/8d6e2a58/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatementHintsIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatementHintsIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatementHintsIT.java
new file mode 100644
index 0000000..efc52d2
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatementHintsIT.java
@@ -0,0 +1,161 @@
+/*
+ * 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.phoenix.end2end;
+
+import static org.apache.phoenix.util.TestUtil.*;
+import static org.junit.Assert.*;
+
+import java.sql.*;
+import java.util.Properties;
+
+import org.junit.Test;
+
+
+/**
+ * End-to-End tests on various statement hints.
+ */
+public class StatementHintsIT extends BaseHBaseManagedTimeIT {
+
+    private static void initTableValues() throws Exception {
+        Properties props = new Properties(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+        
+        try {
+            String ddl = "CREATE TABLE test_table" +
+                    "   (a_integer integer not null, \n" +
+                    "    a_string varchar not null, \n" +
+                    "    a_id char(3) not null,\n" +
+                    "    b_string varchar \n" +
+                    "    CONSTRAINT pk PRIMARY KEY (a_integer, a_string, a_id))\n";
+            createTestTable(getUrl(), ddl);
+            
+            String query;
+            PreparedStatement stmt;
+            
+            query = "UPSERT INTO test_table"
+                    + "(a_integer, a_string, a_id, b_string) "
+                    + "VALUES(?,?,?,?)";
+            stmt = conn.prepareStatement(query);
+            
+            stmt.setInt(1, 1);
+            stmt.setString(2, "ab");
+            stmt.setString(3, "123");
+            stmt.setString(4, "abc");
+            stmt.execute();
+            
+            stmt.setInt(1, 1);
+            stmt.setString(2, "abc");
+            stmt.setString(3, "456");
+            stmt.setString(4, "abc");
+            stmt.execute();
+            
+            stmt.setInt(1, 1);
+            stmt.setString(2, "de");
+            stmt.setString(3, "123");
+            stmt.setString(4, "abc");
+            stmt.execute();
+            
+            stmt.setInt(1, 2);
+            stmt.setString(2, "abc");
+            stmt.setString(3, "123");
+            stmt.setString(4, "def");
+            stmt.execute();
+
+            stmt.setInt(1, 3);
+            stmt.setString(2, "abc");
+            stmt.setString(3, "123");
+            stmt.setString(4, "ghi");
+            stmt.execute();
+
+            stmt.setInt(1, 4);
+            stmt.setString(2, "abc");
+            stmt.setString(3, "123");
+            stmt.setString(4, "jkl");
+            stmt.execute();
+            conn.commit();
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSelectForceRangeScan() throws Exception {
+        Properties props = new Properties(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            initTableValues();
+            String query = "SELECT /*+ RANGE_SCAN */ * FROM test_table WHERE a_integer IN (1, 2, 3, 4)";
+            PreparedStatement stmt = conn.prepareStatement(query);
+            ResultSet rs = stmt.executeQuery();
+            
+            assertTrue(rs.next());
+            assertEquals(1, rs.getInt(1));
+            assertEquals("ab", rs.getString(2));
+            assertEquals("123", rs.getString(3));
+            assertEquals("abc", rs.getString(4));
+            
+            assertTrue(rs.next());
+            assertTrue(rs.next());
+            
+            assertTrue(rs.next());
+            assertEquals(2, rs.getInt(1));
+            assertEquals("abc", rs.getString(2));
+            assertEquals("123", rs.getString(3));
+            assertEquals("def", rs.getString(4));
+            
+            assertTrue(rs.next());
+            assertTrue(rs.next());
+            assertEquals(4, rs.getInt(1));
+            assertEquals("abc", rs.getString(2));
+            assertEquals("123", rs.getString(3));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSelectForceSkipScan() throws Exception {
+        Properties props = new Properties(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            initTableValues();
+            // second slot on the 
+            String query = "SELECT /*+ SKIP_SCAN */ * FROM test_table WHERE a_string = 'abc'";
+            PreparedStatement stmt = conn.prepareStatement(query);
+            ResultSet rs = stmt.executeQuery();
+            
+            assertTrue(rs.next());
+            assertEquals(1, rs.getInt(1));
+            assertEquals("abc", rs.getString(2));
+            assertEquals("456", rs.getString(3));
+            
+            assertTrue(rs.next());
+            assertTrue(rs.next());
+            
+            assertTrue(rs.next());
+            assertEquals(4, rs.getInt(1));
+            assertEquals("abc", rs.getString(2));
+            assertEquals("123", rs.getString(3));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/8d6e2a58/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatsManagerIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatsManagerIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatsManagerIT.java
new file mode 100644
index 0000000..332d982
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatsManagerIT.java
@@ -0,0 +1,195 @@
+/*
+ * 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.phoenix.end2end;
+
+import static org.apache.phoenix.util.TestUtil.PHOENIX_JDBC_URL;
+import static org.apache.phoenix.util.TestUtil.STABLE_NAME;
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.util.Properties;
+
+import org.apache.phoenix.query.ConnectionQueryServices;
+import org.apache.phoenix.query.StatsManager;
+import org.apache.phoenix.query.StatsManagerImpl;
+import org.apache.phoenix.query.StatsManagerImpl.TimeKeeper;
+import org.apache.phoenix.schema.TableRef;
+import org.apache.phoenix.util.PhoenixRuntime;
+import org.junit.Test;
+
+
+/**
+ * 
+ * Test for stats manager, which is a client-side process that caches the
+ * first and last key of a given table. The {@link #testStatsManager()}
+ * test must be the only test here, as it relies on state that is only
+ * cleared between test runs.
+ *
+ */
+public class StatsManagerIT extends BaseParallelIteratorsRegionSplitterIT {
+    
+    private static class ManualTimeKeeper implements TimeKeeper {
+        private long currentTime = 0;
+        @Override
+        public long currentTimeMillis() {
+            return currentTime;
+        }
+        
+        public void setCurrentTimeMillis(long currentTime) {
+            this.currentTime = currentTime;
+        }
+    }
+
+    private static interface ChangeDetector {
+        boolean isChanged();
+    }
+
+    private boolean waitForAsyncChange(ChangeDetector detector, long maxWaitTimeMs) throws Exception {
+        long startTime = System.currentTimeMillis();
+        do {
+            if (detector.isChanged()) {
+                return true;
+            }
+            try {
+                Thread.sleep(500);
+            } catch (InterruptedException e) {
+                throw e;
+            }
+        } while (System.currentTimeMillis() - startTime < maxWaitTimeMs);
+        return false;
+    }
+
+    private static class MinKeyChange implements ChangeDetector {
+        private byte[] value;
+        private StatsManager stats;
+        private TableRef table;
+        
+        public MinKeyChange(StatsManager stats, TableRef table) {
+            this.value = stats.getMinKey(table);
+            this.stats = stats;
+            this.table = table;
+        }
+        @Override
+        public boolean isChanged() {
+            return value != stats.getMinKey(table);
+        }
+    }
+
+    private static class MaxKeyChange implements ChangeDetector {
+        private byte[] value;
+        private StatsManager stats;
+        private TableRef table;
+        
+        public MaxKeyChange(StatsManager stats, TableRef table) {
+            this.value = stats.getMaxKey(table);
+            this.stats = stats;
+            this.table = table;
+        }
+        @Override
+        public boolean isChanged() {
+            return value != stats.getMaxKey(table);
+        }
+    }
+
+    @Test
+    public void testStatsManager() throws Exception {
+        long ts = nextTimestamp();
+        initTableValues(ts);
+        String url = getUrl() + ";" + PhoenixRuntime.CURRENT_SCN_ATTRIB + "=" + ts;
+        Properties props = new Properties(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(url, props);
+        TableRef table = getTableRef(conn,ts);
+
+        int updateFreq = 5;
+        int maxAge = 10;
+        int startTime = 100;
+        long waitTime = 5000;
+        
+        ManualTimeKeeper timeKeeper = new ManualTimeKeeper();
+        timeKeeper.setCurrentTimeMillis(startTime);
+        ConnectionQueryServices services = driver.getConnectionQueryServices(getUrl(), TEST_PROPERTIES);
+        StatsManager stats = new StatsManagerImpl(services, updateFreq, maxAge, timeKeeper);
+        MinKeyChange minKeyChange = new MinKeyChange(stats, table);
+        MaxKeyChange maxKeyChange = new MaxKeyChange(stats, table);
+        
+        byte[] minKey = stats.getMinKey(table);
+        assertTrue(minKey == null);
+        assertTrue(waitForAsyncChange(minKeyChange,waitTime));
+        assertArrayEquals(KMIN, stats.getMinKey(table));
+        assertArrayEquals(KMAX, stats.getMaxKey(table));
+        minKeyChange = new MinKeyChange(stats, table);
+        
+        url = PHOENIX_JDBC_URL + ";" + PhoenixRuntime.CURRENT_SCN_ATTRIB + "=" + ts+2;
+        props = new Properties(TEST_PROPERTIES);
+        conn = DriverManager.getConnection(url, props);
+        PreparedStatement delStmt = conn.prepareStatement("delete from " + STABLE_NAME + " where id=?");
+        delStmt.setString(1, new String(KMIN));
+        delStmt.execute();
+        PreparedStatement upsertStmt = conn.prepareStatement("upsert into " + STABLE_NAME + " VALUES (?, ?)");
+        upsertStmt.setString(1, new String(KMIN2));
+        upsertStmt.setInt(2, 1);
+        upsertStmt.execute();
+        conn.commit();
+
+        assertFalse(waitForAsyncChange(minKeyChange,waitTime)); // Stats won't change until they're attempted to be retrieved again
+        timeKeeper.setCurrentTimeMillis(timeKeeper.currentTimeMillis() + updateFreq);
+        minKeyChange = new MinKeyChange(stats, table); // Will kick off change, but will upate asynchronously
+        assertArrayEquals(KMIN, minKeyChange.value);
+        assertTrue(waitForAsyncChange(minKeyChange,waitTime));
+        assertArrayEquals(KMIN2, stats.getMinKey(table));
+        assertArrayEquals(KMAX, stats.getMaxKey(table));
+        minKeyChange = new MinKeyChange(stats, table);
+        
+        timeKeeper.setCurrentTimeMillis(timeKeeper.currentTimeMillis() + maxAge);
+        minKeyChange = new MinKeyChange(stats, table); // Will kick off change, but will upate asynchronously
+        assertTrue(null == minKeyChange.value);
+        assertTrue(waitForAsyncChange(minKeyChange,waitTime));
+        assertArrayEquals(KMIN2, stats.getMinKey(table));
+        assertArrayEquals(KMAX, stats.getMaxKey(table));
+        maxKeyChange = new MaxKeyChange(stats, table);
+        
+        delStmt.setString(1, new String(KMAX));
+        delStmt.execute();
+        upsertStmt.setString(1, new String(KMAX2));
+        upsertStmt.setInt(2, 1);
+        upsertStmt.execute();
+        conn.commit();
+        conn.close();
+
+        assertFalse(waitForAsyncChange(maxKeyChange,waitTime)); // Stats won't change until they're attempted to be retrieved again
+        timeKeeper.setCurrentTimeMillis(timeKeeper.currentTimeMillis() + updateFreq);
+        maxKeyChange = new MaxKeyChange(stats, table); // Will kick off change, but will upate asynchronously
+        assertArrayEquals(KMAX, maxKeyChange.value);
+        assertTrue(waitForAsyncChange(maxKeyChange,waitTime));
+        assertArrayEquals(KMAX2, stats.getMaxKey(table));
+        assertArrayEquals(KMIN2, stats.getMinKey(table));
+        maxKeyChange = new MaxKeyChange(stats, table);
+        
+        timeKeeper.setCurrentTimeMillis(timeKeeper.currentTimeMillis() + maxAge);
+        maxKeyChange = new MaxKeyChange(stats, table); // Will kick off change, but will upate asynchronously
+        assertTrue(null == maxKeyChange.value);
+        assertTrue(waitForAsyncChange(maxKeyChange,waitTime));
+        assertArrayEquals(KMIN2, stats.getMinKey(table));
+        assertArrayEquals(KMAX2, stats.getMaxKey(table));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/8d6e2a58/phoenix-core/src/it/java/org/apache/phoenix/end2end/StddevIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/StddevIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/StddevIT.java
new file mode 100644
index 0000000..8cd76b5
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/StddevIT.java
@@ -0,0 +1,135 @@
+/*
+ * 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.phoenix.end2end;
+
+import static org.apache.phoenix.util.TestUtil.PHOENIX_JDBC_URL;
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.*;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.sql.*;
+import java.util.Properties;
+
+import org.junit.Test;
+
+import org.apache.phoenix.util.PhoenixRuntime;
+
+public class StddevIT extends BaseClientManagedTimeIT {
+
+    @Test
+    public void testSTDDEV_POP() throws Exception {
+        long ts = nextTimestamp();
+        String tenantId = getOrganizationId();
+        initATableValues(tenantId, getDefaultSplits(tenantId), null, ts);
+
+        String query = "SELECT STDDEV_POP(A_INTEGER) FROM aTable";
+
+        Properties props = new Properties(TEST_PROPERTIES);
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 2)); // Execute at
+                                                                                     // timestamp 2
+        Connection conn = DriverManager.getConnection(PHOENIX_JDBC_URL, props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            BigDecimal stddev = rs.getBigDecimal(1);
+            stddev = stddev.setScale(1, RoundingMode.HALF_UP);
+            assertEquals(2.6, stddev.doubleValue(),0.0);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testSTDDEV_SAMP() throws Exception {
+        long ts = nextTimestamp();
+        String tenantId = getOrganizationId();
+        initATableValues(tenantId, getDefaultSplits(tenantId), null, ts);
+
+        String query = "SELECT STDDEV_SAMP(x_decimal) FROM aTable";
+
+        Properties props = new Properties(TEST_PROPERTIES);
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 2)); // Execute at
+                                                                                     // timestamp 2
+        Connection conn = DriverManager.getConnection(PHOENIX_JDBC_URL, props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            BigDecimal stddev = rs.getBigDecimal(1);
+            stddev = stddev.setScale(1, RoundingMode.HALF_UP);
+            assertEquals(2.0, stddev.doubleValue(),0.0);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSTDDEV_POPOnDecimalColType() throws Exception {
+        long ts = nextTimestamp();
+        String tenantId = getOrganizationId();
+        initATableValues(tenantId, getDefaultSplits(tenantId), null, ts);
+
+        String query = "SELECT STDDEV_POP(x_decimal) FROM aTable";
+
+        Properties props = new Properties(TEST_PROPERTIES);
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 2)); // Execute at
+                                                                                     // timestamp 2
+        Connection conn = DriverManager.getConnection(PHOENIX_JDBC_URL, props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            BigDecimal stddev = rs.getBigDecimal(1);
+            stddev = stddev.setScale(10, RoundingMode.HALF_UP);
+            assertEquals(1.6679994671, stddev.doubleValue(), 0.0);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSTDDEV_SAMPOnDecimalColType() throws Exception {
+        long ts = nextTimestamp();
+        String tenantId = getOrganizationId();
+        initATableValues(tenantId, getDefaultSplits(tenantId), null, ts);
+
+        String query = "SELECT STDDEV_SAMP(x_decimal) FROM aTable";
+
+        Properties props = new Properties(TEST_PROPERTIES);
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 2)); // Execute at
+                                                                                     // timestamp 2
+        Connection conn = DriverManager.getConnection(PHOENIX_JDBC_URL, props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            BigDecimal stddev = rs.getBigDecimal(1);
+            stddev = stddev.setScale(10, RoundingMode.HALF_UP);
+            assertEquals(2.0428737928, stddev.doubleValue(), 0.0);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/8d6e2a58/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
new file mode 100644
index 0000000..465e266
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificTablesDDLIT.java
@@ -0,0 +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.phoenix.end2end;
+
+import static org.apache.phoenix.exception.SQLExceptionCode.CANNOT_DEFINE_PK_FOR_VIEW;
+import static org.apache.phoenix.exception.SQLExceptionCode.CANNOT_DROP_PK;
+import static org.apache.phoenix.exception.SQLExceptionCode.CANNOT_MODIFY_VIEW_PK;
+import static org.apache.phoenix.exception.SQLExceptionCode.CANNOT_MUTATE_TABLE;
+import static org.apache.phoenix.exception.SQLExceptionCode.TABLE_UNDEFINED;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_CATALOG_TABLE;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TYPE_SEQUENCE;
+import static org.apache.phoenix.schema.PTableType.SYSTEM;
+import static org.apache.phoenix.schema.PTableType.TABLE;
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.apache.hadoop.hbase.client.HBaseAdmin;
+import org.apache.phoenix.exception.SQLExceptionCode;
+import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
+import org.apache.phoenix.schema.ColumnAlreadyExistsException;
+import org.apache.phoenix.schema.ColumnNotFoundException;
+import org.apache.phoenix.schema.PTableType;
+import org.apache.phoenix.schema.TableAlreadyExistsException;
+import org.apache.phoenix.schema.TableNotFoundException;
+import org.apache.phoenix.util.PhoenixRuntime;
+import org.apache.phoenix.util.SchemaUtil;
+import org.apache.phoenix.util.StringUtil;
+import org.junit.Test;
+
+public class TenantSpecificTablesDDLIT extends BaseTenantSpecificTablesIT {
+    
+    @Test
+    public void testCreateTenantSpecificTable() throws Exception {
+        // ensure we didn't create a physical HBase table for the tenant-specific table
+        HBaseAdmin admin = driver.getConnectionQueryServices(getUrl(), TEST_PROPERTIES).getAdmin();
+        assertEquals(0, admin.listTables(TENANT_TABLE_NAME).length);
+    }
+    
+    @Test
+    public void testCreateTenantTableTwice() throws Exception {
+        try {
+            createTestTable(PHOENIX_JDBC_TENANT_SPECIFIC_URL, TENANT_TABLE_DDL, null, nextTimestamp(), false);
+        	fail();
+        }
+        catch (TableAlreadyExistsException expected) {}
+    }
+    
+    @Test
+    public void testCreateTenantViewFromNonMultiTenant() throws Exception {
+        createTestTable(getUrl(), "CREATE TABLE NON_MULTI_TENANT_TABLE (K VARCHAR PRIMARY KEY)", null, nextTimestamp());
+        try {
+            // Only way to get this exception is to attempt to derive from a global, multi-type table, as we won't find
+            // a tenant-specific table when we attempt to resolve the base table.
+            createTestTable(PHOENIX_JDBC_TENANT_SPECIFIC_URL, "CREATE VIEW TENANT_TABLE2 (COL VARCHAR) AS SELECT * FROM NON_MULTI_TENANT_TABLE", null, nextTimestamp());
+        }
+        catch (TableNotFoundException expected) {
+        }
+    }
+
+    @Test
+    public void testAlterMultiTenantWithViewsToGlobal() throws Exception {
+        Properties props = new Properties();
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(nextTimestamp()));
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            conn.createStatement().execute("alter table " + PARENT_TABLE_NAME + " set MULTI_TENANT=false");
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+        }
+    }
+    
+    @Test
+    public void testCreateTenantTableWithSameWhereClause() throws Exception {
+        createTestTable(getUrl(), PARENT_TABLE_DDL.replace(PARENT_TABLE_NAME, PARENT_TABLE_NAME + "_II"), null, nextTimestamp());
+        createTestTable(PHOENIX_JDBC_TENANT_SPECIFIC_URL, TENANT_TABLE_DDL.replace(TENANT_TABLE_NAME, TENANT_TABLE_NAME + "2"), null, nextTimestamp());
+    }
+    
+    @Test(expected=TableNotFoundException.class)
+    public void testDeletionOfParentTableFailsOnTenantSpecificConnection() throws Exception {
+        createTestTable(getUrl(), PARENT_TABLE_DDL.replace(PARENT_TABLE_NAME, "TEMP_PARENT"), null, nextTimestamp());
+        Properties props = new Properties();
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(nextTimestamp()));
+        props.setProperty(PhoenixRuntime.TENANT_ID_ATTRIB, TENANT_ID); // connection is tenant-specific
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.createStatement().execute("DROP TABLE TEMP_PARENT");
+        conn.close();
+    }
+    
+    public void testCreationOfParentTableFailsOnTenantSpecificConnection() throws Exception {
+        try {
+            createTestTable(PHOENIX_JDBC_TENANT_SPECIFIC_URL, "CREATE TABLE PARENT_TABLE ( \n" + 
+                    "                user VARCHAR ,\n" + 
+                    "                id INTEGER not null primary key desc\n" + 
+                    "                ) ", null, nextTimestamp());
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.CANNOT_CREATE_TENANT_SPECIFIC_TABLE.getErrorCode(), e.getErrorCode());
+        }
+    }
+    
+    @Test
+    public void testTenantSpecificAndParentTablesMayBeInDifferentSchemas() throws SQLException {
+        createTestTable(PHOENIX_JDBC_TENANT_SPECIFIC_URL, "CREATE VIEW DIFFSCHEMA.TENANT_TABLE ( \n" + 
+                "                tenant_col VARCHAR) AS SELECT * \n" + 
+                "                FROM " + PARENT_TABLE_NAME + " WHERE tenant_type_id = 'aaa'", null, nextTimestamp());
+        try {
+            createTestTable(PHOENIX_JDBC_TENANT_SPECIFIC_URL, "CREATE VIEW DIFFSCHEMA.TENANT_TABLE ( \n" + 
+                    "                tenant_col VARCHAR) AS SELECT *\n"+
+                    "                FROM DIFFSCHEMA." + PARENT_TABLE_NAME + " WHERE tenant_type_id = 'aaa'", null, nextTimestamp());
+            fail();
+        }
+        catch (SQLException expected) {
+            assertEquals(TABLE_UNDEFINED.getErrorCode(), expected.getErrorCode());
+        }
+        String newDDL =
+        "CREATE TABLE DIFFSCHEMA." + PARENT_TABLE_NAME + " ( \n" + 
+        "                user VARCHAR ,\n" + 
+        "                tenant_id VARCHAR(5) NOT NULL,\n" + 
+        "                tenant_type_id VARCHAR(3) NOT NULL, \n" + 
+        "                id INTEGER NOT NULL\n" + 
+        "                CONSTRAINT pk PRIMARY KEY (tenant_id, tenant_type_id, id)) MULTI_TENANT=true";
+        createTestTable(getUrl(), newDDL, null, nextTimestamp());
+        createTestTable(PHOENIX_JDBC_TENANT_SPECIFIC_URL, "CREATE VIEW DIFFSCHEMA.TENANT_TABLE ( \n" + 
+                "                tenant_col VARCHAR) AS SELECT *\n"+
+                "                FROM DIFFSCHEMA." + PARENT_TABLE_NAME + " WHERE tenant_type_id = 'aaa'", null, nextTimestamp());
+    }
+    
+    @Test
+    public void testTenantSpecificTableCannotDeclarePK() throws SQLException {
+        try {
+            createTestTable(PHOENIX_JDBC_TENANT_SPECIFIC_URL, "CREATE VIEW TENANT_TABLE2 ( \n" + 
+                    "                tenant_col VARCHAR PRIMARY KEY) AS SELECT *\n" + 
+                    "                FROM PARENT_TABLE", null, nextTimestamp());
+            fail();
+        }
+        catch (SQLException expected) {
+            assertEquals(CANNOT_DEFINE_PK_FOR_VIEW.getErrorCode(), expected.getErrorCode());
+        }
+    }
+    
+    @Test(expected=ColumnAlreadyExistsException.class)
+    public void testTenantSpecificTableCannotOverrideParentCol() throws SQLException {
+        createTestTable(PHOENIX_JDBC_TENANT_SPECIFIC_URL, "CREATE VIEW TENANT_TABLE2 ( \n" + 
+                "                user INTEGER) AS SELECT *\n" + 
+                "                FROM PARENT_TABLE", null, nextTimestamp());
+    }
+    
+    @Test
+    public void testBaseTableWrongFormatWithTenantTypeId() throws Exception {
+        // only two PK columns for multi_tenant, multi_type
+        try {
+            createTestTable(getUrl(), "CREATE TABLE BASE_TABLE2 (TENANT_ID VARCHAR NOT NULL PRIMARY KEY, ID VARCHAR NOT NULL, A INTEGER) MULTI_TENANT=true", null, nextTimestamp());
+            fail();
+        }
+        catch (SQLException expected) {
+            assertEquals(SQLExceptionCode.INSUFFICIENT_MULTI_TENANT_COLUMNS.getErrorCode(), expected.getErrorCode());
+        }
+    }
+    
+    @Test
+    public void testBaseTableWrongFormatWithNoTenantTypeId() throws Exception {
+        // tenantId column of wrong type
+        try {
+            createTestTable(getUrl(), "CREATE TABLE BASE_TABLE5 (TENANT_ID INTEGER NOT NULL, ID VARCHAR, A INTEGER CONSTRAINT PK PRIMARY KEY (TENANT_ID, ID)) MULTI_TENANT=true", null, nextTimestamp());
+            fail();
+        }
+        catch (SQLException expected) {
+            assertEquals(SQLExceptionCode.INSUFFICIENT_MULTI_TENANT_COLUMNS.getErrorCode(), expected.getErrorCode());
+        }
+    }
+    
+    @Test
+    public void testAddDropColumn() throws Exception {
+        Properties props = new Properties();
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(nextTimestamp()));
+        Connection conn = DriverManager.getConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL, props);
+        conn.setAutoCommit(true);
+        try {
+            conn.createStatement().executeUpdate("upsert into " + TENANT_TABLE_NAME + " (id, tenant_col) values (1, 'Viva Las Vegas')");
+            
+            conn.createStatement().execute("alter view " + TENANT_TABLE_NAME + " add tenant_col2 char(1) null");
+            
+            conn.close();
+            props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(nextTimestamp()));
+            conn = DriverManager.getConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL, props);
+            conn.setAutoCommit(true);
+            
+            conn.createStatement().executeUpdate("upsert into " + TENANT_TABLE_NAME + " (id, tenant_col2) values (2, 'a')");
+            conn.close();
+            props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(nextTimestamp()));
+            conn = DriverManager.getConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL, props);
+            conn.setAutoCommit(true);
+            
+            ResultSet rs = conn.createStatement().executeQuery("select count(*) from " + TENANT_TABLE_NAME);
+            rs.next();
+            assertEquals(2, rs.getInt(1));
+            
+            rs = conn.createStatement().executeQuery("select count(*) from " + TENANT_TABLE_NAME + " where tenant_col2 = 'a'");
+            rs.next();
+            assertEquals(1, rs.getInt(1));
+            
+            conn.close();
+            props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(nextTimestamp()));
+            conn = DriverManager.getConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL, props);
+            conn.setAutoCommit(true);
+            
+            conn.createStatement().execute("alter view " + TENANT_TABLE_NAME + " drop column tenant_col");
+            
+            conn.close();
+            props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(nextTimestamp()));
+            conn = DriverManager.getConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL, props);
+            conn.setAutoCommit(true);
+            
+            rs = conn.createStatement().executeQuery("select count(*) from " + TENANT_TABLE_NAME + "");
+            rs.next();
+            assertEquals(2, rs.getInt(1));
+            
+            try {
+                rs = conn.createStatement().executeQuery("select tenant_col from TENANT_TABLE");
+                fail();
+            }
+            catch (ColumnNotFoundException expected) {}
+        }
+        finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testMutationOfPKInTenantTablesNotAllowed() throws Exception {
+        Properties props = new Properties();
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(nextTimestamp()));
+        Connection conn = DriverManager.getConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL, props);
+        try {
+            try {
+                conn.createStatement().execute("alter table " + TENANT_TABLE_NAME + " add new_tenant_pk char(1) primary key");
+                fail();
+            }
+            catch (SQLException expected) {
+                assertEquals(CANNOT_MODIFY_VIEW_PK.getErrorCode(), expected.getErrorCode());
+            }
+            
+            try {
+                conn.createStatement().execute("alter table " + TENANT_TABLE_NAME + " drop column id");
+                fail();
+            }
+            catch (SQLException expected) {
+                assertEquals(CANNOT_DROP_PK.getErrorCode(), expected.getErrorCode());
+            }
+        }
+        finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testColumnMutationInParentTableWithExistingTenantTable() throws Exception {
+        Properties props = new Properties();
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(nextTimestamp()));
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            // try adding a PK col
+            try {
+                conn.createStatement().execute("alter table " + PARENT_TABLE_NAME + " add new_pk varchar primary key");
+                fail();
+            }
+            catch (SQLException expected) {
+                assertEquals(CANNOT_MUTATE_TABLE.getErrorCode(), expected.getErrorCode());
+            }
+            
+            // try adding a non-PK col
+            try {
+                conn.createStatement().execute("alter table " + PARENT_TABLE_NAME + " add new_col char(1)");
+                fail();
+            }
+            catch (SQLException expected) {
+                assertEquals(CANNOT_MUTATE_TABLE.getErrorCode(), expected.getErrorCode());
+            }
+            
+            // try removing a PK col
+            try {
+                conn.createStatement().execute("alter table " + PARENT_TABLE_NAME + " drop column id");
+                fail();
+            }
+            catch (SQLException expected) {
+                assertEquals(CANNOT_DROP_PK.getErrorCode(), expected.getErrorCode());
+            }
+            
+            // try removing a non-PK col
+            try {
+                conn.createStatement().execute("alter table " + PARENT_TABLE_NAME + " drop column user");
+                fail();
+            }
+            catch (SQLException expected) {
+                assertEquals(CANNOT_MUTATE_TABLE.getErrorCode(), expected.getErrorCode());
+            }
+        }
+        finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testDropParentTableWithExistingTenantTable() throws Exception {
+        Properties props = new Properties();
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(nextTimestamp()));
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            conn.createStatement().executeUpdate("drop table " + PARENT_TABLE_NAME);
+            fail("Should not have been allowed to drop a parent table to which tenant-specific tables still point.");
+        }
+        catch (SQLException expected) {
+            assertEquals(CANNOT_MUTATE_TABLE.getErrorCode(), expected.getErrorCode());
+        }
+        finally {
+            conn.close();
+        }
+    }
+    
+    @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 secondTenantId = "tenant2";
+        String secondTenatConnectionURL = PHOENIX_JDBC_TENANT_SPECIFIC_URL.replace(TENANT_ID,  secondTenantId);
+        createTestTable(secondTenatConnectionURL, TENANT_TABLE_DDL, null, nextTimestamp(), false);
+        
+        Properties props = new Properties();
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(nextTimestamp()));
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            // empty string means global tenant id
+            // make sure connections w/o tenant id only see non-tenant-specific tables, both SYSTEM and USER
+            DatabaseMetaData meta = conn.getMetaData();
+            ResultSet rs = meta.getTables("", null, null, null);
+            assertTrue(rs.next());
+            assertTableMetaData(rs, SYSTEM_CATALOG_SCHEMA, SYSTEM_CATALOG_TABLE, SYSTEM);
+            assertTrue(rs.next());
+            assertTableMetaData(rs, SYSTEM_CATALOG_SCHEMA, TYPE_SEQUENCE, SYSTEM);
+            assertTrue(rs.next());
+            assertTableMetaData(rs, null, PARENT_TABLE_NAME, TABLE);
+            assertTrue(rs.next());
+            assertTableMetaData(rs, null, PARENT_TABLE_NAME_NO_TENANT_TYPE_ID, TABLE);
+            assertFalse(rs.next());
+            
+            // make sure connections w/o tenant id only see non-tenant-specific columns
+            rs = meta.getColumns("", null, null, null);
+            while (rs.next()) {
+                assertNotEquals(TENANT_TABLE_NAME, rs.getString("TABLE_NAME"));
+            }
+            
+            // null catalog means across all tenant_ids
+            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, rs.getString(PhoenixDatabaseMetaData.TABLE_NAME));
+            assertEquals(PARENT_TABLE_NAME, rs.getString(PhoenixDatabaseMetaData.SUPERTABLE_NAME));
+            assertTrue(rs.next());
+            assertEquals(secondTenantId, rs.getString(PhoenixDatabaseMetaData.TABLE_CAT));
+            assertEquals(TENANT_TABLE_NAME, rs.getString(PhoenixDatabaseMetaData.TABLE_NAME));
+            assertEquals(PARENT_TABLE_NAME, rs.getString(PhoenixDatabaseMetaData.SUPERTABLE_NAME));
+            assertFalse(rs.next());
+            conn.close();
+            
+            // Global connection sees all tenant tables
+            conn = DriverManager.getConnection(getUrl());
+            rs = conn.getMetaData().getSuperTables(TENANT_ID, null, null);
+            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 = conn.getMetaData().getCatalogs();
+            assertTrue(rs.next());
+            assertEquals(TENANT_ID, rs.getString(PhoenixDatabaseMetaData.TABLE_CAT));
+            assertTrue(rs.next());
+            assertEquals(secondTenantId, rs.getString(PhoenixDatabaseMetaData.TABLE_CAT));
+            assertFalse(rs.next());
+        } finally {
+            props.clear();
+            props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(nextTimestamp()));
+            Connection secondTenantCon = DriverManager.getConnection(secondTenatConnectionURL, props);
+            try {
+                secondTenantCon.createStatement().executeUpdate("drop view " + TENANT_TABLE_NAME);
+            } finally {
+                try {secondTenantCon.close();} catch (Exception ignored) {}
+            }
+            conn.close();
+        }
+        
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(nextTimestamp()));
+        conn = DriverManager.getConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL, props);
+        try {   
+            // make sure tenant-specific connections only see their own tables
+            DatabaseMetaData meta = conn.getMetaData();
+            ResultSet rs = meta.getTables(null, null, null, null);
+            assertTrue(rs.next());
+            assertTableMetaData(rs, null, TENANT_TABLE_NAME, PTableType.VIEW);
+            assertTrue(rs.next());
+            assertTableMetaData(rs, null, TENANT_TABLE_NAME_NO_TENANT_TYPE_ID, PTableType.VIEW);
+            assertFalse(rs.next());
+            
+            // make sure tenants see parent table's columns and their own
+            rs = meta.getColumns(null, null, null, null);
+            assertTrue(rs.next());
+            assertColumnMetaData(rs, null, TENANT_TABLE_NAME, "user");
+            assertTrue(rs.next());
+            assertColumnMetaData(rs, null, TENANT_TABLE_NAME, "tenant_id");
+            assertTrue(rs.next());
+            assertColumnMetaData(rs, null, TENANT_TABLE_NAME, "tenant_type_id");
+            assertTrue(rs.next());
+            assertColumnMetaData(rs, null, TENANT_TABLE_NAME, "id");
+            assertTrue(rs.next());
+            assertColumnMetaData(rs, null, TENANT_TABLE_NAME, "tenant_col");
+            assertTrue(rs.next());
+            assertColumnMetaData(rs, null, TENANT_TABLE_NAME_NO_TENANT_TYPE_ID, "user");
+            assertTrue(rs.next());
+            assertColumnMetaData(rs, null, TENANT_TABLE_NAME_NO_TENANT_TYPE_ID, "tenant_id");
+            assertTrue(rs.next());
+            assertColumnMetaData(rs, null, TENANT_TABLE_NAME_NO_TENANT_TYPE_ID, "id");
+            assertTrue(rs.next());
+            assertColumnMetaData(rs, null, TENANT_TABLE_NAME_NO_TENANT_TYPE_ID, "tenant_col");
+            assertFalse(rs.next()); 
+        }
+        finally {
+            conn.close();
+        }
+    }
+    
+    private void assertTableMetaData(ResultSet rs, String schema, String table, PTableType tableType) throws SQLException {
+        assertEquals(schema, rs.getString("TABLE_SCHEM"));
+        assertEquals(table, rs.getString("TABLE_NAME"));
+        assertEquals(tableType.toString(), rs.getString("TABLE_TYPE"));
+    }
+    
+    private void assertColumnMetaData(ResultSet rs, String schema, String table, String column) throws SQLException {
+        assertEquals(schema, rs.getString("TABLE_SCHEM"));
+        assertEquals(table, rs.getString("TABLE_NAME"));
+        assertEquals(SchemaUtil.normalizeIdentifier(column), rs.getString("COLUMN_NAME"));
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/8d6e2a58/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificTablesDMLIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificTablesDMLIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificTablesDMLIT.java
new file mode 100644
index 0000000..d6601e3
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificTablesDMLIT.java
@@ -0,0 +1,387 @@
+/*
+ * 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.phoenix.end2end;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.apache.phoenix.schema.TableNotFoundException;
+import org.apache.phoenix.util.PhoenixRuntime;
+import org.apache.phoenix.util.TestUtil;
+import org.junit.Test;
+
+/**
+ * TODO: derived from BaseClientMangedTimeTest, but not setting SCN
+ * 
+ * @since 2.0
+ */
+public class TenantSpecificTablesDMLIT extends BaseTenantSpecificTablesIT {
+    
+    @Test
+    public void testBasicUpsertSelect() throws Exception {
+        Connection conn = nextConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL);
+        try {
+            conn.setAutoCommit(false);
+            conn.createStatement().executeUpdate("upsert into " + TENANT_TABLE_NAME + " (id, tenant_col) values (1, 'Cheap Sunglasses')");
+            conn.createStatement().executeUpdate("upsert into " + TENANT_TABLE_NAME + " (id, tenant_col) values (2, 'Viva Las Vegas')");
+            conn.commit();
+            conn.close();
+            
+            conn = nextConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL);
+            ResultSet rs = conn.createStatement().executeQuery("select tenant_col from " + TENANT_TABLE_NAME + " where id = 1");
+            assertTrue("Expected 1 row in result set", rs.next());
+            assertEquals("Cheap Sunglasses", rs.getString(1));
+            assertFalse("Expected 1 row in result set", rs.next());
+        }
+        finally {
+            conn.close();
+        }
+    }
+    
+    private Connection nextConnection(String url) throws SQLException {
+        Properties props = new Properties(TestUtil.TEST_PROPERTIES);
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(nextTimestamp()));
+        return DriverManager.getConnection(url, props);
+    }
+    
+    @Test
+    public void testJoinWithGlobalTable() throws Exception {
+        Connection conn = nextConnection(getUrl());
+        conn.createStatement().execute("create table foo (k INTEGER NOT NULL PRIMARY KEY)");
+        conn.close();
+
+        conn = nextConnection(getUrl());
+        conn.createStatement().execute("upsert into foo(k) values(1)");
+        conn.commit();
+        conn.close();
+
+        conn = nextConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL);
+        try {
+            conn.setAutoCommit(false);
+            conn.createStatement().executeUpdate("upsert into " + TENANT_TABLE_NAME + " (id, tenant_col) values (1, 'Cheap Sunglasses')");
+            conn.createStatement().executeUpdate("upsert into " + TENANT_TABLE_NAME + " (id, tenant_col) values (2, 'Viva Las Vegas')");
+            conn.commit();
+            conn.close();
+            
+            conn = nextConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL);
+            ResultSet rs = conn.createStatement().executeQuery("select tenant_col from " + TENANT_TABLE_NAME + " join foo on k=id");
+            assertTrue("Expected 1 row in result set", rs.next());
+            assertEquals("Cheap Sunglasses", rs.getString(1));
+            assertFalse("Expected 1 row in result set", rs.next());
+        }
+        finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testSelectOnlySeesTenantData() throws Exception {
+        Connection conn = nextConnection(getUrl());
+        try {
+            conn.setAutoCommit(true);
+            conn.createStatement().executeUpdate("delete from " + PARENT_TABLE_NAME);
+            conn.close();
+            
+            conn = nextConnection(getUrl());
+            conn.setAutoCommit(true);
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME + " (tenant_id, tenant_type_id, id, user) values ('AC/DC', 'abc', 1, 'Bon Scott')");
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME + " (tenant_id, tenant_type_id, id, user) values ('" + TENANT_ID + "', '" + TENANT_TYPE_ID + "', 1, 'Billy Gibbons')");
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME + " (tenant_id, tenant_type_id, id, user) values ('" + TENANT_ID + "', 'def', 1, 'Billy Gibbons')");
+            conn.close();
+            
+            conn = nextConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL);
+            ResultSet rs = conn.createStatement().executeQuery("select user from " + TENANT_TABLE_NAME);
+            assertTrue("Expected 1 row in result set", rs.next());
+            assertEquals("Billy Gibbons", rs.getString(1));
+            assertFalse("Expected 1 row in result set", rs.next());
+            
+            rs = conn.createStatement().executeQuery("select count(*) from " + TENANT_TABLE_NAME);
+            assertTrue("Expected 1 row in result set", rs.next());
+            assertEquals(1, rs.getInt(1));
+            assertFalse("Expected 1 row in result set", rs.next());
+        }
+        finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testDeleteOnlyDeletesTenantData() throws Exception {
+        Connection conn = nextConnection(getUrl());
+        try {
+            conn.setAutoCommit(true);
+            conn.createStatement().executeUpdate("delete from " + PARENT_TABLE_NAME);
+            conn.close();
+            
+            conn = nextConnection(getUrl());
+            conn.setAutoCommit(true);
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME + " (tenant_id, tenant_type_id, id, user) values ('AC/DC', 'abc', 1, 'Bon Scott')");
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME + " (tenant_id, tenant_type_id, id, user) values ('" + TENANT_ID + "', '" + TENANT_TYPE_ID + "', 1, 'Billy Gibbons')");
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME + " (tenant_id, tenant_type_id, id, user) values ('" + TENANT_ID + "', 'def', 1, 'Billy Gibbons')");
+            conn.close();
+            
+            conn = nextConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL);
+            conn.setAutoCommit(true);
+            int count = conn.createStatement().executeUpdate("delete from " + TENANT_TABLE_NAME);
+            assertEquals("Expected 1 row have been deleted", 1, count);
+            conn.close();
+            
+            conn = nextConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL);
+            conn.setAutoCommit(true);
+            ResultSet rs = conn.createStatement().executeQuery("select * from " + TENANT_TABLE_NAME);
+            assertFalse("Expected no rows in result set", rs.next());
+            conn.close();
+            
+            conn = nextConnection(getUrl());
+            rs = conn.createStatement().executeQuery("select count(*) from " + PARENT_TABLE_NAME);
+            rs.next();
+            assertEquals(2, rs.getInt(1));
+        }
+        finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testDeleteOnlyDeletesTenantDataWithNoTenantTypeId() throws Exception {
+        Connection conn = nextConnection(getUrl());
+        try {
+            conn.setAutoCommit(true);
+            conn.createStatement().executeUpdate("delete from " + PARENT_TABLE_NAME_NO_TENANT_TYPE_ID);
+            conn.close();
+            
+            conn = nextConnection(getUrl());
+            conn.setAutoCommit(true);
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME_NO_TENANT_TYPE_ID + " (tenant_id, id, user) values ('AC/DC', 1, 'Bon Scott')");
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME_NO_TENANT_TYPE_ID + " (tenant_id, id, user) values ('" + TENANT_ID + "', 1, 'Billy Gibbons')");
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME_NO_TENANT_TYPE_ID + " (tenant_id, id, user) values ('" + TENANT_ID + "', 2, 'Billy Gibbons')");
+            conn.close();
+            
+            conn = nextConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL);
+            conn.setAutoCommit(true);
+            int count = conn.createStatement().executeUpdate("delete from " + TENANT_TABLE_NAME_NO_TENANT_TYPE_ID);
+            assertEquals("Expected 2 rows have been deleted", 2, count);
+            conn.close();
+            
+            conn = nextConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL);
+            ResultSet rs = conn.createStatement().executeQuery("select * from " + TENANT_TABLE_NAME_NO_TENANT_TYPE_ID);
+            assertFalse("Expected no rows in result set", rs.next());
+            conn.close();
+            
+            conn = nextConnection(getUrl());
+            rs = conn.createStatement().executeQuery("select count(*) from " + PARENT_TABLE_NAME_NO_TENANT_TYPE_ID);
+            rs.next();
+            assertEquals(1, rs.getInt(1));
+        }
+        finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testDeleteAllTenantTableData() throws Exception {
+        Connection conn = nextConnection(getUrl());
+        try {
+            conn.setAutoCommit(true);
+            conn.createStatement().executeUpdate("delete from " + PARENT_TABLE_NAME);
+            conn.close();
+            
+            conn = nextConnection(getUrl());
+            conn.setAutoCommit(true);
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME + " (tenant_id, tenant_type_id, id, user) values ('AC/DC', 'abc', 1, 'Bon Scott')");
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME + " (tenant_id, tenant_type_id, id, user) values ('" + TENANT_ID + "', '" + TENANT_TYPE_ID + "', 1, 'Billy Gibbons')");
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME + " (tenant_id, tenant_type_id, id, user) values ('" + TENANT_ID + "', 'def', 1, 'Billy Gibbons')");
+            
+            conn.close();
+            
+            conn = nextConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL);
+            conn.createStatement().execute("delete from " + TENANT_TABLE_NAME);
+            conn.commit();
+            conn.close();
+            
+            conn = nextConnection(getUrl());
+            ResultSet rs = conn.createStatement().executeQuery("select count(*) from " + PARENT_TABLE_NAME);
+            rs.next();
+            assertEquals(2, rs.getInt(1));
+        }
+        finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testDropTenantTableDeletesNoData() throws Exception {
+        Connection conn = nextConnection(getUrl());
+        try {
+            conn.setAutoCommit(true);
+            conn.createStatement().executeUpdate("delete from " + PARENT_TABLE_NAME_NO_TENANT_TYPE_ID);
+            conn.close();
+            
+            conn = nextConnection(getUrl());
+            conn.setAutoCommit(true);
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME_NO_TENANT_TYPE_ID + " (tenant_id, id, user) values ('AC/DC', 1, 'Bon Scott')");
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME_NO_TENANT_TYPE_ID + " (tenant_id, id, user) values ('" + TENANT_ID + "', 1, 'Billy Gibbons')");
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME_NO_TENANT_TYPE_ID + " (tenant_id, id, user) values ('" + TENANT_ID + "', 2, 'Billy Gibbons')");
+            
+            conn.close();
+            
+            conn = nextConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL);
+            conn.createStatement().execute("drop view " + TENANT_TABLE_NAME_NO_TENANT_TYPE_ID);
+            conn.close();
+            
+            conn = nextConnection(getUrl());
+            ResultSet rs = conn.createStatement().executeQuery("select count(*) from " + PARENT_TABLE_NAME_NO_TENANT_TYPE_ID);
+            rs.next();
+            assertEquals(3, rs.getInt(1));
+        }
+        finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testUpsertSelectOnlyUpsertsTenantData() throws Exception {
+        Connection conn = nextConnection(getUrl());
+        try {
+            conn.setAutoCommit(true);
+            conn.createStatement().executeUpdate("delete from " + PARENT_TABLE_NAME);
+            conn.close();
+            
+            conn = nextConnection(getUrl());
+            conn.setAutoCommit(true);
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME + " (tenant_id, tenant_type_id, id, user) values ('AC/DC', 'aaa', 1, 'Bon Scott')");
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME + " (tenant_id, tenant_type_id, id, user) values ('" + TENANT_ID + "', '" + TENANT_TYPE_ID + "', 1, 'Billy Gibbons')");
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME + " (tenant_id, tenant_type_id, id, user) values ('" + TENANT_ID + "', 'def', 2, 'Billy Gibbons')");
+            conn.close();
+            
+            conn = nextConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL);
+            conn.setAutoCommit(true);
+            int count = conn.createStatement().executeUpdate("upsert into " + TENANT_TABLE_NAME + "(id, user) select id+100, user from " + TENANT_TABLE_NAME);
+            assertEquals("Expected 1 row to have been inserted", 1, count);
+            conn.close();
+            
+            conn = nextConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL);
+            ResultSet rs = conn.createStatement().executeQuery("select count(*) from " + TENANT_TABLE_NAME);
+            rs.next();
+            assertEquals(2, rs.getInt(1));
+        }
+        finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testUpsertSelectOnlyUpsertsTenantDataWithDifferentTenantTable() throws Exception {
+        createTestTable(PHOENIX_JDBC_TENANT_SPECIFIC_URL, "CREATE VIEW ANOTHER_TENANT_TABLE ( " + 
+            "tenant_col VARCHAR) AS SELECT * FROM PARENT_TABLE WHERE tenant_type_id = 'def'", null, nextTimestamp(), false);
+        
+        Connection conn = nextConnection(getUrl());
+        try {
+            conn.setAutoCommit(true);
+            conn.createStatement().executeUpdate("delete from " + PARENT_TABLE_NAME);
+            conn.close();
+            
+            conn = nextConnection(getUrl());
+            conn.setAutoCommit(true);
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME + " (tenant_id, tenant_type_id, id, user) values ('AC/DC', 'aaa', 1, 'Bon Scott')");
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME + " (tenant_id, tenant_type_id, id, user) values ('" + TENANT_ID + "', '" + TENANT_TYPE_ID + "', 1, 'Billy Gibbons')");
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME + " (tenant_id, tenant_type_id, id, user) values ('" + TENANT_ID + "', 'def', 2, 'Billy Gibbons')");
+            conn.close();
+            
+            conn = nextConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL);
+            conn.setAutoCommit(true);
+            int count = conn.createStatement().executeUpdate("upsert into " + TENANT_TABLE_NAME + "(id, user) select id+100, user from ANOTHER_TENANT_TABLE where id=2");
+            assertEquals("Expected 1 row to have been inserted", 1, count);
+            conn.close();
+            
+            conn = nextConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL);
+            ResultSet rs = conn.createStatement().executeQuery("select count(*) from " + TENANT_TABLE_NAME);
+            rs.next();
+            assertEquals(2, rs.getInt(1));
+        }
+        finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testUpsertValuesOnlyUpsertsTenantData() throws Exception {
+        Connection conn = nextConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL);
+        try {
+            conn.setAutoCommit(true);
+            int count = conn.createStatement().executeUpdate("upsert into " + TENANT_TABLE_NAME + " (id, user) values (1, 'Bon Scott')");
+            assertEquals("Expected 1 row to have been inserted", 1, count);
+            conn.close();
+            
+            conn = nextConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL);
+            ResultSet rs = conn.createStatement().executeQuery("select count(*) from " + TENANT_TABLE_NAME);
+            rs.next();
+            assertEquals(1, rs.getInt(1));
+        }
+        finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testBaseTableCanBeUsedInStatementsInMultitenantConnections() throws Exception {
+        Connection conn = nextConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL);
+        try {
+            ResultSet rs = conn.createStatement().executeQuery("select * from " + PARENT_TABLE_NAME);
+            assertFalse(rs.next());
+            conn.close();
+            
+            conn = nextConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL);
+            conn.setAutoCommit(true);
+            conn.createStatement().executeUpdate("upsert into " + PARENT_TABLE_NAME + " (tenant_type_id, id, user) values ('" + TENANT_TYPE_ID + "', 1, 'Billy Gibbons')");
+            conn.close();
+
+            conn = nextConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL);
+            rs = conn.createStatement().executeQuery("select user from " + PARENT_TABLE_NAME);
+            assertTrue(rs.next());
+            assertEquals(rs.getString(1),"Billy Gibbons");
+            assertFalse(rs.next());
+        }
+        finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testTenantTableCannotBeUsedInStatementsInNonMultitenantConnections() throws Exception {
+        Connection conn = nextConnection(getUrl());
+        try {
+            try {
+                conn.createStatement().execute("select * from " + TENANT_TABLE_NAME);
+                fail();
+            }
+            catch (TableNotFoundException expected) {};   
+        }
+        finally {
+            conn.close();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/8d6e2a58/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexIT.java
new file mode 100644
index 0000000..2f4a1d1
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexIT.java
@@ -0,0 +1,33 @@
+/*
+ * 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.phoenix.end2end;
+
+import org.junit.Test;
+
+public class TenantSpecificViewIndexIT extends BaseTenantSpecificViewIndexIT {
+	
+    @Test
+    public void testUpdatableView() throws Exception {
+        testUpdatableView(null);
+    }
+
+    @Test
+    public void testUpdatableViewsWithSameNameDifferentTenants() throws Exception {
+        testUpdatableViewsWithSameNameDifferentTenants(null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/8d6e2a58/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexSaltedIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexSaltedIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexSaltedIT.java
new file mode 100644
index 0000000..cb56e5d
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexSaltedIT.java
@@ -0,0 +1,17 @@
+package org.apache.phoenix.end2end;
+
+import org.junit.Test;
+
+public class TenantSpecificViewIndexSaltedIT extends BaseTenantSpecificViewIndexIT {
+    private static final Integer SALT_BUCKETS = 3;
+    
+    @Test
+    public void testUpdatableSaltedView() throws Exception {
+        testUpdatableView(SALT_BUCKETS);
+    }
+    
+    @Test
+    public void testUpdatableViewsWithSameNameDifferentTenants() throws Exception {
+        testUpdatableViewsWithSameNameDifferentTenants(SALT_BUCKETS);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/8d6e2a58/phoenix-core/src/it/java/org/apache/phoenix/end2end/ToCharFunctionIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ToCharFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ToCharFunctionIT.java
new file mode 100644
index 0000000..07bd645
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ToCharFunctionIT.java
@@ -0,0 +1,240 @@
+/*
+ * 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.phoenix.end2end;
+
+import static org.apache.phoenix.util.TestUtil.PHOENIX_JDBC_URL;
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.text.DateFormat;
+import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
+import java.util.Properties;
+import java.util.TimeZone;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.phoenix.util.PhoenixRuntime;
+
+/**
+ * Tests for the TO_CHAR built-in function.
+ * 
+ * @see ToCharFunction
+ * 
+ * @since 0.1
+ */
+public class ToCharFunctionIT extends BaseClientManagedTimeIT {
+    
+    public static final String TO_CHAR_TABLE_NAME = "TO_CHAR_TABLE";
+    
+    private Date row1Date;
+    private Time row1Time;
+    private Timestamp row1Timestamp;
+    private Integer row1Integer;
+    private BigDecimal row1Decimal;
+    private Date row2Date;
+    private Time row2Time;
+    private Timestamp row2Timestamp;
+    private Integer row2Integer;
+    private BigDecimal row2Decimal;
+    
+    public static final String TO_CHAR_TABLE_DDL = "create table " + TO_CHAR_TABLE_NAME +
+        "(pk integer not null, \n" + 
+        "col_date date not null, \n" +
+        "col_time date not null, \n" +
+        "col_timestamp timestamp not null, \n" +
+        "col_integer integer not null, \n" + 
+        "col_decimal decimal not null \n" + 
+        "CONSTRAINT my_pk PRIMARY KEY (pk))";
+
+    @Before
+    @edu.umd.cs.findbugs.annotations.SuppressWarnings(
+            value="DMI_BIGDECIMAL_CONSTRUCTED_FROM_DOUBLE", 
+            justification="Test code.")
+    public void initTable() throws Exception {
+        long ts = nextTimestamp();
+        createTestTable(getUrl(), TO_CHAR_TABLE_DDL, null, ts-2);
+        String url = PHOENIX_JDBC_URL + ";" + PhoenixRuntime.CURRENT_SCN_ATTRIB + "=" + ts;
+        Properties props = new Properties(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(url, props);
+        conn.setAutoCommit(false);
+        
+        PreparedStatement stmt = conn.prepareStatement(
+                "upsert into " + TO_CHAR_TABLE_NAME +
+                "    (pk, " +
+                "    col_date," +
+                "    col_time," +
+                "    col_timestamp," +
+                "    col_integer," +
+                "    col_decimal)" +
+                "VALUES (?, ?, ?, ?, ?, ?)");
+        
+        row1Date = new Date(System.currentTimeMillis() - 10000);
+        row1Time = new Time(System.currentTimeMillis() - 1000);
+        row1Timestamp = new Timestamp(System.currentTimeMillis() + 10000);
+        row1Integer = 666;
+        row1Decimal = new BigDecimal(33.333);
+        
+        stmt.setInt(1, 1);
+        stmt.setDate(2, row1Date);
+        stmt.setTime(3, row1Time);
+        stmt.setTimestamp(4, row1Timestamp);
+        stmt.setInt(5, row1Integer);
+        stmt.setBigDecimal(6, row1Decimal);
+        stmt.execute();
+        
+        row2Date = new Date(System.currentTimeMillis() - 1234567);
+        row2Time = new Time(System.currentTimeMillis() - 1234);
+        row2Timestamp = new Timestamp(System.currentTimeMillis() + 1234567);
+        row2Integer = 10011;
+        row2Decimal = new BigDecimal(123456789.66);
+        
+        stmt.setInt(1, 2);
+        stmt.setDate(2, row2Date);
+        stmt.setTime(3, row2Time);
+        stmt.setTimestamp(4, row2Timestamp);
+        stmt.setInt(5, row2Integer);
+        stmt.setBigDecimal(6, row2Decimal);
+        
+        stmt.execute();
+        conn.commit();
+        conn.close();
+    }
+    
+    @Test
+    public void testDateProjection() throws Exception {
+        String pattern = "yyyy.MM.dd G HH:mm:ss z";
+        String query = "select to_char(col_date, '" + pattern + "') from " + TO_CHAR_TABLE_NAME + " WHERE pk = 1";
+        String expectedString = getGMTDateFormat(pattern).format(row1Date);
+        runOneRowProjectionQuery(query, expectedString);
+    }
+    
+    @Test
+    public void testTimeProjection() throws Exception {
+        String pattern = "HH:mm:ss z";
+        String query = "select to_char(col_time, '" + pattern + "') from " + TO_CHAR_TABLE_NAME + " WHERE pk = 1";
+        String expectedString = getGMTDateFormat(pattern).format(row1Time);
+        runOneRowProjectionQuery(query, expectedString);
+    }
+
+    @Test
+    public void testTimestampProjection() throws Exception {
+        String pattern = "yyMMddHHmmssZ";
+        String query = "select to_char(col_timestamp, '" + pattern + "') from " + TO_CHAR_TABLE_NAME + " WHERE pk = 2";
+        String expectedString = getGMTDateFormat(pattern).format(row2Timestamp);
+        runOneRowProjectionQuery(query, expectedString);
+    }
+    
+    @Test
+    public void testIntegerProjection() throws Exception {
+        String pattern = "00";
+        String query = "select to_char(col_integer, '" + pattern + "') from " + TO_CHAR_TABLE_NAME + " WHERE pk = 1";
+        String expectedString = new DecimalFormat(pattern).format(row1Integer);
+        runOneRowProjectionQuery(query, expectedString);
+    }
+    
+    @Test
+    public void testDecimalProjection() throws Exception {
+        String pattern = "0.###E0";
+        String query = "select to_char(col_decimal, '" + pattern + "') from " + TO_CHAR_TABLE_NAME + " WHERE pk = 2";
+        String expectedString = new DecimalFormat(pattern).format(row2Decimal);
+        runOneRowProjectionQuery(query, expectedString);
+    }
+    
+    @Test 
+    public void testDateFilter() throws Exception {
+        String pattern = "yyyyMMddHHmmssZ";
+        String expectedString = getGMTDateFormat(pattern).format(row1Date);
+        String query = "select pk from " + TO_CHAR_TABLE_NAME + " WHERE to_char(col_date, '" + pattern + "') = '" + expectedString + "'";
+        runOneRowFilterQuery(query, 1);
+    }
+    
+    @Test 
+    public void testTimeFilter() throws Exception {
+        String pattern = "ddHHmmssSSSZ";
+        String expectedString = getGMTDateFormat(pattern).format(row1Time);
+        String query = "select pk from " + TO_CHAR_TABLE_NAME + " WHERE to_char(col_time, '" + pattern + "') = '" + expectedString + "'";
+        runOneRowFilterQuery(query, 1);
+    }
+    
+    @Test 
+    public void testTimestampFilter() throws Exception {
+        String pattern = "yy.MM.dd G HH:mm:ss z";
+        String expectedString = getGMTDateFormat(pattern).format(row2Timestamp);
+        String query = "select pk from " + TO_CHAR_TABLE_NAME + " WHERE to_char(col_timestamp, '" + pattern + "') = '" + expectedString + "'";
+        runOneRowFilterQuery(query, 2);
+    }
+    
+    @Test 
+    public void testIntegerFilter() throws Exception {
+        String pattern = "000";
+        String expectedString = new DecimalFormat(pattern).format(row1Integer);
+        String query = "select pk from " + TO_CHAR_TABLE_NAME + " WHERE to_char(col_integer, '" + pattern + "') = '" + expectedString + "'";
+        runOneRowFilterQuery(query, 1);
+    }
+    
+    @Test 
+    public void testDecimalFilter() throws Exception {
+        String pattern = "00.###E0";
+        String expectedString = new DecimalFormat(pattern).format(row2Decimal);
+        String query = "select pk from " + TO_CHAR_TABLE_NAME + " WHERE to_char(col_decimal, '" + pattern + "') = '" + expectedString + "'";
+        runOneRowFilterQuery(query, 2);
+    }
+    
+    private void runOneRowProjectionQuery(String oneRowQuery, String projectedValue) throws Exception {
+    	runOneRowQueryTest(oneRowQuery, null, projectedValue);
+    }
+    
+    private void runOneRowFilterQuery(String oneRowQuery, int pkValue) throws Exception {
+    	runOneRowQueryTest(oneRowQuery, pkValue, null);
+    }
+    
+    private void runOneRowQueryTest(String oneRowQuery, Integer pkValue, String projectedValue) throws Exception {
+        Connection conn = DriverManager.getConnection(PHOENIX_JDBC_URL);
+        try {
+            PreparedStatement statement = conn.prepareStatement(oneRowQuery);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            if (pkValue != null)
+            	assertEquals(pkValue.intValue(), rs.getInt(1));
+            else
+            	assertEquals(projectedValue, rs.getString(1));
+            assertFalse(rs.next());
+        }
+        finally {
+        	conn.close();
+        }
+    }
+    
+    private DateFormat getGMTDateFormat(String pattern) {
+        DateFormat result = new SimpleDateFormat(pattern);
+        result.setTimeZone(TimeZone.getTimeZone("GMT"));
+        return result;
+    }
+}
\ No newline at end of file


Mime
View raw message