diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index dfe61ff4e0c4a107e6f1e24667e271557eef2de3..1ce007dc228105849d89d4fc720b9a8bf729ee1b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -148,7 +148,7 @@ pages_prepare: &pages_prepare
     refs:
       - /^release-.*$/i
   script:
-    - echo "Deploying"
+    - echo "Deploying documentation"
     - make doc
     - cp -r build/doc/html public
   artifacts:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f989ba0266a4fda67ffb71bc9e900c21a20484ed..f331067707f361da9430a24cce58d18808e4ac02 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [0.14.0] - 2024-02-20
+
+### Added ###
+
+* `utils.merge_entities` now has a `merge_id_with_resolved_entity` keyword
+  which allows to identify property values with each other in case that one is
+  an id and the other is an Entity with this id. Default is ``False``, so no
+  change to the default behavior.
+* `apiutils.escape_quoted_text` for escaping text in queries.
+
+### Changed ###
+
+* `cached_query()` now also caches uniqueness related exceptions.
+
 ## [0.13.2] - 2023-12-15
 
 ### Fixed ###
diff --git a/CITATION.cff b/CITATION.cff
index b9f249ed501bd1c6dd217e56d7ea47748c1032dc..cbcb570b27b7cd71f50645614222302bccc34805 100644
--- a/CITATION.cff
+++ b/CITATION.cff
@@ -20,6 +20,6 @@ authors:
     given-names: Stefan
     orcid: https://orcid.org/0000-0001-7214-8125
 title: CaosDB - Pylib
-version: 0.13.2
+version: 0.14.0
 doi: 10.3390/data4020083
-date-released: 2023-10-11
+date-released: 2024-02-20
diff --git a/setup.py b/setup.py
index 55e6ee154e2906494bece48c2f741e53e78aa2fe..a77b1095e2c7e3f678a85f7aff49d0a213bb9381 100755
--- a/setup.py
+++ b/setup.py
@@ -48,8 +48,8 @@ from setuptools import find_packages, setup
 
 ISRELEASED = True
 MAJOR = 0
-MINOR = 13
-MICRO = 2
+MINOR = 14
+MICRO = 0
 # Do not tag as pre-release until this commit
 # https://github.com/pypa/packaging/pull/515
 # has made it into a release. Probably we should wait for pypa/packaging>=21.4
diff --git a/src/caosdb/utils/escape.py b/src/caosdb/utils/escape.py
new file mode 100644
index 0000000000000000000000000000000000000000..eecb8885581ec6ea9ecc7a0afb6028430b3d9622
--- /dev/null
+++ b/src/caosdb/utils/escape.py
@@ -0,0 +1,6 @@
+
+from linkahead.utils.escape import *
+from warnings import warn
+
+warn(("CaosDB was renamed to LinkAhead. Please import this library as `import linkahead.utils.escape`. Using the"
+      " old name, starting with caosdb, is deprecated."), DeprecationWarning)
diff --git a/src/doc/conf.py b/src/doc/conf.py
index 84bdc6eac88b345e2b0dd04c5d1f9c413362746b..771a1f048f79b779526c56b2fd56712761021757 100644
--- a/src/doc/conf.py
+++ b/src/doc/conf.py
@@ -29,10 +29,10 @@ copyright = '2023, IndiScale GmbH'
 author = 'Daniel Hornung'
 
 # The short X.Y version
-version = '0.13.2'
+version = '0.14.0'
 # The full version, including alpha/beta/rc tags
 # release = '0.5.2-rc2'
-release = '0.13.2'
+release = '0.14.0'
 
 
 # -- General configuration ---------------------------------------------------
diff --git a/src/doc/index.rst b/src/doc/index.rst
index 24373d4d7c7d68be51915b25cc6201a84a6a4dc0..5139461d47067fed340459c33432a71e80108e7b 100644
--- a/src/doc/index.rst
+++ b/src/doc/index.rst
@@ -15,6 +15,7 @@ Welcome to PyLinkAhead's documentation!
    High Level API <high_level_api>
    Code gallery <gallery/index>
    API documentation <_apidoc/linkahead>
