Index: oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryManagerImpl.java
===================================================================
--- oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryManagerImpl.java	(revision 1396532)
+++ oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryManagerImpl.java	(working copy)
@@ -33,12 +33,13 @@
 import javax.jcr.query.QueryManager;
 import javax.jcr.query.QueryResult;
 import javax.jcr.query.qom.QueryObjectModelFactory;
-import org.apache.jackrabbit.oak.api.CoreValue;
+import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.SessionQueryEngine;
 import org.apache.jackrabbit.oak.api.Result;
 import org.apache.jackrabbit.oak.jcr.SessionDelegate;
 import org.apache.jackrabbit.oak.jcr.query.qom.QueryObjectModelFactoryImpl;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 
 /**
  * The implementation of the corresponding JCR interface.
@@ -110,7 +111,7 @@
     public QueryResult executeQuery(String statement, String language,
             long limit, long offset, HashMap<String, Value> bindVariableMap) throws RepositoryException {
         try {
-            HashMap<String, CoreValue> bindMap = convertMap(bindVariableMap);
+            HashMap<String, PropertyState> bindMap = convertMap(bindVariableMap);
             NamePathMapper namePathMapper = sessionDelegate.getNamePathMapper();
             Result r = queryEngine.executeQuery(statement, language, limit, offset,
                     bindMap, sessionDelegate.getRoot(), namePathMapper);
@@ -122,10 +123,10 @@
         }
     }
 
-    private HashMap<String, CoreValue> convertMap(HashMap<String, Value> bindVariableMap) {
-        HashMap<String, CoreValue> map = new HashMap<String, CoreValue>();
+    private HashMap<String, PropertyState> convertMap(HashMap<String, Value> bindVariableMap) {
+        HashMap<String, PropertyState> map = new HashMap<String, PropertyState>();
         for (Entry<String, Value> e : bindVariableMap.entrySet()) {
-            map.put(e.getKey(), sessionDelegate.getValueFactory().getCoreValue(e.getValue()));
+            map.put(e.getKey(), PropertyStates.createProperty("", sessionDelegate.getValueFactory().getCoreValue(e.getValue())));
         }
         return map;
     }
Index: oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryResultImpl.java
===================================================================
--- oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryResultImpl.java	(revision 1396532)
+++ oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryResultImpl.java	(working copy)
@@ -30,13 +30,14 @@
 
 import org.apache.jackrabbit.commons.iterator.NodeIteratorAdapter;
 import org.apache.jackrabbit.commons.iterator.RowIteratorAdapter;
-import org.apache.jackrabbit.oak.api.CoreValue;
+import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Result;
 import org.apache.jackrabbit.oak.api.ResultRow;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.jcr.NodeDelegate;
 import org.apache.jackrabbit.oak.jcr.NodeImpl;
 import org.apache.jackrabbit.oak.jcr.SessionDelegate;
+import org.apache.jackrabbit.oak.plugins.memory.CoreValues;
 
 /**
  * The implementation of the corresponding JCR interface.
@@ -227,8 +228,8 @@
         };
     }
 
-    Value createValue(CoreValue value) {
-        return value == null ? null : sessionDelegate.getValueFactory().createValue(value);
+    Value createValue(PropertyState value) {
+        return value == null ? null : sessionDelegate.getValueFactory().createValue(CoreValues.getValue(value));
     }
 
 }
Index: oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/RowImpl.java
===================================================================
--- oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/RowImpl.java	(revision 1396532)
+++ oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/RowImpl.java	(working copy)
@@ -22,7 +22,8 @@
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 import javax.jcr.query.Row;
-import org.apache.jackrabbit.oak.api.CoreValue;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.ResultRow;
 
 /**
@@ -89,7 +90,7 @@
 
     @Override
     public Value[] getValues() throws RepositoryException {
-        CoreValue[] values = row.getValues();
+        PropertyState[] values = row.getValues();
         int len = values.length;
         Value[] v2 = new Value[values.length];
         for (int i = 0; i < len; i++) {
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java	(revision 1396532)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java	(working copy)
@@ -25,7 +25,7 @@
 import org.apache.jackrabbit.oak.kernel.KernelNodeStore;
 import org.apache.jackrabbit.oak.plugins.index.IndexDefinition;
 import org.apache.jackrabbit.oak.plugins.index.IndexDefinitionImpl;
-import org.apache.jackrabbit.oak.plugins.memory.MemoryValueFactory;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.query.ast.Operator;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
 import org.apache.jackrabbit.oak.security.authorization.AccessControlProviderImpl;
@@ -62,7 +62,7 @@
         FilterImpl filter = new FilterImpl(null);
         filter.restrictPath("/", Filter.PathRestriction.EXACT);
         filter.restrictProperty("foo", Operator.EQUAL,
-                MemoryValueFactory.INSTANCE.createValue("bar"));
+                PropertyStates.stringProperty("", "bar"));
         Cursor cursor = index.query(filter, store.getRoot());
         assertTrue(cursor.next());
         assertEquals("/", cursor.currentRow().getPath());
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/FilterTest.java
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/FilterTest.java	(revision 1396532)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/FilterTest.java	(working copy)
@@ -25,8 +25,8 @@
 import java.util.ArrayList;
 import java.util.Random;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
-import org.apache.jackrabbit.oak.plugins.memory.StringValue;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.query.ast.Operator;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.junit.Test;
@@ -39,8 +39,8 @@
     @Test
     public void propertyRestriction() {
 
-        CoreValue one = new StringValue("1");
-        CoreValue two = new StringValue("2");
+        PropertyState one = PropertyStates.stringProperty("", "1");
+        PropertyState two = PropertyStates.stringProperty("", "2");
 
         FilterImpl f = new FilterImpl(null);
         assertTrue(null == f.getPropertyRestriction("x"));
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java	(revision 1396532)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java	(working copy)
@@ -37,15 +37,17 @@
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.oak.AbstractOakTest;
 import org.apache.jackrabbit.oak.api.ContentSession;
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.CoreValueFactory;
+import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Result;
 import org.apache.jackrabbit.oak.api.ResultRow;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.SessionQueryEngine;
 import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -107,7 +109,7 @@
     }
 
     protected Result executeQuery(String statement, String language,
-            HashMap<String, CoreValue> sv) throws ParseException {
+            HashMap<String, PropertyState> sv) throws ParseException {
         return qe.executeQuery(statement, language, Long.MAX_VALUE, 0, sv,
                 session.getLatestRoot(), null);
     }
@@ -141,15 +143,15 @@
                 vf);
         root.commit();
 
-        HashMap<String, CoreValue> sv = new HashMap<String, CoreValue>();
-        sv.put("id", vf.createValue("1"));
+        HashMap<String, PropertyState> sv = new HashMap<String, PropertyState>();
+        sv.put("id", PropertyStates.stringProperty("", "1"));
         Iterator<? extends ResultRow> result;
         result = executeQuery("select * from [nt:base] where id = $id",
                 QueryEngineImpl.SQL2, sv).getRows().iterator();
         assertTrue(result.hasNext());
         assertEquals("/test/hello", result.next().getPath());
 
-        sv.put("id", vf.createValue("2"));
+        sv.put("id", PropertyStates.stringProperty("", "2"));
         result = executeQuery("select * from [nt:base] where id = $id",
                 QueryEngineImpl.SQL2, sv).getRows().iterator();
         assertTrue(result.hasNext());
@@ -288,13 +290,13 @@
 
     protected static String readRow(ResultRow row) {
         StringBuilder buff = new StringBuilder();
-        CoreValue[] values = row.getValues();
+        PropertyState[] values = row.getValues();
         for (int i = 0; i < values.length; i++) {
             if (i > 0) {
                 buff.append(", ");
             }
-            CoreValue v = values[i];
-            buff.append(v == null ? "null" : v.getString());
+            PropertyState v = values[i];
+            buff.append(v == null ? "null" : v.getValue(Type.STRING));
         }
         return buff.toString();
     }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/IdentifierManager.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/IdentifierManager.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/IdentifierManager.java	(working copy)
@@ -33,7 +33,6 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import org.apache.jackrabbit.JcrConstants;
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Result;
 import org.apache.jackrabbit.oak.api.ResultRow;
@@ -42,7 +41,7 @@
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
-import org.apache.jackrabbit.oak.plugins.memory.StringValue;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -156,8 +155,8 @@
      * such tree exists or isn't accessible to the content session.
      */
     @CheckForNull
