s_object *
RS_PostgreSQL_getResult(Con_Handle * conHandle)
{
    S_EVALUATOR RS_DBI_connection * con;
    S_EVALUATOR RS_DBI_resultSet * result;
    PGconn *my_connection;
    Res_Handle *rsHandle;
    Sint res_id;
 
    PGresult *my_result;
   
    con = RS_DBI_getConnection(conHandle);
    my_connection = (PGconn *) con->drvConnection;

    if (con->num_res > 0) {
        res_id = (Sint) con->resultSetIds[0];
        rsHandle = RS_DBI_asResHandle(MGR_ID(conHandle), CON_ID(conHandle), res_id);
        result = RS_DBI_getResultSet(rsHandle);
        if (result->completed == 0) {
            RS_DBI_errorMessage("connection with pending rows, close resultSet before continuing", RS_DBI_ERROR);
        }
        else {
            RS_PostgreSQL_closeResultSet(rsHandle);
        }
    }

    my_result = PQgetResult(my_connection);
    if(my_result == NULL)
       return S_NULL_ENTRY;
    if (strcmp(PQresultErrorMessage(my_result), "") != 0) {
        char *errResultMsg;
        const char *omsg;
        size_t len;
        omsg = PQerrorMessage(my_connection);
        len = strlen(omsg);
        errResultMsg = malloc(len + 80); /* 80 should be larger than the length of "could not ..."*/
        snprintf(errResultMsg, len + 80, "could not Retrieve the result : %s", omsg);
        RS_DBI_errorMessage(errResultMsg, RS_DBI_ERROR);
        free(errResultMsg);

        /*  Frees the storage associated with a PGresult.
         *  void PQclear(PGresult *res);   */

        PQclear(my_result);

    }

    /* we now create the wrapper and copy values */
    PROTECT(rsHandle = RS_DBI_allocResultSet(conHandle));
    result = RS_DBI_getResultSet(rsHandle);
    result->drvResultSet = (void *) my_result;
    result->rowCount = (Sint) 0;
    result->isSelect = 0;
    result->rowsAffected = 0;
    result->completed = 1;
    UNPROTECT(1);
    return rsHandle;
}
Exemple #2
0
SEXP RS_MySQL_resultSetInfo(SEXP rsHandle) {
  RS_DBI_resultSet   *result;
  SEXP output, flds;
  int  n = 6;
  char  *rsDesc[] = {"statement", "isSelect", "rowsAffected",
    "rowCount", "completed", "fieldDescription"};
  SEXPTYPE rsType[]  = {STRSXP, INTSXP, INTSXP,
    INTSXP,   INTSXP, VECSXP};
  int  rsLen[]   = {1, 1, 1, 1, 1, 1};

  result = RS_DBI_getResultSet(rsHandle);
  flds = R_NilValue;

  output = RS_DBI_createNamedList(rsDesc, rsType, rsLen, n);

  SET_LST_CHR_EL(output,0,0,mkChar(result->statement));
  LST_INT_EL(output,1,0) = result->isSelect;
  LST_INT_EL(output,2,0) = result->rowsAffected;
  LST_INT_EL(output,3,0) = result->rowCount;
  LST_INT_EL(output,4,0) = result->completed;
  if(flds != R_NilValue)
    SET_ELEMENT(LST_EL(output, 5), (int) 0, flds);

  return output;
}
Exemple #3
0
SEXP RS_MySQL_nextResultSet(SEXP conHandle) {
  RS_DBI_resultSet  *result;
  SEXP rsHandle;
  int num_fields, is_select;

  RS_DBI_connection* con = RS_DBI_getConnection(conHandle);
  MYSQL* my_connection = con->drvConnection;

  int rc = mysql_next_result(my_connection);
  if (rc < 0) {
    error("no more result sets");
  } else if (rc > 0){
    error("error in getting next result set");
  }

  /* the following comes verbatim from RS_MySQL_exec() */
  MYSQL_RES* my_result = mysql_use_result(my_connection);
  if (!my_result)
    my_result = NULL;

  num_fields = mysql_field_count(my_connection);
  is_select = TRUE;
  if (!my_result) {
    if (num_fields > 0) {
      error("error in getting next result set");
    } else {
      is_select = FALSE;
    }
  }

  /* we now create the wrapper and copy values */
  rsHandle = RS_DBI_allocResultSet(conHandle);
  result = RS_DBI_getResultSet(rsHandle);
  result->statement = RS_DBI_copyString("<UNKNOWN>");
  result->drvResultSet = (void *) my_result;
  result->rowCount = (int) 0;
  result->isSelect = is_select;
  if (!is_select){
    result->rowsAffected = (int) mysql_affected_rows(my_connection);
    result->completed = 1;
  } else {
    result->rowsAffected = (int) -1;
    result->completed = 0;
  }

  if (is_select)
    result->fields = RS_MySQL_createDataMappings(rsHandle);

  return rsHandle;
}
Exemple #4
0
SEXP rmysql_fields_info(SEXP rsHandle) {
  RS_DBI_resultSet* result = RS_DBI_getResultSet(rsHandle);
  RMySQLFields* flds = result->fields;
  int n = flds->num_fields;

  // Allocate output
  SEXP output = PROTECT(allocVector(VECSXP, 4));
  SEXP output_nms = PROTECT(allocVector(STRSXP, 4));
  SET_NAMES(output, output_nms);
  UNPROTECT(1);

  SET_STRING_ELT(output_nms, 0, mkChar("name"));
  SEXP names = PROTECT(allocVector(STRSXP, n));
  for (int j = 0; j < n; j++) {
    SET_STRING_ELT(names, j, mkChar(flds->name[j]));
  }
  SET_VECTOR_ELT(output, 0, names);
  UNPROTECT(1);

  SET_STRING_ELT(output_nms, 1, mkChar("Sclass"));
  SEXP sclass = PROTECT(allocVector(STRSXP, n));
  for (int j = 0; j < n; j++) {
    const char* type = type2char(flds->Sclass[j]);
    SET_STRING_ELT(sclass, j, mkChar(type));
  }
  SET_VECTOR_ELT(output, 1, sclass);
  UNPROTECT(1);

  SET_STRING_ELT(output_nms, 2, mkChar("type"));
  SEXP types = PROTECT(allocVector(STRSXP, n));
  for (int j = 0; j < n; j++) {
    char* type = rmysql_type(flds->type[j]);
    SET_STRING_ELT(types, j, mkChar(type));
  }
  SET_VECTOR_ELT(output, 2, types);
  UNPROTECT(1);

  SET_STRING_ELT(output_nms, 3, mkChar("length"));
  SEXP lens = PROTECT(allocVector(INTSXP, n));
  for (int j = 0; j < n; j++) {
    INTEGER(lens)[j] = flds->length[j];
  }
  SET_VECTOR_ELT(output, 3, lens);
  UNPROTECT(1);

  UNPROTECT(1);
  return output;
}
Exemple #5
0
void RS_DBI_freeResultSet(SEXP rsHandle) {
  RS_DBI_connection* con = RS_DBI_getConnection(rsHandle);
  RS_DBI_resultSet* result = RS_DBI_getResultSet(rsHandle);

  if(result->drvResultSet) {
    error("internal error in RS_DBI_freeResultSet: non-freed result->drvResultSet (some memory leaked)");
  }

  if (result->statement)
    free(result->statement);
  if (result->fields)
    rmysql_fields_free(result->fields);
  free(result);
  result = NULL;

  /* update connection's resultSet table */
  int indx = RS_DBI_lookup(con->resultSetIds, con->length, RES_ID(rsHandle));
  RS_DBI_freeEntry(con->resultSetIds, indx);
  con->resultSets[indx] = NULL;
  con->num_res -= 1;
}
Exemple #6
0
SEXP RS_MySQL_closeResultSet(SEXP resHandle) {
  RS_DBI_resultSet *result;
  MYSQL_RES        *my_result;

  result = RS_DBI_getResultSet(resHandle);

  my_result = (MYSQL_RES *) result->drvResultSet;
  if(my_result){
    // we need to flush any possibly remaining rows (see Manual Ch 20 p358)
    MYSQL_ROW row;
    while((row = mysql_fetch_row(result->drvResultSet)))
      ;
  }
  mysql_free_result(my_result);

  // need to NULL drvResultSet, otherwise can't free the rsHandle
  result->drvResultSet = (void *) NULL;
  RS_DBI_freeResultSet(resHandle);

  return ScalarLogical(TRUE);
}
Exemple #7
0
RS_DBI_fields *
RS_PostgreSQL_createDataMappings(Res_Handle * rsHandle)
{
    PGresult *my_result;

    RS_DBI_connection *con;
    RS_DBI_resultSet *result;
    RS_DBI_fields *flds;

    int j, num_fields, internal_type;
    char errMsg[128];

    result = RS_DBI_getResultSet(rsHandle);
    my_result = (PGresult *) result->drvResultSet;

    con = RS_DBI_getConnection(rsHandle);
    num_fields = PQnfields(my_result);

    flds = RS_DBI_allocFields(num_fields); /* this returns malloced data (not from R) */

    char buff[1000];            /* Buffer to hold the sql query to check whether the given column is nullable */
    PGconn *conn;
    PGresult *res;
    conn = (PGconn *) con->drvConnection;

    for (j = 0; j < num_fields; j++) {

        flds->name[j] = RS_DBI_copyString(PQfname(my_result, j));

        flds->type[j] = (int) PQftype(my_result, j);

        flds->length[j] = (Sint) PQfsize(my_result, j);

        /* NOTE: PQfmod is -1 incase of no information */
        flds->precision[j] = (Sint) PQfmod(my_result, j);

        flds->scale[j] = (Sint) - 1;

        /* PQftablecol returns the column number (within its table) of
         * the column making up the specified query result column.Zero
         * is returned if the column number is out of range, or if the
         * specified column is not a simple reference to a table
         * column, or when using pre-3.0 protocol. So
         * "if(PQftablecol(my_result,j) !=0)" checks whether the
         * particular colomn in the result set is column of table or
         * not. Or else there is no meaning in checking whether a
         * column is nullable or not if it does not belong to the
         * table.
         */

        flds->nullOk[j] = (Sint) INT_MIN; /* This should translate to NA in R */

        if (PQftablecol(my_result, j) != 0) {
            /* Code to find whether a row can be nullable or not */
            /* we might better just store the table id and column number
               for lazy evaluation at dbColumnInfo call*/
            /* although the database structure can change, we are not in transaction anyway
               and there is no guarantee in current code */
            snprintf(buff, 1000, "select attnotnull from pg_attribute where attrelid=%d and attnum='%d'",
                     PQftable(my_result, j), PQftablecol(my_result, j));
            res = PQexec(conn, buff);

            if (res && (PQntuples(res) > 0)) {
                const char * attnotnull = PQgetvalue(res, 0, 0);
                if(strcmp(attnotnull, "f") == 0) {
                    flds->nullOk[j] = (Sint) 1; /* nollOK is TRUE when attnotnull is f*/
                }
                if(strcmp(attnotnull, "t") == 0) {
                    flds->nullOk[j] = (Sint) 0; /* nollOK is FALSE when attnotnull is t*/
                }
            }
            PQclear(res);
        }

        internal_type = (int) PQftype(my_result, j);

        switch (internal_type) {
        case BOOLOID:
            flds->Sclass[j] = LOGICAL_TYPE;
            break;
        case BPCHAROID:
            flds->Sclass[j] = CHARACTER_TYPE;
            flds->isVarLength[j] = (Sint) 0;
            break;
        case VARCHAROID:
        case TEXTOID:
        case BYTEAOID:
        case NAMEOID:
        case MACADDROID:
        case INETOID:
            flds->Sclass[j] = CHARACTER_TYPE;
            flds->isVarLength[j] = (Sint) 1;
            break;
        case INT2OID:
        case INT4OID:
        case OIDOID:
            flds->Sclass[j] = INTEGER_TYPE;
            break;
        case INT8OID:
            if (sizeof(Sint) >= 8) {
                flds->Sclass[j] = INTEGER_TYPE;
            }
            else {
                flds->Sclass[j] = NUMERIC_TYPE;
            }
            break;
        case NUMERICOID:
        case FLOAT8OID:
        case FLOAT4OID:
            flds->Sclass[j] = NUMERIC_TYPE;
            break;
        case DATEOID:
        case TIMEOID:
        case TIMETZOID:
        case TIMESTAMPOID:
        case TIMESTAMPTZOID:
        case INTERVALOID:
            flds->Sclass[j] = CHARACTER_TYPE;
            /*flds->isVarLength[j] = (Sint) 1; */
            break;
        default:
            flds->Sclass[j] = CHARACTER_TYPE;
            flds->isVarLength[j] = (Sint) 1;
            snprintf(buff, 1000, "select typname, typcategory from pg_type where oid = %d", internal_type);
            res = PQexec(conn, buff);
            if (res) {
                char * typename;
                char * typecat;
                int ntuples;
                ntuples = PQntuples(res);
                if(ntuples == 1) {
                    typename = PQgetvalue(res, 0, 0);
                    typecat = PQgetvalue(res, 0, 1);
                    if(*typecat == 'E') { /* This is enum, ok */
                    } else if(*typecat == 'A') { /*This is array, ok */
                    } else {
                        snprintf(errMsg, 128, "unrecognized PostgreSQL field type %s (id:%d) in column %d", typename, internal_type, j);
                        RS_DBI_errorMessage(errMsg, RS_DBI_WARNING);
                    }
                } else {
                    snprintf(errMsg, 128, "oid: %d, ntuples: %d", internal_type, ntuples);
                    RS_DBI_errorMessage(errMsg, RS_DBI_WARNING);
                }
                PQclear(res);
            } else {
Exemple #8
0
Res_Handle *
RS_PostgreSQL_exec(Con_Handle * conHandle, s_object * statement)
{
    S_EVALUATOR RS_DBI_connection * con;
    Res_Handle *rsHandle;
    RS_DBI_resultSet *result;
    PGconn *my_connection;
    PGresult *my_result;

    Sint res_id, is_select=0;
    char *dyn_statement;

    con = RS_DBI_getConnection(conHandle);
    my_connection = (PGconn *) con->drvConnection;
    dyn_statement = RS_DBI_copyString(CHR_EL(statement, 0));

    /* Do we have a pending resultSet in the current connection?
     * PostgreSQL only allows  one resultSet per connection.
     */
    if (con->num_res > 0) {
        res_id = (Sint) con->resultSetIds[0];   /* recall, PostgreSQL has only 1 res */
        rsHandle = RS_DBI_asResHandle(MGR_ID(conHandle), CON_ID(conHandle), res_id);
        result = RS_DBI_getResultSet(rsHandle);
        if (result->completed == 0) {
            free(dyn_statement);
            RS_DBI_errorMessage("connection with pending rows, close resultSet before continuing", RS_DBI_ERROR);
        }
        else {
            RS_PostgreSQL_closeResultSet(rsHandle);
        }
    }

    /* Here is where we actually run the query */

    /* Example: PGresult *PQexec(PGconn *conn, const char *command); */

    my_result = PQexec(my_connection, dyn_statement);
    if (my_result == NULL) {
        char *errMsg;
        const char *omsg;
        size_t len;
        omsg = PQerrorMessage(my_connection);
        len = strlen(omsg);
        free(dyn_statement);
        errMsg = R_alloc(len + 80, 1); /* 80 should be larger than the length of "could not ..."*/
        snprintf(errMsg, len + 80,  "could not run statement: %s", omsg);
        RS_DBI_errorMessage(errMsg, RS_DBI_ERROR);
    }


    /* ExecStatusType PQresultStatus(const PGresult *res); */

    if (PQresultStatus(my_result) == PGRES_TUPLES_OK) {
        is_select = (Sint) TRUE;
    }
    if (PQresultStatus(my_result) == PGRES_COMMAND_OK) {
        is_select = (Sint) FALSE;
    }

    /* char *PQresultErrorMessage(const PGresult *res); */

    if (strcmp(PQresultErrorMessage(my_result), "") != 0) {
        char *errResultMsg;
        const char *omsg;
        size_t len;
        omsg = PQerrorMessage(my_connection);
        len = strlen(omsg);
        errResultMsg = R_alloc(len + 80, 1); /* 80 should be larger than the length of "could not ..."*/
        snprintf(errResultMsg, len + 80, "could not Retrieve the result : %s", omsg);
        /*  Frees the storage associated with a PGresult.
         *  void PQclear(PGresult *res);   */
        PQclear(my_result);
        free(dyn_statement);
        RS_DBI_errorMessage(errResultMsg, RS_DBI_ERROR);
    }

    /* we now create the wrapper and copy values */
    PROTECT(rsHandle = RS_DBI_allocResultSet(conHandle));
    result = RS_DBI_getResultSet(rsHandle);
    result->statement = RS_DBI_copyString(dyn_statement);
    result->drvResultSet = (void *) my_result;
    result->rowCount = (Sint) 0;
    result->isSelect = is_select;

    /*  Returns the number of rows affected by the SQL command.
     *  char *PQcmdTuples(PGresult *res);
     */

    if (!is_select) {
        result->rowsAffected = (Sint) atoi(PQcmdTuples(my_result));
        result->completed = 1;
    }
    else {
        result->rowsAffected = (Sint) - 1;
        result->completed = 0;
    }

    if (is_select) {
        result->fields = RS_PostgreSQL_createDataMappings(rsHandle);
    }
    free(dyn_statement);
    UNPROTECT(1);
    return rsHandle;
}
Exemple #9
0
RMySQLFields* RS_MySQL_createDataMappings(SEXP rsHandle) {
  // Fetch MySQL field descriptions
  RS_DBI_resultSet* result = RS_DBI_getResultSet(rsHandle);
  MYSQL_RES* my_result = result->drvResultSet;
  MYSQL_FIELD* select_dp = mysql_fetch_fields(my_result);
  int num_fields = mysql_num_fields(my_result);

  // Allocate memory for output object
  RMySQLFields* flds = malloc(sizeof(RMySQLFields));
  if (!flds) {
    error("Could not allocate memory for database fields");
  }

  flds->num_fields =  num_fields;
  flds->name =        calloc(num_fields, sizeof(char *));
  flds->type =        calloc(num_fields, sizeof(int));
  flds->length =      calloc(num_fields, sizeof(int));
  flds->precision =   calloc(num_fields, sizeof(int));
  flds->scale =       calloc(num_fields, sizeof(int));
  flds->nullOk =      calloc(num_fields, sizeof(int));
  flds->isVarLength = calloc(num_fields, sizeof(int));
  flds->Sclass =      calloc(num_fields, sizeof(SEXPTYPE));

  /* WARNING: TEXT fields are represented as BLOBS (sic),
   * not VARCHAR or some kind of string type. More troublesome is the
   * fact that a TEXT fields can be BINARY to indicate case-sensitivity.
   * The bottom line is that MySQL has a serious deficiency re: text
   * types (IMHO).  A binary object (in SQL92, X/SQL at least) can
   * store all kinds of non-ASCII, non-printable stuff that can
   * potentially screw up S and R CHARACTER_TYPE.  We are on thin ice.
   *
   * I'm aware that I'm introducing a potential bug here by following
   * the MySQL convention of treating BLOB's as TEXT (I'm symplifying
   * in order to properly handle commonly-found TEXT fields, at the
   * risk of core dumping when bona fide Binary objects are being
   * retrieved.
   *
   * Possible workaround: if strlen() of the field equals the
   *    MYSQL_FIELD->length for all rows, then we are probably(?) safe
   * in considering TEXT a character type (non-binary).
   */
  for (int j = 0; j < num_fields; j++){
    /* First, save the name, MySQL internal field name, type, length, etc. */
    flds->name[j] = RS_DBI_copyString(select_dp[j].name);
    flds->type[j] = select_dp[j].type;  /* recall that these are enum*/
    flds->length[j] = select_dp[j].length;
    flds->precision[j] = select_dp[j].length;
    flds->scale[j] = select_dp[j].decimals;
    flds->nullOk[j] = (!IS_NOT_NULL(select_dp[j].flags));

    int internal_type = select_dp[j].type;
    switch(internal_type) {
      case FIELD_TYPE_VAR_STRING:
      case FIELD_TYPE_STRING:
        flds->Sclass[j] = STRSXP;
        flds->isVarLength[j] = (int) 1;
        break;
      case FIELD_TYPE_TINY:            /* 1-byte TINYINT   */
      case FIELD_TYPE_SHORT:           /* 2-byte SMALLINT  */
      case FIELD_TYPE_INT24:           /* 3-byte MEDIUMINT */
        flds->Sclass[j] = INTSXP;
      case FIELD_TYPE_LONG:            /* 4-byte INTEGER   */
        /* if unsigned, turn into numeric (may be too large for ints/long)*/
        if(select_dp[j].flags & UNSIGNED_FLAG) {
          warning("Unsigned INTEGER in col %d imported as numeric", j);
          flds->Sclass[j] = REALSXP;
        } else {
          flds->Sclass[j] = INTSXP;
        }
        break;
      case FIELD_TYPE_LONGLONG:       /* 8-byte BIGINT   */
        flds->Sclass[j] = REALSXP;
        break;

  #if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 50003 /* 5.0.3 */
      case FIELD_TYPE_BIT:
        if(flds->precision[j] <= sizeof(int)) {
          /* can R int hold the bytes? */
          flds->Sclass[j] = INTSXP;
        } else {
          flds->Sclass[j] = STRSXP;
          warning(
            "BIT field in column %d too long (%d bits) for an R integer (imported as character)",
            j+1, flds->precision[j]
          );
        }
        break;
  #endif
        flds->Sclass[j] = REALSXP;
        break;
      case FIELD_TYPE_DECIMAL:
  #if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 50003 /* 5.0.3 */
      case FIELD_TYPE_NEWDECIMAL:
  #endif
        warning("Decimal MySQL column %d imported as numeric", j);
        flds->Sclass[j] = REALSXP;
        break;
      case FIELD_TYPE_FLOAT:
      case FIELD_TYPE_DOUBLE:
        flds->Sclass[j] = REALSXP;
        break;
      case FIELD_TYPE_BLOB:         /* TODO: how should we bring large ones*/
      case FIELD_TYPE_TINY_BLOB:
      case FIELD_TYPE_MEDIUM_BLOB:
      case FIELD_TYPE_LONG_BLOB:
        flds->Sclass[j] = STRSXP;   /* Grr! Hate this! */
        flds->isVarLength[j] = (int) 1;
        break;
      case FIELD_TYPE_DATE:
      case FIELD_TYPE_TIME:
      case FIELD_TYPE_DATETIME:
      case FIELD_TYPE_YEAR:
      case FIELD_TYPE_NEWDATE:
        flds->Sclass[j] = STRSXP;
        flds->isVarLength[j] = (int) 1;
        break;
      case FIELD_TYPE_ENUM:
        flds->Sclass[j] = STRSXP;   /* see the MySQL ref. manual */
        flds->isVarLength[j] = (int) 1;
        break;
      case FIELD_TYPE_SET:
        flds->Sclass[j] = STRSXP;
        flds->isVarLength[j] = (int) 0;
        break;
      default:
        flds->Sclass[j] = STRSXP;
        flds->isVarLength[j] = (int) 1;
        warning("unrecognized MySQL field type %d in column %d imported as character",
          internal_type, j);
        break;
    }
  }
  return flds;
}
Exemple #10
0
// output is a named list
SEXP RS_MySQL_fetch(SEXP rsHandle, SEXP max_rec) {
  MySQLDriver   *mgr;
  RS_DBI_resultSet *result;
  RMySQLFields* flds;
  MYSQL_RES *my_result;
  MYSQL_ROW  row;
  SEXP output, s_tmp;

  unsigned long  *lens;
  int    i, j, null_item, expand;
  int   completed;
  SEXPTYPE  *fld_Sclass;
  int   num_rec;
  int    num_fields;

  result = RS_DBI_getResultSet(rsHandle);
  flds = result->fields;
  if(!flds)
    error("corrupt resultSet, missing fieldDescription");
  num_rec = asInteger(max_rec);
  expand = (num_rec < 0);   // dyn expand output to accommodate all rows
  if(expand || num_rec == 0){
    mgr = rmysql_driver();
    num_rec = mgr->fetch_default_rec;
  }
  num_fields = flds->num_fields;
  PROTECT(output = NEW_LIST((int) num_fields));
  RS_DBI_allocOutput(output, flds, num_rec, 0);
  fld_Sclass = flds->Sclass;

  // actual fetching....
  my_result = (MYSQL_RES *) result->drvResultSet;
  completed = (int) 0;

  for(i = 0; ; i++){
    if(i==num_rec){  // exhausted the allocated space

      if(expand){    // do we extend or return the records fetched so far
        num_rec = 2 * num_rec;
        RS_DBI_allocOutput(output, flds, num_rec, expand);
      }
      else
        break;       // okay, no more fetching for now
    }
    row = mysql_fetch_row(my_result);
    if(row==NULL){    // either we finish or we encounter an error
      unsigned int  err_no;
      RS_DBI_connection   *con;
      con = RS_DBI_getConnection(rsHandle);
      err_no = mysql_errno((MYSQL *) con->drvConnection);
      completed = (int) (err_no ? -1 : 1);
      break;
    }
    lens = mysql_fetch_lengths(my_result);

    for(j = 0; j < num_fields; j++){

      null_item = (row[j] == NULL);

      switch((int)fld_Sclass[j]){

      case INTSXP:
        if(null_item)
          NA_SET(&(LST_INT_EL(output,j,i)), INTSXP);
        else
          LST_INT_EL(output,j,i) = (int) atol(row[j]);
        break;

      case STRSXP:
        // BUG: I need to verify that a TEXT field (which is stored as
        // a BLOB by MySQL!) is indeed char and not a true
        // Binary obj (MySQL does not truly distinguish them). This
        // test is very gross.
        if(null_item)
          SET_LST_CHR_EL(output,j,i,NA_STRING);
        else {
          if((size_t) lens[j] != strlen(row[j])){
            warning("internal error: row %d field %d truncated", i, j);
          }
          SET_LST_CHR_EL(output,j,i,mkChar(row[j]));
        }
        break;

      case REALSXP:
        if(null_item)
          NA_SET(&(LST_NUM_EL(output,j,i)), REALSXP);
        else
          LST_NUM_EL(output,j,i) = (double) atof(row[j]);
        break;

      default:  // error, but we'll try the field as character (!)
        if(null_item)
          SET_LST_CHR_EL(output,j,i, NA_STRING);
        else {
          warning("unrecognized field type %d in column %d", fld_Sclass[j], j);
          SET_LST_CHR_EL(output,j,i,mkChar(row[j]));
        }
        break;
      }
    }
  }

  // actual number of records fetched
  if(i < num_rec){
    num_rec = i;
    // adjust the length of each of the members in the output_list
    for(j = 0; j<num_fields; j++){
      s_tmp = LST_EL(output,j);
      PROTECT(SET_LENGTH(s_tmp, num_rec));
      SET_ELEMENT(output, j, s_tmp);
      UNPROTECT(1);
    }
  }
  if(completed < 0)
    warning("error while fetching rows");

  result->rowCount += num_rec;
  result->completed = (int) completed;

  UNPROTECT(1);
  return output;
}
Exemple #11
0
/* Execute (currently) one sql statement (INSERT, DELETE, SELECT, etc.),
* set coercion type mappings between the server internal data types and
* S classes.   Returns  an S handle to a resultSet object.
*/
SEXP RS_MySQL_exec(SEXP conHandle, SEXP statement) {
  RS_DBI_connection *con;
  SEXP rsHandle;
  RS_DBI_resultSet  *result;
  MYSQL             *my_connection;
  MYSQL_RES         *my_result;
  int      num_fields, state;
  int     res_id, is_select;
  char     *dyn_statement;

  con = RS_DBI_getConnection(conHandle);
  my_connection = (MYSQL *) con->drvConnection;
  dyn_statement = RS_DBI_copyString(CHR_EL(statement,0));

  /* Do we have a pending resultSet in the current connection?
   * MySQL only allows  one resultSet per connection.
   */
  if(con->num_res>0){
    res_id = (int) con->resultSetIds[0]; /* recall, MySQL has only 1 res */
  rsHandle = RS_DBI_asResHandle(MGR_ID(conHandle),
    CON_ID(conHandle), res_id);
  result = RS_DBI_getResultSet(rsHandle);
  if(result->completed == 0){
    free(dyn_statement);
    error("connection with pending rows, close resultSet before continuing");
  }
  else
    RS_MySQL_closeResultSet(rsHandle);
  }

  /* Here is where we actually run the query */
  state = mysql_query(my_connection, dyn_statement);
  if(state) {
    error("could not run statement: %s", mysql_error(my_connection));
  }

  /* Do we need output column/field descriptors?  Only for SELECT-like
   * statements. The MySQL reference manual suggests invoking
   * mysql_use_result() and if it succeed the statement is SELECT-like
   * that can use a resultSet.  Otherwise call mysql_field_count()
   * and if it returns zero, the sql was not a SELECT-like statement.
   * Finally a non-zero means a failed SELECT-like statement.
   */
  my_result = mysql_use_result(my_connection);
  if(!my_result)
    my_result = (MYSQL_RES *) NULL;

  num_fields = (int) mysql_field_count(my_connection);
  is_select = (int) TRUE;
  if(!my_result){
    if(num_fields>0){
      free(dyn_statement);
      error("error in select/select-like");
    }
    else
      is_select = FALSE;
  }

  /* we now create the wrapper and copy values */
  rsHandle = RS_DBI_allocResultSet(conHandle);
  result = RS_DBI_getResultSet(rsHandle);
  result->statement = RS_DBI_copyString(dyn_statement);
  result->drvResultSet = (void *) my_result;
  result->rowCount = (int) 0;
  result->isSelect = is_select;
  if(!is_select){
    result->rowsAffected = (int) mysql_affected_rows(my_connection);
    result->completed = 1;
  }
  else {
    result->rowsAffected = (int) -1;
    result->completed = 0;
  }

  if(is_select)
    result->fields = RS_MySQL_createDataMappings(rsHandle);

  free(dyn_statement);
  return rsHandle;
}