+   Related Projects <related_projects/index>
    Back to Overview <https://docs.indiscale.com/>
 
 
diff --git a/src/doc/related_projects/index.rst b/src/doc/related_projects/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..fd607dfbb67a19c46f525b3f89ce5f597a711676
--- /dev/null
+++ b/src/doc/related_projects/index.rst
@@ -0,0 +1,25 @@
+Related Projects
+++++++++++++++++
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Contents:
+   :hidden:
+
+.. container:: projects
+
+   For in-depth documentation for users, administrators  and developers, you may want to visit the subproject-specific documentation pages for:
+
+   :`Server <https://docs.indiscale.com/caosdb-server>`_: The Java part of the LinkAhead server.
+
+   :`MySQL backend <https://docs.indiscale.com/caosdb-mysqlbackend>`_: The MySQL/MariaDB components of the LinkAhead server.
+
+   :`WebUI <https://docs.indiscale.com/caosdb-webui>`_: The default web frontend for the LinkAhead server.
+
+   :`Advanced user tools <https://docs.indiscale.com/caosdb-advanced-user-tools>`_: The advanced Python tools for LinkAhead.
+
+   :`LinkAhead Crawler <https://docs.indiscale.com/caosdb-crawler/>`_: The crawler is the main tool for automatic data integration in LinkAhead.
+
+   :`LinkAhead <https://docs.indiscale.com/caosdb-deploy>`_: Your all inclusive LinkAhead software package.
+
+   :`Back to Overview <https://docs.indiscale.com/>`_: LinkAhead Documentation.
diff --git a/src/linkahead/apiutils.py b/src/linkahead/apiutils.py
index 597342e38a961c628edd84dd8dff37471ef2570b..e2ed0facea84e6056b1ac877b4417ce6ad8ef504 100644
--- a/src/linkahead/apiutils.py
+++ b/src/linkahead/apiutils.py
@@ -215,6 +215,10 @@ def compare_entities(old_entity: Entity, new_entity: Entity, compare_referenced_
     if old_entity is new_entity:
         return (olddiff, newdiff)
 
+    if type(old_entity) is not type(new_entity):
+        raise ValueError(
+            "Comparison of different Entity types is not supported.")
+
     for attr in SPECIAL_ATTRIBUTES:
         try:
             oldattr = old_entity.__getattribute__(attr)
@@ -354,7 +358,7 @@ def empty_diff(old_entity: Entity, new_entity: Entity, compare_referenced_record
 
 
 def merge_entities(entity_a: Entity, entity_b: Entity, merge_references_with_empty_diffs=True,
-                   force=False):
+                   force=False, merge_id_with_resolved_entity: bool = False):
     """Merge entity_b into entity_a such that they have the same parents and properties.
 
     datatype, unit, value, name and description will only be changed in entity_a
@@ -372,16 +376,22 @@ def merge_entities(entity_a: Entity, entity_b: Entity, merge_references_with_emp
     Parameters
     ----------
     entity_a, entity_b : Entity
-       The entities to be merged. entity_b will be merged into entity_a in place
+        The entities to be merged. entity_b will be merged into entity_a in place
     merge_references_with_empty_diffs : bool, optional
-       Whether the merge is performed if entity_a and entity_b both reference
-       record(s) that may be different Python objects but have empty diffs. If
-       set to `False` a merge conflict will be raised in this case
-       instead. Default is True.
+        Whether the merge is performed if entity_a and entity_b both reference
+        record(s) that may be different Python objects but have empty diffs. If
+        set to `False` a merge conflict will be raised in this case
+        instead. Default is True.
     force : bool, optional
-       If True, in case `entity_a` and `entity_b` have the same properties, the
-       values of `entity_a` are replaced by those of `entity_b` in the merge.
-       If `False`, an EntityMergeConflictError is raised instead. Default is False.
+        If True, in case `entity_a` and `entity_b` have the same properties, the
+        values of `entity_a` are replaced by those of `entity_b` in the
+        merge. If `False`, an EntityMergeConflictError is raised
+        instead. Default is False.
+    merge_id_with_resolved_entity : bool, optional
+        If true, the values of two reference properties will be considered the
+        same if one is an integer id and the other is a db.Entity with this
+        id. I.e., a value 123 is identified with a value ``<Record
+        id=123/>``. Default is False.
 
     Returns
     -------
@@ -427,13 +437,31 @@ def merge_entities(entity_a: Entity, entity_b: Entity, merge_references_with_emp
                         setattr(entity_a.get_property(key), attribute,
                                 diff_r2["properties"][key][attribute])
                     else:
-                        raise EntityMergeConflictError(
-                            f"Entity a ({entity_a.id}, {entity_a.name}) "
-                            f"has a Property '{key}' with {attribute}="
-                            f"{diff_r2['properties'][key][attribute]}\n"
-                            f"Entity b ({entity_b.id}, {entity_b.name}) "
-                            f"has a Property '{key}' with {attribute}="
-                            f"{diff_r1['properties'][key][attribute]}")
+                        raise_error = True
+                        if merge_id_with_resolved_entity is True and attribute == "value":
+                            # Do a special check for the case of an id value on the
+                            # one hand, and a resolved entity on the other side.
+                            this = entity_a.get_property(key).value
+                            that = entity_b.get_property(key).value
+                            same = False
+                            if isinstance(this, list) and isinstance(that, list):
+                                if len(this) == len(that):
+                                    same = all([_same_id_as_resolved_entity(a, b)
+                                                for a, b in zip(this, that)])
+                            else:
+                                same = _same_id_as_resolved_entity(this, that)
+                            if same is True:
+                                setattr(entity_a.get_property(key), attribute,
+                                        diff_r2["properties"][key][attribute])
+                                raise_error = False
+                        if raise_error is True:
+                            raise EntityMergeConflictError(
+                                f"Entity a ({entity_a.id}, {entity_a.name}) "
+                                f"has a Property '{key}' with {attribute}="
+                                f"{diff_r2['properties'][key][attribute]}\n"
+                                f"Entity b ({entity_b.id}, {entity_b.name}) "
+                                f"has a Property '{key}' with {attribute}="
+                                f"{diff_r1['properties'][key][attribute]}")
         else:
             # TODO: This is a temporary FIX for
             #       https://gitlab.indiscale.com/caosdb/src/caosdb-pylib/-/issues/105
@@ -477,11 +505,11 @@ def describe_diff(olddiff, newdiff, name=None, as_update=True):
 
     if len(olddiff["parents"]) > 0:
         description += ("Parents that are only in the old version:\n"
-                        + ", ".join(olddiff["parents"]))
+                        + ", ".join(olddiff["parents"]) + "\n")
 
     if len(newdiff["parents"]) > 0:
         description += ("Parents that are only in the new version:\n"
-                        + ", ".join(olddiff["parents"]))
+                        + ", ".join(olddiff["parents"]) + "\n")
 
     for prop in list(set(list(olddiff["properties"].keys())
                          + list(newdiff["properties"].keys()))):
@@ -586,3 +614,16 @@ def create_flat_list(ent_list: List[Entity], flat: List[Entity]):
                     flat.append(p.value)
                 # TODO: move inside if block?
                 create_flat_list([p.value], flat)
+
+
+def _same_id_as_resolved_entity(this, that):
+    """Checks whether ``this`` and ``that`` either are the same or whether one
+    is an id and the other is a db.Entity with this id.
+
+    """
+    if isinstance(this, Entity) and not isinstance(that, Entity):
+        # this is an Entity with an id, that is not
+        return this.id is not None and this.id == that
+    if not isinstance(this, Entity) and isinstance(that, Entity):
+        return that.id is not None and that.id == this
+    return this == that
diff --git a/src/linkahead/cached.py b/src/linkahead/cached.py
index 2eff5b1b7e0b9c3a6b3b5b461c6920a2a90f3202..b27afe0469bcaac733ece4c0be3d8d124f6305c0 100644
--- a/src/linkahead/cached.py
+++ b/src/linkahead/cached.py
@@ -36,6 +36,7 @@ from enum import Enum
 from functools import lru_cache
 from typing import Union
 
+from .exceptions import EmptyUniqueQueryError, QueryNotUniqueError
 from .utils import get_entity
 from .common.models import execute_query, Entity, Container
 
@@ -80,16 +81,22 @@ If a query phrase is given, the result must be unique.  If this is not what you
     if count != 1:
         raise ValueError("You must supply exactly one argument.")
 
+    result = (None, )
     if eid is not None:
-        return _cached_access(AccessType.EID, eid, unique=True)
+        result = _cached_access(AccessType.EID, eid, unique=True)
     if name is not None:
-        return _cached_access(AccessType.NAME, name, unique=True)
+        result = _cached_access(AccessType.NAME, name, unique=True)
     if path is not None:
-        return _cached_access(AccessType.PATH, path, unique=True)
+        result = _cached_access(AccessType.PATH, path, unique=True)
     if query is not None:
-        return _cached_access(AccessType.QUERY, query, unique=True)
+        result = _cached_access(AccessType.QUERY, query, unique=True)
 
-    raise ValueError("Not all arguments may be None.")
+    if result != (None, ):
+        if isinstance(result, (QueryNotUniqueError, EmptyUniqueQueryError)):
+            raise result
+        return result
+
+    raise RuntimeError("This line should never be reached.")
 
 
 def cached_query(query_string) -> Container:
@@ -98,7 +105,10 @@ def cached_query(query_string) -> Container:
 All additional arguments are at their default values.
 
     """
-    return _cached_access(AccessType.QUERY, query_string, unique=False)
+    result = _cached_access(AccessType.QUERY, query_string, unique=False)
+    if isinstance(result, (QueryNotUniqueError, EmptyUniqueQueryError)):
+        raise result
+    return result
 
 
 @lru_cache(maxsize=DEFAULT_SIZE)
@@ -111,14 +121,17 @@ def _cached_access(kind: AccessType, value: Union[str, int], unique=True):
     if value in _DUMMY_CACHE:
         return _DUMMY_CACHE[value]
 
-    if kind == AccessType.QUERY:
-        return execute_query(value, unique=unique)
-    if kind == AccessType.NAME:
-        return get_entity.get_entity_by_name(value)
-    if kind == AccessType.EID:
-        return get_entity.get_entity_by_id(value)
-    if kind == AccessType.PATH:
-        return get_entity.get_entity_by_path(value)
+    try:
+        if kind == AccessType.QUERY:
+            return execute_query(value, unique=unique)
+        if kind == AccessType.NAME:
+            return get_entity.get_entity_by_name(value)
+        if kind == AccessType.EID:
+            return get_entity.get_entity_by_id(value)
+        if kind == AccessType.PATH:
+            return get_entity.get_entity_by_path(value)
+    except (QueryNotUniqueError, EmptyUniqueQueryError) as exc:
+        return exc
 
     raise ValueError(f"Unknown AccessType: {kind}")
 
diff --git a/src/linkahead/common/models.py b/src/linkahead/common/models.py
index 38c1349067fce68dc3dc0311dc621bd0e383d4b0..ea537ffe8c44a7a7fb79c2d4080b63f9b3da2284 100644
--- a/src/linkahead/common/models.py
+++ b/src/linkahead/common/models.py
@@ -63,8 +63,7 @@ from ..exceptions import (AmbiguousEntityError, AuthorizationError,
                           UniqueNamesError, UnqualifiedParentsError,
                           UnqualifiedPropertiesError)
 from .datatype import (BOOLEAN, DATETIME, DOUBLE, INTEGER, TEXT,
-                       get_list_datatype,
-                       is_list_datatype, is_reference)
+                       get_list_datatype, is_list_datatype, is_reference)
 from .state import State
 from .timezone import TimeZone
 from .utils import uuid, xml2str
