xref: /core/dbaccess/source/ui/misc/WCopyTable.cxx (revision aa67679df76ac39a177e683350356084f4974a21)
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 <strings.hrc>
21 #include <strings.hxx>
22 #include <core_resource.hxx>
23 #include <sqlmessage.hxx>
24 #include <UITools.hxx>
25 #include <WColumnSelect.hxx>
26 #include <WCopyTable.hxx>
27 #include <WCPage.hxx>
28 #include <WExtendPages.hxx>
29 #include <WNameMatch.hxx>
30 #include <WTypeSelect.hxx>
31 
32 #include <com/sun/star/sdb/application/CopyTableOperation.hpp>
33 #include <com/sun/star/sdb/SQLContext.hpp>
34 #include <com/sun/star/sdbc/ColumnValue.hpp>
35 #include <com/sun/star/sdbc/DataType.hpp>
36 #include <com/sun/star/sdbc/XResultSet.hpp>
37 #include <com/sun/star/sdbc/XStatement.hpp>
38 #include <com/sun/star/sdbc/XRow.hpp>
39 #include <com/sun/star/sdbcx/KeyType.hpp>
40 #include <com/sun/star/sdbcx/XAppend.hpp>
41 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
42 #include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp>
43 #include <com/sun/star/sdbcx/XKeysSupplier.hpp>
44 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
45 #include <com/sun/star/sdbcx/XViewsSupplier.hpp>
46 #include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
47 #include <com/sun/star/task/InteractionHandler.hpp>
48 
49 #include <comphelper/interaction.hxx>
50 #include <connectivity/dbtools.hxx>
51 #include <connectivity/dbmetadata.hxx>
52 #include <connectivity/dbexception.hxx>
53 #include <o3tl/safeint.hxx>
54 #include <rtl/ustrbuf.hxx>
55 #include <sal/log.hxx>
56 #include <comphelper/diagnose_ex.hxx>
57 
58 #include <algorithm>
59 #include <utility>
60 
61 using namespace ::dbaui;
62 using namespace ::com::sun::star::uno;
63 using namespace ::com::sun::star::beans;
64 using namespace ::com::sun::star::container;
65 using namespace ::com::sun::star::util;
66 using namespace ::com::sun::star::sdb;
67 using namespace ::com::sun::star::sdbc;
68 using namespace ::com::sun::star::sdbcx;
69 using namespace ::com::sun::star::lang;
70 using namespace ::com::sun::star::task;
71 using namespace dbtools;
72 
73 namespace CopyTableOperation = ::com::sun::star::sdb::application::CopyTableOperation;
74 
75 #define MAX_PAGES   4   // max. number of pages, which are shown
76 
77 namespace
78 {
clearColumns(ODatabaseExport::TColumns & _rColumns,ODatabaseExport::TColumnVector & _rColumnsVec)79     void clearColumns(ODatabaseExport::TColumns& _rColumns, ODatabaseExport::TColumnVector& _rColumnsVec)
80     {
81         for (auto const& column : _rColumns)
82             delete column.second;
83 
84         _rColumnsVec.clear();
85         _rColumns.clear();
86     }
87 }
88 
89 // ICopyTableSourceObject
~ICopyTableSourceObject()90 ICopyTableSourceObject::~ICopyTableSourceObject()
91 {
92 }
93 
94 // ObjectCopySource
ObjectCopySource(const Reference<XConnection> & _rxConnection,const Reference<XPropertySet> & _rxObject)95 ObjectCopySource::ObjectCopySource( const Reference< XConnection >& _rxConnection, const Reference< XPropertySet >& _rxObject )
96     :m_xConnection( _rxConnection, UNO_SET_THROW )
97     ,m_xMetaData( _rxConnection->getMetaData(), UNO_SET_THROW )
98     ,m_xObject( _rxObject, UNO_SET_THROW )
99     ,m_xObjectPSI( _rxObject->getPropertySetInfo(), UNO_SET_THROW )
100     ,m_xObjectColumns( Reference< XColumnsSupplier >( _rxObject, UNO_QUERY_THROW )->getColumns(), UNO_SET_THROW )
101 {
102 }
103 
getQualifiedObjectName() const104 OUString ObjectCopySource::getQualifiedObjectName() const
105 {
106     OUString sName;
107 
108     if ( !m_xObjectPSI->hasPropertyByName( PROPERTY_COMMAND ) )
109         sName = ::dbtools::composeTableName( m_xMetaData, m_xObject, ::dbtools::EComposeRule::InDataManipulation, false );
110     else
111         m_xObject->getPropertyValue( PROPERTY_NAME ) >>= sName;
112     return sName;
113 }
114 
isView() const115 bool ObjectCopySource::isView() const
116 {
117     bool bIsView = false;
118     try
119     {
120         if ( m_xObjectPSI->hasPropertyByName( PROPERTY_TYPE ) )
121         {
122             OUString sObjectType;
123             OSL_VERIFY( m_xObject->getPropertyValue( PROPERTY_TYPE ) >>= sObjectType );
124             bIsView = sObjectType == "VIEW";
125         }
126     }
127     catch( const Exception& )
128     {
129         DBG_UNHANDLED_EXCEPTION("dbaccess");
130     }
131     return bIsView;
132 }
133 
copyUISettingsTo(const Reference<XPropertySet> & _rxObject) const134 void ObjectCopySource::copyUISettingsTo( const Reference< XPropertySet >& _rxObject ) const
135 {
136     static constexpr OUString aCopyProperties[] {
137         PROPERTY_FONT, PROPERTY_ROW_HEIGHT, PROPERTY_TEXTCOLOR,PROPERTY_TEXTLINECOLOR,PROPERTY_TEXTEMPHASIS,PROPERTY_TEXTRELIEF
138     };
139     for (const auto & aCopyProperty : aCopyProperties)
140     {
141         if ( m_xObjectPSI->hasPropertyByName( aCopyProperty ) )
142             _rxObject->setPropertyValue( aCopyProperty, m_xObject->getPropertyValue( aCopyProperty ) );
143     }
144 }
145 
copyFilterAndSortingTo(const Reference<XConnection> & _xConnection,const Reference<XPropertySet> & _rxObject) const146 void ObjectCopySource::copyFilterAndSortingTo( const Reference< XConnection >& _xConnection,const Reference< XPropertySet >& _rxObject ) const
147 {
148     static constexpr std::pair< OUString, OUString > aProperties[] {
149                  std::pair< OUString, OUString >(PROPERTY_FILTER,u" AND "_ustr)
150                 ,std::pair< OUString, OUString >(PROPERTY_ORDER,u" ORDER BY "_ustr)
151     };
152 
153     try
154     {
155         const OUString sSourceName = ::dbtools::composeTableNameForSelect(m_xConnection,m_xObject) + ".";
156         const OUString sTargetName = ::dbtools::composeTableNameForSelect(_xConnection,_rxObject);
157         const OUString sTargetNameTemp = sTargetName + ".";
158 
159         OUStringBuffer sStatement = "SELECT * FROM " + sTargetName + " WHERE 0=1";
160 
161         for (const std::pair<OUString,OUString> & aProperty : aProperties)
162         {
163             if ( m_xObjectPSI->hasPropertyByName( aProperty.first ) )
164             {
165                 OUString sFilter;
166                 m_xObject->getPropertyValue( aProperty.first ) >>= sFilter;
167                 if ( !sFilter.isEmpty() )
168                 {
169                     sStatement.append(aProperty.second);
170                     sFilter = sFilter.replaceFirst(sSourceName,sTargetNameTemp);
171                     _rxObject->setPropertyValue( aProperty.first, Any(sFilter) );
172                     sStatement.append(sFilter);
173                 }
174             }
175         }
176 
177         _xConnection->createStatement()->executeQuery(sStatement.makeStringAndClear());
178 
179         if ( m_xObjectPSI->hasPropertyByName( PROPERTY_APPLYFILTER ) )
180             _rxObject->setPropertyValue( PROPERTY_APPLYFILTER, m_xObject->getPropertyValue( PROPERTY_APPLYFILTER ) );
181     }
182     catch(Exception&)
183     {
184     }
185 }
186 
getColumnNames() const187 Sequence< OUString > ObjectCopySource::getColumnNames() const
188 {
189     return m_xObjectColumns->getElementNames();
190 }
191 
getPrimaryKeyColumnNames() const192 Sequence< OUString > ObjectCopySource::getPrimaryKeyColumnNames() const
193 {
194     const Reference<XNameAccess> xPrimaryKeyColumns = getPrimaryKeyColumns_throw(m_xObject);
195     Sequence< OUString > aKeyColNames;
196     if ( xPrimaryKeyColumns.is() )
197         aKeyColNames = xPrimaryKeyColumns->getElementNames();
198     return aKeyColNames;
199 }
200 
createFieldDescription(const OUString & _rColumnName) const201 OFieldDescription* ObjectCopySource::createFieldDescription( const OUString& _rColumnName ) const
202 {
203     Reference< XPropertySet > xColumn( m_xObjectColumns->getByName( _rColumnName ), UNO_QUERY_THROW );
204     return new OFieldDescription( xColumn );
205 }
206 
getSelectStatement() const207 OUString ObjectCopySource::getSelectStatement() const
208 {
209     OUString sSelectStatement;
210     if ( m_xObjectPSI->hasPropertyByName( PROPERTY_COMMAND ) )
211     {   // query
212         OSL_VERIFY( m_xObject->getPropertyValue( PROPERTY_COMMAND ) >>= sSelectStatement );
213     }
214     else
215     {   // table
216         OUStringBuffer aSQL( "SELECT " );
217 
218         // we need to create the sql stmt with column names
219         // otherwise it is possible that names don't match
220         const OUString sQuote = m_xMetaData->getIdentifierQuoteString();
221 
222         Sequence< OUString > aColumnNames = getColumnNames();
223         for (sal_Int32 i = 0; i < aColumnNames.getLength(); ++i)
224         {
225             if (i > 0)
226                 aSQL.append(", ");
227             aSQL.append(::dbtools::quoteName(sQuote, aColumnNames[i]));
228         }
229 
230         aSQL.append( " FROM " + ::dbtools::composeTableNameForSelect( m_xConnection, m_xObject ) );
231 
232         sSelectStatement = aSQL.makeStringAndClear();
233     }
234 
235     return sSelectStatement;
236 }
237 
getPreparedSelectStatement() const238 ::utl::SharedUNOComponent< XPreparedStatement > ObjectCopySource::getPreparedSelectStatement() const
239 {
240     ::utl::SharedUNOComponent< XPreparedStatement > xStatement(
241         m_xConnection->prepareStatement( getSelectStatement() ),
242         ::utl::SharedUNOComponent< XPreparedStatement >::TakeOwnership
243     );
244     return xStatement;
245 }
246 
247 // NamedTableCopySource
NamedTableCopySource(const Reference<XConnection> & _rxConnection,OUString _sTableName)248 NamedTableCopySource::NamedTableCopySource( const Reference< XConnection >& _rxConnection, OUString _sTableName )
249     :m_xConnection( _rxConnection, UNO_SET_THROW )
250     ,m_xMetaData( _rxConnection->getMetaData(), UNO_SET_THROW )
251     ,m_sTableName(std::move( _sTableName ))
252 {
253     ::dbtools::qualifiedNameComponents( m_xMetaData, m_sTableName, m_sTableCatalog, m_sTableSchema, m_sTableBareName, ::dbtools::EComposeRule::Complete );
254     impl_ensureColumnInfo_throw();
255 }
256 
getQualifiedObjectName() const257 OUString NamedTableCopySource::getQualifiedObjectName() const
258 {
259     return m_sTableName;
260 }
261 
isView() const262 bool NamedTableCopySource::isView() const
263 {
264     OUString sTableType;
265     try
266     {
267         Reference< XResultSet > xTableDesc( m_xMetaData->getTables( Any( m_sTableCatalog ), m_sTableSchema, m_sTableBareName,
268             Sequence< OUString >() ) );
269         Reference< XRow > xTableDescRow( xTableDesc, UNO_QUERY_THROW );
270         OSL_VERIFY( xTableDesc->next() );
271         sTableType = xTableDescRow->getString( 4 );
272         OSL_ENSURE( !xTableDescRow->wasNull(), "NamedTableCopySource::isView: invalid table type!" );
273     }
274     catch( const Exception& )
275     {
276         DBG_UNHANDLED_EXCEPTION("dbaccess");
277     }
278     return sTableType == "VIEW";
279 }
280 
copyUISettingsTo(const Reference<XPropertySet> &) const281 void NamedTableCopySource::copyUISettingsTo( const Reference< XPropertySet >& /*_rxObject*/ ) const
282 {
283     // not supported: we do not have UI settings to copy
284 }
285 
copyFilterAndSortingTo(const Reference<XConnection> &,const Reference<XPropertySet> &) const286 void NamedTableCopySource::copyFilterAndSortingTo( const Reference< XConnection >& ,const Reference< XPropertySet >& /*_rxObject*/ ) const
287 {
288 }
289 
impl_ensureColumnInfo_throw()290 void NamedTableCopySource::impl_ensureColumnInfo_throw()
291 {
292     if ( !m_aColumnInfo.empty() )
293         return;
294 
295     Reference< XResultSetMetaDataSupplier > xStatementMetaSupp( impl_ensureStatement_throw().getTyped(), UNO_QUERY_THROW );
296     Reference< XResultSetMetaData > xStatementMeta( xStatementMetaSupp->getMetaData(), UNO_SET_THROW );
297 
298     sal_Int32 nColCount( xStatementMeta->getColumnCount() );
299     for ( sal_Int32 i = 1; i <= nColCount; ++i )
300     {
301         OFieldDescription aDesc;
302 
303         aDesc.SetName(          xStatementMeta->getColumnName(      i ) );
304         aDesc.SetHelpText(      xStatementMeta->getColumnLabel(     i ) );
305         aDesc.SetTypeValue(     xStatementMeta->getColumnType(      i ) );
306         aDesc.SetTypeName(      xStatementMeta->getColumnTypeName(  i ) );
307         aDesc.SetPrecision(     xStatementMeta->getPrecision(       i ) );
308         aDesc.SetScale(         xStatementMeta->getScale(           i ) );
309         aDesc.SetIsNullable(    xStatementMeta->isNullable(         i ) );
310         aDesc.SetCurrency(      xStatementMeta->isCurrency(         i ) );
311         aDesc.SetAutoIncrement( xStatementMeta->isAutoIncrement(    i ) );
312 
313         m_aColumnInfo.push_back( aDesc );
314     }
315 }
316 
impl_ensureStatement_throw()317 ::utl::SharedUNOComponent< XPreparedStatement > const & NamedTableCopySource::impl_ensureStatement_throw()
318 {
319     if ( !m_xStatement.is() )
320         m_xStatement.set( m_xConnection->prepareStatement( getSelectStatement() ), UNO_SET_THROW );
321     return m_xStatement;
322 }
323 
getColumnNames() const324 Sequence< OUString > NamedTableCopySource::getColumnNames() const
325 {
326     Sequence< OUString > aNames( m_aColumnInfo.size() );
327     std::transform(m_aColumnInfo.begin(), m_aColumnInfo.end(), aNames.getArray(),
328                    [](const auto& elem) { return elem.GetName(); });
329 
330     return aNames;
331 }
332 
getPrimaryKeyColumnNames() const333 Sequence< OUString > NamedTableCopySource::getPrimaryKeyColumnNames() const
334 {
335     Sequence< OUString > aPKColNames;
336 
337     try
338     {
339         Reference< XResultSet > xPKDesc( m_xMetaData->getPrimaryKeys( Any( m_sTableCatalog ), m_sTableSchema, m_sTableBareName ) );
340         Reference< XRow > xPKDescRow( xPKDesc, UNO_QUERY_THROW );
341         while ( xPKDesc->next() )
342         {
343             sal_Int32 len( aPKColNames.getLength() );
344             aPKColNames.realloc( len + 1 );
345             aPKColNames.getArray()[ len ] = xPKDescRow->getString( 4 );    // COLUMN_NAME
346         }
347     }
348     catch( const Exception& )
349     {
350         DBG_UNHANDLED_EXCEPTION("dbaccess");
351     }
352 
353     return aPKColNames;
354 }
355 
createFieldDescription(const OUString & _rColumnName) const356 OFieldDescription* NamedTableCopySource::createFieldDescription( const OUString& _rColumnName ) const
357 {
358     for (auto const& elem : m_aColumnInfo)
359         if ( elem.GetName() == _rColumnName )
360             return new OFieldDescription(elem);
361 
362     return nullptr;
363 }
364 
getSelectStatement() const365 OUString NamedTableCopySource::getSelectStatement() const
366 {
367     return "SELECT * FROM " +
368         ::dbtools::composeTableNameForSelect( m_xConnection, m_sTableCatalog, m_sTableSchema, m_sTableBareName );
369 }
370 
getPreparedSelectStatement() const371 ::utl::SharedUNOComponent< XPreparedStatement > NamedTableCopySource::getPreparedSelectStatement() const
372 {
373     return const_cast< NamedTableCopySource* >( this )->impl_ensureStatement_throw();
374 }
375 
376 namespace {
377 
378 // DummyCopySource
379 class DummyCopySource : public ICopyTableSourceObject
380 {
381 public:
DummyCopySource()382     DummyCopySource() { }
383 
384     static const DummyCopySource& Instance();
385 
386     // ICopyTableSourceObject overridables
387     virtual OUString            getQualifiedObjectName() const override;
388     virtual bool                isView() const override;
389     virtual void                copyUISettingsTo( const css::uno::Reference< css::beans::XPropertySet >& _rxObject ) const override;
390     virtual void                copyFilterAndSortingTo(const css::uno::Reference< css::sdbc::XConnection >& _xConnection, const css::uno::Reference< css::beans::XPropertySet >& _rxObject ) const override;
391     virtual css::uno::Sequence< OUString >
392                                 getColumnNames() const override;
393     virtual css::uno::Sequence< OUString >
394                                 getPrimaryKeyColumnNames() const override;
395     virtual OFieldDescription*  createFieldDescription( const OUString& _rColumnName ) const override;
396     virtual OUString            getSelectStatement() const override;
397     virtual ::utl::SharedUNOComponent< XPreparedStatement >
398                                 getPreparedSelectStatement() const override;
399 };
400 
401 }
402 
Instance()403 const DummyCopySource& DummyCopySource::Instance()
404 {
405     static DummyCopySource s_aTheInstance;
406     return s_aTheInstance;
407 }
408 
getQualifiedObjectName() const409 OUString DummyCopySource::getQualifiedObjectName() const
410 {
411     SAL_WARN("dbaccess.ui",  "DummyCopySource::getQualifiedObjectName: not to be called!" );
412     return OUString();
413 }
414 
isView() const415 bool DummyCopySource::isView() const
416 {
417     SAL_WARN("dbaccess.ui",  "DummyCopySource::isView: not to be called!" );
418     return false;
419 }
420 
copyUISettingsTo(const Reference<XPropertySet> &) const421 void DummyCopySource::copyUISettingsTo( const Reference< XPropertySet >& /*_rxObject*/ ) const
422 {
423     // no support
424 }
425 
copyFilterAndSortingTo(const Reference<XConnection> &,const Reference<XPropertySet> &) const426 void DummyCopySource::copyFilterAndSortingTo( const Reference< XConnection >& ,const Reference< XPropertySet >& /*_rxObject*/ ) const
427 {
428 }
429 
getColumnNames() const430 Sequence< OUString > DummyCopySource::getColumnNames() const
431 {
432     return Sequence< OUString >();
433 }
434 
getPrimaryKeyColumnNames() const435 Sequence< OUString > DummyCopySource::getPrimaryKeyColumnNames() const
436 {
437     SAL_WARN("dbaccess.ui",  "DummyCopySource::getPrimaryKeyColumnNames: not to be called!" );
438     return Sequence< OUString >();
439 }
440 
createFieldDescription(const OUString &) const441 OFieldDescription* DummyCopySource::createFieldDescription( const OUString& /*_rColumnName*/ ) const
442 {
443     SAL_WARN("dbaccess.ui",  "DummyCopySource::createFieldDescription: not to be called!" );
444     return nullptr;
445 }
446 
getSelectStatement() const447 OUString DummyCopySource::getSelectStatement() const
448 {
449     SAL_WARN("dbaccess.ui",  "DummyCopySource::getSelectStatement: not to be called!" );
450     return OUString();
451 }
452 
getPreparedSelectStatement() const453 ::utl::SharedUNOComponent< XPreparedStatement > DummyCopySource::getPreparedSelectStatement() const
454 {
455     SAL_WARN("dbaccess.ui",  "DummyCopySource::getPreparedSelectStatement: not to be called!" );
456     return ::utl::SharedUNOComponent< XPreparedStatement >();
457 }
458 
459 namespace
460 {
lcl_canCreateViewFor_nothrow(const Reference<XConnection> & _rxConnection)461     bool lcl_canCreateViewFor_nothrow( const Reference< XConnection >& _rxConnection )
462     {
463         Reference< XViewsSupplier > xSup( _rxConnection, UNO_QUERY );
464         Reference< XDataDescriptorFactory > xViewFac;
465         if ( xSup.is() )
466             xViewFac.set( xSup->getViews(), UNO_QUERY );
467         return xViewFac.is();
468     }
469 
lcl_sameConnection_throw(const Reference<XConnection> & _rxLHS,const Reference<XConnection> & _rxRHS)470     bool lcl_sameConnection_throw( const Reference< XConnection >& _rxLHS, const Reference< XConnection >& _rxRHS )
471     {
472         Reference< XDatabaseMetaData > xMetaLHS( _rxLHS->getMetaData(), UNO_SET_THROW );
473         Reference< XDatabaseMetaData > xMetaRHS( _rxRHS->getMetaData(), UNO_SET_THROW );
474         return xMetaLHS->getURL() == xMetaRHS->getURL();
475     }
476 }
477 
478 // OCopyTableWizard
OCopyTableWizard(weld::Window * pParent,const OUString & _rDefaultName,sal_Int16 _nOperation,const ICopyTableSourceObject & _rSourceObject,const Reference<XConnection> & _xSourceConnection,const Reference<XConnection> & _xConnection,const Reference<XComponentContext> & _rxContext,const Reference<XInteractionHandler> & _xInteractionHandler)479 OCopyTableWizard::OCopyTableWizard(weld::Window* pParent, const OUString& _rDefaultName, sal_Int16 _nOperation,
480         const ICopyTableSourceObject& _rSourceObject, const Reference< XConnection >& _xSourceConnection,
481         const Reference< XConnection >& _xConnection, const Reference< XComponentContext >& _rxContext,
482         const Reference< XInteractionHandler>& _xInteractionHandler)
483     : vcl::RoadmapWizardMachine(pParent)
484     , m_mNameMapping(comphelper::UStringMixLess(_xConnection->getMetaData().is() && _xConnection->getMetaData()->supportsMixedCaseQuotedIdentifiers()))
485     , m_xDestConnection( _xConnection )
486     , m_rSourceObject( _rSourceObject )
487     , m_xFormatter( getNumberFormatter( _xConnection, _rxContext ) )
488     , m_xContext(_rxContext)
489     , m_xInteractionHandler(_xInteractionHandler)
490     , m_sTypeNames(DBA_RES(STR_TABLEDESIGN_DBFIELDTYPES))
491     , m_nPageCount(0)
492     , m_bDeleteSourceColumns(true)
493     , m_bInterConnectionCopy( _xSourceConnection != _xConnection )
494     , m_sName( _rDefaultName )
495     , m_nOperation( _nOperation )
496     , m_ePressed( WIZARD_NONE )
497     , m_bCreatePrimaryKeyColumn(false)
498     , m_bUseHeaderLine(false)
499 {
500     construct();
501 
502     // extract table name
503     OUString sInitialTableName( _rDefaultName );
504     try
505     {
506         m_sSourceName = m_rSourceObject.getQualifiedObjectName();
507         OSL_ENSURE( !m_sSourceName.isEmpty(), "OCopyTableWizard::OCopyTableWizard: unable to retrieve the source object's name!" );
508 
509         if ( sInitialTableName.isEmpty() )
510             sInitialTableName = m_sSourceName;
511 
512         if ( m_sName.isEmpty() )
513         {
514             if ( _xSourceConnection == m_xDestConnection )
515             {
516                 Reference< XTablesSupplier > xSup( m_xDestConnection, UNO_QUERY_THROW );
517                 m_sName = ::dbtools::createUniqueName( xSup->getTables(), sInitialTableName, false );
518             }
519             else
520                 m_sName = sInitialTableName;
521         }
522     }
523     catch ( const Exception& )
524     {
525         m_sName = sInitialTableName;
526     }
527 
528     ::dbaui::fillTypeInfo( _xSourceConnection, m_sTypeNames, m_aTypeInfo, m_aTypeInfoIndex );
529     ::dbaui::fillTypeInfo( m_xDestConnection, m_sTypeNames, m_aDestTypeInfo, m_aDestTypeInfoIndex );
530     loadData( m_rSourceObject, m_vSourceColumns, m_vSourceVec );
531 
532     bool bAllowViews = true;
533     // if the source is a, don't allow creating views
534     if ( m_rSourceObject.isView() )
535         bAllowViews = false;
536     // no views if the target connection does not support creating them
537     if ( !lcl_canCreateViewFor_nothrow( m_xDestConnection ) )
538         bAllowViews = false;
539     // no views if we're copying to a different database
540     if ( !lcl_sameConnection_throw( _xSourceConnection, m_xDestConnection ) )
541         bAllowViews = false;
542 
543     if ( m_bInterConnectionCopy )
544     {
545         Reference< XDatabaseMetaData > xSrcMeta = _xSourceConnection->getMetaData();
546         OUString sCatalog;
547         OUString sSchema;
548         OUString sTable;
549         ::dbtools::qualifiedNameComponents( xSrcMeta,
550                                             m_sName,
551                                             sCatalog,
552                                             sSchema,
553                                             sTable,
554                                             ::dbtools::EComposeRule::InDataManipulation);
555 
556         m_sName = ::dbtools::composeTableName(m_xDestConnection->getMetaData(),sCatalog,sSchema,sTable,false,::dbtools::EComposeRule::InTableDefinitions);
557     }
558 
559     std::unique_ptr<OCopyTable> xPage1(new OCopyTable(CreatePageContainer(), this));
560     xPage1->disallowUseHeaderLine();
561     if ( !bAllowViews )
562         xPage1->disallowViews();
563     xPage1->setCreateStyleAction();
564     AddWizardPage(std::move(xPage1));
565 
566     AddWizardPage( std::make_unique<OWizNameMatching>(CreatePageContainer(), this));
567     AddWizardPage( std::make_unique<OWizColumnSelect>(CreatePageContainer(), this));
568     AddWizardPage( std::make_unique<OWizNormalExtend>(CreatePageContainer(), this));
569     ActivatePage();
570 
571     m_xAssistant->set_current_page(0);
572 }
573 
CreatePageContainer()574 weld::Container* OCopyTableWizard::CreatePageContainer()
575 {
576     OUString sIdent(OUString::number(m_nPageCount));
577     weld::Container* pPageContainer = m_xAssistant->append_page(sIdent);
578     return pPageContainer;
579 }
580 
OCopyTableWizard(weld::Window * pParent,OUString _sDefaultName,sal_Int16 _nOperation,ODatabaseExport::TColumns && _rSourceColumns,const ODatabaseExport::TColumnVector & _rSourceColVec,const Reference<XConnection> & _xConnection,const Reference<XNumberFormatter> & _xFormatter,TypeSelectionPageFactory _pTypeSelectionPageFactory,SvStream & _rTypeSelectionPageArg,const Reference<XComponentContext> & _rxContext)581 OCopyTableWizard::OCopyTableWizard( weld::Window* pParent, OUString _sDefaultName, sal_Int16 _nOperation,
582         ODatabaseExport::TColumns&& _rSourceColumns, const ODatabaseExport::TColumnVector& _rSourceColVec,
583         const Reference< XConnection >& _xConnection, const Reference< XNumberFormatter >&  _xFormatter,
584         TypeSelectionPageFactory _pTypeSelectionPageFactory, SvStream& _rTypeSelectionPageArg, const Reference< XComponentContext >& _rxContext )
585     : vcl::RoadmapWizardMachine(pParent)
586     , m_vSourceColumns(std::move(_rSourceColumns))
587     , m_mNameMapping(comphelper::UStringMixLess(_xConnection->getMetaData().is() && _xConnection->getMetaData()->supportsMixedCaseQuotedIdentifiers()))
588     , m_xDestConnection( _xConnection )
589     , m_rSourceObject( DummyCopySource::Instance() )
590     , m_xFormatter(_xFormatter)
591     , m_xContext(_rxContext)
592     , m_sTypeNames(DBA_RES(STR_TABLEDESIGN_DBFIELDTYPES))
593     , m_nPageCount(0)
594     , m_bDeleteSourceColumns(false)
595     , m_bInterConnectionCopy( false )
596     , m_sName(std::move(_sDefaultName))
597     , m_nOperation( _nOperation )
598     , m_ePressed( WIZARD_NONE )
599     , m_bCreatePrimaryKeyColumn(false)
600     , m_bUseHeaderLine(false)
601 {
602     construct();
603     for (auto const& sourceCol : _rSourceColVec)
604     {
605         m_vSourceVec.emplace_back(m_vSourceColumns.find(sourceCol->first));
606     }
607 
608     ::dbaui::fillTypeInfo( _xConnection, m_sTypeNames, m_aTypeInfo, m_aTypeInfoIndex );
609     ::dbaui::fillTypeInfo( _xConnection, m_sTypeNames, m_aDestTypeInfo, m_aDestTypeInfoIndex );
610 
611     m_xInteractionHandler = InteractionHandler::createWithParent(m_xContext, nullptr);
612 
613     std::unique_ptr<OCopyTable> xPage1(new OCopyTable(CreatePageContainer(), this));
614     xPage1->disallowViews();
615     xPage1->setCreateStyleAction();
616     AddWizardPage(std::move(xPage1));
617 
618     AddWizardPage(std::make_unique<OWizNameMatching>(CreatePageContainer(), this));
619     AddWizardPage(std::make_unique<OWizColumnSelect>(CreatePageContainer(), this));
620     AddWizardPage((*_pTypeSelectionPageFactory)(CreatePageContainer(), this, _rTypeSelectionPageArg));
621 
622     ActivatePage();
623 
624     m_xAssistant->set_current_page(0);
625 }
626 
construct()627 void OCopyTableWizard::construct()
628 {
629     m_xAssistant->set_size_request(700, 350);
630 
631     m_xPrevPage->set_label(DBA_RES(STR_WIZ_PB_PREV));
632     m_xNextPage->set_label(DBA_RES(STR_WIZ_PB_NEXT));
633     m_xFinish->set_label(DBA_RES(STR_WIZ_PB_OK));
634 
635     m_xHelp->show();
636     m_xCancel->show();
637     m_xPrevPage->show();
638     m_xNextPage->show();
639     m_xFinish->show();
640 
641     m_xPrevPage->connect_clicked( LINK( this, OCopyTableWizard, ImplPrevHdl ) );
642     m_xNextPage->connect_clicked( LINK( this, OCopyTableWizard, ImplNextHdl ) );
643     m_xFinish->connect_clicked( LINK( this, OCopyTableWizard, ImplOKHdl ) );
644 
645     m_xNextPage->grab_focus();
646 
647     if (!m_vDestColumns.empty())
648         // source is a html or rtf table
649         m_xAssistant->change_default_button(nullptr, m_xNextPage.get());
650     else
651         m_xAssistant->change_default_button(nullptr, m_xFinish.get());
652 
653     m_pTypeInfo = std::make_shared<OTypeInfo>();
654     m_pTypeInfo->aUIName = m_sTypeNames.getToken(TYPE_OTHER, ';');
655     m_bAddPKFirstTime = true;
656 }
657 
~OCopyTableWizard()658 OCopyTableWizard::~OCopyTableWizard()
659 {
660     if ( m_bDeleteSourceColumns )
661         clearColumns(m_vSourceColumns,m_vSourceVec);
662 
663     clearColumns(m_vDestColumns,m_aDestVec);
664 
665     // clear the type information
666     m_aTypeInfoIndex.clear();
667     m_aTypeInfo.clear();
668     m_aDestTypeInfoIndex.clear();
669     m_aDestTypeInfo.clear();
670 }
671 
IMPL_LINK_NOARG(OCopyTableWizard,ImplPrevHdl,weld::Button &,void)672 IMPL_LINK_NOARG(OCopyTableWizard, ImplPrevHdl, weld::Button&, void)
673 {
674     m_ePressed = WIZARD_PREV;
675     if ( GetCurLevel() )
676     {
677         if ( getOperation() != CopyTableOperation::AppendData )
678         {
679             if(GetCurLevel() == 2)
680                 ShowPage(GetCurLevel()-2);
681             else
682                 ShowPrevPage();
683         }
684         else
685             ShowPrevPage();
686     }
687 }
688 
IMPL_LINK_NOARG(OCopyTableWizard,ImplNextHdl,weld::Button &,void)689 IMPL_LINK_NOARG(OCopyTableWizard, ImplNextHdl, weld::Button&, void)
690 {
691     m_ePressed = WIZARD_NEXT;
692     if ( GetCurLevel() < MAX_PAGES )
693     {
694         if ( getOperation() != CopyTableOperation::AppendData )
695         {
696             if(GetCurLevel() == 0)
697                 ShowPage(GetCurLevel()+2);
698             else
699                 ShowNextPage();
700         }
701         else
702             ShowNextPage();
703     }
704 }
705 
CheckColumns(sal_Int32 & _rnBreakPos)706 bool OCopyTableWizard::CheckColumns(sal_Int32& _rnBreakPos)
707 {
708     bool bRet = true;
709     m_vColumnPositions.clear();
710     m_vColumnTypes.clear();
711 
712     OSL_ENSURE( m_xDestConnection.is(), "OCopyTableWizard::CheckColumns: No connection!" );
713     // If database is able to process PrimaryKeys, set PrimaryKey
714     if ( m_xDestConnection.is() )
715     {
716         bool bPKeyAllowed = supportsPrimaryKey();
717 
718         bool bContainsColumns = !m_vDestColumns.empty();
719 
720         if ( bPKeyAllowed && shouldCreatePrimaryKey() )
721         {
722             // add extra column for the primary key
723             TOTypeInfoSP pTypeInfo = queryPrimaryKeyType(m_aDestTypeInfo);
724             if ( pTypeInfo )
725             {
726                 if ( m_bAddPKFirstTime )
727                 {
728                     // tdf#114955: since we chose to create a primary key
729                     // be sure all other columns won't be in primary key
730                     for (auto const& elem : m_vDestColumns)
731                         elem.second->SetPrimaryKey(false);
732                     OFieldDescription* pField = new OFieldDescription();
733                     pField->SetName(m_aKeyName);
734                     pField->FillFromTypeInfo(pTypeInfo,true,true);
735                     pField->SetAutoIncrement(pTypeInfo->bAutoIncrement);
736                     pField->SetPrimaryKey(true);
737                     m_bAddPKFirstTime = false;
738                     insertColumn(0,pField);
739                 }
740                 m_vColumnPositions.emplace_back(1,1);
741                 m_vColumnTypes.push_back(pTypeInfo->nType);
742             }
743         }
744 
745         if ( bContainsColumns )
746         {   // we have dest columns so look for the matching column
747             for (auto const& elemSource : m_vSourceVec)
748             {
749                 ODatabaseExport::TColumns::const_iterator aDestIter = m_vDestColumns.find(m_mNameMapping[elemSource->first]);
750 
751                 if ( aDestIter != m_vDestColumns.end() )
752                 {
753                     ODatabaseExport::TColumnVector::const_iterator aFind = std::find(m_aDestVec.begin(),m_aDestVec.end(),aDestIter);
754                     assert(aFind != m_aDestVec.end());
755                     sal_Int32 nPos = (aFind - m_aDestVec.begin())+1;
756                     m_vColumnPositions.emplace_back(nPos,nPos);
757                     m_vColumnTypes.push_back((*aFind)->second->GetType());
758                 }
759                 else
760                 {
761                     m_vColumnPositions.emplace_back( COLUMN_POSITION_NOT_FOUND, COLUMN_POSITION_NOT_FOUND );
762                     m_vColumnTypes.push_back(0);
763                 }
764             }
765         }
766         else
767         {
768             Reference< XDatabaseMetaData > xMetaData( m_xDestConnection->getMetaData() );
769             OUString sExtraChars = xMetaData->getExtraNameCharacters();
770             sal_Int32 nMaxNameLen       = getMaxColumnNameLength();
771 
772             _rnBreakPos=0;
773             for (auto const& elemSource : m_vSourceVec)
774             {
775                 OFieldDescription* pField = new OFieldDescription(*elemSource->second);
776                 pField->SetName(convertColumnName(TExportColumnFindFunctor(&m_vDestColumns),elemSource->first,sExtraChars,nMaxNameLen));
777                 TOTypeInfoSP pType = convertType(elemSource->second->getSpecialTypeInfo(),bRet);
778                 pField->SetType(pType);
779                 if ( !bPKeyAllowed )
780                     pField->SetPrimaryKey(false);
781 
782                 // now create a column
783                 insertColumn(m_vDestColumns.size(),pField);
784                 m_vColumnPositions.emplace_back(m_vDestColumns.size(),m_vDestColumns.size());
785                 m_vColumnTypes.push_back(elemSource->second->GetType());
786                 ++_rnBreakPos;
787                 if (!bRet)
788                     break;
789             }
790         }
791     }
792     return bRet;
793 }
794 
IMPL_LINK_NOARG(OCopyTableWizard,ImplOKHdl,weld::Button &,void)795 IMPL_LINK_NOARG(OCopyTableWizard, ImplOKHdl, weld::Button&, void)
796 {
797     m_ePressed = WIZARD_FINISH;
798     bool bFinish = DeactivatePage();
799 
800     if(!bFinish)
801         return;
802 
803     weld::WaitObject aWait(m_xAssistant.get());
804     switch(getOperation())
805     {
806         case CopyTableOperation::CopyDefinitionAndData:
807         case CopyTableOperation::CopyDefinitionOnly:
808         {
809             bool bOnFirstPage = GetCurLevel() == 0;
810             if ( bOnFirstPage )
811             {
812                 // we came from the first page so we have to clear
813                 // all column information already collected
814                 clearDestColumns();
815                 m_mNameMapping.clear();
816             }
817             sal_Int32 nBreakPos = 0;
818             bool bCheckOk = CheckColumns(nBreakPos);
819             if ( bOnFirstPage && !bCheckOk )
820             {
821                 showColumnTypeNotSupported(m_vSourceVec[nBreakPos-1]->first);
822                 OWizTypeSelect* pPage = static_cast<OWizTypeSelect*>(GetPage(3));
823                 if ( pPage )
824                 {
825                     m_mNameMapping.clear();
826                     pPage->setDisplayRow(nBreakPos);
827                     ShowPage(3);
828                     return;
829                 }
830             }
831             if ( m_xDestConnection.is() )
832             {
833                 if ( supportsPrimaryKey() )
834                 {
835                     bool noPrimaryKey = std::none_of(m_vDestColumns.begin(),m_vDestColumns.end(),
836                         [] (const ODatabaseExport::TColumns::value_type& tCol) { return tCol.second->IsPrimaryKey(); });
837                     if ( noPrimaryKey && m_xInteractionHandler.is() )
838                     {
839 
840                         OUString sMsg(DBA_RES(STR_TABLEDESIGN_NO_PRIM_KEY));
841                         SQLContext aError(sMsg, {}, {}, 0, {}, {});
842                         ::rtl::Reference xRequest( new ::comphelper::OInteractionRequest( Any( aError ) ) );
843                         ::rtl::Reference xYes = new ::comphelper::OInteractionApprove;
844                         xRequest->addContinuation( xYes );
845                         xRequest->addContinuation( new ::comphelper::OInteractionDisapprove );
846                         ::rtl::Reference< ::comphelper::OInteractionAbort > xAbort = new ::comphelper::OInteractionAbort;
847                         xRequest->addContinuation( xAbort );
848 
849                         m_xInteractionHandler->handle( xRequest );
850 
851                         if ( xYes->wasSelected() )
852                         {
853                             OCopyTable* pPage = static_cast<OCopyTable*>(GetPage(0));
854                             m_bCreatePrimaryKeyColumn = true;
855                             m_aKeyName = pPage->GetKeyName();
856                             if ( m_aKeyName.isEmpty() )
857                                 m_aKeyName = "ID";
858                             m_aKeyName = createUniqueName( m_aKeyName );
859                             sal_Int32 nBreakPos2 = 0;
860                             CheckColumns(nBreakPos2);
861                         }
862                         else if ( xAbort->wasSelected() )
863                         {
864                             ShowPage(3);
865                             return;
866                         }
867                     }
868                 }
869             }
870             break;
871         }
872         case CopyTableOperation::AppendData:
873         case CopyTableOperation::CreateAsView:
874             break;
875         default:
876         {
877             SAL_WARN("dbaccess.ui", "OCopyTableWizard::ImplOKHdl: invalid creation style!");
878         }
879     }
880 
881     m_xAssistant->response(RET_OK);
882 }
883 
setCreatePrimaryKey(bool _bDoCreate,const OUString & _rSuggestedName)884 void OCopyTableWizard::setCreatePrimaryKey( bool _bDoCreate, const OUString& _rSuggestedName )
885 {
886     m_bCreatePrimaryKeyColumn = _bDoCreate;
887     if ( !_rSuggestedName.isEmpty() )
888         m_aKeyName = _rSuggestedName;
889 
890     OCopyTable* pSettingsPage = dynamic_cast< OCopyTable* >( GetPage( 0 ) );
891     OSL_ENSURE( pSettingsPage, "OCopyTableWizard::setCreatePrimaryKey: page should have been added in the ctor!" );
892     if ( pSettingsPage )
893         pSettingsPage->setCreatePrimaryKey( _bDoCreate, _rSuggestedName );
894 }
895 
ActivatePage()896 void OCopyTableWizard::ActivatePage()
897 {
898     OWizardPage* pCurrent = static_cast<OWizardPage*>(GetPage(GetCurLevel()));
899     if (pCurrent)
900     {
901         bool bFirstTime = pCurrent->IsFirstTime();
902         if(bFirstTime)
903             pCurrent->Reset();
904 
905         CheckButtons();
906 
907         m_xAssistant->set_title(pCurrent->GetTitle());
908     }
909 }
910 
CheckButtons()911 void OCopyTableWizard::CheckButtons()
912 {
913     if(GetCurLevel() == 0) // the first page has no back button
914     {
915         if(m_nPageCount > 1)
916             m_xNextPage->set_sensitive(true);
917         else
918             m_xNextPage->set_sensitive(false);
919 
920         m_xPrevPage->set_sensitive(false);
921     }
922     else if(GetCurLevel() == m_nPageCount-1) // the last page has no next button
923     {
924         m_xNextPage->set_sensitive(false);
925         m_xPrevPage->set_sensitive(true);
926     }
927     else
928     {
929         m_xPrevPage->set_sensitive(true);
930         // next already has its state
931     }
932 }
933 
EnableNextButton(bool bEnable)934 void OCopyTableWizard::EnableNextButton(bool bEnable)
935 {
936     m_xNextPage->set_sensitive(bEnable);
937 }
938 
DeactivatePage()939 bool OCopyTableWizard::DeactivatePage()
940 {
941     OWizardPage* pPage = static_cast<OWizardPage*>(GetPage(GetCurLevel()));
942     return pPage && pPage->LeavePage();
943 }
944 
AddWizardPage(std::unique_ptr<OWizardPage> xPage)945 void OCopyTableWizard::AddWizardPage(std::unique_ptr<OWizardPage> xPage)
946 {
947     AddPage(std::move(xPage));
948     ++m_nPageCount;
949 }
950 
insertColumn(sal_Int32 _nPos,OFieldDescription * _pField)951 void OCopyTableWizard::insertColumn(sal_Int32 _nPos,OFieldDescription* _pField)
952 {
953     OSL_ENSURE(_pField,"FieldDescrioption is null!");
954     if ( !_pField )
955         return;
956 
957     ODatabaseExport::TColumns::const_iterator aFind = m_vDestColumns.find(_pField->GetName());
958     if ( aFind != m_vDestColumns.end() )
959     {
960         delete aFind->second;
961         m_vDestColumns.erase(aFind);
962     }
963 
964     m_aDestVec.insert(m_aDestVec.begin() + _nPos,
965         m_vDestColumns.emplace(_pField->GetName(),_pField).first);
966     m_mNameMapping[_pField->GetName()] = _pField->GetName();
967 }
968 
replaceColumn(sal_Int32 _nPos,OFieldDescription * _pField,const OUString & _sOldName)969 void OCopyTableWizard::replaceColumn(sal_Int32 _nPos,OFieldDescription* _pField,const OUString& _sOldName)
970 {
971     OSL_ENSURE(_pField,"FieldDescrioption is null!");
972     if ( _pField )
973     {
974         m_vDestColumns.erase(_sOldName);
975         OSL_ENSURE( m_vDestColumns.find(_pField->GetName()) == m_vDestColumns.end(),"Column with that name already exist!");
976 
977         m_aDestVec[_nPos] = m_vDestColumns.emplace(_pField->GetName(),_pField).first;
978     }
979 }
980 
loadData(const ICopyTableSourceObject & _rSourceObject,ODatabaseExport::TColumns & _rColumns,ODatabaseExport::TColumnVector & _rColVector)981 void OCopyTableWizard::loadData(  const ICopyTableSourceObject& _rSourceObject, ODatabaseExport::TColumns& _rColumns, ODatabaseExport::TColumnVector& _rColVector )
982 {
983     for (auto const& column : _rColumns)
984         delete column.second;
985 
986     _rColVector.clear();
987     _rColumns.clear();
988 
989     OFieldDescription* pActFieldDescr = nullptr;
990     static constexpr OUStringLiteral sCreateParam(u"x");
991     // ReadOnly-Flag
992     // On drop no line must be editable.
993     // On add only empty lines must be editable.
994     // On Add and Drop all lines can be edited.
995     for (auto& column : _rSourceObject.getColumnNames())
996     {
997         // get the properties of the column
998         pActFieldDescr = _rSourceObject.createFieldDescription(column);
999         OSL_ENSURE( pActFieldDescr, "OCopyTableWizard::loadData: illegal field description!" );
1000         if ( !pActFieldDescr )
1001             continue;
1002 
1003         sal_Int32 nType           = pActFieldDescr->GetType();
1004         sal_Int32 nScale          = pActFieldDescr->GetScale();
1005         sal_Int32 nPrecision      = pActFieldDescr->GetPrecision();
1006         bool bAutoIncrement   = pActFieldDescr->IsAutoIncrement();
1007         OUString sTypeName = pActFieldDescr->GetTypeName();
1008 
1009         // search for type
1010         bool bForce;
1011         TOTypeInfoSP pTypeInfo = ::dbaui::getTypeInfoFromType(m_aTypeInfo,nType,sTypeName,sCreateParam,nPrecision,nScale,bAutoIncrement,bForce);
1012         if ( !pTypeInfo )
1013             pTypeInfo = m_pTypeInfo;
1014 
1015         pActFieldDescr->FillFromTypeInfo(pTypeInfo,true,false);
1016         _rColVector.emplace_back(_rColumns.emplace(pActFieldDescr->GetName(),pActFieldDescr).first);
1017     }
1018 
1019     // determine which columns belong to the primary key
1020     for (auto& keyColName : _rSourceObject.getPrimaryKeyColumnNames())
1021     {
1022         ODatabaseExport::TColumns::const_iterator keyPos = _rColumns.find(keyColName);
1023         if ( keyPos != _rColumns.end() )
1024         {
1025             keyPos->second->SetPrimaryKey( true );
1026             keyPos->second->SetIsNullable( ColumnValue::NO_NULLS );
1027         }
1028     }
1029 }
1030 
clearDestColumns()1031 void OCopyTableWizard::clearDestColumns()
1032 {
1033     clearColumns(m_vDestColumns,m_aDestVec);
1034     m_bAddPKFirstTime = true;
1035     m_mNameMapping.clear();
1036 }
1037 
appendColumns(Reference<XColumnsSupplier> const & _rxColSup,const ODatabaseExport::TColumnVector * _pVec,bool _bKeyColumns)1038 void OCopyTableWizard::appendColumns( Reference<XColumnsSupplier> const & _rxColSup, const ODatabaseExport::TColumnVector* _pVec, bool _bKeyColumns)
1039 {
1040     // now append the columns
1041     OSL_ENSURE(_rxColSup.is(),"No columns supplier");
1042     if(!_rxColSup.is())
1043         return;
1044     Reference<XNameAccess> xColumns = _rxColSup->getColumns();
1045     OSL_ENSURE(xColumns.is(),"No columns");
1046     Reference<XDataDescriptorFactory> xColumnFactory(xColumns,UNO_QUERY);
1047 
1048     Reference<XAppend> xAppend(xColumns,UNO_QUERY);
1049     OSL_ENSURE(xAppend.is(),"No XAppend Interface!");
1050 
1051     for (auto const& elem : *_pVec)
1052     {
1053         OFieldDescription* pField = elem->second;
1054         if(!pField)
1055             continue;
1056 
1057         Reference<XPropertySet> xColumn;
1058         if(pField->IsPrimaryKey() || !_bKeyColumns)
1059             xColumn = xColumnFactory->createDataDescriptor();
1060         if(xColumn.is())
1061         {
1062             if(!_bKeyColumns)
1063                 dbaui::setColumnProperties(xColumn,pField);
1064             else
1065                 xColumn->setPropertyValue(PROPERTY_NAME,Any(pField->GetName()));
1066 
1067             xAppend->appendByDescriptor(xColumn);
1068             xColumn = nullptr;
1069             // now only the settings are missing
1070             if(xColumns->hasByName(pField->GetName()))
1071             {
1072                 xColumn.set(xColumns->getByName(pField->GetName()),UNO_QUERY);
1073                 OSL_ENSURE(xColumn.is(),"OCopyTableWizard::appendColumns: Column is NULL!");
1074                 if ( xColumn.is() )
1075                     pField->copyColumnSettingsTo(xColumn);
1076             }
1077             else
1078             {
1079                 SAL_WARN("dbaccess.ui", "OCopyTableWizard::appendColumns: invalid field name!");
1080             }
1081 
1082         }
1083     }
1084 }
1085 
appendKey(Reference<XKeysSupplier> const & _rxSup,const ODatabaseExport::TColumnVector * _pVec)1086 void OCopyTableWizard::appendKey( Reference<XKeysSupplier> const & _rxSup, const ODatabaseExport::TColumnVector* _pVec)
1087 {
1088     if(!_rxSup.is())
1089         return; // the database doesn't support keys
1090     OSL_ENSURE(_rxSup.is(),"No XKeysSupplier!");
1091     Reference<XDataDescriptorFactory> xKeyFactory(_rxSup->getKeys(),UNO_QUERY);
1092     OSL_ENSURE(xKeyFactory.is(),"No XDataDescriptorFactory Interface!");
1093     if ( !xKeyFactory.is() )
1094         return;
1095     Reference<XAppend> xAppend(xKeyFactory,UNO_QUERY);
1096     OSL_ENSURE(xAppend.is(),"No XAppend Interface!");
1097 
1098     Reference<XPropertySet> xKey = xKeyFactory->createDataDescriptor();
1099     OSL_ENSURE(xKey.is(),"Key is null!");
1100     xKey->setPropertyValue(PROPERTY_TYPE,Any(KeyType::PRIMARY));
1101 
1102     Reference<XColumnsSupplier> xColSup(xKey,UNO_QUERY);
1103     if(xColSup.is())
1104     {
1105         appendColumns(xColSup,_pVec,true);
1106         Reference<XNameAccess> xColumns = xColSup->getColumns();
1107         if(xColumns.is() && xColumns->getElementNames().hasElements())
1108             xAppend->appendByDescriptor(xKey);
1109     }
1110 
1111 }
1112 
createView() const1113 Reference< XPropertySet > OCopyTableWizard::createView() const
1114 {
1115     OUString sCommand( m_rSourceObject.getSelectStatement() );
1116     OSL_ENSURE( !sCommand.isEmpty(), "OCopyTableWizard::createView: no statement in the source object!" );
1117         // there are legitimate cases in which getSelectStatement does not provide a statement,
1118         // but in all those cases, this method here should never be called.
1119     return ::dbaui::createView( m_sName, m_xDestConnection, sCommand );
1120 }
1121 
returnTable()1122 Reference< XPropertySet > OCopyTableWizard::returnTable()
1123 {
1124     if ( getOperation() == CopyTableOperation::AppendData )
1125         return getTable();
1126     else
1127         return createTable();
1128 }
1129 
getTable() const1130 Reference< XPropertySet > OCopyTableWizard::getTable() const
1131 {
1132     Reference< XPropertySet > xTable;
1133 
1134     Reference<XTablesSupplier> xSup( m_xDestConnection, UNO_QUERY );
1135     Reference< XNameAccess > xTables;
1136     if(xSup.is())
1137         xTables = xSup->getTables();
1138     if(xTables.is() && xTables->hasByName(m_sName))
1139         xTables->getByName(m_sName) >>= xTable;
1140 
1141     return xTable;
1142 }
1143 
createTable()1144 Reference< XPropertySet > OCopyTableWizard::createTable()
1145 {
1146     Reference< XPropertySet > xTable;
1147 
1148     Reference<XTablesSupplier> xSup( m_xDestConnection, UNO_QUERY );
1149     Reference< XNameAccess > xTables;
1150     if(xSup.is())
1151         xTables = xSup->getTables();
1152     Reference<XDataDescriptorFactory> xFact(xTables,UNO_QUERY);
1153     OSL_ENSURE(xFact.is(),"No XDataDescriptorFactory available!");
1154     if(!xFact.is())
1155         return nullptr;
1156 
1157     xTable = xFact->createDataDescriptor();
1158     OSL_ENSURE(xTable.is(),"Could not create a new object!");
1159     if(!xTable.is())
1160         return nullptr;
1161 
1162     OUString sCatalog,sSchema,sTable;
1163     Reference< XDatabaseMetaData> xMetaData = m_xDestConnection->getMetaData();
1164     ::dbtools::qualifiedNameComponents(xMetaData,
1165                                        m_sName,
1166                                        sCatalog,
1167                                        sSchema,
1168                                        sTable,
1169                                        ::dbtools::EComposeRule::InDataManipulation);
1170 
1171     if ( sCatalog.isEmpty() && xMetaData->supportsCatalogsInTableDefinitions() )
1172     {
1173         sCatalog = m_xDestConnection->getCatalog();
1174     }
1175 
1176     if ( sSchema.isEmpty() && xMetaData->supportsSchemasInTableDefinitions() )
1177     {
1178         // query of current schema is quite inconsistent. In case of some
1179         // DBMS's each user has their own schema.
1180         sSchema = xMetaData->getUserName();
1181         // In case of mysql it is not that simple
1182         if(xMetaData->getDatabaseProductName() == "MySQL")
1183         {
1184             Reference< XStatement > xSelect = m_xDestConnection->createStatement();
1185             Reference< XResultSet > xRs = xSelect->executeQuery(u"select database()"_ustr);
1186             (void)xRs->next(); // first and only result
1187             Reference< XRow > xRow( xRs, UNO_QUERY_THROW );
1188             sSchema = xRow->getString(1);
1189         }
1190     }
1191 
1192     xTable->setPropertyValue(PROPERTY_CATALOGNAME,Any(sCatalog));
1193     xTable->setPropertyValue(PROPERTY_SCHEMANAME,Any(sSchema));
1194     xTable->setPropertyValue(PROPERTY_NAME,Any(sTable));
1195 
1196     Reference< XColumnsSupplier > xSuppDestinationColumns( xTable, UNO_QUERY );
1197     // now append the columns
1198     const ODatabaseExport::TColumnVector& rVec = getDestVector();
1199     appendColumns( xSuppDestinationColumns, &rVec );
1200     // now append the primary key
1201     Reference<XKeysSupplier> xKeySup(xTable,UNO_QUERY);
1202     appendKey(xKeySup, &rVec);
1203 
1204     Reference<XAppend> xAppend(xTables,UNO_QUERY);
1205     if(xAppend.is())
1206         xAppend->appendByDescriptor(xTable);
1207 
1208     //  xTable = NULL;
1209     // we need to reget the table because after appending it, it is no longer valid
1210     if(xTables->hasByName(m_sName))
1211         xTables->getByName(m_sName) >>= xTable;
1212     else
1213     {
1214         OUString sComposedName(
1215             ::dbtools::composeTableName( m_xDestConnection->getMetaData(), xTable, ::dbtools::EComposeRule::InDataManipulation, false ) );
1216         if(xTables->hasByName(sComposedName))
1217         {
1218             xTables->getByName(sComposedName) >>= xTable;
1219             m_sName = sComposedName;
1220         }
1221         else
1222             xTable = nullptr;
1223     }
1224 
1225     if(xTable.is())
1226     {
1227         xSuppDestinationColumns.set( xTable, UNO_QUERY_THROW );
1228         // insert new table name into table filter
1229         ::dbaui::appendToFilter(m_xDestConnection, m_sName, GetComponentContext(), m_xAssistant.get());
1230 
1231         // copy ui settings
1232         m_rSourceObject.copyUISettingsTo( xTable );
1233         //copy filter and sorting
1234         m_rSourceObject.copyFilterAndSortingTo(m_xDestConnection,xTable);
1235         // set column mappings
1236         Reference<XNameAccess> xNameAccess = xSuppDestinationColumns->getColumns();
1237         Sequence< OUString> aSeq = xNameAccess->getElementNames();
1238 
1239         for (sal_Int32 i = 0; i < aSeq.getLength(); ++i)
1240         {
1241             ODatabaseExport::TColumns::const_iterator aDestIter = m_vDestColumns.find(aSeq[i]);
1242 
1243             if ( aDestIter != m_vDestColumns.end() )
1244             {
1245                 ODatabaseExport::TColumnVector::const_iterator aFind = std::find(m_aDestVec.begin(),m_aDestVec.end(),aDestIter);
1246                 sal_Int32 nPos = (aFind - m_aDestVec.begin())+1;
1247 
1248                 ODatabaseExport::TPositions::iterator aPosFind = std::find_if(
1249                     m_vColumnPositions.begin(),
1250                     m_vColumnPositions.end(),
1251                     [nPos] (const ODatabaseExport::TPositions::value_type& tPos) {
1252                         return tPos.first == nPos;
1253                     }
1254                 );
1255 
1256                 if ( m_vColumnPositions.end() != aPosFind )
1257                 {
1258                     aPosFind->second = i + 1;
1259                     OSL_ENSURE( m_vColumnTypes.size() > o3tl::make_unsigned( aPosFind - m_vColumnPositions.begin() ),
1260                         "Invalid index for vector!" );
1261                     m_vColumnTypes[ aPosFind - m_vColumnPositions.begin() ] = (*aFind)->second->GetType();
1262                 }
1263             }
1264         }
1265     }
1266 
1267     return xTable;
1268 }
1269 
supportsPrimaryKey(const Reference<XConnection> & _rxConnection)1270 bool OCopyTableWizard::supportsPrimaryKey( const Reference< XConnection >& _rxConnection )
1271 {
1272     OSL_PRECOND( _rxConnection.is(), "OCopyTableWizard::supportsPrimaryKey: invalid connection!" );
1273     if ( !_rxConnection.is() )
1274         return false;
1275 
1276     ::dbtools::DatabaseMetaData aMetaData( _rxConnection );
1277     return aMetaData.supportsPrimaryKeys();
1278 }
1279 
supportsViews(const Reference<XConnection> & _rxConnection)1280 bool OCopyTableWizard::supportsViews( const Reference< XConnection >& _rxConnection )
1281 {
1282     OSL_PRECOND( _rxConnection.is(), "OCopyTableWizard::supportsViews: invalid connection!" );
1283     if ( !_rxConnection.is() )
1284         return false;
1285 
1286     bool bSupportsViews( false );
1287     try
1288     {
1289         Reference< XDatabaseMetaData > xMetaData( _rxConnection->getMetaData(), UNO_SET_THROW );
1290         Reference< XViewsSupplier > xViewSups( _rxConnection, UNO_QUERY );
1291         bSupportsViews = xViewSups.is();
1292         if ( !bSupportsViews )
1293         {
1294             try
1295             {
1296                 Reference< XResultSet > xRs( xMetaData->getTableTypes(), UNO_SET_THROW );
1297                 Reference< XRow > xRow( xRs, UNO_QUERY_THROW );
1298                 while ( xRs->next() )
1299                 {
1300                     OUString sValue = xRow->getString( 1 );
1301                     if ( !xRow->wasNull() && sValue.equalsIgnoreAsciiCase("View") )
1302                     {
1303                         bSupportsViews = true;
1304                         break;
1305                     }
1306                 }
1307             }
1308             catch( const SQLException& )
1309             {
1310                 DBG_UNHANDLED_EXCEPTION("dbaccess");
1311             }
1312         }
1313     }
1314     catch( const Exception& )
1315     {
1316         DBG_UNHANDLED_EXCEPTION("dbaccess");
1317     }
1318     return bSupportsViews;
1319 }
1320 
getMaxColumnNameLength() const1321 sal_Int32 OCopyTableWizard::getMaxColumnNameLength() const
1322 {
1323     sal_Int32 nLen = 0;
1324     if ( m_xDestConnection.is() )
1325     {
1326         try
1327         {
1328             Reference< XDatabaseMetaData > xMetaData( m_xDestConnection->getMetaData(), UNO_SET_THROW );
1329             nLen = xMetaData->getMaxColumnNameLength();
1330         }
1331         catch(const Exception&)
1332         {
1333             DBG_UNHANDLED_EXCEPTION("dbaccess");
1334         }
1335     }
1336     return nLen;
1337 }
1338 
setOperation(const sal_Int16 _nOperation)1339 void OCopyTableWizard::setOperation( const sal_Int16 _nOperation )
1340 {
1341     m_nOperation = _nOperation;
1342 }
1343 
1344 
convertColumnName(const TColumnFindFunctor & _rCmpFunctor,const OUString & _sColumnName,std::u16string_view _sExtraChars,sal_Int32 _nMaxNameLen)1345 OUString OCopyTableWizard::convertColumnName(const TColumnFindFunctor&   _rCmpFunctor,
1346                                                     const OUString&  _sColumnName,
1347                                                     std::u16string_view  _sExtraChars,
1348                                                     sal_Int32               _nMaxNameLen)
1349 {
1350     OUString sAlias = _sColumnName;
1351     if ( isSQL92CheckEnabled( m_xDestConnection ) )
1352         sAlias = ::dbtools::convertName2SQLName(_sColumnName,_sExtraChars);
1353     if((_nMaxNameLen && sAlias.getLength() > _nMaxNameLen) || _rCmpFunctor(sAlias))
1354     {
1355         sal_Int32 nDiff = 1;
1356         do
1357         {
1358             ++nDiff;
1359             if(_nMaxNameLen && sAlias.getLength() >= _nMaxNameLen)
1360                 sAlias = sAlias.copy(0,sAlias.getLength() - (sAlias.getLength()-_nMaxNameLen+nDiff));
1361 
1362             OUString sName(sAlias);
1363             sal_Int32 nPos = 1;
1364             sName += OUString::number(nPos);
1365 
1366             while(_rCmpFunctor(sName))
1367             {
1368                 sName = sAlias + OUString::number(++nPos);
1369             }
1370             sAlias = sName;
1371             // we have to check again, it could happen that the name is already too long
1372         }
1373         while(_nMaxNameLen && sAlias.getLength() > _nMaxNameLen);
1374     }
1375     OSL_ENSURE(m_mNameMapping.find(_sColumnName) == m_mNameMapping.end(),"name doubled!");
1376     m_mNameMapping[_sColumnName] = sAlias;
1377     return sAlias;
1378 }
1379 
removeColumnNameFromNameMap(const OUString & _sName)1380 void OCopyTableWizard::removeColumnNameFromNameMap(const OUString& _sName)
1381 {
1382     m_mNameMapping.erase(_sName);
1383 }
1384 
supportsType(sal_Int32 _nDataType,sal_Int32 & _rNewDataType)1385 bool OCopyTableWizard::supportsType(sal_Int32 _nDataType,   sal_Int32& _rNewDataType)
1386 {
1387     bool bRet = m_aDestTypeInfo.find(_nDataType) != m_aDestTypeInfo.end();
1388     if ( bRet )
1389         _rNewDataType = _nDataType;
1390     return bRet;
1391 }
1392 
convertType(const TOTypeInfoSP & _pType,bool & _bNotConvert)1393 TOTypeInfoSP OCopyTableWizard::convertType(const TOTypeInfoSP& _pType, bool& _bNotConvert)
1394 {
1395     if ( !m_bInterConnectionCopy )
1396         // no need to convert if the source and destination connection are the same
1397         return _pType;
1398 
1399     bool bForce;
1400     TOTypeInfoSP pType = ::dbaui::getTypeInfoFromType(m_aDestTypeInfo,_pType->nType,_pType->aTypeName,_pType->aCreateParams,_pType->nPrecision,_pType->nMaximumScale,_pType->bAutoIncrement,bForce);
1401     if ( !pType || bForce )
1402     { // no type found so we have to find the correct one ourself
1403         sal_Int32 nDefaultType = DataType::VARCHAR;
1404         switch(_pType->nType)
1405         {
1406             case DataType::TINYINT:
1407                 if(supportsType(DataType::SMALLINT,nDefaultType))
1408                     break;
1409                 [[fallthrough]];
1410             case DataType::SMALLINT:
1411                 if(supportsType(DataType::INTEGER,nDefaultType))
1412                     break;
1413                 [[fallthrough]];
1414             case DataType::INTEGER:
1415                 if(supportsType(DataType::FLOAT,nDefaultType))
1416                     break;
1417                 [[fallthrough]];
1418             case DataType::FLOAT:
1419                 if(supportsType(DataType::REAL,nDefaultType))
1420                     break;
1421                 [[fallthrough]];
1422             case DataType::DATE:
1423             case DataType::TIME:
1424                 if( DataType::DATE == _pType->nType || DataType::TIME == _pType->nType )
1425                 {
1426                     if(supportsType(DataType::TIMESTAMP,nDefaultType))
1427                         break;
1428                 }
1429                 [[fallthrough]];
1430             case DataType::TIMESTAMP:
1431             case DataType::REAL:
1432             case DataType::BIGINT:
1433                 if ( supportsType(DataType::DOUBLE,nDefaultType) )
1434                     break;
1435                 [[fallthrough]];
1436             case DataType::DOUBLE:
1437                 if ( supportsType(DataType::NUMERIC,nDefaultType) )
1438                     break;
1439                 [[fallthrough]];
1440             case DataType::NUMERIC:
1441                 supportsType(DataType::DECIMAL,nDefaultType);
1442                 break;
1443             case DataType::DECIMAL:
1444                 if ( supportsType(DataType::NUMERIC,nDefaultType) )
1445                     break;
1446                 if ( supportsType(DataType::DOUBLE,nDefaultType) )
1447                     break;
1448                 break;
1449             case DataType::VARCHAR:
1450                 if ( supportsType(DataType::LONGVARCHAR,nDefaultType) )
1451                     break;
1452                 break;
1453             case DataType::LONGVARCHAR:
1454                 if ( supportsType(DataType::CLOB,nDefaultType) )
1455                     break;
1456                 break;
1457             case DataType::BINARY:
1458                 if ( supportsType(DataType::VARBINARY,nDefaultType) )
1459                     break;
1460                 break;
1461             case DataType::VARBINARY:
1462                 if ( supportsType(DataType::LONGVARBINARY,nDefaultType) )
1463                     break;
1464                 break;
1465             case DataType::LONGVARBINARY:
1466                 if ( supportsType(DataType::BLOB,nDefaultType) )
1467                     break;
1468                 if ( supportsType(DataType::LONGVARCHAR,nDefaultType) )
1469                     break;
1470                 if ( supportsType(DataType::CLOB,nDefaultType) )
1471                     break;
1472                 break;
1473             default:
1474                 nDefaultType = DataType::VARCHAR;
1475         }
1476         pType = ::dbaui::getTypeInfoFromType(m_aDestTypeInfo,nDefaultType,_pType->aTypeName,_pType->aCreateParams,_pType->nPrecision,_pType->nMaximumScale,_pType->bAutoIncrement,bForce);
1477         if ( !pType )
1478         {
1479             _bNotConvert = false;
1480             pType = ::dbaui::getTypeInfoFromType(m_aDestTypeInfo,DataType::VARCHAR,_pType->aTypeName,u"x"_ustr,50,0,false,bForce);
1481             if ( !pType )
1482                 pType = m_pTypeInfo;
1483         }
1484         else if ( bForce )
1485             _bNotConvert = false;
1486     }
1487     return pType;
1488 }
1489 
createUniqueName(const OUString & _sName)1490 OUString OCopyTableWizard::createUniqueName(const OUString& _sName)
1491 {
1492     OUString sName = _sName;
1493     Sequence< OUString > aColumnNames( m_rSourceObject.getColumnNames() );
1494     if ( aColumnNames.hasElements() )
1495         sName = ::dbtools::createUniqueName( aColumnNames, sName, false );
1496     else
1497     {
1498         if ( m_vSourceColumns.find(sName) != m_vSourceColumns.end())
1499         {
1500             sal_Int32 nPos = 0;
1501             while(m_vSourceColumns.find(sName) != m_vSourceColumns.end())
1502             {
1503                 sName = _sName + OUString::number(++nPos);
1504             }
1505         }
1506     }
1507     return sName;
1508 }
1509 
showColumnTypeNotSupported(std::u16string_view _rColumnName)1510 void OCopyTableWizard::showColumnTypeNotSupported(std::u16string_view _rColumnName)
1511 {
1512     OUString sMessage( DBA_RES( STR_UNKNOWN_TYPE_FOUND ) );
1513     sMessage = sMessage.replaceFirst("#1",_rColumnName);
1514     showError(sMessage);
1515 }
1516 
showError(const OUString & _sErrorMessage)1517 void OCopyTableWizard::showError(const OUString& _sErrorMessage)
1518 {
1519     SQLExceptionInfo aInfo(_sErrorMessage);
1520     showError(aInfo.get());
1521 }
1522 
showError(const Any & _aError)1523 void OCopyTableWizard::showError(const Any& _aError)
1524 {
1525     if ( _aError.hasValue() && m_xInteractionHandler.is() )
1526     {
1527         try
1528         {
1529             ::rtl::Reference< ::comphelper::OInteractionRequest > xRequest( new ::comphelper::OInteractionRequest( _aError ) );
1530             m_xInteractionHandler->handle( xRequest );
1531         }
1532         catch( const Exception& )
1533         {
1534             DBG_UNHANDLED_EXCEPTION("dbaccess");
1535         }
1536     }
1537 }
1538 
1539 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1540