Odb.cpp
上传用户:xaxinn
上传日期:2007-01-03
资源大小:39k
文件大小:18k
- // Odb.cpp
- //
- // Release 1, Copyright (C) 1999 Ben Bryant
- // This is sample source code, nothing more is implied. Use it only as such.
- // This software is provided 'as-is', without warranty. In no event will the
- // author be held liable for any damages arising from the use of this software.
- // Permission is granted to anyone to use this software for any purpose.
- // The origin of this software must not be misrepresented; you must not claim
- // that you wrote the original software. Altered source versions must be plainly
- // marked as such, and must not be misrepresented as being the original software.
- // Ben Bryant bcbryant@firstobject.com
- #include "stdafx.h"
- #include "Odb.h"
- #ifdef _DEBUG
- #undef THIS_FILE
- static char THIS_FILE[]=__FILE__;
- #define new DEBUG_NEW
- #endif
- //////////////////////////////////////////////////////////////////
- // Oracle Call Interface (OCI) Wrapper
- //
- #ifdef __STDC__
- #include <oci.h>
- #else
- #define __STDC__ 1
- #include <oci.h>
- #undef __STDC__
- #endif
- //////////////////////////////////////////////////////////////////////
- // COdb::OdbField
- //////////////////////////////////////////////////////////////////////
- struct COdb::OdbField
- {
- //
- // This structure maintains an OCI field/column/variable
- //
- CString csName;
- dvoid* pBuffer;
- ub2 wType;
- ub2 wSize;
- ub2 wLen;
- sb2 nInd;
- BOOL bQuotedOnUpdate;
- // Methods
- OdbField();
- ~OdbField();
- void SetSize( ub2 w );
- void Set( CString cs );
- CString Get() const;
- void Free();
- };
- COdb::OdbField::OdbField()
- {
- pBuffer = NULL;
- wType = SQLT_STR;
- wSize = 0;
- wLen = 0;
- nInd = 0;
- bQuotedOnUpdate = TRUE;
- }
- COdb::OdbField::~OdbField()
- {
- Free();
- }
- void COdb::OdbField::SetSize( ub2 w )
- {
- //
- // Make sure buffer is at least size w
- //
- if ( w > wSize || ! pBuffer )
- {
- Free();
- pBuffer = (dvoid*) new char[w];
- wSize = w;
- }
- }
- void COdb::OdbField::Set( CString cs )
- {
- //
- // Set buffer to hold CString
- //
- wLen = cs.GetLength() + 1;
- SetSize( wLen );
- strcpy( (char*)pBuffer, cs );
- }
- CString COdb::OdbField::Get() const
- {
- //
- // All fields are converted to strings by the OCI
- // because SQLT_STR and SQLT_LNG are specified when binding fields
- // Copy from buffer into a string
- //
- CString csResult;
- csResult.Format("%.*s", wLen, (const char*)pBuffer);
- csResult.TrimRight();
- return csResult;
- }
- void COdb::OdbField::Free()
- {
- //
- // Free field's allocated buffer
- //
- if ( pBuffer )
- {
- delete pBuffer;
- pBuffer = NULL;
- }
- }
- //////////////////////////////////////////////////////////////////////
- // COdb::OdbRecordSet
- //////////////////////////////////////////////////////////////////////
- struct COdb::OdbRecordSet
- {
- //
- // This structure maintains a recordset
- // A recordset holds a row of select results
- //
- CPtrArray paFields;
- int m_nRows;
- CString m_csStatement;
- // Methods
- OdbRecordSet();
- ~OdbRecordSet();
- int Find( const CString& csName );
- void RemoveAll();
- };
- COdb::OdbRecordSet::OdbRecordSet()
- {
- }
- COdb::OdbRecordSet::~OdbRecordSet()
- {
- RemoveAll();
- }
- void COdb::OdbRecordSet::RemoveAll()
- {
- //
- // Remove fields
- //
- for ( int iField=0; iField<paFields.GetSize(); ++iField )
- delete (OdbField*)paFields[iField];
- paFields.RemoveAll();
- }
- int COdb::OdbRecordSet::Find( const CString& csName )
- {
- //
- // Return iField or -1 if name not found
- //
- for ( int iField=0; iField<paFields.GetSize(); ++iField )
- {
- OdbField* pField = (OdbField*)paFields.GetAt( iField );
- if ( pField->csName == csName )
- return iField;
- }
- return -1;
- }
- //////////////////////////////////////////////////////////////////////
- // OdbContext
- //////////////////////////////////////////////////////////////////////
- struct COdb::OdbContext
- {
- //
- // This structure maintains all of the OCI variables
- // It is declared here rather than in the header
- // to isolate OCI from COdb clients
- //
- OdbContext();
- void Clear();
- char szError[512];
- HRESULT hr;
- OCIEnv* hpEnv;
- OCIServer* hpServer;
- OCIError* hpErr;
- OCISvcCtx* hpContext;
- OCIStmt *hpSelect;
- OdbRecordSet rsSelect;
- };
- COdb::OdbContext::OdbContext()
- {
- Clear();
- }
- void COdb::OdbContext::Clear()
- {
- hr = OCI_SUCCESS;
- hpEnv = NULL;
- hpErr = NULL;
- hpServer = NULL;
- hpContext = NULL;
- hpSelect = NULL;
- szError[0] = ' ';
- }
- //////////////////////////////////////////////////////////////////////
- // COdb
- //////////////////////////////////////////////////////////////////////
- COdb::COdb()
- {
- m_po = new OdbContext;
- }
- COdb::~COdb()
- {
- Close();
- delete m_po;
- }
- void COdb::CheckErr( short status )
- {
- //
- // This sets the OCI error string
- // Later, this might be made to generate an exception
- // The table of codes and strings is kept here in a static array
- //
- static struct CErrorPair
- {
- sword w;
- const char* szText;
- }
- epaErrors[] =
- {
- OCI_SUCCESS, "SUCCESS",
- OCI_SUCCESS_WITH_INFO, "SUCCESS_WITH_INFO",
- OCI_NEED_DATA, "NEED_DATA",
- OCI_NO_DATA, "NO_DATA",
- OCI_INVALID_HANDLE, "INVALID_HANDLE",
- OCI_STILL_EXECUTING, "STILL_EXECUTE",
- OCI_CONTINUE, "CONTINUE",
- };
- // Check status
- m_po->hr = status;
- if ( m_po->hr == OCI_ERROR )
- {
- // for the special case OCI_ERROR, call OCIErrorGet for the string
- sb4 errcode = 0;
- OCIErrorGet(m_po->hpErr, 1, NULL, &errcode, (unsigned char*)m_po->szError, sizeof(m_po->szError), OCI_HTYPE_ERROR);
- }
- else
- {
- // Look for description in static array
- DWORD nTotalResultCodes = sizeof(epaErrors) / sizeof(CErrorPair);
- for ( int iPos = 0; iPos < sizeof(epaErrors); ++ iPos )
- {
- if ( epaErrors[iPos].w == m_po->hr )
- {
- strcpy( m_po->szError, epaErrors[iPos].szText );
- break;
- }
- }
- }
- }
- CString COdb::GetErrorDescription()
- {
- return m_po->szError;
- }
- HRESULT COdb::Open( const CString csConnect )
- {
- //
- // This opens a connection with the database
- // Connect string format is user/password@host
- //
- CString csUser, csPassword, csServer;
- int iSlash = csConnect.Find("/");
- if ( iSlash > -1 )
- {
- csUser = csConnect.Left(iSlash);
- csPassword = csConnect.Mid(iSlash + 1);
- int iAt = csConnect.Find("@");
- if ( iAt > iSlash )
- {
- csPassword = csConnect.Mid( iSlash + 1, iAt - iSlash - 1 );
- csServer = csConnect.Mid(iAt + 1);
- }
- }
- else
- {
- csUser = csConnect;
- }
- if ( csUser.IsEmpty() && csPassword.IsEmpty() )
- {
- // For default OS logon, specify slash for user name and password
- csUser = "/";
- csPassword = "/";
- }
- // First, we make sure its closed
- Close();
- // Initialize OCI DLL specifying the mode
- CheckErr( OCIInitialize( OCI_OBJECT, NULL, NULL, NULL, NULL ) );
- if ( FAILED(m_po->hr) )
- return m_po->hr;
- // Note an alternative is to use the OCILogon/Logoff functions
- // Here we allocate all of the handles explicitly
- // Initialize the environment handle
- CheckErr( OCIEnvInit( &m_po->hpEnv, OCI_DEFAULT, 0, NULL ) );
- if ( FAILED(m_po->hr) )
- return m_po->hr;
- // Allocate error, server, and service context handles
- OCIHandleAlloc( m_po->hpEnv, (void**)&m_po->hpErr, OCI_HTYPE_ERROR, 0, NULL );
- OCIHandleAlloc( m_po->hpEnv, (void**)&m_po->hpServer, OCI_HTYPE_SERVER, 0, NULL );
- OCIHandleAlloc( m_po->hpEnv, (void**)&m_po->hpContext, OCI_HTYPE_SVCCTX, 0, NULL );
- // Associate TNS with server handle
- CheckErr( OCIServerAttach( m_po->hpServer,
- m_po->hpErr,
- (const unsigned char*)(const char*)csServer,
- csServer.GetLength(),
- 0 )
- );
- if ( FAILED(m_po->hr) )
- return m_po->hr;
- // Get server version string
- const nVersionLength = 1024;
- CString csVersion;
- CheckErr( OCIServerVersion( m_po->hpServer,
- m_po->hpErr,
- (text*)csVersion.GetBuffer(nVersionLength),
- nVersionLength,
- OCI_HTYPE_SERVER )
- );
- if ( FAILED(m_po->hr) )
- return m_po->hr;
- csVersion.ReleaseBuffer();
- m_csLog += csVersion + "n";
- // Specify server handle to service context
- CheckErr( OCIAttrSet( m_po->hpContext,
- OCI_HTYPE_SVCCTX,
- m_po->hpServer,
- 0,
- OCI_ATTR_SERVER,
- m_po->hpErr )
- );
- if ( FAILED(m_po->hr) )
- return m_po->hr;
- // Allocate a session handle
- OCISession *hpSession = NULL;
- OCIHandleAlloc( m_po->hpEnv, (void**)&hpSession, OCI_HTYPE_SESSION, 0, NULL);
- // Associate username with session handle
- OCIAttrSet( hpSession,
- OCI_HTYPE_SESSION,
- (void*)(const char*)csUser,
- csUser.GetLength(),
- OCI_ATTR_USERNAME,
- m_po->hpErr
- );
- // Associate password with session handle
- OCIAttrSet( hpSession,
- OCI_HTYPE_SESSION,
- (void*)(const char*)csPassword,
- csPassword.GetLength(),
- OCI_ATTR_PASSWORD,
- m_po->hpErr
- );
- // Open session using service context and session handle
- CheckErr( OCISessionBegin( m_po->hpContext,
- m_po->hpErr,
- hpSession,
- OCI_CRED_RDBMS,
- OCI_DEFAULT )
- );
- if ( FAILED(m_po->hr) )
- return m_po->hr;
- // Specify session handle to service context
- OCIAttrSet( m_po->hpContext,
- OCI_HTYPE_SVCCTX,
- hpSession,
- 0,
- OCI_ATTR_SESSION,
- m_po->hpErr
- );
- // Change date format
- if ( SUCCEEDED(m_po->hr) )
- {
- CString csSQL = "alter session set nls_date_format = 'yyyy-mm-dd hh24:mi:ss'";
- m_po->hr = Exec( csSQL );
- }
- return m_po->hr;
- }
- HRESULT COdb::Close()
- {
- //
- // This closes and/or cleans up the connection
- // It should work even if the connection is closed or partly open
- //
- m_po->hr = OCI_SUCCESS;
- // Free select statement handle
- if (m_po->hpSelect)
- OCIHandleFree( m_po->hpSelect, OCI_HTYPE_STMT );
- // Detach server from server handle
- if (m_po->hpErr)
- OCIServerDetach( m_po->hpServer, m_po->hpErr, OCI_DEFAULT );
- // Free server handle
- if (m_po->hpServer)
- CheckErr( OCIHandleFree(m_po->hpServer, OCI_HTYPE_SERVER) );
- // Free service context
- if (m_po->hpContext)
- OCIHandleFree( m_po->hpContext, OCI_HTYPE_SVCCTX);
- // Free error handle
- if (m_po->hpErr)
- OCIHandleFree( m_po->hpErr, OCI_HTYPE_ERROR );
- m_po->Clear();
- return m_po->hr;
- }
- HRESULT COdb::Exec( const CString csStatement )
- {
- //
- // Execute statement
- //
- m_csLog += csStatement + "n";
- // Allocate statement handle
- OCIStmt *hpStatement = NULL;
- CheckErr( OCIHandleAlloc( m_po->hpEnv,
- (void**)&hpStatement,
- OCI_HTYPE_STMT,
- 0,
- NULL )
- );
- // Associate statement string with handle
- CString csCleanStatement = csStatement;
- CleanWhitespace( csCleanStatement );
- CheckErr( OCIStmtPrepare( hpStatement,
- m_po->hpErr,
- (text *)csCleanStatement.GetBuffer(0),
- csCleanStatement.GetLength(),
- OCI_NTV_SYNTAX,
- OCI_DEFAULT )
- );
- // Execute statement
- CheckErr( OCIStmtExecute( m_po->hpContext,
- hpStatement,
- m_po->hpErr,
- 1, // iters
- 0, // row offset
- NULL, NULL, // snapshot in/out
- OCI_DEFAULT )
- );
- // Free statement handle
- if ( hpStatement )
- OCIHandleFree( hpStatement, OCI_HTYPE_STMT );
- return m_po->hr;
- }
- HRESULT COdb::Select( const CString csStatement )
- {
- //
- // Execute select statement and parse the select list description
- // Use member recordset rsSelect
- // See also Get, FetchNext, IsEOS
- //
- m_csLog += csStatement + "n";
- // Free previous select handle and remove fields
- m_po->rsSelect.m_nRows = 0;
- m_po->rsSelect.RemoveAll();
- if (m_po->hpSelect)
- OCIHandleFree( m_po->hpSelect, OCI_HTYPE_STMT );
- // Allocate statement handle
- CheckErr( OCIHandleAlloc( m_po->hpEnv,
- (void**)&m_po->hpSelect, // ref to statement handle pointer
- OCI_HTYPE_STMT, // type of handle being allocated
- 0,
- NULL )
- );
- // Associate statement string with handle
- m_po->rsSelect.m_csStatement = csStatement;
- CleanWhitespace( m_po->rsSelect.m_csStatement );
- CheckErr( OCIStmtPrepare( m_po->hpSelect,
- m_po->hpErr, // error handle pointer
- (text *)m_po->rsSelect.m_csStatement.GetBuffer(0), // statement string
- m_po->rsSelect.m_csStatement.GetLength(),
- OCI_NTV_SYNTAX, // specify native syntax
- OCI_DEFAULT )
- );
- // Execute but don't fetch yet
- CheckErr( OCIStmtExecute( m_po->hpContext,
- m_po->hpSelect, // prepared by previous function calls
- m_po->hpErr,
- 0, // 'iters' i.e. max rows to fetch during this call
- 0,
- NULL, NULL, OCI_DEFAULT )
- );
- // If it returns OCI_NO_DATA then no need to define recordset
- if ( m_po->hr == OCI_NO_DATA || FAILED(m_po->hr) )
- {
- return m_po->hr;
- }
- // Load the types into recordset
- int nColumnCount = 0;
- while ( m_po->hr == OCI_SUCCESS )
- {
- // Get pointer to column
- void* pFieldAttr = NULL;
- HRESULT hrGetNext;
- hrGetNext = OCIParamGet( m_po->hpSelect, OCI_HTYPE_STMT,
- m_po->hpErr,
- &pFieldAttr,
- nColumnCount+1 // position
- );
- if ( hrGetNext != OCI_SUCCESS )
- break;
- // Increment column count and allocate an OdbField structure
- ++nColumnCount;
- OdbField* pField = new OdbField;
- m_po->rsSelect.paFields.Add( pField );
- // Get data type
- CheckErr( OCIAttrGet( pFieldAttr, OCI_DTYPE_PARAM,
- &pField->wType,
- 0,
- OCI_ATTR_DATA_TYPE,
- m_po->hpErr )
- );
- // Get data size
- CheckErr( OCIAttrGet( pFieldAttr, OCI_DTYPE_PARAM,
- &pField->wSize,
- 0,
- OCI_ATTR_DATA_SIZE,
- m_po->hpErr )
- );
- // Type conversions
- if ( pField->wType == SQLT_LNG )
- {
- // LONG size
- pField->wSize = 32760;
- }
- else if ( pField->wType == SQLT_DAT )
- {
- // String is bound to DATE
- pField->wType = SQLT_STR;
- pField->wSize = 50;
- }
- else if ( pField->wType == SQLT_NUM )
- {
- // String is bound to NUMBER
- pField->wType = SQLT_STR;
- pField->wSize += 1; // allow for null-terminator
- }
- // Get column name
- text* pName;
- ub4 nNameLen;
- CheckErr( OCIAttrGet( pFieldAttr, OCI_DTYPE_PARAM,
- (void**)&pName,
- &nNameLen,
- OCI_ATTR_NAME,
- m_po->hpErr )
- );
- // Set size and name
- pField->SetSize( pField->wSize );
- pField->csName.Format( "%.*s", nNameLen, pName );
- }
- // Bind storage for receiving input variables
- OCIDefine *pDefn; // to hold pointer to field definition
- int iField;
- for ( iField=0; iField<m_po->rsSelect.paFields.GetSize(); ++iField )
- {
- // Get pointer to field structure
- OdbField* pField = (OdbField*)m_po->rsSelect.paFields[iField];
- // Bind
- pDefn = NULL;
- CheckErr( OCIDefineByPos( m_po->hpSelect,
- &pDefn, // function allocs and gives back a pointer to field definition
- m_po->hpErr,
- iField+1, // position in select starting at 1
- pField->pBuffer, // storage area for field result
- pField->wSize, // sizeof storage area
- pField->wType, // field type
- &pField->nInd, // indp, null indicator
- &pField->wLen, // rlenp
- NULL,
- OCI_DEFAULT )
- );
- }
- // Fetch
- FetchNext();
- return m_po->hr;
- }
- HRESULT COdb::FetchNext()
- {
- //
- // Fetch next row of select statement and parse the select list description
- //
- CheckErr( OCIStmtFetch( m_po->hpSelect,
- m_po->hpErr,
- 1, // 'nrows' i.e. max rows
- OCI_FETCH_NEXT,
- OCI_DEFAULT )
- );
- // Set result to 0 if no data returned
- m_csResults = "";
- m_po->rsSelect.m_nRows = 0;
- if ( SUCCEEDED(m_po->hr) && m_po->hr != OCI_NO_DATA )
- {
- // Set number of rows to 1 so IsEOS() will fail
- m_po->rsSelect.m_nRows = 1;
- // TEMPORARY: create a results string
- for ( int iField=0; iField<m_po->rsSelect.paFields.GetSize(); ++iField )
- {
- OdbField* pField = (OdbField*)m_po->rsSelect.paFields[iField];
- if ( ! m_csResults.IsEmpty() )
- m_csResults += ", ";
- m_csResults += pField->csName + "=" + GetField( iField );
- }
- }
- return m_po->hr;
- }
- BOOL COdb::IsEOS()
- {
- //
- // Return FALSE if m_nRows is 1, otherwise TRUE
- //
- BOOL bIsEOS = TRUE;
- if ( m_po->rsSelect.m_nRows )
- bIsEOS = FALSE;
- return bIsEOS;
- }
- CString COdb::GetField( int iField )
- {
- //
- // Return string from indexed field in rsSelect
- //
- if ( iField >= 0 && iField < m_po->rsSelect.paFields.GetSize() )
- {
- OdbField* pField= (OdbField*)m_po->rsSelect.paFields.GetAt( iField );
- return pField->Get();
- }
- return "";
- }
- CString COdb::GetField( CString csName )
- {
- //
- // Return string from named field in rsSelect
- //
- for ( int iField = 0; iField < m_po->rsSelect.paFields.GetSize(); ++iField )
- {
- OdbField* pField= (OdbField*)m_po->rsSelect.paFields.GetAt( iField );
- if ( pField->csName.CompareNoCase(csName) == 0 )
- return pField->Get();
- }
- return "";
- }
- BOOL COdb::GetField( int iField, CString& csName, CString& csValue, BOOL bQuotesIfValueRequires )
- {
- //
- // Return TRUE if iField in range, otherwise FALSE if iField is invalid
- // Get name and value, surround value with quotes if flag set
- //
- if ( iField >= 0 && iField < m_po->rsSelect.paFields.GetSize() )
- {
- OdbField* pField= (OdbField*)m_po->rsSelect.paFields.GetAt( iField );
- if ( bQuotesIfValueRequires && pField->bQuotedOnUpdate )
- csValue = "'" + ProcessQuotes(pField->Get()) + "'";
- else
- csValue = pField->Get();
- csName = pField->csName;
- return TRUE;
- }
- return FALSE;
- }
- void COdb::CleanWhitespace( CString& csStatement )
- {
- //
- // Sometimes crlfs can make a statement execute improperly
- // Replace whitespace with spaces because Exec and Select balk at crlfs etc
- //
- char* pStatement = csStatement.GetBuffer(0);
- while ( *pStatement )
- {
- if ( strchr( "rnt", *pStatement ) )
- *pStatement = ' ';
- ++pStatement;
- }
- csStatement.ReleaseBuffer();
- }
- CString COdb::ProcessQuotes( CString csValue )
- {
- //
- // If there is any chance the value contains quotes...
- // call this before placing quotes around the value
- //
- LPCTSTR szQuote = "'";
- // Does the string contain any single quotes?
- if ( csValue.Find( szQuote ) >= 0 )
- {
- // Loop through every occurence of szQuote
- int n = csValue.SpanExcluding(szQuote).GetLength();
- while ( n < csValue.GetLength() )
- {
- // Insert the extra quote
- csValue = csValue.Left(n) + csValue[n] + csValue.Mid(n);
- // Increment n past the two quotes
- // This is where we start the next search from
- n += 2;
- // Note that csValue is now a char longer!
- // Check in case quote was the last char in the string
- if ( n >= csValue.GetLength() )
- break;
- // Increment span count to next quote or end of string
- n += csValue.Mid(n).SpanExcluding(szQuote).GetLength();
- }
- }
- return csValue;
- }