You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@quickstep.apache.org by na...@apache.org on 2016/07/17 23:10:05 UTC

[1/3] incubator-quickstep git commit: Adaptively adjust the batch size.

Repository: incubator-quickstep
Updated Branches:
  refs/heads/lookahead_exact 1cabb63ea -> 6df54af46


Adaptively adjust the batch 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/08833a8f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-quickstep/tree/08833a8f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-quickstep/diff/08833a8f

Branch: refs/heads/lookahead_exact
Commit: 08833a8fcef5d2d07cc81da6af274fae5059d35d
Parents: 1cabb63
Author: Navneet Potti <na...@gmail.com>
Authored: Sat Jul 16 17:10:32 2016 -0500
Committer: Navneet Potti <na...@gmail.com>
Committed: Sat Jul 16 17:58:08 2016 -0500

----------------------------------------------------------------------
 storage/HashTable.hpp | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/08833a8f/storage/HashTable.hpp
----------------------------------------------------------------------
diff --git a/storage/HashTable.hpp b/storage/HashTable.hpp
index 564b783..a74d71c 100644
--- a/storage/HashTable.hpp
+++ b/storage/HashTable.hpp
@@ -2274,12 +2274,22 @@ void HashTable<ValueT, resizable, serializable, force_key_copy, allow_duplicate_
       bloom_filter_adapter.reset(new BloomFilterAdapter(
               probe_bloom_filters_, probe_attribute_ids_, attr_size_vectors));
 
-      static const uint32_t kMaxBatchSize = FLAGS_bloom_adapter_batch_size;
+      // We want to have large batch sizes for cache efficiency while probeing,
+      // but small batch sizes to ensure that the adaptation logic kicks in
+      // (and does early). We use exponentially increasing batch sizes to
+      // achieve a balance between the two.
+      //
+      // We also keep track of num_tuples_left in the block, to ensure that
+      // we don't reserve an unnecessarily large vector.
+      std::uint32_t batch_size_try = FLAGS_bloom_adapter_batch_size;
+      std::uint32_t num_tuples_left = accessor->getNumTuples();
       std::vector<tuple_id> batch;
-      batch.reserve(kMaxBatchSize);
 
       do {
-        while (batch.size() < kMaxBatchSize && accessor->next())
+        std::uint32_t batch_size =
+            batch_size_try > num_tuples_left ? batch_size_try : num_tuples_left;
+        batch.reserve(batch_size);
+        while (batch.size() < batch_size && accessor->next())
           batch.push_back(accessor->getCurrentPosition());
 
         std::size_t num_hits = bloom_filter_adapter->bulkProbe(accessor, batch);
@@ -2303,6 +2313,8 @@ void HashTable<ValueT, resizable, serializable, force_key_copy, allow_duplicate_
           }
         }
         batch.clear();
+        num_tuples_left -= batch_size;
+        batch_size_try = batch_size * 2;
       } while (!accessor->iterationFinished());
     }
 


[3/3] incubator-quickstep git commit: Optimize RowStore to PackedRowStore bulk insert

Posted by na...@apache.org.
Optimize RowStore to PackedRowStore bulk insert


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

Branch: refs/heads/lookahead_exact
Commit: 6df54af46263a2de7d3a27ceb2295b72064ffdce
Parents: 50682c8
Author: Navneet Potti <na...@gmail.com>
Authored: Thu Jun 23 16:21:47 2016 -0500
Committer: Navneet Potti <na...@gmail.com>
Committed: Sat Jul 16 20:51:04 2016 -0500

----------------------------------------------------------------------
 storage/PackedRowStoreTupleStorageSubBlock.cpp | 281 ++++++++++++++++----
 storage/PackedRowStoreTupleStorageSubBlock.hpp |   7 +
 2 files changed, 242 insertions(+), 46 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/6df54af4/storage/PackedRowStoreTupleStorageSubBlock.cpp
----------------------------------------------------------------------
diff --git a/storage/PackedRowStoreTupleStorageSubBlock.cpp b/storage/PackedRowStoreTupleStorageSubBlock.cpp
index ef83a29..ba747d3 100644
--- a/storage/PackedRowStoreTupleStorageSubBlock.cpp
+++ b/storage/PackedRowStoreTupleStorageSubBlock.cpp
@@ -204,68 +204,257 @@ tuple_id PackedRowStoreTupleStorageSubBlock::bulkInsertTuples(ValueAccessor *acc
   return header_->num_tuples - original_num_tuples;
 }
 
