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 <memory>
21 #include <dbu_reghelper.hxx>
22 #include <strings.hrc>
23 #include <strings.hxx>
24 #include <stringconstants.hxx>
25 #include <core_resource.hxx>
26 #include <sqlmessage.hxx>
27 #include <uiservices.hxx>
28 #include <WCopyTable.hxx>
29 
30 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
31 #include <com/sun/star/sdb/application/XCopyTableWizard.hpp>
32 #include <com/sun/star/sdb/application/CopyTableContinuation.hpp>
33 #include <com/sun/star/sdb/application/CopyTableOperation.hpp>
34 #include <com/sun/star/ucb/AlreadyInitializedException.hpp>
35 #include <com/sun/star/lang/NotInitializedException.hpp>
36 #include <com/sun/star/sdbc/XDataSource.hpp>
37 #include <com/sun/star/sdbc/DataType.hpp>
38 #include <com/sun/star/container/XNameAccess.hpp>
39 #include <com/sun/star/container/XChild.hpp>
40 #include <com/sun/star/task/InteractionHandler.hpp>
41 #include <com/sun/star/frame/XModel.hpp>
42 #include <com/sun/star/sdb/DatabaseContext.hpp>
43 #include <com/sun/star/sdb/XDocumentDataSource.hpp>
44 #include <com/sun/star/sdb/XCompletedConnection.hpp>
45 #include <com/sun/star/sdb/CommandType.hpp>
46 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
47 #include <com/sun/star/sdb/XQueriesSupplier.hpp>
48 #include <com/sun/star/lang/DisposedException.hpp>
49 #include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp>
50 #include <com/sun/star/sdbc/XParameters.hpp>
51 #include <com/sun/star/sdbc/XRow.hpp>
52 #include <com/sun/star/sdbcx/XRowLocate.hpp>
53 #include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
54 #include <com/sun/star/sdb/SQLContext.hpp>
55 #include <com/sun/star/sdbc/DriverManager.hpp>
56 #include <com/sun/star/sdbc/ConnectionPool.hpp>
57 
58 #include <comphelper/processfactory.hxx>
59 #include <comphelper/interaction.hxx>
60 #include <comphelper/namedvaluecollection.hxx>
61 #include <comphelper/proparrhlp.hxx>
62 #include <connectivity/dbexception.hxx>
63 #include <connectivity/dbtools.hxx>
64 #include <cppuhelper/exc_hlp.hxx>
65 #include <cppuhelper/implbase.hxx>
66 #include <comphelper/interfacecontainer2.hxx>
67 #include <rtl/ustrbuf.hxx>
68 #include <sal/log.hxx>
69 #include <svtools/genericunodialog.hxx>
70 #include <toolkit/helper/vclunohelper.hxx>
71 #include <tools/diagnose_ex.h>
72 #include <unotools/sharedunocomponent.hxx>
73 #include <vcl/svapp.hxx>
74 
75 namespace dbaui
76 {
77 
78     using ::com::sun::star::uno::Reference;
79     using ::com::sun::star::uno::XInterface;
80     using ::com::sun::star::uno::UNO_QUERY;
81     using ::com::sun::star::uno::UNO_QUERY_THROW;
82     using ::com::sun::star::uno::UNO_SET_THROW;
83     using ::com::sun::star::uno::Exception;
84     using ::com::sun::star::uno::RuntimeException;
85     using ::com::sun::star::uno::Any;
86     using ::com::sun::star::uno::makeAny;
87     using ::com::sun::star::uno::Sequence;
88     using ::com::sun::star::uno::XComponentContext;
89     using ::com::sun::star::beans::XPropertySetInfo;
90     using ::com::sun::star::lang::XMultiServiceFactory;
91     using ::com::sun::star::beans::Property;
92     using ::com::sun::star::sdb::application::XCopyTableWizard;
93     using ::com::sun::star::sdb::application::XCopyTableListener;
94     using ::com::sun::star::sdb::application::CopyTableRowEvent;
95     using ::com::sun::star::beans::Optional;
96     using ::com::sun::star::lang::IllegalArgumentException;
97     using ::com::sun::star::ucb::AlreadyInitializedException;
98     using ::com::sun::star::beans::XPropertySet;
99     using ::com::sun::star::lang::NotInitializedException;
100     using ::com::sun::star::lang::XServiceInfo;
101     using ::com::sun::star::sdbc::XConnection;
102     using ::com::sun::star::sdbc::XDataSource;
103     using ::com::sun::star::container::XNameAccess;
104     using ::com::sun::star::container::XChild;
105     using ::com::sun::star::task::InteractionHandler;
106     using ::com::sun::star::task::XInteractionHandler;
107     using ::com::sun::star::frame::XModel;
108     using ::com::sun::star::sdb::DatabaseContext;
109     using ::com::sun::star::sdb::XDatabaseContext;
110     using ::com::sun::star::sdb::XDocumentDataSource;
111     using ::com::sun::star::sdb::XCompletedConnection;
112     using ::com::sun::star::lang::WrappedTargetException;
113     using ::com::sun::star::sdbcx::XTablesSupplier;
114     using ::com::sun::star::sdb::XQueriesSupplier;
115     using ::com::sun::star::lang::DisposedException;
116     using ::com::sun::star::sdbc::XPreparedStatement;
117     using ::com::sun::star::sdb::XSingleSelectQueryComposer;
118     using ::com::sun::star::sdbc::XDatabaseMetaData;
119     using ::com::sun::star::sdbcx::XColumnsSupplier;
120     using ::com::sun::star::sdbc::XParameters;
121     using ::com::sun::star::sdbc::XResultSet;
122     using ::com::sun::star::sdbc::XRow;
123     using ::com::sun::star::sdbcx::XRowLocate;
124     using ::com::sun::star::sdbc::XResultSetMetaDataSupplier;
125     using ::com::sun::star::sdbc::XResultSetMetaData;
126     using ::com::sun::star::sdbc::SQLException;
127     using ::com::sun::star::sdb::SQLContext;
128     using ::com::sun::star::sdbc::ConnectionPool;
129     using ::com::sun::star::sdbc::XDriverManager;
130     using ::com::sun::star::sdbc::DriverManager;
131     using ::com::sun::star::beans::PropertyValue;
132 
133     namespace CopyTableOperation = ::com::sun::star::sdb::application::CopyTableOperation;
134     namespace CopyTableContinuation = ::com::sun::star::sdb::application::CopyTableContinuation;
135     namespace CommandType = ::com::sun::star::sdb::CommandType;
136     namespace DataType = ::com::sun::star::sdbc::DataType;
137 
138     typedef ::utl::SharedUNOComponent< XConnection >    SharedConnection;
139 
140     // CopyTableWizard
141     typedef ::svt::OGenericUnoDialog        CopyTableWizard_DialogBase;
142     typedef ::cppu::ImplInheritanceHelper<   CopyTableWizard_DialogBase
143                                          ,   XCopyTableWizard
144                                          >   CopyTableWizard_Base;
145     class CopyTableWizard
146             :public CopyTableWizard_Base
147             ,public ::comphelper::OPropertyArrayUsageHelper< CopyTableWizard >
148     {
149     public:
150         // XServiceInfo
151         virtual OUString SAL_CALL getImplementationName() override;
152         virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
153 
154         // XServiceInfo - static methods
155         /// @throws RuntimeException
156         static Sequence< OUString >  getSupportedServiceNames_Static();
157         /// @throws RuntimeException
158         static OUString              getImplementationName_Static();
159         static Reference< XInterface >      Create( const Reference< XMultiServiceFactory >& );
160 
161         // XCopyTableWizard
162         virtual ::sal_Int16 SAL_CALL getOperation() override;
163         virtual void SAL_CALL setOperation( ::sal_Int16 _operation ) override;
164         virtual OUString SAL_CALL getDestinationTableName() override;
165         virtual void SAL_CALL setDestinationTableName( const OUString& _destinationTableName ) override;
166         virtual Optional< OUString > SAL_CALL getCreatePrimaryKey() override;
167         virtual void SAL_CALL setCreatePrimaryKey( const Optional< OUString >& _newPrimaryKey ) override;
168         virtual sal_Bool SAL_CALL getUseHeaderLineAsColumnNames() override;
169         virtual void SAL_CALL setUseHeaderLineAsColumnNames( sal_Bool _bUseHeaderLineAsColumnNames ) override;
170         virtual void SAL_CALL addCopyTableListener( const Reference< XCopyTableListener >& Listener ) override;
171         virtual void SAL_CALL removeCopyTableListener( const Reference< XCopyTableListener >& Listener ) override;
172 
173         // XCopyTableWizard::XExecutableDialog
174         virtual void SAL_CALL setTitle( const OUString& aTitle ) override;
175         virtual ::sal_Int16 SAL_CALL execute(  ) override;
176 
177         // XInitialization
178         virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override;
179 
180         // XPropertySet
181         virtual Reference< XPropertySetInfo > SAL_CALL getPropertySetInfo() override;
182         virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
183 
184         // OPropertyArrayUsageHelper
185         virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override;
186 
187     public:
getMutex()188         ::osl::Mutex&   getMutex() { return m_aMutex; }
isInitialized() const189         bool            isInitialized() const { return m_xSourceConnection.is() && m_pSourceObject.get() && m_xDestConnection.is(); }
190 
191     protected:
192         explicit CopyTableWizard( const Reference< XComponentContext >& _rxORB );
193         virtual ~CopyTableWizard() override;
194 
195         // OGenericUnoDialog overridables
196         virtual std::unique_ptr<weld::DialogController> createDialog(const css::uno::Reference<css::awt::XWindow>& rParent) override;
197         virtual void executedDialog( sal_Int16 _nExecutionResult ) override;
198 
199     private:
200         /// ensures our current attribute values are reflected in the dialog
201         void    impl_attributesToDialog_nothrow( OCopyTableWizard& _rDialog ) const;
202 
203         /// ensures the current dialog settings are reflected in our attributes
204         void    impl_dialogToAttributes_nothrow( const OCopyTableWizard& _rDialog );
205 
206         /** returns our typed dialog
207 
208             @throws css::uno::RuntimeException
209                 if we don't have a dialog at the moment the method is called
210         */
211         OCopyTableWizard&
212                 impl_getDialog_throw();
213 
214         /** ensures the given argument sequence contains a valid data access descriptor at the given position
215             @param _rAllArgs
216                 the arguments as passed to ->initialize
217             @param _nArgPos
218                 the position within ->_rAllArgs which contains the data access descriptor
219             @param _out_rxConnection
220                 will, upon successful return, contain the connection for the data source
221             @param _out_rxDocInteractionHandler
222                 will, upon successful return, contain the interaction handler which could
223                 be deduced from database document described by the descriptor, if any.
224                 (It is possible that the descriptor does not allow to deduce a database document,
225                 in which case <code>_out_rxDocInteractionHandler</code> will be <NULL/>.)
226             @return the data access descriptor
227         */
228         Reference< XPropertySet >
229                 impl_ensureDataAccessDescriptor_throw(
230                     const Sequence< Any >& _rAllArgs,
231                     const sal_Int16 _nArgPos,
232                     SharedConnection& _out_rxConnection,
233                     Reference< XInteractionHandler >& _out_rxDocInteractionHandler
234                 ) const;
235 
236         /** extracts the source object (table or query) described by the given descriptor,
237             relative to m_xSourceConnection
238         */
239         std::unique_ptr< ICopyTableSourceObject >
240                 impl_extractSourceObject_throw(
241                     const Reference< XPropertySet >& _rxDescriptor,
242                     sal_Int32& _out_rCommandType
243                 ) const;
244 
245         /** extracts the result set to copy records from, and the selection-related aspects, if any.
246 
247             Effectively, this method extracts m_xSourceResultSet, m_aSourceSelection, and m_bSourceSelectionBookmarks.
248 
249             If an inconsistent/insufficient sub set of those properties is present in the descriptor, and exception
250             is thrown.
251         */
252         void    impl_extractSourceResultSet_throw(
253                     const Reference< XPropertySet >& i_rDescriptor
254                 );
255 
256         /** checks whether the given copy source descriptor contains settings which are not
257             supported (yet)
258 
259             Throws an IllegalArgumentException if the descriptor contains a valid setting, which is
260             not yet supported.
261         */
262         void    impl_checkForUnsupportedSettings_throw(
263             const Reference< XPropertySet >& _rxSourceDescriptor ) const;
264 
265         /** obtains the connection described by the given data access descriptor
266 
267             If needed and possible, the method will ask the user, using the interaction
268             handler associated with the database described by the descriptor.
269 
270             All errors are handled with the InteractionHandler associated with the data source,
271             if there is one. Else, they will be silenced (but asserted in non-product builds).
272 
273             @param _rxDataSourceDescriptor
274                 the data access descriptor describing the data source whose connection
275                 should be obtained. Must not be <NULL/>.
276             @param _out_rxDocInteractionHandler
277                 the interaction handler which could be deduced from the descriptor
278 
279             @throws RuntimeException
280                 if anything goes seriously wrong.
281         */
282         SharedConnection
283                 impl_extractConnection_throw(
284                     const Reference< XPropertySet >& _rxDataSourceDescriptor,
285                     Reference< XInteractionHandler >& _out_rxDocInteractionHandler
286                 ) const;
287 
288         /** actually copies the table
289 
290             This method is called after the dialog has been successfully executed.
291         */
292         void    impl_doCopy_nothrow();
293 
294         /** creates the INSERT INTO statement
295             @param  _xTable The destination table.
296         */
297         OUString impl_getServerSideCopyStatement_throw( const Reference< XPropertySet >& _xTable );
298 
299         /** creates the statement which, when executed, will produce the source data to copy
300 
301             If the source object refers to a query which contains parameters, those parameters
302             are filled in, using an interaction handler.
303         */
304         ::utl::SharedUNOComponent< XPreparedStatement >
305                 impl_createSourceStatement_throw() const;
306 
307         /** copies the data rows from the given source result set to the given destination table
308         */
309         void    impl_copyRows_throw(
310                     const Reference< XResultSet >& _rxSourceResultSet,
311                     const Reference< XPropertySet >& _rxDestTable
312                 );
313 
314         /** processes an error which occurred during copying
315 
316             First, all listeners are ask. If a listener tells to cancel or continue copying, this is reported to the
317             method's caller. If a listener tells to ask the user, this is done, and the user's decision is
318             reported to the method's caller.
319 
320             @return
321                 <TRUE/> if and only if copying should be continued.
322         */
323         bool    impl_processCopyError_nothrow(
324                     const CopyTableRowEvent& _rEvent );
325 
326 private:
327         Reference<XComponentContext>    m_xContext;
328 
329         // attributes
330         sal_Int16                       m_nOperation;
331         OUString                        m_sDestinationTable;
332         Optional< OUString >            m_aPrimaryKeyName;
333         bool                        m_bUseHeaderLineAsColumnNames;
334 
335         // source
336         SharedConnection                m_xSourceConnection;
337         sal_Int32                       m_nCommandType;
338         std::unique_ptr< ICopyTableSourceObject >
339                                         m_pSourceObject;
340         Reference< XResultSet >         m_xSourceResultSet;
341         Sequence< Any >                 m_aSourceSelection;
342         bool                        m_bSourceSelectionBookmarks;
343 
344         // destination
345         SharedConnection                m_xDestConnection;
346 
347         // other
348         Reference< XInteractionHandler > m_xInteractionHandler;
349         ::comphelper::OInterfaceContainerHelper2
350                                         m_aCopyTableListeners;
351         sal_Int16                       m_nOverrideExecutionResult;
352     };
353 
354 // MethodGuard
355 class CopyTableAccessGuard
356 {
357 public:
CopyTableAccessGuard(CopyTableWizard & _rWizard)358     explicit CopyTableAccessGuard( CopyTableWizard& _rWizard )
359         :m_rWizard( _rWizard )
360     {
361         m_rWizard.getMutex().acquire();
362         if ( !m_rWizard.isInitialized() )
363             throw NotInitializedException();
364     }
365 
~CopyTableAccessGuard()366     ~CopyTableAccessGuard()
367     {
368         m_rWizard.getMutex().release();
369     }
370 
371 private:
372     CopyTableWizard&    m_rWizard;
373 };
374 
CopyTableWizard(const Reference<XComponentContext> & _rxORB)375 CopyTableWizard::CopyTableWizard( const Reference< XComponentContext >& _rxORB )
376     :CopyTableWizard_Base( _rxORB )
377     ,m_xContext( _rxORB )
378     ,m_nOperation( CopyTableOperation::CopyDefinitionAndData )
379     ,m_sDestinationTable()
380     ,m_aPrimaryKeyName( false,  "ID" )
381     ,m_bUseHeaderLineAsColumnNames( true )
382     ,m_xSourceConnection()
383     ,m_nCommandType( CommandType::COMMAND )
384     ,m_pSourceObject()
385     ,m_xSourceResultSet()
386     ,m_aSourceSelection()
387     ,m_bSourceSelectionBookmarks( true )
388     ,m_xDestConnection()
389     ,m_aCopyTableListeners( m_aMutex )
390     ,m_nOverrideExecutionResult( -1 )
391 {
392 }
393 
~CopyTableWizard()394 CopyTableWizard::~CopyTableWizard()
395 {
396     acquire();
397 
398     // protect some members whose dtor might potentially throw
399     try { m_xSourceConnection.clear();  }
400     catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION("dbaccess"); }
401     try { m_xDestConnection.clear();  }
402     catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION("dbaccess"); }
403 
404     // TODO: shouldn't we have explicit disposal support? If a listener is registered
405     // at our instance, and perhaps holds this our instance by a hard ref, then we'll never
406     // be destroyed.
407     // However, adding XComponent support to the GenericUNODialog probably requires
408     // some thinking - would it break existing clients which do not call a dispose, then?
409 }
410 
Create(const Reference<XMultiServiceFactory> & _rxFactory)411 Reference< XInterface > CopyTableWizard::Create( const Reference< XMultiServiceFactory >& _rxFactory )
412 {
413     return *( new CopyTableWizard( comphelper::getComponentContext(_rxFactory) ) );
414 }
415 
getImplementationName()416 OUString SAL_CALL CopyTableWizard::getImplementationName()
417 {
418     return getImplementationName_Static();
419 }
420 
getImplementationName_Static()421 OUString CopyTableWizard::getImplementationName_Static()
422 {
423     return "org.openoffice.comp.dbu.CopyTableWizard";
424 }
425 
getSupportedServiceNames()426 css::uno::Sequence<OUString> SAL_CALL CopyTableWizard::getSupportedServiceNames()
427 {
428     return getSupportedServiceNames_Static();
429 }
430 
getSupportedServiceNames_Static()431 css::uno::Sequence<OUString> CopyTableWizard::getSupportedServiceNames_Static()
432 {
433     css::uno::Sequence<OUString> aSupported { "com.sun.star.sdb.application.CopyTableWizard" };
434     return aSupported;
435 }
436 
getPropertySetInfo()437 Reference< XPropertySetInfo > SAL_CALL CopyTableWizard::getPropertySetInfo()
438 {
439     Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
440     return xInfo;
441 }
442 
getOperation()443 ::sal_Int16 SAL_CALL CopyTableWizard::getOperation()
444 {
445     CopyTableAccessGuard aGuard( *this );
446     return m_nOperation;
447 }
448 
setOperation(::sal_Int16 _operation)449 void SAL_CALL CopyTableWizard::setOperation( ::sal_Int16 _operation )
450 {
451     CopyTableAccessGuard aGuard( *this );
452 
453     if  (   ( _operation != CopyTableOperation::CopyDefinitionAndData )
454         &&  ( _operation != CopyTableOperation::CopyDefinitionOnly )
455         &&  ( _operation != CopyTableOperation::CreateAsView )
456         &&  ( _operation != CopyTableOperation::AppendData )
457         )
458         throw IllegalArgumentException( OUString(), *this, 1 );
459 
460     if  (   ( _operation == CopyTableOperation::CreateAsView )
461         &&  !OCopyTableWizard::supportsViews( m_xDestConnection )
462         )
463         throw IllegalArgumentException(
464             DBA_RES( STR_CTW_NO_VIEWS_SUPPORT ),
465             *this,
466             1
467         );
468 
469     m_nOperation = _operation;
470 }
471 
getDestinationTableName()472 OUString SAL_CALL CopyTableWizard::getDestinationTableName()
473 {
474     CopyTableAccessGuard aGuard( *this );
475     return m_sDestinationTable;
476 }
477 
setDestinationTableName(const OUString & _destinationTableName)478 void SAL_CALL CopyTableWizard::setDestinationTableName( const OUString& _destinationTableName )
479 {
480     CopyTableAccessGuard aGuard( *this );
481     m_sDestinationTable = _destinationTableName;
482 }
483 
getCreatePrimaryKey()484 Optional< OUString > SAL_CALL CopyTableWizard::getCreatePrimaryKey()
485 {
486     CopyTableAccessGuard aGuard( *this );
487     return m_aPrimaryKeyName;
488 }
489 
setCreatePrimaryKey(const Optional<OUString> & _newPrimaryKey)490 void SAL_CALL CopyTableWizard::setCreatePrimaryKey( const Optional< OUString >& _newPrimaryKey )
491 {
492     CopyTableAccessGuard aGuard( *this );
493 
494     if ( _newPrimaryKey.IsPresent && !OCopyTableWizard::supportsPrimaryKey( m_xDestConnection ) )
495         throw IllegalArgumentException(
496             DBA_RES( STR_CTW_NO_PRIMARY_KEY_SUPPORT ),
497             *this,
498             1
499         );
500 
501     m_aPrimaryKeyName = _newPrimaryKey;
502 }
503 
getUseHeaderLineAsColumnNames()504 sal_Bool SAL_CALL CopyTableWizard::getUseHeaderLineAsColumnNames()
505 {
506     CopyTableAccessGuard aGuard( *this );
507     return m_bUseHeaderLineAsColumnNames;
508 }
509 
setUseHeaderLineAsColumnNames(sal_Bool _bUseHeaderLineAsColumnNames)510 void SAL_CALL CopyTableWizard::setUseHeaderLineAsColumnNames( sal_Bool _bUseHeaderLineAsColumnNames )
511 {
512     CopyTableAccessGuard aGuard( *this );
513     m_bUseHeaderLineAsColumnNames = _bUseHeaderLineAsColumnNames;
514 }
515 
addCopyTableListener(const Reference<XCopyTableListener> & _rxListener)516 void SAL_CALL CopyTableWizard::addCopyTableListener( const Reference< XCopyTableListener >& _rxListener )
517 {
518     CopyTableAccessGuard aGuard( *this );
519     if ( _rxListener.is() )
520         m_aCopyTableListeners.addInterface( _rxListener );
521 }
522 
removeCopyTableListener(const Reference<XCopyTableListener> & _rxListener)523 void SAL_CALL CopyTableWizard::removeCopyTableListener( const Reference< XCopyTableListener >& _rxListener )
524 {
525     CopyTableAccessGuard aGuard( *this );
526     if ( _rxListener.is() )
527         m_aCopyTableListeners.removeInterface( _rxListener );
528 }
529 
setTitle(const OUString & _rTitle)530 void SAL_CALL CopyTableWizard::setTitle( const OUString& _rTitle )
531 {
532     CopyTableAccessGuard aGuard( *this );
533     CopyTableWizard_DialogBase::setTitle( _rTitle );
534 }
535 
execute()536 ::sal_Int16 SAL_CALL CopyTableWizard::execute(  )
537 {
538     CopyTableAccessGuard aGuard( *this );
539 
540     m_nOverrideExecutionResult = -1;
541     sal_Int16 nExecutionResult = CopyTableWizard_DialogBase::execute();
542     if ( m_nOverrideExecutionResult )
543         nExecutionResult = m_nOverrideExecutionResult;
544 
545     return nExecutionResult;
546 }
547 
impl_getDialog_throw()548 OCopyTableWizard& CopyTableWizard::impl_getDialog_throw()
549 {
550     OCopyTableWizard* pWizard = dynamic_cast<OCopyTableWizard*>(m_xDialog.get());
551     if ( !pWizard )
552         throw DisposedException( OUString(), *this );
553     return *pWizard;
554 }
555 
impl_attributesToDialog_nothrow(OCopyTableWizard & _rDialog) const556 void CopyTableWizard::impl_attributesToDialog_nothrow( OCopyTableWizard& _rDialog ) const
557 {
558     // primary key column
559     _rDialog.setCreatePrimaryKey( m_aPrimaryKeyName.IsPresent, m_aPrimaryKeyName.Value );
560     _rDialog.setUseHeaderLine(m_bUseHeaderLineAsColumnNames);
561 
562     // everything else was passed at construction time already
563 }
564 
impl_dialogToAttributes_nothrow(const OCopyTableWizard & _rDialog)565 void CopyTableWizard::impl_dialogToAttributes_nothrow( const OCopyTableWizard& _rDialog )
566 {
567     m_aPrimaryKeyName.IsPresent = _rDialog.shouldCreatePrimaryKey();
568     if ( m_aPrimaryKeyName.IsPresent )
569         m_aPrimaryKeyName.Value = _rDialog.getPrimaryKeyName();
570     else
571         m_aPrimaryKeyName.Value.clear();
572 
573     m_sDestinationTable = _rDialog.getName();
574 
575     m_nOperation = _rDialog.getOperation();
576     m_bUseHeaderLineAsColumnNames = _rDialog.UseHeaderLine();
577 }
578 
579 namespace
580 {
581     /** tries to obtain the InteractionHandler associated with a given data source
582 
583         If the data source is a sdb-level data source, it will have a DatabaseDocument associated
584         with it. This document may have an InteractionHandler used while loading it.
585 
586         @throws RuntimeException
587             if it occurs during invoking any of the data source's methods, or if any of the involved
588             components violates its contract by not providing the required interfaces
589     */
lcl_getInteractionHandler_throw(const Reference<XDataSource> & _rxDataSource,const Reference<XInteractionHandler> & _rFallback)590     Reference< XInteractionHandler > lcl_getInteractionHandler_throw( const Reference< XDataSource >& _rxDataSource, const Reference< XInteractionHandler >& _rFallback )
591     {
592         Reference< XInteractionHandler > xHandler( _rFallback );
593 
594         // try to obtain the document model
595         Reference< XModel > xDocumentModel;
596         Reference< XDocumentDataSource > xDocDataSource( _rxDataSource, UNO_QUERY );
597         if ( xDocDataSource.is() )
598             xDocumentModel.set( xDocDataSource->getDatabaseDocument(), UNO_QUERY_THROW );
599 
600         // see whether the document model can provide a handler
601         if ( xDocumentModel.is() )
602         {
603             ::comphelper::NamedValueCollection aModelArgs( xDocumentModel->getArgs() );
604             xHandler = aModelArgs.getOrDefault( "InteractionHandler", xHandler );
605         }
606 
607         return xHandler;
608     }
609     /** tries to obtain the InteractionHandler associated with a given connection
610 
611         If the connection belongs to a sdb-level data source, then this data source
612         is examined for an interaction handler. Else, <NULL/> is returned.
613 
614         @throws RuntimeException
615             if it occurs during invoking any of the data source's methods, or if any of the involved
616             components violates its contract by not providing the required interfaces
617     */
lcl_getInteractionHandler_throw(const Reference<XConnection> & _rxConnection,const Reference<XInteractionHandler> & _rFallback)618     Reference< XInteractionHandler > lcl_getInteractionHandler_throw( const Reference< XConnection >& _rxConnection, const Reference< XInteractionHandler >& _rFallback )
619     {
620         // try whether there is a data source which the connection belongs to
621         Reference< XDataSource > xDataSource;
622         Reference< XChild > xAsChild( _rxConnection, UNO_QUERY );
623         if ( xAsChild.is() )
624             xDataSource.set(xAsChild->getParent(), css::uno::UNO_QUERY);
625 
626         if ( xDataSource.is() )
627             return lcl_getInteractionHandler_throw( xDataSource, _rFallback );
628 
629         return _rFallback;
630     }
631 }
632 
impl_ensureDataAccessDescriptor_throw(const Sequence<Any> & _rAllArgs,const sal_Int16 _nArgPos,SharedConnection & _out_rxConnection,Reference<XInteractionHandler> & _out_rxDocInteractionHandler) const633 Reference< XPropertySet > CopyTableWizard::impl_ensureDataAccessDescriptor_throw(
634     const Sequence< Any >& _rAllArgs, const sal_Int16 _nArgPos, SharedConnection& _out_rxConnection,
635     Reference< XInteractionHandler >& _out_rxDocInteractionHandler ) const
636 {
637     Reference< XPropertySet > xDescriptor;
638     _rAllArgs[ _nArgPos ] >>= xDescriptor;
639 
640     // the descriptor must be non-NULL, of course
641     bool bIsValid = xDescriptor.is();
642 
643     // it must support the proper service
644     if ( bIsValid )
645     {
646         Reference< XServiceInfo > xSI( xDescriptor, UNO_QUERY );
647         bIsValid =  (   xSI.is()
648                     &&  xSI->supportsService( "com.sun.star.sdb.DataAccessDescriptor" ) );
649     }
650 
651     // it must be able to provide a connection
652     if ( bIsValid )
653     {
654         _out_rxConnection = impl_extractConnection_throw( xDescriptor, _out_rxDocInteractionHandler );
655         bIsValid = _out_rxConnection.is();
656     }
657 
658     if ( !bIsValid )
659     {
660         throw IllegalArgumentException(
661             DBA_RES( STR_CTW_INVALID_DATA_ACCESS_DESCRIPTOR ),
662             *const_cast< CopyTableWizard* >( this ),
663             _nArgPos + 1
664         );
665     }
666 
667     return xDescriptor;
668 }
669 
670 namespace
671 {
lcl_hasNonEmptyStringValue_throw(const Reference<XPropertySet> & _rxDescriptor,const Reference<XPropertySetInfo> & rxPSI,const OUString & _rPropertyName)672     bool lcl_hasNonEmptyStringValue_throw( const Reference< XPropertySet >& _rxDescriptor,
673         const Reference< XPropertySetInfo >& rxPSI, const OUString& _rPropertyName )
674     {
675         OUString sValue;
676         if ( rxPSI->hasPropertyByName( _rPropertyName ) )
677         {
678             OSL_VERIFY( _rxDescriptor->getPropertyValue( _rPropertyName ) >>= sValue );
679         }
680         return !sValue.isEmpty();
681     }
682 }
683 
impl_checkForUnsupportedSettings_throw(const Reference<XPropertySet> & _rxSourceDescriptor) const684 void CopyTableWizard::impl_checkForUnsupportedSettings_throw( const Reference< XPropertySet >& _rxSourceDescriptor ) const
685 {
686     OSL_PRECOND( _rxSourceDescriptor.is(), "CopyTableWizard::impl_checkForUnsupportedSettings_throw: illegal argument!" );
687     Reference< XPropertySetInfo > xPSI( _rxSourceDescriptor->getPropertySetInfo(), UNO_SET_THROW );
688     OUString sUnsupportedSetting;
689 
690     const OUString aSettings[] = {
691         OUString(PROPERTY_FILTER), OUString(PROPERTY_ORDER), OUString(PROPERTY_HAVING_CLAUSE), OUString(PROPERTY_GROUP_BY)
692     };
693     for (const auto & aSetting : aSettings)
694     {
695         if ( lcl_hasNonEmptyStringValue_throw( _rxSourceDescriptor, xPSI, aSetting ) )
696         {
697             sUnsupportedSetting = aSetting;
698             break;
699         }
700     }
701 
702     if ( !sUnsupportedSetting.isEmpty() )
703     {
704         OUString sMessage(
705             DBA_RES(STR_CTW_ERROR_UNSUPPORTED_SETTING).
706             replaceFirst("$name$", sUnsupportedSetting));
707         throw IllegalArgumentException(
708             sMessage,
709             *const_cast< CopyTableWizard* >( this ),
710             1
711         );
712     }
713 
714 }
715 
impl_extractSourceObject_throw(const Reference<XPropertySet> & _rxDescriptor,sal_Int32 & _out_rCommandType) const716 std::unique_ptr< ICopyTableSourceObject > CopyTableWizard::impl_extractSourceObject_throw( const Reference< XPropertySet >& _rxDescriptor, sal_Int32& _out_rCommandType ) const
717 {
718     OSL_PRECOND( _rxDescriptor.is() && m_xSourceConnection.is(), "CopyTableWizard::impl_extractSourceObject_throw: illegal arguments!" );
719 
720     Reference< XPropertySetInfo > xPSI( _rxDescriptor->getPropertySetInfo(), UNO_SET_THROW );
721     if  (   !xPSI->hasPropertyByName( PROPERTY_COMMAND )
722         ||  !xPSI->hasPropertyByName( PROPERTY_COMMAND_TYPE )
723         )
724         throw IllegalArgumentException("Expecting a table or query specification.",
725                                        // TODO: resource
726                                        *const_cast< CopyTableWizard* >( this ), 1);
727 
728     OUString sCommand;
729     _out_rCommandType = CommandType::COMMAND;
730     OSL_VERIFY( _rxDescriptor->getPropertyValue( PROPERTY_COMMAND ) >>= sCommand );
731     OSL_VERIFY( _rxDescriptor->getPropertyValue( PROPERTY_COMMAND_TYPE ) >>= _out_rCommandType );
732 
733     std::unique_ptr< ICopyTableSourceObject > pSourceObject;
734     Reference< XNameAccess > xContainer;
735     switch ( _out_rCommandType )
736     {
737     case CommandType::TABLE:
738     {
739         Reference< XTablesSupplier > xSuppTables( m_xSourceConnection.getTyped(), UNO_QUERY );
740         if ( xSuppTables.is() )
741             xContainer.set( xSuppTables->getTables(), UNO_SET_THROW );
742     }
743     break;
744     case CommandType::QUERY:
745     {
746         Reference< XQueriesSupplier > xSuppQueries( m_xSourceConnection.getTyped(), UNO_QUERY );
747         if ( xSuppQueries.is() )
748             xContainer.set( xSuppQueries->getQueries(), UNO_SET_THROW );
749     }
750     break;
751     default:
752         throw IllegalArgumentException(
753             DBA_RES( STR_CTW_ONLY_TABLES_AND_QUERIES_SUPPORT ),
754             *const_cast< CopyTableWizard* >( this ),
755             1
756         );
757     }
758 
759     if ( xContainer.is() )
760     {
761         pSourceObject.reset( new ObjectCopySource( m_xSourceConnection,
762             Reference< XPropertySet >( xContainer->getByName( sCommand ), UNO_QUERY_THROW ) ) );
763     }
764     else
765     {
766         // our source connection is an SDBC level connection only, not a SDBCX level one
767         // Which means it cannot provide the to-be-copied object as component.
768 
769         if ( _out_rCommandType == CommandType::QUERY )
770             // we cannot copy a query if the connection cannot provide it ...
771             throw IllegalArgumentException(
772                 DBA_RES( STR_CTW_ERROR_NO_QUERY ),
773                 *const_cast< CopyTableWizard* >( this ),
774                 1
775             );
776         pSourceObject.reset( new NamedTableCopySource( m_xSourceConnection, sCommand ) );
777     }
778 
779     return pSourceObject;
780 }
781 
impl_extractSourceResultSet_throw(const Reference<XPropertySet> & i_rDescriptor)782 void CopyTableWizard::impl_extractSourceResultSet_throw( const Reference< XPropertySet >& i_rDescriptor )
783 {
784     Reference< XPropertySetInfo > xPSI( i_rDescriptor->getPropertySetInfo(), UNO_SET_THROW );
785 
786     // extract relevant settings
787     if ( xPSI->hasPropertyByName( PROPERTY_RESULT_SET ) )
788         m_xSourceResultSet.set( i_rDescriptor->getPropertyValue( PROPERTY_RESULT_SET ), UNO_QUERY );
789 
790     if ( xPSI->hasPropertyByName( PROPERTY_SELECTION ) )
791         OSL_VERIFY( i_rDescriptor->getPropertyValue( PROPERTY_SELECTION ) >>= m_aSourceSelection );
792 
793     if ( xPSI->hasPropertyByName( PROPERTY_BOOKMARK_SELECTION ) )
794         OSL_VERIFY( i_rDescriptor->getPropertyValue( PROPERTY_BOOKMARK_SELECTION ) >>= m_bSourceSelectionBookmarks );
795 
796     // sanity checks
797     const bool bHasResultSet = m_xSourceResultSet.is();
798     const bool bHasSelection = m_aSourceSelection.hasElements();
799     if ( bHasSelection && !bHasResultSet )
800         throw IllegalArgumentException("A result set is needed when specifying a selection to copy.",
801                                        // TODO: resource
802                                        *this, 1);
803 
804     if ( bHasSelection && m_bSourceSelectionBookmarks )
805     {
806         Reference< XRowLocate > xRowLocate( m_xSourceResultSet, UNO_QUERY );
807         if ( !xRowLocate.is() )
808         {
809             ::dbtools::throwGenericSQLException(
810                 DBA_RES(STR_CTW_COPY_SOURCE_NEEDS_BOOKMARKS),
811                 *this
812             );
813         }
814     }
815 }
816 
impl_extractConnection_throw(const Reference<XPropertySet> & _rxDataSourceDescriptor,Reference<XInteractionHandler> & _out_rxDocInteractionHandler) const817 SharedConnection CopyTableWizard::impl_extractConnection_throw( const Reference< XPropertySet >& _rxDataSourceDescriptor,
818     Reference< XInteractionHandler >& _out_rxDocInteractionHandler ) const
819 {
820     SharedConnection xConnection;
821 
822     OSL_PRECOND( _rxDataSourceDescriptor.is(), "CopyTableWizard::impl_extractConnection_throw: no descriptor!" );
823     if ( !_rxDataSourceDescriptor.is() )
824         return xConnection;
825 
826     Reference< XInteractionHandler > xInteractionHandler;
827 
828     do
829     {
830     Reference< XPropertySetInfo > xPSI( _rxDataSourceDescriptor->getPropertySetInfo(), UNO_SET_THROW );
831 
832     // if there's an ActiveConnection, use it
833     if ( xPSI->hasPropertyByName( PROPERTY_ACTIVE_CONNECTION ) )
834     {
835         Reference< XConnection > xPure;
836         OSL_VERIFY( _rxDataSourceDescriptor->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ) >>= xPure );
837         xConnection.reset( xPure, SharedConnection::NoTakeOwnership );
838     }
839     if ( xConnection.is() )
840     {
841         xInteractionHandler = lcl_getInteractionHandler_throw( xConnection.getTyped(), m_xInteractionHandler );
842         SAL_WARN_IF( !xInteractionHandler.is(), "dbaccess.ui", "CopyTableWizard::impl_extractConnection_throw: lcl_getInteractionHandler_throw returned nonsense!" );
843         break;
844     }
845 
846     // there could be a DataSourceName or a DatabaseLocation, describing the css.sdb.DataSource
847     OUString sDataSource, sDatabaseLocation;
848     if ( xPSI->hasPropertyByName( PROPERTY_DATASOURCENAME ) )
849         OSL_VERIFY( _rxDataSourceDescriptor->getPropertyValue( PROPERTY_DATASOURCENAME ) >>= sDataSource );
850     if ( xPSI->hasPropertyByName( PROPERTY_DATABASE_LOCATION ) )
851         OSL_VERIFY( _rxDataSourceDescriptor->getPropertyValue( PROPERTY_DATABASE_LOCATION ) >>= sDatabaseLocation );
852 
853     // need a DatabaseContext for loading the data source
854     Reference< XDatabaseContext > xDatabaseContext = DatabaseContext::create( m_xContext );
855     Reference< XDataSource > xDataSource;
856     if ( !sDataSource.isEmpty() )
857         xDataSource.set( xDatabaseContext->getByName( sDataSource ), UNO_QUERY_THROW );
858     if ( !xDataSource.is() && !sDatabaseLocation.isEmpty() )
859         xDataSource.set( xDatabaseContext->getByName( sDatabaseLocation ), UNO_QUERY_THROW );
860 
861     if ( xDataSource.is() )
862     {
863         // first, try connecting with completion
864         xInteractionHandler = lcl_getInteractionHandler_throw( xDataSource, m_xInteractionHandler );
865         SAL_WARN_IF( !xInteractionHandler.is(), "dbaccess.ui", "CopyTableWizard::impl_extractConnection_throw: lcl_getInteractionHandler_throw returned nonsense!" );
866         if ( xInteractionHandler.is() )
867         {
868             Reference< XCompletedConnection > xInteractiveConnection( xDataSource, UNO_QUERY );
869             if ( xInteractiveConnection.is() )
870                 xConnection.reset( xInteractiveConnection->connectWithCompletion( xInteractionHandler ), SharedConnection::TakeOwnership );
871         }
872 
873         // interactively connecting was not successful or possible -> connect without interaction
874         if ( !xConnection.is() )
875         {
876             xConnection.reset( xDataSource->getConnection( OUString(), OUString() ), SharedConnection::TakeOwnership );
877         }
878     }
879 
880     if ( xConnection.is() )
881         break;
882 
883     // finally, there could be a ConnectionResource/ConnectionInfo
884     OUString sConnectionResource;
885     Sequence< PropertyValue > aConnectionInfo;
886     if ( xPSI->hasPropertyByName( PROPERTY_CONNECTION_RESOURCE ) )
887         OSL_VERIFY( _rxDataSourceDescriptor->getPropertyValue( PROPERTY_CONNECTION_RESOURCE ) >>= sConnectionResource );
888     if ( xPSI->hasPropertyByName( PROPERTY_CONNECTION_INFO ) )
889         OSL_VERIFY( _rxDataSourceDescriptor->getPropertyValue( PROPERTY_CONNECTION_INFO ) >>= aConnectionInfo );
890 
891     Reference< XDriverManager > xDriverManager;
892     try {
893         xDriverManager.set( ConnectionPool::create( m_xContext ), UNO_QUERY_THROW );
894     } catch( const Exception& ) {  }
895     if ( !xDriverManager.is() )
896         // no connection pool installed
897         xDriverManager.set( DriverManager::create( m_xContext ), UNO_QUERY_THROW );
898 
899     if ( aConnectionInfo.hasElements() )
900         xConnection.set( xDriverManager->getConnectionWithInfo( sConnectionResource, aConnectionInfo ), UNO_SET_THROW );
901     else
902         xConnection.set( xDriverManager->getConnection( sConnectionResource ), UNO_SET_THROW );
903     }
904     while ( false );
905 
906     if ( xInteractionHandler != m_xInteractionHandler )
907         _out_rxDocInteractionHandler = xInteractionHandler;
908 
909     return xConnection;
910 }
911 
impl_createSourceStatement_throw() const912 ::utl::SharedUNOComponent< XPreparedStatement > CopyTableWizard::impl_createSourceStatement_throw() const
913 {
914     OSL_PRECOND( m_xSourceConnection.is(), "CopyTableWizard::impl_createSourceStatement_throw: illegal call!" );
915     if ( !m_xSourceConnection.is() )
916         throw RuntimeException( "CopyTableWizard::impl_createSourceStatement_throw: illegal call!", *const_cast< CopyTableWizard* >( this ));
917 
918     ::utl::SharedUNOComponent< XPreparedStatement > xStatement;
919     switch ( m_nCommandType )
920     {
921     case CommandType::TABLE:
922         xStatement.set( m_pSourceObject->getPreparedSelectStatement(), UNO_SET_THROW );
923         break;
924 
925     case CommandType::QUERY:
926     {
927         OUString sQueryCommand( m_pSourceObject->getSelectStatement() );
928         xStatement.set( m_pSourceObject->getPreparedSelectStatement(), UNO_SET_THROW );
929 
930         // check whether we have to fill in parameter values
931         // create and fill a composer
932 
933         Reference< XMultiServiceFactory > xFactory( m_xSourceConnection, UNO_QUERY );
934         ::utl::SharedUNOComponent< XSingleSelectQueryComposer > xComposer;
935         if ( xFactory.is() )
936             // note: connections below the sdb-level are allowed to not support the XMultiServiceFactory interface
937             xComposer.set( xFactory->createInstance( SERVICE_NAME_SINGLESELECTQUERYCOMPOSER ), UNO_QUERY );
938 
939         if ( xComposer.is() )
940         {
941             xComposer->setQuery( sQueryCommand );
942 
943             Reference< XParameters > xStatementParams( xStatement, UNO_QUERY );
944             OSL_ENSURE( xStatementParams.is(), "CopyTableWizard::impl_createSourceStatement_throw: no access to the statement's parameters!" );
945                 // the statement should be a css.sdbc.PreparedStatement (this is what
946                 // we created), and a prepared statement is required to support XParameters
947             if ( xStatementParams.is() )
948             {
949                 OSL_ENSURE( m_xInteractionHandler.is(),
950                    "CopyTableWizard::impl_createSourceStatement_throw: no interaction handler for the parameters request!" );
951                 // we should always have an interaction handler - as last fallback, we create an own one in ::initialize
952 
953                 if ( m_xInteractionHandler.is() )
954                     ::dbtools::askForParameters( xComposer, xStatementParams, m_xSourceConnection, m_xInteractionHandler );
955             }
956         }
957     }
958     break;
959 
960     default:
961         // this should not have survived initialization phase
962         throw RuntimeException("No case matched, this should not have survived the initialization phase", *const_cast< CopyTableWizard* >( this ));
963     }
964 
965     return xStatement;
966 }
967 
968 namespace
969 {
970     class ValueTransfer
971     {
972     public:
ValueTransfer(const sal_Int32 & _rSourcePos,const sal_Int32 & _rDestPos,const std::vector<sal_Int32> & _rColTypes,const Reference<XRow> & _rxSource,const Reference<XParameters> & _rxDest)973         ValueTransfer( const sal_Int32& _rSourcePos, const sal_Int32& _rDestPos, const std::vector< sal_Int32 >& _rColTypes,
974             const Reference< XRow >& _rxSource, const Reference< XParameters >& _rxDest )
975             :m_rSourcePos( _rSourcePos )
976             ,m_rDestPos( _rDestPos )
977             ,m_rColTypes( _rColTypes )
978             ,m_xSource( _rxSource )
979             ,m_xDest( _rxDest )
980         {
981         }
982 
983     template< typename VALUE_TYPE >
transferValue(VALUE_TYPE (SAL_CALL XRow::* _pGetter)(sal_Int32),void (SAL_CALL XParameters::* _pSetter)(sal_Int32,VALUE_TYPE))984     void transferValue( VALUE_TYPE ( SAL_CALL XRow::*_pGetter )( sal_Int32 ),
985         void (SAL_CALL XParameters::*_pSetter)( sal_Int32, VALUE_TYPE ) )
986     {
987         VALUE_TYPE value( (m_xSource.get()->*_pGetter)( m_rSourcePos ) );
988         if ( m_xSource->wasNull() )
989             m_xDest->setNull( m_rDestPos, m_rColTypes[ m_rSourcePos ] );
990         else
991             (m_xDest.get()->*_pSetter)( m_rDestPos, value );
992     }
993  template< typename VALUE_TYPE >
transferComplexValue(VALUE_TYPE (SAL_CALL XRow::* _pGetter)(sal_Int32),void (SAL_CALL XParameters::* _pSetter)(sal_Int32,const VALUE_TYPE &))994     void transferComplexValue( VALUE_TYPE ( SAL_CALL XRow::*_pGetter )( sal_Int32 ),
995         void (SAL_CALL XParameters::*_pSetter)( sal_Int32, const VALUE_TYPE& ) )
996     {
997         const VALUE_TYPE value( (m_xSource.get()->*_pGetter)( m_rSourcePos ) );
998         if ( m_xSource->wasNull() )
999             m_xDest->setNull( m_rDestPos, m_rColTypes[ m_rSourcePos ] );
1000         else
1001             (m_xDest.get()->*_pSetter)( m_rDestPos, value );
1002     }
1003     private:
1004         const sal_Int32&                    m_rSourcePos;
1005         const sal_Int32&                    m_rDestPos;
1006         const std::vector< sal_Int32 >    m_rColTypes;
1007         const Reference< XRow >             m_xSource;
1008         const Reference< XParameters >      m_xDest;
1009     };
1010 }
1011 
impl_processCopyError_nothrow(const CopyTableRowEvent & _rEvent)1012 bool CopyTableWizard::impl_processCopyError_nothrow( const CopyTableRowEvent& _rEvent )
1013 {
1014     Reference< XCopyTableListener > xListener;
1015     try
1016     {
1017         ::comphelper::OInterfaceIteratorHelper2 aIter( m_aCopyTableListeners );
1018         while ( aIter.hasMoreElements() )
1019         {
1020             xListener.set( aIter.next(), UNO_QUERY_THROW );
1021             sal_Int16 nListenerChoice = xListener->copyRowError( _rEvent );
1022             switch ( nListenerChoice )
1023             {
1024             case CopyTableContinuation::Proceed:            return true;    // continue copying
1025             case CopyTableContinuation::CallNextHandler:    continue;       // continue the loop, ask next listener
1026             case CopyTableContinuation::Cancel:             return false;   // cancel copying
1027             case CopyTableContinuation::AskUser:            break;          // stop asking the listeners, ask the user
1028 
1029             default:
1030                 SAL_WARN("dbaccess.ui", "CopyTableWizard::impl_processCopyError_nothrow: invalid listener response!" );
1031                 // ask next listener
1032                 continue;
1033             }
1034         }
1035     }
1036     catch( const Exception& )
1037     {
1038         DBG_UNHANDLED_EXCEPTION("dbaccess");
1039     }
1040 
1041     // no listener felt responsible for the error, or a listener told to ask the user
1042 
1043     try
1044     {
1045         SQLContext aError;
1046         aError.Context = *this;
1047         aError.Message = DBA_RES(STR_ERROR_OCCURRED_WHILE_COPYING);
1048 
1049         ::dbtools::SQLExceptionInfo aInfo( _rEvent.Error );
1050         if ( aInfo.isValid() )
1051             aError.NextException = _rEvent.Error;
1052         else
1053         {
1054             // a non-SQL exception happened
1055             Exception aException;
1056             OSL_VERIFY( _rEvent.Error >>= aException );
1057             SQLContext aContext;
1058             aContext.Context = aException.Context;
1059             aContext.Message = aException.Message;
1060             aContext.Details = _rEvent.Error.getValueTypeName();
1061             aError.NextException <<= aContext;
1062         }
1063 
1064         ::rtl::Reference< ::comphelper::OInteractionRequest > xRequest( new ::comphelper::OInteractionRequest( makeAny( aError ) ) );
1065 
1066         ::rtl::Reference< ::comphelper::OInteractionApprove > xYes = new ::comphelper::OInteractionApprove;
1067         xRequest->addContinuation( xYes.get() );
1068         xRequest->addContinuation( new ::comphelper::OInteractionDisapprove );
1069 
1070         OSL_ENSURE( m_xInteractionHandler.is(),
1071             "CopyTableWizard::impl_processCopyError_nothrow: we always should have an interaction handler!" );
1072         if ( m_xInteractionHandler.is() )
1073             m_xInteractionHandler->handle( xRequest.get() );
1074 
1075         if ( xYes->wasSelected() )
1076             // continue copying
1077             return true;
1078     }
1079     catch( const Exception& )
1080     {
1081         DBG_UNHANDLED_EXCEPTION("dbaccess");
1082     }
1083 
1084     // cancel copying
1085     return false;
1086 }
1087 
impl_copyRows_throw(const Reference<XResultSet> & _rxSourceResultSet,const Reference<XPropertySet> & _rxDestTable)1088 void CopyTableWizard::impl_copyRows_throw( const Reference< XResultSet >& _rxSourceResultSet,
1089     const Reference< XPropertySet >& _rxDestTable )
1090 {
1091     OSL_PRECOND( m_xDestConnection.is(), "CopyTableWizard::impl_copyRows_throw: illegal call!" );
1092     if ( !m_xDestConnection.is() )
1093         throw RuntimeException( "m_xDestConnection is set to null, CopyTableWizard::impl_copyRows_throw: illegal call!", *this );
1094 
1095     Reference< XDatabaseMetaData > xDestMetaData( m_xDestConnection->getMetaData(), UNO_SET_THROW );
1096 
1097     const OCopyTableWizard& rWizard             = impl_getDialog_throw();
1098     ODatabaseExport::TPositions aColumnPositions = rWizard.GetColumnPositions();
1099 
1100     Reference< XRow > xRow              ( _rxSourceResultSet, UNO_QUERY_THROW );
1101     Reference< XRowLocate > xRowLocate  ( _rxSourceResultSet, UNO_QUERY_THROW );
1102 
1103     Reference< XResultSetMetaDataSupplier > xSuppResMeta( _rxSourceResultSet, UNO_QUERY_THROW );
1104     Reference< XResultSetMetaData> xMeta( xSuppResMeta->getMetaData() );
1105 
1106     // we need a vector which all types
1107     sal_Int32 nCount = xMeta->getColumnCount();
1108     std::vector< sal_Int32 > aSourceColTypes;
1109     aSourceColTypes.reserve( nCount + 1 );
1110     aSourceColTypes.push_back( -1 ); // just to avoid an every time i-1 call
1111 
1112     std::vector< sal_Int32 > aSourcePrec;
1113     aSourcePrec.reserve( nCount + 1 );
1114     aSourcePrec.push_back( -1 ); // just to avoid an every time i-1 call
1115 
1116     for ( sal_Int32 k=1; k <= nCount; ++k )
1117     {
1118         aSourceColTypes.push_back( xMeta->getColumnType( k ) );
1119         aSourcePrec.push_back( xMeta->getPrecision( k ) );
1120     }
1121 
1122     // now create, fill and execute the prepared statement
1123     Reference< XPreparedStatement > xStatement( ODatabaseExport::createPreparedStatment( xDestMetaData, _rxDestTable, aColumnPositions ), UNO_SET_THROW );
1124     Reference< XParameters > xStatementParams( xStatement, UNO_QUERY_THROW );
1125 
1126     const bool bSelectedRecordsOnly = m_aSourceSelection.hasElements();
1127     const Any* pSelectedRow         = m_aSourceSelection.getConstArray();
1128     const Any* pSelEnd              = pSelectedRow + m_aSourceSelection.getLength();
1129 
1130     sal_Int32 nRowCount = 0;
1131     bool bContinue = false;
1132 
1133     CopyTableRowEvent aCopyEvent;
1134     aCopyEvent.Source = *this;
1135     aCopyEvent.SourceData = _rxSourceResultSet;
1136 
1137     do // loop as long as there are more rows or the selection ends
1138     {
1139         bContinue = false;
1140         if ( bSelectedRecordsOnly )
1141         {
1142             if ( pSelectedRow != pSelEnd )
1143             {
1144                 if ( m_bSourceSelectionBookmarks )
1145                 {
1146                     bContinue = xRowLocate->moveToBookmark( *pSelectedRow );
1147                 }
1148                 else
1149                 {
1150                     sal_Int32 nPos = 0;
1151                     OSL_VERIFY( *pSelectedRow >>= nPos );
1152                     bContinue = _rxSourceResultSet->absolute( nPos );
1153                 }
1154                 ++pSelectedRow;
1155             }
1156         }
1157         else
1158             bContinue = _rxSourceResultSet->next();
1159 
1160         if ( !bContinue )
1161         {
1162             break;
1163         }
1164 
1165         ++nRowCount;
1166 
1167         aCopyEvent.Error.clear();
1168         try
1169         {
1170             // notify listeners
1171             m_aCopyTableListeners.notifyEach( &XCopyTableListener::copyingRow, aCopyEvent );
1172 
1173             sal_Int32 nDestColumn( 0 );
1174             sal_Int32 nSourceColumn( 1 );
1175             ValueTransfer aTransfer( nSourceColumn, nDestColumn, aSourceColTypes, xRow, xStatementParams );
1176 
1177             for ( auto const& rColumnPos : aColumnPositions )
1178             {
1179                 nDestColumn = rColumnPos.first;
1180                 if ( nDestColumn == COLUMN_POSITION_NOT_FOUND )
1181                 {
1182                     ++nSourceColumn;
1183                     // otherwise we don't get the correct value when only the 2nd source column was selected
1184                     continue;
1185                 }
1186 
1187                 if ( ( nSourceColumn < 1 ) || ( nSourceColumn >= static_cast<sal_Int32>(aSourceColTypes.size()) ) )
1188                 {   // ( we have to check here against 1 because the parameters are 1 based)
1189                     ::dbtools::throwSQLException("Internal error: invalid column type index.",
1190                                                  ::dbtools::StandardSQLState::INVALID_DESCRIPTOR_INDEX, *this);
1191                 }
1192 
1193                 switch ( aSourceColTypes[ nSourceColumn ] )
1194                 {
1195                     case DataType::DOUBLE:
1196                     case DataType::REAL:
1197                         aTransfer.transferValue( &XRow::getDouble, &XParameters::setDouble );
1198                         break;
1199 
1200                     case DataType::CHAR:
1201                     case DataType::VARCHAR:
1202                     case DataType::LONGVARCHAR:
1203                     case DataType::DECIMAL:
1204                     case DataType::NUMERIC:
1205                         aTransfer.transferComplexValue( &XRow::getString, &XParameters::setString );
1206                         break;
1207 
1208                     case DataType::BIGINT:
1209                         aTransfer.transferValue( &XRow::getLong, &XParameters::setLong );
1210                         break;
1211 
1212                     case DataType::FLOAT:
1213                         aTransfer.transferValue( &XRow::getFloat, &XParameters::setFloat );
1214                         break;
1215 
1216                     case DataType::LONGVARBINARY:
1217                     case DataType::BINARY:
1218                     case DataType::VARBINARY:
1219                         aTransfer.transferComplexValue( &XRow::getBytes, &XParameters::setBytes );
1220                         break;
1221 
1222                     case DataType::DATE:
1223                         aTransfer.transferComplexValue( &XRow::getDate, &XParameters::setDate );
1224                         break;
1225 
1226                     case DataType::TIME:
1227                         aTransfer.transferComplexValue( &XRow::getTime, &XParameters::setTime );
1228                         break;
1229 
1230                     case DataType::TIMESTAMP:
1231                         aTransfer.transferComplexValue( &XRow::getTimestamp, &XParameters::setTimestamp );
1232                         break;
1233 
1234                     case DataType::BIT:
1235                         if ( aSourcePrec[nSourceColumn] > 1 )
1236                         {
1237                             aTransfer.transferComplexValue( &XRow::getBytes, &XParameters::setBytes );
1238                             break;
1239                         }
1240                         [[fallthrough]];
1241                     case DataType::BOOLEAN:
1242                         aTransfer.transferValue( &XRow::getBoolean, &XParameters::setBoolean );
1243                         break;
1244 
1245                     case DataType::TINYINT:
1246                         aTransfer.transferValue( &XRow::getByte, &XParameters::setByte );
1247                         break;
1248 
1249                     case DataType::SMALLINT:
1250                         aTransfer.transferValue( &XRow::getShort, &XParameters::setShort );
1251                         break;
1252 
1253                     case DataType::INTEGER:
1254                         aTransfer.transferValue( &XRow::getInt, &XParameters::setInt );
1255                         break;
1256 
1257                     case DataType::BLOB:
1258                         aTransfer.transferComplexValue( &XRow::getBlob, &XParameters::setBlob );
1259                         break;
1260 
1261                     case DataType::CLOB:
1262                         aTransfer.transferComplexValue( &XRow::getClob, &XParameters::setClob );
1263                         break;
1264 
1265                     default:
1266                     {
1267                         OUString aMessage( DBA_RES( STR_CTW_UNSUPPORTED_COLUMN_TYPE ) );
1268 
1269                         aMessage = aMessage.replaceFirst( "$type$", OUString::number( aSourceColTypes[ nSourceColumn ] ) );
1270                         aMessage = aMessage.replaceFirst( "$pos$", OUString::number( nSourceColumn ) );
1271 
1272                         ::dbtools::throwSQLException(
1273                             aMessage,
1274                             ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
1275                             *this
1276                         );
1277                     }
1278                 }
1279                 ++nSourceColumn;
1280             }
1281             xStatement->executeUpdate();
1282 
1283             // notify listeners
1284             m_aCopyTableListeners.notifyEach( &XCopyTableListener::copiedRow, aCopyEvent );
1285         }
1286         catch( const Exception& )
1287         {
1288             aCopyEvent.Error = ::cppu::getCaughtException();
1289         }
1290 
1291         if ( aCopyEvent.Error.hasValue() )
1292             bContinue = impl_processCopyError_nothrow( aCopyEvent );
1293     }
1294     while( bContinue );
1295 }
1296 
impl_doCopy_nothrow()1297 void CopyTableWizard::impl_doCopy_nothrow()
1298 {
1299     Any aError;
1300 
1301     try
1302     {
1303         OCopyTableWizard& rWizard( impl_getDialog_throw() );
1304 
1305         weld::WaitObject aWO(rWizard.getDialog());
1306         Reference< XPropertySet > xTable;
1307 
1308         switch ( rWizard.getOperation() )
1309         {
1310             case CopyTableOperation::CopyDefinitionOnly:
1311             case CopyTableOperation::CopyDefinitionAndData:
1312             {
1313                 xTable = rWizard.createTable();
1314 
1315                 if( !xTable.is() )
1316                 {
1317                     SAL_WARN("dbaccess.ui", "CopyTableWizard::impl_doCopy_nothrow: createTable should throw here, shouldn't it?" );
1318                     break;
1319                 }
1320 
1321                 if( CopyTableOperation::CopyDefinitionOnly == rWizard.getOperation() )
1322                     break;
1323 
1324                 [[fallthrough]];
1325             }
1326 
1327             case CopyTableOperation::AppendData:
1328             {
1329 
1330                 if ( !xTable.is() )
1331                 {
1332                     xTable = rWizard.createTable();
1333                     if ( !xTable.is() )
1334                     {
1335                         SAL_WARN("dbaccess.ui", "CopyTableWizard::impl_doCopy_nothrow: createTable should throw here, shouldn't it?" );
1336                         break;
1337                     }
1338                 }
1339 
1340                 ::utl::SharedUNOComponent< XPreparedStatement > xSourceStatement;
1341                 ::utl::SharedUNOComponent< XResultSet > xSourceResultSet;
1342 
1343                 if ( m_xSourceResultSet.is() )
1344                 {
1345                     xSourceResultSet.reset( m_xSourceResultSet, ::utl::SharedUNOComponent< XResultSet >::NoTakeOwnership );
1346                 }
1347                 else
1348                 {
1349                     const bool bIsSameConnection = ( m_xSourceConnection.getTyped() == m_xDestConnection.getTyped() );
1350                     const bool bIsTable = ( CommandType::TABLE == m_nCommandType );
1351                     bool bDone = false;
1352                     if ( bIsSameConnection && bIsTable )
1353                     {
1354                         // try whether the server supports copying via SQL
1355                         try
1356                         {
1357                             m_xDestConnection->createStatement()->executeUpdate( impl_getServerSideCopyStatement_throw(xTable) );
1358                             bDone = true;
1359                         }
1360                         catch( const Exception& )
1361                         {
1362                             // this is allowed.
1363                         }
1364                     }
1365 
1366                     if ( !bDone )
1367                     {
1368                         xSourceStatement.set( impl_createSourceStatement_throw(), UNO_SET_THROW );
1369                         xSourceResultSet.set( xSourceStatement->executeQuery(), UNO_SET_THROW );
1370                     }
1371                 }
1372 
1373                 if ( xSourceResultSet.is() )
1374                     impl_copyRows_throw( xSourceResultSet, xTable );
1375             }
1376             break;
1377 
1378             case CopyTableOperation::CreateAsView:
1379                 rWizard.createView();
1380                 break;
1381 
1382             default:
1383                 SAL_WARN("dbaccess.ui", "CopyTableWizard::impl_doCopy_nothrow: What operation, please?" );
1384                 break;
1385         }
1386     }
1387     catch( const Exception& )
1388     {
1389         aError = ::cppu::getCaughtException();
1390 
1391         // silence the error of the user cancelling the parameter's dialog
1392         SQLException aSQLError;
1393         if ( ( aError >>= aSQLError ) && ( aSQLError.ErrorCode == ::dbtools::ParameterInteractionCancelled ) )
1394         {
1395             aError.clear();
1396             m_nOverrideExecutionResult = RET_CANCEL;
1397         }
1398     }
1399 
1400     if ( aError.hasValue() && m_xInteractionHandler.is() )
1401     {
1402         try
1403         {
1404             ::rtl::Reference< ::comphelper::OInteractionRequest > xRequest( new ::comphelper::OInteractionRequest( aError ) );
1405             m_xInteractionHandler->handle( xRequest.get() );
1406         }
1407         catch( const Exception& )
1408         {
1409             DBG_UNHANDLED_EXCEPTION("dbaccess");
1410         }
1411     }
1412 }
1413 
impl_getServerSideCopyStatement_throw(const Reference<XPropertySet> & _xTable)1414 OUString CopyTableWizard::impl_getServerSideCopyStatement_throw(const Reference< XPropertySet >& _xTable)
1415 {
1416     const Reference<XColumnsSupplier> xDestColsSup(_xTable,UNO_QUERY_THROW);
1417     const Sequence< OUString> aDestColumnNames = xDestColsSup->getColumns()->getElementNames();
1418     const Reference< XDatabaseMetaData > xDestMetaData( m_xDestConnection->getMetaData(), UNO_SET_THROW );
1419     const OUString sQuote = xDestMetaData->getIdentifierQuoteString();
1420     OUStringBuffer sColumns;
1421     // 1st check if the columns matching
1422     for ( auto const & rColumnPositionPair : impl_getDialog_throw().GetColumnPositions() )
1423     {
1424         if ( COLUMN_POSITION_NOT_FOUND != rColumnPositionPair.second )
1425         {
1426             if ( !sColumns.isEmpty() )
1427                 sColumns.append(",");
1428             sColumns.append(sQuote).append(aDestColumnNames[rColumnPositionPair.second - 1]).append(sQuote);
1429         }
1430     }
1431     const OUString sComposedTableName = ::dbtools::composeTableName( xDestMetaData, _xTable, ::dbtools::EComposeRule::InDataManipulation, true );
1432     OUString sSql("INSERT INTO " + sComposedTableName + " ( " + sColumns.makeStringAndClear() + " ) " + m_pSourceObject->getSelectStatement());
1433 
1434     return sSql;
1435 }
1436 
initialize(const Sequence<Any> & _rArguments)1437 void SAL_CALL CopyTableWizard::initialize( const Sequence< Any >& _rArguments )
1438 {
1439     ::osl::MutexGuard aGuard( m_aMutex );
1440     if ( isInitialized() )
1441         throw AlreadyInitializedException( OUString(), *this );
1442 
1443     sal_Int32 nArgCount( _rArguments.getLength() );
1444     if ( ( nArgCount != 2 ) && ( nArgCount != 3 ) )
1445         throw IllegalArgumentException(
1446             DBA_RES( STR_CTW_ILLEGAL_PARAMETER_COUNT ),
1447             *this,
1448             1
1449         );
1450 
1451     try
1452     {
1453         if ( nArgCount == 3 )
1454         {   // ->createWithInteractionHandler
1455             if ( !( _rArguments[2] >>= m_xInteractionHandler ) )
1456                 throw IllegalArgumentException(
1457                     DBA_RES( STR_CTW_ERROR_INVALID_INTERACTIONHANDLER ),
1458                     *this,
1459                     3
1460                 );
1461         }
1462         if ( !m_xInteractionHandler.is() )
1463             m_xInteractionHandler = InteractionHandler::createWithParent(m_xContext, nullptr);
1464 
1465         Reference< XInteractionHandler > xSourceDocHandler;
1466         Reference< XPropertySet > xSourceDescriptor( impl_ensureDataAccessDescriptor_throw( _rArguments, 0, m_xSourceConnection, xSourceDocHandler ) );
1467         impl_checkForUnsupportedSettings_throw( xSourceDescriptor );
1468         m_pSourceObject = impl_extractSourceObject_throw( xSourceDescriptor, m_nCommandType );
1469         impl_extractSourceResultSet_throw( xSourceDescriptor );
1470 
1471         Reference< XInteractionHandler > xDestDocHandler;
1472         impl_ensureDataAccessDescriptor_throw( _rArguments, 1, m_xDestConnection, xDestDocHandler );
1473 
1474         if ( xDestDocHandler.is() && !m_xInteractionHandler.is() )
1475             m_xInteractionHandler = xDestDocHandler;
1476 
1477         Reference< XPropertySet > xInteractionHandler(m_xInteractionHandler, UNO_QUERY);
1478         if (xInteractionHandler.is())
1479         {
1480             Any aParentWindow(xInteractionHandler->getPropertyValue("ParentWindow"));
1481             aParentWindow >>= m_xParent;
1482         }
1483     }
1484     catch( const RuntimeException& ) { throw; }
1485     catch( const SQLException& ) { throw; }
1486     catch( const Exception& )
1487     {
1488         throw WrappedTargetException(
1489             DBA_RES( STR_CTW_ERROR_DURING_INITIALIZATION ),
1490             *this,
1491             ::cppu::getCaughtException()
1492         );
1493     }
1494 }
1495 
getInfoHelper()1496 ::cppu::IPropertyArrayHelper& CopyTableWizard::getInfoHelper()
1497 {
1498     return *getArrayHelper();
1499 }
1500 
createArrayHelper() const1501 ::cppu::IPropertyArrayHelper* CopyTableWizard::createArrayHelper( ) const
1502 {
1503     Sequence< Property > aProps;
1504     describeProperties( aProps );
1505     return new ::cppu::OPropertyArrayHelper( aProps );
1506 }
1507 
createDialog(const css::uno::Reference<css::awt::XWindow> & rParent)1508 std::unique_ptr<weld::DialogController> CopyTableWizard::createDialog(const css::uno::Reference<css::awt::XWindow>& rParent)
1509 {
1510     OSL_PRECOND( isInitialized(), "CopyTableWizard::createDialog: not initialized!" );
1511         // this should have been prevented in ::execute already
1512 
1513     auto xWizard = std::make_unique<OCopyTableWizard>(
1514         Application::GetFrameWeld(rParent),
1515         m_sDestinationTable,
1516         m_nOperation,
1517         *m_pSourceObject,
1518         m_xSourceConnection.getTyped(),
1519         m_xDestConnection.getTyped(),
1520         m_xContext,
1521         m_xInteractionHandler);
1522 
1523     impl_attributesToDialog_nothrow(*xWizard);
1524 
1525     return xWizard;
1526 }
1527 
executedDialog(sal_Int16 _nExecutionResult)1528 void CopyTableWizard::executedDialog( sal_Int16 _nExecutionResult )
1529 {
1530     CopyTableWizard_DialogBase::executedDialog( _nExecutionResult );
1531 
1532     if ( _nExecutionResult == RET_OK )
1533         impl_doCopy_nothrow();
1534 
1535     // do this after impl_doCopy_nothrow: The attributes may change during copying, for instance
1536     // if the user entered an unqualified table name
1537     impl_dialogToAttributes_nothrow( impl_getDialog_throw() );
1538 }
1539 
1540 } // namespace dbaui
1541 
createRegistryInfo_CopyTableWizard()1542 extern "C" void createRegistryInfo_CopyTableWizard()
1543 {
1544     static ::dbaui::OMultiInstanceAutoRegistration< ::dbaui::CopyTableWizard > aAutoRegistration;
1545 }
1546 
1547 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1548