📄 odbc.c
字号:
if( sqlStatus == SQL_NO_DATA )
{
if( queryType == DBMS_QUERY_CONTINUE )
setErrorString( errorInfo, "No more data found", 18 );
else
setErrorString( errorInfo, "No data found", 13 );
return( CRYPT_ERROR_NOTFOUND );
}
return( getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
CRYPT_ERROR_READ ) );
}
/* If we're just doing a presence check we don't bother fetching data */
if( queryType == DBMS_QUERY_CHECK )
return( CRYPT_OK );
/* Read the data */
sqlStatus = SQLGetData( hStmt, 1, dataType, data, dataMaxLength,
&length );
if( !sqlStatusOK( sqlStatus ) )
return( getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
CRYPT_ERROR_READ ) );
*dataLength = ( int ) length;
return( CRYPT_OK );
}
/* Perform a transaction that returns information */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int performQuery( INOUT DBMS_STATE_INFO *dbmsInfo,
IN_BUFFER_OPT( commandLength ) const char *command,
IN_LENGTH_SHORT_Z const int commandLength,
OUT_BUFFER_OPT( dataMaxLength, *dataLength ) \
void *data,
IN_LENGTH_SHORT_Z const int dataMaxLength,
OUT_LENGTH_SHORT_Z int *dataLength,
IN_ARRAY_OPT( BOUND_DATA_MAXITEMS ) \
const BOUND_DATA *boundData,
IN_ENUM_OPT( DBMS_CACHEDQUERY ) \
const DBMS_CACHEDQUERY_TYPE queryEntry,
IN_ENUM( DBMS_QUERY ) const DBMS_QUERY_TYPE queryType )
{
const SQLHSTMT hStmt = dbmsInfo->hStmt[ queryEntry ];
BOUND_DATA_STATE boundDataState;
SQLRETURN sqlStatus;
int status;
assert( isWritePtr( dbmsInfo, sizeof( DBMS_STATE_INFO ) ) );
assert( ( command == NULL && commandLength == 0 && \
( queryType == DBMS_QUERY_CONTINUE || \
queryType == DBMS_QUERY_CANCEL ) ) || \
isReadPtr( command, commandLength ) );
assert( ( data == NULL && dataMaxLength == 0 && dataLength == NULL ) || \
( isWritePtr( data, dataMaxLength ) && \
isWritePtr( dataLength, sizeof( int ) ) ) );
assert( ( boundData == NULL ) || \
isReadPtr( boundData, \
sizeof( BOUND_DATA ) * BOUND_DATA_MAXITEMS ) );
REQUIRES( ( ( queryType == DBMS_QUERY_CONTINUE || \
queryType == DBMS_QUERY_CANCEL ) && \
command == NULL && commandLength == 0 ) || \
( ( queryType == DBMS_QUERY_START || \
queryType == DBMS_QUERY_CHECK || \
queryType == DBMS_QUERY_NORMAL ) && \
command != NULL && \
commandLength > 0 && commandLength < MAX_INTLENGTH_SHORT ) );
REQUIRES( ( data == NULL && dataMaxLength == 0 && \
dataLength == NULL ) || \
( data != NULL && dataMaxLength >= 16 && \
dataMaxLength < MAX_INTLENGTH_SHORT && \
dataLength != NULL ) );
REQUIRES( queryEntry >= DBMS_CACHEDQUERY_NONE && \
queryEntry < DBMS_CACHEDQUERY_LAST );
REQUIRES( queryType > DBMS_QUERY_NONE && \
queryType < DBMS_QUERY_LAST );
/* Clear return value */
if( dataLength != NULL )
*dataLength = 0;
/* If we're starting a new query, handle the query initialisation and
parameter binding */
if( queryType == DBMS_QUERY_START || \
queryType == DBMS_QUERY_CHECK || \
queryType == DBMS_QUERY_NORMAL )
{
/* Prepare the query for execution if necessary. The entry at
position DBMS_CACHEDQUERY_NONE is never cached so the following
code is always executed for this case */
if( !dbmsInfo->hStmtPrepared[ queryEntry ] )
{
char query[ SQL_QUERY_BUFSIZE + 8 ];
int queryLength;
status = convertQuery( dbmsInfo, query, SQL_QUERY_BUFSIZE,
&queryLength, command, commandLength );
if( cryptStatusError( status ) )
return( status );
sqlStatus = SQLPrepare( hStmt, query, queryLength );
if( !sqlStatusOK( sqlStatus ) )
return( getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
CRYPT_ERROR_READ ) );
if( queryEntry != DBMS_CACHEDQUERY_NONE )
dbmsInfo->hStmtPrepared[ queryEntry ] = TRUE;
}
/* Bind in any query parameters that may be required */
if( boundData != NULL )
{
status = bindParameters( hStmt, boundData, &boundDataState,
dbmsInfo );
if( cryptStatusError( status ) )
return( status );
}
}
switch( queryType )
{
case DBMS_QUERY_START:
/* Execute the query */
sqlStatus = SQLExecute( hStmt );
if( !sqlStatusOK( sqlStatus ) )
return( getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
CRYPT_ERROR_READ ) );
/* If we're starting an ongoing query with results to be fetched
later, we're done */
if( data == NULL )
return( CRYPT_OK );
/* Drop through to fetch the first set of results */
case DBMS_QUERY_CONTINUE:
assert( isWritePtr( data, dataMaxLength ) );
REQUIRES( data != NULL && \
dataMaxLength >= 16 && \
dataMaxLength < MAX_INTLENGTH_SHORT );
/* We're in the middle of a continuing query, fetch the next set
of results. If we've run out of results (indicated by a not-
found status) we explicitly signal to the caller that the
query has completed */
status = fetchData( dbmsInfo->hStmt[ queryEntry ], data,
dataMaxLength, dataLength,
DBMS_QUERY_CONTINUE, dbmsInfo );
return( cryptStatusOK( status ) ? CRYPT_OK : \
( status == CRYPT_ERROR_NOTFOUND ) ? \
CRYPT_ERROR_COMPLETE : status );
case DBMS_QUERY_CANCEL:
/* Cancel any outstanding requests to clear the hStmt and make
it ready for re-use */
SQLCloseCursor( dbmsInfo->hStmt[ queryEntry ] );
return( CRYPT_OK );
case DBMS_QUERY_CHECK:
case DBMS_QUERY_NORMAL:
/* Only return a maximum of a single row in response to a point
query. This is a simple optimisation to ensure that the
database client doesn't start sucking across huge amounts of
data when it's not necessary */
( void ) SQLSetStmtAttr( hStmt, SQL_ATTR_MAX_ROWS,
( SQLPOINTER ) 1, SQL_IS_INTEGER );
/* Execute the SQL statement and fetch the results */
sqlStatus = SQLExecute( hStmt );
if( sqlStatusOK( sqlStatus ) )
{
status = fetchData( hStmt, data, dataMaxLength, dataLength,
queryType, dbmsInfo );
SQLCloseCursor( hStmt );
}
else
{
status = getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
CRYPT_ERROR_READ );
}
/* Reset the statement handle's multi-row result handling */
( void ) SQLSetStmtAttr( hStmt, SQL_ATTR_MAX_ROWS,
( SQLPOINTER ) 0, SQL_IS_INTEGER );
return( status );
}
retIntError();
}
/****************************************************************************
* *
* Database Write Routines *
* *
****************************************************************************/
/* Perform a transaction that updates the database without returning any
data */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int performUpdate( INOUT DBMS_STATE_INFO *dbmsInfo,
IN_BUFFER_OPT( commandLength ) const char *command,
IN_LENGTH_SHORT_Z const int commandLength,
IN_ARRAY_OPT( BOUND_DATA_MAXITEMS ) \
const BOUND_DATA *boundData,
IN_ENUM( DBMS_UPDATE ) \
const DBMS_UPDATE_TYPE updateType )
{
const SQLHSTMT hStmt = dbmsInfo->hStmt[ DBMS_CACHEDQUERY_NONE ];
BOUND_DATA_STATE boundDataState;
SQLRETURN sqlStatus;
char query[ SQL_QUERY_BUFSIZE + 8 ];
int queryLength, status;
assert( isWritePtr( dbmsInfo, sizeof( DBMS_STATE_INFO ) ) );
assert( ( command == NULL && commandLength == 0 && \
updateType == DBMS_UPDATE_ABORT ) || \
isReadPtr( command, commandLength ) );
assert( ( boundData == NULL ) || \
isReadPtr( boundData, \
sizeof( BOUND_DATA ) * BOUND_DATA_MAXITEMS ) );
REQUIRES( ( updateType == DBMS_UPDATE_ABORT && \
command == NULL && commandLength == 0 ) || \
( updateType != DBMS_UPDATE_ABORT && \
command != NULL && \
commandLength > 0 && commandLength < MAX_INTLENGTH_SHORT ) );
REQUIRES( updateType > DBMS_UPDATE_NONE && \
updateType < DBMS_UPDATE_LAST );
/* If we're aborting a transaction, roll it back, re-enable autocommit,
and clean up */
if( updateType == DBMS_UPDATE_ABORT )
{
sqlStatus = SQLEndTran( SQL_HANDLE_DBC, dbmsInfo->hDbc,
SQL_ROLLBACK );
( void ) SQLSetConnectAttr( dbmsInfo->hDbc, SQL_ATTR_AUTOCOMMIT,
( SQLPOINTER ) SQL_AUTOCOMMIT_ON,
SQL_IS_UINTEGER );
if( !sqlStatusOK( sqlStatus ) )
return( getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
CRYPT_ERROR_WRITE ) );
return( CRYPT_OK );
}
/* If it's the start of a transaction, turn autocommit off */
if( updateType == DBMS_UPDATE_BEGIN )
{
( void ) SQLSetConnectAttr( dbmsInfo->hDbc, SQL_ATTR_AUTOCOMMIT,
( SQLPOINTER ) SQL_AUTOCOMMIT_OFF,
SQL_IS_UINTEGER );
}
/* Bind in any necessary parameters to the hStmt */
if( boundData != NULL )
{
status = bindParameters( hStmt, boundData, &boundDataState,
dbmsInfo );
if( cryptStatusError( status ) )
return( status );
}
/* Execute the command */
status = convertQuery( dbmsInfo, query, SQL_QUERY_BUFSIZE, &queryLength,
command, commandLength );
if( cryptStatusError( status ) )
return( status );
sqlStatus = SQLExecDirect( hStmt, query, queryLength );
if( !sqlStatusOK( sqlStatus ) )
{
/* The return status from a delete operation can be reported in
several ways at the whim of the driver. Some drivers always
report success even though nothing was found to delete (more
common in ODBC 2.x drivers, see the code further on for the
handling for this), others report a failure to delete anything
with an SQL_NO_DATA status (more common in ODBC 3.x drivers).
For this case we convert the overall status to a
CRYPT_ERROR_NOTFOUND and update the sqlStatus as required if we
need to continue */
if( sqlStatus == SQL_NO_DATA && \
command != NULL && commandLength >= 6 && \
!strCompare( command, "DELETE", 6 ) )
{
status = CRYPT_ERROR_NOTFOUND;
if( updateType != DBMS_UPDATE_COMMIT )
return( status );
}
else
{
/* If we hit an error at this point we can only exit if we're
not finishing a transaction. If we are, the commit turns
into an abort further down */
status = getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
CRYPT_ERROR_WRITE );
if( updateType != DBMS_UPDATE_COMMIT )
return( status );
}
}
else
{
/* If we're performing a delete the operation will succeed even
though nothing was found to delete so we make sure that we
actually changed something */
if( command != NULL && commandLength >= 6 && \
!strCompare( command, "DELETE", 6 ) )
{
SQLUINTEGER rowCount;
sqlStatus = SQLRowCount( hStmt, &rowCount );
if( !sqlStatusOK( sqlStatus ) || rowCount <= 0 )
status = CRYPT_ERROR_NOTFOUND;
}
}
/* If it's the end of a transaction, commit the transaction and turn
autocommit on again */
if( updateType == DBMS_UPDATE_COMMIT )
{
/* If we've had a failure before this point, abort, otherwise
commit. The SQLSMALLINT cast is necessary (although spurious) in
some development environments */
sqlStatus = SQLEndTran( SQL_HANDLE_DBC, dbmsInfo->hDbc,
( SQLSMALLINT ) \
( cryptStatusError( status ) ? \
SQL_ROLLBACK : SQL_COMMIT ) );
if( dbmsInfo->transactIsDestructive )
{
int i;
/* If transactions are destructive for this back-end type,
invalidate all prepared statements */
for( i = 0; i < NO_CACHED_QUERIES; i++ )
dbmsInfo->hStmtPrepared[ i ] = FALSE;
}
( void ) SQLSetConnectAttr( dbmsInfo->hDbc, SQL_ATTR_AUTOCOMMIT,
( SQLPOINTER ) SQL_AUTOCOMMIT_ON,
SQL_IS_UINTEGER );
if( cryptStatusOK( status ) && !sqlStatusOK( sqlStatus ) )
{
status = getErrorInfo
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -