quickstep-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jianq...@apache.org
Subject [1/6] incubator-quickstep git commit: Add "COPY TO" operator for exporting data from Quickstep. [Forced Update!]
Date Mon, 28 Aug 2017 22:01:53 GMT
Repository: incubator-quickstep
Updated Branches:
  refs/heads/copy-to 99ad1b2e5 -> b665fdac7 (forced update)


http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b665fdac/relational_operators/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/relational_operators/CMakeLists.txt b/relational_operators/CMakeLists.txt
index 57ba9f9..0a60dca 100644
--- a/relational_operators/CMakeLists.txt
+++ b/relational_operators/CMakeLists.txt
@@ -71,6 +71,7 @@ add_library(quickstep_relationaloperators_SortMergeRunOperatorHelpers SortMergeR
             SortMergeRunOperatorHelpers.hpp)
 add_library(quickstep_relationaloperators_SortRunGenerationOperator SortRunGenerationOperator.cpp
             SortRunGenerationOperator.hpp)
+add_library(quickstep_relationaloperators_TableExportOperator TableExportOperator.cpp TableExportOperator.hpp)
 add_library(quickstep_relationaloperators_TableGeneratorOperator TableGeneratorOperator.cpp TableGeneratorOperator.hpp)
 add_library(quickstep_relationaloperators_TextScanOperator TextScanOperator.cpp TextScanOperator.hpp)
 add_library(quickstep_relationaloperators_UnionAllOperator UnionAllOperator.cpp UnionAllOperator.hpp)
@@ -473,6 +474,25 @@ target_link_libraries(quickstep_relationaloperators_SortRunGenerationOperator
                       quickstep_utility_Macros
                       quickstep_utility_SortConfiguration
                       tmb)
