⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 odbc.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 5 页
字号:
		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 + -