/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* 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/.
*/
#include "Util.hxx"
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
#include <com/sun/star/sdbc/DataType.hpp>
#include <com/sun/star/sdbc/SQLException.hpp>
#include <o3tl/string_view.hxx>
using namespace ::connectivity;
using namespace ::com::sun::star;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::uno;
using namespace firebird;
OUString firebird::sanitizeIdentifier(std::u16string_view rIdentifier)
{
std::u16string_view sRet = o3tl::trim(rIdentifier);
assert(sRet.size() <= 31);
// Firebird identifiers cannot be longer than this.
return OUString(sRet);
}
OUString firebird::StatusVectorToString(
const ISC_STATUS_ARRAY& rStatusVector,
std::u16string_view rCause)
{
OUStringBuffer buf;
const ISC_STATUS* pStatus =
reinterpret_cast<
const ISC_STATUS*>(&rStatusVector);
buf.append(
"firebird_sdbc error:");
try
{
char msg[512];
// Size is based on suggestion in docs.
while(fb_interpret(msg,
sizeof(msg), &pStatus))
{
// TODO: verify encoding
buf.append(
"\n*"
+ OUString(msg, strlen(msg), RTL_TEXTENCODING_UTF8));
}
}
catch (...)
{
SAL_WARN(
"connectivity.firebird",
"ignore fb_interpret exception");
}
buf.append(OUString::Concat(
"\ncaused by\n'") + rCause +
"'\n");
OUString error = buf.makeStringAndClear();
SAL_WARN(
"connectivity.firebird", error);
return error;
}
void firebird::evaluateStatusVector(
const ISC_STATUS_ARRAY& rStatusVector,
std::u16string_view rCause,
const uno::Reference< XInterface >& _rxContext)
{
if (IndicatesError(rStatusVector))
{
OUString error = StatusVectorToString(rStatusVector, rCause);
throw SQLException(error, _rxContext, OUString(), 1, Any());
}
}
static sal_Int32 lcl_getNumberType(
short aType, NumberSubType aSubType )
{
switch(aSubType)
{
case NumberSubType::Numeric:
return DataType::NUMERIC;
case NumberSubType::Decimal:
return DataType::DECIMAL;
default:
switch(aType)
{
case SQL_SHORT:
return DataType::SMALLINT;
case SQL_LONG:
return DataType::INTEGER;
case SQL_DOUBLE:
return DataType::
DOUBLE;
case SQL_INT64:
return DataType::BIGINT;
default:
assert(
false);
// not a number
return 0;
}
}
}
static sal_Int32 lcl_getCharColumnType(
short aType, std::u16string_view sCharset )
{
switch(aType)
{
case SQL_TEXT:
if( sCharset == u
"OCTETS")
return DataType::BINARY;
else
return DataType::
CHAR;
case SQL_VARYING:
if( sCharset == u
"OCTETS")
return DataType::VARBINARY;
else
return DataType::VARCHAR;
default:
assert(
false);
return 0;
}
}
sal_Int32 firebird::ColumnTypeInfo::getSdbcType()
const
{
short aType = m_aType & ~1;
// Remove last bit -- it is used to denote whether column
// can store Null, not needed for type determination
short aSubType = m_aSubType;
if( m_nScale > 0 )
{
// numeric / decimal
if(aType == SQL_SHORT || aType == SQL_LONG || aType == SQL_DOUBLE
|| aType == SQL_INT64)
{
// if scale is set without subtype then imply numeric
if(
static_cast<NumberSubType>(aSubType) == NumberSubType::Other )
aSubType =
static_cast<
short>(NumberSubType::Numeric);
}
}
switch (aType)
{
case SQL_TEXT:
case SQL_VARYING:
return lcl_getCharColumnType(aType, m_sCharsetName);
case SQL_SHORT:
case SQL_LONG:
case SQL_DOUBLE:
case SQL_INT64:
return lcl_getNumberType(aType,
static_cast<NumberSubType>(aSubType) );
case SQL_FLOAT:
return DataType::
FLOAT;
case SQL_D_FLOAT:
return DataType::
DOUBLE;
case SQL_TIMESTAMP:
return DataType::TIMESTAMP;
case SQL_BLOB:
switch (
static_cast<BlobSubtype>(aSubType))
{
case BlobSubtype::Blob:
return DataType::BLOB;
case BlobSubtype::Clob:
return DataType::CLOB;
case BlobSubtype::Image:
return DataType::LONGVARBINARY;
default:
SAL_WARN(
"connectivity.firebird",
"Unknown subtype for Blob type: " << aSubType);
assert(!
"Unknown subtype for Blob type");
// Should never happen
return 0;
}
case SQL_ARRAY:
return DataType::ARRAY;
case SQL_TYPE_TIME:
return DataType::TIME;
case SQL_TYPE_DATE:
return DataType::DATE;
case SQL_NULL:
return DataType::SQLNULL;
case SQL_QUAD:
// Is a "Blob ID" according to the docs
return 0;
// TODO: verify
case SQL_BOOLEAN:
return DataType::BOOLEAN;
default:
assert(
false);
// Should never happen
return 0;
}
}
OUString firebird::ColumnTypeInfo::getColumnTypeName()
const
{
sal_Int32 nDataType = this->getSdbcType();
switch (nDataType)
{
case DataType::BIT:
return u
"BIT"_ustr;
case DataType::TINYINT:
return u
"TINYINT"_ustr;
case DataType::SMALLINT:
return u
"SMALLINT"_ustr;
case DataType::INTEGER:
return u
"INTEGER"_ustr;
case DataType::BIGINT:
return u
"BIGINT"_ustr;
case DataType::
FLOAT:
return u
"FLOAT"_ustr;
case DataType::REAL:
return u
"REAL"_ustr;
case DataType::
DOUBLE:
return u
"DOUBLE"_ustr;
case DataType::NUMERIC:
return u
"NUMERIC"_ustr;
case DataType::DECIMAL:
return u
"DECIMAL"_ustr;
case DataType::
CHAR:
return u
"CHAR"_ustr;
case DataType::VARCHAR:
return u
"VARCHAR"_ustr;
case DataType::LONGVARCHAR:
return u
"LONGVARCHAR"_ustr;
case DataType::DATE:
return u
"DATE"_ustr;
case DataType::TIME:
return u
"TIME"_ustr;
case DataType::TIMESTAMP:
return u
"TIMESTAMP"_ustr;
case DataType::BINARY:
// in Firebird, that is the same datatype "CHAR" as DataType::CHAR,
// only with CHARACTER SET OCTETS; we use the synonym CHARACTER
// to fool LO into seeing it as different types.
return u
"CHARACTER"_ustr;
case DataType::VARBINARY:
// see above comment about DataType::BINARY.
return u
"CHARACTER VARYING"_ustr;
case DataType::LONGVARBINARY:
return "BLOB SUB_TYPE " + OUString::number(
static_cast<
short>(BlobSubtype::Image));
case DataType::ARRAY:
return u
"ARRAY"_ustr;
case DataType::BLOB:
return u
"BLOB SUB_TYPE BINARY"_ustr;
case DataType::CLOB:
return u
"BLOB SUB_TYPE TEXT"_ustr;
case DataType::BOOLEAN:
return u
"BOOLEAN"_ustr;
case DataType::SQLNULL:
return u
"NULL"_ustr;
default:
assert(
false);
// Should never happen
return OUString();
}
}
short firebird::getFBTypeFromBlrType(
short blrType)
{
switch (blrType)
{
case blr_text:
return SQL_TEXT;
case blr_text2:
assert(
false);
return 0;
// No idea if this should be supported
case blr_varying:
return SQL_VARYING;
case blr_varying2:
assert(
false);
return 0;
// No idea if this should be supported
case blr_short:
return SQL_SHORT;
case blr_long:
return SQL_LONG;
case blr_float:
return SQL_FLOAT;
case blr_double:
return SQL_DOUBLE;
case blr_d_float:
return SQL_D_FLOAT;
case blr_timestamp:
return SQL_TIMESTAMP;
case blr_blob:
return SQL_BLOB;
// case blr_SQL_ARRAY:
// return OUString("SQL_ARRAY");
case blr_sql_time:
return SQL_TYPE_TIME;
case blr_sql_date:
return SQL_TYPE_DATE;
case blr_int64:
return SQL_INT64;
// case SQL_NULL:
// return OUString("SQL_NULL");
case blr_quad:
return SQL_QUAD;
case blr_bool:
return SQL_BOOLEAN;
default:
// If this happens we have hit one of the extra types in ibase.h
// look up blr_* for a list, e.g. blr_domain_name, blr_not_nullable etc.
assert(
false);
return 0;
}
}
void firebird::mallocSQLVAR(XSQLDA* pSqlda)
{
// TODO: confirm the sizings below.
XSQLVAR* pVar = pSqlda->sqlvar;
for (
int i=0; i < pSqlda->sqld; i++, pVar++)
{
int dtype = (pVar->sqltype & ~1);
/* drop flag bit for now */
switch(dtype) {
case SQL_TEXT:
pVar->sqldata =
static_cast<
char *>(malloc(
sizeof(
char)*pVar->sqllen));
break;
case SQL_VARYING:
pVar->sqldata =
static_cast<
char *>(malloc(
sizeof(
char)*pVar->sqllen + 2));
break;
case SQL_SHORT:
pVar->sqldata =
static_cast<
char*>(malloc(
sizeof(sal_Int16)));
break;
case SQL_LONG:
pVar->sqldata =
static_cast<
char*>(malloc(
sizeof(sal_Int32)));
break;
case SQL_FLOAT:
pVar->sqldata =
static_cast<
char *>(malloc(
sizeof(
float)));
break;
case SQL_DOUBLE:
case SQL_D_FLOAT:
pVar->sqldata =
static_cast<
char *>(malloc(
sizeof(
double)));
break;
case SQL_TIMESTAMP:
pVar->sqldata =
static_cast<
char*>(malloc(
sizeof(ISC_TIMESTAMP)));
break;
// an ARRAY is in fact a BLOB of a specialized type
// See https://firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-datatypes-bnrytypes.html#fblangref25-datatypes-array
case SQL_ARRAY:
case SQL_BLOB:
pVar->sqldata =
static_cast<
char*>(malloc(
sizeof(ISC_QUAD)));
break;
case SQL_TYPE_TIME:
pVar->sqldata =
static_cast<
char*>(malloc(
sizeof(ISC_TIME)));
break;
case SQL_TYPE_DATE:
pVar->sqldata =
static_cast<
char*>(malloc(
sizeof(ISC_DATE)));
break;
case SQL_INT64:
pVar->sqldata =
static_cast<
char *>(malloc(
sizeof(sal_Int64)));
break;
case SQL_BOOLEAN:
pVar->sqldata =
static_cast<
char *>(malloc(
sizeof(sal_Bool)));
break;
// See https://www.firebirdsql.org/file/documentation/html/en/refdocs/fblangref25/firebird-25-language-reference.html#fblangref25-datatypes-special-sqlnull
case SQL_NULL:
pVar->sqldata = nullptr;
break;
case SQL_QUAD:
assert(
false);
// TODO: implement
break;
default:
SAL_WARN(
"connectivity.firebird",
"Unknown type: " << dtype);
assert(
false);
break;
}
/* allocate variable to hold NULL status */
pVar->sqlind =
static_cast<
short *>(malloc(
sizeof(
short)));
}
}
void firebird::freeSQLVAR(XSQLDA* pSqlda)
{
XSQLVAR* pVar = pSqlda->sqlvar;
for (
int i=0; i < pSqlda->sqld; i++, pVar++)
{
int dtype = (pVar->sqltype & ~1);
/* drop flag bit for now */
switch(dtype) {
case SQL_TEXT:
case SQL_VARYING:
case SQL_SHORT:
case SQL_LONG:
case SQL_FLOAT:
case SQL_DOUBLE:
case SQL_D_FLOAT:
case SQL_TIMESTAMP:
// an ARRAY is in fact a BLOB of a specialized type
// See https://firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-datatypes-bnrytypes.html#fblangref25-datatypes-array
case SQL_ARRAY:
case SQL_BLOB:
case SQL_INT64:
case SQL_TYPE_TIME:
case SQL_TYPE_DATE:
case SQL_BOOLEAN:
if(pVar->sqldata)
{
free(pVar->sqldata);
pVar->sqldata = nullptr;
}
break;
case SQL_NULL:
// See SQL_NULL case in mallocSQLVAR
assert(pVar->sqldata == nullptr);
break;
case SQL_QUAD:
assert(
false);
// TODO: implement
break;
default:
SAL_WARN(
"connectivity.firebird",
"Unknown type: " << dtype);
// assert(false);
break;
}
if(pVar->sqlind)
{
free(pVar->sqlind);
pVar->sqlind = nullptr;
}
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */