mirror of
https://github.com/postgres/postgres.git
synced 2025-11-25 12:03:53 +03:00
Replace old PostODBC driver with new one...
This one is based on an older PostODBC driver, rewritten and maintained by InsightDist(?)
This commit is contained in:
694
src/interfaces/odbc/results.c
Normal file
694
src/interfaces/odbc/results.c
Normal file
@@ -0,0 +1,694 @@
|
||||
|
||||
/* Module: results.c
|
||||
*
|
||||
* Description: This module contains functions related to
|
||||
* retrieving result information through the ODBC API.
|
||||
*
|
||||
* Classes: n/a
|
||||
*
|
||||
* API functions: SQLRowCount, SQLNumResultCols, SQLDescribeCol, SQLColAttributes,
|
||||
* SQLGetData, SQLFetch, SQLExtendedFetch,
|
||||
* SQLMoreResults(NI), SQLSetPos(NI), SQLSetScrollOptions(NI),
|
||||
* SQLSetCursorName(NI), SQLGetCursorName(NI)
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "psqlodbc.h"
|
||||
#include "environ.h"
|
||||
#include "connection.h"
|
||||
#include "statement.h"
|
||||
#include "bind.h"
|
||||
#include "qresult.h"
|
||||
#include "convert.h"
|
||||
#include "pgtypes.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#include <sqlext.h>
|
||||
|
||||
|
||||
RETCODE SQL_API SQLRowCount(
|
||||
HSTMT hstmt,
|
||||
SDWORD FAR *pcrow)
|
||||
{
|
||||
StatementClass *stmt = (StatementClass *) hstmt;
|
||||
QResultClass *res;
|
||||
char *msg, *ptr;
|
||||
|
||||
if ( ! stmt)
|
||||
return SQL_ERROR;
|
||||
|
||||
if(stmt->statement_type == STMT_TYPE_SELECT) {
|
||||
if (stmt->status == STMT_FINISHED) {
|
||||
res = SC_get_Result(stmt);
|
||||
|
||||
if(res && pcrow) {
|
||||
*pcrow = QR_get_num_tuples(res);
|
||||
return SQL_SUCCESS;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
res = SC_get_Result(stmt);
|
||||
if (res && pcrow) {
|
||||
msg = QR_get_command(res);
|
||||
mylog("*** msg = '%s'\n", msg);
|
||||
trim(msg); // get rid of trailing spaces
|
||||
ptr = strrchr(msg, ' ');
|
||||
if (ptr) {
|
||||
*pcrow = atoi(ptr+1);
|
||||
mylog("**** SQLRowCount(): THE ROWS: *pcrow = %d\n", *pcrow);
|
||||
}
|
||||
else {
|
||||
*pcrow = -1;
|
||||
|
||||
mylog("**** SQLRowCount(): NO ROWS: *pcrow = %d\n", *pcrow);
|
||||
}
|
||||
|
||||
return SQL_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
// This returns the number of columns associated with the database
|
||||
// attached to "hstmt".
|
||||
|
||||
|
||||
RETCODE SQL_API SQLNumResultCols(
|
||||
HSTMT hstmt,
|
||||
SWORD FAR *pccol)
|
||||
{
|
||||
StatementClass *stmt = (StatementClass *) hstmt;
|
||||
QResultClass *result;
|
||||
|
||||
if ( ! stmt)
|
||||
return SQL_INVALID_HANDLE;
|
||||
|
||||
SC_clear_error(stmt);
|
||||
|
||||
/* CC: Now check for the "prepared, but not executed" situation, that enables us to
|
||||
deal with "SQLPrepare -- SQLDescribeCol -- ... -- SQLExecute" situations.
|
||||
(AutoCAD 13 ASE/ASI just _loves_ that ;-) )
|
||||
*/
|
||||
mylog("**** SQLNumResultCols: calling SC_pre_execute\n");
|
||||
|
||||
SC_pre_execute(stmt);
|
||||
|
||||
result = SC_get_Result(stmt);
|
||||
|
||||
mylog("SQLNumResultCols: result = %u, status = %d, numcols = %d\n", result, stmt->status, result != NULL ? QR_NumResultCols(result) : -1);
|
||||
if (( ! result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE)) ) {
|
||||
/* no query has been executed on this statement */
|
||||
stmt->errornumber = STMT_SEQUENCE_ERROR;
|
||||
stmt->errormsg = "No query has been executed with that handle";
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
*pccol = QR_NumResultCols(result);
|
||||
|
||||
return SQL_SUCCESS;
|
||||
}
|
||||
|
||||
// - - - - - - - - -
|
||||
|
||||
// Return information about the database column the user wants
|
||||
// information about.
|
||||
/* CC: preliminary implementation */
|
||||
RETCODE SQL_API SQLDescribeCol(
|
||||
HSTMT hstmt,
|
||||
UWORD icol,
|
||||
UCHAR FAR *szColName,
|
||||
SWORD cbColNameMax,
|
||||
SWORD FAR *pcbColName,
|
||||
SWORD FAR *pfSqlType,
|
||||
UDWORD FAR *pcbColDef,
|
||||
SWORD FAR *pibScale,
|
||||
SWORD FAR *pfNullable)
|
||||
{
|
||||
/* gets all the information about a specific column */
|
||||
StatementClass *stmt = (StatementClass *) hstmt;
|
||||
QResultClass *result;
|
||||
char *name;
|
||||
Int4 fieldtype;
|
||||
|
||||
if ( ! stmt)
|
||||
return SQL_INVALID_HANDLE;
|
||||
|
||||
SC_clear_error(stmt);
|
||||
|
||||
/* CC: Now check for the "prepared, but not executed" situation, that enables us to
|
||||
deal with "SQLPrepare -- SQLDescribeCol -- ... -- SQLExecute" situations.
|
||||
(AutoCAD 13 ASE/ASI just _loves_ that ;-) )
|
||||
*/
|
||||
|
||||
SC_pre_execute(stmt);
|
||||
|
||||
|
||||
result = SC_get_Result(stmt);
|
||||
mylog("**** SQLDescribeCol: result = %u, stmt->status = %d, !finished=%d, !premature=%d\n", result, stmt->status, stmt->status != STMT_FINISHED, stmt->status != STMT_PREMATURE);
|
||||
if ( (NULL == result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE))) {
|
||||
/* no query has been executed on this statement */
|
||||
stmt->errornumber = STMT_SEQUENCE_ERROR;
|
||||
stmt->errormsg = "No query has been assigned to this statement.";
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
if (cbColNameMax >= 1) {
|
||||
name = QR_get_fieldname(result, (Int2) (icol-1));
|
||||
mylog("describeCol: col %d fieldname = '%s'\n", icol - 1, name);
|
||||
/* our indices start from 0 whereas ODBC defines indices starting from 1 */
|
||||
if (NULL != pcbColName) {
|
||||
// we want to get the total number of bytes in the column name
|
||||
if (NULL == name)
|
||||
*pcbColName = 0;
|
||||
else
|
||||
*pcbColName = strlen(name);
|
||||
}
|
||||
if (NULL != szColName) {
|
||||
// get the column name into the buffer if there is one
|
||||
if (NULL == name)
|
||||
szColName[0] = '\0';
|
||||
else
|
||||
strncpy_null(szColName, name, cbColNameMax);
|
||||
}
|
||||
}
|
||||
|
||||
fieldtype = QR_get_field_type(result, (Int2) (icol-1));
|
||||
mylog("describeCol: col %d fieldtype = %d\n", icol - 1, fieldtype);
|
||||
|
||||
if (NULL != pfSqlType) {
|
||||
*pfSqlType = pgtype_to_sqltype(fieldtype);
|
||||
if (*pfSqlType == PG_UNKNOWN)
|
||||
*pfSqlType = SQL_CHAR;
|
||||
}
|
||||
|
||||
if (NULL != pcbColDef)
|
||||
*pcbColDef = pgtype_precision(fieldtype);
|
||||
|
||||
if (NULL != pibScale) {
|
||||
Int2 scale;
|
||||
scale = pgtype_scale(fieldtype);
|
||||
if(scale == -1) { scale = 0; }
|
||||
|
||||
*pibScale = scale;
|
||||
}
|
||||
if (NULL != pfNullable) {
|
||||
*pfNullable = pgtype_nullable(fieldtype);
|
||||
}
|
||||
|
||||
return SQL_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns result column descriptor information for a result set.
|
||||
|
||||
RETCODE SQL_API SQLColAttributes(
|
||||
HSTMT hstmt,
|
||||
UWORD icol,
|
||||
UWORD fDescType,
|
||||
PTR rgbDesc,
|
||||
SWORD cbDescMax,
|
||||
SWORD FAR *pcbDesc,
|
||||
SDWORD FAR *pfDesc)
|
||||
{
|
||||
StatementClass *stmt = (StatementClass *) hstmt;
|
||||
char *value;
|
||||
Int4 field_type;
|
||||
|
||||
if( ! stmt) {
|
||||
return SQL_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
/* CC: Now check for the "prepared, but not executed" situation, that enables us to
|
||||
deal with "SQLPrepare -- SQLDescribeCol -- ... -- SQLExecute" situations.
|
||||
(AutoCAD 13 ASE/ASI just _loves_ that ;-) )
|
||||
*/
|
||||
SC_pre_execute(stmt);
|
||||
|
||||
mylog("**** SQLColAtt: result = %u, status = %d, numcols = %d\n", stmt->result, stmt->status, stmt->result != NULL ? QR_NumResultCols(stmt->result) : -1);
|
||||
|
||||
if ( (NULL == stmt->result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE)) ) {
|
||||
stmt->errormsg = "Can't get column attributes: no result found.";
|
||||
stmt->errornumber = STMT_SEQUENCE_ERROR;
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
if(icol < 1) {
|
||||
// we do not support bookmarks
|
||||
stmt->errormsg = "Bookmarks are not currently supported.";
|
||||
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
icol -= 1;
|
||||
field_type = QR_get_field_type(stmt->result, icol);
|
||||
mylog("colAttr: col %d field_type = %d\n", icol, field_type);
|
||||
switch(fDescType) {
|
||||
case SQL_COLUMN_AUTO_INCREMENT:
|
||||
if (NULL != pfDesc) {
|
||||
*pfDesc = pgtype_auto_increment(field_type);
|
||||
|
||||
if(*pfDesc == -1) { /* "not applicable" becomes false */
|
||||
*pfDesc = FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SQL_COLUMN_CASE_SENSITIVE:
|
||||
if (NULL != pfDesc)
|
||||
*pfDesc = pgtype_case_sensitive(field_type);
|
||||
break;
|
||||
case SQL_COLUMN_COUNT:
|
||||
if (NULL != pfDesc)
|
||||
*pfDesc = QR_NumResultCols(stmt->result);
|
||||
break;
|
||||
case SQL_COLUMN_DISPLAY_SIZE:
|
||||
if (NULL != pfDesc)
|
||||
*pfDesc = pgtype_precision(field_type);
|
||||
|
||||
mylog("colAttr: col %d fieldsize = %d\n", icol, *pfDesc);
|
||||
|
||||
break;
|
||||
case SQL_COLUMN_LABEL:
|
||||
case SQL_COLUMN_NAME:
|
||||
value = QR_get_fieldname(stmt->result, icol);
|
||||
strncpy_null((char *)rgbDesc, value, cbDescMax);
|
||||
/* CC: Check for Nullpointesr */
|
||||
if (NULL != pcbDesc)
|
||||
*pcbDesc = strlen(value);
|
||||
break;
|
||||
case SQL_COLUMN_LENGTH:
|
||||
if (NULL != pfDesc)
|
||||
*pfDesc = pgtype_precision(field_type);
|
||||
return SQL_SUCCESS;
|
||||
break;
|
||||
case SQL_COLUMN_MONEY:
|
||||
if (NULL != pfDesc)
|
||||
*pfDesc = pgtype_money(field_type);
|
||||
break;
|
||||
case SQL_COLUMN_NULLABLE:
|
||||
if (NULL != pfDesc)
|
||||
*pfDesc = pgtype_nullable(field_type);
|
||||
break;
|
||||
case SQL_COLUMN_OWNER_NAME:
|
||||
return SQL_ERROR;
|
||||
break;
|
||||
case SQL_COLUMN_PRECISION:
|
||||
if (NULL != pfDesc)
|
||||
*pfDesc = pgtype_precision(field_type);
|
||||
break;
|
||||
case SQL_COLUMN_QUALIFIER_NAME:
|
||||
strncpy_null((char *)rgbDesc, "", cbDescMax);
|
||||
if (NULL != pfDesc)
|
||||
*pcbDesc = 1;
|
||||
break;
|
||||
case SQL_COLUMN_SCALE:
|
||||
if (NULL != pfDesc)
|
||||
*pfDesc = pgtype_scale(field_type);
|
||||
break;
|
||||
case SQL_COLUMN_SEARCHABLE:
|
||||
if (NULL != pfDesc)
|
||||
*pfDesc = pgtype_searchable(field_type);
|
||||
break;
|
||||
case SQL_COLUMN_TABLE_NAME:
|
||||
return SQL_ERROR;
|
||||
break;
|
||||
case SQL_COLUMN_TYPE:
|
||||
if (NULL != pfDesc) {
|
||||
*pfDesc = pgtype_to_sqltype(field_type);
|
||||
if (*pfDesc == PG_UNKNOWN)
|
||||
*pfDesc = SQL_CHAR;
|
||||
}
|
||||
break;
|
||||
case SQL_COLUMN_TYPE_NAME:
|
||||
value = pgtype_to_name(field_type);
|
||||
strncpy_null((char *)rgbDesc, value, cbDescMax);
|
||||
if (NULL != pcbDesc)
|
||||
*pcbDesc = strlen(value);
|
||||
break;
|
||||
case SQL_COLUMN_UNSIGNED:
|
||||
if (NULL != pfDesc) {
|
||||
*pfDesc = pgtype_unsigned(field_type);
|
||||
if(*pfDesc == -1) {
|
||||
*pfDesc = FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SQL_COLUMN_UPDATABLE:
|
||||
// everything should be updatable, I guess, unless access permissions
|
||||
// prevent it--are we supposed to check for that here? seems kind
|
||||
// of complicated. hmm...
|
||||
if (NULL != pfDesc)
|
||||
*pfDesc = SQL_ATTR_WRITE;
|
||||
break;
|
||||
}
|
||||
|
||||
return SQL_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns result data for a single column in the current row.
|
||||
|
||||
RETCODE SQL_API SQLGetData(
|
||||
HSTMT hstmt,
|
||||
UWORD icol,
|
||||
SWORD fCType,
|
||||
PTR rgbValue,
|
||||
SDWORD cbValueMax,
|
||||
SDWORD FAR *pcbValue)
|
||||
{
|
||||
QResultClass *res;
|
||||
StatementClass *stmt = (StatementClass *) hstmt;
|
||||
int num_cols, num_rows;
|
||||
Int4 field_type;
|
||||
void *value;
|
||||
int result;
|
||||
|
||||
if( ! stmt) {
|
||||
return SQL_INVALID_HANDLE;
|
||||
}
|
||||
res = stmt->result;
|
||||
|
||||
if (STMT_EXECUTING == stmt->status) {
|
||||
stmt->errormsg = "Can't get data while statement is still executing.";
|
||||
stmt->errornumber = STMT_SEQUENCE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (stmt->status != STMT_FINISHED) {
|
||||
stmt->errornumber = STMT_STATUS_ERROR;
|
||||
stmt->errormsg = "GetData can only be called after the successful execution on a SQL statement";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (icol == 0) {
|
||||
stmt->errormsg = "Bookmarks are not currently supported.";
|
||||
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
// use zero-based column numbers
|
||||
icol--;
|
||||
|
||||
// make sure the column number is valid
|
||||
num_cols = QR_NumResultCols(res);
|
||||
if (icol >= num_cols) {
|
||||
stmt->errormsg = "Invalid column number.";
|
||||
stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
if ( stmt->manual_result) {
|
||||
// make sure we're positioned on a valid row
|
||||
num_rows = QR_get_num_tuples(res);
|
||||
if((stmt->currTuple < 0) ||
|
||||
(stmt->currTuple >= num_rows)) {
|
||||
stmt->errormsg = "Not positioned on a valid row for GetData.";
|
||||
stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
|
||||
return SQL_ERROR;
|
||||
}
|
||||
value = QR_get_value_manual(res, stmt->currTuple, icol);
|
||||
}
|
||||
else { /* its a SOCKET result (backend data) */
|
||||
if (stmt->currTuple == -1 || ! res || QR_end_tuples(res)) {
|
||||
stmt->errormsg = "Not positioned on a valid row for GetData.";
|
||||
stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
value = QR_get_value_backend(res, icol);
|
||||
|
||||
}
|
||||
|
||||
field_type = QR_get_field_type(res, icol);
|
||||
|
||||
mylog("**** SQLGetData: icol = %d, fCType = %d, field_type = %d, value = '%s'\n", icol, fCType, field_type, value);
|
||||
|
||||
result = copy_and_convert_field(field_type, value,
|
||||
fCType, rgbValue, cbValueMax, pcbValue);
|
||||
|
||||
|
||||
if(result == COPY_UNSUPPORTED_TYPE) {
|
||||
stmt->errormsg = "Received an unsupported type from Postgres.";
|
||||
stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
|
||||
return SQL_ERROR;
|
||||
} else if(result == COPY_UNSUPPORTED_CONVERSION) {
|
||||
stmt->errormsg = "Couldn't handle the necessary data type conversion.";
|
||||
stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
|
||||
return SQL_ERROR;
|
||||
} else if(result == COPY_RESULT_TRUNCATED) {
|
||||
stmt->errornumber = STMT_TRUNCATED;
|
||||
stmt->errormsg = "The buffer was too small for the result.";
|
||||
return SQL_SUCCESS_WITH_INFO;
|
||||
} else if(result != COPY_OK) {
|
||||
stmt->errormsg = "Unrecognized return value from copy_and_convert_field.";
|
||||
stmt->errornumber = STMT_INTERNAL_ERROR;
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
return SQL_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns data for bound columns in the current row ("hstmt->iCursor"),
|
||||
// advances the cursor.
|
||||
|
||||
RETCODE SQL_API SQLFetch(
|
||||
HSTMT hstmt)
|
||||
{
|
||||
StatementClass *stmt = (StatementClass *) hstmt;
|
||||
QResultClass *res;
|
||||
int retval;
|
||||
Int2 num_cols, lf;
|
||||
Oid type;
|
||||
char *value;
|
||||
ColumnInfoClass *ci;
|
||||
|
||||
|
||||
if ( ! stmt)
|
||||
return SQL_INVALID_HANDLE;
|
||||
|
||||
SC_clear_error(stmt);
|
||||
|
||||
if ( ! (res = stmt->result)) {
|
||||
stmt->errormsg = "Null statement result in SQLFetch.";
|
||||
stmt->errornumber = STMT_SEQUENCE_ERROR;
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
ci = QR_get_fields(res); /* the column info */
|
||||
|
||||
if (stmt->status == STMT_EXECUTING) {
|
||||
stmt->errormsg = "Can't fetch while statement is still executing.";
|
||||
stmt->errornumber = STMT_SEQUENCE_ERROR;
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
if (stmt->status != STMT_FINISHED) {
|
||||
stmt->errornumber = STMT_STATUS_ERROR;
|
||||
stmt->errormsg = "Fetch can only be called after the successful execution on a SQL statement";
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
if (stmt->bindings == NULL) {
|
||||
// just to avoid a crash if the user insists on calling this
|
||||
// function even if SQL_ExecDirect has reported an Error
|
||||
stmt->errormsg = "Bindings were not allocated properly.";
|
||||
stmt->errornumber = STMT_SEQUENCE_ERROR;
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
if ( stmt->manual_result) {
|
||||
if (QR_get_num_tuples(res) -1 == stmt->currTuple ||
|
||||
(stmt->maxRows > 0 && stmt->currTuple == stmt->maxRows - 1))
|
||||
/* if we are at the end of a tuple list, we return a "no data found" */
|
||||
return SQL_NO_DATA_FOUND;
|
||||
|
||||
mylog("**** SQLFetch: manual_result\n");
|
||||
(stmt->currTuple)++;
|
||||
}
|
||||
else {
|
||||
|
||||
// read from the cache or the physical next tuple
|
||||
retval = QR_next_tuple(res);
|
||||
if (retval < 0) {
|
||||
mylog("**** SQLFetch: end_tuples\n");
|
||||
return SQL_NO_DATA_FOUND;
|
||||
}
|
||||
else if (retval > 0)
|
||||
(stmt->currTuple)++; // all is well
|
||||
|
||||
else {
|
||||
mylog("SQLFetch: error\n");
|
||||
stmt->errornumber = STMT_EXEC_ERROR;
|
||||
stmt->errormsg = "Error fetching next row";
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
num_cols = QR_NumResultCols(res);
|
||||
|
||||
for (lf=0; lf < num_cols; lf++) {
|
||||
|
||||
mylog("fetch: cols=%d, lf=%d, buffer[] = %u\n",
|
||||
num_cols, lf, stmt->bindings[lf].buffer);
|
||||
|
||||
if (stmt->bindings[lf].buffer != NULL) {
|
||||
// this column has a binding
|
||||
|
||||
// type = QR_get_field_type(res, lf);
|
||||
type = CI_get_oid(ci, lf); /* speed things up */
|
||||
|
||||
if (stmt->manual_result)
|
||||
value = QR_get_value_manual(res, stmt->currTuple, lf);
|
||||
else
|
||||
value = QR_get_value_backend(res, lf);
|
||||
|
||||
retval = copy_and_convert_field_bindinfo(type, value, &(stmt->bindings[lf]));
|
||||
|
||||
// check whether the complete result was copied
|
||||
if(retval == COPY_UNSUPPORTED_TYPE) {
|
||||
stmt->errormsg = "Received an unsupported type from Postgres.";
|
||||
stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
|
||||
return SQL_ERROR;
|
||||
|
||||
} else if(retval == COPY_UNSUPPORTED_CONVERSION) {
|
||||
stmt->errormsg = "Couldn't handle the necessary data type conversion.";
|
||||
stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
|
||||
return SQL_ERROR;
|
||||
|
||||
} else if(retval == COPY_RESULT_TRUNCATED) {
|
||||
/* The result has been truncated during the copy */
|
||||
/* this will generate a SQL_SUCCESS_WITH_INFO result */
|
||||
stmt->errornumber = STMT_TRUNCATED;
|
||||
stmt->errormsg = "A buffer was too small for the return value to fit in";
|
||||
return SQL_SUCCESS_WITH_INFO;
|
||||
|
||||
} else if(retval != COPY_OK) {
|
||||
stmt->errormsg = "Unrecognized return value from copy_and_convert_field.";
|
||||
stmt->errornumber = STMT_INTERNAL_ERROR;
|
||||
return SQL_ERROR;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SQL_SUCCESS;
|
||||
}
|
||||
|
||||
// This fetchs a block of data (rowset).
|
||||
|
||||
RETCODE SQL_API SQLExtendedFetch(
|
||||
HSTMT hstmt,
|
||||
UWORD fFetchType,
|
||||
SDWORD irow,
|
||||
UDWORD FAR *pcrow,
|
||||
UWORD FAR *rgfRowStatus)
|
||||
{
|
||||
StatementClass *stmt = (StatementClass *) hstmt;
|
||||
|
||||
if ( ! stmt)
|
||||
return SQL_INVALID_HANDLE;
|
||||
|
||||
/* Currently, only for manual results can this be done
|
||||
because not all the tuples are read in ahead of time.
|
||||
*/
|
||||
if ( ! stmt->manual_result)
|
||||
return SQL_ERROR;
|
||||
|
||||
// CC: we currently only support fetches in one row bits
|
||||
if (NULL != pcrow)
|
||||
*pcrow = 1;
|
||||
if (NULL != rgfRowStatus)
|
||||
*rgfRowStatus = SQL_ROW_SUCCESS;
|
||||
|
||||
switch (fFetchType) {
|
||||
case SQL_FETCH_NEXT:
|
||||
return SQLFetch(hstmt);
|
||||
case SQL_FETCH_PRIOR:
|
||||
if (stmt->currTuple <= 0)
|
||||
return SQL_ERROR;
|
||||
stmt->currTuple--;
|
||||
return SQLFetch(hstmt);
|
||||
case SQL_FETCH_FIRST:
|
||||
stmt->currTuple = -1;
|
||||
return SQLFetch(hstmt);
|
||||
case SQL_FETCH_LAST:
|
||||
stmt->currTuple = QR_get_num_tuples(stmt->result)-1;
|
||||
return SQLFetch(hstmt);
|
||||
case SQL_FETCH_ABSOLUTE:
|
||||
if (irow == 0) {
|
||||
stmt->currTuple = stmt->currTuple > 0 ? stmt->currTuple-2 : -1;
|
||||
} else if (irow > 0) {
|
||||
stmt->currTuple = irow-2;
|
||||
return SQLFetch(hstmt);
|
||||
} else {
|
||||
// CC: ??? not sure about the specification in that case
|
||||
return SQL_ERROR;
|
||||
}
|
||||
default:
|
||||
return SQL_ERROR;
|
||||
}
|
||||
return SQL_SUCCESS;
|
||||
}
|
||||
|
||||
// This determines whether there are more results sets available for
|
||||
// the "hstmt".
|
||||
|
||||
/* CC: return SQL_NO_DATA_FOUND since we do not support multiple result sets */
|
||||
RETCODE SQL_API SQLMoreResults(
|
||||
HSTMT hstmt)
|
||||
{
|
||||
return SQL_NO_DATA_FOUND;
|
||||
}
|
||||
|
||||
// This positions the cursor within a block of data.
|
||||
|
||||
RETCODE SQL_API SQLSetPos(
|
||||
HSTMT hstmt,
|
||||
UWORD irow,
|
||||
UWORD fOption,
|
||||
UWORD fLock)
|
||||
{
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
// Sets options that control the behavior of cursors.
|
||||
|
||||
RETCODE SQL_API SQLSetScrollOptions(
|
||||
HSTMT hstmt,
|
||||
UWORD fConcurrency,
|
||||
SDWORD crowKeyset,
|
||||
UWORD crowRowset)
|
||||
{
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
// Set the cursor name on a statement handle
|
||||
|
||||
RETCODE SQL_API SQLSetCursorName(
|
||||
HSTMT hstmt,
|
||||
UCHAR FAR *szCursor,
|
||||
SWORD cbCursor)
|
||||
{
|
||||
return SQL_SUCCESS;
|
||||
}
|
||||
|
||||
// Return the cursor name for a statement handle
|
||||
|
||||
RETCODE SQL_API SQLGetCursorName(
|
||||
HSTMT hstmt,
|
||||
UCHAR FAR *szCursor,
|
||||
SWORD cbCursorMax,
|
||||
SWORD FAR *pcbCursor)
|
||||
{
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user