SQLRETURN SQL_API myodbc_single_fetch()

in driver/results.cc [1953:2272]


SQLRETURN SQL_API myodbc_single_fetch( SQLHSTMT             hstmt,
                                       SQLUSMALLINT         fFetchType,
                                       SQLLEN               irow,
                                       SQLULEN             *pcrow,
                                       SQLUSMALLINT        *rgfRowStatus,
                                       my_bool              upd_status )
{
  SQLULEN           rows_to_fetch;
  long              cur_row, max_row;
  SQLRETURN         row_res, res;
  STMT              *stmt= (STMT *) hstmt;
  MYSQL_ROW         values= 0;
  MYSQL_ROW_OFFSET  save_position= 0;
  SQLULEN           dummy_pcrow;
  BOOL              disconnected= FALSE;
  long              brow= 0;

  try
  {
    if ( !stmt->result )
      return stmt->set_error("24000", "Fetch without a SELECT", 0);

    cur_row = stmt->current_row;

    if ( !pcrow )
      pcrow= &dummy_pcrow;

    /* for scrollable cursor("scroller") max_row is max row for currently
       fetched part of resultset */
    max_row= (long) num_rows(stmt);
    stmt->reset_getdata_position();
    stmt->current_values= 0;          /* For SQLGetData */

    switch ( fFetchType )
    {
      case SQL_FETCH_NEXT:
        cur_row= (stmt->current_row < 0 ? 0 :
                  stmt->current_row + stmt->rows_found_in_set);
        break;
      case SQL_FETCH_PRIOR:
        cur_row= (stmt->current_row <= 0 ? -1 :
                  (long)(stmt->current_row - stmt->ard->array_size));
        break;
      case SQL_FETCH_FIRST:
        cur_row= 0L;
        break;
      case SQL_FETCH_LAST:
        cur_row = max_row - (long)stmt->ard->array_size;
        break;
      case SQL_FETCH_ABSOLUTE:
        if (irow < 0)
        {
          /* Fetch from end of result set */
          if ( max_row+irow < 0 && -irow <= (long) stmt->ard->array_size )
          {
            /*
              | FetchOffset | > LastResultRow AND
              | FetchOffset | <= RowsetSize
            */
            cur_row= 0;     /* Return from beginning */
          }
          else
            cur_row = max_row + (long)irow;     /* Ok if max_row <= -irow */
        }
        else
          cur_row= (long) irow - 1;
        break;

      case SQL_FETCH_RELATIVE:
        cur_row = stmt->current_row + (long)irow;
        if (stmt->current_row > 0 && cur_row < 0 &&
           (long) - irow <= (long)stmt->ard->array_size)
        {
          cur_row= 0;
        }
        break;

      case SQL_FETCH_BOOKMARK:
        {
          if (stmt->stmt_options.bookmark_ptr)
          {
            DESCREC *arrec;
            IS_BOOKMARK_VARIABLE(stmt);
            arrec= desc_get_rec(stmt->ard, -1, FALSE);

            if (arrec->concise_type == SQL_C_BOOKMARK)
            {
              brow = (long)(*((SQLLEN *) stmt->stmt_options.bookmark_ptr));
            }
            else
            {
              brow= atol((const char*) stmt->stmt_options.bookmark_ptr);
            }
          }

          cur_row = brow + (long)irow;
          if (cur_row < 0 && (long)-irow <= (long)stmt->ard->array_size)
          {
            cur_row= 0;
          }
        }
        break;

      default:
          return stmt->set_error( MYERR_S1106, "Fetch type out of range", 0);
    }

    if ( cur_row < 0 )
    {
      stmt->current_row= -1;  /* Before first row */
      stmt->rows_found_in_set= 0;
      data_seek(stmt, 0L);
      stmt->set_error("01S07", "One or more row has error.", 0);
      return SQL_SUCCESS_WITH_INFO; //SQL_NO_DATA_FOUND
    }
    if ( cur_row > max_row )
    {
      if (scroller_exists(stmt))
      {
        while (cur_row > (max_row= (long)scroller_move(stmt)));

        switch (scroller_prefetch(stmt))
        {
          case SQL_NO_DATA:
            stmt->set_error("01S07", "One or more row has error.", 0);
            return SQL_SUCCESS_WITH_INFO; //SQL_NO_DATA_FOUND
          case SQL_ERROR:   return stmt->set_error(MYERR_S1000,
                                            mysql_error(stmt->dbc->mysql), 0);
        }
      }
      else
      {
        cur_row= max_row;
      }
    }

    if ( !stmt->result_array && !if_forward_cache(stmt) )
    {
        /*
          If Dynamic, it loses the stmt->end_of_set, so
          seek to desired row, might have new data or
          might be deleted
        */
        if ( stmt->stmt_options.cursor_type != SQL_CURSOR_DYNAMIC &&
             cur_row && cur_row == (long)(stmt->current_row +
                                          stmt->rows_found_in_set) )
            row_seek(stmt, stmt->end_of_set);
        else
            data_seek(stmt, cur_row);
    }
    stmt->current_row= cur_row;

    if (scroller_exists(stmt)
      || (if_forward_cache(stmt) && !stmt->result_array))
    {
      rows_to_fetch= stmt->ard->array_size;
    }
    else
    {
      rows_to_fetch= myodbc_min(max_row-cur_row,
                                (long)stmt->ard->array_size);
    }

    /* out params has been silently fetched */
    if (rows_to_fetch == 0)
    {
      if (stmt->out_params_state != OPS_UNKNOWN)
      {
        rows_to_fetch= 1;
      }
      else
      {
        *pcrow= 0;
        stmt->rows_found_in_set= 0;

        if ( upd_status && stmt->ird->rows_processed_ptr )
        {
          *stmt->ird->rows_processed_ptr= 0;
        }

        stmt->set_error("01S07", "One or more row has error.", 0);
        return SQL_SUCCESS_WITH_INFO; //SQL_NO_DATA_FOUND
      }
    }

    res= SQL_SUCCESS;
    {
      save_position= row_tell(stmt);
      /* - Actual fetching happens here - */
      if (!(values = stmt->fetch_row()) )
      {
        if (scroller_exists(stmt))
        {
          scroller_move(stmt);

          row_res= scroller_prefetch(stmt);

          if (row_res != SQL_SUCCESS)
          {
            goto exitSQLSingleFetch;
          }

          if ( !(values = stmt->fetch_row()) )
          {
            goto exitSQLSingleFetch;
          }

          /* Not sure that is right, but see it better than nothing */
          save_position= row_tell(stmt);
        }
        else
        {
          goto exitSQLSingleFetch;
        }
      }

      if ( stmt->fix_fields )
      {
          values= (*stmt->fix_fields)(stmt,values);
      }

      stmt->current_values= values;
    }

    if (!stmt->fix_fields)
    {
        fill_ird_data_lengths(stmt->ird, fetch_lengths(stmt),
                              stmt->result->field_count);
    }

    row_res= fill_fetch_buffers(stmt, values, cur_row);

    /* For SQL_SUCCESS we need all rows to be SQL_SUCCESS */
    if (res != row_res)
    {
      /* Any successful row makes overall result SQL_SUCCESS_WITH_INFO */
      if (SQL_SUCCEEDED(row_res))
      {
        res= SQL_SUCCESS_WITH_INFO;
      }
      /* Else error */
      else if (cur_row == 0)
      {
        /* SQL_ERROR only if all rows fail */
        res= SQL_ERROR;
      }
      else
      {
        res= SQL_SUCCESS_WITH_INFO;
      }
    }

    /* "Fetching" includes buffers filling. I think errors in that
       have to affect row status */

    if (rgfRowStatus)
    {
      rgfRowStatus[cur_row]= sqlreturn2row_status(row_res);
    }
    /*
      No need to update rowStatusPtr_ex, it's the same as rgfRowStatus.
    */
    if (upd_status && stmt->ird->array_status_ptr)
    {
      stmt->ird->array_status_ptr[cur_row]= sqlreturn2row_status(row_res);
    }

exitSQLSingleFetch:
    stmt->rows_found_in_set= 1;
    *pcrow= cur_row;

    disconnected= is_connection_lost(mysql_errno(stmt->dbc->mysql))
      && handle_connection_error(stmt);

    if ( upd_status && stmt->ird->rows_processed_ptr )
    {
      *stmt->ird->rows_processed_ptr= cur_row;
    }

    /* It is possible that both rgfRowStatus and array_status_ptr are set
    (and upp_status is TRUE) */
    if ( rgfRowStatus )
    {
      rgfRowStatus[cur_row]= disconnected ? SQL_ROW_ERROR : SQL_ROW_NOROW;
    }
    /*
      No need to update rowStatusPtr_ex, it's the same as rgfRowStatus.
    */
    if ( upd_status && stmt->ird->array_status_ptr )
    {
      stmt->ird->array_status_ptr[cur_row]= disconnected? SQL_ROW_ERROR
                                                  : SQL_ROW_NOROW;
    }

    if (SQL_SUCCEEDED(res) && !if_forward_cache(stmt))
    {
      /* reset result position */
      stmt->end_of_set= row_seek(stmt, save_position);
    }

    if (SQL_SUCCEEDED(res)
      && stmt->rows_found_in_set < stmt->ard->array_size)
    {
      if (disconnected)
      {
        return SQL_ERROR;
      }
      else if (stmt->rows_found_in_set == 0)
      {
        stmt->set_error("01S07", "One or more row has error.", 0);
        return SQL_SUCCESS_WITH_INFO; //SQL_NO_DATA_FOUND
      }
    }
  }
  catch(MYERROR &e)
  {
    res = e.retcode;
  }
  return res;
}