 
       // Every element in the range identifies a row in the right columns where
       // the first column matches. Need to check if all other columns also match
-      for(auto i =  range.first; i != range.second; ++i)
+      bool match{false};
+      for(auto i = range.first; i != range.second; ++i)
       {
         const auto right_index = i->second;
 
         // If all of the columns in right_columns[right_index] == all of the columns in left_columns[left_index]
         // Then this index pair is added to the result as a matching pair of row indices
-        if( true == rows_equal(left_columns, right_columns, left_index, right_index))
+        if( true == rows_equal(left_columns, right_columns, left_index, right_index)){
           result.emplace_back(left_index, right_index);
+          match = true;
+        }
+      }
+
+      // For left joins, insert a NULL if no match is found
+      if((false == match) && (join_method == join_kind::LEFT)){
+        constexpr int JoinNullValue{-1};
+        result.emplace_back(left_index, JoinNullValue);
       }
     }
 

  return os;
}

// A new instance of this class will be created for each *TEST(JoinTest, ...)
// Put all repeated setup and validation stuff here
template <typename multi_column_t>
struct InnerJoinTest : public testing::Test


// See this header for all of the recursive handling of tuples of vectors
#include "tuple_vectors.h"

enum struct join_kind
{
  INNER,
  LEFT
};

// Creates a gdf_column from a std::vector
template <typename col_type>
