blob: e4479ac9bf5989c31dff2664ffc82bdc487c9fc4 [file] [log] [blame]
/*
* 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_DROP_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.KEY_SEQ;
import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.ORDINAL_POSITION;
import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA;
import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_FUNCTION_TABLE;
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.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.phoenix.exception.SQLExceptionCode;
import org.apache.phoenix.jdbc.PhoenixConnection;
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.PropertiesUtil;
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
Connection conn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES));
HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin();
assertEquals(0, admin.listTables(TENANT_TABLE_NAME).length);
}
@Test
public void testCreateTenantTableTwice() throws Exception {
try {
Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
Connection conn = DriverManager.getConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL, props);
conn.createStatement().execute(TENANT_TABLE_DDL);
fail();
}
catch (TableAlreadyExistsException expected) {}
}
@Test
public void testCreateTenantViewFromNonMultiTenant() throws Exception {
String tableName = generateUniqueName();
createTestTable(getUrl(), "CREATE TABLE " + tableName + " (K VARCHAR PRIMARY KEY)");
try {
String viewName = generateUniqueName();
// 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 " + viewName + " (COL VARCHAR) AS SELECT * FROM " + tableName);
}
catch (TableNotFoundException expected) {
}
}
@Test
public void testAlteringMultiTenancyForTableWithViewsNotAllowed() throws Exception {
Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
String multiTenantTable = "MT_" + generateUniqueName();
String globalTable = "G_" + generateUniqueName();
// create the two base tables
try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
String ddl = "CREATE TABLE " + multiTenantTable + " (TENANT_ID VARCHAR NOT NULL, PK1 VARCHAR NOT NULL, V1 VARCHAR, V2 VARCHAR, V3 VARCHAR CONSTRAINT NAME_PK PRIMARY KEY(TENANT_ID, PK1)) MULTI_TENANT = true ";
conn.createStatement().execute(ddl);
ddl = "CREATE TABLE " + globalTable + " (TENANT_ID VARCHAR NOT NULL, PK1 VARCHAR NOT NULL, V1 VARCHAR, V2 VARCHAR, V3 VARCHAR CONSTRAINT NAME_PK PRIMARY KEY(TENANT_ID, PK1)) ";
conn.createStatement().execute(ddl);
}
String t1 = generateUniqueName();
props.setProperty(PhoenixRuntime.TENANT_ID_ATTRIB, t1);
// create view on multi-tenant table
try (Connection tenantConn = DriverManager.getConnection(getUrl(), props)) {
String viewName = "V_" + generateUniqueName();
String viewDDL = "CREATE VIEW " + viewName + " AS SELECT * FROM " + multiTenantTable;
tenantConn.createStatement().execute(viewDDL);
}
props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
// create view on global table
try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
String viewName = "V_" + generateUniqueName();
conn.createStatement().execute("CREATE VIEW " + viewName + " AS SELECT * FROM " + globalTable);
}
props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
try {
conn.createStatement().execute("ALTER TABLE " + globalTable + " SET MULTI_TENANT = " + true);
fail();
} catch (SQLException e) {
assertEquals(SQLExceptionCode.CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
}
try {
conn.createStatement().execute("ALTER TABLE " + multiTenantTable + " SET MULTI_TENANT = " + false);
fail();
} catch (SQLException e) {
assertEquals(SQLExceptionCode.CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
}
}
}
@Test(expected=TableNotFoundException.class)
public void testDeletionOfParentTableFailsOnTenantSpecificConnection() throws Exception {
Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
props.setProperty(PhoenixRuntime.TENANT_ID_ATTRIB, TENANT_ID); // connection is tenant-specific
Connection conn = DriverManager.getConnection(getUrl(), props);
conn.createStatement().execute("DROP TABLE " + PARENT_TABLE_NAME);
conn.close();
}
public void testCreationOfParentTableFailsOnTenantSpecificConnection() throws Exception {
try {
createTestTable(PHOENIX_JDBC_TENANT_SPECIFIC_URL, "CREATE TABLE " + generateUniqueName() + "( \n" +
" \"user\" VARCHAR ,\n" +
" id INTEGER not null primary key desc\n" +
" ) ");
fail();
} catch (SQLException e) {
assertEquals(SQLExceptionCode.CANNOT_CREATE_TENANT_SPECIFIC_TABLE.getErrorCode(), e.getErrorCode());
}
}
@Test
public void testTenantSpecificAndParentTablesMayBeInDifferentSchemas() throws SQLException {
String fullTableName = "DIFFSCHEMA." + generateUniqueName();
createTestTable(PHOENIX_JDBC_TENANT_SPECIFIC_URL, "CREATE VIEW " + fullTableName + " ( \n" +
" tenant_col VARCHAR) AS SELECT * \n" +
" FROM " + PARENT_TABLE_NAME + " WHERE tenant_type_id = 'aaa'");
try {
createTestTable(PHOENIX_JDBC_TENANT_SPECIFIC_URL, "CREATE VIEW " + fullTableName + "( \n" +
" tenant_col VARCHAR) AS SELECT *\n"+
" FROM DIFFSCHEMA." + PARENT_TABLE_NAME + " WHERE tenant_type_id = 'aaa'");
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);
createTestTable(PHOENIX_JDBC_TENANT_SPECIFIC_URL, "CREATE VIEW " + fullTableName + "( \n" +
" tenant_col VARCHAR) AS SELECT *\n"+
" FROM DIFFSCHEMA." + PARENT_TABLE_NAME + " WHERE tenant_type_id = 'aaa'");
}
@Test
public void testTenantSpecificTableCanDeclarePK() throws SQLException {
createTestTable(PHOENIX_JDBC_TENANT_SPECIFIC_URL, "CREATE VIEW " + generateUniqueName() + "( \n" +
" tenant_col VARCHAR PRIMARY KEY) AS SELECT *\n" +
" FROM " + PARENT_TABLE_NAME);
}
@Test(expected=ColumnAlreadyExistsException.class)
public void testTenantSpecificTableCannotOverrideParentCol() throws SQLException {
createTestTable(PHOENIX_JDBC_TENANT_SPECIFIC_URL, "CREATE VIEW " + generateUniqueName() + " ( \n" +
" \"user\" INTEGER) AS SELECT *\n" +
" FROM " + PARENT_TABLE_NAME);
}
@Test
public void testBaseTableWrongFormatWithTenantTypeId() throws Exception {
// only two PK columns for multi_tenant, multi_type
try {
createTestTable(getUrl(),
"CREATE TABLE " + generateUniqueName() +
"(TENANT_ID VARCHAR NOT NULL PRIMARY KEY, ID VARCHAR, A INTEGER) MULTI_TENANT=true");
fail();
}
catch (SQLException expected) {
assertEquals(SQLExceptionCode.INSUFFICIENT_MULTI_TENANT_COLUMNS.getErrorCode(), expected.getErrorCode());
}
}
@Test
public void testAddDropColumn() throws Exception {
Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
Connection conn = DriverManager.getConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL, props);
conn.setAutoCommit(true);
try {
conn.createStatement().execute("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.createStatement().execute("upsert into " + TENANT_TABLE_NAME + " (id, tenant_col2) values (2, 'a')");
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.createStatement().execute("alter view " + TENANT_TABLE_NAME + " drop column tenant_col");
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_NAME);
fail();
}
catch (ColumnNotFoundException expected) {}
}
finally {
conn.close();
}
}
@Test
public void testDropOfPKInTenantTablesNotAllowed() throws Exception {
Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
Connection conn = DriverManager.getConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL, props);
try {
// try removing a PK col
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 = PropertiesUtil.deepCopy(TEST_PROPERTIES);
Connection conn = DriverManager.getConnection(getUrl(), props);
try {
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, which is allowed
try {
conn.createStatement().execute("alter table " + PARENT_TABLE_NAME + " drop column \"user\"");
}
catch (SQLException expected) {
fail("We should be able to drop a non pk base table column");
}
}
finally {
conn.close();
}
}
@Test
public void testDisallowDropParentTableWithExistingTenantTable() throws Exception {
Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
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 testAllowDropParentTableWithCascadeAndSingleTenantTable() throws Exception {
Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
Connection conn = DriverManager.getConnection(getUrl(), props);
Connection connTenant = null;
try {
// Drop Parent Table
conn.createStatement().executeUpdate("DROP TABLE " + PARENT_TABLE_NAME + " CASCADE");
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
String tenantTable2 = "V_" + generateUniqueName();
createTestTable(PHOENIX_JDBC_TENANT_SPECIFIC_URL2, TENANT_TABLE_DDL.replace(TENANT_TABLE_NAME, tenantTable2));
//TODO Create some tenant specific table indexes
Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
Connection conn = null;
Connection connTenant1 = null;
Connection connTenant2 = null;
try {
List<String> sortedCatalogs = Arrays.asList(TENANT_ID, TENANT_ID2);
Collections.sort(sortedCatalogs);
conn = DriverManager.getConnection(getUrl(), props);
DatabaseMetaData meta = conn.getMetaData();
ResultSet rs = meta.getTables(null, "", StringUtil.escapeLike(TENANT_TABLE_NAME), new String[] {PTableType.VIEW.getValue().getString()});
assertTrue(rs.next());
assertEquals(TENANT_ID, rs.getString(PhoenixDatabaseMetaData.TABLE_CAT));
assertTableMetaData(rs, null, TENANT_TABLE_NAME, PTableType.VIEW);
assertFalse(rs.next());
rs = meta.getTables(null, "", StringUtil.escapeLike(tenantTable2), new String[] {PTableType.VIEW.getValue().getString()});
assertTrue(rs.next());
assertEquals(TENANT_ID2, rs.getString(PhoenixDatabaseMetaData.TABLE_CAT));
assertTableMetaData(rs, null, tenantTable2, PTableType.VIEW);
assertFalse(rs.next());
rs = meta.getTables(null, "", StringUtil.escapeLike(TENANT_TABLE_NAME_NO_TENANT_TYPE_ID), new String[] {PTableType.VIEW.getValue().getString()});
assertTrue(rs.next());
assertEquals(TENANT_ID, rs.getString(PhoenixDatabaseMetaData.TABLE_CAT));
assertTableMetaData(rs, null, TENANT_TABLE_NAME_NO_TENANT_TYPE_ID, PTableType.VIEW);
assertFalse(rs.next());
// Drop Parent Table
conn.createStatement().executeUpdate("DROP TABLE " + PARENT_TABLE_NAME + " CASCADE");
// Validate Tenant Views are dropped
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
rs = meta.getTables(null, "", StringUtil.escapeLike(TENANT_TABLE_NAME), new String[] {PTableType.VIEW.getValue().getString()});
assertFalse(rs.next());
rs = meta.getTables(null, "", StringUtil.escapeLike(tenantTable2), new String[] {PTableType.VIEW.getValue().getString()});
assertFalse(rs.next());
rs = meta.getTables(null, "", StringUtil.escapeLike(TENANT_TABLE_NAME_NO_TENANT_TYPE_ID), new String[] {PTableType.VIEW.getValue().getString()});
assertTrue(rs.next());
assertEquals(TENANT_ID, rs.getString(PhoenixDatabaseMetaData.TABLE_CAT));
assertTableMetaData(rs, null, TENANT_TABLE_NAME_NO_TENANT_TYPE_ID, PTableType.VIEW);
assertFalse(rs.next());
} 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 = "T_" + generateUniqueName();
String secondTenatConnectionURL = PHOENIX_JDBC_TENANT_SPECIFIC_URL.replace(TENANT_ID, tenantId2);
String tenantTable2 = "V_" + generateUniqueName();
createTestTable(secondTenatConnectionURL, TENANT_TABLE_DDL.replace(TENANT_TABLE_NAME, tenantTable2));
Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
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("", "", StringUtil.escapeLike(PARENT_TABLE_NAME), new String[] {TABLE.getValue().getString()});
assertTrue(rs.next());
assertTableMetaData(rs, null, PARENT_TABLE_NAME, TABLE);
assertFalse(rs.next());
rs = meta.getTables("", "", StringUtil.escapeLike(PARENT_TABLE_NAME_NO_TENANT_TYPE_ID), new String[] {TABLE.getValue().getString()});
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"));
assertNotEquals(tenantTable2, rs.getString("TABLE_NAME"));
}
List<String> sortedTableNames = Arrays.asList(TENANT_TABLE_NAME, TENANT_TABLE_NAME_NO_TENANT_TYPE_ID);
Collections.sort(sortedTableNames);
List<String> sortedParentNames;
if (sortedTableNames.get(0).equals(TENANT_TABLE_NAME)) {
sortedParentNames = Arrays.asList(PARENT_TABLE_NAME, PARENT_TABLE_NAME_NO_TENANT_TYPE_ID);
} else {
sortedParentNames = Arrays.asList(PARENT_TABLE_NAME_NO_TENANT_TYPE_ID, PARENT_TABLE_NAME);
}
rs = meta.getSuperTables(TENANT_ID, null, null);
assertTrue(rs.next());
assertEquals(TENANT_ID, rs.getString(PhoenixDatabaseMetaData.TABLE_CAT));
assertEquals(sortedTableNames.get(0), rs.getString(PhoenixDatabaseMetaData.TABLE_NAME));
assertEquals(sortedParentNames.get(0), rs.getString(PhoenixDatabaseMetaData.SUPERTABLE_NAME));
assertTrue(rs.next());
assertEquals(TENANT_ID, rs.getString(PhoenixDatabaseMetaData.TABLE_CAT));
assertEquals(sortedTableNames.get(1), rs.getString(PhoenixDatabaseMetaData.TABLE_NAME));
assertEquals(sortedParentNames.get(1), rs.getString(PhoenixDatabaseMetaData.SUPERTABLE_NAME));
assertFalse(rs.next());
rs = meta.getSuperTables(tenantId2, null, null);
assertTrue(rs.next());
assertEquals(tenantId2, rs.getString(PhoenixDatabaseMetaData.TABLE_CAT));
assertEquals(tenantTable2, rs.getString(PhoenixDatabaseMetaData.TABLE_NAME));
assertEquals(PARENT_TABLE_NAME, rs.getString(PhoenixDatabaseMetaData.SUPERTABLE_NAME));
assertFalse(rs.next());
conn.close();
Set<String> sortedCatalogs = new HashSet<>(Arrays.asList(TENANT_ID, tenantId2));
rs = conn.getMetaData().getCatalogs();
while (rs.next()) {
sortedCatalogs.remove(rs.getString(PhoenixDatabaseMetaData.TABLE_CAT));
}
assertTrue("Should have found both tenant IDs", sortedCatalogs.isEmpty());
} finally {
props.clear();
conn.close();
}
conn = DriverManager.getConnection(PHOENIX_JDBC_TENANT_SPECIFIC_URL, props);
try {
// make sure tenant-specific connections only see their own tables and the global tables
DatabaseMetaData meta = conn.getMetaData();
ResultSet rs = meta.getTables("", SYSTEM_CATALOG_SCHEMA, null, new String[] {PTableType.SYSTEM.getValue().getString()});
assertTrue(rs.next());
assertTableMetaData(rs, PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA, PhoenixDatabaseMetaData.SYSTEM_CATALOG_TABLE, PTableType.SYSTEM);
assertTrue(rs.next());
assertTableMetaData(rs, SYSTEM_CATALOG_SCHEMA, SYSTEM_FUNCTION_TABLE, SYSTEM);
assertTrue(rs.next());
assertTableMetaData(rs, PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA, PhoenixDatabaseMetaData.TYPE_SEQUENCE, PTableType.SYSTEM);
assertTrue(rs.next());
assertTableMetaData(rs, SYSTEM_CATALOG_SCHEMA, PhoenixDatabaseMetaData.SYSTEM_STATS_TABLE, PTableType.SYSTEM);
assertFalse(rs.next());
rs = meta.getTables(null, "", StringUtil.escapeLike(tenantTable2), new String[] {TABLE.getValue().getString()});
assertFalse(rs.next());
rs = meta.getTables(null, "", StringUtil.escapeLike(PARENT_TABLE_NAME), new String[] {TABLE.getValue().getString()});
assertTrue(rs.next());
assertTableMetaData(rs, null, PARENT_TABLE_NAME, TABLE);
assertFalse(rs.next());
rs = meta.getTables(null, "", StringUtil.escapeLike(PARENT_TABLE_NAME_NO_TENANT_TYPE_ID), new String[] {TABLE.getValue().getString()});
assertTrue(rs.next());
assertTableMetaData(rs, null, PARENT_TABLE_NAME_NO_TENANT_TYPE_ID, TABLE);
assertFalse(rs.next());
rs = meta.getTables(null, "", StringUtil.escapeLike(TENANT_TABLE_NAME), new String[] {PTableType.VIEW.getValue().getString()});
assertTrue(rs.next());
assertTableMetaData(rs, null, TENANT_TABLE_NAME, PTableType.VIEW);
assertFalse(rs.next());
rs = meta.getTables(null, "", StringUtil.escapeLike(TENANT_TABLE_NAME_NO_TENANT_TYPE_ID), new String[] {PTableType.VIEW.getValue().getString()});
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, StringUtil.escapeLike(TENANT_TABLE_NAME), null);
assertTrue(rs.next());
assertColumnMetaData(rs, null, TENANT_TABLE_NAME, "\"user\"", 1);
assertTrue(rs.next());
// (tenant_id column is not visible in tenant-specific connection)
assertColumnMetaData(rs, null, TENANT_TABLE_NAME, "tenant_type_id", 2);
assertEquals(1, rs.getInt(KEY_SEQ));
assertTrue(rs.next());
assertColumnMetaData(rs, null, TENANT_TABLE_NAME, "id", 3);
assertTrue(rs.next());
assertColumnMetaData(rs, null, TENANT_TABLE_NAME, "tenant_col", 4);
assertFalse(rs.next());
rs = meta.getColumns(null, null, StringUtil.escapeLike(TENANT_TABLE_NAME_NO_TENANT_TYPE_ID), null);
assertTrue(rs.next());
assertColumnMetaData(rs, null, TENANT_TABLE_NAME_NO_TENANT_TYPE_ID, "\"user\"", 1);
assertTrue(rs.next());
// (tenant_id column is not visible in tenant-specific connection)
assertColumnMetaData(rs, null, TENANT_TABLE_NAME_NO_TENANT_TYPE_ID, "id", 2);
assertTrue(rs.next());
assertColumnMetaData(rs, null, TENANT_TABLE_NAME_NO_TENANT_TYPE_ID, "tenant_col", 3);
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"));
}
private void assertColumnMetaData(ResultSet rs, String schema, String table, String column, int ordinalPosition)
throws SQLException {
assertColumnMetaData(rs, schema, table, column);
assertEquals(ordinalPosition, rs.getInt(ORDINAL_POSITION));
}
}