{"action":"created","comment":{"url":"https://api.github.com/repos/cmu-db/peloton/pulls/comments/185157936","pull_request_review_id":116492475,"id":185157936,"diff_hunk":"@@ -6,156 +6,304 @@\n //\n // Identification: src/codegen/operator/table_scan_translator.cpp\n //\n-// Copyright (c) 2015-2017, Carnegie Mellon University Database Group\n+// Copyright (c) 2015-2018, Carnegie Mellon University Database Group\n //\n //===----------------------------------------------------------------------===//\n \n #include \"codegen/operator/table_scan_translator.h\"\n \n #include \"codegen/lang/if.h\"\n #include \"codegen/proxy/executor_context_proxy.h\"\n-#include \"codegen/proxy/storage_manager_proxy.h\"\n #include \"codegen/proxy/transaction_runtime_proxy.h\"\n #include \"codegen/proxy/runtime_functions_proxy.h\"\n #include \"codegen/proxy/zone_map_proxy.h\"\n #include \"codegen/type/boolean_type.h\"\n+#include \"codegen/vector.h\"\n #include \"planner/seq_scan_plan.h\"\n #include \"storage/data_table.h\"\n-#include \"storage/zone_map_manager.h\"\n \n namespace peloton {\n namespace codegen {\n \n-//===----------------------------------------------------------------------===//\n-// TABLE SCAN TRANSLATOR\n-//===----------------------------------------------------------------------===//\n+////////////////////////////////////////////////////////////////////////////////\n+///\n+/// AttributeAccess\n+///\n+////////////////////////////////////////////////////////////////////////////////\n+\n+/**\n+ * This class enables deferred access to any one available attribute in an input\n+ * row. The main (overridden) function, Access(), is responsible for loading the\n+ * attribute the class was constructed with from the input row provided to the\n+ * function.\n+ */\n+class TableScanTranslator::AttributeAccess : public RowBatch::AttributeAccess {\n+ public:\n+  AttributeAccess(const TileGroup::TileGroupAccess &access,\n+                  const planner::AttributeInfo *ai)\n+      : tile_group_access_(access), ai_(ai) {}\n+\n+  // Access an attribute in the given row\n+  codegen::Value Access(CodeGen &codegen, RowBatch::Row &row) override {\n+    auto raw_row = tile_group_access_.GetRow(row.GetTID(codegen));\n+    return raw_row.LoadColumn(codegen, ai_->attribute_id);\n+  }\n+\n+  const planner::AttributeInfo *GetAttributeRef() const { return ai_; }\n+\n+ private:\n+  // The accessor we use to load column values\n+  const TileGroup::TileGroupAccess &tile_group_access_;\n+  // The attribute we will access\n+  const planner::AttributeInfo *ai_;\n+};\n+\n+////////////////////////////////////////////////////////////////////////////////\n+///\n+/// ScanConsumer\n+///\n+////////////////////////////////////////////////////////////////////////////////\n+\n+/**\n+ * The ScanConsumer is the callback functor used when issuing a sequential scan\n+ * over a table. The meat of callback is in ProcessTuples(), which is called to\n+ * process a batch of tuples within a tile group of a table.\n+ */\n+class TableScanTranslator::ScanConsumer : public codegen::ScanCallback {\n+ public:\n+  // Constructor\n+  ScanConsumer(ConsumerContext &ctx, const planner::SeqScanPlan &plan,\n+               Vector &selection_vector)\n+      : ctx_(ctx),\n+        plan_(plan),\n+        selection_vector_(selection_vector),\n+        tile_group_id_(nullptr),\n+        tile_group_ptr_(nullptr) {}\n+\n+  // The callback when starting iteration over a new tile group\n+  void TileGroupStart(CodeGen &, llvm::Value *tile_group_id,\n+                      llvm::Value *tile_group_ptr) override {\n+    tile_group_id_ = tile_group_id;\n+    tile_group_ptr_ = tile_group_ptr;\n+  }\n+\n+  // The code that forms the body of the scan loop\n+  void ProcessTuples(CodeGen &codegen, llvm::Value *tid_start,\n+                     llvm::Value *tid_end,\n+                     TileGroup::TileGroupAccess &tile_group_access) override;\n+\n+  // The callback when finishing iteration over a tile group\n+  void TileGroupFinish(CodeGen &, llvm::Value *) override {}\n+\n+ private:\n+  void SetupRowBatch(RowBatch &batch,\n+                     TileGroup::TileGroupAccess &tile_group_access,\n+                     std::vector<AttributeAccess> &access) const;\n+\n+  void FilterRowsByVisibility(CodeGen &codegen, llvm::Value *tid_start,\n+                              llvm::Value *tid_end,\n+                              Vector &selection_vector) const;\n+\n+  // Filter all the rows whose TIDs are in the range [tid_start, tid_end] and\n+  // store their TIDs in the output TID selection vector\n+  void FilterRowsByPredicate(CodeGen &codegen,\n+                             const TileGroup::TileGroupAccess &access,\n+                             llvm::Value *tid_start, llvm::Value *tid_end,\n+                             Vector &selection_vector) const;\n+\n+ private:\n+  // The consumer context\n+  ConsumerContext &ctx_;\n+  // The plan node\n+  const planner::SeqScanPlan &plan_;\n+  // The selection vector used for vectorized scans\n+  Vector &selection_vector_;\n+  // The current tile group id we're scanning over\n+  llvm::Value *tile_group_id_;\n+  // The current tile group we're scanning over\n+  llvm::Value *tile_group_ptr_;\n+};\n+\n+////////////////////////////////////////////////////////////////////////////////\n+///\n+/// Table Scan Translator\n+///\n+////////////////////////////////////////////////////////////////////////////////\n \n-// Constructor\n TableScanTranslator::TableScanTranslator(const planner::SeqScanPlan &scan,\n                                          CompilationContext &context,\n                                          Pipeline &pipeline)\n-    : OperatorTranslator(context, pipeline),\n-      scan_(scan),\n-      table_(*scan_.GetTable()) {\n-  LOG_DEBUG(\"Constructing TableScanTranslator ...\");\n-\n-  // The restriction, if one exists\n-  const auto *predicate = GetScanPlan().GetPredicate();\n+    : OperatorTranslator(scan, context, pipeline), table_(*scan.GetTable()) {\n+  // Set ourselves as the source of the pipeline\n+  auto parallelism = scan.IsParallel() ? Pipeline::Parallelism::Parallel\n+                                       : Pipeline::Parallelism::Serial;\n+  pipeline.MarkSource(this, parallelism);\n+\n+  // If there is a predicate, prepare a translator for it\n+  const auto *predicate = scan.GetPredicate();\n   if (predicate != nullptr) {\n-    // If there is a predicate, prepare a translator for it\n     context.Prepare(*predicate);\n-\n-    // If the scan's predicate is SIMDable, install a boundary at the output\n-    if (predicate->IsSIMDable()) {\n-      pipeline.InstallBoundaryAtOutput(this);\n-    }\n   }\n-  LOG_DEBUG(\"Finished constructing TableScanTranslator ...\");\n }\n \n-// Produce!\n-void TableScanTranslator::Produce() const {\n-  auto &codegen = GetCodeGen();\n-  auto &table = GetTable();\n+// TODO merge serial and parallel ...\n+// TODO cleanup zonemap stuff\n \n-  LOG_TRACE(\"TableScan on [%u] starting to produce tuples ...\", table.GetOid());\n+llvm::Value *TableScanTranslator::LoadTablePtr(CodeGen &codegen) const {\n+  const storage::DataTable &table = *GetScanPlan().GetTable();\n \n   // Get the table instance from the database\n-  llvm::Value *storage_manager_ptr = GetStorageManagerPtr();\n   llvm::Value *db_oid = codegen.Const32(table.GetDatabaseOid());\n   llvm::Value *table_oid = codegen.Const32(table.GetOid());\n-  llvm::Value *table_ptr =\n-      codegen.Call(StorageManagerProxy::GetTableWithOid,\n-                   {storage_manager_ptr, db_oid, table_oid});\n-\n-  // The selection vector for the scan\n-  auto *raw_vec = codegen.AllocateBuffer(\n-      codegen.Int32Type(), Vector::kDefaultVectorSize, \"scanSelVector\");\n-  Vector sel_vec{raw_vec, Vector::kDefaultVectorSize, codegen.Int32Type()};\n-\n-  auto predicate = const_cast<expression::AbstractExpression *>(\n-      GetScanPlan().GetPredicate());\n-  llvm::Value *predicate_ptr = codegen->CreateIntToPtr(\n-      codegen.Const64((int64_t)predicate),\n-      AbstractExpressionProxy::GetType(codegen)->getPointerTo());\n-  size_t num_preds = 0;\n-\n-  auto *zone_map_manager = storage::ZoneMapManager::GetInstance();\n-  if (predicate != nullptr && zone_map_manager->ZoneMapTableExists()) {\n-    if (predicate->IsZoneMappable()) {\n-      num_preds = predicate->GetNumberofParsedPredicates();\n+  return codegen.Call(StorageManagerProxy::GetTableWithOid,\n+                      {GetStorageManagerPtr(), db_oid, table_oid});\n+}\n+\n+void TableScanTranslator::ProduceSerial() const {\n+  auto producer = [this](ConsumerContext &ctx) {\n+    CodeGen &codegen = GetCodeGen();\n+\n+    // Load the table\n+    auto *table_ptr = LoadTablePtr(codegen);\n+\n+    // The selection vector for the scan\n+    auto *i32_type = codegen.Int32Type();\n+    auto vec_size = Vector::kDefaultVectorSize.load();\n+    auto *raw_vec = codegen.AllocateBuffer(i32_type, vec_size, \"scanPosList\");\n+    Vector position_list{raw_vec, vec_size, i32_type};\n+\n+    auto predicate = const_cast<expression::AbstractExpression *>(\n+        GetScanPlan().GetPredicate());\n+    llvm::Value *predicate_ptr = codegen->CreateIntToPtr(\n+        codegen.Const64((int64_t)predicate),\n+        AbstractExpressionProxy::GetType(codegen)->getPointerTo());\n+    size_t num_preds = 0;\n+\n+    auto *zone_map_manager = storage::ZoneMapManager::GetInstance();\n+    if (predicate != nullptr && zone_map_manager->ZoneMapTableExists()) {\n+      if (predicate->IsZoneMappable()) {\n+        num_preds = predicate->GetNumberofParsedPredicates();\n+      }\n     }\n-  }\n-  ScanConsumer scan_consumer{*this, sel_vec};\n-  table_.GenerateScan(codegen, table_ptr, sel_vec.GetCapacity(), scan_consumer,\n-                      predicate_ptr, num_preds);\n-  LOG_TRACE(\"TableScan on [%u] finished producing tuples ...\", table.GetOid());\n+\n+    ScanConsumer scan_consumer{ctx, GetScanPlan(), position_list};\n+    table_.GenerateScan(codegen, table_ptr, nullptr, nullptr, vec_size,\n+                        predicate_ptr, num_preds, scan_consumer);\n+  };\n+\n+  // Execute serially\n+  GetPipeline().RunSerial(producer);\n }\n \n-// Get the stringified name of this scan\n-std::string TableScanTranslator::GetName() const {\n-  std::string name = \"Scan('\" + GetTable().GetName() + \"'\";\n-  auto *predicate = GetScanPlan().GetPredicate();\n-  if (predicate != nullptr && predicate->IsSIMDable()) {\n-    name.append(\", \").append(std::to_string(Vector::kDefaultVectorSize));\n-  }\n-  name.append(\")\");\n-  return name;\n+void TableScanTranslator::ProduceParallel() const {\n+  CodeGen &codegen = GetCodeGen();\n+\n+  // The input arguments\n+  const storage::DataTable &table = *GetScanPlan().GetTable();\n+\n+  // The use RuntimeFunctions::ExecuteTableScan() to launch a parallel scan.\n+  // We pass in the database and table IDs to scan the correct table.\n+  auto *dispatcher =\n+      RuntimeFunctionsProxy::ExecuteTableScan.GetFunction(codegen);\n+  std::vector<llvm::Value *> dispatch_args = {\n+      codegen.Const32(table.GetDatabaseOid()), codegen.Const32(table.GetOid())};\n+\n+  // Our function needs to know the start and stop tile groups to scan\n+  std::vector<llvm::Type *> pipeline_arg_types = {codegen.Int64Type(),\n+                                                  codegen.Int64Type()};\n+\n+  // Parallel production\n+  auto producer = [this, &codegen](ConsumerContext &ctx,\n+                                   const std::vector<llvm::Value *> params) {\n+    PELOTON_ASSERT(params.size() == 2);\n+\n+    // Load the table pointer\n+    auto *table_ptr = LoadTablePtr(codegen);\n+\n+    // The selection vector for the scan\n+    auto *i32_type = codegen.Int32Type();\n+    auto vec_size = Vector::kDefaultVectorSize.load();\n+    auto *raw_vec = codegen.AllocateBuffer(i32_type, vec_size, \"scanPosList\");\n+    Vector position_list{raw_vec, vec_size, i32_type};\n+\n+    // zonemap\n+    auto predicate = const_cast<expression::AbstractExpression *>(\n+        GetScanPlan().GetPredicate());\n+    llvm::Value *predicate_ptr = codegen->CreateIntToPtr(\n+        codegen.Const64((int64_t)predicate),\n+        AbstractExpressionProxy::GetType(codegen)->getPointerTo());\n+    size_t num_preds = 0;\n+\n+    auto *zone_map_manager = storage::ZoneMapManager::GetInstance();\n+    if (predicate != nullptr && zone_map_manager->ZoneMapTableExists()) {\n+      if (predicate->IsZoneMappable()) {\n+        num_preds = predicate->GetNumberofParsedPredicates();\n+      }\n+    }\n+\n+    // Scan the given range of the table\n+    ScanConsumer scan_consumer{ctx, GetScanPlan(), position_list};\n+    table_.GenerateScan(codegen, table_ptr, params[0], params[1], vec_size,\n+                        predicate_ptr, num_preds, scan_consumer);\n+  };\n+\n+  // Execute parallel\n+  auto &pipeline = GetPipeline();\n+  pipeline.RunParallel(dispatcher, dispatch_args, pipeline_arg_types, producer);\n }\n \n-// Table accessor\n-const storage::DataTable &TableScanTranslator::GetTable() const {\n-  return *scan_.GetTable();\n+void TableScanTranslator::Produce() const {\n+  if (GetPipeline().IsParallel()) {","path":"src/codegen/operator/table_scan_translator.cpp","position":315,"original_position":315,"commit_id":"5fae69632da082d8cfab7351abd33de054039818","original_commit_id":"5fae69632da082d8cfab7351abd33de054039818","user":{"login":"pmenon","id":243434,"avatar_url":"https://avatars0.githubusercontent.com/u/243434?v=4","gravatar_id":"","url":"https://api.github.com/users/pmenon","html_url":"https://github.com/pmenon","followers_url":"https://api.github.com/users/pmenon/followers","following_url":"https://api.github.com/users/pmenon/following{/other_user}","gists_url":"https://api.github.com/users/pmenon/gists{/gist_id}","starred_url":"https://api.github.com/users/pmenon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pmenon/subscriptions","organizations_url":"https://api.github.com/users/pmenon/orgs","repos_url":"https://api.github.com/users/pmenon/repos","events_url":"https://api.github.com/users/pmenon/events{/privacy}","received_events_url":"https://api.github.com/users/pmenon/received_events","type":"User","site_admin":false},"body":"Correct. Each operator can choose whether they want to be parallel or not. Unless an operator in the pipeline specifically calls `Pipeline::SetSerial`, the decision for the parallelism of the pipeline is made in `Pipeline::MarkSource`. The decision takes into account:\r\n1. Whether parallel execution is enabled.\r\n2. Whether the consumer supports parallel execution.\r\n3. The preference of parallelism of all operators in the pipeline.\r\n\r\nI think the comments are clear, but let me know otherwise!","created_at":"2018-05-01T02:39:30Z","updated_at":"2018-05-01T02:39:30Z","html_url":"https://github.com/cmu-db/peloton/pull/1304#discussion_r185157936","pull_request_url":"https://api.github.com/repos/cmu-db/peloton/pulls/1304","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/cmu-db/peloton/pulls/comments/185157936"},"html":{"href":"https://github.com/cmu-db/peloton/pull/1304#discussion_r185157936"},"pull_request":{"href":"https://api.github.com/repos/cmu-db/peloton/pulls/1304"}},"in_reply_to_id":185066357},"pull_request":{"url":"https://api.github.com/repos/cmu-db/peloton/pulls/1304","id":182041033,"html_url":"https://github.com/cmu-db/peloton/pull/1304","diff_url":"https://github.com/cmu-db/peloton/pull/1304.diff","patch_url":"https://github.com/cmu-db/peloton/pull/1304.patch","issue_url":"https://api.github.com/repos/cmu-db/peloton/issues/1304","number":1304,"state":"open","locked":false,"title":"Parallel query execution","user":{"login":"pmenon","id":243434,"avatar_url":"https://avatars0.githubusercontent.com/u/243434?v=4","gravatar_id":"","url":"https://api.github.com/users/pmenon","html_url":"https://github.com/pmenon","followers_url":"https://api.github.com/users/pmenon/followers","following_url":"https://api.github.com/users/pmenon/following{/other_user}","gists_url":"https://api.github.com/users/pmenon/gists{/gist_id}","starred_url":"https://api.github.com/users/pmenon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pmenon/subscriptions","organizations_url":"https://api.github.com/users/pmenon/orgs","repos_url":"https://api.github.com/users/pmenon/repos","events_url":"https://api.github.com/users/pmenon/events{/privacy}","received_events_url":"https://api.github.com/users/pmenon/received_events","type":"User","site_admin":false},"body":"This is a PR for parallel query execution in codegen. \r\n\r\n- Supported parallel operators:\r\n  - [x] Sequential scans\r\n  - [x] Sorting\r\n  - [x] Hash joins\r\n  - [ ] Aggregations\r\n  - [ ] Inserts (purposely disabled until we have COPY)\r\n  - [ ] Updates\r\n  - [ ] Deletes\r\n- Parallelism is done in fork-join manner. \r\n- Settings added to toggle parallel execution, and set the minimum table size threshold before parallel execution kicks in. This logic should be modified.\r\n  - @chenboy Check `plan_generator` to see if the logic looks good.\r\n - I've split up `RuntimeState` into `QueryState` and `PipelineState`.\r\n   - `QueryState` exists for the lifetime of the query. It is initialized in the `init()` function, and torn down in the `tearDown()` function.\r\n   - `PipelineState` exists for only one pipeline. It is created on entry to the pipeline function, and is torn down upon exit.\r\n - I've cleaned up proxies to simplify loading/store struct member variables using names rather than index positions.\r\n   - What used to be `codegen.CreateGEP(..., 0, 2)` to load struct elements is now `codegen.Load(HashTableProxy::directory, ...)`.\r\n   - Similarly done for storing elements of a struct: `codegen.Store(...)`\r\n- Removed `CCHashTable`, added generic `HashTable` that will eventually be used for both joins and aggregations.\r\n\r\n### Review notes:\r\n- Most of the heavy lifting is done in `pipeline.cpp`. Pipelines can be run serially or in parallel.\r\n  - This means all operators need to pass a `std::function` when launching pipelines.\r\n- Many of the changed files are in the proxies, making is simpler to create them.\r\n\r\n### TODO:\r\n~~- Remove `std::mutex` from buffer output~~ (This will be another PR)\r\n~~- Enable parallel aggregations~~ (This will be another PR when parallel agg team is done)\r\n~~- Add tests for memory leak~~ (Done)","created_at":"2018-04-17T03:17:22Z","updated_at":"2018-05-01T02:39:30Z","closed_at":null,"merged_at":null,"merge_commit_sha":"1704400e958f458385d5a2452693c8d0c95baf7f","assignee":{"login":"pmenon","id":243434,"avatar_url":"https://avatars0.githubusercontent.com/u/243434?v=4","gravatar_id":"","url":"https://api.github.com/users/pmenon","html_url":"https://github.com/pmenon","followers_url":"https://api.github.com/users/pmenon/followers","following_url":"https://api.github.com/users/pmenon/following{/other_user}","gists_url":"https://api.github.com/users/pmenon/gists{/gist_id}","starred_url":"https://api.github.com/users/pmenon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pmenon/subscriptions","organizations_url":"https://api.github.com/users/pmenon/orgs","repos_url":"https://api.github.com/users/pmenon/repos","events_url":"https://api.github.com/users/pmenon/events{/privacy}","received_events_url":"https://api.github.com/users/pmenon/received_events","type":"User","site_admin":false},"assignees":[{"login":"pmenon","id":243434,"avatar_url":"https://avatars0.githubusercontent.com/u/243434?v=4","gravatar_id":"","url":"https://api.github.com/users/pmenon","html_url":"https://github.com/pmenon","followers_url":"https://api.github.com/users/pmenon/followers","following_url":"https://api.github.com/users/pmenon/following{/other_user}","gists_url":"https://api.github.com/users/pmenon/gists{/gist_id}","starred_url":"https://api.github.com/users/pmenon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pmenon/subscriptions","organizations_url":"https://api.github.com/users/pmenon/orgs","repos_url":"https://api.github.com/users/pmenon/repos","events_url":"https://api.github.com/users/pmenon/events{/privacy}","received_events_url":"https://api.github.com/users/pmenon/received_events","type":"User","site_admin":false}],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":325402180,"url":"https://api.github.com/repos/cmu-db/peloton/labels/core","name":"core","color":"207de5","default":false},{"id":325403144,"url":"https://api.github.com/repos/cmu-db/peloton/labels/feature","name":"feature","color":"006b75","default":false},{"id":747453861,"url":"https://api.github.com/repos/cmu-db/peloton/labels/in%20progress","name":"in progress","color":"e5fc37","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/cmu-db/peloton/pulls/1304/commits","review_comments_url":"https://api.github.com/repos/cmu-db/peloton/pulls/1304/comments","review_comment_url":"https://api.github.com/repos/cmu-db/peloton/pulls/comments{/number}","comments_url":"https://api.github.com/repos/cmu-db/peloton/issues/1304/comments","statuses_url":"https://api.github.com/repos/cmu-db/peloton/statuses/5fae69632da082d8cfab7351abd33de054039818","head":{"label":"pmenon:mt","ref":"mt","sha":"5fae69632da082d8cfab7351abd33de054039818","user":{"login":"pmenon","id":243434,"avatar_url":"https://avatars0.githubusercontent.com/u/243434?v=4","gravatar_id":"","url":"https://api.github.com/users/pmenon","html_url":"https://github.com/pmenon","followers_url":"https://api.github.com/users/pmenon/followers","following_url":"https://api.github.com/users/pmenon/following{/other_user}","gists_url":"https://api.github.com/users/pmenon/gists{/gist_id}","starred_url":"https://api.github.com/users/pmenon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pmenon/subscriptions","organizations_url":"https://api.github.com/users/pmenon/orgs","repos_url":"https://api.github.com/users/pmenon/repos","events_url":"https://api.github.com/users/pmenon/events{/privacy}","received_events_url":"https://api.github.com/users/pmenon/received_events","type":"User","site_admin":false},"repo":{"id":86023302,"name":"peloton-1","full_name":"pmenon/peloton-1","owner":{"login":"pmenon","id":243434,"avatar_url":"https://avatars0.githubusercontent.com/u/243434?v=4","gravatar_id":"","url":"https://api.github.com/users/pmenon","html_url":"https://github.com/pmenon","followers_url":"https://api.github.com/users/pmenon/followers","following_url":"https://api.github.com/users/pmenon/following{/other_user}","gists_url":"https://api.github.com/users/pmenon/gists{/gist_id}","starred_url":"https://api.github.com/users/pmenon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pmenon/subscriptions","organizations_url":"https://api.github.com/users/pmenon/orgs","repos_url":"https://api.github.com/users/pmenon/repos","events_url":"https://api.github.com/users/pmenon/events{/privacy}","received_events_url":"https://api.github.com/users/pmenon/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/pmenon/peloton-1","description":"The Self-Driving Database Management System","fork":true,"url":"https://api.github.com/repos/pmenon/peloton-1","forks_url":"https://api.github.com/repos/pmenon/peloton-1/forks","keys_url":"https://api.github.com/repos/pmenon/peloton-1/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pmenon/peloton-1/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pmenon/peloton-1/teams","hooks_url":"https://api.github.com/repos/pmenon/peloton-1/hooks","issue_events_url":"https://api.github.com/repos/pmenon/peloton-1/issues/events{/number}","events_url":"https://api.github.com/repos/pmenon/peloton-1/events","assignees_url":"https://api.github.com/repos/pmenon/peloton-1/assignees{/user}","branches_url":"https://api.github.com/repos/pmenon/peloton-1/branches{/branch}","tags_url":"https://api.github.com/repos/pmenon/peloton-1/tags","blobs_url":"https://api.github.com/repos/pmenon/peloton-1/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pmenon/peloton-1/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pmenon/peloton-1/git/refs{/sha}","trees_url":"https://api.github.com/repos/pmenon/peloton-1/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pmenon/peloton-1/statuses/{sha}","languages_url":"https://api.github.com/repos/pmenon/peloton-1/languages","stargazers_url":"https://api.github.com/repos/pmenon/peloton-1/stargazers","contributors_url":"https://api.github.com/repos/pmenon/peloton-1/contributors","subscribers_url":"https://api.github.com/repos/pmenon/peloton-1/subscribers","subscription_url":"https://api.github.com/repos/pmenon/peloton-1/subscription","commits_url":"https://api.github.com/repos/pmenon/peloton-1/commits{/sha}","git_commits_url":"https://api.github.com/repos/pmenon/peloton-1/git/commits{/sha}","comments_url":"https://api.github.com/repos/pmenon/peloton-1/comments{/number}","issue_comment_url":"https://api.github.com/repos/pmenon/peloton-1/issues/comments{/number}","contents_url":"https://api.github.com/repos/pmenon/peloton-1/contents/{+path}","compare_url":"https://api.github.com/repos/pmenon/peloton-1/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pmenon/peloton-1/merges","archive_url":"https://api.github.com/repos/pmenon/peloton-1/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pmenon/peloton-1/downloads","issues_url":"https://api.github.com/repos/pmenon/peloton-1/issues{/number}","pulls_url":"https://api.github.com/repos/pmenon/peloton-1/pulls{/number}","milestones_url":"https://api.github.com/repos/pmenon/peloton-1/milestones{/number}","notifications_url":"https://api.github.com/repos/pmenon/peloton-1/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pmenon/peloton-1/labels{/name}","releases_url":"https://api.github.com/repos/pmenon/peloton-1/releases{/id}","deployments_url":"https://api.github.com/repos/pmenon/peloton-1/deployments","created_at":"2017-03-24T03:28:49Z","updated_at":"2018-05-01T02:11:55Z","pushed_at":"2018-05-01T02:11:53Z","git_url":"git://github.com/pmenon/peloton-1.git","ssh_url":"git@github.com:pmenon/peloton-1.git","clone_url":"https://github.com/pmenon/peloton-1.git","svn_url":"https://github.com/pmenon/peloton-1","homepage":"http://pelotondb.org","size":68395,"stargazers_count":2,"watchers_count":2,"language":"C++","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0"},"forks":1,"open_issues":0,"watchers":2,"default_branch":"master"}},"base":{"label":"cmu-db:master","ref":"master","sha":"70408014402417dd97a3ae09ffbbba2375336130","user":{"login":"cmu-db","id":6220241,"avatar_url":"https://avatars0.githubusercontent.com/u/6220241?v=4","gravatar_id":"","url":"https://api.github.com/users/cmu-db","html_url":"https://github.com/cmu-db","followers_url":"https://api.github.com/users/cmu-db/followers","following_url":"https://api.github.com/users/cmu-db/following{/other_user}","gists_url":"https://api.github.com/users/cmu-db/gists{/gist_id}","starred_url":"https://api.github.com/users/cmu-db/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cmu-db/subscriptions","organizations_url":"https://api.github.com/users/cmu-db/orgs","repos_url":"https://api.github.com/users/cmu-db/repos","events_url":"https://api.github.com/users/cmu-db/events{/privacy}","received_events_url":"https://api.github.com/users/cmu-db/received_events","type":"Organization","site_admin":false},"repo":{"id":28202609,"name":"peloton","full_name":"cmu-db/peloton","owner":{"login":"cmu-db","id":6220241,"avatar_url":"https://avatars0.githubusercontent.com/u/6220241?v=4","gravatar_id":"","url":"https://api.github.com/users/cmu-db","html_url":"https://github.com/cmu-db","followers_url":"https://api.github.com/users/cmu-db/followers","following_url":"https://api.github.com/users/cmu-db/following{/other_user}","gists_url":"https://api.github.com/users/cmu-db/gists{/gist_id}","starred_url":"https://api.github.com/users/cmu-db/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cmu-db/subscriptions","organizations_url":"https://api.github.com/users/cmu-db/orgs","repos_url":"https://api.github.com/users/cmu-db/repos","events_url":"https://api.github.com/users/cmu-db/events{/privacy}","received_events_url":"https://api.github.com/users/cmu-db/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/cmu-db/peloton","description":"The Self-Driving Database Management System","fork":false,"url":"https://api.github.com/repos/cmu-db/peloton","forks_url":"https://api.github.com/repos/cmu-db/peloton/forks","keys_url":"https://api.github.com/repos/cmu-db/peloton/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cmu-db/peloton/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cmu-db/peloton/teams","hooks_url":"https://api.github.com/repos/cmu-db/peloton/hooks","issue_events_url":"https://api.github.com/repos/cmu-db/peloton/issues/events{/number}","events_url":"https://api.github.com/repos/cmu-db/peloton/events","assignees_url":"https://api.github.com/repos/cmu-db/peloton/assignees{/user}","branches_url":"https://api.github.com/repos/cmu-db/peloton/branches{/branch}","tags_url":"https://api.github.com/repos/cmu-db/peloton/tags","blobs_url":"https://api.github.com/repos/cmu-db/peloton/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cmu-db/peloton/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cmu-db/peloton/git/refs{/sha}","trees_url":"https://api.github.com/repos/cmu-db/peloton/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cmu-db/peloton/statuses/{sha}","languages_url":"https://api.github.com/repos/cmu-db/peloton/languages","stargazers_url":"https://api.github.com/repos/cmu-db/peloton/stargazers","contributors_url":"https://api.github.com/repos/cmu-db/peloton/contributors","subscribers_url":"https://api.github.com/repos/cmu-db/peloton/subscribers","subscription_url":"https://api.github.com/repos/cmu-db/peloton/subscription","commits_url":"https://api.github.com/repos/cmu-db/peloton/commits{/sha}","git_commits_url":"https://api.github.com/repos/cmu-db/peloton/git/commits{/sha}","comments_url":"https://api.github.com/repos/cmu-db/peloton/comments{/number}","issue_comment_url":"https://api.github.com/repos/cmu-db/peloton/issues/comments{/number}","contents_url":"https://api.github.com/repos/cmu-db/peloton/contents/{+path}","compare_url":"https://api.github.com/repos/cmu-db/peloton/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cmu-db/peloton/merges","archive_url":"https://api.github.com/repos/cmu-db/peloton/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cmu-db/peloton/downloads","issues_url":"https://api.github.com/repos/cmu-db/peloton/issues{/number}","pulls_url":"https://api.github.com/repos/cmu-db/peloton/pulls{/number}","milestones_url":"https://api.github.com/repos/cmu-db/peloton/milestones{/number}","notifications_url":"https://api.github.com/repos/cmu-db/peloton/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cmu-db/peloton/labels{/name}","releases_url":"https://api.github.com/repos/cmu-db/peloton/releases{/id}","deployments_url":"https://api.github.com/repos/cmu-db/peloton/deployments","created_at":"2014-12-18T21:35:23Z","updated_at":"2018-04-30T13:02:22Z","pushed_at":"2018-05-01T00:36:20Z","git_url":"git://github.com/cmu-db/peloton.git","ssh_url":"git@github.com:cmu-db/peloton.git","clone_url":"https://github.com/cmu-db/peloton.git","svn_url":"https://github.com/cmu-db/peloton","homepage":"http://pelotondb.io","size":68830,"stargazers_count":1229,"watchers_count":1229,"language":"C++","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":454,"mirror_url":null,"archived":false,"open_issues_count":155,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0"},"forks":454,"open_issues":155,"watchers":1229,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/cmu-db/peloton/pulls/1304"},"html":{"href":"https://github.com/cmu-db/peloton/pull/1304"},"issue":{"href":"https://api.github.com/repos/cmu-db/peloton/issues/1304"},"comments":{"href":"https://api.github.com/repos/cmu-db/peloton/issues/1304/comments"},"review_comments":{"href":"https://api.github.com/repos/cmu-db/peloton/pulls/1304/comments"},"review_comment":{"href":"https://api.github.com/repos/cmu-db/peloton/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/cmu-db/peloton/pulls/1304/commits"},"statuses":{"href":"https://api.github.com/repos/cmu-db/peloton/statuses/5fae69632da082d8cfab7351abd33de054039818"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"pmenon"}}