diff --git a/com.oracle.truffle.r.engine/hbase-site.xml b/com.oracle.truffle.r.engine/hbase-site.xml
new file mode 100644
index 0000000000000000000000000000000000000000..31dd14dcb2e1e47a491832a98d9ae943d1ca9f2a
--- /dev/null
+++ b/com.oracle.truffle.r.engine/hbase-site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
+<!--
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<configuration>
+  <property>
+    <name>hbase.rootdir</name>
+    <value>file:///home/julien/hbase/root</value>
+  </property>
+  <property>
+    <name>hbase.zookeeper.property.dataDir</name>
+    <value>/home/julien/hbase/root/zookeeper</value>
+  </property>
+</configuration>
diff --git a/com.oracle.truffle.r.engine/oracle.config b/com.oracle.truffle.r.engine/oracle.config
new file mode 100644
index 0000000000000000000000000000000000000000..676cc848c308c0a9303477005ca673d05b3017a8
--- /dev/null
+++ b/com.oracle.truffle.r.engine/oracle.config
@@ -0,0 +1,5 @@
+host = myoracle.com
+sid = myview
+port = 7658
+user = julilope
+passwd = Pa$$w0rd
diff --git a/com.oracle.truffle.r.engine/postgre.config b/com.oracle.truffle.r.engine/postgre.config
new file mode 100644
index 0000000000000000000000000000000000000000..39dd278151eb10c99d66e7b091e5d6c342b355b4
--- /dev/null
+++ b/com.oracle.truffle.r.engine/postgre.config
@@ -0,0 +1,5 @@
+host = localhost
+sid = postgres
+port = 5432
+user = julien
+passwd = Pa$$w0rd
diff --git a/com.oracle.truffle.r.engine/postgre2.config b/com.oracle.truffle.r.engine/postgre2.config
new file mode 100644
index 0000000000000000000000000000000000000000..8ef8ed55fbf64fc1ac673aa5f1ce3e526d6fb55f
--- /dev/null
+++ b/com.oracle.truffle.r.engine/postgre2.config
@@ -0,0 +1,5 @@
+host = localhost
+sid = other
+port = 5432
+user = julien
+passwd = Pa$$w0rd
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RExecute.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RExecute.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b0b2a3201f0ee596019ddc9a1ce691f37b3551b
--- /dev/null
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RExecute.java
@@ -0,0 +1,20 @@
+package com.oracle.truffle.r.engine;
+
+import java.io.Serializable;
+
+import org.apache.hadoop.hive.ql.exec.UDF;
+
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.api.vm.PolyglotEngine.Value;
+import com.oracle.truffle.r.runtime.RRuntime;
+
+public class RExecute extends UDF {
+    public static String evaluate(final String program) {
+        final Value v = PolyglotEngine.newBuilder().config("application/x-r", "REngine", null).build().eval(Source.newBuilder(program).name("RBuilder").mimeType(RRuntime.R_APP_MIME).build());
+        final Object res = v.get();
+        if (res instanceof Serializable)
+            return res.toString();
+        return v.getSourceLocation().getCode();
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RExecuteApply.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RExecuteApply.java
new file mode 100644
index 0000000000000000000000000000000000000000..a9ed0dbe03ee7feb69a4c5443d9cea11295be5e3
--- /dev/null
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RExecuteApply.java
@@ -0,0 +1,12 @@
+package com.oracle.truffle.r.engine;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.hadoop.hive.ql.exec.UDF;
+
+public class RExecuteApply extends UDF {
+    public static String evaluate(final String fun, final List<String> args) {
+        return RExecute.evaluate("f = " + fun + ";f(" + args.stream().map(arg -> arg.toString()).collect(Collectors.joining(", ")) + ")");
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java
index 65adfcfd0f9f825430961e248f74cbcbca055a18..382f3e47880672abb96d540d0f5cfd24fdba1d5b 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java
@@ -22,8 +22,14 @@
  */
 package com.oracle.truffle.r.engine;
 
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Serializable;
+import java.io.StringWriter;
+import java.util.Arrays;
 import java.util.HashMap;
-
+import java.util.Map;
+import java.util.stream.Collectors;
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.TruffleLanguage;
@@ -33,17 +39,22 @@ import com.oracle.truffle.api.instrumentation.ProvidedTags;
 import com.oracle.truffle.api.instrumentation.StandardTags;
 import com.oracle.truffle.api.metadata.ScopeProvider;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.api.vm.PolyglotEngine.Value;
 import com.oracle.truffle.r.engine.interop.RForeignAccessFactoryImpl;
 import com.oracle.truffle.r.nodes.RASTBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinPackages;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags;
+import com.oracle.truffle.r.nodes.qirinterface.QIRInterface;
 import com.oracle.truffle.r.runtime.ExitException;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RAccuracyInfo;
 import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.RValue;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.context.Engine.IncompleteSourceException;
 import com.oracle.truffle.r.runtime.context.Engine.ParseException;
@@ -123,6 +134,7 @@ public final class TruffleRLanguageImpl extends TruffleRLanguage implements Scop
 
     @Override
     protected void disposeContext(RContext context) {
+        QIRInterface.closeDrivers();
         context.destroy();
     }
 
@@ -228,4 +240,72 @@ public final class TruffleRLanguageImpl extends TruffleRLanguage implements Scop
     public AbstractScope findScope(RContext langContext, Node node, Frame frame) {
         return RScope.createScope(node, frame);
     }
+
+    public static final Map<String, RValue> valueCache = new HashMap<>();
+    public static PolyglotEngine vm = null;
+
+    public static final RValue executeR(final String program) {
+        if (vm == null)
+            vm = PolyglotEngine.newBuilder().config("application/x-r", "REngine", null).build();
+        RValue res = valueCache.get(program);
+        if (res != null)
+            return res;
+        try {
+            final Value v = vm.eval(Source.newBuilder(program).name("RBuilder").mimeType(RRuntime.R_APP_MIME).build());
+            final Object o = v.get();
+            if (o instanceof Serializable)
+                res = new RValue((Serializable) o);
+            else
+                res = new RValue(v.getSourceLocation().getCode());
+            valueCache.put(program, res);
+            return res;
+        } catch (Exception e) {
+            StringWriter sw = new StringWriter();
+            PrintWriter pw = new PrintWriter(sw);
+            e.printStackTrace(pw);
+            throw new RuntimeException(sw.toString());
+        }
+    }
+
+    public static final RValue executeApply(final RValue fun, final RValue args[]) {
+        return executeR("f = " + fun.getValue() + "\nf(" + Arrays.stream(args).map(arg -> arg.getValue().toString()).collect(Collectors.joining(", ")) + ")");
+    }
+
+    public static final String executeApply(final String fun, final String args[]) {
+        return executeR("f = " + fun + "\nf(" +
+                        Arrays.stream(args).map(s -> s.equals("TRUE") ? s : s.equals("FALSE") ? s : s.matches("[A-Za-z_].*") ? "\"" + s + "\"" : s).collect(Collectors.joining(", ")) +
+                        ")").getValue().toString();
+    }
+
+    public static final RValue translate(final Integer i) throws IOException {
+        return new RValue(i);
+    }
+
+    public static final RValue translate(final Double d) throws IOException {
+        return new RValue(d);
+    }
+
+    public static final RValue translate(final String s) throws IOException {
+        return new RValue(s);
+    }
+
+    public static final RValue translate(final Boolean b) throws IOException {
+        return new RValue(b);
+    }
+
+    public static final Integer translateBackToInteger(final RValue v) {
+        return (Integer) v.getValue();
+    }
+
+    public static final Double translateBackToDouble(final RValue v) {
+        return (Double) v.getValue();
+    }
+
+    public static final String translateBackToString(final RValue v) {
+        return (String) v.getValue();
+    }
+
+    public static final Boolean translateBackToBoolean(final RValue v) {
+        return (Boolean) v.getValue();
+    }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
index e1c3ca08e3b0899498abbe29882a93dc486d7462..22761219ac9546f0177dac0fba74038d96a6fbb7 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
@@ -117,6 +117,9 @@ import com.oracle.truffle.r.nodes.builtin.fastr.FastRStackTraceNodeGen;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRStats.FastRProfAttr;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRStats.FastRProfFuncounts;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRStats.FastRProfTypecounts;
+import com.oracle.truffle.r.nodes.builtin.query.RForceQueryBuiltin;
+import com.oracle.truffle.r.nodes.builtin.query.RForceQueryBuiltinNodeGen;
+import com.oracle.truffle.r.nodes.builtin.query.RTableBuiltin;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRStatsFactory;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRSyntaxTree;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRSyntaxTreeNodeGen;
@@ -132,6 +135,7 @@ import com.oracle.truffle.r.nodes.builtin.fastr.FastRTry;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRTryNodeGen;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastrDqrls;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastrDqrlsNodeGen;
+import com.oracle.truffle.r.nodes.builtin.query.RTableBuiltinNodeGen;
 import com.oracle.truffle.r.nodes.builtin.fastr.memprof.FastRprofmem;
 import com.oracle.truffle.r.nodes.builtin.fastr.memprof.FastRprofmemNodeGen;
 import com.oracle.truffle.r.nodes.builtin.fastr.memprof.FastRprofmemShow;
@@ -741,6 +745,10 @@ public class BasePackage extends RBuiltinPackage {
         add(Xtfrm.class, XtfrmNodeGen::create);
         add(IsSingle.class, IsSingleNodeGen::create);
 
+        // query
+        add(RForceQueryBuiltin.class, RForceQueryBuiltinNodeGen::create);
+        add(RTableBuiltin.class, RTableBuiltinNodeGen::create);
+
         // infix functions
         add(Subscript.class, SubscriptNodeGen::create, Subscript::special);
         add(Subscript.DefaultBuiltin.class, SubscriptNodeGen::create, Subscript::special);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/AccessField.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/AccessField.java
index 1663b1ff5ace4d4cab3bd9c25712f45d953c911d..c40c9d62e8fd21afbe53ed68cf82510f9f19a082 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/AccessField.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/AccessField.java
@@ -31,6 +31,8 @@ import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.FrameDescriptor;
+import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
@@ -43,12 +45,21 @@ import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.builtins.RSpecialFactory;
+import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 
+import qir.ast.QIRLambda;
+import qir.ast.QIRVariable;
+import qir.ast.data.QIRTcons;
+import qir.ast.data.QIRTdestr;
+import qir.ast.data.QIRTnil;
+import qir.ast.operator.QIRProject;
+
 @NodeChild(value = "arguments", type = RNode[].class)
 abstract class AccessFieldSpecial extends SpecialsUtils.ListFieldSpecialBase {
 
@@ -103,6 +114,14 @@ public abstract class AccessField extends RBuiltinNode.Arg2 {
         if (!invalidAtomicVector.profile(container instanceof RAbstractListVector) && container instanceof RAbstractVector) {
             error.enter();
             throw error(RError.Message.DOLLAR_ATOMIC_VECTORS);
+        } else if (container instanceof REnvironment && ((REnvironment) container).get("queryId") != null) {
+            final Object queryId = ((REnvironment) container).get("queryId");
+            if (queryId != null) {
+                final QIRVariable v = new QIRVariable(null, "t");
+                RContext.queries.set((int) queryId, new QIRProject(getSourceSection(),
+                                new QIRLambda(null, null, v, new QIRTcons(null, field, new QIRTdestr(null, v, field), QIRTnil.instance), new FrameDescriptor()), RContext.queries.get((int) queryId)));
+                return container;
+            }
         }
         return extract.applyAccessField(container, field);
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/query/RForceQueryBuiltin.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/query/RForceQueryBuiltin.java
new file mode 100644
index 0000000000000000000000000000000000000000..c600196a79f340ef4997254f1607c62525c676c1
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/query/RForceQueryBuiltin.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.builtin.query;
+
+import qir.ast.QIRNode;
+
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.qirinterface.QIRInterface;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+
+/**
+ * This builtin takes a query and returns a data frame that are the results of this query.
+ */
+@RBuiltin(name = "query.force", visibility = OFF, kind = PRIMITIVE, parameterNames = {"query"}, behavior = COMPLEX)
+public abstract class RForceQueryBuiltin extends RBuiltinNode.Arg1 {
+    static {
+        Casts.noCasts(RForceQueryBuiltin.class);
+    }
+
+    @TruffleBoundary
+    @Specialization
+    public static final RList force(final REnvironment rQuery) {
+        // Every query is identified by an integer included in the object returned by the query.
+        final Object tmp = rQuery.get("queryId");
+        final int queryId = tmp != null ? (int) tmp : RContext.INVALID_QUERY_ID;
+        if (queryId == RContext.INVALID_QUERY_ID)
+            throw new RuntimeException("Not a query object.");
+
+        // TODO: Handle results given in streaming.
+        QIRNode query = RContext.queries.get(queryId);
+        if (query == null)
+            throw new RuntimeException("Unrecognized or empty query: " + queryId);
+
+        final RList cachedResults = RContext.results.get(queryId);
+        if (cachedResults != null) // The query has been run before.
+            return cachedResults;
+        query = QIRInterface.normalize(query, RContext.envs.get(queryId));
+        RContext.queries.set(queryId, query);
+
+        final Map<String, List<Object>> rawResults = QIRInterface.run(query);
+        // Create the data frame. TODO: Find a simpler way.
+        final String frameCode = rawResults.entrySet().stream().map(
+                        entry -> entry.getKey() + "=c(" +
+                                        String.join(",", entry.getValue().stream().map(x -> x instanceof String ? "\"" + x + "\"" : x.toString()).collect(Collectors.toList())) +
+                                        ")").collect(Collectors.joining("\n")) +
+                        "\ndata.frame(" + String.join(", ", rawResults.keySet()) + ")";
+        final RList queryResults = (RList) RContext.getEngine().parseAndEval(
+                        Source.newBuilder(frameCode).name("ShouldNotExistBuilder").mimeType(RRuntime.R_APP_MIME).build(),
+                        REnvironment.baseEnv().getFrame(), false);
+        RContext.results.set(queryId, queryResults);
+
+        return queryResults;
+    }
+}
\ No newline at end of file
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/query/RTableBuiltin.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/query/RTableBuiltin.java
new file mode 100644
index 0000000000000000000000000000000000000000..59266673342dfe42f1a2e7cc3836bfa7fb81a0a8
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/query/RTableBuiltin.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.builtin.query;
+
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
+import com.oracle.truffle.api.CompilerDirectives.*;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
+
+@RBuiltin(name = "new.tableRef", visibility = OFF, kind = PRIMITIVE, parameterNames = {"tableName", "dbName", "configFile", "schemaName"}, behavior = PURE)
+public abstract class RTableBuiltin extends RBuiltinNode.Arg4 {
+    static {
+        Casts.noCasts(RTableBuiltin.class);
+    }
+
+    @TruffleBoundary
+    @Specialization
+    public static final REnvironment table(final String tableName, final String dbName, final String configFile, final String schemaName) {
+        final REnvironment res = RDataFactory.createNewEnv(null);
+
+        try {
+            res.put("tableName", tableName);
+            res.put("dbName", dbName);
+            res.put("configFile", configFile);
+            res.put("schemaName", schemaName);
+        } catch (PutException e) {
+            e.printStackTrace();
+        }
+        return res;
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java
index 9804249bf44414d431978d3717805b1b4cf7b260..c9b15ec27b5268dd74c9d2c14a40db624595a001 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java
@@ -50,6 +50,15 @@ import com.oracle.truffle.r.nodes.function.RCallSpecialNode;
 import com.oracle.truffle.r.nodes.function.SaveArgumentsNode;
 import com.oracle.truffle.r.nodes.function.WrapDefaultArgumentNode;
 import com.oracle.truffle.r.nodes.function.signature.MissingNode;
+import com.oracle.truffle.r.nodes.query.RFromNode;
+import com.oracle.truffle.r.nodes.query.RGroupNode;
+import com.oracle.truffle.r.nodes.query.RJoinNode;
+import com.oracle.truffle.r.nodes.query.RLeftJoinNode;
+import com.oracle.truffle.r.nodes.query.ROrderNode;
+import com.oracle.truffle.r.nodes.query.RQueryVisitor;
+import com.oracle.truffle.r.nodes.query.RRightJoinNode;
+import com.oracle.truffle.r.nodes.query.RSelectNode;
+import com.oracle.truffle.r.nodes.query.RWhereNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -149,6 +158,51 @@ public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> {
         return RCallSpecialNode.createCall(source, lhs.asRNode(), createSignature(args), createArguments(args));
     }
 
+    @Override
+    public final RSyntaxNode handleQueries(final RSyntaxNode expression) {
+        return expression.accept(RQueryVisitor.instance);
+    }
+
+    @Override
+    public final RSyntaxNode select(final SourceSection source, final RSyntaxNode formatter, final RSyntaxNode child) {
+        return new RSelectNode(source, formatter.asRNode(), child.asRNode());
+    }
+
+    @Override
+    public final RSyntaxNode from(final SourceSection source, final RSyntaxNode child) {
+        return new RFromNode(source, child.asRNode());
+    }
+
+    @Override
+    public final RSyntaxNode where(final SourceSection source, final RSyntaxNode filter, final RSyntaxNode child) {
+        return new RWhereNode(source, filter.asRNode(), child.asRNode());
+    }
+
+    @Override
+    public final RSyntaxNode groupby(final SourceSection source, final RSyntaxNode group, final RSyntaxNode child) {
+        return new RGroupNode(source, group.asRNode(), child.asRNode());
+    }
+
+    @Override
+    public final RSyntaxNode orderby(final SourceSection source, final RSyntaxNode order, final RSyntaxNode asc, final RSyntaxNode child) {
+        return new ROrderNode(source, order.asRNode(), asc.asRNode(), child.asRNode());
+    }
+
+    @Override
+    public final RSyntaxNode join(final SourceSection source, final RSyntaxNode child1, final RSyntaxNode child2, final RSyntaxNode f) {
+        return new RJoinNode(source, f.asRNode(), child1.asRNode(), child2.asRNode());
+    }
+
+    @Override
+    public final RSyntaxNode leftJoin(final SourceSection source, final RSyntaxNode child1, final RSyntaxNode child2, final RSyntaxNode f) {
+        return new RLeftJoinNode(source, f.asRNode(), child1.asRNode(), child2.asRNode());
+    }
+
+    @Override
+    public final RSyntaxNode rightJoin(final SourceSection source, final RSyntaxNode child1, final RSyntaxNode child2, final RSyntaxNode f) {
+        return new RRightJoinNode(source, f.asRNode(), child1.asRNode(), child2.asRNode());
+    }
+
     private static ArgumentsSignature createSignature(List<Argument<RSyntaxNode>> args) {
         String[] argumentNames = args.stream().map(arg -> arg.name).toArray(String[]::new);
         ArgumentsSignature signature = ArgumentsSignature.get(argumentNames);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RSyntaxNodeVisitor.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RSyntaxNodeVisitor.java
new file mode 100644
index 0000000000000000000000000000000000000000..3e59ca741443589897aecd95ec3c58a322fe2743
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RSyntaxNodeVisitor.java
@@ -0,0 +1,121 @@
+package com.oracle.truffle.r.nodes;
+
+import com.oracle.truffle.r.nodes.access.ConstantNode;
+import com.oracle.truffle.r.nodes.access.WriteLocalFrameVariableNode;
+import com.oracle.truffle.r.nodes.access.variables.LookupNode;
+import com.oracle.truffle.r.nodes.builtin.InternalNode;
+import com.oracle.truffle.r.nodes.control.*;
+import com.oracle.truffle.r.nodes.control.IfNode;
+import com.oracle.truffle.r.nodes.function.FunctionExpressionNode;
+import com.oracle.truffle.r.nodes.function.RCallNode;
+import com.oracle.truffle.r.nodes.function.RCallSpecialNode;
+import com.oracle.truffle.r.nodes.function.signature.MissingNode;
+import com.oracle.truffle.r.nodes.query.*;
+import com.oracle.truffle.r.runtime.nodes.IRSyntaxNodeVisitor;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
+
+/**
+ * An interface for visitors for R Truffle nodes.
+ */
+public interface RSyntaxNodeVisitor<T> extends IRSyntaxNodeVisitor<T> {
+    public abstract T visit(final IfNode ifNode);
+
+    public abstract T visit(final WhileNode whileNode);
+
+    public abstract T visit(final ForNode forNode);
+
+    public abstract T visit(final BreakNode b);
+
+    public abstract T visit(final BlockNode block);
+
+    public abstract T visit(final LookupNode var);
+
+    public abstract T visit(final WriteLocalFrameVariableNode var);
+
+    public abstract T visit(final FunctionExpressionNode fun);
+
+    public abstract T visit(final InternalNode b);
+
+    public abstract T visit(final RCallNode callNode);
+
+    public abstract T visit(final RCallSpecialNode callNode);
+
+    public abstract T visit(final ReplacementDispatchNode repl);
+
+    public abstract T visit(final RQIRWrapperNode qir);
+
+    public abstract T visit(final RSelectNode select);
+
+    public abstract T visit(final RFromNode from);
+
+    public abstract T visit(final RWhereNode where);
+
+    public abstract T visit(final RGroupNode group);
+
+    public abstract T visit(final ROrderNode order);
+
+    public abstract T visit(final RJoinNode join);
+
+    public abstract T visit(final RLeftJoinNode join);
+
+    public abstract T visit(final RRightJoinNode join);
+
+    public abstract T visit(final RLimitNode limit);
+
+    public abstract T visit(final ConstantNode cst);
+
+    public abstract T visit(final MissingNode node);
+
+    @Override
+    public default T visit(final RSyntaxNode node) {
+        if (node instanceof IfNode)
+            return visit((IfNode) node);
+        if (node instanceof WhileNode)
+            return visit((WhileNode) node);
+        if (node instanceof ForNode)
+            return visit((ForNode) node);
+        if (node instanceof BreakNode)
+            return visit((BreakNode) node);
+        if (node instanceof BlockNode)
+            return visit((BlockNode) node);
+        if (node instanceof LookupNode)
+            return visit((LookupNode) node);
+        if (node instanceof WriteLocalFrameVariableNode)
+            return visit((WriteLocalFrameVariableNode) node);
+        if (node instanceof FunctionExpressionNode)
+            return visit((FunctionExpressionNode) node);
+        if (node instanceof InternalNode)
+            return visit((InternalNode) node);
+        if (node instanceof RCallNode)
+            return visit((RCallNode) node);
+        if (node instanceof RCallSpecialNode)
+            return visit((RCallSpecialNode) node);
+        if (node instanceof ReplacementDispatchNode)
+            return visit((ReplacementDispatchNode) node);
+        if (node instanceof RQIRWrapperNode)
+            return visit((RQIRWrapperNode) node);
+        if (node instanceof RSelectNode)
+            return visit((RSelectNode) node);
+        if (node instanceof RFromNode)
+            return visit((RFromNode) node);
+        if (node instanceof RWhereNode)
+            return visit((RWhereNode) node);
+        if (node instanceof RGroupNode)
+            return visit((RGroupNode) node);
+        if (node instanceof ROrderNode)
+            return visit((ROrderNode) node);
+        if (node instanceof RJoinNode)
+            return visit((RJoinNode) node);
+        if (node instanceof RLeftJoinNode)
+            return visit((RLeftJoinNode) node);
+        if (node instanceof RRightJoinNode)
+            return visit((RRightJoinNode) node);
+        if (node instanceof RLimitNode)
+            return visit((RLimitNode) node);
+        if (node instanceof ConstantNode)
+            return visit((ConstantNode) node);
+        if (node instanceof MissingNode)
+            return visit((MissingNode) node);
+        throw new RuntimeException("Internal error: unknown node to visit: " + node.getClass() + ".");
+    }
+}
\ No newline at end of file
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteLocalFrameVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteLocalFrameVariableNode.java
index 19b3ab595db32b7160421cf956bfb64aa4e501c5..58f07b278db0aeecb981f7d3c5d9772d348343d0 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteLocalFrameVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteLocalFrameVariableNode.java
@@ -53,7 +53,7 @@ public abstract class WriteLocalFrameVariableNode extends BaseWriteVariableNode
         return WriteLocalFrameVariableNodeGen.create(name, Mode.INVISIBLE, null);
     }
 
-    private final Mode mode;
+    public final Mode mode;
 
     private final ValueProfile storedObjectProfile = ValueProfile.createClassProfile();
     private final BranchProfile invalidateProfile = BranchProfile.create();
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LookupNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LookupNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..358f657156255e6af910516015a176d0f2d1c473
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LookupNode.java
@@ -0,0 +1,50 @@
+package com.oracle.truffle.r.nodes.access.variables;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
+import com.oracle.truffle.r.runtime.nodes.RSourceSectionNode;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
+
+public final class LookupNode extends RSourceSectionNode implements RSyntaxNode, RSyntaxLookup {
+
+    @Child private ReadVariableNode read;
+    @Child private SetVisibilityNode visibility;
+
+    LookupNode(SourceSection sourceSection, ReadVariableNode read) {
+        super(sourceSection);
+        this.read = read;
+    }
+
+    @Override
+    public void voidExecute(VirtualFrame frame) {
+        read.executeInternal(frame, frame);
+    }
+
+    @Override
+    public Object execute(VirtualFrame frame) {
+        return read.executeInternal(frame, frame);
+    }
+
+    @Override
+    public Object visibleExecute(VirtualFrame frame) {
+        if (visibility == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            visibility = insert(SetVisibilityNode.create());
+        }
+        visibility.execute(frame, true);
+        return read.executeInternal(frame, frame);
+    }
+
+    @Override
+    public String getIdentifier() {
+        return read.getIdentifier();
+    }
+
+    @Override
+    public boolean isFunctionLookup() {
+        return read.isFunctionLookup();
+    }
+}
\ No newline at end of file
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
index 338251a3ff4be1790e17ee1d2f8f6361ee9a7ca0..3db82a9b2c9413691c66c98adcb2397232c94c56 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
@@ -48,7 +48,6 @@ import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
-import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RArguments;
@@ -76,51 +75,8 @@ import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor.FrameAndSlo
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor.LookupResult;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor.MultiSlotData;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
-import com.oracle.truffle.r.runtime.nodes.RSourceSectionNode;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
-final class LookupNode extends RSourceSectionNode implements RSyntaxNode, RSyntaxLookup {
-
-    @Child private ReadVariableNode read;
-    @Child private SetVisibilityNode visibility;
-
-    LookupNode(SourceSection sourceSection, ReadVariableNode read) {
-        super(sourceSection);
-        this.read = read;
-    }
-
-    @Override
-    public void voidExecute(VirtualFrame frame) {
-        read.executeInternal(frame, frame);
-    }
-
-    @Override
-    public Object execute(VirtualFrame frame) {
-        return read.executeInternal(frame, frame);
-    }
-
-    @Override
-    public Object visibleExecute(VirtualFrame frame) {
-        if (visibility == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            visibility = insert(SetVisibilityNode.create());
-        }
-        visibility.execute(frame, true);
-        return read.executeInternal(frame, frame);
-    }
-
-    @Override
-    public String getIdentifier() {
-        return read.getIdentifier();
-    }
-
-    @Override
-    public boolean isFunctionLookup() {
-        return read.isFunctionLookup();
-    }
-}
-
 /**
  * This node is used to read a variable from the local or enclosing environments. It specializes to
  * a particular layout of frame descriptors and enclosing environments, and re-specializes in case
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java
index 1205b62c9e8c59c4854ed3b1684773021d5cb882..b66f41e9d5190bc5e5af1e36a33afe0ae0276be7 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java
@@ -99,6 +99,18 @@ public final class ForNode extends AbstractLoopNode implements RSyntaxNode, RSyn
         return RNull.instance;
     }
 
+    public final RSyntaxLookup getVar() {
+        return var;
+    }
+
+    public final RNode getRange() {
+        return writeRangeNode.getRhs();
+    }
+
+    public final RNode getBody() {
+        return ((ForRepeatingNode) loopNode.getRepeatingNode()).body;
+    }
+
     private static final class ForRepeatingNode extends AbstractRepeatingNode {
 
         private final ConditionProfile conditionProfile = ConditionProfile.createCountingProfile();
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementDispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementDispatchNode.java
index 46906aeebc5ce7b0133ff4eef73a250d0baef3a8..719b54a06200ad11ed8fb5afeefcffed841449d4 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementDispatchNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementDispatchNode.java
@@ -55,11 +55,11 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 public final class ReplacementDispatchNode extends OperatorNode {
 
     // these are only @Child to make instrumentation work
-    @Child private RNode lhs;
-    @Child private RNode rhs;
+    @Child public RNode lhs;
+    @Child public RNode rhs;
 
-    private final boolean isSuper;
-    private final int tempNamesStartIndex;
+    public final boolean isSuper;
+    public final int tempNamesStartIndex;
 
     public ReplacementDispatchNode(SourceSection src, RSyntaxLookup operator, RSyntaxNode lhs, RSyntaxNode rhs, boolean isSuper, int tempNamesStartIndex) {
         super(src, operator);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/WhileNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/WhileNode.java
index 98e84c5e61956437f0144286eb04b308d26d9104..b41dc4b0873dd05685e19b53f152348dd592d570 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/WhileNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/WhileNode.java
@@ -56,6 +56,14 @@ public final class WhileNode extends AbstractLoopNode implements RSyntaxNode, RS
         return RNull.instance;
     }
 
+    public RSyntaxNode getCondition() {
+        return ((WhileRepeatingNode) loop.getRepeatingNode()).condition.getRSyntaxNode();
+    }
+
+    public RNode getBody() {
+        return ((WhileRepeatingNode) loop.getRepeatingNode()).body;
+    }
+
     private static final class WhileRepeatingNode extends AbstractRepeatingNode {
 
         @Child private ConvertBooleanNode condition;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
index 2b427df2deb4d73160eed621fee45ebb0ccf1377..cb218d54517a09533e0370ebe6099d4caac134fe 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
@@ -102,7 +102,7 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
      */
     private String name;
     private SourceSection sourceSectionR;
-    private final SourceSection[] argSourceSections;
+    public final SourceSection[] argSourceSections;
 
     @Child private RNode saveArguments;
     @Child private FrameSlotNode onExitSlot;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallSpecialNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallSpecialNode.java
index 98a793a0147a1f032e48fafd264fc5299d5874e0..0783ed2799b0a6a1876cd6e0d6b87b6488f2b123 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallSpecialNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallSpecialNode.java
@@ -130,7 +130,7 @@ public final class RCallSpecialNode extends RCallBaseNode implements RSyntaxNode
 
     private final RSyntaxNode[] arguments;
     private final ArgumentsSignature signature;
-    private final RBuiltinDescriptor expectedFunction;
+    public final RBuiltinDescriptor expectedFunction;
     private final RVisibility visible;
 
     /**
@@ -376,6 +376,10 @@ public final class RCallSpecialNode extends RCallBaseNode implements RSyntaxNode
         return signature == null ? ArgumentsSignature.empty(1) : signature;
     }
 
+    public RNode getFunctionNode() {
+        return functionNode;
+    }
+
     @Override
     public RSyntaxElement[] getSyntaxArguments() {
         return arguments == null ? new RSyntaxElement[]{RSyntaxLookup.createDummyLookup(RSyntaxNode.LAZY_DEPARSE, "...", false)} : arguments;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/qirinterface/QIRInterface.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/qirinterface/QIRInterface.java
new file mode 100644
index 0000000000000000000000000000000000000000..d888147f80fc5d6ce4ba5b9f5e82121f5c5f86d5
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/qirinterface/QIRInterface.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.qirinterface;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import com.oracle.truffle.api.frame.Frame;
+import com.oracle.truffle.api.frame.FrameDescriptor;
+import com.oracle.truffle.api.frame.FrameSlot;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.api.vm.PolyglotEngine.Value;
+import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
+import com.oracle.truffle.r.nodes.function.FunctionExpressionNode;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.data.RPromise;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+
+import qir.ast.*;
+import qir.ast.data.*;
+import qir.ast.expression.*;
+import qir.driver.DBDriver;
+import qir.driver.QIRDriver;
+
+public final class QIRInterface {
+    /**
+     * Runs a query in the database pointed by the given driver and returns the results.
+     *
+     * @param query The query to be executed in the targeted database
+     * @return The results of the query
+     */
+    public static final Map<String, List<Object>> run(final QIRNode query) {
+        // Run the query and retrieve results
+        QIRNode qirRes = QIRDriver.run(query);
+
+        // An error during the evaluation of the query
+        if (qirRes == null)
+            throw new RuntimeException("Could not run query: " + query);
+
+        // The query did not return the right type
+        if (!(qirRes instanceof QIRList))
+            throw new RuntimeException("Internal error: query returned a non-list type");
+
+        // The query returned an empty result
+        if (qirRes == QIRLnil.instance)
+            return new HashMap<>();
+
+        final Map<String, List<Object>> results = new HashMap<>();
+
+        // Inhabit the result columns with empty lists (we need the keys to create the lists) TODO:
+        // Find a way to add the first row here and skip it on the next loop
+        for (QIRNode firstRow = ((QIRLcons) qirRes).value; firstRow != QIRTnil.instance; firstRow = ((QIRTcons) firstRow).tail)
+            results.put(((QIRTcons) firstRow).id, new ArrayList<>());
+
+        // Process the rest of the rows TODO: Error if a row has unknown column
+        for (; qirRes != QIRLnil.instance; qirRes = ((QIRLcons) qirRes).tail)
+            for (QIRNode row = ((QIRLcons) qirRes).value; row != QIRTnil.instance; row = ((QIRTcons) row).tail)
+                results.get(((QIRTcons) row).id).add(QIRToRType(((QIRTcons) row).value));
+
+        return results;
+    }
+
+    /**
+     * Resolves the free variables of a query.
+     *
+     * @param arg The query to be executed in the targeted database
+     * @param argFrame The environment of execution
+     * @return The closed query
+     */
+    public static final QIRNode normalize(final QIRNode arg, final Frame argFrame) {
+        QIRNode query = arg;
+        final SourceSection dummy = Source.newBuilder("").name("QIR interface").mimeType(RRuntime.R_APP_MIME).build().createUnavailableSection();
+
+        for (Map<String, QIRVariable> fvs = QIRDriver.getFreeVars(query); !fvs.isEmpty(); fvs = QIRDriver.getFreeVars(query))
+            for (final QIRVariable fv : fvs.values()) {
+                final String varName = fv.id;
+                Frame frame = argFrame;
+                FrameSlot varSlot = frame.getFrameDescriptor().findFrameSlot(varName);
+                for (; varSlot == null && frame.getArguments()[4] instanceof Frame && ((Frame) frame.getArguments()[4]).getArguments()[4] instanceof Frame; frame = (Frame) frame.getArguments()[4])
+                    varSlot = ((Frame) frame.getArguments()[4]).getFrameDescriptor().findFrameSlot(varName);
+                query = new QIRApply(dummy, new QIRLambda(dummy, null, new QIRVariable(dummy, varName, varSlot), query, new FrameDescriptor()),
+                                varName.equals("interval") ? new QIRExternal(dummy, "interval") : varName.equals("having") ? new QIRExternal(dummy, "having") // TODO:
+                                                                                                                                                              // Remove
+                                                                                                                                                              // this
+                                                                                                                                                              // hack
+                                                : RToQIRType(fv.sourceSection, varSlot != null ? frame.getValue(varSlot) : RContext.getInstance().lookupBuiltin(varName)));
+            }
+        return query;
+    }
+
+    /**
+     * This function closes the connection of database drivers.
+     */
+    public static final void closeDrivers() {
+        DBDriver.closeDrivers();
+    }
+
+    /**
+     * Translates a QIR expression into a R statement.
+     *
+     * @param value The QIR expression to translate
+     * @return The translation of the QIR expression in R
+     * @throws UnsupportedOperationException If the type of the value is not supported.
+     */
+    @SuppressWarnings("unchecked")
+    static final <T> T QIRToRType(final QIRNode value) throws UnsupportedOperationException {
+        if (value instanceof QIRBaseValue<?>)
+            return ((QIRBaseValue<T>) value).value;
+        throw new RuntimeException("Unsupported value: " + value);
+    }
+
+    /**
+     * Translates a R statement into a QIR expression.
+     *
+     * @param src The {@link SourceSection} of the given value
+     * @param value The R statement to translate
+     * @return The translation of the R statement in QIR
+     */
+    static final <T> QIRNode RToQIRType(final SourceSection src, final T value) {
+        if (value instanceof BigInteger)
+            return new QIRBigNumber(src, (BigInteger) value);
+        if (value instanceof Long)
+            return new QIRNumber(src, (Long) value);
+        if (value instanceof Byte)
+            return new QIRNumber(src, (Byte) value);
+        if (value instanceof Double)
+            return new QIRDouble(src, (Double) value);
+        if (value instanceof Boolean)
+            return QIRBoolean.create((Boolean) value);
+        if (value instanceof String)
+            return new QIRString(src, (String) value);
+        if (value instanceof REnvironment) {
+            REnvironment env = (REnvironment) value;
+            final Object queryId = env.get("queryId");
+            if (queryId != null) // The object is a query
+                return normalize(RContext.queries.get((Integer) queryId), RContext.envs.get((Integer) queryId));
+            final String tableName = (String) env.get("tableName");
+            final String schemaName = (String) env.get("schemaName");
+            final String dbName = (String) env.get("dbName");
+            final String configFile = (String) env.get("configFile");
+            if (tableName != null && dbName != null && configFile != null) // The object is a table
+                return new QIRTable(src, new QIRString(null, tableName), new QIRString(null, dbName), new QIRString(null, configFile), new QIRString(null, schemaName));
+            // else, the object is considered a tuple
+            QIRTuple tuple = QIRTnil.instance;
+            for (final Object x : env.getFrame().getFrameDescriptor().getIdentifiers())
+                tuple = new QIRTcons(src, (String) x, RToQIRType(src, env.get((String) x)), tuple);
+            return tuple;
+        }
+        if (value instanceof FunctionExpressionNode) {
+            return RFunctionToQIRType(src, ((FunctionExpressionNode) value).getSyntaxDebugName(), (FunctionDefinitionNode) (((FunctionExpressionNode) value).getCallTarget().getRootNode()));
+        }
+        if (value instanceof RFunction) {
+            final RFunction fun = (RFunction) value;
+            switch (fun.getName()) {
+                case "new.env":
+                    return new QIRLambda(src, "new.env", new QIRVariable(null, "_", null), QIRTnil.instance, new FrameDescriptor());
+                case "return":
+                case "(":
+                case "query.force": {
+                    final QIRVariable x = new QIRVariable(null, "x", null);
+                    return new QIRLambda(src, "identity", x, x, new FrameDescriptor());
+                }
+                case "new.tableRef":
+                    final QIRVariable tableName = new QIRVariable(null, "__tmp__");
+                    final QIRVariable dbName = new QIRVariable(null, "__tmp2__");
+                    final QIRVariable configFile = new QIRVariable(null, "__tmp3__");
+                    final QIRVariable schemaName = new QIRVariable(null, "__tmp4__");
+                    return new QIRLambda(null, null, tableName,
+                                    new QIRLambda(null, null, dbName, new QIRLambda(null, null, configFile,
+                                                    new QIRLambda(null, null, schemaName, new QIRTable(src, tableName, dbName, configFile, schemaName),
+                                                                    new FrameDescriptor()),
+                                                    new FrameDescriptor()),
+                                                    new FrameDescriptor()),
+                                    new FrameDescriptor());
+                case "sum":
+                case "min":
+                case "max":
+                case "date":
+                    return new QIRExternal(src, fun.getName());
+                case "substr":
+                    return new QIRExternal(src, "substring");
+                case "mean":
+                    return new QIRExternal(src, "avg");
+                case "length":
+                    return new QIRExternal(src, "count");
+                case "match":
+                    return new QIRExternal(src, "match");
+                case "format": {
+                    final QIRVariable x = new QIRVariable(null, "x", null);
+                    final QIRVariable y = new QIRVariable(null, "y", null);
+                    return new QIRLambda(null, null, x, new QIRLambda(null, null, y, new QIRApply(null, new QIRExternal(src, "extractYear"), x), new FrameDescriptor()),
+                                    new FrameDescriptor());
+                }
+                case "regexpr":
+                    final QIRVariable x = new QIRVariable(null, "x", null);
+                    final QIRVariable y = new QIRVariable(null, "y", null);
+                    return new QIRLambda(null, null, x, new QIRLambda(null, null, y, new QIRApply(null, new QIRApply(null, new QIRExternal(src, "like"), y), x), new FrameDescriptor()),
+                                    new FrameDescriptor());
+                default:
+                    return RFunctionToQIRType(src, fun.getName(), (FunctionDefinitionNode) fun.getRootNode());
+            }
+        }
+        if (value instanceof RPromise) {
+            final RPromise fun = (RPromise) value;
+            if (fun.isEvaluated())
+                return RToQIRType(src, fun.getValue());
+            return RToQIRType(src, fun.getClosure().eval(fun.getFrame()));
+        }
+        throw new RuntimeException("Unsupported value: " + value);
+    }
+
+    static final QIRNode execute(final String program) {
+        final PolyglotEngine vm = PolyglotEngine.newBuilder().config("application/x-sl", "SLEngine", null).build();
+        final Source source = Source.newBuilder(program).name("mySrc").mimeType(RRuntime.R_APP_MIME).build();
+        final Value v = vm.eval(source);
+
+        if (v == null)
+            throw new RuntimeException("No function main() defined in SL source file." + source.getCode());
+        return RToQIRType(v.getSourceLocation(), v.get());
+    }
+
+    static final QIRTruffleNode apply(final QIRTruffleNode fun, final Object arg) {
+        return new QIRTruffleNode(fun.sourceSection, "r", fun.executeTruffle, fun.apply, fun.code + "(" + arg + ")");
+    }
+
+    private static final QIRNode RFunctionToQIRType(final SourceSection src, final String funName, final FunctionDefinitionNode fun) {
+        try {
+            QIRNode res = ((FunctionDefinitionNode) fun.getCallTarget().getRootNode()).getBody().accept(QIRTranslateVisitor.instance);
+            final String[] args = ((FunctionDefinitionNode) fun.getCallTarget().getRootNode()).getSignature().getNames();
+
+            if (args.length == 0)
+                return new QIRLambda(src, funName, null, res, new FrameDescriptor());
+            for (int i = args.length - 1; i >= 0; i--)
+                res = new QIRLambda(src, funName, new QIRVariable(null, args[i], null), res, new FrameDescriptor());
+            return res;
+        } catch (UnsupportedOperationException e) {
+            final SourceSection funSrc = fun.getCallTarget().getRootNode().getSourceSection();
+            // TODO: Handle dependencies
+            return new QIRTruffleNode(funSrc, "r", QIRInterface::execute, QIRInterface::apply, funSrc.getCode());
+        }
+    }
+}
\ No newline at end of file
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/qirinterface/QIRTranslateVisitor.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/qirinterface/QIRTranslateVisitor.java
new file mode 100644
index 0000000000000000000000000000000000000000..4b4f009e8b8e7bd372cf9ead1577afe498dfabf5
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/qirinterface/QIRTranslateVisitor.java
@@ -0,0 +1,287 @@
+package com.oracle.truffle.r.nodes.qirinterface;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.oracle.truffle.api.frame.FrameDescriptor;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.nodes.RSyntaxNodeVisitor;
+import com.oracle.truffle.r.nodes.access.AccessArgumentNode;
+import com.oracle.truffle.r.nodes.access.ConstantNode;
+import com.oracle.truffle.r.nodes.access.WriteLocalFrameVariableNode;
+import com.oracle.truffle.r.nodes.access.variables.LookupNode;
+import com.oracle.truffle.r.nodes.builtin.InternalNode;
+import com.oracle.truffle.r.nodes.control.*;
+import com.oracle.truffle.r.nodes.function.FunctionExpressionNode;
+import com.oracle.truffle.r.nodes.function.RCallNode;
+import com.oracle.truffle.r.nodes.function.RCallSpecialNode;
+import com.oracle.truffle.r.nodes.function.signature.MissingNode;
+import com.oracle.truffle.r.nodes.query.*;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
+
+import qir.ast.*;
+import qir.ast.data.*;
+import qir.ast.expression.QIRBoolean;
+import qir.ast.expression.QIRNull;
+import qir.ast.expression.QIRNumber;
+import qir.ast.expression.QIRString;
+import qir.ast.expression.arithmetic.*;
+import qir.ast.expression.logic.*;
+import qir.ast.expression.relational.*;
+import qir.ast.operator.*;
+
+/**
+ * This visitor translates a {@link RNode} into a {@link QIRNode}.
+ */
+public final class QIRTranslateVisitor implements RSyntaxNodeVisitor<QIRNode> {
+    public static final QIRTranslateVisitor instance = new QIRTranslateVisitor();
+
+    private QIRTranslateVisitor() {
+    }
+
+    private final SourceSection dummy = Source.newBuilder("").name("QIR translate").mimeType(RRuntime.R_APP_MIME).build().createUnavailableSection();
+
+    @Override
+    public final QIRNode visit(final IfNode ifNode) {
+        return new QIRIf(ifNode.getSourceSection(), ifNode.getCondition().asRSyntaxNode().accept(this), ifNode.getThenPart().asRSyntaxNode().accept(this),
+                        ifNode.getElsePart().asRSyntaxNode().accept(this));
+    }
+
+    @Override
+    public final QIRNode visit(final WhileNode whileNode) {
+        return new QIRTruffleNode(whileNode.getSourceSection(), "r", QIRInterface::execute, QIRInterface::apply, whileNode.getSourceSection().getCode());
+    }
+
+    @Override
+    public final QIRNode visit(final ForNode forNode) {
+        return new QIRTruffleNode(forNode.getSourceSection(), "r", QIRInterface::execute, QIRInterface::apply, forNode.getSourceSection().getCode());
+    }
+
+    @Override
+    public final QIRNode visit(final BreakNode breakNode) {
+        return new QIRTruffleNode(breakNode.getSourceSection(), "r", QIRInterface::execute, QIRInterface::apply, breakNode.getSourceSection().getCode());
+    }
+
+    /**
+     * Translation of a sequence of statements. Note: This is also where the STRAD-ASSIGN rules are
+     * handled.
+     */
+    @Override
+    public final QIRNode visit(final BlockNode block) {
+        QIRNode result = null;
+        final RNode[] children = block.getSequence();
+        final int len = children.length;
+
+        if (len == 0) // Block is empty
+            return QIRNull.instance;
+
+        result = children[len - 1].asRSyntaxNode().accept(this); // The last statement of the block
+        /*
+         * In this loop, we wrap the result with the other statements of the block in reverse order
+         * in the way described by the STRAD-ASSIGN rules.
+         */
+        for (int i = len - 2; i > -1; i--) {
+            final RNode child = children[i];
+            if (child instanceof WriteLocalFrameVariableNode) { // STRAD-ASSIGN-ID
+                final RNode value = ((WriteLocalFrameVariableNode) child).getRhs();
+
+                // If the assignment value reads an argument, then we translate to a lambda.
+                result = new QIRLambda(dummy, null, new QIRVariable(dummy, ((WriteLocalFrameVariableNode) child).getName(), null), result, new FrameDescriptor());
+                // Else we apply STRAD-ASSIGN-ID normally
+                if (!(value instanceof AccessArgumentNode))
+                    result = new QIRApply(dummy, result, value.asRSyntaxNode().accept(this));
+            } else if (child instanceof ReplacementDispatchNode && ((ReplacementDispatchNode) child).lhs instanceof LookupNode) {
+                final RNode value = ((ReplacementDispatchNode) child).rhs;
+
+                // If the assignment value reads an argument, then we translate to a lambda.
+                result = new QIRLambda(dummy, null, new QIRVariable(dummy, ((LookupNode) ((ReplacementDispatchNode) child).lhs).getIdentifier(), null), result, new FrameDescriptor());
+                // Else we apply STRAD-ASSIGN-ID normally
+                if (!(value instanceof AccessArgumentNode))
+                    result = new QIRApply(dummy, result, value.asRSyntaxNode().accept(this));
+            } else if (child instanceof ReplacementDispatchNode) { // STRAD-ASSIGN-FIELD and
+                                                                   // STRAD-ASSIGN-DOT
+                // TODO: Take care of STRAD-ASSIGN-DOT
+                // TODO: RCallSpecialNode is not necessarily a write
+                final ReplacementDispatchNode repl = (ReplacementDispatchNode) child;
+                final RCallSpecialNode lhs = (RCallSpecialNode) repl.lhs;
+                final LookupNode receiver = (LookupNode) lhs.getSyntaxArguments()[0];
+                final ConstantNode eid = (ConstantNode) lhs.getSyntaxArguments()[1];
+                final RNode value = repl.rhs;
+                result = new QIRApply(dummy, new QIRLambda(dummy, null, new QIRVariable(dummy, receiver.getIdentifier(), null), result, new FrameDescriptor()),
+                                new QIRTcons(dummy, (String) eid.getValue(), value.asRSyntaxNode().accept(this), receiver.accept(this)));
+            } else
+                throw new UnsupportedOperationException("Cannot translate \"" + block.getSourceSection() + "\" to QIR: untranslatable sequence.");
+        }
+        return result;
+    }
+
+    @Override
+    public final QIRNode visit(final LookupNode var) {
+        return new QIRVariable(var.getSourceSection(), var.getIdentifier(), null);
+    }
+
+    @Override
+    public final QIRNode visit(final WriteLocalFrameVariableNode var) {
+        // STRAD-ASSIGN-ID should be handled in BlockNode.
+        throw new RuntimeException("Error in translation to QIR: should not have visited an assignment.");
+    }
+
+    @Override
+    public final QIRNode visit(final FunctionExpressionNode fun) {
+        return QIRInterface.RToQIRType(fun.getSourceSection(), fun);
+    }
+
+    @Override
+    public final QIRNode visit(final InternalNode fun) {
+        throw new RuntimeException("Error in translation to QIR: InternalNode unsupported.");
+    }
+
+    @Override
+    public final QIRNode visit(final RCallNode call) {
+        try {
+            return visitBuiltin(call.getSourceSection(), ((LookupNode) call.getFunction()).getIdentifier(),
+                            Arrays.stream(call.getArguments().getArguments()).map(arg -> arg.accept(this)).collect(Collectors.toList()));
+        } catch (final RuntimeException e) {
+        }
+        final QIRNode fun = call.getFunction().asRSyntaxNode().accept(this);
+        final RSyntaxNode[] args = call.getArguments().getArguments();
+        final int nbArgs = args.length;
+        QIRNode res = fun;
+
+        if (nbArgs == 0) // call is an application with no arguments
+            return new QIRApply(dummy, fun, null);
+        for (final RSyntaxNode arg : args)
+            res = new QIRApply(dummy, res, arg.accept(this));
+        return res;
+    }
+
+    @Override
+    public final QIRNode visit(final RCallSpecialNode call) {
+        return visitBuiltin(call.getSourceSection(), call.expectedFunction.getName(),
+                        Arrays.stream(call.getSyntaxArguments()).map(arg -> ((RSyntaxNode) arg).accept(this)).collect(Collectors.toList()));
+    }
+
+    private static final QIRNode visitBuiltin(final SourceSection src, final String name, final List<QIRNode> args) {
+        switch (name) {
+            case "c":
+                QIRList res = QIRLnil.instance;
+                for (int i = args.size() - 1; i >= 0; i--)
+                    res = new QIRLcons(src, args.get(i), res);
+                return res;
+            case "$":
+                return new QIRTdestr(src, args.get(0), ((QIRString) args.get(1)).value);
+            case "+":
+                return QIRPlusNodeGen.create(src, args.get(0), args.get(1));
+            case "-":
+                return QIRMinusNodeGen.create(src, args.get(0), args.get(1));
+            case "*":
+                return QIRStarNodeGen.create(src, args.get(0), args.get(1));
+            case "/":
+                return QIRDivNodeGen.create(src, args.get(0), args.get(1));
+            case "==":
+                return QIREqualNodeGen.create(src, args.get(0), args.get(1));
+            case "!=":
+                return QIRNotNodeGen.create(src, QIREqualNodeGen.create(src, args.get(0), args.get(1)));
+            case "&":
+            case "&&":
+                return QIRAndNodeGen.create(src, args.get(0), args.get(1));
+            case "|":
+            case "||":
+                return QIROrNodeGen.create(src, args.get(0), args.get(1));
+            case "<=":
+                return QIRLowerOrEqualNodeGen.create(src, args.get(0), args.get(1));
+            case "<":
+                return QIRLowerThanNodeGen.create(src, args.get(0), args.get(1));
+            case ">=":
+                return QIRNotNodeGen.create(src, QIRLowerThanNodeGen.create(src, args.get(0), args.get(1)));
+            case ">":
+                return QIRNotNodeGen.create(src, QIRLowerOrEqualNodeGen.create(src, args.get(0), args.get(1)));
+            case "!":
+                return QIRNotNodeGen.create(src, args.get(0));
+            case "(":
+                if (args.size() == 1)
+                    return args.get(0);
+            default:
+                throw new RuntimeException("Unknown call special node: " + name);
+        }
+    }
+
+    @Override
+    public final QIRNode visit(final ReplacementDispatchNode repl) {
+        // STRAD-ASSIGN-FIELD and STRAD-ASSIGN-DOT should be handled in BlockNode.
+        throw new RuntimeException("Error in translation to QIR: should not have visited an assignment.");
+    }
+
+    /**
+     * Translation of a subquery.
+     */
+    @Override
+    public final QIRNode visit(final RQIRWrapperNode query) {
+        return RContext.queries.get(query.id);
+    }
+
+    @Override
+    public final QIRNode visit(final RSelectNode select) {
+        return new QIRProject(select.getSourceSection(), select.formatter.asRSyntaxNode().accept(this), select.query.asRSyntaxNode().accept(this));
+    }
+
+    @Override
+    public final QIRNode visit(final RFromNode from) {
+        return new QIRScan(from.getSourceSection(), from.getTable().asRSyntaxNode().accept(this));
+    }
+
+    @Override
+    public final QIRNode visit(final RWhereNode where) {
+        return new QIRFilter(where.getSourceSection(), where.getFilter().asRSyntaxNode().accept(this), where.getQuery().asRSyntaxNode().accept(this));
+    }
+
+    @Override
+    public final QIRNode visit(final RGroupNode group) {
+        return new QIRGroupBy(group.getSourceSection(), group.getGroup().asRSyntaxNode().accept(this), group.getQuery().asRSyntaxNode().accept(this));
+    }
+
+    @Override
+    public final QIRNode visit(final ROrderNode order) {
+        QIRNode f = order.getIsAscending().asRSyntaxNode().accept(this);
+
+        for (QIRNode l = ((QIRLambda) f).body; l instanceof QIRLcons; l = ((QIRLcons) l).tail)
+            ((QIRLcons) l).value = QIRBoolean.create(((QIRLcons) l).value.equals(new QIRNumber(null, 1)));
+        return new QIRSortBy(order.getSourceSection(), order.getOrder().asRSyntaxNode().accept(this), f,
+                        order.getQuery().asRSyntaxNode().accept(this));
+    }
+
+    @Override
+    public final QIRNode visit(final RJoinNode join) {
+        return new QIRJoin(join.getSourceSection(), join.getFilter().asRSyntaxNode().accept(this), join.getLeft().asRSyntaxNode().accept(this), join.getRight().asRSyntaxNode().accept(this));
+    }
+
+    @Override
+    public final QIRNode visit(final RLeftJoinNode join) {
+        return new QIRLeftJoin(join.getSourceSection(), join.getFilter().asRSyntaxNode().accept(this), join.getLeft().asRSyntaxNode().accept(this), join.getRight().asRSyntaxNode().accept(this));
+    }
+
+    @Override
+    public final QIRNode visit(final RRightJoinNode join) {
+        return new QIRRightJoin(join.getSourceSection(), join.getFilter().asRSyntaxNode().accept(this), join.getLeft().asRSyntaxNode().accept(this), join.getRight().asRSyntaxNode().accept(this));
+    }
+
+    @Override
+    public final QIRNode visit(final RLimitNode limit) {
+        return new QIRLimit(limit.getSourceSection(), limit.getLimit().asRSyntaxNode().accept(this), limit.getQuery().asRSyntaxNode().accept(this));
+    }
+
+    @Override
+    public final QIRNode visit(final ConstantNode cst) {
+        return QIRInterface.RToQIRType(cst.getSourceSection(), cst.getValue());
+    }
+
+    @Override
+    public final QIRNode visit(final MissingNode node) {
+        throw new RuntimeException("Cannot translate MissingNode to QIR.");
+    }
+}
\ No newline at end of file
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RFromNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RFromNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..fb2bb93612deccc8565e21d9a7cc5593971c970b
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RFromNode.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.query;
+
+import com.oracle.truffle.api.nodes.NodeInfo;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+
+@NodeInfo(shortName = "From")
+public final class RFromNode extends RQueryNode {
+    @Child private RNode table;
+
+    public RFromNode(final SourceSection src, final RNode table) {
+        super(src);
+        this.table = table;
+    }
+
+    public final RNode getTable() {
+        return table;
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RGroupNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RGroupNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..4cdbb523b3613c25370860ff64cd936c9942ea66
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RGroupNode.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.query;
+
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+
+@NodeInfo(shortName = "GroupBy")
+public final class RGroupNode extends RQueryNode {
+    @Child private RNode group;
+    @Child private RNode query;
+
+    public RGroupNode(final SourceSection src, final RNode group, final RNode query) {
+        super(src);
+        this.group = group;
+        this.query = query;
+    }
+
+    public final RNode getGroup() {
+        return group;
+    }
+
+    public final RNode getQuery() {
+        return query;
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RJoinNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RJoinNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..8aedec3a3b4a48b251a3ff7dc064081307e11273
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RJoinNode.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.query;
+
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+
+@NodeInfo(shortName = "Join")
+public final class RJoinNode extends RQueryNode {
+    @Child private RNode filter;
+    @Child private RNode left;
+    @Child private RNode right;
+
+    public RJoinNode(final SourceSection src, final RNode filter, final RNode left, final RNode right) {
+        super(src);
+        this.filter = filter;
+        this.left = left;
+        this.right = right;
+    }
+
+    public final RNode getFilter() {
+        return filter;
+    }
+
+    public final RNode getLeft() {
+        return left;
+    }
+
+    public final RNode getRight() {
+        return right;
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RLeftJoinNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RLeftJoinNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..797eacb8714b8bea0628bb6537f273cafd6e0a42
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RLeftJoinNode.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.query;
+
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+
+@NodeInfo(shortName = "Join")
+public final class RLeftJoinNode extends RQueryNode {
+    @Child private RNode filter;
+    @Child private RNode left;
+    @Child private RNode right;
+
+    public RLeftJoinNode(final SourceSection src, final RNode filter, final RNode left, final RNode right) {
+        super(src);
+        this.filter = filter;
+        this.left = left;
+        this.right = right;
+    }
+
+    public final RNode getFilter() {
+        return filter;
+    }
+
+    public final RNode getLeft() {
+        return left;
+    }
+
+    public final RNode getRight() {
+        return right;
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RLimitNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RLimitNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..1fe4d66f4fdbbdf821319d6d196e05d9fce80b11
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RLimitNode.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.query;
+
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+
+@NodeInfo(shortName = "Limit")
+public final class RLimitNode extends RQueryNode {
+    @Child private RNode limit;
+    @Child private RNode query;
+
+    public RLimitNode(final SourceSection src, final RNode limit, final RNode query) {
+        super(src);
+        this.limit = limit;
+        this.query = query;
+    }
+
+    public final RNode getLimit() {
+        return limit;
+    }
+
+    public final RNode getQuery() {
+        return query;
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/ROrderNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/ROrderNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..b6a50d4d1b060e6f7e5c378ea4981ac14b1b7091
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/ROrderNode.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.query;
+
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+
+@NodeInfo(shortName = "OrderBy")
+public final class ROrderNode extends RQueryNode {
+    @Child private RNode order;
+    @Child private RNode isAscending;
+    @Child private RNode query;
+
+    public ROrderNode(final SourceSection src, final RNode order, final RNode isAscending, final RNode query) {
+        super(src);
+        this.order = order;
+        this.isAscending = isAscending;
+        this.query = query;
+    }
+
+    public final RNode getOrder() {
+        return order;
+    }
+
+    public final RNode getIsAscending() {
+        return isAscending;
+    }
+
+    public final RNode getQuery() {
+        return query;
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RQIRWrapperNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RQIRWrapperNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..31d954defdac50402052c378ac0a7cb0bdbb36e4
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RQIRWrapperNode.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.query;
+
+import com.oracle.truffle.api.CompilerDirectives.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.ArgumentsSignature;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
+import com.oracle.truffle.r.runtime.nodes.RSourceSectionNode;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxFunction;
+
+@NodeInfo(shortName = "query", description = "The node representing a query")
+public final class RQIRWrapperNode extends RSourceSectionNode implements RSyntaxFunction {
+    // The unique identifier of the query
+    public final int id;
+
+    public RQIRWrapperNode(final SourceSection src) {
+        super(src);
+        this.id = RContext.createFreshQuery();
+    }
+
+    @Override
+    public final REnvironment execute(VirtualFrame frame) {
+        final int instanceId = RContext.envs.get(id) == null ? id : RContext.createFreshQueryFrom(id);
+        RContext.envs.set(instanceId, frame);
+        return createQuery(instanceId);
+    }
+
+    @TruffleBoundary
+    private static final REnvironment createQuery(int instanceId) {
+        final REnvironment res = RDataFactory.createNewEnv(null);
+        try {
+            res.put("queryId", instanceId);
+        } catch (PutException e) {
+            e.printStackTrace();
+            return null;
+        }
+        return res;
+    }
+
+    @Override
+    public ArgumentsSignature getSyntaxSignature() {
+        return null;
+    }
+
+    @Override
+    public RSyntaxElement[] getSyntaxArgumentDefaults() {
+        return new RSyntaxElement[0];
+    }
+
+    @Override
+    public RSyntaxElement getSyntaxBody() {
+        return this;
+    }
+
+    @Override
+    public String getSyntaxDebugName() {
+        return getSourceSection().getCode();
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RQueryNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RQueryNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..2697e2f79f472f4914011975a1bdedc8941b876a
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RQueryNode.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.query;
+
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.NodeInfo;
+import com.oracle.truffle.api.object.DynamicObject;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.nodes.RSourceSectionNode;
+
+@NodeInfo(shortName = "Query")
+public abstract class RQueryNode extends RSourceSectionNode {
+    public RQueryNode(final SourceSection src) {
+        super(src);
+    }
+
+    @Override
+    public final DynamicObject execute(final VirtualFrame frame) {
+        throw new RuntimeException("We should not execute a RQueryNode directly.");
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RQueryVisitor.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RQueryVisitor.java
new file mode 100644
index 0000000000000000000000000000000000000000..a0fcc33f2020d5fa84059531bd73cb397fb476f6
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RQueryVisitor.java
@@ -0,0 +1,179 @@
+package com.oracle.truffle.r.nodes.query;
+
+import java.util.Arrays;
+
+import com.oracle.truffle.r.nodes.RSyntaxNodeVisitor;
+import com.oracle.truffle.r.nodes.access.ConstantNode;
+import com.oracle.truffle.r.nodes.access.WriteLocalFrameVariableNode;
+import com.oracle.truffle.r.nodes.access.variables.LookupNode;
+import com.oracle.truffle.r.nodes.builtin.InternalNode;
+import com.oracle.truffle.r.nodes.control.*;
+import com.oracle.truffle.r.nodes.function.FunctionExpressionNode;
+import com.oracle.truffle.r.nodes.function.RCallNode;
+import com.oracle.truffle.r.nodes.function.RCallSpecialNode;
+import com.oracle.truffle.r.nodes.function.signature.MissingNode;
+import com.oracle.truffle.r.nodes.qirinterface.QIRTranslateVisitor;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
+
+import qir.ast.QIRNode;
+
+/**
+ * This visitor detects queries in a R statement and calls {@link QIRTranslateVisitor} to translate
+ * them into {@link QIRNode}.
+ */
+public final class RQueryVisitor implements RSyntaxNodeVisitor<RSyntaxNode> {
+    public static final RQueryVisitor instance = new RQueryVisitor();
+
+    private RQueryVisitor() {
+    }
+
+    @Override
+    public final RSyntaxNode visit(final IfNode ifNode) {
+        return new IfNode(ifNode.getSourceSection(), ifNode.getSyntaxLHS(), ifNode.getCondition().asRSyntaxNode().accept(this), ifNode.getThenPart().asRSyntaxNode().accept(this),
+                        ifNode.getElsePart() == null ? null : ifNode.getElsePart().asRSyntaxNode().accept(this));
+    }
+
+    @Override
+    public final RSyntaxNode visit(final WhileNode whileNode) {
+        return new WhileNode(whileNode.getSourceSection(), whileNode.getSyntaxLHS(), whileNode.getCondition().accept(this), whileNode.getBody().asRSyntaxNode().accept(this));
+    }
+
+    @Override
+    public final RSyntaxNode visit(final ForNode forNode) {
+        final RNode range = forNode.getRange();
+        return range == null ? forNode : new ForNode(forNode.getSourceSection(), forNode.getSyntaxLHS(), forNode.getVar(), range.asRSyntaxNode().accept(this).asRNode(),
+                        forNode.getBody().asRSyntaxNode().accept(this).asRNode());
+    }
+
+    @Override
+    public final RSyntaxNode visit(final BreakNode breakNode) {
+        return breakNode;
+    }
+
+    @Override
+    public final RSyntaxNode visit(final BlockNode block) {
+        final RNode[] children = block.getSequence();
+
+        for (int i = 0; i < children.length; i++)
+            children[i] = children[i].asRSyntaxNode().accept(this).asRNode();
+        return block;
+    }
+
+    @Override
+    public final RSyntaxNode visit(final LookupNode var) {
+        return var;
+    }
+
+    @Override
+    public final RSyntaxNode visit(final WriteLocalFrameVariableNode var) {
+        return WriteLocalFrameVariableNode.create(var.getName(), var.mode, var.getRhs().asRSyntaxNode().accept(this).asRNode()).asRSyntaxNode();
+    }
+
+    @Override
+    public final RSyntaxNode visit(final FunctionExpressionNode fun) {
+        return fun;
+    }
+
+    @Override
+    public final RSyntaxNode visit(final InternalNode fun) {
+        return fun;
+    }
+
+    @Override
+    public final RSyntaxNode visit(final RCallNode call) {
+        return RCallNode.createCall(call.getSourceSection(), call.getFunction().asRSyntaxNode().accept(this).asRNode(), call.getSyntaxSignature(),
+                        Arrays.asList(call.getArguments().getArguments()).stream().map(arg -> arg.accept(this)).toArray(RSyntaxNode[]::new));
+    }
+
+    @Override
+    public final RSyntaxNode visit(final RCallSpecialNode call) {
+        return RCallSpecialNode.createCall(call.getSourceSection(), call.getFunctionNode().asRSyntaxNode().accept(this).asRNode(), call.getSyntaxSignature(),
+                        Arrays.asList((RSyntaxNode[]) call.getSyntaxArguments()).stream().map(arg -> arg.accept(this)).toArray(RSyntaxNode[]::new));
+    }
+
+    @Override
+    public final RSyntaxNode visit(final ReplacementDispatchNode repl) {
+        return new ReplacementDispatchNode(repl.getSourceSection(), repl.getSyntaxLHS(), repl.lhs.asRSyntaxNode().accept(this), repl.rhs.asRSyntaxNode().accept(this), repl.isSuper,
+                        repl.tempNamesStartIndex);
+    }
+
+    @Override
+    public final RSyntaxNode visit(final RQIRWrapperNode qir) {
+        return qir;
+    }
+
+    @Override
+    public final RSyntaxNode visit(final RSelectNode select) {
+        final RQIRWrapperNode res = new RQIRWrapperNode(select.getSourceSection());
+        RContext.queries.set(res.id, select.accept(QIRTranslateVisitor.instance));
+        return res;
+    }
+
+    @Override
+    public final RSyntaxNode visit(final RFromNode from) {
+        final RQIRWrapperNode res = new RQIRWrapperNode(from.getSourceSection());
+        RContext.queries.set(res.id, from.accept(QIRTranslateVisitor.instance));
+        return res;
+    }
+
+    @Override
+    public final RSyntaxNode visit(final RWhereNode where) {
+        final RQIRWrapperNode res = new RQIRWrapperNode(where.getSourceSection());
+        RContext.queries.set(res.id, where.accept(QIRTranslateVisitor.instance));
+        return res;
+    }
+
+    @Override
+    public final RSyntaxNode visit(final RGroupNode group) {
+        final RQIRWrapperNode res = new RQIRWrapperNode(group.getSourceSection());
+        RContext.queries.set(res.id, group.accept(QIRTranslateVisitor.instance));
+        return res;
+    }
+
+    @Override
+    public final RSyntaxNode visit(final ROrderNode order) {
+        final RQIRWrapperNode res = new RQIRWrapperNode(order.getSourceSection());
+        RContext.queries.set(res.id, order.accept(QIRTranslateVisitor.instance));
+        return res;
+    }
+
+    @Override
+    public final RSyntaxNode visit(final RJoinNode join) {
+        final RQIRWrapperNode res = new RQIRWrapperNode(join.getSourceSection());
+        RContext.queries.set(res.id, join.accept(QIRTranslateVisitor.instance));
+        return res;
+    }
+
+    @Override
+    public final RSyntaxNode visit(final RLeftJoinNode join) {
+        final RQIRWrapperNode res = new RQIRWrapperNode(join.getSourceSection());
+        RContext.queries.set(res.id, join.accept(QIRTranslateVisitor.instance));
+        return res;
+    }
+
+    @Override
+    public final RSyntaxNode visit(final RRightJoinNode join) {
+        final RQIRWrapperNode res = new RQIRWrapperNode(join.getSourceSection());
+        RContext.queries.set(res.id, join.accept(QIRTranslateVisitor.instance));
+        return res;
+    }
+
+    @Override
+    public final RSyntaxNode visit(final RLimitNode limit) {
+        final RQIRWrapperNode res = new RQIRWrapperNode(limit.getSourceSection());
+        RContext.queries.set(res.id, limit.accept(QIRTranslateVisitor.instance));
+        return res;
+    }
+
+    @Override
+    public final RSyntaxNode visit(final ConstantNode cst) {
+        return ConstantNode.create(cst.getSourceSection(), cst.getValue());
+    }
+
+    @Override
+    public final RSyntaxNode visit(final MissingNode node) {
+        return node;
+    }
+}
\ No newline at end of file
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RRightJoinNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RRightJoinNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..8c5e290e2ea9d15c9e5a4aa14e4ec5474ae9707d
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RRightJoinNode.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.query;
+
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+
+@NodeInfo(shortName = "Join")
+public final class RRightJoinNode extends RQueryNode {
+    @Child private RNode filter;
+    @Child private RNode left;
+    @Child private RNode right;
+
+    public RRightJoinNode(final SourceSection src, final RNode filter, final RNode left, final RNode right) {
+        super(src);
+        this.filter = filter;
+        this.left = left;
+        this.right = right;
+    }
+
+    public final RNode getFilter() {
+        return filter;
+    }
+
+    public final RNode getLeft() {
+        return left;
+    }
+
+    public final RNode getRight() {
+        return right;
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RSelectNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RSelectNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..cea4032ec106f81aaf506a5a0fce97cbb7e99dc2
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RSelectNode.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.query;
+
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+
+@NodeInfo(shortName = "Select")
+public final class RSelectNode extends RQueryNode {
+    @Child public RNode formatter;
+    @Child public RNode query;
+
+    public RSelectNode(final SourceSection src, final RNode formatter, final RNode query) {
+        super(src);
+        this.formatter = formatter;
+        this.query = query;
+    }
+
+    public final RNode getFormatter() {
+        return formatter;
+    }
+
+    public final RNode getQuery() {
+        return query;
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RWhereNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RWhereNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..386cc2343711272e292c77bc063a550c7fe6f5d0
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/query/RWhereNode.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.query;
+
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+
+@NodeInfo(shortName = "Where")
+public final class RWhereNode extends RQueryNode {
+    @Child private RNode filter;
+    @Child private RNode query;
+
+    public RWhereNode(final SourceSection src, final RNode filter, final RNode query) {
+        super(src);
+        this.filter = filter;
+        this.query = query;
+    }
+
+    public final RNode getFilter() {
+        return filter;
+    }
+
+    public final RNode getQuery() {
+        return query;
+    }
+}
diff --git a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g
index bc0af11a7ac5e645ae0f930cdceb4b535ae59d9a..0eac340fe02739f7edcdfcf838eaabcff4a8e361 100644
--- a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g
+++ b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g
@@ -262,7 +262,7 @@ script returns [List<T> v]
         	throw new RecognitionException(input); 
         }
     }
-    : n_ ( s=statement { $v.add($s.v); })*
+    : n_ ( s=statement { $v.add(builder.handleQueries($s.v)); })*
     ;
     
 root_function [String name] returns [RootCallTarget v]
@@ -275,7 +275,7 @@ root_function [String name] returns [RootCallTarget v]
         	throw RInternalError.shouldNotReachHere("not at EOF after parsing deserialized function"); 
         }
     }
-    : n_ op=FUNCTION n_ LPAR  n_ (par_decl[params] (n_ COMMA n_ par_decl[params])* n_)? RPAR n_ body=expr_or_assign { $v = builder.rootFunction(language, src($op, last()), params, $body.v, name); }
+    : n_ op=FUNCTION n_ LPAR  n_ (par_decl[params] (n_ COMMA n_ par_decl[params])* n_)? RPAR n_ body=expr_or_assign { $v = builder.rootFunction(language, src($op, last()), params, builder.handleQueries($body.v), name); }
     ;
 
 statement returns [T v]
@@ -356,7 +356,7 @@ repeat_expr returns [T v]
 
 function [T assignedTo] returns [T v]
     @init { List<Argument<T>> params = new ArrayList<>(); }
-    : op=FUNCTION n_ LPAR  n_ (par_decl[params] (n_ COMMA n_ par_decl[params])* n_)? RPAR n_ body=expr_or_assign { $v = builder.function(language, src($op, last()), params, $body.v, assignedTo); }
+    : op=FUNCTION n_ LPAR  n_ (par_decl[params] (n_ COMMA n_ par_decl[params])* n_)? RPAR n_ body=expr_or_assign { $v = builder.function(language, src($op, last()), params, builder.handleQueries($body.v), assignedTo); }
     ;
 
 par_decl [List<Argument<T>> l]
@@ -501,7 +501,19 @@ simple_expr returns [T v]
     | op=LPAR n_ ea=expr_or_assign n_ y=RPAR    { $v = builder.call(src($op, $y), operator($op), $ea.v); }
     | s=sequence                                { $v = $s.v; }
     | e=expr_wo_assign                          { $v = $e.v; }
+    | q=query                          			{ $v = $q.v; }
     ;
+    
+query returns [T v]
+	: op=SELECT n_ LPAR n_ formatter=expr n_ COMMA n_ child=expr end=RPAR { $v = builder.select(src($op, $end), $formatter.v, $child.v); }
+	| op=FROM n_ LPAR n_ child=expr end=RPAR { $v = builder.from(src($op, $end), $child.v); }
+	| op=WHERE n_ LPAR n_ filter=expr n_ COMMA n_ child=expr end=RPAR { $v = builder.where(src($op, $end), $filter.v, $child.v); }
+	| op=GROUP n_ LPAR n_ group=expr n_ COMMA n_ child=expr end=RPAR { $v = builder.groupby(src($op, $end), $group.v, $child.v); }
+	| op=ORDER n_ LPAR n_ order=expr n_ COMMA n_ asc=expr COMMA n_ child=expr end=RPAR { $v = builder.orderby(src($op, $end), $order.v, $asc.v, $child.v); }
+	| op=JOIN n_ LPAR n_ child1=expr n_ COMMA n_ child2=expr COMMA n_ f=expr end=RPAR { $v = builder.join(src($op, $end), $child1.v, $child2.v, $f.v); }
+	| op=LJOIN n_ LPAR n_ child1=expr n_ COMMA n_ child2=expr COMMA n_ f=expr end=RPAR { $v = builder.leftJoin(src($op, $end), $child1.v, $child2.v, $f.v); }	
+	| op=RJOIN n_ LPAR n_ child1=expr n_ COMMA n_ child2=expr COMMA n_ f=expr end=RPAR { $v = builder.rightJoin(src($op, $end), $child1.v, $child2.v, $f.v); }
+	;
 
 number returns [T v]
     : i=INTEGER {
@@ -656,6 +668,14 @@ IF     : 'if' ;
 ELSE   : 'else' ;
 NEXT   : 'next' ;
 BREAK  : 'break' ;
+SELECT : 'query.select' ;
+FROM : 'query.from' ;
+WHERE : 'query.where' ;
+GROUP : 'query.group' ;
+ORDER : 'query.order' ;
+JOIN : 'query.join' ;
+LJOIN : 'query.leftJoin' ;
+RJOIN : 'query.rightJoin' ;
 
 WS      : ('\u0009'|'\u0020'|'\u00A0') { $channel=HIDDEN; } ;
 NEWLINE : LINE_BREAK { if(incompleteNesting > 0) $channel=HIDDEN; } ;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/LazyResourceHandlerFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/LazyResourceHandlerFactory.java
index 649c8a0e75539d024400ba2656fed85353fe6ad6..8a4d0411ca3e27e61f7f1f3e537f332a81d0fa3f 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/LazyResourceHandlerFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/LazyResourceHandlerFactory.java
@@ -24,18 +24,22 @@ package com.oracle.truffle.r.runtime;
 
 import java.io.BufferedReader;
 import java.io.File;
+import java.io.FilePermission;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.net.MalformedURLException;
 import java.net.URL;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.security.CodeSource;
+import java.security.Permissions;
+import java.security.ProtectionDomain;
+import java.security.cert.Certificate;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
-
 import com.oracle.truffle.r.runtime.ResourceHandlerFactory.Handler;
 
 /**
@@ -58,9 +62,21 @@ class LazyResourceHandlerFactory extends ResourceHandlerFactory implements Handl
         return this;
     }
 
+    public static final ProtectionDomain getHack() {
+        Certificate[] certs = null;
+        Permissions permissions = new Permissions();
+        permissions.add(new FilePermission("file:/home/julien/eclipse/workspace/R.jar", "read"));
+        permissions.add(new FilePermission("file:/home/julien/eclipse/workspace/phd/fastr", "read"));
+        try {
+            return new ProtectionDomain(new CodeSource(new URL("file:/home/julien/eclipse/workspace/R.jar"), certs), permissions);
+        } catch (MalformedURLException e1) {
+            throw new RuntimeException("Bad domain");
+        }
+    }
+
     @Override
     public Map<String, String> getRFiles(Class<?> accessor, String pkgName) {
-        CodeSource source = accessor.getProtectionDomain().getCodeSource();
+        CodeSource source = getHack().getCodeSource();
         Map<String, String> result = new HashMap<>();
         try {
             URL url = source.getLocation();
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java
index 232be3ac2c2b6d253c73b978cd4af0a56b4b8fee..7fc6adfb489daaed45702ccd49b5db62b783c134 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java
@@ -177,12 +177,14 @@ public final class REnvVars implements RContext.ContextState {
     public static String rHome() {
         if (rHome == null) {
             rHome = System.getenv(R_HOME);
+            rHome = "/home/julien/eclipse/workspace/phd/fastr";
             Path rHomePath;
             if (rHome == null) {
                 rHomePath = getRHomePath();
             } else {
                 rHomePath = Paths.get(rHome);
             }
+            rHomePath = Paths.get(rHome);
             if (!validateRHome(rHomePath, markerFile())) {
                 Utils.rSuicide("R_HOME is not set correctly");
             }
@@ -191,7 +193,7 @@ public final class REnvVars implements RContext.ContextState {
         return rHome;
     }
 
-    private static CodeSource codeSource = REnvVars.class.getProtectionDomain().getCodeSource();
+    private static CodeSource codeSource = LazyResourceHandlerFactory.getHack().getCodeSource();
 
     /**
      * In the case where {@code R_HOME} is not set, which should only occur when FastR is invoked
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RValue.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..84f0ccb7fdc653648264e7511b4ed8ade3899ffb
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RValue.java
@@ -0,0 +1,59 @@
+package com.oracle.truffle.r.runtime;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.sql.SQLData;
+import java.sql.SQLException;
+import java.sql.SQLInput;
+import java.sql.SQLOutput;
+import java.util.Base64;
+
+public class RValue implements SQLData {
+    private Serializable value = null;
+    private String base64 = null;
+    private String typeName = null;
+
+    public RValue() {
+    }
+
+    public RValue(Serializable value) throws IOException {
+        this.value = value;
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream(baos);
+        oos.writeObject(value);
+        oos.close();
+        base64 = Base64.getEncoder().encodeToString(baos.toByteArray());
+    }
+
+    public final Serializable getValue() {
+        return this.value;
+    }
+
+    @Override
+    public final String getSQLTypeName() throws SQLException {
+        return this.typeName;
+    }
+
+    @Override
+    public final void readSQL(SQLInput stream, String type) throws SQLException {
+        base64 = stream.readString();
+        byte[] data = Base64.getDecoder().decode(base64);
+        try {
+            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
+            value = (Serializable) ois.readObject();
+            ois.close();
+            typeName = type;
+        } catch (IOException | ClassNotFoundException e) {
+            throw new RuntimeException(e.getMessage());
+        }
+    }
+
+    @Override
+    public final void writeSQL(SQLOutput stream) throws SQLException {
+        stream.writeString(base64);
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
index e229eb4c45359b8f129193e759f89db870ad906b..5a94cb1380634e9d42b98d7c91fbe30eeee9d416 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
@@ -22,6 +22,7 @@
  */
 package com.oracle.truffle.r.runtime.context;
 
+import java.util.ArrayList;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.io.IOException;
@@ -39,7 +40,6 @@ import java.nio.charset.CodingErrorAction;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.List;
@@ -58,6 +58,7 @@ import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.TruffleContext;
 import com.oracle.truffle.api.TruffleLanguage;
 import com.oracle.truffle.api.TruffleLanguage.Env;
+import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.instrumentation.AllocationReporter;
 import com.oracle.truffle.api.instrumentation.Instrumenter;
 import com.oracle.truffle.api.interop.ForeignAccess;
@@ -93,6 +94,7 @@ import com.oracle.truffle.r.runtime.conn.ConnectionSupport;
 import com.oracle.truffle.r.runtime.conn.StdConnections;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RTruffleObject;
 import com.oracle.truffle.r.runtime.env.REnvironment;
@@ -103,6 +105,8 @@ import com.oracle.truffle.r.runtime.nodes.RCodeBuilder;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 import com.oracle.truffle.r.runtime.rng.RRNG;
 
+import qir.ast.QIRNode;
+
 /**
  * Encapsulates the runtime state ("context") of an R session. All access to that state from the
  * implementation <b>must</b> go through this class. There can be multiple instances
@@ -121,6 +125,38 @@ import com.oracle.truffle.r.runtime.rng.RRNG;
  * Contexts can be destroyed
  */
 public final class RContext implements RTruffleObject {
+    private static int queryId = 0;
+    public static final int INVALID_QUERY_ID = -1;
+    // Static representation of queries.
+    public static final List<QIRNode> queries = new ArrayList<>();
+    public static final List<VirtualFrame> envs = new ArrayList<>();
+    // A query returns a vector of tuples. A tuple is a map from a column name to a value.
+    public static final List<RList> results = new ArrayList<>();
+
+    /**
+     * Returns a fresh identifier for a query.
+     *
+     * @return A unique query identifier
+     */
+    public static int createFreshQuery() {
+        queries.add(null);
+        envs.add(null);
+        results.add(null);
+        return queryId++;
+    }
+
+    /**
+     * Returns a fresh identifier for the given query.
+     *
+     * @param id The identifier of the query.
+     * @return A unique query identifier
+     */
+    public static int createFreshQueryFrom(int id) {
+        queries.add(queries.get(id));
+        envs.add(null);
+        results.add(null);
+        return queryId++;
+    }
 
     public static final int CONSOLE_WIDTH = 80;
 
@@ -291,6 +327,8 @@ public final class RContext implements RTruffleObject {
      */
     private static boolean embedded;
 
+    private static boolean deepEmbedded;
+
     /*
      * Workarounds to finesse project circularities between runtime/nodes.
      */
@@ -371,6 +409,15 @@ public final class RContext implements RTruffleObject {
         return embedded;
     }
 
+    public static void setDeepEmbedded() {
+        deepEmbedded = true;
+        embedded = true;
+    }
+
+    public static boolean isDeepEmbedded() {
+        return deepEmbedded;
+    }
+
     /**
      * Sets the fields that do not depend on complex initialization.
      *
@@ -487,7 +534,9 @@ public final class RContext implements RTruffleObject {
         stateDLL.initialize(this);
         stateRFFI = RFFIFactory.getInstance().newContextState();
         // separate in case initialize calls getStateRFFI()!
-        stateRFFI.initialize(this);
+
+        if (!deepEmbedded)
+            stateRFFI.initialize(this);
 
         if (!embedded) {
             doEnvOptionsProfileInitialization();
@@ -497,7 +546,8 @@ public final class RContext implements RTruffleObject {
         stateRErrorHandling.initialize(this);
         stateRConnection.initialize(this);
         stateStdConnections.initialize(this);
-        stateRNG.initialize(this);
+        if (!deepEmbedded)
+            stateRNG.initialize(this);
         stateRSerialize.initialize(this);
         stateLazyDBCache.initialize(this);
         stateInstrumentation.initialize(this);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/IRSyntaxNodeVisitor.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/IRSyntaxNodeVisitor.java
new file mode 100644
index 0000000000000000000000000000000000000000..1b175cb2dccbacfaff8b9cbccd7114f473777ed6
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/IRSyntaxNodeVisitor.java
@@ -0,0 +1,5 @@
+package com.oracle.truffle.r.runtime.nodes;
+
+public interface IRSyntaxNodeVisitor<T> {
+    public abstract T visit(final RSyntaxNode node);
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RCodeBuilder.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RCodeBuilder.java
index 3eaed47f55b88159cd36daa8df6e9feac6ecac1d..8fcd0952d9242aa7c254c254d48d719c99a0e8d6 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RCodeBuilder.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RCodeBuilder.java
@@ -96,6 +96,51 @@ public interface RCodeBuilder<T> {
      */
     T call(SourceSection source, T lhs, List<Argument<T>> arguments);
 
+    /**
+     * Processes queries in an expression to make them ready for evaluation.
+     */
+    T handleQueries(final T expression);
+
+    /**
+     * Creates a select query.
+     */
+    T select(final SourceSection source, final T formatter, final T child);
+
+    /**
+     * Creates a from query.
+     */
+    T from(final SourceSection source, final T child);
+
+    /**
+     * Creates a where query.
+     */
+    T where(final SourceSection source, final T filter, final T child);
+
+    /**
+     * Creates a group by query.
+     */
+    T groupby(final SourceSection source, final T group, final T child);
+
+    /**
+     * Creates an order by query.
+     */
+    T orderby(final SourceSection source, final T order, final T asc, final T child);
+
+    /**
+     * Creates a join query.
+     */
+    T join(final SourceSection source, final T child1, final T child2, final T f);
+
+    /**
+     * Creates a left join query.
+     */
+    T leftJoin(final SourceSection source, final T child1, final T child2, final T f);
+
+    /**
+     * Creates a right join query.
+     */
+    T rightJoin(final SourceSection source, final T child1, final T child2, final T f);
+
     /**
      * Creates a constant, the value is expected to be one of FastR's scalar types (byte, int,
      * double, RComplex, String, RNull).
@@ -157,6 +202,8 @@ public interface RCodeBuilder<T> {
 
             @Override
             protected T visit(RSyntaxFunction element) {
+                if (element.getSyntaxBody() == element)
+                    return (T) element;
                 ArrayList<Argument<T>> params = createArguments(element.getSyntaxSignature(), element.getSyntaxArgumentDefaults());
                 return function(RContext.getInstance().getLanguage(), element.getLazySourceSection(), params, accept(element.getSyntaxBody()), element.getSyntaxDebugName());
             }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxNode.java
index 6cd0441293d5f54cdf1c5fa586872a1dc7fb769d..237803da857b684bfbc1b2f33f907c232bf164ce 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxNode.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxNode.java
@@ -97,6 +97,10 @@ public interface RSyntaxNode extends RSyntaxElement {
      */
     SourceSection LAZY_DEPARSE = RSource.createUnknown("lazy deparse");
 
+    public default <T> T accept(final IRSyntaxNodeVisitor<T> visitor) {
+        return visitor.visit(this);
+    }
+
     static boolean isInternal(SourceSection sourceSection) {
         if (sourceSection == RSyntaxNode.INTERNAL) {
             return true;
diff --git a/com.oracle.truffle.r.test/oracle.config b/com.oracle.truffle.r.test/oracle.config
new file mode 100644
index 0000000000000000000000000000000000000000..676cc848c308c0a9303477005ca673d05b3017a8
--- /dev/null
+++ b/com.oracle.truffle.r.test/oracle.config
@@ -0,0 +1,5 @@
+host = myoracle.com
+sid = myview
+port = 7658
+user = julilope
+passwd = Pa$$w0rd
diff --git a/com.oracle.truffle.r.test/postgre.config b/com.oracle.truffle.r.test/postgre.config
new file mode 100644
index 0000000000000000000000000000000000000000..39dd278151eb10c99d66e7b091e5d6c342b355b4
--- /dev/null
+++ b/com.oracle.truffle.r.test/postgre.config
@@ -0,0 +1,5 @@
+host = localhost
+sid = postgres
+port = 5432
+user = julien
+passwd = Pa$$w0rd
diff --git a/com.oracle.truffle.r.test/postgre2.config b/com.oracle.truffle.r.test/postgre2.config
new file mode 100644
index 0000000000000000000000000000000000000000..8ef8ed55fbf64fc1ac673aa5f1ce3e526d6fb55f
--- /dev/null
+++ b/com.oracle.truffle.r.test/postgre2.config
@@ -0,0 +1,5 @@
+host = localhost
+sid = other
+port = 5432
+user = julien
+passwd = Pa$$w0rd
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/RTestSuite.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/RTestSuite.java
new file mode 100644
index 0000000000000000000000000000000000000000..52734b9528e3223e862314c5b7d126eaa2ed2934
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/RTestSuite.java
@@ -0,0 +1,62 @@
+package com.oracle.truffle.r.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.oracle.truffle.r.launcher.RscriptCommand;
+
+/**
+ * This class runs FastR on every R file it can find in given directories (it will search
+ * recursively).
+ */
+public final class RTestSuite {
+    private static final String defaultTestsDirectory = "tests";
+
+    public static final void main(String[] args) {
+        final AtomicInteger successfulTests = new AtomicInteger(0);
+        final AtomicInteger totalTests = new AtomicInteger(0);
+
+        Arrays.asList(args.length == 0 ? new String[]{defaultTestsDirectory} : args).stream().map(
+                        dir -> {
+                            try {
+                                return Files.find(Paths.get(dir), 50,
+                                                (p, bfa) -> (p.toString().endsWith(".R") || p.toString().endsWith(".r")) && bfa.isRegularFile() && !p.toString().startsWith("tests/tpch/"));
+                            } catch (IOException e) {
+                                e.printStackTrace();
+                                return null;
+                            }
+                        }).flatMap(x -> x).forEach(file -> {
+                            System.out.println("Testing: " + file);
+                            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                            final String expected;
+                            try {
+                                expected = String.join("\n", Files.readAllLines(Paths.get(file.toString().replaceFirst("\\.[Rr]", ".out"))));
+                                RscriptCommand.doMain(new String[]{"Rscript", file.toString()}, null, System.in, new PrintStream(baos), System.err);
+                                if (expected.equals(baos.toString()))
+                                    successfulTests.incrementAndGet();
+                                else {
+                                    System.out.println("[FAILED] Expected: ");
+                                    System.out.println(expected);
+                                    System.out.println("got: ");
+                                    System.out.println(baos.toString());
+                                }
+                            } catch (IOException e) {
+                                System.err.println("[FAILED] Could not find expected output.");
+                            }
+                            System.out.flush();
+                            System.err.flush();
+                            try {
+                                Thread.sleep(10);
+                            } catch (InterruptedException e) {
+                                e.printStackTrace();
+                            }
+                            totalTests.incrementAndGet();
+                        });
+        System.out.println("Passed " + successfulTests + "/" + totalTests + " tests.");
+    }
+}
diff --git a/com.oracle.truffle.r.test/tests/QueryJoinCrossDatabases.R b/com.oracle.truffle.r.test/tests/QueryJoinCrossDatabases.R
new file mode 100644
index 0000000000000000000000000000000000000000..37f46f7c340c09b00a943e9a7e705988d202d6f4
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/QueryJoinCrossDatabases.R
@@ -0,0 +1,10 @@
+emp = new.tableRef("emp", "PostgreSQL", "postgre.config", "public")
+dept = new.tableRef("dept", "HBase", "hbase-site.xml", "default")
+q = query.select(function (x) {
+             res = new.env()
+             res$ename = x$ename
+             res },
+    query.where(function (x) x$loc == "NEW YORK",
+    query.join(query.from(emp), query.from(dept), function (x,y) x$deptno == y$deptno)))
+results = query.force(q)
+print(results)
diff --git a/com.oracle.truffle.r.test/tests/QueryJoinCrossDatabases.out b/com.oracle.truffle.r.test/tests/QueryJoinCrossDatabases.out
new file mode 100644
index 0000000000000000000000000000000000000000..cbe7579f1f501521493744679959003df4419b5f
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/QueryJoinCrossDatabases.out
@@ -0,0 +1,5 @@
+   ename
+1  CLARK
+2   KING
+3 MILLER
+
diff --git a/com.oracle.truffle.r.test/tests/hbase/QueryGroup.R b/com.oracle.truffle.r.test/tests/hbase/QueryGroup.R
new file mode 100644
index 0000000000000000000000000000000000000000..af7ff70b113d59d629dff64be81ccaaa1e48089d
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/hbase/QueryGroup.R
@@ -0,0 +1,10 @@
+emp = new.tableRef("emp", "HBase", "hbase-site.xml", "default")
+q = query.group(function (x) c(x$deptno),
+    query.select(function (x) {
+             res = new.env()
+             res$deptno = x$deptno
+             res$sumSal = sum(x$sal)
+             res },
+    query.from(emp)))
+results = query.force(q)
+print(results)
diff --git a/com.oracle.truffle.r.test/tests/hbase/QueryGroup.out b/com.oracle.truffle.r.test/tests/hbase/QueryGroup.out
new file mode 100644
index 0000000000000000000000000000000000000000..5739114066855bf44b32225bcdd22826843a82cd
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/hbase/QueryGroup.out
@@ -0,0 +1,5 @@
+  sumsal deptno
+1   4047      1
+2  13356      3
+3  15810      2
+
diff --git a/com.oracle.truffle.r.test/tests/hbase/QuerySimple.R b/com.oracle.truffle.r.test/tests/hbase/QuerySimple.R
new file mode 100644
index 0000000000000000000000000000000000000000..2424aee7cdb6604157798340bf30e850f4f9d8bd
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/hbase/QuerySimple.R
@@ -0,0 +1,8 @@
+emp = new.tableRef("emp", "HBase", "hbase-site.xml", "default")
+q = query.select(function (x) {
+             res = new.env()
+             res$ename = x$ename
+             res },
+    query.from(emp))
+results = query.force(q)
+print(results)
diff --git a/com.oracle.truffle.r.test/tests/hbase/QuerySimple.out b/com.oracle.truffle.r.test/tests/hbase/QuerySimple.out
new file mode 100644
index 0000000000000000000000000000000000000000..fede24703d03399465a1ff3bac5db28040aed622
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/hbase/QuerySimple.out
@@ -0,0 +1,16 @@
+    ename
+1   SMITH
+2  TURNER
+3   ADAMS
+4   JAMES
+5    FORD
+6  MILLER
+7   ALLEN
+8    WARD
+9   JONES
+10 MARTIN
+11  BLAKE
+12  CLARK
+13  SCOTT
+14   KING
+
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QueryCallFunction.R b/com.oracle.truffle.r.test/tests/pgsql/QueryCallFunction.R
new file mode 100644
index 0000000000000000000000000000000000000000..86909b7734dee6150f91b1d492093e48d0bf1b36
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QueryCallFunction.R
@@ -0,0 +1,14 @@
+dol_to_euro = function (dol) dol * 89.0 / 100.0
+
+emp = new.tableRef("emp", "PostgreSQL", "postgre.config", "public")
+minsalary = 2500.0
+q = query.select(function (x) {
+             res = new.env()
+             res$empno = x$empno
+             res$ename = x$ename
+             res$salary = dol_to_euro(x$sal)
+             res },
+    query.where(function (x) x$sal >= minsalary,
+    query.from(emp)))
+results = query.force(q)
+print(results)
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QueryCallFunction.out b/com.oracle.truffle.r.test/tests/pgsql/QueryCallFunction.out
new file mode 100644
index 0000000000000000000000000000000000000000..e8791d46bbef34fc6421899df046ef917b8db886
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QueryCallFunction.out
@@ -0,0 +1,7 @@
+  ename empno  salary
+1 SMITH     1 2225.00
+2  WARD     3 4645.80
+3 JONES     4 3115.00
+4 SCOTT     8 3769.15
+5 ADAMS    11 4450.00
+
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QueryCallFunctionWithDeps.R b/com.oracle.truffle.r.test/tests/pgsql/QueryCallFunctionWithDeps.R
new file mode 100644
index 0000000000000000000000000000000000000000..8f2f0297aefff9f701d66e9b7c31a6981cefc46a
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QueryCallFunctionWithDeps.R
@@ -0,0 +1,15 @@
+dol_to_euro = function (dol) dol * 89.0 / 100.0
+dol_to_euro2 = dol_to_euro
+
+emp = new.tableRef("emp", "PostgreSQL", "postgre.config", "public")
+minsalary = 2500.0
+q = query.select(function (x) {
+             res = new.env()
+             res$empno = x$empno
+             res$ename = x$ename
+             res$salary = dol_to_euro2(x$sal)
+             res },
+    query.where(function (x) x$sal >= minsalary,
+    query.from(emp)))
+results = query.force(q)
+print(results)
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QueryCallFunctionWithDeps.out b/com.oracle.truffle.r.test/tests/pgsql/QueryCallFunctionWithDeps.out
new file mode 100644
index 0000000000000000000000000000000000000000..e8791d46bbef34fc6421899df046ef917b8db886
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QueryCallFunctionWithDeps.out
@@ -0,0 +1,7 @@
+  ename empno  salary
+1 SMITH     1 2225.00
+2  WARD     3 4645.80
+3 JONES     4 3115.00
+4 SCOTT     8 3769.15
+5 ADAMS    11 4450.00
+
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QueryGroup.R b/com.oracle.truffle.r.test/tests/pgsql/QueryGroup.R
new file mode 100644
index 0000000000000000000000000000000000000000..403e66eb84b0fcf37faeb16c0bfbbc2cdc0751e9
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QueryGroup.R
@@ -0,0 +1,10 @@
+emp = new.tableRef("emp", "PostgreSQL", "postgre.config", "public")
+q = query.group(function (x) c(x$deptno),
+    query.select(function (x) {
+             res = new.env()
+             res$deptno = x$deptno
+             res$sumSal = sum(x$sal)
+             res },
+    query.from(emp)))
+results = query.force(q)
+print(results)
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QueryGroup.out b/com.oracle.truffle.r.test/tests/pgsql/QueryGroup.out
new file mode 100644
index 0000000000000000000000000000000000000000..5739114066855bf44b32225bcdd22826843a82cd
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QueryGroup.out
@@ -0,0 +1,5 @@
+  sumsal deptno
+1   4047      1
+2  13356      3
+3  15810      2
+
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QueryInline.R b/com.oracle.truffle.r.test/tests/pgsql/QueryInline.R
new file mode 100644
index 0000000000000000000000000000000000000000..593c5bd06f535d7842de0518b2d68c4061d65259
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QueryInline.R
@@ -0,0 +1,7 @@
+q = query.select(function (x) {
+             res = new.env()
+             res$ename = x$ename
+             res },
+    query.from(new.tableRef("emp", "PostgreSQL", "postgre.config", "public")))
+results = query.force(q)
+print(results)
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QueryInline.out b/com.oracle.truffle.r.test/tests/pgsql/QueryInline.out
new file mode 100644
index 0000000000000000000000000000000000000000..356dee918520abf13a9eae9c5862667d60f3f32b
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QueryInline.out
@@ -0,0 +1,16 @@
+    ename
+1   SMITH
+2   ALLEN
+3    WARD
+4   JONES
+5  MARTIN
+6   BLAKE
+7   CLARK
+8   SCOTT
+9    KING
+10 TURNER
+11  ADAMS
+12  JAMES
+13   FORD
+14 MILLER
+
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QueryInlineWithFreeVars.R b/com.oracle.truffle.r.test/tests/pgsql/QueryInlineWithFreeVars.R
new file mode 100644
index 0000000000000000000000000000000000000000..f479ee11dad2924d24265b420436a00da3fe2bd5
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QueryInlineWithFreeVars.R
@@ -0,0 +1,11 @@
+tableName = "emp"
+dbName = "PostgreSQL"
+configFile = "postgre.config"
+schemaName = "public"
+q = query.select(function (x) {
+             res = new.env()
+             res$ename = x$ename
+             res },
+    query.from(new.tableRef("emp", "PostgreSQL", "postgre.config", "public")))
+results = query.force(q)
+print(results)
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QueryInlineWithFreeVars.out b/com.oracle.truffle.r.test/tests/pgsql/QueryInlineWithFreeVars.out
new file mode 100644
index 0000000000000000000000000000000000000000..356dee918520abf13a9eae9c5862667d60f3f32b
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QueryInlineWithFreeVars.out
@@ -0,0 +1,16 @@
+    ename
+1   SMITH
+2   ALLEN
+3    WARD
+4   JONES
+5  MARTIN
+6   BLAKE
+7   CLARK
+8   SCOTT
+9    KING
+10 TURNER
+11  ADAMS
+12  JAMES
+13   FORD
+14 MILLER
+
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QueryJoin.R b/com.oracle.truffle.r.test/tests/pgsql/QueryJoin.R
new file mode 100644
index 0000000000000000000000000000000000000000..fdc09299ceaf6c6d10e4deb7e178c39139c4bd73
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QueryJoin.R
@@ -0,0 +1,10 @@
+emp = new.tableRef("emp", "PostgreSQL", "postgre.config", "public")
+dept = new.tableRef("dept", "PostgreSQL", "postgre.config", "public")
+q = query.select(function (x) {
+             res = new.env()
+             res$ename = x$ename
+             res },
+    query.where(function (x) x$loc == "NEW YORK",
+    query.join(query.from(emp), query.from(dept), function (x,y) x$deptno == y$deptno)))
+results = query.force(q)
+print(results)
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QueryJoin.out b/com.oracle.truffle.r.test/tests/pgsql/QueryJoin.out
new file mode 100644
index 0000000000000000000000000000000000000000..cbe7579f1f501521493744679959003df4419b5f
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QueryJoin.out
@@ -0,0 +1,5 @@
+   ename
+1  CLARK
+2   KING
+3 MILLER
+
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QueryJoinCrossDatabases.R b/com.oracle.truffle.r.test/tests/pgsql/QueryJoinCrossDatabases.R
new file mode 100644
index 0000000000000000000000000000000000000000..6fe629b7af40051082be0703f64e2307a89072cb
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QueryJoinCrossDatabases.R
@@ -0,0 +1,10 @@
+emp = new.tableRef("emp", "PostgreSQL", "postgre.config", "public")
+dept = new.tableRef("dept", "PostgreSQL", "postgre2.config", "public")
+q = query.select(function (x) {
+             res = new.env()
+             res$ename = x$ename
+             res },
+    query.where(function (x) x$loc == "NEW YORK",
+    query.join(query.from(emp), query.from(dept), function (x,y) x$deptno == y$deptno)))
+results = query.force(q)
+print(results)
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QueryJoinCrossDatabases.out b/com.oracle.truffle.r.test/tests/pgsql/QueryJoinCrossDatabases.out
new file mode 100644
index 0000000000000000000000000000000000000000..8952963704068ed8a5ebee42f41e93e8193281db
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QueryJoinCrossDatabases.out
@@ -0,0 +1,8 @@
+   ename
+1  SMITH
+2   WARD
+3  JONES
+4 MARTIN
+5  JAMES
+6   FORD
+
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QueryLambda.R b/com.oracle.truffle.r.test/tests/pgsql/QueryLambda.R
new file mode 100644
index 0000000000000000000000000000000000000000..85e7be39c434aebd3d94bc0d8b8e6c75ce9cea51
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QueryLambda.R
@@ -0,0 +1,12 @@
+emp = new.tableRef("emp", "PostgreSQL", "postgre.config", "public")
+minsalary = 2500.0
+q = query.select(function (x) {
+             res = new.env()
+             res$empno = x$empno
+             res$ename = x$ename
+             res$salary = (function (dol) dol * 89.0 / 100.0)(x$sal)
+             res },
+    query.where(function (x) x$sal >= minsalary,
+    query.from(emp)))
+results = query.force(q)
+print(results)
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QueryLambda.out b/com.oracle.truffle.r.test/tests/pgsql/QueryLambda.out
new file mode 100644
index 0000000000000000000000000000000000000000..e8791d46bbef34fc6421899df046ef917b8db886
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QueryLambda.out
@@ -0,0 +1,7 @@
+  ename empno  salary
+1 SMITH     1 2225.00
+2  WARD     3 4645.80
+3 JONES     4 3115.00
+4 SCOTT     8 3769.15
+5 ADAMS    11 4450.00
+
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QueryNested.R b/com.oracle.truffle.r.test/tests/pgsql/QueryNested.R
new file mode 100644
index 0000000000000000000000000000000000000000..4ddc544bb0cdb9936fa4f27fe534b595f953e28a
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QueryNested.R
@@ -0,0 +1,25 @@
+# Returns the exchange rate between rfrom and rto
+getRate = function(rfrom, rto)
+{
+  change = new.tableRef("change", "PostgreSQL", "postgre.config", "public")
+  rate = query.force(query.where(function (r) r$cfrom == rfrom && r$cto == rto,
+                     query.from(change)))
+  if (rfrom == rto) 1.0 else rate$change
+}
+
+# Returns the names of employees earning at least minSalary in the curr
+# currency
+atLeast = function(minSalary, curr)
+{
+  emp = new.tableRef("emp", "PostgreSQL", "postgre.config", "public")
+  query.select(function (e) { r = new.env()
+                        r$name = e$ename
+                        r },
+  query.where(function (e) e$sal >= minSalary * getRate("USD", curr),
+  query.from(emp)))
+}
+
+richUSPeople = atLeast(2000.0, "USD")
+richEURPeople = atLeast(2000.0, "EUR")
+print(query.force(richUSPeople))
+print(query.force(richEURPeople))
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QueryNested.out b/com.oracle.truffle.r.test/tests/pgsql/QueryNested.out
new file mode 100644
index 0000000000000000000000000000000000000000..53da7150c82f6978dfcb1dec73b98d72aa03d3cf
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QueryNested.out
@@ -0,0 +1,18 @@
+    name
+1  SMITH
+2  ALLEN
+3   WARD
+4  JONES
+5  SCOTT
+6  ADAMS
+7 MILLER
+    name
+1  SMITH
+2  ALLEN
+3   WARD
+4  JONES
+5  SCOTT
+6  ADAMS
+7   FORD
+8 MILLER
+
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QueryPartial.R b/com.oracle.truffle.r.test/tests/pgsql/QueryPartial.R
new file mode 100644
index 0000000000000000000000000000000000000000..869b0f9e2893e13f3322bdd96a0c602a826325e9
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QueryPartial.R
@@ -0,0 +1,13 @@
+movie = new.tableRef("movie", "PostgreSQL", "postgre.config", "public")
+
+filter = function (collection)
+  query.where(function (line) line$year < 1990 && line$year > 1985, collection)
+
+results = query.select(function (movie) {
+                          res = new.env()
+			  res$title = movie$title
+			  res },
+	  filter(
+          query.from(movie)))
+
+print(query.force(results))
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QueryPartial.out b/com.oracle.truffle.r.test/tests/pgsql/QueryPartial.out
new file mode 100644
index 0000000000000000000000000000000000000000..f4be23850b819e07ea7887f35e4da29fda591f65
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QueryPartial.out
@@ -0,0 +1,89 @@
+                                            title
+1  The Naked Gun: From the Files of Police Squad!
+2                                        Scrooged
+3                                      Parenthood
+4                          The Accidental Tourist
+5                              The Little Mermaid
+6                                The Untouchables
+7                               Castle in the Sky
+8                                        Heathers
+9                              Dangerous Liaisons
+10                          Good Morning, Vietnam
+11                                     Spaceballs
+12                        When Harry Met Sally...
+13                                            Big
+14                                        Tin Men
+15                               The Last Emperor
+16                                         Batman
+17                             The Princess Bride
+18                               Crocodile Dundee
+19                                   Working Girl
+20                                       Hoosiers
+21                                   Tango & Cash
+22                                    Bull Durham
+23                     Throw Momma from the Train
+24                              Casualties of War
+25                                       9½ Weeks
+26                                   Major League
+27                                     Uncle Buck
+28                  Tucker: The Man and His Dream
+29                                Ghostbusters II
+30                           The War of the Roses
+31                                 At Close Range
+32                                        Platoon
+33                                 Broadcast News
+34                       Sex, Lies, and Videotape
+35                            A Fish Called Wanda
+36                                   My Left Foot
+37                               Fatal Attraction
+38                       Children of a Lesser God
+39                                       Ironweed
+40                         Little Shop of Horrors
+41                                     Lean on Me
+42                                    Wall Street
+43                        Crimes and Misdemeanors
+44                                 Something Wild
+45                                    Blue Velvet
+46                             Dead Poets Society
+47                             Do the Right Thing
+48                  The Last Temptation of Christ
+49                   Planes, Trains & Automobiles
+50                                          Glory
+51                                     Young Guns
+52                                Say Anything...
+53                                       Die Hard
+54                         Hannah and Her Sisters
+55                            Crimes of the Heart
+56                                Tequila Sunrise
+57                              Full Metal Jacket
+58                     Born on the Fourth of July
+59                                Raising Arizona
+60                                     Moonstruck
+61                       Ferris Bueller's Day Off
+62                                     Radio Days
+63                                        Top Gun
+64          National Lampoon's Christmas Vacation
+65                                  Lethal Weapon
+66                                    Beetlejuice
+67                             My Neighbor Totoro
+68               Bill & Ted's Excellent Adventure
+69                                    The Accused
+70                                       Predator
+71                        The Fabulous Baker Boys
+72                                 ¡Three Amigos!
+73                             The Color of Money
+74                      The Great Mouse Detective
+75                             Driving Miss Daisy
+76                                 Less Than Zero
+77              The Unbearable Lightness of Being
+78             Indiana Jones and the Last Crusade
+79                       Honey, I Shrunk the Kids
+80                           The Land Before Time
+81                            Mississippi Burning
+82                     Back to the Future Part II
+83                                        The Fly
+84                                Field of Dreams
+85                                       Rain Man
+86                        Who Framed Roger Rabbit
+87                                  Another Woman
+
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QuerySideEffect.R b/com.oracle.truffle.r.test/tests/pgsql/QuerySideEffect.R
new file mode 100644
index 0000000000000000000000000000000000000000..3ddb0e6a3337befbf1b53902e1e94cd957178dd8
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QuerySideEffect.R
@@ -0,0 +1,16 @@
+emp = new.tableRef("emp", "PostgreSQL", "postgre.config", "public")
+minsalary = 2500.0
+q = query.select(function (x) {
+             res = new.env()
+             res$empno = x$empno
+             res$ename = x$ename
+             res$salary = (function (dol){
+               a = dol * 89.0 / 100.0
+               while (a > 1000.0) a = a * 89.0 / 100.0
+               a
+             })(x$sal)
+             res },
+    query.where(function (x) x$sal >= minsalary,
+    query.from(emp)))
+results = query.force(q)
+print(results)
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QuerySideEffect.out b/com.oracle.truffle.r.test/tests/pgsql/QuerySideEffect.out
new file mode 100644
index 0000000000000000000000000000000000000000..ae30189ca424eb38b87f5bc998d4383ccd07a1c2
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QuerySideEffect.out
@@ -0,0 +1,7 @@
+  ename empno            salary
+1 SMITH     1 984.1472014255202
+2  WARD     3 908.9094161734163
+3 JONES     4  971.310575818447
+4 SCOTT     8 930.9438795980075
+5 ADAMS    11 978.2054933202209
+
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QuerySimple.R b/com.oracle.truffle.r.test/tests/pgsql/QuerySimple.R
new file mode 100644
index 0000000000000000000000000000000000000000..832f3de143155ccddd61dc45d8a993fe34e07b8d
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QuerySimple.R
@@ -0,0 +1,8 @@
+emp = new.tableRef("emp", "PostgreSQL", "postgre.config", "public")
+q = query.select(function (x) {
+             res = new.env()
+             res$ename = x$ename
+             res },
+    query.from(emp))
+results = query.force(q)
+print(results)
diff --git a/com.oracle.truffle.r.test/tests/pgsql/QuerySimple.out b/com.oracle.truffle.r.test/tests/pgsql/QuerySimple.out
new file mode 100644
index 0000000000000000000000000000000000000000..356dee918520abf13a9eae9c5862667d60f3f32b
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/pgsql/QuerySimple.out
@@ -0,0 +1,16 @@
+    ename
+1   SMITH
+2   ALLEN
+3    WARD
+4   JONES
+5  MARTIN
+6   BLAKE
+7   CLARK
+8   SCOTT
+9    KING
+10 TURNER
+11  ADAMS
+12  JAMES
+13   FORD
+14 MILLER
+
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH1.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH1.R
new file mode 100644
index 0000000000000000000000000000000000000000..8e483ac8afb8ed3b19038ca66b73d6e91c4682b4
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH1.R
@@ -0,0 +1,20 @@
+lineitem = new.tableRef("lineitem", "PostgreSQL", "postgre.config", "tpch")
+q =
+order(function(x) c(x$l_returnflag, x$l_linestatus), function(x) c(TRUE, TRUE),
+group(function(x) c(x$l_returnflag, x$l_linestatus),
+select(function(x){
+  y = new.env()
+  y$l_returnflag = x$l_returnflag
+  y$l_linestatus = x$l_linestatus
+  y$sum_qty = sum(x$l_quantity)
+  y$sum_base_price = sum(x$l_extendedprice)
+  y$sum_disc_price = sum(x$l_extendedprice * (1 - x$l_discount))
+  y$sum_charge = sum(x$l_extendedprice * (1 - x$l_discount) * (1 + x$l_tax))
+  y$avg_qty = mean(x$l_quantity)
+  y$avg_price = mean(x$l_extendedprice)
+  y$avg_disc = mean(x$l_discount)
+  y$count_order = length()
+  y},
+where(function(x) x$l_shipdate <= date("1998-12-01") - interval(90, "day"),
+from(lineitem)))))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH10.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH10.R
new file mode 100644
index 0000000000000000000000000000000000000000..79e850acaf2f1676999bb3908cfc78f41cdc3d6b
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH10.R
@@ -0,0 +1,30 @@
+customer = new.tableRef("customer", "PostgreSQL", "postgre.config", "tpch")
+orders = new.tableRef("orders", "PostgreSQL", "postgre.config", "tpch")
+lineitem = new.tableRef("lineitem", "PostgreSQL", "postgre.config", "tpch")
+nation = new.tableRef("nation", "PostgreSQL", "postgre.config", "tpch")
+q =
+order(function(x) c(x$revenue), function(x) c(FALSE),
+group(function(x) c(x$c_custkey, x$c_name, x$c_acctbal, x$c_phone, x$n_name, x$c_address, x$c_comment),
+select(function(x){
+  y = new.env()
+  y$c_custkey = x$c_custkey
+  y$c_name = x$c_name
+  y$revenue = sum(x$l_extendedprice * (1 - x$l_discount))
+  y$c_acctbal = x$c_acctbal
+  y$n_name = x$n_name
+  y$c_address = x$c_address
+  y$c_phone = x$c_phone
+  y$c_comment = x$c_comment
+  y
+},
+where(function(x) x$o_orderdate >= date("1993-10-01")
+  && x$o_orderdate < date("1993-10-01") + interval(3, "month")
+  && x$l_returnflag == "R",
+join(from(nation),
+join(from(customer),
+join(from(lineitem),
+     from(orders),
+     function(x, y) x$l_orderkey == y$o_orderkey),
+     function(x, y) x$c_custkey == y$o_custkey),
+     function(x, y) x$n_nationkey == y$c_nationkey)))))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH11.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH11.R
new file mode 100644
index 0000000000000000000000000000000000000000..eb4750784a7fab549041e44d4fdb008209015ad6
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH11.R
@@ -0,0 +1,31 @@
+partsupp = new.tableRef("partsupp", "PostgreSQL", "postgre.config", "tpch")
+supplier = new.tableRef("supplier", "PostgreSQL", "postgre.config", "tpch")
+nation = new.tableRef("nation", "PostgreSQL", "postgre.config", "tpch")
+q =
+order(function(x) c(x$value), function(x) c(FALSE),
+group(function(x) having(
+  x$ps_partkey, sum(x$ps_supplycost * x$ps_availqty) >
+  select(function(x){
+    y = new.env()
+    y$sumps = sum(x$ps_supplycost * x$ps_availqty) * 0.0001
+    y
+  },
+  where(function(x) x$n_name == "GERMANY",
+  join(from(partsupp),
+  join(from(supplier),
+       from(nation),
+       function(x, y) x$s_nationkey == y$n_nationkey),
+       function(x, y) x$ps_suppkey == y$s_suppkey)))
+),
+select(function(x){y = new.env()
+  y$ps_partkey = x$ps_partkey
+  y$value = sum(x$ps_supplycost * x$ps_availqty)
+  y
+},
+where(function(x) x$n_name == "GERMANY",
+join(from(partsupp),
+join(from(supplier),
+     from(nation),
+     function(x, y) x$s_nationkey == y$n_nationkey),
+     function(x, y) x$ps_suppkey == y$s_suppkey)))))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH12.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH12.R
new file mode 100644
index 0000000000000000000000000000000000000000..379f087147b0acddbbcaafd9ef590fe0f117eacd
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH12.R
@@ -0,0 +1,20 @@
+orders = new.tableRef("orders", "PostgreSQL", "postgre.config", "tpch")
+lineitem = new.tableRef("lineitem", "PostgreSQL", "postgre.config", "tpch")
+q =
+order(function(x) c(x$l_shipmode), function(x) c(TRUE),
+group(function(x) c(x$l_shipmode),
+select(function(x){
+  y = new.env()
+  y$l_shipmode = x$l_shipmode
+  y$high_line_count = sum(if (x$o_orderpriority == "1-URGENT" || x$o_orderpriority == "2-HIGH") 1 else 0)
+  y$low_line_count = sum(if (x$o_orderpriority != "1-URGENT" && x$o_orderpriority != "2-HIGH") 1 else 0)
+  y
+},
+where(function(x) (x$l_shipmode == "MAIL" || x$l_shipmode == "SHIP")
+  && x$l_commitdate < x$l_receiptdate && x$l_shipdate < x$l_commitdate
+  && x$l_receiptdate >= date("1994-01-01")
+  && x$l_receiptdate < date("1994-01-01") + interval(1, "year"),
+join(from(orders),
+     from(lineitem),
+     function(x, y) x$o_orderkey == y$l_orderkey)))))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH13.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH13.R
new file mode 100644
index 0000000000000000000000000000000000000000..f2142740a0730c2623b98b1a8fd77be7bc2953ab
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH13.R
@@ -0,0 +1,21 @@
+customer = new.tableRef("customer", "PostgreSQL", "postgre.config", "tpch")
+orders = new.tableRef("orders", "PostgreSQL", "postgre.config", "tpch")
+q =
+order(function(x) c(x$custdist, x$c_count), function(x) c(FALSE, FALSE),
+group(function(x) c(x$c_count),
+select(function(x){
+  y = new.env()
+  y$c_count = x$c_count
+  y$custdist = length()
+  y
+},
+from(group(function(x) c(x$c_custkey),
+     select(function(x){
+       y = new.env()
+       y$c_custkey = x$c_custkey
+       y$c_count = length(x$o_orderkey)
+       y
+     },
+     leftJoin(from(customer), from(orders),
+       function(x, y) x$c_custkey == y$o_custkey && !(regexpr("%special%requests%", y$o_comment)))))))))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH14.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH14.R
new file mode 100644
index 0000000000000000000000000000000000000000..0140252c33085035497c8cea2e9f19d7d6e9bb95
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH14.R
@@ -0,0 +1,12 @@
+lineitem = new.tableRef("lineitem", "PostgreSQL", "postgre.config", "tpch")
+part = new.tableRef("part", "PostgreSQL", "postgre.config", "tpch")
+q =
+select(function(x){
+  y = new.env()
+  y$promo_revenue = 100.00 * sum(if (regexpr("PROMO%", x$p_type)) x$l_extendedprice * (1 - x$l_discount) else 0) / sum(x$l_extendedprice * (1 - x$l_discount))
+  y
+},
+where(function(x) x$l_shipdate >= date("1995-09-01")
+  && x$l_shipdate < date("1995-09-01") + interval(1, "month"),
+join(from(lineitem), from(part), function(x, y) x$l_partkey == y$p_partkey)))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH15-se.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH15-se.R
new file mode 100644
index 0000000000000000000000000000000000000000..ed31330255ad75c9adc9ac8a1ca0e789b113c914
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH15-se.R
@@ -0,0 +1,27 @@
+max_rev = function() {
+  while (y == null) y = new.env()
+  select(function(x){
+    y = new.env()
+    y$totalrevenue = max(x$total_revenue)
+    y
+  },
+  from(revenue))
+}
+
+supplier = new.tableRef("supplier", "PostgreSQL", "postgre.config", "tpch")
+revenue = new.tableRef("revenue", "PostgreSQL", "postgre.config", "tpch")
+q =
+order(function(x) c(x$s_suppkey), function(x) c(TRUE),
+select(function(x){
+  y = new.env()
+  y$s_suppkey = x$s_suppkey
+  y$s_name = x$s_name
+  y$s_address = x$s_address
+  y$s_phone = x$s_phone
+  y$total_revenue = x$total_revenue
+  y
+},
+where(function(x){x$total_revenue == max_rev()},
+join(from(supplier),
+from(revenue), function(x, y) x$s_suppkey == y$supplier_no))))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH15-udf.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH15-udf.R
new file mode 100644
index 0000000000000000000000000000000000000000..56a0ee187b5163993d271aedbe3a3dbe2e63ef0b
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH15-udf.R
@@ -0,0 +1,26 @@
+max_rev = function() {
+  select(function(x){
+    y = new.env()
+    y$totalrevenue = max(x$total_revenue)
+    y
+  },
+  from(revenue))
+}
+
+supplier = new.tableRef("supplier", "PostgreSQL", "postgre.config", "tpch")
+revenue = new.tableRef("revenue", "PostgreSQL", "postgre.config", "tpch")
+q =
+order(function(x) c(x$s_suppkey), function(x) c(TRUE),
+select(function(x){
+  y = new.env()
+  y$s_suppkey = x$s_suppkey
+  y$s_name = x$s_name
+  y$s_address = x$s_address
+  y$s_phone = x$s_phone
+  y$total_revenue = x$total_revenue
+  y
+},
+where(function(x){x$total_revenue == max_rev()},
+join(from(supplier),
+from(revenue), function(x, y) x$s_suppkey == y$supplier_no))))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH15.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH15.R
new file mode 100644
index 0000000000000000000000000000000000000000..ea4cd6869fd8e0add8e8ace85c56d952f0eaebfc
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH15.R
@@ -0,0 +1,24 @@
+supplier = new.tableRef("supplier", "PostgreSQL", "postgre.config", "tpch")
+revenue = new.tableRef("revenue", "PostgreSQL", "postgre.config", "tpch")
+q =
+order(function(x) c(x$s_suppkey), function(x) c(TRUE),
+select(function(x){
+  y = new.env()
+  y$s_suppkey = x$s_suppkey
+  y$s_name = x$s_name
+  y$s_address = x$s_address
+  y$s_phone = x$s_phone
+  y$total_revenue = x$total_revenue
+  y
+},
+where(function(x){x$total_revenue ==
+  select(function(x){
+    y = new.env()
+    y$totalrevenue = max(x$total_revenue)
+    y
+  },
+  from(revenue))
+},
+join(from(supplier),
+from(revenue), function(x, y) x$s_suppkey == y$supplier_no))))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH16.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH16.R
new file mode 100644
index 0000000000000000000000000000000000000000..35d78e4d6e34abb7e315b6a49c378c97510dd44e
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH16.R
@@ -0,0 +1,28 @@
+supplier = new.tableRef("supplier", "PostgreSQL", "postgre.config", "tpch")
+partsupp = new.tableRef("partsupp", "PostgreSQL", "postgre.config", "tpch")
+part = new.tableRef("part", "PostgreSQL", "postgre.config", "tpch")
+q =
+order(function(x) c(x$supplier_cnt, x$p_brand, x$p_type, x$p_size), function(x) c(FALSE, TRUE, TRUE, TRUE),
+group(function(x) c(x$p_brand, x$p_type, x$p_size),
+select(function(x){
+  y = new.env()
+  y$p_brand = x$p_brand
+  y$p_type = x$p_type
+  y$p_size = x$p_size
+  y$supplier_cnt = length(x$ps_suppkey)
+  y
+},
+where(function(x) x$p_brand != 'Brand#45' && !(regexpr('MEDIUM POLISHED%', x$p_type))
+  && match(x$p_size, c(49, 14, 23, 45, 19, 3, 36, 9))
+  && !match(x$ps_suppkey, query.force(
+    select(function(y){
+      z = new.env()
+      z$s_suppkey = y$s_suppkey
+      z
+    },
+    where(function(y) regexpr("%Customer%Complaints%", y$s_comment),
+    from(supplier))))),
+join(from(partsupp),
+     from(part),
+     function(x, y) x$ps_partkey == y$p_partkey)))))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH17.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH17.R
new file mode 100644
index 0000000000000000000000000000000000000000..07a93344764d91f6065592edf3c27a4a27f1bd0c
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH17.R
@@ -0,0 +1,22 @@
+lineitem = new.tableRef("lineitem", "PostgreSQL", "postgre.config", "tpch")
+part = new.tableRef("part", "PostgreSQL", "postgre.config", "tpch")
+q =
+select(function(x){
+  y = new.env()
+  y$avg_yearly = sum(x$l_extendedprice) / 7.0
+  y
+},
+where(function(x) x$p_brand == 'Brand#23' && x$p_container == 'MED BOX'
+  && x$l_quantity < query.force(
+    select(function(y){
+      z = new.env()
+      z$sum_qty = 0.2 * mean(y$l_quantity)
+      z
+    },
+    where(function(y) y$l_partkey == x$p_partkey,
+    from(lineitem)))
+  ),
+join(from(lineitem),
+     from(part),
+     function(x, y) x$l_partkey == y$p_partkey)))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH18.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH18.R
new file mode 100644
index 0000000000000000000000000000000000000000..5724bd455bfe789cf50beffddf2fd630aea8138a
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH18.R
@@ -0,0 +1,31 @@
+lineitem = new.tableRef("lineitem", "PostgreSQL", "postgre.config", "tpch")
+customer = new.tableRef("customer", "PostgreSQL", "postgre.config", "tpch")
+orders = new.tableRef("orders", "PostgreSQL", "postgre.config", "tpch")
+q =
+order(function(x) c(x$o_totalprice, x$o_orderdate), function(x) c(FALSE, TRUE),
+group(function(x) c(x$c_name, x$c_custkey, x$o_orderkey, x$o_orderdate, x$o_totalprice),
+select(function(x){
+  y = new.env()
+  y$c_name = x$c_name
+  y$c_custkey = x$c_custkey
+  y$o_orderkey = x$o_orderkey
+  y$o_orderdate = x$o_orderdate
+  y$o_totalprice = x$o_totalprice
+  y$sum_qty = sum(x$l_quantity)
+  y
+},
+where(function(x) match(x$o_orderkey, query.force(
+    group(function(y) having(y$l_orderkey, sum(y$l_quantity) > 300),
+    select(function(y){
+      z = new.env()
+      z$l_orderkey = y$l_orderkey
+      z
+    },
+    from(lineitem)))))
+,
+join(from(customer),
+join(from(orders),
+     from(lineitem),
+     function(x, y) x$o_orderkey == y$l_orderkey),
+     function(x, y) x$c_custkey == y$o_custkey)))))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH19.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH19.R
new file mode 100644
index 0000000000000000000000000000000000000000..ead35151062a362a62ac231c379652a427c99dbf
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH19.R
@@ -0,0 +1,23 @@
+part = new.tableRef("part", "PostgreSQL", "postgre.config", "tpch")
+lineitem = new.tableRef("lineitem", "PostgreSQL", "postgre.config", "tpch")
+q =
+select(function(x){
+  y = new.env()
+  y$revenue = sum(x$l_extendedprice * (1 - x$l_discount))
+  y
+},
+where(function(x)
+  (x$p_brand == 'Brand#12' && match(x$p_container, c('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG'))
+  && x$l_quantity >= 1 && x$l_quantity <= 1 + 10 && x$p_size >= 1 && x$p_size <= 5
+  && match(x$l_shipmode, c('AIR', 'AIR REG')) && x$l_shipinstruct == 'DELIVER IN PERSON')
+  || (x$p_brand == 'Brand#23' && match(x$p_container, c('MED BAG', 'MED BOX', 'MED PKG', 'MED PACK'))
+  && x$l_quantity >= 10 && x$l_quantity <= 10 + 10 && x$p_size >= 1 && x$p_size <= 10
+  && match(x$l_shipmode, c('AIR', 'AIR REG')) && x$l_shipinstruct == 'DELIVER IN PERSON')
+  || (x$p_brand == 'Brand#34' && match(x$p_container, c('LG CASE', 'LG BOX', 'LG PACK', 'LG PKG'))
+  && x$l_quantity >= 20 && x$l_quantity <= 20 + 10 && x$p_size >= 1 && x$p_size <= 15
+  && match(x$l_shipmode, c('AIR', 'AIR REG')) && x$l_shipinstruct == 'DELIVER IN PERSON')
+,
+join(from(part),
+     from(lineitem),
+     function(x, y) x$p_partkey == y$l_partkey)))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH2.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH2.R
new file mode 100644
index 0000000000000000000000000000000000000000..1914b57df3d858e7ef48e4fe41203064f2cb1d31
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH2.R
@@ -0,0 +1,44 @@
+part = new.tableRef("part", "PostgreSQL", "postgre.config", "tpch")
+partsupp = new.tableRef("partsupp", "PostgreSQL", "postgre.config", "tpch")
+supplier = new.tableRef("supplier", "PostgreSQL", "postgre.config", "tpch")
+nation = new.tableRef("nation", "PostgreSQL", "postgre.config", "tpch")
+region = new.tableRef("region", "PostgreSQL", "postgre.config", "tpch")
+q =
+order(function(x) c(x$s_acctbal, x$n_name, x$s_name, x$p_partkey), function (x) c(FALSE, TRUE, TRUE, TRUE),
+select(function(x){
+  y = new.env()
+  y$s_acctbal = x$s_acctbal
+  y$s_name = x$s_name
+  y$n_name = x$n_name
+  y$p_partkey = x$p_partkey
+  y$p_mfgr = x$p_mfgr
+  y$s_address = x$s_address
+  y$s_phone = x$s_phone
+  y$s_comment = x$s_comment
+  y
+},
+where(function(x) x$p_size == 15 && regexpr("%BRASS", x$p_type) && x$r_name == "EUROPE"
+        && x$ps_supplycost == select(function(x){
+                                y = new.env()
+                                y$msc = min(x$ps_supplycost)
+                                y
+                              },
+                              where(function(x) x$r_name == "EUROPE",
+                              join(from(partsupp),
+                              join(from(supplier),
+                              join(from(nation),
+                              from(region),
+                              function(y, z) y$n_regionkey == z$r_regionkey),
+                              function(y, z) y$s_nationkey == z$n_nationkey),
+                              function(y, z) y$ps_partkey == x$p_partkey
+                                && y$ps_suppkey == z$s_suppkey))),
+  join(from(part),
+  join(from(partsupp),
+  join(from(supplier),
+  join(from(nation),
+       from(region),
+       function(x, y) x$n_regionkey == y$r_regionkey),
+       function(x, y) x$s_nationkey == y$n_nationkey),
+       function(x, y) x$ps_suppkey == y$s_suppkey),
+       function(x, y) x$p_partkey == y$ps_partkey))))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH20.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH20.R
new file mode 100644
index 0000000000000000000000000000000000000000..505ac5a29a748e50dd3ed8bfef957c876ea44660
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH20.R
@@ -0,0 +1,51 @@
+supplier = new.tableRef("supplier", "PostgreSQL", "postgre.config", "tpch")
+nation = new.tableRef("nation", "PostgreSQL", "postgre.config", "tpch")
+part = new.tableRef("part", "PostgreSQL", "postgre.config", "tpch")
+partsupp = new.tableRef("partsupp", "PostgreSQL", "postgre.config", "tpch")
+lineitem = new.tableRef("lineitem", "PostgreSQL", "postgre.config", "tpch")
+q =
+order(function(x) c(x$s_name), function(x) c(TRUE),
+select(function(x){
+  y = new.env()
+  y$s_name = x$s_name
+  y$s_address = x$s_address
+  y
+},
+where(function(x) x$n_name == 'CANADA'
+  && match(x$s_suppkey, query.force(
+      select(function(y){
+        z = new.env()
+        z$ps_suppkey = y$ps_suppkey
+        z
+      },
+      where(function(y) match(y$ps_partkey, query.force(
+        select(function(z){
+          a = new.env()
+          a$p_partkey = z$p_partkey
+          a
+        },
+        where(function(z) regexpr("forest%", z$p_name)
+        ,
+        from(part)))
+      ))
+      && y$ps_availqty > query.force(
+          select(function(z){
+            a = new.env()
+            a$sum_qty = 0.5 * sum(z$l_quantity)
+            a
+          },
+          where(function(z) z$l_partkey == y$ps_partkey
+          && z$l_suppkey == y$ps_suppkey
+          && z$l_shipdate >= date("1994-01-01")
+          && z$l_shipdate < date("1994-01-01") + interval(1, "year")
+          ,
+          from(lineitem)))
+        )
+      ,
+      from(partsupp)))
+  ))
+,
+join(from(supplier),
+     from(nation),
+     function(x, y) x$s_nationkey == y$n_nationkey))))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH22.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH22.R
new file mode 100644
index 0000000000000000000000000000000000000000..f00c9d86edfe1465539a4fdab5990e9398522166
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH22.R
@@ -0,0 +1,42 @@
+lineitem = new.tableRef("lineitem", "PostgreSQL", "postgre.config", "tpch")
+customer = new.tableRef("customer", "PostgreSQL", "postgre.config", "tpch")
+orders = new.tableRef("orders", "PostgreSQL", "postgre.config", "tpch")
+q =
+order(function(x) c(x$cntrycode), function(x) c(TRUE),
+group(function(x) c(x$cntrycode),
+select(function(x){
+  y = new.env()
+  y$cntrycode = x$cntrycode
+  y$numcust = length()
+  y$totacctbal = sum(x$c_acctbal)
+  y
+},
+from(
+  select(function(x){
+    y = new.env()
+    y$cntrycode = substr(x$c_phone, 1, 2)
+    y$c_acctbal = x$c_acctbal
+    y
+  },
+  where(function(x) match(substr(x$c_phone, 1, 2), c('13', '31', '23', '29', '30', '18', '17'))
+  && x$c_acctbal > query.force(
+    select(function(y){
+      z = new.env()
+      z$avg_bal = mean(y$c_acctbal)
+      z
+    },
+    where(function(y) y$c_acctbal > 0.00
+    && match(substr(y$c_phone, 1, 2), c('13', '31', '23', '29', '30', '18', '17'))
+    ,
+    from(customer))))
+  && query.force(
+    select(function(y){
+      z = new.env()
+      z$count_orders = length()
+      z
+    },
+    where(function(y) y$o_custkey > x$c_custkey,
+    from(orders)))) == 0,
+  from(customer)))
+))))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH3.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH3.R
new file mode 100644
index 0000000000000000000000000000000000000000..630408575814eb7d840c0f9478ea4c883b8a0451
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH3.R
@@ -0,0 +1,21 @@
+customer = new.tableRef("customer", "PostgreSQL", "postgre.config", "tpch")
+orders = new.tableRef("orders", "PostgreSQL", "postgre.config", "tpch")
+lineitem = new.tableRef("lineitem", "PostgreSQL", "postgre.config", "tpch")
+q =
+order(function(x) c(x$revenue, x$o_orderdate), function(x) c(FALSE, TRUE),
+group(function(x) c(x$l_orderkey, x$o_orderdate, x$o_shippriority),
+select(function(x){y = new.env()
+  y$l_orderkey = x$l_orderkey
+  y$revenue = sum(x$l_extendedprice * (1 - x$l_discount))
+  y$o_orderdate = x$o_orderdate
+  y$o_shippriority = x$o_shippriority
+  y
+},
+where(function(x) x$c_mktsegment == "BUILDING"
+  && x$o_orderdate < date("1995-03-15") && x$l_shipdate > date("1995-03-15"),
+join(from(customer),
+join(from(orders),
+from(lineitem),
+function(x, y) x$o_orderkey == y$l_orderkey),
+function(x, y) x$c_custkey == y$o_custkey)))))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH4.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH4.R
new file mode 100644
index 0000000000000000000000000000000000000000..8acb494b281fbd742d0b3c7ae717d000daaaac95
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH4.R
@@ -0,0 +1,26 @@
+customer = new.tableRef("customer", "PostgreSQL", "postgre.config", "tpch")
+orders = new.tableRef("orders", "PostgreSQL", "postgre.config", "tpch")
+lineitem = new.tableRef("lineitem", "PostgreSQL", "postgre.config", "tpch")
+q =
+order(function(x) c(x$o_orderpriority), function(x) c(TRUE),
+group(function(x) c(x$o_orderpriority),
+select(function(x){
+  y = new.env()
+  y$o_orderpriority = x$o_orderpriority
+  y$order_count = length()
+  y
+},
+where(function(x) x$o_orderdate >= date("1993-07-01")
+               && x$o_orderdate < date("1993-07-01") + interval(3, "month")
+               && query.force(
+                    select(function(x){
+                      y = new.env()
+                      y$count = length()
+                      y
+                    },
+                    where(function(y) y$l_orderkey == x$o_orderkey
+                      && y$l_commitdate < y$l_receiptdate,
+                    from(lineitem)))
+                  ) > 0,
+from(orders)))))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH5.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH5.R
new file mode 100644
index 0000000000000000000000000000000000000000..e34f5c8ff1d43445cba5345e646292acc367c9f1
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH5.R
@@ -0,0 +1,29 @@
+customer = new.tableRef("customer", "PostgreSQL", "postgre.config", "tpch")
+orders = new.tableRef("orders", "PostgreSQL", "postgre.config", "tpch")
+lineitem = new.tableRef("lineitem", "PostgreSQL", "postgre.config", "tpch")
+supplier = new.tableRef("supplier", "PostgreSQL", "postgre.config", "tpch")
+nation = new.tableRef("nation", "PostgreSQL", "postgre.config", "tpch")
+region = new.tableRef("region", "PostgreSQL", "postgre.config", "tpch")
+q =
+order(function(x) c(x$revenue), function(x) c(FALSE),
+group(function(x) c(x$n_name),
+select(function(x){
+  y = new.env()
+  y$n_name = x$n_name
+  y$revenue = sum(x$l_extendedprice * (1 - x$l_discount))
+  y
+},
+where(function(x) x$r_name == "ASIA" && x$o_orderdate >= date("1994-01-01")
+       && x$o_orderdate < date("1994-01-01") + interval(1, "year"),
+join(from(customer),
+join(from(orders),
+join(from(lineitem),
+join(from(supplier),
+join(from(nation),
+     from(region),
+     function(x, y) x$n_regionkey == y$r_regionkey),
+     function(x, y) x$s_nationkey == y$n_nationkey),
+     function(x, y) x$l_suppkey == y$s_suppkey),
+     function(x, y) x$o_orderkey == y$l_orderkey),
+     function(x, y) x$c_custkey == y$o_custkey && x$c_nationkey == y$s_nationkey)))))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH6.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH6.R
new file mode 100644
index 0000000000000000000000000000000000000000..9fe1b33ceca6633df66e791b89e6e42653cf1af7
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH6.R
@@ -0,0 +1,11 @@
+lineitem = new.tableRef("lineitem", "PostgreSQL", "postgre.config", "tpch")
+q =
+select(function(x){
+  y = new.env()
+  y$revenue = sum(x$l_extendedprice * x$l_discount)
+  y
+},
+where(function(x) x$l_shipdate >= date("1994-01-01") && x$l_shipdate < date("1994-01-01") + interval(1, "year")
+        && x$l_discount >= 0.06 - 0.01 && x$l_discount <= 0.06 + 0.01 && x$l_quantity < 24,
+from(lineitem)))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH7.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH7.R
new file mode 100644
index 0000000000000000000000000000000000000000..4db3d200ab36720b55ac7c16f4abc5aee9132f8a
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH7.R
@@ -0,0 +1,29 @@
+customer = new.tableRef("customer", "PostgreSQL", "postgre.config", "tpch")
+orders = new.tableRef("orders", "PostgreSQL", "postgre.config", "tpch")
+lineitem = new.tableRef("lineitem", "PostgreSQL", "postgre.config", "tpch")
+supplier = new.tableRef("supplier", "PostgreSQL", "postgre.config", "tpch")
+nation = new.tableRef("nation", "PostgreSQL", "postgre.config", "tpch")
+q =
+order(function(x) c(x$supp_nation, x$cust_nation, x$l_year), function(x) c(TRUE, TRUE, TRUE),
+group(function(x) c(x$supp_nation, x$cust_nation, x$l_year),
+select(function(x){
+  y = new.env()
+  y$supp_nation = x$n_name
+  y$cust_nation = x$n_name
+  y$l_year = format(x$l_shipdate, "%Y")
+  y$volume = x$l_extendedprice * (1 - x$l_discount)
+  y
+},
+join(from(nation),
+join(from(nation),
+join(from(customer),
+join(from(orders),
+join(from(supplier),
+     from(lineitem),
+     function(x, y)x$s_suppkey == y$l_suppkey),
+     function(x, y)x$o_orderkey == y$l_orderkey),
+     function(x, y)x$c_custkey == y$o_custkey),
+     function(x, y)x$n_nationkey == y$s_nationkey),
+     function(x, y)x$n_nationkey == y$c_nationkey && ((x$n_name == "FRANCE" && y$n_name == "GERMANY")
+                  || (x$n_name == "GERMANY" && y$n_name == "FRANCE"))))))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH8.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH8.R
new file mode 100644
index 0000000000000000000000000000000000000000..af28bf1a2629bb4ce211f92762e8106fd2ed0019
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH8.R
@@ -0,0 +1,41 @@
+customer = new.tableRef("customer", "PostgreSQL", "postgre.config", "tpch")
+orders = new.tableRef("orders", "PostgreSQL", "postgre.config", "tpch")
+nation = new.tableRef("nation", "PostgreSQL", "postgre.config", "tpch")
+region = new.tableRef("region", "PostgreSQL", "postgre.config", "tpch")
+supplier = new.tableRef("supplier", "PostgreSQL", "postgre.config", "tpch")
+part = new.tableRef("part", "PostgreSQL", "postgre.config", "tpch")
+lineitem = new.tableRef("lineitem", "PostgreSQL", "postgre.config", "tpch")
+q =
+order(function(x) c(x$o_year), function(x) c(TRUE),
+group(function(x) c(x$o_year),
+select(function(x){
+  y = new.env()
+  y$o_year = x$o_year
+  y$mkt_share = sum(if (x$nation == "BRAZIL") x$volume else 0) / sum(x$volume)
+  y
+},
+from(select(function(x){
+       y = new.env()
+       y$o_year = format(x$o_orderdate, "%Y")
+       y$volume = x$l_extendedprice * (1 - x$l_discount)
+       y$nation = x$n_name
+       y
+     },
+     where(function(x) x$r_name == "AMERICA" && x$p_type == "ECONOMY ANODIZED STEEL"
+       && x$o_orderdate >= date("1995-01-01") && x$o_orderdate <= date("1996-12-31"),
+     join(from(nation),
+     join(from(region),
+     join(from(nation),
+     join(from(customer),
+     join(from(orders),
+     join(from(supplier),
+     join(from(part),
+          from(lineitem),
+          function(x, y) x$p_partkey == y$l_partkey),
+          function(x, y) x$s_suppkey == y$l_suppkey),
+          function(x, y) x$o_orderkey == y$l_orderkey),
+          function(x, y) x$c_custkey == y$o_custkey),
+          function(x, y) x$n_nationkey == y$c_nationkey),
+          function(x, y) x$r_regionkey == y$n_regionkey),
+          function(x, y) x$n_nationkey == y$s_nationkey)))))))
+print(query.force(q))
diff --git a/com.oracle.truffle.r.test/tests/tpch/QueryTPCH9.R b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH9.R
new file mode 100644
index 0000000000000000000000000000000000000000..027104f14e3600e1e0a4a8aa2bb2740565e1b3e9
--- /dev/null
+++ b/com.oracle.truffle.r.test/tests/tpch/QueryTPCH9.R
@@ -0,0 +1,35 @@
+part = new.tableRef("part", "PostgreSQL", "postgre.config", "tpch")
+supplier = new.tableRef("supplier", "PostgreSQL", "postgre.config", "tpch")
+lineitem = new.tableRef("lineitem", "PostgreSQL", "postgre.config", "tpch")
+partsupp = new.tableRef("partsupp", "PostgreSQL", "postgre.config", "tpch")
+orders = new.tableRef("orders", "PostgreSQL", "postgre.config", "tpch")
+nation = new.tableRef("nation", "PostgreSQL", "postgre.config", "tpch")
+q =
+order(function(x) c(x$nation, x$o_year), function(x) c(TRUE, FALSE),
+group(function(x) c(x$nation, x$o_year),
+select(function(x){
+  y = new.env()
+  y$nation = x$nation
+  y$o_year = x$o_year
+  y$sum_profit = sum(x$amount)
+y},
+from(select(function(x){
+       y = new.env()
+       y$nation = x$n_name
+       y$o_year = format(x$o_orderdate, "%Y")
+       y$amount = x$l_extendedprice * (1 - x$l_discount) - x$ps_supplycost * x$l_quantity
+       y
+     },
+     where(function(x) regexpr("%green%", x$p_name),
+     join(from(partsupp),
+     join(from(orders),
+     join(from(part),
+     join(from(lineitem),
+     join(from(supplier),
+          from(nation),
+          function(x, y) x$s_nationkey == y$n_nationkey),
+          function(x, y) x$l_suppkey == y$s_suppkey),
+          function(x, y) x$p_partkey == y$l_partkey),
+          function(x, y) x$o_orderkey == y$l_orderkey),
+          function(x, y) x$ps_suppkey == y$l_suppkey && x$ps_partkey == y$l_partkey)))))))
+print(query.force(q))
diff --git a/mx.fastr/suite.py b/mx.fastr/suite.py
index 154838f37e9a3a80639bf5b8f328a617909dc450..4a898db5a388e2dd373b2dd84168c0d92a435a06 100644
--- a/mx.fastr/suite.py
+++ b/mx.fastr/suite.py
@@ -80,6 +80,26 @@ suite = {
       "sha1" : "0baa82bff19059401e90e1b90020beb9c96305d7",
     },
 
+    "JDK_TOOLS" : {
+      "path" : "${JAVA_HOME}/lib/tools.jar",
+      "sha1" : "NOCHECK",
+    },
+
+    "QIR" : {
+      "path" : "../qir/qir.jar",
+      "sha1" : "NOCHECK",
+    },
+
+    "PGSQL" : {
+      "path" : "../lib/postgresql.jar",
+      "sha1" : "NOCHECK",
+    },
+
+    "OJDBC" : {
+      "path" : "../lib/ojdbc.jar",
+      "sha1" : "NOCHECK",
+    },
+
     "ANTLR-C-3.5" : {
       "path" : "libdownloads/antlr-complete-3.5.1.jar",
       "urls" : ["http://central.maven.org/maven2/org/antlr/antlr-complete/3.5.1/antlr-complete-3.5.1.jar"],
@@ -123,6 +143,7 @@ suite = {
       "dependencies" : [
         "com.oracle.truffle.r.runtime",
         "truffle:TRUFFLE_DEBUG",
+        "QIR"
       ],
       "checkstyle" : "com.oracle.truffle.r.runtime",
       "javaCompliance" : "1.8",
@@ -137,6 +158,7 @@ suite = {
       "sourceDirs" : ["src"],
       "dependencies" : [
         "com.oracle.truffle.r.library",
+        "QIR"
       ],
       "checkstyle" : "com.oracle.truffle.r.runtime",
       "javaCompliance" : "1.8",
@@ -206,6 +228,9 @@ suite = {
         "com.oracle.truffle.r.parser",
         "truffle:JLINE",
         "truffle:TRUFFLE_DEBUG",
+        "QIR",
+        "PGSQL",
+        "OJDBC",
         "truffle:TRUFFLE_NFI",
       ],
      "generatedDependencies" : [
@@ -228,6 +253,7 @@ suite = {
         "truffle:TRUFFLE_API",
         "truffle:TRUFFLE_DEBUG",
         "XZ-1.5",
+        "QIR"
       ],
       "checkstyle" : "com.oracle.truffle.r.runtime",
       "javaCompliance" : "1.8",
diff --git a/oracle.config b/oracle.config
new file mode 100644
index 0000000000000000000000000000000000000000..676cc848c308c0a9303477005ca673d05b3017a8
--- /dev/null
+++ b/oracle.config
@@ -0,0 +1,5 @@
+host = myoracle.com
+sid = myview
+port = 7658
+user = julilope
+passwd = Pa$$w0rd
diff --git a/postgre.config b/postgre.config
new file mode 100644
index 0000000000000000000000000000000000000000..39dd278151eb10c99d66e7b091e5d6c342b355b4
--- /dev/null
+++ b/postgre.config
@@ -0,0 +1,5 @@
+host = localhost
+sid = postgres
+port = 5432
+user = julien
+passwd = Pa$$w0rd
diff --git a/postgre2.config b/postgre2.config
new file mode 100644
index 0000000000000000000000000000000000000000..8ef8ed55fbf64fc1ac673aa5f1ce3e526d6fb55f
--- /dev/null
+++ b/postgre2.config
@@ -0,0 +1,5 @@
+host = localhost
+sid = other
+port = 5432
+user = julien
+passwd = Pa$$w0rd