You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@quickstep.apache.org by ra...@apache.org on 2016/08/05 22:52:25 UTC

[01/30] incubator-quickstep git commit: Disallow negative number of worker threads. [Forced Update!]

Repository: incubator-quickstep
Updated Branches:
  refs/heads/quickstep-28-29 e9a42feec -> d9c9e6867 (forced update)


Disallow negative number of worker threads.

- Fixed a bug thereby Quickstep command line now disallows negative
  number of worker threads.
- If the user provides zero or fewer worker threads, we switch to the
  default number of worker threasd, instead of terminating the process.


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

Branch: refs/heads/quickstep-28-29
Commit: 31f1bbb1c71d9a18af27ee540c83f513125b656f
Parents: 040a511
Author: Harshad Deshmukh <hb...@apache.org>
Authored: Thu Jun 30 11:04:29 2016 -0500
Committer: Harshad Deshmukh <hb...@apache.org>
Committed: Sun Jul 3 23:20:00 2016 -0500

----------------------------------------------------------------------
 cli/QuickstepCli.cpp | 23 +++++++++++------------
 1 file changed, 11 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/31f1bbb1/cli/QuickstepCli.cpp
----------------------------------------------------------------------
diff --git a/cli/QuickstepCli.cpp b/cli/QuickstepCli.cpp
index 3f99130..02a55a0 100644
--- a/cli/QuickstepCli.cpp
+++ b/cli/QuickstepCli.cpp
@@ -200,19 +200,18 @@ int main(int argc, char* argv[]) {
   // that we computed above, provided it did return a valid value.
   // TODO(jmp): May need to change this at some point to keep one thread
   //            available for the OS if the hardware concurrency level is high.
-  const unsigned int real_num_workers = quickstep::FLAGS_num_workers != 0
-                                      ? quickstep::FLAGS_num_workers
-                                      : (num_hw_threads != 0 ?
-                                         num_hw_threads
-                                         : 1);
-
-  if (real_num_workers > 0) {
-    printf("Starting Quickstep with %d worker thread(s) and a %.2f GB buffer pool\n",
-           real_num_workers,
-           (static_cast<double>(quickstep::FLAGS_buffer_pool_slots) * quickstep::kSlotSizeBytes)/quickstep::kAGigaByte);
-  } else {
-    LOG(FATAL) << "Quickstep needs at least one worker thread to run";
+  if (quickstep::FLAGS_num_workers <= 0) {
+    LOG(INFO) << "Quickstep expects at least one worker thread, switching to "
+                 "the default number of worker threads";
   }
+  const int real_num_workers = quickstep::FLAGS_num_workers > 0
+                                   ? quickstep::FLAGS_num_workers
+                                   : (num_hw_threads != 0 ? num_hw_threads : 1);
+
+  DCHECK_GT(real_num_workers, 0);
+  printf("Starting Quickstep with %d worker thread(s) and a %.2f GB buffer pool\n",
+         real_num_workers,
+         (static_cast<double>(quickstep::FLAGS_buffer_pool_slots) * quickstep::kSlotSizeBytes)/quickstep::kAGigaByte);
 
 #ifdef QUICKSTEP_HAVE_FILE_MANAGER_HDFS
   if (quickstep::FLAGS_use_hdfs) {


[28/30] incubator-quickstep git commit: Initial commit for QUICKSTEP-28 and QUICKSTEP-29. Code refactoring and cleanup, some more optimizations are pending.

Posted by ra...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/storage/FastHashTable.hpp
----------------------------------------------------------------------
diff --git a/storage/FastHashTable.hpp b/storage/FastHashTable.hpp
new file mode 100644
index 0000000..12e447f
--- /dev/null
+++ b/storage/FastHashTable.hpp
@@ -0,0 +1,2640 @@
+/**
+ *   Copyright 2011-2015 Quickstep Technologies LLC.
+ *   Copyright 2015-2016 Pivotal Software, Inc.
+ *   Copyright 2016, Quickstep Research Group, Computer Sciences Department,
+ *     University of Wisconsin\u2014Madison.
+ *
+ *   Licensed 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.
+ **/
+
+#ifndef QUICKSTEP_STORAGE_FAST_HASH_TABLE_HPP_
+#define QUICKSTEP_STORAGE_FAST_HASH_TABLE_HPP_
+
+#include <atomic>
+#include <cstddef>
+#include <cstdlib>
+#include <type_traits>
+#include <vector>
+
+#include "catalog/CatalogTypedefs.hpp"
+#include "storage/HashTableBase.hpp"
+#include "storage/StorageBlob.hpp"
+#include "storage/StorageBlockInfo.hpp"
+#include "storage/StorageConstants.hpp"
+#include "storage/StorageManager.hpp"
+#include "storage/TupleReference.hpp"
+#include "storage/ValueAccessor.hpp"
+#include "storage/ValueAccessorUtil.hpp"
+#include "expressions/aggregation/AggregationHandleAvg.hpp"
+#include "threading/SpinSharedMutex.hpp"
+#include "threading/SpinMutex.hpp"
+#include "types/Type.hpp"
+#include "types/TypedValue.hpp"
+#include "utility/BloomFilter.hpp"
+#include "utility/HashPair.hpp"
+#include "utility/Macros.hpp"
+#include "storage/HashTable.hpp"
+
+namespace quickstep {
+
+/** \addtogroup Storage
+ *  @{
+ */
+
+/**
+ * @brief Base class for hash table.
+ *
+ * This class is templated so that the core hash-table logic can be reused in
+ * different contexts requiring different value types and semantics (e.g.
+ * hash-joins vs. hash-based grouping for aggregates vs. hash-based indices).
+ * The base template defines the interface that HashTables provide to clients
+ * and implements some common functionality for all HashTables. There a few
+ * different (also templated) implementation classes that inherit from this
+ * base class and have different physical layouts with different performance
+ * characteristics. As of this writing, they are:
+ *      1. LinearOpenAddressingHashTable - All keys/values are stored directly
+ *         in a single array of buckets. Collisions are handled by simply
+ *         advancing to the "next" adjacent bucket until an empty bucket is
+ *         found. This implementation is vulnerable to performance degradation
+ *         due to the formation of bucket chains when there are many duplicate
+ *         and/or consecutive keys.
+ *      2. SeparateChainingHashTable - Keys/values are stored in a separate
+ *         region of memory from the base hash table slot array. Every bucket
+ *         has a "next" pointer so that entries that collide (i.e. map to the
+ *         same base slot) form chains of pointers with each other. Although
+ *         this implementation has some extra indirection compared to
+ *         LinearOpenAddressingHashTable, it does not have the same
+ *         vulnerabilities to key skew, and it additionally supports a very
+ *         efficient bucket-preallocation mechanism that minimizes cache
+ *         coherency overhead when multiple threads are building a HashTable
+ *         as part of a hash-join.
+ *      3. SimpleScalarSeparateChainingHashTable - A simplified version of
+ *         SeparateChainingHashTable that is only usable for single, scalar
+ *         keys with a reversible hash function. This implementation exploits
+ *         the reversible hash to avoid storing separate copies of keys at all,
+ *         and to skip an extra key comparison when hash codes collide.
+ *
+ * @note If you need to create a HashTable and not just use it as a client, see
+ *       HashTableFactory, which simplifies the process of creating a
+ *       HashTable.
+ *
+ * @param ValueT The mapped value in this hash table. Must be
+ *        copy-constructible. For a serializable hash table, ValueT must also
+ *        be trivially copyable and trivially destructible (and beware of
+ *        pointers to external memory).
+ * @param resizable Whether this hash table is resizable (using memory from a
+ *        StorageManager) or not (using a private, fixed memory allocation).
+ * @param serializable If true, this hash table can safely be saved to and
+ *        loaded from disk. If false, some out of band memory may be used (e.g.
+ *        to store variable length keys).
+ * @param force_key_copy If true, inserted keys are always copied into this
+ *        HashTable's memory. If false, pointers to external values may be
+ *        stored instead. force_key_copy should be true if the hash table will
+ *        outlive the external key values which are inserted into it. Note that
+ *        if serializable is true and force_key_copy is false, then relative
+ *        offsets will be used instead of absolute pointers to keys, meaning
+ *        that the pointed-to keys must be serialized and deserialized in
+ *        exactly the same relative byte order (e.g. as part of the same
+ *        StorageBlock), and keys must not change position relative to this
+ *        HashTable (beware TupleStorageSubBlocks that may self-reorganize when
+ *        modified). If serializable and resizable are both true, then
+ *        force_key_copy must also be true.
+ * @param allow_duplicate_keys If true, multiple values can be mapped to the
+ *        same key. If false, one and only one value may be mapped.
+ **/
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+class FastHashTable : public HashTableBase<resizable,
+                                       serializable,
+                                       force_key_copy,
+                                       allow_duplicate_keys> {
+  static_assert(!(serializable && resizable && !force_key_copy),
+                "A HashTable must have force_key_copy=true when serializable "
+                "and resizable are both true.");
+
+  // TODO(chasseur): GCC 4.8.3 doesn't yet implement
+  // std::is_trivially_copyable. In the future, we should include a
+  // static_assert that prevents a serializable HashTable from being used with
+  // a ValueT which is not trivially copyable.
+
+ public:
+  // Shadow template parameters. This is useful for shared test harnesses.
+//  typedef ValueT value_type;
+  static constexpr bool template_resizable = resizable;
+  static constexpr bool template_serializable = serializable;
+  static constexpr bool template_force_key_copy = force_key_copy;
+  static constexpr bool template_allow_duplicate_keys = allow_duplicate_keys;
+
+  // Some HashTable implementations (notably LinearOpenAddressingHashTable)
+  // use a special hash code to represent an empty bucket, and another special
+  // code to indicate that a bucket is currently being inserted into. For those
+  // HashTables, this is a surrogate hash value for empty buckets. Keys which
+  // actually hash to this value should have their hashes mutated (e.g. by
+  // adding 1). We use zero, since we will often be using memory which is
+  // already zeroed-out and this saves us the trouble of a memset. This has
+  // some downside, as the hash function we use is the identity hash for
+  // integers, and the integer 0 is common in many data sets and must be
+  // adjusted (and will then spuriously collide with 1). Nevertheless, this
+  // expense is outweighed by no longer having to memset large regions of
+  // memory when initializing a HashTable.
+  static constexpr unsigned char kEmptyHashByte = 0x0;
+  static constexpr std::size_t kEmptyHash = 0x0;
+
+  // A surrogate hash value for a bucket which is currently being inserted
+  // into. As with kEmptyHash, keys which actually hash to this value should
+  // have their hashes adjusted.
+  static constexpr std::size_t kPendingHash = ~kEmptyHash;
+
+  /**
+   * @brief Virtual destructor.
+   **/
+  virtual ~FastHashTable() {
+    if (resizable) {
+      if (blob_.valid()) {
+        if (serializable) {
+          DEV_WARNING("Destroying a resizable serializable HashTable's underlying "
+                      "StorageBlob.");
+        }
+        const block_id blob_id = blob_->getID();
+        blob_.release();
+        storage_manager_->deleteBlockOrBlobFile(blob_id);
+      }
+    }
+  }
+
+  /**
+   * @brief Get the ID of the StorageBlob used to store a resizable HashTable.
+   *
+   * @warning This method must not be used for a non-resizable HashTable.
+   *
+   * @return The ID of the StorageBlob used to store this HashTable.
+   **/
+  inline block_id getBlobId() const {
+    DEBUG_ASSERT(resizable);
+    return blob_->getID();
+  }
+
+  /**
+   * @brief Erase all entries in this hash table.
+   *
+   * @warning This method is not guaranteed to be threadsafe.
+   **/
+  virtual void clear() = 0;
+
+  /**
+   * @brief Add a new entry into the hash table.
+   *
+   * @warning The key must not be null.
+   * @warning This method is threadsafe with regard to other calls to put(),
+   *          putCompositeKey(), putValueAccessor(), and
+   *          putValueAccessorCompositeKey(), but should not be used
+   *          simultaneously with upsert(), upsertCompositeKey(),
+   *          upsertValueAccessor(), or upsertValueAccessorCompositeKey().
+   * @note This version is for single scalar keys, see also putCompositeKey().
+   * @note If the hash table is (close to) full and resizable is true, this
+   *       routine might result in rebuilding the entire hash table.
+   *
+   * @param key The key.
+   * @param value The value payload.
+   * @return HashTablePutResult::kOK if an entry was successfully inserted,
+   *         HashTablePutResult::kDuplicateKey if allow_duplicate_keys is false
+   *         and key was a duplicate, or HashTablePutResult::kOutOfSpace if
+   *         resizable is false and storage space for the hash table has been
+   *         exhausted.
+   **/
+  HashTablePutResult put(const TypedValue &key,
+                         const uint8_t &value);
+
+  /**
+   * @brief Add a new entry into the hash table (composite key version).
+   *
+   * @warning No component of the key may be null.
+   * @warning This method is threadsafe with regard to other calls to put(),
+   *          putCompositeKey(), putValueAccessor(), and
+   *          putValueAccessorCompositeKey(), but should not be used
+   *          simultaneously with upsert(), upsertCompositeKey(),
+   *          upsertValueAccessor(), or upsertValueAccessorCompositeKey().
+   * @note This version is for composite keys, see also put().
+   * @note If the hash table is (close to) full and resizable is true, this
+   *       routine might result in rebuilding the entire hash table.
+   *
+   * @param key The components of the key.
+   * @param value The value payload.
+   * @return HashTablePutResult::kOK if an entry was successfully inserted,
+   *         HashTablePutResult::kDuplicateKey if allow_duplicate_keys is false
+   *         and key was a duplicate, or HashTablePutResult::kOutOfSpace if
+   *         resizable is false and storage space for the hash table has been
+   *         exhausted.
+   **/
+  HashTablePutResult putCompositeKey(const std::vector<TypedValue> &key,
+                                     const uint8_t &value);
+
+  HashTablePutResult putCompositeKeyFast(const std::vector<TypedValue> &key,
+                                     const uint8_t *value_ptr);
+
+  /**
+   * @brief Add (multiple) new entries into the hash table from a
+   *        ValueAccessor.
+   *
+   * @warning This method is threadsafe with regard to other calls to put(),
+   *          putCompositeKey(), putValueAccessor(), and
+   *          putValueAccessorCompositeKey(), but should not be used
+   *          simultaneously with upsert(), upsertCompositeKey(),
+   *          upsertValueAccessor(), or upsertValueAccessorCompositeKey().
+   * @note This version is for scalar keys, see also
+   *       putValueAccessorCompositeKey().
+   * @note If the hash table fills up while this call is in progress and
+   *       resizable is true, this might result in rebuilding the entire hash
+   *       table.
+   *
+   * @param accessor A ValueAccessor which will be used to access keys.
+   *        beginIteration() should be called on accessor before calling this
+   *        method.
+   * @param key_attr_id The attribute ID of the keys to be read from accessor.
+   * @param check_for_null_keys If true, each key will be checked to see if it
+   *        is null before inserting it (null keys are skipped). This must be
+   *        set to true if some of the keys that will be read from accessor may
+   *        be null.
+   * @param functor A pointer to a functor, which should provide a call
+   *        operator that takes const ValueAccessor& as an argument (or better
+   *        yet, a templated call operator which takes a const reference to
+   *        some subclass of ValueAccessor as an argument) and returns either
+   *        a ValueT or a reference to a ValueT. The functor should generate
+   *        the appropriate mapped value for the current tuple the accessor is
+   *        iterating on.
+   * @return HashTablePutResult::kOK if all keys and generated values from
+   *         accessor were successfully inserted.
+   *         HashTablePutResult::kOutOfSpace is returned if this hash-table is
+   *         non-resizable and ran out of space (note that some entries may
+   *         still have been inserted, and accessor's iteration will be left on
+   *         the first tuple which could not be inserted).
+   *         HashTablePutResult::kDuplicateKey is returned if
+   *         allow_duplicate_keys is false and a duplicate key is encountered
+   *         (as with HashTablePutResult::kOutOfSpace, some entries may have
+   *         been inserted, and accessor will be left on the tuple with a
+   *         duplicate key).
+   **/
+  template <typename FunctorT>
+  HashTablePutResult putValueAccessor(ValueAccessor *accessor,
+                                      const attribute_id key_attr_id,
+                                      const bool check_for_null_keys,
+                                      FunctorT *functor);
+
+  /**
+   * @brief Add (multiple) new entries into the hash table from a
+   *        ValueAccessor (composite key version).
+   *
+   * @warning This method is threadsafe with regard to other calls to put(),
+   *          putCompositeKey(), putValueAccessor(), and
+   *          putValueAccessorCompositeKey(), but should not be used
+   *          simultaneously with upsert(), upsertCompositeKey(),
+   *          upsertValueAccessor(), or upsertValueAccessorCompositeKey().
+   * @note This version is for composite keys, see also putValueAccessor().
+   * @note If the hash table fills up while this call is in progress and
+   *       resizable is true, this might result in rebuilding the entire hash
+   *       table.
+   *
+   * @param accessor A ValueAccessor which will be used to access keys.
+   *        beginIteration() should be called on accessor before calling this
+   *        method.
+   * @param key_attr_ids The attribute IDs of each key component to be read
+   *        from accessor.
+   * @param check_for_null_keys If true, each key will be checked to see if it
+   *        has a null component before inserting it (null keys are skipped).
+   *        This must be set to true if some of the keys that will be read from
+   *        accessor may be null.
+   * @param functor A pointer to a functor, which should provide a call
+   *        operator that takes const ValueAccessor& as an argument (or better
+   *        yet, a templated call operator which takes a const reference to
+   *        some subclass of ValueAccessor as an argument) and returns either
+   *        a ValueT or a reference to a ValueT. The functor should generate
+   *        the appropriate mapped value for the current tuple the accessor is
+   *        iterating on.
+   * @return HashTablePutResult::kOK if all keys and generated values from
+   *         accessor were successfully inserted.
+   *         HashTablePutResult::kOutOfSpace is returned if this hash-table is
+   *         non-resizable and ran out of space (note that some entries may
+   *         still have been inserted, and accessor's iteration will be left on
+   *         the first tuple which could not be inserted).
+   *         HashTablePutResult::kDuplicateKey is returned if
+   *         allow_duplicate_keys is false and a duplicate key is encountered
+   *         (as with HashTablePutResult::kOutOfSpace, some entries may have
+   *         been inserted, and accessor will be left on the tuple with a
+   *         duplicate key).
+   **/
+  template <typename FunctorT>
+  HashTablePutResult putValueAccessorCompositeKey(
+      ValueAccessor *accessor,
+      const std::vector<attribute_id> &key_attr_ids,
+      const bool check_for_null_keys,
+      FunctorT *functor);
+
+  /**
+   * @brief Apply a functor to the value mapped to a key, first inserting a new
+   *        value if one is not already present.
+   *
+   * @warning The key must not be null.
+   * @warning This method is only usable if allow_duplicate_keys is false.
+   * @warning This method is threadsafe with regard to other calls to upsert(),
+   *          upsertCompositeKey(), upsertValueAccessor(), and
+   *          upsertValueAccessorCompositeKey(), but should not be used
+   *          simultaneously with put(), putCompositeKey(), putValueAccessor(),
+   *          or putValueAccessorCompositeKey().
+   * @warning The ValueT* pointer passed to functor's call operator is only
+   *          guaranteed to be valid for the duration of the call. The functor
+   *          should not store a copy of the pointer and assume that it remains
+   *          valid.
+   * @warning Although this method itself is threadsafe, the ValueT object
+   *          accessed by functor is not guaranteed to be (although it is
+   *          guaranteed that its initial insertion will be atomic). If it is
+   *          possible for multiple threads to call upsert() with the same key
+   *          at the same time, then their access to ValueT should be made
+   *          threadsafe (e.g. with the use of atomic types, mutexes, or some
+   *          other external synchronization).
+   * @note This version is for single scalar keys, see also
+   *       upsertCompositeKey().
+   * @note If the hash table is (close to) full and resizable is true, this
+   *       routine might result in rebuilding the entire hash table.
+   *
+   * @param key The key.
+   * @param initial_value If there was not already a preexisting entry in this
+   *        HashTable for the specified key, then the value will be initialized
+   *        with a copy of initial_value. This parameter is ignored if a value
+   *        is already present for key.
+   * @param functor A pointer to a functor, which should provide a call
+   *        operator which takes ValueT* as an argument. The call operator will
+   *        be invoked once on the value corresponding to key (which may be
+   *        newly inserted and default-constructed).
+   * @return True on success, false if upsert failed because there was not
+   *         enough space to insert a new entry in this HashTable.
+   **/
+  template <typename FunctorT>
+  bool upsert(const TypedValue &key,
+              const uint8_t &initial_value,
+              FunctorT *functor);
+
+  /**
+   * @brief Apply a functor to the value mapped to a key, first inserting a new
+   *        value if one is not already present.
+   *
+   * @warning The key must not be null.
+   * @warning This method is only usable if allow_duplicate_keys is false.
+   * @warning This method is threadsafe with regard to other calls to upsert(),
+   *          upsertCompositeKey(), upsertValueAccessor(), and
+   *          upsertValueAccessorCompositeKey(), but should not be used
+   *          simultaneously with put(), putCompositeKey(), putValueAccessor(),
+   *          or putValueAccessorCompositeKey().
+   * @warning The ValueT* pointer passed to functor's call operator is only
+   *          guaranteed to be valid for the duration of the call. The functor
+   *          should not store a copy of the pointer and assume that it remains
+   *          valid.
+   * @warning Although this method itself is threadsafe, the ValueT object
+   *          accessed by functor is not guaranteed to be (although it is
+   *          guaranteed that its initial insertion will be atomic). If it is
+   *          possible for multiple threads to call upsertCompositeKey() with
+   *          the same key at the same time, then their access to ValueT should
+   *          be made threadsafe (e.g. with the use of atomic types, mutexes,
+   *          or some other external synchronization).
+   * @note This version is for composite keys, see also upsert().
+   * @note If the hash table is (close to) full and resizable is true, this
+   *       routine might result in rebuilding the entire hash table.
+   *
+   * @param key The key.
+   * @param initial_value If there was not already a preexisting entry in this
+   *        HashTable for the specified key, then the value will be initialized
+   *        with a copy of initial_value. This parameter is ignored if a value
+   *        is already present for key.
+   * @param functor A pointer to a functor, which should provide a call
+   *        operator which takes ValueT* as an argument. The call operator will
+   *        be invoked once on the value corresponding to key (which may be
+   *        newly inserted and default-constructed).
+   * @return True on success, false if upsert failed because there was not
+   *         enough space to insert a new entry in this HashTable.
+   **/
+  template <typename FunctorT>
+  bool upsertCompositeKey(const std::vector<TypedValue> &key,
+                          const uint8_t &initial_value,
+                          FunctorT *functor);
+
+
+  template <typename FunctorT>
+  bool upsertCompositeKeyFast(const std::vector<TypedValue> &key,
+                          const uint8_t *init_value_ptr,
+                          FunctorT *functor);
+
+  bool upsertCompositeKeyNewFast(const std::vector<TypedValue> &key,
+                          const uint8_t *init_value_ptr,
+                          const uint8_t *source_state);
+
+  /**
+   * @brief Apply a functor to (multiple) entries in this hash table, with keys
+   *        drawn from a ValueAccessor. New values are first inserted if not
+   *        already present.
+   *
+   * @warning This method is only usable if allow_duplicate_keys is false.
+   * @warning This method is threadsafe with regard to other calls to upsert(),
+   *          upsertCompositeKey(), upsertValueAccessor(), and
+   *          upsertValueAccessorCompositeKey(), but should not be used
+   *          simultaneously with put(), putCompositeKey(), putValueAccessor(),
+   *          or putValueAccessorCompositeKey().
+   * @warning The ValueAccessor reference and ValueT* pointer passed to
+   *          functor's call operator are only guaranteed to be valid for the
+   *          duration of the call. The functor should not store a copy of
+   *          these pointers and assume that they remain valid.
+   * @warning Although this method itself is threadsafe, the ValueT object
+   *          accessed by functor is not guaranteed to be (although it is
+   *          guaranteed that its initial insertion will be atomic). If it is
+   *          possible for multiple threads to call upsertValueAccessor() with
+   *          the same key at the same time, then their access to ValueT should
+   *          be made threadsafe (e.g. with the use of atomic types, mutexes,
+   *          or some other external synchronization).
+   * @note This version is for single scalar keys, see also
+   *       upsertValueAccessorCompositeKey().
+   * @note If the hash table is (close to) full and resizable is true, this
+   *       routine might result in rebuilding the entire hash table.
+   *
+   * @param accessor A ValueAccessor which will be used to access keys.
+   *        beginIteration() should be called on accessor before calling this
+   *        method.
+   * @param key_attr_id The attribute ID of the keys to be read from accessor.
+   * @param check_for_null_keys If true, each key will be checked to see if it
+   *        is null before upserting it (null keys are skipped). This must be
+   *        set to true if some of the keys that will be read from accessor may
+   *        be null.
+   * @param functor A pointer to a functor, which should provide a call
+   *        operator that takes two arguments: const ValueAccessor& (or better
+   *        yet, a templated call operator which takes a const reference to
+   *        some subclass of ValueAccessor as its first argument) and ValueT*.
+   *        The call operator will be invoked once for every tuple with a
+   *        non-null key in accessor.
+   * @return True on success, false if upsert failed because there was not
+   *         enough space to insert new entries for all the keys in accessor
+   *         (note that some entries may still have been upserted, and
+   *         accessor's iteration will be left on the first tuple which could
+   *         not be inserted).
+   **/
+  template <typename FunctorT>
+  bool upsertValueAccessor(ValueAccessor *accessor,
+                           const attribute_id key_attr_id,
+                           const bool check_for_null_keys,
+                           const uint8_t &initial_value,
+                           FunctorT *functor);
+
+
+  bool upsertValueAccessorFast(const std::vector<std::vector<attribute_id>> &argument_ids,
+                           ValueAccessor *accessor,
+                           const attribute_id key_attr_id,
+                           const bool check_for_null_keys);
+
+  /**
+   * @brief Apply a functor to (multiple) entries in this hash table, with keys
+   *        drawn from a ValueAccessor. New values are first inserted if not
+   *        already present. Composite key version.
+   *
+   * @warning This method is only usable if allow_duplicate_keys is false.
+   * @warning This method is threadsafe with regard to other calls to upsert(),
+   *          upsertCompositeKey(), upsertValueAccessor(), and
+   *          upsertValueAccessorCompositeKey(), but should not be used
+   *          simultaneously with put(), putCompositeKey(), putValueAccessor(),
+   *          or putValueAccessorCompositeKey().
+   * @warning The ValueAccessor reference and ValueT* pointer passed to
+   *          functor's call operator are only guaranteed to be valid for the
+   *          duration of the call. The functor should not store a copy of
+   *          these pointers and assume that they remain valid.
+   * @warning Although this method itself is threadsafe, the ValueT object
+   *          accessed by functor is not guaranteed to be (although it is
+   *          guaranteed that its initial insertion will be atomic). If it is
+   *          possible for multiple threads to call upsertValueAccessor() with
+   *          the same key at the same time, then their access to ValueT should
+   *          be made threadsafe (e.g. with the use of atomic types, mutexes,
+   *          or some other external synchronization).
+   * @note This version is for composite keys, see also upsertValueAccessor().
+   * @note If the hash table is (close to) full and resizable is true, this
+   *       routine might result in rebuilding the entire hash table.
+   *
+   * @param accessor A ValueAccessor which will be used to access keys.
+   *        beginIteration() should be called on accessor before calling this
+   *        method.
+   * @param key_attr_ids The attribute IDs of each key component to be read
+   *        from accessor.
+   * @param check_for_null_keys If true, each key will be checked to see if it
+   *        is null before upserting it (null keys are skipped). This must be
+   *        set to true if some of the keys that will be read from accessor may
+   *        be null.
+   * @param functor A pointer to a functor, which should provide a call
+   *        operator that takes two arguments: const ValueAccessor& (or better
+   *        yet, a templated call operator which takes a const reference to
+   *        some subclass of ValueAccessor as its first argument) and ValueT*.
+   *        The call operator will be invoked once for every tuple with a
+   *        non-null key in accessor.
+   * @return True on success, false if upsert failed because there was not
+   *         enough space to insert new entries for all the keys in accessor
+   *         (note that some entries may still have been upserted, and
+   *         accessor's iteration will be left on the first tuple which could
+   *         not be inserted).
+   **/
+  template <typename FunctorT>
+  bool upsertValueAccessorCompositeKey(
+      ValueAccessor *accessor,
+      const std::vector<attribute_id> &key_attr_ids,
+      const bool check_for_null_keys,
+      const uint8_t &initial_value,
+      FunctorT *functor);
+
+  bool upsertValueAccessorCompositeKeyFast(
+      const std::vector<std::vector<attribute_id>> &argument,
+      ValueAccessor *accessor,
+      const std::vector<attribute_id> &key_attr_ids,
+      const bool check_for_null_keys);
+
+  /**
+   * @brief Determine the number of entries (key-value pairs) contained in this
+   *        HashTable.
+   * @note For some HashTable implementations, this is O(1), but for others it
+   *       may be O(n) where n is the number of buckets.
+   *
+   * @warning This method assumes that no concurrent calls to put(),
+   *          putCompositeKey(), putValueAccessor(),
+   *          putValueAccessorCompositeKey(), upsert(), upsertCompositeKey(),
+   *          upsertValueAccessor(), or upsertValueAccessorCompositeKey() are
+   *          taking place (i.e. that this HashTable is immutable for the
+   *          duration of the call). Concurrent calls to getSingle(),
+   *          getSingleCompositeKey(), getAll(), getAllCompositeKey(),
+   *          getAllFromValueAccessor(), getAllFromValueAccessorCompositeKey(),
+   *          forEach(), and forEachCompositeKey() are safe.
+   *
+   * @return The number of entries in this HashTable.
+   **/
+  virtual std::size_t numEntries() const = 0;
+
+  /**
+   * @brief Lookup a key against this hash table to find a matching entry.
+   *
+   * @warning Only usable with the hash table that does not allow duplicate
+   *          keys.
+   * @warning The key must not be null.
+   * @warning This method assumes that no concurrent calls to put(),
+   *          putCompositeKey(), putValueAccessor(),
+   *          putValueAccessorCompositeKey(), upsert(), upsertCompositeKey(),
+   *          upsertValueAccessor(), or upsertValueAccessorCompositeKey() are
+   *          taking place (i.e. that this HashTable is immutable for the
+   *          duration of the call and as long as the returned pointer may be
+   *          dereferenced). Concurrent calls to getSingle(),
+   *          getSingleCompositeKey(), getAll(), getAllCompositeKey(),
+   *          getAllFromValueAccessor(), getAllFromValueAccessorCompositeKey(),
+   *          forEach(), and forEachCompositeKey() are safe.
+   * @note This version is for single scalar keys. See also
+   *       getSingleCompositeKey().
+   *
+   * @param key The key to look up.
+   * @return The value of a matched entry if a matching key is found.
+   *         Otherwise, return NULL.
+   **/
+  virtual const uint8_t* getSingle(const TypedValue &key) const = 0;
+
+  /**
+   * @brief Lookup a composite key against this hash table to find a matching
+   *        entry.
+   *
+   * @warning Only usable with the hash table that does not allow duplicate
+   *          keys.
+   * @warning The key must not be null.
+   * @warning This method assumes that no concurrent calls to put(),
+   *          putCompositeKey(), putValueAccessor(),
+   *          putValueAccessorCompositeKey(), upsert(), upsertCompositeKey(),
+   *          upsertValueAccessor(), or upsertValueAccessorCompositeKey() are
+   *          taking place (i.e. that this HashTable is immutable for the
+   *          duration of the call and as long as the returned pointer may be
+   *          dereferenced). Concurrent calls to getSingle(),
+   *          getSingleCompositeKey(), getAll(), getAllCompositeKey(),
+   *          getAllFromValueAccessor(), getAllFromValueAccessorCompositeKey(),
+   *          forEach(), and forEachCompositeKey() are safe.
+   * @note This version is for composite keys. See also getSingle().
+   *
+   * @param key The key to look up.
+   * @return The value of a matched entry if a matching key is found.
+   *         Otherwise, return NULL.
+   **/
+  virtual const uint8_t* getSingleCompositeKey(const std::vector<TypedValue> &key) const = 0;
+  virtual const uint8_t* getSingleCompositeKey(const std::vector<TypedValue> &key, int index) const = 0;
+
+  /**
+   * @brief Lookup a key against this hash table to find matching entries.
+   *
+   * @warning The key must not be null.
+   * @warning This method assumes that no concurrent calls to put(),
+   *          putCompositeKey(), putValueAccessor(),
+   *          putValueAccessorCompositeKey(), upsert(), upsertCompositeKey(),
+   *          upsertValueAccessor(), or upsertValueAccessorCompositeKey() are
+   *          taking place (i.e. that this HashTable is immutable for the
+   *          duration of the call and as long as the returned pointer may be
+   *          dereferenced). Concurrent calls to getSingle(),
+   *          getSingleCompositeKey(), getAll(), getAllCompositeKey(),
+   *          getAllFromValueAccessor(), getAllFromValueAccessorCompositeKey(),
+   *          forEach(), and forEachCompositeKey() are safe.
+   * @note It is more efficient to call getSingle() if the hash table does not
+   *       allow duplicate keys.
+   * @note This version is for single scalar keys. See also
+   *       getAllCompositeKey().
+   *
+   * @param key The key to look up.
+   * @param values A vector to hold values of all matching entries. Matches
+   *        will be appended to the vector.
+   **/
+  virtual void getAll(const TypedValue &key, std::vector<const uint8_t*> *values) const = 0;
+
+  /**
+   * @brief Lookup a composite key against this hash table to find matching
+   *        entries.
+   *
+   * @warning The key must not be null.
+   * @warning This method assumes that no concurrent calls to put(),
+   *          putCompositeKey(), putValueAccessor(),
+   *          putValueAccessorCompositeKey(), upsert(), upsertCompositeKey(),
+   *          upsertValueAccessor(), or upsertValueAccessorCompositeKey() are
+   *          taking place (i.e. that this HashTable is immutable for the
+   *          duration of the call and as long as the returned pointer may be
+   *          dereferenced). Concurrent calls to getSingle(),
+   *          getSingleCompositeKey(), getAll(), getAllCompositeKey(),
+   *          getAllFromValueAccessor(), getAllFromValueAccessorCompositeKey(),
+   *          forEach(), and forEachCompositeKey() are safe.
+   * @note It is more efficient to call getSingleCompositeKey() if the hash
+   *       table does not allow duplicate keys.
+   * @note This version is for composite keys. See also getAll().
+   *
+   * @param key The key to look up.
+   * @param values A vector to hold values of all matching entries. Matches
+   *        will be appended to the vector.
+   **/
+  virtual void getAllCompositeKey(const std::vector<TypedValue> &key,
+                                  std::vector<const uint8_t*> *values) const = 0;
+
+  /**
+   * @brief Lookup (multiple) keys from a ValueAccessor and apply a functor to
+   *        the matching values.
+   *
+   * @warning This method assumes that no concurrent calls to put(),
+   *          putCompositeKey(), putValueAccessor(),
+   *          putValueAccessorCompositeKey(), upsert(), upsertCompositeKey(),
+   *          upsertValueAccessor(), or upsertValueAccessorCompositeKey() are
+   *          taking place (i.e. that this HashTable is immutable for the
+   *          duration of the call and as long as the returned pointer may be
+   *          dereferenced). Concurrent calls to getSingle(),
+   *          getSingleCompositeKey(), getAll(), getAllCompositeKey(),
+   *          getAllFromValueAccessor(), getAllFromValueAccessorCompositeKey(),
+   *          forEach(), and forEachCompositeKey() are safe.
+   * @note This version is for single scalar keys. See also
+   *       getAllFromValueAccessorCompositeKey().
+   *
+   * @param accessor A ValueAccessor which will be used to access keys.
+   *        beginIteration() should be called on accessor before calling this
+   *        method.
+   * @param key_attr_id The attribute ID of the keys to be read from accessor.
+   * @param check_for_null_keys If true, each key will be checked to see if it
+   *        is null before looking it up (null keys are skipped). This must be
+   *        set to true if some of the keys that will be read from accessor may
+   *        be null.
+   * @param functor A pointer to a functor, which should provide a call
+   *        operator that takes 2 arguments: const ValueAccessor& (or better
+   *        yet, a templated call operator which takes a const reference to
+   *        some subclass of ValueAccessor as its first argument) and
+   *        const ValueT&. The functor will be invoked once for each pair of a
+   *        key taken from accessor and matching value.
+   **/
+  template <typename FunctorT>
+  void getAllFromValueAccessor(ValueAccessor *accessor,
+                               const attribute_id key_attr_id,
+                               const bool check_for_null_keys,
+                               FunctorT *functor) const;
+
+  /**
+   * @brief Lookup (multiple) keys from a ValueAccessor, apply a functor to the
+   *        matching values and additionally call a recordMatch() function of
+   *        the functor when the first match for a key is found.
+   * @warning This method assumes that no concurrent calls to put(),
+   *          putCompositeKey(), putValueAccessor(),
+   *          putValueAccessorCompositeKey(), upsert(), upsertCompositeKey(),
+   *          upsertValueAccessor(), or upsertValueAccessorCompositeKey() are
+   *          taking place (i.e. that this HashTable is immutable for the
+   *          duration of the call and as long as the returned pointer may be
+   *          dereferenced). Concurrent calls to getSingle(),
+   *          getSingleCompositeKey(), getAll(), getAllCompositeKey(),
+   *          getAllFromValueAccessor(), getAllFromValueAccessorCompositeKey(),
+   *          forEach(), and forEachCompositeKey() are safe.
+   * @note This version is for single scalar keys. See also
+   *       getAllFromValueAccessorCompositeKeyWithExtraWorkForFirstMatch().
+   *
+   * @param accessor A ValueAccessor which will be used to access keys.
+   *        beginIteration() should be called on accessor before calling this
+   *        method.
+   * @param key_attr_id The attribute ID of the keys to be read from accessor.
+   * @param check_for_null_keys If true, each key will be checked to see if it
+   *        is null before looking it up (null keys are skipped). This must be
+   *        set to true if some of the keys that will be read from accessor may
+   *        be null.
+   * @param functor A pointer to a functor, which should provide two functions:
+   *        1) An operator that takes 2 arguments: const ValueAccessor& (or better
+   *        yet, a templated call operator which takes a const reference to
+   *        some subclass of ValueAccessor as its first argument) and
+   *        const ValueT&. The operator will be invoked once for each pair of a
+   *        key taken from accessor and matching value.
+   *        2) A function hasMatch that takes 1 argument: const ValueAccessor&.
+   *        The function will be called only once for a key from accessor when
+   *        the first match is found.
+   */
+  template <typename FunctorT>
+  void getAllFromValueAccessorWithExtraWorkForFirstMatch(
+      ValueAccessor *accessor,
+      const attribute_id key_attr_id,
+      const bool check_for_null_keys,
+      FunctorT *functor) const;
+
+  /**
+   * @brief Lookup (multiple) keys from a ValueAccessor, apply a functor to the
+   *        matching values and additionally call a recordMatch() function of
+   *        the functor when the first match for a key is found. Composite key
+   *        version.
+   * @warning This method assumes that no concurrent calls to put(),
+   *          putCompositeKey(), putValueAccessor(),
+   *          putValueAccessorCompositeKey(), upsert(), upsertCompositeKey(),
+   *          upsertValueAccessor(), or upsertValueAccessorCompositeKey() are
+   *          taking place (i.e. that this HashTable is immutable for the
+   *          duration of the call and as long as the returned pointer may be
+   *          dereferenced). Concurrent calls to getSingle(),
+   *          getSingleCompositeKey(), getAll(), getAllCompositeKey(),
+   *          getAllFromValueAccessor(), getAllFromValueAccessorCompositeKey(),
+   *          forEach(), and forEachCompositeKey() are safe.
+   *
+   * @param accessor A ValueAccessor which will be used to access keys.
+   *        beginIteration() should be called on accessor before calling this
+   *        method.
+   * @param key_attr_id The attribute ID of the keys to be read from accessor.
+   * @param check_for_null_keys If true, each key will be checked to see if it
+   *        is null before looking it up (null keys are skipped). This must be
+   *        set to true if some of the keys that will be read from accessor may
+   *        be null.
+   * @param functor A pointer to a functor, which should provide two functions:
+   *        1) An operator that takes 2 arguments: const ValueAccessor& (or better
+   *        yet, a templated call operator which takes a const reference to
+   *        some subclass of ValueAccessor as its first argument) and
+   *        const ValueT&. The operator will be invoked once for each pair of a
+   *        key taken from accessor and matching value.
+   *        2) A function hasMatch that takes 1 argument: const ValueAccessor&.
+   *        The function will be called only once for a key from accessor when
+   *        the first match is found.
+   */
+  template <typename FunctorT>
+  void getAllFromValueAccessorCompositeKeyWithExtraWorkForFirstMatch(
+      ValueAccessor *accessor,
+      const std::vector<attribute_id> &key_attr_ids,
+      const bool check_for_null_keys,
+      FunctorT *functor) const;
+
+  /**
+   * @brief Lookup (multiple) keys from a ValueAccessor and apply a functor to
+   *        the matching values. Composite key version.
+   *
+   * @warning This method assumes that no concurrent calls to put(),
+   *          putCompositeKey(), putValueAccessor(),
+   *          putValueAccessorCompositeKey(), upsert(), upsertCompositeKey(),
+   *          upsertValueAccessor(), or upsertValueAccessorCompositeKey() are
+   *          taking place (i.e. that this HashTable is immutable for the
+   *          duration of the call and as long as the returned pointer may be
+   *          dereferenced). Concurrent calls to getSingle(),
+   *          getSingleCompositeKey(), getAll(), getAllCompositeKey(),
+   *          getAllFromValueAccessor(), getAllFromValueAccessorCompositeKey(),
+   *          forEach(), and forEachCompositeKey() are safe.
+   * @note This version is for composite keys. See also
+   *       getAllFromValueAccessor().
+   *
+   * @param accessor A ValueAccessor which will be used to access keys.
+   *        beginIteration() should be called on accessor before calling this
+   *        method.
+   * @param key_attr_ids The attribute IDs of each key component to be read
+   *        from accessor.
+   * @param check_for_null_keys If true, each key will be checked to see if it
+   *        has a null component before inserting it (null keys are skipped).
+   *        This must be set to true if some of the keys that will be read from
+   *        accessor may be null.
+   * @param functor A pointer to a functor, which should provide a call
+   *        operator that takes 2 arguments: const ValueAccessor& (or better
+   *        yet, a templated call operator which takes a const reference to
+   *        some subclass of ValueAccessor as its first argument) and
+   *        const ValueT&. The functor will be invoked once for each pair of a
+   *        key taken from accessor and matching value.
+   **/
+  template <typename FunctorT>
+  void getAllFromValueAccessorCompositeKey(ValueAccessor *accessor,
+                                           const std::vector<attribute_id> &key_attr_ids,
+                                           const bool check_for_null_keys,
+                                           FunctorT *functor) const;
+
+  /**
+   * @brief Apply the functor to each key with a match in the hash table.
+   *
+   * @param accessor A ValueAccessor which will be used to access keys.
+   *        beginIteration() should be called on accessor before calling this
+   *        method.
+   * @param key_attr_id The attribute ID of the keys to be read from accessor.
+   * @param check_for_null_keys If true, each key will be checked to see if it
+   *        is null before looking it up (null keys are skipped). This must be
+   *        set to true if some of the keys that will be read from accessor may
+   *        be null.
+   * @param functor A pointer to a functor which should provide an operator that
+   *        takes 1 argument: const ValueAccessor&. The operator will be called
+   *        only once for a key from accessor if there is a match.
+   */
+  template <typename FunctorT>
+  void runOverKeysFromValueAccessorIfMatchFound(ValueAccessor *accessor,
+                                                const attribute_id key_attr_id,
+                                                const bool check_for_null_keys,
+                                                FunctorT *functor) const {
+    return runOverKeysFromValueAccessor<true>(accessor,
+                                              key_attr_id,
+                                              check_for_null_keys,
+                                              functor);
+  }
+
+  /**
+   * @brief Apply the functor to each key with a match in the hash table.
+   *
+   * @param accessor A ValueAccessor which will be used to access keys.
+   *        beginIteration() should be called on accessor before calling this
+   *        method.
+   * @param key_attr_id The attribute ID of the keys to be read from accessor.
+   * @param check_for_null_keys If true, each key will be checked to see if it
+   *        is null before looking it up (null keys are skipped). This must be
+   *        set to true if some of the keys that will be read from accessor may
+   *        be null.
+   * @param functor A pointer to a functor which should provide an operator that
+   *        takes 1 argument: const ValueAccessor&. The operator will be called
+   *        only once for a key from accessor if there is a match.
+   */
+  template <typename FunctorT>
+  void runOverKeysFromValueAccessorIfMatchFoundCompositeKey(
+      ValueAccessor *accessor,
+      const std::vector<attribute_id> &key_attr_ids,
+      const bool check_for_null_keys,
+      FunctorT *functor) const {
+    return runOverKeysFromValueAccessorCompositeKey<true>(accessor,
+                                                          key_attr_ids,
+                                                          check_for_null_keys,
+                                                          functor);
+  }
+
+  /**
+   * @brief Apply the functor to each key without a match in the hash table.
+   *
+   * @param accessor A ValueAccessor which will be used to access keys.
+   *        beginIteration() should be called on accessor before calling this
+   *        method.
+   * @param key_attr_id The attribute ID of the keys to be read from accessor.
+   * @param check_for_null_keys If true, each key will be checked to see if it
+   *        is null before looking it up (null keys are skipped). This must be
+   *        set to true if some of the keys that will be read from accessor may
+   *        be null.
+   * @param functor A pointer to a functor which should provide an operator that
+   *        takes 1 argument: const ValueAccessor&. The operator will be called
+   *        only once for a key from accessor if there is no match.
+   */
+  template <typename FunctorT>
+  void runOverKeysFromValueAccessorIfMatchNotFound(
+      ValueAccessor *accessor,
+      const attribute_id key_attr_id,
+      const bool check_for_null_keys,
+      FunctorT *functor) const {
+    return runOverKeysFromValueAccessor<false>(accessor,
+                                               key_attr_id,
+                                               check_for_null_keys,
+                                               functor);
+  }
+
+  /**
+   * @brief Apply the functor to each key without a match in the hash table.
+   *
+   * @param accessor A ValueAccessor which will be used to access keys.
+   *        beginIteration() should be called on accessor before calling this
+   *        method.
+   * @param key_attr_id The attribute ID of the keys to be read from accessor.
+   * @param check_for_null_keys If true, each key will be checked to see if it
+   *        is null before looking it up (null keys are skipped). This must be
+   *        set to true if some of the keys that will be read from accessor may
+   *        be null.
+   * @param functor A pointer to a functor which should provide an operator that
+   *        takes 1 argument: const ValueAccessor&. The operator will be called
+   *        only once for a key from accessor if there is no match.
+   */
+  template <typename FunctorT>
+  void runOverKeysFromValueAccessorIfMatchNotFoundCompositeKey(
+      ValueAccessor *accessor,
+      const std::vector<attribute_id> &key_attr_ids,
+      const bool check_for_null_keys,
+      FunctorT *functor) const {
+    return runOverKeysFromValueAccessorCompositeKey<false>(accessor,
+                                                           key_attr_ids,
+                                                           check_for_null_keys,
+                                                           functor);
+  }
+
+  /**
+   * @brief Apply a functor to each key, value pair in this hash table.
+   *
+   * @warning This method assumes that no concurrent calls to put(),
+   *          putCompositeKey(), putValueAccessor(),
+   *          putValueAccessorCompositeKey(), upsert(), upsertCompositeKey(),
+   *          upsertValueAccessor(), or upsertValueAccessorCompositeKey() are
+   *          taking place (i.e. that this HashTable is immutable for the
+   *          duration of the call and as long as the returned pointer may be
+   *          dereferenced). Concurrent calls to getSingle(),
+   *          getSingleCompositeKey(), getAll(), getAllCompositeKey(),
+   *          getAllFromValueAccessor(), getAllFromValueAccessorCompositeKey(),
+   *          forEach(), and forEachCompositeKey() are safe.
+   * @note This version is for single scalar keys. See also
+   *       forEachCompositeKey().
+   *
+   * @param functor A pointer to a functor, which should provide a call
+   *        operator which takes 2 arguments: const TypedValue&, const ValueT&.
+   *        The call operator will be invoked once on each key, value pair in
+   *        this hash table (note that if allow_duplicate_keys is true,
+   *        the call may occur multiple times for the same key with different
+   *        values).
+   * @return The number of key-value pairs visited.
+   **/
+  template <typename FunctorT>
+  std::size_t forEach(FunctorT *functor) const;
+
+  /**
+   * @brief Apply a functor to each key, value pair in this hash table.
+   *
+   * @warning This method assumes that no concurrent calls to put(),
+   *          putCompositeKey(), putValueAccessor(),
+   *          putValueAccessorCompositeKey(), upsert(), upsertCompositeKey(),
+   *          upsertValueAccessor(), or upsertValueAccessorCompositeKey() are
+   *          taking place (i.e. that this HashTable is immutable for the
+   *          duration of the call and as long as the returned pointer may be
+   *          dereferenced). Concurrent calls to getSingle(),
+   *          getSingleCompositeKey(), getAll(), getAllCompositeKey(),
+   *          getAllFromValueAccessor(), getAllFromValueAccessorCompositeKey(),
+   *          forEach(), and forEachCompositeKey() are safe.
+   * @note This version is for composite keys. See also forEach().
+   *
+   * @param functor A pointer to a functor, which should provide a call
+   *        operator which takes 2 arguments: const std::vector<TypedValue>&,
+   *        const ValueT&. The call operator will be invoked once on each key,
+   *        value pair in this hash table (note that if allow_duplicate_keys is
+   *        true, the call may occur multiple times for the same key with
+   *        different values).
+   * @return The number of key-value pairs visited.
+   **/
+  template <typename FunctorT>
+  std::size_t forEachCompositeKey(FunctorT *functor) const;
+
+  template <typename FunctorT>
+  std::size_t forEachCompositeKeyFast(FunctorT *functor) const;
+
+  template <typename FunctorT>
+  std::size_t forEachCompositeKeyFast(FunctorT *functor, int index) const;
+  /**
+   * @brief A call to this function will cause a bloom filter to be built
+   *        during the build phase of this hash table.
+   **/
+  inline void enableBuildSideBloomFilter() {
+    has_build_side_bloom_filter_ = true;
+  }
+
+  /**
+   * @brief A call to this function will cause a set of bloom filters to be
+   *        probed during the probe phase of this hash table.
+   **/
+  inline void enableProbeSideBloomFilter() {
+    has_probe_side_bloom_filter_ = true;
+  }
+
+  /**
+   * @brief This function sets the pointer to the bloom filter to be
+   *        used during the build phase of this hash table.
+   * @warning Should call enable_build_side_bloom_filter() first to enable
+   *          bloom filter usage during build phase.
+   * @note The ownership of the bloom filter lies with the caller.
+   *
+   * @param bloom_filter The pointer to the bloom filter.
+   **/
+  inline void setBuildSideBloomFilter(BloomFilter *bloom_filter) {
+    build_bloom_filter_ = bloom_filter;
+  }
+
+  /**
+   * @brief This function adds a pointer to the list of bloom filters to be
+   *        used during the probe phase of this hash table.
+   * @warning Should call enable_probe_side_bloom_filter() first to enable
+   *          bloom filter usage during probe phase.
+   * @note The ownership of the bloom filter lies with the caller.
+   *
+   * @param bloom_filter The pointer to the bloom filter.
+   **/
+  inline void addProbeSideBloomFilter(const BloomFilter *bloom_filter) {
+    probe_bloom_filters_.emplace_back(bloom_filter);
+  }
+
+  /**
+   * @brief This function adds a vector of attribute ids corresponding to a
+   *        bloom filter used during the probe phase of this hash table.
+   * @warning Should call enable_probe_side_bloom_filter() first to enable
+   *          bloom filter usage during probe phase.
+   *
+   * @param probe_attribute_ids The vector of attribute ids to use for probing
+   *        the bloom filter.
+   **/
+  inline void addProbeSideAttributeIds(std::vector<attribute_id> &&probe_attribute_ids) {
+    probe_attribute_ids_.push_back(probe_attribute_ids);
+  }
+
+ protected:
+  /**
+   * @brief Constructor for new resizable hash table.
+   *
+   * @param key_types A vector of one or more types (>1 indicates a composite
+   *        key).
+   * @param num_entries The estimated number of entries this hash table will
+   *        hold.
+   * @param storage_manager The StorageManager to use (a StorageBlob will be
+   *        allocated to hold this hash table's contents).
+   * @param adjust_hashes If true, the hash of a key should be modified by
+   *        applying AdjustHash() so that it does not collide with one of the
+   *        special values kEmptyHash or kPendingHash. If false, the hash is
+   *        used as-is.
+   * @param use_scalar_literal_hash If true, the key is a single scalar literal
+   *        (non-composite) that it is safe to use the simplified hash function
+   *        TypedValue::getHashScalarLiteral() on. If false, the generic
+   *        TypedValue::getHash() method will be used.
+   * @param preallocate_supported If true, this HashTable overrides
+   *        preallocateForBulkInsert() to allow bulk-allocation of resources
+   *        (i.e. buckets and variable-length key storage) in a single up-front
+   *        pass when bulk-inserting entries. If false, resources are allocated
+   *        on the fly for each entry.
+   **/
+  FastHashTable(const std::vector<const Type*> &key_types,
+            const std::size_t num_entries,
+            const std::vector<AggregationHandle *> &handles,
+            const std::vector<std::size_t> &payload_sizes,
+            StorageManager *storage_manager,
+            const bool adjust_hashes,
+            const bool use_scalar_literal_hash,
+            const bool preallocate_supported)
+        : key_types_(key_types),
+          scalar_key_inline_(true),
+          key_inline_(nullptr),
+          adjust_hashes_(adjust_hashes),
+          use_scalar_literal_hash_(use_scalar_literal_hash),
+          preallocate_supported_(preallocate_supported),
+          handles_(handles),
+          total_payload_size_(std::accumulate(payload_sizes.begin(), payload_sizes.end(), sizeof(SpinMutex))),
+          storage_manager_(storage_manager),
+          hash_table_memory_(nullptr),
+          hash_table_memory_size_(0) {
+    DEBUG_ASSERT(resizable);
+    std::size_t running_sum = sizeof(SpinMutex);
+    for (auto size : payload_sizes) {
+        payload_offsets_.emplace_back(running_sum);
+        running_sum+=size;
+    }
+  }
+
+  /**
+   * @brief Constructor for non-resizable hash table.
+   *
+   * @param key_types A vector of one or more types (>1 indicates a composite
+   *        key).
+   * @param hash_table_memory A pointer to memory to use for this hash table.
+   * @param hash_table_memory_size The size of hash_table_memory in bytes.
+   * @param new_hash_table If true, this hash table is being constructed for
+   *        the first time and hash_table_memory will be cleared. If false,
+   *        reload a pre-existing hash table.
+   * @param hash_table_memory_zeroed If new_hash_table is true, setting this to
+   *        true means that this HashTable will assume that hash_table_memory
+   *        has already been zeroed-out (any newly-allocated block or blob
+   *        memory from StorageManager is zeroed-out). If false, this HashTable
+   *        will explicitly zero-fill its memory as neccessary. This parameter
+   *        has no effect when new_hash_table is false.
+   * @param adjust_hashes If true, the hash of a key should be modified by
+   *        applying AdjustHash() so that it does not collide with one of the
+   *        special values kEmptyHash or kPendingHash. If false, the hash is
+   *        used as-is.
+   * @param use_scalar_literal_hash If true, the key is a single scalar literal
+   *        (non-composite) that it is safe to use the simplified hash function
+   *        TypedValue::getHashScalarLiteral() on. If false, the generic
+   *        TypedValue::getHash() method will be used.
+   * @param preallocate_supported If true, this HashTable overrides
+   *        preallocateForBulkInsert() to allow bulk-allocation of resources
+   *        (i.e. buckets and variable-length key storage) in a single up-front
+   *        pass when bulk-inserting entries. If false, resources are allocated
+   *        on the fly for each entry.
+   **/
+  FastHashTable(const std::vector<const Type*> &key_types,
+            void *hash_table_memory,
+            const std::size_t hash_table_memory_size,
+            const bool new_hash_table,
+            const bool hash_table_memory_zeroed,
+            const bool adjust_hashes,
+            const bool use_scalar_literal_hash,
+            const bool preallocate_supported)
+      : key_types_(key_types),
+        scalar_key_inline_(true),
+        key_inline_(nullptr),
+        adjust_hashes_(adjust_hashes),
+        use_scalar_literal_hash_(use_scalar_literal_hash),
+        preallocate_supported_(preallocate_supported),
+        storage_manager_(nullptr),
+        hash_table_memory_(hash_table_memory),
+        hash_table_memory_size_(hash_table_memory_size) {
+    DEBUG_ASSERT(!resizable);
+  }
+
+  // Adjust 'hash' so that it is not exactly equal to either of the special
+  // values kEmptyHash or kPendingHash.
+  inline constexpr static std::size_t AdjustHash(const std::size_t hash) {
+    return hash + (hash == kEmptyHash) - (hash == kPendingHash);
+  }
+
+  // Set information about which key components are stored inline. This usually
+  // comes from a HashTableKeyManager, and is set by the constructor of a
+  // subclass of HashTable.
+  inline void setKeyInline(const std::vector<bool> *key_inline) {
+    scalar_key_inline_ = key_inline->front();
+    key_inline_ = key_inline;
+  }
+
+  // Generate a hash for a composite key by hashing each component of 'key' and
+  // mixing their bits with CombineHashes().
+  inline std::size_t hashCompositeKey(const std::vector<TypedValue> &key) const;
+
+  // If 'force_key_copy' is true and some part of a composite key is
+  // variable-length, calculate the total number of bytes for variable-length
+  // key components that need to be copied. Otherwise, return 0 to indicate
+  // that no variable-length copy is required.
+  inline std::size_t calculateVariableLengthCompositeKeyCopySize(
+      const std::vector<TypedValue> &key) const;
+
+  // Helpers for put. If this HashTable is resizable, 'resize_shared_mutex_'
+  // should be locked in shared mode before calling either of these methods.
+  virtual HashTablePutResult putInternal(const TypedValue &key,
+                                         const std::size_t variable_key_size,
+                                         const uint8_t &value,
+                                         HashTablePreallocationState *prealloc_state) = 0;
+  virtual HashTablePutResult putCompositeKeyInternal(const std::vector<TypedValue> &key,
+                                                     const std::size_t variable_key_size,
+                                                     const uint8_t &value,
+                                                     HashTablePreallocationState *prealloc_state) = 0;
+
+  virtual HashTablePutResult putCompositeKeyInternalFast(const std::vector<TypedValue> &key,
+                                                     const std::size_t variable_key_size,
+                                                     const std::uint8_t *init_value_ptr,
+                                                     HashTablePreallocationState *prealloc_state) = 0;
+
+
+  // Helpers for upsert. Both return a pointer to the value corresponding to
+  // 'key'. If this HashTable is resizable, 'resize_shared_mutex_' should be
+  // locked in shared mode while calling and using the returned pointer. May
+  // return NULL if there is not enough space to insert a new key, in which
+  // case a resizable HashTable should release the 'resize_shared_mutex_' and
+  // call resize(), then try again.
+  virtual uint8_t* upsertInternal(const TypedValue &key,
+                                 const std::size_t variable_key_size,
+                                 const uint8_t &initial_value) = 0;
+  virtual uint8_t* upsertInternalFast(const TypedValue &key,
+                                 const std::uint8_t *init_value_ptr,
+                                 const std::size_t variable_key_size) = 0;
+  virtual uint8_t* upsertCompositeKeyInternal(const std::vector<TypedValue> &key,
+                                             const std::size_t variable_key_size,
+                                             const uint8_t &initial_value) = 0;
+
+  virtual uint8_t* upsertCompositeKeyInternalFast(const std::vector<TypedValue> &key,
+                                                  const std::uint8_t *init_value_ptr,
+                                                  const std::size_t variable_key_size) = 0;
+
+  // Helpers for forEach. Each return true on success, false if no more entries
+  // exist to iterate over. After a successful call, '*key' is overwritten with
+  // the key of the next entry, '*value' points to the associated value, and
+  // '*entry_num' is incremented to the next (implementation defined) entry to
+  // check ('*entry_num' should initially be set to zero).
+  virtual bool getNextEntry(TypedValue *key,
+                            const uint8_t **value,
+                            std::size_t *entry_num) const = 0;
+  virtual bool getNextEntryCompositeKey(std::vector<TypedValue> *key,
+                                        const uint8_t **value,
+                                        std::size_t *entry_num) const = 0;
+
+  // Helpers for getAllFromValueAccessor. Each return true on success, false if
+  // no more entries exist for the specified key. After a successful call,
+  // '*value' points to the associated value, and '*entry_num' is incremented
+  // to the next (implementation defined) entry to check ('*entry_num' should
+  // initially be set to zero).
+  virtual bool getNextEntryForKey(const TypedValue &key,
+                                  const std::size_t hash_code,
+                                  const uint8_t **value,
+                                  std::size_t *entry_num) const = 0;
+  virtual bool getNextEntryForCompositeKey(const std::vector<TypedValue> &key,
+                                           const std::size_t hash_code,
+                                           const uint8_t **value,
+                                           std::size_t *entry_num) const = 0;
+
+  // Return true if key exists in the hash table.
+  virtual bool hasKey(const TypedValue &key) const = 0;
+  virtual bool hasCompositeKey(const std::vector<TypedValue> &key) const = 0;
+
+  // For a resizable HashTable, grow to accomodate more entries. If
+  // 'extra_buckets' is not zero, it may serve as a "hint" to implementations
+  // that at least the requested number of extra buckets are required when
+  // resizing (mainly used in putValueAccessor() and
+  // putValueAccessorCompositeKey() when 'preallocate_supported_' is true).
+  // Implementations are free to ignore 'extra_buckets'. If
+  // 'extra_variable_storage' is not zero, implementations will attempt to
+  // allocate at least enough additional variable-key storage space to
+  // accomodate the number of bytes specified. 'retry_num' is intended ONLY for
+  // when resize() recursively calls itself and should not be set to nonzero by
+  // any other caller.
+  virtual void resize(const std::size_t extra_buckets,
+                      const std::size_t extra_variable_storage,
+                      const std::size_t retry_num = 0) = 0;
+
+  // In the case where 'allow_duplicate_keys' is true, it is possible to
+  // pre-calculate the number of key-value entries and the amount of
+  // variable-length key storage that will be needed to insert all the
+  // entries from a ValueAccessor in putValueAccessor() or
+  // putValueAccessorCompositeKey() before actually inserting anything. Some
+  // HashTable implemetations (notably SeparateChainingHashTable) can achieve
+  // better performance by ammortizing the cost of allocating certain resources
+  // (buckets and variable-length key storage) in one up-front allocation. This
+  // method is intended to support that. Returns true and fills in
+  // '*prealloc_state' if pre-allocation was successful. Returns false if a
+  // resize() is needed.
+  virtual bool preallocateForBulkInsert(const std::size_t total_entries,
+                                        const std::size_t total_variable_key_size,
+                                        HashTablePreallocationState *prealloc_state) {
+    FATAL_ERROR("Called HashTable::preallocateForBulkInsert() on a HashTable "
+                "implementation that does not support preallocation.");
+  }
+
+  // Type(s) of keys.
+  const std::vector<const Type*> key_types_;
+
+  // Information about whether key components are stored inline or in a
+  // separate variable-length storage region. This is usually determined by a
+  // HashTableKeyManager and set by calling setKeyInline().
+  bool scalar_key_inline_;
+  const std::vector<bool> *key_inline_;
+
+  // Whether hashes should be adjusted by AdjustHash() before being used.
+  const bool adjust_hashes_;
+  // Whether it is safe to use the simplified TypedValue::getHashScalarLiteral()
+  // method instead of the generic TypedValue::getHash() method.
+  const bool use_scalar_literal_hash_;
+  // Whether preallocateForBulkInsert() is supported by this HashTable.
+  const bool preallocate_supported_;
+
+  const std::vector<AggregationHandle *> handles_;
+  const std::size_t total_payload_size_;
+  std::vector<std::size_t> payload_offsets_;
+
+  // Used only when resizable is true:
+  StorageManager *storage_manager_;
+  MutableBlobReference blob_;
+  // Locked in shared mode for most operations, exclusive mode during resize.
+  // Not locked at all for non-resizable HashTables.
+  alignas(kCacheLineBytes) SpinSharedMutex<true> resize_shared_mutex_;
+
+  // Used only when resizable is false:
+  void *hash_table_memory_;
+  const std::size_t hash_table_memory_size_;
+virtual size_t get_buckets_allocated() const {return 0;}
+
+ private:
+  // Assign '*key_vector' with the attribute values specified by 'key_attr_ids'
+  // at the current position of 'accessor'. If 'check_for_null_keys' is true,
+  // stops and returns true if any of the values is null, otherwise returns
+  // false.
+  template <typename ValueAccessorT>
+  inline static bool GetCompositeKeyFromValueAccessor(
+      const ValueAccessorT &accessor,
+      const std::vector<attribute_id> &key_attr_ids,
+      const bool check_for_null_keys,
+      std::vector<TypedValue> *key_vector) {
+    for (std::vector<attribute_id>::size_type key_idx = 0;
+         key_idx < key_attr_ids.size();
+         ++key_idx) {
+      (*key_vector)[key_idx] = accessor.getTypedValue(key_attr_ids[key_idx]);
+      if (check_for_null_keys && (*key_vector)[key_idx].isNull()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  // If run_if_match_found is true, apply the functor to each key if a match is
+  // found; otherwise, apply the functor if no match is found.
+  template <bool run_if_match_found, typename FunctorT>
+  void runOverKeysFromValueAccessor(ValueAccessor *accessor,
+                                    const attribute_id key_attr_id,
+                                    const bool check_for_null_keys,
+                                    FunctorT *functor) const;
+
+  template <bool run_if_match_found, typename FunctorT>
+  void runOverKeysFromValueAccessorCompositeKey(
+      ValueAccessor *accessor,
+      const std::vector<attribute_id> &key_attr_ids,
+      const bool check_for_null_keys,
+      FunctorT *functor) const;
+
+  // Method containing the actual logic implementing getAllFromValueAccessor().
+  // Has extra template parameters that control behavior to avoid some
+  // inner-loop branching.
+  template <typename FunctorT,
+            bool check_for_null_keys,
+            bool adjust_hashes_template,
+            bool use_scalar_literal_hash_template>
+  void getAllFromValueAccessorImpl(ValueAccessor *accessor,
+                                   const attribute_id key_attr_id,
+                                   FunctorT *functor) const;
+
+  // Data structures used for bloom filter optimized semi-joins.
+  bool has_build_side_bloom_filter_ = false;
+  bool has_probe_side_bloom_filter_ = false;
+  BloomFilter *build_bloom_filter_;
+  std::vector<const BloomFilter*> probe_bloom_filters_;
+  std::vector<std::vector<attribute_id>> probe_attribute_ids_;
+  DISALLOW_COPY_AND_ASSIGN(FastHashTable);
+};
+
+
+/**
+ * @brief An instantiation of the HashTable template for use in aggregations.
+ * @note This has force_key_copy = true, so that we don't have dangling pointers
+ * to blocks that are evicted.
+ **/
+using AggregationStateFastHashTable = FastHashTable<true, false, true, false>;
+
+/** @} */
+
+// ----------------------------------------------------------------------------
+// Implementations of template class methods follow.
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+HashTablePutResult FastHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::put(const TypedValue &key,
+          const uint8_t &value) {
+  const std::size_t variable_size = (force_key_copy && !scalar_key_inline_) ? key.getDataSize()
+                                                                            : 0;
+  if (resizable) {
+    HashTablePutResult result = HashTablePutResult::kOutOfSpace;
+    while (result == HashTablePutResult::kOutOfSpace) {
+      {
+        SpinSharedMutexSharedLock<true> lock(resize_shared_mutex_);
+        result = putInternal(key, variable_size, value, nullptr);
+      }
+      if (result == HashTablePutResult::kOutOfSpace) {
+        resize(0, variable_size);
+      }
+    }
+    return result;
+  } else {
+    return putInternal(key, variable_size, value, nullptr);
+  }
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+HashTablePutResult FastHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::putCompositeKey(const std::vector<TypedValue> &key,
+                      const uint8_t& value) {
+  const std::size_t variable_size = calculateVariableLengthCompositeKeyCopySize(key);
+  if (resizable) {
+    HashTablePutResult result = HashTablePutResult::kOutOfSpace;
+    while (result == HashTablePutResult::kOutOfSpace) {
+      {
+        SpinSharedMutexSharedLock<true> lock(resize_shared_mutex_);
+        result = putCompositeKeyInternal(key, variable_size, value, nullptr);
+      }
+      if (result == HashTablePutResult::kOutOfSpace) {
+        resize(0, variable_size);
+      }
+    }
+    return result;
+  } else {
+    return putCompositeKeyInternal(key, variable_size, value, nullptr);
+  }
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+HashTablePutResult FastHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::putCompositeKeyFast(const std::vector<TypedValue> &key,
+                      const std::uint8_t* init_value_ptr) {
+  const std::size_t variable_size = calculateVariableLengthCompositeKeyCopySize(key);
+  if (resizable) {
+    HashTablePutResult result = HashTablePutResult::kOutOfSpace;
+    while (result == HashTablePutResult::kOutOfSpace) {
+      {
+        SpinSharedMutexSharedLock<true> lock(resize_shared_mutex_);
+        result = putCompositeKeyInternalFast(key, variable_size, init_value_ptr, nullptr);
+      }
+      if (result == HashTablePutResult::kOutOfSpace) {
+        resize(0, variable_size);
+      }
+    }
+    return result;
+  } else {
+    return putCompositeKeyInternalFast(key, variable_size, init_value_ptr, nullptr);
+  }
+}
+
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+template <typename FunctorT>
+HashTablePutResult FastHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::putValueAccessor(ValueAccessor *accessor,
+                       const attribute_id key_attr_id,
+                       const bool check_for_null_keys,
+                       FunctorT *functor) {
+  HashTablePutResult result = HashTablePutResult::kOutOfSpace;
+  std::size_t variable_size;
+  HashTablePreallocationState prealloc_state;
+  bool using_prealloc = allow_duplicate_keys && preallocate_supported_;
+  return InvokeOnAnyValueAccessor(
+      accessor,
+      [&](auto *accessor) -> HashTablePutResult {  // NOLINT(build/c++11)
+    if (using_prealloc) {
+      std::size_t total_entries = 0;
+      std::size_t total_variable_key_size = 0;
+      if (check_for_null_keys || (force_key_copy && !scalar_key_inline_)) {
+        // If we need to filter out nulls OR make variable copies, make a
+        // prepass over the ValueAccessor.
+        while (accessor->next()) {
+          TypedValue key = accessor->getTypedValue(key_attr_id);
+          if (check_for_null_keys && key.isNull()) {
+            continue;
+          }
+          ++total_entries;
+          total_variable_key_size += (force_key_copy && !scalar_key_inline_) ? key.getDataSize() : 0;
+        }
+        accessor->beginIteration();
+      } else {
+        total_entries = accessor->getNumTuples();
+      }
+      if (resizable) {
+        bool prealloc_succeeded = false;
+        while (!prealloc_succeeded) {
+          {
+            SpinSharedMutexSharedLock<true> lock(resize_shared_mutex_);
+            prealloc_succeeded = this->preallocateForBulkInsert(total_entries,
+                                                                total_variable_key_size,
+                                                                &prealloc_state);
+          }
+          if (!prealloc_succeeded) {
+            this->resize(total_entries, total_variable_key_size);
+          }
+        }
+      } else {
+        using_prealloc = this->preallocateForBulkInsert(total_entries,
+                                                        total_variable_key_size,
+                                                        &prealloc_state);
+      }
+    }
+    std::unique_ptr<BloomFilter> thread_local_bloom_filter;
+    if (has_build_side_bloom_filter_) {
+      thread_local_bloom_filter.reset(new BloomFilter(build_bloom_filter_->getRandomSeed(),
+                                                      build_bloom_filter_->getNumberOfHashes(),
+                                                      build_bloom_filter_->getBitArraySize()));
+    }
+    if (resizable) {
+      while (result == HashTablePutResult::kOutOfSpace) {
+        {
+          result = HashTablePutResult::kOK;
+          SpinSharedMutexSharedLock<true> lock(resize_shared_mutex_);
+          while (accessor->next()) {
+            TypedValue key = accessor->getTypedValue(key_attr_id);
+            if (check_for_null_keys && key.isNull()) {
+              continue;
+            }
+            variable_size = (force_key_copy && !scalar_key_inline_) ? key.getDataSize() : 0;
+            result = this->putInternal(key,
+                                       variable_size,
+                                       (*functor)(*accessor),
+                                       using_prealloc ? &prealloc_state : nullptr);
+            // Insert into bloom filter, if enabled.
+            if (has_build_side_bloom_filter_) {
+              thread_local_bloom_filter->insertUnSafe(static_cast<const std::uint8_t *>(key.getDataPtr()),
+                                                      key.getDataSize());
+            }
+            if (result == HashTablePutResult::kDuplicateKey) {
+              DEBUG_ASSERT(!using_prealloc);
+              return result;
+            } else if (result == HashTablePutResult::kOutOfSpace) {
+              DEBUG_ASSERT(!using_prealloc);
+              break;
+            }
+          }
+        }
+        if (result == HashTablePutResult::kOutOfSpace) {
+          this->resize(0, variable_size);
+          accessor->previous();
+        }
+      }
+    } else {
+      while (accessor->next()) {
+        TypedValue key = accessor->getTypedValue(key_attr_id);
+        if (check_for_null_keys && key.isNull()) {
+          continue;
+        }
+        variable_size = (force_key_copy && !scalar_key_inline_) ? key.getDataSize() : 0;
+        result = this->putInternal(key,
+                                   variable_size,
+                                   (*functor)(*accessor),
+                                   using_prealloc ? &prealloc_state : nullptr);
+        // Insert into bloom filter, if enabled.
+        if (has_build_side_bloom_filter_) {
+          thread_local_bloom_filter->insertUnSafe(static_cast<const std::uint8_t *>(key.getDataPtr()),
+                                                  key.getDataSize());
+        }
+        if (result != HashTablePutResult::kOK) {
+          return result;
+        }
+      }
+    }
+    // Update the build side bloom filter with thread local copy, if available.
+    if (has_build_side_bloom_filter_) {
+      build_bloom_filter_->bitwiseOr(thread_local_bloom_filter.get());
+    }
+
+    return HashTablePutResult::kOK;
+  });
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+template <typename FunctorT>
+HashTablePutResult FastHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::putValueAccessorCompositeKey(ValueAccessor *accessor,
+                                   const std::vector<attribute_id> &key_attr_ids,
+                                   const bool check_for_null_keys,
+                                   FunctorT *functor) {
+  DEBUG_ASSERT(key_types_.size() == key_attr_ids.size());
+  HashTablePutResult result = HashTablePutResult::kOutOfSpace;
+  std::size_t variable_size;
+  HashTablePreallocationState prealloc_state;
+  bool using_prealloc = allow_duplicate_keys && preallocate_supported_;
+  std::vector<TypedValue> key_vector;
+  key_vector.resize(key_attr_ids.size());
+  return InvokeOnAnyValueAccessor(
+      accessor,
+      [&](auto *accessor) -> HashTablePutResult {  // NOLINT(build/c++11)
+    if (using_prealloc) {
+      std::size_t total_entries = 0;
+      std::size_t total_variable_key_size = 0;
+      if (check_for_null_keys || force_key_copy) {
+        // If we need to filter out nulls OR make variable copies, make a
+        // prepass over the ValueAccessor.
+        while (accessor->next()) {
+          if (this->GetCompositeKeyFromValueAccessor(*accessor,
+                                                     key_attr_ids,
+                                                     check_for_null_keys,
+                                                     &key_vector)) {
+            continue;
+          }
+          ++total_entries;
+          total_variable_key_size += this->calculateVariableLengthCompositeKeyCopySize(key_vector);
+        }
+        accessor->beginIteration();
+      } else {
+        total_entries = accessor->getNumTuples();
+      }
+      if (resizable) {
+        bool prealloc_succeeded = false;
+        while (!prealloc_succeeded) {
+          {
+            SpinSharedMutexSharedLock<true> lock(resize_shared_mutex_);
+            prealloc_succeeded = this->preallocateForBulkInsert(total_entries,
+                                                                total_variable_key_size,
+                                                                &prealloc_state);
+          }
+          if (!prealloc_succeeded) {
+            this->resize(total_entries, total_variable_key_size);
+          }
+        }
+      } else {
+        using_prealloc = this->preallocateForBulkInsert(total_entries,
+                                                        total_variable_key_size,
+                                                        &prealloc_state);
+      }
+    }
+    if (resizable) {
+      while (result == HashTablePutResult::kOutOfSpace) {
+        {
+          result = HashTablePutResult::kOK;
+          SpinSharedMutexSharedLock<true> lock(resize_shared_mutex_);
+          while (accessor->next()) {
+            if (this->GetCompositeKeyFromValueAccessor(*accessor,
+                                                       key_attr_ids,
+                                                       check_for_null_keys,
+                                                       &key_vector)) {
+              continue;
+            }
+            variable_size = this->calculateVariableLengthCompositeKeyCopySize(key_vector);
+            result = this->putCompositeKeyInternal(key_vector,
+                                                   variable_size,
+                                                   (*functor)(*accessor),
+                                                   using_prealloc ? &prealloc_state : nullptr);
+            if (result == HashTablePutResult::kDuplicateKey) {
+              DEBUG_ASSERT(!using_prealloc);
+              return result;
+            } else if (result == HashTablePutResult::kOutOfSpace) {
+              DEBUG_ASSERT(!using_prealloc);
+              break;
+            }
+          }
+        }
+        if (result == HashTablePutResult::kOutOfSpace) {
+          this->resize(0, variable_size);
+          accessor->previous();
+        }
+      }
+    } else {
+      while (accessor->next()) {
+        if (this->GetCompositeKeyFromValueAccessor(*accessor,
+                                                   key_attr_ids,
+                                                   check_for_null_keys,
+                                                   &key_vector)) {
+          continue;
+        }
+        variable_size = this->calculateVariableLengthCompositeKeyCopySize(key_vector);
+        result = this->putCompositeKeyInternal(key_vector,
+                                               variable_size,
+                                               (*functor)(*accessor),
+                                               using_prealloc ? &prealloc_state : nullptr);
+        if (result != HashTablePutResult::kOK) {
+          return result;
+        }
+      }
+    }
+
+    return HashTablePutResult::kOK;
+  });
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+template <typename FunctorT>
+bool FastHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::upsert(const TypedValue &key,
+             const uint8_t &initial_value,
+             FunctorT *functor) {
+  DEBUG_ASSERT(!allow_duplicate_keys);
+  const std::size_t variable_size = (force_key_copy && !scalar_key_inline_) ? key.getDataSize() : 0;
+  if (resizable) {
+    for (;;) {
+      {
+        SpinSharedMutexSharedLock<true> resize_lock(resize_shared_mutex_);
+        uint8_t *value = upsertInternal(key, variable_size, initial_value);
+        if (value != nullptr) {
+          (*functor)(value);
+          return true;
+        }
+      }
+      resize(0, force_key_copy && !scalar_key_inline_ ? key.getDataSize() : 0);
+    }
+  } else {
+    uint8_t *value = upsertInternal(key, variable_size, initial_value);
+    if (value == nullptr) {
+      return false;
+    } else {
+      (*functor)(value);
+      return true;
+    }
+  }
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+template <typename FunctorT>
+bool FastHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::upsertCompositeKey(const std::vector<TypedValue> &key,
+                         const uint8_t &initial_value,
+                         FunctorT *functor) {
+  DEBUG_ASSERT(!allow_duplicate_keys);
+  const std::size_t variable_size = calculateVariableLengthCompositeKeyCopySize(key);
+  if (resizable) {
+    for (;;) {
+      {
+        SpinSharedMutexSharedLock<true> resize_lock(resize_shared_mutex_);
+        uint8_t *value = upsertCompositeKeyInternal(key, variable_size, initial_value);
+        if (value 

<TRUNCATED>


[09/30] incubator-quickstep git commit: Added the Date Type.

Posted by ra...@apache.org.
Added the Date Type.


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

Branch: refs/heads/quickstep-28-29
Commit: 9a398d3ff76ab66b4eadf4d864a9fa10feb83336
Parents: 2cc7880
Author: Zuyu Zhang <zu...@twitter.com>
Authored: Fri Jul 15 10:50:08 2016 -0700
Committer: Zuyu Zhang <zu...@twitter.com>
Committed: Sat Jul 16 15:08:00 2016 -0700

----------------------------------------------------------------------
 third_party/cpplint/cpplint.py    |   3 +-
 third_party/iwyu/iwyu_helper.py   |   1 +
 types/CMakeLists.txt              |  27 ++++++
 types/DateType.cpp                | 148 +++++++++++++++++++++++++++++++++
 types/DateType.hpp                | 132 +++++++++++++++++++++++++++++
 types/DatetimeLit.hpp             |  61 ++++++++++++++
 types/Type.cpp                    |   3 +
 types/Type.hpp                    |   5 +-
 types/Type.proto                  |   1 +
 types/TypeFactory.cpp             |   7 ++
 types/TypeFactory.hpp             |   1 +
 types/TypeID.cpp                  |   1 +
 types/TypeID.hpp                  |   1 +
 types/TypedValue.cpp              |  18 ++++
 types/TypedValue.hpp              |  22 +++++
 types/TypedValue.proto            |   8 ++
 types/tests/DateType_unittest.cpp | 146 ++++++++++++++++++++++++++++++++
 types/tests/TypeTest_common.hpp   |   8 +-
 18 files changed, 587 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/9a398d3f/third_party/cpplint/cpplint.py
----------------------------------------------------------------------
diff --git a/third_party/cpplint/cpplint.py b/third_party/cpplint/cpplint.py
index 28b8afa..469283f 100755
--- a/third_party/cpplint/cpplint.py
+++ b/third_party/cpplint/cpplint.py
@@ -3,6 +3,7 @@
 # Original version: svn revision 141
 #
 # This file modified for quickstep as follows:
+#   - Allow no copyright message at the top of the file.
 #   - Allow line length up to 120 characters by default.
 #   - Recognize .hpp files as C++ source.
 #   - Allow use of C++11 <chrono> header.
@@ -5982,7 +5983,7 @@ def ProcessFileData(filename, file_extension, lines, error,
 
   ResetNolintSuppressions()
 
-  CheckForCopyright(filename, lines, error)
+  # CheckForCopyright(filename, lines, error)
 
   RemoveMultiLineComments(filename, lines, error)
   clean_lines = CleansedLines(lines)

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/9a398d3f/third_party/iwyu/iwyu_helper.py
----------------------------------------------------------------------
diff --git a/third_party/iwyu/iwyu_helper.py b/third_party/iwyu/iwyu_helper.py
index 13697be..dff4d55 100755
--- a/third_party/iwyu/iwyu_helper.py
+++ b/third_party/iwyu/iwyu_helper.py
@@ -25,6 +25,7 @@ QUICKSTEP_INCLUDES = [ '.',
                        './third_party/benchmark/include',
                        './third_party/glog/src',
                        './third_party/googletest/googletest/include',
+                       './third_party/protobuf/src',
                        './third_party/re2',
                        './third_party/tmb/include']
 QUICKSTEP_DEFINES = [ '-DQUICKSTEP_DEBUG',

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/9a398d3f/types/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/types/CMakeLists.txt b/types/CMakeLists.txt
index 0ccdfd7..420891a 100644
--- a/types/CMakeLists.txt
+++ b/types/CMakeLists.txt
@@ -32,6 +32,7 @@ QS_PROTOBUF_GENERATE_CPP(types_Type_proto_srcs types_Type_proto_hdrs Type.proto)
 # Declare micro-libs:
 add_library(quickstep_types_CharType CharType.cpp CharType.hpp)
 add_library(quickstep_types_DateOperatorOverloads ../empty_src.cpp DateOperatorOverloads.hpp)
+add_library(quickstep_types_DateType DateType.cpp DateType.hpp)
 add_library(quickstep_types_DatetimeIntervalType DatetimeIntervalType.cpp DatetimeIntervalType.hpp)
 add_library(quickstep_types_DatetimeLit ../empty_src.cpp DatetimeLit.hpp)
 add_library(quickstep_types_DatetimeType DatetimeType.cpp DatetimeType.hpp)
@@ -71,6 +72,15 @@ target_link_libraries(quickstep_types_DateOperatorOverloads
                       quickstep_types_IntervalLit
                       quickstep_types_port_gmtime_r
                       quickstep_types_port_timegm)
+target_link_libraries(quickstep_types_DateType
+                      glog
+                      quickstep_types_DatetimeLit
+                      quickstep_types_NullCoercibilityCheckMacro
+                      quickstep_types_Type
+                      quickstep_types_TypeID
+                      quickstep_types_TypedValue
+                      quickstep_utility_CheckSnprintf
+                      quickstep_utility_Macros)
 target_link_libraries(quickstep_types_DatetimeIntervalType
                       glog
                       quickstep_types_IntervalLit
@@ -156,6 +166,7 @@ target_link_libraries(quickstep_types_Type
 target_link_libraries(quickstep_types_TypeFactory
                       glog
                       quickstep_types_CharType
+                      quickstep_types_DateType
                       quickstep_types_DatetimeIntervalType
                       quickstep_types_DatetimeType
                       quickstep_types_DoubleType
@@ -210,6 +221,7 @@ add_library(quickstep_types ../empty_src.cpp TypesModule.hpp)
 target_link_libraries(quickstep_types
                       quickstep_types_CharType
                       quickstep_types_DateOperatorOverloads
+                      quickstep_types_DateType
                       quickstep_types_DatetimeIntervalType
                       quickstep_types_DatetimeLit
                       quickstep_types_DatetimeType
@@ -264,6 +276,21 @@ target_link_libraries(CharType_unittest
                       quickstep_utility_MemStream)
 add_test(CharType_unittest CharType_unittest)
 
+add_executable(DateType_unittest "${CMAKE_CURRENT_SOURCE_DIR}/tests/DateType_unittest.cpp")
+target_link_libraries(DateType_unittest
+                      gtest
+                      gtest_main
+                      quickstep_types_DatetimeLit
+                      quickstep_types_DateType
+                      quickstep_types_NullType
+                      quickstep_types_Type
+                      quickstep_types_TypeFactory
+                      quickstep_types_TypeID
+                      quickstep_types_TypedValue
+                      quickstep_types_tests_TypeTest_common
+                      quickstep_utility_MemStream)
+add_test(DateType_unittest DateType_unittest)
+
 add_executable(DatetimeIntervalType_unittest
                "${CMAKE_CURRENT_SOURCE_DIR}/tests/DatetimeIntervalType_unittest.cpp")
 target_link_libraries(DatetimeIntervalType_unittest

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/9a398d3f/types/DateType.cpp
----------------------------------------------------------------------
diff --git a/types/DateType.cpp b/types/DateType.cpp
new file mode 100644
index 0000000..9637acc
--- /dev/null
+++ b/types/DateType.cpp
@@ -0,0 +1,148 @@
+/**
+ *   Licensed 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.
+ **/
+
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS
+#endif
+
+#include "types/DateType.hpp"
+
+#include <cinttypes>
+#include <cstddef>
+#include <cstdint>
+#include <cstdio>
+#include <string>
+
+#include "types/DatetimeLit.hpp"
+#include "types/NullCoercibilityCheckMacro.hpp"
+#include "types/Type.hpp"
+#include "types/TypeID.hpp"
+#include "types/TypedValue.hpp"
+#include "utility/CheckSnprintf.hpp"
+
+#include "glog/logging.h"
+
+// NetBSD's libc has snprintf, but it doesn't show up in the std namespace for
+// C++.
+#ifndef __NetBSD__
+using std::snprintf;
+#endif
+
+namespace quickstep {
+
+bool DateType::isCoercibleFrom(const Type &original_type) const {
+  QUICKSTEP_NULL_COERCIBILITY_CHECK();
+  return (original_type.getTypeID() == kDate);
+}
+
+bool DateType::isSafelyCoercibleFrom(const Type &original_type) const {
+  QUICKSTEP_NULL_COERCIBILITY_CHECK();
+  return (original_type.getTypeID() == kDate);
+}
+
+std::string DateType::printValueToString(const TypedValue &value) const {
+  DCHECK(!value.isNull());
+
+  const DateLit literal = value.getLiteral<DateLit>();
+  const std::int32_t year = literal.year;
+
+  char datebuf[DateLit::kIsoChars + 1];
+  std::size_t chars_written = 0;
+  int snprintf_result = 0;
+
+  // ISO 8601 requires that "expanded" year ranges (> 4 digits or before year
+  // 0) are prefixed with a plus or minus.
+  if ((year > 9999) || (year < 0)) {
+    snprintf_result = snprintf(datebuf, sizeof(datebuf), "%+05" PRId32 "-", year);
+  } else {
+    snprintf_result = snprintf(datebuf, sizeof(datebuf), "%04" PRId32 "-", year);
+  }
+  CheckSnprintf(snprintf_result, sizeof(datebuf), &chars_written);
+
+  // All the rest of the ISO 8601 date/time parts.
+  snprintf_result = snprintf(datebuf + chars_written,
+                             sizeof(datebuf) - chars_written,
+                             "%02d-%02d",
+                             literal.month,
+                             literal.day);
+  CheckSnprintf(snprintf_result, sizeof(datebuf), &chars_written);
+
+  return std::string(datebuf);
+}
+
+void DateType::printValueToFile(const TypedValue &value,
+                                FILE *file,
+                                const int padding) const {
+  // We simply re-use the logic from printValueToString(), as trying to do
+  // padding on-the fly with so many different fields is too much of a hassle.
+  std::fprintf(file, "%*s", padding, printValueToString(value).c_str());
+}
+
+bool DateType::parseValueFromString(const std::string &value_string,
+                                    TypedValue *value) const {
+  std::int32_t year;
+  std::uint32_t month, day;
+
+  int date_chars = 0;
+
+  const int matched = std::sscanf(value_string.c_str(),
+                                  "%d-%2u-%2u%n",
+                                  &year, &month, &day, &date_chars);
+
+  // Check that the string is date, then check that there is no unmatched
+  // garbage at the end of the string.
+  if (matched != 3 ||
+      (static_cast<std::string::size_type>(date_chars) != value_string.length())) {
+    return false;
+  }
+
+  // Validate month.
+  if ((month == 0) || (month > 12)) {
+    return false;
+  }
+
+  // Validate day-of-month.
+  if (day == 0) {
+    return false;
+  }
+  switch (month) {
+    case 2: {
+      const std::uint32_t days_in_february
+          = ((year & 3) == 0 && ((year % 25) != 0 || (year & 15) == 0))
+            ? 29 : 28;
+      if (day > days_in_february) {
+        return false;
+      }
+      break;
+    }
+    case 4:  // Fall-through for 30-day months.
+    case 6:
+    case 9:
+    case 11:
+      if (day > 30) {
+        return false;
+      }
+      break;
+    default:
+      if (day > 31) {
+        return false;
+      }
+      break;
+  }
+
+  *value = TypedValue(DateLit::Create(year, month, day));
+  return true;
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/9a398d3f/types/DateType.hpp
----------------------------------------------------------------------
diff --git a/types/DateType.hpp b/types/DateType.hpp
new file mode 100644
index 0000000..2a13be1
--- /dev/null
+++ b/types/DateType.hpp
@@ -0,0 +1,132 @@
+/**
+ *   Licensed 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.
+ **/
+
+#ifndef QUICKSTEP_TYPES_DATE_TYPE_HPP_
+#define QUICKSTEP_TYPES_DATE_TYPE_HPP_
+
+#include <cstddef>
+#include <cstdio>
+#include <string>
+
+#include "types/DatetimeLit.hpp"
+#include "types/Type.hpp"
+#include "types/TypeID.hpp"
+#include "utility/Macros.hpp"
+
+namespace quickstep {
+
+class TypedValue;
+
+/** \addtogroup Types
+ *  @{
+ */
+
+/**
+ * @brief A type representing the date.
+ **/
+class DateType : public Type {
+ public:
+  typedef DateLit cpptype;
+
+  static const TypeID kStaticTypeID = kDate;
+
+  /**
+   * @brief Get a reference to the non-nullable singleton instance of this
+   *        Type.
+   *
+   * @return A reference to the non-nullable singleton instance of this Type.
+   **/
+  static const DateType& InstanceNonNullable() {
+    static DateType instance(false);
+    return instance;
+  }
+
+  /**
+   * @brief Get a reference to the nullable singleton instance of this Type.
+   *
+   * @return A reference to the nullable singleton instance of this Type.
+   **/
+  static const DateType& InstanceNullable() {
+    static DateType instance(true);
+    return instance;
+  }
+
+  /**
+   * @brief Get a reference to a singleton instance of this Type.
+   *
+   * @param nullable Whether to get the nullable version of this Type.
+   * @return A reference to the desired singleton instance of this Type.
+   **/
+  static const DateType& Instance(const bool nullable) {
+    if (nullable) {
+      return InstanceNullable();
+    } else {
+      return InstanceNonNullable();
+    }
+  }
+
+  const Type& getNullableVersion() const override {
+    return InstanceNullable();
+  }
+
+  const Type& getNonNullableVersion() const override {
+    return InstanceNonNullable();
+  }
+
+  std::size_t estimateAverageByteLength() const override {
+    return sizeof(DateLit);
+  }
+
+  bool isCoercibleFrom(const Type &original_type) const override;
+
+  bool isSafelyCoercibleFrom(const Type &original_type) const override;
+
+  int getPrintWidth() const override {
+    return DateLit::kIsoChars;
+  }
+
+  std::string printValueToString(const TypedValue &value) const override;
+
+  void printValueToFile(const TypedValue &value,
+                        FILE *file,
+                        const int padding = 0) const override;
+
+  /**
+   * @note value_string is expected to be in (possibly extended) ISO-8601
+   *       format. Extended ISO-8601 date format is "YYYY-MM-DD". YYYY is an
+   *       integer year (in extended format, this may be more than 4 digits and
+   *       include an optional sign prefix +/-). MM is a 2-digit month in the
+   *       range 01-12. DD is a 2-digit day of month in the appropriate range
+   *       for the month (i.e. 28 days for February in non-leap years, 29 days
+   *       for February in leap years, 30 days for April, June, September, and
+   *       November, and 31 days for all other months). Note that parsing will
+   *       fail if there are any "extra" characters at the end of the string
+   *       after a parsable ISO-8601 date.
+   **/
+  bool parseValueFromString(const std::string &value_string,
+                            TypedValue *value) const override;
+
+ private:
+  explicit DateType(const bool nullable)
+      : Type(Type::kOther, kDate, nullable, sizeof(DateLit), sizeof(DateLit)) {
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(DateType);
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_TYPES_DATE_TYPE_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/9a398d3f/types/DatetimeLit.hpp
----------------------------------------------------------------------
diff --git a/types/DatetimeLit.hpp b/types/DatetimeLit.hpp
index 017bcce..c87b510 100644
--- a/types/DatetimeLit.hpp
+++ b/types/DatetimeLit.hpp
@@ -31,6 +31,67 @@ namespace quickstep {
  */
 
 /**
+ * @brief A literal representing the date.
+ **/
+struct DateLit {
+  // Note that although there is no year 0 in the Gregorian calendar, ISO 8601
+  // has year 0 equivalent to 1 BCE, year -1 equivalent to 2 BCE, and so on.
+  std::int32_t year;
+  std::uint8_t month, day;
+
+  // The maximum number of characters needed to represent any date in ISO 8601
+  // notation.
+  static constexpr int kIsoChars
+      = 1     // + or - prefix
+        + 5   // Year digits
+        + 1   // -
+        + 2   // Month
+        + 1   // -
+        + 2;  // Day
+
+  static DateLit Create(const std::int32_t _year,
+                        const std::uint8_t _month,
+                        const std::uint8_t _day) {
+    DateLit date;
+    date.year = _year;
+    date.month = _month;
+    date.day = _day;
+
+    return date;
+  }
+
+  inline bool operator< (const DateLit& rhs) const {
+    return (year != rhs.year)
+        ? (year < rhs.year)
+        : ((month != rhs.month) ? (month < rhs.month) : (day < rhs.day));
+  }
+
+  inline bool operator> (const DateLit& rhs) const {
+    return (year != rhs.year)
+        ? (year > rhs.year)
+        : ((month != rhs.month) ? (month > rhs.month) : (day > rhs.day));
+  }
+
+  inline bool operator<=(const DateLit& rhs) const {
+    return !(*this > rhs);
+  }
+
+  inline bool operator>=(const DateLit& rhs) const {
+    return !(*this < rhs);
+  }
+
+  inline bool operator==(const DateLit& rhs) const {
+    return (year == rhs.year) &&
+           (month == rhs.month) &&
+           (day == rhs.day);
+  }
+
+  inline bool operator!=(const DateLit& rhs) const {
+    return !(*this == rhs);
+  }
+};
+
+/**
  * @brief A literal representing the datetime.
  **/
 struct DatetimeLit {

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/9a398d3f/types/Type.cpp
----------------------------------------------------------------------
diff --git a/types/Type.cpp b/types/Type.cpp
index 8981bc2..b89b2ac 100644
--- a/types/Type.cpp
+++ b/types/Type.cpp
@@ -41,6 +41,9 @@ serialization::Type Type::getProto() const {
     case kDouble:
       proto.set_type_id(serialization::Type::DOUBLE);
       break;
+    case kDate:
+      proto.set_type_id(serialization::Type::DATE);
+      break;
     case kDatetime:
       proto.set_type_id(serialization::Type::DATETIME);
       break;

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/9a398d3f/types/Type.hpp
----------------------------------------------------------------------
diff --git a/types/Type.hpp b/types/Type.hpp
index fc6f4e2..84a0de0 100644
--- a/types/Type.hpp
+++ b/types/Type.hpp
@@ -32,6 +32,7 @@
 
 namespace quickstep {
 
+struct DateLit;
 struct DatetimeIntervalLit;
 struct DatetimeLit;
 struct YearMonthIntervalLit;
@@ -95,7 +96,7 @@ class Type {
   enum SuperTypeID {
     kNumeric = 0,  // Fixed-length numeric types (Int, Long, Float, Double)
     kAsciiString,  // ASCII strings (Char, VarChar)
-    kOther         // Others (Datetime, DatetimeInterval, YearMonthInterval)
+    kOther         // Others (Date, Datetime, DatetimeInterval, YearMonthInterval)
   };
 
   /**
@@ -370,6 +371,8 @@ class Type {
         return TypedValue(*static_cast<const float*>(value_ptr));
       case kDouble:
         return TypedValue(*static_cast<const double*>(value_ptr));
+      case kDate:
+        return TypedValue(*static_cast<const DateLit*>(value_ptr));
       case kDatetime:
         return TypedValue(*static_cast<const DatetimeLit*>(value_ptr));
       case kDatetimeInterval:

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/9a398d3f/types/Type.proto
----------------------------------------------------------------------
diff --git a/types/Type.proto b/types/Type.proto
index dbf248e..cfcb713 100644
--- a/types/Type.proto
+++ b/types/Type.proto
@@ -29,6 +29,7 @@ message Type {
     DATETIME_INTERVAL = 7;
     YEAR_MONTH_INTERVAL = 8;
     NULL_TYPE = 9;
+    DATE = 10;
   }
 
   required TypeID type_id = 1;

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/9a398d3f/types/TypeFactory.cpp
----------------------------------------------------------------------
diff --git a/types/TypeFactory.cpp b/types/TypeFactory.cpp
index a8f2961..3709a50 100644
--- a/types/TypeFactory.cpp
+++ b/types/TypeFactory.cpp
@@ -18,8 +18,10 @@
 #include "types/TypeFactory.hpp"
 
 #include <cstddef>
+#include <string>
 
 #include "types/CharType.hpp"
+#include "types/DateType.hpp"
 #include "types/DatetimeIntervalType.hpp"
 #include "types/DatetimeType.hpp"
 #include "types/DoubleType.hpp"
@@ -49,6 +51,8 @@ const Type& TypeFactory::GetType(const TypeID id,
       return FloatType::Instance(nullable);
     case kDouble:
       return DoubleType::Instance(nullable);
+    case kDate:
+      return DateType::Instance(nullable);
     case kDatetime:
       return DatetimeType::Instance(nullable);
     case kDatetimeInterval:
@@ -90,6 +94,7 @@ bool TypeFactory::ProtoIsValid(const serialization::Type &proto) {
     case serialization::Type::LONG:
     case serialization::Type::FLOAT:
     case serialization::Type::DOUBLE:
+    case serialization::Type::DATE:
     case serialization::Type::DATETIME:
     case serialization::Type::DATETIME_INTERVAL:
     case serialization::Type::YEAR_MONTH_INTERVAL:
@@ -119,6 +124,8 @@ const Type& TypeFactory::ReconstructFromProto(const serialization::Type &proto)
       return FloatType::Instance(proto.nullable());
     case serialization::Type::DOUBLE:
       return DoubleType::Instance(proto.nullable());
+    case serialization::Type::DATE:
+      return DateType::Instance(proto.nullable());
     case serialization::Type::DATETIME:
       return DatetimeType::Instance(proto.nullable());
     case serialization::Type::DATETIME_INTERVAL:

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/9a398d3f/types/TypeFactory.hpp
----------------------------------------------------------------------
diff --git a/types/TypeFactory.hpp b/types/TypeFactory.hpp
index ed9be5e..e63a613 100644
--- a/types/TypeFactory.hpp
+++ b/types/TypeFactory.hpp
@@ -52,6 +52,7 @@ class TypeFactory {
       case kLong:
       case kFloat:
       case kDouble:
+      case kDate:
       case kDatetime:
       case kDatetimeInterval:
       case kYearMonthInterval:

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/9a398d3f/types/TypeID.cpp
----------------------------------------------------------------------
diff --git a/types/TypeID.cpp b/types/TypeID.cpp
index 1aeea04..6a5a4c7 100644
--- a/types/TypeID.cpp
+++ b/types/TypeID.cpp
@@ -26,6 +26,7 @@ const char *kTypeNames[] = {
   "Double",
   "Char",
   "VarChar",
+  "Date",
   "Datetime",
   "DatetimeInterval",
   "YearMonthInterval",

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/9a398d3f/types/TypeID.hpp
----------------------------------------------------------------------
diff --git a/types/TypeID.hpp b/types/TypeID.hpp
index 23d32da..069e9c0 100644
--- a/types/TypeID.hpp
+++ b/types/TypeID.hpp
@@ -34,6 +34,7 @@ enum TypeID {
   kDouble,
   kChar,
   kVarChar,
+  kDate,
   kDatetime,
   kDatetimeInterval,
   kYearMonthInterval,

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/9a398d3f/types/TypedValue.cpp
----------------------------------------------------------------------
diff --git a/types/TypedValue.cpp b/types/TypedValue.cpp
index bc1ebd9..cef7cb0 100644
--- a/types/TypedValue.cpp
+++ b/types/TypedValue.cpp
@@ -49,6 +49,7 @@ bool TypedValue::isPlausibleInstanceOf(const TypeSignature type) const {
     case kLong:
     case kFloat:
     case kDouble:
+    case kDate:
     case kDatetime:
     case kDatetimeInterval:
     case kYearMonthInterval:
@@ -104,6 +105,15 @@ serialization::TypedValue TypedValue::getProto() const {
         proto.set_double_value(getLiteral<double>());
       }
       break;
+    case kDate:
+      proto.set_type_id(serialization::Type::DATE);
+      if (!isNull()) {
+        serialization::TypedValue::DateLit *literal_date_proto = proto.mutable_date_value();
+        literal_date_proto->set_year(value_union_.date_value.year);
+        literal_date_proto->set_month(value_union_.date_value.month);
+        literal_date_proto->set_day(value_union_.date_value.day);
+      }
+      break;
     case kDatetime:
       proto.set_type_id(serialization::Type::DATETIME);
       if (!isNull()) {
@@ -171,6 +181,14 @@ TypedValue TypedValue::ReconstructFromProto(const serialization::TypedValue &pro
       return proto.has_double_value() ?
           TypedValue(static_cast<double>(proto.double_value())) :
           TypedValue(kDouble);
+    case serialization::Type::DATE:
+      if (proto.has_date_value()) {
+        return TypedValue(DateLit::Create(proto.date_value().year(),
+                                          proto.date_value().month(),
+                                          proto.date_value().day()));
+      } else {
+        return TypedValue(kDate);
+      }
     case serialization::Type::DATETIME:
       if (proto.has_datetime_value()) {
         DatetimeLit datetime;

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/9a398d3f/types/TypedValue.hpp
----------------------------------------------------------------------
diff --git a/types/TypedValue.hpp b/types/TypedValue.hpp
index 6e22111..10c3c05 100644
--- a/types/TypedValue.hpp
+++ b/types/TypedValue.hpp
@@ -127,6 +127,14 @@ class TypedValue {
   }
 
   /**
+   * @brief Constructor for a literal value of DateType.
+   **/
+  explicit TypedValue(const DateLit literal_date)
+      : value_info_(static_cast<std::uint64_t>(kDate)) {
+    value_union_.date_value = literal_date;
+  }
+
+  /**
    * @brief Constructor for a literal value of DatetimeType.
    **/
   explicit TypedValue(const DatetimeLit literal_datetime)
@@ -276,6 +284,7 @@ class TypedValue {
       case kLong:
       case kFloat:
       case kDouble:
+      case kDate:
       case kDatetime:
       case kDatetimeInterval:
       case kYearMonthInterval:
@@ -307,6 +316,7 @@ class TypedValue {
         return sizeof(value_union_.int_value) <= sizeof(std::size_t);
       case kLong:
       case kDouble:
+      case kDate:
       case kDatetime:
       case kDatetimeInterval:
       case kYearMonthInterval:
@@ -384,6 +394,7 @@ class TypedValue {
         return sizeof(int);
       case kLong:
       case kDouble:
+      case kDate:
       case kDatetime:
       case kDatetimeInterval:
       case kYearMonthInterval:
@@ -469,6 +480,7 @@ class TypedValue {
                    || getTypeID() == kLong
                    || getTypeID() == kFloat
                    || getTypeID() == kDouble
+                   || getTypeID() == kDate
                    || getTypeID() == kDatetime
                    || getTypeID() == kDatetimeInterval
                    || getTypeID() == kYearMonthInterval));
@@ -564,6 +576,7 @@ class TypedValue {
       case kLong:
       case kFloat:
       case kDouble:
+      case kDate:
       case kDatetime:
       case kDatetimeInterval:
       case kYearMonthInterval:
@@ -659,6 +672,7 @@ class TypedValue {
       case kLong:
       case kFloat:
       case kDouble:
+      case kDate:
       case kDatetime:
       case kDatetimeInterval:
       case kYearMonthInterval:
@@ -778,6 +792,7 @@ class TypedValue {
     float float_value;
     double double_value;
     const void* out_of_line_data;
+    DateLit date_value;
     DatetimeLit datetime_value;
     DatetimeIntervalLit datetime_interval_value;
     YearMonthIntervalLit year_month_interval_value;
@@ -853,6 +868,13 @@ inline double TypedValue::getLiteral<double>() const {
 }
 
 template <>
+inline DateLit TypedValue::getLiteral<DateLit>() const {
+  DCHECK_EQ(kDate, getTypeID());
+  DCHECK(!isNull());
+  return value_union_.date_value;
+}
+
+template <>
 inline DatetimeLit TypedValue::getLiteral<DatetimeLit>() const {
   DCHECK_EQ(kDatetime, getTypeID());
   DCHECK(!isNull());

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/9a398d3f/types/TypedValue.proto
----------------------------------------------------------------------
diff --git a/types/TypedValue.proto b/types/TypedValue.proto
index 78a38cb..c265c73 100644
--- a/types/TypedValue.proto
+++ b/types/TypedValue.proto
@@ -31,4 +31,12 @@ message TypedValue {
   optional int64 datetime_value = 7;
   optional int64 datetime_interval_value = 8;
   optional int64 year_month_interval_value = 9;
+
+  message DateLit {
+    required int32 year = 1;
+    required uint32 month = 2;
+    required uint32 day = 3;
+  }
+
+  optional DateLit date_value = 10;
 }

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/9a398d3f/types/tests/DateType_unittest.cpp
----------------------------------------------------------------------
diff --git a/types/tests/DateType_unittest.cpp b/types/tests/DateType_unittest.cpp
new file mode 100644
index 0000000..637f369
--- /dev/null
+++ b/types/tests/DateType_unittest.cpp
@@ -0,0 +1,146 @@
+/**
+ *   Licensed 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.
+ **/
+
+#include <cstddef>
+#include <string>
+
+#include "types/DateType.hpp"
+#include "types/DatetimeLit.hpp"
+#include "types/NullType.hpp"
+#include "types/Type.hpp"
+#include "types/TypeFactory.hpp"
+#include "types/TypeID.hpp"
+#include "types/TypedValue.hpp"
+#include "types/tests/TypeTest_common.hpp"
+#include "utility/MemStream.hpp"
+
+#include "gtest/gtest.h"
+
+namespace quickstep {
+
+TEST(DateTypeTest, PrintValueTest) {
+  const Type &date_type = TypeFactory::GetType(kDate);
+
+  // Skynet becomes self-aware at 2:14 AM on August 29, 1997.
+  TypedValue judgement_day_value(DateLit::Create(1997, 8, 29));
+  EXPECT_EQ(std::string("1997-08-29"),
+            date_type.printValueToString(judgement_day_value));
+
+  // Check extended formatting for years far in the past.
+  TypedValue dawn_age_value(DateLit::Create(-18017, 4, 13));
+  EXPECT_EQ(std::string("-18017-04-13"),
+            date_type.printValueToString(dawn_age_value));
+}
+
+namespace {
+
+static const std::size_t kPaddedLength = 200;
+
+void CheckPrintValueToFile(const DateLit literal_value,
+                           const std::string &expected_string) {
+  // Test writing to FILE* with a MemStream.
+  MemStream memstream;
+
+  const Type &date_type = TypeFactory::GetType(kDate);
+  const TypedValue date_value = date_type.makeValue(&literal_value);
+
+  // First, check printing without any padding
+  date_type.printValueToFile(date_value, memstream.file());
+  EXPECT_STREQ(expected_string.c_str(), memstream.str());
+  memstream.reset();
+
+  // Specifying padding less than the length needed to print the value should
+  // have no effect.
+  date_type.printValueToFile(date_value, memstream.file(), 1);
+  EXPECT_STREQ(expected_string.c_str(), memstream.str());
+  memstream.reset();
+
+  // Test padding up to a specified length.
+  std::string padded(kPaddedLength - expected_string.size(), ' ');
+  padded.append(expected_string);
+  date_type.printValueToFile(date_value, memstream.file(), kPaddedLength);
+  EXPECT_STREQ(padded.c_str(), memstream.str());
+  memstream.reset();
+}
+
+}  // namespace
+
+TEST(DateTypeTest, PrintValueToFileTest) {
+  CheckPrintValueToFile(DateLit::Create(1997, 8, 29), "1997-08-29");
+
+  CheckPrintValueToFile(DateLit::Create(-18017, 4, 13), "-18017-04-13");
+}
+
+TEST(DateTypeTest, ParseValueFromStringTest) {
+  // Parse the same value in a bunch of different valid format variations.
+  type_test::CheckSuccessfulParseLiteralValueFromString<DateType>(
+      DateLit::Create(1997, 8, 29),
+      "1997-08-29");
+  type_test::CheckSuccessfulParseLiteralValueFromString<DateType>(
+      DateLit::Create(1997, 8, 29),
+      "+1997-08-29");
+  type_test::CheckSuccessfulParseLiteralValueFromString<DateType>(
+      DateLit::Create(1997, 8, 29),
+      "+001997-08-29");
+
+  type_test::CheckSuccessfulParseLiteralValueFromString<DateType>(
+      DateLit::Create(1871, 3, 18),
+      "1871-03-18");
+  type_test::CheckSuccessfulParseLiteralValueFromString<DateType>(
+      DateLit::Create(1871, 3, 18),
+      "+1871-03-18");
+  type_test::CheckSuccessfulParseLiteralValueFromString<DateType>(
+      DateLit::Create(1871, 3, 18),
+      "+001871-03-18");
+
+  // Parse extended format with negative year.
+  type_test::CheckSuccessfulParseLiteralValueFromString<DateType>(
+      DateLit::Create(-18017, 4, 13),
+      "-18017-04-13");
+
+  // Test some parses that we expect to fail.
+  const Type &date_type = TypeFactory::GetType(kDate);
+  TypedValue value;
+  EXPECT_FALSE(date_type.parseValueFromString("foo", &value));
+  EXPECT_FALSE(date_type.parseValueFromString("42", &value));
+  EXPECT_FALSE(date_type.parseValueFromString("1997-08-29 ", &value));
+  EXPECT_FALSE(date_type.parseValueFromString(" 1997-08-29 ", &value));
+  EXPECT_FALSE(date_type.parseValueFromString("1997-08-29.", &value));
+  EXPECT_FALSE(date_type.parseValueFromString("1997-08-29q", &value));
+  EXPECT_FALSE(date_type.parseValueFromString("1997-08-29.q", &value));
+  EXPECT_FALSE(date_type.parseValueFromString("1e3-08-29", &value));
+  EXPECT_FALSE(date_type.parseValueFromString("--18017-04-13", &value));
+  EXPECT_FALSE(date_type.parseValueFromString("1997-08--29 ", &value));
+  EXPECT_FALSE(date_type.parseValueFromString("1997-08--2 ", &value));
+}
+
+TEST(DateTypeTest, IsCoercibleFromTest) {
+  type_test::CheckIsCoercibleFrom(DateType::InstanceNonNullable(),
+                                  {kDate});
+}
+
+TEST(DateTypeTest, IsSafelyCoercibleFromTest) {
+  type_test::CheckIsSafelyCoercibleFrom(DateType::InstanceNonNullable(),
+                                        {kDate});
+}
+
+TEST(DateTypeTest, CoerceValueTest) {
+  // The epoch.
+  type_test::CheckLiteralCoerce<DateType, DateType>(DateLit::Create(1970, 1, 1));
+
+  type_test::CheckNullLiteralCoerce<DateType, DateType>();
+  type_test::CheckNullLiteralCoerce<DateType, NullType>();
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/9a398d3f/types/tests/TypeTest_common.hpp
----------------------------------------------------------------------
diff --git a/types/tests/TypeTest_common.hpp b/types/tests/TypeTest_common.hpp
index 7cc9cc2..d185405 100644
--- a/types/tests/TypeTest_common.hpp
+++ b/types/tests/TypeTest_common.hpp
@@ -101,7 +101,7 @@ void CheckIsCoercibleFrom(
   // Can't coerce from other types.
   for (const TypeID original_type_id
        : {kInt, kLong, kFloat, kDouble, kChar, kVarChar,
-          kDatetime, kDatetimeInterval, kYearMonthInterval}) {
+          kDate, kDatetime, kDatetimeInterval, kYearMonthInterval}) {
     if (std::find(expected_coercible.begin(), expected_coercible.end(), original_type_id)
         == expected_coercible.end()) {
       const Type &original_type = TypeFactory::TypeRequiresLengthParameter(original_type_id)
@@ -183,7 +183,7 @@ void CheckStringTypeIsCoercibleFrom(const TypeID target_type_id,
 
   // Other types are not coercible to strings.
   for (const TypeID original_type_id
-       : {kInt, kLong, kFloat, kDouble, kDatetime, kDatetimeInterval, kYearMonthInterval}) {
+       : {kInt, kLong, kFloat, kDouble, kDate, kDatetime, kDatetimeInterval, kYearMonthInterval}) {
     const Type &original_type = TypeFactory::TypeRequiresLengthParameter(original_type_id)
                                 ? TypeFactory::GetType(original_type_id, 10, false)
                                 : TypeFactory::GetType(original_type_id, false);
@@ -232,7 +232,7 @@ void CheckIsSafelyCoercibleFrom(
   // Can't coerce from other types.
   for (const TypeID original_type_id
        : {kInt, kLong, kFloat, kDouble, kChar, kVarChar,
-          kDatetime, kDatetimeInterval, kYearMonthInterval}) {
+          kDate, kDatetime, kDatetimeInterval, kYearMonthInterval}) {
     if (std::find(expected_coercible.begin(), expected_coercible.end(), original_type_id)
         == expected_coercible.end()) {
       const Type &original_type = TypeFactory::TypeRequiresLengthParameter(original_type_id)
@@ -314,7 +314,7 @@ void CheckStringTypeIsSafelyCoercibleFrom(const TypeID target_type_id,
 
   // Other types are not coercible to strings.
   for (const TypeID original_type_id
-       : {kInt, kLong, kFloat, kDouble, kDatetime, kDatetimeInterval, kYearMonthInterval}) {
+       : {kInt, kLong, kFloat, kDouble, kDate, kDatetime, kDatetimeInterval, kYearMonthInterval}) {
     const Type &original_type = TypeFactory::TypeRequiresLengthParameter(original_type_id)
                                 ? TypeFactory::GetType(original_type_id, 10, false)
                                 : TypeFactory::GetType(original_type_id, false);



[02/30] incubator-quickstep git commit: Minor changes in profiling work order output.

Posted by ra...@apache.org.
Minor changes in profiling work order output.

- Now prints query ID along with each work order entry.
- Removed spaces between two columns.


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

Branch: refs/heads/quickstep-28-29
Commit: 04c8224b1584b982412c7023d041c1060d5c0342
Parents: 31f1bbb
Author: Harshad Deshmukh <hb...@apache.org>
Authored: Wed Jul 6 11:38:49 2016 -0500
Committer: Harshad Deshmukh <hb...@apache.org>
Committed: Wed Jul 6 22:28:28 2016 -0500

----------------------------------------------------------------------
 query_execution/Foreman.cpp | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/04c8224b/query_execution/Foreman.cpp
----------------------------------------------------------------------
diff --git a/query_execution/Foreman.cpp b/query_execution/Foreman.cpp
index f9f2e7a..98146e2 100644
--- a/query_execution/Foreman.cpp
+++ b/query_execution/Foreman.cpp
@@ -238,16 +238,17 @@ void Foreman::printWorkOrderProfilingResults(const std::size_t query_id,
   const std::vector<
       std::tuple<std::size_t, std::size_t, std::size_t>>
       &recorded_times = policy_enforcer_->getProfilingResults(query_id);
-  fputs("Worker ID, NUMA Socket, Operator ID, Time (microseconds)\n", out);
+  fputs("Query ID,Worker ID,NUMA Socket,Operator ID,Time (microseconds)\n", out);
   for (auto workorder_entry : recorded_times) {
     // Note: Index of the "worker thread index" in the tuple is 0.
     const std::size_t worker_id = std::get<0>(workorder_entry);
     fprintf(out,
-            "%lu, %d, %lu, %lu\n",
+            "%lu,%lu,%d,%lu,%lu\n",
+            query_id,
             worker_id,
             worker_directory_->getNUMANode(worker_id),
-            std::get<1>(workorder_entry),
-            std::get<2>(workorder_entry));
+            std::get<1>(workorder_entry),  // Operator ID.
+            std::get<2>(workorder_entry));  // Time.
   }
 }
 


[15/30] incubator-quickstep git commit: - Supported ROWS mode for AVG window aggregation. - Created WindowAggregateFunctions in expressions/window_aggregation. - Created WindowAggregationHandle for AVG to actually do the calculation. - Other functions wi

Posted by ra...@apache.org.
- Supported ROWS mode for AVG window aggregation.
- Created WindowAggregateFunctions in expressions/window_aggregation.
- Created WindowAggregationHandle for AVG to actually do the calculation.
- Other functions will be supported in future PRs.
- RANGE mode is not supported yet.


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

Branch: refs/heads/quickstep-28-29
Commit: c0bb4620ca7d3e2ba6334efc009376164d2cd6f8
Parents: 2b78380
Author: shixuan-fan <sh...@apache.org>
Authored: Fri Jul 8 18:23:47 2016 +0000
Committer: Zuyu Zhang <zu...@twitter.com>
Committed: Mon Jul 25 13:21:40 2016 -0700

----------------------------------------------------------------------
 expressions/CMakeLists.txt                      |   1 +
 expressions/window_aggregation/CMakeLists.txt   | 206 ++++++++++
 .../WindowAggregateFunction.cpp                 |  58 +++
 .../WindowAggregateFunction.hpp                 | 149 +++++++
 .../WindowAggregateFunction.proto               |  32 ++
 .../WindowAggregateFunctionAvg.cpp              |  85 ++++
 .../WindowAggregateFunctionAvg.hpp              |  75 ++++
 .../WindowAggregateFunctionCount.cpp            |  59 +++
 .../WindowAggregateFunctionCount.hpp            |  75 ++++
 .../WindowAggregateFunctionFactory.cpp          | 106 +++++
 .../WindowAggregateFunctionFactory.hpp          |  96 +++++
 .../WindowAggregateFunctionMax.cpp              |  67 ++++
 .../WindowAggregateFunctionMax.hpp              |  75 ++++
 .../WindowAggregateFunctionMin.cpp              |  66 ++++
 .../WindowAggregateFunctionMin.hpp              |  75 ++++
 .../WindowAggregateFunctionSum.cpp              |  82 ++++
 .../WindowAggregateFunctionSum.hpp              |  75 ++++
 .../WindowAggregationHandle.hpp                 | 137 +++++++
 .../WindowAggregationHandleAvg.cpp              | 241 ++++++++++++
 .../WindowAggregationHandleAvg.hpp              | 101 +++++
 .../window_aggregation/WindowAggregationID.hpp  |  44 +++
 .../WindowAggregationHandleAvg_unittest.cpp     | 387 +++++++++++++++++++
 query_optimizer/CMakeLists.txt                  |   2 +
 query_optimizer/ExecutionGenerator.cpp          |   5 +-
 query_optimizer/expressions/CMakeLists.txt      |   2 +-
 .../expressions/WindowAggregateFunction.cpp     |   4 +-
 .../expressions/WindowAggregateFunction.hpp     |  10 +-
 query_optimizer/resolver/CMakeLists.txt         |   2 +
 query_optimizer/resolver/Resolver.cpp           |  40 +-
 query_optimizer/resolver/Resolver.hpp           |   5 +-
 .../tests/execution_generator/Select.test       |  78 +++-
 relational_operators/CMakeLists.txt             |   1 +
 .../WindowAggregationOperator.cpp               |  15 +-
 .../WindowAggregationOperator.hpp               |  30 +-
 relational_operators/WorkOrder.proto            |   3 +-
 storage/CMakeLists.txt                          |  17 +-
 storage/WindowAggregationOperationState.cpp     | 183 +++++++--
 storage/WindowAggregationOperationState.hpp     |  68 +---
 storage/WindowAggregationOperationState.proto   |   6 +-
 ...WindowAggregationOperationState_unittest.cpp |  14 +-
 40 files changed, 2613 insertions(+), 164 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/expressions/CMakeLists.txt b/expressions/CMakeLists.txt
index 53ad5d4..6ef3c24 100644
--- a/expressions/CMakeLists.txt
+++ b/expressions/CMakeLists.txt
@@ -17,6 +17,7 @@ add_subdirectory(aggregation)
 add_subdirectory(predicate)
 add_subdirectory(scalar)
 add_subdirectory(table_generator)
+add_subdirectory(window_aggregation)
 
 QS_PROTOBUF_GENERATE_CPP(expressions_Expressions_proto_srcs
                          expressions_Expressions_proto_hdrs

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/CMakeLists.txt b/expressions/window_aggregation/CMakeLists.txt
new file mode 100644
index 0000000..6a16fcc
--- /dev/null
+++ b/expressions/window_aggregation/CMakeLists.txt
@@ -0,0 +1,206 @@
+#   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.
+
+QS_PROTOBUF_GENERATE_CPP(expressions_windowaggregation_WindowAggregateFunction_proto_srcs
+                         expressions_windowaggregation_WindowAggregateFunction_proto_hdrs
+                         WindowAggregateFunction.proto)
+
+# Declare micro-libs:
+add_library(quickstep_expressions_windowaggregation_WindowAggregateFunction
+            WindowAggregateFunction.cpp
+            WindowAggregateFunction.hpp)
+add_library(quickstep_expressions_windowaggregation_WindowAggregateFunction_proto
+            ${expressions_windowaggregation_WindowAggregateFunction_proto_srcs})
+add_library(quickstep_expressions_windowaggregation_WindowAggregateFunctionAvg
+            WindowAggregateFunctionAvg.cpp
+            WindowAggregateFunctionAvg.hpp)
+add_library(quickstep_expressions_windowaggregation_WindowAggregateFunctionCount
+            WindowAggregateFunctionCount.cpp
+            WindowAggregateFunctionCount.hpp)
+add_library(quickstep_expressions_windowaggregation_WindowAggregateFunctionFactory
+            WindowAggregateFunctionFactory.cpp
+            WindowAggregateFunctionFactory.hpp)
+add_library(quickstep_expressions_windowaggregation_WindowAggregateFunctionMax
+            WindowAggregateFunctionMax.cpp
+            WindowAggregateFunctionMax.hpp)
+add_library(quickstep_expressions_windowaggregation_WindowAggregateFunctionMin
+            WindowAggregateFunctionMin.cpp
+            WindowAggregateFunctionMin.hpp)
+add_library(quickstep_expressions_windowaggregation_WindowAggregateFunctionSum
+            WindowAggregateFunctionSum.cpp
+            WindowAggregateFunctionSum.hpp)
+add_library(quickstep_expressions_windowaggregation_WindowAggregationHandle
+            ../../empty_src.cpp
+            WindowAggregationHandle.hpp)
+add_library(quickstep_expressions_windowaggregation_WindowAggregationHandleAvg
+            WindowAggregationHandleAvg.cpp
+            WindowAggregationHandleAvg.hpp)
+add_library(quickstep_expressions_windowaggregation_WindowAggregationID
+            ../../empty_src.cpp
+            WindowAggregationID.hpp)
+
+# Link dependencies:
+target_link_libraries(quickstep_expressions_windowaggregation_WindowAggregateFunction
+                      glog
+                      quickstep_catalog_CatalogTypedefs
+                      quickstep_expressions_windowaggregation_WindowAggregateFunction_proto
+                      quickstep_expressions_windowaggregation_WindowAggregationID
+                      quickstep_storage_StorageBlockInfo
+                      quickstep_utility_Macros)
+target_link_libraries(quickstep_expressions_windowaggregation_WindowAggregateFunction_proto
+                      ${PROTOBUF_LIBRARY})
+target_link_libraries(quickstep_expressions_windowaggregation_WindowAggregateFunctionAvg
+                      glog
+                      quickstep_expressions_windowaggregation_WindowAggregateFunction
+                      quickstep_expressions_windowaggregation_WindowAggregationHandleAvg
+                      quickstep_expressions_windowaggregation_WindowAggregationID
+                      quickstep_types_Type
+                      quickstep_types_TypeFactory
+                      quickstep_types_TypeID
+                      quickstep_types_operations_binaryoperations_BinaryOperation
+                      quickstep_types_operations_binaryoperations_BinaryOperationFactory
+                      quickstep_types_operations_binaryoperations_BinaryOperationID
+                      quickstep_utility_Macros)
+target_link_libraries(quickstep_expressions_windowaggregation_WindowAggregateFunctionCount
+                      glog
+                      quickstep_expressions_windowaggregation_WindowAggregateFunction
+                      quickstep_expressions_windowaggregation_WindowAggregationHandle
+                      quickstep_expressions_windowaggregation_WindowAggregationID
+                      quickstep_types_Type
+                      quickstep_types_TypeFactory
+                      quickstep_types_TypeID
+                      quickstep_utility_Macros)
+target_link_libraries(quickstep_expressions_windowaggregation_WindowAggregateFunctionFactory
+                      glog
+                      quickstep_expressions_windowaggregation_WindowAggregateFunctionAvg
+                      quickstep_expressions_windowaggregation_WindowAggregateFunctionCount
+                      quickstep_expressions_windowaggregation_WindowAggregateFunctionMax
+                      quickstep_expressions_windowaggregation_WindowAggregateFunctionMin
+                      quickstep_expressions_windowaggregation_WindowAggregateFunctionSum
+                      quickstep_expressions_windowaggregation_WindowAggregateFunction_proto
+                      quickstep_expressions_windowaggregation_WindowAggregationID
+                      quickstep_utility_Macros)
+target_link_libraries(quickstep_expressions_windowaggregation_WindowAggregateFunctionMax
+                      glog
+                      quickstep_expressions_windowaggregation_WindowAggregateFunction
+                      quickstep_expressions_windowaggregation_WindowAggregationHandle
+                      quickstep_expressions_windowaggregation_WindowAggregationID
+                      quickstep_types_Type
+                      quickstep_types_operations_comparisons_Comparison
+                      quickstep_types_operations_comparisons_ComparisonFactory
+                      quickstep_types_operations_comparisons_ComparisonID
+                      quickstep_utility_Macros)
+target_link_libraries(quickstep_expressions_windowaggregation_WindowAggregateFunctionMin
+                      glog
+                      quickstep_expressions_windowaggregation_WindowAggregateFunction
+                      quickstep_expressions_windowaggregation_WindowAggregationHandle
+                      quickstep_expressions_windowaggregation_WindowAggregationID
+                      quickstep_types_Type
+                      quickstep_types_operations_comparisons_Comparison
+                      quickstep_types_operations_comparisons_ComparisonFactory
+                      quickstep_types_operations_comparisons_ComparisonID
+                      quickstep_utility_Macros)
+target_link_libraries(quickstep_expressions_windowaggregation_WindowAggregateFunctionSum
+                      glog
+                      quickstep_expressions_windowaggregation_WindowAggregateFunction
+                      quickstep_expressions_windowaggregation_WindowAggregationHandle
+                      quickstep_expressions_windowaggregation_WindowAggregationID
+                      quickstep_types_Type
+                      quickstep_types_TypeFactory
+                      quickstep_types_TypeID
+                      quickstep_types_operations_binaryoperations_BinaryOperation
+                      quickstep_types_operations_binaryoperations_BinaryOperationFactory
+                      quickstep_types_operations_binaryoperations_BinaryOperationID
+                      quickstep_utility_Macros)
+target_link_libraries(quickstep_expressions_windowaggregation_WindowAggregationHandle
+                      glog
+                      quickstep_catalog_CatalogRelationSchema
+                      quickstep_catalog_CatalogTypedefs
+                      quickstep_storage_StorageBlockInfo
+                      quickstep_types_TypedValue
+                      quickstep_types_containers_ColumnVector
+                      quickstep_types_containers_ColumnVectorsValueAccessor
+                      quickstep_types_operations_comparisons_Comparison
+                      quickstep_types_operations_comparisons_ComparisonFactory
+                      quickstep_types_operations_comparisons_ComparisonID
+                      quickstep_utility_Macros)
+target_link_libraries(quickstep_expressions_windowaggregation_WindowAggregationHandleAvg
+                      glog
+                      quickstep_catalog_CatalogTypedefs
+                      quickstep_expressions_scalar_Scalar
+                      quickstep_expressions_scalar_ScalarAttribute
+                      quickstep_expressions_windowaggregation_WindowAggregationHandle
+                      quickstep_storage_ValueAccessor
+                      quickstep_types_Type
+                      quickstep_types_TypeFactory
+                      quickstep_types_TypeID
+                      quickstep_types_TypedValue
+                      quickstep_types_containers_ColumnVectorsValueAccessor
+                      quickstep_types_operations_binaryoperations_BinaryOperation
+                      quickstep_types_operations_binaryoperations_BinaryOperationFactory
+                      quickstep_types_operations_binaryoperations_BinaryOperationID
+                      quickstep_types_operations_comparisons_Comparison
+                      quickstep_utility_Macros)
+
+# Submodule all-in-one library:
+add_library(quickstep_expressions_windowaggregation ../../empty_src.cpp)
+target_link_libraries(quickstep_expressions_windowaggregation
+                      quickstep_expressions_windowaggregation_WindowAggregateFunction
+                      quickstep_expressions_windowaggregation_WindowAggregateFunction_proto
+                      quickstep_expressions_windowaggregation_WindowAggregateFunctionAvg
+                      quickstep_expressions_windowaggregation_WindowAggregateFunctionCount
+                      quickstep_expressions_windowaggregation_WindowAggregateFunctionFactory
+                      quickstep_expressions_windowaggregation_WindowAggregateFunctionMax
+                      quickstep_expressions_windowaggregation_WindowAggregateFunctionMin
+                      quickstep_expressions_windowaggregation_WindowAggregateFunctionSum
+                      quickstep_expressions_windowaggregation_WindowAggregationHandle
+                      quickstep_expressions_windowaggregation_WindowAggregationHandleAvg
+                      quickstep_expressions_windowaggregation_WindowAggregationID)
+
+# Tests:
+
+# Unified executable to ammortize cost of linking.
+add_executable(WindowAggregationHandle_tests
+               "${CMAKE_CURRENT_SOURCE_DIR}/tests/WindowAggregationHandleAvg_unittest.cpp")
+target_link_libraries(WindowAggregationHandle_tests
+                      gtest
+                      gtest_main
+                      quickstep_catalog_CatalogTypedefs
+                      quickstep_expressions_windowaggregation_WindowAggregateFunction
+                      quickstep_expressions_windowaggregation_WindowAggregateFunctionFactory
+                      quickstep_expressions_windowaggregation_WindowAggregationHandle
+                      quickstep_expressions_windowaggregation_WindowAggregationHandleAvg
+                      quickstep_expressions_windowaggregation_WindowAggregationID
+                      quickstep_storage_ValueAccessor
+                      quickstep_types_CharType
+                      quickstep_types_DateOperatorOverloads
+                      quickstep_types_DatetimeIntervalType
+                      quickstep_types_DatetimeType
+                      quickstep_types_DoubleType
+                      quickstep_types_FloatType
+                      quickstep_types_IntType
+                      quickstep_types_IntervalLit
+                      quickstep_types_LongType
+                      quickstep_types_Type
+                      quickstep_types_TypeFactory
+                      quickstep_types_TypeID
+                      quickstep_types_TypedValue
+                      quickstep_types_VarCharType
+                      quickstep_types_YearMonthIntervalType
+                      quickstep_types_containers_ColumnVector
+                      quickstep_types_containers_ColumnVectorsValueAccessor)
+add_test(WindowAggregationHandle_tests WindowAggregationHandle_tests)

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/WindowAggregateFunction.cpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunction.cpp b/expressions/window_aggregation/WindowAggregateFunction.cpp
new file mode 100644
index 0000000..3911e1c
--- /dev/null
+++ b/expressions/window_aggregation/WindowAggregateFunction.cpp
@@ -0,0 +1,58 @@
+/**
+ *   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.
+ **/
+
+#include "expressions/window_aggregation/WindowAggregateFunction.hpp"
+
+#include <type_traits>
+
+#include "expressions/window_aggregation/WindowAggregateFunction.pb.h"
+#include "expressions/window_aggregation/WindowAggregationID.hpp"
+
+#include "glog/logging.h"
+
+namespace quickstep {
+
+serialization::WindowAggregateFunction WindowAggregateFunction::getProto() const {
+  serialization::WindowAggregateFunction proto;
+  switch (win_agg_id_) {
+    case WindowAggregationID::kAvg:
+      proto.set_window_aggregation_id(serialization::WindowAggregateFunction::AVG);
+      break;
+    case WindowAggregationID::kCount:
+      proto.set_window_aggregation_id(serialization::WindowAggregateFunction::COUNT);
+      break;
+    case WindowAggregationID::kMax:
+      proto.set_window_aggregation_id(serialization::WindowAggregateFunction::MAX);
+      break;
+    case WindowAggregationID::kMin:
+      proto.set_window_aggregation_id(serialization::WindowAggregateFunction::MIN);
+      break;
+    case WindowAggregationID::kSum:
+      proto.set_window_aggregation_id(serialization::WindowAggregateFunction::SUM);
+      break;
+    default: {
+      LOG(FATAL) << "Unrecognized WindowAggregationID: "
+                 << static_cast<std::underlying_type<WindowAggregationID>::type>(win_agg_id_);
+    }
+  }
+
+  return proto;
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/WindowAggregateFunction.hpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunction.hpp b/expressions/window_aggregation/WindowAggregateFunction.hpp
new file mode 100644
index 0000000..e40479b
--- /dev/null
+++ b/expressions/window_aggregation/WindowAggregateFunction.hpp
@@ -0,0 +1,149 @@
+/**
+ *   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.
+ **/
+
+#ifndef QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_HPP_
+#define QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_HPP_
+
+#include <string>
+#include <vector>
+
+#include "catalog/CatalogTypedefs.hpp"
+#include "expressions/window_aggregation/WindowAggregateFunction.pb.h"
+#include "expressions/window_aggregation/WindowAggregationID.hpp"
+#include "storage/StorageBlockInfo.hpp"
+#include "utility/Macros.hpp"
+
+namespace quickstep {
+
+class CatalogRelationSchema;
+class Type;
+class WindowAggregationHandle;
+
+/** \addtogroup Expressions
+ *  @{
+ */
+
+/**
+ * @brief A class representing a particular window aggregate function in the
+ *        abstract sense. Each named aggregate function is represented by a
+ *        singleton subclass of WindowAggregateFunction.
+ *
+ * WindowAggregateFunction provides informational methods about the
+ * applicability of a particular window aggregate function to particular
+ * argument Type(s). The actual implementation of the window aggregate
+ * functions' logic is in the WindowAggregationHandle class hierarchy, and can
+ * be different depending on the particular argument Type(s) given to the window
+ * aggregate. To perform a window aggregation, a caller should first call
+ * WindowAggregateFunction::createHandle() to instantiate an
+ * WindowAggregationHandle object, then use the methods of
+ * WindowAggregationHandle to do the actual window aggregation. Finally, delete
+ * the WindowAggregationHandle after finished.
+ * 
+ * See WindowAggregationHandle for more detailed information about how
+ * window aggregates are actually computed.
+ **/
+class WindowAggregateFunction {
+ public:
+  /**
+   * @brief Get the ID of this window aggregate (i.e. its unique ID amongst all
+   *        the WindowAggregateFunctions).
+   *
+   * @return The WindowAggregationID of this WindowAggregateFunction.
+   **/
+  inline WindowAggregationID getWindowAggregationID() const {
+    return win_agg_id_;
+  }
+
+  /**
+   * @brief Get the human-readable name of this WindowAggregateFunction.
+   *
+   * @return The human-readable name of this WindowAggregateFunction.
+   **/
+  virtual std::string getName() const = 0;
+
+  /**
+   * @brief Get the serialized protocol buffer representation of this
+   *        WindowAggregateFunction.
+   *
+   * @return A serialized protocol buffer representation of this
+   *         WindowAggregateFunction.
+   **/
+  virtual serialization::WindowAggregateFunction getProto() const;
+
+  /**
+   * @brief Determine if this WindowAggregateFunction can be applied to
+   *        arguments of particular Type(s).
+   *
+   * @param argument_types A list of zero or more Types (in order) for
+   *        arguments to this WindowAggregateFunction.
+   * @return Whether this WindowAggregateFunction is applicable to the given
+   *         argument_types.
+   **/
+  virtual bool canApplyToTypes(
+      const std::vector<const Type*> &argument_types) const = 0;
+
+  /**
+   * @brief Determine the result Type for this WindowAggregateFunction given
+   *        arguments of particular Type(s).
+   *
+   * @param argument_types A list of zero or more Types (in order) for
+   *        arguments to this WindowAggregateFunction.
+   * @return The result Type for this WindowAggregateFunction applied to the
+   *         specified argument_types, or nullptr if this
+   *         WindowAggregateFunction is not applicable to the specified Type(s).
+   **/
+  virtual const Type* resultTypeForArgumentTypes(
+      const std::vector<const Type*> &argument_types) const = 0;
+
+  /**
+   * @brief Create a WindowAggregationHandle to compute aggregates.
+   *
+   * @warning It is an error to call this method for argument_types which this
+   *          WindowAggregateFunction can not apply to. For safety, check
+   *          canApplyToTypes() first.
+   *
+   * @param argument_types A list of zero or more Types (in order) for
+   *        arguments to this WindowAggregateFunction.
+   * @param partition_key_types A list or zero or more Types for partition keys
+   *                            to this WindowAggregateFunction.
+   * 
+   * @return A new WindowAggregationHandle that can be used to compute this
+   *         WindowAggregateFunction over the specified argument_types. Caller
+   *         is responsible for deleting the returned object.
+   **/
+  virtual WindowAggregationHandle* createHandle(
+      const std::vector<const Type*> &argument_types,
+      const std::vector<const Type*> &partition_key_types) const = 0;
+
+ protected:
+  explicit WindowAggregateFunction(const WindowAggregationID win_agg_id)
+      : win_agg_id_(win_agg_id) {
+  }
+
+ private:
+  const WindowAggregationID win_agg_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowAggregateFunction);
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/WindowAggregateFunction.proto
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunction.proto b/expressions/window_aggregation/WindowAggregateFunction.proto
new file mode 100644
index 0000000..fe8d799
--- /dev/null
+++ b/expressions/window_aggregation/WindowAggregateFunction.proto
@@ -0,0 +1,32 @@
+//   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.
+
+syntax = "proto2";
+
+package quickstep.serialization;
+
+message WindowAggregateFunction {
+  enum WindowAggregationID {
+    AVG = 0;
+    COUNT = 1;
+    MAX = 2;
+    MIN = 3;
+    SUM = 4;    
+  }
+
+  required WindowAggregationID window_aggregation_id = 1;
+}

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/WindowAggregateFunctionAvg.cpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionAvg.cpp b/expressions/window_aggregation/WindowAggregateFunctionAvg.cpp
new file mode 100644
index 0000000..bc31a53
--- /dev/null
+++ b/expressions/window_aggregation/WindowAggregateFunctionAvg.cpp
@@ -0,0 +1,85 @@
+/**
+ *   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.
+ **/
+
+#include "expressions/window_aggregation/WindowAggregateFunctionAvg.hpp"
+
+#include <vector>
+
+#include "expressions/window_aggregation/WindowAggregationHandleAvg.hpp"
+#include "types/Type.hpp"
+#include "types/TypeFactory.hpp"
+#include "types/TypeID.hpp"
+#include "types/operations/binary_operations/BinaryOperation.hpp"
+#include "types/operations/binary_operations/BinaryOperationFactory.hpp"
+#include "types/operations/binary_operations/BinaryOperationID.hpp"
+
+#include "glog/logging.h"
+
+namespace quickstep {
+
+bool WindowAggregateFunctionAvg::canApplyToTypes(
+    const std::vector<const Type*> &argument_types) const {
+  // AVG is unary.
+  if (argument_types.size() != 1) {
+    return false;
+  }
+
+  // Argument must be addable and divisible.
+  return BinaryOperationFactory::GetBinaryOperation(BinaryOperationID::kAdd)
+             .canApplyToTypes(*argument_types.front(), *argument_types.front()) &&
+         BinaryOperationFactory::GetBinaryOperation(BinaryOperationID::kDivide)
+             .canApplyToTypes(*argument_types.front(), TypeFactory::GetType(kDouble));
+}
+
+const Type* WindowAggregateFunctionAvg::resultTypeForArgumentTypes(
+    const std::vector<const Type*> &argument_types) const {
+  if (!canApplyToTypes(argument_types)) {
+    return nullptr;
+  }
+
+  // The type used to sum values is nullable, and we automatically widen int to
+  // long and float to double to have more headroom when adding up many values.
+  const Type *sum_type = &(argument_types.front()->getNullableVersion());
+  switch (sum_type->getTypeID()) {
+    case kInt:
+      sum_type = &TypeFactory::GetType(kLong, true);
+      break;
+    case kFloat:
+      sum_type = &TypeFactory::GetType(kDouble, true);
+      break;
+    default:
+      break;
+  }
+
+  return BinaryOperationFactory::GetBinaryOperation(BinaryOperationID::kDivide)
+             .resultTypeForArgumentTypes(*sum_type, TypeFactory::GetType(kDouble));
+}
+
+WindowAggregationHandle* WindowAggregateFunctionAvg::createHandle(
+    const std::vector<const Type*> &argument_types,
+    const std::vector<const Type*> &partition_key_types) const {
+  DCHECK(canApplyToTypes(argument_types))
+      << "Attempted to create an WindowAggregationHandleAvg for argument Type(s)"
+      << " that AVG can not be applied to.";
+
+  return new WindowAggregationHandleAvg(partition_key_types,
+                                        *argument_types.front());
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/WindowAggregateFunctionAvg.hpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionAvg.hpp b/expressions/window_aggregation/WindowAggregateFunctionAvg.hpp
new file mode 100644
index 0000000..32fd9d5
--- /dev/null
+++ b/expressions/window_aggregation/WindowAggregateFunctionAvg.hpp
@@ -0,0 +1,75 @@
+/**
+ *   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.
+ **/
+
+#ifndef QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_AVG_HPP_
+#define QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_AVG_HPP_
+
+#include <string>
+#include <vector>
+
+#include "expressions/window_aggregation/WindowAggregateFunction.hpp"
+#include "expressions/window_aggregation/WindowAggregationID.hpp"
+#include "utility/Macros.hpp"
+
+namespace quickstep {
+
+class Type;
+class WindowAggregationHandle;
+
+/** \addtogroup Expressions
+ *  @{
+ */
+
+/**
+ * @brief WindowAggregateFunction representing SQL AVG() OVER term.
+ **/
+class WindowAggregateFunctionAvg : public WindowAggregateFunction {
+ public:
+  static const WindowAggregateFunctionAvg& Instance() {
+    static WindowAggregateFunctionAvg instance;
+    return instance;
+  }
+
+  std::string getName() const override {
+    return "AVG";
+  }
+
+  bool canApplyToTypes(
+      const std::vector<const Type*> &argument_types) const override;
+
+  const Type* resultTypeForArgumentTypes(
+      const std::vector<const Type*> &argument_types) const override;
+
+  WindowAggregationHandle* createHandle(
+      const std::vector<const Type*> &argument_types,
+      const std::vector<const Type*> &partition_key_types) const override;
+
+ private:
+  WindowAggregateFunctionAvg()
+      : WindowAggregateFunction(WindowAggregationID::kAvg) {
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(WindowAggregateFunctionAvg);
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_AVG_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/WindowAggregateFunctionCount.cpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionCount.cpp b/expressions/window_aggregation/WindowAggregateFunctionCount.cpp
new file mode 100644
index 0000000..504e000
--- /dev/null
+++ b/expressions/window_aggregation/WindowAggregateFunctionCount.cpp
@@ -0,0 +1,59 @@
+/**
+ *   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.
+ **/
+
+#include "expressions/window_aggregation/WindowAggregateFunctionCount.hpp"
+
+#include <vector>
+
+#include "expressions/window_aggregation/WindowAggregationHandle.hpp"
+#include "types/Type.hpp"
+#include "types/TypeFactory.hpp"
+#include "types/TypeID.hpp"
+
+#include "glog/logging.h"
+
+namespace quickstep {
+
+bool WindowAggregateFunctionCount::canApplyToTypes(
+    const std::vector<const Type*> &argument_types) const {
+  // COUNT may be nullary (i.e. COUNT(*)) or unary.
+  return argument_types.size() <= 1;
+}
+
+const Type* WindowAggregateFunctionCount::resultTypeForArgumentTypes(
+    const std::vector<const Type*> &argument_types) const {
+  if (!canApplyToTypes(argument_types)) {
+    return nullptr;
+  }
+
+  return &TypeFactory::GetType(kLong);
+}
+
+WindowAggregationHandle* WindowAggregateFunctionCount::createHandle(
+    const std::vector<const Type*> &argument_types,
+    const std::vector<const Type*> &partition_key_types) const {
+  DCHECK(canApplyToTypes(argument_types))
+      << "Attempted to create a WindowAggregationHandleCount for argument Types "
+      << "that COUNT can not be applied to (> 1 argument).";
+
+  // TODO(Shixuan): Add handle for Count.
+  return nullptr;
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/WindowAggregateFunctionCount.hpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionCount.hpp b/expressions/window_aggregation/WindowAggregateFunctionCount.hpp
new file mode 100644
index 0000000..1b40fdd
--- /dev/null
+++ b/expressions/window_aggregation/WindowAggregateFunctionCount.hpp
@@ -0,0 +1,75 @@
+/**
+ *   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.
+ **/
+
+#ifndef QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_COUNT_HPP_
+#define QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_COUNT_HPP_
+
+#include <string>
+#include <vector>
+
+#include "expressions/window_aggregation/WindowAggregateFunction.hpp"
+#include "expressions/window_aggregation/WindowAggregationID.hpp"
+#include "utility/Macros.hpp"
+
+namespace quickstep {
+
+class Type;
+class WindowAggregationHandle;
+
+/** \addtogroup Expressions
+ *  @{
+ */
+
+/**
+ * @brief WindowAggregateFunction representing SQL COUNT() OVER term.
+ **/
+class WindowAggregateFunctionCount : public WindowAggregateFunction {
+ public:
+  static const WindowAggregateFunctionCount& Instance() {
+    static WindowAggregateFunctionCount instance;
+    return instance;
+  }
+
+  std::string getName() const override {
+    return "COUNT";
+  }
+
+  bool canApplyToTypes(
+      const std::vector<const Type*> &argument_types) const override;
+
+  const Type* resultTypeForArgumentTypes(
+      const std::vector<const Type*> &argument_types) const override;
+
+  WindowAggregationHandle* createHandle(
+      const std::vector<const Type*> &argument_types,
+      const std::vector<const Type*> &partition_key_types) const override;
+
+ private:
+  WindowAggregateFunctionCount()
+      : WindowAggregateFunction(WindowAggregationID::kCount) {
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(WindowAggregateFunctionCount);
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_COUNT_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/WindowAggregateFunctionFactory.cpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionFactory.cpp b/expressions/window_aggregation/WindowAggregateFunctionFactory.cpp
new file mode 100644
index 0000000..65247f2
--- /dev/null
+++ b/expressions/window_aggregation/WindowAggregateFunctionFactory.cpp
@@ -0,0 +1,106 @@
+/**
+ *   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.
+ **/
+
+#include "expressions/window_aggregation/WindowAggregateFunctionFactory.hpp"
+
+#include <string>
+#include <type_traits>
+
+#include "expressions/window_aggregation/WindowAggregateFunction.pb.h"
+#include "expressions/window_aggregation/WindowAggregateFunctionAvg.hpp"
+#include "expressions/window_aggregation/WindowAggregateFunctionCount.hpp"
+#include "expressions/window_aggregation/WindowAggregateFunctionMax.hpp"
+#include "expressions/window_aggregation/WindowAggregateFunctionMin.hpp"
+#include "expressions/window_aggregation/WindowAggregateFunctionSum.hpp"
+#include "expressions/window_aggregation/WindowAggregationID.hpp"
+
+#include "glog/logging.h"
+
+namespace quickstep {
+
+const WindowAggregateFunction& WindowAggregateFunctionFactory::Get(
+    const WindowAggregationID agg_id) {
+  switch (agg_id) {
+    case WindowAggregationID::kAvg:
+      return WindowAggregateFunctionAvg::Instance();
+    case WindowAggregationID::kCount:
+      return WindowAggregateFunctionCount::Instance();
+    case WindowAggregationID::kMax:
+      return WindowAggregateFunctionMax::Instance();
+    case WindowAggregationID::kMin:
+      return WindowAggregateFunctionMin::Instance();
+    case WindowAggregationID::kSum:
+      return WindowAggregateFunctionSum::Instance();
+    default: {
+      LOG(FATAL) << "Unrecognized WindowAggregationID: "
+                 << static_cast<std::underlying_type<WindowAggregationID>::type>(agg_id);
+    }
+  }
+}
+
+const WindowAggregateFunction* WindowAggregateFunctionFactory::GetByName(
+    const std::string &name) {
+  if (name == "avg") {
+    return &WindowAggregateFunctionAvg::Instance();
+  } else if (name == "count") {
+    return &WindowAggregateFunctionCount::Instance();
+  } else if (name == "max") {
+    return &WindowAggregateFunctionMax::Instance();
+  } else if (name == "min") {
+    return &WindowAggregateFunctionMin::Instance();
+  } else if (name == "sum") {
+    return &WindowAggregateFunctionSum::Instance();
+  } else {
+    return nullptr;
+  }
+}
+
+bool WindowAggregateFunctionFactory::ProtoIsValid(
+    const serialization::WindowAggregateFunction &proto) {
+  return proto.IsInitialized() &&
+         serialization::WindowAggregateFunction::WindowAggregationID_IsValid(proto.window_aggregation_id());
+}
+
+const WindowAggregateFunction& WindowAggregateFunctionFactory::ReconstructFromProto(
+    const serialization::WindowAggregateFunction &proto) {
+  DCHECK(ProtoIsValid(proto))
+      << "Attempted to reconstruct an WindowAggregateFunction from an invalid proto:\n"
+      << proto.DebugString();
+
+  switch (proto.window_aggregation_id()) {
+    case serialization::WindowAggregateFunction::AVG:
+      return WindowAggregateFunctionAvg::Instance();
+    case serialization::WindowAggregateFunction::COUNT:
+      return WindowAggregateFunctionCount::Instance();
+    case serialization::WindowAggregateFunction::MAX:
+      return WindowAggregateFunctionMax::Instance();
+    case serialization::WindowAggregateFunction::MIN:
+      return WindowAggregateFunctionMin::Instance();
+    case serialization::WindowAggregateFunction::SUM:
+      return WindowAggregateFunctionSum::Instance();
+    default: {
+      LOG(FATAL) << "Unrecognized serialization::WindowAggregateFunction::WindowAggregationID: "
+                 << proto.window_aggregation_id()
+                 << "\nFull proto debug string:\n"
+                 << proto.DebugString();
+    }
+  }
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/WindowAggregateFunctionFactory.hpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionFactory.hpp b/expressions/window_aggregation/WindowAggregateFunctionFactory.hpp
new file mode 100644
index 0000000..1d59e93
--- /dev/null
+++ b/expressions/window_aggregation/WindowAggregateFunctionFactory.hpp
@@ -0,0 +1,96 @@
+/**
+ *   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.
+ **/
+
+#ifndef QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_FACTORY_HPP_
+#define QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_FACTORY_HPP_
+
+#include <string>
+
+#include "expressions/window_aggregation/WindowAggregationID.hpp"
+#include "utility/Macros.hpp"
+
+namespace quickstep {
+
+class WindowAggregateFunction;
+namespace serialization { class WindowAggregateFunction; }
+
+/** \addtogroup Expressions
+ *  @{
+ */
+
+/**
+ * @brief All-static factory with methods that provide access to the various
+ *        concrete implementations of WindowAggregateFunction.
+ *
+ * WindowAggregateFunctionFactory allows client code to use any
+ * WindowAggregateFunction in Quickstep in a generic way without having to know
+ * about all the specific subclasses of WindowAggregateFunction. In particular,
+ * it is used to deserialize WindowAggregateFunctions used in
+ * WindowAggregationOperationState from their protobuf representations
+ * (originally created by the optimizer) when deserializing a QueryContext.
+ **/
+namespace WindowAggregateFunctionFactory {
+  /**
+   * @brief Get a particular WindowAggregateFunction by its ID.
+   *
+   * @param agg_id The ID of the desired WindowAggregateFunction.
+   * @return A reference to the singleton instance of the
+   *         WindowAggregateFunction specified by agg_id.
+   **/
+  const WindowAggregateFunction& Get(const WindowAggregationID agg_id);
+
+  /**
+   * @brief Get a particular WindowAggregateFunction by its name in SQL syntax.
+   *
+   * @param name The name of the desired WindowAggregateFunction in lower case.
+   * @return A pointer to the WindowAggregateFunction specified by name, or NULL
+   *         if name does not match any known WindowAggregateFunction.
+   **/
+  const WindowAggregateFunction* GetByName(const std::string &name);
+
+  /**
+   * @brief Determine if a serialized protobuf representation of a
+   *        WindowAggregateFunction is fully-formed and valid.
+   *
+   * @param proto A serialized protobuf representation of a
+   *              WindowAggregateFunction to check for validity.
+   * @return Whether proto is fully-formed and valid.
+   **/
+  bool ProtoIsValid(const serialization::WindowAggregateFunction &proto);
+
+  /**
+   * @brief Get the WindowAggregateFunction represented by a proto.
+   *
+   * @warning It is an error to call this method with an invalid proto.
+   *          ProtoIsValid() should be called first to check.
+   *
+   * @param proto A serialized protobuf representation of a
+   *              WindowAggregateFunction.
+   * @return The WindowAggregateFunction represented by proto.
+   **/
+  const WindowAggregateFunction& ReconstructFromProto(
+      const serialization::WindowAggregateFunction &proto);
+
+}  // namespace WindowAggregateFunctionFactory
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_FACTORY_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/WindowAggregateFunctionMax.cpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionMax.cpp b/expressions/window_aggregation/WindowAggregateFunctionMax.cpp
new file mode 100644
index 0000000..f3997c7
--- /dev/null
+++ b/expressions/window_aggregation/WindowAggregateFunctionMax.cpp
@@ -0,0 +1,67 @@
+/**
+ *   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.
+ **/
+
+#include "expressions/window_aggregation/WindowAggregateFunctionMax.hpp"
+
+#include <vector>
+
+#include "expressions/window_aggregation/WindowAggregationHandle.hpp"
+#include "types/Type.hpp"
+#include "types/operations/comparisons/Comparison.hpp"
+#include "types/operations/comparisons/ComparisonFactory.hpp"
+#include "types/operations/comparisons/ComparisonID.hpp"
+
+#include "glog/logging.h"
+
+namespace quickstep {
+
+bool WindowAggregateFunctionMax::canApplyToTypes(
+    const std::vector<const Type*> &argument_types) const {
+  // MAX is unary.
+  if (argument_types.size() != 1) {
+    return false;
+  }
+
+  // Argument must be comparable by '>'.
+  return ComparisonFactory::GetComparison(ComparisonID::kGreater).canCompareTypes(
+      *argument_types.front(),
+      *argument_types.front());
+}
+
+const Type* WindowAggregateFunctionMax::resultTypeForArgumentTypes(
+    const std::vector<const Type*> &argument_types) const {
+  if (!canApplyToTypes(argument_types)) {
+    return nullptr;
+  }
+
+  return &(argument_types.front()->getNullableVersion());
+}
+
+WindowAggregationHandle* WindowAggregateFunctionMax::createHandle(
+    const std::vector<const Type*> &argument_types,
+    const std::vector<const Type*> &partition_key_types) const {
+  DCHECK(canApplyToTypes(argument_types))
+      << "Attempted to create a WindowAggregationHandleMax for argument Type(s) "
+      << "that MAX can not be applied to.";
+
+  // TODO(Shixuan): Add handle for Max.
+  return nullptr;
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/WindowAggregateFunctionMax.hpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionMax.hpp b/expressions/window_aggregation/WindowAggregateFunctionMax.hpp
new file mode 100644
index 0000000..00c788e
--- /dev/null
+++ b/expressions/window_aggregation/WindowAggregateFunctionMax.hpp
@@ -0,0 +1,75 @@
+/**
+ *   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.
+ **/
+
+#ifndef QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_MAX_HPP_
+#define QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_MAX_HPP_
+
+#include <string>
+#include <vector>
+
+#include "expressions/window_aggregation/WindowAggregateFunction.hpp"
+#include "expressions/window_aggregation/WindowAggregationID.hpp"
+#include "utility/Macros.hpp"
+
+namespace quickstep {
+
+class Type;
+class WindowAggregationHandle;
+
+/** \addtogroup Expressions
+ *  @{
+ */
+
+/**
+ * @brief WindowAggregateFunction representing SQL MAX() OVER term.
+ **/
+class WindowAggregateFunctionMax : public WindowAggregateFunction {
+ public:
+  static const WindowAggregateFunctionMax& Instance() {
+    static WindowAggregateFunctionMax instance;
+    return instance;
+  }
+
+  std::string getName() const override {
+    return "MAX";
+  }
+
+  bool canApplyToTypes(
+      const std::vector<const Type*> &argument_types) const override;
+
+  const Type* resultTypeForArgumentTypes(
+      const std::vector<const Type*> &argument_types) const override;
+
+  WindowAggregationHandle* createHandle(
+      const std::vector<const Type*> &argument_types,
+      const std::vector<const Type*> &partition_key_types) const override;
+
+ private:
+  WindowAggregateFunctionMax()
+      : WindowAggregateFunction(WindowAggregationID::kMax) {
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(WindowAggregateFunctionMax);
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_MAX_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/WindowAggregateFunctionMin.cpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionMin.cpp b/expressions/window_aggregation/WindowAggregateFunctionMin.cpp
new file mode 100644
index 0000000..a13e28e
--- /dev/null
+++ b/expressions/window_aggregation/WindowAggregateFunctionMin.cpp
@@ -0,0 +1,66 @@
+/**
+ *   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.
+ **/
+
+#include "expressions/window_aggregation/WindowAggregateFunctionMin.hpp"
+
+#include <vector>
+
+#include "expressions/window_aggregation/WindowAggregationHandle.hpp"
+#include "types/Type.hpp"
+#include "types/operations/comparisons/Comparison.hpp"
+#include "types/operations/comparisons/ComparisonFactory.hpp"
+#include "types/operations/comparisons/ComparisonID.hpp"
+
+#include "glog/logging.h"
+
+namespace quickstep {
+
+bool WindowAggregateFunctionMin::canApplyToTypes(
+    const std::vector<const Type*> &argument_types) const {
+  // MIN is unary.
+  if (argument_types.size() != 1) {
+    return false;
+  }
+
+  // Argument must be comparable by '<'.
+  return ComparisonFactory::GetComparison(ComparisonID::kLess).canCompareTypes(
+      *argument_types.front(),
+      *argument_types.front());
+}
+
+const Type* WindowAggregateFunctionMin::resultTypeForArgumentTypes(
+    const std::vector<const Type*> &argument_types) const {
+  if (!canApplyToTypes(argument_types)) {
+    return nullptr;
+  }
+
+  return &(argument_types.front()->getNullableVersion());
+}
+
+WindowAggregationHandle* WindowAggregateFunctionMin::createHandle(
+    const std::vector<const Type*> &argument_types,
+    const std::vector<const Type*> &partition_key_types) const {
+  DCHECK(canApplyToTypes(argument_types))
+      << "Attempted to create a WindowAggregationHandleMin for argument Type(s) "
+      << "that MIN can not be applied to.";
+
+  return nullptr;
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/WindowAggregateFunctionMin.hpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionMin.hpp b/expressions/window_aggregation/WindowAggregateFunctionMin.hpp
new file mode 100644
index 0000000..aeba539
--- /dev/null
+++ b/expressions/window_aggregation/WindowAggregateFunctionMin.hpp
@@ -0,0 +1,75 @@
+/**
+ *   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.
+ **/
+
+#ifndef QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_MIN_HPP_
+#define QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_MIN_HPP_
+
+#include <string>
+#include <vector>
+
+#include "expressions/window_aggregation/WindowAggregateFunction.hpp"
+#include "expressions/window_aggregation/WindowAggregationID.hpp"
+#include "utility/Macros.hpp"
+
+namespace quickstep {
+
+class Type;
+class WindowAggregationHandle;
+
+/** \addtogroup Expressions
+ *  @{
+ */
+
+/**
+ * @brief WindowAggregateFunction representing SQL MIN() OVER term.
+ **/
+class WindowAggregateFunctionMin : public WindowAggregateFunction {
+ public:
+  static const WindowAggregateFunctionMin& Instance() {
+    static WindowAggregateFunctionMin instance;
+    return instance;
+  }
+
+  std::string getName() const override {
+    return "MIN";
+  }
+
+  bool canApplyToTypes(
+      const std::vector<const Type*> &argument_types) const override;
+
+  const Type* resultTypeForArgumentTypes(
+      const std::vector<const Type*> &argument_types) const override;
+
+  WindowAggregationHandle* createHandle(
+      const std::vector<const Type*> &argument_types,
+      const std::vector<const Type*> &partition_key_types) const override;
+
+ private:
+  WindowAggregateFunctionMin()
+      : WindowAggregateFunction(WindowAggregationID::kMin) {
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(WindowAggregateFunctionMin);
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_MIN_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/WindowAggregateFunctionSum.cpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionSum.cpp b/expressions/window_aggregation/WindowAggregateFunctionSum.cpp
new file mode 100644
index 0000000..636c53a
--- /dev/null
+++ b/expressions/window_aggregation/WindowAggregateFunctionSum.cpp
@@ -0,0 +1,82 @@
+/**
+ *   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.
+ **/
+
+#include "expressions/window_aggregation/WindowAggregateFunctionSum.hpp"
+
+#include <vector>
+
+#include "expressions/window_aggregation/WindowAggregationHandle.hpp"
+#include "types/Type.hpp"
+#include "types/TypeFactory.hpp"
+#include "types/TypeID.hpp"
+#include "types/operations/binary_operations/BinaryOperation.hpp"
+#include "types/operations/binary_operations/BinaryOperationFactory.hpp"
+#include "types/operations/binary_operations/BinaryOperationID.hpp"
+
+#include "glog/logging.h"
+
+namespace quickstep {
+
+bool WindowAggregateFunctionSum::canApplyToTypes(
+    const std::vector<const Type*> &argument_types) const {
+  // SUM is unary.
+  if (argument_types.size() != 1) {
+    return false;
+  }
+
+  // Argument must be addable.
+  return BinaryOperationFactory::GetBinaryOperation(BinaryOperationID::kAdd)
+             .canApplyToTypes(*argument_types.front(), *argument_types.front());
+}
+
+const Type* WindowAggregateFunctionSum::resultTypeForArgumentTypes(
+    const std::vector<const Type*> &argument_types) const {
+  if (!canApplyToTypes(argument_types)) {
+    return nullptr;
+  }
+
+  // SUM may return NULL if there are no input rows, and we automatically widen
+  // int to long and float to double to have more headroom when adding up many
+  // values.
+  const Type *sum_type = &(argument_types.front()->getNullableVersion());
+  switch (sum_type->getTypeID()) {
+    case kInt:
+      sum_type = &TypeFactory::GetType(kLong, true);
+      break;
+    case kFloat:
+      sum_type = &TypeFactory::GetType(kDouble, true);
+      break;
+    default:
+      break;
+  }
+
+  return sum_type;
+}
+
+WindowAggregationHandle* WindowAggregateFunctionSum::createHandle(
+    const std::vector<const Type*> &argument_types,
+    const std::vector<const Type*> &partition_key_types) const {
+  DCHECK(canApplyToTypes(argument_types))
+      << "Attempted to create a WindowAggregationHandleSum for argument Type(s) "
+      << "that SUM can not be applied to.";
+
+  return nullptr;
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/WindowAggregateFunctionSum.hpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionSum.hpp b/expressions/window_aggregation/WindowAggregateFunctionSum.hpp
new file mode 100644
index 0000000..047113c
--- /dev/null
+++ b/expressions/window_aggregation/WindowAggregateFunctionSum.hpp
@@ -0,0 +1,75 @@
+/**
+ *   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.
+ **/
+
+#ifndef QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_SUM_HPP_
+#define QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_SUM_HPP_
+
+#include <string>
+#include <vector>
+
+#include "expressions/window_aggregation/WindowAggregateFunction.hpp"
+#include "expressions/window_aggregation/WindowAggregationID.hpp"
+#include "utility/Macros.hpp"
+
+namespace quickstep {
+
+class Type;
+class WindowAggregationHandle;
+
+/** \addtogroup Expressions
+ *  @{
+ */
+
+/**
+ * @brief WindowAggregateFunction representing SQL SUM() OVER term.
+ **/
+class WindowAggregateFunctionSum : public WindowAggregateFunction {
+ public:
+  static const WindowAggregateFunctionSum& Instance() {
+    static WindowAggregateFunctionSum instance;
+    return instance;
+  }
+
+  std::string getName() const override {
+    return "SUM";
+  }
+
+  bool canApplyToTypes(
+      const std::vector<const Type*> &argument_types) const override;
+
+  const Type* resultTypeForArgumentTypes(
+      const std::vector<const Type*> &argument_types) const override;
+
+  WindowAggregationHandle* createHandle(
+      const std::vector<const Type*> &argument_types,
+      const std::vector<const Type*> &partition_key_types) const override;
+
+ private:
+  WindowAggregateFunctionSum()
+      : WindowAggregateFunction(WindowAggregationID::kSum) {
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(WindowAggregateFunctionSum);
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_SUM_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/WindowAggregationHandle.hpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregationHandle.hpp b/expressions/window_aggregation/WindowAggregationHandle.hpp
new file mode 100644
index 0000000..65f95d9
--- /dev/null
+++ b/expressions/window_aggregation/WindowAggregationHandle.hpp
@@ -0,0 +1,137 @@
+/**
+ *   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.
+ **/
+
+#ifndef QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATION_HANDLE_HPP_
+#define QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATION_HANDLE_HPP_
+
+#include <cstddef>
+#include <memory>
+#include <vector>
+
+#include "catalog/CatalogRelationSchema.hpp"
+#include "catalog/CatalogTypedefs.hpp"
+#include "storage/StorageBlockInfo.hpp"
+#include "types/TypedValue.hpp"
+#include "types/containers/ColumnVector.hpp"
+#include "types/containers/ColumnVectorsValueAccessor.hpp"
+#include "types/operations/comparisons/Comparison.hpp"
+#include "types/operations/comparisons/ComparisonFactory.hpp"
+#include "types/operations/comparisons/ComparisonID.hpp"
+#include "utility/Macros.hpp"
+
+namespace quickstep {
+
+class InsertDestinationInterface;
+class Scalar;
+class StorageManager;
+class Type;
+class ValueAccessor;
+
+/** \addtogroup Expressions
+ *  @{
+ */
+
+/**
+ * @brief WindowAggregationHandle encapsulates logic for actually computing
+ *        window aggregates with particular argument(s).
+ * @note See also WindowAggregateFunction, which represents a SQL aggregate
+ *       function in the abstract sense.
+ *
+ * A WindowAggregationHandle is created by calling
+ * WindowAggregateFunction::createHandle(). The WindowAggregationHandle object
+ * provides methods that are used to actually compute the window aggregate,
+ * storing intermediate results in WindowAggregationState objects.
+ *
+ * The work flow for computing a window aggregate is:
+ *     1. Create an initial state by createInitialState().
+ *     2. One thread will handle all the computation, iterating from the first
+ *        tuple to the last tuple. Note there will be two modes that could be
+ *        used upon different situations:
+ *        a. If the window aggregate is defined as accumulative, which are:
+ *           i.  Functions applied to whole partition, such as rank(), ntile()
+ *               and dense_rank().
+ *           ii. The window frame is defined as "BETWEEN UNBOUNDED PRECEDING
+ *               AND CURRENT ROW" or "BETWEEN CURRENT ROW AND UNBOUNDED
+ *               FOLLOWING".
+ *           Then, for functions except median, we could store some global
+ *           values in the state without keeping all the tuple values around.
+ *        b. If the window frame is sliding, such as "BETWEEN 3 PRECEDING AND
+ *           3 FOLLOWING", we have to store all the tuples in the state so that
+ *           we could know which values should be dropped as the window slides.
+ *        For each computed value, generate a tuple store in the column vector.
+ *     3. Insert the new column into the original relation and return.
+ *
+ * TODO(Shixuan): Currently we don't support parallelization. The basic idea for
+ * parallelization is to calculate the partial result inside each block. Each
+ * block could visit the following blocks as long as the block's last partition
+ * is not finished. WindowAggregationOperationState will be used for handling
+ * the global state of the calculation.
+ **/
+
+class WindowAggregationHandle {
+ public:
+  /**
+   * @brief Destructor.
+   **/
+  virtual ~WindowAggregationHandle() {}
+
+  /**
+   * @brief Calculate the window aggregate result.
+   *
+   * @param block_accessors A pointer to the value accessor of block attributes.
+   * @param arguments The ColumnVectors of arguments
+   * @param partition_by_ids The ids of partition keys.
+   * @param is_row True if the frame mode is ROWS, false if it is RANGE.
+   * @param num_preceding The number of rows/range that precedes the current row.
+   * @param num_following The number of rows/range that follows the current row.
+   *
+   * @return A ColumnVector of the calculated window aggregates.
+   **/
+  virtual ColumnVector* calculate(ColumnVectorsValueAccessor* block_accessors,
+                                  std::vector<ColumnVector*> &&arguments,
+                                  const std::vector<attribute_id> &partition_by_ids,
+                                  const bool is_row,
+                                  const std::int64_t num_preceding,
+                                  const std::int64_t num_following) const = 0;
+
+ protected:
+  /**
+   * @brief Constructor.
+   *
+   * @param partition_key_types The Types of the partition key.
+   **/
+  explicit WindowAggregationHandle(
+      const std::vector<const Type*> &partition_key_types) {
+    // Comparison operators for checking if two tuples belong to the same partition.
+    for (const Type *partition_key_type : partition_key_types) {
+      equal_comparators_.emplace_back(
+          ComparisonFactory::GetComparison(ComparisonID::kEqual)
+              .makeUncheckedComparatorForTypes(*partition_key_type, *partition_key_type));
+    }
+  }
+
+  std::vector<std::unique_ptr<UncheckedComparator>> equal_comparators_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WindowAggregationHandle);
+};
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATION_HANDLE_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/WindowAggregationHandleAvg.cpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregationHandleAvg.cpp b/expressions/window_aggregation/WindowAggregationHandleAvg.cpp
new file mode 100644
index 0000000..a6a10d4
--- /dev/null
+++ b/expressions/window_aggregation/WindowAggregationHandleAvg.cpp
@@ -0,0 +1,241 @@
+/**
+ *   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.
+ **/
+
+#include "expressions/window_aggregation/WindowAggregationHandleAvg.hpp"
+
+#include <cstddef>
+#include <memory>
+#include <vector>
+
+#include "catalog/CatalogTypedefs.hpp"
+#include "expressions/scalar/Scalar.hpp"
+#include "expressions/scalar/ScalarAttribute.hpp"
+#include "storage/ValueAccessor.hpp"
+#include "types/Type.hpp"
+#include "types/TypeFactory.hpp"
+#include "types/TypeID.hpp"
+#include "types/TypedValue.hpp"
+#include "types/containers/ColumnVectorsValueAccessor.hpp"
+#include "types/operations/binary_operations/BinaryOperation.hpp"
+#include "types/operations/binary_operations/BinaryOperationFactory.hpp"
+#include "types/operations/binary_operations/BinaryOperationID.hpp"
+#include "types/operations/comparisons/Comparison.hpp"
+
+#include "glog/logging.h"
+
+namespace quickstep {
+
+WindowAggregationHandleAvg::WindowAggregationHandleAvg(
+    const std::vector<const Type*> &partition_key_types,
+    const Type &type)
+    : WindowAggregationHandle(partition_key_types),
+      argument_type_(type) {
+  // We sum Int as Long and Float as Double so that we have more headroom when
+  // adding many values.
+  TypeID type_id;
+  switch (type.getTypeID()) {
+    case kInt:
+    case kLong:
+      type_id = kLong;
+      break;
+    case kFloat:
+    case kDouble:
+      type_id = kDouble;
+      break;
+    default:
+      type_id = type.getTypeID();
+      break;
+  }
+
+  sum_type_ = &(TypeFactory::GetType(type_id));
+
+  // Result is nullable, because AVG() over 0 values (or all NULL values) is
+  // NULL.
+  result_type_
+      = &(BinaryOperationFactory::GetBinaryOperation(BinaryOperationID::kDivide)
+              .resultTypeForArgumentTypes(*sum_type_, TypeFactory::GetType(kDouble))
+                  ->getNullableVersion());
+
+  // Make operators to do arithmetic:
+  // Add operator for summing argument values.
+  fast_add_operator_.reset(
+      BinaryOperationFactory::GetBinaryOperation(BinaryOperationID::kAdd)
+          .makeUncheckedBinaryOperatorForTypes(*sum_type_, argument_type_));
+  // Divide operator for dividing sum by count to get final average.
+  divide_operator_.reset(
+      BinaryOperationFactory::GetBinaryOperation(BinaryOperationID::kDivide)
+          .makeUncheckedBinaryOperatorForTypes(*sum_type_, TypeFactory::GetType(kDouble)));
+}
+
+ColumnVector* WindowAggregationHandleAvg::calculate(
+    ColumnVectorsValueAccessor *tuple_accessor,
+    std::vector<ColumnVector*> &&arguments,
+    const std::vector<attribute_id> &partition_by_ids,
+    const bool is_row,
+    const std::int64_t num_preceding,
+    const std::int64_t num_following) const {
+  DCHECK_EQ(1u, arguments.size());
+  DCHECK(arguments[0]->isNative());
+  DCHECK_EQ(static_cast<std::size_t>(tuple_accessor->getNumTuples()),
+            static_cast<const NativeColumnVector*>(arguments[0])->size());
+
+  // Initialize the output column and argument accessor.
+  NativeColumnVector *window_aggregates =
+      new NativeColumnVector(*result_type_, tuple_accessor->getNumTuples());
+  ColumnVectorsValueAccessor* argument_accessor = new ColumnVectorsValueAccessor();
+  argument_accessor->addColumn(arguments[0]);
+
+  // Create a window for each tuple and calculate the window aggregate.
+  tuple_accessor->beginIteration();
+  argument_accessor->beginIteration();
+
+  while (tuple_accessor->next() && argument_accessor->next()) {
+    const TypedValue window_aggregate = this->calculateOneWindow(tuple_accessor,
+                                                                 argument_accessor,
+                                                                 partition_by_ids,
+                                                                 is_row,
+                                                                 num_preceding,
+                                                                 num_following);
+    window_aggregates->appendTypedValue(window_aggregate);
+  }
+
+  return window_aggregates;
+}
+
+TypedValue WindowAggregationHandleAvg::calculateOneWindow(
+    ColumnVectorsValueAccessor *tuple_accessor,
+    ColumnVectorsValueAccessor *argument_accessor,
+    const std::vector<attribute_id> &partition_by_ids,
+    const bool is_row,
+    const std::int64_t num_preceding,
+    const std::int64_t num_following) const {
+  // Initialize.
+  TypedValue sum = sum_type_->makeZeroValue();
+  TypedValue current_value = argument_accessor->getTypedValue(0);
+  std::uint64_t count = 0;
+
+  // Ignore the value if null.
+  if (!current_value.isNull()) {
+    sum = fast_add_operator_->applyToTypedValues(sum, current_value);
+    count++;
+  }
+
+  // Get the partition key for the current row.
+  std::vector<TypedValue> current_row_partition_key;
+  for (attribute_id partition_by_id : partition_by_ids) {
+    current_row_partition_key.push_back(
+        tuple_accessor->getTypedValue(partition_by_id));
+  }
+
+  // Get current position.
+  tuple_id current_tuple_id = tuple_accessor->getCurrentPositionVirtual();
+
+  // Find preceding tuples.
+  int count_preceding = 0;
+  tuple_id preceding_tuple_id = current_tuple_id;
+  while (num_preceding == -1 || count_preceding < num_preceding) {
+    preceding_tuple_id--;
+
+    // No more preceding tuples.
+    if (preceding_tuple_id < 0) {
+      break;
+    }
+
+    // Get the partition keys and compare. If not the same partition as the
+    // current row, stop searching preceding tuples.
+    if (!samePartition(tuple_accessor,
+                       current_row_partition_key,
+                       preceding_tuple_id,
+                       partition_by_ids)) {
+      break;
+    }
+
+    // Actually count the element and do the calculation.
+    count_preceding++;
+    TypedValue preceding_value =
+        argument_accessor->getTypedValueAtAbsolutePosition(0, preceding_tuple_id);
+
+    // Ignore the value if null.
+    if (!preceding_value.isNull()) {
+      sum = fast_add_operator_->applyToTypedValues(sum, preceding_value);
+      count++;
+    }
+  }
+
+  // Find following tuples.
+  int count_following = 0;
+  tuple_id following_tuple_id = current_tuple_id;
+  while (num_following == -1 || count_following < num_following) {
+    following_tuple_id++;
+
+    // No more following tuples.
+    if (following_tuple_id == tuple_accessor->getNumTuples()) {
+      break;
+    }
+
+    // Get the partition keys and compare. If not the same partition as the
+    // current row, stop searching preceding tuples.
+    if (!samePartition(tuple_accessor,
+                       current_row_partition_key,
+                       following_tuple_id,
+                       partition_by_ids)) {
+      break;
+    }
+
+    // Actually count the element and do the calculation.
+    count_following++;
+    TypedValue following_value =
+        argument_accessor->getTypedValueAtAbsolutePosition(0, following_tuple_id);
+
+    // Ignore the value if null.
+    if (!following_value.isNull()) {
+      sum = fast_add_operator_->applyToTypedValues(sum, following_value);
+      count++;
+    }
+  }
+
+  // If all values are NULLs, return NULL; Otherwise, return the quotient.
+  if (count == 0) {
+    return result_type_->makeNullValue();
+  } else {
+    return divide_operator_->applyToTypedValues(sum,
+                                                TypedValue(static_cast<double>(count)));
+  }
+}
+
+bool WindowAggregationHandleAvg::samePartition(
+    const ColumnVectorsValueAccessor *tuple_accessor,
+    const std::vector<TypedValue> &current_row_partition_key,
+    const tuple_id boundary_tuple_id,
+    const std::vector<attribute_id> &partition_by_ids) const {
+  for (std::size_t partition_by_index = 0;
+       partition_by_index < partition_by_ids.size();
+       ++partition_by_index) {
+    if (!equal_comparators_[partition_by_index]->compareTypedValues(
+            current_row_partition_key[partition_by_index],
+            tuple_accessor->getTypedValueAtAbsolutePosition(
+                partition_by_ids[partition_by_index], boundary_tuple_id))) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/WindowAggregationHandleAvg.hpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregationHandleAvg.hpp b/expressions/window_aggregation/WindowAggregationHandleAvg.hpp
new file mode 100644
index 0000000..5b41779
--- /dev/null
+++ b/expressions/window_aggregation/WindowAggregationHandleAvg.hpp
@@ -0,0 +1,101 @@
+/**
+ *   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.
+ **/
+
+#ifndef QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATION_HANDLE_AVG_HPP_
+#define QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATION_HANDLE_AVG_HPP_
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <queue>
+#include <vector>
+
+#include "catalog/CatalogTypedefs.hpp"
+#include "expressions/window_aggregation/WindowAggregationHandle.hpp"
+#include "types/Type.hpp"
+#include "types/TypedValue.hpp"
+#include "types/operations/binary_operations/BinaryOperation.hpp"
+#include "types/operations/comparisons/Comparison.hpp"
+#include "utility/Macros.hpp"
+
+#include "glog/logging.h"
+
+namespace quickstep {
+
+class ColumnVector;
+class ColumnVectorsValueAccessor;
+class ValueAccessor;
+
+/** \addtogroup Expressions
+ *  @{
+ */
+
+/**
+ * @brief A WindowAggregationHandle for average.
+ **/
+class WindowAggregationHandleAvg : public WindowAggregationHandle {
+ public:
+  ~WindowAggregationHandleAvg() override {}
+
+  ColumnVector* calculate(ColumnVectorsValueAccessor* block_accessors,
+                          std::vector<ColumnVector*> &&arguments,
+                          const std::vector<attribute_id> &partition_by_ids,
+                          const bool is_row,
+                          const std::int64_t num_preceding,
+                          const std::int64_t num_following) const override;
+
+ private:
+  friend class WindowAggregateFunctionAvg;
+
+  /**
+   * @brief Constructor.
+   *
+   * @param partition_key_types The Types of the partition key.
+   * @param type Type of the avg value.
+   **/
+  WindowAggregationHandleAvg(const std::vector<const Type*> &partition_key_types,
+                             const Type &type);
+
+  TypedValue calculateOneWindow(
+      ColumnVectorsValueAccessor *tuple_accessor,
+      ColumnVectorsValueAccessor *argument_accessor,
+      const std::vector<attribute_id> &partition_by_ids,
+      const bool is_row,
+      const std::int64_t num_preceding,
+      const std::int64_t num_following) const;
+
+  bool samePartition(const ColumnVectorsValueAccessor *tuple_accessor,
+                     const std::vector<TypedValue> &current_row_partition_key,
+                     const tuple_id boundary_tuple_id,
+                     const std::vector<attribute_id> &partition_by_ids) const;
+
+  const Type &argument_type_;
+  const Type *sum_type_;
+  const Type *result_type_;
+  std::unique_ptr<UncheckedBinaryOperator> fast_add_operator_;
+  std::unique_ptr<UncheckedBinaryOperator> divide_operator_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowAggregationHandleAvg);
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATION_HANDLE_AVG_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/WindowAggregationID.hpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregationID.hpp b/expressions/window_aggregation/WindowAggregationID.hpp
new file mode 100644
index 0000000..8122df3
--- /dev/null
+++ b/expressions/window_aggregation/WindowAggregationID.hpp
@@ -0,0 +1,44 @@
+/**
+ *   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.
+ **/
+
+#ifndef QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATION_ID_HPP_
+#define QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATION_ID_HPP_
+
+namespace quickstep {
+
+/** \addtogroup Expressions
+ *  @{
+ */
+
+/**
+ * @brief The possible types of window aggregations.
+ **/
+enum class WindowAggregationID {
+  kAvg,
+  kCount,
+  kMin,
+  kMax,
+  kSum
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATION_ID_HPP_



[14/30] incubator-quickstep git commit: - Supported ROWS mode for AVG window aggregation. - Created WindowAggregateFunctions in expressions/window_aggregation. - Created WindowAggregationHandle for AVG to actually do the calculation. - Other functions wi

Posted by ra...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/expressions/window_aggregation/tests/WindowAggregationHandleAvg_unittest.cpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/tests/WindowAggregationHandleAvg_unittest.cpp b/expressions/window_aggregation/tests/WindowAggregationHandleAvg_unittest.cpp
new file mode 100644
index 0000000..c044a98
--- /dev/null
+++ b/expressions/window_aggregation/tests/WindowAggregationHandleAvg_unittest.cpp
@@ -0,0 +1,387 @@
+/**
+ *   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.
+ *   limitations under the License.
+ **/
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+#include "catalog/CatalogTypedefs.hpp"
+#include "expressions/window_aggregation/WindowAggregateFunction.hpp"
+#include "expressions/window_aggregation/WindowAggregateFunctionFactory.hpp"
+#include "expressions/window_aggregation/WindowAggregationHandle.hpp"
+#include "expressions/window_aggregation/WindowAggregationHandleAvg.hpp"
+#include "expressions/window_aggregation/WindowAggregationID.hpp"
+#include "storage/ValueAccessor.hpp"
+#include "types/CharType.hpp"
+#include "types/DateOperatorOverloads.hpp"
+#include "types/DatetimeIntervalType.hpp"
+#include "types/DoubleType.hpp"
+#include "types/FloatType.hpp"
+#include "types/IntType.hpp"
+#include "types/IntervalLit.hpp"
+#include "types/LongType.hpp"
+#include "types/Type.hpp"
+#include "types/TypeFactory.hpp"
+#include "types/TypeID.hpp"
+#include "types/TypedValue.hpp"
+#include "types/VarCharType.hpp"
+#include "types/YearMonthIntervalType.hpp"
+#include "types/containers/ColumnVector.hpp"
+#include "types/containers/ColumnVectorsValueAccessor.hpp"
+
+#include "gtest/gtest.h"
+
+namespace quickstep {
+
+namespace {
+
+  constexpr int kNumTuples = 100;
+  constexpr int kNumTuplesPerPartition = 8;
+  constexpr int kNullInterval = 25;
+  constexpr int kNumPreceding = 2;
+  constexpr int kNumFollowing = 2;
+
+}  // namespace
+
+// Attribute value could be null if set true.
+class WindowAggregationHandleAvgTest : public::testing::Test {
+ protected:
+  // Handle initialization.
+  void initializeHandle(const Type &argument_type) {
+    const WindowAggregateFunction &function =
+        WindowAggregateFunctionFactory::Get(WindowAggregationID::kAvg);
+    std::vector<const Type*> partition_key_types(1, &TypeFactory::GetType(kInt, false));
+    handle_avg_.reset(function.createHandle(std::vector<const Type*>(1, &argument_type),
+                                            std::move(partition_key_types)));
+  }
+
+  // Test canApplyToTypes().
+  static bool CanApplyToTypesTest(TypeID typeID) {
+    const Type &type = (typeID == kChar || typeID == kVarChar) ?
+        TypeFactory::GetType(typeID, static_cast<std::size_t>(10)) :
+        TypeFactory::GetType(typeID);
+
+    return WindowAggregateFunctionFactory::Get(WindowAggregationID::kAvg).canApplyToTypes(
+        std::vector<const Type*>(1, &type));
+  }
+
+  // Test resultTypeForArgumentTypes().
+  static bool ResultTypeForArgumentTypesTest(TypeID input_type_id,
+                                             TypeID output_type_id) {
+    const Type *result_type
+        = WindowAggregateFunctionFactory::Get(WindowAggregationID::kAvg).resultTypeForArgumentTypes(
+            std::vector<const Type*>(1, &TypeFactory::GetType(input_type_id)));
+    return (result_type->getTypeID() == output_type_id);
+  }
+
+  template <typename CppType>
+  static void CheckAvgValues(
+      const std::vector<CppType*> &expected,
+      const ColumnVector *actual) {
+    EXPECT_TRUE(actual->isNative());
+    const NativeColumnVector *native = static_cast<const NativeColumnVector*>(actual);
+
+    EXPECT_EQ(expected.size(), native->size());
+    for (std::size_t i = 0; i < expected.size(); ++i) {
+      if (expected[i] == nullptr) {
+        EXPECT_TRUE(native->getTypedValue(i).isNull());
+      } else {
+        EXPECT_EQ(*expected[i], native->getTypedValue(i).getLiteral<CppType>());
+      }
+    }
+  }
+
+  // Static templated methods for set a meaningful value to data types.
+  template <typename CppType>
+  static void SetDataType(int value, CppType *data) {
+    *data = value;
+  }
+
+  template <typename GenericType, typename OutputType = DoubleType>
+  void checkWindowAggregationAvgGeneric() {
+    const GenericType &type = GenericType::Instance(true);
+    initializeHandle(type);
+
+    // Create argument, partition key and cpptype vectors.
+    std::vector<typename GenericType::cpptype*> argument_cpp_vector;
+    argument_cpp_vector.reserve(kNumTuples);
+    ColumnVector *argument_type_vector =
+        createArgumentGeneric<GenericType>(&argument_cpp_vector);
+    NativeColumnVector *partition_key_vector =
+        new NativeColumnVector(IntType::InstanceNonNullable(), kNumTuples + 2);
+
+    for (int i = 0; i < kNumTuples; ++i) {
+      partition_key_vector->appendTypedValue(TypedValue(i / kNumTuplesPerPartition));
+    }
+
+    // Create tuple ValueAccessor.
+    ColumnVectorsValueAccessor *tuple_accessor = new ColumnVectorsValueAccessor();
+    tuple_accessor->addColumn(partition_key_vector);
+    tuple_accessor->addColumn(argument_type_vector);
+
+    // Test UNBOUNDED PRECEDING AND CURRENT ROW.
+    checkAccumulate<GenericType, OutputType>(tuple_accessor,
+                                             argument_type_vector,
+                                             argument_cpp_vector);
+    // Test kNumPreceding PRECEDING AND kNumFollowing FOLLOWING.
+    checkSlidingWindow<GenericType, OutputType>(tuple_accessor,
+                                                argument_type_vector,
+                                                argument_cpp_vector);
+  }
+
+  template <typename GenericType>
+  ColumnVector *createArgumentGeneric(
+      std::vector<typename GenericType::cpptype*> *argument_cpp_vector) {
+    const GenericType &type = GenericType::Instance(true);
+    NativeColumnVector *column = new NativeColumnVector(type, kNumTuples);
+
+    for (int i = 0; i < kNumTuples; ++i) {
+      // Insert a NULL every kNullInterval tuples.
+      if (i % kNullInterval == 0) {
+        argument_cpp_vector->push_back(nullptr);
+        column->appendTypedValue(type.makeNullValue());
+        continue;
+      }
+
+      typename GenericType::cpptype *val = new typename GenericType::cpptype;
+
+      if (type.getTypeID() == kInt || type.getTypeID() == kLong) {
+        SetDataType(i - 10, val);
+      } else {
+        SetDataType(static_cast<float>(i - 10) / 10, val);
+      }
+
+      column->appendTypedValue(type.makeValue(val));
+      argument_cpp_vector->push_back(val);
+    }
+
+    return column;
+  }
+
+  template <typename GenericType, typename OutputType>
+  void checkAccumulate(ColumnVectorsValueAccessor *tuple_accessor,
+                       ColumnVector *argument_type_vector,
+                       const std::vector<typename GenericType::cpptype*> &argument_cpp_vector) {
+    std::vector<ColumnVector*> arguments;
+    arguments.push_back(argument_type_vector);
+    // The partition key index is 0.
+    std::vector<attribute_id> partition_key(1, 0);
+
+    ColumnVector *result =
+        handle_avg_->calculate(tuple_accessor,
+                               std::move(arguments),
+                               partition_key,
+                               true  /* is_row */,
+                               -1  /* num_preceding: UNBOUNDED PRECEDING */,
+                               0  /* num_following: CURRENT ROW */);
+
+    // Get the cpptype result.
+    std::vector<typename OutputType::cpptype*> result_cpp_vector;
+    typename GenericType::cpptype sum;
+    int count;
+    for (std::size_t i = 0; i < argument_cpp_vector.size(); ++i) {
+      // Start of new partition
+      if (i % kNumTuplesPerPartition == 0) {
+        SetDataType(0, &sum);
+        count = 0;
+      }
+
+      typename GenericType::cpptype *value = argument_cpp_vector[i];
+      if (value != nullptr) {
+        sum += *value;
+        count++;
+      }
+
+      if (count == 0) {
+        result_cpp_vector.push_back(nullptr);
+      } else {
+        typename OutputType::cpptype *result_cpp_value =
+            new typename OutputType::cpptype;
+        *result_cpp_value = static_cast<typename OutputType::cpptype>(sum) / count;
+        result_cpp_vector.push_back(result_cpp_value);
+      }
+    }
+
+    CheckAvgValues(result_cpp_vector, result);
+  }
+
+  template <typename GenericType, typename OutputType>
+  void checkSlidingWindow(ColumnVectorsValueAccessor *tuple_accessor,
+                          ColumnVector *argument_type_vector,
+                          const std::vector<typename GenericType::cpptype*> &argument_cpp_vector) {
+    std::vector<ColumnVector*> arguments;
+    arguments.push_back(argument_type_vector);
+    // The partition key index is 0.
+    std::vector<attribute_id> partition_key(1, 0);
+
+    ColumnVector *result =
+        handle_avg_->calculate(tuple_accessor,
+                               std::move(arguments),
+                               partition_key,
+                               true  /* is_row */,
+                               kNumPreceding,
+                               kNumFollowing);
+
+    // Get the cpptype result.
+    // For each value, calculate all surrounding values in the window.
+    std::vector<typename OutputType::cpptype*> result_cpp_vector;
+
+    for (std::size_t i = 0; i < argument_cpp_vector.size(); ++i) {
+      typename GenericType::cpptype sum;
+      SetDataType(0, &sum);
+      int count = 0;
+
+      if (argument_cpp_vector[i] != nullptr) {
+        sum += *argument_cpp_vector[i];
+        count++;
+      }
+
+      for (std::size_t precede = 1; precede <= kNumPreceding; ++precede) {
+        // Not the same partition.
+        if (i / kNumTuplesPerPartition != (i - precede) / kNumTuplesPerPartition ||
+            i < precede) {
+          break;
+        }
+
+        if (argument_cpp_vector[i - precede] != nullptr) {
+          sum += *argument_cpp_vector[i - precede];
+          count++;
+        }
+      }
+
+      for (int follow = 1; follow <= kNumPreceding; ++follow) {
+        // Not the same partition.
+        if (i / kNumTuplesPerPartition != (i + follow) / kNumTuplesPerPartition ||
+            i + follow >= kNumTuples) {
+          break;
+        }
+
+        if (argument_cpp_vector[i + follow] != nullptr) {
+          sum += *argument_cpp_vector[i + follow];
+          count++;
+        }
+      }
+
+      if (count == 0) {
+        result_cpp_vector.push_back(nullptr);
+      } else {
+        typename OutputType::cpptype *result_cpp_value =
+            new typename OutputType::cpptype;
+        *result_cpp_value = static_cast<typename OutputType::cpptype>(sum) / count;
+        result_cpp_vector.push_back(result_cpp_value);
+      }
+    }
+
+    CheckAvgValues(result_cpp_vector, result);
+  }
+
+  std::unique_ptr<WindowAggregationHandle> handle_avg_;
+};
+
+template <>
+void WindowAggregationHandleAvgTest::CheckAvgValues<double>(
+    const std::vector<double*> &expected,
+    const ColumnVector *actual) {
+  EXPECT_TRUE(actual->isNative());
+  const NativeColumnVector *native = static_cast<const NativeColumnVector*>(actual);
+
+  EXPECT_EQ(expected.size(), native->size());
+  for (std::size_t i = 0; i < expected.size(); ++i) {
+    if (expected[i] == nullptr) {
+      EXPECT_TRUE(native->getTypedValue(i).isNull());
+    } else {
+      EXPECT_EQ(*expected[i], native->getTypedValue(i).getLiteral<double>());
+    }
+  }
+}
+
+template <>
+void WindowAggregationHandleAvgTest::SetDataType<DatetimeIntervalLit>(
+    int value, DatetimeIntervalLit *data) {
+  data->interval_ticks = value;
+}
+
+template <>
+void WindowAggregationHandleAvgTest::SetDataType<YearMonthIntervalLit>(
+    int value, YearMonthIntervalLit *data) {
+  data->months = value;
+}
+
+typedef WindowAggregationHandleAvgTest WindowAggregationHandleAvgDeathTest;
+
+TEST_F(WindowAggregationHandleAvgTest, IntTypeTest) {
+  checkWindowAggregationAvgGeneric<IntType>();
+}
+
+TEST_F(WindowAggregationHandleAvgTest, LongTypeTest) {
+  checkWindowAggregationAvgGeneric<LongType>();
+}
+
+TEST_F(WindowAggregationHandleAvgTest, FloatTypeTest) {
+  checkWindowAggregationAvgGeneric<FloatType>();
+}
+
+TEST_F(WindowAggregationHandleAvgTest, DoubleTypeTest) {
+  checkWindowAggregationAvgGeneric<DoubleType>();
+}
+
+TEST_F(WindowAggregationHandleAvgTest, DatetimeIntervalTypeTest) {
+  checkWindowAggregationAvgGeneric<DatetimeIntervalType, DatetimeIntervalType>();
+}
+
+TEST_F(WindowAggregationHandleAvgTest, YearMonthIntervalTypeTest) {
+  checkWindowAggregationAvgGeneric<YearMonthIntervalType, YearMonthIntervalType>();
+}
+
+#ifdef QUICKSTEP_DEBUG
+TEST_F(WindowAggregationHandleAvgDeathTest, CharTypeTest) {
+  const Type &type = CharType::Instance(true, 10);
+  EXPECT_DEATH(initializeHandle(type), "");
+}
+
+TEST_F(WindowAggregationHandleAvgDeathTest, VarTypeTest) {
+  const Type &type = VarCharType::Instance(true, 10);
+  EXPECT_DEATH(initializeHandle(type), "");
+}
+#endif
+
+TEST_F(WindowAggregationHandleAvgDeathTest, canApplyToTypeTest) {
+  EXPECT_TRUE(CanApplyToTypesTest(kInt));
+  EXPECT_TRUE(CanApplyToTypesTest(kLong));
+  EXPECT_TRUE(CanApplyToTypesTest(kFloat));
+  EXPECT_TRUE(CanApplyToTypesTest(kDouble));
+  EXPECT_FALSE(CanApplyToTypesTest(kChar));
+  EXPECT_FALSE(CanApplyToTypesTest(kVarChar));
+  EXPECT_FALSE(CanApplyToTypesTest(kDatetime));
+  EXPECT_TRUE(CanApplyToTypesTest(kDatetimeInterval));
+  EXPECT_TRUE(CanApplyToTypesTest(kYearMonthInterval));
+}
+
+TEST_F(WindowAggregationHandleAvgDeathTest, ResultTypeForArgumentTypeTest) {
+  EXPECT_TRUE(ResultTypeForArgumentTypesTest(kInt, kDouble));
+  EXPECT_TRUE(ResultTypeForArgumentTypesTest(kLong, kDouble));
+  EXPECT_TRUE(ResultTypeForArgumentTypesTest(kFloat, kDouble));
+  EXPECT_TRUE(ResultTypeForArgumentTypesTest(kDouble, kDouble));
+  EXPECT_TRUE(ResultTypeForArgumentTypesTest(kDatetimeInterval, kDatetimeInterval));
+  EXPECT_TRUE(ResultTypeForArgumentTypesTest(kYearMonthInterval, kYearMonthInterval));
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/query_optimizer/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/query_optimizer/CMakeLists.txt b/query_optimizer/CMakeLists.txt
index 7e53b9d..a56b714 100644
--- a/query_optimizer/CMakeLists.txt
+++ b/query_optimizer/CMakeLists.txt
@@ -69,6 +69,8 @@ target_link_libraries(quickstep_queryoptimizer_ExecutionGenerator
                       quickstep_expressions_predicate_Predicate
                       quickstep_expressions_scalar_Scalar
                       quickstep_expressions_scalar_ScalarAttribute
+                      quickstep_expressions_windowaggregation_WindowAggregateFunction
+                      quickstep_expressions_windowaggregation_WindowAggregateFunction_proto
                       quickstep_queryexecution_QueryContext
                       quickstep_queryexecution_QueryContext_proto
                       quickstep_queryoptimizer_ExecutionHeuristics

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/query_optimizer/ExecutionGenerator.cpp
----------------------------------------------------------------------
diff --git a/query_optimizer/ExecutionGenerator.cpp b/query_optimizer/ExecutionGenerator.cpp
index 43d63f9..ce21ade 100644
--- a/query_optimizer/ExecutionGenerator.cpp
+++ b/query_optimizer/ExecutionGenerator.cpp
@@ -48,6 +48,8 @@
 #include "expressions/predicate/Predicate.hpp"
 #include "expressions/scalar/Scalar.hpp"
 #include "expressions/scalar/ScalarAttribute.hpp"
+#include "expressions/window_aggregation/WindowAggregateFunction.hpp"
+#include "expressions/window_aggregation/WindowAggregateFunction.pb.h"
 #include "query_execution/QueryContext.hpp"
 #include "query_execution/QueryContext.pb.h"
 #include "query_optimizer/ExecutionHeuristics.hpp"
@@ -1652,7 +1654,7 @@ void ExecutionGenerator::convertWindowAggregate(
   // Get input.
   const CatalogRelationInfo *input_relation_info =
       findRelationInfoOutputByPhysical(physical_plan->input());
-  window_aggr_state_proto->set_relation_id(input_relation_info->relation->getID());
+  window_aggr_state_proto->set_input_relation_id(input_relation_info->relation->getID());
 
   // Get window aggregate function expression.
   const E::AliasPtr &named_window_aggregate_expression =
@@ -1713,6 +1715,7 @@ void ExecutionGenerator::convertWindowAggregate(
   const QueryPlan::DAGNodeIndex window_aggregation_operator_index =
       execution_plan_->addRelationalOperator(
           new WindowAggregationOperator(query_handle_->query_id(),
+                                        *input_relation_info->relation,
                                         *output_relation,
                                         window_aggr_state_index,
                                         insert_destination_index));

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/query_optimizer/expressions/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/query_optimizer/expressions/CMakeLists.txt b/query_optimizer/expressions/CMakeLists.txt
index 08d7df5..d12644a 100644
--- a/query_optimizer/expressions/CMakeLists.txt
+++ b/query_optimizer/expressions/CMakeLists.txt
@@ -304,7 +304,7 @@ target_link_libraries(quickstep_queryoptimizer_expressions_UnaryExpression
                       quickstep_utility_Macros)
 target_link_libraries(quickstep_queryoptimizer_expressions_WindowAggregateFunction
                       glog
-                      quickstep_expressions_aggregation_AggregateFunction
+                      quickstep_expressions_windowaggregation_WindowAggregateFunction
                       quickstep_queryoptimizer_OptimizerTree
                       quickstep_queryoptimizer_expressions_AttributeReference
                       quickstep_queryoptimizer_expressions_Expression

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/query_optimizer/expressions/WindowAggregateFunction.cpp
----------------------------------------------------------------------
diff --git a/query_optimizer/expressions/WindowAggregateFunction.cpp b/query_optimizer/expressions/WindowAggregateFunction.cpp
index 7b1f304..be5db59 100644
--- a/query_optimizer/expressions/WindowAggregateFunction.cpp
+++ b/query_optimizer/expressions/WindowAggregateFunction.cpp
@@ -22,7 +22,7 @@
 #include <utility>
 #include <vector>
 
-#include "expressions/aggregation/AggregateFunction.hpp"
+#include "expressions/window_aggregation/WindowAggregateFunction.hpp"
 #include "query_optimizer/expressions/AttributeReference.hpp"
 #include "query_optimizer/expressions/Expression.hpp"
 #include "query_optimizer/expressions/PatternMatcher.hpp"
@@ -59,7 +59,7 @@ const Type& WindowAggregateFunction::getValueType() const {
 }
 
 WindowAggregateFunctionPtr WindowAggregateFunction::Create(
-    const ::quickstep::AggregateFunction &window_aggregate,
+    const ::quickstep::WindowAggregateFunction &window_aggregate,
     const std::vector<ScalarPtr> &arguments,
     const WindowInfo &window_info,
     const std::string &window_name,

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/query_optimizer/expressions/WindowAggregateFunction.hpp
----------------------------------------------------------------------
diff --git a/query_optimizer/expressions/WindowAggregateFunction.hpp b/query_optimizer/expressions/WindowAggregateFunction.hpp
index 0bee28f..0cc80df 100644
--- a/query_optimizer/expressions/WindowAggregateFunction.hpp
+++ b/query_optimizer/expressions/WindowAggregateFunction.hpp
@@ -33,8 +33,8 @@
 
 namespace quickstep {
 
-class AggregateFunction;
 class Type;
+class WindowAggregateFunction;
 
 namespace optimizer {
 namespace expressions {
@@ -140,7 +140,7 @@ class WindowAggregateFunction : public Expression {
    * @return The WindowAggregateFunction singleton (from the expression system)
    *         for this node.
    **/
-  inline const ::quickstep::AggregateFunction& window_aggregate() const {
+  inline const ::quickstep::WindowAggregateFunction& window_aggregate() const {
     return window_aggregate_;
   }
 
@@ -185,7 +185,7 @@ class WindowAggregateFunction : public Expression {
    * @param is_distinct Whether this is a DISTINCT aggregation.
    * @return A new AggregateFunctionPtr.
    **/
-  static WindowAggregateFunctionPtr Create(const ::quickstep::AggregateFunction &window_aggregate,
+  static WindowAggregateFunctionPtr Create(const ::quickstep::WindowAggregateFunction &window_aggregate,
                                            const std::vector<ScalarPtr> &arguments,
                                            const WindowInfo &window_info,
                                            const std::string &window_name,
@@ -209,7 +209,7 @@ class WindowAggregateFunction : public Expression {
    * @param window_info The window info of the window aggregate function.
    * @param is_distinct Indicates whether this is a DISTINCT aggregation.
    */
-  WindowAggregateFunction(const ::quickstep::AggregateFunction &window_aggregate,
+  WindowAggregateFunction(const ::quickstep::WindowAggregateFunction &window_aggregate,
                           const std::vector<ScalarPtr> &arguments,
                           const WindowInfo &window_info,
                           const std::string &window_name,
@@ -228,7 +228,7 @@ class WindowAggregateFunction : public Expression {
   // window_aggregate_. If it really needs to be seperated from the
   // AggregationFunction, a new class for WindowAggregationFunction should be
   // created as quickstep::WindowAggregateFunction.
-  const ::quickstep::AggregateFunction &window_aggregate_;
+  const ::quickstep::WindowAggregateFunction &window_aggregate_;
   std::vector<ScalarPtr> arguments_;
   const WindowInfo window_info_;
   const std::string window_name_;

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/query_optimizer/resolver/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/query_optimizer/resolver/CMakeLists.txt b/query_optimizer/resolver/CMakeLists.txt
index fb75767..9313e51 100644
--- a/query_optimizer/resolver/CMakeLists.txt
+++ b/query_optimizer/resolver/CMakeLists.txt
@@ -39,6 +39,8 @@ target_link_libraries(quickstep_queryoptimizer_resolver_Resolver
                       quickstep_expressions_tablegenerator_GeneratorFunction
                       quickstep_expressions_tablegenerator_GeneratorFunctionFactory
                       quickstep_expressions_tablegenerator_GeneratorFunctionHandle
+                      quickstep_expressions_windowaggregation_WindowAggregateFunction
+                      quickstep_expressions_windowaggregation_WindowAggregateFunctionFactory
                       quickstep_parser_ParseAssignment
                       quickstep_parser_ParseBasicExpressions
                       quickstep_parser_ParseBlockProperties

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/query_optimizer/resolver/Resolver.cpp
----------------------------------------------------------------------
diff --git a/query_optimizer/resolver/Resolver.cpp b/query_optimizer/resolver/Resolver.cpp
index f10378b..c224388 100644
--- a/query_optimizer/resolver/Resolver.cpp
+++ b/query_optimizer/resolver/Resolver.cpp
@@ -35,6 +35,8 @@
 #include "expressions/table_generator/GeneratorFunction.hpp"
 #include "expressions/table_generator/GeneratorFunctionFactory.hpp"
 #include "expressions/table_generator/GeneratorFunctionHandle.hpp"
+#include "expressions/window_aggregation/WindowAggregateFunction.hpp"
+#include "expressions/window_aggregation/WindowAggregateFunctionFactory.hpp"
 #include "parser/ParseAssignment.hpp"
 #include "parser/ParseBasicExpressions.hpp"
 #include "parser/ParseBlockProperties.hpp"
@@ -2624,11 +2626,19 @@ E::ScalarPtr Resolver::resolveFunctionCall(
         << "COUNT aggregate has both star (*) and non-star arguments.";
   }
 
-  // Try to look up the AggregateFunction by name using
-  // AggregateFunctionFactory.
-  const ::quickstep::AggregateFunction *aggregate
-      = AggregateFunctionFactory::GetByName(function_name);
-  if (aggregate == nullptr) {
+  // Try to look up the AggregateFunction/WindowAggregationFunction by name.
+  // TODO(Shixuan): We might want to create a new abstract class Function to
+  // include both AggregateFunction and WindowAggregateFunction, which will make
+  // this part of code cleaner.
+  const ::quickstep::AggregateFunction *aggregate = nullptr;
+  const ::quickstep::WindowAggregateFunction *window_aggregate = nullptr;
+  if (parse_function_call.isWindow()) {
+    window_aggregate = WindowAggregateFunctionFactory::GetByName(function_name);
+  } else {
+    aggregate = AggregateFunctionFactory::GetByName(function_name);
+  }
+
+  if (aggregate == nullptr && window_aggregate == nullptr) {
     THROW_SQL_ERROR_AT(&parse_function_call)
         << "Unrecognized function name \""
         << parse_function_call.name()->value()
@@ -2656,11 +2666,14 @@ E::ScalarPtr Resolver::resolveFunctionCall(
   }
 
   // Make sure a naked COUNT() with no arguments wasn't specified.
-  if ((aggregate->getAggregationID() == AggregationID::kCount)
-      && (resolved_arguments.empty())
-      && (!count_star)) {
-    THROW_SQL_ERROR_AT(&parse_function_call)
-        << "COUNT aggregate requires an argument (either scalar or star (*))";
+  if ((aggregate != nullptr &&
+       aggregate->getAggregationID() == AggregationID::kCount) ||
+      (window_aggregate != nullptr &&
+       window_aggregate->getWindowAggregationID() == WindowAggregationID::kCount)) {
+    if ((resolved_arguments.empty()) && !count_star) {
+      THROW_SQL_ERROR_AT(&parse_function_call)
+          << "COUNT aggregate requires an argument (either scalar or star (*))";
+    }
   }
 
   // Resolve each of the Scalar arguments to the aggregate.
@@ -2670,7 +2683,8 @@ E::ScalarPtr Resolver::resolveFunctionCall(
   }
 
   // Make sure that the aggregate can apply to the specified argument(s).
-  if (!aggregate->canApplyToTypes(argument_types)) {
+  if ((aggregate != nullptr && !aggregate->canApplyToTypes(argument_types))
+      || (window_aggregate != nullptr && !window_aggregate->canApplyToTypes(argument_types))) {
     THROW_SQL_ERROR_AT(&parse_function_call)
         << "Aggregate function " << aggregate->getName()
         << " can not apply to the given argument(s).";
@@ -2679,7 +2693,7 @@ E::ScalarPtr Resolver::resolveFunctionCall(
   if (parse_function_call.isWindow()) {
     return resolveWindowAggregateFunction(parse_function_call,
                                           expression_resolution_info,
-                                          aggregate,
+                                          window_aggregate,
                                           resolved_arguments);
   }
 
@@ -2705,7 +2719,7 @@ E::ScalarPtr Resolver::resolveFunctionCall(
 E::ScalarPtr Resolver::resolveWindowAggregateFunction(
     const ParseFunctionCall &parse_function_call,
     ExpressionResolutionInfo *expression_resolution_info,
-    const ::quickstep::AggregateFunction *window_aggregate,
+    const ::quickstep::WindowAggregateFunction *window_aggregate,
     const std::vector<E::ScalarPtr> &resolved_arguments) {
   // A window aggregate function might be defined OVER a window name or a window.
   E::WindowAggregateFunctionPtr window_aggregate_function;

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/query_optimizer/resolver/Resolver.hpp
----------------------------------------------------------------------
diff --git a/query_optimizer/resolver/Resolver.hpp b/query_optimizer/resolver/Resolver.hpp
index f4024e9..373430c 100644
--- a/query_optimizer/resolver/Resolver.hpp
+++ b/query_optimizer/resolver/Resolver.hpp
@@ -23,7 +23,6 @@
 #include <unordered_set>
 #include <vector>
 
-#include "query_optimizer/expressions/AggregateFunction.hpp"
 #include "query_optimizer/expressions/Alias.hpp"
 #include "query_optimizer/expressions/ExprId.hpp"
 #include "query_optimizer/expressions/NamedExpression.hpp"
@@ -460,14 +459,14 @@ class Resolver {
    * @param expression_resolution_info Resolution info that contains the name
    *                                   resolver and info to be updated after
    *                                   resolution.
-   * @param aggregate The aggregate function.
+   * @param aggregate The window aggregate function.
    * @param resolved_arguments The resolved arguments.
    * @return An expression in the query optimizer.
    */
   expressions::ScalarPtr resolveWindowAggregateFunction(
       const ParseFunctionCall &parse_function_call,
       ExpressionResolutionInfo *expression_resolution_info,
-      const ::quickstep::AggregateFunction *aggregate,
+      const ::quickstep::WindowAggregateFunction *aggregate,
       const std::vector<expressions::ScalarPtr> &resolved_arguments);
 
   /**

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/query_optimizer/tests/execution_generator/Select.test
----------------------------------------------------------------------
diff --git a/query_optimizer/tests/execution_generator/Select.test b/query_optimizer/tests/execution_generator/Select.test
index 16127cc..30a3c39 100644
--- a/query_optimizer/tests/execution_generator/Select.test
+++ b/query_optimizer/tests/execution_generator/Select.test
@@ -953,19 +953,79 @@ WHERE double_col < 0
 ==
 
 # Window Aggregation Test.
-# Currently this is not supported, an empty table will be returned.
-SELECT avg(int_col) OVER w FROM test
+SELECT char_col, long_col, avg(long_col) OVER w FROM test
 WINDOW w AS
-(PARTITION BY char_col
- ORDER BY long_col DESC NULLS LAST
+(ORDER BY char_col DESC NULLS LAST
  ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW);
 --
-+------------------------+
-|avg(int_col)            |
-+------------------------+
-+------------------------+
++--------------------+--------------------+------------------------+
+|char_col            |long_col            |avg(long_col)           |
++--------------------+--------------------+------------------------+
+|          8 2.828427|                  64|                      64|
+|          6 2.449490|                  36|                      50|
+|          4 2.000000|                  16|      38.666666666666664|
+|         24 4.898979|                 576|                     173|
+|         22 4.690416|                 484|      235.19999999999999|
+|         20 4.472136|                 400|      262.66666666666669|
+|          2 1.414214|                   4|      225.71428571428572|
+|         18 4.242641|                 324|                     238|
+|         16 4.000000|                 256|                     240|
+|         14 3.741657|                 196|      235.59999999999999|
+|         12 3.464102|                 144|      227.27272727272728|
+|         10 3.162278|                 100|      216.66666666666666|
+|          0 0.000000|                   0|                     200|
+|         -9 3.000000|                  81|                   191.5|
+|         -7 2.645751|                  49|                     182|
+|         -5 2.236068|                  25|                172.1875|
+|         -3 1.732051|                   9|      162.58823529411765|
+|        -23 4.795832|                 529|      182.94444444444446|
+|        -21 4.582576|                 441|      196.52631578947367|
+|        -19 4.358899|                 361|                  204.75|
+|        -17 4.123106|                 289|      208.76190476190476|
+|        -15 3.872983|                 225|                   209.5|
+|        -13 3.605551|                 169|       207.7391304347826|
+|        -11 3.316625|                 121|                 204.125|
+|         -1 1.000000|                   1|                     196|
++--------------------+--------------------+------------------------+
 ==
 
+SELECT long_col, int_col, avg(int_col) OVER w FROM test
+WINDOW w AS
+(ORDER BY long_col DESC NULLS LAST
+ ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING);
+--
++--------------------+-----------+------------------------+
+|long_col            |int_col    |avg(int_col)            |
++--------------------+-----------+------------------------+
+|                 576|         24|       7.666666666666667|
+|                 529|        -23|                     0.5|
+|                 484|         22|                     0.5|
+|                 441|        -21|                  -10.25|
+|                 400|       NULL|                       0|
+|                 361|        -19|                   -9.75|
+|                 324|         18|                    -0.5|
+|                 289|        -17|     -3.3999999999999999|
+|                 256|         16|      3.2000000000000002|
+|                 225|        -15|                      -3|
+|                 196|         14|      2.7999999999999998|
+|                 169|        -13|     -2.6000000000000001|
+|                 144|         12|                     0.5|
+|                 121|        -11|                   -5.25|
+|                 100|       NULL|                       0|
+|                  81|         -9|                   -4.75|
+|                  64|          8|                    -0.5|
+|                  49|         -7|     -1.3999999999999999|
+|                  36|          6|                     1.2|
+|                  25|         -5|                      -1|
+|                  16|          4|     0.80000000000000004|
+|                   9|         -3|    -0.59999999999999998|
+|                   4|          2|                     0.5|
+|                   1|         -1|    -0.66666666666666663|
+|                   0|       NULL|                     0.5|
++--------------------+-----------+------------------------+
+==
+
+# Currently this is not supported, an empty table will be returned.
 SELECT int_col, sum(float_col) OVER
 (PARTITION BY char_col, long_col
  ORDER BY double_col DESC NULLS LAST, int_col ASC NULLS FIRST
@@ -987,5 +1047,5 @@ WINDOW w AS
 +------------------------+
 |sum(avg(int_col))       |
 +------------------------+
-|                    NULL|
+|                     -18|
 +------------------------+

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/relational_operators/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/relational_operators/CMakeLists.txt b/relational_operators/CMakeLists.txt
index 249441d..a51370b 100644
--- a/relational_operators/CMakeLists.txt
+++ b/relational_operators/CMakeLists.txt
@@ -434,6 +434,7 @@ target_link_libraries(quickstep_relationaloperators_WindowAggregationOperator
                       quickstep_relationaloperators_WorkOrder
                       quickstep_relationaloperators_WorkOrder_proto
                       quickstep_storage_StorageBlockInfo
+                      quickstep_storage_WindowAggregationOperationState
                       quickstep_utility_Macros
                       tmb)                      
 target_link_libraries(quickstep_relationaloperators_WorkOrder

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/relational_operators/WindowAggregationOperator.cpp
----------------------------------------------------------------------
diff --git a/relational_operators/WindowAggregationOperator.cpp b/relational_operators/WindowAggregationOperator.cpp
index 93cb9d4..3149864 100644
--- a/relational_operators/WindowAggregationOperator.cpp
+++ b/relational_operators/WindowAggregationOperator.cpp
@@ -21,11 +21,13 @@
 
 #include <vector>
 
+#include "catalog/CatalogRelation.hpp"
 #include "query_execution/QueryContext.hpp"
 #include "query_execution/WorkOrderProtosContainer.hpp"
 #include "query_execution/WorkOrdersContainer.hpp"
 #include "relational_operators/WorkOrder.pb.h"
 #include "storage/StorageBlockInfo.hpp"
+#include "storage/WindowAggregationOperationState.hpp"
 
 #include "tmb/id_typedefs.h"
 
@@ -40,10 +42,14 @@ bool WindowAggregationOperator::getAllWorkOrders(
   DCHECK(query_context != nullptr);
 
   if (blocking_dependencies_met_ && !generated_) {
+    std::vector<block_id> relation_blocks =
+        input_relation_.getBlocksSnapshot();
+
     container->addNormalWorkOrder(
         new WindowAggregationWorkOrder(
             query_id_,
             query_context->releaseWindowAggregationState(window_aggregation_state_index_),
+            std::move(relation_blocks),
             query_context->getInsertDestination(output_destination_index_)),
         op_index_);
     generated_ = true;
@@ -67,6 +73,11 @@ serialization::WorkOrder* WindowAggregationOperator::createWorkOrderProto() {
   proto->set_query_id(query_id_);
   proto->SetExtension(serialization::WindowAggregationWorkOrder::window_aggr_state_index,
                       window_aggregation_state_index_);
+
+  const std::vector<block_id> relation_blocks = input_relation_.getBlocksSnapshot();
+  for (const block_id bid : relation_blocks) {
+    proto->AddExtension(serialization::WindowAggregationWorkOrder::block_ids, bid);
+  }
   proto->SetExtension(serialization::WindowAggregationWorkOrder::insert_destination_index,
                       output_destination_index_);
 
@@ -75,8 +86,8 @@ serialization::WorkOrder* WindowAggregationOperator::createWorkOrderProto() {
 
 
 void WindowAggregationWorkOrder::execute() {
-  std::cout << "Window aggregation is not supported yet.\n"
-      << "An empty table is returned\n";
+  state_->windowAggregateBlocks(output_destination_,
+                                block_ids_);
 }
 
 }  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/relational_operators/WindowAggregationOperator.hpp
----------------------------------------------------------------------
diff --git a/relational_operators/WindowAggregationOperator.hpp b/relational_operators/WindowAggregationOperator.hpp
index f3dfd14..bd83248 100644
--- a/relational_operators/WindowAggregationOperator.hpp
+++ b/relational_operators/WindowAggregationOperator.hpp
@@ -58,16 +58,19 @@ class WindowAggregationOperator : public RelationalOperator {
    *
    * @param query_id The ID of this query.
    * @param input_relation The relation to perform aggregation over.
+   * @param output_relation The relation for output.
    * @param window_aggregation_state_index The index of WindowAggregationState
    *                                       in QueryContext.
    * @param output_destination_index The index of InsertDestination in
    *                                 QueryContext for the output.
    **/
   WindowAggregationOperator(const std::size_t query_id,
+                            const CatalogRelation &input_relation,
                             const CatalogRelation &output_relation,
                             const QueryContext::window_aggregation_state_id window_aggregation_state_index,
                             const QueryContext::insert_destination_id output_destination_index)
       : RelationalOperator(query_id),
+        input_relation_(input_relation),
         output_relation_(output_relation),
         window_aggregation_state_index_(window_aggregation_state_index),
         output_destination_index_(output_destination_index),
@@ -99,6 +102,7 @@ class WindowAggregationOperator : public RelationalOperator {
    **/
   serialization::WorkOrder* createWorkOrderProto();
 
+  const CatalogRelation &input_relation_;
   const CatalogRelation &output_relation_;
   const QueryContext::window_aggregation_state_id window_aggregation_state_index_;
   const QueryContext::insert_destination_id output_destination_index_;
@@ -117,43 +121,25 @@ class WindowAggregationWorkOrder : public WorkOrder {
    *
    * @param query_id The ID of this query.
    * @param state The WindowAggregationOperatorState to use.
+   * @param block_ids The blocks' id of the input relation.
    * @param output_destination The InsertDestination for output.
    **/
   WindowAggregationWorkOrder(const std::size_t query_id,
                              WindowAggregationOperationState *state,
+                             std::vector<block_id> &&block_ids,
                              InsertDestination *output_destination)
       : WorkOrder(query_id),
         state_(state),
+        block_ids_(std::move(block_ids)),
         output_destination_(output_destination)  {}
 
   ~WindowAggregationWorkOrder() override {}
 
-  /**
-   * @brief Get the pointer to WindowAggregationOperationState.
-   * @note This is a quickfix for "unused variable". After the window aggregate
-   *       functions are built, these methods might be dropped.
-   *
-   * @return A pointer to the window aggregation operation state.
-   **/
-  WindowAggregationOperationState* state() {
-    return state_;
-  }
-
-  /**
-   * @brief Get the pointer to output destination.
-   * @note This is a quickfix for "unused variable". After the window aggregate
-   *       functions are built, these methods might be dropped.
-   *
-   * @return A pointer to the output destination.
-   **/
-  InsertDestination* output_destination() {
-    return output_destination_;
-  }
-
   void execute() override;
 
  private:
   WindowAggregationOperationState *state_;
+  const std::vector<block_id> block_ids_;
   InsertDestination *output_destination_;
 
   DISALLOW_COPY_AND_ASSIGN(WindowAggregationWorkOrder);

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/relational_operators/WorkOrder.proto
----------------------------------------------------------------------
diff --git a/relational_operators/WorkOrder.proto b/relational_operators/WorkOrder.proto
index 69dee1b..076735f 100644
--- a/relational_operators/WorkOrder.proto
+++ b/relational_operators/WorkOrder.proto
@@ -249,6 +249,7 @@ message WindowAggregationWorkOrder {
   extend WorkOrder {
     // All required
     optional uint32 window_aggr_state_index = 336;
-    optional int32 insert_destination_index = 337;
+    repeated fixed64 block_ids = 337;
+    optional int32 insert_destination_index = 338;
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/storage/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/storage/CMakeLists.txt b/storage/CMakeLists.txt
index 9df66e1..582effd 100644
--- a/storage/CMakeLists.txt
+++ b/storage/CMakeLists.txt
@@ -1053,19 +1053,26 @@ target_link_libraries(quickstep_storage_WindowAggregationOperationState
                       quickstep_catalog_CatalogTypedefs
                       quickstep_expressions_ExpressionFactories
                       quickstep_expressions_Expressions_proto
-                      quickstep_expressions_aggregation_AggregateFunction
-                      quickstep_expressions_aggregation_AggregateFunctionFactory
-                      quickstep_expressions_aggregation_AggregationHandle
-                      quickstep_expressions_aggregation_AggregationID
                       quickstep_expressions_scalar_Scalar
                       quickstep_expressions_scalar_ScalarAttribute
+                      quickstep_expressions_windowaggregation_WindowAggregateFunction
+                      quickstep_expressions_windowaggregation_WindowAggregateFunctionFactory
+                      quickstep_expressions_windowaggregation_WindowAggregationHandle
+                      quickstep_expressions_windowaggregation_WindowAggregationID
+                      quickstep_storage_InsertDestination
                       quickstep_storage_StorageBlockInfo
                       quickstep_storage_StorageManager
+                      quickstep_storage_SubBlocksReference
+                      quickstep_storage_ValueAccessor
+                      quickstep_storage_ValueAccessorUtil
                       quickstep_storage_WindowAggregationOperationState_proto
+                      quickstep_types_containers_ColumnVector
+                      quickstep_types_containers_ColumnVectorUtil
+                      quickstep_types_containers_ColumnVectorsValueAccessor
                       quickstep_utility_Macros)
 target_link_libraries(quickstep_storage_WindowAggregationOperationState_proto
-                      quickstep_expressions_aggregation_AggregateFunction_proto
                       quickstep_expressions_Expressions_proto
+                      quickstep_expressions_windowaggregation_WindowAggregateFunction_proto
                       ${PROTOBUF_LIBRARY})
 
 # Module all-in-one library:

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/storage/WindowAggregationOperationState.cpp
----------------------------------------------------------------------
diff --git a/storage/WindowAggregationOperationState.cpp b/storage/WindowAggregationOperationState.cpp
index a0bcc37..0cdfc1a 100644
--- a/storage/WindowAggregationOperationState.cpp
+++ b/storage/WindowAggregationOperationState.cpp
@@ -31,14 +31,21 @@
 #include "catalog/CatalogTypedefs.hpp"
 #include "expressions/ExpressionFactories.hpp"
 #include "expressions/Expressions.pb.h"
-#include "expressions/aggregation/AggregateFunction.hpp"
-#include "expressions/aggregation/AggregateFunctionFactory.hpp"
-#include "expressions/aggregation/AggregationHandle.hpp"
-#include "expressions/aggregation/AggregationID.hpp"
 #include "expressions/scalar/Scalar.hpp"
 #include "expressions/scalar/ScalarAttribute.hpp"
+#include "expressions/window_aggregation/WindowAggregateFunction.hpp"
+#include "expressions/window_aggregation/WindowAggregateFunctionFactory.hpp"
+#include "expressions/window_aggregation/WindowAggregationHandle.hpp"
+#include "expressions/window_aggregation/WindowAggregationID.hpp"
+#include "storage/InsertDestination.hpp"
 #include "storage/StorageManager.hpp"
+#include "storage/SubBlocksReference.hpp"
+#include "storage/ValueAccessor.hpp"
+#include "storage/ValueAccessorUtil.hpp"
 #include "storage/WindowAggregationOperationState.pb.h"
+#include "types/containers/ColumnVector.hpp"
+#include "types/containers/ColumnVectorsValueAccessor.hpp"
+#include "types/containers/ColumnVectorUtil.hpp"
 
 #include "glog/logging.h"
 
@@ -46,23 +53,21 @@ namespace quickstep {
 
 WindowAggregationOperationState::WindowAggregationOperationState(
     const CatalogRelationSchema &input_relation,
-    const AggregateFunction *window_aggregate_function,
+    const WindowAggregateFunction *window_aggregate_function,
     std::vector<std::unique_ptr<const Scalar>> &&arguments,
-    std::vector<std::unique_ptr<const Scalar>> &&partition_by_attributes,
+    const std::vector<std::unique_ptr<const Scalar>> &partition_by_attributes,
     const bool is_row,
     const std::int64_t num_preceding,
     const std::int64_t num_following,
     StorageManager *storage_manager)
     : input_relation_(input_relation),
       arguments_(std::move(arguments)),
-      partition_by_attributes_(std::move(partition_by_attributes)),
       is_row_(is_row),
       num_preceding_(num_preceding),
       num_following_(num_following),
       storage_manager_(storage_manager) {
   // Get the Types of this window aggregate's arguments so that we can create an
   // AggregationHandle.
-  // TODO(Shixuan): Next step: New handles for window aggregation function.
   std::vector<const Type*> argument_types;
   for (const std::unique_ptr<const Scalar> &argument : arguments_) {
     argument_types.emplace_back(&argument->getType());
@@ -71,28 +76,18 @@ WindowAggregationOperationState::WindowAggregationOperationState(
   // Check if window aggregate function could apply to the arguments.
   DCHECK(window_aggregate_function->canApplyToTypes(argument_types));
 
+  // IDs and types of partition keys.
+  std::vector<const Type*> partition_by_types;
+  for (const std::unique_ptr<const Scalar> &partition_by_attribute : partition_by_attributes) {
+    partition_by_ids_.push_back(
+        partition_by_attribute->getAttributeIdForValueAccessor());
+    partition_by_types.push_back(&partition_by_attribute->getType());
+  }
+
   // Create the handle and initial state.
   window_aggregation_handle_.reset(
-      window_aggregate_function->createHandle(argument_types));
-  window_aggregation_state_.reset(
-      window_aggregation_handle_->createInitialState());
-
-#ifdef QUICKSTEP_ENABLE_VECTOR_COPY_ELISION_SELECTION
-  // See if all of this window aggregate's arguments are attributes in the input
-  // relation. If so, remember the attribute IDs so that we can do copy elision
-  // when actually performing the window aggregation.
-  arguments_as_attributes_.reserve(arguments_.size());
-  for (const std::unique_ptr<const Scalar> &argument : arguments_) {
-    const attribute_id argument_id = argument->getAttributeIdForValueAccessor();
-    if (argument_id == -1) {
-      arguments_as_attributes_.clear();
-      break;
-    } else {
-      DCHECK_EQ(input_relation_.getID(), argument->getRelationIdForValueAccessor());
-      arguments_as_attributes_.push_back(argument_id);
-    }
-  }
-#endif
+      window_aggregate_function->createHandle(std::move(argument_types),
+                                              std::move(partition_by_types)));
 }
 
 WindowAggregationOperationState* WindowAggregationOperationState::ReconstructFromProto(
@@ -101,10 +96,6 @@ WindowAggregationOperationState* WindowAggregationOperationState::ReconstructFro
     StorageManager *storage_manager) {
   DCHECK(ProtoIsValid(proto, database));
 
-  // Rebuild contructor arguments from their representation in 'proto'.
-  const AggregateFunction *aggregate_function
-      = &AggregateFunctionFactory::ReconstructFromProto(proto.function());
-
   std::vector<std::unique_ptr<const Scalar>> arguments;
   arguments.reserve(proto.arguments_size());
   for (int argument_idx = 0; argument_idx < proto.arguments_size(); ++argument_idx) {
@@ -126,10 +117,10 @@ WindowAggregationOperationState* WindowAggregationOperationState::ReconstructFro
   const std::int64_t num_preceding = proto.num_preceding();
   const std::int64_t num_following = proto.num_following();
 
-  return new WindowAggregationOperationState(database.getRelationSchemaById(proto.relation_id()),
-                                             aggregate_function,
+  return new WindowAggregationOperationState(database.getRelationSchemaById(proto.input_relation_id()),
+                                             &WindowAggregateFunctionFactory::ReconstructFromProto(proto.function()),
                                              std::move(arguments),
-                                             std::move(partition_by_attributes),
+                                             partition_by_attributes,
                                              is_row,
                                              num_preceding,
                                              num_following,
@@ -139,11 +130,11 @@ WindowAggregationOperationState* WindowAggregationOperationState::ReconstructFro
 bool WindowAggregationOperationState::ProtoIsValid(const serialization::WindowAggregationOperationState &proto,
                                                    const CatalogDatabaseLite &database) {
   if (!proto.IsInitialized() ||
-      !database.hasRelationWithId(proto.relation_id())) {
+      !database.hasRelationWithId(proto.input_relation_id())) {
     return false;
   }
 
-  if (!AggregateFunctionFactory::ProtoIsValid(proto.function())) {
+  if (!WindowAggregateFunctionFactory::ProtoIsValid(proto.function())) {
     return false;
   }
 
@@ -176,4 +167,122 @@ bool WindowAggregationOperationState::ProtoIsValid(const serialization::WindowAg
   return true;
 }
 
+void WindowAggregationOperationState::windowAggregateBlocks(
+    InsertDestination *output_destination,
+    const std::vector<block_id> &block_ids) {
+  // TODO(Shixuan): This is a quick fix for currently unsupported functions in
+  // order to pass the query_optimizer test.
+  if (window_aggregation_handle_.get() == nullptr) {
+    std::cout << "The function will be supported in the near future :)\n";
+    return;
+  }
+
+  // TODO(Shixuan): RANGE frame mode should be supported to make SQL grammar
+  // work. This will need Order Key to be passed so that we know where the
+  // window should start and end.
+  if (!is_row_) {
+    std::cout << "Currently we don't support RANGE frame mode :(\n";
+    return;
+  }
+
+  // Get the total number of tuples.
+  int num_tuples = 0;
+  for (const block_id block_idx : block_ids) {
+    num_tuples +=
+        storage_manager_->getBlock(block_idx, input_relation_)->getNumTuples();
+  }
+
+  // Construct column vectors for attributes.
+  std::vector<ColumnVector*> attribute_vecs;
+  for (std::size_t attr_id = 0; attr_id < input_relation_.size(); ++attr_id) {
+    const CatalogAttribute *attr = input_relation_.getAttributeById(attr_id);
+    const Type &type = attr->getType();
+
+    if (NativeColumnVector::UsableForType(type)) {
+      attribute_vecs.push_back(new NativeColumnVector(type, num_tuples));
+    } else {
+      attribute_vecs.push_back(new IndirectColumnVector(type, num_tuples));
+    }
+  }
+
+  // Construct column vectors for arguments.
+  std::vector<ColumnVector*> argument_vecs;
+  for (const std::unique_ptr<const Scalar> &argument : arguments_) {
+    const Type &type = argument->getType();
+
+    if (NativeColumnVector::UsableForType(type)) {
+      argument_vecs.push_back(new NativeColumnVector(type, num_tuples));
+    } else {
+      argument_vecs.push_back(new IndirectColumnVector(type, num_tuples));
+    }
+  }
+
+  // TODO(Shixuan): Add Support for Vector Copy Elision Selection.
+  // Add tuples and arguments into ColumnVectors.
+  for (const block_id block_idx : block_ids) {
+    BlockReference block = storage_manager_->getBlock(block_idx, input_relation_);
+    const TupleStorageSubBlock &tuple_block = block->getTupleStorageSubBlock();
+    SubBlocksReference sub_block_ref(tuple_block,
+                                     block->getIndices(),
+                                     block->getIndicesConsistent());
+    ValueAccessor *tuple_accessor = tuple_block.createValueAccessor();
+    ColumnVectorsValueAccessor *argument_accessor = new ColumnVectorsValueAccessor();
+    for (const std::unique_ptr<const Scalar> &argument : arguments_) {
+      argument_accessor->addColumn(argument->getAllValues(tuple_accessor,
+                                                          &sub_block_ref));
+    }
+
+    InvokeOnAnyValueAccessor(tuple_accessor,
+                             [&] (auto *tuple_accessor) -> void {  // NOLINT(build/c++11)
+      tuple_accessor->beginIteration();
+      argument_accessor->beginIteration();
+
+      while (tuple_accessor->next() && argument_accessor->next()) {
+        for (std::size_t attr_id = 0; attr_id < attribute_vecs.size(); ++attr_id) {
+          ColumnVector *attr_vec = attribute_vecs[attr_id];
+          if (attr_vec->isNative()) {
+            static_cast<NativeColumnVector*>(attr_vec)->appendTypedValue(
+                tuple_accessor->getTypedValue(attr_id));
+          } else {
+            static_cast<IndirectColumnVector*>(attr_vec)->appendTypedValue(
+                tuple_accessor->getTypedValue(attr_id));
+          }
+        }
+
+        for (std::size_t argument_idx = 0;
+             argument_idx < argument_vecs.size();
+             ++argument_idx) {
+          ColumnVector *argument = argument_vecs[argument_idx];
+          if (argument->isNative()) {
+            static_cast<NativeColumnVector*>(argument)->appendTypedValue(
+                argument_accessor->getTypedValue(argument_idx));
+          } else {
+            static_cast<IndirectColumnVector*>(argument)->appendTypedValue(
+                argument_accessor->getTypedValue(argument_idx));
+          }
+        }
+      }
+    });
+  }
+
+  // Construct the value accessor for tuples in all blocks
+  ColumnVectorsValueAccessor *all_blocks_accessor
+      = new ColumnVectorsValueAccessor();
+  for (ColumnVector *attr_vec : attribute_vecs) {
+    all_blocks_accessor->addColumn(attr_vec);
+  }
+
+  // Do actual calculation in handle.
+  ColumnVector *window_aggregates =
+      window_aggregation_handle_->calculate(all_blocks_accessor,
+                                            std::move(argument_vecs),
+                                            partition_by_ids_,
+                                            is_row_,
+                                            num_preceding_,
+                                            num_following_);
+
+  all_blocks_accessor->addColumn(window_aggregates);
+  output_destination->bulkInsertTuples(all_blocks_accessor);
+}
+
 }  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/storage/WindowAggregationOperationState.hpp
----------------------------------------------------------------------
diff --git a/storage/WindowAggregationOperationState.hpp b/storage/WindowAggregationOperationState.hpp
index d7b3e6a..9792a99 100644
--- a/storage/WindowAggregationOperationState.hpp
+++ b/storage/WindowAggregationOperationState.hpp
@@ -25,20 +25,20 @@
 #include <vector>
 
 #include "catalog/CatalogTypedefs.hpp"
-#include "expressions/aggregation/AggregationHandle.hpp"
 #include "expressions/scalar/Scalar.hpp"
 #include "expressions/scalar/ScalarAttribute.hpp"
+#include "expressions/window_aggregation/WindowAggregationHandle.hpp"
 #include "storage/StorageBlockInfo.hpp"
 #include "storage/WindowAggregationOperationState.pb.h"
 #include "utility/Macros.hpp"
 
 namespace quickstep {
 
-class AggregateFunction;
 class CatalogDatabaseLite;
 class CatalogRelationSchema;
 class InsertDestination;
 class StorageManager;
+class WindowAggregateFunction;
 
 /** \addtogroup Storage
  *  @{
@@ -63,13 +63,12 @@ class WindowAggregationOperationState {
    *                      current row. -1 means UNBOUNDED PRECEDING.
    * @param num_following The number of rows/range for the tuples following the
    *                      current row. -1 means UNBOUNDED FOLLOWING.
-   * @param storage_manager The StorageManager to use for allocating hash
-   *        tables.
+   * @param storage_manager The StorageManager to get block references.
    */
   WindowAggregationOperationState(const CatalogRelationSchema &input_relation,
-                                  const AggregateFunction *window_aggregate_function,
+                                  const WindowAggregateFunction *window_aggregate_function,
                                   std::vector<std::unique_ptr<const Scalar>> &&arguments,
-                                  std::vector<std::unique_ptr<const Scalar>> &&partition_by_attributes,
+                                  const std::vector<std::unique_ptr<const Scalar>> &partition_by_attributes,
                                   const bool is_row,
                                   const std::int64_t num_preceding,
                                   const std::int64_t num_following,
@@ -107,66 +106,29 @@ class WindowAggregationOperationState {
                            const CatalogDatabaseLite &database);
 
   /**
-   * @brief Get the is_row info.
-   * @note This is a quickfix for "unused variable". After the window aggregate
-   *       functions are built, these methods might be dropped.
-   * 
-   * @return True if the frame mode is ROW, false if it is RANGE.
-   **/
-  const bool is_row() const { return is_row_; }
-
-  /**
-   * @brief Get the num_preceding info.
-   * @note This is a quickfix for "unused variable". After the window aggregate
-   *       functions are built, these methods might be dropped.
-   *
-   * @return The number of rows/range that precedes the current row.
-   **/
-  const std::int64_t num_preceding() const { return num_preceding_; }
-
-  /**
-   * @brief Get the num_following info.
-   * @note This is a quickfix for "unused variable". After the window aggregate
-   *       functions are built, these methods might be dropped.
+   * @brief Compute window aggregates on the tuples of the given relation.
    *
-   * @return The number of rows/range that follows the current row.
+   * @param output_destination The output destination for the computed window
+   *                           aggregate.
+   * @param block_ids The id of the blocks to be computed.
    **/
-  const std::int64_t num_following() const { return num_following_; }
-
-  /**
-   * @brief Get the pointer to StorageManager.
-   * @note This is a quickfix for "unused variable". After the window aggregate
-   *       functions are built, these methods might be dropped.
-   *
-   * @return A pointer to the storage manager.
-   **/
-  StorageManager *storage_manager() { return storage_manager_; }
+  void windowAggregateBlocks(InsertDestination *output_destination,
+                             const std::vector<block_id> &block_ids);
 
  private:
   const CatalogRelationSchema &input_relation_;
-
-  // TODO(Shixuan): Handle and State for window aggregation will be needed for
-  //                actual calculation.
-  std::unique_ptr<AggregationHandle> window_aggregation_handle_;
-  std::unique_ptr<AggregationState> window_aggregation_state_;
+  const std::vector<block_id> block_ids_;
+  std::unique_ptr<WindowAggregationHandle> window_aggregation_handle_;
   std::vector<std::unique_ptr<const Scalar>> arguments_;
+  std::vector<attribute_id> partition_by_ids_;
 
-  // We don't add order_by_attributes here since it is not needed after sorting.
-  std::vector<std::unique_ptr<const Scalar>> partition_by_attributes_;
-
-  // Window framing information.
+  // Frame info.
   const bool is_row_;
   const std::int64_t num_preceding_;
   const std::int64_t num_following_;
 
   StorageManager *storage_manager_;
 
-#ifdef QUICKSTEP_ENABLE_VECTOR_COPY_ELISION_SELECTION
-  // If all an aggregate's argument expressions are simply attributes in
-  // 'input_relation_', then this caches the attribute IDs of those arguments.
-  std::vector<attribute_id> arguments_as_attributes_;
-#endif
-
   DISALLOW_COPY_AND_ASSIGN(WindowAggregationOperationState);
 };
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/storage/WindowAggregationOperationState.proto
----------------------------------------------------------------------
diff --git a/storage/WindowAggregationOperationState.proto b/storage/WindowAggregationOperationState.proto
index c7bd0ef..d888461 100644
--- a/storage/WindowAggregationOperationState.proto
+++ b/storage/WindowAggregationOperationState.proto
@@ -19,12 +19,12 @@ syntax = "proto2";
 
 package quickstep.serialization;
 
-import "expressions/aggregation/AggregateFunction.proto";
+import "expressions/window_aggregation/WindowAggregateFunction.proto";
 import "expressions/Expressions.proto";
 
 message WindowAggregationOperationState {
-  required int32 relation_id = 1;
-  required AggregateFunction function = 2;
+  required int32 input_relation_id = 1;
+  required WindowAggregateFunction function = 2;
   repeated Scalar arguments = 3;
   repeated Scalar partition_by_attributes = 4;
   required bool is_row = 5;

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/c0bb4620/storage/tests/WindowAggregationOperationState_unittest.cpp
----------------------------------------------------------------------
diff --git a/storage/tests/WindowAggregationOperationState_unittest.cpp b/storage/tests/WindowAggregationOperationState_unittest.cpp
index c572034..d58f0f5 100644
--- a/storage/tests/WindowAggregationOperationState_unittest.cpp
+++ b/storage/tests/WindowAggregationOperationState_unittest.cpp
@@ -23,7 +23,7 @@
 #include "catalog/CatalogDatabase.hpp"
 #include "catalog/CatalogRelation.hpp"
 #include "catalog/CatalogTypedefs.hpp"
-#include "expressions/aggregation/AggregateFunction.pb.h"
+#include "expressions/window_aggregation/WindowAggregateFunction.pb.h"
 #include "storage/WindowAggregationOperationState.hpp"
 #include "storage/WindowAggregationOperationState.pb.h"
 
@@ -57,8 +57,8 @@ TEST_F(WindowAggregationOperationStateProtoTest, UninitializationTest) {
 
 TEST_F(WindowAggregationOperationStateProtoTest, InvalidRelationIdTest) {
   serialization::WindowAggregationOperationState proto;
-  proto.set_relation_id(kInvalidTableId);
-  proto.mutable_function()->set_aggregation_id(serialization::AggregateFunction::AVG);
+  proto.set_input_relation_id(kInvalidTableId);
+  proto.mutable_function()->set_window_aggregation_id(serialization::WindowAggregateFunction::AVG);
   proto.set_is_row(true);
   proto.set_num_preceding(kValidNum);
   proto.set_num_following(kValidNum);
@@ -67,8 +67,8 @@ TEST_F(WindowAggregationOperationStateProtoTest, InvalidRelationIdTest) {
 
 TEST_F(WindowAggregationOperationStateProtoTest, InvalidNumTest) {
   serialization::WindowAggregationOperationState proto;
-  proto.set_relation_id(rel_id_);
-  proto.mutable_function()->set_aggregation_id(serialization::AggregateFunction::AVG);
+  proto.set_input_relation_id(rel_id_);
+  proto.mutable_function()->set_window_aggregation_id(serialization::WindowAggregateFunction::AVG);
   proto.set_is_row(true);
   proto.set_num_preceding(kInvalidNum);
   proto.set_num_following(kValidNum);
@@ -81,8 +81,8 @@ TEST_F(WindowAggregationOperationStateProtoTest, InvalidNumTest) {
 
 TEST_F(WindowAggregationOperationStateProtoTest, ValidTest) {
   serialization::WindowAggregationOperationState proto;
-  proto.set_relation_id(rel_id_);
-  proto.mutable_function()->set_aggregation_id(serialization::AggregateFunction::AVG);
+  proto.set_input_relation_id(rel_id_);
+  proto.mutable_function()->set_window_aggregation_id(serialization::WindowAggregateFunction::AVG);
   proto.set_is_row(true);
   proto.set_num_preceding(kValidNum);
   proto.set_num_following(kValidNum);


[17/30] incubator-quickstep git commit: Introduced Shiftboss for the distributed version.

Posted by ra...@apache.org.
Introduced Shiftboss for the distributed version.


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

Branch: refs/heads/quickstep-28-29
Commit: 7415ee87fa088054e09b2a9efb88389c43351b12
Parents: 9f9e3b7
Author: Zuyu Zhang <zu...@twitter.com>
Authored: Fri Jul 22 13:29:03 2016 -0700
Committer: Zuyu Zhang <zu...@twitter.com>
Committed: Wed Jul 27 15:06:13 2016 -0700

----------------------------------------------------------------------
 catalog/CatalogDatabaseCache.hpp             |   5 +
 query_execution/CMakeLists.txt               |  24 ++
 query_execution/QueryExecutionMessages.proto |  30 ++
 query_execution/QueryExecutionTypedefs.hpp   |   8 +
 query_execution/Shiftboss.cpp                | 360 ++++++++++++++++++++++
 query_execution/Shiftboss.hpp                | 241 +++++++++++++++
 storage/StorageManager.hpp                   |   1 +
 7 files changed, 669 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7415ee87/catalog/CatalogDatabaseCache.hpp
----------------------------------------------------------------------
diff --git a/catalog/CatalogDatabaseCache.hpp b/catalog/CatalogDatabaseCache.hpp
index 77afe2a..b3e73a6 100644
--- a/catalog/CatalogDatabaseCache.hpp
+++ b/catalog/CatalogDatabaseCache.hpp
@@ -54,6 +54,11 @@ namespace serialization { class CatalogDatabase; }
 class CatalogDatabaseCache : public CatalogDatabaseLite {
  public:
   /**
+   * @brief Constructor.
+   **/
+  CatalogDatabaseCache() {}
+
+  /**
    * @brief Constructor. Reconstruct a database cache from its serialized
    *        Protocol Buffer form.
    *

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7415ee87/query_execution/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/query_execution/CMakeLists.txt b/query_execution/CMakeLists.txt
index f582ba5..8bf1ab1 100644
--- a/query_execution/CMakeLists.txt
+++ b/query_execution/CMakeLists.txt
@@ -52,6 +52,7 @@ if (ENABLE_DISTRIBUTED)
 endif()
 add_library(quickstep_queryexecution_QueryManagerSingleNode QueryManagerSingleNode.cpp QueryManagerSingleNode.hpp)
 if (ENABLE_DISTRIBUTED)
+  add_library(quickstep_queryexecution_Shiftboss Shiftboss.cpp Shiftboss.hpp)
   add_library(quickstep_queryexecution_ShiftbossDirectory ../empty_src.cpp ShiftbossDirectory.hpp)
 endif()
 add_library(quickstep_queryexecution_WorkOrderProtosContainer ../empty_src.cpp WorkOrderProtosContainer.hpp)
@@ -157,6 +158,8 @@ target_link_libraries(quickstep_queryexecution_QueryContext_proto
                       quickstep_utility_SortConfiguration_proto
                       ${PROTOBUF_LIBRARY})
 target_link_libraries(quickstep_queryexecution_QueryExecutionMessages_proto
+                      quickstep_catalog_Catalog_proto
+                      quickstep_queryexecution_QueryContext_proto
                       quickstep_relationaloperators_WorkOrder_proto
                       ${PROTOBUF_LIBRARY})
 target_link_libraries(quickstep_queryexecution_QueryExecutionState
@@ -214,6 +217,26 @@ target_link_libraries(quickstep_queryexecution_QueryManagerSingleNode
                       quickstep_utility_Macros
                       tmb)
 if (ENABLE_DISTRIBUTED)
+  target_link_libraries(quickstep_queryexecution_Shiftboss
+                        glog
+                        quickstep_catalog_CatalogDatabaseCache
+                        quickstep_catalog_CatalogTypedefs
+                        quickstep_queryexecution_QueryContext
+                        quickstep_queryexecution_QueryExecutionMessages_proto
+                        quickstep_queryexecution_QueryExecutionTypedefs
+                        quickstep_queryexecution_QueryExecutionUtil
+                        quickstep_queryexecution_WorkerDirectory
+                        quickstep_queryexecution_WorkerMessage
+                        quickstep_relationaloperators_RebuildWorkOrder
+                        quickstep_relationaloperators_WorkOrderFactory
+                        quickstep_storage_InsertDestination
+                        quickstep_storage_StorageBlock
+                        quickstep_storage_StorageBlockInfo
+                        quickstep_storage_StorageManager
+                        quickstep_threading_Thread
+                        quickstep_threading_ThreadUtil
+                        quickstep_utility_Macros
+                        tmb)
   target_link_libraries(quickstep_queryexecution_ShiftbossDirectory
                         quickstep_utility_Macros
                         tmb)
@@ -272,6 +295,7 @@ if (ENABLE_DISTRIBUTED)
   target_link_libraries(quickstep_queryexecution
                         quickstep_queryexecution_BlockLocator
                         quickstep_queryexecution_QueryManagerDistributed
+                        quickstep_queryexecution_Shiftboss
                         quickstep_queryexecution_ShiftbossDirectory)
 endif()
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7415ee87/query_execution/QueryExecutionMessages.proto
----------------------------------------------------------------------
diff --git a/query_execution/QueryExecutionMessages.proto b/query_execution/QueryExecutionMessages.proto
index fa20993..591ca6c 100644
--- a/query_execution/QueryExecutionMessages.proto
+++ b/query_execution/QueryExecutionMessages.proto
@@ -16,6 +16,8 @@ syntax = "proto2";
 
 package quickstep.serialization;
 
+import "catalog/Catalog.proto";
+import "query_execution/QueryContext.proto";
 import "relational_operators/WorkOrder.proto";
 
 // Used for any messages that do not carry payloads.
@@ -73,6 +75,25 @@ message WorkOrdersAvailableMessage {
 }
 
 // Distributed version related messages.
+message ShiftbossRegistrationMessage {
+  // The total Work Order processing capacity in Shiftboss, which equals to the
+  // sum of the capacity of each worker managed by Shiftboss.
+  required uint64 work_order_capacity = 1;
+}
+
+message ShiftbossRegistrationResponseMessage {
+}
+
+message QueryInitiateMessage {
+  required uint64 query_id = 1;
+  required CatalogDatabase catalog_database_cache = 2;
+  required QueryContext query_context = 3;
+}
+
+message QueryInitiateResponseMessage {
+  required uint64 query_id = 1;
+}
+
 message WorkOrderMessage {
   required uint64 query_id = 1;
   required uint64 operator_index = 2;
@@ -92,6 +113,15 @@ message InitiateRebuildResponseMessage {
   required uint64 num_rebuild_work_orders = 3;
 }
 
+message QueryResultRelationMessage {
+  required int32 relation_id = 1;
+  repeated fixed64 blocks = 2 [packed=true];
+}
+
+message QueryResultRelationResponseMessage {
+  required int32 relation_id = 1;
+}
+
 // BlockLocator related messages.
 message BlockDomainRegistrationMessage {
   // Format IP:Port, i.e., "0.0.0.0:0".

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7415ee87/query_execution/QueryExecutionTypedefs.hpp
----------------------------------------------------------------------
diff --git a/query_execution/QueryExecutionTypedefs.hpp b/query_execution/QueryExecutionTypedefs.hpp
index 61e76d7..d73d4ee 100644
--- a/query_execution/QueryExecutionTypedefs.hpp
+++ b/query_execution/QueryExecutionTypedefs.hpp
@@ -73,9 +73,17 @@ enum QueryExecutionMessageType : message_type_id {
   kPoisonMessage,  // From the main thread to Foreman and Workers.
 
 #ifdef QUICKSTEP_DISTRIBUTED
+  kShiftbossRegistrationMessage,  // From Shiftboss to Foreman.
+  kShiftbossRegistrationResponseMessage,  // From Foreman to Shiftboss.
+  kQueryInitiateMessage,  // From Foreman to Shiftboss.
+  kQueryInitiateResponseMessage,  // From Shiftboss to Foreman.
+
   kInitiateRebuildMessage,  // From Foreman to Shiftboss.
   kInitiateRebuildResponseMessage,  // From Shiftboss to Foreman.
 
+  kQueryResultRelationMessage,  // From Foreman to Shiftboss.
+  kQueryResultRelationResponseMessage,  // From Shiftboss to Foreman.
+
   // BlockLocator related messages, sorted in a life cycle of StorageManager
   // with a unique block domain.
   kBlockDomainRegistrationMessage,  // From Worker to BlockLocator.

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7415ee87/query_execution/Shiftboss.cpp
----------------------------------------------------------------------
diff --git a/query_execution/Shiftboss.cpp b/query_execution/Shiftboss.cpp
new file mode 100644
index 0000000..af56306
--- /dev/null
+++ b/query_execution/Shiftboss.cpp
@@ -0,0 +1,360 @@
+/**
+ *   Licensed 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.
+ **/
+
+#include "query_execution/Shiftboss.hpp"
+
+#include <cstddef>
+#include <cstdlib>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "catalog/CatalogTypedefs.hpp"
+#include "query_execution/QueryContext.hpp"
+#include "query_execution/QueryExecutionMessages.pb.h"
+#include "query_execution/QueryExecutionTypedefs.hpp"
+#include "query_execution/QueryExecutionUtil.hpp"
+#include "query_execution/WorkerMessage.hpp"
+#include "relational_operators/RebuildWorkOrder.hpp"
+#include "relational_operators/WorkOrderFactory.hpp"
+#include "storage/InsertDestination.hpp"
+#include "storage/StorageBlock.hpp"
+#include "storage/StorageBlockInfo.hpp"
+#include "storage/StorageManager.hpp"
+#include "threading/ThreadUtil.hpp"
+
+#include "glog/logging.h"
+
+#include "tmb/address.h"
+#include "tmb/id_typedefs.h"
+#include "tmb/message_bus.h"
+#include "tmb/message_style.h"
+#include "tmb/tagged_message.h"
+
+using std::free;
+using std::malloc;
+using std::move;
+using std::size_t;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+using tmb::TaggedMessage;
+
+namespace quickstep {
+
+class WorkOrder;
+
+void Shiftboss::run() {
+  if (cpu_id_ >= 0) {
+    // We can pin the shiftboss thread to a CPU if specified.
+    ThreadUtil::BindToCPU(cpu_id_);
+  }
+
+  for (;;) {
+    // Receive() is a blocking call, causing this thread to sleep until next
+    // message is received.
+    AnnotatedMessage annotated_message(bus_->Receive(shiftboss_client_id_, 0, true));
+    LOG(INFO) << "Shiftboss (id '" << shiftboss_client_id_
+              << "') received the typed '" << annotated_message.tagged_message.message_type()
+              << "' message from client " << annotated_message.sender;
+    switch (annotated_message.tagged_message.message_type()) {
+      case kShiftbossRegistrationResponseMessage: {
+        foreman_client_id_ = annotated_message.sender;
+        break;
+      }
+      case kQueryInitiateMessage: {
+        const TaggedMessage &tagged_message = annotated_message.tagged_message;
+
+        serialization::QueryInitiateMessage proto;
+        CHECK(proto.ParseFromArray(tagged_message.message(), tagged_message.message_bytes()));
+
+        processQueryInitiateMessage(proto.query_id(), proto.catalog_database_cache(), proto.query_context());
+        break;
+      }
+      case kWorkOrderMessage: {
+        const TaggedMessage &tagged_message = annotated_message.tagged_message;
+
+        serialization::WorkOrderMessage proto;
+        CHECK(proto.ParseFromArray(tagged_message.message(), tagged_message.message_bytes()));
+
+        const std::size_t query_id = proto.query_id();
+        DCHECK_EQ(1u, query_contexts_.count(query_id));
+
+        WorkOrder *work_order = WorkOrderFactory::ReconstructFromProto(proto.work_order(),
+                                                                       &database_cache_,
+                                                                       query_contexts_[query_id].get(),
+                                                                       storage_manager_,
+                                                                       shiftboss_client_id_,
+                                                                       bus_);
+
+        unique_ptr<WorkerMessage> worker_message(
+            WorkerMessage::WorkOrderMessage(work_order, proto.operator_index()));
+
+        TaggedMessage worker_tagged_message(worker_message.get(),
+                                            sizeof(*worker_message),
+                                            kWorkOrderMessage);
+
+        const size_t worker_index = getSchedulableWorker();
+        LOG(INFO) << "Shiftboss (id '" << shiftboss_client_id_
+                  << "') forwarded WorkOrderMessage (typed '" << kWorkOrderMessage
+                  << "') from Foreman to worker " << worker_index;
+
+        QueryExecutionUtil::SendTMBMessage(bus_,
+                                           shiftboss_client_id_,
+                                           workers_->getClientID(worker_index),
+                                           move(worker_tagged_message));
+        break;
+      }
+      case kInitiateRebuildMessage: {
+        // Construct rebuild work orders, and send back their number to
+        // 'ForemanDistributed'.
+        const TaggedMessage &tagged_message = annotated_message.tagged_message;
+
+        serialization::InitiateRebuildMessage proto;
+        CHECK(proto.ParseFromArray(tagged_message.message(), tagged_message.message_bytes()));
+
+        processInitiateRebuildMessage(proto.query_id(),
+                                      proto.operator_index(),
+                                      proto.insert_destination_index(),
+                                      proto.relation_id());
+        break;
+      }
+      case kWorkOrderCompleteMessage:  // Fall through.
+      case kRebuildWorkOrderCompleteMessage:
+      case kDataPipelineMessage:
+      case kWorkOrdersAvailableMessage:
+      case kWorkOrderFeedbackMessage: {
+        LOG(INFO) << "Shiftboss (id '" << shiftboss_client_id_
+                  << "') forwarded typed '" << annotated_message.tagged_message.message_type()
+                  << "' message from worker (client " << annotated_message.sender << ") to Foreman";
+
+        DCHECK_NE(foreman_client_id_, tmb::kClientIdNone);
+        QueryExecutionUtil::SendTMBMessage(bus_,
+                                           shiftboss_client_id_,
+                                           foreman_client_id_,
+                                           move(annotated_message.tagged_message));
+        break;
+      }
+      case kQueryResultRelationMessage: {
+        // TODO(zuyu): Rename to kSaveQueryResultMessage.
+        const TaggedMessage &tagged_message = annotated_message.tagged_message;
+
+        serialization::QueryResultRelationMessage proto;
+        CHECK(proto.ParseFromArray(tagged_message.message(), tagged_message.message_bytes()));
+
+        for (int i = 0; i < proto.blocks_size(); ++i) {
+          const block_id block = proto.blocks(i);
+          storage_manager_->saveBlockOrBlob(block);
+          if (storage_manager_->blockOrBlobIsLoaded(block)) {
+            // NOTE(zuyu): eviction is required to avoid accesses to the query
+            // result relation schema in CatalogDatabaseCache, for all query
+            // optimizer execution generator unit tests and the single-process
+            // Quickstep CLI.
+            storage_manager_->evictBlockOrBlob(block);
+          }
+        }
+
+        serialization::QueryResultRelationResponseMessage ack_proto;
+        ack_proto.set_relation_id(proto.relation_id());
+
+        const size_t ack_proto_length = ack_proto.ByteSize();
+        char *ack_proto_bytes = static_cast<char*>(malloc(ack_proto_length));
+        CHECK(ack_proto.SerializeToArray(ack_proto_bytes, ack_proto_length));
+
+        TaggedMessage ack_message(static_cast<const void*>(ack_proto_bytes),
+                                  ack_proto_length,
+                                  kQueryResultRelationResponseMessage);
+        free(ack_proto_bytes);
+
+        LOG(INFO) << "Shiftboss (id '" << shiftboss_client_id_
+                  << "') sent QueryResultRelationResponseMessage (typed '" << kQueryResultRelationResponseMessage
+                  << ") to Foreman";
+        QueryExecutionUtil::SendTMBMessage(bus_,
+                                           shiftboss_client_id_,
+                                           foreman_client_id_,
+                                           move(ack_message));
+        break;
+      }
+      case kPoisonMessage: {
+        LOG(INFO) << "Shiftboss (id '" << shiftboss_client_id_
+                  << "') forwarded PoisonMessage (typed '" << kPoisonMessage
+                  << "') from Foreman to all workers";
+
+        tmb::MessageStyle broadcast_style;
+        broadcast_style.Broadcast(true);
+
+        tmb::MessageBus::SendStatus send_status =
+            bus_->Send(shiftboss_client_id_,
+                       worker_addresses_,
+                       broadcast_style,
+                       move(annotated_message.tagged_message));
+        DCHECK(send_status == tmb::MessageBus::SendStatus::kOK);
+        return;
+      }
+      default: {
+        LOG(FATAL) << "Unknown TMB message type";
+      }
+    }
+  }
+}
+
+size_t Shiftboss::getSchedulableWorker() {
+  const size_t num_workers = workers_->getNumWorkers();
+
+  size_t curr_worker = start_worker_index_;
+  for (;;) {
+    if (workers_->getNumQueuedWorkOrders(curr_worker) < max_msgs_per_worker_) {
+      start_worker_index_ = (curr_worker + 1) % num_workers;
+      // TODO(zuyu): workers_->incrementNumQueuedWorkOrders(curr_worker);
+      // But we need a WorkOrder queue first.
+      return curr_worker;
+    }
+
+    curr_worker = (curr_worker + 1) % num_workers;
+  }
+}
+
+void Shiftboss::registerWithForeman() {
+  LOG(INFO) << "Shiftboss (id '" << shiftboss_client_id_
+            << "') sent ShiftbossRegistrationMessage (typed '" << kShiftbossRegistrationMessage
+            << "') to all";
+
+  tmb::Address all_addresses;
+  all_addresses.All(true);
+
+  tmb::MessageStyle style;
+
+  serialization::ShiftbossRegistrationMessage proto;
+  proto.set_work_order_capacity(getWorkOrderCapacity());
+
+  const size_t proto_length = proto.ByteSize();
+  char *proto_bytes = static_cast<char*>(malloc(proto_length));
+  CHECK(proto.SerializeToArray(proto_bytes, proto_length));
+
+  TaggedMessage message(static_cast<const void*>(proto_bytes),
+                        proto_length,
+                        kShiftbossRegistrationMessage);
+  free(proto_bytes);
+
+  tmb::MessageBus::SendStatus send_status =
+      bus_->Send(shiftboss_client_id_, all_addresses, style, move(message));
+  DCHECK(send_status == tmb::MessageBus::SendStatus::kOK);
+}
+
+void Shiftboss::processQueryInitiateMessage(
+    const std::size_t query_id,
+    const serialization::CatalogDatabase &catalog_database_cache_proto,
+    const serialization::QueryContext &query_context_proto) {
+  database_cache_.update(catalog_database_cache_proto);
+
+  unique_ptr<QueryContext> query_context(
+      new QueryContext(query_context_proto,
+                       database_cache_,
+                       storage_manager_,
+                       shiftboss_client_id_,
+                       bus_));
+  query_contexts_.emplace(query_id, move(query_context));
+
+  LOG(INFO) << "Shiftboss (id '" << shiftboss_client_id_
+            << "') sent QueryInitiateResponseMessage (typed '" << kQueryInitiateResponseMessage
+            << "') to Foreman";
+
+  serialization::QueryInitiateResponseMessage proto;
+  proto.set_query_id(query_id);
+
+  const size_t proto_length = proto.ByteSize();
+  char *proto_bytes = static_cast<char*>(malloc(proto_length));
+  CHECK(proto.SerializeToArray(proto_bytes, proto_length));
+
+  TaggedMessage ack_message(static_cast<const void*>(proto_bytes),
+                            proto_length,
+                            kQueryInitiateResponseMessage);
+  free(proto_bytes);
+
+  QueryExecutionUtil::SendTMBMessage(bus_,
+                                     shiftboss_client_id_,
+                                     foreman_client_id_,
+                                     move(ack_message));
+}
+
+void Shiftboss::processInitiateRebuildMessage(const std::size_t query_id,
+                                              const std::size_t op_index,
+                                              const QueryContext::insert_destination_id dest_index,
+                                              const relation_id rel_id) {
+  DCHECK_NE(foreman_client_id_, tmb::kClientIdNone);
+
+  DCHECK_EQ(1u, query_contexts_.count(query_id));
+  InsertDestination *insert_destination = query_contexts_[query_id]->getInsertDestination(dest_index);
+  DCHECK(insert_destination != nullptr);
+
+  vector<MutableBlockReference> partially_filled_block_refs;
+  insert_destination->getPartiallyFilledBlocks(&partially_filled_block_refs);
+
+  LOG(INFO) << "Shiftboss (id '" << shiftboss_client_id_
+            << "') sent InitiateRebuildResponseMessage (typed '" << kInitiateRebuildResponseMessage
+            << "') to Foreman";
+
+  serialization::InitiateRebuildResponseMessage proto;
+  proto.set_query_id(query_id);
+  proto.set_operator_index(op_index);
+  proto.set_num_rebuild_work_orders(partially_filled_block_refs.size());
+
+  const size_t proto_length = proto.ByteSize();
+  char *proto_bytes = static_cast<char*>(malloc(proto_length));
+  CHECK(proto.SerializeToArray(proto_bytes, proto_length));
+
+  TaggedMessage ack_message(static_cast<const void*>(proto_bytes),
+                            proto_length,
+                            kInitiateRebuildResponseMessage);
+  free(proto_bytes);
+
+  QueryExecutionUtil::SendTMBMessage(bus_,
+                                     shiftboss_client_id_,
+                                     foreman_client_id_,
+                                     move(ack_message));
+
+  for (size_t i = 0; i < partially_filled_block_refs.size(); ++i) {
+    // NOTE(zuyu): Worker releases the memory after the execution of
+    // RebuildWorkOrder on the Worker.
+    WorkOrder *rebuild_work_order =
+        new RebuildWorkOrder(query_id,
+                             move(partially_filled_block_refs[i]),
+                             op_index,
+                             rel_id,
+                             shiftboss_client_id_,
+                             bus_);
+
+    unique_ptr<WorkerMessage> worker_message(
+        WorkerMessage::RebuildWorkOrderMessage(rebuild_work_order, op_index));
+
+    TaggedMessage worker_tagged_message(worker_message.get(),
+                                        sizeof(*worker_message),
+                                        kRebuildWorkOrderMessage);
+
+    const size_t worker_index = getSchedulableWorker();
+    LOG(INFO) << "Shiftboss (id '" << shiftboss_client_id_
+              << "') sent RebuildWorkOrderMessage (typed '" << kRebuildWorkOrderMessage
+              << "') to worker " << worker_index;
+
+    QueryExecutionUtil::SendTMBMessage(bus_,
+                                       shiftboss_client_id_,
+                                       workers_->getClientID(worker_index),
+                                       move(worker_tagged_message));
+  }
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7415ee87/query_execution/Shiftboss.hpp
----------------------------------------------------------------------
diff --git a/query_execution/Shiftboss.hpp b/query_execution/Shiftboss.hpp
new file mode 100644
index 0000000..096ab74
--- /dev/null
+++ b/query_execution/Shiftboss.hpp
@@ -0,0 +1,241 @@
+/**
+ *   Licensed 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.
+ **/
+
+#ifndef QUICKSTEP_QUERY_EXECUTION_SHIFTBOSS_HPP_
+#define QUICKSTEP_QUERY_EXECUTION_SHIFTBOSS_HPP_
+
+#include <cstddef>
+#include <memory>
+#include <unordered_map>
+
+#include "catalog/CatalogDatabaseCache.hpp"
+#include "catalog/CatalogTypedefs.hpp"
+#include "query_execution/QueryContext.hpp"
+#include "query_execution/QueryExecutionTypedefs.hpp"
+#include "query_execution/WorkerDirectory.hpp"
+#include "threading/Thread.hpp"
+#include "utility/Macros.hpp"
+
+#include "glog/logging.h"
+
+#include "tmb/address.h"
+#include "tmb/id_typedefs.h"
+#include "tmb/message_bus.h"
+
+namespace quickstep {
+
+class StorageManager;
+
+namespace serialization {
+class CatalogDatabase;
+class QueryContext;
+}  // namespace serialization
+
+/** \addtogroup QueryExecution
+ *  @{
+ */
+
+/**
+ * @brief The Shiftboss accepts workorder protos from shiftboss, and assigns
+ *        the workorders to workers.
+ **/
+class Shiftboss : public Thread {
+ public:
+  /**
+   * @brief Constructor.
+   *
+   * @param bus A pointer to the TMB.
+   * @param storage_manager The StorageManager to use.
+   * @param workers A pointer to the WorkerDirectory.
+   * @param cpu_id The ID of the CPU to which the Shiftboss thread can be pinned.
+   *
+   * @note If cpu_id is not specified, Shiftboss thread can be possibly moved
+   *       around on different CPUs by the OS.
+  **/
+  Shiftboss(tmb::MessageBus *bus,
+            StorageManager *storage_manager,
+            WorkerDirectory *workers,
+            const int cpu_id = -1)
+      : bus_(DCHECK_NOTNULL(bus)),
+        storage_manager_(DCHECK_NOTNULL(storage_manager)),
+        workers_(DCHECK_NOTNULL(workers)),
+        cpu_id_(cpu_id),
+        shiftboss_client_id_(tmb::kClientIdNone),
+        foreman_client_id_(tmb::kClientIdNone),
+        max_msgs_per_worker_(1),
+        start_worker_index_(0u) {
+    // Check to have at least one Worker.
+    DCHECK_GT(workers->getNumWorkers(), 0u);
+
+    shiftboss_client_id_ = bus_->Connect();
+    LOG(INFO) << "Shiftboss TMB client ID: " << shiftboss_client_id_;
+    DCHECK_NE(shiftboss_client_id_, tmb::kClientIdNone);
+
+    // Messages between Foreman and Shiftboss.
+    bus_->RegisterClientAsSender(shiftboss_client_id_, kShiftbossRegistrationMessage);
+    bus_->RegisterClientAsReceiver(shiftboss_client_id_, kShiftbossRegistrationResponseMessage);
+
+    bus_->RegisterClientAsReceiver(shiftboss_client_id_, kQueryInitiateMessage);
+    bus_->RegisterClientAsSender(shiftboss_client_id_, kQueryInitiateResponseMessage);
+
+    bus_->RegisterClientAsReceiver(shiftboss_client_id_, kInitiateRebuildMessage);
+    bus_->RegisterClientAsSender(shiftboss_client_id_, kInitiateRebuildResponseMessage);
+
+    // Message sent to Worker.
+    bus_->RegisterClientAsSender(shiftboss_client_id_, kRebuildWorkOrderMessage);
+
+    // Message sent to Foreman.
+    bus_->RegisterClientAsSender(shiftboss_client_id_, kCatalogRelationNewBlockMessage);
+    bus_->RegisterClientAsSender(shiftboss_client_id_, kDataPipelineMessage);
+    bus_->RegisterClientAsSender(shiftboss_client_id_, kWorkOrdersAvailableMessage);
+    bus_->RegisterClientAsSender(shiftboss_client_id_, kWorkOrderFeedbackMessage);
+
+    // Forward the following message types from Foreman to Workers.
+    bus_->RegisterClientAsReceiver(shiftboss_client_id_, kWorkOrderMessage);
+    bus_->RegisterClientAsSender(shiftboss_client_id_, kWorkOrderMessage);
+
+    // Forward the following message types from Workers to Foreman.
+    bus_->RegisterClientAsReceiver(shiftboss_client_id_, kWorkOrderCompleteMessage);
+    bus_->RegisterClientAsSender(shiftboss_client_id_, kWorkOrderCompleteMessage);
+
+    bus_->RegisterClientAsReceiver(shiftboss_client_id_, kRebuildWorkOrderCompleteMessage);
+    bus_->RegisterClientAsSender(shiftboss_client_id_, kRebuildWorkOrderCompleteMessage);
+
+    bus_->RegisterClientAsReceiver(shiftboss_client_id_, kQueryResultRelationMessage);
+    bus_->RegisterClientAsSender(shiftboss_client_id_, kQueryResultRelationResponseMessage);
+
+    // Stop itself.
+    bus_->RegisterClientAsReceiver(shiftboss_client_id_, kPoisonMessage);
+    // Stop all workers.
+    bus_->RegisterClientAsSender(shiftboss_client_id_, kPoisonMessage);
+
+    for (std::size_t i = 0; i < workers_->getNumWorkers(); ++i) {
+      worker_addresses_.AddRecipient(workers_->getClientID(i));
+    }
+
+    registerWithForeman();
+  }
+
+  ~Shiftboss() override {
+  }
+
+  /**
+   * @brief Get the TMB client ID of Shiftboss thread.
+   *
+   * @return TMB client ID of shiftboss thread.
+   **/
+  inline tmb::client_id getBusClientID() const {
+    return shiftboss_client_id_;
+  }
+
+  /**
+   * @brief Get the Work Order processing capacity of all Workers managed by
+   *        Shiftboss during a single round of WorkOrder dispatch.
+   **/
+  inline std::size_t getWorkOrderCapacity() const {
+    DCHECK_NE(max_msgs_per_worker_, 0u);
+    return max_msgs_per_worker_ * workers_->getNumWorkers();
+  }
+
+  /**
+   * @brief Get the Worker to assign WorkOrders for execution. Block to wait if
+   *        all Workers have reached their capacity for queued WorkOrders.
+   **/
+  // TODO(zuyu): To achieve non-blocking, we need a queue to cache received
+  // normal Work Order protos from Foreman and the generated rebuild Work Orders.
+  inline std::size_t getSchedulableWorker();
+
+  /**
+   * @brief Set the maximum number of messages that should be allocated to each
+   *        worker during a single round of WorkOrder dispatch.
+   *
+   * @param max_msgs_per_worker Maximum number of messages.
+   **/
+  inline void setMaxMessagesPerWorker(const std::size_t max_msgs_per_worker) {
+    max_msgs_per_worker_ = max_msgs_per_worker;
+  }
+
+ protected:
+  /**
+   * @brief The shiftboss receives workorders, and based on the response it
+   *        assigns workorders to workers.
+   *
+   * @note  The workers who get the messages from the Shiftboss execute and
+   *        subsequently delete the WorkOrder contained in the message.
+   **/
+  void run() override;
+
+ private:
+  void registerWithForeman();
+
+  /**
+   * @brief Process the Shiftboss initiate message and ack back.
+   *
+   * @param query_id The given query id.
+   * @param catalog_database_cache_proto The proto used to update
+   *        CatalogDatabaseCache.
+   * @param query_context_proto The QueryContext proto.
+   **/
+  void processQueryInitiateMessage(const std::size_t query_id,
+                                   const serialization::CatalogDatabase &catalog_database_cache_proto,
+                                   const serialization::QueryContext &query_context_proto);
+
+  /**
+   * @brief Process the RebuildWorkOrder initiate message and ack back.
+   *
+   * @param query_id The ID of the query to which this RebuildWorkOrder initiate
+   *        message belongs.
+   * @param op_index The index of the operator for rebuild work orders.
+   * @param dest_index The InsertDestination index in QueryContext to rebuild.
+   * @param rel_id The relation that needs to generate rebuild work orders.
+   **/
+  void processInitiateRebuildMessage(const std::size_t query_id,
+                                     const std::size_t op_index,
+                                     const QueryContext::insert_destination_id dest_index,
+                                     const relation_id rel_id);
+
+  // TODO(zuyu): Use two buses for the message communication between Foreman and Shiftboss,
+  // and Shiftboss and Worker thread pool.
+  tmb::MessageBus *bus_;
+
+  CatalogDatabaseCache database_cache_;
+  StorageManager *storage_manager_;
+  WorkerDirectory *workers_;
+
+  // The ID of the CPU that the Shiftboss thread can optionally be pinned to.
+  const int cpu_id_;
+
+  tmb::client_id shiftboss_client_id_, foreman_client_id_;
+
+  // TMB recipients for all workers managed by this Shiftboss.
+  tmb::Address worker_addresses_;
+
+  // During a single round of WorkOrder dispatch, a Worker should be allocated
+  // at most these many WorkOrders.
+  std::size_t max_msgs_per_worker_;
+
+  // The worker index for scheduling Work Order.
+  std::size_t start_worker_index_;
+
+  // QueryContexts per query.
+  std::unordered_map<std::size_t, std::unique_ptr<QueryContext>> query_contexts_;
+
+  DISALLOW_COPY_AND_ASSIGN(Shiftboss);
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_QUERY_EXECUTION_SHIFTBOSS_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7415ee87/storage/StorageManager.hpp
----------------------------------------------------------------------
diff --git a/storage/StorageManager.hpp b/storage/StorageManager.hpp
index 50ddb0f..348018f 100644
--- a/storage/StorageManager.hpp
+++ b/storage/StorageManager.hpp
@@ -619,6 +619,7 @@ class StorageManager {
   FRIEND_TEST(BlockLocatorTest, BlockTest);
   FRIEND_TEST(BlockLocatorTest, BlobTest);
 
+  friend class Shiftboss;
   FRIEND_TEST(StorageManagerTest, DifferentNUMANodeBlobTestWithEviction);
   FRIEND_TEST(StorageManagerTest, EvictFromSameShardTest);
 


[26/30] incubator-quickstep git commit: Initial commit for QUICKSTEP-28 and QUICKSTEP-29. Code refactoring and cleanup, some more optimizations are pending.

Posted by ra...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/storage/FastSeparateChainingHashTable.hpp
----------------------------------------------------------------------
diff --git a/storage/FastSeparateChainingHashTable.hpp b/storage/FastSeparateChainingHashTable.hpp
new file mode 100644
index 0000000..64c4979
--- /dev/null
+++ b/storage/FastSeparateChainingHashTable.hpp
@@ -0,0 +1,1761 @@
+/**
+ *   Copyright 2011-2015 Quickstep Technologies LLC.
+ *   Copyright 2015-2016 Pivotal Software, Inc.
+ *
+ *   Licensed 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.
+ **/
+
+#ifndef QUICKSTEP_STORAGE_FAST_SEPARATE_CHAINING_HASH_TABLE_HPP_
+#define QUICKSTEP_STORAGE_FAST_SEPARATE_CHAINING_HASH_TABLE_HPP_
+
+#include <algorithm>
+#include <atomic>
+#include <cstddef>
+#include <cstring>
+#include <limits>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "storage/HashTable.hpp"
+#include "storage/FastHashTable.hpp"
+#include "storage/HashTableBase.hpp"
+#include "storage/HashTableKeyManager.hpp"
+#include "storage/StorageBlob.hpp"
+#include "storage/StorageBlockInfo.hpp"
+#include "storage/StorageConstants.hpp"
+#include "storage/StorageManager.hpp"
+#include "threading/SpinSharedMutex.hpp"
+#include "types/Type.hpp"
+#include "types/TypedValue.hpp"
+#include "utility/Alignment.hpp"
+#include "utility/Macros.hpp"
+#include "utility/PrimeNumber.hpp"
+
+namespace quickstep {
+
+/** \addtogroup Storage
+ *  @{
+ */
+
+/**
+ * @brief A hash table implementation which uses separate chaining for buckets.
+ **/
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+class FastSeparateChainingHashTable : public FastHashTable<resizable,
+                                                   serializable,
+                                                   force_key_copy,
+                                                   allow_duplicate_keys> {
+ public:
+  FastSeparateChainingHashTable(const std::vector<const Type*> &key_types,
+                            const std::size_t num_entries,
+                            const std::vector<std::size_t> &payload_sizes,
+                            const std::vector<AggregationHandle *> &handles,
+                            StorageManager *storage_manager);
+
+  FastSeparateChainingHashTable(const std::vector<const Type*> &key_types,
+                            void *hash_table_memory,
+                            const std::size_t hash_table_memory_size,
+                            const bool new_hash_table,
+                            const bool hash_table_memory_zeroed);
+
+  // Delegating constructors for single scalar keys.
+  FastSeparateChainingHashTable(const Type &key_type,
+                            const std::size_t num_entries,
+                            StorageManager *storage_manager)
+      : FastSeparateChainingHashTable(std::vector<const Type*>(1, &key_type),
+                                  num_entries,
+                                  storage_manager) {
+  }
+
+  FastSeparateChainingHashTable(const Type &key_type,
+                            void *hash_table_memory,
+                            const std::size_t hash_table_memory_size,
+                            const bool new_hash_table,
+                            const bool hash_table_memory_zeroed)
+      : FastSeparateChainingHashTable(std::vector<const Type*>(1, &key_type),
+                                  hash_table_memory,
+                                  hash_table_memory_size,
+                                  new_hash_table,
+                                  hash_table_memory_zeroed) {
+  }
+
+  ~FastSeparateChainingHashTable() override {
+    DestroyValues(buckets_,
+                  header_->buckets_allocated.load(std::memory_order_relaxed),
+                  bucket_size_);
+    std::free(init_payload_);
+  }
+
+  void clear() override;
+
+  std::size_t numEntries() const override {
+    return header_->buckets_allocated.load(std::memory_order_relaxed);
+  }
+
+  const uint8_t* getSingle(const TypedValue &key) const override;
+  const uint8_t* getSingleCompositeKey(const std::vector<TypedValue> &key) const override;
+  const uint8_t* getSingleCompositeKey(const std::vector<TypedValue> &key, int index) const override;
+
+  void getAll(const TypedValue &key,
+              std::vector<const uint8_t*> *values) const override;
+  void getAllCompositeKey(const std::vector<TypedValue> &key,
+                          std::vector<const uint8_t*> *values) const override;
+
+ protected:
+  HashTablePutResult putInternal(const TypedValue &key,
+                                 const std::size_t variable_key_size,
+                                 const uint8_t &value,
+                                 HashTablePreallocationState *prealloc_state) override;
+  HashTablePutResult putCompositeKeyInternal(const std::vector<TypedValue> &key,
+                                             const std::size_t variable_key_size,
+                                             const uint8_t &value,
+                                             HashTablePreallocationState *prealloc_state) override;
+  HashTablePutResult putCompositeKeyInternalFast(const std::vector<TypedValue> &key,
+                                             const std::size_t variable_key_size,
+                                             const std::uint8_t *init_value_ptr,
+                                             HashTablePreallocationState *prealloc_state) override;
+
+  uint8_t* upsertInternal(const TypedValue &key,
+                         const std::size_t variable_key_size,
+                         const uint8_t &initial_value) override;
+  uint8_t* upsertInternalFast(const TypedValue &key,
+                         const std::uint8_t *init_value_ptr,
+                         const std::size_t variable_key_size) override;
+
+  uint8_t* upsertCompositeKeyInternal(const std::vector<TypedValue> &key,
+                                     const std::size_t variable_key_size,
+                                     const uint8_t &initial_value) override;
+
+  uint8_t* upsertCompositeKeyInternalFast(const std::vector<TypedValue> &key,
+                                     const std::uint8_t *init_value_ptr,
+                                     const std::size_t variable_key_size) override;
+
+  bool getNextEntry(TypedValue *key,
+                    const uint8_t **value,
+                    std::size_t *entry_num) const override;
+  bool getNextEntryCompositeKey(std::vector<TypedValue> *key,
+                                const uint8_t **value,
+                                std::size_t *entry_num) const override;
+
+  bool getNextEntryForKey(const TypedValue &key,
+                          const std::size_t hash_code,
+                          const uint8_t **value,
+                          std::size_t *entry_num) const override;
+  bool getNextEntryForCompositeKey(const std::vector<TypedValue> &key,
+                                   const std::size_t hash_code,
+                                   const uint8_t **value,
+                                   std::size_t *entry_num) const override;
+
+  bool hasKey(const TypedValue &key) const override;
+  bool hasCompositeKey(const std::vector<TypedValue> &key) const override;
+
+  void resize(const std::size_t extra_buckets,
+              const std::size_t extra_variable_storage,
+              const std::size_t retry_num = 0) override;
+
+  bool preallocateForBulkInsert(const std::size_t total_entries,
+                                const std::size_t total_variable_key_size,
+                                HashTablePreallocationState *prealloc_state) override;
+
+  size_t get_buckets_allocated() const override {return header_->buckets_allocated;}
+
+ private:
+  struct Header {
+    std::size_t num_slots;
+    std::size_t num_buckets;
+    alignas(kCacheLineBytes)
+        std::atomic<std::size_t> buckets_allocated;
+    alignas(kCacheLineBytes)
+        std::atomic<std::size_t> variable_length_bytes_allocated;
+  };
+
+  std::uint8_t *init_payload_;
+  std::size_t kBucketAlignment;
+
+  // Value's offset in a bucket is the first alignof(ValueT) boundary after the
+  // next pointer and hash code.
+  std::size_t kValueOffset;
+
+  // Round bucket size up to a multiple of kBucketAlignment.
+  constexpr std::size_t ComputeBucketSize(const std::size_t fixed_key_size) {
+    return (((kValueOffset + this->total_payload_size_ + fixed_key_size - 1) / kBucketAlignment) + 1)
+           * kBucketAlignment;
+  }
+  // If ValueT is not trivially destructible, invoke its destructor for all
+  // values held in the specified buckets (including those in "empty" buckets
+  // that were default constructed). If ValueT is trivially destructible, this
+  // is a no-op.
+  void DestroyValues(void *buckets,
+                            const std::size_t num_buckets,
+                            const std::size_t bucket_size);
+
+  // Attempt to find an empty bucket to insert 'hash_code' into, starting after
+  // '*bucket' in the chain (or, if '*bucket' is NULL, starting from the slot
+  // array). Returns true and stores SIZE_T_MAX in '*pending_chain_ptr' if an
+  // empty bucket is found. Returns false if 'allow_duplicate_keys' is false
+  // and a hash collision is found (caller should then check whether there is a
+  // genuine key collision or the hash collision is spurious). Returns false
+  // and sets '*bucket' to NULL if there are no more empty buckets in the hash
+  // table. If 'variable_key_allocation_required' is nonzero, this method will
+  // attempt to allocate storage for a variable-length key BEFORE allocating a
+  // bucket, so that no bucket number below 'header_->num_buckets' is ever
+  // deallocated after being allocated.
+  inline bool locateBucketForInsertion(const std::size_t hash_code,
+                                       const std::size_t variable_key_allocation_required,
+                                       void **bucket,
+                                       std::atomic<std::size_t> **pending_chain_ptr,
+                                       std::size_t *pending_chain_ptr_finish_value,
+                                       HashTablePreallocationState *prealloc_state);
+
+  // Write a scalar 'key' and its 'hash_code' into the '*bucket', which was
+  // found by locateBucketForInsertion(). Assumes that storage for a
+  // variable-length key copy (if any) was already allocated by a successful
+  // call to allocateVariableLengthKeyStorage().
+  inline void writeScalarKeyToBucket(const TypedValue &key,
+                                     const std::size_t hash_code,
+                                     void *bucket,
+                                     HashTablePreallocationState *prealloc_state);
+
+  // Write a composite 'key' and its 'hash_code' into the '*bucket', which was
+  // found by locateBucketForInsertion(). Assumes that storage for
+  // variable-length key copies (if any) was already allocated by a successful
+  // call to allocateVariableLengthKeyStorage().
+  inline void writeCompositeKeyToBucket(const std::vector<TypedValue> &key,
+                                        const std::size_t hash_code,
+                                        void *bucket,
+                                        HashTablePreallocationState *prealloc_state);
+
+  // Determine whether it is actually necessary to resize this hash table.
+  // Checks that there is at least one unallocated bucket, and that there is
+  // at least 'extra_variable_storage' bytes of variable-length storage free.
+  bool isFull(const std::size_t extra_variable_storage) const;
+
+  // Helper object to manage key storage.
+  HashTableKeyManager<serializable, force_key_copy> key_manager_;
+
+  // In-memory structure is as follows:
+  //   - SeparateChainingHashTable::Header
+  //   - Array of slots, interpreted as follows:
+  //       - 0 = Points to nothing (empty)
+  //       - SIZE_T_MAX = Pending (some thread is starting a chain from this
+  //         slot and will overwrite it soon)
+  //       - Anything else = The number of the first bucket in the chain for
+  //         this slot PLUS ONE (i.e. subtract one to get the actual bucket
+  //         number).
+  //   - Array of buckets, each of which is:
+  //       - atomic size_t "next" pointer, interpreted the same as slots above.
+  //       - size_t hash value
+  //       - possibly some unused bytes as needed so that ValueT's alignment
+  //         requirement is met
+  //       - ValueT value slot
+  //       - fixed-length key storage (which may include pointers to external
+  //         memory or offsets of variable length keys stored within this hash
+  //         table)
+  //       - possibly some additional unused bytes so that bucket size is a
+  //         multiple of both alignof(std::atomic<std::size_t>) and
+  //         alignof(ValueT)
+  //   - Variable-length key storage region (referenced by offsets stored in
+  //     fixed-length keys).
+  Header *header_;
+
+  std::atomic<std::size_t> *slots_;
+  void *buckets_;
+  const std::size_t bucket_size_;
+
+  DISALLOW_COPY_AND_ASSIGN(FastSeparateChainingHashTable);
+};
+
+/** @} */
+
+// ----------------------------------------------------------------------------
+// Implementations of template class methods follow.
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::FastSeparateChainingHashTable(const std::vector<const Type*> &key_types,
+                                const std::size_t num_entries,
+                                const std::vector<std::size_t> &payload_sizes,
+                                const std::vector<AggregationHandle *> &handles,
+                                StorageManager *storage_manager)
+        : FastHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>(
+              key_types,
+              num_entries,
+              handles,
+              payload_sizes,
+              storage_manager,
+              false,
+              false,
+              true),
+          kBucketAlignment(alignof(std::atomic<std::size_t>)),
+          kValueOffset(sizeof(std::atomic<std::size_t>) + sizeof(std::size_t)),
+          key_manager_(this->key_types_, kValueOffset + this->total_payload_size_),
+          bucket_size_(ComputeBucketSize(key_manager_.getFixedKeySize())) {
+  init_payload_ = static_cast<std::uint8_t *>(calloc(this->total_payload_size_, 1));
+  for (auto handle : handles)
+      handle->initPayload(init_payload_);
+  // Bucket size always rounds up to the alignment requirement of the atomic
+  // size_t "next" pointer at the front or a ValueT, whichever is larger.
+  //
+  // Give base HashTable information about what key components are stored
+  // inline from 'key_manager_'.
+  this->setKeyInline(key_manager_.getKeyInline());
+
+  // Pick out a prime number of slots and calculate storage requirements.
+  std::size_t num_slots_tmp = get_next_prime_number(num_entries * kHashTableLoadFactor);
+  std::size_t required_memory = sizeof(Header)
+                                + num_slots_tmp * sizeof(std::atomic<std::size_t>)
+                                + (num_slots_tmp / kHashTableLoadFactor)
+                                    * (bucket_size_ + key_manager_.getEstimatedVariableKeySize());
+  std::size_t num_storage_slots = this->storage_manager_->SlotsNeededForBytes(required_memory);
+  if (num_storage_slots == 0) {
+    FATAL_ERROR("Storage requirement for SeparateChainingHashTable "
+                "exceeds maximum allocation size.");
+  }
+
+  // Get a StorageBlob to hold the hash table.
+  const block_id blob_id = this->storage_manager_->createBlob(num_storage_slots);
+  this->blob_ = this->storage_manager_->getBlobMutable(blob_id);
+
+  void *aligned_memory_start = this->blob_->getMemoryMutable();
+  std::size_t available_memory = num_storage_slots * kSlotSizeBytes;
+  if (align(alignof(Header),
+            sizeof(Header),
+            aligned_memory_start,
+            available_memory)
+          == nullptr) {
+    // With current values from StorageConstants.hpp, this should be
+    // impossible. A blob is at least 1 MB, while a Header has alignment
+    // requirement of just kCacheLineBytes (64 bytes).
+    FATAL_ERROR("StorageBlob used to hold resizable "
+                "SeparateChainingHashTable is too small to meet alignment "
+                "requirements of SeparateChainingHashTable::Header.");
+  } else if (aligned_memory_start != this->blob_->getMemoryMutable()) {
+    // This should also be impossible, since the StorageManager allocates slots
+    // aligned to kCacheLineBytes.
+    DEV_WARNING("StorageBlob memory adjusted by "
+                << (num_storage_slots * kSlotSizeBytes - available_memory)
+                << " bytes to meet alignment requirement for "
+                << "SeparateChainingHashTable::Header.");
+  }
+
+  // Locate the header.
+  header_ = static_cast<Header*>(aligned_memory_start);
+  aligned_memory_start = static_cast<char*>(aligned_memory_start) + sizeof(Header);
+  available_memory -= sizeof(Header);
+
+  // Recompute the number of slots & buckets using the actual available memory.
+  // Most likely, we got some extra free bucket space due to "rounding up" to
+  // the storage blob's size. It's also possible (though very unlikely) that we
+  // will wind up with fewer buckets than we initially wanted because of screwy
+  // alignment requirements for ValueT.
+  std::size_t num_buckets_tmp
+      = available_memory / (kHashTableLoadFactor * sizeof(std::atomic<std::size_t>)
+                            + bucket_size_
+                            + key_manager_.getEstimatedVariableKeySize());
+  num_slots_tmp = get_previous_prime_number(num_buckets_tmp * kHashTableLoadFactor);
+  num_buckets_tmp = num_slots_tmp / kHashTableLoadFactor;
+  DEBUG_ASSERT(num_slots_tmp > 0);
+  DEBUG_ASSERT(num_buckets_tmp > 0);
+
+  // Locate the slot array.
+  slots_ = static_cast<std::atomic<std::size_t>*>(aligned_memory_start);
+  aligned_memory_start = static_cast<char*>(aligned_memory_start)
+                         + sizeof(std::atomic<std::size_t>) * num_slots_tmp;
+  available_memory -= sizeof(std::atomic<std::size_t>) * num_slots_tmp;
+
+  // Locate the buckets.
+  buckets_ = aligned_memory_start;
+  // Extra-paranoid: If ValueT has an alignment requirement greater than that
+  // of std::atomic<std::size_t>, we may need to adjust the start of the bucket
+  // array.
+  if (align(kBucketAlignment,
+            bucket_size_,
+            buckets_,
+            available_memory)
+          == nullptr) {
+    FATAL_ERROR("StorageBlob used to hold resizable "
+                "SeparateChainingHashTable is too small to meet "
+                "alignment requirements of buckets.");
+  } else if (buckets_ != aligned_memory_start) {
+    DEV_WARNING("Bucket array start position adjusted to meet alignment "
+                "requirement for SeparateChainingHashTable's value type.");
+    if (num_buckets_tmp * bucket_size_ > available_memory) {
+      --num_buckets_tmp;
+    }
+  }
+
+  // Fill in the header.
+  header_->num_slots = num_slots_tmp;
+  header_->num_buckets = num_buckets_tmp;
+  header_->buckets_allocated.store(0, std::memory_order_relaxed);
+  header_->variable_length_bytes_allocated.store(0, std::memory_order_relaxed);
+  available_memory -= bucket_size_ * (header_->num_buckets);
+
+  // Locate variable-length key storage region, and give it all the remaining
+  // bytes in the blob.
+  key_manager_.setVariableLengthStorageInfo(
+      static_cast<char*>(buckets_) + header_->num_buckets * bucket_size_,
+      available_memory,
+      &(header_->variable_length_bytes_allocated));
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::FastSeparateChainingHashTable(const std::vector<const Type*> &key_types,
+                                void *hash_table_memory,
+                                const std::size_t hash_table_memory_size,
+                                const bool new_hash_table,
+                                const bool hash_table_memory_zeroed)
+        : FastHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>(
+              key_types,
+              hash_table_memory,
+              hash_table_memory_size,
+              new_hash_table,
+              hash_table_memory_zeroed,
+              false,
+              false,
+              true),
+          kBucketAlignment(alignof(std::atomic<std::size_t>) < alignof(uint8_t) ? alignof(uint8_t)
+                                                  : alignof(std::atomic<std::size_t>)),
+          kValueOffset((((sizeof(std::atomic<std::size_t>) + sizeof(std::size_t) - 1) /
+                                           alignof(uint8_t)) + 1) * alignof(uint8_t)),
+          key_manager_(this->key_types_, kValueOffset + sizeof(uint8_t)),
+          bucket_size_(ComputeBucketSize(key_manager_.getFixedKeySize())) {
+  // Bucket size always rounds up to the alignment requirement of the atomic
+  // size_t "next" pointer at the front or a ValueT, whichever is larger.
+  //
+  // Make sure that the larger of the two alignment requirements also satisfies
+  // the smaller.
+  static_assert(alignof(std::atomic<std::size_t>) < alignof(uint8_t)
+                    ? alignof(uint8_t) % alignof(std::atomic<std::size_t>) == 0
+                    : alignof(std::atomic<std::size_t>) % alignof(uint8_t) == 0,
+                "Alignment requirement of std::atomic<std::size_t> does not "
+                "evenly divide with alignment requirement of ValueT.");
+
+  // Give base HashTable information about what key components are stored
+  // inline from 'key_manager_'.
+  this->setKeyInline(key_manager_.getKeyInline());
+
+  // FIXME(chasseur): If we are reconstituting a HashTable using a block of
+  // memory whose start was aligned differently than the memory block that was
+  // originally used (modulo alignof(Header)), we could wind up with all of our
+  // data structures misaligned. If memory is inside a
+  // StorageBlock/StorageBlob, this will never occur, since the StorageManager
+  // always allocates slots aligned to kCacheLineBytes. Similarly, this isn't
+  // a problem for memory inside any other allocation aligned to at least
+  // alignof(Header) == kCacheLineBytes.
+
+  void *aligned_memory_start = this->hash_table_memory_;
+  std::size_t available_memory = this->hash_table_memory_size_;
+
+  if (align(alignof(Header),
+            sizeof(Header),
+            aligned_memory_start,
+            available_memory)
+          == nullptr) {
+    FATAL_ERROR("Attempted to create a non-resizable "
+                << "SeparateChainingHashTable with "
+                << available_memory << " bytes of memory at "
+                << aligned_memory_start << " which either can not fit a "
+                << "SeparateChainingHashTable::Header or meet its alignement "
+                << "requirement.");
+  } else if (aligned_memory_start != this->hash_table_memory_) {
+    // In general, we could get memory of any alignment, although at least
+    // cache-line aligned would be nice.
+    DEV_WARNING("StorageBlob memory adjusted by "
+                << (this->hash_table_memory_size_ - available_memory)
+                << " bytes to meet alignment requirement for "
+                << "SeparateChainingHashTable::Header.");
+  }
+
+  header_ = static_cast<Header*>(aligned_memory_start);
+  aligned_memory_start = static_cast<char*>(aligned_memory_start) + sizeof(Header);
+  available_memory -= sizeof(Header);
+
+  if (new_hash_table) {
+    std::size_t estimated_bucket_capacity
+        = available_memory / (kHashTableLoadFactor * sizeof(std::atomic<std::size_t>)
+                              + bucket_size_
+                              + key_manager_.getEstimatedVariableKeySize());
+    std::size_t num_slots = get_previous_prime_number(estimated_bucket_capacity * kHashTableLoadFactor);
+
+    // Fill in the header.
+    header_->num_slots = num_slots;
+    header_->num_buckets = num_slots / kHashTableLoadFactor;
+    header_->buckets_allocated.store(0, std::memory_order_relaxed);
+    header_->variable_length_bytes_allocated.store(0, std::memory_order_relaxed);
+  }
+
+  // Locate the slot array.
+  slots_ = static_cast<std::atomic<std::size_t>*>(aligned_memory_start);
+  aligned_memory_start = static_cast<char*>(aligned_memory_start)
+                         + sizeof(std::atomic<std::size_t>) * header_->num_slots;
+  available_memory -= sizeof(std::atomic<std::size_t>) * header_->num_slots;
+
+  if (new_hash_table && !hash_table_memory_zeroed) {
+    std::memset(slots_, 0x0, sizeof(std::atomic<std::size_t>) * header_->num_slots);
+  }
+
+  // Locate the buckets.
+  buckets_ = aligned_memory_start;
+  // Extra-paranoid: sizeof(Header) should almost certainly be a multiple of
+  // kBucketAlignment, unless ValueT has some members with seriously big
+  // (> kCacheLineBytes) alignment requirements specified using alignas().
+  if (align(kBucketAlignment,
+            bucket_size_,
+            buckets_,
+            available_memory)
+          == nullptr) {
+    FATAL_ERROR("Attempted to create a non-resizable "
+                << "SeparateChainingHashTable with "
+                << this->hash_table_memory_size_ << " bytes of memory at "
+                << this->hash_table_memory_ << ", which can hold an aligned "
+                << "SeparateChainingHashTable::Header but does not have "
+                << "enough remaining space for even a single hash bucket.");
+  } else if (buckets_ != aligned_memory_start) {
+    DEV_WARNING("Bucket array start position adjusted to meet alignment "
+                "requirement for SeparateChainingHashTable's value type.");
+    if (header_->num_buckets * bucket_size_ > available_memory) {
+      DEBUG_ASSERT(new_hash_table);
+      --(header_->num_buckets);
+    }
+  }
+  available_memory -= bucket_size_ * header_->num_buckets;
+
+  // Make sure "next" pointers in buckets are zeroed-out.
+  if (new_hash_table && !hash_table_memory_zeroed) {
+    std::memset(buckets_, 0x0, header_->num_buckets * bucket_size_);
+  }
+
+  // Locate variable-length key storage region.
+  key_manager_.setVariableLengthStorageInfo(
+      static_cast<char*>(buckets_) + header_->num_buckets * bucket_size_,
+      available_memory,
+      &(header_->variable_length_bytes_allocated));
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+void FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::clear() {
+  const std::size_t used_buckets = header_->buckets_allocated.load(std::memory_order_relaxed);
+  // Destroy existing values, if necessary.
+  DestroyValues(buckets_,
+                used_buckets,
+                bucket_size_);
+
+  // Zero-out slot array.
+  std::memset(slots_, 0x0, sizeof(std::atomic<std::size_t>) * header_->num_slots);
+
+  // Zero-out used buckets.
+  std::memset(buckets_, 0x0, used_buckets * bucket_size_);
+
+  header_->buckets_allocated.store(0, std::memory_order_relaxed);
+  header_->variable_length_bytes_allocated.store(0, std::memory_order_relaxed);
+  key_manager_.zeroNextVariableLengthKeyOffset();
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+const uint8_t* FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::getSingle(const TypedValue &key) const {
+  DEBUG_ASSERT(!allow_duplicate_keys);
+  DEBUG_ASSERT(this->key_types_.size() == 1);
+  DEBUG_ASSERT(key.isPlausibleInstanceOf(this->key_types_.front()->getSignature()));
+
+  const std::size_t hash_code = key.getHash();
+  std::size_t bucket_ref = slots_[hash_code % header_->num_slots].load(std::memory_order_relaxed);
+  while (bucket_ref != 0) {
+    DEBUG_ASSERT(bucket_ref != std::numeric_limits<std::size_t>::max());
+    const char *bucket = static_cast<const char*>(buckets_) + (bucket_ref - 1) * bucket_size_;
+    const std::size_t bucket_hash = *reinterpret_cast<const std::size_t*>(
+        bucket + sizeof(std::atomic<std::size_t>));
+    if ((bucket_hash == hash_code) && key_manager_.scalarKeyCollisionCheck(key, bucket)) {
+      // Match located.
+      return reinterpret_cast<const uint8_t*>(bucket + kValueOffset);
+    }
+    bucket_ref = reinterpret_cast<const std::atomic<std::size_t>*>(bucket)->load(std::memory_order_relaxed);
+  }
+
+  // Reached the end of the chain and didn't find a match.
+  return nullptr;
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+const uint8_t* FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::getSingleCompositeKey(const std::vector<TypedValue> &key) const {
+  DEBUG_ASSERT(!allow_duplicate_keys);
+  DEBUG_ASSERT(this->key_types_.size() == key.size());
+
+  const std::size_t hash_code = this->hashCompositeKey(key);
+  std::size_t bucket_ref = slots_[hash_code % header_->num_slots].load(std::memory_order_relaxed);
+  while (bucket_ref != 0) {
+    DEBUG_ASSERT(bucket_ref != std::numeric_limits<std::size_t>::max());
+    const char *bucket = static_cast<const char*>(buckets_) + (bucket_ref - 1) * bucket_size_;
+    const std::size_t bucket_hash = *reinterpret_cast<const std::size_t*>(
+        bucket + sizeof(std::atomic<std::size_t>));
+    if ((bucket_hash == hash_code) && key_manager_.compositeKeyCollisionCheck(key, bucket)) {
+      // Match located.
+      return reinterpret_cast<const uint8_t*>(bucket + kValueOffset);
+    }
+    bucket_ref = reinterpret_cast<const std::atomic<std::size_t>*>(bucket)->load(std::memory_order_relaxed);
+  }
+
+  // Reached the end of the chain and didn't find a match.
+  return nullptr;
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+const uint8_t* FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::getSingleCompositeKey(const std::vector<TypedValue> &key, int index) const {
+  DEBUG_ASSERT(!allow_duplicate_keys);
+  DEBUG_ASSERT(this->key_types_.size() == key.size());
+
+  const std::size_t hash_code = this->hashCompositeKey(key);
+  std::size_t bucket_ref = slots_[hash_code % header_->num_slots].load(std::memory_order_relaxed);
+  while (bucket_ref != 0) {
+    DEBUG_ASSERT(bucket_ref != std::numeric_limits<std::size_t>::max());
+    const char *bucket = static_cast<const char*>(buckets_) + (bucket_ref - 1) * bucket_size_;
+    const std::size_t bucket_hash = *reinterpret_cast<const std::size_t*>(
+        bucket + sizeof(std::atomic<std::size_t>));
+    if ((bucket_hash == hash_code) && key_manager_.compositeKeyCollisionCheck(key, bucket)) {
+      // Match located.
+      return reinterpret_cast<const uint8_t*>(bucket + kValueOffset)+this->payload_offsets_[index];
+    }
+    bucket_ref = reinterpret_cast<const std::atomic<std::size_t>*>(bucket)->load(std::memory_order_relaxed);
+  }
+
+  // Reached the end of the chain and didn't find a match.
+  return nullptr;
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+void FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::getAll(const TypedValue &key, std::vector<const uint8_t*> *values) const {
+  DEBUG_ASSERT(this->key_types_.size() == 1);
+  DEBUG_ASSERT(key.isPlausibleInstanceOf(this->key_types_.front()->getSignature()));
+
+  const std::size_t hash_code = key.getHash();
+  std::size_t bucket_ref = slots_[hash_code % header_->num_slots].load(std::memory_order_relaxed);
+  while (bucket_ref != 0) {
+    DEBUG_ASSERT(bucket_ref != std::numeric_limits<std::size_t>::max());
+    const char *bucket = static_cast<const char*>(buckets_) + (bucket_ref - 1) * bucket_size_;
+    const std::size_t bucket_hash = *reinterpret_cast<const std::size_t*>(
+        bucket + sizeof(std::atomic<std::size_t>));
+    if ((bucket_hash == hash_code) && key_manager_.scalarKeyCollisionCheck(key, bucket)) {
+      // Match located.
+      values->push_back(reinterpret_cast<const uint8_t*>(bucket + kValueOffset));
+      if (!allow_duplicate_keys) {
+        return;
+      }
+    }
+    bucket_ref = reinterpret_cast<const std::atomic<std::size_t>*>(bucket)->load(std::memory_order_relaxed);
+  }
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+void FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::getAllCompositeKey(const std::vector<TypedValue> &key, std::vector<const uint8_t*> *values) const {
+  DEBUG_ASSERT(this->key_types_.size() == key.size());
+
+  const std::size_t hash_code = this->hashCompositeKey(key);
+  std::size_t bucket_ref = slots_[hash_code % header_->num_slots].load(std::memory_order_relaxed);
+  while (bucket_ref != 0) {
+    DEBUG_ASSERT(bucket_ref != std::numeric_limits<std::size_t>::max());
+    const char *bucket = static_cast<const char*>(buckets_) + (bucket_ref - 1) * bucket_size_;
+    const std::size_t bucket_hash = *reinterpret_cast<const std::size_t*>(
+        bucket + sizeof(std::atomic<std::size_t>));
+    if ((bucket_hash == hash_code) && key_manager_.compositeKeyCollisionCheck(key, bucket)) {
+      // Match located.
+      values->push_back(reinterpret_cast<const uint8_t*>(bucket + kValueOffset));
+      if (!allow_duplicate_keys) {
+        return;
+      }
+    }
+    bucket_ref = reinterpret_cast<const std::atomic<std::size_t>*>(bucket)->load(std::memory_order_relaxed);
+  }
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+HashTablePutResult
+    FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+        ::putInternal(const TypedValue &key,
+                      const std::size_t variable_key_size,
+                      const uint8_t &value,
+                      HashTablePreallocationState *prealloc_state) {
+  DEBUG_ASSERT(this->key_types_.size() == 1);
+  DEBUG_ASSERT(key.isPlausibleInstanceOf(this->key_types_.front()->getSignature()));
+
+  if (prealloc_state == nullptr) {
+    // Early check for a free bucket.
+    if (header_->buckets_allocated.load(std::memory_order_relaxed) >= header_->num_buckets) {
+      return HashTablePutResult::kOutOfSpace;
+    }
+
+    // TODO(chasseur): If allow_duplicate_keys is true, avoid storing more than
+    // one copy of the same variable-length key.
+    if (!key_manager_.allocateVariableLengthKeyStorage(variable_key_size)) {
+      // Ran out of variable-length key storage space.
+      return HashTablePutResult::kOutOfSpace;
+    }
+  }
+
+  const std::size_t hash_code = key.getHash();
+  void *bucket = nullptr;
+  std::atomic<std::size_t> *pending_chain_ptr;
+  std::size_t pending_chain_ptr_finish_value;
+  for (;;) {
+    if (locateBucketForInsertion(hash_code,
+                                 0,
+                                 &bucket,
+                                 &pending_chain_ptr,
+                                 &pending_chain_ptr_finish_value,
+                                 prealloc_state)) {
+      // Found an empty bucket.
+      break;
+    } else if (bucket == nullptr) {
+      // Ran out of buckets. Deallocate any variable space that we were unable
+      // to use.
+      DEBUG_ASSERT(prealloc_state == nullptr);
+      key_manager_.deallocateVariableLengthKeyStorage(variable_key_size);
+      return HashTablePutResult::kOutOfSpace;
+    } else {
+      // Hash collision found, and duplicates aren't allowed.
+      DEBUG_ASSERT(!allow_duplicate_keys);
+      DEBUG_ASSERT(prealloc_state == nullptr);
+      if (key_manager_.scalarKeyCollisionCheck(key, bucket)) {
+        // Duplicate key. Deallocate any variable storage space and return.
+        key_manager_.deallocateVariableLengthKeyStorage(variable_key_size);
+        return HashTablePutResult::kDuplicateKey;
+      }
+    }
+  }
+
+  // Write the key and hash.
+  writeScalarKeyToBucket(key, hash_code, bucket, prealloc_state);
+
+  // Store the value by using placement new with ValueT's copy constructor.
+  new(static_cast<char*>(bucket) + kValueOffset) uint8_t(value);
+
+  // Update the previous chain pointer to point to the new bucket.
+  pending_chain_ptr->store(pending_chain_ptr_finish_value, std::memory_order_release);
+
+  // We're all done.
+  return HashTablePutResult::kOK;
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+HashTablePutResult
+    FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+        ::putCompositeKeyInternal(const std::vector<TypedValue> &key,
+                                  const std::size_t variable_key_size,
+                                  const uint8_t &value,
+                                  HashTablePreallocationState *prealloc_state) {
+  DEBUG_ASSERT(this->key_types_.size() == key.size());
+
+  if (prealloc_state == nullptr) {
+    // Early check for a free bucket.
+    if (header_->buckets_allocated.load(std::memory_order_relaxed) >= header_->num_buckets) {
+      return HashTablePutResult::kOutOfSpace;
+    }
+
+    // TODO(chasseur): If allow_duplicate_keys is true, avoid storing more than
+    // one copy of the same variable-length key.
+    if (!key_manager_.allocateVariableLengthKeyStorage(variable_key_size)) {
+      // Ran out of variable-length key storage space.
+      return HashTablePutResult::kOutOfSpace;
+    }
+  }
+
+  const std::size_t hash_code = this->hashCompositeKey(key);
+  void *bucket = nullptr;
+  std::atomic<std::size_t> *pending_chain_ptr;
+  std::size_t pending_chain_ptr_finish_value;
+  for (;;) {
+    if (locateBucketForInsertion(hash_code,
+                                 0,
+                                 &bucket,
+                                 &pending_chain_ptr,
+                                 &pending_chain_ptr_finish_value,
+                                 prealloc_state)) {
+      // Found an empty bucket.
+      break;
+    } else if (bucket == nullptr) {
+      // Ran out of buckets. Deallocate any variable space that we were unable
+      // to use.
+      DEBUG_ASSERT(prealloc_state == nullptr);
+      key_manager_.deallocateVariableLengthKeyStorage(variable_key_size);
+      return HashTablePutResult::kOutOfSpace;
+    } else {
+      // Hash collision found, and duplicates aren't allowed.
+      DEBUG_ASSERT(!allow_duplicate_keys);
+      DEBUG_ASSERT(prealloc_state == nullptr);
+      if (key_manager_.compositeKeyCollisionCheck(key, bucket)) {
+        // Duplicate key. Deallocate any variable storage space and return.
+        key_manager_.deallocateVariableLengthKeyStorage(variable_key_size);
+        return HashTablePutResult::kDuplicateKey;
+      }
+    }
+  }
+
+  // Write the key and hash.
+  writeCompositeKeyToBucket(key, hash_code, bucket, prealloc_state);
+
+  // Store the value by using placement new with ValueT's copy constructor.
+  new(static_cast<char*>(bucket) + kValueOffset) uint8_t(value);
+
+  // Update the previous chain pointer to point to the new bucket.
+  pending_chain_ptr->store(pending_chain_ptr_finish_value, std::memory_order_release);
+
+  // We're all done.
+  return HashTablePutResult::kOK;
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+HashTablePutResult
+    FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+        ::putCompositeKeyInternalFast(const std::vector<TypedValue> &key,
+                                  const std::size_t variable_key_size,
+                                  const uint8_t *init_value_ptr,
+                                  HashTablePreallocationState *prealloc_state) {
+  DEBUG_ASSERT(this->key_types_.size() == key.size());
+
+  if (prealloc_state == nullptr) {
+    // Early check for a free bucket.
+    if (header_->buckets_allocated.load(std::memory_order_relaxed) >= header_->num_buckets) {
+      return HashTablePutResult::kOutOfSpace;
+    }
+
+    // TODO(chasseur): If allow_duplicate_keys is true, avoid storing more than
+    // one copy of the same variable-length key.
+    if (!key_manager_.allocateVariableLengthKeyStorage(variable_key_size)) {
+      // Ran out of variable-length key storage space.
+      return HashTablePutResult::kOutOfSpace;
+    }
+  }
+
+  const std::size_t hash_code = this->hashCompositeKey(key);
+  void *bucket = nullptr;
+  std::atomic<std::size_t> *pending_chain_ptr;
+  std::size_t pending_chain_ptr_finish_value;
+  for (;;) {
+    if (locateBucketForInsertion(hash_code,
+                                 0,
+                                 &bucket,
+                                 &pending_chain_ptr,
+                                 &pending_chain_ptr_finish_value,
+                                 prealloc_state)) {
+      // Found an empty bucket.
+      break;
+    } else if (bucket == nullptr) {
+      // Ran out of buckets. Deallocate any variable space that we were unable
+      // to use.
+      DEBUG_ASSERT(prealloc_state == nullptr);
+      key_manager_.deallocateVariableLengthKeyStorage(variable_key_size);
+      return HashTablePutResult::kOutOfSpace;
+    } else {
+      // Hash collision found, and duplicates aren't allowed.
+      DEBUG_ASSERT(!allow_duplicate_keys);
+      DEBUG_ASSERT(prealloc_state == nullptr);
+      if (key_manager_.compositeKeyCollisionCheck(key, bucket)) {
+        // Duplicate key. Deallocate any variable storage space and return.
+        key_manager_.deallocateVariableLengthKeyStorage(variable_key_size);
+        return HashTablePutResult::kDuplicateKey;
+      }
+    }
+  }
+
+  // Write the key and hash.
+  writeCompositeKeyToBucket(key, hash_code, bucket, prealloc_state);
+
+  // Store the value by using placement new with ValueT's copy constructor.
+//  new(static_cast<char*>(bucket) + kValueOffset) uint8_t(value);
+    uint8_t *value = static_cast<uint8_t*>(bucket) + kValueOffset;
+        memcpy(value, init_value_ptr, this->total_payload_size_);
+  // Update the previous chain pointer to point to the new bucket.
+  pending_chain_ptr->store(pending_chain_ptr_finish_value, std::memory_order_release);
+
+  // We're all done.
+  return HashTablePutResult::kOK;
+}
+
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+uint8_t* FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::upsertInternal(const TypedValue &key,
+                     const std::size_t variable_key_size,
+                     const uint8_t &initial_value) {
+  DEBUG_ASSERT(!allow_duplicate_keys);
+  DEBUG_ASSERT(this->key_types_.size() == 1);
+  DEBUG_ASSERT(key.isPlausibleInstanceOf(this->key_types_.front()->getSignature()));
+
+  if (variable_key_size > 0) {
+    // Don't allocate yet, since the key may already be present. However, we
+    // do check if either the allocated variable storage space OR the free
+    // space is big enough to hold the key (at least one must be true: either
+    // the key is already present and allocated, or we need to be able to
+    // allocate enough space for it).
+    std::size_t allocated_bytes = header_->variable_length_bytes_allocated.load(std::memory_order_relaxed);
+    if ((allocated_bytes < variable_key_size)
+        && (allocated_bytes + variable_key_size > key_manager_.getVariableLengthKeyStorageSize())) {
+      return nullptr;
+    }
+  }
+
+  const std::size_t hash_code = key.getHash();
+  void *bucket = nullptr;
+  std::atomic<std::size_t> *pending_chain_ptr;
+  std::size_t pending_chain_ptr_finish_value;
+  for (;;) {
+    if (locateBucketForInsertion(hash_code,
+                                 variable_key_size,
+                                 &bucket,
+                                 &pending_chain_ptr,
+                                 &pending_chain_ptr_finish_value,
+                                 nullptr)) {
+      // Found an empty bucket.
+      break;
+    } else if (bucket == nullptr) {
+      // Ran out of buckets or variable-key space.
+      return nullptr;
+    } else if (key_manager_.scalarKeyCollisionCheck(key, bucket)) {
+      // Found an already-existing entry for this key.
+      return reinterpret_cast<uint8_t*>(static_cast<char*>(bucket) + kValueOffset);
+    }
+  }
+
+  // We are now writing to an empty bucket.
+  // Write the key and hash.
+  writeScalarKeyToBucket(key, hash_code, bucket, nullptr);
+
+  // Copy the supplied 'initial_value' into place.
+  uint8_t *value = new(static_cast<char*>(bucket) + kValueOffset) uint8_t(initial_value);
+
+  // Update the previous chain pointer to point to the new bucket.
+  pending_chain_ptr->store(pending_chain_ptr_finish_value, std::memory_order_release);
+
+  // Return the value.
+  return value;
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+uint8_t* FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::upsertInternalFast(const TypedValue &key,
+                     const std::uint8_t *init_value_ptr,
+                     const std::size_t variable_key_size) {
+  DEBUG_ASSERT(!allow_duplicate_keys);
+  DEBUG_ASSERT(this->key_types_.size() == 1);
+  DEBUG_ASSERT(key.isPlausibleInstanceOf(this->key_types_.front()->getSignature()));
+
+  if (variable_key_size > 0) {
+    // Don't allocate yet, since the key may already be present. However, we
+    // do check if either the allocated variable storage space OR the free
+    // space is big enough to hold the key (at least one must be true: either
+    // the key is already present and allocated, or we need to be able to
+    // allocate enough space for it).
+    std::size_t allocated_bytes = header_->variable_length_bytes_allocated.load(std::memory_order_relaxed);
+    if ((allocated_bytes < variable_key_size)
+        && (allocated_bytes + variable_key_size > key_manager_.getVariableLengthKeyStorageSize())) {
+      return nullptr;
+    }
+  }
+
+  const std::size_t hash_code = key.getHash();
+  void *bucket = nullptr;
+  std::atomic<std::size_t> *pending_chain_ptr;
+  std::size_t pending_chain_ptr_finish_value;
+  for (;;) {
+    if (locateBucketForInsertion(hash_code,
+                                 variable_key_size,
+                                 &bucket,
+                                 &pending_chain_ptr,
+                                 &pending_chain_ptr_finish_value,
+                                 nullptr)) {
+      // Found an empty bucket.
+      break;
+    } else if (bucket == nullptr) {
+      // Ran out of buckets or variable-key space.
+      return nullptr;
+    } else if (key_manager_.scalarKeyCollisionCheck(key, bucket)) {
+      // Found an already-existing entry for this key.
+      return reinterpret_cast<uint8_t*>(static_cast<char*>(bucket) + kValueOffset);
+    }
+  }
+
+  // We are now writing to an empty bucket.
+  // Write the key and hash.
+  writeScalarKeyToBucket(key, hash_code, bucket, nullptr);
+
+  // Copy the supplied 'initial_value' into place.
+//  uint8_t *value = new(static_cast<char*>(bucket) + kValueOffset) uint8_t(initial_value);
+
+    uint8_t *value = static_cast<unsigned char*>(bucket) + kValueOffset;
+    if (init_value_ptr == nullptr)
+        memcpy(value, init_payload_, this->total_payload_size_);
+    else
+        memcpy(value, init_value_ptr, this->total_payload_size_);
+
+
+  // Update the previous chain pointer to point to the new bucket.
+  pending_chain_ptr->store(pending_chain_ptr_finish_value, std::memory_order_release);
+
+  // Return the value.
+  return value;
+}
+
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+uint8_t* FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::upsertCompositeKeyInternal(const std::vector<TypedValue> &key,
+                                 const std::size_t variable_key_size,
+                                 const uint8_t &initial_value) {
+  DEBUG_ASSERT(!allow_duplicate_keys);
+  DEBUG_ASSERT(this->key_types_.size() == key.size());
+
+  if (variable_key_size > 0) {
+    // Don't allocate yet, since the key may already be present. However, we
+    // do check if either the allocated variable storage space OR the free
+    // space is big enough to hold the key (at least one must be true: either
+    // the key is already present and allocated, or we need to be able to
+    // allocate enough space for it).
+    std::size_t allocated_bytes = header_->variable_length_bytes_allocated.load(std::memory_order_relaxed);
+    if ((allocated_bytes < variable_key_size)
+        && (allocated_bytes + variable_key_size > key_manager_.getVariableLengthKeyStorageSize())) {
+      return nullptr;
+    }
+  }
+
+  const std::size_t hash_code = this->hashCompositeKey(key);
+  void *bucket = nullptr;
+  std::atomic<std::size_t> *pending_chain_ptr;
+  std::size_t pending_chain_ptr_finish_value;
+  for (;;) {
+    if (locateBucketForInsertion(hash_code,
+                                 variable_key_size,
+                                 &bucket,
+                                 &pending_chain_ptr,
+                                 &pending_chain_ptr_finish_value,
+                                 nullptr)) {
+      // Found an empty bucket.
+      break;
+    } else if (bucket == nullptr) {
+      // Ran out of buckets or variable-key space.
+      return nullptr;
+    } else if (key_manager_.compositeKeyCollisionCheck(key, bucket)) {
+      // Found an already-existing entry for this key.
+      return reinterpret_cast<uint8_t*>(static_cast<char*>(bucket) + kValueOffset);
+    }
+  }
+
+  // We are now writing to an empty bucket.
+  // Write the key and hash.
+  writeCompositeKeyToBucket(key, hash_code, bucket, nullptr);
+
+  // Copy the supplied 'initial_value' into place.
+  uint8_t *value = new(static_cast<char*>(bucket) + kValueOffset) uint8_t(initial_value);
+
+  // Update the previous chaing pointer to point to the new bucket.
+  pending_chain_ptr->store(pending_chain_ptr_finish_value, std::memory_order_release);
+
+  // Return the value.
+  return value;
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+uint8_t* FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::upsertCompositeKeyInternalFast(const std::vector<TypedValue> &key,
+                                 const std::uint8_t *init_value_ptr,
+                                 const std::size_t variable_key_size) {
+  DEBUG_ASSERT(!allow_duplicate_keys);
+  DEBUG_ASSERT(this->key_types_.size() == key.size());
+
+  if (variable_key_size > 0) {
+    // Don't allocate yet, since the key may already be present. However, we
+    // do check if either the allocated variable storage space OR the free
+    // space is big enough to hold the key (at least one must be true: either
+    // the key is already present and allocated, or we need to be able to
+    // allocate enough space for it).
+    std::size_t allocated_bytes = header_->variable_length_bytes_allocated.load(std::memory_order_relaxed);
+    if ((allocated_bytes < variable_key_size)
+        && (allocated_bytes + variable_key_size > key_manager_.getVariableLengthKeyStorageSize())) {
+      return nullptr;
+    }
+  }
+
+  const std::size_t hash_code = this->hashCompositeKey(key);
+  void *bucket = nullptr;
+  std::atomic<std::size_t> *pending_chain_ptr;
+  std::size_t pending_chain_ptr_finish_value;
+  for (;;) {
+    if (locateBucketForInsertion(hash_code,
+                                 variable_key_size,
+                                 &bucket,
+                                 &pending_chain_ptr,
+                                 &pending_chain_ptr_finish_value,
+                                 nullptr)) {
+      // Found an empty bucket.
+      break;
+    } else if (bucket == nullptr) {
+      // Ran out of buckets or variable-key space.
+      return nullptr;
+    } else if (key_manager_.compositeKeyCollisionCheck(key, bucket)) {
+      // Found an already-existing entry for this key.
+      return reinterpret_cast<uint8_t*>(static_cast<char*>(bucket) + kValueOffset);
+    }
+  }
+
+  // We are now writing to an empty bucket.
+  // Write the key and hash.
+  writeCompositeKeyToBucket(key, hash_code, bucket, nullptr);
+
+//  uint8_t *value;
+//  value = static_cast<unsigned char*>(bucket) + kValueOffset;
+    uint8_t *value = static_cast<unsigned char*>(bucket) + kValueOffset;
+    if (init_value_ptr == nullptr)
+        memcpy(value, init_payload_, this->total_payload_size_);
+    else
+        memcpy(value, init_value_ptr, this->total_payload_size_);
+
+  // Update the previous chaing pointer to point to the new bucket.
+  pending_chain_ptr->store(pending_chain_ptr_finish_value, std::memory_order_release);
+
+  // Return the value.
+  return value;
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+bool FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::getNextEntry(TypedValue *key, const uint8_t **value, std::size_t *entry_num) const {
+  DEBUG_ASSERT(this->key_types_.size() == 1);
+  if (*entry_num < header_->buckets_allocated.load(std::memory_order_relaxed)) {
+    const char *bucket = static_cast<const char*>(buckets_) + (*entry_num) * bucket_size_;
+    *key = key_manager_.getKeyComponentTyped(bucket, 0);
+    *value = reinterpret_cast<const uint8_t*>(bucket + kValueOffset);
+    ++(*entry_num);
+    return true;
+  } else {
+    return false;
+  }
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+bool FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::getNextEntryCompositeKey(std::vector<TypedValue> *key,
+                               const uint8_t **value,
+                               std::size_t *entry_num) const {
+  if (*entry_num < header_->buckets_allocated.load(std::memory_order_relaxed)) {
+    const char *bucket = static_cast<const char*>(buckets_) + (*entry_num) * bucket_size_;
+    for (std::vector<const Type*>::size_type key_idx = 0;
+         key_idx < this->key_types_.size();
+         ++key_idx) {
+      key->emplace_back(key_manager_.getKeyComponentTyped(bucket, key_idx));
+    }
+    *value = reinterpret_cast<const uint8_t*>(bucket + kValueOffset);
+    ++(*entry_num);
+    return true;
+  } else {
+    return false;
+  }
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+bool FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::getNextEntryForKey(const TypedValue &key,
+                         const std::size_t hash_code,
+                         const uint8_t **value,
+                         std::size_t *entry_num) const {
+  DEBUG_ASSERT(this->key_types_.size() == 1);
+  DEBUG_ASSERT(key.isPlausibleInstanceOf(this->key_types_.front()->getSignature()));
+
+  if (*entry_num == 0) {
+    *entry_num = slots_[hash_code % header_->num_slots].load(std::memory_order_relaxed);
+  } else if (*entry_num == std::numeric_limits<std::size_t>::max()) {
+    return false;
+  }
+
+  while (*entry_num != 0) {
+    DEBUG_ASSERT(*entry_num != std::numeric_limits<std::size_t>::max());
+    const char *bucket = static_cast<const char*>(buckets_) + (*entry_num - 1) * bucket_size_;
+    *entry_num = reinterpret_cast<const std::atomic<std::size_t>*>(bucket)->load(std::memory_order_relaxed);
+    const std::size_t bucket_hash = *reinterpret_cast<const std::size_t*>(
+        bucket + sizeof(std::atomic<std::size_t>));
+    if ((bucket_hash == hash_code) && key_manager_.scalarKeyCollisionCheck(key, bucket)) {
+      // Match located.
+      *value = reinterpret_cast<const uint8_t*>(bucket + kValueOffset);
+      if (*entry_num == 0) {
+        // If this is the last bucket in the chain, prevent the next call from
+        // starting over again.
+        *entry_num = std::numeric_limits<std::size_t>::max();
+      }
+      return true;
+    }
+  }
+
+  // Reached the end of the chain.
+  return false;
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+bool FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::getNextEntryForCompositeKey(const std::vector<TypedValue> &key,
+                                  const std::size_t hash_code,
+                                  const uint8_t **value,
+                                  std::size_t *entry_num) const {
+  DEBUG_ASSERT(this->key_types_.size() == key.size());
+
+  if (*entry_num == 0) {
+    *entry_num = slots_[hash_code % header_->num_slots].load(std::memory_order_relaxed);
+  } else if (*entry_num == std::numeric_limits<std::size_t>::max()) {
+    return false;
+  }
+
+  while (*entry_num != 0) {
+    DEBUG_ASSERT(*entry_num != std::numeric_limits<std::size_t>::max());
+    const char *bucket = static_cast<const char*>(buckets_) + (*entry_num - 1) * bucket_size_;
+    *entry_num = reinterpret_cast<const std::atomic<std::size_t>*>(bucket)->load(std::memory_order_relaxed);
+    const std::size_t bucket_hash = *reinterpret_cast<const std::size_t*>(
+        bucket + sizeof(std::atomic<std::size_t>));
+    if ((bucket_hash == hash_code) && key_manager_.compositeKeyCollisionCheck(key, bucket)) {
+      // Match located.
+      *value = reinterpret_cast<const uint8_t*>(bucket + kValueOffset);
+      if (*entry_num == 0) {
+        // If this is the last bucket in the chain, prevent the next call from
+        // starting over again.
+        *entry_num = std::numeric_limits<std::size_t>::max();
+      }
+      return true;
+    }
+  }
+
+  // Reached the end of the chain.
+  return false;
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+bool FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::hasKey(const TypedValue &key) const {
+  DEBUG_ASSERT(this->key_types_.size() == 1);
+  DEBUG_ASSERT(key.isPlausibleInstanceOf(this->key_types_.front()->getSignature()));
+
+  const std::size_t hash_code = key.getHash();
+  std::size_t bucket_ref = slots_[hash_code % header_->num_slots].load(std::memory_order_relaxed);
+  while (bucket_ref != 0) {
+    DEBUG_ASSERT(bucket_ref != std::numeric_limits<std::size_t>::max());
+    const char *bucket = static_cast<const char*>(buckets_) + (bucket_ref - 1) * bucket_size_;
+    const std::size_t bucket_hash = *reinterpret_cast<const std::size_t*>(
+        bucket + sizeof(std::atomic<std::size_t>));
+    if ((bucket_hash == hash_code) && key_manager_.scalarKeyCollisionCheck(key, bucket)) {
+      // Find a match.
+      return true;
+    }
+    bucket_ref = reinterpret_cast<const std::atomic<std::size_t>*>(bucket)->load(std::memory_order_relaxed);
+  }
+  return false;
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+bool FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::hasCompositeKey(const std::vector<TypedValue> &key) const {
+  DEBUG_ASSERT(this->key_types_.size() == key.size());
+
+  const std::size_t hash_code = this->hashCompositeKey(key);
+  std::size_t bucket_ref = slots_[hash_code % header_->num_slots].load(std::memory_order_relaxed);
+  while (bucket_ref != 0) {
+    DEBUG_ASSERT(bucket_ref != std::numeric_limits<std::size_t>::max());
+    const char *bucket = static_cast<const char*>(buckets_) + (bucket_ref - 1) * bucket_size_;
+    const std::size_t bucket_hash = *reinterpret_cast<const std::size_t*>(
+        bucket + sizeof(std::atomic<std::size_t>));
+    if ((bucket_hash == hash_code) && key_manager_.compositeKeyCollisionCheck(key, bucket)) {
+      // Find a match.
+      return true;
+    }
+    bucket_ref = reinterpret_cast<const std::atomic<std::size_t>*>(bucket)->load(std::memory_order_relaxed);
+  }
+  return false;
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+void FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::resize(const std::size_t extra_buckets,
+             const std::size_t extra_variable_storage,
+             const std::size_t retry_num) {
+  DEBUG_ASSERT(resizable);
+
+  // A retry should never be necessary with this implementation of HashTable.
+  // Separate chaining ensures that any resized hash table with more buckets
+  // than the original table will be able to hold more entries than the
+  // original.
+  DEBUG_ASSERT(retry_num == 0);
+
+  SpinSharedMutexExclusiveLock<true> write_lock(this->resize_shared_mutex_);
+
+  // Recheck whether the hash table is still full. Note that multiple threads
+  // might wait to rebuild this hash table simultaneously. Only the first one
+  // should do the rebuild.
+  if (!isFull(extra_variable_storage)) {
+    return;
+  }
+
+  // Approximately double the number of buckets and slots.
+  //
+  // TODO(chasseur): It may be worth it to more than double the number of
+  // buckets here so that we can maintain a good, sparse fill factor for a
+  // longer time as more values are inserted. Such behavior should take into
+  // account kHashTableLoadFactor.
+  std::size_t resized_num_slots = get_next_prime_number(
+      (header_->num_buckets + extra_buckets / 2) * kHashTableLoadFactor * 2);
+  std::size_t variable_storage_required
+      = (resized_num_slots / kHashTableLoadFactor) * key_manager_.getEstimatedVariableKeySize();
+  const std::size_t original_variable_storage_used
+      = header_->variable_length_bytes_allocated.load(std::memory_order_relaxed);
+  // If this resize was triggered by a too-large variable-length key, bump up
+  // the variable-length storage requirement.
+  if ((extra_variable_storage > 0)
+      && (extra_variable_storage + original_variable_storage_used
+          > key_manager_.getVariableLengthKeyStorageSize())) {
+    variable_storage_required += extra_variable_storage;
+  }
+
+  const std::size_t resized_memory_required
+      = sizeof(Header)
+        + resized_num_slots * sizeof(std::atomic<std::size_t>)
+        + (resized_num_slots / kHashTableLoadFactor) * bucket_size_
+        + variable_storage_required;
+  const std::size_t resized_storage_slots
+      = this->storage_manager_->SlotsNeededForBytes(resized_memory_required);
+  if (resized_storage_slots == 0) {
+    FATAL_ERROR("Storage requirement for resized SeparateChainingHashTable "
+                "exceeds maximum allocation size.");
+  }
+
+  // Get a new StorageBlob to hold the resized hash table.
+  const block_id resized_blob_id = this->storage_manager_->createBlob(resized_storage_slots);
+  MutableBlobReference resized_blob = this->storage_manager_->getBlobMutable(resized_blob_id);
+
+  // Locate data structures inside the new StorageBlob.
+  void *aligned_memory_start = resized_blob->getMemoryMutable();
+  std::size_t available_memory = resized_storage_slots * kSlotSizeBytes;
+  if (align(alignof(Header),
+            sizeof(Header),
+            aligned_memory_start,
+            available_memory)
+          == nullptr) {
+    // Should be impossible, as noted in constructor.
+    FATAL_ERROR("StorageBlob used to hold resized SeparateChainingHashTable "
+                "is too small to meet alignment requirements of "
+                "LinearOpenAddressingHashTable::Header.");
+  } else if (aligned_memory_start != resized_blob->getMemoryMutable()) {
+    // Again, should be impossible.
+    DEV_WARNING("In SeparateChainingHashTable::resize(), StorageBlob "
+                << "memory adjusted by "
+                << (resized_num_slots * kSlotSizeBytes - available_memory)
+                << " bytes to meet alignment requirement for "
+                << "LinearOpenAddressingHashTable::Header.");
+  }
+
+  Header *resized_header = static_cast<Header*>(aligned_memory_start);
+  aligned_memory_start = static_cast<char*>(aligned_memory_start) + sizeof(Header);
+  available_memory -= sizeof(Header);
+
+  // As in constructor, recompute the number of slots and buckets using the
+  // actual available memory.
+  std::size_t resized_num_buckets
+      = (available_memory - extra_variable_storage)
+        / (kHashTableLoadFactor * sizeof(std::atomic<std::size_t>)
+           + bucket_size_
+           + key_manager_.getEstimatedVariableKeySize());
+  resized_num_slots = get_previous_prime_number(resized_num_buckets * kHashTableLoadFactor);
+  resized_num_buckets = resized_num_slots / kHashTableLoadFactor;
+
+  // Locate slot array.
+  std::atomic<std::size_t> *resized_slots = static_cast<std::atomic<std::size_t>*>(aligned_memory_start);
+  aligned_memory_start = static_cast<char*>(aligned_memory_start)
+                         + sizeof(std::atomic<std::size_t>) * resized_num_slots;
+  available_memory -= sizeof(std::atomic<std::size_t>) * resized_num_slots;
+
+  // As in constructor, we will be extra paranoid and use align() to locate the
+  // start of the array of buckets, as well.
+  void *resized_buckets = aligned_memory_start;
+  if (align(kBucketAlignment,
+            bucket_size_,
+            resized_buckets,
+            available_memory)
+          == nullptr) {
+    FATAL_ERROR("StorageBlob used to hold resized SeparateChainingHashTable "
+                "is too small to meet alignment requirements of buckets.");
+  } else if (resized_buckets != aligned_memory_start) {
+    DEV_WARNING("Bucket array start position adjusted to meet alignment "
+                "requirement for SeparateChainingHashTable's value type.");
+    if (resized_num_buckets * bucket_size_ + variable_storage_required > available_memory) {
+      --resized_num_buckets;
+    }
+  }
+  aligned_memory_start = static_cast<char*>(aligned_memory_start)
+                         + resized_num_buckets * bucket_size_;
+  available_memory -= resized_num_buckets * bucket_size_;
+
+  void *resized_variable_length_key_storage = aligned_memory_start;
+  const std::size_t resized_variable_length_key_storage_size = available_memory;
+
+  const std::size_t original_buckets_used = header_->buckets_allocated.load(std::memory_order_relaxed);
+
+  // Initialize the header.
+  resized_header->num_slots = resized_num_slots;
+  resized_header->num_buckets = resized_num_buckets;
+  resized_header->buckets_allocated.store(original_buckets_used, std::memory_order_relaxed);
+  resized_header->variable_length_bytes_allocated.store(
+      original_variable_storage_used,
+      std::memory_order_relaxed);
+
+  // Bulk-copy buckets. This is safe because:
+  //     1. The "next" pointers will be adjusted when rebuilding chains below.
+  //     2. The hash codes will stay the same.
+  //     3. For key components:
+  //       a. Inline keys will stay exactly the same.
+  //       b. Offsets into variable-length storage will remain valid, because
+  //          we also do a byte-for-byte copy of variable-length storage below.
+  //       c. Absolute external pointers will still point to the same address.
+  //       d. Relative pointers are not used with resizable hash tables.
+  //     4. If values are not trivially copyable, then we invoke ValueT's copy
+  //        or move constructor with placement new.
+  std::memcpy(resized_buckets, buckets_, original_buckets_used * bucket_size_);
+
+  // TODO(chasseur): std::is_trivially_copyable is not yet implemented in
+  // GCC 4.8.3, so we assume we need to invoke ValueT's copy or move
+  // constructor, even though the plain memcpy above could suffice for many
+  // possible ValueTs.
+  void *current_value_original = static_cast<char*>(buckets_) + kValueOffset;
+  void *current_value_resized = static_cast<char*>(resized_buckets) + kValueOffset;
+  for (std::size_t bucket_num = 0; bucket_num < original_buckets_used; ++bucket_num) {
+    // Use a move constructor if available to avoid a deep-copy, since resizes
+    // always succeed.
+    new (current_value_resized) uint8_t(std::move(*static_cast<uint8_t*>(current_value_original)));
+    current_value_original = static_cast<char*>(current_value_original) + bucket_size_;
+    current_value_resized = static_cast<char*>(current_value_resized) + bucket_size_;
+  }
+
+  // Copy over variable-length key components, if any.
+  if (original_variable_storage_used > 0) {
+    DEBUG_ASSERT(original_variable_storage_used
+                 == key_manager_.getNextVariableLengthKeyOffset());
+    DEBUG_ASSERT(original_variable_storage_used <= resized_variable_length_key_storage_size);
+    std::memcpy(resized_variable_length_key_storage,
+                key_manager_.getVariableLengthKeyStorage(),
+                original_variable_storage_used);
+  }
+
+  // Destroy values in the original hash table, if neccesary,
+  DestroyValues(buckets_,
+                original_buckets_used,
+                bucket_size_);
+
+  // Make resized structures active.
+  std::swap(this->blob_, resized_blob);
+  header_ = resized_header;
+  slots_ = resized_slots;
+  buckets_ = resized_buckets;
+  key_manager_.setVariableLengthStorageInfo(
+      resized_variable_length_key_storage,
+      resized_variable_length_key_storage_size,
+      &(resized_header->variable_length_bytes_allocated));
+
+  // Drop the old blob.
+  const block_id old_blob_id = resized_blob->getID();
+  resized_blob.release();
+  this->storage_manager_->deleteBlockOrBlobFile(old_blob_id);
+
+  // Rebuild chains.
+  void *current_bucket = buckets_;
+  for (std::size_t bucket_num = 0; bucket_num < original_buckets_used; ++bucket_num) {
+    std::atomic<std::size_t> *next_ptr
+        = static_cast<std::atomic<std::size_t>*>(current_bucket);
+    const std::size_t hash_code = *reinterpret_cast<const std::size_t*>(
+        static_cast<const char*>(current_bucket) + sizeof(std::atomic<std::size_t>));
+
+    const std::size_t slot_number = hash_code % header_->num_slots;
+    std::size_t slot_ptr_value = 0;
+    if (slots_[slot_number].compare_exchange_strong(slot_ptr_value,
+                                                    bucket_num + 1,
+                                                    std::memory_order_relaxed)) {
+      // This bucket is the first in the chain for this block, so reset its
+      // next pointer to 0.
+      next_ptr->store(0, std::memory_order_relaxed);
+    } else {
+      // A chain already exists starting from this slot, so put this bucket at
+      // the head.
+      next_ptr->store(slot_ptr_value, std::memory_order_relaxed);
+      slots_[slot_number].store(bucket_num + 1, std::memory_order_relaxed);
+    }
+    current_bucket = static_cast<char*>(current_bucket) + bucket_size_;
+  }
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+bool FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::preallocateForBulkInsert(const std::size_t total_entries,
+                               const std::size_t total_variable_key_size,
+                               HashTablePreallocationState *prealloc_state) {
+  DEBUG_ASSERT(allow_duplicate_keys);
+  if (!key_manager_.allocateVariableLengthKeyStorage(total_variable_key_size)) {
+    return false;
+  }
+
+  // We use load then compare-exchange here instead of simply fetch-add,
+  // because if multiple threads are simultaneously trying to allocate more
+  // than one bucket and exceed 'header_->num_buckets', their respective
+  // rollbacks might happen in such an order that some bucket ranges get
+  // skipped, while others might get double-allocated later.
+  std::size_t original_buckets_allocated = header_->buckets_allocated.load(std::memory_order_relaxed);
+  std::size_t buckets_post_allocation = original_buckets_allocated + total_entries;
+  while ((buckets_post_allocation <= header_->num_buckets)
+         && !header_->buckets_allocated.compare_exchange_weak(original_buckets_allocated,
+                                                              buckets_post_allocation,
+                                                              std::memory_order_relaxed)) {
+    buckets_post_allocation = original_buckets_allocated + total_entries;
+  }
+
+  if (buckets_post_allocation > header_->num_buckets) {
+    key_manager_.deallocateVariableLengthKeyStorage(total_variable_key_size);
+    return false;
+  }
+
+  prealloc_state->bucket_position = original_buckets_allocated;
+  if (total_variable_key_size != 0) {
+    prealloc_state->variable_length_key_position
+        = key_manager_.incrementNextVariableLengthKeyOffset(total_variable_key_size);
+  }
+  return true;
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+void FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::DestroyValues(void *hash_buckets,
+                    const std::size_t num_buckets,
+                    const std::size_t bucket_size) {
+  if (!std::is_trivially_destructible<uint8_t>::value) {
+    void *value_ptr = static_cast<char*>(hash_buckets) + kValueOffset;
+    for (std::size_t bucket_num = 0;
+         bucket_num < num_buckets;
+         ++bucket_num) {
+     static_cast<uint8_t*>(value_ptr)->~uint8_t();
+      value_ptr = static_cast<char*>(value_ptr) + bucket_size;
+    }
+  }
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+inline bool FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::locateBucketForInsertion(const std::size_t hash_code,
+                               const std::size_t variable_key_allocation_required,
+                               void **bucket,
+                               std::atomic<std::size_t> **pending_chain_ptr,
+                               std::size_t *pending_chain_ptr_finish_value,
+                               HashTablePreallocationState *prealloc_state) {
+  DEBUG_ASSERT((prealloc_state == nullptr) || allow_duplicate_keys);
+  if (*bucket == nullptr) {
+    *pending_chain_ptr = &(slots_[hash_code % header_->num_slots]);
+  } else {
+    *pending_chain_ptr = static_cast<std::atomic<std::size_t>*>(*bucket);
+  }
+  for (;;) {
+    std::size_t existing_chain_ptr = 0;
+    if ((*pending_chain_ptr)->compare_exchange_strong(existing_chain_ptr,
+                                                      std::numeric_limits<std::size_t>::max(),
+                                                      std::memory_order_acq_rel)) {
+      // Got to the end of the chain. Allocate a new bucket.
+
+      // First, allocate variable-length key storage, if needed (i.e. if this
+      // is an upsert and we didn't allocate up-front).
+      if ((prealloc_state == nullptr)
+          && !key_manager_.allocateVariableLengthKeyStorage(variable_key_allocation_required)) {
+        // Ran out of variable-length storage.
+        (*pending_chain_ptr)->store(0, std::memory_order_release);
+        *bucket = nullptr;
+        return false;
+      }
+
+      const std::size_t allocated_bucket_num
+          = (prealloc_state == nullptr)
+              ? header_->buckets_allocated.fetch_add(1, std::memory_order_relaxed)
+              : (prealloc_state->bucket_position)++;
+      if (allocated_bucket_num >= header_->num_buckets) {
+        // Ran out of buckets.
+        DEBUG_ASSERT(prealloc_state == nullptr);
+        header_->buckets_allocated.fetch_sub(1, std::memory_order_relaxed);
+        (*pending_chain_ptr)->store(0, std::memory_order_release);
+        *bucket = nullptr;
+        return false;
+      } else {
+        *bucket = static_cast<char*>(buckets_) + allocated_bucket_num * bucket_size_;
+        *pending_chain_ptr_finish_value = allocated_bucket_num + 1;
+        return true;
+      }
+    }
+    // Spin until the real "next" pointer is available.
+    while (existing_chain_ptr == std::numeric_limits<std::size_t>::max()) {
+      existing_chain_ptr = (*pending_chain_ptr)->load(std::memory_order_acquire);
+    }
+    if (existing_chain_ptr == 0) {
+      // Other thread had to roll back, so try again.
+      continue;
+    }
+    // Chase the next pointer.
+    *bucket = static_cast<char*>(buckets_) + (existing_chain_ptr - 1) * bucket_size_;
+    *pending_chain_ptr = static_cast<std::atomic<std::size_t>*>(*bucket);
+    if (!allow_duplicate_keys) {
+      const std::size_t hash_in_bucket
+          = *reinterpret_cast<const std::size_t*>(static_cast<const char*>(*bucket)
+                                                  + sizeof(std::atomic<std::size_t>));
+      if (hash_in_bucket == hash_code) {
+        return false;
+      }
+    }
+  }
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+inline void FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::writeScalarKeyToBucket(const TypedValue &key,
+                             const std::size_t hash_code,
+                             void *bucket,
+                             HashTablePreallocationState *prealloc_state) {
+  *reinterpret_cast<std::size_t*>(static_cast<char*>(bucket) + sizeof(std::atomic<std::size_t>))
+      = hash_code;
+  key_manager_.writeKeyComponentToBucket(key, 0, bucket, prealloc_state);
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+inline void FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::writeCompositeKeyToBucket(const std::vector<TypedValue> &key,
+                                const std::size_t hash_code,
+                                void *bucket,
+                                HashTablePreallocationState *prealloc_state) {
+  DEBUG_ASSERT(key.size() == this->key_types_.size());
+  *reinterpret_cast<std::size_t*>(static_cast<char*>(bucket) + sizeof(std::atomic<std::size_t>))
+      = hash_code;
+  for (std::size_t idx = 0;
+       idx < this->key_types_.size();
+       ++idx) {
+    key_manager_.writeKeyComponentToBucket(key[idx], idx, bucket, prealloc_state);
+  }
+}
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+bool FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::isFull(const std::size_t extra_variable_storage) const {
+  if (header_->buckets_allocated.load(std::memory_order_relaxed) >= header_->num_buckets) {
+    // All buckets are allocated.
+    return true;
+  }
+
+  if (extra_variable_storage > 0) {
+    if (extra_variable_storage
+            + header_->variable_length_bytes_allocated.load(std::memory_order_relaxed)
+        > key_manager_.getVariableLengthKeyStorageSize()) {
+      // Not enough variable-length key storage space.
+      return true;
+    }
+  }
+
+  return false;
+}
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_STORAGE_SEPARATE_CHAINING_HASH_TABLE_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/storage/HashTableBase.hpp
----------------------------------------------------------------------
diff --git a/storage/HashTableBase.hpp b/storage/HashTableBase.hpp
index c37c1ec..ebdf464 100644
--- a/storage/HashTableBase.hpp
+++ b/storage/HashTableBase.hpp
@@ -63,7 +63,7 @@ class HashTableBase {
  public:
   virtual ~HashTableBase() {
   }
-
+virtual size_t get_buckets_allocated() const {return 0;}
  protected:
   HashTableBase() {
   }

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/storage/HashTablePool.hpp
----------------------------------------------------------------------
diff --git a/storage/HashTablePool.hpp b/storage/HashTablePool.hpp
index c16d0f1..909b233 100644
--- a/storage/HashTablePool.hpp
+++ b/storage/HashTablePool.hpp
@@ -25,6 +25,8 @@
 
 #include "expressions/aggregation/AggregationHandle.hpp"
 #include "storage/HashTableBase.hpp"
+#include "storage/FastHashTable.hpp"
+#include "storage/FastHashTableFactory.hpp"
 #include "threading/SpinMutex.hpp"
 #include "utility/Macros.hpp"
 #include "utility/StringUtil.hpp"
@@ -79,6 +81,19 @@ class HashTablePool {
         agg_handle_(DCHECK_NOTNULL(agg_handle)),
         storage_manager_(DCHECK_NOTNULL(storage_manager)) {}
 
+  HashTablePool(const std::size_t estimated_num_entries,
+                const HashTableImplType hash_table_impl_type,
+                const std::vector<const Type *> &group_by_types,
+                const std::vector<std::size_t> &payload_sizes,
+                const std::vector<AggregationHandle *> &handles,
+                StorageManager *storage_manager)
+      : estimated_num_entries_(reduceEstimatedCardinality(estimated_num_entries)),
+        hash_table_impl_type_(hash_table_impl_type),
+        group_by_types_(group_by_types),
+        payload_sizes_(payload_sizes),
+        handles_(handles),
+        storage_manager_(DCHECK_NOTNULL(storage_manager)) {}
+
   /**
    * @brief Check out a hash table for insertion.
    *
@@ -98,6 +113,20 @@ class HashTablePool {
     return createNewHashTable();
   }
 
+  AggregationStateHashTableBase* getHashTableFast() {
+    {
+      SpinMutexLock lock(mutex_);
+      if (!hash_tables_.empty()) {
+        std::unique_ptr<AggregationStateHashTableBase> ret_hash_table(
+            std::move(hash_tables_.back()));
+        hash_tables_.pop_back();
+        DCHECK(ret_hash_table != nullptr);
+        return ret_hash_table.release();
+      }
+    }
+    return createNewHashTableFast();
+  }
+
   /**
    * @brief Return a previously checked out hash table.
    *
@@ -132,6 +161,16 @@ class HashTablePool {
                                                storage_manager_);
   }
 
+  AggregationStateHashTableBase* createNewHashTableFast() {
+    return AggregationStateFastHashTableFactory::CreateResizable(
+                hash_table_impl_type_,
+                group_by_types_,
+                estimated_num_entries_,
+                payload_sizes_,
+                handles_,
+                storage_manager_);
+  }
+
   inline std::size_t reduceEstimatedCardinality(
       const std::size_t original_estimate) const {
     if (original_estimate < kEstimateReductionFactor) {
@@ -151,7 +190,10 @@ class HashTablePool {
 
   const std::vector<const Type *> group_by_types_;
 
+  std::vector<std::size_t> payload_sizes_;
+
   AggregationHandle *agg_handle_;
+  const std::vector<AggregationHandle *> handles_;
   StorageManager *storage_manager_;
 
   SpinMutex mutex_;

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/storage/StorageBlock.cpp
----------------------------------------------------------------------
diff --git a/storage/StorageBlock.cpp b/storage/StorageBlock.cpp
index fdd438d..b9faa2a 100644
--- a/storage/StorageBlock.cpp
+++ b/storage/StorageBlock.cpp
@@ -38,6 +38,7 @@
 #include "storage/CompressedPackedRowStoreTupleStorageSubBlock.hpp"
 #include "storage/CountedReference.hpp"
 #include "storage/HashTableBase.hpp"
+#include "storage/FastHashTable.hpp"
 #include "storage/IndexSubBlock.hpp"
 #include "storage/InsertDestinationInterface.hpp"
 #include "storage/PackedRowStoreTupleStorageSubBlock.hpp"
@@ -494,6 +495,92 @@ void StorageBlock::aggregateGroupBy(
                                              hash_table);
 }
 
+
+void StorageBlock::aggregateGroupByFast(
+    const std::vector<std::vector<std::unique_ptr<const Scalar>>> &arguments,
+    const std::vector<std::unique_ptr<const Scalar>> &group_by,
+    const Predicate *predicate,
+    AggregationStateHashTableBase *hash_table,
+    std::unique_ptr<TupleIdSequence> *reuse_matches,
+    std::vector<std::unique_ptr<ColumnVector>> *reuse_group_by_vectors) const {
+  DCHECK_GT(group_by.size(), 0u)
+      << "Called aggregateGroupBy() with zero GROUP BY expressions";
+
+  SubBlocksReference sub_blocks_ref(*tuple_store_,
+                                    indices_,
+                                    indices_consistent_);
+
+  // IDs of 'arguments' as attributes in the ValueAccessor we create below.
+  std::vector<attribute_id> arg_ids;
+  std::vector<std::vector<attribute_id>> argument_ids;
+
+  // IDs of GROUP BY key element(s) in the ValueAccessor we create below.
+  std::vector<attribute_id> key_ids;
+
+  // An intermediate ValueAccessor that stores the materialized 'arguments' for
+  // this aggregate, as well as the GROUP BY expression values.
+  ColumnVectorsValueAccessor temp_result;
+  {
+    std::unique_ptr<ValueAccessor> accessor;
+    if (predicate) {
+      if (!*reuse_matches) {
+        // If there is a filter predicate that hasn't already been evaluated,
+        // evaluate it now and save the results for other aggregates on this
+        // same block.
+        reuse_matches->reset(getMatchesForPredicate(predicate));
+      }
+
+      // Create a filtered ValueAccessor that only iterates over predicate
+      // matches.
+      accessor.reset(tuple_store_->createValueAccessor(reuse_matches->get()));
+    } else {
+      // Create a ValueAccessor that iterates over all tuples in this block
+      accessor.reset(tuple_store_->createValueAccessor());
+    }
+
+    attribute_id attr_id = 0;
+
+    // First, put GROUP BY keys into 'temp_result'.
+    if (reuse_group_by_vectors->empty()) {
+      // Compute GROUP BY values from group_by Scalars, and store them in
+      // reuse_group_by_vectors for reuse by other aggregates on this same
+      // block.
+      reuse_group_by_vectors->reserve(group_by.size());
+      for (const std::unique_ptr<const Scalar> &group_by_element : group_by) {
+        reuse_group_by_vectors->emplace_back(
+            group_by_element->getAllValues(accessor.get(), &sub_blocks_ref));
+        temp_result.addColumn(reuse_group_by_vectors->back().get(), false);
+        key_ids.push_back(attr_id++);
+      }
+    } else {
+      // Reuse precomputed GROUP BY values from reuse_group_by_vectors.
+      DCHECK_EQ(group_by.size(), reuse_group_by_vectors->size())
+          << "Wrong number of reuse_group_by_vectors";
+      for (const std::unique_ptr<ColumnVector> &reuse_cv : *reuse_group_by_vectors) {
+        temp_result.addColumn(reuse_cv.get(), false);
+        key_ids.push_back(attr_id++);
+      }
+    }
+
+    // Compute argument vectors and add them to 'temp_result'.
+    for (const std::vector<std::unique_ptr<const Scalar>> &argument : arguments) {
+        arg_ids.clear();
+        for (const std::unique_ptr<const Scalar> &args : argument) {
+          temp_result.addColumn(args->getAllValues(accessor.get(), &sub_blocks_ref));
+          arg_ids.push_back(attr_id++);
+        }
+        argument_ids.push_back(arg_ids);
+     }
+  }
+
+  static_cast<AggregationStateFastHashTable *>(hash_table)->upsertValueAccessorCompositeKeyFast(
+      argument_ids,
+      &temp_result,
+      key_ids,
+      true);
+}
+
+
 void StorageBlock::aggregateDistinct(
     const AggregationHandle &handle,
     const std::vector<std::unique_ptr<const Scalar>> &arguments,
@@ -582,7 +669,6 @@ void StorageBlock::aggregateDistinct(
       &temp_result, key_ids, distinctify_hash_table);
 }
 
-
 // TODO(chasseur): Vectorization for updates.
 StorageBlock::UpdateResult StorageBlock::update(
     const unordered_map<attribute_id, unique_ptr<const Scalar>> &assignments,



[18/30] incubator-quickstep git commit: Added README for types module.

Posted by ra...@apache.org.
Added README for types module.


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

Branch: refs/heads/quickstep-28-29
Commit: 33554c3edcac6becb84bfcdcdb8a60b9dd6a3f0b
Parents: 7415ee8
Author: Craig Chasseur <sp...@gmail.com>
Authored: Wed Jul 27 20:01:53 2016 -0700
Committer: Craig Chasseur <sp...@gmail.com>
Committed: Wed Jul 27 20:01:53 2016 -0700

----------------------------------------------------------------------
 types/README.md | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 102 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/33554c3e/types/README.md
----------------------------------------------------------------------
diff --git a/types/README.md b/types/README.md
new file mode 100644
index 0000000..baf01aa
--- /dev/null
+++ b/types/README.md
@@ -0,0 +1,102 @@
+# The Quickstep Type System
+
+The types module is used across Quickstep and handles details of how date values
+are stored and represented, how they are parsed from and printed to
+human-readable text, and low-level operations on values that form the building
+blocks for more complex [expressions](../expressions).
+
+## The Type Class
+
+Every distinct concrete type in Quickstep is represented by a single object of
+a class derived from the base `quickstep::Type` class. All types have some
+common properties, including the following:
+
+  * A `TypeID` - an enum identifying the type, e.g. `kInt` for 32-bit integers,
+    or `kVarChar` for variable-length strings.
+  * Nullability - whether the type allows NULL values. All types have both a
+    nullable and a non-nullable flavor, except for NullType, a special type that
+    can ONLY store NULLs and has no non-nullable version.
+  * Storage size - minimum and maximum byte length. For fixed-length types like
+    basic numeric types and fixed length `CHAR(X)` strings, these lengths are
+    the same. For variable-length types like `VARCHAR(X)`, they can be
+    different (and the `Type` class has a method `estimateAverageByteLength()`
+    that can be used to make educated guesses when allocating storage). Note
+    that storage requirements really only apply to uncompressed, non-NULL
+    values. The actual bytes needed to store the values in the
+    [storage system](../storage) may be different if
+    [compression](../compression) is used, and some storage formats might store
+    NULLs differently.
+
+Some categories of types have additional properties (e.g. `CharType` and
+`VarCharType` also have a length parameter that indicates the maximum length of
+string that can be stored).
+
+### Getting a Type
+
+Each distinct, concrete Type is represented by a single object in the entire
+Quickstep process. To actually get a reference to usable `Type`, most code will
+go through the `TypeFactory`. `TypeFactory` provides static methods to access
+specific types by `TypeID` and other parameters. It can also deserialize a type
+from its protobuf representation (a `quickstep::serialization::Type` message).
+Finally, it also provides methods that can determine a `Type` that two different
+types can be cast to.
+
+### More on the `Type` Interface
+
+In addition to methods that allow inspection of a type's properties (e.g. those
+listed above), the Type class defines an interface with useful functionality
+common to all types:
+
+  * Serialization (of the type itself) - the `getProto()` method produces a
+    protobuf message that can be serialized and deserialized and later passed to
+    the TypeFactory to get back the same type.
+  * Relationship to other types - `equals()` determines if two types are exactly
+    the same, while `isCoercibleFrom()` determines if it is possible to convert
+    from another type to a given type (e.g. with a `CAST`), and
+    `isSafelyCoercibleFrom()` determines if such a conversion can always be done
+    without loss of precision.
+  * Printing to human-readable format - `printValueToString()` and
+    `printValueToFile()` can print out values of a type (see `TypedValue` below)
+    in human-readable format.
+  * Parsing from human-readable format - Similarly, `parseValueFromString()`
+    produces a `TypedValue` that is parsed from a string in human-readable
+    format.
+  * Making values - `makeValue()` creates a `TypedValue` from a bare pointer to
+    a value's representation in storage. For nullable types, `makeNullValue()`
+    makes a NULL value, and for numeric types, `makeZeroValue()` makes a zero
+    of that type.
+  * Coercing values - `coerceValue()` takes a value of another type and converts
+    it to the given type (e.g. as part of a `CAST`).
+
+## The TypedValue Class
+
+An individual typed value in Quickstep is represented by an instance of the
+`TypedValue` class. TypedValues can be created by methods of the `Type` class,
+by operation and expression classes that operate on values, or simply by calling
+one of several constructors provided in the class itself for convenience.
+TypedValues have C++ value semantics (i.e. they are copyable, assignable, and
+movable). A TypedValue may own its own data, or it may be a lightweight
+reference to data that is stored elsewhere in memory (this can be checked with
+`isReference()`, and any reference can be upgraded to own its own data copy by
+calling `ensureNotReference()`).
+
+Here are some of the things you can do with a TypedValue:
+
+  * NULL checks - calling `isNull()` determines if the TypedValue represents a
+    NULL. Several methods of TypedValue are usable only for non-NULL values, so
+    it is often important to check this first if in doubt.
+  * Access to underlying data - `getDataPtr()` returns an untyped `void*`
+    pointer to the underlying data, and `getDataSize()` returns the size of the
+    underlying data in bytes. Depending on the type of the value, the templated
+    method `getLiteral()` can be used to get the underlying data as a literal
+    scalar, or `getAsciiStringLength()` can be used to get the string length of
+    a `CHAR(X)` or `VARCHAR(X)` without counting null-terminators.
+  * Hashing - `getHash()` returns a hash of the value, which is suitable for
+    use in the HashTables of the [storage system](../storage), or in generic
+    hash-based C++ containers. `fastEqualCheck()` is provided to quickly check
+    whether two TypedValues of the same type (e.g. in the same hash table) are
+    actually equal.
+  * Serialization/Deserialization - `getProto()` serializes a TypedValue to a
+    `serialization::TypedValue` protobuf. The static method `ProtoIsValid()`
+    checks whether a serialized TypedValue is valid, and
+    `ReconstructFromProto()` rebuilds a TypedValue from its serialized form.


[05/30] incubator-quickstep git commit: Introduced QueryManagerBase, and renamed QueryManagerSingleNode.

Posted by ra...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b4e25edc/query_execution/tests/QueryManagerSingleNode_unittest.cpp
----------------------------------------------------------------------
diff --git a/query_execution/tests/QueryManagerSingleNode_unittest.cpp b/query_execution/tests/QueryManagerSingleNode_unittest.cpp
new file mode 100644
index 0000000..52cee20
--- /dev/null
+++ b/query_execution/tests/QueryManagerSingleNode_unittest.cpp
@@ -0,0 +1,942 @@
+/**
+ *   Copyright 2011-2015 Quickstep Technologies LLC.
+ *   Copyright 2015-2016 Pivotal Software, Inc.
+ *
+ *   Licensed 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.
+ **/
+
+#include <climits>
+#include <cstdlib>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "catalog/CatalogDatabase.hpp"
+#include "catalog/CatalogRelation.hpp"
+#include "catalog/CatalogTypedefs.hpp"
+#include "query_execution/QueryContext.hpp"
+#include "query_execution/QueryContext.pb.h"
+#include "query_execution/QueryExecutionMessages.pb.h"
+#include "query_execution/QueryExecutionState.hpp"
+#include "query_execution/QueryExecutionTypedefs.hpp"
+#include "query_execution/QueryManagerSingleNode.hpp"
+#include "query_execution/WorkOrdersContainer.hpp"
+#include "query_execution/WorkerDirectory.hpp"
+#include "query_execution/WorkerMessage.hpp"
+#include "query_optimizer/QueryHandle.hpp"
+#include "query_optimizer/QueryPlan.hpp"
+#include "relational_operators/RelationalOperator.hpp"
+#include "relational_operators/WorkOrder.hpp"
+#include "storage/InsertDestination.hpp"
+#include "storage/InsertDestination.pb.h"
+#include "storage/StorageBlock.hpp"
+#include "storage/StorageBlockInfo.hpp"
+#include "storage/StorageManager.hpp"
+#include "utility/DAG.hpp"
+#include "utility/Macros.hpp"
+
+#include "glog/logging.h"
+#include "gtest/gtest.h"
+
+#include "tmb/id_typedefs.h"
+#include "tmb/tagged_message.h"
+
+namespace tmb { class MessageBus; }
+
+using std::move;
+using std::unique_ptr;
+using std::vector;
+
+using tmb::client_id;
+
+namespace quickstep {
+
+class WorkOrderProtosContainer;
+
+class MockWorkOrder : public WorkOrder {
+ public:
+  explicit MockWorkOrder(const int op_index)
+      : WorkOrder(0), op_index_(op_index) {}
+
+  void execute() override {
+    VLOG(3) << "WorkOrder[" << op_index_ << "] executing.";
+  }
+
+  inline QueryPlan::DAGNodeIndex getOpIndex() const {
+    return op_index_;
+  }
+
+ private:
+  const QueryPlan::DAGNodeIndex op_index_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockWorkOrder);
+};
+
+class MockOperator: public RelationalOperator {
+ public:
+  enum function_name {
+    kFeedInputBlock = 0,
+    kFeedInputBlocks,
+    kDoneFeedingInputBlocks,
+    kGetAllWorkOrders
+  };
+
+  MockOperator(const bool produce_workorders,
+               const bool has_streaming_input,
+               const int max_getworkorder_iters = 1,
+               const int max_workorders = INT_MAX)
+      : RelationalOperator(0 /* Query Id */),
+        produce_workorders_(produce_workorders),
+        has_streaming_input_(has_streaming_input),
+        max_workorders_(max_workorders),
+        max_getworkorder_iters_(max_getworkorder_iters),
+        num_calls_get_workorders_(0),
+        num_workorders_generated_(0),
+        num_calls_feedblock_(0),
+        num_calls_feedblocks_(0),
+        num_calls_donefeedingblocks_(0) {
+  }
+
+#define MOCK_OP_LOG(x) VLOG(x) << "Op[" << op_index_ << "]: " << __func__ << ": "
+
+  // The methods below are used to check whether QueryManager calls the Relational
+  // operator, how many times it calls a particular method etc.
+  inline int getNumWorkOrders() const {
+    return num_workorders_generated_;
+  }
+
+  inline int getNumCalls(const function_name fname) const {
+    switch (fname) {
+      case kFeedInputBlock:
+        return num_calls_feedblock_;
+      case kFeedInputBlocks:
+        return num_calls_feedblocks_;
+      case kDoneFeedingInputBlocks:
+        return num_calls_donefeedingblocks_;
+      case kGetAllWorkOrders:
+        return num_calls_get_workorders_;
+      default:
+        return -1;
+    }
+  }
+
+  inline bool getBlockingDependenciesMet() const {
+    MOCK_OP_LOG(3) << "met.";
+    return blocking_dependencies_met_;
+  }
+
+  void setInsertDestinationID(const QueryContext::insert_destination_id insert_destination_index) {
+    insert_destination_index_ = insert_destination_index;
+  }
+
+  // Mock to trigger doneFeedingInputBlocks for the dependent operators
+  // in QueryManager::markOperatorFinished.
+  void setOutputRelationID(const relation_id rel_id) {
+    output_relation_id_ = rel_id;
+  }
+
+  // Override methods from the base class.
+  bool getAllWorkOrders(
+      WorkOrdersContainer *container,
+      QueryContext *query_context,
+      StorageManager *storage_manager,
+      const tmb::client_id foreman_client_id,
+      tmb::MessageBus *bus) override {
+    ++num_calls_get_workorders_;
+    if (produce_workorders_) {
+      if (has_streaming_input_) {
+        if ((num_calls_feedblock_ > 0 || num_calls_feedblocks_ > 0) && (num_workorders_generated_ < max_workorders_)) {
+          MOCK_OP_LOG(3) << "[stream] generate WorkOrder";
+          container->addNormalWorkOrder(new MockWorkOrder(op_index_), op_index_);
+          ++num_workorders_generated_;
+        }
+      } else {
+        if (blocking_dependencies_met_ && (num_workorders_generated_ < max_workorders_)) {
+          MOCK_OP_LOG(3) << "[static] generate WorkOrder";
+          container->addNormalWorkOrder(new MockWorkOrder(op_index_), op_index_);
+          ++num_workorders_generated_;
+        }
+      }
+    }
+    MOCK_OP_LOG(3) << "count(" << num_calls_get_workorders_ << ") "
+                   << "return(" << (num_calls_get_workorders_ == max_getworkorder_iters_) << ")";
+    return num_calls_get_workorders_ == max_getworkorder_iters_;
+  }
+
+  bool getAllWorkOrderProtos(WorkOrderProtosContainer *container) override {
+    return true;
+  }
+
+  void feedInputBlock(const block_id input_block_id,
+                      const relation_id input_relation_id) override {
+    ++num_calls_feedblock_;
+    MOCK_OP_LOG(3) << "count(" << num_calls_feedblock_ << ")";
+  }
+
+  void feedInputBlocks(const relation_id rel_id,
+                       std::vector<block_id> *partially_filled_blocks) override {
+    ++num_calls_feedblocks_;
+    MOCK_OP_LOG(3) << "count(" << num_calls_feedblocks_ << ")";
+  }
+
+  void doneFeedingInputBlocks(const relation_id rel_id) override {
+    ++num_calls_donefeedingblocks_;
+    MOCK_OP_LOG(3) << "count(" << num_calls_donefeedingblocks_ << ")";
+  }
+
+  QueryContext::insert_destination_id getInsertDestinationID() const override {
+    return insert_destination_index_;
+  }
+
+  const relation_id getOutputRelationID() const override {
+    return output_relation_id_;
+  }
+
+ private:
+  const bool produce_workorders_;
+  const bool has_streaming_input_;
+  const int max_workorders_;
+  const int max_getworkorder_iters_;
+
+  int num_calls_get_workorders_;
+  int num_workorders_generated_;
+  int num_calls_feedblock_;
+  int num_calls_feedblocks_;
+  int num_calls_donefeedingblocks_;
+
+  QueryContext::insert_destination_id insert_destination_index_ = QueryContext::kInvalidInsertDestinationId;
+
+  relation_id output_relation_id_ = -1;
+
+#undef MOCK_OP_LOG
+
+  DISALLOW_COPY_AND_ASSIGN(MockOperator);
+};
+
+
+class QueryManagerTest : public ::testing::Test {
+ protected:
+  virtual void SetUp() {
+    db_.reset(new CatalogDatabase(nullptr /* catalog */, "database"));
+    storage_manager_.reset(new StorageManager("./"));
+    bus_.Initialize();
+    query_handle_.reset(new QueryHandle(0));  // dummy query ID.
+    query_plan_ = query_handle_->getQueryPlanMutable();
+    query_handle_->getQueryContextProtoMutable()->set_query_id(query_handle_->query_id());
+  }
+
+  inline void constructQueryManager() {
+    query_manager_.reset(new QueryManagerSingleNode(
+        0, 1, query_handle_.get(), db_.get(), storage_manager_.get(), &bus_));
+  }
+
+  inline const int getNumWorkOrdersInExecution(const QueryPlan::DAGNodeIndex index) const {
+    return query_manager_->getQueryExecutionState().getNumQueuedWorkOrders(index);
+  }
+
+  inline const int getNumOperatorsFinished() const {
+    return query_manager_->getQueryExecutionState().getNumOperatorsFinished();
+  }
+
+  inline bool getOperatorFinishedStatus(const QueryPlan::DAGNodeIndex index) const {
+    return query_manager_->getQueryExecutionState().hasExecutionFinished(index);
+  }
+
+  inline bool placeDataPipelineMessage(const QueryPlan::DAGNodeIndex source_operator_index) {
+    VLOG(3) << "Place DataPipeline message for Op[" << source_operator_index << "]";
+    serialization::DataPipelineMessage proto;
+    proto.set_operator_index(source_operator_index);
+
+    proto.set_block_id(0);  // dummy block ID
+    proto.set_relation_id(0);  // dummy relation ID.
+    proto.set_query_id(0);  // dummy query ID.
+
+    // NOTE(zuyu): Using the heap memory to serialize proto as a c-like string.
+    const std::size_t proto_length = proto.ByteSize();
+    char *proto_bytes = static_cast<char*>(std::malloc(proto_length));
+    CHECK(proto.SerializeToArray(proto_bytes, proto_length));
+
+    tmb::TaggedMessage tagged_message(static_cast<const void *>(proto_bytes),
+                                      proto_length,
+                                      kDataPipelineMessage);
+    std::free(proto_bytes);
+    query_manager_->processMessage(tagged_message);
+    return query_manager_->getQueryExecutionState().hasQueryExecutionFinished();
+  }
+
+  inline bool placeWorkOrderCompleteMessage(const QueryPlan::DAGNodeIndex index) {
+    VLOG(3) << "Place WorkOrderComplete message for Op[" << index << "]";
+    TaggedMessage tagged_message;
+    serialization::NormalWorkOrderCompletionMessage proto;
+    proto.set_operator_index(index);
+    proto.set_worker_thread_index(1);  // dummy worker ID.
+    proto.set_query_id(0);  // dummy query ID.
+
+    // NOTE(zuyu): Using the heap memory to serialize proto as a c-like string.
+    const size_t proto_length = proto.ByteSize();
+    char *proto_bytes = static_cast<char*>(std::malloc(proto_length));
+    CHECK(proto.SerializeToArray(proto_bytes, proto_length));
+
+    TaggedMessage message(static_cast<const void*>(proto_bytes),
+                          proto_length,
+                          kWorkOrderCompleteMessage);
+    std::free(proto_bytes);
+    query_manager_->processMessage(message);
+
+    return query_manager_->getQueryExecutionState().hasQueryExecutionFinished();
+  }
+
+  inline bool placeRebuildWorkOrderCompleteMessage(const QueryPlan::DAGNodeIndex index) {
+    VLOG(3) << "Place RebuildWorkOrderComplete message for Op[" << index << "]";
+    serialization::RebuildWorkOrderCompletionMessage proto;
+    proto.set_operator_index(index);
+    proto.set_worker_thread_index(1);  // dummy worker thread ID.
+    proto.set_query_id(0);  // dummy query ID.
+
+    // NOTE(zuyu): Using the heap memory to serialize proto as a c-like string.
+    const size_t proto_length = proto.ByteSize();
+    char *proto_bytes = static_cast<char*>(std::malloc(proto_length));
+    CHECK(proto.SerializeToArray(proto_bytes, proto_length));
+
+    TaggedMessage message(static_cast<const void*>(proto_bytes),
+                          proto_length,
+                          kRebuildWorkOrderCompleteMessage);
+
+    std::free(proto_bytes);
+    query_manager_->processMessage(message);
+
+    return query_manager_->getQueryExecutionState().hasQueryExecutionFinished();
+  }
+
+  inline bool placeOutputBlockMessage(const QueryPlan::DAGNodeIndex index) {
+    VLOG(3) << "Place OutputBlock message for Op[" << index << "]";
+    serialization::DataPipelineMessage proto;
+    proto.set_operator_index(index);
+
+    proto.set_block_id(0);  // dummy block ID
+    proto.set_relation_id(0);  // dummy relation ID.
+    proto.set_query_id(0);  // dummy query ID.
+
+    // NOTE(zuyu): Using the heap memory to serialize proto as a c-like string.
+    const std::size_t proto_length = proto.ByteSize();
+    char *proto_bytes = static_cast<char*>(std::malloc(proto_length));
+    CHECK(proto.SerializeToArray(proto_bytes, proto_length));
+
+    tmb::TaggedMessage tagged_message(static_cast<const void *>(proto_bytes),
+                                      proto_length,
+                                      kDataPipelineMessage);
+    std::free(proto_bytes);
+    query_manager_->processMessage(tagged_message);
+    return query_manager_->getQueryExecutionState().hasQueryExecutionFinished();
+  }
+
+  unique_ptr<CatalogDatabase> db_;
+  unique_ptr<StorageManager> storage_manager_;
+
+  QueryPlan *query_plan_;
+  unique_ptr<QueryHandle> query_handle_;
+  unique_ptr<QueryManagerSingleNode> query_manager_;
+
+  MessageBusImpl bus_;
+
+  client_id worker_client_id_;
+
+  unique_ptr<WorkerDirectory> workers_;
+};
+
+TEST_F(QueryManagerTest, SingleNodeDAGNoWorkOrdersTest) {
+  // This test creates a DAG of a single node. No workorders are generated.
+  query_plan_->addRelationalOperator(new MockOperator(false, false));
+
+  const MockOperator &op = static_cast<const MockOperator &>(
+      query_plan_->getQueryPlanDAG().getNodePayload(0));
+
+  constructQueryManager();
+
+  // op doesn't have any dependencies.
+  EXPECT_TRUE(op.getBlockingDependenciesMet());
+
+  // We expect one call for op's getAllWorkOrders().
+  EXPECT_EQ(1, op.getNumCalls(MockOperator::kGetAllWorkOrders));
+  EXPECT_EQ(0, op.getNumCalls(MockOperator::kFeedInputBlock));
+  EXPECT_EQ(0, op.getNumCalls(MockOperator::kFeedInputBlocks));
+}
+
+TEST_F(QueryManagerTest, SingleNodeDAGStaticWorkOrdersTest) {
+  // This test creates a DAG of a single node. Static workorders are generated.
+  const QueryPlan::DAGNodeIndex id =
+      query_plan_->addRelationalOperator(new MockOperator(true, false, 1));
+
+  const MockOperator &op = static_cast<const MockOperator &>(
+      query_plan_->getQueryPlanDAG().getNodePayload(id));
+
+  constructQueryManager();
+
+  // op doesn't have any dependencies.
+  EXPECT_TRUE(op.getBlockingDependenciesMet());
+
+  // We expect one call for op's getAllWorkOrders().
+  EXPECT_EQ(1, op.getNumCalls(MockOperator::kGetAllWorkOrders));
+  EXPECT_EQ(0, op.getNumCalls(MockOperator::kFeedInputBlock));
+  EXPECT_EQ(0, op.getNumCalls(MockOperator::kFeedInputBlocks));
+
+  // One workorder is generated.
+  EXPECT_EQ(1, op.getNumWorkOrders());
+
+  unique_ptr<WorkerMessage> worker_message;
+  worker_message.reset(query_manager_->getNextWorkerMessage(0, -1));
+  EXPECT_TRUE(worker_message != nullptr);
+
+  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
+            worker_message->getType());
+  EXPECT_EQ(0u, worker_message->getRelationalOpIndex());
+
+  delete worker_message->getWorkOrder();
+
+  EXPECT_EQ(1, getNumWorkOrdersInExecution(id));
+  EXPECT_EQ(0, getNumOperatorsFinished());
+
+  // Send a message to QueryManager upon workorder completion.
+  // Last event processed by QueryManager.
+  EXPECT_TRUE(placeWorkOrderCompleteMessage(id));
+
+  EXPECT_EQ(0, getNumWorkOrdersInExecution(id));
+  EXPECT_EQ(1, getNumOperatorsFinished());
+  EXPECT_TRUE(getOperatorFinishedStatus(id));
+}
+
+TEST_F(QueryManagerTest, SingleNodeDAGDynamicWorkOrdersTest) {
+  // This test creates a DAG of a single node. WorkOrders are generated
+  // dynamically as pending work orders complete execution, i.e.,
+  // getAllWorkOrders() is called multiple times.  getAllWorkOrders() will be
+  // called 5 times and 3 work orders will be returned, i.e., 1st 3 calls to
+  // getAllWorkOrders() insert 1 WorkOrder and return false, and the next will
+  // insert no WorkOrder and return true.
+
+  // TODO(shoban): This test can not be more robust than this because of fixed
+  // scaffolding of mocking. If we use gMock, we can do much better.
+  const QueryPlan::DAGNodeIndex id =
+      query_plan_->addRelationalOperator(new MockOperator(true, false, 4, 3));
+
+  const MockOperator &op = static_cast<const MockOperator &>(
+      query_plan_->getQueryPlanDAG().getNodePayload(id));
+
+  constructQueryManager();
+
+  // op doesn't have any dependencies.
+  EXPECT_TRUE(op.getBlockingDependenciesMet());
+
+  for (int i = 0; i < 3; ++i) {
+    // We expect one call for op's getAllWorkOrders().
+    EXPECT_EQ(i + 1, op.getNumCalls(MockOperator::kGetAllWorkOrders));
+
+    // One workorder is generated.
+    // EXPECT_EQ(1, getWorkerInputQueueSize());
+    EXPECT_EQ(i + 1, op.getNumWorkOrders());
+
+    unique_ptr<WorkerMessage> worker_message;
+    worker_message.reset(query_manager_->getNextWorkerMessage(id, -1));
+
+    EXPECT_TRUE(worker_message != nullptr);
+    EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
+              worker_message->getType());
+    EXPECT_EQ(id, worker_message->getRelationalOpIndex());
+
+    delete worker_message->getWorkOrder();
+
+    EXPECT_EQ(1, getNumWorkOrdersInExecution(id));
+    EXPECT_EQ(0, getNumOperatorsFinished());
+
+    if (i < 2) {
+      // Send a message to QueryManager upon workorder completion.
+      EXPECT_FALSE(placeWorkOrderCompleteMessage(id));
+    } else {
+      // Send a message to QueryManager upon workorder completion.
+      // Last event.
+      EXPECT_TRUE(placeWorkOrderCompleteMessage(id));
+    }
+  }
+
+  EXPECT_EQ(0, getNumWorkOrdersInExecution(id));
+
+  EXPECT_EQ(1, getNumOperatorsFinished());
+  EXPECT_TRUE(getOperatorFinishedStatus(id));
+
+  // We place this check in the end, since it's true throughout the test.
+  EXPECT_EQ(0, op.getNumCalls(MockOperator::kFeedInputBlock));
+  EXPECT_EQ(0, op.getNumCalls(MockOperator::kFeedInputBlocks));
+}
+
+TEST_F(QueryManagerTest, TwoNodesDAGBlockingLinkTest) {
+  // We use two nodes in the DAG with a blocking link between them.
+  // There is no streaming of data involved in this test.
+  const QueryPlan::DAGNodeIndex id1 =
+      query_plan_->addRelationalOperator(new MockOperator(true, false));
+  const QueryPlan::DAGNodeIndex id2 =
+      query_plan_->addRelationalOperator(new MockOperator(true, false));
+
+  // Create a blocking link.
+  query_plan_->addDirectDependency(id2, id1, true);
+
+  static_cast<MockOperator *>(
+      query_plan_->getQueryPlanDAGMutable()->getNodePayloadMutable(id1))
+          ->setOutputRelationID(0xdead);
+
+  const MockOperator &op1 = static_cast<const MockOperator &>(
+      query_plan_->getQueryPlanDAG().getNodePayload(id1));
+  const MockOperator &op2 = static_cast<const MockOperator &>(
+      query_plan_->getQueryPlanDAG().getNodePayload(id2));
+
+  constructQueryManager();
+
+  // op1 doesn't have any dependencies
+  EXPECT_TRUE(op1.getBlockingDependenciesMet());
+
+  // Only op1 should receive a call to getAllWorkOrders initially.
+  EXPECT_EQ(1, op1.getNumCalls(MockOperator::kGetAllWorkOrders));
+  EXPECT_EQ(0, op1.getNumCalls(MockOperator::kFeedInputBlock));
+  EXPECT_EQ(0, op1.getNumCalls(MockOperator::kFeedInputBlocks));
+
+  EXPECT_EQ(0, op2.getNumCalls(MockOperator::kGetAllWorkOrders));
+  EXPECT_EQ(0, op2.getNumCalls(MockOperator::kFeedInputBlock));
+  EXPECT_EQ(0, op2.getNumCalls(MockOperator::kFeedInputBlocks));
+
+  // Only op1 should produce a workorder.
+  EXPECT_EQ(1, op1.getNumWorkOrders());
+  EXPECT_EQ(0, op2.getNumWorkOrders());
+
+  // Foreman hasn't yet got workorder completion response for the workorder.
+  unique_ptr<WorkerMessage> worker_message;
+  worker_message.reset(query_manager_->getNextWorkerMessage(id1, -1));
+
+  EXPECT_TRUE(worker_message != nullptr);
+  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
+            worker_message->getType());
+  EXPECT_EQ(id1, worker_message->getRelationalOpIndex());
+
+  delete worker_message->getWorkOrder();
+
+  EXPECT_EQ(1, getNumWorkOrdersInExecution(id1));
+  EXPECT_EQ(0, getNumWorkOrdersInExecution(id2));
+  EXPECT_EQ(0, getNumOperatorsFinished());
+
+  // Send a message to Foreman upon workorder (generated by op1) completion.
+  EXPECT_FALSE(placeWorkOrderCompleteMessage(id1));
+
+  EXPECT_EQ(0, getNumWorkOrdersInExecution(id1));
+  // op1 is over now, op2 still to go.
+  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kDoneFeedingInputBlocks));
+  EXPECT_EQ(1, getNumOperatorsFinished());
+
+  EXPECT_TRUE(getOperatorFinishedStatus(id1));
+  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kDoneFeedingInputBlocks));
+  EXPECT_FALSE(getOperatorFinishedStatus(id2));
+
+  worker_message.reset(query_manager_->getNextWorkerMessage(id2, -1));
+  EXPECT_TRUE(worker_message != nullptr);
+  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
+            worker_message->getType());
+  EXPECT_EQ(id2, worker_message->getRelationalOpIndex());
+
+  delete worker_message->getWorkOrder();
+
+  EXPECT_EQ(1, getNumWorkOrdersInExecution(id2));
+
+  // op1 is op2's blocking dependency.
+  EXPECT_TRUE(op2.getBlockingDependenciesMet());
+
+  EXPECT_EQ(1, op1.getNumCalls(MockOperator::kGetAllWorkOrders));
+  // op2 should get first call of getAllWorkOrders() when op1 is over.
+  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kGetAllWorkOrders));
+
+  EXPECT_EQ(1, op2.getNumWorkOrders());
+
+  // Send a message to QueryManager upon workorder (generated by op2) completion.
+  // Note that the worker hasn't yet popped the workorder. Usually this won't
+  // happen as workers pop workorders first, execute and then send the response.
+  EXPECT_TRUE(placeWorkOrderCompleteMessage(id2));
+
+  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kGetAllWorkOrders));
+
+  EXPECT_EQ(0, getNumWorkOrdersInExecution(id1));
+  EXPECT_EQ(0, getNumWorkOrdersInExecution(id2));
+
+  EXPECT_EQ(2, getNumOperatorsFinished());
+  EXPECT_TRUE(getOperatorFinishedStatus(id1));
+  EXPECT_TRUE(getOperatorFinishedStatus(id2));
+
+  // Expect no additional calls to getAllWorkOrders.
+  EXPECT_EQ(1, op1.getNumCalls(MockOperator::kGetAllWorkOrders));
+  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kGetAllWorkOrders));
+}
+
+TEST_F(QueryManagerTest, TwoNodesDAGPipeLinkTest) {
+  // We use two nodes in the DAG with a non-blocking link between them.
+  // We stream output of op1 to op2. Sequeuce of events is as follows:
+  // 1. op1 creates a workorder.
+  // 2. We send a "block full" (from op1) to QueryManager.
+  // 3. op2 creates a workorder because of step 2.
+  const QueryPlan::DAGNodeIndex id1 =
+      query_plan_->addRelationalOperator(new MockOperator(true, false, 1));
+  const QueryPlan::DAGNodeIndex id2 =
+      query_plan_->addRelationalOperator(new MockOperator(true, true, 3));
+
+  // Create a non-blocking link.
+  query_plan_->addDirectDependency(id2, id1, false);
+
+  static_cast<MockOperator *>(
+      query_plan_->getQueryPlanDAGMutable()->getNodePayloadMutable(id1))
+      ->setOutputRelationID(0xdead);
+
+  const MockOperator &op1 = static_cast<const MockOperator &>(
+      query_plan_->getQueryPlanDAG().getNodePayload(id1));
+  const MockOperator &op2 = static_cast<const MockOperator &>(
+      query_plan_->getQueryPlanDAG().getNodePayload(id2));
+
+  constructQueryManager();
+
+  // As none of the operators have a blocking link, blocking dependencies should
+  // be met.
+  EXPECT_TRUE(op1.getBlockingDependenciesMet());
+  EXPECT_TRUE(op2.getBlockingDependenciesMet());
+
+  EXPECT_EQ(1, op1.getNumCalls(MockOperator::kGetAllWorkOrders));
+  EXPECT_EQ(1, op1.getNumWorkOrders());
+  EXPECT_EQ(0, op1.getNumCalls(MockOperator::kFeedInputBlock));
+  EXPECT_EQ(0, op1.getNumCalls(MockOperator::kFeedInputBlocks));
+
+  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kGetAllWorkOrders));
+  // op2 will generate workorder only after receiving a streaming input.
+  EXPECT_EQ(0, op2.getNumWorkOrders());
+  EXPECT_EQ(0, op2.getNumCalls(MockOperator::kFeedInputBlock));
+  EXPECT_EQ(0, op2.getNumCalls(MockOperator::kFeedInputBlocks));
+
+  unique_ptr<WorkerMessage> worker_message;
+  worker_message.reset(query_manager_->getNextWorkerMessage(id1, -1));
+
+  EXPECT_TRUE(worker_message != nullptr);
+  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
+            worker_message->getType());
+  EXPECT_EQ(id1, worker_message->getRelationalOpIndex());
+
+  delete worker_message->getWorkOrder();
+
+  // Send a message to QueryManager upon block getting full (output of op1).
+  EXPECT_FALSE(placeOutputBlockMessage(id1));
+
+  // op1 is not finished yet because the response of workorder completion hasn't
+  // been received yet by the QueryManager.
+  EXPECT_FALSE(getOperatorFinishedStatus(id1));
+  EXPECT_FALSE(getOperatorFinishedStatus(id2));
+
+  // No additional call to op1's getAllWorkOrders.
+  EXPECT_EQ(1, op1.getNumCalls(MockOperator::kGetAllWorkOrders));
+  EXPECT_EQ(0, op1.getNumCalls(MockOperator::kFeedInputBlock));
+  EXPECT_EQ(0, op1.getNumCalls(MockOperator::kFeedInputBlocks));
+
+  // Output from op1 should be fed to op2.
+  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kFeedInputBlock));
+  EXPECT_EQ(0, op2.getNumCalls(MockOperator::kFeedInputBlocks));
+
+  // A call to op2's getAllWorkOrders because of the streamed input.
+  EXPECT_EQ(2, op2.getNumCalls(MockOperator::kGetAllWorkOrders));
+  EXPECT_EQ(1, op2.getNumWorkOrders());
+
+  // Place a message of a workorder completion of op1 on Foreman's input queue.
+  EXPECT_FALSE(placeWorkOrderCompleteMessage(id1));
+
+  EXPECT_EQ(0, getNumWorkOrdersInExecution(id1));
+  EXPECT_TRUE(getOperatorFinishedStatus(id1));
+  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kDoneFeedingInputBlocks));
+
+  // An additional call to op2's getAllWorkOrders because of completion of op1.
+  EXPECT_EQ(3, op2.getNumCalls(MockOperator::kGetAllWorkOrders));
+  EXPECT_EQ(2, op2.getNumWorkOrders());
+
+  worker_message.reset(query_manager_->getNextWorkerMessage(id2, -1));
+
+  EXPECT_TRUE(worker_message != nullptr);
+  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
+            worker_message->getType());
+  EXPECT_EQ(id2, worker_message->getRelationalOpIndex());
+
+  delete worker_message->getWorkOrder();
+
+  // Place a message of a workorder completion of op2 on Foreman's input queue.
+  EXPECT_FALSE(placeWorkOrderCompleteMessage(id2));
+
+  EXPECT_TRUE(getOperatorFinishedStatus(id1));
+
+  worker_message.reset(query_manager_->getNextWorkerMessage(id2, -1));
+
+  EXPECT_TRUE(worker_message != nullptr);
+  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
+            worker_message->getType());
+  EXPECT_EQ(id2, worker_message->getRelationalOpIndex());
+
+  delete worker_message->getWorkOrder();
+
+  EXPECT_EQ(1, getNumWorkOrdersInExecution(id2));
+  EXPECT_FALSE(getOperatorFinishedStatus(id2));
+
+  // Send a message to Foreman upon workorder (generated by op2) completion.
+  EXPECT_TRUE(placeWorkOrderCompleteMessage(id2));
+
+  EXPECT_TRUE(getOperatorFinishedStatus(id1));
+  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kDoneFeedingInputBlocks));
+
+  EXPECT_EQ(0, getNumWorkOrdersInExecution(id2));
+  EXPECT_TRUE(getOperatorFinishedStatus(id2));
+}
+
+TEST_F(QueryManagerTest, TwoNodesDAGPartiallyFilledBlocksTest) {
+  // In this test, we create a 2-node DAG with a non-blocking link between them.
+  // There is no streaming of data from op1 to op2 during the execution of op1.
+  // op1 produces a partially filled block at the end of its execution which is
+  // rebuilt and then fed to op2.
+  const QueryPlan::DAGNodeIndex id1 =
+      query_plan_->addRelationalOperator(new MockOperator(true, false, 1));
+  const QueryPlan::DAGNodeIndex id2 =
+      query_plan_->addRelationalOperator(new MockOperator(true, true, 3, 1));
+
+  // Create a non-blocking link.
+  query_plan_->addDirectDependency(id2, id1, false);
+
+  // Create a relation, owned by db_.*/
+  CatalogRelation *relation =
+      new CatalogRelation(nullptr /* catalog_database */, "test_relation");
+  const relation_id output_relation_id = db_->addRelation(relation);
+
+  // Setup the InsertDestination proto in the query context proto.
+  serialization::QueryContext *query_context_proto =
+      query_handle_->getQueryContextProtoMutable();
+
+  const QueryContext::insert_destination_id insert_destination_index =
+      query_context_proto->insert_destinations_size();
+  serialization::InsertDestination *insert_destination_proto =
+      query_context_proto->add_insert_destinations();
+
+  insert_destination_proto->set_insert_destination_type(
+      serialization::InsertDestinationType::BLOCK_POOL);
+  insert_destination_proto->set_relation_id(output_relation_id);
+  insert_destination_proto->set_relational_op_index(id1);
+
+  MockOperator *op1_mutable = static_cast<MockOperator *>(
+      query_plan_->getQueryPlanDAGMutable()->getNodePayloadMutable(id1));
+  op1_mutable->setInsertDestinationID(insert_destination_index);
+  op1_mutable->setOutputRelationID(output_relation_id);
+
+  const MockOperator &op1 = static_cast<const MockOperator &>(
+      query_plan_->getQueryPlanDAG().getNodePayload(id1));
+  const MockOperator &op2 = static_cast<const MockOperator &>(
+      query_plan_->getQueryPlanDAG().getNodePayload(id2));
+
+  constructQueryManager();
+
+  // NOTE(zuyu): An operator generally has no ideas about partially filled
+  // blocks, but InsertDestination in QueryContext does.
+  // Mock to add partially filled blocks in the InsertDestination.
+  InsertDestination *insert_destination =
+      query_manager_->getQueryContextMutable()->getInsertDestination(
+          insert_destination_index);
+  DCHECK(insert_destination != nullptr);
+  MutableBlockReference block_ref;
+  static_cast<BlockPoolInsertDestination *>(insert_destination)
+      ->available_block_refs_.push_back(move(block_ref));
+
+  // There's no blocking dependency in the DAG.
+  EXPECT_TRUE(op1.getBlockingDependenciesMet());
+  EXPECT_TRUE(op2.getBlockingDependenciesMet());
+
+  EXPECT_EQ(1, op1.getNumCalls(MockOperator::kGetAllWorkOrders));
+  EXPECT_EQ(1, op1.getNumWorkOrders());
+
+  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kGetAllWorkOrders));
+  EXPECT_EQ(0, op2.getNumWorkOrders());
+
+  unique_ptr<WorkerMessage> worker_message;
+  worker_message.reset(query_manager_->getNextWorkerMessage(id1, -1));
+
+  EXPECT_TRUE(worker_message != nullptr);
+  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
+            worker_message->getType());
+  EXPECT_EQ(id1, worker_message->getRelationalOpIndex());
+
+  delete worker_message->getWorkOrder();
+
+  // Send a message to QueryManager upon workorder (generated by op1) completion.
+  EXPECT_FALSE(placeWorkOrderCompleteMessage(id1));
+
+  EXPECT_EQ(0, getNumWorkOrdersInExecution(id1));
+
+  worker_message.reset(query_manager_->getNextWorkerMessage(id1, -1));
+  EXPECT_TRUE(worker_message != nullptr);
+  EXPECT_EQ(WorkerMessage::WorkerMessageType::kRebuildWorkOrder,
+            worker_message->getType());
+
+  EXPECT_EQ(id1, worker_message->getRelationalOpIndex());
+
+  delete worker_message->getWorkOrder();
+
+  // op1 generates a rebuild workorder. The block is rebuilt and streamed
+  // to Foreman.
+  EXPECT_FALSE(placeDataPipelineMessage(id1));
+
+  EXPECT_FALSE(placeRebuildWorkOrderCompleteMessage(id1));
+  // Based on the streamed input, op2's getAllWorkOrders should produce a
+  // workorder.
+  EXPECT_EQ(3, op2.getNumCalls(MockOperator::kGetAllWorkOrders));
+  EXPECT_EQ(1, op2.getNumWorkOrders());
+
+  worker_message.reset(query_manager_->getNextWorkerMessage(id2, -1));
+
+  EXPECT_TRUE(worker_message != nullptr);
+  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
+            worker_message->getType());
+
+  EXPECT_EQ(id2, worker_message->getRelationalOpIndex());
+
+  delete worker_message->getWorkOrder();
+
+  EXPECT_TRUE(getOperatorFinishedStatus(id1));
+  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kDoneFeedingInputBlocks));
+  EXPECT_FALSE(getOperatorFinishedStatus(id2));
+  EXPECT_EQ(1, getNumWorkOrdersInExecution(id2));
+
+  // Send a message to QueryManager upon workorder (generated by op2) completion.
+  EXPECT_TRUE(placeWorkOrderCompleteMessage(id2));
+
+  EXPECT_EQ(0, getNumWorkOrdersInExecution(id2));
+
+  EXPECT_TRUE(getOperatorFinishedStatus(id2));
+}
+
+TEST_F(QueryManagerTest, MultipleNodesNoOutputTest) {
+  // When an operator produces workorders but no output, the QueryManager should
+  // check the dependents of this operator to make progress.
+  const QueryPlan::DAGNodeIndex kNumNodes = 5;
+  std::vector<QueryPlan::DAGNodeIndex> ids;
+  ids.reserve(kNumNodes);
+
+  for (QueryPlan::DAGNodeIndex i = 0; i < kNumNodes; ++i) {
+    if (i == 0) {
+      ids[i] = query_plan_->addRelationalOperator(new MockOperator(true, false));
+    } else {
+      ids[i] = query_plan_->addRelationalOperator(new MockOperator(true, true));
+    }
+    VLOG(3) << ids[i];
+  }
+
+  /**
+   * The DAG looks like this:
+   *
+   * op1 -> op2 -> op3 -> op4 -> op5
+   *
+   **/
+  for (QueryPlan::DAGNodeIndex i = 0; i < kNumNodes - 1; ++i) {
+    query_plan_->addDirectDependency(ids[i + 1], ids[i], false);
+    static_cast<MockOperator*>(query_plan_->getQueryPlanDAGMutable()->getNodePayloadMutable(ids[i]))
+        ->setOutputRelationID(0xdead);
+  }
+
+  std::vector<const MockOperator*> operators;
+  for (QueryPlan::DAGNodeIndex i = 0; i < kNumNodes; ++i) {
+    operators.push_back(static_cast<const MockOperator*>(&query_plan_->getQueryPlanDAG().getNodePayload(ids[i])));
+  }
+
+  constructQueryManager();
+
+  // operators[0] should have produced a workorder by now.
+  EXPECT_EQ(1, operators[0]->getNumWorkOrders());
+
+  unique_ptr<WorkerMessage> worker_message;
+  worker_message.reset(query_manager_->getNextWorkerMessage(ids[0], -1));
+
+  EXPECT_TRUE(worker_message != nullptr);
+  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
+            worker_message->getType());
+
+  EXPECT_EQ(ids[0], worker_message->getRelationalOpIndex());
+
+  delete worker_message->getWorkOrder();
+
+  EXPECT_EQ(1, getNumWorkOrdersInExecution(ids[0]));
+  EXPECT_FALSE(getOperatorFinishedStatus(ids[0]));
+
+  for (QueryPlan::DAGNodeIndex i = 0; i < kNumNodes; ++i) {
+    EXPECT_EQ(1, operators[ids[i]]->getNumCalls(MockOperator::kGetAllWorkOrders));
+  }
+
+  // Send a message to QueryManager upon workorder (generated by operators[0])
+  // completion.
+  EXPECT_TRUE(placeWorkOrderCompleteMessage(ids[0]));
+
+  for (QueryPlan::DAGNodeIndex i = 0; i < kNumNodes; ++i) {
+    EXPECT_EQ(0, getNumWorkOrdersInExecution(ids[i]));
+    EXPECT_TRUE(getOperatorFinishedStatus(ids[i]));
+    if (i < kNumNodes - 1) {
+      EXPECT_EQ(1, operators[i + 1]->getNumCalls(MockOperator::kDoneFeedingInputBlocks));
+    }
+  }
+}
+
+TEST_F(QueryManagerTest, OutOfOrderWorkOrderCompletionTest) {
+  // Consider two operators, both generate one workorder each. The dependent's
+  // workorder finishes before dependency's workorder.
+  const QueryPlan::DAGNodeIndex id1 = query_plan_->addRelationalOperator(new MockOperator(true, false, 1));
+  const QueryPlan::DAGNodeIndex id2 = query_plan_->addRelationalOperator(new MockOperator(true, true, 2, 1));
+
+  // Create a non-blocking link.
+  query_plan_->addDirectDependency(id2, id1, false);
+
+  constructQueryManager();
+
+  unique_ptr<WorkerMessage> worker_message;
+  worker_message.reset(query_manager_->getNextWorkerMessage(id1, -1));
+
+  EXPECT_TRUE(worker_message != nullptr);
+  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
+            worker_message->getType());
+
+  EXPECT_EQ(id1, worker_message->getRelationalOpIndex());
+
+  delete worker_message->getWorkOrder();
+
+  // Send a message to QueryManager upon a block (output of op1) getting full.
+  EXPECT_FALSE(placeOutputBlockMessage(id1));
+
+  // op1 is not finished yet because the response of workorder completion hasn't
+  // been received yet.
+  EXPECT_FALSE(getOperatorFinishedStatus(id1));
+  EXPECT_FALSE(getOperatorFinishedStatus(id2));
+
+  worker_message.reset(query_manager_->getNextWorkerMessage(id2, -1));
+  EXPECT_TRUE(worker_message != nullptr);
+  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
+            worker_message->getType());
+
+  EXPECT_EQ(id2, worker_message->getRelationalOpIndex());
+
+  delete worker_message->getWorkOrder();
+
+  // As mentioned earlier, op2 finishes before op1.
+  EXPECT_FALSE(placeWorkOrderCompleteMessage(id2));
+
+  // op1's workorder execution is over.
+  EXPECT_TRUE(placeWorkOrderCompleteMessage(id1));
+
+  EXPECT_TRUE(getOperatorFinishedStatus(id1));
+  EXPECT_TRUE(getOperatorFinishedStatus(id2));
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b4e25edc/query_execution/tests/QueryManager_unittest.cpp
----------------------------------------------------------------------
diff --git a/query_execution/tests/QueryManager_unittest.cpp b/query_execution/tests/QueryManager_unittest.cpp
deleted file mode 100644
index 37e2cdd..0000000
--- a/query_execution/tests/QueryManager_unittest.cpp
+++ /dev/null
@@ -1,940 +0,0 @@
-/**
- *   Copyright 2011-2015 Quickstep Technologies LLC.
- *   Copyright 2015-2016 Pivotal Software, Inc.
- *
- *   Licensed 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.
- **/
-
-#include <climits>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "catalog/CatalogDatabase.hpp"
-#include "catalog/CatalogRelation.hpp"
-#include "catalog/CatalogTypedefs.hpp"
-#include "query_execution/QueryContext.hpp"
-#include "query_execution/QueryContext.pb.h"
-#include "query_execution/QueryExecutionMessages.pb.h"
-#include "query_execution/QueryExecutionState.hpp"
-#include "query_execution/QueryExecutionTypedefs.hpp"
-#include "query_execution/QueryManager.hpp"
-#include "query_execution/WorkOrdersContainer.hpp"
-#include "query_execution/WorkerDirectory.hpp"
-#include "query_execution/WorkerMessage.hpp"
-#include "query_optimizer/QueryHandle.hpp"
-#include "query_optimizer/QueryPlan.hpp"
-#include "relational_operators/RelationalOperator.hpp"
-#include "relational_operators/WorkOrder.hpp"
-#include "storage/InsertDestination.hpp"
-#include "storage/InsertDestination.pb.h"
-#include "storage/StorageBlock.hpp"
-#include "storage/StorageBlockInfo.hpp"
-#include "storage/StorageManager.hpp"
-#include "utility/DAG.hpp"
-#include "utility/Macros.hpp"
-
-#include "glog/logging.h"
-#include "gtest/gtest.h"
-
-#include "tmb/id_typedefs.h"
-#include "tmb/message_bus.h"
-#include "tmb/tagged_message.h"
-
-using std::move;
-using std::unique_ptr;
-using std::vector;
-
-using tmb::client_id;
-
-namespace quickstep {
-
-class WorkOrderProtosContainer;
-
-class MockWorkOrder : public WorkOrder {
- public:
-  explicit MockWorkOrder(const int op_index)
-      : WorkOrder(0), op_index_(op_index) {}
-
-  void execute() override {
-    VLOG(3) << "WorkOrder[" << op_index_ << "] executing.";
-  }
-
-  inline QueryPlan::DAGNodeIndex getOpIndex() const {
-    return op_index_;
-  }
-
- private:
-  const QueryPlan::DAGNodeIndex op_index_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockWorkOrder);
-};
-
-class MockOperator: public RelationalOperator {
- public:
-  enum function_name {
-    kFeedInputBlock = 0,
-    kFeedInputBlocks,
-    kDoneFeedingInputBlocks,
-    kGetAllWorkOrders
-  };
-
-  MockOperator(const bool produce_workorders,
-               const bool has_streaming_input,
-               const int max_getworkorder_iters = 1,
-               const int max_workorders = INT_MAX)
-      : RelationalOperator(0 /* Query Id */),
-        produce_workorders_(produce_workorders),
-        has_streaming_input_(has_streaming_input),
-        max_workorders_(max_workorders),
-        max_getworkorder_iters_(max_getworkorder_iters),
-        num_calls_get_workorders_(0),
-        num_workorders_generated_(0),
-        num_calls_feedblock_(0),
-        num_calls_feedblocks_(0),
-        num_calls_donefeedingblocks_(0) {
-  }
-
-#define MOCK_OP_LOG(x) VLOG(x) << "Op[" << op_index_ << "]: " << __func__ << ": "
-
-  // The methods below are used to check whether QueryManager calls the Relational
-  // operator, how many times it calls a particular method etc.
-  inline int getNumWorkOrders() const {
-    return num_workorders_generated_;
-  }
-
-  inline int getNumCalls(const function_name fname) const {
-    switch (fname) {
-      case kFeedInputBlock:
-        return num_calls_feedblock_;
-      case kFeedInputBlocks:
-        return num_calls_feedblocks_;
-      case kDoneFeedingInputBlocks:
-        return num_calls_donefeedingblocks_;
-      case kGetAllWorkOrders:
-        return num_calls_get_workorders_;
-      default:
-        return -1;
-    }
-  }
-
-  inline bool getBlockingDependenciesMet() const {
-    MOCK_OP_LOG(3) << "met.";
-    return blocking_dependencies_met_;
-  }
-
-  void setInsertDestinationID(const QueryContext::insert_destination_id insert_destination_index) {
-    insert_destination_index_ = insert_destination_index;
-  }
-
-  // Mock to trigger doneFeedingInputBlocks for the dependent operators
-  // in QueryManager::markOperatorFinished.
-  void setOutputRelationID(const relation_id rel_id) {
-    output_relation_id_ = rel_id;
-  }
-
-  // Override methods from the base class.
-  bool getAllWorkOrders(
-      WorkOrdersContainer *container,
-      QueryContext *query_context,
-      StorageManager *storage_manager,
-      const tmb::client_id foreman_client_id,
-      tmb::MessageBus *bus) override {
-    ++num_calls_get_workorders_;
-    if (produce_workorders_) {
-      if (has_streaming_input_) {
-        if ((num_calls_feedblock_ > 0 || num_calls_feedblocks_ > 0) && (num_workorders_generated_ < max_workorders_)) {
-          MOCK_OP_LOG(3) << "[stream] generate WorkOrder";
-          container->addNormalWorkOrder(new MockWorkOrder(op_index_), op_index_);
-          ++num_workorders_generated_;
-        }
-      } else {
-        if (blocking_dependencies_met_ && (num_workorders_generated_ < max_workorders_)) {
-          MOCK_OP_LOG(3) << "[static] generate WorkOrder";
-          container->addNormalWorkOrder(new MockWorkOrder(op_index_), op_index_);
-          ++num_workorders_generated_;
-        }
-      }
-    }
-    MOCK_OP_LOG(3) << "count(" << num_calls_get_workorders_ << ") "
-                   << "return(" << (num_calls_get_workorders_ == max_getworkorder_iters_) << ")";
-    return num_calls_get_workorders_ == max_getworkorder_iters_;
-  }
-
-  bool getAllWorkOrderProtos(WorkOrderProtosContainer *container) override {
-    return true;
-  }
-
-  void feedInputBlock(const block_id input_block_id,
-                      const relation_id input_relation_id) override {
-    ++num_calls_feedblock_;
-    MOCK_OP_LOG(3) << "count(" << num_calls_feedblock_ << ")";
-  }
-
-  void feedInputBlocks(const relation_id rel_id,
-                       std::vector<block_id> *partially_filled_blocks) override {
-    ++num_calls_feedblocks_;
-    MOCK_OP_LOG(3) << "count(" << num_calls_feedblocks_ << ")";
-  }
-
-  void doneFeedingInputBlocks(const relation_id rel_id) override {
-    ++num_calls_donefeedingblocks_;
-    MOCK_OP_LOG(3) << "count(" << num_calls_donefeedingblocks_ << ")";
-  }
-
-  QueryContext::insert_destination_id getInsertDestinationID() const override {
-    return insert_destination_index_;
-  }
-
-  const relation_id getOutputRelationID() const override {
-    return output_relation_id_;
-  }
-
- private:
-  const bool produce_workorders_;
-  const bool has_streaming_input_;
-  const int max_workorders_;
-  const int max_getworkorder_iters_;
-
-  int num_calls_get_workorders_;
-  int num_workorders_generated_;
-  int num_calls_feedblock_;
-  int num_calls_feedblocks_;
-  int num_calls_donefeedingblocks_;
-
-  QueryContext::insert_destination_id insert_destination_index_ = QueryContext::kInvalidInsertDestinationId;
-
-  relation_id output_relation_id_ = -1;
-
-#undef MOCK_OP_LOG
-
-  DISALLOW_COPY_AND_ASSIGN(MockOperator);
-};
-
-
-class QueryManagerTest : public ::testing::Test {
- protected:
-  virtual void SetUp() {
-    db_.reset(new CatalogDatabase(nullptr /* catalog */, "database"));
-    storage_manager_.reset(new StorageManager("./"));
-    bus_.Initialize();
-    query_handle_.reset(new QueryHandle(0));  // dummy query ID.
-    query_plan_ = query_handle_->getQueryPlanMutable();
-    query_handle_->getQueryContextProtoMutable()->set_query_id(query_handle_->query_id());
-  }
-
-  inline void constructQueryManager() {
-    query_manager_.reset(new QueryManager(
-        0, 1, query_handle_.get(), db_.get(), storage_manager_.get(), &bus_));
-  }
-
-  inline const int getNumWorkOrdersInExecution(const QueryPlan::DAGNodeIndex index) const {
-    return query_manager_->getQueryExecutionState().getNumQueuedWorkOrders(index);
-  }
-
-  inline const int getNumOperatorsFinished() const {
-    return query_manager_->getQueryExecutionState().getNumOperatorsFinished();
-  }
-
-  inline bool getOperatorFinishedStatus(const QueryPlan::DAGNodeIndex index) const {
-    return query_manager_->getQueryExecutionState().hasExecutionFinished(index);
-  }
-
-  inline bool placeDataPipelineMessage(const QueryPlan::DAGNodeIndex source_operator_index) {
-    VLOG(3) << "Place DataPipeline message for Op[" << source_operator_index << "]";
-    serialization::DataPipelineMessage proto;
-    proto.set_operator_index(source_operator_index);
-
-    proto.set_block_id(0);  // dummy block ID
-    proto.set_relation_id(0);  // dummy relation ID.
-    proto.set_query_id(0);  // dummy query ID.
-
-    // NOTE(zuyu): Using the heap memory to serialize proto as a c-like string.
-    const std::size_t proto_length = proto.ByteSize();
-    char *proto_bytes = static_cast<char*>(std::malloc(proto_length));
-    CHECK(proto.SerializeToArray(proto_bytes, proto_length));
-
-    tmb::TaggedMessage tagged_message(static_cast<const void *>(proto_bytes),
-                                      proto_length,
-                                      kDataPipelineMessage);
-    std::free(proto_bytes);
-    query_manager_->processMessage(tagged_message);
-    return query_manager_->getQueryExecutionState().hasQueryExecutionFinished();
-  }
-
-  inline bool placeWorkOrderCompleteMessage(const QueryPlan::DAGNodeIndex index) {
-    VLOG(3) << "Place WorkOrderComplete message for Op[" << index << "]";
-    TaggedMessage tagged_message;
-    serialization::NormalWorkOrderCompletionMessage proto;
-    proto.set_operator_index(index);
-    proto.set_worker_thread_index(1);  // dummy worker ID.
-    proto.set_query_id(0);  // dummy query ID.
-
-    // NOTE(zuyu): Using the heap memory to serialize proto as a c-like string.
-    const size_t proto_length = proto.ByteSize();
-    char *proto_bytes = static_cast<char*>(std::malloc(proto_length));
-    CHECK(proto.SerializeToArray(proto_bytes, proto_length));
-
-    TaggedMessage message(static_cast<const void*>(proto_bytes),
-                          proto_length,
-                          kWorkOrderCompleteMessage);
-    std::free(proto_bytes);
-    query_manager_->processMessage(message);
-
-    return query_manager_->getQueryExecutionState().hasQueryExecutionFinished();
-  }
-
-  inline bool placeRebuildWorkOrderCompleteMessage(const QueryPlan::DAGNodeIndex index) {
-    VLOG(3) << "Place RebuildWorkOrderComplete message for Op[" << index << "]";
-    serialization::RebuildWorkOrderCompletionMessage proto;
-    proto.set_operator_index(index);
-    proto.set_worker_thread_index(1);  // dummy worker thread ID.
-    proto.set_query_id(0);  // dummy query ID.
-
-    // NOTE(zuyu): Using the heap memory to serialize proto as a c-like string.
-    const size_t proto_length = proto.ByteSize();
-    char *proto_bytes = static_cast<char*>(std::malloc(proto_length));
-    CHECK(proto.SerializeToArray(proto_bytes, proto_length));
-
-    TaggedMessage message(static_cast<const void*>(proto_bytes),
-                          proto_length,
-                          kRebuildWorkOrderCompleteMessage);
-
-    std::free(proto_bytes);
-    query_manager_->processMessage(message);
-
-    return query_manager_->getQueryExecutionState().hasQueryExecutionFinished();
-  }
-
-  inline bool placeOutputBlockMessage(const QueryPlan::DAGNodeIndex index) {
-    VLOG(3) << "Place OutputBlock message for Op[" << index << "]";
-    serialization::DataPipelineMessage proto;
-    proto.set_operator_index(index);
-
-    proto.set_block_id(0);  // dummy block ID
-    proto.set_relation_id(0);  // dummy relation ID.
-    proto.set_query_id(0);  // dummy query ID.
-
-    // NOTE(zuyu): Using the heap memory to serialize proto as a c-like string.
-    const std::size_t proto_length = proto.ByteSize();
-    char *proto_bytes = static_cast<char*>(std::malloc(proto_length));
-    CHECK(proto.SerializeToArray(proto_bytes, proto_length));
-
-    tmb::TaggedMessage tagged_message(static_cast<const void *>(proto_bytes),
-                                      proto_length,
-                                      kDataPipelineMessage);
-    std::free(proto_bytes);
-    query_manager_->processMessage(tagged_message);
-    return query_manager_->getQueryExecutionState().hasQueryExecutionFinished();
-  }
-
-  unique_ptr<CatalogDatabase> db_;
-  unique_ptr<StorageManager> storage_manager_;
-
-  QueryPlan *query_plan_;
-  unique_ptr<QueryHandle> query_handle_;
-  unique_ptr<QueryManager> query_manager_;
-
-  MessageBusImpl bus_;
-
-  client_id worker_client_id_;
-
-  unique_ptr<WorkerDirectory> workers_;
-};
-
-TEST_F(QueryManagerTest, SingleNodeDAGNoWorkOrdersTest) {
-  // This test creates a DAG of a single node. No workorders are generated.
-  query_plan_->addRelationalOperator(new MockOperator(false, false));
-
-  const MockOperator &op = static_cast<const MockOperator &>(
-      query_plan_->getQueryPlanDAG().getNodePayload(0));
-
-  constructQueryManager();
-
-  // op doesn't have any dependencies.
-  EXPECT_TRUE(op.getBlockingDependenciesMet());
-
-  // We expect one call for op's getAllWorkOrders().
-  EXPECT_EQ(1, op.getNumCalls(MockOperator::kGetAllWorkOrders));
-  EXPECT_EQ(0, op.getNumCalls(MockOperator::kFeedInputBlock));
-  EXPECT_EQ(0, op.getNumCalls(MockOperator::kFeedInputBlocks));
-}
-
-TEST_F(QueryManagerTest, SingleNodeDAGStaticWorkOrdersTest) {
-  // This test creates a DAG of a single node. Static workorders are generated.
-  const QueryPlan::DAGNodeIndex id =
-      query_plan_->addRelationalOperator(new MockOperator(true, false, 1));
-
-  const MockOperator &op = static_cast<const MockOperator &>(
-      query_plan_->getQueryPlanDAG().getNodePayload(id));
-
-  constructQueryManager();
-
-  // op doesn't have any dependencies.
-  EXPECT_TRUE(op.getBlockingDependenciesMet());
-
-  // We expect one call for op's getAllWorkOrders().
-  EXPECT_EQ(1, op.getNumCalls(MockOperator::kGetAllWorkOrders));
-  EXPECT_EQ(0, op.getNumCalls(MockOperator::kFeedInputBlock));
-  EXPECT_EQ(0, op.getNumCalls(MockOperator::kFeedInputBlocks));
-
-  // One workorder is generated.
-  EXPECT_EQ(1, op.getNumWorkOrders());
-
-  unique_ptr<WorkerMessage> worker_message;
-  worker_message.reset(query_manager_->getNextWorkerMessage(0, -1));
-  EXPECT_TRUE(worker_message != nullptr);
-
-  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
-            worker_message->getType());
-  EXPECT_EQ(0u, worker_message->getRelationalOpIndex());
-
-  delete worker_message->getWorkOrder();
-
-  EXPECT_EQ(1, getNumWorkOrdersInExecution(id));
-  EXPECT_EQ(0, getNumOperatorsFinished());
-
-  // Send a message to QueryManager upon workorder completion.
-  // Last event processed by QueryManager.
-  EXPECT_TRUE(placeWorkOrderCompleteMessage(id));
-
-  EXPECT_EQ(0, getNumWorkOrdersInExecution(id));
-  EXPECT_EQ(1, getNumOperatorsFinished());
-  EXPECT_TRUE(getOperatorFinishedStatus(id));
-}
-
-TEST_F(QueryManagerTest, SingleNodeDAGDynamicWorkOrdersTest) {
-  // This test creates a DAG of a single node. WorkOrders are generated
-  // dynamically as pending work orders complete execution, i.e.,
-  // getAllWorkOrders() is called multiple times.  getAllWorkOrders() will be
-  // called 5 times and 3 work orders will be returned, i.e., 1st 3 calls to
-  // getAllWorkOrders() insert 1 WorkOrder and return false, and the next will
-  // insert no WorkOrder and return true.
-
-  // TODO(shoban): This test can not be more robust than this because of fixed
-  // scaffolding of mocking. If we use gMock, we can do much better.
-  const QueryPlan::DAGNodeIndex id =
-      query_plan_->addRelationalOperator(new MockOperator(true, false, 4, 3));
-
-  const MockOperator &op = static_cast<const MockOperator &>(
-      query_plan_->getQueryPlanDAG().getNodePayload(id));
-
-  constructQueryManager();
-
-  // op doesn't have any dependencies.
-  EXPECT_TRUE(op.getBlockingDependenciesMet());
-
-  for (int i = 0; i < 3; ++i) {
-    // We expect one call for op's getAllWorkOrders().
-    EXPECT_EQ(i + 1, op.getNumCalls(MockOperator::kGetAllWorkOrders));
-
-    // One workorder is generated.
-    // EXPECT_EQ(1, getWorkerInputQueueSize());
-    EXPECT_EQ(i + 1, op.getNumWorkOrders());
-
-    unique_ptr<WorkerMessage> worker_message;
-    worker_message.reset(query_manager_->getNextWorkerMessage(id, -1));
-
-    EXPECT_TRUE(worker_message != nullptr);
-    EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
-              worker_message->getType());
-    EXPECT_EQ(id, worker_message->getRelationalOpIndex());
-
-    delete worker_message->getWorkOrder();
-
-    EXPECT_EQ(1, getNumWorkOrdersInExecution(id));
-    EXPECT_EQ(0, getNumOperatorsFinished());
-
-    if (i < 2) {
-      // Send a message to QueryManager upon workorder completion.
-      EXPECT_FALSE(placeWorkOrderCompleteMessage(id));
-    } else {
-      // Send a message to QueryManager upon workorder completion.
-      // Last event.
-      EXPECT_TRUE(placeWorkOrderCompleteMessage(id));
-    }
-  }
-
-  EXPECT_EQ(0, getNumWorkOrdersInExecution(id));
-
-  EXPECT_EQ(1, getNumOperatorsFinished());
-  EXPECT_TRUE(getOperatorFinishedStatus(id));
-
-  // We place this check in the end, since it's true throughout the test.
-  EXPECT_EQ(0, op.getNumCalls(MockOperator::kFeedInputBlock));
-  EXPECT_EQ(0, op.getNumCalls(MockOperator::kFeedInputBlocks));
-}
-
-TEST_F(QueryManagerTest, TwoNodesDAGBlockingLinkTest) {
-  // We use two nodes in the DAG with a blocking link between them.
-  // There is no streaming of data involved in this test.
-  const QueryPlan::DAGNodeIndex id1 =
-      query_plan_->addRelationalOperator(new MockOperator(true, false));
-  const QueryPlan::DAGNodeIndex id2 =
-      query_plan_->addRelationalOperator(new MockOperator(true, false));
-
-  // Create a blocking link.
-  query_plan_->addDirectDependency(id2, id1, true);
-
-  static_cast<MockOperator *>(
-      query_plan_->getQueryPlanDAGMutable()->getNodePayloadMutable(id1))
-          ->setOutputRelationID(0xdead);
-
-  const MockOperator &op1 = static_cast<const MockOperator &>(
-      query_plan_->getQueryPlanDAG().getNodePayload(id1));
-  const MockOperator &op2 = static_cast<const MockOperator &>(
-      query_plan_->getQueryPlanDAG().getNodePayload(id2));
-
-  constructQueryManager();
-
-  // op1 doesn't have any dependencies
-  EXPECT_TRUE(op1.getBlockingDependenciesMet());
-
-  // Only op1 should receive a call to getAllWorkOrders initially.
-  EXPECT_EQ(1, op1.getNumCalls(MockOperator::kGetAllWorkOrders));
-  EXPECT_EQ(0, op1.getNumCalls(MockOperator::kFeedInputBlock));
-  EXPECT_EQ(0, op1.getNumCalls(MockOperator::kFeedInputBlocks));
-
-  EXPECT_EQ(0, op2.getNumCalls(MockOperator::kGetAllWorkOrders));
-  EXPECT_EQ(0, op2.getNumCalls(MockOperator::kFeedInputBlock));
-  EXPECT_EQ(0, op2.getNumCalls(MockOperator::kFeedInputBlocks));
-
-  // Only op1 should produce a workorder.
-  EXPECT_EQ(1, op1.getNumWorkOrders());
-  EXPECT_EQ(0, op2.getNumWorkOrders());
-
-  // Foreman hasn't yet got workorder completion response for the workorder.
-  unique_ptr<WorkerMessage> worker_message;
-  worker_message.reset(query_manager_->getNextWorkerMessage(id1, -1));
-
-  EXPECT_TRUE(worker_message != nullptr);
-  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
-            worker_message->getType());
-  EXPECT_EQ(id1, worker_message->getRelationalOpIndex());
-
-  delete worker_message->getWorkOrder();
-
-  EXPECT_EQ(1, getNumWorkOrdersInExecution(id1));
-  EXPECT_EQ(0, getNumWorkOrdersInExecution(id2));
-  EXPECT_EQ(0, getNumOperatorsFinished());
-
-  // Send a message to Foreman upon workorder (generated by op1) completion.
-  EXPECT_FALSE(placeWorkOrderCompleteMessage(id1));
-
-  EXPECT_EQ(0, getNumWorkOrdersInExecution(id1));
-  // op1 is over now, op2 still to go.
-  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kDoneFeedingInputBlocks));
-  EXPECT_EQ(1, getNumOperatorsFinished());
-
-  EXPECT_TRUE(getOperatorFinishedStatus(id1));
-  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kDoneFeedingInputBlocks));
-  EXPECT_FALSE(getOperatorFinishedStatus(id2));
-
-  worker_message.reset(query_manager_->getNextWorkerMessage(id2, -1));
-  EXPECT_TRUE(worker_message != nullptr);
-  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
-            worker_message->getType());
-  EXPECT_EQ(id2, worker_message->getRelationalOpIndex());
-
-  delete worker_message->getWorkOrder();
-
-  EXPECT_EQ(1, getNumWorkOrdersInExecution(id2));
-
-  // op1 is op2's blocking dependency.
-  EXPECT_TRUE(op2.getBlockingDependenciesMet());
-
-  EXPECT_EQ(1, op1.getNumCalls(MockOperator::kGetAllWorkOrders));
-  // op2 should get first call of getAllWorkOrders() when op1 is over.
-  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kGetAllWorkOrders));
-
-  EXPECT_EQ(1, op2.getNumWorkOrders());
-
-  // Send a message to QueryManager upon workorder (generated by op2) completion.
-  // Note that the worker hasn't yet popped the workorder. Usually this won't
-  // happen as workers pop workorders first, execute and then send the response.
-  EXPECT_TRUE(placeWorkOrderCompleteMessage(id2));
-
-  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kGetAllWorkOrders));
-
-  EXPECT_EQ(0, getNumWorkOrdersInExecution(id1));
-  EXPECT_EQ(0, getNumWorkOrdersInExecution(id2));
-
-  EXPECT_EQ(2, getNumOperatorsFinished());
-  EXPECT_TRUE(getOperatorFinishedStatus(id1));
-  EXPECT_TRUE(getOperatorFinishedStatus(id2));
-
-  // Expect no additional calls to getAllWorkOrders.
-  EXPECT_EQ(1, op1.getNumCalls(MockOperator::kGetAllWorkOrders));
-  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kGetAllWorkOrders));
-}
-
-TEST_F(QueryManagerTest, TwoNodesDAGPipeLinkTest) {
-  // We use two nodes in the DAG with a non-blocking link between them.
-  // We stream output of op1 to op2. Sequeuce of events is as follows:
-  // 1. op1 creates a workorder.
-  // 2. We send a "block full" (from op1) to QueryManager.
-  // 3. op2 creates a workorder because of step 2.
-  const QueryPlan::DAGNodeIndex id1 =
-      query_plan_->addRelationalOperator(new MockOperator(true, false, 1));
-  const QueryPlan::DAGNodeIndex id2 =
-      query_plan_->addRelationalOperator(new MockOperator(true, true, 3));
-
-  // Create a non-blocking link.
-  query_plan_->addDirectDependency(id2, id1, false);
-
-  static_cast<MockOperator *>(
-      query_plan_->getQueryPlanDAGMutable()->getNodePayloadMutable(id1))
-      ->setOutputRelationID(0xdead);
-
-  const MockOperator &op1 = static_cast<const MockOperator &>(
-      query_plan_->getQueryPlanDAG().getNodePayload(id1));
-  const MockOperator &op2 = static_cast<const MockOperator &>(
-      query_plan_->getQueryPlanDAG().getNodePayload(id2));
-
-  constructQueryManager();
-
-  // As none of the operators have a blocking link, blocking dependencies should
-  // be met.
-  EXPECT_TRUE(op1.getBlockingDependenciesMet());
-  EXPECT_TRUE(op2.getBlockingDependenciesMet());
-
-  EXPECT_EQ(1, op1.getNumCalls(MockOperator::kGetAllWorkOrders));
-  EXPECT_EQ(1, op1.getNumWorkOrders());
-  EXPECT_EQ(0, op1.getNumCalls(MockOperator::kFeedInputBlock));
-  EXPECT_EQ(0, op1.getNumCalls(MockOperator::kFeedInputBlocks));
-
-  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kGetAllWorkOrders));
-  // op2 will generate workorder only after receiving a streaming input.
-  EXPECT_EQ(0, op2.getNumWorkOrders());
-  EXPECT_EQ(0, op2.getNumCalls(MockOperator::kFeedInputBlock));
-  EXPECT_EQ(0, op2.getNumCalls(MockOperator::kFeedInputBlocks));
-
-  unique_ptr<WorkerMessage> worker_message;
-  worker_message.reset(query_manager_->getNextWorkerMessage(id1, -1));
-
-  EXPECT_TRUE(worker_message != nullptr);
-  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
-            worker_message->getType());
-  EXPECT_EQ(id1, worker_message->getRelationalOpIndex());
-
-  delete worker_message->getWorkOrder();
-
-  // Send a message to QueryManager upon block getting full (output of op1).
-  EXPECT_FALSE(placeOutputBlockMessage(id1));
-
-  // op1 is not finished yet because the response of workorder completion hasn't
-  // been received yet by the QueryManager.
-  EXPECT_FALSE(getOperatorFinishedStatus(id1));
-  EXPECT_FALSE(getOperatorFinishedStatus(id2));
-
-  // No additional call to op1's getAllWorkOrders.
-  EXPECT_EQ(1, op1.getNumCalls(MockOperator::kGetAllWorkOrders));
-  EXPECT_EQ(0, op1.getNumCalls(MockOperator::kFeedInputBlock));
-  EXPECT_EQ(0, op1.getNumCalls(MockOperator::kFeedInputBlocks));
-
-  // Output from op1 should be fed to op2.
-  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kFeedInputBlock));
-  EXPECT_EQ(0, op2.getNumCalls(MockOperator::kFeedInputBlocks));
-
-  // A call to op2's getAllWorkOrders because of the streamed input.
-  EXPECT_EQ(2, op2.getNumCalls(MockOperator::kGetAllWorkOrders));
-  EXPECT_EQ(1, op2.getNumWorkOrders());
-
-  // Place a message of a workorder completion of op1 on Foreman's input queue.
-  EXPECT_FALSE(placeWorkOrderCompleteMessage(id1));
-
-  EXPECT_EQ(0, getNumWorkOrdersInExecution(id1));
-  EXPECT_TRUE(getOperatorFinishedStatus(id1));
-  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kDoneFeedingInputBlocks));
-
-  // An additional call to op2's getAllWorkOrders because of completion of op1.
-  EXPECT_EQ(3, op2.getNumCalls(MockOperator::kGetAllWorkOrders));
-  EXPECT_EQ(2, op2.getNumWorkOrders());
-
-  worker_message.reset(query_manager_->getNextWorkerMessage(id2, -1));
-
-  EXPECT_TRUE(worker_message != nullptr);
-  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
-            worker_message->getType());
-  EXPECT_EQ(id2, worker_message->getRelationalOpIndex());
-
-  delete worker_message->getWorkOrder();
-
-  // Place a message of a workorder completion of op2 on Foreman's input queue.
-  EXPECT_FALSE(placeWorkOrderCompleteMessage(id2));
-
-  EXPECT_TRUE(getOperatorFinishedStatus(id1));
-
-  worker_message.reset(query_manager_->getNextWorkerMessage(id2, -1));
-
-  EXPECT_TRUE(worker_message != nullptr);
-  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
-            worker_message->getType());
-  EXPECT_EQ(id2, worker_message->getRelationalOpIndex());
-
-  delete worker_message->getWorkOrder();
-
-  EXPECT_EQ(1, getNumWorkOrdersInExecution(id2));
-  EXPECT_FALSE(getOperatorFinishedStatus(id2));
-
-  // Send a message to Foreman upon workorder (generated by op2) completion.
-  EXPECT_TRUE(placeWorkOrderCompleteMessage(id2));
-
-  EXPECT_TRUE(getOperatorFinishedStatus(id1));
-  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kDoneFeedingInputBlocks));
-
-  EXPECT_EQ(0, getNumWorkOrdersInExecution(id2));
-  EXPECT_TRUE(getOperatorFinishedStatus(id2));
-}
-
-TEST_F(QueryManagerTest, TwoNodesDAGPartiallyFilledBlocksTest) {
-  // In this test, we create a 2-node DAG with a non-blocking link between them.
-  // There is no streaming of data from op1 to op2 during the execution of op1.
-  // op1 produces a partially filled block at the end of its execution which is
-  // rebuilt and then fed to op2.
-  const QueryPlan::DAGNodeIndex id1 =
-      query_plan_->addRelationalOperator(new MockOperator(true, false, 1));
-  const QueryPlan::DAGNodeIndex id2 =
-      query_plan_->addRelationalOperator(new MockOperator(true, true, 3, 1));
-
-  // Create a non-blocking link.
-  query_plan_->addDirectDependency(id2, id1, false);
-
-  // Create a relation, owned by db_.*/
-  CatalogRelation *relation =
-      new CatalogRelation(nullptr /* catalog_database */, "test_relation");
-  const relation_id output_relation_id = db_->addRelation(relation);
-
-  // Setup the InsertDestination proto in the query context proto.
-  serialization::QueryContext *query_context_proto =
-      query_handle_->getQueryContextProtoMutable();
-
-  const QueryContext::insert_destination_id insert_destination_index =
-      query_context_proto->insert_destinations_size();
-  serialization::InsertDestination *insert_destination_proto =
-      query_context_proto->add_insert_destinations();
-
-  insert_destination_proto->set_insert_destination_type(
-      serialization::InsertDestinationType::BLOCK_POOL);
-  insert_destination_proto->set_relation_id(output_relation_id);
-  insert_destination_proto->set_relational_op_index(id1);
-
-  MockOperator *op1_mutable = static_cast<MockOperator *>(
-      query_plan_->getQueryPlanDAGMutable()->getNodePayloadMutable(id1));
-  op1_mutable->setInsertDestinationID(insert_destination_index);
-  op1_mutable->setOutputRelationID(output_relation_id);
-
-  const MockOperator &op1 = static_cast<const MockOperator &>(
-      query_plan_->getQueryPlanDAG().getNodePayload(id1));
-  const MockOperator &op2 = static_cast<const MockOperator &>(
-      query_plan_->getQueryPlanDAG().getNodePayload(id2));
-
-  constructQueryManager();
-
-  // NOTE(zuyu): An operator generally has no ideas about partially filled
-  // blocks, but InsertDestination in QueryContext does.
-  // Mock to add partially filled blocks in the InsertDestination.
-  InsertDestination *insert_destination =
-      query_manager_->getQueryContextMutable()->getInsertDestination(
-          insert_destination_index);
-  DCHECK(insert_destination != nullptr);
-  MutableBlockReference block_ref;
-  static_cast<BlockPoolInsertDestination *>(insert_destination)
-      ->available_block_refs_.push_back(move(block_ref));
-
-  // There's no blocking dependency in the DAG.
-  EXPECT_TRUE(op1.getBlockingDependenciesMet());
-  EXPECT_TRUE(op2.getBlockingDependenciesMet());
-
-  EXPECT_EQ(1, op1.getNumCalls(MockOperator::kGetAllWorkOrders));
-  EXPECT_EQ(1, op1.getNumWorkOrders());
-
-  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kGetAllWorkOrders));
-  EXPECT_EQ(0, op2.getNumWorkOrders());
-
-  unique_ptr<WorkerMessage> worker_message;
-  worker_message.reset(query_manager_->getNextWorkerMessage(id1, -1));
-
-  EXPECT_TRUE(worker_message != nullptr);
-  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
-            worker_message->getType());
-  EXPECT_EQ(id1, worker_message->getRelationalOpIndex());
-
-  delete worker_message->getWorkOrder();
-
-  // Send a message to QueryManager upon workorder (generated by op1) completion.
-  EXPECT_FALSE(placeWorkOrderCompleteMessage(id1));
-
-  EXPECT_EQ(0, getNumWorkOrdersInExecution(id1));
-
-  worker_message.reset(query_manager_->getNextWorkerMessage(id1, -1));
-  EXPECT_TRUE(worker_message != nullptr);
-  EXPECT_EQ(WorkerMessage::WorkerMessageType::kRebuildWorkOrder,
-            worker_message->getType());
-
-  EXPECT_EQ(id1, worker_message->getRelationalOpIndex());
-
-  delete worker_message->getWorkOrder();
-
-  // op1 generates a rebuild workorder. The block is rebuilt and streamed
-  // to Foreman.
-  EXPECT_FALSE(placeDataPipelineMessage(id1));
-
-  EXPECT_FALSE(placeRebuildWorkOrderCompleteMessage(id1));
-  // Based on the streamed input, op2's getAllWorkOrders should produce a
-  // workorder.
-  EXPECT_EQ(3, op2.getNumCalls(MockOperator::kGetAllWorkOrders));
-  EXPECT_EQ(1, op2.getNumWorkOrders());
-
-  worker_message.reset(query_manager_->getNextWorkerMessage(id2, -1));
-
-  EXPECT_TRUE(worker_message != nullptr);
-  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
-            worker_message->getType());
-
-  EXPECT_EQ(id2, worker_message->getRelationalOpIndex());
-
-  delete worker_message->getWorkOrder();
-
-  EXPECT_TRUE(getOperatorFinishedStatus(id1));
-  EXPECT_EQ(1, op2.getNumCalls(MockOperator::kDoneFeedingInputBlocks));
-  EXPECT_FALSE(getOperatorFinishedStatus(id2));
-  EXPECT_EQ(1, getNumWorkOrdersInExecution(id2));
-
-  // Send a message to QueryManager upon workorder (generated by op2) completion.
-  EXPECT_TRUE(placeWorkOrderCompleteMessage(id2));
-
-  EXPECT_EQ(0, getNumWorkOrdersInExecution(id2));
-
-  EXPECT_TRUE(getOperatorFinishedStatus(id2));
-}
-
-TEST_F(QueryManagerTest, MultipleNodesNoOutputTest) {
-  // When an operator produces workorders but no output, the QueryManager should
-  // check the dependents of this operator to make progress.
-  const QueryPlan::DAGNodeIndex kNumNodes = 5;
-  std::vector<QueryPlan::DAGNodeIndex> ids;
-  ids.reserve(kNumNodes);
-
-  for (QueryPlan::DAGNodeIndex i = 0; i < kNumNodes; ++i) {
-    if (i == 0) {
-      ids[i] = query_plan_->addRelationalOperator(new MockOperator(true, false));
-    } else {
-      ids[i] = query_plan_->addRelationalOperator(new MockOperator(true, true));
-    }
-    VLOG(3) << ids[i];
-  }
-
-  /**
-   * The DAG looks like this:
-   *
-   * op1 -> op2 -> op3 -> op4 -> op5
-   *
-   **/
-  for (QueryPlan::DAGNodeIndex i = 0; i < kNumNodes - 1; ++i) {
-    query_plan_->addDirectDependency(ids[i + 1], ids[i], false);
-    static_cast<MockOperator*>(query_plan_->getQueryPlanDAGMutable()->getNodePayloadMutable(ids[i]))
-        ->setOutputRelationID(0xdead);
-  }
-
-  std::vector<const MockOperator*> operators;
-  for (QueryPlan::DAGNodeIndex i = 0; i < kNumNodes; ++i) {
-    operators.push_back(static_cast<const MockOperator*>(&query_plan_->getQueryPlanDAG().getNodePayload(ids[i])));
-  }
-
-  constructQueryManager();
-
-  // operators[0] should have produced a workorder by now.
-  EXPECT_EQ(1, operators[0]->getNumWorkOrders());
-
-  unique_ptr<WorkerMessage> worker_message;
-  worker_message.reset(query_manager_->getNextWorkerMessage(ids[0], -1));
-
-  EXPECT_TRUE(worker_message != nullptr);
-  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
-            worker_message->getType());
-
-  EXPECT_EQ(ids[0], worker_message->getRelationalOpIndex());
-
-  delete worker_message->getWorkOrder();
-
-  EXPECT_EQ(1, getNumWorkOrdersInExecution(ids[0]));
-  EXPECT_FALSE(getOperatorFinishedStatus(ids[0]));
-
-  for (QueryPlan::DAGNodeIndex i = 0; i < kNumNodes; ++i) {
-    EXPECT_EQ(1, operators[ids[i]]->getNumCalls(MockOperator::kGetAllWorkOrders));
-  }
-
-  // Send a message to QueryManager upon workorder (generated by operators[0])
-  // completion.
-  EXPECT_TRUE(placeWorkOrderCompleteMessage(ids[0]));
-
-  for (QueryPlan::DAGNodeIndex i = 0; i < kNumNodes; ++i) {
-    EXPECT_EQ(0, getNumWorkOrdersInExecution(ids[i]));
-    EXPECT_TRUE(getOperatorFinishedStatus(ids[i]));
-    if (i < kNumNodes - 1) {
-      EXPECT_EQ(1, operators[i + 1]->getNumCalls(MockOperator::kDoneFeedingInputBlocks));
-    }
-  }
-}
-
-TEST_F(QueryManagerTest, OutOfOrderWorkOrderCompletionTest) {
-  // Consider two operators, both generate one workorder each. The dependent's
-  // workorder finishes before dependency's workorder.
-  const QueryPlan::DAGNodeIndex id1 = query_plan_->addRelationalOperator(new MockOperator(true, false, 1));
-  const QueryPlan::DAGNodeIndex id2 = query_plan_->addRelationalOperator(new MockOperator(true, true, 2, 1));
-
-  // Create a non-blocking link.
-  query_plan_->addDirectDependency(id2, id1, false);
-
-  constructQueryManager();
-
-  unique_ptr<WorkerMessage> worker_message;
-  worker_message.reset(query_manager_->getNextWorkerMessage(id1, -1));
-
-  EXPECT_TRUE(worker_message != nullptr);
-  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
-            worker_message->getType());
-
-  EXPECT_EQ(id1, worker_message->getRelationalOpIndex());
-
-  delete worker_message->getWorkOrder();
-
-  // Send a message to QueryManager upon a block (output of op1) getting full.
-  EXPECT_FALSE(placeOutputBlockMessage(id1));
-
-  // op1 is not finished yet because the response of workorder completion hasn't
-  // been received yet.
-  EXPECT_FALSE(getOperatorFinishedStatus(id1));
-  EXPECT_FALSE(getOperatorFinishedStatus(id2));
-
-  worker_message.reset(query_manager_->getNextWorkerMessage(id2, -1));
-  EXPECT_TRUE(worker_message != nullptr);
-  EXPECT_EQ(WorkerMessage::WorkerMessageType::kWorkOrder,
-            worker_message->getType());
-
-  EXPECT_EQ(id2, worker_message->getRelationalOpIndex());
-
-  delete worker_message->getWorkOrder();
-
-  // As mentioned earlier, op2 finishes before op1.
-  EXPECT_FALSE(placeWorkOrderCompleteMessage(id2));
-
-  // op1's workorder execution is over.
-  EXPECT_TRUE(placeWorkOrderCompleteMessage(id1));
-
-  EXPECT_TRUE(getOperatorFinishedStatus(id1));
-  EXPECT_TRUE(getOperatorFinishedStatus(id2));
-}
-
-}  // namespace quickstep


[23/30] incubator-quickstep git commit: TMB: Added Support for an Empty, but Typed TaggedMessage.

Posted by ra...@apache.org.
TMB: Added Support for an Empty, but Typed TaggedMessage.


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

Branch: refs/heads/quickstep-28-29
Commit: 52a32a372612a3e8f5ab268886d4b9cbb546c205
Parents: 260b862
Author: Zuyu Zhang <zu...@twitter.com>
Authored: Fri Jul 29 19:57:49 2016 -0700
Committer: Zuyu Zhang <zu...@twitter.com>
Committed: Mon Aug 1 10:11:19 2016 -0700

----------------------------------------------------------------------
 third_party/tmb/include/tmb/tagged_message.h | 9 +++++++++
 1 file changed, 9 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/52a32a37/third_party/tmb/include/tmb/tagged_message.h
----------------------------------------------------------------------
diff --git a/third_party/tmb/include/tmb/tagged_message.h b/third_party/tmb/include/tmb/tagged_message.h
index 49dcee7..f3a77ee 100644
--- a/third_party/tmb/include/tmb/tagged_message.h
+++ b/third_party/tmb/include/tmb/tagged_message.h
@@ -63,6 +63,15 @@ class TaggedMessage {
   }
 
   /**
+   * @brief Constructor which creates an empty, but typed message.
+   **/
+  explicit TaggedMessage(const message_type_id message_type)
+      : payload_inline_(true),
+        message_type_(message_type) {
+    payload_.in_line.size = 0;
+  }
+
+  /**
    * @brief Constructor.
    *
    * @param msg A pointer to the message contents in memory, which will be


[16/30] incubator-quickstep git commit: Minor bug fix in QueryManager base.

Posted by ra...@apache.org.
Minor bug fix in QueryManager base.

- Modified the order in which we check the completion of query and
  completion of an operator in the queryStatus function.


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

Branch: refs/heads/quickstep-28-29
Commit: 9f9e3b7b1957e774a982f260c93968a9617c5354
Parents: c0bb462
Author: Harshad Deshmukh <hb...@apache.org>
Authored: Mon Jul 25 12:23:34 2016 -0500
Committer: Harshad Deshmukh <hb...@apache.org>
Committed: Tue Jul 26 16:21:42 2016 -0500

----------------------------------------------------------------------
 query_execution/QueryManagerBase.cpp | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/9f9e3b7b/query_execution/QueryManagerBase.cpp
----------------------------------------------------------------------
diff --git a/query_execution/QueryManagerBase.cpp b/query_execution/QueryManagerBase.cpp
index 37beb02..d2a3341 100644
--- a/query_execution/QueryManagerBase.cpp
+++ b/query_execution/QueryManagerBase.cpp
@@ -71,15 +71,16 @@ QueryManagerBase::QueryManagerBase(QueryHandle *query_handle)
 
 QueryManagerBase::QueryStatusCode QueryManagerBase::queryStatus(
     const dag_node_index op_index) {
-  if (query_exec_state_->hasExecutionFinished(op_index)) {
-    return QueryStatusCode::kOperatorExecuted;
-  }
-
-  // As kQueryExecuted takes precedence over kOperatorExecuted, we check again.
+  // As kQueryExecuted takes precedence over kOperatorExecuted, we first check
+  // whether the query has finished its execution.
   if (query_exec_state_->hasQueryExecutionFinished()) {
     return QueryStatusCode::kQueryExecuted;
   }
 
+  if (query_exec_state_->hasExecutionFinished(op_index)) {
+    return QueryStatusCode::kOperatorExecuted;
+  }
+
   return QueryStatusCode::kNone;
 }
 


[20/30] incubator-quickstep git commit: Refactored getProto in CatalogRelation.

Posted by ra...@apache.org.
Refactored getProto in CatalogRelation.


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

Branch: refs/heads/quickstep-28-29
Commit: e53186e5547ac619a67aabdfacbb1abdbca78b60
Parents: aaecc76
Author: Zuyu Zhang <zu...@twitter.com>
Authored: Sat Jul 30 06:29:05 2016 -0700
Committer: Zuyu Zhang <zu...@twitter.com>
Committed: Sat Jul 30 06:29:05 2016 -0700

----------------------------------------------------------------------
 catalog/CatalogRelation.cpp | 16 +---------------
 1 file changed, 1 insertion(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/e53186e5/catalog/CatalogRelation.cpp
----------------------------------------------------------------------
diff --git a/catalog/CatalogRelation.cpp b/catalog/CatalogRelation.cpp
index 01aebb5..682b6be 100644
--- a/catalog/CatalogRelation.cpp
+++ b/catalog/CatalogRelation.cpp
@@ -143,21 +143,7 @@ CatalogRelation::CatalogRelation(const serialization::CatalogRelationSchema &pro
 }
 
 serialization::CatalogRelationSchema CatalogRelation::getProto() const {
-  serialization::CatalogRelationSchema proto;
-
-  proto.set_relation_id(id_);
-  proto.set_name(name_);
-  proto.set_temporary(temporary_);
-
-  for (PtrVector<CatalogAttribute, true>::const_iterator it = attr_vec_.begin();
-       it != attr_vec_.end();
-       ++it) {
-    if (it.isNull()) {
-      proto.add_attributes();
-    } else {
-      proto.add_attributes()->MergeFrom(it->getProto());
-    }
-  }
+  serialization::CatalogRelationSchema proto = CatalogRelationSchema::getProto();
 
   proto.MutableExtension(serialization::CatalogRelation::default_layout)
       ->MergeFrom(getDefaultStorageBlockLayout().getDescription());


[29/30] incubator-quickstep git commit: Initial commit for QUICKSTEP-28 and QUICKSTEP-29. Code refactoring and cleanup, some more optimizations are pending.

Posted by ra...@apache.org.
Initial commit for QUICKSTEP-28 and QUICKSTEP-29. Code refactoring and cleanup, some more optimizations are pending.


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

Branch: refs/heads/quickstep-28-29
Commit: 39485d620402273890e92072e05f856fd9da7f9d
Parents: ccea2ff
Author: rathijit <ra...@node-2.hashtable.quickstep-pg0.wisc.cloudlab.us>
Authored: Mon Jul 4 02:44:48 2016 -0500
Committer: rathijit <ra...@node-2.aggregation.quickstep-pg0.wisc.cloudlab.us>
Committed: Fri Aug 5 14:23:35 2016 -0500

----------------------------------------------------------------------
 .../aggregation/AggregationConcreteHandle.cpp   |   29 +-
 .../aggregation/AggregationConcreteHandle.hpp   |  223 ++
 expressions/aggregation/AggregationHandle.hpp   |    8 +-
 .../aggregation/AggregationHandleAvg.cpp        |   40 +-
 .../aggregation/AggregationHandleAvg.hpp        |   62 +-
 .../aggregation/AggregationHandleCount.cpp      |   38 +-
 .../aggregation/AggregationHandleCount.hpp      |   50 +-
 .../aggregation/AggregationHandleDistinct.cpp   |    2 +-
 .../aggregation/AggregationHandleDistinct.hpp   |    2 +-
 .../aggregation/AggregationHandleMax.cpp        |   29 +-
 .../aggregation/AggregationHandleMax.hpp        |   39 +-
 .../aggregation/AggregationHandleMin.cpp        |   30 +-
 .../aggregation/AggregationHandleMin.hpp        |   44 +-
 .../aggregation/AggregationHandleSum.cpp        |   31 +-
 .../aggregation/AggregationHandleSum.hpp        |   52 +-
 expressions/aggregation/CMakeLists.txt          |    7 +
 storage/AggregationOperationState.cpp           |   95 +-
 storage/AggregationOperationState.hpp           |    7 +-
 storage/CMakeLists.txt                          |   58 +
 storage/FastHashTable.hpp                       | 2640 ++++++++++++++++++
 storage/FastHashTableFactory.hpp                |  300 ++
 storage/FastSeparateChainingHashTable.hpp       | 1761 ++++++++++++
 storage/HashTableBase.hpp                       |    2 +-
 storage/HashTablePool.hpp                       |   42 +
 storage/StorageBlock.cpp                        |   88 +-
 storage/StorageBlock.hpp                        |    8 +
 threading/SpinMutex.hpp                         |    2 +
 27 files changed, 5587 insertions(+), 102 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/expressions/aggregation/AggregationConcreteHandle.cpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationConcreteHandle.cpp b/expressions/aggregation/AggregationConcreteHandle.cpp
index 05ca58d..d808302 100644
--- a/expressions/aggregation/AggregationConcreteHandle.cpp
+++ b/expressions/aggregation/AggregationConcreteHandle.cpp
@@ -22,6 +22,7 @@
 
 #include "catalog/CatalogTypedefs.hpp"
 #include "storage/HashTable.hpp"
+#include "storage/FastHashTable.hpp"
 #include "storage/HashTableFactory.hpp"
 
 namespace quickstep {
@@ -49,22 +50,24 @@ void AggregationConcreteHandle::insertValueAccessorIntoDistinctifyHashTable(
     AggregationStateHashTableBase *distinctify_hash_table) const {
   // If the key-value pair is already there, we don't need to update the value,
   // which should always be "true". I.e. the value is just a placeholder.
-  const auto noop_upserter = [](const auto &accessor, const bool *value) -> void {};
+//  const auto noop_upserter = [](const auto &accessor, const bool *value) -> void {};
 
-  AggregationStateHashTable<bool> *hash_table =
-      static_cast<AggregationStateHashTable<bool>*>(distinctify_hash_table);
+  AggregationStateFastHashTable *hash_table =
+      static_cast<AggregationStateFastHashTable *>(distinctify_hash_table);
   if (key_ids.size() == 1) {
-    hash_table->upsertValueAccessor(accessor,
-                                    key_ids[0],
-                                    true /* check_for_null_keys */,
-                                    true /* initial_value */,
-                                    &noop_upserter);
+// TODO(rathijit): fix
+//    hash_table->upsertValueAccessor(accessor,
+//                                    key_ids[0],
+//                                    true /* check_for_null_keys */,
+//                                    true /* initial_value */,
+//                                    &noop_upserter);
   } else {
-    hash_table->upsertValueAccessorCompositeKey(accessor,
-                                                key_ids,
-                                                true /* check_for_null_keys */,
-                                                true /* initial_value */,
-                                                &noop_upserter);
+    std::vector<std::vector<attribute_id>> empty_args;
+    empty_args.resize(1);
+    hash_table->upsertValueAccessorCompositeKeyFast(empty_args,
+                                                    accessor,
+                                                    key_ids,
+                                                    true /* check_for_null_keys */);
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/expressions/aggregation/AggregationConcreteHandle.hpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationConcreteHandle.hpp b/expressions/aggregation/AggregationConcreteHandle.hpp
index 0267e17..c5d7b3c 100644
--- a/expressions/aggregation/AggregationConcreteHandle.hpp
+++ b/expressions/aggregation/AggregationConcreteHandle.hpp
@@ -31,6 +31,7 @@
 #include "types/TypedValue.hpp"
 #include "types/containers/ColumnVector.hpp"
 #include "utility/Macros.hpp"
+#include "threading/SpinMutex.hpp"
 
 #include "glog/logging.h"
 
@@ -79,6 +80,37 @@ class HashTableStateUpserter {
   DISALLOW_COPY_AND_ASSIGN(HashTableStateUpserter);
 };
 
+template <typename HandleT>
+class HashTableStateUpserterFast {
+ public:
+  /**
+   * @brief Constructor.
+   *
+   * @param handle The aggregation handle being used.
+   * @param source_state The aggregation state in the source aggregation hash
+   *        table. The corresponding state (for the same key) in the destination
+   *        hash table will be upserted.
+   **/
+  HashTableStateUpserterFast(const HandleT &handle, const uint8_t *source_state)
+      : handle_(handle), source_state_(source_state) {}
+
+  /**
+   * @brief The operator for the functor required for the upsert.
+   *
+   * @param destination_state The aggregation state in the aggregation hash
+   *        table that is being upserted.
+   **/
+  void operator()(uint8_t *destination_state) {
+    handle_.mergeStatesFast(source_state_, destination_state);
+  }
+
+ private:
+  const HandleT &handle_;
+  const uint8_t *source_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(HashTableStateUpserterFast);
+};
+
 /**
  * @brief A class to support the functor for merging group by hash tables.
  **/
@@ -129,6 +161,53 @@ class HashTableMerger {
   DISALLOW_COPY_AND_ASSIGN(HashTableMerger);
 };
 
+template <typename HandleT, typename HashTableT>
+class HashTableMergerFast {
+ public:
+  /**
+   * @brief Constructor
+   *
+   * @param handle The Aggregation handle being used.
+   * @param destination_hash_table The destination hash table to which other
+   *        hash tables will be merged.
+   **/
+  HashTableMergerFast(const HandleT &handle,
+                  AggregationStateHashTableBase *destination_hash_table)
+      : handle_(handle),
+        destination_hash_table_(
+            static_cast<HashTableT *>(destination_hash_table)) {}
+
+  /**
+   * @brief The operator for the functor.
+   *
+   * @param group_by_key The group by key being merged.
+   * @param source_state The aggregation state for the given key in the source
+   *        aggregation hash table.
+   **/
+  inline void operator()(const std::vector<TypedValue> &group_by_key,
+                         const uint8_t *source_state) {
+    const uint8_t *original_state =
+        destination_hash_table_->getSingleCompositeKey(group_by_key);
+    if (original_state != nullptr) {
+      HashTableStateUpserterFast<HandleT> upserter(
+          handle_, source_state);
+      // The CHECK is required as upsertCompositeKey can return false if the
+      // hash table runs out of space during the upsert process. The ideal
+      // solution will be to retry again if the upsert fails.
+      CHECK(destination_hash_table_->upsertCompositeKeyFast(
+          group_by_key, original_state, &upserter));
+    } else {
+      destination_hash_table_->putCompositeKeyFast(group_by_key, source_state);
+    }
+  }
+
+ private:
+  const HandleT &handle_;
+  HashTableT *destination_hash_table_;
+
+  DISALLOW_COPY_AND_ASSIGN(HashTableMergerFast);
+};
+
 /**
  * @brief The helper intermediate subclass of AggregationHandle that provides
  *        virtual method implementations as well as helper methods that are
@@ -208,11 +287,26 @@ class AggregationConcreteHandle : public AggregationHandle {
 
   template <typename HandleT,
             typename HashTableT>
+  void aggregateOnDistinctifyHashTableForGroupByUnaryHelperFast(
+      const AggregationStateHashTableBase &distinctify_hash_table,
+      AggregationStateHashTableBase *hash_table) const;
+
+
+  template <typename HandleT,
+            typename HashTableT>
   ColumnVector* finalizeHashTableHelper(
       const Type &result_type,
       const AggregationStateHashTableBase &hash_table,
       std::vector<std::vector<TypedValue>> *group_by_keys) const;
 
+  template <typename HandleT,
+            typename HashTableT>
+  ColumnVector* finalizeHashTableHelperFast(
+      const Type &result_type,
+      const AggregationStateHashTableBase &hash_table,
+      std::vector<std::vector<TypedValue>> *group_by_keys,
+      int index) const;
+
   template <typename HandleT, typename HashTableT>
   inline TypedValue finalizeGroupInHashTable(
       const AggregationStateHashTableBase &hash_table,
@@ -224,11 +318,29 @@ class AggregationConcreteHandle : public AggregationHandle {
     return static_cast<const HandleT*>(this)->finalizeHashTableEntry(*group_state);
   }
 
+  template <typename HandleT, typename HashTableT>
+  inline TypedValue finalizeGroupInHashTableFast(
+      const AggregationStateHashTableBase &hash_table,
+      const std::vector<TypedValue> &group_key,
+      int index) const {
+    const std::uint8_t *group_state
+        = static_cast<const HashTableT&>(hash_table).getSingleCompositeKey(group_key, index);
+    DCHECK(group_state != nullptr)
+        << "Could not find entry for specified group_key in HashTable";
+    return static_cast<const HandleT*>(this)->finalizeHashTableEntryFast(group_state);
+  }
+
   template <typename HandleT, typename StateT, typename HashTableT>
   void mergeGroupByHashTablesHelper(
       const AggregationStateHashTableBase &source_hash_table,
       AggregationStateHashTableBase *destination_hash_table) const;
 
+  template <typename HandleT, typename HashTableT>
+  void mergeGroupByHashTablesHelperFast(
+      const AggregationStateHashTableBase &source_hash_table,
+      AggregationStateHashTableBase *destination_hash_table) const;
+
+
  private:
   DISALLOW_COPY_AND_ASSIGN(AggregationConcreteHandle);
 };
@@ -302,6 +414,12 @@ class HashTableAggregateFinalizer {
     output_column_vector_->appendTypedValue(handle_.finalizeHashTableEntry(group_state));
   }
 
+  inline void operator()(const std::vector<TypedValue> &group_by_key,
+                         const unsigned char *byte_ptr) {
+    group_by_keys_->emplace_back(group_by_key);
+    output_column_vector_->appendTypedValue(handle_.finalizeHashTableEntryFast(byte_ptr));
+  }
+
  private:
   const HandleT &handle_;
   std::vector<std::vector<TypedValue>> *group_by_keys_;
@@ -414,6 +532,42 @@ void AggregationConcreteHandle::aggregateOnDistinctifyHashTableForGroupByUnaryHe
 
 template <typename HandleT,
           typename HashTableT>
+void AggregationConcreteHandle::aggregateOnDistinctifyHashTableForGroupByUnaryHelperFast(
+    const AggregationStateHashTableBase &distinctify_hash_table,
+    AggregationStateHashTableBase *aggregation_hash_table) const {
+  const HandleT& handle = static_cast<const HandleT&>(*this);
+  HashTableT *target_hash_table = static_cast<HashTableT*>(aggregation_hash_table);
+
+  // A lambda function which will be called on each key-value pair from the
+  // distinctify hash table.
+  const auto aggregate_functor = [&handle, &target_hash_table](
+      std::vector<TypedValue> &key,
+      const bool &dumb_placeholder) {
+    // For each (composite) key vector in the distinctify hash table with size N.
+    // The first N-1 entries are GROUP BY columns and the last entry is the argument
+    // to be aggregated on.
+    const TypedValue argument(std::move(key.back()));
+    key.pop_back();
+
+    // An upserter as lambda function for aggregating the argument into its
+    // GROUP BY group's entry inside aggregation_hash_table.
+    const auto upserter = [&handle, &argument](std::uint8_t *state) {
+      handle.iterateUnaryInlFast(argument, state+sizeof(SpinMutex));
+    };
+
+    target_hash_table->upsertCompositeKeyFast(key, nullptr, &upserter);
+  };
+
+  const HashTableT &source_hash_table =
+      static_cast<const HashTableT&>(distinctify_hash_table);
+  // Invoke the lambda function "aggregate_functor" on each composite key vector
+  // from the distinctify hash table.
+  source_hash_table.forEachCompositeKeyFast(&aggregate_functor);
+}
+
+
+template <typename HandleT,
+          typename HashTableT>
 ColumnVector* AggregationConcreteHandle::finalizeHashTableHelper(
     const Type &result_type,
     const AggregationStateHashTableBase &hash_table,
@@ -463,6 +617,59 @@ ColumnVector* AggregationConcreteHandle::finalizeHashTableHelper(
 }
 
 template <typename HandleT,
+          typename HashTableT>
+ColumnVector* AggregationConcreteHandle::finalizeHashTableHelperFast(
+    const Type &result_type,
+    const AggregationStateHashTableBase &hash_table,
+    std::vector<std::vector<TypedValue>> *group_by_keys,
+    int index) const {
+  const HandleT &handle = static_cast<const HandleT&>(*this);
+  const HashTableT &hash_table_concrete = static_cast<const HashTableT&>(hash_table);
+
+  if (group_by_keys->empty()) {
+    if (NativeColumnVector::UsableForType(result_type)) {
+      NativeColumnVector *result = new NativeColumnVector(result_type,
+                                                          hash_table_concrete.numEntries());
+      HashTableAggregateFinalizer<HandleT, NativeColumnVector> finalizer(
+          handle,
+          group_by_keys,
+          result);
+      hash_table_concrete.forEachCompositeKeyFast(&finalizer, index);
+      return result;
+    } else {
+      IndirectColumnVector *result = new IndirectColumnVector(result_type,
+                                                              hash_table_concrete.numEntries());
+      HashTableAggregateFinalizer<HandleT, IndirectColumnVector> finalizer(
+          handle,
+          group_by_keys,
+          result);
+      hash_table_concrete.forEachCompositeKeyFast(&finalizer, index);
+      return result;
+    }
+  } else {
+    if (NativeColumnVector::UsableForType(result_type)) {
+      NativeColumnVector *result = new NativeColumnVector(result_type,
+                                                          group_by_keys->size());
+      for (const std::vector<TypedValue> &group_by_key : *group_by_keys) {
+        result->appendTypedValue(finalizeGroupInHashTableFast<HandleT, HashTableT>(hash_table,
+                                                                                   group_by_key,
+                                                                                   index));
+      }
+      return result;
+    } else {
+      IndirectColumnVector *result = new IndirectColumnVector(result_type,
+                                                              hash_table_concrete.numEntries());
+      for (const std::vector<TypedValue> &group_by_key : *group_by_keys) {
+        result->appendTypedValue(finalizeGroupInHashTableFast<HandleT, HashTableT>(hash_table,
+                                                                                   group_by_key,
+                                                                                   index));
+      }
+      return result;
+    }
+  }
+}
+
+template <typename HandleT,
           typename StateT,
           typename HashTableT>
 void AggregationConcreteHandle::mergeGroupByHashTablesHelper(
@@ -478,6 +685,22 @@ void AggregationConcreteHandle::mergeGroupByHashTablesHelper(
   source_hash_table_concrete.forEachCompositeKey(&merger);
 }
 
+template <typename HandleT,
+          typename HashTableT>
+void AggregationConcreteHandle::mergeGroupByHashTablesHelperFast(
+    const AggregationStateHashTableBase &source_hash_table,
+    AggregationStateHashTableBase *destination_hash_table) const {
+  const HandleT &handle = static_cast<const HandleT &>(*this);
+  const HashTableT &source_hash_table_concrete =
+      static_cast<const HashTableT &>(source_hash_table);
+
+  HashTableMergerFast<HandleT, HashTableT> merger(handle,
+                                              destination_hash_table);
+
+  source_hash_table_concrete.forEachCompositeKeyFast(&merger);
+}
+
+
 }  // namespace quickstep
 
 #endif  // QUICKSTEP_EXPRESSIONS_AGGREGATION_AGGREGATION_CONCRETE_HANDLE_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/expressions/aggregation/AggregationHandle.hpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandle.hpp b/expressions/aggregation/AggregationHandle.hpp
index cdebb03..d9c3897 100644
--- a/expressions/aggregation/AggregationHandle.hpp
+++ b/expressions/aggregation/AggregationHandle.hpp
@@ -265,7 +265,7 @@ class AggregationHandle {
    **/
   virtual ColumnVector* finalizeHashTable(
       const AggregationStateHashTableBase &hash_table,
-      std::vector<std::vector<TypedValue>> *group_by_keys) const = 0;
+      std::vector<std::vector<TypedValue>> *group_by_keys, int index) const = 0;
 
   /**
    * @brief Create a new HashTable for the distinctify step for DISTINCT aggregation.
@@ -362,6 +362,12 @@ class AggregationHandle {
       const AggregationStateHashTableBase &source_hash_table,
       AggregationStateHashTableBase *destination_hash_table) const = 0;
 
+  virtual size_t getPayloadSize() const {return 8;}
+  virtual void setPayloadOffset(std::size_t) {}
+  virtual void iterateInlFast(const std::vector<TypedValue> &arguments, uint8_t *byte_ptr) {}
+  virtual void mergeStatesFast(const uint8_t *src, uint8_t *dst) const {}
+  virtual void initPayload(uint8_t *byte_ptr) {}
+
  protected:
   AggregationHandle() {
   }

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/expressions/aggregation/AggregationHandleAvg.cpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleAvg.cpp b/expressions/aggregation/AggregationHandleAvg.cpp
index 42a2fb9..d0bd3b8 100644
--- a/expressions/aggregation/AggregationHandleAvg.cpp
+++ b/expressions/aggregation/AggregationHandleAvg.cpp
@@ -137,8 +137,7 @@ void AggregationHandleAvg::aggregateValueAccessorIntoHashTable(
     AggregationStateHashTableBase *hash_table) const {
   DCHECK_EQ(1u, argument_ids.size())
       << "Got wrong number of arguments for AVG: " << argument_ids.size();
-
-  aggregateValueAccessorIntoHashTableUnaryHelper<
+/*  aggregateValueAccessorIntoHashTableUnaryHelper<
       AggregationHandleAvg,
       AggregationStateAvg,
       AggregationStateHashTable<AggregationStateAvg>>(
@@ -146,7 +145,14 @@ void AggregationHandleAvg::aggregateValueAccessorIntoHashTable(
           argument_ids.front(),
           group_by_key_ids,
           blank_state_,
-          hash_table);
+          hash_table); */
+
+/*     static_cast<AggregationStateFastHashTable *>(hash_table)->upsertValueAccessorCompositeKeyFast(
+      argument_ids.front(),
+      accessor,
+      group_by_key_ids,
+      true,
+      const_cast<AggregationHandleAvg *>(this));*/
 }
 
 void AggregationHandleAvg::mergeStates(
@@ -161,6 +167,19 @@ void AggregationHandleAvg::mergeStates(
                                                                   avg_source.sum_);
 }
 
+void AggregationHandleAvg::mergeStatesFast(
+    const uint8_t *source,
+    uint8_t *destination) const {
+    const TypedValue *src_sum_ptr = reinterpret_cast<const TypedValue *>(source + blank_state_.sum_offset);
+    const std::int64_t *src_count_ptr = reinterpret_cast<const std::int64_t *>(source + blank_state_.count_offset);
+    TypedValue *dst_sum_ptr = reinterpret_cast<TypedValue *>(destination+blank_state_.sum_offset);
+    std::int64_t *dst_count_ptr = reinterpret_cast<std::int64_t *>(destination + blank_state_.count_offset);
+    (*dst_count_ptr) += (*src_count_ptr);
+    *dst_sum_ptr = merge_add_operator_->applyToTypedValues(*dst_sum_ptr, *src_sum_ptr);
+}
+
+
+
 TypedValue AggregationHandleAvg::finalize(const AggregationState &state) const {
   const AggregationStateAvg &agg_state = static_cast<const AggregationStateAvg&>(state);
   if (agg_state.count_ == 0) {
@@ -175,12 +194,14 @@ TypedValue AggregationHandleAvg::finalize(const AggregationState &state) const {
 
 ColumnVector* AggregationHandleAvg::finalizeHashTable(
     const AggregationStateHashTableBase &hash_table,
-    std::vector<std::vector<TypedValue>> *group_by_keys) const {
-  return finalizeHashTableHelper<AggregationHandleAvg,
-                                 AggregationStateHashTable<AggregationStateAvg>>(
+    std::vector<std::vector<TypedValue>> *group_by_keys,
+    int index) const {
+  return finalizeHashTableHelperFast<AggregationHandleAvg,
+                                 AggregationStateFastHashTable>(
       *result_type_,
       hash_table,
-      group_by_keys);
+      group_by_keys,
+      index);
 }
 
 AggregationState* AggregationHandleAvg::aggregateOnDistinctifyHashTableForSingle(
@@ -206,9 +227,8 @@ void AggregationHandleAvg::aggregateOnDistinctifyHashTableForGroupBy(
 void AggregationHandleAvg::mergeGroupByHashTables(
     const AggregationStateHashTableBase &source_hash_table,
     AggregationStateHashTableBase *destination_hash_table) const {
-  mergeGroupByHashTablesHelper<AggregationHandleAvg,
-                               AggregationStateAvg,
-                               AggregationStateHashTable<AggregationStateAvg>>(
+  mergeGroupByHashTablesHelperFast<AggregationHandleAvg,
+                               AggregationStateFastHashTable>(
       source_hash_table, destination_hash_table);
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/expressions/aggregation/AggregationHandleAvg.hpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleAvg.hpp b/expressions/aggregation/AggregationHandleAvg.hpp
index 4ad4b21..fe178f6 100644
--- a/expressions/aggregation/AggregationHandleAvg.hpp
+++ b/expressions/aggregation/AggregationHandleAvg.hpp
@@ -29,6 +29,7 @@
 #include "expressions/aggregation/AggregationConcreteHandle.hpp"
 #include "expressions/aggregation/AggregationHandle.hpp"
 #include "storage/HashTableBase.hpp"
+#include "storage/FastHashTable.hpp"
 #include "threading/SpinMutex.hpp"
 #include "types/Type.hpp"
 #include "types/TypedValue.hpp"
@@ -57,7 +58,10 @@ class AggregationStateAvg : public AggregationState {
    */
   AggregationStateAvg(const AggregationStateAvg &orig)
       : sum_(orig.sum_),
-        count_(orig.count_) {
+        count_(orig.count_),
+        sum_offset(orig.sum_offset),
+        count_offset(orig.count_offset),
+        mutex_offset(orig.mutex_offset) {
   }
 
   /**
@@ -65,11 +69,19 @@ class AggregationStateAvg : public AggregationState {
    */
   ~AggregationStateAvg() override {}
 
+  size_t getPayloadSize() const {
+     size_t p1 = reinterpret_cast<size_t>(&sum_);
+     size_t p2 = reinterpret_cast<size_t>(&mutex_);
+     return (p2-p1);
+  }
+
  private:
   friend class AggregationHandleAvg;
 
   AggregationStateAvg()
-      : sum_(0), count_(0) {
+      : sum_(0), count_(0), sum_offset(0),
+        count_offset(reinterpret_cast<uint8_t *>(&count_)-reinterpret_cast<uint8_t *>(&sum_)),
+        mutex_offset(reinterpret_cast<uint8_t *>(&mutex_)-reinterpret_cast<uint8_t *>(&sum_)) {
   }
 
   // TODO(shoban): We might want to specialize sum_ and count_ to use atomics
@@ -77,6 +89,8 @@ class AggregationStateAvg : public AggregationState {
   TypedValue sum_;
   std::int64_t count_;
   SpinMutex mutex_;
+
+  int sum_offset, count_offset, mutex_offset;
 };
 
 /**
@@ -109,6 +123,26 @@ class AggregationHandleAvg : public AggregationConcreteHandle {
     ++state->count_;
   }
 
+  inline void iterateUnaryInlFast(const TypedValue &value, uint8_t *byte_ptr) {
+    DCHECK(value.isPlausibleInstanceOf(argument_type_.getSignature()));
+    if (value.isNull()) return;
+    TypedValue *sum_ptr = reinterpret_cast<TypedValue *>(byte_ptr + blank_state_.sum_offset);
+    std::int64_t *count_ptr = reinterpret_cast<std::int64_t *>(byte_ptr + blank_state_.count_offset);
+    *sum_ptr = fast_add_operator_->applyToTypedValues(*sum_ptr, value);
+    ++(*count_ptr);
+  }
+
+  inline void iterateInlFast(const std::vector<TypedValue> &arguments, uint8_t *byte_ptr) override {
+     iterateUnaryInlFast(arguments.front(), byte_ptr);
+  }
+
+  void initPayload(uint8_t *byte_ptr) override {
+    TypedValue *sum_ptr = reinterpret_cast<TypedValue *>(byte_ptr + blank_state_.sum_offset);
+    std::int64_t *count_ptr = reinterpret_cast<std::int64_t *>(byte_ptr + blank_state_.count_offset);
+    *sum_ptr = blank_state_.sum_;
+    *count_ptr = blank_state_.count_;
+  }
+
   AggregationState* accumulateColumnVectors(
       const std::vector<std::unique_ptr<ColumnVector>> &column_vectors) const override;
 
@@ -127,6 +161,9 @@ class AggregationHandleAvg : public AggregationConcreteHandle {
   void mergeStates(const AggregationState &source,
                    AggregationState *destination) const override;
 
+  void mergeStatesFast(const uint8_t *source,
+                   uint8_t *destination) const override;
+
   TypedValue finalize(const AggregationState &state) const override;
 
   inline TypedValue finalizeHashTableEntry(const AggregationState &state) const {
@@ -139,9 +176,24 @@ class AggregationHandleAvg : public AggregationConcreteHandle {
                                                 TypedValue(static_cast<double>(agg_state.count_)));
   }
 
+  inline TypedValue finalizeHashTableEntryFast(const uint8_t *byte_ptr) const {
+//    const AggregationStateAvg &agg_state = static_cast<const AggregationStateAvg&>(state);
+    // TODO(chasseur): Could improve performance further if we made a special
+    // version of finalizeHashTable() that collects all the sums into one
+    // ColumnVector and all the counts into another and then applies
+    // '*divide_operator_' to them in bulk.
+
+    uint8_t *value_ptr = const_cast<uint8_t*>(byte_ptr);
+    TypedValue *sum_ptr = reinterpret_cast<TypedValue *>(value_ptr + blank_state_.sum_offset);
+    std::int64_t *count_ptr = reinterpret_cast<std::int64_t *>(value_ptr + blank_state_.count_offset);
+    return divide_operator_->applyToTypedValues(*sum_ptr,
+                                                TypedValue(static_cast<double>(*count_ptr)));
+  }
+
   ColumnVector* finalizeHashTable(
       const AggregationStateHashTableBase &hash_table,
-      std::vector<std::vector<TypedValue>> *group_by_keys) const override;
+      std::vector<std::vector<TypedValue>> *group_by_keys,
+      int index) const override;
 
   /**
    * @brief Implementation of AggregationHandle::aggregateOnDistinctifyHashTableForSingle()
@@ -162,6 +214,10 @@ class AggregationHandleAvg : public AggregationConcreteHandle {
       const AggregationStateHashTableBase &source_hash_table,
       AggregationStateHashTableBase *destination_hash_table) const override;
 
+  size_t getPayloadSize() const override {
+      return blank_state_.getPayloadSize();
+  }
+
  private:
   friend class AggregateFunctionAvg;
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/expressions/aggregation/AggregationHandleCount.cpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleCount.cpp b/expressions/aggregation/AggregationHandleCount.cpp
index 964b7c2..dc11dac 100644
--- a/expressions/aggregation/AggregationHandleCount.cpp
+++ b/expressions/aggregation/AggregationHandleCount.cpp
@@ -135,18 +135,18 @@ template <bool count_star, bool nullable_type>
   if (count_star) {
     DCHECK_EQ(0u, argument_ids.size())
         << "Got wrong number of arguments for COUNT(*): " << argument_ids.size();
-    aggregateValueAccessorIntoHashTableNullaryHelper<
+/*    aggregateValueAccessorIntoHashTableNullaryHelper<
         AggregationHandleCount<count_star, nullable_type>,
         AggregationStateCount,
         AggregationStateHashTable<AggregationStateCount>>(
             accessor,
             group_by_key_ids,
             AggregationStateCount(),
-            hash_table);
+            hash_table);*/
   } else {
     DCHECK_EQ(1u, argument_ids.size())
         << "Got wrong number of arguments for COUNT: " << argument_ids.size();
-    aggregateValueAccessorIntoHashTableUnaryHelper<
+/*    aggregateValueAccessorIntoHashTableUnaryHelper<
         AggregationHandleCount<count_star, nullable_type>,
         AggregationStateCount,
         AggregationStateHashTable<AggregationStateCount>>(
@@ -154,7 +154,7 @@ template <bool count_star, bool nullable_type>
             argument_ids.front(),
             group_by_key_ids,
             AggregationStateCount(),
-            hash_table);
+            hash_table); */
   }
 }
 
@@ -170,14 +170,25 @@ template <bool count_star, bool nullable_type>
 }
 
 template <bool count_star, bool nullable_type>
+void AggregationHandleCount<count_star, nullable_type>::mergeStatesFast(
+    const uint8_t *source,
+    uint8_t *destination) const {
+    const std::int64_t *src_count_ptr = reinterpret_cast<const std::int64_t *>(source);
+    std::int64_t *dst_count_ptr = reinterpret_cast<std::int64_t *>(destination);
+    (*dst_count_ptr) += (*src_count_ptr);
+}
+
+template <bool count_star, bool nullable_type>
     ColumnVector* AggregationHandleCount<count_star, nullable_type>::finalizeHashTable(
         const AggregationStateHashTableBase &hash_table,
-        std::vector<std::vector<TypedValue>> *group_by_keys) const {
-  return finalizeHashTableHelper<AggregationHandleCount<count_star, nullable_type>,
-                                 AggregationStateHashTable<AggregationStateCount>>(
+        std::vector<std::vector<TypedValue>> *group_by_keys,
+        int index) const {
+  return finalizeHashTableHelperFast<AggregationHandleCount<count_star, nullable_type>,
+                                 AggregationStateFastHashTable>(
       TypeFactory::GetType(kLong),
       hash_table,
-      group_by_keys);
+      group_by_keys,
+      index);
 }
 
 template <bool count_star, bool nullable_type>
@@ -197,12 +208,10 @@ void AggregationHandleCount<count_star, nullable_type>
         const AggregationStateHashTableBase &distinctify_hash_table,
         AggregationStateHashTableBase *aggregation_hash_table) const {
   DCHECK_EQ(count_star, false);
-  aggregateOnDistinctifyHashTableForGroupByUnaryHelper<
+  aggregateOnDistinctifyHashTableForGroupByUnaryHelperFast<
       AggregationHandleCount<count_star, nullable_type>,
-      AggregationStateCount,
-      AggregationStateHashTable<AggregationStateCount>>(
+      AggregationStateFastHashTable>(
           distinctify_hash_table,
-          AggregationStateCount(),
           aggregation_hash_table);
 }
 
@@ -210,10 +219,9 @@ template <bool count_star, bool nullable_type>
 void AggregationHandleCount<count_star, nullable_type>::mergeGroupByHashTables(
     const AggregationStateHashTableBase &source_hash_table,
     AggregationStateHashTableBase *destination_hash_table) const {
-  mergeGroupByHashTablesHelper<
+  mergeGroupByHashTablesHelperFast<
       AggregationHandleCount,
-      AggregationStateCount,
-      AggregationStateHashTable<AggregationStateCount>>(source_hash_table,
+      AggregationStateFastHashTable>(source_hash_table,
                                                         destination_hash_table);
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/expressions/aggregation/AggregationHandleCount.hpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleCount.hpp b/expressions/aggregation/AggregationHandleCount.hpp
index 50138b9..4046106 100644
--- a/expressions/aggregation/AggregationHandleCount.hpp
+++ b/expressions/aggregation/AggregationHandleCount.hpp
@@ -30,6 +30,7 @@
 #include "expressions/aggregation/AggregationConcreteHandle.hpp"
 #include "expressions/aggregation/AggregationHandle.hpp"
 #include "storage/HashTableBase.hpp"
+#include "storage/FastHashTable.hpp"
 #include "types/TypedValue.hpp"
 #include "utility/Macros.hpp"
 
@@ -62,6 +63,10 @@ class AggregationStateCount : public AggregationState {
    */
   ~AggregationStateCount() override {}
 
+  size_t getPayloadSize() const {
+     return sizeof(count_);
+  }
+
  private:
   friend class AggregationHandleCount<false, false>;
   friend class AggregationHandleCount<false, true>;
@@ -108,6 +113,11 @@ class AggregationHandleCount : public AggregationConcreteHandle {
     state->count_.fetch_add(1, std::memory_order_relaxed);
   }
 
+  inline void iterateNullaryInlFast(uint8_t *byte_ptr) {
+      std::int64_t *count_ptr = reinterpret_cast<std::int64_t *>(byte_ptr);
+      (*count_ptr)++;
+  }
+
   /**
    * @brief Iterate with count aggregation state.
    */
@@ -117,6 +127,25 @@ class AggregationHandleCount : public AggregationConcreteHandle {
     }
   }
 
+  inline void iterateUnaryInlFast(const TypedValue &value, uint8_t *byte_ptr) const {
+    if ((!nullable_type) || (!value.isNull())) {
+      std::int64_t *count_ptr = reinterpret_cast<std::int64_t *>(byte_ptr);
+      (*count_ptr)++;
+    }
+  }
+
+  inline void iterateInlFast(const std::vector<TypedValue> &arguments, uint8_t *byte_ptr) override {
+     if (arguments.size())
+         iterateUnaryInlFast(arguments.front(), byte_ptr);
+     else
+         iterateNullaryInlFast(byte_ptr);
+  }
+
+  void initPayload(uint8_t *byte_ptr) override {
+    std::int64_t *count_ptr = reinterpret_cast<std::int64_t *>(byte_ptr);
+    *count_ptr = 0;
+  }
+
   AggregationState* accumulateNullary(const std::size_t num_tuples) const override {
     return new AggregationStateCount(num_tuples);
   }
@@ -139,6 +168,9 @@ class AggregationHandleCount : public AggregationConcreteHandle {
   void mergeStates(const AggregationState &source,
                    AggregationState *destination) const override;
 
+  void mergeStatesFast(const uint8_t *source,
+                   uint8_t *destination) const override;
+
   TypedValue finalize(const AggregationState &state) const override {
     return TypedValue(static_cast<const AggregationStateCount&>(state).count_.load(std::memory_order_relaxed));
   }
@@ -147,9 +179,21 @@ class AggregationHandleCount : public AggregationConcreteHandle {
     return TypedValue(static_cast<const AggregationStateCount&>(state).count_.load(std::memory_order_relaxed));
   }
 
+  inline TypedValue finalizeHashTableEntryFast(const uint8_t *byte_ptr) const {
+//    const AggregationStateAvg &agg_state = static_cast<const AggregationStateAvg&>(state);
+    // TODO(chasseur): Could improve performance further if we made a special
+    // version of finalizeHashTable() that collects all the sums into one
+    // ColumnVector and all the counts into another and then applies
+    // '*divide_operator_' to them in bulk.
+
+    const std::int64_t *count_ptr = reinterpret_cast<const std::int64_t *>(byte_ptr);
+    return TypedValue(*count_ptr);
+  }
+
   ColumnVector* finalizeHashTable(
       const AggregationStateHashTableBase &hash_table,
-      std::vector<std::vector<TypedValue>> *group_by_keys) const override;
+      std::vector<std::vector<TypedValue>> *group_by_keys,
+      int index) const override;
 
   /**
    * @brief Implementation of AggregationHandle::aggregateOnDistinctifyHashTableForSingle()
@@ -170,6 +214,10 @@ class AggregationHandleCount : public AggregationConcreteHandle {
       const AggregationStateHashTableBase &source_hash_table,
       AggregationStateHashTableBase *destination_hash_table) const override;
 
+  size_t getPayloadSize() const override {
+      return sizeof(std::int64_t);
+  }
+
  private:
   friend class AggregateFunctionCount;
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/expressions/aggregation/AggregationHandleDistinct.cpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleDistinct.cpp b/expressions/aggregation/AggregationHandleDistinct.cpp
index fe8ffcf..0088239 100644
--- a/expressions/aggregation/AggregationHandleDistinct.cpp
+++ b/expressions/aggregation/AggregationHandleDistinct.cpp
@@ -63,7 +63,7 @@ void AggregationHandleDistinct::aggregateValueAccessorIntoHashTable(
 
 ColumnVector* AggregationHandleDistinct::finalizeHashTable(
     const AggregationStateHashTableBase &hash_table,
-    std::vector<std::vector<TypedValue>> *group_by_keys) const {
+    std::vector<std::vector<TypedValue>> *group_by_keys, int index) const {
   DCHECK(group_by_keys->empty());
 
   const auto keys_retriever = [&group_by_keys](std::vector<TypedValue> &group_by_key,

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/expressions/aggregation/AggregationHandleDistinct.hpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleDistinct.hpp b/expressions/aggregation/AggregationHandleDistinct.hpp
index 6342c2b..1e36845 100644
--- a/expressions/aggregation/AggregationHandleDistinct.hpp
+++ b/expressions/aggregation/AggregationHandleDistinct.hpp
@@ -107,7 +107,7 @@ class AggregationHandleDistinct : public AggregationConcreteHandle {
 
   ColumnVector* finalizeHashTable(
       const AggregationStateHashTableBase &hash_table,
-      std::vector<std::vector<TypedValue>> *group_by_keys) const override;
+      std::vector<std::vector<TypedValue>> *group_by_keys, int index) const override;
 
   void mergeGroupByHashTables(
       const AggregationStateHashTableBase &source_hash_table,

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/expressions/aggregation/AggregationHandleMax.cpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleMax.cpp b/expressions/aggregation/AggregationHandleMax.cpp
index a7a4a52..f0eebde 100644
--- a/expressions/aggregation/AggregationHandleMax.cpp
+++ b/expressions/aggregation/AggregationHandleMax.cpp
@@ -87,7 +87,7 @@ void AggregationHandleMax::aggregateValueAccessorIntoHashTable(
   DCHECK_EQ(1u, argument_ids.size())
       << "Got wrong number of arguments for MAX: " << argument_ids.size();
 
-  aggregateValueAccessorIntoHashTableUnaryHelper<
+/*  aggregateValueAccessorIntoHashTableUnaryHelper<
       AggregationHandleMax,
       AggregationStateMax,
       AggregationStateHashTable<AggregationStateMax>>(
@@ -95,7 +95,7 @@ void AggregationHandleMax::aggregateValueAccessorIntoHashTable(
           argument_ids.front(),
           group_by_key_ids,
           AggregationStateMax(type_),
-          hash_table);
+          hash_table);*/
 }
 
 void AggregationHandleMax::mergeStates(
@@ -109,14 +109,26 @@ void AggregationHandleMax::mergeStates(
   }
 }
 
+void AggregationHandleMax::mergeStatesFast(
+    const std::uint8_t *source,
+    std::uint8_t *destination) const {
+    const TypedValue *src_max_ptr = reinterpret_cast<const TypedValue *>(source);
+    TypedValue *dst_max_ptr = reinterpret_cast<TypedValue *>(destination);
+    if (!(src_max_ptr->isNull())) {
+      compareAndUpdateFast(dst_max_ptr, *src_max_ptr);
+  }
+}
+
 ColumnVector* AggregationHandleMax::finalizeHashTable(
     const AggregationStateHashTableBase &hash_table,
-    std::vector<std::vector<TypedValue>> *group_by_keys) const {
-  return finalizeHashTableHelper<AggregationHandleMax,
-                                 AggregationStateHashTable<AggregationStateMax>>(
+    std::vector<std::vector<TypedValue>> *group_by_keys,
+    int index) const {
+  return finalizeHashTableHelperFast<AggregationHandleMax,
+                                 AggregationStateFastHashTable>(
       type_.getNullableVersion(),
       hash_table,
-      group_by_keys);
+      group_by_keys,
+      index);
 }
 
 AggregationState* AggregationHandleMax::aggregateOnDistinctifyHashTableForSingle(
@@ -142,9 +154,8 @@ void AggregationHandleMax::aggregateOnDistinctifyHashTableForGroupBy(
 void AggregationHandleMax::mergeGroupByHashTables(
     const AggregationStateHashTableBase &source_hash_table,
     AggregationStateHashTableBase *destination_hash_table) const {
-  mergeGroupByHashTablesHelper<AggregationHandleMax,
-                               AggregationStateMax,
-                               AggregationStateHashTable<AggregationStateMax>>(
+  mergeGroupByHashTablesHelperFast<AggregationHandleMax,
+                               AggregationStateFastHashTable>(
       source_hash_table, destination_hash_table);
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/expressions/aggregation/AggregationHandleMax.hpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleMax.hpp b/expressions/aggregation/AggregationHandleMax.hpp
index 5af5a12..ce7c702 100644
--- a/expressions/aggregation/AggregationHandleMax.hpp
+++ b/expressions/aggregation/AggregationHandleMax.hpp
@@ -29,6 +29,7 @@
 #include "expressions/aggregation/AggregationConcreteHandle.hpp"
 #include "expressions/aggregation/AggregationHandle.hpp"
 #include "storage/HashTableBase.hpp"
+#include "storage/FastHashTable.hpp"
 #include "threading/SpinMutex.hpp"
 #include "types/Type.hpp"
 #include "types/TypedValue.hpp"
@@ -105,6 +106,22 @@ class AggregationHandleMax : public AggregationConcreteHandle {
     compareAndUpdate(static_cast<AggregationStateMax*>(state), value);
   }
 
+  inline void iterateUnaryInlFast(const TypedValue &value, std::uint8_t *byte_ptr) const {
+    DCHECK(value.isPlausibleInstanceOf(type_.getSignature()));
+    TypedValue *max_ptr = reinterpret_cast<TypedValue *>(byte_ptr);
+    compareAndUpdateFast(max_ptr, value);
+  }
+
+  inline void iterateInlFast(const std::vector<TypedValue> &arguments, uint8_t *byte_ptr) override {
+    iterateUnaryInlFast(arguments.front(), byte_ptr);
+  }
+
+  void initPayload(uint8_t *byte_ptr) override {
+    TypedValue *max_ptr = reinterpret_cast<TypedValue *>(byte_ptr);
+    TypedValue t1 = (type_.getNullableVersion().makeNullValue());
+    *max_ptr = t1;
+  }
+
   AggregationState* accumulateColumnVectors(
       const std::vector<std::unique_ptr<ColumnVector>> &column_vectors) const override;
 
@@ -123,6 +140,9 @@ class AggregationHandleMax : public AggregationConcreteHandle {
   void mergeStates(const AggregationState &source,
                    AggregationState *destination) const override;
 
+  void mergeStatesFast(const std::uint8_t *source,
+                   std::uint8_t *destination) const override;
+
   TypedValue finalize(const AggregationState &state) const override {
     return TypedValue(static_cast<const AggregationStateMax&>(state).max_);
   }
@@ -131,9 +151,15 @@ class AggregationHandleMax : public AggregationConcreteHandle {
     return TypedValue(static_cast<const AggregationStateMax&>(state).max_);
   }
 
+  inline TypedValue finalizeHashTableEntryFast(const std::uint8_t *byte_ptr) const {
+    const TypedValue *max_ptr = reinterpret_cast<const TypedValue *>(byte_ptr);
+    return TypedValue(*max_ptr);
+  }
+
   ColumnVector* finalizeHashTable(
       const AggregationStateHashTableBase &hash_table,
-      std::vector<std::vector<TypedValue>> *group_by_keys) const override;
+      std::vector<std::vector<TypedValue>> *group_by_keys,
+      int index) const override;
 
   /**
    * @brief Implementation of AggregationHandle::aggregateOnDistinctifyHashTableForSingle()
@@ -155,6 +181,10 @@ class AggregationHandleMax : public AggregationConcreteHandle {
       const AggregationStateHashTableBase &source_hash_table,
       AggregationStateHashTableBase *destination_hash_table) const override;
 
+  size_t getPayloadSize() const override {
+      return sizeof(TypedValue);
+  }
+
  private:
   friend class AggregateFunctionMax;
 
@@ -181,6 +211,13 @@ class AggregationHandleMax : public AggregationConcreteHandle {
     }
   }
 
+  inline void compareAndUpdateFast(TypedValue *max_ptr, const TypedValue &value) const {
+    if (value.isNull()) return;
+    if (max_ptr->isNull() || fast_comparator_->compareTypedValues(value, *max_ptr)) {
+      *max_ptr = value;
+    }
+  }
+
   const Type &type_;
   std::unique_ptr<UncheckedComparator> fast_comparator_;
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/expressions/aggregation/AggregationHandleMin.cpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleMin.cpp b/expressions/aggregation/AggregationHandleMin.cpp
index ca9b163..cbedd9b 100644
--- a/expressions/aggregation/AggregationHandleMin.cpp
+++ b/expressions/aggregation/AggregationHandleMin.cpp
@@ -89,7 +89,7 @@ void AggregationHandleMin::aggregateValueAccessorIntoHashTable(
   DCHECK_EQ(1u, argument_ids.size())
       << "Got wrong number of arguments for MIN: " << argument_ids.size();
 
-  aggregateValueAccessorIntoHashTableUnaryHelper<
+/*  aggregateValueAccessorIntoHashTableUnaryHelper<
       AggregationHandleMin,
       AggregationStateMin,
       AggregationStateHashTable<AggregationStateMin>>(
@@ -97,7 +97,7 @@ void AggregationHandleMin::aggregateValueAccessorIntoHashTable(
           argument_ids.front(),
           group_by_key_ids,
           AggregationStateMin(type_),
-          hash_table);
+          hash_table);*/
 }
 
 void AggregationHandleMin::mergeStates(
@@ -111,14 +111,27 @@ void AggregationHandleMin::mergeStates(
   }
 }
 
+void AggregationHandleMin::mergeStatesFast(
+    const std::uint8_t *source,
+    std::uint8_t *destination) const {
+    const TypedValue *src_min_ptr = reinterpret_cast<const TypedValue *>(source);
+    TypedValue *dst_min_ptr = reinterpret_cast<TypedValue *>(destination);
+
+    if (!(src_min_ptr->isNull())) {
+      compareAndUpdateFast(dst_min_ptr, *src_min_ptr);
+    }
+}
+
 ColumnVector* AggregationHandleMin::finalizeHashTable(
     const AggregationStateHashTableBase &hash_table,
-    std::vector<std::vector<TypedValue>> *group_by_keys) const {
-  return finalizeHashTableHelper<AggregationHandleMin,
-                                 AggregationStateHashTable<AggregationStateMin>>(
+    std::vector<std::vector<TypedValue>> *group_by_keys,
+    int index) const {
+  return finalizeHashTableHelperFast<AggregationHandleMin,
+                                 AggregationStateFastHashTable>(
       type_.getNonNullableVersion(),
       hash_table,
-      group_by_keys);
+      group_by_keys,
+      index);
 }
 
 AggregationState* AggregationHandleMin::aggregateOnDistinctifyHashTableForSingle(
@@ -144,9 +157,8 @@ void AggregationHandleMin::aggregateOnDistinctifyHashTableForGroupBy(
 void AggregationHandleMin::mergeGroupByHashTables(
     const AggregationStateHashTableBase &source_hash_table,
     AggregationStateHashTableBase *destination_hash_table) const {
-  mergeGroupByHashTablesHelper<AggregationHandleMin,
-                               AggregationStateMin,
-                               AggregationStateHashTable<AggregationStateMin>>(
+  mergeGroupByHashTablesHelperFast<AggregationHandleMin,
+                               AggregationStateFastHashTable>(
       source_hash_table, destination_hash_table);
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/expressions/aggregation/AggregationHandleMin.hpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleMin.hpp b/expressions/aggregation/AggregationHandleMin.hpp
index f68bb9d..7c7869e 100644
--- a/expressions/aggregation/AggregationHandleMin.hpp
+++ b/expressions/aggregation/AggregationHandleMin.hpp
@@ -29,6 +29,7 @@
 #include "expressions/aggregation/AggregationConcreteHandle.hpp"
 #include "expressions/aggregation/AggregationHandle.hpp"
 #include "storage/HashTableBase.hpp"
+#include "storage/FastHashTable.hpp"
 #include "threading/SpinMutex.hpp"
 #include "types/Type.hpp"
 #include "types/TypedValue.hpp"
@@ -64,6 +65,11 @@ class AggregationStateMin : public AggregationState {
    */
   ~AggregationStateMin() override {}
 
+  size_t getPayloadSize() const {
+     return sizeof(TypedValue);
+  }
+
+
  private:
   friend class AggregationHandleMin;
 
@@ -104,6 +110,22 @@ class AggregationHandleMin : public AggregationConcreteHandle {
     compareAndUpdate(state, value);
   }
 
+  inline void iterateUnaryInlFast(const TypedValue &value, uint8_t *byte_ptr) {
+      DCHECK(value.isPlausibleInstanceOf(type_.getSignature()));
+      TypedValue *min_ptr = reinterpret_cast<TypedValue *>(byte_ptr);
+      compareAndUpdateFast(min_ptr, value);
+  }
+
+  inline void iterateInlFast(const std::vector<TypedValue> &arguments, uint8_t *byte_ptr) override {
+    iterateUnaryInlFast(arguments.front(), byte_ptr);
+  }
+
+  void initPayload(uint8_t *byte_ptr) override {
+    TypedValue *min_ptr = reinterpret_cast<TypedValue *>(byte_ptr);
+    TypedValue t1 = (type_.getNullableVersion().makeNullValue());
+    *min_ptr = t1;
+  }
+
   AggregationState* accumulateColumnVectors(
       const std::vector<std::unique_ptr<ColumnVector>> &column_vectors) const override;
 
@@ -122,6 +144,9 @@ class AggregationHandleMin : public AggregationConcreteHandle {
   void mergeStates(const AggregationState &source,
                    AggregationState *destination) const override;
 
+  void mergeStatesFast(const uint8_t *source,
+                   uint8_t *destination) const override;
+
   TypedValue finalize(const AggregationState &state) const override {
     return static_cast<const AggregationStateMin&>(state).min_;
   }
@@ -130,9 +155,15 @@ class AggregationHandleMin : public AggregationConcreteHandle {
     return static_cast<const AggregationStateMin&>(state).min_;
   }
 
+  inline TypedValue finalizeHashTableEntryFast(const std::uint8_t *byte_ptr) const {
+    const TypedValue *min_ptr = reinterpret_cast<const TypedValue *>(byte_ptr);
+    return TypedValue(*min_ptr);
+  }
+
   ColumnVector* finalizeHashTable(
       const AggregationStateHashTableBase &hash_table,
-      std::vector<std::vector<TypedValue>> *group_by_keys) const override;
+      std::vector<std::vector<TypedValue>> *group_by_keys,
+      int index) const override;
 
   /**
    * @brief Implementation of AggregationHandle::aggregateOnDistinctifyHashTableForSingle()
@@ -153,6 +184,10 @@ class AggregationHandleMin : public AggregationConcreteHandle {
       const AggregationStateHashTableBase &source_hash_table,
       AggregationStateHashTableBase *destination_hash_table) const override;
 
+  size_t getPayloadSize() const override {
+      return sizeof(TypedValue);
+  }
+
  private:
   friend class AggregateFunctionMin;
 
@@ -178,6 +213,13 @@ class AggregationHandleMin : public AggregationConcreteHandle {
     }
   }
 
+  inline void compareAndUpdateFast(TypedValue *min_ptr, const TypedValue &value) const {
+    if (value.isNull()) return;
+    if (min_ptr->isNull() || fast_comparator_->compareTypedValues(value, *min_ptr)) {
+      *min_ptr = value;
+    }
+  }
+
   const Type &type_;
   std::unique_ptr<UncheckedComparator> fast_comparator_;
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/expressions/aggregation/AggregationHandleSum.cpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleSum.cpp b/expressions/aggregation/AggregationHandleSum.cpp
index 691ff39..ad486eb 100644
--- a/expressions/aggregation/AggregationHandleSum.cpp
+++ b/expressions/aggregation/AggregationHandleSum.cpp
@@ -93,7 +93,6 @@ AggregationState* AggregationHandleSum::accumulateColumnVectors(
     const std::vector<std::unique_ptr<ColumnVector>> &column_vectors) const {
   DCHECK_EQ(1u, column_vectors.size())
       << "Got wrong number of ColumnVectors for SUM: " << column_vectors.size();
-
   std::size_t num_tuples = 0;
   TypedValue cv_sum = fast_operator_->accumulateColumnVector(
       blank_state_.sum_,
@@ -127,7 +126,7 @@ void AggregationHandleSum::aggregateValueAccessorIntoHashTable(
   DCHECK_EQ(1u, argument_ids.size())
       << "Got wrong number of arguments for SUM: " << argument_ids.size();
 
-  aggregateValueAccessorIntoHashTableUnaryHelper<
+/*  aggregateValueAccessorIntoHashTableUnaryHelper<
       AggregationHandleSum,
       AggregationStateSum,
       AggregationStateHashTable<AggregationStateSum>>(
@@ -135,7 +134,7 @@ void AggregationHandleSum::aggregateValueAccessorIntoHashTable(
           argument_ids.front(),
           group_by_key_ids,
           blank_state_,
-          hash_table);
+          hash_table);*/
 }
 
 void AggregationHandleSum::mergeStates(
@@ -150,6 +149,17 @@ void AggregationHandleSum::mergeStates(
   sum_destination->null_ = sum_destination->null_ && sum_source.null_;
 }
 
+void AggregationHandleSum::mergeStatesFast(
+    const uint8_t *source,
+    uint8_t *destination) const {
+    const TypedValue *src_sum_ptr = reinterpret_cast<const TypedValue *>(source+blank_state_.sum_offset);
+    const bool *src_null_ptr = reinterpret_cast<const bool *>(source+blank_state_.null_offset);
+    TypedValue *dst_sum_ptr = reinterpret_cast<TypedValue *>(destination+blank_state_.sum_offset);
+    bool *dst_null_ptr = reinterpret_cast<bool *>(destination+blank_state_.null_offset);
+    *dst_sum_ptr = merge_operator_->applyToTypedValues(*dst_sum_ptr, *src_sum_ptr);
+    *dst_null_ptr = (*dst_null_ptr) && (*src_null_ptr);
+}
+
 TypedValue AggregationHandleSum::finalize(const AggregationState &state) const {
   const AggregationStateSum &agg_state = static_cast<const AggregationStateSum&>(state);
   if (agg_state.null_) {
@@ -162,12 +172,14 @@ TypedValue AggregationHandleSum::finalize(const AggregationState &state) const {
 
 ColumnVector* AggregationHandleSum::finalizeHashTable(
     const AggregationStateHashTableBase &hash_table,
-    std::vector<std::vector<TypedValue>> *group_by_keys) const {
-  return finalizeHashTableHelper<AggregationHandleSum,
-                                 AggregationStateHashTable<AggregationStateSum>>(
+    std::vector<std::vector<TypedValue>> *group_by_keys,
+    int index) const {
+  return finalizeHashTableHelperFast<AggregationHandleSum,
+                                 AggregationStateFastHashTable>(
       *result_type_,
       hash_table,
-      group_by_keys);
+      group_by_keys,
+      index);
 }
 
 AggregationState* AggregationHandleSum::aggregateOnDistinctifyHashTableForSingle(
@@ -193,9 +205,8 @@ void AggregationHandleSum::aggregateOnDistinctifyHashTableForGroupBy(
 void AggregationHandleSum::mergeGroupByHashTables(
     const AggregationStateHashTableBase &source_hash_table,
     AggregationStateHashTableBase *destination_hash_table) const {
-  mergeGroupByHashTablesHelper<AggregationHandleSum,
-                               AggregationStateSum,
-                               AggregationStateHashTable<AggregationStateSum>>(
+  mergeGroupByHashTablesHelperFast<AggregationHandleSum,
+                               AggregationStateFastHashTable>(
       source_hash_table, destination_hash_table);
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/expressions/aggregation/AggregationHandleSum.hpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleSum.hpp b/expressions/aggregation/AggregationHandleSum.hpp
index fdc0884..a8d2b5a 100644
--- a/expressions/aggregation/AggregationHandleSum.hpp
+++ b/expressions/aggregation/AggregationHandleSum.hpp
@@ -29,6 +29,7 @@
 #include "expressions/aggregation/AggregationConcreteHandle.hpp"
 #include "expressions/aggregation/AggregationHandle.hpp"
 #include "storage/HashTableBase.hpp"
+#include "storage/FastHashTable.hpp"
 #include "threading/SpinMutex.hpp"
 #include "types/Type.hpp"
 #include "types/TypedValue.hpp"
@@ -57,27 +58,39 @@ class AggregationStateSum : public AggregationState {
    */
   AggregationStateSum(const AggregationStateSum &orig)
       : sum_(orig.sum_),
-        null_(orig.null_) {
+        null_(orig.null_),
+        sum_offset(orig.sum_offset),
+        null_offset(orig.null_offset) {
   }
 
  private:
   friend class AggregationHandleSum;
 
   AggregationStateSum()
-      : sum_(0), null_(true) {
+      : sum_(0), null_(true), sum_offset(0),
+        null_offset(reinterpret_cast<uint8_t *>(&null_)-reinterpret_cast<uint8_t *>(&sum_)) {
   }
 
   AggregationStateSum(TypedValue &&sum, const bool is_null)
       : sum_(std::move(sum)), null_(is_null) {
   }
 
+  size_t getPayloadSize() const {
+     size_t p1 = reinterpret_cast<size_t>(&sum_);
+     size_t p2 = reinterpret_cast<size_t>(&mutex_);
+     return (p2-p1);
+  }
+
   // TODO(shoban): We might want to specialize sum_ to use atomics for int types
   // similar to in AggregationStateCount.
   TypedValue sum_;
   bool null_;
   SpinMutex mutex_;
+
+  int sum_offset, null_offset;
 };
 
+
 /**
  * @brief An aggregationhandle for sum.
  **/
@@ -105,6 +118,26 @@ class AggregationHandleSum : public AggregationConcreteHandle {
     state->null_ = false;
   }
 
+  inline void iterateUnaryInlFast(const TypedValue &value, uint8_t *byte_ptr) {
+    DCHECK(value.isPlausibleInstanceOf(argument_type_.getSignature()));
+    if (value.isNull()) return;
+    TypedValue *sum_ptr = reinterpret_cast<TypedValue *>(byte_ptr + blank_state_.sum_offset);
+    bool *null_ptr = reinterpret_cast<bool *>(byte_ptr + blank_state_.null_offset);
+    *sum_ptr = fast_operator_->applyToTypedValues(*sum_ptr, value);
+    *null_ptr = false;
+  }
+
+  inline void iterateInlFast(const std::vector<TypedValue> &arguments, uint8_t *byte_ptr) override {
+     iterateUnaryInlFast(arguments.front(), byte_ptr);
+  }
+
+  void initPayload(uint8_t *byte_ptr) override {
+    TypedValue *sum_ptr = reinterpret_cast<TypedValue *>(byte_ptr + blank_state_.sum_offset);
+    bool *null_ptr = reinterpret_cast<bool *>(byte_ptr + blank_state_.null_offset);
+    *sum_ptr = blank_state_.sum_;
+    *null_ptr = true;
+  }
+
   AggregationState* accumulateColumnVectors(
       const std::vector<std::unique_ptr<ColumnVector>> &column_vectors) const override;
 
@@ -123,15 +156,24 @@ class AggregationHandleSum : public AggregationConcreteHandle {
   void mergeStates(const AggregationState &source,
                    AggregationState *destination) const override;
 
+  void mergeStatesFast(const uint8_t *source,
+                   uint8_t *destination) const override;
+
   TypedValue finalize(const AggregationState &state) const override;
 
   inline TypedValue finalizeHashTableEntry(const AggregationState &state) const {
     return static_cast<const AggregationStateSum&>(state).sum_;
   }
 
+  inline TypedValue finalizeHashTableEntryFast(const uint8_t *byte_ptr) const {
+    uint8_t *value_ptr = const_cast<uint8_t*>(byte_ptr);
+    TypedValue *sum_ptr = reinterpret_cast<TypedValue *>(value_ptr + blank_state_.sum_offset);
+    return *sum_ptr;
+  }
+
   ColumnVector* finalizeHashTable(
       const AggregationStateHashTableBase &hash_table,
-      std::vector<std::vector<TypedValue>> *group_by_keys) const override;
+      std::vector<std::vector<TypedValue>> *group_by_keys, int index) const override;
 
   /**
    * @brief Implementation of AggregationHandle::aggregateOnDistinctifyHashTableForSingle()
@@ -152,6 +194,10 @@ class AggregationHandleSum : public AggregationConcreteHandle {
       const AggregationStateHashTableBase &source_hash_table,
       AggregationStateHashTableBase *destination_hash_table) const override;
 
+  size_t getPayloadSize() const override {
+      return blank_state_.getPayloadSize();
+  }
+
  private:
   friend class AggregateFunctionSum;
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/expressions/aggregation/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/expressions/aggregation/CMakeLists.txt b/expressions/aggregation/CMakeLists.txt
index 5744c52..6f259fa 100644
--- a/expressions/aggregation/CMakeLists.txt
+++ b/expressions/aggregation/CMakeLists.txt
@@ -144,9 +144,11 @@ target_link_libraries(quickstep_expressions_aggregation_AggregationConcreteHandl
                       glog
                       quickstep_catalog_CatalogTypedefs
                       quickstep_expressions_aggregation_AggregationHandle
+                      quickstep_storage_FastHashTable
                       quickstep_storage_HashTable
                       quickstep_storage_HashTableBase
                       quickstep_storage_HashTableFactory
+                      quickstep_threading_SpinMutex
                       quickstep_types_TypedValue
                       quickstep_types_containers_ColumnVector
                       quickstep_utility_Macros)
@@ -161,6 +163,7 @@ target_link_libraries(quickstep_expressions_aggregation_AggregationHandleAvg
                       quickstep_catalog_CatalogTypedefs
                       quickstep_expressions_aggregation_AggregationConcreteHandle
                       quickstep_expressions_aggregation_AggregationHandle
+                      quickstep_storage_FastHashTable
                       quickstep_storage_HashTable
                       quickstep_storage_HashTableBase
                       quickstep_storage_HashTableFactory
@@ -178,6 +181,7 @@ target_link_libraries(quickstep_expressions_aggregation_AggregationHandleCount
                       quickstep_catalog_CatalogTypedefs
                       quickstep_expressions_aggregation_AggregationConcreteHandle
                       quickstep_expressions_aggregation_AggregationHandle
+                      quickstep_storage_FastHashTable
                       quickstep_storage_HashTable
                       quickstep_storage_HashTableBase
                       quickstep_storage_HashTableFactory
@@ -202,6 +206,7 @@ target_link_libraries(quickstep_expressions_aggregation_AggregationHandleMax
                       quickstep_catalog_CatalogTypedefs
                       quickstep_expressions_aggregation_AggregationConcreteHandle
                       quickstep_expressions_aggregation_AggregationHandle
+                      quickstep_storage_FastHashTable
                       quickstep_storage_HashTable
                       quickstep_storage_HashTableBase
                       quickstep_storage_HashTableFactory
@@ -218,6 +223,7 @@ target_link_libraries(quickstep_expressions_aggregation_AggregationHandleMin
                       quickstep_catalog_CatalogTypedefs
                       quickstep_expressions_aggregation_AggregationConcreteHandle
                       quickstep_expressions_aggregation_AggregationHandle
+                      quickstep_storage_FastHashTable
                       quickstep_storage_HashTable
                       quickstep_storage_HashTableBase
                       quickstep_storage_HashTableFactory
@@ -234,6 +240,7 @@ target_link_libraries(quickstep_expressions_aggregation_AggregationHandleSum
                       quickstep_catalog_CatalogTypedefs
                       quickstep_expressions_aggregation_AggregationConcreteHandle
                       quickstep_expressions_aggregation_AggregationHandle
+                      quickstep_storage_FastHashTable
                       quickstep_storage_HashTable
                       quickstep_storage_HashTableBase
                       quickstep_storage_HashTableFactory

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/storage/AggregationOperationState.cpp
----------------------------------------------------------------------
diff --git a/storage/AggregationOperationState.cpp b/storage/AggregationOperationState.cpp
index 4878cf1..185c6b1 100644
--- a/storage/AggregationOperationState.cpp
+++ b/storage/AggregationOperationState.cpp
@@ -83,6 +83,9 @@ AggregationOperationState::AggregationOperationState(
     group_by_types.emplace_back(&group_by_element->getType());
   }
 
+  std::vector<AggregationHandle *> group_by_handles;
+  group_by_handles.clear();
+
   if (aggregate_functions.size() == 0) {
     // If there is no aggregation function, then it is a distinctify operation
     // on the group-by expressions.
@@ -92,11 +95,17 @@ AggregationOperationState::AggregationOperationState(
     arguments_.push_back({});
     is_distinct_.emplace_back(false);
 
-    group_by_hashtable_pools_.emplace_back(std::unique_ptr<HashTablePool>(
+ /*   group_by_hashtable_pools_.emplace_back(std::unique_ptr<HashTablePool>(
         new HashTablePool(estimated_num_entries,
                           hash_table_impl_type,
                           group_by_types,
                           handles_.back().get(),
+                          storage_manager)));*/
+    group_by_hashtable_pools_.emplace_back(std::unique_ptr<HashTablePool>(
+        new HashTablePool(estimated_num_entries,
+                          hash_table_impl_type,
+                          group_by_types,
+                          handles_.back(),
                           storage_manager)));
   } else {
     // Set up each individual aggregate in this operation.
@@ -107,6 +116,7 @@ AggregationOperationState::AggregationOperationState(
     std::vector<bool>::const_iterator is_distinct_it = is_distinct_.begin();
     std::vector<HashTableImplType>::const_iterator distinctify_hash_table_impl_types_it
         = distinctify_hash_table_impl_types.begin();
+    std::vector<std::size_t> payload_sizes;
     for (; agg_func_it != aggregate_functions.end(); ++agg_func_it, ++args_it, ++is_distinct_it) {
       // Get the Types of this aggregate's arguments so that we can create an
       // AggregationHandle.
@@ -126,12 +136,15 @@ AggregationOperationState::AggregationOperationState(
 
       if (!group_by_list_.empty()) {
         // Aggregation with GROUP BY: create a HashTable pool for per-group states.
-        group_by_hashtable_pools_.emplace_back(std::unique_ptr<HashTablePool>(
+ /*       group_by_hashtable_pools_.emplace_back(std::unique_ptr<HashTablePool>(
             new HashTablePool(estimated_num_entries,
                               hash_table_impl_type,
                               group_by_types,
                               handles_.back().get(),
-                              storage_manager)));
+                              storage_manager)));*/
+         group_by_handles.emplace_back(handles_.back());
+         payload_sizes.emplace_back(handles_.back()->getPayloadSize());
+
       } else {
         // Aggregation without GROUP BY: create a single global state.
         single_states_.emplace_back(handles_.back()->createInitialState());
@@ -166,17 +179,40 @@ AggregationOperationState::AggregationOperationState(
         // the number of entries in the distinctify hash table. We may estimate
         // for each distinct aggregation an estimated_num_distinct_keys value during
         // query optimization, if it worths.
-        distinctify_hashtables_.emplace_back(
+ /*       distinctify_hashtables_.emplace_back(
             handles_.back()->createDistinctifyHashTable(
                 *distinctify_hash_table_impl_types_it,
                 key_types,
                 estimated_num_entries,
+                storage_manager));*/
+
+std::vector<AggregationHandle *> local;
+local.emplace_back(handles_.back());
+        distinctify_hashtables_.emplace_back(
+AggregationStateFastHashTableFactory::CreateResizable(
+                *distinctify_hash_table_impl_types_it,
+                key_types,
+                estimated_num_entries,
+                {0},
+                local,
                 storage_manager));
+
         ++distinctify_hash_table_impl_types_it;
       } else {
         distinctify_hashtables_.emplace_back(nullptr);
       }
     }
+
+      if (!group_by_handles.empty()) {
+        // Aggregation with GROUP BY: create a HashTable pool for per-group states.
+        group_by_hashtable_pools_.emplace_back(std::unique_ptr<HashTablePool>(
+            new HashTablePool(estimated_num_entries,
+                              hash_table_impl_type,
+                              group_by_types,
+                              payload_sizes,
+                              group_by_handles,
+                              storage_manager)));
+      }
   }
 }
 
@@ -410,17 +446,24 @@ void AggregationOperationState::aggregateBlockHashTable(const block_id input_blo
       // Call StorageBlock::aggregateGroupBy() to aggregate this block's values
       // directly into the (threadsafe) shared global HashTable for this
       // aggregate.
-      DCHECK(group_by_hashtable_pools_[agg_idx] != nullptr);
-      AggregationStateHashTableBase *agg_hash_table = group_by_hashtable_pools_[agg_idx]->getHashTable();
+      DCHECK(group_by_hashtable_pools_[0] != nullptr);
+      AggregationStateHashTableBase *agg_hash_table = group_by_hashtable_pools_[0]->getHashTableFast();
       DCHECK(agg_hash_table != nullptr);
-      block->aggregateGroupBy(*handles_[agg_idx],
+ /*     block->aggregateGroupBy(*handles_[agg_idx],
                               arguments_[agg_idx],
                               group_by_list_,
                               predicate_.get(),
                               agg_hash_table,
                               &reuse_matches,
+                              &reuse_group_by_vectors);*/
+      block->aggregateGroupByFast(arguments_,
+                              group_by_list_,
+                              predicate_.get(),
+                              agg_hash_table,
+                              &reuse_matches,
                               &reuse_group_by_vectors);
-      group_by_hashtable_pools_[agg_idx]->returnHashTable(agg_hash_table);
+      group_by_hashtable_pools_[0]->returnHashTable(agg_hash_table);
+      break;
     }
   }
 }
@@ -444,6 +487,12 @@ void AggregationOperationState::finalizeSingleState(InsertDestination *output_de
   output_destination->insertTuple(Tuple(std::move(attribute_values)));
 }
 
+void AggregationOperationState::mergeGroupByHashTables(AggregationStateHashTableBase *src,
+                                                       AggregationStateHashTableBase *dst) {
+    HashTableMergerNewFast merger(dst);
+    (static_cast<FastHashTable<true, false, true, false> *>(src))->forEachCompositeKeyFast(&merger);
+}
+
 void AggregationOperationState::finalizeHashTable(InsertDestination *output_destination) {
   // Each element of 'group_by_keys' is a vector of values for a particular
   // group (which is also the prefix of the finalized Tuple for that group).
@@ -455,18 +504,21 @@ void AggregationOperationState::finalizeHashTable(InsertDestination *output_dest
 
   // TODO(harshad) - Find heuristics for faster merge, even in a single thread.
   // e.g. Keep merging entries from smaller hash tables to larger.
+//  auto *hash_tables = group_by_hashtable_pools_[0]->getAllHashTables();
+
+  auto *hash_tables = group_by_hashtable_pools_[0]->getAllHashTables();
   for (std::size_t agg_idx = 0; agg_idx < handles_.size(); ++agg_idx) {
-    auto *hash_tables = group_by_hashtable_pools_[agg_idx]->getAllHashTables();
     if (hash_tables->size() > 1) {
       for (int hash_table_index = 0;
            hash_table_index < static_cast<int>(hash_tables->size() - 1);
            ++hash_table_index) {
         // Merge each hash table to the last hash table.
-        handles_[agg_idx]->mergeGroupByHashTables(
-            (*(*hash_tables)[hash_table_index]),
+        mergeGroupByHashTables(
+            (*hash_tables)[hash_table_index].get(),
             hash_tables->back().get());
       }
     }
+    break;
   }
 
   // Collect per-aggregate finalized values.
@@ -475,16 +527,16 @@ void AggregationOperationState::finalizeHashTable(InsertDestination *output_dest
        agg_idx < handles_.size();
        ++agg_idx) {
     if (is_distinct_[agg_idx]) {
-      DCHECK(group_by_hashtable_pools_[agg_idx] != nullptr);
-      auto *hash_tables = group_by_hashtable_pools_[agg_idx]->getAllHashTables();
+      DCHECK(group_by_hashtable_pools_[0] != nullptr);
+      auto *hash_tables = group_by_hashtable_pools_[0]->getAllHashTables();
       DCHECK(hash_tables != nullptr);
       if (hash_tables->empty()) {
         // We may have a case where hash_tables is empty, e.g. no input blocks.
         // However for aggregateOnDistinctifyHashTableForGroupBy to work
         // correctly, we should create an empty group by hash table.
-        AggregationStateHashTableBase *new_hash_table = group_by_hashtable_pools_[agg_idx]->getHashTable();
-        group_by_hashtable_pools_[agg_idx]->returnHashTable(new_hash_table);
-        hash_tables = group_by_hashtable_pools_[agg_idx]->getAllHashTables();
+        AggregationStateHashTableBase *new_hash_table = group_by_hashtable_pools_[0]->getHashTableFast();
+        group_by_hashtable_pools_[0]->returnHashTable(new_hash_table);
+        hash_tables = group_by_hashtable_pools_[0]->getAllHashTables();
       }
       DCHECK(hash_tables->back() != nullptr);
       AggregationStateHashTableBase *agg_hash_table = hash_tables->back().get();
@@ -494,21 +546,22 @@ void AggregationOperationState::finalizeHashTable(InsertDestination *output_dest
           agg_hash_table);
     }
 
-    auto *hash_tables = group_by_hashtable_pools_[agg_idx]->getAllHashTables();
+    auto *hash_tables = group_by_hashtable_pools_[0]->getAllHashTables();
     DCHECK(hash_tables != nullptr);
     if (hash_tables->empty()) {
       // We may have a case where hash_tables is empty, e.g. no input blocks.
       // However for aggregateOnDistinctifyHashTableForGroupBy to work
       // correctly, we should create an empty group by hash table.
-      AggregationStateHashTableBase *new_hash_table = group_by_hashtable_pools_[agg_idx]->getHashTable();
-      group_by_hashtable_pools_[agg_idx]->returnHashTable(new_hash_table);
-      hash_tables = group_by_hashtable_pools_[agg_idx]->getAllHashTables();
+      AggregationStateHashTableBase *new_hash_table = group_by_hashtable_pools_[0]->getHashTable();
+      group_by_hashtable_pools_[0]->returnHashTable(new_hash_table);
+      hash_tables = group_by_hashtable_pools_[0]->getAllHashTables();
     }
     AggregationStateHashTableBase *agg_hash_table = hash_tables->back().get();
     DCHECK(agg_hash_table != nullptr);
     ColumnVector* agg_result_col =
         handles_[agg_idx]->finalizeHashTable(*agg_hash_table,
-                                             &group_by_keys);
+                                             &group_by_keys,
+                                              agg_idx);
     if (agg_result_col != nullptr) {
       final_values.emplace_back(agg_result_col);
     }

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/storage/AggregationOperationState.hpp
----------------------------------------------------------------------
diff --git a/storage/AggregationOperationState.hpp b/storage/AggregationOperationState.hpp
index 0199749..8934cda 100644
--- a/storage/AggregationOperationState.hpp
+++ b/storage/AggregationOperationState.hpp
@@ -165,6 +165,8 @@ class AggregationOperationState {
    **/
   void finalizeAggregate(InsertDestination *output_destination);
 
+  int dflag;
+
  private:
   // Merge locally (per storage block) aggregated states with global aggregation
   // states.
@@ -185,7 +187,8 @@ class AggregationOperationState {
 
   // Each individual aggregate in this operation has an AggregationHandle and
   // some number of Scalar arguments.
-  std::vector<std::unique_ptr<AggregationHandle>> handles_;
+//  std::vector<std::unique_ptr<AggregationHandle>> handles_;
+  std::vector<AggregationHandle *> handles_;
   std::vector<std::vector<std::unique_ptr<const Scalar>>> arguments_;
 
   // For each aggregate, whether DISTINCT should be applied to the aggregate's
@@ -215,6 +218,8 @@ class AggregationOperationState {
 
   StorageManager *storage_manager_;
 
+  void mergeGroupByHashTables(AggregationStateHashTableBase *src, AggregationStateHashTableBase *dst);
+
   DISALLOW_COPY_AND_ASSIGN(AggregationOperationState);
 };
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/storage/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/storage/CMakeLists.txt b/storage/CMakeLists.txt
index 582effd..e67b21f 100644
--- a/storage/CMakeLists.txt
+++ b/storage/CMakeLists.txt
@@ -196,6 +196,9 @@ if (ENABLE_DISTRIBUTED)
 endif()
 
 add_library(quickstep_storage_EvictionPolicy EvictionPolicy.cpp EvictionPolicy.hpp)
+add_library(quickstep_storage_FastHashTable ../empty_src.cpp FastHashTable.hpp)
+add_library(quickstep_storage_FastHashTableFactory ../empty_src.cpp FastHashTableFactory.hpp)
+add_library(quickstep_storage_FastSeparateChainingHashTable ../empty_src.cpp FastSeparateChainingHashTable.hpp)
 add_library(quickstep_storage_FileManager ../empty_src.cpp FileManager.hpp)
 if (QUICKSTEP_HAVE_FILE_MANAGER_HDFS)
   add_library(quickstep_storage_FileManagerHdfs FileManagerHdfs.cpp FileManagerHdfs.hpp)
@@ -624,6 +627,55 @@ target_link_libraries(quickstep_storage_EvictionPolicy
                       quickstep_threading_SpinMutex
                       quickstep_threading_SpinSharedMutex
                       quickstep_utility_Macros)
+target_link_libraries(quickstep_storage_FastHashTable
+                      quickstep_catalog_CatalogTypedefs
+                      quickstep_expressions_aggregation_AggregationHandleAvg
+                      quickstep_storage_HashTable
+                      quickstep_storage_HashTableBase
+                      quickstep_storage_StorageBlob
+                      quickstep_storage_StorageBlockInfo
+                      quickstep_storage_StorageConstants
+                      quickstep_storage_StorageManager
+                      quickstep_storage_TupleReference
+                      quickstep_storage_ValueAccessor
+                      quickstep_storage_ValueAccessorUtil
+                      quickstep_threading_SpinMutex
+                      quickstep_threading_SpinSharedMutex
+                      quickstep_types_Type
+                      quickstep_types_TypedValue
+                      quickstep_utility_BloomFilter
+                      quickstep_utility_HashPair
+                      quickstep_utility_Macros)
+target_link_libraries(quickstep_storage_FastHashTableFactory
+                      glog
+                      quickstep_storage_FastHashTable
+                      quickstep_storage_FastSeparateChainingHashTable
+                      quickstep_storage_HashTable
+                      quickstep_storage_HashTable_proto
+                      quickstep_storage_HashTableBase
+                      quickstep_storage_HashTableFactory
+                      quickstep_storage_LinearOpenAddressingHashTable
+                      quickstep_storage_SeparateChainingHashTable
+                      quickstep_storage_SimpleScalarSeparateChainingHashTable
+                      quickstep_storage_TupleReference
+                      quickstep_types_TypeFactory
+                      quickstep_utility_BloomFilter
+                      quickstep_utility_Macros)
+target_link_libraries(quickstep_storage_FastSeparateChainingHashTable
+                      quickstep_storage_FastHashTable
+                      quickstep_storage_HashTable
+                      quickstep_storage_HashTableBase
+                      quickstep_storage_HashTableKeyManager
+                      quickstep_storage_StorageBlob
+                      quickstep_storage_StorageBlockInfo
+                      quickstep_storage_StorageConstants
+                      quickstep_storage_StorageManager
+                      quickstep_threading_SpinSharedMutex
+                      quickstep_types_Type
+                      quickstep_types_TypedValue
+                      quickstep_utility_Alignment
+                      quickstep_utility_Macros
+                      quickstep_utility_PrimeNumber)
 target_link_libraries(quickstep_storage_FileManager
                       quickstep_storage_StorageBlockInfo
                       quickstep_utility_Macros
@@ -709,6 +761,8 @@ target_link_libraries(quickstep_storage_HashTableKeyManager
 target_link_libraries(quickstep_storage_HashTablePool
                       glog
                       quickstep_expressions_aggregation_AggregationHandle
+                      quickstep_storage_FastHashTable
+                      quickstep_storage_FastHashTableFactory
                       quickstep_storage_HashTableBase
                       quickstep_threading_SpinMutex
                       quickstep_utility_Macros
@@ -913,6 +967,7 @@ target_link_libraries(quickstep_storage_StorageBlock
                       quickstep_storage_CompressedColumnStoreTupleStorageSubBlock
                       quickstep_storage_CompressedPackedRowStoreTupleStorageSubBlock
                       quickstep_storage_CountedReference
+                      quickstep_storage_FastHashTable
                       quickstep_storage_HashTableBase
                       quickstep_storage_IndexSubBlock
                       quickstep_storage_InsertDestinationInterface
@@ -1096,6 +1151,9 @@ target_link_libraries(quickstep_storage
                       quickstep_storage_EvictionPolicy
                       quickstep_storage_FileManager
                       quickstep_storage_FileManagerLocal
+                      quickstep_storage_FastHashTable
+                      quickstep_storage_FastHashTableFactory
+                      quickstep_storage_FastSeparateChainingHashTable
                       quickstep_storage_HashTable
                       quickstep_storage_HashTable_proto
                       quickstep_storage_HashTableBase


[22/30] incubator-quickstep git commit: Update the NOTICE file to acknowledge all the copyrights

Posted by ra...@apache.org.
Update the NOTICE file to acknowledge all the copyrights


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

Branch: refs/heads/quickstep-28-29
Commit: 260b8624a8a1f2161e8c38fe56bd00ae1bfb579b
Parents: d0172fd
Author: Jignesh Patel <jm...@hotmail.com>
Authored: Mon Aug 1 09:56:44 2016 -0500
Committer: Jignesh Patel <jm...@hotmail.com>
Committed: Mon Aug 1 09:56:44 2016 -0500

----------------------------------------------------------------------
 NOTICE | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/260b8624/NOTICE
----------------------------------------------------------------------
diff --git a/NOTICE b/NOTICE
index 9cfd585..32db8b8 100644
--- a/NOTICE
+++ b/NOTICE
@@ -6,3 +6,39 @@ The Apache Software Foundation (http://www.apache.org/).
 
 Portions Copyright (c) 2011-2015, Quickstep Technologies, LLC.
 Portions Copyright (c) 2015-2016, Pivotal Software, Inc.
+
+[Copyright for third_party/benchmark]
+Portions Copyright (c) Arne Beer <ar...@twobeer.de>
+Portions Copyright (c) Christopher Seymour <ch...@hotmail.com>
+Portions Copyright (c) David Coeurjolly <da...@liris.cnrs.fr>
+Portions Copyright (c) Dominic Hamon <dm...@stripysock.com>
+Portions Copyright (c) Eugene Zhuk <eu...@gmail.com>
+Portions Copyright (c) Evgeny Safronov <di...@gmail.com>
+Portions Copyright (c) Felix Homann <li...@showlabor.de>
+Portions Copyright (c) Google Inc.
+Portions Copyright (c) JianXiong Zhou <zh...@gmail.com>
+Portions Copyright (c) Lei Xu <ed...@gmail.com>
+Portions Copyright (c) Matt Clarkson <ma...@gmail.com>
+Portions Copyright (c) Oleksandr Sochka <sa...@gmail.com>
+Portions Copyright (c) Paul Redmond <pa...@gmail.com>
+Portions Copyright (c) Shuo Chen <ch...@chenshuo.com>
+Portions Copyright (c) Yusuke Suzuki <ut...@gmail.com>
+
+[Copyright for third_party/cpplint]
+Portions Copyright (c) 2009 Google Inc 
+
+[Copyright for third_party/farmhash]
+Copyright (c) 2014 Google, Inc.
+
+[Copyright for third_party/gflags]
+Copyright (c) 2006, Google Inc.
+
+[Copyright for third_party/glog]
+Copyright (c) 2008, Google Inc.
+
+[Copyright for third_party/gpertools]
+Copyright (c) 2005, Google Inc.
+
+[Copyright for third_party/linenoise]
+Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
+Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>


[04/30] incubator-quickstep git commit: Added ExecutionGenerator support for Window Aggregation.

Posted by ra...@apache.org.
Added ExecutionGenerator support for Window Aggregation.


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

Branch: refs/heads/quickstep-28-29
Commit: 7671a5893349f060d95e20cd8b98ad076c614cec
Parents: 303e51e
Author: shixuan-fan <sh...@apache.org>
Authored: Wed Jun 29 20:25:18 2016 +0000
Committer: Zuyu Zhang <zu...@apache.org>
Committed: Fri Jul 8 11:15:20 2016 -0500

----------------------------------------------------------------------
 query_execution/CMakeLists.txt                  |   2 +
 query_execution/QueryContext.cpp                |  14 ++
 query_execution/QueryContext.hpp                |  48 +++++
 query_execution/QueryContext.proto              |   5 +-
 query_optimizer/CMakeLists.txt                  |   2 +
 query_optimizer/ExecutionGenerator.cpp          | 101 ++++++++++-
 query_optimizer/ExecutionGenerator.hpp          |   8 +
 query_optimizer/cost_model/CMakeLists.txt       |   2 +
 query_optimizer/cost_model/SimpleCostModel.cpp  |   9 +
 query_optimizer/cost_model/SimpleCostModel.hpp  |   5 +
 .../cost_model/StarSchemaSimpleCostModel.cpp    |   8 +
 .../cost_model/StarSchemaSimpleCostModel.hpp    |   4 +
 query_optimizer/resolver/Resolver.cpp           |   7 +-
 .../tests/execution_generator/Select.test       |  39 ++++
 query_optimizer/tests/resolver/Select.test      |  33 ++++
 relational_operators/CMakeLists.txt             |  14 ++
 .../WindowAggregationOperator.cpp               |  82 +++++++++
 .../WindowAggregationOperator.hpp               | 166 +++++++++++++++++
 relational_operators/WorkOrder.proto            |   9 +
 storage/CMakeLists.txt                          |  45 ++++-
 storage/WindowAggregationOperationState.cpp     | 179 +++++++++++++++++++
 storage/WindowAggregationOperationState.hpp     | 177 ++++++++++++++++++
 storage/WindowAggregationOperationState.proto   |  33 ++++
 ...WindowAggregationOperationState_unittest.cpp |  92 ++++++++++
 24 files changed, 1079 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/query_execution/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/query_execution/CMakeLists.txt b/query_execution/CMakeLists.txt
index 2be451c..e1b1183 100644
--- a/query_execution/CMakeLists.txt
+++ b/query_execution/CMakeLists.txt
@@ -118,6 +118,7 @@ target_link_libraries(quickstep_queryexecution_QueryContext
                       quickstep_storage_HashTableFactory
                       quickstep_storage_InsertDestination
                       quickstep_storage_InsertDestination_proto
+                      quickstep_storage_WindowAggregationOperationState
                       quickstep_types_TypedValue
                       quickstep_types_containers_Tuple
                       quickstep_utility_BloomFilter
@@ -130,6 +131,7 @@ target_link_libraries(quickstep_queryexecution_QueryContext_proto
                       quickstep_storage_AggregationOperationState_proto
                       quickstep_storage_HashTable_proto
                       quickstep_storage_InsertDestination_proto
+                      quickstep_storage_WindowAggregationOperationState_proto
                       quickstep_types_containers_Tuple_proto
                       quickstep_utility_SortConfiguration_proto
                       ${PROTOBUF_LIBRARY})

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/query_execution/QueryContext.cpp
----------------------------------------------------------------------
diff --git a/query_execution/QueryContext.cpp b/query_execution/QueryContext.cpp
index 54dd557..7019b6a 100644
--- a/query_execution/QueryContext.cpp
+++ b/query_execution/QueryContext.cpp
@@ -140,6 +140,13 @@ QueryContext::QueryContext(const serialization::QueryContext &proto,
 
     update_groups_.push_back(move(update_group));
   }
+
+  for (int i = 0; i < proto.window_aggregation_states_size(); ++i) {
+    window_aggregation_states_.emplace_back(
+        WindowAggregationOperationState::ReconstructFromProto(proto.window_aggregation_states(i),
+                                                              database,
+                                                              storage_manager));
+  }
 }
 
 bool QueryContext::ProtoIsValid(const serialization::QueryContext &proto,
@@ -231,6 +238,13 @@ bool QueryContext::ProtoIsValid(const serialization::QueryContext &proto,
     }
   }
 
+  for (int i = 0; i < proto.window_aggregation_states_size(); ++i) {
+    if (!WindowAggregationOperationState::ProtoIsValid(proto.window_aggregation_states(i),
+                                                       database)) {
+      return false;
+    }
+  }
+
   return proto.IsInitialized();
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/query_execution/QueryContext.hpp
----------------------------------------------------------------------
diff --git a/query_execution/QueryContext.hpp b/query_execution/QueryContext.hpp
index 7d5628d..9171250 100644
--- a/query_execution/QueryContext.hpp
+++ b/query_execution/QueryContext.hpp
@@ -33,6 +33,7 @@
 #include "storage/AggregationOperationState.hpp"
 #include "storage/HashTable.hpp"
 #include "storage/InsertDestination.hpp"
+#include "storage/WindowAggregationOperationState.hpp"
 #include "types/containers/Tuple.hpp"
 #include "utility/BloomFilter.hpp"
 #include "utility/Macros.hpp"
@@ -120,6 +121,11 @@ class QueryContext {
   typedef std::uint32_t update_group_id;
 
   /**
+   * @brief A unique identifier for a window aggregation state.
+   **/
+  typedef std::uint32_t window_aggregation_state_id;
+
+  /**
    * @brief Constructor.
    *
    * @param proto A serialized Protocol Buffer representation of a
@@ -460,6 +466,47 @@ class QueryContext {
     return update_groups_[id];
   }
 
+  /**
+   * @brief Whether the given WindowAggregationOperationState id is valid.
+   *
+   * @param id The WindowAggregationOperationState id.
+   *
+   * @return True if valid, otherwise false.
+   **/
+  bool isValidWindowAggregationStateId(const window_aggregation_state_id id) const {
+    return id < window_aggregation_states_.size();
+  }
+
+  /**
+   * @brief Get the WindowAggregationOperationState.
+   *
+   * @param id The WindowAggregationOperationState id in the query.
+   *
+   * @return The WindowAggregationOperationState, already created in the
+   *         constructor.
+   **/
+  inline WindowAggregationOperationState* getWindowAggregationState(
+      const window_aggregation_state_id id) {
+    DCHECK_LT(id, window_aggregation_states_.size());
+    DCHECK(window_aggregation_states_[id]);
+    return window_aggregation_states_[id].get();
+  }
+
+  /**
+   * @brief Release the given WindowAggregationOperationState.
+   *
+   * @param id The id of the WindowAggregationOperationState to destroy.
+   *
+   * @return The WindowAggregationOperationState, already created in the
+   *         constructor.
+   **/
+  inline WindowAggregationOperationState* releaseWindowAggregationState(
+      const window_aggregation_state_id id) {
+    DCHECK_LT(id, window_aggregation_states_.size());
+    DCHECK(window_aggregation_states_[id]);
+    return window_aggregation_states_[id].release();
+  }
+
  private:
   std::vector<std::unique_ptr<AggregationOperationState>> aggregation_states_;
   std::vector<std::unique_ptr<BloomFilter>> bloom_filters_;
@@ -471,6 +518,7 @@ class QueryContext {
   std::vector<std::unique_ptr<const SortConfiguration>> sort_configs_;
   std::vector<std::unique_ptr<Tuple>> tuples_;
   std::vector<std::unordered_map<attribute_id, std::unique_ptr<const Scalar>>> update_groups_;
+  std::vector<std::unique_ptr<WindowAggregationOperationState>> window_aggregation_states_;
 
   DISALLOW_COPY_AND_ASSIGN(QueryContext);
 };

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/query_execution/QueryContext.proto
----------------------------------------------------------------------
diff --git a/query_execution/QueryContext.proto b/query_execution/QueryContext.proto
index 98cd0b6..ddf8326 100644
--- a/query_execution/QueryContext.proto
+++ b/query_execution/QueryContext.proto
@@ -22,6 +22,7 @@ import "expressions/table_generator/GeneratorFunction.proto";
 import "storage/AggregationOperationState.proto";
 import "storage/HashTable.proto";
 import "storage/InsertDestination.proto";
+import "storage/WindowAggregationOperationState.proto";
 import "types/containers/Tuple.proto";
 import "utility/BloomFilter.proto";
 import "utility/SortConfiguration.proto";
@@ -55,5 +56,7 @@ message QueryContext {
   // NOTE(zuyu): For UpdateWorkOrder only.
   repeated UpdateGroup update_groups = 10;
 
-  required uint64 query_id = 11;
+  repeated WindowAggregationOperationState window_aggregation_states = 11;
+
+  required uint64 query_id = 12;
 }

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/query_optimizer/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/query_optimizer/CMakeLists.txt b/query_optimizer/CMakeLists.txt
index 8912414..7e53b9d 100644
--- a/query_optimizer/CMakeLists.txt
+++ b/query_optimizer/CMakeLists.txt
@@ -88,6 +88,7 @@ target_link_libraries(quickstep_queryoptimizer_ExecutionGenerator
                       quickstep_queryoptimizer_expressions_Predicate
                       quickstep_queryoptimizer_expressions_Scalar
                       quickstep_queryoptimizer_expressions_ScalarLiteral
+                      quickstep_queryoptimizer_expressions_WindowAggregateFunction
                       quickstep_queryoptimizer_physical_Aggregate
                       quickstep_queryoptimizer_physical_CopyFrom
                       quickstep_queryoptimizer_physical_CreateIndex
@@ -130,6 +131,7 @@ target_link_libraries(quickstep_queryoptimizer_ExecutionGenerator
                       quickstep_relationaloperators_TableGeneratorOperator
                       quickstep_relationaloperators_TextScanOperator
                       quickstep_relationaloperators_UpdateOperator
+                      quickstep_relationaloperators_WindowAggregationOperator
                       quickstep_storage_AggregationOperationState_proto
                       quickstep_storage_HashTableFactory
                       quickstep_storage_HashTable_proto

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/query_optimizer/ExecutionGenerator.cpp
----------------------------------------------------------------------
diff --git a/query_optimizer/ExecutionGenerator.cpp b/query_optimizer/ExecutionGenerator.cpp
index 45f5f78..43d63f9 100644
--- a/query_optimizer/ExecutionGenerator.cpp
+++ b/query_optimizer/ExecutionGenerator.cpp
@@ -63,6 +63,7 @@
 #include "query_optimizer/expressions/PatternMatcher.hpp"
 #include "query_optimizer/expressions/Scalar.hpp"
 #include "query_optimizer/expressions/ScalarLiteral.hpp"
+#include "query_optimizer/expressions/WindowAggregateFunction.hpp"
 #include "query_optimizer/physical/CopyFrom.hpp"
 #include "query_optimizer/physical/CreateIndex.hpp"
 #include "query_optimizer/physical/CreateTable.hpp"
@@ -104,6 +105,7 @@
 #include "relational_operators/TableGeneratorOperator.hpp"
 #include "relational_operators/TextScanOperator.hpp"
 #include "relational_operators/UpdateOperator.hpp"
+#include "relational_operators/WindowAggregationOperator.hpp"
 #include "storage/AggregationOperationState.pb.h"
 #include "storage/HashTable.pb.h"
 #include "storage/HashTableFactory.hpp"
@@ -284,8 +286,8 @@ void ExecutionGenerator::generatePlanInternal(
       return convertUpdateTable(
           std::static_pointer_cast<const P::UpdateTable>(physical_plan));
     case P::PhysicalType::kWindowAggregate:
-      THROW_SQL_ERROR()
-          << "Window aggregate function is not supported yet :(";
+      return convertWindowAggregate(
+          std::static_pointer_cast<const P::WindowAggregate>(physical_plan));
     default:
       LOG(FATAL) << "Unknown physical plan node "
                  << physical_plan->getShortString();
@@ -1639,5 +1641,100 @@ void ExecutionGenerator::convertTableGenerator(
   temporary_relation_info_vec_.emplace_back(tablegen_index, output_relation);
 }
 
+void ExecutionGenerator::convertWindowAggregate(
+    const P::WindowAggregatePtr &physical_plan) {
+  // Create window_aggregation_operation_state proto.
+  const QueryContext::window_aggregation_state_id window_aggr_state_index =
+      query_context_proto_->window_aggregation_states_size();
+  S::WindowAggregationOperationState *window_aggr_state_proto =
+      query_context_proto_->add_window_aggregation_states();
+
+  // Get input.
+  const CatalogRelationInfo *input_relation_info =
+      findRelationInfoOutputByPhysical(physical_plan->input());
+  window_aggr_state_proto->set_relation_id(input_relation_info->relation->getID());
+
+  // Get window aggregate function expression.
+  const E::AliasPtr &named_window_aggregate_expression =
+      physical_plan->window_aggregate_expression();
+  const E::WindowAggregateFunctionPtr &window_aggregate_function =
+      std::static_pointer_cast<const E::WindowAggregateFunction>(
+          named_window_aggregate_expression->expression());
+
+  // Set the AggregateFunction.
+  window_aggr_state_proto->mutable_function()->MergeFrom(
+      window_aggregate_function->window_aggregate().getProto());
+
+  // Set the arguments.
+  for (const E::ScalarPtr &argument : window_aggregate_function->arguments()) {
+    unique_ptr<const Scalar> concretized_argument(argument->concretize(attribute_substitution_map_));
+    window_aggr_state_proto->add_arguments()->MergeFrom(concretized_argument->getProto());
+  }
+
+  // Set partition keys.
+  const E::WindowInfo &window_info = window_aggregate_function->window_info();
+  for (const E::ScalarPtr &partition_by_attribute
+      : window_info.partition_by_attributes) {
+    unique_ptr<const Scalar> concretized_partition_by_attribute(
+        partition_by_attribute->concretize(attribute_substitution_map_));
+    window_aggr_state_proto->add_partition_by_attributes()
+        ->MergeFrom(concretized_partition_by_attribute->getProto());
+  }
+
+  // Set window frame info.
+  if (window_info.frame_info == nullptr) {
+    // If the frame is not specified, use the default setting:
+    //   1. If ORDER BY key is specified, use cumulative aggregation:
+    //      ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW.
+    //   2. If ORDER BY key is not specified either, use the whole partition:
+    //      ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING.
+    window_aggr_state_proto->set_is_row(true);  // frame mode: ROWS.
+    window_aggr_state_proto->set_num_preceding(-1);  // UNBOUNDED PRECEDING.
+    window_aggr_state_proto->set_num_following(
+        window_info.order_by_attributes.empty()
+            ? -1  // UNBOUNDED FOLLOWING.
+            : 0);  // CURRENT ROW.
+  } else {
+    const E::WindowFrameInfo *window_frame_info = window_info.frame_info;
+    window_aggr_state_proto->set_is_row(window_frame_info->is_row);
+    window_aggr_state_proto->set_num_preceding(window_frame_info->num_preceding);
+    window_aggr_state_proto->set_num_following(window_frame_info->num_following);
+  }
+
+  // Create InsertDestination proto.
+  const CatalogRelation *output_relation = nullptr;
+  const QueryContext::insert_destination_id insert_destination_index =
+      query_context_proto_->insert_destinations_size();
+  S::InsertDestination *insert_destination_proto = query_context_proto_->add_insert_destinations();
+  createTemporaryCatalogRelation(physical_plan,
+                                 &output_relation,
+                                 insert_destination_proto);
+
+  const QueryPlan::DAGNodeIndex window_aggregation_operator_index =
+      execution_plan_->addRelationalOperator(
+          new WindowAggregationOperator(query_handle_->query_id(),
+                                        *output_relation,
+                                        window_aggr_state_index,
+                                        insert_destination_index));
+
+  // TODO(Shixuan): Once parallelism is introduced, the is_pipeline_breaker
+  //                could be set to false.
+  if (!input_relation_info->isStoredRelation()) {
+    execution_plan_->addDirectDependency(window_aggregation_operator_index,
+                                         input_relation_info->producer_operator_index,
+                                         true /* is_pipeline_breaker */);
+  }
+
+  insert_destination_proto->set_relational_op_index(window_aggregation_operator_index);
+
+  // Add to map and temp_relation_info_vec.
+  physical_to_output_relation_map_.emplace(
+      std::piecewise_construct,
+      std::forward_as_tuple(physical_plan),
+      std::forward_as_tuple(window_aggregation_operator_index, output_relation));
+  temporary_relation_info_vec_.emplace_back(window_aggregation_operator_index,
+                                            output_relation);
+}
+
 }  // namespace optimizer
 }  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/query_optimizer/ExecutionGenerator.hpp
----------------------------------------------------------------------
diff --git a/query_optimizer/ExecutionGenerator.hpp b/query_optimizer/ExecutionGenerator.hpp
index c7fd018..9186707 100644
--- a/query_optimizer/ExecutionGenerator.hpp
+++ b/query_optimizer/ExecutionGenerator.hpp
@@ -60,6 +60,7 @@
 #include "query_optimizer/physical/TableReference.hpp"
 #include "query_optimizer/physical/TopLevelPlan.hpp"
 #include "query_optimizer/physical/UpdateTable.hpp"
+#include "query_optimizer/physical/WindowAggregate.hpp"
 #include "utility/Macros.hpp"
 
 #include "glog/logging.h"
@@ -347,6 +348,13 @@ class ExecutionGenerator {
   void convertTableGenerator(const physical::TableGeneratorPtr &physical_plan);
 
   /**
+   * @brief Converts a physical WindowAggregate to a WindowAggregation operator.
+   *
+   * @param physical_plan The WindowAggregate to be converted.
+   */
+  void convertWindowAggregate(const physical::WindowAggregatePtr &physical_plan);
+
+  /**
    * @brief Converts a list of NamedExpressions in the optimizer expression
    *        system to scalars proto in QueryContext proto.
    *

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/query_optimizer/cost_model/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/query_optimizer/cost_model/CMakeLists.txt b/query_optimizer/cost_model/CMakeLists.txt
index 6bf5240..5d5b596 100644
--- a/query_optimizer/cost_model/CMakeLists.txt
+++ b/query_optimizer/cost_model/CMakeLists.txt
@@ -40,6 +40,7 @@ target_link_libraries(quickstep_queryoptimizer_costmodel_SimpleCostModel
                       quickstep_queryoptimizer_physical_TableGenerator
                       quickstep_queryoptimizer_physical_TableReference
                       quickstep_queryoptimizer_physical_TopLevelPlan
+                      quickstep_queryoptimizer_physical_WindowAggregate
                       quickstep_utility_Macros)
 target_link_libraries(quickstep_queryoptimizer_costmodel_StarSchemaSimpleCostModel
                       glog
@@ -64,6 +65,7 @@ target_link_libraries(quickstep_queryoptimizer_costmodel_StarSchemaSimpleCostMod
                       quickstep_queryoptimizer_physical_TableGenerator
                       quickstep_queryoptimizer_physical_TableReference
                       quickstep_queryoptimizer_physical_TopLevelPlan
+                      quickstep_queryoptimizer_physical_WindowAggregate
                       quickstep_utility_Macros)
 
 # Module all-in-one library:

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/query_optimizer/cost_model/SimpleCostModel.cpp
----------------------------------------------------------------------
diff --git a/query_optimizer/cost_model/SimpleCostModel.cpp b/query_optimizer/cost_model/SimpleCostModel.cpp
index 48f76fa..e5222ff 100644
--- a/query_optimizer/cost_model/SimpleCostModel.cpp
+++ b/query_optimizer/cost_model/SimpleCostModel.cpp
@@ -33,6 +33,7 @@
 #include "query_optimizer/physical/TableGenerator.hpp"
 #include "query_optimizer/physical/TableReference.hpp"
 #include "query_optimizer/physical/TopLevelPlan.hpp"
+#include "query_optimizer/physical/WindowAggregate.hpp"
 
 #include "glog/logging.h"
 
@@ -72,6 +73,9 @@ std::size_t SimpleCostModel::estimateCardinality(
       return estimateCardinality(
           shared_subplans_[shared_subplan_reference->subplan_id()]);
     }
+    case P::PhysicalType::kWindowAggregate:
+      return estimateCardinalityForWindowAggregate(
+          std::static_pointer_cast<const P::WindowAggregate>(physical_plan));
     default:
       LOG(FATAL) << "Unsupported physical plan:" << physical_plan->toString();
   }
@@ -118,6 +122,11 @@ std::size_t SimpleCostModel::estimateCardinalityForAggregate(
                   estimateCardinality(physical_plan->input()) / 10);
 }
 
+std::size_t SimpleCostModel::estimateCardinalityForWindowAggregate(
+    const physical::WindowAggregatePtr &physical_plan) {
+  return estimateCardinality(physical_plan->input());
+}
+
 }  // namespace cost
 }  // namespace optimizer
 }  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/query_optimizer/cost_model/SimpleCostModel.hpp
----------------------------------------------------------------------
diff --git a/query_optimizer/cost_model/SimpleCostModel.hpp b/query_optimizer/cost_model/SimpleCostModel.hpp
index 9862198..9837039 100644
--- a/query_optimizer/cost_model/SimpleCostModel.hpp
+++ b/query_optimizer/cost_model/SimpleCostModel.hpp
@@ -32,6 +32,7 @@
 #include "query_optimizer/physical/TableGenerator.hpp"
 #include "query_optimizer/physical/TableReference.hpp"
 #include "query_optimizer/physical/TopLevelPlan.hpp"
+#include "query_optimizer/physical/WindowAggregate.hpp"
 #include "utility/Macros.hpp"
 
 namespace quickstep {
@@ -88,6 +89,10 @@ class SimpleCostModel : public CostModel {
   std::size_t estimateCardinalityForAggregate(
       const physical::AggregatePtr &physical_plan);
 
+  // Return the estimated cardinality of the input plan.
+  std::size_t estimateCardinalityForWindowAggregate(
+      const physical::WindowAggregatePtr &physical_plan);
+
   const std::vector<physical::PhysicalPtr> &shared_subplans_;
 
   DISALLOW_COPY_AND_ASSIGN(SimpleCostModel);

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/query_optimizer/cost_model/StarSchemaSimpleCostModel.cpp
----------------------------------------------------------------------
diff --git a/query_optimizer/cost_model/StarSchemaSimpleCostModel.cpp b/query_optimizer/cost_model/StarSchemaSimpleCostModel.cpp
index eb9fcc1..badfeb1 100644
--- a/query_optimizer/cost_model/StarSchemaSimpleCostModel.cpp
+++ b/query_optimizer/cost_model/StarSchemaSimpleCostModel.cpp
@@ -85,6 +85,9 @@ std::size_t StarSchemaSimpleCostModel::estimateCardinality(
     case P::PhysicalType::kSort:
       return estimateCardinality(
           std::static_pointer_cast<const P::Sort>(physical_plan)->input());
+    case P::PhysicalType::kWindowAggregate:
+      return estimateCardinalityForWindowAggregate(
+          std::static_pointer_cast<const P::WindowAggregate>(physical_plan));
     default:
       LOG(FATAL) << "Unsupported physical plan:" << physical_plan->toString();
   }
@@ -141,6 +144,11 @@ std::size_t StarSchemaSimpleCostModel::estimateCardinalityForAggregate(
                   estimateCardinality(physical_plan->input()) / 10);
 }
 
+std::size_t StarSchemaSimpleCostModel::estimateCardinalityForWindowAggregate(
+    const P::WindowAggregatePtr &physical_plan) {
+  return estimateCardinality(physical_plan->input());
+}
+
 double StarSchemaSimpleCostModel::estimateSelectivity(
     const physical::PhysicalPtr &physical_plan) {
   switch (physical_plan->getPhysicalType()) {

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/query_optimizer/cost_model/StarSchemaSimpleCostModel.hpp
----------------------------------------------------------------------
diff --git a/query_optimizer/cost_model/StarSchemaSimpleCostModel.hpp b/query_optimizer/cost_model/StarSchemaSimpleCostModel.hpp
index c63e55a..83032cf 100644
--- a/query_optimizer/cost_model/StarSchemaSimpleCostModel.hpp
+++ b/query_optimizer/cost_model/StarSchemaSimpleCostModel.hpp
@@ -33,6 +33,7 @@
 #include "query_optimizer/physical/TableGenerator.hpp"
 #include "query_optimizer/physical/TableReference.hpp"
 #include "query_optimizer/physical/TopLevelPlan.hpp"
+#include "query_optimizer/physical/WindowAggregate.hpp"
 #include "utility/Macros.hpp"
 
 namespace quickstep {
@@ -94,6 +95,9 @@ class StarSchemaSimpleCostModel : public CostModel {
   std::size_t estimateCardinalityForAggregate(
       const physical::AggregatePtr &physical_plan);
 
+  std::size_t estimateCardinalityForWindowAggregate(
+      const physical::WindowAggregatePtr &physical_plan);
+
   double estimateSelectivityForSelection(
       const physical::SelectionPtr &physical_plan);
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/query_optimizer/resolver/Resolver.cpp
----------------------------------------------------------------------
diff --git a/query_optimizer/resolver/Resolver.cpp b/query_optimizer/resolver/Resolver.cpp
index c07751a..f10378b 100644
--- a/query_optimizer/resolver/Resolver.cpp
+++ b/query_optimizer/resolver/Resolver.cpp
@@ -1860,12 +1860,17 @@ L::LogicalPtr Resolver::resolveJoinedTableReference(
 L::LogicalPtr Resolver::resolveSortInWindow(
     const L::LogicalPtr &logical_plan,
     const E::WindowInfo &window_info) {
-  // Sort the table by (p_key, o_key)
+  // Sort the table by (p_key, o_key).
   std::vector<E::AttributeReferencePtr> sort_attributes(window_info.partition_by_attributes);
   sort_attributes.insert(sort_attributes.end(),
                          window_info.order_by_attributes.begin(),
                          window_info.order_by_attributes.end());
 
+  // If (p_key, o_key) is empty, no sort is needed.
+  if (sort_attributes.empty()) {
+    return logical_plan;
+  }
+
   std::vector<bool> sort_directions(
       window_info.partition_by_attributes.size(), true);
   sort_directions.insert(sort_directions.end(),

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/query_optimizer/tests/execution_generator/Select.test
----------------------------------------------------------------------
diff --git a/query_optimizer/tests/execution_generator/Select.test b/query_optimizer/tests/execution_generator/Select.test
index 05f7108..16127cc 100644
--- a/query_optimizer/tests/execution_generator/Select.test
+++ b/query_optimizer/tests/execution_generator/Select.test
@@ -950,3 +950,42 @@ WHERE double_col < 0
 +--------------------+
 |                   5|
 +--------------------+
+==
+
+# Window Aggregation Test.
+# Currently this is not supported, an empty table will be returned.
+SELECT avg(int_col) OVER w FROM test
+WINDOW w AS
+(PARTITION BY char_col
+ ORDER BY long_col DESC NULLS LAST
+ ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW);
+--
++------------------------+
+|avg(int_col)            |
++------------------------+
++------------------------+
+==
+
+SELECT int_col, sum(float_col) OVER
+(PARTITION BY char_col, long_col
+ ORDER BY double_col DESC NULLS LAST, int_col ASC NULLS FIRST
+ RANGE BETWEEN 3 PRECEDING AND 3 FOLLOWING)
+FROM test;
+--
++-----------+------------------------+
+|int_col    |sum(float_col)          |
++-----------+------------------------+
++-----------+------------------------+
+==
+
+SELECT sum(avg(int_col) OVER w) FROM test
+WINDOW w AS
+(PARTITION BY char_col
+ ORDER BY long_col
+ ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW);
+--
++------------------------+
+|sum(avg(int_col))       |
++------------------------+
+|                    NULL|
++------------------------+

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/query_optimizer/tests/resolver/Select.test
----------------------------------------------------------------------
diff --git a/query_optimizer/tests/resolver/Select.test b/query_optimizer/tests/resolver/Select.test
index 89ab84d..5e11ac0 100644
--- a/query_optimizer/tests/resolver/Select.test
+++ b/query_optimizer/tests/resolver/Select.test
@@ -3269,6 +3269,39 @@ TopLevelPlan
     type=Double NULL]
 ==
 
+SELECT avg(int_col) OVER w FROM test
+WINDOW w AS
+(ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING);
+--
+TopLevelPlan
++-plan=Project
+| +-input=WindowAggregate
+| | +-input=TableReference[relation_name=Test,relation_alias=test]
+| | | +-AttributeReference[id=0,name=int_col,relation=test,type=Int NULL]
+| | | +-AttributeReference[id=1,name=long_col,relation=test,type=Long]
+| | | +-AttributeReference[id=2,name=float_col,relation=test,type=Float]
+| | | +-AttributeReference[id=3,name=double_col,relation=test,type=Double NULL]
+| | | +-AttributeReference[id=4,name=char_col,relation=test,type=Char(20)]
+| | | +-AttributeReference[id=5,name=vchar_col,relation=test,
+| | |   type=VarChar(20) NULL]
+| | +-window_aggregate_expression=Alias[id=6,name=,alias=$window_aggregate0,
+| |   relation=$window_aggregate,type=Double NULL]
+| |   +-WindowAggregateFunction[function=AVG,window_name=w,is_ascending=[],
+| |     nulls_first=[],frame_mode=row,num_preceding=-1,num_following=-1]
+| |     +-arguments=
+| |     | +-AttributeReference[id=0,name=int_col,relation=test,type=Int NULL]
+| |     +-partition_by=
+| |     | +-[]
+| |     +-order_by=
+| |       +-[]
+| +-project_list=
+|   +-Alias[id=6,name=,alias=avg(int_col),relation=,type=Double NULL]
+|     +-AttributeReference[id=6,name=,alias=$window_aggregate0,
+|       relation=$window_aggregate,type=Double NULL]
++-output_attributes=
+  +-AttributeReference[id=6,name=,alias=avg(int_col),relation=,type=Double NULL]
+==
+
 SELECT int_col, sum(float_col) OVER w1 FROM test
 WINDOW w2 AS
 (PARTITION BY vchar_col, long_col

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/relational_operators/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/relational_operators/CMakeLists.txt b/relational_operators/CMakeLists.txt
index 91d1097..249441d 100644
--- a/relational_operators/CMakeLists.txt
+++ b/relational_operators/CMakeLists.txt
@@ -61,6 +61,7 @@ add_library(quickstep_relationaloperators_SortRunGenerationOperator SortRunGener
 add_library(quickstep_relationaloperators_TableGeneratorOperator TableGeneratorOperator.cpp TableGeneratorOperator.hpp)
 add_library(quickstep_relationaloperators_TextScanOperator TextScanOperator.cpp TextScanOperator.hpp)
 add_library(quickstep_relationaloperators_UpdateOperator UpdateOperator.cpp UpdateOperator.hpp)
+add_library(quickstep_relationaloperators_WindowAggregationOperator WindowAggregationOperator.cpp WindowAggregationOperator.hpp)
 add_library(quickstep_relationaloperators_WorkOrder ../empty_src.cpp WorkOrder.hpp)
 add_library(quickstep_relationaloperators_WorkOrderFactory WorkOrderFactory.cpp WorkOrderFactory.hpp)
 add_library(quickstep_relationaloperators_WorkOrder_proto
@@ -423,6 +424,18 @@ target_link_libraries(quickstep_relationaloperators_UpdateOperator
                       quickstep_threading_ThreadIDBasedMap
                       quickstep_utility_Macros
                       tmb)
+target_link_libraries(quickstep_relationaloperators_WindowAggregationOperator
+                      glog
+                      quickstep_catalog_CatalogRelation
+                      quickstep_queryexecution_QueryContext
+                      quickstep_queryexecution_WorkOrderProtosContainer
+                      quickstep_queryexecution_WorkOrdersContainer
+                      quickstep_relationaloperators_RelationalOperator
+                      quickstep_relationaloperators_WorkOrder
+                      quickstep_relationaloperators_WorkOrder_proto
+                      quickstep_storage_StorageBlockInfo
+                      quickstep_utility_Macros
+                      tmb)                      
 target_link_libraries(quickstep_relationaloperators_WorkOrder
                       glog
                       quickstep_queryexecution_QueryExecutionTypedefs
@@ -487,6 +500,7 @@ target_link_libraries(quickstep_relationaloperators
                       quickstep_relationaloperators_TableGeneratorOperator
                       quickstep_relationaloperators_TextScanOperator
                       quickstep_relationaloperators_UpdateOperator
+                      quickstep_relationaloperators_WindowAggregationOperator
                       quickstep_relationaloperators_WorkOrder
                       quickstep_relationaloperators_WorkOrderFactory
                       quickstep_relationaloperators_WorkOrder_proto)

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/relational_operators/WindowAggregationOperator.cpp
----------------------------------------------------------------------
diff --git a/relational_operators/WindowAggregationOperator.cpp b/relational_operators/WindowAggregationOperator.cpp
new file mode 100644
index 0000000..93cb9d4
--- /dev/null
+++ b/relational_operators/WindowAggregationOperator.cpp
@@ -0,0 +1,82 @@
+/**
+ *   Copyright 2011-2015 Quickstep Technologies LLC.
+ *   Copyright 2015-2016 Pivotal Software, Inc.
+ *   Copyright 2016, Quickstep Research Group, Computer Sciences Department,
+ *     University of Wisconsin\u2014Madison.
+ *
+ *   Licensed 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.
+ **/
+
+#include "relational_operators/WindowAggregationOperator.hpp"
+
+#include <vector>
+
+#include "query_execution/QueryContext.hpp"
+#include "query_execution/WorkOrderProtosContainer.hpp"
+#include "query_execution/WorkOrdersContainer.hpp"
+#include "relational_operators/WorkOrder.pb.h"
+#include "storage/StorageBlockInfo.hpp"
+
+#include "tmb/id_typedefs.h"
+
+namespace quickstep {
+
+bool WindowAggregationOperator::getAllWorkOrders(
+    WorkOrdersContainer *container,
+    QueryContext *query_context,
+    StorageManager *storage_manager,
+    const tmb::client_id scheduler_client_id,
+    tmb::MessageBus *bus) {
+  DCHECK(query_context != nullptr);
+
+  if (blocking_dependencies_met_ && !generated_) {
+    container->addNormalWorkOrder(
+        new WindowAggregationWorkOrder(
+            query_id_,
+            query_context->releaseWindowAggregationState(window_aggregation_state_index_),
+            query_context->getInsertDestination(output_destination_index_)),
+        op_index_);
+    generated_ = true;
+  }
+
+  return generated_;
+}
+
+bool WindowAggregationOperator::getAllWorkOrderProtos(WorkOrderProtosContainer *container) {
+  if (blocking_dependencies_met_ && !generated_) {
+    container->addWorkOrderProto(createWorkOrderProto(), op_index_);
+    generated_ = true;
+  }
+
+  return generated_;
+}
+
+serialization::WorkOrder* WindowAggregationOperator::createWorkOrderProto() {
+  serialization::WorkOrder *proto = new serialization::WorkOrder;
+  proto->set_work_order_type(serialization::WINDOW_AGGREGATION);
+  proto->set_query_id(query_id_);
+  proto->SetExtension(serialization::WindowAggregationWorkOrder::window_aggr_state_index,
+                      window_aggregation_state_index_);
+  proto->SetExtension(serialization::WindowAggregationWorkOrder::insert_destination_index,
+                      output_destination_index_);
+
+  return proto;
+}
+
+
+void WindowAggregationWorkOrder::execute() {
+  std::cout << "Window aggregation is not supported yet.\n"
+      << "An empty table is returned\n";
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/relational_operators/WindowAggregationOperator.hpp
----------------------------------------------------------------------
diff --git a/relational_operators/WindowAggregationOperator.hpp b/relational_operators/WindowAggregationOperator.hpp
new file mode 100644
index 0000000..f3dfd14
--- /dev/null
+++ b/relational_operators/WindowAggregationOperator.hpp
@@ -0,0 +1,166 @@
+/**
+ *   Copyright 2011-2015 Quickstep Technologies LLC.
+ *   Copyright 2015-2016 Pivotal Software, Inc.
+ *   Copyright 2016, Quickstep Research Group, Computer Sciences Department,
+ *     University of Wisconsin\u2014Madison.
+ *
+ *   Licensed 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.
+ **/
+
+#ifndef QUICKSTEP_RELATIONAL_OPERATORS_WINDOW_AGGREGATION_OPERATOR_HPP_
+#define QUICKSTEP_RELATIONAL_OPERATORS_WINDOW_AGGREGATION_OPERATOR_HPP_
+
+#include <vector>
+
+#include "catalog/CatalogRelation.hpp"
+#include "query_execution/QueryContext.hpp"
+#include "relational_operators/RelationalOperator.hpp"
+#include "relational_operators/WorkOrder.hpp"
+#include "storage/StorageBlockInfo.hpp"
+#include "utility/Macros.hpp"
+
+#include "glog/logging.h"
+
+#include "tmb/id_typedefs.h"
+
+namespace tmb { class MessageBus; }
+
+namespace quickstep {
+
+class StorageManager;
+class WindowAggregationOperationState;
+class WorkOrderProtosContainer;
+class WorkOrdersContainer;
+
+namespace serialization { class WorkOrder; }
+
+/** \addtogroup RelationalOperators
+ *  @{
+ */
+
+/**
+ * @brief An operator which performs window aggregation over a relation.
+ **/
+class WindowAggregationOperator : public RelationalOperator {
+ public:
+  /**
+   * @brief Constructor.
+   *
+   * @param query_id The ID of this query.
+   * @param input_relation The relation to perform aggregation over.
+   * @param window_aggregation_state_index The index of WindowAggregationState
+   *                                       in QueryContext.
+   * @param output_destination_index The index of InsertDestination in
+   *                                 QueryContext for the output.
+   **/
+  WindowAggregationOperator(const std::size_t query_id,
+                            const CatalogRelation &output_relation,
+                            const QueryContext::window_aggregation_state_id window_aggregation_state_index,
+                            const QueryContext::insert_destination_id output_destination_index)
+      : RelationalOperator(query_id),
+        output_relation_(output_relation),
+        window_aggregation_state_index_(window_aggregation_state_index),
+        output_destination_index_(output_destination_index),
+        generated_(false) {}
+
+  ~WindowAggregationOperator() override {}
+
+  bool getAllWorkOrders(WorkOrdersContainer *container,
+                        QueryContext *query_context,
+                        StorageManager *storage_manager,
+                        const tmb::client_id scheduler_client_id,
+                        tmb::MessageBus *bus) override;
+
+  bool getAllWorkOrderProtos(WorkOrderProtosContainer *container) override;
+
+  const relation_id getOutputRelationID() const override {
+    return output_relation_.getID();
+  }
+
+  QueryContext::insert_destination_id getInsertDestinationID() const override {
+    return output_destination_index_;
+  }
+
+ private:
+  /**
+   * @brief Create Work Order proto.
+   *
+   * @return A window aggregation work order.
+   **/
+  serialization::WorkOrder* createWorkOrderProto();
+
+  const CatalogRelation &output_relation_;
+  const QueryContext::window_aggregation_state_id window_aggregation_state_index_;
+  const QueryContext::insert_destination_id output_destination_index_;
+  bool generated_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowAggregationOperator);
+};
+
+/**
+ * @brief A WorkOrder produced by WindowAggregationOperator.
+ **/
+class WindowAggregationWorkOrder : public WorkOrder {
+ public:
+  /**
+   * @brief Constructor
+   *
+   * @param query_id The ID of this query.
+   * @param state The WindowAggregationOperatorState to use.
+   * @param output_destination The InsertDestination for output.
+   **/
+  WindowAggregationWorkOrder(const std::size_t query_id,
+                             WindowAggregationOperationState *state,
+                             InsertDestination *output_destination)
+      : WorkOrder(query_id),
+        state_(state),
+        output_destination_(output_destination)  {}
+
+  ~WindowAggregationWorkOrder() override {}
+
+  /**
+   * @brief Get the pointer to WindowAggregationOperationState.
+   * @note This is a quickfix for "unused variable". After the window aggregate
+   *       functions are built, these methods might be dropped.
+   *
+   * @return A pointer to the window aggregation operation state.
+   **/
+  WindowAggregationOperationState* state() {
+    return state_;
+  }
+
+  /**
+   * @brief Get the pointer to output destination.
+   * @note This is a quickfix for "unused variable". After the window aggregate
+   *       functions are built, these methods might be dropped.
+   *
+   * @return A pointer to the output destination.
+   **/
+  InsertDestination* output_destination() {
+    return output_destination_;
+  }
+
+  void execute() override;
+
+ private:
+  WindowAggregationOperationState *state_;
+  InsertDestination *output_destination_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowAggregationWorkOrder);
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_RELATIONAL_OPERATORS_WINDOW_AGGREGATION_OPERATOR_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/relational_operators/WorkOrder.proto
----------------------------------------------------------------------
diff --git a/relational_operators/WorkOrder.proto b/relational_operators/WorkOrder.proto
index 3ed065a..69dee1b 100644
--- a/relational_operators/WorkOrder.proto
+++ b/relational_operators/WorkOrder.proto
@@ -41,6 +41,7 @@ enum WorkOrderType {
   TABLE_GENERATOR = 17;
   TEXT_SCAN = 18;
   UPDATE = 19;
+  WINDOW_AGGREGATION = 20;
 }
 
 message WorkOrder {
@@ -243,3 +244,11 @@ message UpdateWorkOrder {
     optional fixed64 block_id = 325;
   }
 }
+
+message WindowAggregationWorkOrder {
+  extend WorkOrder {
+    // All required
+    optional uint32 window_aggr_state_index = 336;
+    optional int32 insert_destination_index = 337;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/storage/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/storage/CMakeLists.txt b/storage/CMakeLists.txt
index b536411..9df66e1 100644
--- a/storage/CMakeLists.txt
+++ b/storage/CMakeLists.txt
@@ -131,6 +131,9 @@ QS_PROTOBUF_GENERATE_CPP(storage_InsertDestination_proto_srcs
 QS_PROTOBUF_GENERATE_CPP(storage_StorageBlockLayout_proto_srcs
                          storage_StorageBlockLayout_proto_hdrs
                          StorageBlockLayout.proto)
+QS_PROTOBUF_GENERATE_CPP(storage_WindowAggregationOperationState_proto_srcs
+                         storage_WindowAggregationOperationState_proto_hdrs
+                         WindowAggregationOperationState.proto)
 
 if (ENABLE_DISTRIBUTED)
   GRPC_GENERATE_CPP(storage_DataExchange_proto_srcs
@@ -254,6 +257,11 @@ add_library(quickstep_storage_TupleReference ../empty_src.cpp TupleReference.hpp
 add_library(quickstep_storage_TupleStorageSubBlock TupleStorageSubBlock.cpp TupleStorageSubBlock.hpp)
 add_library(quickstep_storage_ValueAccessor ../empty_src.cpp ValueAccessor.hpp)
 add_library(quickstep_storage_ValueAccessorUtil ../empty_src.cpp ValueAccessorUtil.hpp)
+add_library(quickstep_storage_WindowAggregationOperationState
+            WindowAggregationOperationState.hpp
+            WindowAggregationOperationState.cpp)
+add_library(quickstep_storage_WindowAggregationOperationState_proto ${storage_WindowAggregationOperationState_proto_srcs})
+
 
 # Link dependencies:
 target_link_libraries(quickstep_storage_AggregationOperationState
@@ -1038,6 +1046,27 @@ target_link_libraries(quickstep_storage_ValueAccessorUtil
                       quickstep_storage_ValueAccessor
                       quickstep_types_containers_ColumnVectorsValueAccessor
                       quickstep_utility_Macros)
+target_link_libraries(quickstep_storage_WindowAggregationOperationState
+                      glog
+                      quickstep_catalog_CatalogDatabaseLite
+                      quickstep_catalog_CatalogRelationSchema
+                      quickstep_catalog_CatalogTypedefs
+                      quickstep_expressions_ExpressionFactories
+                      quickstep_expressions_Expressions_proto
+                      quickstep_expressions_aggregation_AggregateFunction
+                      quickstep_expressions_aggregation_AggregateFunctionFactory
+                      quickstep_expressions_aggregation_AggregationHandle
+                      quickstep_expressions_aggregation_AggregationID
+                      quickstep_expressions_scalar_Scalar
+                      quickstep_expressions_scalar_ScalarAttribute
+                      quickstep_storage_StorageBlockInfo
+                      quickstep_storage_StorageManager
+                      quickstep_storage_WindowAggregationOperationState_proto
+                      quickstep_utility_Macros)
+target_link_libraries(quickstep_storage_WindowAggregationOperationState_proto
+                      quickstep_expressions_aggregation_AggregateFunction_proto
+                      quickstep_expressions_Expressions_proto
+                      ${PROTOBUF_LIBRARY})
 
 # Module all-in-one library:
 add_library(quickstep_storage ../empty_src.cpp StorageModule.hpp)
@@ -1096,7 +1125,9 @@ target_link_libraries(quickstep_storage
                       quickstep_storage_TupleReference
                       quickstep_storage_TupleStorageSubBlock
                       quickstep_storage_ValueAccessor
-                      quickstep_storage_ValueAccessorUtil)
+                      quickstep_storage_ValueAccessorUtil
+                      quickstep_storage_WindowAggregationOperationState
+                      quickstep_storage_WindowAggregationOperationState_proto)
 if (QUICKSTEP_HAVE_FILE_MANAGER_HDFS)
   target_link_libraries(quickstep_storage
                         quickstep_storage_FileManagerHdfs)
@@ -1636,6 +1667,18 @@ target_link_libraries(StorageManager_unittest
                       quickstep_storage_StorageBlockInfo
                       quickstep_storage_StorageManager
                       quickstep_utility_ShardedLockManager)
+
+add_executable(WindowAggregationOperationState_unittest
+               "${CMAKE_CURRENT_SOURCE_DIR}/tests/WindowAggregationOperationState_unittest.cpp")
+target_link_libraries(WindowAggregationOperationState_unittest
+                      gtest
+                      gtest_main
+                      quickstep_catalog_CatalogDatabase
+                      quickstep_catalog_CatalogRelation
+                      quickstep_catalog_CatalogTypedefs
+                      quickstep_storage_WindowAggregationOperationState
+                      quickstep_storage_WindowAggregationOperationState_proto
+                      ${LIBS})
 if (QUICKSTEP_HAVE_LIBNUMA)
   target_link_libraries(StorageManager_unittest
                         ${LIBNUMA_LIBRARY})

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/storage/WindowAggregationOperationState.cpp
----------------------------------------------------------------------
diff --git a/storage/WindowAggregationOperationState.cpp b/storage/WindowAggregationOperationState.cpp
new file mode 100644
index 0000000..a0bcc37
--- /dev/null
+++ b/storage/WindowAggregationOperationState.cpp
@@ -0,0 +1,179 @@
+/**
+ *   Copyright 2011-2015 Quickstep Technologies LLC.
+ *   Copyright 2015-2016 Pivotal Software, Inc.
+ *   Copyright 2016, Quickstep Research Group, Computer Sciences Department,
+ *     University of Wisconsin\u2014Madison.
+ *
+ *   Licensed 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.
+ **/
+
+#include "storage/WindowAggregationOperationState.hpp"
+
+#include <cstddef>
+#include <cstdio>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "catalog/CatalogDatabaseLite.hpp"
+#include "catalog/CatalogRelationSchema.hpp"
+#include "catalog/CatalogTypedefs.hpp"
+#include "expressions/ExpressionFactories.hpp"
+#include "expressions/Expressions.pb.h"
+#include "expressions/aggregation/AggregateFunction.hpp"
+#include "expressions/aggregation/AggregateFunctionFactory.hpp"
+#include "expressions/aggregation/AggregationHandle.hpp"
+#include "expressions/aggregation/AggregationID.hpp"
+#include "expressions/scalar/Scalar.hpp"
+#include "expressions/scalar/ScalarAttribute.hpp"
+#include "storage/StorageManager.hpp"
+#include "storage/WindowAggregationOperationState.pb.h"
+
+#include "glog/logging.h"
+
+namespace quickstep {
+
+WindowAggregationOperationState::WindowAggregationOperationState(
+    const CatalogRelationSchema &input_relation,
+    const AggregateFunction *window_aggregate_function,
+    std::vector<std::unique_ptr<const Scalar>> &&arguments,
+    std::vector<std::unique_ptr<const Scalar>> &&partition_by_attributes,
+    const bool is_row,
+    const std::int64_t num_preceding,
+    const std::int64_t num_following,
+    StorageManager *storage_manager)
+    : input_relation_(input_relation),
+      arguments_(std::move(arguments)),
+      partition_by_attributes_(std::move(partition_by_attributes)),
+      is_row_(is_row),
+      num_preceding_(num_preceding),
+      num_following_(num_following),
+      storage_manager_(storage_manager) {
+  // Get the Types of this window aggregate's arguments so that we can create an
+  // AggregationHandle.
+  // TODO(Shixuan): Next step: New handles for window aggregation function.
+  std::vector<const Type*> argument_types;
+  for (const std::unique_ptr<const Scalar> &argument : arguments_) {
+    argument_types.emplace_back(&argument->getType());
+  }
+
+  // Check if window aggregate function could apply to the arguments.
+  DCHECK(window_aggregate_function->canApplyToTypes(argument_types));
+
+  // Create the handle and initial state.
+  window_aggregation_handle_.reset(
+      window_aggregate_function->createHandle(argument_types));
+  window_aggregation_state_.reset(
+      window_aggregation_handle_->createInitialState());
+
+#ifdef QUICKSTEP_ENABLE_VECTOR_COPY_ELISION_SELECTION
+  // See if all of this window aggregate's arguments are attributes in the input
+  // relation. If so, remember the attribute IDs so that we can do copy elision
+  // when actually performing the window aggregation.
+  arguments_as_attributes_.reserve(arguments_.size());
+  for (const std::unique_ptr<const Scalar> &argument : arguments_) {
+    const attribute_id argument_id = argument->getAttributeIdForValueAccessor();
+    if (argument_id == -1) {
+      arguments_as_attributes_.clear();
+      break;
+    } else {
+      DCHECK_EQ(input_relation_.getID(), argument->getRelationIdForValueAccessor());
+      arguments_as_attributes_.push_back(argument_id);
+    }
+  }
+#endif
+}
+
+WindowAggregationOperationState* WindowAggregationOperationState::ReconstructFromProto(
+    const serialization::WindowAggregationOperationState &proto,
+    const CatalogDatabaseLite &database,
+    StorageManager *storage_manager) {
+  DCHECK(ProtoIsValid(proto, database));
+
+  // Rebuild contructor arguments from their representation in 'proto'.
+  const AggregateFunction *aggregate_function
+      = &AggregateFunctionFactory::ReconstructFromProto(proto.function());
+
+  std::vector<std::unique_ptr<const Scalar>> arguments;
+  arguments.reserve(proto.arguments_size());
+  for (int argument_idx = 0; argument_idx < proto.arguments_size(); ++argument_idx) {
+    arguments.emplace_back(ScalarFactory::ReconstructFromProto(
+        proto.arguments(argument_idx),
+        database));
+  }
+
+  std::vector<std::unique_ptr<const Scalar>> partition_by_attributes;
+  for (int attribute_idx = 0;
+       attribute_idx < proto.partition_by_attributes_size();
+       ++attribute_idx) {
+    partition_by_attributes.emplace_back(ScalarFactory::ReconstructFromProto(
+        proto.partition_by_attributes(attribute_idx),
+        database));
+  }
+
+  const bool is_row = proto.is_row();
+  const std::int64_t num_preceding = proto.num_preceding();
+  const std::int64_t num_following = proto.num_following();
+
+  return new WindowAggregationOperationState(database.getRelationSchemaById(proto.relation_id()),
+                                             aggregate_function,
+                                             std::move(arguments),
+                                             std::move(partition_by_attributes),
+                                             is_row,
+                                             num_preceding,
+                                             num_following,
+                                             storage_manager);
+}
+
+bool WindowAggregationOperationState::ProtoIsValid(const serialization::WindowAggregationOperationState &proto,
+                                                   const CatalogDatabaseLite &database) {
+  if (!proto.IsInitialized() ||
+      !database.hasRelationWithId(proto.relation_id())) {
+    return false;
+  }
+
+  if (!AggregateFunctionFactory::ProtoIsValid(proto.function())) {
+    return false;
+  }
+
+  // TODO(chasseur): We may also want to check that the specified
+  // AggregateFunction is applicable to the specified arguments, but that
+  // requires partial deserialization and may be too heavyweight for this
+  // method.
+  // TODO(Shixuan): The TODO for AggregateFunction could also be applied here.
+  for (int argument_idx = 0;
+       argument_idx < proto.arguments_size();
+       ++argument_idx) {
+    if (!ScalarFactory::ProtoIsValid(proto.arguments(argument_idx), database)) {
+      return false;
+    }
+  }
+
+  for (int attribute_idx = 0;
+       attribute_idx < proto.partition_by_attributes_size();
+       ++attribute_idx) {
+    if (!ScalarFactory::ProtoIsValid(proto.partition_by_attributes(attribute_idx),
+                                     database)) {
+      return false;
+    }
+  }
+
+  if (proto.num_preceding() < -1 || proto.num_following() < -1) {
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/storage/WindowAggregationOperationState.hpp
----------------------------------------------------------------------
diff --git a/storage/WindowAggregationOperationState.hpp b/storage/WindowAggregationOperationState.hpp
new file mode 100644
index 0000000..d7b3e6a
--- /dev/null
+++ b/storage/WindowAggregationOperationState.hpp
@@ -0,0 +1,177 @@
+/**
+ *   Copyright 2011-2015 Quickstep Technologies LLC.
+ *   Copyright 2015-2016 Pivotal Software, Inc.
+ *   Copyright 2016, Quickstep Research Group, Computer Sciences Department,
+ *     University of Wisconsin\u2014Madison.
+ *
+ *   Licensed 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.
+ **/
+
+#ifndef QUICKSTEP_STORAGE_WINDOW_AGGREGATION_OPERATION_STATE_HPP_
+#define QUICKSTEP_STORAGE_WINDOW_AGGREGATION_OPERATION_STATE_HPP_
+
+#include <cstddef>
+#include <memory>
+#include <vector>
+
+#include "catalog/CatalogTypedefs.hpp"
+#include "expressions/aggregation/AggregationHandle.hpp"
+#include "expressions/scalar/Scalar.hpp"
+#include "expressions/scalar/ScalarAttribute.hpp"
+#include "storage/StorageBlockInfo.hpp"
+#include "storage/WindowAggregationOperationState.pb.h"
+#include "utility/Macros.hpp"
+
+namespace quickstep {
+
+class AggregateFunction;
+class CatalogDatabaseLite;
+class CatalogRelationSchema;
+class InsertDestination;
+class StorageManager;
+
+/** \addtogroup Storage
+ *  @{
+ */
+
+/**
+ * @brief Helper class for maintaining the state of window aggregation. 
+ **/
+class WindowAggregationOperationState {
+ public:
+  /**
+   * @brief Constructor for window aggregation operation state.
+   *
+   * @param input_relation Input relation on which window aggregation is computed.
+   * @param window_aggregate_functions The window aggregate function to be
+   *                                   computed.
+   * @param arguments A list of argument expressions to that aggregate.
+   * @param partition_by_attributes A list of window partition key.
+   * @param is_row True if the window frame is calculated by ROW, false if it is
+   *               calculated by RANGE.
+   * @param num_preceding The number of rows/range for the tuples preceding the
+   *                      current row. -1 means UNBOUNDED PRECEDING.
+   * @param num_following The number of rows/range for the tuples following the
+   *                      current row. -1 means UNBOUNDED FOLLOWING.
+   * @param storage_manager The StorageManager to use for allocating hash
+   *        tables.
+   */
+  WindowAggregationOperationState(const CatalogRelationSchema &input_relation,
+                                  const AggregateFunction *window_aggregate_function,
+                                  std::vector<std::unique_ptr<const Scalar>> &&arguments,
+                                  std::vector<std::unique_ptr<const Scalar>> &&partition_by_attributes,
+                                  const bool is_row,
+                                  const std::int64_t num_preceding,
+                                  const std::int64_t num_following,
+                                  StorageManager *storage_manager);
+
+  ~WindowAggregationOperationState() {}
+
+  /**
+   * @brief Generate the window aggregation operation state from the serialized
+   *        Protocol Buffer representation.
+   *
+   * @param proto A serialized protocol buffer representation of a
+   *        WindowAggregationOperationState, originally generated by the
+   *        optimizer.
+   * @param database The database for resolving relation and attribute
+   *        references.
+   * @param storage_manager The StorageManager to use.
+   **/
+  static WindowAggregationOperationState* ReconstructFromProto(
+      const serialization::WindowAggregationOperationState &proto,
+      const CatalogDatabaseLite &database,
+      StorageManager *storage_manager);
+
+  /**
+   * @brief Check whether a serialization::AggregationOperationState is
+   *        fully-formed and all parts are valid.
+   *
+   * @param proto A serialized Protocol Buffer representation of an
+   *        AggregationOperationState, originally generated by the optimizer.
+   * @param database The Database to resolve relation and attribute references
+   *        in.
+   * @return Whether proto is fully-formed and valid.
+   **/
+  static bool ProtoIsValid(const serialization::WindowAggregationOperationState &proto,
+                           const CatalogDatabaseLite &database);
+
+  /**
+   * @brief Get the is_row info.
+   * @note This is a quickfix for "unused variable". After the window aggregate
+   *       functions are built, these methods might be dropped.
+   * 
+   * @return True if the frame mode is ROW, false if it is RANGE.
+   **/
+  const bool is_row() const { return is_row_; }
+
+  /**
+   * @brief Get the num_preceding info.
+   * @note This is a quickfix for "unused variable". After the window aggregate
+   *       functions are built, these methods might be dropped.
+   *
+   * @return The number of rows/range that precedes the current row.
+   **/
+  const std::int64_t num_preceding() const { return num_preceding_; }
+
+  /**
+   * @brief Get the num_following info.
+   * @note This is a quickfix for "unused variable". After the window aggregate
+   *       functions are built, these methods might be dropped.
+   *
+   * @return The number of rows/range that follows the current row.
+   **/
+  const std::int64_t num_following() const { return num_following_; }
+
+  /**
+   * @brief Get the pointer to StorageManager.
+   * @note This is a quickfix for "unused variable". After the window aggregate
+   *       functions are built, these methods might be dropped.
+   *
+   * @return A pointer to the storage manager.
+   **/
+  StorageManager *storage_manager() { return storage_manager_; }
+
+ private:
+  const CatalogRelationSchema &input_relation_;
+
+  // TODO(Shixuan): Handle and State for window aggregation will be needed for
+  //                actual calculation.
+  std::unique_ptr<AggregationHandle> window_aggregation_handle_;
+  std::unique_ptr<AggregationState> window_aggregation_state_;
+  std::vector<std::unique_ptr<const Scalar>> arguments_;
+
+  // We don't add order_by_attributes here since it is not needed after sorting.
+  std::vector<std::unique_ptr<const Scalar>> partition_by_attributes_;
+
+  // Window framing information.
+  const bool is_row_;
+  const std::int64_t num_preceding_;
+  const std::int64_t num_following_;
+
+  StorageManager *storage_manager_;
+
+#ifdef QUICKSTEP_ENABLE_VECTOR_COPY_ELISION_SELECTION
+  // If all an aggregate's argument expressions are simply attributes in
+  // 'input_relation_', then this caches the attribute IDs of those arguments.
+  std::vector<attribute_id> arguments_as_attributes_;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(WindowAggregationOperationState);
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_STORAGE_WINDOW_AGGREGATION_OPERATION_STATE_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/storage/WindowAggregationOperationState.proto
----------------------------------------------------------------------
diff --git a/storage/WindowAggregationOperationState.proto b/storage/WindowAggregationOperationState.proto
new file mode 100644
index 0000000..c7bd0ef
--- /dev/null
+++ b/storage/WindowAggregationOperationState.proto
@@ -0,0 +1,33 @@
+//   Copyright 2011-2015 Quickstep Technologies LLC.
+//   Copyright 2015-2016 Pivotal Software, Inc.
+//   Copyright 2016, Quickstep Research Group, Computer Sciences Department,
+//     University of Wisconsin\u2014Madison.
+//
+//   Licensed 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.
+
+syntax = "proto2";
+
+package quickstep.serialization;
+
+import "expressions/aggregation/AggregateFunction.proto";
+import "expressions/Expressions.proto";
+
+message WindowAggregationOperationState {
+  required int32 relation_id = 1;
+  required AggregateFunction function = 2;
+  repeated Scalar arguments = 3;
+  repeated Scalar partition_by_attributes = 4;
+  required bool is_row = 5;
+  required int64 num_preceding = 6;  // -1 means UNBOUNDED PRECEDING.
+  required int64 num_following = 7;  // -1 means UNBOUNDED FOLLOWING.
+}

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/7671a589/storage/tests/WindowAggregationOperationState_unittest.cpp
----------------------------------------------------------------------
diff --git a/storage/tests/WindowAggregationOperationState_unittest.cpp b/storage/tests/WindowAggregationOperationState_unittest.cpp
new file mode 100644
index 0000000..c572034
--- /dev/null
+++ b/storage/tests/WindowAggregationOperationState_unittest.cpp
@@ -0,0 +1,92 @@
+/**
+ *   Copyright 2011-2015 Quickstep Technologies LLC.
+ *   Copyright 2015-2016 Pivotal Software, Inc.
+ *   Copyright 2016, Quickstep Research Group, Computer Sciences Department,
+ *     University of Wisconsin\u2014Madison.
+ *
+ *   Licensed 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.
+ **/
+
+#include <cstddef>
+#include <memory>
+
+#include "catalog/CatalogDatabase.hpp"
+#include "catalog/CatalogRelation.hpp"
+#include "catalog/CatalogTypedefs.hpp"
+#include "expressions/aggregation/AggregateFunction.pb.h"
+#include "storage/WindowAggregationOperationState.hpp"
+#include "storage/WindowAggregationOperationState.pb.h"
+
+#include "gtest/gtest.h"
+
+using std::unique_ptr;
+
+namespace quickstep {
+
+namespace {
+  constexpr relation_id kInvalidTableId = 100;
+  constexpr std::int64_t kInvalidNum = -10;
+  constexpr std::int64_t kValidNum = 10;
+}  // namespace
+
+class WindowAggregationOperationStateProtoTest : public ::testing::Test {
+ protected:
+  virtual void SetUp() {
+    database_.reset(new CatalogDatabase(nullptr, "db"));
+    rel_id_ = database_->addRelation(new CatalogRelation(nullptr, "rel"));
+  }
+
+  unique_ptr<CatalogDatabase> database_;
+  relation_id rel_id_;
+};
+
+TEST_F(WindowAggregationOperationStateProtoTest, UninitializationTest) {
+  serialization::WindowAggregationOperationState proto;
+  EXPECT_FALSE(WindowAggregationOperationState::ProtoIsValid(proto, *database_.get()));
+}
+
+TEST_F(WindowAggregationOperationStateProtoTest, InvalidRelationIdTest) {
+  serialization::WindowAggregationOperationState proto;
+  proto.set_relation_id(kInvalidTableId);
+  proto.mutable_function()->set_aggregation_id(serialization::AggregateFunction::AVG);
+  proto.set_is_row(true);
+  proto.set_num_preceding(kValidNum);
+  proto.set_num_following(kValidNum);
+  EXPECT_FALSE(WindowAggregationOperationState::ProtoIsValid(proto, *database_.get()));
+}
+
+TEST_F(WindowAggregationOperationStateProtoTest, InvalidNumTest) {
+  serialization::WindowAggregationOperationState proto;
+  proto.set_relation_id(rel_id_);
+  proto.mutable_function()->set_aggregation_id(serialization::AggregateFunction::AVG);
+  proto.set_is_row(true);
+  proto.set_num_preceding(kInvalidNum);
+  proto.set_num_following(kValidNum);
+  EXPECT_FALSE(WindowAggregationOperationState::ProtoIsValid(proto, *database_.get()));
+
+  proto.set_num_preceding(kValidNum);
+  proto.set_num_following(kInvalidNum);
+  EXPECT_FALSE(WindowAggregationOperationState::ProtoIsValid(proto, *database_.get()));
+}
+
+TEST_F(WindowAggregationOperationStateProtoTest, ValidTest) {
+  serialization::WindowAggregationOperationState proto;
+  proto.set_relation_id(rel_id_);
+  proto.mutable_function()->set_aggregation_id(serialization::AggregateFunction::AVG);
+  proto.set_is_row(true);
+  proto.set_num_preceding(kValidNum);
+  proto.set_num_following(kValidNum);
+  EXPECT_TRUE(WindowAggregationOperationState::ProtoIsValid(proto, *database_.get()));
+}
+
+}  // namespace quickstep


[12/30] incubator-quickstep git commit: Added QueryManagerDistributed.

Posted by ra...@apache.org.
Added QueryManagerDistributed.


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

Branch: refs/heads/quickstep-28-29
Commit: cdecf0b561d0cf1c80516d51401bd098a6263659
Parents: db0e7e3
Author: Zuyu Zhang <zu...@apache.org>
Authored: Tue Jul 12 22:04:54 2016 -0700
Committer: Zuyu Zhang <zu...@twitter.com>
Committed: Tue Jul 19 11:46:42 2016 -0700

----------------------------------------------------------------------
 CMakeLists.txt                               |  10 ++
 query_execution/CMakeLists.txt               |  34 +++-
 query_execution/QueryExecutionMessages.proto |  34 +++-
 query_execution/QueryExecutionTypedefs.hpp   |   3 +
 query_execution/QueryManagerDistributed.cpp  | 159 +++++++++++++++++++
 query_execution/QueryManagerDistributed.hpp  | 105 +++++++++++++
 query_execution/ShiftbossDirectory.hpp       | 181 ++++++++++++++++++++++
 7 files changed, 518 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/cdecf0b5/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0e5d3de..0bbde61 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -281,6 +281,16 @@ else()
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
   endif()
 
+  if (ENABLE_DISTRIBUTED)
+    # Clang reports such warning when using Protoc 3.0 beta.
+    if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
+      CHECK_CXX_COMPILER_FLAG("-Wno-extended-offsetof" COMPILER_HAS_WNO_EXTENDED_OFFSETOF)
+      if (COMPILER_HAS_WNO_EXTENDED_OFFSETOF)
+        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-extended-offsetof")
+      endif()
+    endif()
+  endif()
+
   # One of the protobuf headers includes a nested anonymous union within
   # another anonymous type. Ordinarily we work around this by compiling the
   # protobuf libraries themselves with "-Wno-nested-anon-types" and including

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/cdecf0b5/query_execution/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/query_execution/CMakeLists.txt b/query_execution/CMakeLists.txt
index 028531d..8c12a5d 100644
--- a/query_execution/CMakeLists.txt
+++ b/query_execution/CMakeLists.txt
@@ -28,10 +28,10 @@ else()
 endif()
 
 # Declare micro-libs:
+add_library(quickstep_queryexecution_AdmitRequestMessage ../empty_src.cpp AdmitRequestMessage.hpp)
 if (ENABLE_DISTRIBUTED)
   add_library(quickstep_queryexecution_BlockLocator BlockLocator.cpp BlockLocator.hpp)
 endif()
-add_library(quickstep_queryexecution_AdmitRequestMessage ../empty_src.cpp AdmitRequestMessage.hpp)
 add_library(quickstep_queryexecution_ForemanBase ../empty_src.cpp ForemanBase.hpp)
 add_library(quickstep_queryexecution_ForemanSingleNode ForemanSingleNode.cpp ForemanSingleNode.hpp)
 add_library(quickstep_queryexecution_PolicyEnforcer PolicyEnforcer.cpp PolicyEnforcer.hpp)
@@ -46,7 +46,13 @@ add_library(quickstep_queryexecution_QueryExecutionState ../empty_src.cpp QueryE
 add_library(quickstep_queryexecution_QueryExecutionTypedefs ../empty_src.cpp QueryExecutionTypedefs.hpp)
 add_library(quickstep_queryexecution_QueryExecutionUtil ../empty_src.cpp QueryExecutionUtil.hpp)
 add_library(quickstep_queryexecution_QueryManagerBase QueryManagerBase.cpp QueryManagerBase.hpp)
+if (ENABLE_DISTRIBUTED)
+  add_library(quickstep_queryexecution_QueryManagerDistributed QueryManagerDistributed.cpp QueryManagerDistributed.hpp)
+endif()
 add_library(quickstep_queryexecution_QueryManagerSingleNode QueryManagerSingleNode.cpp QueryManagerSingleNode.hpp)
+if (ENABLE_DISTRIBUTED)
+  add_library(quickstep_queryexecution_ShiftbossDirectory ../empty_src.cpp ShiftbossDirectory.hpp)
+endif()
 add_library(quickstep_queryexecution_WorkOrderProtosContainer ../empty_src.cpp WorkOrderProtosContainer.hpp)
 add_library(quickstep_queryexecution_WorkOrdersContainer WorkOrdersContainer.cpp WorkOrdersContainer.hpp)
 add_library(quickstep_queryexecution_Worker Worker.cpp Worker.hpp)
@@ -143,6 +149,7 @@ target_link_libraries(quickstep_queryexecution_QueryContext_proto
                       quickstep_utility_SortConfiguration_proto
                       ${PROTOBUF_LIBRARY})
 target_link_libraries(quickstep_queryexecution_QueryExecutionMessages_proto
+                      quickstep_relationaloperators_WorkOrder_proto
                       ${PROTOBUF_LIBRARY})
 target_link_libraries(quickstep_queryexecution_QueryExecutionState
                       glog
@@ -167,6 +174,22 @@ target_link_libraries(quickstep_queryexecution_QueryManagerBase
                       quickstep_storage_StorageBlockInfo
                       quickstep_utility_DAG
                       quickstep_utility_Macros)
+if (ENABLE_DISTRIBUTED)
+  target_link_libraries(quickstep_queryexecution_QueryManagerDistributed
+                        quickstep_queryexecution_QueryContext
+                        quickstep_queryexecution_QueryExecutionMessages_proto
+                        quickstep_queryexecution_QueryExecutionState
+                        quickstep_queryexecution_QueryExecutionTypedefs
+                        quickstep_queryexecution_QueryExecutionUtil
+                        quickstep_queryexecution_QueryManagerBase
+                        quickstep_queryexecution_ShiftbossDirectory
+                        quickstep_queryexecution_WorkOrderProtosContainer
+                        quickstep_relationaloperators_RelationalOperator
+                        quickstep_relationaloperators_WorkOrder_proto
+                        quickstep_utility_DAG
+                        quickstep_utility_Macros
+                        tmb)
+endif()
 target_link_libraries(quickstep_queryexecution_QueryManagerSingleNode
                       quickstep_catalog_CatalogTypedefs
                       quickstep_queryexecution_QueryContext
@@ -182,6 +205,11 @@ target_link_libraries(quickstep_queryexecution_QueryManagerSingleNode
                       quickstep_utility_DAG
                       quickstep_utility_Macros
                       tmb)
+if (ENABLE_DISTRIBUTED)
+  target_link_libraries(quickstep_queryexecution_ShiftbossDirectory
+                        quickstep_utility_Macros
+                        tmb)
+endif()
 target_link_libraries(quickstep_queryexecution_WorkOrderProtosContainer
                       glog
                       quickstep_relationaloperators_WorkOrder_proto
@@ -233,7 +261,9 @@ target_link_libraries(quickstep_queryexecution
                       quickstep_queryexecution_WorkerSelectionPolicy)
 if (ENABLE_DISTRIBUTED)
   target_link_libraries(quickstep_queryexecution
-                        quickstep_queryexecution_BlockLocator)
+                        quickstep_queryexecution_BlockLocator
+                        quickstep_queryexecution_QueryManagerDistributed
+                        quickstep_queryexecution_ShiftbossDirectory)
 endif()
 
 # Tests:

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/cdecf0b5/query_execution/QueryExecutionMessages.proto
----------------------------------------------------------------------
diff --git a/query_execution/QueryExecutionMessages.proto b/query_execution/QueryExecutionMessages.proto
index 65a8946..fa20993 100644
--- a/query_execution/QueryExecutionMessages.proto
+++ b/query_execution/QueryExecutionMessages.proto
@@ -16,22 +16,24 @@ syntax = "proto2";
 
 package quickstep.serialization;
 
+import "relational_operators/WorkOrder.proto";
+
 // Used for any messages that do not carry payloads.
 message EmptyMessage {
 }
 
 // Note: There are different types of completion messages for normal work orders
 // rebuild work orders. This can be potentially helpful when we want to collect
-// different statistics for executing different types of work orders. 
-// e.g. In select normal work order completion message, we could be interested 
-// in the selectivity of the block whose work order got execute. In rebuild work 
-// order completion message, we may be interested in adding the compression 
+// different statistics for executing different types of work orders.
+// e.g. In select normal work order completion message, we could be interested
+// in the selectivity of the block whose work order got execute. In rebuild work
+// order completion message, we may be interested in adding the compression
 // ratio or dictionary size of the rebuilt block.
 
-// TODO(harshad) : If there are different fields in the two message types below, 
+// TODO(harshad) : If there are different fields in the two message types below,
 // create a base message class called WorkOrderCompletionMessage and make the
 // two classes below extend the base class. All the common fields in both the
-// classes can be moved to the base class. 
+// classes can be moved to the base class.
 
 // A message sent upon completion of a normal (not rebuild) WorkOrder execution.
 message NormalWorkOrderCompletionMessage {
@@ -70,6 +72,26 @@ message WorkOrdersAvailableMessage {
   required uint64 query_id = 2;
 }
 
+// Distributed version related messages.
+message WorkOrderMessage {
+  required uint64 query_id = 1;
+  required uint64 operator_index = 2;
+  required WorkOrder work_order = 3;
+}
+
+message InitiateRebuildMessage {
+  required uint64 query_id = 1;
+  required uint64 operator_index = 2;
+  required uint64 insert_destination_index = 3;
+  required  int32 relation_id = 4;
+}
+
+message InitiateRebuildResponseMessage {
+  required uint64 query_id = 1;
+  required uint64 operator_index = 2;
+  required uint64 num_rebuild_work_orders = 3;
+}
+
 // BlockLocator related messages.
 message BlockDomainRegistrationMessage {
   // Format IP:Port, i.e., "0.0.0.0:0".

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/cdecf0b5/query_execution/QueryExecutionTypedefs.hpp
----------------------------------------------------------------------
diff --git a/query_execution/QueryExecutionTypedefs.hpp b/query_execution/QueryExecutionTypedefs.hpp
index 9d1060f..61e76d7 100644
--- a/query_execution/QueryExecutionTypedefs.hpp
+++ b/query_execution/QueryExecutionTypedefs.hpp
@@ -73,6 +73,9 @@ enum QueryExecutionMessageType : message_type_id {
   kPoisonMessage,  // From the main thread to Foreman and Workers.
 
 #ifdef QUICKSTEP_DISTRIBUTED
+  kInitiateRebuildMessage,  // From Foreman to Shiftboss.
+  kInitiateRebuildResponseMessage,  // From Shiftboss to Foreman.
+
   // BlockLocator related messages, sorted in a life cycle of StorageManager
   // with a unique block domain.
   kBlockDomainRegistrationMessage,  // From Worker to BlockLocator.

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/cdecf0b5/query_execution/QueryManagerDistributed.cpp
----------------------------------------------------------------------
diff --git a/query_execution/QueryManagerDistributed.cpp b/query_execution/QueryManagerDistributed.cpp
new file mode 100644
index 0000000..e906fa5
--- /dev/null
+++ b/query_execution/QueryManagerDistributed.cpp
@@ -0,0 +1,159 @@
+/**
+ *   Licensed 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.
+ **/
+
+#include "query_execution/QueryManagerDistributed.hpp"
+
+#include <cstddef>
+#include <cstdlib>
+#include <memory>
+#include <utility>
+
+#include "query_execution/QueryContext.hpp"
+#include "query_execution/QueryExecutionMessages.pb.h"
+#include "query_execution/QueryExecutionTypedefs.hpp"
+#include "query_execution/QueryExecutionUtil.hpp"
+#include "query_execution/ShiftbossDirectory.hpp"
+#include "query_execution/WorkOrderProtosContainer.hpp"
+#include "relational_operators/RelationalOperator.hpp"
+#include "relational_operators/WorkOrder.pb.h"
+#include "utility/DAG.hpp"
+
+#include "glog/logging.h"
+
+#include "tmb/id_typedefs.h"
+
+using std::free;
+using std::malloc;
+using std::move;
+using std::size_t;
+using std::unique_ptr;
+
+namespace quickstep {
+
+QueryManagerDistributed::QueryManagerDistributed(QueryHandle *query_handle,
+                                                 ShiftbossDirectory *shiftbosses,
+                                                 const tmb::client_id foreman_client_id,
+                                                 tmb::MessageBus *bus)
+    : QueryManagerBase(query_handle),
+      shiftbosses_(shiftbosses),
+      foreman_client_id_(foreman_client_id),
+      bus_(bus),
+      normal_workorder_protos_container_(
+          new WorkOrderProtosContainer(num_operators_in_dag_)) {
+  // Collect all the workorders from all the relational operators in the DAG.
+  for (dag_node_index index = 0; index < num_operators_in_dag_; ++index) {
+    if (checkAllBlockingDependenciesMet(index)) {
+      query_dag_->getNodePayloadMutable(index)->informAllBlockingDependenciesMet();
+      processOperator(index, false);
+    }
+  }
+}
+
+serialization::WorkOrderMessage* QueryManagerDistributed::getNextWorkOrderMessage(
+    const dag_node_index start_operator_index) {
+  // Default policy: Operator with lowest index first.
+  size_t num_operators_checked = 0;
+  for (dag_node_index index = start_operator_index;
+       num_operators_checked < num_operators_in_dag_;
+       index = (index + 1) % num_operators_in_dag_, ++num_operators_checked) {
+    if (query_exec_state_->hasExecutionFinished(index)) {
+      continue;
+    }
+    unique_ptr<serialization::WorkOrder> work_order_proto(
+        normal_workorder_protos_container_->getWorkOrderProto(index));
+    if (work_order_proto != nullptr) {
+      query_exec_state_->incrementNumQueuedWorkOrders(index);
+
+      unique_ptr<serialization::WorkOrderMessage> message_proto(new serialization::WorkOrderMessage);
+      message_proto->set_query_id(query_id_);
+      message_proto->set_operator_index(index);
+      message_proto->mutable_work_order()->MergeFrom(*work_order_proto);
+
+      return message_proto.release();
+    }
+  }
+  // No normal WorkOrder protos available right now.
+  return nullptr;
+}
+
+bool QueryManagerDistributed::fetchNormalWorkOrders(const dag_node_index index) {
+  bool generated_new_workorder_protos = false;
+  if (!query_exec_state_->hasDoneGenerationWorkOrders(index)) {
+    // Do not fetch any work units until all blocking dependencies are met.
+    // The releational operator is not aware of blocking dependencies for
+    // uncorrelated scalar queries.
+    if (!checkAllBlockingDependenciesMet(index)) {
+      return false;
+    }
+    const size_t num_pending_workorder_protos_before =
+        normal_workorder_protos_container_->getNumWorkOrderProtos(index);
+    const bool done_generation =
+        query_dag_->getNodePayloadMutable(index)
+            ->getAllWorkOrderProtos(normal_workorder_protos_container_.get());
+    if (done_generation) {
+      query_exec_state_->setDoneGenerationWorkOrders(index);
+    }
+
+    // TODO(shoban): It would be a good check to see if operator is making
+    // useful progress, i.e., the operator either generates work orders to
+    // execute or still has pending work orders executing. However, this will not
+    // work if Foreman polls operators without feeding data. This check can be
+    // enabled, if Foreman is refactored to call getAllWorkOrders() only when
+    // pending work orders are completed or new input blocks feed.
+
+    generated_new_workorder_protos =
+        (num_pending_workorder_protos_before <
+         normal_workorder_protos_container_->getNumWorkOrderProtos(index));
+  }
+  return generated_new_workorder_protos;
+}
+
+bool QueryManagerDistributed::initiateRebuild(const dag_node_index index) {
+  DCHECK(checkRebuildRequired(index));
+  DCHECK(!checkRebuildInitiated(index));
+
+  const RelationalOperator &op = query_dag_->getNodePayload(index);
+  DCHECK_NE(op.getInsertDestinationID(), QueryContext::kInvalidInsertDestinationId);
+
+  serialization::InitiateRebuildMessage proto;
+  proto.set_operator_index(index);
+  proto.set_insert_destination_index(op.getInsertDestinationID());
+  proto.set_relation_id(op.getOutputRelationID());
+
+  const size_t proto_length = proto.ByteSize();
+  char *proto_bytes = static_cast<char*>(malloc(proto_length));
+  CHECK(proto.SerializeToArray(proto_bytes, proto_length));
+
+  TaggedMessage tagged_msg(static_cast<const void *>(proto_bytes),
+                           proto_length,
+                           kInitiateRebuildMessage);
+  free(proto_bytes);
+
+  LOG(INFO) << "ForemanDistributed sent InitiateRebuildMessage (typed '" << kInitiateRebuildMessage
+            << "') to Shiftboss";
+  // TODO(zuyu): Multiple workers support.
+  QueryExecutionUtil::SendTMBMessage(bus_,
+                                     foreman_client_id_,
+                                     shiftbosses_->getClientId(0),
+                                     move(tagged_msg));
+
+  // The negative value indicates that the number of rebuild work orders is to be
+  // determined.
+  query_exec_state_->setRebuildStatus(index, -1, true);
+
+  // Wait for Shiftbosses to report the number of rebuild work orders.
+  return false;
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/cdecf0b5/query_execution/QueryManagerDistributed.hpp
----------------------------------------------------------------------
diff --git a/query_execution/QueryManagerDistributed.hpp b/query_execution/QueryManagerDistributed.hpp
new file mode 100644
index 0000000..8641c22
--- /dev/null
+++ b/query_execution/QueryManagerDistributed.hpp
@@ -0,0 +1,105 @@
+/**
+ *   Licensed 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.
+ **/
+
+#ifndef QUICKSTEP_QUERY_EXECUTION_QUERY_MANAGER_DISTRIBUTED_HPP_
+#define QUICKSTEP_QUERY_EXECUTION_QUERY_MANAGER_DISTRIBUTED_HPP_
+
+#include <memory>
+
+#include "query_execution/QueryExecutionState.hpp"
+#include "query_execution/QueryManagerBase.hpp"
+#include "query_execution/WorkOrderProtosContainer.hpp"
+#include "utility/Macros.hpp"
+
+#include "tmb/id_typedefs.h"
+
+namespace tmb { class MessageBus; }
+
+namespace quickstep {
+
+class QueryHandle;
+class ShiftbossDirectory;
+
+namespace serialization { class WorkOrderMessage; }
+
+/** \addtogroup QueryExecution
+ *  @{
+ */
+
+/**
+ * @brief A class that manages the execution of a query including generation
+ *        of new work orders, keeping track of the query exection state.
+ **/
+class QueryManagerDistributed final : public QueryManagerBase {
+ public:
+  /**
+   * @brief Constructor.
+   *
+   * @param query_handle The QueryHandle object for this query.
+   * @param shiftbosses The ShiftbossDirectory to use.
+   * @param foreman_client_id The TMB client ID of the foreman thread.
+   * @param bus The TMB used for communication.
+   **/
+  QueryManagerDistributed(QueryHandle *query_handle,
+                          ShiftbossDirectory *shiftbosses,
+                          const tmb::client_id foreman_client_id,
+                          tmb::MessageBus *bus);
+
+  ~QueryManagerDistributed() override {}
+
+  bool fetchNormalWorkOrders(const dag_node_index index) override;
+
+ /**
+   * @brief Get the next normal workorder to be excuted, wrapped in a
+   *        WorkOrderMessage proto.
+   *
+   * @param start_operator_index Begin the search for the schedulable WorkOrder
+   *        with the operator at this index.
+   *
+   * @return A pointer to the WorkOrderMessage proto. If there is no WorkOrder
+   *         to be executed, return NULL.
+   **/
+  serialization::WorkOrderMessage* getNextWorkOrderMessage(
+      const dag_node_index start_operator_index);
+
+ private:
+  bool checkNormalExecutionOver(const dag_node_index index) const override {
+    return (checkAllDependenciesMet(index) &&
+            !normal_workorder_protos_container_->hasWorkOrderProto(index) &&
+            query_exec_state_->getNumQueuedWorkOrders(index) == 0 &&
+            query_exec_state_->hasDoneGenerationWorkOrders(index));
+  }
+
+  bool initiateRebuild(const dag_node_index index) override;
+
+  bool checkRebuildOver(const dag_node_index index) const override {
+    return query_exec_state_->hasRebuildInitiated(index) &&
+           (query_exec_state_->getNumRebuildWorkOrders(index) == 0);
+  }
+
+  ShiftbossDirectory *shiftbosses_;
+
+  const tmb::client_id foreman_client_id_;
+  tmb::MessageBus *bus_;
+
+  std::unique_ptr<WorkOrderProtosContainer> normal_workorder_protos_container_;
+
+  DISALLOW_COPY_AND_ASSIGN(QueryManagerDistributed);
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_QUERY_EXECUTION_QUERY_MANAGER_DISTRIBUTED_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/cdecf0b5/query_execution/ShiftbossDirectory.hpp
----------------------------------------------------------------------
diff --git a/query_execution/ShiftbossDirectory.hpp b/query_execution/ShiftbossDirectory.hpp
new file mode 100644
index 0000000..abb5f32
--- /dev/null
+++ b/query_execution/ShiftbossDirectory.hpp
@@ -0,0 +1,181 @@
+/**
+ *   Licensed 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.
+ **/
+
+#ifndef QUICKSTEP_QUERY_EXECUTION_SHIFTBOSS_DIRECTORY_HPP_
+#define QUICKSTEP_QUERY_EXECUTION_SHIFTBOSS_DIRECTORY_HPP_
+
+#include <cstddef>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "utility/Macros.hpp"
+
+#include "glog/logging.h"
+
+#include "tmb/id_typedefs.h"
+
+namespace quickstep {
+
+/** \addtogroup QueryExecution
+ *  @{
+ */
+
+/**
+ * @brief A class which keeps the metadata about the shiftbosses.
+ *
+ * @note This class is intended to be used only by ForemanDistributed thread.
+ *       Therefore, none of the methods in this class are thread-safe.
+ **/
+class ShiftbossDirectory {
+ public:
+  /**
+   * @brief Constructor.
+  **/
+  ShiftbossDirectory() = default;
+
+  /**
+   * @brief Add the Shiftboss.
+   *
+   * @param shiftboss_id the TMB client ID of Shiftboss thread.
+   * @param capacity The Work Order processing capacity of Shiftboss.
+   **/
+  void addShiftboss(const tmb::client_id shiftboss_id,
+                    const std::size_t capacity) {
+    client_id_indices_.emplace(shiftboss_id, client_ids_.size());
+    client_ids_.push_back(shiftboss_id);
+    work_order_capacities_.push_back(capacity);
+    num_queued_work_orders_.push_back(0u);
+  }
+
+  /**
+   * @brief Whether the ShiftbossDirectory has any Shiftboss.
+   *
+   * @return True if no Shiftboss in ShiftbossDirectory. Otherwise false.
+   **/
+  bool empty() const {
+    DCHECK_EQ(client_ids_.empty(), client_id_indices_.empty());
+    DCHECK_EQ(client_ids_.empty(), work_order_capacities_.empty());
+    DCHECK_EQ(client_ids_.empty(), num_queued_work_orders_.empty());
+
+    return client_ids_.empty();
+  }
+
+  /**
+   * @brief Get the number of Shiftboss in ShiftbossDirectory.
+   *
+   * @return The number of Shiftboss in ShiftbossDirectory.
+   **/
+  std::size_t size() const {
+    DCHECK_EQ(client_ids_.size(), client_id_indices_.size());
+    DCHECK_EQ(client_ids_.size(), work_order_capacities_.size());
+    DCHECK_EQ(client_ids_.size(), num_queued_work_orders_.size());
+
+    return client_ids_.size();
+  }
+
+  /**
+   * @brief Get the TMB client ID of the specified Shiftboss.
+   *
+   * @param shiftboss_index The index of Shiftboss.
+   *
+   * @return The TMB client ID of the given Shiftboss.
+   **/
+  tmb::client_id getClientId(const std::size_t shiftboss_index) const {
+    DCHECK_LT(shiftboss_index, size());
+    return client_ids_[shiftboss_index];
+  }
+
+  /**
+   * @brief Get the Shiftboss index from the specified client id.
+   *
+   * @param shiftboss_id The TMB client ID of Shiftboss.
+   *
+   * @return The index of the given Shiftboss.
+   **/
+  std::size_t getShiftbossIndex(const tmb::client_id shiftboss_id) const {
+    const auto it = client_id_indices_.find(shiftboss_id);
+    DCHECK(it != client_id_indices_.end());
+
+    return it->second;
+  }
+
+  /**
+   * @brief Whether the given Shiftboss has reached its capacity.
+   *
+   * @param shiftboss_index The index of Shiftboss.
+   *
+   * @return True if reached the capacity. Otherwise false.
+   **/
+  bool hasReachedCapacity(const std::size_t shiftboss_index) const {
+    DCHECK_LT(shiftboss_index, size());
+    return num_queued_work_orders_[shiftboss_index] >= work_order_capacities_[shiftboss_index];
+  }
+
+  /**
+   * @brief Add the number of new work orders for the given Shiftboss.
+   *
+   * @param shiftboss_index The index of Shiftboss.
+   * @param num_new_work_orders The number of the new work orders will be
+   *        executed on Shiftboss indexed by 'shiftboss_index'.
+   **/
+  void addNumQueuedWorkOrders(const std::size_t shiftboss_index,
+                              const std::size_t num_new_work_orders) {
+    num_queued_work_orders_[shiftboss_index] += num_new_work_orders;
+  }
+
+  /**
+   * @brief Increase the number of queued workorders for the given Shiftboss by 1.
+   *
+   * @param shiftboss_index The index of Shiftboss.
+   **/
+  void incrementNumQueuedWorkOrders(const std::size_t shiftboss_index) {
+    DCHECK_LT(shiftboss_index, size());
+    ++num_queued_work_orders_[shiftboss_index];
+  }
+
+  /**
+   * @brief Decrease the number of queued workorders for the given Shiftboss by 1.
+   *
+   * @param shiftboss_index The index of Shiftboss.
+   **/
+  void decrementNumQueuedWorkOrders(const std::size_t shiftboss_index) {
+    DCHECK_LT(shiftboss_index, size());
+    DCHECK_GE(num_queued_work_orders_[shiftboss_index], 1u);
+    --num_queued_work_orders_[shiftboss_index];
+  }
+
+ private:
+  // The TMB client IDs of Shiftbosses.
+  // TODO(zuyu): Support deletions, as Shiftbosses go down.
+  std::vector<tmb::client_id> client_ids_;
+
+  // The map from the TMB client ID of Shiftboss to its index in 'client_ids_'.
+  std::unordered_map<tmb::client_id, std::size_t> client_id_indices_;
+
+  // The max number of WorkOrders per Shiftboss.
+  std::vector<std::size_t> work_order_capacities_;
+
+  // The number of WorkOrders queued for execution per Shiftboss, and the value
+  // should be not greater than that of work_order_capacities_.
+  std::vector<std::size_t> num_queued_work_orders_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShiftbossDirectory);
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_QUERY_EXECUTION_SHIFTBOSS_DIRECTORY_HPP_


[06/30] incubator-quickstep git commit: Introduced QueryManagerBase, and renamed QueryManagerSingleNode.

Posted by ra...@apache.org.
Introduced QueryManagerBase, and renamed QueryManagerSingleNode.


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

Branch: refs/heads/quickstep-28-29
Commit: b4e25edce8bd49b4f53f63886ad02966ae93bf91
Parents: 7671a58
Author: Zuyu Zhang <zu...@apache.org>
Authored: Fri Jul 8 11:49:14 2016 -0500
Committer: Zuyu Zhang <zu...@twitter.com>
Committed: Tue Jul 12 15:51:06 2016 -0700

----------------------------------------------------------------------
 query_execution/CMakeLists.txt                  |  40 +-
 query_execution/PolicyEnforcer.cpp              |  14 +-
 query_execution/PolicyEnforcer.hpp              |   8 +-
 query_execution/QueryManager.cpp                | 470 ---------
 query_execution/QueryManager.hpp                | 374 --------
 query_execution/QueryManagerBase.cpp            | 305 ++++++
 query_execution/QueryManagerBase.hpp            | 312 ++++++
 query_execution/QueryManagerSingleNode.cpp      | 194 ++++
 query_execution/QueryManagerSingleNode.hpp      | 140 +++
 .../tests/QueryManagerSingleNode_unittest.cpp   | 942 +++++++++++++++++++
 query_execution/tests/QueryManager_unittest.cpp | 940 ------------------
 11 files changed, 1931 insertions(+), 1808 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b4e25edc/query_execution/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/query_execution/CMakeLists.txt b/query_execution/CMakeLists.txt
index e1b1183..5a9189c 100644
--- a/query_execution/CMakeLists.txt
+++ b/query_execution/CMakeLists.txt
@@ -45,7 +45,8 @@ add_library(quickstep_queryexecution_QueryExecutionMessages_proto
 add_library(quickstep_queryexecution_QueryExecutionState ../empty_src.cpp QueryExecutionState.hpp)
 add_library(quickstep_queryexecution_QueryExecutionTypedefs ../empty_src.cpp QueryExecutionTypedefs.hpp)
 add_library(quickstep_queryexecution_QueryExecutionUtil ../empty_src.cpp QueryExecutionUtil.hpp)
-add_library(quickstep_queryexecution_QueryManager QueryManager.cpp QueryManager.hpp)
+add_library(quickstep_queryexecution_QueryManagerBase QueryManagerBase.cpp QueryManagerBase.hpp)
+add_library(quickstep_queryexecution_QueryManagerSingleNode QueryManagerSingleNode.cpp QueryManagerSingleNode.hpp)
 add_library(quickstep_queryexecution_WorkOrderProtosContainer ../empty_src.cpp WorkOrderProtosContainer.hpp)
 add_library(quickstep_queryexecution_WorkOrdersContainer WorkOrdersContainer.cpp WorkOrdersContainer.hpp)
 add_library(quickstep_queryexecution_Worker Worker.cpp Worker.hpp)
@@ -89,18 +90,19 @@ target_link_libraries(quickstep_queryexecution_ForemanSingleNode
                       tmb
                       ${GFLAGS_LIB_NAME})
 target_link_libraries(quickstep_queryexecution_PolicyEnforcer
-                      ${GFLAGS_LIB_NAME}
                       glog
                       quickstep_catalog_CatalogTypedefs
                       quickstep_queryexecution_QueryExecutionMessages_proto
                       quickstep_queryexecution_QueryExecutionTypedefs
-                      quickstep_queryexecution_QueryManager
+                      quickstep_queryexecution_QueryManagerBase
+                      quickstep_queryexecution_QueryManagerSingleNode
                       quickstep_queryexecution_WorkerDirectory
                       quickstep_queryexecution_WorkerMessage
                       quickstep_queryoptimizer_QueryHandle
                       quickstep_relationaloperators_WorkOrder
                       quickstep_utility_Macros
-                      tmb)
+                      tmb
+                      ${GFLAGS_LIB_NAME})
 target_link_libraries(quickstep_queryexecution_QueryContext
                       glog
                       quickstep_catalog_CatalogDatabaseLite
@@ -149,7 +151,7 @@ target_link_libraries(quickstep_queryexecution_QueryExecutionUtil
                       quickstep_queryexecution_WorkerMessage
                       quickstep_utility_Macros
                       tmb)
-target_link_libraries(quickstep_queryexecution_QueryManager
+target_link_libraries(quickstep_queryexecution_QueryManagerBase
                       quickstep_catalog_CatalogDatabase
                       quickstep_catalog_CatalogRelation
                       quickstep_catalog_CatalogTypedefs
@@ -158,14 +160,25 @@ target_link_libraries(quickstep_queryexecution_QueryManager
                       quickstep_queryexecution_QueryExecutionMessages_proto
                       quickstep_queryexecution_QueryExecutionState
                       quickstep_queryexecution_QueryExecutionTypedefs
+                      quickstep_queryoptimizer_QueryHandle
+                      quickstep_queryoptimizer_QueryPlan
+                      quickstep_relationaloperators_RelationalOperator
+                      quickstep_relationaloperators_WorkOrder
+                      quickstep_storage_StorageBlockInfo
+                      quickstep_utility_DAG
+                      quickstep_utility_Macros)
+target_link_libraries(quickstep_queryexecution_QueryManagerSingleNode
+                      quickstep_catalog_CatalogTypedefs
+                      quickstep_queryexecution_QueryContext
+                      quickstep_queryexecution_QueryExecutionState
+                      quickstep_queryexecution_QueryManagerBase
                       quickstep_queryexecution_WorkOrdersContainer
                       quickstep_queryexecution_WorkerMessage
                       quickstep_queryoptimizer_QueryHandle
                       quickstep_relationaloperators_RebuildWorkOrder
                       quickstep_relationaloperators_RelationalOperator
-                      quickstep_relationaloperators_WorkOrder
+                      quickstep_storage_InsertDestination
                       quickstep_storage_StorageBlock
-                      quickstep_storage_StorageBlockInfo
                       quickstep_utility_DAG
                       quickstep_utility_Macros
                       tmb)
@@ -210,7 +223,8 @@ target_link_libraries(quickstep_queryexecution
                       quickstep_queryexecution_QueryExecutionState
                       quickstep_queryexecution_QueryExecutionTypedefs
                       quickstep_queryexecution_QueryExecutionUtil
-                      quickstep_queryexecution_QueryManager
+                      quickstep_queryexecution_QueryManagerBase
+                      quickstep_queryexecution_QueryManagerSingleNode
                       quickstep_queryexecution_WorkOrderProtosContainer
                       quickstep_queryexecution_WorkOrdersContainer
                       quickstep_queryexecution_Worker
@@ -248,9 +262,9 @@ if (ENABLE_DISTRIBUTED)
   add_test(BlockLocator_unittest BlockLocator_unittest)
 endif()
 
-add_executable(QueryManager_unittest
-  "${CMAKE_CURRENT_SOURCE_DIR}/tests/QueryManager_unittest.cpp")
-target_link_libraries(QueryManager_unittest
+add_executable(QueryManagerSingleNode_unittest
+  "${CMAKE_CURRENT_SOURCE_DIR}/tests/QueryManagerSingleNode_unittest.cpp")
+target_link_libraries(QueryManagerSingleNode_unittest
                       glog
                       gtest
                       gtest_main
@@ -262,7 +276,7 @@ target_link_libraries(QueryManager_unittest
                       quickstep_queryexecution_QueryExecutionMessages_proto
                       quickstep_queryexecution_QueryExecutionState
                       quickstep_queryexecution_QueryExecutionTypedefs
-                      quickstep_queryexecution_QueryManager
+                      quickstep_queryexecution_QueryManagerSingleNode
                       quickstep_queryexecution_WorkOrdersContainer
                       quickstep_queryexecution_WorkerDirectory
                       quickstep_queryexecution_WorkerMessage
@@ -278,7 +292,7 @@ target_link_libraries(QueryManager_unittest
                       quickstep_utility_DAG
                       quickstep_utility_Macros
                       tmb)
-add_test(QueryManager_unittest QueryManager_unittest)
+add_test(QueryManagerSingleNode_unittest QueryManagerSingleNode_unittest)
 
 add_executable(WorkOrdersContainer_unittest
                "${CMAKE_CURRENT_SOURCE_DIR}/tests/WorkOrdersContainer_unittest.cpp")

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b4e25edc/query_execution/PolicyEnforcer.cpp
----------------------------------------------------------------------
diff --git a/query_execution/PolicyEnforcer.cpp b/query_execution/PolicyEnforcer.cpp
index 84aa86a..f310ee1 100644
--- a/query_execution/PolicyEnforcer.cpp
+++ b/query_execution/PolicyEnforcer.cpp
@@ -26,7 +26,7 @@
 
 #include "catalog/CatalogTypedefs.hpp"
 #include "query_execution/QueryExecutionMessages.pb.h"
-#include "query_execution/QueryManager.hpp"
+#include "query_execution/QueryManagerSingleNode.hpp"
 #include "query_execution/WorkerDirectory.hpp"
 #include "query_optimizer/QueryHandle.hpp"
 #include "relational_operators/WorkOrder.hpp"
@@ -47,8 +47,8 @@ bool PolicyEnforcer::admitQuery(QueryHandle *query_handle) {
     if (admitted_queries_.find(query_id) == admitted_queries_.end()) {
       // Query with the same ID not present, ok to admit.
       admitted_queries_[query_id].reset(
-          new QueryManager(foreman_client_id_, num_numa_nodes_, query_handle,
-                           catalog_database_, storage_manager_, bus_));
+          new QueryManagerSingleNode(foreman_client_id_, num_numa_nodes_, query_handle,
+                                     catalog_database_, storage_manager_, bus_));
       return true;
     } else {
       LOG(ERROR) << "Query with the same ID " << query_id << " exists";
@@ -124,9 +124,9 @@ void PolicyEnforcer::processMessage(const TaggedMessage &tagged_message) {
       LOG(FATAL) << "Unknown message type found in PolicyEnforcer";
   }
   DCHECK(admitted_queries_.find(query_id) != admitted_queries_.end());
-  const QueryManager::QueryStatusCode return_code =
+  const QueryManagerBase::QueryStatusCode return_code =
       admitted_queries_[query_id]->processMessage(tagged_message);
-  if (return_code == QueryManager::QueryStatusCode::kQueryExecuted) {
+  if (return_code == QueryManagerBase::QueryStatusCode::kQueryExecuted) {
     removeQuery(query_id);
     if (!waiting_queries_.empty()) {
       // Admit the earliest waiting query.
@@ -156,12 +156,12 @@ void PolicyEnforcer::getWorkerMessages(
   std::vector<std::size_t> finished_queries_ids;
 
   for (const auto &admitted_query_info : admitted_queries_) {
-    QueryManager *curr_query_manager = admitted_query_info.second.get();
+    QueryManagerBase *curr_query_manager = admitted_query_info.second.get();
     DCHECK(curr_query_manager != nullptr);
     std::size_t messages_collected_curr_query = 0;
     while (messages_collected_curr_query < per_query_share) {
       WorkerMessage *next_worker_message =
-          curr_query_manager->getNextWorkerMessage(0, kAnyNUMANodeID);
+          static_cast<QueryManagerSingleNode*>(curr_query_manager)->getNextWorkerMessage(0, kAnyNUMANodeID);
       if (next_worker_message != nullptr) {
         ++messages_collected_curr_query;
         worker_messages->push_back(std::unique_ptr<WorkerMessage>(next_worker_message));

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b4e25edc/query_execution/PolicyEnforcer.hpp
----------------------------------------------------------------------
diff --git a/query_execution/PolicyEnforcer.hpp b/query_execution/PolicyEnforcer.hpp
index 470ff2a..79e61d1 100644
--- a/query_execution/PolicyEnforcer.hpp
+++ b/query_execution/PolicyEnforcer.hpp
@@ -26,7 +26,7 @@
 #include <vector>
 
 #include "query_execution/QueryExecutionTypedefs.hpp"
-#include "query_execution/QueryManager.hpp"
+#include "query_execution/QueryManagerBase.hpp"
 #include "query_execution/WorkerMessage.hpp"
 #include "utility/Macros.hpp"
 
@@ -190,8 +190,8 @@ class PolicyEnforcer {
   tmb::MessageBus *bus_;
   const bool profile_individual_workorders_;
 
-  // Key = query ID, value = QueryManager* for the key query.
-  std::unordered_map<std::size_t, std::unique_ptr<QueryManager>> admitted_queries_;
+  // Key = query ID, value = QueryManagerBase* for the key query.
+  std::unordered_map<std::size_t, std::unique_ptr<QueryManagerBase>> admitted_queries_;
 
   // The queries which haven't been admitted yet.
   std::queue<QueryHandle*> waiting_queries_;
@@ -214,4 +214,4 @@ class PolicyEnforcer {
 
 }  // namespace quickstep
 
-#endif  // QUICKSTEP_QUERY_EXECUTION_QUERY_MANAGER_HPP_
+#endif  // QUICKSTEP_QUERY_EXECUTION_POLICY_ENFORCER_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b4e25edc/query_execution/QueryManager.cpp
----------------------------------------------------------------------
diff --git a/query_execution/QueryManager.cpp b/query_execution/QueryManager.cpp
deleted file mode 100644
index d20b592..0000000
--- a/query_execution/QueryManager.cpp
+++ /dev/null
@@ -1,470 +0,0 @@
-/**
- *   Copyright 2016, Quickstep Research Group, Computer Sciences Department,
- *     University of Wisconsin\u2014Madison.
- *
- *   Licensed 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.
- **/
-
-#include "query_execution/QueryManager.hpp"
-
-#include <cstddef>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "catalog/CatalogDatabase.hpp"
-#include "catalog/CatalogRelation.hpp"
-#include "catalog/CatalogTypedefs.hpp"
-#include "catalog/PartitionScheme.hpp"
-#include "query_execution/QueryExecutionMessages.pb.h"
-#include "query_execution/QueryExecutionTypedefs.hpp"
-#include "query_execution/WorkerMessage.hpp"
-#include "query_optimizer/QueryHandle.hpp"
-#include "relational_operators/RebuildWorkOrder.hpp"
-#include "relational_operators/WorkOrder.hpp"
-#include "storage/StorageBlock.hpp"
-#include "storage/StorageBlockInfo.hpp"
-
-#include "glog/logging.h"
-
-using std::pair;
-
-namespace quickstep {
-class CatalogDatabaseLite;
-class StorageManager;
-}
-
-namespace quickstep {
-
-QueryManager::QueryManager(const tmb::client_id foreman_client_id,
-                           const std::size_t num_numa_nodes,
-                           QueryHandle *query_handle,
-                           CatalogDatabaseLite *catalog_database,
-                           StorageManager *storage_manager,
-                           tmb::MessageBus *bus)
-      : foreman_client_id_(foreman_client_id),
-        query_id_(DCHECK_NOTNULL(query_handle)->query_id()),
-        catalog_database_(DCHECK_NOTNULL(catalog_database)),
-        storage_manager_(DCHECK_NOTNULL(storage_manager)),
-        bus_(DCHECK_NOTNULL(bus)) {
-  DCHECK(query_handle->getQueryPlanMutable() != nullptr);
-  query_dag_ = query_handle->getQueryPlanMutable()->getQueryPlanDAGMutable();
-  DCHECK(query_dag_ != nullptr);
-
-  const dag_node_index num_operators_in_dag = query_dag_->size();
-
-  output_consumers_.resize(num_operators_in_dag);
-  blocking_dependencies_.resize(num_operators_in_dag);
-
-  query_exec_state_.reset(new QueryExecutionState(num_operators_in_dag));
-  workorders_container_.reset(
-      new WorkOrdersContainer(num_operators_in_dag, num_numa_nodes));
-
-  query_context_.reset(new QueryContext(query_handle->getQueryContextProto(),
-                                        *catalog_database_,
-                                        storage_manager_,
-                                        foreman_client_id_,
-                                        bus_));
-
-  for (dag_node_index node_index = 0;
-       node_index < num_operators_in_dag;
-       ++node_index) {
-    const QueryContext::insert_destination_id insert_destination_index =
-        query_dag_->getNodePayload(node_index).getInsertDestinationID();
-    if (insert_destination_index != QueryContext::kInvalidInsertDestinationId) {
-      // Rebuild is necessary whenever InsertDestination is present.
-      query_exec_state_->setRebuildRequired(node_index);
-      query_exec_state_->setRebuildStatus(node_index, 0, false);
-    }
-
-    for (const pair<dag_node_index, bool> &dependent_link :
-         query_dag_->getDependents(node_index)) {
-      const dag_node_index dependent_op_index = dependent_link.first;
-      if (!query_dag_->getLinkMetadata(node_index, dependent_op_index)) {
-        // The link is not a pipeline-breaker. Streaming of blocks is possible
-        // between these two operators.
-        output_consumers_[node_index].push_back(dependent_op_index);
-      } else {
-        // The link is a pipeline-breaker. Streaming of blocks is not possible
-        // between these two operators.
-        blocking_dependencies_[dependent_op_index].push_back(node_index);
-      }
-    }
-  }
-
-  // Collect all the workorders from all the relational operators in the DAG.
-  for (dag_node_index index = 0; index < num_operators_in_dag; ++index) {
-    if (checkAllBlockingDependenciesMet(index)) {
-      query_dag_->getNodePayloadMutable(index)->informAllBlockingDependenciesMet();
-      processOperator(index, false);
-    }
-  }
-}
-
-WorkerMessage* QueryManager::getNextWorkerMessage(
-    const dag_node_index start_operator_index, const int numa_node) {
-  // Default policy: Operator with lowest index first.
-  WorkOrder *work_order = nullptr;
-  size_t num_operators_checked = 0;
-  for (dag_node_index index = start_operator_index;
-       num_operators_checked < query_dag_->size();
-       index = (index + 1) % query_dag_->size(), ++num_operators_checked) {
-    if (query_exec_state_->hasExecutionFinished(index)) {
-      continue;
-    }
-    if (numa_node != -1) {
-      // First try to get a normal WorkOrder from the specified NUMA node.
-      work_order = workorders_container_->getNormalWorkOrderForNUMANode(index, numa_node);
-      if (work_order != nullptr) {
-        // A WorkOrder found on the given NUMA node.
-        query_exec_state_->incrementNumQueuedWorkOrders(index);
-        return WorkerMessage::WorkOrderMessage(work_order, index);
-      } else {
-        // Normal workorder not found on this node. Look for a rebuild workorder
-        // on this NUMA node.
-        work_order = workorders_container_->getRebuildWorkOrderForNUMANode(index, numa_node);
-        if (work_order != nullptr) {
-          return WorkerMessage::RebuildWorkOrderMessage(work_order, index);
-        }
-      }
-    }
-    // Either no workorder found on the given NUMA node, or numa_node is -1.
-    // Try to get a normal WorkOrder from other NUMA nodes.
-    work_order = workorders_container_->getNormalWorkOrder(index);
-    if (work_order != nullptr) {
-      query_exec_state_->incrementNumQueuedWorkOrders(index);
-      return WorkerMessage::WorkOrderMessage(work_order, index);
-    } else {
-      // Normal WorkOrder not found, look for a RebuildWorkOrder.
-      work_order = workorders_container_->getRebuildWorkOrder(index);
-      if (work_order != nullptr) {
-        return WorkerMessage::RebuildWorkOrderMessage(work_order, index);
-      }
-    }
-  }
-  // No WorkOrders available right now.
-  return nullptr;
-}
-
-QueryManager::QueryStatusCode QueryManager::processMessage(
-    const TaggedMessage &tagged_message) {
-  dag_node_index op_index;
-  switch (tagged_message.message_type()) {
-    case kWorkOrderCompleteMessage: {
-      serialization::NormalWorkOrderCompletionMessage proto;
-      CHECK(proto.ParseFromArray(tagged_message.message(),
-                                 tagged_message.message_bytes()));
-
-      op_index = proto.operator_index();
-      processWorkOrderCompleteMessage(proto.operator_index());
-      break;
-    }
-    case kRebuildWorkOrderCompleteMessage: {
-      serialization::RebuildWorkOrderCompletionMessage proto;
-      CHECK(proto.ParseFromArray(tagged_message.message(),
-                                 tagged_message.message_bytes()));
-
-      op_index = proto.operator_index();
-      processRebuildWorkOrderCompleteMessage(proto.operator_index());
-      break;
-    }
-    case kCatalogRelationNewBlockMessage: {
-      serialization::CatalogRelationNewBlockMessage proto;
-      CHECK(proto.ParseFromArray(tagged_message.message(),
-                                 tagged_message.message_bytes()));
-
-      const block_id block = proto.block_id();
-
-      CatalogRelation *relation =
-          static_cast<CatalogDatabase*>(catalog_database_)->getRelationByIdMutable(proto.relation_id());
-      relation->addBlock(block);
-
-      if (proto.has_partition_id()) {
-        relation->getPartitionSchemeMutable()->addBlockToPartition(
-            proto.partition_id(), block);
-      }
-      return QueryStatusCode::kNone;
-    }
-    case kDataPipelineMessage: {
-      // Possible message senders include InsertDestinations and some
-      // operators which modify existing blocks.
-      serialization::DataPipelineMessage proto;
-      CHECK(proto.ParseFromArray(tagged_message.message(),
-                                 tagged_message.message_bytes()));
-
-      op_index = proto.operator_index();
-      processDataPipelineMessage(proto.operator_index(),
-                                 proto.block_id(),
-                                 proto.relation_id());
-      break;
-    }
-    case kWorkOrdersAvailableMessage: {
-      serialization::WorkOrdersAvailableMessage proto;
-      CHECK(proto.ParseFromArray(tagged_message.message(),
-                                 tagged_message.message_bytes()));
-
-      op_index = proto.operator_index();
-
-      // Check if new work orders are available.
-      fetchNormalWorkOrders(op_index);
-
-      // Dispatch the WorkerMessages to the workers. We prefer to start the search
-      // for the schedulable WorkOrders beginning from 'op_index'. The first
-      // candidate worker to receive the next WorkOrder is the one that sent the
-      // response message to Foreman.
-      // TODO(zuyu): Improve the data locality for the next WorkOrder.
-      break;
-    }
-    case kWorkOrderFeedbackMessage: {
-      WorkOrder::FeedbackMessage msg(
-          const_cast<void *>(tagged_message.message()),
-          tagged_message.message_bytes());
-
-      op_index = msg.header().rel_op_index;
-      processFeedbackMessage(msg);
-      break;
-    }
-    default:
-      LOG(FATAL) << "Unknown message type found in QueryManager";
-  }
-
-  if (query_exec_state_->hasExecutionFinished(op_index)) {
-    return QueryStatusCode::kOperatorExecuted;
-  }
-
-  // As kQueryExecuted takes precedence over kOperatorExecuted, we check again.
-  if (query_exec_state_->hasQueryExecutionFinished()) {
-    return QueryStatusCode::kQueryExecuted;
-  }
-
-  return QueryStatusCode::kNone;
-}
-
-void QueryManager::processFeedbackMessage(
-    const WorkOrder::FeedbackMessage &msg) {
-  RelationalOperator *op =
-      query_dag_->getNodePayloadMutable(msg.header().rel_op_index);
-  op->receiveFeedbackMessage(msg);
-}
-
-void QueryManager::processWorkOrderCompleteMessage(
-    const dag_node_index op_index) {
-  query_exec_state_->decrementNumQueuedWorkOrders(op_index);
-
-  // Check if new work orders are available and fetch them if so.
-  fetchNormalWorkOrders(op_index);
-
-  if (checkRebuildRequired(op_index)) {
-    if (checkNormalExecutionOver(op_index)) {
-      if (!checkRebuildInitiated(op_index)) {
-        if (initiateRebuild(op_index)) {
-          // Rebuild initiated and completed right away.
-          markOperatorFinished(op_index);
-        } else {
-          // Rebuild under progress.
-        }
-      } else if (checkRebuildOver(op_index)) {
-        // Rebuild was under progress and now it is over.
-        markOperatorFinished(op_index);
-      }
-    } else {
-      // Normal execution under progress for this operator.
-    }
-  } else if (checkOperatorExecutionOver(op_index)) {
-    // Rebuild not required for this operator and its normal execution is
-    // complete.
-    markOperatorFinished(op_index);
-  }
-
-  for (const pair<dag_node_index, bool> &dependent_link :
-       query_dag_->getDependents(op_index)) {
-    const dag_node_index dependent_op_index = dependent_link.first;
-    if (checkAllBlockingDependenciesMet(dependent_op_index)) {
-      // Process the dependent operator (of the operator whose WorkOrder
-      // was just executed) for which all the dependencies have been met.
-      processOperator(dependent_op_index, true);
-    }
-  }
-}
-
-void QueryManager::processRebuildWorkOrderCompleteMessage(const dag_node_index op_index) {
-  query_exec_state_->decrementNumRebuildWorkOrders(op_index);
-
-  if (checkRebuildOver(op_index)) {
-    markOperatorFinished(op_index);
-
-    for (const pair<dag_node_index, bool> &dependent_link :
-         query_dag_->getDependents(op_index)) {
-      const dag_node_index dependent_op_index = dependent_link.first;
-      if (checkAllBlockingDependenciesMet(dependent_op_index)) {
-        processOperator(dependent_op_index, true);
-      }
-    }
-  }
-}
-
-void QueryManager::processOperator(const dag_node_index index,
-                                   const bool recursively_check_dependents) {
-  if (fetchNormalWorkOrders(index)) {
-    // Fetched work orders. Return to wait for the generated work orders to
-    // execute, and skip the execution-finished checks.
-    return;
-  }
-
-  if (checkNormalExecutionOver(index)) {
-    if (checkRebuildRequired(index)) {
-      if (!checkRebuildInitiated(index)) {
-        // Rebuild hasn't started, initiate it.
-        if (initiateRebuild(index)) {
-          // Rebuild initiated and completed right away.
-          markOperatorFinished(index);
-        } else {
-          // Rebuild WorkOrders have been generated.
-          return;
-        }
-      } else if (checkRebuildOver(index)) {
-        // Rebuild had been initiated and it is over.
-        markOperatorFinished(index);
-      }
-    } else {
-      // Rebuild is not required and normal execution over, mark finished.
-      markOperatorFinished(index);
-    }
-    // If we reach here, that means the operator has been marked as finished.
-    if (recursively_check_dependents) {
-      for (const pair<dag_node_index, bool> &dependent_link :
-           query_dag_->getDependents(index)) {
-        const dag_node_index dependent_op_index = dependent_link.first;
-        if (checkAllBlockingDependenciesMet(dependent_op_index)) {
-          processOperator(dependent_op_index, true);
-        }
-      }
-    }
-  }
-}
-
-void QueryManager::processDataPipelineMessage(const dag_node_index op_index,
-                                              const block_id block,
-                                              const relation_id rel_id) {
-  for (const dag_node_index consumer_index :
-       output_consumers_[op_index]) {
-    // Feed the streamed block to the consumer. Note that 'output_consumers_'
-    // only contain those dependents of operator with index = op_index which are
-    // eligible to receive streamed input.
-    query_dag_->getNodePayloadMutable(consumer_index)->feedInputBlock(block, rel_id);
-    // Because of the streamed input just fed, check if there are any new
-    // WorkOrders available and if so, fetch them.
-    fetchNormalWorkOrders(consumer_index);
-  }
-}
-
-bool QueryManager::fetchNormalWorkOrders(const dag_node_index index) {
-  bool generated_new_workorders = false;
-  if (!query_exec_state_->hasDoneGenerationWorkOrders(index)) {
-    // Do not fetch any work units until all blocking dependencies are met.
-    // The releational operator is not aware of blocking dependencies for
-    // uncorrelated scalar queries.
-    if (!checkAllBlockingDependenciesMet(index)) {
-      return false;
-    }
-    const size_t num_pending_workorders_before =
-        workorders_container_->getNumNormalWorkOrders(index);
-    const bool done_generation =
-        query_dag_->getNodePayloadMutable(index)->getAllWorkOrders(workorders_container_.get(),
-                                                                   query_context_.get(),
-                                                                   storage_manager_,
-                                                                   foreman_client_id_,
-                                                                   bus_);
-    if (done_generation) {
-      query_exec_state_->setDoneGenerationWorkOrders(index);
-    }
-
-    // TODO(shoban): It would be a good check to see if operator is making
-    // useful progress, i.e., the operator either generates work orders to
-    // execute or still has pending work orders executing. However, this will not
-    // work if Foreman polls operators without feeding data. This check can be
-    // enabled, if Foreman is refactored to call getAllWorkOrders() only when
-    // pending work orders are completed or new input blocks feed.
-
-    generated_new_workorders =
-        (num_pending_workorders_before <
-         workorders_container_->getNumNormalWorkOrders(index));
-  }
-  return generated_new_workorders;
-}
-
-void QueryManager::markOperatorFinished(const dag_node_index index) {
-  query_exec_state_->setExecutionFinished(index);
-
-  RelationalOperator *op = query_dag_->getNodePayloadMutable(index);
-  op->updateCatalogOnCompletion();
-
-  const relation_id output_rel = op->getOutputRelationID();
-  for (const pair<dag_node_index, bool> &dependent_link : query_dag_->getDependents(index)) {
-    const dag_node_index dependent_op_index = dependent_link.first;
-    RelationalOperator *dependent_op = query_dag_->getNodePayloadMutable(dependent_op_index);
-    // Signal dependent operator that current operator is done feeding input blocks.
-    if (output_rel >= 0) {
-      dependent_op->doneFeedingInputBlocks(output_rel);
-    }
-    if (checkAllBlockingDependenciesMet(dependent_op_index)) {
-      dependent_op->informAllBlockingDependenciesMet();
-    }
-  }
-}
-
-bool QueryManager::initiateRebuild(const dag_node_index index) {
-  DCHECK(!workorders_container_->hasRebuildWorkOrder(index));
-  DCHECK(checkRebuildRequired(index));
-  DCHECK(!checkRebuildInitiated(index));
-
-  getRebuildWorkOrders(index, workorders_container_.get());
-
-  query_exec_state_->setRebuildStatus(
-      index, workorders_container_->getNumRebuildWorkOrders(index), true);
-
-  return (query_exec_state_->getNumRebuildWorkOrders(index) == 0);
-}
-
-void QueryManager::getRebuildWorkOrders(const dag_node_index index,
-                          WorkOrdersContainer *container) {
-  const RelationalOperator &op = query_dag_->getNodePayload(index);
-  const QueryContext::insert_destination_id insert_destination_index = op.getInsertDestinationID();
-
-  if (insert_destination_index == QueryContext::kInvalidInsertDestinationId) {
-    return;
-  }
-
-  std::vector<MutableBlockReference> partially_filled_block_refs;
-
-  DCHECK(query_context_ != nullptr);
-  InsertDestination *insert_destination = query_context_->getInsertDestination(insert_destination_index);
-  DCHECK(insert_destination != nullptr);
-
-  insert_destination->getPartiallyFilledBlocks(&partially_filled_block_refs);
-
-  for (std::vector<MutableBlockReference>::size_type i = 0;
-       i < partially_filled_block_refs.size();
-       ++i) {
-    container->addRebuildWorkOrder(
-        new RebuildWorkOrder(query_id_,
-                             std::move(partially_filled_block_refs[i]),
-                             index,
-                             op.getOutputRelationID(),
-                             foreman_client_id_,
-                             bus_),
-        index);
-  }
-}
-
-}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b4e25edc/query_execution/QueryManager.hpp
----------------------------------------------------------------------
diff --git a/query_execution/QueryManager.hpp b/query_execution/QueryManager.hpp
deleted file mode 100644
index b52460f..0000000
--- a/query_execution/QueryManager.hpp
+++ /dev/null
@@ -1,374 +0,0 @@
-/**
- *   Copyright 2016, Quickstep Research Group, Computer Sciences Department,
- *     University of Wisconsin\u2014Madison.
- *
- *   Licensed 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.
- **/
-
-#ifndef QUICKSTEP_QUERY_EXECUTION_QUERY_MANAGER_HPP_
-#define QUICKSTEP_QUERY_EXECUTION_QUERY_MANAGER_HPP_
-
-#include <cstddef>
-#include <memory>
-#include <vector>
-
-#include "catalog/CatalogTypedefs.hpp"
-#include "query_execution/QueryContext.hpp"
-#include "query_execution/QueryExecutionState.hpp"
-#include "query_execution/QueryExecutionTypedefs.hpp"
-#include "query_execution/WorkOrdersContainer.hpp"
-#include "relational_operators/RelationalOperator.hpp"
-#include "relational_operators/WorkOrder.hpp"
-#include "storage/StorageBlockInfo.hpp"
-#include "utility/DAG.hpp"
-#include "utility/Macros.hpp"
-
-#include "tmb/id_typedefs.h"
-#include "tmb/message_bus.h"
-#include "tmb/tagged_message.h"
-
-namespace quickstep {
-
-class CatalogDatabaseLite;
-class QueryHandle;
-class StorageManager;
-class WorkerMessage;
-
-/** \addtogroup QueryExecution
- *  @{
- */
-
-/**
- * @brief A class that manages the execution of a query including generation
- *        of new work orders, keeping track of the query exection state.
- **/
-class QueryManager {
- public:
-  typedef DAG<RelationalOperator, bool>::size_type_nodes dag_node_index;
-
-  /**
-   * @brief Return codes for processMessage() function.
-   *
-   * @note When both operator and query get executed, kQueryExecuted takes
-   *       precedence over kOperatorExecuted.
-   **/
-  enum class QueryStatusCode {
-    kOperatorExecuted = 0,  // An operator in the query finished execution.
-    kQueryExecuted,         // The query got executed.
-    kNone                   // None of the above.
-  };
-
-  /**
-   * @brief Constructor.
-   *
-   * @param foreman_client_id The TMB client ID of the foreman thread.
-   * @param num_numa_nodes The number of NUMA nodes used by the system.
-   * @param query_handle The QueryHandle object for this query.
-   * @param catalog_database The CatalogDatabse used by the query.
-   * @param storage_manager The StorageManager used by the query.
-   * @param bus The TMB used for communication.
-   **/
-  QueryManager(const tmb::client_id foreman_client_id,
-               const std::size_t num_numa_nodes,
-               QueryHandle *query_handle,
-               CatalogDatabaseLite *catalog_database,
-               StorageManager *storage_manager,
-               tmb::MessageBus *bus);
-
- /**
-   * @brief Get the next workorder to be excuted, wrapped in a WorkerMessage.
-   *
-   * @param start_operator_index Begin the search for the schedulable WorkOrder
-   *        with the operator at this index.
-   * @param numa_node The next WorkOrder should preferably have its input(s)
-   *        from this numa_node. This is a hint and not a binding requirement.
-   *
-   * @return A pointer to the WorkerMessage. If there's no WorkOrder to be
-   *         executed, return NULL.
-   **/
-  WorkerMessage *getNextWorkerMessage(
-      const dag_node_index start_operator_index,
-      const numa_node_id node_id = -1);
-
-  /**
-   * @brief Process a message sent to the QueryManager.
-   *
-   * @param tagged_message TaggedMessage sent to the QueryManager.
-   *
-   * @return QueryStatusCode as determined after the message is processed.
-   **/
-  QueryStatusCode processMessage(const TaggedMessage &tagged_message);
-
-  /**
-   * @brief Get the QueryExecutionState for this query.
-   **/
-  inline const QueryExecutionState& getQueryExecutionState() const {
-    return *query_exec_state_;
-  }
-
-  /**
-   * @brief Get a pointer to the QueryContext.
-   **/
-  inline QueryContext* getQueryContextMutable() {
-    return query_context_.get();
-  }
-
- private:
-  /**
-   * @brief Process the received WorkOrder complete message.
-   *
-   * @param node_index The index of the specified operator node in the query DAG
-   *        for the completed WorkOrder.
-   **/
-  void processWorkOrderCompleteMessage(const dag_node_index op_index);
-
-  /**
-   * @brief Process the received RebuildWorkOrder complete message.
-   *
-   * @param node_index The index of the specified operator node in the query DAG
-   *        for the completed RebuildWorkOrder.
-   **/
-  void processRebuildWorkOrderCompleteMessage(const dag_node_index op_index);
-
-  /**
-   * @brief Process a current relational operator: Get its workorders and store
-   *        them in the WorkOrdersContainer for this query. If the operator can
-   *        be marked as done, do so.
-   *
-   * @param index The index of the relational operator to be processed in the
-   *        query plan DAG.
-   * @param recursively_check_dependents If an operator is done, should we
-   *        call processOperator on its dependents recursively.
-   **/
-  void processOperator(const dag_node_index index,
-                       const bool recursively_check_dependents);
-
-  /**
-   * @brief Process the received data pipeline message.
-   *
-   * @param node_index The index of the specified operator node in the query DAG
-   *        for the pipelining block.
-   * @param block The block id.
-   * @param rel_id The ID of the relation that produced 'block'.
-   **/
-  void processDataPipelineMessage(const dag_node_index op_index,
-                                  const block_id block,
-                                  const relation_id rel_id);
-
-  /**
-   * @brief Process the received work order feedback message and notify
-   *        relational operator.
-   *
-   * @param message Feedback message from work order.
-   **/
-  void processFeedbackMessage(const WorkOrder::FeedbackMessage &message);
-
-  /**
-   * @brief Fetch all work orders currently available in relational operator and
-   *        store them internally.
-   *
-   * @param index The index of the relational operator to be processed in the
-   *        query plan DAG.
-   *
-   * @return Whether any work order was generated by op.
-   **/
-  bool fetchNormalWorkOrders(const dag_node_index index);
-
-  /**
-   * @brief This function does the following things:
-   *        1. Mark the given relational operator as "done".
-   *        2. For all the dependents of this operator, check if all of their
-   *        blocking dependencies are met. If so inform them that the blocking
-   *        dependencies are met.
-   *        3. Check if the given operator is done producing output. If it's
-   *        done, inform the dependents that they won't receive input anymore
-   *        from the given operator.
-   *
-   * @param index The index of the given relational operator in the DAG.
-   **/
-  void markOperatorFinished(const dag_node_index index);
-
-  /**
-   * @brief Check if all the dependencies of the node at specified index have
-   *        finished their execution.
-   *
-   * @note This function's true return value is a pre-requisite for calling
-   *       getRebuildWorkOrders()
-   *
-   * @param node_index The index of the specified node in the query DAG.
-   *
-   * @return True if all the dependencies have finished their execution. False
-   *         otherwise.
-   **/
-  inline bool checkAllDependenciesMet(const dag_node_index node_index) const {
-    for (const dag_node_index dependency_index :
-         query_dag_->getDependencies(node_index)) {
-      // If at least one of the dependencies is not met, return false.
-      if (!query_exec_state_->hasExecutionFinished(dependency_index)) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  /**
-   * @brief Check if all the blocking dependencies of the node at specified
-   *        index have finished their execution.
-   *
-   * @note A blocking dependency is the one which is pipeline breaker. Output of
-   *       a dependency can't be streamed to its dependent if the link between
-   *       them is pipeline breaker.
-   *
-   * @param node_index The index of the specified node in the query DAG.
-   *
-   * @return True if all the blocking dependencies have finished their
-   *         execution. False otherwise.
-   **/
-  inline bool checkAllBlockingDependenciesMet(
-      const dag_node_index node_index) const {
-    for (const dag_node_index blocking_dependency_index :
-         blocking_dependencies_[node_index]) {
-      if (!query_exec_state_->hasExecutionFinished(
-              blocking_dependency_index)) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  /**
-   * @brief Check if the execution of the given operator is over.
-   *
-   * @param index The index of the given operator in the DAG.
-   *
-   * @return True if the execution of the given operator is over, false
-   *         otherwise.
-   **/
-  inline bool checkOperatorExecutionOver(const dag_node_index index) const {
-    if (checkRebuildRequired(index)) {
-      return (checkNormalExecutionOver(index) && checkRebuildOver(index));
-    } else {
-      return checkNormalExecutionOver(index);
-    }
-  }
-
-  /**
-   * @brief Check if the given operator's normal execution is over.
-   *
-   * @note The conditions for a given operator's normal execution to get over:
-   *       1. All of its  normal (i.e. non rebuild) WorkOrders have finished
-   *       execution.
-   *       2. The operator is done generating work orders.
-   *       3. All of the dependencies of the given operator have been met.
-   *
-   * @param index The index of the given operator in the DAG.
-   *
-   * @return True if the normal execution of the given operator is over, false
-   *         otherwise.
-   **/
-  inline bool checkNormalExecutionOver(const dag_node_index index) const {
-    return (checkAllDependenciesMet(index) &&
-            !workorders_container_->hasNormalWorkOrder(index) &&
-            query_exec_state_->getNumQueuedWorkOrders(index) == 0 &&
-            query_exec_state_->hasDoneGenerationWorkOrders(index));
-  }
-
-  /**
-   * @brief Check if the rebuild operation is required for a given operator.
-   *
-   * @param index The index of the given operator in the DAG.
-   *
-   * @return True if the rebuild operation is required, false otherwise.
-   **/
-  inline bool checkRebuildRequired(const dag_node_index index) const {
-    return query_exec_state_->isRebuildRequired(index);
-  }
-
-  /**
-   * @brief Check if the rebuild operation for a given operator is over.
-   *
-   * @param index The index of the given operator in the DAG.
-   *
-   * @return True if the rebuild operation is over, false otherwise.
-   **/
-  inline bool checkRebuildOver(const dag_node_index index) const {
-    return query_exec_state_->hasRebuildInitiated(index) &&
-           !workorders_container_->hasRebuildWorkOrder(index) &&
-           (query_exec_state_->getNumRebuildWorkOrders(index) == 0);
-  }
-
-  /**
-   * @brief Check if the rebuild operation for a given operator has been
-   *        initiated.
-   *
-   * @param index The index of the given operator in the DAG.
-   *
-   * @return True if the rebuild operation has been initiated, false otherwise.
-   **/
-  inline bool checkRebuildInitiated(const dag_node_index index) const {
-    return query_exec_state_->hasRebuildInitiated(index);
-  }
-
-  /**
-   * @brief Initiate the rebuild process for partially filled blocks generated
-   *        during the execution of the given operator.
-   *
-   * @param index The index of the given operator in the DAG.
-   *
-   * @return True if the rebuild is over immediately, i.e. the operator didn't
-   *         generate any rebuild WorkOrders, false otherwise.
-   **/
-  bool initiateRebuild(const dag_node_index index);
-
-  /**
-   * @brief Get the rebuild WorkOrders for an operator.
-   *
-   * @note This function should be called only once, when all the normal
-   *       WorkOrders generated by an operator finish their execution.
-   *
-   * @param index The index of the operator in the query plan DAG.
-   * @param container A pointer to a WorkOrdersContainer to be used to store the
-   *        generated WorkOrders.
-   **/
-  void getRebuildWorkOrders(const dag_node_index index,
-                            WorkOrdersContainer *container);
-
-  const tmb::client_id foreman_client_id_;
-  const std::size_t query_id_;
-
-  CatalogDatabaseLite *catalog_database_;
-  StorageManager *storage_manager_;
-  tmb::MessageBus *bus_;
-
-  DAG<RelationalOperator, bool> *query_dag_;
-
-  std::unique_ptr<QueryContext> query_context_;
-
-  // For all nodes, store their receiving dependents.
-  std::vector<std::vector<dag_node_index>> output_consumers_;
-
-  // For all nodes, store their pipeline breaking dependencies (if any).
-  std::vector<std::vector<dag_node_index>> blocking_dependencies_;
-
-  std::unique_ptr<QueryExecutionState> query_exec_state_;
-
-  std::unique_ptr<WorkOrdersContainer> workorders_container_;
-
-  DISALLOW_COPY_AND_ASSIGN(QueryManager);
-};
-
-/** @} */
-
-}  // namespace quickstep
-
-#endif  // QUICKSTEP_QUERY_EXECUTION_QUERY_MANAGER_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b4e25edc/query_execution/QueryManagerBase.cpp
----------------------------------------------------------------------
diff --git a/query_execution/QueryManagerBase.cpp b/query_execution/QueryManagerBase.cpp
new file mode 100644
index 0000000..f7e183f
--- /dev/null
+++ b/query_execution/QueryManagerBase.cpp
@@ -0,0 +1,305 @@
+/**
+ *   Copyright 2016, Quickstep Research Group, Computer Sciences Department,
+ *     University of Wisconsin\u2014Madison.
+ *
+ *   Licensed 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.
+ **/
+
+#include "query_execution/QueryManagerBase.hpp"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "catalog/CatalogDatabase.hpp"
+#include "catalog/CatalogRelation.hpp"
+#include "catalog/CatalogTypedefs.hpp"
+#include "catalog/PartitionScheme.hpp"
+#include "query_execution/QueryContext.hpp"
+#include "query_execution/QueryExecutionMessages.pb.h"
+#include "query_execution/QueryExecutionTypedefs.hpp"
+#include "query_optimizer/QueryHandle.hpp"
+#include "query_optimizer/QueryPlan.hpp"
+#include "relational_operators/WorkOrder.hpp"
+#include "storage/StorageBlockInfo.hpp"
+
+#include "glog/logging.h"
+
+using std::pair;
+
+namespace quickstep {
+
+QueryManagerBase::QueryManagerBase(QueryHandle *query_handle,
+                                   CatalogDatabaseLite *catalog_database)
+    : query_id_(DCHECK_NOTNULL(query_handle)->query_id()),
+      catalog_database_(DCHECK_NOTNULL(catalog_database)),
+      query_dag_(DCHECK_NOTNULL(
+          DCHECK_NOTNULL(query_handle->getQueryPlanMutable())->getQueryPlanDAGMutable())),
+      num_operators_in_dag_(query_dag_->size()),
+      output_consumers_(num_operators_in_dag_),
+      blocking_dependencies_(num_operators_in_dag_),
+      query_exec_state_(new QueryExecutionState(num_operators_in_dag_)) {
+  for (dag_node_index node_index = 0;
+       node_index < num_operators_in_dag_;
+       ++node_index) {
+    const QueryContext::insert_destination_id insert_destination_index =
+        query_dag_->getNodePayload(node_index).getInsertDestinationID();
+    if (insert_destination_index != QueryContext::kInvalidInsertDestinationId) {
+      // Rebuild is necessary whenever InsertDestination is present.
+      query_exec_state_->setRebuildRequired(node_index);
+      query_exec_state_->setRebuildStatus(node_index, 0, false);
+    }
+
+    for (const pair<dag_node_index, bool> &dependent_link :
+         query_dag_->getDependents(node_index)) {
+      const dag_node_index dependent_op_index = dependent_link.first;
+      if (!query_dag_->getLinkMetadata(node_index, dependent_op_index)) {
+        // The link is not a pipeline-breaker. Streaming of blocks is possible
+        // between these two operators.
+        output_consumers_[node_index].push_back(dependent_op_index);
+      } else {
+        // The link is a pipeline-breaker. Streaming of blocks is not possible
+        // between these two operators.
+        blocking_dependencies_[dependent_op_index].push_back(node_index);
+      }
+    }
+  }
+}
+
+QueryManagerBase::QueryStatusCode QueryManagerBase::processMessage(
+    const TaggedMessage &tagged_message) {
+  dag_node_index op_index;
+  switch (tagged_message.message_type()) {
+    case kWorkOrderCompleteMessage: {
+      serialization::NormalWorkOrderCompletionMessage proto;
+      CHECK(proto.ParseFromArray(tagged_message.message(),
+                                 tagged_message.message_bytes()));
+
+      op_index = proto.operator_index();
+      processWorkOrderCompleteMessage(proto.operator_index());
+      break;
+    }
+    case kRebuildWorkOrderCompleteMessage: {
+      serialization::RebuildWorkOrderCompletionMessage proto;
+      CHECK(proto.ParseFromArray(tagged_message.message(),
+                                 tagged_message.message_bytes()));
+
+      op_index = proto.operator_index();
+      processRebuildWorkOrderCompleteMessage(proto.operator_index());
+      break;
+    }
+    case kCatalogRelationNewBlockMessage: {
+      serialization::CatalogRelationNewBlockMessage proto;
+      CHECK(proto.ParseFromArray(tagged_message.message(),
+                                 tagged_message.message_bytes()));
+
+      const block_id block = proto.block_id();
+
+      CatalogRelation *relation =
+          static_cast<CatalogDatabase*>(catalog_database_)->getRelationByIdMutable(proto.relation_id());
+      relation->addBlock(block);
+
+      if (proto.has_partition_id()) {
+        relation->getPartitionSchemeMutable()->addBlockToPartition(
+            proto.partition_id(), block);
+      }
+      return QueryStatusCode::kNone;
+    }
+    case kDataPipelineMessage: {
+      // Possible message senders include InsertDestinations and some
+      // operators which modify existing blocks.
+      serialization::DataPipelineMessage proto;
+      CHECK(proto.ParseFromArray(tagged_message.message(),
+                                 tagged_message.message_bytes()));
+
+      op_index = proto.operator_index();
+      processDataPipelineMessage(proto.operator_index(),
+                                 proto.block_id(),
+                                 proto.relation_id());
+      break;
+    }
+    case kWorkOrdersAvailableMessage: {
+      serialization::WorkOrdersAvailableMessage proto;
+      CHECK(proto.ParseFromArray(tagged_message.message(),
+                                 tagged_message.message_bytes()));
+
+      op_index = proto.operator_index();
+
+      // Check if new work orders are available.
+      fetchNormalWorkOrders(op_index);
+      break;
+    }
+    case kWorkOrderFeedbackMessage: {
+      WorkOrder::FeedbackMessage msg(
+          const_cast<void *>(tagged_message.message()),
+          tagged_message.message_bytes());
+
+      op_index = msg.header().rel_op_index;
+      processFeedbackMessage(msg);
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unknown message type found in QueryManager";
+  }
+
+  if (query_exec_state_->hasExecutionFinished(op_index)) {
+    return QueryStatusCode::kOperatorExecuted;
+  }
+
+  // As kQueryExecuted takes precedence over kOperatorExecuted, we check again.
+  if (query_exec_state_->hasQueryExecutionFinished()) {
+    return QueryStatusCode::kQueryExecuted;
+  }
+
+  return QueryStatusCode::kNone;
+}
+
+void QueryManagerBase::processFeedbackMessage(
+    const WorkOrder::FeedbackMessage &msg) {
+  RelationalOperator *op =
+      query_dag_->getNodePayloadMutable(msg.header().rel_op_index);
+  op->receiveFeedbackMessage(msg);
+}
+
+void QueryManagerBase::processWorkOrderCompleteMessage(
+    const dag_node_index op_index) {
+  query_exec_state_->decrementNumQueuedWorkOrders(op_index);
+
+  // Check if new work orders are available and fetch them if so.
+  fetchNormalWorkOrders(op_index);
+
+  if (checkRebuildRequired(op_index)) {
+    if (checkNormalExecutionOver(op_index)) {
+      if (!checkRebuildInitiated(op_index)) {
+        if (initiateRebuild(op_index)) {
+          // Rebuild initiated and completed right away.
+          markOperatorFinished(op_index);
+        } else {
+          // Rebuild under progress.
+        }
+      } else if (checkRebuildOver(op_index)) {
+        // Rebuild was under progress and now it is over.
+        markOperatorFinished(op_index);
+      }
+    } else {
+      // Normal execution under progress for this operator.
+    }
+  } else if (checkOperatorExecutionOver(op_index)) {
+    // Rebuild not required for this operator and its normal execution is
+    // complete.
+    markOperatorFinished(op_index);
+  }
+
+  for (const pair<dag_node_index, bool> &dependent_link :
+       query_dag_->getDependents(op_index)) {
+    const dag_node_index dependent_op_index = dependent_link.first;
+    if (checkAllBlockingDependenciesMet(dependent_op_index)) {
+      // Process the dependent operator (of the operator whose WorkOrder
+      // was just executed) for which all the dependencies have been met.
+      processOperator(dependent_op_index, true);
+    }
+  }
+}
+
+void QueryManagerBase::processRebuildWorkOrderCompleteMessage(const dag_node_index op_index) {
+  query_exec_state_->decrementNumRebuildWorkOrders(op_index);
+
+  if (checkRebuildOver(op_index)) {
+    markOperatorFinished(op_index);
+
+    for (const pair<dag_node_index, bool> &dependent_link :
+         query_dag_->getDependents(op_index)) {
+      const dag_node_index dependent_op_index = dependent_link.first;
+      if (checkAllBlockingDependenciesMet(dependent_op_index)) {
+        processOperator(dependent_op_index, true);
+      }
+    }
+  }
+}
+
+void QueryManagerBase::processOperator(const dag_node_index index,
+                                       const bool recursively_check_dependents) {
+  if (fetchNormalWorkOrders(index)) {
+    // Fetched work orders. Return to wait for the generated work orders to
+    // execute, and skip the execution-finished checks.
+    return;
+  }
+
+  if (checkNormalExecutionOver(index)) {
+    if (checkRebuildRequired(index)) {
+      if (!checkRebuildInitiated(index)) {
+        // Rebuild hasn't started, initiate it.
+        if (initiateRebuild(index)) {
+          // Rebuild initiated and completed right away.
+          markOperatorFinished(index);
+        } else {
+          // Rebuild WorkOrders have been generated.
+          return;
+        }
+      } else if (checkRebuildOver(index)) {
+        // Rebuild had been initiated and it is over.
+        markOperatorFinished(index);
+      }
+    } else {
+      // Rebuild is not required and normal execution over, mark finished.
+      markOperatorFinished(index);
+    }
+    // If we reach here, that means the operator has been marked as finished.
+    if (recursively_check_dependents) {
+      for (const pair<dag_node_index, bool> &dependent_link :
+           query_dag_->getDependents(index)) {
+        const dag_node_index dependent_op_index = dependent_link.first;
+        if (checkAllBlockingDependenciesMet(dependent_op_index)) {
+          processOperator(dependent_op_index, true);
+        }
+      }
+    }
+  }
+}
+
+void QueryManagerBase::processDataPipelineMessage(const dag_node_index op_index,
+                                                  const block_id block,
+                                                  const relation_id rel_id) {
+  for (const dag_node_index consumer_index :
+       output_consumers_[op_index]) {
+    // Feed the streamed block to the consumer. Note that 'output_consumers_'
+    // only contain those dependents of operator with index = op_index which are
+    // eligible to receive streamed input.
+    query_dag_->getNodePayloadMutable(consumer_index)->feedInputBlock(block, rel_id);
+    // Because of the streamed input just fed, check if there are any new
+    // WorkOrders available and if so, fetch them.
+    fetchNormalWorkOrders(consumer_index);
+  }
+}
+
+void QueryManagerBase::markOperatorFinished(const dag_node_index index) {
+  query_exec_state_->setExecutionFinished(index);
+
+  RelationalOperator *op = query_dag_->getNodePayloadMutable(index);
+  op->updateCatalogOnCompletion();
+
+  const relation_id output_rel = op->getOutputRelationID();
+  for (const pair<dag_node_index, bool> &dependent_link : query_dag_->getDependents(index)) {
+    const dag_node_index dependent_op_index = dependent_link.first;
+    RelationalOperator *dependent_op = query_dag_->getNodePayloadMutable(dependent_op_index);
+    // Signal dependent operator that current operator is done feeding input blocks.
+    if (output_rel >= 0) {
+      dependent_op->doneFeedingInputBlocks(output_rel);
+    }
+    if (checkAllBlockingDependenciesMet(dependent_op_index)) {
+      dependent_op->informAllBlockingDependenciesMet();
+    }
+  }
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b4e25edc/query_execution/QueryManagerBase.hpp
----------------------------------------------------------------------
diff --git a/query_execution/QueryManagerBase.hpp b/query_execution/QueryManagerBase.hpp
new file mode 100644
index 0000000..9e192c8
--- /dev/null
+++ b/query_execution/QueryManagerBase.hpp
@@ -0,0 +1,312 @@
+/**
+ *   Copyright 2016, Quickstep Research Group, Computer Sciences Department,
+ *     University of Wisconsin\u2014Madison.
+ *
+ *   Licensed 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.
+ **/
+
+#ifndef QUICKSTEP_QUERY_EXECUTION_QUERY_MANAGER_BASE_HPP_
+#define QUICKSTEP_QUERY_EXECUTION_QUERY_MANAGER_BASE_HPP_
+
+#include <cstddef>
+#include <memory>
+#include <vector>
+
+#include "catalog/CatalogTypedefs.hpp"
+#include "query_execution/QueryExecutionState.hpp"
+#include "query_execution/QueryExecutionTypedefs.hpp"
+#include "relational_operators/RelationalOperator.hpp"
+#include "relational_operators/WorkOrder.hpp"
+#include "storage/StorageBlockInfo.hpp"
+#include "utility/DAG.hpp"
+#include "utility/Macros.hpp"
+
+namespace quickstep {
+
+class CatalogDatabaseLite;
+class QueryHandle;
+
+/** \addtogroup QueryExecution
+ *  @{
+ */
+
+/**
+ * @brief A base class that manages the execution of a query including
+ *        generation of new work orders, and keeping track of the query
+ *        exection state.
+ **/
+class QueryManagerBase {
+ public:
+  typedef DAG<RelationalOperator, bool>::size_type_nodes dag_node_index;
+
+  /**
+   * @brief Return codes for processMessage() function.
+   *
+   * @note When both operator and query get executed, kQueryExecuted takes
+   *       precedence over kOperatorExecuted.
+   **/
+  enum class QueryStatusCode {
+    kOperatorExecuted = 0,  // An operator in the query finished execution.
+    kQueryExecuted,         // The query got executed.
+    kNone                   // None of the above.
+  };
+
+  /**
+   * @brief Constructor.
+   *
+   * @param query_handle The QueryHandle object for this query.
+   * @param catalog_database The CatalogDatabse used by the query.
+   **/
+  QueryManagerBase(QueryHandle *query_handle,
+                   CatalogDatabaseLite *catalog_database);
+
+  /**
+   * @brief Virtual destructor.
+   **/
+  virtual ~QueryManagerBase() {}
+
+  /**
+   * @brief Process a message sent to the QueryManager.
+   *
+   * @param tagged_message TaggedMessage sent to the QueryManager.
+   *
+   * @return QueryStatusCode as determined after the message is processed.
+   **/
+  QueryStatusCode processMessage(const TaggedMessage &tagged_message);
+
+  /**
+   * @brief Get the QueryExecutionState for this query.
+   **/
+  inline const QueryExecutionState& getQueryExecutionState() const {
+    return *query_exec_state_;
+  }
+
+ protected:
+  /**
+   * @brief Process the received WorkOrder complete message.
+   *
+   * @param node_index The index of the specified operator node in the query DAG
+   *        for the completed WorkOrder.
+   **/
+  void processWorkOrderCompleteMessage(const dag_node_index op_index);
+
+  /**
+   * @brief Process the received RebuildWorkOrder complete message.
+   *
+   * @param node_index The index of the specified operator node in the query DAG
+   *        for the completed RebuildWorkOrder.
+   **/
+  void processRebuildWorkOrderCompleteMessage(const dag_node_index op_index);
+
+  /**
+   * @brief Process a current relational operator: Get its workorders and store
+   *        them in the WorkOrdersContainer for this query. If the operator can
+   *        be marked as done, do so.
+   *
+   * @param index The index of the relational operator to be processed in the
+   *        query plan DAG.
+   * @param recursively_check_dependents If an operator is done, should we
+   *        call processOperator on its dependents recursively.
+   **/
+  void processOperator(const dag_node_index index,
+                       const bool recursively_check_dependents);
+
+  /**
+   * @brief Process the received data pipeline message.
+   *
+   * @param node_index The index of the specified operator node in the query DAG
+   *        for the pipelining block.
+   * @param block The block id.
+   * @param rel_id The ID of the relation that produced 'block'.
+   **/
+  void processDataPipelineMessage(const dag_node_index op_index,
+                                  const block_id block,
+                                  const relation_id rel_id);
+
+  /**
+   * @brief Process the received work order feedback message and notify
+   *        relational operator.
+   *
+   * @param message Feedback message from work order.
+   **/
+  void processFeedbackMessage(const WorkOrder::FeedbackMessage &message);
+
+  /**
+   * @brief This function does the following things:
+   *        1. Mark the given relational operator as "done".
+   *        2. For all the dependents of this operator, check if all of their
+   *        blocking dependencies are met. If so inform them that the blocking
+   *        dependencies are met.
+   *        3. Check if the given operator is done producing output. If it's
+   *        done, inform the dependents that they won't receive input anymore
+   *        from the given operator.
+   *
+   * @param index The index of the given relational operator in the DAG.
+   **/
+  void markOperatorFinished(const dag_node_index index);
+
+  /**
+   * @brief Check if all the dependencies of the node at specified index have
+   *        finished their execution.
+   *
+   * @note This function's true return value is a pre-requisite for calling
+   *       getRebuildWorkOrders()
+   *
+   * @param node_index The index of the specified node in the query DAG.
+   *
+   * @return True if all the dependencies have finished their execution. False
+   *         otherwise.
+   **/
+  inline bool checkAllDependenciesMet(const dag_node_index node_index) const {
+    for (const dag_node_index dependency_index :
+         query_dag_->getDependencies(node_index)) {
+      // If at least one of the dependencies is not met, return false.
+      if (!query_exec_state_->hasExecutionFinished(dependency_index)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * @brief Check if all the blocking dependencies of the node at specified
+   *        index have finished their execution.
+   *
+   * @note A blocking dependency is the one which is pipeline breaker. Output of
+   *       a dependency can't be streamed to its dependent if the link between
+   *       them is pipeline breaker.
+   *
+   * @param node_index The index of the specified node in the query DAG.
+   *
+   * @return True if all the blocking dependencies have finished their
+   *         execution. False otherwise.
+   **/
+  inline bool checkAllBlockingDependenciesMet(
+      const dag_node_index node_index) const {
+    for (const dag_node_index blocking_dependency_index :
+         blocking_dependencies_[node_index]) {
+      if (!query_exec_state_->hasExecutionFinished(
+              blocking_dependency_index)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * @brief Check if the execution of the given operator is over.
+   *
+   * @param index The index of the given operator in the DAG.
+   *
+   * @return True if the execution of the given operator is over, false
+   *         otherwise.
+   **/
+  inline bool checkOperatorExecutionOver(const dag_node_index index) const {
+    return this->checkNormalExecutionOver(index) &&
+           (!checkRebuildRequired(index) || this->checkRebuildOver(index));
+  }
+
+  /**
+   * @brief Check if the rebuild operation is required for a given operator.
+   *
+   * @param index The index of the given operator in the DAG.
+   *
+   * @return True if the rebuild operation is required, false otherwise.
+   **/
+  inline bool checkRebuildRequired(const dag_node_index index) const {
+    return query_exec_state_->isRebuildRequired(index);
+  }
+
+  /**
+   * @brief Check if the rebuild operation for a given operator has been
+   *        initiated.
+   *
+   * @param index The index of the given operator in the DAG.
+   *
+   * @return True if the rebuild operation has been initiated, false otherwise.
+   **/
+  inline bool checkRebuildInitiated(const dag_node_index index) const {
+    return query_exec_state_->hasRebuildInitiated(index);
+  }
+
+  const std::size_t query_id_;
+
+  CatalogDatabaseLite *catalog_database_;
+
+  DAG<RelationalOperator, bool> *query_dag_;
+  const dag_node_index num_operators_in_dag_;
+
+  // For all nodes, store their receiving dependents.
+  std::vector<std::vector<dag_node_index>> output_consumers_;
+
+  // For all nodes, store their pipeline breaking dependencies (if any).
+  std::vector<std::vector<dag_node_index>> blocking_dependencies_;
+
+  std::unique_ptr<QueryExecutionState> query_exec_state_;
+
+ private:
+  /**
+   * @brief Fetch all work orders currently available in relational operator and
+   *        store them internally.
+   *
+   * @param index The index of the relational operator to be processed in the
+   *        query plan DAG.
+   *
+   * @return Whether any work order was generated by op.
+   **/
+  virtual bool fetchNormalWorkOrders(const dag_node_index index) = 0;
+
+  /**
+   * @brief Check if the given operator's normal execution is over.
+   *
+   * @note The conditions for a given operator's normal execution to get over:
+   *       1. All of its  normal (i.e. non rebuild) WorkOrders have finished
+   *       execution.
+   *       2. The operator is done generating work orders.
+   *       3. All of the dependencies of the given operator have been met.
+   *
+   * @param index The index of the given operator in the DAG.
+   *
+   * @return True if the normal execution of the given operator is over, false
+   *         otherwise.
+   **/
+  virtual bool checkNormalExecutionOver(const dag_node_index index) const = 0;
+
+  /**
+   * @brief Initiate the rebuild process for partially filled blocks generated
+   *        during the execution of the given operator.
+   *
+   * @param index The index of the given operator in the DAG.
+   *
+   * @return True if the rebuild is over immediately, i.e. the operator didn't
+   *         generate any rebuild WorkOrders, false otherwise.
+   **/
+  virtual bool initiateRebuild(const dag_node_index index) = 0;
+
+  /**
+   * @brief Check if the rebuild operation for a given operator is over.
+   *
+   * @param index The index of the given operator in the DAG.
+   *
+   * @return True if the rebuild operation is over, false otherwise.
+   **/
+  virtual bool checkRebuildOver(const dag_node_index index) const = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(QueryManagerBase);
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_QUERY_EXECUTION_QUERY_MANAGER_BASE_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b4e25edc/query_execution/QueryManagerSingleNode.cpp
----------------------------------------------------------------------
diff --git a/query_execution/QueryManagerSingleNode.cpp b/query_execution/QueryManagerSingleNode.cpp
new file mode 100644
index 0000000..193b188
--- /dev/null
+++ b/query_execution/QueryManagerSingleNode.cpp
@@ -0,0 +1,194 @@
+/**
+ *   Copyright 2016, Quickstep Research Group, Computer Sciences Department,
+ *     University of Wisconsin\u2014Madison.
+ *
+ *   Licensed 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.
+ **/
+
+#include "query_execution/QueryManagerSingleNode.hpp"
+
+#include <cstddef>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "catalog/CatalogTypedefs.hpp"
+#include "query_execution/WorkerMessage.hpp"
+#include "query_optimizer/QueryHandle.hpp"
+#include "relational_operators/RebuildWorkOrder.hpp"
+#include "relational_operators/RelationalOperator.hpp"
+#include "storage/InsertDestination.hpp"
+#include "storage/StorageBlock.hpp"
+#include "utility/DAG.hpp"
+
+#include "glog/logging.h"
+
+#include "tmb/id_typedefs.h"
+
+namespace quickstep {
+
+class WorkOrder;
+
+QueryManagerSingleNode::QueryManagerSingleNode(
+    const tmb::client_id foreman_client_id,
+    const std::size_t num_numa_nodes,
+    QueryHandle *query_handle,
+    CatalogDatabaseLite *catalog_database,
+    StorageManager *storage_manager,
+    tmb::MessageBus *bus)
+    : QueryManagerBase(query_handle, catalog_database),
+      foreman_client_id_(foreman_client_id),
+      storage_manager_(DCHECK_NOTNULL(storage_manager)),
+      bus_(DCHECK_NOTNULL(bus)),
+      query_context_(new QueryContext(query_handle->getQueryContextProto(),
+                                      *catalog_database_,
+                                      storage_manager_,
+                                      foreman_client_id_,
+                                      bus_)),
+      workorders_container_(
+          new WorkOrdersContainer(num_operators_in_dag_, num_numa_nodes)) {
+  // Collect all the workorders from all the relational operators in the DAG.
+  for (dag_node_index index = 0; index < num_operators_in_dag_; ++index) {
+    if (checkAllBlockingDependenciesMet(index)) {
+      query_dag_->getNodePayloadMutable(index)->informAllBlockingDependenciesMet();
+      processOperator(index, false);
+    }
+  }
+}
+
+WorkerMessage* QueryManagerSingleNode::getNextWorkerMessage(
+    const dag_node_index start_operator_index, const numa_node_id numa_node) {
+  // Default policy: Operator with lowest index first.
+  WorkOrder *work_order = nullptr;
+  size_t num_operators_checked = 0;
+  for (dag_node_index index = start_operator_index;
+       num_operators_checked < num_operators_in_dag_;
+       index = (index + 1) % num_operators_in_dag_, ++num_operators_checked) {
+    if (query_exec_state_->hasExecutionFinished(index)) {
+      continue;
+    }
+    if (numa_node != kAnyNUMANodeID) {
+      // First try to get a normal WorkOrder from the specified NUMA node.
+      work_order = workorders_container_->getNormalWorkOrderForNUMANode(index, numa_node);
+      if (work_order != nullptr) {
+        // A WorkOrder found on the given NUMA node.
+        query_exec_state_->incrementNumQueuedWorkOrders(index);
+        return WorkerMessage::WorkOrderMessage(work_order, index);
+      } else {
+        // Normal workorder not found on this node. Look for a rebuild workorder
+        // on this NUMA node.
+        work_order = workorders_container_->getRebuildWorkOrderForNUMANode(index, numa_node);
+        if (work_order != nullptr) {
+          return WorkerMessage::RebuildWorkOrderMessage(work_order, index);
+        }
+      }
+    }
+    // Either no workorder found on the given NUMA node, or numa_node is
+    // 'kAnyNUMANodeID'.
+    // Try to get a normal WorkOrder from other NUMA nodes.
+    work_order = workorders_container_->getNormalWorkOrder(index);
+    if (work_order != nullptr) {
+      query_exec_state_->incrementNumQueuedWorkOrders(index);
+      return WorkerMessage::WorkOrderMessage(work_order, index);
+    } else {
+      // Normal WorkOrder not found, look for a RebuildWorkOrder.
+      work_order = workorders_container_->getRebuildWorkOrder(index);
+      if (work_order != nullptr) {
+        return WorkerMessage::RebuildWorkOrderMessage(work_order, index);
+      }
+    }
+  }
+  // No WorkOrders available right now.
+  return nullptr;
+}
+
+bool QueryManagerSingleNode::fetchNormalWorkOrders(const dag_node_index index) {
+  bool generated_new_workorders = false;
+  if (!query_exec_state_->hasDoneGenerationWorkOrders(index)) {
+    // Do not fetch any work units until all blocking dependencies are met.
+    // The releational operator is not aware of blocking dependencies for
+    // uncorrelated scalar queries.
+    if (!checkAllBlockingDependenciesMet(index)) {
+      return false;
+    }
+    const size_t num_pending_workorders_before =
+        workorders_container_->getNumNormalWorkOrders(index);
+    const bool done_generation =
+        query_dag_->getNodePayloadMutable(index)->getAllWorkOrders(workorders_container_.get(),
+                                                                   query_context_.get(),
+                                                                   storage_manager_,
+                                                                   foreman_client_id_,
+                                                                   bus_);
+    if (done_generation) {
+      query_exec_state_->setDoneGenerationWorkOrders(index);
+    }
+
+    // TODO(shoban): It would be a good check to see if operator is making
+    // useful progress, i.e., the operator either generates work orders to
+    // execute or still has pending work orders executing. However, this will not
+    // work if Foreman polls operators without feeding data. This check can be
+    // enabled, if Foreman is refactored to call getAllWorkOrders() only when
+    // pending work orders are completed or new input blocks feed.
+
+    generated_new_workorders =
+        (num_pending_workorders_before <
+         workorders_container_->getNumNormalWorkOrders(index));
+  }
+  return generated_new_workorders;
+}
+
+bool QueryManagerSingleNode::initiateRebuild(const dag_node_index index) {
+  DCHECK(!workorders_container_->hasRebuildWorkOrder(index));
+  DCHECK(checkRebuildRequired(index));
+  DCHECK(!checkRebuildInitiated(index));
+
+  getRebuildWorkOrders(index, workorders_container_.get());
+
+  query_exec_state_->setRebuildStatus(
+      index, workorders_container_->getNumRebuildWorkOrders(index), true);
+
+  return (query_exec_state_->getNumRebuildWorkOrders(index) == 0);
+}
+
+void QueryManagerSingleNode::getRebuildWorkOrders(const dag_node_index index,
+                                                  WorkOrdersContainer *container) {
+  const RelationalOperator &op = query_dag_->getNodePayload(index);
+  const QueryContext::insert_destination_id insert_destination_index = op.getInsertDestinationID();
+
+  if (insert_destination_index == QueryContext::kInvalidInsertDestinationId) {
+    return;
+  }
+
+  std::vector<MutableBlockReference> partially_filled_block_refs;
+
+  DCHECK(query_context_ != nullptr);
+  InsertDestination *insert_destination = query_context_->getInsertDestination(insert_destination_index);
+  DCHECK(insert_destination != nullptr);
+
+  insert_destination->getPartiallyFilledBlocks(&partially_filled_block_refs);
+
+  for (std::vector<MutableBlockReference>::size_type i = 0;
+       i < partially_filled_block_refs.size();
+       ++i) {
+    container->addRebuildWorkOrder(
+        new RebuildWorkOrder(query_id_,
+                             std::move(partially_filled_block_refs[i]),
+                             index,
+                             op.getOutputRelationID(),
+                             foreman_client_id_,
+                             bus_),
+        index);
+  }
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b4e25edc/query_execution/QueryManagerSingleNode.hpp
----------------------------------------------------------------------
diff --git a/query_execution/QueryManagerSingleNode.hpp b/query_execution/QueryManagerSingleNode.hpp
new file mode 100644
index 0000000..5533f06
--- /dev/null
+++ b/query_execution/QueryManagerSingleNode.hpp
@@ -0,0 +1,140 @@
+/**
+ *   Copyright 2016, Quickstep Research Group, Computer Sciences Department,
+ *     University of Wisconsin\u2014Madison.
+ *
+ *   Licensed 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.
+ **/
+
+#ifndef QUICKSTEP_QUERY_EXECUTION_QUERY_MANAGER_SINGLE_NODE_HPP_
+#define QUICKSTEP_QUERY_EXECUTION_QUERY_MANAGER_SINGLE_NODE_HPP_
+
+#include <cstddef>
+#include <memory>
+
+#include "catalog/CatalogTypedefs.hpp"
+#include "query_execution/QueryContext.hpp"
+#include "query_execution/QueryExecutionState.hpp"
+#include "query_execution/QueryManagerBase.hpp"
+#include "query_execution/WorkOrdersContainer.hpp"
+#include "utility/Macros.hpp"
+
+#include "tmb/id_typedefs.h"
+
+namespace tmb { class MessageBus; }
+
+namespace quickstep {
+
+class CatalogDatabaseLite;
+class QueryHandle;
+class StorageManager;
+class WorkerMessage;
+
+/** \addtogroup QueryExecution
+ *  @{
+ */
+
+/**
+ * @brief A class that manages the execution of a query including generation
+ *        of new work orders, keeping track of the query exection state.
+ **/
+class QueryManagerSingleNode final : public QueryManagerBase {
+ public:
+  /**
+   * @brief Constructor.
+   *
+   * @param foreman_client_id The TMB client ID of the foreman thread.
+   * @param num_numa_nodes The number of NUMA nodes used by the system.
+   * @param query_handle The QueryHandle object for this query.
+   * @param catalog_database The CatalogDatabse used by the query.
+   * @param storage_manager The StorageManager used by the query.
+   * @param bus The TMB used for communication.
+   **/
+  QueryManagerSingleNode(const tmb::client_id foreman_client_id,
+                         const std::size_t num_numa_nodes,
+                         QueryHandle *query_handle,
+                         CatalogDatabaseLite *catalog_database,
+                         StorageManager *storage_manager,
+                         tmb::MessageBus *bus);
+
+  ~QueryManagerSingleNode() override {}
+
+ /**
+   * @brief Get the next workorder to be excuted, wrapped in a WorkerMessage.
+   *
+   * @param start_operator_index Begin the search for the schedulable WorkOrder
+   *        with the operator at this index.
+   * @param numa_node The next WorkOrder should preferably have its input(s)
+   *        from this numa_node. This is a hint and not a binding requirement.
+   *
+   * @return A pointer to the WorkerMessage. If there's no WorkOrder to be
+   *         executed, return NULL.
+   **/
+  WorkerMessage* getNextWorkerMessage(
+      const dag_node_index start_operator_index,
+      const numa_node_id node_id = kAnyNUMANodeID);
+
+  /**
+   * @brief Get a pointer to the QueryContext.
+   **/
+  inline QueryContext* getQueryContextMutable() {
+    return query_context_.get();
+  }
+
+ private:
+  bool fetchNormalWorkOrders(const dag_node_index index) override;
+
+  bool checkNormalExecutionOver(const dag_node_index index) const override {
+    return (checkAllDependenciesMet(index) &&
+            !workorders_container_->hasNormalWorkOrder(index) &&
+            query_exec_state_->getNumQueuedWorkOrders(index) == 0 &&
+            query_exec_state_->hasDoneGenerationWorkOrders(index));
+  }
+
+  bool initiateRebuild(const dag_node_index index) override;
+
+  bool checkRebuildOver(const dag_node_index index) const override {
+    return query_exec_state_->hasRebuildInitiated(index) &&
+           !workorders_container_->hasRebuildWorkOrder(index) &&
+           (query_exec_state_->getNumRebuildWorkOrders(index) == 0);
+  }
+
+  /**
+   * @brief Get the rebuild WorkOrders for an operator.
+   *
+   * @note This function should be called only once, when all the normal
+   *       WorkOrders generated by an operator finish their execution.
+   *
+   * @param index The index of the operator in the query plan DAG.
+   * @param container A pointer to a WorkOrdersContainer to be used to store the
+   *        generated WorkOrders.
+   **/
+  void getRebuildWorkOrders(const dag_node_index index,
+                            WorkOrdersContainer *container);
+
+  const tmb::client_id foreman_client_id_;
+
+  StorageManager *storage_manager_;
+  tmb::MessageBus *bus_;
+
+  std::unique_ptr<QueryContext> query_context_;
+
+  std::unique_ptr<WorkOrdersContainer> workorders_container_;
+
+  DISALLOW_COPY_AND_ASSIGN(QueryManagerSingleNode);
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_QUERY_EXECUTION_QUERY_MANAGER_SINGLE_NODE_HPP_


[27/30] incubator-quickstep git commit: Initial commit for QUICKSTEP-28 and QUICKSTEP-29. Code refactoring and cleanup, some more optimizations are pending.

Posted by ra...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/storage/FastHashTableFactory.hpp
----------------------------------------------------------------------
diff --git a/storage/FastHashTableFactory.hpp b/storage/FastHashTableFactory.hpp
new file mode 100644
index 0000000..6ad3212
--- /dev/null
+++ b/storage/FastHashTableFactory.hpp
@@ -0,0 +1,300 @@
+/**
+ *   Copyright 2015-2016 Pivotal Software, Inc.
+ *
+ *   Licensed 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.
+ **/
+
+#ifndef QUICKSTEP_STORAGE_FAST_HASH_TABLE_FACTORY_HPP_
+#define QUICKSTEP_STORAGE_FAST_HASH_TABLE_FACTORY_HPP_
+
+#include <cstddef>
+#include <string>
+#include <vector>
+
+#include "storage/HashTable.hpp"
+#include "storage/FastHashTable.hpp"
+#include "storage/HashTableBase.hpp"
+#include "storage/HashTableFactory.hpp"
+#include "storage/HashTable.pb.h"
+#include "storage/LinearOpenAddressingHashTable.hpp"
+#include "storage/SeparateChainingHashTable.hpp"
+#include "storage/FastSeparateChainingHashTable.hpp"
+#include "storage/SimpleScalarSeparateChainingHashTable.hpp"
+#include "storage/TupleReference.hpp"
+#include "types/TypeFactory.hpp"
+#include "utility/BloomFilter.hpp"
+#include "utility/Macros.hpp"
+
+#include "glog/logging.h"
+
+namespace quickstep {
+
+class StorageManager;
+class Type;
+
+/** \addtogroup Storage
+ *  @{
+ */
+
+/**
+ * @brief Templated all-static factory class that makes it easier to
+ *        instantiate HashTables with the particular HashTable implementation
+ *        chosen at runtime. All template parameters are exactly the same as
+ *        those of HashTable.
+ **/
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
+class FastHashTableFactory {
+ public:
+  /**
+   * @brief Create a new resizable HashTable, with the type selected by
+   *        hash_table_type. Other parameters are forwarded to the HashTable's
+   *        constructor.
+   *
+   * @param hash_table_type The specific HashTable implementation that should
+   *        be used.
+   * @param key_types A vector of one or more types (>1 indicates a composite
+   *        key). Forwarded as-is to the HashTable's constructor.
+   * @param num_entries The estimated number of entries the HashTable will
+   *        hold. Forwarded as-is to the HashTable's constructor.
+   * @param storage_manager The StorageManager to use (a StorageBlob will be
+   *        allocated to hold the HashTable's contents). Forwarded as-is to the
+   *        HashTable's constructor.
+   * @return A new resizable HashTable.
+   **/
+  static FastHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>*
+      CreateResizable(const HashTableImplType hash_table_type,
+                      const std::vector<const Type*> &key_types,
+                      const std::size_t num_entries,
+                      const std::vector<std::size_t> &payload_sizes,
+                      const std::vector<AggregationHandle *> &handles,
+                      StorageManager *storage_manager) {
+    DCHECK(resizable);
+
+    switch (hash_table_type) {
+      case HashTableImplType::kSeparateChaining:
+        return new FastSeparateChainingHashTable<
+            resizable,
+            serializable,
+            force_key_copy,
+            allow_duplicate_keys>(key_types, num_entries, payload_sizes, handles, storage_manager);
+      case HashTableImplType::kLinearOpenAddressing:
+/*        return new LinearOpenAddressingHashTable<
+            ValueT,
+            resizable,
+            serializable,
+            force_key_copy,
+            allow_duplicate_keys>(key_types, num_entries, storage_manager);*/
+        return new FastSeparateChainingHashTable<
+            resizable,
+            serializable,
+            force_key_copy,
+            allow_duplicate_keys>(key_types, num_entries, payload_sizes, handles, storage_manager);
+      case HashTableImplType::kSimpleScalarSeparateChaining:
+        return new FastSeparateChainingHashTable<
+            resizable,
+            serializable,
+            force_key_copy,
+            allow_duplicate_keys>(key_types, num_entries, payload_sizes, handles, storage_manager);
+/*        return new SimpleScalarSeparateChainingHashTable<
+            ValueT,
+            resizable,
+            serializable,
+            force_key_copy,
+            allow_duplicate_keys>(key_types, num_entries, storage_manager);*/
+      default: {
+        LOG(FATAL) << "Unrecognized HashTableImplType in HashTableFactory::createResizable()\n";
+      }
+    }
+  }
+
+  /**
+   * @brief Create a new fixed-sized HashTable, with the type selected by
+   *        hash_table_type. Other parameters are forwarded to the HashTables's
+   *        constructor.
+   *
+   * @param hash_table_type The specific HashTable implementation that should
+   *        be used.
+   * @param key_types A vector of one or more types (>1 indicates a composite
+   *        key). Forwarded as-is to the HashTable's constructor.
+   * @param hash_table_memory A pointer to memory to use for the HashTable.
+   *        Forwarded as-is to the HashTable's constructor.
+   * @param hash_table_memory_size The size of hash_table_memory in bytes.
+   *        Forwarded as-is to the HashTable's constructor.
+   * @param new_hash_table If true, the HashTable is being constructed for the
+   *        first time and hash_table_memory will be cleared. If false, reload
+   *        a pre-existing HashTable. Forwarded as-is to the HashTable's
+   *        constructor.
+   * @param hash_table_memory_zeroed If new_hash_table is true, setting this to
+   *        true means that the HashTable will assume that hash_table_memory
+   *        has already been zeroed-out (any newly-allocated block or blob
+   *        memory from StorageManager is zeroed-out). If false, the HashTable
+   *        will explicitly zero-fill its memory as neccessary. This parameter
+   *        has no effect when new_hash_table is false. Forwarded as-is to the
+   *        HashTable's constructor.
+   * @return A new (or reloaded) fixed-size HashTable.
+   **/
+  static FastHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>*
+      CreateFixedSize(const HashTableImplType hash_table_type,
+                      const std::vector<const Type*> &key_types,
+                      void *hash_table_memory,
+                      const std::size_t hash_table_memory_size,
+                      const bool new_hash_table,
+                      const bool hash_table_memory_zeroed) {
+    DCHECK(!resizable);
+
+    switch (hash_table_type) {
+      case HashTableImplType::kSeparateChaining:
+        return new SeparateChainingHashTable<
+            int,
+            resizable,
+            serializable,
+            force_key_copy,
+            allow_duplicate_keys>(key_types,
+                                  hash_table_memory,
+                                  hash_table_memory_size,
+                                  new_hash_table,
+                                  hash_table_memory_zeroed);
+      case HashTableImplType::kLinearOpenAddressing:
+/*        return new LinearOpenAddressingHashTable<
+            ValueT,
+            resizable,
+            serializable,
+            force_key_copy,
+            allow_duplicate_keys>(key_types,
+                                  hash_table_memory,
+                                  hash_table_memory_size,
+                                  new_hash_table,
+                                  hash_table_memory_zeroed);*/
+      case HashTableImplType::kSimpleScalarSeparateChaining:
+/*        return new SimpleScalarSeparateChainingHashTable<
+            ValueT,
+            resizable,
+            serializable,
+            force_key_copy,
+            allow_duplicate_keys>(key_types,
+                                  hash_table_memory,
+                                  hash_table_memory_size,
+                                  new_hash_table,
+                                  hash_table_memory_zeroed);*/
+      default: {
+        LOG(FATAL) << "Unrecognized HashTableImplType\n";
+      }
+    }
+  }
+
+  /**
+   * @brief Check whether a serialization::HashTable describing a resizable
+   *        HashTable is fully-formed and all parts are valid.
+   *
+   * @param proto A serialized Protocol Buffer description of a HashTable,
+   *        originally generated by the optimizer.
+   * @return Whether proto is fully-formed and valid.
+   **/
+  static bool ProtoIsValid(const serialization::HashTable &proto) {
+    if (!proto.IsInitialized() ||
+        !serialization::HashTableImplType_IsValid(
+            proto.hash_table_impl_type())) {
+      return false;
+    }
+
+    for (int i = 0; i < proto.key_types_size(); ++i) {
+      if (!TypeFactory::ProtoIsValid(proto.key_types(i))) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  /**
+   * @brief Create a new resizable HashTable according to a protobuf
+   *        description.
+   *
+   * @param proto A protobuf description of a resizable HashTable.
+   * @param storage_manager The StorageManager to use (a StorageBlob will be
+   *        allocated to hold the HashTable's contents).
+   * @param bloom_filters A vector of pointers to bloom filters that may be used
+   *        during hash table construction in build/probe phase.
+   * @return A new resizable HashTable with parameters specified by proto.
+   **/
+  static FastHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>*
+      CreateResizableFromProto(const serialization::HashTable &proto,
+                               StorageManager *storage_manager,
+                               const std::vector<std::unique_ptr<BloomFilter>> &bloom_filters) {
+    DCHECK(ProtoIsValid(proto))
+        << "Attempted to create HashTable from invalid proto description:\n"
+        << proto.DebugString();
+
+    std::vector<const Type*> key_types;
+    for (int i = 0; i < proto.key_types_size(); ++i) {
+      key_types.emplace_back(&TypeFactory::ReconstructFromProto(proto.key_types(i)));
+    }
+
+    auto hash_table = CreateResizable(HashTableImplTypeFromProto(proto.hash_table_impl_type()),
+                                      key_types,
+                                      proto.estimated_num_entries(),
+                                      storage_manager);
+
+    // TODO(ssaurabh): These lazy initializations can be moved from here and pushed to the
+    //                 individual implementations of the hash table constructors.
+
+    // Check if there are any build side bloom filter defined on the hash table.
+    if (proto.build_side_bloom_filter_id_size() > 0) {
+      hash_table->enableBuildSideBloomFilter();
+      hash_table->setBuildSideBloomFilter(bloom_filters[proto.build_side_bloom_filter_id(0)].get());
+    }
+
+    // Check if there are any probe side bloom filters defined on the hash table.
+    if (proto.probe_side_bloom_filters_size() > 0) {
+      hash_table->enableProbeSideBloomFilter();
+      // Add as many probe bloom filters as defined by the proto.
+      for (int j = 0; j < proto.probe_side_bloom_filters_size(); ++j) {
+        // Add the pointer to the probe bloom filter within the list of probe bloom filters to use.
+        const auto probe_side_bloom_filter = proto.probe_side_bloom_filters(j);
+        hash_table->addProbeSideBloomFilter(bloom_filters[probe_side_bloom_filter.probe_side_bloom_filter_id()].get());
+
+        // Add the attribute ids corresponding to this probe bloom filter.
+        std::vector<attribute_id> probe_attribute_ids;
+        for (int k = 0; k < probe_side_bloom_filter.probe_side_attr_ids_size(); ++k) {
+          const attribute_id probe_attribute_id = probe_side_bloom_filter.probe_side_attr_ids(k);
+          probe_attribute_ids.push_back(probe_attribute_id);
+        }
+        hash_table->addProbeSideAttributeIds(std::move(probe_attribute_ids));
+      }
+    }
+
+    return hash_table;
+  }
+
+ private:
+  // Class is all-static and should not be instantiated.
+  FastHashTableFactory();
+
+  DISALLOW_COPY_AND_ASSIGN(FastHashTableFactory);
+};
+
+/**
+ * @brief Convenient alias that provides a HashTableFactory whose only template
+ *        parameter is the aggregate state type.
+ **/
+using AggregationStateFastHashTableFactory
+    = FastHashTableFactory<true, false, true, false>;
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_STORAGE_HASH_TABLE_FACTORY_HPP_


[11/30] incubator-quickstep git commit: Refactored messages processing in both PolicyEnforcer and QueryManager.

Posted by ra...@apache.org.
Refactored messages processing in both PolicyEnforcer and QueryManager.


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

Branch: refs/heads/quickstep-28-29
Commit: db0e7e3ccf4c2d631670a4dc0cd2499f9b0294b4
Parents: cf81b5e
Author: Zuyu Zhang <zu...@apache.org>
Authored: Sat Jul 9 14:54:05 2016 -0700
Committer: Zuyu Zhang <zu...@twitter.com>
Committed: Sat Jul 16 17:23:05 2016 -0700

----------------------------------------------------------------------
 query_execution/CMakeLists.txt                  | 10 +--
 query_execution/PolicyEnforcer.cpp              | 61 ++++++++++---
 query_execution/PolicyEnforcer.hpp              | 10 ++-
 query_execution/QueryManagerBase.cpp            | 91 ++------------------
 query_execution/QueryManagerBase.hpp            | 90 ++++++++++---------
 query_execution/QueryManagerSingleNode.cpp      |  4 +-
 query_execution/QueryManagerSingleNode.hpp      |  4 +-
 .../tests/QueryManagerSingleNode_unittest.cpp   | 79 +++--------------
 8 files changed, 126 insertions(+), 223 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/db0e7e3c/query_execution/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/query_execution/CMakeLists.txt b/query_execution/CMakeLists.txt
index 5a9189c..028531d 100644
--- a/query_execution/CMakeLists.txt
+++ b/query_execution/CMakeLists.txt
@@ -91,8 +91,12 @@ target_link_libraries(quickstep_queryexecution_ForemanSingleNode
                       ${GFLAGS_LIB_NAME})
 target_link_libraries(quickstep_queryexecution_PolicyEnforcer
                       glog
+                      quickstep_catalog_CatalogDatabase
+                      quickstep_catalog_CatalogRelation
                       quickstep_catalog_CatalogTypedefs
+                      quickstep_catalog_PartitionScheme
                       quickstep_queryexecution_QueryExecutionMessages_proto
+                      quickstep_queryexecution_QueryExecutionState
                       quickstep_queryexecution_QueryExecutionTypedefs
                       quickstep_queryexecution_QueryManagerBase
                       quickstep_queryexecution_QueryManagerSingleNode
@@ -100,6 +104,7 @@ target_link_libraries(quickstep_queryexecution_PolicyEnforcer
                       quickstep_queryexecution_WorkerMessage
                       quickstep_queryoptimizer_QueryHandle
                       quickstep_relationaloperators_WorkOrder
+                      quickstep_storage_StorageBlockInfo
                       quickstep_utility_Macros
                       tmb
                       ${GFLAGS_LIB_NAME})
@@ -152,14 +157,9 @@ target_link_libraries(quickstep_queryexecution_QueryExecutionUtil
                       quickstep_utility_Macros
                       tmb)
 target_link_libraries(quickstep_queryexecution_QueryManagerBase
-                      quickstep_catalog_CatalogDatabase
-                      quickstep_catalog_CatalogRelation
                       quickstep_catalog_CatalogTypedefs
-                      quickstep_catalog_PartitionScheme
                       quickstep_queryexecution_QueryContext
-                      quickstep_queryexecution_QueryExecutionMessages_proto
                       quickstep_queryexecution_QueryExecutionState
-                      quickstep_queryexecution_QueryExecutionTypedefs
                       quickstep_queryoptimizer_QueryHandle
                       quickstep_queryoptimizer_QueryPlan
                       quickstep_relationaloperators_RelationalOperator

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/db0e7e3c/query_execution/PolicyEnforcer.cpp
----------------------------------------------------------------------
diff --git a/query_execution/PolicyEnforcer.cpp b/query_execution/PolicyEnforcer.cpp
index f310ee1..4cba8c5 100644
--- a/query_execution/PolicyEnforcer.cpp
+++ b/query_execution/PolicyEnforcer.cpp
@@ -24,12 +24,19 @@
 #include <unordered_map>
 #include <vector>
 
+#include "catalog/CatalogDatabase.hpp"
+#include "catalog/CatalogRelation.hpp"
 #include "catalog/CatalogTypedefs.hpp"
+#include "catalog/PartitionScheme.hpp"
 #include "query_execution/QueryExecutionMessages.pb.h"
+#include "query_execution/QueryExecutionState.hpp"
+#include "query_execution/QueryManagerBase.hpp"
 #include "query_execution/QueryManagerSingleNode.hpp"
 #include "query_execution/WorkerDirectory.hpp"
+#include "query_execution/WorkerMessage.hpp"
 #include "query_optimizer/QueryHandle.hpp"
 #include "relational_operators/WorkOrder.hpp"
+#include "storage/StorageBlockInfo.hpp"
 
 #include "gflags/gflags.h"
 #include "glog/logging.h"
@@ -62,10 +69,9 @@ bool PolicyEnforcer::admitQuery(QueryHandle *query_handle) {
 }
 
 void PolicyEnforcer::processMessage(const TaggedMessage &tagged_message) {
-  // TODO(harshad) : Provide processXMessage() public functions in
-  // QueryManager, so that we need to extract message from the
-  // TaggedMessage only once.
   std::size_t query_id;
+  QueryManagerBase::dag_node_index op_index;
+
   switch (tagged_message.message_type()) {
     case kWorkOrderCompleteMessage: {
       serialization::NormalWorkOrderCompletionMessage proto;
@@ -73,12 +79,17 @@ void PolicyEnforcer::processMessage(const TaggedMessage &tagged_message) {
       // WorkOrder. It can be accessed in this scope.
       CHECK(proto.ParseFromArray(tagged_message.message(),
                                  tagged_message.message_bytes()));
-      query_id = proto.query_id();
       worker_directory_->decrementNumQueuedWorkOrders(
           proto.worker_thread_index());
       if (profile_individual_workorders_) {
         recordTimeForWorkOrder(proto);
       }
+
+      query_id = proto.query_id();
+      DCHECK(admitted_queries_.find(query_id) != admitted_queries_.end());
+
+      op_index = proto.operator_index();
+      admitted_queries_[query_id]->processWorkOrderCompleteMessage(op_index);
       break;
     }
     case kRebuildWorkOrderCompleteMessage: {
@@ -87,23 +98,43 @@ void PolicyEnforcer::processMessage(const TaggedMessage &tagged_message) {
       // rebuild WorkOrder. It can be accessed in this scope.
       CHECK(proto.ParseFromArray(tagged_message.message(),
                                  tagged_message.message_bytes()));
-      query_id = proto.query_id();
       worker_directory_->decrementNumQueuedWorkOrders(
           proto.worker_thread_index());
+
+      query_id = proto.query_id();
+      DCHECK(admitted_queries_.find(query_id) != admitted_queries_.end());
+
+      op_index = proto.operator_index();
+      admitted_queries_[query_id]->processRebuildWorkOrderCompleteMessage(op_index);
       break;
     }
     case kCatalogRelationNewBlockMessage: {
       serialization::CatalogRelationNewBlockMessage proto;
       CHECK(proto.ParseFromArray(tagged_message.message(),
                                  tagged_message.message_bytes()));
-      query_id = proto.query_id();
-      break;
+
+      const block_id block = proto.block_id();
+
+      CatalogRelation *relation =
+          static_cast<CatalogDatabase*>(catalog_database_)->getRelationByIdMutable(proto.relation_id());
+      relation->addBlock(block);
+
+      if (proto.has_partition_id()) {
+        relation->getPartitionSchemeMutable()->addBlockToPartition(
+            proto.partition_id(), block);
+      }
+      return;
     }
     case kDataPipelineMessage: {
       serialization::DataPipelineMessage proto;
       CHECK(proto.ParseFromArray(tagged_message.message(),
                                  tagged_message.message_bytes()));
       query_id = proto.query_id();
+      DCHECK(admitted_queries_.find(query_id) != admitted_queries_.end());
+
+      op_index = proto.operator_index();
+      admitted_queries_[query_id]->processDataPipelineMessage(
+          op_index, proto.block_id(), proto.relation_id());
       break;
     }
     case kWorkOrdersAvailableMessage: {
@@ -111,6 +142,12 @@ void PolicyEnforcer::processMessage(const TaggedMessage &tagged_message) {
       CHECK(proto.ParseFromArray(tagged_message.message(),
                                  tagged_message.message_bytes()));
       query_id = proto.query_id();
+      DCHECK(admitted_queries_.find(query_id) != admitted_queries_.end());
+
+      op_index = proto.operator_index();
+
+      // Check if new work orders are available.
+      admitted_queries_[query_id]->fetchNormalWorkOrders(op_index);
       break;
     }
     case kWorkOrderFeedbackMessage: {
@@ -118,15 +155,17 @@ void PolicyEnforcer::processMessage(const TaggedMessage &tagged_message) {
           const_cast<void *>(tagged_message.message()),
           tagged_message.message_bytes());
       query_id = msg.header().query_id;
+      DCHECK(admitted_queries_.find(query_id) != admitted_queries_.end());
+
+      op_index = msg.header().rel_op_index;
+      admitted_queries_[query_id]->processFeedbackMessage(op_index, msg);
       break;
     }
     default:
       LOG(FATAL) << "Unknown message type found in PolicyEnforcer";
   }
-  DCHECK(admitted_queries_.find(query_id) != admitted_queries_.end());
-  const QueryManagerBase::QueryStatusCode return_code =
-      admitted_queries_[query_id]->processMessage(tagged_message);
-  if (return_code == QueryManagerBase::QueryStatusCode::kQueryExecuted) {
+  if (admitted_queries_[query_id]->queryStatus(op_index) ==
+          QueryManagerBase::QueryStatusCode::kQueryExecuted) {
     removeQuery(query_id);
     if (!waiting_queries_.empty()) {
       // Admit the earliest waiting query.

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/db0e7e3c/query_execution/PolicyEnforcer.hpp
----------------------------------------------------------------------
diff --git a/query_execution/PolicyEnforcer.hpp b/query_execution/PolicyEnforcer.hpp
index 79e61d1..8bd6d92 100644
--- a/query_execution/PolicyEnforcer.hpp
+++ b/query_execution/PolicyEnforcer.hpp
@@ -33,8 +33,8 @@
 #include "glog/logging.h"
 
 #include "tmb/id_typedefs.h"
-#include "tmb/message_bus.h"
-#include "tmb/tagged_message.h"
+
+namespace tmb { class MessageBus; }
 
 namespace quickstep {
 
@@ -43,6 +43,12 @@ class QueryHandle;
 class StorageManager;
 class WorkerDirectory;
 
+namespace serialization { class NormalWorkOrderCompletionMessage; }
+
+/** \addtogroup QueryExecution
+ *  @{
+ */
+
 /**
  * @brief A class that ensures that a high level policy is maintained
  *        in sharing resources among concurrent queries.

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/db0e7e3c/query_execution/QueryManagerBase.cpp
----------------------------------------------------------------------
diff --git a/query_execution/QueryManagerBase.cpp b/query_execution/QueryManagerBase.cpp
index f7e183f..37beb02 100644
--- a/query_execution/QueryManagerBase.cpp
+++ b/query_execution/QueryManagerBase.cpp
@@ -21,13 +21,8 @@
 #include <utility>
 #include <vector>
 
-#include "catalog/CatalogDatabase.hpp"
-#include "catalog/CatalogRelation.hpp"
 #include "catalog/CatalogTypedefs.hpp"
-#include "catalog/PartitionScheme.hpp"
 #include "query_execution/QueryContext.hpp"
-#include "query_execution/QueryExecutionMessages.pb.h"
-#include "query_execution/QueryExecutionTypedefs.hpp"
 #include "query_optimizer/QueryHandle.hpp"
 #include "query_optimizer/QueryPlan.hpp"
 #include "relational_operators/WorkOrder.hpp"
@@ -39,10 +34,8 @@ using std::pair;
 
 namespace quickstep {
 
-QueryManagerBase::QueryManagerBase(QueryHandle *query_handle,
-                                   CatalogDatabaseLite *catalog_database)
+QueryManagerBase::QueryManagerBase(QueryHandle *query_handle)
     : query_id_(DCHECK_NOTNULL(query_handle)->query_id()),
-      catalog_database_(DCHECK_NOTNULL(catalog_database)),
       query_dag_(DCHECK_NOTNULL(
           DCHECK_NOTNULL(query_handle->getQueryPlanMutable())->getQueryPlanDAGMutable())),
       num_operators_in_dag_(query_dag_->size()),
@@ -76,82 +69,8 @@ QueryManagerBase::QueryManagerBase(QueryHandle *query_handle,
   }
 }
 
-QueryManagerBase::QueryStatusCode QueryManagerBase::processMessage(
-    const TaggedMessage &tagged_message) {
-  dag_node_index op_index;
-  switch (tagged_message.message_type()) {
-    case kWorkOrderCompleteMessage: {
-      serialization::NormalWorkOrderCompletionMessage proto;
-      CHECK(proto.ParseFromArray(tagged_message.message(),
-                                 tagged_message.message_bytes()));
-
-      op_index = proto.operator_index();
-      processWorkOrderCompleteMessage(proto.operator_index());
-      break;
-    }
-    case kRebuildWorkOrderCompleteMessage: {
-      serialization::RebuildWorkOrderCompletionMessage proto;
-      CHECK(proto.ParseFromArray(tagged_message.message(),
-                                 tagged_message.message_bytes()));
-
-      op_index = proto.operator_index();
-      processRebuildWorkOrderCompleteMessage(proto.operator_index());
-      break;
-    }
-    case kCatalogRelationNewBlockMessage: {
-      serialization::CatalogRelationNewBlockMessage proto;
-      CHECK(proto.ParseFromArray(tagged_message.message(),
-                                 tagged_message.message_bytes()));
-
-      const block_id block = proto.block_id();
-
-      CatalogRelation *relation =
-          static_cast<CatalogDatabase*>(catalog_database_)->getRelationByIdMutable(proto.relation_id());
-      relation->addBlock(block);
-
-      if (proto.has_partition_id()) {
-        relation->getPartitionSchemeMutable()->addBlockToPartition(
-            proto.partition_id(), block);
-      }
-      return QueryStatusCode::kNone;
-    }
-    case kDataPipelineMessage: {
-      // Possible message senders include InsertDestinations and some
-      // operators which modify existing blocks.
-      serialization::DataPipelineMessage proto;
-      CHECK(proto.ParseFromArray(tagged_message.message(),
-                                 tagged_message.message_bytes()));
-
-      op_index = proto.operator_index();
-      processDataPipelineMessage(proto.operator_index(),
-                                 proto.block_id(),
-                                 proto.relation_id());
-      break;
-    }
-    case kWorkOrdersAvailableMessage: {
-      serialization::WorkOrdersAvailableMessage proto;
-      CHECK(proto.ParseFromArray(tagged_message.message(),
-                                 tagged_message.message_bytes()));
-
-      op_index = proto.operator_index();
-
-      // Check if new work orders are available.
-      fetchNormalWorkOrders(op_index);
-      break;
-    }
-    case kWorkOrderFeedbackMessage: {
-      WorkOrder::FeedbackMessage msg(
-          const_cast<void *>(tagged_message.message()),
-          tagged_message.message_bytes());
-
-      op_index = msg.header().rel_op_index;
-      processFeedbackMessage(msg);
-      break;
-    }
-    default:
-      LOG(FATAL) << "Unknown message type found in QueryManager";
-  }
-
+QueryManagerBase::QueryStatusCode QueryManagerBase::queryStatus(
+    const dag_node_index op_index) {
   if (query_exec_state_->hasExecutionFinished(op_index)) {
     return QueryStatusCode::kOperatorExecuted;
   }
@@ -165,9 +84,9 @@ QueryManagerBase::QueryStatusCode QueryManagerBase::processMessage(
 }
 
 void QueryManagerBase::processFeedbackMessage(
-    const WorkOrder::FeedbackMessage &msg) {
+    const dag_node_index op_index, const WorkOrder::FeedbackMessage &msg) {
   RelationalOperator *op =
-      query_dag_->getNodePayloadMutable(msg.header().rel_op_index);
+      query_dag_->getNodePayloadMutable(op_index);
   op->receiveFeedbackMessage(msg);
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/db0e7e3c/query_execution/QueryManagerBase.hpp
----------------------------------------------------------------------
diff --git a/query_execution/QueryManagerBase.hpp b/query_execution/QueryManagerBase.hpp
index 9e192c8..6edfd5c 100644
--- a/query_execution/QueryManagerBase.hpp
+++ b/query_execution/QueryManagerBase.hpp
@@ -24,7 +24,6 @@
 
 #include "catalog/CatalogTypedefs.hpp"
 #include "query_execution/QueryExecutionState.hpp"
-#include "query_execution/QueryExecutionTypedefs.hpp"
 #include "relational_operators/RelationalOperator.hpp"
 #include "relational_operators/WorkOrder.hpp"
 #include "storage/StorageBlockInfo.hpp"
@@ -33,7 +32,6 @@
 
 namespace quickstep {
 
-class CatalogDatabaseLite;
 class QueryHandle;
 
 /** \addtogroup QueryExecution
@@ -50,7 +48,7 @@ class QueryManagerBase {
   typedef DAG<RelationalOperator, bool>::size_type_nodes dag_node_index;
 
   /**
-   * @brief Return codes for processMessage() function.
+   * @brief Return codes for queryStatus() function.
    *
    * @note When both operator and query get executed, kQueryExecuted takes
    *       precedence over kOperatorExecuted.
@@ -65,10 +63,8 @@ class QueryManagerBase {
    * @brief Constructor.
    *
    * @param query_handle The QueryHandle object for this query.
-   * @param catalog_database The CatalogDatabse used by the query.
    **/
-  QueryManagerBase(QueryHandle *query_handle,
-                   CatalogDatabaseLite *catalog_database);
+  explicit QueryManagerBase(QueryHandle *query_handle);
 
   /**
    * @brief Virtual destructor.
@@ -76,26 +72,16 @@ class QueryManagerBase {
   virtual ~QueryManagerBase() {}
 
   /**
-   * @brief Process a message sent to the QueryManager.
-   *
-   * @param tagged_message TaggedMessage sent to the QueryManager.
-   *
-   * @return QueryStatusCode as determined after the message is processed.
-   **/
-  QueryStatusCode processMessage(const TaggedMessage &tagged_message);
-
-  /**
    * @brief Get the QueryExecutionState for this query.
    **/
   inline const QueryExecutionState& getQueryExecutionState() const {
     return *query_exec_state_;
   }
 
- protected:
   /**
    * @brief Process the received WorkOrder complete message.
    *
-   * @param node_index The index of the specified operator node in the query DAG
+   * @param op_index The index of the specified operator node in the query DAG
    *        for the completed WorkOrder.
    **/
   void processWorkOrderCompleteMessage(const dag_node_index op_index);
@@ -103,28 +89,15 @@ class QueryManagerBase {
   /**
    * @brief Process the received RebuildWorkOrder complete message.
    *
-   * @param node_index The index of the specified operator node in the query DAG
+   * @param op_index The index of the specified operator node in the query DAG
    *        for the completed RebuildWorkOrder.
    **/
   void processRebuildWorkOrderCompleteMessage(const dag_node_index op_index);
 
   /**
-   * @brief Process a current relational operator: Get its workorders and store
-   *        them in the WorkOrdersContainer for this query. If the operator can
-   *        be marked as done, do so.
-   *
-   * @param index The index of the relational operator to be processed in the
-   *        query plan DAG.
-   * @param recursively_check_dependents If an operator is done, should we
-   *        call processOperator on its dependents recursively.
-   **/
-  void processOperator(const dag_node_index index,
-                       const bool recursively_check_dependents);
-
-  /**
    * @brief Process the received data pipeline message.
    *
-   * @param node_index The index of the specified operator node in the query DAG
+   * @param op_index The index of the specified operator node in the query DAG
    *        for the pipelining block.
    * @param block The block id.
    * @param rel_id The ID of the relation that produced 'block'.
@@ -134,12 +107,50 @@ class QueryManagerBase {
                                   const relation_id rel_id);
 
   /**
+   * @brief Fetch all work orders currently available in relational operator and
+   *        store them internally.
+   *
+   * @param index The index of the relational operator to be processed in the
+   *        query plan DAG.
+   *
+   * @return Whether any work order was generated by op.
+   **/
+  virtual bool fetchNormalWorkOrders(const dag_node_index index) = 0;
+
+  /**
    * @brief Process the received work order feedback message and notify
    *        relational operator.
    *
+   * @param op_index The index of the specified operator node in the query DAG
+   *        for the feedback message.
    * @param message Feedback message from work order.
    **/
-  void processFeedbackMessage(const WorkOrder::FeedbackMessage &message);
+  void processFeedbackMessage(const dag_node_index op_index,
+                              const WorkOrder::FeedbackMessage &message);
+
+  /**
+   * @brief Get the query status after processing an incoming message.
+   *
+   * @param op_index The index of the specified operator node in the query DAG
+   *        for the incoming message.
+   *
+   * @return QueryStatusCode as determined after the message is processed.
+   **/
+  QueryStatusCode queryStatus(const dag_node_index op_index);
+
+ protected:
+  /**
+   * @brief Process a current relational operator: Get its workorders and store
+   *        them in the WorkOrdersContainer for this query. If the operator can
+   *        be marked as done, do so.
+   *
+   * @param index The index of the relational operator to be processed in the
+   *        query plan DAG.
+   * @param recursively_check_dependents If an operator is done, should we
+   *        call processOperator on its dependents recursively.
+   **/
+  void processOperator(const dag_node_index index,
+                       const bool recursively_check_dependents);
 
   /**
    * @brief This function does the following things:
@@ -241,8 +252,6 @@ class QueryManagerBase {
 
   const std::size_t query_id_;
 
-  CatalogDatabaseLite *catalog_database_;
-
   DAG<RelationalOperator, bool> *query_dag_;
   const dag_node_index num_operators_in_dag_;
 
@@ -256,17 +265,6 @@ class QueryManagerBase {
 
  private:
   /**
-   * @brief Fetch all work orders currently available in relational operator and
-   *        store them internally.
-   *
-   * @param index The index of the relational operator to be processed in the
-   *        query plan DAG.
-   *
-   * @return Whether any work order was generated by op.
-   **/
-  virtual bool fetchNormalWorkOrders(const dag_node_index index) = 0;
-
-  /**
    * @brief Check if the given operator's normal execution is over.
    *
    * @note The conditions for a given operator's normal execution to get over:

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/db0e7e3c/query_execution/QueryManagerSingleNode.cpp
----------------------------------------------------------------------
diff --git a/query_execution/QueryManagerSingleNode.cpp b/query_execution/QueryManagerSingleNode.cpp
index 193b188..12f8ff5 100644
--- a/query_execution/QueryManagerSingleNode.cpp
+++ b/query_execution/QueryManagerSingleNode.cpp
@@ -46,12 +46,12 @@ QueryManagerSingleNode::QueryManagerSingleNode(
     CatalogDatabaseLite *catalog_database,
     StorageManager *storage_manager,
     tmb::MessageBus *bus)
-    : QueryManagerBase(query_handle, catalog_database),
+    : QueryManagerBase(query_handle),
       foreman_client_id_(foreman_client_id),
       storage_manager_(DCHECK_NOTNULL(storage_manager)),
       bus_(DCHECK_NOTNULL(bus)),
       query_context_(new QueryContext(query_handle->getQueryContextProto(),
-                                      *catalog_database_,
+                                      *catalog_database,
                                       storage_manager_,
                                       foreman_client_id_,
                                       bus_)),

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/db0e7e3c/query_execution/QueryManagerSingleNode.hpp
----------------------------------------------------------------------
diff --git a/query_execution/QueryManagerSingleNode.hpp b/query_execution/QueryManagerSingleNode.hpp
index 5533f06..b9bad2e 100644
--- a/query_execution/QueryManagerSingleNode.hpp
+++ b/query_execution/QueryManagerSingleNode.hpp
@@ -68,6 +68,8 @@ class QueryManagerSingleNode final : public QueryManagerBase {
 
   ~QueryManagerSingleNode() override {}
 
+  bool fetchNormalWorkOrders(const dag_node_index index) override;
+
  /**
    * @brief Get the next workorder to be excuted, wrapped in a WorkerMessage.
    *
@@ -91,8 +93,6 @@ class QueryManagerSingleNode final : public QueryManagerBase {
   }
 
  private:
-  bool fetchNormalWorkOrders(const dag_node_index index) override;
-
   bool checkNormalExecutionOver(const dag_node_index index) const override {
     return (checkAllDependenciesMet(index) &&
             !workorders_container_->hasNormalWorkOrder(index) &&

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/db0e7e3c/query_execution/tests/QueryManagerSingleNode_unittest.cpp
----------------------------------------------------------------------
diff --git a/query_execution/tests/QueryManagerSingleNode_unittest.cpp b/query_execution/tests/QueryManagerSingleNode_unittest.cpp
index 52cee20..39ca58c 100644
--- a/query_execution/tests/QueryManagerSingleNode_unittest.cpp
+++ b/query_execution/tests/QueryManagerSingleNode_unittest.cpp
@@ -16,7 +16,6 @@
  **/
 
 #include <climits>
-#include <cstdlib>
 #include <memory>
 #include <utility>
 #include <vector>
@@ -26,7 +25,6 @@
 #include "catalog/CatalogTypedefs.hpp"
 #include "query_execution/QueryContext.hpp"
 #include "query_execution/QueryContext.pb.h"
-#include "query_execution/QueryExecutionMessages.pb.h"
 #include "query_execution/QueryExecutionState.hpp"
 #include "query_execution/QueryExecutionTypedefs.hpp"
 #include "query_execution/QueryManagerSingleNode.hpp"
@@ -49,7 +47,6 @@
 #include "gtest/gtest.h"
 
 #include "tmb/id_typedefs.h"
-#include "tmb/tagged_message.h"
 
 namespace tmb { class MessageBus; }
 
@@ -254,89 +251,33 @@ class QueryManagerTest : public ::testing::Test {
 
   inline bool placeDataPipelineMessage(const QueryPlan::DAGNodeIndex source_operator_index) {
     VLOG(3) << "Place DataPipeline message for Op[" << source_operator_index << "]";
-    serialization::DataPipelineMessage proto;
-    proto.set_operator_index(source_operator_index);
-
-    proto.set_block_id(0);  // dummy block ID
-    proto.set_relation_id(0);  // dummy relation ID.
-    proto.set_query_id(0);  // dummy query ID.
-
-    // NOTE(zuyu): Using the heap memory to serialize proto as a c-like string.
-    const std::size_t proto_length = proto.ByteSize();
-    char *proto_bytes = static_cast<char*>(std::malloc(proto_length));
-    CHECK(proto.SerializeToArray(proto_bytes, proto_length));
-
-    tmb::TaggedMessage tagged_message(static_cast<const void *>(proto_bytes),
-                                      proto_length,
-                                      kDataPipelineMessage);
-    std::free(proto_bytes);
-    query_manager_->processMessage(tagged_message);
+
+    query_manager_->processDataPipelineMessage(source_operator_index,
+                                               0 /* dummy block ID */,
+                                               0 /* dummy relation ID */);
     return query_manager_->getQueryExecutionState().hasQueryExecutionFinished();
   }
 
   inline bool placeWorkOrderCompleteMessage(const QueryPlan::DAGNodeIndex index) {
     VLOG(3) << "Place WorkOrderComplete message for Op[" << index << "]";
-    TaggedMessage tagged_message;
-    serialization::NormalWorkOrderCompletionMessage proto;
-    proto.set_operator_index(index);
-    proto.set_worker_thread_index(1);  // dummy worker ID.
-    proto.set_query_id(0);  // dummy query ID.
-
-    // NOTE(zuyu): Using the heap memory to serialize proto as a c-like string.
-    const size_t proto_length = proto.ByteSize();
-    char *proto_bytes = static_cast<char*>(std::malloc(proto_length));
-    CHECK(proto.SerializeToArray(proto_bytes, proto_length));
-
-    TaggedMessage message(static_cast<const void*>(proto_bytes),
-                          proto_length,
-                          kWorkOrderCompleteMessage);
-    std::free(proto_bytes);
-    query_manager_->processMessage(message);
 
+    query_manager_->processWorkOrderCompleteMessage(index);
     return query_manager_->getQueryExecutionState().hasQueryExecutionFinished();
   }
 
   inline bool placeRebuildWorkOrderCompleteMessage(const QueryPlan::DAGNodeIndex index) {
     VLOG(3) << "Place RebuildWorkOrderComplete message for Op[" << index << "]";
-    serialization::RebuildWorkOrderCompletionMessage proto;
-    proto.set_operator_index(index);
-    proto.set_worker_thread_index(1);  // dummy worker thread ID.
-    proto.set_query_id(0);  // dummy query ID.
-
-    // NOTE(zuyu): Using the heap memory to serialize proto as a c-like string.
-    const size_t proto_length = proto.ByteSize();
-    char *proto_bytes = static_cast<char*>(std::malloc(proto_length));
-    CHECK(proto.SerializeToArray(proto_bytes, proto_length));
-
-    TaggedMessage message(static_cast<const void*>(proto_bytes),
-                          proto_length,
-                          kRebuildWorkOrderCompleteMessage);
-
-    std::free(proto_bytes);
-    query_manager_->processMessage(message);
 
+    query_manager_->processRebuildWorkOrderCompleteMessage(index);
     return query_manager_->getQueryExecutionState().hasQueryExecutionFinished();
   }
 
   inline bool placeOutputBlockMessage(const QueryPlan::DAGNodeIndex index) {
     VLOG(3) << "Place OutputBlock message for Op[" << index << "]";
-    serialization::DataPipelineMessage proto;
-    proto.set_operator_index(index);
-
-    proto.set_block_id(0);  // dummy block ID
-    proto.set_relation_id(0);  // dummy relation ID.
-    proto.set_query_id(0);  // dummy query ID.
-
-    // NOTE(zuyu): Using the heap memory to serialize proto as a c-like string.
-    const std::size_t proto_length = proto.ByteSize();
-    char *proto_bytes = static_cast<char*>(std::malloc(proto_length));
-    CHECK(proto.SerializeToArray(proto_bytes, proto_length));
-
-    tmb::TaggedMessage tagged_message(static_cast<const void *>(proto_bytes),
-                                      proto_length,
-                                      kDataPipelineMessage);
-    std::free(proto_bytes);
-    query_manager_->processMessage(tagged_message);
+
+    query_manager_->processDataPipelineMessage(index,
+                                               0 /* dummy block ID */,
+                                               0 /* dummy relation ID */);
     return query_manager_->getQueryExecutionState().hasQueryExecutionFinished();
   }
 


[30/30] incubator-quickstep git commit: Fixed 4 failures on unit tests

Posted by ra...@apache.org.
Fixed 4 failures on unit tests


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

Branch: refs/heads/quickstep-28-29
Commit: d9c9e6867d3cbd6ed6209e944a2b7d91748e40d3
Parents: 39485d6
Author: rathijit <ra...@node-2.aggregation.quickstep-pg0.wisc.cloudlab.us>
Authored: Fri Aug 5 06:00:12 2016 -0500
Committer: rathijit <ra...@node-2.aggregation.quickstep-pg0.wisc.cloudlab.us>
Committed: Fri Aug 5 14:23:35 2016 -0500

----------------------------------------------------------------------
 .../aggregation/AggregationConcreteHandle.cpp   | 14 +++---
 .../aggregation/AggregationConcreteHandle.hpp   | 41 ++++++++++++++--
 expressions/aggregation/AggregationHandle.hpp   |  6 ++-
 .../aggregation/AggregationHandleAvg.cpp        | 14 +++---
 .../aggregation/AggregationHandleAvg.hpp        | 15 +++++-
 .../aggregation/AggregationHandleCount.cpp      |  7 +--
 .../aggregation/AggregationHandleCount.hpp      | 19 ++++++--
 .../aggregation/AggregationHandleDistinct.cpp   |  2 +-
 .../aggregation/AggregationHandleDistinct.hpp   |  2 +-
 .../aggregation/AggregationHandleMax.cpp        | 14 +++---
 .../aggregation/AggregationHandleMax.hpp        | 13 ++++-
 .../aggregation/AggregationHandleMin.cpp        | 14 +++---
 .../aggregation/AggregationHandleMin.hpp        | 15 +++++-
 .../aggregation/AggregationHandleSum.cpp        | 15 +++---
 .../aggregation/AggregationHandleSum.hpp        | 15 +++++-
 storage/AggregationOperationState.cpp           | 51 +++++++++++---------
 storage/CMakeLists.txt                          |  1 -
 storage/FastHashTable.hpp                       | 41 +++++++++++++++-
 storage/FastSeparateChainingHashTable.hpp       | 16 +++---
 19 files changed, 221 insertions(+), 94 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d9c9e686/expressions/aggregation/AggregationConcreteHandle.cpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationConcreteHandle.cpp b/expressions/aggregation/AggregationConcreteHandle.cpp
index d808302..9bc8fc8 100644
--- a/expressions/aggregation/AggregationConcreteHandle.cpp
+++ b/expressions/aggregation/AggregationConcreteHandle.cpp
@@ -50,17 +50,17 @@ void AggregationConcreteHandle::insertValueAccessorIntoDistinctifyHashTable(
     AggregationStateHashTableBase *distinctify_hash_table) const {
   // If the key-value pair is already there, we don't need to update the value,
   // which should always be "true". I.e. the value is just a placeholder.
-//  const auto noop_upserter = [](const auto &accessor, const bool *value) -> void {};
+  //  const auto noop_upserter = [](const auto &accessor, const bool *value) -> void {};
 
   AggregationStateFastHashTable *hash_table =
       static_cast<AggregationStateFastHashTable *>(distinctify_hash_table);
   if (key_ids.size() == 1) {
-// TODO(rathijit): fix
-//    hash_table->upsertValueAccessor(accessor,
-//                                    key_ids[0],
-//                                    true /* check_for_null_keys */,
-//                                    true /* initial_value */,
-//                                    &noop_upserter);
+    std::vector<std::vector<attribute_id>> args;
+    args.emplace_back(key_ids);
+    hash_table->upsertValueAccessorFast(args,
+                                    accessor,
+                                    key_ids[0],
+                                    true /* check_for_null_keys */);
   } else {
     std::vector<std::vector<attribute_id>> empty_args;
     empty_args.resize(1);

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d9c9e686/expressions/aggregation/AggregationConcreteHandle.hpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationConcreteHandle.hpp b/expressions/aggregation/AggregationConcreteHandle.hpp
index c5d7b3c..df02721 100644
--- a/expressions/aggregation/AggregationConcreteHandle.hpp
+++ b/expressions/aggregation/AggregationConcreteHandle.hpp
@@ -27,6 +27,7 @@
 #include "catalog/CatalogTypedefs.hpp"
 #include "expressions/aggregation/AggregationHandle.hpp"
 #include "storage/HashTable.hpp"
+#include "storage/FastHashTable.hpp"
 #include "storage/HashTableBase.hpp"
 #include "types/TypedValue.hpp"
 #include "types/containers/ColumnVector.hpp"
@@ -278,6 +279,11 @@ class AggregationConcreteHandle : public AggregationHandle {
       const AggregationStateHashTableBase &distinctify_hash_table) const;
 
   template <typename HandleT,
+            typename StateT>
+  StateT* aggregateOnDistinctifyHashTableForSingleUnaryHelperFast(
+      const AggregationStateHashTableBase &distinctify_hash_table) const;
+
+  template <typename HandleT,
             typename StateT,
             typename HashTableT>
   void aggregateOnDistinctifyHashTableForGroupByUnaryHelper(
@@ -289,7 +295,7 @@ class AggregationConcreteHandle : public AggregationHandle {
             typename HashTableT>
   void aggregateOnDistinctifyHashTableForGroupByUnaryHelperFast(
       const AggregationStateHashTableBase &distinctify_hash_table,
-      AggregationStateHashTableBase *hash_table) const;
+      AggregationStateHashTableBase *hash_table, int index) const;
 
 
   template <typename HandleT,
@@ -494,6 +500,31 @@ StateT* AggregationConcreteHandle::aggregateOnDistinctifyHashTableForSingleUnary
 }
 
 template <typename HandleT,
+          typename StateT>
+StateT* AggregationConcreteHandle::aggregateOnDistinctifyHashTableForSingleUnaryHelperFast(
+    const AggregationStateHashTableBase &distinctify_hash_table) const {
+  const HandleT& handle = static_cast<const HandleT&>(*this);
+  StateT *state = static_cast<StateT*>(createInitialState());
+
+  // A lambda function which will be called on each key from the distinctify
+  // hash table.
+  const auto aggregate_functor = [&handle, &state](const TypedValue &key,
+                                                   const std::uint8_t &dumb_placeholder) {
+    // For each (unary) key in the distinctify hash table, aggregate the key
+    // into "state".
+    handle.iterateUnaryInl(state, key);
+  };
+
+  const AggregationStateFastHashTable &hash_table =
+      static_cast<const AggregationStateFastHashTable &>(distinctify_hash_table);
+  // Invoke the lambda function "aggregate_functor" on each key from the distinctify
+  // hash table.
+  hash_table.forEach(&aggregate_functor);
+
+  return state;
+}
+
+template <typename HandleT,
           typename StateT,
           typename HashTableT>
 void AggregationConcreteHandle::aggregateOnDistinctifyHashTableForGroupByUnaryHelper(
@@ -534,13 +565,13 @@ template <typename HandleT,
           typename HashTableT>
 void AggregationConcreteHandle::aggregateOnDistinctifyHashTableForGroupByUnaryHelperFast(
     const AggregationStateHashTableBase &distinctify_hash_table,
-    AggregationStateHashTableBase *aggregation_hash_table) const {
+    AggregationStateHashTableBase *aggregation_hash_table, int index) const {
   const HandleT& handle = static_cast<const HandleT&>(*this);
   HashTableT *target_hash_table = static_cast<HashTableT*>(aggregation_hash_table);
 
   // A lambda function which will be called on each key-value pair from the
   // distinctify hash table.
-  const auto aggregate_functor = [&handle, &target_hash_table](
+  const auto aggregate_functor = [&handle, &target_hash_table, &index](
       std::vector<TypedValue> &key,
       const bool &dumb_placeholder) {
     // For each (composite) key vector in the distinctify hash table with size N.
@@ -552,10 +583,10 @@ void AggregationConcreteHandle::aggregateOnDistinctifyHashTableForGroupByUnaryHe
     // An upserter as lambda function for aggregating the argument into its
     // GROUP BY group's entry inside aggregation_hash_table.
     const auto upserter = [&handle, &argument](std::uint8_t *state) {
-      handle.iterateUnaryInlFast(argument, state+sizeof(SpinMutex));
+      handle.iterateUnaryInlFast(argument, state);
     };
 
-    target_hash_table->upsertCompositeKeyFast(key, nullptr, &upserter);
+    target_hash_table->upsertCompositeKeyFast(key, nullptr, &upserter, index);
   };
 
   const HashTableT &source_hash_table =

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d9c9e686/expressions/aggregation/AggregationHandle.hpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandle.hpp b/expressions/aggregation/AggregationHandle.hpp
index d9c3897..368b94e 100644
--- a/expressions/aggregation/AggregationHandle.hpp
+++ b/expressions/aggregation/AggregationHandle.hpp
@@ -347,7 +347,7 @@ class AggregationHandle {
    */
   virtual void aggregateOnDistinctifyHashTableForGroupBy(
       const AggregationStateHashTableBase &distinctify_hash_table,
-      AggregationStateHashTableBase *aggregation_hash_table) const = 0;
+      AggregationStateHashTableBase *aggregation_hash_table, int index) const = 0;
 
   /**
    * @brief Merge two GROUP BY hash tables in one.
@@ -362,11 +362,13 @@ class AggregationHandle {
       const AggregationStateHashTableBase &source_hash_table,
       AggregationStateHashTableBase *destination_hash_table) const = 0;
 
-  virtual size_t getPayloadSize() const {return 8;}
+  virtual size_t getPayloadSize() const {return 1;}
   virtual void setPayloadOffset(std::size_t) {}
   virtual void iterateInlFast(const std::vector<TypedValue> &arguments, uint8_t *byte_ptr) {}
   virtual void mergeStatesFast(const uint8_t *src, uint8_t *dst) const {}
   virtual void initPayload(uint8_t *byte_ptr) {}
+  virtual void BlockUpdate() {}
+  virtual void AllowUpdate() {}
 
  protected:
   AggregationHandle() {

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d9c9e686/expressions/aggregation/AggregationHandleAvg.cpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleAvg.cpp b/expressions/aggregation/AggregationHandleAvg.cpp
index d0bd3b8..93adf55 100644
--- a/expressions/aggregation/AggregationHandleAvg.cpp
+++ b/expressions/aggregation/AggregationHandleAvg.cpp
@@ -42,7 +42,7 @@ namespace quickstep {
 class StorageManager;
 
 AggregationHandleAvg::AggregationHandleAvg(const Type &type)
-    : argument_type_(type) {
+    : argument_type_(type), block_update(false) {
   // We sum Int as Long and Float as Double so that we have more headroom when
   // adding many values.
   TypeID type_precision_id;
@@ -206,7 +206,7 @@ ColumnVector* AggregationHandleAvg::finalizeHashTable(
 
 AggregationState* AggregationHandleAvg::aggregateOnDistinctifyHashTableForSingle(
     const AggregationStateHashTableBase &distinctify_hash_table) const {
-  return aggregateOnDistinctifyHashTableForSingleUnaryHelper<
+  return aggregateOnDistinctifyHashTableForSingleUnaryHelperFast<
       AggregationHandleAvg,
       AggregationStateAvg>(
           distinctify_hash_table);
@@ -214,14 +214,12 @@ AggregationState* AggregationHandleAvg::aggregateOnDistinctifyHashTableForSingle
 
 void AggregationHandleAvg::aggregateOnDistinctifyHashTableForGroupBy(
     const AggregationStateHashTableBase &distinctify_hash_table,
-    AggregationStateHashTableBase *aggregation_hash_table) const {
-  aggregateOnDistinctifyHashTableForGroupByUnaryHelper<
+    AggregationStateHashTableBase *aggregation_hash_table, int index) const {
+  aggregateOnDistinctifyHashTableForGroupByUnaryHelperFast<
       AggregationHandleAvg,
-      AggregationStateAvg,
-      AggregationStateHashTable<AggregationStateAvg>>(
+      AggregationStateFastHashTable>(
           distinctify_hash_table,
-          blank_state_,
-          aggregation_hash_table);
+          aggregation_hash_table, index);
 }
 
 void AggregationHandleAvg::mergeGroupByHashTables(

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d9c9e686/expressions/aggregation/AggregationHandleAvg.hpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleAvg.hpp b/expressions/aggregation/AggregationHandleAvg.hpp
index fe178f6..3de0f2b 100644
--- a/expressions/aggregation/AggregationHandleAvg.hpp
+++ b/expressions/aggregation/AggregationHandleAvg.hpp
@@ -123,7 +123,7 @@ class AggregationHandleAvg : public AggregationConcreteHandle {
     ++state->count_;
   }
 
-  inline void iterateUnaryInlFast(const TypedValue &value, uint8_t *byte_ptr) {
+  inline void iterateUnaryInlFast(const TypedValue &value, uint8_t *byte_ptr) const {
     DCHECK(value.isPlausibleInstanceOf(argument_type_.getSignature()));
     if (value.isNull()) return;
     TypedValue *sum_ptr = reinterpret_cast<TypedValue *>(byte_ptr + blank_state_.sum_offset);
@@ -133,9 +133,18 @@ class AggregationHandleAvg : public AggregationConcreteHandle {
   }
 
   inline void iterateInlFast(const std::vector<TypedValue> &arguments, uint8_t *byte_ptr) override {
+     if (block_update) return;
      iterateUnaryInlFast(arguments.front(), byte_ptr);
   }
 
+  void BlockUpdate() override {
+      block_update = true;
+  }
+
+  void AllowUpdate() override {
+      block_update = false;
+  }
+
   void initPayload(uint8_t *byte_ptr) override {
     TypedValue *sum_ptr = reinterpret_cast<TypedValue *>(byte_ptr + blank_state_.sum_offset);
     std::int64_t *count_ptr = reinterpret_cast<std::int64_t *>(byte_ptr + blank_state_.count_offset);
@@ -208,7 +217,7 @@ class AggregationHandleAvg : public AggregationConcreteHandle {
    */
   void aggregateOnDistinctifyHashTableForGroupBy(
       const AggregationStateHashTableBase &distinctify_hash_table,
-      AggregationStateHashTableBase *aggregation_hash_table) const override;
+      AggregationStateHashTableBase *aggregation_hash_table, int index) const override;
 
   void mergeGroupByHashTables(
       const AggregationStateHashTableBase &source_hash_table,
@@ -235,6 +244,8 @@ class AggregationHandleAvg : public AggregationConcreteHandle {
   std::unique_ptr<UncheckedBinaryOperator> merge_add_operator_;
   std::unique_ptr<UncheckedBinaryOperator> divide_operator_;
 
+  bool block_update;
+
   DISALLOW_COPY_AND_ASSIGN(AggregationHandleAvg);
 };
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d9c9e686/expressions/aggregation/AggregationHandleCount.cpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleCount.cpp b/expressions/aggregation/AggregationHandleCount.cpp
index dc11dac..a1d5388 100644
--- a/expressions/aggregation/AggregationHandleCount.cpp
+++ b/expressions/aggregation/AggregationHandleCount.cpp
@@ -196,7 +196,7 @@ AggregationState* AggregationHandleCount<count_star, nullable_type>
     ::aggregateOnDistinctifyHashTableForSingle(
         const AggregationStateHashTableBase &distinctify_hash_table) const {
   DCHECK_EQ(count_star, false);
-  return aggregateOnDistinctifyHashTableForSingleUnaryHelper<
+  return aggregateOnDistinctifyHashTableForSingleUnaryHelperFast<
       AggregationHandleCount<count_star, nullable_type>,
       AggregationStateCount>(
           distinctify_hash_table);
@@ -206,13 +206,14 @@ template <bool count_star, bool nullable_type>
 void AggregationHandleCount<count_star, nullable_type>
     ::aggregateOnDistinctifyHashTableForGroupBy(
         const AggregationStateHashTableBase &distinctify_hash_table,
-        AggregationStateHashTableBase *aggregation_hash_table) const {
+        AggregationStateHashTableBase *aggregation_hash_table, int index) const {
   DCHECK_EQ(count_star, false);
   aggregateOnDistinctifyHashTableForGroupByUnaryHelperFast<
       AggregationHandleCount<count_star, nullable_type>,
       AggregationStateFastHashTable>(
           distinctify_hash_table,
-          aggregation_hash_table);
+          aggregation_hash_table,
+          index);
 }
 
 template <bool count_star, bool nullable_type>

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d9c9e686/expressions/aggregation/AggregationHandleCount.hpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleCount.hpp b/expressions/aggregation/AggregationHandleCount.hpp
index 4046106..64d03a3 100644
--- a/expressions/aggregation/AggregationHandleCount.hpp
+++ b/expressions/aggregation/AggregationHandleCount.hpp
@@ -135,15 +135,24 @@ class AggregationHandleCount : public AggregationConcreteHandle {
   }
 
   inline void iterateInlFast(const std::vector<TypedValue> &arguments, uint8_t *byte_ptr) override {
+     if (block_update) return;
      if (arguments.size())
          iterateUnaryInlFast(arguments.front(), byte_ptr);
      else
          iterateNullaryInlFast(byte_ptr);
   }
 
+  void BlockUpdate() override {
+     block_update = true;
+  }
+
+  void AllowUpdate() override {
+     block_update = false;
+  }
+
   void initPayload(uint8_t *byte_ptr) override {
-    std::int64_t *count_ptr = reinterpret_cast<std::int64_t *>(byte_ptr);
-    *count_ptr = 0;
+     std::int64_t *count_ptr = reinterpret_cast<std::int64_t *>(byte_ptr);
+     *count_ptr = 0;
   }
 
   AggregationState* accumulateNullary(const std::size_t num_tuples) const override {
@@ -208,7 +217,7 @@ class AggregationHandleCount : public AggregationConcreteHandle {
    */
   void aggregateOnDistinctifyHashTableForGroupBy(
       const AggregationStateHashTableBase &distinctify_hash_table,
-      AggregationStateHashTableBase *aggregation_hash_table) const override;
+      AggregationStateHashTableBase *aggregation_hash_table, int index) const override;
 
   void mergeGroupByHashTables(
       const AggregationStateHashTableBase &source_hash_table,
@@ -224,9 +233,11 @@ class AggregationHandleCount : public AggregationConcreteHandle {
   /**
    * @brief Constructor.
    **/
-  AggregationHandleCount() {
+  AggregationHandleCount() : block_update(false) {
   }
 
+  bool block_update;
+
   DISALLOW_COPY_AND_ASSIGN(AggregationHandleCount);
 };
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d9c9e686/expressions/aggregation/AggregationHandleDistinct.cpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleDistinct.cpp b/expressions/aggregation/AggregationHandleDistinct.cpp
index 0088239..cfcedcf 100644
--- a/expressions/aggregation/AggregationHandleDistinct.cpp
+++ b/expressions/aggregation/AggregationHandleDistinct.cpp
@@ -70,7 +70,7 @@ ColumnVector* AggregationHandleDistinct::finalizeHashTable(
                                                const bool &dumb_placeholder) -> void {
     group_by_keys->emplace_back(std::move(group_by_key));
   };
-  static_cast<const AggregationStateHashTable<bool>&>(hash_table).forEachCompositeKey(&keys_retriever);
+  static_cast<const AggregationStateFastHashTable&>(hash_table).forEachCompositeKey(&keys_retriever);
 
   return nullptr;
 }

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d9c9e686/expressions/aggregation/AggregationHandleDistinct.hpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleDistinct.hpp b/expressions/aggregation/AggregationHandleDistinct.hpp
index 1e36845..2947d12 100644
--- a/expressions/aggregation/AggregationHandleDistinct.hpp
+++ b/expressions/aggregation/AggregationHandleDistinct.hpp
@@ -88,7 +88,7 @@ class AggregationHandleDistinct : public AggregationConcreteHandle {
 
   void aggregateOnDistinctifyHashTableForGroupBy(
       const AggregationStateHashTableBase &distinctify_hash_table,
-      AggregationStateHashTableBase *groupby_hash_table) const override {
+      AggregationStateHashTableBase *groupby_hash_table, int index) const override {
     LOG(FATAL) << "AggregationHandleDistinct does not support "
                << "aggregateOnDistinctifyHashTableForGroupBy().";
   }

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d9c9e686/expressions/aggregation/AggregationHandleMax.cpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleMax.cpp b/expressions/aggregation/AggregationHandleMax.cpp
index f0eebde..ef44742 100644
--- a/expressions/aggregation/AggregationHandleMax.cpp
+++ b/expressions/aggregation/AggregationHandleMax.cpp
@@ -37,7 +37,7 @@ namespace quickstep {
 class StorageManager;
 
 AggregationHandleMax::AggregationHandleMax(const Type &type)
-    : type_(type) {
+    : type_(type), block_update(false) {
   fast_comparator_.reset(ComparisonFactory::GetComparison(ComparisonID::kGreater)
                          .makeUncheckedComparatorForTypes(type,
                                                           type.getNonNullableVersion()));
@@ -133,7 +133,7 @@ ColumnVector* AggregationHandleMax::finalizeHashTable(
 
 AggregationState* AggregationHandleMax::aggregateOnDistinctifyHashTableForSingle(
     const AggregationStateHashTableBase &distinctify_hash_table) const {
-  return aggregateOnDistinctifyHashTableForSingleUnaryHelper<
+  return aggregateOnDistinctifyHashTableForSingleUnaryHelperFast<
       AggregationHandleMax,
       AggregationStateMax>(
           distinctify_hash_table);
@@ -141,14 +141,12 @@ AggregationState* AggregationHandleMax::aggregateOnDistinctifyHashTableForSingle
 
 void AggregationHandleMax::aggregateOnDistinctifyHashTableForGroupBy(
     const AggregationStateHashTableBase &distinctify_hash_table,
-    AggregationStateHashTableBase *aggregation_hash_table) const {
-  aggregateOnDistinctifyHashTableForGroupByUnaryHelper<
+    AggregationStateHashTableBase *aggregation_hash_table, int index) const {
+  aggregateOnDistinctifyHashTableForGroupByUnaryHelperFast<
       AggregationHandleMax,
-      AggregationStateMax,
-      AggregationStateHashTable<AggregationStateMax>>(
+      AggregationStateFastHashTable>(
           distinctify_hash_table,
-          AggregationStateMax(type_),
-          aggregation_hash_table);
+          aggregation_hash_table, index);
 }
 
 void AggregationHandleMax::mergeGroupByHashTables(

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d9c9e686/expressions/aggregation/AggregationHandleMax.hpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleMax.hpp b/expressions/aggregation/AggregationHandleMax.hpp
index ce7c702..d82b9a8 100644
--- a/expressions/aggregation/AggregationHandleMax.hpp
+++ b/expressions/aggregation/AggregationHandleMax.hpp
@@ -113,9 +113,18 @@ class AggregationHandleMax : public AggregationConcreteHandle {
   }
 
   inline void iterateInlFast(const std::vector<TypedValue> &arguments, uint8_t *byte_ptr) override {
+    if (block_update) return;
     iterateUnaryInlFast(arguments.front(), byte_ptr);
   }
 
+  void BlockUpdate() override {
+      block_update = true;
+  }
+
+  void AllowUpdate() override {
+      block_update = false;
+  }
+
   void initPayload(uint8_t *byte_ptr) override {
     TypedValue *max_ptr = reinterpret_cast<TypedValue *>(byte_ptr);
     TypedValue t1 = (type_.getNullableVersion().makeNullValue());
@@ -175,7 +184,7 @@ class AggregationHandleMax : public AggregationConcreteHandle {
    */
   void aggregateOnDistinctifyHashTableForGroupBy(
       const AggregationStateHashTableBase &distinctify_hash_table,
-      AggregationStateHashTableBase *aggregation_hash_table) const override;
+      AggregationStateHashTableBase *aggregation_hash_table, int index) const override;
 
   void mergeGroupByHashTables(
       const AggregationStateHashTableBase &source_hash_table,
@@ -221,6 +230,8 @@ class AggregationHandleMax : public AggregationConcreteHandle {
   const Type &type_;
   std::unique_ptr<UncheckedComparator> fast_comparator_;
 
+  bool block_update;
+
   DISALLOW_COPY_AND_ASSIGN(AggregationHandleMax);
 };
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d9c9e686/expressions/aggregation/AggregationHandleMin.cpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleMin.cpp b/expressions/aggregation/AggregationHandleMin.cpp
index cbedd9b..b6e46f4 100644
--- a/expressions/aggregation/AggregationHandleMin.cpp
+++ b/expressions/aggregation/AggregationHandleMin.cpp
@@ -39,7 +39,7 @@ namespace quickstep {
 class StorageManager;
 
 AggregationHandleMin::AggregationHandleMin(const Type &type)
-    : type_(type) {
+    : type_(type), block_update(false) {
   fast_comparator_.reset(ComparisonFactory::GetComparison(ComparisonID::kLess)
                          .makeUncheckedComparatorForTypes(type,
                                                           type.getNonNullableVersion()));
@@ -136,7 +136,7 @@ ColumnVector* AggregationHandleMin::finalizeHashTable(
 
 AggregationState* AggregationHandleMin::aggregateOnDistinctifyHashTableForSingle(
     const AggregationStateHashTableBase &distinctify_hash_table) const {
-  return aggregateOnDistinctifyHashTableForSingleUnaryHelper<
+  return aggregateOnDistinctifyHashTableForSingleUnaryHelperFast<
       AggregationHandleMin,
       AggregationStateMin>(
           distinctify_hash_table);
@@ -144,14 +144,12 @@ AggregationState* AggregationHandleMin::aggregateOnDistinctifyHashTableForSingle
 
 void AggregationHandleMin::aggregateOnDistinctifyHashTableForGroupBy(
     const AggregationStateHashTableBase &distinctify_hash_table,
-    AggregationStateHashTableBase *aggregation_hash_table) const {
-  aggregateOnDistinctifyHashTableForGroupByUnaryHelper<
+    AggregationStateHashTableBase *aggregation_hash_table, int index) const {
+  aggregateOnDistinctifyHashTableForGroupByUnaryHelperFast<
       AggregationHandleMin,
-      AggregationStateMin,
-      AggregationStateHashTable<AggregationStateMin>>(
+      AggregationStateFastHashTable>(
           distinctify_hash_table,
-          AggregationStateMin(type_),
-          aggregation_hash_table);
+          aggregation_hash_table, index);
 }
 
 void AggregationHandleMin::mergeGroupByHashTables(

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d9c9e686/expressions/aggregation/AggregationHandleMin.hpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleMin.hpp b/expressions/aggregation/AggregationHandleMin.hpp
index 7c7869e..adf79d7 100644
--- a/expressions/aggregation/AggregationHandleMin.hpp
+++ b/expressions/aggregation/AggregationHandleMin.hpp
@@ -110,16 +110,25 @@ class AggregationHandleMin : public AggregationConcreteHandle {
     compareAndUpdate(state, value);
   }
 
-  inline void iterateUnaryInlFast(const TypedValue &value, uint8_t *byte_ptr) {
+  inline void iterateUnaryInlFast(const TypedValue &value, uint8_t *byte_ptr) const {
       DCHECK(value.isPlausibleInstanceOf(type_.getSignature()));
       TypedValue *min_ptr = reinterpret_cast<TypedValue *>(byte_ptr);
       compareAndUpdateFast(min_ptr, value);
   }
 
   inline void iterateInlFast(const std::vector<TypedValue> &arguments, uint8_t *byte_ptr) override {
+    if (block_update) return;
     iterateUnaryInlFast(arguments.front(), byte_ptr);
   }
 
+  void BlockUpdate() override {
+      block_update = true;
+  }
+
+  void AllowUpdate() override {
+      block_update = false;
+  }
+
   void initPayload(uint8_t *byte_ptr) override {
     TypedValue *min_ptr = reinterpret_cast<TypedValue *>(byte_ptr);
     TypedValue t1 = (type_.getNullableVersion().makeNullValue());
@@ -178,7 +187,7 @@ class AggregationHandleMin : public AggregationConcreteHandle {
    */
   void aggregateOnDistinctifyHashTableForGroupBy(
       const AggregationStateHashTableBase &distinctify_hash_table,
-      AggregationStateHashTableBase *aggregation_hash_table) const override;
+      AggregationStateHashTableBase *aggregation_hash_table, int index) const override;
 
   void mergeGroupByHashTables(
       const AggregationStateHashTableBase &source_hash_table,
@@ -223,6 +232,8 @@ class AggregationHandleMin : public AggregationConcreteHandle {
   const Type &type_;
   std::unique_ptr<UncheckedComparator> fast_comparator_;
 
+  bool block_update;
+
   DISALLOW_COPY_AND_ASSIGN(AggregationHandleMin);
 };
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d9c9e686/expressions/aggregation/AggregationHandleSum.cpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleSum.cpp b/expressions/aggregation/AggregationHandleSum.cpp
index ad486eb..1b0e355 100644
--- a/expressions/aggregation/AggregationHandleSum.cpp
+++ b/expressions/aggregation/AggregationHandleSum.cpp
@@ -43,7 +43,7 @@ namespace quickstep {
 class StorageManager;
 
 AggregationHandleSum::AggregationHandleSum(const Type &type)
-    : argument_type_(type) {
+    : argument_type_(type), block_update(false) {
   // We sum Int as Long and Float as Double so that we have more headroom when
   // adding many values.
   TypeID type_precision_id;
@@ -184,7 +184,7 @@ ColumnVector* AggregationHandleSum::finalizeHashTable(
 
 AggregationState* AggregationHandleSum::aggregateOnDistinctifyHashTableForSingle(
     const AggregationStateHashTableBase &distinctify_hash_table) const {
-  return aggregateOnDistinctifyHashTableForSingleUnaryHelper<
+  return aggregateOnDistinctifyHashTableForSingleUnaryHelperFast<
       AggregationHandleSum,
       AggregationStateSum>(
           distinctify_hash_table);
@@ -192,14 +192,13 @@ AggregationState* AggregationHandleSum::aggregateOnDistinctifyHashTableForSingle
 
 void AggregationHandleSum::aggregateOnDistinctifyHashTableForGroupBy(
     const AggregationStateHashTableBase &distinctify_hash_table,
-    AggregationStateHashTableBase *aggregation_hash_table) const {
-  aggregateOnDistinctifyHashTableForGroupByUnaryHelper<
+    AggregationStateHashTableBase *aggregation_hash_table, int index) const {
+  aggregateOnDistinctifyHashTableForGroupByUnaryHelperFast<
       AggregationHandleSum,
-      AggregationStateSum,
-      AggregationStateHashTable<AggregationStateSum>>(
+      AggregationStateFastHashTable>(
           distinctify_hash_table,
-          blank_state_,
-          aggregation_hash_table);
+          aggregation_hash_table,
+          index);
 }
 
 void AggregationHandleSum::mergeGroupByHashTables(

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d9c9e686/expressions/aggregation/AggregationHandleSum.hpp
----------------------------------------------------------------------
diff --git a/expressions/aggregation/AggregationHandleSum.hpp b/expressions/aggregation/AggregationHandleSum.hpp
index a8d2b5a..53f43ce 100644
--- a/expressions/aggregation/AggregationHandleSum.hpp
+++ b/expressions/aggregation/AggregationHandleSum.hpp
@@ -118,7 +118,7 @@ class AggregationHandleSum : public AggregationConcreteHandle {
     state->null_ = false;
   }
 
-  inline void iterateUnaryInlFast(const TypedValue &value, uint8_t *byte_ptr) {
+  inline void iterateUnaryInlFast(const TypedValue &value, uint8_t *byte_ptr) const {
     DCHECK(value.isPlausibleInstanceOf(argument_type_.getSignature()));
     if (value.isNull()) return;
     TypedValue *sum_ptr = reinterpret_cast<TypedValue *>(byte_ptr + blank_state_.sum_offset);
@@ -128,9 +128,18 @@ class AggregationHandleSum : public AggregationConcreteHandle {
   }
 
   inline void iterateInlFast(const std::vector<TypedValue> &arguments, uint8_t *byte_ptr) override {
+     if (block_update) return;
      iterateUnaryInlFast(arguments.front(), byte_ptr);
   }
 
+  void BlockUpdate() override {
+      block_update = true;
+  }
+
+  void AllowUpdate() override {
+      block_update = false;
+  }
+
   void initPayload(uint8_t *byte_ptr) override {
     TypedValue *sum_ptr = reinterpret_cast<TypedValue *>(byte_ptr + blank_state_.sum_offset);
     bool *null_ptr = reinterpret_cast<bool *>(byte_ptr + blank_state_.null_offset);
@@ -188,7 +197,7 @@ class AggregationHandleSum : public AggregationConcreteHandle {
    */
   void aggregateOnDistinctifyHashTableForGroupBy(
       const AggregationStateHashTableBase &distinctify_hash_table,
-      AggregationStateHashTableBase *aggregation_hash_table) const override;
+      AggregationStateHashTableBase *aggregation_hash_table, int index) const override;
 
   void mergeGroupByHashTables(
       const AggregationStateHashTableBase &source_hash_table,
@@ -214,6 +223,8 @@ class AggregationHandleSum : public AggregationConcreteHandle {
   std::unique_ptr<UncheckedBinaryOperator> fast_operator_;
   std::unique_ptr<UncheckedBinaryOperator> merge_operator_;
 
+  bool block_update;
+
   DISALLOW_COPY_AND_ASSIGN(AggregationHandleSum);
 };
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d9c9e686/storage/AggregationOperationState.cpp
----------------------------------------------------------------------
diff --git a/storage/AggregationOperationState.cpp b/storage/AggregationOperationState.cpp
index 185c6b1..14d4ea6 100644
--- a/storage/AggregationOperationState.cpp
+++ b/storage/AggregationOperationState.cpp
@@ -105,7 +105,8 @@ AggregationOperationState::AggregationOperationState(
         new HashTablePool(estimated_num_entries,
                           hash_table_impl_type,
                           group_by_types,
-                          handles_.back(),
+                          {1},
+                          handles_,
                           storage_manager)));
   } else {
     // Set up each individual aggregate in this operation.
@@ -142,8 +143,11 @@ AggregationOperationState::AggregationOperationState(
                               group_by_types,
                               handles_.back().get(),
                               storage_manager)));*/
+         if (*is_distinct_it) {
+            handles_.back()->BlockUpdate();
+         }
          group_by_handles.emplace_back(handles_.back());
-         payload_sizes.emplace_back(handles_.back()->getPayloadSize());
+         payload_sizes.emplace_back(group_by_handles.back()->getPayloadSize());
 
       } else {
         // Aggregation without GROUP BY: create a single global state.
@@ -186,26 +190,26 @@ AggregationOperationState::AggregationOperationState(
                 estimated_num_entries,
                 storage_manager));*/
 
-std::vector<AggregationHandle *> local;
-local.emplace_back(handles_.back());
+        std::vector<AggregationHandle *> local;
+        // local.emplace_back(handles_.back());
+        local.clear();
         distinctify_hashtables_.emplace_back(
-AggregationStateFastHashTableFactory::CreateResizable(
+        AggregationStateFastHashTableFactory::CreateResizable(
                 *distinctify_hash_table_impl_types_it,
                 key_types,
                 estimated_num_entries,
                 {0},
                 local,
                 storage_manager));
-
         ++distinctify_hash_table_impl_types_it;
       } else {
         distinctify_hashtables_.emplace_back(nullptr);
       }
     }
 
-      if (!group_by_handles.empty()) {
-        // Aggregation with GROUP BY: create a HashTable pool for per-group states.
-        group_by_hashtable_pools_.emplace_back(std::unique_ptr<HashTablePool>(
+    if (!group_by_handles.empty()) {
+      // Aggregation with GROUP BY: create a HashTable pool for per-group states.
+      group_by_hashtable_pools_.emplace_back(std::unique_ptr<HashTablePool>(
             new HashTablePool(estimated_num_entries,
                               hash_table_impl_type,
                               group_by_types,
@@ -213,7 +217,7 @@ AggregationStateFastHashTableFactory::CreateResizable(
                               group_by_handles,
                               storage_manager)));
       }
-  }
+    }
 }
 
 AggregationOperationState* AggregationOperationState::ReconstructFromProto(
@@ -442,13 +446,15 @@ void AggregationOperationState::aggregateBlockHashTable(const block_id input_blo
                                distinctify_hashtables_[agg_idx].get(),
                                &reuse_matches,
                                &reuse_group_by_vectors);
-    } else {
-      // Call StorageBlock::aggregateGroupBy() to aggregate this block's values
-      // directly into the (threadsafe) shared global HashTable for this
-      // aggregate.
-      DCHECK(group_by_hashtable_pools_[0] != nullptr);
-      AggregationStateHashTableBase *agg_hash_table = group_by_hashtable_pools_[0]->getHashTableFast();
-      DCHECK(agg_hash_table != nullptr);
+    }
+  }
+
+  // Call StorageBlock::aggregateGroupBy() to aggregate this block's values
+  // directly into the (threadsafe) shared global HashTable for this
+  // aggregate.
+  DCHECK(group_by_hashtable_pools_[0] != nullptr);
+  AggregationStateHashTableBase *agg_hash_table = group_by_hashtable_pools_[0]->getHashTableFast();
+  DCHECK(agg_hash_table != nullptr);
  /*     block->aggregateGroupBy(*handles_[agg_idx],
                               arguments_[agg_idx],
                               group_by_list_,
@@ -456,16 +462,13 @@ void AggregationOperationState::aggregateBlockHashTable(const block_id input_blo
                               agg_hash_table,
                               &reuse_matches,
                               &reuse_group_by_vectors);*/
-      block->aggregateGroupByFast(arguments_,
+  block->aggregateGroupByFast(arguments_,
                               group_by_list_,
                               predicate_.get(),
                               agg_hash_table,
                               &reuse_matches,
                               &reuse_group_by_vectors);
-      group_by_hashtable_pools_[0]->returnHashTable(agg_hash_table);
-      break;
-    }
-  }
+  group_by_hashtable_pools_[0]->returnHashTable(agg_hash_table);
 }
 
 void AggregationOperationState::finalizeSingleState(InsertDestination *output_destination) {
@@ -541,9 +544,11 @@ void AggregationOperationState::finalizeHashTable(InsertDestination *output_dest
       DCHECK(hash_tables->back() != nullptr);
       AggregationStateHashTableBase *agg_hash_table = hash_tables->back().get();
       DCHECK(agg_hash_table != nullptr);
+      handles_[agg_idx]->AllowUpdate();
       handles_[agg_idx]->aggregateOnDistinctifyHashTableForGroupBy(
           *distinctify_hashtables_[agg_idx],
-          agg_hash_table);
+          agg_hash_table,
+          agg_idx);
     }
 
     auto *hash_tables = group_by_hashtable_pools_[0]->getAllHashTables();

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d9c9e686/storage/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/storage/CMakeLists.txt b/storage/CMakeLists.txt
index e67b21f..74b4df5 100644
--- a/storage/CMakeLists.txt
+++ b/storage/CMakeLists.txt
@@ -629,7 +629,6 @@ target_link_libraries(quickstep_storage_EvictionPolicy
                       quickstep_utility_Macros)
 target_link_libraries(quickstep_storage_FastHashTable
                       quickstep_catalog_CatalogTypedefs
-                      quickstep_expressions_aggregation_AggregationHandleAvg
                       quickstep_storage_HashTable
                       quickstep_storage_HashTableBase
                       quickstep_storage_StorageBlob

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d9c9e686/storage/FastHashTable.hpp
----------------------------------------------------------------------
diff --git a/storage/FastHashTable.hpp b/storage/FastHashTable.hpp
index 12e447f..cba039a 100644
--- a/storage/FastHashTable.hpp
+++ b/storage/FastHashTable.hpp
@@ -35,7 +35,6 @@
 #include "storage/TupleReference.hpp"
 #include "storage/ValueAccessor.hpp"
 #include "storage/ValueAccessorUtil.hpp"
-#include "expressions/aggregation/AggregationHandleAvg.hpp"
 #include "threading/SpinSharedMutex.hpp"
 #include "threading/SpinMutex.hpp"
 #include "types/Type.hpp"
@@ -434,6 +433,11 @@ class FastHashTable : public HashTableBase<resizable,
                           const uint8_t *init_value_ptr,
                           FunctorT *functor);
 
+  template <typename FunctorT>
+  bool upsertCompositeKeyFast(const std::vector<TypedValue> &key,
+                          const uint8_t *init_value_ptr,
+                          FunctorT *functor, int index);
+
   bool upsertCompositeKeyNewFast(const std::vector<TypedValue> &key,
                           const uint8_t *init_value_ptr,
                           const uint8_t *source_state);
@@ -1851,6 +1855,41 @@ template <bool resizable,
           bool serializable,
           bool force_key_copy,
           bool allow_duplicate_keys>
+template <typename FunctorT>
+bool FastHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
+    ::upsertCompositeKeyFast(const std::vector<TypedValue> &key,
+                         const std::uint8_t *init_value_ptr,
+                         FunctorT *functor, int index) {
+  DEBUG_ASSERT(!allow_duplicate_keys);
+  const std::size_t variable_size = calculateVariableLengthCompositeKeyCopySize(key);
+  if (resizable) {
+    for (;;) {
+      {
+        SpinSharedMutexSharedLock<true> resize_lock(resize_shared_mutex_);
+        uint8_t *value = upsertCompositeKeyInternalFast(key, init_value_ptr, variable_size);
+        if (value != nullptr) {
+          (*functor)(value+payload_offsets_[index]);
+          return true;
+        }
+      }
+      resize(0, variable_size);
+    }
+  } else {
+    uint8_t *value = upsertCompositeKeyInternalFast(key, init_value_ptr, variable_size);
+    if (value == nullptr) {
+      return false;
+    } else {
+      (*functor)(value+payload_offsets_[index]);
+      return true;
+    }
+  }
+}
+
+
+template <bool resizable,
+          bool serializable,
+          bool force_key_copy,
+          bool allow_duplicate_keys>
 bool FastHashTable<resizable, serializable, force_key_copy, allow_duplicate_keys>
     ::upsertCompositeKeyNewFast(const std::vector<TypedValue> &key,
                          const std::uint8_t *init_value_ptr,

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d9c9e686/storage/FastSeparateChainingHashTable.hpp
----------------------------------------------------------------------
diff --git a/storage/FastSeparateChainingHashTable.hpp b/storage/FastSeparateChainingHashTable.hpp
index 64c4979..756d6e5 100644
--- a/storage/FastSeparateChainingHashTable.hpp
+++ b/storage/FastSeparateChainingHashTable.hpp
@@ -308,8 +308,11 @@ FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_dup
           key_manager_(this->key_types_, kValueOffset + this->total_payload_size_),
           bucket_size_(ComputeBucketSize(key_manager_.getFixedKeySize())) {
   init_payload_ = static_cast<std::uint8_t *>(calloc(this->total_payload_size_, 1));
-  for (auto handle : handles)
-      handle->initPayload(init_payload_);
+  int k = 0;
+  for (auto handle : handles) {
+      handle->initPayload(init_payload_+this->payload_offsets_[k]);
+      k++;
+  }
   // Bucket size always rounds up to the alignment requirement of the atomic
   // size_t "next" pointer at the front or a ValueT, whichever is larger.
   //
@@ -437,8 +440,7 @@ FastSeparateChainingHashTable<resizable, serializable, force_key_copy, allow_dup
               true),
           kBucketAlignment(alignof(std::atomic<std::size_t>) < alignof(uint8_t) ? alignof(uint8_t)
                                                   : alignof(std::atomic<std::size_t>)),
-          kValueOffset((((sizeof(std::atomic<std::size_t>) + sizeof(std::size_t) - 1) /
-                                           alignof(uint8_t)) + 1) * alignof(uint8_t)),
+          kValueOffset(sizeof(std::atomic<std::size_t>) + sizeof(std::size_t)),
           key_manager_(this->key_types_, kValueOffset + sizeof(uint8_t)),
           bucket_size_(ComputeBucketSize(key_manager_.getFixedKeySize())) {
   // Bucket size always rounds up to the alignment requirement of the atomic
@@ -1046,7 +1048,6 @@ uint8_t* FastSeparateChainingHashTable<resizable, serializable, force_key_copy,
     else
         memcpy(value, init_value_ptr, this->total_payload_size_);
 
-
   // Update the previous chain pointer to point to the new bucket.
   pending_chain_ptr->store(pending_chain_ptr_finish_value, std::memory_order_release);
 
@@ -1168,10 +1169,11 @@ uint8_t* FastSeparateChainingHashTable<resizable, serializable, force_key_copy,
 //  uint8_t *value;
 //  value = static_cast<unsigned char*>(bucket) + kValueOffset;
     uint8_t *value = static_cast<unsigned char*>(bucket) + kValueOffset;
-    if (init_value_ptr == nullptr)
+    if (init_value_ptr == nullptr) {
         memcpy(value, init_payload_, this->total_payload_size_);
-    else
+    } else {
         memcpy(value, init_value_ptr, this->total_payload_size_);
+    }
 
   // Update the previous chaing pointer to point to the new bucket.
   pending_chain_ptr->store(pending_chain_ptr_finish_value, std::memory_order_release);



[25/30] incubator-quickstep git commit: Initial commit for QUICKSTEP-28 and QUICKSTEP-29. Code refactoring and cleanup, some more optimizations are pending.

Posted by ra...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/storage/StorageBlock.hpp
----------------------------------------------------------------------
diff --git a/storage/StorageBlock.hpp b/storage/StorageBlock.hpp
index 3ae3812..8a0bd42 100644
--- a/storage/StorageBlock.hpp
+++ b/storage/StorageBlock.hpp
@@ -466,6 +466,14 @@ class StorageBlock : public StorageBlockBase {
                         std::vector<std::unique_ptr<ColumnVector>>
                             *reuse_group_by_vectors) const;
 
+
+  void aggregateGroupByFast(const std::vector<std::vector<std::unique_ptr<const Scalar>>> &arguments,
+                        const std::vector<std::unique_ptr<const Scalar>> &group_by,
+                        const Predicate *predicate,
+                        AggregationStateHashTableBase *hash_table,
+                        std::unique_ptr<TupleIdSequence> *reuse_matches,
+                        std::vector<std::unique_ptr<ColumnVector>>
+                            *reuse_group_by_vectors) const;
   /**
    * @brief Inserts the GROUP BY expressions and aggregation arguments together
    *        as keys into the distinctify hash table.

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/39485d62/threading/SpinMutex.hpp
----------------------------------------------------------------------
diff --git a/threading/SpinMutex.hpp b/threading/SpinMutex.hpp
index b64a6ca..c9838e4 100644
--- a/threading/SpinMutex.hpp
+++ b/threading/SpinMutex.hpp
@@ -42,6 +42,8 @@ class SpinMutex {
   SpinMutex() : locked_(false) {
   }
 
+  explicit SpinMutex(uint8_t *ptr): locked_(*ptr) {}
+
   /**
    * @note This call does NOT yield when contended. SpinMutex is intended
    *       mainly for cases where locks are held briefly and it is better to


[10/30] incubator-quickstep git commit: Added Comparisons for the Date Type.

Posted by ra...@apache.org.
Added Comparisons for the Date Type.


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

Branch: refs/heads/quickstep-28-29
Commit: cf81b5e62ddf7e96f3fcdb1bcc97ee0768a6997a
Parents: 9a398d3
Author: Zuyu Zhang <zu...@twitter.com>
Authored: Fri Jul 15 16:56:14 2016 -0700
Committer: Zuyu Zhang <zu...@twitter.com>
Committed: Sat Jul 16 15:51:21 2016 -0700

----------------------------------------------------------------------
 .../operations/comparisons/BasicComparison.cpp  |  6 ++-
 .../operations/comparisons/BasicComparison.hpp  | 29 ++++++++++--
 types/operations/comparisons/CMakeLists.txt     |  2 -
 types/operations/comparisons/ComparisonUtil.hpp | 34 +++++++++++++++
 .../comparisons/LiteralComparators.hpp          | 24 +++++-----
 .../comparisons/tests/Comparison_unittest.cpp   | 46 +++++++++++++++++---
 6 files changed, 115 insertions(+), 26 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/cf81b5e6/types/operations/comparisons/BasicComparison.cpp
----------------------------------------------------------------------
diff --git a/types/operations/comparisons/BasicComparison.cpp b/types/operations/comparisons/BasicComparison.cpp
index 21b92ca..ed0ce62 100644
--- a/types/operations/comparisons/BasicComparison.cpp
+++ b/types/operations/comparisons/BasicComparison.cpp
@@ -23,7 +23,6 @@
 #include "types/Type.hpp"
 #include "types/TypeID.hpp"
 #include "utility/EqualsAnyConstant.hpp"
-#include "utility/Macros.hpp"
 
 #include "glog/logging.h"
 
@@ -49,6 +48,9 @@ bool BasicComparison::canCompareTypes(const Type &left, const Type &right) const
           return false;
       }
     }
+    case kDate: {
+      return right.getTypeID() == kDate;
+    }
     case kDatetime: {
       return right.getTypeID() == kDatetime;
     }
@@ -89,7 +91,7 @@ bool BasicComparison::canComparePartialTypes(const Type *left_type,
   const Type *known_type = (left_type != nullptr) ? left_type : right_type;
   return QUICKSTEP_EQUALS_ANY_CONSTANT(known_type->getTypeID(),
                                        kInt, kLong, kFloat, kDouble,
-                                       kDatetime, kDatetimeInterval, kYearMonthInterval,
+                                       kDate, kDatetime, kDatetimeInterval, kYearMonthInterval,
                                        kChar, kVarChar);
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/cf81b5e6/types/operations/comparisons/BasicComparison.hpp
----------------------------------------------------------------------
diff --git a/types/operations/comparisons/BasicComparison.hpp b/types/operations/comparisons/BasicComparison.hpp
index 9741c32..c95f774 100644
--- a/types/operations/comparisons/BasicComparison.hpp
+++ b/types/operations/comparisons/BasicComparison.hpp
@@ -22,8 +22,6 @@
 #include <cstdint>
 #include <string>
 
-#include "types/DatetimeLit.hpp"
-#include "types/IntervalLit.hpp"
 #include "types/Type.hpp"
 #include "types/TypeErrors.hpp"
 #include "types/TypeFactory.hpp"
@@ -37,6 +35,11 @@
 
 namespace quickstep {
 
+struct DateLit;
+struct DatetimeIntervalLit;
+struct DatetimeLit;
+struct YearMonthIntervalLit;
+
 /** \addtogroup Types
  *  @{
  */
@@ -161,6 +164,18 @@ bool BasicComparison::compareTypedValuesCheckedHelper(const TypedValue &left,
       }
       break;
     }
+    case kDate: {
+      if (right.getTypeID() == kDate) {
+        if (left.isNull() || right.isNull()) {
+          return false;
+        }
+
+        ComparisonFunctor<DateLit> comparison_functor;
+        return comparison_functor(left.getLiteral<DateLit>(), right.getLiteral<DateLit>());
+      }
+      LOG(FATAL) << "Comparison " << getName() << " can not be applied to types "
+                 << left_type.getName() << " and " << right_type.getName();
+    }
     case kDatetime: {
       if (right.getTypeID() == kDatetime) {
         if (left.isNull() || right.isNull()) {
@@ -269,7 +284,8 @@ UncheckedComparator* BasicComparison::makeUncheckedComparatorForTypesHelper(cons
                                                                             const Type &right) const {
   if (left.getSuperTypeID() == Type::kNumeric && right.getSuperTypeID() == Type::kNumeric) {
     return makeNumericComparatorOuterHelper<LiteralComparator>(left, right);
-  } else if ((left.getTypeID() == kDatetime && right.getTypeID() == kDatetime)                 ||
+  } else if ((left.getTypeID() == kDate && right.getTypeID() == kDate)                         ||
+             (left.getTypeID() == kDatetime && right.getTypeID() == kDatetime)                 ||
              (left.getTypeID() == kDatetimeInterval && right.getTypeID() == kDatetimeInterval) ||
              (left.getTypeID() == kYearMonthInterval && right.getTypeID() == kYearMonthInterval)) {
     return makeDateComparatorOuterHelper<LiteralComparator>(left, right);
@@ -358,6 +374,12 @@ UncheckedComparator* BasicComparison::makeDateComparatorOuterHelper(
     const Type &left,
     const Type &right) const {
   switch (left.getTypeID()) {
+    case kDate:
+      if (left.isNullable()) {
+        return makeDateComparatorInnerHelper<ComparatorType, DateLit, true>(left, right);
+      } else {
+        return makeDateComparatorInnerHelper<ComparatorType, DateLit, false>(left, right);
+      }
     case kDatetime:
       if (left.isNullable()) {
         return makeDateComparatorInnerHelper<ComparatorType, DatetimeLit, true>(left, right);
@@ -389,6 +411,7 @@ UncheckedComparator* BasicComparison::makeDateComparatorInnerHelper(
     const Type &left,
     const Type &right) const {
   switch (right.getTypeID()) {
+    case kDate:
     case kDatetime:
     case kDatetimeInterval:
     case kYearMonthInterval:

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/cf81b5e6/types/operations/comparisons/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/types/operations/comparisons/CMakeLists.txt b/types/operations/comparisons/CMakeLists.txt
index d2dfecf..d48cdc2 100644
--- a/types/operations/comparisons/CMakeLists.txt
+++ b/types/operations/comparisons/CMakeLists.txt
@@ -64,8 +64,6 @@ target_link_libraries(quickstep_types_operations_comparisons_AsciiStringComparat
                       quickstep_utility_Macros)
 target_link_libraries(quickstep_types_operations_comparisons_BasicComparison
                       glog
-                      quickstep_types_DatetimeLit
-                      quickstep_types_IntervalLit
                       quickstep_types_Type
                       quickstep_types_TypeErrors
                       quickstep_types_TypeFactory

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/cf81b5e6/types/operations/comparisons/ComparisonUtil.hpp
----------------------------------------------------------------------
diff --git a/types/operations/comparisons/ComparisonUtil.hpp b/types/operations/comparisons/ComparisonUtil.hpp
index d7da3fd..d589192 100644
--- a/types/operations/comparisons/ComparisonUtil.hpp
+++ b/types/operations/comparisons/ComparisonUtil.hpp
@@ -163,6 +163,11 @@ auto InvokeOnLessComparatorForTypeIgnoreNullability(const Type &type,
           comp(0, 0);
       return functor(comp);
     }
+    case kDate: {
+      LessLiteralUncheckedComparator<DateLit, false,
+                                     DateLit, false> comp;
+      return functor(comp);
+    }
     case kDatetime: {
       LessLiteralUncheckedComparator<DatetimeLit, false,
                                      DatetimeLit, false> comp;
@@ -429,6 +434,18 @@ auto InvokeOnLessComparatorForDifferentTypesIgnoreNullability(
               left_type, right_type, functor);
       }
     }
+    case kDate: {
+      switch (right_type.getTypeID()) {
+        case kDate: {
+          LessLiteralUncheckedComparator<DateLit, false,
+                                         DateLit, false> comp;
+          return functor(comp);
+        }
+        default:
+          return comparison_util_detail::InvokeOnLessComparatorForDifferentTypesFallback(
+              left_type, right_type, functor);
+      }
+    }
     case kDatetime: {
       switch (right_type.getTypeID()) {
         case kDatetime: {
@@ -766,6 +783,18 @@ auto InvokeOnBothLessComparatorsForDifferentTypesIgnoreNullability(
               left_type, right_type, functor);
       }
     }
+    case kDate: {
+      switch (right_type.getTypeID()) {
+        case kDate: {
+          LessLiteralUncheckedComparator<DateLit, false,
+                                         DateLit, false> comp;
+          return functor(comp, comp);
+        }
+        default:
+          return comparison_util_detail::InvokeOnBothLessComparatorsForDifferentTypesFallback(
+              left_type, right_type, functor);
+      }
+    }
     case kDatetime: {
       switch (right_type.getTypeID()) {
         case kDatetime: {
@@ -963,6 +992,8 @@ inline bool CheckUntypedValuesEqual(const Type &type, const void *left, const vo
       return STLCharEqual(static_cast<const AsciiStringSuperType&>(type).getStringLength())(left, right);
     case kVarChar:
       return STLVarCharEqual()(left, right);
+    case kDate:
+      return STLLiteralEqual<DateLit>()(left, right);
     case kDatetime:
       return STLLiteralEqual<DatetimeLit>()(left, right);
     case kDatetimeInterval:
@@ -1220,6 +1251,9 @@ inline TypedValue GetLastValueForType(const Type &type) {
       return TypedValue(kChar, kLastString, 2);
     case kVarChar:
       return TypedValue(kVarChar, kLastString, 2);
+    case kDate: {
+      return TypedValue(DateLit::Create(99999, 12, 31));
+    }
     case kDatetime: {
       DatetimeLit lit;
       lit.ticks = std::numeric_limits<std::int64_t>::max();

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/cf81b5e6/types/operations/comparisons/LiteralComparators.hpp
----------------------------------------------------------------------
diff --git a/types/operations/comparisons/LiteralComparators.hpp b/types/operations/comparisons/LiteralComparators.hpp
index 3bf6b00..406ae40 100644
--- a/types/operations/comparisons/LiteralComparators.hpp
+++ b/types/operations/comparisons/LiteralComparators.hpp
@@ -257,8 +257,8 @@ class LiteralUncheckedComparator : public UncheckedComparator {
 };
 
 /**
- * @brief The equal UncheckedComparator for seven literals
- *        (int, std::int64_t, float, double, DatetimeLit, DatetimeIntervalLit, and YearMonthIntervalLit).
+ * @brief The equal UncheckedComparator for the following literals
+ *        (int, std::int64_t, float, double, DateLit, DatetimeLit, DatetimeIntervalLit, and YearMonthIntervalLit).
  **/
 template <typename LeftCppType, bool left_nullable,
           typename RightCppType, bool right_nullable>
@@ -267,8 +267,8 @@ using EqualLiteralUncheckedComparator = LiteralUncheckedComparator<EqualFunctor,
                                                                    RightCppType, right_nullable>;
 
 /**
- * @brief The not-equal UncheckedComparator for seven literals
- *        (int, std::int64_t, float, double, DatetimeLit, DatetimeIntervalLit, and YearMonthIntervalLit).
+ * @brief The not-equal UncheckedComparator for the following literals
+ *        (int, std::int64_t, float, double, DateLit, DatetimeLit, DatetimeIntervalLit, and YearMonthIntervalLit).
  **/
 template <typename LeftCppType, bool left_nullable,
           typename RightCppType, bool right_nullable>
@@ -277,8 +277,8 @@ using NotEqualLiteralUncheckedComparator = LiteralUncheckedComparator<NotEqualFu
                                                                       RightCppType, right_nullable>;
 
 /**
- * @brief The less-than UncheckedComparator for seven literals
- *        (int, std::int64_t, float, double, DatetimeLit, DatetimeIntervalLit, and YearMonthIntervalLit).
+ * @brief The less-than UncheckedComparator for the following literals
+ *        (int, std::int64_t, float, double, DateLit, DatetimeLit, DatetimeIntervalLit, and YearMonthIntervalLit).
  **/
 template <typename LeftCppType, bool left_nullable,
           typename RightCppType, bool right_nullable>
@@ -287,8 +287,8 @@ using LessLiteralUncheckedComparator = LiteralUncheckedComparator<LessFunctor,
                                                                   RightCppType, right_nullable>;
 
 /**
- * @brief The less-than-or-equal UncheckedComparator for seven literals
- *        (int, std::int64_t, float, double, DatetimeLit, DatetimeIntervalLit, and YearMonthIntervalLit).
+ * @brief The less-than-or-equal UncheckedComparator for the following literals
+ *        (int, std::int64_t, float, double, DateLit, DatetimeLit, DatetimeIntervalLit, and YearMonthIntervalLit).
  **/
 template <typename LeftCppType, bool left_nullable,
           typename RightCppType, bool right_nullable>
@@ -297,8 +297,8 @@ using LessOrEqualLiteralUncheckedComparator = LiteralUncheckedComparator<LessOrE
                                                                          RightCppType, right_nullable>;
 
 /**
- * @brief The greater-than UncheckedComparator for seven literals
- *        (int, std::int64_t, float, double, DatetimeLit, DatetimeIntervalLit, and YearMonthIntervalLit).
+ * @brief The greater-than UncheckedComparator for the following literals
+ *        (int, std::int64_t, float, double, DateLit, DatetimeLit, DatetimeIntervalLit, and YearMonthIntervalLit).
  **/
 template <typename LeftCppType, bool left_nullable,
           typename RightCppType, bool right_nullable>
@@ -307,8 +307,8 @@ using GreaterLiteralUncheckedComparator = LiteralUncheckedComparator<GreaterFunc
                                                                      RightCppType, right_nullable>;
 
 /**
- * @brief The greater-than-or-equal UncheckedComparator for seven literals
- *        (int, std::int64_t, float, double, DatetimeLit, DatetimeIntervalLit, and YearMonthIntervalLit).
+ * @brief The greater-than-or-equal UncheckedComparator for the following literals
+ *        (int, std::int64_t, float, double, DateLit, DatetimeLit, DatetimeIntervalLit, and YearMonthIntervalLit).
  **/
 template <typename LeftCppType, bool left_nullable,
           typename RightCppType, bool right_nullable>

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/cf81b5e6/types/operations/comparisons/tests/Comparison_unittest.cpp
----------------------------------------------------------------------
diff --git a/types/operations/comparisons/tests/Comparison_unittest.cpp b/types/operations/comparisons/tests/Comparison_unittest.cpp
index 0d5e387..d09aab7 100644
--- a/types/operations/comparisons/tests/Comparison_unittest.cpp
+++ b/types/operations/comparisons/tests/Comparison_unittest.cpp
@@ -25,8 +25,6 @@
 #include <utility>
 #include <vector>
 
-#include "gtest/gtest.h"
-
 #include "types/DatetimeLit.hpp"
 #include "types/IntervalLit.hpp"
 #include "types/Type.hpp"
@@ -40,6 +38,8 @@
 #include "utility/Macros.hpp"
 #include "utility/ScopedBuffer.hpp"
 
+#include "gtest/gtest.h"
+
 using std::int64_t;
 using std::min;
 using std::numeric_limits;
@@ -147,6 +147,23 @@ class ComparisonTest : public ::testing::Test {
     numeric_typed_values_.emplace_back(double_min_.get(),
                                        &TypeFactory::GetType(kDouble, false));
 
+    date_null_.reset(new TypedValue(kDate));
+    date_positive_.reset(new TypedValue(DateLit::Create(2016, 7, 15)));
+    date_negative_.reset(new TypedValue(DateLit::Create(-18017, 4, 13)));
+    date_max_.reset(new TypedValue(DateLit::Create(99999, 12, 31)));
+    date_min_.reset(new TypedValue(DateLit::Create(-99999, 1, 1)));
+
+    date_typed_values_.emplace_back(date_null_.get(),
+                                    &TypeFactory::GetType(kDate, true));
+    date_typed_values_.emplace_back(date_positive_.get(),
+                                    &TypeFactory::GetType(kDate, false));
+    date_typed_values_.emplace_back(date_negative_.get(),
+                                    &TypeFactory::GetType(kDate, false));
+    date_typed_values_.emplace_back(date_max_.get(),
+                                    &TypeFactory::GetType(kDate, false));
+    date_typed_values_.emplace_back(date_min_.get(),
+                                    &TypeFactory::GetType(kDate, false));
+
     datetime_null_.reset(new TypedValue(kDatetime));
     DatetimeLit datetime;
     datetime.ticks = 0;
@@ -347,6 +364,7 @@ class ComparisonTest : public ::testing::Test {
   void checkComparison(const Comparison &comparison) {
     checkNumericComparison(comparison);
     checkStringComparison(comparison);
+    checkDateComparison(comparison);
     checkDatetimeComparison(comparison);
     checkDatetimeIntervalComparison(comparison);
     checkYearMonthIntervalComparison(comparison);
@@ -524,6 +542,19 @@ class ComparisonTest : public ::testing::Test {
     }
   }
 
+  void checkDateComparison(const Comparison &comparison) {
+    for (const std::pair<const TypedValue*, const Type*> &left_item : date_typed_values_) {
+      for (const std::pair<const TypedValue*, const Type*> &right_item : date_typed_values_) {
+        checkDateComparisonChecked<DateLit>(comparison,
+                                            *left_item.first, *left_item.second,
+                                            *right_item.first, *right_item.second);
+        checkDateComparisonUnchecked<DateLit>(comparison,
+                                              *left_item.first, *left_item.second,
+                                              *right_item.first, *right_item.second);
+      }
+    }
+  }
+
   void checkDatetimeComparison(const Comparison &comparison) {
     for (const std::pair<const TypedValue*, const Type*> &left_item : datetime_typed_values_) {
       for (const std::pair<const TypedValue*, const Type*> &right_item : datetime_typed_values_) {
@@ -719,6 +750,7 @@ class ComparisonTest : public ::testing::Test {
       char_ref_extended_short_, char_lit_extended_short_,
       char_ref_extended_long_, char_lit_extended_long_,
       varchar_null_, varchar_ref_short_, varchar_lit_short_, varchar_ref_long_, varchar_lit_long_,
+      date_null_, date_positive_, date_negative_, date_max_, date_min_,
       datetime_null_, datetime_zero_, datetime_positive_, datetime_negative_, datetime_max_, datetime_min_,
       datetime_interval_null_, datetime_interval_zero_, datetime_interval_positive_, datetime_interval_negative_,
       year_month_interval_null_, year_month_interval_zero_,
@@ -727,7 +759,7 @@ class ComparisonTest : public ::testing::Test {
   unique_ptr<ScopedBuffer> extended_short_char_buffer_, extended_long_char_buffer_;
 
   vector<pair<const TypedValue*, const Type*>>
-      numeric_typed_values_, datetime_typed_values_, datetime_interval_typed_values_,
+      numeric_typed_values_, date_typed_values_, datetime_typed_values_, datetime_interval_typed_values_,
       year_month_interval_typed_values_, string_typed_values_;
 };
 
@@ -803,7 +835,7 @@ void CheckBasicComparisonApplicableToPartialTypes(const Comparison &comparison)
 
   // If only one argument is known, any orderable Type is OK.
   for (const TypeID type_id
-       : {kInt, kLong, kFloat, kDouble, kDatetime, kDatetimeInterval, kYearMonthInterval}) {
+       : {kInt, kLong, kFloat, kDouble, kDate, kDatetime, kDatetimeInterval, kYearMonthInterval}) {
     EXPECT_TRUE(comparison.canComparePartialTypes(
         &TypeFactory::GetType(type_id, false), nullptr));
     EXPECT_TRUE(comparison.canComparePartialTypes(
@@ -827,12 +859,12 @@ void CheckBasicComparisonApplicableToPartialTypes(const Comparison &comparison)
 
   // If both types are known, expect the same thing as canCompareTypes().
   for (const TypeID first_type_id
-       : {kInt, kLong, kFloat, kDouble, kDatetime, kDatetimeInterval, kYearMonthInterval}) {
+       : {kInt, kLong, kFloat, kDouble, kDate, kDatetime, kDatetimeInterval, kYearMonthInterval}) {
     const Type &first_type = TypeFactory::GetType(first_type_id, false);
     const Type &first_type_nullable = TypeFactory::GetType(first_type_id, true);
 
     for (const TypeID second_type_id
-         : {kInt, kLong, kFloat, kDouble, kDatetime, kDatetimeInterval, kYearMonthInterval}) {
+         : {kInt, kLong, kFloat, kDouble, kDate, kDatetime, kDatetimeInterval, kYearMonthInterval}) {
       const Type &second_type = TypeFactory::GetType(second_type_id, false);
       const Type &second_type_nullable = TypeFactory::GetType(second_type_id, true);
       EXPECT_EQ(comparison.canCompareTypes(first_type, second_type),
@@ -864,7 +896,7 @@ void CheckBasicComparisonApplicableToPartialTypes(const Comparison &comparison)
     const Type &first_type_nullable = TypeFactory::GetType(first_type_id, 10, true);
 
     for (const TypeID second_type_id
-         : {kInt, kLong, kFloat, kDouble, kDatetime, kDatetimeInterval, kYearMonthInterval}) {
+         : {kInt, kLong, kFloat, kDouble, kDate, kDatetime, kDatetimeInterval, kYearMonthInterval}) {
       const Type &second_type = TypeFactory::GetType(second_type_id, false);
       const Type &second_type_nullable = TypeFactory::GetType(second_type_id, true);
       EXPECT_EQ(comparison.canCompareTypes(first_type, second_type),


[19/30] incubator-quickstep git commit: Renamed a QueryExecutionMessage.

Posted by ra...@apache.org.
Renamed a QueryExecutionMessage.


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

Branch: refs/heads/quickstep-28-29
Commit: aaecc76b7bea85f46bf06dc2e63fccf43636d7eb
Parents: 33554c3
Author: Zuyu Zhang <zu...@twitter.com>
Authored: Thu Jul 28 11:11:33 2016 -0700
Committer: Zuyu Zhang <zu...@twitter.com>
Committed: Thu Jul 28 11:11:33 2016 -0700

----------------------------------------------------------------------
 query_execution/QueryExecutionMessages.proto |  4 +-
 query_execution/QueryExecutionTypedefs.hpp   |  4 +-
 query_execution/Shiftboss.cpp                | 45 +++++++++++------------
 query_execution/Shiftboss.hpp                |  4 +-
 4 files changed, 28 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/aaecc76b/query_execution/QueryExecutionMessages.proto
----------------------------------------------------------------------
diff --git a/query_execution/QueryExecutionMessages.proto b/query_execution/QueryExecutionMessages.proto
index 591ca6c..308d736 100644
--- a/query_execution/QueryExecutionMessages.proto
+++ b/query_execution/QueryExecutionMessages.proto
@@ -113,12 +113,12 @@ message InitiateRebuildResponseMessage {
   required uint64 num_rebuild_work_orders = 3;
 }
 
-message QueryResultRelationMessage {
+message SaveQueryResultMessage {
   required int32 relation_id = 1;
   repeated fixed64 blocks = 2 [packed=true];
 }
 
-message QueryResultRelationResponseMessage {
+message SaveQueryResultResponseMessage {
   required int32 relation_id = 1;
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/aaecc76b/query_execution/QueryExecutionTypedefs.hpp
----------------------------------------------------------------------
diff --git a/query_execution/QueryExecutionTypedefs.hpp b/query_execution/QueryExecutionTypedefs.hpp
index d73d4ee..b67209f 100644
--- a/query_execution/QueryExecutionTypedefs.hpp
+++ b/query_execution/QueryExecutionTypedefs.hpp
@@ -81,8 +81,8 @@ enum QueryExecutionMessageType : message_type_id {
   kInitiateRebuildMessage,  // From Foreman to Shiftboss.
   kInitiateRebuildResponseMessage,  // From Shiftboss to Foreman.
 
-  kQueryResultRelationMessage,  // From Foreman to Shiftboss.
-  kQueryResultRelationResponseMessage,  // From Shiftboss to Foreman.
+  kSaveQueryResultMessage,  // From Foreman to Shiftboss.
+  kSaveQueryResultResponseMessage,  // From Shiftboss to Foreman.
 
   // BlockLocator related messages, sorted in a life cycle of StorageManager
   // with a unique block domain.

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/aaecc76b/query_execution/Shiftboss.cpp
----------------------------------------------------------------------
diff --git a/query_execution/Shiftboss.cpp b/query_execution/Shiftboss.cpp
index af56306..7f655c6 100644
--- a/query_execution/Shiftboss.cpp
+++ b/query_execution/Shiftboss.cpp
@@ -149,11 +149,10 @@ void Shiftboss::run() {
                                            move(annotated_message.tagged_message));
         break;
       }
-      case kQueryResultRelationMessage: {
-        // TODO(zuyu): Rename to kSaveQueryResultMessage.
+      case kSaveQueryResultMessage: {
         const TaggedMessage &tagged_message = annotated_message.tagged_message;
 
-        serialization::QueryResultRelationMessage proto;
+        serialization::SaveQueryResultMessage proto;
         CHECK(proto.ParseFromArray(tagged_message.message(), tagged_message.message_bytes()));
 
         for (int i = 0; i < proto.blocks_size(); ++i) {
@@ -168,25 +167,25 @@ void Shiftboss::run() {
           }
         }
 
-        serialization::QueryResultRelationResponseMessage ack_proto;
-        ack_proto.set_relation_id(proto.relation_id());
+        serialization::SaveQueryResultResponseMessage proto_response;
+        proto_response.set_relation_id(proto.relation_id());
 
-        const size_t ack_proto_length = ack_proto.ByteSize();
-        char *ack_proto_bytes = static_cast<char*>(malloc(ack_proto_length));
-        CHECK(ack_proto.SerializeToArray(ack_proto_bytes, ack_proto_length));
+        const size_t proto_response_length = proto_response.ByteSize();
+        char *proto_response_bytes = static_cast<char*>(malloc(proto_response_length));
+        CHECK(proto_response.SerializeToArray(proto_response_bytes, proto_response_length));
 
-        TaggedMessage ack_message(static_cast<const void*>(ack_proto_bytes),
-                                  ack_proto_length,
-                                  kQueryResultRelationResponseMessage);
-        free(ack_proto_bytes);
+        TaggedMessage message_response(static_cast<const void*>(proto_response_bytes),
+                                       proto_response_length,
+                                       kSaveQueryResultResponseMessage);
+        free(proto_response_bytes);
 
         LOG(INFO) << "Shiftboss (id '" << shiftboss_client_id_
-                  << "') sent QueryResultRelationResponseMessage (typed '" << kQueryResultRelationResponseMessage
-                  << ") to Foreman";
+                  << "') sent SaveQueryResultResponseMessage (typed '" << kSaveQueryResultResponseMessage
+                  << "') to Foreman";
         QueryExecutionUtil::SendTMBMessage(bus_,
                                            shiftboss_client_id_,
                                            foreman_client_id_,
-                                           move(ack_message));
+                                           move(message_response));
         break;
       }
       case kPoisonMessage: {
@@ -280,15 +279,15 @@ void Shiftboss::processQueryInitiateMessage(
   char *proto_bytes = static_cast<char*>(malloc(proto_length));
   CHECK(proto.SerializeToArray(proto_bytes, proto_length));
 
-  TaggedMessage ack_message(static_cast<const void*>(proto_bytes),
-                            proto_length,
-                            kQueryInitiateResponseMessage);
+  TaggedMessage message_response(static_cast<const void*>(proto_bytes),
+                                 proto_length,
+                                 kQueryInitiateResponseMessage);
   free(proto_bytes);
 
   QueryExecutionUtil::SendTMBMessage(bus_,
                                      shiftboss_client_id_,
                                      foreman_client_id_,
-                                     move(ack_message));
+                                     move(message_response));
 }
 
 void Shiftboss::processInitiateRebuildMessage(const std::size_t query_id,
@@ -317,15 +316,15 @@ void Shiftboss::processInitiateRebuildMessage(const std::size_t query_id,
   char *proto_bytes = static_cast<char*>(malloc(proto_length));
   CHECK(proto.SerializeToArray(proto_bytes, proto_length));
 
-  TaggedMessage ack_message(static_cast<const void*>(proto_bytes),
-                            proto_length,
-                            kInitiateRebuildResponseMessage);
+  TaggedMessage message_response(static_cast<const void*>(proto_bytes),
+                                 proto_length,
+                                 kInitiateRebuildResponseMessage);
   free(proto_bytes);
 
   QueryExecutionUtil::SendTMBMessage(bus_,
                                      shiftboss_client_id_,
                                      foreman_client_id_,
-                                     move(ack_message));
+                                     move(message_response));
 
   for (size_t i = 0; i < partially_filled_block_refs.size(); ++i) {
     // NOTE(zuyu): Worker releases the memory after the execution of

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/aaecc76b/query_execution/Shiftboss.hpp
----------------------------------------------------------------------
diff --git a/query_execution/Shiftboss.hpp b/query_execution/Shiftboss.hpp
index 096ab74..9e24d62 100644
--- a/query_execution/Shiftboss.hpp
+++ b/query_execution/Shiftboss.hpp
@@ -112,8 +112,8 @@ class Shiftboss : public Thread {
     bus_->RegisterClientAsReceiver(shiftboss_client_id_, kRebuildWorkOrderCompleteMessage);
     bus_->RegisterClientAsSender(shiftboss_client_id_, kRebuildWorkOrderCompleteMessage);
 
-    bus_->RegisterClientAsReceiver(shiftboss_client_id_, kQueryResultRelationMessage);
-    bus_->RegisterClientAsSender(shiftboss_client_id_, kQueryResultRelationResponseMessage);
+    bus_->RegisterClientAsReceiver(shiftboss_client_id_, kSaveQueryResultMessage);
+    bus_->RegisterClientAsSender(shiftboss_client_id_, kSaveQueryResultResponseMessage);
 
     // Stop itself.
     bus_->RegisterClientAsReceiver(shiftboss_client_id_, kPoisonMessage);


[21/30] incubator-quickstep git commit: RANGE mode and computation optimization. - Supported RANGE mode for window aggregation. - Optimized the AVG calculation time complexity from O(nk) to O(n), where n is the number of tuples and k is the window size.

Posted by ra...@apache.org.
RANGE mode and computation optimization.
- Supported RANGE mode for window aggregation.
- Optimized the AVG calculation time complexity from O(nk) to O(n), where n is the number of tuples and k is the window size.


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

Branch: refs/heads/quickstep-28-29
Commit: d0172fde0cffbf10bc858090e11169e11834be89
Parents: e53186e
Author: shixuan <sh...@apache.org>
Authored: Tue Jul 26 11:49:07 2016 -0500
Committer: shixuan <sh...@apache.org>
Committed: Mon Aug 1 09:38:34 2016 -0500

----------------------------------------------------------------------
 expressions/window_aggregation/CMakeLists.txt   |  15 +-
 .../WindowAggregateFunction.hpp                 |  19 +-
 .../WindowAggregateFunctionAvg.cpp              |  14 +-
 .../WindowAggregateFunctionAvg.hpp              |   6 +-
 .../WindowAggregateFunctionCount.cpp            |   6 +-
 .../WindowAggregateFunctionCount.hpp            |   6 +-
 .../WindowAggregateFunctionMax.cpp              |   6 +-
 .../WindowAggregateFunctionMax.hpp              |   6 +-
 .../WindowAggregateFunctionMin.cpp              |   6 +-
 .../WindowAggregateFunctionMin.hpp              |   6 +-
 .../WindowAggregateFunctionSum.cpp              |   6 +-
 .../WindowAggregateFunctionSum.hpp              |   6 +-
 .../WindowAggregationHandle.cpp                 | 186 ++++++++++++++++
 .../WindowAggregationHandle.hpp                 | 100 ++++++---
 .../WindowAggregationHandleAvg.cpp              | 201 ++++++-----------
 .../WindowAggregationHandleAvg.hpp              |  35 ++-
 .../WindowAggregationHandleAvg_unittest.cpp     | 220 +++++++++++++++----
 query_optimizer/ExecutionGenerator.cpp          |  11 +-
 query_optimizer/resolver/Resolver.cpp           |  19 +-
 .../tests/execution_generator/Select.test       |  41 +++-
 storage/WindowAggregationOperationState.cpp     |  69 +++---
 storage/WindowAggregationOperationState.hpp     |   9 +-
 storage/WindowAggregationOperationState.proto   |   1 +
 23 files changed, 692 insertions(+), 302 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/expressions/window_aggregation/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/CMakeLists.txt b/expressions/window_aggregation/CMakeLists.txt
index 6a16fcc..3a79b7e 100644
--- a/expressions/window_aggregation/CMakeLists.txt
+++ b/expressions/window_aggregation/CMakeLists.txt
@@ -44,7 +44,7 @@ add_library(quickstep_expressions_windowaggregation_WindowAggregateFunctionSum
             WindowAggregateFunctionSum.cpp
             WindowAggregateFunctionSum.hpp)
 add_library(quickstep_expressions_windowaggregation_WindowAggregationHandle
-            ../../empty_src.cpp
+            WindowAggregationHandle.cpp
             WindowAggregationHandle.hpp)
 add_library(quickstep_expressions_windowaggregation_WindowAggregationHandleAvg
             WindowAggregationHandleAvg.cpp
@@ -130,10 +130,17 @@ target_link_libraries(quickstep_expressions_windowaggregation_WindowAggregationH
                       glog
                       quickstep_catalog_CatalogRelationSchema
                       quickstep_catalog_CatalogTypedefs
+                      quickstep_expressions_scalar_Scalar
                       quickstep_storage_StorageBlockInfo
+                      quickstep_types_Type
+                      quickstep_types_TypeFactory
+                      quickstep_types_TypeID
                       quickstep_types_TypedValue
                       quickstep_types_containers_ColumnVector
                       quickstep_types_containers_ColumnVectorsValueAccessor
+                      quickstep_types_operations_binaryoperations_BinaryOperation
+                      quickstep_types_operations_binaryoperations_BinaryOperationFactory
+                      quickstep_types_operations_binaryoperations_BinaryOperationID
                       quickstep_types_operations_comparisons_Comparison
                       quickstep_types_operations_comparisons_ComparisonFactory
                       quickstep_types_operations_comparisons_ComparisonID
@@ -141,8 +148,6 @@ target_link_libraries(quickstep_expressions_windowaggregation_WindowAggregationH
 target_link_libraries(quickstep_expressions_windowaggregation_WindowAggregationHandleAvg
                       glog
                       quickstep_catalog_CatalogTypedefs
-                      quickstep_expressions_scalar_Scalar
-                      quickstep_expressions_scalar_ScalarAttribute
                       quickstep_expressions_windowaggregation_WindowAggregationHandle
                       quickstep_storage_ValueAccessor
                       quickstep_types_Type
@@ -179,11 +184,13 @@ add_executable(WindowAggregationHandle_tests
 target_link_libraries(WindowAggregationHandle_tests
                       gtest
                       gtest_main
+                      quickstep_catalog_CatalogAttribute
                       quickstep_catalog_CatalogTypedefs
+                      quickstep_expressions_scalar_Scalar
+                      quickstep_expressions_scalar_ScalarAttribute
                       quickstep_expressions_windowaggregation_WindowAggregateFunction
                       quickstep_expressions_windowaggregation_WindowAggregateFunctionFactory
                       quickstep_expressions_windowaggregation_WindowAggregationHandle
-                      quickstep_expressions_windowaggregation_WindowAggregationHandleAvg
                       quickstep_expressions_windowaggregation_WindowAggregationID
                       quickstep_storage_ValueAccessor
                       quickstep_types_CharType

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/expressions/window_aggregation/WindowAggregateFunction.hpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunction.hpp b/expressions/window_aggregation/WindowAggregateFunction.hpp
index e40479b..7ffc4ae 100644
--- a/expressions/window_aggregation/WindowAggregateFunction.hpp
+++ b/expressions/window_aggregation/WindowAggregateFunction.hpp
@@ -20,6 +20,7 @@
 #ifndef QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_HPP_
 #define QUICKSTEP_EXPRESSIONS_WINDOW_AGGREGATION_WINDOW_AGGREGATE_FUNCTION_HPP_
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -32,6 +33,7 @@
 namespace quickstep {
 
 class CatalogRelationSchema;
+class Scalar;
 class Type;
 class WindowAggregationHandle;
 
@@ -120,16 +122,23 @@ class WindowAggregateFunction {
    *
    * @param argument_types A list of zero or more Types (in order) for
    *        arguments to this WindowAggregateFunction.
-   * @param partition_key_types A list or zero or more Types for partition keys
-   *                            to this WindowAggregateFunction.
+   * @param partition_by_attributes A list of attributes used as partition key.
+   * @param order_by_attributes A list of attributes used as order key.
+   * @param is_row True if the frame mode is ROWS, false if RANGE.
+   * @param num_preceding The number of rows/range that precedes the current row.
+   * @param num_following The number of rows/range that follows the current row.
    * 
    * @return A new WindowAggregationHandle that can be used to compute this
-   *         WindowAggregateFunction over the specified argument_types. Caller
-   *         is responsible for deleting the returned object.
+   *         WindowAggregateFunction over the specified window definition.
+   *         Caller is responsible for deleting the returned object.
    **/
   virtual WindowAggregationHandle* createHandle(
       const std::vector<const Type*> &argument_types,
-      const std::vector<const Type*> &partition_key_types) const = 0;
+      const std::vector<std::unique_ptr<const Scalar>> &partition_by_attributes,
+      const std::vector<std::unique_ptr<const Scalar>> &order_by_attributes,
+      const bool is_row,
+      const std::int64_t num_preceding,
+      const std::int64_t num_following) const = 0;
 
  protected:
   explicit WindowAggregateFunction(const WindowAggregationID win_agg_id)

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/expressions/window_aggregation/WindowAggregateFunctionAvg.cpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionAvg.cpp b/expressions/window_aggregation/WindowAggregateFunctionAvg.cpp
index bc31a53..beb1c7a 100644
--- a/expressions/window_aggregation/WindowAggregateFunctionAvg.cpp
+++ b/expressions/window_aggregation/WindowAggregateFunctionAvg.cpp
@@ -73,13 +73,21 @@ const Type* WindowAggregateFunctionAvg::resultTypeForArgumentTypes(
 
 WindowAggregationHandle* WindowAggregateFunctionAvg::createHandle(
     const std::vector<const Type*> &argument_types,
-    const std::vector<const Type*> &partition_key_types) const {
+    const std::vector<std::unique_ptr<const Scalar>> &partition_by_attributes,
+    const std::vector<std::unique_ptr<const Scalar>> &order_by_attributes,
+    const bool is_row,
+    const std::int64_t num_preceding,
+    const std::int64_t num_following) const {
   DCHECK(canApplyToTypes(argument_types))
       << "Attempted to create an WindowAggregationHandleAvg for argument Type(s)"
       << " that AVG can not be applied to.";
 
-  return new WindowAggregationHandleAvg(partition_key_types,
-                                        *argument_types.front());
+  return new WindowAggregationHandleAvg(partition_by_attributes,
+                                        order_by_attributes,
+                                        is_row,
+                                        num_preceding,
+                                        num_following,
+                                        argument_types[0]);
 }
 
 }  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/expressions/window_aggregation/WindowAggregateFunctionAvg.hpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionAvg.hpp b/expressions/window_aggregation/WindowAggregateFunctionAvg.hpp
index 32fd9d5..0e50415 100644
--- a/expressions/window_aggregation/WindowAggregateFunctionAvg.hpp
+++ b/expressions/window_aggregation/WindowAggregateFunctionAvg.hpp
@@ -58,7 +58,11 @@ class WindowAggregateFunctionAvg : public WindowAggregateFunction {
 
   WindowAggregationHandle* createHandle(
       const std::vector<const Type*> &argument_types,
-      const std::vector<const Type*> &partition_key_types) const override;
+      const std::vector<std::unique_ptr<const Scalar>> &partition_by_attributes,
+      const std::vector<std::unique_ptr<const Scalar>> &order_by_attributes,
+      const bool is_row,
+      const std::int64_t num_preceding,
+      const std::int64_t num_following) const override;
 
  private:
   WindowAggregateFunctionAvg()

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/expressions/window_aggregation/WindowAggregateFunctionCount.cpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionCount.cpp b/expressions/window_aggregation/WindowAggregateFunctionCount.cpp
index 504e000..ccd81ac 100644
--- a/expressions/window_aggregation/WindowAggregateFunctionCount.cpp
+++ b/expressions/window_aggregation/WindowAggregateFunctionCount.cpp
@@ -47,7 +47,11 @@ const Type* WindowAggregateFunctionCount::resultTypeForArgumentTypes(
 
 WindowAggregationHandle* WindowAggregateFunctionCount::createHandle(
     const std::vector<const Type*> &argument_types,
-    const std::vector<const Type*> &partition_key_types) const {
+    const std::vector<std::unique_ptr<const Scalar>> &partition_by_attributes,
+    const std::vector<std::unique_ptr<const Scalar>> &order_by_attributes,
+    const bool is_row,
+    const std::int64_t num_preceding,
+    const std::int64_t num_following) const {
   DCHECK(canApplyToTypes(argument_types))
       << "Attempted to create a WindowAggregationHandleCount for argument Types "
       << "that COUNT can not be applied to (> 1 argument).";

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/expressions/window_aggregation/WindowAggregateFunctionCount.hpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionCount.hpp b/expressions/window_aggregation/WindowAggregateFunctionCount.hpp
index 1b40fdd..2e5506a 100644
--- a/expressions/window_aggregation/WindowAggregateFunctionCount.hpp
+++ b/expressions/window_aggregation/WindowAggregateFunctionCount.hpp
@@ -58,7 +58,11 @@ class WindowAggregateFunctionCount : public WindowAggregateFunction {
 
   WindowAggregationHandle* createHandle(
       const std::vector<const Type*> &argument_types,
-      const std::vector<const Type*> &partition_key_types) const override;
+      const std::vector<std::unique_ptr<const Scalar>> &partition_by_attributes,
+      const std::vector<std::unique_ptr<const Scalar>> &order_by_attributes,
+      const bool is_row,
+      const std::int64_t num_preceding,
+      const std::int64_t num_following) const override;
 
  private:
   WindowAggregateFunctionCount()

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/expressions/window_aggregation/WindowAggregateFunctionMax.cpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionMax.cpp b/expressions/window_aggregation/WindowAggregateFunctionMax.cpp
index f3997c7..acfce82 100644
--- a/expressions/window_aggregation/WindowAggregateFunctionMax.cpp
+++ b/expressions/window_aggregation/WindowAggregateFunctionMax.cpp
@@ -55,7 +55,11 @@ const Type* WindowAggregateFunctionMax::resultTypeForArgumentTypes(
 
 WindowAggregationHandle* WindowAggregateFunctionMax::createHandle(
     const std::vector<const Type*> &argument_types,
-    const std::vector<const Type*> &partition_key_types) const {
+    const std::vector<std::unique_ptr<const Scalar>> &partition_by_attributes,
+    const std::vector<std::unique_ptr<const Scalar>> &order_by_attributes,
+    const bool is_row,
+    const std::int64_t num_preceding,
+    const std::int64_t num_following) const {
   DCHECK(canApplyToTypes(argument_types))
       << "Attempted to create a WindowAggregationHandleMax for argument Type(s) "
       << "that MAX can not be applied to.";

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/expressions/window_aggregation/WindowAggregateFunctionMax.hpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionMax.hpp b/expressions/window_aggregation/WindowAggregateFunctionMax.hpp
index 00c788e..a215703 100644
--- a/expressions/window_aggregation/WindowAggregateFunctionMax.hpp
+++ b/expressions/window_aggregation/WindowAggregateFunctionMax.hpp
@@ -58,7 +58,11 @@ class WindowAggregateFunctionMax : public WindowAggregateFunction {
 
   WindowAggregationHandle* createHandle(
       const std::vector<const Type*> &argument_types,
-      const std::vector<const Type*> &partition_key_types) const override;
+      const std::vector<std::unique_ptr<const Scalar>> &partition_by_attributes,
+      const std::vector<std::unique_ptr<const Scalar>> &order_by_attributes,
+      const bool is_row,
+      const std::int64_t num_preceding,
+      const std::int64_t num_following) const override;
 
  private:
   WindowAggregateFunctionMax()

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/expressions/window_aggregation/WindowAggregateFunctionMin.cpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionMin.cpp b/expressions/window_aggregation/WindowAggregateFunctionMin.cpp
index a13e28e..cd845bd 100644
--- a/expressions/window_aggregation/WindowAggregateFunctionMin.cpp
+++ b/expressions/window_aggregation/WindowAggregateFunctionMin.cpp
@@ -55,7 +55,11 @@ const Type* WindowAggregateFunctionMin::resultTypeForArgumentTypes(
 
 WindowAggregationHandle* WindowAggregateFunctionMin::createHandle(
     const std::vector<const Type*> &argument_types,
-    const std::vector<const Type*> &partition_key_types) const {
+    const std::vector<std::unique_ptr<const Scalar>> &partition_by_attributes,
+    const std::vector<std::unique_ptr<const Scalar>> &order_by_attributes,
+    const bool is_row,
+    const std::int64_t num_preceding,
+    const std::int64_t num_following) const {
   DCHECK(canApplyToTypes(argument_types))
       << "Attempted to create a WindowAggregationHandleMin for argument Type(s) "
       << "that MIN can not be applied to.";

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/expressions/window_aggregation/WindowAggregateFunctionMin.hpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionMin.hpp b/expressions/window_aggregation/WindowAggregateFunctionMin.hpp
index aeba539..fab88a8 100644
--- a/expressions/window_aggregation/WindowAggregateFunctionMin.hpp
+++ b/expressions/window_aggregation/WindowAggregateFunctionMin.hpp
@@ -58,7 +58,11 @@ class WindowAggregateFunctionMin : public WindowAggregateFunction {
 
   WindowAggregationHandle* createHandle(
       const std::vector<const Type*> &argument_types,
-      const std::vector<const Type*> &partition_key_types) const override;
+      const std::vector<std::unique_ptr<const Scalar>> &partition_by_attributes,
+      const std::vector<std::unique_ptr<const Scalar>> &order_by_attributes,
+      const bool is_row,
+      const std::int64_t num_preceding,
+      const std::int64_t num_following) const override;
 
  private:
   WindowAggregateFunctionMin()

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/expressions/window_aggregation/WindowAggregateFunctionSum.cpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionSum.cpp b/expressions/window_aggregation/WindowAggregateFunctionSum.cpp
index 636c53a..e2aeb60 100644
--- a/expressions/window_aggregation/WindowAggregateFunctionSum.cpp
+++ b/expressions/window_aggregation/WindowAggregateFunctionSum.cpp
@@ -71,7 +71,11 @@ const Type* WindowAggregateFunctionSum::resultTypeForArgumentTypes(
 
 WindowAggregationHandle* WindowAggregateFunctionSum::createHandle(
     const std::vector<const Type*> &argument_types,
-    const std::vector<const Type*> &partition_key_types) const {
+    const std::vector<std::unique_ptr<const Scalar>> &partition_by_attributes,
+    const std::vector<std::unique_ptr<const Scalar>> &order_by_attributes,
+    const bool is_row,
+    const std::int64_t num_preceding,
+    const std::int64_t num_following) const {
   DCHECK(canApplyToTypes(argument_types))
       << "Attempted to create a WindowAggregationHandleSum for argument Type(s) "
       << "that SUM can not be applied to.";

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/expressions/window_aggregation/WindowAggregateFunctionSum.hpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregateFunctionSum.hpp b/expressions/window_aggregation/WindowAggregateFunctionSum.hpp
index 047113c..8d7d61d 100644
--- a/expressions/window_aggregation/WindowAggregateFunctionSum.hpp
+++ b/expressions/window_aggregation/WindowAggregateFunctionSum.hpp
@@ -58,7 +58,11 @@ class WindowAggregateFunctionSum : public WindowAggregateFunction {
 
   WindowAggregationHandle* createHandle(
       const std::vector<const Type*> &argument_types,
-      const std::vector<const Type*> &partition_key_types) const override;
+      const std::vector<std::unique_ptr<const Scalar>> &partition_by_attributes,
+      const std::vector<std::unique_ptr<const Scalar>> &order_by_attributes,
+      const bool is_row,
+      const std::int64_t num_preceding,
+      const std::int64_t num_following) const override;
 
  private:
   WindowAggregateFunctionSum()

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/expressions/window_aggregation/WindowAggregationHandle.cpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregationHandle.cpp b/expressions/window_aggregation/WindowAggregationHandle.cpp
new file mode 100644
index 0000000..835eaff
--- /dev/null
+++ b/expressions/window_aggregation/WindowAggregationHandle.cpp
@@ -0,0 +1,186 @@
+/**
+ *   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.
+ **/
+
+#include "expressions/window_aggregation/WindowAggregationHandle.hpp"
+
+#include <cstddef>
+#include <memory>
+#include <vector>
+
+#include "catalog/CatalogTypedefs.hpp"
+#include "expressions/scalar/Scalar.hpp"
+#include "types/Type.hpp"
+#include "types/TypeFactory.hpp"
+#include "types/TypeID.hpp"
+#include "types/TypedValue.hpp"
+#include "types/containers/ColumnVectorsValueAccessor.hpp"
+#include "types/operations/binary_operations/BinaryOperation.hpp"
+#include "types/operations/binary_operations/BinaryOperationFactory.hpp"
+#include "types/operations/binary_operations/BinaryOperationID.hpp"
+#include "types/operations/comparisons/Comparison.hpp"
+
+#include "glog/logging.h"
+
+namespace quickstep {
+
+WindowAggregationHandle::WindowAggregationHandle(
+    const std::vector<std::unique_ptr<const Scalar>> &partition_by_attributes,
+    const std::vector<std::unique_ptr<const Scalar>> &order_by_attributes,
+    const bool is_row,
+    const std::int64_t num_preceding,
+    const std::int64_t num_following)
+    : is_row_(is_row),
+      num_preceding_(num_preceding),
+      num_following_(num_following) {
+  // IDs and types of partition keys.
+  std::vector<const Type*> partition_key_types;
+  for (const std::unique_ptr<const Scalar> &partition_by_attribute : partition_by_attributes) {
+    partition_key_ids_.push_back(
+        partition_by_attribute->getAttributeIdForValueAccessor());
+    partition_key_types.push_back(&partition_by_attribute->getType());
+  }
+
+  // Comparison operators for checking if two tuples belong to the same partition.
+  for (const Type *partition_key_type : partition_key_types) {
+    partition_equal_comparators_.emplace_back(
+        ComparisonFactory::GetComparison(ComparisonID::kEqual)
+            .makeUncheckedComparatorForTypes(*partition_key_type, *partition_key_type));
+  }
+
+  // IDs and types of order keys.
+  const Type *first_order_key_type = nullptr;
+  for (const std::unique_ptr<const Scalar> &order_by_attribute : order_by_attributes) {
+    order_key_ids_.push_back(
+        order_by_attribute->getAttributeIdForValueAccessor());
+    if (first_order_key_type == nullptr) {
+      first_order_key_type = &order_by_attribute->getType();
+    }
+  }
+
+  // ID and type of the first order key if in RANGE mode.
+  if (!is_row) {
+    DCHECK(first_order_key_type != nullptr);
+
+    // Comparators and operators to check window frame in RANGE mode.
+    const Type &long_type = TypeFactory::GetType(kLong, false);
+    range_compare_type_ =
+        TypeFactory::GetUnifyingType(*first_order_key_type, long_type);
+
+    range_add_operator_.reset(
+        BinaryOperationFactory::GetBinaryOperation(BinaryOperationID::kAdd)
+            .makeUncheckedBinaryOperatorForTypes(*first_order_key_type, long_type));
+    range_comparator_.reset(
+        ComparisonFactory::GetComparison(ComparisonID::kLessOrEqual)
+            .makeUncheckedComparatorForTypes(*range_compare_type_, *range_compare_type_));
+  }
+}
+
+bool WindowAggregationHandle::samePartition(
+    const ColumnVectorsValueAccessor *tuple_accessor,
+    const tuple_id test_tuple_id) const {
+  // If test tuple does not exist.
+  if (test_tuple_id < 0 ||
+      test_tuple_id >= tuple_accessor->getNumTuples()) {
+    return false;
+  }
+
+  // Check all partition by attributes.
+  for (std::size_t partition_by_index = 0;
+       partition_by_index < partition_key_ids_.size();
+       ++partition_by_index) {
+    if (!partition_equal_comparators_[partition_by_index]->compareTypedValues(
+            tuple_accessor->getTypedValue(partition_key_ids_[partition_by_index]),
+            tuple_accessor->getTypedValueAtAbsolutePosition(
+                partition_key_ids_[partition_by_index], test_tuple_id))) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool WindowAggregationHandle::inWindow(
+    const ColumnVectorsValueAccessor *tuple_accessor,
+    const tuple_id test_tuple_id) const {
+  // If test tuple does not exist.
+  if (!samePartition(tuple_accessor, test_tuple_id)) {
+    return false;
+  }
+
+  tuple_id current_tuple_id = tuple_accessor->getCurrentPosition();
+
+  // If test tuple is the current tuple, then it is in the window.
+  if (test_tuple_id == current_tuple_id) {
+    return true;
+  }
+
+  // In ROWS mode, check the difference of tuple_id.
+  if (is_row_) {
+    if (num_preceding_ != -1 &&
+        test_tuple_id < current_tuple_id - num_preceding_) {
+      return false;
+    }
+
+    if (num_following_ != -1 &&
+        test_tuple_id > current_tuple_id + num_following_) {
+      return false;
+    }
+  } else {
+    // In RANGE mode, check the difference of first order key value.
+    // Get the test value.
+    const Type &long_type = TypeFactory::GetType(kLong, false);
+    TypedValue test_value =
+        range_add_operator_->applyToTypedValues(
+            tuple_accessor->getTypedValueAtAbsolutePosition(order_key_ids_[0], test_tuple_id),
+            long_type.makeZeroValue());
+
+    // NULL will be considered not in range.
+    if (test_value.isNull() ||
+        tuple_accessor->getTypedValue(order_key_ids_[0]).isNull()) {
+      return false;
+    }
+
+    // Get the boundary value if it is not UNBOUNDED.
+    if (num_preceding_ > -1) {
+      // num_preceding needs to be negated for calculation.
+      std::int64_t neg_num_preceding = -num_preceding_;
+      TypedValue start_boundary_value =
+          range_add_operator_->applyToTypedValues(
+              tuple_accessor->getTypedValue(order_key_ids_[0]),
+              long_type.makeValue(&neg_num_preceding));
+      if (!range_comparator_->compareTypedValues(start_boundary_value, test_value)) {
+        return false;
+      }
+    }
+
+    if (num_following_ > -1) {
+      TypedValue end_boundary_value =
+          range_add_operator_->applyToTypedValues(
+              tuple_accessor->getTypedValue(order_key_ids_[0]),
+              long_type.makeValue(&num_following_));
+      if (!range_comparator_->compareTypedValues(test_value, end_boundary_value)) {
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/expressions/window_aggregation/WindowAggregationHandle.hpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregationHandle.hpp b/expressions/window_aggregation/WindowAggregationHandle.hpp
index 65f95d9..41d1d96 100644
--- a/expressions/window_aggregation/WindowAggregationHandle.hpp
+++ b/expressions/window_aggregation/WindowAggregationHandle.hpp
@@ -27,19 +27,23 @@
 #include "catalog/CatalogRelationSchema.hpp"
 #include "catalog/CatalogTypedefs.hpp"
 #include "storage/StorageBlockInfo.hpp"
+#include "types/Type.hpp"
+#include "types/TypeFactory.hpp"
+#include "types/TypeID.hpp"
 #include "types/TypedValue.hpp"
 #include "types/containers/ColumnVector.hpp"
 #include "types/containers/ColumnVectorsValueAccessor.hpp"
 #include "types/operations/comparisons/Comparison.hpp"
 #include "types/operations/comparisons/ComparisonFactory.hpp"
 #include "types/operations/comparisons/ComparisonID.hpp"
+#include "types/operations/binary_operations/BinaryOperation.hpp"
+#include "types/operations/binary_operations/BinaryOperationFactory.hpp"
+#include "types/operations/binary_operations/BinaryOperationID.hpp"
 #include "utility/Macros.hpp"
 
 namespace quickstep {
 
-class InsertDestinationInterface;
 class Scalar;
-class StorageManager;
 class Type;
 class ValueAccessor;
 
@@ -55,27 +59,29 @@ class ValueAccessor;
  *
  * A WindowAggregationHandle is created by calling
  * WindowAggregateFunction::createHandle(). The WindowAggregationHandle object
- * provides methods that are used to actually compute the window aggregate,
- * storing intermediate results in WindowAggregationState objects.
+ * provides methods that are used to actually compute the window aggregate.
  *
  * The work flow for computing a window aggregate is:
- *     1. Create an initial state by createInitialState().
- *     2. One thread will handle all the computation, iterating from the first
+ *     1. One thread will handle all the computation, iterating from the first
  *        tuple to the last tuple. Note there will be two modes that could be
  *        used upon different situations:
  *        a. If the window aggregate is defined as accumulative, which are:
  *           i.  Functions applied to whole partition, such as rank(), ntile()
- *               and dense_rank().
+ *               and dense_rank(). (Not implemented yet).
  *           ii. The window frame is defined as "BETWEEN UNBOUNDED PRECEDING
  *               AND CURRENT ROW" or "BETWEEN CURRENT ROW AND UNBOUNDED
  *               FOLLOWING".
  *           Then, for functions except median, we could store some global
- *           values in the state without keeping all the tuple values around.
+ *           values without keeping all the tuple values around. For simplicity,
+ *           in avg(), count() and sum(), we treat the accumulative one as
+ *           sliding window since the time complexity does not vary.
  *        b. If the window frame is sliding, such as "BETWEEN 3 PRECEDING AND
- *           3 FOLLOWING", we have to store all the tuples in the state so that
+ *           3 FOLLOWING", we have to store all the tuples in the state (at
+ *           least two pointers to the start tuple and end tuple), so that
  *           we could know which values should be dropped as the window slides.
- *        For each computed value, generate a tuple store in the column vector.
- *     3. Insert the new column into the original relation and return.
+ *        For each computed value, generate a TypedValue and store it into a
+ *        ColumnVector for window aggregate values.
+ *     2. Return the result ColumnVector.
  *
  * TODO(Shixuan): Currently we don't support parallelization. The basic idea for
  * parallelization is to calculate the partial result inside each block. Each
@@ -96,37 +102,67 @@ class WindowAggregationHandle {
    *
    * @param block_accessors A pointer to the value accessor of block attributes.
    * @param arguments The ColumnVectors of arguments
-   * @param partition_by_ids The ids of partition keys.
-   * @param is_row True if the frame mode is ROWS, false if it is RANGE.
-   * @param num_preceding The number of rows/range that precedes the current row.
-   * @param num_following The number of rows/range that follows the current row.
    *
    * @return A ColumnVector of the calculated window aggregates.
    **/
   virtual ColumnVector* calculate(ColumnVectorsValueAccessor* block_accessors,
-                                  std::vector<ColumnVector*> &&arguments,
-                                  const std::vector<attribute_id> &partition_by_ids,
-                                  const bool is_row,
-                                  const std::int64_t num_preceding,
-                                  const std::int64_t num_following) const = 0;
+                                  const std::vector<ColumnVector*> &arguments) const = 0;
 
  protected:
   /**
    * @brief Constructor.
    *
-   * @param partition_key_types The Types of the partition key.
+   * @param partition_by_attributes A list of attributes used as partition key.
+   * @param order_by_attributes A list of attributes used as order key.
+   * @param is_row True if the frame mode is ROWS, false if RANGE.
+   * @param num_preceding The number of rows/range that precedes the current row.
+   * @param num_following The number of rows/range that follows the current row.
+   **/
+  WindowAggregationHandle(
+      const std::vector<std::unique_ptr<const Scalar>> &partition_by_attributes,
+      const std::vector<std::unique_ptr<const Scalar>> &order_by_attributes,
+      const bool is_row,
+      const std::int64_t num_preceding,
+      const std::int64_t num_following);
+
+  /**
+   * @brief Check if test tuple is in the same partition as the current
+   *        tuple in the accessor.
+   *
+   * @param tuple_accessor The ValueAccessor for tuples.
+   * @param test_tuple_id The id of the test tuple.
+   *
+   * @return True if test tuple is in the same partition as the current tuple in
+   *         the accessor, false if not.
    **/
-  explicit WindowAggregationHandle(
-      const std::vector<const Type*> &partition_key_types) {
-    // Comparison operators for checking if two tuples belong to the same partition.
-    for (const Type *partition_key_type : partition_key_types) {
-      equal_comparators_.emplace_back(
-          ComparisonFactory::GetComparison(ComparisonID::kEqual)
-              .makeUncheckedComparatorForTypes(*partition_key_type, *partition_key_type));
-    }
-  }
-
-  std::vector<std::unique_ptr<UncheckedComparator>> equal_comparators_;
+  bool samePartition(const ColumnVectorsValueAccessor *tuple_accessor,
+                     const tuple_id test_tuple_id) const;
+
+  /**
+   * @brief Check if test tuple is in the defined range.
+   *
+   * @param tuple_accessor The ValueAccessor for tuples.
+   * @param test_tuple_id The id of the test tuple.
+   *
+   * @return True if test tuple is in the defined window, false if not.
+   **/
+  bool inWindow(const ColumnVectorsValueAccessor *tuple_accessor,
+                const tuple_id test_tuple_id) const;
+
+  // IDs and comparators for partition keys.
+  std::vector<attribute_id> partition_key_ids_;
+  std::vector<std::unique_ptr<UncheckedComparator>> partition_equal_comparators_;
+
+  // IDs, type, Comparator and operator for frame boundary check in RANGE mode.
+  std::vector<attribute_id> order_key_ids_;
+  std::unique_ptr<UncheckedBinaryOperator> range_add_operator_;
+  std::unique_ptr<UncheckedComparator> range_comparator_;  // Less than or Equal
+  const Type* range_compare_type_;
+
+  // Window frame information.
+  const bool is_row_;
+  const std::int64_t num_preceding_;
+  const std::int64_t num_following_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(WindowAggregationHandle);

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/expressions/window_aggregation/WindowAggregationHandleAvg.cpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregationHandleAvg.cpp b/expressions/window_aggregation/WindowAggregationHandleAvg.cpp
index a6a10d4..e6a4b3f 100644
--- a/expressions/window_aggregation/WindowAggregationHandleAvg.cpp
+++ b/expressions/window_aggregation/WindowAggregationHandleAvg.cpp
@@ -24,8 +24,7 @@
 #include <vector>
 
 #include "catalog/CatalogTypedefs.hpp"
-#include "expressions/scalar/Scalar.hpp"
-#include "expressions/scalar/ScalarAttribute.hpp"
+#include "expressions/window_aggregation/WindowAggregationHandle.hpp"
 #include "storage/ValueAccessor.hpp"
 #include "types/Type.hpp"
 #include "types/TypeFactory.hpp"
@@ -42,14 +41,21 @@
 namespace quickstep {
 
 WindowAggregationHandleAvg::WindowAggregationHandleAvg(
-    const std::vector<const Type*> &partition_key_types,
-    const Type &type)
-    : WindowAggregationHandle(partition_key_types),
-      argument_type_(type) {
+    const std::vector<std::unique_ptr<const Scalar>> &partition_by_attributes,
+    const std::vector<std::unique_ptr<const Scalar>> &order_by_attributes,
+    const bool is_row,
+    const std::int64_t num_preceding,
+    const std::int64_t num_following,
+    const Type *argument_type)
+    : WindowAggregationHandle(partition_by_attributes,
+                              order_by_attributes,
+                              is_row,
+                              num_preceding,
+                              num_following) {
   // We sum Int as Long and Float as Double so that we have more headroom when
   // adding many values.
   TypeID type_id;
-  switch (type.getTypeID()) {
+  switch (argument_type->getTypeID()) {
     case kInt:
     case kLong:
       type_id = kLong;
@@ -59,7 +65,7 @@ WindowAggregationHandleAvg::WindowAggregationHandleAvg(
       type_id = kDouble;
       break;
     default:
-      type_id = type.getTypeID();
+      type_id = argument_type->getTypeID();
       break;
   }
 
@@ -76,7 +82,13 @@ WindowAggregationHandleAvg::WindowAggregationHandleAvg(
   // Add operator for summing argument values.
   fast_add_operator_.reset(
       BinaryOperationFactory::GetBinaryOperation(BinaryOperationID::kAdd)
-          .makeUncheckedBinaryOperatorForTypes(*sum_type_, argument_type_));
+          .makeUncheckedBinaryOperatorForTypes(*sum_type_, *argument_type));
+
+  // Subtract operator for dropping argument values off the window.
+  fast_subtract_operator_.reset(
+      BinaryOperationFactory::GetBinaryOperation(BinaryOperationID::kSubtract)
+          .makeUncheckedBinaryOperatorForTypes(*sum_type_, *argument_type));
+
   // Divide operator for dividing sum by count to get final average.
   divide_operator_.reset(
       BinaryOperationFactory::GetBinaryOperation(BinaryOperationID::kDivide)
@@ -85,11 +97,7 @@ WindowAggregationHandleAvg::WindowAggregationHandleAvg(
 
 ColumnVector* WindowAggregationHandleAvg::calculate(
     ColumnVectorsValueAccessor *tuple_accessor,
-    std::vector<ColumnVector*> &&arguments,
-    const std::vector<attribute_id> &partition_by_ids,
-    const bool is_row,
-    const std::int64_t num_preceding,
-    const std::int64_t num_following) const {
+    const std::vector<ColumnVector*> &arguments) const {
   DCHECK_EQ(1u, arguments.size());
   DCHECK(arguments[0]->isNative());
   DCHECK_EQ(static_cast<std::size_t>(tuple_accessor->getNumTuples()),
@@ -98,144 +106,69 @@ ColumnVector* WindowAggregationHandleAvg::calculate(
   // Initialize the output column and argument accessor.
   NativeColumnVector *window_aggregates =
       new NativeColumnVector(*result_type_, tuple_accessor->getNumTuples());
-  ColumnVectorsValueAccessor* argument_accessor = new ColumnVectorsValueAccessor();
+  ColumnVectorsValueAccessor *argument_accessor = new ColumnVectorsValueAccessor();
   argument_accessor->addColumn(arguments[0]);
 
+  // Initialize the information about the window.
+  TypedValue sum = sum_type_->makeZeroValue();
+  std::uint64_t count = 0;
+  tuple_id start_tuple_id = 0;  // The id of the first tuple in the window.
+  tuple_id end_tuple_id = 0;  // The id of the tuple that just passed the last
+                              // tuple in the window.
+
   // Create a window for each tuple and calculate the window aggregate.
   tuple_accessor->beginIteration();
   argument_accessor->beginIteration();
 
   while (tuple_accessor->next() && argument_accessor->next()) {
-    const TypedValue window_aggregate = this->calculateOneWindow(tuple_accessor,
-                                                                 argument_accessor,
-                                                                 partition_by_ids,
-                                                                 is_row,
-                                                                 num_preceding,
-                                                                 num_following);
-    window_aggregates->appendTypedValue(window_aggregate);
-  }
-
-  return window_aggregates;
-}
-
-TypedValue WindowAggregationHandleAvg::calculateOneWindow(
-    ColumnVectorsValueAccessor *tuple_accessor,
-    ColumnVectorsValueAccessor *argument_accessor,
-    const std::vector<attribute_id> &partition_by_ids,
-    const bool is_row,
-    const std::int64_t num_preceding,
-    const std::int64_t num_following) const {
-  // Initialize.
-  TypedValue sum = sum_type_->makeZeroValue();
-  TypedValue current_value = argument_accessor->getTypedValue(0);
-  std::uint64_t count = 0;
-
-  // Ignore the value if null.
-  if (!current_value.isNull()) {
-    sum = fast_add_operator_->applyToTypedValues(sum, current_value);
-    count++;
-  }
-
-  // Get the partition key for the current row.
-  std::vector<TypedValue> current_row_partition_key;
-  for (attribute_id partition_by_id : partition_by_ids) {
-    current_row_partition_key.push_back(
-        tuple_accessor->getTypedValue(partition_by_id));
-  }
-
-  // Get current position.
-  tuple_id current_tuple_id = tuple_accessor->getCurrentPositionVirtual();
-
-  // Find preceding tuples.
-  int count_preceding = 0;
-  tuple_id preceding_tuple_id = current_tuple_id;
-  while (num_preceding == -1 || count_preceding < num_preceding) {
-    preceding_tuple_id--;
-
-    // No more preceding tuples.
-    if (preceding_tuple_id < 0) {
-      break;
+    tuple_id current_tuple_id = tuple_accessor->getCurrentPosition();
+
+    // If current tuple is not in the same partition as the previous tuple,
+    // reset the window.
+    if (!samePartition(tuple_accessor, current_tuple_id - 1)) {
+      start_tuple_id = current_tuple_id;
+      end_tuple_id = current_tuple_id;
+      count = 0;
+      sum = sum_type_->makeZeroValue();
     }
 
-    // Get the partition keys and compare. If not the same partition as the
-    // current row, stop searching preceding tuples.
-    if (!samePartition(tuple_accessor,
-                       current_row_partition_key,
-                       preceding_tuple_id,
-                       partition_by_ids)) {
-      break;
+    // Drop tuples that will be out of the window from the beginning.
+    while (!inWindow(tuple_accessor, start_tuple_id)) {
+      TypedValue start_value =
+          argument_accessor->getTypedValueAtAbsolutePosition(0, start_tuple_id);
+      // Ignore the value if NULL.
+      if (!start_value.isNull()) {
+        sum = fast_subtract_operator_->applyToTypedValues(sum, start_value);
+        count--;
+      }
+
+      start_tuple_id++;
     }
 
-    // Actually count the element and do the calculation.
-    count_preceding++;
-    TypedValue preceding_value =
-        argument_accessor->getTypedValueAtAbsolutePosition(0, preceding_tuple_id);
+    // Add tuples that will be included by the window at the end.
+    while (inWindow(tuple_accessor, end_tuple_id)) {
+      TypedValue end_value =
+          argument_accessor->getTypedValueAtAbsolutePosition(0, end_tuple_id);
 
-    // Ignore the value if null.
-    if (!preceding_value.isNull()) {
-      sum = fast_add_operator_->applyToTypedValues(sum, preceding_value);
-      count++;
-    }
-  }
-
-  // Find following tuples.
-  int count_following = 0;
-  tuple_id following_tuple_id = current_tuple_id;
-  while (num_following == -1 || count_following < num_following) {
-    following_tuple_id++;
+      // Ignore the value if NULL.
+      if (!end_value.isNull()) {
+        sum = fast_add_operator_->applyToTypedValues(sum, end_value);
+        count++;
+      }
 
-    // No more following tuples.
-    if (following_tuple_id == tuple_accessor->getNumTuples()) {
-      break;
+      end_tuple_id++;
     }
 
-    // Get the partition keys and compare. If not the same partition as the
-    // current row, stop searching preceding tuples.
-    if (!samePartition(tuple_accessor,
-                       current_row_partition_key,
-                       following_tuple_id,
-                       partition_by_ids)) {
-      break;
+    // If all values are NULLs, return NULL; Otherwise, return the quotient.
+    if (count == 0) {
+      window_aggregates->appendTypedValue(result_type_->makeNullValue());
+    } else {
+      window_aggregates->appendTypedValue(
+          divide_operator_->applyToTypedValues(sum, TypedValue(static_cast<double>(count))));
     }
-
-    // Actually count the element and do the calculation.
-    count_following++;
-    TypedValue following_value =
-        argument_accessor->getTypedValueAtAbsolutePosition(0, following_tuple_id);
-
-    // Ignore the value if null.
-    if (!following_value.isNull()) {
-      sum = fast_add_operator_->applyToTypedValues(sum, following_value);
-      count++;
-    }
-  }
-
-  // If all values are NULLs, return NULL; Otherwise, return the quotient.
-  if (count == 0) {
-    return result_type_->makeNullValue();
-  } else {
-    return divide_operator_->applyToTypedValues(sum,
-                                                TypedValue(static_cast<double>(count)));
   }
-}
 
-bool WindowAggregationHandleAvg::samePartition(
-    const ColumnVectorsValueAccessor *tuple_accessor,
-    const std::vector<TypedValue> &current_row_partition_key,
-    const tuple_id boundary_tuple_id,
-    const std::vector<attribute_id> &partition_by_ids) const {
-  for (std::size_t partition_by_index = 0;
-       partition_by_index < partition_by_ids.size();
-       ++partition_by_index) {
-    if (!equal_comparators_[partition_by_index]->compareTypedValues(
-            current_row_partition_key[partition_by_index],
-            tuple_accessor->getTypedValueAtAbsolutePosition(
-                partition_by_ids[partition_by_index], boundary_tuple_id))) {
-      return false;
-    }
-  }
-
-  return true;
+  return window_aggregates;
 }
 
 }  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/expressions/window_aggregation/WindowAggregationHandleAvg.hpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/WindowAggregationHandleAvg.hpp b/expressions/window_aggregation/WindowAggregationHandleAvg.hpp
index 5b41779..f7f2e4d 100644
--- a/expressions/window_aggregation/WindowAggregationHandleAvg.hpp
+++ b/expressions/window_aggregation/WindowAggregationHandleAvg.hpp
@@ -31,7 +31,6 @@
 #include "types/Type.hpp"
 #include "types/TypedValue.hpp"
 #include "types/operations/binary_operations/BinaryOperation.hpp"
-#include "types/operations/comparisons/Comparison.hpp"
 #include "utility/Macros.hpp"
 
 #include "glog/logging.h"
@@ -54,11 +53,7 @@ class WindowAggregationHandleAvg : public WindowAggregationHandle {
   ~WindowAggregationHandleAvg() override {}
 
   ColumnVector* calculate(ColumnVectorsValueAccessor* block_accessors,
-                          std::vector<ColumnVector*> &&arguments,
-                          const std::vector<attribute_id> &partition_by_ids,
-                          const bool is_row,
-                          const std::int64_t num_preceding,
-                          const std::int64_t num_following) const override;
+                          const std::vector<ColumnVector*> &arguments) const override;
 
  private:
   friend class WindowAggregateFunctionAvg;
@@ -66,29 +61,25 @@ class WindowAggregationHandleAvg : public WindowAggregationHandle {
   /**
    * @brief Constructor.
    *
-   * @param partition_key_types The Types of the partition key.
-   * @param type Type of the avg value.
+   * @param partition_by_attributes A list of attributes used as partition key.
+   * @param order_by_attributes A list of attributes used as order key.
+   * @param is_row True if the frame mode is ROWS, false if RANGE.
+   * @param num_preceding The number of rows/range that precedes the current row.
+   * @param num_following The number of rows/range that follows the current row.
+   * @param argument_type Type of the argument.
    **/
-  WindowAggregationHandleAvg(const std::vector<const Type*> &partition_key_types,
-                             const Type &type);
-
-  TypedValue calculateOneWindow(
-      ColumnVectorsValueAccessor *tuple_accessor,
-      ColumnVectorsValueAccessor *argument_accessor,
-      const std::vector<attribute_id> &partition_by_ids,
+  WindowAggregationHandleAvg(
+      const std::vector<std::unique_ptr<const Scalar>> &partition_by_attributes,
+      const std::vector<std::unique_ptr<const Scalar>> &order_by_attributes,
       const bool is_row,
       const std::int64_t num_preceding,
-      const std::int64_t num_following) const;
-
-  bool samePartition(const ColumnVectorsValueAccessor *tuple_accessor,
-                     const std::vector<TypedValue> &current_row_partition_key,
-                     const tuple_id boundary_tuple_id,
-                     const std::vector<attribute_id> &partition_by_ids) const;
+      const std::int64_t num_following,
+      const Type *argument_type);
 
-  const Type &argument_type_;
   const Type *sum_type_;
   const Type *result_type_;
   std::unique_ptr<UncheckedBinaryOperator> fast_add_operator_;
+  std::unique_ptr<UncheckedBinaryOperator> fast_subtract_operator_;
   std::unique_ptr<UncheckedBinaryOperator> divide_operator_;
 
   DISALLOW_COPY_AND_ASSIGN(WindowAggregationHandleAvg);

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/expressions/window_aggregation/tests/WindowAggregationHandleAvg_unittest.cpp
----------------------------------------------------------------------
diff --git a/expressions/window_aggregation/tests/WindowAggregationHandleAvg_unittest.cpp b/expressions/window_aggregation/tests/WindowAggregationHandleAvg_unittest.cpp
index c044a98..cb58083 100644
--- a/expressions/window_aggregation/tests/WindowAggregationHandleAvg_unittest.cpp
+++ b/expressions/window_aggregation/tests/WindowAggregationHandleAvg_unittest.cpp
@@ -23,11 +23,13 @@
 #include <memory>
 #include <vector>
 
+#include "catalog/CatalogAttribute.hpp"
 #include "catalog/CatalogTypedefs.hpp"
+#include "expressions/scalar/Scalar.hpp"
+#include "expressions/scalar/ScalarAttribute.hpp"
 #include "expressions/window_aggregation/WindowAggregateFunction.hpp"
 #include "expressions/window_aggregation/WindowAggregateFunctionFactory.hpp"
 #include "expressions/window_aggregation/WindowAggregationHandle.hpp"
-#include "expressions/window_aggregation/WindowAggregationHandleAvg.hpp"
 #include "expressions/window_aggregation/WindowAggregationID.hpp"
 #include "storage/ValueAccessor.hpp"
 #include "types/CharType.hpp"
@@ -58,6 +60,9 @@ namespace {
   constexpr int kNullInterval = 25;
   constexpr int kNumPreceding = 2;
   constexpr int kNumFollowing = 2;
+  constexpr int kPartitionKeyIndex = 0;
+  constexpr int kOrderKeyIndex = 1;
+  constexpr int kNumTuplesPerOrderKey = 2;
 
 }  // namespace
 
@@ -65,12 +70,27 @@ namespace {
 class WindowAggregationHandleAvgTest : public::testing::Test {
  protected:
   // Handle initialization.
-  void initializeHandle(const Type &argument_type) {
+  WindowAggregationHandle* initializeHandle(const Type &argument_type,
+                                            const bool is_row = true,
+                                            const std::int64_t num_preceding = -1,
+                                            const std::int64_t num_following = 0) {
     const WindowAggregateFunction &function =
         WindowAggregateFunctionFactory::Get(WindowAggregationID::kAvg);
+    const Type &int_type = TypeFactory::GetType(kInt, false);
+    std::vector<std::unique_ptr<const Scalar>> partition_by_attributes;
+    std::vector<std::unique_ptr<const Scalar>> order_by_attributes;
+    partition_by_attributes.emplace_back(
+        new ScalarAttribute(CatalogAttribute(nullptr, "partition_key", int_type, kPartitionKeyIndex)));
+    order_by_attributes.emplace_back(
+        new ScalarAttribute(CatalogAttribute(nullptr, "order_key", int_type, kOrderKeyIndex)));
     std::vector<const Type*> partition_key_types(1, &TypeFactory::GetType(kInt, false));
-    handle_avg_.reset(function.createHandle(std::vector<const Type*>(1, &argument_type),
-                                            std::move(partition_key_types)));
+
+    return function.createHandle(std::vector<const Type*>(1, &argument_type),
+                                 partition_by_attributes,
+                                 order_by_attributes,
+                                 is_row,
+                                 num_preceding,
+                                 num_following);
   }
 
   // Test canApplyToTypes().
@@ -117,24 +137,25 @@ class WindowAggregationHandleAvgTest : public::testing::Test {
 
   template <typename GenericType, typename OutputType = DoubleType>
   void checkWindowAggregationAvgGeneric() {
-    const GenericType &type = GenericType::Instance(true);
-    initializeHandle(type);
-
     // Create argument, partition key and cpptype vectors.
     std::vector<typename GenericType::cpptype*> argument_cpp_vector;
     argument_cpp_vector.reserve(kNumTuples);
     ColumnVector *argument_type_vector =
         createArgumentGeneric<GenericType>(&argument_cpp_vector);
     NativeColumnVector *partition_key_vector =
-        new NativeColumnVector(IntType::InstanceNonNullable(), kNumTuples + 2);
+        new NativeColumnVector(IntType::InstanceNonNullable(), kNumTuples);
+    NativeColumnVector *order_key_vector =
+        new NativeColumnVector(IntType::InstanceNonNullable(), kNumTuples);
 
     for (int i = 0; i < kNumTuples; ++i) {
       partition_key_vector->appendTypedValue(TypedValue(i / kNumTuplesPerPartition));
+      order_key_vector->appendTypedValue(TypedValue(i / kNumTuplesPerOrderKey));
     }
 
     // Create tuple ValueAccessor.
     ColumnVectorsValueAccessor *tuple_accessor = new ColumnVectorsValueAccessor();
     tuple_accessor->addColumn(partition_key_vector);
+    tuple_accessor->addColumn(order_key_vector);
     tuple_accessor->addColumn(argument_type_vector);
 
     // Test UNBOUNDED PRECEDING AND CURRENT ROW.
@@ -182,45 +203,95 @@ class WindowAggregationHandleAvgTest : public::testing::Test {
                        const std::vector<typename GenericType::cpptype*> &argument_cpp_vector) {
     std::vector<ColumnVector*> arguments;
     arguments.push_back(argument_type_vector);
-    // The partition key index is 0.
-    std::vector<attribute_id> partition_key(1, 0);
 
-    ColumnVector *result =
-        handle_avg_->calculate(tuple_accessor,
-                               std::move(arguments),
-                               partition_key,
-                               true  /* is_row */,
-                               -1  /* num_preceding: UNBOUNDED PRECEDING */,
-                               0  /* num_following: CURRENT ROW */);
+    // Check ROWS mode.
+    WindowAggregationHandle *rows_handle =
+        initializeHandle(GenericType::Instance(true),
+                         true  /* is_row */,
+                         -1  /* num_preceding: UNBOUNDED PRECEDING */,
+                         0  /* num_following: CURRENT ROW */);
+    ColumnVector *rows_result =
+        rows_handle->calculate(tuple_accessor, arguments);
 
     // Get the cpptype result.
-    std::vector<typename OutputType::cpptype*> result_cpp_vector;
-    typename GenericType::cpptype sum;
-    int count;
+    std::vector<typename OutputType::cpptype*> rows_result_cpp_vector;
+    typename GenericType::cpptype rows_sum;
+    int rows_count;
     for (std::size_t i = 0; i < argument_cpp_vector.size(); ++i) {
       // Start of new partition
       if (i % kNumTuplesPerPartition == 0) {
-        SetDataType(0, &sum);
-        count = 0;
+        SetDataType(0, &rows_sum);
+        rows_count = 0;
       }
 
       typename GenericType::cpptype *value = argument_cpp_vector[i];
       if (value != nullptr) {
-        sum += *value;
-        count++;
+        rows_sum += *value;
+        rows_count++;
       }
 
-      if (count == 0) {
-        result_cpp_vector.push_back(nullptr);
+      if (rows_count == 0) {
+        rows_result_cpp_vector.push_back(nullptr);
       } else {
         typename OutputType::cpptype *result_cpp_value =
             new typename OutputType::cpptype;
-        *result_cpp_value = static_cast<typename OutputType::cpptype>(sum) / count;
-        result_cpp_vector.push_back(result_cpp_value);
+        *result_cpp_value = static_cast<typename OutputType::cpptype>(rows_sum) / rows_count;
+        rows_result_cpp_vector.push_back(result_cpp_value);
+      }
+    }
+
+    CheckAvgValues(rows_result_cpp_vector, rows_result);
+
+    // Check RANGE mode.
+    WindowAggregationHandle *range_handle =
+        initializeHandle(GenericType::Instance(true),
+                         false  /* is_row */,
+                         -1  /* num_preceding: UNBOUNDED PRECEDING */,
+                         0  /* num_following: CURRENT ROW */);
+    ColumnVector *range_result =
+        range_handle->calculate(tuple_accessor, arguments);
+
+    // Get the cpptype result.
+    std::vector<typename OutputType::cpptype*> range_result_cpp_vector;
+    typename GenericType::cpptype range_sum;
+    int range_count;
+    std::size_t current_tuple = 0;
+    while (current_tuple < kNumTuples) {
+      // Start of new partition
+      if (current_tuple % kNumTuplesPerPartition == 0) {
+        SetDataType(0, &range_sum);
+        range_count = 0;
+      }
+
+      // We have to consider following tuples with the same order key value.
+      std::size_t next_tuple = current_tuple;
+      while (next_tuple < kNumTuples &&
+             next_tuple / kNumTuplesPerPartition == current_tuple / kNumTuplesPerPartition &&
+             next_tuple / kNumTuplesPerOrderKey == current_tuple / kNumTuplesPerOrderKey) {
+        typename GenericType::cpptype *value = argument_cpp_vector[next_tuple];
+        if (value != nullptr) {
+          range_sum += *value;
+          range_count++;
+        }
+
+        next_tuple++;
+      }
+
+      // Calculate the result cpp value.
+      typename OutputType::cpptype *result_cpp_value = nullptr;
+      if (range_count != 0) {
+        result_cpp_value = new typename OutputType::cpptype;
+        *result_cpp_value = static_cast<typename OutputType::cpptype>(range_sum) / range_count;
+      }
+
+      // Add the result values to the tuples with in the same order key value.
+      while (current_tuple != next_tuple) {
+        range_result_cpp_vector.push_back(result_cpp_value);
+        current_tuple++;
       }
     }
 
-    CheckAvgValues(result_cpp_vector, result);
+    CheckAvgValues(range_result_cpp_vector, range_result);
   }
 
   template <typename GenericType, typename OutputType>
@@ -229,20 +300,19 @@ class WindowAggregationHandleAvgTest : public::testing::Test {
                           const std::vector<typename GenericType::cpptype*> &argument_cpp_vector) {
     std::vector<ColumnVector*> arguments;
     arguments.push_back(argument_type_vector);
-    // The partition key index is 0.
-    std::vector<attribute_id> partition_key(1, 0);
 
-    ColumnVector *result =
-        handle_avg_->calculate(tuple_accessor,
-                               std::move(arguments),
-                               partition_key,
-                               true  /* is_row */,
-                               kNumPreceding,
-                               kNumFollowing);
+    // Check ROWS mode.
+    WindowAggregationHandle *rows_handle =
+        initializeHandle(GenericType::Instance(true),
+                         true  /* is_row */,
+                         kNumPreceding,
+                         kNumFollowing);
+    ColumnVector *rows_result =
+        rows_handle->calculate(tuple_accessor, arguments);
 
     // Get the cpptype result.
     // For each value, calculate all surrounding values in the window.
-    std::vector<typename OutputType::cpptype*> result_cpp_vector;
+    std::vector<typename OutputType::cpptype*> rows_result_cpp_vector;
 
     for (std::size_t i = 0; i < argument_cpp_vector.size(); ++i) {
       typename GenericType::cpptype sum;
@@ -281,19 +351,81 @@ class WindowAggregationHandleAvgTest : public::testing::Test {
       }
 
       if (count == 0) {
-        result_cpp_vector.push_back(nullptr);
+        rows_result_cpp_vector.push_back(nullptr);
       } else {
         typename OutputType::cpptype *result_cpp_value =
             new typename OutputType::cpptype;
         *result_cpp_value = static_cast<typename OutputType::cpptype>(sum) / count;
-        result_cpp_vector.push_back(result_cpp_value);
+        rows_result_cpp_vector.push_back(result_cpp_value);
       }
     }
 
-    CheckAvgValues(result_cpp_vector, result);
-  }
+    CheckAvgValues(rows_result_cpp_vector, rows_result);
+
+    // Check RANGE mode.
+    WindowAggregationHandle *range_handle =
+        initializeHandle(GenericType::Instance(true),
+                         false  /* is_row */,
+                         kNumPreceding,
+                         kNumFollowing);
+    ColumnVector *range_result =
+        range_handle->calculate(tuple_accessor, arguments);
+
+    // Get the cpptype result.
+    // For each value, calculate all surrounding values in the window.
+    std::vector<typename OutputType::cpptype*> range_result_cpp_vector;
+
+    for (std::size_t i = 0; i < argument_cpp_vector.size(); ++i) {
+      typename GenericType::cpptype sum;
+      SetDataType(0, &sum);
+      int count = 0;
+
+      if (argument_cpp_vector[i] != nullptr) {
+        sum += *argument_cpp_vector[i];
+        count++;
+      }
+
+      int preceding_bound = i / kNumTuplesPerOrderKey - kNumPreceding;
+      for (std::size_t precede = 1; precede <= kNumTuples; ++precede) {
+        // Not in range or the same partition.
+        if (i / kNumTuplesPerPartition != (i - precede) / kNumTuplesPerPartition ||
+            static_cast<int>((i - precede) / kNumTuplesPerOrderKey) < preceding_bound) {
+          break;
+        }
+
+        if (argument_cpp_vector[i - precede] != nullptr) {
+          sum += *argument_cpp_vector[i - precede];
+          count++;
+        }
+      }
+
+      int following_bound = i / kNumTuplesPerOrderKey + kNumFollowing;
+      for (int follow = 1; follow <= kNumTuples; ++follow) {
+        // Not in range or the same partition.
+        if (i + follow >= kNumTuples ||
+            i / kNumTuplesPerPartition != (i + follow) / kNumTuplesPerPartition ||
+            static_cast<int>((i + follow) / kNumTuplesPerOrderKey) > following_bound) {
+          break;
+        }
+
+        if (argument_cpp_vector[i + follow] != nullptr) {
+          sum += *argument_cpp_vector[i + follow];
+          count++;
+        }
+      }
+
+      if (count == 0) {
+        rows_result_cpp_vector.push_back(nullptr);
+      } else {
+        typename OutputType::cpptype *result_cpp_value =
+            new typename OutputType::cpptype;
+        *result_cpp_value = static_cast<typename OutputType::cpptype>(sum) / count;
+        range_result_cpp_vector.push_back(result_cpp_value);
+      }
+    }
 
-  std::unique_ptr<WindowAggregationHandle> handle_avg_;
+    CheckAvgValues(range_result_cpp_vector, range_result);
+  }
 };
 
 template <>

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/query_optimizer/ExecutionGenerator.cpp
----------------------------------------------------------------------
diff --git a/query_optimizer/ExecutionGenerator.cpp b/query_optimizer/ExecutionGenerator.cpp
index ce21ade..88103df 100644
--- a/query_optimizer/ExecutionGenerator.cpp
+++ b/query_optimizer/ExecutionGenerator.cpp
@@ -1663,7 +1663,7 @@ void ExecutionGenerator::convertWindowAggregate(
       std::static_pointer_cast<const E::WindowAggregateFunction>(
           named_window_aggregate_expression->expression());
 
-  // Set the AggregateFunction.
+  // Set the WindowAggregateFunction.
   window_aggr_state_proto->mutable_function()->MergeFrom(
       window_aggregate_function->window_aggregate().getProto());
 
@@ -1683,6 +1683,15 @@ void ExecutionGenerator::convertWindowAggregate(
         ->MergeFrom(concretized_partition_by_attribute->getProto());
   }
 
+  // Set order keys.
+  for (const E::ScalarPtr &order_by_attribute
+      : window_info.order_by_attributes) {
+    unique_ptr<const Scalar> concretized_order_by_attribute(
+        order_by_attribute->concretize(attribute_substitution_map_));
+    window_aggr_state_proto->add_order_by_attributes()
+        ->MergeFrom(concretized_order_by_attribute->getProto());
+  }
+
   // Set window frame info.
   if (window_info.frame_info == nullptr) {
     // If the frame is not specified, use the default setting:

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/query_optimizer/resolver/Resolver.cpp
----------------------------------------------------------------------
diff --git a/query_optimizer/resolver/Resolver.cpp b/query_optimizer/resolver/Resolver.cpp
index c224388..46808bf 100644
--- a/query_optimizer/resolver/Resolver.cpp
+++ b/query_optimizer/resolver/Resolver.cpp
@@ -814,9 +814,9 @@ L::LogicalPtr Resolver::resolveInsertSelection(
       cast_expressions.emplace_back(selection_attributes[aid]);
     } else {
       // TODO(jianqiao): implement Cast operation for non-numeric types.
-      if (destination_type.getSuperTypeID() == Type::kNumeric
-          && selection_type.getSuperTypeID() == Type::kNumeric
-          && destination_type.isSafelyCoercibleFrom(selection_type)) {
+      if (destination_type.getSuperTypeID() == Type::SuperTypeID::kNumeric &&
+          selection_type.getSuperTypeID() == Type::SuperTypeID::kNumeric &&
+          destination_type.isSafelyCoercibleFrom(selection_type)) {
         // Add cast operation
         const E::AttributeReferencePtr attr = selection_attributes[aid];
         const E::ExpressionPtr cast_expr =
@@ -1691,6 +1691,19 @@ E::WindowInfo Resolver::resolveWindow(const ParseWindow &parse_window,
   // Resolve window frame
   if (parse_window.frame_info() != nullptr) {
     const quickstep::ParseFrameInfo *parse_frame_info = parse_window.frame_info();
+    // For FRAME mode, the first attribute in ORDER BY must be numeric.
+    // TODO(Shixuan): Time-related types should also be supported. To handle
+    // this, some changes in the parser needs to be done since the time range
+    // should be specified with time units. Also, UNBOUNDED flags might be
+    // needed because -1 might not make sense in this case.
+    if (!parse_frame_info->is_row &&
+        (order_by_attributes.empty() ||
+         order_by_attributes[0]->getValueType().getSuperTypeID() != Type::SuperTypeID::kNumeric)) {
+      THROW_SQL_ERROR_AT(&parse_window)
+          << "A numeric attribute should be specified as the first ORDER BY "
+          << "attribute in FRAME mode";
+    }
+
     frame_info = new E::WindowFrameInfo(parse_frame_info->is_row,
                                         parse_frame_info->num_preceding,
                                         parse_frame_info->num_following);

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/query_optimizer/tests/execution_generator/Select.test
----------------------------------------------------------------------
diff --git a/query_optimizer/tests/execution_generator/Select.test b/query_optimizer/tests/execution_generator/Select.test
index 30a3c39..6bada6c 100644
--- a/query_optimizer/tests/execution_generator/Select.test
+++ b/query_optimizer/tests/execution_generator/Select.test
@@ -1025,17 +1025,40 @@ WINDOW w AS
 +--------------------+-----------+------------------------+
 ==
 
-# Currently this is not supported, an empty table will be returned.
-SELECT int_col, sum(float_col) OVER
-(PARTITION BY char_col, long_col
- ORDER BY double_col DESC NULLS LAST, int_col ASC NULLS FIRST
- RANGE BETWEEN 3 PRECEDING AND 3 FOLLOWING)
+SELECT float_col, double_col, avg(double_col) OVER
+(ORDER BY float_col DESC NULLS LAST, int_col ASC NULLS FIRST
+ RANGE BETWEEN 2 PRECEDING AND 2 FOLLOWING)
 FROM test;
 --
-+-----------+------------------------+
-|int_col    |sum(float_col)          |
-+-----------+------------------------+
-+-----------+------------------------+
++---------------+------------------------+------------------------+
+|float_col      |double_col              |avg(double_col)         |
++---------------+------------------------+------------------------+
+|     4.89897966|      117.57550765359254|     -5.2010907233390986|
+|     4.79583168|     -110.30412503619254|     -3.3458568752518572|
+|     4.69041586|      103.18914671611546|     -3.3458568752518572|
+|      4.5825758|     -96.234089594072643|     -4.2942570191393745|
+|     4.47213602|                    NULL|     -4.2942570191393745|
+|     4.35889912|      -82.81907992727281|     -3.1771278735018194|
+|      4.2426405|      76.367532368147124|     -3.1771278735018194|
+|     4.12310553|     -70.092795635500224|     -3.6217507631683268|
+|              4|                      64|     -3.0100796703699935|
+|     3.87298346|     -58.094750193111253|     -3.0100796703699935|
+|      3.7416575|       52.38320341483518|     -3.0100796703699935|
+|     3.60555124|     -46.872166581031856|     -3.1193833079868254|
+|     3.46410155|      41.569219381653056|     -3.1193833079868254|
+|     3.31662488|       -36.4828726939094|     -2.8361542397614437|
+|      3.1622777|                    NULL|     -2.8361542397614437|
+|              3|                     -27|     -2.7526926834086507|
+|     2.82842708|      22.627416997969522|     -8.4826069851706123|
+|     2.64575124|     -18.520259177452136|     -9.0010404404476727|
+|     2.44948983|      14.696938456699067|     -4.1547599319129516|
+|     2.23606801|     -11.180339887498949|     -4.2708832009567148|
+|              2|                       8|     0.11724429467951912|
+|     1.73205078|      -5.196152422706632|     -4.7108157334609286|
+|     1.41421354|      2.8284271247461903|     -5.1226841602152344|
+|              1|                      -1|      -1.638218767582549|
+|              0|                    NULL|      1.1580686755098886|
++---------------+------------------------+------------------------+
 ==
 
 SELECT sum(avg(int_col) OVER w) FROM test

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/storage/WindowAggregationOperationState.cpp
----------------------------------------------------------------------
diff --git a/storage/WindowAggregationOperationState.cpp b/storage/WindowAggregationOperationState.cpp
index 0cdfc1a..49fa44d 100644
--- a/storage/WindowAggregationOperationState.cpp
+++ b/storage/WindowAggregationOperationState.cpp
@@ -56,15 +56,13 @@ WindowAggregationOperationState::WindowAggregationOperationState(
     const WindowAggregateFunction *window_aggregate_function,
     std::vector<std::unique_ptr<const Scalar>> &&arguments,
     const std::vector<std::unique_ptr<const Scalar>> &partition_by_attributes,
+    const std::vector<std::unique_ptr<const Scalar>> &order_by_attributes,
     const bool is_row,
     const std::int64_t num_preceding,
     const std::int64_t num_following,
     StorageManager *storage_manager)
     : input_relation_(input_relation),
       arguments_(std::move(arguments)),
-      is_row_(is_row),
-      num_preceding_(num_preceding),
-      num_following_(num_following),
       storage_manager_(storage_manager) {
   // Get the Types of this window aggregate's arguments so that we can create an
   // AggregationHandle.
@@ -76,18 +74,14 @@ WindowAggregationOperationState::WindowAggregationOperationState(
   // Check if window aggregate function could apply to the arguments.
   DCHECK(window_aggregate_function->canApplyToTypes(argument_types));
 
-  // IDs and types of partition keys.
-  std::vector<const Type*> partition_by_types;
-  for (const std::unique_ptr<const Scalar> &partition_by_attribute : partition_by_attributes) {
-    partition_by_ids_.push_back(
-        partition_by_attribute->getAttributeIdForValueAccessor());
-    partition_by_types.push_back(&partition_by_attribute->getType());
-  }
-
   // Create the handle and initial state.
   window_aggregation_handle_.reset(
-      window_aggregate_function->createHandle(std::move(argument_types),
-                                              std::move(partition_by_types)));
+      window_aggregate_function->createHandle(argument_types,
+                                              partition_by_attributes,
+                                              order_by_attributes,
+                                              is_row,
+                                              num_preceding,
+                                              num_following));
 }
 
 WindowAggregationOperationState* WindowAggregationOperationState::ReconstructFromProto(
@@ -113,6 +107,15 @@ WindowAggregationOperationState* WindowAggregationOperationState::ReconstructFro
         database));
   }
 
+  std::vector<std::unique_ptr<const Scalar>> order_by_attributes;
+  for (int attribute_idx = 0;
+       attribute_idx < proto.order_by_attributes_size();
+       ++attribute_idx) {
+    order_by_attributes.emplace_back(ScalarFactory::ReconstructFromProto(
+        proto.order_by_attributes(attribute_idx),
+        database));
+  }
+
   const bool is_row = proto.is_row();
   const std::int64_t num_preceding = proto.num_preceding();
   const std::int64_t num_following = proto.num_following();
@@ -121,6 +124,7 @@ WindowAggregationOperationState* WindowAggregationOperationState::ReconstructFro
                                              &WindowAggregateFunctionFactory::ReconstructFromProto(proto.function()),
                                              std::move(arguments),
                                              partition_by_attributes,
+                                             order_by_attributes,
                                              is_row,
                                              num_preceding,
                                              num_following,
@@ -160,6 +164,15 @@ bool WindowAggregationOperationState::ProtoIsValid(const serialization::WindowAg
     }
   }
 
+  for (int attribute_idx = 0;
+       attribute_idx < proto.order_by_attributes_size();
+       ++attribute_idx) {
+    if (!ScalarFactory::ProtoIsValid(proto.order_by_attributes(attribute_idx),
+                                     database)) {
+      return false;
+    }
+  }
+
   if (proto.num_preceding() < -1 || proto.num_following() < -1) {
     return false;
   }
@@ -177,14 +190,6 @@ void WindowAggregationOperationState::windowAggregateBlocks(
     return;
   }
 
-  // TODO(Shixuan): RANGE frame mode should be supported to make SQL grammar
-  // work. This will need Order Key to be passed so that we know where the
-  // window should start and end.
-  if (!is_row_) {
-    std::cout << "Currently we don't support RANGE frame mode :(\n";
-    return;
-  }
-
   // Get the total number of tuples.
   int num_tuples = 0;
   for (const block_id block_idx : block_ids) {
@@ -226,7 +231,11 @@ void WindowAggregationOperationState::windowAggregateBlocks(
                                      block->getIndices(),
                                      block->getIndicesConsistent());
     ValueAccessor *tuple_accessor = tuple_block.createValueAccessor();
-    ColumnVectorsValueAccessor *argument_accessor = new ColumnVectorsValueAccessor();
+    ColumnVectorsValueAccessor *argument_accessor = nullptr;
+    if (!arguments_.empty()) {
+      argument_accessor = new ColumnVectorsValueAccessor();
+    }
+
     for (const std::unique_ptr<const Scalar> &argument : arguments_) {
       argument_accessor->addColumn(argument->getAllValues(tuple_accessor,
                                                           &sub_block_ref));
@@ -235,9 +244,15 @@ void WindowAggregationOperationState::windowAggregateBlocks(
     InvokeOnAnyValueAccessor(tuple_accessor,
                              [&] (auto *tuple_accessor) -> void {  // NOLINT(build/c++11)
       tuple_accessor->beginIteration();
-      argument_accessor->beginIteration();
+      if (argument_accessor != nullptr) {
+        argument_accessor->beginIteration();
+      }
+
+      while (tuple_accessor->next()) {
+        if (argument_accessor != nullptr) {
+          argument_accessor->next();
+        }
 
-      while (tuple_accessor->next() && argument_accessor->next()) {
         for (std::size_t attr_id = 0; attr_id < attribute_vecs.size(); ++attr_id) {
           ColumnVector *attr_vec = attribute_vecs[attr_id];
           if (attr_vec->isNative()) {
@@ -275,11 +290,7 @@ void WindowAggregationOperationState::windowAggregateBlocks(
   // Do actual calculation in handle.
   ColumnVector *window_aggregates =
       window_aggregation_handle_->calculate(all_blocks_accessor,
-                                            std::move(argument_vecs),
-                                            partition_by_ids_,
-                                            is_row_,
-                                            num_preceding_,
-                                            num_following_);
+                                            argument_vecs);
 
   all_blocks_accessor->addColumn(window_aggregates);
   output_destination->bulkInsertTuples(all_blocks_accessor);

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/storage/WindowAggregationOperationState.hpp
----------------------------------------------------------------------
diff --git a/storage/WindowAggregationOperationState.hpp b/storage/WindowAggregationOperationState.hpp
index 9792a99..726b102 100644
--- a/storage/WindowAggregationOperationState.hpp
+++ b/storage/WindowAggregationOperationState.hpp
@@ -57,6 +57,7 @@ class WindowAggregationOperationState {
    *                                   computed.
    * @param arguments A list of argument expressions to that aggregate.
    * @param partition_by_attributes A list of window partition key.
+   * @param order_by_attributes A list of window order key.
    * @param is_row True if the window frame is calculated by ROW, false if it is
    *               calculated by RANGE.
    * @param num_preceding The number of rows/range for the tuples preceding the
@@ -69,6 +70,7 @@ class WindowAggregationOperationState {
                                   const WindowAggregateFunction *window_aggregate_function,
                                   std::vector<std::unique_ptr<const Scalar>> &&arguments,
                                   const std::vector<std::unique_ptr<const Scalar>> &partition_by_attributes,
+                                  const std::vector<std::unique_ptr<const Scalar>> &order_by_attributes,
                                   const bool is_row,
                                   const std::int64_t num_preceding,
                                   const std::int64_t num_following,
@@ -120,13 +122,6 @@ class WindowAggregationOperationState {
   const std::vector<block_id> block_ids_;
   std::unique_ptr<WindowAggregationHandle> window_aggregation_handle_;
   std::vector<std::unique_ptr<const Scalar>> arguments_;
-  std::vector<attribute_id> partition_by_ids_;
-
-  // Frame info.
-  const bool is_row_;
-  const std::int64_t num_preceding_;
-  const std::int64_t num_following_;
-
   StorageManager *storage_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(WindowAggregationOperationState);

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/d0172fde/storage/WindowAggregationOperationState.proto
----------------------------------------------------------------------
diff --git a/storage/WindowAggregationOperationState.proto b/storage/WindowAggregationOperationState.proto
index d888461..f879713 100644
--- a/storage/WindowAggregationOperationState.proto
+++ b/storage/WindowAggregationOperationState.proto
@@ -30,4 +30,5 @@ message WindowAggregationOperationState {
   required bool is_row = 5;
   required int64 num_preceding = 6;  // -1 means UNBOUNDED PRECEDING.
   required int64 num_following = 7;  // -1 means UNBOUNDED FOLLOWING.
+  repeated Scalar order_by_attributes = 8;
 }


[08/30] incubator-quickstep git commit: Add an example to the quickstart guide

Posted by ra...@apache.org.
Add an example to the quickstart guide


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

Branch: refs/heads/quickstep-28-29
Commit: 2cc78802b4f57d1d8dbf498672a5661a57570ff0
Parents: 8181f15
Author: Jignesh Patel <jm...@hotmail.com>
Authored: Thu Jul 14 01:27:05 2016 -0500
Committer: Jignesh Patel <jm...@hotmail.com>
Committed: Thu Jul 14 01:27:05 2016 -0500

----------------------------------------------------------------------
 README.md | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 73 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/2cc78802/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index bdfb7fc..bf9ed8d 100644
--- a/README.md
+++ b/README.md
@@ -40,8 +40,79 @@ And, it is **open source!**
 4. Checkout the dependencies: ```git submodule update```
 5. Go into the build directory: ```cd build```
 6. Create the Makefile: ```cmake -D CMAKE_BUILD_TYPE=Release ..```  
-7. Build: ```make -j4```. Note you may replace the 4 with the number of cores on your machine.
-8. Start quickstep: ```./quickstep_cli_shell --initialize_db=true```. You can now fire SQL queries. To quit, you can type in ```quit;``` Your data is stored in the directory ```qsstor```
+7. Build: ```make -j4```. Note you may replace the 4 with the number of cores 
+   on your machine.
+8. Start quickstep: ```./quickstep_cli_shell --initialize_db=true```. You can 
+   now fire SQL queries. To quit, you can type in ```quit;``` Your data is 
+   stored in the directory ```qsstor```. Note the next time you start Quickstep,
+   you can omit the ``` --initialize_db``` flag (as the database has already 
+   been initialized), and simply start Quickstep as: ```./quickstep_cli_shell```.
+   There are also a number of optional flags that you can specify, and to see
+   the full list, you can type in: ```./quickstep_cli_shell --help```
+9. Next let us load some data and fire some queries. A few points to note:
+The SQL surface of Quickstep is small (it will grow over time). The
+traditional SQL CREATE TABLE and SELECT statements work. The data types
+that are supported include INTEGER, FLOAT, DOUBLE, VARCHAR, CHAR, DATE,
+and DATETIME. Quickstep also does not have support for NULLS or keys (yet).
+Let create two tables by typing into the Quickstep shell (which you opened
+in the step above.
+
+```
+CREATE TABLE Weather (cid INTEGER, recordDate DATE, highTemperature FLOAT, lowTemperature FLOAT);
+```
+
+and then,
+
+```
+CREATE TABLE City (cid Integer, name VARCHAR(80), state CHAR(2));
+```
+
+10. Next, let us insert some tuples in these two tables.
+    ```
+    INSERT INTO City VALUES (1, 'Madison', 'WI');
+    INSERT INTO City VALUES (2, 'Palo Alto', 'CA');
+    INSERT INTO Weather VALUES (1, '2015-11-1', 50, 30);
+    INSERT INTO Weather VALUES (1, '2015-11-2', 51, 32);
+    INSERT INTO Weather VALUES (2, '2015-11-1', 60, 50);
+    ```
+
+11. Now we can issue SQL queries such as:
+  a. Find all weather records for California:
+  ```
+  SELECT * FROM WEATHER W, City C WHERE C.cid = W.cid AND C.state = 'CA';
+  ```
+
+  b. Find the min and max temperature for each city, printing the ```cid```:
+  ```
+  SELECT cid, MIN(lowTemperature), MAX(highTemperature) FROM Weather GROUP BY cid;
+  ```
+
+  c. Find the min and max temperature for each city using a nested query, and 
+     printing thie city name:
+  ```
+  SELECT * FROM City C, (SELECT cid, MIN(lowTemperature), MAX(highTemperature) FROM Weather GROUP BY cid) AS T WHERE C.cid = T.cid;
+  ```
+
+12. Quickstep also supports a COPY TABLE command. If you want to try that, then
+    from a separate shell file type in the following: 
+
+    ```
+    echo "3|2015-11-3|49|29" > /tmp/tmp.tbl
+    echo "3|2015-11-4|48|28" >> /tmp/tmp.tbl
+    echo "3|2015-11-5|47|27" >> /tmp/tmp.tbl
+    ```
+   
+    Then, load this new data by typing the following SQL in the Quickstep shell:
+
+    ```
+    COPY Weather FROM '/tmp/tmp.tbl' WITH (DELIMITER '|');
+    ```
+
+    Now, you have loaded three more tuples into the Weather table, and you can
+    fire the SQL queries above again against this modified database.
+
+    Remember, to quit Quickstep, you can type in ```quit;``` into the Quickstep
+    shell.
 
 
 ## Additional pointers


[03/30] incubator-quickstep git commit: Renamed Foreman related classes.

Posted by ra...@apache.org.
Renamed Foreman related classes.


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

Branch: refs/heads/quickstep-28-29
Commit: 303e51eeb507d0270c15013f8bb30da2cf6c8314
Parents: 04c8224
Author: Zuyu Zhang <zu...@apache.org>
Authored: Thu Jul 7 14:13:19 2016 -0500
Committer: Zuyu Zhang <zu...@apache.org>
Committed: Thu Jul 7 14:18:46 2016 -0500

----------------------------------------------------------------------
 CMakeLists.txt                                  |   2 +-
 cli/CMakeLists.txt                              |   1 -
 cli/CommandExecutor.cpp                         |   1 -
 cli/CommandExecutor.hpp                         |   1 -
 cli/QuickstepCli.cpp                            |  21 +-
 cli/tests/CMakeLists.txt                        |   2 +-
 cli/tests/CommandExecutorTestRunner.cpp         |   2 +-
 cli/tests/CommandExecutorTestRunner.hpp         |  15 +-
 query_execution/CMakeLists.txt                  |  28 +-
 query_execution/Foreman.cpp                     | 255 ------------------
 query_execution/Foreman.hpp                     | 140 ----------
 query_execution/ForemanBase.hpp                 |  85 ++++++
 query_execution/ForemanLite.hpp                 |  85 ------
 query_execution/ForemanSingleNode.cpp           | 256 +++++++++++++++++++
 query_execution/ForemanSingleNode.hpp           | 140 ++++++++++
 query_optimizer/tests/CMakeLists.txt            |   2 +-
 .../tests/ExecutionGeneratorTestRunner.cpp      |   2 +-
 .../tests/ExecutionGeneratorTestRunner.hpp      |  15 +-
 18 files changed, 527 insertions(+), 526 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/303e51ee/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 20e1fb9..0e5d3de 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -748,7 +748,7 @@ target_link_libraries(quickstep_cli_shell
                       quickstep_parser_ParseStatement
                       quickstep_parser_SqlParserWrapper
                       quickstep_queryexecution_AdmitRequestMessage
-                      quickstep_queryexecution_Foreman
+                      quickstep_queryexecution_ForemanSingleNode
                       quickstep_queryexecution_QueryContext
                       quickstep_queryexecution_QueryExecutionTypedefs
                       quickstep_queryexecution_QueryExecutionUtil

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/303e51ee/cli/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt
index 44ec223..9637055 100644
--- a/cli/CMakeLists.txt
+++ b/cli/CMakeLists.txt
@@ -89,7 +89,6 @@ target_link_libraries(quickstep_cli_CommandExecutor
                       quickstep_cli_PrintToScreen
                       quickstep_parser_ParseStatement
                       quickstep_parser_SqlParserWrapper
-                      quickstep_queryexecution_Foreman
                       quickstep_queryoptimizer_QueryHandle
                       quickstep_queryoptimizer_QueryPlan
                       quickstep_queryoptimizer_QueryProcessor

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/303e51ee/cli/CommandExecutor.cpp
----------------------------------------------------------------------
diff --git a/cli/CommandExecutor.cpp b/cli/CommandExecutor.cpp
index 7083ef5..8acfae8 100644
--- a/cli/CommandExecutor.cpp
+++ b/cli/CommandExecutor.cpp
@@ -34,7 +34,6 @@
 #include "parser/ParseStatement.hpp"
 #include "parser/ParseString.hpp"
 #include "parser/SqlParserWrapper.hpp"
-#include "query_execution/Foreman.hpp"
 #include "query_optimizer/QueryHandle.hpp"
 #include "query_optimizer/QueryPlan.hpp"
 #include "query_optimizer/QueryProcessor.hpp"

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/303e51ee/cli/CommandExecutor.hpp
----------------------------------------------------------------------
diff --git a/cli/CommandExecutor.hpp b/cli/CommandExecutor.hpp
index 3435aeb..19d03e6 100644
--- a/cli/CommandExecutor.hpp
+++ b/cli/CommandExecutor.hpp
@@ -32,7 +32,6 @@ namespace tmb { class MessageBus; }
 namespace quickstep {
 
 class CatalogDatabase;
-class Foreman;
 class ParseStatement;
 class QueryProcessor;
 class StorageManager;

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/303e51ee/cli/QuickstepCli.cpp
----------------------------------------------------------------------
diff --git a/cli/QuickstepCli.cpp b/cli/QuickstepCli.cpp
index 02a55a0..68a3599 100644
--- a/cli/QuickstepCli.cpp
+++ b/cli/QuickstepCli.cpp
@@ -58,7 +58,7 @@ typedef quickstep::LineReaderDumb LineReaderImpl;
 #include "parser/ParseStatement.hpp"
 #include "parser/SqlParserWrapper.hpp"
 #include "query_execution/AdmitRequestMessage.hpp"
-#include "query_execution/Foreman.hpp"
+#include "query_execution/ForemanSingleNode.hpp"
 #include "query_execution/QueryExecutionTypedefs.hpp"
 #include "query_execution/QueryExecutionUtil.hpp"
 #include "query_execution/Worker.hpp"
@@ -104,7 +104,7 @@ using quickstep::AdmitRequestMessage;
 using quickstep::CatalogRelation;
 using quickstep::DefaultsConfigurator;
 using quickstep::DropRelation;
-using quickstep::Foreman;
+using quickstep::ForemanSingleNode;
 using quickstep::InputParserUtil;
 using quickstep::MessageBusImpl;
 using quickstep::MessageStyle;
@@ -353,14 +353,15 @@ int main(int argc, char* argv[]) {
                                    worker_client_ids,
                                    worker_numa_nodes);
 
-  Foreman foreman(main_thread_client_id,
-                  &worker_directory,
-                  &bus,
-                  query_processor->getDefaultDatabase(),
-                  query_processor->getStorageManager(),
-                  -1,  // Don't pin the Foreman thread.
-                  num_numa_nodes_system,
-                  quickstep::FLAGS_profile_and_report_workorder_perf);
+  ForemanSingleNode foreman(
+      main_thread_client_id,
+      &worker_directory,
+      &bus,
+      query_processor->getDefaultDatabase(),
+      query_processor->getStorageManager(),
+      -1,  // Don't pin the Foreman thread.
+      num_numa_nodes_system,
+      quickstep::FLAGS_profile_and_report_workorder_perf);
 
   // Start the worker threads.
   for (Worker &worker : workers) {

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/303e51ee/cli/tests/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/cli/tests/CMakeLists.txt b/cli/tests/CMakeLists.txt
index d177d6c..7da56d1 100644
--- a/cli/tests/CMakeLists.txt
+++ b/cli/tests/CMakeLists.txt
@@ -33,7 +33,7 @@ target_link_libraries(quickstep_cli_tests_CommandExecutorTest
                       quickstep_parser_ParseStatement
                       quickstep_parser_SqlParserWrapper
                       quickstep_queryexecution_AdmitRequestMessage
-                      quickstep_queryexecution_Foreman
+                      quickstep_queryexecution_ForemanSingleNode
                       quickstep_queryexecution_QueryContext
                       quickstep_queryexecution_QueryExecutionTypedefs
                       quickstep_queryexecution_QueryExecutionUtil

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/303e51ee/cli/tests/CommandExecutorTestRunner.cpp
----------------------------------------------------------------------
diff --git a/cli/tests/CommandExecutorTestRunner.cpp b/cli/tests/CommandExecutorTestRunner.cpp
index 794f7e1..bd7082f 100644
--- a/cli/tests/CommandExecutorTestRunner.cpp
+++ b/cli/tests/CommandExecutorTestRunner.cpp
@@ -27,7 +27,7 @@
 #include "cli/PrintToScreen.hpp"
 #include "parser/ParseStatement.hpp"
 #include "query_execution/AdmitRequestMessage.hpp"
-#include "query_execution/Foreman.hpp"
+#include "query_execution/ForemanSingleNode.hpp"
 #include "query_execution/QueryExecutionTypedefs.hpp"
 #include "query_execution/Worker.hpp"
 #include "query_optimizer/ExecutionGenerator.hpp"

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/303e51ee/cli/tests/CommandExecutorTestRunner.hpp
----------------------------------------------------------------------
diff --git a/cli/tests/CommandExecutorTestRunner.hpp b/cli/tests/CommandExecutorTestRunner.hpp
index 8fb5b65..69692ae 100644
--- a/cli/tests/CommandExecutorTestRunner.hpp
+++ b/cli/tests/CommandExecutorTestRunner.hpp
@@ -25,7 +25,7 @@
 #include <vector>
 
 #include "parser/SqlParserWrapper.hpp"
-#include "query_execution/Foreman.hpp"
+#include "query_execution/ForemanSingleNode.hpp"
 #include "query_execution/QueryExecutionTypedefs.hpp"
 #include "query_execution/QueryExecutionUtil.hpp"
 #include "query_execution/Worker.hpp"
@@ -77,11 +77,12 @@ class CommandExecutorTestRunner : public TextBasedTestRunner {
 
     workers_.reset(new WorkerDirectory(1 /* number of workers */,
                                        worker_client_ids, numa_nodes));
-    foreman_.reset(new Foreman(main_thread_client_id_,
-                               workers_.get(),
-                               &bus_,
-                               test_database_loader_.catalog_database(),
-                               test_database_loader_.storage_manager()));
+    foreman_.reset(
+        new ForemanSingleNode(main_thread_client_id_,
+                              workers_.get(),
+                              &bus_,
+                              test_database_loader_.catalog_database(),
+                              test_database_loader_.storage_manager()));
 
     foreman_->start();
     worker_->start();
@@ -104,7 +105,7 @@ class CommandExecutorTestRunner : public TextBasedTestRunner {
   tmb::client_id main_thread_client_id_;
 
   MessageBusImpl bus_;
-  std::unique_ptr<Foreman> foreman_;
+  std::unique_ptr<ForemanSingleNode> foreman_;
   std::unique_ptr<Worker> worker_;
 
   std::unique_ptr<WorkerDirectory> workers_;

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/303e51ee/query_execution/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/query_execution/CMakeLists.txt b/query_execution/CMakeLists.txt
index b031a44..2be451c 100644
--- a/query_execution/CMakeLists.txt
+++ b/query_execution/CMakeLists.txt
@@ -1,6 +1,6 @@
 #   Copyright 2011-2015 Quickstep Technologies LLC.
 #   Copyright 2015-2016 Pivotal Software, Inc.
-#   Copyright 2016, Quickstep Research Group, Computer Sciences Department, 
+#   Copyright 2016, Quickstep Research Group, Computer Sciences Department,
 #     University of Wisconsin\u2014Madison.
 #
 #   Licensed under the Apache License, Version 2.0 (the "License");
@@ -32,8 +32,8 @@ if (ENABLE_DISTRIBUTED)
   add_library(quickstep_queryexecution_BlockLocator BlockLocator.cpp BlockLocator.hpp)
 endif()
 add_library(quickstep_queryexecution_AdmitRequestMessage ../empty_src.cpp AdmitRequestMessage.hpp)
-add_library(quickstep_queryexecution_Foreman Foreman.cpp Foreman.hpp)
-add_library(quickstep_queryexecution_ForemanLite ../empty_src.cpp ForemanLite.hpp)
+add_library(quickstep_queryexecution_ForemanBase ../empty_src.cpp ForemanBase.hpp)
+add_library(quickstep_queryexecution_ForemanSingleNode ForemanSingleNode.cpp ForemanSingleNode.hpp)
 add_library(quickstep_queryexecution_PolicyEnforcer PolicyEnforcer.cpp PolicyEnforcer.hpp)
 add_library(quickstep_queryexecution_QueryContext QueryContext.cpp QueryContext.hpp)
 add_library(quickstep_queryexecution_QueryContext_proto
@@ -69,11 +69,15 @@ if (ENABLE_DISTRIBUTED)
                         quickstep_utility_Macros
                         tmb)
 endif()
-target_link_libraries(quickstep_queryexecution_Foreman
-                      ${GFLAGS_LIB_NAME} 
+target_link_libraries(quickstep_queryexecution_ForemanBase
+                      glog
+                      quickstep_threading_Thread
+                      quickstep_utility_Macros
+                      tmb)
+target_link_libraries(quickstep_queryexecution_ForemanSingleNode
                       glog
                       quickstep_queryexecution_AdmitRequestMessage
-                      quickstep_queryexecution_ForemanLite
+                      quickstep_queryexecution_ForemanBase
                       quickstep_queryexecution_PolicyEnforcer
                       quickstep_queryexecution_QueryExecutionTypedefs
                       quickstep_queryexecution_QueryExecutionUtil
@@ -82,12 +86,8 @@ target_link_libraries(quickstep_queryexecution_Foreman
                       quickstep_threading_ThreadUtil
                       quickstep_utility_EqualsAnyConstant
                       quickstep_utility_Macros
-                      tmb)
-target_link_libraries(quickstep_queryexecution_ForemanLite
-                      glog
-                      quickstep_threading_Thread
-                      quickstep_utility_Macros
-                      tmb)
+                      tmb
+                      ${GFLAGS_LIB_NAME})
 target_link_libraries(quickstep_queryexecution_PolicyEnforcer
                       ${GFLAGS_LIB_NAME}
                       glog
@@ -199,8 +199,8 @@ target_link_libraries(quickstep_queryexecution_WorkerSelectionPolicy
 add_library(quickstep_queryexecution ../empty_src.cpp QueryExecutionModule.hpp)
 target_link_libraries(quickstep_queryexecution
                       quickstep_queryexecution_AdmitRequestMessage
-                      quickstep_queryexecution_Foreman
-                      quickstep_queryexecution_ForemanLite
+                      quickstep_queryexecution_ForemanBase
+                      quickstep_queryexecution_ForemanSingleNode
                       quickstep_queryexecution_PolicyEnforcer
                       quickstep_queryexecution_QueryContext
                       quickstep_queryexecution_QueryContext_proto

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/303e51ee/query_execution/Foreman.cpp
----------------------------------------------------------------------
diff --git a/query_execution/Foreman.cpp b/query_execution/Foreman.cpp
deleted file mode 100644
index 98146e2..0000000
--- a/query_execution/Foreman.cpp
+++ /dev/null
@@ -1,255 +0,0 @@
-/**
- *   Copyright 2011-2015 Quickstep Technologies LLC.
- *   Copyright 2015-2016 Pivotal Software, Inc.
- *
- *   Licensed 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.
- **/
-
-#include "query_execution/Foreman.hpp"
-
-#include <cstddef>
-#include <cstdio>
-#include <memory>
-#include <tuple>
-#include <utility>
-#include <vector>
-
-#include "query_execution/AdmitRequestMessage.hpp"
-#include "query_execution/QueryExecutionTypedefs.hpp"
-#include "query_execution/QueryExecutionUtil.hpp"
-#include "query_execution/WorkerDirectory.hpp"
-#include "query_execution/WorkerMessage.hpp"
-#include "threading/ThreadUtil.hpp"
-#include "utility/EqualsAnyConstant.hpp"
-#include "utility/Macros.hpp"
-
-#include "gflags/gflags.h"
-#include "glog/logging.h"
-
-#include "tmb/message_bus.h"
-#include "tmb/tagged_message.h"
-
-using std::move;
-using std::size_t;
-using std::unique_ptr;
-using std::vector;
-
-namespace quickstep {
-
-DEFINE_uint64(min_load_per_worker, 2, "The minimum load defined as the number "
-              "of pending work orders for the worker. This information is used "
-              "by the Foreman to assign work orders to worker threads");
-
-Foreman::Foreman(const tmb::client_id main_thread_client_id,
-                 WorkerDirectory *worker_directory,
-                 tmb::MessageBus *bus,
-                 CatalogDatabaseLite *catalog_database,
-                 StorageManager *storage_manager,
-                 const int cpu_id,
-                 const size_t num_numa_nodes,
-                 const bool profile_individual_workorders)
-    : ForemanLite(bus, cpu_id),
-      main_thread_client_id_(main_thread_client_id),
-      worker_directory_(DCHECK_NOTNULL(worker_directory)),
-      catalog_database_(DCHECK_NOTNULL(catalog_database)),
-      storage_manager_(DCHECK_NOTNULL(storage_manager)) {
-  const std::vector<QueryExecutionMessageType> sender_message_types{
-      kPoisonMessage,
-      kRebuildWorkOrderMessage,
-      kWorkOrderMessage,
-      kWorkloadCompletionMessage};
-
-  for (const auto message_type : sender_message_types) {
-    bus_->RegisterClientAsSender(foreman_client_id_, message_type);
-  }
-
-  const std::vector<QueryExecutionMessageType> receiver_message_types{
-      kAdmitRequestMessage,
-      kCatalogRelationNewBlockMessage,
-      kDataPipelineMessage,
-      kPoisonMessage,
-      kRebuildWorkOrderCompleteMessage,
-      kWorkOrderFeedbackMessage,
-      kWorkOrdersAvailableMessage,
-      kWorkOrderCompleteMessage};
-
-  for (const auto message_type : receiver_message_types) {
-    bus_->RegisterClientAsReceiver(foreman_client_id_, message_type);
-  }
-
-  policy_enforcer_.reset(new PolicyEnforcer(
-      foreman_client_id_,
-      num_numa_nodes,
-      catalog_database_,
-      storage_manager_,
-      worker_directory_,
-      bus_,
-      profile_individual_workorders));
-}
-
-void Foreman::run() {
-  if (cpu_id_ >= 0) {
-    // We can pin the foreman thread to a CPU if specified.
-    ThreadUtil::BindToCPU(cpu_id_);
-  }
-
-  // Event loop
-  for (;;) {
-    // Receive() causes this thread to sleep until next message is received.
-    const AnnotatedMessage annotated_msg =
-        bus_->Receive(foreman_client_id_, 0, true);
-    const TaggedMessage &tagged_message = annotated_msg.tagged_message;
-    const tmb::message_type_id message_type = tagged_message.message_type();
-    switch (message_type) {
-      case kCatalogRelationNewBlockMessage:  // Fall through
-      case kDataPipelineMessage:
-      case kRebuildWorkOrderCompleteMessage:
-      case kWorkOrderCompleteMessage:
-      case kWorkOrderFeedbackMessage:
-      case kWorkOrdersAvailableMessage: {
-        policy_enforcer_->processMessage(tagged_message);
-        break;
-      }
-
-      case kAdmitRequestMessage: {
-        const AdmitRequestMessage *msg =
-            static_cast<const AdmitRequestMessage *>(tagged_message.message());
-        const vector<QueryHandle *> &query_handles = msg->getQueryHandles();
-
-        DCHECK(!query_handles.empty());
-        bool all_queries_admitted = true;
-        if (query_handles.size() == 1u) {
-          all_queries_admitted =
-              policy_enforcer_->admitQuery(query_handles.front());
-        } else {
-          all_queries_admitted = policy_enforcer_->admitQueries(query_handles);
-        }
-        if (!all_queries_admitted) {
-          LOG(WARNING) << "The scheduler could not admit all the queries";
-          // TODO(harshad) - Inform the main thread about the failure.
-        }
-        break;
-      }
-      case kPoisonMessage: {
-        if (policy_enforcer_->hasQueries()) {
-          LOG(WARNING) << "Foreman thread exiting while some queries are "
-                          "under execution or waiting to be admitted";
-        }
-        return;
-      }
-      default:
-        LOG(FATAL) << "Unknown message type to Foreman";
-    }
-
-    if (canCollectNewMessages(message_type)) {
-      vector<unique_ptr<WorkerMessage>> new_messages;
-      policy_enforcer_->getWorkerMessages(&new_messages);
-      dispatchWorkerMessages(new_messages);
-    }
-
-    // We check again, as some queries may produce zero work orders and finish
-    // their execution.
-    if (!policy_enforcer_->hasQueries()) {
-      // Signal the main thread that there are no queries to be executed.
-      // Currently the message doesn't have any real content.
-      const int dummy_payload = 0;
-      TaggedMessage completion_tagged_message(
-          &dummy_payload, sizeof(dummy_payload), kWorkloadCompletionMessage);
-      const tmb::MessageBus::SendStatus send_status =
-          QueryExecutionUtil::SendTMBMessage(
-              bus_,
-              foreman_client_id_,
-              main_thread_client_id_,
-              move(completion_tagged_message));
-      CHECK(send_status == tmb::MessageBus::SendStatus::kOK)
-          << "Message could not be sent from Foreman with TMB client ID "
-          << foreman_client_id_ << " to main thread with TMB client ID"
-          << main_thread_client_id_;
-    }
-  }
-}
-
-bool Foreman::canCollectNewMessages(const tmb::message_type_id message_type) {
-  if (QUICKSTEP_EQUALS_ANY_CONSTANT(message_type,
-                                    kCatalogRelationNewBlockMessage,
-                                    kWorkOrderFeedbackMessage)) {
-    return false;
-  } else if (worker_directory_->getLeastLoadedWorker().second <=
-             FLAGS_min_load_per_worker) {
-    // If the least loaded worker has only one pending work order, we should
-    // collect new messages and dispatch them.
-    return true;
-  } else {
-    return false;
-  }
-}
-
-void Foreman::dispatchWorkerMessages(const vector<unique_ptr<WorkerMessage>> &messages) {
-  for (const auto &message : messages) {
-    DCHECK(message != nullptr);
-    const int recipient_worker_thread_index = message->getRecipientHint();
-    if (recipient_worker_thread_index != WorkerMessage::kInvalidRecipientIndexHint) {
-      sendWorkerMessage(static_cast<size_t>(recipient_worker_thread_index),
-                        *message);
-      worker_directory_->incrementNumQueuedWorkOrders(recipient_worker_thread_index);
-    } else {
-      const size_t least_loaded_worker_thread_index = worker_directory_->getLeastLoadedWorker().first;
-      sendWorkerMessage(least_loaded_worker_thread_index, *message);
-      worker_directory_->incrementNumQueuedWorkOrders(least_loaded_worker_thread_index);
-    }
-  }
-}
-
-void Foreman::sendWorkerMessage(const size_t worker_thread_index,
-                                const WorkerMessage &message) {
-  tmb::message_type_id type;
-  if (message.getType() == WorkerMessage::WorkerMessageType::kRebuildWorkOrder) {
-    type = kRebuildWorkOrderMessage;
-  } else if (message.getType() == WorkerMessage::WorkerMessageType::kWorkOrder) {
-    type = kWorkOrderMessage;
-  } else {
-    FATAL_ERROR("Invalid WorkerMessageType");
-  }
-  TaggedMessage worker_tagged_message(&message, sizeof(message), type);
-
-  const tmb::MessageBus::SendStatus send_status =
-      QueryExecutionUtil::SendTMBMessage(bus_,
-                                         foreman_client_id_,
-                                         worker_directory_->getClientID(worker_thread_index),
-                                         move(worker_tagged_message));
-  CHECK(send_status == tmb::MessageBus::SendStatus::kOK) <<
-      "Message could not be sent from Foreman with TMB client ID "
-      << foreman_client_id_ << " to Foreman with TMB client ID "
-      << worker_directory_->getClientID(worker_thread_index);
-}
-
-void Foreman::printWorkOrderProfilingResults(const std::size_t query_id,
-                                             std::FILE *out) const {
-  const std::vector<
-      std::tuple<std::size_t, std::size_t, std::size_t>>
-      &recorded_times = policy_enforcer_->getProfilingResults(query_id);
-  fputs("Query ID,Worker ID,NUMA Socket,Operator ID,Time (microseconds)\n", out);
-  for (auto workorder_entry : recorded_times) {
-    // Note: Index of the "worker thread index" in the tuple is 0.
-    const std::size_t worker_id = std::get<0>(workorder_entry);
-    fprintf(out,
-            "%lu,%lu,%d,%lu,%lu\n",
-            query_id,
-            worker_id,
-            worker_directory_->getNUMANode(worker_id),
-            std::get<1>(workorder_entry),  // Operator ID.
-            std::get<2>(workorder_entry));  // Time.
-  }
-}
-
-}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/303e51ee/query_execution/Foreman.hpp
----------------------------------------------------------------------
diff --git a/query_execution/Foreman.hpp b/query_execution/Foreman.hpp
deleted file mode 100644
index 7be57e7..0000000
--- a/query_execution/Foreman.hpp
+++ /dev/null
@@ -1,140 +0,0 @@
-/**
- *   Copyright 2011-2015 Quickstep Technologies LLC.
- *   Copyright 2015-2016 Pivotal Software, Inc.
- *
- *   Licensed 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.
- **/
-
-#ifndef QUICKSTEP_QUERY_EXECUTION_FOREMAN_HPP_
-#define QUICKSTEP_QUERY_EXECUTION_FOREMAN_HPP_
-
-#include <cstddef>
-#include <cstdio>
-#include <memory>
-#include <vector>
-
-#include "query_execution/ForemanLite.hpp"
-#include "query_execution/PolicyEnforcer.hpp"
-#include "utility/Macros.hpp"
-
-#include "tmb/id_typedefs.h"
-#include "tmb/message_bus.h"
-
-namespace quickstep {
-
-class CatalogDatabaseLite;
-class StorageManager;
-class WorkerDirectory;
-class WorkerMessage;
-
-/** \addtogroup QueryExecution
- *  @{
- */
-
-/**
- * @brief The Foreman receives queries from the main thread, messages from the
- *        policy enforcer and dispatches the work to worker threads. It also
- *        receives work completion messages from workers.
- **/
-class Foreman final : public ForemanLite {
- public:
-  /**
-   * @brief Constructor.
-   *
-   * @param main_thread_client_id The TMB client ID of the main thread.
-   * @param worker_directory The worker directory.
-   * @param bus A pointer to the TMB.
-   * @param catalog_database The catalog database where this query is executed.
-   * @param storage_manager The StorageManager to use.
-   * @param cpu_id The ID of the CPU to which the Foreman thread can be pinned.
-   * @param num_numa_nodes The number of NUMA nodes in the system.
-   * @param profile_individual_workorders Whether every workorder's execution
-   *        be profiled or not.
-   *
-   * @note If cpu_id is not specified, Foreman thread can be possibly moved
-   *       around on different CPUs by the OS.
-  **/
-  Foreman(const tmb::client_id main_thread_client_id,
-          WorkerDirectory *worker_directory,
-          tmb::MessageBus *bus,
-          CatalogDatabaseLite *catalog_database,
-          StorageManager *storage_manager,
-          const int cpu_id = -1,
-          const std::size_t num_numa_nodes = 1,
-          const bool profile_individual_workorders = false);
-
-  ~Foreman() override {}
-
-  /**
-   * @brief Print the results of profiling individual work orders for a given
-   *        query.
-   *
-   * TODO(harshad) - Add the name of the operator to the output.
-   * TODO(harshad) - Add the CPU core ID of the operator to the output. This
-   * will require modifying the WorkerDirectory to remember worker affinities.
-   * Until then, the users can refer to the worker_affinities provided to the
-   * cli to infer the CPU core ID where a given worker is pinned.
-   *
-   * @param query_id The ID of the query for which the results are to be printed.
-   * @param out The file stream.
-   **/
-  void printWorkOrderProfilingResults(const std::size_t query_id,
-                                      std::FILE *out) const;
-
- protected:
-  void run() override;
-
- private:
-  /**
-   * @brief Dispatch schedulable WorkOrders, wrapped in WorkerMessages to the
-   *        worker threads.
-   *
-   * @param messages The messages to be dispatched.
-   **/
-  void dispatchWorkerMessages(
-      const std::vector<std::unique_ptr<WorkerMessage>> &messages);
-
-  /**
-   * @brief Send the given message to the specified worker.
-   *
-   * @param worker_thread_index The logical index of the recipient worker thread
-   *        in WorkerDirectory.
-   * @param message The WorkerMessage to be sent.
-   **/
-  void sendWorkerMessage(const std::size_t worker_thread_index,
-                         const WorkerMessage &message);
-
-  /**
-   * @brief Check if we can collect new messages from the PolicyEnforcer.
-   *
-   * @param message_type The type of the last received message.
-   **/
-  bool canCollectNewMessages(const tmb::message_type_id message_type);
-
-  const tmb::client_id main_thread_client_id_;
-
-  WorkerDirectory *worker_directory_;
-
-  CatalogDatabaseLite *catalog_database_;
-  StorageManager *storage_manager_;
-
-  std::unique_ptr<PolicyEnforcer> policy_enforcer_;
-
-  DISALLOW_COPY_AND_ASSIGN(Foreman);
-};
-
-/** @} */
-
-}  // namespace quickstep
-
-#endif  // QUICKSTEP_QUERY_EXECUTION_FOREMAN_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/303e51ee/query_execution/ForemanBase.hpp
----------------------------------------------------------------------
diff --git a/query_execution/ForemanBase.hpp b/query_execution/ForemanBase.hpp
new file mode 100644
index 0000000..274b8fc
--- /dev/null
+++ b/query_execution/ForemanBase.hpp
@@ -0,0 +1,85 @@
+/**
+ *   Copyright 2016 Pivotal Software, Inc.
+ *
+ *   Licensed 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.
+ **/
+
+#ifndef QUICKSTEP_QUERY_EXECUTION_FOREMAN_BASE_HPP_
+#define QUICKSTEP_QUERY_EXECUTION_FOREMAN_BASE_HPP_
+
+#include "threading/Thread.hpp"
+#include "utility/Macros.hpp"
+
+#include "glog/logging.h"
+
+#include "tmb/id_typedefs.h"
+#include "tmb/message_bus.h"
+
+namespace quickstep {
+
+/** \addtogroup QueryExecution
+ *  @{
+ */
+
+/**
+ * @brief A base class that Foreman implements. This class is used to derive
+ *        for implementations for both the single-node and distributed versions.
+ **/
+class ForemanBase : public Thread {
+ public:
+  /**
+   * @brief Constructor.
+   *
+   * @param bus A pointer to the TMB.
+   * @param cpu_id The ID of the CPU to which the Foreman thread can be pinned.
+   *
+   * @note If cpu_id is not specified, Foreman thread can be possibly moved
+   *       around on different CPUs by the OS.
+  **/
+  ForemanBase(tmb::MessageBus *bus,
+              const int cpu_id)
+      : bus_(DCHECK_NOTNULL(bus)),
+        cpu_id_(cpu_id) {
+    foreman_client_id_ = bus_->Connect();
+  }
+
+  ~ForemanBase() override {}
+
+  /**
+   * @brief Get the TMB client ID of Foreman thread.
+   *
+   * @return TMB client ID of foreman thread.
+   **/
+  tmb::client_id getBusClientID() const {
+    return foreman_client_id_;
+  }
+
+ protected:
+  void run() override = 0;
+
+  tmb::MessageBus *bus_;
+
+  tmb::client_id foreman_client_id_;
+
+  // The ID of the CPU that the Foreman thread can optionally be pinned to.
+  const int cpu_id_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ForemanBase);
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_QUERY_EXECUTION_FOREMAN_BASE_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/303e51ee/query_execution/ForemanLite.hpp
----------------------------------------------------------------------
diff --git a/query_execution/ForemanLite.hpp b/query_execution/ForemanLite.hpp
deleted file mode 100644
index cb6cdf3..0000000
--- a/query_execution/ForemanLite.hpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- *   Copyright 2016 Pivotal Software, Inc.
- *
- *   Licensed 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.
- **/
-
-#ifndef QUICKSTEP_QUERY_EXECUTION_FOREMAN_LITE_HPP_
-#define QUICKSTEP_QUERY_EXECUTION_FOREMAN_LITE_HPP_
-
-#include "threading/Thread.hpp"
-#include "utility/Macros.hpp"
-
-#include "glog/logging.h"
-
-#include "tmb/id_typedefs.h"
-#include "tmb/message_bus.h"
-
-namespace quickstep {
-
-/** \addtogroup QueryExecution
- *  @{
- */
-
-/**
- * @brief A base class that Foreman implements. This class is used to derive
- *        for implementations for both the single-node and distributed versions.
- **/
-class ForemanLite : public Thread {
- public:
-  /**
-   * @brief Constructor.
-   *
-   * @param bus A pointer to the TMB.
-   * @param cpu_id The ID of the CPU to which the Foreman thread can be pinned.
-   *
-   * @note If cpu_id is not specified, Foreman thread can be possibly moved
-   *       around on different CPUs by the OS.
-  **/
-  ForemanLite(tmb::MessageBus *bus,
-              const int cpu_id)
-      : bus_(DCHECK_NOTNULL(bus)),
-        cpu_id_(cpu_id) {
-    foreman_client_id_ = bus_->Connect();
-  }
-
-  ~ForemanLite() override {}
-
-  /**
-   * @brief Get the TMB client ID of Foreman thread.
-   *
-   * @return TMB client ID of foreman thread.
-   **/
-  tmb::client_id getBusClientID() const {
-    return foreman_client_id_;
-  }
-
- protected:
-  void run() override = 0;
-
-  tmb::MessageBus *bus_;
-
-  tmb::client_id foreman_client_id_;
-
-  // The ID of the CPU that the Foreman thread can optionally be pinned to.
-  const int cpu_id_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ForemanLite);
-};
-
-/** @} */
-
-}  // namespace quickstep
-
-#endif  // QUICKSTEP_QUERY_EXECUTION_FOREMAN_LITE_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/303e51ee/query_execution/ForemanSingleNode.cpp
----------------------------------------------------------------------
diff --git a/query_execution/ForemanSingleNode.cpp b/query_execution/ForemanSingleNode.cpp
new file mode 100644
index 0000000..3aa1f0b
--- /dev/null
+++ b/query_execution/ForemanSingleNode.cpp
@@ -0,0 +1,256 @@
+/**
+ *   Copyright 2011-2015 Quickstep Technologies LLC.
+ *   Copyright 2015-2016 Pivotal Software, Inc.
+ *
+ *   Licensed 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.
+ **/
+
+#include "query_execution/ForemanSingleNode.hpp"
+
+#include <cstddef>
+#include <cstdio>
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "query_execution/AdmitRequestMessage.hpp"
+#include "query_execution/QueryExecutionTypedefs.hpp"
+#include "query_execution/QueryExecutionUtil.hpp"
+#include "query_execution/WorkerDirectory.hpp"
+#include "query_execution/WorkerMessage.hpp"
+#include "threading/ThreadUtil.hpp"
+#include "utility/EqualsAnyConstant.hpp"
+#include "utility/Macros.hpp"
+
+#include "gflags/gflags.h"
+#include "glog/logging.h"
+
+#include "tmb/message_bus.h"
+#include "tmb/tagged_message.h"
+
+using std::move;
+using std::size_t;
+using std::unique_ptr;
+using std::vector;
+
+namespace quickstep {
+
+DEFINE_uint64(min_load_per_worker, 2, "The minimum load defined as the number "
+              "of pending work orders for the worker. This information is used "
+              "by the Foreman to assign work orders to worker threads");
+
+ForemanSingleNode::ForemanSingleNode(
+    const tmb::client_id main_thread_client_id,
+    WorkerDirectory *worker_directory,
+    tmb::MessageBus *bus,
+    CatalogDatabaseLite *catalog_database,
+    StorageManager *storage_manager,
+    const int cpu_id,
+    const size_t num_numa_nodes,
+    const bool profile_individual_workorders)
+    : ForemanBase(bus, cpu_id),
+      main_thread_client_id_(main_thread_client_id),
+      worker_directory_(DCHECK_NOTNULL(worker_directory)),
+      catalog_database_(DCHECK_NOTNULL(catalog_database)),
+      storage_manager_(DCHECK_NOTNULL(storage_manager)) {
+  const std::vector<QueryExecutionMessageType> sender_message_types{
+      kPoisonMessage,
+      kRebuildWorkOrderMessage,
+      kWorkOrderMessage,
+      kWorkloadCompletionMessage};
+
+  for (const auto message_type : sender_message_types) {
+    bus_->RegisterClientAsSender(foreman_client_id_, message_type);
+  }
+
+  const std::vector<QueryExecutionMessageType> receiver_message_types{
+      kAdmitRequestMessage,
+      kCatalogRelationNewBlockMessage,
+      kDataPipelineMessage,
+      kPoisonMessage,
+      kRebuildWorkOrderCompleteMessage,
+      kWorkOrderFeedbackMessage,
+      kWorkOrdersAvailableMessage,
+      kWorkOrderCompleteMessage};
+
+  for (const auto message_type : receiver_message_types) {
+    bus_->RegisterClientAsReceiver(foreman_client_id_, message_type);
+  }
+
+  policy_enforcer_.reset(new PolicyEnforcer(
+      foreman_client_id_,
+      num_numa_nodes,
+      catalog_database_,
+      storage_manager_,
+      worker_directory_,
+      bus_,
+      profile_individual_workorders));
+}
+
+void ForemanSingleNode::run() {
+  if (cpu_id_ >= 0) {
+    // We can pin the foreman thread to a CPU if specified.
+    ThreadUtil::BindToCPU(cpu_id_);
+  }
+
+  // Event loop
+  for (;;) {
+    // Receive() causes this thread to sleep until next message is received.
+    const AnnotatedMessage annotated_msg =
+        bus_->Receive(foreman_client_id_, 0, true);
+    const TaggedMessage &tagged_message = annotated_msg.tagged_message;
+    const tmb::message_type_id message_type = tagged_message.message_type();
+    switch (message_type) {
+      case kCatalogRelationNewBlockMessage:  // Fall through
+      case kDataPipelineMessage:
+      case kRebuildWorkOrderCompleteMessage:
+      case kWorkOrderCompleteMessage:
+      case kWorkOrderFeedbackMessage:
+      case kWorkOrdersAvailableMessage: {
+        policy_enforcer_->processMessage(tagged_message);
+        break;
+      }
+
+      case kAdmitRequestMessage: {
+        const AdmitRequestMessage *msg =
+            static_cast<const AdmitRequestMessage *>(tagged_message.message());
+        const vector<QueryHandle *> &query_handles = msg->getQueryHandles();
+
+        DCHECK(!query_handles.empty());
+        bool all_queries_admitted = true;
+        if (query_handles.size() == 1u) {
+          all_queries_admitted =
+              policy_enforcer_->admitQuery(query_handles.front());
+        } else {
+          all_queries_admitted = policy_enforcer_->admitQueries(query_handles);
+        }
+        if (!all_queries_admitted) {
+          LOG(WARNING) << "The scheduler could not admit all the queries";
+          // TODO(harshad) - Inform the main thread about the failure.
+        }
+        break;
+      }
+      case kPoisonMessage: {
+        if (policy_enforcer_->hasQueries()) {
+          LOG(WARNING) << "Foreman thread exiting while some queries are "
+                          "under execution or waiting to be admitted";
+        }
+        return;
+      }
+      default:
+        LOG(FATAL) << "Unknown message type to Foreman";
+    }
+
+    if (canCollectNewMessages(message_type)) {
+      vector<unique_ptr<WorkerMessage>> new_messages;
+      policy_enforcer_->getWorkerMessages(&new_messages);
+      dispatchWorkerMessages(new_messages);
+    }
+
+    // We check again, as some queries may produce zero work orders and finish
+    // their execution.
+    if (!policy_enforcer_->hasQueries()) {
+      // Signal the main thread that there are no queries to be executed.
+      // Currently the message doesn't have any real content.
+      const int dummy_payload = 0;
+      TaggedMessage completion_tagged_message(
+          &dummy_payload, sizeof(dummy_payload), kWorkloadCompletionMessage);
+      const tmb::MessageBus::SendStatus send_status =
+          QueryExecutionUtil::SendTMBMessage(
+              bus_,
+              foreman_client_id_,
+              main_thread_client_id_,
+              move(completion_tagged_message));
+      CHECK(send_status == tmb::MessageBus::SendStatus::kOK)
+          << "Message could not be sent from Foreman with TMB client ID "
+          << foreman_client_id_ << " to main thread with TMB client ID"
+          << main_thread_client_id_;
+    }
+  }
+}
+
+bool ForemanSingleNode::canCollectNewMessages(const tmb::message_type_id message_type) {
+  if (QUICKSTEP_EQUALS_ANY_CONSTANT(message_type,
+                                    kCatalogRelationNewBlockMessage,
+                                    kWorkOrderFeedbackMessage)) {
+    return false;
+  } else if (worker_directory_->getLeastLoadedWorker().second <=
+             FLAGS_min_load_per_worker) {
+    // If the least loaded worker has only one pending work order, we should
+    // collect new messages and dispatch them.
+    return true;
+  } else {
+    return false;
+  }
+}
+
+void ForemanSingleNode::dispatchWorkerMessages(const vector<unique_ptr<WorkerMessage>> &messages) {
+  for (const auto &message : messages) {
+    DCHECK(message != nullptr);
+    const int recipient_worker_thread_index = message->getRecipientHint();
+    if (recipient_worker_thread_index != WorkerMessage::kInvalidRecipientIndexHint) {
+      sendWorkerMessage(static_cast<size_t>(recipient_worker_thread_index),
+                        *message);
+      worker_directory_->incrementNumQueuedWorkOrders(recipient_worker_thread_index);
+    } else {
+      const size_t least_loaded_worker_thread_index = worker_directory_->getLeastLoadedWorker().first;
+      sendWorkerMessage(least_loaded_worker_thread_index, *message);
+      worker_directory_->incrementNumQueuedWorkOrders(least_loaded_worker_thread_index);
+    }
+  }
+}
+
+void ForemanSingleNode::sendWorkerMessage(const size_t worker_thread_index,
+                                          const WorkerMessage &message) {
+  tmb::message_type_id type;
+  if (message.getType() == WorkerMessage::WorkerMessageType::kRebuildWorkOrder) {
+    type = kRebuildWorkOrderMessage;
+  } else if (message.getType() == WorkerMessage::WorkerMessageType::kWorkOrder) {
+    type = kWorkOrderMessage;
+  } else {
+    FATAL_ERROR("Invalid WorkerMessageType");
+  }
+  TaggedMessage worker_tagged_message(&message, sizeof(message), type);
+
+  const tmb::MessageBus::SendStatus send_status =
+      QueryExecutionUtil::SendTMBMessage(bus_,
+                                         foreman_client_id_,
+                                         worker_directory_->getClientID(worker_thread_index),
+                                         move(worker_tagged_message));
+  CHECK(send_status == tmb::MessageBus::SendStatus::kOK) <<
+      "Message could not be sent from Foreman with TMB client ID "
+      << foreman_client_id_ << " to Foreman with TMB client ID "
+      << worker_directory_->getClientID(worker_thread_index);
+}
+
+void ForemanSingleNode::printWorkOrderProfilingResults(const std::size_t query_id,
+                                                       std::FILE *out) const {
+  const std::vector<
+      std::tuple<std::size_t, std::size_t, std::size_t>>
+      &recorded_times = policy_enforcer_->getProfilingResults(query_id);
+  fputs("Query ID,Worker ID,NUMA Socket,Operator ID,Time (microseconds)\n", out);
+  for (auto workorder_entry : recorded_times) {
+    // Note: Index of the "worker thread index" in the tuple is 0.
+    const std::size_t worker_id = std::get<0>(workorder_entry);
+    fprintf(out,
+            "%lu,%lu,%d,%lu,%lu\n",
+            query_id,
+            worker_id,
+            worker_directory_->getNUMANode(worker_id),
+            std::get<1>(workorder_entry),  // Operator ID.
+            std::get<2>(workorder_entry));  // Time.
+  }
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/303e51ee/query_execution/ForemanSingleNode.hpp
----------------------------------------------------------------------
diff --git a/query_execution/ForemanSingleNode.hpp b/query_execution/ForemanSingleNode.hpp
new file mode 100644
index 0000000..7506d35
--- /dev/null
+++ b/query_execution/ForemanSingleNode.hpp
@@ -0,0 +1,140 @@
+/**
+ *   Copyright 2011-2015 Quickstep Technologies LLC.
+ *   Copyright 2015-2016 Pivotal Software, Inc.
+ *
+ *   Licensed 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.
+ **/
+
+#ifndef QUICKSTEP_QUERY_EXECUTION_FOREMAN_HPP_
+#define QUICKSTEP_QUERY_EXECUTION_FOREMAN_HPP_
+
+#include <cstddef>
+#include <cstdio>
+#include <memory>
+#include <vector>
+
+#include "query_execution/ForemanBase.hpp"
+#include "query_execution/PolicyEnforcer.hpp"
+#include "utility/Macros.hpp"
+
+#include "tmb/id_typedefs.h"
+#include "tmb/message_bus.h"
+
+namespace quickstep {
+
+class CatalogDatabaseLite;
+class StorageManager;
+class WorkerDirectory;
+class WorkerMessage;
+
+/** \addtogroup QueryExecution
+ *  @{
+ */
+
+/**
+ * @brief The Foreman receives queries from the main thread, messages from the
+ *        policy enforcer and dispatches the work to worker threads. It also
+ *        receives work completion messages from workers.
+ **/
+class ForemanSingleNode final : public ForemanBase {
+ public:
+  /**
+   * @brief Constructor.
+   *
+   * @param main_thread_client_id The TMB client ID of the main thread.
+   * @param worker_directory The worker directory.
+   * @param bus A pointer to the TMB.
+   * @param catalog_database The catalog database where this query is executed.
+   * @param storage_manager The StorageManager to use.
+   * @param cpu_id The ID of the CPU to which the Foreman thread can be pinned.
+   * @param num_numa_nodes The number of NUMA nodes in the system.
+   * @param profile_individual_workorders Whether every workorder's execution
+   *        be profiled or not.
+   *
+   * @note If cpu_id is not specified, Foreman thread can be possibly moved
+   *       around on different CPUs by the OS.
+  **/
+  ForemanSingleNode(const tmb::client_id main_thread_client_id,
+          WorkerDirectory *worker_directory,
+          tmb::MessageBus *bus,
+          CatalogDatabaseLite *catalog_database,
+          StorageManager *storage_manager,
+          const int cpu_id = -1,
+          const std::size_t num_numa_nodes = 1,
+          const bool profile_individual_workorders = false);
+
+  ~ForemanSingleNode() override {}
+
+  /**
+   * @brief Print the results of profiling individual work orders for a given
+   *        query.
+   *
+   * TODO(harshad) - Add the name of the operator to the output.
+   * TODO(harshad) - Add the CPU core ID of the operator to the output. This
+   * will require modifying the WorkerDirectory to remember worker affinities.
+   * Until then, the users can refer to the worker_affinities provided to the
+   * cli to infer the CPU core ID where a given worker is pinned.
+   *
+   * @param query_id The ID of the query for which the results are to be printed.
+   * @param out The file stream.
+   **/
+  void printWorkOrderProfilingResults(const std::size_t query_id,
+                                      std::FILE *out) const;
+
+ protected:
+  void run() override;
+
+ private:
+  /**
+   * @brief Dispatch schedulable WorkOrders, wrapped in WorkerMessages to the
+   *        worker threads.
+   *
+   * @param messages The messages to be dispatched.
+   **/
+  void dispatchWorkerMessages(
+      const std::vector<std::unique_ptr<WorkerMessage>> &messages);
+
+  /**
+   * @brief Send the given message to the specified worker.
+   *
+   * @param worker_thread_index The logical index of the recipient worker thread
+   *        in WorkerDirectory.
+   * @param message The WorkerMessage to be sent.
+   **/
+  void sendWorkerMessage(const std::size_t worker_thread_index,
+                         const WorkerMessage &message);
+
+  /**
+   * @brief Check if we can collect new messages from the PolicyEnforcer.
+   *
+   * @param message_type The type of the last received message.
+   **/
+  bool canCollectNewMessages(const tmb::message_type_id message_type);
+
+  const tmb::client_id main_thread_client_id_;
+
+  WorkerDirectory *worker_directory_;
+
+  CatalogDatabaseLite *catalog_database_;
+  StorageManager *storage_manager_;
+
+  std::unique_ptr<PolicyEnforcer> policy_enforcer_;
+
+  DISALLOW_COPY_AND_ASSIGN(ForemanSingleNode);
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_QUERY_EXECUTION_FOREMAN_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/303e51ee/query_optimizer/tests/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/query_optimizer/tests/CMakeLists.txt b/query_optimizer/tests/CMakeLists.txt
index 5b58f75..9cad47f 100644
--- a/query_optimizer/tests/CMakeLists.txt
+++ b/query_optimizer/tests/CMakeLists.txt
@@ -117,7 +117,7 @@ target_link_libraries(quickstep_queryoptimizer_tests_ExecutionGeneratorTest
                       quickstep_parser_ParseStatement
                       quickstep_parser_SqlParserWrapper
                       quickstep_queryexecution_AdmitRequestMessage
-                      quickstep_queryexecution_Foreman
+                      quickstep_queryexecution_ForemanSingleNode
                       quickstep_queryexecution_QueryContext
                       quickstep_queryexecution_QueryExecutionTypedefs
                       quickstep_queryexecution_QueryExecutionUtil

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/303e51ee/query_optimizer/tests/ExecutionGeneratorTestRunner.cpp
----------------------------------------------------------------------
diff --git a/query_optimizer/tests/ExecutionGeneratorTestRunner.cpp b/query_optimizer/tests/ExecutionGeneratorTestRunner.cpp
index 8c1d306..563a777 100644
--- a/query_optimizer/tests/ExecutionGeneratorTestRunner.cpp
+++ b/query_optimizer/tests/ExecutionGeneratorTestRunner.cpp
@@ -25,7 +25,7 @@
 #include "cli/PrintToScreen.hpp"
 #include "parser/ParseStatement.hpp"
 #include "query_execution/AdmitRequestMessage.hpp"
-#include "query_execution/Foreman.hpp"
+#include "query_execution/ForemanSingleNode.hpp"
 #include "query_execution/QueryExecutionUtil.hpp"
 #include "query_execution/Worker.hpp"
 #include "query_optimizer/ExecutionGenerator.hpp"

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/303e51ee/query_optimizer/tests/ExecutionGeneratorTestRunner.hpp
----------------------------------------------------------------------
diff --git a/query_optimizer/tests/ExecutionGeneratorTestRunner.hpp b/query_optimizer/tests/ExecutionGeneratorTestRunner.hpp
index bb2a26f..d1d9380 100644
--- a/query_optimizer/tests/ExecutionGeneratorTestRunner.hpp
+++ b/query_optimizer/tests/ExecutionGeneratorTestRunner.hpp
@@ -25,7 +25,7 @@
 #include <vector>
 
 #include "parser/SqlParserWrapper.hpp"
-#include "query_execution/Foreman.hpp"
+#include "query_execution/ForemanSingleNode.hpp"
 #include "query_execution/QueryExecutionTypedefs.hpp"
 #include "query_execution/Worker.hpp"
 #include "query_execution/WorkerDirectory.hpp"
@@ -80,11 +80,12 @@ class ExecutionGeneratorTestRunner : public TextBasedTestRunner {
 
     workers_.reset(new WorkerDirectory(1 /* number of workers */,
                                        worker_client_ids, numa_nodes));
-    foreman_.reset(new Foreman(main_thread_client_id_,
-                               workers_.get(),
-                               &bus_,
-                               test_database_loader_.catalog_database(),
-                               test_database_loader_.storage_manager()));
+    foreman_.reset(
+        new ForemanSingleNode(main_thread_client_id_,
+                              workers_.get(),
+                              &bus_,
+                              test_database_loader_.catalog_database(),
+                              test_database_loader_.storage_manager()));
 
     foreman_->start();
     worker_->start();
@@ -105,7 +106,7 @@ class ExecutionGeneratorTestRunner : public TextBasedTestRunner {
   TestDatabaseLoader test_database_loader_;
 
   MessageBusImpl bus_;
-  std::unique_ptr<Foreman> foreman_;
+  std::unique_ptr<ForemanSingleNode> foreman_;
   std::unique_ptr<Worker> worker_;
 
   std::unique_ptr<WorkerDirectory> workers_;


[24/30] incubator-quickstep git commit: Cleaned up the messages w/ a dummy payload.

Posted by ra...@apache.org.
Cleaned up the messages w/ a dummy payload.


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

Branch: refs/heads/quickstep-28-29
Commit: ccea2ff83ea73e950d52c152cc422a9e93cf6aad
Parents: 52a32a3
Author: Zuyu Zhang <zu...@twitter.com>
Authored: Fri Jul 29 23:52:33 2016 -0700
Committer: Zuyu Zhang <zu...@twitter.com>
Committed: Mon Aug 1 10:11:19 2016 -0700

----------------------------------------------------------------------
 query_execution/ForemanSingleNode.cpp           |   4 +-
 query_execution/QueryExecutionMessages.proto    |   4 -
 query_execution/QueryExecutionUtil.hpp          |   5 +-
 query_execution/README.md                       | 110 +++++++++----------
 query_execution/WorkerMessage.hpp               |  13 +--
 query_execution/tests/BlockLocator_unittest.cpp |  11 +-
 storage/tests/DataExchange_unittest.cpp         |  11 +-
 7 files changed, 60 insertions(+), 98 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/ccea2ff8/query_execution/ForemanSingleNode.cpp
----------------------------------------------------------------------
diff --git a/query_execution/ForemanSingleNode.cpp b/query_execution/ForemanSingleNode.cpp
index cda02a7..d2b56ae 100644
--- a/query_execution/ForemanSingleNode.cpp
+++ b/query_execution/ForemanSingleNode.cpp
@@ -167,9 +167,7 @@ void ForemanSingleNode::run() {
     if (!policy_enforcer_->hasQueries()) {
       // Signal the main thread that there are no queries to be executed.
       // Currently the message doesn't have any real content.
-      const int dummy_payload = 0;
-      TaggedMessage completion_tagged_message(
-          &dummy_payload, sizeof(dummy_payload), kWorkloadCompletionMessage);
+      TaggedMessage completion_tagged_message(kWorkloadCompletionMessage);
       const tmb::MessageBus::SendStatus send_status =
           QueryExecutionUtil::SendTMBMessage(
               bus_,

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/ccea2ff8/query_execution/QueryExecutionMessages.proto
----------------------------------------------------------------------
diff --git a/query_execution/QueryExecutionMessages.proto b/query_execution/QueryExecutionMessages.proto
index 308d736..f2219f6 100644
--- a/query_execution/QueryExecutionMessages.proto
+++ b/query_execution/QueryExecutionMessages.proto
@@ -20,10 +20,6 @@ import "catalog/Catalog.proto";
 import "query_execution/QueryContext.proto";
 import "relational_operators/WorkOrder.proto";
 
-// Used for any messages that do not carry payloads.
-message EmptyMessage {
-}
-
 // Note: There are different types of completion messages for normal work orders
 // rebuild work orders. This can be potentially helpful when we want to collect
 // different statistics for executing different types of work orders.

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/ccea2ff8/query_execution/QueryExecutionUtil.hpp
----------------------------------------------------------------------
diff --git a/query_execution/QueryExecutionUtil.hpp b/query_execution/QueryExecutionUtil.hpp
index 6ea4a29..5994f22 100644
--- a/query_execution/QueryExecutionUtil.hpp
+++ b/query_execution/QueryExecutionUtil.hpp
@@ -123,10 +123,7 @@ class QueryExecutionUtil {
     style.Broadcast(true);
     Address address;
     address.All(true);
-    std::unique_ptr<WorkerMessage> poison_message(WorkerMessage::PoisonMessage());
-    TaggedMessage poison_tagged_message(poison_message.get(),
-                                        sizeof(*poison_message),
-                                        kPoisonMessage);
+    TaggedMessage poison_tagged_message(kPoisonMessage);
 
     const tmb::MessageBus::SendStatus send_status = bus->Send(
         sender_id, address, style, std::move(poison_tagged_message));

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/ccea2ff8/query_execution/README.md
----------------------------------------------------------------------
diff --git a/query_execution/README.md b/query_execution/README.md
index 22ad91d..12e0f57 100644
--- a/query_execution/README.md
+++ b/query_execution/README.md
@@ -3,19 +3,19 @@
 ## Types of threads
 There are two kinds of threads in Quickstep - Foreman and Worker. The foreman
 thread controls the query execution progress, finds schedulable work (called as
-WorkOrder) and assigns (or schedules) it for execution to the Worker threads. 
+WorkOrder) and assigns (or schedules) it for execution to the Worker threads.
 The Worker threads receive the WorkOrders and execute them. After execution they
-send a completion message (or response message) back to Foreman. 
+send a completion message (or response message) back to Foreman.
 
 ## High level functionality of Foreman
-Foreman requests all the RelationalOperators in the physical query plan 
+Foreman requests all the RelationalOperators in the physical query plan
 represented as a DAG to give any schedulable work (in the form of WorkOrders).
 While doing so, Foreman has to respect dependencies between operators. There are
-two kinds of dependencies between operators - pipeline breaking (or blocking) 
+two kinds of dependencies between operators - pipeline breaking (or blocking)
 and pipeline non-breaking (or non-blocking). In the first case, the output of
 the producer operator can't be pipelined to the consumer operator. In the second
 case, the Foreman will facilitate the pipelining of the intermediate output
-produced by the producer operator to the consumer operator. 
+produced by the producer operator to the consumer operator.
 
 ## Messages in execution engine
 
@@ -26,110 +26,110 @@ of the message.
 Foreman -> Worker : WorkerMessage which consists of the following things
 - A pointer to the WorkOrder to be executed. The WorkOrder could be a normal
 WorkOrder or a rebuild WorkOrder. A normal WorkOrder involves the invocation of
-WorkOrder::execute() method which is overriden by all of the RelationalOperator 
-classes. A rebuild WorkOrder has one StorageBlock as input and calls a 
+WorkOrder::execute() method which is overriden by all of the RelationalOperator
+classes. A rebuild WorkOrder has one StorageBlock as input and calls a
 rebuild() method on the block. More details about rebuild() can be found in the
-storage module. 
+storage module.
 - The index of the relational operator in the query plan DAG that produced the
-WorkOrder. 
-
-Main thread -> Worker : WorkerMessage of type PoisonMessage. This message is
-used to terminate the Worker thread, typically when shutting down the Quickstep
-process. 
+WorkOrder.
 
 ### ForemanMessage
-Multiple senders are possible for this message. There are multiple types of 
+Multiple senders are possible for this message. There are multiple types of
 ForemanMessages, each of which indicates the purpose of the message.
 
-Worker -> Foreman : ForemanMessage of types WorkOrderCompletion and 
-RebuildCompletion are sent after a Worker finishes executing a respective type 
-of WorkOrder. This message helps the Foreman track the progress of individual 
-operators as well as the whole query. 
+Worker -> Foreman : ForemanMessage of types WorkOrderCompletion and
+RebuildCompletion are sent after a Worker finishes executing a respective type
+of WorkOrder. This message helps the Foreman track the progress of individual
+operators as well as the whole query.
 
 Some relational operators and InsertDestination -> Foreman : ForemanMessage of
-types DataPipeline and WorkOrdersAvailable. InsertDestination first determines 
+types DataPipeline and WorkOrdersAvailable. InsertDestination first determines
 when an output block of a relational operator gets full. Once a block is full,
-it streams the unique block ID of the filled block along with the index of the 
-relational operator that produced the block to Foreman with the message type 
+it streams the unique block ID of the filled block along with the index of the
+relational operator that produced the block to Foreman with the message type
 DataPipeline. Some operators which modify the block in place also send similar
-messages to Foreman. 
+messages to Foreman.
 
 ### FeedbackMessage
 This message is sent from Workers to the Foreman during a WorkOrder execution.
 
 In certain operators, e.g. TextScan (used for bulk loading data from text files)
-and Sort, there is a communication between the relational operator and its 
-WorkOrders. In such cases, when a WorkOrder is under execution on a Worker 
+and Sort, there is a communication between the relational operator and its
+WorkOrders. In such cases, when a WorkOrder is under execution on a Worker
 thread, a FeedbackMessage is sent from the WorkOrder via the Worker to Foreman.
 Foreman relays this message to the relational operator that produced the sender
-WorkOrder. The relational operator uses this message to update its internal 
-state to potentially generate newer WorkOrders. 
+WorkOrder. The relational operator uses this message to update its internal
+state to potentially generate newer WorkOrders.
+
+### PoisonMessage
+This message is used to terminate a thread (i.e., Foreman and Worker), typically
+when shutting down the Quickstep process.
 
 ## How does the Foreman react after receiving various messages?
 ### WorkOrder completion message
 * Update the book-keeping of pending WorkOrders per Worker and per operator.
 * Fetch new WorkOrders if available for the operator of whose WorkOrder was
-just executed.  
+just executed.
 * Update the state of an operator - the possible options are:
   - Normal WorkOrders are still under execution
   - All normal WorkOrders have finished execution and rebuild WorkOrders are yet
-  to be generated. 
+  to be generated.
   - All normal WorkOrders have finished execution, rebuild WorkOrders have been
-  generated and issued to Workers. 
+  generated and issued to Workers.
   - All normal and rebuild WorkOrders have been executed AND all the dependency
-  operators for the given operator have finished execution, therefore the given 
-  operator has finished its execution. 
-* Fetch the WorkOrders from the dependents of the given operator. 
+  operators for the given operator have finished execution, therefore the given
+  operator has finished its execution.
+* Fetch the WorkOrders from the dependents of the given operator.
 
 ### Rebuild WorkOrder completion message
 * Update the book-keeping of pending WorkOrders per Worker and per operator.
 * If all the rebuild WorkOrders have finished their execution, try to fetch the
 WorkOrders of the dependent operators of the operator whose rebuild WorkOrder
-was just executed. 
+was just executed.
 
 ### Data pipeline message
-* Find the consumer operators (i.e. operators which have a non 
-pipeline-breaking link) of the producer operator. 
-* Stream the block ID to the eligible consumer operators. 
-* Fetch new WorkOrders from these consumer operators which may have become 
-available because of the streaming of data. 
+* Find the consumer operators (i.e. operators which have a non
+pipeline-breaking link) of the producer operator.
+* Stream the block ID to the eligible consumer operators.
+* Fetch new WorkOrders from these consumer operators which may have become
+available because of the streaming of data.
 
 ### WorkOrder available message
 * Fetch new WorkOrders that may have become available.
 
 ### Feedback message
-* Relay the feedback message to a specified relational operator. The recipient 
-operator is specified in the header of the message. 
+* Relay the feedback message to a specified relational operator. The recipient
+operator is specified in the header of the message.
 
 ## Example
-We look at a sample query to better describe the flow of messages - 
+We look at a sample query to better describe the flow of messages -
 
 SELECT R.a, S.b from R, S where R.a = S.a and R.c < 20;
 
-This is an equi-join query which can be implemented using a hash join. We assume 
-that S is a larger relation and the build relation is the output of the 
+This is an equi-join query which can be implemented using a hash join. We assume
+that S is a larger relation and the build relation is the output of the
 selection on R.
 
 The query execution plan involves the following operators:
-* SelectOperator to filter R based on predicate R.c < 20 (We call the output as 
-R') 
+* SelectOperator to filter R based on predicate R.c < 20 (We call the output as
+R')
 * BuildHashOperator to construct a hash table on R'
 * HashJoinOperator to probe the hash table, where the probe relation is S
 * DestroyHashTableOperator to destroy the hash table after the join is done
-* Multiple DropTableOperators to destroy the temporaray relations produced as 
-output. 
+* Multiple DropTableOperators to destroy the temporaray relations produced as
+output.
 
 R has two blocks with IDs as 1 and 2. S has two blocks with IDs as 3 and 4.
-We assume that the SelectOperator produces one filled block and one partially 
-filled block as output. Note that in the query plan DAG, the link between 
-SelectOperator and BuildHashOperator allows streaming of data. The 
-HashJoinOperator's WorkOrder can't be generated unless all of the 
+We assume that the SelectOperator produces one filled block and one partially
+filled block as output. Note that in the query plan DAG, the link between
+SelectOperator and BuildHashOperator allows streaming of data. The
+HashJoinOperator's WorkOrder can't be generated unless all of the
 BuildHashOperator's WorkOrders have finished their execution. The execution is
-assumed to be performed by a single Worker thread. 
+assumed to be performed by a single Worker thread.
 
-The following table describes the message exchange that happens during the 
-query excution. We primarily focus on three operators - Select, BuildHash and 
-HashJoin (probe). 
+The following table describes the message exchange that happens during the
+query excution. We primarily focus on three operators - Select, BuildHash and
+HashJoin (probe).
 
 | Sender | Receiver | Message  | Message Description |
 |:-----------------:|----------|---------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/ccea2ff8/query_execution/WorkerMessage.hpp
----------------------------------------------------------------------
diff --git a/query_execution/WorkerMessage.hpp b/query_execution/WorkerMessage.hpp
index 560c1ba..a0434de 100644
--- a/query_execution/WorkerMessage.hpp
+++ b/query_execution/WorkerMessage.hpp
@@ -35,7 +35,6 @@ class WorkerMessage {
   enum class WorkerMessageType {
     kRebuildWorkOrder = 0,
     kWorkOrder,
-    kPoison
   };
 
   /**
@@ -70,15 +69,6 @@ class WorkerMessage {
   }
 
   /**
-   * @brief A static factory method for generating a poison message.
-   *
-   * @return The constructed PoisonMessage.
-   **/
-  static WorkerMessage* PoisonMessage() {
-    return new WorkerMessage(nullptr, 0, WorkerMessageType::kPoison);
-  }
-
-  /**
    * @brief Destructor.
    **/
   ~WorkerMessage() {
@@ -128,8 +118,7 @@ class WorkerMessage {
   /**
    * @brief Constructor.
    *
-   * @param work_unit The work order to be executed by the worker. A NULL
-   *        workorder indicates a poison message.
+   * @param work_unit The work order to be executed by the worker.
    * @param relational_op_index The index of the relational operator in the
    *        query plan DAG that generated the given WorkOrder.
    * @param type Type of the WorkerMessage.

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/ccea2ff8/query_execution/tests/BlockLocator_unittest.cpp
----------------------------------------------------------------------
diff --git a/query_execution/tests/BlockLocator_unittest.cpp b/query_execution/tests/BlockLocator_unittest.cpp
index fe7b86b..fd25e9e 100644
--- a/query_execution/tests/BlockLocator_unittest.cpp
+++ b/query_execution/tests/BlockLocator_unittest.cpp
@@ -90,16 +90,7 @@ class BlockLocatorTest : public ::testing::Test {
   virtual void TearDown() {
     storage_manager_.reset();
 
-    serialization::EmptyMessage proto;
-
-    const int proto_length = proto.ByteSize();
-    char *proto_bytes = static_cast<char*>(malloc(proto_length));
-    CHECK(proto.SerializeToArray(proto_bytes, proto_length));
-
-    TaggedMessage message(static_cast<const void*>(proto_bytes),
-                          proto_length,
-                          kPoisonMessage);
-    free(proto_bytes);
+    TaggedMessage message(kPoisonMessage);
 
     LOG(INFO) << "Worker (id '" << worker_client_id_
               << "') sent PoisonMessage (typed '" << kPoisonMessage

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/ccea2ff8/storage/tests/DataExchange_unittest.cpp
----------------------------------------------------------------------
diff --git a/storage/tests/DataExchange_unittest.cpp b/storage/tests/DataExchange_unittest.cpp
index 38d12f6..4bad17b 100644
--- a/storage/tests/DataExchange_unittest.cpp
+++ b/storage/tests/DataExchange_unittest.cpp
@@ -105,16 +105,7 @@ class DataExchangeTest : public ::testing::Test {
     data_exchanger_expected_.shutdown();
     storage_manager_expected_.reset();
 
-    serialization::EmptyMessage proto;
-
-    const int proto_length = proto.ByteSize();
-    char *proto_bytes = static_cast<char*>(malloc(proto_length));
-    CHECK(proto.SerializeToArray(proto_bytes, proto_length));
-
-    TaggedMessage message(static_cast<const void*>(proto_bytes),
-                          proto_length,
-                          kPoisonMessage);
-    free(proto_bytes);
+    TaggedMessage message(kPoisonMessage);
 
     LOG(INFO) << "Worker (id '" << worker_client_id_
               << "') sent PoisonMessage (typed '" << kPoisonMessage


[07/30] incubator-quickstep git commit: Add a quick start guide. Move the old README to a developer README.

Posted by ra...@apache.org.
Add a quick start guide. Move the old README to a developer README.


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

Branch: refs/heads/quickstep-28-29
Commit: 8181f1550dd9630f5bdcfe13da6df561324f6b55
Parents: b4e25ed
Author: Jignesh Patel <jm...@hotmail.com>
Authored: Wed Jul 13 16:09:54 2016 -0500
Committer: Zuyu Zhang <zu...@twitter.com>
Committed: Wed Jul 13 17:50:01 2016 -0700

----------------------------------------------------------------------
 DEV_README.md | 108 ++++++++++++++++++++++++++++++++
 README.md     | 176 +++++++++++++++++++++--------------------------------
 2 files changed, 176 insertions(+), 108 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/8181f155/DEV_README.md
----------------------------------------------------------------------
diff --git a/DEV_README.md b/DEV_README.md
new file mode 100644
index 0000000..04d5d66
--- /dev/null
+++ b/DEV_README.md
@@ -0,0 +1,108 @@
+# Apache Quickstep (Incubating)
+
+[![Travis Widget]][Travis]
+
+[Travis]: https://travis-ci.org/apache/incubator-quickstep
+[Travis Widget]: https://travis-ci.org/apache/incubator-quickstep.svg?branch=master
+
+Apache Quickstep is an experimental high-performance database engine designed with the
+aim of Data at Bare-Metal Speed. It began life in 2011 as a
+[research project at the University of Wisconsin](https://quickstep.cs.wisc.edu)
+and was acquired by [Pivotal](https://pivotal.io) in 2015.
+Quickstep entered incubation at the
+[Apache Software Foundation](https://www.apache.org) in April, 2016.
+
+## Getting Started (Building)
+
+A [build guide](BUILDING.md) is available which includes instructions for
+building Quickstep for the first time. You may also find it useful to use one
+of the [pre-made Vagrant boxes](build/vagrant) for Quickstep that are already
+set up with all of the development tools needed to build Quickstep.
+
+## Documentation
+
+All publicly-visible classes and functions in the Quickstep code base have
+Doxygen documentation. Simply run `doxygen` in the root of the Quickstep source
+to generate browsable HTML documentation. Of course, the Doxygen comments
+should also be useful when reading header files directly.
+
+In addition to the Doxygen and inline code comments explaining implementation
+details, a high-level overview for each module that comprises Quickstep is
+included in the README files in each subdirectory.
+
+## Architectural Overview
+
+Quickstep is composed of several different modules that handle different
+concerns of a database system. The main modules are:
+
+* [Utility](utility) - Reusable general-purpose code that is used by many
+  other modules.
+* [Threading](threading) - Provides a cross-platform abstraction for threads
+  and synchronization primitives that abstracts the underlying OS threading
+  features.
+* [Types](types) - The core type system used across all of Quickstep. Handles
+  details of how SQL types are stored, parsed, serialized & deserialized, and
+  converted. Also includes basic containers for typed values (tuples and
+  column-vectors) and low-level operations that apply to typed values (e.g.
+  basic arithmetic and comparisons).
+* [Catalog](catalog) - Keeps track of database schema as well as physical
+  storage information for relations (e.g. which physical blocks store a
+  relation's data, and any physical partitioning and placement information).
+* [Storage](storage) - Handles the physical storage of relation data in
+  self-contained, self-describing blocks, both in-memory and on persistent
+  storage (disk or a distributed filesystem). Also includes some heavyweight
+  run-time data structures used in query processing (e.g. hash tables for join
+  and aggregation). Includes a buffer manager component for managing memory
+  use and a file manager component that handles data persistence.
+* [Compression](compression) - A simple implementation of ordered dictionary
+  compression. Several storage formats in the Storage module are capable of
+  storing compressed column data and evaluating some expressions directly on
+  compressed data without decompressing. The common code supporting compression
+  is in this module.
+* [Expressions](expressions) - This module builds on the simple operations
+  provided by the Types module to support arbitrarily complex expressions over
+  data, including scalar expressions, predicates, and aggregate functions with
+  and without grouping.
+* [Relational Operators](relational_operators) - This module provides the
+  building blocks for queries in Quickstep. A query is represented as a
+  directed acyclic graph of relational operators, each of which is responsible
+  for applying some relational-algebraic operation(s) to tranform its input.
+  Operators generate individual self-contained "work orders" that can be
+  executed independently. Most operators are parallelism-friendly and generate
+  one work-order per storage block of input.
+* [Query Execution](query_execution) - Handles the actual scheduling and
+  execution of work from a query at runtime. The central class is the Foreman,
+  an independent thread with a global view of the query plan and progress. The
+  Foreman dispatches work-orders to stateless Worker threads and monitors their
+  progress, and also coordinates streaming of partial results between producers
+  and consumers in a query plan DAG to maximize parallelism. This module also
+  includes the QueryContext class, which holds global shared state for an
+  individual query and is designed to support easy
+  serialization/deserialization for distributed execution.
+* [Parser](parser) - A simple SQL lexer and parser that parses SQL syntax into
+  an abstract syntax tree for consumption by the Query Optimizer.
+* [Query Optimizer](query_optimizer) - Takes the abstract syntax tree generated
+  by the parser and transforms it into a runable query-plan DAG for the Query
+  Execution module. The Query Optimizer is responsible for resolving references
+  to relations and attributes in the query, checking it for semantic
+  correctness, and applying optimizations (e.g. filter pushdown, column
+  pruning, join ordering) as part of the transformation process.
+* [Command-Line Interface](cli) - An interactive SQL shell interface to
+  Quickstep.
+
+## Licensing
+
+Quickstep is licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/pivotalsoftware/quickstep/blob/master/LICENSE) for the full license text.
+
+## Disclaimer
+Apache Quickstep is an effort undergoing incubation at the Apache Software
+Foundation (ASF), sponsored by the Apache Incubator PMC.
+
+Incubation is required of all newly accepted projects until a further
+review indicates that the infrastructure, communications, and decision
+making process have stabilized in a manner consistent with other
+successful ASF projects.
+
+While incubation status is not necessarily a reflection of the
+completeness or stability of the code, it does indicate that the
+project has yet to be fully endorsed by the ASF.

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/8181f155/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 04d5d66..bdfb7fc 100644
--- a/README.md
+++ b/README.md
@@ -1,108 +1,68 @@
-# Apache Quickstep (Incubating)
-
-[![Travis Widget]][Travis]
-
-[Travis]: https://travis-ci.org/apache/incubator-quickstep
-[Travis Widget]: https://travis-ci.org/apache/incubator-quickstep.svg?branch=master
-
-Apache Quickstep is an experimental high-performance database engine designed with the
-aim of Data at Bare-Metal Speed. It began life in 2011 as a
-[research project at the University of Wisconsin](https://quickstep.cs.wisc.edu)
-and was acquired by [Pivotal](https://pivotal.io) in 2015.
-Quickstep entered incubation at the
-[Apache Software Foundation](https://www.apache.org) in April, 2016.
-
-## Getting Started (Building)
-
-A [build guide](BUILDING.md) is available which includes instructions for
-building Quickstep for the first time. You may also find it useful to use one
-of the [pre-made Vagrant boxes](build/vagrant) for Quickstep that are already
-set up with all of the development tools needed to build Quickstep.
-
-## Documentation
-
-All publicly-visible classes and functions in the Quickstep code base have
-Doxygen documentation. Simply run `doxygen` in the root of the Quickstep source
-to generate browsable HTML documentation. Of course, the Doxygen comments
-should also be useful when reading header files directly.
-
-In addition to the Doxygen and inline code comments explaining implementation
-details, a high-level overview for each module that comprises Quickstep is
-included in the README files in each subdirectory.
-
-## Architectural Overview
-
-Quickstep is composed of several different modules that handle different
-concerns of a database system. The main modules are:
-
-* [Utility](utility) - Reusable general-purpose code that is used by many
-  other modules.
-* [Threading](threading) - Provides a cross-platform abstraction for threads
-  and synchronization primitives that abstracts the underlying OS threading
-  features.
-* [Types](types) - The core type system used across all of Quickstep. Handles
-  details of how SQL types are stored, parsed, serialized & deserialized, and
-  converted. Also includes basic containers for typed values (tuples and
-  column-vectors) and low-level operations that apply to typed values (e.g.
-  basic arithmetic and comparisons).
-* [Catalog](catalog) - Keeps track of database schema as well as physical
-  storage information for relations (e.g. which physical blocks store a
-  relation's data, and any physical partitioning and placement information).
-* [Storage](storage) - Handles the physical storage of relation data in
-  self-contained, self-describing blocks, both in-memory and on persistent
-  storage (disk or a distributed filesystem). Also includes some heavyweight
-  run-time data structures used in query processing (e.g. hash tables for join
-  and aggregation). Includes a buffer manager component for managing memory
-  use and a file manager component that handles data persistence.
-* [Compression](compression) - A simple implementation of ordered dictionary
-  compression. Several storage formats in the Storage module are capable of
-  storing compressed column data and evaluating some expressions directly on
-  compressed data without decompressing. The common code supporting compression
-  is in this module.
-* [Expressions](expressions) - This module builds on the simple operations
-  provided by the Types module to support arbitrarily complex expressions over
-  data, including scalar expressions, predicates, and aggregate functions with
-  and without grouping.
-* [Relational Operators](relational_operators) - This module provides the
-  building blocks for queries in Quickstep. A query is represented as a
-  directed acyclic graph of relational operators, each of which is responsible
-  for applying some relational-algebraic operation(s) to tranform its input.
-  Operators generate individual self-contained "work orders" that can be
-  executed independently. Most operators are parallelism-friendly and generate
-  one work-order per storage block of input.
-* [Query Execution](query_execution) - Handles the actual scheduling and
-  execution of work from a query at runtime. The central class is the Foreman,
-  an independent thread with a global view of the query plan and progress. The
-  Foreman dispatches work-orders to stateless Worker threads and monitors their
-  progress, and also coordinates streaming of partial results between producers
-  and consumers in a query plan DAG to maximize parallelism. This module also
-  includes the QueryContext class, which holds global shared state for an
-  individual query and is designed to support easy
-  serialization/deserialization for distributed execution.
-* [Parser](parser) - A simple SQL lexer and parser that parses SQL syntax into
-  an abstract syntax tree for consumption by the Query Optimizer.
-* [Query Optimizer](query_optimizer) - Takes the abstract syntax tree generated
-  by the parser and transforms it into a runable query-plan DAG for the Query
-  Execution module. The Query Optimizer is responsible for resolving references
-  to relations and attributes in the query, checking it for semantic
-  correctness, and applying optimizations (e.g. filter pushdown, column
-  pruning, join ordering) as part of the transformation process.
-* [Command-Line Interface](cli) - An interactive SQL shell interface to
-  Quickstep.
-
-## Licensing
-
-Quickstep is licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/pivotalsoftware/quickstep/blob/master/LICENSE) for the full license text.
-
-## Disclaimer
-Apache Quickstep is an effort undergoing incubation at the Apache Software
-Foundation (ASF), sponsored by the Apache Incubator PMC.
-
-Incubation is required of all newly accepted projects until a further
-review indicates that the infrastructure, communications, and decision
-making process have stabilized in a manner consistent with other
-successful ASF projects.
-
-While incubation status is not necessarily a reflection of the
-completeness or stability of the code, it does indicate that the
-project has yet to be fully endorsed by the ASF.
+# Apache Quickstep (Incubating)
+
+[![Travis Widget]][Travis]
+
+[Travis]: https://travis-ci.org/apache/incubator-quickstep
+[Travis Widget]: https://travis-ci.org/apache/incubator-quickstep.svg?branch=master
+
+## What is Quickstep?
+Apache Quickstep is high-performance database engine designed to exploit the full potential of hardware that is packed in modern computing boxes (servers and laptops). The initial version (available now!) targets single-node in-memory environments. If your data spills overs the memory limit Quickstep will still work, so you don't have to obsessively worry about the in-memory part. Also, if your working set fits in memory then Quickstep will transparently and automatically figure that out, and cache that hot set to  deliver in-memory performance.
+
+Distributed execution is the next big feature for Quickstep.  
+
+Quickstep began life in 2011 as a
+[research project at the University of Wisconsin](https://www.cs.wisc.edu/~jignesh)
+and entered incubation at the
+[Apache Software Foundation](https://www.apache.org) in April, 2016.
+
+## Why Quickstep?
+Did you know that the hardware that you have in your laptop was spread across a small cluster just a decade ago? (PS: Hopefully you are not using a very old laptop!) If you look at a high-end server box, then that packs compute and storage power that was a full rack about 5 years ago! And, the way hardware technology is going, that box is going to become even more powerful in the future. In fact, it is likely that the computing power in each box is going to grow faster than other hardware components (e.g. networking) in data centers. So, if you care about performance and/or total operating costs, paying attention to single box performance is likely to be super important in the long run.
+
+In other words there is a small data center in an individual compute boxes today! Quickstep aims to allow you to fully exploit the potential of that data center that is hidden in each individual box today. We call this the **scaling-in approach**, and it complements a scaling-out approach. But without scaling-in, you are overpaying (by a lot!) when you run your data service.
+
+## What are the key ingredients?
+
+Modern computing boxes contain a large number of computing cores and large main memory configuration. Quickstep allows you to fully exploit these hardware resources using novel data processing, data storage, and query processing methods that include:
+
+1. A unique **decoupling of data-flow from control-flow** for query execution that allows for unlimited intra and inter-query parallelism. Thus, using all the processing core effectively.
+
+2. A **template meta-programming** framework that provides fast vectorized query execution. Thus, using each processor cycle very efficiently.
+
+3. A **hybrid data storage** architecture that includes columnar and row-store. Yes, this may surprise some of you, but sometimes a row-store beats a column-store!
+
+And, it is **open source!**
+
+## Giving it a spin
+
+1. Checkout the code: ```git clone https://git-wip-us.apache.org/repos/asf/incubator-quickstep.git quickstep```
+2. Then, go to the code directory: ```cd quickstep```
+3. Initialize the dependencies: ```git submodule init```
+4. Checkout the dependencies: ```git submodule update```
+5. Go into the build directory: ```cd build```
+6. Create the Makefile: ```cmake -D CMAKE_BUILD_TYPE=Release ..```  
+7. Build: ```make -j4```. Note you may replace the 4 with the number of cores on your machine.
+8. Start quickstep: ```./quickstep_cli_shell --initialize_db=true```. You can now fire SQL queries. To quit, you can type in ```quit;``` Your data is stored in the directory ```qsstor```
+
+
+## Additional pointers
+
+1. For other build options, see the more comprehensive [build guide](BUILDING.md).
+2. To get started as a developer, you should start with the [code organization guide](DEV_README.md).
+
+
+## Licensing
+
+Quickstep is licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/pivotalsoftware/quickstep/blob/master/LICENSE) for the full license text.
+
+## Disclaimer
+Apache Quickstep is an effort undergoing incubation at the Apache Software
+Foundation (ASF), sponsored by the Apache Incubator PMC.
+
+Incubation is required of all newly accepted projects until a further
+review indicates that the infrastructure, communications, and decision
+making process have stabilized in a manner consistent with other
+successful ASF projects.
+
+While incubation status is not necessarily a reflection of the
+completeness or stability of the code, it does indicate that the
+project has yet to be fully endorsed by the ASF.


[13/30] incubator-quickstep git commit: Introduced PolicyEnforcerBase and its single-node implementation.

Posted by ra...@apache.org.
Introduced PolicyEnforcerBase and its single-node implementation.


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

Branch: refs/heads/quickstep-28-29
Commit: 2b78380ec5689658c909ba63241b78f72f4644bd
Parents: cdecf0b
Author: Zuyu Zhang <zu...@twitter.com>
Authored: Mon Jul 18 12:18:04 2016 -0700
Committer: Zuyu Zhang <zu...@twitter.com>
Committed: Wed Jul 20 09:12:13 2016 -0700

----------------------------------------------------------------------
 query_execution/CMakeLists.txt               |  23 +-
 query_execution/ForemanSingleNode.cpp        |   6 +-
 query_execution/ForemanSingleNode.hpp        |   7 +-
 query_execution/PolicyEnforcer.cpp           | 254 ----------------------
 query_execution/PolicyEnforcer.hpp           | 223 -------------------
 query_execution/PolicyEnforcerBase.cpp       | 177 +++++++++++++++
 query_execution/PolicyEnforcerBase.hpp       | 196 +++++++++++++++++
 query_execution/PolicyEnforcerSingleNode.cpp | 110 ++++++++++
 query_execution/PolicyEnforcerSingleNode.hpp | 104 +++++++++
 9 files changed, 612 insertions(+), 488 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/2b78380e/query_execution/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/query_execution/CMakeLists.txt b/query_execution/CMakeLists.txt
index 8c12a5d..f582ba5 100644
--- a/query_execution/CMakeLists.txt
+++ b/query_execution/CMakeLists.txt
@@ -34,7 +34,8 @@ if (ENABLE_DISTRIBUTED)
 endif()
 add_library(quickstep_queryexecution_ForemanBase ../empty_src.cpp ForemanBase.hpp)
 add_library(quickstep_queryexecution_ForemanSingleNode ForemanSingleNode.cpp ForemanSingleNode.hpp)
-add_library(quickstep_queryexecution_PolicyEnforcer PolicyEnforcer.cpp PolicyEnforcer.hpp)
+add_library(quickstep_queryexecution_PolicyEnforcerBase PolicyEnforcerBase.cpp PolicyEnforcerBase.hpp)
+add_library(quickstep_queryexecution_PolicyEnforcerSingleNode PolicyEnforcerSingleNode.cpp PolicyEnforcerSingleNode.hpp)
 add_library(quickstep_queryexecution_QueryContext QueryContext.cpp QueryContext.hpp)
 add_library(quickstep_queryexecution_QueryContext_proto
             ${queryexecution_QueryContext_proto_srcs}
@@ -85,7 +86,7 @@ target_link_libraries(quickstep_queryexecution_ForemanSingleNode
                       glog
                       quickstep_queryexecution_AdmitRequestMessage
                       quickstep_queryexecution_ForemanBase
-                      quickstep_queryexecution_PolicyEnforcer
+                      quickstep_queryexecution_PolicyEnforcerSingleNode
                       quickstep_queryexecution_QueryExecutionTypedefs
                       quickstep_queryexecution_QueryExecutionUtil
                       quickstep_queryexecution_WorkerDirectory
@@ -95,22 +96,29 @@ target_link_libraries(quickstep_queryexecution_ForemanSingleNode
                       quickstep_utility_Macros
                       tmb
                       ${GFLAGS_LIB_NAME})
-target_link_libraries(quickstep_queryexecution_PolicyEnforcer
+target_link_libraries(quickstep_queryexecution_PolicyEnforcerBase
                       glog
                       quickstep_catalog_CatalogDatabase
                       quickstep_catalog_CatalogRelation
-                      quickstep_catalog_CatalogTypedefs
                       quickstep_catalog_PartitionScheme
                       quickstep_queryexecution_QueryExecutionMessages_proto
                       quickstep_queryexecution_QueryExecutionState
                       quickstep_queryexecution_QueryExecutionTypedefs
                       quickstep_queryexecution_QueryManagerBase
+                      quickstep_relationaloperators_WorkOrder
+                      quickstep_storage_StorageBlockInfo
+                      quickstep_utility_Macros
+                      tmb)
+target_link_libraries(quickstep_queryexecution_PolicyEnforcerSingleNode
+                      glog
+                      quickstep_catalog_CatalogTypedefs
+                      quickstep_queryexecution_PolicyEnforcerBase
+                      quickstep_queryexecution_QueryExecutionState
+                      quickstep_queryexecution_QueryManagerBase
                       quickstep_queryexecution_QueryManagerSingleNode
                       quickstep_queryexecution_WorkerDirectory
                       quickstep_queryexecution_WorkerMessage
                       quickstep_queryoptimizer_QueryHandle
-                      quickstep_relationaloperators_WorkOrder
-                      quickstep_storage_StorageBlockInfo
                       quickstep_utility_Macros
                       tmb
                       ${GFLAGS_LIB_NAME})
@@ -244,7 +252,8 @@ target_link_libraries(quickstep_queryexecution
                       quickstep_queryexecution_AdmitRequestMessage
                       quickstep_queryexecution_ForemanBase
                       quickstep_queryexecution_ForemanSingleNode
-                      quickstep_queryexecution_PolicyEnforcer
+                      quickstep_queryexecution_PolicyEnforcerBase
+                      quickstep_queryexecution_PolicyEnforcerSingleNode
                       quickstep_queryexecution_QueryContext
                       quickstep_queryexecution_QueryContext_proto
                       quickstep_queryexecution_QueryExecutionMessages_proto

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/2b78380e/query_execution/ForemanSingleNode.cpp
----------------------------------------------------------------------
diff --git a/query_execution/ForemanSingleNode.cpp b/query_execution/ForemanSingleNode.cpp
index 3aa1f0b..cda02a7 100644
--- a/query_execution/ForemanSingleNode.cpp
+++ b/query_execution/ForemanSingleNode.cpp
@@ -25,6 +25,7 @@
 #include <vector>
 
 #include "query_execution/AdmitRequestMessage.hpp"
+#include "query_execution/PolicyEnforcerSingleNode.hpp"
 #include "query_execution/QueryExecutionTypedefs.hpp"
 #include "query_execution/QueryExecutionUtil.hpp"
 #include "query_execution/WorkerDirectory.hpp"
@@ -36,6 +37,7 @@
 #include "gflags/gflags.h"
 #include "glog/logging.h"
 
+#include "tmb/id_typedefs.h"
 #include "tmb/message_bus.h"
 #include "tmb/tagged_message.h"
 
@@ -46,6 +48,8 @@ using std::vector;
 
 namespace quickstep {
 
+class QueryHandle;
+
 DEFINE_uint64(min_load_per_worker, 2, "The minimum load defined as the number "
               "of pending work orders for the worker. This information is used "
               "by the Foreman to assign work orders to worker threads");
@@ -88,7 +92,7 @@ ForemanSingleNode::ForemanSingleNode(
     bus_->RegisterClientAsReceiver(foreman_client_id_, message_type);
   }
 
-  policy_enforcer_.reset(new PolicyEnforcer(
+  policy_enforcer_.reset(new PolicyEnforcerSingleNode(
       foreman_client_id_,
       num_numa_nodes,
       catalog_database_,

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/2b78380e/query_execution/ForemanSingleNode.hpp
----------------------------------------------------------------------
diff --git a/query_execution/ForemanSingleNode.hpp b/query_execution/ForemanSingleNode.hpp
index 7506d35..caef5e0 100644
--- a/query_execution/ForemanSingleNode.hpp
+++ b/query_execution/ForemanSingleNode.hpp
@@ -24,11 +24,12 @@
 #include <vector>
 
 #include "query_execution/ForemanBase.hpp"
-#include "query_execution/PolicyEnforcer.hpp"
+#include "query_execution/PolicyEnforcerSingleNode.hpp"
 #include "utility/Macros.hpp"
 
 #include "tmb/id_typedefs.h"
-#include "tmb/message_bus.h"
+
+namespace tmb { class MessageBus; }
 
 namespace quickstep {
 
@@ -128,7 +129,7 @@ class ForemanSingleNode final : public ForemanBase {
   CatalogDatabaseLite *catalog_database_;
   StorageManager *storage_manager_;
 
-  std::unique_ptr<PolicyEnforcer> policy_enforcer_;
+  std::unique_ptr<PolicyEnforcerSingleNode> policy_enforcer_;
 
   DISALLOW_COPY_AND_ASSIGN(ForemanSingleNode);
 };

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/2b78380e/query_execution/PolicyEnforcer.cpp
----------------------------------------------------------------------
diff --git a/query_execution/PolicyEnforcer.cpp b/query_execution/PolicyEnforcer.cpp
deleted file mode 100644
index 4cba8c5..0000000
--- a/query_execution/PolicyEnforcer.cpp
+++ /dev/null
@@ -1,254 +0,0 @@
-/**
- *   Copyright 2016, Quickstep Research Group, Computer Sciences Department,
- *     University of Wisconsin\u2014Madison.
- *
- *   Licensed 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.
- **/
-
-#include "query_execution/PolicyEnforcer.hpp"
-
-#include <cstddef>
-#include <memory>
-#include <queue>
-#include <utility>
-#include <unordered_map>
-#include <vector>
-
-#include "catalog/CatalogDatabase.hpp"
-#include "catalog/CatalogRelation.hpp"
-#include "catalog/CatalogTypedefs.hpp"
-#include "catalog/PartitionScheme.hpp"
-#include "query_execution/QueryExecutionMessages.pb.h"
-#include "query_execution/QueryExecutionState.hpp"
-#include "query_execution/QueryManagerBase.hpp"
-#include "query_execution/QueryManagerSingleNode.hpp"
-#include "query_execution/WorkerDirectory.hpp"
-#include "query_execution/WorkerMessage.hpp"
-#include "query_optimizer/QueryHandle.hpp"
-#include "relational_operators/WorkOrder.hpp"
-#include "storage/StorageBlockInfo.hpp"
-
-#include "gflags/gflags.h"
-#include "glog/logging.h"
-
-namespace quickstep {
-
-DEFINE_uint64(max_msgs_per_dispatch_round, 20, "Maximum number of messages that"
-              " can be allocated in a single round of dispatch of messages to"
-              " the workers.");
-
-bool PolicyEnforcer::admitQuery(QueryHandle *query_handle) {
-  if (admitted_queries_.size() < kMaxConcurrentQueries) {
-    // Ok to admit the query.
-    const std::size_t query_id = query_handle->query_id();
-    if (admitted_queries_.find(query_id) == admitted_queries_.end()) {
-      // Query with the same ID not present, ok to admit.
-      admitted_queries_[query_id].reset(
-          new QueryManagerSingleNode(foreman_client_id_, num_numa_nodes_, query_handle,
-                                     catalog_database_, storage_manager_, bus_));
-      return true;
-    } else {
-      LOG(ERROR) << "Query with the same ID " << query_id << " exists";
-      return false;
-    }
-  } else {
-    // This query will have to wait.
-    waiting_queries_.push(query_handle);
-    return false;
-  }
-}
-
-void PolicyEnforcer::processMessage(const TaggedMessage &tagged_message) {
-  std::size_t query_id;
-  QueryManagerBase::dag_node_index op_index;
-
-  switch (tagged_message.message_type()) {
-    case kWorkOrderCompleteMessage: {
-      serialization::NormalWorkOrderCompletionMessage proto;
-      // Note: This proto message contains the time it took to execute the
-      // WorkOrder. It can be accessed in this scope.
-      CHECK(proto.ParseFromArray(tagged_message.message(),
-                                 tagged_message.message_bytes()));
-      worker_directory_->decrementNumQueuedWorkOrders(
-          proto.worker_thread_index());
-      if (profile_individual_workorders_) {
-        recordTimeForWorkOrder(proto);
-      }
-
-      query_id = proto.query_id();
-      DCHECK(admitted_queries_.find(query_id) != admitted_queries_.end());
-
-      op_index = proto.operator_index();
-      admitted_queries_[query_id]->processWorkOrderCompleteMessage(op_index);
-      break;
-    }
-    case kRebuildWorkOrderCompleteMessage: {
-      serialization::RebuildWorkOrderCompletionMessage proto;
-      // Note: This proto message contains the time it took to execute the
-      // rebuild WorkOrder. It can be accessed in this scope.
-      CHECK(proto.ParseFromArray(tagged_message.message(),
-                                 tagged_message.message_bytes()));
-      worker_directory_->decrementNumQueuedWorkOrders(
-          proto.worker_thread_index());
-
-      query_id = proto.query_id();
-      DCHECK(admitted_queries_.find(query_id) != admitted_queries_.end());
-
-      op_index = proto.operator_index();
-      admitted_queries_[query_id]->processRebuildWorkOrderCompleteMessage(op_index);
-      break;
-    }
-    case kCatalogRelationNewBlockMessage: {
-      serialization::CatalogRelationNewBlockMessage proto;
-      CHECK(proto.ParseFromArray(tagged_message.message(),
-                                 tagged_message.message_bytes()));
-
-      const block_id block = proto.block_id();
-
-      CatalogRelation *relation =
-          static_cast<CatalogDatabase*>(catalog_database_)->getRelationByIdMutable(proto.relation_id());
-      relation->addBlock(block);
-
-      if (proto.has_partition_id()) {
-        relation->getPartitionSchemeMutable()->addBlockToPartition(
-            proto.partition_id(), block);
-      }
-      return;
-    }
-    case kDataPipelineMessage: {
-      serialization::DataPipelineMessage proto;
-      CHECK(proto.ParseFromArray(tagged_message.message(),
-                                 tagged_message.message_bytes()));
-      query_id = proto.query_id();
-      DCHECK(admitted_queries_.find(query_id) != admitted_queries_.end());
-
-      op_index = proto.operator_index();
-      admitted_queries_[query_id]->processDataPipelineMessage(
-          op_index, proto.block_id(), proto.relation_id());
-      break;
-    }
-    case kWorkOrdersAvailableMessage: {
-      serialization::WorkOrdersAvailableMessage proto;
-      CHECK(proto.ParseFromArray(tagged_message.message(),
-                                 tagged_message.message_bytes()));
-      query_id = proto.query_id();
-      DCHECK(admitted_queries_.find(query_id) != admitted_queries_.end());
-
-      op_index = proto.operator_index();
-
-      // Check if new work orders are available.
-      admitted_queries_[query_id]->fetchNormalWorkOrders(op_index);
-      break;
-    }
-    case kWorkOrderFeedbackMessage: {
-      WorkOrder::FeedbackMessage msg(
-          const_cast<void *>(tagged_message.message()),
-          tagged_message.message_bytes());
-      query_id = msg.header().query_id;
-      DCHECK(admitted_queries_.find(query_id) != admitted_queries_.end());
-
-      op_index = msg.header().rel_op_index;
-      admitted_queries_[query_id]->processFeedbackMessage(op_index, msg);
-      break;
-    }
-    default:
-      LOG(FATAL) << "Unknown message type found in PolicyEnforcer";
-  }
-  if (admitted_queries_[query_id]->queryStatus(op_index) ==
-          QueryManagerBase::QueryStatusCode::kQueryExecuted) {
-    removeQuery(query_id);
-    if (!waiting_queries_.empty()) {
-      // Admit the earliest waiting query.
-      QueryHandle *new_query = waiting_queries_.front();
-      waiting_queries_.pop();
-      admitQuery(new_query);
-    }
-  }
-}
-
-void PolicyEnforcer::getWorkerMessages(
-    std::vector<std::unique_ptr<WorkerMessage>> *worker_messages) {
-  // Iterate over admitted queries until either there are no more
-  // messages available, or the maximum number of messages have
-  // been collected.
-  DCHECK(worker_messages->empty());
-  // TODO(harshad) - Make this function generic enough so that it
-  // works well when multiple queries are getting executed.
-  std::size_t per_query_share = 0;
-  if (!admitted_queries_.empty()) {
-    per_query_share = FLAGS_max_msgs_per_dispatch_round / admitted_queries_.size();
-  } else {
-    LOG(WARNING) << "Requesting WorkerMessages when no query is running";
-    return;
-  }
-  DCHECK_GT(per_query_share, 0u);
-  std::vector<std::size_t> finished_queries_ids;
-
-  for (const auto &admitted_query_info : admitted_queries_) {
-    QueryManagerBase *curr_query_manager = admitted_query_info.second.get();
-    DCHECK(curr_query_manager != nullptr);
-    std::size_t messages_collected_curr_query = 0;
-    while (messages_collected_curr_query < per_query_share) {
-      WorkerMessage *next_worker_message =
-          static_cast<QueryManagerSingleNode*>(curr_query_manager)->getNextWorkerMessage(0, kAnyNUMANodeID);
-      if (next_worker_message != nullptr) {
-        ++messages_collected_curr_query;
-        worker_messages->push_back(std::unique_ptr<WorkerMessage>(next_worker_message));
-      } else {
-        // No more work ordes from the current query at this time.
-        // Check if the query's execution is over.
-        if (curr_query_manager->getQueryExecutionState().hasQueryExecutionFinished()) {
-          // If the query has been executed, remove it.
-          finished_queries_ids.push_back(admitted_query_info.first);
-        }
-        break;
-      }
-    }
-  }
-  for (const std::size_t finished_qid : finished_queries_ids) {
-    removeQuery(finished_qid);
-  }
-}
-
-void PolicyEnforcer::removeQuery(const std::size_t query_id) {
-  DCHECK(admitted_queries_.find(query_id) != admitted_queries_.end());
-  if (!admitted_queries_[query_id]->getQueryExecutionState().hasQueryExecutionFinished()) {
-    LOG(WARNING) << "Removing query with ID " << query_id
-                 << " that hasn't finished its execution";
-  }
-  admitted_queries_.erase(query_id);
-}
-
-bool PolicyEnforcer::admitQueries(
-    const std::vector<QueryHandle*> &query_handles) {
-  for (QueryHandle *curr_query : query_handles) {
-    if (!admitQuery(curr_query)) {
-      return false;
-    }
-  }
-  return true;
-}
-
-void PolicyEnforcer::recordTimeForWorkOrder(
-    const serialization::NormalWorkOrderCompletionMessage &proto) {
-  const std::size_t query_id = proto.query_id();
-  if (workorder_time_recorder_.find(query_id) == workorder_time_recorder_.end()) {
-    workorder_time_recorder_[query_id];
-  }
-  workorder_time_recorder_[query_id].emplace_back(
-      proto.worker_thread_index(),
-      proto.operator_index(),
-      proto.execution_time_in_microseconds());
-}
-
-}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/2b78380e/query_execution/PolicyEnforcer.hpp
----------------------------------------------------------------------
diff --git a/query_execution/PolicyEnforcer.hpp b/query_execution/PolicyEnforcer.hpp
deleted file mode 100644
index 8bd6d92..0000000
--- a/query_execution/PolicyEnforcer.hpp
+++ /dev/null
@@ -1,223 +0,0 @@
-/**
- *   Copyright 2016, Quickstep Research Group, Computer Sciences Department,
- *     University of Wisconsin\u2014Madison.
- *
- *   Licensed 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.
- **/
-
-#ifndef QUICKSTEP_QUERY_EXECUTION_POLICY_ENFORCER_HPP_
-#define QUICKSTEP_QUERY_EXECUTION_POLICY_ENFORCER_HPP_
-
-#include <cstddef>
-#include <memory>
-#include <queue>
-#include <tuple>
-#include <unordered_map>
-#include <vector>
-
-#include "query_execution/QueryExecutionTypedefs.hpp"
-#include "query_execution/QueryManagerBase.hpp"
-#include "query_execution/WorkerMessage.hpp"
-#include "utility/Macros.hpp"
-
-#include "glog/logging.h"
-
-#include "tmb/id_typedefs.h"
-
-namespace tmb { class MessageBus; }
-
-namespace quickstep {
-
-class CatalogDatabaseLite;
-class QueryHandle;
-class StorageManager;
-class WorkerDirectory;
-
-namespace serialization { class NormalWorkOrderCompletionMessage; }
-
-/** \addtogroup QueryExecution
- *  @{
- */
-
-/**
- * @brief A class that ensures that a high level policy is maintained
- *        in sharing resources among concurrent queries.
- **/
-class PolicyEnforcer {
- public:
-  /**
-   * @brief Constructor.
-   *
-   * @param foreman_client_id The TMB client ID of the Foreman.
-   * @param num_numa_nodes Number of NUMA nodes used by the system.
-   * @param catalog_database The CatalogDatabase used.
-   * @param storage_manager The StorageManager used.
-   * @param bus The TMB.
-   **/
-  PolicyEnforcer(const tmb::client_id foreman_client_id,
-                 const std::size_t num_numa_nodes,
-                 CatalogDatabaseLite *catalog_database,
-                 StorageManager *storage_manager,
-                 WorkerDirectory *worker_directory,
-                 tmb::MessageBus *bus,
-                 const bool profile_individual_workorders = false)
-      : foreman_client_id_(foreman_client_id),
-        num_numa_nodes_(num_numa_nodes),
-        catalog_database_(catalog_database),
-        storage_manager_(storage_manager),
-        worker_directory_(worker_directory),
-        bus_(bus),
-        profile_individual_workorders_(profile_individual_workorders) {}
-
-  /**
-   * @brief Destructor.
-   **/
-  ~PolicyEnforcer() {
-    if (hasQueries()) {
-      LOG(WARNING) << "Destructing PolicyEnforcer with some unfinished or "
-                      "waiting queries";
-    }
-  }
-
-  /**
-   * @brief Admit a query to the system.
-   *
-   * @param query_handle The QueryHandle for the new query.
-   *
-   * @return Whether the query was admitted to the system.
-   **/
-  bool admitQuery(QueryHandle *query_handle);
-
-  /**
-   * @brief Admit multiple queries in the system.
-   *
-   * @note In the current simple implementation, we only allow one active
-   *       query in the system. Other queries will have to wait.
-   *
-   * @param query_handles A vector of QueryHandles for the queries to be
-   *        admitted.
-   *
-   * @return True if all the queries were admitted, false if at least one query
-   *         was not admitted.
-   **/
-  bool admitQueries(const std::vector<QueryHandle*> &query_handles);
-
-  /**
-   * @brief Remove a given query that is under execution.
-   *
-   * @note This function is made public so that it is possible for a query
-   *       to be killed. Otherwise, it should only be used privately by the
-   *       class.
-   *
-   * TODO(harshad) - Extend this function to support removal of waiting queries.
-   *
-   * @param query_id The ID of the query to be removed.
-   **/
-  void removeQuery(const std::size_t query_id);
-
-  /**
-   * @brief Get worker messages to be dispatched. These worker messages come
-   *        from the active queries.
-   *
-   * @param worker_messages The worker messages to be dispatched.
-   **/
-  void getWorkerMessages(
-      std::vector<std::unique_ptr<WorkerMessage>> *worker_messages);
-
-  /**
-   * @brief Process a message sent to the Foreman, which gets passed on to the
-   *        policy enforcer.
-   *
-   * @param message The message.
-   **/
-  void processMessage(const TaggedMessage &tagged_message);
-
-  /**
-   * @brief Check if there are any queries to be executed.
-   *
-   * @return True if there is at least one active or waiting query, false if
-   *         the policy enforcer doesn't have any query.
-   **/
-  inline bool hasQueries() const {
-    return !(admitted_queries_.empty() && waiting_queries_.empty());
-  }
-
-  /**
-   * @brief Get the profiling results for individual work order execution for a
-   *        given query.
-   *
-   * @note This function should only be called if profiling individual work
-   *       orders option is enabled.
-   *
-   * @param query_id The ID of the query for which the profiling results are
-   *        requested.
-   *
-   * @return A vector of tuples, each being a single profiling entry.
-   **/
-  inline const std::vector<std::tuple<std::size_t, std::size_t, std::size_t>>&
-      getProfilingResults(const std::size_t query_id) const {
-    DCHECK(profile_individual_workorders_);
-    DCHECK(workorder_time_recorder_.find(query_id) !=
-           workorder_time_recorder_.end());
-    return workorder_time_recorder_.at(query_id);
-  }
-
- private:
-  static constexpr std::size_t kMaxConcurrentQueries = 1;
-
-  /**
-   * @brief Record the execution time for a finished WorkOrder.
-   *
-   * TODO(harshad) - Extend the functionality to rebuild work orders.
-   *
-   * @param proto The completion message proto sent after the WorkOrder
-   *        execution.
-   **/
-  void recordTimeForWorkOrder(
-      const serialization::NormalWorkOrderCompletionMessage &proto);
-
-  const tmb::client_id foreman_client_id_;
-  const std::size_t num_numa_nodes_;
-
-  CatalogDatabaseLite *catalog_database_;
-  StorageManager *storage_manager_;
-  WorkerDirectory *worker_directory_;
-
-  tmb::MessageBus *bus_;
-  const bool profile_individual_workorders_;
-
-  // Key = query ID, value = QueryManagerBase* for the key query.
-  std::unordered_map<std::size_t, std::unique_ptr<QueryManagerBase>> admitted_queries_;
-
-  // The queries which haven't been admitted yet.
-  std::queue<QueryHandle*> waiting_queries_;
-
-  // Key = Query ID.
-  // Value = A tuple indicating a record of executing a work order.
-  // Within a tuple ...
-  // 1st element: Logical worker ID.
-  // 2nd element: Operator ID.
-  // 3rd element: Time in microseconds to execute the work order.
-  std::unordered_map<
-      std::size_t,
-      std::vector<std::tuple<std::size_t, std::size_t, std::size_t>>>
-      workorder_time_recorder_;
-
-  DISALLOW_COPY_AND_ASSIGN(PolicyEnforcer);
-};
-
-/** @} */
-
-}  // namespace quickstep
-
-#endif  // QUICKSTEP_QUERY_EXECUTION_POLICY_ENFORCER_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/2b78380e/query_execution/PolicyEnforcerBase.cpp
----------------------------------------------------------------------
diff --git a/query_execution/PolicyEnforcerBase.cpp b/query_execution/PolicyEnforcerBase.cpp
new file mode 100644
index 0000000..d16a502
--- /dev/null
+++ b/query_execution/PolicyEnforcerBase.cpp
@@ -0,0 +1,177 @@
+/**
+ *   Copyright 2016, Quickstep Research Group, Computer Sciences Department,
+ *     University of Wisconsin\u2014Madison.
+ *
+ *   Licensed 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.
+ **/
+
+#include "query_execution/PolicyEnforcerBase.hpp"
+
+#include <cstddef>
+#include <memory>
+#include <queue>
+#include <unordered_map>
+#include <vector>
+
+#include "catalog/CatalogDatabase.hpp"
+#include "catalog/CatalogRelation.hpp"
+#include "catalog/PartitionScheme.hpp"
+#include "query_execution/QueryExecutionMessages.pb.h"
+#include "query_execution/QueryExecutionState.hpp"
+#include "query_execution/QueryManagerBase.hpp"
+#include "relational_operators/WorkOrder.hpp"
+#include "storage/StorageBlockInfo.hpp"
+
+#include "glog/logging.h"
+
+namespace quickstep {
+
+void PolicyEnforcerBase::processMessage(const TaggedMessage &tagged_message) {
+  std::size_t query_id;
+  QueryManagerBase::dag_node_index op_index;
+
+  switch (tagged_message.message_type()) {
+    case kWorkOrderCompleteMessage: {
+      serialization::NormalWorkOrderCompletionMessage proto;
+      // Note: This proto message contains the time it took to execute the
+      // WorkOrder. It can be accessed in this scope.
+      CHECK(proto.ParseFromArray(tagged_message.message(),
+                                 tagged_message.message_bytes()));
+      decrementNumQueuedWorkOrders(proto.worker_thread_index());
+
+      if (profile_individual_workorders_) {
+        recordTimeForWorkOrder(proto);
+      }
+
+      query_id = proto.query_id();
+      DCHECK(admitted_queries_.find(query_id) != admitted_queries_.end());
+
+      op_index = proto.operator_index();
+      admitted_queries_[query_id]->processWorkOrderCompleteMessage(op_index);
+      break;
+    }
+    case kRebuildWorkOrderCompleteMessage: {
+      serialization::RebuildWorkOrderCompletionMessage proto;
+      // Note: This proto message contains the time it took to execute the
+      // rebuild WorkOrder. It can be accessed in this scope.
+      CHECK(proto.ParseFromArray(tagged_message.message(),
+                                 tagged_message.message_bytes()));
+      decrementNumQueuedWorkOrders(proto.worker_thread_index());
+
+      query_id = proto.query_id();
+      DCHECK(admitted_queries_.find(query_id) != admitted_queries_.end());
+
+      op_index = proto.operator_index();
+      admitted_queries_[query_id]->processRebuildWorkOrderCompleteMessage(op_index);
+      break;
+    }
+    case kCatalogRelationNewBlockMessage: {
+      serialization::CatalogRelationNewBlockMessage proto;
+      CHECK(proto.ParseFromArray(tagged_message.message(),
+                                 tagged_message.message_bytes()));
+
+      const block_id block = proto.block_id();
+
+      CatalogRelation *relation =
+          static_cast<CatalogDatabase*>(catalog_database_)->getRelationByIdMutable(proto.relation_id());
+      relation->addBlock(block);
+
+      if (proto.has_partition_id()) {
+        relation->getPartitionSchemeMutable()->addBlockToPartition(
+            proto.partition_id(), block);
+      }
+      return;
+    }
+    case kDataPipelineMessage: {
+      serialization::DataPipelineMessage proto;
+      CHECK(proto.ParseFromArray(tagged_message.message(),
+                                 tagged_message.message_bytes()));
+      query_id = proto.query_id();
+      DCHECK(admitted_queries_.find(query_id) != admitted_queries_.end());
+
+      op_index = proto.operator_index();
+      admitted_queries_[query_id]->processDataPipelineMessage(
+          op_index, proto.block_id(), proto.relation_id());
+      break;
+    }
+    case kWorkOrdersAvailableMessage: {
+      serialization::WorkOrdersAvailableMessage proto;
+      CHECK(proto.ParseFromArray(tagged_message.message(),
+                                 tagged_message.message_bytes()));
+      query_id = proto.query_id();
+      DCHECK(admitted_queries_.find(query_id) != admitted_queries_.end());
+
+      op_index = proto.operator_index();
+
+      // Check if new work orders are available.
+      admitted_queries_[query_id]->fetchNormalWorkOrders(op_index);
+      break;
+    }
+    case kWorkOrderFeedbackMessage: {
+      WorkOrder::FeedbackMessage msg(
+          const_cast<void *>(tagged_message.message()),
+          tagged_message.message_bytes());
+      query_id = msg.header().query_id;
+      DCHECK(admitted_queries_.find(query_id) != admitted_queries_.end());
+
+      op_index = msg.header().rel_op_index;
+      admitted_queries_[query_id]->processFeedbackMessage(op_index, msg);
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unknown message type found in PolicyEnforcer";
+  }
+  if (admitted_queries_[query_id]->queryStatus(op_index) ==
+          QueryManagerBase::QueryStatusCode::kQueryExecuted) {
+    removeQuery(query_id);
+    if (!waiting_queries_.empty()) {
+      // Admit the earliest waiting query.
+      QueryHandle *new_query = waiting_queries_.front();
+      waiting_queries_.pop();
+      admitQuery(new_query);
+    }
+  }
+}
+
+void PolicyEnforcerBase::removeQuery(const std::size_t query_id) {
+  DCHECK(admitted_queries_.find(query_id) != admitted_queries_.end());
+  if (!admitted_queries_[query_id]->getQueryExecutionState().hasQueryExecutionFinished()) {
+    LOG(WARNING) << "Removing query with ID " << query_id
+                 << " that hasn't finished its execution";
+  }
+  admitted_queries_.erase(query_id);
+}
+
+bool PolicyEnforcerBase::admitQueries(
+    const std::vector<QueryHandle*> &query_handles) {
+  for (QueryHandle *curr_query : query_handles) {
+    if (!admitQuery(curr_query)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void PolicyEnforcerBase::recordTimeForWorkOrder(
+    const serialization::NormalWorkOrderCompletionMessage &proto) {
+  const std::size_t query_id = proto.query_id();
+  if (workorder_time_recorder_.find(query_id) == workorder_time_recorder_.end()) {
+    workorder_time_recorder_[query_id];
+  }
+  workorder_time_recorder_[query_id].emplace_back(
+      proto.worker_thread_index(),
+      proto.operator_index(),
+      proto.execution_time_in_microseconds());
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/2b78380e/query_execution/PolicyEnforcerBase.hpp
----------------------------------------------------------------------
diff --git a/query_execution/PolicyEnforcerBase.hpp b/query_execution/PolicyEnforcerBase.hpp
new file mode 100644
index 0000000..0482ebc
--- /dev/null
+++ b/query_execution/PolicyEnforcerBase.hpp
@@ -0,0 +1,196 @@
+/**
+ *   Copyright 2016, Quickstep Research Group, Computer Sciences Department,
+ *     University of Wisconsin\u2014Madison.
+ *
+ *   Licensed 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.
+ **/
+
+#ifndef QUICKSTEP_QUERY_EXECUTION_POLICY_ENFORCER_BASE_HPP_
+#define QUICKSTEP_QUERY_EXECUTION_POLICY_ENFORCER_BASE_HPP_
+
+#include <cstddef>
+#include <memory>
+#include <queue>
+#include <tuple>
+#include <unordered_map>
+#include <vector>
+
+#include "query_execution/QueryExecutionTypedefs.hpp"
+#include "query_execution/QueryManagerBase.hpp"
+#include "utility/Macros.hpp"
+
+#include "glog/logging.h"
+
+namespace quickstep {
+
+class CatalogDatabaseLite;
+class QueryHandle;
+
+namespace serialization { class NormalWorkOrderCompletionMessage; }
+
+/** \addtogroup QueryExecution
+ *  @{
+ */
+
+/**
+ * @brief A base class that ensures that a high level policy is maintained
+ *        in sharing resources among concurrent queries.
+ **/
+class PolicyEnforcerBase {
+ public:
+  /**
+   * @brief Constructor.
+   *
+   * @param catalog_database The CatalogDatabase used.
+   * @param profile_individual_workorders If true, profile each normal work order.
+   **/
+  PolicyEnforcerBase(CatalogDatabaseLite *catalog_database,
+                     const bool profile_individual_workorders)
+      : catalog_database_(catalog_database),
+        profile_individual_workorders_(profile_individual_workorders) {}
+
+  /**
+   * @brief Virtual Destructor.
+   **/
+  virtual ~PolicyEnforcerBase() {
+    if (hasQueries()) {
+      LOG(WARNING) << "Destructing PolicyEnforcer with some unfinished or "
+                      "waiting queries";
+    }
+  }
+
+  /**
+   * @brief Admit multiple queries in the system.
+   *
+   * @note In the current simple implementation, we only allow one active
+   *       query in the system. Other queries will have to wait.
+   *
+   * @param query_handles A vector of QueryHandles for the queries to be
+   *        admitted.
+   *
+   * @return True if all the queries were admitted, false if at least one query
+   *         was not admitted.
+   **/
+  bool admitQueries(const std::vector<QueryHandle*> &query_handles);
+
+  /**
+   * @brief Remove a given query that is under execution.
+   *
+   * @note This function is made public so that it is possible for a query
+   *       to be killed. Otherwise, it should only be used privately by the
+   *       class.
+   *
+   * TODO(harshad) - Extend this function to support removal of waiting queries.
+   *
+   * @param query_id The ID of the query to be removed.
+   **/
+  void removeQuery(const std::size_t query_id);
+
+  /**
+   * @brief Process a message sent to the Foreman, which gets passed on to the
+   *        policy enforcer.
+   *
+   * @param message The message.
+   **/
+  void processMessage(const TaggedMessage &tagged_message);
+
+  /**
+   * @brief Check if there are any queries to be executed.
+   *
+   * @return True if there is at least one active or waiting query, false if
+   *         the policy enforcer doesn't have any query.
+   **/
+  inline bool hasQueries() const {
+    return !(admitted_queries_.empty() && waiting_queries_.empty());
+  }
+
+  /**
+   * @brief Get the profiling results for individual work order execution for a
+   *        given query.
+   *
+   * @note This function should only be called if profiling individual work
+   *       orders option is enabled.
+   *
+   * @param query_id The ID of the query for which the profiling results are
+   *        requested.
+   *
+   * @return A vector of tuples, each being a single profiling entry.
+   **/
+  inline const std::vector<std::tuple<std::size_t, std::size_t, std::size_t>>&
+      getProfilingResults(const std::size_t query_id) const {
+    DCHECK(profile_individual_workorders_);
+    DCHECK(workorder_time_recorder_.find(query_id) !=
+           workorder_time_recorder_.end());
+    return workorder_time_recorder_.at(query_id);
+  }
+
+ protected:
+  static constexpr std::size_t kMaxConcurrentQueries = 1;
+
+  /**
+   * @brief Record the execution time for a finished WorkOrder.
+   *
+   * TODO(harshad) - Extend the functionality to rebuild work orders.
+   *
+   * @param proto The completion message proto sent after the WorkOrder
+   *        execution.
+   **/
+  void recordTimeForWorkOrder(
+      const serialization::NormalWorkOrderCompletionMessage &proto);
+
+  CatalogDatabaseLite *catalog_database_;
+
+  const bool profile_individual_workorders_;
+
+  // Key = query ID, value = QueryManagerBase* for the key query.
+  std::unordered_map<std::size_t, std::unique_ptr<QueryManagerBase>> admitted_queries_;
+
+  // The queries which haven't been admitted yet.
+  std::queue<QueryHandle*> waiting_queries_;
+
+  // Key = Query ID.
+  // Value = A tuple indicating a record of executing a work order.
+  // Within a tuple ...
+  // 1st element: Logical worker ID.
+  // 2nd element: Operator ID.
+  // 3rd element: Time in microseconds to execute the work order.
+  std::unordered_map<
+      std::size_t,
+      std::vector<std::tuple<std::size_t, std::size_t, std::size_t>>>
+      workorder_time_recorder_;
+
+ private:
+  /**
+   * @brief Admit a query to the system.
+   *
+   * @param query_handle The QueryHandle for the new query.
+   *
+   * @return Whether the query was admitted to the system.
+   **/
+  virtual bool admitQuery(QueryHandle *query_handle) = 0;
+
+  /**
+   * @brief Decrement the number of queued workorders for the given worker by 1.
+   *
+   * @param worker_index The logical ID of the given worker.
+   **/
+  virtual void decrementNumQueuedWorkOrders(const std::size_t worker_index) = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(PolicyEnforcerBase);
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_QUERY_EXECUTION_POLICY_ENFORCER_BASE_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/2b78380e/query_execution/PolicyEnforcerSingleNode.cpp
----------------------------------------------------------------------
diff --git a/query_execution/PolicyEnforcerSingleNode.cpp b/query_execution/PolicyEnforcerSingleNode.cpp
new file mode 100644
index 0000000..549e39f
--- /dev/null
+++ b/query_execution/PolicyEnforcerSingleNode.cpp
@@ -0,0 +1,110 @@
+/**
+ *   Licensed 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.
+ **/
+
+#include "query_execution/PolicyEnforcerSingleNode.hpp"
+
+#include <cstddef>
+#include <memory>
+#include <queue>
+#include <utility>
+#include <unordered_map>
+#include <vector>
+
+#include "catalog/CatalogTypedefs.hpp"
+#include "query_execution/QueryExecutionState.hpp"
+#include "query_execution/QueryManagerBase.hpp"
+#include "query_execution/QueryManagerSingleNode.hpp"
+#include "query_execution/WorkerDirectory.hpp"
+#include "query_execution/WorkerMessage.hpp"
+#include "query_optimizer/QueryHandle.hpp"
+
+#include "gflags/gflags.h"
+#include "glog/logging.h"
+
+namespace quickstep {
+
+DEFINE_uint64(max_msgs_per_dispatch_round, 20, "Maximum number of messages that"
+              " can be allocated in a single round of dispatch of messages to"
+              " the workers.");
+
+void PolicyEnforcerSingleNode::getWorkerMessages(
+    std::vector<std::unique_ptr<WorkerMessage>> *worker_messages) {
+  // Iterate over admitted queries until either there are no more
+  // messages available, or the maximum number of messages have
+  // been collected.
+  DCHECK(worker_messages->empty());
+  // TODO(harshad) - Make this function generic enough so that it
+  // works well when multiple queries are getting executed.
+  std::size_t per_query_share = 0;
+  if (!admitted_queries_.empty()) {
+    per_query_share = FLAGS_max_msgs_per_dispatch_round / admitted_queries_.size();
+  } else {
+    LOG(WARNING) << "Requesting WorkerMessages when no query is running";
+    return;
+  }
+  DCHECK_GT(per_query_share, 0u);
+  std::vector<std::size_t> finished_queries_ids;
+
+  for (const auto &admitted_query_info : admitted_queries_) {
+    QueryManagerBase *curr_query_manager = admitted_query_info.second.get();
+    DCHECK(curr_query_manager != nullptr);
+    std::size_t messages_collected_curr_query = 0;
+    while (messages_collected_curr_query < per_query_share) {
+      WorkerMessage *next_worker_message =
+          static_cast<QueryManagerSingleNode*>(curr_query_manager)->getNextWorkerMessage(0, kAnyNUMANodeID);
+      if (next_worker_message != nullptr) {
+        ++messages_collected_curr_query;
+        worker_messages->push_back(std::unique_ptr<WorkerMessage>(next_worker_message));
+      } else {
+        // No more work ordes from the current query at this time.
+        // Check if the query's execution is over.
+        if (curr_query_manager->getQueryExecutionState().hasQueryExecutionFinished()) {
+          // If the query has been executed, remove it.
+          finished_queries_ids.push_back(admitted_query_info.first);
+        }
+        break;
+      }
+    }
+  }
+  for (const std::size_t finished_qid : finished_queries_ids) {
+    removeQuery(finished_qid);
+  }
+}
+
+bool PolicyEnforcerSingleNode::admitQuery(QueryHandle *query_handle) {
+  if (admitted_queries_.size() < PolicyEnforcerBase::kMaxConcurrentQueries) {
+    // Ok to admit the query.
+    const std::size_t query_id = query_handle->query_id();
+    if (admitted_queries_.find(query_id) == admitted_queries_.end()) {
+      // Query with the same ID not present, ok to admit.
+      admitted_queries_[query_id].reset(
+          new QueryManagerSingleNode(foreman_client_id_, num_numa_nodes_, query_handle,
+                                     catalog_database_, storage_manager_, bus_));
+      return true;
+    } else {
+      LOG(ERROR) << "Query with the same ID " << query_id << " exists";
+      return false;
+    }
+  } else {
+    // This query will have to wait.
+    waiting_queries_.push(query_handle);
+    return false;
+  }
+}
+
+void PolicyEnforcerSingleNode::decrementNumQueuedWorkOrders(const std::size_t worker_index) {
+  worker_directory_->decrementNumQueuedWorkOrders(worker_index);
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/2b78380e/query_execution/PolicyEnforcerSingleNode.hpp
----------------------------------------------------------------------
diff --git a/query_execution/PolicyEnforcerSingleNode.hpp b/query_execution/PolicyEnforcerSingleNode.hpp
new file mode 100644
index 0000000..671fd83
--- /dev/null
+++ b/query_execution/PolicyEnforcerSingleNode.hpp
@@ -0,0 +1,104 @@
+/**
+ *   Licensed 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.
+ **/
+
+#ifndef QUICKSTEP_QUERY_EXECUTION_POLICY_ENFORCER_SINGLE_NODE_HPP_
+#define QUICKSTEP_QUERY_EXECUTION_POLICY_ENFORCER_SINGLE_NODE_HPP_
+
+#include <cstddef>
+#include <memory>
+#include <vector>
+
+#include "query_execution/PolicyEnforcerBase.hpp"
+#include "utility/Macros.hpp"
+
+#include "tmb/id_typedefs.h"
+
+namespace tmb { class MessageBus; }
+
+namespace quickstep {
+
+class CatalogDatabaseLite;
+class QueryHandle;
+class StorageManager;
+class WorkerDirectory;
+class WorkerMessage;
+
+/** \addtogroup QueryExecution
+ *  @{
+ */
+
+/**
+ * @brief A class that ensures that a high level policy is maintained
+ *        in sharing resources among concurrent queries.
+ **/
+class PolicyEnforcerSingleNode final : public PolicyEnforcerBase {
+ public:
+  /**
+   * @brief Constructor.
+   *
+   * @param foreman_client_id The TMB client ID of the Foreman.
+   * @param num_numa_nodes Number of NUMA nodes used by the system.
+   * @param catalog_database The CatalogDatabase used.
+   * @param storage_manager The StorageManager used.
+   * @param bus The TMB.
+   **/
+  PolicyEnforcerSingleNode(const tmb::client_id foreman_client_id,
+                           const std::size_t num_numa_nodes,
+                           CatalogDatabaseLite *catalog_database,
+                           StorageManager *storage_manager,
+                           WorkerDirectory *worker_directory,
+                           tmb::MessageBus *bus,
+                           const bool profile_individual_workorders = false)
+      : PolicyEnforcerBase(catalog_database, profile_individual_workorders),
+        foreman_client_id_(foreman_client_id),
+        num_numa_nodes_(num_numa_nodes),
+        storage_manager_(storage_manager),
+        worker_directory_(worker_directory),
+        bus_(bus) {}
+
+  /**
+   * @brief Destructor.
+   **/
+  ~PolicyEnforcerSingleNode() override {}
+
+  bool admitQuery(QueryHandle *query_handle) override;
+
+  /**
+   * @brief Get worker messages to be dispatched. These worker messages come
+   *        from the active queries.
+   *
+   * @param worker_messages The worker messages to be dispatched.
+   **/
+  void getWorkerMessages(
+      std::vector<std::unique_ptr<WorkerMessage>> *worker_messages);
+
+ private:
+  void decrementNumQueuedWorkOrders(const std::size_t worker_index) override;
+
+  const tmb::client_id foreman_client_id_;
+  const std::size_t num_numa_nodes_;
+
+  StorageManager *storage_manager_;
+  WorkerDirectory *worker_directory_;
+
+  tmb::MessageBus *bus_;
+
+  DISALLOW_COPY_AND_ASSIGN(PolicyEnforcerSingleNode);
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_QUERY_EXECUTION_POLICY_ENFORCER_SINGLE_NODE_HPP_