diff --git a/CHANGELOG.md b/CHANGELOG.md
index 54d7e05dbf3fe57eb204d905c39b215d2eb0b196..e3a6b23b4802d0afbce2d35e3360d9a7b4291ad1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Added
 
+* SELECT query support for the GRPC API
+
 ### Changed
 
 * The default behavior of the query FIND SomeName [...] (as well as COUNT and SELECT) is being made configurable and changes
diff --git a/caosdb-proto b/caosdb-proto
index c439aa40a30c9214db315018b84fa50112c9251e..13d083b84400507f6f1967a099e2af006af2a231 160000
--- a/caosdb-proto
+++ b/caosdb-proto
@@ -1 +1 @@
-Subproject commit c439aa40a30c9214db315018b84fa50112c9251e
+Subproject commit 13d083b84400507f6f1967a099e2af006af2a231
diff --git a/src/doc/entities.png b/src/doc/entities.png
index ba3e111a78ddec275636ff0b7e8b324edb13f7be..9f0247eb98ca33d4b62168c34ae7d8564f6fc394 100644
Binary files a/src/doc/entities.png and b/src/doc/entities.png differ
diff --git a/src/doc/entities.svg b/src/doc/entities.svg
new file mode 100644
index 0000000000000000000000000000000000000000..63251e31297f4ef480b497a089987e79296f9534
--- /dev/null
+++ b/src/doc/entities.svg
@@ -0,0 +1,310 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="257.60461mm"
+   height="92.070786mm"
+   viewBox="0 0 257.60462 92.070786"
+   version="1.1"
+   id="svg8"
+   inkscape:version="1.0.2 (e86c870879, 2021-01-15)"
+   sodipodi:docname="entities.svg"
+   inkscape:export-filename="/home/daniel/indiscale/software/linkahead/caosdb-server/doc/entities.png"
+   inkscape:export-xdpi="138.03999"
+   inkscape:export-ydpi="138.03999">
+  <defs
+     id="defs2">
+    <marker
+       style="overflow:visible"
+       id="marker1306"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="EmptyTriangleOutM"
+       inkscape:isstock="true">
+      <path
+         transform="matrix(0.4,0,0,0.4,-1.8,0)"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="M 5.77,0 -2.88,5 V -5 Z"
+         id="path1304" />
+    </marker>
+    <marker
+       style="overflow:visible"
+       id="marker1244"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="EmptyTriangleOutM"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         transform="matrix(0.4,0,0,0.4,-1.8,0)"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="M 5.77,0 -2.88,5 V -5 Z"
+         id="path1242" />
+    </marker>
+    <marker
+       style="overflow:visible"
+       id="marker1184"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="EmptyTriangleOutM"
+       inkscape:isstock="true">
+      <path
+         transform="matrix(0.4,0,0,0.4,-1.8,0)"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="M 5.77,0 -2.88,5 V -5 Z"
+         id="path1182" />
+    </marker>
+    <marker
+       style="overflow:visible"
+       id="EmptyTriangleOutM"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="EmptyTriangleOutM"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         transform="matrix(0.4,0,0,0.4,-1.8,0)"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="M 5.77,0 -2.88,5 V -5 Z"
+         id="path1029" />
+    </marker>
+    <marker
+       style="overflow:visible"
+       id="Arrow1Lstart"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow1Lstart"
+       inkscape:isstock="true">
+      <path
+         transform="matrix(0.8,0,0,0.8,10,0)"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="M 0,0 5,-5 -12.5,0 5,5 Z"
+         id="path866" />
+    </marker>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.98994949"
+     inkscape:cx="456.88236"
+     inkscape:cy="211.12615"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1920"
+     inkscape:window-height="1135"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1"
+     inkscape:document-rotation="0" />
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(200.30478,-69.702334)">
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#EmptyTriangleOutM)"
+       d="m -164.37076,125.68023 c 0,0 7.98157,-12.50783 14.18443,-16.03726 16.03406,-9.12339 40.17589,5.10656 52.013732,-9.04239 2.255094,-2.695361 2.46672,-11.843345 2.46672,-11.843345"
+       id="path962"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cssc" />
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker1244)"
+       d="m -96.257355,126.19649 c 0.965381,-4.15542 1.084194,-8.26873 7.5598,-11.26854 6.807025,-3.15335 7.474053,-2.81327 8.712136,-12.65522 0.438632,-3.486838 0.252106,-13.515495 0.252106,-13.515495"
+       id="path1240"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cssc" />
+    <g
+       id="g41"
+       transform="translate(0,-26.458334)">
+      <rect
+         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.39511;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         id="rect835"
+         width="54.226971"
+         height="16.809242"
+         x="-98.980247"
+         y="96.858223" />
+      <text
+         xml:space="preserve"
+         style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
+         x="-87.690086"
+         y="108.18256"
+         id="text817"><tspan
+           sodipodi:role="line"
+           id="tspan815"
+           x="-87.690086"
+           y="108.18256"
+           style="stroke-width:0.264583">Entity</tspan></text>
+    </g>
+    <rect
+       style="fill:#5bb374;fill-opacity:1;stroke:#000000;stroke-width:1.39511;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect835-3"
+       width="69.995872"
+       height="16.809242"
+       x="-199.60722"
+       y="125.42733" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
+       x="-196.9794"
+       y="136.75168"
+       id="text821"><tspan
+         sodipodi:role="line"
+         id="tspan819"
+         x="-196.9794"
+         y="136.75168"
+         style="stroke-width:0.264583">Record Type</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker1306)"
+       d="m -35.372097,126.19649 c -0.965381,-4.15542 -1.084194,-8.26873 -7.5598,-11.26854 -6.807025,-3.15335 -19.11572,-2.81327 -20.353803,-12.65522 -0.438632,-3.486838 -0.475048,-13.515495 -0.475048,-13.515495"
+       id="path1302"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cssc" />
+    <rect
+       style="fill:#e25b58;fill-opacity:1;stroke:#000000;stroke-width:1.39511;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect835-5"
+       width="49.950661"
+       height="16.809242"
+       x="-115.86278"
+       y="125.42733" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
+       x="-109.21966"
+       y="137.77745"
+       id="text825"><tspan
+         sodipodi:role="line"
+         id="tspan823"
+         x="-109.21966"
+         y="137.77745"
+         style="stroke-width:0.264583">Record</tspan></text>
+    <rect
+       style="fill:#5c6de1;fill-opacity:1;stroke:#000000;stroke-width:1.39511;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect835-7"
+       width="52.356087"
+       height="16.809242"
+       x="-52.16354"
+       y="125.42733" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
+       x="-48.754131"
+       y="136.5889"
+       id="text829"><tspan
+         sodipodi:role="line"
+         id="tspan827"
+         x="-48.754131"
+         y="136.5889"
+         style="stroke-width:0.264583">Property</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker1184)"
+       d="m 33.047535,126.20395 c 0,0 -7.981575,-12.50783 -14.184434,-16.03726 -16.0340604,-9.12339 -52.346724,5.10656 -64.184564,-9.04239 -2.25509,-2.695361 -2.46672,-12.221321 -2.46672,-12.221321"
+       id="path1180"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cssc" />
+    <rect
+       style="fill:#d87ac8;fill-opacity:1;stroke:#000000;stroke-width:1.39511;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect835-6"
+       width="41.932575"
+       height="16.809235"
+       x="13.941126"
+       y="125.42734" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
+       x="25.812365"
+       y="137.77745"
+       id="text833"><tspan
+         sodipodi:role="line"
+         id="tspan831"
+         x="25.812365"
+         y="137.77745"
+         style="stroke-width:0.264583">File</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6.35px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
+       x="-164.62788"
+       y="152.51477"
+       id="text1001"><tspan
+         sodipodi:role="line"
+         id="tspan1003"
+         x="-164.62788"
+         y="152.51477">&quot;blue print&quot; </tspan><tspan
+         sodipodi:role="line"
+         id="tspan1005"
+         x="-164.62788"
+         y="160.45227">of data objects</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6.35px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
+       x="-90.906052"
+       y="152.51477"
+       id="text1001-5"><tspan
+         sodipodi:role="line"
+         id="tspan843"
+         x="-90.906052"
+         y="152.51477">actual instances</tspan><tspan
+         sodipodi:role="line"
+         id="tspan845"
+         x="-90.906052"
+         y="160.45227">of data objects</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6.35px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
+       x="-26.117271"
+       y="152.51477"
+       id="text1001-6"><tspan
+         sodipodi:role="line"
+         id="tspan1050"
+         x="-26.117271"
+         y="152.51477">Records can </tspan><tspan
+         sodipodi:role="line"
+         id="tspan1052"
+         x="-26.117271"
+         y="160.45227">have Properties</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6.35px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
+       x="35.023685"
+       y="153.13023"
+       id="text1001-9"><tspan
+         sodipodi:role="line"
+         id="tspan1118"
+         x="35.023685"
+         y="153.13023">representation </tspan><tspan
+         sodipodi:role="line"
+         id="tspan1120"
+         x="35.023685"
+         y="161.06773">of files</tspan></text>
+  </g>
+</svg>
diff --git a/src/main/java/org/caosdb/server/datatype/BooleanDatatype.java b/src/main/java/org/caosdb/server/datatype/BooleanDatatype.java
index 92c3440aeab6ef1cc86426c4e5d2356aef6720aa..366b77937d3dcf020d98c035821bed0aedaadf8d 100644
--- a/src/main/java/org/caosdb/server/datatype/BooleanDatatype.java
+++ b/src/main/java/org/caosdb/server/datatype/BooleanDatatype.java
@@ -32,6 +32,8 @@ public class BooleanDatatype extends AbstractDatatype {
   public SingleValue parseValue(final Object value) throws Message {
     if (value instanceof BooleanValue) {
       return (SingleValue) value;
+    } else if (value instanceof CollectionValue) {
+      throw ServerMessages.DATA_TYPE_DOES_NOT_ACCEPT_COLLECTION_VALUES;
     } else if (value instanceof SingleValue) {
       return parse(((SingleValue) value).toDatabaseString());
     } else {
diff --git a/src/main/java/org/caosdb/server/datatype/DoubleDatatype.java b/src/main/java/org/caosdb/server/datatype/DoubleDatatype.java
index e9e2526521d044fc89746eb0e3204de97f473e0c..f3f17ace28802cdb045a4cd66a7ca2a78cde2d1c 100644
--- a/src/main/java/org/caosdb/server/datatype/DoubleDatatype.java
+++ b/src/main/java/org/caosdb/server/datatype/DoubleDatatype.java
@@ -34,6 +34,8 @@ public class DoubleDatatype extends AbstractDatatype {
     try {
       if (value instanceof GenericValue) {
         return parse(((GenericValue) value).toDatabaseString());
+      } else if (value instanceof CollectionValue) {
+        throw ServerMessages.DATA_TYPE_DOES_NOT_ACCEPT_COLLECTION_VALUES;
       } else {
         return parse(value.toString());
       }
diff --git a/src/main/java/org/caosdb/server/datatype/IntegerDatatype.java b/src/main/java/org/caosdb/server/datatype/IntegerDatatype.java
index 0b1116ed5cbc135f846b5c7fbc0e1d497576380f..f3a58a4a52b888e00ec80f71793cd452c4b98910 100644
--- a/src/main/java/org/caosdb/server/datatype/IntegerDatatype.java
+++ b/src/main/java/org/caosdb/server/datatype/IntegerDatatype.java
@@ -33,6 +33,8 @@ public class IntegerDatatype extends AbstractDatatype {
     try {
       if (value instanceof GenericValue) {
         return new GenericValue(Long.parseLong(((GenericValue) value).toDatabaseString()));
+      } else if (value instanceof CollectionValue) {
+        throw ServerMessages.DATA_TYPE_DOES_NOT_ACCEPT_COLLECTION_VALUES;
       } else {
         return new GenericValue(Long.parseLong(value.toString()));
       }
diff --git a/src/main/java/org/caosdb/server/datatype/TextDatatype.java b/src/main/java/org/caosdb/server/datatype/TextDatatype.java
index af06ed6de1f0f49b481b2a706f529037e555785e..3b32fadcb3467528ac377e67db1081295f85c7ec 100644
--- a/src/main/java/org/caosdb/server/datatype/TextDatatype.java
+++ b/src/main/java/org/caosdb/server/datatype/TextDatatype.java
@@ -23,6 +23,7 @@
 package org.caosdb.server.datatype;
 
 import org.caosdb.server.entity.Message;
+import org.caosdb.server.utils.ServerMessages;
 
 @DatatypeDefinition(name = "Text")
 public class TextDatatype extends AbstractDatatype {
@@ -31,6 +32,8 @@ public class TextDatatype extends AbstractDatatype {
   public SingleValue parseValue(final Object value) throws Message {
     if (value instanceof GenericValue) {
       return (GenericValue) value;
+    } else if (value instanceof CollectionValue) {
+      throw ServerMessages.DATA_TYPE_DOES_NOT_ACCEPT_COLLECTION_VALUES;
     }
     return new GenericValue(value.toString());
   }
diff --git a/src/main/java/org/caosdb/server/entity/container/TransactionContainer.java b/src/main/java/org/caosdb/server/entity/container/TransactionContainer.java
index 289287c82fcd32092d15cb1f10a9dbbd83e77fde..d396345508a028700db6fd8e973390fd634e54d9 100644
--- a/src/main/java/org/caosdb/server/entity/container/TransactionContainer.java
+++ b/src/main/java/org/caosdb/server/entity/container/TransactionContainer.java
@@ -34,6 +34,7 @@ import org.caosdb.server.entity.Message;
 import org.caosdb.server.entity.Message.MessageType;
 import org.caosdb.server.entity.xml.ToElementable;
 import org.caosdb.server.jobs.JobTarget;
+import org.caosdb.server.query.Query;
 import org.caosdb.server.utils.EntityStatus;
 import org.jdom2.Element;
 
@@ -120,6 +121,7 @@ public class TransactionContainer extends Container<EntityInterface>
   private HashMap<String, FileProperties> files = new HashMap<String, FileProperties>();
 
   private TransactionBenchmark benchmark;
+  private Query query;
 
   public void setFiles(final HashMap<String, FileProperties> files) {
     this.files = files;
@@ -183,4 +185,12 @@ public class TransactionContainer extends Container<EntityInterface>
       addMessage(new Message(MessageType.Error, m.getCode(), m.getDescription(), m.getBody()));
     }
   }
+
+  public void setQuery(Query query) {
+    this.query = query;
+  }
+
+  public Query getQuery() {
+    return query;
+  }
 }
diff --git a/src/main/java/org/caosdb/server/grpc/CaosDBToGrpcConverters.java b/src/main/java/org/caosdb/server/grpc/CaosDBToGrpcConverters.java
index fc3c26f664cd81ec27139c2591f52be7af29c3bd..6fef590746bbf2c3ff802baec73385e4b061ec95 100644
--- a/src/main/java/org/caosdb/server/grpc/CaosDBToGrpcConverters.java
+++ b/src/main/java/org/caosdb/server/grpc/CaosDBToGrpcConverters.java
@@ -45,9 +45,14 @@ import org.caosdb.api.entity.v1.MessageCode;
 import org.caosdb.api.entity.v1.Parent;
 import org.caosdb.api.entity.v1.ReferenceDataType;
 import org.caosdb.api.entity.v1.ScalarValue;
+import org.caosdb.api.entity.v1.SelectQueryColumn;
+import org.caosdb.api.entity.v1.SelectQueryHeader;
+import org.caosdb.api.entity.v1.SelectQueryResult;
+import org.caosdb.api.entity.v1.SelectQueryRow;
 import org.caosdb.api.entity.v1.SpecialValue;
 import org.caosdb.api.entity.v1.Version;
 import org.caosdb.datetime.DateTimeInterface;
+import org.caosdb.server.database.exceptions.TransactionException;
 import org.caosdb.server.datatype.AbstractCollectionDatatype;
 import org.caosdb.server.datatype.AbstractDatatype;
 import org.caosdb.server.datatype.BooleanDatatype;
@@ -71,10 +76,13 @@ import org.caosdb.server.entity.Message;
 import org.caosdb.server.entity.Role;
 import org.caosdb.server.entity.StatementStatus;
 import org.caosdb.server.entity.container.ParentContainer;
+import org.caosdb.server.entity.container.RetrieveContainer;
 import org.caosdb.server.entity.wrapper.Property;
 import org.caosdb.server.entity.xml.SerializeFieldStrategy;
 import org.caosdb.server.permissions.EntityACI;
 import org.caosdb.server.permissions.EntityPermission;
+import org.caosdb.server.query.Query;
+import org.caosdb.server.query.Query.Selection;
 
 public class CaosDBToGrpcConverters {
 
@@ -588,4 +596,200 @@ public class CaosDBToGrpcConverters {
     }
     return result;
   }
+
+  public org.caosdb.api.entity.v1.SelectQueryResult.Builder convertSelectResult(
+      RetrieveContainer container) {
+    SelectQueryResult.Builder result = SelectQueryResult.newBuilder();
+    result.setHeader(convertSelectQueryHeader(container));
+    for (EntityInterface e : container) {
+      result.addDataRows(convert(container.getQuery().getSelections(), e));
+    }
+    return result;
+  }
+
+  private SelectQueryRow.Builder convert(List<Selection> selections, EntityInterface e) {
+
+    SelectQueryRow.Builder result = SelectQueryRow.newBuilder();
+    for (Selection s : selections) {
+      org.caosdb.api.entity.v1.Value.Builder value = getSelectedValue(s, e);
+      result.addCells(value);
+    }
+    return result;
+  }
+
+  /**
+   * Handle special selectors like "id", "name", "value", "parent",...
+   *
+   * @return a {@link org.caosdb.api.entity.v1.Value.Builder}, if the selector has been handled.
+   *     Return null when the caller must handle the selector otherwise.
+   */
+  private org.caosdb.api.entity.v1.Value.Builder handleSpecialSelectors(
+      String selector, EntityInterface e) {
+    org.caosdb.api.entity.v1.Value.Builder result = null;
+    switch (selector) {
+      case "value":
+        try {
+          e.parseValue();
+        } catch (Message m) {
+          throw new TransactionException(m);
+        }
+        if (e.hasValue()) {
+          return convert(e.getValue());
+        }
+        break;
+      case "version":
+        if (e.hasVersion()) {
+          result = org.caosdb.api.entity.v1.Value.newBuilder();
+          result.setScalarValue(convertStringValue(e.getVersion().getId()));
+          return result;
+        }
+        break;
+      case "name":
+        if (e.hasName()) {
+          result = org.caosdb.api.entity.v1.Value.newBuilder();
+          result.setScalarValue(convertStringValue(e.getName()));
+          return result;
+        }
+        break;
+
+      case "id":
+        result = org.caosdb.api.entity.v1.Value.newBuilder();
+        result.setScalarValue(convertStringValue(e.getId().toString()));
+        return result;
+
+      case "description":
+        if (e.hasDescription()) {
+          result = org.caosdb.api.entity.v1.Value.newBuilder();
+          result.setScalarValue(convertStringValue(e.getDescription()));
+          return result;
+        }
+        break;
+
+      case "unit":
+        final String unit = getStringUnit(e);
+        if (unit != null) {
+          result = org.caosdb.api.entity.v1.Value.newBuilder();
+          result.setScalarValue(convertStringValue(unit));
+          return result;
+        }
+        break;
+      case "datatype":
+        if (e.hasDatatype()) {
+          result = org.caosdb.api.entity.v1.Value.newBuilder();
+          result.setScalarValue(convertStringValue(e.getDatatype().toString()));
+          return result;
+        }
+        break;
+      case "path":
+        if (e.hasFileProperties() && e.getFileProperties().hasPath()) {
+          result = org.caosdb.api.entity.v1.Value.newBuilder();
+          result.setScalarValue(convertStringValue(e.getFileProperties().getPath()));
+          return result;
+        }
+        break;
+      case "parent":
+        if (e.hasParents()) {
+          result = org.caosdb.api.entity.v1.Value.newBuilder();
+          CollectionValues.Builder parents = CollectionValues.newBuilder();
+          for (org.caosdb.server.entity.wrapper.Parent p : e.getParents()) {
+            parents.addValues(ScalarValue.newBuilder().setStringValue(p.getName()));
+          }
+          result.setListValues(parents);
+          return result;
+        }
+        break;
+      default:
+        break;
+    }
+    return null;
+  }
+
+  private org.caosdb.api.entity.v1.Value.Builder getSelectedValue(Selection s, EntityInterface e) {
+    org.caosdb.api.entity.v1.Value.Builder result = null;
+    String selector = s.getSelector();
+    result = handleSpecialSelectors(selector, e);
+    if (result == null) {
+      // selector for a normal property
+      List<org.caosdb.api.entity.v1.Value.Builder> results = new LinkedList<>();
+      for (Property p : e.getProperties()) {
+        if (p.getName() != null && p.getName().equals(selector)) {
+          if (s.getSubselection() == null) {
+            // no subselection -> just return the actual value
+            try {
+              p.parseValue();
+            } catch (Message m) {
+              throw new TransactionException(m);
+            }
+
+            results.add(convert(p.getValue()));
+          } else {
+            // with subselection, e.g. p1.unit, site.geolocation.longitude
+            String subselection = s.getSubselection().getSelector();
+            result = handleSpecialSelectors(subselection, p);
+            if (result == null) {
+              // normal property
+              Value v = p.getValue();
+
+              if (v instanceof ReferenceValue) {
+                EntityInterface referenced_entity = ((ReferenceValue) v).getEntity();
+                result = getSelectedValue(s.getSubselection(), referenced_entity);
+              } else if (v instanceof CollectionValue) {
+                for (Value i : (CollectionValue) v) {
+                  if (i instanceof ReferenceValue) {
+                    EntityInterface referenced_entity = ((ReferenceValue) i).getEntity();
+                    result = getSelectedValue(s.getSubselection(), referenced_entity);
+                    results.add(result);
+                  } else {
+                    // Non-reference scalar value
+                    result = getSelectedValue(s.getSubselection(), p);
+                    results.add(result);
+                  }
+                }
+              } else {
+                // Actual sub-property
+                result = getSelectedValue(s.getSubselection(), p);
+                results.add(result);
+              }
+
+            } else {
+              results.add(result);
+            }
+          }
+        }
+      }
+
+      if (results.size() > 1) {
+        // There have been multiple matching properties
+        CollectionValues.Builder values = CollectionValues.newBuilder();
+        for (org.caosdb.api.entity.v1.Value.Builder v : results) {
+          // Concatenate all found values to a list
+          if (v.hasScalarValue()) {
+            values.addValues(v.getScalarValueBuilder());
+          } else {
+            values.addAllValues(v.getListValuesBuilder().getValuesList());
+          }
+        }
+        result = org.caosdb.api.entity.v1.Value.newBuilder();
+        result.setListValues(values);
+      } else if (results.size() == 1) {
+        // There has been exactly one matching property
+        result = results.get(0);
+      }
+    }
+
+    if (result == null) {
+      // no matching property found
+      result = org.caosdb.api.entity.v1.Value.newBuilder();
+    }
+    return result;
+  }
+
+  private SelectQueryHeader.Builder convertSelectQueryHeader(RetrieveContainer container) {
+    SelectQueryHeader.Builder result = SelectQueryHeader.newBuilder();
+    Query query = container.getQuery();
+    for (Selection s : query.getSelections()) {
+      result.addColumns(SelectQueryColumn.newBuilder().setName(s.toString()));
+    }
+    return result;
+  }
 }
diff --git a/src/main/java/org/caosdb/server/grpc/EntityTransactionServiceImpl.java b/src/main/java/org/caosdb/server/grpc/EntityTransactionServiceImpl.java
index 315358deacf2d77828add722d29a6c4643acd283..030b52544fec858ef3c62c531e9627baf5500551 100644
--- a/src/main/java/org/caosdb/server/grpc/EntityTransactionServiceImpl.java
+++ b/src/main/java/org/caosdb/server/grpc/EntityTransactionServiceImpl.java
@@ -33,6 +33,7 @@ import org.caosdb.api.entity.v1.Entity;
 import org.caosdb.api.entity.v1.EntityRequest;
 import org.caosdb.api.entity.v1.EntityResponse;
 import org.caosdb.api.entity.v1.EntityTransactionServiceGrpc.EntityTransactionServiceImplBase;
+import org.caosdb.api.entity.v1.FindQueryResult;
 import org.caosdb.api.entity.v1.IdResponse;
 import org.caosdb.api.entity.v1.InsertRequest;
 import org.caosdb.api.entity.v1.InsertResponse;
@@ -43,6 +44,7 @@ import org.caosdb.api.entity.v1.MultiTransactionResponse;
 import org.caosdb.api.entity.v1.MultiUpdateEntityACLRequest;
 import org.caosdb.api.entity.v1.MultiUpdateEntityACLResponse;
 import org.caosdb.api.entity.v1.RetrieveResponse;
+import org.caosdb.api.entity.v1.SelectQueryResult;
 import org.caosdb.api.entity.v1.TransactionRequest;
 import org.caosdb.api.entity.v1.TransactionRequest.WrappedRequestsCase;
 import org.caosdb.api.entity.v1.TransactionResponse;
@@ -59,6 +61,7 @@ import org.caosdb.server.entity.UpdateEntity;
 import org.caosdb.server.entity.container.RetrieveContainer;
 import org.caosdb.server.entity.container.WritableContainer;
 import org.caosdb.server.permissions.EntityPermission;
+import org.caosdb.server.query.Query;
 import org.caosdb.server.transaction.Retrieve;
 import org.caosdb.server.transaction.RetrieveACL;
 import org.caosdb.server.transaction.UpdateACL;
@@ -77,6 +80,24 @@ public class EntityTransactionServiceImpl extends EntityTransactionServiceImplBa
     this.fileTransmissionService = fileTransmissionService;
   }
 
+  private void prepareDownload(
+      EntityResponse.Builder entityResponse, EntityInterface entity, FileDownload fileDownload)
+      throws Exception {
+    try {
+      entity.checkPermission(EntityPermission.RETRIEVE_FILE);
+      if (fileDownload == null) {
+        fileDownload = fileTransmissionService.registerFileDownload(null);
+      }
+      entity.getFileProperties().retrieveFromFileSystem();
+      entityResponse.setDownloadId(
+          fileTransmissionService.registerFileDownload(
+              fileDownload.getId(), entity.getFileProperties()));
+    } catch (AuthenticationException exc) {
+      entityResponse.addErrors(caosdbToGrpc.convert(ServerMessages.AUTHORIZATION_ERROR));
+      entityResponse.addInfos(caosdbToGrpc.convert(new Message(exc.getMessage())));
+    }
+  }
+
   /**
    * Handle read-only transactions. Of these only one may be a query at the moment, the others must
    * be ID retrieves.
@@ -129,30 +150,42 @@ public class EntityTransactionServiceImpl extends EntityTransactionServiceImplBa
 
     final Retrieve transaction = new Retrieve(container);
     transaction.execute();
-    if (container.getFlags().containsKey("query_count_result")) {
-      final int count = Integer.parseInt(container.getFlags().get("query_count_result"));
+    if (container.getQuery() != null && container.getQuery().getType() == Query.Type.COUNT) {
+      // this was a count query
+      final int count = container.getQuery().getCount();
       builder
           .addResponsesBuilder()
           .setRetrieveResponse(RetrieveResponse.newBuilder().setCountResult(count));
+    } else if (container.getQuery() != null
+        && container.getQuery().getType() == Query.Type.SELECT) {
+      // this was a select query
+      SelectQueryResult.Builder selectResult = caosdbToGrpc.convertSelectResult(container);
+      builder
+          .addResponsesBuilder()
+          .setRetrieveResponse(RetrieveResponse.newBuilder().setSelectResult(selectResult));
+    } else if (container.getQuery() != null && container.getQuery().getType() == Query.Type.FIND) {
+      // this was a find query
+      final boolean download_files_container = container.getFlags().containsKey("download_files");
+      FindQueryResult.Builder findResult = FindQueryResult.newBuilder();
+      for (final EntityInterface entity : container) {
+        final EntityResponse.Builder entityResponse = caosdbToGrpc.convert(entity);
+        if ((download_files_container || entity.getFlags().containsKey("download_files"))
+            && entity.hasFileProperties()) {
+          prepareDownload(entityResponse, entity, fileDownload);
+        }
+        findResult.addResultSet(entityResponse);
+      }
+      builder
+          .addResponsesBuilder()
+          .setRetrieveResponse(RetrieveResponse.newBuilder().setFindResult(findResult));
     } else {
+      // normal retrieval via id
       final boolean download_files_container = container.getFlags().containsKey("download_files");
       for (final EntityInterface entity : container) {
         final EntityResponse.Builder entityResponse = caosdbToGrpc.convert(entity);
         if ((download_files_container || entity.getFlags().containsKey("download_files"))
             && entity.hasFileProperties()) {
-          try {
-            entity.checkPermission(EntityPermission.RETRIEVE_FILE);
-            if (fileDownload == null) {
-              fileDownload = fileTransmissionService.registerFileDownload(null);
-            }
-            entity.getFileProperties().retrieveFromFileSystem();
-            entityResponse.setDownloadId(
-                fileTransmissionService.registerFileDownload(
-                    fileDownload.getId(), entity.getFileProperties()));
-          } catch (AuthenticationException exc) {
-            entityResponse.addErrors(caosdbToGrpc.convert(ServerMessages.AUTHORIZATION_ERROR));
-            entityResponse.addInfos(caosdbToGrpc.convert(new Message(exc.getMessage())));
-          }
+          prepareDownload(entityResponse, entity, fileDownload);
         }
         builder
             .addResponsesBuilder()
diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckQueryTemplate.java b/src/main/java/org/caosdb/server/jobs/core/CheckQueryTemplate.java
index 1b5ccb41f58b92d2230fb99e5635ad02bd9922f5..42dccf552d69bbba6bee2ab909eaf31b5159041f 100644
--- a/src/main/java/org/caosdb/server/jobs/core/CheckQueryTemplate.java
+++ b/src/main/java/org/caosdb/server/jobs/core/CheckQueryTemplate.java
@@ -26,8 +26,6 @@ import org.caosdb.server.entity.Role;
 import org.caosdb.server.jobs.EntityJob;
 import org.caosdb.server.query.Query;
 import org.caosdb.server.query.Query.ParsingException;
-import org.caosdb.server.query.Query.Type;
-import org.caosdb.server.utils.EntityStatus;
 import org.caosdb.server.utils.ServerMessages;
 
 public class CheckQueryTemplate extends EntityJob {
@@ -40,20 +38,22 @@ public class CheckQueryTemplate extends EntityJob {
         final Query q = new Query(getEntity().getQueryTemplateDefinition());
         try {
           q.parse();
-          if (q.getType() == Type.COUNT) {
-            getEntity().setEntityStatus(EntityStatus.UNQUALIFIED);
-            getEntity().addError(ServerMessages.QUERY_TEMPLATE_WITH_COUNT);
-          }
-          if (q.getSelections() != null && !q.getSelections().isEmpty()) {
-            getEntity().setEntityStatus(EntityStatus.UNQUALIFIED);
-            getEntity().addError(ServerMessages.QUERY_TEMPLATE_WITH_SELECT);
+          switch (q.getType()) {
+            case COUNT:
+              getEntity().addError(ServerMessages.QUERY_TEMPLATE_WITH_COUNT);
+              break;
+
+            case SELECT:
+              getEntity().addError(ServerMessages.QUERY_TEMPLATE_WITH_SELECT);
+              break;
+
+            default:
+              break;
           }
         } catch (final ParsingException e) {
-          getEntity().setEntityStatus(EntityStatus.UNQUALIFIED);
           getEntity().addError(ServerMessages.QUERY_PARSING_ERROR);
         }
       } else {
-        getEntity().setEntityStatus(EntityStatus.UNQUALIFIED);
         getEntity().addError(ServerMessages.QUERY_TEMPLATE_HAS_NO_QUERY_DEFINITION);
       }
     }
diff --git a/src/main/java/org/caosdb/server/jobs/core/ExecuteQuery.java b/src/main/java/org/caosdb/server/jobs/core/ExecuteQuery.java
index 0a19977b199195610d96f887c4c538a75b491511..352a7428f2007fc59cbf0b3fb7f067d1198857c1 100644
--- a/src/main/java/org/caosdb/server/jobs/core/ExecuteQuery.java
+++ b/src/main/java/org/caosdb/server/jobs/core/ExecuteQuery.java
@@ -31,8 +31,6 @@ import org.caosdb.server.jobs.JobAnnotation;
 import org.caosdb.server.jobs.TransactionStage;
 import org.caosdb.server.query.Query;
 import org.caosdb.server.query.Query.ParsingException;
-import org.caosdb.server.query.Query.Type;
-import org.caosdb.server.utils.EntityStatus;
 import org.caosdb.server.utils.ServerMessages;
 
 @JobAnnotation(flag = "query", stage = TransactionStage.INIT)
@@ -41,27 +39,23 @@ public class ExecuteQuery extends FlagJob {
   @Override
   protected void job(final String value) {
 
-    final Query queryInstance = new Query(value, getTransaction().getTransactor(), getContainer());
-    try {
-      if (value != null) {
+    if (value != null) {
+      final Query queryInstance =
+          new Query(value, getTransaction().getTransactor(), getContainer());
+      getContainer().setQuery(queryInstance);
+      try {
         queryInstance.execute(getTransaction().getAccess());
+      } catch (final ParsingException e) {
+        getContainer().addError(ServerMessages.QUERY_PARSING_ERROR);
+      } catch (final UnsupportedOperationException e) {
+        getContainer().addError(ServerMessages.QUERY_EXCEPTION);
+        getContainer()
+            .addMessage(new Message(MessageType.Info, (MessageCode) null, e.getMessage()));
+      }
+      getContainer().addMessage(queryInstance);
+      for (final EntityInterface entity : getContainer()) {
+        getTransaction().getSchedule().addAll(loadJobs(entity, getTransaction()));
       }
-    } catch (final ParsingException e) {
-      getContainer().addMessage(ServerMessages.QUERY_PARSING_ERROR);
-      getContainer().setStatus(EntityStatus.UNQUALIFIED);
-    } catch (final UnsupportedOperationException e) {
-      getContainer().addMessage(ServerMessages.QUERY_EXCEPTION);
-      getContainer().setStatus(EntityStatus.UNQUALIFIED);
-      getContainer().addMessage(new Message(MessageType.Info, (MessageCode) null, e.getMessage()));
-    }
-    getContainer().addMessage(queryInstance);
-    if (queryInstance.getQuery().getType() == Type.COUNT) {
-      getContainer()
-          .getFlags()
-          .put("query_count_result", Integer.toString(queryInstance.getCount()));
-    }
-    for (final EntityInterface entity : getContainer()) {
-      getTransaction().getSchedule().addAll(loadJobs(entity, getTransaction()));
     }
   }
 }
diff --git a/src/main/java/org/caosdb/server/query/CQLParser.g4 b/src/main/java/org/caosdb/server/query/CQLParser.g4
index ac49197e0602e3b7e668e4ac5ba05cc44f595b22..1e5a732140588682abe23bc32d66db3d5104c9c8 100644
--- a/src/main/java/org/caosdb/server/query/CQLParser.g4
+++ b/src/main/java/org/caosdb/server/query/CQLParser.g4
@@ -43,7 +43,7 @@ cq returns [Query.Type t, List<Query.Selection> s, Query.Pattern e, Query.Role r
 :
 
     (
-       SELECT prop_sel {$s = $prop_sel.s;} FROM {$t = Query.Type.FIND;}
+       SELECT prop_sel {$s = $prop_sel.s;} FROM {$t = Query.Type.SELECT;}
        | FIND {$t = Query.Type.FIND;}
        | COUNT {$t = Query.Type.COUNT;})
     (version {$v = $version.v;})?
diff --git a/src/main/java/org/caosdb/server/query/Query.java b/src/main/java/org/caosdb/server/query/Query.java
index 1171aa053ceae8a46003a3401954c7c1c347161b..c4a458630456d057ccddf26c48def0e72ccf3789 100644
--- a/src/main/java/org/caosdb/server/query/Query.java
+++ b/src/main/java/org/caosdb/server/query/Query.java
@@ -166,7 +166,8 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
 
   public enum Type {
     FIND,
-    COUNT
+    COUNT,
+    SELECT,
   };
 
   public static final class Pattern {
@@ -736,7 +737,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
   }
   /** Fill entities from `resultSet` into `container`. */
   private void fillContainerWithResult() {
-    if (this.container != null && this.type == Type.FIND) {
+    if (this.container != null && (this.type == Type.FIND || this.type == Type.SELECT)) {
       for (final IdVersionAclTriplet t : this.resultSet) {
 
         final Entity e = new RetrieveEntity(new EntityID(t.id), t.version);
diff --git a/src/test/java/org/caosdb/server/query/TestCQL.java b/src/test/java/org/caosdb/server/query/TestCQL.java
index cfd53f94f305088f04f9a5eb0a2e443ad65f9c6c..fe73e2b2c33749fac6d72a4c5b478eca5d154465 100644
--- a/src/test/java/org/caosdb/server/query/TestCQL.java
+++ b/src/test/java/org/caosdb/server/query/TestCQL.java
@@ -278,6 +278,10 @@ public class TestCQL {
       "SELECT 'name with spaces.and dot', 'name with spaces'.name, name with spaces.name, name with\\,comma and\\.dot and \\'single_quote.sub FROM ENTITY";
   String issue130b = "SELECT 'Wrapper' FROM RECORD TestRT";
 
+  // quotation marks gone rogue
+  String quotation1 =
+      "FIND ENTITY WHICH HAS A PROPERTY LIKE '*with double*' AND A PROPERTY LIKE '*and single*' AND A PROPERTY LIKE '*what\\'s wrong?*' AND A PROPERTY LIKE '*\\'*' AND A PROPERTY LIKE '*nothin\\'*' AND A PROPERTY LIKE '*\"\\'bla*'";
+
   @Test
   public void testQuery1()
       throws InterruptedException, SQLException, ConnectionException, QueryException {
@@ -6082,7 +6086,7 @@ public class TestCQL {
     assertEquals(Query.Pattern.TYPE_NORMAL, sfq.e.type);
     assertEquals("ename", sfq.e.toString());
     assertEquals(Query.Role.RECORD, sfq.r);
-    assertEquals(Query.Type.FIND, sfq.t);
+    assertEquals(Query.Type.SELECT, sfq.t);
     assertNotNull(sfq.s);
     assertFalse(sfq.s.isEmpty());
     assertEquals(1, sfq.s.size());
@@ -6105,7 +6109,7 @@ public class TestCQL {
     assertEquals(Query.Pattern.TYPE_NORMAL, sfq.e.type);
     assertEquals("ename", sfq.e.toString());
     assertEquals(Query.Role.RECORD, sfq.r);
-    assertEquals(Query.Type.FIND, sfq.t);
+    assertEquals(Query.Type.SELECT, sfq.t);
     assertNotNull(sfq.s);
     assertFalse(sfq.s.isEmpty());
     assertEquals(1, sfq.s.size());
@@ -6128,7 +6132,7 @@ public class TestCQL {
     assertEquals(Query.Pattern.TYPE_NORMAL, sfq.e.type);
     assertEquals("ename", sfq.e.toString());
     assertEquals(Query.Role.RECORD, sfq.r);
-    assertEquals(Query.Type.FIND, sfq.t);
+    assertEquals(Query.Type.SELECT, sfq.t);
     assertNotNull(sfq.s);
     assertFalse(sfq.s.isEmpty());
     assertEquals(3, sfq.s.size());
@@ -6156,7 +6160,7 @@ public class TestCQL {
     assertEquals(Query.Pattern.TYPE_NORMAL, sfq.e.type);
     assertEquals("ename", sfq.e.toString());
     assertEquals(Query.Role.RECORD, sfq.r);
-    assertEquals(Query.Type.FIND, sfq.t);
+    assertEquals(Query.Type.SELECT, sfq.t);
     assertNotNull(sfq.s);
     assertFalse(sfq.s.isEmpty());
     assertEquals(4, sfq.s.size());
@@ -6619,7 +6623,7 @@ public class TestCQL {
     assertEquals(Query.Pattern.TYPE_NORMAL, sfq.e.type);
     assertEquals("ename", sfq.e.toString());
     assertNull(sfq.r);
-    assertEquals(Query.Type.FIND, sfq.t);
+    assertEquals(Query.Type.SELECT, sfq.t);
     assertNotNull(sfq.s);
     assertFalse(sfq.s.isEmpty());
     assertEquals(1, sfq.s.size());
@@ -6989,4 +6993,24 @@ public class TestCQL {
     assertEquals("Wrapper", sfq.s.get(0).getSelector());
     assertNull(sfq.s.get(0).getSubselection());
   }
+
+  @Test
+  public void testQuotation1() {
+    CQLLexer lexer;
+    lexer = new CQLLexer(CharStreams.fromString(this.quotation1));
+    final CommonTokenStream tokens = new CommonTokenStream(lexer);
+
+    final CQLParser parser = new CQLParser(tokens);
+    final CqContext sfq = parser.cq();
+
+    assertTrue(sfq.filter instanceof Conjunction);
+    Conjunction conj = (Conjunction) sfq.filter;
+    assertEquals(6, conj.getFilters().size());
+    assertEquals("POV(null,LIKE ,%with double%)", conj.getFilters().get(0).toString());
+    assertEquals("POV(null,LIKE ,%and single%)", conj.getFilters().get(1).toString());
+    assertEquals("POV(null,LIKE ,%what's wrong?%)", conj.getFilters().get(2).toString());
+    assertEquals("POV(null,LIKE ,%'%)", conj.getFilters().get(3).toString());
+    assertEquals("POV(null,LIKE ,%nothin'%)", conj.getFilters().get(4).toString());
+    assertEquals("POV(null,LIKE ,%\"'bla%)", conj.getFilters().get(5).toString());
+  }
 }