1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-25 12:03:53 +03:00

Update for version 6-40-0002 and re-merge Thomas' changes.

This commit is contained in:
Byron Nikolaidis
1998-12-29 01:49:58 +00:00
parent d73cf044f6
commit 6d7735e7f0
26 changed files with 1568 additions and 572 deletions

View File

@@ -8,7 +8,7 @@
*
* API functions: SQLRowCount, SQLNumResultCols, SQLDescribeCol, SQLColAttributes,
* SQLGetData, SQLFetch, SQLExtendedFetch,
* SQLMoreResults(NI), SQLSetPos(NI), SQLSetScrollOptions(NI),
* SQLMoreResults(NI), SQLSetPos, SQLSetScrollOptions(NI),
* SQLSetCursorName, SQLGetCursorName
*
* Comments: See "notice.txt" for copyright and license information.
@@ -57,13 +57,18 @@ char *msg, *ptr;
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
if (stmt->manual_result) {
if (pcrow)
*pcrow = -1;
return SQL_SUCCESS;
}
if(stmt->statement_type == STMT_TYPE_SELECT) {
if (stmt->status == STMT_FINISHED) {
res = SC_get_Result(stmt);
if(res && pcrow) {
*pcrow = globals.use_declarefetch ? 0 : QR_get_num_tuples(res);
*pcrow = globals.use_declarefetch ? -1 : QR_get_num_tuples(res);
return SQL_SUCCESS;
}
}
@@ -176,6 +181,7 @@ Int4 fieldtype = 0;
int precision = 0;
ConnInfo *ci;
char parse_ok;
char buf[255];
mylog("%s: entering...\n", func);
@@ -247,7 +253,8 @@ char parse_ok;
if (icol >= QR_NumResultCols(result)) {
stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
stmt->errormsg = "Invalid column number in DescribeCol.";
SC_log_error(func, "", stmt);
sprintf(buf, "Col#=%d, #Cols=%d", icol, QR_NumResultCols(result));
SC_log_error(func, buf, stmt);
return SQL_ERROR;
}
@@ -610,7 +617,6 @@ int num_cols, num_rows;
Int4 field_type;
void *value;
int result;
char multiple;
mylog("SQLGetData: enter, stmt=%u\n", stmt);
@@ -674,7 +680,7 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt);
mylog(" value = '%s'\n", value);
}
else { /* its a SOCKET result (backend data) */
if (stmt->currTuple == -1 || ! res || QR_end_tuples(res)) {
if (stmt->currTuple == -1 || ! res || ! res->tupleField) {
stmt->errormsg = "Not positioned on a valid row for GetData.";
stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
SC_log_error(func, "", stmt);
@@ -690,14 +696,12 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt);
mylog("**** SQLGetData: icol = %d, fCType = %d, field_type = %d, value = '%s'\n", icol, fCType, field_type, value);
/* Is this another call for the same column to retrieve more data? */
multiple = (icol == stmt->current_col) ? TRUE : FALSE;
stmt->current_col = icol;
result = copy_and_convert_field(stmt, field_type, value,
fCType, rgbValue, cbValueMax, pcbValue, multiple);
fCType, rgbValue, cbValueMax, pcbValue);
stmt->current_col = icol;
stmt->current_col = -1;
switch(result) {
case COPY_OK:
@@ -725,7 +729,7 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt);
return SQL_ERROR;
case COPY_NO_DATA_FOUND:
SC_log_error(func, "no data found", stmt);
/* SC_log_error(func, "no data found", stmt); */
return SQL_NO_DATA_FOUND;
default:
@@ -736,70 +740,27 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt);
}
}
// Returns data for bound columns in the current row ("hstmt->iCursor"),
// advances the cursor.
RETCODE SQL_API SQLFetch(
HSTMT hstmt)
RETCODE
SC_fetch(StatementClass *stmt)
{
static char *func = "SQLFetch";
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *res;
int retval;
static char *func = "SC_fetch";
QResultClass *res = stmt->result;
int retval, result;
Int2 num_cols, lf;
Oid type;
char *value;
ColumnInfoClass *ci;
// TupleField *tupleField;
mylog("SQLFetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result);
if ( ! stmt) {
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
SC_clear_error(stmt);
if ( ! (res = stmt->result)) {
stmt->errormsg = "Null statement result in SQLFetch.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
stmt->last_fetch_count = 0;
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;
SC_log_error(func, "", stmt);
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";
SC_log_error(func, "", stmt);
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;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
mylog("manual_result = %d, use_declarefetch = %d\n", stmt->manual_result, globals.use_declarefetch);
if ( stmt->manual_result || ! globals.use_declarefetch) {
if (stmt->currTuple >= QR_get_num_tuples(res) -1 ||
(stmt->maxRows > 0 && stmt->currTuple == stmt->maxRows - 1)) {
(stmt->options.maxRows > 0 && stmt->currTuple == stmt->options.maxRows - 1)) {
/* if at the end of the tuples, return "no data found"
and set the cursor past the end of the result set
@@ -833,10 +794,16 @@ mylog("SQLFetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result);
num_cols = QR_NumResultCols(res);
result = SQL_SUCCESS;
stmt->last_fetch_count = 1;
for (lf=0; lf < num_cols; lf++) {
mylog("fetch: cols=%d, lf=%d, stmt = %u, stmt->bindings = %u, buffer[] = %u\n", num_cols, lf, stmt, stmt->bindings, stmt->bindings[lf].buffer);
/* reset for SQLGetData */
stmt->bindings[lf].data_left = -1;
if (stmt->bindings[lf].buffer != NULL) {
// this column has a binding
@@ -855,13 +822,12 @@ mylog("SQLFetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result);
value = QR_get_value_backend_row(res, stmt->currTuple, lf);
}
mylog("value = '%s'\n", value);
mylog("value = '%s'\n", (value==NULL)?"<NULL>":value);
retval = copy_and_convert_field_bindinfo(stmt, type, value, lf);
mylog("copy_and_convert: retval = %d\n", retval);
switch(retval) {
case COPY_OK:
break; /* OK, do next bound column */
@@ -870,37 +836,99 @@ mylog("SQLFetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result);
stmt->errormsg = "Received an unsupported type from Postgres.";
stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
result = SQL_ERROR;
break;
case COPY_UNSUPPORTED_CONVERSION:
stmt->errormsg = "Couldn't handle the necessary data type conversion.";
stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
result = SQL_ERROR;
break;
case COPY_RESULT_TRUNCATED:
stmt->errornumber = STMT_TRUNCATED;
stmt->errormsg = "The buffer was too small for the result.";
return SQL_SUCCESS_WITH_INFO;
result = SQL_SUCCESS_WITH_INFO;
break;
case COPY_GENERAL_ERROR: /* error msg already filled in */
SC_log_error(func, "", stmt);
return SQL_ERROR;
result = SQL_ERROR;
break;
/* This would not be meaningful in SQLFetch. */
case COPY_NO_DATA_FOUND:
SC_log_error(func, "no data found", stmt);
return SQL_NO_DATA_FOUND;
break;
default:
stmt->errormsg = "Unrecognized return value from copy_and_convert_field.";
stmt->errornumber = STMT_INTERNAL_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
result = SQL_ERROR;
break;
}
}
}
return SQL_SUCCESS;
return result;
}
// Returns data for bound columns in the current row ("hstmt->iCursor"),
// advances the cursor.
RETCODE SQL_API SQLFetch(
HSTMT hstmt)
{
static char *func = "SQLFetch";
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *res;
mylog("SQLFetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result);
if ( ! stmt) {
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
SC_clear_error(stmt);
if ( ! (res = stmt->result)) {
stmt->errormsg = "Null statement result in SQLFetch.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (stmt->status == STMT_EXECUTING) {
stmt->errormsg = "Can't fetch while statement is still executing.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
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";
SC_log_error(func, "", stmt);
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;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
QR_set_rowset_size(res, 1);
QR_inc_base(res, stmt->last_fetch_count);
return SC_fetch(stmt);
}
// This fetchs a block of data (rowset).
@@ -914,9 +942,10 @@ RETCODE SQL_API SQLExtendedFetch(
{
static char *func = "SQLExtendedFetch";
StatementClass *stmt = (StatementClass *) hstmt;
int num_tuples;
QResultClass *res;
int num_tuples, i, save_rowset_size;
RETCODE result;
char truncated, error;
mylog("SQLExtendedFetch: stmt=%u\n", stmt);
@@ -925,43 +954,108 @@ mylog("SQLExtendedFetch: stmt=%u\n", stmt);
return SQL_INVALID_HANDLE;
}
if ( globals.use_declarefetch) {
SC_log_error(func, "SQLExtendedFetch with UseDeclareFetch not yet supported", stmt);
if ( globals.use_declarefetch && ! stmt->manual_result) {
if ( fFetchType != SQL_FETCH_NEXT) {
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Unsupported fetch type for SQLExtendedFetch with UseDeclareFetch option.";
return SQL_ERROR;
}
}
SC_clear_error(stmt);
if ( ! (res = stmt->result)) {
stmt->errormsg = "Null statement result in SQLExtendedFetch.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (stmt->status == STMT_EXECUTING) {
stmt->errormsg = "Can't fetch while statement is still executing.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (stmt->status != STMT_FINISHED) {
stmt->errornumber = STMT_STATUS_ERROR;
stmt->errormsg = "ExtendedFetch can only be called after the successful execution on a SQL statement";
SC_log_error(func, "", stmt);
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;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
/* Initialize to no rows fetched */
if (rgfRowStatus)
*rgfRowStatus = SQL_ROW_NOROW;
for (i = 0; i < stmt->options.rowset_size; i++)
*(rgfRowStatus + i) = SQL_ROW_NOROW;
if (pcrow)
*pcrow = 0;
num_tuples = QR_get_num_tuples(stmt->result);
num_tuples = QR_get_num_tuples(res);
/* Save and discard the saved rowset size */
save_rowset_size = stmt->save_rowset_size;
stmt->save_rowset_size = -1;
switch (fFetchType) {
case SQL_FETCH_NEXT:
/* From the odbc spec... If positioned before the start of the RESULT SET,
then this should be equivalent to SQL_FETCH_FIRST.
*/
if (stmt->rowset_start < 0)
stmt->rowset_start = 0;
else {
stmt->rowset_start += (save_rowset_size > 0 ? save_rowset_size : stmt->options.rowset_size);
}
mylog("SQL_FETCH_NEXT: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
break;
case SQL_FETCH_PRIOR:
mylog("SQL_FETCH_PRIOR: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
/* If already before result set, return no data found */
if (stmt->currTuple <= 0)
return SQL_NO_DATA_FOUND;
stmt->currTuple -= 2;
/* From the odbc spec... If positioned after the end of the RESULT SET,
then this should be equivalent to SQL_FETCH_LAST.
*/
if (stmt->rowset_start >= num_tuples) {
stmt->rowset_start = num_tuples <= 0 ? 0 : (num_tuples - stmt->options.rowset_size);
}
else {
stmt->rowset_start -= stmt->options.rowset_size;
}
break;
case SQL_FETCH_FIRST:
mylog("SQL_FETCH_FIRST: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
stmt->currTuple = -1;
stmt->rowset_start = 0;
break;
case SQL_FETCH_LAST:
mylog("SQL_FETCH_LAST: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
stmt->currTuple = num_tuples <= 0 ? -1 : (num_tuples - 2);
stmt->rowset_start = num_tuples <= 0 ? 0 : (num_tuples - stmt->options.rowset_size) ;
break;
case SQL_FETCH_ABSOLUTE:
@@ -969,17 +1063,31 @@ mylog("SQLExtendedFetch: stmt=%u\n", stmt);
/* Position before result set, but dont fetch anything */
if (irow == 0) {
stmt->rowset_start = -1;
stmt->currTuple = -1;
return SQL_NO_DATA_FOUND;
}
/* Position before the desired row */
else if (irow > 0) {
stmt->currTuple = irow-2;
stmt->rowset_start = irow - 1;
}
/* Position with respect to the end of the result set */
else {
stmt->currTuple = num_tuples + irow - 1;
stmt->rowset_start = num_tuples + irow;
}
break;
case SQL_FETCH_RELATIVE:
/* Refresh the current rowset -- not currently implemented, but lie anyway */
if (irow == 0) {
break;
}
stmt->rowset_start += irow;
break;
default:
@@ -988,18 +1096,95 @@ mylog("SQLExtendedFetch: stmt=%u\n", stmt);
}
mylog("SQLExtendedFetch: new currTuple = %d\n", stmt->currTuple);
result = SQLFetch(hstmt);
if (result == SQL_SUCCESS) {
if (rgfRowStatus)
*rgfRowStatus = SQL_ROW_SUCCESS;
if (pcrow)
*pcrow = 1;
/***********************************/
/* CHECK FOR PROPER CURSOR STATE */
/***********************************/
/* Handle Declare Fetch style specially because the end is not really the end... */
if ( globals.use_declarefetch && ! stmt->manual_result) {
if (QR_end_tuples(res)) {
return SQL_NO_DATA_FOUND;
}
}
else {
/* If *new* rowset is after the result_set, return no data found */
if (stmt->rowset_start >= num_tuples) {
stmt->rowset_start = num_tuples;
return SQL_NO_DATA_FOUND;
}
}
return result;
/* If *new* rowset is prior to result_set, return no data found */
if (stmt->rowset_start < 0) {
if (stmt->rowset_start + stmt->options.rowset_size <= 0) {
stmt->rowset_start = -1;
return SQL_NO_DATA_FOUND;
}
else { /* overlap with beginning of result set, so get first rowset */
stmt->rowset_start = 0;
}
}
/* currTuple is always 1 row prior to the rowset */
stmt->currTuple = stmt->rowset_start - 1;
/* increment the base row in the tuple cache */
QR_set_rowset_size(res, stmt->options.rowset_size);
QR_inc_base(res, stmt->last_fetch_count);
/* Physical Row advancement occurs for each row fetched below */
mylog("SQLExtendedFetch: new currTuple = %d\n", stmt->currTuple);
truncated = error = FALSE;
for (i = 0; i < stmt->options.rowset_size; i++) {
stmt->bind_row = i; // set the binding location
result = SC_fetch(stmt);
/* Determine Function status */
if (result == SQL_NO_DATA_FOUND)
break;
else if (result == SQL_SUCCESS_WITH_INFO)
truncated = TRUE;
else if (result == SQL_ERROR)
error = TRUE;
/* Determine Row Status */
if (rgfRowStatus) {
if (result == SQL_ERROR)
*(rgfRowStatus + i) = SQL_ROW_ERROR;
else
*(rgfRowStatus + i)= SQL_ROW_SUCCESS;
}
}
/* Save the fetch count for SQLSetPos */
stmt->last_fetch_count= i;
/* Reset next binding row */
stmt->bind_row = 0;
/* Move the cursor position to the first row in the result set. */
stmt->currTuple = stmt->rowset_start;
/* For declare/fetch, need to reset cursor to beginning of rowset */
if (globals.use_declarefetch && ! stmt->manual_result) {
QR_set_position(res, 0);
}
/* Set the number of rows retrieved */
if (pcrow)
*pcrow = i;
if (i == 0)
return SQL_NO_DATA_FOUND; /* Only DeclareFetch should wind up here */
else if (error)
return SQL_ERROR;
else if (truncated)
return SQL_SUCCESS_WITH_INFO;
else
return SQL_SUCCESS;
}
@@ -1014,8 +1199,8 @@ RETCODE SQL_API SQLMoreResults(
return SQL_NO_DATA_FOUND;
}
// This positions the cursor within a block of data.
// This positions the cursor within a rowset, that was positioned using SQLExtendedFetch.
// This will be useful (so far) only when using SQLGetData after SQLExtendedFetch.
RETCODE SQL_API SQLSetPos(
HSTMT hstmt,
UWORD irow,
@@ -1023,12 +1208,57 @@ RETCODE SQL_API SQLSetPos(
UWORD fLock)
{
static char *func = "SQLSetPos";
char buf[128];
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *res;
int num_cols, i;
BindInfoClass *bindings = stmt->bindings;
sprintf(buf, "SQLSetPos not implemented: irow=%d, fOption=%d, fLock=%d\n", irow, fOption, fLock);
if ( ! stmt) {
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
if (fOption != SQL_POSITION && fOption != SQL_REFRESH) {
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Only SQL_POSITION/REFRESH is supported for SQLSetPos";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if ( ! (res = stmt->result)) {
stmt->errormsg = "Null statement result in SQLSetPos.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
num_cols = QR_NumResultCols(res);
if (irow == 0) {
stmt->errornumber = STMT_ROW_OUT_OF_RANGE;
stmt->errormsg = "Driver does not support Bulk operations.";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (irow > stmt->last_fetch_count) {
stmt->errornumber = STMT_ROW_OUT_OF_RANGE;
stmt->errormsg = "Row value out of range";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
irow--;
/* Reset for SQLGetData */
for (i = 0; i < num_cols; i++)
bindings[i].data_left = -1;
QR_set_position(res, irow);
stmt->currTuple = stmt->rowset_start + irow;
return SQL_SUCCESS;
SC_log_error(func, buf, (StatementClass *) hstmt);
return SQL_ERROR;
}
// Sets options that control the behavior of cursors.