Files
loongoffice/dbaccess/source/core/api/SingleSelectQueryComposer.cxx
Stephan Bergmann 33ecd0d5c4 Change OUStringLiteral from char[] to char16_t[]
This is a prerequisite for making conversion from OUStringLiteral to OUString
more efficient at least for C++20 (by replacing its internals with a constexpr-
generated sal_uString-compatible layout with a SAL_STRING_STATIC_FLAG refCount,
conditionally for C++20 for now).

For a configure-wise bare-bones build on Linux, size reported by `du -bs
instdir` grew by 118792 bytes from 1155636636 to 1155755428.

In most places just a u"..." string literal prefix had to be added.  In some
places

  char const a[] = "...";

variables have been changed to char16_t, and a few places required even further
changes to code (which prompted the addition of include/o3tl/string_view.hxx
helper function o3tl::equalsIgnoreAsciiCase and the additional
OUString::createFromAscii overload).

For all uses of macros expanding to string literals, the relevant uses have been
rewritten as

  u"" MACRO

instead of changing the macro definitions.  It should be possible to change at
least some of those macro definitions (and drop the u"" from their call sites)
in follow-up commits.

Change-Id: Iec4ef1a057d412d22443312d40c6a8a290dc6144
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/101483
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2020-08-28 08:07:09 +02:00