-tuple_id PackedRowStoreTupleStorageSubBlock::bulkInsertTuplesWithRemappedAttributes(
+// Unnamed namespace for helper functions that are implementation details and
+// need not be exposed in the interface of the class (i.e., in the *.hpp file).
+// 
+// The first helper function here is used to provide an optimized bulk insertion path
+// from RowStore to RowStore blocks, where contiguous attributes are copied
+// together. For uniformity, there is another helper function that provides
+// semantically identical `runs` for other layouts as well.
+namespace {
+enum RunType {
+  kContiguousAttributes,
+  kNullableAttribute,
+};
+
+struct RunInfo {
+  RunType run_type_;
+  attribute_id mapped_attr_id_;
+  std::size_t size_;
+  int nullable_attr_idx_;
+
+  static const int invalid_nullable_attr_idx_ = -1;
+
+  RunInfo(RunType run_type, attribute_id mapped_attr_id, std::size_t size,
+          int nullable_attr_idx)
+      : run_type_(run_type),
+        mapped_attr_id_(mapped_attr_id),
+        size_(size),
+        nullable_attr_idx_(nullable_attr_idx) {}
+
+  static RunInfo getContiguousAttrsRun(attribute_id mapped_attr_id,
+                               std::size_t size) {
+    return RunInfo(kContiguousAttributes, mapped_attr_id,
+                   size, invalid_nullable_attr_idx_);
+  }
+
+  static RunInfo getNullableAttrRun(attribute_id mapped_attr_id, std::size_t size,
+                            int nullable_attr_idx) {
+    return RunInfo(kNullableAttribute, mapped_attr_id,
+                   size, nullable_attr_idx);
+  }
+
+};
+
+// This helper function examines the schema of the input and output blocks
+// and determines runs of attributes that can be copied at once.
+// 
+// For the i-th run of contiguous non-nullable attributes in the attribute map,
+// `runs[i]` will contain a RunInfo struct.
+//   run_start is the starting (mapped) attribute id of the run, and
+//   size is the total size (i.e., num bytes) of all attributes in the run.
+//
+// For 4B integer attrs, with attribute_map {0,2,5,6,7,3,4,10}
+// `runs` will be {(0,4), (2,4), (5,12), (3,8), (10,4)}
+//
+// Nullable attributes also break runs, just like non-contiguous attributes.
+// They are recorded in `runs` using a sentinel run size kNullAttrRunSize (-1).
+// In the above example, if 6 was nullable,
+// `runs` will be {(0,4), (2,4), (5,4), (6,-1), (7,4), (3,8), (10,4)}
+void getRunsForRowLayoutHelper(
+    const CatalogRelationSchema &relation,
+    const std::vector<attribute_id> &attribute_map,
+    const std::vector<std::size_t> &attrs_max_size,
+    std::vector<RunInfo> &runs) {
+  std::size_t num_attrs = attribute_map.size();
+  std::size_t curr_run_size = 0;
+  std::size_t curr_run_start = 0;
+  for (std::size_t curr_attr = 0; curr_attr < num_attrs; ++curr_attr) {
+    int null_idx = relation.getNullableAttributeIndex(curr_attr);
+    if (null_idx == kInvalidCatalogId) {
+      if (curr_run_size != 0) {
+        if (attribute_map[curr_attr] == 1 + attribute_map[curr_attr - 1]) {
+          // If curr_attr is non-nullable,
+          // ... and if there is an ongoing run,
+          // ... and if curr_attr is adjacent to the previous attribute,
+          // ... then we continue the current run.
+          curr_run_size += attrs_max_size[curr_attr];
+        } else {
+          // If curr_attr is non-nullable
+          // ... and there is an ongoing run,
+          // ... but curr_attr is not adjacent to the previous attribute,
+          // ... then we end the current run,
+          runs.push_back(RunInfo::getContiguousAttrsRun(
+              attribute_map[curr_run_start], curr_run_size));
+
+          // ... and begin a new run.
+          curr_run_size = attrs_max_size[curr_attr];
+          curr_run_start = curr_attr;
+        }
+      } else {
+        // If curr_attr is non-nullable, ...
+        // ... but there is no ongoing run,
+        // ... then we begin a new run.
+        curr_run_size = attrs_max_size[curr_attr];
+        curr_run_start = curr_attr;
+      }
+    } else {
+      if (curr_run_size != 0) {
+        // If curr_attr is nullable,
+        // ... and there is an ongoing run,
+        // ... then we end the current run,
+        runs.push_back(RunInfo::getContiguousAttrsRun(
+            attribute_map[curr_run_start], curr_run_size));
+        curr_run_size = 0;
+
+        // ... and record nulability of curr_attr as below.
+      } else {
+        // If curr_attr is nullable,
+        // ... and there is no ongoing run,
+        // ... we just have to record the nullability of curr_attr as below.
+        // (Nothing to do here.)
+      }
+
+      // We must record that curr_attr is nullable, by inserting it into `runs`
+      runs.push_back(RunInfo::getNullableAttrRun(
+          attribute_map[curr_attr], attrs_max_size[curr_attr], null_idx));
+    }
+  } // end for-loop on attributes in attribute_map
+
+  if (curr_run_size != 0) {
+    // If there is an ongoing run, then we end it. 
+    runs.push_back(RunInfo::getContiguousAttrsRun(
+        attribute_map[curr_run_start], curr_run_size));
+  }
+}
+
+// (See comments for above function for context.)
+// For other layouts (not Row Store), the input attributes may not be contiguous,
+// even if they have successive attribute IDs. So we just create a dummy `runs`
+// vector, with every attribute being a run of its size. Again, as above,
+// nullable attributes are given a sentinel run size kNullAttrRunSize.
+void getRunsForOtherLayoutHelper(
+    const CatalogRelationSchema &relation,
     const std::vector<attribute_id> &attribute_map,
-    ValueAccessor *accessor) {
+    const std::vector<std::size_t> &attrs_max_size,
+    std::vector<RunInfo> &runs) {
+  std::size_t num_attrs = attribute_map.size();
+  for (std::size_t curr_attr = 0; curr_attr < num_attrs; ++curr_attr) {
+    int null_idx = relation.getNullableAttributeIndex(curr_attr);
+    if (null_idx == kInvalidCatalogId) {
+      runs.push_back(RunInfo::getContiguousAttrsRun(
+          attribute_map[curr_attr], attrs_max_size[curr_attr]));
+    } else {
+      runs.push_back(RunInfo::getNullableAttrRun(
+          attribute_map[curr_attr], attrs_max_size[curr_attr], null_idx));
+    }
+  }
+
+}
+} // end Unnamed Namespace
+
+template <bool nullable_attrs> tuple_id
+PackedRowStoreTupleStorageSubBlock::bulkInsertTuplesWithRemappedAttributesHelper(
+    const std::vector<attribute_id> &attribute_map, ValueAccessor *accessor) {
   DEBUG_ASSERT(attribute_map.size() == relation_.size());
 
   const tuple_id original_num_tuples = header_->num_tuples;
-  char *dest_addr = static_cast<char*>(tuple_storage_)
-                      + header_->num_tuples * relation_.getFixedByteLength();
+  char *dest_addr = static_cast<char *>(tuple_storage_) +
+                    header_->num_tuples * relation_.getFixedByteLength();
   const unsigned num_nullable_attrs = relation_.numNullableAttributes();
+  const std::vector<std::size_t> &attrs_max_size =
+      relation_.getMaximumAttributeByteLengths();
+
+  std::vector<RunInfo> runs;
+  runs.reserve(attribute_map.size());
+
+  // We have an optimized implementation for RowStore to RowStore bulk insert.
+  // For other layouts, we just create dummy `runs` for uniformity.
+  auto impl = accessor->getImplementationType();
+  if (impl == ValueAccessor::Implementation::kPackedRowStore ||
+      impl == ValueAccessor::Implementation::kSplitRowStore) {
+    getRunsForRowLayoutHelper(relation_, attribute_map, attrs_max_size, runs);
+  } else {
+    getRunsForOtherLayoutHelper(relation_, attribute_map, attrs_max_size, runs);
+  }
 
   InvokeOnAnyValueAccessor(
-      accessor,
-      [this, &num_nullable_attrs, &attribute_map, &dest_addr](auto *accessor) -> void {  // NOLINT(build/c++11)
-    const std::size_t num_attrs = relation_.size();
-    const std::vector<std::size_t> &attrs_max_size =
-        relation_.getMaximumAttributeByteLengths();
-
-    if (num_nullable_attrs != 0) {
-      while (this->hasSpaceToInsert<true>(1) && accessor->next()) {
-        for (std::size_t curr_attr = 0; curr_attr < num_attrs; ++curr_attr) {
-          const std::size_t attr_size = attrs_max_size[curr_attr];
-          const attribute_id nullable_idx = relation_.getNullableAttributeIndex(curr_attr);
-          // If this attribute is nullable, check for a returned null value.
-          if (nullable_idx != kInvalidCatalogId) {
-            const void *attr_value
-                = accessor->template getUntypedValue<true>(attribute_map[curr_attr]);
-            if (attr_value == nullptr) {
-              null_bitmap_->setBit(
-                  header_->num_tuples * num_nullable_attrs + nullable_idx,
-                  true);
+    accessor,
+    [this, &num_nullable_attrs, &dest_addr, &runs, &attrs_max_size]
+    (auto *accessor) -> void {  // NOLINT(build/c++11)
+
+      // Inner lambda inserts one tuple.
+      // Defining a lambda here reduces code duplication. It can't be defined
+      // as a separate function because getUntypedValue is only a member of
+      // the downcasted ValueAccessor subtypes, not the
+      // base ValueAccessor type. So the inner lambda has to be nested inside
+      // the outer lambda. 
+      auto insertOneTupleUsingRuns =
+          [this, &accessor, &runs, &dest_addr, num_nullable_attrs]() -> void {
+        for (auto &run : runs) {
+          if (!nullable_attrs
+              || run.run_type_ == RunType::kContiguousAttributes) {
+            memcpy(dest_addr,
+                   accessor->template getUntypedValue<false>(run.mapped_attr_id_),
+                   run.size_);
+            dest_addr += run.size_;
+          } else {
+            // It's a nullable attribute. Does it have null value?
+            const void *attr_value =
+                accessor->template getUntypedValue<true>(run.mapped_attr_id_);
+            if (attr_value != nullptr) {
+              memcpy(dest_addr, attr_value, run.size_);
             } else {
-              memcpy(dest_addr, attr_value, attr_size);
+              this->null_bitmap_->setBit(
+                  this->header_->num_tuples * num_nullable_attrs
+                      + run.nullable_attr_idx_,
+                  true);
             }
-          } else {
-            memcpy(dest_addr,
-                   accessor->template getUntypedValue<false>(attribute_map[curr_attr]),
-                   attr_size);
+            // In either case, increment dest_addr (leaving blank space in
+            // case of null values)
+            dest_addr += run.size_;
           }
-          dest_addr += attr_size;
-        }
-        ++(header_->num_tuples);
-      }
-    } else {
-      while (this->hasSpaceToInsert<false>(1) && accessor->next()) {
-        for (std::size_t curr_attr = 0; curr_attr < num_attrs; ++curr_attr) {
-          const std::size_t attr_size = attrs_max_size[curr_attr];
-          memcpy(dest_addr,
-                 accessor->template getUntypedValue<false>(attribute_map[curr_attr]),
-                 attr_size);
-          dest_addr += attr_size;
         }
-        ++(header_->num_tuples);
+        ++(this->header_->num_tuples);
+      }; // end (inner) lambda: insertOneTupleUsingRuns
+
+      // Insert one tuple at a time into this subblock using the lambda fn above.
+      // We have split the loop in two and inserted in batches in order to make
+      // fewer calls to the somewhat expensive function hasSpaceToInsert.
+      //
+      // Note the order of the test conditions in the inner for-loop here. The
+      // loop terminates either when i == kNumTuplesInBatch, in which case
+      // accessor position has not been incremented past the last insert,
+      // or when accessor->next() returns false, in which case all tuples have
+      // been inserted. So there is no possibility of missing a tuple between the
+      // two loops.
+      const unsigned kNumTuplesInBatch = 1000;
+      while (this->hasSpaceToInsert<nullable_attrs>(kNumTuplesInBatch)
+             && !accessor->iterationFinished()) {
+        for (unsigned i = 0; i < kNumTuplesInBatch && accessor->next(); ++i)
+          insertOneTupleUsingRuns();
       }
-    }
-  });
+      while (this->hasSpaceToInsert<nullable_attrs>(1) && accessor->next())
+        insertOneTupleUsingRuns();
+    });  // End (outer) lambda: argument to InvokeOnAnyValueAccessor
 
   return header_->num_tuples - original_num_tuples;
 }
 
-const void* PackedRowStoreTupleStorageSubBlock::getAttributeValue(
-    const tuple_id tuple,
-    const attribute_id attr) const {
+tuple_id
+PackedRowStoreTupleStorageSubBlock::bulkInsertTuplesWithRemappedAttributes(
+    const std::vector<attribute_id> &attribute_map, ValueAccessor *accessor) {
+  const unsigned num_nullable_attrs = relation_.numNullableAttributes();
+  if (num_nullable_attrs > 0)
+    return bulkInsertTuplesWithRemappedAttributesHelper<true>(
+        attribute_map, accessor);
+  
+  return bulkInsertTuplesWithRemappedAttributesHelper<false>(
+        attribute_map, accessor);
+}
+
+const void *PackedRowStoreTupleStorageSubBlock::getAttributeValue(
+    const tuple_id tuple, const attribute_id attr) const {
   DEBUG_ASSERT(hasTupleWithID(tuple));
   DEBUG_ASSERT(relation_.hasAttributeWithId(attr));
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/6df54af4/storage/PackedRowStoreTupleStorageSubBlock.hpp
----------------------------------------------------------------------
diff --git a/storage/PackedRowStoreTupleStorageSubBlock.hpp b/storage/PackedRowStoreTupleStorageSubBlock.hpp
index 9a208dc..302e294 100644
--- a/storage/PackedRowStoreTupleStorageSubBlock.hpp
+++ b/storage/PackedRowStoreTupleStorageSubBlock.hpp
@@ -198,6 +198,13 @@ class PackedRowStoreTupleStorageSubBlock: public TupleStorageSubBlock {
   // relations that have no nullable attributes (and therefore no NULL-bitmap).
   template <bool nullable_attrs>
   bool hasSpaceToInsert(const tuple_id num_tuples) const;
+  
+  // Helper function for bulkInsertTuplesWithRemappedAttributes which is
+  // templated on the 'nullable_attrs' flag (similar in semantics to its use in
+  // the above functions). 
+  template <bool nullable_attrs>
+  tuple_id bulkInsertTuplesWithRemappedAttributesHelper(
+      const std::vector<attribute_id> &attribute_map, ValueAccessor *accessor);
 
   PackedRowStoreHeader *header_;
   std::unique_ptr<BitVector<false>> null_bitmap_;


[2/3] incubator-quickstep git commit: Add getDependencyAttrs for (almost) every Scalar*

Posted by na...@apache.org.
Add getDependencyAttrs for (almost) every Scalar*


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

Branch: refs/heads/lookahead_exact
Commit: 50682c800effbd3e17f874697afa1a558881c0ca
Parents: 08833a8
Author: Navneet Potti <na...@gmail.com>
Authored: Sat Jul 16 20:03:12 2016 -0500
Committer: Navneet Potti <na...@gmail.com>
Committed: Sat Jul 16 20:03:12 2016 -0500

----------------------------------------------------------------------
 expressions/scalar/Scalar.hpp                 | 9 +++++++++
 expressions/scalar/ScalarAttribute.hpp        | 4 ++++
 expressions/scalar/ScalarBinaryExpression.hpp | 5 +++++
 expressions/scalar/ScalarCaseExpression.hpp   | 5 +++++
 expressions/scalar/ScalarLiteral.hpp          | 2 ++
 expressions/scalar/ScalarUnaryExpression.hpp  | 4 ++++
 6 files changed, 29 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/50682c80/expressions/scalar/Scalar.hpp
----------------------------------------------------------------------
diff --git a/expressions/scalar/Scalar.hpp b/expressions/scalar/Scalar.hpp
index 6894fcd..8119c37 100644
--- a/expressions/scalar/Scalar.hpp
+++ b/expressions/scalar/Scalar.hpp
@@ -29,6 +29,7 @@
 
 namespace quickstep {
 
+class CatalogAttribute;
 class ColumnVector;
 class Type;
 class ValueAccessor;
@@ -223,6 +224,14 @@ class Scalar {
       ValueAccessor *right_accessor,
       const std::vector<std::pair<tuple_id, tuple_id>> &joined_tuple_ids) const = 0;
 
+  /**
+   * @brief Get a vector of all attributes that this Scalar depends on.
+   *
+   * @param Output parameter: vector to push the dependency attributes into.
+   */
+  virtual void getDependencyAttributes(
+      std::vector<const CatalogAttribute*> attrs) const = 0;
+
  protected:
   explicit Scalar(const Type &type) : type_(type) {
   }

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/50682c80/expressions/scalar/ScalarAttribute.hpp
----------------------------------------------------------------------
diff --git a/expressions/scalar/ScalarAttribute.hpp b/expressions/scalar/ScalarAttribute.hpp
index eda5e8e..f95c373 100644
--- a/expressions/scalar/ScalarAttribute.hpp
+++ b/expressions/scalar/ScalarAttribute.hpp
@@ -89,6 +89,10 @@ class ScalarAttribute : public Scalar {
     return attribute_;
   }
 
+  void getDependencyAttributes(std::vector<const CatalogAttribute*> attrs) const override {
+    attrs.push_back(&attribute_);
+  }
+  
  protected:
   const CatalogAttribute &attribute_;
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/50682c80/expressions/scalar/ScalarBinaryExpression.hpp
----------------------------------------------------------------------
diff --git a/expressions/scalar/ScalarBinaryExpression.hpp b/expressions/scalar/ScalarBinaryExpression.hpp
index 77de0a3..bdf7c1e 100644
--- a/expressions/scalar/ScalarBinaryExpression.hpp
+++ b/expressions/scalar/ScalarBinaryExpression.hpp
@@ -105,6 +105,11 @@ class ScalarBinaryExpression : public Scalar {
       ValueAccessor *right_accessor,
       const std::vector<std::pair<tuple_id, tuple_id>> &joined_tuple_ids) const override;
 
+  void getDependencyAttributes(std::vector<const CatalogAttribute*> attrs) const override {
+    left_operand_->getDependencyAttributes(attrs);
+    right_operand_->getDependencyAttributes(attrs);
+  }
+
  private:
   void initHelper(bool own_children);
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/50682c80/expressions/scalar/ScalarCaseExpression.hpp
----------------------------------------------------------------------
diff --git a/expressions/scalar/ScalarCaseExpression.hpp b/expressions/scalar/ScalarCaseExpression.hpp
index fc1862b..b990f75 100644
--- a/expressions/scalar/ScalarCaseExpression.hpp
+++ b/expressions/scalar/ScalarCaseExpression.hpp
@@ -140,6 +140,11 @@ class ScalarCaseExpression : public Scalar {
       ValueAccessor *right_accessor,
       const std::vector<std::pair<tuple_id, tuple_id>> &joined_tuple_ids) const override;
 
+  void getDependencyAttributes(std::vector<const CatalogAttribute*> attrs) const override {
+    // TODO(nav): Implement this!
+    FATAL_ERROR("getDependencyAttributes() not defined for ScalarCaseExpression yet");
+  }
+
  private:
   // Merge the values in the NativeColumnVector 'case_result' into '*output' at
   // the positions specified by 'case_matches'. If '*source_sequence' is

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/50682c80/expressions/scalar/ScalarLiteral.hpp
----------------------------------------------------------------------
diff --git a/expressions/scalar/ScalarLiteral.hpp b/expressions/scalar/ScalarLiteral.hpp
index 3ab6671..0016a9d 100644
--- a/expressions/scalar/ScalarLiteral.hpp
+++ b/expressions/scalar/ScalarLiteral.hpp
@@ -109,6 +109,8 @@ class ScalarLiteral : public Scalar {
       ValueAccessor *right_accessor,
       const std::vector<std::pair<tuple_id, tuple_id>> &joined_tuple_ids) const override;
 
+  void getDependencyAttributes(std::vector<const CatalogAttribute*> attrs) const override {}
+
  private:
   TypedValue internal_literal_;
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/50682c80/expressions/scalar/ScalarUnaryExpression.hpp
----------------------------------------------------------------------
diff --git a/expressions/scalar/ScalarUnaryExpression.hpp b/expressions/scalar/ScalarUnaryExpression.hpp
index 90b360e..3881cd5 100644
--- a/expressions/scalar/ScalarUnaryExpression.hpp
+++ b/expressions/scalar/ScalarUnaryExpression.hpp
@@ -101,6 +101,10 @@ class ScalarUnaryExpression : public Scalar {
       ValueAccessor *right_accessor,
       const std::vector<std::pair<tuple_id, tuple_id>> &joined_tuple_ids) const override;
 
+  void getDependencyAttributes(std::vector<const CatalogAttribute*> attrs) const override {
+    operand_->getDependencyAttributes(attrs);
+  }
+
  private:
   void initHelper(bool own_children);