1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <connectivity/dbtools.hxx>
21 #include <connectivity/dbconversion.hxx>
22 #include <connectivity/dbcharset.hxx>
23 #include <SQLStatementHelper.hxx>
24 #include <unotools/confignode.hxx>
25 #include <resource/sharedresources.hxx>
26 #include <strings.hrc>
27 #include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp>
28 #include <com/sun/star/sdbc/SQLException.hpp>
29 #include <com/sun/star/sdbc/XConnection.hpp>
30 #include <com/sun/star/sdbc/XDataSource.hpp>
31 #include <com/sun/star/sdbc/ColumnValue.hpp>
32 #include <com/sun/star/sdbc/DataType.hpp>
33 #include <com/sun/star/sdbc/DriverManager.hpp>
34 #include <com/sun/star/sdbc/XRow.hpp>
35 #include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
36 #include <com/sun/star/sdbcx/XKeysSupplier.hpp>
37 #include <com/sun/star/sdbcx/XDataDefinitionSupplier.hpp>
38 #include <com/sun/star/sdbcx/Privilege.hpp>
39 #include <com/sun/star/container/XIndexAccess.hpp>
40 #include <com/sun/star/sdbc/KeyRule.hpp>
41 #include <com/sun/star/sdbcx/KeyType.hpp>
42 #include <TConnection.hxx>
43 #include <connectivity/sdbcx/VColumn.hxx>
44 #include <com/sun/star/frame/XModel.hpp>
45 #include <com/sun/star/container/XChild.hpp>
46 
47 #include <comphelper/types.hxx>
48 #include <tools/diagnose_ex.h>
49 #include <unotools/sharedunocomponent.hxx>
50 #include <algorithm>
51 #include <string_view>
52 
53 namespace dbtools
54 {
55 
56     using namespace ::com::sun::star::uno;
57     using namespace ::com::sun::star::beans;
58     using namespace ::com::sun::star::sdb;
59     using namespace ::com::sun::star::sdbc;
60     using namespace ::com::sun::star::sdbcx;
61     using namespace ::com::sun::star::lang;
62     using namespace ::com::sun::star::container;
63     using namespace ::com::sun::star::frame;
64     using namespace connectivity;
65     using namespace comphelper;
66 
createStandardTypePart(const Reference<XPropertySet> & xColProp,const Reference<XConnection> & _xConnection,std::u16string_view _sCreatePattern)67 OUString createStandardTypePart(const Reference< XPropertySet >& xColProp,const Reference< XConnection>& _xConnection,std::u16string_view _sCreatePattern)
68 {
69 
70     Reference<XDatabaseMetaData> xMetaData = _xConnection->getMetaData();
71 
72     ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap();
73 
74     OUString sTypeName;
75     sal_Int32       nDataType   = 0;
76     sal_Int32       nPrecision  = 0;
77     sal_Int32       nScale      = 0;
78 
79     nDataType = nPrecision = nScale = 0;
80 
81     xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_TYPENAME))           >>= sTypeName;
82     xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_TYPE))               >>= nDataType;
83     xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_PRECISION))          >>= nPrecision;
84     xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCALE))              >>= nScale;
85 
86     OUStringBuffer aSql;
87 
88     // check if the user enter a specific string to create autoincrement values
89     OUString sAutoIncrementValue;
90     Reference<XPropertySetInfo> xPropInfo = xColProp->getPropertySetInfo();
91     if ( xPropInfo.is() && xPropInfo->hasPropertyByName(rPropMap.getNameByIndex(PROPERTY_ID_AUTOINCREMENTCREATION)) )
92         xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_AUTOINCREMENTCREATION)) >>= sAutoIncrementValue;
93     // look if we have to use precisions
94     bool bUseLiteral = false;
95     OUString sPrefix,sPostfix,sCreateParams;
96     {
97         Reference<XResultSet> xRes = xMetaData->getTypeInfo();
98         if(xRes.is())
99         {
100             Reference<XRow> xRow(xRes,UNO_QUERY);
101             while(xRes->next())
102             {
103                 OUString sTypeName2Cmp = xRow->getString(1);
104                 sal_Int32 nType = xRow->getShort(2);
105                 sPrefix = xRow->getString (4);
106                 sPostfix = xRow->getString (5);
107                 sCreateParams = xRow->getString(6);
108                 // first identical type will be used if typename is empty
109                 if ( sTypeName.isEmpty() && nType == nDataType )
110                     sTypeName = sTypeName2Cmp;
111 
112                 if( sTypeName.equalsIgnoreAsciiCase(sTypeName2Cmp) && nType == nDataType && !sCreateParams.isEmpty() && !xRow->wasNull())
113                 {
114                     bUseLiteral = true;
115                     break;
116                 }
117             }
118         }
119     }
120 
121     if ( !sAutoIncrementValue.isEmpty() )
122     {
123         sal_Int32 nIndex = sTypeName.indexOf(sAutoIncrementValue);
124         if (nIndex != -1)
125             sTypeName = sTypeName.replaceAt(nIndex,sTypeName.getLength() - nIndex,OUString());
126     }
127 
128     if ( (nPrecision > 0 || nScale > 0) && bUseLiteral )
129     {
130         sal_Int32 nParenPos = sTypeName.indexOf('(');
131         if ( nParenPos == -1 )
132         {
133             aSql.append(sTypeName);
134             aSql.append("(");
135         }
136         else
137         {
138             aSql.append(sTypeName.subView(0, ++nParenPos));
139         }
140 
141         if ( nPrecision > 0 && nDataType != DataType::TIMESTAMP )
142         {
143             aSql.append(nPrecision);
144             if ( (nScale > 0) || (!_sCreatePattern.empty() && sCreateParams.indexOf(_sCreatePattern) != -1) )
145                 aSql.append(",");
146         }
147         if ( (nScale > 0) || ( !_sCreatePattern.empty() && sCreateParams.indexOf(_sCreatePattern) != -1 ) || nDataType == DataType::TIMESTAMP )
148             aSql.append(nScale);
149 
150         if ( nParenPos == -1 )
151             aSql.append(")");
152         else
153         {
154             nParenPos = sTypeName.indexOf(')',nParenPos);
155             aSql.append(sTypeName.subView(nParenPos));
156         }
157     }
158     else
159         aSql.append(sTypeName); // simply add the type name
160 
161     OUString aDefault = ::comphelper::getString(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_DEFAULTVALUE)));
162     if ( !aDefault.isEmpty() )
163     {
164         aSql.append(" DEFAULT ");
165         aSql.append(sPrefix);
166         aSql.append(aDefault);
167         aSql.append(sPostfix);
168     } // if ( aDefault.getLength() )
169 
170     return aSql.makeStringAndClear();
171 }
172 
createStandardColumnPart(const Reference<XPropertySet> & xColProp,const Reference<XConnection> & _xConnection,ISQLStatementHelper * _pHelper,std::u16string_view _sCreatePattern)173 OUString createStandardColumnPart(const Reference< XPropertySet >& xColProp,const Reference< XConnection>& _xConnection,ISQLStatementHelper* _pHelper,std::u16string_view _sCreatePattern)
174 {
175     Reference<XDatabaseMetaData> xMetaData = _xConnection->getMetaData();
176 
177     ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap();
178 
179     bool bIsAutoIncrement = false;
180     xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_ISAUTOINCREMENT))    >>= bIsAutoIncrement;
181 
182     const OUString sQuoteString = xMetaData->getIdentifierQuoteString();
183     OUStringBuffer aSql = ::dbtools::quoteName(sQuoteString,::comphelper::getString(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME))));
184 
185     // check if the user enter a specific string to create autoincrement values
186     OUString sAutoIncrementValue;
187     Reference<XPropertySetInfo> xPropInfo = xColProp->getPropertySetInfo();
188     if ( xPropInfo.is() && xPropInfo->hasPropertyByName(rPropMap.getNameByIndex(PROPERTY_ID_AUTOINCREMENTCREATION)) )
189         xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_AUTOINCREMENTCREATION)) >>= sAutoIncrementValue;
190 
191     aSql.append(" ");
192 
193     aSql.append(createStandardTypePart(xColProp, _xConnection, _sCreatePattern));
194 
195     if(::comphelper::getINT32(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_ISNULLABLE))) == ColumnValue::NO_NULLS)
196         aSql.append(" NOT NULL");
197 
198     if ( bIsAutoIncrement && !sAutoIncrementValue.isEmpty())
199     {
200         aSql.append(" ");
201         aSql.append(sAutoIncrementValue);
202     }
203 
204     if ( _pHelper )
205         _pHelper->addComment(xColProp,aSql);
206 
207     return aSql.makeStringAndClear();
208 }
209 
210 
createStandardCreateStatement(const Reference<XPropertySet> & descriptor,const Reference<XConnection> & _xConnection,ISQLStatementHelper * _pHelper,std::u16string_view _sCreatePattern)211 OUString createStandardCreateStatement(const Reference< XPropertySet >& descriptor,const Reference< XConnection>& _xConnection,ISQLStatementHelper* _pHelper,std::u16string_view _sCreatePattern)
212 {
213     OUStringBuffer aSql("CREATE TABLE ");
214     OUString sCatalog,sSchema,sTable,sComposedName;
215 
216     Reference<XDatabaseMetaData> xMetaData = _xConnection->getMetaData();
217     ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap();
218 
219     descriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME))  >>= sCatalog;
220     descriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCHEMANAME))   >>= sSchema;
221     descriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME))         >>= sTable;
222 
223     sComposedName = ::dbtools::composeTableName( xMetaData, sCatalog, sSchema, sTable, true, ::dbtools::EComposeRule::InTableDefinitions );
224     if ( sComposedName.isEmpty() )
225         ::dbtools::throwFunctionSequenceException(_xConnection);
226 
227     aSql.append(sComposedName);
228     aSql.append(" (");
229 
230     // columns
231     Reference<XColumnsSupplier> xColumnSup(descriptor,UNO_QUERY);
232     Reference<XIndexAccess> xColumns(xColumnSup->getColumns(),UNO_QUERY);
233     // check if there are columns
234     if(!xColumns.is() || !xColumns->getCount())
235         ::dbtools::throwFunctionSequenceException(_xConnection);
236 
237     Reference< XPropertySet > xColProp;
238 
239     sal_Int32 nCount = xColumns->getCount();
240     for(sal_Int32 i=0;i<nCount;++i)
241     {
242         if ( (xColumns->getByIndex(i) >>= xColProp) && xColProp.is() )
243         {
244             aSql.append(createStandardColumnPart(xColProp,_xConnection,_pHelper,_sCreatePattern));
245             aSql.append(",");
246         }
247     }
248     return aSql.makeStringAndClear();
249 }
250 namespace
251 {
generateColumnNames(const Reference<XIndexAccess> & _xColumns,const Reference<XDatabaseMetaData> & _xMetaData)252     OUString generateColumnNames(const Reference<XIndexAccess>& _xColumns,const Reference<XDatabaseMetaData>& _xMetaData)
253     {
254         ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap();
255 
256         const OUString sQuote(_xMetaData->getIdentifierQuoteString());
257         OUStringBuffer sSql( " (" );
258         Reference< XPropertySet > xColProp;
259 
260         sal_Int32 nColCount  = _xColumns->getCount();
261         for(sal_Int32 i=0;i<nColCount;++i)
262         {
263             if ( (_xColumns->getByIndex(i) >>= xColProp) && xColProp.is() )
264                 sSql.append( ::dbtools::quoteName(sQuote,::comphelper::getString(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME)))) +
265                             ",");
266         }
267 
268         if ( nColCount )
269             sSql[sSql.getLength()-1] = ')';
270         return sSql.makeStringAndClear();
271     }
272 }
273 
createStandardKeyStatement(const Reference<XPropertySet> & descriptor,const Reference<XConnection> & _xConnection)274 OUString createStandardKeyStatement(const Reference< XPropertySet >& descriptor,const Reference< XConnection>& _xConnection)
275 {
276     Reference<XDatabaseMetaData> xMetaData = _xConnection->getMetaData();
277     ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap();
278 
279     OUStringBuffer aSql;
280     // keys
281     Reference<XKeysSupplier> xKeySup(descriptor,UNO_QUERY);
282     Reference<XIndexAccess> xKeys = xKeySup->getKeys();
283     if ( xKeys.is() )
284     {
285         Reference< XPropertySet > xColProp;
286         Reference<XIndexAccess> xColumns;
287         Reference<XColumnsSupplier> xColumnSup;
288         OUString sCatalog,sSchema,sTable,sComposedName;
289         bool bPKey = false;
290         for(sal_Int32 i=0;i<xKeys->getCount();++i)
291         {
292             if ( (xKeys->getByIndex(i) >>= xColProp) && xColProp.is() )
293             {
294 
295                 sal_Int32 nKeyType      = ::comphelper::getINT32(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_TYPE)));
296 
297                 if ( nKeyType == KeyType::PRIMARY )
298                 {
299                     if(bPKey)
300                         ::dbtools::throwFunctionSequenceException(_xConnection);
301 
302                     bPKey = true;
303                     xColumnSup.set(xColProp,UNO_QUERY);
304                     xColumns.set(xColumnSup->getColumns(),UNO_QUERY);
305                     if(!xColumns.is() || !xColumns->getCount())
306                         ::dbtools::throwFunctionSequenceException(_xConnection);
307 
308                     aSql.append(" PRIMARY KEY ");
309                     aSql.append(generateColumnNames(xColumns,xMetaData));
310                 }
311                 else if(nKeyType == KeyType::UNIQUE)
312                 {
313                     xColumnSup.set(xColProp,UNO_QUERY);
314                     xColumns.set(xColumnSup->getColumns(),UNO_QUERY);
315                     if(!xColumns.is() || !xColumns->getCount())
316                         ::dbtools::throwFunctionSequenceException(_xConnection);
317 
318                     aSql.append(" UNIQUE ");
319                     aSql.append(generateColumnNames(xColumns,xMetaData));
320                 }
321                 else if(nKeyType == KeyType::FOREIGN)
322                 {
323                     sal_Int32 nDeleteRule   = getINT32(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_DELETERULE)));
324 
325                     xColumnSup.set(xColProp,UNO_QUERY);
326                     xColumns.set(xColumnSup->getColumns(),UNO_QUERY);
327                     if(!xColumns.is() || !xColumns->getCount())
328                         ::dbtools::throwFunctionSequenceException(_xConnection);
329 
330                     aSql.append(" FOREIGN KEY ");
331                     OUString sRefTable = getString(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_REFERENCEDTABLE)));
332                     ::dbtools::qualifiedNameComponents(xMetaData,
333                                                         sRefTable,
334                                                         sCatalog,
335                                                         sSchema,
336                                                         sTable,
337                                                         ::dbtools::EComposeRule::InDataManipulation);
338                     sComposedName = ::dbtools::composeTableName( xMetaData, sCatalog, sSchema, sTable, true, ::dbtools::EComposeRule::InTableDefinitions );
339 
340 
341                     if ( sComposedName.isEmpty() )
342                         ::dbtools::throwFunctionSequenceException(_xConnection);
343 
344                     aSql.append(generateColumnNames(xColumns,xMetaData));
345 
346                     switch(nDeleteRule)
347                     {
348                         case KeyRule::CASCADE:
349                             aSql.append(" ON DELETE CASCADE ");
350                             break;
351                         case KeyRule::RESTRICT:
352                             aSql.append(" ON DELETE RESTRICT ");
353                             break;
354                         case KeyRule::SET_NULL:
355                             aSql.append(" ON DELETE SET NULL ");
356                             break;
357                         case KeyRule::SET_DEFAULT:
358                             aSql.append(" ON DELETE SET DEFAULT ");
359                             break;
360                         default:
361                             ;
362                     }
363                 }
364             }
365         }
366     }
367 
368     if ( !aSql.isEmpty() )
369     {
370         if ( aSql[aSql.getLength() - 1] == ',' )
371             aSql[aSql.getLength() - 1] = ')';
372         else
373             aSql.append(")");
374     }
375 
376     return aSql.makeStringAndClear();
377 
378 }
379 
createSqlCreateTableStatement(const Reference<XPropertySet> & descriptor,const Reference<XConnection> & _xConnection)380 OUString createSqlCreateTableStatement(  const Reference< XPropertySet >& descriptor,
381                                          const Reference< XConnection>& _xConnection)
382 {
383     OUString aSql = ::dbtools::createStandardCreateStatement(descriptor,_xConnection,nullptr,{});
384     const OUString sKeyStmt = ::dbtools::createStandardKeyStatement(descriptor,_xConnection);
385     if ( !sKeyStmt.isEmpty() )
386         aSql += sKeyStmt;
387     else
388     {
389         if ( aSql.endsWith(",") )
390             aSql = aSql.replaceAt(aSql.getLength()-1, 1, ")");
391         else
392             aSql += ")";
393     }
394     return aSql;
395 }
396 namespace
397 {
lcl_createSDBCXColumn(const Reference<XNameAccess> & _xPrimaryKeyColumns,const Reference<XConnection> & _xConnection,const Any & _aCatalog,const OUString & _aSchema,const OUString & _aTable,const OUString & _rQueryName,const OUString & _rName,bool _bCase,bool _bQueryForInfo,bool _bIsAutoIncrement,bool _bIsCurrency,sal_Int32 _nDataType)398     Reference<XPropertySet> lcl_createSDBCXColumn(const Reference<XNameAccess>& _xPrimaryKeyColumns,
399                                           const Reference<XConnection>& _xConnection,
400                                           const Any& _aCatalog,
401                                           const OUString& _aSchema,
402                                           const OUString& _aTable,
403                                           const OUString& _rQueryName,
404                                           const OUString& _rName,
405                                           bool _bCase,
406                                           bool _bQueryForInfo,
407                                           bool _bIsAutoIncrement,
408                                           bool _bIsCurrency,
409                                           sal_Int32 _nDataType)
410     {
411         Reference<XPropertySet> xProp;
412         Reference<XDatabaseMetaData> xMetaData = _xConnection->getMetaData();
413         Reference< XResultSet > xResult = xMetaData->getColumns(_aCatalog, _aSchema, _aTable, _rQueryName);
414         OUString sCatalog;
415         _aCatalog >>= sCatalog;
416 
417         if ( xResult.is() )
418         {
419             UStringMixEqual aMixCompare(_bCase);
420             Reference< XRow > xRow(xResult,UNO_QUERY);
421             while( xResult->next() )
422             {
423                 if ( aMixCompare(xRow->getString(4),_rName) )
424                 {
425                     sal_Int32       nField5 = xRow->getInt(5);
426                     OUString aField6 = xRow->getString(6);
427                     sal_Int32       nField7 = xRow->getInt(7)
428                                 ,   nField9 = xRow->getInt(9)
429                                 ,   nField11= xRow->getInt(11);
430                     OUString sField12 = xRow->getString(12),
431                                     sField13 = xRow->getString(13);
432                     ::comphelper::disposeComponent(xRow);
433 
434                     bool bAutoIncrement = _bIsAutoIncrement
435                             ,bIsCurrency    = _bIsCurrency;
436                     if ( _bQueryForInfo )
437                     {
438                         const OUString sQuote = xMetaData->getIdentifierQuoteString();
439                         OUString sQuotedName  = ::dbtools::quoteName(sQuote,_rName);
440                         OUString sComposedName = composeTableNameForSelect(_xConnection, getString( _aCatalog ), _aSchema, _aTable );
441 
442                         ColumnInformationMap aInfo(_bCase);
443                         collectColumnInformation(_xConnection,sComposedName,sQuotedName,aInfo);
444                         ColumnInformationMap::const_iterator aIter = aInfo.begin();
445                         if ( aIter != aInfo.end() )
446                         {
447                             bAutoIncrement  = aIter->second.first.first;
448                             bIsCurrency     = aIter->second.first.second;
449                             if ( DataType::OTHER == nField5 )
450                                 nField5     = aIter->second.second;
451                         }
452                     }
453                     else if ( DataType::OTHER == nField5 )
454                         nField5 = _nDataType;
455 
456                     if ( nField11 != ColumnValue::NO_NULLS )
457                     {
458                         try
459                         {
460                             if ( _xPrimaryKeyColumns.is() )
461                             {
462                                 if ( _xPrimaryKeyColumns->hasByName(_rName) )
463                                     nField11 = ColumnValue::NO_NULLS;
464 
465                             }
466                             else
467                             {
468                                 Reference< XResultSet > xPKeys = xMetaData->getPrimaryKeys( _aCatalog, _aSchema, _aTable );
469                                 Reference< XRow > xPKeyRow( xPKeys, UNO_QUERY_THROW );
470                                 while( xPKeys->next() ) // there can be only one primary key
471                                 {
472                                     OUString sKeyColumn = xPKeyRow->getString(4);
473                                     if ( aMixCompare(_rName,sKeyColumn) )
474                                     {
475                                         nField11 = ColumnValue::NO_NULLS;
476                                         break;
477                                     }
478                                 }
479                             }
480                         }
481                         catch(SQLException&)
482                         {
483                             TOOLS_WARN_EXCEPTION( "connectivity.commontools", "lcl_createSDBCXColumn" );
484                         }
485                     }
486 
487                     xProp = new connectivity::sdbcx::OColumn(_rName,
488                                                 aField6,
489                                                 sField13,
490                                                 sField12,
491                                                 nField11,
492                                                 nField7,
493                                                 nField9,
494                                                 nField5,
495                                                 bAutoIncrement,
496                                                 false,
497                                                 bIsCurrency,
498                                                 _bCase,
499                                                 sCatalog,
500                                                 _aSchema,
501                                                 _aTable);
502 
503                     break;
504                 }
505             }
506         }
507 
508         return xProp;
509     }
510 
lcl_getXModel(const Reference<XInterface> & _xIface)511     Reference< XModel> lcl_getXModel(const Reference< XInterface>& _xIface)
512     {
513         Reference< XInterface > xParent = _xIface;
514         Reference< XModel > xModel(xParent,UNO_QUERY);
515         while( xParent.is() && !xModel.is() )
516         {
517             Reference<XChild> xChild(xParent,UNO_QUERY);
518             xParent.set(xChild.is() ? xChild->getParent() : Reference< XInterface >(),UNO_QUERY);
519             xModel.set(xParent,UNO_QUERY);
520         }
521         return xModel;
522     }
523 }
524 
createSDBCXColumn(const Reference<XPropertySet> & _xTable,const Reference<XConnection> & _xConnection,const OUString & _rName,bool _bCase,bool _bQueryForInfo,bool _bIsAutoIncrement,bool _bIsCurrency,sal_Int32 _nDataType)525 Reference<XPropertySet> createSDBCXColumn(const Reference<XPropertySet>& _xTable,
526                                           const Reference<XConnection>& _xConnection,
527                                           const OUString& _rName,
528                                           bool _bCase,
529                                           bool _bQueryForInfo,
530                                           bool _bIsAutoIncrement,
531                                           bool _bIsCurrency,
532                                           sal_Int32 _nDataType)
533 {
534     Reference<XPropertySet> xProp;
535     OSL_ENSURE(_xTable.is(),"Table is NULL!");
536     if ( !_xTable.is() )
537         return xProp;
538 
539     ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap();
540     Any aCatalog = _xTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME));
541     OUString sCatalog;
542     aCatalog >>= sCatalog;
543 
544     OUString aSchema, aTable;
545     _xTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCHEMANAME))  >>= aSchema;
546     _xTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME))        >>= aTable;
547 
548     Reference<XNameAccess> xPrimaryKeyColumns = getPrimaryKeyColumns_throw(_xTable);
549 
550     xProp = lcl_createSDBCXColumn(xPrimaryKeyColumns,_xConnection,aCatalog, aSchema, aTable, _rName,_rName,_bCase,_bQueryForInfo,_bIsAutoIncrement,_bIsCurrency,_nDataType);
551     if ( !xProp.is() )
552     {
553         xProp = lcl_createSDBCXColumn(xPrimaryKeyColumns,_xConnection,aCatalog, aSchema, aTable, "%",_rName,_bCase,_bQueryForInfo,_bIsAutoIncrement,_bIsCurrency,_nDataType);
554         if ( !xProp.is() )
555             xProp = new connectivity::sdbcx::OColumn(_rName,
556                                                 OUString(),OUString(),OUString(),
557                                                 ColumnValue::NULLABLE_UNKNOWN,
558                                                 0,
559                                                 0,
560                                                 DataType::VARCHAR,
561                                                 _bIsAutoIncrement,
562                                                 false,
563                                                 _bIsCurrency,
564                                                 _bCase,
565                                                 sCatalog,
566                                                 aSchema,
567                                                 aTable);
568 
569     }
570 
571     return xProp;
572 }
573 
574 
getBooleanDataSourceSetting(const Reference<XConnection> & _rxConnection,const char * _pAsciiSettingName)575 bool getBooleanDataSourceSetting( const Reference< XConnection >& _rxConnection, const char* _pAsciiSettingName )
576 {
577     bool bValue( false );
578     try
579     {
580         Reference< XPropertySet> xDataSourceProperties( findDataSource( _rxConnection ), UNO_QUERY );
581         OSL_ENSURE( xDataSourceProperties.is(), "::dbtools::getBooleanDataSourceSetting: somebody is using this with a non-SDB-level connection!" );
582         if ( xDataSourceProperties.is() )
583         {
584             Reference< XPropertySet > xSettings(
585                 xDataSourceProperties->getPropertyValue("Settings"),
586                 UNO_QUERY_THROW
587             );
588             OSL_VERIFY( xSettings->getPropertyValue( OUString::createFromAscii( _pAsciiSettingName ) ) >>= bValue );
589         }
590     }
591     catch( const Exception& )
592     {
593         DBG_UNHANDLED_EXCEPTION("connectivity.commontools");
594     }
595     return bValue;
596 }
597 
getDataSourceSetting(const Reference<XInterface> & _xChild,const OUString & _sAsciiSettingsName,Any & _rSettingsValue)598 bool getDataSourceSetting( const Reference< XInterface >& _xChild, const OUString& _sAsciiSettingsName,
599     Any& /* [out] */ _rSettingsValue )
600 {
601     bool bIsPresent = false;
602     try
603     {
604         const Reference< XPropertySet> xDataSourceProperties( findDataSource( _xChild ), UNO_QUERY );
605         if ( !xDataSourceProperties.is() )
606             return false;
607 
608         const Reference< XPropertySet > xSettings(
609                 xDataSourceProperties->getPropertyValue("Settings"),
610                 UNO_QUERY_THROW
611             );
612 
613         _rSettingsValue = xSettings->getPropertyValue( _sAsciiSettingsName );
614         bIsPresent = true;
615     }
616     catch( const Exception& )
617     {
618         bIsPresent = false;
619     }
620     return bIsPresent;
621 }
622 
getDataSourceSetting(const Reference<XInterface> & _rxDataSource,const char * _pAsciiSettingsName,Any & _rSettingsValue)623 bool getDataSourceSetting( const Reference< XInterface >& _rxDataSource, const char* _pAsciiSettingsName,
624     Any& /* [out] */ _rSettingsValue )
625 {
626     OUString sAsciiSettingsName = OUString::createFromAscii(_pAsciiSettingsName);
627     return getDataSourceSetting( _rxDataSource, sAsciiSettingsName,_rSettingsValue );
628 }
629 
isDataSourcePropertyEnabled(const Reference<XInterface> & _xProp,const OUString & _sProperty,bool _bDefault)630 bool isDataSourcePropertyEnabled(const Reference<XInterface>& _xProp, const OUString& _sProperty, bool _bDefault)
631 {
632     bool bEnabled = _bDefault;
633     try
634     {
635         Reference< XPropertySet> xProp(findDataSource(_xProp),UNO_QUERY);
636         if ( xProp.is() )
637         {
638             Sequence< PropertyValue > aInfo;
639             xProp->getPropertyValue("Info") >>= aInfo;
640             const PropertyValue* pValue =std::find_if(aInfo.begin(),
641                                                 aInfo.end(),
642                                                 [&_sProperty](const PropertyValue& lhs)
643                                                 { return lhs.Name == _sProperty; });
644             if ( pValue != aInfo.end() )
645                 pValue->Value >>= bEnabled;
646         }
647     }
648     catch(SQLException&)
649     {
650         DBG_UNHANDLED_EXCEPTION("connectivity.commontools");
651     }
652     return bEnabled;
653 }
654 
getDataDefinitionByURLAndConnection(const OUString & _rsUrl,const Reference<XConnection> & _xConnection,const Reference<XComponentContext> & _rxContext)655 Reference< XTablesSupplier> getDataDefinitionByURLAndConnection(
656             const OUString& _rsUrl,
657             const Reference< XConnection>& _xConnection,
658             const Reference< XComponentContext >& _rxContext)
659 {
660     Reference< XTablesSupplier> xTablesSup;
661     try
662     {
663         Reference< XDriverManager2 > xManager = DriverManager::create( _rxContext );
664         Reference< XDataDefinitionSupplier > xSupp( xManager->getDriverByURL( _rsUrl ), UNO_QUERY );
665 
666         if ( xSupp.is() )
667         {
668             xTablesSup = xSupp->getDataDefinitionByConnection( _xConnection );
669             OSL_ENSURE(xTablesSup.is(),"No table supplier!");
670         }
671     }
672     catch( const Exception& )
673     {
674         DBG_UNHANDLED_EXCEPTION("connectivity.commontools");
675     }
676     return xTablesSup;
677 }
678 
679 
getTablePrivileges(const Reference<XDatabaseMetaData> & _xMetaData,const OUString & _sCatalog,const OUString & _sSchema,const OUString & _sTable)680 sal_Int32 getTablePrivileges(const Reference< XDatabaseMetaData>& _xMetaData,
681                              const OUString& _sCatalog,
682                              const OUString& _sSchema,
683                              const OUString& _sTable)
684 {
685     OSL_ENSURE(_xMetaData.is(),"Invalid metadata!");
686     sal_Int32 nPrivileges = 0;
687     try
688     {
689         Any aVal;
690         if(!_sCatalog.isEmpty())
691             aVal <<= _sCatalog;
692         Reference< XResultSet > xPrivileges = _xMetaData->getTablePrivileges(aVal, _sSchema, _sTable);
693         Reference< XRow > xCurrentRow(xPrivileges, UNO_QUERY);
694 
695         const OUString sUserWorkingFor = _xMetaData->getUserName();
696         static const char sSELECT[] = "SELECT";
697         static const char sINSERT[] = "INSERT";
698         static const char sUPDATE[] = "UPDATE";
699         static const char sDELETE[] = "DELETE";
700         static const char sREAD[] = "READ";
701         static const char sCREATE[] = "CREATE";
702         static const char sALTER[] = "ALTER";
703         static const char sREFERENCE[] = "REFERENCE";
704         static const char sDROP[] = "DROP";
705 
706         if ( xCurrentRow.is() )
707         {
708             // after creation the set is positioned before the first record, per definition
709             OUString sPrivilege, sGrantee;
710             while ( xPrivileges->next() )
711             {
712                 sGrantee    = xCurrentRow->getString(5);
713                 sPrivilege  = xCurrentRow->getString(6);
714 
715                 if (!sUserWorkingFor.equalsIgnoreAsciiCase(sGrantee))
716                     continue;
717 
718                 if (sPrivilege.equalsIgnoreAsciiCase(sSELECT))
719                     nPrivileges |= Privilege::SELECT;
720                 else if (sPrivilege.equalsIgnoreAsciiCase(sINSERT))
721                     nPrivileges |= Privilege::INSERT;
722                 else if (sPrivilege.equalsIgnoreAsciiCase(sUPDATE))
723                     nPrivileges |= Privilege::UPDATE;
724                 else if (sPrivilege.equalsIgnoreAsciiCase(sDELETE))
725                     nPrivileges |= Privilege::DELETE;
726                 else if (sPrivilege.equalsIgnoreAsciiCase(sREAD))
727                     nPrivileges |= Privilege::READ;
728                 else if (sPrivilege.equalsIgnoreAsciiCase(sCREATE))
729                     nPrivileges |= Privilege::CREATE;
730                 else if (sPrivilege.equalsIgnoreAsciiCase(sALTER))
731                     nPrivileges |= Privilege::ALTER;
732                 else if (sPrivilege.equalsIgnoreAsciiCase(sREFERENCE))
733                     nPrivileges |= Privilege::REFERENCE;
734                 else if (sPrivilege.equalsIgnoreAsciiCase(sDROP))
735                     nPrivileges |= Privilege::DROP;
736             }
737         }
738         disposeComponent(xPrivileges);
739 
740         // Some drivers put a table privilege as soon as any column has the privilege,
741         // some drivers only if all columns have the privilege.
742         // To unify the situation, collect column privileges here, too.
743         Reference< XResultSet > xColumnPrivileges = _xMetaData->getColumnPrivileges(aVal, _sSchema, _sTable, "%");
744         Reference< XRow > xColumnCurrentRow(xColumnPrivileges, UNO_QUERY);
745         if ( xColumnCurrentRow.is() )
746         {
747             // after creation the set is positioned before the first record, per definition
748             OUString sPrivilege, sGrantee;
749             while ( xColumnPrivileges->next() )
750             {
751                 sGrantee    = xColumnCurrentRow->getString(6);
752                 sPrivilege  = xColumnCurrentRow->getString(7);
753 
754                 if (!sUserWorkingFor.equalsIgnoreAsciiCase(sGrantee))
755                     continue;
756 
757                 if (sPrivilege.equalsIgnoreAsciiCase(sSELECT))
758                     nPrivileges |= Privilege::SELECT;
759                 else if (sPrivilege.equalsIgnoreAsciiCase(sINSERT))
760                     nPrivileges |= Privilege::INSERT;
761                 else if (sPrivilege.equalsIgnoreAsciiCase(sUPDATE))
762                     nPrivileges |= Privilege::UPDATE;
763                 else if (sPrivilege.equalsIgnoreAsciiCase(sDELETE))
764                     nPrivileges |= Privilege::DELETE;
765                 else if (sPrivilege.equalsIgnoreAsciiCase(sREAD))
766                     nPrivileges |= Privilege::READ;
767                 else if (sPrivilege.equalsIgnoreAsciiCase(sCREATE))
768                     nPrivileges |= Privilege::CREATE;
769                 else if (sPrivilege.equalsIgnoreAsciiCase(sALTER))
770                     nPrivileges |= Privilege::ALTER;
771                 else if (sPrivilege.equalsIgnoreAsciiCase(sREFERENCE))
772                     nPrivileges |= Privilege::REFERENCE;
773                 else if (sPrivilege.equalsIgnoreAsciiCase(sDROP))
774                     nPrivileges |= Privilege::DROP;
775             }
776         }
777         disposeComponent(xColumnPrivileges);
778     }
779     catch(const SQLException& e)
780     {
781         // some drivers don't support any privileges so we assume that we are allowed to do all we want :-)
782         if(e.SQLState == "IM001")
783             nPrivileges |=  Privilege::DROP         |
784                             Privilege::REFERENCE    |
785                             Privilege::ALTER        |
786                             Privilege::CREATE       |
787                             Privilege::READ         |
788                             Privilege::DELETE       |
789                             Privilege::UPDATE       |
790                             Privilege::INSERT       |
791                             Privilege::SELECT;
792         else
793             OSL_FAIL("Could not collect the privileges !");
794     }
795     return nPrivileges;
796 }
797 
798 // we need some more information about the column
collectColumnInformation(const Reference<XConnection> & _xConnection,std::u16string_view _sComposedName,std::u16string_view _rName,ColumnInformationMap & _rInfo)799 void collectColumnInformation(const Reference< XConnection>& _xConnection,
800                               std::u16string_view _sComposedName,
801                               std::u16string_view _rName,
802                               ColumnInformationMap& _rInfo)
803 {
804     OUString sSelect = OUString::Concat("SELECT ") + _rName +
805         " FROM " + _sComposedName +
806         " WHERE 0 = 1";
807 
808     try
809     {
810         ::utl::SharedUNOComponent< XStatement > xStmt( _xConnection->createStatement() );
811         Reference< XPropertySet > xStatementProps( xStmt, UNO_QUERY_THROW );
812         xStatementProps->setPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_ESCAPEPROCESSING ), makeAny( false ) );
813         Reference< XResultSet > xResult( xStmt->executeQuery( sSelect ), UNO_SET_THROW );
814         Reference< XResultSetMetaDataSupplier > xSuppMeta( xResult, UNO_QUERY_THROW );
815         Reference< XResultSetMetaData > xMeta( xSuppMeta->getMetaData(), UNO_SET_THROW );
816 
817         sal_Int32 nCount = xMeta->getColumnCount();
818         OSL_ENSURE( nCount != 0, "::dbtools::collectColumnInformation: result set has empty (column-less) meta data!" );
819         for (sal_Int32 i=1; i <= nCount ; ++i)
820         {
821             _rInfo.emplace( xMeta->getColumnName(i),
822                 ColumnInformation(TBoolPair(xMeta->isAutoIncrement(i),xMeta->isCurrency(i)),xMeta->getColumnType(i)));
823         }
824     }
825     catch( const Exception& )
826     {
827         DBG_UNHANDLED_EXCEPTION("connectivity.commontools");
828     }
829 }
830 
831 
isEmbeddedInDatabase(const Reference<XInterface> & _rxComponent,Reference<XConnection> & _rxActualConnection)832 bool isEmbeddedInDatabase( const Reference< XInterface >& _rxComponent, Reference< XConnection >& _rxActualConnection )
833 {
834     bool bIsEmbedded = false;
835     try
836     {
837         Reference< XModel > xModel = lcl_getXModel( _rxComponent );
838 
839         if ( xModel.is() )
840         {
841             Sequence< PropertyValue > aArgs = xModel->getArgs();
842             const PropertyValue* pIter = aArgs.getConstArray();
843             const PropertyValue* pEnd  = pIter + aArgs.getLength();
844             for(;pIter != pEnd;++pIter)
845             {
846                 if ( pIter->Name == "ComponentData" )
847                 {
848                     Sequence<PropertyValue> aDocumentContext;
849                     pIter->Value >>= aDocumentContext;
850                     const PropertyValue* pContextIter = aDocumentContext.getConstArray();
851                     const PropertyValue* pContextEnd  = pContextIter + aDocumentContext.getLength();
852                     for(;pContextIter != pContextEnd;++pContextIter)
853                     {
854                         if (   pContextIter->Name == "ActiveConnection"
855                             && ( pContextIter->Value >>= _rxActualConnection )
856                            )
857                         {
858                             bIsEmbedded = true;
859                             break;
860                         }
861                     }
862                     break;
863                 }
864             }
865         }
866     }
867     catch(Exception&)
868     {
869         // not interested in
870     }
871     return bIsEmbedded;
872 }
873 
874 namespace
875 {
lcl_getEncodingName(rtl_TextEncoding _eEncoding)876     OUString lcl_getEncodingName( rtl_TextEncoding _eEncoding )
877     {
878         OUString sEncodingName;
879 
880         OCharsetMap aCharsets;
881         OCharsetMap::CharsetIterator aEncodingPos = aCharsets.find( _eEncoding );
882         OSL_ENSURE( aEncodingPos != aCharsets.end(), "lcl_getEncodingName: *which* encoding?" );
883         if ( aEncodingPos != aCharsets.end() )
884             sEncodingName = (*aEncodingPos).getIanaName();
885 
886         return sEncodingName;
887     }
888 }
889 
890 
convertUnicodeString(const OUString & _rSource,OString & _rDest,rtl_TextEncoding _eEncoding)891 sal_Int32 DBTypeConversion::convertUnicodeString( const OUString& _rSource, OString& _rDest, rtl_TextEncoding _eEncoding )
892 {
893     if ( !rtl_convertUStringToString( &_rDest.pData, _rSource.getStr(), _rSource.getLength(),
894             _eEncoding,
895             RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
896             RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE |
897             RTL_UNICODETOTEXT_FLAGS_PRIVATE_MAPTO0 )
898         )
899     {
900         SharedResources aResources;
901         OUString sMessage = aResources.getResourceStringWithSubstitution( STR_CANNOT_CONVERT_STRING,
902             "$string$", _rSource,
903             "$charset$",  lcl_getEncodingName( _eEncoding )
904         );
905 
906         throw SQLException(
907             sMessage,
908             nullptr,
909             "22018",
910             22018,
911             Any()
912         );
913     }
914 
915     return _rDest.getLength();
916 }
917 
918 
convertUnicodeStringToLength(const OUString & _rSource,OString & _rDest,sal_Int32 _nMaxLen,rtl_TextEncoding _eEncoding)919 sal_Int32 DBTypeConversion::convertUnicodeStringToLength( const OUString& _rSource, OString&  _rDest,
920    sal_Int32 _nMaxLen, rtl_TextEncoding _eEncoding )
921 {
922     sal_Int32 nLen = convertUnicodeString( _rSource, _rDest, _eEncoding );
923     if ( nLen > _nMaxLen )
924     {
925         SharedResources aResources;
926         OUString sMessage = aResources.getResourceStringWithSubstitution( STR_STRING_LENGTH_EXCEEDED,
927             "$string$", _rSource,
928             "$maxlen$", OUString::number( _nMaxLen ),
929             "$charset$", lcl_getEncodingName( _eEncoding )
930         );
931 
932         throw SQLException(
933             sMessage,
934             nullptr,
935             "22001",
936             22001,
937             Any()
938         );
939     }
940 
941     return nLen;
942 }
lcl_getReportEngines()943 static OUString lcl_getReportEngines()
944 {
945     return "org.openoffice.Office.DataAccess/ReportEngines";
946 }
947 
lcl_getDefaultReportEngine()948 static OUString lcl_getDefaultReportEngine()
949 {
950     return "DefaultReportEngine";
951 }
952 
lcl_getReportEngineNames()953 static OUString lcl_getReportEngineNames()
954 {
955     return "ReportEngineNames";
956 }
957 
getDefaultReportEngineServiceName(const Reference<XComponentContext> & _rxORB)958 OUString getDefaultReportEngineServiceName(const Reference< XComponentContext >& _rxORB)
959 {
960     ::utl::OConfigurationTreeRoot aReportEngines = ::utl::OConfigurationTreeRoot::createWithComponentContext(
961         _rxORB, lcl_getReportEngines(), -1, ::utl::OConfigurationTreeRoot::CM_READONLY);
962 
963     if ( aReportEngines.isValid() )
964     {
965         OUString sDefaultReportEngineName;
966         aReportEngines.getNodeValue(lcl_getDefaultReportEngine()) >>= sDefaultReportEngineName;
967         if ( !sDefaultReportEngineName.isEmpty() )
968         {
969             ::utl::OConfigurationNode aReportEngineNames = aReportEngines.openNode(lcl_getReportEngineNames());
970             if ( aReportEngineNames.isValid() )
971             {
972                 ::utl::OConfigurationNode aReportEngine = aReportEngineNames.openNode(sDefaultReportEngineName);
973                 if ( aReportEngine.isValid() )
974                 {
975                     OUString sRet;
976                     aReportEngine.getNodeValue("ServiceName") >>= sRet;
977                     return sRet;
978                 }
979             }
980         }
981         else
982             return "org.libreoffice.report.pentaho.SOReportJobFactory";
983     }
984     else
985         return "org.libreoffice.report.pentaho.SOReportJobFactory";
986     return OUString();
987 }
988 
isAggregateColumn(const Reference<XSingleSelectQueryComposer> & _xParser,const Reference<XPropertySet> & _xField)989 bool isAggregateColumn(const Reference< XSingleSelectQueryComposer > &_xParser, const Reference< XPropertySet > &_xField)
990 {
991     OUString sName;
992     _xField->getPropertyValue("Name") >>= sName;
993     Reference< XColumnsSupplier > xColumnsSupplier(_xParser, UNO_QUERY);
994     Reference< css::container::XNameAccess >  xCols;
995     if (xColumnsSupplier.is())
996         xCols = xColumnsSupplier->getColumns();
997 
998     return isAggregateColumn(xCols, sName);
999 }
1000 
isAggregateColumn(const Reference<XNameAccess> & _xColumns,const OUString & _sName)1001 bool isAggregateColumn(const Reference< XNameAccess > &_xColumns, const OUString &_sName)
1002 {
1003     if ( _xColumns.is() && _xColumns->hasByName(_sName) )
1004     {
1005         Reference<XPropertySet> xProp(_xColumns->getByName(_sName),UNO_QUERY);
1006         assert(xProp.is());
1007         return isAggregateColumn( xProp );
1008     }
1009     return false;
1010 }
1011 
isAggregateColumn(const Reference<XPropertySet> & _xColumn)1012 bool isAggregateColumn( const Reference< XPropertySet > &_xColumn )
1013 {
1014     bool bAgg(false);
1015 
1016     static constexpr OUStringLiteral sAgg = u"AggregateFunction";
1017     if ( _xColumn->getPropertySetInfo()->hasPropertyByName(sAgg) )
1018         _xColumn->getPropertyValue(sAgg) >>= bAgg;
1019 
1020     return bAgg;
1021 }
1022 
1023 
1024 }   // namespace dbtools
1025 
1026 
1027 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1028