1
0
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:
Marc G. Fournier
1998-04-13 15:02:05 +00:00
parent d4d26f9c33
commit 9e3382dfc5
37 changed files with 12152 additions and 0 deletions

View 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;
}