You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ja...@apache.org on 2014/07/28 01:50:52 UTC

git commit: PHOENIX-1125 SQLException when connection string is of form jdbc:phoenix:localhost:1234:userName

Repository: phoenix
Updated Branches:
  refs/heads/3.0 2df0594ff -> af2f98123


PHOENIX-1125 SQLException when connection string is of form jdbc:phoenix:localhost:1234:userName


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

Branch: refs/heads/3.0
Commit: af2f981239c5b0d8387d31ec0a291fced2a3edc9
Parents: 2df0594
Author: James Taylor <jt...@salesforce.com>
Authored: Sun Jul 27 16:54:00 2014 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Sun Jul 27 16:54:00 2014 -0700

----------------------------------------------------------------------
 .../apache/phoenix/jdbc/PhoenixConnection.java  |  22 +-
 .../phoenix/jdbc/PhoenixDatabaseMetaData.java   |   4 +-
 .../org/apache/phoenix/jdbc/PhoenixDriver.java  |   4 +-
 .../phoenix/jdbc/PhoenixEmbeddedDriver.java     | 160 +++++++-------
 .../phoenix/query/BaseQueryServicesImpl.java    |   4 +-
 .../phoenix/query/ConnectionQueryServices.java  |   2 +
 .../query/ConnectionQueryServicesImpl.java      |   7 +
 .../query/ConnectionlessQueryServicesImpl.java  |  10 +-
 .../query/DelegateConnectionQueryServices.java  |   5 +
 .../apache/phoenix/query/QueryServicesImpl.java |   6 +-
 .../phoenix/query/QueryServicesOptions.java     |   4 +-
 .../org/apache/phoenix/util/ReadOnlyProps.java  |  21 +-
 .../phoenix/jdbc/PhoenixEmbeddedDriverTest.java |  37 ++--
 .../apache/phoenix/jdbc/PhoenixTestDriver.java  |   6 +-
 .../java/org/apache/phoenix/query/BaseTest.java |   4 +-
 .../phoenix/query/ConnectionlessTest.java       | 218 +++++++++++++++++++
 .../phoenix/query/ConnectionlessUpsertTest.java | 182 ----------------
 .../phoenix/query/QueryServicesTestImpl.java    |  10 +-
 pom.xml                                         |   4 +-
 19 files changed, 404 insertions(+), 306 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/af2f9812/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 4209c0f..e712819 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
@@ -41,7 +41,12 @@ import java.sql.Savepoint;
 import java.sql.Statement;
 import java.sql.Struct;
 import java.text.Format;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
 import java.util.concurrent.Executor;
 
 import javax.annotation.Nullable;
@@ -132,7 +137,6 @@ public class PhoenixConnection implements Connection, org.apache.phoenix.jdbc.Jd
         this.isAutoCommit = connection.isAutoCommit;
     }
     