1882 lines
74 KiB
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 <string.h>
#include <sal/log.hxx>
#include <composertools.hxx>
#include <strings.hrc>
#include <core_resource.hxx>
#include <stringconstants.hxx>
#include "HelperCollections.hxx"
#include <SingleSelectQueryComposer.hxx>
#include <sqlbison.hxx>
#include <sdbcoretools.hxx>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/i18n/LocaleData.hpp>
#include <com/sun/star/script/Converter.hpp>
#include <com/sun/star/sdb/BooleanComparisonMode.hpp>
#include <com/sun/star/sdb/SQLFilterOperator.hpp>
#include <com/sun/star/sdb/XQueriesSupplier.hpp>
#include <com/sun/star/sdb/CommandType.hpp>
#include <com/sun/star/sdbc/ColumnSearch.hpp>
#include <com/sun/star/sdbc/DataType.hpp>
#include <com/sun/star/sdbc/XConnection.hpp>
#include <com/sun/star/sdbc/XResultSetMetaData.hpp>
#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
#include <com/sun/star/sdbc/XParameters.hpp>
#include <com/sun/star/util/NumberFormatter.hpp>
#include <comphelper/types.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <connectivity/dbtools.hxx>
#include <connectivity/PColumn.hxx>
#include <connectivity/predicateinput.hxx>
#include <tools/diagnose_ex.h>
#include <osl/diagnose.h>
#include <unotools/sharedunocomponent.hxx>
#include <memory>
using namespace ::dbaccess;
using namespace ::dbtools;
using namespace ::comphelper;
using namespace ::connectivity;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::sdb;
using namespace ::com::sun::star::sdbcx;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::i18n;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::script;
using namespace ::com::sun::star::util;
using namespace ::cppu;
using namespace ::osl;
using namespace ::utl;
namespace dbaccess {
namespace BooleanComparisonMode = ::com::sun::star::sdb::BooleanComparisonMode;
}
#define STR_SELECT "SELECT "
#define STR_FROM " FROM "
#define STR_WHERE " WHERE "
#define STR_GROUP_BY " GROUP BY "
#define STR_HAVING " HAVING "
#define STR_ORDER_BY " ORDER BY "
#define STR_AND " AND "
#define STR_OR " OR "
#define STR_LIKE OUString(" LIKE ")
#define L_BRACKET "("
#define R_BRACKET ")"
#define COMMA ","
namespace
{
/** parses the given statement, using the given parser, returns a parse node representing
the statement
If the statement cannot be parsed, an error is thrown.
*/
std::unique_ptr<OSQLParseNode> parseStatement_throwError( OSQLParser& _rParser, const OUString& _rStatement, const Reference< XInterface >& _rxContext )
{
OUString aErrorMsg;
std::unique_ptr<OSQLParseNode> pNewSqlParseNode = _rParser.parseTree( aErrorMsg, _rStatement );
if ( !pNewSqlParseNode )
{
OUString sSQLStateGeneralError( getStandardSQLState( StandardSQLState::GENERAL_ERROR ) );
SQLException aError2( aErrorMsg, _rxContext, sSQLStateGeneralError, 1000, Any() );
SQLException aError1( _rStatement, _rxContext, sSQLStateGeneralError, 1000, makeAny( aError2 ) );
throw SQLException(_rParser.getContext().getErrorMessage(OParseContext::ErrorCode::General),_rxContext,sSQLStateGeneralError,1000,makeAny(aError1));
}
return pNewSqlParseNode;
}
/** checks whether the given parse node describes a valid single select statement, throws
an error if not
*/
void checkForSingleSelect_throwError( const OSQLParseNode* pStatementNode, OSQLParseTreeIterator& _rIterator,
const Reference< XInterface >& _rxContext, const OUString& _rOriginatingCommand )
{
const OSQLParseNode* pOldNode = _rIterator.getParseTree();
// determine the statement type
_rIterator.setParseTree( pStatementNode );
_rIterator.traverseAll();
bool bIsSingleSelect = ( _rIterator.getStatementType() == OSQLStatementType::Select );
// throw the error, if necessary
if ( !bIsSingleSelect || SQL_ISRULE( pStatementNode, union_statement ) ) // #i4229# OJ
{
// restore the old node before throwing the exception
_rIterator.setParseTree( pOldNode );
// and now really ...
SQLException aError1( _rOriginatingCommand, _rxContext, getStandardSQLState( StandardSQLState::GENERAL_ERROR ), 1000, Any() );
throw SQLException( DBA_RES( RID_STR_ONLY_QUERY ), _rxContext,
getStandardSQLState( StandardSQLState::GENERAL_ERROR ), 1000, makeAny( aError1 ) );
}
delete pOldNode;
}
/** combines parseStatement_throwError and checkForSingleSelect_throwError
*/
void parseAndCheck_throwError( OSQLParser& _rParser, const OUString& _rStatement,
OSQLParseTreeIterator& _rIterator, const Reference< XInterface >& _rxContext )
{
std::unique_ptr<OSQLParseNode> pNode = parseStatement_throwError( _rParser, _rStatement, _rxContext );
checkForSingleSelect_throwError( pNode.release(), _rIterator, _rxContext, _rStatement );
}
/** transforms a parse node describing a complete statement into a pure select
statement, without any filter/order/groupby/having clauses
*/
OUString getPureSelectStatement( const OSQLParseNode* _pRootNode, const Reference< XConnection >& _rxConnection )
{
OUString sSQL = STR_SELECT;
_pRootNode->getChild(1)->parseNodeToStr( sSQL, _rxConnection );
_pRootNode->getChild(2)->parseNodeToStr( sSQL, _rxConnection );
sSQL += STR_FROM;
_pRootNode->getChild(3)->getChild(0)->getChild(1)->parseNodeToStr( sSQL, _rxConnection );
return sSQL;
}
/** resets an SQL iterator, including deletion of the parse tree, and dispose
*/
void resetIterator( OSQLParseTreeIterator& _rIterator )
{
const OSQLParseNode* pSqlParseNode = _rIterator.getParseTree();
_rIterator.setParseTree(nullptr);
delete pSqlParseNode;
_rIterator.dispose();
}
void lcl_addFilterCriteria_throw(sal_Int32 i_nFilterOperator,const OUString& i_sValue,OUStringBuffer& o_sRet)
{
switch( i_nFilterOperator )
{
case SQLFilterOperator::EQUAL:
o_sRet.append(" = " ).append( i_sValue);
break;
case SQLFilterOperator::NOT_EQUAL:
o_sRet.append(" <> " ).append( i_sValue);
break;
case SQLFilterOperator::LESS:
o_sRet.append(" < " ).append( i_sValue);
break;
case SQLFilterOperator::GREATER:
o_sRet.append(" > " ).append( i_sValue);
break;
case SQLFilterOperator::LESS_EQUAL:
o_sRet.append(" <= " ).append( i_sValue);
break;
case SQLFilterOperator::GREATER_EQUAL:
o_sRet.append(" >= " ).append( i_sValue);
break;
case SQLFilterOperator::LIKE:
o_sRet.append(" LIKE " ).append( i_sValue);
break;
case SQLFilterOperator::NOT_LIKE:
o_sRet.append(" NOT LIKE " ).append( i_sValue);
break;
case SQLFilterOperator::SQLNULL:
o_sRet.append(" IS NULL");
break;
case SQLFilterOperator::NOT_SQLNULL:
o_sRet.append(" IS NOT NULL");
break;
default:
throw SQLException();
}
}
}
OSingleSelectQueryComposer::OSingleSelectQueryComposer(const Reference< XNameAccess>& _rxTables,
const Reference< XConnection>& _xConnection,
const Reference<XComponentContext>& _rContext )
:OSubComponent(m_aMutex,_xConnection)
,OPropertyContainer(m_aBHelper)
,m_aSqlParser( _rContext, &m_aParseContext )
,m_aSqlIterator( _xConnection, _rxTables, m_aSqlParser )
,m_aAdditiveIterator( _xConnection, _rxTables, m_aSqlParser )
,m_aElementaryParts( size_t(SQLPartCount) )
,m_xConnection(_xConnection)
,m_xMetaData(_xConnection->getMetaData())
,m_xConnectionTables( _rxTables )
,m_aContext( _rContext )
,m_nBoolCompareMode( BooleanComparisonMode::EQUAL_INTEGER )
,m_nCommandType(CommandType::COMMAND)
{
if ( !m_aContext.is() || !m_xConnection.is() || !m_xConnectionTables.is() )
throw IllegalArgumentException();
registerProperty(PROPERTY_ORIGINAL,PROPERTY_ID_ORIGINAL,PropertyAttribute::BOUND|PropertyAttribute::READONLY,&m_sOriginal,cppu::UnoType<decltype(m_sOriginal)>::get());
m_aCurrentColumns.resize(4);
m_aLocale = m_aParseContext.getPreferredLocale();
m_xNumberFormatsSupplier = dbtools::getNumberFormats( m_xConnection, true, m_aContext );
Reference< XLocaleData4 > xLocaleData( LocaleData::create(m_aContext) );
LocaleDataItem aData = xLocaleData->getLocaleItem(m_aLocale);
m_sDecimalSep = aData.decimalSeparator;
OSL_ENSURE(m_sDecimalSep.getLength() == 1,"OSingleSelectQueryComposer::OSingleSelectQueryComposer decimal separator is not 1 length");
try
{
Any aValue;
Reference<XInterface> xDs = dbaccess::getDataSource(_xConnection);
if ( dbtools::getDataSourceSetting(xDs,static_cast <OUString> (PROPERTY_BOOLEANCOMPARISONMODE),aValue) )
{
OSL_VERIFY( aValue >>= m_nBoolCompareMode );
}
Reference< XQueriesSupplier > xQueriesAccess(m_xConnection, UNO_QUERY);
if (xQueriesAccess.is())
m_xConnectionQueries = xQueriesAccess->getQueries();
}
catch(Exception&)
{
}
}
OSingleSelectQueryComposer::~OSingleSelectQueryComposer()
{
}
// OComponentHelper
void SAL_CALL OSingleSelectQueryComposer::disposing()
{
OSubComponent::disposing();
MutexGuard aGuard(m_aMutex);
resetIterator( m_aSqlIterator );
resetIterator( m_aAdditiveIterator );
m_xConnectionTables = nullptr;
m_xConnection = nullptr;
clearCurrentCollections();
}
IMPLEMENT_FORWARD_XINTERFACE3(OSingleSelectQueryComposer,OSubComponent,OSingleSelectQueryComposer_BASE,OPropertyContainer)
IMPLEMENT_SERVICE_INFO1(OSingleSelectQueryComposer,"org.openoffice.comp.dba.OSingleSelectQueryComposer",SERVICE_NAME_SINGLESELECTQUERYCOMPOSER)
css::uno::Sequence<sal_Int8> OSingleSelectQueryComposer::getImplementationId()
{
return css::uno::Sequence<sal_Int8>();
}
IMPLEMENT_GETTYPES3(OSingleSelectQueryComposer,OSubComponent,OSingleSelectQueryComposer_BASE,OPropertyContainer)
IMPLEMENT_PROPERTYCONTAINER_DEFAULTS(OSingleSelectQueryComposer)
// XSingleSelectQueryAnalyzer
OUString SAL_CALL OSingleSelectQueryComposer::getQuery( )
{
::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed);
::osl::MutexGuard aGuard( m_aMutex );
TGetParseNode F_tmp(&OSQLParseTreeIterator::getParseTree);
return getStatementPart(F_tmp,m_aSqlIterator);
}
void SAL_CALL OSingleSelectQueryComposer::setQuery( const OUString& command )
{
::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed);
::osl::MutexGuard aGuard( m_aMutex );
m_nCommandType = CommandType::COMMAND;
// first clear the tables and columns
clearCurrentCollections();
// now set the new one
setQuery_Impl(command);
m_sOriginal = command;
// reset the additive iterator to the same statement
parseAndCheck_throwError( m_aSqlParser, m_sOriginal, m_aAdditiveIterator, *this );
// we have no "elementary" parts anymore (means filter/groupby/having/order clauses)
for ( SQLPart eLoopParts = Where; eLoopParts != SQLPartCount; incSQLPart( eLoopParts ) )
m_aElementaryParts[ eLoopParts ].clear();
}
void SAL_CALL OSingleSelectQueryComposer::setCommand( const OUString& Command,sal_Int32 _nCommandType )
{
OUStringBuffer sSQL;
switch(_nCommandType)
{
case CommandType::COMMAND:
setElementaryQuery(Command);
return;
case CommandType::TABLE:
if ( m_xConnectionTables->hasByName(Command) )
{
sSQL.append("SELECT * FROM ");
Reference< XPropertySet > xTable;
try
{
m_xConnectionTables->getByName( Command ) >>= xTable;
}
catch(const WrappedTargetException& e)
{
SQLException e2;
if ( e.TargetException >>= e2 )
throw e2;
}
catch(Exception&)
{
DBG_UNHANDLED_EXCEPTION("dbaccess");
}
sSQL.append(dbtools::composeTableNameForSelect(m_xConnection,xTable));
}
else
{
OUString sMessage( DBA_RES( RID_STR_TABLE_DOES_NOT_EXIST ) );
throwGenericSQLException(sMessage.replaceAll( "$table$", Command ),*this);
}
break;
case CommandType::QUERY:
if ( m_xConnectionQueries->hasByName(Command) )
{
Reference<XPropertySet> xQuery(m_xConnectionQueries->getByName(Command),UNO_QUERY);
OUString sCommand;
xQuery->getPropertyValue(PROPERTY_COMMAND) >>= sCommand;
sSQL.append(sCommand);
}
else
{
OUString sMessage( DBA_RES( RID_STR_QUERY_DOES_NOT_EXIST ) );
throwGenericSQLException(sMessage.replaceAll( "$table$", Command ),*this);
}
break;
default:
break;
}
::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed);
::osl::MutexGuard aGuard( m_aMutex );
m_nCommandType = _nCommandType;
m_sCommand = Command;
// first clear the tables and columns
clearCurrentCollections();
// now set the new one
OUString sCommand = sSQL.makeStringAndClear();
setElementaryQuery(sCommand);
m_sOriginal = sCommand;
}
void OSingleSelectQueryComposer::setQuery_Impl( const OUString& command )
{
// parse this
parseAndCheck_throwError( m_aSqlParser, command, m_aSqlIterator, *this );
// strip it from all clauses, to have the pure SELECT statement
m_aPureSelectSQL = getPureSelectStatement( m_aSqlIterator.getParseTree(), m_xConnection );
// update tables
getTables();
}
Sequence< Sequence< PropertyValue > > SAL_CALL OSingleSelectQueryComposer::getStructuredHavingClause( )
{
TGetParseNode F_tmp(&OSQLParseTreeIterator::getSimpleHavingTree);
return getStructuredCondition(F_tmp);
}
Sequence< Sequence< PropertyValue > > SAL_CALL OSingleSelectQueryComposer::getStructuredFilter( )
{
TGetParseNode F_tmp(&OSQLParseTreeIterator::getSimpleWhereTree);
return getStructuredCondition(F_tmp);
}
void SAL_CALL OSingleSelectQueryComposer::appendHavingClauseByColumn( const Reference< XPropertySet >& column, sal_Bool andCriteria,sal_Int32 filterOperator )
{
auto F_tmp = std::mem_fn(&OSingleSelectQueryComposer::implSetHavingClause);
setConditionByColumn(column,andCriteria,F_tmp,filterOperator);
}
void SAL_CALL OSingleSelectQueryComposer::appendFilterByColumn( const Reference< XPropertySet >& column, sal_Bool andCriteria,sal_Int32 filterOperator )
{
auto F_tmp = std::mem_fn(&OSingleSelectQueryComposer::implSetFilter);
setConditionByColumn(column,andCriteria,F_tmp,filterOperator);
}
OUString OSingleSelectQueryComposer::impl_getColumnRealName_throw(const Reference< XPropertySet >& column, bool bGroupBy)
{
::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed);
getColumns();
if ( !column.is()
|| !m_aCurrentColumns[SelectColumns]
|| !column->getPropertySetInfo()->hasPropertyByName(PROPERTY_NAME)
)
{
OUString sError(DBA_RES(RID_STR_COLUMN_UNKNOWN_PROP));
SQLException aErr(sError.replaceAll("%value", PROPERTY_NAME),*this,SQLSTATE_GENERAL,1000,Any() );
throw SQLException(DBA_RES(RID_STR_COLUMN_NOT_VALID),*this,SQLSTATE_GENERAL,1000,makeAny(aErr) );
}
OUString aName, aNewName;
column->getPropertyValue(PROPERTY_NAME) >>= aName;
if ( bGroupBy &&
!m_xMetaData->supportsGroupByUnrelated() &&
m_aCurrentColumns[SelectColumns] &&
!m_aCurrentColumns[SelectColumns]->hasByName(aName) )
{
OUString sError(DBA_RES(RID_STR_COLUMN_MUST_VISIBLE));
throw SQLException(sError.replaceAll("%name", aName),*this,SQLSTATE_GENERAL,1000,Any() );
}
OUString aQuote = m_xMetaData->getIdentifierQuoteString();
if ( m_aCurrentColumns[SelectColumns]->hasByName(aName) )
{
Reference<XPropertySet> xColumn;
m_aCurrentColumns[SelectColumns]->getByName(aName) >>= xColumn;
OSL_ENSURE(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_REALNAME),"Property REALNAME not available!");
OSL_ENSURE(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_TABLENAME),"Property TABLENAME not available!");
OSL_ENSURE(xColumn->getPropertySetInfo()->hasPropertyByName("Function"),"Property FUNCTION not available!");
OUString sRealName, sTableName;
xColumn->getPropertyValue(PROPERTY_REALNAME) >>= sRealName;
xColumn->getPropertyValue(PROPERTY_TABLENAME) >>= sTableName;
bool bFunction = false;
xColumn->getPropertyValue("Function") >>= bFunction;
if ( sRealName == aName )
{
if ( bFunction )
aNewName = aName;
else
{
if(sTableName.indexOf('.') != -1)
{
OUString aCatlog,aSchema,aTable;
::dbtools::qualifiedNameComponents(m_xMetaData,sTableName,aCatlog,aSchema,aTable,::dbtools::EComposeRule::InDataManipulation);
sTableName = ::dbtools::composeTableName( m_xMetaData, aCatlog, aSchema, aTable, true, ::dbtools::EComposeRule::InDataManipulation );
}
else if (!sTableName.isEmpty())
sTableName = ::dbtools::quoteName(aQuote,sTableName);
if(sTableName.isEmpty())
aNewName = ::dbtools::quoteName(aQuote,sRealName);
else
aNewName = sTableName + "." + ::dbtools::quoteName(aQuote,sRealName);
}
}
else
aNewName = ::dbtools::quoteName(aQuote,aName);
}
else
aNewName = getTableAlias(column) + ::dbtools::quoteName(aQuote,aName);
return aNewName;
}
OUString OSingleSelectQueryComposer::impl_getColumnNameOrderBy_throw(const Reference< XPropertySet >& column)
{
::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed);
getColumns();
if ( !column.is()
|| !m_aCurrentColumns[SelectColumns]
|| !column->getPropertySetInfo()->hasPropertyByName(PROPERTY_NAME)
)
{
OUString sError(DBA_RES(RID_STR_COLUMN_UNKNOWN_PROP));
SQLException aErr(sError.replaceAll("%value", PROPERTY_NAME),*this,SQLSTATE_GENERAL,1000,Any() );
throw SQLException(DBA_RES(RID_STR_COLUMN_NOT_VALID),*this,SQLSTATE_GENERAL,1000,makeAny(aErr) );
}
OUString aName;
column->getPropertyValue(PROPERTY_NAME) >>= aName;
const OUString aQuote = m_xMetaData->getIdentifierQuoteString();
if ( m_aCurrentColumns[SelectColumns] &&
m_aCurrentColumns[SelectColumns]->hasByName(aName) )
{
// It is a column from the SELECT list, use it as such.
return ::dbtools::quoteName(aQuote,aName);
}
// Nope, it is an unrelated column.
// Is that supported?
if ( !m_xMetaData->supportsOrderByUnrelated() )
{
OUString sError(DBA_RES(RID_STR_COLUMN_MUST_VISIBLE));
throw SQLException(sError.replaceAll("%name", aName),*this,SQLSTATE_GENERAL,1000,Any() );
}
// We need to refer to it by its "real" name, that is by schemaName.tableName.columnNameInTable
return impl_getColumnRealName_throw(column, false);
}
void SAL_CALL OSingleSelectQueryComposer::appendOrderByColumn( const Reference< XPropertySet >& column, sal_Bool ascending )
{
::osl::MutexGuard aGuard( m_aMutex );
OUString sColumnName( impl_getColumnNameOrderBy_throw(column) );
OUString sOrder = getOrder();
if ( !(sOrder.isEmpty() || sColumnName.isEmpty()) )
sOrder += COMMA;
sOrder += sColumnName;
if ( !(ascending || sColumnName.isEmpty()) )
sOrder += " DESC ";
setOrder(sOrder);
}
void SAL_CALL OSingleSelectQueryComposer::appendGroupByColumn( const Reference< XPropertySet >& column)
{
::osl::MutexGuard aGuard( m_aMutex );
OUString sColumnName( impl_getColumnRealName_throw(column, true) );
OrderCreator aComposer;
aComposer.append( getGroup() );
aComposer.append( sColumnName );
setGroup( aComposer.getComposedAndClear() );
}
OUString OSingleSelectQueryComposer::composeStatementFromParts( const std::vector< OUString >& _rParts )
{
OSL_ENSURE( _rParts.size() == size_t(SQLPartCount), "OSingleSelectQueryComposer::composeStatementFromParts: invalid parts array!" );
OUStringBuffer aSql( m_aPureSelectSQL );
for ( SQLPart eLoopParts = Where; eLoopParts != SQLPartCount; incSQLPart( eLoopParts ) )
if ( !_rParts[ eLoopParts ].isEmpty() )
{
aSql.append( getKeyword( eLoopParts ) );
aSql.append( _rParts[ eLoopParts ] );
}
return aSql.makeStringAndClear();
}
OUString SAL_CALL OSingleSelectQueryComposer::getElementaryQuery()
{
return composeStatementFromParts( m_aElementaryParts );
}
void SAL_CALL OSingleSelectQueryComposer::setElementaryQuery( const OUString& _rElementary )
{
::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed);
::osl::MutexGuard aGuard( m_aMutex );
// remember the 4 current "additive" clauses
std::vector< OUString > aAdditiveClauses( SQLPartCount );
for ( SQLPart eLoopParts = Where; eLoopParts != SQLPartCount; incSQLPart( eLoopParts ) )
aAdditiveClauses[ eLoopParts ] = getSQLPart( eLoopParts, m_aAdditiveIterator, false );
// clear the tables and columns
clearCurrentCollections();
// set and parse the new query
setQuery_Impl( _rElementary );
// get the 4 elementary parts of the statement
for ( SQLPart eLoopParts = Where; eLoopParts != SQLPartCount; incSQLPart( eLoopParts ) )
m_aElementaryParts[ eLoopParts ] = getSQLPart( eLoopParts, m_aSqlIterator, false );
// reset the AdditiveIterator: m_aPureSelectSQL may have changed
try
{
parseAndCheck_throwError( m_aSqlParser, composeStatementFromParts( aAdditiveClauses ), m_aAdditiveIterator, *this );
}
catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("dbaccess");
SAL_WARN("dbaccess", "OSingleSelectQueryComposer::setElementaryQuery: there should be no error anymore for the additive statement!" );
// every part of the additive statement should have passed other tests already, and should not
// be able to cause any errors ... me thinks
}
}
namespace
{
OUString getComposedClause( const OUString& _rElementaryClause, const OUString& _rAdditionalClause,
TokenComposer& _rComposer, const OUString& _rKeyword )
{
_rComposer.clear();
_rComposer.append( _rElementaryClause );
_rComposer.append( _rAdditionalClause );
OUString sComposed = _rComposer.getComposedAndClear();
if ( !sComposed.isEmpty() )
sComposed = _rKeyword + sComposed;
return sComposed;
}
}
void OSingleSelectQueryComposer::setSingleAdditiveClause( SQLPart _ePart, const OUString& _rClause )
{
::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed);
::osl::MutexGuard aGuard( m_aMutex );
// if nothing is changed, do nothing
if ( getSQLPart( _ePart, m_aAdditiveIterator, false ) == _rClause )
return;
// collect the 4 single parts as they're currently set
std::vector< OUString > aClauses;
aClauses.reserve( size_t(SQLPartCount) );
for ( SQLPart eLoopParts = Where; eLoopParts != SQLPartCount; incSQLPart( eLoopParts ) )
aClauses.push_back( getSQLPart( eLoopParts, m_aSqlIterator, true ) );
// overwrite the one part in question here
std::unique_ptr< TokenComposer > pComposer;
if ( ( _ePart == Where ) || ( _ePart == Having ) )
pComposer.reset( new FilterCreator );
else
pComposer.reset( new OrderCreator );
aClauses[ _ePart ] = getComposedClause( m_aElementaryParts[ _ePart ], _rClause,
*pComposer, getKeyword( _ePart ) );
// construct the complete statement
OUStringBuffer aSql(m_aPureSelectSQL);
for ( SQLPart eLoopParts = Where; eLoopParts != SQLPartCount; incSQLPart( eLoopParts ) )
aSql.append(aClauses[ eLoopParts ]);
// set the query
setQuery_Impl(aSql.makeStringAndClear());
// clear column collections which (might) have changed
clearColumns( ParameterColumns );
if ( _ePart == Order )
clearColumns( OrderColumns );
else if ( _ePart == Group )
clearColumns( GroupByColumns );
// also, since the "additive filter" change, we need to rebuild our "additive" statement
aSql = m_aPureSelectSQL;
// again, first get all the old additive parts
for ( SQLPart eLoopParts = Where; eLoopParts != SQLPartCount; incSQLPart( eLoopParts ) )
aClauses[ eLoopParts ] = getSQLPart( eLoopParts, m_aAdditiveIterator, true );
// then overwrite the one in question
aClauses[ _ePart ] = getComposedClause( OUString(), _rClause, *pComposer, getKeyword( _ePart ) );
// and parse it, so that m_aAdditiveIterator is up to date
for ( SQLPart eLoopParts = Where; eLoopParts != SQLPartCount; incSQLPart( eLoopParts ) )
aSql.append(aClauses[ eLoopParts ]);
try
{
parseAndCheck_throwError( m_aSqlParser, aSql.makeStringAndClear(), m_aAdditiveIterator, *this );
}
catch( const Exception& )
{
SAL_WARN("dbaccess", "OSingleSelectQueryComposer::setSingleAdditiveClause: there should be no error anymore for the additive statement!" );
// every part of the additive statement should have passed other tests already, and should not
// be able to cause any errors ... me thinks
}
}
void SAL_CALL OSingleSelectQueryComposer::setFilter( const OUString& filter )
{
setSingleAdditiveClause( Where, filter );
}
void SAL_CALL OSingleSelectQueryComposer::setOrder( const OUString& order )
{
setSingleAdditiveClause( Order, order );
}
void SAL_CALL OSingleSelectQueryComposer::setGroup( const OUString& group )
{
setSingleAdditiveClause( Group, group );
}
void SAL_CALL OSingleSelectQueryComposer::setHavingClause( const OUString& filter )
{
setSingleAdditiveClause( Having, filter );
}
// XTablesSupplier
Reference< XNameAccess > SAL_CALL OSingleSelectQueryComposer::getTables( )
{
::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed);
::osl::MutexGuard aGuard( m_aMutex );
if ( !m_pTables )
{
const OSQLTables& aTables = m_aSqlIterator.getTables();
std::vector< OUString> aNames;
for (auto const& elem : aTables)
aNames.push_back(elem.first);
m_pTables.reset( new OPrivateTables(aTables,m_xMetaData->supportsMixedCaseQuotedIdentifiers(),*this,m_aMutex,aNames) );
}
return m_pTables.get();
}
// XColumnsSupplier
Reference< XNameAccess > SAL_CALL OSingleSelectQueryComposer::getColumns( )
{
::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed);
::osl::MutexGuard aGuard( m_aMutex );
if ( !!m_aCurrentColumns[SelectColumns] )
return m_aCurrentColumns[SelectColumns].get();
std::vector< OUString> aNames;
::rtl::Reference< OSQLColumns> aSelectColumns;
bool bCase = true;
Reference< XNameAccess> xQueryColumns;
if ( m_nCommandType == CommandType::QUERY )
{
Reference<XColumnsSupplier> xSup(m_xConnectionQueries->getByName(m_sCommand),UNO_QUERY);
if(xSup.is())
xQueryColumns = xSup->getColumns();
}
do {
try
{
SharedUNOComponent< XStatement, DisposableComponent > xStatement;
SharedUNOComponent< XPreparedStatement, DisposableComponent > xPreparedStatement;
bCase = m_xMetaData->supportsMixedCaseQuotedIdentifiers();
aSelectColumns = m_aSqlIterator.getSelectColumns();
OUStringBuffer aSQL( m_aPureSelectSQL + STR_WHERE " ( 0 = 1 )");
// preserve the original WHERE clause
// #i102234#
OUString sOriginalWhereClause = getSQLPart( Where, m_aSqlIterator, false );
if ( !sOriginalWhereClause.isEmpty() )
{
aSQL.append( " AND ( " ).append( sOriginalWhereClause ).append( " ) " );
}
OUString sGroupBy = getSQLPart( Group, m_aSqlIterator, true );
if ( !sGroupBy.isEmpty() )
aSQL.append( sGroupBy );
OUString sSQL( aSQL.makeStringAndClear() );
// normalize the statement so that it doesn't contain any application-level features anymore
OUString sError;
const std::unique_ptr< OSQLParseNode > pStatementTree( m_aSqlParser.parseTree( sError, sSQL ) );
OSL_ENSURE(pStatementTree, "OSingleSelectQueryComposer::getColumns: could not parse the "
"column retrieval statement!");
if (pStatementTree)
if ( !pStatementTree->parseNodeToExecutableStatement( sSQL, m_xConnection, m_aSqlParser, nullptr ) )
break;
Reference< XResultSetMetaData > xResultSetMeta;
Reference< XResultSetMetaDataSupplier > xResMetaDataSup;
try
{
xPreparedStatement.set( m_xConnection->prepareStatement( sSQL ), UNO_QUERY_THROW );
xResMetaDataSup.set( xPreparedStatement, UNO_QUERY_THROW );
xResultSetMeta.set( xResMetaDataSup->getMetaData(), UNO_SET_THROW );
}
catch( const Exception& ) { }
if ( !xResultSetMeta.is() && xPreparedStatement.is() )
{
try
{
//@see issue http://qa.openoffice.org/issues/show_bug.cgi?id=110111
// access returns a different order of column names when executing select * from
// and asking the columns from the metadata.
Reference< XParameters > xParameters( xPreparedStatement, UNO_QUERY_THROW );
Reference< XIndexAccess > xPara = getParameters();
for(sal_Int32 i = 1;i <= xPara->getCount();++i)
xParameters->setNull(i,DataType::VARCHAR);
xResMetaDataSup.set(xPreparedStatement->executeQuery(), UNO_QUERY_THROW );
xResultSetMeta.set( xResMetaDataSup->getMetaData(), UNO_SET_THROW );
}
catch( const Exception& ) { }
}
if ( !xResultSetMeta.is() )
{
xStatement.reset( Reference< XStatement >( m_xConnection->createStatement(), UNO_SET_THROW ) );
Reference< XPropertySet > xStatementProps( xStatement, UNO_QUERY_THROW );
try { xStatementProps->setPropertyValue( PROPERTY_ESCAPE_PROCESSING, makeAny( false ) ); }
catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("dbaccess"); }
xResMetaDataSup.set( xStatement->executeQuery( sSQL ), UNO_QUERY_THROW );
xResultSetMeta.set( xResMetaDataSup->getMetaData(), UNO_SET_THROW );
if (xResultSetMeta.is())
{
SAL_WARN("dbaccess", "OSingleSelectQueryComposer::getColumns failed to get xResultSetMeta from executed PreparedStatement, but got it from 'no escape processing' statement. SQL command:\n\t" << sSQL );
}
}
if ( aSelectColumns->empty() )
{
// This is a valid case. If we can syntactically parse the query, but not semantically
// (e.g. because it is based on a table we do not know), then there will be no SelectColumns
aSelectColumns = ::connectivity::parse::OParseColumn::createColumnsForResultSet( xResultSetMeta, m_xMetaData ,xQueryColumns);
break;
}
const ::comphelper::UStringMixEqual aCaseCompare( bCase );
std::set< size_t > aUsedSelectColumns;
::connectivity::parse::OParseColumn::StringMap aColumnNames;
sal_Int32 nCount = xResultSetMeta->getColumnCount();
OSL_ENSURE( static_cast<size_t>(nCount) == aSelectColumns->size(), "OSingleSelectQueryComposer::getColumns: inconsistent column counts, this might result in wrong columns!" );
for(sal_Int32 i=1;i<=nCount;++i)
{
OUString sColumnName = xResultSetMeta->getColumnName(i);
OUString sColumnLabel;
if ( xQueryColumns.is() && xQueryColumns->hasByName(sColumnName) )
{
Reference<XPropertySet> xQueryColumn(xQueryColumns->getByName(sColumnName),UNO_QUERY_THROW);
xQueryColumn->getPropertyValue(PROPERTY_LABEL) >>= sColumnLabel;
}
else
sColumnLabel = xResultSetMeta->getColumnLabel(i);
bool bFound = false;
OSQLColumns::Vector::const_iterator aFind = ::connectivity::find(aSelectColumns->begin(),aSelectColumns->end(),sColumnLabel,aCaseCompare);
size_t nFoundSelectColumnPos = aFind - aSelectColumns->begin();
if ( aFind != aSelectColumns->end() )
{
if ( aUsedSelectColumns.find( nFoundSelectColumnPos ) != aUsedSelectColumns.end() )
{ // we found a column name which exists twice
// so we start after the first found
do
{
aFind = ::connectivity::findRealName(++aFind,aSelectColumns->end(),sColumnName,aCaseCompare);
nFoundSelectColumnPos = aFind - aSelectColumns->begin();
}
while ( ( aUsedSelectColumns.find( nFoundSelectColumnPos ) != aUsedSelectColumns.end() )
&& ( aFind != aSelectColumns->end() )
);
}
if ( aFind != aSelectColumns->end() )
{
(*aFind)->getPropertyValue(PROPERTY_NAME) >>= sColumnName;
aUsedSelectColumns.insert( nFoundSelectColumnPos );
aNames.push_back(sColumnName);
bFound = true;
}
}
if ( bFound )
continue;
OSQLColumns::Vector::const_iterator aRealFind = ::connectivity::findRealName(
aSelectColumns->begin(), aSelectColumns->end(), sColumnName, aCaseCompare );
if ( i > static_cast< sal_Int32>( aSelectColumns->size() ) )
{
aSelectColumns->emplace_back(::connectivity::parse::OParseColumn::createColumnForResultSet( xResultSetMeta, m_xMetaData, i ,aColumnNames)
);
OSL_ENSURE( aSelectColumns->size() == static_cast<size_t>(i), "OSingleSelectQueryComposer::getColumns: inconsistency!" );
}
else if ( aRealFind == aSelectColumns->end() )
{
// we can now only look if we found it under the realname property
// here we have to make the assumption that the position is correct
OSQLColumns::Vector::const_iterator aFind2 = aSelectColumns->begin() + i-1;
Reference<XPropertySet> xProp = *aFind2;
if ( !xProp.is() || !xProp->getPropertySetInfo()->hasPropertyByName( PROPERTY_REALNAME ) )
continue;
::connectivity::parse::OParseColumn* pColumn = new ::connectivity::parse::OParseColumn(xProp,bCase);
pColumn->setFunction(::comphelper::getBOOL(xProp->getPropertyValue("Function")));
pColumn->setAggregateFunction(::comphelper::getBOOL(xProp->getPropertyValue("AggregateFunction")));
OUString sRealName;
xProp->getPropertyValue(PROPERTY_REALNAME) >>= sRealName;
if ( sColumnName.isEmpty() )
xProp->getPropertyValue(PROPERTY_NAME) >>= sColumnName;
sal_Int32 j = 0;
while ( std::any_of(aNames.begin(),aNames.end(),
[&aCaseCompare, &sColumnName](const OUString& lhs)
{ return aCaseCompare(lhs, sColumnName); } ) )
{
sColumnName += OUString::number(++j);
}
pColumn->setName(sColumnName);
pColumn->setRealName(sRealName);
pColumn->setTableName(::comphelper::getString(xProp->getPropertyValue(PROPERTY_TABLENAME)));
(*aSelectColumns)[i-1] = pColumn;
}
else
continue;
aUsedSelectColumns.insert( static_cast<size_t>(i - 1) );
aNames.push_back( sColumnName );
}
}
catch(const Exception&)
{
}
} while ( false );
bool bMissingSomeColumnLabels = !aNames.empty() && aNames.size() != aSelectColumns->size();
SAL_WARN_IF(bMissingSomeColumnLabels, "dbaccess", "We have column labels for *some* columns but not all");
//^^this happens in the evolution address book where we have real column names of e.g.
//first_name, second_name and city. On parsing via
//OSQLParseTreeIterator::appendColumns it creates some labels using those real names
//but the evo address book gives them proper labels of First Name, Second Name and City
//the munge means that here we have e.g. just "City" as a label because it matches
//This is all a horrible mess
if (bMissingSomeColumnLabels)
aNames.clear();
if ( aNames.empty() )
m_aCurrentColumns[ SelectColumns ] = OPrivateColumns::createWithIntrinsicNames( aSelectColumns, bCase, *this, m_aMutex );
else
m_aCurrentColumns[ SelectColumns ].reset( new OPrivateColumns( aSelectColumns, bCase, *this, m_aMutex, aNames ) );
return m_aCurrentColumns[SelectColumns].get();
}
bool OSingleSelectQueryComposer::setORCriteria(OSQLParseNode const * pCondition, OSQLParseTreeIterator& _rIterator,
std::vector< std::vector < PropertyValue > >& rFilters, const Reference< css::util::XNumberFormatter > & xFormatter) const
{
// Round brackets around the expression
if (pCondition->count() == 3 &&
SQL_ISPUNCTUATION(pCondition->getChild(0),"(") &&
SQL_ISPUNCTUATION(pCondition->getChild(2),")"))
{
return setORCriteria(pCondition->getChild(1), _rIterator, rFilters, xFormatter);
}
// OR logic expression
// a searchcondition can only look like this: search_condition SQL_TOKEN_OR boolean_term
else if (SQL_ISRULE(pCondition,search_condition))
{
bool bResult = true;
for (int i = 0; bResult && i < 3; i+=2)
{
// Is the first element a OR logic expression again?
// Then descend recursively ...
if (SQL_ISRULE(pCondition->getChild(i),search_condition))
bResult = setORCriteria(pCondition->getChild(i), _rIterator, rFilters, xFormatter);
else
{
rFilters.emplace_back();
bResult = setANDCriteria(pCondition->getChild(i), _rIterator, rFilters[rFilters.size() - 1], xFormatter);
}
}
return bResult;
}
else
{
rFilters.emplace_back();
return setANDCriteria(pCondition, _rIterator, rFilters[rFilters.size() - 1], xFormatter);
}
}
bool OSingleSelectQueryComposer::setANDCriteria( OSQLParseNode const * pCondition,
OSQLParseTreeIterator& _rIterator, std::vector < PropertyValue >& rFilter, const Reference< XNumberFormatter > & xFormatter) const
{
// Round brackets
if (SQL_ISRULE(pCondition,boolean_primary))
{
// this should not occur
SAL_WARN("dbaccess","boolean_primary in And-Criteria");
return false;
}
// The first element is an AND logical expression again
else if ( SQL_ISRULE(pCondition,boolean_term) && pCondition->count() == 3 )
{
return setANDCriteria(pCondition->getChild(0), _rIterator, rFilter, xFormatter) &&
setANDCriteria(pCondition->getChild(2), _rIterator, rFilter, xFormatter);
}
else if (SQL_ISRULE(pCondition, comparison_predicate))
{
return setComparisonPredicate(pCondition,_rIterator,rFilter,xFormatter);
}
else if (SQL_ISRULE(pCondition,like_predicate))
{
return setLikePredicate(pCondition,_rIterator,rFilter,xFormatter);
}
else if (SQL_ISRULE(pCondition,test_for_null) ||
SQL_ISRULE(pCondition,in_predicate) ||
SQL_ISRULE(pCondition,all_or_any_predicate) ||
SQL_ISRULE(pCondition,between_predicate))
{
if (SQL_ISRULE(pCondition->getChild(0), column_ref))
{
PropertyValue aItem;
OUString aValue;
OUString aColumnName;
pCondition->parseNodeToStr( aValue, m_xConnection );
pCondition->getChild(0)->parseNodeToStr( aColumnName, m_xConnection );
// don't display the column name
aValue = aValue.copy(aColumnName.getLength());
aValue = aValue.trim();
aItem.Name = getColumnName(pCondition->getChild(0),_rIterator);
aItem.Value <<= aValue;
aItem.Handle = 0; // just to know that this is not one the known ones
if ( SQL_ISRULE(pCondition,like_predicate) )
{
if ( SQL_ISTOKEN(pCondition->getChild(1)->getChild(0),NOT) )
aItem.Handle = SQLFilterOperator::NOT_LIKE;
else
aItem.Handle = SQLFilterOperator::LIKE;
}
else if (SQL_ISRULE(pCondition,test_for_null))
{
if (SQL_ISTOKEN(pCondition->getChild(1)->getChild(1),NOT) )
aItem.Handle = SQLFilterOperator::NOT_SQLNULL;
else
aItem.Handle = SQLFilterOperator::SQLNULL;
}
else if (SQL_ISRULE(pCondition,in_predicate))
{
SAL_WARN("dbaccess", "OSingleSelectQueryComposer::setANDCriteria: in_predicate not implemented!" );
}
else if (SQL_ISRULE(pCondition,all_or_any_predicate))
{
SAL_WARN("dbaccess", "OSingleSelectQueryComposer::setANDCriteria: all_or_any_predicate not implemented!" );
}
else if (SQL_ISRULE(pCondition,between_predicate))
{
SAL_WARN("dbaccess", "OSingleSelectQueryComposer::setANDCriteria: between_predicate not implemented!" );
}
rFilter.push_back(aItem);
}
else
return false;
}
else if (SQL_ISRULE(pCondition,existence_test) ||
SQL_ISRULE(pCondition,unique_test))
{
// this couldn't be handled here, too complex
// as we need a field name
return false;
}
else
return false;
return true;
}
sal_Int32 OSingleSelectQueryComposer::getPredicateType(OSQLParseNode const * _pPredicate)
{
sal_Int32 nPredicate = SQLFilterOperator::EQUAL;
switch (_pPredicate->getNodeType())
{
case SQLNodeType::Equal:
nPredicate = SQLFilterOperator::EQUAL;
break;
case SQLNodeType::NotEqual:
nPredicate = SQLFilterOperator::NOT_EQUAL;
break;
case SQLNodeType::Less:
nPredicate = SQLFilterOperator::LESS;
break;
case SQLNodeType::LessEq:
nPredicate = SQLFilterOperator::LESS_EQUAL;
break;
case SQLNodeType::Great:
nPredicate = SQLFilterOperator::GREATER;
break;
case SQLNodeType::GreatEq:
nPredicate = SQLFilterOperator::GREATER_EQUAL;
break;
default:
SAL_WARN("dbaccess","Wrong NodeType!");
}
return nPredicate;
}
bool OSingleSelectQueryComposer::setLikePredicate(OSQLParseNode const * pCondition, OSQLParseTreeIterator const & _rIterator,
std::vector < PropertyValue >& rFilter, const Reference< css::util::XNumberFormatter > & xFormatter) const
{
OSL_ENSURE(SQL_ISRULE(pCondition, like_predicate),"setLikePredicate: pCondition is not a LikePredicate");
assert(pCondition->count() == 2);
OSQLParseNode const *pRowValue = pCondition->getChild(0);
OSQLParseNode const *pPart2 = pCondition->getChild(1);
PropertyValue aItem;
if ( SQL_ISTOKEN(pPart2->getChild(0),NOT) )
aItem.Handle = SQLFilterOperator::NOT_LIKE;
else
aItem.Handle = SQLFilterOperator::LIKE;
if (SQL_ISRULE(pRowValue, column_ref))
{
OUString aValue;
// skip (optional "NOT") and "LIKE"
for (size_t i=2; i < pPart2->count(); i++)
{
pPart2->getChild(i)->parseNodeToPredicateStr(
aValue, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep );
}
aItem.Name = getColumnName(pRowValue,_rIterator);
aItem.Value <<= aValue;
rFilter.push_back(aItem);
}
else if (SQL_ISRULE(pRowValue, set_fct_spec ) ||
SQL_ISRULE(pRowValue, general_set_fct))
{
OUString aValue;
OUString aColumnName;
pPart2->getChild(2)->parseNodeToPredicateStr(aValue, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep );
pPart2->getChild(3)->parseNodeToPredicateStr(aValue, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep );
pRowValue->parseNodeToPredicateStr( aColumnName, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep );
aItem.Name = getColumnName(pRowValue,_rIterator);
aItem.Value <<= aValue;
rFilter.push_back(aItem);
}
else // Can only be an expression
{
OUString aName, aValue;
OSQLParseNode const *pValue = pPart2->getChild(2);
// Field names
for (size_t i=0;i< pRowValue->count();i++)
pRowValue->getChild(i)->parseNodeToPredicateStr( aName, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep );
// Criterion
for(size_t i=0;i< pValue->count();i++)
pValue->getChild(i)->parseNodeToPredicateStr(aValue, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep );
pPart2->getChild(3)->parseNodeToPredicateStr(aValue, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep );
aItem.Name = aName;
aItem.Value <<= aValue;
rFilter.push_back(aItem);
}
return true;
}
bool OSingleSelectQueryComposer::setComparisonPredicate(OSQLParseNode const * pCondition, OSQLParseTreeIterator const & _rIterator,
std::vector < PropertyValue >& rFilter, const Reference< css::util::XNumberFormatter > & xFormatter) const
{
OSL_ENSURE(SQL_ISRULE(pCondition, comparison_predicate),"setComparisonPredicate: pCondition is not a ComparisonPredicate");
if (SQL_ISRULE(pCondition->getChild(0), column_ref) ||
SQL_ISRULE(pCondition->getChild(pCondition->count()-1), column_ref))
{
PropertyValue aItem;
OUString aValue;
sal_uInt32 nPos;
if (SQL_ISRULE(pCondition->getChild(0), column_ref))
{
nPos = 0;
size_t i=1;
aItem.Handle = getPredicateType(pCondition->getChild(i));
// go forward - don't display the operator
for (i++;i < pCondition->count();i++)
pCondition->getChild(i)->parseNodeToPredicateStr(
aValue, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep );
}
else if (SQL_ISRULE(pCondition->getChild(pCondition->count()-1), column_ref))
{
nPos = pCondition->count()-1;
sal_Int32 i = pCondition->count() - 2;
switch (pCondition->getChild(i)->getNodeType())
{
case SQLNodeType::Equal:
aItem.Handle = SQLFilterOperator::EQUAL;
break;
case SQLNodeType::NotEqual:
aItem.Handle = SQLFilterOperator::NOT_EQUAL;
break;
case SQLNodeType::Less:
// take the opposite as we change the order
aItem.Handle = SQLFilterOperator::GREATER_EQUAL;
break;
case SQLNodeType::LessEq:
// take the opposite as we change the order
aItem.Handle = SQLFilterOperator::GREATER;
break;
case SQLNodeType::Great:
// take the opposite as we change the order
aItem.Handle = SQLFilterOperator::LESS_EQUAL;
break;
case SQLNodeType::GreatEq:
// take the opposite as we change the order
aItem.Handle = SQLFilterOperator::LESS;
break;
default:
break;
}
// go backward - don't display the operator
for (i--; i >= 0; i--)
pCondition->getChild(i)->parseNodeToPredicateStr(
aValue, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep );
}
else
return false;
aItem.Name = getColumnName(pCondition->getChild(nPos),_rIterator);
aItem.Value <<= aValue;
rFilter.push_back(aItem);
}
else if (SQL_ISRULE(pCondition->getChild(0), set_fct_spec ) ||
SQL_ISRULE(pCondition->getChild(0), general_set_fct))
{
PropertyValue aItem;
OUString aValue;
OUString aColumnName;
pCondition->getChild(2)->parseNodeToPredicateStr(aValue, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep );
pCondition->getChild(0)->parseNodeToPredicateStr( aColumnName, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep );
aItem.Name = getColumnName(pCondition->getChild(0),_rIterator);
aItem.Value <<= aValue;
aItem.Handle = getPredicateType(pCondition->getChild(1));
rFilter.push_back(aItem);
}
else // Can only be an expression
{
PropertyValue aItem;
OUString aName, aValue;
OSQLParseNode *pLhs = pCondition->getChild(0);
OSQLParseNode *pRhs = pCondition->getChild(2);
// Field names
for (size_t i=0;i< pLhs->count();i++)
pLhs->getChild(i)->parseNodeToPredicateStr( aName, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep );
// Criterion
aItem.Handle = getPredicateType(pCondition->getChild(1));
for(size_t i=0;i< pRhs->count();i++)
pRhs->getChild(i)->parseNodeToPredicateStr(aValue, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep );
aItem.Name = aName;
aItem.Value <<= aValue;
rFilter.push_back(aItem);
}
return true;
}
// Functions for analysing SQL
OUString OSingleSelectQueryComposer::getColumnName( ::connectivity::OSQLParseNode const * pColumnRef, OSQLParseTreeIterator const & _rIterator )
{
OUString aTableRange, aColumnName;
_rIterator.getColumnRange(pColumnRef,aColumnName,aTableRange);
return aColumnName;
}
OUString SAL_CALL OSingleSelectQueryComposer::getFilter( )
{
::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed);
::osl::MutexGuard aGuard( m_aMutex );
return getSQLPart(Where,m_aAdditiveIterator,false);
}
OUString SAL_CALL OSingleSelectQueryComposer::getOrder( )
{
::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed);
::osl::MutexGuard aGuard( m_aMutex );
return getSQLPart(Order,m_aAdditiveIterator,false);
}
OUString SAL_CALL OSingleSelectQueryComposer::getGroup( )
{
::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed);
::osl::MutexGuard aGuard( m_aMutex );
return getSQLPart(Group,m_aAdditiveIterator,false);
}
OUString OSingleSelectQueryComposer::getHavingClause()
{
::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed);
::osl::MutexGuard aGuard( m_aMutex );
return getSQLPart(Having,m_aAdditiveIterator,false);
}
OUString OSingleSelectQueryComposer::getTableAlias(const Reference< XPropertySet >& column) const
{
OUString sReturn;
if(m_pTables && m_pTables->getCount() > 1)
{
OUString aCatalog,aSchema,aTable,aComposedName,aColumnName;
if(column->getPropertySetInfo()->hasPropertyByName(PROPERTY_CATALOGNAME))
column->getPropertyValue(PROPERTY_CATALOGNAME) >>= aCatalog;
if(column->getPropertySetInfo()->hasPropertyByName(PROPERTY_SCHEMANAME))
column->getPropertyValue(PROPERTY_SCHEMANAME) >>= aSchema;
if(column->getPropertySetInfo()->hasPropertyByName(PROPERTY_TABLENAME))
column->getPropertyValue(PROPERTY_TABLENAME) >>= aTable;
column->getPropertyValue(PROPERTY_NAME) >>= aColumnName;
Sequence< OUString> aNames(m_pTables->getElementNames());
const OUString* pBegin = aNames.getConstArray();
const OUString* const pEnd = pBegin + aNames.getLength();
if(aTable.isEmpty())
{ // we haven't found a table name, now we must search every table for this column
for(;pBegin != pEnd;++pBegin)
{
Reference<XColumnsSupplier> xColumnsSupp;
m_pTables->getByName(*pBegin) >>= xColumnsSupp;
if(xColumnsSupp.is() && xColumnsSupp->getColumns()->hasByName(aColumnName))
{
aTable = *pBegin;
break;
}
}
}
else
{
aComposedName = ::dbtools::composeTableName( m_xMetaData, aCatalog, aSchema, aTable, false, ::dbtools::EComposeRule::InDataManipulation );
// Is this the right case for the table name?
// Else, look for it with different case, if applicable.
if(!m_pTables->hasByName(aComposedName))
{
::comphelper::UStringMixLess aTmp(m_aAdditiveIterator.getTables().key_comp());
::comphelper::UStringMixEqual aComp(aTmp.isCaseSensitive());
for(;pBegin != pEnd;++pBegin)
{
Reference<XPropertySet> xTableProp;
m_pTables->getByName(*pBegin) >>= xTableProp;
OSL_ENSURE(xTableProp.is(),"Table isn't a propertyset!");
if(xTableProp.is())
{
OUString aCatalog2,aSchema2,aTable2;
xTableProp->getPropertyValue(PROPERTY_CATALOGNAME) >>= aCatalog2;
xTableProp->getPropertyValue(PROPERTY_SCHEMANAME) >>= aSchema2;
xTableProp->getPropertyValue(PROPERTY_NAME) >>= aTable2;
if(aComp(aCatalog,aCatalog2) && aComp(aSchema,aSchema2) && aComp(aTable,aTable2))
{
aCatalog = aCatalog2;
aSchema = aSchema2;
aTable = aTable2;
break;
}
}
}
}
}
if(pBegin != pEnd)
{
sReturn = ::dbtools::composeTableName( m_xMetaData, aCatalog, aSchema, aTable, true, ::dbtools::EComposeRule::InDataManipulation ) + ".";
}
}
return sReturn;
}
Reference< XIndexAccess > SAL_CALL OSingleSelectQueryComposer::getParameters( )
{
// now set the Parameters
if ( !m_aCurrentColumns[ParameterColumns] )
{
::rtl::Reference< OSQLColumns> aCols = m_aSqlIterator.getParameters();
std::vector< OUString> aNames;
for (auto const& elem : *aCols)
aNames.push_back(getString(elem->getPropertyValue(PROPERTY_NAME)));
m_aCurrentColumns[ParameterColumns].reset( new OPrivateColumns(aCols,m_xMetaData->supportsMixedCaseQuotedIdentifiers(),*this,m_aMutex,aNames,true) );
}
return m_aCurrentColumns[ParameterColumns].get();
}
void OSingleSelectQueryComposer::clearColumns( const EColumnType _eType )
{
OPrivateColumns* pColumns = m_aCurrentColumns[ _eType ].get();
if ( pColumns != nullptr )
{
pColumns->disposing();
m_aColumnsCollection.push_back( std::move(m_aCurrentColumns[ _eType ]) );
}
}
void OSingleSelectQueryComposer::clearCurrentCollections()
{
for (auto & currentColumn : m_aCurrentColumns)
{
if (currentColumn)
{
currentColumn->disposing();
m_aColumnsCollection.push_back(std::move(currentColumn));
}
}
if(m_pTables)
{
m_pTables->disposing();
m_aTablesCollection.push_back(std::move(m_pTables));
}
}
Reference< XIndexAccess > OSingleSelectQueryComposer::setCurrentColumns( EColumnType _eType,
const ::rtl::Reference< OSQLColumns >& _rCols )
{
::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed);
::osl::MutexGuard aGuard( m_aMutex );
// now set the group columns
if ( !m_aCurrentColumns[_eType] )
{
std::vector< OUString> aNames;
for (auto const& elem : *_rCols)
aNames.push_back(getString(elem->getPropertyValue(PROPERTY_NAME)));
m_aCurrentColumns[_eType].reset( new OPrivateColumns(_rCols,m_xMetaData->supportsMixedCaseQuotedIdentifiers(),*this,m_aMutex,aNames,true) );
}
return m_aCurrentColumns[_eType].get();
}
Reference< XIndexAccess > SAL_CALL OSingleSelectQueryComposer::getGroupColumns( )
{
return setCurrentColumns( GroupByColumns, m_aAdditiveIterator.getGroupColumns() );
}
Reference< XIndexAccess > SAL_CALL OSingleSelectQueryComposer::getOrderColumns( )
{
return setCurrentColumns( OrderColumns, m_aAdditiveIterator.getOrderColumns() );
}
OUString SAL_CALL OSingleSelectQueryComposer::getQueryWithSubstitution( )
{
::osl::MutexGuard aGuard( m_aMutex );
::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed);
OUString sSqlStatement( getQuery() );
const OSQLParseNode* pStatementNode = m_aSqlIterator.getParseTree();
if ( pStatementNode )
{
SQLException aError;
if ( !pStatementNode->parseNodeToExecutableStatement( sSqlStatement, m_xConnection, m_aSqlParser, &aError ) )
throw aError;
}
return sSqlStatement;
}
OUString OSingleSelectQueryComposer::getStatementPart( TGetParseNode const & _aGetFunctor, OSQLParseTreeIterator& _rIterator )
{
OUString sResult;
const OSQLParseNode* pNode = _aGetFunctor( &_rIterator );
if ( pNode )
pNode->parseNodeToStr( sResult, m_xConnection );
return sResult;
}
namespace
{
OUString lcl_getDecomposedColumnName(const OUString& rComposedName, const OUString& rQuoteString)
{
const sal_Int32 nQuoteLength = rQuoteString.getLength();
OUString sName = rComposedName.trim();
OUString sColumnName;
sal_Int32 nPos, nRPos = 0;
for (;;)
{
nPos = sName.indexOf( rQuoteString, nRPos );
if ( nPos >= 0 )
{
nRPos = sName.indexOf( rQuoteString, nPos + nQuoteLength );
if ( nRPos > nPos )
{
if ( nRPos + nQuoteLength < sName.getLength() )
{
nRPos += nQuoteLength; // -1 + 1 skip dot
}
else
{
sColumnName = sName.copy( nPos + nQuoteLength, nRPos - nPos - nQuoteLength );
break;
}
}
else
break;
}
else
break;
}
return sColumnName.isEmpty() ? rComposedName : sColumnName;
}
OUString lcl_getCondition(const Sequence< Sequence< PropertyValue > >& filter,
const OPredicateInputController& i_aPredicateInputController,
const Reference< XNameAccess >& i_xSelectColumns,
const OUString& rQuoteString)
{
OUStringBuffer sRet;
const Sequence< PropertyValue >* pOrIter = filter.getConstArray();
const Sequence< PropertyValue >* pOrEnd = pOrIter + filter.getLength();
while ( pOrIter != pOrEnd )
{
if ( pOrIter->hasElements() )
{
sRet.append(L_BRACKET);
const PropertyValue* pAndIter = pOrIter->getConstArray();
const PropertyValue* pAndEnd = pAndIter + pOrIter->getLength();
while ( pAndIter != pAndEnd )
{
sRet.append(pAndIter->Name);
OUString sValue;
pAndIter->Value >>= sValue;
const OUString sColumnName = lcl_getDecomposedColumnName( pAndIter->Name, rQuoteString );
if ( i_xSelectColumns.is() && i_xSelectColumns->hasByName(sColumnName) )
{
Reference<XPropertySet> xColumn(i_xSelectColumns->getByName(sColumnName),UNO_QUERY);
sValue = i_aPredicateInputController.getPredicateValueStr(sValue,xColumn);
}
else
{
sValue = i_aPredicateInputController.getPredicateValueStr(pAndIter->Name,sValue);
}
lcl_addFilterCriteria_throw(pAndIter->Handle,sValue,sRet);
++pAndIter;
if ( pAndIter != pAndEnd )
sRet.append(STR_AND);
}
sRet.append(R_BRACKET);
}
++pOrIter;
if ( pOrIter != pOrEnd && !sRet.isEmpty() )
sRet.append(STR_OR);
}
return sRet.makeStringAndClear();
}
}
void SAL_CALL OSingleSelectQueryComposer::setStructuredFilter( const Sequence< Sequence< PropertyValue > >& filter )
{
OPredicateInputController aPredicateInput(m_aContext, m_xConnection, &m_aParseContext);
setFilter(lcl_getCondition(filter, aPredicateInput, getColumns(), m_xMetaData->getIdentifierQuoteString()));
}
void SAL_CALL OSingleSelectQueryComposer::setStructuredHavingClause( const Sequence< Sequence< PropertyValue > >& filter )
{
OPredicateInputController aPredicateInput(m_aContext, m_xConnection);
setHavingClause(lcl_getCondition(filter, aPredicateInput, getColumns(), m_xMetaData->getIdentifierQuoteString()));
}
void OSingleSelectQueryComposer::setConditionByColumn( const Reference< XPropertySet >& column, bool andCriteria, std::function<bool(OSingleSelectQueryComposer *, const OUString&)> const & _aSetFunctor, sal_Int32 filterOperator)
{
try
{
::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed);
if ( !column.is()
|| !column->getPropertySetInfo()->hasPropertyByName(PROPERTY_VALUE)
|| !column->getPropertySetInfo()->hasPropertyByName(PROPERTY_NAME)
|| !column->getPropertySetInfo()->hasPropertyByName(PROPERTY_TYPE))
throw SQLException(DBA_RES(RID_STR_COLUMN_NOT_VALID),*this,SQLSTATE_GENERAL,1000,Any() );
sal_Int32 nType = 0;
column->getPropertyValue(PROPERTY_TYPE) >>= nType;
sal_Int32 nSearchable = dbtools::getSearchColumnFlag(m_xConnection,nType);
if(nSearchable == ColumnSearch::NONE)
throw SQLException(DBA_RES(RID_STR_COLUMN_NOT_SEARCHABLE),*this,SQLSTATE_GENERAL,1000,Any() );
::osl::MutexGuard aGuard( m_aMutex );
OUString aName;
column->getPropertyValue(PROPERTY_NAME) >>= aName;
const Any aValue = column->getPropertyValue(PROPERTY_VALUE);
OUStringBuffer aSQL;
const OUString aQuote = m_xMetaData->getIdentifierQuoteString();
getColumns();
// TODO: if this is called for HAVING, check that the column is a GROUP BY column
// or that it is an aggregate function
if ( m_aCurrentColumns[SelectColumns] && m_aCurrentColumns[SelectColumns]->hasByName(aName) )
{
Reference<XPropertySet> xColumn;
m_aCurrentColumns[SelectColumns]->getByName(aName) >>= xColumn;
OSL_ENSURE(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_REALNAME),"Property REALNAME not available!");
OSL_ENSURE(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_TABLENAME),"Property TABLENAME not available!");
OSL_ENSURE(xColumn->getPropertySetInfo()->hasPropertyByName("AggregateFunction"),"Property AggregateFunction not available!");
OUString sRealName,sTableName;
xColumn->getPropertyValue(PROPERTY_REALNAME) >>= sRealName;
xColumn->getPropertyValue(PROPERTY_TABLENAME) >>= sTableName;
if(sTableName.indexOf('.') != -1)
{
OUString aCatlog,aSchema,aTable;
::dbtools::qualifiedNameComponents(m_xMetaData,sTableName,aCatlog,aSchema,aTable,::dbtools::EComposeRule::InDataManipulation);
sTableName = ::dbtools::composeTableName( m_xMetaData, aCatlog, aSchema, aTable, true, ::dbtools::EComposeRule::InDataManipulation );
}
else
sTableName = ::dbtools::quoteName(aQuote,sTableName);
if ( !::comphelper::getBOOL(xColumn->getPropertyValue("Function")) )
{
aSQL = sTableName + "." + ::dbtools::quoteName( aQuote, sRealName );
}
else
aSQL = sRealName;
}
else
{
aSQL = getTableAlias( column ) + ::dbtools::quoteName( aQuote, aName );
}
if ( aValue.hasValue() )
{
if( !m_xTypeConverter.is() )
m_xTypeConverter.set( Converter::create(m_aContext) );
OSL_ENSURE(m_xTypeConverter.is(),"NO typeconverter!");
if ( nType != DataType::BOOLEAN && DataType::BIT != nType )
{
lcl_addFilterCriteria_throw(filterOperator,"",aSQL);
}
switch(nType)
{
case DataType::VARCHAR:
case DataType::CHAR:
case DataType::LONGVARCHAR:
aSQL.append( DBTypeConversion::toSQLString( nType, aValue, m_xTypeConverter ) );
break;
case DataType::CLOB:
{
Reference< XClob > xClob(aValue,UNO_QUERY);
if ( xClob.is() )
{
const ::sal_Int64 nLength = xClob->length();
if ( sal_Int64(nLength + aSQL.getLength() + STR_LIKE.getLength() ) < sal_Int64(SAL_MAX_INT32) )
{
aSQL.append("'").append(xClob->getSubString(1,static_cast<sal_Int32>(nLength))).append("'");
}
}
else
{
aSQL.append( DBTypeConversion::toSQLString( nType, aValue, m_xTypeConverter ) );
}
}
break;
case DataType::VARBINARY:
case DataType::BINARY:
case DataType::LONGVARBINARY:
{
Sequence<sal_Int8> aSeq;
if(!(aValue >>= aSeq))
throw SQLException(DBA_RES(RID_STR_NOT_SEQUENCE_INT8),*this,SQLSTATE_GENERAL,1000,Any() );
if(nSearchable == ColumnSearch::CHAR)
{
aSQL.append( "\'" );
}
aSQL.append( "0x" );
const sal_Int8* pBegin = aSeq.getConstArray();
const sal_Int8* pEnd = pBegin + aSeq.getLength();
for(;pBegin != pEnd;++pBegin)
{
aSQL.append( static_cast<sal_Int32>(*pBegin), 16 );
}
if(nSearchable == ColumnSearch::CHAR)
aSQL.append( "\'" );
}
break;
case DataType::BIT:
case DataType::BOOLEAN:
{
bool bValue = false;
m_xTypeConverter->convertToSimpleType(aValue, TypeClass_BOOLEAN) >>= bValue;
OUString sColumnExp = aSQL.makeStringAndClear();
getBooleanComparisonPredicate( sColumnExp, bValue, m_nBoolCompareMode, aSQL );
}
break;
default:
aSQL.append( DBTypeConversion::toSQLString( nType, aValue, m_xTypeConverter ) );
break;
}
}
else
{
sal_Int32 nFilterOp = filterOperator;
if ( filterOperator != SQLFilterOperator::SQLNULL && filterOperator != SQLFilterOperator::NOT_SQLNULL )
nFilterOp = SQLFilterOperator::SQLNULL;
lcl_addFilterCriteria_throw(nFilterOp,"",aSQL);
}
// Attach filter
// Construct SELECT without WHERE and ORDER BY
OUString sFilter = getFilter();
if ( !sFilter.isEmpty() && !aSQL.isEmpty() )
{
sFilter = L_BRACKET + sFilter + R_BRACKET +
(andCriteria ? OUStringLiteral(u"" STR_AND) : OUStringLiteral(u"" STR_OR));
}
sFilter += aSQL;
// add the filter and the sort order
_aSetFunctor(this,sFilter);
}
catch (css::lang::WrappedTargetException & e)
{
if (e.TargetException.isExtractableTo(
cppu::UnoType<css::sdbc::SQLException>::get()))
{
cppu::throwException(e.TargetException);
}
else
{
throw;
}
}
}
Sequence< Sequence< PropertyValue > > OSingleSelectQueryComposer::getStructuredCondition( TGetParseNode const & _aGetFunctor )
{
::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed);
MutexGuard aGuard(m_aMutex);
Sequence< Sequence< PropertyValue > > aFilterSeq;
OUString sFilter = getStatementPart( _aGetFunctor, m_aAdditiveIterator );
if ( !sFilter.isEmpty() )
{
OUString aSql(m_aPureSelectSQL + STR_WHERE + sFilter);
// build a temporary parse node
const OSQLParseNode* pTempNode = m_aAdditiveIterator.getParseTree();
OUString aErrorMsg;
std::unique_ptr<OSQLParseNode> pSqlParseNode( m_aSqlParser.parseTree(aErrorMsg,aSql));
if (pSqlParseNode)
{
m_aAdditiveIterator.setParseTree(pSqlParseNode.get());
// normalize the filter
OSQLParseNode* pWhereNode = const_cast<OSQLParseNode*>(m_aAdditiveIterator.getWhereTree());
OSQLParseNode* pCondition = pWhereNode->getChild(1);
#if OSL_DEBUG_LEVEL > 0
OUString sCondition;
pCondition->parseNodeToStr( sCondition, m_xConnection );
#endif
OSQLParseNode::negateSearchCondition(pCondition);
pCondition = pWhereNode->getChild(1);
#if OSL_DEBUG_LEVEL > 0
sCondition.clear();
pCondition->parseNodeToStr( sCondition, m_xConnection );
#endif
OSQLParseNode::disjunctiveNormalForm(pCondition);
pCondition = pWhereNode->getChild(1);
#if OSL_DEBUG_LEVEL > 0
sCondition.clear();
pCondition->parseNodeToStr( sCondition, m_xConnection );
#endif
OSQLParseNode::absorptions(pCondition);
pCondition = pWhereNode->getChild(1);
#if OSL_DEBUG_LEVEL > 0
sCondition.clear();
pCondition->parseNodeToStr( sCondition, m_xConnection );
#endif
if ( pCondition )
{
std::vector< std::vector < PropertyValue > > aFilters;
Reference< XNumberFormatter > xFormatter( NumberFormatter::create(m_aContext), UNO_QUERY_THROW );
xFormatter->attachNumberFormatsSupplier( m_xNumberFormatsSupplier );
if (setORCriteria(pCondition, m_aAdditiveIterator, aFilters, xFormatter))
{
aFilterSeq.realloc(aFilters.size());
Sequence<PropertyValue>* pFilters = aFilterSeq.getArray();
for (auto const& filter : aFilters)
{
pFilters->realloc(filter.size());
PropertyValue* pFilter = pFilters->getArray();
for (auto const& elem : filter)
{
*pFilter = elem;
++pFilter;
}
++pFilters;
}
}
}
// restore
m_aAdditiveIterator.setParseTree(pTempNode);
}
}
return aFilterSeq;
}
OUString OSingleSelectQueryComposer::getKeyword( SQLPart _ePart )
{
OUString sKeyword;
switch(_ePart)
{
default:
SAL_WARN("dbaccess", "OSingleSelectQueryComposer::getKeyWord: Invalid enum value!" );
[[fallthrough]]; // fallback to WHERE
case Where:
sKeyword = STR_WHERE;
break;
case Group:
sKeyword = STR_GROUP_BY;
break;
case Having:
sKeyword = STR_HAVING;
break;
case Order:
sKeyword = STR_ORDER_BY;
break;
}
return sKeyword;
}
OUString OSingleSelectQueryComposer::getSQLPart( SQLPart _ePart, OSQLParseTreeIterator& _rIterator, bool _bWithKeyword )
{
TGetParseNode F_tmp(&OSQLParseTreeIterator::getSimpleWhereTree);
OUString sKeyword( getKeyword( _ePart ) );
switch(_ePart)
{
case Where:
F_tmp = TGetParseNode(&OSQLParseTreeIterator::getSimpleWhereTree);
break;
case Group:
F_tmp = TGetParseNode (&OSQLParseTreeIterator::getSimpleGroupByTree);
break;
case Having:
F_tmp = TGetParseNode(&OSQLParseTreeIterator::getSimpleHavingTree);
break;
case Order:
F_tmp = TGetParseNode(&OSQLParseTreeIterator::getSimpleOrderTree);
break;
default:
SAL_WARN("dbaccess","Invalid enum value!");
}
OUString sRet = getStatementPart( F_tmp, _rIterator );
if ( _bWithKeyword && !sRet.isEmpty() )
sRet = sKeyword + sRet;
return sRet;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */