ultimatepp/bazaar/plugin/gdal/port/cpl_odbc.cpp
cxl 23ff1e7e82 .gdal moved to bazaar
git-svn-id: svn://ultimatepp.org/upp/trunk@9273 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2015-12-07 13:36:24 +00:00

1828 lines
57 KiB
C++

/******************************************************************************
* $Id: cpl_odbc.cpp 29027 2015-04-26 18:29:41Z tamas $
*
* Project: OGR ODBC Driver
* Purpose: Declarations for ODBC Access Cover API.
* Author: Frank Warmerdam, warmerdam@pobox.com
*
******************************************************************************
* Copyright (c) 2003, Frank Warmerdam
* Copyright (c) 2008, Even Rouault <even dot rouault at mines-paris dot org>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
****************************************************************************/
#include "cpl_odbc.h"
#include "cpl_vsi.h"
#include "cpl_string.h"
#include "cpl_error.h"
#ifndef WIN32CE /* ODBC is not supported on Windows CE. */
CPL_CVSID("$Id: cpl_odbc.cpp 29027 2015-04-26 18:29:41Z tamas $");
#ifndef SQLColumns_TABLE_CAT
#define SQLColumns_TABLE_CAT 1
#define SQLColumns_TABLE_SCHEM 2
#define SQLColumns_TABLE_NAME 3
#define SQLColumns_COLUMN_NAME 4
#define SQLColumns_DATA_TYPE 5
#define SQLColumns_TYPE_NAME 6
#define SQLColumns_COLUMN_SIZE 7
#define SQLColumns_BUFFER_LENGTH 8
#define SQLColumns_DECIMAL_DIGITS 9
#define SQLColumns_NUM_PREC_RADIX 10
#define SQLColumns_NULLABLE 11
#define SQLColumns_REMARKS 12
#define SQLColumns_COLUMN_DEF 13
#define SQLColumns_SQL_DATA_TYPE 14
#define SQLColumns_SQL_DATETIME_SUB 15
#define SQLColumns_CHAR_OCTET_LENGTH 16
#define SQLColumns_ORDINAL_POSITION 17
#define SQLColumns_IS_NULLABLE 18
#endif /* ndef SQLColumns_TABLE_CAT */
/************************************************************************/
/* CPLODBCDriverInstaller() */
/************************************************************************/
CPLODBCDriverInstaller::CPLODBCDriverInstaller()
: m_nUsageCount(0)
{
memset( m_szPathOut, '\0', ODBC_FILENAME_MAX );
memset( m_szError, '\0', SQL_MAX_MESSAGE_LENGTH );
}
/************************************************************************/
/* InstallDriver() */
/************************************************************************/
int CPLODBCDriverInstaller::InstallDriver( const char* pszDriver,
CPL_UNUSED const char* pszPathIn,
WORD fRequest )
{
CPLAssert( NULL != pszDriver );
// Try to install driver to system-wide location
if ( FALSE == SQLInstallDriverEx( pszDriver, NULL, m_szPathOut,
ODBC_FILENAME_MAX, NULL, fRequest,
&m_nUsageCount ) )
{
const WORD nErrorNum = 1; // TODO - a function param?
CPL_UNUSED RETCODE cRet = SQL_ERROR;
// Failure is likely related to no write permissions to
// system-wide default location, so try to install to HOME
static char* pszEnvIni = NULL;
if (pszEnvIni == NULL)
{
// Read HOME location
char* pszEnvHome = NULL;
pszEnvHome = getenv("HOME");
CPLAssert( NULL != pszEnvHome );
CPLDebug( "ODBC", "HOME=%s", pszEnvHome );
// Set ODBCSYSINI variable pointing to HOME location
pszEnvIni = (char *)CPLMalloc( strlen(pszEnvHome) + 12 );
sprintf( pszEnvIni, "ODBCSYSINI=%s", pszEnvHome );
/* a 'man putenv' shows that we cannot free pszEnvIni */
/* because the pointer is used directly by putenv in old glibc */
putenv( pszEnvIni );
CPLDebug( "ODBC", "%s", pszEnvIni );
}
// Try to install ODBC driver in new location
if ( FALSE == SQLInstallDriverEx( pszDriver, NULL, m_szPathOut,
ODBC_FILENAME_MAX, NULL, fRequest,
&m_nUsageCount ) )
{
cRet = SQLInstallerError( nErrorNum, &m_nErrorCode,
m_szError, SQL_MAX_MESSAGE_LENGTH, NULL );
CPLAssert( SQL_SUCCESS == cRet || SQL_SUCCESS_WITH_INFO == cRet );
// FAIL
return FALSE;
}
}
// SUCCESS
return TRUE;
}
/************************************************************************/
/* RemoveDriver() */
/************************************************************************/
int CPLODBCDriverInstaller::RemoveDriver( const char* pszDriverName, int fRemoveDSN )
{
CPLAssert( NULL != pszDriverName );
if ( FALSE == SQLRemoveDriver( pszDriverName, fRemoveDSN, &m_nUsageCount ) )
{
const WORD nErrorNum = 1; // TODO - a function param?
// Retrieve error code and message
SQLInstallerError( nErrorNum, &m_nErrorCode,
m_szError, SQL_MAX_MESSAGE_LENGTH, NULL );
return FALSE;
}
// SUCCESS
return TRUE;
}
/************************************************************************/
/* CPLODBCSession() */
/************************************************************************/
CPLODBCSession::CPLODBCSession()
{
m_szLastError[0] = '\0';
m_hEnv = NULL;
m_hDBC = NULL;
m_bInTransaction = FALSE;
m_bAutoCommit = TRUE;
}
/************************************************************************/
/* ~CPLODBCSession() */
/************************************************************************/
CPLODBCSession::~CPLODBCSession()
{
CloseSession();
}
/************************************************************************/
/* CloseSession() */
/************************************************************************/
int CPLODBCSession::CloseSession()
{
if( m_hDBC!=NULL )
{
if ( IsInTransaction() )
CPLError( CE_Warning, CPLE_AppDefined, "Closing session with active transactions." );
CPLDebug( "ODBC", "SQLDisconnect()" );
SQLDisconnect( m_hDBC );
SQLFreeConnect( m_hDBC );
m_hDBC = NULL;
}
if( m_hEnv!=NULL )
{
SQLFreeEnv( m_hEnv );
m_hEnv = NULL;
}
return TRUE;
}
/************************************************************************/
/* ClearTransaction() */
/************************************************************************/
int CPLODBCSession::ClearTransaction()
{
#if (ODBCVER >= 0x0300)
if (m_bAutoCommit)
return TRUE;
SQLUINTEGER bAutoCommit;
/* See if we already in manual commit mode */
if ( Failed( SQLGetConnectAttr( m_hDBC, SQL_ATTR_AUTOCOMMIT, &bAutoCommit, sizeof(SQLUINTEGER), NULL) ) )
return FALSE;
if (bAutoCommit == SQL_AUTOCOMMIT_OFF)
{
/* switch the connection to auto commit mode (default) */
if( Failed( SQLSetConnectAttr( m_hDBC, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0 ) ) )
return FALSE;
}
m_bInTransaction = FALSE;
m_bAutoCommit = TRUE;
#endif
return TRUE;
}
/************************************************************************/
/* CommitTransaction() */
/************************************************************************/
int CPLODBCSession::BeginTransaction()
{
#if (ODBCVER >= 0x0300)
SQLUINTEGER bAutoCommit;
/* See if we already in manual commit mode */
if ( Failed( SQLGetConnectAttr( m_hDBC, SQL_ATTR_AUTOCOMMIT, &bAutoCommit, sizeof(SQLUINTEGER), NULL) ) )
return FALSE;
if (bAutoCommit == SQL_AUTOCOMMIT_ON)
{
/* switch the connection to manual commit mode */
if( Failed( SQLSetConnectAttr( m_hDBC, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, 0 ) ) )
return FALSE;
}
m_bInTransaction = TRUE;
m_bAutoCommit = FALSE;
#endif
return TRUE;
}
/************************************************************************/
/* CommitTransaction() */
/************************************************************************/
int CPLODBCSession::CommitTransaction()
{
#if (ODBCVER >= 0x0300)
if (m_bInTransaction)
{
if( Failed( SQLEndTran( SQL_HANDLE_DBC, m_hDBC, SQL_COMMIT ) ) )
{
return FALSE;
}
m_bInTransaction = FALSE;
}
#endif
return TRUE;
}
/************************************************************************/
/* RollbackTransaction() */
/************************************************************************/
int CPLODBCSession::RollbackTransaction()
{
#if (ODBCVER >= 0x0300)
if (m_bInTransaction)
{
m_bInTransaction = FALSE;
if( Failed( SQLEndTran( SQL_HANDLE_DBC, m_hDBC, SQL_ROLLBACK ) ) )
{
return FALSE;
}
}
#endif
return TRUE;
}
/************************************************************************/
/* Failed() */
/* */
/* Test if a return code indicates failure, return TRUE if that */
/* is the case. Also update error text. */
/************************************************************************/
int CPLODBCSession::Failed( int nRetCode, HSTMT hStmt )
{
SQLCHAR achSQLState[SQL_MAX_MESSAGE_LENGTH];
SQLINTEGER nNativeError;
SQLSMALLINT nTextLength=0;
m_szLastError[0] = '\0';
if( nRetCode == SQL_SUCCESS || nRetCode == SQL_SUCCESS_WITH_INFO )
return FALSE;
SQLError( m_hEnv, m_hDBC, hStmt, achSQLState, &nNativeError,
(SQLCHAR *) m_szLastError, sizeof(m_szLastError)-1,
&nTextLength );
m_szLastError[nTextLength] = '\0';
if( nRetCode == SQL_ERROR && m_bInTransaction )
RollbackTransaction();
return TRUE;
}
/************************************************************************/
/* EstablishSession() */
/************************************************************************/
/**
* Connect to database and logon.
*
* @param pszDSN The name of the DSN being used to connect. This is not
* optional.
*
* @param pszUserid the userid to logon as, may be NULL if not not required,
* or provided by the DSN.
*
* @param pszPassword the password to logon with. May be NULL if not required
* or provided by the DSN.
*
* @return TRUE on success or FALSE on failure. Call GetLastError() to get
* details on failure.
*/
int CPLODBCSession::EstablishSession( const char *pszDSN,
const char *pszUserid,
const char *pszPassword )
{
CloseSession();
if( Failed( SQLAllocEnv( &m_hEnv ) ) )
return FALSE;
if( Failed( SQLAllocConnect( m_hEnv, &m_hDBC ) ) )
{
CloseSession();
return FALSE;
}
SQLSetConnectOption( m_hDBC,SQL_LOGIN_TIMEOUT,30 );
if( pszUserid == NULL )
pszUserid = "";
if( pszPassword == NULL )
pszPassword = "";
int bFailed;
if( strstr(pszDSN,"=") != NULL )
{
SQLCHAR szOutConnString[1024];
SQLSMALLINT nOutConnStringLen = 0;
CPLDebug( "ODBC", "SQLDriverConnect(%s)", pszDSN );
bFailed = Failed(
SQLDriverConnect( m_hDBC, NULL,
(SQLCHAR *) pszDSN, (SQLSMALLINT)strlen(pszDSN),
szOutConnString, sizeof(szOutConnString),
&nOutConnStringLen, SQL_DRIVER_NOPROMPT ) );
}
else
{
CPLDebug( "ODBC", "SQLConnect(%s)", pszDSN );
bFailed = Failed(
SQLConnect( m_hDBC, (SQLCHAR *) pszDSN, SQL_NTS,
(SQLCHAR *) pszUserid, SQL_NTS,
(SQLCHAR *) pszPassword, SQL_NTS ) );
}
if( bFailed )
{
CPLDebug( "ODBC", "... failed: %s", GetLastError() );
CloseSession();
return FALSE;
}
return TRUE;
}
/************************************************************************/
/* GetLastError() */
/************************************************************************/
/**
* Returns the last ODBC error message.
*
* @return pointer to an internal buffer with the error message in it.
* Do not free or alter. Will be an empty (but not NULL) string if there is
* no pending error info.
*/
const char *CPLODBCSession::GetLastError()
{
return m_szLastError;
}
/************************************************************************/
/* ==================================================================== */
/* CPLODBCStatement */
/* ==================================================================== */
/************************************************************************/
/************************************************************************/
/* CPLODBCStatement() */
/************************************************************************/
CPLODBCStatement::CPLODBCStatement( CPLODBCSession *poSession )
{
m_poSession = poSession;
if( Failed(
SQLAllocStmt( poSession->GetConnection(), &m_hStmt ) ) )
{
m_hStmt = NULL;
return;
}
m_nColCount = 0;
m_papszColNames = NULL;
m_panColType = NULL;
m_papszColTypeNames = NULL;
m_panColSize = NULL;
m_panColPrecision = NULL;
m_panColNullable = NULL;
m_papszColColumnDef = NULL;
m_papszColValues = NULL;
m_panColValueLengths = NULL;
m_pszStatement = NULL;
m_nStatementMax = 0;
m_nStatementLen = 0;
}
/************************************************************************/
/* ~CPLODBCStatement() */
/************************************************************************/
CPLODBCStatement::~CPLODBCStatement()
{
Clear();
if( m_hStmt != NULL )
SQLFreeStmt( m_hStmt, SQL_DROP );
}
/************************************************************************/
/* ExecuteSQL() */
/************************************************************************/
/**
* Execute an SQL statement.
*
* This method will execute the passed (or stored) SQL statement,
* and initialize information about the resultset if there is one.
* If a NULL statement is passed, the internal stored statement that
* has been previously set via Append() or Appendf() calls will be used.
*
* @param pszStatement the SQL statement to execute, or NULL if the
* internally saved one should be used.
*
* @return TRUE on success or FALSE if there is an error. Error details
* can be fetched with OGRODBCSession::GetLastError().
*/
int CPLODBCStatement::ExecuteSQL( const char *pszStatement )
{
if( m_poSession == NULL || m_hStmt == NULL )
{
// we should post an error.
return FALSE;
}
if( pszStatement != NULL )
{
Clear();
Append( pszStatement );
}
#if (ODBCVER >= 0x0300)
if ( !m_poSession->IsInTransaction() )
{
/* commit pending transactions and set to autocommit mode*/
m_poSession->ClearTransaction();
}
#endif
if( Failed(
SQLExecDirect( m_hStmt, (SQLCHAR *) m_pszStatement, SQL_NTS ) ) )
return FALSE;
return CollectResultsInfo();
}
/************************************************************************/
/* CollectResultsInfo() */
/************************************************************************/
int CPLODBCStatement::CollectResultsInfo()
{
if( m_poSession == NULL || m_hStmt == NULL )
{
// we should post an error.
return FALSE;
}
if( Failed( SQLNumResultCols(m_hStmt,&m_nColCount) ) )
return FALSE;
/* -------------------------------------------------------------------- */
/* Allocate per column information. */
/* -------------------------------------------------------------------- */
m_papszColNames = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1));
m_papszColValues = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1));
m_panColValueLengths = (_SQLLEN *) CPLCalloc(sizeof(_SQLLEN),(m_nColCount+1));
m_panColType = (SQLSMALLINT *) CPLCalloc(sizeof(SQLSMALLINT),m_nColCount);
m_papszColTypeNames = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1));
m_panColSize = (_SQLULEN *) CPLCalloc(sizeof(_SQLULEN),m_nColCount);
m_panColPrecision = (SQLSMALLINT *) CPLCalloc(sizeof(SQLSMALLINT),m_nColCount);
m_panColNullable = (SQLSMALLINT *) CPLCalloc(sizeof(SQLSMALLINT),m_nColCount);
m_papszColColumnDef = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1));
/* -------------------------------------------------------------------- */
/* Fetch column descriptions. */
/* -------------------------------------------------------------------- */
for( SQLUSMALLINT iCol = 0; iCol < m_nColCount; iCol++ )
{
SQLCHAR szName[256];
SQLSMALLINT nNameLength = 0;
if ( Failed( SQLDescribeCol(m_hStmt, iCol+1,
szName, sizeof(szName), &nNameLength,
m_panColType + iCol,
m_panColSize + iCol,
m_panColPrecision + iCol,
m_panColNullable + iCol) ) )
return FALSE;
szName[nNameLength] = '\0'; // Paranoid; the string should be
// null-terminated by the driver
m_papszColNames[iCol] = CPLStrdup((const char*)szName);
// SQLDescribeCol() fetches just a subset of column attributes.
// In addition to above data we need data type name.
if ( Failed( SQLColAttribute(m_hStmt, iCol + 1, SQL_DESC_TYPE_NAME,
szName, sizeof(szName),
&nNameLength, NULL) ) )
return FALSE;
szName[nNameLength] = '\0'; // Paranoid
m_papszColTypeNames[iCol] = CPLStrdup((const char*)szName);
// CPLDebug( "ODBC", "%s %s %d", m_papszColNames[iCol],
// szName, m_panColType[iCol] );
}
return TRUE;
}
/************************************************************************/
/* GetRowCountAffected() */
/************************************************************************/
int CPLODBCStatement::GetRowCountAffected()
{
SQLLEN nResultCount=0;
SQLRowCount( m_hStmt, &nResultCount );
return (int)nResultCount;
}
/************************************************************************/
/* GetColCount() */
/************************************************************************/
/**
* Fetch the resultset column count.
*
* @return the column count, or zero if there is no resultset.
*/
int CPLODBCStatement::GetColCount()
{
return m_nColCount;
}
/************************************************************************/
/* GetColName() */
/************************************************************************/
/**
* Fetch a column name.
*
* @param iCol the zero based column index.
*
* @return NULL on failure (out of bounds column), or a pointer to an
* internal copy of the column name.
*/
const char *CPLODBCStatement::GetColName( int iCol )
{
if( iCol < 0 || iCol >= m_nColCount )
return NULL;
else
return m_papszColNames[iCol];
}
/************************************************************************/
/* GetColType() */
/************************************************************************/
/**
* Fetch a column data type.
*
* The return type code is a an ODBC SQL_ code, one of SQL_UNKNOWN_TYPE,
* SQL_CHAR, SQL_NUMERIC, SQL_DECIMAL, SQL_INTEGER, SQL_SMALLINT, SQL_FLOAT,
* SQL_REAL, SQL_DOUBLE, SQL_DATETIME, SQL_VARCHAR, SQL_TYPE_DATE,
* SQL_TYPE_TIME, SQL_TYPE_TIMESTAMPT.
*
* @param iCol the zero based column index.
*
* @return type code or -1 if the column is illegal.
*/
short CPLODBCStatement::GetColType( int iCol )
{
if( iCol < 0 || iCol >= m_nColCount )
return -1;
else
return m_panColType[iCol];
}
/************************************************************************/
/* GetColTypeName() */
/************************************************************************/
/**
* Fetch a column data type name.
*
* Returns data source-dependent data type name; for example, "CHAR",
* "VARCHAR", "MONEY", "LONG VARBINAR", or "CHAR ( ) FOR BIT DATA".
*
* @param iCol the zero based column index.
*
* @return NULL on failure (out of bounds column), or a pointer to an
* internal copy of the column dat type name.
*/
const char *CPLODBCStatement::GetColTypeName( int iCol )
{
if( iCol < 0 || iCol >= m_nColCount )
return NULL;
else
return m_papszColTypeNames[iCol];
}
/************************************************************************/
/* GetColSize() */
/************************************************************************/
/**
* Fetch the column width.
*
* @param iCol the zero based column index.
*
* @return column width, zero for unknown width columns.
*/
short CPLODBCStatement::GetColSize( int iCol )
{
if( iCol < 0 || iCol >= m_nColCount )
return -1;
else
return (short) m_panColSize[iCol];
}
/************************************************************************/
/* GetColPrecision() */
/************************************************************************/
/**
* Fetch the column precision.
*
* @param iCol the zero based column index.
*
* @return column precision, may be zero or the same as column size for
* columns to which it does not apply.
*/
short CPLODBCStatement::GetColPrecision( int iCol )
{
if( iCol < 0 || iCol >= m_nColCount )
return -1;
else
return m_panColPrecision[iCol];
}
/************************************************************************/
/* GetColNullable() */
/************************************************************************/
/**
* Fetch the column nullability.
*
* @param iCol the zero based column index.
*
* @return TRUE if the column may contains or FALSE otherwise.
*/
short CPLODBCStatement::GetColNullable( int iCol )
{
if( iCol < 0 || iCol >= m_nColCount )
return -1;
else
return m_panColNullable[iCol];
}
/************************************************************************/
/* GetColColumnDef() */
/************************************************************************/
/**
* Fetch a column default value.
*
* Returns the default value of a column.
*
* @param iCol the zero based column index.
*
* @return NULL if the default value is not dpecified
* or the internal copy of the default value.
*/
const char *CPLODBCStatement::GetColColumnDef( int iCol )
{
if( iCol < 0 || iCol >= m_nColCount )
return NULL;
else
return m_papszColColumnDef[iCol];
}
/************************************************************************/
/* Fetch() */
/************************************************************************/
/**
* Fetch a new record.
*
* Requests the next row in the current resultset using the SQLFetchScroll()
* call. Note that many ODBC drivers only support the default forward
* fetching one record at a time. Only SQL_FETCH_NEXT (the default) should
* be considered reliable on all drivers.
*
* Currently it isn't clear how to determine whether an error or a normal
* out of data condition has occured if Fetch() fails.
*
* @param nOrientation One of SQL_FETCH_NEXT, SQL_FETCH_LAST, SQL_FETCH_PRIOR,
* SQL_FETCH_ABSOLUTE, or SQL_FETCH_RELATIVE (default is SQL_FETCH_NEXT).
*
* @param nOffset the offset (number of records), ignored for some
* orientations.
*
* @return TRUE if a new row is successfully fetched, or FALSE if not.
*/
int CPLODBCStatement::Fetch( int nOrientation, int nOffset )
{
ClearColumnData();
if( m_hStmt == NULL || m_nColCount < 1 )
return FALSE;
/* -------------------------------------------------------------------- */
/* Fetch a new row. Note that some brain dead drives (such as */
/* the unixodbc text file driver) don't implement */
/* SQLScrollFetch(), so we try to stick to SQLFetch() if we */
/* can). */
/* -------------------------------------------------------------------- */
SQLRETURN nRetCode;
if( nOrientation == SQL_FETCH_NEXT && nOffset == 0 )
{
nRetCode = SQLFetch( m_hStmt );
if( Failed(nRetCode) )
{
if ( nRetCode != SQL_NO_DATA )
{
CPLError( CE_Failure, CPLE_AppDefined, "%s",
m_poSession->GetLastError() );
}
return FALSE;
}
}
else
{
nRetCode = SQLFetchScroll(m_hStmt, (SQLSMALLINT) nOrientation, nOffset);
if( Failed(nRetCode) )
{
if ( nRetCode == SQL_NO_DATA )
{
CPLError( CE_Failure, CPLE_AppDefined, "%s",
m_poSession->GetLastError() );
}
return FALSE;
}
}
/* -------------------------------------------------------------------- */
/* Pull out all the column values. */
/* -------------------------------------------------------------------- */
SQLSMALLINT iCol;
for( iCol = 0; iCol < m_nColCount; iCol++ )
{
char szWrkData[513];
_SQLLEN cbDataLen;
SQLSMALLINT nFetchType = GetTypeMapping( m_panColType[iCol] );
// Handle values other than WCHAR and BINARY as CHAR.
if( nFetchType != SQL_C_BINARY && nFetchType != SQL_C_WCHAR )
nFetchType = SQL_C_CHAR;
szWrkData[0] = '\0';
szWrkData[sizeof(szWrkData)-1] = '\0';
nRetCode = SQLGetData( m_hStmt, iCol + 1, nFetchType,
szWrkData, sizeof(szWrkData)-1,
&cbDataLen );
/* SQLGetData() is giving garbage values in the first 4 bytes of cbDataLen *
* in some architectures. Converting it to (int) discards the unnecessary *
* bytes. This should not be a problem unless the buffer size reaches *
* 2GB. (#3385) */
cbDataLen = (int) cbDataLen;
if( Failed( nRetCode ) )
{
if ( nRetCode == SQL_NO_DATA )
{
CPLError( CE_Failure, CPLE_AppDefined, "%s",
m_poSession->GetLastError() );
}
return FALSE;
}
if( cbDataLen == SQL_NULL_DATA )
{
m_papszColValues[iCol] = NULL;
m_panColValueLengths[iCol] = 0;
}
// assume big result: should check for state=SQLSATE 01004.
else if( nRetCode == SQL_SUCCESS_WITH_INFO )
{
if( cbDataLen >= (_SQLLEN)(sizeof(szWrkData)-1) )
{
cbDataLen = (_SQLLEN)(sizeof(szWrkData)-1);
if (nFetchType == SQL_C_CHAR)
while ((cbDataLen > 1) && (szWrkData[cbDataLen - 1] == 0))
--cbDataLen; // trimming the extra terminators: bug 990
else if (nFetchType == SQL_C_WCHAR)
while ((cbDataLen > 1) && (szWrkData[cbDataLen - 1] == 0)
&& (szWrkData[cbDataLen - 2] == 0))
cbDataLen -= 2; // trimming the extra terminators
}
m_papszColValues[iCol] = (char *) CPLMalloc(cbDataLen+2);
memcpy( m_papszColValues[iCol], szWrkData, cbDataLen );
m_papszColValues[iCol][cbDataLen] = '\0';
m_papszColValues[iCol][cbDataLen+1] = '\0';
m_panColValueLengths[iCol] = cbDataLen;
while( TRUE )
{
_SQLLEN nChunkLen;
nRetCode = SQLGetData( m_hStmt, (SQLUSMALLINT) iCol+1,
nFetchType,
szWrkData, sizeof(szWrkData)-1,
&cbDataLen );
if( nRetCode == SQL_NO_DATA )
break;
if( Failed( nRetCode ) )
{
CPLError( CE_Failure, CPLE_AppDefined, "%s",
m_poSession->GetLastError() );
return FALSE;
}
if( cbDataLen >= (int) (sizeof(szWrkData) - 1)
|| cbDataLen == SQL_NO_TOTAL )
{
nChunkLen = sizeof(szWrkData)-1;
if (nFetchType == SQL_C_CHAR)
while ( (nChunkLen > 1)
&& (szWrkData[nChunkLen - 1] == 0) )
--nChunkLen; // trimming the extra terminators
else if (nFetchType == SQL_C_WCHAR)
while ( (nChunkLen > 1)
&& (szWrkData[nChunkLen - 1] == 0)
&& (szWrkData[nChunkLen - 2] == 0) )
nChunkLen -= 2; // trimming the extra terminators
}
else
nChunkLen = cbDataLen;
szWrkData[nChunkLen] = '\0';
m_papszColValues[iCol] = (char *)
CPLRealloc( m_papszColValues[iCol],
m_panColValueLengths[iCol] + nChunkLen + 2 );
memcpy( m_papszColValues[iCol] + m_panColValueLengths[iCol],
szWrkData, nChunkLen );
m_panColValueLengths[iCol] += nChunkLen;
m_papszColValues[iCol][m_panColValueLengths[iCol]] = '\0';
m_papszColValues[iCol][m_panColValueLengths[iCol]+1] = '\0';
}
}
else
{
m_panColValueLengths[iCol] = cbDataLen;
m_papszColValues[iCol] = (char *) CPLMalloc(cbDataLen+2);
memcpy( m_papszColValues[iCol], szWrkData, cbDataLen );
m_papszColValues[iCol][cbDataLen] = '\0';
m_papszColValues[iCol][cbDataLen+1] = '\0';
}
// Trim white space off end, if there is any.
if( nFetchType == SQL_C_CHAR && m_papszColValues[iCol] != NULL )
{
char *pszTarget = m_papszColValues[iCol];
size_t iEnd = strlen(pszTarget);
while ( iEnd > 0 && pszTarget[iEnd - 1] == ' ' )
pszTarget[--iEnd] = '\0';
}
// Convert WCHAR to UTF-8, assuming the WCHAR is UCS-2.
if( nFetchType == SQL_C_WCHAR && m_papszColValues[iCol] != NULL
&& m_panColValueLengths[iCol] > 0 )
{
wchar_t *pwszSrc = (wchar_t *) m_papszColValues[iCol];
m_papszColValues[iCol] =
CPLRecodeFromWChar( pwszSrc, CPL_ENC_UCS2, CPL_ENC_UTF8 );
m_panColValueLengths[iCol] = strlen(m_papszColValues[iCol]);
CPLFree( pwszSrc );
}
}
return TRUE;
}
/************************************************************************/
/* GetColData() */
/************************************************************************/
/**
* Fetch column data.
*
* Fetches the data contents of the requested column for the currently loaded
* row. The result is returned as a string regardless of the column type.
* NULL is returned if an illegal column is given, or if the actual column
* is "NULL".
*
* @param iCol the zero based column to fetch.
*
* @param pszDefault the value to return if the column does not exist, or is
* NULL. Defaults to NULL.
*
* @return pointer to internal column data or NULL on failure.
*/
const char *CPLODBCStatement::GetColData( int iCol, const char *pszDefault )
{
if( iCol < 0 || iCol >= m_nColCount )
return pszDefault;
else if( m_papszColValues[iCol] != NULL )
return m_papszColValues[iCol];
else
return pszDefault;
}
/************************************************************************/
/* GetColData() */
/************************************************************************/
/**
* Fetch column data.
*
* Fetches the data contents of the requested column for the currently loaded
* row. The result is returned as a string regardless of the column type.
* NULL is returned if an illegal column is given, or if the actual column
* is "NULL".
*
* @param pszColName the name of the column requested.
*
* @param pszDefault the value to return if the column does not exist, or is
* NULL. Defaults to NULL.
*
* @return pointer to internal column data or NULL on failure.
*/
const char *CPLODBCStatement::GetColData( const char *pszColName,
const char *pszDefault )
{
int iCol = GetColId( pszColName );
if( iCol == -1 )
return pszDefault;
else
return GetColData( iCol, pszDefault );
}
/************************************************************************/
/* GetColDataLength() */
/************************************************************************/
int CPLODBCStatement::GetColDataLength( int iCol )
{
if( iCol < 0 || iCol >= m_nColCount )
return 0;
else if( m_papszColValues[iCol] != NULL )
return (int)m_panColValueLengths[iCol];
else
return 0;
}
/************************************************************************/
/* GetColId() */
/************************************************************************/
/**
* Fetch column index.
*
* Gets the column index corresponding with the passed name. The
* name comparisons are case insensitive.
*
* @param pszColName the name to search for.
*
* @return the column index, or -1 if not found.
*/
int CPLODBCStatement::GetColId( const char *pszColName )
{
for( SQLSMALLINT iCol = 0; iCol < m_nColCount; iCol++ )
if( EQUAL(pszColName, m_papszColNames[iCol]) )
return iCol;
return -1;
}
/************************************************************************/
/* ClearColumnData() */
/************************************************************************/
void CPLODBCStatement::ClearColumnData()
{
if( m_nColCount > 0 )
{
for( int iCol = 0; iCol < m_nColCount; iCol++ )
{
if( m_papszColValues[iCol] != NULL )
{
CPLFree( m_papszColValues[iCol] );
m_papszColValues[iCol] = NULL;
}
}
}
}
/************************************************************************/
/* Failed() */
/************************************************************************/
int CPLODBCStatement::Failed( int nResultCode )
{
if( m_poSession != NULL )
return m_poSession->Failed( nResultCode, m_hStmt );
return TRUE;
}
/************************************************************************/
/* Append(const char *) */
/************************************************************************/
/**
* Append text to internal command.
*
* The passed text is appended to the internal SQL command text.
*
* @param pszText text to append.
*/
void CPLODBCStatement::Append( const char *pszText )
{
size_t nTextLen = strlen(pszText);
if( m_nStatementMax < m_nStatementLen + nTextLen + 1 )
{
m_nStatementMax = (m_nStatementLen + nTextLen) * 2 + 100;
if( m_pszStatement == NULL )
{
m_pszStatement = (char *) VSIMalloc(m_nStatementMax);
m_pszStatement[0] = '\0';
}
else
{
m_pszStatement = (char *) VSIRealloc(m_pszStatement, m_nStatementMax);
}
}
strcpy( m_pszStatement + m_nStatementLen, pszText );
m_nStatementLen += nTextLen;
}
/************************************************************************/
/* AppendEscaped(const char *) */
/************************************************************************/
/**
* Append text to internal command.
*
* The passed text is appended to the internal SQL command text after
* escaping any special characters so it can be used as a character string
* in an SQL statement.
*
* @param pszText text to append.
*/
void CPLODBCStatement::AppendEscaped( const char *pszText )
{
size_t iIn, iOut ,nTextLen = strlen(pszText);
char *pszEscapedText = (char *) VSIMalloc(nTextLen*2 + 1);
for( iIn = 0, iOut = 0; iIn < nTextLen; iIn++ )
{
switch( pszText[iIn] )
{
case '\'':
case '\\':
pszEscapedText[iOut++] = '\\';
pszEscapedText[iOut++] = pszText[iIn];
break;
default:
pszEscapedText[iOut++] = pszText[iIn];
break;
}
}
pszEscapedText[iOut] = '\0';
Append( pszEscapedText );
CPLFree( pszEscapedText );
}
/************************************************************************/
/* Append(int) */
/************************************************************************/
/**
* Append to internal command.
*
* The passed value is formatted and appended to the internal SQL command text.
*
* @param nValue value to append to the command.
*/
void CPLODBCStatement::Append( int nValue )
{
char szFormattedValue[100];
sprintf( szFormattedValue, "%d", nValue );
Append( szFormattedValue );
}
/************************************************************************/
/* Append(double) */
/************************************************************************/
/**
* Append to internal command.
*
* The passed value is formatted and appended to the internal SQL command text.
*
* @param dfValue value to append to the command.
*/
void CPLODBCStatement::Append( double dfValue )
{
char szFormattedValue[100];
sprintf( szFormattedValue, "%24g", dfValue );
Append( szFormattedValue );
}
/************************************************************************/
/* Appendf() */
/************************************************************************/
/**
* Append to internal command.
*
* The passed format is used to format other arguments and the result is
* appended to the internal command text. Long results may not be formatted
* properly, and should be appended with the direct Append() methods.
*
* @param pszFormat printf() style format string.
*
* @return FALSE if formatting fails dueto result being too large.
*/
int CPLODBCStatement::Appendf( const char *pszFormat, ... )
{
va_list args;
char szFormattedText[8000];
int bSuccess;
va_start( args, pszFormat );
#if defined(HAVE_VSNPRINTF)
bSuccess = vsnprintf( szFormattedText, sizeof(szFormattedText)-1,
pszFormat, args ) < (int) sizeof(szFormattedText)-1;
#else
vsprintf( szFormattedText, pszFormat, args );
bSuccess = TRUE;
#endif
va_end( args );
if( bSuccess )
Append( szFormattedText );
return bSuccess;
}
/************************************************************************/
/* Clear() */
/************************************************************************/
/**
* Clear internal command text and result set definitions.
*/
void CPLODBCStatement::Clear()
{
/* Closing the cursor if opened */
if( m_hStmt != NULL )
SQLFreeStmt( m_hStmt, SQL_CLOSE );
ClearColumnData();
if( m_pszStatement != NULL )
{
CPLFree( m_pszStatement );
m_pszStatement = NULL;
}
m_nStatementLen = 0;
m_nStatementMax = 0;
m_nColCount = 0;
if( m_papszColNames )
{
CPLFree( m_panColType );
m_panColType = NULL;
CSLDestroy( m_papszColTypeNames );
m_papszColTypeNames = NULL;
CPLFree( m_panColSize );
m_panColSize = NULL;
CPLFree( m_panColPrecision );
m_panColPrecision = NULL;
CPLFree( m_panColNullable );
m_panColNullable = NULL;
CSLDestroy( m_papszColColumnDef );
m_papszColColumnDef = NULL;
CSLDestroy( m_papszColNames );
m_papszColNames = NULL;
CPLFree( m_papszColValues );
m_papszColValues = NULL;
CPLFree( m_panColValueLengths );
m_panColValueLengths = NULL;
}
}
/************************************************************************/
/* GetColumns() */
/************************************************************************/
/**
* Fetch column definitions for a table.
*
* The SQLColumn() method is used to fetch the definitions for the columns
* of a table (or other queriable object such as a view). The column
* definitions are digested and used to populate the CPLODBCStatement
* column definitions essentially as if a "SELECT * FROM tablename" had
* been done; however, no resultset will be available.
*
* @param pszTable the name of the table to query information on. This
* should not be empty.
*
* @param pszCatalog the catalog to find the table in, use NULL (the
* default) if no catalog is available.
*
* @param pszSchema the schema to find the table in, use NULL (the
* default) if no schema is available.
*
* @return TRUE on success or FALSE on failure.
*/
int CPLODBCStatement::GetColumns( const char *pszTable,
const char *pszCatalog,
const char *pszSchema )
{
#ifdef notdef
if( pszCatalog == NULL )
pszCatalog = "";
if( pszSchema == NULL )
pszSchema = "";
#endif
#if (ODBCVER >= 0x0300)
if ( !m_poSession->IsInTransaction() )
{
/* commit pending transactions and set to autocommit mode*/
m_poSession->ClearTransaction();
}
#endif
/* -------------------------------------------------------------------- */
/* Fetch columns resultset for this table. */
/* -------------------------------------------------------------------- */
if( Failed( SQLColumns( m_hStmt,
(SQLCHAR *) pszCatalog, SQL_NTS,
(SQLCHAR *) pszSchema, SQL_NTS,
(SQLCHAR *) pszTable, SQL_NTS,
(SQLCHAR *) NULL /* "" */, SQL_NTS ) ) )
return FALSE;
/* -------------------------------------------------------------------- */
/* Allocate per column information. */
/* -------------------------------------------------------------------- */
#ifdef notdef
// SQLRowCount() is too unreliable (with unixodbc on AIX for instance)
// so we now avoid it.
SQLINTEGER nResultCount=0;
if( Failed(SQLRowCount( m_hStmt, &nResultCount ) ) )
nResultCount = 0;
if( nResultCount < 1 )
m_nColCount = 500; // Hopefully lots.
else
m_nColCount = nResultCount;
#endif
m_nColCount = 500;
m_papszColNames = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1));
m_papszColValues = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1));
m_panColType = (SQLSMALLINT *) CPLCalloc(sizeof(SQLSMALLINT),m_nColCount);
m_papszColTypeNames = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1));
m_panColSize = (_SQLULEN *) CPLCalloc(sizeof(_SQLULEN),m_nColCount);
m_panColPrecision = (SQLSMALLINT *) CPLCalloc(sizeof(SQLSMALLINT),m_nColCount);
m_panColNullable = (SQLSMALLINT *) CPLCalloc(sizeof(SQLSMALLINT),m_nColCount);
m_papszColColumnDef = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1));
/* -------------------------------------------------------------------- */
/* Establish columns to use for key information. */
/* -------------------------------------------------------------------- */
SQLUSMALLINT iCol;
for( iCol = 0; iCol < m_nColCount; iCol++ )
{
char szWrkData[8193];
_SQLLEN cbDataLen;
if( Failed( SQLFetch( m_hStmt ) ) )
{
m_nColCount = iCol;
break;
}
szWrkData[0] = '\0';
SQLGetData( m_hStmt, SQLColumns_COLUMN_NAME, SQL_C_CHAR,
szWrkData, sizeof(szWrkData)-1, &cbDataLen );
m_papszColNames[iCol] = CPLStrdup(szWrkData);
SQLGetData( m_hStmt, SQLColumns_DATA_TYPE, SQL_C_CHAR,
szWrkData, sizeof(szWrkData)-1, &cbDataLen );
m_panColType[iCol] = (short) atoi(szWrkData);
SQLGetData( m_hStmt, SQLColumns_TYPE_NAME, SQL_C_CHAR,
szWrkData, sizeof(szWrkData)-1, &cbDataLen );
m_papszColTypeNames[iCol] = CPLStrdup(szWrkData);
SQLGetData( m_hStmt, SQLColumns_COLUMN_SIZE, SQL_C_CHAR,
szWrkData, sizeof(szWrkData)-1, &cbDataLen );
m_panColSize[iCol] = atoi(szWrkData);
SQLGetData( m_hStmt, SQLColumns_DECIMAL_DIGITS, SQL_C_CHAR,
szWrkData, sizeof(szWrkData)-1, &cbDataLen );
m_panColPrecision[iCol] = (short) atoi(szWrkData);
SQLGetData( m_hStmt, SQLColumns_NULLABLE, SQL_C_CHAR,
szWrkData, sizeof(szWrkData)-1, &cbDataLen );
m_panColNullable[iCol] = atoi(szWrkData) == SQL_NULLABLE;
#if (ODBCVER >= 0x0300)
SQLGetData( m_hStmt, SQLColumns_COLUMN_DEF, SQL_C_CHAR,
szWrkData, sizeof(szWrkData)-1, &cbDataLen );
if (cbDataLen > 0)
m_papszColColumnDef[iCol] = CPLStrdup(szWrkData);
#endif
}
return TRUE;
}
/************************************************************************/
/* GetPrimaryKeys() */
/************************************************************************/
/**
* Fetch primary keys for a table.
*
* The SQLPrimaryKeys() function is used to fetch a list of fields
* forming the primary key. The result is returned as a result set matching
* the SQLPrimaryKeys() function result set. The 4th column in the result
* set is the column name of the key, and if the result set contains only
* one record then that single field will be the complete primary key.
*
* @param pszTable the name of the table to query information on. This
* should not be empty.
*
* @param pszCatalog the catalog to find the table in, use NULL (the
* default) if no catalog is available.
*
* @param pszSchema the schema to find the table in, use NULL (the
* default) if no schema is available.
*
* @return TRUE on success or FALSE on failure.
*/
int CPLODBCStatement::GetPrimaryKeys( const char *pszTable,
const char *pszCatalog,
const char *pszSchema )
{
if( pszCatalog == NULL )
pszCatalog = "";
if( pszSchema == NULL )
pszSchema = "";
#if (ODBCVER >= 0x0300)
if ( !m_poSession->IsInTransaction() )
{
/* commit pending transactions and set to autocommit mode*/
m_poSession->ClearTransaction();
}
#endif
/* -------------------------------------------------------------------- */
/* Fetch columns resultset for this table. */
/* -------------------------------------------------------------------- */
if( Failed( SQLPrimaryKeys( m_hStmt,
(SQLCHAR *) pszCatalog, SQL_NTS,
(SQLCHAR *) pszSchema, SQL_NTS,
(SQLCHAR *) pszTable, SQL_NTS ) ) )
return FALSE;
else
return CollectResultsInfo();
}
/************************************************************************/
/* GetTables() */
/************************************************************************/
/**
* Fetch tables in database.
*
* The SQLTables() function is used to fetch a list tables in the
* database. The result is returned as a result set matching
* the SQLTables() function result set. The 3rd column in the result
* set is the table name. Only tables of type "TABLE" are returned.
*
* @param pszCatalog the catalog to find the table in, use NULL (the
* default) if no catalog is available.
*
* @param pszSchema the schema to find the table in, use NULL (the
* default) if no schema is available.
*
* @return TRUE on success or FALSE on failure.
*/
int CPLODBCStatement::GetTables( const char *pszCatalog,
const char *pszSchema )
{
CPLDebug( "ODBC", "CatalogNameL: %s\nSchema name: %s\n",
pszCatalog, pszSchema );
#if (ODBCVER >= 0x0300)
if ( !m_poSession->IsInTransaction() )
{
/* commit pending transactions and set to autocommit mode*/
m_poSession->ClearTransaction();
}
#endif
/* -------------------------------------------------------------------- */
/* Fetch columns resultset for this table. */
/* -------------------------------------------------------------------- */
if( Failed( SQLTables( m_hStmt,
(SQLCHAR *) pszCatalog, SQL_NTS,
(SQLCHAR *) pszSchema, SQL_NTS,
(SQLCHAR *) NULL, SQL_NTS,
(SQLCHAR *) "'TABLE','VIEW'", SQL_NTS ) ) )
return FALSE;
else
return CollectResultsInfo();
}
/************************************************************************/
/* DumpResult() */
/************************************************************************/
/**
* Dump resultset to file.
*
* The contents of the current resultset are dumped in a simply formatted
* form to the provided file. If requested, the schema definition will
* be written first.
*
* @param fp the file to write to. stdout or stderr are acceptable.
*
* @param bShowSchema TRUE to force writing schema information for the rowset
* before the rowset data itself. Default is FALSE.
*/
void CPLODBCStatement::DumpResult( FILE *fp, int bShowSchema )
{
int iCol;
/* -------------------------------------------------------------------- */
/* Display schema */
/* -------------------------------------------------------------------- */
if( bShowSchema )
{
fprintf( fp, "Column Definitions:\n" );
for( iCol = 0; iCol < GetColCount(); iCol++ )
{
fprintf( fp, " %2d: %-24s ", iCol, GetColName(iCol) );
if( GetColPrecision(iCol) > 0
&& GetColPrecision(iCol) != GetColSize(iCol) )
fprintf( fp, " Size:%3d.%d",
GetColSize(iCol), GetColPrecision(iCol) );
else
fprintf( fp, " Size:%5d", GetColSize(iCol) );
CPLString osType = GetTypeName( GetColType(iCol) );
fprintf( fp, " Type:%s", osType.c_str() );
if( GetColNullable(iCol) )
fprintf( fp, " NULLABLE" );
fprintf( fp, "\n" );
}
fprintf( fp, "\n" );
}
/* -------------------------------------------------------------------- */
/* Display results */
/* -------------------------------------------------------------------- */
int iRecord = 0;
while( Fetch() )
{
fprintf( fp, "Record %d\n", iRecord++ );
for( iCol = 0; iCol < GetColCount(); iCol++ )
{
fprintf( fp, " %s: %s\n", GetColName(iCol), GetColData(iCol) );
}
}
}
/************************************************************************/
/* GetTypeName() */
/************************************************************************/
/**
* Get name for SQL column type.
*
* Returns a string name for the indicated type code (as returned
* from CPLODBCStatement::GetColType()).
*
* @param nTypeCode the SQL_ code, such as SQL_CHAR.
*
* @return internal string, "UNKNOWN" if code not recognised.
*/
CPLString CPLODBCStatement::GetTypeName( int nTypeCode )
{
switch( nTypeCode )
{
case SQL_CHAR:
return "CHAR";
case SQL_NUMERIC:
return "NUMERIC";
case SQL_DECIMAL:
return "DECIMAL";
case SQL_INTEGER:
return "INTEGER";
case SQL_SMALLINT:
return "SMALLINT";
case SQL_FLOAT:
return "FLOAT";
case SQL_REAL:
return "REAL";
case SQL_DOUBLE:
return "DOUBLE";
case SQL_DATETIME:
return "DATETIME";
case SQL_VARCHAR:
return "VARCHAR";
case SQL_TYPE_DATE:
return "DATE";
case SQL_TYPE_TIME:
return "TIME";
case SQL_TYPE_TIMESTAMP:
return "TIMESTAMP";
default:
CPLString osResult;
osResult.Printf( "UNKNOWN:%d", nTypeCode );
return osResult;
}
}
/************************************************************************/
/* GetTypeMapping() */
/************************************************************************/
/**
* Get appropriate C data type for SQL column type.
*
* Returns a C data type code, corresponding to the indicated SQL data
* type code (as returned from CPLODBCStatement::GetColType()).
*
* @param nTypeCode the SQL_ code, such as SQL_CHAR.
*
* @return data type code. The valid code is always returned. If SQL
* code is not recognised, SQL_C_BINARY will be returned.
*/
SQLSMALLINT CPLODBCStatement::GetTypeMapping( SQLSMALLINT nTypeCode )
{
switch( nTypeCode )
{
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_LONGVARCHAR:
return SQL_C_CHAR;
case SQL_WCHAR:
case SQL_WVARCHAR:
case SQL_WLONGVARCHAR:
return SQL_C_WCHAR;
case SQL_DECIMAL:
case SQL_NUMERIC:
return SQL_C_NUMERIC;
case SQL_SMALLINT:
return SQL_C_SSHORT;
case SQL_INTEGER:
return SQL_C_SLONG;
case SQL_REAL:
return SQL_C_FLOAT;
case SQL_FLOAT:
case SQL_DOUBLE:
return SQL_C_DOUBLE;
case SQL_BIGINT:
return SQL_C_SBIGINT;
case SQL_BIT:
case SQL_TINYINT:
/* case SQL_TYPE_UTCDATETIME:
case SQL_TYPE_UTCTIME:*/
case SQL_INTERVAL_MONTH:
case SQL_INTERVAL_YEAR:
case SQL_INTERVAL_YEAR_TO_MONTH:
case SQL_INTERVAL_DAY:
case SQL_INTERVAL_HOUR:
case SQL_INTERVAL_MINUTE:
case SQL_INTERVAL_SECOND:
case SQL_INTERVAL_DAY_TO_HOUR:
case SQL_INTERVAL_DAY_TO_MINUTE:
case SQL_INTERVAL_DAY_TO_SECOND:
case SQL_INTERVAL_HOUR_TO_MINUTE:
case SQL_INTERVAL_HOUR_TO_SECOND:
case SQL_INTERVAL_MINUTE_TO_SECOND:
case SQL_GUID:
return SQL_C_CHAR;
case SQL_DATE:
case SQL_TYPE_DATE:
return SQL_C_DATE;
case SQL_TIME:
case SQL_TYPE_TIME:
return SQL_C_TIME;
case SQL_TIMESTAMP:
case SQL_TYPE_TIMESTAMP:
return SQL_C_TIMESTAMP;
case SQL_BINARY:
case SQL_VARBINARY:
case SQL_LONGVARBINARY:
return SQL_C_BINARY;
default:
return SQL_C_CHAR;
}
}
#endif /* #ifndef WIN32CE */