+target_link_libraries(quickstep_relationaloperators_TableExportOperator
+                      glog
+                      quickstep_catalog_CatalogAttribute
+                      quickstep_catalog_CatalogRelation
+                      quickstep_catalog_CatalogTypedefs
+                      quickstep_queryexecution_QueryContext
+                      quickstep_queryexecution_WorkOrderProtosContainer
+                      quickstep_queryexecution_WorkOrdersContainer
+                      quickstep_relationaloperators_RelationalOperator
+                      quickstep_relationaloperators_WorkOrder
+                      quickstep_relationaloperators_WorkOrder_proto
+                      quickstep_storage_StorageBlockInfo
+                      quickstep_storage_ValueAccessor
+                      quickstep_threading_SpinMutex
+                      quickstep_types_TypedValue
+                      quickstep_types_containers_Tuple
+                      quickstep_utility_BulkIOConfiguration
+                      quickstep_utility_Macros
+                      quickstep_utility_StringUtil)
 target_link_libraries(quickstep_relationaloperators_TableGeneratorOperator
                       glog
                       quickstep_catalog_CatalogRelation
@@ -508,6 +528,7 @@ target_link_libraries(quickstep_relationaloperators_TextScanOperator
                       quickstep_types_containers_ColumnVector
                       quickstep_types_containers_ColumnVectorsValueAccessor
                       quickstep_types_containers_Tuple
+                      quickstep_utility_BulkIOConfiguration
                       quickstep_utility_Glob
                       quickstep_utility_Macros
                       tmb)
@@ -637,6 +658,7 @@ target_link_libraries(quickstep_relationaloperators
                       quickstep_relationaloperators_SortMergeRunOperatorHelpers
                       quickstep_relationaloperators_SortMergeRunOperator_proto
                       quickstep_relationaloperators_SortRunGenerationOperator
+                      quickstep_relationaloperators_TableExportOperator
                       quickstep_relationaloperators_TableGeneratorOperator
                       quickstep_relationaloperators_TextScanOperator
                       quickstep_relationaloperators_UnionAllOperator

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b665fdac/relational_operators/RelationalOperator.hpp
----------------------------------------------------------------------
diff --git a/relational_operators/RelationalOperator.hpp b/relational_operators/RelationalOperator.hpp
index 8035685..8eb59f0 100644
--- a/relational_operators/RelationalOperator.hpp
+++ b/relational_operators/RelationalOperator.hpp
@@ -86,6 +86,7 @@ class RelationalOperator {
     kSelect,
     kSortMergeRun,
     kSortRunGeneration,
+    kTableExport,
     kTableGenerator,
     kTextScan,
     kUnionAll,

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b665fdac/relational_operators/TableExportOperator.cpp
----------------------------------------------------------------------
diff --git a/relational_operators/TableExportOperator.cpp b/relational_operators/TableExportOperator.cpp
new file mode 100644
index 0000000..067033f
--- /dev/null
+++ b/relational_operators/TableExportOperator.cpp
@@ -0,0 +1,329 @@
+/**
+ * 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 "relational_operators/TableExportOperator.hpp"
+
+#include <cstdio>
+#include <exception>
+#include <string>
+#include <utility>
+
+#include "catalog/CatalogAttribute.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/ValueAccessor.hpp"
+#include "threading/SpinMutex.hpp"
+#include "types/TypedValue.hpp"
+#include "types/containers/Tuple.hpp"
+#include "utility/BulkIOConfiguration.hpp"
+#include "utility/StringUtil.hpp"
+
+#include "glog/logging.h"
+
+#include "tmb/id_typedefs.h"
+
+namespace quickstep {
+
+bool TableExportOperator::getAllWorkOrders(
+    WorkOrdersContainer *container,
+    QueryContext *query_context,
+    StorageManager *storage_manager,
+    const tmb::client_id scheduler_client_id,
+    tmb::MessageBus *bus) {
+  const auto add_work_order = [&](const block_id input_block_id,
+                                  const bool is_first_work_order) -> void {
+    std::unique_ptr<std::string> output_buffer = std::make_unique<std::string>();
+    container->addNormalWorkOrder(
+        new TableExportToStringWorkOrder(query_id_,
+                                         input_relation_,
+                                         input_block_id,
+                                         options_->getFormat(),
+                                         is_first_work_order && options_->hasHeader(),
+                                         options_->getDelimiter(),
+                                         options_->escapeStrings(),
+                                         options_->getQuoteCharacter(),
+                                         options_->getNullString(),
+                                         output_buffer.get(),
+                                         op_index_,
+                                         scheduler_client_id,
+                                         storage_manager,
+                                         bus),
+        op_index_);
+
+    SpinMutexLock lock(output_buffers_mutex_);
+    output_buffers_.emplace(input_block_id, output_buffer.release());
+  };
+
+  if (input_relation_is_stored_) {
+    if (!started_) {
+      for (std::size_t i = 0; i < input_relation_block_ids_.size(); ++i) {
+        add_work_order(input_relation_block_ids_[i], i == 0);
+      }
+      num_workorders_generated_ = input_relation_block_ids_.size();
+      started_ = true;
+    }
+    return true;
+  } else {
+    while (num_workorders_generated_ < input_relation_block_ids_.size()) {
+      add_work_order(input_relation_block_ids_[num_workorders_generated_],
+                     num_workorders_generated_ == 0);
+      ++num_workorders_generated_;
+    }
+    return done_feeding_input_relation_;
+  }
+}
+
+bool TableExportOperator::getAllWorkOrderProtos(
+    WorkOrderProtosContainer *container) {
+  // TODO(quickstep-team): Implement TextExportOperator for the distributed case.
+  LOG(FATAL) << "TableExportOperator::getAllWorkOrderProtos() is not supported";
+}
+
+void TableExportOperator::receiveFeedbackMessage(
+    const WorkOrder::FeedbackMessage &msg) {
+  DCHECK(TableExportOperator::kBlockOutputMessage == msg.type());
+  DCHECK(msg.payload_size() == sizeof(block_id));
+
+  if (file_ == nullptr) {
+    const std::string lo_file_name = ToLower(file_name_);
+    if (lo_file_name == "$stdout") {
+      file_ = stdout;
+    } else if (lo_file_name == "$stderr") {
+      file_ = stderr;
+    } else {
+      file_ = std::fopen(file_name_.substr(1).c_str(), "wb");
+      // TODO(quickstep-team): Decent handling of exceptions at query runtime.
+      if (file_ == nullptr) {
+        throw std::runtime_error("Can not open file " + file_name_ + " for writing");
+      }
+    }
+  }
+
+  // Mark block done.
+  const block_id done_block_id = *static_cast<const block_id*>(msg.payload());
+  {
+    SpinMutexLock lock(output_buffers_mutex_);
+    DCHECK(output_buffers_.find(done_block_id) != output_buffers_.end());
+    output_buffers_.at(done_block_id).done = true;
+  }
+
+  // FIXME(jianqiao): Use work orders to perform the "write to file" operation
+  // instead of doing it here inside this thread -- as it may stall the scheduler.
+  while (num_blocks_written_ < num_workorders_generated_) {
+    // Write block exported strings to file in the same order as the blocks are
+    // in \p input_relation_block_ids_.
+    block_id next_block_id;
+    {
+      SpinMutexLock lock(block_ids_mutex_);
+      next_block_id = input_relation_block_ids_[num_blocks_written_];
+    }
+    std::unique_ptr<std::string> output_buffer;
+    {
+      SpinMutexLock lock(output_buffers_mutex_);
+      auto it = output_buffers_.find(next_block_id);
+      if (it == output_buffers_.end() || !it->second.done) {
+        break;
+      }
+      output_buffer = std::move(it->second.buffer);
+      output_buffers_.erase(it);
+    }
+    std::fwrite(output_buffer->c_str(), 1, output_buffer->length(), file_);
+    ++num_blocks_written_;
+  }
+}
+
+void TableExportOperator::updateCatalogOnCompletion() {
+  if (file_ != nullptr && file_ != stdout && file_ != stderr) {
+    std::fclose(file_);
+  }
+  file_ = nullptr;
+}
+
+void TableExportToStringWorkOrder::execute() {
+  BlockReference block(
+      storage_manager_->getBlock(input_block_id_, input_relation_));
+  std::unique_ptr<ValueAccessor> accessor(
+      block->getTupleStorageSubBlock().createValueAccessor());
+
+  switch (format_) {
+    case BulkIOFormat::kCSV:
+      writeToString<&TableExportToStringWorkOrder::quoteCSVField>(
+          accessor.get(), output_buffer_);
+      break;
+    case BulkIOFormat::kText:
+      writeToString<&TableExportToStringWorkOrder::escapeTextField>(
+          accessor.get(), output_buffer_);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported export format in TableExportWorkOrder::execute()";
+  }
+
+  // Send completion message to operator.
+  FeedbackMessage msg(TableExportOperator::kBlockOutputMessage,
+                      getQueryID(),
+                      operator_index_,
+                      new block_id(input_block_id_),
+                      sizeof(input_block_id_));
+  SendFeedbackMessage(
+      bus_, ClientIDMap::Instance()->getValue(), scheduler_client_id_, msg);
+}
+
+inline std::string TableExportToStringWorkOrder::quoteCSVField(std::string &&field) const {
+  bool need_quote = false;
+  for (const char c : field) {
+    if (c == field_terminator_ || c == quote_character_ || c == '\n') {
+      need_quote = true;
+      break;
+    }
+  }
+  if (!need_quote) {
+    return std::move(field);
+  }
+
+  std::string quoted;
+  quoted.push_back(quote_character_);
+  for (const char c : field) {
+    if (c == quote_character_) {
+      quoted.push_back(c);
+    }
+    quoted.push_back(c);
+  }
+  quoted.push_back(quote_character_);
+  return quoted;
+}
+
+
+inline std::string TableExportToStringWorkOrder::escapeTextField(std::string &&field) const {
+  if (escape_strings_ == false || field == "\\N") {
+    return std::move(field);
+  }
+  bool need_escape = false;
+  for (const unsigned char c : field) {
+    if (c < ' ' || c == '\\' || c == field_terminator_) {
+      need_escape = true;
+      break;
+    }
+  }
+  if (!need_escape) {
+    return std::move(field);
+  }
+
+  std::string escaped;
+  for (const unsigned char c : field) {
+    if (c < 32) {
+      switch (c) {
+        case '\b':
+          // Backspace.
+          escaped.append("\\b");
+          break;
+        case '\f':
+          // Form-feed.
+          escaped.append("\\f");
+          break;
+        case '\n':
+          // Newline.
+          escaped.append("\\n");
+          break;
+        case '\r':
+          // Carriage return.
+          escaped.append("\\r");
+          break;
+        case '\t':
+          // Tab.
+          escaped.append("\\t");
+          break;
+        case '\v':
+          // Vertical tab
+          escaped.append("\\v");
+          break;
+        default: {
+          // Use hexidecimal representation.
+          static const std::string digits = "0123456789ABCDEF";
+          escaped.append("\\x");
+          escaped.push_back(digits.at(c >> 4));
+          escaped.push_back(digits.at(c & 0xF));
+          break;
+        }
+      }
+    } else {
+      if (c == '\\' || c == field_terminator_) {
+        escaped.push_back('\\');
+      }
+      escaped.push_back(c);
+    }
+  }
+  return escaped;
+}
+
+template <std::string (TableExportToStringWorkOrder::*transform)(std::string&&) const,
+          typename Container, typename Functor>
+inline void TableExportToStringWorkOrder::writeEachToString(const Container &container,
+                                                            std::string *output,
+                                                            const Functor &functor) const {
+  auto it = container.begin();
+  if (it != container.end()) {
+    std::size_t idx = 0;
+    output->append((this->*transform)(functor(*it, idx++)));
+    while ((++it) != container.end()) {
+      output->push_back(field_terminator_);
+      output->append((this->*transform)(functor(*it, idx++)));
+    }
+  }
+}
+
+template <std::string (TableExportToStringWorkOrder::*transform)(std::string&&) const>
+void TableExportToStringWorkOrder::writeToString(ValueAccessor *accessor,
+                                                 std::string *output) const {
+  std::vector<const Type*> value_types;
+  value_types.reserve(input_relation_.size());
+  for (const CatalogAttribute &attribute : input_relation_) {
+    value_types.emplace_back(&attribute.getType());
+  }
+
+  // Write table header to the output buffer.
+  if (print_header_) {
+    writeEachToString<transform>(
+        input_relation_, output,
+        [&](const CatalogAttribute &attr, const std::size_t idx) -> std::string {
+      return attr.getDisplayName();
+    });
+    output->push_back('\n');
+  }
+
+  // Write table rows to the output buffer.
+  accessor->beginIterationVirtual();
+  while (accessor->nextVirtual()) {
+    std::unique_ptr<Tuple> tuple(accessor->getTupleVirtual());
+    writeEachToString<transform>(
+        *tuple, output,
+        [&](const TypedValue &value, const std::size_t idx) -> std::string {
+      if (value.isNull()) {
+        return null_string_;
+      } else {
+        return value_types[idx]->printValueToString(value);
+      }
+    });
+    output->push_back('\n');
+  }
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b665fdac/relational_operators/TableExportOperator.hpp
----------------------------------------------------------------------
diff --git a/relational_operators/TableExportOperator.hpp b/relational_operators/TableExportOperator.hpp
new file mode 100644
index 0000000..9581cb1
--- /dev/null
+++ b/relational_operators/TableExportOperator.hpp
@@ -0,0 +1,268 @@
+/**
+ * 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_RELATIONAL_OPERATORS_TABLE_EXPORT_OPERATOR_HPP_
+#define QUICKSTEP_RELATIONAL_OPERATORS_TABLE_EXPORT_OPERATOR_HPP_
+
+#include <cstddef>
+#include <cstdio>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "catalog/CatalogRelation.hpp"
+#include "catalog/CatalogTypedefs.hpp"
+#include "query_execution/QueryContext.hpp"
+#include "relational_operators/RelationalOperator.hpp"
+#include "relational_operators/WorkOrder.hpp"
+#include "storage/StorageBlockInfo.hpp"
+#include "threading/SpinMutex.hpp"
+#include "utility/BulkIOConfiguration.hpp"
+#include "utility/Macros.hpp"
+
+#include "glog/logging.h"
+
+#include "tmb/id_typedefs.h"
+
+namespace tmb { class MessageBus; }
+
+namespace quickstep {
+
+class CatalogRelationSchema;
+class StorageManager;
+class ValueAccessor;
+class WorkOrderProtosContainer;
+class WorkOrdersContainer;
+
+namespace serialization { class WorkOrder; }
+
+/** \addtogroup RelationalOperators
+ *  @{
+ */
+
+class TableExportOperator : public RelationalOperator {
+ public:
+  /**
+   * @brief Feedback message to Foreman when a TableExportToStringWorkOrder has
+   *        completed writing a block to the string buffer.
+   */
+  enum FeedbackMessageType : WorkOrder::FeedbackMessageType {
+      kBlockOutputMessage,
+  };
+
+  /**
+   * @brief Constructor.
+   *
+   * @param query_id The ID of the query to which this operator belongs.
+   * @param input_relation The relation to export.
+   * @param input_relation_is_stored If input_relation is a stored relation and
+   *        is fully available to the operator before it can start generating
+   *        workorders.
+   * @param file_name The name of the file to export the relation to.
+   * @param options The options that specify the detailed format of the output
+   *        file.
+   */
+  TableExportOperator(const std::size_t query_id,
+                      const CatalogRelation &input_relation,
+                      const bool input_relation_is_stored,
+                      const std::string &file_name,
+                      const BulkIOConfigurationPtr &options)
+      : RelationalOperator(query_id),
+        input_relation_(input_relation),
+        input_relation_is_stored_(input_relation_is_stored),
+        file_name_(file_name),
+        options_(options),
+        input_relation_block_ids_(input_relation_is_stored
+                                      ? input_relation.getBlocksSnapshot()
+                                      : std::vector<block_id>()),
+        num_workorders_generated_(0),
+        started_(false),
+        num_blocks_written_(0),
+        file_(nullptr) {}
+
+  ~TableExportOperator() override {}
+
+  OperatorType getOperatorType() const override {
+    return kTableExport;
+  }
+
+  std::string getName() const override {
+    return "TableExportOperator";
+  }
+
+  /**
+   * @return The relation to export.
+   */
+  const CatalogRelation& input_relation() const {
+    return input_relation_;
+  }
+
+  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;
+
+  void feedInputBlock(const block_id input_block_id,
+                      const relation_id input_relation_id,
+                      const partition_id part_id) override {
+    if (input_relation_id == input_relation_.getID()) {
+      SpinMutexLock lock(block_ids_mutex_);
+      input_relation_block_ids_.emplace_back(input_block_id);
+    }
+  }
+
+  void receiveFeedbackMessage(const WorkOrder::FeedbackMessage &msg) override;
+
+  void updateCatalogOnCompletion() override;
+
+ private:
+  // Buffer for storing a block's exported string.
+  struct BlockBuffer {
+    BlockBuffer(std::string *buffer_in)
+        : done(false),
+          buffer(buffer_in) {}
+    bool done;
+    std::unique_ptr<std::string> buffer;
+  };
+
+  const CatalogRelation &input_relation_;
+  const bool input_relation_is_stored_;
+  const std::string file_name_;
+  const BulkIOConfigurationPtr options_;
+
+  std::vector<block_id> input_relation_block_ids_;
+  std::size_t num_workorders_generated_;
+  SpinMutex block_ids_mutex_;
+
+  bool started_;
+
+  std::size_t num_blocks_written_;
+  std::unordered_map<block_id, BlockBuffer> output_buffers_;
+  SpinMutex output_buffers_mutex_;
+
+  FILE *file_;
+
+  DISALLOW_COPY_AND_ASSIGN(TableExportOperator);
+};
+
+class TableExportToStringWorkOrder : public WorkOrder {
+ public:
+  /**
+   * @brief Constructor.
+   *
+   * @param query_id The ID of the query to which this work order belongs.
+   * @param input_relation The relation to export.
+   * @param input_block_id The block id.
+   * @param format The output file format.
+   * @param print_header Whether to write table headers.
+   * @param field_terminator The character that separates attribute values in
+   *        each output row.
+   * @param escape_strings Whether to encode special characters as escape
+   *        sequences. NOTE: This options is for TEXT format only and is ignored
+   *        otherwiae.
+   * @param quote_character The quote character. NOTE: This options is for CSV
+   *        format only and is ignored otherwiae.
+   * @param null_string The string that represents a null value.
+   * @param output_buffer The string buffer for writing the output to.
+   * @param operator_index TableExportOperator index to send feedback messages
+   *        to.
+   * @param scheduler_client_id The TMB client ID of the scheduler thread.
+   * @param storage_manager The StorageManager to use.
+   * @param bus TMB to send the feedback message on.
+   */
+  TableExportToStringWorkOrder(const std::size_t query_id,
+                               const CatalogRelationSchema &input_relation,
+                               const block_id input_block_id,
+                               const BulkIOFormat format,
+                               const bool print_header,
+                               const char field_terminator,
+                               const bool escape_strings,
+                               const char quote_character,
+                               const std::string null_string,
+                               std::string *output_buffer,
+                               const std::size_t operator_index,
+                               const tmb::client_id scheduler_client_id,
+                               StorageManager *storage_manager,
+                               MessageBus *bus)
+      : WorkOrder(query_id),
+        input_relation_(input_relation),
+        input_block_id_(input_block_id),
+        format_(format),
+        print_header_(print_header),
+        field_terminator_(field_terminator),
+        escape_strings_(escape_strings),
+        quote_character_(quote_character),
+        null_string_(null_string),
+        output_buffer_(output_buffer),
+        operator_index_(operator_index),
+        scheduler_client_id_(scheduler_client_id),
+        storage_manager_(storage_manager),
+        bus_(bus) {
+  }
+
+  ~TableExportToStringWorkOrder() override {}
+
+  void execute() override;
+
+ private:
+  inline std::string quoteCSVField(std::string &&field) const;
+  inline std::string escapeTextField(std::string &&field) const;
+
+  // Helper method for writing each entry from a table row to the output string
+  // buffer, with proper transformations (separated by delimiter character,
+  // escape special characters, add quotes, etc.).
+  template <std::string (TableExportToStringWorkOrder::*transform)(std::string&&) const,
+            typename Container, typename Functor>
+  inline void writeEachToString(const Container &container,
+                                std::string *output,
+                                const Functor &functor) const;
+
+  // Write all the rows from a value accessor to the output string buffer.
+  template <std::string (TableExportToStringWorkOrder::*transform)(std::string&&) const>
+  void writeToString(ValueAccessor *accessor, std::string *output) const;
+
+  const CatalogRelationSchema &input_relation_;
+  const block_id input_block_id_;
+
+  const BulkIOFormat format_;
+  const bool print_header_;
+  const char field_terminator_;
+  const bool escape_strings_;
+  const char quote_character_;
+  const std::string null_string_;
+
+  std::string *output_buffer_;
+
+  const std::size_t operator_index_;
+  const tmb::client_id scheduler_client_id_;
+  StorageManager *storage_manager_;
+  MessageBus *bus_;
+
+  DISALLOW_COPY_AND_ASSIGN(TableExportToStringWorkOrder);
+};
+
+/** @} */
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_RELATIONAL_OPERATORS_TABLE_EXPORT_OPERATOR_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b665fdac/relational_operators/TextScanOperator.cpp
----------------------------------------------------------------------
diff --git a/relational_operators/TextScanOperator.cpp b/relational_operators/TextScanOperator.cpp
index 3ca3af4..3cb78fb 100644
--- a/relational_operators/TextScanOperator.cpp
+++ b/relational_operators/TextScanOperator.cpp
@@ -31,6 +31,7 @@
 #include <cstdint>
 #include <cstdio>
 #include <cstdlib>
+#include <exception>
 #include <memory>
 #include <string>
 #include <utility>
@@ -54,6 +55,7 @@
 #include "types/containers/ColumnVector.hpp"
 #include "types/containers/ColumnVectorsValueAccessor.hpp"
 #include "types/containers/Tuple.hpp"
+#include "utility/BulkIOConfiguration.hpp"
 #include "utility/Glob.hpp"
 
 #include "gflags/gflags.h"
@@ -61,9 +63,6 @@
 
 #include "tmb/id_typedefs.h"
 
-using std::size_t;
-using std::string;
-
 namespace quickstep {
 
 // Text segment size set to 256KB.
@@ -82,14 +81,19 @@ static bool ValidateTextScanTextSegmentSize(const char *flagname,
   return true;
 }
 
-static const volatile bool text_scan_text_segment_size_dummy = gflags::RegisterFlagValidator(
-    &FLAGS_textscan_text_segment_size, &ValidateTextScanTextSegmentSize);
+static const volatile bool text_scan_text_segment_size_dummy =
+    gflags::RegisterFlagValidator(
+        &FLAGS_textscan_text_segment_size, &ValidateTextScanTextSegmentSize);
 
 namespace {
 
-size_t getFileSize(const string &file_name) {
+static std::size_t GetFileSize(const std::string &file_name) {
   // Use standard C libary to retrieve the file size.
   FILE *fp = std::fopen(file_name.c_str(), "rb");
+  // TODO(quickstep-team): Decent handling of exceptions at query runtime.
+  if (fp == nullptr) {
+    throw std::runtime_error("Can not open file " + file_name + " for reading");
+  }
   std::fseek(fp, 0, SEEK_END);
   const std::size_t file_size = std::ftell(fp);
   std::fclose(fp);
@@ -127,7 +131,7 @@ bool TextScanOperator::getAllWorkOrders(
         << "File " << file << " is not readable due to permission issues.";
 #endif  // QUICKSTEP_HAVE_UNISTD
 
-    const std::size_t file_size = getFileSize(file);
+    const std::size_t file_size = GetFileSize(file);
 
     std::size_t text_offset = 0;
     for (size_t num_full_segments = file_size / FLAGS_textscan_text_segment_size;
@@ -138,8 +142,8 @@ bool TextScanOperator::getAllWorkOrders(
                                 file,
                                 text_offset,
                                 FLAGS_textscan_text_segment_size,
-                                field_terminator_,
-                                process_escape_sequences_,
+                                options_->getDelimiter(),
+                                options_->escapeStrings(),
                                 output_destination),
           op_index_);
     }
@@ -152,8 +156,8 @@ bool TextScanOperator::getAllWorkOrders(
                                 file,
                                 text_offset,
                                 file_size - text_offset,
-                                field_terminator_,
-                                process_escape_sequences_,
+                                options_->getDelimiter(),
+                                options_->escapeStrings(),
                                 output_destination),
           op_index_);
     }
@@ -169,22 +173,25 @@ bool TextScanOperator::getAllWorkOrderProtos(WorkOrderProtosContainer *container
     return true;
   }
 
-  for (const string &file : files) {
-    const std::size_t file_size = getFileSize(file);
+  for (const std::string &file : files) {
+    const std::size_t file_size = GetFileSize(file);
 
     size_t text_offset = 0;
     for (size_t num_full_segments = file_size / FLAGS_textscan_text_segment_size;
          num_full_segments > 0;
          --num_full_segments, text_offset += FLAGS_textscan_text_segment_size) {
-      container->addWorkOrderProto(createWorkOrderProto(file, text_offset, FLAGS_textscan_text_segment_size),
-                                   op_index_);
+      container->addWorkOrderProto(
+          createWorkOrderProto(file, text_offset,
+                               FLAGS_textscan_text_segment_size),
+          op_index_);
     }
 
     // Deal with the residual partial segment whose size is less than
     // 'FLAGS_textscan_text_segment_size'.
     if (text_offset < file_size) {
-      container->addWorkOrderProto(createWorkOrderProto(file, text_offset, file_size - text_offset),
-                                   op_index_);
+      container->addWorkOrderProto(
+          createWorkOrderProto(file, text_offset, file_size - text_offset),
+          op_index_);
     }
   }
 
@@ -192,9 +199,10 @@ bool TextScanOperator::getAllWorkOrderProtos(WorkOrderProtosContainer *container
   return true;
 }
 
-serialization::WorkOrder* TextScanOperator::createWorkOrderProto(const string &filename,
-                                                                 const size_t text_offset,
-                                                                 const size_t text_segment_size) {
+serialization::WorkOrder* TextScanOperator::createWorkOrderProto(
+    const std::string &filename,
+    const std::size_t text_offset,
+    const std::size_t text_segment_size) {
   serialization::WorkOrder *proto = new serialization::WorkOrder;
   proto->set_work_order_type(serialization::TEXT_SCAN);
   proto->set_query_id(query_id_);
@@ -202,9 +210,10 @@ serialization::WorkOrder* TextScanOperator::createWorkOrderProto(const string &f
   proto->SetExtension(serialization::TextScanWorkOrder::filename, filename);
   proto->SetExtension(serialization::TextScanWorkOrder::text_offset, text_offset);
   proto->SetExtension(serialization::TextScanWorkOrder::text_segment_size, text_segment_size);
-  proto->SetExtension(serialization::TextScanWorkOrder::field_terminator, field_terminator_);
+  proto->SetExtension(serialization::TextScanWorkOrder::field_terminator,
+                      options_->getDelimiter());
   proto->SetExtension(serialization::TextScanWorkOrder::process_escape_sequences,
-                      process_escape_sequences_);
+                      options_->escapeStrings());
   proto->SetExtension(serialization::TextScanWorkOrder::insert_destination_index,
                       output_destination_index_);
 
@@ -235,12 +244,14 @@ void TextScanWorkOrder::execute() {
     file_handle = hdfsOpenFile(hdfs, filename_.c_str(), O_RDONLY, buffer_size,
                                0 /* default replication */, 0 /* default block size */);
     if (file_handle == nullptr) {
-      LOG(ERROR) << "Failed to open file " << filename_ << " with error: " << strerror(errno);
+      LOG(ERROR) << "Failed to open file " << filename_
+                 << " with error: " << strerror(errno);
       return;
     }
 
     if (hdfsSeek(hdfs, file_handle, text_offset_)) {
-      LOG(ERROR) << "Failed to seek in file " << filename_ << " with error: " << strerror(errno);
+      LOG(ERROR) << "Failed to seek in file " << filename_
+                 << " with error: " << strerror(errno);
 
       hdfsCloseFile(hdfs, file_handle);
       return;
@@ -248,7 +259,9 @@ void TextScanWorkOrder::execute() {
 
     bytes_read = hdfsRead(hdfs, file_handle, buffer, text_segment_size_);
     while (bytes_read != text_segment_size_) {
-      bytes_read += hdfsRead(hdfs, file_handle, buffer + bytes_read, text_segment_size_ - bytes_read);
+      bytes_read += hdfsRead(hdfs, file_handle,
+                             buffer + bytes_read,
+                             text_segment_size_ - bytes_read);
     }
   }
 #endif  // QUICKSTEP_HAVE_FILE_MANAGER_HDFS
@@ -325,7 +338,8 @@ void TextScanWorkOrder::execute() {
   if (use_hdfs) {
 #ifdef QUICKSTEP_HAVE_FILE_MANAGER_HDFS
     if (hdfsSeek(hdfs, file_handle, dynamic_read_offset)) {
-      LOG(ERROR) << "Failed to seek in file " << filename_ << " with error: " << strerror(errno);
+      LOG(ERROR) << "Failed to seek in file " << filename_
+                 << " with error: " << strerror(errno);
 
       hdfsCloseFile(hdfs, file_handle);
       return;
@@ -343,7 +357,9 @@ void TextScanWorkOrder::execute() {
 
       // Read again when acrossing the HDFS block boundary.
       if (bytes_read != dynamic_read_size) {
-        bytes_read += hdfsRead(hdfs, file_handle, buffer + bytes_read, dynamic_read_size - bytes_read);
+        bytes_read += hdfsRead(hdfs, file_handle,
+                               buffer + bytes_read,
+                               dynamic_read_size - bytes_read);
       }
 #endif  // QUICKSTEP_HAVE_FILE_MANAGER_HDFS
     } else {

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b665fdac/relational_operators/TextScanOperator.hpp
----------------------------------------------------------------------
diff --git a/relational_operators/TextScanOperator.hpp b/relational_operators/TextScanOperator.hpp
index 4dbeb92..cc51818 100644
--- a/relational_operators/TextScanOperator.hpp
+++ b/relational_operators/TextScanOperator.hpp
@@ -32,6 +32,7 @@
 #include "relational_operators/RelationalOperator.hpp"
 #include "relational_operators/WorkOrder.hpp"
 #include "types/containers/Tuple.hpp"
+#include "utility/BulkIOConfiguration.hpp"
 #include "utility/Macros.hpp"
 
 #include "glog/logging.h"
@@ -104,32 +105,28 @@ class TextScanFormatError : public std::exception {
 class TextScanOperator : public RelationalOperator {
  public:
   /**
-   * @brief Constructor
+   * @brief Constructor.
    *
    * @param query_id The ID of the query to which this operator belongs.
    * @param file_pattern The glob-like file pattern of the sources to load. The
    *        pattern could include * (wildcard for multiple chars) and ?
    *        (wildcard for single char). It defaults to single file load, if a
    *        file is specified.
-   * @param field_terminator The string which separates attribute values in
-   *        the text file.
-   * @param process_escape_sequences Whether to decode escape sequences in the
-   *        text file.
+   * @param options The options that specify the detailed format of the input
+            file(s).
    * @param output_relation The output relation.
    * @param output_destination_index The index of the InsertDestination in the
    *        QueryContext to insert tuples.
    **/
   TextScanOperator(const std::size_t query_id,
                    const std::string &file_pattern,
-                   const char field_terminator,
-                   const bool process_escape_sequences,
+                   const BulkIOConfigurationPtr &options,
                    const CatalogRelation &output_relation,
                    const QueryContext::insert_destination_id output_destination_index)
       : RelationalOperator(query_id, 1u, output_relation.getNumPartitions() != 1u /* has_repartition */,
                            output_relation.getNumPartitions()),
         file_pattern_(file_pattern),
-        field_terminator_(field_terminator),
-        process_escape_sequences_(process_escape_sequences),
+        options_(options),
         output_relation_(output_relation),
         output_destination_index_(output_destination_index),
         work_generated_(false) {}
@@ -166,8 +163,7 @@ class TextScanOperator : public RelationalOperator {
                                                  const std::size_t text_segment_size);
 
   const std::string file_pattern_;
-  const char field_terminator_;
-  const bool process_escape_sequences_;
+  const BulkIOConfigurationPtr options_;
 
   const CatalogRelation &output_relation_;
   const QueryContext::insert_destination_id output_destination_index_;

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b665fdac/relational_operators/WorkOrder.proto
----------------------------------------------------------------------
diff --git a/relational_operators/WorkOrder.proto b/relational_operators/WorkOrder.proto
index 6dafbe0..aaf7929 100644
--- a/relational_operators/WorkOrder.proto
+++ b/relational_operators/WorkOrder.proto
@@ -21,7 +21,7 @@ package quickstep.serialization;
 
 import "relational_operators/SortMergeRunOperator.proto";
 
-// Next tag: 26.
+// Next tag: 27.
 enum WorkOrderType {
   AGGREGATION = 1;
   BUILD_AGGREGATION_EXISTENCE_MAP = 23;
@@ -43,6 +43,7 @@ enum WorkOrderType {
   SELECT = 15;
   SORT_MERGE_RUN = 16;
   SORT_RUN_GENERATION = 17;
+  TABLE_EXPORT_TO_STRING = 26;
   TABLE_GENERATOR = 18;
   TEXT_SCAN = 19;
   UNION_ALL = 24;

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b665fdac/relational_operators/tests/TextScanOperator_unittest.cpp
----------------------------------------------------------------------
diff --git a/relational_operators/tests/TextScanOperator_unittest.cpp b/relational_operators/tests/TextScanOperator_unittest.cpp
index c92a3dd..d609817 100644
--- a/relational_operators/tests/TextScanOperator_unittest.cpp
+++ b/relational_operators/tests/TextScanOperator_unittest.cpp
@@ -40,6 +40,7 @@
 #include "threading/ThreadIDBasedMap.hpp"
 #include "types/TypeFactory.hpp"
 #include "types/TypeID.hpp"
+#include "utility/BulkIOConfiguration.hpp"
 #include "utility/MemStream.hpp"
 
 #include "gflags/gflags.h"
@@ -191,11 +192,15 @@ TEST_F(TextScanOperatorTest, ScanTest) {
   output_destination_proto->set_relation_id(relation_->getID());
   output_destination_proto->set_relational_op_index(kOpIndex);
 
+  std::unique_ptr<BulkIOConfiguration> options =
+      std::make_unique<BulkIOConfiguration>(BulkIOFormat::kText);
+  options->setDelimiter('\t');
+  options->setEscapeStrings(true);
+
   std::unique_ptr<TextScanOperator> text_scan_op(
       new TextScanOperator(kQueryId,
                            input_filename,
-                           '\t',
-                           true,
+                           BulkIOConfigurationPtr(options.release()),
                            *relation_,
                            output_destination_index));
 

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b665fdac/utility/BulkIOConfiguration.cpp
----------------------------------------------------------------------
diff --git a/utility/BulkIOConfiguration.cpp b/utility/BulkIOConfiguration.cpp
new file mode 100644
index 0000000..af95dca
--- /dev/null
+++ b/utility/BulkIOConfiguration.cpp
@@ -0,0 +1,50 @@
+/**
+ * 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 "utility/BulkIOConfiguration.hpp"
+
+#include "glog/logging.h"
+
+namespace quickstep {
+
+void BulkIOConfiguration::initializeDefaultParameters(const BulkIOFormat format) {
+  switch (format) {
+    case BulkIOFormat::kCSV: {
+      delimiter_ = ',';
+      escape_strings_ = false;
+      header_ = true;
+      quote_ = '"';
+      null_string_ = "";
+      break;
+    }
+    case BulkIOFormat::kText: {
+      delimiter_ = '\t';
+      escape_strings_ = true;
+      header_ = false;
+      quote_ = 0;
+      null_string_ = "\\N";
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unexpected format in "
+                 << "BulkIOConfiguration::initializeDefaultParameters()";
+  }
+}
+
+}  // namespace quickstep

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b665fdac/utility/BulkIOConfiguration.hpp
----------------------------------------------------------------------
diff --git a/utility/BulkIOConfiguration.hpp b/utility/BulkIOConfiguration.hpp
new file mode 100644
index 0000000..6e02dd7
--- /dev/null
+++ b/utility/BulkIOConfiguration.hpp
@@ -0,0 +1,198 @@
+/**
+ * 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_UTILITY_BULK_IO_CONFIGURATION_HPP_
+#define QUICKSTEP_UTILITY_BULK_IO_CONFIGURATION_HPP_
+
+#include <memory>
+#include <string>
+
+#include "utility/Macros.hpp"
+
+#include "glog/logging.h"
+
+namespace quickstep {
+
+/**
+ * @brief External file format for bulk I/O.
+ */
+enum class BulkIOFormat {
+  kCSV,
+  kText
+};
+
+class BulkIOConfiguration;
+typedef std::shared_ptr<const BulkIOConfiguration> BulkIOConfigurationPtr;
+
+/**
+ * @brief Detailed file format configuration for bulk I/O (i.e. COPY operations)
+ *        that moves data between Quickstep tables and external files.
+ */
+class BulkIOConfiguration {
+ public:
+  /**
+   * @brief Constructor.
+   *
+   * @param format External file format.
+   */
+  BulkIOConfiguration(const BulkIOFormat format)
+      : format_(format) {
+    initializeDefaultParameters(format);
+  }
+
+  /**
+   * @brief Get the external file format.
+   *
+   * @return The external file format.
+   */
+  inline BulkIOFormat getFormat() const {
+    return format_;
+  }
+
+  /**
+   * @brief Get the external file format's name.
+   *
+   * @return The external file format's name.
+   */
+  inline std::string getFormatName() const {
+    switch (format_) {
+      case BulkIOFormat::kCSV:
+        return "CSV";
+      case BulkIOFormat::kText:
+        return "TEXT";
+      default:
+        break;
+    }
+    LOG(FATAL) << "Unexpected format in BulkIOConfiguration::getFormatName()";
+  }
+
+  /**
+   * @brief Get the delimiter character (which is the character that separates
+   *        attribute values in external files).
+   *
+   * @return The delimiter character.
+   */
+  inline char getDelimiter() const {
+    return delimiter_;
+  }
+
+  /**
+   * @brief Set the delimiter character.
+   *
+   * @param delimiter The delimiter character to set.
+   */
+  inline void setDelimiter(const char delimiter) {
+    delimiter_ = delimiter;
+  }
+
+  /**
+   * @brief Check whether to encode/decode between special characters and escape
+   *        sequences.
+   *
+   * @return Whether to encode/decode between special characters and escape
+   *         sequences.
+   */
+  inline bool escapeStrings() const {
+    return escape_strings_;
+  }
+
+  /**
+   * @brief Set whether to encode/decode between special characters and escape
+   *        sequences.
+   *
+   * @param escape_strings A bool value to set that indicates whether to
+   *        encode/decode between special characters and escape sequences.
+   */
+  inline void setEscapeStrings(const bool escape_strings) {
+    escape_strings_ = escape_strings;
+  }
+
+  /**
+   * @brief Check whether the external files contain headers (for CSV format only).
+   *
+   * @return Whether the external files contain headers.
+   */
+  inline bool hasHeader() const {
+    return header_;
+  }
+
+  /**
+   * @brief Set whether the external files contain headers (for CSV format only).
+   *
+   * @param header A bool value to set that indicates whether the external files
+   *        contain headers.
+   */
+  inline void setHeader(const bool header) {
+    header_ = header;
+  }
+
+  /**
+   * @brief Get the quote character (for CSV format only).
+   *
+   * @return The quote character.
+   */
+  inline char getQuoteCharacter() const {
+    return quote_;
+  }
+
+  /**
+   * @brief Set the quote character (for CSV format only).
+   *
+   * @param quote The quote character to set.
+   */
+  inline void setQuoteCharacter(const char quote) {
+    quote_ = quote;
+  }
+
+  /**
+   * @brief Get the string that represents a null value.
+   *
+   * @return The string that represents a null value.
+   */
+  inline const std::string& getNullString() const {
+    return null_string_;
+  }
+
+  /**
+   * @brief Set the string that represents a null value.
+   *
+   * @param null_string The string to set that represents a null value.
+   */
+  inline void setNullString(const std::string &null_string) {
+    null_string_ = null_string;
+  }
+
+ private:
+  // Initialize default options for CSV and TEXT formats.
+  void initializeDefaultParameters(const BulkIOFormat format);
+
+  const BulkIOFormat format_;
+
+  char delimiter_;
+  bool escape_strings_;
+  bool header_;
+  char quote_;
+  std::string null_string_;
+
+  DISALLOW_COPY_AND_ASSIGN(BulkIOConfiguration);
+};
+
+}  // namespace quickstep
+
+#endif  // QUICKSTEP_UTILITY_BULK_IO_CONFIGURATION_HPP_

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b665fdac/utility/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/utility/CMakeLists.txt b/utility/CMakeLists.txt
index b7a08f4..3230bee 100644
--- a/utility/CMakeLists.txt
+++ b/utility/CMakeLists.txt
@@ -168,6 +168,7 @@ add_library(quickstep_utility_BloomFilter ../empty_src.cpp BloomFilter.hpp)
 add_library(quickstep_utility_BloomFilter_proto
             ${quickstep_utility_BloomFilter_proto_srcs}
             ${quickstep_utility_BloomFilter_proto_hdrs})
+add_library(quickstep_utility_BulkIOConfiguration BulkIOConfiguration.cpp BulkIOConfiguration.hpp)
 add_library(quickstep_utility_CalculateInstalledMemory CalculateInstalledMemory.cpp CalculateInstalledMemory.hpp)
 add_library(quickstep_utility_Cast ../empty_src.cpp Cast.hpp)
 add_library(quickstep_utility_CheckSnprintf ../empty_src.cpp CheckSnprintf.hpp)
@@ -249,6 +250,8 @@ target_link_libraries(quickstep_utility_CompositeHash
 target_link_libraries(quickstep_utility_BarrieredReadWriteConcurrentBitVector
                       quickstep_utility_BitManipulation
                       quickstep_utility_Macros)
+target_link_libraries(quickstep_utility_BulkIOConfiguration
+                      quickstep_utility_Macros)
 target_link_libraries(quickstep_utility_DAG
                       glog
                       quickstep_utility_Macros)
@@ -353,6 +356,7 @@ target_link_libraries(quickstep_utility
                       quickstep_utility_BitVector
                       quickstep_utility_BloomFilter
                       quickstep_utility_BloomFilter_proto
+                      quickstep_utility_BulkIOConfiguration
                       quickstep_utility_CalculateInstalledMemory
                       quickstep_utility_Cast
                       quickstep_utility_CheckSnprintf

http://git-wip-us.apache.org/repos/asf/incubator-quickstep/blob/b665fdac/utility/ExecutionDAGVisualizer.cpp
----------------------------------------------------------------------
diff --git a/utility/ExecutionDAGVisualizer.cpp b/utility/ExecutionDAGVisualizer.cpp
index 2dc6976..9cbdd9e 100644
--- a/utility/ExecutionDAGVisualizer.cpp
+++ b/utility/ExecutionDAGVisualizer.cpp
@@ -55,12 +55,15 @@ using std::to_string;
 namespace quickstep {
 
 DEFINE_bool(visualize_execution_dag_partition_info, false,
-            "If true, display the operator partition info in the visualized execution plan DAG."
-            "Valid iif 'visualize_execution_dag' turns on.");
+            "If true, display the operator partition info in the visualized "
+            "execution plan DAG. Valid if 'visualize_execution_dag' turns on.");
 
 ExecutionDAGVisualizer::ExecutionDAGVisualizer(const QueryPlan &plan) {
+  using ROEnumType =
+      typename std::underlying_type<RelationalOperator::OperatorType>::type;
+
   // Do not display these relational operators in the graph.
-  const std::unordered_set<typename std::underlying_type<RelationalOperator::OperatorType>::type> no_display_op_types =
+  const std::unordered_set<ROEnumType> no_display_op_types =
       { RelationalOperator::kDestroyAggregationState,
         RelationalOperator::kDestroyHash,
         RelationalOperator::kDropTable };


Mime
View raw message