LibreOffice Module connectivity (master) 1
Util.cxx
Go to the documentation of this file.
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
18using namespace ::connectivity;
19
20using namespace ::com::sun::star;
21using namespace ::com::sun::star::sdbc;
22using namespace ::com::sun::star::uno;
23
24using namespace firebird;
25
26OUString 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
34OUString 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 + 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
62void 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
73static 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}
98static 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
118sal_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
183OUString 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
246short 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
296void 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
363void 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
414sal_Int64 firebird::pow10Integer(int nDecimalCount)
415{
416 sal_Int64 nRet = 1;
417 for(int i=0; i< nDecimalCount; i++)
418 {
419 nRet *= 10;
420 }
421 return nRet;
422}
423
424/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
sal_Int32 nDataType
uno::Type m_aType
static sal_Int32 lcl_getNumberType(short aType, NumberSubType aSubType)
Definition: Util.cxx:73
static sal_Int32 lcl_getCharColumnType(short aType, std::u16string_view sCharset)
Definition: Util.cxx:98
float u
#define SAL_WARN(area, stream)
NumberSubType
Numeric and decimal types can be identified by their subtype in Firebird API.
Definition: Util.hxx:37
void freeSQLVAR(XSQLDA *pSqlda)
bool IndicatesError(const ISC_STATUS_ARRAY &rStatusVector)
Definition: Util.hxx:92
short getFBTypeFromBlrType(short blrType)
Internally (i.e.
OUString sanitizeIdentifier(std::u16string_view rIdentifier)
Make sure an identifier is safe to use within the database.
OUString StatusVectorToString(const ISC_STATUS_ARRAY &rStatusVector, std::u16string_view rCause)
sal_Int64 pow10Integer(int nDecimalCount)
void evaluateStatusVector(const ISC_STATUS_ARRAY &rStatusVector, std::u16string_view aCause, const css::uno::Reference< css::uno::XInterface > &_rxContext)
Evaluate a firebird status vector and throw exceptions as necessary.
void mallocSQLVAR(XSQLDA *pSqlda)
int i
std::basic_string_view< charT, traits > trim(std::basic_string_view< charT, traits > str)
unsigned char sal_Bool