static int
rasqal_join_rowsource_reset(rasqal_rowsource* rowsource, void *user_data)
{
  rasqal_join_rowsource_context* con;
  int rc;
  
  con = (rasqal_join_rowsource_context*)user_data;

  con->state = JS_START;
  con->failed = 0;
  
  rc = rasqal_rowsource_reset(con->left);
  if(rc)
    return rc;

  return rasqal_rowsource_reset(con->right);
}
static int
rasqal_distinct_rowsource_reset(rasqal_rowsource* rowsource, void *user_data)
{
  rasqal_distinct_rowsource_context *con;
  con = (rasqal_distinct_rowsource_context*)user_data;

  return rasqal_rowsource_reset(con->rowsource);
}
static rasqal_row*
rasqal_graph_rowsource_read_row(rasqal_rowsource* rowsource, void *user_data)
{
  rasqal_graph_rowsource_context *con;
  rasqal_row* row = NULL;

  con = (rasqal_graph_rowsource_context*)user_data;

  if(con->finished)
    return NULL;
  
  while(1) {
    row = rasqal_rowsource_read_row(con->rowsource);
    if(row)
      break;
    
    if(rasqal_graph_next_dg(con)) {
      con->finished = 1;
      break;
    }
    if(rasqal_rowsource_reset(con->rowsource)) {
      con->finished = 1;
      break;
    }
  }

  /* If a row is returned, put the GRAPH variable value as first literal */
  if(row) {
    rasqal_row* nrow;
    int i;
    
    nrow = rasqal_new_row_for_size(rowsource->world, 1 + row->size);
    if(!nrow) {
      rasqal_free_row(row);
      row = NULL;
    } else {
      nrow->rowsource = rowsource;
      nrow->offset = row->offset;
      
      /* Put GRAPH variable value (or NULL) first in result row */
      nrow->values[0] = rasqal_new_literal_from_literal(con->var->value);

      /* Copy (size-1) remaining variables from input row */
      for(i = 0; i < row->size; i++)
        nrow->values[i + 1] = rasqal_new_literal_from_literal(row->values[i]);
      rasqal_free_row(row);
      row = nrow;
    }
  }
  
  return row;
}
static int
rasqal_graph_rowsource_reset(rasqal_rowsource* rowsource, void *user_data)
{
  rasqal_graph_rowsource_context *con;
  con = (rasqal_graph_rowsource_context*)user_data;

  con->finished = 0;
  con->dg_offset = -1;
  con->offset = 0;

  rasqal_graph_next_dg(con);
  
  return rasqal_rowsource_reset(con->rowsource);
}
static rasqal_row*
rasqal_join_rowsource_read_row(rasqal_rowsource* rowsource, void *user_data)
{
  rasqal_join_rowsource_context* con;
  rasqal_row* row = NULL;
  rasqal_query *query = rowsource->query;

  con = (rasqal_join_rowsource_context*)user_data;

  if(con->failed || con->state == JS_FINISHED)
    return NULL;

  while(1) {
    rasqal_row *right_row;
    int bresult = 1;
    int compatible = 1;

    if(con->state == JS_START) {
      /* start / re-start left */
      if(con->left_row)
        rasqal_free_row(con->left_row);

      con->left_row  = rasqal_rowsource_read_row(con->left);
#ifdef RASQAL_DEBUG
      RASQAL_DEBUG2("rowsource %p read left row : ", rowsource);
      if(con->left_row)
        rasqal_row_print(con->left_row, stderr);
      else
        fputs("NONE", stderr);
      fputs("\n", stderr);
#endif
      con->state = JS_INIT_RIGHT;
    }

    if(con->state == JS_INIT_RIGHT) {
      /* start right */

      if(!con->left_row) {
        con->state = JS_FINISHED;
        return NULL;
      }

      con->right_rows_joined_count = 0;

      rasqal_rowsource_reset(con->right);
    }


    right_row = rasqal_rowsource_read_row(con->right);
#ifdef RASQAL_DEBUG
    RASQAL_DEBUG2("rowsource %p read right row : ", rowsource);
    if(right_row)
      rasqal_row_print(right_row, stderr);
    else
      fputs("NONE", stderr);
    fputs("\n", stderr);
#endif

    if(!right_row && con->state == JS_READ_RIGHT) {
      /* right has finished */

      /* restart left */
      con->state = JS_START;

      /* if all right table returned no bindings, return left row */
      if(!con->right_rows_joined_count) {
        /* otherwise return LEFT or RIGHT row only */
        if(con->join_type == RASQAL_JOIN_TYPE_LEFT) {
          /* LEFT JOIN - add left row if expr fails or not compatible */
          if(con->left_row) {
            con->right_rows_joined_count++;
        
            row = rasqal_join_rowsource_build_merged_row(rowsource, con, NULL);
            break;
          }
        }
      }

      /* restart left by continuing the loop */
      continue;
    }


    /* state is always JS_READ_RIGHT at this point */
    con->state = JS_READ_RIGHT;

    /* now may have both left and right rows so compute compatibility */
    if(right_row) {
      compatible = rasqal_row_compatible_check(con->rc_map,
                                               con->left_row, right_row);
      RASQAL_DEBUG2("join rows compatible: %s\n", compatible ? "YES" : "NO");
    }


    if(con->constant_join_condition >= 0) {
      /* Get constant join expression value */
      bresult = con->constant_join_condition;
    } else if(con->expr) {
      /* Check join expression if present */
      rasqal_literal *result;
      int error = 0;
      
      result = rasqal_expression_evaluate2(con->expr, query->eval_context,
                                           &error);
#ifdef RASQAL_DEBUG
      RASQAL_DEBUG1("join expression result: ");
      if(error)
        fputs("type error", DEBUG_FH);
      else
        rasqal_literal_print(result, DEBUG_FH);
      fputc('\n', DEBUG_FH);
#endif

      if(error) {
        bresult = 0;
      } else {
        error = 0;
        bresult = rasqal_literal_as_boolean(result, &error);
#ifdef RASQAL_DEBUG
        if(error)
          RASQAL_DEBUG1("filter boolean expression returned error\n");
        else
          RASQAL_DEBUG2("filter boolean expression result: %d\n", bresult);
#endif
        rasqal_free_literal(result);
      }
    }
    
    if(con->join_type == RASQAL_JOIN_TYPE_NATURAL) {
      /* found a row if compatible and constraint matches */
      if(compatible && bresult && right_row) {
        con->right_rows_joined_count++;

        /* consumes right_row */
        row = rasqal_join_rowsource_build_merged_row(rowsource, con, right_row);
        break;
      }
      
    } else if(con->join_type == RASQAL_JOIN_TYPE_LEFT) {
      /*
       * { merge(mu1, mu2) | mu1 in Omega1 and mu2 in Omega2, and mu1
       *   and mu2 are compatible and expr(merge(mu1, mu2)) is true }
       */
      if(compatible && bresult) {
        con->right_rows_joined_count++;

        /* No constraint OR constraint & compatible so return merged row */

        /* Compute row only now it is known to be needed (consumes right_row) */
        row = rasqal_join_rowsource_build_merged_row(rowsource, con, right_row);
        break;
      }

#if 0    
      /*
       * { mu1 | mu1 in Omega1 and mu2 in Omega2, and mu1 and mu2 are
       * not compatible }
       */
      if(!compatible) {
        /* otherwise return LEFT or RIGHT row only */
        if(con->join_type == RASQAL_JOIN_TYPE_LEFT) {
          /* LEFT JOIN - add left row if expr fails or not compatible */
          if(con->left_row) {
            con->right_rows_joined_count++;

            row = rasqal_join_rowsource_build_merged_row(rowsource, con, NULL);
            if(right_row)
              rasqal_free_row(right_row);
            break;
          }
        }
      }
#endif

      /*
       * { mu1 | mu1 in Omega1 and mu2 in Omega2, and mu1 and mu2 are
       *   compatible and for all mu2, expr(merge(mu1, mu2)) is false }
       */
      
      /* The above is handled using check for
       * !con->right_rows_joined_count earlier, to generate a row
       * once.
       */

    } /* end if LEFT JOIN */

    if(right_row)
      rasqal_free_row(right_row);
      
  } /* end while */

  if(row) {
    rasqal_row_set_rowsource(row, rowsource);
    row->offset = con->offset++;

    rasqal_row_bind_variables(row, rowsource->query->vars_table);
  }
  
  return row;
}