You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@quickstep.apache.org by ji...@apache.org on 2017/09/15 17:17:12 UTC

incubator-quickstep git commit: Quickstep for GRAIL.

Repository: incubator-quickstep
Updated Branches:
  refs/heads/trace [created] c29587a1a


Quickstep for GRAIL.


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

Branch: refs/heads/trace
Commit: c29587a1a04d07209a5e5f014b05da7521825552
Parents: d85f7a9
Author: Jianqiao Zhu <ji...@cs.wisc.edu>
Authored: Sat Sep 2 23:06:37 2017 -0500
Committer: Jianqiao Zhu <ji...@cs.wisc.edu>
Committed: Thu Sep 14 13:12:19 2017 -0500

----------------------------------------------------------------------
 api/python/ancestor.py                          |  42 +++++
 api/python/ancestor.txt                         |  16 ++
 api/python/descendant.py                        |  42 +++++
 api/python/example.py                           |  11 ++
 api/python/path.py                              | 106 +++++++++++
 api/python/plan.txt                             |   7 +
 api/python/quickstep.py                         |  25 +++
 api/python/quickstep.pyc                        | Bin 0 -> 1514 bytes
 catalog/CatalogRelationStatistics.hpp           |   9 +
 cli/CommandExecutor.cpp                         | 182 ++++++++++++++-----
 cli/Constants.hpp                               |   1 +
 .../aggregation/AggregateFunctionMax.cpp        |   4 +-
 .../aggregation/AggregateFunctionMin.cpp        |   4 +-
 query_optimizer/ExecutionGenerator.cpp          |   1 -
 query_optimizer/resolver/Resolver.cpp           |   9 +-
 storage/AggregationOperationState.cpp           |   3 +-
 16 files changed, 410 insertions(+), 52 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c29587a1/api/python/ancestor.py