@@ -82,7 +81,7 @@ NONE = "NONE"
 
 
 SPECIAL_ATTRIBUTES = ["name", "role", "datatype", "description",
-                      "id", "path", "checksum", "size"]
+                      "id", "path", "checksum", "size", "value"]
 
 
 class Entity:
@@ -155,7 +154,7 @@ class Entity:
         # Copy special attributes:
         # TODO: this might rise an exception when copying
         #       special file attributes like checksum and size.
-        for attribute in SPECIAL_ATTRIBUTES + ["value"]:
+        for attribute in SPECIAL_ATTRIBUTES:
             val = getattr(self, attribute)
             if val is not None:
                 setattr(new, attribute, val)
@@ -1503,7 +1502,12 @@ def _parse_value(datatype, value):
         return float(value)
 
     if datatype == INTEGER:
-        return int(str(value))
+        if isinstance(value, int):
+            return value
+        elif isinstance(value, float) and value.is_integer():
+            return int(value)
+        else:
+            return int(str(value))
 
     if datatype == BOOLEAN:
         if str(value).lower() == "true":
@@ -4510,8 +4514,9 @@ def execute_query(q, unique=False, raise_exception_on_error=True, cache=True,
         Whether an exception should be raised when there are errors in the
         resulting entities. Defaults to True.
     cache : bool
-        Whether to use the query server-side cache (equivalent to adding a
-        "cache" flag). Defaults to True.
+        Whether to use the server's query cache (equivalent to adding a
+        "cache" flag) to the Query object. Defaults to True.  Not to be
+        confused with the ``cached`` module.
     flags : dict of str
         Flags to be added to the request.
     page_length : int
