Quelle querycontroller.cxx
Sprache: C
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project .
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2 . 0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at http : //mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice :
*
* Licensed to the Apache Software Foundation ( ASF ) under one or more
* contributor license agreements . See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership . The ASF licenses this file to you under the Apache
* License , Version 2 . 0 ( the " License " ) ; you may not use this file
* except in compliance with the License . You may obtain a copy of
* the License at http : //www.apache.org/licenses/LICENSE-2.0 .
*/
#include <browserids.hxx>
#include <core_resource.hxx>
#include <strings.hrc>
#include <strings.hxx>
#include <query.hrc>
#include <stringconstants.hxx>
#include <defaultobjectnamecheck.hxx>
#include <dlgsave.hxx>
#include <querycontainerwindow.hxx>
#include <querycontroller.hxx>
#include <QueryDesignView.hxx>
#include <QueryTableView.hxx>
#include <sqlmessage.hxx>
#include <TableConnectionData.hxx>
#include <TableFieldDescription.hxx>
#include <UITools.hxx>
#include <QueryPropertiesDialog.hxx>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/frame/FrameSearchFlag.hpp>
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#include <com/sun/star/sdb/CommandType.hpp>
#include <com/sun/star/sdb/SQLContext.hpp>
#include <com/sun/star/sdb/XQueriesSupplier.hpp>
#include <com/sun/star/sdb/XQueryDefinitionsSupplier.hpp>
#include <com/sun/star/sdb/XSQLQueryComposerFactory.hpp>
#include <com/sun/star/sdbcx/XAppend.hpp>
#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp>
#include <com/sun/star/sdbcx/XDrop.hpp>
#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
#include <com/sun/star/sdbcx/XViewsSupplier.hpp>
#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
#include <com/sun/star/util/XCloseable.hpp>
#include <com/sun/star/util/VetoException.hpp>
#include <com/sun/star/ui/XUIElement.hpp>
#include <comphelper/propertysequence.hxx>
#include <comphelper/property.hxx>
#include <comphelper/types.hxx>
#include <connectivity/dbexception.hxx>
#include <connectivity/dbtools.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <svl/undo.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <osl/diagnose.h>
#include <utility>
#include <vcl/stdtext.hxx>
#include <vcl/svapp.hxx>
#include <vcl/weld.hxx>
#include <osl/mutex.hxx>
#include <o3tl/string_view.hxx>
#include <memory>
#include <vector>
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
org_openoffice_comp_dbu_OQueryDesign_get_implementation(
css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const & )
{
return cppu::acquire(new ::dbaui::OQueryController(context));
}
namespace dbaui
{
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::frame;
using namespace ::com::sun::star::util;
using namespace ::com::sun::star::lang;
namespace {
class OViewController : public OQueryController
{
virtual OUString SAL_CALL getImplementationName() override
{
return u"org.openoffice.comp.dbu.OViewDesign" _ustr;
}
virtual Sequence< OUString> SAL_CALL getSupportedServiceNames() override
{
return { u"com.sun.star.sdb.ViewDesign" _ustr };
}
public :
explicit OViewController(const Reference< XComponentContext >& _rM) : OQueryController(_rM){}
};
}
}
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
org_openoffice_comp_dbu_OViewDesign_get_implementation(
css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const & )
{
return cppu::acquire(new ::dbaui::OViewController(context));
}
namespace dbaui
{
using namespace ::connectivity;
namespace
{
OUString lcl_getObjectResourceString(TranslateId pResId, sal_Int32 _nCommandType)
{
OUString sMessageText = DBA_RES(pResId);
OUString sObjectType = DBA_RES(RSC_QUERY_OBJECT_TYPE[_nCommandType]);
sMessageText = sMessageText.replaceFirst( "$object$" , sObjectType );
return sMessageText;
}
}
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::sdbcx;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::sdb;
using namespace ::com::sun::star::ui;
using namespace ::com::sun::star::awt;
using namespace ::dbtools;
using namespace ::comphelper;
namespace
{
void ensureToolbars( OQueryController& _rController, bool _bDesign )
{
Reference< css::frame::XLayoutManager > xLayoutManager = OGenericUnoController::getLayoutManager( _rController.getFrame() );
if ( !xLayoutManager.is() )
return ;
xLayoutManager->lock();
static constexpr OUString s_sDesignToolbar = u"private:resource/toolbar/designobjectbar" _ustr;
static constexpr OUString s_sSqlToolbar = u"private:resource/toolbar/sqlobjectbar" _ustr;
if ( _bDesign )
{
xLayoutManager->destroyElement( s_sSqlToolbar );
xLayoutManager->createElement( s_sDesignToolbar );
}
else
{
xLayoutManager->destroyElement( s_sDesignToolbar );
xLayoutManager->createElement( s_sSqlToolbar );
}
xLayoutManager->unlock();
xLayoutManager->doLayout();
}
/**
* The value of m_nLimit is updated when LimitBox loses its focus
* So in those case when execution needs recent data , grab the focus
* ( e . g . execute SQL statement , change views )
*/
void grabFocusFromLimitBox( OQueryController& _rController )
{
Reference< XLayoutManager > xLayoutManager = OGenericUnoController::getLayoutManager( _rController.getFrame() );
Reference< XUIElement > xUIElement = xLayoutManager->getElement(u"private:resource/toolbar/designobjectbar" _ustr);
if (xUIElement.is())
{
Reference< XWindow > xWindow(xUIElement->getRealInterface(), css::uno::UNO_QUERY);
VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
if ( pWindow && pWindow->HasChildPathFocus() )
{
pWindow->GrabFocusToDocument();
}
}
}
}
OUString SAL_CALL OQueryController::getImplementationName()
{
return u"org.openoffice.comp.dbu.OQueryDesign" _ustr;
}
Sequence< OUString> SAL_CALL OQueryController::getSupportedServiceNames()
{
return { u"com.sun.star.sdb.QueryDesign" _ustr };
}
OQueryController::OQueryController(const Reference< XComponentContext >& _rM)
:OJoinController(_rM)
,OQueryController_PBase( getBroadcastHelper() )
,m_pParseContext( new svxform::OSystemParseContext )
,m_aSqlParser( _rM, m_pParseContext.get() )
,m_nLimit(-1 )
,m_nVisibleRows(0 x400)
,m_nSplitPos(-1 )
,m_nCommandType( CommandType::QUERY )
,m_bGraphicalDesign(false )
,m_bDistinct(false )
,m_bEscapeProcessing(true )
{
InvalidateAll();
registerProperty( PROPERTY_ACTIVECOMMAND, PROPERTY_ID_ACTIVECOMMAND, PropertyAttribute::READONLY | PropertyAttribute::BOUND,
&m_sStatement, cppu::UnoType<decltype(m_sStatement)>::get() );
registerProperty( PROPERTY_ESCAPE_PROCESSING, PROPERTY_ID_ESCAPE_PROCESSING, PropertyAttribute::READONLY | PropertyAttribute::BOUND,
&m_bEscapeProcessing, cppu::UnoType<decltype(m_bEscapeProcessing)>::get() );
}
OQueryController::~OQueryController()
{
if ( !getBroadcastHelper().bDisposed && !getBroadcastHelper().bInDispose )
{
OSL_FAIL("Please check who doesn't dispose this component!" );
// increment ref count to prevent double call of Dtor
osl_atomic_increment( &m_refCount );
dispose();
}
}
IMPLEMENT_FORWARD_XINTERFACE2( OQueryController, OJoinController, OQueryController_PBase )
IMPLEMENT_FORWARD_XTYPEPROVIDER2( OQueryController, OJoinController, OQueryController_PBase )
Reference< XPropertySetInfo > SAL_CALL OQueryController::getPropertySetInfo()
{
Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
return xInfo;
}
void SAL_CALL OQueryController::getFastPropertyValue( Any& o_rValue, sal_Int32 i_nHandle ) const
{
switch ( i_nHandle )
{
case PROPERTY_ID_CURRENT_QUERY_DESIGN:
{
::comphelper::NamedValueCollection aCurrentDesign;
aCurrentDesign.put( u"GraphicalDesign" _ustr, isGraphicalDesign() );
aCurrentDesign.put( PROPERTY_ESCAPE_PROCESSING, m_bEscapeProcessing );
if ( isGraphicalDesign() )
{
getContainer()->SaveUIConfig();
saveViewSettings( aCurrentDesign, true );
aCurrentDesign.put( u"Statement" _ustr, m_sStatement );
}
else
{
aCurrentDesign.put( u"Statement" _ustr, getContainer()->getStatement() );
}
o_rValue <<= aCurrentDesign.getPropertyValues();
}
break ;
default :
OPropertyContainer::getFastPropertyValue( o_rValue, i_nHandle );
break ;
}
}
::cppu::IPropertyArrayHelper& OQueryController::getInfoHelper()
{
return *getArrayHelper();
}
::cppu::IPropertyArrayHelper* OQueryController::createArrayHelper( ) const
{
Sequence< Property > aProps;
describeProperties( aProps );
// one additional property:
const sal_Int32 nLength = aProps.getLength();
aProps.realloc( nLength + 1 );
auto pProps = aProps.getArray();
pProps[ nLength ] = Property(
u"CurrentQueryDesign" _ustr,
PROPERTY_ID_CURRENT_QUERY_DESIGN,
::cppu::UnoType< Sequence< PropertyValue > >::get(),
PropertyAttribute::READONLY
);
std::sort(
pProps,
pProps + aProps.getLength(),
::comphelper::PropertyCompareByName()
);
return new ::cppu::OPropertyArrayHelper(aProps);
}
void OQueryController::deleteIterator()
{
if (m_pSqlIterator)
{
delete m_pSqlIterator->getParseTree();
m_pSqlIterator->dispose();
m_pSqlIterator.reset();
}
}
void OQueryController::disposing()
{
OQueryController_PBase::disposing();
deleteIterator();
m_pParseContext.reset();
clearFields();
OTableFields().swap(m_vUnUsedFieldsDesc);
::comphelper::disposeComponent(m_xComposer);
OJoinController::disposing();
OQueryController_PBase::disposing();
}
void OQueryController::clearFields()
{
OTableFields().swap(m_vTableFieldDesc);
}
FeatureState OQueryController::GetState(sal_uInt16 _nId) const
{
FeatureState aReturn;
aReturn.bEnabled = true ;
// (disabled automatically)
switch (_nId)
{
case ID_BROWSER_EDITDOC:
if ( editingCommand() )
aReturn.bEnabled = false ;
else if ( editingView() && !m_xAlterView.is() )
aReturn.bEnabled = false ;
else
aReturn = OJoinController::GetState( _nId );
break ;
case ID_BROWSER_ESCAPEPROCESSING:
aReturn.bChecked = !m_bEscapeProcessing;
aReturn.bEnabled = ( m_pSqlIterator != nullptr ) && !m_bGraphicalDesign;
break ;
case SID_RELATION_ADD_RELATION:
aReturn.bEnabled = isEditable() && m_bGraphicalDesign && m_vTableData.size() > 1 ;
break ;
case ID_BROWSER_SAVEASDOC:
aReturn.bEnabled = !editingCommand() && (!m_bGraphicalDesign || !(m_vTableFieldDesc.empty() || m_vTableData.empty()));
break ;
case ID_BROWSER_SAVEDOC:
aReturn.bEnabled = isEditable() && (!m_bGraphicalDesign || !(m_vTableFieldDesc.empty() || m_vTableData.empty()));
break ;
case SID_PRINTDOCDIRECT:
break ;
case ID_BROWSER_CUT:
aReturn.bEnabled = isEditable() && getContainer() && getContainer()->isCutAllowed();
break ;
case ID_BROWSER_COPY:
aReturn.bEnabled = getContainer() && getContainer()->isCopyAllowed();
break ;
case ID_BROWSER_PASTE:
aReturn.bEnabled = isEditable() && getContainer() && getContainer()->isPasteAllowed();
break ;
case ID_BROWSER_SQL:
aReturn.bEnabled = m_bEscapeProcessing && m_pSqlIterator;
aReturn.bChecked = m_bGraphicalDesign;
break ;
case SID_BROWSER_CLEAR_QUERY:
aReturn.bEnabled = isEditable() && (!m_sStatement.isEmpty() || !m_vTableData.empty());
break ;
case SID_QUERY_VIEW_FUNCTIONS:
case SID_QUERY_VIEW_TABLES:
case SID_QUERY_VIEW_ALIASES:
aReturn.bChecked = getContainer() && getContainer()->isSlotEnabled(_nId);
aReturn.bEnabled = m_bGraphicalDesign;
break ;
case SID_QUERY_DISTINCT_VALUES:
aReturn.bEnabled = m_bGraphicalDesign && isEditable();
aReturn.bChecked = m_bDistinct;
break ;
case SID_QUERY_LIMIT:
aReturn.bEnabled = m_bGraphicalDesign;
if ( aReturn.bEnabled )
aReturn.aValue <<= m_nLimit;
break ;
case SID_QUERY_PROP_DLG:
aReturn.bEnabled = m_bGraphicalDesign;
break ;
case ID_BROWSER_QUERY_EXECUTE:
aReturn.bEnabled = true ;
break ;
case SID_DB_QUERY_PREVIEW:
aReturn.bEnabled = true ;
aReturn.bChecked = getContainer() && getContainer()->getPreviewFrame().is();
break ;
#if OSL_DEBUG_LEVEL > 0
case ID_EDIT_QUERY_SQL:
break ;
case ID_EDIT_QUERY_DESIGN:
break ;
#endif
case ID_BROWSER_ADDTABLE:
if ( !m_bGraphicalDesign )
{
aReturn.bEnabled = false ;
break ;
}
[[fallthrough]];
default :
aReturn = OJoinController::GetState(_nId);
break ;
}
return aReturn;
}
void OQueryController::Execute(sal_uInt16 _nId, const Sequence< PropertyValue >& aArgs)
{
switch (_nId)
{
case ID_BROWSER_ESCAPEPROCESSING:
setEscapeProcessing_fireEvent( !m_bEscapeProcessing );
if ( !editingView() )
setModified(true );
InvalidateFeature(ID_BROWSER_SQL);
break ;
case ID_BROWSER_SAVEASDOC:
case ID_BROWSER_SAVEDOC:
grabFocusFromLimitBox(*this );
doSaveAsDoc(ID_BROWSER_SAVEASDOC == _nId);
break ;
case SID_RELATION_ADD_RELATION:
{
OJoinDesignView* pView = getJoinView();
if ( pView )
static_cast <OQueryTableView*>(pView->getTableView())->createNewConnection();
}
break ;
case SID_PRINTDOCDIRECT:
break ;
case ID_BROWSER_CUT:
getContainer()->cut();
break ;
case ID_BROWSER_COPY:
getContainer()->copy();
break ;
case ID_BROWSER_PASTE:
getContainer()->paste();
break ;
case ID_BROWSER_SQL:
{
grabFocusFromLimitBox(*this );
if ( !getContainer()->checkStatement() )
break ;
SQLExceptionInfo aError;
try
{
setStatement_fireEvent( getContainer()->getStatement() );
if (m_sStatement.isEmpty() && m_pSqlIterator)
{
// change the view of the data
delete m_pSqlIterator->getParseTree();
m_pSqlIterator->setParseTree(nullptr);
m_bGraphicalDesign = !m_bGraphicalDesign;
impl_setViewMode( &aError );
}
else
{
OUString aErrorMsg;
std::unique_ptr<::connectivity::OSQLParseNode> pNode = m_aSqlParser.parseTree(aErrorMsg,m_sStatement,m_bGraphicalDesign);
if ( pNode )
{
assert(m_pSqlIterator && "SqlIterator must exist" );
delete m_pSqlIterator->getParseTree();
m_pSqlIterator->setParseTree(pNode.release());
m_pSqlIterator->traverseAll();
if ( m_pSqlIterator->hasErrors() )
{
aError = m_pSqlIterator->getErrors();
}
else
{
const OSQLTables& rTabs = m_pSqlIterator->getTables();
if ( m_pSqlIterator->getStatementType() != OSQLStatementType::Select || rTabs.empty() )
{
aError = SQLException(
DBA_RES(STR_QRY_NOSELECT),
nullptr,
u"S1000" _ustr,
1000 ,
Any()
);
}
else
{
// change the view of the data
m_bGraphicalDesign = !m_bGraphicalDesign;
OUString sNewStatement;
m_pSqlIterator->getParseTree()->parseNodeToStr( sNewStatement, getConnection() );
setStatement_fireEvent( sNewStatement );
getContainer()->SaveUIConfig();
m_vTableConnectionData.clear();
impl_setViewMode( &aError );
}
}
}
else
{
aError = SQLException(
DBA_RES(STR_QRY_SYNTAX),
nullptr,
u"S1000" _ustr,
1000 ,
Any()
);
}
}
}
catch (const SQLException&)
{
aError = ::cppu::getCaughtException();
}
catch (const Exception&)
{
DBG_UNHANDLED_EXCEPTION("dbaccess" );
}
if ( aError.isValid() )
showError( aError );
if (m_bGraphicalDesign)
{
InvalidateFeature(ID_BROWSER_ADDTABLE);
InvalidateFeature(SID_RELATION_ADD_RELATION);
}
}
break ;
case SID_BROWSER_CLEAR_QUERY:
{
GetUndoManager().EnterListAction(DBA_RES(STR_QUERY_UNDO_TABWINDELETE), OUString(), 0 , ViewShellId(-1 ) );
getContainer()->clear();
GetUndoManager().LeaveListAction();
setStatement_fireEvent( OUString() );
if (m_bGraphicalDesign)
InvalidateFeature(ID_BROWSER_ADDTABLE);
}
break ;
case SID_QUERY_VIEW_FUNCTIONS:
case SID_QUERY_VIEW_TABLES:
case SID_QUERY_VIEW_ALIASES:
getContainer()->setSlotEnabled(_nId,!getContainer()->isSlotEnabled(_nId));
setModified(true );
break ;
case SID_QUERY_DISTINCT_VALUES:
m_bDistinct = !m_bDistinct;
setModified(true );
break ;
case SID_QUERY_LIMIT:
if ( aArgs.hasElements() && aArgs[0 ].Name == "DBLimit.Value" )
{
aArgs[0 ].Value >>= m_nLimit;
setModified(true );
}
break ;
case SID_QUERY_PROP_DLG:
grabFocusFromLimitBox(*this );
execute_QueryPropDlg();
break ;
case ID_BROWSER_QUERY_EXECUTE:
grabFocusFromLimitBox(*this );
if ( getContainer()->checkStatement() )
executeQuery();
break ;
case SID_DB_QUERY_PREVIEW:
try
{
Reference< css::util::XCloseable > xCloseFrame( getContainer()->getPreviewFrame(), UNO_QUERY );
if ( xCloseFrame.is() )
{
try
{
xCloseFrame->close( true );
}
catch (const Exception&)
{
OSL_FAIL( "OQueryController::Execute(SID_DB_QUERY_PREVIEW): *nobody* is expected to veto closing the preview frame!" );
}
}
else
Execute(ID_BROWSER_QUERY_EXECUTE,Sequence< PropertyValue >());
}
catch (const Exception&)
{
}
break ;
default :
OJoinController::Execute(_nId,aArgs);
return ; // else we would invalidate twice
}
InvalidateFeature(_nId);
}
void OQueryController::impl_showAutoSQLViewError( const css::uno::Any& _rErrorDetails )
{
SQLContext aErrorContext(
lcl_getObjectResourceString(STR_ERROR_PARSING_STATEMENT, m_nCommandType), *this , {}, 0 ,
_rErrorDetails, lcl_getObjectResourceString(STR_INFO_OPENING_IN_SQL_VIEW, m_nCommandType));
showError( aErrorContext );
}
void OQueryController::impl_setViewMode( ::dbtools::SQLExceptionInfo* _pErrorInfo )
{
OSL_PRECOND( getContainer(), "OQueryController::impl_setViewMode: illegal call!" );
bool wasModified = isModified();
SQLExceptionInfo aError;
bool bSuccess = getContainer()->switchView( &aError );
if ( !bSuccess )
{
m_bGraphicalDesign = !m_bGraphicalDesign;
// restore old state
getContainer()->switchView( nullptr );
// don't pass &aError here, this would overwrite the error which the first switchView call
// returned in this location.
if ( _pErrorInfo )
*_pErrorInfo = std::move(aError);
else
showError( aError );
}
else
{
ensureToolbars( *this , m_bGraphicalDesign );
}
setModified( wasModified );
}
void OQueryController::impl_initialize(const ::comphelper::NamedValueCollection& rArguments)
{
OJoinController::impl_initialize(rArguments);
OUString sCommand;
m_nCommandType = CommandType::QUERY;
// reading parameters:
// legacy parameters first (later overwritten by regular parameters)
OUString sIndependentSQLCommand;
if ( rArguments.get_ensureType( u"IndependentSQLCommand" _ustr, sIndependentSQLCommand ) )
{
OSL_FAIL( "OQueryController::impl_initialize: IndependentSQLCommand is regognized for compatibility only!" );
sCommand = sIndependentSQLCommand;
m_nCommandType = CommandType::COMMAND;
}
OUString sCurrentQuery;
if ( rArguments.get_ensureType( u"CurrentQuery" _ustr, sCurrentQuery ) )
{
OSL_FAIL( "OQueryController::impl_initialize: CurrentQuery is regognized for compatibility only!" );
sCommand = sCurrentQuery;
m_nCommandType = CommandType::QUERY;
}
bool bCreateView( false );
if ( rArguments.get_ensureType( u"CreateView" _ustr, bCreateView ) && bCreateView )
{
OSL_FAIL( "OQueryController::impl_initialize: CurrentQuery is regognized for compatibility only!" );
m_nCommandType = CommandType::TABLE;
}
// non-legacy parameters which overwrite the legacy parameters
rArguments.get_ensureType( PROPERTY_COMMAND, sCommand );
rArguments.get_ensureType( PROPERTY_COMMAND_TYPE, m_nCommandType );
// translate Command/Type into proper members
// TODO/Later: all this (including those members) should be hidden behind some abstract interface,
// which is implemented for all the three commands
switch ( m_nCommandType )
{
case CommandType::QUERY:
case CommandType::TABLE:
m_sName = sCommand;
break ;
case CommandType::COMMAND:
setStatement_fireEvent( sCommand );
m_sName.clear();
break ;
default :
OSL_FAIL( "OQueryController::impl_initialize: logic error in code!" );
throw RuntimeException();
}
// more legacy parameters
bool bGraphicalDesign( true );
if ( rArguments.get_ensureType( PROPERTY_QUERYDESIGNVIEW, bGraphicalDesign ) )
{
OSL_FAIL( "OQueryController::impl_initialize: QueryDesignView is regognized for compatibility only!" );
m_bGraphicalDesign = bGraphicalDesign;
}
// more non-legacy
rArguments.get_ensureType( PROPERTY_GRAPHICAL_DESIGN, m_bGraphicalDesign );
bool bEscapeProcessing( true );
if ( rArguments.get_ensureType( PROPERTY_ESCAPE_PROCESSING, bEscapeProcessing ) )
{
setEscapeProcessing_fireEvent( bEscapeProcessing );
OSL_ENSURE( m_bEscapeProcessing || !m_bGraphicalDesign, "OQueryController::impl_initialize: can't do the graphical design without escape processing!" );
if ( !m_bEscapeProcessing )
m_bGraphicalDesign = false ;
}
// initial design
bool bForceInitialDesign = false ;
Sequence< PropertyValue > aCurrentQueryDesignProps;
aCurrentQueryDesignProps = rArguments.getOrDefault( u"CurrentQueryDesign" _ustr, aCurrentQueryDesignProps );
if ( aCurrentQueryDesignProps.hasElements() )
{
::comphelper::NamedValueCollection aCurrentQueryDesign( aCurrentQueryDesignProps );
if ( aCurrentQueryDesign.has( PROPERTY_GRAPHICAL_DESIGN ) )
{
aCurrentQueryDesign.get_ensureType( PROPERTY_GRAPHICAL_DESIGN, m_bGraphicalDesign );
}
if ( aCurrentQueryDesign.has( PROPERTY_ESCAPE_PROCESSING ) )
{
aCurrentQueryDesign.get_ensureType( PROPERTY_ESCAPE_PROCESSING, m_bEscapeProcessing );
}
if ( aCurrentQueryDesign.has( u"Statement" _ustr ) )
{
OUString sStatement;
aCurrentQueryDesign.get_ensureType( u"Statement" _ustr, sStatement );
aCurrentQueryDesign.remove( u"Statement" _ustr );
setStatement_fireEvent( sStatement );
}
loadViewSettings( aCurrentQueryDesign );
bForceInitialDesign = true ;
}
if ( !ensureConnected() )
{ // we have no connection so what else should we do
m_bGraphicalDesign = false ;
if ( editingView() )
{
connectionLostMessage();
throw SQLException();
}
}
// check the view capabilities
if ( isConnected() && editingView() )
{
Reference< XViewsSupplier > xViewsSup( getConnection(), UNO_QUERY );
Reference< XNameAccess > xViews;
if ( xViewsSup.is() )
xViews = xViewsSup->getViews();
if ( !xViews.is() )
{ // we can't create views so we ask if the user wants to create a query instead
m_nCommandType = CommandType::QUERY;
bool bClose = false ;
{
OUString aTitle(DBA_RES(STR_QUERYDESIGN_NO_VIEW_SUPPORT));
OUString aMessage(DBA_RES(STR_QUERYDESIGN_NO_VIEW_ASK));
OSQLMessageBox aDlg(getFrameWeld(), aTitle, aMessage, MessBoxStyle::YesNo | MessBoxStyle::DefaultYes, MessageType::Query);
bClose = aDlg.run() == RET_NO;
}
if ( bClose )
throw VetoException();
}
// now if we are to edit an existing view, check whether this is possible
if ( !m_sName.isEmpty() )
{
Any aView( xViews->getByName( m_sName ) );
// will throw if there is no such view
if ( !( aView >>= m_xAlterView ) )
{
throw IllegalArgumentException(
DBA_RES(STR_NO_ALTER_VIEW_SUPPORT),
*this ,
1
);
}
}
}
OSL_ENSURE(getDataSource().is(),"OQueryController::impl_initialize: need a datasource!" );
try
{
getContainer()->initialize();
impl_reset( bForceInitialDesign );
SQLExceptionInfo aError;
const bool bAttemptedGraphicalDesign = m_bGraphicalDesign;
if ( bForceInitialDesign )
{
getContainer()->forceInitialView();
}
else
{
impl_setViewMode( &aError );
}
if ( aError.isValid() && bAttemptedGraphicalDesign && !m_bGraphicalDesign )
{
// we tried initializing the graphical view, this failed, and we were automatically switched to SQL
// view => tell this to the user
if ( !editingView() )
{
impl_showAutoSQLViewError( aError.get() );
}
}
ClearUndoManager();
if ( m_bGraphicalDesign
&& ( ( m_sName.isEmpty() && !editingCommand() )
|| ( m_sStatement.isEmpty() && editingCommand() )
)
)
{
Application::PostUserEvent( LINK( this , OQueryController, OnExecuteAddTable ) );
}
setModified(false );
}
catch (const SQLException& e)
{
DBG_UNHANDLED_EXCEPTION("dbaccess" );
// we caught an exception so we switch to text only mode
{
m_bGraphicalDesign = false ;
getContainer()->initialize();
OSQLMessageBox aBox(getFrameWeld(), e);
aBox.run();
}
throw ;
}
}
void OQueryController::onLoadedMenu(const Reference< css::frame::XLayoutManager >& /*_xLayoutManager*/)
{
ensureToolbars( *this , m_bGraphicalDesign );
}
OUString OQueryController::getPrivateTitle( ) const
{
if ( m_sName.isEmpty() )
{
if ( !editingCommand() )
{
SolarMutexGuard aSolarGuard;
::osl::MutexGuard aGuard( getMutex() );
OUString aDefaultName = DBA_RES(editingView() ? STR_VIEW_TITLE : STR_QRY_TITLE);
return o3tl::getToken(aDefaultName, 0 , ' ' ) + OUString::number(getCurrentStartNumber());
}
}
return m_sName;
}
void OQueryController::setQueryComposer()
{
if (!isConnected())
return ;
Reference< XSQLQueryComposerFactory > xFactory(getConnection(), UNO_QUERY);
OSL_ENSURE(xFactory.is(),"Connection doesn't support a querycomposer" );
if ( !(xFactory.is() && getContainer()) )
return ;
try
{
m_xComposer = xFactory->createQueryComposer();
getContainer()->setStatement(m_sStatement);
}
catch (const Exception&)
{
m_xComposer = nullptr;
}
OSL_ENSURE(m_xComposer.is(),"No querycomposer available!" );
Reference<XTablesSupplier> xTablesSup(getConnection(), UNO_QUERY);
deleteIterator();
m_pSqlIterator.reset(new ::connectivity::OSQLParseTreeIterator( getConnection(), xTablesSup->getTables(), m_aSqlParser ));
}
bool OQueryController::Construct(vcl::Window* pParent)
{
// TODO: we have to check if we should create the text view or the design view
setView( VclPtr<OQueryContainerWindow>::Create( pParent, *this , getORB() ) );
return OJoinController::Construct(pParent);
}
OJoinDesignView* OQueryController::getJoinView()
{
return getContainer()->getDesignView();
}
void OQueryController::describeSupportedFeatures()
{
OJoinController::describeSupportedFeatures();
implDescribeSupportedFeature( u".uno:SaveAs" _ustr, ID_BROWSER_SAVEASDOC, CommandGroup::DOCUMENT );
implDescribeSupportedFeature( u".uno:SbaNativeSql" _ustr, ID_BROWSER_ESCAPEPROCESSING,CommandGroup::FORMAT );
implDescribeSupportedFeature( u".uno:DBViewFunctions" _ustr, SID_QUERY_VIEW_FUNCTIONS, CommandGroup::VIEW );
implDescribeSupportedFeature( u".uno:DBViewTableNames" _ustr, SID_QUERY_VIEW_TABLES, CommandGroup::VIEW );
implDescribeSupportedFeature( u".uno:DBViewAliases" _ustr, SID_QUERY_VIEW_ALIASES, CommandGroup::VIEW );
implDescribeSupportedFeature( u".uno:DBDistinctValues" _ustr, SID_QUERY_DISTINCT_VALUES, CommandGroup::FORMAT );
implDescribeSupportedFeature( u".uno:DBChangeDesignMode" _ustr,ID_BROWSER_SQL, CommandGroup::VIEW );
implDescribeSupportedFeature( u".uno:DBClearQuery" _ustr, SID_BROWSER_CLEAR_QUERY, CommandGroup::EDIT );
implDescribeSupportedFeature( u".uno:SbaExecuteSql" _ustr, ID_BROWSER_QUERY_EXECUTE, CommandGroup::VIEW );
implDescribeSupportedFeature( u".uno:DBAddRelation" _ustr, SID_RELATION_ADD_RELATION, CommandGroup::EDIT );
implDescribeSupportedFeature( u".uno:DBQueryPreview" _ustr, SID_DB_QUERY_PREVIEW, CommandGroup::VIEW );
implDescribeSupportedFeature( u".uno:DBLimit" _ustr, SID_QUERY_LIMIT, CommandGroup::FORMAT );
implDescribeSupportedFeature( u".uno:DBQueryPropertiesDialog" _ustr, SID_QUERY_PROP_DLG, CommandGroup::FORMAT );
#if OSL_DEBUG_LEVEL > 0
implDescribeSupportedFeature( u".uno:DBShowParseTree" _ustr, ID_EDIT_QUERY_SQL );
implDescribeSupportedFeature( u".uno:DBMakeDisjunct" _ustr, ID_EDIT_QUERY_DESIGN );
#endif
}
void OQueryController::impl_onModifyChanged()
{
OJoinController::impl_onModifyChanged();
InvalidateFeature(SID_BROWSER_CLEAR_QUERY);
InvalidateFeature(ID_BROWSER_SAVEASDOC);
InvalidateFeature(ID_BROWSER_QUERY_EXECUTE);
}
void SAL_CALL OQueryController::disposing( const EventObject& Source )
{
SolarMutexGuard aGuard;
if ( getContainer() && Source.Source.is() )
{
if ( Source.Source == m_aCurrentFrame.getFrame() )
{ // our frame is being disposed -> close the preview window (if we have one)
Reference< XFrame2 > xPreviewFrame( getContainer()->getPreviewFrame() );
::comphelper::disposeComponent( xPreviewFrame );
}
else if ( Source.Source == getContainer()->getPreviewFrame() )
{
getContainer()->disposingPreview();
}
}
OJoinController_BASE::disposing(Source);
}
void OQueryController::reconnect(bool _bUI)
{
deleteIterator();
::comphelper::disposeComponent(m_xComposer);
OJoinController::reconnect( _bUI );
if (isConnected())
{
setQueryComposer();
}
else
{
if (m_bGraphicalDesign)
{
m_bGraphicalDesign = false ;
// don't call Execute(SQL) because this changes the sql statement
impl_setViewMode( nullptr );
}
InvalidateAll();
}
}
void OQueryController::saveViewSettings( ::comphelper::NamedValueCollection& o_rViewSettings, const bool i_includingCriteria ) const
{
saveTableWindows( o_rViewSettings );
::comphelper::NamedValueCollection aAllFieldsData;
::comphelper::NamedValueCollection aFieldData;
sal_Int32 i = 1 ;
for (auto const & fieldDesc : m_vTableFieldDesc)
{
if ( !fieldDesc->IsEmpty() )
{
aFieldData.clear();
fieldDesc->Save( aFieldData, i_includingCriteria );
const OUString sFieldSettingName = "Field" + OUString::number( i );
aAllFieldsData.put( sFieldSettingName, aFieldData.getPropertyValues() );
}
++i;
}
o_rViewSettings.put( u"Fields" _ustr, aAllFieldsData.getPropertyValues() );
o_rViewSettings.put( u"SplitterPosition" _ustr, m_nSplitPos );
o_rViewSettings.put( u"VisibleRows" _ustr, m_nVisibleRows );
}
void OQueryController::loadViewSettings( const ::comphelper::NamedValueCollection& o_rViewSettings )
{
loadTableWindows( o_rViewSettings );
m_nSplitPos = o_rViewSettings.getOrDefault( u"SplitterPosition" _ustr, m_nSplitPos );
m_nVisibleRows = o_rViewSettings.getOrDefault( u"VisibleRows" _ustr, m_nVisibleRows );
m_aFieldInformation = o_rViewSettings.getOrDefault( u"Fields" _ustr, m_aFieldInformation );
}
void OQueryController::execute_QueryPropDlg()
{
QueryPropertiesDialog aQueryPropDlg(getContainer()->GetFrameWeld(), m_bDistinct, m_nLimit);
if (aQueryPropDlg.run() == RET_OK)
{
m_bDistinct = aQueryPropDlg.getDistinct();
m_nLimit = aQueryPropDlg.getLimit();
InvalidateFeature( SID_QUERY_DISTINCT_VALUES );
InvalidateFeature( SID_QUERY_LIMIT, nullptr, true );
}
}
sal_Int32 OQueryController::getColWidth(sal_uInt16 _nColPos) const
{
if ( _nColPos < m_aFieldInformation.getLength() )
{
rtl::Reference<OTableFieldDesc> pField( new OTableFieldDesc());
pField->Load( m_aFieldInformation[ _nColPos ], false );
return pField->GetColWidth();
}
return 0 ;
}
Reference<XNameAccess> OQueryController::getObjectContainer() const
{
Reference< XNameAccess > xElements;
if ( editingView() )
{
Reference< XViewsSupplier > xViewsSupp( getConnection(), UNO_QUERY );
if ( xViewsSupp.is() )
xElements = xViewsSupp->getViews();
}
else
{
Reference< XQueriesSupplier > xQueriesSupp( getConnection(), UNO_QUERY );
if ( xQueriesSupp.is() )
xElements = xQueriesSupp->getQueries();
else
{
Reference< XQueryDefinitionsSupplier > xQueryDefsSupp( getDataSource(), UNO_QUERY );
if ( xQueryDefsSupp.is() )
xElements = xQueryDefsSupp->getQueryDefinitions();
}
}
OSL_ENSURE( xElements.is(), "OQueryController::getObjectContainer: unable to obtain the container!" );
return xElements;
}
void OQueryController::executeQuery()
{
// we don't need to check the connection here because we already check the composer
// which can't live without his connection
OUString sTranslatedStmt = translateStatement( false );
OUString sDataSourceName = getDataSourceName();
if ( sDataSourceName.isEmpty() || sTranslatedStmt.isEmpty() )
return ;
try
{
getContainer()->showPreview( getFrame() );
InvalidateFeature(SID_DB_QUERY_PREVIEW);
URL aWantToDispatch;
aWantToDispatch.Complete = ".component:DB/DataSourceBrowser" ;
OUString sFrameName( FRAME_NAME_QUERY_PREVIEW );
sal_Int32 nSearchFlags = FrameSearchFlag::CHILDREN;
Reference< XDispatch> xDisp;
Reference< XDispatchProvider> xProv( getFrame()->findFrame( sFrameName, nSearchFlags ), UNO_QUERY );
if (!xProv.is())
{
xProv.set( getFrame(), UNO_QUERY );
if (xProv.is())
xDisp = xProv->queryDispatch(aWantToDispatch, sFrameName, nSearchFlags);
}
else
{
xDisp = xProv->queryDispatch(aWantToDispatch, sFrameName, FrameSearchFlag::SELF);
}
if (xDisp.is())
{
auto aProps(::comphelper::InitPropertySequence(
{
{ PROPERTY_DATASOURCENAME, Any(sDataSourceName) },
{ PROPERTY_COMMAND_TYPE, Any(CommandType::COMMAND) },
{ PROPERTY_COMMAND, Any(sTranslatedStmt) },
{ PROPERTY_ENABLE_BROWSER, Any(false ) },
{ PROPERTY_ACTIVE_CONNECTION, Any(getConnection()) },
{ PROPERTY_UPDATE_CATALOGNAME, Any(m_sUpdateCatalogName) },
{ PROPERTY_UPDATE_SCHEMANAME, Any(m_sUpdateSchemaName) },
{ PROPERTY_UPDATE_TABLENAME, Any(OUString()) },
{ PROPERTY_ESCAPE_PROCESSING, Any(m_bEscapeProcessing) }
}));
xDisp->dispatch(aWantToDispatch, aProps);
// check the state of the beamer
// be notified when the beamer frame is closed
Reference< XComponent > xComponent = getFrame()->findFrame( sFrameName, nSearchFlags );
if (xComponent.is())
{
OSL_ENSURE(Reference< XFrame >(xComponent, UNO_QUERY).get() == getContainer()->getPreviewFrame().get(),
"OQueryController::executeQuery: oops ... which window do I have here?" );
Reference< XEventListener> xEvtL(static_cast <cppu::OWeakObject*>(this ),UNO_QUERY);
xComponent->addEventListener(xEvtL);
}
}
else
{
OSL_FAIL("Couldn't create a beamer window!" );
}
}
catch (const Exception&)
{
OSL_FAIL("Couldn't create a beamer window!" );
}
}
bool OQueryController::askForNewName(const Reference<XNameAccess>& _xElements, bool _bSaveAs)
{
OSL_ENSURE( !editingCommand(), "OQueryController::askForNewName: not to be called when designing an independent statement!" );
if ( editingCommand() )
return false ;
OSL_PRECOND( _xElements.is(), "OQueryController::askForNewName: invalid container!" );
if ( !_xElements.is() )
return false ;
bool bRet = true ;
bool bNew = _bSaveAs || !_xElements->hasByName( m_sName );
if (bNew)
{
OUString aDefaultName;
if (!m_sName.isEmpty())
aDefaultName = m_sName;
else
{
OUString sName = DBA_RES(editingView() ? STR_VIEW_TITLE : STR_QRY_TITLE);
aDefaultName = ::dbtools::createUniqueName(_xElements, sName.getToken(0 , ' ' ));
}
DynamicTableOrQueryNameCheck aNameChecker( getConnection(), CommandType::QUERY );
OSaveAsDlg aDlg(
getFrameWeld(),
m_nCommandType,
getORB(),
getConnection(),
aDefaultName,
aNameChecker,
SADFlags::NONE );
bRet = ( aDlg.run() == RET_OK );
if ( bRet )
{
m_sName = aDlg.getName();
if ( editingView() )
{
m_sUpdateCatalogName = aDlg.getCatalog();
m_sUpdateSchemaName = aDlg.getSchema();
}
}
}
return bRet;
}
bool OQueryController::doSaveAsDoc(bool _bSaveAs)
{
OSL_ENSURE(isEditable(),"Slot ID_BROWSER_SAVEDOC should not be enabled!" );
if ( !editingCommand() && !haveDataSource() )
{
OUString aMessage(DBA_RES(STR_DATASOURCE_DELETED));
OSQLWarningBox aBox(getFrameWeld(), aMessage);
aBox.run();
return false ;
}
Reference< XNameAccess > xElements = getObjectContainer();
if ( !xElements.is() )
return false ;
if ( !getContainer()->checkStatement() )
return false ;
OUString sTranslatedStmt = translateStatement();
if ( editingCommand() )
{
setModified( false );
// this is all we need to do here. translateStatement implicitly set our m_sStatement, and
// notified it, and that's all
return true ;
}
if ( sTranslatedStmt.isEmpty() )
return false ;
// first we need a name for our query so ask the user
// did we get a name
OUString sOriginalName( m_sName );
if ( !askForNewName( xElements, _bSaveAs ) || m_sName.isEmpty() )
return false ;
SQLExceptionInfo aInfo;
bool bSuccess = false ;
bool bNew = false ;
try
{
bNew = _bSaveAs
|| ( !xElements->hasByName( m_sName ) );
Reference<XPropertySet> xQuery;
if ( bNew ) // just to make sure the query already exists
{
// drop the query, in case it already exists
if ( xElements->hasByName( m_sName ) )
{
Reference< XDrop > xNameCont( xElements, UNO_QUERY );
if ( xNameCont.is() )
xNameCont->dropByName( m_sName );
else
{
Reference< XNameContainer > xCont( xElements, UNO_QUERY );
if ( xCont.is() )
xCont->removeByName( m_sName );
}
}
// create a new (empty, uninitialized) query resp. view
Reference< XDataDescriptorFactory > xFact( xElements, UNO_QUERY );
if ( xFact.is() )
{
xQuery = xFact->createDataDescriptor();
// to set the name is only allowed when the query is new
xQuery->setPropertyValue( PROPERTY_NAME, Any( m_sName ) );
}
else
{
Reference< XSingleServiceFactory > xSingleFac( xElements, UNO_QUERY );
if ( xSingleFac.is() )
xQuery.set(xSingleFac->createInstance(), css::uno::UNO_QUERY);
}
}
else
{
xElements->getByName( m_sName ) >>= xQuery;
}
if ( !xQuery.is() )
throw RuntimeException();
// the new commands
if ( editingView() && !bNew )
{
OSL_ENSURE( xQuery == m_xAlterView, "OQueryController::doSaveAsDoc: already have another alterable view ...!?" );
m_xAlterView.set( xQuery, UNO_QUERY_THROW );
m_xAlterView->alterCommand( sTranslatedStmt );
}
else
{ // we're creating a query, or a *new* view
xQuery->setPropertyValue( PROPERTY_COMMAND, Any( sTranslatedStmt ) );
if ( editingView() )
{
xQuery->setPropertyValue( PROPERTY_CATALOGNAME, Any( m_sUpdateCatalogName ) );
xQuery->setPropertyValue( PROPERTY_SCHEMANAME, Any( m_sUpdateSchemaName ) );
}
if ( editingQuery() )
{
xQuery->setPropertyValue( PROPERTY_UPDATE_TABLENAME, Any( OUString() ) );
xQuery->setPropertyValue( PROPERTY_ESCAPE_PROCESSING, css::uno::Any( m_bEscapeProcessing ) );
xQuery->setPropertyValue( PROPERTY_LAYOUTINFORMATION, getViewData() );
}
}
if ( bNew )
{
Reference< XAppend > xAppend( xElements, UNO_QUERY );
if ( xAppend.is() )
{
xAppend->appendByDescriptor( xQuery );
}
else
{
Reference< XNameContainer > xCont( xElements, UNO_QUERY );
if ( xCont.is() )
xCont->insertByName( m_sName, Any( xQuery ) );
}
if ( editingView() )
{
Reference< XPropertySet > xViewProps;
if ( xElements->hasByName( m_sName ) )
xViewProps.set( xElements->getByName( m_sName ), UNO_QUERY );
if ( !xViewProps.is() ) // correct name and try again
m_sName = ::dbtools::composeTableName( getMetaData(), xQuery, ::dbtools::EComposeRule::InDataManipulation, false );
OSL_ENSURE( xElements->hasByName( m_sName ), "OQueryController::doSaveAsDoc: newly created view does not exist!" );
if ( xElements->hasByName( m_sName ) )
m_xAlterView.set( xElements->getByName( m_sName ), UNO_QUERY );
// now check if our datasource has set a tablefilter and if so, append the new table name to it
::dbaui::appendToFilter(getConnection(), m_sName, getORB(), getFrameWeld());
}
Reference< XTitleChangeListener> xEventListener(impl_getTitleHelper_throw(),UNO_QUERY);
if ( xEventListener.is() )
{
TitleChangedEvent aEvent;
xEventListener->titleChanged(aEvent);
}
releaseNumberForComponent();
}
setModified( false );
bSuccess = true ;
}
catch (const SQLException&)
{
if ( !bNew )
m_sName = sOriginalName;
aInfo = SQLExceptionInfo( ::cppu::getCaughtException() );
}
catch (const Exception&)
{
DBG_UNHANDLED_EXCEPTION("dbaccess" );
if ( !bNew )
m_sName = sOriginalName;
}
showError( aInfo );
// if we successfully saved a view we were creating, then close the designer
if ( bSuccess && editingView() && !m_xAlterView.is() )
{
closeTask();
}
if ( bSuccess && editingView() )
InvalidateFeature( ID_BROWSER_EDITDOC );
return bSuccess;
}
namespace {
struct CommentStrip
{
OUString maComment;
bool mbLastOnLine;
CommentStrip( OUString sComment, bool bLastOnLine )
: maComment(std::move( sComment)), mbLastOnLine( bLastOnLine) {}
};
}
/** Obtain all comments in a query.
See also delComment ( ) implementation for OSQLParser : : parseTree ( ) .
*/
static std::vector< CommentStrip > getComment( const OUString& rQuery )
{
std::vector< CommentStrip > aRet;
// First a quick search if there is any "--" or "//" or "/*", if not then
// the whole copying loop is pointless.
if (rQuery.indexOf( "--" ) < 0 && rQuery.indexOf( "//" ) < 0 &&
rQuery.indexOf( "/*" ) < 0)
return aRet;
const sal_Unicode* pCopy = rQuery.getStr();
const sal_Int32 nQueryLen = rQuery.getLength();
bool bIsText1 = false ; // "text"
bool bIsText2 = false ; // 'text'
bool bComment2 = false ; // /* comment */
bool bComment = false ; // -- or // comment
OUStringBuffer aBuf;
for (sal_Int32 i=0 ; i < nQueryLen; ++i)
{
if (bComment2)
{
aBuf.append( &pCopy[i], 1 );
if ((i+1 ) < nQueryLen)
{
if (pCopy[i]=='*' && pCopy[i+1 ]=='/' )
{
bComment2 = false ;
aBuf.append( &pCopy[++i], 1 );
aRet.emplace_back( aBuf.makeStringAndClear(), false );
}
}
else
{
// comment can't close anymore, actually an error, but...
aRet.emplace_back( aBuf.makeStringAndClear(), false );
}
continue ;
}
if (pCopy[i] == '\n' || i == nQueryLen-1 )
{
if (bComment)
{
if (i == nQueryLen-1 && pCopy[i] != '\n' )
aBuf.append( &pCopy[i], 1 );
aRet.emplace_back( aBuf.makeStringAndClear(), true );
bComment = false ;
}
else if (!aRet.empty())
aRet.back().mbLastOnLine = true ;
}
else if (!bComment)
{
if (pCopy[i] == '\"' && !bIsText2)
bIsText1 = !bIsText1;
else if (pCopy[i] == '\' ' && !bIsText1)
bIsText2 = !bIsText2;
if (!bIsText1 && !bIsText2 && (i+1 ) < nQueryLen)
{
if ((pCopy[i]=='-' && pCopy[i+1 ]=='-' ) || (pCopy[i]=='/' && pCopy[i+1 ]=='/' ))
bComment = true ;
else if (pCopy[i]=='/' && pCopy[i+1 ]=='*' )
bComment2 = true ;
}
}
if (bComment || bComment2)
aBuf.append( &pCopy[i], 1 );
}
return aRet;
}
/** Concat/insert comments that were previously obtained with getComment().
NOTE : The current parser implementation does not preserve newlines , so all
comments are always appended to the entire query , also inline comments
that would need positioning anyway that can ' t be obtained after
recomposition . This is ugly but at least allows commented queries while
preserving the comments _ somehow_ .
*/
static OUString concatComment( const OUString& rQuery, const std::vector< CommentStrip >& rComments )
{
// No comments => return query.
if (rComments.empty())
return rQuery;
const sal_Unicode* pBeg = rQuery.getStr();
const sal_Int32 nLen = rQuery.getLength();
const size_t nComments = rComments.size();
// Obtaining the needed size once should be faster than reallocating.
// Also add a blank or linefeed for each comment.
sal_Int32 nBufSize = nLen + nComments;
for (auto const & comment : rComments)
nBufSize += comment.maComment.getLength();
OUStringBuffer aBuf( nBufSize );
sal_Int32 nIndBeg = 0 ;
sal_Int32 nIndLF = rQuery.indexOf('\n' );
size_t i = 0 ;
while (nIndLF >= 0 && i < nComments)
{
aBuf.append( pBeg + nIndBeg, nIndLF - nIndBeg);
do
{
aBuf.append( rComments[i].maComment);
} while (!rComments[i++].mbLastOnLine && i < nComments);
aBuf.append( pBeg + nIndLF, 1 ); // the LF
nIndBeg = nIndLF + 1 ;
nIndLF = (nIndBeg < nLen ? rQuery.indexOf( '\n' , nIndBeg) : -1 );
}
// Append remainder of query.
if (nIndBeg < nLen)
aBuf.append( pBeg + nIndBeg, nLen - nIndBeg);
// Append all remaining comments, preserve lines.
bool bNewLine = false ;
for ( ; i < nComments; ++i)
{
if (!bNewLine)
aBuf.append( ' ' );
aBuf.append( rComments[i].maComment);
if (rComments[i].mbLastOnLine)
{
aBuf.append( '\n' );
bNewLine = true ;
}
else
bNewLine = false ;
}
return aBuf.makeStringAndClear();
}
OUString OQueryController::translateStatement( bool _bFireStatementChange )
{
// now set the properties
setStatement_fireEvent( getContainer()->getStatement(), _bFireStatementChange );
OUString sTranslatedStmt;
if (!m_sStatement.isEmpty() && m_xComposer.is() && m_bEscapeProcessing)
{
try
{
OUString aErrorMsg;
std::vector< CommentStrip > aComments = getComment( m_sStatement);
std::unique_ptr<::connectivity::OSQLParseNode> pNode = m_aSqlParser.parseTree( aErrorMsg, m_sStatement, m_bGraphicalDesign );
if (pNode)
{
pNode->parseNodeToStr( sTranslatedStmt, getConnection() );
}
m_xComposer->setQuery(sTranslatedStmt);
sTranslatedStmt = m_xComposer->getComposedQuery();
sTranslatedStmt = concatComment( sTranslatedStmt, aComments);
}
catch (const SQLException& e)
{
::dbtools::SQLExceptionInfo aInfo(e);
showError(aInfo);
// an error occurred so we clear the statement
sTranslatedStmt.clear();
}
}
else if (m_sStatement.isEmpty())
{
showError(SQLException(DBA_RES(STR_QRY_NOSELECT), nullptr, u"S1000" _ustr, 1000 , Any()));
}
else
sTranslatedStmt = m_sStatement;
return sTranslatedStmt;
}
short OQueryController::saveModified()
{
SolarMutexGuard aSolarGuard;
::osl::MutexGuard aGuard( getMutex() );
short nRet = RET_YES;
if ( !isConnected() || !isModified() )
return nRet;
if ( !m_bGraphicalDesign
|| ( !m_vTableFieldDesc.empty()
&& !m_vTableData.empty()
)
)
{
OUString sMessageText( lcl_getObjectResourceString( STR_QUERY_SAVEMODIFIED, m_nCommandType ) );
std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(getFrameWeld(),
VclMessageType::Question, VclButtonsType::YesNo,
sMessageText));
xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL);
xQueryBox->set_default_response(RET_YES);
nRet = xQueryBox->run();
if ( ( nRet == RET_YES )
&& !doSaveAsDoc( false )
)
{
nRet = RET_CANCEL;
}
}
return nRet;
}
void OQueryController::impl_reset( const bool i_bForceCurrentControllerSettings )
{
bool bValid = false ;
Sequence< PropertyValue > aLayoutInformation;
// get command from the query if a query name was supplied
if ( !i_bForceCurrentControllerSettings && !editingCommand() )
{
if ( !m_sName.isEmpty() )
{
Reference< XNameAccess > xQueries = getObjectContainer();
if ( xQueries.is() )
{
Reference< XPropertySet > xProp;
if ( xQueries->hasByName( m_sName ) && ( xQueries->getByName( m_sName ) >>= xProp ) && xProp.is() )
{
OUString sNewStatement;
xProp->getPropertyValue( PROPERTY_COMMAND ) >>= sNewStatement;
setStatement_fireEvent( sNewStatement );
if ( editingQuery() )
{
bool bNewEscapeProcessing( true );
xProp->getPropertyValue( PROPERTY_ESCAPE_PROCESSING ) >>= bNewEscapeProcessing;
setEscapeProcessing_fireEvent( bNewEscapeProcessing );
}
m_bGraphicalDesign = m_bGraphicalDesign && m_bEscapeProcessing;
bValid = true ;
try
{
if ( editingQuery() )
xProp->getPropertyValue( PROPERTY_LAYOUTINFORMATION ) >>= aLayoutInformation;
}
catch ( const Exception& )
{
OSL_FAIL( "OQueryController::impl_reset: could not retrieve the layout information from the query!" );
}
}
}
}
}
else
{
bValid = true ;
// assume that we got all necessary information during initialization
}
if ( bValid )
{
// load the layoutInformation
if ( aLayoutInformation.hasElements() )
{
try
{
loadViewSettings( aLayoutInformation );
}
catch ( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("dbaccess" );
}
}
if ( !m_sStatement.isEmpty() )
{
setQueryComposer();
bool bError( false );
if ( !m_pSqlIterator )
{
bError = true ;
}
else if ( m_bEscapeProcessing )
{
OUString aErrorMsg;
std::unique_ptr< ::connectivity::OSQLParseNode > pNode(
m_aSqlParser.parseTree( aErrorMsg, m_sStatement, m_bGraphicalDesign ) );
if (pNode)
{
delete m_pSqlIterator->getParseTree();
m_pSqlIterator->setParseTree( pNode.release() );
m_pSqlIterator->traverseAll();
if ( m_pSqlIterator->hasErrors() )
{
if ( !i_bForceCurrentControllerSettings && m_bGraphicalDesign && !editingView() )
{
impl_showAutoSQLViewError( Any( m_pSqlIterator->getErrors() ) );
}
bError = true ;
}
}
else
{
if ( !i_bForceCurrentControllerSettings && !editingView() )
{
OUString aTitle(DBA_RES(STR_SVT_SQL_SYNTAX_ERROR));
OSQLMessageBox aDlg(getFrameWeld(), aTitle, aErrorMsg);
aDlg.run();
}
bError = true ;
}
}
if ( bError )
{
m_bGraphicalDesign = false ;
if ( editingView() )
// if we're editing a view whose statement could not be parsed, default to "no escape processing"
setEscapeProcessing_fireEvent( false );
}
}
}
if (!m_pSqlIterator)
setQueryComposer();
OSL_ENSURE(m_pSqlIterator,"No SQLIterator set!" );
getContainer()->setNoneVisibleRow(m_nVisibleRows);
}
void OQueryController::reset()
{
impl_reset();
getContainer()->reset();
ClearUndoManager();
}
void OQueryController::setStatement_fireEvent( const OUString& _rNewStatement, bool _bFireStatementChange )
{
Any aOldValue( m_sStatement );
m_sStatement = _rNewStatement;
Any aNewValue( m_sStatement );
sal_Int32 nHandle = PROPERTY_ID_ACTIVECOMMAND;
if ( _bFireStatementChange )
fire( &nHandle, &aNewValue, &aOldValue, 1 , false );
}
void OQueryController::setEscapeProcessing_fireEvent( const bool _bEscapeProcessing )
{
if ( _bEscapeProcessing == m_bEscapeProcessing )
return ;
Any aOldValue( m_bEscapeProcessing );
m_bEscapeProcessing = _bEscapeProcessing;
Any aNewValue( m_bEscapeProcessing );
sal_Int32 nHandle = PROPERTY_ID_ESCAPE_PROCESSING;
fire( &nHandle, &aNewValue, &aOldValue, 1 , false );
}
IMPL_LINK_NOARG( OQueryController, OnExecuteAddTable, void *, void )
{
Execute( ID_BROWSER_ADDTABLE,Sequence<PropertyValue>() );
}
bool OQueryController::allowViews() const
{
return true ;
}
bool OQueryController::allowQueries() const
{
OSL_ENSURE( getSdbMetaData().isConnected(), "OQueryController::allowQueries: illegal call!" );
if ( !getSdbMetaData().supportsSubqueriesInFrom() )
return false ;
bool bCreatingView = ( m_nCommandType == CommandType::TABLE );
return !bCreatingView;
}
Any SAL_CALL OQueryController::getViewData()
{
::osl::MutexGuard aGuard( getMutex() );
getContainer()->SaveUIConfig();
::comphelper::NamedValueCollection aViewSettings;
saveViewSettings( aViewSettings, false );
return Any( aViewSettings.getPropertyValues() );
}
void SAL_CALL OQueryController::restoreViewData(const Any& /*Data*/)
{
// TODO
}
} // namespace dbaui
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 in Prozent C=97 H=98 G=97
¤ Dauer der Verarbeitung: 0.29 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland
2026-10-09
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126438
#Domains=1867298