http://git-wip-us.apache.org/repos/asf/phoenix/blob/9cd7e86b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java index 985d3cc..fe7c424 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java @@ -63,6 +63,7 @@ import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.PK_NAME; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.REF_GENERATION; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.REGION_NAME; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.REMARKS; +import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.STORE_NULLS; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SALT_BUCKETS; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SCOPE_CATALOG; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SCOPE_SCHEMA; @@ -105,10 +106,10 @@ import org.apache.phoenix.util.ByteUtil; /** - * + * * Constants used during querying * - * + * * @since 0.1 */ public interface QueryConstants { @@ -119,7 +120,7 @@ public interface QueryConstants { public static final String NULL_SCHEMA_NAME = ""; public static final String NULL_DISPLAY_TEXT = "<null>"; public static final long UNSET_TIMESTAMP = -1; - + public enum JoinType {INNER, LEFT_OUTER} public final static String SYSTEM_SCHEMA_NAME = "SYSTEM"; public final static byte[] SYSTEM_SCHEMA_NAME_BYTES = Bytes.toBytes(SYSTEM_SCHEMA_NAME); @@ -137,7 +138,7 @@ public interface QueryConstants { public final static byte[] UNGROUPED_AGG_ROW_KEY = Bytes.toBytes("a"); public final static PName AGG_COLUMN_NAME = SINGLE_COLUMN_NAME; public final static PName AGG_COLUMN_FAMILY_NAME = SINGLE_COLUMN_FAMILY_NAME; - + public static final byte[] ARRAY_VALUE_COLUMN_FAMILY = Bytes.toBytes("a"); // Use empty byte array for column qualifier so as not to accidentally conflict with any other columns public static final byte[] ARRAY_VALUE_COLUMN_QUALIFIER = ByteUtil.EMPTY_BYTE_ARRAY; @@ -150,7 +151,7 @@ public interface QueryConstants { */ public static final byte SEPARATOR_BYTE = (byte) 0; public static final byte[] SEPARATOR_BYTE_ARRAY = new byte[] {SEPARATOR_BYTE}; - + public static final String DEFAULT_COPROCESS_PATH = "phoenix.jar"; public final static int MILLIS_IN_DAY = 1000 * 60 * 60 * 24; @@ -163,10 +164,10 @@ public interface QueryConstants { public static final byte[] DEFAULT_COLUMN_FAMILY_BYTES = Bytes.toBytes(DEFAULT_COLUMN_FAMILY); public static final ImmutableBytesPtr DEFAULT_COLUMN_FAMILY_BYTES_PTR = new ImmutableBytesPtr( DEFAULT_COLUMN_FAMILY_BYTES); - + public static final String ALL_FAMILY_PROPERTIES_KEY = ""; public static final String SYSTEM_TABLE_PK_NAME = "pk"; - + public static final double MILLIS_TO_NANOS_CONVERTOR = Math.pow(10, 6); public static final BigDecimal BD_MILLIS_NANOS_CONVERSION = BigDecimal.valueOf(MILLIS_TO_NANOS_CONVERTOR); public static final BigDecimal BD_MILLIS_IN_DAY = BigDecimal.valueOf(QueryConstants.MILLIS_IN_DAY); @@ -211,7 +212,7 @@ public interface QueryConstants { // Unused TYPE_NAME + " VARCHAR," + REMARKS + " VARCHAR," + - SELF_REFERENCING_COL_NAME + " VARCHAR," + + SELF_REFERENCING_COL_NAME + " VARCHAR," + REF_GENERATION + " VARCHAR," + BUFFER_LENGTH + " INTEGER," + NUM_PREC_RADIX + " INTEGER," + @@ -227,14 +228,15 @@ public interface QueryConstants { IS_AUTOINCREMENT + " VARCHAR," + INDEX_TYPE + " UNSIGNED_TINYINT," + INDEX_DISABLE_TIMESTAMP + " BIGINT," + + STORE_NULLS + " BOOLEAN," + "CONSTRAINT " + SYSTEM_TABLE_PK_NAME + " PRIMARY KEY (" + TENANT_ID + "," + TABLE_SCHEM + "," + TABLE_NAME + "," + COLUMN_NAME + "," + COLUMN_FAMILY + "))\n" + HConstants.VERSIONS + "=" + MetaDataProtocol.DEFAULT_MAX_META_DATA_VERSIONS + ",\n" + HColumnDescriptor.KEEP_DELETED_CELLS + "=" + MetaDataProtocol.DEFAULT_META_DATA_KEEP_DELETED_CELLS + ",\n" + // Install split policy to prevent a tenant's metadata from being split across regions. HTableDescriptor.SPLIT_POLICY + "='" + MetaDataSplitPolicy.class.getName() + "'\n"; - - public static final String CREATE_STATS_TABLE_METADATA = + + public static final String CREATE_STATS_TABLE_METADATA = "CREATE TABLE " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_STATS_TABLE + "\"(\n" + // PK columns PHYSICAL_NAME + " VARCHAR NOT NULL," + @@ -243,7 +245,7 @@ public interface QueryConstants { GUIDE_POSTS_COUNT + " BIGINT," + GUIDE_POSTS + " VARBINARY," + GUIDE_POSTS_WIDTH + " BIGINT," + - MIN_KEY + " VARBINARY," + + MIN_KEY + " VARBINARY," + MAX_KEY + " VARBINARY," + LAST_STATS_UPDATE_TIME+ " DATE, "+ GUIDE_POSTS_ROW_COUNT+ " BIGINT, "+ @@ -254,22 +256,22 @@ public interface QueryConstants { HColumnDescriptor.KEEP_DELETED_CELLS + "=" + MetaDataProtocol.DEFAULT_META_DATA_KEEP_DELETED_CELLS + ",\n" + // Install split policy to prevent a physical table's stats from being split across regions. HTableDescriptor.SPLIT_POLICY + "='" + MetaDataSplitPolicy.class.getName() + "'\n"; - + public static final String CREATE_SEQUENCE_METADATA = - "CREATE TABLE " + SYSTEM_CATALOG_SCHEMA + ".\"" + TYPE_SEQUENCE + "\"(\n" + + "CREATE TABLE " + SYSTEM_CATALOG_SCHEMA + ".\"" + TYPE_SEQUENCE + "\"(\n" + TENANT_ID + " VARCHAR NULL," + - SEQUENCE_SCHEMA + " VARCHAR NULL, \n" + + SEQUENCE_SCHEMA + " VARCHAR NULL, \n" + SEQUENCE_NAME + " VARCHAR NOT NULL, \n" + - START_WITH + " BIGINT, \n" + - CURRENT_VALUE + " BIGINT, \n" + - INCREMENT_BY + " BIGINT, \n" + - CACHE_SIZE + " BIGINT, \n" + + START_WITH + " BIGINT, \n" + + CURRENT_VALUE + " BIGINT, \n" + + INCREMENT_BY + " BIGINT, \n" + + CACHE_SIZE + " BIGINT, \n" + // the following three columns were added in 3.1/4.1 - MIN_VALUE + " BIGINT, \n" + - MAX_VALUE + " BIGINT, \n" + - CYCLE_FLAG + " BOOLEAN, \n" + - LIMIT_REACHED_FLAG + " BOOLEAN \n" + - " CONSTRAINT " + SYSTEM_TABLE_PK_NAME + " PRIMARY KEY (" + TENANT_ID + "," + SEQUENCE_SCHEMA + "," + SEQUENCE_NAME + "))\n" + + MIN_VALUE + " BIGINT, \n" + + MAX_VALUE + " BIGINT, \n" + + CYCLE_FLAG + " BOOLEAN, \n" + + LIMIT_REACHED_FLAG + " BOOLEAN \n" + + " CONSTRAINT " + SYSTEM_TABLE_PK_NAME + " PRIMARY KEY (" + TENANT_ID + "," + SEQUENCE_SCHEMA + "," + SEQUENCE_NAME + "))\n" + HConstants.VERSIONS + "=" + MetaDataProtocol.DEFAULT_MAX_META_DATA_VERSIONS + ",\n" + HColumnDescriptor.KEEP_DELETED_CELLS + "=" + MetaDataProtocol.DEFAULT_META_DATA_KEEP_DELETED_CELLS + "\n"; }
http://git-wip-us.apache.org/repos/asf/phoenix/blob/9cd7e86b/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java index b7ab899..38aac31 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java @@ -186,6 +186,11 @@ public class DelegateTable implements PTable { } @Override + public boolean getStoreNulls() { + return delegate.getStoreNulls(); + } + + @Override public ViewType getViewType() { return delegate.getViewType(); } @@ -221,7 +226,7 @@ public class DelegateTable implements PTable { } private final PTable delegate; - + public DelegateTable(PTable delegate) { this.delegate = delegate; } http://git-wip-us.apache.org/repos/asf/phoenix/blob/9cd7e86b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java index 8a3a2b2..71d5827 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java @@ -47,6 +47,7 @@ import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.PARENT_TENANT_ID; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.PHYSICAL_NAME; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.PK_NAME; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.REGION_NAME; +import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.STORE_NULLS; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SALT_BUCKETS; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SORT_ORDER; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA; @@ -163,7 +164,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Objects; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterators; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; @@ -193,8 +193,9 @@ public class MetaDataClient { MULTI_TENANT + "," + VIEW_TYPE + "," + VIEW_INDEX_ID + "," + - INDEX_TYPE + - ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + INDEX_TYPE + "," + + STORE_NULLS + + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; private static final String CREATE_LINK = "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE + "\"( " + TENANT_ID + "," + @@ -213,14 +214,14 @@ public class MetaDataClient { PARENT_TENANT_ID + " " + PVarchar.INSTANCE.getSqlTypeName() + // Dynamic column for now to prevent schema change ") VALUES (?, ?, ?, ?, ?, ?)"; private static final String INCREMENT_SEQ_NUM = - "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE + "\"( " + + "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE + "\"( " + TENANT_ID + "," + TABLE_SCHEM + "," + TABLE_NAME + "," + TABLE_SEQ_NUM + ") VALUES (?, ?, ?, ?)"; private static final String MUTATE_TABLE = - "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE + "\"( " + + "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE + "\"( " + TENANT_ID + "," + TABLE_SCHEM + "," + TABLE_NAME + "," + @@ -228,36 +229,15 @@ public class MetaDataClient { TABLE_SEQ_NUM + "," + COLUMN_COUNT + ") VALUES (?, ?, ?, ?, ?, ?)"; - private static final String MUTATE_MULTI_TENANT = - "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE + "\"( " + - TENANT_ID + "," + - TABLE_SCHEM + "," + - TABLE_NAME + "," + - MULTI_TENANT + - ") VALUES (?, ?, ?, ?)"; - private static final String MUTATE_DISABLE_WAL = - "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE + "\"( " + - TENANT_ID + "," + - TABLE_SCHEM + "," + - TABLE_NAME + "," + - DISABLE_WAL + - ") VALUES (?, ?, ?, ?)"; - private static final String MUTATE_IMMUTABLE_ROWS = - "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE + "\"( " + - TENANT_ID + "," + - TABLE_SCHEM + "," + - TABLE_NAME + "," + - IMMUTABLE_ROWS + - ") VALUES (?, ?, ?, ?)"; private static final String UPDATE_INDEX_STATE = - "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE + "\"( " + + "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE + "\"( " + TENANT_ID + "," + TABLE_SCHEM + "," + TABLE_NAME + "," + - INDEX_STATE + + INDEX_STATE + ") VALUES (?, ?, ?, ?)"; private static final String UPDATE_INDEX_STATE_TO_ACTIVE = - "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE + "\"( " + + "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE + "\"( " + TENANT_ID + "," + TABLE_SCHEM + "," + TABLE_NAME + "," + @@ -265,7 +245,7 @@ public class MetaDataClient { INDEX_DISABLE_TIMESTAMP + ") VALUES (?, ?, ?, ?, ?)"; private static final String INSERT_COLUMN = - "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE + "\"( " + + "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE + "\"( " + TENANT_ID + "," + TABLE_SCHEM + "," + TABLE_NAME + "," + @@ -275,17 +255,17 @@ public class MetaDataClient { NULLABLE + "," + COLUMN_SIZE + "," + DECIMAL_DIGITS + "," + - ORDINAL_POSITION + "," + + ORDINAL_POSITION + "," + SORT_ORDER + "," + DATA_TABLE_NAME + "," + // write this both in the column and table rows for access by metadata APIs ARRAY_SIZE + "," + - VIEW_CONSTANT + "," + - IS_VIEW_REFERENCED + "," + + VIEW_CONSTANT + "," + + IS_VIEW_REFERENCED + "," + PK_NAME + "," + // write this both in the column and table rows for access by metadata APIs KEY_SEQ + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; private static final String UPDATE_COLUMN_POSITION = - "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE + "\" ( " + + "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE + "\" ( " + TENANT_ID + "," + TABLE_SCHEM + "," + TABLE_NAME + "," + @@ -293,16 +273,16 @@ public class MetaDataClient { COLUMN_FAMILY + "," + ORDINAL_POSITION + ") VALUES (?, ?, ?, ?, ?, ?)"; - + private final PhoenixConnection connection; - + private static final HColumnDescriptor defaultColDescriptor = new HColumnDescriptor(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES); - + public MetaDataClient(PhoenixConnection connection) { this.connection = connection; } - + public PhoenixConnection getConnection() { return connection; } @@ -311,7 +291,7 @@ public class MetaDataClient { MetaDataMutationResult result = updateCache(schemaName, tableName, true); return result.getMutationTime(); } - + /** * Update the cache with the latest as of the connection scn. * @param schemaName @@ -322,11 +302,11 @@ public class MetaDataClient { public MetaDataMutationResult updateCache(String schemaName, String tableName) throws SQLException { return updateCache(schemaName, tableName, false); } - + private MetaDataMutationResult updateCache(String schemaName, String tableName, boolean alwaysHitServer) throws SQLException { - return updateCache(connection.getTenantId(), schemaName, tableName, alwaysHitServer); + return updateCache(connection.getTenantId(), schemaName, tableName, alwaysHitServer); } - + public MetaDataMutationResult updateCache(PName tenantId, String schemaName, String tableName) throws SQLException { return updateCache(tenantId, schemaName, tableName, false); } @@ -336,7 +316,7 @@ public class MetaDataClient { long clientTimeStamp = scn == null ? HConstants.LATEST_TIMESTAMP : scn; return clientTimeStamp; } - + private MetaDataMutationResult updateCache(PName tenantId, String schemaName, String tableName, boolean alwaysHitServer) throws SQLException { // TODO: pass byte[] herez long clientTimeStamp = getClientTimeStamp(); @@ -357,16 +337,16 @@ public class MetaDataClient { if (table != null && !alwaysHitServer && (systemTable || tableTimestamp == clientTimeStamp - 1)) { return new MetaDataMutationResult(MutationCode.TABLE_ALREADY_EXISTS,QueryConstants.UNSET_TIMESTAMP,table); } - + int maxTryCount = tenantId == null ? 1 : 2; int tryCount = 0; MetaDataMutationResult result; - + do { final byte[] schemaBytes = PVarchar.INSTANCE.toBytes(schemaName); final byte[] tableBytes = PVarchar.INSTANCE.toBytes(tableName); result = connection.getQueryServices().getTable(tenantId, schemaBytes, tableBytes, tableTimestamp, clientTimeStamp); - + if (SYSTEM_CATALOG_SCHEMA.equals(schemaName)) { return result; } @@ -406,10 +386,10 @@ public class MetaDataClient { } tenantId = null; // Try again with global tenantId } while (++tryCount < maxTryCount); - + return result; } - + /** * Fault in the physical table to the cache and add any indexes it has to the indexes * of the table for which we just updated. @@ -542,7 +522,7 @@ public class MetaDataClient { sortOrder = pkSortOrder.getSecond(); } } - + String columnName = columnDefName.getColumnName(); PName familyName = null; if (def.isPK() && !pkConstraint.getColumnNames().isEmpty() ) { @@ -563,7 +543,7 @@ public class MetaDataClient { } else if (!isPK) { familyName = PNameFactory.newName(defaultColumnFamily == null ? QueryConstants.DEFAULT_COLUMN_FAMILY : defaultColumnFamily); } - + if (isPK && !addingToPK && pkConstraint.getColumnNames().size() <= 1) { if (def.isNull() && def.isNullSet()) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.SINGLE_PK_MAY_NOT_BE_NULL) @@ -571,7 +551,7 @@ public class MetaDataClient { } isNull = false; } - + PColumn column = new PColumnImpl(PNameFactory.newName(columnName), familyName, def.getDataType(), def.getMaxLength(), def.getScale(), isNull, position, sortOrder, def.getArraySize(), null, false); return column; @@ -634,7 +614,7 @@ public class MetaDataClient { MetaDataUtil.getLocalIndexSchemaName(table.getSchemaName().getString()), MetaDataUtil.getLocalIndexTableName(table.getTableName().getString())))); } - + for (final PName name : names) { PTable indexLogicalTable = new DelegateTable(table) { @Override @@ -657,7 +637,7 @@ public class MetaDataClient { ReadOnlyProps props = connection.getQueryServices().getProps(); final long msMinBetweenUpdates = props .getLong(QueryServices.MIN_STATS_UPDATE_FREQ_MS_ATTRIB, - props.getLong(QueryServices.STATS_UPDATE_FREQ_MS_ATTRIB, + props.getLong(QueryServices.STATS_UPDATE_FREQ_MS_ATTRIB, QueryServicesOptions.DEFAULT_STATS_UPDATE_FREQ_MS) / 2); byte[] tenantIdBytes = ByteUtil.EMPTY_BYTE_ARRAY; Long scn = connection.getSCN(); @@ -686,7 +666,7 @@ public class MetaDataClient { MutationState mutationState = plan.execute(); rowCount = mutationState.getUpdateCount(); } - + /* * Update the stats table so that client will pull the new one with the updated stats. * Even if we don't run the command due to the last update time, invalidate the cache. @@ -706,7 +686,7 @@ public class MetaDataClient { props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(connection.getSCN()+1)); PhoenixConnection conn = DriverManager.getConnection(connection.getURL(), props).unwrap(PhoenixConnection.class); MetaDataClient newClientAtNextTimeStamp = new MetaDataClient(conn); - + // Re-resolve the tableRef from the now newer connection conn.setAutoCommit(true); ColumnResolver resolver = FromCompiler.getResolver(dataTableNode, conn); @@ -739,7 +719,7 @@ public class MetaDataClient { } throw new IllegalStateException(); // impossible } - + private MutationState buildIndex(PTable index, TableRef dataTableRef) throws SQLException { AlterIndexStatement indexStatement = null; boolean wasAutoCommit = connection.getAutoCommit(); @@ -747,7 +727,7 @@ public class MetaDataClient { try { connection.setAutoCommit(true); MutationPlan mutationPlan; - + // For local indexes, we optimize the initial index population by *not* sending Puts over // the wire for the index rows, as we don't need to do that. Instead, we tap into our // region observer to generate the index rows based on the data rows as we scan @@ -798,7 +778,7 @@ public class MetaDataClient { for (ColumnReference columnRef : indexMaintainer.getAllColumns()) { scan.addColumn(columnRef.getFamily(), columnRef.getQualifier()); } - + // Go through MutationPlan abstraction so that we can create local indexes // with a connectionless connection (which makes testing easier). mutationPlan = new MutationPlan() { @@ -834,7 +814,7 @@ public class MetaDataClient { // rows that were added. return new MutationState(0, connection, rowCount); } - + }; } else { PostIndexDDLCompiler compiler = new PostIndexDDLCompiler(connection, dataTableRef); @@ -844,13 +824,13 @@ public class MetaDataClient { } catch (IOException e) { throw new SQLException(e); } - } + } MutationState state = connection.getQueryServices().updateData(mutationPlan); - indexStatement = FACTORY.alterIndex(FACTORY.namedTable(null, + indexStatement = FACTORY.alterIndex(FACTORY.namedTable(null, TableName.create(index.getSchemaName().getString(), index.getTableName().getString())), dataTableRef.getTable().getTableName().getString(), false, PIndexState.ACTIVE); alterIndex(indexStatement); - + return state; } finally { connection.setAutoCommit(wasAutoCommit); @@ -914,7 +894,7 @@ public class MetaDataClient { public MutationState createIndex(CreateIndexStatement statement, byte[][] splits) throws SQLException { PrimaryKeyConstraint pk = statement.getIndexConstraint(); TableName indexTableName = statement.getIndexTableName(); - + List<Pair<ColumnName, SortOrder>> indexedPkColumns = pk.getColumnNames(); List<ColumnName> includedColumns = statement.getIncludeColumns(); TableRef tableRef = null; @@ -952,7 +932,7 @@ public class MetaDataClient { } List<Pair<ColumnName, SortOrder>> allPkColumns = Lists.newArrayListWithExpectedSize(unusedPkColumns.size()); List<ColumnDef> columnDefs = Lists.newArrayListWithExpectedSize(includedColumns.size() + indexedPkColumns.size()); - + if (dataTable.isMultiTenant()) { // Add tenant ID column as first column in index PColumn col = dataTable.getPKColumns().get(posOffset); @@ -988,7 +968,7 @@ public class MetaDataClient { columnDefs.add(FACTORY.columnDef(colName, dataType.getSqlTypeName(), col.isNullable(), col.getMaxLength(), col.getScale(), false, SortOrder.getDefault())); } } - + // Next all the PK columns from the data table that aren't indexed if (!unusedPkColumns.isEmpty()) { for (PColumn col : unusedPkColumns) { @@ -1003,7 +983,7 @@ public class MetaDataClient { } } pk = FACTORY.primaryKey(null, allPkColumns); - + // Last all the included columns (minus any PK columns) for (ColumnName colName : includedColumns) { PColumn col = resolver.resolveColumn(null, colName.getFamilyName(), colName.getColumnName()).getColumn(); @@ -1024,7 +1004,7 @@ public class MetaDataClient { } } } - + // Don't re-allocate indexId on ConcurrentTableMutationException, // as there's no need to burn another sequence value. if (allocateIndexId && indexId == null) { @@ -1072,7 +1052,7 @@ public class MetaDataClient { if (table == null) { return new MutationState(0,connection); } - + // If our connection is at a fixed point-in-time, we need to open a new // connection so that our new index table is visible. if (connection.getSCN() != null) { @@ -1097,7 +1077,7 @@ public class MetaDataClient { } return new MutationState(1, connection); } - + public MutationState createSequence(CreateSequenceStatement statement, long startWith, long incrementBy, long cacheSize, long minValue, long maxValue) throws SQLException { Long scn = connection.getSCN(); @@ -1132,7 +1112,7 @@ public class MetaDataClient { } return null; } - + private PTable createTableInternal(CreateTableStatement statement, byte[][] splits, final PTable parent, String viewStatement, ViewType viewType, final byte[][] viewColumnConstants, final BitSet isViewColumnReferenced, Short indexId, IndexType indexType) throws SQLException { final PTableType tableType = statement.getTableType(); boolean wasAutoCommit = connection.getAutoCommit(); @@ -1140,7 +1120,7 @@ public class MetaDataClient { try { connection.setAutoCommit(false); List<Mutation> tableMetaData = Lists.newArrayListWithExpectedSize(statement.getColumnDefs().size() + 3); - + TableName tableNameNode = statement.getTableName(); String schemaName = tableNameNode.getSchemaName(); String tableName = tableNameNode.getTableName(); @@ -1148,6 +1128,7 @@ public class MetaDataClient { PName tenantId = connection.getTenantId(); String tenantIdStr = tenantId == null ? null : connection.getTenantId().getString(); boolean multiTenant = false; + boolean storeNulls = false; Integer saltBucketNum = null; String defaultFamilyName = null; boolean isImmutableRows = false; @@ -1172,8 +1153,9 @@ public class MetaDataClient { physicalNames = Collections.singletonList(PNameFactory.newName(MetaDataUtil.getViewIndexPhysicalName(physicalName.getBytes()))); } } - + multiTenant = parent.isMultiTenant(); + storeNulls = parent.getStoreNulls(); parentTableName = parent.getTableName().getString(); // Pass through data table sequence number so we can check it hasn't changed PreparedStatement incrementStatement = connection.prepareStatement(INCREMENT_SEQ_NUM); @@ -1196,7 +1178,7 @@ public class MetaDataClient { linkStatement.setByte(5, LinkType.INDEX_TABLE.getSerializedValue()); linkStatement.execute(); } - + PrimaryKeyConstraint pkConstraint = statement.getPrimaryKeyConstraint(); String pkName = null; List<Pair<ColumnName,SortOrder>> pkColumnsNames = Collections.<Pair<ColumnName,SortOrder>>emptyList(); @@ -1206,14 +1188,14 @@ public class MetaDataClient { pkColumnsIterator = pkColumnsNames.iterator(); pkName = pkConstraint.getName(); } - + Map<String,Object> tableProps = Maps.newHashMapWithExpectedSize(statement.getProps().size()); Map<String,Object> commonFamilyProps = Collections.emptyMap(); // Somewhat hacky way of determining if property is for HColumnDescriptor or HTableDescriptor HColumnDescriptor defaultDescriptor = new HColumnDescriptor(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES); if (!statement.getProps().isEmpty()) { commonFamilyProps = Maps.newHashMapWithExpectedSize(statement.getProps().size()); - + Collection<Pair<String,Object>> props = statement.getProps().get(QueryConstants.ALL_FAMILY_PROPERTIES_KEY); for (Pair<String,Object> prop : props) { if (defaultDescriptor.getValue(prop.getFirst()) == null) { @@ -1223,7 +1205,7 @@ public class MetaDataClient { } } } - + // Although unusual, it's possible to set a mapped VIEW as having immutable rows. // This tells Phoenix that you're managing the index maintenance yourself. if (tableType != PTableType.INDEX && (tableType != PTableType.VIEW || viewType == ViewType.MAPPED)) { @@ -1234,7 +1216,7 @@ public class MetaDataClient { isImmutableRows = isImmutableRowsProp; } } - + // Can't set any of these on views or shared indexes on views if (tableType != PTableType.VIEW && indexId == null) { saltBucketNum = (Integer) tableProps.remove(PhoenixDatabaseMetaData.SALT_BUCKETS); @@ -1253,13 +1235,13 @@ public class MetaDataClient { } addSaltColumn = (saltBucketNum != null); } - + boolean removedProp = false; // Can't set MULTI_TENANT or DEFAULT_COLUMN_FAMILY_NAME on an INDEX or a non mapped VIEW if (tableType != PTableType.INDEX && (tableType != PTableType.VIEW || viewType == ViewType.MAPPED)) { Boolean multiTenantProp = (Boolean) tableProps.remove(PhoenixDatabaseMetaData.MULTI_TENANT); multiTenant = Boolean.TRUE.equals(multiTenantProp); - defaultFamilyName = (String)tableProps.remove(PhoenixDatabaseMetaData.DEFAULT_COLUMN_FAMILY_NAME); + defaultFamilyName = (String)tableProps.remove(PhoenixDatabaseMetaData.DEFAULT_COLUMN_FAMILY_NAME); removedProp = (defaultFamilyName != null); } @@ -1268,29 +1250,34 @@ public class MetaDataClient { if (disableWALProp != null) { disableWAL = disableWALProp; } + + Boolean storeNullsProp = (Boolean) tableProps.remove(PhoenixDatabaseMetaData + .STORE_NULLS); + storeNulls = storeNullsProp == null ? false : storeNullsProp; + // Delay this check as it is supported to have IMMUTABLE_ROWS and SALT_BUCKETS defined on views if ((statement.getTableType() == PTableType.VIEW || indexId != null) && !tableProps.isEmpty()) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.VIEW_WITH_PROPERTIES).build() .buildException(); } if (removedProp) { - tableProps.put(PhoenixDatabaseMetaData.DEFAULT_COLUMN_FAMILY_NAME, defaultFamilyName); + tableProps.put(PhoenixDatabaseMetaData.DEFAULT_COLUMN_FAMILY_NAME, defaultFamilyName); } - + List<ColumnDef> colDefs = statement.getColumnDefs(); List<PColumn> columns; - LinkedHashSet<PColumn> pkColumns; - + LinkedHashSet<PColumn> pkColumns; + if (tenantId != null && (tableType != PTableType.VIEW && indexId == null)) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_CREATE_TENANT_SPECIFIC_TABLE) .setSchemaName(schemaName).setTableName(tableName).build().buildException(); } - + if (tableType == PTableType.VIEW) { physicalNames = Collections.singletonList(PNameFactory.newName(parent.getPhysicalName().getString())); if (viewType == ViewType.MAPPED) { columns = newArrayListWithExpectedSize(colDefs.size()); - pkColumns = newLinkedHashSetWithExpectedSize(colDefs.size()); + pkColumns = newLinkedHashSetWithExpectedSize(colDefs.size()); } else { // Propagate property values to VIEW. // TODO: formalize the known set of these properties @@ -1306,7 +1293,7 @@ public class MetaDataClient { columns = newArrayListWithExpectedSize(allColumns.size() + colDefs.size()); columns.addAll(allColumns); pkColumns = newLinkedHashSet(parent.getPKColumns()); - + // Add row linking from view to its parent table // FIXME: not currently used, but see PHOENIX-1367 // as fixing that will require it's usage. @@ -1321,7 +1308,7 @@ public class MetaDataClient { } } else { columns = newArrayListWithExpectedSize(colDefs.size()); - pkColumns = newLinkedHashSetWithExpectedSize(colDefs.size() + 1); // in case salted + pkColumns = newLinkedHashSetWithExpectedSize(colDefs.size() + 1); // in case salted } // Don't add link for mapped view, as it just points back to itself and causes the drop to @@ -1343,11 +1330,11 @@ public class MetaDataClient { } } } - + PreparedStatement colUpsert = connection.prepareStatement(INSERT_COLUMN); Map<String, PName> familyNames = Maps.newLinkedHashMap(); boolean isPK = false; - + int positionOffset = columns.size(); if (saltBucketNum != null) { positionOffset++; @@ -1356,7 +1343,7 @@ public class MetaDataClient { } } int position = positionOffset; - + for (ColumnDef colDef : colDefs) { if (colDef.isPK()) { if (isPK) { @@ -1374,7 +1361,7 @@ public class MetaDataClient { .setColumnName(colDef.getColumnDefName().getColumnName()).build().buildException(); } } - + PColumn column = newColumn(position++, colDef, pkConstraint, defaultFamilyName, false); if (SchemaUtil.isPKColumn(column)) { // TODO: remove this constraint? @@ -1444,7 +1431,7 @@ public class MetaDataClient { .setTableName(tableName) .build().buildException(); } - + List<Pair<byte[],Map<String,Object>>> familyPropList = Lists.newArrayListWithExpectedSize(familyNames.size()); if (!statement.getProps().isEmpty()) { for (String familyName : statement.getProps().keySet()) { @@ -1459,7 +1446,7 @@ public class MetaDataClient { } } throwIfInsufficientColumns(schemaName, tableName, pkColumns, saltBucketNum!=null, multiTenant); - + for (PName familyName : familyNames.values()) { Collection<Pair<String,Object>> props = statement.getProps().get(familyName.getString()); if (props.isEmpty()) { @@ -1468,33 +1455,35 @@ public class MetaDataClient { Map<String,Object> combinedFamilyProps = Maps.newHashMapWithExpectedSize(props.size() + commonFamilyProps.size()); combinedFamilyProps.putAll(commonFamilyProps); for (Pair<String,Object> prop : props) { - // Don't allow specifying column families for TTL. TTL can only apply for the all the column families of the table + // Don't allow specifying column families for TTL. TTL can only apply for the all the column families of the table // i.e. it can't be column family specific. if (!familyName.equals(QueryConstants.ALL_FAMILY_PROPERTIES_KEY) && prop.getFirst().equals(TTL)) { - throw new SQLExceptionInfo.Builder(SQLExceptionCode.COLUMN_FAMILY_NOT_ALLOWED_FOR_TTL).build().buildException(); + throw new SQLExceptionInfo.Builder(SQLExceptionCode.COLUMN_FAMILY_NOT_ALLOWED_FOR_TTL).build().buildException(); } combinedFamilyProps.put(prop.getFirst(), prop.getSecond()); } familyPropList.add(new Pair<byte[],Map<String,Object>>(familyName.getBytes(),combinedFamilyProps)); } } - + if (familyNames.isEmpty()) { //if there are no family names, use the default column family name. This also takes care of the case when - //the table ddl has only PK cols present (which means familyNames is empty). + //the table ddl has only PK cols present (which means familyNames is empty). byte[] cf = defaultFamilyName == null ? QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES : Bytes.toBytes(defaultFamilyName); familyPropList.add(new Pair<byte[],Map<String,Object>>(cf, commonFamilyProps)); } - - // Bootstrapping for our SYSTEM.TABLE that creates itself before it exists + + // Bootstrapping for our SYSTEM.TABLE that creates itself before it exists if (SchemaUtil.isMetaTable(schemaName,tableName)) { // TODO: what about stats for system catalog? PName newSchemaName = PNameFactory.newName(schemaName); PTable table = PTableImpl.makePTable(tenantId,newSchemaName, PNameFactory.newName(tableName), tableType, null, MetaDataProtocol.MIN_TABLE_TIMESTAMP, PTable.INITIAL_SEQ_NUM, - PNameFactory.newName(QueryConstants.SYSTEM_TABLE_PK_NAME), null, columns, null, null, + PNameFactory.newName(QueryConstants.SYSTEM_TABLE_PK_NAME), null, columns, null, null, Collections.<PTable>emptyList(), isImmutableRows, - Collections.<PName>emptyList(), defaultFamilyName == null ? null : PNameFactory.newName(defaultFamilyName), null, Boolean.TRUE.equals(disableWAL), false, null, indexId, indexType); + Collections.<PName>emptyList(), defaultFamilyName == null ? null : + PNameFactory.newName(defaultFamilyName), null, + Boolean.TRUE.equals(disableWAL), false, false, null, indexId, indexType); connection.addTable(table); } else if (tableType == PTableType.INDEX && indexId == null) { if (tableProps.get(HTableDescriptor.MAX_FILESIZE) == null) { @@ -1502,7 +1491,7 @@ public class MetaDataClient { int nIndexKeyValueColumns = columns.size() - nIndexRowKeyColumns; int nBaseRowKeyColumns = parent.getPKColumns().size() - (parent.getBucketNum() == null ? 0 : 1); int nBaseKeyValueColumns = parent.getColumns().size() - parent.getPKColumns().size(); - /* + /* * Approximate ratio between index table size and data table size: * More or less equal to the ratio between the number of key value columns in each. We add one to * the key value column count to take into account our empty key value. We add 1/4 for any key @@ -1519,7 +1508,7 @@ public class MetaDataClient { } } } - + short nextKeySeq = 0; for (int i = 0; i < columns.size(); i++) { PColumn column = columns.get(i); @@ -1549,10 +1538,10 @@ public class MetaDataClient { Short keySeq = SchemaUtil.isPKColumn(column) ? ++nextKeySeq : null; addColumnMutation(schemaName, tableName, column, colUpsert, parentTableName, pkName, keySeq, saltBucketNum != null); } - + tableMetaData.addAll(connection.getMutationState().toMutations().next().getSecond()); connection.rollback(); - + String dataTableName = parent == null || tableType == PTableType.VIEW ? null : parent.getTableName().getString(); PIndexState indexState = parent == null || tableType == PTableType.VIEW ? null : PIndexState.BUILDING; PreparedStatement tableUpsert = connection.prepareStatement(CREATE_TABLE); @@ -1590,11 +1579,12 @@ public class MetaDataClient { } else { tableUpsert.setByte(18, indexType.getSerializedValue()); } + tableUpsert.setBoolean(19, storeNulls); tableUpsert.execute(); - + tableMetaData.addAll(connection.getMutationState().toMutations().next().getSecond()); connection.rollback(); - + /* * The table metadata must be in the following order: * 1) table header row @@ -1602,7 +1592,7 @@ public class MetaDataClient { * 3) parent table header row */ Collections.reverse(tableMetaData); - + if (parent != null && tableType == PTableType.INDEX && indexType == IndexType.LOCAL) { tableProps.put(MetaDataUtil.PARENT_TABLE_KEY, parent.getPhysicalName().getString()); tableProps.put(MetaDataUtil.IS_LOCAL_INDEX_TABLE_PROP_NAME, Boolean.TRUE); @@ -1612,7 +1602,7 @@ public class MetaDataClient { QueryServices.ROW_KEY_ORDER_SALTED_TABLE_ATTRIB, QueryServicesOptions.DEFAULT_ROW_KEY_ORDER_SALTED_TABLE)); } MetaDataMutationResult result = connection.getQueryServices().createTable( - tableMetaData, + tableMetaData, viewType == ViewType.MAPPED || indexId != null ? physicalNames.get(0).getBytes() : null, tableType, tableProps, familyPropList, splits); MutationCode code = result.getMutationCode(); @@ -1638,10 +1628,11 @@ public class MetaDataClient { default: PName newSchemaName = PNameFactory.newName(schemaName); PTable table = PTableImpl.makePTable( - tenantId, newSchemaName, PNameFactory.newName(tableName), tableType, indexState, result.getMutationTime(), - PTable.INITIAL_SEQ_NUM, pkName == null ? null : PNameFactory.newName(pkName), saltBucketNum, columns, + tenantId, newSchemaName, PNameFactory.newName(tableName), tableType, indexState, result.getMutationTime(), + PTable.INITIAL_SEQ_NUM, pkName == null ? null : PNameFactory.newName(pkName), saltBucketNum, columns, dataTableName == null ? null : newSchemaName, dataTableName == null ? null : PNameFactory.newName(dataTableName), Collections.<PTable>emptyList(), isImmutableRows, - physicalNames, defaultFamilyName == null ? null : PNameFactory.newName(defaultFamilyName), viewStatement, Boolean.TRUE.equals(disableWAL), multiTenant, viewType, indexId, indexType); + physicalNames, defaultFamilyName == null ? null : PNameFactory.newName(defaultFamilyName), viewStatement, Boolean.TRUE.equals(disableWAL), multiTenant, storeNulls, viewType, + indexId, indexType); connection.addTable(table); if (tableType == PTableType.VIEW) { // Set wasUpdated to true to force attempt to add @@ -1677,14 +1668,14 @@ public class MetaDataClient { } return false; } - + /** * A table can be a parent table to tenant-specific tables if all of the following conditions are true: * <p> * FOR TENANT-SPECIFIC TABLES WITH TENANT_TYPE_ID SPECIFIED: * <ol> * <li>It has 3 or more PK columns AND - * <li>First PK (tenant id) column is not nullible AND + * <li>First PK (tenant id) column is not nullible AND * <li>Firsts PK column's data type is either VARCHAR or CHAR AND * <li>Second PK (tenant type id) column is not nullible AND * <li>Second PK column data type is either VARCHAR or CHAR @@ -1692,7 +1683,7 @@ public class MetaDataClient { * FOR TENANT-SPECIFIC TABLES WITH NO TENANT_TYPE_ID SPECIFIED: * <ol> * <li>It has 2 or more PK columns AND - * <li>First PK (tenant id) column is not nullible AND + * <li>First PK (tenant id) column is not nullible AND * <li>Firsts PK column's data type is either VARCHAR or CHAR * </ol> */ @@ -1914,10 +1905,12 @@ public class MetaDataClient { } private long incrementTableSeqNum(PTable table, PTableType expectedType, int columnCountDelta) throws SQLException { - return incrementTableSeqNum(table, expectedType, columnCountDelta, null, null, null); + return incrementTableSeqNum(table, expectedType, columnCountDelta, null, null, null, null); } - - private long incrementTableSeqNum(PTable table, PTableType expectedType, int columnCountDelta, Boolean isImmutableRows, Boolean disableWAL, Boolean isMultiTenant) throws SQLException { + + private long incrementTableSeqNum(PTable table, PTableType expectedType, int columnCountDelta, + Boolean isImmutableRows, Boolean disableWAL, Boolean isMultiTenant, Boolean storeNulls) + throws SQLException { String schemaName = table.getSchemaName().getString(); String tableName = table.getTableName().getString(); // Ordinal position is 1-based and we don't count SALT column in ordinal position @@ -1937,32 +1930,36 @@ public class MetaDataClient { tableUpsert.close(); } if (isImmutableRows != null) { - PreparedStatement tableBoolUpsert = connection.prepareStatement(MUTATE_IMMUTABLE_ROWS); - tableBoolUpsert.setString(1, tenantId); - tableBoolUpsert.setString(2, schemaName); - tableBoolUpsert.setString(3, tableName); - tableBoolUpsert.setBoolean(4, isImmutableRows); - tableBoolUpsert.execute(); + mutateBooleanProperty(tenantId, schemaName, tableName, IMMUTABLE_ROWS, isImmutableRows); } if (disableWAL != null) { - PreparedStatement tableBoolUpsert = connection.prepareStatement(MUTATE_DISABLE_WAL); - tableBoolUpsert.setString(1, tenantId); - tableBoolUpsert.setString(2, schemaName); - tableBoolUpsert.setString(3, tableName); - tableBoolUpsert.setBoolean(4, disableWAL); - tableBoolUpsert.execute(); + mutateBooleanProperty(tenantId, schemaName, tableName, DISABLE_WAL, disableWAL); } if (isMultiTenant != null) { - PreparedStatement tableBoolUpsert = connection.prepareStatement(MUTATE_MULTI_TENANT); - tableBoolUpsert.setString(1, tenantId); - tableBoolUpsert.setString(2, schemaName); - tableBoolUpsert.setString(3, tableName); - tableBoolUpsert.setBoolean(4, isMultiTenant); - tableBoolUpsert.execute(); + mutateBooleanProperty(tenantId, schemaName, tableName, MULTI_TENANT, isMultiTenant); + } + if (storeNulls != null) { + mutateBooleanProperty(tenantId, schemaName, tableName, STORE_NULLS, storeNulls); } return seqNum; } - + + private void mutateBooleanProperty(String tenantId, String schemaName, String tableName, + String propertyName, boolean propertyValue) throws SQLException { + String updatePropertySql = "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE + "\"( " + + TENANT_ID + "," + + TABLE_SCHEM + "," + + TABLE_NAME + "," + + propertyName + + ") VALUES (?, ?, ?, ?)"; + PreparedStatement tableBoolUpsert = connection.prepareStatement(updatePropertySql); + tableBoolUpsert.setString(1, tenantId); + tableBoolUpsert.setString(2, schemaName); + tableBoolUpsert.setString(3, tableName); + tableBoolUpsert.setBoolean(4, propertyValue); + tableBoolUpsert.execute(); + } + public MutationState addColumn(AddColumnStatement statement) throws SQLException { connection.rollback(); boolean wasAutoCommit = connection.getAutoCommit(); @@ -1972,11 +1969,12 @@ public class MetaDataClient { TableName tableNameNode = statement.getTable().getName(); String schemaName = tableNameNode.getSchemaName(); String tableName = tableNameNode.getTableName(); - + Boolean isImmutableRowsProp = null; Boolean multiTenantProp = null; Boolean disableWALProp = null; - + Boolean storeNullsProp = null; + // flatten, separate and validate properties ListMultimap<String,Pair<String,Object>> stmtPropsMap = statement.getProps(); Map<String, Map<String, Object>> stmtFamiliesPropsMap = new HashMap<>(stmtPropsMap.size()); @@ -1984,7 +1982,7 @@ public class MetaDataClient { PTable table = FromCompiler.getResolver(statement, connection).getTables().get(0).getTable(); Map<String,Object> commonFamilyProps = new HashMap<>(); List<ColumnDef> columnDefs = statement.getColumnDefs(); - if (columnDefs == null) { + if (columnDefs == null) { columnDefs = Lists.newArrayListWithExpectedSize(1); } for (String family : stmtPropsMap.keySet()) { @@ -2003,7 +2001,7 @@ public class MetaDataClient { if (!family.equals(QueryConstants.ALL_FAMILY_PROPERTIES_KEY)) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.COLUMN_FAMILY_NOT_ALLOWED_TABLE_PROPERTY) .setMessage("Column Family: " + family + ", Property: " + propName).build() - .buildException(); + .buildException(); } tableProps.put(propName, prop.getSecond()); } else { @@ -2019,7 +2017,9 @@ public class MetaDataClient { multiTenantProp = (Boolean)prop.getSecond(); } else if (propName.equals(DISABLE_WAL)) { disableWALProp = (Boolean)prop.getSecond(); - } + } else if (propName.equals(STORE_NULLS)) { + storeNullsProp = (Boolean) prop.getSecond(); + } } else { if (isHColumnProperty(propName)) { if (family.equals(QueryConstants.ALL_FAMILY_PROPERTIES_KEY)) { @@ -2029,7 +2029,7 @@ public class MetaDataClient { } } else { // invalid property - neither of HTableProp, HColumnProp or PhoenixTableProp - // FIXME: This isn't getting triggered as currently a property gets evaluated + // FIXME: This isn't getting triggered as currently a property gets evaluated // as HTableProp if its neither HColumnProp or PhoenixTableProp. throw new SQLExceptionInfo.Builder(SQLExceptionCode.SET_UNSUPPORTED_PROP_ON_ALTER_TABLE) .setMessage("Column Family: " + family + ", Property: " + propName).build() @@ -2042,8 +2042,8 @@ public class MetaDataClient { stmtFamiliesPropsMap.put(family, colFamilyPropsMap); } } - - + + boolean retried = false; while (true) { ColumnResolver resolver = FromCompiler.getResolver(statement, connection); @@ -2086,6 +2086,12 @@ public class MetaDataClient { disableWAL = disableWALProp; } } + Boolean storeNulls = null; + if (storeNullsProp != null) { + if (storeNullsProp.booleanValue() != table.getStoreNulls()) { + storeNulls = storeNullsProp; + } + } boolean isAddingPKColumn = false; PreparedStatement colUpsert = connection.prepareStatement(INSERT_COLUMN); @@ -2104,7 +2110,7 @@ public class MetaDataClient { throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_ADD_NOT_NULLABLE_COLUMN) .setColumnName(colDef.getColumnDefName().getColumnName()).build().buildException(); } - } + } throwIfAlteringViewPK(colDef, table); PColumn column = newColumn(position++, colDef, PrimaryKeyConstraint.EMPTY, table.getDefaultFamilyName() == null ? null : table.getDefaultFamilyName().getString(), true); columns.add(column); @@ -2121,7 +2127,7 @@ public class MetaDataClient { } addColumnMutation(schemaName, tableName, column, colUpsert, null, pkName, keySeq, table.getBucketNum() != null); } - + // Add any new PK columns to end of index PK if (isAddingPKColumn) { for (PTable index : table.getIndexes()) { @@ -2166,13 +2172,14 @@ public class MetaDataClient { tableMetaData.addAll(connection.getMutationState().toMutations().next().getSecond()); connection.rollback(); } - long seqNum = incrementTableSeqNum(table, statement.getTableType(), 1, isImmutableRows, disableWAL, multiTenant); + long seqNum = incrementTableSeqNum(table, statement.getTableType(), 1, + isImmutableRows, disableWAL, multiTenant, storeNulls); tableMetaData.addAll(connection.getMutationState().toMutations().next().getSecond()); connection.rollback(); // Force the table header row to be first Collections.reverse(tableMetaData); - + byte[] family = colFamiliesForPColumnsToBeAdded.size() > 0 ? colFamiliesForPColumnsToBeAdded.iterator().next().getBytes() : null; // Figure out if the empty column family is changing as a result of adding the new column @@ -2190,7 +2197,7 @@ public class MetaDataClient { } } } - + Map<String, Map<String, Object>> allFamiliesProps = new HashMap<>(existingColumnFamilies.size() + stmtFamiliesPropsMap.size()); commonFamilyProps = Collections.unmodifiableMap(commonFamilyProps); if (columnDefs.size() == 0) { @@ -2208,11 +2215,11 @@ public class MetaDataClient { allFamiliesProps.put(colFamily, m); } } - + if (table.getColumnFamilies().isEmpty() && columnDefs.size() == 0 && !commonFamilyProps.isEmpty()) { allFamiliesProps.put(Bytes.toString(table.getDefaultFamilyName() == null ? QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES : table.getDefaultFamilyName().getBytes() ), commonFamilyProps); } - + // Now go through the column family properties specified in the statement // and merge them with the common family properties. for (String f : stmtFamiliesPropsMap.keySet()) { @@ -2227,32 +2234,32 @@ public class MetaDataClient { if (!existingColumnFamilies.contains(f)) { throw new ColumnFamilyNotFoundException(f); } else { - // If the family already exists then an attempt was made to + // If the family already exists then an attempt was made to // add property for a column family that doesn't have a column - // being added in the statement. + // being added in the statement. throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_SET_PROPERTY_FOR_COLUMN_NOT_ADDED).build() .buildException(); } } } } - + // Views are not allowed to have any of these properties. if (table.getType() == PTableType.VIEW && (!stmtFamiliesPropsMap.isEmpty() || !commonFamilyProps.isEmpty() || !tableProps.isEmpty())) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.VIEW_WITH_PROPERTIES).build() .buildException(); } - + HTableDescriptor newTableDesc = null; if (!tableProps.isEmpty()) { - newTableDesc = modifyTableProps(tableName, table, tableProps); + newTableDesc = modifyTableProps(tableName, table, tableProps); } - + if (columnDefs.size() > 0) { - // Make sure that all the CFs of the table have the same TTL as the empty CF. + // Make sure that all the CFs of the table have the same TTL as the empty CF. setTTLToEmptyCFTTL(allFamiliesProps, table, newTableDesc); } - + List<Pair<byte[], Map<String, Object>>> families = new ArrayList<>(allFamiliesProps.size()); for (Entry<String, Map<String, Object>> entry : allFamiliesProps.entrySet()) { byte[] cf = entry.getKey().getBytes(); @@ -2261,7 +2268,7 @@ public class MetaDataClient { families.add(new Pair<>(cf, props)); } } - + MetaDataMutationResult result = connection.getQueryServices().addColumn(tableMetaData, families, table); try { MutationCode code = processMutationResult(schemaName, tableName, result); @@ -2325,8 +2332,8 @@ public class MetaDataClient { connection.setAutoCommit(wasAutoCommit); } } - - private void setTTLToEmptyCFTTL(Map<String, Map<String, Object>> familyProps, PTable table, + + private void setTTLToEmptyCFTTL(Map<String, Map<String, Object>> familyProps, PTable table, HTableDescriptor newTableDesc) throws SQLException { if (!familyProps.isEmpty()) { int emptyCFTTL = getTTLForEmptyCf(SchemaUtil.getEmptyColumnFamily(table), table.getPhysicalName().getBytes(), newTableDesc); @@ -2365,9 +2372,9 @@ public class MetaDataClient { if (sqlE != null) { throw sqlE; } - } + } } - + private String dropColumnMutations(PTable table, List<PColumn> columnsToDrop, List<Mutation> tableMetaData) throws SQLException { String tenantId = connection.getTenantId() == null ? "" : connection.getTenantId().getString(); String schemaName = table.getSchemaName().getString(); @@ -2379,8 +2386,8 @@ public class MetaDataClient { * can factor out the tenant ID, schema name, and table name columns */ StringBuilder buf = new StringBuilder("DELETE FROM " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE + "\" WHERE "); - buf.append("(" + - TENANT_ID + "," + TABLE_SCHEM + "," + TABLE_NAME + "," + + buf.append("(" + + TENANT_ID + "," + TABLE_SCHEM + "," + TABLE_NAME + "," + COLUMN_NAME + ", " + COLUMN_FAMILY + ") IN ("); for(PColumn columnToDrop : columnsToDrop) { buf.append("('" + tenantId + "'"); @@ -2390,16 +2397,16 @@ public class MetaDataClient { buf.append(",'" + (columnToDrop.getFamilyName() == null ? "" : columnToDrop.getFamilyName().getString()) + "'),"); } buf.setCharAt(buf.length()-1, ')'); - + connection.createStatement().execute(buf.toString()); - + Collections.sort(columnsToDrop,new Comparator<PColumn> () { @Override public int compare(PColumn left, PColumn right) { return Ints.compare(left.getPosition(), right.getPosition()); } }); - + boolean isSalted = table.getBucketNum() != null; int columnsToDropIndex = 0; PreparedStatement colUpdate = connection.prepareStatement(UPDATE_COLUMN_POSITION); @@ -2420,7 +2427,7 @@ public class MetaDataClient { } return familyName; } - + /** * Calculate what the new column family will be after the column is dropped, returning null * if unchanged. @@ -2435,7 +2442,7 @@ public class MetaDataClient { // If unchanged, return null return null; } - + public MutationState dropColumn(DropColumnStatement statement) throws SQLException { connection.rollback(); boolean wasAutoCommit = connection.getAutoCommit(); @@ -2459,7 +2466,7 @@ public class MetaDataClient { List<TableRef> indexesToDrop = Lists.newArrayListWithExpectedSize(table.getIndexes().size()); List<Mutation> tableMetaData = Lists.newArrayListWithExpectedSize((table.getIndexes().size() + 1) * (1 + table.getColumns().size() - columnRefs.size())); List<PColumn> tableColumnsToDrop = Lists.newArrayListWithExpectedSize(columnRefs.size()); - + for(ColumnName column : columnRefs) { ColumnRef columnRef = null; try { @@ -2479,7 +2486,7 @@ public class MetaDataClient { } columnsToDrop.add(new ColumnRef(tableRef, columnToDrop.getPosition())); } - + dropColumnMutations(table, tableColumnsToDrop, tableMetaData); for (PTable index : table.getIndexes()) { List<PColumn> indexColumnsToDrop = Lists.newArrayListWithExpectedSize(columnRefs.size()); @@ -2500,17 +2507,17 @@ public class MetaDataClient { incrementTableSeqNum(index, index.getType(), -1); dropColumnMutations(index, indexColumnsToDrop, tableMetaData); } - + } tableMetaData.addAll(connection.getMutationState().toMutations().next().getSecond()); connection.rollback(); - + long seqNum = incrementTableSeqNum(table, statement.getTableType(), -1); tableMetaData.addAll(connection.getMutationState().toMutations().next().getSecond()); connection.rollback(); // Force table header to be first in list Collections.reverse(tableMetaData); - + /* * Ensure our "empty column family to be" exists. Somewhat of an edge case, but can occur if we drop the last column * in a column family that was the empty column family. In that case, we have to pick another one. If there are no other @@ -2583,10 +2590,10 @@ public class MetaDataClient { TableRef droppedColumnTableRef = droppedColumnRef.getTableRef(); PColumn droppedColumn = droppedColumnRef.getColumn(); MutationPlan plan = compiler.compile( - Collections.singletonList(droppedColumnTableRef), - getNewEmptyColumnFamilyOrNull(droppedColumnTableRef.getTable(), droppedColumn), - null, - Collections.singletonList(droppedColumn), + Collections.singletonList(droppedColumnTableRef), + getNewEmptyColumnFamilyOrNull(droppedColumnTableRef.getTable(), droppedColumn), + null, + Collections.singletonList(droppedColumn), ts); state = connection.getQueryServices().updateData(plan); } @@ -2685,7 +2692,7 @@ public class MetaDataClient { connection.setAutoCommit(wasAutoCommit); } } - + private void throwIfAlteringViewPK(ColumnDef col, PTable table) throws SQLException { if (col != null && col.isPK() && table.getType() == PTableType.VIEW) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_MODIFY_VIEW_PK) @@ -2695,7 +2702,7 @@ public class MetaDataClient { .build().buildException(); } } - + public PTableStats getTableStats(PTable table) throws SQLException { /* * The shared view index case is tricky, because we don't have @@ -2729,14 +2736,14 @@ public class MetaDataClient { } return table.getTableStats(); } - + private boolean isHColumnProperty(String propName) { return defaultColDescriptor.getValue(propName) != null; } private boolean isHTableProperty(String propName) { return !isHColumnProperty(propName) && !TableProperty.isPhoenixTableProperty(propName); - } + } private HashSet<String> existingColumnFamilies(PTable table) { List<PColumnFamily> cfs = table.getColumnFamilies(); http://git-wip-us.apache.org/repos/asf/phoenix/blob/9cd7e86b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java index 35e7964..ee4bebc 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java @@ -29,46 +29,46 @@ import org.apache.phoenix.schema.stats.PTableStats; /** * Definition of a Phoenix table * - * + * * @since 0.1 */ public interface PTable { public static final long INITIAL_SEQ_NUM = 0; public static final String IS_IMMUTABLE_ROWS_PROP_NAME = "IMMUTABLE_ROWS"; public static final boolean DEFAULT_DISABLE_WAL = false; - - public enum ViewType { + + public enum ViewType { MAPPED((byte)1), READ_ONLY((byte)2), UPDATABLE((byte)3); private final byte[] byteValue; private final byte serializedValue; - + ViewType(byte serializedValue) { this.serializedValue = serializedValue; this.byteValue = Bytes.toBytes(this.name()); } - + public byte[] getBytes() { return byteValue; } - + public boolean isReadOnly() { return this != UPDATABLE; } - + public byte getSerializedValue() { return this.serializedValue; } - + public static ViewType fromSerializedValue(byte serializedValue) { if (serializedValue < 1 || serializedValue > ViewType.values().length) { throw new IllegalArgumentException("Invalid ViewType " + serializedValue); } return ViewType.values()[serializedValue-1]; } - + public ViewType combine(ViewType otherType) { if (otherType == null) { return this; @@ -80,34 +80,34 @@ public interface PTable { } } - public enum IndexType { + public enum IndexType { GLOBAL((byte)1), LOCAL((byte)2); private final byte[] byteValue; private final byte serializedValue; - + IndexType(byte serializedValue) { this.serializedValue = serializedValue; this.byteValue = Bytes.toBytes(this.name()); } - + public byte[] getBytes() { return byteValue; } - + public byte getSerializedValue() { return this.serializedValue; } - + public static IndexType getDefault() { return GLOBAL; } - + public static IndexType fromToken(String token) { return IndexType.valueOf(token.trim().toUpperCase()); } - + public static IndexType fromSerializedValue(byte serializedValue) { if (serializedValue < 1 || serializedValue > IndexType.values().length) { throw new IllegalArgumentException("Invalid IndexType " + serializedValue); @@ -132,20 +132,20 @@ public interface PTable { private final byte[] byteValue; private final byte serializedValue; - + LinkType(byte serializedValue) { this.serializedValue = serializedValue; this.byteValue = Bytes.toBytes(this.name()); } - + public byte[] getBytes() { return byteValue; } - + public byte getSerializedValue() { return this.serializedValue; } - + public static LinkType fromSerializedValue(byte serializedValue) { if (serializedValue < 1 || serializedValue > LinkType.values().length) { return null; @@ -160,7 +160,7 @@ public interface PTable { * @return table name */ PName getName(); - PName getSchemaName(); + PName getSchemaName(); PName getTableName(); PName getTenantId(); @@ -215,7 +215,7 @@ public interface PTable { * @return the PColumn with the given name * @throws ColumnNotFoundException if no PK column with the given name * can be found - * @throws ColumnNotFoundException + * @throws ColumnNotFoundException */ PColumn getPKColumn(String name) throws ColumnNotFoundException; @@ -295,7 +295,7 @@ public interface PTable { * on or null if not an index. */ PName getParentSchemaName(); - + /** * For a view, return the name of table in Phoenix that physically stores data. * Currently a single name, but when views are allowed over multiple tables, will become multi-valued. @@ -309,15 +309,16 @@ public interface PTable { void getIndexMaintainers(ImmutableBytesWritable ptr); IndexMaintainer getIndexMaintainer(PTable dataTable); PName getDefaultFamilyName(); - + boolean isWALDisabled(); boolean isMultiTenant(); + boolean getStoreNulls(); ViewType getViewType(); String getViewStatement(); Short getViewIndexId(); PTableKey getKey(); - + int getEstimatedSize(); IndexType getIndexType(); PTableStats getTableStats();