docs: def upsert(self, table, data_list, on_conflict_fields, on_conflict_action='update',
                 update_fields=None, return_cols='id'):
          """
          Create a bulk upsert statement which is much faster (~6x in tests with 10k & 100k rows and n cols)
          for upserting data then executemany()
  
          TODO: Is there a limit of length the query can be? If so handle it.
          """
          data_list = copy.deepcopy(data_list)  # Create deepcopy so the original list does not get modified
          # Make sure that `data_list` is a list
          if not isinstance(data_list, list):
              data_list = [data_list]
          # Make sure data_list has content
          if len(data_list) == 0:
              # No need to continue
              return []
          # Data in the list must be dicts (just check the first one)
          if not isinstance(data_list[0], dict):
              logger.critical("Data must be a list of dicts")
              # TODO: raise some error here rather then returning None
              return None
  
          # Make sure on_conflict_fields is a list
          if not isinstance(on_conflict_fields, list):
              on_conflict_fields = [on_conflict_fields]
          # Make sure on_conflict_fields has data
          if len(on_conflict_fields) == 0 or on_conflict_fields[0] is None:
              # No need to continue
              logger.critical("Must pass in `on_conflict_fields` argument")
              # TODO: raise some error here rather then returning None
              return None
  
          # Make sure return_cols is a list
          if return_cols is None or len(return_cols) == 0 or return_cols[0] is None:
              return_cols = ''
          elif not isinstance(return_cols, list):
              return_cols = [return_cols]
  
          if len(return_cols) > 0:
              return_cols = 'RETURNING ' + ','.join(return_cols)
  
          # Make sure update_fields is a list/valid
          if on_conflict_action == 'update':
              if not isinstance(update_fields, list):
                  update_fields = [update_fields]
              # If noting is passed in, set `update_fields` to all (data_list-on_conflict_fields)
              if len(update_fields) == 0 or update_fields[0] is None:
                  update_fields = list(set(data_list[0].keys()) - set(on_conflict_fields))
                  # If update_fields is empty here that could only mean that all fields are set as conflict_fields
                  if len(update_fields) == 0:
                      logger.critical("Not all the fields can be `on_conflict_fields` when doing an update")
                      # TODO: raise some error here rather then returning None
                      return None
  
              # If everything is good to go with the update fields
              fields_update_tmp = []
              for key in data_list[0].keys():
                  fields_update_tmp.append('"{0}"="excluded"."{0}"'.format(key))
              conflict_action_sql = 'UPDATE SET {update_fields}'\
                                    .format(update_fields=', '.join(fields_update_tmp))
          else:
              # Do nothing on conflict
              conflict_action_sql = 'NOTHING'
  
          try:
              with self.getcursor() as cur:
                  query = """INSERT INTO {table} ({insert_fields})
                             VALUES {values}
                             ON CONFLICT ({on_conflict_fields}) DO
                             {conflict_action_sql}
                             {return_cols}
                          """.format(table=table,
                                     insert_fields='"{0}"'.format('","'.join(data_list[0].keys())),
                                     values=','.join(['%s'] * len(data_list)),
                                     on_conflict_fields=','.join(on_conflict_fields),
                                     conflict_action_sql=conflict_action_sql,
                                     return_cols=return_cols,
                                     )
                  # Get all the values for each row and create a lists of lists
                  values = []
                  for row in [list(v.values()) for v in data_list]:
                      values.append(_check_values(row))
  
                  query = cur.mogrify(query, values)
  
                  cur.execute(query)
  
                  try:
                      return cur.fetchall()
                  except Exception:
                      return None
  
          except Exception as e:
              logger.exception("Error upserting data")
              logger.debug("Error upserting data: {data}".format(data=data_list))
              raise e.with_traceback(sys.exc_info()[2])