-    public String getPath(CoreValue referenceValue) {
-        int type = referenceValue.getType();
+    public String getPath(PropertyState referenceValue) {
+        int type = referenceValue.getType().tag();
         if (type == PropertyType.REFERENCE || type == PropertyType.WEAKREFERENCE) {
             return resolveUUID(referenceValue);
         } else {
@@ -188,7 +187,7 @@
                 final String uuid = getIdentifier(tree);
                 String reference = weak ? PropertyType.TYPENAME_WEAKREFERENCE : PropertyType.TYPENAME_REFERENCE;
                 String pName = propertyName == null ? "*" : propertyName;   // TODO: sanitize against injection attacks!?
-                Map<String, ? extends CoreValue> bindings = Collections.singletonMap("uuid", new StringValue(uuid));
+                Map<String, ? extends PropertyState> bindings = Collections.singletonMap("uuid", PropertyStates.stringProperty("", uuid));
 
                 Result result = root.getQueryEngine().executeQuery(
                         "SELECT * FROM [nt:base] WHERE PROPERTY([" + pName + "], '" + reference + "') = $uuid",
@@ -283,12 +282,12 @@
 
     @CheckForNull
     private String resolveUUID(String uuid) {
-        return resolveUUID(new StringValue(uuid));
+        return resolveUUID(PropertyStates.stringProperty("", uuid));
     }
 
-    private String resolveUUID(CoreValue uuid) {
+    private String resolveUUID(PropertyState uuid) {
         try {
-            Map<String, CoreValue> bindings = Collections.singletonMap("id", uuid);
+            Map<String, PropertyState> bindings = Collections.singletonMap("id", uuid);
             Result result = root.getQueryEngine().executeQuery(
                     "SELECT * FROM [nt:base] WHERE [jcr:uuid] = $id", Query.JCR_SQL2,
                     Long.MAX_VALUE, 0, bindings, root, new NamePathMapper.Default());
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexUpdate.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexUpdate.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexUpdate.java	(working copy)
@@ -51,22 +51,21 @@
         this.remove = Maps.newHashMap();
     }
 
-    public void insert(String path, Iterable<CoreValue> values) {
+    public void insert(String path, PropertyState value) {
         Preconditions.checkArgument(path.startsWith(this.path));
-        putValues(insert, path.substring(this.path.length()), values);
+        putValues(insert, path.substring(this.path.length()), value);
     }
 
-    public void remove(String path, Iterable<CoreValue> values) {
+    public void remove(String path, PropertyState value) {
         Preconditions.checkArgument(path.startsWith(this.path));
-        putValues(remove, path.substring(this.path.length()), values);
+        putValues(remove, path.substring(this.path.length()), value);
     }
 
-    private static void putValues(
-            Map<String, Set<String>> map,
-            String path, Iterable<CoreValue> values) {
-        for (CoreValue value : values) {
-            if (value.getType() != PropertyType.BINARY) {
-                String key = PropertyIndex.encode(value);
+    private static void putValues(Map<String, Set<String>> map, String path,
+            PropertyState value) {
+        if (value.getType().tag() != PropertyType.BINARY) {
+            List<String> keys = PropertyIndex.encode(value);
+            for (String key : keys) {
                 Set<String> paths = map.get(key);
                 if (paths == null) {
                     paths = Sets.newHashSet();
@@ -77,7 +76,6 @@
         }
     }
 
-
     public void apply() throws CommitFailedException {
         boolean unique = node.getProperty("unique") != null;
         NodeBuilder index = node.child(":index");
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexDiff.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexDiff.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexDiff.java	(working copy)
@@ -22,7 +22,6 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.plugins.memory.CoreValues;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -109,22 +108,22 @@
     @Override
     public void propertyAdded(PropertyState after) {
         for (PropertyIndexUpdate update : getIndexes(after.getName())) {
-            update.insert(getPath(), CoreValues.getValues(after));
+            update.insert(getPath(), after);
         }
     }
 
     @Override
     public void propertyChanged(PropertyState before, PropertyState after) {
         for (PropertyIndexUpdate update : getIndexes(after.getName())) {
-            update.remove(getPath(), CoreValues.getValues(before));
-            update.insert(getPath(), CoreValues.getValues(after));
+            update.remove(getPath(), before);
+            update.insert(getPath(), after);
         }
     }
 
     @Override
     public void propertyDeleted(PropertyState before) {
         for (PropertyIndexUpdate update : getIndexes(before.getName())) {
-            update.remove(getPath(), CoreValues.getValues(before));
+            update.remove(getPath(), before);
         }
     }
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java	(working copy)
@@ -18,11 +18,9 @@
 
 import java.util.Set;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
-import org.apache.jackrabbit.oak.plugins.memory.CoreValues;
-import org.apache.jackrabbit.oak.plugins.memory.StringValue;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
@@ -66,10 +64,10 @@
     }
 
     public Set<String> find(String name, String value) {
-        return find(name, new StringValue(value));
+        return find(name, PropertyStates.stringProperty("", value));
     }
 
-    public Set<String> find(String name, CoreValue value) {
+    public Set<String> find(String name, PropertyState value) {
         Set<String> paths = Sets.newHashSet();
 
         PropertyState property = null;
@@ -79,7 +77,8 @@
             if (state != null) {
                 state = state.getChildNode(":index");
                 if (state != null) {
-                    property = state.getProperty(PropertyIndex.encode(value));
+                    //TODO what happens when I search using an mvp?
+                    property = state.getProperty(PropertyIndex.encode(value).get(0));
                 }
             }
         }
@@ -92,12 +91,9 @@
         } else {
             // No index available, so first check this node for a match
             property = root.getProperty(name);
-            if (property != null) {
-                for (CoreValue cv : CoreValues.getValues(property)) {
-                    if (cv.equals(value)) {
-                        paths.add("");
-                        break;
-                    }
+            if (property != null){
+                if(PropertyStates.match(property, value)){
+                    paths.add("");                    
                 }
             }
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java	(working copy)
@@ -18,17 +18,20 @@
 
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Set;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.query.index.IndexRowImpl;
 import org.apache.jackrabbit.oak.query.index.TraversingCursor;
 import org.apache.jackrabbit.oak.spi.query.Cursor;
 import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction;
 import org.apache.jackrabbit.oak.spi.query.IndexRow;
-import org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction;
 import org.apache.jackrabbit.oak.spi.query.QueryIndex;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
@@ -39,16 +42,20 @@
 
     private static final int MAX_STRING_LENGTH = 100; // TODO: configurable
 
-    static String encode(CoreValue value) {
-        try {
-            String string = value.getString();
-            if (string.length() > MAX_STRING_LENGTH) {
-                string.substring(0, MAX_STRING_LENGTH);
+    static List<String> encode(PropertyState value) {
+        List<String> values = new ArrayList<String>();
+
+        for (String v : value.getValue(Type.STRINGS)) {
+            try {
+                if (v.length() > MAX_STRING_LENGTH) {
+                    v = v.substring(0, MAX_STRING_LENGTH);
+                }
+                values.add(URLEncoder.encode(v, Charsets.UTF_8.name()));
+            } catch (UnsupportedEncodingException e) {
+                throw new IllegalStateException("UTF-8 is unsupported", e);
             }
-            return URLEncoder.encode(string, Charsets.UTF_8.name());
-        } catch (UnsupportedEncodingException e) {
-           throw new IllegalStateException("UTF-8 is unsupported", e);
         }
+        return values;
     }
 
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneEditor.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneEditor.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneEditor.java	(working copy)
@@ -26,11 +26,11 @@
 
 import javax.jcr.PropertyType;
 
+import org.apache.jackrabbit.oak.api.Blob;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.plugins.index.IndexDefinition;
-import org.apache.jackrabbit.oak.plugins.memory.CoreValues;
 import org.apache.jackrabbit.oak.spi.commit.CommitHook;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
@@ -220,28 +220,30 @@
             document.add(newPathField(path));
             for (PropertyState property : state.getProperties()) {
                 String pname = property.getName();
-                for (CoreValue value : CoreValues.getValues(property)) {
-                    document.add(newPropertyField(pname,
-                            parseStringValue(value)));
+                switch (property.getType().tag()) {
+                case PropertyType.BINARY:
+                    for (Blob v : property.getValue(Type.BINARIES)) {
+                        document.add(newPropertyField(pname,
+                                parseStringValue(v)));
+                    }
+                    break;
+                default:
+                    for (String v : property.getValue(Type.STRINGS)) {
+                        document.add(newPropertyField(pname, v));
+                    }
+                    break;
                 }
             }
             return document;
         }
 
-        private static String parseStringValue(CoreValue value) {
-            String string;
-            if (value.getType() != PropertyType.BINARY) {
-                string = value.getString();
-            } else {
-                try {
-                    string = TIKA.parseToString(value.getNewStream());
-                } catch (IOException e) {
-                    string = "";
-                } catch (TikaException e) {
-                    string = "";
-                }
+        private static String parseStringValue(Blob v) {
+            try {
+                return TIKA.parseToString(v.getNewStream());
+            } catch (IOException e) {
+            } catch (TikaException e) {
             }
-            return string;
+            return "";
         }
 
     }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java	(working copy)
@@ -29,6 +29,7 @@
 import javax.jcr.nodetype.NodeTypeManager;
 
 import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.core.ReadOnlyTree;
 import org.apache.jackrabbit.oak.plugins.index.IndexDefinition;
@@ -200,10 +201,10 @@
             boolean isLike = pr.isLike;
 
             if (pr.first != null) {
-                first = pr.first.getString();
+                first = pr.first.getValue(Type.STRING);
             }
             if (pr.last != null) {
-                last = pr.last.getString();
+                last = pr.last.getValue(Type.STRING);
             }
 
             if (isLike) {
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/PrefixContentIndex.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/PrefixContentIndex.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/PrefixContentIndex.java	(working copy)
@@ -22,6 +22,7 @@
 
 import javax.jcr.PropertyType;
 
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.kernel.TypeCodes;
 import org.apache.jackrabbit.oak.query.index.IndexRowImpl;
@@ -80,7 +81,7 @@
             throw new IllegalArgumentException("No restriction for *");
         }
         // TODO need to use correct json representation
-        String v = restriction.first.getString();
+        String v = restriction.first.getValue(Type.STRING);
         v = index.getPrefix() + v;
         return "prefixIndex \"" + v + '"';
     }
@@ -92,7 +93,7 @@
             throw new IllegalArgumentException("No restriction for *");
         }
         // TODO need to use correct json representation
-        String v = restriction.first.getString();
+        String v = restriction.first.getValue(Type.STRING);
         v = index.getPrefix() + v;
         // TODO revisit code after the removal of revisionId
         String revisionId = "";
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/PropertyContentIndex.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/PropertyContentIndex.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/PropertyContentIndex.java	(working copy)
@@ -19,7 +19,9 @@
 package org.apache.jackrabbit.oak.plugins.index.old;
 
 import java.util.Iterator;
-import org.apache.jackrabbit.oak.api.CoreValue;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.query.index.IndexRowImpl;
 import org.apache.jackrabbit.oak.spi.query.Cursor;
 import org.apache.jackrabbit.oak.spi.query.Filter;
@@ -69,8 +71,8 @@
         if (restriction == null) {
             throw new IllegalArgumentException("No restriction for " + propertyName);
         }
-        CoreValue first = restriction.first;
-        String f = first == null ? null : first.toString();
+        PropertyState first = restriction.first;
+        String f = first == null ? null : first.getValue(Type.STRING);
         // TODO revisit code after the removal of revisionId
         String revisionId = "";
         Iterator<String> it = index.getPaths(f, revisionId);
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/EmptyPropertyState.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/EmptyPropertyState.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/EmptyPropertyState.java	(working copy)
@@ -26,6 +26,8 @@
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
 
+import com.google.common.collect.Iterables;
+
 import static org.apache.jackrabbit.oak.api.Type.BINARIES;
 import static org.apache.jackrabbit.oak.api.Type.STRING;
 import static org.apache.jackrabbit.oak.api.Type.STRINGS;
@@ -110,7 +112,8 @@
                 return getValue(BINARIES).equals(that.getValue(BINARIES));
             }
             else {
-                return getValue(STRINGS).equals(that.getValue(STRINGS));
+                return Iterables.elementsEqual(getValue(STRINGS),
+                        that.getValue(STRINGS));
             }
         }
         else {
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/BinaryValue.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/BinaryValue.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/BinaryValue.java	(working copy)
@@ -17,10 +17,13 @@
 package org.apache.jackrabbit.oak.plugins.memory;
 
 import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.io.InputStream;
 
 import javax.jcr.PropertyType;
 
+import com.google.common.base.Charsets;
+
 public final class BinaryValue extends MemoryValue {
 
     private final byte[] value;
@@ -44,9 +47,19 @@
         return new ByteArrayInputStream(value);
     }
 
+    /**
+     * Gets the string representation of this binary value.
+     * 
+     * @return string representation of this binary value.
+     * 
+     */
     @Override
     public String getString() {
-        return "<binary>";
+        try {
+            return new String(value, Charsets.UTF_8.name());
+        } catch (IOException e) {
+            throw new IllegalStateException("UTF-8 is unsupported", e);
+        }
     }
 
     @Override
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/AbstractBlob.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/AbstractBlob.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/AbstractBlob.java	(working copy)
@@ -26,6 +26,8 @@
 import com.google.common.hash.HashCode;
 import com.google.common.hash.Hasher;
 import com.google.common.hash.Hashing;
+import com.google.common.io.ByteStreams;
+
 import org.apache.jackrabbit.oak.api.Blob;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -106,4 +108,34 @@
             log.warn("Error while closing stream", e);
         }
     }
+
+    @Override
+    public int compareTo(Blob o) {
+        return compare(getNewStream(), o.getNewStream()) ? 0 : 1;
+    }
+
+    private static boolean compare(InputStream in2, InputStream in1) {
+        try {
+            try {
+                byte[] buf1 = new byte[0x1000];
+                byte[] buf2 = new byte[0x1000];
+
+                while (true) {
+                    int read1 = ByteStreams.read(in1, buf1, 0, 0x1000);
+                    int read2 = ByteStreams.read(in2, buf2, 0, 0x1000);
+                    if (read1 != read2 || !Arrays.equals(buf1, buf2)) {
+                        return false;
+                    } else if (read1 != 0x1000) {
+                        return true;
+                    }
+                }
+            } finally {
+                in1.close();
+                in2.close();
+            }
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
 }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/PropertyStates.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/PropertyStates.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/PropertyStates.java	(working copy)
@@ -18,13 +18,24 @@
  */
 package org.apache.jackrabbit.oak.plugins.memory;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
 import java.math.BigDecimal;
+import java.text.ParseException;
+import java.util.Arrays;
+import java.util.Iterator;
 import java.util.List;
 
 import javax.annotation.Nonnull;
 import javax.jcr.PropertyType;
 
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
+import com.google.common.io.ByteStreams;
+
 import org.apache.jackrabbit.mk.api.MicroKernel;
 import org.apache.jackrabbit.mk.json.JsopReader;
 import org.apache.jackrabbit.oak.api.Blob;
@@ -34,6 +45,7 @@
 import org.apache.jackrabbit.oak.kernel.TypeCodes;
 import org.apache.jackrabbit.oak.kernel.KernelBlob;
 
+import static org.apache.jackrabbit.oak.api.Type.BINARIES;
 import static org.apache.jackrabbit.oak.api.Type.DATE;
 import static org.apache.jackrabbit.oak.api.Type.DATES;
 import static org.apache.jackrabbit.oak.api.Type.NAME;
@@ -395,4 +407,144 @@
             throw new IllegalArgumentException("Unexpected token: " + reader.getToken());
         }
     }
+
+    /**
+     * Convert a value to the given target type, if possible.
+     * 
+     * @param v the value to convert
+     * @param targetType the target property type
+     * @return the converted value, or null if converting is not possible
+     */
+    public static PropertyState convert(PropertyState value, int targetType) {
+        // TODO support full set of conversion features defined in the JCR spec
+        // at 3.6.4 Property Type Conversion
+        // re-use existing code if possible
+        String name = value.getName();
+        try {
+            switch (targetType) {
+            case PropertyType.STRING:
+                return stringProperty(name, value.getValue(Type.STRING));
+            case PropertyType.DATE:
+                return dateProperty(name, value.getValue(Type.STRING));
+            case PropertyType.LONG:
+                return longProperty(name, value.getValue(Type.LONG));
+            case PropertyType.DOUBLE:
+                return doubleProperty(name, value.getValue(Type.DOUBLE));
+            case PropertyType.DECIMAL:
+                return decimalProperty(name, value.getValue(Type.DECIMAL));
+            case PropertyType.BOOLEAN:
+                return booleanProperty(name, value.getValue(Type.BOOLEAN));
+            case PropertyType.NAME:
+                return nameProperty(name, value.getValue(Type.STRING));
+            case PropertyType.PATH:
+                return pathProperty(name, value.getValue(Type.STRING));
+            case PropertyType.REFERENCE:
+                return referenceProperty(name, value.getValue(Type.STRING));
+            case PropertyType.WEAKREFERENCE:
+                return weakreferenceProperty(name, value.getValue(Type.STRING));
+            case PropertyType.URI:
+                return uriProperty(name, value.getValue(Type.STRING));
+            case PropertyType.BINARY:
+                try {
+                    byte[] data = value.getValue(Type.STRING).getBytes("UTF-8");
+                    return binaryProperty(name, data);
+                } catch (IOException e) {
+                    // I don't know in what case that could really occur
+                    // except if UTF-8 isn't supported
+                    throw new IllegalArgumentException(
+                            value.getValue(Type.STRING), e);
+                }
+            }
+            return null;
+            // throw new IllegalArgumentException("Unknown property type: " +
+            // targetType);
+        } catch (UnsupportedOperationException e) {
+            // TODO detect unsupported conversions, so that no exception is
+            // thrown
+            // because exceptions are slow
+            return null;
+            // throw new IllegalArgumentException("<unsupported conversion of "
+            // +
+            // v + " (" + PropertyType.nameFromValue(v.getType()) + ") to type "
+            // +
+            // PropertyType.nameFromValue(targetType) + ">");
+        }
+    }
+
+    public static int compareTo(PropertyState p1, PropertyState p2) {
+        if (p1.getType().tag() != p2.getType().tag()) {
+            return Integer.signum(p1.getType().tag() - p2.getType().tag());
+        }
+        switch (p1.getType().tag()) {
+        case PropertyType.BINARY:
+            return compare(p1.getValue(Type.BINARIES),
+                    p2.getValue(Type.BINARIES));
+        default:
+            return compare(p1.getValue(Type.STRINGS), p2.getValue(Type.STRINGS));
+        }
+    }
+
+    private static <T extends Comparable<T>> int compare(Iterable<T> p1,
+            Iterable<T> p2) {
+        Iterator<T> i1 = p1.iterator();
+        Iterator<T> i2 = p2.iterator();
+        while (i1.hasNext() || i2.hasNext()) {
+            if (!i1.hasNext()) {
+                return 1;
+            }
+            if (!i2.hasNext()) {
+                return -1;
+            }
+            int compare = i1.next().compareTo(i2.next());
+            if (compare != 0) {
+                return compare;
+            }
+        }
+        return 0;
+    }
+
+    public static boolean match(PropertyState p1, PropertyState p2) {
+        if (p1.getType().tag() != p2.getType().tag()) {
+            return false;
+        }
+
+        switch (p1.getType().tag()) {
+        case PropertyType.BINARY:
+            if (p1.isArray() && !p2.isArray()) {
+                return contains(p1.getValue(Type.BINARIES),
+                        p2.getValue(Type.BINARY));
+            }
+            if (!p1.isArray() && p2.isArray()) {
+                return contains(p2.getValue(Type.BINARIES),
+                        p2.getValue(Type.BINARY));
+            }
+            
+            // both arrays or both single values
+            return compare(p1.getValue(Type.BINARIES),
+                    p2.getValue(Type.BINARIES)) == 0;
+        default:
+            if (p1.isArray() && !p2.isArray()) {
+                return contains(p1.getValue(Type.STRINGS),
+                        p2.getValue(Type.STRING));
+            }
+            if (!p1.isArray() && p2.isArray()) {
+                return contains(p2.getValue(Type.STRINGS),
+                        p2.getValue(Type.STRING));
+            }
+            // both arrays  or both single values
+            return compare(p1.getValue(Type.STRINGS), p2.getValue(Type.STRINGS)) == 0;
+        }
+    }
+
+    private static <T extends Comparable<T>> boolean contains(Iterable<T> p1, T p2) {
+        Iterator<T> i1 = p1.iterator();
+        while (i1.hasNext()) {
+            int compare = i1.next().compareTo(p2);
+            if (compare == 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
 }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java	(working copy)
@@ -32,7 +32,6 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.spi.state.AbstractNodeState;
@@ -42,7 +41,6 @@
 import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static org.apache.jackrabbit.oak.api.Type.STRING;
 
 /**
  * In-memory node state builder. The following two builder states are used
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/BinaryPropertyState.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/BinaryPropertyState.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/BinaryPropertyState.java	(working copy)
@@ -16,6 +16,10 @@
  */
 package org.apache.jackrabbit.oak.plugins.memory;
 
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+
 import org.apache.jackrabbit.oak.api.Blob;
 import org.apache.jackrabbit.oak.api.Type;
 
@@ -29,7 +33,18 @@
 
     @Override
     public String getString() {
-        return "<binary>";
+        StringWriter sw = new StringWriter();
+        InputStreamReader input = new InputStreamReader(value.getNewStream());
+        try {
+            int n = 0;
+            char[] buffer = new char[1024 * 4];
+            while (-1 != (n = input.read(buffer))) {
+                sw.write(buffer, 0, n);
+            }
+        } catch (IOException e) {
+            throw new IllegalStateException("Unable to fetch string value", e);
+        }
+        return sw.toString();
     }
 
     @Override
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/SessionQueryEngineImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/SessionQueryEngineImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/SessionQueryEngineImpl.java	(working copy)
@@ -20,7 +20,7 @@
 import java.util.List;
 import java.util.Map;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
+import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Result;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.SessionQueryEngine;
@@ -53,7 +53,7 @@
 
     @Override
     public Result executeQuery(String statement, String language, long limit,
-            long offset, Map<String, ? extends CoreValue> bindings, Root root,
+            long offset, Map<String, ? extends PropertyState> bindings, Root root,
             NamePathMapper namePathMapper) throws ParseException {
         return queryEngine.executeQuery(statement, language, limit, offset,
                 bindings, root, namePathMapper);
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java	(working copy)
@@ -13,8 +13,6 @@
  */
 package org.apache.jackrabbit.oak.query;
 
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -22,8 +20,6 @@
 import java.util.Iterator;
 import java.util.List;
 
-import javax.jcr.PropertyType;
-
 import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.CoreValueFactory;
 import org.apache.jackrabbit.oak.api.PropertyState;
@@ -31,6 +27,7 @@
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.plugins.memory.CoreValues;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.query.ast.AstVisitorBase;
 import org.apache.jackrabbit.oak.query.ast.BindVariableValueImpl;
 import org.apache.jackrabbit.oak.query.ast.ChildNodeImpl;
@@ -80,7 +77,7 @@
 
     final SourceImpl source;
     final ConstraintImpl constraint;
-    final HashMap<String, CoreValue> bindVariableMap = new HashMap<String, CoreValue>();
+    final HashMap<String, PropertyState> bindVariableMap = new HashMap<String, PropertyState>();
     final HashMap<String, Integer> selectorIndexes = new HashMap<String, Integer>();
     final ArrayList<SelectorImpl> selectors = new ArrayList<SelectorImpl>();
 
@@ -280,7 +277,7 @@
         return source;
     }
 
-    void bindValue(String varName, CoreValue value) {
+    void bindValue(String varName, PropertyState value) {
         bindVariableMap.put(varName, value);
     }
 
@@ -315,8 +312,8 @@
             String plan = source.getPlan(root);
             columns = new ColumnImpl[] { new ColumnImpl("explain", "plan", "plan")};
             ResultRowImpl r = new ResultRowImpl(this,
-                    new String[0],
-                    new CoreValue[] { getValueFactory().createValue(plan) },
+                    new String[0], 
+                    new PropertyState[] { PropertyStates.stringProperty("plan", plan)},
                     null);
             it = Arrays.asList(r).iterator();
         } else {
@@ -347,18 +344,18 @@
                 ArrayList<ResultRowImpl> list = new ArrayList<ResultRowImpl>();
                 ResultRowImpl r = new ResultRowImpl(this,
                         new String[0],
-                        new CoreValue[] {
-                            getValueFactory().createValue("query"),
-                            getValueFactory().createValue(resultCount),
+                        new PropertyState[] {
+                            PropertyStates.stringProperty("query", "query"),
+                            PropertyStates.longProperty("resultCount", resultCount)
                             },
                         null);
                 list.add(r);
                 for (SelectorImpl selector : selectors) {
                     r = new ResultRowImpl(this,
                             new String[0],
-                            new CoreValue[] {
-                                getValueFactory().createValue(selector.getSelectorName()),
-                                getValueFactory().createValue(selector.getScanCount()),
+                            new PropertyState[] {
+                                PropertyStates.stringProperty("selector", selector.getSelectorName()),
+                                PropertyStates.longProperty("scanCount", selector.getScanCount()),
                                 },
                             null);
                     list.add(r);
@@ -369,7 +366,48 @@
         return it;
     }
 
-    public int compareRows(CoreValue[][] orderValues, CoreValue[][] orderValues2) {
+    public int compareRows(PropertyState[] orderValuesIn,
+            PropertyState[] orderValues2In) {
+        CoreValue[][] orderValues = null;
+        CoreValue[][] orderValues2 = null;
+        if (orderValuesIn != null) {
+            int size = orderValuesIn.length;
+            orderValues = new CoreValue[size][];
+            for (int i = 0; i < size; i++) {
+                PropertyState p = orderValuesIn[i];
+                CoreValue[] x;
+                if (p == null) {
+                    x = null;
+                } else if (p.isArray()) {
+                    List<CoreValue> list = CoreValues.getValues(p);
+                    x = list.toArray(new CoreValue[list.size()]);
+                } else {
+                    x = new CoreValue[] { CoreValues.getValue(p) };
+                }
+                orderValues[i] = x;
+            }
+        }
+        if (orderValues2In != null) {
+            int size = orderValues2In.length;
+            orderValues2 = new CoreValue[size][];
+            for (int i = 0; i < size; i++) {
+                PropertyState p = orderValues2In[i];
+                CoreValue[] x;
+                if (p == null) {
+                    x = null;
+                } else if (p.isArray()) {
+                    List<CoreValue> list = CoreValues.getValues(p);
+                    x = list.toArray(new CoreValue[list.size()]);
+                } else {
+                    x = new CoreValue[] { CoreValues.getValue(p) };
+                }
+                orderValues2[i] = x;
+            }
+        }
+        return compareRows(orderValues, orderValues2);
+    }
+
+    private int compareRows(CoreValue[][] orderValues, CoreValue[][] orderValues2) {
         int comp = 0;
         for (int i = 0, size = orderings.length; i < size; i++) {
             CoreValue[] a = orderValues[i];
@@ -396,7 +434,7 @@
         return comp;
     }
 
-    public static int compareValues(CoreValue[] orderValues, CoreValue[] orderValues2) {
+    private static int compareValues(CoreValue[] orderValues, CoreValue[] orderValues2) {
         int l1 = orderValues.length;
         int l2 = orderValues2.length;
         int len = Math.max(l1, l2);
@@ -418,89 +456,6 @@
         return 0;
     }
 
-    public static int getType(PropertyState p, int ifUnknown) {
-        if (p.count() > 0) {
-            return p.getType().tag();
-        }
-        return ifUnknown;
-    }
-
-    /**
-     * Convert a value to the given target type, if possible.
-     * 
-     * @param v the value to convert
-     * @param targetType the target property type
-     * @return the converted value, or null if converting is not possible
-     */
-    public CoreValue convert(CoreValue v, int targetType) {
-        // TODO support full set of conversion features defined in the JCR spec
-        // at 3.6.4 Property Type Conversion
-        // re-use existing code if possible
-        int sourceType = v.getType();
-        if (sourceType == targetType) {
-            return v;
-        }
-        CoreValueFactory vf = getValueFactory();
-        switch (sourceType) {
-        case PropertyType.STRING:
-            switch(targetType) {
-            case PropertyType.BINARY:
-                try {
-                    byte[] data = v.getString().getBytes("UTF-8");
-                    return vf.createValue(new ByteArrayInputStream(data));
-                } catch (IOException e) {
-                    // I don't know in what case that could really occur
-                    // except if UTF-8 isn't supported
-                    throw new IllegalArgumentException(v.getString(), e);
-                }
-            }
-            return vf.createValue(v.getString(), targetType);
-        }
-        try {
-            switch (targetType) {
-            case PropertyType.STRING:
-                return vf.createValue(v.getString());
-            case PropertyType.BOOLEAN:
-                return vf.createValue(v.getBoolean());
-            case PropertyType.DATE:
-                return vf.createValue(v.getString(), PropertyType.DATE);
-            case PropertyType.LONG:
-                return vf.createValue(v.getLong());
-            case PropertyType.DOUBLE:
-                return vf.createValue(v.getDouble());
-            case PropertyType.DECIMAL:
-                return vf.createValue(v.getString(), PropertyType.DECIMAL);
-            case PropertyType.NAME:
-                return vf.createValue(getOakPath(v.getString()), PropertyType.NAME);
-            case PropertyType.PATH:
-                return vf.createValue(v.getString(), PropertyType.PATH);
-            case PropertyType.REFERENCE:
-                return vf.createValue(v.getString(), PropertyType.REFERENCE);
-            case PropertyType.WEAKREFERENCE:
-                return vf.createValue(v.getString(), PropertyType.WEAKREFERENCE);
-            case PropertyType.URI:
-                return vf.createValue(v.getString(), PropertyType.URI);
-            case PropertyType.BINARY:
-                try {
-                    byte[] data = v.getString().getBytes("UTF-8");
-                    return vf.createValue(new ByteArrayInputStream(data));
-                } catch (IOException e) {
-                    // I don't know in what case that could really occur
-                    // except if UTF-8 isn't supported
-                    throw new IllegalArgumentException(v.getString(), e);
-                }
-            }
-            throw new IllegalArgumentException("Unknown property type: " + targetType);
-        } catch (UnsupportedOperationException e) {
-            // TODO detect unsupported conversions, so that no exception is thrown
-            // because exceptions are slow
-            return null;
-            // throw new IllegalArgumentException("<unsupported conversion of " + 
-            //        v + " (" + PropertyType.nameFromValue(v.getType()) + ") to type " + 
-            //        PropertyType.nameFromValue(targetType) + ">");
-        }
-    }
-
     public String getOakPath(String jcrPath) {
         NamePathMapper m = getNamePathMapper();
         if (m == null) {
@@ -609,30 +564,19 @@
             paths[i] = s.currentPath();
         }
         int columnCount = columns.length;
-        CoreValue[] values = new CoreValue[columnCount];
+        PropertyState[] values = new PropertyState[columnCount];
         for (int i = 0; i < columnCount; i++) {
             ColumnImpl c = columns[i];
-            PropertyState p = c.currentProperty();
-            values[i] = p == null ? null : CoreValues.getValue(p);
+            values[i] = c.currentProperty();
         }
-        CoreValue[][] orderValues;
+        PropertyState[] orderValues;
         if (orderings == null) {
             orderValues = null;
         } else {
             int size = orderings.length;
-            orderValues = new CoreValue[size][];
+            orderValues = new PropertyState[size];
             for (int i = 0; i < size; i++) {
-                PropertyState p = orderings[i].getOperand().currentProperty();
-                CoreValue[] x;
-                if (p == null) {
-                    x = null;
-                } else if (p.isArray()) {
-                    List<CoreValue> list = CoreValues.getValues(p);
-                    x = list.toArray(new CoreValue[list.size()]);
-                } else {
-                    x = new CoreValue[] { CoreValues.getValue(p) };
-                }
-                orderValues[i] = x;
+                orderValues[i] = orderings[i].getOperand().currentProperty();
             }
         }
         return new ResultRowImpl(this, paths, values, orderValues);
@@ -657,8 +601,8 @@
         throw new IllegalArgumentException("Column not found: " + columnName);
     }
 
-    public CoreValue getBindVariableValue(String bindVariableName) {
-        CoreValue v = bindVariableMap.get(bindVariableName);
+    public PropertyState getBindVariableValue(String bindVariableName) {
+        PropertyState v = bindVariableMap.get(bindVariableName);
         if (v == null) {
             throw new IllegalArgumentException("Bind variable value not set: " + bindVariableName);
         }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java	(working copy)
@@ -19,19 +19,21 @@
 package org.apache.jackrabbit.oak.query.ast;
 
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Locale;
 
 import javax.jcr.PropertyType;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
-import org.apache.jackrabbit.oak.plugins.memory.CoreValues;
 import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.query.SQL2Parser;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
 
+import com.google.common.collect.Iterables;
+
 /**
  * A property expression.
  */
@@ -115,17 +117,15 @@
         // asterisk - create a multi-value property
         // warning: the returned property state may have a mixed type
         // (not all values may have the same type)
-        ArrayList<CoreValue> values = new ArrayList<CoreValue>();
+
+        //TODO this doesn't play well with the idea that the types may be different
+        List<String> values = new ArrayList<String>();
         for (PropertyState p : tree.getProperties()) {
             if (matchesPropertyType(p)) {
-                if (p.isArray()) {
-                    values.addAll(CoreValues.getValues(p));
-                } else {
-                    values.add(CoreValues.getValue(p));
-                }
+                Iterables.addAll(values, p.getValue(Type.STRINGS));
             }
         }
-        return PropertyStates.createProperty("*", values);
+        return PropertyStates.stringProperty("*", values);
     }
 
     private boolean matchesPropertyType(PropertyState state) {
@@ -143,7 +143,7 @@
     }
 
     @Override
-    public void restrict(FilterImpl f, Operator operator, CoreValue v) {
+    public void restrict(FilterImpl f, Operator operator, PropertyState v) {
         if (f.getSelector() == selector) {
             if (operator == Operator.NOT_EQUAL && v != null) {
                 // not supported
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java	(working copy)
@@ -20,8 +20,8 @@
 
 import javax.jcr.PropertyType;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
@@ -67,14 +67,14 @@
     }
 
     @Override
-    public void restrict(FilterImpl f, Operator operator, CoreValue v) {
+    public void restrict(FilterImpl f, Operator operator, PropertyState v) {
         if (v == null) {
             return;
         }
         if (!isName(v)) {
             throw new IllegalArgumentException("Invalid name value: " + v.toString());
         }
-        String path = v.getString();
+        String path = v.getValue(Type.STRING);
         // Name escaping (convert _x0020_ to space)
         path = decodeName(path);
         if (PathUtils.isAbsolute(path)) {
@@ -105,9 +105,9 @@
      * @param v the value
      * @return true if it can be converted
      */
-    private static boolean isName(CoreValue v) {
+    private static boolean isName(PropertyState v) {
         // TODO correctly validate JCR names - see JCR 2.0 spec 3.2.4 Naming Restrictions
-        switch (v.getType()) {
+        switch (v.getType().tag()) {
         case PropertyType.DATE:
         case PropertyType.DECIMAL:
         case PropertyType.DOUBLE:
@@ -115,7 +115,7 @@
         case PropertyType.BOOLEAN:
             return false;
         }
-        String n = v.getString();
+        String n = v.getValue(Type.STRING);
         if (n.startsWith("[") && !n.endsWith("]")) {
             return false;
         }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java	(working copy)
@@ -18,7 +18,6 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.query.Query;
@@ -61,7 +60,7 @@
     }
 
     @Override
-    public void restrict(FilterImpl f, Operator operator, CoreValue v) {
+    public void restrict(FilterImpl f, Operator operator, PropertyState v) {
         if (f.getSelector() == selector) {
             if (operator == Operator.NOT_EQUAL && v != null) {
                 // not supported
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java	(working copy)
@@ -18,10 +18,8 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.plugins.memory.CoreValues;
-import org.apache.jackrabbit.oak.query.Query;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
 
 /**
@@ -72,35 +70,27 @@
         }
         if (!p1.isArray() && !p2.isArray()) {
             // both are single valued
-            return CoreValues.getValue(p1).equals(CoreValues.getValue(p2));
+            return PropertyStates.match(p1, p2);
         }
         // TODO what is the expected result of an equi join for multi-valued properties?
         if (!p1.isArray() && p2.isArray()) {
-            CoreValue x = CoreValues.getValue(p1);
-            for (CoreValue y : CoreValues.getValues(p2)) {
-                if (y.getType() != x.getType()) {
-                    y = query.convert(y, x.getType());
-                }
-                if (y != null && x.equals(y)) {
-                    return true;
-                }
+            if (p1.getType().tag() != p2.getType().tag()) {
+                p1 = PropertyStates.convert(p1, p2.getType().tag());
             }
+            if (p1 != null && PropertyStates.match(p1, p2)) {
+                return true;
+            }
             return false;
         } else if (p1.isArray() && !p2.isArray()) {
-            CoreValue x = CoreValues.getValue(p2);
-            for (CoreValue y : CoreValues.getValues(p1)) {
-                if (y.getType() != x.getType()) {
-                    y = query.convert(y, x.getType());
-                }
-                if (x.equals(y)) {
-                    return true;
-                }
+            if (p1.getType().tag() != p2.getType().tag()) {
+                p2 = PropertyStates.convert(p2, p1.getType().tag());
             }
+            if (p2 != null && PropertyStates.match(p1, p2)) {
+                return true;
+            }
             return false;
         }
-        CoreValue[] l1 = CoreValues.getValues(p1).toArray(new CoreValue[p1.count()]);
-        CoreValue[] l2 = CoreValues.getValues(p2).toArray(new CoreValue[p2.count()]);
-        return Query.compareValues(l1, l2) == 0;
+        return PropertyStates.match(p1, p2);
     }
 
     @Override
@@ -110,13 +100,13 @@
         if (f.getSelector() == selector1 && p2 != null) {
             if (!p2.isArray()) {
                 // TODO support join on multi-valued properties
-                f.restrictProperty(property1Name, Operator.EQUAL, CoreValues.getValue(p2));
+                f.restrictProperty(property1Name, Operator.EQUAL, p2);
             }
         }
         if (f.getSelector() == selector2 && p1 != null) {
             if (!p1.isArray()) {
                 // TODO support join on multi-valued properties
-                f.restrictProperty(property2Name, Operator.EQUAL, CoreValues.getValue(p1));
+                f.restrictProperty(property2Name, Operator.EQUAL, p1);
             }
         }
     }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LiteralImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LiteralImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LiteralImpl.java	(working copy)
@@ -19,23 +19,25 @@
 package org.apache.jackrabbit.oak.query.ast;
 
 import java.util.Locale;
-import org.apache.jackrabbit.oak.api.CoreValue;
-import org.apache.jackrabbit.oak.query.SQL2Parser;
 
 import javax.jcr.PropertyType;
 
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.query.SQL2Parser;
+
 /**
  * A literal of a certain data type, possibly "cast(..)" of a literal.
  */
 public class LiteralImpl extends StaticOperandImpl {
 
-    private final CoreValue value;
+    private final PropertyState value;
 
-    public LiteralImpl(CoreValue value) {
+    public LiteralImpl(PropertyState value) {
         this.value = value;
     }
 
-    public CoreValue getLiteralValue() {
+    public PropertyState getLiteralValue() {
         return value;
     }
 
@@ -46,16 +48,16 @@
 
     @Override
     public String toString() {
-        String type = PropertyType.nameFromValue(value.getType());
+        String type = PropertyType.nameFromValue(value.getType().tag());
         return "cast(" + escape() + " as " + type.toLowerCase(Locale.ENGLISH) + ')';
     }
 
     private String escape() {
-        return SQL2Parser.escapeStringLiteral(value.getString());
+        return SQL2Parser.escapeStringLiteral(value.getValue(Type.STRING));
     }
 
     @Override
-    CoreValue currentValue() {
+    PropertyState currentValue() {
         return value;
     }
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java	(working copy)
@@ -16,7 +16,6 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
 
@@ -27,7 +26,7 @@
 
     public abstract PropertyState currentProperty();
 
-    public abstract void restrict(FilterImpl f, Operator operator, CoreValue v);
+    public abstract void restrict(FilterImpl f, Operator operator, PropertyState v);
 
     /**
      * Check whether the condition can be applied to a selector (to restrict the
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java	(working copy)
@@ -18,7 +18,6 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
@@ -63,7 +62,7 @@
     }
 
     @Override
-    public void restrict(FilterImpl f, Operator operator, CoreValue v) {
+    public void restrict(FilterImpl f, Operator operator, PropertyState v) {
         // TODO support LOCALNAME index conditions
     }
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ComparisonImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ComparisonImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ComparisonImpl.java	(working copy)
@@ -18,10 +18,9 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.plugins.memory.CoreValues;
-import org.apache.jackrabbit.oak.query.Query;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
 
 /**
@@ -51,6 +50,13 @@
         return operand2;
     }
 
+    public static int getType(PropertyState p, int ifUnknown) {
+        if (p.count() > 0) {
+            return p.getType().tag();
+        }
+        return ifUnknown;
+    }
+
     @Override
     public boolean evaluate() {
         // JCR 2.0 spec, 6.7.16 Comparison:
@@ -59,59 +65,61 @@
         if (p1 == null) {
             return false;
         }
-        // "operand2 always evaluates to a scalar value"
-        CoreValue v2 = operand2.currentValue();
-        if (v2 == null) {
+        PropertyState p2 = operand2.currentValue();
+        if (p2 == null) {
             // if the property doesn't exist, the result is always false
             // even for "null <> 'x'" (same as in SQL) 
             return false;
         }
-        boolean isArray = p1.isArray();
-        int v1Type = Query.getType(p1, v2.getType());
-        if (v1Type != v2.getType()) {
+        int v1Type = getType(p1, p2.getType().tag());
+        if (v1Type != p2.getType().tag()) {
             // "the value of operand2 is converted to the
             // property type of the value of operand1"
-            v2 = query.convert(v2, v1Type);
-            if (v2 == null) {
+            p2 = PropertyStates.convert(p2, v1Type);
+            if (p2 == null) {
                 return false;
             }
         }
-        if (!isArray) {
-            return evaluate(CoreValues.getValue(p1), v2);
-        }
-        // for multi-valued properties: if any of the value matches,
-        // then return true
-        for (CoreValue v1 : CoreValues.getValues(p1)) {
-            if (evaluate(v1, v2)) {
-                return true;
-            }
-        }
-        return false;
+        return evaluate(p1, p2);
     }
 
-    private boolean evaluate(CoreValue v1, CoreValue v2) {
+    /**
+     * "operand2 always evaluates to a scalar value"
+     * 
+     * for multi-valued properties: if any of the value matches, then return true
+     * 
+     * @param p1
+     * @param p2
+     * @return
+     */
+    private boolean evaluate(PropertyState p1, PropertyState p2) {
         switch (operator) {
         case EQUAL:
-            return v1.equals(v2);
+            return PropertyStates.match(p1, p2);
+        case NOT_EQUAL:
+            return !PropertyStates.match(p1, p2);
         case GREATER_OR_EQUAL:
-            return v1.compareTo(v2) >= 0;
+            return PropertyStates.compareTo(p1, p2) >= 0;
         case GREATER_THAN:
-            return v1.compareTo(v2) > 0;
+            return PropertyStates.compareTo(p1, p2) > 0;
         case LESS_OR_EQUAL:
-            return v1.compareTo(v2) <= 0;
+            return PropertyStates.compareTo(p1, p2) <= 0;
         case LESS_THAN:
-            return v1.compareTo(v2) < 0;
-        case NOT_EQUAL:
-            return !v1.equals(v2);
+            return PropertyStates.compareTo(p1, p2) < 0;
         case LIKE:
-            return evaluateLike(v1, v2);
+            return evaluateLike(p1, p2);
         }
         throw new IllegalArgumentException("Unknown operator: " + operator);
     }
 
-    private static boolean evaluateLike(CoreValue v1, CoreValue v2) {
-        LikePattern like = new LikePattern(v2.getString());
-        return like.matches(v1.getString());
+    private static boolean evaluateLike(PropertyState v1, PropertyState v2) {
+        LikePattern like = new LikePattern(v2.getValue(Type.STRING));
+        for (String s : v1.getValue(Type.STRINGS)) {
+            if (like.matches(s)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     @Override
@@ -126,7 +134,7 @@
 
     @Override
     public void restrict(FilterImpl f) {
-        CoreValue v = operand2.currentValue();
+        PropertyState v = operand2.currentValue();
         if (v != null) {
             operand1.restrict(f, operator, v);
             // TODO OAK-347
@@ -155,8 +163,7 @@
 
     @Override
     public void restrictPushDown(SelectorImpl s) {
-        CoreValue v = operand2.currentValue();
-        if (v != null) {
+        if (operand2.currentValue() != null) {
             if (operand1.canRestrictSelector(s)) {
                 s.restrictSelector(this);
             }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/BindVariableValueImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/BindVariableValueImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/BindVariableValueImpl.java	(working copy)
@@ -18,7 +18,7 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
+import org.apache.jackrabbit.oak.api.PropertyState;
 
 /**
  * A bind variable.
@@ -46,7 +46,7 @@
     }
 
     @Override
-    CoreValue currentValue() {
+    PropertyState currentValue() {
         return query.getBindVariableValue(bindVariableName);
     }
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LengthImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LengthImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LengthImpl.java	(working copy)
@@ -20,7 +20,6 @@
 
 import javax.jcr.PropertyType;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
@@ -65,9 +64,9 @@
     }
 
     @Override
-    public void restrict(FilterImpl f, Operator operator, CoreValue v) {
+    public void restrict(FilterImpl f, Operator operator, PropertyState v) {
         if (v != null) {
-            switch (v.getType()) {
+            switch (v.getType().tag()) {
             case PropertyType.LONG:
             case PropertyType.DECIMAL:
             case PropertyType.DOUBLE:
@@ -81,7 +80,7 @@
             default:
                 throw new IllegalArgumentException(
                         "Can not compare the length with a constant of type "
-                                + PropertyType.nameFromValue(v.getType()) +
+                                + PropertyType.nameFromValue(v.getType().tag()) +
                                 " and value " + v.toString());
             }
         }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java	(working copy)
@@ -18,13 +18,12 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
 
-import static org.apache.jackrabbit.oak.api.Type.STRING;
-
 /**
  * The function "lower(..)".
  */
@@ -63,9 +62,9 @@
     }
 
     @Override
-    public void restrict(FilterImpl f, Operator operator, CoreValue v) {
+    public void restrict(FilterImpl f, Operator operator, PropertyState v) {
         // LOWER(x) implies x is not null
-        operand.restrict(f, Operator.NOT_EQUAL, null);        
+        operand.restrict(f, Operator.NOT_EQUAL, null);
     }
 
     @Override
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/UpperCaseImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/UpperCaseImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/UpperCaseImpl.java	(working copy)
@@ -18,13 +18,12 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
 
-import static org.apache.jackrabbit.oak.api.Type.STRING;
-
 /**
  * The function "upper(..)".
  */
@@ -63,7 +62,7 @@
     }
 
     @Override
-    public void restrict(FilterImpl f, Operator operator, CoreValue v) {
+    public void restrict(FilterImpl f, Operator operator, PropertyState v) {
         // UPPER(x) implies x is not null
         operand.restrict(f, Operator.NOT_EQUAL, null);
     }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java	(working copy)
@@ -18,7 +18,6 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
 
@@ -59,7 +58,7 @@
     @Override
     public void restrict(FilterImpl f) {
         if (f.getSelector() == selector) {
-            f.restrictProperty(propertyName, Operator.NOT_EQUAL, (CoreValue) null);
+            f.restrictProperty(propertyName, Operator.NOT_EQUAL, null);
         }
     }
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java	(working copy)
@@ -18,18 +18,18 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+import static org.apache.jackrabbit.oak.api.Type.STRINGS;
+
 import java.text.ParseException;
 import java.util.ArrayList;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.query.ast.ComparisonImpl.LikePattern;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
 
-import static org.apache.jackrabbit.oak.api.Type.STRING;
-import static org.apache.jackrabbit.oak.api.Type.STRINGS;
-
 /**
  * A fulltext "contains(...)" condition.
  */
@@ -95,9 +95,9 @@
         // if a fulltext index is used, to avoid filtering too much
         // (we don't know what exact options are used in the fulltext index)
         // (stop word, special characters,...)
-        CoreValue v = fullTextSearchExpression.currentValue();
+        PropertyState v = fullTextSearchExpression.currentValue();
         try {
-            FullTextExpression expr = FullTextParser.parse(v.getString());
+            FullTextExpression expr = FullTextParser.parse(v.getValue(Type.STRING));
             return expr.evaluate(buff.toString());
         } catch (ParseException e) {
             throw new IllegalArgumentException("Invalid expression: " + fullTextSearchExpression, e);
@@ -122,10 +122,10 @@
     public void restrict(FilterImpl f) {
         if (propertyName != null) {
             if (f.getSelector() == selector) {
-                f.restrictProperty(propertyName, Operator.NOT_EQUAL, (CoreValue) null);
+                f.restrictProperty(propertyName, Operator.NOT_EQUAL, null);
             }
         }
-        f.restrictFulltextCondition(fullTextSearchExpression.currentValue().getString());
+        f.restrictFulltextCondition(fullTextSearchExpression.currentValue().getValue(Type.STRING));
     }
 
     @Override
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/StaticOperandImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/StaticOperandImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/StaticOperandImpl.java	(working copy)
@@ -18,13 +18,13 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
+import org.apache.jackrabbit.oak.api.PropertyState;
 
 /**
  * The base class for static operands (literal, bind variables).
  */
 public abstract class StaticOperandImpl extends AstElement {
 
-    abstract CoreValue currentValue();
+    abstract PropertyState currentValue();
 
 }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElementFactory.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElementFactory.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElementFactory.java	(working copy)
@@ -13,7 +13,7 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
+import org.apache.jackrabbit.oak.api.PropertyState;
 
 /**
  * A factory for syntax tree elements.
@@ -84,7 +84,7 @@
         return new LengthImpl(propertyValue);
     }
 
-    public LiteralImpl literal(CoreValue literalValue) {
+    public LiteralImpl literal(PropertyState literalValue) {
         return new LiteralImpl(literalValue);
     }
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/IndexRowImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/IndexRowImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/IndexRowImpl.java	(working copy)
@@ -18,7 +18,7 @@
  */
 package org.apache.jackrabbit.oak.query.index;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
+import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.spi.query.IndexRow;
 
 /**
@@ -38,7 +38,7 @@
     }
 
     @Override
-    public CoreValue getValue(String columnName) {
+    public PropertyState getValue(String columnName) {
         return null;
     }
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java	(working copy)
@@ -18,8 +18,9 @@
  */
 package org.apache.jackrabbit.oak.query.index;
 
+import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.commons.PathUtils;
-import org.apache.jackrabbit.oak.api.CoreValue;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.query.ast.Operator;
 import org.apache.jackrabbit.oak.query.ast.SelectorImpl;
 import org.apache.jackrabbit.oak.spi.query.Filter;
@@ -182,69 +183,70 @@
         x.propertyType = propertyType;
     }
 
-    public void restrictProperty(String propertyName, Operator op, CoreValue value) {
+    public void restrictProperty(String propertyName, Operator op, PropertyState v) {
         PropertyRestriction x = propertyRestrictions.get(propertyName);
         if (x == null) {
             x = new PropertyRestriction();
             x.propertyName = propertyName;
             propertyRestrictions.put(propertyName, x);
         }
-        CoreValue oldFirst = x.first, oldLast = x.last;
+        PropertyState oldFirst = x.first;
+        PropertyState oldLast = x.last;
         switch (op) {
         case EQUAL:
-            x.first = maxValue(oldFirst, value);
+            x.first = maxValue(oldFirst, v);
             x.firstIncluding = x.first == oldFirst ? x.firstIncluding : true;
-            x.last = minValue(oldLast, value);
+            x.last = minValue(oldLast, v);
             x.lastIncluding = x.last == oldLast ? x.lastIncluding : true;
             break;
         case NOT_EQUAL:
-            if (value != null) {
+            if (v != null) {
                 throw new IllegalArgumentException("NOT_EQUAL only supported for NOT_EQUAL NULL");
             }
             break;
         case GREATER_THAN:
-            x.first = maxValue(oldFirst, value);
+            x.first = maxValue(oldFirst, v);
             x.firstIncluding = false;
             break;
         case GREATER_OR_EQUAL:
-            x.first = maxValue(oldFirst, value);
+            x.first = maxValue(oldFirst, v);
             x.firstIncluding = x.first == oldFirst ? x.firstIncluding : true;
             break;
         case LESS_THAN:
-            x.last = minValue(oldLast, value);
+            x.last = minValue(oldLast, v);
             x.lastIncluding = false;
             break;
         case LESS_OR_EQUAL:
-            x.last = minValue(oldLast, value);
+            x.last = minValue(oldLast, v);
             x.lastIncluding = x.last == oldLast ? x.lastIncluding : true;
             break;
         case LIKE:
             // LIKE is handled in the fulltext index
             x.isLike = true;
-            x.first = value;
+            x.first = v;
             break;
         }
         if (x.first != null && x.last != null) {
-            if (x.first.compareTo(x.last) > 0) {
+            if (PropertyStates.compareTo(x.first, x.last) > 0) {
                 setAlwaysFalse();
-            } else if (x.first.compareTo(x.last) == 0 && (!x.firstIncluding || !x.lastIncluding)) {
+            } else if (PropertyStates.compareTo(x.first, x.last) == 0 && (!x.firstIncluding || !x.lastIncluding)) {
                 setAlwaysFalse();
             }
         }
     }
 
-    static CoreValue maxValue(CoreValue a, CoreValue b) {
+    static PropertyState maxValue(PropertyState a, PropertyState b) {
         if (a == null) {
             return b;
         }
-        return a.compareTo(b) < 0 ? b : a;
+        return PropertyStates.compareTo(a, b) < 0 ? b : a;
     }
 
-    static CoreValue minValue(CoreValue a, CoreValue b) {
+    static PropertyState minValue(PropertyState a, PropertyState b) {
         if (a == null) {
             return b;
         }
-        return a.compareTo(b) < 0 ? a : b;
+        return PropertyStates.compareTo(a, b) <= 0 ? a : b;
     }
 
     @Override
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ResultRowImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ResultRowImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ResultRowImpl.java	(working copy)
@@ -16,7 +16,7 @@
  */
 package org.apache.jackrabbit.oak.query;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
+import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.ResultRow;
 import org.apache.jackrabbit.oak.query.ast.ColumnImpl;
 import org.apache.jackrabbit.oak.query.ast.SelectorImpl;
@@ -28,10 +28,10 @@
 
     private final Query query;
     private final String[] paths;
-    private final CoreValue[] values;
-    private final CoreValue[][] orderValues;
+    private final PropertyState[] values;
+    private final PropertyState[] orderValues;
 
-    ResultRowImpl(Query query, String[] paths, CoreValue[] values, CoreValue[][] orderValues) {
+    ResultRowImpl(Query query, String[] paths, PropertyState[] values, PropertyState[] orderValues) {
         this.query = query;
         this.paths = paths;
         this.values = values;
@@ -58,13 +58,13 @@
     }
 
     @Override
-    public CoreValue getValue(String columnName) {
+    public PropertyState getValue(String columnName) {
         return values[query.getColumnIndex(columnName)];
     }
 
     @Override
-    public CoreValue[] getValues() {
-        CoreValue[] v2 = new CoreValue[values.length];
+    public PropertyState[] getValues() {
+        PropertyState[] v2 = new PropertyState[values.length];
         System.arraycopy(values, 0, v2, 0, values.length);
         return v2;
     }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java	(working copy)
@@ -22,8 +22,8 @@
 import java.util.Map;
 import java.util.Map.Entry;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.CoreValueFactory;
+import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.query.index.TraversingIndex;
@@ -94,7 +94,7 @@
     }
 
     public ResultImpl executeQuery(String statement, String language, 
-            long limit, long offset, Map<String, ? extends CoreValue> bindings,
+            long limit, long offset, Map<String, ? extends PropertyState> bindings,
             Root root,
             NamePathMapper namePathMapper) throws ParseException {
         Query q = parseQuery(statement, language);
@@ -103,7 +103,7 @@
         q.setLimit(limit);
         q.setOffset(offset);
         if (bindings != null) {
-            for (Entry<String, ? extends CoreValue> e : bindings.entrySet()) {
+            for (Entry<String, ? extends PropertyState> e : bindings.entrySet()) {
                 q.bindValue(e.getKey(), e.getValue());
             }
         }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java	(working copy)
@@ -16,9 +16,11 @@
  */
 package org.apache.jackrabbit.oak.query;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.CoreValueFactory;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.query.ast.AstElementFactory;
 import org.apache.jackrabbit.oak.query.ast.Operator;
 import org.apache.jackrabbit.oak.query.ast.BindVariableValueImpl;
@@ -66,7 +68,7 @@
     private int currentTokenType;
     private String currentToken;
     private boolean currentTokenQuoted;
-    private CoreValue currentValue;
+    private PropertyState currentValue;
     private ArrayList<String> expected;
 
     // The bind variables
@@ -186,9 +188,9 @@
     private String readName() throws ParseException {
         if (readIf("[")) {
             if (currentTokenType == VALUE) {
-                CoreValue value = readString();
+                PropertyState value = readString();
                 read("]");
-                return value.getString();
+                return value.getValue(Type.STRING);
             } else {
                 int level = 1;
                 StringBuilder buff = new StringBuilder();
@@ -356,8 +358,8 @@
                     if (!(esc instanceof LiteralImpl)) {
                         throw getSyntaxError("only ESCAPE '\' is supported");
                     }
-                    CoreValue v = ((LiteralImpl) esc).getLiteralValue();
-                    if (!v.getString().equals("\\")) {
+                    PropertyState v = ((LiteralImpl) esc).getLiteralValue();
+                    if (!v.getValue(Type.STRING).equals("\\")) {
                         throw getSyntaxError("only ESCAPE '\' is supported");
                     }
                 }
@@ -518,7 +520,7 @@
         } else if ("PROPERTY".equalsIgnoreCase(functionName)) {
             PropertyValueImpl pv = parsePropertyValue(readName());
             read(",");
-            op = factory.propertyValue(pv.getSelectorName(), pv.getPropertyName(), readString().getString());
+            op = factory.propertyValue(pv.getSelectorName(), pv.getPropertyName(), readString().getValue(Type.STRING));
         } else {
             throw getSyntaxError("LENGTH, NAME, LOCALNAME, SCORE, LOWER, UPPER, or PROPERTY");
         }
@@ -542,19 +544,19 @@
             if (currentTokenType != VALUE) {
                 throw getSyntaxError("number");
             }
-            int valueType = currentValue.getType();
+            int valueType = currentValue.getType().tag();
             switch (valueType) {
             case PropertyType.LONG:
-                currentValue = valueFactory.createValue(-currentValue.getLong());
+                currentValue = PropertyStates.longProperty("", -currentValue.getValue(Type.LONG));
                 break;
             case PropertyType.DOUBLE:
-                currentValue = valueFactory.createValue(-currentValue.getDouble());
+                currentValue = PropertyStates.doubleProperty("", -currentValue.getValue(Type.DOUBLE));
                 break;
             case PropertyType.BOOLEAN:
-                currentValue = valueFactory.createValue(!currentValue.getBoolean());
+                currentValue = PropertyStates.booleanProperty("", !currentValue.getValue(Type.BOOLEAN));
                 break;
             case PropertyType.DECIMAL:
-                currentValue = valueFactory.createValue(currentValue.getDecimal().negate());
+                currentValue = PropertyStates.decimalProperty("", currentValue.getValue(Type.DECIMAL).negate());
                 break;
             default:
                 throw getSyntaxError("Illegal operation: -" + currentValue);
@@ -577,10 +579,10 @@
             }
             return var;
         } else if (readIf("TRUE")) {
-            LiteralImpl literal = getUncastLiteral(valueFactory.createValue(true));
+            LiteralImpl literal = getUncastLiteral(PropertyStates.booleanProperty("", true));
             return literal;
         } else if (readIf("FALSE")) {
-            LiteralImpl literal = getUncastLiteral(valueFactory.createValue(false));
+            LiteralImpl literal = getUncastLiteral(PropertyStates.booleanProperty("", false));
             return literal;
         } else if (readIf("CAST")) {
             read("(");
@@ -589,7 +591,7 @@
                 throw getSyntaxError("literal");
             }
             LiteralImpl literal = (LiteralImpl) op;
-            CoreValue value = literal.getLiteralValue();
+            PropertyState value = literal.getLiteralValue();
             read("AS");
             value = parseCastAs(value);
             read(")");
@@ -604,8 +606,8 @@
                         throw getSyntaxError("literal");
                     }
                     LiteralImpl literal = (LiteralImpl) op;
-                    CoreValue value = literal.getLiteralValue();
-                    value = valueFactory.createValue(value.getString(), PropertyType.DATE);
+                    PropertyState value = literal.getLiteralValue();
+                    value = PropertyStates.dateProperty("", value.getValue(Type.STRING));
                     literal = factory.literal(value);
                     return literal;
                 }
@@ -620,44 +622,23 @@
      * @param value the original value
      * @return the literal
      */
-    private LiteralImpl getUncastLiteral(CoreValue value) {
+    private LiteralImpl getUncastLiteral(PropertyState value) {
         return factory.literal(value);
     }
 
-    private CoreValue parseCastAs(CoreValue value) throws ParseException {
+    private PropertyState parseCastAs(PropertyState value)
+            throws ParseException {
         if (currentTokenQuoted) {
             throw getSyntaxError("data type (STRING|BINARY|...)");
         }
         int propertyType = getPropertyTypeFromName(currentToken);
         read();
-        switch (propertyType) {
-        case PropertyType.STRING:
-            return valueFactory.createValue(value.getString());
-        case PropertyType.BINARY:
-            return valueFactory.createValue(value.getString(), PropertyType.BINARY);
-        case PropertyType.DATE:
-            return valueFactory.createValue(value.getString(), PropertyType.DATE);
-        case PropertyType.LONG:
-            return valueFactory.createValue(value.getLong());
-        case PropertyType.DOUBLE:
-            return valueFactory.createValue(value.getDouble());
-        case PropertyType.DECIMAL:
-            return valueFactory.createValue(value.getDecimal());
-        case PropertyType.BOOLEAN:
-            return valueFactory.createValue(value.getBoolean());
-        case PropertyType.NAME:
-            return valueFactory.createValue(value.getString(), PropertyType.NAME);
-        case PropertyType.PATH:
-            return valueFactory.createValue(value.getString(), PropertyType.PATH);
-        case PropertyType.REFERENCE:
-            return valueFactory.createValue(value.getString(), PropertyType.REFERENCE);
-        case PropertyType.WEAKREFERENCE:
-            return valueFactory.createValue(value.getString(), PropertyType.WEAKREFERENCE);
-        case PropertyType.URI:
-            return valueFactory.createValue(value.getString(), PropertyType.URI);
-        default:
+
+        PropertyState v = PropertyStates.convert(value, propertyType);
+        if (v == null) {
             throw getSyntaxError("data type (STRING|BINARY|...)");
         }
+        return v;
     }
 
     /**
@@ -807,7 +788,7 @@
         }
         String s;
         if (currentTokenType == VALUE) {
-            s = currentValue.getString();
+            s = currentValue.getValue(Type.STRING);
         } else {
             s = currentToken;
         }
@@ -815,11 +796,11 @@
         return s;
     }
 
-    private CoreValue readString() throws ParseException {
+    private PropertyState readString() throws ParseException {
         if (currentTokenType != VALUE) {
             throw getSyntaxError("string value");
         }
-        CoreValue value = currentValue;
+        PropertyState value = currentValue;
         read();
         return value;
     }
@@ -999,7 +980,7 @@
                         break;
                     }
                     checkLiterals(false);
-                    currentValue = valueFactory.createValue(number);
+                    currentValue = PropertyStates.longProperty("", number);
                     currentTokenType = VALUE;
                     currentToken = "0";
                     parseIndex = i;
@@ -1035,7 +1016,7 @@
                 // (not in the JCR 1.0 spec)
                 // (confusing isn't it?)
                 currentTokenType = IDENTIFIER;
-                currentToken = currentValue.getString();
+                currentToken = currentValue.getValue(Type.STRING);
             }
             return;
         case CHAR_END:
@@ -1069,7 +1050,7 @@
         }
         currentToken = "'";
         checkLiterals(false);
-        currentValue = valueFactory.createValue(result);
+        currentValue = PropertyStates.stringProperty("", result);
         parseIndex = i;
         currentTokenType = VALUE;
     }
@@ -1112,7 +1093,7 @@
         }
         checkLiterals(false);
 
-        currentValue = valueFactory.createValue(bd);
+        currentValue = PropertyStates.decimalProperty("", bd);
         currentTokenType = VALUE;
     }
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/MembershipProviderImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/MembershipProviderImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/MembershipProviderImpl.java	(working copy)
@@ -150,7 +150,7 @@
                 memberPaths = Iterables.transform(vs, new Function<CoreValue,String>() {
                     @Override
                     public String apply(@Nullable CoreValue value) {
-                        return identifierManager.getPath(value);
+                        return identifierManager.getPath(PropertyStates.createProperty("", value));
                     }
                 });
             }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserProviderImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserProviderImpl.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserProviderImpl.java	(working copy)
@@ -30,7 +30,6 @@
 
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.api.security.user.Impersonation;
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Result;
 import org.apache.jackrabbit.oak.api.ResultRow;
@@ -206,7 +205,7 @@
         // can be omitted as principals names are stored in user defined
         // index as well.
         try {
-            CoreValue bindValue = valueFactory.createValue(principal.getName());
+            PropertyState bindValue = PropertyStates.stringProperty("", principal.getName());
             StringBuilder stmt = new StringBuilder();
             stmt.append("SELECT * FROM [").append(UserConstants.NT_REP_AUTHORIZABLE).append(']');
             stmt.append("WHERE [").append(UserConstants.REP_PRINCIPAL_NAME).append("] = $principalName");
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/api/SessionQueryEngine.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/api/SessionQueryEngine.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/api/SessionQueryEngine.java	(working copy)
@@ -61,7 +61,7 @@
      * @throws IllegalArgumentException if there was an error executing the query
      */
     Result executeQuery(String statement, String language,
-            long limit, long offset, Map<String, ? extends CoreValue> bindings,
+            long limit, long offset, Map<String, ? extends PropertyState> bindings,
             Root root, NamePathMapper namePathMapper) throws ParseException;
 
     // TODO pass namespace mapping
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/api/ResultRow.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/api/ResultRow.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/api/ResultRow.java	(working copy)
@@ -25,8 +25,8 @@
 
     String getPath(String selectorName);
 
-    CoreValue getValue(String columnName);
+    PropertyState getValue(String columnName);
 
-    CoreValue[] getValues();
+    PropertyState[] getValues();
 
 }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/api/Blob.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/api/Blob.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/api/Blob.java	(working copy)
@@ -25,7 +25,7 @@
 /**
  * Immutable representation of a binary value of finite length.
  */
-public interface Blob {
+public interface Blob extends Comparable<Blob> {
 
     /**
      * Returns a new stream for this value object. Multiple calls to this
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/IndexRow.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/IndexRow.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/IndexRow.java	(working copy)
@@ -18,7 +18,7 @@
  */
 package org.apache.jackrabbit.oak.spi.query;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
+import org.apache.jackrabbit.oak.api.PropertyState;
 
 /**
  * A row returned by the index.
@@ -40,6 +40,6 @@
      * @param columnName the column name
      * @return the value, or null if not available
      */
-    CoreValue getValue(String columnName);
+    PropertyState getValue(String columnName);
 
 }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java	(revision 1396532)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java	(working copy)
@@ -19,9 +19,12 @@
 package org.apache.jackrabbit.oak.spi.query;
 
 import java.util.Collection;
+import java.util.Iterator;
+
 import javax.jcr.PropertyType;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
 
 /**
  * The filter for an index lookup.
@@ -79,7 +82,7 @@
         /**
          * The first value to read, or null to read from the beginning.
          */
-        public CoreValue first;
+        public PropertyState first;
 
         /**
          * Whether values that match the first should be returned.
@@ -89,7 +92,7 @@
         /**
          * The last value to read, or null to read until the end.
          */
-        public CoreValue last;
+        public PropertyState last;
 
         /**
          * Whether values that match the last should be returned.
@@ -110,8 +113,31 @@
 
         @Override
         public String toString() {
-            return (first == null ? "" : ((firstIncluding ? "[" : "(") + first)) + ".." +
-                    (last == null ? "" : last + (lastIncluding ? "]" : ")"));
+            String firstString = "";
+            if (first != null) {
+                firstString += firstIncluding ? "[" : "(";
+                Iterator<String> iterator = first.getValue(Type.STRINGS)
+                        .iterator();
+                while (iterator.hasNext()) {
+                    firstString += iterator.next();
+                    if (iterator.hasNext()) {
+                        firstString += ", ";
+                    }
+                }
+            }
+            String lastString = "";
+            if (last != null) {
+                Iterator<String> iterator = last.getValue(Type.STRINGS)
+                        .iterator();
+                while (iterator.hasNext()) {
+                    lastString += iterator.next();
+                    if (iterator.hasNext()) {
+                        lastString += ", ";
+                    }
+                }
+                lastString += lastIncluding ? "]" : ")";
+            }
+            return firstString + ".." + lastString;
         }
 
     }
