/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <odbc/OTools.hxx>
#include <odbc/OFunctions.hxx>
#include <com/sun/star/sdbc/DataType.hpp>
#include <o3tl/safeint.hxx>
#include <osl/diagnose.h>
#include <osl/endian.h>
#include <odbc/OConnection.hxx>
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
#include <string.h>
using namespace connectivity::odbc;
using namespace com::sun::star::uno;
using namespace com::sun::star::sdbc;
using namespace com::sun::star::util;
namespace {
size_t sqlTypeLen ( SQLSMALLINT _nType )
{
switch (_nType)
{
case SQL_C_SSHORT:
case SQL_C_SHORT:
return sizeof(SQLSMALLINT);
case SQL_C_USHORT:
return sizeof(SQLUSMALLINT);
case SQL_C_SLONG:
case SQL_C_LONG:
return sizeof(SQLINTEGER);
case SQL_C_ULONG:
return sizeof(SQLUINTEGER);
case SQL_C_FLOAT:
return sizeof(SQLREAL);
case SQL_C_DOUBLE:
static_assert(sizeof(SQLDOUBLE) == sizeof(SQLFLOAT), "SQLDOUBLE/SQLFLOAT confusion");
return sizeof(SQLDOUBLE);
case SQL_C_BIT:
return sizeof(SQLCHAR);
case SQL_C_STINYINT:
case SQL_C_TINYINT:
return sizeof(SQLSCHAR);
case SQL_C_UTINYINT:
return sizeof(SQLCHAR);
case SQL_C_SBIGINT:
return sizeof(SQLBIGINT);
case SQL_C_UBIGINT:
return sizeof(SQLUBIGINT);
/* UnixODBC gives this the same value as SQL_C_UBIGINT
case SQL_C_BOOKMARK:
return sizeof(BOOKMARK); */
case SQL_C_TYPE_DATE:
case SQL_C_DATE:
return sizeof(SQL_DATE_STRUCT);
case SQL_C_TYPE_TIME:
case SQL_C_TIME:
return sizeof(SQL_TIME_STRUCT);
case SQL_C_TYPE_TIMESTAMP:
case SQL_C_TIMESTAMP:
return sizeof(SQL_TIMESTAMP_STRUCT);
case SQL_C_NUMERIC:
return sizeof(SQL_NUMERIC_STRUCT);
case SQL_C_GUID:
return sizeof(SQLGUID);
case SQL_C_INTERVAL_YEAR:
case SQL_C_INTERVAL_MONTH:
case SQL_C_INTERVAL_DAY:
case SQL_C_INTERVAL_HOUR:
case SQL_C_INTERVAL_MINUTE:
case SQL_C_INTERVAL_SECOND:
case SQL_C_INTERVAL_YEAR_TO_MONTH:
case SQL_C_INTERVAL_DAY_TO_HOUR:
case SQL_C_INTERVAL_DAY_TO_MINUTE:
case SQL_C_INTERVAL_DAY_TO_SECOND:
case SQL_C_INTERVAL_HOUR_TO_MINUTE:
case SQL_C_INTERVAL_HOUR_TO_SECOND:
case SQL_C_INTERVAL_MINUTE_TO_SECOND:
return sizeof(SQL_INTERVAL_STRUCT);
// ** Variable-sized datatypes -> cannot predict length
case SQL_C_CHAR:
case SQL_C_WCHAR:
case SQL_C_BINARY:
// UnixODBC gives this the same value as SQL_C_BINARY
//case SQL_C_VARBOOKMARK:
// Unknown datatype -> cannot predict length
default:
return static_cast<size_t>(-1);
}
}
void appendSQLWCHARs(OUStringBuffer & s, SQLWCHAR const * d, sal_Int32 n)
{
static_assert(
sizeof (SQLWCHAR) == sizeof (sal_Unicode) || sizeof (SQLWCHAR) == 4,
"bad SQLWCHAR");
if (sizeof (SQLWCHAR) == sizeof (sal_Unicode)) {
s.append(reinterpret_cast<sal_Unicode const *>(d), n);
} else {
for (sal_Int32 i = 0; i < n; ++i) {
s.appendUtf32(d[i]);
}
}
}
}
void OTools::getValue( OConnection const * _pConnection,
SQLHANDLE _aStatementHandle,
sal_Int32 columnIndex,
SQLSMALLINT _nType,
bool &_bWasNull,
const css::uno::Reference< css::uno::XInterface >& _xInterface,
void* _pValue,
SQLLEN _nSize)
{
const size_t properSize = sqlTypeLen(_nType);
if ( properSize == static_cast<size_t>(-1) )
SAL_WARN( "connectivity.drivers", "connectivity::odbc::OTools::getValue: unknown SQL type - cannot check buffer size");
else
{
OSL_ENSURE(static_cast<size_t>(_nSize) == properSize, "connectivity::odbc::OTools::getValue got wrongly sized memory region to write result to");
if ( o3tl::make_unsigned(_nSize) > properSize )
{
SAL_WARN( "connectivity.drivers", "memory region is too big - trying to fudge it");
memset(_pValue, 0, _nSize);
#ifdef OSL_BIGENDIAN
// This is skewed in favour of integer types
_pValue = static_cast<char*>(_pValue) + _nSize - properSize;
#endif
}
}
OSL_ENSURE(o3tl::make_unsigned(_nSize) >= properSize, "memory region is too small");
SQLLEN pcbValue = SQL_NULL_DATA;
OTools::ThrowException(_pConnection,
_pConnection->functions().GetData(_aStatementHandle,
static_cast<SQLUSMALLINT>(columnIndex),
_nType,
_pValue,
_nSize,
&pcbValue),
_aStatementHandle,SQL_HANDLE_STMT,_xInterface,false);
_bWasNull = pcbValue == SQL_NULL_DATA;
}
void OTools::ThrowException(const OConnection* _pConnection,
const SQLRETURN _rRetCode,
const SQLHANDLE _pContext,
const SQLSMALLINT _nHandleType,
const Reference< XInterface >& _xInterface,
const bool _bNoFound)
{
switch(_rRetCode)
{
case SQL_NEED_DATA:
case SQL_STILL_EXECUTING:
case SQL_SUCCESS:
case SQL_SUCCESS_WITH_INFO:
return;
case SQL_NO_DATA_FOUND:
if(_bNoFound)
return; // no need to throw an exception
break;
case SQL_ERROR: break;
case SQL_INVALID_HANDLE: SAL_WARN( "connectivity.drivers", "SdbODBC3_SetStatus: SQL_INVALID_HANDLE");
throw SQLException();
}
// Additional Information on the latest ODBC-functioncall available
// SQLError provides this Information.
OUString errorMessage;
OUString sqlState;
SQLINTEGER pfNativeError;
SQLRETURN n;
// Information for latest operation:
// when hstmt != SQL_NULL_HSTMT is (Used from SetStatus in SdbCursor, SdbTable, ...),
// then the status of the latest statements will be fetched, without the Status of the last
// statements of this connection [what in this case will probably be the same, but the Reference
// Manual isn't totally clear in this...].
// corresponding for hdbc.
if (bUseWChar && _pConnection->functions().has(ODBC3SQLFunctionId::GetDiagRecW))
{
SQLWCHAR szSqlState[6];
SQLWCHAR szErrorMessage[SQL_MAX_MESSAGE_LENGTH];
szErrorMessage[0] = '\0';
SQLSMALLINT cchErrorMsg = 0;
n = _pConnection->functions().GetDiagRecW(_nHandleType,_pContext,1,
szSqlState,
&pfNativeError,
szErrorMessage, std::size(szErrorMessage) - 1, &cchErrorMsg);
if (SQL_SUCCEEDED(n))
{
errorMessage = toUString(szErrorMessage, cchErrorMsg);
sqlState = toUString(szSqlState, 5);
}
}
else
{
SQLCHAR szSqlState[6];
SQLCHAR szErrorMessage[SQL_MAX_MESSAGE_LENGTH];
szErrorMessage[0] = '\0';
SQLSMALLINT pcbErrorMsg = 0;
n = _pConnection->functions().GetDiagRec(_nHandleType,_pContext,1,
szSqlState,
&pfNativeError,
szErrorMessage,sizeof szErrorMessage - 1,&pcbErrorMsg);
if (SQL_SUCCEEDED(n))
{
rtl_TextEncoding _nTextEncoding = osl_getThreadTextEncoding();
errorMessage = toUString(szErrorMessage, pcbErrorMsg, _nTextEncoding);
sqlState = toUString(szSqlState, 5, _nTextEncoding);
}
}
OSL_ENSURE(n != SQL_INVALID_HANDLE,"SdbODBC3_SetStatus: SQLError returned SQL_INVALID_HANDLE");
OSL_ENSURE(n == SQL_SUCCESS || n == SQL_SUCCESS_WITH_INFO || n == SQL_NO_DATA_FOUND || n == SQL_ERROR,"SdbODBC3_SetStatus: SQLError failed");
// For the Return Code of SQLError see ODBC 2.0 Programmer's Reference Page 287ff
throw SQLException(errorMessage, _xInterface, sqlState, pfNativeError, Any());
}
Sequence<sal_Int8> OTools::getBytesValue(const OConnection* _pConnection,
const SQLHANDLE _aStatementHandle,
const sal_Int32 columnIndex,
const SQLSMALLINT _fSqlType,
bool &_bWasNull,
const Reference< XInterface >& _xInterface)
{
sal_Int8 aCharArray[2048];
// First try to fetch the data with the little Buffer:
const SQLLEN nMaxLen = sizeof aCharArray;
SQLLEN pcbValue = SQL_NO_TOTAL;
Sequence<sal_Int8> aData;
OSL_ENSURE( _fSqlType != SQL_CHAR && _fSqlType != SQL_VARCHAR && _fSqlType != SQL_LONGVARCHAR &&
_fSqlType != SQL_WCHAR && _fSqlType != SQL_WVARCHAR && _fSqlType != SQL_WLONGVARCHAR,
"connectivity::odbc::OTools::getBytesValue called with character _fSqlType");
while (pcbValue == SQL_NO_TOTAL || pcbValue > nMaxLen)
{
OTools::ThrowException(_pConnection,
_pConnection->functions().GetData(
_aStatementHandle,
static_cast<SQLUSMALLINT>(columnIndex),
_fSqlType,
static_cast<SQLPOINTER>(aCharArray),
nMaxLen,
&pcbValue),
_aStatementHandle,SQL_HANDLE_STMT,_xInterface);
_bWasNull = pcbValue == SQL_NULL_DATA;
if(_bWasNull)
return Sequence<sal_Int8>();
SQLLEN nReadBytes;
// After the SQLGetData that wrote out to aCharArray the last byte of the data,
// pcbValue will not be SQL_NO_TOTAL -> we have a reliable count
if ( (pcbValue == SQL_NO_TOTAL) || (pcbValue >= nMaxLen) )
{
// we filled the buffer
nReadBytes = nMaxLen;
}
else
{
nReadBytes = pcbValue;
}
const sal_Int32 nLen = aData.getLength();
aData.realloc(nLen + nReadBytes);
memcpy(aData.getArray() + nLen, aCharArray, nReadBytes);
}
return aData;
}
OUString OTools::getStringValue(OConnection const * _pConnection,
SQLHANDLE _aStatementHandle,
sal_Int32 columnIndex,
SQLSMALLINT _fSqlType,
bool &_bWasNull,
const Reference< XInterface >& _xInterface,
const rtl_TextEncoding _nTextEncoding)
{
OUStringBuffer aData;
switch(_fSqlType)
{
case SQL_WVARCHAR:
case SQL_WCHAR:
case SQL_WLONGVARCHAR:
{
SQLWCHAR waCharArray[2048];
static_assert(sizeof(waCharArray) % sizeof(SQLWCHAR) == 0, "must fit in evenly");
static_assert(sizeof(SQLWCHAR) == 2 || sizeof(SQLWCHAR) == 4, "must be 2 or 4");
// Size == number of bytes, Len == number of UTF-16 or UCS4 code units
const SQLLEN nMaxSize = sizeof(waCharArray);
const SQLLEN nMaxLen = sizeof(waCharArray) / sizeof(SQLWCHAR);
static_assert(nMaxLen * sizeof(SQLWCHAR) == nMaxSize, "sizes must match");
// read the unicode data
SQLLEN pcbValue = SQL_NO_TOTAL;
while ((pcbValue == SQL_NO_TOTAL ) || (pcbValue >= nMaxSize) )
{
OTools::ThrowException(_pConnection,
_pConnection->functions().GetData(
_aStatementHandle,
static_cast<SQLUSMALLINT>(columnIndex),
SQL_C_WCHAR,
&waCharArray,
SQLLEN(nMaxLen)*sizeof(sal_Unicode),
&pcbValue),
_aStatementHandle,SQL_HANDLE_STMT,_xInterface);
_bWasNull = pcbValue == SQL_NULL_DATA;
if(_bWasNull)
return OUString();
SQLLEN nReadChars;
OSL_ENSURE( (pcbValue < 0) || (pcbValue % 2 == 0),
"ODBC: SQLGetData of SQL_C_WCHAR returned odd number of bytes");
if ( (pcbValue == SQL_NO_TOTAL) || (pcbValue >= nMaxSize) )
{
// we filled the buffer; remove the terminating null character
nReadChars = nMaxLen-1;
if ( waCharArray[nReadChars] != 0)
{
SAL_WARN( "connectivity.drivers", "Buggy ODBC driver? Did not null-terminate (variable length) data!");
++nReadChars;
}
}
else
{
nReadChars = pcbValue/sizeof(SQLWCHAR);
}
appendSQLWCHARs(aData, waCharArray, nReadChars);
}
break;
}
default:
{
char aCharArray[2048];
// read the unicode data
const SQLLEN nMaxLen = sizeof(aCharArray);
SQLLEN pcbValue = SQL_NO_TOTAL;
while ((pcbValue == SQL_NO_TOTAL ) || (pcbValue >= nMaxLen) )
{
OTools::ThrowException(_pConnection,
_pConnection->functions().GetData(
_aStatementHandle,
static_cast<SQLUSMALLINT>(columnIndex),
SQL_C_CHAR,
&aCharArray,
nMaxLen,
&pcbValue),
_aStatementHandle,SQL_HANDLE_STMT,_xInterface);
_bWasNull = pcbValue == SQL_NULL_DATA;
if(_bWasNull)
return OUString();
SQLLEN nReadChars;
if ( (pcbValue == SQL_NO_TOTAL) || (pcbValue >= nMaxLen) )
{
// we filled the buffer; remove the terminating null character
nReadChars = nMaxLen-1;
if ( aCharArray[nReadChars] != 0)
{
SAL_WARN( "connectivity.drivers", "Buggy ODBC driver? Did not null-terminate (variable length) data!");
++nReadChars;
}
}
else
{
nReadChars = pcbValue;
}
aData.append(OUString(aCharArray, nReadChars, _nTextEncoding));
}
break;
}
}
return aData.makeStringAndClear();
}
void OTools::GetInfo(OConnection const * _pConnection,
SQLHANDLE _aConnectionHandle,
SQLUSMALLINT _nInfo,
OUString &_rValue,
const Reference< XInterface >& _xInterface,
rtl_TextEncoding _nTextEncoding)
{
if (bUseWChar && _pConnection->functions().has(ODBC3SQLFunctionId::GetInfoW))
{
SQLWCHAR aValue[512];
SQLSMALLINT nValueLen=0;
// SQLGetInfoW takes / outputs count of bytes, not characters
OTools::ThrowException(_pConnection,
_pConnection->functions().GetInfoW(_aConnectionHandle,_nInfo,aValue,sizeof(aValue)-sizeof(SQLWCHAR),&nValueLen),
_aConnectionHandle,SQL_HANDLE_DBC,_xInterface);
_rValue = toUString(aValue, nValueLen / sizeof(SQLWCHAR));
}
else
{
SQLCHAR aValue[512];
SQLSMALLINT nValueLen=0;
OTools::ThrowException(_pConnection,
_pConnection->functions().GetInfo(_aConnectionHandle,_nInfo,aValue,sizeof(aValue)-1,&nValueLen),
_aConnectionHandle,SQL_HANDLE_DBC,_xInterface);
_rValue = toUString(aValue, nValueLen, _nTextEncoding);
}
}
void OTools::GetInfo(OConnection const * _pConnection,
SQLHANDLE _aConnectionHandle,
SQLUSMALLINT _nInfo,
sal_Int32 &_rValue,
const Reference< XInterface >& _xInterface)
{
SQLSMALLINT nValueLen;
_rValue = 0; // in case the driver uses only 16 of the 32 bits (as it does, for example, for SQL_CATALOG_LOCATION)
OTools::ThrowException(_pConnection,
_pConnection->functions().GetInfo(_aConnectionHandle,_nInfo,&_rValue,sizeof _rValue,&nValueLen),
_aConnectionHandle,SQL_HANDLE_DBC,_xInterface);
}
void OTools::GetInfo(OConnection const * _pConnection,
SQLHANDLE _aConnectionHandle,
SQLUSMALLINT _nInfo,
SQLUINTEGER &_rValue,
const Reference< XInterface >& _xInterface)
{
SQLSMALLINT nValueLen;
_rValue = 0; // in case the driver uses only 16 of the 32 bits (as it does, for example, for SQL_CATALOG_LOCATION)
OTools::ThrowException(_pConnection,
_pConnection->functions().GetInfo(_aConnectionHandle,_nInfo,&_rValue,sizeof _rValue,&nValueLen),
_aConnectionHandle,SQL_HANDLE_DBC,_xInterface);
}
void OTools::GetInfo(OConnection const * _pConnection,
SQLHANDLE _aConnectionHandle,
SQLUSMALLINT _nInfo,
SQLUSMALLINT &_rValue,
const Reference< XInterface >& _xInterface)
{
SQLSMALLINT nValueLen;
_rValue = 0; // in case the driver uses only 16 of the 32 bits (as it does, for example, for SQL_CATALOG_LOCATION)
OTools::ThrowException(_pConnection,
_pConnection->functions().GetInfo(_aConnectionHandle,_nInfo,&_rValue,sizeof _rValue,&nValueLen),
_aConnectionHandle,SQL_HANDLE_DBC,_xInterface);
}
sal_Int32 OTools::MapOdbcType2Jdbc(SQLSMALLINT _nType)
{
sal_Int32 nValue = DataType::VARCHAR;
switch(_nType)
{
case SQL_BIT:
nValue = DataType::BIT;
break;
case SQL_TINYINT:
nValue = DataType::TINYINT;
break;
case SQL_SMALLINT:
nValue = DataType::SMALLINT;
break;
case SQL_INTEGER:
nValue = DataType::INTEGER;
break;
case SQL_BIGINT:
nValue = DataType::BIGINT;
break;
case SQL_FLOAT:
nValue = DataType::FLOAT;
break;
case SQL_REAL:
nValue = DataType::REAL;
break;
case SQL_DOUBLE:
nValue = DataType::DOUBLE;
break;
case SQL_NUMERIC:
nValue = DataType::NUMERIC;
break;
case SQL_DECIMAL:
nValue = DataType::DECIMAL;
break;
case SQL_WCHAR:
case SQL_CHAR:
nValue = DataType::CHAR;
break;
case SQL_WVARCHAR:
case SQL_VARCHAR:
nValue = DataType::VARCHAR;
break;
case SQL_WLONGVARCHAR:
case SQL_LONGVARCHAR:
nValue = DataType::LONGVARCHAR;
break;
case SQL_TYPE_DATE:
case SQL_DATE:
nValue = DataType::DATE;
break;
case SQL_TYPE_TIME:
case SQL_TIME:
nValue = DataType::TIME;
break;
case SQL_TYPE_TIMESTAMP:
case SQL_TIMESTAMP:
nValue = DataType::TIMESTAMP;
break;
case SQL_BINARY:
nValue = DataType::BINARY;
break;
case SQL_VARBINARY:
case SQL_GUID:
nValue = DataType::VARBINARY;
break;
case SQL_LONGVARBINARY:
nValue = DataType::LONGVARBINARY;
break;
default:
OSL_FAIL("Invalid type");
}
return nValue;
}
// jdbcTypeToOdbc
// Convert the JDBC SQL type to the correct ODBC type
SQLSMALLINT OTools::jdbcTypeToOdbc(sal_Int32 jdbcType)
{
// For the most part, JDBC types match ODBC types. We'll
// just convert the ones that we know are different
sal_Int32 odbcType = jdbcType;
switch (jdbcType)
{
case DataType::DATE:
odbcType = SQL_DATE;
break;
case DataType::TIME:
odbcType = SQL_TIME;
break;
case DataType::TIMESTAMP:
odbcType = SQL_TIMESTAMP;
break;
// ODBC doesn't have any notion of CLOB or BLOB
case DataType::CLOB:
odbcType = SQL_LONGVARCHAR;
break;
case DataType::BLOB:
odbcType = SQL_LONGVARBINARY;
break;
}
return odbcType;
}
void OTools::getBindTypes(bool _bUseOldTimeDate,
SQLSMALLINT _nOdbcType,
SQLSMALLINT& fCType,
SQLSMALLINT& fSqlType
)
{
switch(_nOdbcType)
{
case SQL_CHAR: if (!bUseWChar)
{
fCType = SQL_C_CHAR;
fSqlType = SQL_CHAR;
break;
}
[[fallthrough]];
case SQL_WCHAR: fCType = SQL_C_WCHAR;
fSqlType = SQL_WCHAR; break;
case SQL_VARCHAR: if (!bUseWChar)
{
fCType = SQL_C_CHAR;
fSqlType = SQL_VARCHAR;
break;
}
[[fallthrough]];
case SQL_WVARCHAR: fCType = SQL_C_WCHAR;
fSqlType = SQL_WVARCHAR; break;
case SQL_LONGVARCHAR: if (!bUseWChar)
{
fCType = SQL_C_CHAR;
fSqlType = SQL_LONGVARCHAR;
break;
}
[[fallthrough]];
case SQL_WLONGVARCHAR: fCType = SQL_C_WCHAR;
fSqlType = SQL_WLONGVARCHAR; break;
case SQL_DECIMAL: fCType = bUseWChar ? SQL_C_WCHAR : SQL_C_CHAR;
fSqlType = SQL_DECIMAL; break;
case SQL_NUMERIC: fCType = bUseWChar ? SQL_C_WCHAR : SQL_C_CHAR;
fSqlType = SQL_NUMERIC; break;
case SQL_BIT: fCType = SQL_C_TINYINT;
fSqlType = SQL_INTEGER; break;
case SQL_TINYINT: fCType = SQL_C_TINYINT;
fSqlType = SQL_TINYINT; break;
case SQL_SMALLINT: fCType = SQL_C_SHORT;
fSqlType = SQL_SMALLINT; break;
case SQL_INTEGER: fCType = SQL_C_LONG;
fSqlType = SQL_INTEGER; break;
case SQL_BIGINT: fCType = SQL_C_SBIGINT;
fSqlType = SQL_BIGINT; break;
case SQL_FLOAT: fCType = SQL_C_FLOAT;
fSqlType = SQL_FLOAT; break;
case SQL_REAL: fCType = SQL_C_DOUBLE;
fSqlType = SQL_REAL; break;
case SQL_DOUBLE: fCType = SQL_C_DOUBLE;
fSqlType = SQL_DOUBLE; break;
case SQL_BINARY: fCType = SQL_C_BINARY;
fSqlType = SQL_BINARY; break;
case SQL_VARBINARY:
fCType = SQL_C_BINARY;
fSqlType = SQL_VARBINARY; break;
case SQL_LONGVARBINARY: fCType = SQL_C_BINARY;
fSqlType = SQL_LONGVARBINARY; break;
case SQL_DATE:
if(_bUseOldTimeDate)
{
fCType = SQL_C_DATE;
fSqlType = SQL_DATE;
}
else
{
fCType = SQL_C_TYPE_DATE;
fSqlType = SQL_TYPE_DATE;
}
break;
case SQL_TIME:
if(_bUseOldTimeDate)
{
fCType = SQL_C_TIME;
fSqlType = SQL_TIME;
}
else
{
fCType = SQL_C_TYPE_TIME;
fSqlType = SQL_TYPE_TIME;
}
break;
case SQL_TIMESTAMP:
if(_bUseOldTimeDate)
{
fCType = SQL_C_TIMESTAMP;
fSqlType = SQL_TIMESTAMP;
}
else
{
fCType = SQL_C_TYPE_TIMESTAMP;
fSqlType = SQL_TYPE_TIMESTAMP;
}
break;
default: fCType = SQL_C_BINARY;
fSqlType = SQL_LONGVARBINARY; break;
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'appendUtf32' is required to be utilized.
↑ V1048 The 'nValue' variable was assigned the same value.