diff --git a/src/linkahead/utils/escape.py b/src/linkahead/utils/escape.py
new file mode 100644
index 0000000000000000000000000000000000000000..d20a07acfdc9f9f06b31176e08dee92fcb1a19df
--- /dev/null
+++ b/src/linkahead/utils/escape.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+#
+# This file is a part of the LinkAhead Project.
+#
+# Copyright (C) 2024 Henrik tom Wörden <h.tomwoerden@indiscale.com>
+# Copyright (C) 2024 IndiScale GmbH <info@indiscale.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+
+import warnings
+
+
+def escape_squoted_text(text: str) -> str:
+    r"""Return an escaped version of the argument.
+
+    The characters ``\``, ``*`` and ``'`` need to be escaped if used in single quoted
+    expressions in the query language.
+
+    This function returns the given string where the characters ``\``, ``'`` and ``*`` are
+    escaped by a ``\`` (backslash character).
+
+    Parameters
+    ----------
+    text : str
+        The text to be escaped.
+
+    Returns
+    -------
+    out : str
+        The escaped text.
+    """
+    return text.replace("\\", r"\\").replace("'", r"\'").replace("*", r"\*")
+
+
+def escape_dquoted_text(text: str) -> str:
+    r"""Return an escaped version of the argument.
+
+    The characters ``\``, ``*`` and ``"`` need to be escaped if used in double quoted
+    expressions in the query language.
+
+    This function returns the given string where the characters ``\``, ``"`` and ``*`` are
+    escaped by a ``\`` (backslash character).
+
+    Parameters
+    ----------
+    text : str
+        The text to be escaped.
+
+    Returns
+    -------
+    out : str
+        The escaped text.
+    """
+    return text.replace("\\", r"\\").replace('"', r"\"").replace("*", r"\*")
+
+
+def escape_quoted_text(text: str) -> str:
+    """
+    Please use escape_squoted_text or escape_dquoted_text instead of this function.
+    """
+    warnings.warn("Please use escape_squoted_text or escape_dquoted_text", DeprecationWarning)
+    return escape_squoted_text(text)
diff --git a/src/linkahead/utils/get_entity.py b/src/linkahead/utils/get_entity.py
index ea9f3228bfc32f223979846623fccdec45752e5d..282f7c86e10571d0e0d62b93da7f61bba5205cba 100644
--- a/src/linkahead/utils/get_entity.py
+++ b/src/linkahead/utils/get_entity.py
@@ -22,7 +22,9 @@
 """Convenience functions to retrieve a specific entity."""
 
 from typing import Union
