1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 */ 9 10 #include "Util.hxx" 11 #include <rtl/ustrbuf.hxx> 12 #include <sal/log.hxx> 13 14 #include <com/sun/star/sdbc/DataType.hpp> 15 #include <com/sun/star/sdbc/SQLException.hpp> 16 #include <o3tl/string_view.hxx> 17 18 using namespace ::connectivity; 19 20 using namespace ::com::sun::star; 21 using namespace ::com::sun::star::sdbc; 22 using namespace ::com::sun::star::uno; 23 24 using namespace firebird; 25 26 OUString firebird::sanitizeIdentifier(std::u16string_view rIdentifier) 27 { 28 std::u16string_view sRet = o3tl::trim(rIdentifier); 29 assert(sRet.size() <= 31); // Firebird identifiers cannot be longer than this. 30 31 return OUString(sRet); 32 } 33 34 OUString firebird::StatusVectorToString(const ISC_STATUS_ARRAY& rStatusVector, 35 std::u16string_view rCause) 36 { 37 OUStringBuffer buf; 38 const ISC_STATUS* pStatus = reinterpret_cast<const ISC_STATUS*>(&rStatusVector); 39 40 buf.append("firebird_sdbc error:"); 41 try 42 { 43 char msg[512]; // Size is based on suggestion in docs. 44 while(fb_interpret(msg, sizeof(msg), &pStatus)) 45 { 46 // TODO: verify encoding 47 buf.append("\n*"); 48 buf.append(OUString(msg, strlen(msg), RTL_TEXTENCODING_UTF8)); 49 } 50 } 51 catch (...) 52 { 53 SAL_WARN("connectivity.firebird", "ignore fb_interpret exception"); 54 } 55 buf.append(OUString::Concat("\ncaused by\n'") + rCause + "'\n"); 56 57 OUString error = buf.makeStringAndClear(); 58 SAL_WARN("connectivity.firebird", error); 59 return error; 60 } 61 62 void firebird::evaluateStatusVector(const ISC_STATUS_ARRAY& rStatusVector, 63 std::u16string_view rCause, 64 const uno::Reference< XInterface >& _rxContext) 65 { 66 if (IndicatesError(rStatusVector)) 67 { 68 OUString error = StatusVectorToString(rStatusVector, rCause); 69 throw SQLException(error, _rxContext, OUString(), 1, Any()); 70 } 71 } 72 73 static sal_Int32 lcl_getNumberType( short aType, NumberSubType aSubType ) 74 { 75 switch(aSubType) 76 { 77 case NumberSubType::Numeric: 78 return DataType::NUMERIC; 79 case NumberSubType::Decimal: 80 return DataType::DECIMAL; 81 default: 82 switch(aType) 83 { 84 case SQL_SHORT: 85 return DataType::SMALLINT; 86 case SQL_LONG: 87 return DataType::INTEGER; 88 case SQL_DOUBLE: 89 return DataType::DOUBLE; 90 case SQL_INT64: 91 return DataType::BIGINT; 92 default: 93 assert(false); // not a number 94 return 0; 95 } 96 } 97 } 98 static sal_Int32 lcl_getCharColumnType( short aType, std::u16string_view sCharset ) 99 { 100 switch(aType) 101 { 102 case SQL_TEXT: 103 if( sCharset == u"OCTETS") 104 return DataType::BINARY; 105 else 106 return DataType::CHAR; 107 case SQL_VARYING: 108 if( sCharset == u"OCTETS") 109 return DataType::VARBINARY; 110 else 111 return DataType::VARCHAR; 112 default: 113 assert(false); 114 return 0; 115 } 116 } 117 118 sal_Int32 firebird::ColumnTypeInfo::getSdbcType() const 119 { 120 short aType = m_aType & ~1; // Remove last bit -- it is used to denote whether column 121 // can store Null, not needed for type determination 122 short aSubType = m_aSubType; 123 if( m_nScale > 0 ) 124 { 125 // numeric / decimal 126 if(aType == SQL_SHORT || aType == SQL_LONG || aType == SQL_DOUBLE 127 || aType == SQL_INT64) 128 { 129 // if scale is set without subtype then imply numeric 130 if( static_cast<NumberSubType>(aSubType) == NumberSubType::Other ) 131 aSubType = static_cast<short>(NumberSubType::Numeric); 132 } 133 } 134 135 switch (aType) 136 { 137 case SQL_TEXT: 138 case SQL_VARYING: 139 return lcl_getCharColumnType(aType, m_sCharsetName); 140 case SQL_SHORT: 141 case SQL_LONG: 142 case SQL_DOUBLE: 143 case SQL_INT64: 144 return lcl_getNumberType(aType, static_cast<NumberSubType>(aSubType) ); 145 case SQL_FLOAT: 146 return DataType::FLOAT; 147 case SQL_D_FLOAT: 148 return DataType::DOUBLE; 149 case SQL_TIMESTAMP: 150 return DataType::TIMESTAMP; 151 case SQL_BLOB: 152 switch (static_cast<BlobSubtype>(aSubType)) 153 { 154 case BlobSubtype::Blob: 155 return DataType::BLOB; 156 case BlobSubtype::Clob: 157 return DataType::CLOB; 158 case BlobSubtype::Image: 159 return DataType::LONGVARBINARY; 160 default: 161 SAL_WARN("connectivity.firebird", "Unknown subtype for Blob type: " << aSubType); 162 assert(!"Unknown subtype for Blob type"); // Should never happen 163 return 0; 164 } 165 case SQL_ARRAY: 166 return DataType::ARRAY; 167 case SQL_TYPE_TIME: 168 return DataType::TIME; 169 case SQL_TYPE_DATE: 170 return DataType::DATE; 171 case SQL_NULL: 172 return DataType::SQLNULL; 173 case SQL_QUAD: // Is a "Blob ID" according to the docs 174 return 0; // TODO: verify 175 case SQL_BOOLEAN: 176 return DataType::BOOLEAN; 177 default: 178 assert(false); // Should never happen 179 return 0; 180 } 181 } 182 183 OUString firebird::ColumnTypeInfo::getColumnTypeName() const 184 { 185 sal_Int32 nDataType = this->getSdbcType(); 186 switch (nDataType) 187 { 188 case DataType::BIT: 189 return "BIT"; 190 case DataType::TINYINT: 191 return "TINYINT"; 192 case DataType::SMALLINT: 193 return "SMALLINT"; 194 case DataType::INTEGER: 195 return "INTEGER"; 196 case DataType::BIGINT: 197 return "BIGINT"; 198 case DataType::FLOAT: 199 return "FLOAT"; 200 case DataType::REAL: 201 return "REAL"; 202 case DataType::DOUBLE: 203 return "DOUBLE"; 204 case DataType::NUMERIC: 205 return "NUMERIC"; 206 case DataType::DECIMAL: 207 return "DECIMAL"; 208 case DataType::CHAR: 209 return "CHAR"; 210 case DataType::VARCHAR: 211 return "VARCHAR"; 212 case DataType::LONGVARCHAR: 213 return "LONGVARCHAR"; 214 case DataType::DATE: 215 return "DATE"; 216 case DataType::TIME: 217 return "TIME"; 218 case DataType::TIMESTAMP: 219 return "TIMESTAMP"; 220 case DataType::BINARY: 221 // in Firebird, that is the same datatype "CHAR" as DataType::CHAR, 222 // only with CHARACTER SET OCTETS; we use the synonym CHARACTER 223 // to fool LO into seeing it as different types. 224 return "CHARACTER"; 225 case DataType::VARBINARY: 226 // see above comment about DataType::BINARY. 227 return "CHARACTER VARYING"; 228 case DataType::LONGVARBINARY: 229 return "BLOB SUB_TYPE " + OUString::number(static_cast<short>(BlobSubtype::Image)); 230 case DataType::ARRAY: 231 return "ARRAY"; 232 case DataType::BLOB: 233 return "BLOB SUB_TYPE BINARY"; 234 case DataType::CLOB: 235 return "BLOB SUB_TYPE TEXT"; 236 case DataType::BOOLEAN: 237 return "BOOLEAN"; 238 case DataType::SQLNULL: 239 return "NULL"; 240 default: 241 assert(false); // Should never happen 242 return OUString(); 243 } 244 } 245 246 short firebird::getFBTypeFromBlrType(short blrType) 247 { 248 switch (blrType) 249 { 250 case blr_text: 251 return SQL_TEXT; 252 case blr_text2: 253 assert(false); 254 return 0; // No idea if this should be supported 255 case blr_varying: 256 return SQL_VARYING; 257 case blr_varying2: 258 assert(false); 259 return 0; // No idea if this should be supported 260 case blr_short: 261 return SQL_SHORT; 262 case blr_long: 263 return SQL_LONG; 264 case blr_float: 265 return SQL_FLOAT; 266 case blr_double: 267 return SQL_DOUBLE; 268 case blr_d_float: 269 return SQL_D_FLOAT; 270 case blr_timestamp: 271 return SQL_TIMESTAMP; 272 case blr_blob: 273 return SQL_BLOB; 274 // case blr_SQL_ARRAY: 275 // return OUString("SQL_ARRAY"); 276 case blr_sql_time: 277 return SQL_TYPE_TIME; 278 case blr_sql_date: 279 return SQL_TYPE_DATE; 280 case blr_int64: 281 return SQL_INT64; 282 // case SQL_NULL: 283 // return OUString("SQL_NULL"); 284 case blr_quad: 285 return SQL_QUAD; 286 case blr_bool: 287 return SQL_BOOLEAN; 288 default: 289 // If this happens we have hit one of the extra types in ibase.h 290 // look up blr_* for a list, e.g. blr_domain_name, blr_not_nullable etc. 291 assert(false); 292 return 0; 293 } 294 } 295 296 void firebird::mallocSQLVAR(XSQLDA* pSqlda) 297 { 298 // TODO: confirm the sizings below. 299 XSQLVAR* pVar = pSqlda->sqlvar; 300 for (int i=0; i < pSqlda->sqld; i++, pVar++) 301 { 302 int dtype = (pVar->sqltype & ~1); /* drop flag bit for now */ 303 switch(dtype) { 304 case SQL_TEXT: 305 pVar->sqldata = static_cast<char *>(malloc(sizeof(char)*pVar->sqllen)); 306 break; 307 case SQL_VARYING: 308 pVar->sqldata = static_cast<char *>(malloc(sizeof(char)*pVar->sqllen + 2)); 309 break; 310 case SQL_SHORT: 311 pVar->sqldata = static_cast<char*>(malloc(sizeof(sal_Int16))); 312 break; 313 case SQL_LONG: 314 pVar->sqldata = static_cast<char*>(malloc(sizeof(sal_Int32))); 315 break; 316 case SQL_FLOAT: 317 pVar->sqldata = static_cast<char *>(malloc(sizeof(float))); 318 break; 319 case SQL_DOUBLE: 320 pVar->sqldata = static_cast<char *>(malloc(sizeof(double))); 321 break; 322 case SQL_D_FLOAT: 323 pVar->sqldata = static_cast<char *>(malloc(sizeof(double))); 324 break; 325 case SQL_TIMESTAMP: 326 pVar->sqldata = static_cast<char*>(malloc(sizeof(ISC_TIMESTAMP))); 327 break; 328 // an ARRAY is in fact a BLOB of a specialized type 329 // See https://firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-datatypes-bnrytypes.html#fblangref25-datatypes-array 330 case SQL_ARRAY: 331 case SQL_BLOB: 332 pVar->sqldata = static_cast<char*>(malloc(sizeof(ISC_QUAD))); 333 break; 334 case SQL_TYPE_TIME: 335 pVar->sqldata = static_cast<char*>(malloc(sizeof(ISC_TIME))); 336 break; 337 case SQL_TYPE_DATE: 338 pVar->sqldata = static_cast<char*>(malloc(sizeof(ISC_DATE))); 339 break; 340 case SQL_INT64: 341 pVar->sqldata = static_cast<char *>(malloc(sizeof(sal_Int64))); 342 break; 343 case SQL_BOOLEAN: 344 pVar->sqldata = static_cast<char *>(malloc(sizeof(sal_Bool))); 345 break; 346 // See https://www.firebirdsql.org/file/documentation/html/en/refdocs/fblangref25/firebird-25-language-reference.html#fblangref25-datatypes-special-sqlnull 347 case SQL_NULL: 348 pVar->sqldata = nullptr; 349 break; 350 case SQL_QUAD: 351 assert(false); // TODO: implement 352 break; 353 default: 354 SAL_WARN("connectivity.firebird", "Unknown type: " << dtype); 355 assert(false); 356 break; 357 } 358 /* allocate variable to hold NULL status */ 359 pVar->sqlind = static_cast<short *>(malloc(sizeof(short))); 360 } 361 } 362 363 void firebird::freeSQLVAR(XSQLDA* pSqlda) 364 { 365 XSQLVAR* pVar = pSqlda->sqlvar; 366 for (int i=0; i < pSqlda->sqld; i++, pVar++) 367 { 368 int dtype = (pVar->sqltype & ~1); /* drop flag bit for now */ 369 switch(dtype) { 370 case SQL_TEXT: 371 case SQL_VARYING: 372 case SQL_SHORT: 373 case SQL_LONG: 374 case SQL_FLOAT: 375 case SQL_DOUBLE: 376 case SQL_D_FLOAT: 377 case SQL_TIMESTAMP: 378 // an ARRAY is in fact a BLOB of a specialized type 379 // See https://firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-datatypes-bnrytypes.html#fblangref25-datatypes-array 380 case SQL_ARRAY: 381 case SQL_BLOB: 382 case SQL_INT64: 383 case SQL_TYPE_TIME: 384 case SQL_TYPE_DATE: 385 case SQL_BOOLEAN: 386 if(pVar->sqldata) 387 { 388 free(pVar->sqldata); 389 pVar->sqldata = nullptr; 390 } 391 break; 392 case SQL_NULL: 393 // See SQL_NULL case in mallocSQLVAR 394 assert(pVar->sqldata == nullptr); 395 break; 396 case SQL_QUAD: 397 assert(false); // TODO: implement 398 break; 399 default: 400 SAL_WARN("connectivity.firebird", "Unknown type: " << dtype); 401 // assert(false); 402 break; 403 } 404 405 if(pVar->sqlind) 406 { 407 free(pVar->sqlind); 408 pVar->sqlind = nullptr; 409 } 410 } 411 } 412 413 414 OUString firebird::escapeWith( const OUString& sText, const char aKey, const char aEscapeChar) 415 { 416 OUString sRet(sText); 417 sal_Int32 aIndex = 0; 418 for (;;) 419 { 420 aIndex = sRet.indexOf(aKey, aIndex); 421 if ( aIndex <= 0 || aIndex >= sRet.getLength()) 422 break; 423 sRet = sRet.replaceAt(aIndex, 1, rtl::OUStringConcatenation(OUStringChar(aEscapeChar) + OUStringChar(aKey)) ); 424 aIndex += 2; 425 } 426 427 return sRet; 428 } 429 430 sal_Int64 firebird::pow10Integer(int nDecimalCount) 431 { 432 sal_Int64 nRet = 1; 433 for(int i=0; i< nDecimalCount; i++) 434 { 435 nRet *= 10; 436 } 437 return nRet; 438 } 439 440 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ 441