----------------------------------------------------------------------
diff --git a/api/python/ancestor.py b/api/python/ancestor.py
new file mode 100644
index 0000000..3318ab9
--- /dev/null
+++ b/api/python/ancestor.py
@@ -0,0 +1,42 @@
+import time
+from quickstep import Quickstep
+
+qs = Quickstep('./quickstep_client')
+
+vertices = [1299454, 31623412, 31739916, 31593563]
+
+# Truncate old tables.
+qs.execute('DROP TABLE cur; DROP TABLE next; DROP TABLE answer;')
+qs.execute('CREATE TABLE cur(id INT); CREATE TABLE next(id INT); CREATE TABLE answer(id INT);')
+
+# Initialize work table.
+init_stmts = ''
+for v in vertices:
+  init_stmts += 'INSERT INTO cur VALUES(' + str(v) + '); '
+qs.execute(init_stmts)
+qs.execute('INSERT INTO answer SELECT id FROM cur;')
+
+# Loop body
+loop_stmts = ''
+loop_stmts += 'DROP TABLE next; CREATE TABLE next(id int); '
+loop_stmts += 'INSERT INTO next SELECT edge.src FROM cur, edge ' + \
+              'WHERE cur.id = edge.dst GROUP BY edge.src; '
+loop_stmts += 'DROP TABLE cur; CREATE TABLE cur(id int); '
+loop_stmts += 'INSERT INTO cur SELECT id FROM next WHERE id NOT IN (SELECT id FROM answer); '
+loop_stmts += 'INSERT INTO answer SELECT id FROM cur; '
+
+for i in range(50):
+  print '--\nIteration ' + str(i) + ':'
+  start = time.time()
+  qs.execute(loop_stmts)
+  end = time.time()
+
+  # We may skip this information for performance.
+  num_vertices = qs.execute('COPY SELECT COUNT(*) FROM answer TO stdout;')
+  print 'Size of graph: ' + num_vertices.rstrip()
+  print 'Time elapsed: ' + str(end - start)
+
+  num_workset = qs.execute('COPY SELECT COUNT(*) FROM cur TO stdout;').rstrip()
+  if num_workset == '0':
+    break
+

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c29587a1/api/python/ancestor.txt
----------------------------------------------------------------------
diff --git a/api/python/ancestor.txt b/api/python/ancestor.txt
new file mode 100644
index 0000000..8d15955
--- /dev/null
+++ b/api/python/ancestor.txt
@@ -0,0 +1,16 @@
+CREATE TABLE r(parent VARCHAR(64), child VARCHAR(64));
+
+INSERT INTO r VALUES('bill', 'mary');
+INSERT INTO r VALUES('mary', 'john');
+...
+...
+...
+
+
+CREATE TABLE s(ancestor VARCHAR(64), offspring VARCHAR(64));
+
+=> ancestor
+
+ancestor('biil', 'mary')
+ancestor('mary', 'john')
+ancestor('bill', 'john')

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c29587a1/api/python/descendant.py
----------------------------------------------------------------------
diff --git a/api/python/descendant.py b/api/python/descendant.py
new file mode 100644
index 0000000..8bdf09f
--- /dev/null
+++ b/api/python/descendant.py
@@ -0,0 +1,42 @@
+import time
+from quickstep import Quickstep
+
+qs = Quickstep('./quickstep_client')
+
+vertices = [1299454, 31623412, 31739916, 31593563]
+
+# Truncate old tables.
+qs.execute('DROP TABLE cur; DROP TABLE next; DROP TABLE answer;')
+qs.execute('CREATE TABLE cur(id INT); CREATE TABLE next(id INT); CREATE TABLE answer(id INT);')
+
+# Initialize work table.
+init_stmts = ''
+for v in vertices:
+  init_stmts += 'INSERT INTO cur VALUES(' + str(v) + '); '
+qs.execute(init_stmts)
+qs.execute('INSERT INTO answer SELECT id FROM cur;')
+
+# Loop body
+loop_stmts = ''
+loop_stmts += 'DROP TABLE next; CREATE TABLE next(id int); '
+loop_stmts += 'INSERT INTO next SELECT edge.dst FROM cur, edge ' + \
+              'WHERE cur.id = edge.src GROUP BY edge.dst; '
+loop_stmts += 'DROP TABLE cur; CREATE TABLE cur(id int); '
+loop_stmts += 'INSERT INTO cur SELECT id FROM next WHERE id NOT IN (SELECT id FROM answer); '
+loop_stmts += 'INSERT INTO answer SELECT id FROM cur; '
+
+for i in range(100):
+  print '--\nIteration ' + str(i) + ':'
+  start = time.time()
+  qs.execute(loop_stmts)
+  end = time.time()
+
+  # We may skip this information for performance.
+  num_vertices = qs.execute('COPY SELECT COUNT(*) FROM answer TO stdout;')
+  print 'Size of graph: ' + num_vertices.rstrip()
+  print 'Time elapsed: ' + str(end - start)
+
+  num_workset = qs.execute('COPY SELECT COUNT(*) FROM cur TO stdout;').rstrip()
+  if num_workset == '0':
+    break
+

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c29587a1/api/python/example.py
----------------------------------------------------------------------
diff --git a/api/python/example.py b/api/python/example.py
new file mode 100644
index 0000000..a9be4fd
--- /dev/null
+++ b/api/python/example.py
@@ -0,0 +1,11 @@
+from quickstep import Quickstep
+
+qs = Quickstep('/Users/jianqiao/Desktop/incubator-quickstep/build/Debug/quickstep_client')
+
+print 'Executing "SELECT * FROM r" ...'
+print '--'
+print qs.execute('SELECT * FROM r')
+
+print 'Executing "COPY SELECT * FROM r TO stdout WITH (FORMAT CSV, HEADER FALSE)" ...'
+print '--'
+print qs.execute('COPY SELECT * FROM r TO stdout WITH (FORMAT CSV, HEADER FALSE)')

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c29587a1/api/python/path.py
----------------------------------------------------------------------
diff --git a/api/python/path.py b/api/python/path.py
new file mode 100644
index 0000000..524ef7d
--- /dev/null
+++ b/api/python/path.py
@@ -0,0 +1,106 @@
+import time
+from quickstep import Quickstep
+
+qs = Quickstep('./quickstep_client')
+
+src_vertices = [981856, 981899, 981913, 1255431, 1255572, 1255586]
+dst_vertices = [1299454, 31623412, 31739916, 31593563]
+
+max_depth = 10
+
+# First generate the backward lineage subgraph within the specified max depth.
+
+print "Prepare tables ..."
+
+# Truncate old tables.
+qs.execute('DROP TABLE cur; DROP TABLE next;')
+qs.execute('CREATE TABLE cur(id INT); CREATE TABLE next(id INT);')
+qs.execute('DROP TABLE answer; CREATE TABLE answer(id INT);')
+
+# Create subgraph edges table.
+qs.execute('DROP TABLE sg_conn;')
+qs.execute('CREATE TABLE sg_conn(src INT, dst INT, depth INT);')
+
+# Initialize work table.
+init_stmts = ''
+for v in dst_vertices:
+  init_stmts += 'INSERT INTO cur VALUES(' + str(v) + '); '
+qs.execute(init_stmts)
+qs.execute('INSERT INTO answer SELECT id FROM cur;')
+
+print "Generating backward lineage subgraph ..."
+
+# Loop body.
+loop_stmts = ''
+loop_stmts += 'INSERT INTO sg_conn SELECT e.src, e.dst, $depth ' + \
+              'FROM cur, conn e WHERE cur.id = e.dst; '
+loop_stmts += 'DROP TABLE next; CREATE TABLE next(id int); '
+loop_stmts += 'INSERT INTO next SELECT e.src FROM cur, conn e ' + \
+              'WHERE cur.id = e.dst GROUP BY e.src; '
+loop_stmts += 'DROP TABLE cur; CREATE TABLE cur(id int); '
+loop_stmts += 'INSERT INTO cur SELECT id FROM next WHERE id NOT IN (SELECT id FROM answer); '
+loop_stmts += 'INSERT INTO answer SELECT id FROM cur;'
+
+for i in range(max_depth):
+  print '--\nIteration ' + str(i + 1) + ':'
+  start = time.time()
+  qs.execute(loop_stmts.replace('$depth', str(i+1)))
+  end = time.time()
+
+  # We may skip this information for performance.
+  num_vertices = qs.execute('COPY SELECT COUNT(*) FROM answer TO stdout;')
+  print 'Size of graph: ' + num_vertices.rstrip()
+  print 'Time elapsed: ' + str(end - start)
+
+  num_workset = qs.execute('COPY SELECT COUNT(*) FROM cur TO stdout;').rstrip()
+  if num_workset == '0':
+    break
+
+# Then generate forward lineage sub-subgraph given sg_conn.
+
+print "Analyzing sg_conn table ..."
+qs.execute('\\analyze sg_conn\n')
+
+# Truncate old tables.
+qs.execute('DROP TABLE cur; DROP TABLE next;')
+qs.execute('CREATE TABLE cur(id INT); CREATE TABLE next(id INT);')
+
+# Initialize work table.
+init_stmts = ''
+for v in src_vertices:
+  init_stmts += 'INSERT INTO next VALUES(' + str(v) + '); '
+qs.execute(init_stmts)
+qs.execute('INSERT INTO cur SELECT id FROM answer WHERE id IN (SELECT id FROM next);')
+
+qs.execute('DROP TABLE answer; CREATE TABLE answer(id INT);')
+qs.execute('INSERT INTO answer SELECT id FROM cur;')
+
+print "Generating forward lineage sub-subgraph ..."
+
+# Loop body.
+loop_stmts = ''
+loop_stmts += 'DROP TABLE next; CREATE TABLE next(id int); '
+loop_stmts += 'INSERT INTO next SELECT e.dst FROM cur, sg_conn e ' + \
+              'WHERE cur.id = e.src ' + \
+              'AND e.depth + $depth <= ' + str(max_depth) + ' GROUP BY e.dst; '
+loop_stmts += 'DROP TABLE cur; CREATE TABLE cur(id int); '
+loop_stmts += 'INSERT INTO cur SELECT id FROM next WHERE id NOT IN (SELECT id FROM answer); '
+loop_stmts += 'INSERT INTO answer SELECT id FROM cur;'
+
+for i in range(max_depth):
+  print '--\nIteration ' + str(i + 1) + ':'
+  start = time.time()
+  qs.execute(loop_stmts.replace('$depth', str(i)))
+  end = time.time()
+
+  # We may skip this information for performance.
+  num_vertices = qs.execute('COPY SELECT COUNT(*) FROM answer TO stdout;')
+  print 'Size of graph: ' + num_vertices.rstrip()
+  print 'Time elapsed: ' + str(end - start)
+
+  num_workset = qs.execute('COPY SELECT COUNT(*) FROM cur TO stdout;').rstrip()
+  if num_workset == '0':
+    break
+
+print "Done."
+

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c29587a1/api/python/plan.txt
----------------------------------------------------------------------
diff --git a/api/python/plan.txt b/api/python/plan.txt
new file mode 100644
index 0000000..8cc87f8
--- /dev/null
+++ b/api/python/plan.txt
@@ -0,0 +1,7 @@
+1. Given filtering condition, select all the vertices.
+2. Given filtering condition, select all the edges (together with the vertices).
+3. List vertices and edges, support navigation.
+4. Visualize vertices and edges (as a subgraph).
+5. Navigation: expand neigbours (1 hop, 2 hops).
+6. Session management, temporary tables.
+7. 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c29587a1/api/python/quickstep.py
----------------------------------------------------------------------
diff --git a/api/python/quickstep.py b/api/python/quickstep.py
new file mode 100644
index 0000000..4dbca8d
--- /dev/null
+++ b/api/python/quickstep.py
@@ -0,0 +1,25 @@
+from subprocess import Popen, PIPE, STDOUT
+
+class Quickstep(object):
+  def __init__(self, client_path, server_ip = None, server_port = None):
+    self.client_path = client_path
+    self.server_ip = server_ip
+    self.server_port = server_port
+
+  def __addSemicolon__(self, query):
+    return query + ('' if query.endswith(';') else ';')
+
+  def execute(self, query):
+    # query = self.__addSemicolon__(query)
+
+    cmd = self.client_path
+    if self.server_ip is not None:
+      cmd += ' -cli_network_ip=' + self.server_ip
+    if self.server_port is not None:
+      cmd += ' -cli_network_port=' + self.server_port
+
+    client_process = Popen([cmd], stdout=PIPE, stdin=PIPE, stderr=PIPE)
+    out, err = client_process.communicate(input = query)
+
+    # Currently we ignore the stderr stream and just return stdout's data.
+    return out

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c29587a1/api/python/quickstep.pyc
----------------------------------------------------------------------
diff --git a/api/python/quickstep.pyc b/api/python/quickstep.pyc
new file mode 100644
index 0000000..9208aca
Binary files /dev/null and b/api/python/quickstep.pyc differ

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c29587a1/catalog/CatalogRelationStatistics.hpp
----------------------------------------------------------------------
diff --git a/catalog/CatalogRelationStatistics.hpp b/catalog/CatalogRelationStatistics.hpp
index df95231..55fc747 100644
--- a/catalog/CatalogRelationStatistics.hpp
+++ b/catalog/CatalogRelationStatistics.hpp
@@ -68,6 +68,15 @@ class CatalogRelationStatistics {
   serialization::CatalogRelationStatistics getProto() const;
 
   /**
+   * @brief Clear all statistics.
+   */
+  void clear() {
+    num_tuples_ = kNullValue;
+    column_stats_.clear();
+    is_exact_ = true;
+  }
+
+  /**
    * @brief Check whether the statistics are exact for the relation.
    *
    * return True if the statistics are exact for the relation, false otherwise.

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c29587a1/cli/CommandExecutor.cpp
----------------------------------------------------------------------
diff --git a/cli/CommandExecutor.cpp b/cli/CommandExecutor.cpp
index 6a84672..fb0dd4c 100644
--- a/cli/CommandExecutor.cpp
+++ b/cli/CommandExecutor.cpp
@@ -201,9 +201,28 @@ void ExecuteAnalyze(const PtrVector<ParseString> &arguments,
     CatalogRelationStatistics *mutable_stat =
         mutable_relation->getStatisticsMutable();
 
+    mutable_stat->clear();
+
     const std::string rel_name = EscapeQuotes(relation.getName(), '"');
 
-    // Get the number of distinct values for each column.
+    // Get the number of tuples for the relation.
+    std::string query_string = "SELECT COUNT(*) FROM \"";
+    query_string.append(rel_name);
+    query_string.append("\";");
+
+    TypedValue num_tuples =
+        ExecuteQueryForSingleResult(main_thread_client_id,
+                                    foreman_client_id,
+                                    query_string,
+                                    bus,
+                                    storage_manager,
+                                    query_processor,
+                                    parser_wrapper.get());
+
+    DCHECK_EQ(TypeID::kLong, num_tuples.getTypeID());
+    mutable_stat->setNumTuples(num_tuples.getLiteral<std::int64_t>());
+
+    // Get the min/max values for each column.
     for (const CatalogAttribute &attribute : relation) {
       const std::string attr_name = EscapeQuotes(attribute.getName(), '"');
       const Type &attr_type = attribute.getType();
@@ -211,24 +230,15 @@ void ExecuteAnalyze(const PtrVector<ParseString> &arguments,
           AggregateFunctionMin::Instance().canApplyToTypes({&attr_type});
       bool is_max_applicable =
           AggregateFunctionMax::Instance().canApplyToTypes({&attr_type});
+      if (!is_min_applicable || !is_max_applicable) {
+        continue;
+      }
 
-      // NOTE(jianqiao): Note that the relation name and the attribute names may
-      // contain non-letter characters, e.g. CREATE TABLE "with space"("1" int).
-      // So here we need to format the names with double quotes (").
-      std::string query_string = "SELECT COUNT(DISTINCT \"";
+      std::string query_string = "SELECT MIN(\"";
       query_string.append(attr_name);
-      query_string.append("\")");
-      if (is_min_applicable) {
-        query_string.append(", MIN(\"");
-        query_string.append(attr_name);
-        query_string.append("\")");
-      }
-      if (is_max_applicable) {
-        query_string.append(", MAX(\"");
-        query_string.append(attr_name);
-        query_string.append("\")");
-      }
-      query_string.append(" FROM \"");
+      query_string.append("\"), MAX(\"");
+      query_string.append(attr_name);
+      query_string.append("\") FROM \"");
       query_string.append(rel_name);
       query_string.append("\";");
 
@@ -240,42 +250,119 @@ void ExecuteAnalyze(const PtrVector<ParseString> &arguments,
                                    storage_manager,
                                    query_processor,
                                    parser_wrapper.get());
-
-      auto results_it = results.begin();
-      DCHECK_EQ(TypeID::kLong, results_it->getTypeID());
+      DCHECK_EQ(2u, results.size());
 
       const attribute_id attr_id = attribute.getID();
-      mutable_stat->setNumDistinctValues(attr_id,
-                                         results_it->getLiteral<std::int64_t>());
-      if (is_min_applicable) {
-        ++results_it;
-        mutable_stat->setMinValue(attr_id, *results_it);
-      }
-      if (is_max_applicable) {
-        ++results_it;
-        mutable_stat->setMaxValue(attr_id, *results_it);
+      mutable_stat->setMinValue(attr_id, results[0]);
+      mutable_stat->setMaxValue(attr_id, results[1]);
+    }
+
+    // Get the number of distinct values for each column.
+    for (const CatalogAttribute &attribute : relation) {
+      const std::string attr_name = EscapeQuotes(attribute.getName(), '"');
+
+      std::string query_string = "SELECT COUNT(*) FROM (SELECT \"";
+      query_string.append(attr_name);
+      query_string.append("\" FROM \"");
+      query_string.append(rel_name);
+      query_string.append("\" GROUP BY \"");
+      query_string.append(attr_name);
+      query_string.append("\") t;");
+
+      TypedValue num_distinct_values =
+          ExecuteQueryForSingleResult(main_thread_client_id,
+                                      foreman_client_id,
+                                      query_string,
+                                      bus,
+                                      storage_manager,
+                                      query_processor,
+                                      parser_wrapper.get());
+
+      DCHECK_EQ(TypeID::kLong, num_distinct_values.getTypeID());
+      mutable_stat->setNumDistinctValues(
+          attribute.getID(), num_distinct_values.getLiteral<std::int64_t>());
+    }
+
+    fprintf(out, "done\n");
+    fflush(out);
+  }
+  query_processor->markCatalogAltered();
+  query_processor->saveCatalog();
+}
+
+void ExecuteAnalyzeRange(const PtrVector<ParseString> &arguments,
+                         const tmb::client_id main_thread_client_id,
+                         const tmb::client_id foreman_client_id,
+                         MessageBus *bus,
+                         StorageManager *storage_manager,
+                         QueryProcessor *query_processor,
+                         FILE *out) {
+  const CatalogDatabase &database = *query_processor->getDefaultDatabase();
+
+  std::unique_ptr<SqlParserWrapper> parser_wrapper(new SqlParserWrapper());
+  std::vector<std::reference_wrapper<const CatalogRelation>> relations;
+  if (arguments.empty()) {
+    relations.insert(relations.begin(), database.begin(), database.end());
+  } else {
+    for (const auto &rel_name : arguments) {
+      const CatalogRelation *rel = database.getRelationByName(rel_name.value());
+      if (rel == nullptr) {
+        THROW_SQL_ERROR_AT(&rel_name) << "Table does not exist";
+      } else {
+        relations.emplace_back(*rel);
       }
     }
+  }
 
-    // Get the number of tuples for the relation.
-    std::string query_string = "SELECT COUNT(*) FROM \"";
-    query_string.append(rel_name);
-    query_string.append("\";");
+  // Analyze each relation in the database.
+  for (const CatalogRelation &relation : relations) {
+    fprintf(out, "Analyzing %s ... ", relation.getName().c_str());
+    fflush(out);
 
-    TypedValue num_tuples =
-        ExecuteQueryForSingleResult(main_thread_client_id,
-                                    foreman_client_id,
-                                    query_string,
-                                    bus,
-                                    storage_manager,
-                                    query_processor,
-                                    parser_wrapper.get());
+    CatalogRelation *mutable_relation =
+        query_processor->getDefaultDatabase()->getRelationByIdMutable(relation.getID());
+    CatalogRelationStatistics *mutable_stat =
+        mutable_relation->getStatisticsMutable();
 
-    DCHECK_EQ(TypeID::kLong, num_tuples.getTypeID());
-    mutable_stat->setNumTuples(num_tuples.getLiteral<std::int64_t>());
+    if (!mutable_stat->isExact()) {
+      mutable_stat->clear();
 
-    mutable_stat->setExactness(true);
+      const std::string rel_name = EscapeQuotes(relation.getName(), '"');
 
+      for (const CatalogAttribute &attribute : relation) {
+        const std::string attr_name = EscapeQuotes(attribute.getName(), '"');
+        const Type &attr_type = attribute.getType();
+        bool is_min_applicable =
+            AggregateFunctionMin::Instance().canApplyToTypes({&attr_type});
+        bool is_max_applicable =
+            AggregateFunctionMax::Instance().canApplyToTypes({&attr_type});
+        if (!is_min_applicable || !is_max_applicable) {
+          continue;
+        }
+
+        std::string query_string = "SELECT MIN(\"";
+        query_string.append(attr_name);
+        query_string.append("\"), MAX(\"");
+        query_string.append(attr_name);
+        query_string.append("\") FROM \"");
+        query_string.append(rel_name);
+        query_string.append("\";");
+
+        std::vector<TypedValue> results =
+            ExecuteQueryForSingleRow(main_thread_client_id,
+                                     foreman_client_id,
+                                     query_string,
+                                     bus,
+                                     storage_manager,
+                                     query_processor,
+                                     parser_wrapper.get());
+        DCHECK_EQ(2u, results.size());
+
+        const attribute_id attr_id = attribute.getID();
+        mutable_stat->setMinValue(attr_id, results[0]);
+        mutable_stat->setMaxValue(attr_id, results[1]);
+      }
+    }
     fprintf(out, "done\n");
     fflush(out);
   }
@@ -314,6 +401,13 @@ void executeCommand(const ParseStatement &statement,
                    bus,
                    storage_manager,
                    query_processor, out);
+  } else if (command_str == kAnalyzeRangeCommand) {
+    ExecuteAnalyzeRange(arguments,
+                        main_thread_client_id,
+                        foreman_client_id,
+                        bus,
+                        storage_manager,
+                        query_processor, out);
   } else {
     THROW_SQL_ERROR_AT(command.command()) << "Invalid Command";
   }

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c29587a1/cli/Constants.hpp
----------------------------------------------------------------------
diff --git a/cli/Constants.hpp b/cli/Constants.hpp
index 0b4a37b..d895f32 100644
--- a/cli/Constants.hpp
+++ b/cli/Constants.hpp
@@ -30,6 +30,7 @@ namespace cli {
 constexpr char kDescribeDatabaseCommand[] = "\\dt";
 constexpr char kDescribeTableCommand[] = "\\d";
 constexpr char kAnalyzeCommand[] = "\\analyze";
+constexpr char kAnalyzeRangeCommand[] = "\\analyzerange";
 
 /** @} */
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c29587a1/expressions/aggregation/AggregateFunctionMax.cpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregateFunctionMax.cpp b/expressions/aggregation/AggregateFunctionMax.cpp
index cc04bf4..6ceacc9 100644
--- a/expressions/aggregation/AggregateFunctionMax.cpp
+++ b/expressions/aggregation/AggregateFunctionMax.cpp
@@ -50,7 +50,9 @@ const Type* AggregateFunctionMax::resultTypeForArgumentTypes(
     return nullptr;
   }
 
-  return &(argument_types.front()->getNullableVersion());
+  // FIXME(jianqiao): The result type can be nullable when it is NOT a group-by
+  // aggregation.
+  return argument_types.front();
 }
 
 AggregationHandle* AggregateFunctionMax::createHandle(

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c29587a1/expressions/aggregation/AggregateFunctionMin.cpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregateFunctionMin.cpp b/expressions/aggregation/AggregateFunctionMin.cpp
index 9fa93de..d88169f 100644
--- a/expressions/aggregation/AggregateFunctionMin.cpp
+++ b/expressions/aggregation/AggregateFunctionMin.cpp
@@ -50,7 +50,9 @@ const Type* AggregateFunctionMin::resultTypeForArgumentTypes(
     return nullptr;
   }
 
-  return &(argument_types.front()->getNullableVersion());
+  // FIXME(jianqiao): The result type can be nullable when it is NOT a group-by
+  // aggregation.
+  return argument_types.front();
 }
 
 AggregationHandle* AggregateFunctionMin::createHandle(

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c29587a1/query_optimizer/ExecutionGenerator.cpp
----------------------------------------------------------------------
diff --git a/query_optimizer/ExecutionGenerator.cpp b/query_optimizer/ExecutionGenerator.cpp
index 372d576..e7ef82a 100644
--- a/query_optimizer/ExecutionGenerator.cpp
+++ b/query_optimizer/ExecutionGenerator.cpp
@@ -1878,7 +1878,6 @@ void ExecutionGenerator::convertAggregate(
       use_parallel_initialization = true;
       aggr_state_num_partitions = CalculateNumFinalizationPartitionsForCollisionFreeVectorTable(max_num_groups);
 
-      DCHECK(!group_by_aggrs_info.empty());
       CalculateCollisionFreeAggregationInfo(max_num_groups, group_by_aggrs_info,
                                             aggr_state_proto->mutable_collision_free_vector_info());
     } else {

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c29587a1/query_optimizer/resolver/Resolver.cpp
----------------------------------------------------------------------
diff --git a/query_optimizer/resolver/Resolver.cpp b/query_optimizer/resolver/Resolver.cpp
index 935e235..b12f59f 100644
--- a/query_optimizer/resolver/Resolver.cpp
+++ b/query_optimizer/resolver/Resolver.cpp
@@ -1016,10 +1016,13 @@ L::LogicalPtr Resolver::resolveInsertSelection(
     if (destination_type.equals(selection_type)) {
       cast_expressions.emplace_back(selection_attributes[aid]);
     } else {
-      // TODO(jianqiao): implement Cast operation for non-numeric types.
+      // TODO(jianqiao): Implement Cast operation for non-numeric types.
+      // TODO(jianqiao): We temporarily disable the safely-coercible check for
+      // tricks that work around "argmin". Will switch it back once the "Cast"
+      // function is supported.
       if (destination_type.getSuperTypeID() == Type::SuperTypeID::kNumeric &&
           selection_type.getSuperTypeID() == Type::SuperTypeID::kNumeric &&
-          destination_type.isSafelyCoercibleFrom(selection_type)) {
+          destination_type.isCoercibleFrom(selection_type)) {
         // Add cast operation
         const E::AttributeReferencePtr attr = selection_attributes[aid];
         const E::ExpressionPtr cast_expr =
@@ -1035,7 +1038,7 @@ L::LogicalPtr Resolver::resolveInsertSelection(
             << insert_statement.relation_name()->value() << "."
             << destination_attributes[aid]->attribute_name() << " has type "
             << selection_attributes[aid]->getValueType().getName()
-            << ", which cannot be safely coerced to the column's type "
+            << ", which cannot be coerced to the column's type "
             << destination_attributes[aid]->getValueType().getName();
      }
     }

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c29587a1/storage/AggregationOperationState.cpp
----------------------------------------------------------------------
diff --git a/storage/AggregationOperationState.cpp b/storage/AggregationOperationState.cpp
index 0f4795f..1d844ee 100644
--- a/storage/AggregationOperationState.cpp
+++ b/storage/AggregationOperationState.cpp
@@ -393,8 +393,7 @@ bool AggregationOperationState::ProtoIsValid(
       }
 
       const S::CollisionFreeVectorInfo &proto_collision_free_vector_info = proto.collision_free_vector_info();
-      if (!proto_collision_free_vector_info.IsInitialized() ||
-          proto_collision_free_vector_info.state_offsets_size() != group_by_expressions_size) {
+      if (!proto_collision_free_vector_info.IsInitialized()) {
         return false;
       }
     }