-    @SuppressWarnings("unchecked")
     public PhoenixConnection(ConnectionQueryServices services, String url, Properties info, PMetaData metaData) throws SQLException {
         this.url = url;
         // Copy so client cannot change
@@ -149,11 +153,17 @@ public class PhoenixConnection implements Connection, org.apache.phoenix.jdbc.Jd
             // TODO: we could avoid creating another wrapper if the only property
             // specified was for the tenant ID
             Map<String, String> existingProps = services.getProps().asMap();
-            Map<String, String> tmpAugmentedProps = Maps.newHashMapWithExpectedSize(existingProps.size() + info.size());
+            final Map<String, String> tmpAugmentedProps = Maps.newHashMapWithExpectedSize(existingProps.size() + info.size());
             tmpAugmentedProps.putAll(existingProps);
-            tmpAugmentedProps.putAll((Map)this.info);
-            final ReadOnlyProps augmentedProps = new ReadOnlyProps(tmpAugmentedProps);
-            this.services = new DelegateConnectionQueryServices(services) {
+            boolean needsDelegate = false;
+            for (Entry<Object, Object> entry : this.info.entrySet()) {
+                String key = entry.getKey().toString();
+                String value = entry.getValue().toString();
+                String oldValue = tmpAugmentedProps.put(key, value);
+                needsDelegate |= !Objects.equal(oldValue, value);
+            }
+            this.services = !needsDelegate ? services : new DelegateConnectionQueryServices(services) {
+                final ReadOnlyProps augmentedProps = new ReadOnlyProps(tmpAugmentedProps);
     
                 @Override
                 public ReadOnlyProps getProps() {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/af2f9812/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
index 27a53e4..de799ce 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
@@ -58,6 +58,7 @@ import org.apache.phoenix.schema.tuple.Tuple;
 import org.apache.phoenix.util.ByteUtil;
 import org.apache.phoenix.util.KeyValueUtil;
 import org.apache.phoenix.util.SchemaUtil;
+import org.apache.phoenix.util.StringUtil;
 
 import com.google.common.collect.Lists;
 
@@ -912,7 +913,8 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData, org.apache.pho
 
     @Override
     public String getUserName() throws SQLException {
-        return ""; // FIXME: what should we return here?
+        String userName = connection.getQueryServices().getUserName();
+        return userName == null ? StringUtil.EMPTY_STRING : userName;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/af2f9812/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDriver.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDriver.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDriver.java
index b305d8f..ac8f330 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDriver.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDriver.java
@@ -94,7 +94,7 @@ public final class PhoenixDriver extends PhoenixEmbeddedDriver {
     		synchronized(this) {
     			result = services;
     			if(result == null) {
-    				services = result = new QueryServicesImpl();
+    				services = result = new QueryServicesImpl(getDefaultProps());
     			}
     		}
     	}
@@ -117,7 +117,7 @@ public final class PhoenixDriver extends PhoenixEmbeddedDriver {
         ConnectionQueryServices connectionQueryServices = connectionQueryServicesMap.get(normalizedConnInfo);
         if (connectionQueryServices == null) {
             if (normalizedConnInfo.isConnectionless()) {
-                connectionQueryServices = new ConnectionlessQueryServicesImpl(services);
+                connectionQueryServices = new ConnectionlessQueryServicesImpl(services, normalizedConnInfo);
             } else {
                 connectionQueryServices = new ConnectionQueryServicesImpl(services, normalizedConnInfo);
             }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/af2f9812/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriver.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriver.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriver.java
index 10c24b8..ca27075 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriver.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriver.java
@@ -35,6 +35,7 @@ import org.apache.phoenix.exception.SQLExceptionInfo;
 import org.apache.phoenix.query.ConnectionQueryServices;
 import org.apache.phoenix.query.QueryServices;
 import org.apache.phoenix.util.PhoenixRuntime;
+import org.apache.phoenix.util.PropertiesUtil;
 import org.apache.phoenix.util.ReadOnlyProps;
 import org.apache.phoenix.util.SQLCloseable;
 
@@ -64,7 +65,18 @@ public abstract class PhoenixEmbeddedDriver implements Driver, org.apache.phoeni
     public final static String MINOR_VERSION_PROP = "DriverMinorVersion";
     public final static String DRIVER_NAME_PROP = "DriverName";
     
+    private final ReadOnlyProps defaultProps;
+    
     PhoenixEmbeddedDriver() {
+        Map<String, String> defaultPropsMap = Maps.newHashMapWithExpectedSize(3);
+        defaultPropsMap.put(MAJOR_VERSION_PROP, Integer.toString(getMajorVersion()));
+        defaultPropsMap.put(MINOR_VERSION_PROP, Integer.toString(getMinorVersion()));
+        defaultPropsMap.put(DRIVER_NAME_PROP, getDriverName());
+        defaultProps = new ReadOnlyProps(defaultPropsMap);
+    }
+    
+    protected ReadOnlyProps getDefaultProps() {
+        return defaultProps;
     }
     
     private String getDriverName() {
@@ -109,11 +121,10 @@ public abstract class PhoenixEmbeddedDriver implements Driver, org.apache.phoeni
             return null;
         }
 
-        ConnectionQueryServices connectionServices = getConnectionQueryServices(url, info);
-        info.setProperty(MAJOR_VERSION_PROP, Integer.toString(getMajorVersion()));
-        info.setProperty(MINOR_VERSION_PROP, Integer.toString(getMinorVersion()));
-        info.setProperty(DRIVER_NAME_PROP, getDriverName());
-        PhoenixConnection connection = connectionServices.connect(url, info);
+        Properties augmentedInfo = PropertiesUtil.deepCopy(info);
+        augmentedInfo.putAll(defaultProps.asMap());
+        ConnectionQueryServices connectionServices = getConnectionQueryServices(url, augmentedInfo);
+        PhoenixConnection connection = connectionServices.connect(url, augmentedInfo);
         return connection;
     }
 
@@ -165,72 +176,79 @@ public abstract class PhoenixEmbeddedDriver implements Driver, org.apache.phoeni
      * @since 0.1.1
      */
     public static class ConnectionInfo {
+        private static SQLException getMalFormedUrlException(String url) {
+            return new SQLExceptionInfo.Builder(SQLExceptionCode.MALFORMED_CONNECTION_URL)
+            .setMessage(url).build().buildException();
+        }
+        
+        /**
+         * Detect url with quorum:1,quorum:2 as HBase does not handle different port numbers
+         * for different quorum hostnames.
+         * @param portStr
+         * @return
+         */
+        private static boolean isMultiPortUrl(String portStr) {
+            int commaIndex = portStr.indexOf(',');
+            if (commaIndex > 0) {
+                try {
+                    Integer.parseInt(portStr.substring(0, commaIndex));
+                    return true;
+                } catch (NumberFormatException otherE) {
+                }
+            }
+            return false;
+        }
+        
         protected static ConnectionInfo create(String url) throws SQLException {
             StringTokenizer tokenizer = new StringTokenizer(url == null ? "" : url.substring(PhoenixRuntime.JDBC_PROTOCOL.length()),DELIMITERS, true);
-            int i = 0;
-            boolean isMalformedUrl = false;
+            int nTokens = 0;
             String[] tokens = new String[5];
             String token = null;
-            while (tokenizer.hasMoreTokens() && !(token=tokenizer.nextToken()).equals(TERMINATOR) && tokenizer.hasMoreTokens() && i < tokens.length) {
+            while (tokenizer.hasMoreTokens() && !(token=tokenizer.nextToken()).equals(TERMINATOR) && tokenizer.hasMoreTokens() && nTokens < tokens.length) {
                 token = tokenizer.nextToken();
                 // This would mean we have an empty string for a token which is illegal
                 if (DELIMITERS.contains(token)) {
-                    isMalformedUrl = true;
-                    break;
+                    throw getMalFormedUrlException(url);
                 }
-                tokens[i++] = token;
+                tokens[nTokens++] = token;
             }
+            if (tokenizer.hasMoreTokens() && !TERMINATOR.equals(token)) {
+                throw getMalFormedUrlException(url);
+            }
+            String quorum = null;
             Integer port = null;
-            if (!isMalformedUrl) {
-                if (tokenizer.hasMoreTokens() && !TERMINATOR.equals(token)) {
-                    isMalformedUrl = true;
-                } else if (i > 1) {
+            String rootNode = null;
+            String principal = null;
+            String keytabFile = null;
+            int tokenIndex = 0;
+            if (nTokens > tokenIndex) {
+                quorum = tokens[tokenIndex++]; // Found quorum
+                if (nTokens > tokenIndex) {
                     try {
-                        port = Integer.parseInt(tokens[1]);
-                        isMalformedUrl = port < 0;
-                        if(i == 4){
-                        	if(!tokens[2].endsWith(".keytab")){
-                        		isMalformedUrl = true;
-                        	}
-                        	tokens[4] = tokens[3];
-                        	tokens[3] = tokens[2];
-                        	tokens[2] = null;
+                        port = Integer.parseInt(tokens[tokenIndex]);
+                        if (port < 0) {
+                            throw getMalFormedUrlException(url);
                         }
-                    } catch (NumberFormatException e) {
-                        // If we have 3 tokens, then the second one must be a port.
-                        // If we only have 2 tokens, the second one might be the root node:
-                        // Assume that is the case if we get a NumberFormatException
-                        if (!tokens[1].startsWith("/")) {
-                            isMalformedUrl = true;
+                        tokenIndex++; // Found port
+                    } catch (NumberFormatException e) { // No port information
+                        if (isMultiPortUrl(tokens[tokenIndex])) {
+                            throw getMalFormedUrlException(url);
                         }
-                        if (i == 2) {
-                            tokens[4] = null;
-                            tokens[3] = null;
-                            tokens[2] = tokens[1];
-                            tokens[1] = null;
-                        } else if (i == 3) {
-                            tokens[4] = tokens[2];
-                            tokens[3] = tokens[1];
-                            tokens[2] = null;
-                            tokens[1] = null;
-                        } else if (i == 4) {
-                            tokens[4] = tokens[3];
-                            tokens[3] = tokens[2];
-                            tokens[2] = tokens[1];
-                            tokens[1] = null;
-                        } else if (i == 5) {
-                            tokens[4] = tokens[3];
-                            tokens[3] = tokens[2];
-                            tokens[2] = tokens[1];
+                    }
+                    if (nTokens > tokenIndex) {
+                        if (tokens[tokenIndex].startsWith("/")) {
+                            rootNode = tokens[tokenIndex++]; // Found rootNode
+                        }
+                        if (nTokens > tokenIndex) {
+                            principal = tokens[tokenIndex++]; // Found principal
+                            if (nTokens > tokenIndex) {
+                                keytabFile = tokens[tokenIndex++]; // Found keytabFile
+                            }
                         }
                     }
                 }
             }
-            if (isMalformedUrl) {
-                throw new SQLExceptionInfo.Builder(SQLExceptionCode.MALFORMED_CONNECTION_URL)
-                .setMessage(url).build().buildException();
-            }
-            return new ConnectionInfo(tokens[0],port,tokens[2], tokens[3], tokens[4]);
+            return new ConnectionInfo(quorum,port,rootNode, principal, keytabFile);
         }
         
         public ConnectionInfo normalize(ReadOnlyProps props) throws SQLException {
@@ -273,19 +291,17 @@ public abstract class PhoenixEmbeddedDriver implements Driver, org.apache.phoeni
                 throw new SQLExceptionInfo.Builder(SQLExceptionCode.MALFORMED_CONNECTION_URL)
                 .setMessage("Root node may not be specified when using the connectionless url \"" + this.toString() + "\"").build().buildException();
             }
+            if(principal == null){
+                if (!isConnectionless) {
+                   principal = props.get(QueryServices.HBASE_CLIENT_PRINCIPAL);
+                }
+            }
             if(keytab == null){
             	 if (!isConnectionless) {
             		 keytab = props.get(QueryServices.HBASE_CLIENT_KEYTAB);
             	 }
             }
-            if(principal == null){
-              	 if (!isConnectionless) {
-              		principal = props.get(QueryServices.HBASE_CLIENT_PRINCIPAL);
-              	 }
-              }
-            if (keytab == null || keytab.equals("")) return new ConnectionInfo(zookeeperQuorum,
-                    port, rootNode);
-            else return new ConnectionInfo(zookeeperQuorum, port, rootNode, keytab, principal);
+            return new ConnectionInfo(zookeeperQuorum, port, rootNode, principal, keytab);
         }
         
         private final Integer port;
@@ -296,7 +312,7 @@ public abstract class PhoenixEmbeddedDriver implements Driver, org.apache.phoeni
         private final String keytab;
         
         // used for testing
-        ConnectionInfo(String zookeeperQuorum, Integer port, String rootNode, String keytab, String principal) {
+        ConnectionInfo(String zookeeperQuorum, Integer port, String rootNode, String principal, String keytab) {
             this.zookeeperQuorum = zookeeperQuorum;
             this.port = port;
             this.rootNode = rootNode;
@@ -321,11 +337,9 @@ public abstract class PhoenixEmbeddedDriver implements Driver, org.apache.phoeni
             if (getRootNode() != null) {
                 connectionProps.put(QueryServices.ZOOKEEPER_ROOT_NODE_ATTRIB, getRootNode());
             }
-            if (getKeytab() != null) {
-                connectionProps.put(QueryServices.HBASE_CLIENT_KEYTAB, getKeytab());
-            }
-            if (getPrincipal() != null) {
+            if (getPrincipal() != null && getKeytab() != null) {
                 connectionProps.put(QueryServices.HBASE_CLIENT_PRINCIPAL, getPrincipal());
+                connectionProps.put(QueryServices.HBASE_CLIENT_KEYTAB, getKeytab());
             }
             return connectionProps.isEmpty() ? ReadOnlyProps.EMPTY_PROPS : new ReadOnlyProps(
                     connectionProps.entrySet().iterator());
@@ -362,8 +376,8 @@ public abstract class PhoenixEmbeddedDriver implements Driver, org.apache.phoeni
             result = prime * result + ((zookeeperQuorum == null) ? 0 : zookeeperQuorum.hashCode());
             result = prime * result + ((port == null) ? 0 : port.hashCode());
             result = prime * result + ((rootNode == null) ? 0 : rootNode.hashCode());
+            result = prime * result + ((principal == null) ? 0 : principal.hashCode());
             result = prime * result + ((keytab == null) ? 0 : keytab.hashCode());
-            result = prime * result + ((principal == null) ? 0 : keytab.hashCode());
             return result;
         }
 
@@ -382,12 +396,12 @@ public abstract class PhoenixEmbeddedDriver implements Driver, org.apache.phoeni
             if (rootNode == null) {
                 if (other.rootNode != null) return false;
             } else if (!rootNode.equals(other.rootNode)) return false;
-            if (keytab == null) {
-                if (other.keytab != null) return false;
-            } else if (!keytab.equals(other.keytab)) return false;
             if (principal == null) {
                 if (other.principal != null) return false;
             } else if (!principal.equals(other.principal)) return false;
+            if (keytab == null) {
+                if (other.keytab != null) return false;
+            } else if (!keytab.equals(other.keytab)) return false;
             return true;
         }
         
@@ -395,8 +409,8 @@ public abstract class PhoenixEmbeddedDriver implements Driver, org.apache.phoeni
 		public String toString() {
 			return zookeeperQuorum + (port == null ? "" : ":" + port)
 					+ (rootNode == null ? "" : ":" + rootNode)
-					+ (keytab == null ? "" : ":" + keytab)
-					+ (principal == null ? "" : ":" + principal);
+					+ (principal == null ? "" : ":" + principal)
+					+ (keytab == null ? "" : ":" + keytab);
 		}
     }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/af2f9812/phoenix-core/src/main/java/org/apache/phoenix/query/BaseQueryServicesImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/BaseQueryServicesImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/query/BaseQueryServicesImpl.java
index 222ea14..f116695 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/BaseQueryServicesImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/BaseQueryServicesImpl.java
@@ -40,7 +40,7 @@ public abstract class BaseQueryServicesImpl implements QueryServices {
     private final ReadOnlyProps props;
     private final QueryOptimizer queryOptimizer;
     
-    public BaseQueryServicesImpl(QueryServicesOptions options) {
+    public BaseQueryServicesImpl(ReadOnlyProps defaultProps, QueryServicesOptions options) {
         this.executor =  JobManager.createThreadPoolExec(
                 options.getKeepAliveMs(), 
                 options.getThreadPoolSize(), 
@@ -48,7 +48,7 @@ public abstract class BaseQueryServicesImpl implements QueryServices {
         this.memoryManager = new GlobalMemoryManager(
                 Runtime.getRuntime().maxMemory() * options.getMaxMemoryPerc() / 100,
                 options.getMaxMemoryWaitMs());
-        this.props = options.getProps();
+        this.props = options.getProps(defaultProps);
         this.queryOptimizer = new QueryOptimizer(this);
     }
     

http://git-wip-us.apache.org/repos/asf/phoenix/blob/af2f9812/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServices.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServices.java b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServices.java
index f085464..fd44d6b 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServices.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServices.java
@@ -103,4 +103,6 @@ public interface ConnectionQueryServices extends QueryServices, MetaDataMutated
     
     public enum Feature {REVERSE_SCAN};
     public boolean supportsFeature(Feature feature);
+    
+    public String getUserName();
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/phoenix/blob/af2f9812/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 3ad7ecc..b13ced2 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
@@ -126,6 +126,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
     // Copy of config.getProps(), but read-only to prevent synchronization that we
     // don't need.
     private final ReadOnlyProps props;
+    private final String userName;
     private final ConcurrentHashMap<ImmutableBytesWritable,ConnectionQueryServices> childServices;
     private final StatsManager statsManager;
     // Cache the latest meta data here for future connections
@@ -168,6 +169,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
         // on the server side during testing.
         this.config = HBaseFactoryProvider.getConfigurationFactory().getConfiguration(config);
         this.props = new ReadOnlyProps(this.config.iterator());
+        this.userName = connectionInfo.getPrincipal();
         this.latestMetaData = newEmptyMetaData();
         // TODO: should we track connection wide memory usage or just org-wide usage?
         // If connection-wide, create a MemoryManager here, otherwise just use the one from the delegate
@@ -1749,4 +1751,9 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
         return false;
     }
     
+    @Override
+    public String getUserName() {
+        return userName;
+    }
+    
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/af2f9812/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 a48f811..1c1bba7 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
@@ -46,6 +46,7 @@ import org.apache.phoenix.hbase.index.util.GenericKeyValueBuilder;
 import org.apache.phoenix.hbase.index.util.KeyValueBuilder;
 import org.apache.phoenix.jdbc.PhoenixConnection;
 import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
+import org.apache.phoenix.jdbc.PhoenixEmbeddedDriver.ConnectionInfo;
 import org.apache.phoenix.schema.NewerTableAlreadyExistsException;
 import org.apache.phoenix.schema.PColumn;
 import org.apache.phoenix.schema.PIndexState;
@@ -85,12 +86,14 @@ import com.google.common.collect.Maps;
 public class ConnectionlessQueryServicesImpl extends DelegateQueryServices implements ConnectionQueryServices  {
     private PMetaData metaData;
     private final Map<SequenceKey, SequenceInfo> sequenceMap = Maps.newHashMap();
+    private final String userName;
     private KeyValueBuilder kvBuilder;
     private volatile boolean initialized;
     private volatile SQLException initializationException;
     
-    public ConnectionlessQueryServicesImpl(QueryServices queryServices) {
+    public ConnectionlessQueryServicesImpl(QueryServices queryServices, ConnectionInfo connInfo) {
         super(queryServices);
+        userName = connInfo.getPrincipal();
         metaData = newEmptyMetaData();
         // Use KeyValueBuilder that builds real KeyValues, as our test utils require this
         this.kvBuilder = GenericKeyValueBuilder.INSTANCE;
@@ -407,4 +410,9 @@ public class ConnectionlessQueryServicesImpl extends DelegateQueryServices imple
     public boolean supportsFeature(Feature feature) {
         return false;
     }
+
+    @Override
+    public String getUserName() {
+        return userName;
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/af2f9812/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java b/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
index 4fa7993..3c119de 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
@@ -226,4 +226,9 @@ public class DelegateConnectionQueryServices extends DelegateQueryServices imple
     public boolean supportsFeature(Feature feature) {
         return getDelegate().supportsFeature(feature);
     }
+
+    @Override
+    public String getUserName() {
+        return getDelegate().getUserName();
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/phoenix/blob/af2f9812/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesImpl.java
index beddc46..564da60 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesImpl.java
@@ -17,6 +17,8 @@
  */
 package org.apache.phoenix.query;
 
+import org.apache.phoenix.util.ReadOnlyProps;
+
 
 
 
@@ -30,7 +32,7 @@ package org.apache.phoenix.query;
  */
 public final class QueryServicesImpl extends BaseQueryServicesImpl {
     
-    public QueryServicesImpl() {
-        super(QueryServicesOptions.withDefaults());
+    public QueryServicesImpl(ReadOnlyProps defaultProps) {
+        super(defaultProps, QueryServicesOptions.withDefaults());
     }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/af2f9812/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
index b4db032..07106a6 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
@@ -130,14 +130,14 @@ public class QueryServicesOptions {
         this.config = config;
     }
     
-    public ReadOnlyProps getProps() {
+    public ReadOnlyProps getProps(ReadOnlyProps defaultProps) {
         // Ensure that HBase RPC time out value is at least as large as our thread time out for query. 
         int threadTimeOutMS = config.getInt(THREAD_TIMEOUT_MS_ATTRIB, DEFAULT_THREAD_TIMEOUT_MS);
         int hbaseRPCTimeOut = config.getInt(HConstants.HBASE_RPC_TIMEOUT_KEY, HConstants.DEFAULT_HBASE_RPC_TIMEOUT);
         if (threadTimeOutMS > hbaseRPCTimeOut) {
             config.setInt(HConstants.HBASE_RPC_TIMEOUT_KEY, threadTimeOutMS);
         }
-        return new ReadOnlyProps(config.iterator());
+        return new ReadOnlyProps(defaultProps, config.iterator());
     }
     
     public QueryServicesOptions setAll(ReadOnlyProps props) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/af2f9812/phoenix-core/src/main/java/org/apache/phoenix/util/ReadOnlyProps.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/ReadOnlyProps.java b/phoenix-core/src/main/java/org/apache/phoenix/util/ReadOnlyProps.java
index 19bc5f3..fe039d3 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/ReadOnlyProps.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/ReadOnlyProps.java
@@ -18,12 +18,15 @@
 
 package org.apache.phoenix.util;
 
-import java.util.*;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import com.google.common.collect.*;
+import com.google.common.collect.ImmutableMap;
 
 /**
  * 
@@ -34,11 +37,11 @@ import com.google.common.collect.*;
  * @since 1.2.2
  */
 public class ReadOnlyProps implements Iterable<Entry<String, String>> {
-    public static final ReadOnlyProps EMPTY_PROPS = new ReadOnlyProps(Iterators.<Entry<String, String>>emptyIterator());
+    public static final ReadOnlyProps EMPTY_PROPS = new ReadOnlyProps();
     private final Map<String, String> props;
     
-    public ReadOnlyProps(Iterator<Entry<String, String>> iterator) {
-        Map<String, String> map = Maps.newHashMap();
+    public ReadOnlyProps(ReadOnlyProps defaultProps, Iterator<Entry<String, String>> iterator) {
+        Map<String, String> map = new HashMap<String,String>(defaultProps.asMap());
         while (iterator.hasNext()) {
             Entry<String,String> entry = iterator.next();
             map.put(entry.getKey(), entry.getValue());
@@ -46,6 +49,14 @@ public class ReadOnlyProps implements Iterable<Entry<String, String>> {
         this.props = ImmutableMap.copyOf(map);
     }
 
+    public ReadOnlyProps(Iterator<Entry<String, String>> iterator) {
+        this(EMPTY_PROPS, iterator);
+    }
+
+    private ReadOnlyProps() {
+        this.props = Collections.emptyMap();
+    }
+
     public ReadOnlyProps(Map<String, String> props) {
         this.props = ImmutableMap.copyOf(props);
     }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/af2f9812/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriverTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriverTest.java b/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriverTest.java
index a9b09e5..79f9ec6 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriverTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriverTest.java
@@ -18,14 +18,17 @@
 
 package org.apache.phoenix.jdbc;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
-import java.sql.*;
-
-import org.junit.Test;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.SQLException;
 
 import org.apache.phoenix.exception.SQLExceptionCode;
 import org.apache.phoenix.jdbc.PhoenixEmbeddedDriver.ConnectionInfo;
+import org.junit.Test;
 
 public class PhoenixEmbeddedDriverTest {
     @Test
@@ -47,10 +50,12 @@ public class PhoenixEmbeddedDriverTest {
             "jdbc:phoenix:v1,v2,v3:/hbase;test=true",
             "jdbc:phoenix:v1,v2,v3:123:/hbase",
             "jdbc:phoenix:v1,v2,v3:123:/hbase;test=false",
-            "jdbc:phoenix:v1,v2,v3:123:/hbase:/user.keytab:user/principal;test=false",
-            "jdbc:phoenix:v1,v2,v3:123:/user.keytab:user/principal;test=false",
-            "jdbc:phoenix:v1,v2,v3:/user.keytab:user/principal;test=false",
-            "jdbc:phoenix:v1,v2,v3:/hbase:/user.keytab:user/principal;test=false"
+            "jdbc:phoenix:v1,v2,v3:123:/hbase:user/principal:/user.keytab;test=false",
+            "jdbc:phoenix:v1,v2,v3:123:user/principal:/user.keytab;test=false",
+            "jdbc:phoenix:v1,v2,v3:user/principal:/user.keytab;test=false",
+            "jdbc:phoenix:v1,v2,v3:/hbase:user/principal:/user.keytab;test=false",
+            "jdbc:phoenix:v1,v2,v3:LongRunningQueries;test=false",
+            "jdbc:phoenix:v1,v2,v3:345:LongRunningQueries;test=false",
         };
         ConnectionInfo[] infos = new ConnectionInfo[] {
             new ConnectionInfo(null,null,null),
@@ -69,10 +74,12 @@ public class PhoenixEmbeddedDriverTest {
             new ConnectionInfo("v1,v2,v3",null,"/hbase"),
             new ConnectionInfo("v1,v2,v3",123,"/hbase"),
             new ConnectionInfo("v1,v2,v3",123,"/hbase"),
-            new ConnectionInfo("v1,v2,v3",123,"/hbase", "/user.keytab","user/principal" ),
-            new ConnectionInfo("v1,v2,v3",123, null, "/user.keytab","user/principal" ),
-            new ConnectionInfo("v1,v2,v3", null, null, "/user.keytab","user/principal" ),
-            new ConnectionInfo("v1,v2,v3",null,"/hbase", "/user.keytab","user/principal" )
+            new ConnectionInfo("v1,v2,v3",123,"/hbase","user/principal", "/user.keytab" ),
+            new ConnectionInfo("v1,v2,v3",123, null,"user/principal", "/user.keytab" ),
+            new ConnectionInfo("v1,v2,v3", null, null,"user/principal", "/user.keytab" ),
+            new ConnectionInfo("v1,v2,v3",null,"/hbase","user/principal", "/user.keytab" ),
+            new ConnectionInfo("v1,v2,v3",null,null,"LongRunningQueries", null ),
+            new ConnectionInfo("v1,v2,v3",345,null,"LongRunningQueries", null ),
         };
         assertEquals(urls.length,infos.length);
         for (int i = 0; i < urls.length; i++) {
@@ -89,18 +96,12 @@ public class PhoenixEmbeddedDriverTest {
         String[] urls = new String[] {
             "jdbc:phoenix::",
             "jdbc:phoenix:;",
-            "jdbc:phoenix:localhost:abc:/hbase",
-            "jdbc:phoenix:localhost:abc:/hbase;foo=bar",
-            "jdbc:phoenix:localhost:123:/hbase:blah",
-            "jdbc:phoenix:localhost:123:/hbase:blah;foo=bas",
             "jdbc:phoenix:v1:1,v2:2,v3:3",
             "jdbc:phoenix:v1:1,v2:2,v3:3;test=true",
             "jdbc:phoenix:v1,v2,v3:-1:/hbase;test=true",
             "jdbc:phoenix:v1,v2,v3:-1",
-            "jdbc:phoenix:v1,v2,v3:123a:/hbase;test=true",
             "jdbc:phoenix:v1,v2,v3:123::/hbase",
             "jdbc:phoenix:v1,v2,v3:123::/hbase;test=false",
-            "jdbc:phoenix:v1,v2,v3:123:/hbase:user;test=false"
         };
         for (String url : urls) {
             try {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/af2f9812/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixTestDriver.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixTestDriver.java b/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixTestDriver.java
index 1184fdf..2423344 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixTestDriver.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixTestDriver.java
@@ -56,13 +56,13 @@ public class PhoenixTestDriver extends PhoenixEmbeddedDriver {
 
     public PhoenixTestDriver() {
         this.overrideProps = ReadOnlyProps.EMPTY_PROPS;
-        queryServices = new QueryServicesTestImpl();
+        queryServices = new QueryServicesTestImpl(getDefaultProps());
     }
 
     // For tests to override the default configuration
     public PhoenixTestDriver(ReadOnlyProps props) {
         overrideProps = props;
-        queryServices = new QueryServicesTestImpl(overrideProps);
+        queryServices = new QueryServicesTestImpl(getDefaultProps(),overrideProps);
     }
 
     @Override
@@ -84,7 +84,7 @@ public class PhoenixTestDriver extends PhoenixEmbeddedDriver {
         if (connectionQueryServices != null) { return connectionQueryServices; }
         ConnectionInfo connInfo = ConnectionInfo.create(url);
         if (connInfo.isConnectionless()) {
-            connectionQueryServices = new ConnectionlessQueryServicesImpl(queryServices);
+            connectionQueryServices = new ConnectionlessQueryServicesImpl(queryServices, connInfo);
         } else {
             connectionQueryServices = new ConnectionQueryServicesTestImpl(queryServices, connInfo);
         }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/af2f9812/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 0336d02..c4a59fb 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
@@ -513,8 +513,8 @@ public abstract class BaseTest {
     
     private static void setDefaultTestConfig(Configuration conf) {
         ConfigUtil.setReplicationConfigIfAbsent(conf);
-        QueryServicesOptions options = QueryServicesTestImpl.getDefaultTestServicesOptions();
-        for (Entry<String,String> entry : options.getProps()) {
+        QueryServices services = new PhoenixTestDriver().getQueryServices();
+        for (Entry<String,String> entry : services.getProps()) {
             conf.set(entry.getKey(), entry.getValue());
         }
         //no point doing sanity checks when running tests.

http://git-wip-us.apache.org/repos/asf/phoenix/blob/af2f9812/phoenix-core/src/test/java/org/apache/phoenix/query/ConnectionlessTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/query/ConnectionlessTest.java b/phoenix-core/src/test/java/org/apache/phoenix/query/ConnectionlessTest.java
new file mode 100644
index 0000000..4a58540
--- /dev/null
+++ b/phoenix-core/src/test/java/org/apache/phoenix/query/ConnectionlessTest.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.query;
+
+import static org.junit.Assert.assertArrayEquals;
+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.Connection;
+import java.sql.Date;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.hadoop.hbase.KeyValue;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.Pair;
+import org.apache.phoenix.exception.SQLExceptionCode;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
+import org.apache.phoenix.jdbc.PhoenixDriver;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.SaltingUtil;
+import org.apache.phoenix.util.ByteUtil;
+import org.apache.phoenix.util.PhoenixRuntime;
+import org.apache.phoenix.util.StringUtil;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+public class ConnectionlessTest {
+    private static final int saltBuckets = 200;
+    private static final String orgId = "00D300000000XHP";
+    private static final String keyPrefix1 = "111";
+    private static final String keyPrefix2 = "112";
+    private static final String entityHistoryId1 = "123456789012";
+    private static final String entityHistoryId2 = "987654321098";
+    private static final String name1 = "Eli";
+    private static final String name2 = "Simon";
+    private static final Date now = new Date(System.currentTimeMillis());
+    private static final byte[] unsaltedRowKey1 = ByteUtil.concat(
+            PDataType.CHAR.toBytes(orgId),PDataType.CHAR.toBytes(keyPrefix1),PDataType.CHAR.toBytes(entityHistoryId1));
+    private static final byte[] unsaltedRowKey2 = ByteUtil.concat(
+            PDataType.CHAR.toBytes(orgId),PDataType.CHAR.toBytes(keyPrefix2),PDataType.CHAR.toBytes(entityHistoryId2));
+    private static final byte[] saltedRowKey1 = ByteUtil.concat(
+            new byte[] {SaltingUtil.getSaltingByte(unsaltedRowKey1, 0, unsaltedRowKey1.length, saltBuckets)},
+            unsaltedRowKey1);
+    private static final byte[] saltedRowKey2 = ByteUtil.concat(
+            new byte[] {SaltingUtil.getSaltingByte(unsaltedRowKey2, 0, unsaltedRowKey2.length, saltBuckets)},
+            unsaltedRowKey2);
+
+    private static String getUrl() {
+        return PhoenixRuntime.JDBC_PROTOCOL + PhoenixRuntime.JDBC_PROTOCOL_SEPARATOR + PhoenixRuntime.CONNECTIONLESS;
+    }
+    
+    @BeforeClass
+    public static void verifyDriverRegistered() throws SQLException {
+        assertTrue(DriverManager.getDriver(getUrl()) == PhoenixDriver.INSTANCE);
+    }
+    
+    @Test
+    public void testConnectionlessUpsert() throws Exception {
+        testConnectionlessUpsert(null);
+    }
+    
+    @Test
+    public void testSaltedConnectionlessUpsert() throws Exception {
+        testConnectionlessUpsert(saltBuckets);
+    }
+  
+    private void testConnectionlessUpsert(Integer saltBuckets) throws Exception {
+        String dmlStmt = "create table core.entity_history(\n" +
+        "    organization_id char(15) not null, \n" + 
+        "    key_prefix char(3) not null,\n" +
+        "    entity_history_id char(12) not null,\n" + 
+        "    created_by varchar,\n" + 
+        "    created_date date\n" +
+        "    CONSTRAINT pk PRIMARY KEY (organization_id, key_prefix, entity_history_id) ) " +
+        (saltBuckets == null ? "" : (PhoenixDatabaseMetaData.SALT_BUCKETS + "=" + saltBuckets));
+        Properties props = new Properties();
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        PreparedStatement statement = conn.prepareStatement(dmlStmt);
+        statement.execute();
+        
+        String upsertStmt = "upsert into core.entity_history(organization_id,key_prefix,entity_history_id, created_by, created_date)\n" +
+        "values(?,?,?,?,?)";
+        statement = conn.prepareStatement(upsertStmt);
+        statement.setString(1, orgId);
+        statement.setString(2, keyPrefix2);
+        statement.setString(3, entityHistoryId2);
+        statement.setString(4, name2);
+        statement.setDate(5,now);
+        statement.execute();
+        statement.setString(1, orgId);
+        statement.setString(2, keyPrefix1);
+        statement.setString(3, entityHistoryId1);
+        statement.setString(4, name1);
+        statement.setDate(5,now);
+        statement.execute();
+        
+        Iterator<Pair<byte[],List<KeyValue>>> dataIterator = PhoenixRuntime.getUncommittedDataIterator(conn);
+        Iterator<KeyValue> iterator = dataIterator.next().getSecond().iterator();
+
+        byte[] expectedRowKey1 = saltBuckets == null ? unsaltedRowKey1 : saltedRowKey1;
+        byte[] expectedRowKey2 = saltBuckets == null ? unsaltedRowKey2 : saltedRowKey2;
+        if (Bytes.compareTo(expectedRowKey1, expectedRowKey2) < 0) {
+            assertRow1(iterator, expectedRowKey1);
+            assertRow2(iterator, expectedRowKey2);
+        } else {
+            assertRow2(iterator, expectedRowKey2);
+            assertRow1(iterator, expectedRowKey1);
+        }
+        
+        assertFalse(iterator.hasNext());
+        assertFalse(dataIterator.hasNext());
+        conn.rollback(); // to clear the list of mutations for the next
+    }
+    
+    private static void assertRow1(Iterator<KeyValue> iterator, byte[] expectedRowKey1) {
+        KeyValue kv;
+        assertTrue(iterator.hasNext());
+        kv = iterator.next();
+        assertArrayEquals(expectedRowKey1, kv.getRow());        
+        assertEquals(name1, PDataType.VARCHAR.toObject(kv.getValue()));
+        assertTrue(iterator.hasNext());
+        kv = iterator.next();
+        assertArrayEquals(expectedRowKey1, kv.getRow());        
+        assertEquals(now, PDataType.DATE.toObject(kv.getValue()));
+        assertTrue(iterator.hasNext());
+        kv = iterator.next();
+        assertArrayEquals(expectedRowKey1, kv.getRow());        
+        assertNull(PDataType.VARCHAR.toObject(kv.getValue()));
+    }
+
+    private static void assertRow2(Iterator<KeyValue> iterator, byte[] expectedRowKey2) {
+        KeyValue kv;
+        assertTrue(iterator.hasNext());
+        kv = iterator.next();
+        assertArrayEquals(expectedRowKey2, kv.getRow());        
+        assertEquals(name2, PDataType.VARCHAR.toObject(kv.getValue()));
+        assertTrue(iterator.hasNext());
+        kv = iterator.next();
+        assertArrayEquals(expectedRowKey2, kv.getRow());        
+        assertEquals(now, PDataType.DATE.toObject(kv.getValue()));
+        assertTrue(iterator.hasNext());
+        kv = iterator.next();
+        assertArrayEquals(expectedRowKey2, kv.getRow());        
+        assertNull(PDataType.VARCHAR.toObject(kv.getValue()));
+    }
+    
+    @Test
+    public void testNoConnectionInfo() throws Exception {
+        try {
+            DriverManager.getConnection(PhoenixRuntime.JDBC_PROTOCOL);
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.MALFORMED_CONNECTION_URL.getSQLState(),e.getSQLState());
+        }
+    }
+    
+    @Test
+    public void testMultipleConnectionQueryServices() throws Exception {
+        String url1 = getUrl();
+        String url2 = url1 + PhoenixRuntime.JDBC_PROTOCOL_SEPARATOR + "LongRunningQueries";
+        Connection conn1 = DriverManager.getConnection(url1);
+        try {
+            assertEquals(StringUtil.EMPTY_STRING, conn1.getMetaData().getUserName());
+            Connection conn2 = DriverManager.getConnection(url2);
+            try {
+                assertEquals("LongRunningQueries", conn2.getMetaData().getUserName());
+                ConnectionQueryServices cqs1 = conn1.unwrap(PhoenixConnection.class).getQueryServices();
+                ConnectionQueryServices cqs2 = conn2.unwrap(PhoenixConnection.class).getQueryServices();
+                assertTrue(cqs1 != cqs2);
+                Connection conn3 = DriverManager.getConnection(url1);
+                try {
+                    ConnectionQueryServices cqs3 = conn3.unwrap(PhoenixConnection.class).getQueryServices();
+                    assertTrue(cqs1 == cqs3);
+                    Connection conn4 = DriverManager.getConnection(url2);
+                    try {
+                        ConnectionQueryServices cqs4 = conn4.unwrap(PhoenixConnection.class).getQueryServices();
+                        assertTrue(cqs2 == cqs4);
+                    } finally {
+                        conn4.close();
+                    }
+                } finally {
+                    conn3.close();
+                }
+            } finally {
+                conn2.close();
+            }
+        } finally {
+            conn1.close();
+        }
+        
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/af2f9812/phoenix-core/src/test/java/org/apache/phoenix/query/ConnectionlessUpsertTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/query/ConnectionlessUpsertTest.java b/phoenix-core/src/test/java/org/apache/phoenix/query/ConnectionlessUpsertTest.java
deleted file mode 100644
index bcc3897..0000000
--- a/phoenix-core/src/test/java/org/apache/phoenix/query/ConnectionlessUpsertTest.java
+++ /dev/null
@@ -1,182 +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.query;
-
-import static org.junit.Assert.assertArrayEquals;
-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.Connection;
-import java.sql.Date;
-import java.sql.DriverManager;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Properties;
-
-import org.apache.hadoop.hbase.KeyValue;
-import org.apache.hadoop.hbase.util.Bytes;
-import org.apache.hadoop.hbase.util.Pair;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import org.apache.phoenix.exception.SQLExceptionCode;
-import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
-import org.apache.phoenix.jdbc.PhoenixDriver;
-import org.apache.phoenix.schema.PDataType;
-import org.apache.phoenix.schema.SaltingUtil;
-import org.apache.phoenix.util.ByteUtil;
-import org.apache.phoenix.util.PhoenixRuntime;
-
-
-public class ConnectionlessUpsertTest {
-    private static final int saltBuckets = 200;
-    private static final String orgId = "00D300000000XHP";
-    private static final String keyPrefix1 = "111";
-    private static final String keyPrefix2 = "112";
-    private static final String entityHistoryId1 = "123456789012";
-    private static final String entityHistoryId2 = "987654321098";
-    private static final String name1 = "Eli";
-    private static final String name2 = "Simon";
-    private static final Date now = new Date(System.currentTimeMillis());
-    private static final byte[] unsaltedRowKey1 = ByteUtil.concat(
-            PDataType.CHAR.toBytes(orgId),PDataType.CHAR.toBytes(keyPrefix1),PDataType.CHAR.toBytes(entityHistoryId1));
-    private static final byte[] unsaltedRowKey2 = ByteUtil.concat(
-            PDataType.CHAR.toBytes(orgId),PDataType.CHAR.toBytes(keyPrefix2),PDataType.CHAR.toBytes(entityHistoryId2));
-    private static final byte[] saltedRowKey1 = ByteUtil.concat(
-            new byte[] {SaltingUtil.getSaltingByte(unsaltedRowKey1, 0, unsaltedRowKey1.length, saltBuckets)},
-            unsaltedRowKey1);
-    private static final byte[] saltedRowKey2 = ByteUtil.concat(
-            new byte[] {SaltingUtil.getSaltingByte(unsaltedRowKey2, 0, unsaltedRowKey2.length, saltBuckets)},
-            unsaltedRowKey2);
-
-    private static String getUrl() {
-        return PhoenixRuntime.JDBC_PROTOCOL + PhoenixRuntime.JDBC_PROTOCOL_SEPARATOR + PhoenixRuntime.CONNECTIONLESS;
-    }
-    
-    @BeforeClass
-    public static void verifyDriverRegistered() throws SQLException {
-        assertTrue(DriverManager.getDriver(getUrl()) == PhoenixDriver.INSTANCE);
-    }
-    
-    @Test
-    public void testConnectionlessUpsert() throws Exception {
-        testConnectionlessUpsert(null);
-    }
-    
-    @Test
-    public void testSaltedConnectionlessUpsert() throws Exception {
-        testConnectionlessUpsert(saltBuckets);
-    }
-  
-    public void testConnectionlessUpsert(Integer saltBuckets) throws Exception {
-        String dmlStmt = "create table core.entity_history(\n" +
-        "    organization_id char(15) not null, \n" + 
-        "    key_prefix char(3) not null,\n" +
-        "    entity_history_id char(12) not null,\n" + 
-        "    created_by varchar,\n" + 
-        "    created_date date\n" +
-        "    CONSTRAINT pk PRIMARY KEY (organization_id, key_prefix, entity_history_id) ) " +
-        (saltBuckets == null ? "" : (PhoenixDatabaseMetaData.SALT_BUCKETS + "=" + saltBuckets));
-        Properties props = new Properties();
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-        PreparedStatement statement = conn.prepareStatement(dmlStmt);
-        statement.execute();
-        
-        String upsertStmt = "upsert into core.entity_history(organization_id,key_prefix,entity_history_id, created_by, created_date)\n" +
-        "values(?,?,?,?,?)";
-        statement = conn.prepareStatement(upsertStmt);
-        statement.setString(1, orgId);
-        statement.setString(2, keyPrefix2);
-        statement.setString(3, entityHistoryId2);
-        statement.setString(4, name2);
-        statement.setDate(5,now);
-        statement.execute();
-        statement.setString(1, orgId);
-        statement.setString(2, keyPrefix1);
-        statement.setString(3, entityHistoryId1);
-        statement.setString(4, name1);
-        statement.setDate(5,now);
-        statement.execute();
-        
-        Iterator<Pair<byte[],List<KeyValue>>> dataIterator = PhoenixRuntime.getUncommittedDataIterator(conn);
-        Iterator<KeyValue> iterator = dataIterator.next().getSecond().iterator();
-
-        byte[] expectedRowKey1 = saltBuckets == null ? unsaltedRowKey1 : saltedRowKey1;
-        byte[] expectedRowKey2 = saltBuckets == null ? unsaltedRowKey2 : saltedRowKey2;
-        if (Bytes.compareTo(expectedRowKey1, expectedRowKey2) < 0) {
-            assertRow1(iterator, expectedRowKey1);
-            assertRow2(iterator, expectedRowKey2);
-        } else {
-            assertRow2(iterator, expectedRowKey2);
-            assertRow1(iterator, expectedRowKey1);
-        }
-        
-        assertFalse(iterator.hasNext());
-        assertFalse(dataIterator.hasNext());
-        conn.rollback(); // to clear the list of mutations for the next
-    }
-    
-    private static void assertRow1(Iterator<KeyValue> iterator, byte[] expectedRowKey1) {
-        KeyValue kv;
-        assertTrue(iterator.hasNext());
-        kv = iterator.next();
-        assertArrayEquals(expectedRowKey1, kv.getRow());        
-        assertEquals(name1, PDataType.VARCHAR.toObject(kv.getValue()));
-        assertTrue(iterator.hasNext());
-        kv = iterator.next();
-        assertArrayEquals(expectedRowKey1, kv.getRow());        
-        assertEquals(now, PDataType.DATE.toObject(kv.getValue()));
-        assertTrue(iterator.hasNext());
-        kv = iterator.next();
-        assertArrayEquals(expectedRowKey1, kv.getRow());        
-        assertNull(PDataType.VARCHAR.toObject(kv.getValue()));
-    }
-
-    private static void assertRow2(Iterator<KeyValue> iterator, byte[] expectedRowKey2) {
-        KeyValue kv;
-        assertTrue(iterator.hasNext());
-        kv = iterator.next();
-        assertArrayEquals(expectedRowKey2, kv.getRow());        
-        assertEquals(name2, PDataType.VARCHAR.toObject(kv.getValue()));
-        assertTrue(iterator.hasNext());
-        kv = iterator.next();
-        assertArrayEquals(expectedRowKey2, kv.getRow());        
-        assertEquals(now, PDataType.DATE.toObject(kv.getValue()));
-        assertTrue(iterator.hasNext());
-        kv = iterator.next();
-        assertArrayEquals(expectedRowKey2, kv.getRow());        
-        assertNull(PDataType.VARCHAR.toObject(kv.getValue()));
-    }
-    
-    @Test
-    public void testNoConnectionInfo() throws Exception {
-        try {
-            DriverManager.getConnection(PhoenixRuntime.JDBC_PROTOCOL);
-            fail();
-        } catch (SQLException e) {
-            assertEquals(SQLExceptionCode.MALFORMED_CONNECTION_URL.getSQLState(),e.getSQLState());
-        }
-    }
-    
-
-}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/af2f9812/phoenix-core/src/test/java/org/apache/phoenix/query/QueryServicesTestImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/query/QueryServicesTestImpl.java b/phoenix-core/src/test/java/org/apache/phoenix/query/QueryServicesTestImpl.java
index 72e7aef..e125755 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/query/QueryServicesTestImpl.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/query/QueryServicesTestImpl.java
@@ -53,11 +53,11 @@ public final class QueryServicesTestImpl extends BaseQueryServicesImpl {
     public static final long DEFAULT_MAX_SERVER_METADATA_CACHE_SIZE =  1024L*1024L*4L; // 4 Mb
     public static final long DEFAULT_MAX_CLIENT_METADATA_CACHE_SIZE =  1024L*1024L*2L; // 2 Mb
     
-    public QueryServicesTestImpl() {
-        this(ReadOnlyProps.EMPTY_PROPS);
+    public QueryServicesTestImpl(ReadOnlyProps defaultProps) {
+        this(defaultProps, ReadOnlyProps.EMPTY_PROPS);
     }
     
-    public static QueryServicesOptions getDefaultTestServicesOptions() {
+    private static QueryServicesOptions getDefaultServicesOptions() {
     	return withDefaults()
                 .setThreadPoolSize(DEFAULT_THREAD_POOL_SIZE)
                 .setQueueSize(DEFAULT_QUEUE_SIZE)
@@ -81,7 +81,7 @@ public final class QueryServicesTestImpl extends BaseQueryServicesImpl {
                 .setMaxServerMetaDataCacheSize(DEFAULT_MAX_SERVER_METADATA_CACHE_SIZE);
     }
     
-    public QueryServicesTestImpl(ReadOnlyProps overrideProps) {
-        super(getDefaultTestServicesOptions().setAll(overrideProps));
+    public QueryServicesTestImpl(ReadOnlyProps defaultProps, ReadOnlyProps overrideProps) {
+        super(defaultProps, getDefaultServicesOptions().setAll(overrideProps));
     }    
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/af2f9812/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 2fe91f5..a57a3ab 100644
--- a/pom.xml
+++ b/pom.xml
@@ -112,8 +112,8 @@
     <maven.rat.version>0.8</maven.rat.version>
     
     <!-- Plugin options -->
-    <numForkedUT>2</numForkedUT>
-    <numForkedIT>2</numForkedIT>
+    <numForkedUT>3</numForkedUT>
+    <numForkedIT>7</numForkedIT>
     
     <!-- Set default encoding so multi-byte tests work correctly on the Mac -->
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>