xref: /core/connectivity/source/drivers/firebird/Util.cxx (revision f6033dca9dc5e2640e796c2ba8853a8e157e338c)
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 
sanitizeIdentifier(std::u16string_view rIdentifier)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 
StatusVectorToString(const ISC_STATUS_ARRAY & rStatusVector,std::u16string_view rCause)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                 + 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 
evaluateStatusVector(const ISC_STATUS_ARRAY & rStatusVector,std::u16string_view rCause,const uno::Reference<XInterface> & _rxContext)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 
lcl_getNumberType(short aType,NumberSubType aSubType)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 }
lcl_getCharColumnType(short aType,std::u16string_view sCharset)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 
getSdbcType() const118 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 
getColumnTypeName() const183 OUString firebird::ColumnTypeInfo::getColumnTypeName() const
184 {
185     sal_Int32 nDataType = this->getSdbcType();
186     switch (nDataType)
187     {
188         case DataType::BIT:
189             return u"BIT"_ustr;
190         case DataType::TINYINT:
191             return u"TINYINT"_ustr;
192         case DataType::SMALLINT:
193             return u"SMALLINT"_ustr;
194         case DataType::INTEGER:
195             return u"INTEGER"_ustr;
196         case DataType::BIGINT:
197             return u"BIGINT"_ustr;
198         case DataType::FLOAT:
199             return u"FLOAT"_ustr;
200         case DataType::REAL:
201             return u"REAL"_ustr;
202         case DataType::DOUBLE:
203             return u"DOUBLE"_ustr;
204         case DataType::NUMERIC:
205             return u"NUMERIC"_ustr;
206         case DataType::DECIMAL:
207             return u"DECIMAL"_ustr;
208         case DataType::CHAR:
209             return u"CHAR"_ustr;
210         case DataType::VARCHAR:
211             return u"VARCHAR"_ustr;
212         case DataType::LONGVARCHAR:
213             return u"LONGVARCHAR"_ustr;
214         case DataType::DATE:
215             return u"DATE"_ustr;
216         case DataType::TIME:
217             return u"TIME"_ustr;
218         case DataType::TIMESTAMP:
219             return u"TIMESTAMP"_ustr;
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 u"CHARACTER"_ustr;
225         case DataType::VARBINARY:
226             // see above comment about DataType::BINARY.
227             return u"CHARACTER VARYING"_ustr;
228         case DataType::LONGVARBINARY:
229             return "BLOB SUB_TYPE " + OUString::number(static_cast<short>(BlobSubtype::Image));
230         case DataType::ARRAY:
231             return u"ARRAY"_ustr;
232         case DataType::BLOB:
233             return u"BLOB SUB_TYPE BINARY"_ustr;
234         case DataType::CLOB:
235             return u"BLOB SUB_TYPE TEXT"_ustr;
236         case DataType::BOOLEAN:
237             return u"BOOLEAN"_ustr;
238         case DataType::SQLNULL:
239             return u"NULL"_ustr;
240         default:
241             assert(false); // Should never happen
242             return OUString();
243      }
244 }
245 
getFBTypeFromBlrType(short blrType)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 
mallocSQLVAR(XSQLDA * pSqlda)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         case SQL_D_FLOAT:
321             pVar->sqldata = static_cast<char *>(malloc(sizeof(double)));
322             break;
323         case SQL_TIMESTAMP:
324             pVar->sqldata = static_cast<char*>(malloc(sizeof(ISC_TIMESTAMP)));
325             break;
326         // an ARRAY is in fact a BLOB of a specialized type
327         // See https://firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-datatypes-bnrytypes.html#fblangref25-datatypes-array
328         case SQL_ARRAY:
329         case SQL_BLOB:
330             pVar->sqldata = static_cast<char*>(malloc(sizeof(ISC_QUAD)));
331             break;
332         case SQL_TYPE_TIME:
333             pVar->sqldata = static_cast<char*>(malloc(sizeof(ISC_TIME)));
334             break;
335         case SQL_TYPE_DATE:
336             pVar->sqldata = static_cast<char*>(malloc(sizeof(ISC_DATE)));
337             break;
338         case SQL_INT64:
339             pVar->sqldata = static_cast<char *>(malloc(sizeof(sal_Int64)));
340             break;
341         case SQL_BOOLEAN:
342             pVar->sqldata = static_cast<char *>(malloc(sizeof(sal_Bool)));
343             break;
344         // See https://www.firebirdsql.org/file/documentation/html/en/refdocs/fblangref25/firebird-25-language-reference.html#fblangref25-datatypes-special-sqlnull
345         case SQL_NULL:
346             pVar->sqldata = nullptr;
347             break;
348         case SQL_QUAD:
349             assert(false); // TODO: implement
350             break;
351         default:
352             SAL_WARN("connectivity.firebird", "Unknown type: " << dtype);
353             assert(false);
354             break;
355         }
356         /* allocate variable to hold NULL status */
357         pVar->sqlind = static_cast<short *>(malloc(sizeof(short)));
358     }
359 }
360 
freeSQLVAR(XSQLDA * pSqlda)361 void firebird::freeSQLVAR(XSQLDA* pSqlda)
362 {
363     XSQLVAR* pVar = pSqlda->sqlvar;
364     for (int i=0; i < pSqlda->sqld; i++, pVar++)
365     {
366         int dtype = (pVar->sqltype & ~1); /* drop flag bit for now */
367         switch(dtype) {
368         case SQL_TEXT:
369         case SQL_VARYING:
370         case SQL_SHORT:
371         case SQL_LONG:
372         case SQL_FLOAT:
373         case SQL_DOUBLE:
374         case SQL_D_FLOAT:
375         case SQL_TIMESTAMP:
376         // an ARRAY is in fact a BLOB of a specialized type
377         // See https://firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-datatypes-bnrytypes.html#fblangref25-datatypes-array
378         case SQL_ARRAY:
379         case SQL_BLOB:
380         case SQL_INT64:
381         case SQL_TYPE_TIME:
382         case SQL_TYPE_DATE:
383         case SQL_BOOLEAN:
384             if(pVar->sqldata)
385             {
386                 free(pVar->sqldata);
387                 pVar->sqldata = nullptr;
388             }
389             break;
390         case SQL_NULL:
391             // See SQL_NULL case in mallocSQLVAR
392             assert(pVar->sqldata == nullptr);
393             break;
394         case SQL_QUAD:
395             assert(false); // TODO: implement
396             break;
397         default:
398             SAL_WARN("connectivity.firebird", "Unknown type: " << dtype);
399 //            assert(false);
400             break;
401         }
402 
403         if(pVar->sqlind)
404         {
405             free(pVar->sqlind);
406             pVar->sqlind = nullptr;
407         }
408     }
409 }
410 
411 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
412