-from ..common.models import execute_query, Entity
+
+from ..common.models import Entity, execute_query
+from .escape import escape_squoted_text
 
 
 def get_entity_by_name(name: str) -> Entity:
@@ -30,6 +32,7 @@ def get_entity_by_name(name: str) -> Entity:
 
     Submits the query "FIND ENTITY WITH name='{name}'".
     """
+    name = escape_squoted_text(name)
     return execute_query(f"FIND ENTITY WITH name='{name}'", unique=True)
 
 
diff --git a/unittests/test_apiutils.py b/unittests/test_apiutils.py
index b9a02926803c1e7b8134cde904ea2021d0281ff4..4705f19a1bdfbc4358790f787f2dce9ea97fee48 100644
--- a/unittests/test_apiutils.py
+++ b/unittests/test_apiutils.py
@@ -26,13 +26,12 @@
 # A. Schlemmer, 02/2018
 
 
-import pytest
 import linkahead as db
 import linkahead.apiutils
-from linkahead.apiutils import (apply_to_ids, compare_entities, create_id_query,
-                                empty_diff, EntityMergeConflictError,
-                                resolve_reference, merge_entities)
-
+import pytest
+from linkahead.apiutils import (EntityMergeConflictError, apply_to_ids,
+                                compare_entities, create_id_query, empty_diff,
+                                merge_entities, resolve_reference)
 from linkahead.common.models import SPECIAL_ATTRIBUTES
 
 
@@ -104,6 +103,8 @@ def test_compare_entities():
     r1.add_parent("lopp")
     r1.add_property("test", value=2)
     r2.add_property("test", value=2)
+    r1.add_property("testi", importance=linkahead.SUGGESTED, value=2)
+    r2.add_property("testi", importance=linkahead.RECOMMENDED, value=2)
     r1.add_property("tests", value=3)
     r2.add_property("tests", value=45)
     r1.add_property("tester", value=3)
@@ -115,8 +116,8 @@ def test_compare_entities():
 
     assert len(diff_r1["parents"]) == 1
     assert len(diff_r2["parents"]) == 0
-    assert len(diff_r1["properties"]) == 3
-    assert len(diff_r2["properties"]) == 3
+    assert len(diff_r1["properties"]) == 4
+    assert len(diff_r2["properties"]) == 4
 
     assert "test" not in diff_r1["properties"]
     assert "test" not in diff_r2["properties"]
@@ -124,6 +125,9 @@ def test_compare_entities():
     assert "tests" in diff_r1["properties"]
     assert "tests" in diff_r2["properties"]
 
+    assert "testi" in diff_r1["properties"]
+    assert "testi" in diff_r2["properties"]
+
     assert "tester" in diff_r1["properties"]
     assert "tester" in diff_r2["properties"]
 
@@ -212,7 +216,6 @@ def test_compare_special_properties():
         assert len(diff_r2["properties"]) == 0
 
 
-@pytest.mark.xfail
 def test_compare_properties():
     p1 = db.Property()
     p2 = db.Property()
@@ -223,21 +226,12 @@ def test_compare_properties():
     assert len(diff_r1["properties"]) == 0
     assert len(diff_r2["properties"]) == 0
 
-    p1.importance = "SUGGESTED"
     diff_r1, diff_r2 = compare_entities(p1, p2)
     assert len(diff_r1["parents"]) == 0
     assert len(diff_r2["parents"]) == 0
     assert len(diff_r1["properties"]) == 0
     assert len(diff_r2["properties"]) == 0
-    assert "importance" in diff_r1
-    assert diff_r1["importance"] == "SUGGESTED"
-
-    # TODO: I'm not sure why it is not like this:
-    # assert diff_r2["importance"] is None
-    # ... but:
-    assert "importance" not in diff_r2
 
-    p2.importance = "SUGGESTED"
     p1.value = 42
     p2.value = 4
 
@@ -588,3 +582,48 @@ def test_merge_missing_list_datatype_82():
     with pytest.raises(TypeError) as te:
         merge_entities(recA, recB_without_DT, force=True)
     assert "Invalid datatype: List valued properties" in str(te.value)
+
+
+def test_merge_id_with_resolved_entity():
+
+    rtname = "TestRT"
+    ref_id = 123
+    ref_rec = db.Record(id=ref_id).add_parent(name=rtname)
+
+    # recA has the resolved referenced record as value, recB its id. Otherwise,
+    # they are identical.
+    recA = db.Record().add_property(name=rtname, value=ref_rec)
+    recB = db.Record().add_property(name=rtname, value=ref_id)
+
+    # default is strict: raise error since values are different
+    with pytest.raises(EntityMergeConflictError):
+        merge_entities(recA, recB)
+
+    # Overwrite from right to left in both cases
+    merge_entities(recA, recB, merge_id_with_resolved_entity=True)
+    assert recA.get_property(rtname).value == ref_id
+    assert recA.get_property(rtname).value == recB.get_property(rtname).value
+
+    recA = db.Record().add_property(name=rtname, value=ref_rec)
+    merge_entities(recB, recA, merge_id_with_resolved_entity=True)
+    assert recB.get_property(rtname).value == ref_rec
+    assert recA.get_property(rtname).value == recB.get_property(rtname).value
+
+    # id mismatches
+    recB = db.Record().add_property(name=rtname, value=ref_id*2)
+    with pytest.raises(EntityMergeConflictError):
+        merge_entities(recA, recB, merge_id_with_resolved_entity=True)
+
+    other_rec = db.Record(id=None).add_parent(name=rtname)
+    recA = db.Record().add_property(name=rtname, value=other_rec)
+    recB = db.Record().add_property(name=rtname, value=ref_id)
+    with pytest.raises(EntityMergeConflictError):
+        merge_entities(recA, recB, merge_id_with_resolved_entity=True)
+
+    # also works in lists:
+    recA = db.Record().add_property(
+        name=rtname, datatype=db.LIST(rtname), value=[ref_rec, ref_id*2])
+    recB = db.Record().add_property(name=rtname, datatype=db.LIST(rtname), value=[ref_id, ref_id*2])
+    merge_entities(recA, recB, merge_id_with_resolved_entity=True)
+    assert recA.get_property(rtname).value == [ref_id, ref_id*2]
+    assert recA.get_property(rtname).value == recB.get_property(rtname).value
diff --git a/unittests/test_utils.py b/unittests/test_utils.py
index 3d8e2896247f66c98f1461c1a1e91baca5f01cb6..e7495a91d5a0d525278fa608777e6c697ac54e99 100644
--- a/unittests/test_utils.py
+++ b/unittests/test_utils.py
@@ -23,8 +23,10 @@
 #
 """Tests for linkahead.common.utils."""
 from __future__ import unicode_literals
-from lxml.etree import Element
+
 from linkahead.common.utils import xml2str
+from linkahead.utils.escape import (escape_dquoted_text, escape_squoted_text)
+from lxml.etree import Element
 
 
 def test_xml2str():
@@ -32,3 +34,12 @@ def test_xml2str():
     element = Element(name)
     serialized = xml2str(element)
     assert serialized == "<Björn/>\n"
+
+
+def test_escape_quoted_text():
+    assert escape_squoted_text("bla") == "bla"
+    assert escape_squoted_text(r"bl\a") == r"bl\\a"
+    assert escape_squoted_text("bl*a") == r"bl\*a"
+    assert escape_squoted_text(r"bl*ab\\lab\*labla") == r"bl\*ab\\\\lab\\\*labla"
+    assert escape_squoted_text("bl'a") == r"bl\'a"
+    assert escape_dquoted_text('bl"a') == r'bl\"a'