You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ja...@apache.org on 2014/01/27 23:15:30 UTC
[14/51] [partial] Initial commit of master branch from github
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java b/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
new file mode 100644
index 0000000..0649daf
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.query;
+
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.hadoop.hbase.HRegionLocation;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.client.HBaseAdmin;
+import org.apache.hadoop.hbase.client.HTableInterface;
+import org.apache.hadoop.hbase.client.Mutation;
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.hadoop.hbase.util.Pair;
+import org.apache.phoenix.client.KeyValueBuilder;
+import org.apache.phoenix.compile.MutationPlan;
+import org.apache.phoenix.coprocessor.MetaDataProtocol.MetaDataMutationResult;
+import org.apache.phoenix.execute.MutationState;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.schema.PColumn;
+import org.apache.phoenix.schema.PMetaData;
+import org.apache.phoenix.schema.PTable;
+import org.apache.phoenix.schema.PTableType;
+import org.apache.phoenix.schema.SequenceKey;
+
+
+public class DelegateConnectionQueryServices extends DelegateQueryServices implements ConnectionQueryServices {
+
+ public DelegateConnectionQueryServices(ConnectionQueryServices delegate) {
+ super(delegate);
+ }
+
+ @Override
+ protected ConnectionQueryServices getDelegate() {
+ return (ConnectionQueryServices)super.getDelegate();
+ }
+
+ @Override
+ public ConnectionQueryServices getChildQueryServices(ImmutableBytesWritable tenantId) {
+ return getDelegate().getChildQueryServices(tenantId);
+ }
+
+ @Override
+ public HTableInterface getTable(byte[] tableName) throws SQLException {
+ return getDelegate().getTable(tableName);
+ }
+
+ @Override
+ public StatsManager getStatsManager() {
+ return getDelegate().getStatsManager();
+ }
+
+ @Override
+ public List<HRegionLocation> getAllTableRegions(byte[] tableName) throws SQLException {
+ return getDelegate().getAllTableRegions(tableName);
+ }
+
+ @Override
+ public PMetaData addTable(PTable table) throws SQLException {
+ return getDelegate().addTable(table);
+ }
+
+ @Override
+ public PMetaData addColumn(String tableName, List<PColumn> columns, long tableTimeStamp, long tableSeqNum,
+ boolean isImmutableRows) throws SQLException {
+ return getDelegate().addColumn(tableName, columns, tableTimeStamp, tableSeqNum, isImmutableRows);
+ }
+
+ @Override
+ public PMetaData removeTable(String tableName)
+ throws SQLException {
+ return getDelegate().removeTable(tableName);
+ }
+
+ @Override
+ public PMetaData removeColumn(String tableName, String familyName, String columnName, long tableTimeStamp,
+ long tableSeqNum) throws SQLException {
+ return getDelegate().removeColumn(tableName, familyName, columnName, tableTimeStamp, tableSeqNum);
+ }
+
+ @Override
+ public PhoenixConnection connect(String url, Properties info) throws SQLException {
+ return getDelegate().connect(url, info);
+ }
+
+ @Override
+ public MetaDataMutationResult getTable(byte[] tenantId, byte[] schemaBytes, byte[] tableBytes, long tableTimestamp, long clientTimestamp) throws SQLException {
+ return getDelegate().getTable(tenantId, schemaBytes, tableBytes, tableTimestamp, clientTimestamp);
+ }
+
+ @Override
+ public MetaDataMutationResult createTable(List<Mutation> tableMetaData, byte[] physicalName,
+ PTableType tableType, Map<String, Object> tableProps, List<Pair<byte[], Map<String, Object>>> families, byte[][] splits)
+ throws SQLException {
+ return getDelegate().createTable(tableMetaData, physicalName, tableType, tableProps, families, splits);
+ }
+
+ @Override
+ public MetaDataMutationResult dropTable(List<Mutation> tabeMetaData, PTableType tableType) throws SQLException {
+ return getDelegate().dropTable(tabeMetaData, tableType);
+ }
+
+ @Override
+ public MetaDataMutationResult addColumn(List<Mutation> tabeMetaData, PTableType tableType, List<Pair<byte[],Map<String,Object>>> families ) throws SQLException {
+ return getDelegate().addColumn(tabeMetaData, tableType, families);
+ }
+
+
+ @Override
+ public MetaDataMutationResult dropColumn(List<Mutation> tabeMetaData, PTableType tableType) throws SQLException {
+ return getDelegate().dropColumn(tabeMetaData, tableType);
+ }
+
+ @Override
+ public MetaDataMutationResult updateIndexState(List<Mutation> tableMetadata, String parentTableName) throws SQLException {
+ return getDelegate().updateIndexState(tableMetadata, parentTableName);
+ }
+
+ @Override
+ public void init(String url, Properties props) throws SQLException {
+ getDelegate().init(url, props);
+ }
+
+ @Override
+ public MutationState updateData(MutationPlan plan) throws SQLException {
+ return getDelegate().updateData(plan);
+ }
+
+ @Override
+ public int getLowestClusterHBaseVersion() {
+ return getDelegate().getLowestClusterHBaseVersion();
+ }
+
+ @Override
+ public HBaseAdmin getAdmin() throws SQLException {
+ return getDelegate().getAdmin();
+ }
+
+ @Override
+ public HTableDescriptor getTableDescriptor(byte[] tableName) throws SQLException {
+ return getDelegate().getTableDescriptor(tableName);
+ }
+
+ @Override
+ public void clearTableRegionCache(byte[] tableName) throws SQLException {
+ getDelegate().clearTableRegionCache(tableName);
+ }
+
+ @Override
+ public boolean hasInvalidIndexConfiguration() {
+ return getDelegate().hasInvalidIndexConfiguration();
+ }
+
+ @Override
+ public long createSequence(String tenantId, String schemaName, String sequenceName, long startWith,
+ long incrementBy, int batchSize, long timestamp) throws SQLException {
+ return getDelegate().createSequence(tenantId, schemaName, sequenceName, startWith, incrementBy, batchSize, timestamp);
+ }
+
+ @Override
+ public long dropSequence(String tenantId, String schemaName, String sequenceName, long timestamp)
+ throws SQLException {
+ return getDelegate().dropSequence(tenantId, schemaName, sequenceName, timestamp);
+ }
+
+ @Override
+ public void reserveSequenceValues(List<SequenceKey> sequenceKeys, long timestamp, long[] values,
+ SQLException[] exceptions) throws SQLException {
+ getDelegate().reserveSequenceValues(sequenceKeys, timestamp, values, exceptions);
+ }
+
+ @Override
+ public void incrementSequenceValues(List<SequenceKey> sequenceKeys, long timestamp, long[] values,
+ SQLException[] exceptions) throws SQLException {
+ getDelegate().incrementSequenceValues(sequenceKeys, timestamp, values, exceptions);
+ }
+
+ @Override
+ public long getSequenceValue(SequenceKey sequenceKey, long timestamp) throws SQLException {
+ return getDelegate().getSequenceValue(sequenceKey, timestamp);
+ }
+
+ @Override
+ public void returnSequenceValues(List<SequenceKey> sequenceKeys, long timestamp, SQLException[] exceptions)
+ throws SQLException {
+ getDelegate().returnSequenceValues(sequenceKeys, timestamp, exceptions);
+ }
+
+ @Override
+ public void addConnection(PhoenixConnection connection) throws SQLException {
+ getDelegate().addConnection(connection);
+ }
+
+ @Override
+ public void removeConnection(PhoenixConnection connection) throws SQLException {
+ getDelegate().removeConnection(connection);
+ }
+
+ @Override
+ public KeyValueBuilder getKeyValueBuilder() {
+ return getDelegate().getKeyValueBuilder();
+ }
+
+ @Override
+ public boolean supportsFeature(Feature feature) {
+ return getDelegate().supportsFeature(feature);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateQueryServices.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateQueryServices.java b/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateQueryServices.java
new file mode 100644
index 0000000..6ddf5c5
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateQueryServices.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.query;
+
+import java.sql.SQLException;
+import java.util.concurrent.ExecutorService;
+
+import org.apache.phoenix.memory.MemoryManager;
+import org.apache.phoenix.optimize.QueryOptimizer;
+import org.apache.phoenix.util.ReadOnlyProps;
+
+
+
+/**
+ *
+ * Class that delegates QueryService calls through to
+ * a parent QueryService.
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public class DelegateQueryServices implements QueryServices {
+ private final QueryServices parent;
+
+ public DelegateQueryServices(QueryServices queryServices) {
+ parent = queryServices;
+ }
+
+ protected QueryServices getDelegate() {
+ return parent;
+ }
+
+ @Override
+ public ExecutorService getExecutor() {
+ return parent.getExecutor();
+ }
+
+ @Override
+ public MemoryManager getMemoryManager() {
+ return parent.getMemoryManager();
+ }
+
+ @Override
+ public void close() throws SQLException {
+ parent.close();
+ }
+
+ @Override
+ public ReadOnlyProps getProps() {
+ return parent.getProps();
+ }
+
+ @Override
+ public QueryOptimizer getOptimizer() {
+ return parent.getOptimizer();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/query/HBaseFactoryProvider.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/HBaseFactoryProvider.java b/phoenix-core/src/main/java/org/apache/phoenix/query/HBaseFactoryProvider.java
new file mode 100644
index 0000000..08e8575
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/HBaseFactoryProvider.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.query;
+
+import org.apache.phoenix.util.InstanceResolver;
+
+/**
+ * Manages factories that provide extension points for HBase.
+ * <p/>
+ * Dependent modules may register their own implementations of the following using {@link java.util.ServiceLoader}:
+ * <ul>
+ * <li>{@link ConfigurationFactory}</li>
+ * <li>{@link HTableFactory}</li>
+ * <li> {@link HConnectionFactory} </li>
+ * </ul>
+ *
+ * If a custom implementation is not registered, the default implementations will be used.
+ *
+ * @author aaraujo
+ * @since 0.2
+ */
+public class HBaseFactoryProvider {
+
+ private static final HTableFactory DEFAULT_HTABLE_FACTORY = new HTableFactory.HTableFactoryImpl();
+ private static final HConnectionFactory DEFAULT_HCONNECTION_FACTORY =
+ new HConnectionFactory.HConnectionFactoryImpl();
+ private static final ConfigurationFactory DEFAULT_CONFIGURATION_FACTORY = new ConfigurationFactory.ConfigurationFactoryImpl();
+
+ public static HTableFactory getHTableFactory() {
+ return InstanceResolver.getSingleton(HTableFactory.class, DEFAULT_HTABLE_FACTORY);
+ }
+
+ public static HConnectionFactory getHConnectionFactory() {
+ return InstanceResolver.getSingleton(HConnectionFactory.class, DEFAULT_HCONNECTION_FACTORY);
+ }
+
+ public static ConfigurationFactory getConfigurationFactory() {
+ return InstanceResolver.getSingleton(ConfigurationFactory.class, DEFAULT_CONFIGURATION_FACTORY);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/query/HConnectionFactory.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/HConnectionFactory.java b/phoenix-core/src/main/java/org/apache/phoenix/query/HConnectionFactory.java
new file mode 100644
index 0000000..d40c540
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/HConnectionFactory.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.query;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.ZooKeeperConnectionException;
+import org.apache.hadoop.hbase.client.HConnection;
+import org.apache.hadoop.hbase.client.HConnectionManager;
+
+/**
+ * Factory for creating {@link HConnection}
+ *
+ * @author ukuchibhotla
+ */
+public interface HConnectionFactory {
+
+ /**
+ * Creates HConnection to access HBase clusters.
+ *
+ * @param configuration object
+ * @return A HConnection instance
+ */
+ HConnection createConnection(Configuration conf) throws ZooKeeperConnectionException;
+
+ /**
+ * Default implementation. Uses standard HBase HConnections.
+ */
+ static class HConnectionFactoryImpl implements HConnectionFactory {
+ @Override
+ public HConnection createConnection(Configuration conf) throws ZooKeeperConnectionException {
+ return HConnectionManager.createConnection(conf);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/query/HTableFactory.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/HTableFactory.java b/phoenix-core/src/main/java/org/apache/phoenix/query/HTableFactory.java
new file mode 100644
index 0000000..cf5b4da
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/HTableFactory.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.query;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+
+import org.apache.hadoop.hbase.client.HConnection;
+import org.apache.hadoop.hbase.client.HTable;
+import org.apache.hadoop.hbase.client.HTableInterface;
+
+/**
+ * Creates clients to access HBase tables.
+ *
+ * @author aaraujo
+ * @since 0.2
+ */
+public interface HTableFactory {
+ /**
+ * Creates an HBase client using an externally managed HConnection and Thread pool.
+ *
+ * @param tableName Name of the table.
+ * @param connection HConnection to use.
+ * @param pool ExecutorService to use.
+ * @return An client to access an HBase table.
+ * @throws IOException if a server or network exception occurs
+ */
+ HTableInterface getTable(byte[] tableName, HConnection connection, ExecutorService pool) throws IOException;
+
+ /**
+ * Default implementation. Uses standard HBase HTables.
+ */
+ static class HTableFactoryImpl implements HTableFactory {
+ @Override
+ public HTableInterface getTable(byte[] tableName, HConnection connection, ExecutorService pool) throws IOException {
+ return new HTable(tableName, connection, pool);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/query/KeyRange.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/KeyRange.java b/phoenix-core/src/main/java/org/apache/phoenix/query/KeyRange.java
new file mode 100644
index 0000000..bc37c57
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/KeyRange.java
@@ -0,0 +1,623 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.query;
+
+import static org.apache.phoenix.query.QueryConstants.SEPARATOR_BYTE_ARRAY;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.io.Writable;
+import org.apache.hadoop.io.WritableUtils;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ComparisonChain;
+import com.google.common.collect.Lists;
+import org.apache.phoenix.schema.ColumnModifier;
+import org.apache.phoenix.util.ByteUtil;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+
+/**
+ *
+ * Class that represents an upper/lower bound key range.
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public class KeyRange implements Writable {
+ public enum Bound { LOWER, UPPER };
+ private static final byte[] DEGENERATE_KEY = new byte[] {1};
+ public static final byte[] UNBOUND = new byte[0];
+ /**
+ * KeyRange for variable length null values. Since we need to represent this using an empty byte array (which
+ * is what we use for upper/lower bound), we create this range using the private constructor rather than
+ * going through the static creation method (where this would not be possible).
+ */
+ public static final KeyRange IS_NULL_RANGE = new KeyRange(ByteUtil.EMPTY_BYTE_ARRAY, true, ByteUtil.EMPTY_BYTE_ARRAY, true);
+ /**
+ * KeyRange for non null variable length values. Since we need to represent this using an empty byte array (which
+ * is what we use for upper/lower bound), we create this range using the private constructor rather than going
+ * through the static creation method (where this would not be possible).
+ */
+ public static final KeyRange IS_NOT_NULL_RANGE = new KeyRange(ByteUtil.nextKey(QueryConstants.SEPARATOR_BYTE_ARRAY), true, UNBOUND, false);
+
+ /**
+ * KeyRange for an empty key range
+ */
+ public static final KeyRange EMPTY_RANGE = new KeyRange(DEGENERATE_KEY, false, DEGENERATE_KEY, false);
+
+ /**
+ * KeyRange that contains all values
+ */
+ public static final KeyRange EVERYTHING_RANGE = new KeyRange(UNBOUND, false, UNBOUND, false);
+
+ public static final Function<byte[], KeyRange> POINT = new Function<byte[], KeyRange>() {
+ @Override
+ public KeyRange apply(byte[] input) {
+ return new KeyRange(input, true, input, true);
+ }
+ };
+ public static final Comparator<KeyRange> COMPARATOR = new Comparator<KeyRange>() {
+ @SuppressWarnings("deprecation")
+ @Override public int compare(KeyRange o1, KeyRange o2) {
+ return ComparisonChain.start()
+// .compareFalseFirst(o1.lowerUnbound(), o2.lowerUnbound())
+ .compare(o2.lowerUnbound(), o1.lowerUnbound())
+ .compare(o1.getLowerRange(), o2.getLowerRange(), Bytes.BYTES_COMPARATOR)
+ // we want o1 lower inclusive to come before o2 lower inclusive, but
+ // false comes before true, so we have to negate
+// .compareTrueFirst(o1.isLowerInclusive(), o2.isLowerInclusive())
+ .compare(o2.isLowerInclusive(), o1.isLowerInclusive())
+ // for the same lower bounding, we want a finite upper bound to
+ // be ordered before an infinite upper bound
+// .compareTrueFirst(o1.upperUnbound(), o2.upperUnbound())
+ .compare(o1.upperUnbound(), o2.upperUnbound())
+ .compare(o1.getUpperRange(), o2.getUpperRange(), Bytes.BYTES_COMPARATOR)
+// .compareFalseFirst(o1.isUpperInclusive(), o2.isUpperInclusive())
+ .compare(o2.isUpperInclusive(), o1.isUpperInclusive())
+ .result();
+ }
+ };
+
+ private byte[] lowerRange;
+ private boolean lowerInclusive;
+ private byte[] upperRange;
+ private boolean upperInclusive;
+ private boolean isSingleKey;
+
+ public static KeyRange getKeyRange(byte[] point) {
+ return getKeyRange(point, true, point, true);
+ }
+
+ public static KeyRange getKeyRange(byte[] lowerRange, byte[] upperRange) {
+ return getKeyRange(lowerRange, true, upperRange, false);
+ }
+
+ // TODO: make non public and move to org.apache.phoenix.type soon
+ public static KeyRange getKeyRange(byte[] lowerRange, boolean lowerInclusive,
+ byte[] upperRange, boolean upperInclusive) {
+ if (lowerRange == null || upperRange == null) {
+ return EMPTY_RANGE;
+ }
+ boolean unboundLower = false;
+ boolean unboundUpper = false;
+ if (lowerRange.length == 0) {
+ lowerRange = UNBOUND;
+ lowerInclusive = false;
+ unboundLower = true;
+ }
+ if (upperRange.length == 0) {
+ upperRange = UNBOUND;
+ upperInclusive = false;
+ unboundUpper = true;
+ }
+
+ if (unboundLower && unboundUpper) {
+ return EVERYTHING_RANGE;
+ }
+ if (!unboundLower && !unboundUpper) {
+ int cmp = Bytes.compareTo(lowerRange, upperRange);
+ if (cmp > 0 || (cmp == 0 && !(lowerInclusive && upperInclusive))) {
+ return EMPTY_RANGE;
+ }
+ }
+ return new KeyRange(lowerRange, unboundLower ? false : lowerInclusive,
+ upperRange, unboundUpper ? false : upperInclusive);
+ }
+
+ public KeyRange() {
+ this.lowerRange = DEGENERATE_KEY;
+ this.lowerInclusive = false;
+ this.upperRange = DEGENERATE_KEY;
+ this.upperInclusive = false;
+ this.isSingleKey = false;
+ }
+
+ private KeyRange(byte[] lowerRange, boolean lowerInclusive, byte[] upperRange, boolean upperInclusive) {
+ this.lowerRange = lowerRange;
+ this.lowerInclusive = lowerInclusive;
+ this.upperRange = upperRange;
+ this.upperInclusive = upperInclusive;
+ init();
+ }
+
+ private void init() {
+ this.isSingleKey = lowerRange != UNBOUND && upperRange != UNBOUND
+ && lowerInclusive && upperInclusive && Bytes.compareTo(lowerRange, upperRange) == 0;
+ }
+
+ public byte[] getRange(Bound bound) {
+ return bound == Bound.LOWER ? getLowerRange() : getUpperRange();
+ }
+
+ public boolean isInclusive(Bound bound) {
+ return bound == Bound.LOWER ? isLowerInclusive() : isUpperInclusive();
+ }
+
+ public boolean isUnbound(Bound bound) {
+ return bound == Bound.LOWER ? lowerUnbound() : upperUnbound();
+ }
+
+ public boolean isSingleKey() {
+ return isSingleKey;
+ }
+
+ public int compareLowerToUpperBound(ImmutableBytesWritable ptr, boolean isInclusive) {
+ return compareLowerToUpperBound(ptr.get(), ptr.getOffset(), ptr.getLength(), isInclusive);
+ }
+
+ public int compareLowerToUpperBound(ImmutableBytesWritable ptr) {
+ return compareLowerToUpperBound(ptr, true);
+ }
+
+ public int compareUpperToLowerBound(ImmutableBytesWritable ptr, boolean isInclusive) {
+ return compareUpperToLowerBound(ptr.get(), ptr.getOffset(), ptr.getLength(), isInclusive);
+ }
+
+ public int compareUpperToLowerBound(ImmutableBytesWritable ptr) {
+ return compareUpperToLowerBound(ptr, true);
+ }
+
+ public int compareLowerToUpperBound( byte[] b, int o, int l) {
+ return compareLowerToUpperBound(b,o,l,true);
+ }
+
+ /**
+ * Compares a lower bound against an upper bound
+ * @param b upper bound byte array
+ * @param o upper bound offset
+ * @param l upper bound length
+ * @param isInclusive upper bound inclusive
+ * @return -1 if the lower bound is less than the upper bound,
+ * 1 if the lower bound is greater than the upper bound,
+ * and 0 if they are equal.
+ */
+ public int compareLowerToUpperBound( byte[] b, int o, int l, boolean isInclusive) {
+ if (lowerUnbound() || b == KeyRange.UNBOUND) {
+ return -1;
+ }
+ int cmp = Bytes.compareTo(lowerRange, 0, lowerRange.length, b, o, l);
+ if (cmp > 0) {
+ return 1;
+ }
+ if (cmp < 0) {
+ return -1;
+ }
+ if (lowerInclusive && isInclusive) {
+ return 0;
+ }
+ return 1;
+ }
+
+ public int compareUpperToLowerBound(byte[] b, int o, int l) {
+ return compareUpperToLowerBound(b,o,l, true);
+ }
+
+ public int compareUpperToLowerBound(byte[] b, int o, int l, boolean isInclusive) {
+ if (upperUnbound() || b == KeyRange.UNBOUND) {
+ return 1;
+ }
+ int cmp = Bytes.compareTo(upperRange, 0, upperRange.length, b, o, l);
+ if (cmp > 0) {
+ return 1;
+ }
+ if (cmp < 0) {
+ return -1;
+ }
+ if (upperInclusive && isInclusive) {
+ return 0;
+ }
+ return -1;
+ }
+
+ public byte[] getLowerRange() {
+ return lowerRange;
+ }
+
+ public boolean isLowerInclusive() {
+ return lowerInclusive;
+ }
+
+ public byte[] getUpperRange() {
+ return upperRange;
+ }
+
+ public boolean isUpperInclusive() {
+ return upperInclusive;
+ }
+
+ public boolean isUnbound() {
+ return lowerUnbound() || upperUnbound();
+ }
+
+ public boolean upperUnbound() {
+ return upperRange == UNBOUND;
+ }
+
+ public boolean lowerUnbound() {
+ return lowerRange == UNBOUND;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.hashCode(lowerRange);
+ if (lowerRange != null)
+ result = prime * result + (lowerInclusive ? 1231 : 1237);
+ result = prime * result + Arrays.hashCode(upperRange);
+ if (upperRange != null)
+ result = prime * result + (upperInclusive ? 1231 : 1237);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ if (isSingleKey()) {
+ return Bytes.toStringBinary(lowerRange);
+ }
+ return (lowerInclusive ? "[" :
+ "(") + (lowerUnbound() ? "*" :
+ Bytes.toStringBinary(lowerRange)) + " - " + (upperUnbound() ? "*" :
+ Bytes.toStringBinary(upperRange)) + (upperInclusive ? "]" : ")" );
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof KeyRange)) {
+ return false;
+ }
+ KeyRange that = (KeyRange)o;
+ return Bytes.compareTo(this.lowerRange,that.lowerRange) == 0 && this.lowerInclusive == that.lowerInclusive &&
+ Bytes.compareTo(this.upperRange, that.upperRange) == 0 && this.upperInclusive == that.upperInclusive;
+ }
+
+ public KeyRange intersect(KeyRange range) {
+ byte[] newLowerRange;
+ byte[] newUpperRange;
+ boolean newLowerInclusive;
+ boolean newUpperInclusive;
+ if (lowerUnbound()) {
+ newLowerRange = range.lowerRange;
+ newLowerInclusive = range.lowerInclusive;
+ } else if (range.lowerUnbound()) {
+ newLowerRange = lowerRange;
+ newLowerInclusive = lowerInclusive;
+ } else {
+ int cmp = Bytes.compareTo(lowerRange, range.lowerRange);
+ if (cmp != 0 || lowerInclusive == range.lowerInclusive) {
+ if (cmp <= 0) {
+ newLowerRange = range.lowerRange;
+ newLowerInclusive = range.lowerInclusive;
+ } else {
+ newLowerRange = lowerRange;
+ newLowerInclusive = lowerInclusive;
+ }
+ } else { // Same lower range, but one is not inclusive
+ newLowerRange = range.lowerRange;
+ newLowerInclusive = false;
+ }
+ }
+ if (upperUnbound()) {
+ newUpperRange = range.upperRange;
+ newUpperInclusive = range.upperInclusive;
+ } else if (range.upperUnbound()) {
+ newUpperRange = upperRange;
+ newUpperInclusive = upperInclusive;
+ } else {
+ int cmp = Bytes.compareTo(upperRange, range.upperRange);
+ if (cmp != 0 || upperInclusive == range.upperInclusive) {
+ if (cmp >= 0) {
+ newUpperRange = range.upperRange;
+ newUpperInclusive = range.upperInclusive;
+ } else {
+ newUpperRange = upperRange;
+ newUpperInclusive = upperInclusive;
+ }
+ } else { // Same upper range, but one is not inclusive
+ newUpperRange = range.upperRange;
+ newUpperInclusive = false;
+ }
+ }
+ if (newLowerRange == lowerRange && newLowerInclusive == lowerInclusive
+ && newUpperRange == upperRange && newUpperInclusive == upperInclusive) {
+ return this;
+ }
+ return getKeyRange(newLowerRange, newLowerInclusive, newUpperRange, newUpperInclusive);
+ }
+
+ public static boolean isDegenerate(byte[] lowerRange, byte[] upperRange) {
+ return lowerRange == KeyRange.EMPTY_RANGE.getLowerRange() && upperRange == KeyRange.EMPTY_RANGE.getUpperRange();
+ }
+
+ public KeyRange appendSeparator() {
+ byte[] lowerBound = getLowerRange();
+ byte[] upperBound = getUpperRange();
+ if (lowerBound != UNBOUND) {
+ lowerBound = ByteUtil.concat(lowerBound, SEPARATOR_BYTE_ARRAY);
+ }
+ if (upperBound != UNBOUND) {
+ upperBound = ByteUtil.concat(upperBound, SEPARATOR_BYTE_ARRAY);
+ }
+ return getKeyRange(lowerBound, lowerInclusive, upperBound, upperInclusive);
+ }
+
+ /**
+ * @return list of at least size 1
+ */
+ @NonNull
+ public static List<KeyRange> coalesce(List<KeyRange> keyRanges) {
+ List<KeyRange> tmp = new ArrayList<KeyRange>();
+ for (KeyRange keyRange : keyRanges) {
+ if (EMPTY_RANGE == keyRange) {
+ continue;
+ }
+ if (EVERYTHING_RANGE == keyRange) {
+ tmp.clear();
+ tmp.add(keyRange);
+ break;
+ }
+ tmp.add(keyRange);
+ }
+ if (tmp.size() == 1) {
+ return tmp;
+ }
+ if (tmp.size() == 0) {
+ return Collections.singletonList(EMPTY_RANGE);
+ }
+
+ Collections.sort(tmp, COMPARATOR);
+ List<KeyRange> tmp2 = new ArrayList<KeyRange>();
+ KeyRange range = tmp.get(0);
+ for (int i=1; i<tmp.size(); i++) {
+ KeyRange otherRange = tmp.get(i);
+ KeyRange intersect = range.intersect(otherRange);
+ if (EMPTY_RANGE == intersect) {
+ tmp2.add(range);
+ range = otherRange;
+ } else {
+ range = range.union(otherRange);
+ }
+ }
+ tmp2.add(range);
+ List<KeyRange> tmp3 = new ArrayList<KeyRange>();
+ range = tmp2.get(0);
+ for (int i=1; i<tmp2.size(); i++) {
+ KeyRange otherRange = tmp2.get(i);
+ assert !range.upperUnbound();
+ assert !otherRange.lowerUnbound();
+ if (range.isUpperInclusive() != otherRange.isLowerInclusive()
+ && Bytes.equals(range.getUpperRange(), otherRange.getLowerRange())) {
+ range = KeyRange.getKeyRange(range.getLowerRange(), range.isLowerInclusive(), otherRange.getUpperRange(), otherRange.isUpperInclusive());
+ } else {
+ tmp3.add(range);
+ range = otherRange;
+ }
+ }
+ tmp3.add(range);
+
+ return tmp3;
+ }
+
+ public KeyRange union(KeyRange other) {
+ if (EMPTY_RANGE == other) return this;
+ if (EMPTY_RANGE == this) return other;
+ byte[] newLower, newUpper;
+ boolean newLowerInclusive, newUpperInclusive;
+ if (this.lowerUnbound() || other.lowerUnbound()) {
+ newLower = UNBOUND;
+ newLowerInclusive = false;
+ } else {
+ int lowerCmp = Bytes.compareTo(this.lowerRange, other.lowerRange);
+ if (lowerCmp < 0) {
+ newLower = lowerRange;
+ newLowerInclusive = lowerInclusive;
+ } else if (lowerCmp == 0) {
+ newLower = lowerRange;
+ newLowerInclusive = this.lowerInclusive || other.lowerInclusive;
+ } else {
+ newLower = other.lowerRange;
+ newLowerInclusive = other.lowerInclusive;
+ }
+ }
+
+ if (this.upperUnbound() || other.upperUnbound()) {
+ newUpper = UNBOUND;
+ newUpperInclusive = false;
+ } else {
+ int upperCmp = Bytes.compareTo(this.upperRange, other.upperRange);
+ if (upperCmp > 0) {
+ newUpper = upperRange;
+ newUpperInclusive = this.upperInclusive;
+ } else if (upperCmp == 0) {
+ newUpper = upperRange;
+ newUpperInclusive = this.upperInclusive || other.upperInclusive;
+ } else {
+ newUpper = other.upperRange;
+ newUpperInclusive = other.upperInclusive;
+ }
+ }
+ return KeyRange.getKeyRange(newLower, newLowerInclusive, newUpper, newUpperInclusive);
+ }
+
+ public static List<KeyRange> of(List<byte[]> keys) {
+ return Lists.transform(keys, POINT);
+ }
+
+ public static List<KeyRange> intersect(List<KeyRange> keyRanges, List<KeyRange> keyRanges2) {
+ List<KeyRange> tmp = new ArrayList<KeyRange>();
+ for (KeyRange r1 : keyRanges) {
+ for (KeyRange r2 : keyRanges2) {
+ KeyRange r = r1.intersect(r2);
+ if (EMPTY_RANGE != r) {
+ tmp.add(r);
+ }
+ }
+ }
+ if (tmp.size() == 0) {
+ return Collections.singletonList(KeyRange.EMPTY_RANGE);
+ }
+ Collections.sort(tmp, KeyRange.COMPARATOR);
+ List<KeyRange> tmp2 = new ArrayList<KeyRange>();
+ KeyRange r = tmp.get(0);
+ for (int i=1; i<tmp.size(); i++) {
+ if (EMPTY_RANGE == r.intersect(tmp.get(i))) {
+ tmp2.add(r);
+ r = tmp.get(i);
+ } else {
+ r = r.intersect(tmp.get(i));
+ }
+ }
+ tmp2.add(r);
+ return tmp2;
+ }
+
+ /**
+ * Fill both upper and lower range of keyRange to keyLength bytes.
+ * If the upper bound is inclusive, it must be filled such that an
+ * intersection with a longer key would still match if the shorter
+ * length matches. For example: (*,00C] intersected with [00Caaa,00Caaa]
+ * should still return [00Caaa,00Caaa] since the 00C matches and is
+ * inclusive.
+ * @param keyLength
+ * @return the newly filled KeyRange
+ */
+ public KeyRange fill(int keyLength) {
+ byte[] lowerRange = this.getLowerRange();
+ byte[] newLowerRange = lowerRange;
+ if (!this.lowerUnbound()) {
+ // If lower range is inclusive, fill with 0x00 since conceptually these bytes are included in the range
+ newLowerRange = ByteUtil.fillKey(lowerRange, keyLength);
+ }
+ byte[] upperRange = this.getUpperRange();
+ byte[] newUpperRange = upperRange;
+ if (!this.upperUnbound()) {
+ // If upper range is inclusive, fill with 0xFF since conceptually these bytes are included in the range
+ newUpperRange = ByteUtil.fillKey(upperRange, keyLength);
+ }
+ if (newLowerRange != lowerRange || newUpperRange != upperRange) {
+ return KeyRange.getKeyRange(newLowerRange, this.isLowerInclusive(), newUpperRange, this.isUpperInclusive());
+ }
+ return this;
+ }
+
+ public KeyRange invert() {
+ byte[] lower = this.getLowerRange();
+ if (!this.lowerUnbound()) {
+ lower = ColumnModifier.SORT_DESC.apply(lower, 0, lower.length);
+ }
+ byte[] upper;
+ if (this.isSingleKey()) {
+ upper = lower;
+ } else {
+ upper = this.getUpperRange();
+ if (!this.upperUnbound()) {
+ upper = ColumnModifier.SORT_DESC.apply(upper, 0, upper.length);
+ }
+ }
+ return KeyRange.getKeyRange(lower, this.isLowerInclusive(), upper, this.isUpperInclusive());
+ }
+
+ @Override
+ public void readFields(DataInput in) throws IOException {
+ int len = WritableUtils.readVInt(in);
+ if (len == 0) {
+ lowerRange = KeyRange.UNBOUND;
+ lowerInclusive = false;
+ } else {
+ if (len < 0) {
+ lowerInclusive = false;
+ lowerRange = new byte[-len - 1];
+ in.readFully(lowerRange);
+ } else {
+ lowerInclusive = true;
+ lowerRange = new byte[len - 1];
+ in.readFully(lowerRange);
+ }
+ }
+ len = WritableUtils.readVInt(in);
+ if (len == 0) {
+ upperRange = KeyRange.UNBOUND;
+ upperInclusive = false;
+ } else {
+ if (len < 0) {
+ upperInclusive = false;
+ upperRange = new byte[-len - 1];
+ in.readFully(upperRange);
+ } else {
+ upperInclusive = true;
+ upperRange = new byte[len - 1];
+ in.readFully(upperRange);
+ }
+ }
+ init();
+ }
+
+ private void writeBound(Bound bound, DataOutput out) throws IOException {
+ // Encode unbound by writing a zero
+ if (isUnbound(bound)) {
+ WritableUtils.writeVInt(out, 0);
+ return;
+ }
+ // Otherwise, inclusive is positive and exclusive is negative, offset by 1
+ byte[] range = getRange(bound);
+ if (isInclusive(bound)){
+ WritableUtils.writeVInt(out, range.length+1);
+ } else {
+ WritableUtils.writeVInt(out, -(range.length+1));
+ }
+ out.write(range);
+ }
+
+ @Override
+ public void write(DataOutput out) throws IOException {
+ writeBound(Bound.LOWER, out);
+ writeBound(Bound.UPPER, out);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/query/MetaDataMutated.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/MetaDataMutated.java b/phoenix-core/src/main/java/org/apache/phoenix/query/MetaDataMutated.java
new file mode 100644
index 0000000..0ed1d56
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/MetaDataMutated.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.query;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import org.apache.phoenix.schema.PColumn;
+import org.apache.phoenix.schema.PMetaData;
+import org.apache.phoenix.schema.PTable;
+
+
+/**
+ *
+ * Interface for applying schema mutations to our client-side schema cache
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public interface MetaDataMutated {
+ PMetaData addTable(PTable table) throws SQLException;
+ PMetaData removeTable(String tableName) throws SQLException;
+ PMetaData addColumn(String tableName, List<PColumn> columns, long tableTimeStamp, long tableSeqNum, boolean isImmutableRows) throws SQLException;
+ PMetaData removeColumn(String tableName, String familyName, String columnName, long tableTimeStamp, long tableSeqNum) throws SQLException;
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/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
new file mode 100644
index 0000000..48bd7f7
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.query;
+
+
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.ARRAY_SIZE;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.BUFFER_LENGTH;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.CACHE_SIZE;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.CHAR_OCTET_LENGTH;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.COLUMN_COUNT;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.COLUMN_DEF;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.COLUMN_MODIFIER;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.COLUMN_NAME;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.COLUMN_SIZE;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.CURRENT_VALUE;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.DATA_TABLE_NAME;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.DATA_TYPE;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.DECIMAL_DIGITS;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.DEFAULT_COLUMN_FAMILY_NAME;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.DISABLE_WAL;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.IMMUTABLE_ROWS;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.INCREMENT_BY;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.INDEX_STATE;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.IS_AUTOINCREMENT;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.IS_NULLABLE;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.LINK_TYPE;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.MULTI_TENANT;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.NULLABLE;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.NUM_PREC_RADIX;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.ORDINAL_POSITION;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.PK_NAME;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.REF_GENERATION_NAME;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.REMARKS_NAME;
+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;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SCOPE_TABLE;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SELF_REFERENCING_COL_NAME_NAME;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SEQUENCE_NAME;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SEQUENCE_SCHEMA;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SOURCE_DATA_TYPE;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SQL_DATA_TYPE;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SQL_DATETIME_SUB;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.START_WITH;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TABLE_CAT_NAME;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TABLE_NAME_NAME;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TABLE_SCHEM_NAME;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TABLE_SEQ_NUM;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TABLE_TYPE_NAME;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TENANT_ID;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TYPE_NAME;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TYPE_SCHEMA;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TYPE_SEQUENCE;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TYPE_TABLE;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_STATEMENT;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_TYPE;
+
+import java.math.BigDecimal;
+
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.index.util.ImmutableBytesPtr;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.phoenix.coprocessor.MetaDataProtocol;
+import org.apache.phoenix.schema.MetaDataSplitPolicy;
+import org.apache.phoenix.schema.PName;
+import org.apache.phoenix.schema.PNormalizedName;
+
+
+/**
+ *
+ * Constants used during querying
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public interface QueryConstants {
+ public static final String NAME_SEPARATOR = ".";
+ public final static byte[] NAME_SEPARATOR_BYTES = Bytes.toBytes(NAME_SEPARATOR);
+ public static final byte NAME_SEPARATOR_BYTE = NAME_SEPARATOR_BYTES[0];
+ 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 PHOENIX_SCHEMA = "system";
+ public final static String PHOENIX_METADATA = "table";
+
+ public final static PName SINGLE_COLUMN_NAME = new PNormalizedName("s");
+ public final static PName SINGLE_COLUMN_FAMILY_NAME = new PNormalizedName("s");
+ public final static byte[] SINGLE_COLUMN = SINGLE_COLUMN_NAME.getBytes();
+ public final static byte[] SINGLE_COLUMN_FAMILY = SINGLE_COLUMN_FAMILY_NAME.getBytes();
+
+ public static final long AGG_TIMESTAMP = HConstants.LATEST_TIMESTAMP;
+ /**
+ * Key used for a single row aggregation where there is no group by
+ */
+ 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[] TRUE = new byte[] {1};
+
+ /**
+ * Separator used between variable length keys for a composite key.
+ * Variable length data types may not use this byte value.
+ */
+ 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;
+
+ public static final String EMPTY_COLUMN_NAME = "_0";
+ public static final byte[] EMPTY_COLUMN_BYTES = Bytes.toBytes(EMPTY_COLUMN_NAME);
+ public static final ImmutableBytesPtr EMPTY_COLUMN_BYTES_PTR = new ImmutableBytesPtr(
+ EMPTY_COLUMN_BYTES);
+ public static final String DEFAULT_COLUMN_FAMILY = EMPTY_COLUMN_NAME;
+ public static final byte[] DEFAULT_COLUMN_FAMILY_BYTES = EMPTY_COLUMN_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);
+
+
+ public static final String CREATE_TABLE_METADATA =
+ // Do not use IF NOT EXISTS as we sometimes catch the TableAlreadyExists exception
+ // and add columns to the SYSTEM.TABLE dynamically.
+ "CREATE TABLE " + TYPE_SCHEMA + ".\"" + TYPE_TABLE + "\"(\n" +
+ // PK columns
+ TENANT_ID + " VARCHAR NULL," +
+ TABLE_SCHEM_NAME + " VARCHAR NULL," +
+ TABLE_NAME_NAME + " VARCHAR NOT NULL," +
+ COLUMN_NAME + " VARCHAR NULL," + // null only for table row
+ TABLE_CAT_NAME + " VARCHAR NULL," + // using for CF - ensures uniqueness for columns
+ // Table metadata (will be null for column rows)
+ TABLE_TYPE_NAME + " CHAR(1)," +
+ REMARKS_NAME + " VARCHAR," +
+ DATA_TYPE + " INTEGER," +
+ PK_NAME + " VARCHAR," +
+ TYPE_NAME + " VARCHAR," +
+ SELF_REFERENCING_COL_NAME_NAME + " VARCHAR," +
+ REF_GENERATION_NAME + " VARCHAR," +
+ TABLE_SEQ_NUM + " BIGINT," +
+ COLUMN_COUNT + " INTEGER," +
+ // Column metadata (will be null for table row)
+ COLUMN_SIZE + " INTEGER," +
+ BUFFER_LENGTH + " INTEGER," +
+ DECIMAL_DIGITS + " INTEGER," +
+ NUM_PREC_RADIX + " INTEGER," +
+ NULLABLE + " INTEGER," +
+ COLUMN_DEF + " VARCHAR," +
+ SQL_DATA_TYPE + " INTEGER," +
+ SQL_DATETIME_SUB + " INTEGER," +
+ CHAR_OCTET_LENGTH + " INTEGER," +
+ ORDINAL_POSITION + " INTEGER," +
+ IS_NULLABLE + " VARCHAR," +
+ SCOPE_CATALOG + " VARCHAR," +
+ SCOPE_SCHEMA + " VARCHAR," +
+ SCOPE_TABLE + " VARCHAR," +
+ SOURCE_DATA_TYPE + " INTEGER," + // supposed to be SHORT
+ IS_AUTOINCREMENT + " VARCHAR," +
+ // Columns added in 1.2.1
+ COLUMN_MODIFIER + " INTEGER," +
+ SALT_BUCKETS + " INTEGER," +
+ // Columns added in 2.0.0
+ DATA_TABLE_NAME + " VARCHAR," +
+ INDEX_STATE + " CHAR(1),\n" +
+ IMMUTABLE_ROWS + " BOOLEAN,\n" +
+ // Columns added in 3.0.0
+ VIEW_STATEMENT + " VARCHAR,\n" +
+ DEFAULT_COLUMN_FAMILY_NAME + " VARCHAR,\n" +
+ DISABLE_WAL + " BOOLEAN,\n" +
+ MULTI_TENANT + " BOOLEAN,\n" +
+ VIEW_TYPE + " UNSIGNED_TINYINT,\n" +
+ LINK_TYPE + " UNSIGNED_TINYINT,\n" +
+ ARRAY_SIZE + " INTEGER,\n" +
+ "CONSTRAINT " + SYSTEM_TABLE_PK_NAME + " PRIMARY KEY (" + TENANT_ID + ","
+ + TABLE_SCHEM_NAME + "," + TABLE_NAME_NAME + "," + COLUMN_NAME + "," + TABLE_CAT_NAME + "))\n" +
+ HConstants.VERSIONS + "=" + MetaDataProtocol.DEFAULT_MAX_META_DATA_VERSIONS + ",\n" +
+ DEFAULT_COLUMN_FAMILY_NAME + "=" + "'_0'" + ",\n" + // Use original default for b/w compat
+ HTableDescriptor.SPLIT_POLICY + "='" + MetaDataSplitPolicy.class.getName() + "'\n";
+
+ public static final String CREATE_SEQUENCE_METADATA =
+ "CREATE TABLE IF NOT EXISTS " + TYPE_SCHEMA + ".\"" + TYPE_SEQUENCE + "\"(\n" +
+ TENANT_ID + " VARCHAR NULL," +
+ SEQUENCE_SCHEMA + " VARCHAR NULL, \n" +
+ SEQUENCE_NAME + " VARCHAR NOT NULL, \n" +
+ START_WITH + " BIGINT NOT NULL, \n" +
+ CURRENT_VALUE + " BIGINT NOT NULL, \n" +
+ INCREMENT_BY + " BIGINT NOT NULL, \n" +
+ CACHE_SIZE + " INTEGER NOT NULL \n" +
+ " CONSTRAINT " + SYSTEM_TABLE_PK_NAME + " PRIMARY KEY (" + TENANT_ID + "," + SEQUENCE_SCHEMA + "," + SEQUENCE_NAME + "))\n" +
+ HConstants.VERSIONS + "=" + MetaDataProtocol.DEFAULT_MAX_META_DATA_VERSIONS + "\n";
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
new file mode 100644
index 0000000..320bac0
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.query;
+
+import java.util.concurrent.ExecutorService;
+
+import org.apache.http.annotation.Immutable;
+
+import org.apache.phoenix.iterate.SpoolTooBigToDiskException;
+import org.apache.phoenix.memory.MemoryManager;
+import org.apache.phoenix.optimize.QueryOptimizer;
+import org.apache.phoenix.util.ReadOnlyProps;
+import org.apache.phoenix.util.SQLCloseable;
+
+
+
+/**
+ *
+ * Interface to group together services needed during querying. The
+ * parameters that may be set in {@link org.apache.hadoop.conf.Configuration}
+ * are documented here: https://github.com/forcedotcom/phoenix/wiki/Tuning
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+@Immutable
+public interface QueryServices extends SQLCloseable {
+ public static final String KEEP_ALIVE_MS_ATTRIB = "phoenix.query.keepAliveMs";
+ public static final String THREAD_POOL_SIZE_ATTRIB = "phoenix.query.threadPoolSize";
+ public static final String QUEUE_SIZE_ATTRIB = "phoenix.query.queueSize";
+ public static final String THREAD_TIMEOUT_MS_ATTRIB = "phoenix.query.timeoutMs";
+ public static final String SPOOL_THRESHOLD_BYTES_ATTRIB = "phoenix.query.spoolThresholdBytes";
+
+ /**
+ * max size to spool the the result into
+ * ${java.io.tmpdir}/ResultSpoolerXXX.bin if
+ * {@link QueryServices#SPOOL_THRESHOLD_BYTES_ATTRIB } is reached.
+ * <p>
+ * default is unlimited(-1)
+ * <p>
+ * if the threshold is reached, a {@link SpoolTooBigToDiskException } will be thrown
+ */
+ public static final String MAX_SPOOL_TO_DISK_BYTES_ATTRIB = "phoenix.query.maxSpoolToDiskBytes";
+
+ public static final String MAX_MEMORY_PERC_ATTRIB = "phoenix.query.maxGlobalMemoryPercentage";
+ public static final String MAX_MEMORY_WAIT_MS_ATTRIB = "phoenix.query.maxGlobalMemoryWaitMs";
+ public static final String MAX_TENANT_MEMORY_PERC_ATTRIB = "phoenix.query.maxTenantMemoryPercentage";
+ public static final String MAX_SERVER_CACHE_SIZE_ATTRIB = "phoenix.query.maxServerCacheBytes";
+ public static final String TARGET_QUERY_CONCURRENCY_ATTRIB = "phoenix.query.targetConcurrency";
+ public static final String MAX_QUERY_CONCURRENCY_ATTRIB = "phoenix.query.maxConcurrency";
+ public static final String DATE_FORMAT_ATTRIB = "phoenix.query.dateFormat";
+ public static final String NUMBER_FORMAT_ATTRIB = "phoenix.query.numberFormat";
+ public static final String STATS_UPDATE_FREQ_MS_ATTRIB = "phoenix.query.statsUpdateFrequency";
+ public static final String MAX_STATS_AGE_MS_ATTRIB = "phoenix.query.maxStatsAge";
+ public static final String CALL_QUEUE_ROUND_ROBIN_ATTRIB = "ipc.server.callqueue.roundrobin";
+ public static final String SCAN_CACHE_SIZE_ATTRIB = "hbase.client.scanner.caching";
+ public static final String MAX_MUTATION_SIZE_ATTRIB = "phoenix.mutate.maxSize";
+ public static final String MUTATE_BATCH_SIZE_ATTRIB = "phoenix.mutate.batchSize";
+ public static final String MAX_SERVER_CACHE_TIME_TO_LIVE_MS = "phoenix.coprocessor.maxServerCacheTimeToLiveMs";
+ public static final String MAX_INTRA_REGION_PARALLELIZATION_ATTRIB = "phoenix.query.maxIntraRegionParallelization";
+ public static final String ROW_KEY_ORDER_SALTED_TABLE_ATTRIB = "phoenix.query.rowKeyOrderSaltedTable";
+ public static final String USE_INDEXES_ATTRIB = "phoenix.query.useIndexes";
+ public static final String IMMUTABLE_ROWS_ATTRIB = "phoenix.mutate.immutableRows";
+ public static final String INDEX_MUTATE_BATCH_SIZE_THRESHOLD_ATTRIB = "phoenix.index.mutableBatchSizeThreshold";
+ public static final String DROP_METADATA_ATTRIB = "phoenix.schema.dropMetaData";
+ public static final String GROUPBY_SPILLABLE_ATTRIB = "phoenix.groupby.spillable";
+ public static final String GROUPBY_SPILL_FILES_ATTRIB = "phoenix.groupby.spillFiles";
+ public static final String GROUPBY_MAX_CACHE_SIZE_ATTRIB = "phoenix.groupby.maxCacheSize";
+
+ public static final String CALL_QUEUE_PRODUCER_ATTRIB_NAME = "CALL_QUEUE_PRODUCER";
+
+ public static final String MASTER_INFO_PORT_ATTRIB = "hbase.master.info.port";
+ public static final String REGIONSERVER_INFO_PORT_ATTRIB = "hbase.regionserver.info.port";
+ public static final String REGIONSERVER_LEASE_PERIOD_ATTRIB = "hbase.regionserver.lease.period";
+ public static final String RPC_TIMEOUT_ATTRIB = "hbase.rpc.timeout";
+ public static final String ZOOKEEPER_QUARUM_ATTRIB = "hbase.zookeeper.quorum";
+ public static final String ZOOKEEPER_PORT_ATTRIB = "hbase.zookeeper.property.clientPort";
+ public static final String ZOOKEEPER_ROOT_NODE_ATTRIB = "zookeeper.znode.parent";
+ public static final String DISTINCT_VALUE_COMPRESS_THRESHOLD_ATTRIB = "phoenix.distinct.value.compress.threshold";
+ public static final String SEQUENCE_CACHE_SIZE_ATTRIB = "phoenix.sequence.cacheSize";
+
+
+ /**
+ * Get executor service used for parallel scans
+ */
+ public ExecutorService getExecutor();
+ /**
+ * Get the memory manager used to track memory usage
+ */
+ public MemoryManager getMemoryManager();
+
+ /**
+ * Get the properties from the HBase configuration in a
+ * read-only structure that avoids any synchronization
+ */
+ public ReadOnlyProps getProps();
+
+ /**
+ * Get query optimizer used to choose the best query plan
+ */
+ public QueryOptimizer getOptimizer();
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesImpl.java
new file mode 100644
index 0000000..5ebde59
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesImpl.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.query;
+
+
+
+
+
+/**
+ *
+ * Real implementation of QueryServices for use in runtime and perf testing
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public final class QueryServicesImpl extends BaseQueryServicesImpl {
+
+ public QueryServicesImpl() {
+ super(QueryServicesOptions.withDefaults());
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
new file mode 100644
index 0000000..7f3025c
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.query;
+
+import static org.apache.phoenix.query.QueryServices.CALL_QUEUE_PRODUCER_ATTRIB_NAME;
+import static org.apache.phoenix.query.QueryServices.CALL_QUEUE_ROUND_ROBIN_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.DATE_FORMAT_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.DROP_METADATA_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.GROUPBY_MAX_CACHE_SIZE_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.GROUPBY_SPILL_FILES_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.GROUPBY_SPILLABLE_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.IMMUTABLE_ROWS_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.INDEX_MUTATE_BATCH_SIZE_THRESHOLD_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.KEEP_ALIVE_MS_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.MASTER_INFO_PORT_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.MAX_INTRA_REGION_PARALLELIZATION_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.MAX_MEMORY_PERC_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.MAX_MEMORY_WAIT_MS_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.MAX_MUTATION_SIZE_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.MAX_QUERY_CONCURRENCY_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.MAX_SERVER_CACHE_SIZE_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.MAX_SERVER_CACHE_TIME_TO_LIVE_MS;
+import static org.apache.phoenix.query.QueryServices.MAX_SPOOL_TO_DISK_BYTES_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.MAX_TENANT_MEMORY_PERC_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.MUTATE_BATCH_SIZE_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.QUEUE_SIZE_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.REGIONSERVER_INFO_PORT_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.REGIONSERVER_LEASE_PERIOD_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.ROW_KEY_ORDER_SALTED_TABLE_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.RPC_TIMEOUT_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.SCAN_CACHE_SIZE_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.SEQUENCE_CACHE_SIZE_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.SPOOL_THRESHOLD_BYTES_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.STATS_UPDATE_FREQ_MS_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.TARGET_QUERY_CONCURRENCY_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.THREAD_POOL_SIZE_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.THREAD_TIMEOUT_MS_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.USE_INDEXES_ATTRIB;
+
+import java.util.Map.Entry;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.regionserver.wal.WALEditCodec;
+
+import org.apache.phoenix.util.DateUtil;
+import org.apache.phoenix.util.ReadOnlyProps;
+
+
+/**
+ * Options for {@link QueryServices}.
+ *
+ * @author syyang
+ * @since 0.1
+ */
+public class QueryServicesOptions {
+ public static final int DEFAULT_KEEP_ALIVE_MS = 60000;
+ public static final int DEFAULT_THREAD_POOL_SIZE = 128;
+ public static final int DEFAULT_QUEUE_SIZE = 500;
+ public static final int DEFAULT_THREAD_TIMEOUT_MS = 600000; // 10min
+ public static final int DEFAULT_SPOOL_THRESHOLD_BYTES = 1024 * 1024 * 20; // 20m
+ public static final int DEFAULT_MAX_MEMORY_PERC = 50; // 50% of heap
+ public static final int DEFAULT_MAX_MEMORY_WAIT_MS = 10000;
+ public static final int DEFAULT_MAX_TENANT_MEMORY_PERC = 100;
+ public static final long DEFAULT_MAX_SERVER_CACHE_SIZE = 1024*1024*100; // 100 Mb
+ public static final int DEFAULT_TARGET_QUERY_CONCURRENCY = 32;
+ public static final int DEFAULT_MAX_QUERY_CONCURRENCY = 64;
+ public static final String DEFAULT_DATE_FORMAT = DateUtil.DEFAULT_DATE_FORMAT;
+ public static final int DEFAULT_STATS_UPDATE_FREQ_MS = 15 * 60000; // 15min
+ public static final int DEFAULT_MAX_STATS_AGE_MS = 24 * 60 * 60000; // 1 day
+ public static final boolean DEFAULT_CALL_QUEUE_ROUND_ROBIN = true;
+ public static final int DEFAULT_MAX_MUTATION_SIZE = 500000;
+ public static final boolean DEFAULT_ROW_KEY_ORDER_SALTED_TABLE = true; // Merge sort on client to ensure salted tables are row key ordered
+ public static final boolean DEFAULT_USE_INDEXES = true; // Use indexes
+ public static final boolean DEFAULT_IMMUTABLE_ROWS = false; // Tables rows may be updated
+ public static final boolean DEFAULT_DROP_METADATA = true; // Drop meta data also.
+
+ public final static int DEFAULT_MUTATE_BATCH_SIZE = 1000; // Batch size for UPSERT SELECT and DELETE
+ // The only downside of it being out-of-sync is that the parallelization of the scan won't be as balanced as it could be.
+ public static final int DEFAULT_MAX_SERVER_CACHE_TIME_TO_LIVE_MS = 30000; // 30 sec (with no activity)
+ public static final int DEFAULT_SCAN_CACHE_SIZE = 1000;
+ public static final int DEFAULT_MAX_INTRA_REGION_PARALLELIZATION = DEFAULT_MAX_QUERY_CONCURRENCY;
+ public static final int DEFAULT_DISTINCT_VALUE_COMPRESS_THRESHOLD = 1024 * 1024 * 1; // 1 Mb
+ public static final int DEFAULT_INDEX_MUTATE_BATCH_SIZE_THRESHOLD = 5;
+ public static final long DEFAULT_MAX_SPOOL_TO_DISK_BYTES = 1024000000;
+
+ //
+ // Spillable GroupBy - SPGBY prefix
+ //
+ // Enable / disable spillable group by
+ public static boolean DEFAULT_GROUPBY_SPILLABLE = true;
+ // Number of spill files / partitions the keys are distributed to
+ // Each spill file fits 2GB of data
+ public static final int DEFAULT_GROUPBY_SPILL_FILES = 2;
+ // Max size of 1st level main memory cache in bytes --> upper bound
+ public static final long DEFAULT_GROUPBY_MAX_CACHE_MAX = 1024L*1024L*100L; // 100 Mb
+
+ public static final int DEFAULT_SEQUENCE_CACHE_SIZE = 100; // reserve 100 sequences at a time
+
+
+ private final Configuration config;
+
+ private QueryServicesOptions(Configuration config) {
+ this.config = config;
+ }
+
+ public ReadOnlyProps getProps() {
+ // Ensure that HBase RPC time out value is at least as large as our thread time out for query.
+ int threadTimeOutMS = config.getInt(THREAD_TIMEOUT_MS_ATTRIB, DEFAULT_THREAD_TIMEOUT_MS);
+ int hbaseRPCTimeOut = config.getInt(HConstants.HBASE_RPC_TIMEOUT_KEY, HConstants.DEFAULT_HBASE_RPC_TIMEOUT);
+ if (threadTimeOutMS > hbaseRPCTimeOut) {
+ config.setInt(HConstants.HBASE_RPC_TIMEOUT_KEY, threadTimeOutMS);
+ }
+ return new ReadOnlyProps(config.iterator());
+ }
+
+ public QueryServicesOptions setAll(ReadOnlyProps props) {
+ for (Entry<String,String> entry : props) {
+ config.set(entry.getKey(), entry.getValue());
+ }
+ return this;
+ }
+
+ public static QueryServicesOptions withDefaults() {
+ Configuration config = HBaseFactoryProvider.getConfigurationFactory().getConfiguration();
+ QueryServicesOptions options = new QueryServicesOptions(config)
+ .setIfUnset(KEEP_ALIVE_MS_ATTRIB, DEFAULT_KEEP_ALIVE_MS)
+ .setIfUnset(THREAD_POOL_SIZE_ATTRIB, DEFAULT_THREAD_POOL_SIZE)
+ .setIfUnset(QUEUE_SIZE_ATTRIB, DEFAULT_QUEUE_SIZE)
+ .setIfUnset(THREAD_TIMEOUT_MS_ATTRIB, DEFAULT_THREAD_TIMEOUT_MS)
+ .setIfUnset(SPOOL_THRESHOLD_BYTES_ATTRIB, DEFAULT_SPOOL_THRESHOLD_BYTES)
+ .setIfUnset(MAX_MEMORY_PERC_ATTRIB, DEFAULT_MAX_MEMORY_PERC)
+ .setIfUnset(MAX_MEMORY_WAIT_MS_ATTRIB, DEFAULT_MAX_MEMORY_WAIT_MS)
+ .setIfUnset(MAX_TENANT_MEMORY_PERC_ATTRIB, DEFAULT_MAX_TENANT_MEMORY_PERC)
+ .setIfUnset(MAX_SERVER_CACHE_SIZE_ATTRIB, DEFAULT_MAX_SERVER_CACHE_SIZE)
+ .setIfUnset(SCAN_CACHE_SIZE_ATTRIB, DEFAULT_SCAN_CACHE_SIZE)
+ .setIfUnset(TARGET_QUERY_CONCURRENCY_ATTRIB, DEFAULT_TARGET_QUERY_CONCURRENCY)
+ .setIfUnset(MAX_QUERY_CONCURRENCY_ATTRIB, DEFAULT_MAX_QUERY_CONCURRENCY)
+ .setIfUnset(DATE_FORMAT_ATTRIB, DEFAULT_DATE_FORMAT)
+ .setIfUnset(STATS_UPDATE_FREQ_MS_ATTRIB, DEFAULT_STATS_UPDATE_FREQ_MS)
+ .setIfUnset(CALL_QUEUE_ROUND_ROBIN_ATTRIB, DEFAULT_CALL_QUEUE_ROUND_ROBIN)
+ .setIfUnset(MAX_MUTATION_SIZE_ATTRIB, DEFAULT_MAX_MUTATION_SIZE)
+ .setIfUnset(MAX_INTRA_REGION_PARALLELIZATION_ATTRIB, DEFAULT_MAX_INTRA_REGION_PARALLELIZATION)
+ .setIfUnset(ROW_KEY_ORDER_SALTED_TABLE_ATTRIB, DEFAULT_ROW_KEY_ORDER_SALTED_TABLE)
+ .setIfUnset(USE_INDEXES_ATTRIB, DEFAULT_USE_INDEXES)
+ .setIfUnset(IMMUTABLE_ROWS_ATTRIB, DEFAULT_IMMUTABLE_ROWS)
+ .setIfUnset(INDEX_MUTATE_BATCH_SIZE_THRESHOLD_ATTRIB, DEFAULT_INDEX_MUTATE_BATCH_SIZE_THRESHOLD)
+ .setIfUnset(MAX_SPOOL_TO_DISK_BYTES_ATTRIB, DEFAULT_MAX_SPOOL_TO_DISK_BYTES)
+ .setIfUnset(DROP_METADATA_ATTRIB, DEFAULT_DROP_METADATA)
+ .setIfUnset(GROUPBY_SPILLABLE_ATTRIB, DEFAULT_GROUPBY_SPILLABLE)
+ .setIfUnset(GROUPBY_MAX_CACHE_SIZE_ATTRIB, DEFAULT_GROUPBY_MAX_CACHE_MAX)
+ .setIfUnset(GROUPBY_SPILL_FILES_ATTRIB, DEFAULT_GROUPBY_SPILL_FILES)
+ .setIfUnset(SEQUENCE_CACHE_SIZE_ATTRIB, DEFAULT_SEQUENCE_CACHE_SIZE)
+ ;
+ // HBase sets this to 1, so we reset it to something more appropriate.
+ // Hopefully HBase will change this, because we can't know if a user set
+ // it to 1, so we'll change it.
+ int scanCaching = config.getInt(SCAN_CACHE_SIZE_ATTRIB, 0);
+ if (scanCaching == 1) {
+ config.setInt(SCAN_CACHE_SIZE_ATTRIB, DEFAULT_SCAN_CACHE_SIZE);
+ } else if (scanCaching <= 0) { // Provides the user with a way of setting it to 1
+ config.setInt(SCAN_CACHE_SIZE_ATTRIB, 1);
+ }
+ return options;
+ }
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ private QueryServicesOptions setIfUnset(String name, int value) {
+ config.setIfUnset(name, Integer.toString(value));
+ return this;
+ }
+
+ private QueryServicesOptions setIfUnset(String name, boolean value) {
+ config.setIfUnset(name, Boolean.toString(value));
+ return this;
+ }
+
+ private QueryServicesOptions setIfUnset(String name, long value) {
+ config.setIfUnset(name, Long.toString(value));
+ return this;
+ }
+
+ private QueryServicesOptions setIfUnset(String name, String value) {
+ config.setIfUnset(name, value);
+ return this;
+ }
+
+ public QueryServicesOptions setKeepAliveMs(int keepAliveMs) {
+ return set(KEEP_ALIVE_MS_ATTRIB, keepAliveMs);
+ }
+
+ public QueryServicesOptions setThreadPoolSize(int threadPoolSize) {
+ return set(THREAD_POOL_SIZE_ATTRIB, threadPoolSize);
+ }
+
+ public QueryServicesOptions setQueueSize(int queueSize) {
+ config.setInt(QUEUE_SIZE_ATTRIB, queueSize);
+ return this;
+ }
+
+ public QueryServicesOptions setThreadTimeoutMs(int threadTimeoutMs) {
+ return set(THREAD_TIMEOUT_MS_ATTRIB, threadTimeoutMs);
+ }
+
+ public QueryServicesOptions setSpoolThresholdBytes(int spoolThresholdBytes) {
+ return set(SPOOL_THRESHOLD_BYTES_ATTRIB, spoolThresholdBytes);
+ }
+
+ public QueryServicesOptions setMaxMemoryPerc(int maxMemoryPerc) {
+ return set(MAX_MEMORY_PERC_ATTRIB, maxMemoryPerc);
+ }
+
+ public QueryServicesOptions setMaxMemoryWaitMs(int maxMemoryWaitMs) {
+ return set(MAX_MEMORY_WAIT_MS_ATTRIB, maxMemoryWaitMs);
+ }
+
+ public QueryServicesOptions setMaxTenantMemoryPerc(int maxTenantMemoryPerc) {
+ return set(MAX_TENANT_MEMORY_PERC_ATTRIB, maxTenantMemoryPerc);
+ }
+
+ public QueryServicesOptions setMaxServerCacheSize(long maxServerCacheSize) {
+ return set(MAX_SERVER_CACHE_SIZE_ATTRIB, maxServerCacheSize);
+ }
+
+ public QueryServicesOptions setScanFetchSize(int scanFetchSize) {
+ return set(SCAN_CACHE_SIZE_ATTRIB, scanFetchSize);
+ }
+
+ public QueryServicesOptions setMaxQueryConcurrency(int maxQueryConcurrency) {
+ return set(MAX_QUERY_CONCURRENCY_ATTRIB, maxQueryConcurrency);
+ }
+
+ public QueryServicesOptions setTargetQueryConcurrency(int targetQueryConcurrency) {
+ return set(TARGET_QUERY_CONCURRENCY_ATTRIB, targetQueryConcurrency);
+ }
+
+ public QueryServicesOptions setDateFormat(String dateFormat) {
+ return set(DATE_FORMAT_ATTRIB, dateFormat);
+ }
+
+ public QueryServicesOptions setStatsUpdateFrequencyMs(int frequencyMs) {
+ return set(STATS_UPDATE_FREQ_MS_ATTRIB, frequencyMs);
+ }
+
+ public QueryServicesOptions setCallQueueRoundRobin(boolean isRoundRobin) {
+ return set(CALL_QUEUE_PRODUCER_ATTRIB_NAME, isRoundRobin);
+ }
+
+ public QueryServicesOptions setMaxMutateSize(int maxMutateSize) {
+ return set(MAX_MUTATION_SIZE_ATTRIB, maxMutateSize);
+ }
+
+ public QueryServicesOptions setMutateBatchSize(int mutateBatchSize) {
+ return set(MUTATE_BATCH_SIZE_ATTRIB, mutateBatchSize);
+ }
+
+ public QueryServicesOptions setMaxIntraRegionParallelization(int maxIntraRegionParallelization) {
+ return set(MAX_INTRA_REGION_PARALLELIZATION_ATTRIB, maxIntraRegionParallelization);
+ }
+
+ public QueryServicesOptions setRowKeyOrderSaltedTable(boolean rowKeyOrderSaltedTable) {
+ return set(ROW_KEY_ORDER_SALTED_TABLE_ATTRIB, rowKeyOrderSaltedTable);
+ }
+
+ public QueryServicesOptions setDropMetaData(boolean dropMetadata) {
+ return set(DROP_METADATA_ATTRIB, dropMetadata);
+ }
+
+ public QueryServicesOptions setSPGBYEnabled(boolean enabled) {
+ return set(GROUPBY_SPILLABLE_ATTRIB, enabled);
+ }
+
+ public QueryServicesOptions setSPGBYMaxCacheSize(long size) {
+ return set(GROUPBY_MAX_CACHE_SIZE_ATTRIB, size);
+ }
+
+ public QueryServicesOptions setSPGBYNumSpillFiles(long num) {
+ return set(GROUPBY_SPILL_FILES_ATTRIB, num);
+ }
+
+
+ private QueryServicesOptions set(String name, boolean value) {
+ config.set(name, Boolean.toString(value));
+ return this;
+ }
+
+ private QueryServicesOptions set(String name, int value) {
+ config.set(name, Integer.toString(value));
+ return this;
+ }
+
+ private QueryServicesOptions set(String name, String value) {
+ config.set(name, value);
+ return this;
+ }
+
+ private QueryServicesOptions set(String name, long value) {
+ config.set(name, Long.toString(value));
+ return this;
+ }
+
+ public int getKeepAliveMs() {
+ return config.getInt(KEEP_ALIVE_MS_ATTRIB, DEFAULT_KEEP_ALIVE_MS);
+ }
+
+ public int getThreadPoolSize() {
+ return config.getInt(THREAD_POOL_SIZE_ATTRIB, DEFAULT_THREAD_POOL_SIZE);
+ }
+
+ public int getQueueSize() {
+ return config.getInt(QUEUE_SIZE_ATTRIB, DEFAULT_QUEUE_SIZE);
+ }
+
+ public int getMaxMemoryPerc() {
+ return config.getInt(MAX_MEMORY_PERC_ATTRIB, DEFAULT_MAX_MEMORY_PERC);
+ }
+
+ public int getMaxMemoryWaitMs() {
+ return config.getInt(MAX_MEMORY_WAIT_MS_ATTRIB, DEFAULT_MAX_MEMORY_WAIT_MS);
+ }
+
+ public int getMaxMutateSize() {
+ return config.getInt(MAX_MUTATION_SIZE_ATTRIB, DEFAULT_MAX_MUTATION_SIZE);
+ }
+
+ public int getMutateBatchSize() {
+ return config.getInt(MUTATE_BATCH_SIZE_ATTRIB, DEFAULT_MUTATE_BATCH_SIZE);
+ }
+
+ public int getMaxIntraRegionParallelization() {
+ return config.getInt(MAX_INTRA_REGION_PARALLELIZATION_ATTRIB, DEFAULT_MAX_INTRA_REGION_PARALLELIZATION);
+ }
+
+ public boolean isUseIndexes() {
+ return config.getBoolean(USE_INDEXES_ATTRIB, DEFAULT_USE_INDEXES);
+ }
+
+ public boolean isImmutableRows() {
+ return config.getBoolean(IMMUTABLE_ROWS_ATTRIB, DEFAULT_IMMUTABLE_ROWS);
+ }
+
+ public boolean isDropMetaData() {
+ return config.getBoolean(DROP_METADATA_ATTRIB, DEFAULT_DROP_METADATA);
+ }
+
+ public boolean isSpillableGroupByEnabled() {
+ return config.getBoolean(GROUPBY_SPILLABLE_ATTRIB, DEFAULT_GROUPBY_SPILLABLE);
+ }
+
+ public long getSpillableGroupByMaxCacheSize() {
+ return config.getLong(GROUPBY_MAX_CACHE_SIZE_ATTRIB, DEFAULT_GROUPBY_MAX_CACHE_MAX);
+ }
+
+ public int getSpillableGroupByNumSpillFiles() {
+ return config.getInt(GROUPBY_SPILL_FILES_ATTRIB, DEFAULT_GROUPBY_SPILL_FILES);
+ }
+
+ public QueryServicesOptions setMaxServerCacheTTLMs(int ttl) {
+ return set(MAX_SERVER_CACHE_TIME_TO_LIVE_MS, ttl);
+ }
+
+ public QueryServicesOptions setMasterInfoPort(int port) {
+ return set(MASTER_INFO_PORT_ATTRIB, port);
+ }
+
+ public QueryServicesOptions setRegionServerInfoPort(int port) {
+ return set(REGIONSERVER_INFO_PORT_ATTRIB, port);
+ }
+
+ public QueryServicesOptions setRegionServerLeasePeriodMs(int period) {
+ return set(REGIONSERVER_LEASE_PERIOD_ATTRIB, period);
+ }
+
+ public QueryServicesOptions setRpcTimeoutMs(int timeout) {
+ return set(RPC_TIMEOUT_ATTRIB, timeout);
+ }
+
+ public QueryServicesOptions setUseIndexes(boolean useIndexes) {
+ return set(USE_INDEXES_ATTRIB, useIndexes);
+ }
+
+ public QueryServicesOptions setImmutableRows(boolean isImmutableRows) {
+ return set(IMMUTABLE_ROWS_ATTRIB, isImmutableRows);
+ }
+
+ public QueryServicesOptions setWALEditCodec(String walEditCodec) {
+ return set(WALEditCodec.WAL_EDIT_CODEC_CLASS_KEY, walEditCodec);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/query/StatsManager.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/StatsManager.java b/phoenix-core/src/main/java/org/apache/phoenix/query/StatsManager.java
new file mode 100644
index 0000000..7d02e02
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/StatsManager.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.query;
+
+import java.sql.SQLException;
+
+import org.apache.phoenix.schema.TableRef;
+
+
+/**
+ *
+ * Interface for managing and caching table statistics.
+ * The frequency of updating the table statistics are controlled
+ * by {@link org.apache.phoenix.query.QueryServices#STATS_UPDATE_FREQ_MS_ATTRIB}.
+ * Table stats may also be manually updated through {@link #updateStats(TableRef)}.
+ *
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public interface StatsManager {
+ /**
+ * Get the minimum key for the given table
+ * @param table the table
+ * @return the minimum key or null if unknown
+ */
+ byte[] getMinKey(TableRef table);
+
+ /**
+ * Get the maximum key for the given table
+ * @param table the table
+ * @return the maximum key or null if unknown
+ */
+ byte[] getMaxKey(TableRef table);
+
+ /**
+ * Manually update the cached table statistics
+ * @param table the table
+ * @throws SQLException
+ */
+ void updateStats(TableRef table) throws SQLException;
+}