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 2017/09/29 01:24:35 UTC

[01/25] phoenix git commit: PHOENIX-4096 Disallow DML operations on connections with CURRENT_SCN set

Repository: phoenix
Updated Branches:
  refs/heads/master 45079c4ea -> dd3b7b6c0


http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
index 79c316a..730f754 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
@@ -124,16 +124,12 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMap.Builder;
 import com.google.common.collect.Lists;
 
-
 /**
  * 
- * JDBC Connection implementation of Phoenix.
- * Currently the following are supported:
- * - Statement
- * - PreparedStatement
- * The connection may only be used with the following options:
- * - ResultSet.TYPE_FORWARD_ONLY
- * - Connection.TRANSACTION_READ_COMMITTED
+ * JDBC Connection implementation of Phoenix. Currently the following are
+ * supported: - Statement - PreparedStatement The connection may only be used
+ * with the following options: - ResultSet.TYPE_FORWARD_ONLY -
+ * Connection.TRANSACTION_READ_COMMITTED
  * 
  * 
  * @since 0.1
@@ -147,14 +143,14 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
     private final int mutateBatchSize;
     private final long mutateBatchSizeBytes;
     private final Long scn;
-    private final boolean replayMutations;
+    private final boolean buildingIndex;
     private MutationState mutationState;
     private List<PhoenixStatement> statements = new ArrayList<>();
     private boolean isAutoFlush = false;
     private boolean isAutoCommit = false;
     private PMetaData metaData;
     private final PName tenantId;
-    private final String datePattern; 
+    private final String datePattern;
     private final String timePattern;
     private final String timestampPattern;
     private int statementExecutionCounter;
@@ -170,7 +166,7 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
     private final LinkedBlockingQueue<WeakReference<TableResultIterator>> scannerQueue;
     private TableResultIteratorFactory tableResultIteratorFactory;
     private boolean isRunningUpgrade;
-    
+
     static {
         Tracing.addTraceMetricsSource();
     }
@@ -181,8 +177,13 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
         return props;
     }
 
-    public PhoenixConnection(PhoenixConnection connection, boolean isDescRowKeyOrderUpgrade, boolean isRunningUpgrade) throws SQLException {
-        this(connection.getQueryServices(), connection.getURL(), connection.getClientInfo(), connection.metaData, connection.getMutationState(), isDescRowKeyOrderUpgrade, isRunningUpgrade, connection.replayMutations);
+    public PhoenixConnection(PhoenixConnection connection,
+            boolean isDescRowKeyOrderUpgrade, boolean isRunningUpgrade)
+                    throws SQLException {
+        this(connection.getQueryServices(), connection.getURL(), connection
+                .getClientInfo(), connection.metaData, connection
+                .getMutationState(), isDescRowKeyOrderUpgrade,
+                isRunningUpgrade, connection.buildingIndex);
         this.isAutoCommit = connection.isAutoCommit;
         this.isAutoFlush = connection.isAutoFlush;
         this.sampler = connection.sampler;
@@ -190,93 +191,140 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
     }
 
     public PhoenixConnection(PhoenixConnection connection) throws SQLException {
-        this(connection, connection.isDescVarLengthRowKeyUpgrade(), connection.isRunningUpgrade());
+        this(connection, connection.isDescVarLengthRowKeyUpgrade(), connection
+                .isRunningUpgrade());
     }
-    
-    public PhoenixConnection(PhoenixConnection connection, MutationState mutationState) throws SQLException {
-        this(connection.getQueryServices(), connection.getURL(), connection.getClientInfo(), connection.getMetaDataCache(), mutationState, connection.isDescVarLengthRowKeyUpgrade(), connection.isRunningUpgrade(), connection.replayMutations);
+
+    public PhoenixConnection(PhoenixConnection connection,
+            MutationState mutationState) throws SQLException {
+        this(connection.getQueryServices(), connection.getURL(), connection
+                .getClientInfo(), connection.getMetaDataCache(), mutationState,
+                connection.isDescVarLengthRowKeyUpgrade(), connection
+                .isRunningUpgrade(), connection.buildingIndex);
     }
-    
-    public PhoenixConnection(PhoenixConnection connection, long scn) throws SQLException {
+
+    public PhoenixConnection(PhoenixConnection connection, long scn)
+            throws SQLException {
         this(connection.getQueryServices(), connection, scn);
     }
-    
-    public PhoenixConnection(ConnectionQueryServices services, PhoenixConnection connection, long scn) throws SQLException {
-        this(services, connection.getURL(), newPropsWithSCN(scn,connection.getClientInfo()), connection.metaData, connection.getMutationState(), connection.isDescVarLengthRowKeyUpgrade(), connection.isRunningUpgrade(), connection.replayMutations);
+
+    public PhoenixConnection(ConnectionQueryServices services,
+            PhoenixConnection connection, long scn) throws SQLException {
+        this(services, connection.getURL(), newPropsWithSCN(scn,
+                connection.getClientInfo()), connection.metaData, connection
+                .getMutationState(), connection.isDescVarLengthRowKeyUpgrade(),
+                connection.isRunningUpgrade(), connection.buildingIndex);
         this.isAutoCommit = connection.isAutoCommit;
         this.isAutoFlush = connection.isAutoFlush;
         this.sampler = connection.sampler;
         this.statementExecutionCounter = connection.statementExecutionCounter;
     }
-    
-    public PhoenixConnection(ConnectionQueryServices services, String url, Properties info, PMetaData metaData) throws SQLException {
+
+    public PhoenixConnection(ConnectionQueryServices services, String url,
+            Properties info, PMetaData metaData) throws SQLException {
         this(services, url, info, metaData, null, false, false, false);
     }
-    
-    public PhoenixConnection(PhoenixConnection connection, ConnectionQueryServices services, Properties info) throws SQLException {
-        this(services, connection.url, info, connection.metaData, null, connection.isDescVarLengthRowKeyUpgrade(), connection.isRunningUpgrade(), connection.replayMutations);
+
+    public PhoenixConnection(PhoenixConnection connection,
+            ConnectionQueryServices services, Properties info)
+                    throws SQLException {
+        this(services, connection.url, info, connection.metaData, null,
+                connection.isDescVarLengthRowKeyUpgrade(), connection
+                .isRunningUpgrade(), connection.buildingIndex);
     }
-    
-    private PhoenixConnection(ConnectionQueryServices services, String url, Properties info, PMetaData metaData, MutationState mutationState, boolean isDescVarLengthRowKeyUpgrade, boolean isRunningUpgrade, boolean replayMutations) throws SQLException {
+
+    private PhoenixConnection(ConnectionQueryServices services, String url,
+            Properties info, PMetaData metaData, MutationState mutationState,
+            boolean isDescVarLengthRowKeyUpgrade, boolean isRunningUpgrade,
+            boolean buildingIndex) throws SQLException {
         GLOBAL_PHOENIX_CONNECTIONS_ATTEMPTED_COUNTER.increment();
         this.url = url;
         this.isDescVarLengthRowKeyUpgrade = isDescVarLengthRowKeyUpgrade;
 
-        // Filter user provided properties based on property policy, if provided.
+        // Filter user provided properties based on property policy, if
+        // provided.
         PropertyPolicyProvider.getPropertyPolicy().evaluate(info);
 
         // Copy so client cannot change
-        this.info = info == null ? new Properties() : PropertiesUtil.deepCopy(info);
+        this.info = info == null ? new Properties() : PropertiesUtil
+                .deepCopy(info);
         final PName tenantId = JDBCUtil.getTenantId(url, info);
         if (this.info.isEmpty() && tenantId == null) {
             this.services = services;
         } else {
-            // Create child services keyed by tenantId to track resource usage for
+            // Create child services keyed by tenantId to track resource usage
+            // for
             // a tenantId for all connections on this JVM.
             if (tenantId != null) {
-                services = services.getChildQueryServices(tenantId.getBytesPtr());
+                services = services.getChildQueryServices(tenantId
+                        .getBytesPtr());
             }
             ReadOnlyProps currentProps = services.getProps();
-            final ReadOnlyProps augmentedProps = currentProps.addAll(filterKnownNonProperties(this.info));
-            this.services = augmentedProps == currentProps ? services : new DelegateConnectionQueryServices(services) {
+            final ReadOnlyProps augmentedProps = currentProps
+                    .addAll(filterKnownNonProperties(this.info));
+            this.services = augmentedProps == currentProps ? services
+                    : new DelegateConnectionQueryServices(services) {
                 @Override
                 public ReadOnlyProps getProps() {
                     return augmentedProps;
                 }
             };
         }
-        
+
         Long scnParam = JDBCUtil.getCurrentSCN(url, this.info);
         checkScn(scnParam);
-        Long replayAtParam = JDBCUtil.getReplayAt(url, this.info);
-        checkReplayAt(replayAtParam);
-        checkScnAndReplayAtEquality(scnParam,replayAtParam);
-        
-        this.scn = scnParam != null ? scnParam : replayAtParam;
-        this.replayMutations = replayMutations || replayAtParam != null;
-        this.isAutoFlush = this.services.getProps().getBoolean(QueryServices.TRANSACTIONS_ENABLED, QueryServicesOptions.DEFAULT_TRANSACTIONS_ENABLED)
-                && this.services.getProps().getBoolean(QueryServices.AUTO_FLUSH_ATTRIB, QueryServicesOptions.DEFAULT_AUTO_FLUSH) ;
+        Long buildIndexAtParam = JDBCUtil.getBuildIndexSCN(url, this.info);
+        checkBuildIndexAt(buildIndexAtParam);
+        checkScnAndBuildIndexAtEquality(scnParam, buildIndexAtParam);
+
+        this.scn = scnParam != null ? scnParam : buildIndexAtParam;
+        this.buildingIndex = buildingIndex || buildIndexAtParam != null;
+        this.isAutoFlush = this.services.getProps().getBoolean(
+                QueryServices.TRANSACTIONS_ENABLED,
+                QueryServicesOptions.DEFAULT_TRANSACTIONS_ENABLED)
+                && this.services.getProps().getBoolean(
+                        QueryServices.AUTO_FLUSH_ATTRIB,
+                        QueryServicesOptions.DEFAULT_AUTO_FLUSH);
         this.isAutoCommit = JDBCUtil.getAutoCommit(
-                url, this.info,
+                url,
+                this.info,
                 this.services.getProps().getBoolean(
                         QueryServices.AUTO_COMMIT_ATTRIB,
                         QueryServicesOptions.DEFAULT_AUTO_COMMIT));
-        this.consistency = JDBCUtil.getConsistencyLevel(url, this.info, this.services.getProps()
-                 .get(QueryServices.CONSISTENCY_ATTRIB,
-                         QueryServicesOptions.DEFAULT_CONSISTENCY_LEVEL));
-        // currently we are not resolving schema set through property, so if schema doesn't exists ,connection will not fail
+        this.consistency = JDBCUtil.getConsistencyLevel(
+                url,
+                this.info,
+                this.services.getProps().get(QueryServices.CONSISTENCY_ATTRIB,
+                        QueryServicesOptions.DEFAULT_CONSISTENCY_LEVEL));
+        // currently we are not resolving schema set through property, so if
+        // schema doesn't exists ,connection will not fail
         // but queries may fail
-        this.schema = JDBCUtil.getSchema(url, this.info,
-                this.services.getProps().get(QueryServices.SCHEMA_ATTRIB, QueryServicesOptions.DEFAULT_SCHEMA));
+        this.schema = JDBCUtil.getSchema(
+                url,
+                this.info,
+                this.services.getProps().get(QueryServices.SCHEMA_ATTRIB,
+                        QueryServicesOptions.DEFAULT_SCHEMA));
         this.tenantId = tenantId;
-        this.mutateBatchSize = JDBCUtil.getMutateBatchSize(url, this.info, this.services.getProps());
-        this.mutateBatchSizeBytes = JDBCUtil.getMutateBatchSizeBytes(url, this.info, this.services.getProps());
-        datePattern = this.services.getProps().get(QueryServices.DATE_FORMAT_ATTRIB, DateUtil.DEFAULT_DATE_FORMAT);
-        timePattern = this.services.getProps().get(QueryServices.TIME_FORMAT_ATTRIB, DateUtil.DEFAULT_TIME_FORMAT);
-        timestampPattern = this.services.getProps().get(QueryServices.TIMESTAMP_FORMAT_ATTRIB, DateUtil.DEFAULT_TIMESTAMP_FORMAT);
-        String numberPattern = this.services.getProps().get(QueryServices.NUMBER_FORMAT_ATTRIB, NumberUtil.DEFAULT_NUMBER_FORMAT);
-        int maxSize = this.services.getProps().getInt(QueryServices.MAX_MUTATION_SIZE_ATTRIB,QueryServicesOptions.DEFAULT_MAX_MUTATION_SIZE);
-        int maxSizeBytes = this.services.getProps().getInt(QueryServices.MAX_MUTATION_SIZE_BYTES_ATTRIB,QueryServicesOptions.DEFAULT_MAX_MUTATION_SIZE_BYTES);
+        this.mutateBatchSize = JDBCUtil.getMutateBatchSize(url, this.info,
+                this.services.getProps());
+        this.mutateBatchSizeBytes = JDBCUtil.getMutateBatchSizeBytes(url,
+                this.info, this.services.getProps());
+        datePattern = this.services.getProps().get(
+                QueryServices.DATE_FORMAT_ATTRIB, DateUtil.DEFAULT_DATE_FORMAT);
+        timePattern = this.services.getProps().get(
+                QueryServices.TIME_FORMAT_ATTRIB, DateUtil.DEFAULT_TIME_FORMAT);
+        timestampPattern = this.services.getProps().get(
+                QueryServices.TIMESTAMP_FORMAT_ATTRIB,
+                DateUtil.DEFAULT_TIMESTAMP_FORMAT);
+        String numberPattern = this.services.getProps().get(
+                QueryServices.NUMBER_FORMAT_ATTRIB,
+                NumberUtil.DEFAULT_NUMBER_FORMAT);
+        int maxSize = this.services.getProps().getInt(
+                QueryServices.MAX_MUTATION_SIZE_ATTRIB,
+                QueryServicesOptions.DEFAULT_MAX_MUTATION_SIZE);
+        int maxSizeBytes = this.services.getProps().getInt(
+                QueryServices.MAX_MUTATION_SIZE_BYTES_ATTRIB,
+                QueryServicesOptions.DEFAULT_MAX_MUTATION_SIZE_BYTES);
         Format dateFormat = DateUtil.getDateFormatter(datePattern);
         Format timeFormat = DateUtil.getDateFormatter(timePattern);
         Format timestampFormat = DateUtil.getDateFormatter(timestampPattern);
@@ -286,28 +334,36 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
         formatters.put(PUnsignedDate.INSTANCE, dateFormat);
         formatters.put(PUnsignedTime.INSTANCE, timeFormat);
         formatters.put(PUnsignedTimestamp.INSTANCE, timestampFormat);
-        formatters.put(PDecimal.INSTANCE, FunctionArgumentType.NUMERIC.getFormatter(numberPattern));
-        // We do not limit the metaData on a connection less than the global one,
+        formatters.put(PDecimal.INSTANCE,
+                FunctionArgumentType.NUMERIC.getFormatter(numberPattern));
+        // We do not limit the metaData on a connection less than the global
+        // one,
         // as there's not much that will be cached here.
         Pruner pruner = new Pruner() {
 
             @Override
             public boolean prune(PTable table) {
-                long maxTimestamp = scn == null ? HConstants.LATEST_TIMESTAMP : scn;
-                return (table.getType() != PTableType.SYSTEM && 
-                        (  table.getTimeStamp() >= maxTimestamp || 
-                         (table.getTenantId()!=null && ! Objects.equal(tenantId, table.getTenantId()))));
+                long maxTimestamp = scn == null ? HConstants.LATEST_TIMESTAMP
+                        : scn;
+                return (table.getType() != PTableType.SYSTEM && (table
+                        .getTimeStamp() >= maxTimestamp || (table.getTenantId() != null && !Objects
+                        .equal(tenantId, table.getTenantId()))));
             }
-            
+
             @Override
             public boolean prune(PFunction function) {
-                long maxTimestamp = scn == null ? HConstants.LATEST_TIMESTAMP : scn;
-                return ( function.getTimeStamp() >= maxTimestamp ||
-                        (function.getTenantId()!=null && ! Objects.equal(tenantId, function.getTenantId())));
+                long maxTimestamp = scn == null ? HConstants.LATEST_TIMESTAMP
+                        : scn;
+                return (function.getTimeStamp() >= maxTimestamp || (function
+                        .getTenantId() != null && !Objects.equal(tenantId,
+                                function.getTenantId())));
             }
         };
-        this.isRequestLevelMetricsEnabled = JDBCUtil.isCollectingRequestLevelMetricsEnabled(url, info, this.services.getProps());
-        this.mutationState = mutationState == null ? newMutationState(maxSize, maxSizeBytes) : new MutationState(mutationState);
+        this.isRequestLevelMetricsEnabled = JDBCUtil
+                .isCollectingRequestLevelMetricsEnabled(url, info,
+                        this.services.getProps());
+        this.mutationState = mutationState == null ? newMutationState(maxSize,
+                maxSizeBytes) : new MutationState(mutationState);
         this.metaData = metaData;
         this.metaData.pruneTables(pruner);
         this.metaData.pruneFunctions(pruner);
@@ -321,22 +377,28 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
         this.isRunningUpgrade = isRunningUpgrade;
         GLOBAL_OPEN_PHOENIX_CONNECTIONS.increment();
     }
-    
+
     private static void checkScn(Long scnParam) throws SQLException {
         if (scnParam != null && scnParam < 0) {
-            throw new SQLExceptionInfo.Builder(SQLExceptionCode.INVALID_SCN).build().buildException();
+            throw new SQLExceptionInfo.Builder(SQLExceptionCode.INVALID_SCN)
+            .build().buildException();
         }
     }
 
-    private static void checkReplayAt(Long replayAtParam) throws SQLException {
+    private static void checkBuildIndexAt(Long replayAtParam) throws SQLException {
         if (replayAtParam != null && replayAtParam < 0) {
-            throw new SQLExceptionInfo.Builder(SQLExceptionCode.INVALID_REPLAY_AT).build().buildException();
+            throw new SQLExceptionInfo.Builder(
+                    SQLExceptionCode.INVALID_REPLAY_AT).build()
+                    .buildException();
         }
     }
 
-    private static void checkScnAndReplayAtEquality(Long scnParam, Long replayAt) throws SQLException {
+    private static void checkScnAndBuildIndexAtEquality(Long scnParam, Long replayAt)
+            throws SQLException {
         if (scnParam != null && replayAt != null && !scnParam.equals(replayAt)) {
-            throw new SQLExceptionInfo.Builder(SQLExceptionCode.UNEQUAL_SCN_AND_REPLAY_AT).build().buildException();
+            throw new SQLExceptionInfo.Builder(
+                    SQLExceptionCode.UNEQUAL_SCN_AND_BUILD_INDEX_AT).build()
+                    .buildException();
         }
     }
 
@@ -354,21 +416,22 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
     }
 
     private ImmutableMap<String, String> getImmutableCustomTracingAnnotations() {
-    	Builder<String, String> result = ImmutableMap.builder();
-    	result.putAll(JDBCUtil.getAnnotations(url, info));
-    	if (getSCN() != null) {
-    		result.put(PhoenixRuntime.CURRENT_SCN_ATTRIB, getSCN().toString());
-    	}
-    	if (getTenantId() != null) {
-    		result.put(PhoenixRuntime.TENANT_ID_ATTRIB, getTenantId().getString());
-    	}
-    	return result.build();
+        Builder<String, String> result = ImmutableMap.builder();
+        result.putAll(JDBCUtil.getAnnotations(url, info));
+        if (getSCN() != null) {
+            result.put(PhoenixRuntime.CURRENT_SCN_ATTRIB, getSCN().toString());
+        }
+        if (getTenantId() != null) {
+            result.put(PhoenixRuntime.TENANT_ID_ATTRIB, getTenantId()
+                    .getString());
+        }
+        return result.build();
     }
 
     public Sampler<?> getSampler() {
         return this.sampler;
     }
-    
+
     public void setSampler(Sampler<?> sampler) throws SQLException {
         this.sampler = sampler;
     }
@@ -377,7 +440,8 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
         return customTracingAnnotations;
     }
 
-    public int executeStatements(Reader reader, List<Object> binds, PrintStream out) throws IOException, SQLException {
+    public int executeStatements(Reader reader, List<Object> binds,
+            PrintStream out) throws IOException, SQLException {
         int bindsOffset = 0;
         int nStatements = 0;
         PhoenixStatementParser parser = new PhoenixStatementParser(reader);
@@ -386,9 +450,10 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
                 PhoenixPreparedStatement stmt = null;
                 try {
                     stmt = new PhoenixPreparedStatement(this, parser);
-                    ParameterMetaData paramMetaData = stmt.getParameterMetaData();
+                    ParameterMetaData paramMetaData = stmt
+                            .getParameterMetaData();
                     for (int i = 0; i < paramMetaData.getParameterCount(); i++) {
-                        stmt.setObject(i+1, binds.get(bindsOffset+i));
+                        stmt.setObject(i + 1, binds.get(bindsOffset + i));
                     }
                     long start = System.currentTimeMillis();
                     boolean isQuery = stmt.execute();
@@ -404,20 +469,30 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
                                 ResultSetMetaData md = rs.getMetaData();
                                 columnCount = md.getColumnCount();
                                 for (int i = 1; i <= columnCount; i++) {
-                                    int displayWidth = md.getColumnDisplaySize(i);
+                                    int displayWidth = md
+                                            .getColumnDisplaySize(i);
                                     String label = md.getColumnLabel(i);
                                     if (md.isSigned(i)) {
-                                        out.print(displayWidth < label.length() ? label.substring(0,displayWidth) : Strings.padStart(label, displayWidth, ' '));
+                                        out.print(displayWidth < label.length() ? label
+                                                .substring(0, displayWidth)
+                                                : Strings.padStart(label,
+                                                        displayWidth, ' '));
                                         out.print(' ');
                                     } else {
-                                        out.print(displayWidth < label.length() ? label.substring(0,displayWidth) : Strings.padEnd(md.getColumnLabel(i), displayWidth, ' '));
+                                        out.print(displayWidth < label.length() ? label
+                                                .substring(0, displayWidth)
+                                                : Strings.padEnd(
+                                                        md.getColumnLabel(i),
+                                                        displayWidth, ' '));
                                         out.print(' ');
                                     }
                                 }
                                 out.println();
                                 for (int i = 1; i <= columnCount; i++) {
-                                    int displayWidth = md.getColumnDisplaySize(i);
-                                    out.print(Strings.padStart("", displayWidth,'-'));
+                                    int displayWidth = md
+                                            .getColumnDisplaySize(i);
+                                    out.print(Strings.padStart("",
+                                            displayWidth, '-'));
                                     out.print(' ');
                                 }
                                 out.println();
@@ -426,13 +501,19 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
                                 if (out != null) {
                                     ResultSetMetaData md = rs.getMetaData();
                                     for (int i = 1; i <= columnCount; i++) {
-                                        int displayWidth = md.getColumnDisplaySize(i);
+                                        int displayWidth = md
+                                                .getColumnDisplaySize(i);
                                         String value = rs.getString(i);
-                                        String valueString = value == null ? QueryConstants.NULL_DISPLAY_TEXT : value;
+                                        String valueString = value == null ? QueryConstants.NULL_DISPLAY_TEXT
+                                                : value;
                                         if (md.isSigned(i)) {
-                                            out.print(Strings.padStart(valueString, displayWidth, ' '));
+                                            out.print(Strings.padStart(
+                                                    valueString, displayWidth,
+                                                    ' '));
                                         } else {
-                                            out.print(Strings.padEnd(valueString, displayWidth, ' '));
+                                            out.print(Strings.padEnd(
+                                                    valueString, displayWidth,
+                                                    ' '));
                                         }
                                         out.print(' ');
                                     }
@@ -440,10 +521,12 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
                                 }
                             } while (rs.next());
                         }
-                    } else if (out != null){
+                    } else if (out != null) {
                         int updateCount = stmt.getUpdateCount();
                         if (updateCount >= 0) {
-                            out.println((updateCount == 0 ? "no" : updateCount) + (updateCount == 1 ? " row " : " rows ") + stmt.getUpdateOperation().toString());
+                            out.println((updateCount == 0 ? "no" : updateCount)
+                                    + (updateCount == 1 ? " row " : " rows ")
+                                    + stmt.getUpdateOperation().toString());
                         }
                     }
                     bindsOffset += paramMetaData.getParameterCount();
@@ -464,59 +547,59 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
     public @Nullable PName getTenantId() {
         return tenantId;
     }
-    
+
     public Long getSCN() {
         return scn;
     }
-    
-    public boolean isReplayMutations() {
-        return replayMutations;
+
+    public boolean isBuildingIndex() {
+        return buildingIndex;
     }
-    
+
     public int getMutateBatchSize() {
         return mutateBatchSize;
     }
 
-    public long getMutateBatchSizeBytes(){
+    public long getMutateBatchSizeBytes() {
         return mutateBatchSizeBytes;
     }
 
     public PMetaData getMetaDataCache() {
         return metaData;
     }
-    
+
     public PTable getTable(PTableKey key) throws TableNotFoundException {
-    	return metaData.getTableRef(key).getTable();
+        return metaData.getTableRef(key).getTable();
     }
-    
+
     public PTableRef getTableRef(PTableKey key) throws TableNotFoundException {
-    	return metaData.getTableRef(key);
+        return metaData.getTableRef(key);
     }
 
     protected MutationState newMutationState(int maxSize, int maxSizeBytes) {
         return new MutationState(maxSize, maxSizeBytes, this);
     }
-    
+
     public MutationState getMutationState() {
         return mutationState;
     }
-    
+
     public String getDatePattern() {
         return datePattern;
     }
-    
+
     public Format getFormatter(PDataType type) {
         return formatters.get(type);
     }
-    
+
     public String getURL() {
         return url;
     }
-    
+
     public ConnectionQueryServices getQueryServices() {
         return services;
     }
-    
+
     @Override
     public void clearWarnings() throws SQLException {
     }
@@ -538,13 +621,15 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
             }
         }
     }
-    
+
     private void checkOpen() throws SQLException {
         if (isClosed) {
-            throw new SQLExceptionInfo.Builder(SQLExceptionCode.CONNECTION_CLOSED).build().buildException();
+            throw new SQLExceptionInfo.Builder(
+                    SQLExceptionCode.CONNECTION_CLOSED).build()
+                    .buildException();
         }
     }
-    
+
     @Override
     public void close() throws SQLException {
         if (isClosed) {
@@ -580,10 +665,12 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
     }
 
     @Override
-    public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
+    public Array createArrayOf(String typeName, Object[] elements)
+            throws SQLException {
         checkOpen();
-    	PDataType arrayPrimitiveType = PDataType.fromSqlTypeName(typeName);
-    	return PArrayDataType.instantiatePhoenixArray(arrayPrimitiveType, elements);
+        PDataType arrayPrimitiveType = PDataType.fromSqlTypeName(typeName);
+        return PArrayDataType.instantiatePhoenixArray(arrayPrimitiveType,
+                elements);
     }
 
     @Override
@@ -609,7 +696,7 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
     public List<PhoenixStatement> getStatements() {
         return statements;
     }
-    
+
     @Override
     public Statement createStatement() throws SQLException {
         checkOpen();
@@ -620,28 +707,33 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
 
     /**
      * Back-door way to inject processing into walking through a result set
+     * 
      * @param statementFactory
      * @return PhoenixStatement
      * @throws SQLException
      */
-    public PhoenixStatement createStatement(PhoenixStatementFactory statementFactory) throws SQLException {
+    public PhoenixStatement createStatement(
+            PhoenixStatementFactory statementFactory) throws SQLException {
         PhoenixStatement statement = statementFactory.newStatement(this);
         statements.add(statement);
         return statement;
     }
 
     @Override
-    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
+    public Statement createStatement(int resultSetType, int resultSetConcurrency)
+            throws SQLException {
         checkOpen();
-        if (resultSetType != ResultSet.TYPE_FORWARD_ONLY || resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) {
+        if (resultSetType != ResultSet.TYPE_FORWARD_ONLY
+                || resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) {
             throw new SQLFeatureNotSupportedException();
         }
         return createStatement();
     }
 
     @Override
-    public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
-            throws SQLException {
+    public Statement createStatement(int resultSetType,
+            int resultSetConcurrency, int resultSetHoldability)
+                    throws SQLException {
         checkOpen();
         if (resultSetHoldability != ResultSet.CLOSE_CURSORS_AT_COMMIT) {
             throw new SQLFeatureNotSupportedException();
@@ -650,7 +742,8 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
     }
 
     @Override
-    public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
+    public Struct createStruct(String typeName, Object[] attributes)
+            throws SQLException {
         throw new SQLFeatureNotSupportedException();
     }
 
@@ -664,8 +757,12 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
     }
 
     public void setAutoFlush(boolean autoFlush) throws SQLException {
-        if (autoFlush && !this.services.getProps().getBoolean(QueryServices.TRANSACTIONS_ENABLED, QueryServicesOptions.DEFAULT_TRANSACTIONS_ENABLED)) {
-            throw new SQLExceptionInfo.Builder(SQLExceptionCode.TX_MUST_BE_ENABLED_TO_SET_AUTO_FLUSH)
+        if (autoFlush
+                && !this.services.getProps().getBoolean(
+                        QueryServices.TRANSACTIONS_ENABLED,
+                        QueryServicesOptions.DEFAULT_TRANSACTIONS_ENABLED)) {
+            throw new SQLExceptionInfo.Builder(
+                    SQLExceptionCode.TX_MUST_BE_ENABLED_TO_SET_AUTO_FLUSH)
             .build().buildException();
         }
         this.isAutoFlush = autoFlush;
@@ -674,20 +771,26 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
     public void flush() throws SQLException {
         mutationState.sendUncommitted();
     }
-        
-    public void setTransactionContext(PhoenixTransactionContext txContext) throws SQLException {
-        if (!this.services.getProps().getBoolean(QueryServices.TRANSACTIONS_ENABLED, QueryServicesOptions.DEFAULT_TRANSACTIONS_ENABLED)) {
-            throw new SQLExceptionInfo.Builder(SQLExceptionCode.TX_MUST_BE_ENABLED_TO_SET_TX_CONTEXT)
+
+    public void setTransactionContext(PhoenixTransactionContext txContext)
+            throws SQLException {
+        if (!this.services.getProps().getBoolean(
+                QueryServices.TRANSACTIONS_ENABLED,
+                QueryServicesOptions.DEFAULT_TRANSACTIONS_ENABLED)) {
+            throw new SQLExceptionInfo.Builder(
+                    SQLExceptionCode.TX_MUST_BE_ENABLED_TO_SET_TX_CONTEXT)
             .build().buildException();
         }
         this.mutationState.rollback();
-        this.mutationState = new MutationState(this.mutationState.getMaxSize(), this.mutationState.getMaxSizeBytes(), this, txContext);
-        
-        // Write data to HBase after each statement execution as the commit may not
+        this.mutationState = new MutationState(this.mutationState.getMaxSize(),
+                this.mutationState.getMaxSizeBytes(), this, txContext);
+
+        // Write data to HBase after each statement execution as the commit may
+        // not
         // come through Phoenix APIs.
         setAutoFlush(true);
     }
-    
+
     public Consistency getConsistency() {
         return this.consistency;
     }
@@ -698,7 +801,7 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
     }
 
     @Override
-    public Properties getClientInfo() throws SQLException { 
+    public Properties getClientInfo() throws SQLException {
         // Defensive copy so client cannot change
         return new Properties(info);
     }
@@ -721,10 +824,11 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
 
     @Override
     public int getTransactionIsolation() throws SQLException {
-        boolean transactionsEnabled = getQueryServices().getProps().getBoolean(QueryServices.TRANSACTIONS_ENABLED, 
+        boolean transactionsEnabled = getQueryServices().getProps().getBoolean(
+                QueryServices.TRANSACTIONS_ENABLED,
                 QueryServicesOptions.DEFAULT_TRANSACTIONS_ENABLED);
-        return  transactionsEnabled ?
-                Connection.TRANSACTION_REPEATABLE_READ : Connection.TRANSACTION_READ_COMMITTED;
+        return transactionsEnabled ? Connection.TRANSACTION_REPEATABLE_READ
+                : Connection.TRANSACTION_READ_COMMITTED;
     }
 
     @Override
@@ -744,7 +848,7 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
 
     @Override
     public boolean isReadOnly() throws SQLException {
-        return readOnly; 
+        return readOnly || (scn != null && !buildingIndex && !isRunningUpgrade);
     }
 
     @Override
@@ -764,58 +868,66 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
     }
 
     @Override
-    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
+    public CallableStatement prepareCall(String sql, int resultSetType,
+            int resultSetConcurrency) throws SQLException {
         throw new SQLFeatureNotSupportedException();
     }
 
     @Override
-    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
-            int resultSetHoldability) throws SQLException {
+    public CallableStatement prepareCall(String sql, int resultSetType,
+            int resultSetConcurrency, int resultSetHoldability)
+                    throws SQLException {
         throw new SQLFeatureNotSupportedException();
     }
 
     @Override
     public PreparedStatement prepareStatement(String sql) throws SQLException {
         checkOpen();
-        PhoenixPreparedStatement statement = new PhoenixPreparedStatement(this, sql);
+        PhoenixPreparedStatement statement = new PhoenixPreparedStatement(this,
+                sql);
         statements.add(statement);
         return statement;
     }
 
     @Override
-    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
+    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
+            throws SQLException {
         checkOpen();
         // Ignore autoGeneratedKeys, and just execute the statement.
         return prepareStatement(sql);
     }
 
     @Override
-    public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
+    public PreparedStatement prepareStatement(String sql, int[] columnIndexes)
+            throws SQLException {
         checkOpen();
         // Ignore columnIndexes, and just execute the statement.
         return prepareStatement(sql);
     }
 
     @Override
-    public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
+    public PreparedStatement prepareStatement(String sql, String[] columnNames)
+            throws SQLException {
         checkOpen();
         // Ignore columnNames, and just execute the statement.
         return prepareStatement(sql);
     }
 
     @Override
-    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
-            throws SQLException {
+    public PreparedStatement prepareStatement(String sql, int resultSetType,
+            int resultSetConcurrency) throws SQLException {
         checkOpen();
-        if (resultSetType != ResultSet.TYPE_FORWARD_ONLY || resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) {
+        if (resultSetType != ResultSet.TYPE_FORWARD_ONLY
+                || resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) {
             throw new SQLFeatureNotSupportedException();
         }
         return prepareStatement(sql);
     }
 
     @Override
-    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
-            int resultSetHoldability) throws SQLException {
+    public PreparedStatement prepareStatement(String sql, int resultSetType,
+            int resultSetConcurrency, int resultSetHoldability)
+                    throws SQLException {
         checkOpen();
         if (resultSetHoldability != ResultSet.CLOSE_CURSORS_AT_COMMIT) {
             throw new SQLFeatureNotSupportedException();
@@ -864,20 +976,22 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
             throw new SQLFeatureNotSupportedException();
         }
         // TODO:
-//        if (catalog == null) {
-//            tenantId = null;
-//        } else {
-//            tenantId = PNameFactory.newName(catalog);
-//        }
+        // if (catalog == null) {
+        // tenantId = null;
+        // } else {
+        // tenantId = PNameFactory.newName(catalog);
+        // }
     }
 
     @Override
-    public void setClientInfo(Properties properties) throws SQLClientInfoException {
+    public void setClientInfo(Properties properties)
+            throws SQLClientInfoException {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public void setClientInfo(String name, String value) throws SQLClientInfoException {
+    public void setClientInfo(String name, String value)
+            throws SQLClientInfoException {
         throw new UnsupportedOperationException();
     }
 
@@ -889,7 +1003,7 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
     @Override
     public void setReadOnly(boolean readOnly) throws SQLException {
         checkOpen();
-        this.readOnly=readOnly;
+        this.readOnly = readOnly;
     }
 
     @Override
@@ -905,13 +1019,16 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
     @Override
     public void setTransactionIsolation(int level) throws SQLException {
         checkOpen();
-        boolean transactionsEnabled = getQueryServices().getProps().getBoolean(QueryServices.TRANSACTIONS_ENABLED, 
+        boolean transactionsEnabled = getQueryServices().getProps().getBoolean(
+                QueryServices.TRANSACTIONS_ENABLED,
                 QueryServicesOptions.DEFAULT_TRANSACTIONS_ENABLED);
         if (level == Connection.TRANSACTION_SERIALIZABLE) {
             throw new SQLFeatureNotSupportedException();
         }
-        if (!transactionsEnabled && level == Connection.TRANSACTION_REPEATABLE_READ) {
-            throw new SQLExceptionInfo.Builder(SQLExceptionCode.TX_MUST_BE_ENABLED_TO_SET_ISOLATION_LEVEL)
+        if (!transactionsEnabled
+                && level == Connection.TRANSACTION_REPEATABLE_READ) {
+            throw new SQLExceptionInfo.Builder(
+                    SQLExceptionCode.TX_MUST_BE_ENABLED_TO_SET_ISOLATION_LEVEL)
             .build().buildException();
         }
     }
@@ -930,11 +1047,14 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
     @Override
     public <T> T unwrap(Class<T> iface) throws SQLException {
         if (!iface.isInstance(this)) {
-            throw new SQLExceptionInfo.Builder(SQLExceptionCode.CLASS_NOT_UNWRAPPABLE)
-                .setMessage(this.getClass().getName() + " not unwrappable from " + iface.getName())
-                .build().buildException();
+            throw new SQLExceptionInfo.Builder(
+                    SQLExceptionCode.CLASS_NOT_UNWRAPPABLE)
+            .setMessage(
+                    this.getClass().getName()
+                    + " not unwrappable from "
+                    + iface.getName()).build().buildException();
         }
-        return (T)this;
+        return (T) this;
     }
 
     @Override
@@ -958,7 +1078,8 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
     }
 
     @Override
-    public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
+    public void setNetworkTimeout(Executor executor, int milliseconds)
+            throws SQLException {
         checkOpen();
     }
 
@@ -967,21 +1088,22 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
         // TODO Auto-generated method stub
         return 0;
     }
-    
+
     @Override
     public void addTable(PTable table, long resolvedTime) throws SQLException {
         metaData.addTable(table, resolvedTime);
-        //Cascade through to connectionQueryServices too
+        // Cascade through to connectionQueryServices too
         getQueryServices().addTable(table, resolvedTime);
     }
-    
+
     @Override
-    public void updateResolvedTimestamp(PTable table, long resolvedTime) throws SQLException {
-    	metaData.updateResolvedTimestamp(table, resolvedTime);
-    	//Cascade through to connectionQueryServices too
+    public void updateResolvedTimestamp(PTable table, long resolvedTime)
+            throws SQLException {
+        metaData.updateResolvedTimestamp(table, resolvedTime);
+        // Cascade through to connectionQueryServices too
         getQueryServices().updateResolvedTimestamp(table, resolvedTime);
     }
-    
+
     @Override
     public void addFunction(PFunction function) throws SQLException {
         // TODO: since a connection is only used by one thread at a time,
@@ -989,7 +1111,7 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
         if (scn == null || scn > function.getTimeStamp()) {
             metaData.addFunction(function);
         }
-        //Cascade through to connectionQueryServices too
+        // Cascade through to connectionQueryServices too
         getQueryServices().addFunction(function);
     }
 
@@ -1001,46 +1123,57 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
     }
 
     @Override
-    public void removeTable(PName tenantId, String tableName, String parentTableName, long tableTimeStamp) throws SQLException {
-        metaData.removeTable(tenantId, tableName, parentTableName, tableTimeStamp);
-        //Cascade through to connectionQueryServices too
-        getQueryServices().removeTable(tenantId, tableName, parentTableName, tableTimeStamp);
+    public void removeTable(PName tenantId, String tableName,
+            String parentTableName, long tableTimeStamp) throws SQLException {
+        metaData.removeTable(tenantId, tableName, parentTableName,
+                tableTimeStamp);
+        // Cascade through to connectionQueryServices too
+        getQueryServices().removeTable(tenantId, tableName, parentTableName,
+                tableTimeStamp);
     }
 
     @Override
-    public void removeFunction(PName tenantId, String functionName, long tableTimeStamp) throws SQLException {
+    public void removeFunction(PName tenantId, String functionName,
+            long tableTimeStamp) throws SQLException {
         metaData.removeFunction(tenantId, functionName, tableTimeStamp);
-        //Cascade through to connectionQueryServices too
-        getQueryServices().removeFunction(tenantId, functionName, tableTimeStamp);
+        // Cascade through to connectionQueryServices too
+        getQueryServices().removeFunction(tenantId, functionName,
+                tableTimeStamp);
     }
 
     @Override
-    public void removeColumn(PName tenantId, String tableName, List<PColumn> columnsToRemove, long tableTimeStamp,
+    public void removeColumn(PName tenantId, String tableName,
+            List<PColumn> columnsToRemove, long tableTimeStamp,
             long tableSeqNum, long resolvedTime) throws SQLException {
-        metaData.removeColumn(tenantId, tableName, columnsToRemove, tableTimeStamp, tableSeqNum, resolvedTime);
-        //Cascade through to connectionQueryServices too
-        getQueryServices().removeColumn(tenantId, tableName, columnsToRemove, tableTimeStamp, tableSeqNum, resolvedTime);
+        metaData.removeColumn(tenantId, tableName, columnsToRemove,
+                tableTimeStamp, tableSeqNum, resolvedTime);
+        // Cascade through to connectionQueryServices too
+        getQueryServices().removeColumn(tenantId, tableName, columnsToRemove,
+                tableTimeStamp, tableSeqNum, resolvedTime);
     }
 
-    protected boolean removeStatement(PhoenixStatement statement) throws SQLException {
+    protected boolean removeStatement(PhoenixStatement statement)
+            throws SQLException {
         return statements.remove(statement);
-   }
+    }
 
     public KeyValueBuilder getKeyValueBuilder() {
         return this.services.getKeyValueBuilder();
     }
-    
+
     /**
-     * Used to track executions of {@link Statement}s and {@link PreparedStatement}s that were created from this connection before
-     * commit or rollback. 0-based. Used to associate partial save errors with SQL statements
-     * invoked by users.
+     * Used to track executions of {@link Statement}s and
+     * {@link PreparedStatement}s that were created from this connection before
+     * commit or rollback. 0-based. Used to associate partial save errors with
+     * SQL statements invoked by users.
+     * 
      * @see CommitException
      * @see #incrementStatementExecutionCounter()
      */
     public int getStatementExecutionCounter() {
-		return statementExecutionCounter;
-	}
-    
+        return statementExecutionCounter;
+    }
+
     public void incrementStatementExecutionCounter() {
         statementExecutionCounter++;
     }
@@ -1052,19 +1185,21 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
     public void setTraceScope(TraceScope traceScope) {
         this.traceScope = traceScope;
     }
-    
+
     public Map<String, Map<MetricType, Long>> getMutationMetrics() {
         return mutationState.getMutationMetricQueue().aggregate();
     }
-    
+
     public Map<String, Map<MetricType, Long>> getReadMetrics() {
-        return mutationState.getReadMetricQueue() != null ? mutationState.getReadMetricQueue().aggregate() : Collections.<String, Map<MetricType, Long>>emptyMap();
+        return mutationState.getReadMetricQueue() != null ? mutationState
+                .getReadMetricQueue().aggregate() : Collections
+                .<String, Map<MetricType, Long>> emptyMap();
     }
-    
+
     public boolean isRequestLevelMetricsEnabled() {
         return isRequestLevelMetricsEnabled;
     }
-    
+
     public void clearMetrics() {
         mutationState.getMutationMetricQueue().clearMetrics();
         if (mutationState.getReadMetricQueue() != null) {
@@ -1073,28 +1208,30 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
     }
 
     /**
-     * Returns true if this connection is being used to upgrade the
-     * data due to PHOENIX-2067 and false otherwise.
+     * Returns true if this connection is being used to upgrade the data due to
+     * PHOENIX-2067 and false otherwise.
+     * 
      * @return
      */
     public boolean isDescVarLengthRowKeyUpgrade() {
         return isDescVarLengthRowKeyUpgrade;
     }
-    
+
     /**
      * Added for tests only. Do not use this elsewhere.
      */
     public ParallelIteratorFactory getIteratorFactory() {
         return parallelIteratorFactory;
     }
-    
+
     /**
      * Added for testing purposes. Do not use this elsewhere.
      */
-    public void setIteratorFactory(ParallelIteratorFactory parallelIteratorFactory) {
+    public void setIteratorFactory(
+            ParallelIteratorFactory parallelIteratorFactory) {
         this.parallelIteratorFactory = parallelIteratorFactory;
     }
-    
+
     public void addIteratorForLeaseRenewal(@Nonnull TableResultIterator itr) {
         if (services.supportsFeature(Feature.RENEW_LEASE)) {
             checkNotNull(itr);
@@ -1125,13 +1262,13 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea
         getQueryServices().removeSchema(schema, schemaTimeStamp);
 
     }
-    
+
     public boolean isRunningUpgrade() {
         return isRunningUpgrade;
     }
-    
+
     public void setRunningUpgrade(boolean isRunningUpgrade) {
         this.isRunningUpgrade = isRunningUpgrade;
     }
-    
+
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/PhoenixInputFormat.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/PhoenixInputFormat.java b/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/PhoenixInputFormat.java
index 25729d6..2871809 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/PhoenixInputFormat.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/PhoenixInputFormat.java
@@ -30,7 +30,9 @@ import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.HRegionLocation;
 import org.apache.hadoop.hbase.TableName;
-import org.apache.hadoop.hbase.client.*;
+import org.apache.hadoop.hbase.client.ConnectionFactory;
+import org.apache.hadoop.hbase.client.RegionLocator;
+import org.apache.hadoop.hbase.client.Scan;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.hbase.util.RegionSizeCalculator;
 import org.apache.hadoop.io.NullWritable;
@@ -46,7 +48,6 @@ import org.apache.phoenix.iterate.MapReduceParallelScanGrouper;
 import org.apache.phoenix.jdbc.PhoenixStatement;
 import org.apache.phoenix.mapreduce.util.ConnectionUtil;
 import org.apache.phoenix.mapreduce.util.PhoenixConfigurationUtil;
-import org.apache.phoenix.mapreduce.util.PhoenixMapReduceUtil;
 import org.apache.phoenix.query.KeyRange;
 import org.apache.phoenix.util.PhoenixRuntime;
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/index/PhoenixIndexImportDirectMapper.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/index/PhoenixIndexImportDirectMapper.java b/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/index/PhoenixIndexImportDirectMapper.java
index c1db27c..eb4bc0e 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/index/PhoenixIndexImportDirectMapper.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/index/PhoenixIndexImportDirectMapper.java
@@ -84,7 +84,7 @@ public class PhoenixIndexImportDirectMapper extends
             String scn = configuration.get(PhoenixConfigurationUtil.CURRENT_SCN_VALUE);
             String txScnValue = configuration.get(PhoenixConfigurationUtil.TX_SCN_VALUE);
             if(txScnValue==null) {
-                overrideProps.put(PhoenixRuntime.CURRENT_SCN_ATTRIB, scn);
+                overrideProps.put(PhoenixRuntime.BUILD_INDEX_AT_ATTRIB, scn);
             }
             connection = ConnectionUtil.getOutputConnection(configuration, overrideProps);
             connection.setAutoCommit(false);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/index/PhoenixIndexImportMapper.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/index/PhoenixIndexImportMapper.java b/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/index/PhoenixIndexImportMapper.java
index 7551527..9e0d629 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/index/PhoenixIndexImportMapper.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/index/PhoenixIndexImportMapper.java
@@ -75,7 +75,7 @@ public class PhoenixIndexImportMapper extends Mapper<NullWritable, PhoenixIndexD
             String scn = configuration.get(PhoenixConfigurationUtil.CURRENT_SCN_VALUE);
             String txScnValue = configuration.get(PhoenixConfigurationUtil.TX_SCN_VALUE);
             if(txScnValue==null) {
-                overrideProps.put(PhoenixRuntime.CURRENT_SCN_ATTRIB, scn);
+                overrideProps.put(PhoenixRuntime.BUILD_INDEX_AT_ATTRIB, scn);
             }
             connection = ConnectionUtil.getOutputConnection(configuration,overrideProps);
             connection.setAutoCommit(false);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/index/PhoenixIndexPartialBuildMapper.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/index/PhoenixIndexPartialBuildMapper.java b/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/index/PhoenixIndexPartialBuildMapper.java
index 0ead358..5b85da5 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/index/PhoenixIndexPartialBuildMapper.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/index/PhoenixIndexPartialBuildMapper.java
@@ -80,7 +80,7 @@ public class PhoenixIndexPartialBuildMapper extends TableMapper<ImmutableBytesWr
             String scn = configuration.get(PhoenixConfigurationUtil.CURRENT_SCN_VALUE);
             String txScnValue = configuration.get(PhoenixConfigurationUtil.TX_SCN_VALUE);
             if(txScnValue==null && scn!=null) {
-                overrideProps.put(PhoenixRuntime.CURRENT_SCN_ATTRIB, scn);
+                overrideProps.put(PhoenixRuntime.BUILD_INDEX_AT_ATTRIB, scn);
             }
             connection = ConnectionUtil.getOutputConnection(configuration, overrideProps).unwrap(PhoenixConnection.class);
             connection.setAutoCommit(false);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
index 5ba134c..1a1e571 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
@@ -2412,6 +2412,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                             try (PhoenixConnection metaConnection = new PhoenixConnection(ConnectionQueryServicesImpl.this, globalUrl,
                                     scnProps, newEmptyMetaData())) {
                                 try {
+                                	metaConnection.setRunningUpgrade(true);
                                     metaConnection.createStatement().executeUpdate(QueryConstants.CREATE_TABLE_METADATA);
                                 } catch (NewerTableAlreadyExistsException ignore) {
                                     // Ignore, as this will happen if the SYSTEM.CATALOG already exists at this fixed

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
index ec05b24..f15e0b1 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
@@ -294,6 +294,7 @@ public class ConnectionlessQueryServicesImpl extends DelegateQueryServices imple
                 scnProps.remove(PhoenixRuntime.TENANT_ID_ATTRIB);
                 String globalUrl = JDBCUtil.removeProperty(url, PhoenixRuntime.TENANT_ID_ATTRIB);
                 metaConnection = new PhoenixConnection(this, globalUrl, scnProps, newEmptyMetaData());
+                metaConnection.setRunningUpgrade(true);
                 try {
                     metaConnection.createStatement().executeUpdate(QueryConstants.CREATE_TABLE_METADATA);
                 } catch (TableAlreadyExistsException ignore) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/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 557a149..ed1b3b2 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
@@ -105,7 +105,6 @@ import static org.apache.phoenix.schema.types.PDataType.FALSE_BYTES;
 import static org.apache.phoenix.schema.types.PDataType.TRUE_BYTES;
 
 import java.io.IOException;
-import java.sql.Connection;
 import java.sql.Date;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
@@ -1210,7 +1209,7 @@ public class MetaDataClient {
         // 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.
         Properties props = new Properties(connection.getClientInfo());
-        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(connection.getSCN()+1));
+        props.setProperty(PhoenixRuntime.BUILD_INDEX_AT_ATTRIB, Long.toString(connection.getSCN()+1));
         PhoenixConnection conn = new PhoenixConnection(connection, connection.getQueryServices(), props);
         MetaDataClient newClientAtNextTimeStamp = new MetaDataClient(conn);
 
@@ -2894,13 +2893,9 @@ public class MetaDataClient {
     }
 
     private void deleteFromStatsTable(List<TableRef> tableRefs, long ts) throws SQLException {
-        Properties props = new Properties(connection.getClientInfo());
-        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts));
-        Connection conn = new PhoenixConnection(connection.getQueryServices(), connection, ts);
-        conn.setAutoCommit(true);
-        boolean success = false;
-        SQLException sqlException = null;
-        try {
+    	boolean isAutoCommit = connection.getAutoCommit();
+    	try {
+	        connection.setAutoCommit(true);
             StringBuilder buf = new StringBuilder("DELETE FROM SYSTEM.STATS WHERE PHYSICAL_NAME IN (");
             for (TableRef ref : tableRefs) {
                 buf.append("'" + ref.getTable().getPhysicalName().getString() + "',");
@@ -2917,25 +2912,9 @@ public class MetaDataClient {
                 }
                 buf.setCharAt(buf.length() - 1, ')');
             }
-            conn.createStatement().execute(buf.toString());
-            success = true;
-        } catch (SQLException e) {
-            sqlException = e;
+            connection.createStatement().execute(buf.toString());
         } finally {
-            try {
-                conn.close();
-            } catch (SQLException e) {
-                if (sqlException == null) {
-                    // If we're not in the middle of throwing another exception
-                    // then throw the exception we got on close.
-                    if (success) {
-                        sqlException = e;
-                    }
-                } else {
-                    sqlException.setNextException(e);
-                }
-            }
-            if (sqlException != null) { throw sqlException; }
+        	connection.setAutoCommit(isAutoCommit);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/main/java/org/apache/phoenix/util/JDBCUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/JDBCUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/JDBCUtil.java
index 7715705..e4f490a 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/JDBCUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/JDBCUtil.java
@@ -133,8 +133,8 @@ public class JDBCUtil {
         return (scnStr == null ? null : Long.parseLong(scnStr));
     }
 
-    public static Long getReplayAt(String url, Properties info) throws SQLException {
-        String scnStr = findProperty(url, info, PhoenixRuntime.REPLAY_AT_ATTRIB);
+    public static Long getBuildIndexSCN(String url, Properties info) throws SQLException {
+        String scnStr = findProperty(url, info, PhoenixRuntime.BUILD_INDEX_AT_ATTRIB);
         return (scnStr == null ? null : Long.parseLong(scnStr));
     }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixRuntime.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixRuntime.java b/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixRuntime.java
index 59b3bb2..16ef206 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixRuntime.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixRuntime.java
@@ -96,7 +96,6 @@ import com.google.common.base.Splitter;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Maps.EntryTransformer;
 
 /**
  *
@@ -123,26 +122,18 @@ public class PhoenixRuntime {
 
     /**
      * Use this connection property to control HBase timestamps
-     * by specifying your own long timestamp value at connection time. All
-     * queries will use this as the upper bound of the time range for scans
-     * and DDL, and DML will use this as t he timestamp for key values.
+     * by specifying your own long timestamp value at connection time.
+     * Specifying this property will force the connection to be read
+     * only - no DML or DDL will be allowed.
      */
     public static final String CURRENT_SCN_ATTRIB = "CurrentSCN";
 
     /**
-     * Use this connection property to set the long time stamp value at
-     * which to replay DML statements after a write failure. The time
-     * stamp value must match the value returned by 
-     * {@link org.apache.phoenix.execute.CommitException#getServerTimestamp()}
-     * when the exception occurred. Used in conjunction with the 
-     * {@link org.apache.phoenix.hbase.index.write.LeaveIndexActiveFailurePolicy}
-     * index write failure policy to provide a means of the client replaying
-     * updates to ensure that secondary indexes are correctly caught up
-     * with any data updates when a write failure occurs. The updates
-     * should be replayed in ascending time stamp order.
+     * Internal connection property to force an index to be built at a
+     * given time stamp.
      */
-    public static final String REPLAY_AT_ATTRIB = "ReplayAt";
-
+    public static final String BUILD_INDEX_AT_ATTRIB = "BuildIndexAt";
+    
     /**
      * Use this connection property to help with fairness of resource allocation
      * for the client and server. The value of the attribute determines the

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
index 3f09a54..ca4be2f 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
@@ -82,6 +82,7 @@ import org.apache.phoenix.schema.types.PDecimal;
 import org.apache.phoenix.schema.types.PInteger;
 import org.apache.phoenix.schema.types.PVarchar;
 import org.apache.phoenix.util.ByteUtil;
+import org.apache.phoenix.util.EnvironmentEdgeManager;
 import org.apache.phoenix.util.PhoenixRuntime;
 import org.apache.phoenix.util.PropertiesUtil;
 import org.apache.phoenix.util.QueryUtil;
@@ -2257,18 +2258,20 @@ public class QueryCompilerTest extends BaseConnectionlessQueryTest {
     }
     
     @Test
-    public void testQueryWithSCN() throws Exception {
+    public void testDMLOfNonIndexWithBuildIndexAt() throws Exception {
         Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        props.put(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(1000));
-        props.put(QueryServices.TRANSACTIONS_ENABLED, Boolean.TRUE.toString());
+        try (Connection conn = DriverManager.getConnection(getUrl(), props);) {
+            conn.createStatement().execute(
+                    "CREATE TABLE t (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR)");
+        }
+        props.put(PhoenixRuntime.BUILD_INDEX_AT_ATTRIB, Long.toString(EnvironmentEdgeManager.currentTimeMillis()+1));
         try (Connection conn = DriverManager.getConnection(getUrl(), props);) {
             try {
-                conn.createStatement().execute(
-                                "CREATE TABLE t (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR) TRANSACTIONAL=true");
+            	conn.createStatement().execute("UPSERT INTO T (k,v1) SELECT k,v1 FROM T");
                 fail();
             } catch (SQLException e) {
                 assertEquals("Unexpected Exception",
-                        SQLExceptionCode.CANNOT_START_TRANSACTION_WITH_SCN_SET
+                        SQLExceptionCode.ONLY_INDEX_UPDATABLE_AT_SCN
                                 .getErrorCode(), e.getErrorCode());
             }
         }
@@ -2778,21 +2781,6 @@ public class QueryCompilerTest extends BaseConnectionlessQueryTest {
     }
     
     @Test
-    public void testSCNInOnDupKey() throws Exception {
-        String url = getUrl() + ";" + PhoenixRuntime.CURRENT_SCN_ATTRIB + "=100";
-        Connection conn = DriverManager.getConnection(url);
-        conn.createStatement().execute("CREATE TABLE t1 (k1 integer not null, k2 integer not null, v bigint, constraint pk primary key (k1,k2))");
-        try {
-            conn.createStatement().execute("UPSERT INTO t1 VALUES(0,0) ON DUPLICATE KEY UPDATE v = v + 1");
-            fail();
-        } catch (SQLException e) {
-            assertEquals(SQLExceptionCode.CANNOT_SET_SCN_IN_ON_DUP_KEY.getErrorCode(), e.getErrorCode());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
     public void testOrderPreservingGroupBy() throws Exception {
         try (Connection conn= DriverManager.getConnection(getUrl())) {
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java b/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java
index 8b93b5c..1fe21d2 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java
@@ -642,13 +642,7 @@ public class TestUtil {
         conn.createStatement().execute(query);
     }
     
-    public static void analyzeTable(String url, long ts, String tableName) throws IOException, SQLException {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts));
-        analyzeTable(url, props, tableName);
-    }
-
-    public static void analyzeTable(String url, Properties props, String tableName) throws IOException, SQLException {
+   public static void analyzeTable(String url, Properties props, String tableName) throws IOException, SQLException {
         Connection conn = DriverManager.getConnection(url, props);
         analyzeTable(conn, tableName);
         conn.close();


[21/25] phoenix git commit: PHOENIX-4248 Breakup IndexExpressionIT into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
PHOENIX-4248 Breakup IndexExpressionIT into several integration tests so as not to create too many tables in one test


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

Branch: refs/heads/master
Commit: 3d9adc6f3f298ab5077b8fddf9d17aa4e4a11e56
Parents: 834133a
Author: James Taylor <jt...@salesforce.com>
Authored: Thu Sep 28 17:02:06 2017 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Thu Sep 28 18:23:40 2017 -0700

----------------------------------------------------------------------
 .../phoenix/end2end/index/BaseIndexIT.java      | 1194 ++++++++++++++++++
 .../index/GlobalImmutableNonTxIndexIT.java      |    2 +-
 .../end2end/index/GlobalImmutableTxIndexIT.java |    2 +-
 .../index/GlobalMutableNonTxIndexIT.java        |    2 +-
 .../end2end/index/GlobalMutableTxIndexIT.java   |    2 +-
 .../end2end/index/IndexExpressionIT.java        |   15 -
 .../apache/phoenix/end2end/index/IndexIT.java   | 1194 ------------------
 .../end2end/index/IndexMaintenanceIT.java       |  457 +++++++
 .../phoenix/end2end/index/IndexMetadataIT.java  |   67 +
 .../phoenix/end2end/index/IndexUsageIT.java     |  775 ++++++++++++
 .../index/IndexWithTableSchemaChangeIT.java     |  375 ++++++
 .../index/LocalImmutableNonTxIndexIT.java       |    2 +-
 .../end2end/index/LocalImmutableTxIndexIT.java  |    2 +-
 .../end2end/index/LocalMutableNonTxIndexIT.java |    2 +-
 .../end2end/index/LocalMutableTxIndexIT.java    |    2 +-
 15 files changed, 2876 insertions(+), 1217 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/3d9adc6f/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseIndexIT.java
new file mode 100644
index 0000000..049416c
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseIndexIT.java
@@ -0,0 +1,1194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you under the Apache License, Version 2.0 (the
+ *   "License"); you may not use this file except in compliance
+ *   with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ */
+
+package org.apache.phoenix.end2end.index;
+
+import static org.apache.phoenix.query.QueryConstants.MILLIS_IN_DAY;
+import static org.apache.phoenix.util.TestUtil.ROW5;
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Properties;
+import java.util.Random;
+
+import org.apache.hadoop.hbase.Cell;
+import org.apache.hadoop.hbase.CellScanner;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.KeyValue;
+import org.apache.hadoop.hbase.client.HBaseAdmin;
+import org.apache.hadoop.hbase.client.HTableInterface;
+import org.apache.hadoop.hbase.client.Result;
+import org.apache.hadoop.hbase.client.ResultScanner;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.ipc.PhoenixRpcSchedulerFactory;
+import org.apache.phoenix.compile.ColumnResolver;
+import org.apache.phoenix.compile.FromCompiler;
+import org.apache.phoenix.end2end.ParallelStatsDisabledIT;
+import org.apache.phoenix.exception.SQLExceptionCode;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
+import org.apache.phoenix.jdbc.PhoenixResultSet;
+import org.apache.phoenix.jdbc.PhoenixStatement;
+import org.apache.phoenix.parse.NamedTableNode;
+import org.apache.phoenix.parse.TableName;
+import org.apache.phoenix.query.BaseTest;
+import org.apache.phoenix.schema.PTable;
+import org.apache.phoenix.schema.PTableImpl;
+import org.apache.phoenix.schema.PTableKey;
+import org.apache.phoenix.schema.PTableType;
+import org.apache.phoenix.util.DateUtil;
+import org.apache.phoenix.util.EnvironmentEdgeManager;
+import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.QueryUtil;
+import org.apache.phoenix.util.ReadOnlyProps;
+import org.apache.phoenix.util.SchemaUtil;
+import org.apache.phoenix.util.TestUtil;
+import org.apache.phoenix.util.TransactionUtil;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public abstract class BaseIndexIT extends ParallelStatsDisabledIT {
+    private static final Random RAND = new Random();
+
+    private final boolean localIndex;
+    private final boolean transactional;
+    private final boolean mutable;
+    private final String tableDDLOptions;
+
+    protected BaseIndexIT(boolean localIndex, boolean mutable, boolean transactional, boolean columnEncoded) {
+        this.localIndex = localIndex;
+        this.transactional = transactional;
+        this.mutable = mutable;
+        StringBuilder optionBuilder = new StringBuilder();
+        if (!columnEncoded) {
+            if (optionBuilder.length()!=0)
+                optionBuilder.append(",");
+            optionBuilder.append("COLUMN_ENCODED_BYTES=0");
+        }
+        if (!mutable) {
+            if (optionBuilder.length()!=0)
+                optionBuilder.append(",");
+            optionBuilder.append("IMMUTABLE_ROWS=true");
+            if (!columnEncoded) {
+                optionBuilder.append(",IMMUTABLE_STORAGE_SCHEME="+PTableImpl.ImmutableStorageScheme.ONE_CELL_PER_COLUMN);
+            }
+        }
+        if (transactional) {
+            if (optionBuilder.length()!=0)
+                optionBuilder.append(",");
+            optionBuilder.append(" TRANSACTIONAL=true ");
+        }
+        this.tableDDLOptions = optionBuilder.toString();
+    }
+
+    @Test
+    public void testIndexWithNullableFixedWithCols() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String tableName = "TBL_" + generateUniqueName();
+        String indexName = "IND_" + generateUniqueName();
+        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
+
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.setAutoCommit(false);
+            String ddl ="CREATE TABLE " + fullTableName + TestUtil.TEST_TABLE_SCHEMA + tableDDLOptions;
+            Statement stmt = conn.createStatement();
+            stmt.execute(ddl);
+            BaseTest.populateTestTable(fullTableName);
+            ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullTableName
+                    + " (char_col1 ASC, int_col1 ASC)"
+                    + " INCLUDE (long_col1, long_col2)";
+            stmt.execute(ddl);
+
+            String query = "SELECT d.char_col1, int_col1 from " + fullTableName + " as d";
+            ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            if(localIndex) {
+                assertEquals(
+                        "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + tableName + " [1]\n" +
+                                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                                "CLIENT MERGE SORT",
+                                QueryUtil.getExplainPlan(rs));
+            } else {
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + indexName + "\n"
+                        + "    SERVER FILTER BY FIRST KEY ONLY", QueryUtil.getExplainPlan(rs));
+            }
+
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("chara", rs.getString(1));
+            assertEquals("chara", rs.getString("char_col1"));
+            assertEquals(2, rs.getInt(2));
+            assertTrue(rs.next());
+            assertEquals("chara", rs.getString(1));
+            assertEquals(3, rs.getInt(2));
+            assertTrue(rs.next());
+            assertEquals("chara", rs.getString(1));
+            assertEquals(4, rs.getInt(2));
+            assertFalse(rs.next());
+
+            conn.createStatement().execute("DROP INDEX " + indexName + " ON " + fullTableName);
+
+            query = "SELECT char_col1, int_col1 from " + fullTableName;
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+
+            query = "SELECT char_col1, int_col1 from "+indexName;
+            try{
+                rs = conn.createStatement().executeQuery(query);
+                fail();
+            } catch (SQLException e) {
+                assertEquals(SQLExceptionCode.TABLE_UNDEFINED.getErrorCode(), e.getErrorCode());
+            }
+        }
+    }
+
+    @Test
+    public void testDeleteFromAllPKColumnIndex() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String tableName = "TBL_" + generateUniqueName();
+        String indexName = "IND_" + generateUniqueName();
+        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
+        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.setAutoCommit(false);
+            String ddl ="CREATE TABLE " + fullTableName + TestUtil.TEST_TABLE_SCHEMA + tableDDLOptions;
+            Statement stmt = conn.createStatement();
+            stmt.execute(ddl);
+            BaseTest.populateTestTable(fullTableName);
+            ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullTableName
+                        + " (long_pk, varchar_pk)"
+                        + " INCLUDE (long_col1, long_col2)";
+            stmt.execute(ddl);
+
+            ResultSet rs;
+
+            rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullTableName);
+            assertTrue(rs.next());
+            assertEquals(3,rs.getInt(1));
+            rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullIndexName);
+            assertTrue(rs.next());
+            assertEquals(3,rs.getInt(1));
+
+            String dml = "DELETE from " + fullTableName + " WHERE long_col2 = 4";
+            assertEquals(1,conn.createStatement().executeUpdate(dml));
+            conn.commit();
+
+            String query = "SELECT /*+ NO_INDEX */ long_pk FROM " + fullTableName;
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals(1L, rs.getLong(1));
+            assertTrue(rs.next());
+            assertEquals(3L, rs.getLong(1));
+            assertFalse(rs.next());
+
+            query = "SELECT long_pk FROM " + fullTableName;
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals(1L, rs.getLong(1));
+            assertTrue(rs.next());
+            assertEquals(3L, rs.getLong(1));
+            assertFalse(rs.next());
+
+            query = "SELECT * FROM " + fullIndexName;
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals(1L, rs.getLong(1));
+            assertTrue(rs.next());
+            assertEquals(3L, rs.getLong(1));
+            assertFalse(rs.next());
+
+            conn.createStatement().execute("DROP INDEX " + indexName + " ON " + fullTableName);
+        }
+    }
+
+    @Test
+    public void testCreateIndexAfterUpsertStarted() throws Exception {
+        testCreateIndexAfterUpsertStarted(false, 
+                SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, generateUniqueName()),
+                SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, generateUniqueName()));
+    }
+
+    @Test
+    public void testCreateIndexAfterUpsertStartedTxnl() throws Exception {
+        if (transactional) {
+            testCreateIndexAfterUpsertStarted(true, 
+                    SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, generateUniqueName()),
+                    SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, generateUniqueName()));
+        }
+    }
+
+    private void testCreateIndexAfterUpsertStarted(boolean readOwnWrites, String fullTableName, String fullIndexName) throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        try (Connection conn1 = DriverManager.getConnection(getUrl(), props)) {
+            conn1.setAutoCommit(true);
+            String ddl ="CREATE TABLE " + fullTableName + TestUtil.TEST_TABLE_SCHEMA + tableDDLOptions;
+            Statement stmt1 = conn1.createStatement();
+            stmt1.execute(ddl);
+            BaseTest.populateTestTable(fullTableName);
+
+            ResultSet rs;
+
+            rs = conn1.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullTableName);
+            assertTrue(rs.next());
+            assertEquals(3,rs.getInt(1));
+
+            try (Connection conn2 = DriverManager.getConnection(getUrl(), props)) {
+
+                String upsert = "UPSERT INTO " + fullTableName
+                        + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+                PreparedStatement pstmt2 = conn2.prepareStatement(upsert);
+                pstmt2.setString(1, "varchar4");
+                pstmt2.setString(2, "char4");
+                pstmt2.setInt(3, 4);
+                pstmt2.setLong(4, 4L);
+                pstmt2.setBigDecimal(5, new BigDecimal(4.0));
+                Date date = DateUtil.parseDate("2015-01-01 00:00:00");
+                pstmt2.setDate(6, date);
+                pstmt2.setString(7, "varchar_a");
+                pstmt2.setString(8, "chara");
+                pstmt2.setInt(9, 2);
+                pstmt2.setLong(10, 2L);
+                pstmt2.setBigDecimal(11, new BigDecimal(2.0));
+                pstmt2.setDate(12, date);
+                pstmt2.setString(13, "varchar_b");
+                pstmt2.setString(14, "charb");
+                pstmt2.setInt(15, 3);
+                pstmt2.setLong(16, 3L);
+                pstmt2.setBigDecimal(17, new BigDecimal(3.0));
+                pstmt2.setDate(18, date);
+                pstmt2.executeUpdate();
+
+                if (readOwnWrites) {
+                    String query = "SELECT long_pk FROM " + fullTableName + " WHERE long_pk=4";
+                    rs = conn2.createStatement().executeQuery(query);
+                    assertTrue(rs.next());
+                    assertFalse(rs.next());
+                }
+
+                String indexName = SchemaUtil.getTableNameFromFullName(fullIndexName);
+                ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullTableName
+                        + " (long_pk, varchar_pk)"
+                        + " INCLUDE (long_col1, long_col2)";
+                stmt1.execute(ddl);
+
+                /*
+                 * Commit upsert after index created through different connection.
+                 * This forces conn2 (which doesn't know about the index yet) to update the metadata
+                 * at commit time, recognize the new index, and generate the correct metadata (or index
+                 * rows for immutable indexes).
+                 *
+                 * For transactional data, this is problematic because the index
+                 * gets a timestamp *after* the commit timestamp of conn2 and thus won't be seen during
+                 * the commit. Also, when the index is being built, the data hasn't yet been committed
+                 * and thus won't be part of the initial index build (fixed by PHOENIX-2446).
+                 */
+                conn2.commit();
+
+                stmt1 = conn1.createStatement();
+                rs = stmt1.executeQuery("SELECT COUNT(*) FROM " + fullTableName);
+                assertTrue(rs.next());
+                assertEquals(4,rs.getInt(1));
+                assertEquals(fullIndexName, stmt1.unwrap(PhoenixStatement.class).getQueryPlan().getTableRef().getTable().getName().getString());
+
+                String query = "SELECT /*+ NO_INDEX */ long_pk FROM " + fullTableName;
+                rs = conn1.createStatement().executeQuery(query);
+                assertTrue(rs.next());
+                assertEquals(1L, rs.getLong(1));
+                assertTrue(rs.next());
+                assertEquals(2L, rs.getLong(1));
+                assertTrue(rs.next());
+                assertEquals(3L, rs.getLong(1));
+                assertTrue(rs.next());
+                assertEquals(4L, rs.getLong(1));
+                assertFalse(rs.next());
+            }
+        }
+    }
+
+    @Test
+    public void testDeleteFromNonPKColumnIndex() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String tableName = "TBL_" + generateUniqueName();
+        String indexName = "IND_" + generateUniqueName();
+        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
+        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
+
+        String ddl ="CREATE TABLE " + fullTableName + TestUtil.TEST_TABLE_SCHEMA + tableDDLOptions;
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.setAutoCommit(false);
+            Statement stmt = conn.createStatement();
+            stmt.execute(ddl);
+            BaseTest.populateTestTable(fullTableName);
+            ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullTableName
+                        + " (long_col1, long_col2)"
+                        + " INCLUDE (decimal_col1, decimal_col2)";
+            stmt.execute(ddl);
+        }
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            ResultSet rs;
+
+            rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullTableName);
+            assertTrue(rs.next());
+            assertEquals(3,rs.getInt(1));
+            rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullIndexName);
+            assertTrue(rs.next());
+            assertEquals(3,rs.getInt(1));
+
+            String dml = "DELETE from " + fullTableName + " WHERE long_col2 = 4";
+            assertEquals(1,conn.createStatement().executeUpdate(dml));
+            conn.commit();
+
+            // query the data table
+            String query = "SELECT /*+ NO_INDEX */ long_pk FROM " + fullTableName;
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals(1L, rs.getLong(1));
+            assertTrue(rs.next());
+            assertEquals(3L, rs.getLong(1));
+            assertFalse(rs.next());
+
+            // query the index table
+            query = "SELECT long_pk FROM " + fullTableName + " ORDER BY long_col1";
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals(1L, rs.getLong(1));
+            assertTrue(rs.next());
+            assertEquals(3L, rs.getLong(1));
+            assertFalse(rs.next());
+
+            conn.createStatement().execute("DROP INDEX " + indexName + " ON " + fullTableName);
+        }
+    }
+
+    @Test
+    public void testGroupByCount() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String tableName = "TBL_" + generateUniqueName();
+        String indexName = "IND_" + generateUniqueName();
+        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.setAutoCommit(false);
+            String ddl ="CREATE TABLE " + fullTableName + TestUtil.TEST_TABLE_SCHEMA + tableDDLOptions;
+            Statement stmt = conn.createStatement();
+            stmt.execute(ddl);
+            BaseTest.populateTestTable(fullTableName);
+            ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullTableName + " (int_col2)";
+            stmt.execute(ddl);
+            ResultSet rs;
+            rs = conn.createStatement().executeQuery("SELECT int_col2, COUNT(*) FROM " + fullTableName + " GROUP BY int_col2");
+            assertTrue(rs.next());
+            assertEquals(1,rs.getInt(2));
+        }
+    }
+
+    @Test
+    public void testSelectDistinctOnTableWithSecondaryImmutableIndex() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String tableName = "TBL_" + generateUniqueName();
+        String indexName = "IND_" + generateUniqueName();
+        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.setAutoCommit(false);
+            String ddl ="CREATE TABLE " + fullTableName + TestUtil.TEST_TABLE_SCHEMA + tableDDLOptions;
+            Statement stmt = conn.createStatement();
+            stmt.execute(ddl);
+            BaseTest.populateTestTable(fullTableName);
+            ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullTableName + " (int_col2)";
+            conn.createStatement().execute(ddl);
+            ResultSet rs = conn.createStatement().executeQuery("SELECT distinct int_col2 FROM " + fullTableName + " where int_col2 > 0");
+            assertTrue(rs.next());
+            assertEquals(3, rs.getInt(1));
+            assertTrue(rs.next());
+            assertEquals(4, rs.getInt(1));
+            assertTrue(rs.next());
+            assertEquals(5, rs.getInt(1));
+            assertFalse(rs.next());
+        }
+    }
+
+    @Test
+    public void testInClauseWithIndexOnColumnOfUsignedIntType() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String tableName = "TBL_" + generateUniqueName();
+        String indexName = "IND_" + generateUniqueName();
+        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.setAutoCommit(false);
+            String ddl ="CREATE TABLE " + fullTableName + TestUtil.TEST_TABLE_SCHEMA + tableDDLOptions;
+            Statement stmt = conn.createStatement();
+            stmt.execute(ddl);
+            BaseTest.populateTestTable(fullTableName);
+            ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullTableName + " (int_col1)";
+            stmt.execute(ddl);
+            ResultSet rs = conn.createStatement().executeQuery("SELECT int_col1 FROM " + fullTableName + " where int_col1 IN (1, 2, 3, 4)");
+            assertTrue(rs.next());
+            assertEquals(2, rs.getInt(1));
+            assertTrue(rs.next());
+            assertEquals(3, rs.getInt(1));
+            assertTrue(rs.next());
+            assertEquals(4, rs.getInt(1));
+            assertFalse(rs.next());
+        }
+    }
+
+    @Test
+    public void createIndexOnTableWithSpecifiedDefaultCF() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String tableName = "TBL_" + generateUniqueName();
+        String indexName = "IND_" + generateUniqueName();
+        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
+        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.setAutoCommit(false);
+            String query;
+            ResultSet rs;
+            String ddl ="CREATE TABLE " + fullTableName
+                    + " (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR) DEFAULT_COLUMN_FAMILY='A'" + (!tableDDLOptions.isEmpty() ? "," + tableDDLOptions : "");
+            Statement stmt = conn.createStatement();
+            stmt.execute(ddl);
+
+            query = "SELECT * FROM " + tableName;
+            rs = conn.createStatement().executeQuery(query);
+            assertFalse(rs.next());
+
+            String options = localIndex ? "SALT_BUCKETS=10, MULTI_TENANT=true, IMMUTABLE_ROWS=true, DISABLE_WAL=true" : "";
+            conn.createStatement().execute(
+                    "CREATE INDEX " + indexName + " ON " + fullTableName + " (v1) INCLUDE (v2) " + options);
+            query = "SELECT * FROM " + fullIndexName;
+            rs = conn.createStatement().executeQuery(query);
+            assertFalse(rs.next());
+
+            //check options set correctly on index
+            TableName indexTableName = TableName.create(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
+            NamedTableNode indexNode = NamedTableNode.create(null, indexTableName, null);
+            ColumnResolver resolver = FromCompiler.getResolver(indexNode, conn.unwrap(PhoenixConnection.class));
+            PTable indexTable = resolver.getTables().get(0).getTable();
+            // Can't set IMMUTABLE_ROWS, MULTI_TENANT or DEFAULT_COLUMN_FAMILY_NAME on an index
+            assertNull(indexTable.getDefaultFamilyName());
+            assertFalse(indexTable.isMultiTenant());
+            assertEquals(mutable, !indexTable.isImmutableRows()); // Should match table
+            if(localIndex) {
+                assertEquals(10, indexTable.getBucketNum().intValue());
+                assertTrue(indexTable.isWALDisabled());
+            }
+        }
+    }
+
+    @Test
+    public void testIndexWithNullableDateCol() throws Exception {
+        String tableName = "TBL_" + generateUniqueName();
+        String indexName = "IND_" + generateUniqueName();
+        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
+        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.setAutoCommit(false);
+            Date date = new Date(System.currentTimeMillis());
+
+            TestUtil.createMultiCFTestTable(conn, fullTableName, tableDDLOptions);
+            populateMultiCFTestTable(fullTableName, date);
+            String ddl = "CREATE " + (localIndex ? " LOCAL " : "") + " INDEX " + indexName + " ON " + fullTableName + " (date_col)";
+            conn.createStatement().execute(ddl);
+
+            String query = "SELECT int_pk from " + fullTableName ;
+            ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            if (localIndex) {
+                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName +" [1]\n"
+                        + "    SERVER FILTER BY FIRST KEY ONLY\n"
+                        + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+            } else {
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullIndexName + "\n"
+                        + "    SERVER FILTER BY FIRST KEY ONLY", QueryUtil.getExplainPlan(rs));
+            }
+
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals(2, rs.getInt(1));
+            assertTrue(rs.next());
+            assertEquals(1, rs.getInt(1));
+            assertTrue(rs.next());
+            assertEquals(3, rs.getInt(1));
+            assertFalse(rs.next());
+
+            query = "SELECT date_col from " + fullTableName + " order by date_col" ;
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            if (localIndex) {
+                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName + " [1]\n"
+                        + "    SERVER FILTER BY FIRST KEY ONLY\n"
+                        + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+            } else {
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullIndexName + "\n"
+                        + "    SERVER FILTER BY FIRST KEY ONLY", QueryUtil.getExplainPlan(rs));
+            }
+
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals(date, rs.getDate(1));
+            assertTrue(rs.next());
+            assertEquals(new Date(date.getTime() + MILLIS_IN_DAY), rs.getDate(1));
+            assertTrue(rs.next());
+            assertEquals(new Date(date.getTime() + 2 * MILLIS_IN_DAY), rs.getDate(1));
+            assertFalse(rs.next());
+        }
+    }
+
+    @Test
+    public void testSelectAllAndAliasWithIndex() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String tableName = "TBL_" + generateUniqueName();
+        String indexName = "IND_" + generateUniqueName();
+        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
+        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.setAutoCommit(false);
+            String query;
+            ResultSet rs;
+            String ddl = "CREATE TABLE " + fullTableName + " (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR) " + tableDDLOptions;
+            conn.createStatement().execute(ddl);
+            query = "SELECT * FROM " + fullTableName;
+            rs = conn.createStatement().executeQuery(query);
+            assertFalse(rs.next());
+
+            ddl = "CREATE " + (localIndex ? " LOCAL " : "") + " INDEX " + indexName + " ON " + fullTableName + " (v2 DESC) INCLUDE (v1)";
+            conn.createStatement().execute(ddl);
+            query = "SELECT * FROM " + fullIndexName;
+            rs = conn.createStatement().executeQuery(query);
+            assertFalse(rs.next());
+
+            PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + fullTableName + " VALUES(?,?,?)");
+            stmt.setString(1,"a");
+            stmt.setString(2, "x");
+            stmt.setString(3, "1");
+            stmt.execute();
+            stmt.setString(1,"b");
+            stmt.setString(2, "y");
+            stmt.setString(3, "2");
+            stmt.execute();
+            conn.commit();
+
+            query = "SELECT * FROM " + fullTableName;
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            if(localIndex){
+                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName+" [1]\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+            } else {
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullIndexName, QueryUtil.getExplainPlan(rs));
+            }
+
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("b",rs.getString(1));
+            assertEquals("y",rs.getString(2));
+            assertEquals("2",rs.getString(3));
+            assertEquals("b",rs.getString("k"));
+            assertEquals("y",rs.getString("v1"));
+            assertEquals("2",rs.getString("v2"));
+            assertTrue(rs.next());
+            assertEquals("a",rs.getString(1));
+            assertEquals("x",rs.getString(2));
+            assertEquals("1",rs.getString(3));
+            assertEquals("a",rs.getString("k"));
+            assertEquals("x",rs.getString("v1"));
+            assertEquals("1",rs.getString("v2"));
+            assertFalse(rs.next());
+
+            query = "SELECT v1 as foo FROM " + fullTableName + " WHERE v2 = '1' ORDER BY foo";
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            if(localIndex){
+                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +fullTableName + " [1,~'1']\n" +
+                        "    SERVER SORTED BY [\"V1\"]\n" +
+                        "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+            } else {
+                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +fullIndexName + " [~'1']\n" +
+                        "    SERVER SORTED BY [\"V1\"]\n" +
+                        "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+            }
+
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("x",rs.getString(1));
+            assertEquals("x",rs.getString("foo"));
+            assertFalse(rs.next());
+        }
+    }
+
+    @Test
+    public void testSelectCF() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String tableName = "TBL_" + generateUniqueName();
+        String indexName = "IND_" + generateUniqueName();
+        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
+        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.setAutoCommit(false);
+            String query;
+            ResultSet rs;
+            String ddl = "CREATE TABLE " + fullTableName + " (k VARCHAR NOT NULL PRIMARY KEY, a.v1 VARCHAR, a.v2 VARCHAR, b.v1 VARCHAR) " + tableDDLOptions;
+            conn.createStatement().execute(ddl);
+            query = "SELECT * FROM " + fullTableName;
+            rs = conn.createStatement().executeQuery(query);
+            assertFalse(rs.next());
+            ddl = "CREATE " + (localIndex ? " LOCAL " : "") + " INDEX " + indexName + " ON " + fullTableName + " (v2 DESC) INCLUDE (a.v1)";
+            conn.createStatement().execute(ddl);
+            query = "SELECT * FROM " + fullIndexName;
+            rs = conn.createStatement().executeQuery(query);
+            assertFalse(rs.next());
+
+            PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + fullTableName + " VALUES(?,?,?,?)");
+            stmt.setString(1,"a");
+            stmt.setString(2, "x");
+            stmt.setString(3, "1");
+            stmt.setString(4, "A");
+            stmt.execute();
+            stmt.setString(1,"b");
+            stmt.setString(2, "y");
+            stmt.setString(3, "2");
+            stmt.setString(4, "B");
+            stmt.execute();
+            conn.commit();
+
+            query = "SELECT * FROM " + fullTableName;
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullTableName, QueryUtil.getExplainPlan(rs));
+
+            query = "SELECT a.* FROM " + fullTableName;
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            if(localIndex) {
+                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName+" [1]\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+            } else {
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullIndexName, QueryUtil.getExplainPlan(rs));
+            }
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("y",rs.getString(1));
+            assertEquals("2",rs.getString(2));
+            assertEquals("y",rs.getString("v1"));
+            assertEquals("2",rs.getString("v2"));
+            assertTrue(rs.next());
+            assertEquals("x",rs.getString(1));
+            assertEquals("1",rs.getString(2));
+            assertEquals("x",rs.getString("v1"));
+            assertEquals("1",rs.getString("v2"));
+            assertFalse(rs.next());
+        }
+    }
+
+    @Test
+    public void testUpsertAfterIndexDrop() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String tableName = "TBL_" + generateUniqueName();
+        String indexName = "IND_" + generateUniqueName();
+        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
+        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.setAutoCommit(false);
+            String query;
+            ResultSet rs;
+            // make sure that the tables are empty, but reachable
+            conn.createStatement().execute(
+                    "CREATE TABLE " + fullTableName
+                    + " (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR)" + tableDDLOptions);
+            query = "SELECT * FROM " + fullTableName;
+            rs = conn.createStatement().executeQuery(query);
+            assertFalse(rs.next());
+
+            conn.createStatement().execute(
+                    "CREATE " + (localIndex ? "LOCAL " : "") + "INDEX " + indexName + " ON " + fullTableName + " (v1, v2)");
+            query = "SELECT * FROM " + fullIndexName;
+            rs = conn.createStatement().executeQuery(query);
+            assertFalse(rs.next());
+
+            // load some data into the table
+            PreparedStatement stmt =
+                    conn.prepareStatement("UPSERT INTO " + fullTableName + " VALUES(?,?,?)");
+            stmt.setString(1, "a");
+            stmt.setString(2, "x");
+            stmt.setString(3, "1");
+            stmt.execute();
+            conn.commit();
+
+            // make sure the index is working as expected
+            query = "SELECT * FROM " + fullIndexName;
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("x", rs.getString(1));
+            assertEquals("1", rs.getString(2));
+            assertEquals("a", rs.getString(3));
+            assertFalse(rs.next());
+
+            String ddl = "DROP INDEX " + indexName + " ON " + fullTableName;
+            conn.createStatement().execute(ddl);
+
+            stmt = conn.prepareStatement("UPSERT INTO " + fullTableName + "(k, v1) VALUES(?,?)");
+            stmt.setString(1, "a");
+            stmt.setString(2, "y");
+            stmt.execute();
+            conn.commit();
+
+            query = "SELECT * FROM " + fullTableName;
+
+            // check that the data table matches as expected
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("a", rs.getString(1));
+            assertEquals("y", rs.getString(2));
+            assertFalse(rs.next());
+        }
+    }
+
+    @Test
+    public void testMultipleUpdatesAcrossRegions() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String tableName = "TBL_" + generateUniqueName();
+        String indexName = "IND_" + generateUniqueName();
+        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
+        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
+
+        String testTable = fullTableName+"_MULTIPLE_UPDATES";
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.setAutoCommit(false);
+            String query;
+            ResultSet rs;
+            // make sure that the tables are empty, but reachable
+            conn.createStatement().execute(
+                    "CREATE TABLE " + testTable
+                    + " (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR) "
+                    + (!tableDDLOptions.isEmpty() ? tableDDLOptions : "") + " SPLIT ON ('b')");
+            query = "SELECT * FROM " + testTable;
+            rs = conn.createStatement().executeQuery(query);
+            assertFalse(rs.next());
+
+            conn.createStatement().execute(
+                    "CREATE " + (localIndex ? "LOCAL " : "") + "INDEX " + indexName + " ON " + testTable + " (v1, v2)");
+            query = "SELECT * FROM " + fullIndexName;
+            rs = conn.createStatement().executeQuery(query);
+            assertFalse(rs.next());
+
+            // load some data into the table
+            PreparedStatement stmt =
+                    conn.prepareStatement("UPSERT INTO " + testTable + " VALUES(?,?,?)");
+            stmt.setString(1, "a");
+            stmt.setString(2, "x");
+            stmt.setString(3, "1");
+            stmt.execute();
+            stmt.setString(1, "b");
+            stmt.setString(2, "y");
+            stmt.setString(3, "2");
+            stmt.execute();
+            stmt.setString(1, "c");
+            stmt.setString(2, "z");
+            stmt.setString(3, "3");
+            stmt.execute();
+            conn.commit();
+
+            query = "SELECT /*+ NO_INDEX */ * FROM " + testTable;
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("a", rs.getString(1));
+            assertEquals("x", rs.getString(2));
+            assertEquals("1", rs.getString(3));
+            assertTrue(rs.next());
+            assertEquals("b", rs.getString(1));
+            assertEquals("y", rs.getString(2));
+            assertEquals("2", rs.getString(3));
+            assertTrue(rs.next());
+            assertEquals("c", rs.getString(1));
+            assertEquals("z", rs.getString(2));
+            assertEquals("3", rs.getString(3));
+            assertFalse(rs.next());
+            
+            // make sure the index is working as expected
+            query = "SELECT * FROM " + testTable;
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            if (localIndex) {
+                assertEquals("CLIENT PARALLEL 2-WAY RANGE SCAN OVER " + testTable+" [1]\n"
+                        + "    SERVER FILTER BY FIRST KEY ONLY\n"
+                        + "CLIENT MERGE SORT",
+                        QueryUtil.getExplainPlan(rs));
+            } else {
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullIndexName + "\n"
+                        + "    SERVER FILTER BY FIRST KEY ONLY",
+                        QueryUtil.getExplainPlan(rs));
+            }
+
+            // check that the data table matches as expected
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("a", rs.getString(1));
+            assertEquals("x", rs.getString(2));
+            assertEquals("1", rs.getString(3));
+            assertTrue(rs.next());
+            assertEquals("b", rs.getString(1));
+            assertEquals("y", rs.getString(2));
+            assertEquals("2", rs.getString(3));
+            assertTrue(rs.next());
+            assertEquals("c", rs.getString(1));
+            assertEquals("z", rs.getString(2));
+            assertEquals("3", rs.getString(3));
+            assertFalse(rs.next());
+        }
+    }
+
+    @Test
+    public void testIndexWithCaseSensitiveCols() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String tableName = "TBL_" + generateUniqueName();
+        String indexName = "IND_" + generateUniqueName();
+        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
+        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.setAutoCommit(false);
+            String query;
+            ResultSet rs;
+            conn.createStatement().execute("CREATE TABLE " + fullTableName + " (k VARCHAR NOT NULL PRIMARY KEY, \"V1\" VARCHAR, \"v2\" VARCHAR)"+tableDDLOptions);
+            query = "SELECT * FROM "+fullTableName;
+            rs = conn.createStatement().executeQuery(query);
+            long ts = conn.unwrap(PhoenixConnection.class).getTable(new PTableKey(null,fullTableName)).getTimeStamp();
+            assertFalse(rs.next());
+            conn.createStatement().execute(
+                    "CREATE " + (localIndex ? "LOCAL " : "") + "INDEX " + indexName + " ON " + fullTableName + "(\"v2\") INCLUDE (\"V1\")");
+            query = "SELECT * FROM "+fullIndexName;
+            rs = conn.createStatement().executeQuery(query);
+            assertFalse(rs.next());
+
+            PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + fullTableName + " VALUES(?,?,?)");
+            stmt.setString(1,"a");
+            stmt.setString(2, "x");
+            stmt.setString(3, "1");
+            stmt.execute();
+            stmt.setString(1,"b");
+            stmt.setString(2, "y");
+            stmt.setString(3, "2");
+            stmt.execute();
+            conn.commit();
+
+            query = "SELECT * FROM " + fullTableName + " WHERE \"v2\" = '1'";
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            if(localIndex){
+                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName + " [1,'1']\n"
+                        + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+            } else {
+                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullIndexName + " ['1']", QueryUtil.getExplainPlan(rs));
+            }
+            
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("a",rs.getString(1));
+            assertEquals("x",rs.getString(2));
+            assertEquals("1",rs.getString(3));
+            assertEquals("a",rs.getString("k"));
+            assertEquals("x",rs.getString("V1"));
+            assertEquals("1",rs.getString("v2"));
+            assertFalse(rs.next());
+
+            query = "SELECT \"V1\", \"V1\" as foo1, \"v2\" as foo, \"v2\" as \"Foo1\", \"v2\" FROM " + fullTableName + " ORDER BY foo";
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            if(localIndex){
+                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName + " [1]\nCLIENT MERGE SORT",
+                        QueryUtil.getExplainPlan(rs));
+            } else {
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER "+fullIndexName, QueryUtil.getExplainPlan(rs));
+            }
+
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("x",rs.getString(1));
+            assertEquals("x",rs.getString("V1"));
+            assertEquals("x",rs.getString(2));
+            assertEquals("x",rs.getString("foo1"));
+            assertEquals("1",rs.getString(3));
+            assertEquals("1",rs.getString("Foo"));
+            assertEquals("1",rs.getString(4));
+            assertEquals("1",rs.getString("Foo1"));
+            assertEquals("1",rs.getString(5));
+            assertEquals("1",rs.getString("v2"));
+            assertTrue(rs.next());
+            assertEquals("y",rs.getString(1));
+            assertEquals("y",rs.getString("V1"));
+            assertEquals("y",rs.getString(2));
+            assertEquals("y",rs.getString("foo1"));
+            assertEquals("2",rs.getString(3));
+            assertEquals("2",rs.getString("Foo"));
+            assertEquals("2",rs.getString(4));
+            assertEquals("2",rs.getString("Foo1"));
+            assertEquals("2",rs.getString(5));
+            assertEquals("2",rs.getString("v2"));
+            assertFalse(rs.next());
+
+            assertNoIndexDeletes(conn, ts, fullIndexName);
+        }
+    }
+
+    private void assertNoIndexDeletes(Connection conn, long minTimestamp, String fullIndexName) throws IOException, SQLException {
+        if (!this.mutable) {
+            PhoenixConnection pconn = conn.unwrap(PhoenixConnection.class);
+            PTable index = pconn.getTable(new PTableKey(null, fullIndexName));
+            byte[] physicalIndexTable = index.getPhysicalName().getBytes();
+            try (HTableInterface hIndex = pconn.getQueryServices().getTable(physicalIndexTable)) {
+                Scan scan = new Scan();
+                scan.setRaw(true);
+                if (this.transactional) {
+                    minTimestamp = TransactionUtil.convertToNanoseconds(minTimestamp);
+                }
+                scan.setTimeRange(minTimestamp, HConstants.LATEST_TIMESTAMP);
+                ResultScanner scanner = hIndex.getScanner(scan);
+                Result result;
+                while ((result = scanner.next()) != null) {
+                    CellScanner cellScanner = result.cellScanner();
+                    while (cellScanner.advance()) {
+                        Cell current = cellScanner.current();
+                        assertEquals (KeyValue.Type.Put.getCode(), current.getTypeByte());
+                    }
+                }
+            };
+        }
+    }
+
+    @Test
+    public void testInFilterOnIndexedTable() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String tableName = "TBL_" + generateUniqueName();
+        String indexName = "IND_" + generateUniqueName();
+        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.setAutoCommit(false);
+            String query;
+            ResultSet rs;
+            String ddl = "CREATE TABLE " + fullTableName +"  (PK1 CHAR(2) NOT NULL PRIMARY KEY, CF1.COL1 BIGINT) " + tableDDLOptions;
+            conn.createStatement().execute(ddl);
+            ddl = "CREATE " + (localIndex ? "LOCAL " : "") + "INDEX " + indexName + " ON " + fullTableName + "(COL1)";
+            conn.createStatement().execute(ddl);
+
+            query = "SELECT COUNT(COL1) FROM " + fullTableName +" WHERE COL1 IN (1,25,50,75,100)";
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+        }
+    }
+
+    @Test
+    public void testIndexWithDecimalCol() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String tableName = "TBL_" + generateUniqueName();
+        String indexName = "IND_" + generateUniqueName();
+        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
+        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.setAutoCommit(false);
+            String query;
+            ResultSet rs;
+            Date date = new Date(System.currentTimeMillis());
+
+            TestUtil.createMultiCFTestTable(conn, fullTableName, tableDDLOptions);
+            populateMultiCFTestTable(fullTableName, date);
+            String ddl = null;
+            ddl = "CREATE " + (localIndex ? "LOCAL " : "") + "INDEX " + indexName + " ON " + fullTableName + " (decimal_pk) INCLUDE (decimal_col1, decimal_col2)";
+            conn.createStatement().execute(ddl);
+
+            query = "SELECT decimal_pk, decimal_col1, decimal_col2 from " + fullTableName ;
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            if(localIndex) {
+                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName+" [1]\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+            } else {
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullIndexName, QueryUtil.getExplainPlan(rs));
+            }
+
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals(new BigDecimal("1.1"), rs.getBigDecimal(1));
+            assertEquals(new BigDecimal("2.1"), rs.getBigDecimal(2));
+            assertEquals(new BigDecimal("3.1"), rs.getBigDecimal(3));
+            assertTrue(rs.next());
+            assertEquals(new BigDecimal("2.2"), rs.getBigDecimal(1));
+            assertEquals(new BigDecimal("3.2"), rs.getBigDecimal(2));
+            assertEquals(new BigDecimal("4.2"), rs.getBigDecimal(3));
+            assertTrue(rs.next());
+            assertEquals(new BigDecimal("3.3"), rs.getBigDecimal(1));
+            assertEquals(new BigDecimal("4.3"), rs.getBigDecimal(2));
+            assertEquals(new BigDecimal("5.3"), rs.getBigDecimal(3));
+            assertFalse(rs.next());
+        }
+    }
+
+    /**
+     * Ensure that HTD contains table priorities correctly.
+     */
+    @Test
+    public void testTableDescriptorPriority() throws SQLException, IOException {
+        String tableName = "TBL_" + generateUniqueName();
+        String indexName = "IND_" + generateUniqueName();
+        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
+        // Check system tables priorities.
+        try (HBaseAdmin admin = driver.getConnectionQueryServices(null, null).getAdmin(); 
+                Connection c = DriverManager.getConnection(getUrl())) {
+            ResultSet rs = c.getMetaData().getTables("", 
+                    "\""+ PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA + "\"", 
+                    null, 
+                    new String[] {PTableType.SYSTEM.toString()});
+            ReadOnlyProps p = c.unwrap(PhoenixConnection.class).getQueryServices().getProps();
+            while (rs.next()) {
+                String schemaName = rs.getString(PhoenixDatabaseMetaData.TABLE_SCHEM);
+                String tName = rs.getString(PhoenixDatabaseMetaData.TABLE_NAME);
+                org.apache.hadoop.hbase.TableName hbaseTableName = SchemaUtil.getPhysicalTableName(SchemaUtil.getTableName(schemaName, tName), p);
+                HTableDescriptor htd = admin.getTableDescriptor(hbaseTableName);
+                String val = htd.getValue("PRIORITY");
+                assertNotNull("PRIORITY is not set for table:" + htd, val);
+                assertTrue(Integer.parseInt(val)
+                        >= PhoenixRpcSchedulerFactory.getMetadataPriority(config));
+            }
+            Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+            String ddl ="CREATE TABLE " + fullTableName + TestUtil.TEST_TABLE_SCHEMA + tableDDLOptions;
+            try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+                conn.setAutoCommit(false);
+                Statement stmt = conn.createStatement();
+                stmt.execute(ddl);
+                BaseTest.populateTestTable(fullTableName);
+                ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName
+                        + " ON " + fullTableName + " (long_col1, long_col2)"
+                        + " INCLUDE (decimal_col1, decimal_col2)";
+                stmt.execute(ddl);
+            }
+
+            HTableDescriptor dataTable = admin.getTableDescriptor(
+                    org.apache.hadoop.hbase.TableName.valueOf(fullTableName));
+            String val = dataTable.getValue("PRIORITY");
+            assertTrue(val == null || Integer.parseInt(val) < HConstants.HIGH_QOS);
+
+            if (!localIndex && mutable) {
+                HTableDescriptor indexTable = admin.getTableDescriptor(
+                        org.apache.hadoop.hbase.TableName.valueOf(indexName));
+                val = indexTable.getValue("PRIORITY");
+                assertNotNull("PRIORITY is not set for table:" + indexTable, val);
+                assertTrue(Integer.parseInt(val) >= PhoenixRpcSchedulerFactory.getIndexPriority(config));
+            }
+        }
+    }
+
+    @Test
+    public void testQueryBackToDataTableWithDescPKColumn() throws SQLException {
+        doTestQueryBackToDataTableWithDescPKColumn(true);
+        doTestQueryBackToDataTableWithDescPKColumn(false);
+    }
+
+    private void doTestQueryBackToDataTableWithDescPKColumn(boolean isSecondPKDesc) throws SQLException {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String tableName = "TBL_" + generateUniqueName();
+        String indexName = "IND_" + generateUniqueName();
+        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
+        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
+
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            // create data table and index table
+            conn.setAutoCommit(true);
+            Statement stmt = conn.createStatement();
+            String ddl = "CREATE TABLE " + fullTableName + "(p1 integer not null, p2 integer not null, " +
+                    " a integer, b integer CONSTRAINT PK PRIMARY KEY ";
+            if (isSecondPKDesc) {
+                ddl += "(p1, p2 desc))";
+            } else {
+                ddl += "(p1 desc, p2))";
+            }
+            stmt.executeUpdate(ddl);
+            ddl = "CREATE "+ (localIndex ? "LOCAL " : "") + " INDEX " + fullIndexName + " on " + fullTableName + "(a)";
+            stmt.executeUpdate(ddl);
+
+            // upsert a single row
+            String upsert = "UPSERT INTO " + fullTableName + " VALUES(1,2,3,4)";
+            stmt.executeUpdate(upsert);
+
+            // try select with index
+            // a = 3, should hit index table, but we select column B, so it will query back to data table
+            String query = "SELECT /*+index(" + fullTableName + " " + fullIndexName + "*/ b from " + fullTableName +
+                    " WHERE a = 3";
+            ResultSet rs = stmt.executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals(4, rs.getInt(1));
+            assertFalse(rs.next());
+            rs.close();
+            stmt.close();
+        }
+    }
+
+    @Test
+    public void testReturnedTimestamp() throws Exception {
+        String tenantId = getOrganizationId();
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            String indexName = generateUniqueName();
+            String tableName =
+                    initATableValues(generateUniqueName(), tenantId, getDefaultSplits(tenantId),
+                        new Date(System.currentTimeMillis()), null, getUrl(), tableDDLOptions);
+            String ddl = "CREATE "+ (localIndex ? "LOCAL " : "") + " INDEX " + indexName + " on " + tableName + "(A_STRING) INCLUDE (B_STRING)";
+            conn.createStatement().executeUpdate(ddl);
+            String query = "SELECT ENTITY_ID,A_STRING,B_STRING FROM " + tableName + " WHERE organization_id=? and entity_id=?";
+
+            PreparedStatement statement = conn.prepareStatement(query);
+            statement.setString(1, tenantId);
+
+            long currentTime = EnvironmentEdgeManager.currentTimeMillis();
+            String entityId = mutable ? ROW5 : Integer.toString(Math.abs(RAND.nextInt() % 1000000000));
+            PreparedStatement ddlStatement = conn.prepareStatement("UPSERT INTO " + tableName + "(ORGANIZATION_ID, ENTITY_ID,A_STRING) VALUES('" + tenantId + "',?,?)");
+            ddlStatement.setString(1, entityId);
+            ddlStatement.setString(2, Integer.toString(Math.abs(RAND.nextInt() % 1000000000)));
+            ddlStatement.executeUpdate();
+            conn.commit();
+ 
+            statement.setString(2, entityId);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertTrue(rs.unwrap(PhoenixResultSet.class).getCurrentRow().getValue(0).getTimestamp() >= currentTime);
+            assertEquals(rs.getString(1).trim(), entityId);
+            assertFalse(rs.next());
+
+            currentTime = EnvironmentEdgeManager.currentTimeMillis();
+            entityId = mutable ? ROW5 : Integer.toString(Math.abs(RAND.nextInt() % 1000000000));
+            ddlStatement = conn.prepareStatement("UPSERT INTO " + tableName + "(ORGANIZATION_ID, ENTITY_ID,B_STRING) VALUES('" + tenantId + "',?,?)");
+            ddlStatement.setString(1, entityId);
+            ddlStatement.setString(2, Integer.toString(Math.abs(RAND.nextInt() % 1000000000)));
+            ddlStatement.executeUpdate();
+            conn.commit();
+            
+            statement.setString(2, entityId);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertTrue(rs.unwrap(PhoenixResultSet.class).getCurrentRow().getValue(0).getTimestamp() >= currentTime);
+            assertEquals(rs.getString(1).trim(), entityId);
+            assertFalse(rs.next());
+
+        } finally {
+            conn.close();
+        }
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3d9adc6f/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalImmutableNonTxIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalImmutableNonTxIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalImmutableNonTxIndexIT.java
index 52b219d..70ad73d 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalImmutableNonTxIndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalImmutableNonTxIndexIT.java
@@ -22,7 +22,7 @@ import java.util.Collection;
 
 import org.junit.runners.Parameterized.Parameters;
 
-public class GlobalImmutableNonTxIndexIT extends IndexIT {
+public class GlobalImmutableNonTxIndexIT extends BaseIndexIT {
 
     public GlobalImmutableNonTxIndexIT(boolean localIndex, boolean mutable, boolean transactional, boolean columnEncoded) {
         super(localIndex, mutable, transactional, columnEncoded);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3d9adc6f/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalImmutableTxIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalImmutableTxIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalImmutableTxIndexIT.java
index 58eab8c..1ba498c 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalImmutableTxIndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalImmutableTxIndexIT.java
@@ -22,7 +22,7 @@ import java.util.Collection;
 
 import org.junit.runners.Parameterized.Parameters;
 
-public class GlobalImmutableTxIndexIT extends IndexIT {
+public class GlobalImmutableTxIndexIT extends BaseIndexIT {
 
     public GlobalImmutableTxIndexIT(boolean localIndex, boolean mutable, boolean transactional, boolean columnEncoded) {
         super(localIndex, mutable, transactional, columnEncoded);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3d9adc6f/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableNonTxIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableNonTxIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableNonTxIndexIT.java
index 9fe1938..f37182d 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableNonTxIndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableNonTxIndexIT.java
@@ -22,7 +22,7 @@ import java.util.Collection;
 
 import org.junit.runners.Parameterized.Parameters;
 
-public class GlobalMutableNonTxIndexIT extends IndexIT {
+public class GlobalMutableNonTxIndexIT extends BaseIndexIT {
 
     public GlobalMutableNonTxIndexIT(boolean localIndex, boolean mutable, boolean transactional, boolean columnEncoded) {
         super(localIndex, mutable, transactional, columnEncoded);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3d9adc6f/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableTxIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableTxIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableTxIndexIT.java
index 4536959..65ca2c5 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableTxIndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableTxIndexIT.java
@@ -22,7 +22,7 @@ import java.util.Collection;
 
 import org.junit.runners.Parameterized.Parameters;
 
-public class GlobalMutableTxIndexIT extends IndexIT {
+public class GlobalMutableTxIndexIT extends BaseIndexIT {
 
     public GlobalMutableTxIndexIT(boolean localIndex, boolean mutable, boolean transactional, boolean columnEncoded) {
         super(localIndex, mutable, transactional, columnEncoded);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3d9adc6f/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexExpressionIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexExpressionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexExpressionIT.java
index 2395b02..2578fe4 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexExpressionIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexExpressionIT.java
@@ -199,8 +199,6 @@ public class IndexExpressionIT extends ParallelStatsDisabledIT {
             // the index table
             verifyResult(rs, 3);
             verifyResult(rs, 4);
-
-            conn.createStatement().execute("DROP INDEX " + indexName + " ON " + fullDataTableName);
         } finally {
             conn.close();
         }
@@ -279,7 +277,6 @@ public class IndexExpressionIT extends ParallelStatsDisabledIT {
             assertEquals("VARCHAR2_CHAR2     _A.VARCHAR2_B.CHAR2   ", rs.getString(1));
             assertEquals(2, rs.getLong(2));
             assertFalse(rs.next());
-            conn.createStatement().execute("DROP INDEX " + indexName + " ON " + fullDataTableName);
         } finally {
             conn.close();
         }
@@ -362,7 +359,6 @@ public class IndexExpressionIT extends ParallelStatsDisabledIT {
             rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullIndexTableName);
             assertTrue(rs.next());
             assertEquals(1, rs.getInt(1));
-            conn.createStatement().execute("DROP INDEX " + indexName + " ON " + fullDataTableName);
         } finally {
             conn.close();
         }
@@ -448,7 +444,6 @@ public class IndexExpressionIT extends ParallelStatsDisabledIT {
             assertEquals(2L, rs.getLong(3));
             assertEquals("VARCHAR1", rs.getString(4));
             assertFalse(rs.next());
-            conn.createStatement().execute("DROP INDEX " + indexName + " ON " + fullDataTableName);
         } finally {
             conn.close();
         }
@@ -503,7 +498,6 @@ public class IndexExpressionIT extends ParallelStatsDisabledIT {
             assertTrue(rs.next());
             assertEquals(1, rs.getInt(2));
             assertFalse(rs.next());
-            conn.createStatement().execute("DROP INDEX " + indexName + " ON " + fullDataTableName);
         } finally {
             conn.close();
         }
@@ -556,7 +550,6 @@ public class IndexExpressionIT extends ParallelStatsDisabledIT {
             assertTrue(rs.next());
             assertEquals(3, rs.getInt(1));
             assertFalse(rs.next());
-            conn.createStatement().execute("DROP INDEX " + indexName + " ON " + fullDataTableName);
         } finally {
             conn.close();
         }
@@ -606,7 +599,6 @@ public class IndexExpressionIT extends ParallelStatsDisabledIT {
             assertTrue(rs.next());
             assertEquals(2, rs.getInt(1));
             assertFalse(rs.next());
-            conn.createStatement().execute("DROP INDEX " + indexName + " ON " + fullDataTableName);
         } finally {
             conn.close();
         }
@@ -660,7 +652,6 @@ public class IndexExpressionIT extends ParallelStatsDisabledIT {
             assertTrue(rs.next());
             assertEquals(3, rs.getInt(1));
             assertFalse(rs.next());
-            conn.createStatement().execute("DROP INDEX " + indexName + " ON " + fullDataTableName);
         } finally {
             conn.close();
         }
@@ -769,7 +760,6 @@ public class IndexExpressionIT extends ParallelStatsDisabledIT {
             assertEquals("y_2",rs.getString(5));
             assertEquals("y_2",rs.getString("\"('cf1'.'V1' || '_' || 'CF2'.'v2')\""));
             assertFalse(rs.next());
-            conn.createStatement().execute("DROP INDEX " + indexName + " ON " + dataTableName);
         } finally {
             conn.close();
         }
@@ -824,7 +814,6 @@ public class IndexExpressionIT extends ParallelStatsDisabledIT {
             assertEquals(2, rs.getInt(1));
             assertEquals(1, rs.getInt(2));
             assertFalse(rs.next());
-            conn.createStatement().execute("DROP INDEX " + indexName + " ON " + fullDataTableName);
         } finally {
             conn.close();
         }
@@ -859,7 +848,6 @@ public class IndexExpressionIT extends ParallelStatsDisabledIT {
         Connection conn = DriverManager.getConnection(getUrl(), props);
 
         String dataTableName = generateUniqueName();
-        String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName;
         String indexName = generateUniqueName();
 
         try {
@@ -946,7 +934,6 @@ public class IndexExpressionIT extends ParallelStatsDisabledIT {
         ResultSet rs;
         PreparedStatement stmt;
         String dataTableName = generateUniqueName();
-        String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName;
         String indexName = generateUniqueName();
 
         Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
@@ -1058,7 +1045,6 @@ public class IndexExpressionIT extends ParallelStatsDisabledIT {
         PreparedStatement stmt;
 
         String dataTableName = generateUniqueName();
-        String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName;
         String indexName = generateUniqueName();
 
         Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
@@ -1181,7 +1167,6 @@ public class IndexExpressionIT extends ParallelStatsDisabledIT {
     private void helpTestUpdatableViewIndex(boolean local) throws Exception {
     	Connection conn = DriverManager.getConnection(getUrl());
         String dataTableName = generateUniqueName();
-        String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName;
         String indexName1 = generateUniqueName();
         String viewName = generateUniqueName();
         String indexName2 = generateUniqueName();


[02/25] phoenix git commit: PHOENIX-4096 Disallow DML operations on connections with CURRENT_SCN set

Posted by ja...@apache.org.
PHOENIX-4096 Disallow DML operations on connections with CURRENT_SCN set


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

Branch: refs/heads/master
Commit: 06f58d56c9e2b3badb5e3f6bd5092f03f58f00a8
Parents: 45079c4
Author: James Taylor <jt...@salesforce.com>
Authored: Thu Sep 28 11:24:22 2017 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Thu Sep 28 17:51:53 2017 -0700

----------------------------------------------------------------------
 .../phoenix/end2end/ExecuteStatementsIT.java    |   4 +-
 .../end2end/IndexToolForPartialBuildIT.java     |   5 +-
 .../phoenix/end2end/QueryExecWithoutSCNIT.java  |   3 +-
 .../org/apache/phoenix/end2end/QueryMoreIT.java |   2 +-
 .../org/apache/phoenix/end2end/UpgradeIT.java   |  23 +-
 .../end2end/index/PartialIndexRebuilderIT.java  |   3 +-
 .../EndToEndCoveredColumnsIndexBuilderIT.java   | 354 ------------
 .../index/covered/FailWithoutRetriesIT.java     | 140 -----
 .../org/apache/phoenix/tx/TransactionIT.java    |  23 +
 .../phoenix/compile/CreateTableCompiler.java    |  22 +-
 .../apache/phoenix/compile/UpsertCompiler.java  |   8 +-
 .../coprocessor/MetaDataRegionObserver.java     |   3 -
 .../phoenix/exception/SQLExceptionCode.java     |   3 +-
 .../apache/phoenix/execute/MutationState.java   |   8 -
 .../org/apache/phoenix/hbase/index/Indexer.java |  11 +-
 .../apache/phoenix/jdbc/PhoenixConnection.java  | 567 ++++++++++++-------
 .../phoenix/mapreduce/PhoenixInputFormat.java   |   5 +-
 .../index/PhoenixIndexImportDirectMapper.java   |   2 +-
 .../index/PhoenixIndexImportMapper.java         |   2 +-
 .../index/PhoenixIndexPartialBuildMapper.java   |   2 +-
 .../query/ConnectionQueryServicesImpl.java      |   1 +
 .../query/ConnectionlessQueryServicesImpl.java  |   1 +
 .../apache/phoenix/schema/MetaDataClient.java   |  33 +-
 .../java/org/apache/phoenix/util/JDBCUtil.java  |   4 +-
 .../org/apache/phoenix/util/PhoenixRuntime.java |  23 +-
 .../phoenix/compile/QueryCompilerTest.java      |  30 +-
 .../java/org/apache/phoenix/util/TestUtil.java  |   8 +-
 27 files changed, 442 insertions(+), 848 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/it/java/org/apache/phoenix/end2end/ExecuteStatementsIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ExecuteStatementsIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ExecuteStatementsIT.java
index c8c0d37..9c11144 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ExecuteStatementsIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ExecuteStatementsIT.java
@@ -17,11 +17,9 @@
  */
 package org.apache.phoenix.end2end;
 
-import static org.apache.phoenix.util.TestUtil.ATABLE_NAME;
 import static org.apache.phoenix.util.TestUtil.A_VALUE;
 import static org.apache.phoenix.util.TestUtil.BTABLE_NAME;
 import static org.apache.phoenix.util.TestUtil.B_VALUE;
-import static org.apache.phoenix.util.TestUtil.PTSDB_NAME;
 import static org.apache.phoenix.util.TestUtil.ROW6;
 import static org.apache.phoenix.util.TestUtil.ROW7;
 import static org.apache.phoenix.util.TestUtil.ROW8;
@@ -145,7 +143,7 @@ public class ExecuteStatementsIT extends ParallelStatsDisabledIT {
         conn.commit();
 
         String btableName = generateUniqueName();
-        ensureTableCreated(getUrl(),btableName, BTABLE_NAME, nextTimestamp()-2);
+        ensureTableCreated(getUrl(),btableName, BTABLE_NAME);
         statement = conn.prepareStatement(
                 "upsert into " + btableName + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
         statement.setString(1, "abc");

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/it/java/org/apache/phoenix/end2end/IndexToolForPartialBuildIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/IndexToolForPartialBuildIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/IndexToolForPartialBuildIT.java
index b71750a..19ffe1a 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/IndexToolForPartialBuildIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/IndexToolForPartialBuildIT.java
@@ -162,7 +162,8 @@ public class IndexToolForPartialBuildIT extends BaseOwnClusterIT {
             PTable pindexTable = PhoenixRuntime.getTable(conn, SchemaUtil.getTableName(schemaName, indxTable));
             assertEquals(PIndexState.BUILDING, pindexTable.getIndexState());
             assertEquals(rs.getLong(1), pindexTable.getTimeStamp());
-            //assert disabled timestamp is set correctly when index mutations are processed on the server
+
+            //assert disabled timestamp
             assertEquals(0, rs.getLong(2));
 
             String selectSql = String.format("SELECT LPAD(UPPER(NAME),11,'x')||'_xyz',ID FROM %s", fullTableName);
@@ -216,8 +217,6 @@ public class IndexToolForPartialBuildIT extends BaseOwnClusterIT {
                 assertEquals("xxUNAME" + i*1000 + "_xyz", rs.getString(1));
             }
             assertFalse(rs.next());
-
-           // conn.createStatement().execute(String.format("DROP INDEX  %s ON %s", indxTable, fullTableName));
         } finally {
             conn.close();
         }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryExecWithoutSCNIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryExecWithoutSCNIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryExecWithoutSCNIT.java
index 51d08d8..a18caf8 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryExecWithoutSCNIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryExecWithoutSCNIT.java
@@ -35,9 +35,8 @@ import org.junit.Test;
 public class QueryExecWithoutSCNIT extends ParallelStatsDisabledIT {
     @Test
     public void testScanNoSCN() throws Exception {
-        long ts = System.currentTimeMillis();
         String tenantId = getOrganizationId();
-        String tableName = initATableValues(tenantId, getDefaultSplits(tenantId), null, ts, getUrl());
+        String tableName = initATableValues(tenantId, getDefaultSplits(tenantId), null, null, getUrl());
         String query = "SELECT a_string, b_string FROM " + tableName + " WHERE organization_id=? and a_integer = 5";
         Properties props = new Properties(); // Test with no CurrentSCN property set
         Connection conn = DriverManager.getConnection(getUrl(), props);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryMoreIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryMoreIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryMoreIT.java
index 8397e4d..77cb19f 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryMoreIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryMoreIT.java
@@ -336,7 +336,7 @@ public class QueryMoreIT extends ParallelStatsDisabledIT {
     @Test // see - https://issues.apache.org/jira/browse/PHOENIX-1696
     public void testSelectColumnMoreThanOnce() throws Exception {
         Date date = new Date(System.currentTimeMillis());
-        initEntityHistoryTableValues("abcd", getDefaultSplits("abcd"), date, 100l);
+        initEntityHistoryTableValues("abcd", getDefaultSplits("abcd"), date, null);
         String query = "SELECT NEW_VALUE, NEW_VALUE FROM " + TestUtil.ENTITY_HISTORY_TABLE_NAME + " LIMIT 1";
         ResultSet rs = DriverManager.getConnection(getUrl()).createStatement().executeQuery(query);
         assertTrue(rs.next());

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/it/java/org/apache/phoenix/end2end/UpgradeIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UpgradeIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UpgradeIT.java
index 8cba241..4cb4642 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UpgradeIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UpgradeIT.java
@@ -20,7 +20,6 @@ package org.apache.phoenix.end2end;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.apache.phoenix.query.ConnectionQueryServicesImpl.UPGRADE_MUTEX;
-import static org.apache.phoenix.query.ConnectionQueryServicesImpl.UPGRADE_MUTEX_LOCKED;
 import static org.apache.phoenix.query.ConnectionQueryServicesImpl.UPGRADE_MUTEX_UNLOCKED;
 import static org.apache.phoenix.query.QueryConstants.BASE_TABLE_BASE_COLUMN_COUNT;
 import static org.apache.phoenix.query.QueryConstants.DIVERGED_VIEW_BASE_COLUMN_COUNT;
@@ -64,10 +63,16 @@ import org.apache.phoenix.query.ConnectionQueryServicesImpl;
 import org.apache.phoenix.query.DelegateConnectionQueryServices;
 import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.query.QueryServices;
-import org.apache.phoenix.schema.*;
+import org.apache.phoenix.schema.PMetaData;
+import org.apache.phoenix.schema.PName;
+import org.apache.phoenix.schema.PNameFactory;
+import org.apache.phoenix.schema.PTable;
+import org.apache.phoenix.schema.PTableType;
 import org.apache.phoenix.util.MetaDataUtil;
 import org.apache.phoenix.util.PhoenixRuntime;
+import org.apache.phoenix.util.PropertiesUtil;
 import org.apache.phoenix.util.SchemaUtil;
+import org.apache.phoenix.util.TestUtil;
 import org.apache.phoenix.util.UpgradeUtil;
 import org.junit.Before;
 import org.junit.Test;
@@ -490,7 +495,7 @@ public class UpgradeIT extends ParallelStatsDisabledIT {
             }
             
             // run upgrade
-            UpgradeUtil.upgradeTo4_5_0(conn.unwrap(PhoenixConnection.class));
+            upgradeTo4_5_0(conn);
             
             // Verify base column counts for tenant specific views
             for (int i = 0; i < 2 ; i++) {
@@ -508,6 +513,12 @@ public class UpgradeIT extends ParallelStatsDisabledIT {
         
         
     }
+
+    private static void upgradeTo4_5_0(Connection conn) throws SQLException {
+        PhoenixConnection pconn = conn.unwrap(PhoenixConnection.class);
+        pconn.setRunningUpgrade(true);
+        UpgradeUtil.upgradeTo4_5_0(pconn);
+    }
     
     private enum ColumnDiff {
         MORE, EQUAL, LESS
@@ -570,7 +581,7 @@ public class UpgradeIT extends ParallelStatsDisabledIT {
             checkBaseColumnCount(null, baseTableSchema, baseTableName, 0);
             
             // run upgrade
-            UpgradeUtil.upgradeTo4_5_0(conn.unwrap(PhoenixConnection.class));
+            upgradeTo4_5_0(conn);
 
             checkBaseColumnCount(tenantId, viewSchema, viewName, expectedBaseColumnCount);
             checkBaseColumnCount(null, baseTableSchema, baseTableName, BASE_TABLE_BASE_COLUMN_COUNT);
@@ -639,8 +650,8 @@ public class UpgradeIT extends ParallelStatsDisabledIT {
                     return true;
                 }
             };
-            try (PhoenixConnection phxConn = new PhoenixConnection(servicesWithUpgrade,
-                    conn.unwrap(PhoenixConnection.class), HConstants.LATEST_TIMESTAMP)) {
+            try (PhoenixConnection phxConn = new PhoenixConnection(servicesWithUpgrade, getUrl(), PropertiesUtil.deepCopy(TestUtil.TEST_PROPERTIES), 
+                    conn.unwrap(PhoenixConnection.class).getMetaDataCache())) {
                 try {
                     phxConn.createStatement().execute(
                             "CREATE TABLE " + generateUniqueName()

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/PartialIndexRebuilderIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/PartialIndexRebuilderIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/PartialIndexRebuilderIT.java
index 8bf2bc8..d0a06b4 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/PartialIndexRebuilderIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/PartialIndexRebuilderIT.java
@@ -895,7 +895,8 @@ public class PartialIndexRebuilderIT extends BaseUniqueNamesOwnClusterIT {
             assertEquals("0", rs.getString(1));
             assertEquals(indexStateOnFailure == PIndexState.DISABLE ? fullTableName : fullIndexName, stmt.getQueryPlan().getContext().getCurrentTable().getTable().getName().getString());
             TestUtil.removeCoprocessor(conn, fullIndexName, WriteFailingRegionObserver.class);
-            
+
+            clock.time += 1000;
             runIndexRebuilder();
             assertEquals(indexStateOnFailure == PIndexState.DISABLE ? PIndexState.INACTIVE : PIndexState.ACTIVE, TestUtil.getIndexState(conn, fullIndexName));
             clock.time += WAIT_AFTER_DISABLED;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/it/java/org/apache/phoenix/hbase/index/covered/EndToEndCoveredColumnsIndexBuilderIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/hbase/index/covered/EndToEndCoveredColumnsIndexBuilderIT.java b/phoenix-core/src/it/java/org/apache/phoenix/hbase/index/covered/EndToEndCoveredColumnsIndexBuilderIT.java
deleted file mode 100644
index 5aa0161..0000000
--- a/phoenix-core/src/it/java/org/apache/phoenix/hbase/index/covered/EndToEndCoveredColumnsIndexBuilderIT.java
+++ /dev/null
@@ -1,354 +0,0 @@
-/*
- * 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.hbase.index.covered;
-
-import static org.apache.phoenix.query.BaseTest.setUpConfigForMiniCluster;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-import java.io.IOException;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-
-import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.hbase.Cell;
-import org.apache.hadoop.hbase.Coprocessor;
-import org.apache.hadoop.hbase.HBaseTestingUtility;
-import org.apache.hadoop.hbase.HColumnDescriptor;
-import org.apache.hadoop.hbase.HTableDescriptor;
-import org.apache.hadoop.hbase.KeyValue;
-import org.apache.hadoop.hbase.client.HBaseAdmin;
-import org.apache.hadoop.hbase.client.HTable;
-import org.apache.hadoop.hbase.client.Put;
-import org.apache.hadoop.hbase.regionserver.Region;
-import org.apache.hadoop.hbase.util.Bytes;
-import org.apache.phoenix.coprocessor.BaseScannerRegionObserver.ReplayWrite;
-import org.apache.phoenix.end2end.NeedsOwnMiniClusterTest;
-import org.apache.phoenix.hbase.index.IndexTestingUtils;
-import org.apache.phoenix.hbase.index.Indexer;
-import org.apache.phoenix.hbase.index.TableName;
-import org.apache.phoenix.hbase.index.covered.update.ColumnReference;
-import org.apache.phoenix.hbase.index.scanner.Scanner;
-import org.apache.phoenix.util.EnvironmentEdge;
-import org.apache.phoenix.util.EnvironmentEdgeManager;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-/**
- * End-to-End test of just the {@link NonTxIndexBuilder}, but with a simple
- * {@link IndexCodec} and BatchCache implementation.
- */
-@Category(NeedsOwnMiniClusterTest.class)
-public class EndToEndCoveredColumnsIndexBuilderIT {
-
-  public class TestState {
-
-    private HTable table;
-    private long ts;
-    private VerifyingIndexCodec codec;
-
-    /**
-     * @param primary
-     * @param codec
-     * @param ts
-     */
-    public TestState(HTable primary, VerifyingIndexCodec codec, long ts) {
-      this.table = primary;
-      this.ts = ts;
-      this.codec = codec;
-    }
-
-  }
-
-  private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
-
-  private static final byte[] row = Bytes.toBytes("row");
-  private static final byte[] family = Bytes.toBytes("FAM");
-  private static final byte[] qual = Bytes.toBytes("qual");
-  private static final HColumnDescriptor FAM1 = new HColumnDescriptor(family);
-
-  @Rule
-  public TableName TestTable = new TableName();
-
-  private TestState state;
-
-  @BeforeClass
-  public static void setupCluster() throws Exception {
-    Configuration conf = UTIL.getConfiguration();
-    setUpConfigForMiniCluster(conf);
-    IndexTestingUtils.setupConfig(conf);
-    // disable version checking, so we can test against whatever version of HBase happens to be
-    // installed (right now, its generally going to be SNAPSHOT versions).
-    conf.setBoolean(Indexer.CHECK_VERSION_CONF_KEY, false);
-    UTIL.startMiniCluster();
-  }
-
-  @Before
-  public void setup() throws Exception {
-    this.state = setupTest(TestTable.getTableNameString());
-  }
-    
-  private interface TableStateVerifier {
-
-    /**
-     * Verify that the state of the table is correct. Should fail the unit test if it isn't as
-     * expected.
-     * @param state
-     */
-    public void verify(TableState state);
-
-  }
-
-  /**
-   * {@link TableStateVerifier} that ensures the kvs returned from the table match the passed
-   * {@link KeyValue}s when querying on the given columns.
-   */
-  private class ListMatchingVerifier implements TableStateVerifier {
-
-    private List<Cell> expectedKvs;
-    private ColumnReference[] columns;
-    private String msg;
-
-    public ListMatchingVerifier(String msg, List<Cell> kvs, ColumnReference... columns) {
-      this.expectedKvs = kvs;
-      this.columns = columns;
-      this.msg = msg;
-    }
-
-    @Override
-    public void verify(TableState state) {
-        IndexMetaData indexMetaData = new IndexMetaData() {
-
-            @Override
-            public boolean isImmutableRows() {
-                return false;
-            }
-
-            @Override
-            public ReplayWrite getReplayWrite() {
-                return null;
-            }
-              
-          };
-      try {
-        Scanner kvs =
-            ((LocalTableState) state).getIndexedColumnsTableState(Arrays.asList(columns), false, false, indexMetaData).getFirst();
-
-        int count = 0;
-        Cell kv;
-        while ((kv = kvs.next()) != null) {
-          Cell next = expectedKvs.get(count++);
-          assertEquals(
-            msg + ": Unexpected kv in table state!\nexpected v1: "
-                + Bytes.toString(next.getValue()) + "\nactual v1:" + Bytes.toString(kv.getValue()),
-            next, kv);
-        }
-
-        assertEquals(msg + ": Didn't find enough kvs in table state!", expectedKvs.size(), count);
-      } catch (IOException e) {
-        fail(msg + ": Got an exception while reading local table state! " + e.getMessage());
-      }
-    }
-  }
-
-  private class VerifyingIndexCodec extends CoveredIndexCodecForTesting {
-
-    private Queue<TableStateVerifier> verifiers = new ArrayDeque<TableStateVerifier>();
-
-    @Override
-    public Iterable<IndexUpdate> getIndexDeletes(TableState state, IndexMetaData context) {
-      verify(state);
-      return super.getIndexDeletes(state, context);
-    }
-
-    @Override
-    public Iterable<IndexUpdate> getIndexUpserts(TableState state, IndexMetaData context) {
-      verify(state);
-      return super.getIndexUpserts(state, context);
-    }
-
-    private void verify(TableState state) {
-      TableStateVerifier verifier = verifiers.poll();
-      if (verifier == null) return;
-      verifier.verify(state);
-    }
-  }
-  
-  /**
-   * Test that we see the expected values in a {@link TableState} when doing single puts against a
-   * region.
-   * @throws Exception on failure
-   */
-  @Test
-  public void testExpectedResultsInTableStateForSinglePut() throws Exception {
-    //just do a simple Put to start with
-    long ts = state.ts;
-    Put p = new Put(row, ts);
-    p.add(family, qual, Bytes.toBytes("v1"));
-    
-    // get all the underlying kvs for the put
-    final List<Cell> expectedKvs = new ArrayList<Cell>();
-    final List<Cell> allKvs = new ArrayList<Cell>();
-    allKvs.addAll(p.getFamilyMap().get(family));
-
-    // setup the verifier for the data we expect to write
-    // first call shouldn't have anything in the table
-    final ColumnReference familyRef =
-        new ColumnReference(EndToEndCoveredColumnsIndexBuilderIT.family, ColumnReference.ALL_QUALIFIERS);
-
-    VerifyingIndexCodec codec = state.codec;
-    codec.verifiers.add(new ListMatchingVerifier("cleanup state 1", expectedKvs, familyRef));
-    codec.verifiers.add(new ListMatchingVerifier("put state 1", allKvs, familyRef));
-
-    // do the actual put (no indexing will actually be done)
-    HTable primary = state.table;
-    primary.put(p);
-    primary.flushCommits();
-
-    // now we do another put to the same row. We should see just the old row state, followed by the
-    // new + old
-    p = new Put(row, ts + 1);
-    p.add(family, qual, Bytes.toBytes("v2"));
-    expectedKvs.addAll(allKvs);
-    // add them first b/c the ts is newer
-    allKvs.addAll(0, p.get(family, qual));
-    codec.verifiers.add(new ListMatchingVerifier("cleanup state 2", expectedKvs, familyRef));
-    codec.verifiers.add(new ListMatchingVerifier("put state 2", allKvs, familyRef));
-    
-    // do the actual put
-    primary.put(p);
-    primary.flushCommits();
-
-    // cleanup after ourselves
-    cleanup(state);
-  }
-
-  /**
-   * Similar to {@link #testExpectedResultsInTableStateForSinglePut()}, but against batches of puts.
-   * Previous implementations managed batches by playing current state against each element in the
-   * batch, rather than combining all the per-row updates into a single mutation for the batch. This
-   * test ensures that we see the correct expected state.
-   * @throws Exception on failure
-   */
-  @Test
-  public void testExpectedResultsInTableStateForBatchPuts() throws Exception {
-    long ts = state.ts;
-    // build up a list of puts to make, all on the same row
-    Put p1 = new Put(row, ts);
-    p1.add(family, qual, Bytes.toBytes("v1"));
-    Put p2 = new Put(row, ts + 1);
-    p2.add(family, qual, Bytes.toBytes("v2"));
-
-    // setup all the verifiers we need. This is just the same as above, but will be called twice
-    // since we need to iterate the batch.
-
-    // get all the underlying kvs for the put
-    final List<Cell> allKvs = new ArrayList<Cell>(2);
-    allKvs.addAll(p2.getFamilyCellMap().get(family));
-    allKvs.addAll(p1.getFamilyCellMap().get(family));
-
-    // setup the verifier for the data we expect to write
-    // both puts should be put into a single batch
-    final ColumnReference familyRef =
-        new ColumnReference(EndToEndCoveredColumnsIndexBuilderIT.family, ColumnReference.ALL_QUALIFIERS);
-    VerifyingIndexCodec codec = state.codec;
-    // no previous state in the table
-    codec.verifiers.add(new ListMatchingVerifier("cleanup state 1", Collections
-        .<Cell> emptyList(), familyRef));
-    codec.verifiers.add(new ListMatchingVerifier("put state 1", p1.getFamilyCellMap().get(family),
-        familyRef));
-
-    codec.verifiers.add(new ListMatchingVerifier("cleanup state 2", p1.getFamilyCellMap().get(family),
-        familyRef));
-    // kvs from both puts should be in the table now
-    codec.verifiers.add(new ListMatchingVerifier("put state 2", allKvs, familyRef));
-
-    // do the actual put (no indexing will actually be done)
-    HTable primary = state.table;
-    primary.setAutoFlush(false);
-    primary.put(Arrays.asList(p1, p2));
-    primary.flushCommits();
-
-    // cleanup after ourselves
-    cleanup(state);
-  }
-
-  /**
-   * @param tableName name of the table to create for the test
-   * @return the supporting state for the test
-   */
-  private TestState setupTest(String tableName) throws IOException {
-    byte[] tableNameBytes = Bytes.toBytes(tableName);
-    @SuppressWarnings("deprecation")
-    HTableDescriptor desc = new HTableDescriptor(tableNameBytes);
-    desc.addFamily(FAM1);
-    // add the necessary simple options to create the builder
-    Map<String, String> indexerOpts = new HashMap<String, String>();
-    // just need to set the codec - we are going to set it later, but we need something here or the
-    // initializer blows up.
-    indexerOpts.put(NonTxIndexBuilder.CODEC_CLASS_NAME_KEY,
-      CoveredIndexCodecForTesting.class.getName());
-    Indexer.enableIndexing(desc, NonTxIndexBuilder.class, indexerOpts, Coprocessor.PRIORITY_USER);
-
-    // create the table
-    HBaseAdmin admin = UTIL.getHBaseAdmin();
-    admin.createTable(desc);
-    HTable primary = new HTable(UTIL.getConfiguration(), tableNameBytes);
-
-    // overwrite the codec so we can verify the current state
-    Region region = UTIL.getMiniHBaseCluster().getRegions(tableNameBytes).get(0);
-    Indexer indexer =
-        (Indexer) region.getCoprocessorHost().findCoprocessor(Indexer.class.getName());
-    NonTxIndexBuilder builder =
-        (NonTxIndexBuilder) indexer.getBuilderForTesting();
-    VerifyingIndexCodec codec = new VerifyingIndexCodec();
-    builder.setIndexCodecForTesting(codec);
-
-    // setup the Puts we want to write
-    final long ts = System.currentTimeMillis();
-    EnvironmentEdge edge = new EnvironmentEdge() {
-
-      @Override
-      public long currentTime() {
-        return ts;
-      }
-    };
-    EnvironmentEdgeManager.injectEdge(edge);
-
-    return new TestState(primary, codec, ts);
-  }
-
-  /**
-   * Cleanup the test based on the passed state.
-   * @param state
-   */
-  private void cleanup(TestState state) throws IOException {
-    EnvironmentEdgeManager.reset();
-    state.table.close();
-    UTIL.deleteTable(state.table.getTableName());
-  }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/it/java/org/apache/phoenix/hbase/index/covered/FailWithoutRetriesIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/hbase/index/covered/FailWithoutRetriesIT.java b/phoenix-core/src/it/java/org/apache/phoenix/hbase/index/covered/FailWithoutRetriesIT.java
deleted file mode 100644
index ba8340c..0000000
--- a/phoenix-core/src/it/java/org/apache/phoenix/hbase/index/covered/FailWithoutRetriesIT.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * 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.hbase.index.covered;
-
-import static org.apache.phoenix.query.BaseTest.setUpConfigForMiniCluster;
-import static org.junit.Assert.fail;
-
-import java.io.IOException;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.hbase.DoNotRetryIOException;
-import org.apache.hadoop.hbase.HBaseTestingUtility;
-import org.apache.hadoop.hbase.HColumnDescriptor;
-import org.apache.hadoop.hbase.HConstants;
-import org.apache.hadoop.hbase.HTableDescriptor;
-import org.apache.hadoop.hbase.client.HBaseAdmin;
-import org.apache.hadoop.hbase.client.HTable;
-import org.apache.hadoop.hbase.client.Put;
-import org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException;
-import org.apache.hadoop.hbase.util.Bytes;
-import org.apache.phoenix.end2end.NeedsOwnMiniClusterTest;
-import org.apache.phoenix.hbase.index.IndexTestingUtils;
-import org.apache.phoenix.hbase.index.Indexer;
-import org.apache.phoenix.hbase.index.TableName;
-import org.apache.phoenix.hbase.index.builder.BaseIndexCodec;
-import org.apache.phoenix.hbase.index.covered.ColumnGroup;
-import org.apache.phoenix.hbase.index.covered.CoveredColumn;
-import org.apache.phoenix.hbase.index.covered.CoveredColumnIndexSpecifierBuilder;
-import org.apache.phoenix.hbase.index.covered.IndexMetaData;
-import org.apache.phoenix.hbase.index.covered.IndexUpdate;
-import org.apache.phoenix.hbase.index.covered.TableState;
-import org.apache.phoenix.hbase.index.util.IndexManagementUtil;
-import org.junit.BeforeClass;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-/**
- * If {@link DoNotRetryIOException} is not subclassed correctly (with the {@link String} constructor),
- * {@link MultiResponse#readFields(java.io.DataInput)} will not correctly deserialize the exception, and just return
- * <tt>null</tt> to the client, which then just goes and retries.
- */
-@Category(NeedsOwnMiniClusterTest.class)
-public class FailWithoutRetriesIT {
-
-    private static final Log LOG = LogFactory.getLog(FailWithoutRetriesIT.class);
-    @Rule
-    public TableName table = new TableName();
-
-    private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
-
-    private String getIndexTableName() {
-        return Bytes.toString(table.getTableName()) + "_index";
-    }
-
-    public static class FailingTestCodec extends BaseIndexCodec {
-
-        @Override
-        public Iterable<IndexUpdate> getIndexDeletes(TableState state, IndexMetaData context) throws IOException {
-            throw new RuntimeException("Intentionally failing deletes for " + FailWithoutRetriesIT.class.getName());
-        }
-
-        @Override
-        public Iterable<IndexUpdate> getIndexUpserts(TableState state, IndexMetaData context) throws IOException {
-            throw new RuntimeException("Intentionally failing upserts for " + FailWithoutRetriesIT.class.getName());
-        }
-    }
-
-    @BeforeClass
-    public static void setupCluster() throws Exception {
-        // setup and verify the config
-        Configuration conf = UTIL.getConfiguration();
-        setUpConfigForMiniCluster(conf);
-        IndexTestingUtils.setupConfig(conf);
-        IndexManagementUtil.ensureMutableIndexingCorrectlyConfigured(conf);
-        // start the cluster
-        UTIL.startMiniCluster();
-    }
-
-    /**
-     * If this test times out, then we didn't fail quickly enough. {@link Indexer} maybe isn't rethrowing the exception
-     * correctly?
-     * <p>
-     * We use a custom codec to enforce the thrown exception.
-     * 
-     * @throws Exception
-     */
-    @Test(timeout = 300000)
-    public void testQuickFailure() throws Exception {
-        // incorrectly setup indexing for the primary table - target index table doesn't exist, which
-        // should quickly return to the client
-        byte[] family = Bytes.toBytes("family");
-        ColumnGroup fam1 = new ColumnGroup(getIndexTableName());
-        // values are [col1]
-        fam1.add(new CoveredColumn(family, CoveredColumn.ALL_QUALIFIERS));
-        CoveredColumnIndexSpecifierBuilder builder = new CoveredColumnIndexSpecifierBuilder();
-        // add the index family
-        builder.addIndexGroup(fam1);
-        // usually, we would create the index table here, but we don't for the sake of the test.
-
-        // setup the primary table
-        String primaryTable = Bytes.toString(table.getTableName());
-        @SuppressWarnings("deprecation")
-        HTableDescriptor pTable = new HTableDescriptor(primaryTable);
-        pTable.addFamily(new HColumnDescriptor(family));
-        // override the codec so we can use our test one
-        builder.build(pTable, FailingTestCodec.class);
-
-        // create the primary table
-        HBaseAdmin admin = UTIL.getHBaseAdmin();
-        admin.createTable(pTable);
-        Configuration conf = new Configuration(UTIL.getConfiguration());
-        // up the number of retries/wait time to make it obvious that we are failing with retries here
-        conf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 20);
-        conf.setLong(HConstants.HBASE_CLIENT_PAUSE, 1000);
-        HTable primary = new HTable(conf, primaryTable);
-        primary.setAutoFlush(false, true);
-
-        // do a simple put that should be indexed
-        Put p = new Put(Bytes.toBytes("row"));
-        p.add(family, null, Bytes.toBytes("value"));
-        primary.put(p);
-        try {
-            primary.flushCommits();
-            fail("Shouldn't have gotten a successful write to the primary table");
-        } catch (RetriesExhaustedWithDetailsException e) {
-            LOG.info("Correclty got a failure of the put!");
-        }
-        primary.close();
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/it/java/org/apache/phoenix/tx/TransactionIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/tx/TransactionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/tx/TransactionIT.java
index c76e19c..9286c2e 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/tx/TransactionIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/tx/TransactionIT.java
@@ -44,6 +44,8 @@ import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.query.QueryServicesOptions;
 import org.apache.phoenix.schema.PTableKey;
 import org.apache.phoenix.transaction.PhoenixTransactionContext;
+import org.apache.phoenix.util.EnvironmentEdgeManager;
+import org.apache.phoenix.util.PhoenixRuntime;
 import org.apache.phoenix.util.PropertiesUtil;
 import org.apache.phoenix.util.StringUtil;
 import org.apache.phoenix.util.TestUtil;
@@ -52,6 +54,27 @@ import org.junit.Test;
 public class TransactionIT  extends ParallelStatsDisabledIT {
 
     @Test
+    public void testQueryWithSCN() throws Exception {
+        String tableName = generateUniqueName();
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        try (Connection conn = DriverManager.getConnection(getUrl(), props);) {
+            conn.createStatement().execute(
+                    "CREATE TABLE " + tableName + " (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR) TRANSACTIONAL=true");
+        }
+        props.put(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(EnvironmentEdgeManager.currentTimeMillis()));
+        try (Connection conn = DriverManager.getConnection(getUrl(), props);) {
+            try {
+            	conn.createStatement().executeQuery("SELECT * FROM " + tableName);
+                fail();
+            } catch (SQLException e) {
+                assertEquals("Unexpected Exception",
+                        SQLExceptionCode.CANNOT_START_TRANSACTION_WITH_SCN_SET
+                                .getErrorCode(), e.getErrorCode());
+            }
+        }
+    }
+
+    @Test
     public void testReCreateTxnTableAfterDroppingExistingNonTxnTable() throws SQLException {
         String tableName = generateUniqueName();
         Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/main/java/org/apache/phoenix/compile/CreateTableCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/CreateTableCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/CreateTableCompiler.java
index 6448edc..4e5580a 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/CreateTableCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/CreateTableCompiler.java
@@ -52,7 +52,6 @@ import org.apache.phoenix.parse.PrimaryKeyConstraint;
 import org.apache.phoenix.parse.SQLParser;
 import org.apache.phoenix.parse.SelectStatement;
 import org.apache.phoenix.parse.TableName;
-import org.apache.phoenix.query.DelegateConnectionQueryServices;
 import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.schema.ColumnRef;
 import org.apache.phoenix.schema.MetaDataClient;
@@ -84,7 +83,6 @@ public class CreateTableCompiler {
         final PhoenixConnection connection = statement.getConnection();
         ColumnResolver resolver = FromCompiler.getResolverForCreation(create, connection);
         PTableType type = create.getTableType();
-        PhoenixConnection connectionToBe = connection;
         PTable parentToBe = null;
         ViewType viewTypeToBe = null;
         Scan scan = new Scan();
@@ -148,24 +146,6 @@ public class CreateTableCompiler {
                     viewStatementToBe = QueryUtil.getViewStatement(baseTableName.getSchemaName(), baseTableName.getTableName(), buf.toString());
                 }
                 if (viewTypeToBe != ViewType.MAPPED) {
-                    Long scn = connection.getSCN();
-                    connectionToBe = (scn != null || tableRef.getTable().isTransactional()) ? connection :
-                        // If we haved no SCN on our connection and the base table is not transactional, freeze the SCN at when
-                        // the base table was resolved to prevent any race condition on
-                        // the error checking we do for the base table. The only potential
-                        // issue is if the base table lives on a different region server
-                        // than the new table will, then we're relying here on the system
-                        // clocks being in sync.
-                        new PhoenixConnection(
-                            // When the new table is created, we still want to cache it
-                            // on our connection.
-                            new DelegateConnectionQueryServices(connection.getQueryServices()) {
-                                @Override
-                                public void addTable(PTable table, long resolvedTime) throws SQLException {
-                                    connection.addTable(table, resolvedTime);
-                                }
-                            },
-                            connection, tableRef.getCurrentTime()+1);
                     viewColumnConstantsToBe = new byte[nColumns][];
                     ViewWhereExpressionVisitor visitor = new ViewWhereExpressionVisitor(parentToBe, viewColumnConstantsToBe);
                     where.accept(visitor);
@@ -201,7 +181,7 @@ public class CreateTableCompiler {
             throw new SQLExceptionInfo.Builder(SQLExceptionCode.SPLIT_POINT_NOT_CONSTANT)
                 .setMessage("Node: " + node).build().buildException();
         }
-        final MetaDataClient client = new MetaDataClient(connectionToBe);
+        final MetaDataClient client = new MetaDataClient(connection);
         final PTable parent = parentToBe;
         
         return new BaseMutationPlan(context, operation) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java
index 6f45e28..6445894 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java
@@ -352,8 +352,12 @@ public class UpsertCompiler {
         // - transactional table with a connection having an SCN
         if (table.getType() == PTableType.VIEW && table.getViewType().isReadOnly()) {
             throw new ReadOnlyTableException(schemaName,tableName);
-        }
-        else if (table.isTransactional() && connection.getSCN() != null) {
+        } else if (connection.isBuildingIndex() && table.getType() != PTableType.INDEX) {
+            throw new SQLExceptionInfo.Builder(SQLExceptionCode.ONLY_INDEX_UPDATABLE_AT_SCN)
+            .setSchemaName(schemaName)
+            .setTableName(tableName)
+            .build().buildException();
+        } else if (table.isTransactional() && connection.getSCN() != null) {
             throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_SPECIFY_SCN_FOR_TXN_TABLE).setSchemaName(schemaName)
             .setTableName(tableName).build().buildException();
         }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataRegionObserver.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataRegionObserver.java b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataRegionObserver.java
index 8f02901..5717c70 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataRegionObserver.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataRegionObserver.java
@@ -530,9 +530,6 @@ public class MetaDataRegionObserver extends BaseRegionObserver {
             int indexRebuildRpcRetriesCounter =
                     config.getInt(QueryServices.INDEX_REBUILD_RPC_RETRIES_COUNTER,
                         QueryServicesOptions.DEFAULT_INDEX_REBUILD_RPC_RETRIES_COUNTER);
-            // Set SCN so that we don't ping server and have the upper bound set back to
-            // the timestamp when the failure occurred.
-            props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(Long.MAX_VALUE));
             // Set various phoenix and hbase level timeouts and rpc retries
             props.setProperty(QueryServices.THREAD_TIMEOUT_MS_ATTRIB,
                 Long.toString(indexRebuildQueryTimeoutMs));

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java b/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
index 76cc2ba..f25f7f1 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
@@ -183,7 +183,8 @@ public enum SQLExceptionCode {
      ROWTIMESTAMP_NOT_ALLOWED_ON_VIEW(531, "42908", "Declaring a column as row_timestamp is not allowed for views."),
      INVALID_SCN(532, "42909", "Value of SCN cannot be less than zero."),
      INVALID_REPLAY_AT(533, "42910", "Value of REPLAY_AT cannot be less than zero."),
-     UNEQUAL_SCN_AND_REPLAY_AT(534, "42911", "If both specified, values of CURRENT_SCN and REPLAY_AT must be equal."),
+     UNEQUAL_SCN_AND_BUILD_INDEX_AT(534, "42911", "If both specified, values of CURRENT_SCN and BUILD_INDEX_AT must be equal."),
+     ONLY_INDEX_UPDATABLE_AT_SCN(535, "42912", "Only an index may be updated when the BUILD_INDEX_AT property is specified"),
      /**
      * HBase and Phoenix specific implementation defined sub-classes.
      * Column family related exceptions.

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/main/java/org/apache/phoenix/execute/MutationState.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/execute/MutationState.java b/phoenix-core/src/main/java/org/apache/phoenix/execute/MutationState.java
index f8cf9ad..4f96207 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/execute/MutationState.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/execute/MutationState.java
@@ -585,14 +585,6 @@ public class MutationState implements SQLCloseable {
                 rowMutationsPertainingToIndex = rowMutations;
             }
             mutationList.addAll(rowMutations);
-            if (connection.isReplayMutations()) {
-                // Propagate IGNORE_NEWER_MUTATIONS when replaying mutations since there will be
-                // future dated data row mutations that will get in the way of generating the
-                // correct index rows on replay.
-                for (Mutation mutation : rowMutations) {
-                    mutation.setAttribute(BaseScannerRegionObserver.REPLAY_WRITES, BaseScannerRegionObserver.REPLAY_TABLE_AND_INDEX_WRITES);
-                }
-            }
             if (mutationsPertainingToIndex != null) mutationsPertainingToIndex
                     .addAll(rowMutationsPertainingToIndex);
         }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/06f58d56/phoenix-core/src/main/java/org/apache/phoenix/hbase/index/Indexer.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/hbase/index/Indexer.java b/phoenix-core/src/main/java/org/apache/phoenix/hbase/index/Indexer.java
index e9d735d..8957b30 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/hbase/index/Indexer.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/hbase/index/Indexer.java
@@ -99,7 +99,6 @@ import org.apache.phoenix.trace.TracingUtils;
 import org.apache.phoenix.trace.util.NullSpan;
 import org.apache.phoenix.util.EnvironmentEdgeManager;
 import org.apache.phoenix.util.IndexUtil;
-import org.apache.phoenix.util.MetaDataUtil;
 import org.apache.phoenix.util.PhoenixRuntime;
 import org.apache.phoenix.util.PropertiesUtil;
 import org.apache.phoenix.util.QueryUtil;
@@ -395,14 +394,6 @@ public class Indexer extends BaseRegionObserver {
         "Somehow didn't return an index update but also didn't propagate the failure to the client!");
   }
 
-  // Assume time stamp of mutation a client defined time stamp if it's not within
-  // a factor of ten of the current time.
-  // TODO: get rid of this and have client pass LATEST_TIMESTAMP unless an SCN is set
-  private static boolean isProbablyClientControlledTimeStamp(Mutation m) {
-      double ratio = EnvironmentEdgeManager.currentTimeMillis() / MetaDataUtil.getClientTimeStamp(m);
-      return ratio > 10 || ratio < 0.10;
-  }
-   
   private static void setTimeStamp(KeyValue kv, byte[] tsBytes) {
       int tsOffset = kv.getTimestampOffset();
       System.arraycopy(tsBytes, 0, kv.getBuffer(), tsOffset, Bytes.SIZEOF_LONG);
@@ -470,7 +461,7 @@ public class Indexer extends BaseRegionObserver {
       
       Mutation firstMutation = miniBatchOp.getOperation(0);
       ReplayWrite replayWrite = this.builder.getReplayWrite(firstMutation);
-      boolean resetTimeStamp = replayWrite == null && !isProbablyClientControlledTimeStamp(firstMutation);
+      boolean resetTimeStamp = replayWrite == null;
       long now = EnvironmentEdgeManager.currentTimeMillis();
       byte[] byteNow = Bytes.toBytes(now);
       for (int i = 0; i < miniBatchOp.size(); i++) {


[20/25] phoenix git commit: PHOENIX-4248 Breakup IndexExpressionIT into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/3d9adc6f/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexIT.java
deleted file mode 100644
index b540ad1..0000000
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexIT.java
+++ /dev/null
@@ -1,1194 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- *   or more contributor license agreements.  See the NOTICE file
- *   distributed with this work for additional information
- *   regarding copyright ownership.  The ASF licenses this file
- *   to you under the Apache License, Version 2.0 (the
- *   "License"); you may not use this file except in compliance
- *   with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- *   Unless required by applicable law or agreed to in writing, software
- *   distributed under the License is distributed on an "AS IS" BASIS,
- *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *   See the License for the specific language governing permissions and
- *   limitations under the License.
- */
-
-package org.apache.phoenix.end2end.index;
-
-import static org.apache.phoenix.query.QueryConstants.MILLIS_IN_DAY;
-import static org.apache.phoenix.util.TestUtil.ROW5;
-import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.io.IOException;
-import java.math.BigDecimal;
-import java.sql.Connection;
-import java.sql.Date;
-import java.sql.DriverManager;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.Properties;
-import java.util.Random;
-
-import org.apache.hadoop.hbase.Cell;
-import org.apache.hadoop.hbase.CellScanner;
-import org.apache.hadoop.hbase.HConstants;
-import org.apache.hadoop.hbase.HTableDescriptor;
-import org.apache.hadoop.hbase.KeyValue;
-import org.apache.hadoop.hbase.client.HBaseAdmin;
-import org.apache.hadoop.hbase.client.HTableInterface;
-import org.apache.hadoop.hbase.client.Result;
-import org.apache.hadoop.hbase.client.ResultScanner;
-import org.apache.hadoop.hbase.client.Scan;
-import org.apache.hadoop.hbase.ipc.PhoenixRpcSchedulerFactory;
-import org.apache.phoenix.compile.ColumnResolver;
-import org.apache.phoenix.compile.FromCompiler;
-import org.apache.phoenix.end2end.ParallelStatsDisabledIT;
-import org.apache.phoenix.exception.SQLExceptionCode;
-import org.apache.phoenix.jdbc.PhoenixConnection;
-import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
-import org.apache.phoenix.jdbc.PhoenixResultSet;
-import org.apache.phoenix.jdbc.PhoenixStatement;
-import org.apache.phoenix.parse.NamedTableNode;
-import org.apache.phoenix.parse.TableName;
-import org.apache.phoenix.query.BaseTest;
-import org.apache.phoenix.schema.PTable;
-import org.apache.phoenix.schema.PTableImpl;
-import org.apache.phoenix.schema.PTableKey;
-import org.apache.phoenix.schema.PTableType;
-import org.apache.phoenix.util.DateUtil;
-import org.apache.phoenix.util.EnvironmentEdgeManager;
-import org.apache.phoenix.util.PropertiesUtil;
-import org.apache.phoenix.util.QueryUtil;
-import org.apache.phoenix.util.ReadOnlyProps;
-import org.apache.phoenix.util.SchemaUtil;
-import org.apache.phoenix.util.TestUtil;
-import org.apache.phoenix.util.TransactionUtil;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public abstract class IndexIT extends ParallelStatsDisabledIT {
-    private static final Random RAND = new Random();
-
-    private final boolean localIndex;
-    private final boolean transactional;
-    private final boolean mutable;
-    private final String tableDDLOptions;
-
-    protected IndexIT(boolean localIndex, boolean mutable, boolean transactional, boolean columnEncoded) {
-        this.localIndex = localIndex;
-        this.transactional = transactional;
-        this.mutable = mutable;
-        StringBuilder optionBuilder = new StringBuilder();
-        if (!columnEncoded) {
-            if (optionBuilder.length()!=0)
-                optionBuilder.append(",");
-            optionBuilder.append("COLUMN_ENCODED_BYTES=0");
-        }
-        if (!mutable) {
-            if (optionBuilder.length()!=0)
-                optionBuilder.append(",");
-            optionBuilder.append("IMMUTABLE_ROWS=true");
-            if (!columnEncoded) {
-                optionBuilder.append(",IMMUTABLE_STORAGE_SCHEME="+PTableImpl.ImmutableStorageScheme.ONE_CELL_PER_COLUMN);
-            }
-        }
-        if (transactional) {
-            if (optionBuilder.length()!=0)
-                optionBuilder.append(",");
-            optionBuilder.append(" TRANSACTIONAL=true ");
-        }
-        this.tableDDLOptions = optionBuilder.toString();
-    }
-
-    @Test
-    public void testIndexWithNullableFixedWithCols() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String tableName = "TBL_" + generateUniqueName();
-        String indexName = "IND_" + generateUniqueName();
-        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
-
-        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
-            conn.setAutoCommit(false);
-            String ddl ="CREATE TABLE " + fullTableName + TestUtil.TEST_TABLE_SCHEMA + tableDDLOptions;
-            Statement stmt = conn.createStatement();
-            stmt.execute(ddl);
-            BaseTest.populateTestTable(fullTableName);
-            ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullTableName
-                    + " (char_col1 ASC, int_col1 ASC)"
-                    + " INCLUDE (long_col1, long_col2)";
-            stmt.execute(ddl);
-
-            String query = "SELECT d.char_col1, int_col1 from " + fullTableName + " as d";
-            ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            if(localIndex) {
-                assertEquals(
-                        "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + tableName + " [1]\n" +
-                                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                                "CLIENT MERGE SORT",
-                                QueryUtil.getExplainPlan(rs));
-            } else {
-                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + indexName + "\n"
-                        + "    SERVER FILTER BY FIRST KEY ONLY", QueryUtil.getExplainPlan(rs));
-            }
-
-            rs = conn.createStatement().executeQuery(query);
-            assertTrue(rs.next());
-            assertEquals("chara", rs.getString(1));
-            assertEquals("chara", rs.getString("char_col1"));
-            assertEquals(2, rs.getInt(2));
-            assertTrue(rs.next());
-            assertEquals("chara", rs.getString(1));
-            assertEquals(3, rs.getInt(2));
-            assertTrue(rs.next());
-            assertEquals("chara", rs.getString(1));
-            assertEquals(4, rs.getInt(2));
-            assertFalse(rs.next());
-
-            conn.createStatement().execute("DROP INDEX " + indexName + " ON " + fullTableName);
-
-            query = "SELECT char_col1, int_col1 from " + fullTableName;
-            rs = conn.createStatement().executeQuery(query);
-            assertTrue(rs.next());
-
-            query = "SELECT char_col1, int_col1 from "+indexName;
-            try{
-                rs = conn.createStatement().executeQuery(query);
-                fail();
-            } catch (SQLException e) {
-                assertEquals(SQLExceptionCode.TABLE_UNDEFINED.getErrorCode(), e.getErrorCode());
-            }
-        }
-    }
-
-    @Test
-    public void testDeleteFromAllPKColumnIndex() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String tableName = "TBL_" + generateUniqueName();
-        String indexName = "IND_" + generateUniqueName();
-        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
-        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
-        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
-            conn.setAutoCommit(false);
-            String ddl ="CREATE TABLE " + fullTableName + TestUtil.TEST_TABLE_SCHEMA + tableDDLOptions;
-            Statement stmt = conn.createStatement();
-            stmt.execute(ddl);
-            BaseTest.populateTestTable(fullTableName);
-            ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullTableName
-                        + " (long_pk, varchar_pk)"
-                        + " INCLUDE (long_col1, long_col2)";
-            stmt.execute(ddl);
-
-            ResultSet rs;
-
-            rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullTableName);
-            assertTrue(rs.next());
-            assertEquals(3,rs.getInt(1));
-            rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullIndexName);
-            assertTrue(rs.next());
-            assertEquals(3,rs.getInt(1));
-
-            String dml = "DELETE from " + fullTableName + " WHERE long_col2 = 4";
-            assertEquals(1,conn.createStatement().executeUpdate(dml));
-            conn.commit();
-
-            String query = "SELECT /*+ NO_INDEX */ long_pk FROM " + fullTableName;
-            rs = conn.createStatement().executeQuery(query);
-            assertTrue(rs.next());
-            assertEquals(1L, rs.getLong(1));
-            assertTrue(rs.next());
-            assertEquals(3L, rs.getLong(1));
-            assertFalse(rs.next());
-
-            query = "SELECT long_pk FROM " + fullTableName;
-            rs = conn.createStatement().executeQuery(query);
-            assertTrue(rs.next());
-            assertEquals(1L, rs.getLong(1));
-            assertTrue(rs.next());
-            assertEquals(3L, rs.getLong(1));
-            assertFalse(rs.next());
-
-            query = "SELECT * FROM " + fullIndexName;
-            rs = conn.createStatement().executeQuery(query);
-            assertTrue(rs.next());
-            assertEquals(1L, rs.getLong(1));
-            assertTrue(rs.next());
-            assertEquals(3L, rs.getLong(1));
-            assertFalse(rs.next());
-
-            conn.createStatement().execute("DROP INDEX " + indexName + " ON " + fullTableName);
-        }
-    }
-
-    @Test
-    public void testCreateIndexAfterUpsertStarted() throws Exception {
-        testCreateIndexAfterUpsertStarted(false, 
-                SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, generateUniqueName()),
-                SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, generateUniqueName()));
-    }
-
-    @Test
-    public void testCreateIndexAfterUpsertStartedTxnl() throws Exception {
-        if (transactional) {
-            testCreateIndexAfterUpsertStarted(true, 
-                    SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, generateUniqueName()),
-                    SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, generateUniqueName()));
-        }
-    }
-
-    private void testCreateIndexAfterUpsertStarted(boolean readOwnWrites, String fullTableName, String fullIndexName) throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        try (Connection conn1 = DriverManager.getConnection(getUrl(), props)) {
-            conn1.setAutoCommit(true);
-            String ddl ="CREATE TABLE " + fullTableName + TestUtil.TEST_TABLE_SCHEMA + tableDDLOptions;
-            Statement stmt1 = conn1.createStatement();
-            stmt1.execute(ddl);
-            BaseTest.populateTestTable(fullTableName);
-
-            ResultSet rs;
-
-            rs = conn1.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullTableName);
-            assertTrue(rs.next());
-            assertEquals(3,rs.getInt(1));
-
-            try (Connection conn2 = DriverManager.getConnection(getUrl(), props)) {
-
-                String upsert = "UPSERT INTO " + fullTableName
-                        + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
-                PreparedStatement pstmt2 = conn2.prepareStatement(upsert);
-                pstmt2.setString(1, "varchar4");
-                pstmt2.setString(2, "char4");
-                pstmt2.setInt(3, 4);
-                pstmt2.setLong(4, 4L);
-                pstmt2.setBigDecimal(5, new BigDecimal(4.0));
-                Date date = DateUtil.parseDate("2015-01-01 00:00:00");
-                pstmt2.setDate(6, date);
-                pstmt2.setString(7, "varchar_a");
-                pstmt2.setString(8, "chara");
-                pstmt2.setInt(9, 2);
-                pstmt2.setLong(10, 2L);
-                pstmt2.setBigDecimal(11, new BigDecimal(2.0));
-                pstmt2.setDate(12, date);
-                pstmt2.setString(13, "varchar_b");
-                pstmt2.setString(14, "charb");
-                pstmt2.setInt(15, 3);
-                pstmt2.setLong(16, 3L);
-                pstmt2.setBigDecimal(17, new BigDecimal(3.0));
-                pstmt2.setDate(18, date);
-                pstmt2.executeUpdate();
-
-                if (readOwnWrites) {
-                    String query = "SELECT long_pk FROM " + fullTableName + " WHERE long_pk=4";
-                    rs = conn2.createStatement().executeQuery(query);
-                    assertTrue(rs.next());
-                    assertFalse(rs.next());
-                }
-
-                String indexName = SchemaUtil.getTableNameFromFullName(fullIndexName);
-                ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullTableName
-                        + " (long_pk, varchar_pk)"
-                        + " INCLUDE (long_col1, long_col2)";
-                stmt1.execute(ddl);
-
-                /*
-                 * Commit upsert after index created through different connection.
-                 * This forces conn2 (which doesn't know about the index yet) to update the metadata
-                 * at commit time, recognize the new index, and generate the correct metadata (or index
-                 * rows for immutable indexes).
-                 *
-                 * For transactional data, this is problematic because the index
-                 * gets a timestamp *after* the commit timestamp of conn2 and thus won't be seen during
-                 * the commit. Also, when the index is being built, the data hasn't yet been committed
-                 * and thus won't be part of the initial index build (fixed by PHOENIX-2446).
-                 */
-                conn2.commit();
-
-                stmt1 = conn1.createStatement();
-                rs = stmt1.executeQuery("SELECT COUNT(*) FROM " + fullTableName);
-                assertTrue(rs.next());
-                assertEquals(4,rs.getInt(1));
-                assertEquals(fullIndexName, stmt1.unwrap(PhoenixStatement.class).getQueryPlan().getTableRef().getTable().getName().getString());
-
-                String query = "SELECT /*+ NO_INDEX */ long_pk FROM " + fullTableName;
-                rs = conn1.createStatement().executeQuery(query);
-                assertTrue(rs.next());
-                assertEquals(1L, rs.getLong(1));
-                assertTrue(rs.next());
-                assertEquals(2L, rs.getLong(1));
-                assertTrue(rs.next());
-                assertEquals(3L, rs.getLong(1));
-                assertTrue(rs.next());
-                assertEquals(4L, rs.getLong(1));
-                assertFalse(rs.next());
-            }
-        }
-    }
-
-    @Test
-    public void testDeleteFromNonPKColumnIndex() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String tableName = "TBL_" + generateUniqueName();
-        String indexName = "IND_" + generateUniqueName();
-        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
-        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
-
-        String ddl ="CREATE TABLE " + fullTableName + TestUtil.TEST_TABLE_SCHEMA + tableDDLOptions;
-        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
-            conn.setAutoCommit(false);
-            Statement stmt = conn.createStatement();
-            stmt.execute(ddl);
-            BaseTest.populateTestTable(fullTableName);
-            ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullTableName
-                        + " (long_col1, long_col2)"
-                        + " INCLUDE (decimal_col1, decimal_col2)";
-            stmt.execute(ddl);
-        }
-        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
-            ResultSet rs;
-
-            rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullTableName);
-            assertTrue(rs.next());
-            assertEquals(3,rs.getInt(1));
-            rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullIndexName);
-            assertTrue(rs.next());
-            assertEquals(3,rs.getInt(1));
-
-            String dml = "DELETE from " + fullTableName + " WHERE long_col2 = 4";
-            assertEquals(1,conn.createStatement().executeUpdate(dml));
-            conn.commit();
-
-            // query the data table
-            String query = "SELECT /*+ NO_INDEX */ long_pk FROM " + fullTableName;
-            rs = conn.createStatement().executeQuery(query);
-            assertTrue(rs.next());
-            assertEquals(1L, rs.getLong(1));
-            assertTrue(rs.next());
-            assertEquals(3L, rs.getLong(1));
-            assertFalse(rs.next());
-
-            // query the index table
-            query = "SELECT long_pk FROM " + fullTableName + " ORDER BY long_col1";
-            rs = conn.createStatement().executeQuery(query);
-            assertTrue(rs.next());
-            assertEquals(1L, rs.getLong(1));
-            assertTrue(rs.next());
-            assertEquals(3L, rs.getLong(1));
-            assertFalse(rs.next());
-
-            conn.createStatement().execute("DROP INDEX " + indexName + " ON " + fullTableName);
-        }
-    }
-
-    @Test
-    public void testGroupByCount() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String tableName = "TBL_" + generateUniqueName();
-        String indexName = "IND_" + generateUniqueName();
-        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
-        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
-            conn.setAutoCommit(false);
-            String ddl ="CREATE TABLE " + fullTableName + TestUtil.TEST_TABLE_SCHEMA + tableDDLOptions;
-            Statement stmt = conn.createStatement();
-            stmt.execute(ddl);
-            BaseTest.populateTestTable(fullTableName);
-            ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullTableName + " (int_col2)";
-            stmt.execute(ddl);
-            ResultSet rs;
-            rs = conn.createStatement().executeQuery("SELECT int_col2, COUNT(*) FROM " + fullTableName + " GROUP BY int_col2");
-            assertTrue(rs.next());
-            assertEquals(1,rs.getInt(2));
-        }
-    }
-
-    @Test
-    public void testSelectDistinctOnTableWithSecondaryImmutableIndex() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String tableName = "TBL_" + generateUniqueName();
-        String indexName = "IND_" + generateUniqueName();
-        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
-        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
-            conn.setAutoCommit(false);
-            String ddl ="CREATE TABLE " + fullTableName + TestUtil.TEST_TABLE_SCHEMA + tableDDLOptions;
-            Statement stmt = conn.createStatement();
-            stmt.execute(ddl);
-            BaseTest.populateTestTable(fullTableName);
-            ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullTableName + " (int_col2)";
-            conn.createStatement().execute(ddl);
-            ResultSet rs = conn.createStatement().executeQuery("SELECT distinct int_col2 FROM " + fullTableName + " where int_col2 > 0");
-            assertTrue(rs.next());
-            assertEquals(3, rs.getInt(1));
-            assertTrue(rs.next());
-            assertEquals(4, rs.getInt(1));
-            assertTrue(rs.next());
-            assertEquals(5, rs.getInt(1));
-            assertFalse(rs.next());
-        }
-    }
-
-    @Test
-    public void testInClauseWithIndexOnColumnOfUsignedIntType() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String tableName = "TBL_" + generateUniqueName();
-        String indexName = "IND_" + generateUniqueName();
-        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
-        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
-            conn.setAutoCommit(false);
-            String ddl ="CREATE TABLE " + fullTableName + TestUtil.TEST_TABLE_SCHEMA + tableDDLOptions;
-            Statement stmt = conn.createStatement();
-            stmt.execute(ddl);
-            BaseTest.populateTestTable(fullTableName);
-            ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullTableName + " (int_col1)";
-            stmt.execute(ddl);
-            ResultSet rs = conn.createStatement().executeQuery("SELECT int_col1 FROM " + fullTableName + " where int_col1 IN (1, 2, 3, 4)");
-            assertTrue(rs.next());
-            assertEquals(2, rs.getInt(1));
-            assertTrue(rs.next());
-            assertEquals(3, rs.getInt(1));
-            assertTrue(rs.next());
-            assertEquals(4, rs.getInt(1));
-            assertFalse(rs.next());
-        }
-    }
-
-    @Test
-    public void createIndexOnTableWithSpecifiedDefaultCF() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String tableName = "TBL_" + generateUniqueName();
-        String indexName = "IND_" + generateUniqueName();
-        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
-        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
-        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
-            conn.setAutoCommit(false);
-            String query;
-            ResultSet rs;
-            String ddl ="CREATE TABLE " + fullTableName
-                    + " (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR) DEFAULT_COLUMN_FAMILY='A'" + (!tableDDLOptions.isEmpty() ? "," + tableDDLOptions : "");
-            Statement stmt = conn.createStatement();
-            stmt.execute(ddl);
-
-            query = "SELECT * FROM " + tableName;
-            rs = conn.createStatement().executeQuery(query);
-            assertFalse(rs.next());
-
-            String options = localIndex ? "SALT_BUCKETS=10, MULTI_TENANT=true, IMMUTABLE_ROWS=true, DISABLE_WAL=true" : "";
-            conn.createStatement().execute(
-                    "CREATE INDEX " + indexName + " ON " + fullTableName + " (v1) INCLUDE (v2) " + options);
-            query = "SELECT * FROM " + fullIndexName;
-            rs = conn.createStatement().executeQuery(query);
-            assertFalse(rs.next());
-
-            //check options set correctly on index
-            TableName indexTableName = TableName.create(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
-            NamedTableNode indexNode = NamedTableNode.create(null, indexTableName, null);
-            ColumnResolver resolver = FromCompiler.getResolver(indexNode, conn.unwrap(PhoenixConnection.class));
-            PTable indexTable = resolver.getTables().get(0).getTable();
-            // Can't set IMMUTABLE_ROWS, MULTI_TENANT or DEFAULT_COLUMN_FAMILY_NAME on an index
-            assertNull(indexTable.getDefaultFamilyName());
-            assertFalse(indexTable.isMultiTenant());
-            assertEquals(mutable, !indexTable.isImmutableRows()); // Should match table
-            if(localIndex) {
-                assertEquals(10, indexTable.getBucketNum().intValue());
-                assertTrue(indexTable.isWALDisabled());
-            }
-        }
-    }
-
-    @Test
-    public void testIndexWithNullableDateCol() throws Exception {
-        String tableName = "TBL_" + generateUniqueName();
-        String indexName = "IND_" + generateUniqueName();
-        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
-        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
-            conn.setAutoCommit(false);
-            Date date = new Date(System.currentTimeMillis());
-
-            TestUtil.createMultiCFTestTable(conn, fullTableName, tableDDLOptions);
-            populateMultiCFTestTable(fullTableName, date);
-            String ddl = "CREATE " + (localIndex ? " LOCAL " : "") + " INDEX " + indexName + " ON " + fullTableName + " (date_col)";
-            conn.createStatement().execute(ddl);
-
-            String query = "SELECT int_pk from " + fullTableName ;
-            ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            if (localIndex) {
-                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName +" [1]\n"
-                        + "    SERVER FILTER BY FIRST KEY ONLY\n"
-                        + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
-            } else {
-                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullIndexName + "\n"
-                        + "    SERVER FILTER BY FIRST KEY ONLY", QueryUtil.getExplainPlan(rs));
-            }
-
-            rs = conn.createStatement().executeQuery(query);
-            assertTrue(rs.next());
-            assertEquals(2, rs.getInt(1));
-            assertTrue(rs.next());
-            assertEquals(1, rs.getInt(1));
-            assertTrue(rs.next());
-            assertEquals(3, rs.getInt(1));
-            assertFalse(rs.next());
-
-            query = "SELECT date_col from " + fullTableName + " order by date_col" ;
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            if (localIndex) {
-                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName + " [1]\n"
-                        + "    SERVER FILTER BY FIRST KEY ONLY\n"
-                        + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
-            } else {
-                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullIndexName + "\n"
-                        + "    SERVER FILTER BY FIRST KEY ONLY", QueryUtil.getExplainPlan(rs));
-            }
-
-            rs = conn.createStatement().executeQuery(query);
-            assertTrue(rs.next());
-            assertEquals(date, rs.getDate(1));
-            assertTrue(rs.next());
-            assertEquals(new Date(date.getTime() + MILLIS_IN_DAY), rs.getDate(1));
-            assertTrue(rs.next());
-            assertEquals(new Date(date.getTime() + 2 * MILLIS_IN_DAY), rs.getDate(1));
-            assertFalse(rs.next());
-        }
-    }
-
-    @Test
-    public void testSelectAllAndAliasWithIndex() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String tableName = "TBL_" + generateUniqueName();
-        String indexName = "IND_" + generateUniqueName();
-        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
-        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
-        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
-            conn.setAutoCommit(false);
-            String query;
-            ResultSet rs;
-            String ddl = "CREATE TABLE " + fullTableName + " (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR) " + tableDDLOptions;
-            conn.createStatement().execute(ddl);
-            query = "SELECT * FROM " + fullTableName;
-            rs = conn.createStatement().executeQuery(query);
-            assertFalse(rs.next());
-
-            ddl = "CREATE " + (localIndex ? " LOCAL " : "") + " INDEX " + indexName + " ON " + fullTableName + " (v2 DESC) INCLUDE (v1)";
-            conn.createStatement().execute(ddl);
-            query = "SELECT * FROM " + fullIndexName;
-            rs = conn.createStatement().executeQuery(query);
-            assertFalse(rs.next());
-
-            PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + fullTableName + " VALUES(?,?,?)");
-            stmt.setString(1,"a");
-            stmt.setString(2, "x");
-            stmt.setString(3, "1");
-            stmt.execute();
-            stmt.setString(1,"b");
-            stmt.setString(2, "y");
-            stmt.setString(3, "2");
-            stmt.execute();
-            conn.commit();
-
-            query = "SELECT * FROM " + fullTableName;
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            if(localIndex){
-                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName+" [1]\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
-            } else {
-                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullIndexName, QueryUtil.getExplainPlan(rs));
-            }
-
-            rs = conn.createStatement().executeQuery(query);
-            assertTrue(rs.next());
-            assertEquals("b",rs.getString(1));
-            assertEquals("y",rs.getString(2));
-            assertEquals("2",rs.getString(3));
-            assertEquals("b",rs.getString("k"));
-            assertEquals("y",rs.getString("v1"));
-            assertEquals("2",rs.getString("v2"));
-            assertTrue(rs.next());
-            assertEquals("a",rs.getString(1));
-            assertEquals("x",rs.getString(2));
-            assertEquals("1",rs.getString(3));
-            assertEquals("a",rs.getString("k"));
-            assertEquals("x",rs.getString("v1"));
-            assertEquals("1",rs.getString("v2"));
-            assertFalse(rs.next());
-
-            query = "SELECT v1 as foo FROM " + fullTableName + " WHERE v2 = '1' ORDER BY foo";
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            if(localIndex){
-                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +fullTableName + " [1,~'1']\n" +
-                        "    SERVER SORTED BY [\"V1\"]\n" +
-                        "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
-            } else {
-                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +fullIndexName + " [~'1']\n" +
-                        "    SERVER SORTED BY [\"V1\"]\n" +
-                        "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
-            }
-
-            rs = conn.createStatement().executeQuery(query);
-            assertTrue(rs.next());
-            assertEquals("x",rs.getString(1));
-            assertEquals("x",rs.getString("foo"));
-            assertFalse(rs.next());
-        }
-    }
-
-    @Test
-    public void testSelectCF() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String tableName = "TBL_" + generateUniqueName();
-        String indexName = "IND_" + generateUniqueName();
-        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
-        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
-        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
-            conn.setAutoCommit(false);
-            String query;
-            ResultSet rs;
-            String ddl = "CREATE TABLE " + fullTableName + " (k VARCHAR NOT NULL PRIMARY KEY, a.v1 VARCHAR, a.v2 VARCHAR, b.v1 VARCHAR) " + tableDDLOptions;
-            conn.createStatement().execute(ddl);
-            query = "SELECT * FROM " + fullTableName;
-            rs = conn.createStatement().executeQuery(query);
-            assertFalse(rs.next());
-            ddl = "CREATE " + (localIndex ? " LOCAL " : "") + " INDEX " + indexName + " ON " + fullTableName + " (v2 DESC) INCLUDE (a.v1)";
-            conn.createStatement().execute(ddl);
-            query = "SELECT * FROM " + fullIndexName;
-            rs = conn.createStatement().executeQuery(query);
-            assertFalse(rs.next());
-
-            PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + fullTableName + " VALUES(?,?,?,?)");
-            stmt.setString(1,"a");
-            stmt.setString(2, "x");
-            stmt.setString(3, "1");
-            stmt.setString(4, "A");
-            stmt.execute();
-            stmt.setString(1,"b");
-            stmt.setString(2, "y");
-            stmt.setString(3, "2");
-            stmt.setString(4, "B");
-            stmt.execute();
-            conn.commit();
-
-            query = "SELECT * FROM " + fullTableName;
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullTableName, QueryUtil.getExplainPlan(rs));
-
-            query = "SELECT a.* FROM " + fullTableName;
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            if(localIndex) {
-                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName+" [1]\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
-            } else {
-                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullIndexName, QueryUtil.getExplainPlan(rs));
-            }
-            rs = conn.createStatement().executeQuery(query);
-            assertTrue(rs.next());
-            assertEquals("y",rs.getString(1));
-            assertEquals("2",rs.getString(2));
-            assertEquals("y",rs.getString("v1"));
-            assertEquals("2",rs.getString("v2"));
-            assertTrue(rs.next());
-            assertEquals("x",rs.getString(1));
-            assertEquals("1",rs.getString(2));
-            assertEquals("x",rs.getString("v1"));
-            assertEquals("1",rs.getString("v2"));
-            assertFalse(rs.next());
-        }
-    }
-
-    @Test
-    public void testUpsertAfterIndexDrop() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String tableName = "TBL_" + generateUniqueName();
-        String indexName = "IND_" + generateUniqueName();
-        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
-        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
-        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
-            conn.setAutoCommit(false);
-            String query;
-            ResultSet rs;
-            // make sure that the tables are empty, but reachable
-            conn.createStatement().execute(
-                    "CREATE TABLE " + fullTableName
-                    + " (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR)" + tableDDLOptions);
-            query = "SELECT * FROM " + fullTableName;
-            rs = conn.createStatement().executeQuery(query);
-            assertFalse(rs.next());
-
-            conn.createStatement().execute(
-                    "CREATE " + (localIndex ? "LOCAL " : "") + "INDEX " + indexName + " ON " + fullTableName + " (v1, v2)");
-            query = "SELECT * FROM " + fullIndexName;
-            rs = conn.createStatement().executeQuery(query);
-            assertFalse(rs.next());
-
-            // load some data into the table
-            PreparedStatement stmt =
-                    conn.prepareStatement("UPSERT INTO " + fullTableName + " VALUES(?,?,?)");
-            stmt.setString(1, "a");
-            stmt.setString(2, "x");
-            stmt.setString(3, "1");
-            stmt.execute();
-            conn.commit();
-
-            // make sure the index is working as expected
-            query = "SELECT * FROM " + fullIndexName;
-            rs = conn.createStatement().executeQuery(query);
-            assertTrue(rs.next());
-            assertEquals("x", rs.getString(1));
-            assertEquals("1", rs.getString(2));
-            assertEquals("a", rs.getString(3));
-            assertFalse(rs.next());
-
-            String ddl = "DROP INDEX " + indexName + " ON " + fullTableName;
-            conn.createStatement().execute(ddl);
-
-            stmt = conn.prepareStatement("UPSERT INTO " + fullTableName + "(k, v1) VALUES(?,?)");
-            stmt.setString(1, "a");
-            stmt.setString(2, "y");
-            stmt.execute();
-            conn.commit();
-
-            query = "SELECT * FROM " + fullTableName;
-
-            // check that the data table matches as expected
-            rs = conn.createStatement().executeQuery(query);
-            assertTrue(rs.next());
-            assertEquals("a", rs.getString(1));
-            assertEquals("y", rs.getString(2));
-            assertFalse(rs.next());
-        }
-    }
-
-    @Test
-    public void testMultipleUpdatesAcrossRegions() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String tableName = "TBL_" + generateUniqueName();
-        String indexName = "IND_" + generateUniqueName();
-        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
-        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
-
-        String testTable = fullTableName+"_MULTIPLE_UPDATES";
-        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
-            conn.setAutoCommit(false);
-            String query;
-            ResultSet rs;
-            // make sure that the tables are empty, but reachable
-            conn.createStatement().execute(
-                    "CREATE TABLE " + testTable
-                    + " (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR) "
-                    + (!tableDDLOptions.isEmpty() ? tableDDLOptions : "") + " SPLIT ON ('b')");
-            query = "SELECT * FROM " + testTable;
-            rs = conn.createStatement().executeQuery(query);
-            assertFalse(rs.next());
-
-            conn.createStatement().execute(
-                    "CREATE " + (localIndex ? "LOCAL " : "") + "INDEX " + indexName + " ON " + testTable + " (v1, v2)");
-            query = "SELECT * FROM " + fullIndexName;
-            rs = conn.createStatement().executeQuery(query);
-            assertFalse(rs.next());
-
-            // load some data into the table
-            PreparedStatement stmt =
-                    conn.prepareStatement("UPSERT INTO " + testTable + " VALUES(?,?,?)");
-            stmt.setString(1, "a");
-            stmt.setString(2, "x");
-            stmt.setString(3, "1");
-            stmt.execute();
-            stmt.setString(1, "b");
-            stmt.setString(2, "y");
-            stmt.setString(3, "2");
-            stmt.execute();
-            stmt.setString(1, "c");
-            stmt.setString(2, "z");
-            stmt.setString(3, "3");
-            stmt.execute();
-            conn.commit();
-
-            query = "SELECT /*+ NO_INDEX */ * FROM " + testTable;
-            rs = conn.createStatement().executeQuery(query);
-            assertTrue(rs.next());
-            assertEquals("a", rs.getString(1));
-            assertEquals("x", rs.getString(2));
-            assertEquals("1", rs.getString(3));
-            assertTrue(rs.next());
-            assertEquals("b", rs.getString(1));
-            assertEquals("y", rs.getString(2));
-            assertEquals("2", rs.getString(3));
-            assertTrue(rs.next());
-            assertEquals("c", rs.getString(1));
-            assertEquals("z", rs.getString(2));
-            assertEquals("3", rs.getString(3));
-            assertFalse(rs.next());
-            
-            // make sure the index is working as expected
-            query = "SELECT * FROM " + testTable;
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            if (localIndex) {
-                assertEquals("CLIENT PARALLEL 2-WAY RANGE SCAN OVER " + testTable+" [1]\n"
-                        + "    SERVER FILTER BY FIRST KEY ONLY\n"
-                        + "CLIENT MERGE SORT",
-                        QueryUtil.getExplainPlan(rs));
-            } else {
-                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullIndexName + "\n"
-                        + "    SERVER FILTER BY FIRST KEY ONLY",
-                        QueryUtil.getExplainPlan(rs));
-            }
-
-            // check that the data table matches as expected
-            rs = conn.createStatement().executeQuery(query);
-            assertTrue(rs.next());
-            assertEquals("a", rs.getString(1));
-            assertEquals("x", rs.getString(2));
-            assertEquals("1", rs.getString(3));
-            assertTrue(rs.next());
-            assertEquals("b", rs.getString(1));
-            assertEquals("y", rs.getString(2));
-            assertEquals("2", rs.getString(3));
-            assertTrue(rs.next());
-            assertEquals("c", rs.getString(1));
-            assertEquals("z", rs.getString(2));
-            assertEquals("3", rs.getString(3));
-            assertFalse(rs.next());
-        }
-    }
-
-    @Test
-    public void testIndexWithCaseSensitiveCols() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String tableName = "TBL_" + generateUniqueName();
-        String indexName = "IND_" + generateUniqueName();
-        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
-        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
-        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
-            conn.setAutoCommit(false);
-            String query;
-            ResultSet rs;
-            conn.createStatement().execute("CREATE TABLE " + fullTableName + " (k VARCHAR NOT NULL PRIMARY KEY, \"V1\" VARCHAR, \"v2\" VARCHAR)"+tableDDLOptions);
-            query = "SELECT * FROM "+fullTableName;
-            rs = conn.createStatement().executeQuery(query);
-            long ts = conn.unwrap(PhoenixConnection.class).getTable(new PTableKey(null,fullTableName)).getTimeStamp();
-            assertFalse(rs.next());
-            conn.createStatement().execute(
-                    "CREATE " + (localIndex ? "LOCAL " : "") + "INDEX " + indexName + " ON " + fullTableName + "(\"v2\") INCLUDE (\"V1\")");
-            query = "SELECT * FROM "+fullIndexName;
-            rs = conn.createStatement().executeQuery(query);
-            assertFalse(rs.next());
-
-            PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + fullTableName + " VALUES(?,?,?)");
-            stmt.setString(1,"a");
-            stmt.setString(2, "x");
-            stmt.setString(3, "1");
-            stmt.execute();
-            stmt.setString(1,"b");
-            stmt.setString(2, "y");
-            stmt.setString(3, "2");
-            stmt.execute();
-            conn.commit();
-
-            query = "SELECT * FROM " + fullTableName + " WHERE \"v2\" = '1'";
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            if(localIndex){
-                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName + " [1,'1']\n"
-                        + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
-            } else {
-                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullIndexName + " ['1']", QueryUtil.getExplainPlan(rs));
-            }
-            
-            rs = conn.createStatement().executeQuery(query);
-            assertTrue(rs.next());
-            assertEquals("a",rs.getString(1));
-            assertEquals("x",rs.getString(2));
-            assertEquals("1",rs.getString(3));
-            assertEquals("a",rs.getString("k"));
-            assertEquals("x",rs.getString("V1"));
-            assertEquals("1",rs.getString("v2"));
-            assertFalse(rs.next());
-
-            query = "SELECT \"V1\", \"V1\" as foo1, \"v2\" as foo, \"v2\" as \"Foo1\", \"v2\" FROM " + fullTableName + " ORDER BY foo";
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            if(localIndex){
-                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName + " [1]\nCLIENT MERGE SORT",
-                        QueryUtil.getExplainPlan(rs));
-            } else {
-                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER "+fullIndexName, QueryUtil.getExplainPlan(rs));
-            }
-
-            rs = conn.createStatement().executeQuery(query);
-            assertTrue(rs.next());
-            assertEquals("x",rs.getString(1));
-            assertEquals("x",rs.getString("V1"));
-            assertEquals("x",rs.getString(2));
-            assertEquals("x",rs.getString("foo1"));
-            assertEquals("1",rs.getString(3));
-            assertEquals("1",rs.getString("Foo"));
-            assertEquals("1",rs.getString(4));
-            assertEquals("1",rs.getString("Foo1"));
-            assertEquals("1",rs.getString(5));
-            assertEquals("1",rs.getString("v2"));
-            assertTrue(rs.next());
-            assertEquals("y",rs.getString(1));
-            assertEquals("y",rs.getString("V1"));
-            assertEquals("y",rs.getString(2));
-            assertEquals("y",rs.getString("foo1"));
-            assertEquals("2",rs.getString(3));
-            assertEquals("2",rs.getString("Foo"));
-            assertEquals("2",rs.getString(4));
-            assertEquals("2",rs.getString("Foo1"));
-            assertEquals("2",rs.getString(5));
-            assertEquals("2",rs.getString("v2"));
-            assertFalse(rs.next());
-
-            assertNoIndexDeletes(conn, ts, fullIndexName);
-        }
-    }
-
-    private void assertNoIndexDeletes(Connection conn, long minTimestamp, String fullIndexName) throws IOException, SQLException {
-        if (!this.mutable) {
-            PhoenixConnection pconn = conn.unwrap(PhoenixConnection.class);
-            PTable index = pconn.getTable(new PTableKey(null, fullIndexName));
-            byte[] physicalIndexTable = index.getPhysicalName().getBytes();
-            try (HTableInterface hIndex = pconn.getQueryServices().getTable(physicalIndexTable)) {
-                Scan scan = new Scan();
-                scan.setRaw(true);
-                if (this.transactional) {
-                    minTimestamp = TransactionUtil.convertToNanoseconds(minTimestamp);
-                }
-                scan.setTimeRange(minTimestamp, HConstants.LATEST_TIMESTAMP);
-                ResultScanner scanner = hIndex.getScanner(scan);
-                Result result;
-                while ((result = scanner.next()) != null) {
-                    CellScanner cellScanner = result.cellScanner();
-                    while (cellScanner.advance()) {
-                        Cell current = cellScanner.current();
-                        assertEquals (KeyValue.Type.Put.getCode(), current.getTypeByte());
-                    }
-                }
-            };
-        }
-    }
-
-    @Test
-    public void testInFilterOnIndexedTable() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String tableName = "TBL_" + generateUniqueName();
-        String indexName = "IND_" + generateUniqueName();
-        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
-        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
-            conn.setAutoCommit(false);
-            String query;
-            ResultSet rs;
-            String ddl = "CREATE TABLE " + fullTableName +"  (PK1 CHAR(2) NOT NULL PRIMARY KEY, CF1.COL1 BIGINT) " + tableDDLOptions;
-            conn.createStatement().execute(ddl);
-            ddl = "CREATE " + (localIndex ? "LOCAL " : "") + "INDEX " + indexName + " ON " + fullTableName + "(COL1)";
-            conn.createStatement().execute(ddl);
-
-            query = "SELECT COUNT(COL1) FROM " + fullTableName +" WHERE COL1 IN (1,25,50,75,100)";
-            rs = conn.createStatement().executeQuery(query);
-            assertTrue(rs.next());
-        }
-    }
-
-    @Test
-    public void testIndexWithDecimalCol() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String tableName = "TBL_" + generateUniqueName();
-        String indexName = "IND_" + generateUniqueName();
-        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
-        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
-        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
-            conn.setAutoCommit(false);
-            String query;
-            ResultSet rs;
-            Date date = new Date(System.currentTimeMillis());
-
-            TestUtil.createMultiCFTestTable(conn, fullTableName, tableDDLOptions);
-            populateMultiCFTestTable(fullTableName, date);
-            String ddl = null;
-            ddl = "CREATE " + (localIndex ? "LOCAL " : "") + "INDEX " + indexName + " ON " + fullTableName + " (decimal_pk) INCLUDE (decimal_col1, decimal_col2)";
-            conn.createStatement().execute(ddl);
-
-            query = "SELECT decimal_pk, decimal_col1, decimal_col2 from " + fullTableName ;
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            if(localIndex) {
-                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName+" [1]\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
-            } else {
-                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullIndexName, QueryUtil.getExplainPlan(rs));
-            }
-
-            rs = conn.createStatement().executeQuery(query);
-            assertTrue(rs.next());
-            assertEquals(new BigDecimal("1.1"), rs.getBigDecimal(1));
-            assertEquals(new BigDecimal("2.1"), rs.getBigDecimal(2));
-            assertEquals(new BigDecimal("3.1"), rs.getBigDecimal(3));
-            assertTrue(rs.next());
-            assertEquals(new BigDecimal("2.2"), rs.getBigDecimal(1));
-            assertEquals(new BigDecimal("3.2"), rs.getBigDecimal(2));
-            assertEquals(new BigDecimal("4.2"), rs.getBigDecimal(3));
-            assertTrue(rs.next());
-            assertEquals(new BigDecimal("3.3"), rs.getBigDecimal(1));
-            assertEquals(new BigDecimal("4.3"), rs.getBigDecimal(2));
-            assertEquals(new BigDecimal("5.3"), rs.getBigDecimal(3));
-            assertFalse(rs.next());
-        }
-    }
-
-    /**
-     * Ensure that HTD contains table priorities correctly.
-     */
-    @Test
-    public void testTableDescriptorPriority() throws SQLException, IOException {
-        String tableName = "TBL_" + generateUniqueName();
-        String indexName = "IND_" + generateUniqueName();
-        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
-        // Check system tables priorities.
-        try (HBaseAdmin admin = driver.getConnectionQueryServices(null, null).getAdmin(); 
-                Connection c = DriverManager.getConnection(getUrl())) {
-            ResultSet rs = c.getMetaData().getTables("", 
-                    "\""+ PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA + "\"", 
-                    null, 
-                    new String[] {PTableType.SYSTEM.toString()});
-            ReadOnlyProps p = c.unwrap(PhoenixConnection.class).getQueryServices().getProps();
-            while (rs.next()) {
-                String schemaName = rs.getString(PhoenixDatabaseMetaData.TABLE_SCHEM);
-                String tName = rs.getString(PhoenixDatabaseMetaData.TABLE_NAME);
-                org.apache.hadoop.hbase.TableName hbaseTableName = SchemaUtil.getPhysicalTableName(SchemaUtil.getTableName(schemaName, tName), p);
-                HTableDescriptor htd = admin.getTableDescriptor(hbaseTableName);
-                String val = htd.getValue("PRIORITY");
-                assertNotNull("PRIORITY is not set for table:" + htd, val);
-                assertTrue(Integer.parseInt(val)
-                        >= PhoenixRpcSchedulerFactory.getMetadataPriority(config));
-            }
-            Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-            String ddl ="CREATE TABLE " + fullTableName + TestUtil.TEST_TABLE_SCHEMA + tableDDLOptions;
-            try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
-                conn.setAutoCommit(false);
-                Statement stmt = conn.createStatement();
-                stmt.execute(ddl);
-                BaseTest.populateTestTable(fullTableName);
-                ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName
-                        + " ON " + fullTableName + " (long_col1, long_col2)"
-                        + " INCLUDE (decimal_col1, decimal_col2)";
-                stmt.execute(ddl);
-            }
-
-            HTableDescriptor dataTable = admin.getTableDescriptor(
-                    org.apache.hadoop.hbase.TableName.valueOf(fullTableName));
-            String val = dataTable.getValue("PRIORITY");
-            assertTrue(val == null || Integer.parseInt(val) < HConstants.HIGH_QOS);
-
-            if (!localIndex && mutable) {
-                HTableDescriptor indexTable = admin.getTableDescriptor(
-                        org.apache.hadoop.hbase.TableName.valueOf(indexName));
-                val = indexTable.getValue("PRIORITY");
-                assertNotNull("PRIORITY is not set for table:" + indexTable, val);
-                assertTrue(Integer.parseInt(val) >= PhoenixRpcSchedulerFactory.getIndexPriority(config));
-            }
-        }
-    }
-
-    @Test
-    public void testQueryBackToDataTableWithDescPKColumn() throws SQLException {
-        doTestQueryBackToDataTableWithDescPKColumn(true);
-        doTestQueryBackToDataTableWithDescPKColumn(false);
-    }
-
-    private void doTestQueryBackToDataTableWithDescPKColumn(boolean isSecondPKDesc) throws SQLException {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String tableName = "TBL_" + generateUniqueName();
-        String indexName = "IND_" + generateUniqueName();
-        String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
-        String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
-
-        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
-            // create data table and index table
-            conn.setAutoCommit(true);
-            Statement stmt = conn.createStatement();
-            String ddl = "CREATE TABLE " + fullTableName + "(p1 integer not null, p2 integer not null, " +
-                    " a integer, b integer CONSTRAINT PK PRIMARY KEY ";
-            if (isSecondPKDesc) {
-                ddl += "(p1, p2 desc))";
-            } else {
-                ddl += "(p1 desc, p2))";
-            }
-            stmt.executeUpdate(ddl);
-            ddl = "CREATE "+ (localIndex ? "LOCAL " : "") + " INDEX " + fullIndexName + " on " + fullTableName + "(a)";
-            stmt.executeUpdate(ddl);
-
-            // upsert a single row
-            String upsert = "UPSERT INTO " + fullTableName + " VALUES(1,2,3,4)";
-            stmt.executeUpdate(upsert);
-
-            // try select with index
-            // a = 3, should hit index table, but we select column B, so it will query back to data table
-            String query = "SELECT /*+index(" + fullTableName + " " + fullIndexName + "*/ b from " + fullTableName +
-                    " WHERE a = 3";
-            ResultSet rs = stmt.executeQuery(query);
-            assertTrue(rs.next());
-            assertEquals(4, rs.getInt(1));
-            assertFalse(rs.next());
-            rs.close();
-            stmt.close();
-        }
-    }
-
-    @Test
-    public void testReturnedTimestamp() throws Exception {
-        String tenantId = getOrganizationId();
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            String indexName = generateUniqueName();
-            String tableName =
-                    initATableValues(generateUniqueName(), tenantId, getDefaultSplits(tenantId),
-                        new Date(System.currentTimeMillis()), null, getUrl(), tableDDLOptions);
-            String ddl = "CREATE "+ (localIndex ? "LOCAL " : "") + " INDEX " + indexName + " on " + tableName + "(A_STRING) INCLUDE (B_STRING)";
-            conn.createStatement().executeUpdate(ddl);
-            String query = "SELECT ENTITY_ID,A_STRING,B_STRING FROM " + tableName + " WHERE organization_id=? and entity_id=?";
-
-            PreparedStatement statement = conn.prepareStatement(query);
-            statement.setString(1, tenantId);
-
-            long currentTime = EnvironmentEdgeManager.currentTimeMillis();
-            String entityId = mutable ? ROW5 : Integer.toString(Math.abs(RAND.nextInt() % 1000000000));
-            PreparedStatement ddlStatement = conn.prepareStatement("UPSERT INTO " + tableName + "(ORGANIZATION_ID, ENTITY_ID,A_STRING) VALUES('" + tenantId + "',?,?)");
-            ddlStatement.setString(1, entityId);
-            ddlStatement.setString(2, Integer.toString(Math.abs(RAND.nextInt() % 1000000000)));
-            ddlStatement.executeUpdate();
-            conn.commit();
- 
-            statement.setString(2, entityId);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertTrue(rs.unwrap(PhoenixResultSet.class).getCurrentRow().getValue(0).getTimestamp() >= currentTime);
-            assertEquals(rs.getString(1).trim(), entityId);
-            assertFalse(rs.next());
-
-            currentTime = EnvironmentEdgeManager.currentTimeMillis();
-            entityId = mutable ? ROW5 : Integer.toString(Math.abs(RAND.nextInt() % 1000000000));
-            ddlStatement = conn.prepareStatement("UPSERT INTO " + tableName + "(ORGANIZATION_ID, ENTITY_ID,B_STRING) VALUES('" + tenantId + "',?,?)");
-            ddlStatement.setString(1, entityId);
-            ddlStatement.setString(2, Integer.toString(Math.abs(RAND.nextInt() % 1000000000)));
-            ddlStatement.executeUpdate();
-            conn.commit();
-            
-            statement.setString(2, entityId);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertTrue(rs.unwrap(PhoenixResultSet.class).getCurrentRow().getValue(0).getTimestamp() >= currentTime);
-            assertEquals(rs.getString(1).trim(), entityId);
-            assertFalse(rs.next());
-
-        } finally {
-            conn.close();
-        }
-    }
-
-
-}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3d9adc6f/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexMaintenanceIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexMaintenanceIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexMaintenanceIT.java
new file mode 100644
index 0000000..7d02e80
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexMaintenanceIT.java
@@ -0,0 +1,457 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.index;
+
+import static org.apache.phoenix.query.QueryConstants.MILLIS_IN_DAY;
+import static org.apache.phoenix.util.TestUtil.INDEX_DATA_SCHEMA;
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.phoenix.end2end.ParallelStatsDisabledIT;
+import org.apache.phoenix.exception.SQLExceptionCode;
+import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.util.DateUtil;
+import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.QueryUtil;
+import org.apache.phoenix.util.TestUtil;
+import org.junit.Test;
+
+public class IndexMaintenanceIT extends ParallelStatsDisabledIT {
+
+    @Test
+    public void testImmutableIndexCreateAndUpdate() throws Exception {
+        helpTestCreateAndUpdate(false, false);
+    }
+
+    @Test
+    public void testImmutableLocalIndexCreateAndUpdate() throws Exception {
+        helpTestCreateAndUpdate(false, true);
+    }
+
+    @Test
+    public void testMutableIndexCreateAndUpdate() throws Exception {
+        helpTestCreateAndUpdate(true, false);
+    }
+
+    @Test
+    public void testMutableLocalIndexCreateAndUpdate() throws Exception {
+        helpTestCreateAndUpdate(true, true);
+    }
+
+    /**
+     * Adds a row to the index data table
+     * 
+     * @param i
+     *            row number
+     */
+    private void insertRow(PreparedStatement stmt, int i) throws SQLException {
+        // insert row
+        stmt.setString(1, "varchar" + String.valueOf(i));
+        stmt.setString(2, "char" + String.valueOf(i));
+        stmt.setInt(3, i);
+        stmt.setLong(4, i);
+        stmt.setBigDecimal(5, new BigDecimal(i*0.5d));
+        Date date = new Date(DateUtil.parseDate("2015-01-01 00:00:00").getTime() + (i - 1) * MILLIS_IN_DAY);
+        stmt.setDate(6, date);
+        stmt.setString(7, "a.varchar" + String.valueOf(i));
+        stmt.setString(8, "a.char" + String.valueOf(i));
+        stmt.setInt(9, i);
+        stmt.setLong(10, i);
+        stmt.setBigDecimal(11, new BigDecimal(i*0.5d));
+        stmt.setDate(12, date);
+        stmt.setString(13, "b.varchar" + String.valueOf(i));
+        stmt.setString(14, "b.char" + String.valueOf(i));
+        stmt.setInt(15, i);
+        stmt.setLong(16, i);
+        stmt.setBigDecimal(17, new BigDecimal(i*0.5d));
+        stmt.setDate(18, date);
+        stmt.executeUpdate();
+    }
+
+    private void verifyResult(ResultSet rs, int i) throws SQLException {
+        assertTrue(rs.next());
+        assertEquals("VARCHAR" + String.valueOf(i) + "_" + StringUtils.rightPad("CHAR" + String.valueOf(i), 10, ' ')
+                + "_A.VARCHAR" + String.valueOf(i) + "_" + StringUtils.rightPad("B.CHAR" + String.valueOf(i), 10, ' '),
+                rs.getString(1));
+        assertEquals(i * 3, rs.getInt(2));
+        Date date = new Date(DateUtil.parseDate("2015-01-01 00:00:00").getTime() + (i) * MILLIS_IN_DAY);
+        assertEquals(date, rs.getDate(3));
+        assertEquals(date, rs.getDate(4));
+        assertEquals(date, rs.getDate(5));
+        assertEquals("varchar" + String.valueOf(i), rs.getString(6));
+        assertEquals("char" + String.valueOf(i), rs.getString(7));
+        assertEquals(i, rs.getInt(8));
+        assertEquals(i, rs.getLong(9));
+        assertEquals(i*0.5d, rs.getDouble(10), 0.000001);
+        assertEquals(i, rs.getLong(11));
+        assertEquals(i, rs.getLong(12));
+    }
+
+    private void createDataTable(Connection conn, String dataTableName, String tableProps) throws SQLException {
+        String tableDDL = "create table " + dataTableName + TestUtil.TEST_TABLE_SCHEMA + tableProps;
+        conn.createStatement().execute(tableDDL);
+    }
+    
+    private void helpTestCreateAndUpdate(boolean mutable, boolean localIndex) throws Exception {
+        String dataTableName = generateUniqueName();
+        String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName;
+        String indexName = generateUniqueName();
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            conn.setAutoCommit(false);
+            createDataTable(conn, fullDataTableName, mutable ? "" : "IMMUTABLE_ROWS=true");
+            populateDataTable(conn, fullDataTableName);
+
+            // create an expression index
+            String ddl = "CREATE "
+                    + (localIndex ? "LOCAL" : "")
+                    + " INDEX " + indexName + " ON "
+                    + fullDataTableName
+                    + " ((UPPER(varchar_pk) || '_' || UPPER(char_pk) || '_' || UPPER(varchar_col1) || '_' || UPPER(b.char_col2)),"
+                    + " (decimal_pk+int_pk+decimal_col2+int_col1)," + " date_pk+1, date1+1, date2+1 )"
+                    + " INCLUDE (long_col1, long_col2)";
+            conn.createStatement().execute(ddl);
+
+            // run select query with expression in WHERE clause
+            String whereSql = "SELECT long_col1, long_col2 from "
+                    + fullDataTableName
+                    + " WHERE UPPER(varchar_pk) || '_' || UPPER(char_pk) || '_' || UPPER(varchar_col1) || '_' || UPPER(b.char_col2) = ?"
+                    + " AND decimal_pk+int_pk+decimal_col2+int_col1=?"
+                    // since a.date1 and b.date2 are NULLABLE and date is fixed width, these expressions are stored as
+                    // DECIMAL in the index (which is not fixed width)
+                    + " AND date_pk+1=? AND date1+1=? AND date2+1=?";
+            PreparedStatement stmt = conn.prepareStatement(whereSql);
+            stmt.setString(1, "VARCHAR1_CHAR1     _A.VARCHAR1_B.CHAR1   ");
+            stmt.setInt(2, 3);
+            Date date = DateUtil.parseDate("2015-01-02 00:00:00");
+            stmt.setDate(3, date);
+            stmt.setDate(4, date);
+            stmt.setDate(5, date);
+
+            // verify that the query does a range scan on the index table
+            ResultSet rs = stmt.executeQuery("EXPLAIN " + whereSql);
+            assertEquals(
+                    localIndex ? "CLIENT PARALLEL 1-WAY RANGE SCAN OVER INDEX_TEST."
+                            + dataTableName
+                            + " [1,'VARCHAR1_CHAR1     _A.VARCHAR1_B.CHAR1   ',3,'2015-01-02 00:00:00.000',1,420,156,800,000,1,420,156,800,000]\nCLIENT MERGE SORT"
+                            : "CLIENT PARALLEL 1-WAY RANGE SCAN OVER INDEX_TEST." + indexName + " ['VARCHAR1_CHAR1     _A.VARCHAR1_B.CHAR1   ',3,'2015-01-02 00:00:00.000',1,420,156,800,000,1,420,156,800,000]",
+                    QueryUtil.getExplainPlan(rs));
+
+            // verify that the correct results are returned
+            rs = stmt.executeQuery();
+            assertTrue(rs.next());
+            assertEquals(1, rs.getInt(1));
+            assertEquals(1, rs.getInt(2));
+            assertFalse(rs.next());
+
+            // verify all rows in data table are present in index table
+            String indexSelectSql = "SELECT UPPER(varchar_pk) || '_' || UPPER(char_pk) || '_' || UPPER(varchar_col1) || '_' || UPPER(b.char_col2), "
+                    + "decimal_pk+int_pk+decimal_col2+int_col1, "
+                    + "date_pk+1, date1+1, date2+1, "
+                    + "varchar_pk, char_pk, int_pk, long_pk, decimal_pk, "
+                    + "long_col1, long_col2 "
+                    + "from "
+                    + fullDataTableName;
+            rs = conn.createStatement().executeQuery("EXPLAIN " + indexSelectSql);
+            assertEquals(localIndex ? "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullDataTableName
+                    + " [1]\nCLIENT MERGE SORT" : "CLIENT PARALLEL 1-WAY FULL SCAN OVER INDEX_TEST." + indexName,
+                    QueryUtil.getExplainPlan(rs));
+            rs = conn.createStatement().executeQuery(indexSelectSql);
+            verifyResult(rs, 1);
+            verifyResult(rs, 2);
+
+            // Insert two more rows to the index data table
+            String upsert = "UPSERT INTO " + fullDataTableName
+                    + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+            stmt = conn.prepareStatement(upsert);
+            insertRow(stmt, 3);
+            insertRow(stmt, 4);
+            conn.commit();
+
+            rs = conn.createStatement().executeQuery(indexSelectSql);
+            verifyResult(rs, 1);
+            verifyResult(rs, 2);
+            // verify that two rows added after index was created were also added to
+            // the index table
+            verifyResult(rs, 3);
+            verifyResult(rs, 4);
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testMutableIndexUpdate() throws Exception {
+        String dataTableName = generateUniqueName();
+        String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName;
+        String indexName = generateUniqueName();
+    	helpTestUpdate(fullDataTableName, indexName, false);
+    }
+
+    @Test
+    public void testMutableLocalIndexUpdate() throws Exception {
+        String dataTableName = generateUniqueName();
+        String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName;
+        String indexName = generateUniqueName();
+        helpTestUpdate(fullDataTableName, indexName, true);
+    }
+    
+    private void helpTestUpdate(String fullDataTableName, String indexName, boolean localIndex) throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            conn.setAutoCommit(false);
+            createDataTable(conn, fullDataTableName, "");
+            populateDataTable(conn, fullDataTableName);
+
+            // create an expression index
+            String ddl = "CREATE "
+                    + (localIndex ? "LOCAL" : "")
+                    + " INDEX " + indexName + " ON "
+                    + fullDataTableName
+                    + " ((UPPER(varchar_pk) || '_' || UPPER(char_pk) || '_' || UPPER(varchar_col1) || '_' || UPPER(char_col2)),"
+                    + " (decimal_pk+int_pk+decimal_col2+int_col1)," + " date_pk+1, date1+1, date2+1 )"
+                    + " INCLUDE (long_col1, long_col2)";
+            PreparedStatement stmt = conn.prepareStatement(ddl);
+            stmt.execute();
+
+            // update index pk column and covered column
+            String upsert = "UPSERT INTO "
+                    + fullDataTableName
+                    + "(varchar_pk, char_pk, int_pk, long_pk, decimal_pk, date_pk, varchar_col1, long_col1) VALUES(?, ?, ?, ?, ?, ?, ?, ?)";
+
+            stmt = conn.prepareStatement(upsert);
+            stmt.setString(1, "varchar1");
+            stmt.setString(2, "char1");
+            stmt.setInt(3, 1);
+            stmt.setLong(4, 1l);
+            stmt.setBigDecimal(5, new BigDecimal(0.5));
+            stmt.setDate(6, DateUtil.parseDate("2015-01-01 00:00:00"));
+            stmt.setString(7, "a.varchar_updated");
+            stmt.setLong(8, 101);
+            stmt.executeUpdate();
+            conn.commit();
+
+            // verify only one row was updated in the data table
+            String selectSql = "UPPER(varchar_pk) || '_' || UPPER(char_pk) || '_' || UPPER(varchar_col1) || '_' || UPPER(char_col2), long_col1 from "
+                    + fullDataTableName;
+            ResultSet rs = conn.createStatement().executeQuery("SELECT /*+ NO_INDEX */ " + selectSql);
+            assertTrue(rs.next());
+            assertEquals("VARCHAR1_CHAR1     _A.VARCHAR_UPDATED_B.CHAR1   ", rs.getString(1));
+            assertEquals(101, rs.getLong(2));
+            assertTrue(rs.next());
+            assertEquals("VARCHAR2_CHAR2     _A.VARCHAR2_B.CHAR2   ", rs.getString(1));
+            assertEquals(2, rs.getLong(2));
+            assertFalse(rs.next());
+
+            // verify that the rows in the index table are also updated
+            rs = conn.createStatement().executeQuery("SELECT " + selectSql);
+            assertTrue(rs.next());
+            assertEquals("VARCHAR1_CHAR1     _A.VARCHAR_UPDATED_B.CHAR1   ", rs.getString(1));
+            assertEquals(101, rs.getLong(2));
+            assertTrue(rs.next());
+            assertEquals("VARCHAR2_CHAR2     _A.VARCHAR2_B.CHAR2   ", rs.getString(1));
+            assertEquals(2, rs.getLong(2));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    private void populateDataTable(Connection conn, String dataTable) throws SQLException {
+        String upsert = "UPSERT INTO " + dataTable
+                + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+        PreparedStatement stmt1 = conn.prepareStatement(upsert);
+        // insert two rows
+        insertRow(stmt1, 1);
+        insertRow(stmt1, 2);
+        conn.commit();
+    }
+
+    @Test
+    public void testDeleteIndexedExpressionImmutableIndex() throws Exception {
+        helpTestDeleteIndexedExpression(false, false);
+    }
+
+    @Test
+    public void testDeleteIndexedExpressionImmutableLocalIndex() throws Exception {
+        helpTestDeleteIndexedExpression(false, true);
+    }
+
+    @Test
+    public void testDeleteIndexedExpressionMutableIndex() throws Exception {
+        helpTestDeleteIndexedExpression(true, false);
+    }
+
+    @Test
+    public void testDeleteIndexedExpressionMutableLocalIndex() throws Exception {
+        helpTestDeleteIndexedExpression(true, true);
+    }
+
+    protected void helpTestDeleteIndexedExpression(boolean mutable, boolean localIndex) throws Exception {
+        String dataTableName = generateUniqueName();
+        String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName;
+        String indexName = generateUniqueName();
+        String fullIndexTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + indexName;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            conn.setAutoCommit(false);
+            createDataTable(conn, fullDataTableName, mutable ? "" : "IMMUTABLE_ROWS=true");
+            populateDataTable(conn, fullDataTableName);
+            String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullDataTableName
+                    + " (2*long_col2)";
+            conn.createStatement().execute(ddl);
+
+            ResultSet rs;
+            rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullDataTableName);
+            assertTrue(rs.next());
+            assertEquals(2, rs.getInt(1));
+            rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullIndexTableName);
+            assertTrue(rs.next());
+            assertEquals(2, rs.getInt(1));
+
+            conn.setAutoCommit(true);
+            String dml = "DELETE from " + fullDataTableName + " WHERE long_col2 = 2";
+            try {
+                conn.createStatement().execute(dml);
+                if (!mutable) {
+                    fail();
+                }
+            } catch (SQLException e) {
+                if (!mutable) {
+                    assertEquals(SQLExceptionCode.INVALID_FILTER_ON_IMMUTABLE_ROWS.getErrorCode(), e.getErrorCode());
+                }
+            }
+
+            if (!mutable) {
+                dml = "DELETE from " + fullDataTableName + " WHERE 2*long_col2 = 4";
+                conn.createStatement().execute(dml);
+            }
+
+            rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullDataTableName);
+            assertTrue(rs.next());
+            assertEquals(1, rs.getInt(1));
+            rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullIndexTableName);
+            assertTrue(rs.next());
+            assertEquals(1, rs.getInt(1));
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testDeleteCoveredColImmutableIndex() throws Exception {
+        helpTestDeleteCoveredCol(false, false);
+    }
+
+    @Test
+    public void testDeleteCoveredColImmutableLocalIndex() throws Exception {
+        helpTestDeleteCoveredCol(false, true);
+    }
+
+    @Test
+    public void testDeleteCoveredColMutableIndex() throws Exception {
+        helpTestDeleteCoveredCol(true, false);
+    }
+
+    @Test
+    public void testDeleteCoveredColMutableLocalIndex() throws Exception {
+        helpTestDeleteCoveredCol(true, true);
+    }
+
+    protected void helpTestDeleteCoveredCol(boolean mutable, boolean localIndex) throws Exception {
+        String dataTableName = generateUniqueName();
+        String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName;
+        String indexName = generateUniqueName();
+        String fullIndexTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + indexName;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            conn.setAutoCommit(false);
+            createDataTable(conn, fullDataTableName, mutable ? "" : "IMMUTABLE_ROWS=true");
+            populateDataTable(conn, fullDataTableName);
+            String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullDataTableName
+                    + " (long_pk, varchar_pk, 1+long_pk, UPPER(varchar_pk) )" + " INCLUDE (long_col1, long_col2)";
+            conn.createStatement().execute(ddl);
+
+            ResultSet rs;
+            rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullDataTableName);
+            assertTrue(rs.next());
+            assertEquals(2, rs.getInt(1));
+            rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullIndexTableName);
+            assertTrue(rs.next());
+            assertEquals(2, rs.getInt(1));
+            
+            String sql = "SELECT LONG_COL1 from " + fullDataTableName + " WHERE LONG_COL2 = 2";
+            rs = conn.createStatement().executeQuery(sql);
+            assertTrue(rs.next());
+            assertFalse(rs.next());
+            
+            String dml = "DELETE from " + fullDataTableName + " WHERE long_col2 = 2";
+            assertEquals(1, conn.createStatement().executeUpdate(dml));
+            conn.commit();
+
+            String query = "SELECT /*+ NO_INDEX */ long_pk, varchar_pk, 1+long_pk, UPPER(varchar_pk) FROM "
+                    + fullDataTableName;
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals(1L, rs.getLong(1));
+            assertEquals("varchar1", rs.getString(2));
+            assertEquals(2L, rs.getLong(3));
+            assertEquals("VARCHAR1", rs.getString(4));
+            assertFalse(rs.next());
+
+            query = "SELECT long_pk, varchar_pk, 1+long_pk, UPPER(varchar_pk) FROM " + fullDataTableName;
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals(1L, rs.getLong(1));
+            assertEquals("varchar1", rs.getString(2));
+            assertEquals(2L, rs.getLong(3));
+            assertEquals("VARCHAR1", rs.getString(4));
+            assertFalse(rs.next());
+
+            query = "SELECT * FROM " + fullIndexTableName;
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+
+            assertEquals(1L, rs.getLong(1));
+            assertEquals("varchar1", rs.getString(2));
+            assertEquals(2L, rs.getLong(3));
+            assertEquals("VARCHAR1", rs.getString(4));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3d9adc6f/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexMetadataIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexMetadataIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexMetadataIT.java
index e9f0194..0ce36dd 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexMetadataIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexMetadataIT.java
@@ -607,4 +607,71 @@ public class IndexMetadataIT extends ParallelStatsDisabledIT {
                 "order by table_name" );
         assertFalse(rs.next());
     }
+    
+    @Test
+    public void testImmutableTableOnlyHasPrimaryKeyIndex() throws Exception {
+        helpTestTableOnlyHasPrimaryKeyIndex(false, false);
+    }
+
+    @Test
+    public void testImmutableLocalTableOnlyHasPrimaryKeyIndex() throws Exception {
+        helpTestTableOnlyHasPrimaryKeyIndex(false, true);
+    }
+
+    @Test
+    public void testMutableTableOnlyHasPrimaryKeyIndex() throws Exception {
+        helpTestTableOnlyHasPrimaryKeyIndex(true, false);
+    }
+
+    @Test
+    public void testMutableLocalTableOnlyHasPrimaryKeyIndex() throws Exception {
+        helpTestTableOnlyHasPrimaryKeyIndex(true, true);
+    }
+
+    private void helpTestTableOnlyHasPrimaryKeyIndex(boolean mutable,
+            boolean localIndex) throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String dataTableName = generateUniqueName();
+        String indexName = generateUniqueName();
+        try {
+            conn.createStatement().execute(
+                "CREATE TABLE " + dataTableName + " ("
+                            + "pk1 VARCHAR not null, "
+                            + "pk2 VARCHAR not null, "
+                            + "CONSTRAINT PK PRIMARY KEY (pk1, pk2))"
+                            + (!mutable ? "IMMUTABLE_ROWS=true" : ""));
+            String query = "SELECT * FROM " + dataTableName;
+            ResultSet rs = conn.createStatement().executeQuery(query);
+            assertFalse(rs.next());
+            conn.createStatement().execute(
+                "CREATE " + (localIndex ? "LOCAL" : "")
+                    + " INDEX " + indexName + " ON " + dataTableName + " (pk2, pk1)");
+            query = "SELECT * FROM " + indexName;
+            rs = conn.createStatement().executeQuery(query);
+            assertFalse(rs.next());
+
+            PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + dataTableName + " VALUES(?,?)");
+            stmt.setString(1, "k11");
+            stmt.setString(2, "k21");
+            stmt.execute();
+            conn.commit();
+
+            query = "SELECT * FROM " + indexName;
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("k21", rs.getString(1));
+            assertEquals("k11", rs.getString(2));
+            assertFalse(rs.next());
+            
+            query = "SELECT * FROM " + dataTableName + " WHERE pk2='k21'";
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("k11", rs.getString(1));
+            assertEquals("k21", rs.getString(2));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
 }


[07/25] phoenix git commit: PHOENIX-4246 Breakup join related tests into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/ee20a8c9/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinIT.java
new file mode 100644
index 0000000..56ec7f2
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinIT.java
@@ -0,0 +1,2316 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.join;
+
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.util.Properties;
+
+import org.apache.phoenix.cache.ServerCacheClient;
+import org.apache.phoenix.exception.SQLExceptionCode;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.QueryUtil;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public abstract class HashJoinIT extends BaseJoinIT {
+    
+    
+    public HashJoinIT(String[] indexDDL, String[] plans) {
+        super(indexDDL, plans);
+    }
+    
+    
+    @Test
+    public void testDefaultJoin() throws Exception {
+        Connection conn = getConnection();
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String query = "SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\"";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "S5");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "0000000006");
+            assertEquals(rs.getString(4), "S6");
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testInnerJoin() throws Exception {
+        Connection conn = getConnection();
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String query = "SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name, next value for " + seqName + " FROM " + tableName1 + " item INNER JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\"";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertEquals(1, rs.getInt(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertEquals(2, rs.getInt(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertEquals(3, rs.getInt(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertEquals(4, rs.getInt(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "S5");
+            assertEquals(5, rs.getInt(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "0000000006");
+            assertEquals(rs.getString(4), "S6");
+            assertEquals(6, rs.getInt(5));
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+            
+    @Test
+    public void testLeftJoin() throws Exception {
+        Connection conn = getConnection();
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String query[] = new String[3];
+        query[0] = "SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name, next value for " + seqName + " FROM " + tableName1 + " item LEFT JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
+        query[1] = "SELECT " + tableName1 + ".\"item_id\", " + tableName1 + ".name, " + tableName2 + ".\"supplier_id\", " + tableName2 + ".name, next value for " + seqName + " FROM " + tableName1 + " LEFT JOIN " + tableName2 + " ON " + tableName1 + ".\"supplier_id\" = " + tableName2 + ".\"supplier_id\" ORDER BY \"item_id\"";
+        query[2] = "SELECT item.\"item_id\", " + tableName1 + ".name, supp.\"supplier_id\", " + tableName2 + ".name, next value for " + seqName + " FROM " + tableName1 + " item LEFT JOIN " + tableName2 + " supp ON " + tableName1 + ".\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
+        try {
+            for (int i = 0; i < query.length; i++) {
+                PreparedStatement statement = conn.prepareStatement(query[i]);
+                ResultSet rs = statement.executeQuery();
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "0000000001");
+                assertEquals(rs.getString(2), "T1");
+                assertEquals(rs.getString(3), "0000000001");
+                assertEquals(rs.getString(4), "S1");
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "0000000002");
+                assertEquals(rs.getString(2), "T2");
+                assertEquals(rs.getString(3), "0000000001");
+                assertEquals(rs.getString(4), "S1");
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "0000000003");
+                assertEquals(rs.getString(2), "T3");
+                assertEquals(rs.getString(3), "0000000002");
+                assertEquals(rs.getString(4), "S2");
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "0000000004");
+                assertEquals(rs.getString(2), "T4");
+                assertEquals(rs.getString(3), "0000000002");
+                assertEquals(rs.getString(4), "S2");
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "0000000005");
+                assertEquals(rs.getString(2), "T5");
+                assertEquals(rs.getString(3), "0000000005");
+                assertEquals(rs.getString(4), "S5");
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "0000000006");
+                assertEquals(rs.getString(2), "T6");
+                assertEquals(rs.getString(3), "0000000006");
+                assertEquals(rs.getString(4), "S6");
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "invalid001");
+                assertEquals(rs.getString(2), "INVALID-1");
+                assertNull(rs.getString(3));
+                assertNull(rs.getString(4));
+
+                assertFalse(rs.next());
+                rs.close();
+                statement.close();
+            }
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testRightJoin() throws Exception {
+        Connection conn = getConnection();
+        String tableName1 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String query = "SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " supp RIGHT JOIN " + tableName2 + " item ON item.\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "S5");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "0000000006");
+            assertEquals(rs.getString(4), "S6");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "invalid001");
+            assertEquals(rs.getString(2), "INVALID-1");
+            assertNull(rs.getString(3));
+            assertNull(rs.getString(4));
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testInnerJoinWithPreFilters() throws Exception {
+        Connection conn = getConnection();
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String query1 = "SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item INNER JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" AND supp.\"supplier_id\" BETWEEN '0000000001' AND '0000000005'";
+        String query2 = "SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item INNER JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" AND (supp.\"supplier_id\" = '0000000001' OR supp.\"supplier_id\" = '0000000005')";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query1);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "S5");
+
+            assertFalse(rs.next());
+            
+            
+            statement = conn.prepareStatement(query2);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "S5");
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testLeftJoinWithPreFilters() throws Exception {
+        Connection conn = getConnection();
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String query = "SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item LEFT JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" AND (supp.\"supplier_id\" = '0000000001' OR supp.\"supplier_id\" = '0000000005') ORDER BY \"item_id\"";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertNull(rs.getString(3));
+            assertNull(rs.getString(4));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertNull(rs.getString(3));
+            assertNull(rs.getString(4));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "S5");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "T6");
+            assertNull(rs.getString(3));
+            assertNull(rs.getString(4));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "invalid001");
+            assertEquals(rs.getString(2), "INVALID-1");
+            assertNull(rs.getString(3));
+            assertNull(rs.getString(4));
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testJoinWithPostFilters() throws Exception {
+        Connection conn = getConnection();
+        String tableName1 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String query1 = "SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " supp RIGHT JOIN " + tableName2 + " item ON item.\"supplier_id\" = supp.\"supplier_id\" WHERE supp.\"supplier_id\" BETWEEN '0000000001' AND '0000000005'";
+        String query2 = "SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName2 + " item LEFT JOIN " + tableName1 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" WHERE supp.\"supplier_id\" = '0000000001' OR supp.\"supplier_id\" = '0000000005'";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query1);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "S5");
+
+            assertFalse(rs.next());
+            
+            
+            statement = conn.prepareStatement(query2);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "S5");
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testStarJoin() throws Exception {
+        Connection conn = getConnection();
+        String tableName1 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_CUSTOMER_TABLE_FULL_NAME);
+        String tableName3 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String[] query = new String[5];
+        query[0] = "SELECT \"order_id\", c.name, i.name iname, quantity, o.\"DATE\" FROM " + tableName1 + " o JOIN "
+            + tableName2 + " c ON o.\"customer_id\" = c.\"customer_id\" JOIN " 
+            + tableName3 + " i ON o.\"item_id\" = i.\"item_id\" ORDER BY \"order_id\"";
+        query[1] = "SELECT \"order_id\", c.name, i.name iname, quantity, o.\"DATE\" FROM " + tableName1 + " o, "
+                + tableName2 + " c, " 
+                + tableName3 + " i WHERE o.\"item_id\" = i.\"item_id\" AND o.\"customer_id\" = c.\"customer_id\" ORDER BY \"order_id\"";
+        query[2] = "SELECT /*+ NO_STAR_JOIN*/ \"order_id\", c.name, i.name iname, quantity, o.\"DATE\" FROM " + tableName1 + " o JOIN "
+                + tableName2 + " c ON o.\"customer_id\" = c.\"customer_id\" JOIN " 
+                + tableName3 + " i ON o.\"item_id\" = i.\"item_id\" ORDER BY \"order_id\"";
+        query[3] = "SELECT /*+ NO_STAR_JOIN*/  \"order_id\", c.name, i.name iname, quantity, o.\"DATE\" FROM (" + tableName1 + " o, "
+                + tableName2 + " c), " 
+                + tableName3 + " i WHERE o.\"item_id\" = i.\"item_id\" AND o.\"customer_id\" = c.\"customer_id\" ORDER BY \"order_id\"";
+        query[4] = "SELECT \"order_id\", c.name, i.name iname, quantity, o.\"DATE\" FROM " + tableName1 + " o, ("
+                + tableName2 + " c, " 
+                + tableName3 + " i) WHERE o.\"item_id\" = i.\"item_id\" AND o.\"customer_id\" = c.\"customer_id\" ORDER BY \"order_id\"";
+        try {
+            for (int i = 0; i < query.length; i++) {
+                PreparedStatement statement = conn.prepareStatement(query[i]);
+                ResultSet rs = statement.executeQuery();
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "000000000000001");
+                assertEquals(rs.getString("\"order_id\""), "000000000000001");
+                assertEquals(rs.getString(2), "C4");
+                assertEquals(rs.getString("C.name"), "C4");
+                assertEquals(rs.getString(3), "T1");
+                assertEquals(rs.getString("iName"), "T1");
+                assertEquals(rs.getInt(4), 1000);
+                assertEquals(rs.getInt("Quantity"), 1000);
+                assertNotNull(rs.getDate(5));
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "000000000000002");
+                assertEquals(rs.getString(2), "C3");
+                assertEquals(rs.getString(3), "T6");
+                assertEquals(rs.getInt(4), 2000);
+                assertNotNull(rs.getDate(5));
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "000000000000003");
+                assertEquals(rs.getString(2), "C2");
+                assertEquals(rs.getString(3), "T2");
+                assertEquals(rs.getInt(4), 3000);
+                assertNotNull(rs.getDate(5));
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "000000000000004");
+                assertEquals(rs.getString(2), "C4");
+                assertEquals(rs.getString(3), "T6");
+                assertEquals(rs.getInt(4), 4000);
+                assertNotNull(rs.getDate(5));
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "000000000000005");
+                assertEquals(rs.getString(2), "C5");
+                assertEquals(rs.getString(3), "T3");
+                assertEquals(rs.getInt(4), 5000);
+                assertNotNull(rs.getDate(5));
+
+                assertFalse(rs.next());
+                
+                if (i < 4) {
+                    rs = conn.createStatement().executeQuery("EXPLAIN " + query[i]);
+                    assertPlansEqual(plans[11 + (i/2)], QueryUtil.getExplainPlan(rs));
+                }
+            }
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testLeftJoinWithAggregation() throws Exception {
+        Connection conn = getConnection();
+        String tableName1 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String query1 = "SELECT i.name, sum(quantity) FROM " + tableName1 + " o LEFT JOIN " 
+            + tableName2 + " i ON o.\"item_id\" = i.\"item_id\" GROUP BY i.name ORDER BY i.name";
+        String query2 = "SELECT i.\"item_id\" iid, sum(quantity) q FROM " + tableName1 + " o LEFT JOIN " 
+                + tableName2 + " i ON o.\"item_id\" = i.\"item_id\" GROUP BY i.\"item_id\" ORDER BY q DESC";
+        String query3 = "SELECT i.\"item_id\" iid, sum(quantity) q FROM " + tableName2 + " i LEFT JOIN " 
+                + tableName1 + " o ON o.\"item_id\" = i.\"item_id\" GROUP BY i.\"item_id\" ORDER BY q DESC NULLS LAST, iid";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query1);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T1");
+            assertEquals(rs.getInt(2), 1000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T2");
+            assertEquals(rs.getInt(2), 3000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T3");
+            assertEquals(rs.getInt(2), 5000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T6");
+            assertEquals(rs.getInt(2), 6000);
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query1);
+            assertPlansEqual(plans[0], QueryUtil.getExplainPlan(rs));
+            
+            statement = conn.prepareStatement(query2);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000006");
+            assertEquals(rs.getInt("q"), 6000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000003");
+            assertEquals(rs.getInt("q"), 5000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000002");
+            assertEquals(rs.getInt("q"), 3000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000001");
+            assertEquals(rs.getInt("q"), 1000);
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query2);
+            assertPlansEqual(plans[1], QueryUtil.getExplainPlan(rs));
+            
+            statement = conn.prepareStatement(query3);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000006");
+            assertEquals(rs.getInt("q"), 6000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000003");
+            assertEquals(rs.getInt("q"), 5000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000002");
+            assertEquals(rs.getInt("q"), 3000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000001");
+            assertEquals(rs.getInt("q"), 1000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000004");
+            assertEquals(rs.getInt("q"), 0);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000005");
+            assertEquals(rs.getInt("q"), 0);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "invalid001");
+            assertEquals(rs.getInt("q"), 0);
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query3);
+            assertPlansEqual(plans[2], QueryUtil.getExplainPlan(rs));
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testRightJoinWithAggregation() throws Exception {
+        Connection conn = getConnection();
+        String tableName1 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String query1 = "SELECT i.name, sum(quantity) FROM " + tableName1 + " o RIGHT JOIN " 
+            + tableName2 + " i ON o.\"item_id\" = i.\"item_id\" GROUP BY i.name ORDER BY i.name";
+        String query2 = "SELECT i.\"item_id\" iid, sum(quantity) q FROM " + tableName1 + " o RIGHT JOIN " 
+            + tableName2 + " i ON o.\"item_id\" = i.\"item_id\" GROUP BY i.\"item_id\" ORDER BY q DESC NULLS LAST, iid";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query1);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "INVALID-1");
+            assertEquals(rs.getInt(2), 0);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T1");
+            assertEquals(rs.getInt(2), 1000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T2");
+            assertEquals(rs.getInt(2), 3000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T3");
+            assertEquals(rs.getInt(2), 5000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T4");
+            assertEquals(rs.getInt(2), 0);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T5");
+            assertEquals(rs.getInt(2), 0);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T6");
+            assertEquals(rs.getInt(2), 6000);
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query1);
+            assertPlansEqual(plans[3], QueryUtil.getExplainPlan(rs));
+            
+            statement = conn.prepareStatement(query2);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000006");
+            assertEquals(rs.getInt("q"), 6000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000003");
+            assertEquals(rs.getInt("q"), 5000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000002");
+            assertEquals(rs.getInt("q"), 3000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000001");
+            assertEquals(rs.getInt("q"), 1000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000004");
+            assertEquals(rs.getInt("q"), 0);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000005");
+            assertEquals(rs.getInt("q"), 0);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "invalid001");
+            assertEquals(rs.getInt("q"), 0);
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query2);
+            assertPlansEqual(plans[4], QueryUtil.getExplainPlan(rs));
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testLeftRightJoin() throws Exception {
+        Connection conn = getConnection();
+        String tableName1 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName3 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String query1 = "SELECT \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + tableName1 + " o LEFT JOIN "
+                + tableName2 + " i ON o.\"item_id\" = i.\"item_id\" RIGHT JOIN "
+                + tableName3 + " s ON i.\"supplier_id\" = s.\"supplier_id\" ORDER BY \"order_id\", s.\"supplier_id\" DESC";
+        String query2 = "SELECT \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + tableName1 + " o LEFT JOIN "
+                + "(" + tableName2 + " i RIGHT JOIN " + tableName3 + " s ON i.\"supplier_id\" = s.\"supplier_id\")" 
+                + " ON o.\"item_id\" = i.\"item_id\" ORDER BY \"order_id\", s.\"supplier_id\" DESC";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query1);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertNull(rs.getString(2));
+            assertEquals(rs.getString(3), "S5");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertNull(rs.getString(2));
+            assertEquals(rs.getString(3), "S4");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertNull(rs.getString(2));
+            assertEquals(rs.getString(3), "S3");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 1000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 2000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 3000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000004");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 4000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "S2");
+            assertEquals(rs.getInt(4), 5000);
+            assertNotNull(rs.getDate(5));
+
+            assertFalse(rs.next());
+            
+            statement = conn.prepareStatement(query2);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 1000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 2000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 3000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000004");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 4000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "S2");
+            assertEquals(rs.getInt(4), 5000);
+            assertNotNull(rs.getDate(5));
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testMultiLeftJoin() throws Exception {
+        Connection conn = getConnection();
+        String[] queries = {
+                "SELECT \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME) + " o LEFT JOIN "
+                        + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i ON o.\"item_id\" = i.\"item_id\" LEFT JOIN "
+                        + getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME) + " s ON i.\"supplier_id\" = s.\"supplier_id\"",
+                "SELECT \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME) + " o LEFT JOIN "
+                        + "(" + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i LEFT JOIN " + getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME) + " s ON i.\"supplier_id\" = s.\"supplier_id\") " 
+                        + "ON o.\"item_id\" = i.\"item_id\""};
+        try {
+            for (String query : queries) {
+                PreparedStatement statement = conn.prepareStatement(query);
+                ResultSet rs = statement.executeQuery();
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "000000000000001");
+                assertEquals(rs.getString(2), "T1");
+                assertEquals(rs.getString(3), "S1");
+                assertEquals(rs.getInt(4), 1000);
+                assertNotNull(rs.getDate(5));
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "000000000000002");
+                assertEquals(rs.getString(2), "T6");
+                assertEquals(rs.getString(3), "S6");
+                assertEquals(rs.getInt(4), 2000);
+                assertNotNull(rs.getDate(5));
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "000000000000003");
+                assertEquals(rs.getString(2), "T2");
+                assertEquals(rs.getString(3), "S1");
+                assertEquals(rs.getInt(4), 3000);
+                assertNotNull(rs.getDate(5));
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "000000000000004");
+                assertEquals(rs.getString(2), "T6");
+                assertEquals(rs.getString(3), "S6");
+                assertEquals(rs.getInt(4), 4000);
+                assertNotNull(rs.getDate(5));
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "000000000000005");
+                assertEquals(rs.getString(2), "T3");
+                assertEquals(rs.getString(3), "S2");
+                assertEquals(rs.getInt(4), 5000);
+                assertNotNull(rs.getDate(5));
+
+                assertFalse(rs.next());
+            }
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testMultiRightJoin() throws Exception {
+        Connection conn = getConnection();
+        String query = "SELECT \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME) + " o RIGHT JOIN "
+            + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i ON o.\"item_id\" = i.\"item_id\" RIGHT JOIN "
+            + getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME) + " s ON i.\"supplier_id\" = s.\"supplier_id\" ORDER BY \"order_id\", s.\"supplier_id\" DESC";
+
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "S5");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertNull(rs.getString(2));
+            assertEquals(rs.getString(3), "S4");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertNull(rs.getString(2));
+            assertEquals(rs.getString(3), "S3");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "S2");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 1000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 2000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 3000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000004");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 4000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "S2");
+            assertEquals(rs.getInt(4), 5000);
+            assertNotNull(rs.getDate(5));
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    // Basically a copy of testMultiRightJoin, but with a very small result scan chunk size
+    // to test that repeated row keys within a single chunk are handled properly
+    @Test
+    public void testMultiRightJoin_SmallChunkSize() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        props.setProperty(QueryServices.SCAN_RESULT_CHUNK_SIZE, "1");
+        props.put(ServerCacheClient.HASH_JOIN_SERVER_CACHE_RESEND_PER_SERVER, "true");
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String query = "SELECT \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME) + " o RIGHT JOIN "
+                + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i ON o.\"item_id\" = i.\"item_id\" RIGHT JOIN "
+                + getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME) + " s ON i.\"supplier_id\" = s.\"supplier_id\" ORDER BY \"order_id\", s.\"supplier_id\" DESC";
+
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "S5");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertNull(rs.getString(2));
+            assertEquals(rs.getString(3), "S4");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertNull(rs.getString(2));
+            assertEquals(rs.getString(3), "S3");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "S2");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 1000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 2000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 3000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000004");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 4000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "S2");
+            assertEquals(rs.getInt(4), 5000);
+            assertNotNull(rs.getDate(5));
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testJoinWithWildcard() throws Exception {
+        Connection conn = getConnection();
+        String query = "SELECT * FROM " + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " LEFT JOIN " + getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME) + " supp ON " + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "0000000001");
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "T1");
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 100);
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 5);
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 10);
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000001");
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Item T1");
+            assertEquals(rs.getString("SUPP.supplier_id"), "0000000001");
+            assertEquals(rs.getString("supp.name"), "S1");
+            assertEquals(rs.getString("supp.phone"), "888-888-1111");
+            assertEquals(rs.getString("supp.address"), "101 YYY Street");
+            assertEquals(rs.getString("supp.loc_id"), "10001");            
+            assertTrue (rs.next());
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "0000000002");
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "T2");
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 200);
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 5);
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 8);
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000001");
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Item T2");
+            assertEquals(rs.getString("SUPP.supplier_id"), "0000000001");
+            assertEquals(rs.getString("supp.name"), "S1");
+            assertEquals(rs.getString("supp.phone"), "888-888-1111");
+            assertEquals(rs.getString("supp.address"), "101 YYY Street");
+            assertEquals(rs.getString("supp.loc_id"), "10001");            
+            assertTrue (rs.next());
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "0000000003");
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "T3");
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 300);
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 8);
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 12);
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000002");
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Item T3");
+            assertEquals(rs.getString("SUPP.supplier_id"), "0000000002");
+            assertEquals(rs.getString("supp.name"), "S2");
+            assertEquals(rs.getString("supp.phone"), "888-888-2222");
+            assertEquals(rs.getString("supp.address"), "202 YYY Street");
+            assertEquals(rs.getString("supp.loc_id"), "10002");            
+            assertTrue (rs.next());
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "0000000004");
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "T4");
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 400);
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 6);
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 10);
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000002");
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Item T4");
+            assertEquals(rs.getString("SUPP.supplier_id"), "0000000002");
+            assertEquals(rs.getString("supp.name"), "S2");
+            assertEquals(rs.getString("supp.phone"), "888-888-2222");
+            assertEquals(rs.getString("supp.address"), "202 YYY Street");
+            assertEquals(rs.getString("supp.loc_id"), "10002");            
+            assertTrue (rs.next());
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "0000000005");
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "T5");
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 500);
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 8);
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 15);
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000005");
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Item T5");
+            assertEquals(rs.getString("SUPP.supplier_id"), "0000000005");
+            assertEquals(rs.getString("supp.name"), "S5");
+            assertEquals(rs.getString("supp.phone"), "888-888-5555");
+            assertEquals(rs.getString("supp.address"), "505 YYY Street");
+            assertEquals(rs.getString("supp.loc_id"), "10005");            
+            assertTrue (rs.next());
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "0000000006");
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "T6");
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 600);
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 8);
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 15);
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000006");
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Item T6");
+            assertEquals(rs.getString("SUPP.supplier_id"), "0000000006");
+            assertEquals(rs.getString("supp.name"), "S6");
+            assertEquals(rs.getString("supp.phone"), "888-888-6666");
+            assertEquals(rs.getString("supp.address"), "606 YYY Street");
+            assertEquals(rs.getString("supp.loc_id"), "10006");            
+            assertTrue (rs.next());
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "invalid001");
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "INVALID-1");
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 0);
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 0);
+            assertEquals(rs.getInt(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 0);
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000000");
+            assertEquals(rs.getString(getDisplayTableName(conn,JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Invalid item for join test");
+            assertNull(rs.getString("SUPP.supplier_id"));
+            assertNull(rs.getString("supp.name"));
+            assertNull(rs.getString("supp.phone"));
+            assertNull(rs.getString("supp.address"));
+            assertNull(rs.getString("supp.loc_id"));
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            assertPlansEqual(plans[5], QueryUtil.getExplainPlan(rs));
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testJoinWithTableWildcard() throws Exception {
+        Connection conn = getConnection();
+        String query = "SELECT s.*, "+ getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".*, \"order_id\" FROM " + getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME) + " o RIGHT JOIN " 
+                + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i ON o.\"item_id\" = i.\"item_id\" RIGHT JOIN "
+                + getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME) + " s ON i.\"supplier_id\" = s.\"supplier_id\" ORDER BY \"order_id\", s.\"supplier_id\" DESC";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            ResultSetMetaData md = rs.getMetaData();
+            assertEquals(md.getColumnCount(), 13);
+            
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "S5");
+            assertEquals(rs.getString(3), "888-888-5555");
+            assertEquals(rs.getString(4), "505 YYY Street");
+            assertEquals(rs.getString(5), "10005");
+            assertEquals(rs.getString(6), "0000000005");
+            assertEquals(rs.getString(7), "T5");
+            assertEquals(rs.getInt(8), 500);
+            assertEquals(rs.getInt(9), 8);
+            assertEquals(rs.getInt(10), 15);
+            assertEquals(rs.getString(11), "0000000005");
+            assertEquals(rs.getString(12), "Item T5");
+            assertNull(rs.getString(13));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "S4");
+            assertEquals(rs.getString(3), "888-888-4444");
+            assertEquals(rs.getString(4), "404 YYY Street");
+            assertNull(rs.getString(5));
+            assertNull(rs.getString(6));
+            assertNull(rs.getString(7));
+            assertEquals(rs.getInt(8), 0);
+            assertEquals(rs.getInt(9), 0);
+            assertEquals(rs.getInt(10), 0);
+            assertNull(rs.getString(11));
+            assertNull(rs.getString(12));            
+            assertNull(rs.getString(13));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "S3");
+            assertEquals(rs.getString(3), "888-888-3333");
+            assertEquals(rs.getString(4), "303 YYY Street");
+            assertNull(rs.getString(5));
+            assertNull(rs.getString(6));
+            assertNull(rs.getString(7));
+            assertEquals(rs.getInt(8), 0);
+            assertEquals(rs.getInt(9), 0);
+            assertEquals(rs.getInt(10), 0);
+            assertNull(rs.getString(11));
+            assertNull(rs.getString(12));            
+            assertNull(rs.getString(13));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "S2");
+            assertEquals(rs.getString(3), "888-888-2222");
+            assertEquals(rs.getString(4), "202 YYY Street");
+            assertEquals(rs.getString(5), "10002");
+            assertEquals(rs.getString(6), "0000000004");
+            assertEquals(rs.getString(7), "T4");
+            assertEquals(rs.getInt(8), 400);
+            assertEquals(rs.getInt(9), 6);
+            assertEquals(rs.getInt(10), 10);
+            assertEquals(rs.getString(11), "0000000002");
+            assertEquals(rs.getString(12), "Item T4");
+            assertNull(rs.getString(13));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "S1");
+            assertEquals(rs.getString(3), "888-888-1111");
+            assertEquals(rs.getString(4), "101 YYY Street");
+            assertEquals(rs.getString(5), "10001");
+            assertEquals(rs.getString(6), "0000000001");
+            assertEquals(rs.getString(7), "T1");
+            assertEquals(rs.getInt(8), 100);
+            assertEquals(rs.getInt(9), 5);
+            assertEquals(rs.getInt(10), 10);
+            assertEquals(rs.getString(11), "0000000001");
+            assertEquals(rs.getString(12), "Item T1");
+            assertEquals(rs.getString(13), "000000000000001");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "S6");
+            assertEquals(rs.getString(3), "888-888-6666");
+            assertEquals(rs.getString(4), "606 YYY Street");
+            assertEquals(rs.getString(5), "10006");
+            assertEquals(rs.getString(6), "0000000006");
+            assertEquals(rs.getString(7), "T6");
+            assertEquals(rs.getInt(8), 600);
+            assertEquals(rs.getInt(9), 8);
+            assertEquals(rs.getInt(10), 15);
+            assertEquals(rs.getString(11), "0000000006");
+            assertEquals(rs.getString(12), "Item T6");
+            assertEquals(rs.getString(13), "000000000000002");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "S1");
+            assertEquals(rs.getString(3), "888-888-1111");
+            assertEquals(rs.getString(4), "101 YYY Street");
+            assertEquals(rs.getString(5), "10001");
+            assertEquals(rs.getString(6), "0000000002");
+            assertEquals(rs.getString(7), "T2");
+            assertEquals(rs.getInt(8), 200);
+            assertEquals(rs.getInt(9), 5);
+            assertEquals(rs.getInt(10), 8);
+            assertEquals(rs.getString(11), "0000000001");
+            assertEquals(rs.getString(12), "Item T2");
+            assertEquals(rs.getString(13), "000000000000003");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "S6");
+            assertEquals(rs.getString(3), "888-888-6666");
+            assertEquals(rs.getString(4), "606 YYY Street");
+            assertEquals(rs.getString(5), "10006");
+            assertEquals(rs.getString(6), "0000000006");
+            assertEquals(rs.getString(7), "T6");
+            assertEquals(rs.getInt(8), 600);
+            assertEquals(rs.getInt(9), 8);
+            assertEquals(rs.getInt(10), 15);
+            assertEquals(rs.getString(11), "0000000006");
+            assertEquals(rs.getString(12), "Item T6");
+            assertEquals(rs.getString(13), "000000000000004");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "S2");
+            assertEquals(rs.getString(3), "888-888-2222");
+            assertEquals(rs.getString(4), "202 YYY Street");
+            assertEquals(rs.getString(5), "10002");
+            assertEquals(rs.getString(6), "0000000003");
+            assertEquals(rs.getString(7), "T3");
+            assertEquals(rs.getInt(8), 300);
+            assertEquals(rs.getInt(9), 8);
+            assertEquals(rs.getInt(10), 12);
+            assertEquals(rs.getString(11), "0000000002");
+            assertEquals(rs.getString(12), "Item T3");
+            assertEquals(rs.getString(13), "000000000000005");
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }        
+    }
+    
+    @Test
+    public void testJoinMultiJoinKeys() throws Exception {
+        Connection conn = getConnection();
+        String query = "SELECT c.name, s.name FROM " + getTableName(conn, JOIN_CUSTOMER_TABLE_FULL_NAME) + " c LEFT JOIN " + getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME) + " s ON \"customer_id\" = \"supplier_id\" AND c.loc_id = s.loc_id AND substr(s.name, 2, 1) = substr(c.name, 2, 1)";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "C1");
+            assertEquals(rs.getString(2), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "C2");
+            assertNull(rs.getString(2));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "C3");
+            assertEquals(rs.getString(2), "S3");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "C4");
+            assertNull(rs.getString(2));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "C5");
+            assertEquals(rs.getString(2), "S5");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "C6");
+            assertNull(rs.getString(2));
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testJoinWithDifferentNumericJoinKeyTypes() throws Exception {
+        Connection conn = getConnection();
+        String query = "SELECT \"order_id\", i.name, i.price, discount2, quantity FROM " + getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME) + " o INNER JOIN " 
+            + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i ON o.\"item_id\" = i.\"item_id\" AND o.price = (i.price * (100 - discount2)) / 100.0 WHERE quantity < 5000";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000004");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getInt(3), 600);
+            assertEquals(rs.getInt(4), 15);
+            assertEquals(rs.getInt(5), 4000);
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testJoinWithDifferentDateJoinKeyTypes() throws Exception {
+        Connection conn = getConnection();
+        String query = "SELECT \"order_id\", c.name, o.\"DATE\" FROM " + getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME) + " o INNER JOIN "
+            + getTableName(conn, JOIN_CUSTOMER_TABLE_FULL_NAME) + " c ON o.\"customer_id\" = c.\"customer_id\" AND o.\"DATE\" = c.\"DATE\"";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertEquals(rs.getString(2), "C4");
+            assertEquals(rs.getTimestamp(3), new Timestamp(format.parse("2013-11-22 14:22:56").getTime()));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+            assertEquals(rs.getString(2), "C3");
+            assertEquals(rs.getTimestamp(3), new Timestamp(format.parse("2013-11-25 10:06:29").getTime()));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertEquals(rs.getString(2), "C2");
+            assertEquals(rs.getTimestamp(3), new Timestamp(format.parse("2013-11-25 16:45:07").getTime()));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+            assertEquals(rs.getString(2), "C5");
+            assertEquals(rs.getTimestamp(3), new Timestamp(format.parse("2013-11-27 09:37:50").getTime()));
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testJoinWithIncomparableJoinKeyTypes() throws Exception {
+        Connection conn = getConnection();
+        String query = "SELECT \"order_id\", i.name, i.price, discount2, quantity FROM " + getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME) + " o INNER JOIN " 
+            + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i ON o.\"item_id\" = i.\"item_id\" AND o.price / 100 = substr(i.name, 2, 1)";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            statement.executeQuery();
+            fail("Should have got SQLException.");
+        } catch (SQLException e) {
+            assertEquals(e.getErrorCode(), SQLExceptionCode.TYPE_MISMATCH.getErrorCode());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testJoinPlanWithIndex() throws Exception {
+        Connection conn = getConnection();
+        String query1 = "SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " item LEFT JOIN " + getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME) + " supp ON substr(item.name, 2, 1) = substr(supp.name, 2, 1) AND (supp.name BETWEEN 'S1' AND 'S5') WHERE item.name BETWEEN 'T1' AND 'T5'";
+        String query2 = "SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " item INNER JOIN " + getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME) + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" WHERE (item.name = 'T1' OR item.name = 'T5') AND (supp.name = 'S1' OR supp.name = 'S5')";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query1);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "0000000003");
+            assertEquals(rs.getString(4), "S3");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000004");
+            assertEquals(rs.getString(4), "S4");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "S5");
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query1);
+            assertPlansEqual(plans[6], QueryUtil.getExplainPlan(rs));
+            
+            
+            statement = conn.prepareStatement(query2);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "S5");
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query2);
+            assertPlansEqual(plans[7], QueryUtil.getExplainPlan(rs));
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testJoinWithSkipMergeOptimization() throws Exception {
+        Connection conn = getConnection();
+        String query = "SELECT s.name FROM " + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i JOIN " 
+            + getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME) + " o ON o.\"item_id\" = i.\"item_id\" AND quantity < 5000 JOIN "
+            + getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME) + " s ON i.\"supplier_id\" = s.\"supplier_id\"";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "S6");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "S6");
+            
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            assertPlansEqual(plans[8], QueryUtil.getExplainPlan(rs));
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testSelfJoin() throws Exception {
+        Connection conn = getConnection();
+        String query1 = "SELECT i2.\"item_id\", i1.name FROM " + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i1 JOIN " 
+            + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i2 ON i1.\"item_id\" = i2.\"item_id\" ORDER BY i1.\"item_id\"";
+        String query2 = "SELECT i1.name, i2.name FROM " + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i1 JOIN " 
+            + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i2 ON i1.\"item_id\" = i2.\"supplier_id\" ORDER BY i1.name, i2.name";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query1);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "T6");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "invalid001");
+            assertEquals(rs.getString(2), "INVALID-1");
+            
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query1);
+            assertPlansEqual(plans[9], QueryUtil.getExplainPlan(rs));
+
+            statement = conn.prepareStatement(query2);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T1");
+            assertEquals(rs.getString(2), "T1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T1");
+            assertEquals(rs.getString(2), "T2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T2");
+            assertEquals(rs.getString(2), "T3");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T2");
+            assertEquals(rs.getString(2), "T4");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T5");
+            assertEquals(rs.getString(2), "T5");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T6");
+            assertEquals(rs.getString(2), "T6");
+            
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query2);
+            assertPlansEqual(plans[10], QueryUtil.getExplainPlan(rs));
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testUpsertWithJoin() throws Exception {
+        String tempTable = generateUniqueName();
+        Connection conn = getConnection();
+        conn.setAutoCommit(true);
+        try {
+            conn.createStatement().execute("CREATE TABLE " + tempTable 
+                    + "   (\"order_id\" varchar not null, " 
+                    + "    item_name varchar not null, " 
+                    + "    supplier_name varchar, "
+                    + "    quantity integer, "
+                    + "    \"DATE\" timestamp "
+                    + "    CONSTRAINT pk PRIMARY KEY (\"order_id\", item_name))");
+            conn.createStatement().execute("UPSERT INTO " + tempTable 
+                    + "(\"order_id\", item_name, supplier_name, quantity, \"DATE\") "
+                    + "SELECT \"order_id\", i.name, s.name, quantity, \"DATE\" FROM "
+                    + getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME) + " o LEFT JOIN " 
+                    + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i ON o.\"item_id\" = i.\"item_id\" LEFT JOIN "
+                    + getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME) + " s ON i.\"supplier_id\" = s.\"supplier_id\"");
+            conn.createStatement().execute("UPSERT INTO " + tempTable 
+                    + "(\"order_id\", item_name, quantity) " 
+                    + "SELECT 'ORDER_SUM', i.name, sum(quantity) FROM " 
+                    + getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME) + " o LEFT JOIN " 
+                    + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i ON o.\"item_id\" = i.\"item_id\" " 
+                    + "GROUP BY i.name ORDER BY i.name");
+            
+            String query = "SELECT * FROM " + tempTable;
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 1000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 2000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 3000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000004");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 4000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "S2");
+            assertEquals(rs.getInt(4), 5000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "ORDER_SUM");
+            assertEquals(rs.getString(2), "T1");
+            assertNull(rs.getString(3));
+            assertEquals(rs.getInt(4), 1000);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "ORDER_SUM");
+            assertEquals(rs.getString(2), "T2");
+            assertNull(rs.getString(3));
+            assertEquals(rs.getInt(4), 3000);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "ORDER_SUM");
+            assertEquals(rs.getString(2), "T3");
+            assertNull(rs.getString(3));
+            assertEquals(rs.getInt(4), 5000);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "ORDER_SUM");
+            assertEquals(rs.getString(2), "T6");
+            assertNull(rs.getString(3));
+            assertEquals(rs.getInt(4), 6000);
+            assertNull(rs.getDate(5));
+
+            assertFalse(rs.next());
+
+            //Bug: PHOENIX-1182
+            String sourceTable = generateUniqueName();
+            String joinTable = generateUniqueName();
+            conn.createStatement().execute("CREATE TABLE " + sourceTable 
+                    + "   (TID CHAR(3) NOT NULL, "
+                    + "    A UNSIGNED_INT NOT NULL, " 
+                    + "    B UNSIGNED_INT NOT NULL "
+                    + "    CONSTRAINT pk PRIMARY KEY (TID, A, B))");
+            conn.createStatement().execute("CREATE TABLE " + joinTable 
+                    + "   (TID CHAR(3) NOT NULL, "
+                    + "    A UNSIGNED_INT NOT NULL, "
+                    + "    B UNSIGNED_INT NOT NULL, "
+                    + "    COUNT UNSIGNED_INT "
+                    + "    CONSTRAINT pk PRIMARY KEY (TID, A, B))");
+            
+            PreparedStatement upsertStmt = conn.prepareStatement(
+                    "upsert into " + sourceTable + "(TID, A, B) " + "values (?, ?, ?)");
+            upsertStmt.setString(1, "1");
+            upsertStmt.setInt(2, 1);
+            upsertStmt.setInt(3, 1);
+            upsertStmt.execute();
+            upsertStmt.setString(1, "1");
+            upsertStmt.setInt(2, 1);
+            upsertStmt.setInt(3, 2);
+            upsertStmt.execute();
+            upsertStmt.setString(1, "1");
+            upsertStmt.setInt(2, 1);
+            upsertStmt.setInt(3, 3);
+            upsertStmt.execute();
+            upsertStmt.setString(1, "1");
+            upsertStmt.setInt(2, 2);
+            upsertStmt.setInt(3, 1);
+            upsertStmt.execute();
+            upsertStmt.setString(1, "1");
+            upsertStmt.setInt(2, 2);
+            upsertStmt.setInt(3, 2);
+            upsertStmt.execute();
+            conn.commit();
+            
+            upsertStmt = conn.prepareStatement(
+                    "upsert into " + joinTable + "(TID, A, B, COUNT) "
+                            + "SELECT t1.TID, t1.A, t2.A, COUNT(*) "
+                            + "FROM " + sourceTable + " t1 "
+                            + "INNER JOIN " + sourceTable + " t2 ON t1.B = t2.B "
+                            + "WHERE t1.A != t2.A AND t1.TID = '1' AND t2.TID = '1' "
+                            + "GROUP BY t1.TID, t1.A, t2.A");
+            upsertStmt.execute();
+            conn.commit();            
+
+            rs = statement.executeQuery("SELECT * FROM " + joinTable);
+            assertTrue(rs.next());
+            assertEquals(rs.getString(1), "1");
+            assertEquals(rs.getInt(2), 1);
+            assertEquals(rs.getInt(3), 2);
+            assertEquals(rs.getInt(4), 2);
+            assertTrue(rs.next());
+            assertEquals(rs.getString(1), "1");
+            assertEquals(rs.getInt(2), 2);
+            assertEquals(rs.getInt(3), 1);
+            assertEquals(rs.getInt(4), 2);
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testSubJoin() throws Exception {
+        Connection conn = getConnection();
+        String query1 = "SELECT i.name, count(c.name), min(s.name), max(quantity) FROM " + getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME) + " o LEFT JOIN " 
+                + "(" + getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME) + " s RIGHT JOIN " + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i ON i.\"supplier_id\" = s.\"supplier_id\")" 
+                + " ON o.\"item_id\" = i.\"item_id\" LEFT JOIN " 
+                + getTableName(conn, JOIN_CUSTOMER_TABLE_FULL_NAME) + " c ON c.\"customer_id\" = o.\"customer_id\" GROUP BY i.name ORDER BY i.name";
+        String query2 = "SELECT * FROM " + getTableName(conn, JOIN_CUSTOMER_TABLE_FULL_NAME) + " c INNER JOIN " 
+                + "(" + getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME) + " o INNER JOIN " 
+                + "(" + getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME) + " s RIGHT JOIN " + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i ON i.\"supplier_id\" = s.\"supplier_id\")" 
+                + " ON o.\"item_id\" = i.\"item_id\") ON c.\"customer_id\" = o.\"customer_id\"" 
+                + " WHERE c.\"customer_id\" <= '0000000005' AND \"order_id\" != '000000000000003' AND i.name != 'T3' ORDER BY c.\"customer_id\", i.name";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query1);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T1");
+            assertEquals(rs.getInt(2), 1);
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 1000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T2");
+            assertEquals(rs.getInt(2), 1);
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 3000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T3");
+            assertEquals(rs.getInt(2), 1);
+            assertEquals(rs.getString(3), "S2");
+            assertEquals(rs.getInt(4), 5000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T6");
+            assertEquals(rs.getInt(2), 2);
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 4000);
+
+            assertFalse(rs.next());
+            
+            statement = conn.prepareStatement(query2);
+            rs = statement.executeQuery();
+            assertTrue(rs.next());
+            assertEquals(rs.getString("C.customer_id"), "0000000003");
+            assertEquals(rs.getString("c.name"), "C3");
+            assertEquals(rs.getString("c.phone"), "999-999-3333");
+            assertEquals(rs.getString("c.address"), "303 XXX Street");
+            assertNull(rs.getString("c.loc_id"));
+            assertEquals(rs.getDate("c.date"), new Date(format.parse("2013-11-25 10:06:29").getTime()));
+            assertEquals(rs.getString("O.order_id"), "000000000000002");
+            assertEquals(rs.getString("O.customer_id"), "0000000003");
+            assertEquals(rs.getString("O.item_id"), "0000000006");
+            assertEquals(rs.getInt("o.price"), 552);
+            assertEquals(rs.getInt("o.quantity"), 2000);
+            assertEquals(rs.getTimestamp("o.date"), new Timestamp(format.parse("2013-11-25 10:06:29").getTime()));
+            assertEquals(rs.getString("I.item_id"), "0000000006");
+            as

<TRUNCATED>

[17/25] phoenix git commit: PHOENIX-4244 Breakup ArrayIT into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/527f786a/phoenix-core/src/it/java/org/apache/phoenix/end2end/Array3IT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/Array3IT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/Array3IT.java
new file mode 100644
index 0000000..73c7e63
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/Array3IT.java
@@ -0,0 +1,770 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end;
+
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Array;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.apache.phoenix.util.PropertiesUtil;
+import org.junit.Test;
+
+public class Array3IT extends ArrayIT {
+
+    @Test
+    public void testArrayConstructorWithMultipleRows5() throws Exception {
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "CREATE TABLE   " + table + "  (region_name VARCHAR PRIMARY KEY, a VARCHAR, b VARCHAR)";
+        conn.createStatement().execute(ddl);
+        conn.commit();
+        conn.close();
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO   " + table + " (region_name, a, b) VALUES('a', 'foo', 'abc')");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO   " + table + " (region_name, a, b) VALUES('b', 'abc', 'dfg')");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO   " + table + " (region_name, a, b) VALUES('c', 'foo', 'abc')");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(ARRAY[a,b], 'oo') from   " + table);
+        assertTrue(rs.next());
+        Array arr = conn.createArrayOf("VARCHAR", new Object[]{"foo", "abc", "oo"});
+        assertEquals(arr, rs.getArray(1));
+        rs.next();
+        arr = conn.createArrayOf("VARCHAR", new Object[]{"abc", "dfg", "oo"});
+        assertEquals(arr, rs.getArray(1));
+        rs.next();
+        arr = conn.createArrayOf("VARCHAR", new Object[]{"foo", "abc", "oo"});
+        assertEquals(arr, rs.getArray(1));
+        rs.next();
+    }
+    
+    @Test
+    public void testPKWithDescArray() throws Exception {
+        Connection conn;
+        PreparedStatement stmt;
+        ResultSet rs;
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        conn.createStatement()
+                .execute(
+                        "CREATE TABLE   " + table + "  ( a VARCHAR ARRAY PRIMARY KEY DESC)\n");
+        conn.close();
+        
+        conn = DriverManager.getConnection(getUrl(), props);
+        stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES(?)");
+        Array a1 = conn.createArrayOf("VARCHAR", new String[] { "a", "ba" });
+        stmt.setArray(1, a1);
+        stmt.execute();
+        Array a2 = conn.createArrayOf("VARCHAR", new String[] { "a", "c" });
+        stmt.setArray(1, a2);
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        rs = conn.createStatement().executeQuery("SELECT a FROM   " + table + "  ORDER BY a DESC");
+        assertTrue(rs.next());
+        assertEquals(a2, rs.getArray(1));
+        assertTrue(rs.next());
+        assertEquals(a1, rs.getArray(1));
+        assertFalse(rs.next());
+        conn.close();
+        
+        conn = DriverManager.getConnection(getUrl(), props);
+        stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES(?)");
+        Array a3 = conn.createArrayOf("VARCHAR", new String[] { "a", "b" });
+        stmt.setArray(1, a3);
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        rs = conn.createStatement().executeQuery("SELECT a FROM   " + table + "  ORDER BY a DESC");
+        assertTrue(rs.next());
+        assertEquals(a2, rs.getArray(1));
+        assertTrue(rs.next());
+        assertEquals(a1, rs.getArray(1));
+        assertTrue(rs.next());
+        assertEquals(a3, rs.getArray(1));
+        assertFalse(rs.next());
+        conn.close();
+    }
+
+    @Test
+    public void testComparisonOperatorsForDesc1()throws Exception{
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "create table   " + table + "  (k varchar array primary key desc)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("upsert into   " + table + "  values (array['a', 'c'])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("select * from   " + table + "  where k >= array['a', 'b']");
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+    }
+
+    @Test
+    public void testComparisonOperatorsForDesc2()throws Exception{
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "create table   " + table + "  (k varchar array primary key desc)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("upsert into   " + table + "  values (array['a', 'c'])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("select * from   " + table + "  where k >= array['a', 'c']");
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+    }
+
+    @Test
+    public void testComparisonOperatorsForDesc3()throws Exception{
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "create table   " + table + "  (k varchar array primary key desc)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("upsert into   " + table + "  values (array['a', 'c'])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("select * from   " + table + "  where k > array['a', 'b']");
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+    }
+
+    @Test
+    public void testComparisonOperatorsForDesc4()throws Exception{
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "create table   " + table + "  (k varchar array primary key desc)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("upsert into   " + table + "  values (array['a', 'b'])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("select * from   " + table + "  where k <= array['a', 'c']");
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+    }
+
+    @Test
+    public void testComparisonOperatorsForDesc5()throws Exception{
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "create table   " + table + "  (k varchar array primary key desc)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("upsert into   " + table + "  values (array['a', 'b'])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("select * from   " + table + "  where k <= array['a', 'b']");
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+    }
+
+    @Test
+    public void testComparisonOperatorsForDesc6()throws Exception{
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "create table   " + table + "  (k varchar array primary key desc)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("upsert into   " + table + "  values (array['a', 'b'])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("select * from   " + table + "  where k < array['a', 'c']");
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+    }
+
+    @Test
+    public void testComparisonOperatorsForDesc7()throws Exception{
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "create table   " + table + "  (k integer array primary key desc)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("upsert into   " + table + "  values (array[1, 2])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("select * from   " + table + "  where k < array[1, 4]");
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+    }
+
+    @Test
+    public void testComparisonOperatorsForDesc8()throws Exception{
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "create table   " + table + "  (k integer array primary key desc)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("upsert into   " + table + "  values (array[1, 2])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("select * from   " + table + "  where k <= array[1, 2]");
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+    }
+
+    @Test
+    public void testServerArrayElementProjection1() throws SQLException {
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "CREATE TABLE   " + table + "  (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 VARCHAR ARRAY)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES (1, ARRAY[1, 2], ARRAY['a', 'b'])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("SELECT arr1, arr1[1], arr2[1] FROM   " + table);
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+        assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2}), rs.getArray(1));
+        assertEquals(1, rs.getInt(2));
+        assertEquals("a", rs.getString(3));
+    }
+
+    @Test
+    public void testServerArrayElementProjection2() throws SQLException {
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "CREATE TABLE   " + table + "  (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 VARCHAR ARRAY, arr3 INTEGER ARRAY)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES (1, ARRAY[1, 2], ARRAY['a', 'b'], ARRAY[2, 3])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("SELECT arr1, arr1[1], arr2[1], arr3[1] from   " + table);
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+        assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2}), rs.getArray(1));
+        assertEquals(1, rs.getInt(2));
+        assertEquals("a", rs.getString(3));
+        assertEquals(2, rs.getInt(4));
+    }
+
+    @Test
+    public void testServerArrayElementProjection3() throws SQLException {
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "CREATE TABLE   " + table + "  (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 VARCHAR ARRAY, arr3 INTEGER ARRAY)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES (1, ARRAY[1, 2], ARRAY['a', 'b'], ARRAY[2, 3])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("SELECT arr1, arr1[1], arr2[1], arr3, arr3[1] from   " + table);
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+        assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2}), rs.getArray(1));
+        assertEquals(1, rs.getInt(2));
+        assertEquals("a", rs.getString(3));
+        assertEquals(conn.createArrayOf("INTEGER", new Integer[]{2, 3}), rs.getArray(4));
+        assertEquals(2, rs.getInt(5));
+    }
+
+    @Test
+    public void testServerArrayElementProjection4() throws SQLException {
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "CREATE TABLE   " + table + "  (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 VARCHAR ARRAY, arr3 INTEGER ARRAY)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES (1, ARRAY[1, 2], ARRAY['a', 'b'], ARRAY[2, 3])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("SELECT arr1, arr1[1], arr2[1], ARRAY_APPEND(arr3, 4), arr3[1] from   " + table);
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+        assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2}), rs.getArray(1));
+        assertEquals(1, rs.getInt(2));
+        assertEquals("a", rs.getString(3));
+        assertEquals(conn.createArrayOf("INTEGER", new Integer[]{2, 3, 4}), rs.getArray(4));
+        assertEquals(2, rs.getInt(5));
+    }
+
+    @Test
+    public void testServerArrayElementProjection5() throws SQLException {
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "CREATE TABLE   " + table + "  (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr3 INTEGER ARRAY)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES (1, ARRAY[1, 2], ARRAY[2, 3])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("SELECT arr1, arr1[1], ARRAY_APPEND(arr1, arr3[1]) from   " + table);
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+        assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2}), rs.getArray(1));
+        assertEquals(1, rs.getInt(2));
+        assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2, 2}), rs.getArray(3));
+    }
+
+    @Test
+    public void testServerArrayElementProjection6() throws SQLException {
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "CREATE TABLE   " + table + "  (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 INTEGER ARRAY)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES (1, ARRAY[1, 2], ARRAY[2, 3])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("SELECT arr1, arr1[1], ARRAY_APPEND(arr1, arr2[1]), p from   " + table);
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+        assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2}), rs.getArray(1));
+        assertEquals(1, rs.getInt(2));
+        assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2, 2}), rs.getArray(3));
+        assertEquals(1, rs.getInt(4));
+    }
+
+    @Test
+    public void testServerArrayElementProjection7() throws SQLException {
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "CREATE TABLE   " + table + "  (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 INTEGER ARRAY)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES (1, ARRAY[1, 2], ARRAY[2, 3])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("SELECT arr1, arr1[1], ARRAY_APPEND(ARRAY_APPEND(arr1, arr2[2]), arr2[1]), p from   " + table);
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+        assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2}), rs.getArray(1));
+        assertEquals(1, rs.getInt(2));
+        assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2, 3, 2}), rs.getArray(3));
+        assertEquals(1, rs.getInt(4));
+    }
+
+    @Test
+    public void testServerArrayElementProjection8() throws SQLException {
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "CREATE TABLE   " + table + "  (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 INTEGER ARRAY)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES (1, ARRAY[1, 2], ARRAY[2, 3])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("SELECT arr1, arr1[1], ARRAY_ELEM(ARRAY_APPEND(arr1, arr2[1]), 1), p, arr2[2] from   " + table);
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+        assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2}), rs.getArray(1));
+        assertEquals(1, rs.getInt(2));
+        assertEquals(1, rs.getInt(3));
+        assertEquals(1, rs.getInt(4));
+        assertEquals(3, rs.getInt(5));
+    }
+
+    @Test
+    public void testServerArrayElementProjection9() throws SQLException {
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "CREATE TABLE   " + table + "  (p INTEGER ARRAY PRIMARY KEY, arr1 INTEGER ARRAY, arr2 INTEGER ARRAY)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES (ARRAY[5, 6], ARRAY[1, 2], ARRAY[2, 3])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("SELECT arr1, arr1[1], ARRAY_ELEM(ARRAY_APPEND(arr1, arr2[1]), 1), p, arr2[2] from   " + table);
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+        assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2}), rs.getArray(1));
+        assertEquals(1, rs.getInt(2));
+        assertEquals(1, rs.getInt(3));
+        assertEquals(conn.createArrayOf("INTEGER", new Integer[]{5, 6}), rs.getArray(4));
+        assertEquals(3, rs.getInt(5));
+    }
+
+    @Test
+    public void testServerArrayElementProjection10() throws SQLException {
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "CREATE TABLE   " + table + "  (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 INTEGER ARRAY)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES (1, ARRAY[1, 2], ARRAY[2, 3])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("SELECT arr1[1] + 5, arr2[1] FROM   " + table);
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+        assertEquals(6, rs.getInt(1));
+        assertEquals(2, rs.getInt(2));
+    }
+
+    @Test
+    public void testServerArrayElementProjection11() throws SQLException {
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "CREATE TABLE   " + table + "  (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 INTEGER ARRAY)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES (1, ARRAY[1, 2], ARRAY[2, 3])");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES (2, ARRAY[1, 2], ARRAY[2, 3])");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES (3, ARRAY[1, 2], ARRAY[2, 3])");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES (4, ARRAY[1, 2], ARRAY[2, 3])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("SELECT CASE WHEN p = 1 THEN arr1[1] WHEN p = 2 THEN arr1[2] WHEN p = 3 THEN arr2[1] ELSE arr2[2] END FROM   " + table);
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+        assertEquals(1, rs.getInt(1));
+        assertTrue(rs.next());
+        assertEquals(2, rs.getInt(1));
+        assertTrue(rs.next());
+        assertEquals(2, rs.getInt(1));
+        assertTrue(rs.next());
+        assertEquals(3, rs.getInt(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testServerArrayElementProjection12() throws SQLException {
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "CREATE TABLE   " + table + "  (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 INTEGER ARRAY)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES (1, ARRAY[1, 2], ARRAY[2, 3])");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES (2, ARRAY[1, 2], ARRAY[2, 3])");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES (3, ARRAY[1, 2], ARRAY[2, 3])");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES (4, ARRAY[1, 2], ARRAY[2, 3])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("SELECT CASE WHEN p = 1 THEN arr1[1] WHEN p = 2 THEN arr1[2] WHEN p = 3 THEN arr2[1] ELSE arr2[2] END, ARRAY_APPEND(arr1, arr1[1]) FROM   " + table);
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+        assertEquals(1, rs.getInt(1));
+        assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2, 1}), rs.getArray(2));
+        assertTrue(rs.next());
+        assertEquals(2, rs.getInt(1));
+        assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2, 1}), rs.getArray(2));
+        assertTrue(rs.next());
+        assertEquals(2, rs.getInt(1));
+        assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2, 1}), rs.getArray(2));
+        assertTrue(rs.next());
+        assertEquals(3, rs.getInt(1));
+        assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2, 1}), rs.getArray(2));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testServerArrayElementProjection13() throws SQLException {
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "CREATE TABLE   " + table + "  (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 INTEGER ARRAY)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES (1, ARRAY[1, 2], ARRAY[2, 3])");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES (2, ARRAY[3, 2], ARRAY[2, 3])");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES (3, ARRAY[3, 5], ARRAY[2, 3])");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES (4, ARRAY[3, 5], ARRAY[6, 3])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("SELECT CASE WHEN arr1[1] = 1 THEN 1 WHEN arr1[2] = 2 THEN 2 WHEN arr2[1] = 2 THEN 2 ELSE arr2[2] END FROM   " + table);
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+        assertEquals(1, rs.getInt(1));
+        assertTrue(rs.next());
+        assertEquals(2, rs.getInt(1));
+        assertTrue(rs.next());
+        assertEquals(2, rs.getInt(1));
+        assertTrue(rs.next());
+        assertEquals(3, rs.getInt(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testServerArrayElementProjection14() throws SQLException {
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "CREATE TABLE   " + table + "  (p INTEGER ARRAY PRIMARY KEY, arr1 INTEGER ARRAY, arr2 INTEGER ARRAY)";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES (ARRAY[5, 6], ARRAY[1, 2], ARRAY[2, 3])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("SELECT ARRAY_ELEM(ARRAY_PREPEND(arr2[1], ARRAY_CAT(arr1, ARRAY[arr2[2],3])), 1), arr1[1], ARRAY_ELEM(ARRAY_APPEND(arr1, arr2[1]), 1), p, arr2[2] from   " + table);
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+        assertEquals(2, rs.getInt(1));
+        assertEquals(1, rs.getInt(2));
+        assertEquals(1, rs.getInt(3));
+        assertEquals(conn.createArrayOf("INTEGER", new Integer[]{5, 6}), rs.getArray(4));
+        assertEquals(3, rs.getInt(5));
+    }
+
+    @Test
+    public void testCharPrimaryKey() throws SQLException{
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "CREATE TABLE   " + table + " (testCharArray CHAR(3)[], CONSTRAINT test_pk PRIMARY KEY(testCharArray)) DEFAULT_COLUMN_FAMILY='T'";
+        conn.createStatement().execute(ddl);
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES (ARRAY['aaa', 'bbb', 'ccc'])");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        stmt = conn.prepareStatement("SELECT testCharArray from   " + table);
+        rs = stmt.executeQuery();
+        assertTrue(rs.next());
+        assertEquals(conn.createArrayOf("CHAR", new String[]{"aaa", "bbb", "ccc"}), rs.getArray(1));
+    }
+
+    @Test
+    public void testArrayIndexFunctionForImmutableTable() throws Exception {
+        String tableName = generateUniqueName();
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            String ddl = "CREATE IMMUTABLE TABLE " + tableName + " (region_name VARCHAR PRIMARY KEY, ZIP VARCHAR ARRAY[10])";
+            conn.createStatement().execute(ddl);
+        }
+        props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.createStatement().executeUpdate("UPSERT INTO " + tableName + " (region_name,zip) VALUES('SF Bay Area',ARRAY['94115','94030','94125'])");
+            conn.commit();
+        }
+        props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            String sql = "SELECT ZIP[2] FROM " + tableName;
+            try (ResultSet rs = conn.createStatement().executeQuery(sql)) {
+                assertTrue(rs.next());
+                assertEquals("94030", rs.getString(1));
+            }
+        }
+    }
+}


[23/25] phoenix git commit: PHOENIX-4251 Breakup ProductMetricsIT into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
PHOENIX-4251 Breakup ProductMetricsIT into several integration tests so as not to create too many tables in one test


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

Branch: refs/heads/master
Commit: dd3b7b6c04acfc53a1d5cdd5e866a59ae71557c3
Parents: eafb58b
Author: James Taylor <jt...@salesforce.com>
Authored: Thu Sep 28 18:23:19 2017 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Thu Sep 28 18:23:44 2017 -0700

----------------------------------------------------------------------
 .../org/apache/phoenix/end2end/DateTimeIT.java  | 277 ++++++++++++++++++-
 .../phoenix/end2end/ProductMetricsIT.java       | 170 +-----------
 2 files changed, 278 insertions(+), 169 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/dd3b7b6c/phoenix-core/src/it/java/org/apache/phoenix/end2end/DateTimeIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/DateTimeIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/DateTimeIT.java
index fb43538..e9a98e3 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/DateTimeIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/DateTimeIT.java
@@ -32,6 +32,7 @@ import static org.apache.phoenix.util.TestUtil.ROW6;
 import static org.apache.phoenix.util.TestUtil.ROW7;
 import static org.apache.phoenix.util.TestUtil.ROW8;
 import static org.apache.phoenix.util.TestUtil.ROW9;
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -39,18 +40,38 @@ import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import java.math.BigDecimal;
-import java.sql.*;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.sql.Types;
 import java.text.Format;
 import java.util.Calendar;
 import java.util.GregorianCalendar;
+import java.util.Properties;
 
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.schema.types.PDate;
+import org.apache.phoenix.util.ByteUtil;
 import org.apache.phoenix.util.DateUtil;
+import org.apache.phoenix.util.PropertiesUtil;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
 
 public class DateTimeIT extends ParallelStatsDisabledIT {
+    private static final String PRODUCT_METRICS_NAME = "PRODUCT_METRICS";
+    private static final Date SPLIT1 = toDate("1970-01-01 01:30:00");
+    private static final Date SPLIT2 = toDate("1970-01-01 02:00:00");
+    private static final String R1 = "R1";
+    private static final String R2 = "R2";
 
     protected Connection conn;
     protected Date date;
@@ -58,6 +79,107 @@ public class DateTimeIT extends ParallelStatsDisabledIT {
     protected final static String ROW10 = "00D123122312312";
     protected  String tableName;
 
+    private static void initDateTableValues(String tablename, Connection conn, String tenantId, Date startDate) throws Exception {
+        double dateIncrement = 2.0;
+        PreparedStatement stmt = conn.prepareStatement(
+                "upsert into " +tablename+
+                        "(" +
+                        "    ORGANIZATION_ID, " +
+                        "    \"DATE\", " +
+                        "    FEATURE, " +
+                        "    UNIQUE_USERS, " +
+                        "    TRANSACTIONS, " +
+                        "    CPU_UTILIZATION, " +
+                        "    DB_UTILIZATION, " +
+                        "    REGION, " +
+                        "    IO_TIME)" +
+                        "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
+        stmt.setString(1, tenantId);
+        stmt.setDate(2, startDate);
+        stmt.setString(3, "A");
+        stmt.setInt(4, 10);
+        stmt.setLong(5, 100L);
+        stmt.setBigDecimal(6, BigDecimal.valueOf(0.5));
+        stmt.setBigDecimal(7, BigDecimal.valueOf(0.2));
+        stmt.setString(8, R2);
+        stmt.setNull(9, Types.BIGINT);
+        stmt.execute();
+
+        startDate = new Date(startDate.getTime() + (long)(QueryConstants.MILLIS_IN_DAY * dateIncrement));
+        stmt.setString(1, tenantId);
+        stmt.setDate(2, startDate);
+        stmt.setString(3, "B");
+        stmt.setInt(4, 20);
+        stmt.setLong(5, 200);
+        stmt.setBigDecimal(6, BigDecimal.valueOf(1.0));
+        stmt.setBigDecimal(7, BigDecimal.valueOf(0.4));
+        stmt.setString(8, null);
+        stmt.setLong(9, 2000);
+        stmt.execute();
+
+        startDate = new Date(startDate.getTime() + (long)(QueryConstants.MILLIS_IN_DAY * dateIncrement));
+        stmt.setString(1, tenantId);
+        stmt.setDate(2, startDate);
+        stmt.setString(3, "C");
+        stmt.setInt(4, 30);
+        stmt.setLong(5, 300);
+        stmt.setBigDecimal(6, BigDecimal.valueOf(2.5));
+        stmt.setBigDecimal(7, BigDecimal.valueOf(0.6));
+        stmt.setString(8, R1);
+        stmt.setNull(9, Types.BIGINT);
+        stmt.execute();
+
+        startDate = new Date(startDate.getTime() + (long)(QueryConstants.MILLIS_IN_DAY * dateIncrement));
+        stmt.setString(1, tenantId);
+        stmt.setDate(2, startDate);
+        stmt.setString(3, "D");
+        stmt.setInt(4, 40);
+        stmt.setLong(5, 400);
+        stmt.setBigDecimal(6, BigDecimal.valueOf(3.0));
+        stmt.setBigDecimal(7, BigDecimal.valueOf(0.8));
+        stmt.setString(8, R1);
+        stmt.setLong(9, 4000);
+        stmt.execute();
+
+        startDate = new Date(startDate.getTime() + (long)(QueryConstants.MILLIS_IN_DAY * dateIncrement));
+        stmt.setString(1, tenantId);
+        stmt.setDate(2, startDate);
+        stmt.setString(3, "E");
+        stmt.setInt(4, 50);
+        stmt.setLong(5, 500);
+        stmt.setBigDecimal(6, BigDecimal.valueOf(3.5));
+        stmt.setBigDecimal(7, BigDecimal.valueOf(1.2));
+        stmt.setString(8, R2);
+        stmt.setLong(9, 5000);
+        stmt.execute();
+
+        startDate = new Date(startDate.getTime() + (long)(QueryConstants.MILLIS_IN_DAY * dateIncrement));
+        stmt.setString(1, tenantId);
+        stmt.setDate(2, startDate);
+        stmt.setString(3, "F");
+        stmt.setInt(4, 60);
+        stmt.setLong(5, 600);
+        stmt.setBigDecimal(6, BigDecimal.valueOf(4.0));
+        stmt.setBigDecimal(7, BigDecimal.valueOf(1.4));
+        stmt.setString(8, null);
+        stmt.setNull(9, Types.BIGINT);
+        stmt.execute();
+    }
+
+    private static void initDateTableValues(String tablename, String tenantId, byte[][] splits, Date startDate) throws Exception {
+        ensureTableCreated(getUrl(), tablename, PRODUCT_METRICS_NAME, splits, null, null);
+
+       Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+       Connection conn = DriverManager.getConnection(getUrl(), props);
+       try {
+           initDateTableValues(tablename, conn, tenantId, startDate);
+           conn.commit();
+       } finally {
+           conn.close();
+       }
+    }
+
+
     public DateTimeIT() throws Exception {
         super();
         date = new Date(System.currentTimeMillis());
@@ -1537,4 +1659,157 @@ public class DateTimeIT extends ParallelStatsDisabledIT {
         assertEquals(true, rs.getBoolean(1));
         assertFalse(rs.next());
     }
+    
+    private static byte[][] getSplits(String tenantId) {
+        return new byte[][] {
+                ByteUtil.concat(Bytes.toBytes(tenantId), PDate.INSTANCE.toBytes(SPLIT1)),
+                ByteUtil.concat(Bytes.toBytes(tenantId), PDate.INSTANCE.toBytes(SPLIT2)),
+        };
+    }
+
+    private static Date toDate(String dateString) {
+        return DateUtil.parseDate(dateString);
+    }
+
+    @Test
+    public void testDateSubtractionCompareNumber() throws Exception {
+        String tablename=generateUniqueName();
+        String tenantId = getOrganizationId();
+        String query = "SELECT feature FROM "+tablename+" WHERE organization_id = ? and ? - \"DATE\" > 3";
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            Date startDate = new Date(System.currentTimeMillis());
+            Date endDate = new Date(startDate.getTime() + 6 * QueryConstants.MILLIS_IN_DAY);
+            initDateTableValues(tablename, tenantId, getSplits(tenantId), startDate);
+            PreparedStatement statement = conn.prepareStatement(query);
+            statement.setString(1, tenantId);
+            statement.setDate(2, endDate);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            assertEquals("A", rs.getString(1));
+            assertTrue(rs.next());
+            assertEquals("B", rs.getString(1));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testDateSubtractionLongToDecimalCompareNumber() throws Exception {
+        String tablename=generateUniqueName();
+        String tenantId = getOrganizationId();
+        String query = "SELECT feature FROM "+tablename+" WHERE organization_id = ? and ? - \"DATE\" - 1.5 > 3";
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            Date startDate = new Date(System.currentTimeMillis());
+            Date endDate = new Date(startDate.getTime() + 9 * QueryConstants.MILLIS_IN_DAY);
+            initDateTableValues(tablename, tenantId, getSplits(tenantId), startDate);
+            PreparedStatement statement = conn.prepareStatement(query);
+            statement.setString(1, tenantId);
+            statement.setDate(2, endDate);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            assertEquals("A", rs.getString(1));
+            assertTrue(rs.next());
+            assertEquals("B", rs.getString(1));
+            assertTrue(rs.next());
+            assertEquals("C", rs.getString(1));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testDateSubtractionCompareDate() throws Exception {
+        String tablename=generateUniqueName();
+        String tenantId = getOrganizationId();
+        String query = "SELECT feature FROM "+tablename+" WHERE organization_id = ? and date - 1 >= ?";
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            Date startDate = new Date(System.currentTimeMillis());
+            Date endDate = new Date(startDate.getTime() + 9 * QueryConstants.MILLIS_IN_DAY);
+            initDateTableValues(tablename, tenantId, getSplits(tenantId), startDate);
+            PreparedStatement statement = conn.prepareStatement(query);
+            statement.setString(1, tenantId);
+            statement.setDate(2, endDate);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            assertEquals("F", rs.getString(1));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testDateAddCompareDate() throws Exception {
+        String tablename=generateUniqueName();
+        String tenantId = getOrganizationId();
+        String query = "SELECT feature FROM "+tablename+" WHERE organization_id = ? and date + 1 >= ?";
+        Connection conn = DriverManager.getConnection(url);
+        try {
+            Date startDate = new Date(System.currentTimeMillis());
+            Date endDate = new Date(startDate.getTime() + 8 * QueryConstants.MILLIS_IN_DAY);
+            initDateTableValues(tablename, tenantId, getSplits(tenantId), startDate);
+            PreparedStatement statement = conn.prepareStatement(query);
+            statement.setString(1, tenantId);
+            statement.setDate(2, endDate);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            assertEquals("E", rs.getString(1));
+            assertTrue(rs.next());
+            assertEquals("F", rs.getString(1));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testCurrentDate() throws Exception {
+        String tablename=generateUniqueName();
+        String tenantId = getOrganizationId();
+        String query = "SELECT feature FROM "+tablename+" WHERE organization_id = ? and \"DATE\" - current_date() > 8";
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            Date startDate = new Date(System.currentTimeMillis());
+            initDateTableValues(tablename, tenantId, getSplits(tenantId), startDate);
+            PreparedStatement statement = conn.prepareStatement(query);
+            statement.setString(1, tenantId);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            assertEquals("F", rs.getString(1));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testCurrentTime() throws Exception {
+        String tablename=generateUniqueName();
+        String tenantId = getOrganizationId();
+        String query = "SELECT feature FROM "+tablename+" WHERE organization_id = ? and \"DATE\" - current_time() > 8";
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            Date startDate = new Date(System.currentTimeMillis());
+            initDateTableValues(tablename, tenantId, getSplits(tenantId), startDate);
+            PreparedStatement statement = conn.prepareStatement(query);
+            statement.setString(1, tenantId);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            assertEquals("F", rs.getString(1));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/dd3b7b6c/phoenix-core/src/it/java/org/apache/phoenix/end2end/ProductMetricsIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ProductMetricsIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ProductMetricsIT.java
index 969b585..858a0fd 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ProductMetricsIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ProductMetricsIT.java
@@ -36,20 +36,15 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Properties;
 
-import org.apache.hadoop.hbase.TableNotFoundException;
 import org.apache.hadoop.hbase.client.HBaseAdmin;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.phoenix.jdbc.PhoenixConnection;
-import org.apache.phoenix.query.ConnectionQueryServices;
 import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.schema.types.PDate;
 import org.apache.phoenix.util.ByteUtil;
 import org.apache.phoenix.util.DateUtil;
-import org.apache.phoenix.util.PhoenixRuntime;
 import org.apache.phoenix.util.PropertiesUtil;
 import org.apache.phoenix.util.SchemaUtil;
-import org.apache.phoenix.util.TestUtil;
-import org.junit.After;
 import org.junit.Test;
 
 import com.google.common.collect.Lists;
@@ -113,7 +108,7 @@ public class ProductMetricsIT extends ParallelStatsDisabledIT {
         }
     }
 
-    protected static void initTableValues(String tablename, Connection conn, String tenantId) throws Exception {
+    private static void initTableValues(String tablename, Connection conn, String tenantId) throws Exception {
         PreparedStatement stmt = conn.prepareStatement(
                 "upsert into " + tablename +
                         " (" +
@@ -298,9 +293,7 @@ public class ProductMetricsIT extends ParallelStatsDisabledIT {
         stmt.execute();
     }
 
-
-
-
+    
     @Test
     public void testDateRangeAggregation() throws Exception {
         String tablename=generateUniqueName();
@@ -1715,147 +1708,6 @@ public class ProductMetricsIT extends ParallelStatsDisabledIT {
     }
 
     @Test
-    public void testDateSubtractionCompareNumber() throws Exception {
-        String tablename=generateUniqueName();
-        String tenantId = getOrganizationId();
-        String query = "SELECT feature FROM "+tablename+" WHERE organization_id = ? and ? - \"DATE\" > 3";
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            Date startDate = new Date(System.currentTimeMillis());
-            Date endDate = new Date(startDate.getTime() + 6 * QueryConstants.MILLIS_IN_DAY);
-            initDateTableValues(tablename, tenantId, getSplits(tenantId), startDate);
-            PreparedStatement statement = conn.prepareStatement(query);
-            statement.setString(1, tenantId);
-            statement.setDate(2, endDate);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            assertEquals("A", rs.getString(1));
-            assertTrue(rs.next());
-            assertEquals("B", rs.getString(1));
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testDateSubtractionLongToDecimalCompareNumber() throws Exception {
-        String tablename=generateUniqueName();
-        String tenantId = getOrganizationId();
-        String query = "SELECT feature FROM "+tablename+" WHERE organization_id = ? and ? - \"DATE\" - 1.5 > 3";
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            Date startDate = new Date(System.currentTimeMillis());
-            Date endDate = new Date(startDate.getTime() + 9 * QueryConstants.MILLIS_IN_DAY);
-            initDateTableValues(tablename, tenantId, getSplits(tenantId), startDate);
-            PreparedStatement statement = conn.prepareStatement(query);
-            statement.setString(1, tenantId);
-            statement.setDate(2, endDate);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            assertEquals("A", rs.getString(1));
-            assertTrue(rs.next());
-            assertEquals("B", rs.getString(1));
-            assertTrue(rs.next());
-            assertEquals("C", rs.getString(1));
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testDateSubtractionCompareDate() throws Exception {
-        String tablename=generateUniqueName();
-        String tenantId = getOrganizationId();
-        String query = "SELECT feature FROM "+tablename+" WHERE organization_id = ? and date - 1 >= ?";
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            Date startDate = new Date(System.currentTimeMillis());
-            Date endDate = new Date(startDate.getTime() + 9 * QueryConstants.MILLIS_IN_DAY);
-            initDateTableValues(tablename, tenantId, getSplits(tenantId), startDate);
-            PreparedStatement statement = conn.prepareStatement(query);
-            statement.setString(1, tenantId);
-            statement.setDate(2, endDate);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            assertEquals("F", rs.getString(1));
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testDateAddCompareDate() throws Exception {
-        String tablename=generateUniqueName();
-        String tenantId = getOrganizationId();
-        String query = "SELECT feature FROM "+tablename+" WHERE organization_id = ? and date + 1 >= ?";
-        Connection conn = DriverManager.getConnection(url);
-        try {
-            Date startDate = new Date(System.currentTimeMillis());
-            Date endDate = new Date(startDate.getTime() + 8 * QueryConstants.MILLIS_IN_DAY);
-            initDateTableValues(tablename, tenantId, getSplits(tenantId), startDate);
-            PreparedStatement statement = conn.prepareStatement(query);
-            statement.setString(1, tenantId);
-            statement.setDate(2, endDate);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            assertEquals("E", rs.getString(1));
-            assertTrue(rs.next());
-            assertEquals("F", rs.getString(1));
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testCurrentDate() throws Exception {
-        String tablename=generateUniqueName();
-        String tenantId = getOrganizationId();
-        String query = "SELECT feature FROM "+tablename+" WHERE organization_id = ? and \"DATE\" - current_date() > 8";
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            Date startDate = new Date(System.currentTimeMillis());
-            initDateTableValues(tablename, tenantId, getSplits(tenantId), startDate);
-            PreparedStatement statement = conn.prepareStatement(query);
-            statement.setString(1, tenantId);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            assertEquals("F", rs.getString(1));
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testCurrentTime() throws Exception {
-        String tablename=generateUniqueName();
-        String tenantId = getOrganizationId();
-        String query = "SELECT feature FROM "+tablename+" WHERE organization_id = ? and \"DATE\" - current_time() > 8";
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            Date startDate = new Date(System.currentTimeMillis());
-            initDateTableValues(tablename, tenantId, getSplits(tenantId), startDate);
-            PreparedStatement statement = conn.prepareStatement(query);
-            statement.setString(1, tenantId);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            assertEquals("F", rs.getString(1));
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
     public void testTruncateNotTraversableToFormScanKey() throws Exception {
         String tablename=generateUniqueName();
         String tenantId = getOrganizationId();
@@ -1879,27 +1731,9 @@ public class ProductMetricsIT extends ParallelStatsDisabledIT {
         }
     }
 
-    private static void destroyTable() throws Exception {
-        String tablename=generateUniqueName();
-        // Physically delete HBase table so that splits occur as expected for each test
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        ConnectionQueryServices services = DriverManager.getConnection(getUrl(), props).unwrap(PhoenixConnection.class).getQueryServices();
-        HBaseAdmin admin = services.getAdmin();
-        try {
-            try {
-                admin.disableTable(tablename);
-                admin.deleteTable(tablename);
-            } catch (TableNotFoundException e) {
-            }
-        } finally {
-            admin.close();
-        }
-    }
-
     @Test
     public void testSaltedOrderBy() throws Exception {
         String tablename=generateUniqueName();
-        destroyTable();
         String ddl = "create table " + tablename +
                 "   (organization_id char(15) not null," +
                 "    \"DATE\" date not null," +


[18/25] phoenix git commit: PHOENIX-4244 Breakup ArrayIT into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
PHOENIX-4244 Breakup ArrayIT into several integration tests so as not to create too many tables in one test


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

Branch: refs/heads/master
Commit: 527f786ac4b4883677fa77d0e31c71ea7d518872
Parents: 06f58d5
Author: James Taylor <jt...@salesforce.com>
Authored: Thu Sep 28 13:18:15 2017 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Thu Sep 28 17:51:57 2017 -0700

----------------------------------------------------------------------
 .../org/apache/phoenix/end2end/Array1IT.java    | 1004 +++++++
 .../org/apache/phoenix/end2end/Array2IT.java    |  815 ++++++
 .../org/apache/phoenix/end2end/Array3IT.java    |  770 ++++++
 .../org/apache/phoenix/end2end/ArrayIT.java     | 2543 +-----------------
 4 files changed, 2617 insertions(+), 2515 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/527f786a/phoenix-core/src/it/java/org/apache/phoenix/end2end/Array1IT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/Array1IT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/Array1IT.java
new file mode 100644
index 0000000..20f4422
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/Array1IT.java
@@ -0,0 +1,1004 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end;
+
+import static org.apache.phoenix.util.TestUtil.B_VALUE;
+import static org.apache.phoenix.util.TestUtil.ROW1;
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Array;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.apache.phoenix.query.BaseTest;
+import org.apache.phoenix.schema.types.PhoenixArray;
+import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.TestUtil;
+import org.junit.Test;
+
+import com.google.common.primitives.Floats;
+
+public class Array1IT extends ArrayIT {
+    private void assertArrayGetString(ResultSet rs, int arrayIndex, Array expectedArray, String expectedString)
+            throws SQLException {
+        assertEquals(expectedArray, rs.getArray(arrayIndex));
+        assertEquals("[" + expectedString + "]", rs.getString(arrayIndex));
+    }
+    
+    private static String createTableWithAllArrayTypes(String url, byte[][] bs, Object object) throws SQLException {
+        String tableName = generateUniqueName();
+        String ddlStmt = "create table "
+                + tableName
+                + "   (organization_id char(15) not null, \n"
+                + "    entity_id char(15) not null,\n"
+                + "    boolean_array boolean array,\n"
+                + "    byte_array tinyint array,\n"
+                + "    double_array double array[],\n"
+                + "    float_array float array,\n"
+                + "    int_array integer array,\n"
+                + "    long_array bigint[5],\n"
+                + "    short_array smallint array,\n"
+                + "    string_array varchar(100) array[3],\n"
+                + "    CONSTRAINT pk PRIMARY KEY (organization_id, entity_id)\n"
+                + ")";
+        BaseTest.createTestTable(url, ddlStmt, bs, null);
+        return tableName;
+    }
+    
+    private static String createSimpleTableWithArray(String url, byte[][] bs, Object object) throws SQLException {
+        String tableName = generateUniqueName();
+        String ddlStmt = "create table "
+                + tableName
+                + "   (organization_id char(15) not null, \n"
+                + "    entity_id char(15) not null,\n"
+                + "    x_double double,\n"
+                + "    a_double_array double array[],\n"
+                + "    a_char_array char(5) array[],\n"
+                + "    CONSTRAINT pk PRIMARY KEY (organization_id, entity_id)\n"
+                + ")";
+        BaseTest.createTestTable(url, ddlStmt, bs, null);
+        return tableName;
+    }
+
+    private static void initSimpleArrayTable(String tableName, String tenantId, Date date, boolean useNull) throws Exception {
+       Properties props = new Properties();
+
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            // Insert all rows at ts
+            PreparedStatement stmt = conn.prepareStatement(
+                    "upsert into " + tableName +
+                    "(" +
+                    "    ORGANIZATION_ID, " +
+                    "    ENTITY_ID, " +
+                    "    x_double, " +
+                    "    a_double_array, a_char_array)" +
+                    "VALUES (?, ?, ?, ?, ?)");
+            stmt.setString(1, tenantId);
+            stmt.setString(2, ROW1);
+            stmt.setDouble(3, 1.2d);
+            // Need to support primitive
+            Double[] doubleArr =  new Double[2];
+            doubleArr[0] = 64.87;
+            doubleArr[1] = 89.96;
+            //doubleArr[2] = 9.9;
+            Array array = conn.createArrayOf("DOUBLE", doubleArr);
+            stmt.setArray(4, array);
+
+            // create character array
+            String[] charArr =  new String[2];
+            charArr[0] = "a";
+            charArr[1] = "b";
+            array = conn.createArrayOf("CHAR", charArr);
+            stmt.setArray(5, array);
+            stmt.execute();
+
+            conn.commit();
+        } finally {
+            conn.close();
+        }
+   }
+
+    @Test
+    public void testScanByArrayValue() throws Exception {
+        String tenantId = getOrganizationId();
+        String tableName = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        initTablesWithArrays(tableName, tenantId, null, false, getUrl());
+        String query = "SELECT a_double_array, /* comment ok? */ b_string, a_float FROM " + tableName + " WHERE ?=organization_id and ?=a_float";
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        TestUtil.analyzeTable(conn, tableName);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            statement.setString(1, tenantId);
+            statement.setFloat(2, 0.01f);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            // Need to support primitive
+            Double[] doubleArr = new Double[4];
+            doubleArr[0] = 25.343;
+            doubleArr[1] = 36.763;
+            doubleArr[2] = 37.56;
+            doubleArr[3] = 386.63;
+            Array array = conn.createArrayOf("DOUBLE",
+                    doubleArr);
+            PhoenixArray resultArray = (PhoenixArray) rs.getArray(1);
+            assertEquals(resultArray, array);
+            assertEquals("[25.343, 36.763, 37.56, 386.63]", rs.getString(1));
+            assertEquals(rs.getString("B_string"), B_VALUE);
+            assertTrue(Floats.compare(rs.getFloat(3), 0.01f) == 0);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testScanWithArrayInWhereClause() throws Exception {
+        String tenantId = getOrganizationId();
+        String tableName = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        initTablesWithArrays(tableName, tenantId, null, false, getUrl());
+        String query = "SELECT a_double_array, /* comment ok? */ b_string, a_float FROM " + tableName + " WHERE ?=organization_id and ?=a_byte_array";
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        TestUtil.analyzeTable(conn, tableName);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            statement.setString(1, tenantId);
+            // Need to support primitive
+            Byte[] byteArr = new Byte[2];
+            byteArr[0] = 25;
+            byteArr[1] = 36;
+            Array array = conn.createArrayOf("TINYINT", byteArr);
+            statement.setArray(2, array);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            // Need to support primitive
+            Double[] doubleArr = new Double[4];
+            doubleArr[0] = 25.343;
+            doubleArr[1] = 36.763;
+            doubleArr[2] = 37.56;
+            doubleArr[3] = 386.63;
+            array = conn.createArrayOf("DOUBLE", doubleArr);
+            Array resultArray = rs.getArray(1);
+            assertEquals(resultArray, array);
+            assertEquals("[25.343, 36.763, 37.56, 386.63]", rs.getString(1));
+            assertEquals(rs.getString("B_string"), B_VALUE);
+            assertTrue(Floats.compare(rs.getFloat(3), 0.01f) == 0);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testScanWithNonFixedWidthArrayInWhereClause() throws Exception {
+        String tenantId = getOrganizationId();
+        String tableName = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        initTablesWithArrays(tableName, tenantId, null, false, getUrl());
+        String query = "SELECT a_double_array, /* comment ok? */ b_string, a_float FROM  " + tableName + "  WHERE ?=organization_id and ?=a_string_array";
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            statement.setString(1, tenantId);
+            // Need to support primitive
+            String[] strArr = new String[4];
+            strArr[0] = "ABC";
+            strArr[1] = "CEDF";
+            strArr[2] = "XYZWER";
+            strArr[3] = "AB";
+            Array array = conn.createArrayOf("VARCHAR", strArr);
+            statement.setArray(2, array);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            // Need to support primitive
+            Double[] doubleArr = new Double[4];
+            doubleArr[0] = 25.343;
+            doubleArr[1] = 36.763;
+            doubleArr[2] = 37.56;
+            doubleArr[3] = 386.63;
+            array = conn.createArrayOf("DOUBLE", doubleArr);
+            Array resultArray = rs.getArray(1);
+            assertEquals(resultArray, array);
+            assertEquals("[25.343, 36.763, 37.56, 386.63]", rs.getString(1));
+            assertEquals(rs.getString("B_string"), B_VALUE);
+            assertTrue(Floats.compare(rs.getFloat(3), 0.01f) == 0);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testScanWithNonFixedWidthArrayInSelectClause() throws Exception {
+        String tenantId = getOrganizationId();
+        String tableName = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        initTablesWithArrays(tableName, tenantId, null, false, getUrl());
+        String query = "SELECT a_string_array FROM  " + tableName;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            String[] strArr = new String[4];
+            strArr[0] = "ABC";
+            strArr[1] = "CEDF";
+            strArr[2] = "XYZWER";
+            strArr[3] = "AB";
+            Array array = conn.createArrayOf("VARCHAR", strArr);
+            PhoenixArray resultArray = (PhoenixArray) rs.getArray(1);
+            assertEquals(resultArray, array);
+            assertEquals("['ABC', 'CEDF', 'XYZWER', 'AB']", rs.getString(1));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSelectSpecificIndexOfAnArrayAsArrayFunction()
+            throws Exception {
+        String tenantId = getOrganizationId();
+        String tableName = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        initTablesWithArrays(tableName, tenantId, null, false, getUrl());
+        String query = "SELECT ARRAY_ELEM(a_double_array,2) FROM  " + tableName;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            // Need to support primitive
+            Double[] doubleArr = new Double[1];
+            doubleArr[0] = 36.763;
+            conn.createArrayOf("DOUBLE", doubleArr);
+            Double result =  rs.getDouble(1);
+            assertEquals(doubleArr[0], result);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSelectSpecificIndexOfAnArray() throws Exception {
+        String tenantId = getOrganizationId();
+        String tableName = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        initTablesWithArrays(tableName, tenantId, null, false, getUrl());
+        String query = "SELECT a_double_array[3] FROM  " + tableName;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            // Need to support primitive
+            Double[] doubleArr = new Double[1];
+            doubleArr[0] = 37.56;
+            Double result =  rs.getDouble(1);
+            assertEquals(doubleArr[0], result);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testCaseWithArray() throws Exception {
+        String tenantId = getOrganizationId();
+        String tableName = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        initTablesWithArrays(tableName, tenantId, null, false, getUrl());
+        String query = "SELECT CASE WHEN A_INTEGER = 1 THEN a_double_array ELSE null END [3] FROM  " + tableName;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            // Need to support primitive
+            Double[] doubleArr = new Double[1];
+            doubleArr[0] = 37.56;
+            Double result =  rs.getDouble(1);
+            assertEquals(doubleArr[0], result);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testUpsertValuesWithArray() throws Exception {
+        String tenantId = getOrganizationId();
+        String tableName = createTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
+        String query = "upsert into  " + tableName + " (ORGANIZATION_ID,ENTITY_ID,a_double_array) values('" + tenantId
+                + "','00A123122312312',ARRAY[2.0,345.8])";
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+                                                                                 // at
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            int executeUpdate = statement.executeUpdate();
+            assertEquals(1, executeUpdate);
+            conn.commit();
+            statement.close();
+            conn.close();
+            // create another connection
+            props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+            conn = DriverManager.getConnection(getUrl(), props);
+            query = "SELECT ARRAY_ELEM(a_double_array,2) FROM  " + tableName;
+            statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            // Need to support primitive
+            Double[] doubleArr = new Double[1];
+            doubleArr[0] = 345.8d;
+            conn.createArrayOf("DOUBLE", doubleArr);
+            Double result = rs.getDouble(1);
+            assertEquals(doubleArr[0], result);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testUpsertSelectWithSelectAsSubQuery1() throws Exception {
+        String tenantId = getOrganizationId();
+        String table1 = createTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
+        Connection conn = null;
+        try {
+            String table2 = createSimpleTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
+            initSimpleArrayTable(table2, tenantId, null, false);
+            Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+            conn = DriverManager.getConnection(getUrl(), props);
+            String query = "upsert into " + table1 + " (ORGANIZATION_ID,ENTITY_ID,a_double_array) "
+                    + "SELECT organization_id, entity_id, a_double_array  FROM " + table2
+                    + " WHERE a_double_array[2] = 89.96";
+            PreparedStatement statement = conn.prepareStatement(query);
+            int executeUpdate = statement.executeUpdate();
+            assertEquals(1, executeUpdate);
+            conn.commit();
+            statement.close();
+            conn.close();
+            // create another connection
+            props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+            conn = DriverManager.getConnection(getUrl(), props);
+            query = "SELECT ARRAY_ELEM(a_double_array,2) FROM " + table1;
+            statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            // Need to support primitive
+            Double[] doubleArr = new Double[1];
+            doubleArr[0] = 89.96d;
+            Double result = rs.getDouble(1);
+            assertEquals(result, doubleArr[0]);
+            assertFalse(rs.next());
+
+        } finally {
+            if (conn != null) {
+                conn.close();
+            }
+        }
+    }
+
+    @Test
+    public void testArraySelectWithORCondition() throws Exception {
+        String tenantId = getOrganizationId();
+        Connection conn = null;
+        try {
+            String table = createSimpleTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
+            initSimpleArrayTable(table, tenantId, null, false);
+            Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+            conn = DriverManager.getConnection(getUrl(), props);
+            String query = "SELECT a_double_array[1]  FROM " + table
+                    + " WHERE a_double_array[2] = 89.96 or a_char_array[0] = 'a'";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            Double[] doubleArr = new Double[1];
+            doubleArr[0] = 64.87d;
+            Double result = rs.getDouble(1);
+            assertEquals(result, doubleArr[0]);
+            assertFalse(rs.next());
+
+        } finally {
+            if (conn != null) {
+                conn.close();
+            }
+        }
+    }
+
+    @Test
+    public void testArraySelectWithANY() throws Exception {
+        String tenantId = getOrganizationId();
+        Connection conn = null;
+        try {
+            String table = createSimpleTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
+            initSimpleArrayTable(table, tenantId, null, false);
+            Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+            conn = DriverManager.getConnection(getUrl(), props);
+            String query = "SELECT a_double_array[1]  FROM " + table
+                    + " WHERE CAST(89.96 AS DOUBLE) = ANY(a_double_array)";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            Double[] doubleArr = new Double[1];
+            doubleArr[0] = 64.87d;
+            Double result = rs.getDouble(1);
+            assertEquals(result, doubleArr[0]);
+            assertFalse(rs.next());
+        } finally {
+            if (conn != null) {
+                conn.close();
+            }
+        }
+    }
+
+    @Test
+    public void testArraySelectWithALL() throws Exception {
+        String tenantId = getOrganizationId();
+        Connection conn = null;
+        try {
+            String table = createSimpleTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
+            initSimpleArrayTable(table, tenantId, null, false);
+            Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+            conn = DriverManager.getConnection(getUrl(), props);
+            String query = "SELECT a_double_array[1]  FROM " + table
+                    + " WHERE CAST(64.87 as DOUBLE) = ALL(a_double_array)";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertFalse(rs.next());
+        } finally {
+            if (conn != null) {
+                conn.close();
+            }
+        }
+    }
+
+    @Test
+    public void testArraySelectWithANYCombinedWithOR() throws Exception {
+        String tenantId = getOrganizationId();
+        Connection conn = null;
+        try {
+            String table = createSimpleTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
+            initSimpleArrayTable(table, tenantId, null, false);
+            Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+            conn = DriverManager.getConnection(getUrl(), props);
+            String query = "SELECT a_double_array[1]  FROM " + table
+                    + " WHERE  a_char_array[0] = 'f' or CAST(89.96 AS DOUBLE) > ANY(a_double_array)";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            Double[] doubleArr = new Double[1];
+            doubleArr[0] = 64.87d;
+            Double result = rs.getDouble(1);
+            assertEquals(result, doubleArr[0]);
+            assertFalse(rs.next());
+        } finally {
+            if (conn != null) {
+                conn.close();
+            }
+        }
+    }
+
+    @Test
+    public void testArraySelectWithALLCombinedWithOR() throws Exception {
+
+        String tenantId = getOrganizationId();
+        Connection conn = null;
+        try {
+            String table = createSimpleTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
+            initSimpleArrayTable(table, tenantId, null, false);
+            Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+            conn = DriverManager.getConnection(getUrl(), props);
+            String query = "SELECT a_double_array[1], a_double_array[2]  FROM " + table
+                    + " WHERE  a_char_array[0] = 'f' or CAST(100.0 AS DOUBLE) > ALL(a_double_array)";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            Double[] doubleArr = new Double[1];
+            doubleArr[0] = 64.87d;
+            Double result = rs.getDouble(1);
+            assertEquals(result, doubleArr[0]);
+            doubleArr = new Double[1];
+            doubleArr[0] = 89.96d;
+            result = rs.getDouble(2);
+            assertEquals(result, doubleArr[0]);
+        } finally {
+            if (conn != null) {
+                conn.close();
+            }
+        }
+    }
+
+    @Test
+    public void testArraySelectWithANYUsingVarLengthArray() throws Exception {
+        Connection conn = null;
+        try {
+    
+            String tenantId = getOrganizationId();
+            String table = createTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
+            initTablesWithArrays(table, tenantId, null, false, getUrl());
+            Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+            conn = DriverManager.getConnection(getUrl(), props);
+            String query = "SELECT a_string_array[1]  FROM " + table
+                    + " WHERE 'XYZWER' = ANY(a_string_array)";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            String[] strArr = new String[1];
+            strArr[0] = "ABC";
+            String result = rs.getString(1);
+            assertEquals(result, strArr[0]);
+            assertFalse(rs.next());
+            query = "SELECT a_string_array[1]  FROM " + table + " WHERE 'AB' = ANY(a_string_array)";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue(rs.next());
+            result = rs.getString(1);
+            assertEquals(result, strArr[0]);
+            assertFalse(rs.next());
+        } finally {
+            if (conn != null) {
+                conn.close();
+            }
+        }
+    }
+
+    @Test
+    public void testSelectWithArrayWithColumnRef() throws Exception {
+
+        String tenantId = getOrganizationId();
+        String table = createTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
+        initTablesWithArrays(table, tenantId, null, false, getUrl());
+        String query = "SELECT a_integer,ARRAY[1,2,a_integer] FROM " + table + " where organization_id =  '"
+                + tenantId + "'";
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            int val = rs.getInt(1);
+            assertEquals(val, 1);
+            Array array = rs.getArray(2);
+            // Need to support primitive
+            Integer[] intArr = new Integer[3];
+            intArr[0] = 1;
+            intArr[1] = 2;
+            intArr[2] = 1;
+            Array resultArr = conn.createArrayOf("INTEGER", intArr);
+            assertEquals(resultArr, array);
+            assertEquals("[1, 2, 1]", rs.getString(2));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSelectWithArrayWithColumnRefWithVarLengthArray() throws Exception {
+
+        String tenantId = getOrganizationId();
+        String table = createTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
+        initTablesWithArrays(table, tenantId, null, false, getUrl());
+        String query = "SELECT b_string,ARRAY['abc','defgh',b_string] FROM " + table + " where organization_id =  '"
+                + tenantId + "'";
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            String val = rs.getString(1);
+            assertEquals(val, "b");
+            Array array = rs.getArray(2);
+            // Need to support primitive
+            String[] strArr = new String[3];
+            strArr[0] = "abc";
+            strArr[1] = "defgh";
+            strArr[2] = "b";
+            Array resultArr = conn.createArrayOf("VARCHAR", strArr);
+            assertEquals(resultArr, array);
+            // since array is var length, last string element is messed up
+            String expectedPrefix = "['abc', 'defgh', 'b";
+            assertTrue("Expected to start with " + expectedPrefix,
+                rs.getString(2).startsWith(expectedPrefix));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSelectWithArrayWithColumnRefWithVarLengthArrayWithNullValue() throws Exception {
+
+        String tenantId = getOrganizationId();
+        String table = createTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
+        initTablesWithArrays(table, tenantId, null, false, getUrl());
+        String query = "SELECT b_string,ARRAY['abc',null,'bcd',null,null,b_string] FROM " + table + " where organization_id =  '"
+                + tenantId + "'";
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            String val = rs.getString(1);
+            assertEquals(val, "b");
+            Array array = rs.getArray(2);
+            // Need to support primitive
+            String[] strArr = new String[6];
+            strArr[0] = "abc";
+            strArr[1] = null;
+            strArr[2] = "bcd";
+            strArr[3] = null;
+            strArr[4] = null;
+            strArr[5] = "b";
+            Array resultArr = conn.createArrayOf("VARCHAR", strArr);
+            assertEquals(resultArr, array);
+            String expectedPrefix = "['abc', null, 'bcd', null, null, 'b";
+            assertTrue("Expected to start with " + expectedPrefix,
+                rs.getString(2).startsWith(expectedPrefix));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testUpsertSelectWithColumnRef() throws Exception {
+
+        String tenantId = getOrganizationId();
+        String table1 = createTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
+        Connection conn = null;
+        try {
+            String table2 = createSimpleTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
+            initSimpleArrayTable(table2, tenantId, null, false);
+            Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+            conn = DriverManager.getConnection(getUrl(), props);
+            String query = "upsert into " + table1 + " (ORGANIZATION_ID,ENTITY_ID, a_unsigned_double, a_double_array) "
+                    + "SELECT organization_id, entity_id, x_double, ARRAY[23.4, 22.1, x_double]  FROM " + table2
+                    + " WHERE a_double_array[2] = 89.96";
+            PreparedStatement statement = conn.prepareStatement(query);
+            int executeUpdate = statement.executeUpdate();
+            assertEquals(1, executeUpdate);
+            conn.commit();
+            statement.close();
+            conn.close();
+            // create another connection
+            props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+            conn = DriverManager.getConnection(getUrl(), props);
+            query = "SELECT ARRAY_ELEM(a_double_array,2) FROM " + table1;
+            statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            // Need to support primitive
+            Double[] doubleArr = new Double[1];
+            doubleArr[0] = 22.1d;
+            Double result = rs.getDouble(1);
+            assertEquals(result, doubleArr[0]);
+            assertFalse(rs.next());
+
+        } finally {
+            if (conn != null) {
+                conn.close();
+            }
+        }
+    }
+
+    @Test
+    public void testCharArraySpecificIndex() throws Exception {
+
+        String tenantId = getOrganizationId();
+        String table = createSimpleTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
+        initSimpleArrayTable(table, tenantId, null, false);
+        String query = "SELECT a_char_array[2] FROM " + table;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            String[] charArr = new String[1];
+            charArr[0] = "b";
+            String result = rs.getString(1);
+            assertEquals(charArr[0], result);
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testArrayWithDescOrder() throws Exception {
+        Connection conn;
+        PreparedStatement stmt;
+        ResultSet rs;
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        conn.createStatement().execute(
+                "CREATE TABLE  " + table + "  ( k VARCHAR, a_string_array VARCHAR(100) ARRAY[4], b_string_array VARCHAR(100) ARRAY[4] \n"
+                        + " CONSTRAINT pk PRIMARY KEY (k, b_string_array DESC)) \n");
+        conn.close();
+        conn = DriverManager.getConnection(getUrl(), props);
+        stmt = conn.prepareStatement("UPSERT INTO " + table + " VALUES(?,?,?)");
+        stmt.setString(1, "a");
+        String[] s = new String[] { "abc", "def", "ghi", "jkll", null, null, "xxx" };
+        Array array = conn.createArrayOf("VARCHAR", s);
+        stmt.setArray(2, array);
+        s = new String[] { "abc", "def", "ghi", "jkll", null, null, null, "xxx" };
+        array = conn.createArrayOf("VARCHAR", s);
+        stmt.setArray(3, array);
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        rs = conn.createStatement().executeQuery("SELECT b_string_array FROM  " + table);
+        assertTrue(rs.next());
+        PhoenixArray strArr = (PhoenixArray)rs.getArray(1);
+        assertEquals(array, strArr);
+        assertEquals("['abc', 'def', 'ghi', 'jkll', null, null, null, 'xxx']", rs.getString(1));
+        conn.close();
+    }
+
+    @Test
+    public void testArrayWithFloatArray() throws Exception {
+        Connection conn;
+        PreparedStatement stmt;
+        ResultSet rs;
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        conn.createStatement().execute("CREATE TABLE  " + table + "  ( k VARCHAR PRIMARY KEY, a Float ARRAY[])");
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES('a',ARRAY[2.0,3.0])");
+        int res = stmt.executeUpdate();
+        assertEquals(1, res);
+        conn.commit();
+        conn.close();
+        conn = DriverManager.getConnection(getUrl(), props);
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_ELEM(a,2) FROM  " + table);
+        assertTrue(rs.next());
+        Float f = new Float(3.0);
+        assertEquals(f, (Float)rs.getFloat(1));
+        conn.close();
+    }
+
+    @Test
+    public void testArrayWithVarCharArray() throws Exception {
+        Connection conn;
+        PreparedStatement stmt;
+        ResultSet rs;
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        conn.createStatement().execute("CREATE TABLE  " + table + "  ( k VARCHAR PRIMARY KEY, a VARCHAR ARRAY[])");
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES('a',ARRAY['a',null])");
+        int res = stmt.executeUpdate();
+        assertEquals(1, res);
+        conn.commit();
+        conn.close();
+        conn = DriverManager.getConnection(getUrl(), props);
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_ELEM(a,2) FROM  " + table);
+        assertTrue(rs.next());
+        assertEquals(null, rs.getString(1));
+        conn.close();
+    }
+
+    @Test
+    public void testArraySelectSingleArrayElemWithCast() throws Exception {
+        Connection conn;
+        PreparedStatement stmt;
+        ResultSet rs;
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        conn.createStatement().execute("CREATE TABLE  " + table + "  ( k VARCHAR PRIMARY KEY, a bigint ARRAY[])");
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES(?,?)");
+        stmt.setString(1, "a");
+        Long[] s = new Long[] {1l, 2l};
+        Array array = conn.createArrayOf("BIGINT", s);
+        stmt.setArray(2, array);
+        stmt.execute();
+        conn.commit();
+        conn.close();
+        conn = DriverManager.getConnection(getUrl(), props);
+        rs = conn.createStatement().executeQuery("SELECT k, CAST(a[2] AS DOUBLE) FROM  " + table);
+        assertTrue(rs.next());
+        assertEquals("a",rs.getString(1));
+        Double d = new Double(2.0);
+        assertEquals(d, (Double)rs.getDouble(2));
+        conn.close();
+    }
+
+    @Test
+    public void testArraySelectGetString() throws Exception {
+        Connection conn;
+        PreparedStatement stmt;
+
+        String tenantId = getOrganizationId();
+
+        // create the table
+        String tableName = createTableWithAllArrayTypes(getUrl(), getDefaultSplits(tenantId), null);
+
+        // populate the table with data
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        conn = DriverManager.getConnection(getUrl(), props);
+        stmt =
+                conn.prepareStatement("UPSERT INTO "
+                        + tableName
+                        + "(ORGANIZATION_ID, ENTITY_ID, BOOLEAN_ARRAY, BYTE_ARRAY, DOUBLE_ARRAY, FLOAT_ARRAY, INT_ARRAY, LONG_ARRAY, SHORT_ARRAY, STRING_ARRAY)\n"
+                        + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+        stmt.setString(1, tenantId);
+        stmt.setString(2, ROW1);
+        // boolean array
+        Array boolArray = conn.createArrayOf("BOOLEAN", new Boolean[] { true, false });
+        int boolIndex = 3;
+        stmt.setArray(boolIndex, boolArray);
+        // byte array
+        Array byteArray = conn.createArrayOf("TINYINT", new Byte[] { 11, 22 });
+        int byteIndex = 4;
+        stmt.setArray(byteIndex, byteArray);
+        // double array
+        Array doubleArray = conn.createArrayOf("DOUBLE", new Double[] { 67.78, 78.89 });
+        int doubleIndex = 5;
+        stmt.setArray(doubleIndex, doubleArray);
+        // float array
+        Array floatArray = conn.createArrayOf("FLOAT", new Float[] { 12.23f, 45.56f });
+        int floatIndex = 6;
+        stmt.setArray(floatIndex, floatArray);
+        // int array
+        Array intArray = conn.createArrayOf("INTEGER", new Integer[] { 5555, 6666 });
+        int intIndex = 7;
+        stmt.setArray(intIndex, intArray);
+        // long array
+        Array longArray = conn.createArrayOf("BIGINT", new Long[] { 7777777L, 8888888L });
+        int longIndex = 8;
+        stmt.setArray(longIndex, longArray);
+        // short array
+        Array shortArray = conn.createArrayOf("SMALLINT", new Short[] { 333, 444 });
+        int shortIndex = 9;
+        stmt.setArray(shortIndex, shortArray);
+        // create character array
+        Array stringArray = conn.createArrayOf("VARCHAR", new String[] { "a", "b" });
+        int stringIndex = 10;
+        stmt.setArray(stringIndex, stringArray);
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        stmt =
+                conn.prepareStatement("SELECT organization_id, entity_id, boolean_array, byte_array, double_array, float_array, int_array, long_array, short_array, string_array FROM "
+                        + tableName);
+        TestUtil.analyzeTable(conn, tableName);
+
+        ResultSet rs = stmt.executeQuery();
+        assertTrue(rs.next());
+
+        assertEquals(tenantId, rs.getString(1));
+        assertEquals(ROW1, rs.getString(2));
+        
+        assertArrayGetString(rs, boolIndex, boolArray, "true, false");
+        assertArrayGetString(rs, byteIndex, byteArray, "11, 22");
+        assertArrayGetString(rs, doubleIndex, doubleArray, "67.78, 78.89");
+        assertArrayGetString(rs, floatIndex, floatArray, "12.23, 45.56");
+        assertArrayGetString(rs, intIndex, intArray, "5555, 6666");
+        assertArrayGetString(rs, longIndex, longArray, "7777777, 8888888");
+        assertArrayGetString(rs, shortIndex, shortArray, "333, 444");
+        assertArrayGetString(rs, stringIndex, stringArray, "'a', 'b'");
+        conn.close();
+    }
+
+    @Test
+    public void testArrayWithCast() throws Exception {
+        Connection conn;
+        PreparedStatement stmt;
+        ResultSet rs;
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName(); 
+        conn.createStatement().execute("CREATE TABLE " + table + " ( k VARCHAR PRIMARY KEY, a bigint ARRAY[])");
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES(?,?)");
+        stmt.setString(1, "a");
+        Long[] s = new Long[] { 1l, 2l };
+        Array array = conn.createArrayOf("BIGINT", s);
+        stmt.setArray(2, array);
+        stmt.execute();
+        conn.commit();
+        conn.close();
+        conn = DriverManager.getConnection(getUrl(), props);
+        rs = conn.createStatement().executeQuery("SELECT CAST(a AS DOUBLE []) FROM  " + table);
+        assertTrue(rs.next());
+        Double[] d = new Double[] { 1.0, 2.0 };
+        array = conn.createArrayOf("DOUBLE", d);
+        PhoenixArray arr = (PhoenixArray)rs.getArray(1);
+        assertEquals(array, arr);
+        assertEquals("[1.0, 2.0]", rs.getString(1));
+        conn.close();
+    }
+
+    @Test
+    public void testArrayWithCastForVarLengthArr() throws Exception {
+        Connection conn;
+        PreparedStatement stmt;
+        ResultSet rs;
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        conn.createStatement().execute("CREATE TABLE  " + table + "  ( k VARCHAR PRIMARY KEY, a VARCHAR(5) ARRAY)");
+        conn.close();
+        conn = DriverManager.getConnection(getUrl(), props);
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES(?,?)");
+        stmt.setString(1, "a");
+        String[] s = new String[] { "1", "2" };
+        PhoenixArray array = (PhoenixArray)conn.createArrayOf("VARCHAR", s);
+        stmt.setArray(2, array);
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        rs = conn.createStatement().executeQuery("SELECT CAST(a AS CHAR ARRAY) FROM  " + table);
+        assertTrue(rs.next());
+        PhoenixArray arr = (PhoenixArray)rs.getArray(1);
+        String[] array2 = (String[])array.getArray();
+        String[] array3 = (String[])arr.getArray();
+        assertEquals(array2[0], array3[0]);
+        assertEquals(array2[1], array3[1]);
+        assertEquals("['1', '2']", rs.getString(1));
+        conn.close();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/527f786a/phoenix-core/src/it/java/org/apache/phoenix/end2end/Array2IT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/Array2IT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/Array2IT.java
new file mode 100644
index 0000000..52bfb86
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/Array2IT.java
@@ -0,0 +1,815 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end;
+
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.sql.Array;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.Properties;
+
+import org.apache.phoenix.schema.types.PhoenixArray;
+import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.SchemaUtil;
+import org.apache.phoenix.util.StringUtil;
+import org.junit.Test;
+
+public class Array2IT extends ArrayIT {
+    @Test
+    public void testFixedWidthCharArray() throws Exception {
+        Connection conn;
+        PreparedStatement stmt;
+        ResultSet rs;
+
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        conn.createStatement().execute("CREATE TABLE  " + table + "  ( k VARCHAR PRIMARY KEY, a CHAR(5) ARRAY)");
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        rs = conn.getMetaData().getColumns(null, null, table, "A");
+        assertTrue(rs.next());
+        assertEquals(5, rs.getInt("COLUMN_SIZE"));
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES(?,?)");
+        stmt.setString(1, "a");
+        String[] s = new String[] {"1","2"};
+        Array array = conn.createArrayOf("CHAR", s);
+        stmt.setArray(2, array);
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        rs = conn.createStatement().executeQuery("SELECT k, a[2] FROM  " + table);
+        assertTrue(rs.next());
+        assertEquals("a",rs.getString(1));
+        assertEquals("2",rs.getString(2));
+        conn.close();
+    }
+
+    @Test
+    public void testSelectArrayUsingUpsertLikeSyntax() throws Exception {
+        String tenantId = getOrganizationId();
+        String table = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        initTablesWithArrays(table, tenantId, null, false, getUrl());
+        String query = "SELECT a_double_array FROM  " + table + "  WHERE a_double_array = CAST(ARRAY [ 25.343, 36.763, 37.56,386.63] AS DOUBLE ARRAY)";
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            Double[] doubleArr =  new Double[4];
+            doubleArr[0] = 25.343;
+            doubleArr[1] = 36.763;
+            doubleArr[2] = 37.56;
+            doubleArr[3] = 386.63;
+            Array array = conn.createArrayOf("DOUBLE", doubleArr);
+            PhoenixArray resultArray = (PhoenixArray) rs.getArray(1);
+            assertEquals(resultArray, array);
+            assertEquals("[25.343, 36.763, 37.56, 386.63]", rs.getString(1));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testArrayIndexUsedInWhereClause() throws Exception {
+        String tenantId = getOrganizationId();
+        String table = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        initTablesWithArrays(table, tenantId, null, false, getUrl());
+        int a_index = 0;
+        String query = "SELECT a_double_array[2] FROM  " + table + "  where a_double_array["+a_index+"2]<?";
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            Double[] doubleArr = new Double[1];
+            doubleArr[0] = 40.0;
+            conn.createArrayOf("DOUBLE", doubleArr);
+            statement.setDouble(1, 40.0d);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            // Need to support primitive
+            doubleArr = new Double[1];
+            doubleArr[0] = 36.763;
+            Double result =  rs.getDouble(1);
+            assertEquals(doubleArr[0], result);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testArrayIndexUsedInGroupByClause() throws Exception {
+        String tenantId = getOrganizationId();
+        String table = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        initTablesWithArrays(table, tenantId, null, false, getUrl());
+        String query = "SELECT a_double_array[2] FROM " + table + "  GROUP BY a_double_array[2]";
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            Double[] doubleArr = new Double[1];
+            doubleArr[0] = 40.0;
+            conn.createArrayOf("DOUBLE", doubleArr);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            doubleArr = new Double[1];
+            doubleArr[0] = 36.763;
+            Double result =  rs.getDouble(1);
+            assertEquals(doubleArr[0], result);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testVariableLengthArrayWithNullValue() throws Exception {
+        String tenantId = getOrganizationId();
+        String table = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        initTablesWithArrays(table, tenantId, null, true, getUrl());
+        String query = "SELECT a_string_array[2] FROM  " + table;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            String[] strArr = new String[1];
+            strArr[0] = "XYZWER";
+            String result = rs.getString(1);
+            assertNull(result);
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSelectSpecificIndexOfAVariableArrayAlongWithAnotherColumn1() throws Exception {
+        String tenantId = getOrganizationId();
+        String table = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        initTablesWithArrays(table, tenantId, null, false, getUrl());
+        String query = "SELECT a_string_array[3],A_INTEGER FROM  " + table;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            String[] strArr = new String[1];
+            strArr[0] = "XYZWER";
+            String result = rs.getString(1);
+            assertEquals(strArr[0], result);
+            int a_integer = rs.getInt(2);
+            assertEquals(1, a_integer);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSelectSpecificIndexOfAVariableArrayAlongWithAnotherColumn2() throws Exception {
+
+        String tenantId = getOrganizationId();
+        String table = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        initTablesWithArrays(table, tenantId, null, false, getUrl());
+        String query = "SELECT A_INTEGER, a_string_array[3] FROM  " + table;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            String[] strArr = new String[1];
+            strArr[0] = "XYZWER";
+            int a_integer = rs.getInt(1);
+            assertEquals(1, a_integer);
+            String result = rs.getString(2);
+            assertEquals(strArr[0], result);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSelectMultipleArrayColumns() throws Exception {
+
+        String tenantId = getOrganizationId();
+        String table = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        initTablesWithArrays(table, tenantId, null, false, getUrl());
+        String query = "SELECT  a_string_array[3], a_double_array[2] FROM  " + table;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            String[] strArr = new String[1];
+            strArr[0] = "XYZWER";
+            Double[] doubleArr = new Double[1];
+            doubleArr[0] = 36.763d;
+            Double a_double = rs.getDouble(2);
+            assertEquals(doubleArr[0], a_double);
+            String result = rs.getString(1);
+            assertEquals(strArr[0], result);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSelectSameArrayColumnMultipleTimesWithDifferentIndices() throws Exception {
+
+        String tenantId = getOrganizationId();
+        String table = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        initTablesWithArrays(table, tenantId, null, false, getUrl());
+        String query = "SELECT a_string_array[1], a_string_array[2], " +
+                "a_string_array[3], a_double_array[1], a_double_array[2], a_double_array[3] " +
+                "FROM  " + table;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            assertEquals("ABC", rs.getString(1));
+            assertEquals("CEDF", rs.getString(2));
+            assertEquals("XYZWER", rs.getString(3));
+            assertEquals(25.343, rs.getDouble(4), 0.0);
+            assertEquals(36.763, rs.getDouble(5), 0.0);
+            assertEquals(37.56, rs.getDouble(6), 0.0);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSelectSameArrayColumnMultipleTimesWithSameIndices() throws Exception {
+
+        String tenantId = getOrganizationId();
+        String table = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        initTablesWithArrays(table, tenantId, null, false, getUrl());
+        String query = "SELECT a_string_array[3], a_string_array[3] FROM " + table;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            String[] strArr = new String[1];
+            strArr[0] = "XYZWER";
+            String result = rs.getString(1);
+            assertEquals(strArr[0], result);
+            result = rs.getString(2);
+            assertEquals(strArr[0], result);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSelectSpecificIndexOfAVariableArray() throws Exception {
+        String tenantId = getOrganizationId();
+        String table = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        initTablesWithArrays(table, tenantId, null, false, getUrl());
+        String query = "SELECT a_string_array[3] FROM  " + table;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            String[] strArr = new String[1];
+            strArr[0] = "XYZWER";
+            String result = rs.getString(1);
+            assertEquals(strArr[0], result);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testWithOutOfRangeIndex() throws Exception {
+        String tenantId = getOrganizationId();
+        String table = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        initTablesWithArrays(table, tenantId, null, false, getUrl());
+        String query = "SELECT a_double_array[100] FROM  " + table;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            PhoenixArray resultArray = (PhoenixArray) rs.getArray(1);
+            assertNull(resultArray);
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testArrayLengthFunctionForVariableLength() throws Exception {
+        String tenantId = getOrganizationId();
+        String table = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        initTablesWithArrays(table, tenantId, null, false, getUrl());
+        String query = "SELECT ARRAY_LENGTH(a_string_array) FROM " + table;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            int result = rs.getInt(1);
+            assertEquals(result, 4);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+
+    @Test
+    public void testArrayLengthFunctionForFixedLength() throws Exception {
+        String tenantId = getOrganizationId();
+        String table = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        initTablesWithArrays(table, tenantId, null, false, getUrl());
+        String query = "SELECT ARRAY_LENGTH(a_double_array) FROM " + table;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            int result = rs.getInt(1);
+            assertEquals(result, 4);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testArraySizeRoundtrip() throws Exception {
+
+        String tenantId = getOrganizationId();
+        String table = createTableWithArray(getUrl(),
+                getDefaultSplits(tenantId), null);
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            ResultSet rs = conn.getMetaData().getColumns(null, null, StringUtil.escapeLike(table), StringUtil.escapeLike(SchemaUtil.normalizeIdentifier("x_long_array")));
+            assertTrue(rs.next());
+            assertEquals(5, rs.getInt("ARRAY_SIZE"));
+            assertFalse(rs.next());
+
+            rs = conn.getMetaData().getColumns(null, null, StringUtil.escapeLike(table), StringUtil.escapeLike(SchemaUtil.normalizeIdentifier("a_string_array")));
+            assertTrue(rs.next());
+            assertEquals(3, rs.getInt("ARRAY_SIZE"));
+            assertFalse(rs.next());
+
+            rs = conn.getMetaData().getColumns(null, null, StringUtil.escapeLike(table), StringUtil.escapeLike(SchemaUtil.normalizeIdentifier("a_double_array")));
+            assertTrue(rs.next());
+            assertEquals(0, rs.getInt("ARRAY_SIZE"));
+            assertTrue(rs.wasNull());
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testVarLengthArrComparisonInWhereClauseWithSameArrays() throws Exception {
+        Connection conn;
+        PreparedStatement stmt;
+        ResultSet rs;
+
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        conn.createStatement()
+                .execute(
+                        "CREATE TABLE  " + table + "  ( k VARCHAR PRIMARY KEY, a_string_array VARCHAR(100) ARRAY[4], b_string_array VARCHAR(100) ARRAY[4])");
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES(?,?,?)");
+        stmt.setString(1, "a");
+        String[] s = new String[] {"abc","def", "ghi","jkl"};
+        Array array = conn.createArrayOf("VARCHAR", s);
+        stmt.setArray(2, array);
+        s = new String[] {"abc","def", "ghi","jkl"};
+        array = conn.createArrayOf("VARCHAR", s);
+        stmt.setArray(3, array);
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        rs = conn.createStatement().executeQuery("SELECT k, a_string_array[2] FROM  " + table + "  where a_string_array=b_string_array");
+        assertTrue(rs.next());
+        assertEquals("a",rs.getString(1));
+        assertEquals("def",rs.getString(2));
+        conn.close();
+    }
+
+    @Test
+    public void testVarLengthArrComparisonInWhereClauseWithDiffSizeArrays() throws Exception {
+        Connection conn;
+        PreparedStatement stmt;
+        ResultSet rs;
+
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        conn.createStatement()
+                .execute(
+                        "CREATE TABLE  " + table + "  ( k VARCHAR PRIMARY KEY, a_string_array VARCHAR(100) ARRAY[4], b_string_array VARCHAR(100) ARRAY[4])");
+        conn.close();
+        conn = DriverManager.getConnection(getUrl(), props);
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES(?,?,?)");
+        stmt.setString(1, "a");
+        String[] s = new String[] { "abc", "def", "ghi", "jkll" };
+        Array array = conn.createArrayOf("VARCHAR", s);
+        stmt.setArray(2, array);
+        s = new String[] { "abc", "def", "ghi", "jklm" };
+        array = conn.createArrayOf("VARCHAR", s);
+        stmt.setArray(3, array);
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        rs = conn.createStatement().executeQuery(
+                "SELECT k, a_string_array[2] FROM  " + table + "  where a_string_array<b_string_array");
+        assertTrue(rs.next());
+        assertEquals("a", rs.getString(1));
+        assertEquals("def", rs.getString(2));
+        conn.close();
+    }
+
+    @Test
+    public void testVarLengthArrComparisonWithNulls() throws Exception {
+        Connection conn;
+        PreparedStatement stmt;
+        ResultSet rs;
+
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        conn.createStatement()
+                .execute(
+                        "CREATE TABLE  " + table + "  ( k VARCHAR PRIMARY KEY, a_string_array VARCHAR(100) ARRAY[4], b_string_array VARCHAR(100) ARRAY[4])");
+        conn.close();
+        conn = DriverManager.getConnection(getUrl(), props);
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES(?,?,?)");
+        stmt.setString(1, "a");
+        String[] s = new String[] { "abc", "def", "ghi", "jkll", null, null, "xxx" };
+        Array array = conn.createArrayOf("VARCHAR", s);
+        stmt.setArray(2, array);
+        s = new String[] { "abc", "def", "ghi", "jkll", null, null, null, "xxx" };
+        array = conn.createArrayOf("VARCHAR", s);
+        stmt.setArray(3, array);
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        rs = conn.createStatement().executeQuery(
+                "SELECT k, a_string_array[2] FROM  " + table + "  where a_string_array>b_string_array");
+        assertTrue(rs.next());
+        assertEquals("a", rs.getString(1));
+        assertEquals("def", rs.getString(2));
+        conn.close();
+    }
+
+    @Test
+    public void testUpsertValuesWithNull() throws Exception {
+
+        String tenantId = getOrganizationId();
+        String table = createTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
+        String query = "upsert into  " + table + " (ORGANIZATION_ID,ENTITY_ID,a_double_array) values('" + tenantId
+                + "','00A123122312312',null)";
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+                                                                                 // at
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            int executeUpdate = statement.executeUpdate();
+            assertEquals(1, executeUpdate);
+            conn.commit();
+            statement.close();
+            conn.close();
+            // create another connection
+            props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+            conn = DriverManager.getConnection(getUrl(), props);
+            query = "SELECT ARRAY_ELEM(a_double_array,2) FROM  " + table;
+            statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            // Need to support primitive
+            Double[] doubleArr = new Double[1];
+            doubleArr[0] = 0.0d;
+            conn.createArrayOf("DOUBLE", doubleArr);
+            Double result = rs.getDouble(1);
+            assertEquals(doubleArr[0], result);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testUpsertValuesWithNullUsingPreparedStmt() throws Exception {
+
+        String tenantId = getOrganizationId();
+        String table = createTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
+        String query = "upsert into  " + table + " (ORGANIZATION_ID,ENTITY_ID,a_string_array) values(?, ?, ?)";
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+                                                                                 // at
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            statement.setString(1, tenantId);
+            statement.setString(2, "00A123122312312");
+            statement.setNull(3, Types.ARRAY);
+            int executeUpdate = statement.executeUpdate();
+            assertEquals(1, executeUpdate);
+            conn.commit();
+            statement.close();
+            conn.close();
+            // create another connection
+            props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+            conn = DriverManager.getConnection(getUrl(), props);
+            query = "SELECT ARRAY_ELEM(a_string_array,1) FROM  " + table;
+            statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            String[] strArr = new String[1];
+            strArr[0] = null;
+            conn.createArrayOf("VARCHAR", strArr);
+            String result = rs.getString(1);
+            assertEquals(strArr[0], result);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testPKWithArray() throws Exception {
+        Connection conn;
+        PreparedStatement stmt;
+        ResultSet rs;
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        conn.createStatement()
+                .execute(
+                        "CREATE TABLE  " + table + "  ( k VARCHAR, a_string_array VARCHAR(100) ARRAY[4], b_string_array VARCHAR(100) ARRAY[4] \n"
+                        + " CONSTRAINT pk PRIMARY KEY (k, b_string_array)) \n");
+        conn.close();
+        conn = DriverManager.getConnection(getUrl(), props);
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES(?,?,?)");
+        stmt.setString(1, "a");
+        String[] s = new String[] { "abc", "def", "ghi", "jkll", null, null, "xxx" };
+        Array array = conn.createArrayOf("VARCHAR", s);
+        stmt.setArray(2, array);
+        s = new String[] { "abc", "def", "ghi", "jkll", null, null, null, "xxx" };
+        array = conn.createArrayOf("VARCHAR", s);
+        stmt.setArray(3, array);
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        conn = DriverManager.getConnection(getUrl(), props);
+        rs = conn.createStatement().executeQuery(
+                "SELECT k, a_string_array[2] FROM  " + table + "  where b_string_array[8]='xxx'");
+        assertTrue(rs.next());
+        assertEquals("a", rs.getString(1));
+        assertEquals("def", rs.getString(2));
+        conn.close();
+    }
+
+    @Test
+    public void testPKWithArrayNotInEnd() throws Exception {
+        Connection conn;
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        try {
+            conn.createStatement().execute(
+                    "CREATE TABLE  " + table + "  ( a_string_array VARCHAR(100) ARRAY[4], b_string_array VARCHAR(100) ARRAY[4], k VARCHAR  \n"
+                            + " CONSTRAINT pk PRIMARY KEY (b_string_array, k))");
+            conn.close();
+            fail();
+        } catch (SQLException e) {
+        } finally {
+            if (conn != null) {
+                conn.close();
+            }
+        }
+
+    }
+
+    @Test
+    public void testArrayRefToLiteral() throws Exception {
+        Connection conn;
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            PreparedStatement stmt = conn.prepareStatement("select ?[2] from \"SYSTEM\".\"catalog\" limit 1");
+            Array array = conn.createArrayOf("CHAR", new String[] {"a","b","c"});
+            stmt.setArray(1, array);
+            ResultSet rs = stmt.executeQuery();
+            assertTrue(rs.next());
+            assertEquals("b", rs.getString(1));
+            assertFalse(rs.next());
+        } catch (SQLException e) {
+        } finally {
+            if (conn != null) {
+                conn.close();
+            }
+        }
+
+    }
+    
+    @Test
+    public void testArrayConstructorWithMultipleRows1() throws Exception {
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "CREATE TABLE  " + table + "  (region_name VARCHAR PRIMARY KEY, a INTEGER, b INTEGER)";
+        conn.createStatement().execute(ddl);
+        conn.commit();
+        conn.close();
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('a', 6,3)");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('b', 2,4)");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('c', 6,3)");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT COUNT(DISTINCT ARRAY[a,b]) from  " + table);
+        assertTrue(rs.next());
+        assertEquals(2, rs.getInt(1));
+    }
+
+    @Test
+    public void testArrayConstructorWithMultipleRows2() throws Exception {
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "CREATE TABLE  " + table + "  (region_name VARCHAR PRIMARY KEY, a INTEGER, b INTEGER)";
+        conn.createStatement().execute(ddl);
+        conn.commit();
+        conn.close();
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('a', 6,3)");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('b', 2,4)");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('c', 6,3)");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY[a,b] from  " + table + " ");
+        assertTrue(rs.next());
+        Array arr = conn.createArrayOf("INTEGER", new Object[]{6, 3});
+        assertEquals(arr, rs.getArray(1));
+        rs.next();
+        arr = conn.createArrayOf("INTEGER", new Object[]{2, 4});
+        assertEquals(arr, rs.getArray(1));
+        rs.next();
+        arr = conn.createArrayOf("INTEGER", new Object[]{6, 3});
+        assertEquals(arr, rs.getArray(1));
+        rs.next();
+    }
+
+    @Test
+    public void testArrayConstructorWithMultipleRows3() throws Exception {
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "CREATE TABLE  " + table + "  (region_name VARCHAR PRIMARY KEY, a VARCHAR, b VARCHAR)";
+        conn.createStatement().execute(ddl);
+        conn.commit();
+        conn.close();
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('a', 'foo', 'abc')");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('b', 'abc', 'dfg')");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('c', 'foo', 'abc')");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY[a,b] from  " + table + " ");
+        assertTrue(rs.next());
+        Array arr = conn.createArrayOf("VARCHAR", new Object[]{"foo", "abc"});
+        assertEquals(arr, rs.getArray(1));
+        rs.next();
+        arr = conn.createArrayOf("VARCHAR", new Object[]{"abc", "dfg"});
+        assertEquals(arr, rs.getArray(1));
+        rs.next();
+        arr = conn.createArrayOf("VARCHAR", new Object[]{"foo", "abc"});
+        assertEquals(arr, rs.getArray(1));
+        rs.next();
+    }
+
+    @Test
+    public void testArrayConstructorWithMultipleRows4() throws Exception {
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String table = generateUniqueName();
+        String ddl = "CREATE TABLE  " + table + "  (region_name VARCHAR PRIMARY KEY, a VARCHAR, b VARCHAR)";
+        conn.createStatement().execute(ddl);
+        conn.commit();
+        conn.close();
+        conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('a', 'foo', 'abc')");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('b', 'abc', 'dfg')");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('c', 'foo', 'abc')");
+        stmt.execute();
+        conn.commit();
+        conn.close();
+        conn = DriverManager.getConnection(getUrl(), props);
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT COUNT(DISTINCT ARRAY[a,b]) from  " + table);
+        assertTrue(rs.next());
+        assertEquals(2, rs.getInt(1));
+    }
+
+}


[25/25] phoenix git commit: PHOENIX-4250 Breakup AlterTableIT into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
PHOENIX-4250 Breakup AlterTableIT into several integration tests so as not to create too many tables in one test


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

Branch: refs/heads/master
Commit: eafb58bec2e35e0a999e40b2831b504c05295328
Parents: 3593ec8
Author: James Taylor <jt...@salesforce.com>
Authored: Thu Sep 28 17:46:27 2017 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Thu Sep 28 18:23:44 2017 -0700

----------------------------------------------------------------------
 .../apache/phoenix/end2end/AlterTableIT.java    | 1231 ------------------
 .../apache/phoenix/end2end/SetPropertyIT.java   | 1022 +++++++++++++++
 .../end2end/SetPropertyOnEncodedTableIT.java    |   34 +
 .../end2end/SetPropertyOnNonEncodedTableIT.java |   34 +
 .../index/IndexWithTableSchemaChangeIT.java     |  318 +++++
 5 files changed, 1408 insertions(+), 1231 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/eafb58be/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
index d3a9c45..5265b09 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
@@ -17,7 +17,6 @@
  */
 package org.apache.phoenix.end2end;
 
-import static org.apache.hadoop.hbase.HColumnDescriptor.DEFAULT_REPLICATION_SCOPE;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.COLUMN_FAMILY;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.COLUMN_NAME;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.COLUMN_QUALIFIER;
@@ -37,7 +36,6 @@ import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import java.math.BigDecimal;
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.PreparedStatement;
@@ -50,12 +48,7 @@ import java.util.Properties;
 
 import org.apache.hadoop.hbase.HColumnDescriptor;
 import org.apache.hadoop.hbase.HTableDescriptor;
-import org.apache.hadoop.hbase.KeepDeletedCells;
 import org.apache.hadoop.hbase.client.HBaseAdmin;
-import org.apache.hadoop.hbase.client.HTable;
-import org.apache.hadoop.hbase.client.Result;
-import org.apache.hadoop.hbase.client.ResultScanner;
-import org.apache.hadoop.hbase.client.Scan;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.phoenix.exception.SQLExceptionCode;
 import org.apache.phoenix.jdbc.PhoenixConnection;
@@ -93,11 +86,8 @@ public class AlterTableIT extends ParallelStatsDisabledIT {
     private String schemaName;
     private String dataTableName;
     private String indexTableName;
-    private String localIndexTableName;
-    private String viewName;
     private String dataTableFullName;
     private String indexTableFullName;
-    private String localIndexTableFullName;
     private String tableDDLOptions;
     private final boolean columnEncoded;
     
@@ -116,11 +106,8 @@ public class AlterTableIT extends ParallelStatsDisabledIT {
         schemaName = "";
         dataTableName = generateUniqueName();
         indexTableName = "I_" + generateUniqueName();
-        localIndexTableName = "LI_" + generateUniqueName();
         dataTableFullName = SchemaUtil.getTableName(schemaName, dataTableName);
         indexTableFullName = SchemaUtil.getTableName(schemaName, indexTableName);
-        localIndexTableFullName = SchemaUtil.getTableName(schemaName, localIndexTableName);
-        viewName = generateUniqueName();
     }
 
     @Test
@@ -257,22 +244,6 @@ public class AlterTableIT extends ParallelStatsDisabledIT {
         }
     }
 
-    private void assertIndexExists(Connection conn, boolean exists) throws SQLException {
-        ResultSet rs = conn.getMetaData().getIndexInfo(null, schemaName, dataTableName, false, false);
-        assertEquals(exists, rs.next());
-    }
-
-    
-    @Test
-    public void testDropIndexedColumnImmutableIndex() throws Exception {
-        helpTestDropIndexedColumn(true);
-    }
-    
-    @Test
-    public void testDropIndexedColumnMutableIndex() throws Exception {
-        helpTestDropIndexedColumn(false);
-    }
-    
     private String generateDDLOptions(String options) {
         StringBuilder sb = new StringBuilder();
         if (!options.isEmpty()) {
@@ -286,270 +257,6 @@ public class AlterTableIT extends ParallelStatsDisabledIT {
         return sb.toString();
     }
     
-    private void helpTestDropIndexedColumn(boolean immutable) throws Exception {
-        String query;
-        ResultSet rs;
-        PreparedStatement stmt;
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        conn.setAutoCommit(false);
-
-        // make sure that the tables are empty, but reachable
-        conn.createStatement().execute(
-          "CREATE TABLE " + dataTableFullName
-              + " (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR) "
-              + generateDDLOptions(immutable ? "IMMUTABLE_ROWS = true" : "")
-              + (!columnEncoded ? ",IMMUTABLE_STORAGE_SCHEME=" + PTable.ImmutableStorageScheme.ONE_CELL_PER_COLUMN : ""));
-        query = "SELECT * FROM " + dataTableFullName;
-        rs = conn.createStatement().executeQuery(query);
-        assertFalse(rs.next());
-
-        conn.createStatement().execute(
-          "CREATE INDEX " + indexTableName + " ON " + dataTableFullName + " (v1, v2)");
-        conn.createStatement().execute(
-            "CREATE LOCAL INDEX " + localIndexTableName + " ON " + dataTableFullName + " (v1, v2)");
-
-        query = "SELECT * FROM " + indexTableFullName;
-        rs = conn.createStatement().executeQuery(query);
-        assertFalse(rs.next());
-
-        // load some data into the table
-        stmt = conn.prepareStatement("UPSERT INTO " + dataTableFullName + " VALUES(?,?,?)");
-        stmt.setString(1, "a");
-        stmt.setString(2, "x");
-        stmt.setString(3, "1");
-        stmt.execute();
-        conn.commit();
-
-        assertIndexExists(conn,true);
-        conn.createStatement().execute("ALTER TABLE " + dataTableFullName + " DROP COLUMN v1");
-        assertIndexExists(conn,false);
-
-        query = "SELECT * FROM " + dataTableFullName;
-        rs = conn.createStatement().executeQuery(query);
-        assertTrue(rs.next());
-        assertEquals("a",rs.getString(1));
-        assertEquals("1",rs.getString(2));
-        assertFalse(rs.next());
-
-        // load some data into the table
-        stmt = conn.prepareStatement("UPSERT INTO " + dataTableFullName + " VALUES(?,?)");
-        stmt.setString(1, "a");
-        stmt.setString(2, "2");
-        stmt.execute();
-        conn.commit();
-
-        query = "SELECT * FROM " + dataTableFullName;
-        rs = conn.createStatement().executeQuery(query);
-        assertTrue(rs.next());
-        assertEquals("a",rs.getString(1));
-        assertEquals("2",rs.getString(2));
-        assertFalse(rs.next());
-    }
-
-    @Test
-    public void testDropCoveredColumn() throws Exception {
-        ResultSet rs;
-        PreparedStatement stmt;
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        conn.setAutoCommit(false);
-
-        // make sure that the tables are empty, but reachable
-        conn.createStatement().execute(
-          "CREATE TABLE " + dataTableFullName
-              + " (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR, v3 VARCHAR) " +  tableDDLOptions);
-        String dataTableQuery = "SELECT * FROM " + dataTableFullName;
-        rs = conn.createStatement().executeQuery(dataTableQuery);
-        assertFalse(rs.next());
-
-        conn.createStatement().execute(
-          "CREATE INDEX " + indexTableName + " ON " + dataTableFullName + " (v1) include (v2, v3)");
-        conn.createStatement().execute(
-            "CREATE LOCAL INDEX " + localIndexTableName + " ON " + dataTableFullName + " (v1) include (v2, v3)");
-        rs = conn.createStatement().executeQuery(dataTableQuery);
-        assertFalse(rs.next());
-        String indexTableQuery = "SELECT * FROM " + indexTableName;
-        rs = conn.createStatement().executeQuery(indexTableQuery);
-        assertFalse(rs.next());
-        String localIndexTableQuery = "SELECT * FROM " + localIndexTableFullName;
-        rs = conn.createStatement().executeQuery(localIndexTableQuery);
-        assertFalse(rs.next());
-
-        // load some data into the table
-        stmt = conn.prepareStatement("UPSERT INTO " + dataTableFullName + " VALUES(?,?,?,?)");
-        stmt.setString(1, "a");
-        stmt.setString(2, "x");
-        stmt.setString(3, "1");
-        stmt.setString(4, "j");
-        stmt.execute();
-        conn.commit();
-
-        assertIndexExists(conn,true);
-        conn.createStatement().execute("ALTER TABLE " + dataTableFullName + " DROP COLUMN v2");
-        assertIndexExists(conn,true);
-
-        // verify data table rows
-        Scan scan = new Scan();
-        HTable table = (HTable) conn.unwrap(PhoenixConnection.class).getQueryServices().getTable(Bytes.toBytes(dataTableFullName));
-        ResultScanner results = table.getScanner(scan);
-        for (Result res : results) {
-        	assertNull("Column value was not deleted",res.getValue(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, Bytes.toBytes("V2")));
-        }
-        results.close();
-        rs = conn.createStatement().executeQuery(dataTableQuery);
-        assertTrue(rs.next());
-        assertEquals("a",rs.getString(1));
-        assertEquals("x",rs.getString(2));
-        assertEquals("j",rs.getString(3));
-        assertFalse(rs.next());
-        
-        // verify index table rows
-        scan = new Scan();
-        table = (HTable) conn.unwrap(PhoenixConnection.class).getQueryServices().getTable(Bytes.toBytes(indexTableFullName));
-        results = table.getScanner(scan);
-        for (Result res : results) {
-        	assertNull("Column value was not deleted",res.getValue(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, Bytes.toBytes("0:V2")));
-        }
-        results.close();
-        rs = conn.createStatement().executeQuery(indexTableQuery);
-        assertTrue(rs.next());
-        assertEquals("x",rs.getString(1));
-        assertEquals("a",rs.getString(2));
-        assertEquals("j",rs.getString(3));
-        assertFalse(rs.next());
-        
-        // verify local index table rows
-        rs = conn.createStatement().executeQuery(localIndexTableQuery);
-        assertTrue(rs.next());
-        assertEquals("x",rs.getString(1));
-        assertEquals("a",rs.getString(2));
-        assertEquals("j",rs.getString(3));
-        assertFalse(rs.next());
-
-        // load some data into the table
-        stmt = conn.prepareStatement("UPSERT INTO " + dataTableFullName + " VALUES(?,?,?)");
-        stmt.setString(1, "a");
-        stmt.setString(2, "y");
-        stmt.setString(3, "k");
-        stmt.execute();
-        conn.commit();
-
-        // verify data table rows
-        rs = conn.createStatement().executeQuery(dataTableQuery);
-        assertTrue(rs.next());
-        assertEquals("a",rs.getString(1));
-        assertEquals("y",rs.getString(2));
-        assertEquals("k",rs.getString(3));
-        assertFalse(rs.next());
-        
-        // verify index table rows
-        rs = conn.createStatement().executeQuery(indexTableQuery);
-        assertTrue(rs.next());
-        assertEquals("y",rs.getString(1));
-        assertEquals("a",rs.getString(2));
-        assertEquals("k",rs.getString(3));
-        assertFalse(rs.next());
-        
-        // verify local index table rows
-        rs = conn.createStatement().executeQuery(localIndexTableQuery);
-        assertTrue(rs.next());
-        assertEquals("y",rs.getString(1));
-        assertEquals("a",rs.getString(2));
-        assertEquals("k",rs.getString(3));
-        assertFalse(rs.next());
-    }
-
-    @Test
-    public void testAddPKColumnToTableWithIndex() throws Exception {
-        String query;
-        ResultSet rs;
-        PreparedStatement stmt;
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        conn.setAutoCommit(false);
-
-        // make sure that the tables are empty, but reachable
-        conn.createStatement().execute(
-          "CREATE TABLE " + dataTableFullName
-              + " (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR) " + tableDDLOptions);
-        query = "SELECT * FROM " + dataTableFullName;
-        rs = conn.createStatement().executeQuery(query);
-        assertFalse(rs.next());
-
-        conn.createStatement().execute(
-          "CREATE INDEX " + indexTableName + " ON " + dataTableFullName + " (v1) include (v2)");
-        query = "SELECT * FROM " + indexTableFullName;
-        rs = conn.createStatement().executeQuery(query);
-        assertFalse(rs.next());
-
-        // load some data into the table
-        stmt = conn.prepareStatement("UPSERT INTO " + dataTableFullName + " VALUES(?,?,?)");
-        stmt.setString(1, "a");
-        stmt.setString(2, "x");
-        stmt.setString(3, "1");
-        stmt.execute();
-        conn.commit();
-
-        assertIndexExists(conn,true);
-        conn.createStatement().execute("ALTER TABLE " + dataTableFullName + " ADD v3 VARCHAR, k2 DECIMAL PRIMARY KEY, k3 DECIMAL PRIMARY KEY");
-        rs = conn.getMetaData().getPrimaryKeys("", schemaName, dataTableName);
-        assertTrue(rs.next());
-        assertEquals("K",rs.getString("COLUMN_NAME"));
-        assertEquals(1, rs.getShort("KEY_SEQ"));
-        assertTrue(rs.next());
-        assertEquals("K2",rs.getString("COLUMN_NAME"));
-        assertEquals(2, rs.getShort("KEY_SEQ"));
-        assertTrue(rs.next());
-        assertEquals("K3",rs.getString("COLUMN_NAME"));
-        assertEquals(3, rs.getShort("KEY_SEQ"));
-        assertFalse(rs.next());
-
-        rs = conn.getMetaData().getPrimaryKeys("", schemaName, indexTableName);
-        assertTrue(rs.next());
-        assertEquals(QueryConstants.DEFAULT_COLUMN_FAMILY + IndexUtil.INDEX_COLUMN_NAME_SEP + "V1",rs.getString("COLUMN_NAME"));
-        assertEquals(1, rs.getShort("KEY_SEQ"));
-        assertTrue(rs.next());
-        assertEquals(IndexUtil.INDEX_COLUMN_NAME_SEP + "K",rs.getString("COLUMN_NAME"));
-        assertEquals(2, rs.getShort("KEY_SEQ"));
-        assertTrue(rs.next());
-        assertEquals(IndexUtil.INDEX_COLUMN_NAME_SEP + "K2",rs.getString("COLUMN_NAME"));
-        assertEquals(3, rs.getShort("KEY_SEQ"));
-        assertTrue(rs.next());
-        assertEquals(IndexUtil.INDEX_COLUMN_NAME_SEP + "K3",rs.getString("COLUMN_NAME"));
-        assertEquals(4, rs.getShort("KEY_SEQ"));
-        assertFalse(rs.next());
-
-        query = "SELECT * FROM " + dataTableFullName;
-        rs = conn.createStatement().executeQuery(query);
-        assertTrue(rs.next());
-        assertEquals("a",rs.getString(1));
-        assertEquals("x",rs.getString(2));
-        assertEquals("1",rs.getString(3));
-        assertNull(rs.getBigDecimal(4));
-        assertFalse(rs.next());
-
-        // load some data into the table
-        stmt = conn.prepareStatement("UPSERT INTO " + dataTableFullName + "(K,K2,V1,V2,K3) VALUES(?,?,?,?,?)");
-        stmt.setString(1, "b");
-        stmt.setBigDecimal(2, BigDecimal.valueOf(2));
-        stmt.setString(3, "y");
-        stmt.setString(4, "2");
-        stmt.setBigDecimal(5, BigDecimal.valueOf(3));
-        stmt.execute();
-        conn.commit();
-
-        query = "SELECT k,k2,k3 FROM " + dataTableFullName + " WHERE v1='y'";
-        rs = conn.createStatement().executeQuery(query);
-        assertTrue(rs.next());
-        assertEquals("b",rs.getString(1));
-        assertEquals(BigDecimal.valueOf(2),rs.getBigDecimal(2));
-        assertEquals(BigDecimal.valueOf(3),rs.getBigDecimal(3));
-        assertFalse(rs.next());
-    }
 
     @Test
     public void testSetSaltedTableAsImmutable() throws Exception {
@@ -1142,903 +849,6 @@ public class AlterTableIT extends ParallelStatsDisabledIT {
         conn1.close();
     }
 
-    @Test
-    public void testSetHColumnProperties() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
-                +"ID1 VARCHAR(15) NOT NULL,\n"
-                +"ID2 VARCHAR(15) NOT NULL,\n"
-                +"CREATED_DATE DATE,\n"
-                +"CREATION_TIME BIGINT,\n"
-                +"LAST_USED DATE,\n"
-                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("SALT_BUCKETS = 8");
-        Connection conn1 = DriverManager.getConnection(getUrl(), props);
-        conn1.createStatement().execute(ddl);
-        ddl = "ALTER TABLE " + dataTableFullName + " SET REPLICATION_SCOPE=1";
-        conn1.createStatement().execute(ddl);
-        try (HBaseAdmin admin = conn1.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-            HColumnDescriptor[] columnFamilies = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName))
-                    .getColumnFamilies();
-            assertEquals(1, columnFamilies.length);
-            assertEquals("0", columnFamilies[0].getNameAsString());
-            assertEquals(1, columnFamilies[0].getScope());
-        }
-    }
-
-    @Test
-    public void testSetHTableProperties() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
-                +"ID1 VARCHAR(15) NOT NULL,\n"
-                +"ID2 VARCHAR(15) NOT NULL,\n"
-                +"CREATED_DATE DATE,\n"
-                +"CREATION_TIME BIGINT,\n"
-                +"LAST_USED DATE,\n"
-                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("SALT_BUCKETS = 8");
-        Connection conn1 = DriverManager.getConnection(getUrl(), props);
-        conn1.createStatement().execute(ddl);
-        ddl = "ALTER TABLE " + dataTableFullName + " SET COMPACTION_ENABLED=FALSE";
-        conn1.createStatement().execute(ddl);
-        try (HBaseAdmin admin = conn1.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-            HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
-            assertEquals(1, tableDesc.getColumnFamilies().length);
-            assertEquals("0", tableDesc.getColumnFamilies()[0].getNameAsString());
-            assertEquals(Boolean.toString(false), tableDesc.getValue(HTableDescriptor.COMPACTION_ENABLED));
-        }
-    }
-
-    @Test
-    public void testSetHTableAndHColumnProperties() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
-                +"ID1 VARCHAR(15) NOT NULL,\n"
-                +"ID2 VARCHAR(15) NOT NULL,\n"
-                +"CREATED_DATE DATE,\n"
-                +"CREATION_TIME BIGINT,\n"
-                +"LAST_USED DATE,\n"
-                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("SALT_BUCKETS = 8");
-        Connection conn1 = DriverManager.getConnection(getUrl(), props);
-        conn1.createStatement().execute(ddl);
-        ddl = "ALTER TABLE " + dataTableFullName + " SET COMPACTION_ENABLED = FALSE, REPLICATION_SCOPE = 1";
-        conn1.createStatement().execute(ddl);
-        try (HBaseAdmin admin = conn1.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-            HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
-            HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
-            assertEquals(1, columnFamilies.length);
-            assertEquals("0", columnFamilies[0].getNameAsString());
-            assertEquals(1, columnFamilies[0].getScope());
-            assertEquals(false, tableDesc.isCompactionEnabled());
-        }
-    }
-
-    @Test
-    public void testSetHTableHColumnAndPhoenixTableProperties() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
-                +"ID1 VARCHAR(15) NOT NULL,\n"
-                +"ID2 VARCHAR(15) NOT NULL,\n"
-                +"CREATED_DATE DATE,\n"
-                +"CF1.CREATION_TIME BIGINT,\n"
-                +"CF2.LAST_USED DATE,\n"
-                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("IMMUTABLE_ROWS=true"
-                + (!columnEncoded ? ",IMMUTABLE_STORAGE_SCHEME=" + PTable.ImmutableStorageScheme.ONE_CELL_PER_COLUMN : ""));
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        conn.createStatement().execute(ddl);
-        assertImmutableRows(conn, dataTableFullName, true);
-        ddl = "ALTER TABLE " + dataTableFullName + " SET COMPACTION_ENABLED = FALSE, VERSIONS = 10";
-        conn.createStatement().execute(ddl);
-        ddl = "ALTER TABLE " + dataTableFullName + " SET COMPACTION_ENABLED = FALSE, CF1.MIN_VERSIONS = 1, CF2.MIN_VERSIONS = 3, MIN_VERSIONS = 8, CF1.KEEP_DELETED_CELLS = true, KEEP_DELETED_CELLS = false";
-        conn.createStatement().execute(ddl);
-
-        try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-            HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
-            HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
-            assertEquals(3, columnFamilies.length);
-
-            assertEquals("0", columnFamilies[0].getNameAsString());
-            assertEquals(8, columnFamilies[0].getMinVersions());
-            assertEquals(10, columnFamilies[0].getMaxVersions());
-            assertEquals(KeepDeletedCells.FALSE, columnFamilies[0].getKeepDeletedCells());
-
-            assertEquals("CF1", columnFamilies[1].getNameAsString());
-            assertEquals(1, columnFamilies[1].getMinVersions());
-            assertEquals(10, columnFamilies[1].getMaxVersions());
-            assertEquals(KeepDeletedCells.TRUE, columnFamilies[1].getKeepDeletedCells());
-
-            assertEquals("CF2", columnFamilies[2].getNameAsString());
-            assertEquals(3, columnFamilies[2].getMinVersions());
-            assertEquals(10, columnFamilies[2].getMaxVersions());
-            assertEquals(KeepDeletedCells.FALSE, columnFamilies[2].getKeepDeletedCells());
-
-            assertEquals(Boolean.toString(false), tableDesc.getValue(HTableDescriptor.COMPACTION_ENABLED));
-        }
-    }
-
-    @Test
-    public void testSpecifyingColumnFamilyForHTablePropertyFails() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
-                +"ID1 VARCHAR(15) NOT NULL,\n"
-                +"ID2 VARCHAR(15) NOT NULL,\n"
-                +"CREATED_DATE DATE,\n"
-                +"CREATION_TIME BIGINT,\n"
-                +"LAST_USED DATE,\n"
-                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("SALT_BUCKETS = 8");
-        Connection conn1 = DriverManager.getConnection(getUrl(), props);
-        conn1.createStatement().execute(ddl);
-        ddl = "ALTER TABLE " + dataTableFullName + " SET CF.COMPACTION_ENABLED = FALSE";
-        try {
-            conn1.createStatement().execute(ddl);
-            fail();
-        } catch (SQLException e) {
-            assertEquals(SQLExceptionCode.COLUMN_FAMILY_NOT_ALLOWED_TABLE_PROPERTY.getErrorCode(), e.getErrorCode());
-        }
-    }
-
-    @Test
-    public void testSpecifyingColumnFamilyForPhoenixTablePropertyFails() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
-                +"ID1 VARCHAR(15) NOT NULL,\n"
-                +"ID2 VARCHAR(15) NOT NULL,\n"
-                +"CREATED_DATE DATE,\n"
-                +"CREATION_TIME BIGINT,\n"
-                +"LAST_USED DATE,\n"
-                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("SALT_BUCKETS = 8");
-        Connection conn1 = DriverManager.getConnection(getUrl(), props);
-        conn1.createStatement().execute(ddl);
-        ddl = "ALTER TABLE " + dataTableFullName + " SET CF.DISABLE_WAL = TRUE";
-        try {
-            conn1.createStatement().execute(ddl);
-            fail();
-        } catch (SQLException e) {
-            assertEquals(SQLExceptionCode.COLUMN_FAMILY_NOT_ALLOWED_TABLE_PROPERTY.getErrorCode(), e.getErrorCode());
-        }
-    }
-
-    @Test
-    public void testSpecifyingColumnFamilyForTTLFails() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
-                +"ID1 VARCHAR(15) NOT NULL,\n"
-                +"ID2 VARCHAR(15) NOT NULL,\n"
-                +"CREATED_DATE DATE,\n"
-                +"CREATION_TIME BIGINT,\n"
-                +"CF.LAST_USED DATE,\n"
-                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("SALT_BUCKETS = 8");
-        Connection conn1 = DriverManager.getConnection(getUrl(), props);
-        conn1.createStatement().execute(ddl);
-        ddl = "ALTER TABLE " + dataTableFullName + " SET CF.TTL = 86400";
-        try {
-            conn1.createStatement().execute(ddl);
-            fail();
-        } catch (SQLException e) {
-            assertEquals(SQLExceptionCode.COLUMN_FAMILY_NOT_ALLOWED_FOR_TTL.getErrorCode(), e.getErrorCode());
-        }
-    }
-
-    @Test
-    public void testSetPropertyNeedsColumnFamilyToExist() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
-                +"ID1 VARCHAR(15) NOT NULL,\n"
-                +"ID2 VARCHAR(15) NOT NULL,\n"
-                +"CREATED_DATE DATE,\n"
-                +"CREATION_TIME BIGINT,\n"
-                +"LAST_USED DATE,\n"
-                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("SALT_BUCKETS = 8");
-        Connection conn1 = DriverManager.getConnection(getUrl(), props);
-        conn1.createStatement().execute(ddl);
-        ddl = "ALTER TABLE " + dataTableFullName + " SET CF.REPLICATION_SCOPE = 1";
-        try {
-            conn1.createStatement().execute(ddl);
-            fail();
-        } catch (SQLException e) {
-            assertEquals(SQLExceptionCode.COLUMN_FAMILY_NOT_FOUND.getErrorCode(), e.getErrorCode());
-        }
-    }
-
-    @Test
-    public void testSetDefaultColumnFamilyNotAllowed() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
-                +"ID1 VARCHAR(15) NOT NULL,\n"
-                +"ID2 VARCHAR(15) NOT NULL,\n"
-                +"CREATED_DATE DATE,\n"
-                +"CREATION_TIME BIGINT,\n"
-                +"LAST_USED DATE,\n"
-                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions(" SALT_BUCKETS = 8");
-        Connection conn1 = DriverManager.getConnection(getUrl(), props);
-        conn1.createStatement().execute(ddl);
-        ddl = "ALTER TABLE " + dataTableFullName + " SET DEFAULT_COLUMN_FAMILY = 'A'";
-        try {
-            conn1.createStatement().execute(ddl);
-            fail();
-        } catch (SQLException e) {
-            assertEquals(SQLExceptionCode.DEFAULT_COLUMN_FAMILY_ONLY_ON_CREATE_TABLE.getErrorCode(), e.getErrorCode());
-        }
-    }
-
-    @Test
-    public void testSetHColumnOrHTablePropertiesOnViewsNotAllowed() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
-                +"ID1 VARCHAR(15) NOT NULL,\n"
-                +"ID2 VARCHAR(15) NOT NULL,\n"
-                +"CREATED_DATE DATE,\n"
-                +"CREATION_TIME BIGINT,\n"
-                +"LAST_USED DATE,\n"
-                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("SALT_BUCKETS = 8");
-        Connection conn1 = DriverManager.getConnection(getUrl(), props);
-        conn1.createStatement().execute(ddl);
-        ddl = "CREATE VIEW " + viewName + "  AS SELECT * FROM " + dataTableFullName + " WHERE CREATION_TIME = 1";
-        conn1.createStatement().execute(ddl);
-        ddl = "ALTER VIEW " + viewName + " SET REPLICATION_SCOPE = 1";
-        try {
-            conn1.createStatement().execute(ddl);
-            fail();
-        } catch (SQLException e) {
-            assertEquals(SQLExceptionCode.VIEW_WITH_PROPERTIES.getErrorCode(), e.getErrorCode());
-        }
-        ddl = "ALTER VIEW " + viewName + " SET COMPACTION_ENABLED = FALSE";
-        try {
-            conn1.createStatement().execute(ddl);
-            fail();
-        } catch (SQLException e) {
-            assertEquals(SQLExceptionCode.VIEW_WITH_PROPERTIES.getErrorCode(), e.getErrorCode());
-        }
-    }
-
-    @Test
-    public void testSetForSomePhoenixTablePropertiesOnViewsAllowed() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
-                +"ID1 VARCHAR(15) NOT NULL,\n"
-                +"ID2 VARCHAR(15) NOT NULL,\n"
-                +"CREATED_DATE DATE,\n"
-                +"CREATION_TIME BIGINT,\n"
-                +"LAST_USED DATE,\n"
-                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("SALT_BUCKETS = 8");
-        Connection conn1 = DriverManager.getConnection(getUrl(), props);
-        conn1.createStatement().execute(ddl);
-        String viewFullName = SchemaUtil.getTableName(schemaName, generateUniqueName());
-        ddl = "CREATE VIEW " + viewFullName + " AS SELECT * FROM " + dataTableFullName + " WHERE CREATION_TIME = 1";
-        conn1.createStatement().execute(ddl);
-        ddl = "ALTER VIEW " + viewFullName + " SET UPDATE_CACHE_FREQUENCY = 10";
-        conn1.createStatement().execute(ddl);
-        conn1.createStatement().execute("SELECT * FROM " + viewFullName);
-        PhoenixConnection pconn = conn1.unwrap(PhoenixConnection.class);
-        assertEquals(10, pconn.getTable(new PTableKey(pconn.getTenantId(), viewFullName)).getUpdateCacheFrequency());
-        ddl = "ALTER VIEW " + viewFullName + " SET UPDATE_CACHE_FREQUENCY = 20";
-        conn1.createStatement().execute(ddl);
-        conn1.createStatement().execute("SELECT * FROM " + viewFullName);
-        pconn = conn1.unwrap(PhoenixConnection.class);
-        assertEquals(20, pconn.getTable(new PTableKey(pconn.getTenantId(), viewFullName)).getUpdateCacheFrequency());
-        assertImmutableRows(conn1, viewFullName, false);
-        ddl = "ALTER VIEW " + viewFullName + " SET DISABLE_WAL = TRUE";
-        try {
-            conn1.createStatement().execute(ddl);
-            fail();
-        } catch (SQLException e) {
-            assertEquals(SQLExceptionCode.VIEW_WITH_PROPERTIES.getErrorCode(), e.getErrorCode());
-        }
-        ddl = "ALTER VIEW " + viewFullName + " SET THROW_INDEX_WRITE_FAILURE = FALSE";
-        try {
-            conn1.createStatement().execute(ddl);
-            fail();
-        } catch (SQLException e) {
-            assertEquals(SQLExceptionCode.VIEW_WITH_PROPERTIES.getErrorCode(), e.getErrorCode());
-        }
-    }
-
-    @Test
-    public void testSettingPropertiesWhenTableHasDefaultColFamilySpecified() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String ddl = "CREATE TABLE  " + dataTableFullName + " (\n"
-                +"ID1 VARCHAR(15) NOT NULL,\n"
-                +"ID2 VARCHAR(15) NOT NULL,\n"
-                +"CREATED_DATE DATE,\n"
-                +"CREATION_TIME BIGINT,\n"
-                +"CF.LAST_USED DATE,\n"
-                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("IMMUTABLE_ROWS=true, DEFAULT_COLUMN_FAMILY = 'XYZ'"
-                + (!columnEncoded ? ",IMMUTABLE_STORAGE_SCHEME=" + PTable.ImmutableStorageScheme.ONE_CELL_PER_COLUMN : ""));
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        conn.createStatement().execute(ddl);
-        assertImmutableRows(conn, dataTableFullName, true);
-        ddl = "ALTER TABLE  " + dataTableFullName
-                + " SET COMPACTION_ENABLED = FALSE, CF.REPLICATION_SCOPE=1, IMMUTABLE_ROWS = TRUE, TTL=1000";
-        conn.createStatement().execute(ddl);
-        assertImmutableRows(conn, dataTableFullName, true);
-        try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-            HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
-            HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
-            assertEquals(2, columnFamilies.length);
-            assertEquals("CF", columnFamilies[0].getNameAsString());
-            assertEquals(1, columnFamilies[0].getScope());
-            assertEquals(1000, columnFamilies[0].getTimeToLive());
-            assertEquals("XYZ", columnFamilies[1].getNameAsString());
-            assertEquals(DEFAULT_REPLICATION_SCOPE, columnFamilies[1].getScope());
-            assertEquals(1000, columnFamilies[1].getTimeToLive());
-            assertEquals(Boolean.toString(false), tableDesc.getValue(HTableDescriptor.COMPACTION_ENABLED));
-        }
-    }
-
-    private static void assertImmutableRows(Connection conn, String fullTableName, boolean expectedValue) throws SQLException {
-        PhoenixConnection pconn = conn.unwrap(PhoenixConnection.class);
-        assertEquals(expectedValue, pconn.getTable(new PTableKey(pconn.getTenantId(), fullTableName)).isImmutableRows());
-    }
-
-    @Test
-    public void testSetPropertyAndAddColumnForExistingColumnFamily() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String ddl = "CREATE TABLE " + dataTableFullName
-                +
-                "  (a_string varchar not null, col1 integer, CF.col2 integer" +
-                "  CONSTRAINT pk PRIMARY KEY (a_string)) " + tableDDLOptions;
-        try {
-            conn.createStatement().execute(ddl);
-            conn.createStatement().execute(
-                    "ALTER TABLE " + dataTableFullName + " ADD CF.col3 integer CF.IN_MEMORY=true");
-            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-                HColumnDescriptor[] columnFamilies = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName))
-                        .getColumnFamilies();
-                assertEquals(2, columnFamilies.length);
-                assertEquals("0", columnFamilies[0].getNameAsString());
-                assertFalse(columnFamilies[0].isInMemory());
-                assertEquals("CF", columnFamilies[1].getNameAsString());
-                assertTrue(columnFamilies[1].isInMemory());
-            }
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testSetPropertyAndAddColumnForNewAndExistingColumnFamily() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String ddl = "CREATE TABLE " + dataTableFullName + " "
-                +
-                "  (a_string varchar not null, col1 integer, CF1.col2 integer" +
-                "  CONSTRAINT pk PRIMARY KEY (a_string)) " + tableDDLOptions;
-        try {
-            conn.createStatement().execute(ddl);
-            conn.createStatement()
-                    .execute(
-                            "ALTER TABLE "
-                                    + dataTableFullName
-                                    + " ADD col4 integer, CF1.col5 integer, CF2.col6 integer IN_MEMORY=true, CF1.REPLICATION_SCOPE=1, CF2.IN_MEMORY=false ");
-            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-                HColumnDescriptor[] columnFamilies = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName))
-                        .getColumnFamilies();
-                assertEquals(3, columnFamilies.length);
-                assertEquals("0", columnFamilies[0].getNameAsString());
-                assertTrue(columnFamilies[0].isInMemory());
-                assertEquals(0, columnFamilies[0].getScope());
-                assertEquals("CF1", columnFamilies[1].getNameAsString());
-                assertTrue(columnFamilies[1].isInMemory());
-                assertEquals(1, columnFamilies[1].getScope());
-                assertEquals("CF2", columnFamilies[2].getNameAsString());
-                assertFalse(columnFamilies[2].isInMemory());
-                assertEquals(0, columnFamilies[2].getScope());
-            }
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testSetPropertyAndAddColumnWhenTableHasExplicitDefaultColumnFamily() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String ddl = "CREATE TABLE " + dataTableFullName + " "
-                +
-                "  (a_string varchar not null, col1 integer, CF1.col2 integer" +
-                "  CONSTRAINT pk PRIMARY KEY (a_string)) " + generateDDLOptions("DEFAULT_COLUMN_FAMILY = 'XYZ'");
-        try {
-            conn.createStatement().execute(ddl);
-            conn.createStatement()
-                    .execute(
-                            "ALTER TABLE "
-                                    + dataTableFullName
-                                    + " ADD col4 integer, CF1.col5 integer, CF2.col6 integer IN_MEMORY=true, CF1.REPLICATION_SCOPE=1, CF2.IN_MEMORY=false, XYZ.REPLICATION_SCOPE=1 ");
-            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-                HColumnDescriptor[] columnFamilies = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName))
-                        .getColumnFamilies();
-                assertEquals(3, columnFamilies.length);
-                assertEquals("CF1", columnFamilies[0].getNameAsString());
-                assertTrue(columnFamilies[0].isInMemory());
-                assertEquals(1, columnFamilies[0].getScope());
-                assertEquals("CF2", columnFamilies[1].getNameAsString());
-                assertFalse(columnFamilies[1].isInMemory());
-                assertEquals(0, columnFamilies[1].getScope());
-                assertEquals("XYZ", columnFamilies[2].getNameAsString());
-                assertTrue(columnFamilies[2].isInMemory());
-                assertEquals(1, columnFamilies[2].getScope());
-            }
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testSetPropertyAndAddColumnFailsForColumnFamilyNotPresentInAddCol() throws Exception {
-    	Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-    	Connection conn = DriverManager.getConnection(getUrl(), props);
-        String ddl = "CREATE TABLE " + dataTableFullName + " "
-                +
-    			"  (a_string varchar not null, col1 integer, CF1.col2 integer" +
-    			"  CONSTRAINT pk PRIMARY KEY (a_string)) "+ generateDDLOptions("DEFAULT_COLUMN_FAMILY = 'XYZ'");
-    	try {
-    		conn.createStatement().execute(ddl);
-    		try {
-                conn.createStatement().execute(
-                        "ALTER TABLE " + dataTableFullName
-                                + " ADD col4 integer CF1.REPLICATION_SCOPE=1, XYZ.IN_MEMORY=true ");
-    			fail();
-    		} catch(SQLException e) {
-    			assertEquals(SQLExceptionCode.CANNOT_SET_PROPERTY_FOR_COLUMN_NOT_ADDED.getErrorCode(), e.getErrorCode());
-    		}
-    	} finally {
-    		conn.close();
-    	}
-    }
-
-    @Test
-    public void testSetPropertyAndAddColumnForDifferentColumnFamilies() throws Exception {
-    	Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-    	Connection conn = DriverManager.getConnection(getUrl(), props);
-        String ddl = "CREATE TABLE " + dataTableFullName
-                +
-                "  (a_string varchar not null, col1 integer, CF1.col2 integer, CF2.col3 integer" +
-                "  CONSTRAINT pk PRIMARY KEY (a_string)) " + generateDDLOptions("DEFAULT_COLUMN_FAMILY = 'XYZ' ");
-        try {
-            conn.createStatement().execute(ddl);
-            conn.createStatement()
-                    .execute(
-                            "ALTER TABLE "
-                                    + dataTableFullName
-                                    + " ADD col4 integer, CF1.col5 integer, CF2.col6 integer, CF3.col7 integer CF1.REPLICATION_SCOPE=1, CF1.IN_MEMORY=false, IN_MEMORY=true ");
-            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-                HColumnDescriptor[] columnFamilies = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName))
-                        .getColumnFamilies();
-                assertEquals(4, columnFamilies.length);
-                assertEquals("CF1", columnFamilies[0].getNameAsString());
-                assertFalse(columnFamilies[0].isInMemory());
-                assertEquals(1, columnFamilies[0].getScope());
-                assertEquals("CF2", columnFamilies[1].getNameAsString());
-                assertTrue(columnFamilies[1].isInMemory());
-                assertEquals(0, columnFamilies[1].getScope());
-                assertEquals("CF3", columnFamilies[2].getNameAsString());
-                assertTrue(columnFamilies[2].isInMemory());
-                assertEquals(0, columnFamilies[2].getScope());
-                assertEquals("XYZ", columnFamilies[3].getNameAsString());
-                assertTrue(columnFamilies[3].isInMemory());
-                assertEquals(0, columnFamilies[3].getScope());
-            }
-    	} finally {
-    		conn.close();
-    	}
-    }
-
-    @Test
-    public void testSetPropertyAndAddColumnUsingDefaultColumnFamilySpecifier() throws Exception {
-    	Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-    	Connection conn = DriverManager.getConnection(getUrl(), props);
-        String ddl = "CREATE TABLE " + dataTableFullName
-                +
-    			"  (a_string varchar not null, col1 integer, CF1.col2 integer" +
-    			"  CONSTRAINT pk PRIMARY KEY (a_string)) " + generateDDLOptions("DEFAULT_COLUMN_FAMILY = 'XYZ'");
-    	try {
-            conn.createStatement().execute(ddl);
-            conn.createStatement().execute(
-                    "ALTER TABLE " + dataTableFullName + " ADD col4 integer XYZ.REPLICATION_SCOPE=1 ");
-            conn.createStatement()
-                    .execute("ALTER TABLE " + dataTableFullName + " ADD XYZ.col5 integer IN_MEMORY=true ");
-            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-                HColumnDescriptor[] columnFamilies = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName))
-                        .getColumnFamilies();
-                assertEquals(2, columnFamilies.length);
-                assertEquals("CF1", columnFamilies[0].getNameAsString());
-                assertFalse(columnFamilies[0].isInMemory());
-                assertEquals(0, columnFamilies[0].getScope());
-                assertEquals("XYZ", columnFamilies[1].getNameAsString());
-                assertTrue(columnFamilies[1].isInMemory());
-                assertEquals(1, columnFamilies[1].getScope());
-            }
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testSetPropertyAndAddColumnForDefaultColumnFamily() throws Exception {
-    	Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-    	Connection conn = DriverManager.getConnection(getUrl(), props);
-    	conn.setAutoCommit(false);
-        String ddl = "CREATE TABLE " + dataTableFullName +
-    			"  (a_string varchar not null, col1 integer" +
-    			"  CONSTRAINT pk PRIMARY KEY (a_string)) " + tableDDLOptions;
-    	try {
-    		conn.createStatement().execute(ddl);
-            conn.createStatement().execute("ALTER TABLE " + dataTableFullName + " ADD col2 integer IN_MEMORY=true");
-    		try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-                HColumnDescriptor[] columnFamilies = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName))
-                        .getColumnFamilies();
-    			assertEquals(1, columnFamilies.length);
-    			assertEquals("0", columnFamilies[0].getNameAsString());
-    			assertTrue(columnFamilies[0].isInMemory());
-    		}
-    	} finally {
-    		conn.close();
-    	}
-    }
-
-    @Test
-    public void testAddNewColumnFamilyProperties() throws Exception {
-    	Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-    	Connection conn = DriverManager.getConnection(getUrl(), props);
-    	conn.setAutoCommit(false);
-
-    	try {
-    		conn.createStatement()
-    		.execute(
-                            "CREATE TABLE "
-                                    + dataTableFullName
-    						+ "  (a_string varchar not null, col1 integer, cf1.col2 integer, col3 integer , cf2.col4 integer "
-    						+ "  CONSTRAINT pk PRIMARY KEY (a_string)) " + generateDDLOptions("immutable_rows=true , SALT_BUCKETS=3 "
-    						+ (!columnEncoded ? ",IMMUTABLE_STORAGE_SCHEME=" + PTable.ImmutableStorageScheme.ONE_CELL_PER_COLUMN : ""))); 
-
-            String ddl = "Alter table " + dataTableFullName + " add cf3.col5 integer, cf4.col6 integer in_memory=true";
-    		conn.createStatement().execute(ddl);
-
-    		try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
-    			assertTrue(tableDesc.isCompactionEnabled());
-    			HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
-    			assertEquals(5, columnFamilies.length);
-    			assertEquals("0", columnFamilies[0].getNameAsString());
-    			assertFalse(columnFamilies[0].isInMemory());
-    			assertEquals("CF1", columnFamilies[1].getNameAsString());
-    			assertFalse(columnFamilies[1].isInMemory());
-    			assertEquals("CF2", columnFamilies[2].getNameAsString());
-    			assertFalse(columnFamilies[2].isInMemory());
-    			assertEquals("CF3", columnFamilies[3].getNameAsString());
-    			assertTrue(columnFamilies[3].isInMemory());
-    			assertEquals("CF4", columnFamilies[4].getNameAsString());
-    			assertTrue(columnFamilies[4].isInMemory());
-    		}
-    	} finally {
-    		conn.close();
-    	}
-    }
-
-    @Test
-    public void testAddProperyToExistingColumnFamily() throws Exception {
-    	Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-    	Connection conn = DriverManager.getConnection(getUrl(), props);
-    	conn.setAutoCommit(false);
-
-    	try {
-    		conn.createStatement()
-    		.execute(
-                            "CREATE TABLE "
-                                    + dataTableFullName
-    						+ "  (a_string varchar not null, col1 integer, cf1.col2 integer, col3 integer , cf2.col4 integer "
-    						+ "  CONSTRAINT pk PRIMARY KEY (a_string)) " + generateDDLOptions("immutable_rows=true , SALT_BUCKETS=3 "
-    						+ (!columnEncoded ? ",IMMUTABLE_STORAGE_SCHEME=" + PTable.ImmutableStorageScheme.ONE_CELL_PER_COLUMN : "")));    
-
-            String ddl = "Alter table " + dataTableFullName + " add cf1.col5 integer in_memory=true";
-    		conn.createStatement().execute(ddl);
-
-    		try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
-    			assertTrue(tableDesc.isCompactionEnabled());
-    			HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
-    			assertEquals(3, columnFamilies.length);
-    			assertEquals("0", columnFamilies[0].getNameAsString());
-    			assertFalse(columnFamilies[0].isInMemory());
-    			assertEquals("CF1", columnFamilies[1].getNameAsString());
-    			assertTrue(columnFamilies[1].isInMemory());
-    			assertEquals("CF2", columnFamilies[2].getNameAsString());
-    			assertFalse(columnFamilies[2].isInMemory());
-    		}
-    	} finally {
-    		conn.close();
-    	}
-    }
-
-    @Test
-    public void testAddTTLToExistingColumnFamily() throws Exception {
-    	Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-    	Connection conn = DriverManager.getConnection(getUrl(), props);
-    	conn.setAutoCommit(false);
-
-    	try {
-            String ddl = "CREATE TABLE " + dataTableFullName
-                    + " (pk char(2) not null primary key, col1 integer, b.col1 integer) " + tableDDLOptions + " SPLIT ON ('EA','EZ') ";
-    		conn.createStatement().execute(ddl);
-            ddl = "ALTER TABLE " + dataTableFullName + " add b.col2 varchar ttl=30";
-    		conn.createStatement().execute(ddl);
-    		fail();
-    	} catch (SQLException e) {
-    		assertEquals(SQLExceptionCode.CANNOT_SET_TABLE_PROPERTY_ADD_COLUMN.getErrorCode(), e.getErrorCode());
-    	} finally {
-    		conn.close();
-    	}
-    }
-
-    @Test
-    public void testSettingTTLWhenAddingColumnNotAllowed() throws Exception {
-    	Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-    	Connection conn = DriverManager.getConnection(getUrl(), props);
-    	conn.setAutoCommit(false);
-
-    	try {
-            String ddl = "CREATE TABLE " + dataTableFullName
-                    + " (pk char(2) not null primary key) " + generateDDLOptions("TTL=100") + " SPLIT ON ('EA','EZ')";
-    		conn.createStatement().execute(ddl);
-            ddl = "ALTER TABLE " + dataTableFullName + " add col1 varchar ttl=30";
-    		conn.createStatement().execute(ddl);
-    		fail();
-    	} catch (SQLException e) {
-    		assertEquals(SQLExceptionCode.CANNOT_SET_TABLE_PROPERTY_ADD_COLUMN.getErrorCode(), e.getErrorCode());
-    	}
-    	try {
-            String ddl = "ALTER TABLE " + dataTableFullName + " add col1 varchar a.ttl=30";
-    		conn.createStatement().execute(ddl);
-    		fail();
-    	} catch (SQLException e) {
-    		assertEquals(SQLExceptionCode.COLUMN_FAMILY_NOT_ALLOWED_FOR_TTL.getErrorCode(), e.getErrorCode());
-    	} finally {
-    		conn.close();
-    	}
-    }
-
-    @Test
-    public void testSetTTLForTableWithOnlyPKCols() throws Exception {
-    	Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-    	Connection conn = DriverManager.getConnection(getUrl(), props);
-    	conn.setAutoCommit(false);
-    	try {
-            String ddl = "create table " + dataTableFullName + " ("
-        		    + " id char(1) NOT NULL,"
-        		    + " col1 integer NOT NULL,"
-        		    + " col2 bigint NOT NULL,"
-        		    + " CONSTRAINT NAME_PK PRIMARY KEY (id, col1, col2)"
-        		    + " ) " + generateDDLOptions("TTL=86400, SALT_BUCKETS = 4, DEFAULT_COLUMN_FAMILY='XYZ'");
-            conn.createStatement().execute(ddl);
-            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
-    			HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
-    			assertEquals(1, columnFamilies.length);
-    			assertEquals("XYZ", columnFamilies[0].getNameAsString());
-    			assertEquals(86400, columnFamilies[0].getTimeToLive());
-    		}
-            ddl = "ALTER TABLE " + dataTableFullName + " SET TTL=30";
-    		conn.createStatement().execute(ddl);
-    		conn.commit();
-    		try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
-    			HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
-    			assertEquals(1, columnFamilies.length);
-    			assertEquals(30, columnFamilies[0].getTimeToLive());
-    			assertEquals("XYZ", columnFamilies[0].getNameAsString());
-    		}
-    	} finally {
-    		conn.close();
-    	}
-    }
-
-    @Test
-    public void testSetHColumnPropertyForTableWithOnlyPKCols1() throws Exception {
-    	Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-    	Connection conn = DriverManager.getConnection(getUrl(), props);
-    	conn.setAutoCommit(false);
-    	try {
-            String ddl = "create table " + dataTableFullName + " ("
-        		    + " id char(1) NOT NULL,"
-        		    + " col1 integer NOT NULL,"
-        		    + " col2 bigint NOT NULL,"
-        		    + " CONSTRAINT NAME_PK PRIMARY KEY (id, col1, col2)"
-        		    + " ) " + generateDDLOptions("TTL=86400, SALT_BUCKETS = 4, DEFAULT_COLUMN_FAMILY='XYZ'");
-            conn.createStatement().execute(ddl);
-            ddl = "ALTER TABLE " + dataTableFullName + " SET IN_MEMORY=true";
-    		conn.createStatement().execute(ddl);
-    		conn.commit();
-    		try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
-    			HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
-    			assertEquals(1, columnFamilies.length);
-    			assertEquals(true, columnFamilies[0].isInMemory());
-    			assertEquals("XYZ", columnFamilies[0].getNameAsString());
-    		}
-    	} finally {
-    		conn.close();
-    	}
-    }
-
-    @Test
-    public void testSetHColumnPropertyForTableWithOnlyPKCols2() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        conn.setAutoCommit(false);
-    	try {
-            String ddl = "create table " + dataTableFullName + " ("
-        		    + " id char(1) NOT NULL,"
-        		    + " col1 integer NOT NULL,"
-        		    + " col2 bigint NOT NULL,"
-        		    + " CONSTRAINT NAME_PK PRIMARY KEY (id, col1, col2)"
-        		    + " ) " + generateDDLOptions("TTL=86400, SALT_BUCKETS = 4");
-            conn.createStatement().execute(ddl);
-            ddl = "ALTER TABLE " + dataTableFullName + " SET IN_MEMORY=true";
-    		conn.createStatement().execute(ddl);
-    		conn.commit();
-    		try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
-    			HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
-    			assertEquals(1, columnFamilies.length);
-    			assertEquals(true, columnFamilies[0].isInMemory());
-    			assertEquals("0", columnFamilies[0].getNameAsString());
-    		}
-    	} finally {
-    		conn.close();
-    	}
-    }
-
-    @Test
-    public void testSetHColumnPropertyAndAddColumnForDefaultCFForTableWithOnlyPKCols() throws Exception {
-    	Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-    	Connection conn = DriverManager.getConnection(getUrl(), props);
-    	conn.setAutoCommit(false);
-    	try {
-            String ddl = "create table " + dataTableFullName + " ("
-        		    + " id char(1) NOT NULL,"
-        		    + " col1 integer NOT NULL,"
-        		    + " col2 bigint NOT NULL,"
-        		    + " CONSTRAINT NAME_PK PRIMARY KEY (id, col1, col2)"
-        		    + " ) " + generateDDLOptions("TTL=86400, SALT_BUCKETS = 4, DEFAULT_COLUMN_FAMILY='XYZ'");
-            conn.createStatement().execute(ddl);
-            ddl = "ALTER TABLE " + dataTableFullName + " ADD COL3 INTEGER IN_MEMORY=true";
-    		conn.createStatement().execute(ddl);
-    		conn.commit();
-    		try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
-    			HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
-    			assertEquals(1, columnFamilies.length);
-    			assertEquals(true, columnFamilies[0].isInMemory());
-    			assertEquals("XYZ", columnFamilies[0].getNameAsString());
-    		}
-    	} finally {
-    		conn.close();
-    	}
-    }
-
-    @Test
-    public void testSetHColumnPropertyAndAddColumnForNewCFForTableWithOnlyPKCols() throws Exception {
-    	Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-    	Connection conn = DriverManager.getConnection(getUrl(), props);
-    	conn.setAutoCommit(false);
-    	try {
-            String ddl = "create table " + dataTableFullName + " ("
-        		    + " id char(1) NOT NULL,"
-        		    + " col1 integer NOT NULL,"
-        		    + " col2 bigint NOT NULL,"
-        		    + " CONSTRAINT NAME_PK PRIMARY KEY (id, col1, col2)"
-        		    + " ) " + generateDDLOptions("TTL=86400, SALT_BUCKETS = 4, DEFAULT_COLUMN_FAMILY='XYZ'");
-            conn.createStatement().execute(ddl);
-            ddl = "ALTER TABLE " + dataTableFullName + " ADD NEWCF.COL3 INTEGER IN_MEMORY=true";
-    		conn.createStatement().execute(ddl);
-    		conn.commit();
-    		try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
-    			HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
-    			assertEquals(2, columnFamilies.length);
-    			assertEquals("NEWCF", columnFamilies[0].getNameAsString());
-    			assertEquals(true, columnFamilies[0].isInMemory());
-    			assertEquals("XYZ", columnFamilies[1].getNameAsString());
-    			assertEquals(false, columnFamilies[1].isInMemory());
-    		}
-    	} finally {
-    		conn.close();
-    	}
-    }
-
-    @Test
-    public void testTTLAssignmentForNewEmptyCF() throws Exception {
-    	Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-    	Connection conn = DriverManager.getConnection(getUrl(), props);
-    	conn.setAutoCommit(false);
-    	try {
-            String ddl = "create table " + dataTableFullName + " ("
-        		    + " id char(1) NOT NULL,"
-        		    + " col1 integer NOT NULL,"
-        		    + " col2 bigint NOT NULL,"
-        		    + " CONSTRAINT NAME_PK PRIMARY KEY (id, col1, col2)"
-        		    + " ) " + generateDDLOptions("TTL=86400, SALT_BUCKETS = 4, DEFAULT_COLUMN_FAMILY='XYZ'");
-            conn.createStatement().execute(ddl);
-            ddl = "ALTER TABLE " + dataTableFullName + " ADD NEWCF.COL3 INTEGER IN_MEMORY=true";
-    		conn.createStatement().execute(ddl);
-    		conn.commit();
-    		try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
-    			HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
-    			assertEquals(2, columnFamilies.length);
-    			assertEquals("NEWCF", columnFamilies[0].getNameAsString());
-    			assertEquals(true, columnFamilies[0].isInMemory());
-    			assertEquals(86400, columnFamilies[0].getTimeToLive());
-    			assertEquals("XYZ", columnFamilies[1].getNameAsString());
-    			assertEquals(false, columnFamilies[1].isInMemory());
-    			assertEquals(86400, columnFamilies[1].getTimeToLive());
-    		}
-
-            ddl = "ALTER TABLE " + dataTableFullName + " SET TTL=1000";
-    		conn.createStatement().execute(ddl);
-    		conn.commit();
-    		try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
-    			HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
-    			assertEquals(2, columnFamilies.length);
-    			assertEquals("NEWCF", columnFamilies[0].getNameAsString());
-    			assertEquals(true, columnFamilies[0].isInMemory());
-    			assertEquals(1000, columnFamilies[0].getTimeToLive());
-    			assertEquals("XYZ", columnFamilies[1].getNameAsString());
-    			assertEquals(false, columnFamilies[1].isInMemory());
-    			assertEquals(86400, columnFamilies[1].getTimeToLive());
-    		}
-
-    		// the new column will be assigned to the column family XYZ. With the a KV column getting added for XYZ,
-    		// the column family will start showing up in PTable.getColumnFamilies() after the column is added. Thus
-    		// being a new column family for the PTable, it will end up inheriting the TTL of the emptyCF (NEWCF).
-            ddl = "ALTER TABLE " + dataTableFullName + " ADD COL3 INTEGER";
-    		conn.createStatement().execute(ddl);
-    		conn.commit();
-    		try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
-    			HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
-    			assertEquals(2, columnFamilies.length);
-    			assertEquals("NEWCF", columnFamilies[0].getNameAsString());
-    			assertEquals(true, columnFamilies[0].isInMemory());
-    			assertEquals(1000, columnFamilies[0].getTimeToLive());
-    			assertEquals("XYZ", columnFamilies[1].getNameAsString());
-    			assertEquals(false, columnFamilies[1].isInMemory());
-    			assertEquals(1000, columnFamilies[1].getTimeToLive());
-    		}
-    	} finally {
-    		conn.close();
-    	}
-    }
-
-    @Test
-    public void testSettingNotHColumnNorPhoenixPropertyEndsUpAsHTableProperty() throws Exception {
-    	Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-    	Connection conn = DriverManager.getConnection(getUrl(), props);
-    	try {
-            String ddl = "create table " + dataTableFullName + " ("
-    				+ " id char(1) NOT NULL,"
-    				+ " col1 integer NOT NULL,"
-    				+ " col2 bigint NOT NULL,"
-    				+ " CONSTRAINT NAME_PK PRIMARY KEY (id, col1, col2)"
-    				+ " ) " +  tableDDLOptions;
-    		conn.createStatement().execute(ddl);
-            ddl = "ALTER TABLE " + dataTableFullName + " ADD NEWCF.COL3 INTEGER NEWCF.UNKNOWN_PROP='ABC'";
-    		try {
-    			conn.createStatement().execute(ddl);
-    			fail();
-    		} catch (SQLException e) {
-    			assertEquals(SQLExceptionCode.CANNOT_SET_TABLE_PROPERTY_ADD_COLUMN.getErrorCode(), e.getErrorCode());
-    		}
-            ddl = "ALTER TABLE " + dataTableFullName + " SET UNKNOWN_PROP='ABC'";
-    		conn.createStatement().execute(ddl);
-    		try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
-    			assertEquals("ABC", tableDesc.getValue("UNKNOWN_PROP"));
-    		}
-    	} finally {
-    		conn.close();
-    	}
-    }
 
     @Test
     public void testAlterStoreNulls() throws SQLException {
@@ -2534,46 +1344,5 @@ public class AlterTableIT extends ParallelStatsDisabledIT {
         }
     }
     
-    @Test
-    public void testAlterImmutableRowsPropertyForOneCellPerKeyValueColumnStorageScheme() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
-                +"ID VARCHAR(15) NOT NULL,\n"
-                +"CREATED_DATE DATE,\n"
-                +"CREATION_TIME BIGINT,\n"
-                +"CONSTRAINT PK PRIMARY KEY (ID)) " + tableDDLOptions;
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        conn.createStatement().execute(ddl);
-        assertImmutableRows(conn, dataTableFullName, false);
-        ddl = "ALTER TABLE " + dataTableFullName + " SET IMMUTABLE_ROWS = true";
-        conn.createStatement().execute(ddl);
-        assertImmutableRows(conn, dataTableFullName, true);
-    }
-    
-    @Test
-    public void testAlterImmutableRowsPropertyForOneCellPerColumnFamilyStorageScheme() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
-                +"ID VARCHAR(15) NOT NULL,\n"
-                +"CREATED_DATE DATE,\n"
-                +"CREATION_TIME BIGINT,\n"
-                +"CONSTRAINT PK PRIMARY KEY (ID)) " + generateDDLOptions("COLUMN_ENCODED_BYTES=4, IMMUTABLE_ROWS=true"
-                + (!columnEncoded ? ",IMMUTABLE_STORAGE_SCHEME=" + PTable.ImmutableStorageScheme.ONE_CELL_PER_COLUMN : ""));
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        conn.createStatement().execute(ddl);
-        assertImmutableRows(conn, dataTableFullName, true);
-        try {
-	        ddl = "ALTER TABLE " + dataTableFullName + " SET IMMUTABLE_ROWS = false";
-	        conn.createStatement().execute(ddl);
-	        if (columnEncoded) {
-	            fail();
-	        }
-        }
-        catch(SQLException e) {
-        	assertEquals(SQLExceptionCode.CANNOT_ALTER_IMMUTABLE_ROWS_PROPERTY.getErrorCode(), e.getErrorCode());
-        }
-        assertImmutableRows(conn, dataTableFullName, columnEncoded);
-    }
-    
 }
  


[24/25] phoenix git commit: PHOENIX-4250 Breakup AlterTableIT into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/eafb58be/phoenix-core/src/it/java/org/apache/phoenix/end2end/SetPropertyIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SetPropertyIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SetPropertyIT.java
new file mode 100644
index 0000000..7a7576d
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SetPropertyIT.java
@@ -0,0 +1,1022 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end;
+
+import static org.apache.hadoop.hbase.HColumnDescriptor.DEFAULT_REPLICATION_SCOPE;
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.KeepDeletedCells;
+import org.apache.hadoop.hbase.client.HBaseAdmin;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.phoenix.exception.SQLExceptionCode;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.schema.PTable;
+import org.apache.phoenix.schema.PTableKey;
+import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.SchemaUtil;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public abstract class SetPropertyIT extends ParallelStatsDisabledIT {
+    private String schemaName;
+    private String dataTableName;
+    private String viewName;
+    private String dataTableFullName;
+    private String tableDDLOptions;
+    private final boolean columnEncoded;
+
+    public SetPropertyIT(boolean columnEncoded) {
+        this.columnEncoded = columnEncoded;
+        this.tableDDLOptions = columnEncoded ? "" : "COLUMN_ENCODED_BYTES=0";
+    }
+    
+    @Before
+    public void setupTableNames() throws Exception {
+        schemaName = "";
+        dataTableName = generateUniqueName();
+        dataTableFullName = SchemaUtil.getTableName(schemaName, dataTableName);
+        viewName = generateUniqueName();
+    }
+
+    private String generateDDLOptions(String options) {
+        StringBuilder sb = new StringBuilder();
+        if (!options.isEmpty()) {
+            sb.append(options);
+        }
+        if (!tableDDLOptions.isEmpty()) {
+            if (sb.length()!=0)
+                sb.append(",");
+            sb.append(tableDDLOptions);
+        }
+        return sb.toString();
+    }
+
+    @Test
+    public void testSetHColumnProperties() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
+                +"ID1 VARCHAR(15) NOT NULL,\n"
+                +"ID2 VARCHAR(15) NOT NULL,\n"
+                +"CREATED_DATE DATE,\n"
+                +"CREATION_TIME BIGINT,\n"
+                +"LAST_USED DATE,\n"
+                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("SALT_BUCKETS = 8");
+        Connection conn1 = DriverManager.getConnection(getUrl(), props);
+        conn1.createStatement().execute(ddl);
+        ddl = "ALTER TABLE " + dataTableFullName + " SET REPLICATION_SCOPE=1";
+        conn1.createStatement().execute(ddl);
+        try (HBaseAdmin admin = conn1.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+            HColumnDescriptor[] columnFamilies = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName))
+                    .getColumnFamilies();
+            assertEquals(1, columnFamilies.length);
+            assertEquals("0", columnFamilies[0].getNameAsString());
+            assertEquals(1, columnFamilies[0].getScope());
+        }
+    }
+
+    @Test
+    public void testSetHTableProperties() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
+                +"ID1 VARCHAR(15) NOT NULL,\n"
+                +"ID2 VARCHAR(15) NOT NULL,\n"
+                +"CREATED_DATE DATE,\n"
+                +"CREATION_TIME BIGINT,\n"
+                +"LAST_USED DATE,\n"
+                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("SALT_BUCKETS = 8");
+        Connection conn1 = DriverManager.getConnection(getUrl(), props);
+        conn1.createStatement().execute(ddl);
+        ddl = "ALTER TABLE " + dataTableFullName + " SET COMPACTION_ENABLED=FALSE";
+        conn1.createStatement().execute(ddl);
+        try (HBaseAdmin admin = conn1.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+            HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
+            assertEquals(1, tableDesc.getColumnFamilies().length);
+            assertEquals("0", tableDesc.getColumnFamilies()[0].getNameAsString());
+            assertEquals(Boolean.toString(false), tableDesc.getValue(HTableDescriptor.COMPACTION_ENABLED));
+        }
+    }
+
+    @Test
+    public void testSetHTableAndHColumnProperties() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
+                +"ID1 VARCHAR(15) NOT NULL,\n"
+                +"ID2 VARCHAR(15) NOT NULL,\n"
+                +"CREATED_DATE DATE,\n"
+                +"CREATION_TIME BIGINT,\n"
+                +"LAST_USED DATE,\n"
+                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("SALT_BUCKETS = 8");
+        Connection conn1 = DriverManager.getConnection(getUrl(), props);
+        conn1.createStatement().execute(ddl);
+        ddl = "ALTER TABLE " + dataTableFullName + " SET COMPACTION_ENABLED = FALSE, REPLICATION_SCOPE = 1";
+        conn1.createStatement().execute(ddl);
+        try (HBaseAdmin admin = conn1.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+            HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
+            HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
+            assertEquals(1, columnFamilies.length);
+            assertEquals("0", columnFamilies[0].getNameAsString());
+            assertEquals(1, columnFamilies[0].getScope());
+            assertEquals(false, tableDesc.isCompactionEnabled());
+        }
+    }
+
+    @Test
+    public void testSetHTableHColumnAndPhoenixTableProperties() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
+                +"ID1 VARCHAR(15) NOT NULL,\n"
+                +"ID2 VARCHAR(15) NOT NULL,\n"
+                +"CREATED_DATE DATE,\n"
+                +"CF1.CREATION_TIME BIGINT,\n"
+                +"CF2.LAST_USED DATE,\n"
+                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("IMMUTABLE_ROWS=true"
+                + (!columnEncoded ? ",IMMUTABLE_STORAGE_SCHEME=" + PTable.ImmutableStorageScheme.ONE_CELL_PER_COLUMN : ""));
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.createStatement().execute(ddl);
+        assertImmutableRows(conn, dataTableFullName, true);
+        ddl = "ALTER TABLE " + dataTableFullName + " SET COMPACTION_ENABLED = FALSE, VERSIONS = 10";
+        conn.createStatement().execute(ddl);
+        ddl = "ALTER TABLE " + dataTableFullName + " SET COMPACTION_ENABLED = FALSE, CF1.MIN_VERSIONS = 1, CF2.MIN_VERSIONS = 3, MIN_VERSIONS = 8, CF1.KEEP_DELETED_CELLS = true, KEEP_DELETED_CELLS = false";
+        conn.createStatement().execute(ddl);
+
+        try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+            HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
+            HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
+            assertEquals(3, columnFamilies.length);
+
+            assertEquals("0", columnFamilies[0].getNameAsString());
+            assertEquals(8, columnFamilies[0].getMinVersions());
+            assertEquals(10, columnFamilies[0].getMaxVersions());
+            assertEquals(KeepDeletedCells.FALSE, columnFamilies[0].getKeepDeletedCells());
+
+            assertEquals("CF1", columnFamilies[1].getNameAsString());
+            assertEquals(1, columnFamilies[1].getMinVersions());
+            assertEquals(10, columnFamilies[1].getMaxVersions());
+            assertEquals(KeepDeletedCells.TRUE, columnFamilies[1].getKeepDeletedCells());
+
+            assertEquals("CF2", columnFamilies[2].getNameAsString());
+            assertEquals(3, columnFamilies[2].getMinVersions());
+            assertEquals(10, columnFamilies[2].getMaxVersions());
+            assertEquals(KeepDeletedCells.FALSE, columnFamilies[2].getKeepDeletedCells());
+
+            assertEquals(Boolean.toString(false), tableDesc.getValue(HTableDescriptor.COMPACTION_ENABLED));
+        }
+    }
+
+    @Test
+    public void testSpecifyingColumnFamilyForHTablePropertyFails() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
+                +"ID1 VARCHAR(15) NOT NULL,\n"
+                +"ID2 VARCHAR(15) NOT NULL,\n"
+                +"CREATED_DATE DATE,\n"
+                +"CREATION_TIME BIGINT,\n"
+                +"LAST_USED DATE,\n"
+                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("SALT_BUCKETS = 8");
+        Connection conn1 = DriverManager.getConnection(getUrl(), props);
+        conn1.createStatement().execute(ddl);
+        ddl = "ALTER TABLE " + dataTableFullName + " SET CF.COMPACTION_ENABLED = FALSE";
+        try {
+            conn1.createStatement().execute(ddl);
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.COLUMN_FAMILY_NOT_ALLOWED_TABLE_PROPERTY.getErrorCode(), e.getErrorCode());
+        }
+    }
+
+    @Test
+    public void testSpecifyingColumnFamilyForPhoenixTablePropertyFails() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
+                +"ID1 VARCHAR(15) NOT NULL,\n"
+                +"ID2 VARCHAR(15) NOT NULL,\n"
+                +"CREATED_DATE DATE,\n"
+                +"CREATION_TIME BIGINT,\n"
+                +"LAST_USED DATE,\n"
+                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("SALT_BUCKETS = 8");
+        Connection conn1 = DriverManager.getConnection(getUrl(), props);
+        conn1.createStatement().execute(ddl);
+        ddl = "ALTER TABLE " + dataTableFullName + " SET CF.DISABLE_WAL = TRUE";
+        try {
+            conn1.createStatement().execute(ddl);
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.COLUMN_FAMILY_NOT_ALLOWED_TABLE_PROPERTY.getErrorCode(), e.getErrorCode());
+        }
+    }
+
+    @Test
+    public void testSpecifyingColumnFamilyForTTLFails() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
+                +"ID1 VARCHAR(15) NOT NULL,\n"
+                +"ID2 VARCHAR(15) NOT NULL,\n"
+                +"CREATED_DATE DATE,\n"
+                +"CREATION_TIME BIGINT,\n"
+                +"CF.LAST_USED DATE,\n"
+                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("SALT_BUCKETS = 8");
+        Connection conn1 = DriverManager.getConnection(getUrl(), props);
+        conn1.createStatement().execute(ddl);
+        ddl = "ALTER TABLE " + dataTableFullName + " SET CF.TTL = 86400";
+        try {
+            conn1.createStatement().execute(ddl);
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.COLUMN_FAMILY_NOT_ALLOWED_FOR_TTL.getErrorCode(), e.getErrorCode());
+        }
+    }
+
+    @Test
+    public void testSetPropertyNeedsColumnFamilyToExist() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
+                +"ID1 VARCHAR(15) NOT NULL,\n"
+                +"ID2 VARCHAR(15) NOT NULL,\n"
+                +"CREATED_DATE DATE,\n"
+                +"CREATION_TIME BIGINT,\n"
+                +"LAST_USED DATE,\n"
+                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("SALT_BUCKETS = 8");
+        Connection conn1 = DriverManager.getConnection(getUrl(), props);
+        conn1.createStatement().execute(ddl);
+        ddl = "ALTER TABLE " + dataTableFullName + " SET CF.REPLICATION_SCOPE = 1";
+        try {
+            conn1.createStatement().execute(ddl);
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.COLUMN_FAMILY_NOT_FOUND.getErrorCode(), e.getErrorCode());
+        }
+    }
+
+    @Test
+    public void testSetDefaultColumnFamilyNotAllowed() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
+                +"ID1 VARCHAR(15) NOT NULL,\n"
+                +"ID2 VARCHAR(15) NOT NULL,\n"
+                +"CREATED_DATE DATE,\n"
+                +"CREATION_TIME BIGINT,\n"
+                +"LAST_USED DATE,\n"
+                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions(" SALT_BUCKETS = 8");
+        Connection conn1 = DriverManager.getConnection(getUrl(), props);
+        conn1.createStatement().execute(ddl);
+        ddl = "ALTER TABLE " + dataTableFullName + " SET DEFAULT_COLUMN_FAMILY = 'A'";
+        try {
+            conn1.createStatement().execute(ddl);
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.DEFAULT_COLUMN_FAMILY_ONLY_ON_CREATE_TABLE.getErrorCode(), e.getErrorCode());
+        }
+    }
+
+    @Test
+    public void testSetHColumnOrHTablePropertiesOnViewsNotAllowed() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
+                +"ID1 VARCHAR(15) NOT NULL,\n"
+                +"ID2 VARCHAR(15) NOT NULL,\n"
+                +"CREATED_DATE DATE,\n"
+                +"CREATION_TIME BIGINT,\n"
+                +"LAST_USED DATE,\n"
+                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("SALT_BUCKETS = 8");
+        Connection conn1 = DriverManager.getConnection(getUrl(), props);
+        conn1.createStatement().execute(ddl);
+        ddl = "CREATE VIEW " + viewName + "  AS SELECT * FROM " + dataTableFullName + " WHERE CREATION_TIME = 1";
+        conn1.createStatement().execute(ddl);
+        ddl = "ALTER VIEW " + viewName + " SET REPLICATION_SCOPE = 1";
+        try {
+            conn1.createStatement().execute(ddl);
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.VIEW_WITH_PROPERTIES.getErrorCode(), e.getErrorCode());
+        }
+        ddl = "ALTER VIEW " + viewName + " SET COMPACTION_ENABLED = FALSE";
+        try {
+            conn1.createStatement().execute(ddl);
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.VIEW_WITH_PROPERTIES.getErrorCode(), e.getErrorCode());
+        }
+    }
+
+    @Test
+    public void testSetForSomePhoenixTablePropertiesOnViewsAllowed() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
+                +"ID1 VARCHAR(15) NOT NULL,\n"
+                +"ID2 VARCHAR(15) NOT NULL,\n"
+                +"CREATED_DATE DATE,\n"
+                +"CREATION_TIME BIGINT,\n"
+                +"LAST_USED DATE,\n"
+                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("SALT_BUCKETS = 8");
+        Connection conn1 = DriverManager.getConnection(getUrl(), props);
+        conn1.createStatement().execute(ddl);
+        String viewFullName = SchemaUtil.getTableName(schemaName, generateUniqueName());
+        ddl = "CREATE VIEW " + viewFullName + " AS SELECT * FROM " + dataTableFullName + " WHERE CREATION_TIME = 1";
+        conn1.createStatement().execute(ddl);
+        ddl = "ALTER VIEW " + viewFullName + " SET UPDATE_CACHE_FREQUENCY = 10";
+        conn1.createStatement().execute(ddl);
+        conn1.createStatement().execute("SELECT * FROM " + viewFullName);
+        PhoenixConnection pconn = conn1.unwrap(PhoenixConnection.class);
+        assertEquals(10, pconn.getTable(new PTableKey(pconn.getTenantId(), viewFullName)).getUpdateCacheFrequency());
+        ddl = "ALTER VIEW " + viewFullName + " SET UPDATE_CACHE_FREQUENCY = 20";
+        conn1.createStatement().execute(ddl);
+        conn1.createStatement().execute("SELECT * FROM " + viewFullName);
+        pconn = conn1.unwrap(PhoenixConnection.class);
+        assertEquals(20, pconn.getTable(new PTableKey(pconn.getTenantId(), viewFullName)).getUpdateCacheFrequency());
+        assertImmutableRows(conn1, viewFullName, false);
+        ddl = "ALTER VIEW " + viewFullName + " SET DISABLE_WAL = TRUE";
+        try {
+            conn1.createStatement().execute(ddl);
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.VIEW_WITH_PROPERTIES.getErrorCode(), e.getErrorCode());
+        }
+        ddl = "ALTER VIEW " + viewFullName + " SET THROW_INDEX_WRITE_FAILURE = FALSE";
+        try {
+            conn1.createStatement().execute(ddl);
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.VIEW_WITH_PROPERTIES.getErrorCode(), e.getErrorCode());
+        }
+    }
+
+    @Test
+    public void testSettingPropertiesWhenTableHasDefaultColFamilySpecified() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String ddl = "CREATE TABLE  " + dataTableFullName + " (\n"
+                +"ID1 VARCHAR(15) NOT NULL,\n"
+                +"ID2 VARCHAR(15) NOT NULL,\n"
+                +"CREATED_DATE DATE,\n"
+                +"CREATION_TIME BIGINT,\n"
+                +"CF.LAST_USED DATE,\n"
+                +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) " + generateDDLOptions("IMMUTABLE_ROWS=true, DEFAULT_COLUMN_FAMILY = 'XYZ'"
+                + (!columnEncoded ? ",IMMUTABLE_STORAGE_SCHEME=" + PTable.ImmutableStorageScheme.ONE_CELL_PER_COLUMN : ""));
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.createStatement().execute(ddl);
+        assertImmutableRows(conn, dataTableFullName, true);
+        ddl = "ALTER TABLE  " + dataTableFullName
+                + " SET COMPACTION_ENABLED = FALSE, CF.REPLICATION_SCOPE=1, IMMUTABLE_ROWS = TRUE, TTL=1000";
+        conn.createStatement().execute(ddl);
+        assertImmutableRows(conn, dataTableFullName, true);
+        try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+            HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
+            HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
+            assertEquals(2, columnFamilies.length);
+            assertEquals("CF", columnFamilies[0].getNameAsString());
+            assertEquals(1, columnFamilies[0].getScope());
+            assertEquals(1000, columnFamilies[0].getTimeToLive());
+            assertEquals("XYZ", columnFamilies[1].getNameAsString());
+            assertEquals(DEFAULT_REPLICATION_SCOPE, columnFamilies[1].getScope());
+            assertEquals(1000, columnFamilies[1].getTimeToLive());
+            assertEquals(Boolean.toString(false), tableDesc.getValue(HTableDescriptor.COMPACTION_ENABLED));
+        }
+    }
+
+    private static void assertImmutableRows(Connection conn, String fullTableName, boolean expectedValue) throws SQLException {
+        PhoenixConnection pconn = conn.unwrap(PhoenixConnection.class);
+        assertEquals(expectedValue, pconn.getTable(new PTableKey(pconn.getTenantId(), fullTableName)).isImmutableRows());
+    }
+
+    @Test
+    public void testSetPropertyAndAddColumnForExistingColumnFamily() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String ddl = "CREATE TABLE " + dataTableFullName
+                +
+                "  (a_string varchar not null, col1 integer, CF.col2 integer" +
+                "  CONSTRAINT pk PRIMARY KEY (a_string)) " + tableDDLOptions;
+        try {
+            conn.createStatement().execute(ddl);
+            conn.createStatement().execute(
+                    "ALTER TABLE " + dataTableFullName + " ADD CF.col3 integer CF.IN_MEMORY=true");
+            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+                HColumnDescriptor[] columnFamilies = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName))
+                        .getColumnFamilies();
+                assertEquals(2, columnFamilies.length);
+                assertEquals("0", columnFamilies[0].getNameAsString());
+                assertFalse(columnFamilies[0].isInMemory());
+                assertEquals("CF", columnFamilies[1].getNameAsString());
+                assertTrue(columnFamilies[1].isInMemory());
+            }
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSetPropertyAndAddColumnForNewAndExistingColumnFamily() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String ddl = "CREATE TABLE " + dataTableFullName + " "
+                +
+                "  (a_string varchar not null, col1 integer, CF1.col2 integer" +
+                "  CONSTRAINT pk PRIMARY KEY (a_string)) " + tableDDLOptions;
+        try {
+            conn.createStatement().execute(ddl);
+            conn.createStatement()
+                    .execute(
+                            "ALTER TABLE "
+                                    + dataTableFullName
+                                    + " ADD col4 integer, CF1.col5 integer, CF2.col6 integer IN_MEMORY=true, CF1.REPLICATION_SCOPE=1, CF2.IN_MEMORY=false ");
+            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+                HColumnDescriptor[] columnFamilies = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName))
+                        .getColumnFamilies();
+                assertEquals(3, columnFamilies.length);
+                assertEquals("0", columnFamilies[0].getNameAsString());
+                assertTrue(columnFamilies[0].isInMemory());
+                assertEquals(0, columnFamilies[0].getScope());
+                assertEquals("CF1", columnFamilies[1].getNameAsString());
+                assertTrue(columnFamilies[1].isInMemory());
+                assertEquals(1, columnFamilies[1].getScope());
+                assertEquals("CF2", columnFamilies[2].getNameAsString());
+                assertFalse(columnFamilies[2].isInMemory());
+                assertEquals(0, columnFamilies[2].getScope());
+            }
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSetPropertyAndAddColumnWhenTableHasExplicitDefaultColumnFamily() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String ddl = "CREATE TABLE " + dataTableFullName + " "
+                +
+                "  (a_string varchar not null, col1 integer, CF1.col2 integer" +
+                "  CONSTRAINT pk PRIMARY KEY (a_string)) " + generateDDLOptions("DEFAULT_COLUMN_FAMILY = 'XYZ'");
+        try {
+            conn.createStatement().execute(ddl);
+            conn.createStatement()
+                    .execute(
+                            "ALTER TABLE "
+                                    + dataTableFullName
+                                    + " ADD col4 integer, CF1.col5 integer, CF2.col6 integer IN_MEMORY=true, CF1.REPLICATION_SCOPE=1, CF2.IN_MEMORY=false, XYZ.REPLICATION_SCOPE=1 ");
+            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+                HColumnDescriptor[] columnFamilies = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName))
+                        .getColumnFamilies();
+                assertEquals(3, columnFamilies.length);
+                assertEquals("CF1", columnFamilies[0].getNameAsString());
+                assertTrue(columnFamilies[0].isInMemory());
+                assertEquals(1, columnFamilies[0].getScope());
+                assertEquals("CF2", columnFamilies[1].getNameAsString());
+                assertFalse(columnFamilies[1].isInMemory());
+                assertEquals(0, columnFamilies[1].getScope());
+                assertEquals("XYZ", columnFamilies[2].getNameAsString());
+                assertTrue(columnFamilies[2].isInMemory());
+                assertEquals(1, columnFamilies[2].getScope());
+            }
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSetPropertyAndAddColumnFailsForColumnFamilyNotPresentInAddCol() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String ddl = "CREATE TABLE " + dataTableFullName + " "
+                +
+                "  (a_string varchar not null, col1 integer, CF1.col2 integer" +
+                "  CONSTRAINT pk PRIMARY KEY (a_string)) "+ generateDDLOptions("DEFAULT_COLUMN_FAMILY = 'XYZ'");
+        try {
+            conn.createStatement().execute(ddl);
+            try {
+                conn.createStatement().execute(
+                        "ALTER TABLE " + dataTableFullName
+                                + " ADD col4 integer CF1.REPLICATION_SCOPE=1, XYZ.IN_MEMORY=true ");
+                fail();
+            } catch(SQLException e) {
+                assertEquals(SQLExceptionCode.CANNOT_SET_PROPERTY_FOR_COLUMN_NOT_ADDED.getErrorCode(), e.getErrorCode());
+            }
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSetPropertyAndAddColumnForDifferentColumnFamilies() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String ddl = "CREATE TABLE " + dataTableFullName
+                +
+                "  (a_string varchar not null, col1 integer, CF1.col2 integer, CF2.col3 integer" +
+                "  CONSTRAINT pk PRIMARY KEY (a_string)) " + generateDDLOptions("DEFAULT_COLUMN_FAMILY = 'XYZ' ");
+        try {
+            conn.createStatement().execute(ddl);
+            conn.createStatement()
+                    .execute(
+                            "ALTER TABLE "
+                                    + dataTableFullName
+                                    + " ADD col4 integer, CF1.col5 integer, CF2.col6 integer, CF3.col7 integer CF1.REPLICATION_SCOPE=1, CF1.IN_MEMORY=false, IN_MEMORY=true ");
+            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+                HColumnDescriptor[] columnFamilies = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName))
+                        .getColumnFamilies();
+                assertEquals(4, columnFamilies.length);
+                assertEquals("CF1", columnFamilies[0].getNameAsString());
+                assertFalse(columnFamilies[0].isInMemory());
+                assertEquals(1, columnFamilies[0].getScope());
+                assertEquals("CF2", columnFamilies[1].getNameAsString());
+                assertTrue(columnFamilies[1].isInMemory());
+                assertEquals(0, columnFamilies[1].getScope());
+                assertEquals("CF3", columnFamilies[2].getNameAsString());
+                assertTrue(columnFamilies[2].isInMemory());
+                assertEquals(0, columnFamilies[2].getScope());
+                assertEquals("XYZ", columnFamilies[3].getNameAsString());
+                assertTrue(columnFamilies[3].isInMemory());
+                assertEquals(0, columnFamilies[3].getScope());
+            }
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSetPropertyAndAddColumnUsingDefaultColumnFamilySpecifier() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String ddl = "CREATE TABLE " + dataTableFullName
+                +
+                "  (a_string varchar not null, col1 integer, CF1.col2 integer" +
+                "  CONSTRAINT pk PRIMARY KEY (a_string)) " + generateDDLOptions("DEFAULT_COLUMN_FAMILY = 'XYZ'");
+        try {
+            conn.createStatement().execute(ddl);
+            conn.createStatement().execute(
+                    "ALTER TABLE " + dataTableFullName + " ADD col4 integer XYZ.REPLICATION_SCOPE=1 ");
+            conn.createStatement()
+                    .execute("ALTER TABLE " + dataTableFullName + " ADD XYZ.col5 integer IN_MEMORY=true ");
+            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+                HColumnDescriptor[] columnFamilies = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName))
+                        .getColumnFamilies();
+                assertEquals(2, columnFamilies.length);
+                assertEquals("CF1", columnFamilies[0].getNameAsString());
+                assertFalse(columnFamilies[0].isInMemory());
+                assertEquals(0, columnFamilies[0].getScope());
+                assertEquals("XYZ", columnFamilies[1].getNameAsString());
+                assertTrue(columnFamilies[1].isInMemory());
+                assertEquals(1, columnFamilies[1].getScope());
+            }
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSetPropertyAndAddColumnForDefaultColumnFamily() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+        String ddl = "CREATE TABLE " + dataTableFullName +
+                "  (a_string varchar not null, col1 integer" +
+                "  CONSTRAINT pk PRIMARY KEY (a_string)) " + tableDDLOptions;
+        try {
+            conn.createStatement().execute(ddl);
+            conn.createStatement().execute("ALTER TABLE " + dataTableFullName + " ADD col2 integer IN_MEMORY=true");
+            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+                HColumnDescriptor[] columnFamilies = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName))
+                        .getColumnFamilies();
+                assertEquals(1, columnFamilies.length);
+                assertEquals("0", columnFamilies[0].getNameAsString());
+                assertTrue(columnFamilies[0].isInMemory());
+            }
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testAddNewColumnFamilyProperties() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+
+        try {
+            conn.createStatement()
+            .execute(
+                            "CREATE TABLE "
+                                    + dataTableFullName
+                            + "  (a_string varchar not null, col1 integer, cf1.col2 integer, col3 integer , cf2.col4 integer "
+                            + "  CONSTRAINT pk PRIMARY KEY (a_string)) " + generateDDLOptions("immutable_rows=true , SALT_BUCKETS=3 "
+                            + (!columnEncoded ? ",IMMUTABLE_STORAGE_SCHEME=" + PTable.ImmutableStorageScheme.ONE_CELL_PER_COLUMN : ""))); 
+
+            String ddl = "Alter table " + dataTableFullName + " add cf3.col5 integer, cf4.col6 integer in_memory=true";
+            conn.createStatement().execute(ddl);
+
+            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
+                assertTrue(tableDesc.isCompactionEnabled());
+                HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
+                assertEquals(5, columnFamilies.length);
+                assertEquals("0", columnFamilies[0].getNameAsString());
+                assertFalse(columnFamilies[0].isInMemory());
+                assertEquals("CF1", columnFamilies[1].getNameAsString());
+                assertFalse(columnFamilies[1].isInMemory());
+                assertEquals("CF2", columnFamilies[2].getNameAsString());
+                assertFalse(columnFamilies[2].isInMemory());
+                assertEquals("CF3", columnFamilies[3].getNameAsString());
+                assertTrue(columnFamilies[3].isInMemory());
+                assertEquals("CF4", columnFamilies[4].getNameAsString());
+                assertTrue(columnFamilies[4].isInMemory());
+            }
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testAddProperyToExistingColumnFamily() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+
+        try {
+            conn.createStatement()
+            .execute(
+                            "CREATE TABLE "
+                                    + dataTableFullName
+                            + "  (a_string varchar not null, col1 integer, cf1.col2 integer, col3 integer , cf2.col4 integer "
+                            + "  CONSTRAINT pk PRIMARY KEY (a_string)) " + generateDDLOptions("immutable_rows=true , SALT_BUCKETS=3 "
+                            + (!columnEncoded ? ",IMMUTABLE_STORAGE_SCHEME=" + PTable.ImmutableStorageScheme.ONE_CELL_PER_COLUMN : "")));    
+
+            String ddl = "Alter table " + dataTableFullName + " add cf1.col5 integer in_memory=true";
+            conn.createStatement().execute(ddl);
+
+            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
+                assertTrue(tableDesc.isCompactionEnabled());
+                HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
+                assertEquals(3, columnFamilies.length);
+                assertEquals("0", columnFamilies[0].getNameAsString());
+                assertFalse(columnFamilies[0].isInMemory());
+                assertEquals("CF1", columnFamilies[1].getNameAsString());
+                assertTrue(columnFamilies[1].isInMemory());
+                assertEquals("CF2", columnFamilies[2].getNameAsString());
+                assertFalse(columnFamilies[2].isInMemory());
+            }
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testAddTTLToExistingColumnFamily() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+
+        try {
+            String ddl = "CREATE TABLE " + dataTableFullName
+                    + " (pk char(2) not null primary key, col1 integer, b.col1 integer) " + tableDDLOptions + " SPLIT ON ('EA','EZ') ";
+            conn.createStatement().execute(ddl);
+            ddl = "ALTER TABLE " + dataTableFullName + " add b.col2 varchar ttl=30";
+            conn.createStatement().execute(ddl);
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.CANNOT_SET_TABLE_PROPERTY_ADD_COLUMN.getErrorCode(), e.getErrorCode());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSettingTTLWhenAddingColumnNotAllowed() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+
+        try {
+            String ddl = "CREATE TABLE " + dataTableFullName
+                    + " (pk char(2) not null primary key) " + generateDDLOptions("TTL=100") + " SPLIT ON ('EA','EZ')";
+            conn.createStatement().execute(ddl);
+            ddl = "ALTER TABLE " + dataTableFullName + " add col1 varchar ttl=30";
+            conn.createStatement().execute(ddl);
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.CANNOT_SET_TABLE_PROPERTY_ADD_COLUMN.getErrorCode(), e.getErrorCode());
+        }
+        try {
+            String ddl = "ALTER TABLE " + dataTableFullName + " add col1 varchar a.ttl=30";
+            conn.createStatement().execute(ddl);
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.COLUMN_FAMILY_NOT_ALLOWED_FOR_TTL.getErrorCode(), e.getErrorCode());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSetTTLForTableWithOnlyPKCols() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+        try {
+            String ddl = "create table " + dataTableFullName + " ("
+                    + " id char(1) NOT NULL,"
+                    + " col1 integer NOT NULL,"
+                    + " col2 bigint NOT NULL,"
+                    + " CONSTRAINT NAME_PK PRIMARY KEY (id, col1, col2)"
+                    + " ) " + generateDDLOptions("TTL=86400, SALT_BUCKETS = 4, DEFAULT_COLUMN_FAMILY='XYZ'");
+            conn.createStatement().execute(ddl);
+            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
+                HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
+                assertEquals(1, columnFamilies.length);
+                assertEquals("XYZ", columnFamilies[0].getNameAsString());
+                assertEquals(86400, columnFamilies[0].getTimeToLive());
+            }
+            ddl = "ALTER TABLE " + dataTableFullName + " SET TTL=30";
+            conn.createStatement().execute(ddl);
+            conn.commit();
+            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
+                HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
+                assertEquals(1, columnFamilies.length);
+                assertEquals(30, columnFamilies[0].getTimeToLive());
+                assertEquals("XYZ", columnFamilies[0].getNameAsString());
+            }
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSetHColumnPropertyForTableWithOnlyPKCols1() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+        try {
+            String ddl = "create table " + dataTableFullName + " ("
+                    + " id char(1) NOT NULL,"
+                    + " col1 integer NOT NULL,"
+                    + " col2 bigint NOT NULL,"
+                    + " CONSTRAINT NAME_PK PRIMARY KEY (id, col1, col2)"
+                    + " ) " + generateDDLOptions("TTL=86400, SALT_BUCKETS = 4, DEFAULT_COLUMN_FAMILY='XYZ'");
+            conn.createStatement().execute(ddl);
+            ddl = "ALTER TABLE " + dataTableFullName + " SET IN_MEMORY=true";
+            conn.createStatement().execute(ddl);
+            conn.commit();
+            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
+                HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
+                assertEquals(1, columnFamilies.length);
+                assertEquals(true, columnFamilies[0].isInMemory());
+                assertEquals("XYZ", columnFamilies[0].getNameAsString());
+            }
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSetHColumnPropertyForTableWithOnlyPKCols2() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+        try {
+            String ddl = "create table " + dataTableFullName + " ("
+                    + " id char(1) NOT NULL,"
+                    + " col1 integer NOT NULL,"
+                    + " col2 bigint NOT NULL,"
+                    + " CONSTRAINT NAME_PK PRIMARY KEY (id, col1, col2)"
+                    + " ) " + generateDDLOptions("TTL=86400, SALT_BUCKETS = 4");
+            conn.createStatement().execute(ddl);
+            ddl = "ALTER TABLE " + dataTableFullName + " SET IN_MEMORY=true";
+            conn.createStatement().execute(ddl);
+            conn.commit();
+            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
+                HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
+                assertEquals(1, columnFamilies.length);
+                assertEquals(true, columnFamilies[0].isInMemory());
+                assertEquals("0", columnFamilies[0].getNameAsString());
+            }
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSetHColumnPropertyAndAddColumnForDefaultCFForTableWithOnlyPKCols() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+        try {
+            String ddl = "create table " + dataTableFullName + " ("
+                    + " id char(1) NOT NULL,"
+                    + " col1 integer NOT NULL,"
+                    + " col2 bigint NOT NULL,"
+                    + " CONSTRAINT NAME_PK PRIMARY KEY (id, col1, col2)"
+                    + " ) " + generateDDLOptions("TTL=86400, SALT_BUCKETS = 4, DEFAULT_COLUMN_FAMILY='XYZ'");
+            conn.createStatement().execute(ddl);
+            ddl = "ALTER TABLE " + dataTableFullName + " ADD COL3 INTEGER IN_MEMORY=true";
+            conn.createStatement().execute(ddl);
+            conn.commit();
+            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
+                HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
+                assertEquals(1, columnFamilies.length);
+                assertEquals(true, columnFamilies[0].isInMemory());
+                assertEquals("XYZ", columnFamilies[0].getNameAsString());
+            }
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSetHColumnPropertyAndAddColumnForNewCFForTableWithOnlyPKCols() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+        try {
+            String ddl = "create table " + dataTableFullName + " ("
+                    + " id char(1) NOT NULL,"
+                    + " col1 integer NOT NULL,"
+                    + " col2 bigint NOT NULL,"
+                    + " CONSTRAINT NAME_PK PRIMARY KEY (id, col1, col2)"
+                    + " ) " + generateDDLOptions("TTL=86400, SALT_BUCKETS = 4, DEFAULT_COLUMN_FAMILY='XYZ'");
+            conn.createStatement().execute(ddl);
+            ddl = "ALTER TABLE " + dataTableFullName + " ADD NEWCF.COL3 INTEGER IN_MEMORY=true";
+            conn.createStatement().execute(ddl);
+            conn.commit();
+            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
+                HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
+                assertEquals(2, columnFamilies.length);
+                assertEquals("NEWCF", columnFamilies[0].getNameAsString());
+                assertEquals(true, columnFamilies[0].isInMemory());
+                assertEquals("XYZ", columnFamilies[1].getNameAsString());
+                assertEquals(false, columnFamilies[1].isInMemory());
+            }
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testTTLAssignmentForNewEmptyCF() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+        try {
+            String ddl = "create table " + dataTableFullName + " ("
+                    + " id char(1) NOT NULL,"
+                    + " col1 integer NOT NULL,"
+                    + " col2 bigint NOT NULL,"
+                    + " CONSTRAINT NAME_PK PRIMARY KEY (id, col1, col2)"
+                    + " ) " + generateDDLOptions("TTL=86400, SALT_BUCKETS = 4, DEFAULT_COLUMN_FAMILY='XYZ'");
+            conn.createStatement().execute(ddl);
+            ddl = "ALTER TABLE " + dataTableFullName + " ADD NEWCF.COL3 INTEGER IN_MEMORY=true";
+            conn.createStatement().execute(ddl);
+            conn.commit();
+            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
+                HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
+                assertEquals(2, columnFamilies.length);
+                assertEquals("NEWCF", columnFamilies[0].getNameAsString());
+                assertEquals(true, columnFamilies[0].isInMemory());
+                assertEquals(86400, columnFamilies[0].getTimeToLive());
+                assertEquals("XYZ", columnFamilies[1].getNameAsString());
+                assertEquals(false, columnFamilies[1].isInMemory());
+                assertEquals(86400, columnFamilies[1].getTimeToLive());
+            }
+
+            ddl = "ALTER TABLE " + dataTableFullName + " SET TTL=1000";
+            conn.createStatement().execute(ddl);
+            conn.commit();
+            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
+                HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
+                assertEquals(2, columnFamilies.length);
+                assertEquals("NEWCF", columnFamilies[0].getNameAsString());
+                assertEquals(true, columnFamilies[0].isInMemory());
+                assertEquals(1000, columnFamilies[0].getTimeToLive());
+                assertEquals("XYZ", columnFamilies[1].getNameAsString());
+                assertEquals(false, columnFamilies[1].isInMemory());
+                assertEquals(86400, columnFamilies[1].getTimeToLive());
+            }
+
+            // the new column will be assigned to the column family XYZ. With the a KV column getting added for XYZ,
+            // the column family will start showing up in PTable.getColumnFamilies() after the column is added. Thus
+            // being a new column family for the PTable, it will end up inheriting the TTL of the emptyCF (NEWCF).
+            ddl = "ALTER TABLE " + dataTableFullName + " ADD COL3 INTEGER";
+            conn.createStatement().execute(ddl);
+            conn.commit();
+            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
+                HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
+                assertEquals(2, columnFamilies.length);
+                assertEquals("NEWCF", columnFamilies[0].getNameAsString());
+                assertEquals(true, columnFamilies[0].isInMemory());
+                assertEquals(1000, columnFamilies[0].getTimeToLive());
+                assertEquals("XYZ", columnFamilies[1].getNameAsString());
+                assertEquals(false, columnFamilies[1].isInMemory());
+                assertEquals(1000, columnFamilies[1].getTimeToLive());
+            }
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSettingNotHColumnNorPhoenixPropertyEndsUpAsHTableProperty() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            String ddl = "create table " + dataTableFullName + " ("
+                    + " id char(1) NOT NULL,"
+                    + " col1 integer NOT NULL,"
+                    + " col2 bigint NOT NULL,"
+                    + " CONSTRAINT NAME_PK PRIMARY KEY (id, col1, col2)"
+                    + " ) " +  tableDDLOptions;
+            conn.createStatement().execute(ddl);
+            ddl = "ALTER TABLE " + dataTableFullName + " ADD NEWCF.COL3 INTEGER NEWCF.UNKNOWN_PROP='ABC'";
+            try {
+                conn.createStatement().execute(ddl);
+                fail();
+            } catch (SQLException e) {
+                assertEquals(SQLExceptionCode.CANNOT_SET_TABLE_PROPERTY_ADD_COLUMN.getErrorCode(), e.getErrorCode());
+            }
+            ddl = "ALTER TABLE " + dataTableFullName + " SET UNKNOWN_PROP='ABC'";
+            conn.createStatement().execute(ddl);
+            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(dataTableFullName));
+                assertEquals("ABC", tableDesc.getValue("UNKNOWN_PROP"));
+            }
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testAlterImmutableRowsPropertyForOneCellPerKeyValueColumnStorageScheme() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
+                +"ID VARCHAR(15) NOT NULL,\n"
+                +"CREATED_DATE DATE,\n"
+                +"CREATION_TIME BIGINT,\n"
+                +"CONSTRAINT PK PRIMARY KEY (ID)) " + tableDDLOptions;
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.createStatement().execute(ddl);
+        assertImmutableRows(conn, dataTableFullName, false);
+        ddl = "ALTER TABLE " + dataTableFullName + " SET IMMUTABLE_ROWS = true";
+        conn.createStatement().execute(ddl);
+        assertImmutableRows(conn, dataTableFullName, true);
+    }
+    
+    @Test
+    public void testAlterImmutableRowsPropertyForOneCellPerColumnFamilyStorageScheme() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String ddl = "CREATE TABLE " + dataTableFullName + " (\n"
+                +"ID VARCHAR(15) NOT NULL,\n"
+                +"CREATED_DATE DATE,\n"
+                +"CREATION_TIME BIGINT,\n"
+                +"CONSTRAINT PK PRIMARY KEY (ID)) " + generateDDLOptions("COLUMN_ENCODED_BYTES=4, IMMUTABLE_ROWS=true"
+                + (!columnEncoded ? ",IMMUTABLE_STORAGE_SCHEME=" + PTable.ImmutableStorageScheme.ONE_CELL_PER_COLUMN : ""));
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.createStatement().execute(ddl);
+        assertImmutableRows(conn, dataTableFullName, true);
+        try {
+            ddl = "ALTER TABLE " + dataTableFullName + " SET IMMUTABLE_ROWS = false";
+            conn.createStatement().execute(ddl);
+            if (columnEncoded) {
+                fail();
+            }
+        }
+        catch(SQLException e) {
+            assertEquals(SQLExceptionCode.CANNOT_ALTER_IMMUTABLE_ROWS_PROPERTY.getErrorCode(), e.getErrorCode());
+        }
+        assertImmutableRows(conn, dataTableFullName, columnEncoded);
+    }
+       
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/eafb58be/phoenix-core/src/it/java/org/apache/phoenix/end2end/SetPropertyOnEncodedTableIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SetPropertyOnEncodedTableIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SetPropertyOnEncodedTableIT.java
new file mode 100644
index 0000000..77b9b79
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SetPropertyOnEncodedTableIT.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.junit.runners.Parameterized.Parameters;
+
+public class SetPropertyOnEncodedTableIT extends SetPropertyIT {
+    public SetPropertyOnEncodedTableIT(boolean columnEncoded) {
+        super(columnEncoded);
+    }
+
+    @Parameters(name="SetPropertyOnEncodedTableIT") // name is used by failsafe as file name in reports
+    public static Collection<Boolean> data() {
+        return Arrays.asList( true);
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/eafb58be/phoenix-core/src/it/java/org/apache/phoenix/end2end/SetPropertyOnNonEncodedTableIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SetPropertyOnNonEncodedTableIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SetPropertyOnNonEncodedTableIT.java
new file mode 100644
index 0000000..26626d5
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SetPropertyOnNonEncodedTableIT.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.junit.runners.Parameterized.Parameters;
+
+public class SetPropertyOnNonEncodedTableIT extends SetPropertyIT {
+    public SetPropertyOnNonEncodedTableIT(boolean columnEncoded) {
+        super(columnEncoded);
+    }
+
+    @Parameters(name="SetPropertyOnNonEncodedTableIT") // name is used by failsafe as file name in reports
+    public static Collection<Boolean> data() {
+        return Arrays.asList( false );
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/eafb58be/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexWithTableSchemaChangeIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexWithTableSchemaChangeIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexWithTableSchemaChangeIT.java
index 659866b..61ec030 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexWithTableSchemaChangeIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexWithTableSchemaChangeIT.java
@@ -31,13 +31,27 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.Properties;
 
+import org.apache.hadoop.hbase.client.HTable;
+import org.apache.hadoop.hbase.client.Result;
+import org.apache.hadoop.hbase.client.ResultScanner;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.phoenix.end2end.ParallelStatsDisabledIT;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.schema.PTable;
 import org.apache.phoenix.util.IndexUtil;
 import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.SchemaUtil;
 import org.junit.Test;
 
 public class IndexWithTableSchemaChangeIT extends ParallelStatsDisabledIT {
 
+    private void assertIndexExists(Connection conn, boolean exists, String schemaName, String dataTableName) throws SQLException {
+        ResultSet rs = conn.getMetaData().getIndexInfo(null, schemaName, dataTableName, false, false);
+        assertEquals(exists, rs.next());
+    }
+
     @Test
     public void testImmutableIndexDropIndexedColumn() throws Exception {
         helpTestDropIndexedColumn(false, false);
@@ -372,4 +386,308 @@ public class IndexWithTableSchemaChangeIT extends ParallelStatsDisabledIT {
         	conn.close();
         }
     }
+    
+    @Test
+    public void testDropIndexedColumnImmutableIndex() throws Exception {
+        helpTestDropIndexedEncodedColumn(true, false);
+    }
+    
+    @Test
+    public void testDropIndexedColumnMutableIndex() throws Exception {
+        helpTestDropIndexedEncodedColumn(false, false);
+    }
+    
+    @Test
+    public void testDropIndexedColumnImmutableEncodedIndex() throws Exception {
+        helpTestDropIndexedEncodedColumn(true, true);
+    }
+    
+    @Test
+    public void testDropIndexedColumnMutableEncodedIndex() throws Exception {
+        helpTestDropIndexedEncodedColumn(false, true);
+    }
+    
+    private void helpTestDropIndexedEncodedColumn(boolean immutable, boolean columnEncoded) throws Exception {
+        String query;
+        ResultSet rs;
+        PreparedStatement stmt;
+        String schemaName = "";
+        String dataTableName = generateUniqueName();
+        String indexTableName = "I_" + generateUniqueName();
+        String localIndexTableName = "LI_" + generateUniqueName();
+        String dataTableFullName = SchemaUtil.getTableName(schemaName, dataTableName);
+        String indexTableFullName = SchemaUtil.getTableName(schemaName, indexTableName);
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+
+        // make sure that the tables are empty, but reachable
+        conn.createStatement().execute(
+          "CREATE TABLE " + dataTableFullName
+              + " (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR) "
+              + (immutable ? "IMMUTABLE_ROWS = true" : "")
+              + (!columnEncoded ? ",IMMUTABLE_STORAGE_SCHEME=" + PTable.ImmutableStorageScheme.ONE_CELL_PER_COLUMN : ""));
+        query = "SELECT * FROM " + dataTableFullName;
+        rs = conn.createStatement().executeQuery(query);
+        assertFalse(rs.next());
+
+        conn.createStatement().execute(
+          "CREATE INDEX " + indexTableName + " ON " + dataTableFullName + " (v1, v2)");
+        conn.createStatement().execute(
+            "CREATE LOCAL INDEX " + localIndexTableName + " ON " + dataTableFullName + " (v1, v2)");
+
+        query = "SELECT * FROM " + indexTableFullName;
+        rs = conn.createStatement().executeQuery(query);
+        assertFalse(rs.next());
+
+        // load some data into the table
+        stmt = conn.prepareStatement("UPSERT INTO " + dataTableFullName + " VALUES(?,?,?)");
+        stmt.setString(1, "a");
+        stmt.setString(2, "x");
+        stmt.setString(3, "1");
+        stmt.execute();
+        conn.commit();
+
+        assertIndexExists(conn,true,schemaName,dataTableName);
+        conn.createStatement().execute("ALTER TABLE " + dataTableFullName + " DROP COLUMN v1");
+        assertIndexExists(conn,false,schemaName,dataTableName);
+
+        query = "SELECT * FROM " + dataTableFullName;
+        rs = conn.createStatement().executeQuery(query);
+        assertTrue(rs.next());
+        assertEquals("a",rs.getString(1));
+        assertEquals("1",rs.getString(2));
+        assertFalse(rs.next());
+
+        // load some data into the table
+        stmt = conn.prepareStatement("UPSERT INTO " + dataTableFullName + " VALUES(?,?)");
+        stmt.setString(1, "a");
+        stmt.setString(2, "2");
+        stmt.execute();
+        conn.commit();
+
+        query = "SELECT * FROM " + dataTableFullName;
+        rs = conn.createStatement().executeQuery(query);
+        assertTrue(rs.next());
+        assertEquals("a",rs.getString(1));
+        assertEquals("2",rs.getString(2));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testDropCoveredColumn() throws Exception {
+        ResultSet rs;
+        PreparedStatement stmt;
+        String schemaName = "";
+        String dataTableName = generateUniqueName();
+        String indexTableName = "I_" + generateUniqueName();
+        String localIndexTableName = "LI_" + generateUniqueName();
+        String dataTableFullName = SchemaUtil.getTableName(schemaName, dataTableName);
+        String indexTableFullName = SchemaUtil.getTableName(schemaName, indexTableName);
+        String localIndexTableFullName = SchemaUtil.getTableName(schemaName, localIndexTableName);
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+
+        // make sure that the tables are empty, but reachable
+        conn.createStatement().execute(
+          "CREATE TABLE " + dataTableFullName
+              + " (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR, v3 VARCHAR) ");
+        String dataTableQuery = "SELECT * FROM " + dataTableFullName;
+        rs = conn.createStatement().executeQuery(dataTableQuery);
+        assertFalse(rs.next());
+
+        conn.createStatement().execute(
+          "CREATE INDEX " + indexTableName + " ON " + dataTableFullName + " (v1) include (v2, v3)");
+        conn.createStatement().execute(
+            "CREATE LOCAL INDEX " + localIndexTableName + " ON " + dataTableFullName + " (v1) include (v2, v3)");
+        rs = conn.createStatement().executeQuery(dataTableQuery);
+        assertFalse(rs.next());
+        String indexTableQuery = "SELECT * FROM " + indexTableName;
+        rs = conn.createStatement().executeQuery(indexTableQuery);
+        assertFalse(rs.next());
+        String localIndexTableQuery = "SELECT * FROM " + localIndexTableFullName;
+        rs = conn.createStatement().executeQuery(localIndexTableQuery);
+        assertFalse(rs.next());
+
+        // load some data into the table
+        stmt = conn.prepareStatement("UPSERT INTO " + dataTableFullName + " VALUES(?,?,?,?)");
+        stmt.setString(1, "a");
+        stmt.setString(2, "x");
+        stmt.setString(3, "1");
+        stmt.setString(4, "j");
+        stmt.execute();
+        conn.commit();
+
+        assertIndexExists(conn,true,schemaName,dataTableName);
+        conn.createStatement().execute("ALTER TABLE " + dataTableFullName + " DROP COLUMN v2");
+        assertIndexExists(conn,true,schemaName,dataTableName);
+
+        // verify data table rows
+        Scan scan = new Scan();
+        HTable table = (HTable) conn.unwrap(PhoenixConnection.class).getQueryServices().getTable(Bytes.toBytes(dataTableFullName));
+        ResultScanner results = table.getScanner(scan);
+        for (Result res : results) {
+            assertNull("Column value was not deleted",res.getValue(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, Bytes.toBytes("V2")));
+        }
+        results.close();
+        rs = conn.createStatement().executeQuery(dataTableQuery);
+        assertTrue(rs.next());
+        assertEquals("a",rs.getString(1));
+        assertEquals("x",rs.getString(2));
+        assertEquals("j",rs.getString(3));
+        assertFalse(rs.next());
+        
+        // verify index table rows
+        scan = new Scan();
+        table = (HTable) conn.unwrap(PhoenixConnection.class).getQueryServices().getTable(Bytes.toBytes(indexTableFullName));
+        results = table.getScanner(scan);
+        for (Result res : results) {
+            assertNull("Column value was not deleted",res.getValue(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, Bytes.toBytes("0:V2")));
+        }
+        results.close();
+        rs = conn.createStatement().executeQuery(indexTableQuery);
+        assertTrue(rs.next());
+        assertEquals("x",rs.getString(1));
+        assertEquals("a",rs.getString(2));
+        assertEquals("j",rs.getString(3));
+        assertFalse(rs.next());
+        
+        // verify local index table rows
+        rs = conn.createStatement().executeQuery(localIndexTableQuery);
+        assertTrue(rs.next());
+        assertEquals("x",rs.getString(1));
+        assertEquals("a",rs.getString(2));
+        assertEquals("j",rs.getString(3));
+        assertFalse(rs.next());
+
+        // load some data into the table
+        stmt = conn.prepareStatement("UPSERT INTO " + dataTableFullName + " VALUES(?,?,?)");
+        stmt.setString(1, "a");
+        stmt.setString(2, "y");
+        stmt.setString(3, "k");
+        stmt.execute();
+        conn.commit();
+
+        // verify data table rows
+        rs = conn.createStatement().executeQuery(dataTableQuery);
+        assertTrue(rs.next());
+        assertEquals("a",rs.getString(1));
+        assertEquals("y",rs.getString(2));
+        assertEquals("k",rs.getString(3));
+        assertFalse(rs.next());
+        
+        // verify index table rows
+        rs = conn.createStatement().executeQuery(indexTableQuery);
+        assertTrue(rs.next());
+        assertEquals("y",rs.getString(1));
+        assertEquals("a",rs.getString(2));
+        assertEquals("k",rs.getString(3));
+        assertFalse(rs.next());
+        
+        // verify local index table rows
+        rs = conn.createStatement().executeQuery(localIndexTableQuery);
+        assertTrue(rs.next());
+        assertEquals("y",rs.getString(1));
+        assertEquals("a",rs.getString(2));
+        assertEquals("k",rs.getString(3));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testAddPKColumnToTableWithIndex() throws Exception {
+        String query;
+        ResultSet rs;
+        PreparedStatement stmt;
+        String schemaName = "";
+        String dataTableName = generateUniqueName();
+        String indexTableName = "I_" + generateUniqueName();
+        String localIndexTableName = "LI_" + generateUniqueName();
+        String dataTableFullName = SchemaUtil.getTableName(schemaName, dataTableName);
+        String indexTableFullName = SchemaUtil.getTableName(schemaName, indexTableName);
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+
+        // make sure that the tables are empty, but reachable
+        conn.createStatement().execute(
+          "CREATE TABLE " + dataTableFullName
+              + " (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR) ");
+        query = "SELECT * FROM " + dataTableFullName;
+        rs = conn.createStatement().executeQuery(query);
+        assertFalse(rs.next());
+
+        conn.createStatement().execute(
+          "CREATE INDEX " + indexTableName + " ON " + dataTableFullName + " (v1) include (v2)");
+        query = "SELECT * FROM " + indexTableFullName;
+        rs = conn.createStatement().executeQuery(query);
+        assertFalse(rs.next());
+
+        // load some data into the table
+        stmt = conn.prepareStatement("UPSERT INTO " + dataTableFullName + " VALUES(?,?,?)");
+        stmt.setString(1, "a");
+        stmt.setString(2, "x");
+        stmt.setString(3, "1");
+        stmt.execute();
+        conn.commit();
+
+        assertIndexExists(conn,true,schemaName,dataTableName);
+        conn.createStatement().execute("ALTER TABLE " + dataTableFullName + " ADD v3 VARCHAR, k2 DECIMAL PRIMARY KEY, k3 DECIMAL PRIMARY KEY");
+        rs = conn.getMetaData().getPrimaryKeys("", schemaName, dataTableName);
+        assertTrue(rs.next());
+        assertEquals("K",rs.getString("COLUMN_NAME"));
+        assertEquals(1, rs.getShort("KEY_SEQ"));
+        assertTrue(rs.next());
+        assertEquals("K2",rs.getString("COLUMN_NAME"));
+        assertEquals(2, rs.getShort("KEY_SEQ"));
+        assertTrue(rs.next());
+        assertEquals("K3",rs.getString("COLUMN_NAME"));
+        assertEquals(3, rs.getShort("KEY_SEQ"));
+        assertFalse(rs.next());
+
+        rs = conn.getMetaData().getPrimaryKeys("", schemaName, indexTableName);
+        assertTrue(rs.next());
+        assertEquals(QueryConstants.DEFAULT_COLUMN_FAMILY + IndexUtil.INDEX_COLUMN_NAME_SEP + "V1",rs.getString("COLUMN_NAME"));
+        assertEquals(1, rs.getShort("KEY_SEQ"));
+        assertTrue(rs.next());
+        assertEquals(IndexUtil.INDEX_COLUMN_NAME_SEP + "K",rs.getString("COLUMN_NAME"));
+        assertEquals(2, rs.getShort("KEY_SEQ"));
+        assertTrue(rs.next());
+        assertEquals(IndexUtil.INDEX_COLUMN_NAME_SEP + "K2",rs.getString("COLUMN_NAME"));
+        assertEquals(3, rs.getShort("KEY_SEQ"));
+        assertTrue(rs.next());
+        assertEquals(IndexUtil.INDEX_COLUMN_NAME_SEP + "K3",rs.getString("COLUMN_NAME"));
+        assertEquals(4, rs.getShort("KEY_SEQ"));
+        assertFalse(rs.next());
+
+        query = "SELECT * FROM " + dataTableFullName;
+        rs = conn.createStatement().executeQuery(query);
+        assertTrue(rs.next());
+        assertEquals("a",rs.getString(1));
+        assertEquals("x",rs.getString(2));
+        assertEquals("1",rs.getString(3));
+        assertNull(rs.getBigDecimal(4));
+        assertFalse(rs.next());
+
+        // load some data into the table
+        stmt = conn.prepareStatement("UPSERT INTO " + dataTableFullName + "(K,K2,V1,V2,K3) VALUES(?,?,?,?,?)");
+        stmt.setString(1, "b");
+        stmt.setBigDecimal(2, BigDecimal.valueOf(2));
+        stmt.setString(3, "y");
+        stmt.setString(4, "2");
+        stmt.setBigDecimal(5, BigDecimal.valueOf(3));
+        stmt.execute();
+        conn.commit();
+
+        query = "SELECT k,k2,k3 FROM " + dataTableFullName + " WHERE v1='y'";
+        rs = conn.createStatement().executeQuery(query);
+        assertTrue(rs.next());
+        assertEquals("b",rs.getString(1));
+        assertEquals(BigDecimal.valueOf(2),rs.getBigDecimal(2));
+        assertEquals(BigDecimal.valueOf(3),rs.getBigDecimal(3));
+        assertFalse(rs.next());
+    }
 }


[11/25] phoenix git commit: PHOENIX-4246 Breakup join related tests into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/ee20a8c9/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinLocalIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinLocalIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinLocalIndexIT.java
deleted file mode 100644
index ec05149..0000000
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinLocalIndexIT.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.phoenix.end2end;
-
-import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.util.Collection;
-import java.util.List;
-import java.util.Properties;
-
-import org.apache.phoenix.util.PropertiesUtil;
-import org.apache.phoenix.util.QueryUtil;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import com.google.common.collect.Lists;
-
-@RunWith(Parameterized.class)
-public class HashJoinLocalIndexIT extends BaseJoinIT {
-    
-    public HashJoinLocalIndexIT(String[] indexDDL, String[] plans) {
-        super(indexDDL, plans);
-    }
-    
-    @Parameters
-    public static Collection<Object> data() {
-        List<Object> testCases = Lists.newArrayList();
-        testCases.add(new String[][] {
-                {
-                "CREATE LOCAL INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
-                "CREATE LOCAL INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name)",
-                "CREATE LOCAL INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
-                }, {
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + " [1,'S1']\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1,*] - [1,'T6']\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    DYNAMIC SERVER FILTER BY \"S.:supplier_id\" IN (\"I.supplier_id\")",
-                
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + " [1,'S1']\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [\"S.PHONE\"]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1,*] - [1,'T6']\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "        CLIENT MERGE SORT\n" +
-                "    DYNAMIC SERVER FILTER BY \"S.:supplier_id\" IN (\"I.supplier_id\")",
-                
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + " [1,*] - [1,'S3']\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "    SERVER AGGREGATE INTO SINGLE ROW\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1,*] - [1,'T6']\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "        CLIENT MERGE SORT",
-                }});
-        return testCases;
-    }
-    
-
-    @Test
-    public void testJoinWithLocalIndex() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {            
-            String query = "select phone, i.name from " + getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME) + " s join " + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i on s.\"supplier_id\" = i.\"supplier_id\" where s.name = 'S1' and i.name < 'T6'";
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "888-888-1111");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "888-888-1111");
-            assertFalse(rs.next());
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            assertPlansEqual(plans[0], QueryUtil.getExplainPlan(rs));
-            
-            query = "select phone, max(i.name) from " + getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME) + " s join " + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i on s.\"supplier_id\" = i.\"supplier_id\" where s.name = 'S1' and i.name < 'T6' group by phone";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "888-888-1111");
-            assertEquals(rs.getString(2), "T2");
-            assertFalse(rs.next());
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            assertPlansEqual(plans[1], QueryUtil.getExplainPlan(rs));
-            
-            query = "select max(phone), max(i.name) from " + getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME) + " s left join " + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i on s.\"supplier_id\" = i.\"supplier_id\" and i.name < 'T6' where s.name <= 'S3'";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "888-888-3333");
-            assertEquals(rs.getString(2), "T4");
-            assertFalse(rs.next());
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            assertPlansEqual(plans[2], QueryUtil.getExplainPlan(rs));
-        } finally {
-            conn.close();
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ee20a8c9/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinMoreIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinMoreIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinMoreIT.java
deleted file mode 100644
index c76eb67..0000000
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinMoreIT.java
+++ /dev/null
@@ -1,909 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.phoenix.end2end;
-
-import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import java.sql.Array;
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.Statement;
-import java.util.Properties;
-
-import org.apache.phoenix.util.PropertiesUtil;
-import org.apache.phoenix.util.QueryUtil;
-import org.junit.Test;
-
-public class HashJoinMoreIT extends ParallelStatsDisabledIT {
-    private final String[] plans = new String[] {
-            /*
-             * testJoinWithKeyRangeOptimization()
-             *     SELECT lhs.col0, lhs.col1, lhs.col2, rhs.col0, rhs.col1, rhs.col2 
-             *     FROM TEMP_TABLE_COMPOSITE_PK lhs 
-             *     JOIN TEMP_TABLE_COMPOSITE_PK rhs ON lhs.col1 = rhs.col2
-             */
-            "CLIENT PARALLEL 4-WAY FULL SCAN OVER %s\n" +
-            "CLIENT MERGE SORT\n" +
-            "    PARALLEL INNER-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 4-WAY FULL SCAN OVER %s\n" +
-            "        CLIENT MERGE SORT",
-            /*
-             * testJoinWithKeyRangeOptimization()
-             *     SELECT lhs.col0, lhs.col1, lhs.col2, rhs.col0, rhs.col1, rhs.col2 
-             *     FROM TEMP_TABLE_COMPOSITE_PK lhs 
-             *     JOIN TEMP_TABLE_COMPOSITE_PK rhs ON lhs.col0 = rhs.col2
-             */
-            "CLIENT PARALLEL 4-WAY FULL SCAN OVER %s\n" +
-            "CLIENT MERGE SORT\n" +
-            "    PARALLEL INNER-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 4-WAY FULL SCAN OVER %s\n" +
-            "        CLIENT MERGE SORT\n" +
-            "    DYNAMIC SERVER FILTER BY LHS.COL0 IN (RHS.COL2)",
-            /*
-             * testJoinWithKeyRangeOptimization()
-             *     SELECT lhs.col0, lhs.col1, lhs.col2, rhs.col0, rhs.col1, rhs.col2 
-             *     FROM TEMP_TABLE_COMPOSITE_PK lhs 
-             *     JOIN TEMP_TABLE_COMPOSITE_PK rhs ON lhs.col0 = rhs.col1 AND lhs.col1 = rhs.col2
-             */
-            "CLIENT PARALLEL 4-WAY FULL SCAN OVER %s\n" +
-            "CLIENT MERGE SORT\n" +
-            "    PARALLEL INNER-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 4-WAY FULL SCAN OVER %s\n" +
-            "        CLIENT MERGE SORT\n" +
-            "    DYNAMIC SERVER FILTER BY (LHS.COL0, LHS.COL1) IN ((RHS.COL1, RHS.COL2))",
-            /*
-             * testJoinWithKeyRangeOptimization()
-             *     SELECT lhs.col0, lhs.col1, lhs.col2, rhs.col0, rhs.col1, rhs.col2 
-             *     FROM TEMP_TABLE_COMPOSITE_PK lhs 
-             *     JOIN TEMP_TABLE_COMPOSITE_PK rhs ON lhs.col0 = rhs.col1 AND lhs.col2 = rhs.col3 - 1 AND lhs.col1 = rhs.col2
-             */
-            "CLIENT PARALLEL 4-WAY FULL SCAN OVER %s\n" +
-            "CLIENT MERGE SORT\n" +
-            "    PARALLEL INNER-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 4-WAY FULL SCAN OVER %s\n" +
-            "        CLIENT MERGE SORT\n" +
-            "    DYNAMIC SERVER FILTER BY (LHS.COL0, LHS.COL1, LHS.COL2) IN ((RHS.COL1, RHS.COL2, TO_INTEGER((RHS.COL3 - 1))))",            
-    };
-    
-    @Test
-    public void testJoinOverSaltedTables() throws Exception {
-        String tempTableNoSalting = generateUniqueName();
-        String tempTableWithSalting = generateUniqueName();
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            conn.createStatement().execute("CREATE TABLE " + tempTableNoSalting 
-                    + "   (mypk INTEGER NOT NULL PRIMARY KEY, " 
-                    + "    col1 INTEGER)");
-            conn.createStatement().execute("CREATE TABLE " + tempTableWithSalting 
-                    + "   (mypk INTEGER NOT NULL PRIMARY KEY, " 
-                    + "    col1 INTEGER) SALT_BUCKETS=4");
-            
-            PreparedStatement upsertStmt = conn.prepareStatement(
-                    "upsert into " + tempTableNoSalting + "(mypk, col1) " + "values (?, ?)");
-            for (int i = 0; i < 3; i++) {
-                upsertStmt.setInt(1, i + 1);
-                upsertStmt.setInt(2, 3 - i);
-                upsertStmt.execute();
-            }
-            conn.commit();
-            
-            upsertStmt = conn.prepareStatement(
-                    "upsert into " + tempTableWithSalting + "(mypk, col1) " + "values (?, ?)");
-            for (int i = 0; i < 6; i++) {
-                upsertStmt.setInt(1, i + 1);
-                upsertStmt.setInt(2, 3 - (i % 3));
-                upsertStmt.execute();
-            }
-            conn.commit();
-            
-            // LHS=unsalted JOIN RHS=salted
-            String query = "SELECT lhs.mypk, lhs.col1, rhs.mypk, rhs.col1 FROM " 
-                    + tempTableNoSalting + " lhs JOIN "
-                    + tempTableWithSalting + " rhs ON rhs.mypk = lhs.col1";
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 1);
-            assertEquals(rs.getInt(2), 3);
-            assertEquals(rs.getInt(3), 3);
-            assertEquals(rs.getInt(4), 1);
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 2);
-            assertEquals(rs.getInt(2), 2);
-            assertEquals(rs.getInt(3), 2);
-            assertEquals(rs.getInt(4), 2);
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 3);
-            assertEquals(rs.getInt(2), 1);
-            assertEquals(rs.getInt(3), 1);
-            assertEquals(rs.getInt(4), 3);
-
-            assertFalse(rs.next());
-            
-            // LHS=salted JOIN RHS=salted
-            query = "SELECT lhs.mypk, lhs.col1, rhs.mypk, rhs.col1 FROM " 
-                    + tempTableWithSalting + " lhs JOIN "
-                    + tempTableNoSalting + " rhs ON rhs.mypk = lhs.col1";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 1);
-            assertEquals(rs.getInt(2), 3);
-            assertEquals(rs.getInt(3), 3);
-            assertEquals(rs.getInt(4), 1);
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 2);
-            assertEquals(rs.getInt(2), 2);
-            assertEquals(rs.getInt(3), 2);
-            assertEquals(rs.getInt(4), 2);
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 3);
-            assertEquals(rs.getInt(2), 1);
-            assertEquals(rs.getInt(3), 1);
-            assertEquals(rs.getInt(4), 3);
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 4);
-            assertEquals(rs.getInt(2), 3);
-            assertEquals(rs.getInt(3), 3);
-            assertEquals(rs.getInt(4), 1);
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 5);
-            assertEquals(rs.getInt(2), 2);
-            assertEquals(rs.getInt(3), 2);
-            assertEquals(rs.getInt(4), 2);
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 6);
-            assertEquals(rs.getInt(2), 1);
-            assertEquals(rs.getInt(3), 1);
-            assertEquals(rs.getInt(4), 3);
-
-            assertFalse(rs.next());
-            
-            // LHS=salted JOIN RHS=salted
-            query = "SELECT lhs.mypk, lhs.col1, rhs.mypk, rhs.col1 FROM " 
-                    + tempTableWithSalting + " lhs JOIN "
-                    + tempTableWithSalting + " rhs ON rhs.mypk = (lhs.col1 + 3)";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 1);
-            assertEquals(rs.getInt(2), 3);
-            assertEquals(rs.getInt(3), 6);
-            assertEquals(rs.getInt(4), 1);
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 2);
-            assertEquals(rs.getInt(2), 2);
-            assertEquals(rs.getInt(3), 5);
-            assertEquals(rs.getInt(4), 2);
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 3);
-            assertEquals(rs.getInt(2), 1);
-            assertEquals(rs.getInt(3), 4);
-            assertEquals(rs.getInt(4), 3);
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 4);
-            assertEquals(rs.getInt(2), 3);
-            assertEquals(rs.getInt(3), 6);
-            assertEquals(rs.getInt(4), 1);
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 5);
-            assertEquals(rs.getInt(2), 2);
-            assertEquals(rs.getInt(3), 5);
-            assertEquals(rs.getInt(4), 2);
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 6);
-            assertEquals(rs.getInt(2), 1);
-            assertEquals(rs.getInt(3), 4);
-            assertEquals(rs.getInt(4), 3);
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testJoinOnDynamicColumns() throws Exception {
-        String tableA = generateUniqueName();
-        String tableB = generateUniqueName();
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = null;
-        PreparedStatement stmt = null;
-        try {
-            conn = DriverManager.getConnection(getUrl(), props);
-            String ddlA = "CREATE TABLE " + tableA + "   (pkA INTEGER NOT NULL, " + "    colA1 INTEGER, "
-                    + "        colA2 VARCHAR " + "CONSTRAINT PK PRIMARY KEY" + "(pkA)" + ")";
-
-            String ddlB = "CREATE TABLE " + tableB + "   (pkB INTEGER NOT NULL PRIMARY KEY, " + "    colB INTEGER)";
-            conn.createStatement().execute(ddlA);
-            conn.createStatement().execute(ddlB);
-
-            String upsertA = "UPSERT INTO " + tableA + " (pkA, colA1, colA2) VALUES(?, ?, ?)";
-            stmt = conn.prepareStatement(upsertA);
-            int i = 0;
-            for (i = 0; i < 5; i++) {
-                stmt.setInt(1, i);
-                stmt.setInt(2, i + 10);
-                stmt.setString(3, "00" + i);
-                stmt.executeUpdate();
-            }
-            conn.commit();
-            stmt.close();
-
-            String sequenceB = generateUniqueName();
-            // upsert select dynamic columns in tableB
-            conn.createStatement().execute("CREATE SEQUENCE " + sequenceB );
-            String upsertBSelectA = "UPSERT INTO " + tableB + " (pkB, pkA INTEGER)"
-                    + "SELECT NEXT VALUE FOR " + sequenceB + ", pkA FROM " + tableA ;
-            stmt = conn.prepareStatement(upsertBSelectA);
-            stmt.executeUpdate();
-            stmt.close();
-            conn.commit();
-            conn.createStatement().execute("DROP SEQUENCE " + sequenceB );
-
-            // perform a join between tableB and tableA by joining on the dynamic column that we upserted in
-            // tableB. This join should return all the rows from table A.
-            String joinSql = "SELECT A.pkA, A.COLA1, A.colA2 FROM " + tableB + " B(pkA INTEGER) JOIN " + tableA + " A ON a.pkA = b.pkA";
-            stmt = conn.prepareStatement(joinSql);
-            ResultSet rs = stmt.executeQuery();
-            i = 0;
-            while (rs.next()) {
-                // check that we get back all the rows that we upserted for tableA above.
-                assertEquals(rs.getInt(1), i);
-                assertEquals(rs.getInt(2), i + 10);
-                assertEquals(rs.getString(3), "00" + i);
-                i++;
-            }
-            assertEquals(5,i);
-        } finally {
-            if (stmt != null) {
-                stmt.close();
-            }
-            if (conn != null) {
-                conn.close();
-            }
-
-        }
-
-    }
-    
-    @Test
-    public void testJoinWithKeyRangeOptimization() throws Exception {
-        String tempTableWithCompositePK = generateUniqueName();
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            conn.createStatement().execute("CREATE TABLE " + tempTableWithCompositePK 
-                    + "   (col0 INTEGER NOT NULL, " 
-                    + "    col1 INTEGER NOT NULL, " 
-                    + "    col2 INTEGER NOT NULL, "
-                    + "    col3 INTEGER "
-                    + "   CONSTRAINT pk PRIMARY KEY (col0, col1, col2)) " 
-                    + "   SALT_BUCKETS=4");
-            
-            PreparedStatement upsertStmt = conn.prepareStatement(
-                    "upsert into " + tempTableWithCompositePK + "(col0, col1, col2, col3) " + "values (?, ?, ?, ?)");
-            for (int i = 0; i < 3; i++) {
-                upsertStmt.setInt(1, i + 1);
-                upsertStmt.setInt(2, i + 2);
-                upsertStmt.setInt(3, i + 3);
-                upsertStmt.setInt(4, i + 5);
-                upsertStmt.execute();
-            }
-            conn.commit();
-            
-            // No leading part of PK
-            String query = "SELECT lhs.col0, lhs.col1, lhs.col2, lhs.col3, rhs.col0, rhs.col1, rhs.col2, rhs.col3 FROM " 
-                    + tempTableWithCompositePK + " lhs JOIN "
-                    + tempTableWithCompositePK + " rhs ON lhs.col1 = rhs.col2";
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 2);
-            assertEquals(rs.getInt(2), 3);
-            assertEquals(rs.getInt(3), 4);
-            assertEquals(rs.getInt(4), 6);            
-            assertEquals(rs.getInt(5), 1);
-            assertEquals(rs.getInt(6), 2);
-            assertEquals(rs.getInt(7), 3);
-            assertEquals(rs.getInt(8), 5);
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 3);
-            assertEquals(rs.getInt(2), 4);
-            assertEquals(rs.getInt(3), 5);
-            assertEquals(rs.getInt(4), 7);
-            assertEquals(rs.getInt(5), 2);
-            assertEquals(rs.getInt(6), 3);
-            assertEquals(rs.getInt(7), 4);
-            assertEquals(rs.getInt(8), 6);
-
-            assertFalse(rs.next());
-            
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            assertEquals(String.format(plans[0],tempTableWithCompositePK,tempTableWithCompositePK), QueryUtil.getExplainPlan(rs));
-            
-            // Two parts of PK but only one leading part
-            query = "SELECT lhs.col0, lhs.col1, lhs.col2, lhs.col3, rhs.col0, rhs.col1, rhs.col2, rhs.col3 FROM " 
-                    + tempTableWithCompositePK + " lhs JOIN "
-                    + tempTableWithCompositePK + " rhs ON lhs.col2 = rhs.col3 AND lhs.col0 = rhs.col2";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 3);
-            assertEquals(rs.getInt(2), 4);
-            assertEquals(rs.getInt(3), 5);
-            assertEquals(rs.getInt(4), 7);
-            assertEquals(rs.getInt(5), 1);
-            assertEquals(rs.getInt(6), 2);
-            assertEquals(rs.getInt(7), 3);
-            assertEquals(rs.getInt(8), 5);
-
-            assertFalse(rs.next());
-            
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            assertEquals(String.format(plans[1],tempTableWithCompositePK,tempTableWithCompositePK), QueryUtil.getExplainPlan(rs));
-            
-            // Two leading parts of PK
-            query = "SELECT lhs.col0, lhs.col1, lhs.col2, lhs.col3, rhs.col0, rhs.col1, rhs.col2, rhs.col3 FROM " 
-                    + tempTableWithCompositePK + " lhs JOIN "
-                    + tempTableWithCompositePK + " rhs ON lhs.col1 = rhs.col2 AND lhs.col0 = rhs.col1";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 2);
-            assertEquals(rs.getInt(2), 3);
-            assertEquals(rs.getInt(3), 4);
-            assertEquals(rs.getInt(4), 6);
-            assertEquals(rs.getInt(5), 1);
-            assertEquals(rs.getInt(6), 2);
-            assertEquals(rs.getInt(7), 3);
-            assertEquals(rs.getInt(8), 5);
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 3);
-            assertEquals(rs.getInt(2), 4);
-            assertEquals(rs.getInt(3), 5);
-            assertEquals(rs.getInt(4), 7);
-            assertEquals(rs.getInt(5), 2);
-            assertEquals(rs.getInt(6), 3);
-            assertEquals(rs.getInt(7), 4);
-            assertEquals(rs.getInt(8), 6);
-
-            assertFalse(rs.next());
-            
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            assertEquals(String.format(plans[2],tempTableWithCompositePK,tempTableWithCompositePK), QueryUtil.getExplainPlan(rs));
-            
-            // All parts of PK
-            query = "SELECT lhs.col0, lhs.col1, lhs.col2, lhs.col3, rhs.col0, rhs.col1, rhs.col2, rhs.col3 FROM " 
-                    + tempTableWithCompositePK + " lhs JOIN "
-                    + tempTableWithCompositePK + " rhs ON lhs.col1 = rhs.col2 AND lhs.col2 = rhs.col3 - 1 AND lhs.col0 = rhs.col1";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 2);
-            assertEquals(rs.getInt(2), 3);
-            assertEquals(rs.getInt(3), 4);
-            assertEquals(rs.getInt(4), 6);
-            assertEquals(rs.getInt(5), 1);
-            assertEquals(rs.getInt(6), 2);
-            assertEquals(rs.getInt(7), 3);
-            assertEquals(rs.getInt(8), 5);
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 3);
-            assertEquals(rs.getInt(2), 4);
-            assertEquals(rs.getInt(3), 5);
-            assertEquals(rs.getInt(4), 7);
-            assertEquals(rs.getInt(5), 2);
-            assertEquals(rs.getInt(6), 3);
-            assertEquals(rs.getInt(7), 4);
-            assertEquals(rs.getInt(8), 6);
-
-            assertFalse(rs.next());
-            
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            assertEquals(String.format(plans[3],tempTableWithCompositePK,tempTableWithCompositePK), QueryUtil.getExplainPlan(rs));
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testSubqueryWithoutData() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        conn.setAutoCommit(false);
-
-        try {
-            String GRAMMAR_TABLE = "CREATE TABLE IF NOT EXISTS GRAMMAR_TABLE (ID INTEGER PRIMARY KEY, " +
-                    "unsig_id UNSIGNED_INT, big_id BIGINT, unsig_long_id UNSIGNED_LONG, tiny_id TINYINT," +
-                    "unsig_tiny_id UNSIGNED_TINYINT, small_id SMALLINT, unsig_small_id UNSIGNED_SMALLINT," + 
-                    "float_id FLOAT, unsig_float_id UNSIGNED_FLOAT, double_id DOUBLE, unsig_double_id UNSIGNED_DOUBLE," + 
-                    "decimal_id DECIMAL, boolean_id BOOLEAN, time_id TIME, date_id DATE, timestamp_id TIMESTAMP," + 
-                    "unsig_time_id TIME, unsig_date_id DATE, unsig_timestamp_id TIMESTAMP, varchar_id VARCHAR (30)," + 
-                    "char_id CHAR (30), binary_id BINARY (100), varbinary_id VARBINARY (100))";
-
-            String LARGE_TABLE = "CREATE TABLE IF NOT EXISTS LARGE_TABLE (ID INTEGER PRIMARY KEY, " +
-                    "unsig_id UNSIGNED_INT, big_id BIGINT, unsig_long_id UNSIGNED_LONG, tiny_id TINYINT," +
-                    "unsig_tiny_id UNSIGNED_TINYINT, small_id SMALLINT, unsig_small_id UNSIGNED_SMALLINT," + 
-                    "float_id FLOAT, unsig_float_id UNSIGNED_FLOAT, double_id DOUBLE, unsig_double_id UNSIGNED_DOUBLE," + 
-                    "decimal_id DECIMAL, boolean_id BOOLEAN, time_id TIME, date_id DATE, timestamp_id TIMESTAMP," + 
-                    "unsig_time_id TIME, unsig_date_id DATE, unsig_timestamp_id TIMESTAMP, varchar_id VARCHAR (30)," + 
-                    "char_id CHAR (30), binary_id BINARY (100), varbinary_id VARBINARY (100))";
-
-            String SECONDARY_LARGE_TABLE = "CREATE TABLE IF NOT EXISTS SECONDARY_LARGE_TABLE (SEC_ID INTEGER PRIMARY KEY," +
-                    "sec_unsig_id UNSIGNED_INT, sec_big_id BIGINT, sec_usnig_long_id UNSIGNED_LONG, sec_tiny_id TINYINT," + 
-                    "sec_unsig_tiny_id UNSIGNED_TINYINT, sec_small_id SMALLINT, sec_unsig_small_id UNSIGNED_SMALLINT," + 
-                    "sec_float_id FLOAT, sec_unsig_float_id UNSIGNED_FLOAT, sec_double_id DOUBLE, sec_unsig_double_id UNSIGNED_DOUBLE," +
-                    "sec_decimal_id DECIMAL, sec_boolean_id BOOLEAN, sec_time_id TIME, sec_date_id DATE," +
-                    "sec_timestamp_id TIMESTAMP, sec_unsig_time_id TIME, sec_unsig_date_id DATE, sec_unsig_timestamp_id TIMESTAMP," +
-                    "sec_varchar_id VARCHAR (30), sec_char_id CHAR (30), sec_binary_id BINARY (100), sec_varbinary_id VARBINARY (100))";
-            createTestTable(getUrl(), GRAMMAR_TABLE);
-            createTestTable(getUrl(), LARGE_TABLE);
-            createTestTable(getUrl(), SECONDARY_LARGE_TABLE);
-
-            String ddl = "SELECT * FROM (SELECT ID, BIG_ID, DATE_ID FROM LARGE_TABLE AS A WHERE (A.ID % 5) = 0) AS A " +
-                    "INNER JOIN (SELECT SEC_ID, SEC_TINY_ID, SEC_UNSIG_FLOAT_ID FROM SECONDARY_LARGE_TABLE AS B WHERE (B.SEC_ID % 5) = 0) AS B " +     
-                    "ON A.ID=B.SEC_ID WHERE A.DATE_ID > ALL (SELECT SEC_DATE_ID FROM SECONDARY_LARGE_TABLE LIMIT 100) " +      
-                    "AND B.SEC_UNSIG_FLOAT_ID = ANY (SELECT sec_unsig_float_id FROM SECONDARY_LARGE_TABLE " +                                       
-                    "WHERE SEC_ID > ALL (SELECT MIN (ID) FROM GRAMMAR_TABLE WHERE UNSIG_ID IS NULL) AND " +
-                    "SEC_UNSIG_ID < ANY (SELECT DISTINCT(UNSIG_ID) FROM LARGE_TABLE WHERE UNSIG_ID<2500) LIMIT 1000) " +
-                    "AND A.ID < 10000";
-            ResultSet rs = conn.createStatement().executeQuery(ddl);
-            assertFalse(rs.next());  
-        } finally {
-            Statement statement = conn.createStatement();
-            String query = "drop table GRAMMAR_TABLE";
-            statement.executeUpdate(query);
-            query = "drop table LARGE_TABLE";
-            statement.executeUpdate(query);
-            query = "drop table SECONDARY_LARGE_TABLE";
-            statement.executeUpdate(query);
-            conn.close();
-        }
-    }
-    
-    // PHOENIX-2381
-    @Test
-    public void testJoinWithMultiTenancy() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            conn.createStatement().execute("CREATE TABLE INVENTORY (" +
-                            " TENANTID UNSIGNED_INT NOT NULL" +
-                            ",ID UNSIGNED_INT NOT NULL" +
-                            ",FOO UNSIGNED_INT NOT NULL" +
-                            ",\"TIMESTAMP\"  UNSIGNED_LONG NOT NULL" +
-                            ",CODES INTEGER ARRAY[] NOT NULL" +
-                            ",V UNSIGNED_LONG" +
-                            " CONSTRAINT pk PRIMARY KEY (TENANTID, ID, FOO, \"TIMESTAMP\" , CODES))" +
-                            " DEFAULT_COLUMN_FAMILY ='E'," +
-                            " MULTI_TENANT=true");
-            PreparedStatement upsertStmt = conn.prepareStatement(
-                    "upsert into INVENTORY "
-                    + "(tenantid, id, foo, \"TIMESTAMP\" , codes) "
-                    + "values (?, ?, ?, ?, ?)");
-            upsertStmt.setInt(1, 15);
-            upsertStmt.setInt(2, 5);
-            upsertStmt.setInt(3, 0);
-            upsertStmt.setLong(4, 6);
-            Array array = conn.createArrayOf("INTEGER", new Object[] {1, 2});
-            upsertStmt.setArray(5, array);
-            upsertStmt.executeUpdate();
-            conn.commit();
-            
-            conn.createStatement().execute("CREATE TABLE PRODUCT_IDS (" +
-                            " PRODUCT_ID UNSIGNED_INT NOT NULL" +
-                            ",PRODUCT_NAME VARCHAR" +
-                            " CONSTRAINT pk PRIMARY KEY (PRODUCT_ID))" +
-                            " DEFAULT_COLUMN_FAMILY ='NAME'");
-            upsertStmt = conn.prepareStatement(
-                    "upsert into PRODUCT_IDS "
-                    + "(product_id, product_name) "
-                    + "values (?, ?)");
-            upsertStmt.setInt(1, 5);
-            upsertStmt.setString(2, "DUMMY");
-            upsertStmt.executeUpdate();
-            conn.commit();
-            conn.close();            
-
-            // Create a tenant-specific connection.
-            props.setProperty("TenantId", "15");
-            conn = DriverManager.getConnection(getUrl(), props);
-            ResultSet rs = conn.createStatement().executeQuery(
-                    "SELECT * FROM INVENTORY INNER JOIN PRODUCT_IDS ON (PRODUCT_ID = INVENTORY.ID)");
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 5);
-            assertFalse(rs.next());
-            rs.close();
-            rs = conn.createStatement().executeQuery(
-                    "SELECT * FROM INVENTORY RIGHT JOIN PRODUCT_IDS ON (PRODUCT_ID = INVENTORY.ID)");
-            assertTrue(rs.next());
-            assertEquals(rs.getInt(1), 5);
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testBug2480() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        conn.setAutoCommit(true);
-        try {
-            conn.createStatement().execute(
-                      "CREATE TABLE master_businessunit ("
-                    + "code varchar(255) PRIMARY KEY, name varchar(255))");
-            conn.createStatement().execute(
-                      "CREATE TABLE master_company ("
-                    + "code varchar(255) PRIMARY KEY, name varchar(255))");
-            conn.createStatement().execute(
-                      "CREATE TABLE master_costcenter ("
-                    + "code varchar(255) PRIMARY KEY, name varchar(255))");
-            conn.createStatement().execute(
-                      "CREATE TABLE master_location ("
-                    + "code varchar(255) PRIMARY KEY, name varchar(255))");
-            conn.createStatement().execute(
-                      "CREATE TABLE master_product ("
-                    + "id integer PRIMARY KEY, product_name varchar(255))");
-            conn.createStatement().execute(
-                      "CREATE TABLE master_purchaseorder ("
-                    + "purchaseOrderNumber varchar(255), "
-                    + "companyCode varchar(255), "
-                    + "businessUnitCode varchar(255), "
-                    + "locationCode varchar(255), "
-                    + "purchaseOrderId varchar(255) PRIMARY KEY, "
-                    + "releasedOn date, name varchar(255))");
-            conn.createStatement().execute(
-                      "CREATE TABLE trans_purchaseorderitem ("
-                    + "purchaseOrderItemId varchar(255) PRIMARY KEY, "
-                    + "purchaseOrderId varchar(255), "
-                    + "lineNo varchar(255), name varchar(255))");
-            conn.createStatement().execute(
-                      "CREATE TABLE trans_purchaseorderitem_costing ("
-                    + "purchaseorderItem_costing_id varchar(255) primary key, "
-                    + "purchaseorderItemId varchar(255), "
-                    + "purchaseorderId varchar(255), "
-                    + "costcenterCode varchar(255))");
-            
-            conn.createStatement().execute("upsert into master_businessunit(code,name) values ('1','BU1')");
-            conn.createStatement().execute("upsert into master_businessunit(code,name) values ('2','BU2')");
-            conn.createStatement().execute("upsert into master_company(code,name) values ('1','Company1')");
-            conn.createStatement().execute("upsert into master_company(code,name) values ('2','Company2')");
-            conn.createStatement().execute("upsert into master_costcenter(code,name) values ('1','CC1')");
-            conn.createStatement().execute("upsert into master_costcenter(code,name) values ('2','CC2')");
-            conn.createStatement().execute("upsert into master_location(code,name) values ('1','Location1')");
-            conn.createStatement().execute("upsert into master_location(code,name) values ('2','Location2')");
-            conn.createStatement().execute("upsert into master_product(id,product_name) values (1,'ProductName1')");
-            conn.createStatement().execute("upsert into master_product(id,product_name) values (2,'Product2')");
-            conn.createStatement().execute("upsert into master_purchaseorder(purchaseOrderNumber,companyCode,businessUnitCode,locationCode,purchaseOrderId,releasedOn,name) values ('1','1','1','1','1','2015-12-01','1')");
-            conn.createStatement().execute("upsert into master_purchaseorder(purchaseOrderNumber,companyCode,businessUnitCode,locationCode,purchaseOrderId,releasedOn,name) values ('2','2','2','2','2','2015-12-02','2')");
-            conn.createStatement().execute("upsert into trans_purchaseorderitem(purchaseOrderItemId,purchaseOrderId,lineNo,name) values ('1','1','1','1')");
-            conn.createStatement().execute("upsert into trans_purchaseorderitem(purchaseOrderItemId,purchaseOrderId,lineNo,name) values ('2','2','2','2')");
-            conn.createStatement().execute("upsert into trans_purchaseorderitem_costing(purchaseorderItem_costing_id,purchaseorderItemId,purchaseorderId,costcenterCode) values ('1','1','1','1')");
-            conn.createStatement().execute("upsert into trans_purchaseorderitem_costing(purchaseorderItem_costing_id,purchaseorderItemId,purchaseorderId,costcenterCode) values ('2','2','2','2')");
-            
-            ResultSet rs = conn.createStatement().executeQuery(
-                      "SELECT DISTINCT "
-                    + "COALESCE( a1.name, 'N.A.'), "
-                    + "COALESCE( a2.name, 'N.A.'), "
-                    + "COALESCE( a3.name, 'N.A.'), "
-                    + "COALESCE( a4.purchaseOrderNumber, 'N.A.'), "
-                    + "COALESCE( a1.name, 'N.A.'), "
-                    + "COALESCE( a4.name, 'N.A.'), "
-                    + "COALESCE( a5.lineNo, 'N.A.'), "
-                    + "COALESCE( a5.name, 'N.A.'), "
-                    + "COALESCE( a7.name,'N.A.') "
-                    + "FROM (master_purchaseorder a4 "
-                    + "LEFT OUTER JOIN master_company a1 "
-                    + "ON a4.companyCode = a1.code "
-                    + "LEFT OUTER JOIN master_businessunit a2 "
-                    + "ON a4.businessUnitCode = a2.code "
-                    + "LEFT OUTER JOIN master_location a3 "
-                    + "ON a4.locationCode = a3.code "
-                    + "LEFT OUTER JOIN trans_purchaseorderitem a5 "
-                    + "ON a5.purchaseOrderId = a4.purchaseOrderId "
-                    + "LEFT OUTER JOIN trans_purchaseorderitem_costing a6 "
-                    + "ON a6.purchaseOrderItemId = a5.purchaseOrderItemId "
-                    + "AND a6.purchaseOrderId = a5.purchaseOrderId "
-                    + "LEFT OUTER JOIN master_costcenter a7 "
-                    + "ON a6.costCenterCode = a7.code)");
-            
-            assertTrue(rs.next());
-            assertEquals("Company1", rs.getString(1));
-            assertEquals("BU1", rs.getString(2));
-            assertEquals("Location1", rs.getString(3));
-            assertEquals("1", rs.getString(4));
-            assertEquals("Company1", rs.getString(5));
-            assertEquals("1", rs.getString(6));
-            assertEquals("1", rs.getString(7));
-            assertEquals("1", rs.getString(8));
-            assertEquals("CC1", rs.getString(9));
-            assertTrue(rs.next());
-            assertEquals("Company2", rs.getString(1));
-            assertEquals("BU2", rs.getString(2));
-            assertEquals("Location2", rs.getString(3));
-            assertEquals("2", rs.getString(4));
-            assertEquals("Company2", rs.getString(5));
-            assertEquals("2", rs.getString(6));
-            assertEquals("2", rs.getString(7));
-            assertEquals("2", rs.getString(8));
-            assertEquals("CC2", rs.getString(9));
-            
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testBug2894() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        conn.setAutoCommit(true);
-        try {
-            conn.createStatement().execute(
-                    "CREATE TABLE IF NOT EXISTS EVENT_COUNT (\n" +
-                    "        BUCKET VARCHAR,\n" +
-                    "        TIMESTAMP_DATE TIMESTAMP,\n" +
-                    "        \"TIMESTAMP\" UNSIGNED_LONG NOT NULL,\n" +
-                    "        LOCATION VARCHAR,\n" +
-                    "        A VARCHAR,\n" +
-                    "        B VARCHAR,\n" +
-                    "        C VARCHAR,\n" +
-                    "        D UNSIGNED_LONG,\n" +
-                    "        E FLOAT\n" +
-                    "    CONSTRAINT pk PRIMARY KEY (BUCKET, \"TIMESTAMP\" DESC, LOCATION, A, B, C)\n" +
-                    ") SALT_BUCKETS=2, COMPRESSION='GZ', TTL=31622400");
-            PreparedStatement stmt = conn.prepareStatement("UPSERT INTO EVENT_COUNT(BUCKET, \"TIMESTAMP\", LOCATION, A, B, C) VALUES(?,?,?,?,?,?)");
-            stmt.setString(1, "5SEC");
-            stmt.setString(3, "Tr/Bal");
-            stmt.setString(4, "A1");
-            stmt.setString(5, "B1");
-            stmt.setString(6, "C1");
-            stmt.setLong(2, 1462993520000000000L);
-            stmt.execute();
-            stmt.setLong(2, 1462993515000000000L);
-            stmt.execute();
-            stmt.setLong(2, 1462993510000000000L);
-            stmt.execute();
-            stmt.setLong(2, 1462993505000000000L);
-            stmt.execute();
-            stmt.setLong(2, 1462993500000000000L);
-            stmt.execute();
-            stmt.setLong(2, 1462993495000000000L);
-            stmt.execute();
-            stmt.setLong(2, 1462993490000000000L);
-            stmt.execute();
-            stmt.setLong(2, 1462993485000000000L);
-            stmt.execute();
-            stmt.setLong(2, 1462993480000000000L);
-            stmt.execute();
-            stmt.setLong(2, 1462993475000000000L);
-            stmt.execute();
-            stmt.setLong(2, 1462993470000000000L);
-            stmt.execute();
-            stmt.setLong(2, 1462993465000000000L);
-            stmt.execute();
-            stmt.setLong(2, 1462993460000000000L);
-            stmt.execute();
-            stmt.setLong(2, 1462993455000000000L);
-            stmt.execute();
-            stmt.setLong(2, 1462993450000000000L);
-            stmt.execute();
-            stmt.setLong(2, 1462993445000000000L);
-            stmt.execute();
-            stmt.setLong(2, 1462993440000000000L);
-            stmt.execute();
-            stmt.setLong(2, 1462993430000000000L);
-            stmt.execute();
-
-            // We'll test the original version of the user table as well as a slightly modified
-            // version, in order to verify that hash join works for columns both having DESC
-            // sort order as well as one having ASC order and the other having DESC order.
-            String[] t = new String[] {"EVENT_LATENCY", "EVENT_LATENCY_2"};
-            for (int i = 0; i < 2; i++) {
-                conn.createStatement().execute(
-                        "CREATE TABLE IF NOT EXISTS " + t[i] + " (\n" +
-                                "        BUCKET VARCHAR,\n" +
-                                "        TIMESTAMP_DATE TIMESTAMP,\n" +
-                                "        \"TIMESTAMP\" UNSIGNED_LONG NOT NULL,\n" +
-                                "        SRC_LOCATION VARCHAR,\n" +
-                                "        DST_LOCATION VARCHAR,\n" +
-                                "        B VARCHAR,\n" +
-                                "        C VARCHAR,\n" +
-                                "        F UNSIGNED_LONG,\n" +
-                                "        G UNSIGNED_LONG,\n" +
-                                "        H UNSIGNED_LONG,\n" +
-                                "        I UNSIGNED_LONG\n" +
-                                "    CONSTRAINT pk PRIMARY KEY (BUCKET, \"TIMESTAMP\"" + (i == 0 ? " DESC" : "") + ", SRC_LOCATION, DST_LOCATION, B, C)\n" +
-                        ") SALT_BUCKETS=2, COMPRESSION='GZ', TTL=31622400");
-                stmt = conn.prepareStatement("UPSERT INTO " + t[i] + "(BUCKET, \"TIMESTAMP\", SRC_LOCATION, DST_LOCATION, B, C) VALUES(?,?,?,?,?,?)");
-                stmt.setString(1, "5SEC");
-                stmt.setString(3, "Tr/Bal");
-                stmt.setString(4, "Tr/Bal");
-                stmt.setString(5, "B1");
-                stmt.setString(6, "C1");
-                stmt.setLong(2, 1462993520000000000L);
-                stmt.execute();
-                stmt.setLong(2, 1462993515000000000L);
-                stmt.execute();
-                stmt.setLong(2, 1462993510000000000L);
-                stmt.execute();
-                stmt.setLong(2, 1462993505000000000L);
-                stmt.execute();
-                stmt.setLong(2, 1462993490000000000L);
-                stmt.execute();
-                stmt.setLong(2, 1462993485000000000L);
-                stmt.execute();
-                stmt.setLong(2, 1462993480000000000L);
-                stmt.execute();
-                stmt.setLong(2, 1462993475000000000L);
-                stmt.execute();
-                stmt.setLong(2, 1462993470000000000L);
-                stmt.execute();
-                stmt.setLong(2, 1462993430000000000L);
-                stmt.execute();
-                
-                String q =
-                        "SELECT C.BUCKET, C.\"TIMESTAMP\" FROM (\n" +
-                        "     SELECT E.BUCKET as BUCKET, L.BUCKET as LBUCKET, E.\"TIMESTAMP\" as TIMESTAMP, L.\"TIMESTAMP\" as LTIMESTAMP FROM\n" +
-                        "        (SELECT BUCKET, \"TIMESTAMP\"  FROM EVENT_COUNT\n" +
-                        "             WHERE BUCKET = '5SEC' AND LOCATION = 'Tr/Bal'\n" +
-                        "                 AND \"TIMESTAMP\"  <= 1462993520000000000 AND \"TIMESTAMP\"  > 1462993420000000000\n" +
-                        "        ) E\n" +
-                        "        JOIN\n" +
-                        "         (SELECT BUCKET, \"TIMESTAMP\"  FROM "+ t[i] +"\n" +
-                        "             WHERE BUCKET = '5SEC' AND SRC_LOCATION = 'Tr/Bal' AND SRC_LOCATION = DST_LOCATION\n" +
-                        "                 AND \"TIMESTAMP\"  <= 1462993520000000000 AND \"TIMESTAMP\"  > 1462993420000000000\n" +
-                        "         ) L\n" +
-                        "     ON L.BUCKET = E.BUCKET AND L.\"TIMESTAMP\"  = E.\"TIMESTAMP\"\n" +
-                        " ) C\n" +
-                        " GROUP BY C.BUCKET, C.\"TIMESTAMP\"";
-                    
-                String p = i == 0 ?
-                        "CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER EVENT_COUNT [0,'5SEC',~1462993520000000000,'Tr/Bal'] - [1,'5SEC',~1462993420000000000,'Tr/Bal']\n" +
-                        "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                        "    SERVER AGGREGATE INTO DISTINCT ROWS BY [E.BUCKET, \"E.TIMESTAMP\"]\n" +
-                        "CLIENT MERGE SORT\n" +
-                        "    PARALLEL INNER-JOIN TABLE 0 (SKIP MERGE)\n" +
-                        "        CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER " + t[i] + " [0,'5SEC',~1462993520000000000,'Tr/Bal'] - [1,'5SEC',~1462993420000000000,'Tr/Bal']\n" +
-                        "            SERVER FILTER BY FIRST KEY ONLY AND SRC_LOCATION = DST_LOCATION\n" +
-                        "        CLIENT MERGE SORT"
-                        :
-                        "CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER EVENT_COUNT [0,'5SEC',~1462993520000000000,'Tr/Bal'] - [1,'5SEC',~1462993420000000000,'Tr/Bal']\n" +
-                        "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                        "    SERVER AGGREGATE INTO DISTINCT ROWS BY [E.BUCKET, \"E.TIMESTAMP\"]\n" +
-                        "CLIENT MERGE SORT\n" +
-                        "    PARALLEL INNER-JOIN TABLE 0 (SKIP MERGE)\n" +
-                        "        CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER " + t[i] + " [0,'5SEC',1462993420000000001,'Tr/Bal'] - [1,'5SEC',1462993520000000000,'Tr/Bal']\n" +
-                        "            SERVER FILTER BY FIRST KEY ONLY AND SRC_LOCATION = DST_LOCATION\n" +
-                        "        CLIENT MERGE SORT";
-                
-                ResultSet rs = conn.createStatement().executeQuery("explain " + q);
-                assertEquals(p, QueryUtil.getExplainPlan(rs));
-                
-                rs = conn.createStatement().executeQuery(q);
-                assertTrue(rs.next());
-                assertEquals("5SEC", rs.getString(1));
-                assertEquals(1462993520000000000L, rs.getLong(2));
-                assertTrue(rs.next());
-                assertEquals("5SEC", rs.getString(1));
-                assertEquals(1462993515000000000L, rs.getLong(2));
-                assertTrue(rs.next());
-                assertEquals("5SEC", rs.getString(1));
-                assertEquals(1462993510000000000L, rs.getLong(2));
-                assertTrue(rs.next());
-                assertEquals("5SEC", rs.getString(1));
-                assertEquals(1462993505000000000L, rs.getLong(2));
-                assertTrue(rs.next());
-                assertEquals("5SEC", rs.getString(1));
-                assertEquals(1462993490000000000L, rs.getLong(2));
-                assertTrue(rs.next());
-                assertEquals("5SEC", rs.getString(1));
-                assertEquals(1462993485000000000L, rs.getLong(2));
-                assertTrue(rs.next());
-                assertEquals("5SEC", rs.getString(1));
-                assertEquals(1462993480000000000L, rs.getLong(2));
-                assertTrue(rs.next());
-                assertEquals("5SEC", rs.getString(1));
-                assertEquals(1462993475000000000L, rs.getLong(2));
-                assertTrue(rs.next());
-                assertEquals("5SEC", rs.getString(1));
-                assertEquals(1462993470000000000L, rs.getLong(2));
-                assertTrue(rs.next());
-                assertEquals("5SEC", rs.getString(1));
-                assertEquals(1462993430000000000L, rs.getLong(2));
-                assertFalse(rs.next());
-            }
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testBug2961() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        conn.setAutoCommit(true);
-        try {
-            conn.createStatement().execute("CREATE TABLE test2961 (\n" + 
-                    "ACCOUNT_ID VARCHAR NOT NULL,\n" + 
-                    "BUCKET_ID VARCHAR NOT NULL,\n" + 
-                    "OBJECT_ID VARCHAR NOT NULL,\n" + 
-                    "OBJECT_VERSION VARCHAR NOT NULL,\n" + 
-                    "LOC VARCHAR,\n" + 
-                    "CONSTRAINT PK PRIMARY KEY (ACCOUNT_ID, BUCKET_ID, OBJECT_ID, OBJECT_VERSION DESC))");
-            conn.createStatement().execute("UPSERT INTO test2961  (ACCOUNT_ID, BUCKET_ID, OBJECT_ID, OBJECT_VERSION, LOC) VALUES ('acct1', 'bucket1', 'obj1', '1111', 'loc1')");
-            ResultSet rs = conn.createStatement().executeQuery("select ACCOUNT_ID, BUCKET_ID, OBJECT_VERSION  from test2961  WHERE ACCOUNT_ID = 'acct1' and BUCKET_ID = 'bucket1' and OBJECT_VERSION = '1111'");
-            assertTrue(rs.next());
-            rs = conn.createStatement().executeQuery("select ACCOUNT_ID, BUCKET_ID, OBJECT_VERSION  from test2961  WHERE ACCOUNT_ID = 'acct1' and BUCKET_ID = 'bucket1' and OBJECT_ID = 'obj1'");
-            assertTrue(rs.next());
-            rs = conn.createStatement().executeQuery("select ACCOUNT_ID, BUCKET_ID, OBJECT_VERSION  from test2961  WHERE ACCOUNT_ID = 'acct1' and BUCKET_ID = 'bucket1' and OBJECT_VERSION = '1111'  and OBJECT_ID = 'obj1'");
-            assertTrue(rs.next());
-
-            conn.createStatement().execute("UPSERT INTO test2961  (ACCOUNT_ID, BUCKET_ID, OBJECT_ID, OBJECT_VERSION, LOC) VALUES ('acct1', 'bucket1', 'obj1', '2222', 'loc1')");
-            rs = conn.createStatement().executeQuery("SELECT  OBJ.ACCOUNT_ID, OBJ.BUCKET_ID, OBJ.OBJECT_ID, OBJ.OBJECT_VERSION, OBJ.LOC "
-                    + "FROM ( SELECT ACCOUNT_ID, BUCKET_ID, OBJECT_ID, MAX(OBJECT_VERSION) AS MAXVER"
-                    + "       FROM test2961 GROUP BY ACCOUNT_ID, BUCKET_ID, OBJECT_ID) AS X "
-                    + "       INNER JOIN test2961 AS OBJ ON X.ACCOUNT_ID = OBJ.ACCOUNT_ID AND X.BUCKET_ID = OBJ.BUCKET_ID AND X.OBJECT_ID = OBJ.OBJECT_ID AND X.MAXVER = OBJ.OBJECT_VERSION");
-            assertTrue(rs.next());
-            assertEquals("2222", rs.getString(4));
-            assertFalse(rs.next());
-
-            rs = conn.createStatement().executeQuery("SELECT  OBJ.ACCOUNT_ID, OBJ.BUCKET_ID, OBJ.OBJECT_ID, OBJ.OBJECT_VERSION, OBJ.LOC "
-                    + "FROM ( SELECT ACCOUNT_ID, BUCKET_ID, OBJECT_ID, MAX(OBJECT_VERSION) AS MAXVER "
-                    + "       FROM test2961 GROUP BY ACCOUNT_ID, BUCKET_ID, OBJECT_ID) AS X "
-                    + "       INNER JOIN test2961 AS OBJ ON X.ACCOUNT_ID = OBJ.ACCOUNT_ID AND X.OBJECT_ID = OBJ.OBJECT_ID AND X.MAXVER = OBJ.OBJECT_VERSION");
-            assertTrue(rs.next());
-            assertEquals("2222", rs.getString(4));
-            assertFalse(rs.next());
-
-            rs = conn.createStatement().executeQuery("SELECT  OBJ.ACCOUNT_ID, OBJ.BUCKET_ID, OBJ.OBJECT_ID, OBJ.OBJECT_VERSION, OBJ.LOC "
-                    + "FROM ( SELECT ACCOUNT_ID, BUCKET_ID, OBJECT_ID, MAX(OBJECT_VERSION) AS MAXVER "
-                    + "       FROM test2961 GROUP BY ACCOUNT_ID, BUCKET_ID, OBJECT_ID) AS X "
-                    + "       INNER JOIN test2961 AS OBJ ON X.ACCOUNT_ID = OBJ.ACCOUNT_ID AND X.BUCKET_ID = OBJ.BUCKET_ID AND  X.MAXVER = OBJ.OBJECT_VERSION");
-            assertTrue(rs.next());
-            assertEquals("2222", rs.getString(4));
-            assertFalse(rs.next());
-
-            conn.createStatement().execute("UPSERT INTO test2961  (ACCOUNT_ID, BUCKET_ID, OBJECT_ID, OBJECT_VERSION, LOC) VALUES ('acct1', 'bucket1', 'obj2', '1111', 'loc1')");
-            conn.createStatement().execute("UPSERT INTO test2961  (ACCOUNT_ID, BUCKET_ID, OBJECT_ID, OBJECT_VERSION, LOC) VALUES ('acct1', 'bucket1', 'obj3', '1111', 'loc1')");
-            String q = "SELECT  OBJ.ACCOUNT_ID, OBJ.BUCKET_ID, OBJ.OBJECT_ID, OBJ.OBJECT_VERSION, OBJ.LOC "
-                    + "FROM ( SELECT ACCOUNT_ID, BUCKET_ID, OBJECT_ID, MAX(OBJECT_VERSION) AS MAXVER "
-                    + "       FROM test2961 GROUP BY ACCOUNT_ID, BUCKET_ID, OBJECT_ID) AS X "
-                    + "       INNER JOIN test2961 AS OBJ ON X.ACCOUNT_ID = OBJ.ACCOUNT_ID AND X.BUCKET_ID = OBJ.BUCKET_ID AND X.OBJECT_ID = OBJ.OBJECT_ID AND  X.MAXVER = OBJ.OBJECT_VERSION";
-            rs = conn.createStatement().executeQuery(q);
-            assertTrue(rs.next());
-            assertEquals("2222", rs.getString(4));
-            assertTrue(rs.next());
-            assertEquals("1111", rs.getString(4));
-            assertTrue(rs.next());
-            assertEquals("1111", rs.getString(4));
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-}


[09/25] phoenix git commit: PHOENIX-4246 Breakup join related tests into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/ee20a8c9/phoenix-core/src/it/java/org/apache/phoenix/end2end/SubqueryIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SubqueryIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SubqueryIT.java
deleted file mode 100644
index 684c3c2..0000000
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SubqueryIT.java
+++ /dev/null
@@ -1,788 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.phoenix.end2end;
-
-import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.Collection;
-import java.util.List;
-import java.util.Properties;
-
-import org.apache.phoenix.util.PropertiesUtil;
-import org.apache.phoenix.util.QueryUtil;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import com.google.common.collect.Lists;
-
-@RunWith(Parameterized.class)
-public class SubqueryIT extends BaseJoinIT {
-    public SubqueryIT(String[] indexDDL, String[] plans) {
-        super(indexDDL, plans);
-    }
-    
-    @Parameters
-    public static Collection<Object> data() {
-        List<Object> testCases = Lists.newArrayList();
-        testCases.add(new String[][] {
-                {}, {
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    SERVER SORTED BY \\[I.NAME\\]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "    SKIP-SCAN-JOIN TABLE 1\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + " \\['000000000000001'\\] - \\[\\*\\]\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    DYNAMIC SERVER FILTER BY \"I.item_id\" IN \\(\\$\\d+.\\$\\d+\\)",
-                
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "    SERVER SORTED BY [I.NAME]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    PARALLEL SEMI-JOIN TABLE 1(DELAYED EVALUATION) (SKIP MERGE)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
-                "        CLIENT MERGE SORT",
-                
-                "CLIENT PARALLEL 4-WAY FULL SCAN OVER " + JOIN_COITEM_TABLE_FULL_NAME + "\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\".+.item_id\", .+.NAME\\]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "            PARALLEL ANTI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
-                "                CLIENT MERGE SORT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 1\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\".+.item_id\", .+.NAME\\]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "            SKIP-SCAN-JOIN TABLE 0\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
-                "                CLIENT MERGE SORT\n" +
-                "            DYNAMIC SERVER FILTER BY \"" + JOIN_ITEM_TABLE_FULL_NAME + ".item_id\" IN \\(\\$\\d+.\\$\\d+\\)\n" +
-                "    AFTER-JOIN SERVER FILTER BY \\(\\$\\d+.\\$\\d+ IS NOT NULL OR \\$\\d+.\\$\\d+ IS NOT NULL\\)",
-                
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    SERVER SORTED BY [I.NAME]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL ANTI-JOIN TABLE 0 (SKIP MERGE)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
-                "        CLIENT MERGE SORT",
-                
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + "\n" +
-                "    SKIP-SCAN-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"O.customer_id\"\\]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "            PARALLEL INNER-JOIN TABLE 0\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            PARALLEL LEFT-JOIN TABLE 1\\(DELAYED EVALUATION\\)\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
-                "                CLIENT MERGE SORT\n" +
-                "            DYNAMIC SERVER FILTER BY \"I.item_id\" IN \\(\"O.item_id\"\\)\n" +
-                "            AFTER-JOIN SERVER FILTER BY \\(I.NAME = 'T2' OR O.QUANTITY > \\$\\d+.\\$\\d+\\)\n" +
-                "    DYNAMIC SERVER FILTER BY \"" + JOIN_CUSTOMER_TABLE_FULL_NAME + ".customer_id\" IN \\(\\$\\d+.\\$\\d+\\)"
-                }});
-        testCases.add(new String[][] {
-                {
-                "CREATE INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
-                "CREATE INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
-                "CREATE INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
-                }, {
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_supplier\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "    PARALLEL SEMI-JOIN TABLE 1 \\(SKIP MERGE\\)\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + " \\['000000000000001'\\] - \\[\\*\\]\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
-                "        CLIENT MERGE SORT",
-                
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_supplier\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "    SERVER SORTED BY [\"I.0:NAME\"]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "    PARALLEL SEMI-JOIN TABLE 1(DELAYED EVALUATION) (SKIP MERGE)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
-                "        CLIENT MERGE SORT",
-                
-                "CLIENT PARALLEL 4-WAY FULL SCAN OVER " + JOIN_COITEM_TABLE_FULL_NAME + "\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" +
-                "            SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY \\[\".+.:item_id\", \".+.0:NAME\"\\]\n" +
-                "            PARALLEL ANTI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
-                "                CLIENT MERGE SORT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 1\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" +
-                "            SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY \\[\".+.:item_id\", \".+.0:NAME\"\\]\n" +
-                "            PARALLEL SEMI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
-                "                CLIENT MERGE SORT\n" +
-                "    AFTER-JOIN SERVER FILTER BY \\(\\$\\d+.\\$\\d+ IS NOT NULL OR \\$\\d+.\\$\\d+ IS NOT NULL\\)",
-                
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    PARALLEL ANTI-JOIN TABLE 0 (SKIP MERGE)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
-                "        CLIENT MERGE SORT",
-                
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_customer\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "    PARALLEL SEMI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"O.customer_id\"\\]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "            PARALLEL INNER-JOIN TABLE 0\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            PARALLEL LEFT-JOIN TABLE 1\\(DELAYED EVALUATION\\)\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
-                "                CLIENT MERGE SORT\n" +
-                "            AFTER-JOIN SERVER FILTER BY \\(\"I.0:NAME\" = 'T2' OR O.QUANTITY > \\$\\d+.\\$\\d+\\)"
-                }});
-        testCases.add(new String[][] {
-                {
-                "CREATE LOCAL INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
-                "CREATE LOCAL INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
-                "CREATE LOCAL INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
-                }, {
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " \\[1\\]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + " \\[1\\]\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "        CLIENT MERGE SORT\n" +
-                "    PARALLEL SEMI-JOIN TABLE 1 \\(SKIP MERGE\\)\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + " \\['000000000000001'\\] - \\[\\*\\]\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    DYNAMIC SERVER FILTER BY \"I.:item_id\" IN \\(\\$\\d+.\\$\\d+\\)",
-                            
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + " [1]\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "    SERVER SORTED BY [\"I.0:NAME\"]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    PARALLEL SEMI-JOIN TABLE 1(DELAYED EVALUATION) (SKIP MERGE)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
-                "        CLIENT MERGE SORT",
-
-                "CLIENT PARALLEL 4-WAY FULL SCAN OVER " + JOIN_COITEM_TABLE_FULL_NAME + "\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " \\[1\\]\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" +
-                "            SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY \\[\".+.:item_id\", \".+.0:NAME\"\\]\n" +
-                "        CLIENT MERGE SORT\n" + 
-                "            PARALLEL ANTI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
-                "                CLIENT MERGE SORT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 1\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " \\[1\\]\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" +
-                "            SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY \\[\".+.:item_id\", \".+.0:NAME\"\\]\n" +
-                "        CLIENT MERGE SORT\n" + 
-                "            PARALLEL SEMI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
-                "                CLIENT MERGE SORT\n" +
-                "            DYNAMIC SERVER FILTER BY \"" + JOIN_SCHEMA + ".idx_item.:item_id\" IN \\(\\$\\d+.\\$\\d+\\)\n" +
-                "    AFTER-JOIN SERVER FILTER BY \\(\\$\\d+.\\$\\d+ IS NOT NULL OR \\$\\d+.\\$\\d+ IS NOT NULL\\)",
-                
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL ANTI-JOIN TABLE 0 (SKIP MERGE)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
-                "        CLIENT MERGE SORT",
-                
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + " \\[1\\]\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL SEMI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " \\[1\\]\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"O.customer_id\"\\]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "            PARALLEL INNER-JOIN TABLE 0\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            PARALLEL LEFT-JOIN TABLE 1\\(DELAYED EVALUATION\\)\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
-                "                CLIENT MERGE SORT\n" +
-                "            DYNAMIC SERVER FILTER BY \"I.:item_id\" IN \\(\"O.item_id\"\\)\n" +
-                "            AFTER-JOIN SERVER FILTER BY \\(\"I.0:NAME\" = 'T2' OR O.QUANTITY > \\$\\d+.\\$\\d+\\)\n" +
-                "    DYNAMIC SERVER FILTER BY \"" + JOIN_SCHEMA + ".idx_customer.:customer_id\" IN \\(\\$\\d+.\\$\\d+\\)"
-                }});
-        return testCases;
-    }
-    
-    
-    @Test
-    public void testNonCorrelatedSubquery() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        String tableName5 = getTableName(conn, JOIN_COITEM_TABLE_FULL_NAME);
-        try {            
-            String query = "SELECT \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" >= ALL (SELECT \"item_id\" FROM " + tableName4 + ") ORDER BY name";
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "invalid001");
-            assertEquals(rs.getString(2), "INVALID-1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "T6");
-
-            assertFalse(rs.next());
-            
-            query = "SELECT \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" < ANY (SELECT \"item_id\" FROM " + tableName4 + ")";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "T2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "T3");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-
-            assertFalse(rs.next());
-            
-            query = "SELECT \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" < (SELECT max(\"item_id\") FROM " + tableName4 + ")";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "T2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "T3");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-
-            assertFalse(rs.next());
-            
-            query = "SELECT * FROM " + tableName5 + " WHERE (item_id, item_name) != ALL (SELECT \"item_id\", name FROM " + tableName1 + ")";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "T4");
-            assertEquals(rs.getString(3), "0000000005");
-            assertEquals(rs.getString(4), "T5");
-
-            assertFalse(rs.next());
-            
-            query = "SELECT * FROM " + tableName5 + " WHERE EXISTS (SELECT \"item_id\", name FROM " + tableName1 + ")";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "0000000002");
-            assertEquals(rs.getString(4), "T3");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "T4");
-            assertEquals(rs.getString(3), "0000000005");
-            assertEquals(rs.getString(4), "T5");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertEquals(rs.getString(3), "0000000003");
-            assertEquals(rs.getString(4), "T3");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "T1");
-
-            assertFalse(rs.next());
-            
-            query = "SELECT \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" < (SELECT \"item_id\" FROM " + tableName4 + ")";
-            statement = conn.prepareStatement(query);
-            try {
-                rs = statement.executeQuery();
-                fail("Should have got Exception.");
-            } catch (SQLException e) {
-            }
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testInSubquery() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        String tableName5 = getTableName(conn, JOIN_COITEM_TABLE_FULL_NAME);
-        try {
-            String query = "SELECT \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" IN (SELECT \"item_id\" FROM " + tableName4 + ") ORDER BY name";
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "T2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "T3");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "T6");
-
-            assertFalse(rs.next());
-
-            query = "SELECT \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" NOT IN (SELECT \"item_id\" FROM " + tableName4 + ") ORDER BY name";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "invalid001");
-            assertEquals(rs.getString(2), "INVALID-1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-
-            assertFalse(rs.next());
-            
-            query = "SELECT i.\"item_id\", s.name FROM " + tableName1 + " i JOIN " + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\" WHERE i.\"item_id\" IN (SELECT \"item_id\" FROM " + tableName4 + " WHERE \"order_id\" > '000000000000001') ORDER BY i.name";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "S2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "S6");
-
-            assertFalse(rs.next());
-            
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            String plan = QueryUtil.getExplainPlan(rs);
-            assertPlansMatch(plans[0], plan);
-            
-            query = "SELECT i.\"item_id\", s.name FROM " + tableName2 + " s LEFT JOIN " + tableName1 + " i ON i.\"supplier_id\" = s.\"supplier_id\" WHERE i.\"item_id\" IN (SELECT \"item_id\" FROM " + tableName4 + ") ORDER BY i.name";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "S2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "S6");
-
-            assertFalse(rs.next());
-            
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            assertPlansEqual(plans[1], QueryUtil.getExplainPlan(rs));
-           
-            query = "SELECT * FROM " + tableName5 + " WHERE (item_id, item_name) IN (SELECT \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" NOT IN (SELECT \"item_id\" FROM " + tableName4 + "))"
-                    + " OR (co_item_id, co_item_name) IN (SELECT \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" IN (SELECT \"item_id\" FROM " + tableName4 + "))";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertEquals(rs.getString(3), "0000000003");
-            assertEquals(rs.getString(4), "T3");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "T1");
-
-            assertFalse(rs.next());
-            
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            plan = QueryUtil.getExplainPlan(rs);
-            assertPlansMatch(plans[2], plan);
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testExistsSubquery() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        String tableName5 = getTableName(conn, JOIN_COITEM_TABLE_FULL_NAME);
-        try {
-            String query = "SELECT \"item_id\", name FROM " + tableName1 + " i WHERE NOT EXISTS (SELECT 1 FROM " + tableName4 + " o WHERE o.\"item_id\" = i.\"item_id\") ORDER BY name";
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "invalid001");
-            assertEquals(rs.getString(2), "INVALID-1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-
-            assertFalse(rs.next());
-            
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            assertPlansEqual(plans[3], QueryUtil.getExplainPlan(rs));
-            
-            query = "SELECT * FROM " + tableName5 + " co WHERE EXISTS (SELECT 1 FROM " + tableName1 + " i WHERE NOT EXISTS (SELECT 1 FROM " + tableName4 + " WHERE \"item_id\" = i.\"item_id\") AND co.item_id = \"item_id\" AND name = co.item_name)"
-                    + " OR EXISTS (SELECT 1 FROM " + tableName1 + " WHERE \"item_id\" IN (SELECT \"item_id\" FROM " + tableName4 + ") AND co.co_item_id = \"item_id\" AND name = co.co_item_name)";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertEquals(rs.getString(3), "0000000003");
-            assertEquals(rs.getString(4), "T3");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "T1");
-
-            assertFalse(rs.next());
-            
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            String plan = QueryUtil.getExplainPlan(rs);
-            assertPlansMatch(plans[2], plan);
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testComparisonSubquery() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        final Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName3 = getTableName(conn, JOIN_CUSTOMER_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        try {
-            String query = "SELECT \"order_id\", name FROM " + tableName4 + 
-                    " o JOIN " + tableName1 + 
-                    " i ON o.\"item_id\" = i.\"item_id\" WHERE quantity = (SELECT max(quantity) FROM " + 
-                    tableName4 + " q WHERE o.\"item_id\" = q.\"item_id\")";
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000003");
-            assertEquals(rs.getString(2), "T2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000004");
-            assertEquals(rs.getString(2), "T6");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000005");
-            assertEquals(rs.getString(2), "T3");
-
-            assertFalse(rs.next());
-
-            query = "SELECT \"order_id\", name FROM " + tableName4 + 
-                    " o JOIN " + tableName1 + 
-                    " i ON o.\"item_id\" = i.\"item_id\" WHERE quantity = (SELECT max(quantity) FROM " + 
-                    tableName1 + " i2 JOIN " + tableName4 + 
-                    " q ON i2.\"item_id\" = q.\"item_id\" WHERE o.\"item_id\" = i2.\"item_id\")";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000003");
-            assertEquals(rs.getString(2), "T2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000004");
-            assertEquals(rs.getString(2), "T6");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000005");
-            assertEquals(rs.getString(2), "T3");
-
-            assertFalse(rs.next());
-
-            query = "SELECT name from " + tableName3 + 
-                    " WHERE \"customer_id\" IN (SELECT \"customer_id\" FROM " + 
-                    tableName1 + " i JOIN " + tableName4 + 
-                    " o ON o.\"item_id\" = i.\"item_id\" WHERE i.name = 'T2' OR quantity > (SELECT avg(quantity) FROM " + 
-                    tableName4 + " q WHERE o.\"item_id\" = q.\"item_id\"))";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "C2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "C4");
-
-            assertFalse(rs.next());
-            
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            String plan = QueryUtil.getExplainPlan(rs);
-            assertPlansMatch(plans[4], plan);
-
-            query = "SELECT \"order_id\" FROM " + tableName4 + 
-                    " o WHERE quantity = (SELECT quantity FROM " + tableName4 + 
-                    " WHERE o.\"item_id\" = \"item_id\" AND \"order_id\" != '000000000000004')";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000001");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000002");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000003");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000005");
-
-            assertFalse(rs.next());
-
-            query = "SELECT \"order_id\" FROM " + tableName4 + 
-                    " o WHERE quantity = (SELECT quantity FROM " + tableName4 + 
-                    " WHERE o.\"item_id\" = \"item_id\" AND \"order_id\" != '000000000000003')";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            try {
-                while(rs.next());
-                fail("Should have got exception.");
-            } catch (SQLException e) {                
-            }
-
-            query = "SELECT \"order_id\" FROM " + tableName4 + 
-                    " o WHERE quantity = (SELECT max(quantity) FROM " + tableName4 + 
-                    " WHERE o.\"item_id\" = \"item_id\" AND \"order_id\" != '000000000000004' GROUP BY \"order_id\")";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000001");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000002");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000003");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000005");
-
-            assertFalse(rs.next());
-
-            query = "SELECT \"order_id\" FROM " + tableName4 + 
-                    " o WHERE quantity = (SELECT max(quantity) FROM " + tableName4 + 
-                    " WHERE o.\"item_id\" = \"item_id\" AND \"order_id\" != '000000000000003' GROUP BY \"order_id\")";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            try {
-                while(rs.next());
-                fail("Should have got exception.");
-            } catch (SQLException e) {                
-            }
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testAnyAllComparisonSubquery() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        try {
-            String query = "SELECT \"order_id\", name FROM " + tableName4 + " o JOIN " + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" WHERE quantity = ALL(SELECT quantity FROM " + tableName4 + " q WHERE o.\"item_id\" = q.\"item_id\")";
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000003");
-            assertEquals(rs.getString(2), "T2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000005");
-            assertEquals(rs.getString(2), "T3");
-
-            assertFalse(rs.next());
-            
-            query = "SELECT \"order_id\", name FROM " + tableName4 + " o JOIN " + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" WHERE quantity != ALL(SELECT max(quantity) FROM " + tableName4 + " q WHERE o.\"item_id\" = q.\"item_id\")";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000002");
-            assertEquals(rs.getString(2), "T6");
-
-            assertFalse(rs.next());
-            
-            query = "SELECT \"order_id\", name FROM " + tableName4 + " o JOIN " + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" WHERE quantity != ANY(SELECT quantity FROM " + tableName4 + " q WHERE o.\"item_id\" = q.\"item_id\" GROUP BY quantity)";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000002");
-            assertEquals(rs.getString(2), "T6");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000004");
-            assertEquals(rs.getString(2), "T6");
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testSubqueryWithUpsert() throws Exception {
-        String tempTable = generateUniqueName();
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        conn.setAutoCommit(true);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        try {            
-            conn.createStatement().execute("CREATE TABLE " + tempTable 
-                    + "   (item_id varchar not null primary key, " 
-                    + "    name varchar)");
-            conn.createStatement().execute("UPSERT INTO " + tempTable + "(item_id, name)"
-                    + "   SELECT \"item_id\", name FROM " + tableName1 
-                    + "   WHERE \"item_id\" NOT IN (SELECT \"item_id\" FROM " + tableName4 + ")");
-            
-            String query = "SELECT name FROM " + tempTable + " ORDER BY item_id";
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "T4");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "T5");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "INVALID-1");
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testSubqueryWithDelete() throws Exception {
-        String tempTable = generateUniqueName();
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        conn.setAutoCommit(true);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        try {            
-            conn.createStatement().execute("CREATE TABLE " + tempTable 
-                    + "   (item_id varchar not null primary key, " 
-                    + "    name varchar)");
-            conn.createStatement().execute("UPSERT INTO " + tempTable + "(item_id, name)"
-                    + "   SELECT \"item_id\", name FROM " + tableName1);
-
-            String query = "SELECT count(*) FROM " + tableName1;
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getInt(1), 7);
-            assertFalse(rs.next());
-            
-            conn.createStatement().execute("DELETE FROM " + tempTable + " WHERE item_id IN ("
-                    + "   SELECT \"item_id\" FROM " + tableName4 + ")");
-            
-            query = "SELECT name FROM " + tempTable + " ORDER BY item_id";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "T4");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "T5");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "INVALID-1");
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-}
-

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ee20a8c9/phoenix-core/src/it/java/org/apache/phoenix/end2end/SubqueryUsingSortMergeJoinIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SubqueryUsingSortMergeJoinIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SubqueryUsingSortMergeJoinIT.java
deleted file mode 100644
index 3e64169..0000000
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SubqueryUsingSortMergeJoinIT.java
+++ /dev/null
@@ -1,566 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.phoenix.end2end;
-
-import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.Collection;
-import java.util.List;
-import java.util.Properties;
-
-import org.apache.phoenix.util.PropertiesUtil;
-import org.apache.phoenix.util.QueryUtil;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import com.google.common.collect.Lists;
-
-@RunWith(Parameterized.class)
-public class SubqueryUsingSortMergeJoinIT extends BaseJoinIT {
-
-    public SubqueryUsingSortMergeJoinIT(String[] indexDDL, String[] plans) {
-        super(indexDDL, plans);
-    }
-    
-    @Parameters
-    public static Collection<Object> data() {
-        List<Object> testCases = Lists.newArrayList();
-        testCases.add(new String[][] {
-                {}, {
-                "SORT-MERGE-JOIN (SEMI) TABLES\n" +
-                "    SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "            SERVER SORTED BY [\"I.supplier_id\"]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    AND\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "    CLIENT SORTED BY [\"I.item_id\"]\n" +
-                "AND (SKIP MERGE)\n" +
-                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + " ['000000000000001'] - [*]\n" +
-                "        SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "CLIENT SORTED BY [I.NAME]",
-
-                "SORT-MERGE-JOIN \\(LEFT\\) TABLES\n" +
-                "    SORT-MERGE-JOIN \\(LEFT\\) TABLES\n" +
-                "        CLIENT PARALLEL 4-WAY FULL SCAN OVER " + JOIN_COITEM_TABLE_FULL_NAME + "\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    AND\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\".+.item_id\", .+.NAME\\]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "            PARALLEL ANTI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"]\\\n" +
-                "                CLIENT MERGE SORT\n" +
-                "    CLIENT SORTED BY \\[.*.CO_ITEM_ID, .*.CO_ITEM_NAME\\]\n" +
-                "AND\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "        SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\".+.item_id\", .+.NAME\\]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "        SKIP-SCAN-JOIN TABLE 0\n" +
-                "            CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "                SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
-                "            CLIENT MERGE SORT\n" +
-                "        DYNAMIC SERVER FILTER BY \"" + JOIN_ITEM_TABLE_FULL_NAME + ".item_id\" IN \\(\\$\\d+.\\$\\d+\\)\n" +
-                "CLIENT FILTER BY \\(\\$\\d+.\\$\\d+ IS NOT NULL OR \\$\\d+.\\$\\d+ IS NOT NULL\\)",            
-
-                "SORT-MERGE-JOIN \\(SEMI\\) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + "\n" +
-                "AND \\(SKIP MERGE\\)\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "        SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"O.customer_id\"\\]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "        PARALLEL INNER-JOIN TABLE 0\n" +
-                "            CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "        PARALLEL LEFT-JOIN TABLE 1\\(DELAYED EVALUATION\\)\n" +
-                "            CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "                SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
-                "            CLIENT MERGE SORT\n" +
-                "        DYNAMIC SERVER FILTER BY \"I.item_id\" IN \\(\"O.item_id\"\\)\n" +
-                "        AFTER-JOIN SERVER FILTER BY \\(I.NAME = 'T2' OR O.QUANTITY > \\$\\d+.\\$\\d+\\)",
-                }});
-        testCases.add(new String[][] {
-                {
-                "CREATE INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
-                "CREATE INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
-                "CREATE INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
-                }, {
-                "SORT-MERGE-JOIN (SEMI) TABLES\n" +
-                "    SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "            SERVER SORTED BY [\"I.0:supplier_id\"]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    AND\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_supplier\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" +
-                "            SERVER SORTED BY [\"S.:supplier_id\"]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    CLIENT SORTED BY [\"I.:item_id\"]\n" +
-                "AND (SKIP MERGE)\n" +
-                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + " ['000000000000001'] - [*]\n" +
-                "        SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "CLIENT SORTED BY [\"I.0:NAME\"]",
-
-                "SORT-MERGE-JOIN \\(LEFT\\) TABLES\n" +
-                "    SORT-MERGE-JOIN \\(LEFT\\) TABLES\n" +
-                "        CLIENT PARALLEL 4-WAY FULL SCAN OVER " + JOIN_COITEM_TABLE_FULL_NAME + "\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    AND\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" +
-                "            SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY \\[\".+.:item_id\", \".+.0:NAME\"\\]\n" +
-                "            PARALLEL ANTI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
-                "                CLIENT MERGE SORT\n" +
-                "    CLIENT SORTED BY \\[.*.CO_ITEM_ID, .*.CO_ITEM_NAME\\]\n" +
-                "AND\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY \\[\".+.:item_id\", \".+.0:NAME\"\\]\n" +
-                "        PARALLEL SEMI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
-                "            CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "                SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
-                "            CLIENT MERGE SORT\n" +
-                "CLIENT FILTER BY \\(\\$\\d+.\\$\\d+ IS NOT NULL OR \\$\\d+.\\$\\d+ IS NOT NULL\\)",
-                
-                "SORT-MERGE-JOIN \\(SEMI\\) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_customer\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        SERVER SORTED BY \\[\"Join.idx_customer.:customer_id\"\\]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "AND \\(SKIP MERGE\\)\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"O.customer_id\"\\]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "        PARALLEL INNER-JOIN TABLE 0\n" +
-                "            CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "        PARALLEL LEFT-JOIN TABLE 1\\(DELAYED EVALUATION\\)\n" +
-                "            CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "                SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
-                "            CLIENT MERGE SORT\n" +
-                "        AFTER-JOIN SERVER FILTER BY \\(\"I.0:NAME\" = 'T2' OR O.QUANTITY > \\$\\d+.\\$\\d+\\)",
-                }});
-        testCases.add(new String[][] {
-                {
-                "CREATE LOCAL INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
-                "CREATE LOCAL INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
-                "CREATE LOCAL INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
-                }, {
-                "SORT-MERGE-JOIN (SEMI) TABLES\n" +
-                "    SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "            SERVER SORTED BY [\"I.0:supplier_id\"]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    AND\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + " [1]\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" +
-                "            SERVER SORTED BY [\"S.:supplier_id\"]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    CLIENT SORTED BY [\"I.:item_id\"]\n" +
-                "AND (SKIP MERGE)\n" +
-                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + " ['000000000000001'] - [*]\n" +
-                "        SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "CLIENT SORTED BY [\"I.0:NAME\"]",
-
-                "SORT-MERGE-JOIN \\(LEFT\\) TABLES\n" +
-                "    SORT-MERGE-JOIN \\(LEFT\\) TABLES\n" +
-                "        CLIENT PARALLEL 4-WAY FULL SCAN OVER " + JOIN_COITEM_TABLE_FULL_NAME + "\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    AND\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " \\[1\\]\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" +
-                "            SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY \\[\".+.:item_id\", \".+.0:NAME\"\\]\n" +
-                "        CLIENT MERGE SORT\n" + 
-                "            PARALLEL ANTI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
-                "                CLIENT MERGE SORT\n" +
-                "    CLIENT SORTED BY \\[.*.CO_ITEM_ID, .*.CO_ITEM_NAME\\]\n" +
-                "AND\n" +
-                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " \\[1\\]\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY \\[\".+.:item_id\", \".+.0:NAME\"\\]\n" +
-                "    CLIENT MERGE SORT\n" + 
-                "        PARALLEL SEMI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
-                "            CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "                SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
-                "            CLIENT MERGE SORT\n" +
-                "        DYNAMIC SERVER FILTER BY \"" + JOIN_SCHEMA + ".idx_item.:item_id\" IN \\(\\$\\d+.\\$\\d+\\)\n" +
-                "CLIENT FILTER BY \\(\\$\\d+.\\$\\d+ IS NOT NULL OR \\$\\d+.\\$\\d+ IS NOT NULL\\)",
-                
-                "SORT-MERGE-JOIN \\(SEMI\\) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + " \\[1\\]\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        SERVER SORTED BY \\[\"Join.idx_customer.:customer_id\"\\]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "AND \\(SKIP MERGE\\)\n" +
-                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " \\[1\\]\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"O.customer_id\"\\]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "        PARALLEL INNER-JOIN TABLE 0\n" +
-                "            CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "        PARALLEL LEFT-JOIN TABLE 1\\(DELAYED EVALUATION\\)\n" +
-                "            CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "                SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
-                "            CLIENT MERGE SORT\n" +
-                "        DYNAMIC SERVER FILTER BY \"I.:item_id\" IN \\(\"O.item_id\"\\)\n" +
-                "        AFTER-JOIN SERVER FILTER BY \\(\"I.0:NAME\" = 'T2' OR O.QUANTITY > \\$\\d+.\\$\\d+\\)",
-                }});
-        return testCases;
-    }    
-
-    @Test
-    public void testInSubquery() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        String tableName5 = getTableName(conn, JOIN_COITEM_TABLE_FULL_NAME);
-        try {
-            String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" IN (SELECT \"item_id\" FROM " + tableName4 + ") ORDER BY name";
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "T2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "T3");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "T6");
-
-            assertFalse(rs.next());
-
-            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" NOT IN (SELECT \"item_id\" FROM " + tableName4 + ") ORDER BY name";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "invalid001");
-            assertEquals(rs.getString(2), "INVALID-1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-
-            assertFalse(rs.next());
-            
-            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ i.\"item_id\", s.name FROM " + tableName1 + " i JOIN " + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\" WHERE i.\"item_id\" IN (SELECT \"item_id\" FROM " + tableName4 + " WHERE \"order_id\" > '000000000000001') ORDER BY i.name";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "S2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "S6");
-
-            assertFalse(rs.next());
-            
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            assertPlansEqual(plans[0], QueryUtil.getExplainPlan(rs));
-            
-            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ i.\"item_id\", s.name FROM " + tableName2 + " s LEFT JOIN " + tableName1 + " i ON i.\"supplier_id\" = s.\"supplier_id\" WHERE i.\"item_id\" IN (SELECT \"item_id\" FROM " + tableName4 + ") ORDER BY i.name";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "S2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "S6");
-
-            assertFalse(rs.next());
-           
-            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ * FROM " + tableName5 + " WHERE (item_id, item_name) IN (SELECT \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" NOT IN (SELECT \"item_id\" FROM " + tableName4 + "))"
-                    + " OR (co_item_id, co_item_name) IN (SELECT \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" IN (SELECT \"item_id\" FROM " + tableName4 + "))";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "T1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertEquals(rs.getString(3), "0000000003");
-            assertEquals(rs.getString(4), "T3");
-
-            assertFalse(rs.next());
-            
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            String plan = QueryUtil.getExplainPlan(rs);
-            assertPlansMatch(plans[1], plan);
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testExistsSubquery() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        String tableName5 = getTableName(conn, JOIN_COITEM_TABLE_FULL_NAME);
-        try {
-            String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"item_id\", name FROM " + tableName1 + " i WHERE NOT EXISTS (SELECT 1 FROM " + tableName4 + " o WHERE o.\"item_id\" = i.\"item_id\") ORDER BY name";
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "invalid001");
-            assertEquals(rs.getString(2), "INVALID-1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-
-            assertFalse(rs.next());
-            
-            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ * FROM " + tableName5 + " co WHERE EXISTS (SELECT 1 FROM " + tableName1 + " i WHERE NOT EXISTS (SELECT 1 FROM " + tableName4 + " WHERE \"item_id\" = i.\"item_id\") AND co.item_id = \"item_id\" AND name = co.item_name)"
-                    + " OR EXISTS (SELECT 1 FROM " + tableName1 + " WHERE \"item_id\" IN (SELECT \"item_id\" FROM " + tableName4 + ") AND co.co_item_id = \"item_id\" AND name = co.co_item_name)";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "T1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertEquals(rs.getString(3), "0000000003");
-            assertEquals(rs.getString(4), "T3");
-
-            assertFalse(rs.next());
-            
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            String plan = QueryUtil.getExplainPlan(rs);
-            assertPlansMatch(plans[1], plan);
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testComparisonSubquery() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName3 = getTableName(conn, JOIN_CUSTOMER_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        try {
-            String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", name FROM " + tableName4 + " o JOIN " + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" WHERE quantity = (SELECT max(quantity) FROM " + tableName4 + " q WHERE o.\"item_id\" = q.\"item_id\")";
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000003");
-            assertEquals(rs.getString(2), "T2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000005");
-            assertEquals(rs.getString(2), "T3");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000004");
-            assertEquals(rs.getString(2), "T6");
-
-            assertFalse(rs.next());
-
-            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ name from " + tableName3 + " WHERE \"customer_id\" IN (SELECT \"customer_id\" FROM " + tableName1 + " i JOIN " + tableName4 + " o ON o.\"item_id\" = i.\"item_id\" WHERE i.name = 'T2' OR quantity > (SELECT avg(quantity) FROM " + tableName4 + " q WHERE o.\"item_id\" = q.\"item_id\"))";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "C2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "C4");
-
-            assertFalse(rs.next());
-            
-            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            String plan = QueryUtil.getExplainPlan(rs);
-            assertPlansMatch(plans[2], plan);
-
-            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\" FROM " + tableName4 + " o WHERE quantity = (SELECT quantity FROM " + tableName4 + " WHERE o.\"item_id\" = \"item_id\" AND \"order_id\" != '000000000000004')";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000001");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000003");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000005");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000002");
-
-            assertFalse(rs.next());
-
-            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\" FROM " + tableName4 + " o WHERE quantity = (SELECT quantity FROM " + tableName4 + " WHERE o.\"item_id\" = \"item_id\" AND \"order_id\" != '000000000000003')";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            try {
-                while(rs.next());
-                fail("Should have got exception.");
-            } catch (SQLException e) {                
-            }
-
-            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\" FROM " + tableName4 + " o WHERE quantity = (SELECT max(quantity) FROM " + tableName4 + " WHERE o.\"item_id\" = \"item_id\" AND \"order_id\" != '000000000000004' GROUP BY \"order_id\")";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000001");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000003");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000005");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000002");
-
-            assertFalse(rs.next());
-
-            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\" FROM " + tableName4 + " o WHERE quantity = (SELECT max(quantity) FROM " + tableName4 + " WHERE o.\"item_id\" = \"item_id\" AND \"order_id\" != '000000000000003' GROUP BY \"order_id\")";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            try {
-                while(rs.next());
-                fail("Should have got exception.");
-            } catch (SQLException e) {                
-            }
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testAnyAllComparisonSubquery() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        try {
-            String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", name FROM " + tableName4 + " o JOIN " + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" WHERE quantity = ALL(SELECT quantity FROM " + tableName4 + " q WHERE o.\"item_id\" = q.\"item_id\")";
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000003");
-            assertEquals(rs.getString(2), "T2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000005");
-            assertEquals(rs.getString(2), "T3");
-
-            assertFalse(rs.next());
-            
-            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", name FROM " + tableName4 + " o JOIN " + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" WHERE quantity != ALL(SELECT max(quantity) FROM " + tableName4 + " q WHERE o.\"item_id\" = q.\"item_id\")";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000002");
-            assertEquals(rs.getString(2), "T6");
-
-            assertFalse(rs.next());
-            
-            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", name FROM " + tableName4 + " o JOIN " + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" WHERE quantity != ANY(SELECT quantity FROM " + tableName4 + " q WHERE o.\"item_id\" = q.\"item_id\" GROUP BY quantity)";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000002");
-            assertEquals(rs.getString(2), "T6");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000004");
-            assertEquals(rs.getString(2), "T6");
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testSubqueryWithUpsert() throws Exception {
-        String tempTable = generateUniqueName();
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        conn.setAutoCommit(true);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        try {            
-            conn.createStatement().execute("CREATE TABLE " + tempTable 
-                    + "   (item_id varchar not null primary key, " 
-                    + "    name varchar)");
-            conn.createStatement().execute("UPSERT /*+ USE_SORT_MERGE_JOIN*/ INTO " + tempTable + "(item_id, name)"
-                    + "   SELECT \"item_id\", name FROM " + tableName1 
-                    + "   WHERE \"item_id\" NOT IN (SELECT \"item_id\" FROM " + tableName4 + ")");
-            
-            String query = "SELECT name FROM " + tempTable + " ORDER BY item_id";
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "T4");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "T5");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "INVALID-1");
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-}
-
-


[15/25] phoenix git commit: PHOENIX-4246 Breakup join related tests into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
PHOENIX-4246 Breakup join related tests into several integration tests so as not to create too many tables in one test


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

Branch: refs/heads/master
Commit: 834133a194a75c9231f9773b0bd89fd752dcf5dc
Parents: ee20a8c
Author: James Taylor <jt...@salesforce.com>
Authored: Thu Sep 28 15:06:35 2017 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Thu Sep 28 17:51:57 2017 -0700

----------------------------------------------------------------------
 .../end2end/join/HashJoinGlobalIndexIT.java     |   2 +-
 .../end2end/join/HashJoinLocalIndexIT.java      |   2 +-
 .../phoenix/end2end/join/HashJoinNoIndexIT.java |   2 +-
 .../join/SortMergeJoinGlobalIndexIT.java        |  84 ++++++++++++
 .../phoenix/end2end/join/SortMergeJoinIT.java   | 134 +------------------
 .../end2end/join/SortMergeJoinLocalIndexIT.java |  84 ++++++++++++
 .../end2end/join/SortMergeJoinNoIndexIT.java    |  66 +++++++++
 7 files changed, 238 insertions(+), 136 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/834133a1/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinGlobalIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinGlobalIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinGlobalIndexIT.java
index 76944a6..a725be7 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinGlobalIndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinGlobalIndexIT.java
@@ -30,7 +30,7 @@ public class HashJoinGlobalIndexIT extends HashJoinIT {
         super(indexDDL, plans);
     }
 
-    @Parameters
+    @Parameters(name="HashJoinGlobalIndexIT_{index}") // name is used by failsafe as file name in reports
     public static Collection<Object> data() {
         List<Object> testCases = Lists.newArrayList();
         testCases.add(new String[][] {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/834133a1/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinLocalIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinLocalIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinLocalIndexIT.java
index dcc454f..59ddce9 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinLocalIndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinLocalIndexIT.java
@@ -46,7 +46,7 @@ public class HashJoinLocalIndexIT extends HashJoinIT {
         super(indexDDL, plans);
     }
     
-    @Parameters
+    @Parameters(name="HashJoinLocalIndexIT_{index}") // name is used by failsafe as file name in reports
     public static Collection<Object> data() {
         List<Object> testCases = Lists.newArrayList();
         testCases.add(new String[][] {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/834133a1/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinNoIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinNoIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinNoIndexIT.java
index 7f8528d..3bbcaf2 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinNoIndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinNoIndexIT.java
@@ -30,7 +30,7 @@ public class HashJoinNoIndexIT extends HashJoinIT {
         super(indexDDL, plans);
     }
 
-    @Parameters
+    @Parameters(name="HashJoinNoIndexIT_{index}") // name is used by failsafe as file name in reports
     public static Collection<Object> data() {
         List<Object> testCases = Lists.newArrayList();
         testCases.add(new String[][] {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/834133a1/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinGlobalIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinGlobalIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinGlobalIndexIT.java
new file mode 100644
index 0000000..ce6f032
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinGlobalIndexIT.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.join;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.runners.Parameterized.Parameters;
+
+import com.google.common.collect.Lists;
+
+public class SortMergeJoinGlobalIndexIT extends SortMergeJoinIT {
+
+    public SortMergeJoinGlobalIndexIT(String[] indexDDL, String[] plans) {
+        super(indexDDL, plans);
+    }
+
+    @Parameters(name="SortMergeJoinGlobalIndexIT_{index}") // name is used by failsafe as file name in reports
+    public static Collection<Object> data() {
+        List<Object> testCases = Lists.newArrayList();
+        testCases.add(new String[][] {
+                {
+                "CREATE INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
+                "CREATE INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
+                "CREATE INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
+                }, {
+                "SORT-MERGE-JOIN (LEFT) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_supplier\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" + 
+                "        SERVER SORTED BY [\"S.:supplier_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "AND\n" +
+                "    SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "            SERVER SORTED BY [\"I.:item_id\"]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    AND (SKIP MERGE)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER FILTER BY QUANTITY < 5000\n" +
+                "            SERVER SORTED BY [\"O.item_id\"]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    CLIENT SORTED BY [\"I.0:supplier_id\"]",
+                
+                "SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        SERVER SORTED BY [\"I.:item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "AND\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "        SERVER SORTED BY [\"O.item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "CLIENT 4 ROW LIMIT",
+                
+                "SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER Join.idx_item\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        SERVER SORTED BY [\"I1.:item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "AND\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER Join.idx_item\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        SERVER SORTED BY [\"I2.:item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "CLIENT SORTED BY [\"I1.:item_id\"]"
+                }});
+        return testCases;
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/834133a1/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinIT.java
index 8e7bfe2..234c2ee 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinIT.java
@@ -34,8 +34,6 @@ import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.sql.Timestamp;
-import java.util.Collection;
-import java.util.List;
 import java.util.Properties;
 
 import org.apache.phoenix.exception.SQLExceptionCode;
@@ -45,140 +43,10 @@ import org.apache.phoenix.util.QueryUtil;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import com.google.common.collect.Lists;
 
 @RunWith(Parameterized.class)
-public class SortMergeJoinIT extends BaseJoinIT {
-    
-    @Parameters
-    public static Collection<Object> data() {
-        List<Object> testCases = Lists.newArrayList();
-        testCases.add(new String[][] {
-                {}, {
-                "SORT-MERGE-JOIN (LEFT) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "AND\n" +
-                "    SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    AND (SKIP MERGE)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER FILTER BY QUANTITY < 5000\n" +
-                "            SERVER SORTED BY [\"O.item_id\"]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    CLIENT SORTED BY [\"I.supplier_id\"]",
-                
-                "SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "AND\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "        SERVER SORTED BY [\"O.item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "CLIENT 4 ROW LIMIT",
-                
-                "SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "AND\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY"
-                }});
-        testCases.add(new String[][] {
-                {
-                "CREATE INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
-                "CREATE INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
-                "CREATE INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
-                }, {
-                "SORT-MERGE-JOIN (LEFT) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_supplier\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "        SERVER SORTED BY [\"S.:supplier_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "AND\n" +
-                "    SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "            SERVER SORTED BY [\"I.:item_id\"]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    AND (SKIP MERGE)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER FILTER BY QUANTITY < 5000\n" +
-                "            SERVER SORTED BY [\"O.item_id\"]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    CLIENT SORTED BY [\"I.0:supplier_id\"]",
-                
-                "SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        SERVER SORTED BY [\"I.:item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "AND\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "        SERVER SORTED BY [\"O.item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "CLIENT 4 ROW LIMIT",
-                
-                "SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER Join.idx_item\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        SERVER SORTED BY [\"I1.:item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "AND\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER Join.idx_item\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        SERVER SORTED BY [\"I2.:item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "CLIENT SORTED BY [\"I1.:item_id\"]"
-                }});
-        testCases.add(new String[][] {
-                {
-                "CREATE LOCAL INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
-                "CREATE LOCAL INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
-                "CREATE LOCAL INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
-                }, {
-                "SORT-MERGE-JOIN (LEFT) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +JOIN_SUPPLIER_TABLE_FULL_NAME + " [1]\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "        SERVER SORTED BY [\"S.:supplier_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "AND\n" +
-                "    SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "            SERVER SORTED BY [\"I.:item_id\"]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    AND (SKIP MERGE)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER FILTER BY QUANTITY < 5000\n" +
-                "            SERVER SORTED BY [\"O.item_id\"]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    CLIENT SORTED BY [\"I.0:supplier_id\"]",
-                
-                "SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        SERVER SORTED BY [\"I.:item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "AND\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "        SERVER SORTED BY [\"O.item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "CLIENT 4 ROW LIMIT",
-                
-                "SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        SERVER SORTED BY [\"I1.:item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "AND\n" +
-                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        SERVER SORTED BY [\"I2.:item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "CLIENT SORTED BY [\"I1.:item_id\"]"
-                }});
-        return testCases;
-    }
+public abstract class SortMergeJoinIT extends BaseJoinIT {
     
-
     public SortMergeJoinIT(String[] indexDDL, String[] plans) {
         super(indexDDL, plans);
     }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/834133a1/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinLocalIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinLocalIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinLocalIndexIT.java
new file mode 100644
index 0000000..919aa69
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinLocalIndexIT.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.join;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.runners.Parameterized.Parameters;
+
+import com.google.common.collect.Lists;
+
+public class SortMergeJoinLocalIndexIT extends SortMergeJoinIT {
+
+    public SortMergeJoinLocalIndexIT(String[] indexDDL, String[] plans) {
+        super(indexDDL, plans);
+    }
+
+    @Parameters(name="SortMergeJoinLocalIndexIT_{index}") // name is used by failsafe as file name in reports
+    public static Collection<Object> data() {
+        List<Object> testCases = Lists.newArrayList();
+        testCases.add(new String[][] {
+                {
+                "CREATE LOCAL INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
+                "CREATE LOCAL INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
+                "CREATE LOCAL INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
+                }, {
+                "SORT-MERGE-JOIN (LEFT) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +JOIN_SUPPLIER_TABLE_FULL_NAME + " [1]\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" + 
+                "        SERVER SORTED BY [\"S.:supplier_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "AND\n" +
+                "    SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "            SERVER SORTED BY [\"I.:item_id\"]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    AND (SKIP MERGE)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER FILTER BY QUANTITY < 5000\n" +
+                "            SERVER SORTED BY [\"O.item_id\"]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    CLIENT SORTED BY [\"I.0:supplier_id\"]",
+                
+                "SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        SERVER SORTED BY [\"I.:item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "AND\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "        SERVER SORTED BY [\"O.item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "CLIENT 4 ROW LIMIT",
+                
+                "SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        SERVER SORTED BY [\"I1.:item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "AND\n" +
+                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        SERVER SORTED BY [\"I2.:item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "CLIENT SORTED BY [\"I1.:item_id\"]"
+                }});
+        return testCases;
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/834133a1/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinNoIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinNoIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinNoIndexIT.java
new file mode 100644
index 0000000..6f04729
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinNoIndexIT.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.join;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.runners.Parameterized.Parameters;
+
+import com.google.common.collect.Lists;
+
+public class SortMergeJoinNoIndexIT extends SortMergeJoinIT {
+
+    public SortMergeJoinNoIndexIT(String[] indexDDL, String[] plans) {
+        super(indexDDL, plans);
+    }
+
+    @Parameters(name="SortMergeJoinNoIndexIT_{index}") // name is used by failsafe as file name in reports
+    public static Collection<Object> data() {
+        List<Object> testCases = Lists.newArrayList();
+        testCases.add(new String[][] {
+                {}, {
+                "SORT-MERGE-JOIN (LEFT) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "AND\n" +
+                "    SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    AND (SKIP MERGE)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER FILTER BY QUANTITY < 5000\n" +
+                "            SERVER SORTED BY [\"O.item_id\"]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    CLIENT SORTED BY [\"I.supplier_id\"]",
+                
+                "SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "AND\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "        SERVER SORTED BY [\"O.item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "CLIENT 4 ROW LIMIT",
+                
+                "SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "AND\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY"
+                }});
+        return testCases;
+    }
+}


[03/25] phoenix git commit: PHOENIX-4246 Breakup join related tests into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/ee20a8c9/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SubqueryIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SubqueryIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SubqueryIT.java
new file mode 100644
index 0000000..604db63
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SubqueryIT.java
@@ -0,0 +1,788 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.join;
+
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.QueryUtil;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import com.google.common.collect.Lists;
+
+@RunWith(Parameterized.class)
+public class SubqueryIT extends BaseJoinIT {
+    public SubqueryIT(String[] indexDDL, String[] plans) {
+        super(indexDDL, plans);
+    }
+    
+    @Parameters
+    public static Collection<Object> data() {
+        List<Object> testCases = Lists.newArrayList();
+        testCases.add(new String[][] {
+                {}, {
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    SERVER SORTED BY \\[I.NAME\\]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "    SKIP-SCAN-JOIN TABLE 1\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + " \\['000000000000001'\\] - \\[\\*\\]\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    DYNAMIC SERVER FILTER BY \"I.item_id\" IN \\(\\$\\d+.\\$\\d+\\)",
+                
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "    SERVER SORTED BY [I.NAME]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    PARALLEL SEMI-JOIN TABLE 1(DELAYED EVALUATION) (SKIP MERGE)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
+                "        CLIENT MERGE SORT",
+                
+                "CLIENT PARALLEL 4-WAY FULL SCAN OVER " + JOIN_COITEM_TABLE_FULL_NAME + "\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\".+.item_id\", .+.NAME\\]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "            PARALLEL ANTI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
+                "                CLIENT MERGE SORT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 1\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\".+.item_id\", .+.NAME\\]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "            SKIP-SCAN-JOIN TABLE 0\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
+                "                CLIENT MERGE SORT\n" +
+                "            DYNAMIC SERVER FILTER BY \"" + JOIN_ITEM_TABLE_FULL_NAME + ".item_id\" IN \\(\\$\\d+.\\$\\d+\\)\n" +
+                "    AFTER-JOIN SERVER FILTER BY \\(\\$\\d+.\\$\\d+ IS NOT NULL OR \\$\\d+.\\$\\d+ IS NOT NULL\\)",
+                
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    SERVER SORTED BY [I.NAME]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL ANTI-JOIN TABLE 0 (SKIP MERGE)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
+                "        CLIENT MERGE SORT",
+                
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + "\n" +
+                "    SKIP-SCAN-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"O.customer_id\"\\]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "            PARALLEL INNER-JOIN TABLE 0\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            PARALLEL LEFT-JOIN TABLE 1\\(DELAYED EVALUATION\\)\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
+                "                CLIENT MERGE SORT\n" +
+                "            DYNAMIC SERVER FILTER BY \"I.item_id\" IN \\(\"O.item_id\"\\)\n" +
+                "            AFTER-JOIN SERVER FILTER BY \\(I.NAME = 'T2' OR O.QUANTITY > \\$\\d+.\\$\\d+\\)\n" +
+                "    DYNAMIC SERVER FILTER BY \"" + JOIN_CUSTOMER_TABLE_FULL_NAME + ".customer_id\" IN \\(\\$\\d+.\\$\\d+\\)"
+                }});
+        testCases.add(new String[][] {
+                {
+                "CREATE INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
+                "CREATE INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
+                "CREATE INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
+                }, {
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_supplier\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" + 
+                "    PARALLEL SEMI-JOIN TABLE 1 \\(SKIP MERGE\\)\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + " \\['000000000000001'\\] - \\[\\*\\]\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
+                "        CLIENT MERGE SORT",
+                
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_supplier\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" + 
+                "    SERVER SORTED BY [\"I.0:NAME\"]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "    PARALLEL SEMI-JOIN TABLE 1(DELAYED EVALUATION) (SKIP MERGE)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
+                "        CLIENT MERGE SORT",
+                
+                "CLIENT PARALLEL 4-WAY FULL SCAN OVER " + JOIN_COITEM_TABLE_FULL_NAME + "\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "            SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY \\[\".+.:item_id\", \".+.0:NAME\"\\]\n" +
+                "            PARALLEL ANTI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
+                "                CLIENT MERGE SORT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 1\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "            SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY \\[\".+.:item_id\", \".+.0:NAME\"\\]\n" +
+                "            PARALLEL SEMI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
+                "                CLIENT MERGE SORT\n" +
+                "    AFTER-JOIN SERVER FILTER BY \\(\\$\\d+.\\$\\d+ IS NOT NULL OR \\$\\d+.\\$\\d+ IS NOT NULL\\)",
+                
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    PARALLEL ANTI-JOIN TABLE 0 (SKIP MERGE)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
+                "        CLIENT MERGE SORT",
+                
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_customer\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" + 
+                "    PARALLEL SEMI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"O.customer_id\"\\]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "            PARALLEL INNER-JOIN TABLE 0\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            PARALLEL LEFT-JOIN TABLE 1\\(DELAYED EVALUATION\\)\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
+                "                CLIENT MERGE SORT\n" +
+                "            AFTER-JOIN SERVER FILTER BY \\(\"I.0:NAME\" = 'T2' OR O.QUANTITY > \\$\\d+.\\$\\d+\\)"
+                }});
+        testCases.add(new String[][] {
+                {
+                "CREATE LOCAL INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
+                "CREATE LOCAL INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
+                "CREATE LOCAL INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
+                }, {
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " \\[1\\]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + " \\[1\\]\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" + 
+                "        CLIENT MERGE SORT\n" +
+                "    PARALLEL SEMI-JOIN TABLE 1 \\(SKIP MERGE\\)\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + " \\['000000000000001'\\] - \\[\\*\\]\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    DYNAMIC SERVER FILTER BY \"I.:item_id\" IN \\(\\$\\d+.\\$\\d+\\)",
+                            
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + " [1]\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" + 
+                "    SERVER SORTED BY [\"I.0:NAME\"]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    PARALLEL SEMI-JOIN TABLE 1(DELAYED EVALUATION) (SKIP MERGE)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
+                "        CLIENT MERGE SORT",
+
+                "CLIENT PARALLEL 4-WAY FULL SCAN OVER " + JOIN_COITEM_TABLE_FULL_NAME + "\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " \\[1\\]\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "            SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY \\[\".+.:item_id\", \".+.0:NAME\"\\]\n" +
+                "        CLIENT MERGE SORT\n" + 
+                "            PARALLEL ANTI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
+                "                CLIENT MERGE SORT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 1\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " \\[1\\]\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "            SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY \\[\".+.:item_id\", \".+.0:NAME\"\\]\n" +
+                "        CLIENT MERGE SORT\n" + 
+                "            PARALLEL SEMI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
+                "                CLIENT MERGE SORT\n" +
+                "            DYNAMIC SERVER FILTER BY \"" + JOIN_SCHEMA + ".idx_item.:item_id\" IN \\(\\$\\d+.\\$\\d+\\)\n" +
+                "    AFTER-JOIN SERVER FILTER BY \\(\\$\\d+.\\$\\d+ IS NOT NULL OR \\$\\d+.\\$\\d+ IS NOT NULL\\)",
+                
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL ANTI-JOIN TABLE 0 (SKIP MERGE)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
+                "        CLIENT MERGE SORT",
+                
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + " \\[1\\]\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" + 
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL SEMI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " \\[1\\]\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"O.customer_id\"\\]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "            PARALLEL INNER-JOIN TABLE 0\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            PARALLEL LEFT-JOIN TABLE 1\\(DELAYED EVALUATION\\)\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
+                "                CLIENT MERGE SORT\n" +
+                "            DYNAMIC SERVER FILTER BY \"I.:item_id\" IN \\(\"O.item_id\"\\)\n" +
+                "            AFTER-JOIN SERVER FILTER BY \\(\"I.0:NAME\" = 'T2' OR O.QUANTITY > \\$\\d+.\\$\\d+\\)\n" +
+                "    DYNAMIC SERVER FILTER BY \"" + JOIN_SCHEMA + ".idx_customer.:customer_id\" IN \\(\\$\\d+.\\$\\d+\\)"
+                }});
+        return testCases;
+    }
+    
+    
+    @Test
+    public void testNonCorrelatedSubquery() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String tableName5 = getTableName(conn, JOIN_COITEM_TABLE_FULL_NAME);
+        try {            
+            String query = "SELECT \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" >= ALL (SELECT \"item_id\" FROM " + tableName4 + ") ORDER BY name";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "invalid001");
+            assertEquals(rs.getString(2), "INVALID-1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "T6");
+
+            assertFalse(rs.next());
+            
+            query = "SELECT \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" < ANY (SELECT \"item_id\" FROM " + tableName4 + ")";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+
+            assertFalse(rs.next());
+            
+            query = "SELECT \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" < (SELECT max(\"item_id\") FROM " + tableName4 + ")";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+
+            assertFalse(rs.next());
+            
+            query = "SELECT * FROM " + tableName5 + " WHERE (item_id, item_name) != ALL (SELECT \"item_id\", name FROM " + tableName1 + ")";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "T5");
+
+            assertFalse(rs.next());
+            
+            query = "SELECT * FROM " + tableName5 + " WHERE EXISTS (SELECT \"item_id\", name FROM " + tableName1 + ")";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "T3");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "T5");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000003");
+            assertEquals(rs.getString(4), "T3");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "T1");
+
+            assertFalse(rs.next());
+            
+            query = "SELECT \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" < (SELECT \"item_id\" FROM " + tableName4 + ")";
+            statement = conn.prepareStatement(query);
+            try {
+                rs = statement.executeQuery();
+                fail("Should have got Exception.");
+            } catch (SQLException e) {
+            }
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testInSubquery() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String tableName5 = getTableName(conn, JOIN_COITEM_TABLE_FULL_NAME);
+        try {
+            String query = "SELECT \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" IN (SELECT \"item_id\" FROM " + tableName4 + ") ORDER BY name";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "T6");
+
+            assertFalse(rs.next());
+
+            query = "SELECT \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" NOT IN (SELECT \"item_id\" FROM " + tableName4 + ") ORDER BY name";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "invalid001");
+            assertEquals(rs.getString(2), "INVALID-1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+
+            assertFalse(rs.next());
+            
+            query = "SELECT i.\"item_id\", s.name FROM " + tableName1 + " i JOIN " + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\" WHERE i.\"item_id\" IN (SELECT \"item_id\" FROM " + tableName4 + " WHERE \"order_id\" > '000000000000001') ORDER BY i.name";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "S6");
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            String plan = QueryUtil.getExplainPlan(rs);
+            assertPlansMatch(plans[0], plan);
+            
+            query = "SELECT i.\"item_id\", s.name FROM " + tableName2 + " s LEFT JOIN " + tableName1 + " i ON i.\"supplier_id\" = s.\"supplier_id\" WHERE i.\"item_id\" IN (SELECT \"item_id\" FROM " + tableName4 + ") ORDER BY i.name";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "S6");
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            assertPlansEqual(plans[1], QueryUtil.getExplainPlan(rs));
+           
+            query = "SELECT * FROM " + tableName5 + " WHERE (item_id, item_name) IN (SELECT \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" NOT IN (SELECT \"item_id\" FROM " + tableName4 + "))"
+                    + " OR (co_item_id, co_item_name) IN (SELECT \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" IN (SELECT \"item_id\" FROM " + tableName4 + "))";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000003");
+            assertEquals(rs.getString(4), "T3");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "T1");
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            plan = QueryUtil.getExplainPlan(rs);
+            assertPlansMatch(plans[2], plan);
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testExistsSubquery() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String tableName5 = getTableName(conn, JOIN_COITEM_TABLE_FULL_NAME);
+        try {
+            String query = "SELECT \"item_id\", name FROM " + tableName1 + " i WHERE NOT EXISTS (SELECT 1 FROM " + tableName4 + " o WHERE o.\"item_id\" = i.\"item_id\") ORDER BY name";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "invalid001");
+            assertEquals(rs.getString(2), "INVALID-1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            assertPlansEqual(plans[3], QueryUtil.getExplainPlan(rs));
+            
+            query = "SELECT * FROM " + tableName5 + " co WHERE EXISTS (SELECT 1 FROM " + tableName1 + " i WHERE NOT EXISTS (SELECT 1 FROM " + tableName4 + " WHERE \"item_id\" = i.\"item_id\") AND co.item_id = \"item_id\" AND name = co.item_name)"
+                    + " OR EXISTS (SELECT 1 FROM " + tableName1 + " WHERE \"item_id\" IN (SELECT \"item_id\" FROM " + tableName4 + ") AND co.co_item_id = \"item_id\" AND name = co.co_item_name)";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000003");
+            assertEquals(rs.getString(4), "T3");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "T1");
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            String plan = QueryUtil.getExplainPlan(rs);
+            assertPlansMatch(plans[2], plan);
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testComparisonSubquery() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        final Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName3 = getTableName(conn, JOIN_CUSTOMER_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        try {
+            String query = "SELECT \"order_id\", name FROM " + tableName4 + 
+                    " o JOIN " + tableName1 + 
+                    " i ON o.\"item_id\" = i.\"item_id\" WHERE quantity = (SELECT max(quantity) FROM " + 
+                    tableName4 + " q WHERE o.\"item_id\" = q.\"item_id\")";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertEquals(rs.getString(2), "T2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000004");
+            assertEquals(rs.getString(2), "T6");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+            assertEquals(rs.getString(2), "T3");
+
+            assertFalse(rs.next());
+
+            query = "SELECT \"order_id\", name FROM " + tableName4 + 
+                    " o JOIN " + tableName1 + 
+                    " i ON o.\"item_id\" = i.\"item_id\" WHERE quantity = (SELECT max(quantity) FROM " + 
+                    tableName1 + " i2 JOIN " + tableName4 + 
+                    " q ON i2.\"item_id\" = q.\"item_id\" WHERE o.\"item_id\" = i2.\"item_id\")";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertEquals(rs.getString(2), "T2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000004");
+            assertEquals(rs.getString(2), "T6");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+            assertEquals(rs.getString(2), "T3");
+
+            assertFalse(rs.next());
+
+            query = "SELECT name from " + tableName3 + 
+                    " WHERE \"customer_id\" IN (SELECT \"customer_id\" FROM " + 
+                    tableName1 + " i JOIN " + tableName4 + 
+                    " o ON o.\"item_id\" = i.\"item_id\" WHERE i.name = 'T2' OR quantity > (SELECT avg(quantity) FROM " + 
+                    tableName4 + " q WHERE o.\"item_id\" = q.\"item_id\"))";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "C2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "C4");
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            String plan = QueryUtil.getExplainPlan(rs);
+            assertPlansMatch(plans[4], plan);
+
+            query = "SELECT \"order_id\" FROM " + tableName4 + 
+                    " o WHERE quantity = (SELECT quantity FROM " + tableName4 + 
+                    " WHERE o.\"item_id\" = \"item_id\" AND \"order_id\" != '000000000000004')";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+
+            assertFalse(rs.next());
+
+            query = "SELECT \"order_id\" FROM " + tableName4 + 
+                    " o WHERE quantity = (SELECT quantity FROM " + tableName4 + 
+                    " WHERE o.\"item_id\" = \"item_id\" AND \"order_id\" != '000000000000003')";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            try {
+                while(rs.next());
+                fail("Should have got exception.");
+            } catch (SQLException e) {                
+            }
+
+            query = "SELECT \"order_id\" FROM " + tableName4 + 
+                    " o WHERE quantity = (SELECT max(quantity) FROM " + tableName4 + 
+                    " WHERE o.\"item_id\" = \"item_id\" AND \"order_id\" != '000000000000004' GROUP BY \"order_id\")";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+
+            assertFalse(rs.next());
+
+            query = "SELECT \"order_id\" FROM " + tableName4 + 
+                    " o WHERE quantity = (SELECT max(quantity) FROM " + tableName4 + 
+                    " WHERE o.\"item_id\" = \"item_id\" AND \"order_id\" != '000000000000003' GROUP BY \"order_id\")";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            try {
+                while(rs.next());
+                fail("Should have got exception.");
+            } catch (SQLException e) {                
+            }
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testAnyAllComparisonSubquery() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        try {
+            String query = "SELECT \"order_id\", name FROM " + tableName4 + " o JOIN " + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" WHERE quantity = ALL(SELECT quantity FROM " + tableName4 + " q WHERE o.\"item_id\" = q.\"item_id\")";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertEquals(rs.getString(2), "T2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+            assertEquals(rs.getString(2), "T3");
+
+            assertFalse(rs.next());
+            
+            query = "SELECT \"order_id\", name FROM " + tableName4 + " o JOIN " + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" WHERE quantity != ALL(SELECT max(quantity) FROM " + tableName4 + " q WHERE o.\"item_id\" = q.\"item_id\")";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+            assertEquals(rs.getString(2), "T6");
+
+            assertFalse(rs.next());
+            
+            query = "SELECT \"order_id\", name FROM " + tableName4 + " o JOIN " + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" WHERE quantity != ANY(SELECT quantity FROM " + tableName4 + " q WHERE o.\"item_id\" = q.\"item_id\" GROUP BY quantity)";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+            assertEquals(rs.getString(2), "T6");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000004");
+            assertEquals(rs.getString(2), "T6");
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSubqueryWithUpsert() throws Exception {
+        String tempTable = generateUniqueName();
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(true);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        try {            
+            conn.createStatement().execute("CREATE TABLE " + tempTable 
+                    + "   (item_id varchar not null primary key, " 
+                    + "    name varchar)");
+            conn.createStatement().execute("UPSERT INTO " + tempTable + "(item_id, name)"
+                    + "   SELECT \"item_id\", name FROM " + tableName1 
+                    + "   WHERE \"item_id\" NOT IN (SELECT \"item_id\" FROM " + tableName4 + ")");
+            
+            String query = "SELECT name FROM " + tempTable + " ORDER BY item_id";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T4");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T5");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "INVALID-1");
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSubqueryWithDelete() throws Exception {
+        String tempTable = generateUniqueName();
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(true);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        try {            
+            conn.createStatement().execute("CREATE TABLE " + tempTable 
+                    + "   (item_id varchar not null primary key, " 
+                    + "    name varchar)");
+            conn.createStatement().execute("UPSERT INTO " + tempTable + "(item_id, name)"
+                    + "   SELECT \"item_id\", name FROM " + tableName1);
+
+            String query = "SELECT count(*) FROM " + tableName1;
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getInt(1), 7);
+            assertFalse(rs.next());
+            
+            conn.createStatement().execute("DELETE FROM " + tempTable + " WHERE item_id IN ("
+                    + "   SELECT \"item_id\" FROM " + tableName4 + ")");
+            
+            query = "SELECT name FROM " + tempTable + " ORDER BY item_id";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T4");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T5");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "INVALID-1");
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+}
+

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ee20a8c9/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SubqueryUsingSortMergeJoinIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SubqueryUsingSortMergeJoinIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SubqueryUsingSortMergeJoinIT.java
new file mode 100644
index 0000000..9335065
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SubqueryUsingSortMergeJoinIT.java
@@ -0,0 +1,566 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.join;
+
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.QueryUtil;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import com.google.common.collect.Lists;
+
+@RunWith(Parameterized.class)
+public class SubqueryUsingSortMergeJoinIT extends BaseJoinIT {
+
+    public SubqueryUsingSortMergeJoinIT(String[] indexDDL, String[] plans) {
+        super(indexDDL, plans);
+    }
+    
+    @Parameters
+    public static Collection<Object> data() {
+        List<Object> testCases = Lists.newArrayList();
+        testCases.add(new String[][] {
+                {}, {
+                "SORT-MERGE-JOIN (SEMI) TABLES\n" +
+                "    SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "            SERVER SORTED BY [\"I.supplier_id\"]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    AND\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "    CLIENT SORTED BY [\"I.item_id\"]\n" +
+                "AND (SKIP MERGE)\n" +
+                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + " ['000000000000001'] - [*]\n" +
+                "        SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "CLIENT SORTED BY [I.NAME]",
+
+                "SORT-MERGE-JOIN \\(LEFT\\) TABLES\n" +
+                "    SORT-MERGE-JOIN \\(LEFT\\) TABLES\n" +
+                "        CLIENT PARALLEL 4-WAY FULL SCAN OVER " + JOIN_COITEM_TABLE_FULL_NAME + "\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    AND\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\".+.item_id\", .+.NAME\\]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "            PARALLEL ANTI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"]\\\n" +
+                "                CLIENT MERGE SORT\n" +
+                "    CLIENT SORTED BY \\[.*.CO_ITEM_ID, .*.CO_ITEM_NAME\\]\n" +
+                "AND\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "        SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\".+.item_id\", .+.NAME\\]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "        SKIP-SCAN-JOIN TABLE 0\n" +
+                "            CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "                SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
+                "            CLIENT MERGE SORT\n" +
+                "        DYNAMIC SERVER FILTER BY \"" + JOIN_ITEM_TABLE_FULL_NAME + ".item_id\" IN \\(\\$\\d+.\\$\\d+\\)\n" +
+                "CLIENT FILTER BY \\(\\$\\d+.\\$\\d+ IS NOT NULL OR \\$\\d+.\\$\\d+ IS NOT NULL\\)",            
+
+                "SORT-MERGE-JOIN \\(SEMI\\) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + "\n" +
+                "AND \\(SKIP MERGE\\)\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "        SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"O.customer_id\"\\]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "        PARALLEL INNER-JOIN TABLE 0\n" +
+                "            CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "        PARALLEL LEFT-JOIN TABLE 1\\(DELAYED EVALUATION\\)\n" +
+                "            CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "                SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
+                "            CLIENT MERGE SORT\n" +
+                "        DYNAMIC SERVER FILTER BY \"I.item_id\" IN \\(\"O.item_id\"\\)\n" +
+                "        AFTER-JOIN SERVER FILTER BY \\(I.NAME = 'T2' OR O.QUANTITY > \\$\\d+.\\$\\d+\\)",
+                }});
+        testCases.add(new String[][] {
+                {
+                "CREATE INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
+                "CREATE INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
+                "CREATE INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
+                }, {
+                "SORT-MERGE-JOIN (SEMI) TABLES\n" +
+                "    SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "            SERVER SORTED BY [\"I.0:supplier_id\"]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    AND\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_supplier\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "            SERVER SORTED BY [\"S.:supplier_id\"]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    CLIENT SORTED BY [\"I.:item_id\"]\n" +
+                "AND (SKIP MERGE)\n" +
+                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + " ['000000000000001'] - [*]\n" +
+                "        SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "CLIENT SORTED BY [\"I.0:NAME\"]",
+
+                "SORT-MERGE-JOIN \\(LEFT\\) TABLES\n" +
+                "    SORT-MERGE-JOIN \\(LEFT\\) TABLES\n" +
+                "        CLIENT PARALLEL 4-WAY FULL SCAN OVER " + JOIN_COITEM_TABLE_FULL_NAME + "\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    AND\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "            SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY \\[\".+.:item_id\", \".+.0:NAME\"\\]\n" +
+                "            PARALLEL ANTI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
+                "                CLIENT MERGE SORT\n" +
+                "    CLIENT SORTED BY \\[.*.CO_ITEM_ID, .*.CO_ITEM_NAME\\]\n" +
+                "AND\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY \\[\".+.:item_id\", \".+.0:NAME\"\\]\n" +
+                "        PARALLEL SEMI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
+                "            CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "                SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
+                "            CLIENT MERGE SORT\n" +
+                "CLIENT FILTER BY \\(\\$\\d+.\\$\\d+ IS NOT NULL OR \\$\\d+.\\$\\d+ IS NOT NULL\\)",
+                
+                "SORT-MERGE-JOIN \\(SEMI\\) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_customer\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        SERVER SORTED BY \\[\"Join.idx_customer.:customer_id\"\\]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "AND \\(SKIP MERGE\\)\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"O.customer_id\"\\]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "        PARALLEL INNER-JOIN TABLE 0\n" +
+                "            CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "        PARALLEL LEFT-JOIN TABLE 1\\(DELAYED EVALUATION\\)\n" +
+                "            CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "                SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
+                "            CLIENT MERGE SORT\n" +
+                "        AFTER-JOIN SERVER FILTER BY \\(\"I.0:NAME\" = 'T2' OR O.QUANTITY > \\$\\d+.\\$\\d+\\)",
+                }});
+        testCases.add(new String[][] {
+                {
+                "CREATE LOCAL INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
+                "CREATE LOCAL INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
+                "CREATE LOCAL INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
+                }, {
+                "SORT-MERGE-JOIN (SEMI) TABLES\n" +
+                "    SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "            SERVER SORTED BY [\"I.0:supplier_id\"]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    AND\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + " [1]\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "            SERVER SORTED BY [\"S.:supplier_id\"]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    CLIENT SORTED BY [\"I.:item_id\"]\n" +
+                "AND (SKIP MERGE)\n" +
+                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + " ['000000000000001'] - [*]\n" +
+                "        SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "CLIENT SORTED BY [\"I.0:NAME\"]",
+
+                "SORT-MERGE-JOIN \\(LEFT\\) TABLES\n" +
+                "    SORT-MERGE-JOIN \\(LEFT\\) TABLES\n" +
+                "        CLIENT PARALLEL 4-WAY FULL SCAN OVER " + JOIN_COITEM_TABLE_FULL_NAME + "\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    AND\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " \\[1\\]\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "            SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY \\[\".+.:item_id\", \".+.0:NAME\"\\]\n" +
+                "        CLIENT MERGE SORT\n" + 
+                "            PARALLEL ANTI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "                    SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
+                "                CLIENT MERGE SORT\n" +
+                "    CLIENT SORTED BY \\[.*.CO_ITEM_ID, .*.CO_ITEM_NAME\\]\n" +
+                "AND\n" +
+                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " \\[1\\]\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY \\[\".+.:item_id\", \".+.0:NAME\"\\]\n" +
+                "    CLIENT MERGE SORT\n" + 
+                "        PARALLEL SEMI-JOIN TABLE 0 \\(SKIP MERGE\\)\n" +
+                "            CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "                SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
+                "            CLIENT MERGE SORT\n" +
+                "        DYNAMIC SERVER FILTER BY \"" + JOIN_SCHEMA + ".idx_item.:item_id\" IN \\(\\$\\d+.\\$\\d+\\)\n" +
+                "CLIENT FILTER BY \\(\\$\\d+.\\$\\d+ IS NOT NULL OR \\$\\d+.\\$\\d+ IS NOT NULL\\)",
+                
+                "SORT-MERGE-JOIN \\(SEMI\\) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + " \\[1\\]\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        SERVER SORTED BY \\[\"Join.idx_customer.:customer_id\"\\]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "AND \\(SKIP MERGE\\)\n" +
+                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " \\[1\\]\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"O.customer_id\"\\]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "        PARALLEL INNER-JOIN TABLE 0\n" +
+                "            CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "        PARALLEL LEFT-JOIN TABLE 1\\(DELAYED EVALUATION\\)\n" +
+                "            CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "                SERVER AGGREGATE INTO DISTINCT ROWS BY \\[\"item_id\"\\]\n" +
+                "            CLIENT MERGE SORT\n" +
+                "        DYNAMIC SERVER FILTER BY \"I.:item_id\" IN \\(\"O.item_id\"\\)\n" +
+                "        AFTER-JOIN SERVER FILTER BY \\(\"I.0:NAME\" = 'T2' OR O.QUANTITY > \\$\\d+.\\$\\d+\\)",
+                }});
+        return testCases;
+    }    
+
+    @Test
+    public void testInSubquery() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String tableName5 = getTableName(conn, JOIN_COITEM_TABLE_FULL_NAME);
+        try {
+            String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" IN (SELECT \"item_id\" FROM " + tableName4 + ") ORDER BY name";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "T6");
+
+            assertFalse(rs.next());
+
+            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" NOT IN (SELECT \"item_id\" FROM " + tableName4 + ") ORDER BY name";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "invalid001");
+            assertEquals(rs.getString(2), "INVALID-1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+
+            assertFalse(rs.next());
+            
+            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ i.\"item_id\", s.name FROM " + tableName1 + " i JOIN " + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\" WHERE i.\"item_id\" IN (SELECT \"item_id\" FROM " + tableName4 + " WHERE \"order_id\" > '000000000000001') ORDER BY i.name";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "S6");
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            assertPlansEqual(plans[0], QueryUtil.getExplainPlan(rs));
+            
+            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ i.\"item_id\", s.name FROM " + tableName2 + " s LEFT JOIN " + tableName1 + " i ON i.\"supplier_id\" = s.\"supplier_id\" WHERE i.\"item_id\" IN (SELECT \"item_id\" FROM " + tableName4 + ") ORDER BY i.name";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "S6");
+
+            assertFalse(rs.next());
+           
+            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ * FROM " + tableName5 + " WHERE (item_id, item_name) IN (SELECT \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" NOT IN (SELECT \"item_id\" FROM " + tableName4 + "))"
+                    + " OR (co_item_id, co_item_name) IN (SELECT \"item_id\", name FROM " + tableName1 + " WHERE \"item_id\" IN (SELECT \"item_id\" FROM " + tableName4 + "))";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "T1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000003");
+            assertEquals(rs.getString(4), "T3");
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            String plan = QueryUtil.getExplainPlan(rs);
+            assertPlansMatch(plans[1], plan);
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testExistsSubquery() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String tableName5 = getTableName(conn, JOIN_COITEM_TABLE_FULL_NAME);
+        try {
+            String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"item_id\", name FROM " + tableName1 + " i WHERE NOT EXISTS (SELECT 1 FROM " + tableName4 + " o WHERE o.\"item_id\" = i.\"item_id\") ORDER BY name";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "invalid001");
+            assertEquals(rs.getString(2), "INVALID-1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+
+            assertFalse(rs.next());
+            
+            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ * FROM " + tableName5 + " co WHERE EXISTS (SELECT 1 FROM " + tableName1 + " i WHERE NOT EXISTS (SELECT 1 FROM " + tableName4 + " WHERE \"item_id\" = i.\"item_id\") AND co.item_id = \"item_id\" AND name = co.item_name)"
+                    + " OR EXISTS (SELECT 1 FROM " + tableName1 + " WHERE \"item_id\" IN (SELECT \"item_id\" FROM " + tableName4 + ") AND co.co_item_id = \"item_id\" AND name = co.co_item_name)";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "T1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000003");
+            assertEquals(rs.getString(4), "T3");
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            String plan = QueryUtil.getExplainPlan(rs);
+            assertPlansMatch(plans[1], plan);
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testComparisonSubquery() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName3 = getTableName(conn, JOIN_CUSTOMER_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        try {
+            String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", name FROM " + tableName4 + " o JOIN " + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" WHERE quantity = (SELECT max(quantity) FROM " + tableName4 + " q WHERE o.\"item_id\" = q.\"item_id\")";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertEquals(rs.getString(2), "T2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+            assertEquals(rs.getString(2), "T3");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000004");
+            assertEquals(rs.getString(2), "T6");
+
+            assertFalse(rs.next());
+
+            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ name from " + tableName3 + " WHERE \"customer_id\" IN (SELECT \"customer_id\" FROM " + tableName1 + " i JOIN " + tableName4 + " o ON o.\"item_id\" = i.\"item_id\" WHERE i.name = 'T2' OR quantity > (SELECT avg(quantity) FROM " + tableName4 + " q WHERE o.\"item_id\" = q.\"item_id\"))";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "C2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "C4");
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            String plan = QueryUtil.getExplainPlan(rs);
+            assertPlansMatch(plans[2], plan);
+
+            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\" FROM " + tableName4 + " o WHERE quantity = (SELECT quantity FROM " + tableName4 + " WHERE o.\"item_id\" = \"item_id\" AND \"order_id\" != '000000000000004')";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+
+            assertFalse(rs.next());
+
+            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\" FROM " + tableName4 + " o WHERE quantity = (SELECT quantity FROM " + tableName4 + " WHERE o.\"item_id\" = \"item_id\" AND \"order_id\" != '000000000000003')";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            try {
+                while(rs.next());
+                fail("Should have got exception.");
+            } catch (SQLException e) {                
+            }
+
+            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\" FROM " + tableName4 + " o WHERE quantity = (SELECT max(quantity) FROM " + tableName4 + " WHERE o.\"item_id\" = \"item_id\" AND \"order_id\" != '000000000000004' GROUP BY \"order_id\")";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+
+            assertFalse(rs.next());
+
+            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\" FROM " + tableName4 + " o WHERE quantity = (SELECT max(quantity) FROM " + tableName4 + " WHERE o.\"item_id\" = \"item_id\" AND \"order_id\" != '000000000000003' GROUP BY \"order_id\")";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            try {
+                while(rs.next());
+                fail("Should have got exception.");
+            } catch (SQLException e) {                
+            }
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testAnyAllComparisonSubquery() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        try {
+            String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", name FROM " + tableName4 + " o JOIN " + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" WHERE quantity = ALL(SELECT quantity FROM " + tableName4 + " q WHERE o.\"item_id\" = q.\"item_id\")";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertEquals(rs.getString(2), "T2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+            assertEquals(rs.getString(2), "T3");
+
+            assertFalse(rs.next());
+            
+            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", name FROM " + tableName4 + " o JOIN " + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" WHERE quantity != ALL(SELECT max(quantity) FROM " + tableName4 + " q WHERE o.\"item_id\" = q.\"item_id\")";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+            assertEquals(rs.getString(2), "T6");
+
+            assertFalse(rs.next());
+            
+            query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", name FROM " + tableName4 + " o JOIN " + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" WHERE quantity != ANY(SELECT quantity FROM " + tableName4 + " q WHERE o.\"item_id\" = q.\"item_id\" GROUP BY quantity)";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+            assertEquals(rs.getString(2), "T6");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000004");
+            assertEquals(rs.getString(2), "T6");
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSubqueryWithUpsert() throws Exception {
+        String tempTable = generateUniqueName();
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(true);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        try {            
+            conn.createStatement().execute("CREATE TABLE " + tempTable 
+                    + "   (item_id varchar not null primary key, " 
+                    + "    name varchar)");
+            conn.createStatement().execute("UPSERT /*+ USE_SORT_MERGE_JOIN*/ INTO " + tempTable + "(item_id, name)"
+                    + "   SELECT \"item_id\", name FROM " + tableName1 
+                    + "   WHERE \"item_id\" NOT IN (SELECT \"item_id\" FROM " + tableName4 + ")");
+            
+            String query = "SELECT name FROM " + tempTable + " ORDER BY item_id";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T4");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T5");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "INVALID-1");
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+}
+
+


[16/25] phoenix git commit: PHOENIX-4244 Breakup ArrayIT into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/527f786a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayIT.java
index cf86614..ddc965a 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayIT.java
@@ -19,35 +19,48 @@ package org.apache.phoenix.end2end;
 
 import static org.apache.phoenix.util.TestUtil.B_VALUE;
 import static org.apache.phoenix.util.TestUtil.ROW1;
-import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 import java.sql.Array;
 import java.sql.Connection;
 import java.sql.Date;
 import java.sql.DriverManager;
 import java.sql.PreparedStatement;
-import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Types;
 import java.util.Properties;
 
 import org.apache.phoenix.query.BaseTest;
-import org.apache.phoenix.schema.types.PhoenixArray;
-import org.apache.phoenix.util.PropertiesUtil;
-import org.apache.phoenix.util.SchemaUtil;
-import org.apache.phoenix.util.StringUtil;
-import org.junit.Test;
 
-import com.google.common.primitives.Floats;
+public abstract class ArrayIT extends ParallelStatsDisabledIT {
 
-public class ArrayIT extends ParallelStatsDisabledIT {
+    protected static String createTableWithArray(String url, byte[][] bs, Object object) throws SQLException {
+        String tableName = generateUniqueName();
+        String ddlStmt = "create table "
+                + tableName
+                + "   (organization_id char(15) not null, \n"
+                + "    entity_id char(15) not null,\n"
+                + "    a_string_array varchar(100) array[3],\n"
+                + "    b_string varchar(100),\n"
+                + "    a_integer integer,\n"
+                + "    a_date date,\n"
+                + "    a_time time,\n"
+                + "    a_timestamp timestamp,\n"
+                + "    x_decimal decimal(31,10),\n"
+                + "    x_long_array bigint[5],\n"
+                + "    x_integer integer,\n"
+                + "    a_byte_array tinyint array,\n"
+                + "    a_short smallint,\n"
+                + "    a_float float,\n"
+                + "    a_double_array double array[],\n"
+                + "    a_unsigned_float unsigned_float,\n"
+                + "    a_unsigned_double unsigned_double \n"
+                + "    CONSTRAINT pk PRIMARY KEY (organization_id, entity_id)\n"
+                + ")";
+        BaseTest.createTestTable(url, ddlStmt, bs, null);
+        return tableName;
+    }
 
-    private static void initTablesWithArrays(String tableName, String tenantId, Date date, boolean useNull, String url) throws Exception {
+    protected static void initTablesWithArrays(String tableName, String tenantId, Date date, boolean useNull, String url) throws Exception {
         Properties props = new Properties();
         Connection conn = DriverManager.getConnection(url, props);
         try {
@@ -121,2504 +134,4 @@ public class ArrayIT extends ParallelStatsDisabledIT {
             conn.close();
         }
     }
-
-	@Test
-	public void testScanByArrayValue() throws Exception {
-		String tenantId = getOrganizationId();
-		String tableName = createTableWithArray(getUrl(),
-				getDefaultSplits(tenantId), null);
-		initTablesWithArrays(tableName, tenantId, null, false, getUrl());
-		String query = "SELECT a_double_array, /* comment ok? */ b_string, a_float FROM " + tableName + " WHERE ?=organization_id and ?=a_float";
-		Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-		Connection conn = DriverManager.getConnection(getUrl(), props);
-        analyzeTable(conn, tableName);
-		try {
-		    PreparedStatement statement = conn.prepareStatement(query);
-			statement.setString(1, tenantId);
-			statement.setFloat(2, 0.01f);
-			ResultSet rs = statement.executeQuery();
-			assertTrue(rs.next());
-			// Need to support primitive
-			Double[] doubleArr = new Double[4];
-			doubleArr[0] = 25.343;
-			doubleArr[1] = 36.763;
-		    doubleArr[2] = 37.56;
-            doubleArr[3] = 386.63;
-			Array array = conn.createArrayOf("DOUBLE",
-					doubleArr);
-			PhoenixArray resultArray = (PhoenixArray) rs.getArray(1);
-			assertEquals(resultArray, array);
-            assertEquals("[25.343, 36.763, 37.56, 386.63]", rs.getString(1));
-			assertEquals(rs.getString("B_string"), B_VALUE);
-			assertTrue(Floats.compare(rs.getFloat(3), 0.01f) == 0);
-			assertFalse(rs.next());
-		} finally {
-			conn.close();
-		}
-	}
-
-    private void analyzeTable(Connection conn, String tableWithArray) throws SQLException {
-        String analyse = "UPDATE STATISTICS  "+tableWithArray;
-		PreparedStatement statement = conn.prepareStatement(analyse);
-        statement.execute();
-    }
-
-	@Test
-	public void testScanWithArrayInWhereClause() throws Exception {
-		String tenantId = getOrganizationId();
-		String tableName = createTableWithArray(getUrl(),
-				getDefaultSplits(tenantId), null);
-		initTablesWithArrays(tableName, tenantId, null, false, getUrl());
-		String query = "SELECT a_double_array, /* comment ok? */ b_string, a_float FROM " + tableName + " WHERE ?=organization_id and ?=a_byte_array";
-		Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-		Connection conn = DriverManager.getConnection(getUrl(), props);
-		//TODO: samarth do we need this?
-		analyzeTable(conn, tableName);
-		try {
-			PreparedStatement statement = conn.prepareStatement(query);
-			statement.setString(1, tenantId);
-			// Need to support primitive
-			Byte[] byteArr = new Byte[2];
-			byteArr[0] = 25;
-			byteArr[1] = 36;
-			Array array = conn.createArrayOf("TINYINT", byteArr);
-			statement.setArray(2, array);
-			ResultSet rs = statement.executeQuery();
-			assertTrue(rs.next());
-			// Need to support primitive
-			Double[] doubleArr = new Double[4];
-			doubleArr[0] = 25.343;
-			doubleArr[1] = 36.763;
-		    doubleArr[2] = 37.56;
-            doubleArr[3] = 386.63;
-			array = conn.createArrayOf("DOUBLE", doubleArr);
-			Array resultArray = rs.getArray(1);
-			assertEquals(resultArray, array);
-            assertEquals("[25.343, 36.763, 37.56, 386.63]", rs.getString(1));
-			assertEquals(rs.getString("B_string"), B_VALUE);
-			assertTrue(Floats.compare(rs.getFloat(3), 0.01f) == 0);
-			assertFalse(rs.next());
-		} finally {
-			conn.close();
-		}
-	}
-
-	@Test
-	public void testScanWithNonFixedWidthArrayInWhereClause() throws Exception {
-		String tenantId = getOrganizationId();
-		String tableName = createTableWithArray(getUrl(),
-				getDefaultSplits(tenantId), null);
-		initTablesWithArrays(tableName, tenantId, null, false, getUrl());
-		String query = "SELECT a_double_array, /* comment ok? */ b_string, a_float FROM  " + tableName + "  WHERE ?=organization_id and ?=a_string_array";
-		Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-		Connection conn = DriverManager.getConnection(getUrl(), props);
-		try {
-			PreparedStatement statement = conn.prepareStatement(query);
-			statement.setString(1, tenantId);
-			// Need to support primitive
-			String[] strArr = new String[4];
-			strArr[0] = "ABC";
-			strArr[1] = "CEDF";
-			strArr[2] = "XYZWER";
-			strArr[3] = "AB";
-			Array array = conn.createArrayOf("VARCHAR", strArr);
-			statement.setArray(2, array);
-			ResultSet rs = statement.executeQuery();
-			assertTrue(rs.next());
-			// Need to support primitive
-			Double[] doubleArr = new Double[4];
-			doubleArr[0] = 25.343;
-			doubleArr[1] = 36.763;
-		    doubleArr[2] = 37.56;
-            doubleArr[3] = 386.63;
-			array = conn.createArrayOf("DOUBLE", doubleArr);
-			Array resultArray = rs.getArray(1);
-			assertEquals(resultArray, array);
-            assertEquals("[25.343, 36.763, 37.56, 386.63]", rs.getString(1));
-			assertEquals(rs.getString("B_string"), B_VALUE);
-			assertTrue(Floats.compare(rs.getFloat(3), 0.01f) == 0);
-			assertFalse(rs.next());
-		} finally {
-			conn.close();
-		}
-	}
-
-	@Test
-	public void testScanWithNonFixedWidthArrayInSelectClause() throws Exception {
-		String tenantId = getOrganizationId();
-		String tableName = createTableWithArray(getUrl(),
-				getDefaultSplits(tenantId), null);
-		initTablesWithArrays(tableName, tenantId, null, false, getUrl());
-		String query = "SELECT a_string_array FROM  " + tableName;
-		Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-		Connection conn = DriverManager.getConnection(getUrl(), props);
-		try {
-			PreparedStatement statement = conn.prepareStatement(query);
-			ResultSet rs = statement.executeQuery();
-			assertTrue(rs.next());
-			String[] strArr = new String[4];
-			strArr[0] = "ABC";
-			strArr[1] = "CEDF";
-			strArr[2] = "XYZWER";
-			strArr[3] = "AB";
-			Array array = conn.createArrayOf("VARCHAR", strArr);
-			PhoenixArray resultArray = (PhoenixArray) rs.getArray(1);
-			assertEquals(resultArray, array);
-            assertEquals("['ABC', 'CEDF', 'XYZWER', 'AB']", rs.getString(1));
-			assertFalse(rs.next());
-		} finally {
-			conn.close();
-		}
-	}
-
-	@Test
-	public void testSelectSpecificIndexOfAnArrayAsArrayFunction()
-			throws Exception {
-		String tenantId = getOrganizationId();
-		String tableName = createTableWithArray(getUrl(),
-				getDefaultSplits(tenantId), null);
-		initTablesWithArrays(tableName, tenantId, null, false, getUrl());
-		String query = "SELECT ARRAY_ELEM(a_double_array,2) FROM  " + tableName;
-		Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-		Connection conn = DriverManager.getConnection(getUrl(), props);
-		try {
-			PreparedStatement statement = conn.prepareStatement(query);
-			ResultSet rs = statement.executeQuery();
-			assertTrue(rs.next());
-			// Need to support primitive
-			Double[] doubleArr = new Double[1];
-			doubleArr[0] = 36.763;
-			conn.createArrayOf("DOUBLE", doubleArr);
-			Double result =  rs.getDouble(1);
-			assertEquals(doubleArr[0], result);
-			assertFalse(rs.next());
-		} finally {
-			conn.close();
-		}
-	}
-
-	@Test
-	public void testSelectSpecificIndexOfAnArray() throws Exception {
-		String tenantId = getOrganizationId();
-		String tableName = createTableWithArray(getUrl(),
-				getDefaultSplits(tenantId), null);
-		initTablesWithArrays(tableName, tenantId, null, false, getUrl());
-		String query = "SELECT a_double_array[3] FROM  " + tableName;
-		Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-		Connection conn = DriverManager.getConnection(getUrl(), props);
-		try {
-			PreparedStatement statement = conn.prepareStatement(query);
-			ResultSet rs = statement.executeQuery();
-			assertTrue(rs.next());
-			// Need to support primitive
-			Double[] doubleArr = new Double[1];
-			doubleArr[0] = 37.56;
-			Double result =  rs.getDouble(1);
-			assertEquals(doubleArr[0], result);
-			assertFalse(rs.next());
-		} finally {
-			conn.close();
-		}
-	}
-
-    @Test
-    public void testCaseWithArray() throws Exception {
-        String tenantId = getOrganizationId();
-        String tableName = createTableWithArray(getUrl(),
-                getDefaultSplits(tenantId), null);
-        initTablesWithArrays(tableName, tenantId, null, false, getUrl());
-        String query = "SELECT CASE WHEN A_INTEGER = 1 THEN a_double_array ELSE null END [3] FROM  " + tableName;
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            // Need to support primitive
-            Double[] doubleArr = new Double[1];
-            doubleArr[0] = 37.56;
-            Double result =  rs.getDouble(1);
-            assertEquals(doubleArr[0], result);
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testUpsertValuesWithArray() throws Exception {
-        String tenantId = getOrganizationId();
-        String tableName = createTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
-        String query = "upsert into  " + tableName + " (ORGANIZATION_ID,ENTITY_ID,a_double_array) values('" + tenantId
-                + "','00A123122312312',ARRAY[2.0,345.8])";
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-                                                                                 // at
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            int executeUpdate = statement.executeUpdate();
-            assertEquals(1, executeUpdate);
-            conn.commit();
-            statement.close();
-            conn.close();
-            // create another connection
-            props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-            conn = DriverManager.getConnection(getUrl(), props);
-            query = "SELECT ARRAY_ELEM(a_double_array,2) FROM  " + tableName;
-            statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            // Need to support primitive
-            Double[] doubleArr = new Double[1];
-            doubleArr[0] = 345.8d;
-            conn.createArrayOf("DOUBLE", doubleArr);
-            Double result = rs.getDouble(1);
-            assertEquals(doubleArr[0], result);
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testUpsertSelectWithSelectAsSubQuery1() throws Exception {
-        String tenantId = getOrganizationId();
-        String table1 = createTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
-        Connection conn = null;
-        try {
-            String table2 = createSimpleTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
-            initSimpleArrayTable(table2, tenantId, null, false);
-            Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-            conn = DriverManager.getConnection(getUrl(), props);
-            String query = "upsert into " + table1 + " (ORGANIZATION_ID,ENTITY_ID,a_double_array) "
-                    + "SELECT organization_id, entity_id, a_double_array  FROM " + table2
-                    + " WHERE a_double_array[2] = 89.96";
-            PreparedStatement statement = conn.prepareStatement(query);
-            int executeUpdate = statement.executeUpdate();
-            assertEquals(1, executeUpdate);
-            conn.commit();
-            statement.close();
-            conn.close();
-            // create another connection
-            props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-            conn = DriverManager.getConnection(getUrl(), props);
-            query = "SELECT ARRAY_ELEM(a_double_array,2) FROM " + table1;
-            statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            // Need to support primitive
-            Double[] doubleArr = new Double[1];
-            doubleArr[0] = 89.96d;
-            Double result = rs.getDouble(1);
-            assertEquals(result, doubleArr[0]);
-            assertFalse(rs.next());
-
-        } finally {
-            if (conn != null) {
-                conn.close();
-            }
-        }
-    }
-
-    @Test
-    public void testArraySelectWithORCondition() throws Exception {
-        String tenantId = getOrganizationId();
-        Connection conn = null;
-        try {
-            String table = createSimpleTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
-            initSimpleArrayTable(table, tenantId, null, false);
-            Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-            conn = DriverManager.getConnection(getUrl(), props);
-            String query = "SELECT a_double_array[1]  FROM " + table
-                    + " WHERE a_double_array[2] = 89.96 or a_char_array[0] = 'a'";
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            Double[] doubleArr = new Double[1];
-            doubleArr[0] = 64.87d;
-            Double result = rs.getDouble(1);
-            assertEquals(result, doubleArr[0]);
-            assertFalse(rs.next());
-
-        } finally {
-            if (conn != null) {
-                conn.close();
-            }
-        }
-    }
-
-    @Test
-    public void testArraySelectWithANY() throws Exception {
-        String tenantId = getOrganizationId();
-        Connection conn = null;
-        try {
-            String table = createSimpleTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
-            initSimpleArrayTable(table, tenantId, null, false);
-            Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-            conn = DriverManager.getConnection(getUrl(), props);
-            String query = "SELECT a_double_array[1]  FROM " + table
-                    + " WHERE CAST(89.96 AS DOUBLE) = ANY(a_double_array)";
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            Double[] doubleArr = new Double[1];
-            doubleArr[0] = 64.87d;
-            Double result = rs.getDouble(1);
-            assertEquals(result, doubleArr[0]);
-            assertFalse(rs.next());
-        } finally {
-            if (conn != null) {
-                conn.close();
-            }
-        }
-    }
-
-    @Test
-    public void testArraySelectWithALL() throws Exception {
-        String tenantId = getOrganizationId();
-        Connection conn = null;
-        try {
-            String table = createSimpleTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
-            initSimpleArrayTable(table, tenantId, null, false);
-            Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-            conn = DriverManager.getConnection(getUrl(), props);
-            String query = "SELECT a_double_array[1]  FROM " + table
-                    + " WHERE CAST(64.87 as DOUBLE) = ALL(a_double_array)";
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertFalse(rs.next());
-        } finally {
-            if (conn != null) {
-                conn.close();
-            }
-        }
-    }
-
-    @Test
-    public void testArraySelectWithANYCombinedWithOR() throws Exception {
-        String tenantId = getOrganizationId();
-        Connection conn = null;
-        try {
-            String table = createSimpleTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
-            initSimpleArrayTable(table, tenantId, null, false);
-            Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-            conn = DriverManager.getConnection(getUrl(), props);
-            String query = "SELECT a_double_array[1]  FROM " + table
-                    + " WHERE  a_char_array[0] = 'f' or CAST(89.96 AS DOUBLE) > ANY(a_double_array)";
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            Double[] doubleArr = new Double[1];
-            doubleArr[0] = 64.87d;
-            Double result = rs.getDouble(1);
-            assertEquals(result, doubleArr[0]);
-            assertFalse(rs.next());
-        } finally {
-            if (conn != null) {
-                conn.close();
-            }
-        }
-    }
-
-    @Test
-    public void testArraySelectWithALLCombinedWithOR() throws Exception {
-
-        String tenantId = getOrganizationId();
-        Connection conn = null;
-        try {
-            String table = createSimpleTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
-            initSimpleArrayTable(table, tenantId, null, false);
-            Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-            conn = DriverManager.getConnection(getUrl(), props);
-            String query = "SELECT a_double_array[1], a_double_array[2]  FROM " + table
-                    + " WHERE  a_char_array[0] = 'f' or CAST(100.0 AS DOUBLE) > ALL(a_double_array)";
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            Double[] doubleArr = new Double[1];
-            doubleArr[0] = 64.87d;
-            Double result = rs.getDouble(1);
-            assertEquals(result, doubleArr[0]);
-            doubleArr = new Double[1];
-            doubleArr[0] = 89.96d;
-            result = rs.getDouble(2);
-            assertEquals(result, doubleArr[0]);
-        } finally {
-            if (conn != null) {
-                conn.close();
-            }
-        }
-    }
-
-    @Test
-    public void testArraySelectWithANYUsingVarLengthArray() throws Exception {
-        Connection conn = null;
-        try {
-    
-            String tenantId = getOrganizationId();
-            String table = createTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
-            initTablesWithArrays(table, tenantId, null, false, getUrl());
-            Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-            conn = DriverManager.getConnection(getUrl(), props);
-            String query = "SELECT a_string_array[1]  FROM " + table
-                    + " WHERE 'XYZWER' = ANY(a_string_array)";
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            String[] strArr = new String[1];
-            strArr[0] = "ABC";
-            String result = rs.getString(1);
-            assertEquals(result, strArr[0]);
-            assertFalse(rs.next());
-            query = "SELECT a_string_array[1]  FROM " + table + " WHERE 'AB' = ANY(a_string_array)";
-            statement = conn.prepareStatement(query);
-            rs = statement.executeQuery();
-            assertTrue(rs.next());
-            result = rs.getString(1);
-            assertEquals(result, strArr[0]);
-            assertFalse(rs.next());
-        } finally {
-            if (conn != null) {
-                conn.close();
-            }
-        }
-    }
-
-    @Test
-    public void testSelectWithArrayWithColumnRef() throws Exception {
-
-        String tenantId = getOrganizationId();
-        String table = createTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
-        initTablesWithArrays(table, tenantId, null, false, getUrl());
-        String query = "SELECT a_integer,ARRAY[1,2,a_integer] FROM " + table + " where organization_id =  '"
-                + tenantId + "'";
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            int val = rs.getInt(1);
-            assertEquals(val, 1);
-            Array array = rs.getArray(2);
-            // Need to support primitive
-            Integer[] intArr = new Integer[3];
-            intArr[0] = 1;
-            intArr[1] = 2;
-            intArr[2] = 1;
-            Array resultArr = conn.createArrayOf("INTEGER", intArr);
-            assertEquals(resultArr, array);
-            assertEquals("[1, 2, 1]", rs.getString(2));
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testSelectWithArrayWithColumnRefWithVarLengthArray() throws Exception {
-
-        String tenantId = getOrganizationId();
-        String table = createTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
-        initTablesWithArrays(table, tenantId, null, false, getUrl());
-        String query = "SELECT b_string,ARRAY['abc','defgh',b_string] FROM " + table + " where organization_id =  '"
-                + tenantId + "'";
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            String val = rs.getString(1);
-            assertEquals(val, "b");
-            Array array = rs.getArray(2);
-            // Need to support primitive
-            String[] strArr = new String[3];
-            strArr[0] = "abc";
-            strArr[1] = "defgh";
-            strArr[2] = "b";
-            Array resultArr = conn.createArrayOf("VARCHAR", strArr);
-            assertEquals(resultArr, array);
-            // since array is var length, last string element is messed up
-            String expectedPrefix = "['abc', 'defgh', 'b";
-            assertTrue("Expected to start with " + expectedPrefix,
-                rs.getString(2).startsWith(expectedPrefix));
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testSelectWithArrayWithColumnRefWithVarLengthArrayWithNullValue() throws Exception {
-
-        String tenantId = getOrganizationId();
-        String table = createTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
-        initTablesWithArrays(table, tenantId, null, false, getUrl());
-        String query = "SELECT b_string,ARRAY['abc',null,'bcd',null,null,b_string] FROM " + table + " where organization_id =  '"
-                + tenantId + "'";
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            String val = rs.getString(1);
-            assertEquals(val, "b");
-            Array array = rs.getArray(2);
-            // Need to support primitive
-            String[] strArr = new String[6];
-            strArr[0] = "abc";
-            strArr[1] = null;
-            strArr[2] = "bcd";
-            strArr[3] = null;
-            strArr[4] = null;
-            strArr[5] = "b";
-            Array resultArr = conn.createArrayOf("VARCHAR", strArr);
-            assertEquals(resultArr, array);
-            String expectedPrefix = "['abc', null, 'bcd', null, null, 'b";
-            assertTrue("Expected to start with " + expectedPrefix,
-                rs.getString(2).startsWith(expectedPrefix));
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testUpsertSelectWithColumnRef() throws Exception {
-
-        String tenantId = getOrganizationId();
-        String table1 = createTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
-        Connection conn = null;
-        try {
-            String table2 = createSimpleTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
-            initSimpleArrayTable(table2, tenantId, null, false);
-            Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-            conn = DriverManager.getConnection(getUrl(), props);
-            String query = "upsert into " + table1 + " (ORGANIZATION_ID,ENTITY_ID, a_unsigned_double, a_double_array) "
-                    + "SELECT organization_id, entity_id, x_double, ARRAY[23.4, 22.1, x_double]  FROM " + table2
-                    + " WHERE a_double_array[2] = 89.96";
-            PreparedStatement statement = conn.prepareStatement(query);
-            int executeUpdate = statement.executeUpdate();
-            assertEquals(1, executeUpdate);
-            conn.commit();
-            statement.close();
-            conn.close();
-            // create another connection
-            props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-            conn = DriverManager.getConnection(getUrl(), props);
-            query = "SELECT ARRAY_ELEM(a_double_array,2) FROM " + table1;
-            statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            // Need to support primitive
-            Double[] doubleArr = new Double[1];
-            doubleArr[0] = 22.1d;
-            Double result = rs.getDouble(1);
-            assertEquals(result, doubleArr[0]);
-            assertFalse(rs.next());
-
-        } finally {
-            if (conn != null) {
-                conn.close();
-            }
-        }
-    }
-
-    @Test
-    public void testCharArraySpecificIndex() throws Exception {
-
-        String tenantId = getOrganizationId();
-        String table = createSimpleTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
-        initSimpleArrayTable(table, tenantId, null, false);
-        String query = "SELECT a_char_array[2] FROM " + table;
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            String[] charArr = new String[1];
-            charArr[0] = "b";
-            String result = rs.getString(1);
-            assertEquals(charArr[0], result);
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testArrayWithDescOrder() throws Exception {
-        Connection conn;
-        PreparedStatement stmt;
-        ResultSet rs;
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        conn.createStatement().execute(
-                "CREATE TABLE  " + table + "  ( k VARCHAR, a_string_array VARCHAR(100) ARRAY[4], b_string_array VARCHAR(100) ARRAY[4] \n"
-                        + " CONSTRAINT pk PRIMARY KEY (k, b_string_array DESC)) \n");
-        conn.close();
-        conn = DriverManager.getConnection(getUrl(), props);
-        stmt = conn.prepareStatement("UPSERT INTO " + table + " VALUES(?,?,?)");
-        stmt.setString(1, "a");
-        String[] s = new String[] { "abc", "def", "ghi", "jkll", null, null, "xxx" };
-        Array array = conn.createArrayOf("VARCHAR", s);
-        stmt.setArray(2, array);
-        s = new String[] { "abc", "def", "ghi", "jkll", null, null, null, "xxx" };
-        array = conn.createArrayOf("VARCHAR", s);
-        stmt.setArray(3, array);
-        stmt.execute();
-        conn.commit();
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        rs = conn.createStatement().executeQuery("SELECT b_string_array FROM  " + table);
-        assertTrue(rs.next());
-        PhoenixArray strArr = (PhoenixArray)rs.getArray(1);
-        assertEquals(array, strArr);
-        assertEquals("['abc', 'def', 'ghi', 'jkll', null, null, null, 'xxx']", rs.getString(1));
-        conn.close();
-    }
-
-    @Test
-    public void testArrayWithFloatArray() throws Exception {
-        Connection conn;
-        PreparedStatement stmt;
-        ResultSet rs;
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        conn.createStatement().execute("CREATE TABLE  " + table + "  ( k VARCHAR PRIMARY KEY, a Float ARRAY[])");
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES('a',ARRAY[2.0,3.0])");
-        int res = stmt.executeUpdate();
-        assertEquals(1, res);
-        conn.commit();
-        conn.close();
-        conn = DriverManager.getConnection(getUrl(), props);
-        rs = conn.createStatement().executeQuery("SELECT ARRAY_ELEM(a,2) FROM  " + table);
-        assertTrue(rs.next());
-        Float f = new Float(3.0);
-        assertEquals(f, (Float)rs.getFloat(1));
-        conn.close();
-    }
-
-    @Test
-    public void testArrayWithVarCharArray() throws Exception {
-        Connection conn;
-        PreparedStatement stmt;
-        ResultSet rs;
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        conn.createStatement().execute("CREATE TABLE  " + table + "  ( k VARCHAR PRIMARY KEY, a VARCHAR ARRAY[])");
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES('a',ARRAY['a',null])");
-        int res = stmt.executeUpdate();
-        assertEquals(1, res);
-        conn.commit();
-        conn.close();
-        conn = DriverManager.getConnection(getUrl(), props);
-        rs = conn.createStatement().executeQuery("SELECT ARRAY_ELEM(a,2) FROM  " + table);
-        assertTrue(rs.next());
-        assertEquals(null, rs.getString(1));
-        conn.close();
-    }
-
-    @Test
-    public void testArraySelectSingleArrayElemWithCast() throws Exception {
-        Connection conn;
-        PreparedStatement stmt;
-        ResultSet rs;
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        conn.createStatement().execute("CREATE TABLE  " + table + "  ( k VARCHAR PRIMARY KEY, a bigint ARRAY[])");
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES(?,?)");
-        stmt.setString(1, "a");
-        Long[] s = new Long[] {1l, 2l};
-        Array array = conn.createArrayOf("BIGINT", s);
-        stmt.setArray(2, array);
-        stmt.execute();
-        conn.commit();
-        conn.close();
-        conn = DriverManager.getConnection(getUrl(), props);
-        rs = conn.createStatement().executeQuery("SELECT k, CAST(a[2] AS DOUBLE) FROM  " + table);
-        assertTrue(rs.next());
-        assertEquals("a",rs.getString(1));
-        Double d = new Double(2.0);
-        assertEquals(d, (Double)rs.getDouble(2));
-        conn.close();
-    }
-
-    @Test
-    public void testArraySelectGetString() throws Exception {
-        Connection conn;
-        PreparedStatement stmt;
-
-        String tenantId = getOrganizationId();
-
-        // create the table
-        String tableName = createTableWithAllArrayTypes(getUrl(), getDefaultSplits(tenantId), null);
-
-        // populate the table with data
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        conn = DriverManager.getConnection(getUrl(), props);
-        stmt =
-                conn.prepareStatement("UPSERT INTO "
-                        + tableName
-                        + "(ORGANIZATION_ID, ENTITY_ID, BOOLEAN_ARRAY, BYTE_ARRAY, DOUBLE_ARRAY, FLOAT_ARRAY, INT_ARRAY, LONG_ARRAY, SHORT_ARRAY, STRING_ARRAY)\n"
-                        + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
-        stmt.setString(1, tenantId);
-        stmt.setString(2, ROW1);
-        // boolean array
-        Array boolArray = conn.createArrayOf("BOOLEAN", new Boolean[] { true, false });
-        int boolIndex = 3;
-        stmt.setArray(boolIndex, boolArray);
-        // byte array
-        Array byteArray = conn.createArrayOf("TINYINT", new Byte[] { 11, 22 });
-        int byteIndex = 4;
-        stmt.setArray(byteIndex, byteArray);
-        // double array
-        Array doubleArray = conn.createArrayOf("DOUBLE", new Double[] { 67.78, 78.89 });
-        int doubleIndex = 5;
-        stmt.setArray(doubleIndex, doubleArray);
-        // float array
-        Array floatArray = conn.createArrayOf("FLOAT", new Float[] { 12.23f, 45.56f });
-        int floatIndex = 6;
-        stmt.setArray(floatIndex, floatArray);
-        // int array
-        Array intArray = conn.createArrayOf("INTEGER", new Integer[] { 5555, 6666 });
-        int intIndex = 7;
-        stmt.setArray(intIndex, intArray);
-        // long array
-        Array longArray = conn.createArrayOf("BIGINT", new Long[] { 7777777L, 8888888L });
-        int longIndex = 8;
-        stmt.setArray(longIndex, longArray);
-        // short array
-        Array shortArray = conn.createArrayOf("SMALLINT", new Short[] { 333, 444 });
-        int shortIndex = 9;
-        stmt.setArray(shortIndex, shortArray);
-        // create character array
-        Array stringArray = conn.createArrayOf("VARCHAR", new String[] { "a", "b" });
-        int stringIndex = 10;
-        stmt.setArray(stringIndex, stringArray);
-        stmt.execute();
-        conn.commit();
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        stmt =
-                conn.prepareStatement("SELECT organization_id, entity_id, boolean_array, byte_array, double_array, float_array, int_array, long_array, short_array, string_array FROM "
-                        + tableName);
-        //TODO: samarth check if this is needed
-        analyzeTable(conn, tableName);
-
-        ResultSet rs = stmt.executeQuery();
-        assertTrue(rs.next());
-
-        assertEquals(tenantId, rs.getString(1));
-        assertEquals(ROW1, rs.getString(2));
-        
-        assertArrayGetString(rs, boolIndex, boolArray, "true, false");
-        assertArrayGetString(rs, byteIndex, byteArray, "11, 22");
-        assertArrayGetString(rs, doubleIndex, doubleArray, "67.78, 78.89");
-        assertArrayGetString(rs, floatIndex, floatArray, "12.23, 45.56");
-        assertArrayGetString(rs, intIndex, intArray, "5555, 6666");
-        assertArrayGetString(rs, longIndex, longArray, "7777777, 8888888");
-        assertArrayGetString(rs, shortIndex, shortArray, "333, 444");
-        assertArrayGetString(rs, stringIndex, stringArray, "'a', 'b'");
-        conn.close();
-    }
-
-    private void assertArrayGetString(ResultSet rs, int arrayIndex, Array expectedArray, String expectedString)
-            throws SQLException {
-        assertEquals(expectedArray, rs.getArray(arrayIndex));
-        assertEquals("[" + expectedString + "]", rs.getString(arrayIndex));
-    }
-    
-    @Test
-    public void testArrayWithCast() throws Exception {
-        Connection conn;
-        PreparedStatement stmt;
-        ResultSet rs;
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName(); 
-        conn.createStatement().execute("CREATE TABLE " + table + " ( k VARCHAR PRIMARY KEY, a bigint ARRAY[])");
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES(?,?)");
-        stmt.setString(1, "a");
-        Long[] s = new Long[] { 1l, 2l };
-        Array array = conn.createArrayOf("BIGINT", s);
-        stmt.setArray(2, array);
-        stmt.execute();
-        conn.commit();
-        conn.close();
-        conn = DriverManager.getConnection(getUrl(), props);
-        rs = conn.createStatement().executeQuery("SELECT CAST(a AS DOUBLE []) FROM  " + table);
-        assertTrue(rs.next());
-        Double[] d = new Double[] { 1.0, 2.0 };
-        array = conn.createArrayOf("DOUBLE", d);
-        PhoenixArray arr = (PhoenixArray)rs.getArray(1);
-        assertEquals(array, arr);
-        assertEquals("[1.0, 2.0]", rs.getString(1));
-        conn.close();
-    }
-
-    @Test
-    public void testArrayWithCastForVarLengthArr() throws Exception {
-        Connection conn;
-        PreparedStatement stmt;
-        ResultSet rs;
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        conn.createStatement().execute("CREATE TABLE  " + table + "  ( k VARCHAR PRIMARY KEY, a VARCHAR(5) ARRAY)");
-        conn.close();
-        conn = DriverManager.getConnection(getUrl(), props);
-        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES(?,?)");
-        stmt.setString(1, "a");
-        String[] s = new String[] { "1", "2" };
-        PhoenixArray array = (PhoenixArray)conn.createArrayOf("VARCHAR", s);
-        stmt.setArray(2, array);
-        stmt.execute();
-        conn.commit();
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        rs = conn.createStatement().executeQuery("SELECT CAST(a AS CHAR ARRAY) FROM  " + table);
-        assertTrue(rs.next());
-        PhoenixArray arr = (PhoenixArray)rs.getArray(1);
-        String[] array2 = (String[])array.getArray();
-        String[] array3 = (String[])arr.getArray();
-        assertEquals(array2[0], array3[0]);
-        assertEquals(array2[1], array3[1]);
-        assertEquals("['1', '2']", rs.getString(1));
-        conn.close();
-    }
-
-    @Test
-    public void testFixedWidthCharArray() throws Exception {
-        Connection conn;
-        PreparedStatement stmt;
-        ResultSet rs;
-
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        conn.createStatement().execute("CREATE TABLE  " + table + "  ( k VARCHAR PRIMARY KEY, a CHAR(5) ARRAY)");
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        rs = conn.getMetaData().getColumns(null, null, table, "A");
-        assertTrue(rs.next());
-        assertEquals(5, rs.getInt("COLUMN_SIZE"));
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES(?,?)");
-        stmt.setString(1, "a");
-        String[] s = new String[] {"1","2"};
-        Array array = conn.createArrayOf("CHAR", s);
-        stmt.setArray(2, array);
-        stmt.execute();
-        conn.commit();
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        rs = conn.createStatement().executeQuery("SELECT k, a[2] FROM  " + table);
-        assertTrue(rs.next());
-        assertEquals("a",rs.getString(1));
-        assertEquals("2",rs.getString(2));
-        conn.close();
-    }
-
-	@Test
-	public void testSelectArrayUsingUpsertLikeSyntax() throws Exception {
-		String tenantId = getOrganizationId();
-		String table = createTableWithArray(getUrl(),
-				getDefaultSplits(tenantId), null);
-		initTablesWithArrays(table, tenantId, null, false, getUrl());
-		String query = "SELECT a_double_array FROM  " + table + "  WHERE a_double_array = CAST(ARRAY [ 25.343, 36.763, 37.56,386.63] AS DOUBLE ARRAY)";
-		Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-		Connection conn = DriverManager.getConnection(getUrl(), props);
-		try {
-			PreparedStatement statement = conn.prepareStatement(query);
-			ResultSet rs = statement.executeQuery();
-			assertTrue(rs.next());
-			Double[] doubleArr =  new Double[4];
-            doubleArr[0] = 25.343;
-            doubleArr[1] = 36.763;
-            doubleArr[2] = 37.56;
-            doubleArr[3] = 386.63;
-			Array array = conn.createArrayOf("DOUBLE", doubleArr);
-			PhoenixArray resultArray = (PhoenixArray) rs.getArray(1);
-			assertEquals(resultArray, array);
-            assertEquals("[25.343, 36.763, 37.56, 386.63]", rs.getString(1));
-			assertFalse(rs.next());
-		} finally {
-			conn.close();
-		}
-	}
-
-	@Test
-	public void testArrayIndexUsedInWhereClause() throws Exception {
-		String tenantId = getOrganizationId();
-		String table = createTableWithArray(getUrl(),
-				getDefaultSplits(tenantId), null);
-		initTablesWithArrays(table, tenantId, null, false, getUrl());
-		int a_index = 0;
-		String query = "SELECT a_double_array[2] FROM  " + table + "  where a_double_array["+a_index+"2]<?";
-		Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-		Connection conn = DriverManager.getConnection(getUrl(), props);
-		try {
-			PreparedStatement statement = conn.prepareStatement(query);
-			Double[] doubleArr = new Double[1];
-			doubleArr[0] = 40.0;
-			conn.createArrayOf("DOUBLE", doubleArr);
-			statement.setDouble(1, 40.0d);
-			ResultSet rs = statement.executeQuery();
-			assertTrue(rs.next());
-			// Need to support primitive
-			doubleArr = new Double[1];
-			doubleArr[0] = 36.763;
-			Double result =  rs.getDouble(1);
-			assertEquals(doubleArr[0], result);
-			assertFalse(rs.next());
-		} finally {
-			conn.close();
-		}
-	}
-
-	@Test
-	public void testArrayIndexUsedInGroupByClause() throws Exception {
-		String tenantId = getOrganizationId();
-		String table = createTableWithArray(getUrl(),
-				getDefaultSplits(tenantId), null);
-		initTablesWithArrays(table, tenantId, null, false, getUrl());
-		String query = "SELECT a_double_array[2] FROM " + table + "  GROUP BY a_double_array[2]";
-		Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-		Connection conn = DriverManager.getConnection(getUrl(), props);
-		try {
-			PreparedStatement statement = conn.prepareStatement(query);
-			Double[] doubleArr = new Double[1];
-			doubleArr[0] = 40.0;
-			conn.createArrayOf("DOUBLE", doubleArr);
-			ResultSet rs = statement.executeQuery();
-			assertTrue(rs.next());
-			doubleArr = new Double[1];
-			doubleArr[0] = 36.763;
-			Double result =  rs.getDouble(1);
-			assertEquals(doubleArr[0], result);
-			assertFalse(rs.next());
-		} finally {
-			conn.close();
-		}
-	}
-
-	@Test
-	public void testVariableLengthArrayWithNullValue() throws Exception {
-		String tenantId = getOrganizationId();
-		String table = createTableWithArray(getUrl(),
-				getDefaultSplits(tenantId), null);
-		initTablesWithArrays(table, tenantId, null, true, getUrl());
-		String query = "SELECT a_string_array[2] FROM  " + table;
-		Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-		Connection conn = DriverManager.getConnection(getUrl(), props);
-		try {
-			PreparedStatement statement = conn.prepareStatement(query);
-			ResultSet rs = statement.executeQuery();
-			assertTrue(rs.next());
-			String[] strArr = new String[1];
-			strArr[0] = "XYZWER";
-			String result = rs.getString(1);
-			assertNull(result);
-		} finally {
-			conn.close();
-		}
-	}
-
-	@Test
-	public void testSelectSpecificIndexOfAVariableArrayAlongWithAnotherColumn1() throws Exception {
-        String tenantId = getOrganizationId();
-        String table = createTableWithArray(getUrl(),
-                getDefaultSplits(tenantId), null);
-        initTablesWithArrays(table, tenantId, null, false, getUrl());
-        String query = "SELECT a_string_array[3],A_INTEGER FROM  " + table;
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            String[] strArr = new String[1];
-            strArr[0] = "XYZWER";
-            String result = rs.getString(1);
-            assertEquals(strArr[0], result);
-            int a_integer = rs.getInt(2);
-            assertEquals(1, a_integer);
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-	}
-
-    @Test
-    public void testSelectSpecificIndexOfAVariableArrayAlongWithAnotherColumn2() throws Exception {
-
-        String tenantId = getOrganizationId();
-        String table = createTableWithArray(getUrl(),
-                getDefaultSplits(tenantId), null);
-        initTablesWithArrays(table, tenantId, null, false, getUrl());
-        String query = "SELECT A_INTEGER, a_string_array[3] FROM  " + table;
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            String[] strArr = new String[1];
-            strArr[0] = "XYZWER";
-            int a_integer = rs.getInt(1);
-            assertEquals(1, a_integer);
-            String result = rs.getString(2);
-            assertEquals(strArr[0], result);
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testSelectMultipleArrayColumns() throws Exception {
-
-        String tenantId = getOrganizationId();
-        String table = createTableWithArray(getUrl(),
-                getDefaultSplits(tenantId), null);
-        initTablesWithArrays(table, tenantId, null, false, getUrl());
-        String query = "SELECT  a_string_array[3], a_double_array[2] FROM  " + table;
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            String[] strArr = new String[1];
-            strArr[0] = "XYZWER";
-            Double[] doubleArr = new Double[1];
-            doubleArr[0] = 36.763d;
-            Double a_double = rs.getDouble(2);
-            assertEquals(doubleArr[0], a_double);
-            String result = rs.getString(1);
-            assertEquals(strArr[0], result);
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testSelectSameArrayColumnMultipleTimesWithDifferentIndices() throws Exception {
-
-        String tenantId = getOrganizationId();
-        String table = createTableWithArray(getUrl(),
-                getDefaultSplits(tenantId), null);
-        initTablesWithArrays(table, tenantId, null, false, getUrl());
-        String query = "SELECT a_string_array[1], a_string_array[2], " +
-                "a_string_array[3], a_double_array[1], a_double_array[2], a_double_array[3] " +
-                "FROM  " + table;
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            assertEquals("ABC", rs.getString(1));
-            assertEquals("CEDF", rs.getString(2));
-            assertEquals("XYZWER", rs.getString(3));
-            assertEquals(25.343, rs.getDouble(4), 0.0);
-            assertEquals(36.763, rs.getDouble(5), 0.0);
-            assertEquals(37.56, rs.getDouble(6), 0.0);
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testSelectSameArrayColumnMultipleTimesWithSameIndices() throws Exception {
-
-        String tenantId = getOrganizationId();
-        String table = createTableWithArray(getUrl(),
-                getDefaultSplits(tenantId), null);
-        initTablesWithArrays(table, tenantId, null, false, getUrl());
-        String query = "SELECT a_string_array[3], a_string_array[3] FROM " + table;
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            String[] strArr = new String[1];
-            strArr[0] = "XYZWER";
-            String result = rs.getString(1);
-            assertEquals(strArr[0], result);
-            result = rs.getString(2);
-            assertEquals(strArr[0], result);
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-	@Test
-	public void testSelectSpecificIndexOfAVariableArray() throws Exception {
-		String tenantId = getOrganizationId();
-		String table = createTableWithArray(getUrl(),
-				getDefaultSplits(tenantId), null);
-		initTablesWithArrays(table, tenantId, null, false, getUrl());
-		String query = "SELECT a_string_array[3] FROM  " + table;
-		Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-		Connection conn = DriverManager.getConnection(getUrl(), props);
-		try {
-			PreparedStatement statement = conn.prepareStatement(query);
-			ResultSet rs = statement.executeQuery();
-			assertTrue(rs.next());
-			String[] strArr = new String[1];
-			strArr[0] = "XYZWER";
-			String result = rs.getString(1);
-			assertEquals(strArr[0], result);
-			assertFalse(rs.next());
-		} finally {
-			conn.close();
-		}
-	}
-
-	@Test
-	public void testWithOutOfRangeIndex() throws Exception {
-		String tenantId = getOrganizationId();
-		String table = createTableWithArray(getUrl(),
-				getDefaultSplits(tenantId), null);
-		initTablesWithArrays(table, tenantId, null, false, getUrl());
-		String query = "SELECT a_double_array[100] FROM  " + table;
-		Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-		Connection conn = DriverManager.getConnection(getUrl(), props);
-		try {
-			PreparedStatement statement = conn.prepareStatement(query);
-			ResultSet rs = statement.executeQuery();
-			assertTrue(rs.next());
-			PhoenixArray resultArray = (PhoenixArray) rs.getArray(1);
-			assertNull(resultArray);
-		} finally {
-			conn.close();
-		}
-	}
-
-	@Test
-	public void testArrayLengthFunctionForVariableLength() throws Exception {
-		String tenantId = getOrganizationId();
-		String table = createTableWithArray(getUrl(),
-				getDefaultSplits(tenantId), null);
-		initTablesWithArrays(table, tenantId, null, false, getUrl());
-		String query = "SELECT ARRAY_LENGTH(a_string_array) FROM " + table;
-		Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-		Connection conn = DriverManager.getConnection(getUrl(), props);
-		try {
-			PreparedStatement statement = conn.prepareStatement(query);
-			ResultSet rs = statement.executeQuery();
-			assertTrue(rs.next());
-			int result = rs.getInt(1);
-			assertEquals(result, 4);
-			assertFalse(rs.next());
-		} finally {
-			conn.close();
-		}
-	}
-
-
-	@Test
-	public void testArrayLengthFunctionForFixedLength() throws Exception {
-		String tenantId = getOrganizationId();
-		String table = createTableWithArray(getUrl(),
-				getDefaultSplits(tenantId), null);
-		initTablesWithArrays(table, tenantId, null, false, getUrl());
-		String query = "SELECT ARRAY_LENGTH(a_double_array) FROM " + table;
-		Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-		Connection conn = DriverManager.getConnection(getUrl(), props);
-		try {
-			PreparedStatement statement = conn.prepareStatement(query);
-			ResultSet rs = statement.executeQuery();
-			assertTrue(rs.next());
-			int result = rs.getInt(1);
-			assertEquals(result, 4);
-			assertFalse(rs.next());
-		} finally {
-			conn.close();
-		}
-	}
-
-    @Test
-    public void testArraySizeRoundtrip() throws Exception {
-
-        String tenantId = getOrganizationId();
-        String table = createTableWithArray(getUrl(),
-                getDefaultSplits(tenantId), null);
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            ResultSet rs = conn.getMetaData().getColumns(null, null, StringUtil.escapeLike(table), StringUtil.escapeLike(SchemaUtil.normalizeIdentifier("x_long_array")));
-            assertTrue(rs.next());
-            assertEquals(5, rs.getInt("ARRAY_SIZE"));
-            assertFalse(rs.next());
-
-            rs = conn.getMetaData().getColumns(null, null, StringUtil.escapeLike(table), StringUtil.escapeLike(SchemaUtil.normalizeIdentifier("a_string_array")));
-            assertTrue(rs.next());
-            assertEquals(3, rs.getInt("ARRAY_SIZE"));
-            assertFalse(rs.next());
-
-            rs = conn.getMetaData().getColumns(null, null, StringUtil.escapeLike(table), StringUtil.escapeLike(SchemaUtil.normalizeIdentifier("a_double_array")));
-            assertTrue(rs.next());
-            assertEquals(0, rs.getInt("ARRAY_SIZE"));
-            assertTrue(rs.wasNull());
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testVarLengthArrComparisonInWhereClauseWithSameArrays() throws Exception {
-        Connection conn;
-        PreparedStatement stmt;
-        ResultSet rs;
-
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        conn.createStatement()
-                .execute(
-                        "CREATE TABLE  " + table + "  ( k VARCHAR PRIMARY KEY, a_string_array VARCHAR(100) ARRAY[4], b_string_array VARCHAR(100) ARRAY[4])");
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES(?,?,?)");
-        stmt.setString(1, "a");
-        String[] s = new String[] {"abc","def", "ghi","jkl"};
-        Array array = conn.createArrayOf("VARCHAR", s);
-        stmt.setArray(2, array);
-        s = new String[] {"abc","def", "ghi","jkl"};
-        array = conn.createArrayOf("VARCHAR", s);
-        stmt.setArray(3, array);
-        stmt.execute();
-        conn.commit();
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        rs = conn.createStatement().executeQuery("SELECT k, a_string_array[2] FROM  " + table + "  where a_string_array=b_string_array");
-        assertTrue(rs.next());
-        assertEquals("a",rs.getString(1));
-        assertEquals("def",rs.getString(2));
-        conn.close();
-    }
-
-    @Test
-    public void testVarLengthArrComparisonInWhereClauseWithDiffSizeArrays() throws Exception {
-        Connection conn;
-        PreparedStatement stmt;
-        ResultSet rs;
-
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        conn.createStatement()
-                .execute(
-                        "CREATE TABLE  " + table + "  ( k VARCHAR PRIMARY KEY, a_string_array VARCHAR(100) ARRAY[4], b_string_array VARCHAR(100) ARRAY[4])");
-        conn.close();
-        conn = DriverManager.getConnection(getUrl(), props);
-        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES(?,?,?)");
-        stmt.setString(1, "a");
-        String[] s = new String[] { "abc", "def", "ghi", "jkll" };
-        Array array = conn.createArrayOf("VARCHAR", s);
-        stmt.setArray(2, array);
-        s = new String[] { "abc", "def", "ghi", "jklm" };
-        array = conn.createArrayOf("VARCHAR", s);
-        stmt.setArray(3, array);
-        stmt.execute();
-        conn.commit();
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        rs = conn.createStatement().executeQuery(
-                "SELECT k, a_string_array[2] FROM  " + table + "  where a_string_array<b_string_array");
-        assertTrue(rs.next());
-        assertEquals("a", rs.getString(1));
-        assertEquals("def", rs.getString(2));
-        conn.close();
-    }
-
-    @Test
-    public void testVarLengthArrComparisonWithNulls() throws Exception {
-        Connection conn;
-        PreparedStatement stmt;
-        ResultSet rs;
-
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        conn.createStatement()
-                .execute(
-                        "CREATE TABLE  " + table + "  ( k VARCHAR PRIMARY KEY, a_string_array VARCHAR(100) ARRAY[4], b_string_array VARCHAR(100) ARRAY[4])");
-        conn.close();
-        conn = DriverManager.getConnection(getUrl(), props);
-        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES(?,?,?)");
-        stmt.setString(1, "a");
-        String[] s = new String[] { "abc", "def", "ghi", "jkll", null, null, "xxx" };
-        Array array = conn.createArrayOf("VARCHAR", s);
-        stmt.setArray(2, array);
-        s = new String[] { "abc", "def", "ghi", "jkll", null, null, null, "xxx" };
-        array = conn.createArrayOf("VARCHAR", s);
-        stmt.setArray(3, array);
-        stmt.execute();
-        conn.commit();
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        rs = conn.createStatement().executeQuery(
-                "SELECT k, a_string_array[2] FROM  " + table + "  where a_string_array>b_string_array");
-        assertTrue(rs.next());
-        assertEquals("a", rs.getString(1));
-        assertEquals("def", rs.getString(2));
-        conn.close();
-    }
-
-    @Test
-    public void testUpsertValuesWithNull() throws Exception {
-
-        String tenantId = getOrganizationId();
-        String table = createTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
-        String query = "upsert into  " + table + " (ORGANIZATION_ID,ENTITY_ID,a_double_array) values('" + tenantId
-                + "','00A123122312312',null)";
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-                                                                                 // at
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            int executeUpdate = statement.executeUpdate();
-            assertEquals(1, executeUpdate);
-            conn.commit();
-            statement.close();
-            conn.close();
-            // create another connection
-            props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-            conn = DriverManager.getConnection(getUrl(), props);
-            query = "SELECT ARRAY_ELEM(a_double_array,2) FROM  " + table;
-            statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            // Need to support primitive
-            Double[] doubleArr = new Double[1];
-            doubleArr[0] = 0.0d;
-            conn.createArrayOf("DOUBLE", doubleArr);
-            Double result = rs.getDouble(1);
-            assertEquals(doubleArr[0], result);
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testUpsertValuesWithNullUsingPreparedStmt() throws Exception {
-
-        String tenantId = getOrganizationId();
-        String table = createTableWithArray(getUrl(), getDefaultSplits(tenantId), null);
-        String query = "upsert into  " + table + " (ORGANIZATION_ID,ENTITY_ID,a_string_array) values(?, ?, ?)";
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-                                                                                 // at
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            statement.setString(1, tenantId);
-            statement.setString(2, "00A123122312312");
-            statement.setNull(3, Types.ARRAY);
-            int executeUpdate = statement.executeUpdate();
-            assertEquals(1, executeUpdate);
-            conn.commit();
-            statement.close();
-            conn.close();
-            // create another connection
-            props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-            conn = DriverManager.getConnection(getUrl(), props);
-            query = "SELECT ARRAY_ELEM(a_string_array,1) FROM  " + table;
-            statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue(rs.next());
-            String[] strArr = new String[1];
-            strArr[0] = null;
-            conn.createArrayOf("VARCHAR", strArr);
-            String result = rs.getString(1);
-            assertEquals(strArr[0], result);
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testPKWithArray() throws Exception {
-        Connection conn;
-        PreparedStatement stmt;
-        ResultSet rs;
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        conn.createStatement()
-                .execute(
-                        "CREATE TABLE  " + table + "  ( k VARCHAR, a_string_array VARCHAR(100) ARRAY[4], b_string_array VARCHAR(100) ARRAY[4] \n"
-                        + " CONSTRAINT pk PRIMARY KEY (k, b_string_array)) \n");
-        conn.close();
-        conn = DriverManager.getConnection(getUrl(), props);
-        stmt = conn.prepareStatement("UPSERT INTO  " + table + "  VALUES(?,?,?)");
-        stmt.setString(1, "a");
-        String[] s = new String[] { "abc", "def", "ghi", "jkll", null, null, "xxx" };
-        Array array = conn.createArrayOf("VARCHAR", s);
-        stmt.setArray(2, array);
-        s = new String[] { "abc", "def", "ghi", "jkll", null, null, null, "xxx" };
-        array = conn.createArrayOf("VARCHAR", s);
-        stmt.setArray(3, array);
-        stmt.execute();
-        conn.commit();
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        rs = conn.createStatement().executeQuery(
-                "SELECT k, a_string_array[2] FROM  " + table + "  where b_string_array[8]='xxx'");
-        assertTrue(rs.next());
-        assertEquals("a", rs.getString(1));
-        assertEquals("def", rs.getString(2));
-        conn.close();
-    }
-
-    @Test
-    public void testPKWithArrayNotInEnd() throws Exception {
-        Connection conn;
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        try {
-            conn.createStatement().execute(
-                    "CREATE TABLE  " + table + "  ( a_string_array VARCHAR(100) ARRAY[4], b_string_array VARCHAR(100) ARRAY[4], k VARCHAR  \n"
-                            + " CONSTRAINT pk PRIMARY KEY (b_string_array, k))");
-            conn.close();
-            fail();
-        } catch (SQLException e) {
-        } finally {
-            if (conn != null) {
-                conn.close();
-            }
-        }
-
-    }
-
-    @Test
-    public void testArrayRefToLiteral() throws Exception {
-        Connection conn;
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            PreparedStatement stmt = conn.prepareStatement("select ?[2] from \"SYSTEM\".\"catalog\" limit 1");
-            Array array = conn.createArrayOf("CHAR", new String[] {"a","b","c"});
-            stmt.setArray(1, array);
-            ResultSet rs = stmt.executeQuery();
-            assertTrue(rs.next());
-            assertEquals("b", rs.getString(1));
-            assertFalse(rs.next());
-        } catch (SQLException e) {
-        } finally {
-            if (conn != null) {
-                conn.close();
-            }
-        }
-
-    }
-    
-    private static String createTableWithAllArrayTypes(String url, byte[][] bs, Object object) throws SQLException {
-        String tableName = generateUniqueName();
-        String ddlStmt = "create table "
-                + tableName
-                + "   (organization_id char(15) not null, \n"
-                + "    entity_id char(15) not null,\n"
-                + "    boolean_array boolean array,\n"
-                + "    byte_array tinyint array,\n"
-                + "    double_array double array[],\n"
-                + "    float_array float array,\n"
-                + "    int_array integer array,\n"
-                + "    long_array bigint[5],\n"
-                + "    short_array smallint array,\n"
-                + "    string_array varchar(100) array[3],\n"
-                + "    CONSTRAINT pk PRIMARY KEY (organization_id, entity_id)\n"
-                + ")";
-        BaseTest.createTestTable(url, ddlStmt, bs, null);
-        return tableName;
-    }
-    
-    private static String createTableWithArray(String url, byte[][] bs, Object object) throws SQLException {
-        String tableName = generateUniqueName();
-		String ddlStmt = "create table "
-				+ tableName
-				+ "   (organization_id char(15) not null, \n"
-				+ "    entity_id char(15) not null,\n"
-				+ "    a_string_array varchar(100) array[3],\n"
-				+ "    b_string varchar(100),\n"
-				+ "    a_integer integer,\n"
-				+ "    a_date date,\n"
-				+ "    a_time time,\n"
-				+ "    a_timestamp timestamp,\n"
-				+ "    x_decimal decimal(31,10),\n"
-				+ "    x_long_array bigint[5],\n"
-				+ "    x_integer integer,\n"
-				+ "    a_byte_array tinyint array,\n"
-				+ "    a_short smallint,\n"
-				+ "    a_float float,\n"
-				+ "    a_double_array double array[],\n"
-				+ "    a_unsigned_float unsigned_float,\n"
-				+ "    a_unsigned_double unsigned_double \n"
-				+ "    CONSTRAINT pk PRIMARY KEY (organization_id, entity_id)\n"
-				+ ")";
-		BaseTest.createTestTable(url, ddlStmt, bs, null);
-		return tableName;
-	}
-
-	private static String createSimpleTableWithArray(String url, byte[][] bs, Object object) throws SQLException {
-		String tableName = generateUniqueName();
-	    String ddlStmt = "create table "
-				+ tableName
-				+ "   (organization_id char(15) not null, \n"
-				+ "    entity_id char(15) not null,\n"
-				+ "    x_double double,\n"
-				+ "    a_double_array double array[],\n"
-				+ "    a_char_array char(5) array[],\n"
-				+ "    CONSTRAINT pk PRIMARY KEY (organization_id, entity_id)\n"
-				+ ")";
-		BaseTest.createTestTable(url, ddlStmt, bs, null);
-		return tableName;
-	}
-
-	private static void initSimpleArrayTable(String tableName, String tenantId, Date date, boolean useNull) throws Exception {
-   	   Properties props = new Properties();
-
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        try {
-            // Insert all rows at ts
-            PreparedStatement stmt = conn.prepareStatement(
-                    "upsert into " + tableName +
-                    "(" +
-                    "    ORGANIZATION_ID, " +
-                    "    ENTITY_ID, " +
-                    "    x_double, " +
-                    "    a_double_array, a_char_array)" +
-                    "VALUES (?, ?, ?, ?, ?)");
-            stmt.setString(1, tenantId);
-            stmt.setString(2, ROW1);
-            stmt.setDouble(3, 1.2d);
-            // Need to support primitive
-            Double[] doubleArr =  new Double[2];
-            doubleArr[0] = 64.87;
-            doubleArr[1] = 89.96;
-            //doubleArr[2] = 9.9;
-            Array array = conn.createArrayOf("DOUBLE", doubleArr);
-            stmt.setArray(4, array);
-
-            // create character array
-            String[] charArr =  new String[2];
-            charArr[0] = "a";
-            charArr[1] = "b";
-            array = conn.createArrayOf("CHAR", charArr);
-            stmt.setArray(5, array);
-            stmt.execute();
-
-            conn.commit();
-        } finally {
-            conn.close();
-        }
-   }
-
-    @Test
-    public void testArrayConstructorWithMultipleRows1() throws Exception {
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        String ddl = "CREATE TABLE  " + table + "  (region_name VARCHAR PRIMARY KEY, a INTEGER, b INTEGER)";
-        conn.createStatement().execute(ddl);
-        conn.commit();
-        conn.close();
-        conn = DriverManager.getConnection(getUrl(), props);
-        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('a', 6,3)");
-        stmt.execute();
-        stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('b', 2,4)");
-        stmt.execute();
-        stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('c', 6,3)");
-        stmt.execute();
-        conn.commit();
-        conn.close();
-        conn = DriverManager.getConnection(getUrl(), props);
-        ResultSet rs;
-        rs = conn.createStatement().executeQuery("SELECT COUNT(DISTINCT ARRAY[a,b]) from  " + table);
-        assertTrue(rs.next());
-        assertEquals(2, rs.getInt(1));
-    }
-
-    @Test
-    public void testArrayConstructorWithMultipleRows2() throws Exception {
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        String ddl = "CREATE TABLE  " + table + "  (region_name VARCHAR PRIMARY KEY, a INTEGER, b INTEGER)";
-        conn.createStatement().execute(ddl);
-        conn.commit();
-        conn.close();
-        conn = DriverManager.getConnection(getUrl(), props);
-        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('a', 6,3)");
-        stmt.execute();
-        stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('b', 2,4)");
-        stmt.execute();
-        stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('c', 6,3)");
-        stmt.execute();
-        conn.commit();
-        conn.close();
-        conn = DriverManager.getConnection(getUrl(), props);
-        ResultSet rs;
-        rs = conn.createStatement().executeQuery("SELECT ARRAY[a,b] from  " + table + " ");
-        assertTrue(rs.next());
-        Array arr = conn.createArrayOf("INTEGER", new Object[]{6, 3});
-        assertEquals(arr, rs.getArray(1));
-        rs.next();
-        arr = conn.createArrayOf("INTEGER", new Object[]{2, 4});
-        assertEquals(arr, rs.getArray(1));
-        rs.next();
-        arr = conn.createArrayOf("INTEGER", new Object[]{6, 3});
-        assertEquals(arr, rs.getArray(1));
-        rs.next();
-    }
-
-    @Test
-    public void testArrayConstructorWithMultipleRows3() throws Exception {
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        String ddl = "CREATE TABLE  " + table + "  (region_name VARCHAR PRIMARY KEY, a VARCHAR, b VARCHAR)";
-        conn.createStatement().execute(ddl);
-        conn.commit();
-        conn.close();
-        conn = DriverManager.getConnection(getUrl(), props);
-        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('a', 'foo', 'abc')");
-        stmt.execute();
-        stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('b', 'abc', 'dfg')");
-        stmt.execute();
-        stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('c', 'foo', 'abc')");
-        stmt.execute();
-        conn.commit();
-        conn.close();
-        conn = DriverManager.getConnection(getUrl(), props);
-        ResultSet rs;
-        rs = conn.createStatement().executeQuery("SELECT ARRAY[a,b] from  " + table + " ");
-        assertTrue(rs.next());
-        Array arr = conn.createArrayOf("VARCHAR", new Object[]{"foo", "abc"});
-        assertEquals(arr, rs.getArray(1));
-        rs.next();
-        arr = conn.createArrayOf("VARCHAR", new Object[]{"abc", "dfg"});
-        assertEquals(arr, rs.getArray(1));
-        rs.next();
-        arr = conn.createArrayOf("VARCHAR", new Object[]{"foo", "abc"});
-        assertEquals(arr, rs.getArray(1));
-        rs.next();
-    }
-
-    @Test
-    public void testArrayConstructorWithMultipleRows4() throws Exception {
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        String ddl = "CREATE TABLE  " + table + "  (region_name VARCHAR PRIMARY KEY, a VARCHAR, b VARCHAR)";
-        conn.createStatement().execute(ddl);
-        conn.commit();
-        conn.close();
-        conn = DriverManager.getConnection(getUrl(), props);
-        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('a', 'foo', 'abc')");
-        stmt.execute();
-        stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('b', 'abc', 'dfg')");
-        stmt.execute();
-        stmt = conn.prepareStatement("UPSERT INTO  " + table + " (region_name, a, b) VALUES('c', 'foo', 'abc')");
-        stmt.execute();
-        conn.commit();
-        conn.close();
-        conn = DriverManager.getConnection(getUrl(), props);
-        ResultSet rs;
-        rs = conn.createStatement().executeQuery("SELECT COUNT(DISTINCT ARRAY[a,b]) from  " + table);
-        assertTrue(rs.next());
-        assertEquals(2, rs.getInt(1));
-    }
-
-    @Test
-    public void testArrayConstructorWithMultipleRows5() throws Exception {
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        String ddl = "CREATE TABLE   " + table + "  (region_name VARCHAR PRIMARY KEY, a VARCHAR, b VARCHAR)";
-        conn.createStatement().execute(ddl);
-        conn.commit();
-        conn.close();
-        conn = DriverManager.getConnection(getUrl(), props);
-        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO   " + table + " (region_name, a, b) VALUES('a', 'foo', 'abc')");
-        stmt.execute();
-        stmt = conn.prepareStatement("UPSERT INTO   " + table + " (region_name, a, b) VALUES('b', 'abc', 'dfg')");
-        stmt.execute();
-        stmt = conn.prepareStatement("UPSERT INTO   " + table + " (region_name, a, b) VALUES('c', 'foo', 'abc')");
-        stmt.execute();
-        conn.commit();
-        conn.close();
-        conn = DriverManager.getConnection(getUrl(), props);
-        ResultSet rs;
-        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(ARRAY[a,b], 'oo') from   " + table);
-        assertTrue(rs.next());
-        Array arr = conn.createArrayOf("VARCHAR", new Object[]{"foo", "abc", "oo"});
-        assertEquals(arr, rs.getArray(1));
-        rs.next();
-        arr = conn.createArrayOf("VARCHAR", new Object[]{"abc", "dfg", "oo"});
-        assertEquals(arr, rs.getArray(1));
-        rs.next();
-        arr = conn.createArrayOf("VARCHAR", new Object[]{"foo", "abc", "oo"});
-        assertEquals(arr, rs.getArray(1));
-        rs.next();
-    }
-    
-    @Test
-    public void testPKWithDescArray() throws Exception {
-        Connection conn;
-        PreparedStatement stmt;
-        ResultSet rs;
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        conn.createStatement()
-                .execute(
-                        "CREATE TABLE   " + table + "  ( a VARCHAR ARRAY PRIMARY KEY DESC)\n");
-        conn.close();
-        
-        conn = DriverManager.getConnection(getUrl(), props);
-        stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES(?)");
-        Array a1 = conn.createArrayOf("VARCHAR", new String[] { "a", "ba" });
-        stmt.setArray(1, a1);
-        stmt.execute();
-        Array a2 = conn.createArrayOf("VARCHAR", new String[] { "a", "c" });
-        stmt.setArray(1, a2);
-        stmt.execute();
-        conn.commit();
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        rs = conn.createStatement().executeQuery("SELECT a FROM   " + table + "  ORDER BY a DESC");
-        assertTrue(rs.next());
-        assertEquals(a2, rs.getArray(1));
-        assertTrue(rs.next());
-        assertEquals(a1, rs.getArray(1));
-        assertFalse(rs.next());
-        conn.close();
-        
-        conn = DriverManager.getConnection(getUrl(), props);
-        stmt = conn.prepareStatement("UPSERT INTO   " + table + "  VALUES(?)");
-        Array a3 = conn.createArrayOf("VARCHAR", new String[] { "a", "b" });
-        stmt.setArray(1, a3);
-        stmt.execute();
-        conn.commit();
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        rs = conn.createStatement().executeQuery("SELECT a FROM   " + table + "  ORDER BY a DESC");
-        assertTrue(rs.next());
-        assertEquals(a2, rs.getArray(1));
-        assertTrue(rs.next());
-        assertEquals(a1, rs.getArray(1));
-        assertTrue(rs.next());
-        assertEquals(a3, rs.getArray(1));
-        assertFalse(rs.next());
-        conn.close();
-    }
-
-    @Test
-    public void testComparisonOperatorsForDesc1()throws Exception{
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        String ddl = "create table   " + table + "  (k varchar array primary key desc)";
-        conn.createStatement().execute(ddl);
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        PreparedStatement stmt = conn.prepareStatement("upsert into   " + table + "  values (array['a', 'c'])");
-        stmt.execute();
-        conn.commit();
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        ResultSet rs;
-        stmt = conn.prepareStatement("select * from   " + table + "  where k >= array['a', 'b']");
-        rs = stmt.executeQuery();
-        assertTrue(rs.next());
-    }
-
-    @Test
-    public void testComparisonOperatorsForDesc2()throws Exception{
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        String ddl = "create table   " + table + "  (k varchar array primary key desc)";
-        conn.createStatement().execute(ddl);
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        PreparedStatement stmt = conn.prepareStatement("upsert into   " + table + "  values (array['a', 'c'])");
-        stmt.execute();
-        conn.commit();
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        ResultSet rs;
-        stmt = conn.prepareStatement("select * from   " + table + "  where k >= array['a', 'c']");
-        rs = stmt.executeQuery();
-        assertTrue(rs.next());
-    }
-
-    @Test
-    public void testComparisonOperatorsForDesc3()throws Exception{
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        String ddl = "create table   " + table + "  (k varchar array primary key desc)";
-        conn.createStatement().execute(ddl);
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        PreparedStatement stmt = conn.prepareStatement("upsert into   " + table + "  values (array['a', 'c'])");
-        stmt.execute();
-        conn.commit();
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        ResultSet rs;
-        stmt = conn.prepareStatement("select * from   " + table + "  where k > array['a', 'b']");
-        rs = stmt.executeQuery();
-        assertTrue(rs.next());
-    }
-
-    @Test
-    public void testComparisonOperatorsForDesc4()throws Exception{
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        String ddl = "create table   " + table + "  (k varchar array primary key desc)";
-        conn.createStatement().execute(ddl);
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        PreparedStatement stmt = conn.prepareStatement("upsert into   " + table + "  values (array['a', 'b'])");
-        stmt.execute();
-        conn.commit();
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        ResultSet rs;
-        stmt = conn.prepareStatement("select * from   " + table + "  where k <= array['a', 'c']");
-        rs = stmt.executeQuery();
-        assertTrue(rs.next());
-    }
-
-    @Test
-    public void testComparisonOperatorsForDesc5()throws Exception{
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        String ddl = "create table   " + table + "  (k varchar array primary key desc)";
-        conn.createStatement().execute(ddl);
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        PreparedStatement stmt = conn.prepareStatement("upsert into   " + table + "  values (array['a', 'b'])");
-        stmt.execute();
-        conn.commit();
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        ResultSet rs;
-        stmt = conn.prepareStatement("select * from   " + table + "  where k <= array['a', 'b']");
-        rs = stmt.executeQuery();
-        assertTrue(rs.next());
-    }
-
-    @Test
-    public void testComparisonOperatorsForDesc6()throws Exception{
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        String ddl = "create table   " + table + "  (k varchar array primary key desc)";
-        conn.createStatement().execute(ddl);
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        PreparedStatement stmt = conn.prepareStatement("upsert into   " + table + "  values (array['a', 'b'])");
-        stmt.execute();
-        conn.commit();
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        ResultSet rs;
-        stmt = conn.prepareStatement("select * from   " + table + "  where k < array['a', 'c']");
-        rs = stmt.executeQuery();
-        assertTrue(rs.next());
-    }
-
-    @Test
-    public void testComparisonOperatorsForDesc7()throws Exception{
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName();
-        String ddl = "create table   " + table + "  (k integer array primary key desc)";
-        conn.createStatement().execute(ddl);
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        PreparedStatement stmt = conn.prepareStatement("upsert into   " + table + "  values (array[1, 2])");
-        stmt.execute();
-        conn.commit();
-        conn.close();
-
-        conn = DriverManager.getConnection(getUrl(), props);
-        ResultSet rs;
-        stmt = conn.prepareStatement("select * from   " + table + "  where k < array[1, 4]");
-        rs = stmt.executeQuery();
-        assertTrue(rs.next());
-    }
-
-    @Test
-    public void testComparisonOperatorsForDesc8()throws Exception{
-
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String table = generateUniqueName

<TRUNCATED>

[10/25] phoenix git commit: PHOENIX-4246 Breakup join related tests into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/ee20a8c9/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortMergeJoinIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortMergeJoinIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortMergeJoinIT.java
deleted file mode 100644
index 2ecc404..0000000
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortMergeJoinIT.java
+++ /dev/null
@@ -1,2563 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.phoenix.end2end;
-
-import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.sql.Connection;
-import java.sql.Date;
-import java.sql.DriverManager;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.sql.Timestamp;
-import java.util.Collection;
-import java.util.List;
-import java.util.Properties;
-
-import org.apache.phoenix.exception.SQLExceptionCode;
-import org.apache.phoenix.query.QueryServices;
-import org.apache.phoenix.util.PropertiesUtil;
-import org.apache.phoenix.util.QueryUtil;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import com.google.common.collect.Lists;
-
-@RunWith(Parameterized.class)
-public class SortMergeJoinIT extends BaseJoinIT {
-    
-    @Parameters
-    public static Collection<Object> data() {
-        List<Object> testCases = Lists.newArrayList();
-        testCases.add(new String[][] {
-                {}, {
-                "SORT-MERGE-JOIN (LEFT) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "AND\n" +
-                "    SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    AND (SKIP MERGE)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER FILTER BY QUANTITY < 5000\n" +
-                "            SERVER SORTED BY [\"O.item_id\"]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    CLIENT SORTED BY [\"I.supplier_id\"]",
-                
-                "SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "AND\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "        SERVER SORTED BY [\"O.item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "CLIENT 4 ROW LIMIT",
-                
-                "SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "AND\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY"
-                }});
-        testCases.add(new String[][] {
-                {
-                "CREATE INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
-                "CREATE INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
-                "CREATE INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
-                }, {
-                "SORT-MERGE-JOIN (LEFT) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_supplier\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "        SERVER SORTED BY [\"S.:supplier_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "AND\n" +
-                "    SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "            SERVER SORTED BY [\"I.:item_id\"]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    AND (SKIP MERGE)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER FILTER BY QUANTITY < 5000\n" +
-                "            SERVER SORTED BY [\"O.item_id\"]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    CLIENT SORTED BY [\"I.0:supplier_id\"]",
-                
-                "SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        SERVER SORTED BY [\"I.:item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "AND\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "        SERVER SORTED BY [\"O.item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "CLIENT 4 ROW LIMIT",
-                
-                "SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER Join.idx_item\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        SERVER SORTED BY [\"I1.:item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "AND\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER Join.idx_item\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        SERVER SORTED BY [\"I2.:item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "CLIENT SORTED BY [\"I1.:item_id\"]"
-                }});
-        testCases.add(new String[][] {
-                {
-                "CREATE LOCAL INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
-                "CREATE LOCAL INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
-                "CREATE LOCAL INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
-                }, {
-                "SORT-MERGE-JOIN (LEFT) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +JOIN_SUPPLIER_TABLE_FULL_NAME + " [1]\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "        SERVER SORTED BY [\"S.:supplier_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "AND\n" +
-                "    SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "            SERVER SORTED BY [\"I.:item_id\"]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    AND (SKIP MERGE)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER FILTER BY QUANTITY < 5000\n" +
-                "            SERVER SORTED BY [\"O.item_id\"]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    CLIENT SORTED BY [\"I.0:supplier_id\"]",
-                
-                "SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        SERVER SORTED BY [\"I.:item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "AND\n" +
-                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "        SERVER SORTED BY [\"O.item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "CLIENT 4 ROW LIMIT",
-                
-                "SORT-MERGE-JOIN (INNER) TABLES\n" +
-                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        SERVER SORTED BY [\"I1.:item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "AND\n" +
-                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "        SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        SERVER SORTED BY [\"I2.:item_id\"]\n" +
-                "    CLIENT MERGE SORT\n" +
-                "CLIENT SORTED BY [\"I1.:item_id\"]"
-                }});
-        return testCases;
-    }
-    
-
-    public SortMergeJoinIT(String[] indexDDL, String[] plans) {
-        super(indexDDL, plans);
-    }
-    
-    @Test
-    public void testDefaultJoin() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "T2");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "T3");
-            assertEquals(rs.getString(3), "0000000002");
-            assertEquals(rs.getString(4), "S2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertEquals(rs.getString(3), "0000000002");
-            assertEquals(rs.getString(4), "S2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-            assertEquals(rs.getString(3), "0000000005");
-            assertEquals(rs.getString(4), "S5");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "0000000006");
-            assertEquals(rs.getString(4), "S6");
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testInnerJoin() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name, next value for " + seqName + " FROM " + tableName1 + " item INNER JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertEquals(1, rs.getInt(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "T2");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertEquals(2, rs.getInt(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "T3");
-            assertEquals(rs.getString(3), "0000000002");
-            assertEquals(rs.getString(4), "S2");
-            assertEquals(3, rs.getInt(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertEquals(rs.getString(3), "0000000002");
-            assertEquals(rs.getString(4), "S2");
-            assertEquals(4, rs.getInt(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-            assertEquals(rs.getString(3), "0000000005");
-            assertEquals(rs.getString(4), "S5");
-            assertEquals(5, rs.getInt(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "0000000006");
-            assertEquals(rs.getString(4), "S6");
-            assertEquals(6, rs.getInt(5));
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-            
-    @Test
-    public void testLeftJoin() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String query[] = new String[3];
-        query[0] = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name, next value for " + seqName + " FROM " + tableName1 + " item LEFT JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
-        query[1] = "SELECT /*+ USE_SORT_MERGE_JOIN*/ " + tableName1 + ".\"item_id\", " + tableName1 + ".name, " + tableName2 + ".\"supplier_id\", " + tableName2 + ".name, next value for " + seqName + " FROM " + tableName1 + " LEFT JOIN " + tableName2 + " ON " + tableName1 + ".\"supplier_id\" = " + tableName2 + ".\"supplier_id\" ORDER BY \"item_id\"";
-        query[2] = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", " + tableName1 + ".name, supp.\"supplier_id\", " + tableName2 + ".name, next value for " + seqName + " FROM " + tableName1 + " item LEFT JOIN " + tableName2 + " supp ON " + tableName1 + ".\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
-        try {
-            for (int i = 0; i < query.length; i++) {
-                PreparedStatement statement = conn.prepareStatement(query[i]);
-                ResultSet rs = statement.executeQuery();
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "0000000001");
-                assertEquals(rs.getString(2), "T1");
-                assertEquals(rs.getString(3), "0000000001");
-                assertEquals(rs.getString(4), "S1");
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "0000000002");
-                assertEquals(rs.getString(2), "T2");
-                assertEquals(rs.getString(3), "0000000001");
-                assertEquals(rs.getString(4), "S1");
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "0000000003");
-                assertEquals(rs.getString(2), "T3");
-                assertEquals(rs.getString(3), "0000000002");
-                assertEquals(rs.getString(4), "S2");
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "0000000004");
-                assertEquals(rs.getString(2), "T4");
-                assertEquals(rs.getString(3), "0000000002");
-                assertEquals(rs.getString(4), "S2");
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "0000000005");
-                assertEquals(rs.getString(2), "T5");
-                assertEquals(rs.getString(3), "0000000005");
-                assertEquals(rs.getString(4), "S5");
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "0000000006");
-                assertEquals(rs.getString(2), "T6");
-                assertEquals(rs.getString(3), "0000000006");
-                assertEquals(rs.getString(4), "S6");
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "invalid001");
-                assertEquals(rs.getString(2), "INVALID-1");
-                assertNull(rs.getString(3));
-                assertNull(rs.getString(4));
-
-                assertFalse(rs.next());
-                rs.close();
-                statement.close();
-            }
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testRightJoin() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName2 + " supp RIGHT JOIN " + tableName1 + " item ON item.\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "T2");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "T3");
-            assertEquals(rs.getString(3), "0000000002");
-            assertEquals(rs.getString(4), "S2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertEquals(rs.getString(3), "0000000002");
-            assertEquals(rs.getString(4), "S2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-            assertEquals(rs.getString(3), "0000000005");
-            assertEquals(rs.getString(4), "S5");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "0000000006");
-            assertEquals(rs.getString(4), "S6");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "invalid001");
-            assertEquals(rs.getString(2), "INVALID-1");
-            assertNull(rs.getString(3));
-            assertNull(rs.getString(4));
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testInnerJoinWithPreFilters() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String query1 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item INNER JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" AND supp.\"supplier_id\" BETWEEN '0000000001' AND '0000000005' ORDER BY \"item_id\"";
-        String query2 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item INNER JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" AND (supp.\"supplier_id\" = '0000000001' OR supp.\"supplier_id\" = '0000000005') ORDER BY \"item_id\"";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query1);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "T2");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "T3");
-            assertEquals(rs.getString(3), "0000000002");
-            assertEquals(rs.getString(4), "S2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertEquals(rs.getString(3), "0000000002");
-            assertEquals(rs.getString(4), "S2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-            assertEquals(rs.getString(3), "0000000005");
-            assertEquals(rs.getString(4), "S5");
-
-            assertFalse(rs.next());
-            
-            
-            statement = conn.prepareStatement(query2);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "T2");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-            assertEquals(rs.getString(3), "0000000005");
-            assertEquals(rs.getString(4), "S5");
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testLeftJoinWithPreFilters() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item LEFT JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" AND (supp.\"supplier_id\" = '0000000001' OR supp.\"supplier_id\" = '0000000005') ORDER BY \"item_id\"";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "T2");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "T3");
-            assertNull(rs.getString(3));
-            assertNull(rs.getString(4));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertNull(rs.getString(3));
-            assertNull(rs.getString(4));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-            assertEquals(rs.getString(3), "0000000005");
-            assertEquals(rs.getString(4), "S5");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "T6");
-            assertNull(rs.getString(3));
-            assertNull(rs.getString(4));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "invalid001");
-            assertEquals(rs.getString(2), "INVALID-1");
-            assertNull(rs.getString(3));
-            assertNull(rs.getString(4));
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testJoinWithPostFilters() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String query1 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName2 + " supp RIGHT JOIN " + tableName1 + " item ON item.\"supplier_id\" = supp.\"supplier_id\" WHERE supp.\"supplier_id\" BETWEEN '0000000001' AND '0000000005' ORDER BY \"item_id\"";
-        String query2 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item LEFT JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" WHERE supp.\"supplier_id\" = '0000000001' OR supp.\"supplier_id\" = '0000000005' ORDER BY \"item_id\"";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query1);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "T2");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "T3");
-            assertEquals(rs.getString(3), "0000000002");
-            assertEquals(rs.getString(4), "S2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertEquals(rs.getString(3), "0000000002");
-            assertEquals(rs.getString(4), "S2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-            assertEquals(rs.getString(3), "0000000005");
-            assertEquals(rs.getString(4), "S5");
-
-            assertFalse(rs.next());
-            
-            
-            statement = conn.prepareStatement(query2);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "T2");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-            assertEquals(rs.getString(3), "0000000005");
-            assertEquals(rs.getString(4), "S5");
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testStarJoin() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName3 = getTableName(conn, JOIN_CUSTOMER_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        String[] query = new String[5];
-        query[0] = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", c.name, i.name iname, quantity, o.\"DATE\" FROM " + tableName4 + " o JOIN "
-            + tableName3 + " c ON o.\"customer_id\" = c.\"customer_id\" JOIN " 
-            + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" ORDER BY \"order_id\"";
-        query[1] = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", c.name, i.name iname, quantity, o.\"DATE\" FROM " + tableName4 + " o, "
-                + tableName3 + " c, " 
-                + tableName1 + " i WHERE o.\"item_id\" = i.\"item_id\" AND o.\"customer_id\" = c.\"customer_id\" ORDER BY \"order_id\"";
-        query[2] = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", c.name, i.name iname, quantity, o.\"DATE\" FROM " + tableName4 + " o JOIN "
-                + tableName3 + " c ON o.\"customer_id\" = c.\"customer_id\" JOIN " 
-                + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" ORDER BY \"order_id\"";
-        query[3] = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", c.name, i.name iname, quantity, o.\"DATE\" FROM (" + tableName4 + " o, "
-                + tableName3 + " c), " 
-                + tableName1 + " i WHERE o.\"item_id\" = i.\"item_id\" AND o.\"customer_id\" = c.\"customer_id\" ORDER BY \"order_id\"";
-        query[4] = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", c.name, i.name iname, quantity, o.\"DATE\" FROM " + tableName4 + " o, ("
-                + tableName3 + " c, " 
-                + tableName1 + " i) WHERE o.\"item_id\" = i.\"item_id\" AND o.\"customer_id\" = c.\"customer_id\" ORDER BY \"order_id\"";
-        try {
-            for (int i = 0; i < query.length; i++) {
-                PreparedStatement statement = conn.prepareStatement(query[i]);
-                ResultSet rs = statement.executeQuery();
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "000000000000001");
-                assertEquals(rs.getString("\"order_id\""), "000000000000001");
-                assertEquals(rs.getString(2), "C4");
-                assertEquals(rs.getString("C.name"), "C4");
-                assertEquals(rs.getString(3), "T1");
-                assertEquals(rs.getString("iName"), "T1");
-                assertEquals(rs.getInt(4), 1000);
-                assertEquals(rs.getInt("Quantity"), 1000);
-                assertNotNull(rs.getDate(5));
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "000000000000002");
-                assertEquals(rs.getString(2), "C3");
-                assertEquals(rs.getString(3), "T6");
-                assertEquals(rs.getInt(4), 2000);
-                assertNotNull(rs.getDate(5));
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "000000000000003");
-                assertEquals(rs.getString(2), "C2");
-                assertEquals(rs.getString(3), "T2");
-                assertEquals(rs.getInt(4), 3000);
-                assertNotNull(rs.getDate(5));
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "000000000000004");
-                assertEquals(rs.getString(2), "C4");
-                assertEquals(rs.getString(3), "T6");
-                assertEquals(rs.getInt(4), 4000);
-                assertNotNull(rs.getDate(5));
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "000000000000005");
-                assertEquals(rs.getString(2), "C5");
-                assertEquals(rs.getString(3), "T3");
-                assertEquals(rs.getInt(4), 5000);
-                assertNotNull(rs.getDate(5));
-
-                assertFalse(rs.next());
-            }
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testLeftJoinWithAggregation() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        String query1 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ i.name, sum(quantity) FROM " + tableName4 + " o LEFT JOIN " 
-            + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" GROUP BY i.name ORDER BY i.name";
-        String query2 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ i.\"item_id\" iid, sum(quantity) q FROM " + tableName4 + " o LEFT JOIN " 
-                + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" GROUP BY i.\"item_id\" ORDER BY q DESC";
-        String query3 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ i.\"item_id\" iid, sum(quantity) q FROM " + tableName1 + " i LEFT JOIN " 
-                + tableName4 + " o ON o.\"item_id\" = i.\"item_id\" GROUP BY i.\"item_id\" ORDER BY q DESC NULLS LAST, iid";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query1);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "T1");
-            assertEquals(rs.getInt(2), 1000);
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "T2");
-            assertEquals(rs.getInt(2), 3000);
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "T3");
-            assertEquals(rs.getInt(2), 5000);
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "T6");
-            assertEquals(rs.getInt(2), 6000);
-
-            assertFalse(rs.next());
-            
-            statement = conn.prepareStatement(query2);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString("iid"), "0000000006");
-            assertEquals(rs.getInt("q"), 6000);
-            assertTrue (rs.next());
-            assertEquals(rs.getString("iid"), "0000000003");
-            assertEquals(rs.getInt("q"), 5000);
-            assertTrue (rs.next());
-            assertEquals(rs.getString("iid"), "0000000002");
-            assertEquals(rs.getInt("q"), 3000);
-            assertTrue (rs.next());
-            assertEquals(rs.getString("iid"), "0000000001");
-            assertEquals(rs.getInt("q"), 1000);
-
-            assertFalse(rs.next());
-            
-            statement = conn.prepareStatement(query3);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString("iid"), "0000000006");
-            assertEquals(rs.getInt("q"), 6000);
-            assertTrue (rs.next());
-            assertEquals(rs.getString("iid"), "0000000003");
-            assertEquals(rs.getInt("q"), 5000);
-            assertTrue (rs.next());
-            assertEquals(rs.getString("iid"), "0000000002");
-            assertEquals(rs.getInt("q"), 3000);
-            assertTrue (rs.next());
-            assertEquals(rs.getString("iid"), "0000000001");
-            assertEquals(rs.getInt("q"), 1000);
-            assertTrue (rs.next());
-            assertEquals(rs.getString("iid"), "0000000004");
-            assertEquals(rs.getInt("q"), 0);
-            assertTrue (rs.next());
-            assertEquals(rs.getString("iid"), "0000000005");
-            assertEquals(rs.getInt("q"), 0);
-            assertTrue (rs.next());
-            assertEquals(rs.getString("iid"), "invalid001");
-            assertEquals(rs.getInt("q"), 0);
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testRightJoinWithAggregation() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        String query1 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ i.name, sum(quantity) FROM " + tableName4 + " o RIGHT JOIN " 
-            + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" GROUP BY i.name ORDER BY i.name";
-        String query2 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ i.\"item_id\" iid, sum(quantity) q FROM " + tableName4 + " o RIGHT JOIN " 
-            + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" GROUP BY i.\"item_id\" ORDER BY q DESC NULLS LAST, iid";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query1);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "INVALID-1");
-            assertEquals(rs.getInt(2), 0);
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "T1");
-            assertEquals(rs.getInt(2), 1000);
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "T2");
-            assertEquals(rs.getInt(2), 3000);
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "T3");
-            assertEquals(rs.getInt(2), 5000);
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "T4");
-            assertEquals(rs.getInt(2), 0);
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "T5");
-            assertEquals(rs.getInt(2), 0);
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "T6");
-            assertEquals(rs.getInt(2), 6000);
-
-            assertFalse(rs.next());
-            
-            statement = conn.prepareStatement(query2);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString("iid"), "0000000006");
-            assertEquals(rs.getInt("q"), 6000);
-            assertTrue (rs.next());
-            assertEquals(rs.getString("iid"), "0000000003");
-            assertEquals(rs.getInt("q"), 5000);
-            assertTrue (rs.next());
-            assertEquals(rs.getString("iid"), "0000000002");
-            assertEquals(rs.getInt("q"), 3000);
-            assertTrue (rs.next());
-            assertEquals(rs.getString("iid"), "0000000001");
-            assertEquals(rs.getInt("q"), 1000);
-            assertTrue (rs.next());
-            assertEquals(rs.getString("iid"), "0000000004");
-            assertEquals(rs.getInt("q"), 0);
-            assertTrue (rs.next());
-            assertEquals(rs.getString("iid"), "0000000005");
-            assertEquals(rs.getInt("q"), 0);
-            assertTrue (rs.next());
-            assertEquals(rs.getString("iid"), "invalid001");
-            assertEquals(rs.getInt("q"), 0);
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testLeftRightJoin() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        String query1 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + tableName4 + " o LEFT JOIN "
-                + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" RIGHT JOIN "
-                + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\" ORDER BY \"order_id\", s.\"supplier_id\" DESC";
-        String query2 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + tableName4 + " o LEFT JOIN "
-                + "(" + tableName1 + " i RIGHT JOIN " + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\")" 
-                + " ON o.\"item_id\" = i.\"item_id\" ORDER BY \"order_id\", s.\"supplier_id\" DESC";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query1);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertNull(rs.getString(1));
-            assertNull(rs.getString(2));
-            assertEquals(rs.getString(3), "S5");
-            assertEquals(rs.getInt(4), 0);
-            assertNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertNull(rs.getString(1));
-            assertNull(rs.getString(2));
-            assertEquals(rs.getString(3), "S4");
-            assertEquals(rs.getInt(4), 0);
-            assertNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertNull(rs.getString(1));
-            assertNull(rs.getString(2));
-            assertEquals(rs.getString(3), "S3");
-            assertEquals(rs.getInt(4), 0);
-            assertNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "S1");
-            assertEquals(rs.getInt(4), 1000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000002");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "S6");
-            assertEquals(rs.getInt(4), 2000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000003");
-            assertEquals(rs.getString(2), "T2");
-            assertEquals(rs.getString(3), "S1");
-            assertEquals(rs.getInt(4), 3000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000004");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "S6");
-            assertEquals(rs.getInt(4), 4000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000005");
-            assertEquals(rs.getString(2), "T3");
-            assertEquals(rs.getString(3), "S2");
-            assertEquals(rs.getInt(4), 5000);
-            assertNotNull(rs.getDate(5));
-
-            assertFalse(rs.next());
-            
-            statement = conn.prepareStatement(query2);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "S1");
-            assertEquals(rs.getInt(4), 1000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000002");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "S6");
-            assertEquals(rs.getInt(4), 2000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000003");
-            assertEquals(rs.getString(2), "T2");
-            assertEquals(rs.getString(3), "S1");
-            assertEquals(rs.getInt(4), 3000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000004");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "S6");
-            assertEquals(rs.getInt(4), 4000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000005");
-            assertEquals(rs.getString(2), "T3");
-            assertEquals(rs.getString(3), "S2");
-            assertEquals(rs.getInt(4), 5000);
-            assertNotNull(rs.getDate(5));
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testRightLeftJoin() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        String query1 = "SELECT \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + tableName1 + " i RIGHT JOIN "
-                + tableName4 + " o ON o.\"item_id\" = i.\"item_id\" LEFT JOIN "
-                + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\" ORDER BY \"order_id\"";
-        String query2 = "SELECT \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + tableName4 + " o RIGHT JOIN "
-                + "(" + tableName1 + " i LEFT JOIN " + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\")" 
-                + " ON o.\"item_id\" = i.\"item_id\" ORDER BY \"order_id\", s.\"supplier_id\" DESC";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query1);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "S1");
-            assertEquals(rs.getInt(4), 1000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000002");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "S6");
-            assertEquals(rs.getInt(4), 2000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000003");
-            assertEquals(rs.getString(2), "T2");
-            assertEquals(rs.getString(3), "S1");
-            assertEquals(rs.getInt(4), 3000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000004");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "S6");
-            assertEquals(rs.getInt(4), 4000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000005");
-            assertEquals(rs.getString(2), "T3");
-            assertEquals(rs.getString(3), "S2");
-            assertEquals(rs.getInt(4), 5000);
-            assertNotNull(rs.getDate(5));
-
-            assertFalse(rs.next());
-            
-            statement = conn.prepareStatement(query2);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertNull(rs.getString(1));
-            assertEquals(rs.getString(2), "INVALID-1");
-            assertNull(rs.getString(3));
-            assertEquals(rs.getInt(4), 0);
-            assertNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertNull(rs.getString(1));
-            assertEquals(rs.getString(2), "T5");
-            assertEquals(rs.getString(3), "S5");
-            assertEquals(rs.getInt(4), 0);
-            assertNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertNull(rs.getString(1));
-            assertEquals(rs.getString(2), "T4");
-            assertEquals(rs.getString(3), "S2");
-            assertEquals(rs.getInt(4), 0);
-            assertNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "S1");
-            assertEquals(rs.getInt(4), 1000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000002");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "S6");
-            assertEquals(rs.getInt(4), 2000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000003");
-            assertEquals(rs.getString(2), "T2");
-            assertEquals(rs.getString(3), "S1");
-            assertEquals(rs.getInt(4), 3000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000004");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "S6");
-            assertEquals(rs.getInt(4), 4000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000005");
-            assertEquals(rs.getString(2), "T3");
-            assertEquals(rs.getString(3), "S2");
-            assertEquals(rs.getInt(4), 5000);
-            assertNotNull(rs.getDate(5));
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testMultiLeftJoin() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        String[] queries = {
-                "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + tableName4 + " o LEFT JOIN "
-                        + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" LEFT JOIN "
-                        + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\" ORDER BY \"order_id\"",
-                "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + tableName4 + " o LEFT JOIN "
-                        + "(" + tableName1 + " i LEFT JOIN " + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\") " 
-                        + "ON o.\"item_id\" = i.\"item_id\" ORDER BY \"order_id\""};
-        try {
-            for (String query : queries) {
-                PreparedStatement statement = conn.prepareStatement(query);
-                ResultSet rs = statement.executeQuery();
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "000000000000001");
-                assertEquals(rs.getString(2), "T1");
-                assertEquals(rs.getString(3), "S1");
-                assertEquals(rs.getInt(4), 1000);
-                assertNotNull(rs.getDate(5));
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "000000000000002");
-                assertEquals(rs.getString(2), "T6");
-                assertEquals(rs.getString(3), "S6");
-                assertEquals(rs.getInt(4), 2000);
-                assertNotNull(rs.getDate(5));
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "000000000000003");
-                assertEquals(rs.getString(2), "T2");
-                assertEquals(rs.getString(3), "S1");
-                assertEquals(rs.getInt(4), 3000);
-                assertNotNull(rs.getDate(5));
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "000000000000004");
-                assertEquals(rs.getString(2), "T6");
-                assertEquals(rs.getString(3), "S6");
-                assertEquals(rs.getInt(4), 4000);
-                assertNotNull(rs.getDate(5));
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "000000000000005");
-                assertEquals(rs.getString(2), "T3");
-                assertEquals(rs.getString(3), "S2");
-                assertEquals(rs.getInt(4), 5000);
-                assertNotNull(rs.getDate(5));
-
-                assertFalse(rs.next());
-            }
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testMultiRightJoin() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + tableName4 + " o RIGHT JOIN "
-            + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" RIGHT JOIN "
-            + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\" ORDER BY \"order_id\", s.\"supplier_id\" DESC";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertNull(rs.getString(1));
-            assertEquals(rs.getString(2), "T5");
-            assertEquals(rs.getString(3), "S5");
-            assertEquals(rs.getInt(4), 0);
-            assertNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertNull(rs.getString(1));
-            assertNull(rs.getString(2));
-            assertEquals(rs.getString(3), "S4");
-            assertEquals(rs.getInt(4), 0);
-            assertNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertNull(rs.getString(1));
-            assertNull(rs.getString(2));
-            assertEquals(rs.getString(3), "S3");
-            assertEquals(rs.getInt(4), 0);
-            assertNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertNull(rs.getString(1));
-            assertEquals(rs.getString(2), "T4");
-            assertEquals(rs.getString(3), "S2");
-            assertEquals(rs.getInt(4), 0);
-            assertNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "S1");
-            assertEquals(rs.getInt(4), 1000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000002");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "S6");
-            assertEquals(rs.getInt(4), 2000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000003");
-            assertEquals(rs.getString(2), "T2");
-            assertEquals(rs.getString(3), "S1");
-            assertEquals(rs.getInt(4), 3000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000004");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "S6");
-            assertEquals(rs.getInt(4), 4000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000005");
-            assertEquals(rs.getString(2), "T3");
-            assertEquals(rs.getString(3), "S2");
-            assertEquals(rs.getInt(4), 5000);
-            assertNotNull(rs.getDate(5));
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    // Basically a copy of testMultiRightJoin, but with a very small result scan chunk size
-    // to test that repeated row keys within a single chunk are handled properly
-    @Test
-    public void testMultiRightJoin_SmallChunkSize() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        props.setProperty(QueryServices.SCAN_RESULT_CHUNK_SIZE, "1");
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + tableName4 + " o RIGHT JOIN "
-                + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" RIGHT JOIN "
-                + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\" ORDER BY \"order_id\", s.\"supplier_id\" DESC";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertNull(rs.getString(1));
-            assertEquals(rs.getString(2), "T5");
-            assertEquals(rs.getString(3), "S5");
-            assertEquals(rs.getInt(4), 0);
-            assertNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertNull(rs.getString(1));
-            assertNull(rs.getString(2));
-            assertEquals(rs.getString(3), "S4");
-            assertEquals(rs.getInt(4), 0);
-            assertNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertNull(rs.getString(1));
-            assertNull(rs.getString(2));
-            assertEquals(rs.getString(3), "S3");
-            assertEquals(rs.getInt(4), 0);
-            assertNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertNull(rs.getString(1));
-            assertEquals(rs.getString(2), "T4");
-            assertEquals(rs.getString(3), "S2");
-            assertEquals(rs.getInt(4), 0);
-            assertNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "S1");
-            assertEquals(rs.getInt(4), 1000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000002");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "S6");
-            assertEquals(rs.getInt(4), 2000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000003");
-            assertEquals(rs.getString(2), "T2");
-            assertEquals(rs.getString(3), "S1");
-            assertEquals(rs.getInt(4), 3000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000004");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "S6");
-            assertEquals(rs.getInt(4), 4000);
-            assertNotNull(rs.getDate(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000005");
-            assertEquals(rs.getString(2), "T3");
-            assertEquals(rs.getString(3), "S2");
-            assertEquals(rs.getInt(4), 5000);
-            assertNotNull(rs.getDate(5));
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testJoinWithWildcard() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ * FROM " + tableName1 + " LEFT JOIN " + tableName2 + " supp ON " + tableName1 + ".\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "0000000001");
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "T1");
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 100);
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 5);
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 10);
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000001");
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Item T1");
-            assertEquals(rs.getString("SUPP.supplier_id"), "0000000001");
-            assertEquals(rs.getString("supp.name"), "S1");
-            assertEquals(rs.getString("supp.phone"), "888-888-1111");
-            assertEquals(rs.getString("supp.address"), "101 YYY Street");
-            assertEquals(rs.getString("supp.loc_id"), "10001");            
-            assertTrue (rs.next());
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "0000000002");
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "T2");
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 200);
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 5);
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 8);
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000001");
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Item T2");
-            assertEquals(rs.getString("SUPP.supplier_id"), "0000000001");
-            assertEquals(rs.getString("supp.name"), "S1");
-            assertEquals(rs.getString("supp.phone"), "888-888-1111");
-            assertEquals(rs.getString("supp.address"), "101 YYY Street");
-            assertEquals(rs.getString("supp.loc_id"), "10001");            
-            assertTrue (rs.next());
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "0000000003");
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "T3");
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 300);
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 8);
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 12);
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000002");
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Item T3");
-            assertEquals(rs.getString("SUPP.supplier_id"), "0000000002");
-            assertEquals(rs.getString("supp.name"), "S2");
-            assertEquals(rs.getString("supp.phone"), "888-888-2222");
-            assertEquals(rs.getString("supp.address"), "202 YYY Street");
-            assertEquals(rs.getString("supp.loc_id"), "10002");            
-            assertTrue (rs.next());
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "0000000004");
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "T4");
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 400);
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 6);
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 10);
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000002");
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Item T4");
-            assertEquals(rs.getString("SUPP.supplier_id"), "0000000002");
-            assertEquals(rs.getString("supp.name"), "S2");
-            assertEquals(rs.getString("supp.phone"), "888-888-2222");
-            assertEquals(rs.getString("supp.address"), "202 YYY Street");
-            assertEquals(rs.getString("supp.loc_id"), "10002");            
-            assertTrue (rs.next());
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "0000000005");
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "T5");
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 500);
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 8);
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 15);
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000005");
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Item T5");
-            assertEquals(rs.getString("SUPP.supplier_id"), "0000000005");
-            assertEquals(rs.getString("supp.name"), "S5");
-            assertEquals(rs.getString("supp.phone"), "888-888-5555");
-            assertEquals(rs.getString("supp.address"), "505 YYY Street");
-            assertEquals(rs.getString("supp.loc_id"), "10005");            
-            assertTrue (rs.next());
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "0000000006");
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "T6");
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 600);
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 8);
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 15);
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000006");
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Item T6");
-            assertEquals(rs.getString("SUPP.supplier_id"), "0000000006");
-            assertEquals(rs.getString("supp.name"), "S6");
-            assertEquals(rs.getString("supp.phone"), "888-888-6666");
-            assertEquals(rs.getString("supp.address"), "606 YYY Street");
-            assertEquals(rs.getString("supp.loc_id"), "10006");            
-            assertTrue (rs.next());
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "invalid001");
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "INVALID-1");
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 0);
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 0);
-            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 0);
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000000");
-            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Invalid item for join test");
-            assertNull(rs.getString("SUPP.supplier_id"));
-            assertNull(rs.getString("supp.name"));
-            assertNull(rs.getString("supp.phone"));
-            assertNull(rs.getString("supp.address"));
-            assertNull(rs.getString("supp.loc_id"));
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testJoinWithTableWildcard() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ s.*, "+ tableName1 + ".*, \"order_id\" FROM " + tableName4 + " o RIGHT JOIN " 
-                + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" RIGHT JOIN "
-                + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\" ORDER BY \"order_id\", s.\"supplier_id\" DESC";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            ResultSetMetaData md = rs.getMetaData();
-            assertEquals(md.getColumnCount(), 13);
-            
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "S5");
-            assertEquals(rs.getString(3), "888-888-5555");
-            assertEquals(rs.getString(4), "505 YYY Street");
-            assertEquals(rs.getString(5), "10005");
-            assertEquals(rs.getString(6), "0000000005");
-            assertEquals(rs.getString(7), "T5");
-            assertEquals(rs.getInt(8), 500);
-            assertEquals(rs.getInt(9), 8);
-            assertEquals(rs.getInt(10), 15);
-            assertEquals(rs.getString(11), "0000000005");
-            assertEquals(rs.getString(12), "Item T5");
-            assertNull(rs.getString(13));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "S4");
-            assertEquals(rs.getString(3), "888-888-4444");
-            assertEquals(rs.getString(4), "404 YYY Street");
-            assertNull(rs.getString(5));
-            assertNull(rs.getString(6));
-            assertNull(rs.getString(7));
-            assertEquals(rs.getInt(8), 0);
-            assertEquals(rs.getInt(9), 0);
-            assertEquals(rs.getInt(10), 0);
-            assertNull(rs.getString(11));
-            assertNull(rs.getString(12));            
-            assertNull(rs.getString(13));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "S3");
-            assertEquals(rs.getString(3), "888-888-3333");
-            assertEquals(rs.getString(4), "303 YYY Street");
-            assertNull(rs.getString(5));
-            assertNull(rs.getString(6));
-            assertNull(rs.getString(7));
-            assertEquals(rs.getInt(8), 0);
-            assertEquals(rs.getInt(9), 0);
-            assertEquals(rs.getInt(10), 0);
-            assertNull(rs.getString(11));
-            assertNull(rs.getString(12));            
-            assertNull(rs.getString(13));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "S2");
-            assertEquals(rs.getString(3), "888-888-2222");
-            assertEquals(rs.getString(4), "202 YYY Street");
-            assertEquals(rs.getString(5), "10002");
-            assertEquals(rs.getString(6), "0000000004");
-            assertEquals(rs.getString(7), "T4");
-            assertEquals(rs.getInt(8), 400);
-            assertEquals(rs.getInt(9), 6);
-            assertEquals(rs.getInt(10), 10);
-            assertEquals(rs.getString(11), "0000000002");
-            assertEquals(rs.getString(12), "Item T4");
-            assertNull(rs.getString(13));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "S1");
-            assertEquals(rs.getString(3), "888-888-1111");
-            assertEquals(rs.getString(4), "101 YYY Street");
-            assertEquals(rs.getString(5), "10001");
-            assertEquals(rs.getString(6), "0000000001");
-            assertEquals(rs.getString(7), "T1");
-            assertEquals(rs.getInt(8), 100);
-            assertEquals(rs.getInt(9), 5);
-            assertEquals(rs.getInt(10), 10);
-            assertEquals(rs.getString(11), "0000000001");
-            assertEquals(rs.getString(12), "Item T1");
-            assertEquals(rs.getString(13), "000000000000001");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "S6");
-            assertEquals(rs.getString(3), "888-888-6666");
-            assertEquals(rs.getString(4), "606 YYY Street");
-            assertEquals(rs.getString(5), "10006");
-            assertEquals(rs.getString(6), "0000000006");
-            assertEquals(rs.getString(7), "T6");
-            assertEquals(rs.getInt(8), 600);
-            assertEquals(rs.getInt(9), 8);
-            assertEquals(rs.getInt(10), 15);
-            assertEquals(rs.getString(11), "0000000006");
-            assertEquals(rs.getString(12), "Item T6");
-            assertEquals(rs.getString(13), "000000000000002");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "S1");
-            assertEquals(rs.getString(3), "888-888-1111");
-            assertEquals(rs.getString(4), "101 YYY Street");
-            assertEquals(rs.getString(5), "10001");
-            assertEquals(rs.getString(6), "0000000002");
-            assertEquals(rs.getString(7), "T2");
-            assertEquals(rs.getInt(8), 200);
-            assertEquals(rs.getInt(9), 5);
-            assertEquals(rs.getInt(10), 8);
-            assertEquals(rs.getString(11), "0000000001");
-            assertEquals(rs.getString(12), "Item T2");
-            assertEquals(rs.getString(13), "000000000000003");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "S6");
-            assertEquals(rs.getString(3), "888-888-6666");
-            assertEquals(rs.getString(4), "606 YYY Street");
-            assertEquals(rs.getString(5), "10006");
-            assertEquals(rs.getString(6), "0000000006");
-            assertEquals(rs.getString(7), "T6");
-            assertEquals(rs.getInt(8), 600);
-            assertEquals(rs.getInt(9), 8);
-            assertEquals(rs.getInt(10), 15);
-            assertEquals(rs.getString(11), "0000000006");
-            assertEquals(rs.getString(12), "Item T6");
-            assertEquals(rs.getString(13), "000000000000004");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "S2");
-            assertEquals(rs.getString(3), "888-888-2222");
-            assertEquals(rs.getString(4), "202 YYY Street");
-            assertEquals(rs.getString(5), "10002");
-            assertEquals(rs.getString(6), "0000000003");
-            assertEquals(rs.getString(7), "T3");
-            assertEquals(rs.getInt(8), 300);
-            assertEquals(rs.getInt(9), 8);
-            assertEquals(rs.getInt(10), 12);
-            assertEquals(rs.getString(11), "0000000002");
-            assertEquals(rs.getString(12), "Item T3");
-            assertEquals(rs.getString(13), "000000000000005");
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }        
-    }
-    
-    @Test
-    public void testJoinMultiJoinKeys() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String tableName3 = getTableName(conn, JOIN_CUSTOMER_TABLE_FULL_NAME);
-        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ c.name, s.name FROM " + tableName3 + " c LEFT JOIN " + tableName2 + " s ON \"customer_id\" = \"supplier_id\" AND c.loc_id = s.loc_id AND substr(s.name, 2, 1) = substr(c.name, 2, 1) ORDER BY \"customer_id\"";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "C1");
-            assertEquals(rs.getString(2), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "C2");
-            assertNull(rs.getString(2));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "C3");
-            assertEquals(rs.getString(2), "S3");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "C4");
-            assertNull(rs.getString(2));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "C5");
-            assertEquals(rs.getString(2), "S5");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "C6");
-            assertNull(rs.getString(2));
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testJoinWithDifferentNumericJoinKeyTypes() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", i.name, i.price, discount2, quantity FROM " + tableName4 + " o INNER JOIN " 
-            + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" AND o.price = (i.price * (100 - discount2)) / 100.0 WHERE quantity < 5000";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000004");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getInt(3), 600);
-            assertEquals(rs.getInt(4), 15);
-            assertEquals(rs.getInt(5), 4000);
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testJoinWithDifferentDateJoinKeyTypes() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName3 = getTableName(conn, JOIN_CUSTOMER_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", c.name, o.\"DATE\" FROM " + tableName4 + " o INNER JOIN "
-            + tableName3 + " c ON o.\"customer_id\" = c.\"customer_id\" AND o.\"DATE\" = c.\"DATE\" ORDER BY \"order_id\"";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000001");
-            assertEquals(rs.getString(2), "C4");
-            assertEquals(rs.getTimestamp(3), new Timestamp(format.parse("2013-11-22 14:22:56").getTime()));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000002");
-            assertEquals(rs.getString(2), "C3");
-            assertEquals(rs.getTimestamp(3), new Timestamp(format.parse("2013-11-25 10:06:29").getTime()));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000003");
-            assertEquals(rs.getString(2), "C2");
-            assertEquals(rs.getTimestamp(3), new Timestamp(format.parse("2013-11-25 16:45:07").getTime()));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "000000000000005");
-            assertEquals(rs.getString(2), "C5");
-            assertEquals(rs.getTimestamp(3), new Timestamp(format.parse("2013-11-27 09:37:50").getTime()));
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testJoinWithIncomparableJoinKeyTypes() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", i.name, i.price, discount2, quantity FROM " + tableName4 + " o INNER JOIN " 
-            + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" AND o.price / 100 = substr(i.name, 2, 1)";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            statement.executeQuery();
-            fail("Should have got SQLException.");
-        } catch (SQLException e) {
-            assertEquals(e.getErrorCode(), SQLExceptionCode.TYPE_MISMATCH.getErrorCode());
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testJoinPlanWithIndex() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String query1 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item LEFT JOIN " + tableName2 + " supp ON substr(item.name, 2, 1) = substr(supp.name, 2, 1) AND (supp.name BETWEEN 'S1' AND 'S5') WHERE item.name BETWEEN 'T1' AND 'T5' ORDER BY \"item_id\"";
-        String query2 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item INNER JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" WHERE (item.name = 'T1' OR item.name = 'T5') AND (supp.name = 'S1' OR supp.name = 'S5') ORDER BY \"item_id\"";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query1);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "T2");
-            assertEquals(rs.getString(3), "0000000002");
-            assertEquals(rs.getString(4), "S2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "T3");
-            assertEquals(rs.getString(3), "0000000003");
-            assertEquals(rs.getString(4), "S3");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertEquals(rs.getString(3), "0000000004");
-            assertEquals(rs.getString(4), "S4");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-            assertEquals(rs.getString(3), "0000000005");
-            assertEquals(rs.getString(4), "S5");
-
-            assertFalse(rs.next());            
-            
-            statement = conn.prepareStatement(query2);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-            assertEquals(rs.getString(3), "0000000005");
-            assertEquals(rs.getString(4), "S5");
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testJoinWithSkipMergeOptimization() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
-        String query = "SELECT /*+ USE

<TRUNCATED>

[14/25] phoenix git commit: PHOENIX-4245 Breakup IndexIT into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
PHOENIX-4245 Breakup IndexIT into several integration tests so as not to create too many tables in one test


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

Branch: refs/heads/master
Commit: fd7776407737cf6cc5cb95eb8c5b9380af4b560c
Parents: 527f786
Author: James Taylor <jt...@salesforce.com>
Authored: Thu Sep 28 13:24:20 2017 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Thu Sep 28 17:51:57 2017 -0700

----------------------------------------------------------------------
 .../index/GlobalImmutableNonTxIndexIT.java      | 38 ++++++++++++++++++++
 .../end2end/index/GlobalImmutableTxIndexIT.java | 38 ++++++++++++++++++++
 .../index/GlobalMutableNonTxIndexIT.java        | 38 ++++++++++++++++++++
 .../end2end/index/GlobalMutableTxIndexIT.java   | 38 ++++++++++++++++++++
 .../apache/phoenix/end2end/index/IndexIT.java   | 17 ++-------
 .../index/LocalImmutableNonTxIndexIT.java       | 38 ++++++++++++++++++++
 .../end2end/index/LocalImmutableTxIndexIT.java  | 38 ++++++++++++++++++++
 .../end2end/index/LocalMutableNonTxIndexIT.java | 38 ++++++++++++++++++++
 .../end2end/index/LocalMutableTxIndexIT.java    | 37 +++++++++++++++++++
 9 files changed, 305 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/fd777640/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalImmutableNonTxIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalImmutableNonTxIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalImmutableNonTxIndexIT.java
new file mode 100644
index 0000000..52b219d
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalImmutableNonTxIndexIT.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.index;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.junit.runners.Parameterized.Parameters;
+
+public class GlobalImmutableNonTxIndexIT extends IndexIT {
+
+    public GlobalImmutableNonTxIndexIT(boolean localIndex, boolean mutable, boolean transactional, boolean columnEncoded) {
+        super(localIndex, mutable, transactional, columnEncoded);
+    }
+
+    @Parameters(name="GlobalImmutableNonTxIndexIT_localIndex={0},mutable={1},transactional={2},columnEncoded={3}") // name is used by failsafe as file name in reports
+    public static Collection<Boolean[]> data() {
+        return Arrays.asList(new Boolean[][] {
+                { false, false, false, false }, { false, false, false, true }
+           });
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/fd777640/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalImmutableTxIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalImmutableTxIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalImmutableTxIndexIT.java
new file mode 100644
index 0000000..58eab8c
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalImmutableTxIndexIT.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.index;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.junit.runners.Parameterized.Parameters;
+
+public class GlobalImmutableTxIndexIT extends IndexIT {
+
+    public GlobalImmutableTxIndexIT(boolean localIndex, boolean mutable, boolean transactional, boolean columnEncoded) {
+        super(localIndex, mutable, transactional, columnEncoded);
+    }
+
+    @Parameters(name="GlobalImmutableTxIndexIT_localIndex={0},mutable={1},transactional={2},columnEncoded={3}") // name is used by failsafe as file name in reports
+    public static Collection<Boolean[]> data() {
+        return Arrays.asList(new Boolean[][] {
+                { false, false, true, false }, { false, false, true, true }, 
+           });
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/fd777640/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableNonTxIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableNonTxIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableNonTxIndexIT.java
new file mode 100644
index 0000000..9fe1938
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableNonTxIndexIT.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.index;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.junit.runners.Parameterized.Parameters;
+
+public class GlobalMutableNonTxIndexIT extends IndexIT {
+
+    public GlobalMutableNonTxIndexIT(boolean localIndex, boolean mutable, boolean transactional, boolean columnEncoded) {
+        super(localIndex, mutable, transactional, columnEncoded);
+    }
+
+    @Parameters(name="GlobalMutableNonTxIndexIT_localIndex={0},mutable={1},transactional={2},columnEncoded={3}") // name is used by failsafe as file name in reports
+    public static Collection<Boolean[]> data() {
+        return Arrays.asList(new Boolean[][] {
+                { false, true, false, false }, { false, true, false, true }
+           });
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/fd777640/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableTxIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableTxIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableTxIndexIT.java
new file mode 100644
index 0000000..4536959
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableTxIndexIT.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.index;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.junit.runners.Parameterized.Parameters;
+
+public class GlobalMutableTxIndexIT extends IndexIT {
+
+    public GlobalMutableTxIndexIT(boolean localIndex, boolean mutable, boolean transactional, boolean columnEncoded) {
+        super(localIndex, mutable, transactional, columnEncoded);
+    }
+
+    @Parameters(name="GlobalMutableTxIndexIT_localIndex={0},mutable={1},transactional={2},columnEncoded={3}") // name is used by failsafe as file name in reports
+    public static Collection<Boolean[]> data() {
+        return Arrays.asList(new Boolean[][] {
+                { false, true, true, false }, { false, true, true, true }
+           });
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/fd777640/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexIT.java
index f8856e3..b540ad1 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexIT.java
@@ -37,8 +37,6 @@ import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
-import java.util.Arrays;
-import java.util.Collection;
 import java.util.Properties;
 import java.util.Random;
 
@@ -79,10 +77,9 @@ import org.apache.phoenix.util.TransactionUtil;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class IndexIT extends ParallelStatsDisabledIT {
+public abstract class IndexIT extends ParallelStatsDisabledIT {
     private static final Random RAND = new Random();
 
     private final boolean localIndex;
@@ -90,7 +87,7 @@ public class IndexIT extends ParallelStatsDisabledIT {
     private final boolean mutable;
     private final String tableDDLOptions;
 
-    public IndexIT(boolean localIndex, boolean mutable, boolean transactional, boolean columnEncoded) {
+    protected IndexIT(boolean localIndex, boolean mutable, boolean transactional, boolean columnEncoded) {
         this.localIndex = localIndex;
         this.transactional = transactional;
         this.mutable = mutable;
@@ -116,16 +113,6 @@ public class IndexIT extends ParallelStatsDisabledIT {
         this.tableDDLOptions = optionBuilder.toString();
     }
 
-    @Parameters(name="IndexIT_localIndex={0},mutable={1},transactional={2},columnEncoded={3}") // name is used by failsafe as file name in reports
-    public static Collection<Boolean[]> data() {
-        return Arrays.asList(new Boolean[][] {
-                { false, false, false, false }, { false, false, false, true }, { false, false, true, false }, { false, false, true, true }, 
-                { false, true, false, false }, { false, true, false, true }, { false, true, true, false }, { false, true, true, true }, 
-                { true, false, false, false }, { true, false, false, true }, { true, false, true, false }, { true, false, true, true }, 
-                { true, true, false, false }, { true, true, false, true }, { true, true, true, false }, { true, true, true, true } 
-           });
-    }
-
     @Test
     public void testIndexWithNullableFixedWithCols() throws Exception {
         Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/fd777640/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalImmutableNonTxIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalImmutableNonTxIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalImmutableNonTxIndexIT.java
new file mode 100644
index 0000000..f2d18d2
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalImmutableNonTxIndexIT.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.index;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.junit.runners.Parameterized.Parameters;
+
+public class LocalImmutableNonTxIndexIT extends IndexIT {
+
+    public LocalImmutableNonTxIndexIT(boolean localIndex, boolean mutable, boolean transactional, boolean columnEncoded) {
+        super(localIndex, mutable, transactional, columnEncoded);
+    }
+
+    @Parameters(name="LocalImmutableNonTxIndexIT_localIndex={0},mutable={1},transactional={2},columnEncoded={3}") // name is used by failsafe as file name in reports
+    public static Collection<Boolean[]> data() {
+        return Arrays.asList(new Boolean[][] {
+                { true, false, false, false }, { true, false, false, true }
+           });
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/fd777640/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalImmutableTxIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalImmutableTxIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalImmutableTxIndexIT.java
new file mode 100644
index 0000000..9e6fd5f
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalImmutableTxIndexIT.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.index;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.junit.runners.Parameterized.Parameters;
+
+public class LocalImmutableTxIndexIT extends IndexIT {
+
+    public LocalImmutableTxIndexIT(boolean localIndex, boolean mutable, boolean transactional, boolean columnEncoded) {
+        super(localIndex, mutable, transactional, columnEncoded);
+    }
+
+    @Parameters(name="LocalImmutableTxIndexIT_localIndex={0},mutable={1},transactional={2},columnEncoded={3}") // name is used by failsafe as file name in reports
+    public static Collection<Boolean[]> data() {
+        return Arrays.asList(new Boolean[][] {
+                { true, false, true, false }, { true, false, true, true }
+           });
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/fd777640/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalMutableNonTxIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalMutableNonTxIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalMutableNonTxIndexIT.java
new file mode 100644
index 0000000..9785d20
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalMutableNonTxIndexIT.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.index;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.junit.runners.Parameterized.Parameters;
+
+public class LocalMutableNonTxIndexIT extends IndexIT {
+
+    public LocalMutableNonTxIndexIT(boolean localIndex, boolean mutable, boolean transactional, boolean columnEncoded) {
+        super(localIndex, mutable, transactional, columnEncoded);
+    }
+
+    @Parameters(name="LocalMutableNonTxIndexIT_localIndex={0},mutable={1},transactional={2},columnEncoded={3}") // name is used by failsafe as file name in reports
+    public static Collection<Boolean[]> data() {
+        return Arrays.asList(new Boolean[][] {
+                { true, true, false, false }, { true, true, false, true }
+           });
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/fd777640/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalMutableTxIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalMutableTxIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalMutableTxIndexIT.java
new file mode 100644
index 0000000..c09a8c0
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalMutableTxIndexIT.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.index;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.junit.runners.Parameterized.Parameters;
+
+public class LocalMutableTxIndexIT extends IndexIT {
+
+    public LocalMutableTxIndexIT(boolean localIndex, boolean mutable, boolean transactional, boolean columnEncoded) {
+        super(localIndex, mutable, transactional, columnEncoded);
+    }
+
+    @Parameters(name="LocalMutableTxIndexIT_localIndex={0},mutable={1},transactional={2},columnEncoded={3}") // name is used by failsafe as file name in reports
+    public static Collection<Boolean[]> data() {
+        return Arrays.asList(new Boolean[][] {
+                { true, true, true, false }, { true, true, true, true } 
+           });
+    }
+}


[04/25] phoenix git commit: PHOENIX-4246 Breakup join related tests into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/ee20a8c9/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinIT.java
new file mode 100644
index 0000000..8e7bfe2
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinIT.java
@@ -0,0 +1,2563 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.join;
+
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.util.Collection;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.phoenix.exception.SQLExceptionCode;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.QueryUtil;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import com.google.common.collect.Lists;
+
+@RunWith(Parameterized.class)
+public class SortMergeJoinIT extends BaseJoinIT {
+    
+    @Parameters
+    public static Collection<Object> data() {
+        List<Object> testCases = Lists.newArrayList();
+        testCases.add(new String[][] {
+                {}, {
+                "SORT-MERGE-JOIN (LEFT) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "AND\n" +
+                "    SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    AND (SKIP MERGE)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER FILTER BY QUANTITY < 5000\n" +
+                "            SERVER SORTED BY [\"O.item_id\"]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    CLIENT SORTED BY [\"I.supplier_id\"]",
+                
+                "SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "AND\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "        SERVER SORTED BY [\"O.item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "CLIENT 4 ROW LIMIT",
+                
+                "SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "AND\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY"
+                }});
+        testCases.add(new String[][] {
+                {
+                "CREATE INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
+                "CREATE INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
+                "CREATE INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
+                }, {
+                "SORT-MERGE-JOIN (LEFT) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_supplier\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" + 
+                "        SERVER SORTED BY [\"S.:supplier_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "AND\n" +
+                "    SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "            SERVER SORTED BY [\"I.:item_id\"]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    AND (SKIP MERGE)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER FILTER BY QUANTITY < 5000\n" +
+                "            SERVER SORTED BY [\"O.item_id\"]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    CLIENT SORTED BY [\"I.0:supplier_id\"]",
+                
+                "SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        SERVER SORTED BY [\"I.:item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "AND\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "        SERVER SORTED BY [\"O.item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "CLIENT 4 ROW LIMIT",
+                
+                "SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER Join.idx_item\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        SERVER SORTED BY [\"I1.:item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "AND\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER Join.idx_item\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        SERVER SORTED BY [\"I2.:item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "CLIENT SORTED BY [\"I1.:item_id\"]"
+                }});
+        testCases.add(new String[][] {
+                {
+                "CREATE LOCAL INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
+                "CREATE LOCAL INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
+                "CREATE LOCAL INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
+                }, {
+                "SORT-MERGE-JOIN (LEFT) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +JOIN_SUPPLIER_TABLE_FULL_NAME + " [1]\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" + 
+                "        SERVER SORTED BY [\"S.:supplier_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "AND\n" +
+                "    SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "            SERVER SORTED BY [\"I.:item_id\"]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    AND (SKIP MERGE)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER FILTER BY QUANTITY < 5000\n" +
+                "            SERVER SORTED BY [\"O.item_id\"]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    CLIENT SORTED BY [\"I.0:supplier_id\"]",
+                
+                "SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        SERVER SORTED BY [\"I.:item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "AND\n" +
+                "    CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "        SERVER SORTED BY [\"O.item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "CLIENT 4 ROW LIMIT",
+                
+                "SORT-MERGE-JOIN (INNER) TABLES\n" +
+                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        SERVER SORTED BY [\"I1.:item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "AND\n" +
+                "    CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "        SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        SERVER SORTED BY [\"I2.:item_id\"]\n" +
+                "    CLIENT MERGE SORT\n" +
+                "CLIENT SORTED BY [\"I1.:item_id\"]"
+                }});
+        return testCases;
+    }
+    
+
+    public SortMergeJoinIT(String[] indexDDL, String[] plans) {
+        super(indexDDL, plans);
+    }
+    
+    @Test
+    public void testDefaultJoin() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "S5");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "0000000006");
+            assertEquals(rs.getString(4), "S6");
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testInnerJoin() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name, next value for " + seqName + " FROM " + tableName1 + " item INNER JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertEquals(1, rs.getInt(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertEquals(2, rs.getInt(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertEquals(3, rs.getInt(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertEquals(4, rs.getInt(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "S5");
+            assertEquals(5, rs.getInt(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "0000000006");
+            assertEquals(rs.getString(4), "S6");
+            assertEquals(6, rs.getInt(5));
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+            
+    @Test
+    public void testLeftJoin() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String query[] = new String[3];
+        query[0] = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name, next value for " + seqName + " FROM " + tableName1 + " item LEFT JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
+        query[1] = "SELECT /*+ USE_SORT_MERGE_JOIN*/ " + tableName1 + ".\"item_id\", " + tableName1 + ".name, " + tableName2 + ".\"supplier_id\", " + tableName2 + ".name, next value for " + seqName + " FROM " + tableName1 + " LEFT JOIN " + tableName2 + " ON " + tableName1 + ".\"supplier_id\" = " + tableName2 + ".\"supplier_id\" ORDER BY \"item_id\"";
+        query[2] = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", " + tableName1 + ".name, supp.\"supplier_id\", " + tableName2 + ".name, next value for " + seqName + " FROM " + tableName1 + " item LEFT JOIN " + tableName2 + " supp ON " + tableName1 + ".\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
+        try {
+            for (int i = 0; i < query.length; i++) {
+                PreparedStatement statement = conn.prepareStatement(query[i]);
+                ResultSet rs = statement.executeQuery();
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "0000000001");
+                assertEquals(rs.getString(2), "T1");
+                assertEquals(rs.getString(3), "0000000001");
+                assertEquals(rs.getString(4), "S1");
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "0000000002");
+                assertEquals(rs.getString(2), "T2");
+                assertEquals(rs.getString(3), "0000000001");
+                assertEquals(rs.getString(4), "S1");
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "0000000003");
+                assertEquals(rs.getString(2), "T3");
+                assertEquals(rs.getString(3), "0000000002");
+                assertEquals(rs.getString(4), "S2");
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "0000000004");
+                assertEquals(rs.getString(2), "T4");
+                assertEquals(rs.getString(3), "0000000002");
+                assertEquals(rs.getString(4), "S2");
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "0000000005");
+                assertEquals(rs.getString(2), "T5");
+                assertEquals(rs.getString(3), "0000000005");
+                assertEquals(rs.getString(4), "S5");
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "0000000006");
+                assertEquals(rs.getString(2), "T6");
+                assertEquals(rs.getString(3), "0000000006");
+                assertEquals(rs.getString(4), "S6");
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "invalid001");
+                assertEquals(rs.getString(2), "INVALID-1");
+                assertNull(rs.getString(3));
+                assertNull(rs.getString(4));
+
+                assertFalse(rs.next());
+                rs.close();
+                statement.close();
+            }
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testRightJoin() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName2 + " supp RIGHT JOIN " + tableName1 + " item ON item.\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "S5");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "0000000006");
+            assertEquals(rs.getString(4), "S6");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "invalid001");
+            assertEquals(rs.getString(2), "INVALID-1");
+            assertNull(rs.getString(3));
+            assertNull(rs.getString(4));
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testInnerJoinWithPreFilters() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String query1 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item INNER JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" AND supp.\"supplier_id\" BETWEEN '0000000001' AND '0000000005' ORDER BY \"item_id\"";
+        String query2 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item INNER JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" AND (supp.\"supplier_id\" = '0000000001' OR supp.\"supplier_id\" = '0000000005') ORDER BY \"item_id\"";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query1);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "S5");
+
+            assertFalse(rs.next());
+            
+            
+            statement = conn.prepareStatement(query2);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "S5");
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testLeftJoinWithPreFilters() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item LEFT JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" AND (supp.\"supplier_id\" = '0000000001' OR supp.\"supplier_id\" = '0000000005') ORDER BY \"item_id\"";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertNull(rs.getString(3));
+            assertNull(rs.getString(4));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertNull(rs.getString(3));
+            assertNull(rs.getString(4));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "S5");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "T6");
+            assertNull(rs.getString(3));
+            assertNull(rs.getString(4));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "invalid001");
+            assertEquals(rs.getString(2), "INVALID-1");
+            assertNull(rs.getString(3));
+            assertNull(rs.getString(4));
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testJoinWithPostFilters() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String query1 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName2 + " supp RIGHT JOIN " + tableName1 + " item ON item.\"supplier_id\" = supp.\"supplier_id\" WHERE supp.\"supplier_id\" BETWEEN '0000000001' AND '0000000005' ORDER BY \"item_id\"";
+        String query2 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item LEFT JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" WHERE supp.\"supplier_id\" = '0000000001' OR supp.\"supplier_id\" = '0000000005' ORDER BY \"item_id\"";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query1);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "S5");
+
+            assertFalse(rs.next());
+            
+            
+            statement = conn.prepareStatement(query2);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "S5");
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testStarJoin() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName3 = getTableName(conn, JOIN_CUSTOMER_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String[] query = new String[5];
+        query[0] = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", c.name, i.name iname, quantity, o.\"DATE\" FROM " + tableName4 + " o JOIN "
+            + tableName3 + " c ON o.\"customer_id\" = c.\"customer_id\" JOIN " 
+            + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" ORDER BY \"order_id\"";
+        query[1] = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", c.name, i.name iname, quantity, o.\"DATE\" FROM " + tableName4 + " o, "
+                + tableName3 + " c, " 
+                + tableName1 + " i WHERE o.\"item_id\" = i.\"item_id\" AND o.\"customer_id\" = c.\"customer_id\" ORDER BY \"order_id\"";
+        query[2] = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", c.name, i.name iname, quantity, o.\"DATE\" FROM " + tableName4 + " o JOIN "
+                + tableName3 + " c ON o.\"customer_id\" = c.\"customer_id\" JOIN " 
+                + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" ORDER BY \"order_id\"";
+        query[3] = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", c.name, i.name iname, quantity, o.\"DATE\" FROM (" + tableName4 + " o, "
+                + tableName3 + " c), " 
+                + tableName1 + " i WHERE o.\"item_id\" = i.\"item_id\" AND o.\"customer_id\" = c.\"customer_id\" ORDER BY \"order_id\"";
+        query[4] = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", c.name, i.name iname, quantity, o.\"DATE\" FROM " + tableName4 + " o, ("
+                + tableName3 + " c, " 
+                + tableName1 + " i) WHERE o.\"item_id\" = i.\"item_id\" AND o.\"customer_id\" = c.\"customer_id\" ORDER BY \"order_id\"";
+        try {
+            for (int i = 0; i < query.length; i++) {
+                PreparedStatement statement = conn.prepareStatement(query[i]);
+                ResultSet rs = statement.executeQuery();
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "000000000000001");
+                assertEquals(rs.getString("\"order_id\""), "000000000000001");
+                assertEquals(rs.getString(2), "C4");
+                assertEquals(rs.getString("C.name"), "C4");
+                assertEquals(rs.getString(3), "T1");
+                assertEquals(rs.getString("iName"), "T1");
+                assertEquals(rs.getInt(4), 1000);
+                assertEquals(rs.getInt("Quantity"), 1000);
+                assertNotNull(rs.getDate(5));
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "000000000000002");
+                assertEquals(rs.getString(2), "C3");
+                assertEquals(rs.getString(3), "T6");
+                assertEquals(rs.getInt(4), 2000);
+                assertNotNull(rs.getDate(5));
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "000000000000003");
+                assertEquals(rs.getString(2), "C2");
+                assertEquals(rs.getString(3), "T2");
+                assertEquals(rs.getInt(4), 3000);
+                assertNotNull(rs.getDate(5));
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "000000000000004");
+                assertEquals(rs.getString(2), "C4");
+                assertEquals(rs.getString(3), "T6");
+                assertEquals(rs.getInt(4), 4000);
+                assertNotNull(rs.getDate(5));
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "000000000000005");
+                assertEquals(rs.getString(2), "C5");
+                assertEquals(rs.getString(3), "T3");
+                assertEquals(rs.getInt(4), 5000);
+                assertNotNull(rs.getDate(5));
+
+                assertFalse(rs.next());
+            }
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testLeftJoinWithAggregation() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String query1 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ i.name, sum(quantity) FROM " + tableName4 + " o LEFT JOIN " 
+            + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" GROUP BY i.name ORDER BY i.name";
+        String query2 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ i.\"item_id\" iid, sum(quantity) q FROM " + tableName4 + " o LEFT JOIN " 
+                + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" GROUP BY i.\"item_id\" ORDER BY q DESC";
+        String query3 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ i.\"item_id\" iid, sum(quantity) q FROM " + tableName1 + " i LEFT JOIN " 
+                + tableName4 + " o ON o.\"item_id\" = i.\"item_id\" GROUP BY i.\"item_id\" ORDER BY q DESC NULLS LAST, iid";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query1);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T1");
+            assertEquals(rs.getInt(2), 1000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T2");
+            assertEquals(rs.getInt(2), 3000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T3");
+            assertEquals(rs.getInt(2), 5000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T6");
+            assertEquals(rs.getInt(2), 6000);
+
+            assertFalse(rs.next());
+            
+            statement = conn.prepareStatement(query2);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000006");
+            assertEquals(rs.getInt("q"), 6000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000003");
+            assertEquals(rs.getInt("q"), 5000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000002");
+            assertEquals(rs.getInt("q"), 3000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000001");
+            assertEquals(rs.getInt("q"), 1000);
+
+            assertFalse(rs.next());
+            
+            statement = conn.prepareStatement(query3);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000006");
+            assertEquals(rs.getInt("q"), 6000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000003");
+            assertEquals(rs.getInt("q"), 5000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000002");
+            assertEquals(rs.getInt("q"), 3000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000001");
+            assertEquals(rs.getInt("q"), 1000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000004");
+            assertEquals(rs.getInt("q"), 0);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000005");
+            assertEquals(rs.getInt("q"), 0);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "invalid001");
+            assertEquals(rs.getInt("q"), 0);
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testRightJoinWithAggregation() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String query1 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ i.name, sum(quantity) FROM " + tableName4 + " o RIGHT JOIN " 
+            + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" GROUP BY i.name ORDER BY i.name";
+        String query2 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ i.\"item_id\" iid, sum(quantity) q FROM " + tableName4 + " o RIGHT JOIN " 
+            + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" GROUP BY i.\"item_id\" ORDER BY q DESC NULLS LAST, iid";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query1);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "INVALID-1");
+            assertEquals(rs.getInt(2), 0);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T1");
+            assertEquals(rs.getInt(2), 1000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T2");
+            assertEquals(rs.getInt(2), 3000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T3");
+            assertEquals(rs.getInt(2), 5000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T4");
+            assertEquals(rs.getInt(2), 0);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T5");
+            assertEquals(rs.getInt(2), 0);
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "T6");
+            assertEquals(rs.getInt(2), 6000);
+
+            assertFalse(rs.next());
+            
+            statement = conn.prepareStatement(query2);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000006");
+            assertEquals(rs.getInt("q"), 6000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000003");
+            assertEquals(rs.getInt("q"), 5000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000002");
+            assertEquals(rs.getInt("q"), 3000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000001");
+            assertEquals(rs.getInt("q"), 1000);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000004");
+            assertEquals(rs.getInt("q"), 0);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "0000000005");
+            assertEquals(rs.getInt("q"), 0);
+            assertTrue (rs.next());
+            assertEquals(rs.getString("iid"), "invalid001");
+            assertEquals(rs.getInt("q"), 0);
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testLeftRightJoin() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String query1 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + tableName4 + " o LEFT JOIN "
+                + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" RIGHT JOIN "
+                + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\" ORDER BY \"order_id\", s.\"supplier_id\" DESC";
+        String query2 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + tableName4 + " o LEFT JOIN "
+                + "(" + tableName1 + " i RIGHT JOIN " + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\")" 
+                + " ON o.\"item_id\" = i.\"item_id\" ORDER BY \"order_id\", s.\"supplier_id\" DESC";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query1);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertNull(rs.getString(2));
+            assertEquals(rs.getString(3), "S5");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertNull(rs.getString(2));
+            assertEquals(rs.getString(3), "S4");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertNull(rs.getString(2));
+            assertEquals(rs.getString(3), "S3");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 1000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 2000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 3000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000004");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 4000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "S2");
+            assertEquals(rs.getInt(4), 5000);
+            assertNotNull(rs.getDate(5));
+
+            assertFalse(rs.next());
+            
+            statement = conn.prepareStatement(query2);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 1000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 2000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 3000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000004");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 4000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "S2");
+            assertEquals(rs.getInt(4), 5000);
+            assertNotNull(rs.getDate(5));
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testRightLeftJoin() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String query1 = "SELECT \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + tableName1 + " i RIGHT JOIN "
+                + tableName4 + " o ON o.\"item_id\" = i.\"item_id\" LEFT JOIN "
+                + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\" ORDER BY \"order_id\"";
+        String query2 = "SELECT \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + tableName4 + " o RIGHT JOIN "
+                + "(" + tableName1 + " i LEFT JOIN " + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\")" 
+                + " ON o.\"item_id\" = i.\"item_id\" ORDER BY \"order_id\", s.\"supplier_id\" DESC";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query1);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 1000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 2000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 3000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000004");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 4000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "S2");
+            assertEquals(rs.getInt(4), 5000);
+            assertNotNull(rs.getDate(5));
+
+            assertFalse(rs.next());
+            
+            statement = conn.prepareStatement(query2);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertEquals(rs.getString(2), "INVALID-1");
+            assertNull(rs.getString(3));
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "S5");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "S2");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 1000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 2000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 3000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000004");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 4000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "S2");
+            assertEquals(rs.getInt(4), 5000);
+            assertNotNull(rs.getDate(5));
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testMultiLeftJoin() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String[] queries = {
+                "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + tableName4 + " o LEFT JOIN "
+                        + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" LEFT JOIN "
+                        + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\" ORDER BY \"order_id\"",
+                "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + tableName4 + " o LEFT JOIN "
+                        + "(" + tableName1 + " i LEFT JOIN " + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\") " 
+                        + "ON o.\"item_id\" = i.\"item_id\" ORDER BY \"order_id\""};
+        try {
+            for (String query : queries) {
+                PreparedStatement statement = conn.prepareStatement(query);
+                ResultSet rs = statement.executeQuery();
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "000000000000001");
+                assertEquals(rs.getString(2), "T1");
+                assertEquals(rs.getString(3), "S1");
+                assertEquals(rs.getInt(4), 1000);
+                assertNotNull(rs.getDate(5));
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "000000000000002");
+                assertEquals(rs.getString(2), "T6");
+                assertEquals(rs.getString(3), "S6");
+                assertEquals(rs.getInt(4), 2000);
+                assertNotNull(rs.getDate(5));
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "000000000000003");
+                assertEquals(rs.getString(2), "T2");
+                assertEquals(rs.getString(3), "S1");
+                assertEquals(rs.getInt(4), 3000);
+                assertNotNull(rs.getDate(5));
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "000000000000004");
+                assertEquals(rs.getString(2), "T6");
+                assertEquals(rs.getString(3), "S6");
+                assertEquals(rs.getInt(4), 4000);
+                assertNotNull(rs.getDate(5));
+                assertTrue (rs.next());
+                assertEquals(rs.getString(1), "000000000000005");
+                assertEquals(rs.getString(2), "T3");
+                assertEquals(rs.getString(3), "S2");
+                assertEquals(rs.getInt(4), 5000);
+                assertNotNull(rs.getDate(5));
+
+                assertFalse(rs.next());
+            }
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testMultiRightJoin() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + tableName4 + " o RIGHT JOIN "
+            + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" RIGHT JOIN "
+            + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\" ORDER BY \"order_id\", s.\"supplier_id\" DESC";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "S5");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertNull(rs.getString(2));
+            assertEquals(rs.getString(3), "S4");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertNull(rs.getString(2));
+            assertEquals(rs.getString(3), "S3");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "S2");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 1000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 2000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 3000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000004");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 4000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "S2");
+            assertEquals(rs.getInt(4), 5000);
+            assertNotNull(rs.getDate(5));
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    // Basically a copy of testMultiRightJoin, but with a very small result scan chunk size
+    // to test that repeated row keys within a single chunk are handled properly
+    @Test
+    public void testMultiRightJoin_SmallChunkSize() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        props.setProperty(QueryServices.SCAN_RESULT_CHUNK_SIZE, "1");
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", i.name, s.name, quantity, \"DATE\" FROM " + tableName4 + " o RIGHT JOIN "
+                + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" RIGHT JOIN "
+                + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\" ORDER BY \"order_id\", s.\"supplier_id\" DESC";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "S5");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertNull(rs.getString(2));
+            assertEquals(rs.getString(3), "S4");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertNull(rs.getString(2));
+            assertEquals(rs.getString(3), "S3");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertNull(rs.getString(1));
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "S2");
+            assertEquals(rs.getInt(4), 0);
+            assertNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 1000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 2000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "S1");
+            assertEquals(rs.getInt(4), 3000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000004");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "S6");
+            assertEquals(rs.getInt(4), 4000);
+            assertNotNull(rs.getDate(5));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "S2");
+            assertEquals(rs.getInt(4), 5000);
+            assertNotNull(rs.getDate(5));
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testJoinWithWildcard() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ * FROM " + tableName1 + " LEFT JOIN " + tableName2 + " supp ON " + tableName1 + ".\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "0000000001");
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "T1");
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 100);
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 5);
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 10);
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000001");
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Item T1");
+            assertEquals(rs.getString("SUPP.supplier_id"), "0000000001");
+            assertEquals(rs.getString("supp.name"), "S1");
+            assertEquals(rs.getString("supp.phone"), "888-888-1111");
+            assertEquals(rs.getString("supp.address"), "101 YYY Street");
+            assertEquals(rs.getString("supp.loc_id"), "10001");            
+            assertTrue (rs.next());
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "0000000002");
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "T2");
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 200);
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 5);
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 8);
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000001");
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Item T2");
+            assertEquals(rs.getString("SUPP.supplier_id"), "0000000001");
+            assertEquals(rs.getString("supp.name"), "S1");
+            assertEquals(rs.getString("supp.phone"), "888-888-1111");
+            assertEquals(rs.getString("supp.address"), "101 YYY Street");
+            assertEquals(rs.getString("supp.loc_id"), "10001");            
+            assertTrue (rs.next());
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "0000000003");
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "T3");
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 300);
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 8);
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 12);
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000002");
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Item T3");
+            assertEquals(rs.getString("SUPP.supplier_id"), "0000000002");
+            assertEquals(rs.getString("supp.name"), "S2");
+            assertEquals(rs.getString("supp.phone"), "888-888-2222");
+            assertEquals(rs.getString("supp.address"), "202 YYY Street");
+            assertEquals(rs.getString("supp.loc_id"), "10002");            
+            assertTrue (rs.next());
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "0000000004");
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "T4");
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 400);
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 6);
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 10);
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000002");
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Item T4");
+            assertEquals(rs.getString("SUPP.supplier_id"), "0000000002");
+            assertEquals(rs.getString("supp.name"), "S2");
+            assertEquals(rs.getString("supp.phone"), "888-888-2222");
+            assertEquals(rs.getString("supp.address"), "202 YYY Street");
+            assertEquals(rs.getString("supp.loc_id"), "10002");            
+            assertTrue (rs.next());
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "0000000005");
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "T5");
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 500);
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 8);
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 15);
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000005");
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Item T5");
+            assertEquals(rs.getString("SUPP.supplier_id"), "0000000005");
+            assertEquals(rs.getString("supp.name"), "S5");
+            assertEquals(rs.getString("supp.phone"), "888-888-5555");
+            assertEquals(rs.getString("supp.address"), "505 YYY Street");
+            assertEquals(rs.getString("supp.loc_id"), "10005");            
+            assertTrue (rs.next());
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "0000000006");
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "T6");
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 600);
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 8);
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 15);
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000006");
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Item T6");
+            assertEquals(rs.getString("SUPP.supplier_id"), "0000000006");
+            assertEquals(rs.getString("supp.name"), "S6");
+            assertEquals(rs.getString("supp.phone"), "888-888-6666");
+            assertEquals(rs.getString("supp.address"), "606 YYY Street");
+            assertEquals(rs.getString("supp.loc_id"), "10006");            
+            assertTrue (rs.next());
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".item_id"), "invalid001");
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".NAME"), "INVALID-1");
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".PRICE"), 0);
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT1"), 0);
+            assertEquals(rs.getInt(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DISCOUNT2"), 0);
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".supplier_id"), "0000000000");
+            assertEquals(rs.getString(getDisplayTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + ".DESCRIPTION"), "Invalid item for join test");
+            assertNull(rs.getString("SUPP.supplier_id"));
+            assertNull(rs.getString("supp.name"));
+            assertNull(rs.getString("supp.phone"));
+            assertNull(rs.getString("supp.address"));
+            assertNull(rs.getString("supp.loc_id"));
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testJoinWithTableWildcard() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ s.*, "+ tableName1 + ".*, \"order_id\" FROM " + tableName4 + " o RIGHT JOIN " 
+                + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" RIGHT JOIN "
+                + tableName2 + " s ON i.\"supplier_id\" = s.\"supplier_id\" ORDER BY \"order_id\", s.\"supplier_id\" DESC";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            ResultSetMetaData md = rs.getMetaData();
+            assertEquals(md.getColumnCount(), 13);
+            
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "S5");
+            assertEquals(rs.getString(3), "888-888-5555");
+            assertEquals(rs.getString(4), "505 YYY Street");
+            assertEquals(rs.getString(5), "10005");
+            assertEquals(rs.getString(6), "0000000005");
+            assertEquals(rs.getString(7), "T5");
+            assertEquals(rs.getInt(8), 500);
+            assertEquals(rs.getInt(9), 8);
+            assertEquals(rs.getInt(10), 15);
+            assertEquals(rs.getString(11), "0000000005");
+            assertEquals(rs.getString(12), "Item T5");
+            assertNull(rs.getString(13));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "S4");
+            assertEquals(rs.getString(3), "888-888-4444");
+            assertEquals(rs.getString(4), "404 YYY Street");
+            assertNull(rs.getString(5));
+            assertNull(rs.getString(6));
+            assertNull(rs.getString(7));
+            assertEquals(rs.getInt(8), 0);
+            assertEquals(rs.getInt(9), 0);
+            assertEquals(rs.getInt(10), 0);
+            assertNull(rs.getString(11));
+            assertNull(rs.getString(12));            
+            assertNull(rs.getString(13));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "S3");
+            assertEquals(rs.getString(3), "888-888-3333");
+            assertEquals(rs.getString(4), "303 YYY Street");
+            assertNull(rs.getString(5));
+            assertNull(rs.getString(6));
+            assertNull(rs.getString(7));
+            assertEquals(rs.getInt(8), 0);
+            assertEquals(rs.getInt(9), 0);
+            assertEquals(rs.getInt(10), 0);
+            assertNull(rs.getString(11));
+            assertNull(rs.getString(12));            
+            assertNull(rs.getString(13));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "S2");
+            assertEquals(rs.getString(3), "888-888-2222");
+            assertEquals(rs.getString(4), "202 YYY Street");
+            assertEquals(rs.getString(5), "10002");
+            assertEquals(rs.getString(6), "0000000004");
+            assertEquals(rs.getString(7), "T4");
+            assertEquals(rs.getInt(8), 400);
+            assertEquals(rs.getInt(9), 6);
+            assertEquals(rs.getInt(10), 10);
+            assertEquals(rs.getString(11), "0000000002");
+            assertEquals(rs.getString(12), "Item T4");
+            assertNull(rs.getString(13));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "S1");
+            assertEquals(rs.getString(3), "888-888-1111");
+            assertEquals(rs.getString(4), "101 YYY Street");
+            assertEquals(rs.getString(5), "10001");
+            assertEquals(rs.getString(6), "0000000001");
+            assertEquals(rs.getString(7), "T1");
+            assertEquals(rs.getInt(8), 100);
+            assertEquals(rs.getInt(9), 5);
+            assertEquals(rs.getInt(10), 10);
+            assertEquals(rs.getString(11), "0000000001");
+            assertEquals(rs.getString(12), "Item T1");
+            assertEquals(rs.getString(13), "000000000000001");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "S6");
+            assertEquals(rs.getString(3), "888-888-6666");
+            assertEquals(rs.getString(4), "606 YYY Street");
+            assertEquals(rs.getString(5), "10006");
+            assertEquals(rs.getString(6), "0000000006");
+            assertEquals(rs.getString(7), "T6");
+            assertEquals(rs.getInt(8), 600);
+            assertEquals(rs.getInt(9), 8);
+            assertEquals(rs.getInt(10), 15);
+            assertEquals(rs.getString(11), "0000000006");
+            assertEquals(rs.getString(12), "Item T6");
+            assertEquals(rs.getString(13), "000000000000002");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "S1");
+            assertEquals(rs.getString(3), "888-888-1111");
+            assertEquals(rs.getString(4), "101 YYY Street");
+            assertEquals(rs.getString(5), "10001");
+            assertEquals(rs.getString(6), "0000000002");
+            assertEquals(rs.getString(7), "T2");
+            assertEquals(rs.getInt(8), 200);
+            assertEquals(rs.getInt(9), 5);
+            assertEquals(rs.getInt(10), 8);
+            assertEquals(rs.getString(11), "0000000001");
+            assertEquals(rs.getString(12), "Item T2");
+            assertEquals(rs.getString(13), "000000000000003");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "S6");
+            assertEquals(rs.getString(3), "888-888-6666");
+            assertEquals(rs.getString(4), "606 YYY Street");
+            assertEquals(rs.getString(5), "10006");
+            assertEquals(rs.getString(6), "0000000006");
+            assertEquals(rs.getString(7), "T6");
+            assertEquals(rs.getInt(8), 600);
+            assertEquals(rs.getInt(9), 8);
+            assertEquals(rs.getInt(10), 15);
+            assertEquals(rs.getString(11), "0000000006");
+            assertEquals(rs.getString(12), "Item T6");
+            assertEquals(rs.getString(13), "000000000000004");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "S2");
+            assertEquals(rs.getString(3), "888-888-2222");
+            assertEquals(rs.getString(4), "202 YYY Street");
+            assertEquals(rs.getString(5), "10002");
+            assertEquals(rs.getString(6), "0000000003");
+            assertEquals(rs.getString(7), "T3");
+            assertEquals(rs.getInt(8), 300);
+            assertEquals(rs.getInt(9), 8);
+            assertEquals(rs.getInt(10), 12);
+            assertEquals(rs.getString(11), "0000000002");
+            assertEquals(rs.getString(12), "Item T3");
+            assertEquals(rs.getString(13), "000000000000005");
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }        
+    }
+    
+    @Test
+    public void testJoinMultiJoinKeys() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String tableName3 = getTableName(conn, JOIN_CUSTOMER_TABLE_FULL_NAME);
+        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ c.name, s.name FROM " + tableName3 + " c LEFT JOIN " + tableName2 + " s ON \"customer_id\" = \"supplier_id\" AND c.loc_id = s.loc_id AND substr(s.name, 2, 1) = substr(c.name, 2, 1) ORDER BY \"customer_id\"";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "C1");
+            assertEquals(rs.getString(2), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "C2");
+            assertNull(rs.getString(2));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "C3");
+            assertEquals(rs.getString(2), "S3");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "C4");
+            assertNull(rs.getString(2));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "C5");
+            assertEquals(rs.getString(2), "S5");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "C6");
+            assertNull(rs.getString(2));
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testJoinWithDifferentNumericJoinKeyTypes() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", i.name, i.price, discount2, quantity FROM " + tableName4 + " o INNER JOIN " 
+            + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" AND o.price = (i.price * (100 - discount2)) / 100.0 WHERE quantity < 5000";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000004");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getInt(3), 600);
+            assertEquals(rs.getInt(4), 15);
+            assertEquals(rs.getInt(5), 4000);
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testJoinWithDifferentDateJoinKeyTypes() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName3 = getTableName(conn, JOIN_CUSTOMER_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", c.name, o.\"DATE\" FROM " + tableName4 + " o INNER JOIN "
+            + tableName3 + " c ON o.\"customer_id\" = c.\"customer_id\" AND o.\"DATE\" = c.\"DATE\" ORDER BY \"order_id\"";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000001");
+            assertEquals(rs.getString(2), "C4");
+            assertEquals(rs.getTimestamp(3), new Timestamp(format.parse("2013-11-22 14:22:56").getTime()));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000002");
+            assertEquals(rs.getString(2), "C3");
+            assertEquals(rs.getTimestamp(3), new Timestamp(format.parse("2013-11-25 10:06:29").getTime()));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000003");
+            assertEquals(rs.getString(2), "C2");
+            assertEquals(rs.getTimestamp(3), new Timestamp(format.parse("2013-11-25 16:45:07").getTime()));
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "000000000000005");
+            assertEquals(rs.getString(2), "C5");
+            assertEquals(rs.getTimestamp(3), new Timestamp(format.parse("2013-11-27 09:37:50").getTime()));
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testJoinWithIncomparableJoinKeyTypes() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String query = "SELECT /*+ USE_SORT_MERGE_JOIN*/ \"order_id\", i.name, i.price, discount2, quantity FROM " + tableName4 + " o INNER JOIN " 
+            + tableName1 + " i ON o.\"item_id\" = i.\"item_id\" AND o.price / 100 = substr(i.name, 2, 1)";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            statement.executeQuery();
+            fail("Should have got SQLException.");
+        } catch (SQLException e) {
+            assertEquals(e.getErrorCode(), SQLExceptionCode.TYPE_MISMATCH.getErrorCode());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testJoinPlanWithIndex() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String query1 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item LEFT JOIN " + tableName2 + " supp ON substr(item.name, 2, 1) = substr(supp.name, 2, 1) AND (supp.name BETWEEN 'S1' AND 'S5') WHERE item.name BETWEEN 'T1' AND 'T5' ORDER BY \"item_id\"";
+        String query2 = "SELECT /*+ USE_SORT_MERGE_JOIN*/ item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item INNER JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" WHERE (item.name = 'T1' OR item.name = 'T5') AND (supp.name = 'S1' OR supp.name = 'S5') ORDER BY \"item_id\"";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query1);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertEquals(rs.getString(3), "0000000002");
+            assertEquals(rs.getString(4), "S2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertEquals(rs.getString(3), "0000000003");
+            assertEquals(rs.getString(4), "S3");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000004");
+            assertEquals(rs.getString(4), "S4");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "S5");
+
+            assertFalse(rs.next());            
+            
+            statement = conn.prepareStatement(query2);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "S1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "S5");
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testJoinWithSkipMergeOptimization() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String tableName4 = getTableName(conn, JOIN_ORDER_TABLE_FULL_NAME);
+        String qu

<TRUNCATED>

[19/25] phoenix git commit: PHOENIX-4248 Breakup IndexExpressionIT into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/3d9adc6f/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexUsageIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexUsageIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexUsageIT.java
new file mode 100644
index 0000000..14b569a
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexUsageIT.java
@@ -0,0 +1,775 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.index;
+
+import static org.apache.phoenix.query.QueryConstants.MILLIS_IN_DAY;
+import static org.apache.phoenix.util.TestUtil.INDEX_DATA_SCHEMA;
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.apache.phoenix.end2end.ParallelStatsDisabledIT;
+import org.apache.phoenix.execute.CommitException;
+import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.util.DateUtil;
+import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.QueryUtil;
+import org.apache.phoenix.util.TestUtil;
+import org.junit.Test;
+
+public class IndexUsageIT extends ParallelStatsDisabledIT {
+
+    /**
+     * Adds a row to the index data table
+     * 
+     * @param i row number
+     */
+    private void insertRow(PreparedStatement stmt, int i) throws SQLException {
+        // insert row
+        stmt.setString(1, "varchar" + String.valueOf(i));
+        stmt.setString(2, "char" + String.valueOf(i));
+        stmt.setInt(3, i);
+        stmt.setLong(4, i);
+        stmt.setBigDecimal(5, new BigDecimal(i*0.5d));
+        Date date = new Date(DateUtil.parseDate("2015-01-01 00:00:00").getTime() + (i - 1) * MILLIS_IN_DAY);
+        stmt.setDate(6, date);
+        stmt.setString(7, "a.varchar" + String.valueOf(i));
+        stmt.setString(8, "a.char" + String.valueOf(i));
+        stmt.setInt(9, i);
+        stmt.setLong(10, i);
+        stmt.setBigDecimal(11, new BigDecimal(i*0.5d));
+        stmt.setDate(12, date);
+        stmt.setString(13, "b.varchar" + String.valueOf(i));
+        stmt.setString(14, "b.char" + String.valueOf(i));
+        stmt.setInt(15, i);
+        stmt.setLong(16, i);
+        stmt.setBigDecimal(17, new BigDecimal(i*0.5d));
+        stmt.setDate(18, date);
+        stmt.executeUpdate();
+    }
+
+    private void createDataTable(Connection conn, String dataTableName, String tableProps) throws SQLException {
+        String tableDDL = "create table " + dataTableName + TestUtil.TEST_TABLE_SCHEMA + tableProps;
+        conn.createStatement().execute(tableDDL);
+    }
+    
+    private void populateDataTable(Connection conn, String dataTable) throws SQLException {
+        String upsert = "UPSERT INTO " + dataTable
+                + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+        PreparedStatement stmt1 = conn.prepareStatement(upsert);
+        // insert two rows
+        insertRow(stmt1, 1);
+        insertRow(stmt1, 2);
+        conn.commit();
+    }
+
+    @Test
+    public void testGroupByCountImmutableIndex() throws Exception {
+        helpTestGroupByCount(false, false);
+    }
+
+    @Test
+    public void testGroupByCountImmutableLocalIndex() throws Exception {
+        helpTestGroupByCount(false, true);
+    }
+
+    @Test
+    public void testGroupByCountMutableIndex() throws Exception {
+        helpTestGroupByCount(true, false);
+    }
+
+    @Test
+    public void testGroupByCountMutableLocalIndex() throws Exception {
+        helpTestGroupByCount(true, true);
+    }
+
+    protected void helpTestGroupByCount(boolean mutable, boolean localIndex) throws Exception {
+        String dataTableName = generateUniqueName();
+        String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName;
+        String indexName = generateUniqueName();
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            conn.setAutoCommit(false);
+            createDataTable(conn, fullDataTableName, mutable ? "" : "IMMUTABLE_ROWS=true");
+            populateDataTable(conn, fullDataTableName);
+            String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullDataTableName
+                    + " (int_col1+int_col2)";
+            conn.createStatement().execute(ddl);
+
+            String groupBySql = "SELECT (int_col1+int_col2), COUNT(*) FROM " + fullDataTableName
+                    + " GROUP BY (int_col1+int_col2)";
+            ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + groupBySql);
+            String expectedPlan = "CLIENT PARALLEL 1-WAY "
+                    + (localIndex ? "RANGE SCAN OVER " + fullDataTableName + " [1]"
+                            : "FULL SCAN OVER INDEX_TEST." + indexName)
+                    + "\n    SERVER FILTER BY FIRST KEY ONLY\n    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [TO_BIGINT(\"(A.INT_COL1 + B.INT_COL2)\")]" 
+                    + (localIndex ? "\nCLIENT MERGE SORT" : "");
+            assertEquals(expectedPlan, QueryUtil.getExplainPlan(rs));
+            rs = conn.createStatement().executeQuery(groupBySql);
+            assertTrue(rs.next());
+            assertEquals(1, rs.getInt(2));
+            assertTrue(rs.next());
+            assertEquals(1, rs.getInt(2));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSelectDistinctImmutableIndex() throws Exception {
+        helpTestSelectDistinct(false, false);
+    }
+
+    @Test
+    public void testSelectDistinctImmutableIndexLocal() throws Exception {
+        helpTestSelectDistinct(false, true);
+    }
+
+    @Test
+    public void testSelectDistinctMutableIndex() throws Exception {
+        helpTestSelectDistinct(true, false);
+    }
+
+    @Test
+    public void testSelectDistinctMutableLocalIndex() throws Exception {
+        helpTestSelectDistinct(true, true);
+    }
+
+    protected void helpTestSelectDistinct(boolean mutable, boolean localIndex) throws Exception {
+        String dataTableName = generateUniqueName();
+        String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName;
+        String indexName = generateUniqueName();
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            conn.setAutoCommit(false);
+            createDataTable(conn, fullDataTableName, mutable ? "" : "IMMUTABLE_ROWS=true");
+            populateDataTable(conn, fullDataTableName);
+            String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullDataTableName
+                    + " (int_col1+1)";
+            conn.createStatement().execute(ddl);
+            String sql = "SELECT distinct int_col1+1 FROM " + fullDataTableName + " where int_col1+1 > 0";
+            ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + sql);
+            String expectedPlan = "CLIENT PARALLEL 1-WAY RANGE SCAN OVER "
+                    + (localIndex ? fullDataTableName + " [1,0] - [1,*]"
+                            : "INDEX_TEST." + indexName + " [0] - [*]")
+                    + "\n    SERVER FILTER BY FIRST KEY ONLY\n    SERVER DISTINCT PREFIX FILTER OVER [TO_BIGINT(\"(A.INT_COL1 + 1)\")]\n    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [TO_BIGINT(\"(A.INT_COL1 + 1)\")]"
+                    + (localIndex ? "\nCLIENT MERGE SORT" : "");
+            assertEquals(expectedPlan, QueryUtil.getExplainPlan(rs));
+            rs = conn.createStatement().executeQuery(sql);
+            assertTrue(rs.next());
+            assertEquals(2, rs.getInt(1));
+            assertTrue(rs.next());
+            assertEquals(3, rs.getInt(1));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testInClauseWithImmutableIndex() throws Exception {
+        helpTestInClauseWithIndex(false, false);
+    }
+
+    @Test
+    public void testInClauseWithImmutableLocalIndex() throws Exception {
+        helpTestInClauseWithIndex(false, true);
+    }
+
+    @Test
+    public void testInClauseWithMutableIndex() throws Exception {
+        helpTestInClauseWithIndex(true, false);
+    }
+
+    @Test
+    public void testInClauseWithMutableLocalIndex() throws Exception {
+        helpTestInClauseWithIndex(true, false);
+    }
+
+    protected void helpTestInClauseWithIndex(boolean mutable, boolean localIndex) throws Exception {
+        String dataTableName = generateUniqueName();
+        String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName;
+        String indexName = generateUniqueName();
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            conn.setAutoCommit(false);
+            createDataTable(conn, fullDataTableName, mutable ? "" : "IMMUTABLE_ROWS=true");
+            populateDataTable(conn, fullDataTableName);
+            String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullDataTableName
+                    + " (int_col1+1)";
+
+            conn.createStatement().execute(ddl);
+            String sql = "SELECT int_col1+1 FROM " + fullDataTableName + " where int_col1+1 IN (2)";
+            ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + sql);
+            assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER "
+                    + (localIndex ? fullDataTableName + " [1,2]\n    SERVER FILTER BY FIRST KEY ONLY\nCLIENT MERGE SORT"
+                            : "INDEX_TEST." + indexName + " [2]\n    SERVER FILTER BY FIRST KEY ONLY"), QueryUtil.getExplainPlan(rs));
+            rs = conn.createStatement().executeQuery(sql);
+            assertTrue(rs.next());
+            assertEquals(2, rs.getInt(1));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testOrderByWithImmutableIndex() throws Exception {
+        helpTestSelectAliasAndOrderByWithIndex(false, false);
+    }
+
+    @Test
+    public void testOrderByWithImmutableLocalIndex() throws Exception {
+        helpTestSelectAliasAndOrderByWithIndex(false, true);
+    }
+
+    @Test
+    public void testOrderByWithMutableIndex() throws Exception {
+        helpTestSelectAliasAndOrderByWithIndex(true, false);
+    }
+
+    @Test
+    public void testOrderByWithMutableLocalIndex() throws Exception {
+        helpTestSelectAliasAndOrderByWithIndex(true, false);
+    }
+
+    protected void helpTestSelectAliasAndOrderByWithIndex(boolean mutable, boolean localIndex) throws Exception {
+        String dataTableName = generateUniqueName();
+        String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName;
+        String indexName = generateUniqueName();
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            conn.setAutoCommit(false);
+            createDataTable(conn, fullDataTableName, mutable ? "" : "IMMUTABLE_ROWS=true");
+            populateDataTable(conn, fullDataTableName);
+            String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullDataTableName
+                    + " (int_col1+1)";
+
+            conn.createStatement().execute(ddl);
+            String sql = "SELECT int_col1+1 AS foo FROM " + fullDataTableName + " ORDER BY foo";
+            ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + sql);
+            assertEquals("CLIENT PARALLEL 1-WAY "
+                    + (localIndex ? "RANGE SCAN OVER " + fullDataTableName
+                            + " [1]\n    SERVER FILTER BY FIRST KEY ONLY\nCLIENT MERGE SORT"
+                            : "FULL SCAN OVER INDEX_TEST." + indexName + "\n    SERVER FILTER BY FIRST KEY ONLY"),
+                    QueryUtil.getExplainPlan(rs));
+            rs = conn.createStatement().executeQuery(sql);
+            assertTrue(rs.next());
+            assertEquals(2, rs.getInt(1));
+            assertTrue(rs.next());
+            assertEquals(3, rs.getInt(1));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testImmutableIndexWithCaseSensitiveCols() throws Exception {
+        helpTestIndexWithCaseSensitiveCols(false, false);
+    }
+    
+    @Test
+    public void testImmutableLocalIndexWithCaseSensitiveCols() throws Exception {
+        helpTestIndexWithCaseSensitiveCols(false, true);
+    }
+    
+    @Test
+    public void testMutableIndexWithCaseSensitiveCols() throws Exception {
+        helpTestIndexWithCaseSensitiveCols(true, false);
+    }
+    
+    @Test
+    public void testMutableLocalIndexWithCaseSensitiveCols() throws Exception {
+        helpTestIndexWithCaseSensitiveCols(true, true);
+    }
+    
+    protected void helpTestIndexWithCaseSensitiveCols(boolean mutable, boolean localIndex) throws Exception {
+        String dataTableName = generateUniqueName();
+        String indexName = generateUniqueName();
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            conn.createStatement().execute("CREATE TABLE " + dataTableName + " (k VARCHAR NOT NULL PRIMARY KEY, \"cf1\".\"V1\" VARCHAR, \"CF2\".\"v2\" VARCHAR) "+ (mutable ? "IMMUTABLE_ROWS=true" : ""));
+            String query = "SELECT * FROM " + dataTableName;
+            ResultSet rs = conn.createStatement().executeQuery(query);
+            assertFalse(rs.next());
+            String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + dataTableName + " (\"cf1\".\"V1\" || '_' || \"CF2\".\"v2\") INCLUDE (\"V1\",\"v2\")";
+            conn.createStatement().execute(ddl);
+            query = "SELECT * FROM " + indexName;
+            rs = conn.createStatement().executeQuery(query);
+            assertFalse(rs.next());
+
+            PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + dataTableName + " VALUES(?,?,?)");
+            stmt.setString(1,"a");
+            stmt.setString(2, "x");
+            stmt.setString(3, "1");
+            stmt.execute();
+            stmt.setString(1,"b");
+            stmt.setString(2, "y");
+            stmt.setString(3, "2");
+            stmt.execute();
+            conn.commit();
+
+            query = "SELECT (\"V1\" || '_' || \"v2\"), k, \"V1\", \"v2\"  FROM " + dataTableName + " WHERE (\"V1\" || '_' || \"v2\") = 'x_1'";
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            if(localIndex){
+                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + dataTableName + " [1,'x_1']\n"
+                           + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+            } else {
+                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + indexName + " ['x_1']", QueryUtil.getExplainPlan(rs));
+            }
+
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("x_1",rs.getString(1));
+            assertEquals("a",rs.getString(2));
+            assertEquals("x",rs.getString(3));
+            assertEquals("1",rs.getString(4));
+            //TODO figure out why this " " is needed
+            assertEquals("x_1",rs.getString("\"('cf1'.'V1' || '_' || 'CF2'.'v2')\""));
+            assertEquals("a",rs.getString("k"));
+            assertEquals("x",rs.getString("V1"));
+            assertEquals("1",rs.getString("v2"));
+            assertFalse(rs.next());
+
+            query = "SELECT \"V1\", \"V1\" as foo1, (\"V1\" || '_' || \"v2\") as foo, (\"V1\" || '_' || \"v2\") as \"Foo1\", (\"V1\" || '_' || \"v2\") FROM " + dataTableName + " ORDER BY foo";
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            if(localIndex){
+                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + dataTableName + " [1]\nCLIENT MERGE SORT",
+                    QueryUtil.getExplainPlan(rs));
+            } else {
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + indexName, QueryUtil.getExplainPlan(rs));
+            }
+
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("x",rs.getString(1));
+            assertEquals("x",rs.getString("V1"));
+            assertEquals("x",rs.getString(2));
+            assertEquals("x",rs.getString("foo1"));
+            assertEquals("x_1",rs.getString(3));
+            assertEquals("x_1",rs.getString("Foo"));
+            assertEquals("x_1",rs.getString(4));
+            assertEquals("x_1",rs.getString("Foo1"));
+            assertEquals("x_1",rs.getString(5));
+            assertEquals("x_1",rs.getString("\"('cf1'.'V1' || '_' || 'CF2'.'v2')\""));
+            assertTrue(rs.next());
+            assertEquals("y",rs.getString(1));
+            assertEquals("y",rs.getString("V1"));
+            assertEquals("y",rs.getString(2));
+            assertEquals("y",rs.getString("foo1"));
+            assertEquals("y_2",rs.getString(3));
+            assertEquals("y_2",rs.getString("Foo"));
+            assertEquals("y_2",rs.getString(4));
+            assertEquals("y_2",rs.getString("Foo1"));
+            assertEquals("y_2",rs.getString(5));
+            assertEquals("y_2",rs.getString("\"('cf1'.'V1' || '_' || 'CF2'.'v2')\""));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }    
+    
+    @Test
+    public void testSelectColOnlyInDataTableImmutableIndex() throws Exception {
+        helpTestSelectColOnlyInDataTable(false, false);
+    }
+
+    @Test
+    public void testSelectColOnlyInDataTableImmutableLocalIndex() throws Exception {
+        helpTestSelectColOnlyInDataTable(false, true);
+    }
+
+    @Test
+    public void testSelectColOnlyInDataTableMutableIndex() throws Exception {
+        helpTestSelectColOnlyInDataTable(true, false);
+    }
+
+    @Test
+    public void testSelectColOnlyInDataTableMutableLocalIndex() throws Exception {
+        helpTestSelectColOnlyInDataTable(true, true);
+    }
+
+    protected void helpTestSelectColOnlyInDataTable(boolean mutable, boolean localIndex) throws Exception {
+        String dataTableName = generateUniqueName();
+        String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName;
+        String indexName = generateUniqueName();
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            conn.setAutoCommit(false);
+            createDataTable(conn, fullDataTableName, mutable ? "" : "IMMUTABLE_ROWS=true");
+            populateDataTable(conn, fullDataTableName);
+            String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullDataTableName
+                    + " (int_col1+1)";
+
+            conn = DriverManager.getConnection(getUrl(), props);
+            conn.setAutoCommit(false);
+            conn.createStatement().execute(ddl);
+            String sql = "SELECT int_col1+1, int_col2 FROM " + fullDataTableName + " WHERE int_col1+1=2";
+            ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + sql);
+            assertEquals("CLIENT PARALLEL 1-WAY "
+                    + (localIndex ? "RANGE SCAN OVER " + fullDataTableName
+                            + " [1,2]\n    SERVER FILTER BY FIRST KEY ONLY\nCLIENT MERGE SORT" : "FULL SCAN OVER "
+                            + fullDataTableName + "\n    SERVER FILTER BY (A.INT_COL1 + 1) = 2"),
+                    QueryUtil.getExplainPlan(rs));
+            rs = conn.createStatement().executeQuery(sql);
+            assertTrue(rs.next());
+            assertEquals(2, rs.getInt(1));
+            assertEquals(1, rs.getInt(2));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+        
+    @Test
+    public void testUpdatableViewWithIndex() throws Exception {
+        helpTestUpdatableViewIndex(false);
+    }
+    
+    @Test
+    public void testUpdatableViewWithLocalIndex() throws Exception {
+        helpTestUpdatableViewIndex(true);
+    }
+       
+    private void helpTestUpdatableViewIndex(boolean local) throws Exception {
+    	Connection conn = DriverManager.getConnection(getUrl());
+        String dataTableName = generateUniqueName();
+        String indexName1 = generateUniqueName();
+        String viewName = generateUniqueName();
+        String indexName2 = generateUniqueName();
+    	try {
+	        String ddl = "CREATE TABLE " + dataTableName + " (k1 INTEGER NOT NULL, k2 INTEGER NOT NULL, k3 DECIMAL, s1 VARCHAR, s2 VARCHAR CONSTRAINT pk PRIMARY KEY (k1, k2, k3))";
+	        conn.createStatement().execute(ddl);
+	        ddl = "CREATE VIEW " + viewName + " AS SELECT * FROM " + dataTableName + " WHERE k1 = 1";
+	        conn.createStatement().execute(ddl);
+	        conn.createStatement().execute("UPSERT INTO " + viewName + "(k2,s1,s2,k3) VALUES(120,'foo0','bar0',50.0)");
+	        conn.createStatement().execute("UPSERT INTO " + viewName + "(k2,s1,s2,k3) VALUES(121,'foo1','bar1',51.0)");
+	        conn.commit();
+	        
+	        ResultSet rs;
+	        conn.createStatement().execute("CREATE " + (local ? "LOCAL" : "") + " INDEX " + indexName1 + " on " + viewName + "(k1+k2+k3) include (s1, s2)");
+	        conn.createStatement().execute("UPSERT INTO " + viewName + "(k2,s1,s2,k3) VALUES(120,'foo2','bar2',50.0)");
+	        conn.commit();
+	
+	        String query = "SELECT k1, k2, k3, s1, s2 FROM " + viewName + " WHERE 	k1+k2+k3 = 173.0";
+	        rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+	        String queryPlan = QueryUtil.getExplainPlan(rs);
+	        if (local) {
+	            assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + dataTableName + " [1,173]\n" + "CLIENT MERGE SORT",
+	                    queryPlan);
+	        } else {
+	            assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER _IDX_" + dataTableName + " [" + Short.MIN_VALUE + ",173]", queryPlan);
+	        }
+	        rs = conn.createStatement().executeQuery(query);
+	        assertTrue(rs.next());
+	        assertEquals(1, rs.getInt(1));
+	        assertEquals(121, rs.getInt(2));
+	        assertTrue(BigDecimal.valueOf(51.0).compareTo(rs.getBigDecimal(3))==0);
+	        assertEquals("foo1", rs.getString(4));
+	        assertEquals("bar1", rs.getString(5));
+	        assertFalse(rs.next());
+	
+	        conn.createStatement().execute("CREATE " + (local ? "LOCAL" : "") + " INDEX " + indexName2 + " on " + viewName + "(s1||'_'||s2)");
+	        
+	        query = "SELECT k1, k2, s1||'_'||s2 FROM " + viewName + " WHERE (s1||'_'||s2)='foo2_bar2'";
+	        rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+	        if (local) {
+	            assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + dataTableName + " [" + (2)
+	                    + ",'foo2_bar2']\n" + "    SERVER FILTER BY FIRST KEY ONLY\n" + "CLIENT MERGE SORT",
+	                    QueryUtil.getExplainPlan(rs));
+	        } else {
+	            assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER _IDX_" + dataTableName + " [" + (Short.MIN_VALUE + 1) + ",'foo2_bar2']\n"
+	                    + "    SERVER FILTER BY FIRST KEY ONLY", QueryUtil.getExplainPlan(rs));
+	        }
+	        rs = conn.createStatement().executeQuery(query);
+	        assertTrue(rs.next());
+	        assertEquals(1, rs.getInt(1));
+	        assertEquals(120, rs.getInt(2));
+	        assertEquals("foo2_bar2", rs.getString(3));
+	        assertFalse(rs.next());
+    	}
+        finally {
+        	conn.close();
+        }
+    }
+    
+    @Test
+    public void testViewUsesMutableTableIndex() throws Exception {
+        helpTestViewUsesTableIndex(false);
+    }
+    
+    @Test
+    public void testViewUsesImmutableTableIndex() throws Exception {
+        helpTestViewUsesTableIndex(true);
+    }
+    
+    private void helpTestViewUsesTableIndex(boolean immutable) throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        try 
+        {
+            String dataTableName = generateUniqueName();
+            String indexName1 = generateUniqueName();
+            String viewName = generateUniqueName();
+            String indexName2 = generateUniqueName();
+        	ResultSet rs;
+	        String ddl = "CREATE TABLE " + dataTableName + " (k1 INTEGER NOT NULL, k2 INTEGER NOT NULL, s1 VARCHAR, s2 VARCHAR, s3 VARCHAR, s4 VARCHAR CONSTRAINT pk PRIMARY KEY (k1, k2)) " + (immutable ? "IMMUTABLE_ROWS = true" : "");
+	        conn.createStatement().execute(ddl);
+	        conn.createStatement().execute("CREATE INDEX " + indexName1 + " ON " + dataTableName + "(k2, s2, s3, s1)");
+	        conn.createStatement().execute("CREATE INDEX " + indexName2 + " ON " + dataTableName + "(k2, s2||'_'||s3, s1, s4)");
+	        
+	        ddl = "CREATE VIEW " + viewName + " AS SELECT * FROM " + dataTableName + " WHERE s1 = 'foo'";
+	        conn.createStatement().execute(ddl);
+	        conn.createStatement().execute("UPSERT INTO " + dataTableName + " VALUES(1,1,'foo','abc','cab')");
+	        conn.createStatement().execute("UPSERT INTO " + dataTableName + " VALUES(2,2,'bar','xyz','zyx')");
+	        conn.commit();
+	        
+	        rs = conn.createStatement().executeQuery("SELECT count(*) FROM " + viewName);
+	        assertTrue(rs.next());
+	        assertEquals(1, rs.getLong(1));
+	        assertFalse(rs.next());
+	        
+	        //i2 should be used since it contains s3||'_'||s4 i
+	        String query = "SELECT s2||'_'||s3 FROM " + viewName + " WHERE k2=1 AND (s2||'_'||s3)='abc_cab'";
+	        rs = conn.createStatement(  ).executeQuery("EXPLAIN " + query);
+	        String queryPlan = QueryUtil.getExplainPlan(rs);
+	        assertEquals(
+	                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + indexName2 + " [1,'abc_cab','foo']\n" +
+	                "    SERVER FILTER BY FIRST KEY ONLY", queryPlan);
+	        rs = conn.createStatement().executeQuery(query);
+	        assertTrue(rs.next());
+	        assertEquals("abc_cab", rs.getString(1));
+	        assertFalse(rs.next());
+	        
+	        conn.createStatement().execute("ALTER VIEW " + viewName + " DROP COLUMN s4");
+	        //i2 cannot be used since s4 has been dropped from the view, so i1 will be used 
+	        rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+	        queryPlan = QueryUtil.getExplainPlan(rs);
+	        assertEquals(
+	                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + indexName1 + " [1]\n" +
+	                "    SERVER FILTER BY FIRST KEY ONLY AND ((\"S2\" || '_' || \"S3\") = 'abc_cab' AND \"S1\" = 'foo')", queryPlan);
+	        rs = conn.createStatement().executeQuery(query);
+	        assertTrue(rs.next());
+	        assertEquals("abc_cab", rs.getString(1));
+	        assertFalse(rs.next());    
+        }
+        finally {
+        	conn.close();
+        }
+    }
+    
+	@Test
+	public void testExpressionThrowsException() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        String dataTableName = generateUniqueName();
+        String indexName = generateUniqueName();
+		try {
+			String ddl = "CREATE TABLE " + dataTableName + " (k1 INTEGER PRIMARY KEY, k2 INTEGER)";
+			conn.createStatement().execute(ddl);
+			ddl = "CREATE INDEX " + indexName + " on " + dataTableName + "(k1/k2)";
+			conn.createStatement().execute(ddl);
+			// upsert should succeed
+			conn.createStatement().execute("UPSERT INTO " + dataTableName + " VALUES(1,1)");
+			conn.commit();
+			// divide by zero should fail
+			conn.createStatement().execute("UPSERT INTO " + dataTableName + " VALUES(1,0)");
+			conn.commit();
+			fail();
+		} catch (CommitException e) {
+		} finally {
+			conn.close();
+		}
+	}
+	
+	@Test
+	public void testImmutableCaseSensitiveFunctionIndex() throws Exception {
+		helpTestCaseSensitiveFunctionIndex(false, false);
+	}
+
+	@Test
+	public void testImmutableLocalCaseSensitiveFunctionIndex() throws Exception {
+		helpTestCaseSensitiveFunctionIndex(false, true);
+	}
+
+	@Test
+	public void testMutableCaseSensitiveFunctionIndex() throws Exception {
+		helpTestCaseSensitiveFunctionIndex(true, false);
+	}
+
+	@Test
+	public void testMutableLocalCaseSensitiveFunctionIndex() throws Exception {
+		helpTestCaseSensitiveFunctionIndex(true, true);
+	}
+
+	protected void helpTestCaseSensitiveFunctionIndex(boolean mutable,
+			boolean localIndex) throws Exception {
+		Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+		Connection conn = DriverManager.getConnection(getUrl(), props);
+        String dataTableName = generateUniqueName();
+        String indexName = generateUniqueName();
+		try {
+			conn.createStatement().execute(
+					"CREATE TABLE " + dataTableName + " (k VARCHAR NOT NULL PRIMARY KEY, v VARCHAR) "
+							+ (!mutable ? "IMMUTABLE_ROWS=true" : ""));
+			String query = "SELECT * FROM  " + dataTableName;
+			ResultSet rs = conn.createStatement().executeQuery(query);
+			assertFalse(rs.next());
+			String ddl = "CREATE " + (localIndex ? "LOCAL" : "")
+					+ " INDEX " + indexName + " ON " + dataTableName + " (REGEXP_SUBSTR(v,'id:\\\\w+'))";
+			conn.createStatement().execute(ddl);
+			query = "SELECT * FROM " + indexName;
+			rs = conn.createStatement().executeQuery(query);
+			assertFalse(rs.next());
+
+			PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + dataTableName + " VALUES(?,?)");
+			stmt.setString(1, "k1");
+			stmt.setString(2, "{id:id1}");
+			stmt.execute();
+			stmt.setString(1, "k2");
+			stmt.setString(2, "{id:id2}");
+			stmt.execute();
+			conn.commit();
+			
+			query = "SELECT k FROM " + dataTableName + " WHERE REGEXP_SUBSTR(v,'id:\\\\w+') = 'id:id1'";
+			rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+			if (localIndex) {
+				assertEquals(
+						"CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + dataTableName + " [1,'id:id1']\n"
+								+ "    SERVER FILTER BY FIRST KEY ONLY\nCLIENT MERGE SORT",
+						QueryUtil.getExplainPlan(rs));
+			} else {
+				assertEquals(
+						"CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + indexName + " ['id:id1']\n"
+								+ "    SERVER FILTER BY FIRST KEY ONLY",
+						QueryUtil.getExplainPlan(rs));
+			}
+
+			rs = conn.createStatement().executeQuery(query);
+			assertTrue(rs.next());
+			assertEquals("k1", rs.getString(1));
+			assertFalse(rs.next());
+		} finally {
+			conn.close();
+		}
+	}
+
+	@Test
+    public void testImmutableTableGlobalIndexExpressionWithJoin() throws Exception {
+        helpTestIndexExpressionWithJoin(false, false);
+    }
+	
+	@Test
+    public void testImmutableTableLocalIndexExpressionWithJoin() throws Exception {
+        helpTestIndexExpressionWithJoin(false, true);
+    }
+	
+	@Test
+    public void testMutableTableGlobalIndexExpressionWithJoin() throws Exception {
+        helpTestIndexExpressionWithJoin(true, false);
+    }
+	
+	@Test
+    public void testMutableTableLocalIndexExpressionWithJoin() throws Exception {
+	    helpTestIndexExpressionWithJoin(true, true);
+    }
+
+    public void helpTestIndexExpressionWithJoin(boolean mutable,
+            boolean localIndex) throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String nameSuffix = "T" + (mutable ? "MUTABLE" : "_IMMUTABLE") + (localIndex ? "_LOCAL" : "_GLOBAL");
+        String tableName = "T" + nameSuffix;
+        String indexName = "IDX" + nameSuffix;
+        try {
+            conn.createStatement().execute(
+                        "CREATE TABLE "
+                                + tableName
+                                + "( c_customer_sk varchar primary key, c_first_name varchar, c_last_name varchar )"
+                                + (!mutable ? "IMMUTABLE_ROWS=true" : ""));
+            String query = "SELECT * FROM " + tableName;
+            ResultSet rs = conn.createStatement().executeQuery(query);
+            assertFalse(rs.next());
+            
+            conn.createStatement().execute(
+                "CREATE " + (localIndex ? "LOCAL" : "")
+                + " INDEX " + indexName + " ON " + tableName + " (c_customer_sk || c_first_name asc)");
+            query = "SELECT * FROM " + indexName;
+            rs = conn.createStatement().executeQuery(query);
+            assertFalse(rs.next());
+            
+            PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + tableName + " VALUES(?,?,?)");
+            stmt.setString(1, "1");
+            stmt.setString(2, "David");
+            stmt.setString(3, "Smith");
+            stmt.execute();
+            conn.commit();
+            
+            query = "select c.c_customer_sk from  " + tableName + " c "
+                    + "left outer join " + tableName + " c2 on c.c_customer_sk = c2.c_customer_sk "
+                    + "where c.c_customer_sk || c.c_first_name = '1David'";
+            rs = conn.createStatement().executeQuery("EXPLAIN "+query);
+            String explainPlan = QueryUtil.getExplainPlan(rs);
+            if (localIndex) {
+            	assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + tableName + " [1,'1David']\n" + 
+                        "    SERVER FILTER BY FIRST KEY ONLY\n" + 
+                        "CLIENT MERGE SORT\n" +
+                        "    PARALLEL LEFT-JOIN TABLE 0 (SKIP MERGE)\n" +
+                        "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + tableName + " [1]\n" + 
+                        "            SERVER FILTER BY FIRST KEY ONLY\n" + 
+                        "        CLIENT MERGE SORT", explainPlan);
+            }
+            else {
+            	assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + indexName + " ['1David']\n" + 
+                        "    SERVER FILTER BY FIRST KEY ONLY\n" + 
+                        "    PARALLEL LEFT-JOIN TABLE 0 (SKIP MERGE)\n" +
+                        "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + indexName + "\n" + 
+                        "            SERVER FILTER BY FIRST KEY ONLY", explainPlan);
+            }
+            
+            rs = conn.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("1", rs.getString(1));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3d9adc6f/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexWithTableSchemaChangeIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexWithTableSchemaChangeIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexWithTableSchemaChangeIT.java
new file mode 100644
index 0000000..659866b
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexWithTableSchemaChangeIT.java
@@ -0,0 +1,375 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.index;
+
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.apache.phoenix.end2end.ParallelStatsDisabledIT;
+import org.apache.phoenix.util.IndexUtil;
+import org.apache.phoenix.util.PropertiesUtil;
+import org.junit.Test;
+
+public class IndexWithTableSchemaChangeIT extends ParallelStatsDisabledIT {
+
+    @Test
+    public void testImmutableIndexDropIndexedColumn() throws Exception {
+        helpTestDropIndexedColumn(false, false);
+    }
+    
+    @Test
+    public void testImmutableLocalIndexDropIndexedColumn() throws Exception {
+        helpTestDropIndexedColumn(false, true);
+    }
+    
+    @Test
+    public void testMutableIndexDropIndexedColumn() throws Exception {
+        helpTestDropIndexedColumn(true, false);
+    }
+    
+    @Test
+    public void testMutableLocalIndexDropIndexedColumn() throws Exception {
+        helpTestDropIndexedColumn(true, true);
+    }
+    
+    public void helpTestDropIndexedColumn(boolean mutable, boolean local) throws Exception {
+        String query;
+        ResultSet rs;
+        PreparedStatement stmt;
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+
+        String dataTableName = generateUniqueName();
+        String indexName = generateUniqueName();
+
+        try {
+	        conn.setAutoCommit(false);
+	
+	        // make sure that the tables are empty, but reachable
+            conn.createStatement().execute(
+                "CREATE TABLE " + dataTableName
+                        + " (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR)"
+                        + (!mutable ? " IMMUTABLE_ROWS=true" : ""));
+	        query = "SELECT * FROM " + dataTableName ;
+	        rs = conn.createStatement().executeQuery(query);
+	        assertFalse(rs.next());
+	        conn.createStatement().execute("CREATE " + ( local ? "LOCAL" : "") + " INDEX " + indexName + " ON " + dataTableName + " (v1 || '_' || v2)");
+	
+	        query = "SELECT * FROM " + dataTableName;
+	        rs = conn.createStatement().executeQuery(query);
+	        assertFalse(rs.next());
+	
+	        // load some data into the table
+	        stmt = conn.prepareStatement("UPSERT INTO " + dataTableName + " VALUES(?,?,?)");
+	        stmt.setString(1, "a");
+	        stmt.setString(2, "x");
+	        stmt.setString(3, "1");
+	        stmt.execute();
+	        conn.commit();
+	
+	        assertIndexExists(conn, dataTableName, true);
+	        conn.createStatement().execute("ALTER TABLE " + dataTableName + " DROP COLUMN v1");
+	        assertIndexExists(conn, dataTableName, false);
+	
+	        query = "SELECT * FROM " + dataTableName;
+	        rs = conn.createStatement().executeQuery(query);
+	        assertTrue(rs.next());
+	        assertEquals("a",rs.getString(1));
+	        assertEquals("1",rs.getString(2));
+	        assertFalse(rs.next());
+	
+	        // load some data into the table
+	        stmt = conn.prepareStatement("UPSERT INTO " + dataTableName + " VALUES(?,?)");
+	        stmt.setString(1, "a");
+	        stmt.setString(2, "2");
+	        stmt.execute();
+	        conn.commit();
+	
+	        query = "SELECT * FROM " + dataTableName;
+	        rs = conn.createStatement().executeQuery(query);
+	        assertTrue(rs.next());
+	        assertEquals("a",rs.getString(1));
+	        assertEquals("2",rs.getString(2));
+	        assertFalse(rs.next());
+        }
+        finally {
+        	conn.close();
+        }
+    }
+    
+    private static void assertIndexExists(Connection conn, String tableName, boolean exists) throws SQLException {
+        ResultSet rs = conn.getMetaData().getIndexInfo(null, null, tableName, false, false);
+        assertEquals(exists, rs.next());
+    }
+    
+    @Test
+    public void testImmutableIndexDropCoveredColumn() throws Exception {
+    	helpTestDropCoveredColumn(false, false);
+    }
+    
+    @Test
+    public void testImmutableLocalIndexDropCoveredColumn() throws Exception {
+    	helpTestDropCoveredColumn(false, true);
+    }
+    
+    @Test
+    public void testMutableIndexDropCoveredColumn() throws Exception {
+    	helpTestDropCoveredColumn(true, false);
+    }
+    
+    @Test
+    public void testMutableLocalIndexDropCoveredColumn() throws Exception {
+    	helpTestDropCoveredColumn(true, true);
+    }
+    
+    public void helpTestDropCoveredColumn(boolean mutable, boolean local) throws Exception {
+        ResultSet rs;
+        PreparedStatement stmt;
+        String dataTableName = generateUniqueName();
+        String indexName = generateUniqueName();
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+	        conn.setAutoCommit(false);
+	
+	        // make sure that the tables are empty, but reachable
+	        conn.createStatement().execute(
+	          "CREATE TABLE " + dataTableName
+	              + " (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR, v3 VARCHAR)");
+	        String dataTableQuery = "SELECT * FROM " + dataTableName;
+	        rs = conn.createStatement().executeQuery(dataTableQuery);
+	        assertFalse(rs.next());
+	
+	        conn.createStatement().execute("CREATE " + ( local ? "LOCAL" : "") + " INDEX " + indexName + " ON " + dataTableName + " (k || '_' || v1) include (v2, v3)");
+	        String indexTableQuery = "SELECT * FROM " + indexName;
+	        rs = conn.createStatement().executeQuery(indexTableQuery);
+	        assertFalse(rs.next());
+	
+	        // load some data into the table
+	        stmt = conn.prepareStatement("UPSERT INTO " + dataTableName + " VALUES(?,?,?,?)");
+	        stmt.setString(1, "a");
+	        stmt.setString(2, "x");
+	        stmt.setString(3, "1");
+	        stmt.setString(4, "j");
+	        stmt.execute();
+	        conn.commit();
+	
+	        assertIndexExists(conn, dataTableName, true);
+	        conn.createStatement().execute("ALTER TABLE " + dataTableName + " DROP COLUMN v2");
+	        assertIndexExists(conn, dataTableName, true);
+	
+	        // verify data table rows
+	        rs = conn.createStatement().executeQuery(dataTableQuery);
+	        assertTrue(rs.next());
+	        assertEquals("a",rs.getString(1));
+	        assertEquals("x",rs.getString(2));
+	        assertEquals("j",rs.getString(3));
+	        assertFalse(rs.next());
+	        
+	        // verify index table rows
+	        rs = conn.createStatement().executeQuery(indexTableQuery);
+	        assertTrue(rs.next());
+	        assertEquals("a_x",rs.getString(1));
+	        assertEquals("a",rs.getString(2));
+	        assertEquals("j",rs.getString(3));
+	        assertFalse(rs.next());
+	
+	        // add another row
+	        stmt = conn.prepareStatement("UPSERT INTO " + dataTableName + " VALUES(?,?,?)");
+	        stmt.setString(1, "b");
+	        stmt.setString(2, "y");
+	        stmt.setString(3, "k");
+	        stmt.execute();
+	        conn.commit();
+	
+	        // verify data table rows
+	        rs = conn.createStatement().executeQuery(dataTableQuery);
+	        assertTrue(rs.next());
+	        assertEquals("a",rs.getString(1));
+	        assertEquals("x",rs.getString(2));
+	        assertEquals("j",rs.getString(3));
+	        assertTrue(rs.next());
+	        assertEquals("b",rs.getString(1));
+	        assertEquals("y",rs.getString(2));
+	        assertEquals("k",rs.getString(3));
+	        assertFalse(rs.next());
+	        
+	        // verify index table rows
+	        rs = conn.createStatement().executeQuery(indexTableQuery);
+	        assertTrue(rs.next());
+	        assertEquals("a_x",rs.getString(1));
+	        assertEquals("a",rs.getString(2));
+	        assertEquals("j",rs.getString(3));
+	        assertTrue(rs.next());
+	        assertEquals("b_y",rs.getString(1));
+	        assertEquals("b",rs.getString(2));
+	        assertEquals("k",rs.getString(3));
+	        assertFalse(rs.next());
+        }
+        finally {
+        	conn.close();
+        }
+    }
+    
+    @Test
+    public void testImmutableIndexAddPKColumnToTable() throws Exception {
+    	helpTestAddPKColumnToTable(false, false);
+    }
+    
+    @Test
+    public void testImmutableLocalIndexAddPKColumnToTable() throws Exception {
+    	helpTestAddPKColumnToTable(false, true);
+    }
+    
+    @Test
+    public void testMutableIndexAddPKColumnToTable() throws Exception {
+    	helpTestAddPKColumnToTable(true, false);
+    }
+    
+    @Test
+    public void testMutableLocalIndexAddPKColumnToTable() throws Exception {
+    	helpTestAddPKColumnToTable(true, true);
+    }
+    
+    public void helpTestAddPKColumnToTable(boolean mutable, boolean local) throws Exception {
+        ResultSet rs;
+        PreparedStatement stmt;
+
+        String dataTableName = generateUniqueName();
+        String indexName = generateUniqueName();
+
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+	        conn.setAutoCommit(false);
+	
+	        // make sure that the tables are empty, but reachable
+	        conn.createStatement().execute(
+	          "CREATE TABLE "  + dataTableName
+	              + " (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR)");
+	        String dataTableQuery = "SELECT * FROM " + dataTableName;
+	        rs = conn.createStatement().executeQuery(dataTableQuery);
+	        assertFalse(rs.next());
+	
+	        conn.createStatement().execute("CREATE " + ( local ? "LOCAL" : "") + " INDEX " + indexName + " ON " + dataTableName + " (v1 || '_' || v2)");
+	        String indexTableQuery = "SELECT * FROM " + indexName;
+	        rs = conn.createStatement().executeQuery(indexTableQuery);
+	        assertFalse(rs.next());
+	
+	        // load some data into the table
+	        stmt = conn.prepareStatement("UPSERT INTO " + dataTableName + " VALUES(?,?,?)");
+	        stmt.setString(1, "a");
+	        stmt.setString(2, "x");
+	        stmt.setString(3, "1");
+	        stmt.execute();
+	        conn.commit();
+	
+	        assertIndexExists(conn, dataTableName, true);
+	        conn.createStatement().execute("ALTER TABLE " + dataTableName + " ADD v3 VARCHAR, k2 DECIMAL PRIMARY KEY");
+	        rs = conn.getMetaData().getPrimaryKeys("", "", dataTableName);
+	        assertTrue(rs.next());
+	        assertEquals("K",rs.getString("COLUMN_NAME"));
+	        assertEquals(1, rs.getShort("KEY_SEQ"));
+	        assertTrue(rs.next());
+	        assertEquals("K2",rs.getString("COLUMN_NAME"));
+	        assertEquals(2, rs.getShort("KEY_SEQ"));
+	
+	        rs = conn.getMetaData().getPrimaryKeys("", "", indexName);
+	        assertTrue(rs.next());
+	        assertEquals(IndexUtil.INDEX_COLUMN_NAME_SEP + "(V1 || '_' || V2)",rs.getString("COLUMN_NAME"));
+	        int offset = local ? 1 : 0;
+	        assertEquals(offset+1, rs.getShort("KEY_SEQ"));
+	        assertTrue(rs.next());
+	        assertEquals(IndexUtil.INDEX_COLUMN_NAME_SEP + "K",rs.getString("COLUMN_NAME"));
+	        assertEquals(offset+2, rs.getShort("KEY_SEQ"));
+	        assertTrue(rs.next());
+	        assertEquals(IndexUtil.INDEX_COLUMN_NAME_SEP + "K2",rs.getString("COLUMN_NAME"));
+	        assertEquals(offset+3, rs.getShort("KEY_SEQ"));
+	
+	        // verify data table rows
+	        rs = conn.createStatement().executeQuery(dataTableQuery);
+	        assertTrue(rs.next());
+	        assertEquals("a",rs.getString(1));
+	        assertEquals("x",rs.getString(2));
+	        assertEquals("1",rs.getString(3));
+	        assertNull(rs.getBigDecimal(4));
+	        assertFalse(rs.next());
+	        
+	        // verify index table rows
+	        rs = conn.createStatement().executeQuery(indexTableQuery);
+	        assertTrue(rs.next());
+	        assertEquals("x_1",rs.getString(1));
+	        assertEquals("a",rs.getString(2));
+	        assertNull(rs.getBigDecimal(3));
+	        assertFalse(rs.next());
+	
+	        // load some data into the table
+	        stmt = conn.prepareStatement("UPSERT INTO " + dataTableName + "(K,K2,V1,V2) VALUES(?,?,?,?)");
+	        stmt.setString(1, "b");
+	        stmt.setBigDecimal(2, BigDecimal.valueOf(2));
+	        stmt.setString(3, "y");
+	        stmt.setString(4, "2");
+	        stmt.execute();
+	        conn.commit();
+	
+	        // verify data table rows
+	        rs = conn.createStatement().executeQuery(dataTableQuery);
+	        assertTrue(rs.next());
+	        assertEquals("a",rs.getString(1));
+	        assertEquals("x",rs.getString(2));
+	        assertEquals("1",rs.getString(3));
+	        assertNull(rs.getString(4));
+	        assertNull(rs.getBigDecimal(5));
+	        assertTrue(rs.next());
+	        assertEquals("b",rs.getString(1));
+	        assertEquals("y",rs.getString(2));
+	        assertEquals("2",rs.getString(3));
+	        assertNull(rs.getString(4));
+	        assertEquals(BigDecimal.valueOf(2),rs.getBigDecimal(5));
+	        assertFalse(rs.next());
+	        
+	        // verify index table rows
+	        rs = conn.createStatement().executeQuery(indexTableQuery);
+	        assertTrue(rs.next());
+	        assertEquals("x_1",rs.getString(1));
+	        assertEquals("a",rs.getString(2));
+	        assertNull(rs.getBigDecimal(3));
+	        assertTrue(rs.next());
+	        assertEquals("y_2",rs.getString(1));
+	        assertEquals("b",rs.getString(2));
+	        assertEquals(BigDecimal.valueOf(2),rs.getBigDecimal(3));
+	        assertFalse(rs.next());
+        }
+        finally {
+        	conn.close();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3d9adc6f/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalImmutableNonTxIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalImmutableNonTxIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalImmutableNonTxIndexIT.java
index f2d18d2..7b7c6ad 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalImmutableNonTxIndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalImmutableNonTxIndexIT.java
@@ -22,7 +22,7 @@ import java.util.Collection;
 
 import org.junit.runners.Parameterized.Parameters;
 
-public class LocalImmutableNonTxIndexIT extends IndexIT {
+public class LocalImmutableNonTxIndexIT extends BaseIndexIT {
 
     public LocalImmutableNonTxIndexIT(boolean localIndex, boolean mutable, boolean transactional, boolean columnEncoded) {
         super(localIndex, mutable, transactional, columnEncoded);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3d9adc6f/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalImmutableTxIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalImmutableTxIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalImmutableTxIndexIT.java
index 9e6fd5f..5ff6d04 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalImmutableTxIndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalImmutableTxIndexIT.java
@@ -22,7 +22,7 @@ import java.util.Collection;
 
 import org.junit.runners.Parameterized.Parameters;
 
-public class LocalImmutableTxIndexIT extends IndexIT {
+public class LocalImmutableTxIndexIT extends BaseIndexIT {
 
     public LocalImmutableTxIndexIT(boolean localIndex, boolean mutable, boolean transactional, boolean columnEncoded) {
         super(localIndex, mutable, transactional, columnEncoded);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3d9adc6f/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalMutableNonTxIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalMutableNonTxIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalMutableNonTxIndexIT.java
index 9785d20..4b9688b 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalMutableNonTxIndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalMutableNonTxIndexIT.java
@@ -22,7 +22,7 @@ import java.util.Collection;
 
 import org.junit.runners.Parameterized.Parameters;
 
-public class LocalMutableNonTxIndexIT extends IndexIT {
+public class LocalMutableNonTxIndexIT extends BaseIndexIT {
 
     public LocalMutableNonTxIndexIT(boolean localIndex, boolean mutable, boolean transactional, boolean columnEncoded) {
         super(localIndex, mutable, transactional, columnEncoded);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3d9adc6f/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalMutableTxIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalMutableTxIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalMutableTxIndexIT.java
index c09a8c0..5f5dd0f 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalMutableTxIndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalMutableTxIndexIT.java
@@ -22,7 +22,7 @@ import java.util.Collection;
 
 import org.junit.runners.Parameterized.Parameters;
 
-public class LocalMutableTxIndexIT extends IndexIT {
+public class LocalMutableTxIndexIT extends BaseIndexIT {
 
     public LocalMutableTxIndexIT(boolean localIndex, boolean mutable, boolean transactional, boolean columnEncoded) {
         super(localIndex, mutable, transactional, columnEncoded);


[12/25] phoenix git commit: PHOENIX-4246 Breakup join related tests into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/ee20a8c9/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java
deleted file mode 100644
index edab319..0000000
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java
+++ /dev/null
@@ -1,3448 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.phoenix.end2end;
-
-import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.io.IOException;
-import java.sql.Connection;
-import java.sql.Date;
-import java.sql.DriverManager;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Random;
-
-import org.apache.hadoop.hbase.HConstants;
-import org.apache.hadoop.hbase.client.Scan;
-import org.apache.hadoop.hbase.coprocessor.ObserverContext;
-import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
-import org.apache.hadoop.hbase.coprocessor.SimpleRegionObserver;
-import org.apache.hadoop.hbase.regionserver.RegionScanner;
-import org.apache.phoenix.cache.GlobalCache;
-import org.apache.phoenix.cache.ServerCacheClient;
-import org.apache.phoenix.cache.TenantCache;
-import org.apache.phoenix.exception.SQLExceptionCode;
-import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
-import org.apache.phoenix.join.HashJoinInfo;
-import org.apache.phoenix.query.QueryServices;
-import org.apache.phoenix.util.ByteUtil;
-import org.apache.phoenix.util.PropertiesUtil;
-import org.apache.phoenix.util.QueryUtil;
-import org.apache.phoenix.util.ReadOnlyProps;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-
-@RunWith(Parameterized.class)
-public class HashJoinIT extends BaseJoinIT {
-    
-    
-    public HashJoinIT(String[] indexDDL, String[] plans) {
-        super(indexDDL, plans);
-    }
-    
-    @Parameters
-    public static Collection<Object> data() {
-        List<Object> testCases = Lists.newArrayList();
-        testCases.add(new String[][] {
-                {}, {
-                /* 
-                 * testLeftJoinWithAggregation()
-                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
-                 *     LEFT JOIN joinItemTable i ON o.item_id = i.item_id 
-                 *     GROUP BY i.name ORDER BY i.name
-                 */     
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.NAME]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME,
-                /* 
-                 * testLeftJoinWithAggregation()
-                 *     SELECT i.item_id iid, sum(quantity) q FROM joinOrderTable o 
-                 *     LEFT JOIN joinItemTable i ON o.item_id = i.item_id 
-                 *     GROUP BY i.item_id ORDER BY q DESC"
-                 */     
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [\"I.item_id\"]\n" +
-                "CLIENT MERGE SORT\n" +
-                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY",
-                /* 
-                 * testLeftJoinWithAggregation()
-                 *     SELECT i.item_id iid, sum(quantity) q FROM joinItemTable i 
-                 *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id 
-                 *     GROUP BY i.item_id ORDER BY q DESC NULLS LAST, iid
-                 */     
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [\"I.item_id\"]\n" +
-                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, \"I.item_id\"]\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
-                /* 
-                 * testRightJoinWithAggregation()
-                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
-                 *     RIGHT JOIN joinItemTable i ON o.item_id = i.item_id 
-                 *     GROUP BY i.name ORDER BY i.name
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.NAME]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
-                /*
-                 * testRightJoinWithAggregation()
-                 *     SELECT i.item_id iid, sum(quantity) q FROM joinOrderTable o 
-                 *     RIGHT JOIN joinItemTable i ON o.item_id = i.item_id 
-                 *     GROUP BY i.item_id ORDER BY q DESC NULLS LAST, iid
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [\"I.item_id\"]\n" +
-                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, \"I.item_id\"]\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
-                /*
-                 * testJoinWithWildcard()
-                 *     SELECT * FROM joinItemTable LEFT JOIN joinSupplierTable supp 
-                 *     ON joinItemTable.supplier_id = supp.supplier_id 
-                 *     ORDER BY item_id
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" + 
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME,
-                /*
-                 * testJoinPlanWithIndex()
-                 *     SELECT item.item_id, item.name, supp.supplier_id, supp.name 
-                 *     FROM joinItemTable item LEFT JOIN joinSupplierTable supp 
-                 *     ON substr(item.name, 2, 1) = substr(supp.name, 2, 1) 
-                 *         AND (supp.name BETWEEN 'S1' AND 'S5') 
-                 *     WHERE item.name BETWEEN 'T1' AND 'T5'
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    SERVER FILTER BY (NAME >= 'T1' AND NAME <= 'T5')\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "            SERVER FILTER BY (NAME >= 'S1' AND NAME <= 'S5')",
-                /*
-                 * testJoinPlanWithIndex()
-                 *     SELECT item.item_id, item.name, supp.supplier_id, supp.name 
-                 *     FROM joinItemTable item INNER JOIN joinSupplierTable supp 
-                 *     ON item.supplier_id = supp.supplier_id 
-                 *     WHERE (item.name = 'T1' OR item.name = 'T5') 
-                 *         AND (supp.name = 'S1' OR supp.name = 'S5')
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    SERVER FILTER BY (NAME = 'T1' OR NAME = 'T5')\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "            SERVER FILTER BY (NAME = 'S1' OR NAME = 'S5')",
-                /*
-                 * testJoinWithSkipMergeOptimization()
-                 *     SELECT s.name FROM joinItemTable i 
-                 *     JOIN joinOrderTable o ON o.item_id = i.item_id AND quantity < 5000 
-                 *     JOIN joinSupplierTable s ON i.supplier_id = s.supplier_id
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    PARALLEL INNER-JOIN TABLE 0 (SKIP MERGE)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER FILTER BY QUANTITY < 5000\n" +
-                "    PARALLEL INNER-JOIN TABLE 1\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "    DYNAMIC SERVER FILTER BY \"I.item_id\" IN (\"O.item_id\")",
-                /*
-                 * testSelfJoin()
-                 *     SELECT i2.item_id, i1.name FROM joinItemTable i1 
-                 *     JOIN joinItemTable i2 ON i1.item_id = i2.item_id 
-                 *     ORDER BY i1.item_id
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    DYNAMIC SERVER FILTER BY \"I1.item_id\" IN (\"I2.item_id\")",
-                /*
-                 * testSelfJoin()
-                 *     SELECT i1.name, i2.name FROM joinItemTable i1 
-                 *     JOIN joinItemTable i2 ON i1.item_id = i2.supplier_id 
-                 *     ORDER BY i1.name, i2.name
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    SERVER SORTED BY [I1.NAME, I2.NAME]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    DYNAMIC SERVER FILTER BY \"I1.item_id\" IN (\"I2.supplier_id\")",
-                /*
-                 * testStarJoin()
-                 *     SELECT order_id, c.name, i.name iname, quantity, o.date 
-                 *     FROM joinOrderTable o 
-                 *     JOIN joinCustomerTable c ON o.customer_id = c.customer_id 
-                 *     JOIN joinItemTable i ON o.item_id = i.item_id 
-                 *     ORDER BY order_id
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + "\n" +
-                "    PARALLEL INNER-JOIN TABLE 1\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME,
-                /*
-                 * testStarJoin()
-                 *     SELECT (*NO_STAR_JOIN*) order_id, c.name, i.name iname, quantity, o.date 
-                 *     FROM joinOrderTable o 
-                 *     JOIN joinCustomerTable c ON o.customer_id = c.customer_id 
-                 *     JOIN joinItemTable i ON o.item_id = i.item_id 
-                 *     ORDER BY order_id
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    SERVER SORTED BY [\"O.order_id\"]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            PARALLEL INNER-JOIN TABLE 0\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + "\n" +
-                "    DYNAMIC SERVER FILTER BY \"I.item_id\" IN (\"O.item_id\")",
-                /*
-                 * testSubJoin()
-                 *     SELECT * FROM joinCustomerTable c 
-                 *     INNER JOIN (joinOrderTable o 
-                 *         INNER JOIN (joinSupplierTable s 
-                 *             RIGHT JOIN joinItemTable i ON i.supplier_id = s.supplier_id)
-                 *         ON o.item_id = i.item_id)
-                 *     ON c.customer_id = o.customer_id
-                 *     WHERE c.customer_id <= '0000000005' 
-                 *         AND order_id != '000000000000003' 
-                 *         AND i.name != 'T3' 
-                 *     ORDER BY c.customer_id, i.name
-                 */
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + " [*] - ['0000000005']\n" +
-                "    SERVER SORTED BY [\"C.customer_id\", I.NAME]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER FILTER BY \"order_id\" != '000000000000003'\n" +
-                "            PARALLEL INNER-JOIN TABLE 0\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "                    SERVER FILTER BY NAME != 'T3'\n" +
-                "                    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "    DYNAMIC SERVER FILTER BY \"C.customer_id\" IN (\"O.customer_id\")",
-                /* 
-                 * testJoinWithSubqueryAndAggregation()
-                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
-                 *     LEFT JOIN (SELECT name, item_id iid FROM joinItemTable) AS i 
-                 *     ON o.item_id = i.iid 
-                 *     GROUP BY i.name ORDER BY i.name
-                 */     
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.NAME]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME,
-                /* 
-                 * testJoinWithSubqueryAndAggregation()
-                 *     SELECT o.iid, sum(o.quantity) q 
-                 *     FROM (SELECT item_id iid, quantity FROM joinOrderTable) AS o 
-                 *     LEFT JOIN (SELECT item_id FROM joinItemTable) AS i 
-                 *     ON o.iid = i.item_id 
-                 *     GROUP BY o.iid ORDER BY q DESC                 
-                 */     
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [O.IID]\n" +
-                "CLIENT MERGE SORT\n" +
-                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0 (SKIP MERGE)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY",
-                /* 
-                 * testJoinWithSubqueryAndAggregation()
-                 *     SELECT i.iid, o.q 
-                 *     FROM (SELECT item_id iid FROM joinItemTable) AS i 
-                 *     LEFT JOIN (SELECT item_id iid, sum(quantity) q FROM joinOrderTable GROUP BY item_id) AS o 
-                 *     ON o.iid = i.iid 
-                 *     ORDER BY o.q DESC NULLS LAST, i.iid
-                 */     
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    SERVER SORTED BY [O.Q DESC NULLS LAST, I.IID]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
-                "        CLIENT MERGE SORT",
-                /* 
-                 * testJoinWithSubqueryAndAggregation()
-                 *     SELECT i.iid, o.q 
-                 *     FROM (SELECT item_id iid, sum(quantity) q FROM joinOrderTable GROUP BY item_id) AS o 
-                 *     JOIN (SELECT item_id iid FROM joinItemTable) AS i 
-                 *     ON o.iid = i.iid 
-                 *     ORDER BY o.q DESC, i.iid
-                 */     
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    SERVER SORTED BY [O.Q DESC, I.IID]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
-                "        CLIENT MERGE SORT",
-                /*
-                 * testNestedSubqueries()
-                 *     SELECT * FROM (SELECT customer_id cid, name, phone, address, loc_id, date FROM joinCustomerTable) AS c 
-                 *     INNER JOIN (SELECT o.oid ooid, o.cid ocid, o.iid oiid, o.price * o.quantity, o.date odate, 
-                 *     qi.iiid iiid, qi.iname iname, qi.iprice iprice, qi.idiscount1 idiscount1, qi.idiscount2 idiscount2, qi.isid isid, qi.idescription idescription, 
-                 *     qi.ssid ssid, qi.sname sname, qi.sphone sphone, qi.saddress saddress, qi.sloc_id sloc_id 
-                 *         FROM (SELECT item_id iid, customer_id cid, order_id oid, price, quantity, date FROM joinOrderTable) AS o 
-                 *         INNER JOIN (SELECT i.iid iiid, i.name iname, i.price iprice, i.discount1 idiscount1, i.discount2 idiscount2, i.sid isid, i.description idescription, 
-                 *         s.sid ssid, s.name sname, s.phone sphone, s.address saddress, s.loc_id sloc_id 
-                 *             FROM (SELECT supplier_id sid, name, phone, address, loc_id FROM joinSupplierTable) AS s 
-                 *             RIGHT JOIN (SELECT item_id iid, name, price, discount1, discount2, supplier_id sid, description FROM joinItemTable) AS i 
-                 *             ON i.sid = s.sid) as qi 
-                 *         ON o.iid = qi.iiid) as qo 
-                 *     ON c.cid = qo.ocid 
-                 *     WHERE c.cid <= '0000000005' 
-                 *         AND qo.ooid != '000000000000003' 
-                 *         AND qo.iname != 'T3' 
-                 *     ORDER BY c.cid, qo.iname
-                 */
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + " [*] - ['0000000005']\n" +
-                "    SERVER SORTED BY [C.CID, QO.INAME]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER FILTER BY \"order_id\" != '000000000000003'\n" +
-                "            PARALLEL INNER-JOIN TABLE 0\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "                    SERVER FILTER BY NAME != 'T3'\n" +
-                "                    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME,
-                /*
-                 * testJoinWithLimit()
-                 *     SELECT order_id, i.name, s.name, s.address, quantity 
-                 *     FROM joinSupplierTable s 
-                 *     LEFT JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
-                 *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 4
-                 */
-                "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "    SERVER 4 ROW LIMIT\n" +
-                "CLIENT 4 ROW LIMIT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    PARALLEL LEFT-JOIN TABLE 1(DELAYED EVALUATION)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    JOIN-SCANNER 4 ROW LIMIT",
-                /*
-                 * testJoinWithLimit()
-                 *     SELECT order_id, i.name, s.name, s.address, quantity 
-                 *     FROM joinSupplierTable s 
-                 *     JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
-                 *     JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 4
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "CLIENT 4 ROW LIMIT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    PARALLEL INNER-JOIN TABLE 1(DELAYED EVALUATION)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    DYNAMIC SERVER FILTER BY \"S.supplier_id\" IN (\"I.supplier_id\")\n" +
-                "    JOIN-SCANNER 4 ROW LIMIT",
-                /*
-                 * testJoinWithSetMaxRows()
-                 *     statement.setMaxRows(4);
-                 *     SELECT order_id, i.name, quantity FROM joinItemTable i
-                 *     JOIN joinOrderTable o ON o.item_id = i.item_id;
-                 *     SELECT o.order_id, i.name, o.quantity FROM joinItemTable i
-                 *     JOIN (SELECT order_id, item_id, quantity FROM joinOrderTable) o
-                 *     ON o.item_id = i.item_id;
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "CLIENT 4 ROW LIMIT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    DYNAMIC SERVER FILTER BY \"I.item_id\" IN (\"O.item_id\")\n" +
-                "    JOIN-SCANNER 4 ROW LIMIT",
-                /*
-                 * testJoinWithOffset()
-                 *     SELECT order_id, i.name, s.name, s.address, quantity 
-                 *     FROM joinSupplierTable s 
-                 *     LEFT JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
-                 *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 1 OFFSET 2
-                 */
-                "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "    SERVER OFFSET 2\n" +
-                "    SERVER 3 ROW LIMIT\n" +
-                "CLIENT 1 ROW LIMIT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    PARALLEL LEFT-JOIN TABLE 1(DELAYED EVALUATION)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    JOIN-SCANNER 3 ROW LIMIT",
-                /*
-                 * testJoinWithOffset()
-                 *     SELECT order_id, i.name, s.name, s.address, quantity 
-                 *     FROM joinSupplierTable s 
-                 *     JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
-                 *     JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 1 OFFSET 2
-                 */
-                "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "    SERVER OFFSET 2\n" +
-                "CLIENT 1 ROW LIMIT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    PARALLEL INNER-JOIN TABLE 1(DELAYED EVALUATION)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    DYNAMIC SERVER FILTER BY \"S.supplier_id\" IN (\"I.supplier_id\")\n" +
-                "    JOIN-SCANNER 3 ROW LIMIT",
-                }});
-        testCases.add(new String[][] {
-                {
-                "CREATE INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
-                "CREATE INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
-                "CREATE INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
-                }, {
-                /* 
-                 * testLeftJoinWithAggregation()
-                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
-                 *     LEFT JOIN joinItemTable i ON o.item_id = i.item_id 
-                 *     GROUP BY i.name ORDER BY i.name
-                 */     
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [\"I.0:NAME\"]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY",
-                /* 
-                 * testLeftJoinWithAggregation()
-                 *     SELECT i.item_id iid, sum(quantity) q FROM joinOrderTable o 
-                 *     LEFT JOIN joinItemTable i ON o.item_id = i.item_id 
-                 *     GROUP BY i.item_id ORDER BY q DESC"
-                 */     
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [\"I.:item_id\"]\n" +
-                "CLIENT MERGE SORT\n" +
-                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY",
-                /* 
-                 * testLeftJoinWithAggregation()
-                 *     SELECT i.item_id iid, sum(quantity) q FROM joinItemTable i 
-                 *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id 
-                 *     GROUP BY i.item_id ORDER BY q DESC NULLS LAST, iid
-                 */     
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [\"I.item_id\"]\n" +
-                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, \"I.item_id\"]\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
-                /* 
-                 * testRightJoinWithAggregation()
-                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
-                 *     RIGHT JOIN joinItemTable i ON o.item_id = i.item_id 
-                 *     GROUP BY i.name ORDER BY i.name
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [\"I.0:NAME\"]\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
-                /*
-                 * testRightJoinWithAggregation()
-                 *     SELECT i.item_id iid, sum(quantity) q FROM joinOrderTable o 
-                 *     RIGHT JOIN joinItemTable i ON o.item_id = i.item_id 
-                 *     GROUP BY i.item_id ORDER BY q DESC NULLS LAST, iid
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [\"I.item_id\"]\n" +
-                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, \"I.item_id\"]\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
-                /*
-                 * testJoinWithWildcard()
-                 *     SELECT * FROM joinItemTable LEFT JOIN joinSupplierTable supp 
-                 *     ON joinItemTable.supplier_id = supp.supplier_id 
-                 *     ORDER BY item_id
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME,
-                /*
-                 * testJoinPlanWithIndex()
-                 *     SELECT item.item_id, item.name, supp.supplier_id, supp.name 
-                 *     FROM joinItemTable item LEFT JOIN joinSupplierTable supp 
-                 *     ON substr(item.name, 2, 1) = substr(supp.name, 2, 1) 
-                 *         AND (supp.name BETWEEN 'S1' AND 'S5') 
-                 *     WHERE item.name BETWEEN 'T1' AND 'T5'
-                 */
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SCHEMA + ".idx_item ['T1'] - ['T5']\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SCHEMA + ".idx_supplier ['S1'] - ['S5']\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY",
-                /*
-                 * testJoinPlanWithIndex()
-                 *     SELECT item.item_id, item.name, supp.supplier_id, supp.name 
-                 *     FROM joinItemTable item INNER JOIN joinSupplierTable supp 
-                 *     ON item.supplier_id = supp.supplier_id 
-                 *     WHERE (item.name = 'T1' OR item.name = 'T5') 
-                 *         AND (supp.name = 'S1' OR supp.name = 'S5')
-                 */
-                "CLIENT PARALLEL 1-WAY SKIP SCAN ON 2 KEYS OVER " + JOIN_SCHEMA + ".idx_item ['T1'] - ['T5']\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY SKIP SCAN ON 2 KEYS OVER " + JOIN_SCHEMA + ".idx_supplier ['S1'] - ['S5']\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY",
-                /*
-                 * testJoinWithSkipMergeOptimization()
-                 *     SELECT s.name FROM joinItemTable i 
-                 *     JOIN joinOrderTable o ON o.item_id = i.item_id AND quantity < 5000 
-                 *     JOIN joinSupplierTable s ON i.supplier_id = s.supplier_id
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "    PARALLEL INNER-JOIN TABLE 0 (SKIP MERGE)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER FILTER BY QUANTITY < 5000\n" +
-                "    PARALLEL INNER-JOIN TABLE 1\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_supplier\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY",
-                /*
-                 * testSelfJoin()
-                 *     SELECT i2.item_id, i1.name FROM joinItemTable i1 
-                 *     JOIN joinItemTable i2 ON i1.item_id = i2.item_id 
-                 *     ORDER BY i1.item_id
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    DYNAMIC SERVER FILTER BY \"I1.item_id\" IN (\"I2.:item_id\")",
-                /*
-                 * testSelfJoin()
-                 *     SELECT i1.name, i2.name FROM joinItemTable i1 
-                 *     JOIN joinItemTable i2 ON i1.item_id = i2.supplier_id 
-                 *     ORDER BY i1.name, i2.name
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    SERVER SORTED BY [\"I1.0:NAME\", \"I2.0:NAME\"]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item",
-                /*
-                 * testStarJoin()
-                 *     SELECT order_id, c.name, i.name iname, quantity, o.date 
-                 *     FROM joinOrderTable o 
-                 *     JOIN joinCustomerTable c ON o.customer_id = c.customer_id 
-                 *     JOIN joinItemTable i ON o.item_id = i.item_id 
-                 *     ORDER BY order_id
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_customer\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "    PARALLEL INNER-JOIN TABLE 1\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY",
-                /*
-                 * testStarJoin()
-                 *     SELECT (*NO_STAR_JOIN*) order_id, c.name, i.name iname, quantity, o.date 
-                 *     FROM joinOrderTable o 
-                 *     JOIN joinCustomerTable c ON o.customer_id = c.customer_id 
-                 *     JOIN joinItemTable i ON o.item_id = i.item_id 
-                 *     ORDER BY order_id
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    SERVER SORTED BY [\"O.order_id\"]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            PARALLEL INNER-JOIN TABLE 0\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_customer\n" +
-                "                    SERVER FILTER BY FIRST KEY ONLY",
-                /*
-                 * testSubJoin()
-                 *     SELECT * FROM joinCustomerTable c 
-                 *     INNER JOIN (joinOrderTable o 
-                 *         INNER JOIN (joinSupplierTable s 
-                 *             RIGHT JOIN joinItemTable i ON i.supplier_id = s.supplier_id)
-                 *         ON o.item_id = i.item_id)
-                 *     ON c.customer_id = o.customer_id
-                 *     WHERE c.customer_id <= '0000000005' 
-                 *         AND order_id != '000000000000003' 
-                 *         AND i.name != 'T3' 
-                 *     ORDER BY c.customer_id, i.name
-                 */
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + " [*] - ['0000000005']\n" +
-                "    SERVER SORTED BY [\"C.customer_id\", \"I.0:NAME\"]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER FILTER BY \"order_id\" != '000000000000003'\n" +
-                "            PARALLEL INNER-JOIN TABLE 0\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "                    SERVER FILTER BY \"NAME\" != 'T3'\n" +
-                "                    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "    DYNAMIC SERVER FILTER BY \"C.customer_id\" IN (\"O.customer_id\")",
-                /* 
-                 * testJoinWithSubqueryAndAggregation()
-                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
-                 *     LEFT JOIN (SELECT name, item_id iid FROM joinItemTable) AS i 
-                 *     ON o.item_id = i.iid 
-                 *     GROUP BY i.name ORDER BY i.name
-                 */     
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.NAME]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY",
-                /* 
-                 * testJoinWithSubqueryAndAggregation()
-                 *     SELECT o.iid, sum(o.quantity) q 
-                 *     FROM (SELECT item_id iid, quantity FROM joinOrderTable) AS o 
-                 *     LEFT JOIN (SELECT item_id FROM joinItemTable) AS i 
-                 *     ON o.iid = i.item_id 
-                 *     GROUP BY o.iid ORDER BY q DESC                 
-                 */     
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [O.IID]\n" +
-                "CLIENT MERGE SORT\n" +
-                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0 (SKIP MERGE)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY",
-                /* 
-                 * testJoinWithSubqueryAndAggregation()
-                 *     SELECT i.iid, o.q 
-                 *     FROM (SELECT item_id iid FROM joinItemTable) AS i 
-                 *     LEFT JOIN (SELECT item_id iid, sum(quantity) q FROM joinOrderTable GROUP BY item_id) AS o 
-                 *     ON o.iid = i.iid 
-                 *     ORDER BY o.q DESC NULLS LAST, i.iid
-                 */     
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    SERVER SORTED BY [O.Q DESC NULLS LAST, I.IID]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
-                "        CLIENT MERGE SORT",
-                /* 
-                 * testJoinWithSubqueryAndAggregation()
-                 *     SELECT i.iid, o.q 
-                 *     FROM (SELECT item_id iid, sum(quantity) q FROM joinOrderTable GROUP BY item_id) AS o 
-                 *     JOIN (SELECT item_id iid FROM joinItemTable) AS i 
-                 *     ON o.iid = i.iid 
-                 *     ORDER BY o.q DESC, i.iid
-                 */     
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    SERVER SORTED BY [O.Q DESC, I.IID]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
-                "        CLIENT MERGE SORT",
-                /*
-                 * testNestedSubqueries()
-                 *     SELECT * FROM (SELECT customer_id cid, name, phone, address, loc_id, date FROM joinCustomerTable) AS c 
-                 *     INNER JOIN (SELECT o.oid ooid, o.cid ocid, o.iid oiid, o.price * o.quantity, o.date odate, 
-                 *     qi.iiid iiid, qi.iname iname, qi.iprice iprice, qi.idiscount1 idiscount1, qi.idiscount2 idiscount2, qi.isid isid, qi.idescription idescription, 
-                 *     qi.ssid ssid, qi.sname sname, qi.sphone sphone, qi.saddress saddress, qi.sloc_id sloc_id 
-                 *         FROM (SELECT item_id iid, customer_id cid, order_id oid, price, quantity, date FROM joinOrderTable) AS o 
-                 *         INNER JOIN (SELECT i.iid iiid, i.name iname, i.price iprice, i.discount1 idiscount1, i.discount2 idiscount2, i.sid isid, i.description idescription, 
-                 *         s.sid ssid, s.name sname, s.phone sphone, s.address saddress, s.loc_id sloc_id 
-                 *             FROM (SELECT supplier_id sid, name, phone, address, loc_id FROM joinSupplierTable) AS s 
-                 *             RIGHT JOIN (SELECT item_id iid, name, price, discount1, discount2, supplier_id sid, description FROM joinItemTable) AS i 
-                 *             ON i.sid = s.sid) as qi 
-                 *         ON o.iid = qi.iiid) as qo 
-                 *     ON c.cid = qo.ocid 
-                 *     WHERE c.cid <= '0000000005' 
-                 *         AND qo.ooid != '000000000000003' 
-                 *         AND qo.iname != 'T3' 
-                 *     ORDER BY c.cid, qo.iname
-                 */
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + " [*] - ['0000000005']\n" +
-                "    SERVER SORTED BY [C.CID, QO.INAME]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER FILTER BY \"order_id\" != '000000000000003'\n" +
-                "            PARALLEL INNER-JOIN TABLE 0\n" +
-                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "                    SERVER FILTER BY \"NAME\" != 'T3'\n" +
-                "                    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME,
-                /*
-                 * testJoinWithLimit()
-                 *     SELECT order_id, i.name, s.name, s.address, quantity 
-                 *     FROM joinSupplierTable s 
-                 *     LEFT JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
-                 *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 4
-                 */
-                "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "    SERVER 4 ROW LIMIT\n" +
-                "CLIENT 4 ROW LIMIT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_SCHEMA + ".idx_item\n" +
-                "    PARALLEL LEFT-JOIN TABLE 1(DELAYED EVALUATION)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    JOIN-SCANNER 4 ROW LIMIT",
-                /*
-                 * testJoinWithLimit()
-                 *     SELECT order_id, i.name, s.name, s.address, quantity 
-                 *     FROM joinSupplierTable s 
-                 *     JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
-                 *     JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 4
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "CLIENT 4 ROW LIMIT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_SCHEMA + ".idx_item\n" +
-                "    PARALLEL INNER-JOIN TABLE 1(DELAYED EVALUATION)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    DYNAMIC SERVER FILTER BY \"S.supplier_id\" IN (\"I.0:supplier_id\")\n" +
-                "    JOIN-SCANNER 4 ROW LIMIT",
-                /*
-                 * testJoinWithSetMaxRows()
-                 *     statement.setMaxRows(4);
-                 *     SELECT order_id, i.name, quantity FROM joinItemTable i
-                 *     JOIN joinOrderTable o ON o.item_id = i.item_id;
-                 *     SELECT o.order_id, i.name, o.quantity FROM joinItemTable i
-                 *     JOIN (SELECT order_id, item_id, quantity FROM joinOrderTable) o
-                 *     ON o.item_id = i.item_id;
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_SCHEMA + ".idx_item\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "CLIENT 4 ROW LIMIT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    JOIN-SCANNER 4 ROW LIMIT",
-                /*
-                 * testJoinWithLimit()
-                 *     SELECT order_id, i.name, s.name, s.address, quantity 
-                 *     FROM joinSupplierTable s 
-                 *     LEFT JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
-                 *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 1 OFFSET 2
-                 */
-                "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "    SERVER OFFSET 2\n" +
-                "    SERVER 3 ROW LIMIT\n" +
-                "CLIENT 1 ROW LIMIT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_SCHEMA + ".idx_item\n" +
-                "    PARALLEL LEFT-JOIN TABLE 1(DELAYED EVALUATION)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    JOIN-SCANNER 3 ROW LIMIT",
-                /*
-                 * testJoinWithLimit()
-                 *     SELECT order_id, i.name, s.name, s.address, quantity 
-                 *     FROM joinSupplierTable s 
-                 *     JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
-                 *     JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 1 OFFSET 2
-                 */
-                "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "    SERVER OFFSET 2\n" +
-                "CLIENT 1 ROW LIMIT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_SCHEMA + ".idx_item\n" +
-                "    PARALLEL INNER-JOIN TABLE 1(DELAYED EVALUATION)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    DYNAMIC SERVER FILTER BY \"S.supplier_id\" IN (\"I.0:supplier_id\")\n" +
-                "    JOIN-SCANNER 3 ROW LIMIT",
-                }});
-        testCases.add(new String[][] {
-                {
-                "CREATE LOCAL INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
-                "CREATE LOCAL INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
-                "CREATE LOCAL INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
-                }, {
-                /* 
-                 * testLeftJoinWithAggregation()
-                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
-                 *     LEFT JOIN joinItemTable i ON o.item_id = i.item_id 
-                 *     GROUP BY i.name ORDER BY i.name
-                 */     
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [\"I.0:NAME\"]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +JOIN_ITEM_TABLE_FULL_NAME +" [1]\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        CLIENT MERGE SORT",
-                /* 
-                 * testLeftJoinWithAggregation()
-                 *     SELECT i.item_id iid, sum(quantity) q FROM joinOrderTable o 
-                 *     LEFT JOIN joinItemTable i ON o.item_id = i.item_id 
-                 *     GROUP BY i.item_id ORDER BY q DESC"
-                 */     
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [\"I.:item_id\"]\n" +
-                "CLIENT MERGE SORT\n" +
-                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME +" [1]\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        CLIENT MERGE SORT",          
-                /* 
-                 * testLeftJoinWithAggregation()
-                 *     SELECT i.item_id iid, sum(quantity) q FROM joinItemTable i 
-                 *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id 
-                 *     GROUP BY i.item_id ORDER BY q DESC NULLS LAST, iid
-                 */     
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [\"I.item_id\"]\n" +
-                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, \"I.item_id\"]\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
-                /* 
-                 * testRightJoinWithAggregation()
-                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
-                 *     RIGHT JOIN joinItemTable i ON o.item_id = i.item_id 
-                 *     GROUP BY i.name ORDER BY i.name
-                 */
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME+" [1]\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [\"I.0:NAME\"]\n" +
-                "CLIENT MERGE SORT\n" + 
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
-                /*
-                 * testRightJoinWithAggregation()
-                 *     SELECT i.item_id iid, sum(quantity) q FROM joinOrderTable o 
-                 *     RIGHT JOIN joinItemTable i ON o.item_id = i.item_id 
-                 *     GROUP BY i.item_id ORDER BY q DESC NULLS LAST, iid
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [\"I.item_id\"]\n" +
-                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, \"I.item_id\"]\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
-                /*
-                 * testJoinWithWildcard()
-                 *     SELECT * FROM joinItemTable LEFT JOIN joinSupplierTable supp 
-                 *     ON joinItemTable.supplier_id = supp.supplier_id 
-                 *     ORDER BY item_id
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME,
-                /*
-                 * testJoinPlanWithIndex()
-                 *     SELECT item.item_id, item.name, supp.supplier_id, supp.name 
-                 *     FROM joinItemTable item LEFT JOIN joinSupplierTable supp 
-                 *     ON substr(item.name, 2, 1) = substr(supp.name, 2, 1) 
-                 *         AND (supp.name BETWEEN 'S1' AND 'S5') 
-                 *     WHERE item.name BETWEEN 'T1' AND 'T5'
-                 */
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1,'T1'] - [1,'T5']\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME +" [1,'S1'] - [1,'S5']\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "        CLIENT MERGE SORT",
-                /*
-                 * testJoinPlanWithIndex()
-                 *     SELECT item.item_id, item.name, supp.supplier_id, supp.name 
-                 *     FROM joinItemTable item INNER JOIN joinSupplierTable supp 
-                 *     ON item.supplier_id = supp.supplier_id 
-                 *     WHERE (item.name = 'T1' OR item.name = 'T5') 
-                 *         AND (supp.name = 'S1' OR supp.name = 'S5')
-                 */
-                "CLIENT PARALLEL 1-WAY SKIP SCAN ON 2 KEYS OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1,'T1'] - [1,'T5']\n" +
-                "CLIENT MERGE SORT\n" + 
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY SKIP SCAN ON 2 KEYS OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME +" [1,'S1'] - [1,'S5']\n" + 
-                "            SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "        CLIENT MERGE SORT",
-                /*
-                 * testJoinWithSkipMergeOptimization()
-                 *     SELECT s.name FROM joinItemTable i 
-                 *     JOIN joinOrderTable o ON o.item_id = i.item_id AND quantity < 5000 
-                 *     JOIN joinSupplierTable s ON i.supplier_id = s.supplier_id
-                 */
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "CLIENT MERGE SORT\n" + 
-                "    PARALLEL INNER-JOIN TABLE 0 (SKIP MERGE)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER FILTER BY QUANTITY < 5000\n" +
-                "    PARALLEL INNER-JOIN TABLE 1\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + " [1]\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "        CLIENT MERGE SORT\n" +
-                "    DYNAMIC SERVER FILTER BY \"I.:item_id\" IN (\"O.item_id\")",
-                /*
-                 * testSelfJoin()
-                 *     SELECT i2.item_id, i1.name FROM joinItemTable i1 
-                 *     JOIN joinItemTable i2 ON i1.item_id = i2.item_id 
-                 *     ORDER BY i1.item_id
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME +" [1]\n"  +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    DYNAMIC SERVER FILTER BY \"I1.item_id\" IN (\"I2.:item_id\")",
-                /*
-                 * testSelfJoin()
-                 *     SELECT i1.name, i2.name FROM joinItemTable i1 
-                 *     JOIN joinItemTable i2 ON i1.item_id = i2.supplier_id 
-                 *     ORDER BY i1.name, i2.name
-                 */
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME +" [1]\n"  +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    SERVER SORTED BY [\"I1.0:NAME\", \"I2.0:NAME\"]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME +" [1]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    DYNAMIC SERVER FILTER BY \"I1.:item_id\" IN (\"I2.0:supplier_id\")",
-                /*
-                 * testStarJoin()
-                 *     SELECT order_id, c.name, i.name iname, quantity, o.date 
-                 *     FROM joinOrderTable o 
-                 *     JOIN joinCustomerTable c ON o.customer_id = c.customer_id 
-                 *     JOIN joinItemTable i ON o.item_id = i.item_id 
-                 *     ORDER BY order_id
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + " [1]\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "        CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 1\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        CLIENT MERGE SORT",
-                /*
-                 * testStarJoin()
-                 *     SELECT (*NO_STAR_JOIN*) order_id, c.name, i.name iname, quantity, o.date 
-                 *     FROM joinOrderTable o 
-                 *     JOIN joinCustomerTable c ON o.customer_id = c.customer_id 
-                 *     JOIN joinItemTable i ON o.item_id = i.item_id 
-                 *     ORDER BY order_id
-                 */
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    SERVER SORTED BY [\"O.order_id\"]\n"+
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            PARALLEL INNER-JOIN TABLE 0\n" +
-                "                CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME+" [1]\n"+
-                "                    SERVER FILTER BY FIRST KEY ONLY\n" + 
-                "                CLIENT MERGE SORT\n" +
-                "    DYNAMIC SERVER FILTER BY \"I.:item_id\" IN (\"O.item_id\")",
-                /*
-                 * testSubJoin()
-                 *     SELECT * FROM joinCustomerTable c 
-                 *     INNER JOIN (joinOrderTable o 
-                 *         INNER JOIN (joinSupplierTable s 
-                 *             RIGHT JOIN joinItemTable i ON i.supplier_id = s.supplier_id)
-                 *         ON o.item_id = i.item_id)
-                 *     ON c.customer_id = o.customer_id
-                 *     WHERE c.customer_id <= '0000000005' 
-                 *         AND order_id != '000000000000003' 
-                 *         AND i.name != 'T3' 
-                 *     ORDER BY c.customer_id, i.name
-                 */
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + " [*] - ['0000000005']\n" +
-                "    SERVER SORTED BY [\"C.customer_id\", \"I.0:NAME\"]\n"+
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER FILTER BY \"order_id\" != '000000000000003'\n" +
-                "            PARALLEL INNER-JOIN TABLE 0\n" +
-                "                CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME +" [1]\n" +
-                "                    SERVER FILTER BY \"NAME\" != 'T3'\n" +
-                "                CLIENT MERGE SORT\n" +
-                "                    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "    DYNAMIC SERVER FILTER BY \"C.customer_id\" IN (\"O.customer_id\")",
-                /* 
-                 * testJoinWithSubqueryAndAggregation()
-                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
-                 *     LEFT JOIN (SELECT name, item_id iid FROM joinItemTable) AS i 
-                 *     ON o.item_id = i.iid 
-                 *     GROUP BY i.name ORDER BY i.name
-                 */     
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.NAME]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +JOIN_ITEM_TABLE_FULL_NAME+" [1]\n"+
-                "            SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        CLIENT MERGE SORT",
-                /* 
-                 * testJoinWithSubqueryAndAggregation()
-                 *     SELECT o.iid, sum(o.quantity) q 
-                 *     FROM (SELECT item_id iid, quantity FROM joinOrderTable) AS o 
-                 *     LEFT JOIN (SELECT item_id FROM joinItemTable) AS i 
-                 *     ON o.iid = i.item_id 
-                 *     GROUP BY o.iid ORDER BY q DESC                 
-                 */     
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [O.IID]\n" +
-                "CLIENT MERGE SORT\n" +
-                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0 (SKIP MERGE)\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "            SERVER FILTER BY FIRST KEY ONLY\n" +
-                "        CLIENT MERGE SORT",
-                /* 
-                 * testJoinWithSubqueryAndAggregation()
-                 *     SELECT i.iid, o.q 
-                 *     FROM (SELECT item_id iid FROM joinItemTable) AS i 
-                 *     LEFT JOIN (SELECT item_id iid, sum(quantity) q FROM joinOrderTable GROUP BY item_id) AS o 
-                 *     ON o.iid = i.iid 
-                 *     ORDER BY o.q DESC NULLS LAST, i.iid
-                 */     
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    SERVER SORTED BY [O.Q DESC NULLS LAST, I.IID]\n"+
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
-                "        CLIENT MERGE SORT",
-                /* 
-                 * testJoinWithSubqueryAndAggregation()
-                 *     SELECT i.iid, o.q 
-                 *     FROM (SELECT item_id iid, sum(quantity) q FROM joinOrderTable GROUP BY item_id) AS o 
-                 *     JOIN (SELECT item_id iid FROM joinItemTable) AS i 
-                 *     ON o.iid = i.iid 
-                 *     ORDER BY o.q DESC, i.iid
-                 */     
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    SERVER SORTED BY [O.Q DESC, I.IID]\n"+
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
-                "        CLIENT MERGE SORT",
-                /*
-                 * testNestedSubqueries()
-                 *     SELECT * FROM (SELECT customer_id cid, name, phone, address, loc_id, date FROM joinCustomerTable) AS c 
-                 *     INNER JOIN (SELECT o.oid ooid, o.cid ocid, o.iid oiid, o.price * o.quantity, o.date odate, 
-                 *     qi.iiid iiid, qi.iname iname, qi.iprice iprice, qi.idiscount1 idiscount1, qi.idiscount2 idiscount2, qi.isid isid, qi.idescription idescription, 
-                 *     qi.ssid ssid, qi.sname sname, qi.sphone sphone, qi.saddress saddress, qi.sloc_id sloc_id 
-                 *         FROM (SELECT item_id iid, customer_id cid, order_id oid, price, quantity, date FROM joinOrderTable) AS o 
-                 *         INNER JOIN (SELECT i.iid iiid, i.name iname, i.price iprice, i.discount1 idiscount1, i.discount2 idiscount2, i.sid isid, i.description idescription, 
-                 *         s.sid ssid, s.name sname, s.phone sphone, s.address saddress, s.loc_id sloc_id 
-                 *             FROM (SELECT supplier_id sid, name, phone, address, loc_id FROM joinSupplierTable) AS s 
-                 *             RIGHT JOIN (SELECT item_id iid, name, price, discount1, discount2, supplier_id sid, description FROM joinItemTable) AS i 
-                 *             ON i.sid = s.sid) as qi 
-                 *         ON o.iid = qi.iiid) as qo 
-                 *     ON c.cid = qo.ocid 
-                 *     WHERE c.cid <= '0000000005' 
-                 *         AND qo.ooid != '000000000000003' 
-                 *         AND qo.iname != 'T3' 
-                 *     ORDER BY c.cid, qo.iname
-                 */
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + " [*] - ['0000000005']\n" +
-                "    SERVER SORTED BY [C.CID, QO.INAME]\n" +
-                "CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "            SERVER FILTER BY \"order_id\" != '000000000000003'\n" +
-                "            PARALLEL INNER-JOIN TABLE 0\n" +
-                "                CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +  JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "                    SERVER FILTER BY \"NAME\" != 'T3'\n" +
-                "                CLIENT MERGE SORT\n" +      
-                "                    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME,
-                /*
-                 * testJoinWithLimit()
-                 *     SELECT order_id, i.name, s.name, s.address, quantity 
-                 *     FROM joinSupplierTable s 
-                 *     LEFT JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
-                 *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 4
-                 */
-                "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "    SERVER 4 ROW LIMIT\n" +
-                "CLIENT 4 ROW LIMIT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "        CLIENT MERGE SORT\n" +      
-                "    PARALLEL LEFT-JOIN TABLE 1(DELAYED EVALUATION)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    JOIN-SCANNER 4 ROW LIMIT",
-                /*
-                 * testJoinWithLimit()
-                 *     SELECT order_id, i.name, s.name, s.address, quantity 
-                 *     FROM joinSupplierTable s 
-                 *     JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
-                 *     JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 4
-                 */
-                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "CLIENT 4 ROW LIMIT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 1(DELAYED EVALUATION)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    DYNAMIC SERVER FILTER BY \"S.supplier_id\" IN (\"I.0:supplier_id\")\n" +
-                "    JOIN-SCANNER 4 ROW LIMIT",
-                /*
-                 * testJoinWithSetMaxRows()
-                 *     statement.setMaxRows(4);
-                 *     SELECT order_id, i.name, quantity FROM joinItemTable i
-                 *     JOIN joinOrderTable o ON o.item_id = i.item_id;
-                 *     SELECT o.order_id, i.name, o.quantity FROM joinItemTable i
-                 *     JOIN (SELECT order_id, item_id, quantity FROM joinOrderTable) o
-                 *     ON o.item_id = i.item_id;
-                 */
-                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "CLIENT MERGE SORT\n" +
-                "CLIENT 4 ROW LIMIT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    DYNAMIC SERVER FILTER BY \"I.:item_id\" IN (\"O.item_id\")\n" +
-                "    JOIN-SCANNER 4 ROW LIMIT",
-                /*
-                 * testJoinWithOffset()
-                 *     SELECT order_id, i.name, s.name, s.address, quantity 
-                 *     FROM joinSupplierTable s 
-                 *     LEFT JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
-                 *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 1 OFFSET 2
-                 */
-                "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "    SERVER OFFSET 2\n" +
-                "    SERVER 3 ROW LIMIT\n" +
-                "CLIENT 1 ROW LIMIT\n" +
-                "    PARALLEL LEFT-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "        CLIENT MERGE SORT\n" +      
-                "    PARALLEL LEFT-JOIN TABLE 1(DELAYED EVALUATION)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    JOIN-SCANNER 3 ROW LIMIT",
-                /*
-                 * testJoinWithOffset()
-                 *     SELECT order_id, i.name, s.name, s.address, quantity 
-                 *     FROM joinSupplierTable s 
-                 *     JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
-                 *     JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 1 OFFSET 2
-                 */
-                "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-                "    SERVER OFFSET 2\n" +
-                "CLIENT 1 ROW LIMIT\n" +
-                "    PARALLEL INNER-JOIN TABLE 0\n" +
-                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
-                "        CLIENT MERGE SORT\n" +
-                "    PARALLEL INNER-JOIN TABLE 1(DELAYED EVALUATION)\n" +
-                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-                "    DYNAMIC SERVER FILTER BY \"S.supplier_id\" IN (\"I.0:supplier_id\")\n" +
-                "    JOIN-SCANNER 3 ROW LIMIT",
-                }});
-        return testCases;
-    }
-    
-    
-    @Test
-    public void testDefaultJoin() throws Exception {
-        Connection conn = getConnection();
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String query = "SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\"";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "T2");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "T3");
-            assertEquals(rs.getString(3), "0000000002");
-            assertEquals(rs.getString(4), "S2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertEquals(rs.getString(3), "0000000002");
-            assertEquals(rs.getString(4), "S2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-            assertEquals(rs.getString(3), "0000000005");
-            assertEquals(rs.getString(4), "S5");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "0000000006");
-            assertEquals(rs.getString(4), "S6");
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-
-    @Test
-    public void testInnerJoin() throws Exception {
-        Connection conn = getConnection();
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String query = "SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name, next value for " + seqName + " FROM " + tableName1 + " item INNER JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\"";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertEquals(1, rs.getInt(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "T2");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertEquals(2, rs.getInt(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "T3");
-            assertEquals(rs.getString(3), "0000000002");
-            assertEquals(rs.getString(4), "S2");
-            assertEquals(3, rs.getInt(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertEquals(rs.getString(3), "0000000002");
-            assertEquals(rs.getString(4), "S2");
-            assertEquals(4, rs.getInt(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-            assertEquals(rs.getString(3), "0000000005");
-            assertEquals(rs.getString(4), "S5");
-            assertEquals(5, rs.getInt(5));
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "0000000006");
-            assertEquals(rs.getString(4), "S6");
-            assertEquals(6, rs.getInt(5));
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-            
-    @Test
-    public void testLeftJoin() throws Exception {
-        Connection conn = getConnection();
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String query[] = new String[3];
-        query[0] = "SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name, next value for " + seqName + " FROM " + tableName1 + " item LEFT JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
-        query[1] = "SELECT " + tableName1 + ".\"item_id\", " + tableName1 + ".name, " + tableName2 + ".\"supplier_id\", " + tableName2 + ".name, next value for " + seqName + " FROM " + tableName1 + " LEFT JOIN " + tableName2 + " ON " + tableName1 + ".\"supplier_id\" = " + tableName2 + ".\"supplier_id\" ORDER BY \"item_id\"";
-        query[2] = "SELECT item.\"item_id\", " + tableName1 + ".name, supp.\"supplier_id\", " + tableName2 + ".name, next value for " + seqName + " FROM " + tableName1 + " item LEFT JOIN " + tableName2 + " supp ON " + tableName1 + ".\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
-        try {
-            for (int i = 0; i < query.length; i++) {
-                PreparedStatement statement = conn.prepareStatement(query[i]);
-                ResultSet rs = statement.executeQuery();
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "0000000001");
-                assertEquals(rs.getString(2), "T1");
-                assertEquals(rs.getString(3), "0000000001");
-                assertEquals(rs.getString(4), "S1");
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "0000000002");
-                assertEquals(rs.getString(2), "T2");
-                assertEquals(rs.getString(3), "0000000001");
-                assertEquals(rs.getString(4), "S1");
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "0000000003");
-                assertEquals(rs.getString(2), "T3");
-                assertEquals(rs.getString(3), "0000000002");
-                assertEquals(rs.getString(4), "S2");
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "0000000004");
-                assertEquals(rs.getString(2), "T4");
-                assertEquals(rs.getString(3), "0000000002");
-                assertEquals(rs.getString(4), "S2");
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "0000000005");
-                assertEquals(rs.getString(2), "T5");
-                assertEquals(rs.getString(3), "0000000005");
-                assertEquals(rs.getString(4), "S5");
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "0000000006");
-                assertEquals(rs.getString(2), "T6");
-                assertEquals(rs.getString(3), "0000000006");
-                assertEquals(rs.getString(4), "S6");
-                assertTrue (rs.next());
-                assertEquals(rs.getString(1), "invalid001");
-                assertEquals(rs.getString(2), "INVALID-1");
-                assertNull(rs.getString(3));
-                assertNull(rs.getString(4));
-
-                assertFalse(rs.next());
-                rs.close();
-                statement.close();
-            }
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testRightJoin() throws Exception {
-        Connection conn = getConnection();
-        String tableName1 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String query = "SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " supp RIGHT JOIN " + tableName2 + " item ON item.\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "T2");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "T3");
-            assertEquals(rs.getString(3), "0000000002");
-            assertEquals(rs.getString(4), "S2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertEquals(rs.getString(3), "0000000002");
-            assertEquals(rs.getString(4), "S2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-            assertEquals(rs.getString(3), "0000000005");
-            assertEquals(rs.getString(4), "S5");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000006");
-            assertEquals(rs.getString(2), "T6");
-            assertEquals(rs.getString(3), "0000000006");
-            assertEquals(rs.getString(4), "S6");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "invalid001");
-            assertEquals(rs.getString(2), "INVALID-1");
-            assertNull(rs.getString(3));
-            assertNull(rs.getString(4));
-
-            assertFalse(rs.next());
-        } finally {
-            conn.close();
-        }
-    }
-    
-    @Test
-    public void testInnerJoinWithPreFilters() throws Exception {
-        Connection conn = getConnection();
-        String tableName1 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String query1 = "SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item INNER JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" AND supp.\"supplier_id\" BETWEEN '0000000001' AND '0000000005'";
-        String query2 = "SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + tableName1 + " item INNER JOIN " + tableName2 + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" AND (supp.\"supplier_id\" = '0000000001' OR supp.\"supplier_id\" = '0000000005')";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query1);
-            ResultSet rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "T2");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000003");
-            assertEquals(rs.getString(2), "T3");
-            assertEquals(rs.getString(3), "0000000002");
-            assertEquals(rs.getString(4), "S2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000004");
-            assertEquals(rs.getString(2), "T4");
-            assertEquals(rs.getString(3), "0000000002");
-            assertEquals(rs.getString(4), "S2");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-            assertEquals(rs.getString(3), "0000000005");
-            assertEquals(rs.getString(4), "S5");
-
-            assertFalse(rs.next());
-            
-            
-            statement = conn.prepareStatement(query2);
-            rs = statement.executeQuery();
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000001");
-            assertEquals(rs.getString(2), "T1");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000002");
-            assertEquals(rs.getString(2), "T2");
-            assertEquals(rs.getString(3), "0000000001");
-            assertEquals(rs.getString(4), "S1");
-            assertTrue (rs.next());
-            assertEquals(rs.getString(1), "0000000005");
-            assertEquals(rs.getString(2), "T5");
-            assertEquals(rs.getString(3), "0000000005");
-      

<TRUNCATED>

[13/25] phoenix git commit: PHOENIX-4246 Breakup join related tests into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
PHOENIX-4246 Breakup join related tests into several integration tests so as not to create too many tables in one test


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

Branch: refs/heads/master
Commit: ee20a8c9ba8c7b491937eaf93a889010daebba98
Parents: fd77764
Author: James Taylor <jt...@salesforce.com>
Authored: Thu Sep 28 14:53:14 2017 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Thu Sep 28 17:51:57 2017 -0700

----------------------------------------------------------------------
 .../org/apache/phoenix/end2end/BaseJoinIT.java  |  470 ---
 .../apache/phoenix/end2end/HashJoinCacheIT.java |  482 ---
 .../org/apache/phoenix/end2end/HashJoinIT.java  | 3448 ------------------
 .../phoenix/end2end/HashJoinLocalIndexIT.java   |  128 -
 .../apache/phoenix/end2end/HashJoinMoreIT.java  |  909 -----
 .../apache/phoenix/end2end/SortMergeJoinIT.java | 2563 -------------
 .../org/apache/phoenix/end2end/SubqueryIT.java  |  788 ----
 .../end2end/SubqueryUsingSortMergeJoinIT.java   |  566 ---
 .../apache/phoenix/end2end/join/BaseJoinIT.java |  473 +++
 .../phoenix/end2end/join/HashJoinCacheIT.java   |  101 +
 .../end2end/join/HashJoinGlobalIndexIT.java     |  399 ++
 .../apache/phoenix/end2end/join/HashJoinIT.java | 2316 ++++++++++++
 .../end2end/join/HashJoinLocalIndexIT.java      |  528 +++
 .../phoenix/end2end/join/HashJoinMoreIT.java    |  910 +++++
 .../phoenix/end2end/join/HashJoinNoIndexIT.java |  391 ++
 .../phoenix/end2end/join/SortMergeJoinIT.java   | 2563 +++++++++++++
 .../apache/phoenix/end2end/join/SubqueryIT.java |  788 ++++
 .../join/SubqueryUsingSortMergeJoinIT.java      |  566 +++
 18 files changed, 9035 insertions(+), 9354 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/ee20a8c9/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseJoinIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseJoinIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseJoinIT.java
deleted file mode 100644
index a823a72..0000000
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseJoinIT.java
+++ /dev/null
@@ -1,470 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.phoenix.end2end;
-
-import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import java.sql.Connection;
-import java.sql.Date;
-import java.sql.DriverManager;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.sql.Timestamp;
-import java.text.SimpleDateFormat;
-import java.util.Map;
-import java.util.Properties;
-import java.util.regex.Pattern;
-
-import org.apache.hadoop.hbase.HConstants;
-import org.apache.phoenix.cache.ServerCacheClient;
-import org.apache.phoenix.util.PropertiesUtil;
-import org.apache.phoenix.util.ReadOnlyProps;
-import org.apache.phoenix.util.SchemaUtil;
-import org.apache.phoenix.util.StringUtil;
-import org.junit.Before;
-import org.junit.BeforeClass;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-
-public abstract class BaseJoinIT extends ParallelStatsDisabledIT {
-	
-    protected static final String JOIN_SCHEMA = "Join";
-    protected static final String JOIN_ORDER_TABLE = "OrderTable";
-    protected static final String JOIN_CUSTOMER_TABLE = "CustomerTable";
-    protected static final String JOIN_ITEM_TABLE = "ItemTable";
-    protected static final String JOIN_SUPPLIER_TABLE = "SupplierTable";
-    protected static final String JOIN_COITEM_TABLE = "CoitemTable";
-    protected static final String JOIN_ORDER_TABLE_FULL_NAME = '"' + JOIN_SCHEMA + "\".\"" + JOIN_ORDER_TABLE + '"';
-    protected static final String JOIN_CUSTOMER_TABLE_FULL_NAME = '"' + JOIN_SCHEMA + "\".\"" + JOIN_CUSTOMER_TABLE + '"';
-    protected static final String JOIN_ITEM_TABLE_FULL_NAME = '"' + JOIN_SCHEMA + "\".\"" + JOIN_ITEM_TABLE + '"';
-    protected static final String JOIN_SUPPLIER_TABLE_FULL_NAME = '"' + JOIN_SCHEMA + "\".\"" + JOIN_SUPPLIER_TABLE + '"';
-    protected static final String JOIN_COITEM_TABLE_FULL_NAME = '"' + JOIN_SCHEMA + "\".\"" + JOIN_COITEM_TABLE + '"';
-
-    private static final Map<String,String> tableDDLMap;
-    
-    static {
-        ImmutableMap.Builder<String,String> builder = ImmutableMap.builder();
-        builder.put(JOIN_ORDER_TABLE_FULL_NAME, "create table " + JOIN_ORDER_TABLE_FULL_NAME +
-                "   (\"order_id\" varchar(15) not null primary key, " +
-                "    \"customer_id\" varchar(10), " +
-                "    \"item_id\" varchar(10), " +
-                "    price integer, " +
-                "    quantity integer, " +
-                "    date timestamp) IMMUTABLE_ROWS=true");
-        builder.put(JOIN_CUSTOMER_TABLE_FULL_NAME, "create table " + JOIN_CUSTOMER_TABLE_FULL_NAME +
-                "   (\"customer_id\" varchar(10) not null primary key, " +
-                "    name varchar, " +
-                "    phone varchar(12), " +
-                "    address varchar, " +
-                "    loc_id varchar(5), " +
-                "    date date) IMMUTABLE_ROWS=true");
-        builder.put(JOIN_ITEM_TABLE_FULL_NAME, "create table " + JOIN_ITEM_TABLE_FULL_NAME +
-                "   (\"item_id\" varchar(10) not null primary key, " +
-                "    name varchar, " +
-                "    price integer, " +
-                "    discount1 integer, " +
-                "    discount2 integer, " +
-                "    \"supplier_id\" varchar(10), " +
-                "    description varchar)");
-        builder.put(JOIN_SUPPLIER_TABLE_FULL_NAME, "create table " + JOIN_SUPPLIER_TABLE_FULL_NAME +
-                "   (\"supplier_id\" varchar(10) not null primary key, " +
-                "    name varchar, " +
-                "    phone varchar(12), " +
-                "    address varchar, " +
-                "    loc_id varchar(5))");
-        builder.put(JOIN_COITEM_TABLE_FULL_NAME, "create table " + JOIN_COITEM_TABLE_FULL_NAME +
-                "   (item_id varchar(10) NOT NULL, " +
-                "    item_name varchar NOT NULL, " +
-                "    co_item_id varchar(10), " +
-                "    co_item_name varchar " +
-                "   CONSTRAINT pk PRIMARY KEY (item_id, item_name)) " +
-                "   SALT_BUCKETS=4");
-        tableDDLMap = builder.build();
-    }
-    
-    protected String seqName;
-    protected String schemaName;
-    protected final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-    protected final String[] plans;
-    private final String[] indexDDL;
-    private final Map<String,String> virtualNameToRealNameMap = Maps.newHashMap();
-    
-    public BaseJoinIT(String[] indexDDL, String[] plans) {
-        this.indexDDL = indexDDL;
-        this.plans = plans;
-    }
-    
-    protected String getTableName(Connection conn, String virtualName) throws Exception {
-        String realName = virtualNameToRealNameMap.get(virtualName);
-        if (realName == null) {
-            realName = SchemaUtil.getTableName(schemaName, generateUniqueName());
-            virtualNameToRealNameMap.put(virtualName, realName);
-            createTable(conn, virtualName, realName);
-            initValues(conn, virtualName, realName);
-            createIndexes(conn, virtualName, realName);
-        }
-        return realName;
-    }
-    
-    protected String getDisplayTableName(Connection conn, String virtualName) throws Exception {
-        return getTableName(conn, virtualName);
-    }
-
-    private void createTable(Connection conn, String virtualName, String realName) throws SQLException {
-        String ddl = tableDDLMap.get(virtualName);
-        if (ddl == null) {
-            throw new IllegalStateException("Expected to find " + virtualName + " in " + tableDDLMap);
-        }
-        ddl =  ddl.replace(virtualName, realName);
-        conn.createStatement().execute(ddl);
-    }
-
-    @Before
-    public void createSchema() throws SQLException {
-        Connection conn = DriverManager.getConnection(getUrl());
-        try {
-            schemaName = "S_" + generateUniqueName();
-            seqName = "SEQ_" + generateUniqueName();
-            conn.createStatement().execute("CREATE SEQUENCE " + seqName);
-        } finally {
-            conn.close();
-        }
-    }
-    
-    private String translateToVirtualPlan(String actualPlan) {
-        int size = virtualNameToRealNameMap.size();
-        String[] virtualNames = new String[size+1];
-        String[] realNames = new String[size+1];
-        int count = 0;
-        for (Map.Entry<String, String>entry : virtualNameToRealNameMap.entrySet()) {
-            virtualNames[count] = entry.getKey();
-            realNames[count] = entry.getValue();
-            count++;
-        }
-        realNames[count] = schemaName;
-        virtualNames[count]= JOIN_SCHEMA;
-        String convertedPlan =  StringUtil.replace(actualPlan, realNames, virtualNames);
-        return convertedPlan;
-    }
-    
-    protected void assertPlansMatch(String virtualPlanRegEx, String actualPlan) {
-        String convertedPlan = translateToVirtualPlan(actualPlan);
-        assertTrue("\"" + convertedPlan + "\" does not match \"" + virtualPlanRegEx + "\"", Pattern.matches(virtualPlanRegEx, convertedPlan));
-    }
-    
-    protected void assertPlansEqual(String virtualPlan, String actualPlan) {
-        String convertedPlan = translateToVirtualPlan(actualPlan);
-        assertEquals(virtualPlan, convertedPlan);
-    }
-    
-    private static void initValues(Connection conn, String virtualName, String realName) throws Exception {
-        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-        if (virtualName.equals(JOIN_CUSTOMER_TABLE_FULL_NAME)) {
-            // Insert into customer table
-            PreparedStatement stmt = conn.prepareStatement(
-                    "upsert into " + realName +
-                    "   (\"customer_id\", " +
-                    "    NAME, " +
-                    "    PHONE, " +
-                    "    ADDRESS, " +
-                    "    LOC_ID, " +
-                    "    DATE) " +
-                    "values (?, ?, ?, ?, ?, ?)");
-            stmt.setString(1, "0000000001");
-            stmt.setString(2, "C1");
-            stmt.setString(3, "999-999-1111");
-            stmt.setString(4, "101 XXX Street");
-            stmt.setString(5, "10001");
-            stmt.setDate(6, new Date(format.parse("2013-11-01 10:20:36").getTime()));
-            stmt.execute();
-                
-            stmt.setString(1, "0000000002");
-            stmt.setString(2, "C2");
-            stmt.setString(3, "999-999-2222");
-            stmt.setString(4, "202 XXX Street");
-            stmt.setString(5, null);
-            stmt.setDate(6, new Date(format.parse("2013-11-25 16:45:07").getTime()));
-            stmt.execute();
-
-            stmt.setString(1, "0000000003");
-            stmt.setString(2, "C3");
-            stmt.setString(3, "999-999-3333");
-            stmt.setString(4, "303 XXX Street");
-            stmt.setString(5, null);
-            stmt.setDate(6, new Date(format.parse("2013-11-25 10:06:29").getTime()));
-            stmt.execute();
-
-            stmt.setString(1, "0000000004");
-            stmt.setString(2, "C4");
-            stmt.setString(3, "999-999-4444");
-            stmt.setString(4, "404 XXX Street");
-            stmt.setString(5, "10004");
-            stmt.setDate(6, new Date(format.parse("2013-11-22 14:22:56").getTime()));
-            stmt.execute();
-
-            stmt.setString(1, "0000000005");
-            stmt.setString(2, "C5");
-            stmt.setString(3, "999-999-5555");
-            stmt.setString(4, "505 XXX Street");
-            stmt.setString(5, "10005");
-            stmt.setDate(6, new Date(format.parse("2013-11-27 09:37:50").getTime()));
-            stmt.execute();
-
-            stmt.setString(1, "0000000006");
-            stmt.setString(2, "C6");
-            stmt.setString(3, "999-999-6666");
-            stmt.setString(4, "606 XXX Street");
-            stmt.setString(5, "10001");
-            stmt.setDate(6, new Date(format.parse("2013-11-01 10:20:36").getTime()));
-            stmt.execute();
-        } else if (virtualName.equals(JOIN_ITEM_TABLE_FULL_NAME)) {
-        
-            // Insert into item table
-            PreparedStatement stmt = conn.prepareStatement(
-                    "upsert into " + realName +
-                    "   (\"item_id\", " +
-                    "    NAME, " +
-                    "    PRICE, " +
-                    "    DISCOUNT1, " +
-                    "    DISCOUNT2, " +
-                    "    \"supplier_id\", " +
-                    "    DESCRIPTION) " +
-                    "values (?, ?, ?, ?, ?, ?, ?)");
-            stmt.setString(1, "0000000001");
-            stmt.setString(2, "T1");
-            stmt.setInt(3, 100);
-            stmt.setInt(4, 5);
-            stmt.setInt(5, 10);
-            stmt.setString(6, "0000000001");
-            stmt.setString(7, "Item T1");
-            stmt.execute();
-
-            stmt.setString(1, "0000000002");
-            stmt.setString(2, "T2");
-            stmt.setInt(3, 200);
-            stmt.setInt(4, 5);
-            stmt.setInt(5, 8);
-            stmt.setString(6, "0000000001");
-            stmt.setString(7, "Item T2");
-            stmt.execute();
-
-            stmt.setString(1, "0000000003");
-            stmt.setString(2, "T3");
-            stmt.setInt(3, 300);
-            stmt.setInt(4, 8);
-            stmt.setInt(5, 12);
-            stmt.setString(6, "0000000002");
-            stmt.setString(7, "Item T3");
-            stmt.execute();
-
-            stmt.setString(1, "0000000004");
-            stmt.setString(2, "T4");
-            stmt.setInt(3, 400);
-            stmt.setInt(4, 6);
-            stmt.setInt(5, 10);
-            stmt.setString(6, "0000000002");
-            stmt.setString(7, "Item T4");
-            stmt.execute();
-
-            stmt.setString(1, "0000000005");
-            stmt.setString(2, "T5");
-            stmt.setInt(3, 500);
-            stmt.setInt(4, 8);
-            stmt.setInt(5, 15);
-            stmt.setString(6, "0000000005");
-            stmt.setString(7, "Item T5");
-            stmt.execute();
-
-            stmt.setString(1, "0000000006");
-            stmt.setString(2, "T6");
-            stmt.setInt(3, 600);
-            stmt.setInt(4, 8);
-            stmt.setInt(5, 15);
-            stmt.setString(6, "0000000006");
-            stmt.setString(7, "Item T6");
-            stmt.execute();
-            
-            stmt.setString(1, "invalid001");
-            stmt.setString(2, "INVALID-1");
-            stmt.setInt(3, 0);
-            stmt.setInt(4, 0);
-            stmt.setInt(5, 0);
-            stmt.setString(6, "0000000000");
-            stmt.setString(7, "Invalid item for join test");
-            stmt.execute();
-        } else if (virtualName.equals(JOIN_SUPPLIER_TABLE_FULL_NAME)) {
-
-            // Insert into supplier table
-            PreparedStatement stmt = conn.prepareStatement(
-                    "upsert into " + realName +
-                    "   (\"supplier_id\", " +
-                    "    NAME, " +
-                    "    PHONE, " +
-                    "    ADDRESS, " +
-                    "    LOC_ID) " +
-                    "values (?, ?, ?, ?, ?)");
-            stmt.setString(1, "0000000001");
-            stmt.setString(2, "S1");
-            stmt.setString(3, "888-888-1111");
-            stmt.setString(4, "101 YYY Street");
-            stmt.setString(5, "10001");
-            stmt.execute();
-                
-            stmt.setString(1, "0000000002");
-            stmt.setString(2, "S2");
-            stmt.setString(3, "888-888-2222");
-            stmt.setString(4, "202 YYY Street");
-            stmt.setString(5, "10002");
-            stmt.execute();
-
-            stmt.setString(1, "0000000003");
-            stmt.setString(2, "S3");
-            stmt.setString(3, "888-888-3333");
-            stmt.setString(4, "303 YYY Street");
-            stmt.setString(5, null);
-            stmt.execute();
-
-            stmt.setString(1, "0000000004");
-            stmt.setString(2, "S4");
-            stmt.setString(3, "888-888-4444");
-            stmt.setString(4, "404 YYY Street");
-            stmt.setString(5, null);
-            stmt.execute();
-
-            stmt.setString(1, "0000000005");
-            stmt.setString(2, "S5");
-            stmt.setString(3, "888-888-5555");
-            stmt.setString(4, "505 YYY Street");
-            stmt.setString(5, "10005");
-            stmt.execute();
-
-            stmt.setString(1, "0000000006");
-            stmt.setString(2, "S6");
-            stmt.setString(3, "888-888-6666");
-            stmt.setString(4, "606 YYY Street");
-            stmt.setString(5, "10006");
-            stmt.execute();
-        } else if (virtualName.equals(JOIN_ORDER_TABLE_FULL_NAME)) {
-
-            // Insert into order table
-            PreparedStatement stmt = conn.prepareStatement(
-                    "upsert into " + realName +
-                    "   (\"order_id\", " +
-                    "    \"customer_id\", " +
-                    "    \"item_id\", " +
-                    "    PRICE, " +
-                    "    QUANTITY," +
-                    "    DATE) " +
-                    "values (?, ?, ?, ?, ?, ?)");
-            stmt.setString(1, "000000000000001");
-            stmt.setString(2, "0000000004");
-            stmt.setString(3, "0000000001");
-            stmt.setInt(4, 100);
-            stmt.setInt(5, 1000);
-            stmt.setTimestamp(6, new Timestamp(format.parse("2013-11-22 14:22:56").getTime()));
-            stmt.execute();
-
-            stmt.setString(1, "000000000000002");
-            stmt.setString(2, "0000000003");
-            stmt.setString(3, "0000000006");
-            stmt.setInt(4, 552);
-            stmt.setInt(5, 2000);
-            stmt.setTimestamp(6, new Timestamp(format.parse("2013-11-25 10:06:29").getTime()));
-            stmt.execute();
-
-            stmt.setString(1, "000000000000003");
-            stmt.setString(2, "0000000002");
-            stmt.setString(3, "0000000002");
-            stmt.setInt(4, 190);
-            stmt.setInt(5, 3000);
-            stmt.setTimestamp(6, new Timestamp(format.parse("2013-11-25 16:45:07").getTime()));
-            stmt.execute();
-
-            stmt.setString(1, "000000000000004");
-            stmt.setString(2, "0000000004");
-            stmt.setString(3, "0000000006");
-            stmt.setInt(4, 510);
-            stmt.setInt(5, 4000);
-            stmt.setTimestamp(6, new Timestamp(format.parse("2013-11-26 13:26:04").getTime()));
-            stmt.execute();
-
-            stmt.setString(1, "000000000000005");
-            stmt.setString(2, "0000000005");
-            stmt.setString(3, "0000000003");
-            stmt.setInt(4, 264);
-            stmt.setInt(5, 5000);
-            stmt.setTimestamp(6, new Timestamp(format.parse("2013-11-27 09:37:50").getTime()));
-            stmt.execute();
-        } else if (virtualName.equals(JOIN_COITEM_TABLE_FULL_NAME)) {
-            // Insert into coitem table
-            PreparedStatement stmt = conn.prepareStatement(
-                    "upsert into " + realName + 
-                    "   (item_id, " + 
-                    "    item_name, " + 
-                    "    co_item_id, " + 
-                    "    co_item_name) " + 
-                    "values (?, ?, ?, ?)");
-            stmt.setString(1, "0000000001");
-            stmt.setString(2, "T1");
-            stmt.setString(3, "0000000002");
-            stmt.setString(4, "T3");
-            stmt.execute();
-            
-            stmt.setString(1, "0000000004");
-            stmt.setString(2, "T4");
-            stmt.setString(3, "0000000003");
-            stmt.setString(4, "T3");
-            stmt.execute();
-            
-            stmt.setString(1, "0000000003");
-            stmt.setString(2, "T4");
-            stmt.setString(3, "0000000005");
-            stmt.setString(4, "T5");
-            stmt.execute();
-            
-            stmt.setString(1, "0000000006");
-            stmt.setString(2, "T6");
-            stmt.setString(3, "0000000001");
-            stmt.setString(4, "T1");
-            stmt.execute();
-        }
-
-        conn.commit();
-    }
-
-	protected Connection getConnection() throws SQLException {
-		Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-		props.put(ServerCacheClient.HASH_JOIN_SERVER_CACHE_RESEND_PER_SERVER, "true");
-		return DriverManager.getConnection(getUrl(), props);
-	}
-	
-    protected void createIndexes(Connection conn, String virtualName, String realName) throws Exception {
-        if (indexDDL != null && indexDDL.length > 0) {
-            for (String ddl : indexDDL) {
-                String newDDL =  ddl.replace(virtualName, realName);
-                if (!newDDL.equals(ddl)) {
-                    conn.createStatement().execute(newDDL);
-                }
-            }
-        }
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ee20a8c9/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinCacheIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinCacheIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinCacheIT.java
deleted file mode 100644
index cebb9ad..0000000
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinCacheIT.java
+++ /dev/null
@@ -1,482 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.phoenix.end2end;
-
-import java.io.IOException;
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Properties;
-import java.util.Random;
-
-import org.apache.hadoop.hbase.client.Scan;
-import org.apache.hadoop.hbase.coprocessor.ObserverContext;
-import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
-import org.apache.hadoop.hbase.coprocessor.SimpleRegionObserver;
-import org.apache.hadoop.hbase.regionserver.RegionScanner;
-import org.apache.phoenix.cache.GlobalCache;
-import org.apache.phoenix.cache.TenantCache;
-import org.apache.phoenix.coprocessor.HashJoinCacheNotFoundException;
-import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
-import org.apache.phoenix.join.HashJoinInfo;
-import org.apache.phoenix.query.QueryServices;
-import org.apache.phoenix.util.ByteUtil;
-import org.apache.phoenix.util.PropertiesUtil;
-import org.apache.phoenix.util.SchemaUtil;
-import org.apache.phoenix.util.TestUtil;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import com.google.common.collect.Lists;
-
-import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
-import static org.junit.Assert.fail;
-
-@RunWith(Parameterized.class)
-public class HashJoinCacheIT extends HashJoinIT {
-    
-    public HashJoinCacheIT(String[] indexDDL, String[] plans) throws Exception {
-        super(indexDDL, plans);
-    }
-    
-    protected String getTableName(Connection conn, String virtualName) throws Exception {
-        String realName = super.getTableName(conn, virtualName);
-        TestUtil.addCoprocessor(conn, SchemaUtil.normalizeFullTableName(realName), InvalidateHashCache.class);
-        return realName;
-    }
-        
-    @Parameters
-    public static Collection<Object> data() {
-        List<Object> testCases = Lists.newArrayList();
-        testCases.add(new String[][] {
-            {}, {
-            /* 
-             * testLeftJoinWithAggregation()
-             *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
-             *     LEFT JOIN joinItemTable i ON o.item_id = i.item_id 
-             *     GROUP BY i.name ORDER BY i.name
-             */     
-            "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-            "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.NAME]\n" +
-            "CLIENT MERGE SORT\n" +
-            "    PARALLEL LEFT-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME,
-            /* 
-             * testLeftJoinWithAggregation()
-             *     SELECT i.item_id iid, sum(quantity) q FROM joinOrderTable o 
-             *     LEFT JOIN joinItemTable i ON o.item_id = i.item_id 
-             *     GROUP BY i.item_id ORDER BY q DESC"
-             */     
-            "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-            "    SERVER AGGREGATE INTO DISTINCT ROWS BY [\"I.item_id\"]\n" +
-            "CLIENT MERGE SORT\n" +
-            "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
-            "    PARALLEL LEFT-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "            SERVER FILTER BY FIRST KEY ONLY",
-            /* 
-             * testLeftJoinWithAggregation()
-             *     SELECT i.item_id iid, sum(quantity) q FROM joinItemTable i 
-             *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id 
-             *     GROUP BY i.item_id ORDER BY q DESC NULLS LAST, iid
-             */     
-            "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "    SERVER FILTER BY FIRST KEY ONLY\n" +
-            "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [\"I.item_id\"]\n" +
-            "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, \"I.item_id\"]\n" +
-            "    PARALLEL LEFT-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
-            /* 
-             * testRightJoinWithAggregation()
-             *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
-             *     RIGHT JOIN joinItemTable i ON o.item_id = i.item_id 
-             *     GROUP BY i.name ORDER BY i.name
-             */
-            "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.NAME]\n" +
-            "CLIENT MERGE SORT\n" +
-            "    PARALLEL LEFT-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
-            /*
-             * testRightJoinWithAggregation()
-             *     SELECT i.item_id iid, sum(quantity) q FROM joinOrderTable o 
-             *     RIGHT JOIN joinItemTable i ON o.item_id = i.item_id 
-             *     GROUP BY i.item_id ORDER BY q DESC NULLS LAST, iid
-             */
-            "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "    SERVER FILTER BY FIRST KEY ONLY\n" +
-            "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [\"I.item_id\"]\n" +
-            "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, \"I.item_id\"]\n" +
-            "    PARALLEL LEFT-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
-            /*
-             * testJoinWithWildcard()
-             *     SELECT * FROM joinItemTable LEFT JOIN joinSupplierTable supp 
-             *     ON joinItemTable.supplier_id = supp.supplier_id 
-             *     ORDER BY item_id
-             */
-            "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" + 
-            "    PARALLEL LEFT-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME,
-            /*
-             * testJoinPlanWithIndex()
-             *     SELECT item.item_id, item.name, supp.supplier_id, supp.name 
-             *     FROM joinItemTable item LEFT JOIN joinSupplierTable supp 
-             *     ON substr(item.name, 2, 1) = substr(supp.name, 2, 1) 
-             *         AND (supp.name BETWEEN 'S1' AND 'S5') 
-             *     WHERE item.name BETWEEN 'T1' AND 'T5'
-             */
-            "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "    SERVER FILTER BY (NAME >= 'T1' AND NAME <= 'T5')\n" +
-            "    PARALLEL LEFT-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-            "            SERVER FILTER BY (NAME >= 'S1' AND NAME <= 'S5')",
-            /*
-             * testJoinPlanWithIndex()
-             *     SELECT item.item_id, item.name, supp.supplier_id, supp.name 
-             *     FROM joinItemTable item INNER JOIN joinSupplierTable supp 
-             *     ON item.supplier_id = supp.supplier_id 
-             *     WHERE (item.name = 'T1' OR item.name = 'T5') 
-             *         AND (supp.name = 'S1' OR supp.name = 'S5')
-             */
-            "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "    SERVER FILTER BY (NAME = 'T1' OR NAME = 'T5')\n" +
-            "    PARALLEL INNER-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-            "            SERVER FILTER BY (NAME = 'S1' OR NAME = 'S5')",
-            /*
-             * testJoinWithSkipMergeOptimization()
-             *     SELECT s.name FROM joinItemTable i 
-             *     JOIN joinOrderTable o ON o.item_id = i.item_id AND quantity < 5000 
-             *     JOIN joinSupplierTable s ON i.supplier_id = s.supplier_id
-             */
-            "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "    PARALLEL INNER-JOIN TABLE 0 (SKIP MERGE)\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-            "            SERVER FILTER BY QUANTITY < 5000\n" +
-            "    PARALLEL INNER-JOIN TABLE 1\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-            "    DYNAMIC SERVER FILTER BY \"I.item_id\" IN (\"O.item_id\")",
-            /*
-             * testSelfJoin()
-             *     SELECT i2.item_id, i1.name FROM joinItemTable i1 
-             *     JOIN joinItemTable i2 ON i1.item_id = i2.item_id 
-             *     ORDER BY i1.item_id
-             */
-            "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "    PARALLEL INNER-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "            SERVER FILTER BY FIRST KEY ONLY\n" +
-            "    DYNAMIC SERVER FILTER BY \"I1.item_id\" IN (\"I2.item_id\")",
-            /*
-             * testSelfJoin()
-             *     SELECT i1.name, i2.name FROM joinItemTable i1 
-             *     JOIN joinItemTable i2 ON i1.item_id = i2.supplier_id 
-             *     ORDER BY i1.name, i2.name
-             */
-            "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "    SERVER SORTED BY [I1.NAME, I2.NAME]\n" +
-            "CLIENT MERGE SORT\n" +
-            "    PARALLEL INNER-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "    DYNAMIC SERVER FILTER BY \"I1.item_id\" IN (\"I2.supplier_id\")",
-            /*
-             * testStarJoin()
-             *     SELECT order_id, c.name, i.name iname, quantity, o.date 
-             *     FROM joinOrderTable o 
-             *     JOIN joinCustomerTable c ON o.customer_id = c.customer_id 
-             *     JOIN joinItemTable i ON o.item_id = i.item_id 
-             *     ORDER BY order_id
-             */
-            "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-            "    PARALLEL INNER-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + "\n" +
-            "    PARALLEL INNER-JOIN TABLE 1\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME,
-            /*
-             * testStarJoin()
-             *     SELECT (*NO_STAR_JOIN*) order_id, c.name, i.name iname, quantity, o.date 
-             *     FROM joinOrderTable o 
-             *     JOIN joinCustomerTable c ON o.customer_id = c.customer_id 
-             *     JOIN joinItemTable i ON o.item_id = i.item_id 
-             *     ORDER BY order_id
-             */
-            "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "    SERVER SORTED BY [\"O.order_id\"]\n" +
-            "CLIENT MERGE SORT\n" +
-            "    PARALLEL INNER-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-            "            PARALLEL INNER-JOIN TABLE 0\n" +
-            "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + "\n" +
-            "    DYNAMIC SERVER FILTER BY \"I.item_id\" IN (\"O.item_id\")",
-            /*
-             * testSubJoin()
-             *     SELECT * FROM joinCustomerTable c 
-             *     INNER JOIN (joinOrderTable o 
-             *         INNER JOIN (joinSupplierTable s 
-             *             RIGHT JOIN joinItemTable i ON i.supplier_id = s.supplier_id)
-             *         ON o.item_id = i.item_id)
-             *     ON c.customer_id = o.customer_id
-             *     WHERE c.customer_id <= '0000000005' 
-             *         AND order_id != '000000000000003' 
-             *         AND i.name != 'T3' 
-             *     ORDER BY c.customer_id, i.name
-             */
-            "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + " [*] - ['0000000005']\n" +
-            "    SERVER SORTED BY [\"C.customer_id\", I.NAME]\n" +
-            "CLIENT MERGE SORT\n" +
-            "    PARALLEL INNER-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-            "            SERVER FILTER BY \"order_id\" != '000000000000003'\n" +
-            "            PARALLEL INNER-JOIN TABLE 0\n" +
-            "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "                    SERVER FILTER BY NAME != 'T3'\n" +
-            "                    PARALLEL LEFT-JOIN TABLE 0\n" +
-            "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-            "    DYNAMIC SERVER FILTER BY \"C.customer_id\" IN (\"O.customer_id\")",
-            /* 
-             * testJoinWithSubqueryAndAggregation()
-             *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
-             *     LEFT JOIN (SELECT name, item_id iid FROM joinItemTable) AS i 
-             *     ON o.item_id = i.iid 
-             *     GROUP BY i.name ORDER BY i.name
-             */     
-            "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-            "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.NAME]\n" +
-            "CLIENT MERGE SORT\n" +
-            "    PARALLEL LEFT-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME,
-            /* 
-             * testJoinWithSubqueryAndAggregation()
-             *     SELECT o.iid, sum(o.quantity) q 
-             *     FROM (SELECT item_id iid, quantity FROM joinOrderTable) AS o 
-             *     LEFT JOIN (SELECT item_id FROM joinItemTable) AS i 
-             *     ON o.iid = i.item_id 
-             *     GROUP BY o.iid ORDER BY q DESC                 
-             */     
-            "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-            "    SERVER AGGREGATE INTO DISTINCT ROWS BY [O.IID]\n" +
-            "CLIENT MERGE SORT\n" +
-            "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
-            "    PARALLEL LEFT-JOIN TABLE 0 (SKIP MERGE)\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "            SERVER FILTER BY FIRST KEY ONLY",
-            /* 
-             * testJoinWithSubqueryAndAggregation()
-             *     SELECT i.iid, o.q 
-             *     FROM (SELECT item_id iid FROM joinItemTable) AS i 
-             *     LEFT JOIN (SELECT item_id iid, sum(quantity) q FROM joinOrderTable GROUP BY item_id) AS o 
-             *     ON o.iid = i.iid 
-             *     ORDER BY o.q DESC NULLS LAST, i.iid
-             */     
-            "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "    SERVER FILTER BY FIRST KEY ONLY\n" +
-            "    SERVER SORTED BY [O.Q DESC NULLS LAST, I.IID]\n" +
-            "CLIENT MERGE SORT\n" +
-            "    PARALLEL LEFT-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-            "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
-            "        CLIENT MERGE SORT",
-            /* 
-             * testJoinWithSubqueryAndAggregation()
-             *     SELECT i.iid, o.q 
-             *     FROM (SELECT item_id iid, sum(quantity) q FROM joinOrderTable GROUP BY item_id) AS o 
-             *     JOIN (SELECT item_id iid FROM joinItemTable) AS i 
-             *     ON o.iid = i.iid 
-             *     ORDER BY o.q DESC, i.iid
-             */     
-            "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "    SERVER FILTER BY FIRST KEY ONLY\n" +
-            "    SERVER SORTED BY [O.Q DESC, I.IID]\n" +
-            "CLIENT MERGE SORT\n" +
-            "    PARALLEL INNER-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-            "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
-            "        CLIENT MERGE SORT",
-            /*
-             * testNestedSubqueries()
-             *     SELECT * FROM (SELECT customer_id cid, name, phone, address, loc_id, date FROM joinCustomerTable) AS c 
-             *     INNER JOIN (SELECT o.oid ooid, o.cid ocid, o.iid oiid, o.price * o.quantity, o.date odate, 
-             *     qi.iiid iiid, qi.iname iname, qi.iprice iprice, qi.idiscount1 idiscount1, qi.idiscount2 idiscount2, qi.isid isid, qi.idescription idescription, 
-             *     qi.ssid ssid, qi.sname sname, qi.sphone sphone, qi.saddress saddress, qi.sloc_id sloc_id 
-             *         FROM (SELECT item_id iid, customer_id cid, order_id oid, price, quantity, date FROM joinOrderTable) AS o 
-             *         INNER JOIN (SELECT i.iid iiid, i.name iname, i.price iprice, i.discount1 idiscount1, i.discount2 idiscount2, i.sid isid, i.description idescription, 
-             *         s.sid ssid, s.name sname, s.phone sphone, s.address saddress, s.loc_id sloc_id 
-             *             FROM (SELECT supplier_id sid, name, phone, address, loc_id FROM joinSupplierTable) AS s 
-             *             RIGHT JOIN (SELECT item_id iid, name, price, discount1, discount2, supplier_id sid, description FROM joinItemTable) AS i 
-             *             ON i.sid = s.sid) as qi 
-             *         ON o.iid = qi.iiid) as qo 
-             *     ON c.cid = qo.ocid 
-             *     WHERE c.cid <= '0000000005' 
-             *         AND qo.ooid != '000000000000003' 
-             *         AND qo.iname != 'T3' 
-             *     ORDER BY c.cid, qo.iname
-             */
-            "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + " [*] - ['0000000005']\n" +
-            "    SERVER SORTED BY [C.CID, QO.INAME]\n" +
-            "CLIENT MERGE SORT\n" +
-            "    PARALLEL INNER-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-            "            SERVER FILTER BY \"order_id\" != '000000000000003'\n" +
-            "            PARALLEL INNER-JOIN TABLE 0\n" +
-            "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "                    SERVER FILTER BY NAME != 'T3'\n" +
-            "                    PARALLEL LEFT-JOIN TABLE 0\n" +
-            "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME,
-            /*
-             * testJoinWithLimit()
-             *     SELECT order_id, i.name, s.name, s.address, quantity 
-             *     FROM joinSupplierTable s 
-             *     LEFT JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
-             *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 4
-             */
-            "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-            "    SERVER 4 ROW LIMIT\n" +
-            "CLIENT 4 ROW LIMIT\n" +
-            "    PARALLEL LEFT-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "    PARALLEL LEFT-JOIN TABLE 1(DELAYED EVALUATION)\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-            "    JOIN-SCANNER 4 ROW LIMIT",
-            /*
-             * testJoinWithLimit()
-             *     SELECT order_id, i.name, s.name, s.address, quantity 
-             *     FROM joinSupplierTable s 
-             *     JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
-             *     JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 4
-             */
-            "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-            "CLIENT 4 ROW LIMIT\n" +
-            "    PARALLEL INNER-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "    PARALLEL INNER-JOIN TABLE 1(DELAYED EVALUATION)\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-            "    DYNAMIC SERVER FILTER BY \"S.supplier_id\" IN (\"I.supplier_id\")\n" +
-            "    JOIN-SCANNER 4 ROW LIMIT",
-            /*
-             * testJoinWithSetMaxRows()
-             *     statement.setMaxRows(4);
-             *     SELECT order_id, i.name, quantity FROM joinItemTable i
-             *     JOIN joinOrderTable o ON o.item_id = i.item_id;
-             *     SELECT o.order_id, i.name, o.quantity FROM joinItemTable i
-             *     JOIN (SELECT order_id, item_id, quantity FROM joinOrderTable) o
-             *     ON o.item_id = i.item_id;
-             */
-            "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "CLIENT 4 ROW LIMIT\n" +
-            "    PARALLEL INNER-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-            "    DYNAMIC SERVER FILTER BY \"I.item_id\" IN (\"O.item_id\")\n" +
-            "    JOIN-SCANNER 4 ROW LIMIT",
-            /*
-             * testJoinWithOffset()
-             *     SELECT order_id, i.name, s.name, s.address, quantity 
-             *     FROM joinSupplierTable s 
-             *     LEFT JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
-             *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 1 OFFSET 2
-             */
-            "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-            "    SERVER OFFSET 2\n" +
-            "    SERVER 3 ROW LIMIT\n" +
-            "CLIENT 1 ROW LIMIT\n" +
-            "    PARALLEL LEFT-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "    PARALLEL LEFT-JOIN TABLE 1(DELAYED EVALUATION)\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-            "    JOIN-SCANNER 3 ROW LIMIT",
-            /*
-             * testJoinWithOffset()
-             *     SELECT order_id, i.name, s.name, s.address, quantity 
-             *     FROM joinSupplierTable s 
-             *     JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
-             *     JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 1 OFFSET 2
-             */
-            "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
-            "    SERVER OFFSET 2\n" +
-            "CLIENT 1 ROW LIMIT\n" +
-            "    PARALLEL INNER-JOIN TABLE 0\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME + "\n" +
-            "    PARALLEL INNER-JOIN TABLE 1(DELAYED EVALUATION)\n" +
-            "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
-            "    DYNAMIC SERVER FILTER BY \"S.supplier_id\" IN (\"I.supplier_id\")\n" +
-            "    JOIN-SCANNER 3 ROW LIMIT",
-            }});
-        return testCases;
-    }
-    
-    @Test
-    public void testInnerJoin() throws Exception {
-		// it involves sequences which may be incremented on re-try when hash
-		// cache is removed so this test may flap sometimes, so we don't need to
-		// test it for this case.	
-    }
-
-	@Test
-	public void testUpsertWithJoin() throws Exception {
-		// TODO: We will enable this test once PHOENIX-3163
-	}
-
-    @Test
-    public void testExpiredCache() throws Exception {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        props.setProperty(QueryServices.MAX_SERVER_CACHE_TIME_TO_LIVE_MS_ATTRIB, "1");
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        String tableName1 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
-        String tableName2 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
-        String query = "SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " +
-                tableName1 + " supp RIGHT JOIN " + tableName2 +
-                " item ON item.\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
-        try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            ResultSet rs = statement.executeQuery();
-            rs.next();
-            fail("HashJoinCacheNotFoundException was not thrown or incorrectly handled");
-        } catch (HashJoinCacheNotFoundException e) {
-            //Expected exception
-        }
-    }
-
-    public static class InvalidateHashCache extends SimpleRegionObserver {
-        public static Random rand= new Random();
-        public static List<ImmutableBytesPtr> lastRemovedJoinIds=new ArrayList<ImmutableBytesPtr>();
-        @Override
-        public RegionScanner preScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c, final Scan scan,
-                final RegionScanner s) throws IOException {
-            final HashJoinInfo joinInfo = HashJoinInfo.deserializeHashJoinFromScan(scan);
-            if (joinInfo != null) {
-                TenantCache cache = GlobalCache.getTenantCache(c.getEnvironment(), null);
-                int count = joinInfo.getJoinIds().length;
-                for (int i = 0; i < count; i++) {
-                    ImmutableBytesPtr joinId = joinInfo.getJoinIds()[i];
-                    if (!ByteUtil.contains(lastRemovedJoinIds,joinId)) {
-                        lastRemovedJoinIds.add(joinId);
-                        cache.removeServerCache(joinId);
-                    }
-                }
-            }
-            return s;
-        }
-        
-    }
-}


[22/25] phoenix git commit: PHOENIX-4249 Decrease unique name allocation for SequenceIT

Posted by ja...@apache.org.
PHOENIX-4249 Decrease unique name allocation for SequenceIT


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

Branch: refs/heads/master
Commit: 3593ec8bc0042388f119652dc8177efc05f39d80
Parents: 3d9adc6
Author: James Taylor <jt...@salesforce.com>
Authored: Thu Sep 28 17:17:24 2017 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Thu Sep 28 18:23:44 2017 -0700

----------------------------------------------------------------------
 .../org/apache/phoenix/end2end/SequenceIT.java  | 191 ++++++++++---------
 .../java/org/apache/phoenix/query/BaseTest.java |  14 ++
 2 files changed, 112 insertions(+), 93 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/3593ec8b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceIT.java
index e226491..bcb3a40 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceIT.java
@@ -61,11 +61,16 @@ import com.google.common.collect.Maps;
 public class SequenceIT extends ParallelStatsDisabledIT {
     private static final String SELECT_NEXT_VALUE_SQL = "SELECT NEXT VALUE FOR %s";
     private static final long BATCH_SIZE = 3;
+    private static final String SCHEMA_NAME = "S";
    
     private Connection conn;
     
-    private static String generateNameWithSchema() {
-    	return SchemaUtil.getTableName(generateUniqueName(), generateUniqueName());
+    private static String generateTableNameWithSchema() {
+    	return SchemaUtil.getTableName(SCHEMA_NAME, generateUniqueName());
+    }
+    
+    private static String generateSequenceNameWithSchema() {
+        return SchemaUtil.getTableName(SCHEMA_NAME, generateUniqueSequenceName());
     }
     
     @BeforeClass
@@ -91,7 +96,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
 	@Test
 	public void testSystemTable() throws Exception {		
-		conn.createStatement().execute("CREATE SEQUENCE " + generateNameWithSchema());
+		conn.createStatement().execute("CREATE SEQUENCE " + generateSequenceNameWithSchema());
 		String query = "SELECT sequence_schema, sequence_name, current_value, increment_by FROM \"SYSTEM\".\"SEQUENCE\"";
 		ResultSet rs = conn.prepareStatement(query).executeQuery();
 		assertTrue(rs.next());
@@ -99,7 +104,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
 	@Test
 	public void testDuplicateSequences() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         
         
 		conn.createStatement().execute("CREATE SEQUENCE " + sequenceName + " START WITH 2 INCREMENT BY 4\n");
@@ -114,7 +119,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
 	@Test
 	public void testSequenceNotFound() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
 		
         
 		String query = "SELECT NEXT value FOR " + sequenceName ;
@@ -132,7 +137,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
         props.setProperty(QueryServices.IS_NAMESPACE_MAPPING_ENABLED, Boolean.toString(true));
         Connection nsConn = DriverManager.getConnection(getUrl(), props);
 
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         String sequenceSchemaName = getSchemaName(sequenceName);
 
         try {
@@ -169,7 +174,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 	
 	@Test
     public void testCreateSequence() throws Exception { 
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         String sequenceNameWithoutSchema = getNameWithoutSchema(sequenceName);
         String schemaName = getSchemaName(sequenceName);
         
@@ -186,7 +191,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
     
     @Test
     public void testCurrentValueFor() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         ResultSet rs;
         
         conn.createStatement().execute("CREATE SEQUENCE " + sequenceName + " START WITH 2 INCREMENT BY 4");
@@ -210,7 +215,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testDropSequence() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         String sequenceNameWithoutSchema = getNameWithoutSchema(sequenceName);
         String schemaName = getSchemaName(sequenceName);
         
@@ -238,15 +243,15 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
 	@Test
 	public void testSelectNextValueFor() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
 		conn.createStatement().execute("CREATE SEQUENCE " + sequenceName + " START WITH 3 INCREMENT BY 2");
         assertSequenceValuesForSingleRow(sequenceName, 3, 5, 7);
 	}
 
 	@Test
 	public void testInsertNextValueFor() throws Exception {
-        String sequenceName = generateNameWithSchema();
-        String tableName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
+        String tableName = generateTableNameWithSchema();
         
         conn.createStatement().execute("CREATE SEQUENCE " + sequenceName + " START WITH 2 INCREMENT BY 1");
 		conn.createStatement().execute("CREATE TABLE " + tableName + " ( id INTEGER NOT NULL PRIMARY KEY)");
@@ -263,7 +268,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSequenceCreation() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         String sequenceNameWithoutSchema = getNameWithoutSchema(sequenceName);
         String schemaName = getSchemaName(sequenceName);
         
@@ -307,7 +312,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSameMultipleSequenceValues() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         
         conn.createStatement().execute("CREATE SEQUENCE " + sequenceName + " START WITH 4 INCREMENT BY 7");
         String query = "SELECT NEXT VALUE FOR " + sequenceName + ", NEXT VALUE FOR " + sequenceName ;
@@ -321,8 +326,8 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testMultipleSequenceValues() throws Exception {
-        String sequenceName = generateNameWithSchema();
-        String alternateSequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
+        String alternateSequenceName = generateSequenceNameWithSchema();
     	
         conn.createStatement().execute("CREATE SEQUENCE " + sequenceName + " START WITH 4 INCREMENT BY 7");
         conn.createStatement().execute("CREATE SEQUENCE " + alternateSequenceName + " START WITH 9 INCREMENT BY 2");
@@ -352,7 +357,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
     
     @Test
     public void testMultipleSequencesNoCycle() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         String sequenceNameWithoutSchema = getNameWithoutSchema(sequenceName);
         String schemaName = getSchemaName(sequenceName);
         String alternateSequenceName = sequenceName + "_ALT";
@@ -394,8 +399,8 @@ public class SequenceIT extends ParallelStatsDisabledIT {
     
     @Test
     public void testMultipleSequencesCycle() throws Exception {
-        String sequenceName = generateNameWithSchema();
-        String alternateSequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
+        String alternateSequenceName = generateSequenceNameWithSchema();
     	
         conn.createStatement().execute(
             "CREATE SEQUENCE " + sequenceName + " START WITH 4 INCREMENT BY 7 MINVALUE 4 MAXVALUE 19 CYCLE");
@@ -424,8 +429,8 @@ public class SequenceIT extends ParallelStatsDisabledIT {
     
 	@Test
 	public void testCompilerOptimization() throws Exception {
-        String sequenceName = generateNameWithSchema();
-        String tableName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
+        String tableName = generateTableNameWithSchema();
 		
         conn.createStatement().execute("CREATE SEQUENCE " + sequenceName + " START WITH 3 INCREMENT BY 2");        
 		conn.createStatement().execute("CREATE TABLE " + tableName + " (k INTEGER NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR) IMMUTABLE_ROWS=true");
@@ -438,8 +443,8 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 	
 	@Test
 	public void testSelectRowAndSequence() throws Exception {
-        String sequenceName = generateNameWithSchema();
-        String tableName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
+        String tableName = generateTableNameWithSchema();
         conn.createStatement().execute("CREATE SEQUENCE " + sequenceName + " START WITH 1 INCREMENT BY 4");
 		conn.createStatement().execute("CREATE TABLE " + tableName + " ( id INTEGER NOT NULL PRIMARY KEY)");
         
@@ -456,8 +461,8 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSelectNextValueForOverMultipleBatches() throws Exception {
-        String sequenceName = generateNameWithSchema();
-        String tableName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
+        String tableName = generateTableNameWithSchema();
 
         conn.createStatement().execute("CREATE SEQUENCE " + sequenceName);
         conn.createStatement().execute("CREATE TABLE " + tableName + " (k BIGINT NOT NULL PRIMARY KEY)");
@@ -476,15 +481,15 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSelectNextValueForGroupBy() throws Exception {
-        String sequenceName = generateNameWithSchema();
-        String tableName = generateNameWithSchema();
-        String SECOND_tableName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
+        String tableName1 = generateTableNameWithSchema();
+        String tableName2 = generateTableNameWithSchema();
     	
         conn.createStatement().execute("CREATE SEQUENCE " + sequenceName);
-        conn.createStatement().execute("CREATE TABLE " + tableName + " (k BIGINT NOT NULL PRIMARY KEY, v VARCHAR)");
-        conn.createStatement().execute("CREATE TABLE "+ SECOND_tableName + " (k BIGINT NOT NULL PRIMARY KEY, v VARCHAR)");
+        conn.createStatement().execute("CREATE TABLE " + tableName1 + " (k BIGINT NOT NULL PRIMARY KEY, v VARCHAR)");
+        conn.createStatement().execute("CREATE TABLE "+ tableName2 + " (k BIGINT NOT NULL PRIMARY KEY, v VARCHAR)");
         
-        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + tableName + " VALUES(NEXT VALUE FOR " + sequenceName + ", ?)");
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + tableName1 + " VALUES(NEXT VALUE FOR " + sequenceName + ", ?)");
         stmt.setString(1, "a");
         stmt.execute();
         stmt.setString(1, "a");
@@ -497,7 +502,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
         stmt.execute();
         conn.commit();
         
-        ResultSet rs = conn.createStatement().executeQuery("SELECT k from " + tableName );
+        ResultSet rs = conn.createStatement().executeQuery("SELECT k from " + tableName1 );
         assertTrue(rs.next());
         assertEquals(1, rs.getInt(1));
         assertTrue(rs.next());
@@ -512,9 +517,9 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
         
         conn.setAutoCommit(true);;
-        conn.createStatement().execute("UPSERT INTO " + SECOND_tableName + " SELECT NEXT VALUE FOR " + sequenceName + ",v FROM " + tableName + " GROUP BY v");
+        conn.createStatement().execute("UPSERT INTO " + tableName2 + " SELECT NEXT VALUE FOR " + sequenceName + ",v FROM " + tableName1 + " GROUP BY v");
         
-        rs = conn.createStatement().executeQuery("SELECT * from " + SECOND_tableName);
+        rs = conn.createStatement().executeQuery("SELECT * from " + tableName2);
         assertTrue(rs.next());
         assertEquals(6, rs.getInt(1));
         assertEquals("a", rs.getString(2));
@@ -529,8 +534,8 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSelectNextValueForMultipleConn() throws Exception {
-        String sequenceName = generateNameWithSchema();
-        String tableName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
+        String tableName = generateTableNameWithSchema();
 
         conn.createStatement().execute("CREATE SEQUENCE " + sequenceName);
         conn.createStatement().execute("CREATE TABLE " + tableName + " (k BIGINT NOT NULL PRIMARY KEY)");
@@ -563,8 +568,8 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSelectNextValueForMultipleConnWithStmtClose() throws Exception {
-        String sequenceName = generateNameWithSchema();
-        String tableName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
+        String tableName = generateTableNameWithSchema();
         
         conn.createStatement().execute("CREATE SEQUENCE " + sequenceName);
         conn.createStatement().execute("CREATE TABLE " + tableName + " (k BIGINT NOT NULL PRIMARY KEY)");
@@ -593,8 +598,8 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSelectNextValueForMultipleConnWithConnClose() throws Exception {
-        String sequenceName = generateNameWithSchema();
-        String tableName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
+        String tableName = generateTableNameWithSchema();
         
         conn.createStatement().execute("CREATE SEQUENCE " + sequenceName);
         conn.createStatement().execute("CREATE TABLE " + tableName + " (k BIGINT NOT NULL PRIMARY KEY)");
@@ -632,9 +637,9 @@ public class SequenceIT extends ParallelStatsDisabledIT {
     }
     
     private void testDropCachedSeq(boolean detectDeleteSeqInEval) throws Exception {
-        String sequenceName = generateNameWithSchema();
-        String alternateSequenceName = generateNameWithSchema();
-        String tableName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
+        String alternateSequenceName = generateSequenceNameWithSchema();
+        String tableName = generateTableNameWithSchema();
         
         conn.createStatement().execute("CREATE SEQUENCE " + sequenceName);
         conn.createStatement().execute("CREATE SEQUENCE " + alternateSequenceName + " START WITH 101");
@@ -690,9 +695,9 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testExplainPlanValidatesSequences() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         String sequenceNameWithoutSchema = getNameWithoutSchema(sequenceName);
-        String tableName = generateNameWithSchema();
+        String tableName = generateTableNameWithSchema();
         
         conn.createStatement().execute("CREATE SEQUENCE " + sequenceName);
         conn.createStatement().execute("CREATE TABLE " + tableName + " (k BIGINT NOT NULL PRIMARY KEY)");
@@ -731,12 +736,12 @@ public class SequenceIT extends ParallelStatsDisabledIT {
     }
     
     private String generateSequenceName() {
-    	return generateUniqueName();
+    	return generateUniqueSequenceName();
     }
     
     @Test
     public void testSelectNextValueInArithmetic() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         
         conn.createStatement().execute("CREATE SEQUENCE " + sequenceName + " START WITH 3 INCREMENT BY 2");
         
@@ -753,7 +758,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
     
     @Test
     public void testSequenceDefault() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
             
         conn.createStatement().execute("CREATE SEQUENCE " + sequenceName);
         
@@ -787,8 +792,8 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSequenceValidateStartValue() throws Exception {
-        String sequenceName = generateNameWithSchema();
-        String alternateSequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
+        String alternateSequenceName = generateSequenceNameWithSchema();
         
         try {
             conn.createStatement().execute(
@@ -813,7 +818,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSequenceValidateMinValue() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         
         try {
             conn.createStatement().execute("CREATE SEQUENCE " + sequenceName + " MINVALUE abc");
@@ -827,7 +832,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSequenceValidateMaxValue() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         
         try {
             conn.createStatement().execute("CREATE SEQUENCE " + sequenceName + " MAXVALUE null");
@@ -841,7 +846,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSequenceValidateMinValueLessThanOrEqualToMaxValue() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
     	
         
         try {
@@ -857,7 +862,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSequenceValidateIncrementConstant() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         
         try {
             conn.createStatement().execute("CREATE SEQUENCE " + sequenceName + " INCREMENT null");
@@ -871,7 +876,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSequenceValidateIncrementNotEqualToZero() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         
         try {
             conn.createStatement().execute("CREATE SEQUENCE " + sequenceName + " INCREMENT 0");
@@ -885,7 +890,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
     
     @Test
     public void testSequenceStartWithMinMaxSameValueIncreasingCycle() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         
         conn.createStatement()
                 .execute(
@@ -896,7 +901,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
     
     @Test
     public void testSequenceStartWithMinMaxSameValueDecreasingCycle() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         
         conn.createStatement()
                 .execute(
@@ -907,7 +912,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
     
     @Test
     public void testSequenceStartWithMinMaxSameValueIncreasingNoCycle() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
     	
         
         conn.createStatement()
@@ -928,7 +933,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
     
     @Test
     public void testSequenceStartWithMinMaxSameValueDecreasingNoCycle() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         
         conn.createStatement()
                 .execute(
@@ -948,7 +953,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSequenceIncreasingCycle() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         
         conn.createStatement()
                 .execute(
@@ -958,7 +963,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSequenceDecreasingCycle() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         
         conn.createStatement()
                 .execute(
@@ -968,7 +973,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSequenceIncreasingNoCycle() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
     	
         
         // client throws exception
@@ -988,7 +993,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSequenceIncreasingUsingMaxValueNoCycle() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         
         // server throws exception
         conn.createStatement().execute(
@@ -1007,7 +1012,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSequenceDecreasingNoCycle() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         
         // client will throw exception
         conn.createStatement()
@@ -1027,7 +1032,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSequenceDecreasingUsingMinValueNoCycle() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         
         // server will throw exception
         conn.createStatement().execute(
@@ -1046,7 +1051,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSequenceIncreasingOverflowNoCycle() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         
         // start with Long.MAX_VALUE
         conn.createStatement().execute(
@@ -1066,7 +1071,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSequenceIncreasingOverflowCycle() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         
         // start with Long.MAX_VALUE
         conn.createStatement()
@@ -1078,7 +1083,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSequenceDecreasingOverflowNoCycle() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         
         // start with Long.MIN_VALUE + 1
         conn.createStatement().execute(
@@ -1097,7 +1102,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testSequenceDecreasingOverflowCycle() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         
         // start with Long.MIN_VALUE + 1
         conn.createStatement()
@@ -1109,8 +1114,8 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testMultipleSequenceValuesNoCycle() throws Exception {
-        String sequenceName = generateNameWithSchema();
-        String alternateSequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
+        String alternateSequenceName = generateSequenceNameWithSchema();
         
         conn.createStatement().execute(
             "CREATE SEQUENCE " + sequenceName + " START WITH 1 INCREMENT BY 2 MINVALUE 1 MAXVALUE 10 CACHE 2");
@@ -1144,8 +1149,8 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testMultipleSequenceValuesCycle() throws Exception {
-        String sequenceName = generateNameWithSchema();
-        String alternateSequenceName = generateNameWithSchema();        
+        String sequenceName = generateSequenceNameWithSchema();
+        String alternateSequenceName = generateSequenceNameWithSchema();        
                 conn.createStatement()
                 .execute(
                     "CREATE SEQUENCE " + sequenceName + " START WITH 1 INCREMENT BY 2 MINVALUE 1 MAXVALUE 10 CYCLE CACHE 2");
@@ -1161,36 +1166,36 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testUpsertSelectGroupByWithSequence() throws Exception {
-        String sequenceName = generateNameWithSchema();
-        String tableName = generateNameWithSchema();
-        String SECOND_tableName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
+        String tableName1 = generateTableNameWithSchema();
+        String tableName2 = generateTableNameWithSchema();
         
         conn.createStatement().execute("CREATE SEQUENCE " + sequenceName);
 
         conn.createStatement()
                 .execute(
-                    "CREATE TABLE " + tableName +  "(event_id BIGINT NOT NULL PRIMARY KEY, user_id char(15), val BIGINT )");
+                    "CREATE TABLE " + tableName1 +  "(event_id BIGINT NOT NULL PRIMARY KEY, user_id char(15), val BIGINT )");
         conn.createStatement()
                 .execute(
-                    "CREATE TABLE " + SECOND_tableName + " (metric_id char(15) NOT NULL PRIMARY KEY, agg_id char(15), metric_val INTEGER )");
+                    "CREATE TABLE " + tableName2 + " (metric_id char(15) NOT NULL PRIMARY KEY, agg_id char(15), metric_val INTEGER )");
 
         
         // 2 rows for user1, 3 rows for user2 and 1 row for user3
-        insertEvent(tableName, 1, "user1", 1);
-        insertEvent(tableName, 2, "user2", 1);
-        insertEvent(tableName, 3, "user1", 1);
-        insertEvent(tableName, 4, "user2", 1);
-        insertEvent(tableName, 5, "user2", 1);
-        insertEvent(tableName, 6, "user3", 1);
+        insertEvent(tableName1, 1, "user1", 1);
+        insertEvent(tableName1, 2, "user2", 1);
+        insertEvent(tableName1, 3, "user1", 1);
+        insertEvent(tableName1, 4, "user2", 1);
+        insertEvent(tableName1, 5, "user2", 1);
+        insertEvent(tableName1, 6, "user3", 1);
         conn.commit();
 
         conn.createStatement()
                 .execute(
-                    "UPSERT INTO " + SECOND_tableName + " SELECT 'METRIC_'||(LPAD(ENCODE(NEXT VALUE FOR " + sequenceName + ",'base62'),5,'0')), user_id, sum(val) FROM " + tableName + " GROUP BY user_id ORDER BY user_id");
+                    "UPSERT INTO " + tableName2 + " SELECT 'METRIC_'||(LPAD(ENCODE(NEXT VALUE FOR " + sequenceName + ",'base62'),5,'0')), user_id, sum(val) FROM " + tableName1 + " GROUP BY user_id ORDER BY user_id");
         conn.commit();
 
         PreparedStatement stmt =
-                conn.prepareStatement("SELECT metric_id, agg_id, metric_val FROM " + SECOND_tableName);
+                conn.prepareStatement("SELECT metric_id, agg_id, metric_val FROM " + tableName2);
         ResultSet rs = stmt.executeQuery();
         assertTrue(rs.next());
         assertEquals("METRIC_00001", rs.getString("metric_id"));
@@ -1215,7 +1220,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
      * value was being unset from true to false.
      */
     public void testNextValuesForSequenceClosingConnections() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         // Create Sequence
         conn.createStatement().execute("CREATE SEQUENCE " + sequenceName + " START WITH 4990 MINVALUE 4990 MAXVALUE 5000 CACHE 10");
         
@@ -1295,8 +1300,8 @@ public class SequenceIT extends ParallelStatsDisabledIT {
     @Test
     public void testValidateBeforeReserve() throws Exception {
         
-        String tableName = generateNameWithSchema();
-        String seqName = generateNameWithSchema();
+        String tableName = generateTableNameWithSchema();
+        String seqName = generateSequenceNameWithSchema();
         
         conn.createStatement().execute(
                 "CREATE TABLE " + tableName + " (k VARCHAR PRIMARY KEY, l BIGINT)");
@@ -1335,8 +1340,8 @@ public class SequenceIT extends ParallelStatsDisabledIT {
     
     @Test
     public void testNoFromClause() throws Exception {
-        String sequenceName = generateNameWithSchema();
-        String alternateSequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
+        String alternateSequenceName = generateSequenceNameWithSchema();
     	
         ResultSet rs;
         
@@ -1367,7 +1372,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
     
     @Test
     public void testReturnAllSequencesNotCalledForNoOpenConnections() throws Exception {
-        String sequenceName = generateNameWithSchema();
+        String sequenceName = generateSequenceNameWithSchema();
         String sequenceNameWithoutSchema = getNameWithoutSchema(sequenceName);
         String schemaName = getSchemaName(sequenceName);
         
@@ -1404,7 +1409,7 @@ public class SequenceIT extends ParallelStatsDisabledIT {
 
     @Test
     public void testPointInTimeSequence() throws Exception {
-        String seqName = generateNameWithSchema();    	
+        String seqName = generateSequenceNameWithSchema();    	
         Properties scnProps = PropertiesUtil.deepCopy(TEST_PROPERTIES);
         scnProps.put(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(EnvironmentEdgeManager.currentTimeMillis()));
         Connection beforeSeqConn = DriverManager.getConnection(getUrl(), scnProps);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3593ec8b/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java b/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java
index a77305c..2c85bb2 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java
@@ -726,6 +726,20 @@ public abstract class BaseTest {
         return "T" + Integer.toString(MAX_SUFFIX_VALUE + nextName).substring(1);
     }
 
+    private static AtomicInteger SEQ_NAME_SUFFIX = new AtomicInteger(0);
+    private static final int MAX_SEQ_SUFFIX_VALUE = 1000000;
+
+    private static final AtomicInteger SEQ_COUNTER = new AtomicInteger(0);
+
+    public static String generateUniqueSequenceName() {
+        int nextName = SEQ_NAME_SUFFIX.incrementAndGet();
+        if (nextName >= MAX_SEQ_SUFFIX_VALUE) {
+            throw new IllegalStateException("Used up all unique sequence names");
+        }
+        SEQ_COUNTER.incrementAndGet();
+        return "S" + Integer.toString(MAX_SEQ_SUFFIX_VALUE + nextName).substring(1);
+    }
+
     public static void tearDownMiniClusterIfBeyondThreshold() throws Exception {
         if (TABLE_COUNTER.get() > TEARDOWN_THRESHOLD) {
             int numTables = TABLE_COUNTER.get();


[05/25] phoenix git commit: PHOENIX-4246 Breakup join related tests into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/ee20a8c9/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinNoIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinNoIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinNoIndexIT.java
new file mode 100644
index 0000000..7f8528d
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinNoIndexIT.java
@@ -0,0 +1,391 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.join;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.runners.Parameterized.Parameters;
+
+import com.google.common.collect.Lists;
+
+public class HashJoinNoIndexIT extends HashJoinIT {
+
+    public HashJoinNoIndexIT(String[] indexDDL, String[] plans) {
+        super(indexDDL, plans);
+    }
+
+    @Parameters
+    public static Collection<Object> data() {
+        List<Object> testCases = Lists.newArrayList();
+        testCases.add(new String[][] {
+                {}, {
+                /* 
+                 * testLeftJoinWithAggregation()
+                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
+                 *     LEFT JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     GROUP BY i.name ORDER BY i.name
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.NAME]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME,
+                /* 
+                 * testLeftJoinWithAggregation()
+                 *     SELECT i.item_id iid, sum(quantity) q FROM joinOrderTable o 
+                 *     LEFT JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     GROUP BY i.item_id ORDER BY q DESC"
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [\"I.item_id\"]\n" +
+                "CLIENT MERGE SORT\n" +
+                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY",
+                /* 
+                 * testLeftJoinWithAggregation()
+                 *     SELECT i.item_id iid, sum(quantity) q FROM joinItemTable i 
+                 *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id 
+                 *     GROUP BY i.item_id ORDER BY q DESC NULLS LAST, iid
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [\"I.item_id\"]\n" +
+                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, \"I.item_id\"]\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
+                /* 
+                 * testRightJoinWithAggregation()
+                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
+                 *     RIGHT JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     GROUP BY i.name ORDER BY i.name
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.NAME]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
+                /*
+                 * testRightJoinWithAggregation()
+                 *     SELECT i.item_id iid, sum(quantity) q FROM joinOrderTable o 
+                 *     RIGHT JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     GROUP BY i.item_id ORDER BY q DESC NULLS LAST, iid
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [\"I.item_id\"]\n" +
+                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, \"I.item_id\"]\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
+                /*
+                 * testJoinWithWildcard()
+                 *     SELECT * FROM joinItemTable LEFT JOIN joinSupplierTable supp 
+                 *     ON joinItemTable.supplier_id = supp.supplier_id 
+                 *     ORDER BY item_id
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" + 
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME,
+                /*
+                 * testJoinPlanWithIndex()
+                 *     SELECT item.item_id, item.name, supp.supplier_id, supp.name 
+                 *     FROM joinItemTable item LEFT JOIN joinSupplierTable supp 
+                 *     ON substr(item.name, 2, 1) = substr(supp.name, 2, 1) 
+                 *         AND (supp.name BETWEEN 'S1' AND 'S5') 
+                 *     WHERE item.name BETWEEN 'T1' AND 'T5'
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    SERVER FILTER BY (NAME >= 'T1' AND NAME <= 'T5')\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "            SERVER FILTER BY (NAME >= 'S1' AND NAME <= 'S5')",
+                /*
+                 * testJoinPlanWithIndex()
+                 *     SELECT item.item_id, item.name, supp.supplier_id, supp.name 
+                 *     FROM joinItemTable item INNER JOIN joinSupplierTable supp 
+                 *     ON item.supplier_id = supp.supplier_id 
+                 *     WHERE (item.name = 'T1' OR item.name = 'T5') 
+                 *         AND (supp.name = 'S1' OR supp.name = 'S5')
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    SERVER FILTER BY (NAME = 'T1' OR NAME = 'T5')\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "            SERVER FILTER BY (NAME = 'S1' OR NAME = 'S5')",
+                /*
+                 * testJoinWithSkipMergeOptimization()
+                 *     SELECT s.name FROM joinItemTable i 
+                 *     JOIN joinOrderTable o ON o.item_id = i.item_id AND quantity < 5000 
+                 *     JOIN joinSupplierTable s ON i.supplier_id = s.supplier_id
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    PARALLEL INNER-JOIN TABLE 0 (SKIP MERGE)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER FILTER BY QUANTITY < 5000\n" +
+                "    PARALLEL INNER-JOIN TABLE 1\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "    DYNAMIC SERVER FILTER BY \"I.item_id\" IN (\"O.item_id\")",
+                /*
+                 * testSelfJoin()
+                 *     SELECT i2.item_id, i1.name FROM joinItemTable i1 
+                 *     JOIN joinItemTable i2 ON i1.item_id = i2.item_id 
+                 *     ORDER BY i1.item_id
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    DYNAMIC SERVER FILTER BY \"I1.item_id\" IN (\"I2.item_id\")",
+                /*
+                 * testSelfJoin()
+                 *     SELECT i1.name, i2.name FROM joinItemTable i1 
+                 *     JOIN joinItemTable i2 ON i1.item_id = i2.supplier_id 
+                 *     ORDER BY i1.name, i2.name
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    SERVER SORTED BY [I1.NAME, I2.NAME]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    DYNAMIC SERVER FILTER BY \"I1.item_id\" IN (\"I2.supplier_id\")",
+                /*
+                 * testStarJoin()
+                 *     SELECT order_id, c.name, i.name iname, quantity, o.date 
+                 *     FROM joinOrderTable o 
+                 *     JOIN joinCustomerTable c ON o.customer_id = c.customer_id 
+                 *     JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     ORDER BY order_id
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + "\n" +
+                "    PARALLEL INNER-JOIN TABLE 1\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME,
+                /*
+                 * testStarJoin()
+                 *     SELECT (*NO_STAR_JOIN*) order_id, c.name, i.name iname, quantity, o.date 
+                 *     FROM joinOrderTable o 
+                 *     JOIN joinCustomerTable c ON o.customer_id = c.customer_id 
+                 *     JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     ORDER BY order_id
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    SERVER SORTED BY [\"O.order_id\"]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            PARALLEL INNER-JOIN TABLE 0\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + "\n" +
+                "    DYNAMIC SERVER FILTER BY \"I.item_id\" IN (\"O.item_id\")",
+                /*
+                 * testSubJoin()
+                 *     SELECT * FROM joinCustomerTable c 
+                 *     INNER JOIN (joinOrderTable o 
+                 *         INNER JOIN (joinSupplierTable s 
+                 *             RIGHT JOIN joinItemTable i ON i.supplier_id = s.supplier_id)
+                 *         ON o.item_id = i.item_id)
+                 *     ON c.customer_id = o.customer_id
+                 *     WHERE c.customer_id <= '0000000005' 
+                 *         AND order_id != '000000000000003' 
+                 *         AND i.name != 'T3' 
+                 *     ORDER BY c.customer_id, i.name
+                 */
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + " [*] - ['0000000005']\n" +
+                "    SERVER SORTED BY [\"C.customer_id\", I.NAME]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER FILTER BY \"order_id\" != '000000000000003'\n" +
+                "            PARALLEL INNER-JOIN TABLE 0\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "                    SERVER FILTER BY NAME != 'T3'\n" +
+                "                    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "    DYNAMIC SERVER FILTER BY \"C.customer_id\" IN (\"O.customer_id\")",
+                /* 
+                 * testJoinWithSubqueryAndAggregation()
+                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
+                 *     LEFT JOIN (SELECT name, item_id iid FROM joinItemTable) AS i 
+                 *     ON o.item_id = i.iid 
+                 *     GROUP BY i.name ORDER BY i.name
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.NAME]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME,
+                /* 
+                 * testJoinWithSubqueryAndAggregation()
+                 *     SELECT o.iid, sum(o.quantity) q 
+                 *     FROM (SELECT item_id iid, quantity FROM joinOrderTable) AS o 
+                 *     LEFT JOIN (SELECT item_id FROM joinItemTable) AS i 
+                 *     ON o.iid = i.item_id 
+                 *     GROUP BY o.iid ORDER BY q DESC                 
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [O.IID]\n" +
+                "CLIENT MERGE SORT\n" +
+                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0 (SKIP MERGE)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY",
+                /* 
+                 * testJoinWithSubqueryAndAggregation()
+                 *     SELECT i.iid, o.q 
+                 *     FROM (SELECT item_id iid FROM joinItemTable) AS i 
+                 *     LEFT JOIN (SELECT item_id iid, sum(quantity) q FROM joinOrderTable GROUP BY item_id) AS o 
+                 *     ON o.iid = i.iid 
+                 *     ORDER BY o.q DESC NULLS LAST, i.iid
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER SORTED BY [O.Q DESC NULLS LAST, I.IID]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
+                "        CLIENT MERGE SORT",
+                /* 
+                 * testJoinWithSubqueryAndAggregation()
+                 *     SELECT i.iid, o.q 
+                 *     FROM (SELECT item_id iid, sum(quantity) q FROM joinOrderTable GROUP BY item_id) AS o 
+                 *     JOIN (SELECT item_id iid FROM joinItemTable) AS i 
+                 *     ON o.iid = i.iid 
+                 *     ORDER BY o.q DESC, i.iid
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER SORTED BY [O.Q DESC, I.IID]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
+                "        CLIENT MERGE SORT",
+                /*
+                 * testNestedSubqueries()
+                 *     SELECT * FROM (SELECT customer_id cid, name, phone, address, loc_id, date FROM joinCustomerTable) AS c 
+                 *     INNER JOIN (SELECT o.oid ooid, o.cid ocid, o.iid oiid, o.price * o.quantity, o.date odate, 
+                 *     qi.iiid iiid, qi.iname iname, qi.iprice iprice, qi.idiscount1 idiscount1, qi.idiscount2 idiscount2, qi.isid isid, qi.idescription idescription, 
+                 *     qi.ssid ssid, qi.sname sname, qi.sphone sphone, qi.saddress saddress, qi.sloc_id sloc_id 
+                 *         FROM (SELECT item_id iid, customer_id cid, order_id oid, price, quantity, date FROM joinOrderTable) AS o 
+                 *         INNER JOIN (SELECT i.iid iiid, i.name iname, i.price iprice, i.discount1 idiscount1, i.discount2 idiscount2, i.sid isid, i.description idescription, 
+                 *         s.sid ssid, s.name sname, s.phone sphone, s.address saddress, s.loc_id sloc_id 
+                 *             FROM (SELECT supplier_id sid, name, phone, address, loc_id FROM joinSupplierTable) AS s 
+                 *             RIGHT JOIN (SELECT item_id iid, name, price, discount1, discount2, supplier_id sid, description FROM joinItemTable) AS i 
+                 *             ON i.sid = s.sid) as qi 
+                 *         ON o.iid = qi.iiid) as qo 
+                 *     ON c.cid = qo.ocid 
+                 *     WHERE c.cid <= '0000000005' 
+                 *         AND qo.ooid != '000000000000003' 
+                 *         AND qo.iname != 'T3' 
+                 *     ORDER BY c.cid, qo.iname
+                 */
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + " [*] - ['0000000005']\n" +
+                "    SERVER SORTED BY [C.CID, QO.INAME]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER FILTER BY \"order_id\" != '000000000000003'\n" +
+                "            PARALLEL INNER-JOIN TABLE 0\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "                    SERVER FILTER BY NAME != 'T3'\n" +
+                "                    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME,
+                /*
+                 * testJoinWithLimit()
+                 *     SELECT order_id, i.name, s.name, s.address, quantity 
+                 *     FROM joinSupplierTable s 
+                 *     LEFT JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
+                 *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 4
+                 */
+                "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "    SERVER 4 ROW LIMIT\n" +
+                "CLIENT 4 ROW LIMIT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    PARALLEL LEFT-JOIN TABLE 1(DELAYED EVALUATION)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    JOIN-SCANNER 4 ROW LIMIT",
+                /*
+                 * testJoinWithLimit()
+                 *     SELECT order_id, i.name, s.name, s.address, quantity 
+                 *     FROM joinSupplierTable s 
+                 *     JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
+                 *     JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 4
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "CLIENT 4 ROW LIMIT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    PARALLEL INNER-JOIN TABLE 1(DELAYED EVALUATION)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    DYNAMIC SERVER FILTER BY \"S.supplier_id\" IN (\"I.supplier_id\")\n" +
+                "    JOIN-SCANNER 4 ROW LIMIT",
+                /*
+                 * testJoinWithSetMaxRows()
+                 *     statement.setMaxRows(4);
+                 *     SELECT order_id, i.name, quantity FROM joinItemTable i
+                 *     JOIN joinOrderTable o ON o.item_id = i.item_id;
+                 *     SELECT o.order_id, i.name, o.quantity FROM joinItemTable i
+                 *     JOIN (SELECT order_id, item_id, quantity FROM joinOrderTable) o
+                 *     ON o.item_id = i.item_id;
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "CLIENT 4 ROW LIMIT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    DYNAMIC SERVER FILTER BY \"I.item_id\" IN (\"O.item_id\")\n" +
+                "    JOIN-SCANNER 4 ROW LIMIT",
+                /*
+                 * testJoinWithOffset()
+                 *     SELECT order_id, i.name, s.name, s.address, quantity 
+                 *     FROM joinSupplierTable s 
+                 *     LEFT JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
+                 *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 1 OFFSET 2
+                 */
+                "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "    SERVER OFFSET 2\n" +
+                "    SERVER 3 ROW LIMIT\n" +
+                "CLIENT 1 ROW LIMIT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    PARALLEL LEFT-JOIN TABLE 1(DELAYED EVALUATION)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    JOIN-SCANNER 3 ROW LIMIT",
+                /*
+                 * testJoinWithOffset()
+                 *     SELECT order_id, i.name, s.name, s.address, quantity 
+                 *     FROM joinSupplierTable s 
+                 *     JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
+                 *     JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 1 OFFSET 2
+                 */
+                "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "    SERVER OFFSET 2\n" +
+                "CLIENT 1 ROW LIMIT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    PARALLEL INNER-JOIN TABLE 1(DELAYED EVALUATION)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    DYNAMIC SERVER FILTER BY \"S.supplier_id\" IN (\"I.supplier_id\")\n" +
+                "    JOIN-SCANNER 3 ROW LIMIT",
+                }});
+        return testCases;
+    }
+}


[06/25] phoenix git commit: PHOENIX-4246 Breakup join related tests into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/ee20a8c9/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinLocalIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinLocalIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinLocalIndexIT.java
new file mode 100644
index 0000000..dcc454f
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinLocalIndexIT.java
@@ -0,0 +1,528 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.join;
+
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.Collection;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.QueryUtil;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import com.google.common.collect.Lists;
+
+@RunWith(Parameterized.class)
+public class HashJoinLocalIndexIT extends HashJoinIT {
+    
+    public HashJoinLocalIndexIT(String[] indexDDL, String[] plans) {
+        super(indexDDL, plans);
+    }
+    
+    @Parameters
+    public static Collection<Object> data() {
+        List<Object> testCases = Lists.newArrayList();
+        testCases.add(new String[][] {
+                {
+                "CREATE LOCAL INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
+                "CREATE LOCAL INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
+                "CREATE LOCAL INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
+                }, {
+                /* 
+                 * testLeftJoinWithAggregation()
+                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
+                 *     LEFT JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     GROUP BY i.name ORDER BY i.name
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [\"I.0:NAME\"]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +JOIN_ITEM_TABLE_FULL_NAME +" [1]\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        CLIENT MERGE SORT",
+                /* 
+                 * testLeftJoinWithAggregation()
+                 *     SELECT i.item_id iid, sum(quantity) q FROM joinOrderTable o 
+                 *     LEFT JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     GROUP BY i.item_id ORDER BY q DESC"
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [\"I.:item_id\"]\n" +
+                "CLIENT MERGE SORT\n" +
+                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME +" [1]\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        CLIENT MERGE SORT",          
+                /* 
+                 * testLeftJoinWithAggregation()
+                 *     SELECT i.item_id iid, sum(quantity) q FROM joinItemTable i 
+                 *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id 
+                 *     GROUP BY i.item_id ORDER BY q DESC NULLS LAST, iid
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [\"I.item_id\"]\n" +
+                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, \"I.item_id\"]\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
+                /* 
+                 * testRightJoinWithAggregation()
+                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
+                 *     RIGHT JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     GROUP BY i.name ORDER BY i.name
+                 */
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME+" [1]\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [\"I.0:NAME\"]\n" +
+                "CLIENT MERGE SORT\n" + 
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
+                /*
+                 * testRightJoinWithAggregation()
+                 *     SELECT i.item_id iid, sum(quantity) q FROM joinOrderTable o 
+                 *     RIGHT JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     GROUP BY i.item_id ORDER BY q DESC NULLS LAST, iid
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [\"I.item_id\"]\n" +
+                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, \"I.item_id\"]\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
+                /*
+                 * testJoinWithWildcard()
+                 *     SELECT * FROM joinItemTable LEFT JOIN joinSupplierTable supp 
+                 *     ON joinItemTable.supplier_id = supp.supplier_id 
+                 *     ORDER BY item_id
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME,
+                /*
+                 * testJoinPlanWithIndex()
+                 *     SELECT item.item_id, item.name, supp.supplier_id, supp.name 
+                 *     FROM joinItemTable item LEFT JOIN joinSupplierTable supp 
+                 *     ON substr(item.name, 2, 1) = substr(supp.name, 2, 1) 
+                 *         AND (supp.name BETWEEN 'S1' AND 'S5') 
+                 *     WHERE item.name BETWEEN 'T1' AND 'T5'
+                 */
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1,'T1'] - [1,'T5']\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME +" [1,'S1'] - [1,'S5']\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" + 
+                "        CLIENT MERGE SORT",
+                /*
+                 * testJoinPlanWithIndex()
+                 *     SELECT item.item_id, item.name, supp.supplier_id, supp.name 
+                 *     FROM joinItemTable item INNER JOIN joinSupplierTable supp 
+                 *     ON item.supplier_id = supp.supplier_id 
+                 *     WHERE (item.name = 'T1' OR item.name = 'T5') 
+                 *         AND (supp.name = 'S1' OR supp.name = 'S5')
+                 */
+                "CLIENT PARALLEL 1-WAY SKIP SCAN ON 2 KEYS OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1,'T1'] - [1,'T5']\n" +
+                "CLIENT MERGE SORT\n" + 
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY SKIP SCAN ON 2 KEYS OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME +" [1,'S1'] - [1,'S5']\n" + 
+                "            SERVER FILTER BY FIRST KEY ONLY\n" + 
+                "        CLIENT MERGE SORT",
+                /*
+                 * testJoinWithSkipMergeOptimization()
+                 *     SELECT s.name FROM joinItemTable i 
+                 *     JOIN joinOrderTable o ON o.item_id = i.item_id AND quantity < 5000 
+                 *     JOIN joinSupplierTable s ON i.supplier_id = s.supplier_id
+                 */
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "CLIENT MERGE SORT\n" + 
+                "    PARALLEL INNER-JOIN TABLE 0 (SKIP MERGE)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER FILTER BY QUANTITY < 5000\n" +
+                "    PARALLEL INNER-JOIN TABLE 1\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + " [1]\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" + 
+                "        CLIENT MERGE SORT\n" +
+                "    DYNAMIC SERVER FILTER BY \"I.:item_id\" IN (\"O.item_id\")",
+                /*
+                 * testSelfJoin()
+                 *     SELECT i2.item_id, i1.name FROM joinItemTable i1 
+                 *     JOIN joinItemTable i2 ON i1.item_id = i2.item_id 
+                 *     ORDER BY i1.item_id
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME +" [1]\n"  +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    DYNAMIC SERVER FILTER BY \"I1.item_id\" IN (\"I2.:item_id\")",
+                /*
+                 * testSelfJoin()
+                 *     SELECT i1.name, i2.name FROM joinItemTable i1 
+                 *     JOIN joinItemTable i2 ON i1.item_id = i2.supplier_id 
+                 *     ORDER BY i1.name, i2.name
+                 */
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME +" [1]\n"  +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER SORTED BY [\"I1.0:NAME\", \"I2.0:NAME\"]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME +" [1]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    DYNAMIC SERVER FILTER BY \"I1.:item_id\" IN (\"I2.0:supplier_id\")",
+                /*
+                 * testStarJoin()
+                 *     SELECT order_id, c.name, i.name iname, quantity, o.date 
+                 *     FROM joinOrderTable o 
+                 *     JOIN joinCustomerTable c ON o.customer_id = c.customer_id 
+                 *     JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     ORDER BY order_id
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + " [1]\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" + 
+                "        CLIENT MERGE SORT\n" +
+                "    PARALLEL INNER-JOIN TABLE 1\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        CLIENT MERGE SORT",
+                /*
+                 * testStarJoin()
+                 *     SELECT (*NO_STAR_JOIN*) order_id, c.name, i.name iname, quantity, o.date 
+                 *     FROM joinOrderTable o 
+                 *     JOIN joinCustomerTable c ON o.customer_id = c.customer_id 
+                 *     JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     ORDER BY order_id
+                 */
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER SORTED BY [\"O.order_id\"]\n"+
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            PARALLEL INNER-JOIN TABLE 0\n" +
+                "                CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME+" [1]\n"+
+                "                    SERVER FILTER BY FIRST KEY ONLY\n" + 
+                "                CLIENT MERGE SORT\n" +
+                "    DYNAMIC SERVER FILTER BY \"I.:item_id\" IN (\"O.item_id\")",
+                /*
+                 * testSubJoin()
+                 *     SELECT * FROM joinCustomerTable c 
+                 *     INNER JOIN (joinOrderTable o 
+                 *         INNER JOIN (joinSupplierTable s 
+                 *             RIGHT JOIN joinItemTable i ON i.supplier_id = s.supplier_id)
+                 *         ON o.item_id = i.item_id)
+                 *     ON c.customer_id = o.customer_id
+                 *     WHERE c.customer_id <= '0000000005' 
+                 *         AND order_id != '000000000000003' 
+                 *         AND i.name != 'T3' 
+                 *     ORDER BY c.customer_id, i.name
+                 */
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + " [*] - ['0000000005']\n" +
+                "    SERVER SORTED BY [\"C.customer_id\", \"I.0:NAME\"]\n"+
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER FILTER BY \"order_id\" != '000000000000003'\n" +
+                "            PARALLEL INNER-JOIN TABLE 0\n" +
+                "                CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME +" [1]\n" +
+                "                    SERVER FILTER BY \"NAME\" != 'T3'\n" +
+                "                CLIENT MERGE SORT\n" +
+                "                    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "    DYNAMIC SERVER FILTER BY \"C.customer_id\" IN (\"O.customer_id\")",
+                /* 
+                 * testJoinWithSubqueryAndAggregation()
+                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
+                 *     LEFT JOIN (SELECT name, item_id iid FROM joinItemTable) AS i 
+                 *     ON o.item_id = i.iid 
+                 *     GROUP BY i.name ORDER BY i.name
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.NAME]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +JOIN_ITEM_TABLE_FULL_NAME+" [1]\n"+
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        CLIENT MERGE SORT",
+                /* 
+                 * testJoinWithSubqueryAndAggregation()
+                 *     SELECT o.iid, sum(o.quantity) q 
+                 *     FROM (SELECT item_id iid, quantity FROM joinOrderTable) AS o 
+                 *     LEFT JOIN (SELECT item_id FROM joinItemTable) AS i 
+                 *     ON o.iid = i.item_id 
+                 *     GROUP BY o.iid ORDER BY q DESC                 
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [O.IID]\n" +
+                "CLIENT MERGE SORT\n" +
+                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0 (SKIP MERGE)\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        CLIENT MERGE SORT",
+                /* 
+                 * testJoinWithSubqueryAndAggregation()
+                 *     SELECT i.iid, o.q 
+                 *     FROM (SELECT item_id iid FROM joinItemTable) AS i 
+                 *     LEFT JOIN (SELECT item_id iid, sum(quantity) q FROM joinOrderTable GROUP BY item_id) AS o 
+                 *     ON o.iid = i.iid 
+                 *     ORDER BY o.q DESC NULLS LAST, i.iid
+                 */     
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER SORTED BY [O.Q DESC NULLS LAST, I.IID]\n"+
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
+                "        CLIENT MERGE SORT",
+                /* 
+                 * testJoinWithSubqueryAndAggregation()
+                 *     SELECT i.iid, o.q 
+                 *     FROM (SELECT item_id iid, sum(quantity) q FROM joinOrderTable GROUP BY item_id) AS o 
+                 *     JOIN (SELECT item_id iid FROM joinItemTable) AS i 
+                 *     ON o.iid = i.iid 
+                 *     ORDER BY o.q DESC, i.iid
+                 */     
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER SORTED BY [O.Q DESC, I.IID]\n"+
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
+                "        CLIENT MERGE SORT",
+                /*
+                 * testNestedSubqueries()
+                 *     SELECT * FROM (SELECT customer_id cid, name, phone, address, loc_id, date FROM joinCustomerTable) AS c 
+                 *     INNER JOIN (SELECT o.oid ooid, o.cid ocid, o.iid oiid, o.price * o.quantity, o.date odate, 
+                 *     qi.iiid iiid, qi.iname iname, qi.iprice iprice, qi.idiscount1 idiscount1, qi.idiscount2 idiscount2, qi.isid isid, qi.idescription idescription, 
+                 *     qi.ssid ssid, qi.sname sname, qi.sphone sphone, qi.saddress saddress, qi.sloc_id sloc_id 
+                 *         FROM (SELECT item_id iid, customer_id cid, order_id oid, price, quantity, date FROM joinOrderTable) AS o 
+                 *         INNER JOIN (SELECT i.iid iiid, i.name iname, i.price iprice, i.discount1 idiscount1, i.discount2 idiscount2, i.sid isid, i.description idescription, 
+                 *         s.sid ssid, s.name sname, s.phone sphone, s.address saddress, s.loc_id sloc_id 
+                 *             FROM (SELECT supplier_id sid, name, phone, address, loc_id FROM joinSupplierTable) AS s 
+                 *             RIGHT JOIN (SELECT item_id iid, name, price, discount1, discount2, supplier_id sid, description FROM joinItemTable) AS i 
+                 *             ON i.sid = s.sid) as qi 
+                 *         ON o.iid = qi.iiid) as qo 
+                 *     ON c.cid = qo.ocid 
+                 *     WHERE c.cid <= '0000000005' 
+                 *         AND qo.ooid != '000000000000003' 
+                 *         AND qo.iname != 'T3' 
+                 *     ORDER BY c.cid, qo.iname
+                 */
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + " [*] - ['0000000005']\n" +
+                "    SERVER SORTED BY [C.CID, QO.INAME]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER FILTER BY \"order_id\" != '000000000000003'\n" +
+                "            PARALLEL INNER-JOIN TABLE 0\n" +
+                "                CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +  JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "                    SERVER FILTER BY \"NAME\" != 'T3'\n" +
+                "                CLIENT MERGE SORT\n" +      
+                "                    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME,
+                /*
+                 * testJoinWithLimit()
+                 *     SELECT order_id, i.name, s.name, s.address, quantity 
+                 *     FROM joinSupplierTable s 
+                 *     LEFT JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
+                 *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 4
+                 */
+                "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "    SERVER 4 ROW LIMIT\n" +
+                "CLIENT 4 ROW LIMIT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "        CLIENT MERGE SORT\n" +      
+                "    PARALLEL LEFT-JOIN TABLE 1(DELAYED EVALUATION)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    JOIN-SCANNER 4 ROW LIMIT",
+                /*
+                 * testJoinWithLimit()
+                 *     SELECT order_id, i.name, s.name, s.address, quantity 
+                 *     FROM joinSupplierTable s 
+                 *     JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
+                 *     JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 4
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "CLIENT 4 ROW LIMIT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    PARALLEL INNER-JOIN TABLE 1(DELAYED EVALUATION)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    DYNAMIC SERVER FILTER BY \"S.supplier_id\" IN (\"I.0:supplier_id\")\n" +
+                "    JOIN-SCANNER 4 ROW LIMIT",
+                /*
+                 * testJoinWithSetMaxRows()
+                 *     statement.setMaxRows(4);
+                 *     SELECT order_id, i.name, quantity FROM joinItemTable i
+                 *     JOIN joinOrderTable o ON o.item_id = i.item_id;
+                 *     SELECT o.order_id, i.name, o.quantity FROM joinItemTable i
+                 *     JOIN (SELECT order_id, item_id, quantity FROM joinOrderTable) o
+                 *     ON o.item_id = i.item_id;
+                 */
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "CLIENT MERGE SORT\n" +
+                "CLIENT 4 ROW LIMIT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    DYNAMIC SERVER FILTER BY \"I.:item_id\" IN (\"O.item_id\")\n" +
+                "    JOIN-SCANNER 4 ROW LIMIT",
+                /*
+                 * testJoinWithOffset()
+                 *     SELECT order_id, i.name, s.name, s.address, quantity 
+                 *     FROM joinSupplierTable s 
+                 *     LEFT JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
+                 *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 1 OFFSET 2
+                 */
+                "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "    SERVER OFFSET 2\n" +
+                "    SERVER 3 ROW LIMIT\n" +
+                "CLIENT 1 ROW LIMIT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "        CLIENT MERGE SORT\n" +      
+                "    PARALLEL LEFT-JOIN TABLE 1(DELAYED EVALUATION)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    JOIN-SCANNER 3 ROW LIMIT",
+                /*
+                 * testJoinWithOffset()
+                 *     SELECT order_id, i.name, s.name, s.address, quantity 
+                 *     FROM joinSupplierTable s 
+                 *     JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
+                 *     JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 1 OFFSET 2
+                 */
+                "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "    SERVER OFFSET 2\n" +
+                "CLIENT 1 ROW LIMIT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER "+ JOIN_ITEM_TABLE_FULL_NAME + " [1]\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    PARALLEL INNER-JOIN TABLE 1(DELAYED EVALUATION)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    DYNAMIC SERVER FILTER BY \"S.supplier_id\" IN (\"I.0:supplier_id\")\n" +
+                "    JOIN-SCANNER 3 ROW LIMIT",
+                /*
+                 * testJoinWithLocalIndex()
+                 *     SELECT phone, i.name 
+                 *     FROM joinSupplierTable s 
+                 *     JOIN joinItemTable i ON s.supplier_id = i.supplier_id 
+                 *     WHERE s.name = 'S1' AND i.name < 'T6'
+                 */
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + " [1,'S1']\n" + 
+                "    SERVER FILTER BY FIRST KEY ONLY\n" + 
+                "CLIENT MERGE SORT\n" + 
+                "    PARALLEL INNER-JOIN TABLE 0\n" + 
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1,*] - [1,'T6']\n" + 
+                "        CLIENT MERGE SORT\n" + 
+                "    DYNAMIC SERVER FILTER BY \"S.:supplier_id\" IN (\"I.0:supplier_id\")",
+                
+                /*
+                 * testJoinWithLocalIndex()
+                 *     SELECT phone, max(i.name) 
+                 *     FROM joinSupplierTable s 
+                 *     JOIN joinItemTable i ON s."supplier_id" = i."supplier_id" 
+                 *     WHERE s.name = 'S1' AND i.name < 'T6' 
+                 *     GROUP BY phone
+                 */
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + " [1,'S1']\n" + 
+                "    SERVER FILTER BY FIRST KEY ONLY\n" + 
+                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [\"S.PHONE\"]\n" + 
+                "CLIENT MERGE SORT\n" + 
+                "    PARALLEL INNER-JOIN TABLE 0\n" + 
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1,*] - [1,'T6']\n" + 
+                "        CLIENT MERGE SORT\n" + 
+                "    DYNAMIC SERVER FILTER BY \"S.:supplier_id\" IN (\"I.0:supplier_id\")",
+                
+                /*
+                 * testJoinWithLocalIndex()
+                 *     SELECT max(phone), max(i.name) 
+                 *     FROM joinSupplierTable s 
+                 *     LEFT JOIN joinItemTable i ON s."supplier_id" = i."supplier_id" AND i.name < 'T6' 
+                 *     WHERE s.name <= 'S3'
+                 */
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + " [1,*] - [1,'S3']\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" + 
+                "    SERVER AGGREGATE INTO SINGLE ROW\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + " [1,*] - [1,'T6']\n" +
+                "        CLIENT MERGE SORT",
+                }});
+        return testCases;
+    }
+    
+    @Test
+    public void testJoinWithLocalIndex() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {            
+            String query = "select phone, i.name from " + getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME) + " s join " + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i on s.\"supplier_id\" = i.\"supplier_id\" where s.name = 'S1' and i.name < 'T6'";
+            System.out.println("1)\n" + query);
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "888-888-1111");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "888-888-1111");
+            assertFalse(rs.next());
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            assertPlansEqual(plans[24], QueryUtil.getExplainPlan(rs));
+            
+            query = "select phone, max(i.name) from " + getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME) + " s join " + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i on s.\"supplier_id\" = i.\"supplier_id\" where s.name = 'S1' and i.name < 'T6' group by phone";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "888-888-1111");
+            assertEquals(rs.getString(2), "T2");
+            assertFalse(rs.next());
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            assertPlansEqual(plans[25], QueryUtil.getExplainPlan(rs));
+            
+            query = "select max(phone), max(i.name) from " + getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME) + " s left join " + getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME) + " i on s.\"supplier_id\" = i.\"supplier_id\" and i.name < 'T6' where s.name <= 'S3'";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "888-888-3333");
+            assertEquals(rs.getString(2), "T4");
+            assertFalse(rs.next());
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            assertPlansEqual(plans[26], QueryUtil.getExplainPlan(rs));
+        } finally {
+            conn.close();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ee20a8c9/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinMoreIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinMoreIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinMoreIT.java
new file mode 100644
index 0000000..37ffd02
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinMoreIT.java
@@ -0,0 +1,910 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.join;
+
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Array;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.Properties;
+
+import org.apache.phoenix.end2end.ParallelStatsDisabledIT;
+import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.QueryUtil;
+import org.junit.Test;
+
+public class HashJoinMoreIT extends ParallelStatsDisabledIT {
+    private final String[] plans = new String[] {
+            /*
+             * testJoinWithKeyRangeOptimization()
+             *     SELECT lhs.col0, lhs.col1, lhs.col2, rhs.col0, rhs.col1, rhs.col2 
+             *     FROM TEMP_TABLE_COMPOSITE_PK lhs 
+             *     JOIN TEMP_TABLE_COMPOSITE_PK rhs ON lhs.col1 = rhs.col2
+             */
+            "CLIENT PARALLEL 4-WAY FULL SCAN OVER %s\n" +
+            "CLIENT MERGE SORT\n" +
+            "    PARALLEL INNER-JOIN TABLE 0\n" +
+            "        CLIENT PARALLEL 4-WAY FULL SCAN OVER %s\n" +
+            "        CLIENT MERGE SORT",
+            /*
+             * testJoinWithKeyRangeOptimization()
+             *     SELECT lhs.col0, lhs.col1, lhs.col2, rhs.col0, rhs.col1, rhs.col2 
+             *     FROM TEMP_TABLE_COMPOSITE_PK lhs 
+             *     JOIN TEMP_TABLE_COMPOSITE_PK rhs ON lhs.col0 = rhs.col2
+             */
+            "CLIENT PARALLEL 4-WAY FULL SCAN OVER %s\n" +
+            "CLIENT MERGE SORT\n" +
+            "    PARALLEL INNER-JOIN TABLE 0\n" +
+            "        CLIENT PARALLEL 4-WAY FULL SCAN OVER %s\n" +
+            "        CLIENT MERGE SORT\n" +
+            "    DYNAMIC SERVER FILTER BY LHS.COL0 IN (RHS.COL2)",
+            /*
+             * testJoinWithKeyRangeOptimization()
+             *     SELECT lhs.col0, lhs.col1, lhs.col2, rhs.col0, rhs.col1, rhs.col2 
+             *     FROM TEMP_TABLE_COMPOSITE_PK lhs 
+             *     JOIN TEMP_TABLE_COMPOSITE_PK rhs ON lhs.col0 = rhs.col1 AND lhs.col1 = rhs.col2
+             */
+            "CLIENT PARALLEL 4-WAY FULL SCAN OVER %s\n" +
+            "CLIENT MERGE SORT\n" +
+            "    PARALLEL INNER-JOIN TABLE 0\n" +
+            "        CLIENT PARALLEL 4-WAY FULL SCAN OVER %s\n" +
+            "        CLIENT MERGE SORT\n" +
+            "    DYNAMIC SERVER FILTER BY (LHS.COL0, LHS.COL1) IN ((RHS.COL1, RHS.COL2))",
+            /*
+             * testJoinWithKeyRangeOptimization()
+             *     SELECT lhs.col0, lhs.col1, lhs.col2, rhs.col0, rhs.col1, rhs.col2 
+             *     FROM TEMP_TABLE_COMPOSITE_PK lhs 
+             *     JOIN TEMP_TABLE_COMPOSITE_PK rhs ON lhs.col0 = rhs.col1 AND lhs.col2 = rhs.col3 - 1 AND lhs.col1 = rhs.col2
+             */
+            "CLIENT PARALLEL 4-WAY FULL SCAN OVER %s\n" +
+            "CLIENT MERGE SORT\n" +
+            "    PARALLEL INNER-JOIN TABLE 0\n" +
+            "        CLIENT PARALLEL 4-WAY FULL SCAN OVER %s\n" +
+            "        CLIENT MERGE SORT\n" +
+            "    DYNAMIC SERVER FILTER BY (LHS.COL0, LHS.COL1, LHS.COL2) IN ((RHS.COL1, RHS.COL2, TO_INTEGER((RHS.COL3 - 1))))",            
+    };
+    
+    @Test
+    public void testJoinOverSaltedTables() throws Exception {
+        String tempTableNoSalting = generateUniqueName();
+        String tempTableWithSalting = generateUniqueName();
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            conn.createStatement().execute("CREATE TABLE " + tempTableNoSalting 
+                    + "   (mypk INTEGER NOT NULL PRIMARY KEY, " 
+                    + "    col1 INTEGER)");
+            conn.createStatement().execute("CREATE TABLE " + tempTableWithSalting 
+                    + "   (mypk INTEGER NOT NULL PRIMARY KEY, " 
+                    + "    col1 INTEGER) SALT_BUCKETS=4");
+            
+            PreparedStatement upsertStmt = conn.prepareStatement(
+                    "upsert into " + tempTableNoSalting + "(mypk, col1) " + "values (?, ?)");
+            for (int i = 0; i < 3; i++) {
+                upsertStmt.setInt(1, i + 1);
+                upsertStmt.setInt(2, 3 - i);
+                upsertStmt.execute();
+            }
+            conn.commit();
+            
+            upsertStmt = conn.prepareStatement(
+                    "upsert into " + tempTableWithSalting + "(mypk, col1) " + "values (?, ?)");
+            for (int i = 0; i < 6; i++) {
+                upsertStmt.setInt(1, i + 1);
+                upsertStmt.setInt(2, 3 - (i % 3));
+                upsertStmt.execute();
+            }
+            conn.commit();
+            
+            // LHS=unsalted JOIN RHS=salted
+            String query = "SELECT lhs.mypk, lhs.col1, rhs.mypk, rhs.col1 FROM " 
+                    + tempTableNoSalting + " lhs JOIN "
+                    + tempTableWithSalting + " rhs ON rhs.mypk = lhs.col1";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 1);
+            assertEquals(rs.getInt(2), 3);
+            assertEquals(rs.getInt(3), 3);
+            assertEquals(rs.getInt(4), 1);
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 2);
+            assertEquals(rs.getInt(2), 2);
+            assertEquals(rs.getInt(3), 2);
+            assertEquals(rs.getInt(4), 2);
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 3);
+            assertEquals(rs.getInt(2), 1);
+            assertEquals(rs.getInt(3), 1);
+            assertEquals(rs.getInt(4), 3);
+
+            assertFalse(rs.next());
+            
+            // LHS=salted JOIN RHS=salted
+            query = "SELECT lhs.mypk, lhs.col1, rhs.mypk, rhs.col1 FROM " 
+                    + tempTableWithSalting + " lhs JOIN "
+                    + tempTableNoSalting + " rhs ON rhs.mypk = lhs.col1";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 1);
+            assertEquals(rs.getInt(2), 3);
+            assertEquals(rs.getInt(3), 3);
+            assertEquals(rs.getInt(4), 1);
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 2);
+            assertEquals(rs.getInt(2), 2);
+            assertEquals(rs.getInt(3), 2);
+            assertEquals(rs.getInt(4), 2);
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 3);
+            assertEquals(rs.getInt(2), 1);
+            assertEquals(rs.getInt(3), 1);
+            assertEquals(rs.getInt(4), 3);
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 4);
+            assertEquals(rs.getInt(2), 3);
+            assertEquals(rs.getInt(3), 3);
+            assertEquals(rs.getInt(4), 1);
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 5);
+            assertEquals(rs.getInt(2), 2);
+            assertEquals(rs.getInt(3), 2);
+            assertEquals(rs.getInt(4), 2);
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 6);
+            assertEquals(rs.getInt(2), 1);
+            assertEquals(rs.getInt(3), 1);
+            assertEquals(rs.getInt(4), 3);
+
+            assertFalse(rs.next());
+            
+            // LHS=salted JOIN RHS=salted
+            query = "SELECT lhs.mypk, lhs.col1, rhs.mypk, rhs.col1 FROM " 
+                    + tempTableWithSalting + " lhs JOIN "
+                    + tempTableWithSalting + " rhs ON rhs.mypk = (lhs.col1 + 3)";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 1);
+            assertEquals(rs.getInt(2), 3);
+            assertEquals(rs.getInt(3), 6);
+            assertEquals(rs.getInt(4), 1);
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 2);
+            assertEquals(rs.getInt(2), 2);
+            assertEquals(rs.getInt(3), 5);
+            assertEquals(rs.getInt(4), 2);
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 3);
+            assertEquals(rs.getInt(2), 1);
+            assertEquals(rs.getInt(3), 4);
+            assertEquals(rs.getInt(4), 3);
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 4);
+            assertEquals(rs.getInt(2), 3);
+            assertEquals(rs.getInt(3), 6);
+            assertEquals(rs.getInt(4), 1);
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 5);
+            assertEquals(rs.getInt(2), 2);
+            assertEquals(rs.getInt(3), 5);
+            assertEquals(rs.getInt(4), 2);
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 6);
+            assertEquals(rs.getInt(2), 1);
+            assertEquals(rs.getInt(3), 4);
+            assertEquals(rs.getInt(4), 3);
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testJoinOnDynamicColumns() throws Exception {
+        String tableA = generateUniqueName();
+        String tableB = generateUniqueName();
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = null;
+        PreparedStatement stmt = null;
+        try {
+            conn = DriverManager.getConnection(getUrl(), props);
+            String ddlA = "CREATE TABLE " + tableA + "   (pkA INTEGER NOT NULL, " + "    colA1 INTEGER, "
+                    + "        colA2 VARCHAR " + "CONSTRAINT PK PRIMARY KEY" + "(pkA)" + ")";
+
+            String ddlB = "CREATE TABLE " + tableB + "   (pkB INTEGER NOT NULL PRIMARY KEY, " + "    colB INTEGER)";
+            conn.createStatement().execute(ddlA);
+            conn.createStatement().execute(ddlB);
+
+            String upsertA = "UPSERT INTO " + tableA + " (pkA, colA1, colA2) VALUES(?, ?, ?)";
+            stmt = conn.prepareStatement(upsertA);
+            int i = 0;
+            for (i = 0; i < 5; i++) {
+                stmt.setInt(1, i);
+                stmt.setInt(2, i + 10);
+                stmt.setString(3, "00" + i);
+                stmt.executeUpdate();
+            }
+            conn.commit();
+            stmt.close();
+
+            String sequenceB = generateUniqueName();
+            // upsert select dynamic columns in tableB
+            conn.createStatement().execute("CREATE SEQUENCE " + sequenceB );
+            String upsertBSelectA = "UPSERT INTO " + tableB + " (pkB, pkA INTEGER)"
+                    + "SELECT NEXT VALUE FOR " + sequenceB + ", pkA FROM " + tableA ;
+            stmt = conn.prepareStatement(upsertBSelectA);
+            stmt.executeUpdate();
+            stmt.close();
+            conn.commit();
+            conn.createStatement().execute("DROP SEQUENCE " + sequenceB );
+
+            // perform a join between tableB and tableA by joining on the dynamic column that we upserted in
+            // tableB. This join should return all the rows from table A.
+            String joinSql = "SELECT A.pkA, A.COLA1, A.colA2 FROM " + tableB + " B(pkA INTEGER) JOIN " + tableA + " A ON a.pkA = b.pkA";
+            stmt = conn.prepareStatement(joinSql);
+            ResultSet rs = stmt.executeQuery();
+            i = 0;
+            while (rs.next()) {
+                // check that we get back all the rows that we upserted for tableA above.
+                assertEquals(rs.getInt(1), i);
+                assertEquals(rs.getInt(2), i + 10);
+                assertEquals(rs.getString(3), "00" + i);
+                i++;
+            }
+            assertEquals(5,i);
+        } finally {
+            if (stmt != null) {
+                stmt.close();
+            }
+            if (conn != null) {
+                conn.close();
+            }
+
+        }
+
+    }
+    
+    @Test
+    public void testJoinWithKeyRangeOptimization() throws Exception {
+        String tempTableWithCompositePK = generateUniqueName();
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            conn.createStatement().execute("CREATE TABLE " + tempTableWithCompositePK 
+                    + "   (col0 INTEGER NOT NULL, " 
+                    + "    col1 INTEGER NOT NULL, " 
+                    + "    col2 INTEGER NOT NULL, "
+                    + "    col3 INTEGER "
+                    + "   CONSTRAINT pk PRIMARY KEY (col0, col1, col2)) " 
+                    + "   SALT_BUCKETS=4");
+            
+            PreparedStatement upsertStmt = conn.prepareStatement(
+                    "upsert into " + tempTableWithCompositePK + "(col0, col1, col2, col3) " + "values (?, ?, ?, ?)");
+            for (int i = 0; i < 3; i++) {
+                upsertStmt.setInt(1, i + 1);
+                upsertStmt.setInt(2, i + 2);
+                upsertStmt.setInt(3, i + 3);
+                upsertStmt.setInt(4, i + 5);
+                upsertStmt.execute();
+            }
+            conn.commit();
+            
+            // No leading part of PK
+            String query = "SELECT lhs.col0, lhs.col1, lhs.col2, lhs.col3, rhs.col0, rhs.col1, rhs.col2, rhs.col3 FROM " 
+                    + tempTableWithCompositePK + " lhs JOIN "
+                    + tempTableWithCompositePK + " rhs ON lhs.col1 = rhs.col2";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 2);
+            assertEquals(rs.getInt(2), 3);
+            assertEquals(rs.getInt(3), 4);
+            assertEquals(rs.getInt(4), 6);            
+            assertEquals(rs.getInt(5), 1);
+            assertEquals(rs.getInt(6), 2);
+            assertEquals(rs.getInt(7), 3);
+            assertEquals(rs.getInt(8), 5);
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 3);
+            assertEquals(rs.getInt(2), 4);
+            assertEquals(rs.getInt(3), 5);
+            assertEquals(rs.getInt(4), 7);
+            assertEquals(rs.getInt(5), 2);
+            assertEquals(rs.getInt(6), 3);
+            assertEquals(rs.getInt(7), 4);
+            assertEquals(rs.getInt(8), 6);
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            assertEquals(String.format(plans[0],tempTableWithCompositePK,tempTableWithCompositePK), QueryUtil.getExplainPlan(rs));
+            
+            // Two parts of PK but only one leading part
+            query = "SELECT lhs.col0, lhs.col1, lhs.col2, lhs.col3, rhs.col0, rhs.col1, rhs.col2, rhs.col3 FROM " 
+                    + tempTableWithCompositePK + " lhs JOIN "
+                    + tempTableWithCompositePK + " rhs ON lhs.col2 = rhs.col3 AND lhs.col0 = rhs.col2";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 3);
+            assertEquals(rs.getInt(2), 4);
+            assertEquals(rs.getInt(3), 5);
+            assertEquals(rs.getInt(4), 7);
+            assertEquals(rs.getInt(5), 1);
+            assertEquals(rs.getInt(6), 2);
+            assertEquals(rs.getInt(7), 3);
+            assertEquals(rs.getInt(8), 5);
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            assertEquals(String.format(plans[1],tempTableWithCompositePK,tempTableWithCompositePK), QueryUtil.getExplainPlan(rs));
+            
+            // Two leading parts of PK
+            query = "SELECT lhs.col0, lhs.col1, lhs.col2, lhs.col3, rhs.col0, rhs.col1, rhs.col2, rhs.col3 FROM " 
+                    + tempTableWithCompositePK + " lhs JOIN "
+                    + tempTableWithCompositePK + " rhs ON lhs.col1 = rhs.col2 AND lhs.col0 = rhs.col1";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 2);
+            assertEquals(rs.getInt(2), 3);
+            assertEquals(rs.getInt(3), 4);
+            assertEquals(rs.getInt(4), 6);
+            assertEquals(rs.getInt(5), 1);
+            assertEquals(rs.getInt(6), 2);
+            assertEquals(rs.getInt(7), 3);
+            assertEquals(rs.getInt(8), 5);
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 3);
+            assertEquals(rs.getInt(2), 4);
+            assertEquals(rs.getInt(3), 5);
+            assertEquals(rs.getInt(4), 7);
+            assertEquals(rs.getInt(5), 2);
+            assertEquals(rs.getInt(6), 3);
+            assertEquals(rs.getInt(7), 4);
+            assertEquals(rs.getInt(8), 6);
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            assertEquals(String.format(plans[2],tempTableWithCompositePK,tempTableWithCompositePK), QueryUtil.getExplainPlan(rs));
+            
+            // All parts of PK
+            query = "SELECT lhs.col0, lhs.col1, lhs.col2, lhs.col3, rhs.col0, rhs.col1, rhs.col2, rhs.col3 FROM " 
+                    + tempTableWithCompositePK + " lhs JOIN "
+                    + tempTableWithCompositePK + " rhs ON lhs.col1 = rhs.col2 AND lhs.col2 = rhs.col3 - 1 AND lhs.col0 = rhs.col1";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 2);
+            assertEquals(rs.getInt(2), 3);
+            assertEquals(rs.getInt(3), 4);
+            assertEquals(rs.getInt(4), 6);
+            assertEquals(rs.getInt(5), 1);
+            assertEquals(rs.getInt(6), 2);
+            assertEquals(rs.getInt(7), 3);
+            assertEquals(rs.getInt(8), 5);
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 3);
+            assertEquals(rs.getInt(2), 4);
+            assertEquals(rs.getInt(3), 5);
+            assertEquals(rs.getInt(4), 7);
+            assertEquals(rs.getInt(5), 2);
+            assertEquals(rs.getInt(6), 3);
+            assertEquals(rs.getInt(7), 4);
+            assertEquals(rs.getInt(8), 6);
+
+            assertFalse(rs.next());
+            
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            assertEquals(String.format(plans[3],tempTableWithCompositePK,tempTableWithCompositePK), QueryUtil.getExplainPlan(rs));
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testSubqueryWithoutData() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+
+        try {
+            String GRAMMAR_TABLE = "CREATE TABLE IF NOT EXISTS GRAMMAR_TABLE (ID INTEGER PRIMARY KEY, " +
+                    "unsig_id UNSIGNED_INT, big_id BIGINT, unsig_long_id UNSIGNED_LONG, tiny_id TINYINT," +
+                    "unsig_tiny_id UNSIGNED_TINYINT, small_id SMALLINT, unsig_small_id UNSIGNED_SMALLINT," + 
+                    "float_id FLOAT, unsig_float_id UNSIGNED_FLOAT, double_id DOUBLE, unsig_double_id UNSIGNED_DOUBLE," + 
+                    "decimal_id DECIMAL, boolean_id BOOLEAN, time_id TIME, date_id DATE, timestamp_id TIMESTAMP," + 
+                    "unsig_time_id TIME, unsig_date_id DATE, unsig_timestamp_id TIMESTAMP, varchar_id VARCHAR (30)," + 
+                    "char_id CHAR (30), binary_id BINARY (100), varbinary_id VARBINARY (100))";
+
+            String LARGE_TABLE = "CREATE TABLE IF NOT EXISTS LARGE_TABLE (ID INTEGER PRIMARY KEY, " +
+                    "unsig_id UNSIGNED_INT, big_id BIGINT, unsig_long_id UNSIGNED_LONG, tiny_id TINYINT," +
+                    "unsig_tiny_id UNSIGNED_TINYINT, small_id SMALLINT, unsig_small_id UNSIGNED_SMALLINT," + 
+                    "float_id FLOAT, unsig_float_id UNSIGNED_FLOAT, double_id DOUBLE, unsig_double_id UNSIGNED_DOUBLE," + 
+                    "decimal_id DECIMAL, boolean_id BOOLEAN, time_id TIME, date_id DATE, timestamp_id TIMESTAMP," + 
+                    "unsig_time_id TIME, unsig_date_id DATE, unsig_timestamp_id TIMESTAMP, varchar_id VARCHAR (30)," + 
+                    "char_id CHAR (30), binary_id BINARY (100), varbinary_id VARBINARY (100))";
+
+            String SECONDARY_LARGE_TABLE = "CREATE TABLE IF NOT EXISTS SECONDARY_LARGE_TABLE (SEC_ID INTEGER PRIMARY KEY," +
+                    "sec_unsig_id UNSIGNED_INT, sec_big_id BIGINT, sec_usnig_long_id UNSIGNED_LONG, sec_tiny_id TINYINT," + 
+                    "sec_unsig_tiny_id UNSIGNED_TINYINT, sec_small_id SMALLINT, sec_unsig_small_id UNSIGNED_SMALLINT," + 
+                    "sec_float_id FLOAT, sec_unsig_float_id UNSIGNED_FLOAT, sec_double_id DOUBLE, sec_unsig_double_id UNSIGNED_DOUBLE," +
+                    "sec_decimal_id DECIMAL, sec_boolean_id BOOLEAN, sec_time_id TIME, sec_date_id DATE," +
+                    "sec_timestamp_id TIMESTAMP, sec_unsig_time_id TIME, sec_unsig_date_id DATE, sec_unsig_timestamp_id TIMESTAMP," +
+                    "sec_varchar_id VARCHAR (30), sec_char_id CHAR (30), sec_binary_id BINARY (100), sec_varbinary_id VARBINARY (100))";
+            createTestTable(getUrl(), GRAMMAR_TABLE);
+            createTestTable(getUrl(), LARGE_TABLE);
+            createTestTable(getUrl(), SECONDARY_LARGE_TABLE);
+
+            String ddl = "SELECT * FROM (SELECT ID, BIG_ID, DATE_ID FROM LARGE_TABLE AS A WHERE (A.ID % 5) = 0) AS A " +
+                    "INNER JOIN (SELECT SEC_ID, SEC_TINY_ID, SEC_UNSIG_FLOAT_ID FROM SECONDARY_LARGE_TABLE AS B WHERE (B.SEC_ID % 5) = 0) AS B " +     
+                    "ON A.ID=B.SEC_ID WHERE A.DATE_ID > ALL (SELECT SEC_DATE_ID FROM SECONDARY_LARGE_TABLE LIMIT 100) " +      
+                    "AND B.SEC_UNSIG_FLOAT_ID = ANY (SELECT sec_unsig_float_id FROM SECONDARY_LARGE_TABLE " +                                       
+                    "WHERE SEC_ID > ALL (SELECT MIN (ID) FROM GRAMMAR_TABLE WHERE UNSIG_ID IS NULL) AND " +
+                    "SEC_UNSIG_ID < ANY (SELECT DISTINCT(UNSIG_ID) FROM LARGE_TABLE WHERE UNSIG_ID<2500) LIMIT 1000) " +
+                    "AND A.ID < 10000";
+            ResultSet rs = conn.createStatement().executeQuery(ddl);
+            assertFalse(rs.next());  
+        } finally {
+            Statement statement = conn.createStatement();
+            String query = "drop table GRAMMAR_TABLE";
+            statement.executeUpdate(query);
+            query = "drop table LARGE_TABLE";
+            statement.executeUpdate(query);
+            query = "drop table SECONDARY_LARGE_TABLE";
+            statement.executeUpdate(query);
+            conn.close();
+        }
+    }
+    
+    // PHOENIX-2381
+    @Test
+    public void testJoinWithMultiTenancy() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            conn.createStatement().execute("CREATE TABLE INVENTORY (" +
+                            " TENANTID UNSIGNED_INT NOT NULL" +
+                            ",ID UNSIGNED_INT NOT NULL" +
+                            ",FOO UNSIGNED_INT NOT NULL" +
+                            ",\"TIMESTAMP\"  UNSIGNED_LONG NOT NULL" +
+                            ",CODES INTEGER ARRAY[] NOT NULL" +
+                            ",V UNSIGNED_LONG" +
+                            " CONSTRAINT pk PRIMARY KEY (TENANTID, ID, FOO, \"TIMESTAMP\" , CODES))" +
+                            " DEFAULT_COLUMN_FAMILY ='E'," +
+                            " MULTI_TENANT=true");
+            PreparedStatement upsertStmt = conn.prepareStatement(
+                    "upsert into INVENTORY "
+                    + "(tenantid, id, foo, \"TIMESTAMP\" , codes) "
+                    + "values (?, ?, ?, ?, ?)");
+            upsertStmt.setInt(1, 15);
+            upsertStmt.setInt(2, 5);
+            upsertStmt.setInt(3, 0);
+            upsertStmt.setLong(4, 6);
+            Array array = conn.createArrayOf("INTEGER", new Object[] {1, 2});
+            upsertStmt.setArray(5, array);
+            upsertStmt.executeUpdate();
+            conn.commit();
+            
+            conn.createStatement().execute("CREATE TABLE PRODUCT_IDS (" +
+                            " PRODUCT_ID UNSIGNED_INT NOT NULL" +
+                            ",PRODUCT_NAME VARCHAR" +
+                            " CONSTRAINT pk PRIMARY KEY (PRODUCT_ID))" +
+                            " DEFAULT_COLUMN_FAMILY ='NAME'");
+            upsertStmt = conn.prepareStatement(
+                    "upsert into PRODUCT_IDS "
+                    + "(product_id, product_name) "
+                    + "values (?, ?)");
+            upsertStmt.setInt(1, 5);
+            upsertStmt.setString(2, "DUMMY");
+            upsertStmt.executeUpdate();
+            conn.commit();
+            conn.close();            
+
+            // Create a tenant-specific connection.
+            props.setProperty("TenantId", "15");
+            conn = DriverManager.getConnection(getUrl(), props);
+            ResultSet rs = conn.createStatement().executeQuery(
+                    "SELECT * FROM INVENTORY INNER JOIN PRODUCT_IDS ON (PRODUCT_ID = INVENTORY.ID)");
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 5);
+            assertFalse(rs.next());
+            rs.close();
+            rs = conn.createStatement().executeQuery(
+                    "SELECT * FROM INVENTORY RIGHT JOIN PRODUCT_IDS ON (PRODUCT_ID = INVENTORY.ID)");
+            assertTrue(rs.next());
+            assertEquals(rs.getInt(1), 5);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testBug2480() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(true);
+        try {
+            conn.createStatement().execute(
+                      "CREATE TABLE master_businessunit ("
+                    + "code varchar(255) PRIMARY KEY, name varchar(255))");
+            conn.createStatement().execute(
+                      "CREATE TABLE master_company ("
+                    + "code varchar(255) PRIMARY KEY, name varchar(255))");
+            conn.createStatement().execute(
+                      "CREATE TABLE master_costcenter ("
+                    + "code varchar(255) PRIMARY KEY, name varchar(255))");
+            conn.createStatement().execute(
+                      "CREATE TABLE master_location ("
+                    + "code varchar(255) PRIMARY KEY, name varchar(255))");
+            conn.createStatement().execute(
+                      "CREATE TABLE master_product ("
+                    + "id integer PRIMARY KEY, product_name varchar(255))");
+            conn.createStatement().execute(
+                      "CREATE TABLE master_purchaseorder ("
+                    + "purchaseOrderNumber varchar(255), "
+                    + "companyCode varchar(255), "
+                    + "businessUnitCode varchar(255), "
+                    + "locationCode varchar(255), "
+                    + "purchaseOrderId varchar(255) PRIMARY KEY, "
+                    + "releasedOn date, name varchar(255))");
+            conn.createStatement().execute(
+                      "CREATE TABLE trans_purchaseorderitem ("
+                    + "purchaseOrderItemId varchar(255) PRIMARY KEY, "
+                    + "purchaseOrderId varchar(255), "
+                    + "lineNo varchar(255), name varchar(255))");
+            conn.createStatement().execute(
+                      "CREATE TABLE trans_purchaseorderitem_costing ("
+                    + "purchaseorderItem_costing_id varchar(255) primary key, "
+                    + "purchaseorderItemId varchar(255), "
+                    + "purchaseorderId varchar(255), "
+                    + "costcenterCode varchar(255))");
+            
+            conn.createStatement().execute("upsert into master_businessunit(code,name) values ('1','BU1')");
+            conn.createStatement().execute("upsert into master_businessunit(code,name) values ('2','BU2')");
+            conn.createStatement().execute("upsert into master_company(code,name) values ('1','Company1')");
+            conn.createStatement().execute("upsert into master_company(code,name) values ('2','Company2')");
+            conn.createStatement().execute("upsert into master_costcenter(code,name) values ('1','CC1')");
+            conn.createStatement().execute("upsert into master_costcenter(code,name) values ('2','CC2')");
+            conn.createStatement().execute("upsert into master_location(code,name) values ('1','Location1')");
+            conn.createStatement().execute("upsert into master_location(code,name) values ('2','Location2')");
+            conn.createStatement().execute("upsert into master_product(id,product_name) values (1,'ProductName1')");
+            conn.createStatement().execute("upsert into master_product(id,product_name) values (2,'Product2')");
+            conn.createStatement().execute("upsert into master_purchaseorder(purchaseOrderNumber,companyCode,businessUnitCode,locationCode,purchaseOrderId,releasedOn,name) values ('1','1','1','1','1','2015-12-01','1')");
+            conn.createStatement().execute("upsert into master_purchaseorder(purchaseOrderNumber,companyCode,businessUnitCode,locationCode,purchaseOrderId,releasedOn,name) values ('2','2','2','2','2','2015-12-02','2')");
+            conn.createStatement().execute("upsert into trans_purchaseorderitem(purchaseOrderItemId,purchaseOrderId,lineNo,name) values ('1','1','1','1')");
+            conn.createStatement().execute("upsert into trans_purchaseorderitem(purchaseOrderItemId,purchaseOrderId,lineNo,name) values ('2','2','2','2')");
+            conn.createStatement().execute("upsert into trans_purchaseorderitem_costing(purchaseorderItem_costing_id,purchaseorderItemId,purchaseorderId,costcenterCode) values ('1','1','1','1')");
+            conn.createStatement().execute("upsert into trans_purchaseorderitem_costing(purchaseorderItem_costing_id,purchaseorderItemId,purchaseorderId,costcenterCode) values ('2','2','2','2')");
+            
+            ResultSet rs = conn.createStatement().executeQuery(
+                      "SELECT DISTINCT "
+                    + "COALESCE( a1.name, 'N.A.'), "
+                    + "COALESCE( a2.name, 'N.A.'), "
+                    + "COALESCE( a3.name, 'N.A.'), "
+                    + "COALESCE( a4.purchaseOrderNumber, 'N.A.'), "
+                    + "COALESCE( a1.name, 'N.A.'), "
+                    + "COALESCE( a4.name, 'N.A.'), "
+                    + "COALESCE( a5.lineNo, 'N.A.'), "
+                    + "COALESCE( a5.name, 'N.A.'), "
+                    + "COALESCE( a7.name,'N.A.') "
+                    + "FROM (master_purchaseorder a4 "
+                    + "LEFT OUTER JOIN master_company a1 "
+                    + "ON a4.companyCode = a1.code "
+                    + "LEFT OUTER JOIN master_businessunit a2 "
+                    + "ON a4.businessUnitCode = a2.code "
+                    + "LEFT OUTER JOIN master_location a3 "
+                    + "ON a4.locationCode = a3.code "
+                    + "LEFT OUTER JOIN trans_purchaseorderitem a5 "
+                    + "ON a5.purchaseOrderId = a4.purchaseOrderId "
+                    + "LEFT OUTER JOIN trans_purchaseorderitem_costing a6 "
+                    + "ON a6.purchaseOrderItemId = a5.purchaseOrderItemId "
+                    + "AND a6.purchaseOrderId = a5.purchaseOrderId "
+                    + "LEFT OUTER JOIN master_costcenter a7 "
+                    + "ON a6.costCenterCode = a7.code)");
+            
+            assertTrue(rs.next());
+            assertEquals("Company1", rs.getString(1));
+            assertEquals("BU1", rs.getString(2));
+            assertEquals("Location1", rs.getString(3));
+            assertEquals("1", rs.getString(4));
+            assertEquals("Company1", rs.getString(5));
+            assertEquals("1", rs.getString(6));
+            assertEquals("1", rs.getString(7));
+            assertEquals("1", rs.getString(8));
+            assertEquals("CC1", rs.getString(9));
+            assertTrue(rs.next());
+            assertEquals("Company2", rs.getString(1));
+            assertEquals("BU2", rs.getString(2));
+            assertEquals("Location2", rs.getString(3));
+            assertEquals("2", rs.getString(4));
+            assertEquals("Company2", rs.getString(5));
+            assertEquals("2", rs.getString(6));
+            assertEquals("2", rs.getString(7));
+            assertEquals("2", rs.getString(8));
+            assertEquals("CC2", rs.getString(9));
+            
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testBug2894() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(true);
+        try {
+            conn.createStatement().execute(
+                    "CREATE TABLE IF NOT EXISTS EVENT_COUNT (\n" +
+                    "        BUCKET VARCHAR,\n" +
+                    "        TIMESTAMP_DATE TIMESTAMP,\n" +
+                    "        \"TIMESTAMP\" UNSIGNED_LONG NOT NULL,\n" +
+                    "        LOCATION VARCHAR,\n" +
+                    "        A VARCHAR,\n" +
+                    "        B VARCHAR,\n" +
+                    "        C VARCHAR,\n" +
+                    "        D UNSIGNED_LONG,\n" +
+                    "        E FLOAT\n" +
+                    "    CONSTRAINT pk PRIMARY KEY (BUCKET, \"TIMESTAMP\" DESC, LOCATION, A, B, C)\n" +
+                    ") SALT_BUCKETS=2, COMPRESSION='GZ', TTL=31622400");
+            PreparedStatement stmt = conn.prepareStatement("UPSERT INTO EVENT_COUNT(BUCKET, \"TIMESTAMP\", LOCATION, A, B, C) VALUES(?,?,?,?,?,?)");
+            stmt.setString(1, "5SEC");
+            stmt.setString(3, "Tr/Bal");
+            stmt.setString(4, "A1");
+            stmt.setString(5, "B1");
+            stmt.setString(6, "C1");
+            stmt.setLong(2, 1462993520000000000L);
+            stmt.execute();
+            stmt.setLong(2, 1462993515000000000L);
+            stmt.execute();
+            stmt.setLong(2, 1462993510000000000L);
+            stmt.execute();
+            stmt.setLong(2, 1462993505000000000L);
+            stmt.execute();
+            stmt.setLong(2, 1462993500000000000L);
+            stmt.execute();
+            stmt.setLong(2, 1462993495000000000L);
+            stmt.execute();
+            stmt.setLong(2, 1462993490000000000L);
+            stmt.execute();
+            stmt.setLong(2, 1462993485000000000L);
+            stmt.execute();
+            stmt.setLong(2, 1462993480000000000L);
+            stmt.execute();
+            stmt.setLong(2, 1462993475000000000L);
+            stmt.execute();
+            stmt.setLong(2, 1462993470000000000L);
+            stmt.execute();
+            stmt.setLong(2, 1462993465000000000L);
+            stmt.execute();
+            stmt.setLong(2, 1462993460000000000L);
+            stmt.execute();
+            stmt.setLong(2, 1462993455000000000L);
+            stmt.execute();
+            stmt.setLong(2, 1462993450000000000L);
+            stmt.execute();
+            stmt.setLong(2, 1462993445000000000L);
+            stmt.execute();
+            stmt.setLong(2, 1462993440000000000L);
+            stmt.execute();
+            stmt.setLong(2, 1462993430000000000L);
+            stmt.execute();
+
+            // We'll test the original version of the user table as well as a slightly modified
+            // version, in order to verify that hash join works for columns both having DESC
+            // sort order as well as one having ASC order and the other having DESC order.
+            String[] t = new String[] {"EVENT_LATENCY", "EVENT_LATENCY_2"};
+            for (int i = 0; i < 2; i++) {
+                conn.createStatement().execute(
+                        "CREATE TABLE IF NOT EXISTS " + t[i] + " (\n" +
+                                "        BUCKET VARCHAR,\n" +
+                                "        TIMESTAMP_DATE TIMESTAMP,\n" +
+                                "        \"TIMESTAMP\" UNSIGNED_LONG NOT NULL,\n" +
+                                "        SRC_LOCATION VARCHAR,\n" +
+                                "        DST_LOCATION VARCHAR,\n" +
+                                "        B VARCHAR,\n" +
+                                "        C VARCHAR,\n" +
+                                "        F UNSIGNED_LONG,\n" +
+                                "        G UNSIGNED_LONG,\n" +
+                                "        H UNSIGNED_LONG,\n" +
+                                "        I UNSIGNED_LONG\n" +
+                                "    CONSTRAINT pk PRIMARY KEY (BUCKET, \"TIMESTAMP\"" + (i == 0 ? " DESC" : "") + ", SRC_LOCATION, DST_LOCATION, B, C)\n" +
+                        ") SALT_BUCKETS=2, COMPRESSION='GZ', TTL=31622400");
+                stmt = conn.prepareStatement("UPSERT INTO " + t[i] + "(BUCKET, \"TIMESTAMP\", SRC_LOCATION, DST_LOCATION, B, C) VALUES(?,?,?,?,?,?)");
+                stmt.setString(1, "5SEC");
+                stmt.setString(3, "Tr/Bal");
+                stmt.setString(4, "Tr/Bal");
+                stmt.setString(5, "B1");
+                stmt.setString(6, "C1");
+                stmt.setLong(2, 1462993520000000000L);
+                stmt.execute();
+                stmt.setLong(2, 1462993515000000000L);
+                stmt.execute();
+                stmt.setLong(2, 1462993510000000000L);
+                stmt.execute();
+                stmt.setLong(2, 1462993505000000000L);
+                stmt.execute();
+                stmt.setLong(2, 1462993490000000000L);
+                stmt.execute();
+                stmt.setLong(2, 1462993485000000000L);
+                stmt.execute();
+                stmt.setLong(2, 1462993480000000000L);
+                stmt.execute();
+                stmt.setLong(2, 1462993475000000000L);
+                stmt.execute();
+                stmt.setLong(2, 1462993470000000000L);
+                stmt.execute();
+                stmt.setLong(2, 1462993430000000000L);
+                stmt.execute();
+                
+                String q =
+                        "SELECT C.BUCKET, C.\"TIMESTAMP\" FROM (\n" +
+                        "     SELECT E.BUCKET as BUCKET, L.BUCKET as LBUCKET, E.\"TIMESTAMP\" as TIMESTAMP, L.\"TIMESTAMP\" as LTIMESTAMP FROM\n" +
+                        "        (SELECT BUCKET, \"TIMESTAMP\"  FROM EVENT_COUNT\n" +
+                        "             WHERE BUCKET = '5SEC' AND LOCATION = 'Tr/Bal'\n" +
+                        "                 AND \"TIMESTAMP\"  <= 1462993520000000000 AND \"TIMESTAMP\"  > 1462993420000000000\n" +
+                        "        ) E\n" +
+                        "        JOIN\n" +
+                        "         (SELECT BUCKET, \"TIMESTAMP\"  FROM "+ t[i] +"\n" +
+                        "             WHERE BUCKET = '5SEC' AND SRC_LOCATION = 'Tr/Bal' AND SRC_LOCATION = DST_LOCATION\n" +
+                        "                 AND \"TIMESTAMP\"  <= 1462993520000000000 AND \"TIMESTAMP\"  > 1462993420000000000\n" +
+                        "         ) L\n" +
+                        "     ON L.BUCKET = E.BUCKET AND L.\"TIMESTAMP\"  = E.\"TIMESTAMP\"\n" +
+                        " ) C\n" +
+                        " GROUP BY C.BUCKET, C.\"TIMESTAMP\"";
+                    
+                String p = i == 0 ?
+                        "CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER EVENT_COUNT [0,'5SEC',~1462993520000000000,'Tr/Bal'] - [1,'5SEC',~1462993420000000000,'Tr/Bal']\n" +
+                        "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                        "    SERVER AGGREGATE INTO DISTINCT ROWS BY [E.BUCKET, \"E.TIMESTAMP\"]\n" +
+                        "CLIENT MERGE SORT\n" +
+                        "    PARALLEL INNER-JOIN TABLE 0 (SKIP MERGE)\n" +
+                        "        CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER " + t[i] + " [0,'5SEC',~1462993520000000000,'Tr/Bal'] - [1,'5SEC',~1462993420000000000,'Tr/Bal']\n" +
+                        "            SERVER FILTER BY FIRST KEY ONLY AND SRC_LOCATION = DST_LOCATION\n" +
+                        "        CLIENT MERGE SORT"
+                        :
+                        "CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER EVENT_COUNT [0,'5SEC',~1462993520000000000,'Tr/Bal'] - [1,'5SEC',~1462993420000000000,'Tr/Bal']\n" +
+                        "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                        "    SERVER AGGREGATE INTO DISTINCT ROWS BY [E.BUCKET, \"E.TIMESTAMP\"]\n" +
+                        "CLIENT MERGE SORT\n" +
+                        "    PARALLEL INNER-JOIN TABLE 0 (SKIP MERGE)\n" +
+                        "        CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER " + t[i] + " [0,'5SEC',1462993420000000001,'Tr/Bal'] - [1,'5SEC',1462993520000000000,'Tr/Bal']\n" +
+                        "            SERVER FILTER BY FIRST KEY ONLY AND SRC_LOCATION = DST_LOCATION\n" +
+                        "        CLIENT MERGE SORT";
+                
+                ResultSet rs = conn.createStatement().executeQuery("explain " + q);
+                assertEquals(p, QueryUtil.getExplainPlan(rs));
+                
+                rs = conn.createStatement().executeQuery(q);
+                assertTrue(rs.next());
+                assertEquals("5SEC", rs.getString(1));
+                assertEquals(1462993520000000000L, rs.getLong(2));
+                assertTrue(rs.next());
+                assertEquals("5SEC", rs.getString(1));
+                assertEquals(1462993515000000000L, rs.getLong(2));
+                assertTrue(rs.next());
+                assertEquals("5SEC", rs.getString(1));
+                assertEquals(1462993510000000000L, rs.getLong(2));
+                assertTrue(rs.next());
+                assertEquals("5SEC", rs.getString(1));
+                assertEquals(1462993505000000000L, rs.getLong(2));
+                assertTrue(rs.next());
+                assertEquals("5SEC", rs.getString(1));
+                assertEquals(1462993490000000000L, rs.getLong(2));
+                assertTrue(rs.next());
+                assertEquals("5SEC", rs.getString(1));
+                assertEquals(1462993485000000000L, rs.getLong(2));
+                assertTrue(rs.next());
+                assertEquals("5SEC", rs.getString(1));
+                assertEquals(1462993480000000000L, rs.getLong(2));
+                assertTrue(rs.next());
+                assertEquals("5SEC", rs.getString(1));
+                assertEquals(1462993475000000000L, rs.getLong(2));
+                assertTrue(rs.next());
+                assertEquals("5SEC", rs.getString(1));
+                assertEquals(1462993470000000000L, rs.getLong(2));
+                assertTrue(rs.next());
+                assertEquals("5SEC", rs.getString(1));
+                assertEquals(1462993430000000000L, rs.getLong(2));
+                assertFalse(rs.next());
+            }
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testBug2961() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(true);
+        try {
+            conn.createStatement().execute("CREATE TABLE test2961 (\n" + 
+                    "ACCOUNT_ID VARCHAR NOT NULL,\n" + 
+                    "BUCKET_ID VARCHAR NOT NULL,\n" + 
+                    "OBJECT_ID VARCHAR NOT NULL,\n" + 
+                    "OBJECT_VERSION VARCHAR NOT NULL,\n" + 
+                    "LOC VARCHAR,\n" + 
+                    "CONSTRAINT PK PRIMARY KEY (ACCOUNT_ID, BUCKET_ID, OBJECT_ID, OBJECT_VERSION DESC))");
+            conn.createStatement().execute("UPSERT INTO test2961  (ACCOUNT_ID, BUCKET_ID, OBJECT_ID, OBJECT_VERSION, LOC) VALUES ('acct1', 'bucket1', 'obj1', '1111', 'loc1')");
+            ResultSet rs = conn.createStatement().executeQuery("select ACCOUNT_ID, BUCKET_ID, OBJECT_VERSION  from test2961  WHERE ACCOUNT_ID = 'acct1' and BUCKET_ID = 'bucket1' and OBJECT_VERSION = '1111'");
+            assertTrue(rs.next());
+            rs = conn.createStatement().executeQuery("select ACCOUNT_ID, BUCKET_ID, OBJECT_VERSION  from test2961  WHERE ACCOUNT_ID = 'acct1' and BUCKET_ID = 'bucket1' and OBJECT_ID = 'obj1'");
+            assertTrue(rs.next());
+            rs = conn.createStatement().executeQuery("select ACCOUNT_ID, BUCKET_ID, OBJECT_VERSION  from test2961  WHERE ACCOUNT_ID = 'acct1' and BUCKET_ID = 'bucket1' and OBJECT_VERSION = '1111'  and OBJECT_ID = 'obj1'");
+            assertTrue(rs.next());
+
+            conn.createStatement().execute("UPSERT INTO test2961  (ACCOUNT_ID, BUCKET_ID, OBJECT_ID, OBJECT_VERSION, LOC) VALUES ('acct1', 'bucket1', 'obj1', '2222', 'loc1')");
+            rs = conn.createStatement().executeQuery("SELECT  OBJ.ACCOUNT_ID, OBJ.BUCKET_ID, OBJ.OBJECT_ID, OBJ.OBJECT_VERSION, OBJ.LOC "
+                    + "FROM ( SELECT ACCOUNT_ID, BUCKET_ID, OBJECT_ID, MAX(OBJECT_VERSION) AS MAXVER"
+                    + "       FROM test2961 GROUP BY ACCOUNT_ID, BUCKET_ID, OBJECT_ID) AS X "
+                    + "       INNER JOIN test2961 AS OBJ ON X.ACCOUNT_ID = OBJ.ACCOUNT_ID AND X.BUCKET_ID = OBJ.BUCKET_ID AND X.OBJECT_ID = OBJ.OBJECT_ID AND X.MAXVER = OBJ.OBJECT_VERSION");
+            assertTrue(rs.next());
+            assertEquals("2222", rs.getString(4));
+            assertFalse(rs.next());
+
+            rs = conn.createStatement().executeQuery("SELECT  OBJ.ACCOUNT_ID, OBJ.BUCKET_ID, OBJ.OBJECT_ID, OBJ.OBJECT_VERSION, OBJ.LOC "
+                    + "FROM ( SELECT ACCOUNT_ID, BUCKET_ID, OBJECT_ID, MAX(OBJECT_VERSION) AS MAXVER "
+                    + "       FROM test2961 GROUP BY ACCOUNT_ID, BUCKET_ID, OBJECT_ID) AS X "
+                    + "       INNER JOIN test2961 AS OBJ ON X.ACCOUNT_ID = OBJ.ACCOUNT_ID AND X.OBJECT_ID = OBJ.OBJECT_ID AND X.MAXVER = OBJ.OBJECT_VERSION");
+            assertTrue(rs.next());
+            assertEquals("2222", rs.getString(4));
+            assertFalse(rs.next());
+
+            rs = conn.createStatement().executeQuery("SELECT  OBJ.ACCOUNT_ID, OBJ.BUCKET_ID, OBJ.OBJECT_ID, OBJ.OBJECT_VERSION, OBJ.LOC "
+                    + "FROM ( SELECT ACCOUNT_ID, BUCKET_ID, OBJECT_ID, MAX(OBJECT_VERSION) AS MAXVER "
+                    + "       FROM test2961 GROUP BY ACCOUNT_ID, BUCKET_ID, OBJECT_ID) AS X "
+                    + "       INNER JOIN test2961 AS OBJ ON X.ACCOUNT_ID = OBJ.ACCOUNT_ID AND X.BUCKET_ID = OBJ.BUCKET_ID AND  X.MAXVER = OBJ.OBJECT_VERSION");
+            assertTrue(rs.next());
+            assertEquals("2222", rs.getString(4));
+            assertFalse(rs.next());
+
+            conn.createStatement().execute("UPSERT INTO test2961  (ACCOUNT_ID, BUCKET_ID, OBJECT_ID, OBJECT_VERSION, LOC) VALUES ('acct1', 'bucket1', 'obj2', '1111', 'loc1')");
+            conn.createStatement().execute("UPSERT INTO test2961  (ACCOUNT_ID, BUCKET_ID, OBJECT_ID, OBJECT_VERSION, LOC) VALUES ('acct1', 'bucket1', 'obj3', '1111', 'loc1')");
+            String q = "SELECT  OBJ.ACCOUNT_ID, OBJ.BUCKET_ID, OBJ.OBJECT_ID, OBJ.OBJECT_VERSION, OBJ.LOC "
+                    + "FROM ( SELECT ACCOUNT_ID, BUCKET_ID, OBJECT_ID, MAX(OBJECT_VERSION) AS MAXVER "
+                    + "       FROM test2961 GROUP BY ACCOUNT_ID, BUCKET_ID, OBJECT_ID) AS X "
+                    + "       INNER JOIN test2961 AS OBJ ON X.ACCOUNT_ID = OBJ.ACCOUNT_ID AND X.BUCKET_ID = OBJ.BUCKET_ID AND X.OBJECT_ID = OBJ.OBJECT_ID AND  X.MAXVER = OBJ.OBJECT_VERSION";
+            rs = conn.createStatement().executeQuery(q);
+            assertTrue(rs.next());
+            assertEquals("2222", rs.getString(4));
+            assertTrue(rs.next());
+            assertEquals("1111", rs.getString(4));
+            assertTrue(rs.next());
+            assertEquals("1111", rs.getString(4));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+}


[08/25] phoenix git commit: PHOENIX-4246 Breakup join related tests into several integration tests so as not to create too many tables in one test

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/ee20a8c9/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/BaseJoinIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/BaseJoinIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/BaseJoinIT.java
new file mode 100644
index 0000000..6e03a37
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/BaseJoinIT.java
@@ -0,0 +1,473 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.join;
+
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.text.SimpleDateFormat;
+import java.util.Map;
+import java.util.Properties;
+import java.util.regex.Pattern;
+
+import org.apache.phoenix.cache.ServerCacheClient;
+import org.apache.phoenix.end2end.ParallelStatsDisabledIT;
+import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.SchemaUtil;
+import org.apache.phoenix.util.StringUtil;
+import org.junit.Before;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+
+public abstract class BaseJoinIT extends ParallelStatsDisabledIT {
+	
+    protected static final String JOIN_SCHEMA = "Join";
+    protected static final String JOIN_ORDER_TABLE = "OrderTable";
+    protected static final String JOIN_CUSTOMER_TABLE = "CustomerTable";
+    protected static final String JOIN_ITEM_TABLE = "ItemTable";
+    protected static final String JOIN_SUPPLIER_TABLE = "SupplierTable";
+    protected static final String JOIN_COITEM_TABLE = "CoitemTable";
+    protected static final String JOIN_ORDER_TABLE_FULL_NAME = '"' + JOIN_SCHEMA + "\".\"" + JOIN_ORDER_TABLE + '"';
+    protected static final String JOIN_CUSTOMER_TABLE_FULL_NAME = '"' + JOIN_SCHEMA + "\".\"" + JOIN_CUSTOMER_TABLE + '"';
+    protected static final String JOIN_ITEM_TABLE_FULL_NAME = '"' + JOIN_SCHEMA + "\".\"" + JOIN_ITEM_TABLE + '"';
+    protected static final String JOIN_SUPPLIER_TABLE_FULL_NAME = '"' + JOIN_SCHEMA + "\".\"" + JOIN_SUPPLIER_TABLE + '"';
+    protected static final String JOIN_COITEM_TABLE_FULL_NAME = '"' + JOIN_SCHEMA + "\".\"" + JOIN_COITEM_TABLE + '"';
+
+    private static final Map<String,String> tableDDLMap;
+    
+    static {
+        ImmutableMap.Builder<String,String> builder = ImmutableMap.builder();
+        builder.put(JOIN_ORDER_TABLE_FULL_NAME, "create table " + JOIN_ORDER_TABLE_FULL_NAME +
+                "   (\"order_id\" varchar(15) not null primary key, " +
+                "    \"customer_id\" varchar(10), " +
+                "    \"item_id\" varchar(10), " +
+                "    price integer, " +
+                "    quantity integer, " +
+                "    date timestamp) IMMUTABLE_ROWS=true");
+        builder.put(JOIN_CUSTOMER_TABLE_FULL_NAME, "create table " + JOIN_CUSTOMER_TABLE_FULL_NAME +
+                "   (\"customer_id\" varchar(10) not null primary key, " +
+                "    name varchar, " +
+                "    phone varchar(12), " +
+                "    address varchar, " +
+                "    loc_id varchar(5), " +
+                "    date date) IMMUTABLE_ROWS=true");
+        builder.put(JOIN_ITEM_TABLE_FULL_NAME, "create table " + JOIN_ITEM_TABLE_FULL_NAME +
+                "   (\"item_id\" varchar(10) not null primary key, " +
+                "    name varchar, " +
+                "    price integer, " +
+                "    discount1 integer, " +
+                "    discount2 integer, " +
+                "    \"supplier_id\" varchar(10), " +
+                "    description varchar)");
+        builder.put(JOIN_SUPPLIER_TABLE_FULL_NAME, "create table " + JOIN_SUPPLIER_TABLE_FULL_NAME +
+                "   (\"supplier_id\" varchar(10) not null primary key, " +
+                "    name varchar, " +
+                "    phone varchar(12), " +
+                "    address varchar, " +
+                "    loc_id varchar(5))");
+        builder.put(JOIN_COITEM_TABLE_FULL_NAME, "create table " + JOIN_COITEM_TABLE_FULL_NAME +
+                "   (item_id varchar(10) NOT NULL, " +
+                "    item_name varchar NOT NULL, " +
+                "    co_item_id varchar(10), " +
+                "    co_item_name varchar " +
+                "   CONSTRAINT pk PRIMARY KEY (item_id, item_name)) " +
+                "   SALT_BUCKETS=4");
+        tableDDLMap = builder.build();
+    }
+    
+    protected String seqName;
+    protected String schemaName;
+    protected final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+    protected final String[] plans;
+    private final String[] indexDDL;
+    private final Map<String,String> virtualNameToRealNameMap = Maps.newHashMap();
+    
+    public BaseJoinIT(String[] indexDDL, String[] plans) {
+        this.indexDDL = indexDDL;
+        this.plans = plans;
+    }
+    
+    public BaseJoinIT() {
+        this.indexDDL = new String[0];
+        this.plans = new String[0];
+    }
+
+    protected String getTableName(Connection conn, String virtualName) throws Exception {
+        String realName = virtualNameToRealNameMap.get(virtualName);
+        if (realName == null) {
+            realName = SchemaUtil.getTableName(schemaName, generateUniqueName());
+            virtualNameToRealNameMap.put(virtualName, realName);
+            createTable(conn, virtualName, realName);
+            initValues(conn, virtualName, realName);
+            createIndexes(conn, virtualName, realName);
+        }
+        return realName;
+    }
+    
+    protected String getDisplayTableName(Connection conn, String virtualName) throws Exception {
+        return getTableName(conn, virtualName);
+    }
+
+    private void createTable(Connection conn, String virtualName, String realName) throws SQLException {
+        String ddl = tableDDLMap.get(virtualName);
+        if (ddl == null) {
+            throw new IllegalStateException("Expected to find " + virtualName + " in " + tableDDLMap);
+        }
+        ddl =  ddl.replace(virtualName, realName);
+        conn.createStatement().execute(ddl);
+    }
+
+    @Before
+    public void createSchema() throws SQLException {
+        Connection conn = DriverManager.getConnection(getUrl());
+        try {
+            schemaName = "S_" + generateUniqueName();
+            seqName = "SEQ_" + generateUniqueName();
+            conn.createStatement().execute("CREATE SEQUENCE " + seqName);
+        } finally {
+            conn.close();
+        }
+    }
+    
+    private String translateToVirtualPlan(String actualPlan) {
+        int size = virtualNameToRealNameMap.size();
+        String[] virtualNames = new String[size+1];
+        String[] realNames = new String[size+1];
+        int count = 0;
+        for (Map.Entry<String, String>entry : virtualNameToRealNameMap.entrySet()) {
+            virtualNames[count] = entry.getKey();
+            realNames[count] = entry.getValue();
+            count++;
+        }
+        realNames[count] = schemaName;
+        virtualNames[count]= JOIN_SCHEMA;
+        String convertedPlan =  StringUtil.replace(actualPlan, realNames, virtualNames);
+        return convertedPlan;
+    }
+    
+    protected void assertPlansMatch(String virtualPlanRegEx, String actualPlan) {
+        String convertedPlan = translateToVirtualPlan(actualPlan);
+        assertTrue("\"" + convertedPlan + "\" does not match \"" + virtualPlanRegEx + "\"", Pattern.matches(virtualPlanRegEx, convertedPlan));
+    }
+    
+    protected void assertPlansEqual(String virtualPlan, String actualPlan) {
+        String convertedPlan = translateToVirtualPlan(actualPlan);
+        assertEquals(virtualPlan, convertedPlan);
+    }
+    
+    private static void initValues(Connection conn, String virtualName, String realName) throws Exception {
+        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        if (virtualName.equals(JOIN_CUSTOMER_TABLE_FULL_NAME)) {
+            // Insert into customer table
+            PreparedStatement stmt = conn.prepareStatement(
+                    "upsert into " + realName +
+                    "   (\"customer_id\", " +
+                    "    NAME, " +
+                    "    PHONE, " +
+                    "    ADDRESS, " +
+                    "    LOC_ID, " +
+                    "    DATE) " +
+                    "values (?, ?, ?, ?, ?, ?)");
+            stmt.setString(1, "0000000001");
+            stmt.setString(2, "C1");
+            stmt.setString(3, "999-999-1111");
+            stmt.setString(4, "101 XXX Street");
+            stmt.setString(5, "10001");
+            stmt.setDate(6, new Date(format.parse("2013-11-01 10:20:36").getTime()));
+            stmt.execute();
+                
+            stmt.setString(1, "0000000002");
+            stmt.setString(2, "C2");
+            stmt.setString(3, "999-999-2222");
+            stmt.setString(4, "202 XXX Street");
+            stmt.setString(5, null);
+            stmt.setDate(6, new Date(format.parse("2013-11-25 16:45:07").getTime()));
+            stmt.execute();
+
+            stmt.setString(1, "0000000003");
+            stmt.setString(2, "C3");
+            stmt.setString(3, "999-999-3333");
+            stmt.setString(4, "303 XXX Street");
+            stmt.setString(5, null);
+            stmt.setDate(6, new Date(format.parse("2013-11-25 10:06:29").getTime()));
+            stmt.execute();
+
+            stmt.setString(1, "0000000004");
+            stmt.setString(2, "C4");
+            stmt.setString(3, "999-999-4444");
+            stmt.setString(4, "404 XXX Street");
+            stmt.setString(5, "10004");
+            stmt.setDate(6, new Date(format.parse("2013-11-22 14:22:56").getTime()));
+            stmt.execute();
+
+            stmt.setString(1, "0000000005");
+            stmt.setString(2, "C5");
+            stmt.setString(3, "999-999-5555");
+            stmt.setString(4, "505 XXX Street");
+            stmt.setString(5, "10005");
+            stmt.setDate(6, new Date(format.parse("2013-11-27 09:37:50").getTime()));
+            stmt.execute();
+
+            stmt.setString(1, "0000000006");
+            stmt.setString(2, "C6");
+            stmt.setString(3, "999-999-6666");
+            stmt.setString(4, "606 XXX Street");
+            stmt.setString(5, "10001");
+            stmt.setDate(6, new Date(format.parse("2013-11-01 10:20:36").getTime()));
+            stmt.execute();
+        } else if (virtualName.equals(JOIN_ITEM_TABLE_FULL_NAME)) {
+        
+            // Insert into item table
+            PreparedStatement stmt = conn.prepareStatement(
+                    "upsert into " + realName +
+                    "   (\"item_id\", " +
+                    "    NAME, " +
+                    "    PRICE, " +
+                    "    DISCOUNT1, " +
+                    "    DISCOUNT2, " +
+                    "    \"supplier_id\", " +
+                    "    DESCRIPTION) " +
+                    "values (?, ?, ?, ?, ?, ?, ?)");
+            stmt.setString(1, "0000000001");
+            stmt.setString(2, "T1");
+            stmt.setInt(3, 100);
+            stmt.setInt(4, 5);
+            stmt.setInt(5, 10);
+            stmt.setString(6, "0000000001");
+            stmt.setString(7, "Item T1");
+            stmt.execute();
+
+            stmt.setString(1, "0000000002");
+            stmt.setString(2, "T2");
+            stmt.setInt(3, 200);
+            stmt.setInt(4, 5);
+            stmt.setInt(5, 8);
+            stmt.setString(6, "0000000001");
+            stmt.setString(7, "Item T2");
+            stmt.execute();
+
+            stmt.setString(1, "0000000003");
+            stmt.setString(2, "T3");
+            stmt.setInt(3, 300);
+            stmt.setInt(4, 8);
+            stmt.setInt(5, 12);
+            stmt.setString(6, "0000000002");
+            stmt.setString(7, "Item T3");
+            stmt.execute();
+
+            stmt.setString(1, "0000000004");
+            stmt.setString(2, "T4");
+            stmt.setInt(3, 400);
+            stmt.setInt(4, 6);
+            stmt.setInt(5, 10);
+            stmt.setString(6, "0000000002");
+            stmt.setString(7, "Item T4");
+            stmt.execute();
+
+            stmt.setString(1, "0000000005");
+            stmt.setString(2, "T5");
+            stmt.setInt(3, 500);
+            stmt.setInt(4, 8);
+            stmt.setInt(5, 15);
+            stmt.setString(6, "0000000005");
+            stmt.setString(7, "Item T5");
+            stmt.execute();
+
+            stmt.setString(1, "0000000006");
+            stmt.setString(2, "T6");
+            stmt.setInt(3, 600);
+            stmt.setInt(4, 8);
+            stmt.setInt(5, 15);
+            stmt.setString(6, "0000000006");
+            stmt.setString(7, "Item T6");
+            stmt.execute();
+            
+            stmt.setString(1, "invalid001");
+            stmt.setString(2, "INVALID-1");
+            stmt.setInt(3, 0);
+            stmt.setInt(4, 0);
+            stmt.setInt(5, 0);
+            stmt.setString(6, "0000000000");
+            stmt.setString(7, "Invalid item for join test");
+            stmt.execute();
+        } else if (virtualName.equals(JOIN_SUPPLIER_TABLE_FULL_NAME)) {
+
+            // Insert into supplier table
+            PreparedStatement stmt = conn.prepareStatement(
+                    "upsert into " + realName +
+                    "   (\"supplier_id\", " +
+                    "    NAME, " +
+                    "    PHONE, " +
+                    "    ADDRESS, " +
+                    "    LOC_ID) " +
+                    "values (?, ?, ?, ?, ?)");
+            stmt.setString(1, "0000000001");
+            stmt.setString(2, "S1");
+            stmt.setString(3, "888-888-1111");
+            stmt.setString(4, "101 YYY Street");
+            stmt.setString(5, "10001");
+            stmt.execute();
+                
+            stmt.setString(1, "0000000002");
+            stmt.setString(2, "S2");
+            stmt.setString(3, "888-888-2222");
+            stmt.setString(4, "202 YYY Street");
+            stmt.setString(5, "10002");
+            stmt.execute();
+
+            stmt.setString(1, "0000000003");
+            stmt.setString(2, "S3");
+            stmt.setString(3, "888-888-3333");
+            stmt.setString(4, "303 YYY Street");
+            stmt.setString(5, null);
+            stmt.execute();
+
+            stmt.setString(1, "0000000004");
+            stmt.setString(2, "S4");
+            stmt.setString(3, "888-888-4444");
+            stmt.setString(4, "404 YYY Street");
+            stmt.setString(5, null);
+            stmt.execute();
+
+            stmt.setString(1, "0000000005");
+            stmt.setString(2, "S5");
+            stmt.setString(3, "888-888-5555");
+            stmt.setString(4, "505 YYY Street");
+            stmt.setString(5, "10005");
+            stmt.execute();
+
+            stmt.setString(1, "0000000006");
+            stmt.setString(2, "S6");
+            stmt.setString(3, "888-888-6666");
+            stmt.setString(4, "606 YYY Street");
+            stmt.setString(5, "10006");
+            stmt.execute();
+        } else if (virtualName.equals(JOIN_ORDER_TABLE_FULL_NAME)) {
+
+            // Insert into order table
+            PreparedStatement stmt = conn.prepareStatement(
+                    "upsert into " + realName +
+                    "   (\"order_id\", " +
+                    "    \"customer_id\", " +
+                    "    \"item_id\", " +
+                    "    PRICE, " +
+                    "    QUANTITY," +
+                    "    DATE) " +
+                    "values (?, ?, ?, ?, ?, ?)");
+            stmt.setString(1, "000000000000001");
+            stmt.setString(2, "0000000004");
+            stmt.setString(3, "0000000001");
+            stmt.setInt(4, 100);
+            stmt.setInt(5, 1000);
+            stmt.setTimestamp(6, new Timestamp(format.parse("2013-11-22 14:22:56").getTime()));
+            stmt.execute();
+
+            stmt.setString(1, "000000000000002");
+            stmt.setString(2, "0000000003");
+            stmt.setString(3, "0000000006");
+            stmt.setInt(4, 552);
+            stmt.setInt(5, 2000);
+            stmt.setTimestamp(6, new Timestamp(format.parse("2013-11-25 10:06:29").getTime()));
+            stmt.execute();
+
+            stmt.setString(1, "000000000000003");
+            stmt.setString(2, "0000000002");
+            stmt.setString(3, "0000000002");
+            stmt.setInt(4, 190);
+            stmt.setInt(5, 3000);
+            stmt.setTimestamp(6, new Timestamp(format.parse("2013-11-25 16:45:07").getTime()));
+            stmt.execute();
+
+            stmt.setString(1, "000000000000004");
+            stmt.setString(2, "0000000004");
+            stmt.setString(3, "0000000006");
+            stmt.setInt(4, 510);
+            stmt.setInt(5, 4000);
+            stmt.setTimestamp(6, new Timestamp(format.parse("2013-11-26 13:26:04").getTime()));
+            stmt.execute();
+
+            stmt.setString(1, "000000000000005");
+            stmt.setString(2, "0000000005");
+            stmt.setString(3, "0000000003");
+            stmt.setInt(4, 264);
+            stmt.setInt(5, 5000);
+            stmt.setTimestamp(6, new Timestamp(format.parse("2013-11-27 09:37:50").getTime()));
+            stmt.execute();
+        } else if (virtualName.equals(JOIN_COITEM_TABLE_FULL_NAME)) {
+            // Insert into coitem table
+            PreparedStatement stmt = conn.prepareStatement(
+                    "upsert into " + realName + 
+                    "   (item_id, " + 
+                    "    item_name, " + 
+                    "    co_item_id, " + 
+                    "    co_item_name) " + 
+                    "values (?, ?, ?, ?)");
+            stmt.setString(1, "0000000001");
+            stmt.setString(2, "T1");
+            stmt.setString(3, "0000000002");
+            stmt.setString(4, "T3");
+            stmt.execute();
+            
+            stmt.setString(1, "0000000004");
+            stmt.setString(2, "T4");
+            stmt.setString(3, "0000000003");
+            stmt.setString(4, "T3");
+            stmt.execute();
+            
+            stmt.setString(1, "0000000003");
+            stmt.setString(2, "T4");
+            stmt.setString(3, "0000000005");
+            stmt.setString(4, "T5");
+            stmt.execute();
+            
+            stmt.setString(1, "0000000006");
+            stmt.setString(2, "T6");
+            stmt.setString(3, "0000000001");
+            stmt.setString(4, "T1");
+            stmt.execute();
+        }
+
+        conn.commit();
+    }
+
+	protected Connection getConnection() throws SQLException {
+		Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+		props.put(ServerCacheClient.HASH_JOIN_SERVER_CACHE_RESEND_PER_SERVER, "true");
+		return DriverManager.getConnection(getUrl(), props);
+	}
+	
+    protected void createIndexes(Connection conn, String virtualName, String realName) throws Exception {
+        if (indexDDL != null && indexDDL.length > 0) {
+            for (String ddl : indexDDL) {
+                String newDDL =  ddl.replace(virtualName, realName);
+                if (!newDDL.equals(ddl)) {
+                    conn.createStatement().execute(newDDL);
+                }
+            }
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ee20a8c9/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinCacheIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinCacheIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinCacheIT.java
new file mode 100644
index 0000000..c49c61f
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinCacheIT.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.join;
+
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.Random;
+
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.coprocessor.ObserverContext;
+import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
+import org.apache.hadoop.hbase.coprocessor.SimpleRegionObserver;
+import org.apache.hadoop.hbase.regionserver.RegionScanner;
+import org.apache.phoenix.cache.GlobalCache;
+import org.apache.phoenix.cache.TenantCache;
+import org.apache.phoenix.coprocessor.HashJoinCacheNotFoundException;
+import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
+import org.apache.phoenix.join.HashJoinInfo;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.util.ByteUtil;
+import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.SchemaUtil;
+import org.apache.phoenix.util.TestUtil;
+import org.junit.Test;
+
+public class HashJoinCacheIT extends BaseJoinIT {
+    
+    @Override
+    protected String getTableName(Connection conn, String virtualName) throws Exception {
+        String realName = super.getTableName(conn, virtualName);
+        TestUtil.addCoprocessor(conn, SchemaUtil.normalizeFullTableName(realName), InvalidateHashCache.class);
+        return realName;
+    }
+                
+    @Test
+    public void testExpiredCache() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        props.setProperty(QueryServices.MAX_SERVER_CACHE_TIME_TO_LIVE_MS_ATTRIB, "1");
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String tableName1 = getTableName(conn, JOIN_SUPPLIER_TABLE_FULL_NAME);
+        String tableName2 = getTableName(conn, JOIN_ITEM_TABLE_FULL_NAME);
+        String query = "SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " +
+                tableName1 + " supp RIGHT JOIN " + tableName2 +
+                " item ON item.\"supplier_id\" = supp.\"supplier_id\" ORDER BY \"item_id\"";
+        try {
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            rs.next();
+            fail("HashJoinCacheNotFoundException was not thrown or incorrectly handled");
+        } catch (HashJoinCacheNotFoundException e) {
+            //Expected exception
+        }
+    }
+
+    public static class InvalidateHashCache extends SimpleRegionObserver {
+        public static Random rand= new Random();
+        public static List<ImmutableBytesPtr> lastRemovedJoinIds=new ArrayList<ImmutableBytesPtr>();
+        @Override
+        public RegionScanner preScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c, final Scan scan,
+                final RegionScanner s) throws IOException {
+            final HashJoinInfo joinInfo = HashJoinInfo.deserializeHashJoinFromScan(scan);
+            if (joinInfo != null) {
+                TenantCache cache = GlobalCache.getTenantCache(c.getEnvironment(), null);
+                int count = joinInfo.getJoinIds().length;
+                for (int i = 0; i < count; i++) {
+                    ImmutableBytesPtr joinId = joinInfo.getJoinIds()[i];
+                    if (!ByteUtil.contains(lastRemovedJoinIds,joinId)) {
+                        lastRemovedJoinIds.add(joinId);
+                        cache.removeServerCache(joinId);
+                    }
+                }
+            }
+            return s;
+        }
+        
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ee20a8c9/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinGlobalIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinGlobalIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinGlobalIndexIT.java
new file mode 100644
index 0000000..76944a6
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinGlobalIndexIT.java
@@ -0,0 +1,399 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.join;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.runners.Parameterized.Parameters;
+
+import com.google.common.collect.Lists;
+
+public class HashJoinGlobalIndexIT extends HashJoinIT {
+
+    public HashJoinGlobalIndexIT(String[] indexDDL, String[] plans) {
+        super(indexDDL, plans);
+    }
+
+    @Parameters
+    public static Collection<Object> data() {
+        List<Object> testCases = Lists.newArrayList();
+        testCases.add(new String[][] {
+                {
+                "CREATE INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
+                "CREATE INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
+                "CREATE INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
+                }, {
+                /* 
+                 * testLeftJoinWithAggregation()
+                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
+                 *     LEFT JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     GROUP BY i.name ORDER BY i.name
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [\"I.0:NAME\"]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY",
+                /* 
+                 * testLeftJoinWithAggregation()
+                 *     SELECT i.item_id iid, sum(quantity) q FROM joinOrderTable o 
+                 *     LEFT JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     GROUP BY i.item_id ORDER BY q DESC"
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [\"I.:item_id\"]\n" +
+                "CLIENT MERGE SORT\n" +
+                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY",
+                /* 
+                 * testLeftJoinWithAggregation()
+                 *     SELECT i.item_id iid, sum(quantity) q FROM joinItemTable i 
+                 *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id 
+                 *     GROUP BY i.item_id ORDER BY q DESC NULLS LAST, iid
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [\"I.item_id\"]\n" +
+                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, \"I.item_id\"]\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
+                /* 
+                 * testRightJoinWithAggregation()
+                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
+                 *     RIGHT JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     GROUP BY i.name ORDER BY i.name
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [\"I.0:NAME\"]\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
+                /*
+                 * testRightJoinWithAggregation()
+                 *     SELECT i.item_id iid, sum(quantity) q FROM joinOrderTable o 
+                 *     RIGHT JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     GROUP BY i.item_id ORDER BY q DESC NULLS LAST, iid
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [\"I.item_id\"]\n" +
+                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, \"I.item_id\"]\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME,
+                /*
+                 * testJoinWithWildcard()
+                 *     SELECT * FROM joinItemTable LEFT JOIN joinSupplierTable supp 
+                 *     ON joinItemTable.supplier_id = supp.supplier_id 
+                 *     ORDER BY item_id
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME,
+                /*
+                 * testJoinPlanWithIndex()
+                 *     SELECT item.item_id, item.name, supp.supplier_id, supp.name 
+                 *     FROM joinItemTable item LEFT JOIN joinSupplierTable supp 
+                 *     ON substr(item.name, 2, 1) = substr(supp.name, 2, 1) 
+                 *         AND (supp.name BETWEEN 'S1' AND 'S5') 
+                 *     WHERE item.name BETWEEN 'T1' AND 'T5'
+                 */
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SCHEMA + ".idx_item ['T1'] - ['T5']\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SCHEMA + ".idx_supplier ['S1'] - ['S5']\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY",
+                /*
+                 * testJoinPlanWithIndex()
+                 *     SELECT item.item_id, item.name, supp.supplier_id, supp.name 
+                 *     FROM joinItemTable item INNER JOIN joinSupplierTable supp 
+                 *     ON item.supplier_id = supp.supplier_id 
+                 *     WHERE (item.name = 'T1' OR item.name = 'T5') 
+                 *         AND (supp.name = 'S1' OR supp.name = 'S5')
+                 */
+                "CLIENT PARALLEL 1-WAY SKIP SCAN ON 2 KEYS OVER " + JOIN_SCHEMA + ".idx_item ['T1'] - ['T5']\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY SKIP SCAN ON 2 KEYS OVER " + JOIN_SCHEMA + ".idx_supplier ['S1'] - ['S5']\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY",
+                /*
+                 * testJoinWithSkipMergeOptimization()
+                 *     SELECT s.name FROM joinItemTable i 
+                 *     JOIN joinOrderTable o ON o.item_id = i.item_id AND quantity < 5000 
+                 *     JOIN joinSupplierTable s ON i.supplier_id = s.supplier_id
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "    PARALLEL INNER-JOIN TABLE 0 (SKIP MERGE)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER FILTER BY QUANTITY < 5000\n" +
+                "    PARALLEL INNER-JOIN TABLE 1\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_supplier\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY",
+                /*
+                 * testSelfJoin()
+                 *     SELECT i2.item_id, i1.name FROM joinItemTable i1 
+                 *     JOIN joinItemTable i2 ON i1.item_id = i2.item_id 
+                 *     ORDER BY i1.item_id
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_FULL_NAME + "\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    DYNAMIC SERVER FILTER BY \"I1.item_id\" IN (\"I2.:item_id\")",
+                /*
+                 * testSelfJoin()
+                 *     SELECT i1.name, i2.name FROM joinItemTable i1 
+                 *     JOIN joinItemTable i2 ON i1.item_id = i2.supplier_id 
+                 *     ORDER BY i1.name, i2.name
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER SORTED BY [\"I1.0:NAME\", \"I2.0:NAME\"]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item",
+                /*
+                 * testStarJoin()
+                 *     SELECT order_id, c.name, i.name iname, quantity, o.date 
+                 *     FROM joinOrderTable o 
+                 *     JOIN joinCustomerTable c ON o.customer_id = c.customer_id 
+                 *     JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     ORDER BY order_id
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_customer\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" + 
+                "    PARALLEL INNER-JOIN TABLE 1\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY",
+                /*
+                 * testStarJoin()
+                 *     SELECT (*NO_STAR_JOIN*) order_id, c.name, i.name iname, quantity, o.date 
+                 *     FROM joinOrderTable o 
+                 *     JOIN joinCustomerTable c ON o.customer_id = c.customer_id 
+                 *     JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     ORDER BY order_id
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER SORTED BY [\"O.order_id\"]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            PARALLEL INNER-JOIN TABLE 0\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_customer\n" +
+                "                    SERVER FILTER BY FIRST KEY ONLY",
+                /*
+                 * testSubJoin()
+                 *     SELECT * FROM joinCustomerTable c 
+                 *     INNER JOIN (joinOrderTable o 
+                 *         INNER JOIN (joinSupplierTable s 
+                 *             RIGHT JOIN joinItemTable i ON i.supplier_id = s.supplier_id)
+                 *         ON o.item_id = i.item_id)
+                 *     ON c.customer_id = o.customer_id
+                 *     WHERE c.customer_id <= '0000000005' 
+                 *         AND order_id != '000000000000003' 
+                 *         AND i.name != 'T3' 
+                 *     ORDER BY c.customer_id, i.name
+                 */
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + " [*] - ['0000000005']\n" +
+                "    SERVER SORTED BY [\"C.customer_id\", \"I.0:NAME\"]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER FILTER BY \"order_id\" != '000000000000003'\n" +
+                "            PARALLEL INNER-JOIN TABLE 0\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "                    SERVER FILTER BY \"NAME\" != 'T3'\n" +
+                "                    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "    DYNAMIC SERVER FILTER BY \"C.customer_id\" IN (\"O.customer_id\")",
+                /* 
+                 * testJoinWithSubqueryAndAggregation()
+                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
+                 *     LEFT JOIN (SELECT name, item_id iid FROM joinItemTable) AS i 
+                 *     ON o.item_id = i.iid 
+                 *     GROUP BY i.name ORDER BY i.name
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.NAME]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY",
+                /* 
+                 * testJoinWithSubqueryAndAggregation()
+                 *     SELECT o.iid, sum(o.quantity) q 
+                 *     FROM (SELECT item_id iid, quantity FROM joinOrderTable) AS o 
+                 *     LEFT JOIN (SELECT item_id FROM joinItemTable) AS i 
+                 *     ON o.iid = i.item_id 
+                 *     GROUP BY o.iid ORDER BY q DESC                 
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [O.IID]\n" +
+                "CLIENT MERGE SORT\n" +
+                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0 (SKIP MERGE)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY",
+                /* 
+                 * testJoinWithSubqueryAndAggregation()
+                 *     SELECT i.iid, o.q 
+                 *     FROM (SELECT item_id iid FROM joinItemTable) AS i 
+                 *     LEFT JOIN (SELECT item_id iid, sum(quantity) q FROM joinOrderTable GROUP BY item_id) AS o 
+                 *     ON o.iid = i.iid 
+                 *     ORDER BY o.q DESC NULLS LAST, i.iid
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER SORTED BY [O.Q DESC NULLS LAST, I.IID]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
+                "        CLIENT MERGE SORT",
+                /* 
+                 * testJoinWithSubqueryAndAggregation()
+                 *     SELECT i.iid, o.q 
+                 *     FROM (SELECT item_id iid, sum(quantity) q FROM joinOrderTable GROUP BY item_id) AS o 
+                 *     JOIN (SELECT item_id iid FROM joinItemTable) AS i 
+                 *     ON o.iid = i.iid 
+                 *     ORDER BY o.q DESC, i.iid
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER SORTED BY [O.Q DESC, I.IID]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [\"item_id\"]\n" +
+                "        CLIENT MERGE SORT",
+                /*
+                 * testNestedSubqueries()
+                 *     SELECT * FROM (SELECT customer_id cid, name, phone, address, loc_id, date FROM joinCustomerTable) AS c 
+                 *     INNER JOIN (SELECT o.oid ooid, o.cid ocid, o.iid oiid, o.price * o.quantity, o.date odate, 
+                 *     qi.iiid iiid, qi.iname iname, qi.iprice iprice, qi.idiscount1 idiscount1, qi.idiscount2 idiscount2, qi.isid isid, qi.idescription idescription, 
+                 *     qi.ssid ssid, qi.sname sname, qi.sphone sphone, qi.saddress saddress, qi.sloc_id sloc_id 
+                 *         FROM (SELECT item_id iid, customer_id cid, order_id oid, price, quantity, date FROM joinOrderTable) AS o 
+                 *         INNER JOIN (SELECT i.iid iiid, i.name iname, i.price iprice, i.discount1 idiscount1, i.discount2 idiscount2, i.sid isid, i.description idescription, 
+                 *         s.sid ssid, s.name sname, s.phone sphone, s.address saddress, s.loc_id sloc_id 
+                 *             FROM (SELECT supplier_id sid, name, phone, address, loc_id FROM joinSupplierTable) AS s 
+                 *             RIGHT JOIN (SELECT item_id iid, name, price, discount1, discount2, supplier_id sid, description FROM joinItemTable) AS i 
+                 *             ON i.sid = s.sid) as qi 
+                 *         ON o.iid = qi.iiid) as qo 
+                 *     ON c.cid = qo.ocid 
+                 *     WHERE c.cid <= '0000000005' 
+                 *         AND qo.ooid != '000000000000003' 
+                 *         AND qo.iname != 'T3' 
+                 *     ORDER BY c.cid, qo.iname
+                 */
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_FULL_NAME + " [*] - ['0000000005']\n" +
+                "    SERVER SORTED BY [C.CID, QO.INAME]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "            SERVER FILTER BY \"order_id\" != '000000000000003'\n" +
+                "            PARALLEL INNER-JOIN TABLE 0\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+                "                    SERVER FILTER BY \"NAME\" != 'T3'\n" +
+                "                    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME,
+                /*
+                 * testJoinWithLimit()
+                 *     SELECT order_id, i.name, s.name, s.address, quantity 
+                 *     FROM joinSupplierTable s 
+                 *     LEFT JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
+                 *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 4
+                 */
+                "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "    SERVER 4 ROW LIMIT\n" +
+                "CLIENT 4 ROW LIMIT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_SCHEMA + ".idx_item\n" +
+                "    PARALLEL LEFT-JOIN TABLE 1(DELAYED EVALUATION)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    JOIN-SCANNER 4 ROW LIMIT",
+                /*
+                 * testJoinWithLimit()
+                 *     SELECT order_id, i.name, s.name, s.address, quantity 
+                 *     FROM joinSupplierTable s 
+                 *     JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
+                 *     JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 4
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "CLIENT 4 ROW LIMIT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_SCHEMA + ".idx_item\n" +
+                "    PARALLEL INNER-JOIN TABLE 1(DELAYED EVALUATION)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    DYNAMIC SERVER FILTER BY \"S.supplier_id\" IN (\"I.0:supplier_id\")\n" +
+                "    JOIN-SCANNER 4 ROW LIMIT",
+                /*
+                 * testJoinWithSetMaxRows()
+                 *     statement.setMaxRows(4);
+                 *     SELECT order_id, i.name, quantity FROM joinItemTable i
+                 *     JOIN joinOrderTable o ON o.item_id = i.item_id;
+                 *     SELECT o.order_id, i.name, o.quantity FROM joinItemTable i
+                 *     JOIN (SELECT order_id, item_id, quantity FROM joinOrderTable) o
+                 *     ON o.item_id = i.item_id;
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_SCHEMA + ".idx_item\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "CLIENT 4 ROW LIMIT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    JOIN-SCANNER 4 ROW LIMIT",
+                /*
+                 * testJoinWithLimit()
+                 *     SELECT order_id, i.name, s.name, s.address, quantity 
+                 *     FROM joinSupplierTable s 
+                 *     LEFT JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
+                 *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 1 OFFSET 2
+                 */
+                "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "    SERVER OFFSET 2\n" +
+                "    SERVER 3 ROW LIMIT\n" +
+                "CLIENT 1 ROW LIMIT\n" +
+                "    PARALLEL LEFT-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_SCHEMA + ".idx_item\n" +
+                "    PARALLEL LEFT-JOIN TABLE 1(DELAYED EVALUATION)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    JOIN-SCANNER 3 ROW LIMIT",
+                /*
+                 * testJoinWithLimit()
+                 *     SELECT order_id, i.name, s.name, s.address, quantity 
+                 *     FROM joinSupplierTable s 
+                 *     JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
+                 *     JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 1 OFFSET 2
+                 */
+                "CLIENT SERIAL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_FULL_NAME + "\n" +
+                "    SERVER OFFSET 2\n" +
+                "CLIENT 1 ROW LIMIT\n" +
+                "    PARALLEL INNER-JOIN TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_SCHEMA + ".idx_item\n" +
+                "    PARALLEL INNER-JOIN TABLE 1(DELAYED EVALUATION)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_FULL_NAME + "\n" +
+                "    DYNAMIC SERVER FILTER BY \"S.supplier_id\" IN (\"I.0:supplier_id\")\n" +
+                "    JOIN-SCANNER 3 ROW LIMIT",
+                }});
+        return testCases;
+    }
+}