diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /connectivity/source/parse/sqliterator.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | connectivity/source/parse/sqliterator.cxx | 2117 |
1 files changed, 2117 insertions, 0 deletions
diff --git a/connectivity/source/parse/sqliterator.cxx b/connectivity/source/parse/sqliterator.cxx new file mode 100644 index 000000000..fcebd2879 --- /dev/null +++ b/connectivity/source/parse/sqliterator.cxx @@ -0,0 +1,2117 @@ +/* -*- 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 <connectivity/sqliterator.hxx> +#include <connectivity/sdbcx/VTable.hxx> +#include <connectivity/sqlparse.hxx> +#include <sqlbison.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/sqlerror.hxx> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/sdb/ErrorCondition.hpp> +#ifdef SQL_TEST_PARSETREEITERATOR +#include <iostream> +#endif +#include <connectivity/PColumn.hxx> +#include <tools/diagnose_ex.h> +#include <TConnection.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbmetadata.hxx> +#include <com/sun/star/sdb/SQLFilterOperator.hpp> +#include <o3tl/safeint.hxx> +#include <sal/log.hxx> + +#include <iterator> +#include <memory> + +using namespace ::comphelper; +using namespace ::connectivity; +using namespace ::connectivity::sdbcx; +using namespace ::dbtools; +using namespace ::connectivity::parse; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; + +namespace connectivity +{ + struct OSQLParseTreeIteratorImpl + { + std::vector< TNodePair > m_aJoinConditions; + Reference< XConnection > m_xConnection; + Reference< XDatabaseMetaData > m_xDatabaseMetaData; + Reference< XNameAccess > m_xTableContainer; + Reference< XNameAccess > m_xQueryContainer; + + std::shared_ptr< OSQLTables > m_pTables; // all tables which participate in the SQL statement + std::shared_ptr< OSQLTables > m_pSubTables; // all tables from sub queries not the tables from the select tables + std::shared_ptr< QueryNameSet > m_pForbiddenQueryNames; + + TraversalParts m_nIncludeMask; + + bool m_bIsCaseSensitive; + + OSQLParseTreeIteratorImpl( const Reference< XConnection >& _rxConnection, const Reference< XNameAccess >& _rxTables ) + :m_xConnection( _rxConnection ) + ,m_nIncludeMask( TraversalParts::All ) + ,m_bIsCaseSensitive( true ) + { + OSL_PRECOND( m_xConnection.is(), "OSQLParseTreeIteratorImpl::OSQLParseTreeIteratorImpl: invalid connection!" ); + m_xDatabaseMetaData = m_xConnection->getMetaData(); + + m_bIsCaseSensitive = m_xDatabaseMetaData.is() && m_xDatabaseMetaData->supportsMixedCaseQuotedIdentifiers(); + m_pTables = std::make_shared<OSQLTables>( m_bIsCaseSensitive ); + m_pSubTables = std::make_shared<OSQLTables>( m_bIsCaseSensitive ); + + m_xTableContainer = _rxTables; + + DatabaseMetaData aMetaData( m_xConnection ); + if ( aMetaData.supportsSubqueriesInFrom() ) + { + // connections might support the XQueriesSupplier interface, if they implement the css.sdb.Connection + // service + Reference< XQueriesSupplier > xSuppQueries( m_xConnection, UNO_QUERY ); + if ( xSuppQueries.is() ) + m_xQueryContainer = xSuppQueries->getQueries(); + } + } + + public: + bool isQueryAllowed( const OUString& _rQueryName ) + { + if ( !m_pForbiddenQueryNames ) + return true; + if ( m_pForbiddenQueryNames->find( _rQueryName ) == m_pForbiddenQueryNames->end() ) + return true; + return false; + } + }; + + namespace { + + /** helper class for temporarily adding a query name to a list of forbidden query names + */ + class ForbidQueryName + { + std::shared_ptr< QueryNameSet >& m_rpAllForbiddenNames; + OUString m_sForbiddenQueryName; + + public: + ForbidQueryName( OSQLParseTreeIteratorImpl& _rIteratorImpl, const OUString& _rForbiddenQueryName ) + :m_rpAllForbiddenNames( _rIteratorImpl.m_pForbiddenQueryNames ) + ,m_sForbiddenQueryName( _rForbiddenQueryName ) + { + if ( !m_rpAllForbiddenNames ) + m_rpAllForbiddenNames = std::make_shared<QueryNameSet>(); + m_rpAllForbiddenNames->insert( m_sForbiddenQueryName ); + } + + ~ForbidQueryName() + { + m_rpAllForbiddenNames->erase( m_sForbiddenQueryName ); + } + }; + + } +} + +OSQLParseTreeIterator::OSQLParseTreeIterator(const Reference< XConnection >& _rxConnection, + const Reference< XNameAccess >& _rxTables, + const OSQLParser& _rParser ) + :m_rParser( _rParser ) + ,m_pImpl( new OSQLParseTreeIteratorImpl( _rxConnection, _rxTables ) ) +{ + setParseTree(nullptr); +} + + +OSQLParseTreeIterator::OSQLParseTreeIterator( const OSQLParseTreeIterator& _rParentIterator, const OSQLParser& _rParser, const OSQLParseNode* pRoot ) + :m_rParser( _rParser ) + ,m_pImpl( new OSQLParseTreeIteratorImpl( _rParentIterator.m_pImpl->m_xConnection, _rParentIterator.m_pImpl->m_xTableContainer ) ) +{ + m_pImpl->m_pForbiddenQueryNames = _rParentIterator.m_pImpl->m_pForbiddenQueryNames; + setParseTree( pRoot ); +} + + +OSQLParseTreeIterator::~OSQLParseTreeIterator() +{ + dispose(); +} + + +const OSQLTables& OSQLParseTreeIterator::getTables() const +{ + return *m_pImpl->m_pTables; +} + + +bool OSQLParseTreeIterator::isCaseSensitive() const +{ + return m_pImpl->m_bIsCaseSensitive; +} + + +void OSQLParseTreeIterator::dispose() +{ + m_aSelectColumns = nullptr; + m_aGroupColumns = nullptr; + m_aOrderColumns = nullptr; + m_aParameters = nullptr; + m_pImpl->m_xTableContainer = nullptr; + m_pImpl->m_xDatabaseMetaData = nullptr; + m_aCreateColumns = nullptr; + m_pImpl->m_pTables->clear(); + m_pImpl->m_pSubTables->clear(); +} + +void OSQLParseTreeIterator::setParseTree(const OSQLParseNode * pNewParseTree) +{ + m_pImpl->m_pTables->clear(); + m_pImpl->m_pSubTables->clear(); + + m_aSelectColumns = new OSQLColumns(); + m_aGroupColumns = new OSQLColumns(); + m_aOrderColumns = new OSQLColumns(); + m_aParameters = new OSQLColumns(); + m_aCreateColumns = new OSQLColumns(); + + m_pParseTree = pNewParseTree; + if (!m_pParseTree) + { + m_eStatementType = OSQLStatementType::Unknown; + return; + } + + // If m_pParseTree, but no connection then return + if ( !m_pImpl->m_xTableContainer.is() ) + return; + + m_xErrors.reset(); + + + // Determine statement type ... + if (SQL_ISRULE(m_pParseTree,select_statement) || SQL_ISRULE(m_pParseTree,union_statement) ) + { + m_eStatementType = OSQLStatementType::Select; + } + else if (SQL_ISRULE(m_pParseTree,insert_statement)) + { + m_eStatementType = OSQLStatementType::Insert; + } + else if (SQL_ISRULE(m_pParseTree,update_statement_searched)) + { + m_eStatementType = OSQLStatementType::Update; + } + else if (SQL_ISRULE(m_pParseTree,delete_statement_searched)) + { + m_eStatementType = OSQLStatementType::Delete; + } + else if (m_pParseTree->count() == 3 && SQL_ISRULE(m_pParseTree->getChild(1),odbc_call_spec)) + { + m_eStatementType = OSQLStatementType::OdbcCall; + } + else if (SQL_ISRULE(m_pParseTree->getChild(0),base_table_def)) + { + m_eStatementType = OSQLStatementType::CreateTable; + m_pParseTree = m_pParseTree->getChild(0); + } + else + { + m_eStatementType = OSQLStatementType::Unknown; + //aIteratorStatus.setInvalidStatement(); + return; + } +} + + +namespace +{ + + void impl_getRowString( const Reference< XRow >& _rxRow, const sal_Int32 _nColumnIndex, OUString& _out_rString ) + { + _out_rString = _rxRow->getString( _nColumnIndex ); + if ( _rxRow->wasNull() ) + _out_rString.clear(); + } + + + OUString lcl_findTableInMetaData( + const Reference< XDatabaseMetaData >& _rxDBMeta, const OUString& _rCatalog, + const OUString& _rSchema, const OUString& _rTableName ) + { + OUString sComposedName; + + static constexpr OUStringLiteral s_sWildcard = u"%" ; + + // we want all catalogues, all schemas, all tables + Sequence< OUString > sTableTypes { "VIEW", "TABLE", s_sWildcard }; // this last one just to be sure to include anything else... + + if ( _rxDBMeta.is() ) + { + sComposedName.clear(); + + Reference< XResultSet> xRes = _rxDBMeta->getTables( + !_rCatalog.isEmpty() ? Any( _rCatalog ) : Any(), !_rSchema.isEmpty() ? _rSchema : s_sWildcard, _rTableName, sTableTypes ); + + Reference< XRow > xCurrentRow( xRes, UNO_QUERY ); + if ( xCurrentRow.is() && xRes->next() ) + { + OUString sCatalog, sSchema, sName; + + impl_getRowString( xCurrentRow, 1, sCatalog ); + impl_getRowString( xCurrentRow, 2, sSchema ); + impl_getRowString( xCurrentRow, 3, sName ); + + sComposedName = ::dbtools::composeTableName( + _rxDBMeta, + sCatalog, + sSchema, + sName, + false, + ::dbtools::EComposeRule::InDataManipulation + ); + } + } + return sComposedName; + } +} + + +void OSQLParseTreeIterator::impl_getQueryParameterColumns( const OSQLTable& _rQuery ) +{ + if ( !( m_pImpl->m_nIncludeMask & TraversalParts::Parameters ) ) + // parameters not to be included in the traversal + return; + + ::rtl::Reference pSubQueryParameterColumns( new OSQLColumns() ); + + // get the command and the EscapeProcessing properties from the sub query + OUString sSubQueryCommand; + bool bEscapeProcessing = false; + try + { + Reference< XPropertySet > xQueryProperties( _rQuery, UNO_QUERY_THROW ); + OSL_VERIFY( xQueryProperties->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_COMMAND ) ) >>= sSubQueryCommand ); + OSL_VERIFY( xQueryProperties->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_ESCAPEPROCESSING ) ) >>= bEscapeProcessing ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.parse"); + } + + // parse the sub query + do { + + if ( !bEscapeProcessing || ( sSubQueryCommand.isEmpty() ) ) + break; + + OUString sError; + std::unique_ptr< OSQLParseNode > pSubQueryNode( const_cast< OSQLParser& >( m_rParser ).parseTree( sError, sSubQueryCommand ) ); + if (!pSubQueryNode) + break; + + OSQLParseTreeIterator aSubQueryIterator( *this, m_rParser, pSubQueryNode.get() ); + aSubQueryIterator.impl_traverse( TraversalParts::Parameters | TraversalParts::SelectColumns ); + // SelectColumns might also contain parameters #i77635# + pSubQueryParameterColumns = aSubQueryIterator.getParameters(); + aSubQueryIterator.dispose(); + + } while ( false ); + + // copy the parameters of the sub query to our own parameter array + m_aParameters->insert( m_aParameters->end(), pSubQueryParameterColumns->begin(), pSubQueryParameterColumns->end() ); +} + + +OSQLTable OSQLParseTreeIterator::impl_locateRecordSource( const OUString& _rComposedName ) +{ + if ( _rComposedName.isEmpty() ) + { + SAL_WARN( "connectivity.parse", "OSQLParseTreeIterator::impl_locateRecordSource: no object name at all?" ); + return OSQLTable(); + } + + OSQLTable aReturn; + OUString sComposedName( _rComposedName ); + + try + { + OUString sCatalog, sSchema, sName; + qualifiedNameComponents( m_pImpl->m_xDatabaseMetaData, sComposedName, sCatalog, sSchema, sName, ::dbtools::EComposeRule::InDataManipulation ); + + // check whether there is a query with the given name + bool bQueryDoesExist = m_pImpl->m_xQueryContainer.is() && m_pImpl->m_xQueryContainer->hasByName( sComposedName ); + + // check whether the table container contains an object with the given name + if ( !bQueryDoesExist && !m_pImpl->m_xTableContainer->hasByName( sComposedName ) ) + sComposedName = lcl_findTableInMetaData( m_pImpl->m_xDatabaseMetaData, sCatalog, sSchema, sName ); + bool bTableDoesExist = m_pImpl->m_xTableContainer->hasByName( sComposedName ); + + // now obtain the object + + // if we're creating a table, and there already is a table or query with the same name, + // this is worth an error + if ( OSQLStatementType::CreateTable == m_eStatementType ) + { + if ( bQueryDoesExist ) + impl_appendError( IParseContext::ErrorCode::InvalidQueryExist, &sName ); + else if ( bTableDoesExist ) + impl_appendError( IParseContext::ErrorCode::InvalidTableExist, &sName ); + else + aReturn = impl_createTableObject( sName, sCatalog, sSchema ); + } + else + { + // queries win over tables, so if there's a query with this name, take this, no matter if + // there's a table, too + if ( bQueryDoesExist ) + { + if ( !m_pImpl->isQueryAllowed( sComposedName ) ) + { + impl_appendError( m_rParser.getErrorHelper().getSQLException( sdb::ErrorCondition::PARSER_CYCLIC_SUB_QUERIES, nullptr ) ); + return nullptr; + } + + m_pImpl->m_xQueryContainer->getByName( sComposedName ) >>= aReturn; + + // collect the parameters from the sub query + ForbidQueryName aForbidName( *m_pImpl, sComposedName ); + impl_getQueryParameterColumns( aReturn ); + } + else if ( bTableDoesExist ) + m_pImpl->m_xTableContainer->getByName( sComposedName ) >>= aReturn; + else + { + if ( m_pImpl->m_xQueryContainer.is() ) + // the connection on which we're working supports sub queries in from (else + // m_xQueryContainer would not have been set), so emit a better error message + impl_appendError( IParseContext::ErrorCode::InvalidTableOrQuery, &sName ); + else + impl_appendError( IParseContext::ErrorCode::InvalidTableNosuch, &sName ); + } + } + } + catch(Exception&) + { + impl_appendError( IParseContext::ErrorCode::InvalidTableNosuch, &sComposedName ); + } + + return aReturn; +} + + +void OSQLParseTreeIterator::traverseOneTableName( OSQLTables& _rTables,const OSQLParseNode * pTableName, const OUString & rTableRange ) +{ + if ( !( m_pImpl->m_nIncludeMask & TraversalParts::TableNames ) ) + // tables should not be included in the traversal + return; + + OSL_ENSURE(pTableName != nullptr,"OSQLParseTreeIterator::traverseOneTableName: pTableName == NULL"); + + Any aCatalog; + OUString aSchema,aTableName,aComposedName; + OUString aTableRange(rTableRange); + + // Get table name + OSQLParseNode::getTableComponents(pTableName,aCatalog,aSchema,aTableName,m_pImpl->m_xDatabaseMetaData); + + // create the composed name like DOMAIN.USER.TABLE1 + aComposedName = ::dbtools::composeTableName(m_pImpl->m_xDatabaseMetaData, + aCatalog.hasValue() ? ::comphelper::getString(aCatalog) : OUString(), + aSchema, + aTableName, + false, + ::dbtools::EComposeRule::InDataManipulation); + + // if there is no alias for the table name assign the original name to it + if ( aTableRange.isEmpty() ) + aTableRange = aComposedName; + + // get the object representing this table/query + OSQLTable aTable = impl_locateRecordSource( aComposedName ); + if ( aTable.is() ) + _rTables[ aTableRange ] = aTable; +} + +void OSQLParseTreeIterator::impl_fillJoinConditions(const OSQLParseNode* i_pJoinCondition) +{ + if (i_pJoinCondition->count() == 3 && // Expression with brackets + SQL_ISPUNCTUATION(i_pJoinCondition->getChild(0),"(") && + SQL_ISPUNCTUATION(i_pJoinCondition->getChild(2),")")) + { + impl_fillJoinConditions(i_pJoinCondition->getChild(1)); + } + else if (SQL_ISRULEOR2(i_pJoinCondition,search_condition,boolean_term) && // AND/OR logic operation: + i_pJoinCondition->count() == 3) + { + // Only allow AND logic operation + if ( SQL_ISTOKEN(i_pJoinCondition->getChild(1),AND) ) + { + impl_fillJoinConditions(i_pJoinCondition->getChild(0)); + impl_fillJoinConditions(i_pJoinCondition->getChild(1)); + } + } + else if (SQL_ISRULE(i_pJoinCondition,comparison_predicate)) + { + // only the comparison of columns is allowed + OSL_ENSURE(i_pJoinCondition->count() == 3,"OQueryDesignView::InsertJoinConnection: error in the parse tree"); + if (SQL_ISRULE(i_pJoinCondition->getChild(0),column_ref) && + SQL_ISRULE(i_pJoinCondition->getChild(2),column_ref) && + i_pJoinCondition->getChild(1)->getNodeType() == SQLNodeType::Equal) + { + m_pImpl->m_aJoinConditions.push_back( TNodePair(i_pJoinCondition->getChild(0),i_pJoinCondition->getChild(2)) ); + } + } +} + +std::vector< TNodePair >& OSQLParseTreeIterator::getJoinConditions() const +{ + return m_pImpl->m_aJoinConditions; +} + +void OSQLParseTreeIterator::getQualified_join( OSQLTables& _rTables, const OSQLParseNode *pTableRef, OUString& aTableRange ) +{ + OSL_PRECOND( SQL_ISRULE( pTableRef, cross_union ) || SQL_ISRULE( pTableRef, qualified_join ) , + "OSQLParseTreeIterator::getQualified_join: illegal node!" ); + + aTableRange.clear(); + + const OSQLParseNode* pNode = getTableNode(_rTables,pTableRef->getChild(0),aTableRange); + if ( isTableNode( pNode ) ) + traverseOneTableName( _rTables, pNode, aTableRange ); + + sal_uInt32 nPos = 4; + if( SQL_ISRULE(pTableRef,cross_union) || pTableRef->getChild(1)->getTokenID() != SQL_TOKEN_NATURAL) + { + nPos = 3; + // join_condition,named_columns_join + if ( SQL_ISRULE( pTableRef, qualified_join ) ) + { + const OSQLParseNode* pJoin_spec = pTableRef->getChild(4); + if ( SQL_ISRULE( pJoin_spec, join_condition ) ) + { + impl_fillJoinConditions(pJoin_spec->getChild(1)); + } + else + { + const OSQLParseNode* pColumnCommalist = pJoin_spec->getChild(2); + // All columns in the column_commalist ... + for (size_t i = 0; i < pColumnCommalist->count(); i++) + { + const OSQLParseNode * pCol = pColumnCommalist->getChild(i); + // add twice because the column must exists in both tables + m_pImpl->m_aJoinConditions.push_back( TNodePair(pCol,pCol) ); + } + } + } + } + + pNode = getTableNode(_rTables,pTableRef->getChild(nPos),aTableRange); + if ( isTableNode( pNode ) ) + traverseOneTableName( _rTables, pNode, aTableRange ); +} + +const OSQLParseNode* OSQLParseTreeIterator::getTableNode( OSQLTables& _rTables, const OSQLParseNode *pTableRef,OUString& rTableRange ) +{ + OSL_PRECOND( SQL_ISRULE( pTableRef, table_ref ) || SQL_ISRULE( pTableRef, joined_table ) + || SQL_ISRULE( pTableRef, qualified_join ) || SQL_ISRULE( pTableRef, cross_union ), + "OSQLParseTreeIterator::getTableNode: only to be called for table_ref nodes!" ); + + const OSQLParseNode* pTableNameNode = nullptr; + + if ( SQL_ISRULE( pTableRef, joined_table ) ) + { + getQualified_join( _rTables, pTableRef->getChild(1), rTableRange ); + } + if ( SQL_ISRULE( pTableRef, qualified_join ) || SQL_ISRULE( pTableRef, cross_union ) ) + { + getQualified_join( _rTables, pTableRef, rTableRange ); + } + else + { + rTableRange = OSQLParseNode::getTableRange(pTableRef); + if ( ( pTableRef->count() == 4 ) // '{' SQL_TOKEN_OJ joined_table '}' + || ( pTableRef->count() == 5 ) // '(' joined_table ')' range_variable op_column_commalist + ) + { + getQualified_join( _rTables, pTableRef->getChild(6 - pTableRef->count()), rTableRange ); + } + else if ( pTableRef->count() == 3 ) // subquery range_variable op_column_commalist || '(' joined_table ')' + { + const OSQLParseNode* pSubQuery = pTableRef->getChild(0); + if ( pSubQuery->isToken() ) + { + getQualified_join( _rTables, pTableRef->getChild(1), rTableRange ); + } + else + { + OSL_ENSURE( pSubQuery->count() == 3, "sub queries should have 3 children!" ); + const OSQLParseNode* pQueryExpression = pSubQuery->getChild(1); + if ( SQL_ISRULE( pQueryExpression, select_statement ) ) + { + getSelect_statement( *m_pImpl->m_pSubTables, pQueryExpression ); + // TODO: now, we need to setup an OSQLTable from pQueryExpression in some way + // and stick it in _rTables[rTableRange]. Probably fake it by + // setting up a full OSQLParseTreeIterator on pQueryExpression + // and using its m_aSelectColumns + // This is necessary in stuff like "SELECT * FROM tbl1 INNER JOIN (SELECT foo, bar FROM tbl2) AS tbl3" + // so that setSelectColumnName() can expand the "*" correctly. + // See e.g. R_UserAndLastSubscription query of https://bugs.libreoffice.org/attachment.cgi?id=71871 + } + else + { + SAL_WARN( "connectivity.parse", "OSQLParseTreeIterator::getTableNode: subquery which is no select_statement: not yet implemented!" ); + } + } + } + else if ( pTableRef->count() == 2 ) // table_node table_primary_as_range_column + { + pTableNameNode = pTableRef->getChild(0); + } + else + SAL_WARN( "connectivity.parse", "OSQLParseTreeIterator::getTableNode: unhandled case!" ); + } + + return pTableNameNode; +} + +void OSQLParseTreeIterator::getSelect_statement(OSQLTables& _rTables,const OSQLParseNode* pSelect) +{ + if(SQL_ISRULE(pSelect,union_statement)) + { + getSelect_statement(_rTables,pSelect->getChild(0)); + //getSelect_statement(pSelect->getChild(3)); + return; + } + OSQLParseNode * pTableRefCommalist = pSelect->getChild(3)->getChild(0)->getChild(1); + + OSL_ENSURE(pTableRefCommalist != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(SQL_ISRULE(pTableRefCommalist,table_ref_commalist),"OSQLParseTreeIterator: error in parse tree!"); + + const OSQLParseNode* pTableName = nullptr; + OUString aTableRange; + for (size_t i = 0; i < pTableRefCommalist->count(); i++) + { // Process FROM clause + aTableRange.clear(); + + const OSQLParseNode* pTableListElement = pTableRefCommalist->getChild(i); + if ( isTableNode( pTableListElement ) ) + { + traverseOneTableName( _rTables, pTableListElement, aTableRange ); + } + else if ( SQL_ISRULE( pTableListElement, table_ref ) ) + { + // Table references can be made up of table names, table names (+),'('joined_table')'(+) + pTableName = pTableListElement->getChild(0); + if( isTableNode( pTableName ) ) + { // Found table names + aTableRange = OSQLParseNode::getTableRange(pTableListElement); + traverseOneTableName( _rTables, pTableName, aTableRange ); + } + else if(SQL_ISPUNCTUATION(pTableName,"{")) + { // '{' SQL_TOKEN_OJ joined_table '}' + getQualified_join( _rTables, pTableListElement->getChild(2), aTableRange ); + } + else + { // '(' joined_table ')' range_variable op_column_commalist + getTableNode( _rTables, pTableListElement, aTableRange ); + } + } + else if (SQL_ISRULE( pTableListElement, qualified_join ) || SQL_ISRULE( pTableListElement, cross_union ) ) + { + getQualified_join( _rTables, pTableListElement, aTableRange ); + } + else if ( SQL_ISRULE( pTableListElement, joined_table ) ) + { + getQualified_join( _rTables, pTableListElement->getChild(1), aTableRange ); + } + + // if (! aIteratorStatus.IsSuccessful()) break; + } +} + +bool OSQLParseTreeIterator::traverseTableNames(OSQLTables& _rTables) +{ + if ( m_pParseTree == nullptr ) + return false; + + OSQLParseNode* pTableName = nullptr; + + switch ( m_eStatementType ) + { + case OSQLStatementType::Select: + getSelect_statement( _rTables, m_pParseTree ); + break; + + case OSQLStatementType::CreateTable: + case OSQLStatementType::Insert: + case OSQLStatementType::Delete: + pTableName = m_pParseTree->getChild(2); + break; + + case OSQLStatementType::Update: + pTableName = m_pParseTree->getChild(1); + break; + default: + break; + } + + if ( pTableName ) + { + traverseOneTableName( _rTables, pTableName, OUString() ); + } + + return !hasErrors(); +} + +OUString OSQLParseTreeIterator::getColumnAlias(const OSQLParseNode* _pDerivedColumn) +{ + OSL_ENSURE(SQL_ISRULE(_pDerivedColumn,derived_column),"No derived column!"); + OUString sColumnAlias; + if(_pDerivedColumn->getChild(1)->count() == 2) + sColumnAlias = _pDerivedColumn->getChild(1)->getChild(1)->getTokenValue(); + else if(!_pDerivedColumn->getChild(1)->isRule()) + sColumnAlias = _pDerivedColumn->getChild(1)->getTokenValue(); + return sColumnAlias; +} + + +namespace +{ + void lcl_getColumnRange( const OSQLParseNode* _pColumnRef, const Reference< XConnection >& _rxConnection, + OUString& _out_rColumnName, OUString& _out_rTableRange, + const OSQLColumns* _pSelectColumns, OUString& _out_rColumnAliasIfPresent ) + { + _out_rColumnName.clear(); + _out_rTableRange.clear(); + _out_rColumnAliasIfPresent.clear(); + if ( SQL_ISRULE( _pColumnRef, column_ref ) ) + { + if( _pColumnRef->count() > 1 ) + { + for ( sal_Int32 i=0; i<static_cast<sal_Int32>(_pColumnRef->count())-2; ++i ) + _pColumnRef->getChild(i)->parseNodeToStr( _out_rTableRange, _rxConnection, nullptr, false, false ); + _out_rColumnName = _pColumnRef->getChild( _pColumnRef->count()-1 )->getChild(0)->getTokenValue(); + } + else + _out_rColumnName = _pColumnRef->getChild(0)->getTokenValue(); + + // look up the column in the select column, to find a possible alias + if ( _pSelectColumns ) + { + for (const Reference< XPropertySet >& xColumn : *_pSelectColumns) + { + try + { + OUString sName, sTableName; + xColumn->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_REALNAME ) ) >>= sName; + xColumn->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_TABLENAME ) ) >>= sTableName; + if ( sName == _out_rColumnName && ( _out_rTableRange.isEmpty() || sTableName == _out_rTableRange ) ) + { + xColumn->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_NAME ) ) >>= _out_rColumnAliasIfPresent; + break; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.parse"); + } + } + } + } + else if(SQL_ISRULE(_pColumnRef,general_set_fct) || SQL_ISRULE(_pColumnRef,set_fct_spec)) + { // Function + _pColumnRef->parseNodeToStr( _out_rColumnName, _rxConnection ); + } + else if(_pColumnRef->getNodeType() == SQLNodeType::Name) + _out_rColumnName = _pColumnRef->getTokenValue(); + } +} + + +void OSQLParseTreeIterator::getColumnRange( const OSQLParseNode* _pColumnRef, + OUString& _rColumnName, + OUString& _rTableRange) const +{ + OUString sDummy; + lcl_getColumnRange( _pColumnRef, m_pImpl->m_xConnection, _rColumnName, _rTableRange, nullptr, sDummy ); +} + + +void OSQLParseTreeIterator::getColumnRange( const OSQLParseNode* _pColumnRef, + OUString& _rColumnName, + OUString& _rTableRange, + OUString& _out_rColumnAliasIfPresent ) const +{ + lcl_getColumnRange( _pColumnRef, m_pImpl->m_xConnection, _rColumnName, _rTableRange, &*m_aSelectColumns, _out_rColumnAliasIfPresent ); +} + + +void OSQLParseTreeIterator::getColumnRange( const OSQLParseNode* _pColumnRef, + const Reference< XConnection >& _rxConnection, OUString& _out_rColumnName, OUString& _out_rTableRange ) +{ + OUString sDummy; + lcl_getColumnRange( _pColumnRef, _rxConnection, _out_rColumnName, _out_rTableRange, nullptr, sDummy ); +} + + +void OSQLParseTreeIterator::traverseCreateColumns(const OSQLParseNode* pSelectNode) +{ + // aIteratorStatus.Clear(); + + if (!pSelectNode || m_eStatementType != OSQLStatementType::CreateTable || m_pImpl->m_pTables->empty()) + { + impl_appendError( IParseContext::ErrorCode::General ); + return; + } + if (!SQL_ISRULE(pSelectNode,base_table_element_commalist)) + return ; + + for (size_t i = 0; i < pSelectNode->count(); i++) + { + OSQLParseNode *pColumnRef = pSelectNode->getChild(i); + + if (SQL_ISRULE(pColumnRef,column_def)) + { + OUString aColumnName; + OUString aTypeName; + sal_Int32 nType = DataType::VARCHAR; + aColumnName = pColumnRef->getChild(0)->getTokenValue(); + + OSQLParseNode *pDatatype = pColumnRef->getChild(1); + if (pDatatype && SQL_ISRULE(pDatatype,character_string_type)) + { + const OSQLParseNode *pType = pDatatype->getChild(0); + aTypeName = pType->getTokenValue(); + if (pDatatype->count() == 2 && (pType->getTokenID() == SQL_TOKEN_CHAR || pType->getTokenID() == SQL_TOKEN_CHARACTER )) + nType = DataType::CHAR; + } + else if(pDatatype && pDatatype->getNodeType() == SQLNodeType::Keyword) + { + aTypeName = "VARCHAR"; + } + + if (!aTypeName.isEmpty()) + { + //TODO:Create a new class for create statement to handle field length + rtl::Reference<OParseColumn> pColumn = new OParseColumn(aColumnName,aTypeName,OUString(),OUString(), + ColumnValue::NULLABLE_UNKNOWN,0,0,nType,false,false,isCaseSensitive(), + OUString(),OUString(),OUString()); + pColumn->setFunction(false); + pColumn->setRealName(aColumnName); + + m_aCreateColumns->push_back(pColumn); + } + } + + } +} + +bool OSQLParseTreeIterator::traverseSelectColumnNames(const OSQLParseNode* pSelectNode) +{ + if ( !( m_pImpl->m_nIncludeMask & TraversalParts::SelectColumns ) ) + return true; + + if (!pSelectNode || m_eStatementType != OSQLStatementType::Select || m_pImpl->m_pTables->empty()) + { + impl_appendError( IParseContext::ErrorCode::General ); + return false; + } + + if(SQL_ISRULE(pSelectNode,union_statement)) + { + return traverseSelectColumnNames( pSelectNode->getChild( 0 ) ) + /*&& traverseSelectColumnNames( pSelectNode->getChild( 3 ) )*/; + } + + // nyi: more checks for correct structure! + if (pSelectNode->getChild(2)->isRule() && SQL_ISPUNCTUATION(pSelectNode->getChild(2)->getChild(0),"*")) + { + // SELECT * ... + setSelectColumnName("*", "", ""); + } + else if (SQL_ISRULE(pSelectNode->getChild(2),scalar_exp_commalist)) + { + // SELECT column[,column] or SELECT COUNT(*) ... + OSQLParseNode * pSelection = pSelectNode->getChild(2); + + for (size_t i = 0; i < pSelection->count(); i++) + { + OSQLParseNode *pColumnRef = pSelection->getChild(i); + + //if (SQL_ISRULE(pColumnRef,select_sublist)) + if (SQL_ISRULE(pColumnRef,derived_column) && + SQL_ISRULE(pColumnRef->getChild(0),column_ref) && + pColumnRef->getChild(0)->count() == 3 && + SQL_ISPUNCTUATION(pColumnRef->getChild(0)->getChild(2),"*")) + { + // All the table's columns + OUString aTableRange; + pColumnRef->getChild(0)->parseNodeToStr( aTableRange, m_pImpl->m_xConnection, nullptr, false, false ); + setSelectColumnName("*", "", aTableRange); + continue; + } + else if (SQL_ISRULE(pColumnRef,derived_column)) + { + OUString aColumnAlias(getColumnAlias(pColumnRef)); // can be empty + OUString sColumnName; + OUString aTableRange; + sal_Int32 nType = DataType::VARCHAR; + bool bFkt(false); + pColumnRef = pColumnRef->getChild(0); + while ( + pColumnRef->getKnownRuleID() != OSQLParseNode::subquery && + pColumnRef->count() == 3 && + SQL_ISPUNCTUATION(pColumnRef->getChild(0),"(") && + SQL_ISPUNCTUATION(pColumnRef->getChild(2),")") + ) + pColumnRef = pColumnRef->getChild(1); + + if (SQL_ISRULE(pColumnRef,column_ref)) + { + getColumnRange(pColumnRef,sColumnName,aTableRange); + OSL_ENSURE(!sColumnName.isEmpty(),"Column name must not be empty!"); + } + else /*if (SQL_ISRULE(pColumnRef,general_set_fct) || SQL_ISRULE(pColumnRef,set_fct_spec) || + SQL_ISRULE(pColumnRef,position_exp) || SQL_ISRULE(pColumnRef,extract_exp) || + SQL_ISRULE(pColumnRef,length_exp) || SQL_ISRULE(pColumnRef,char_value_fct)|| + SQL_ISRULE(pColumnRef,num_value_exp) || SQL_ISRULE(pColumnRef,term))*/ + { + // Function call present + pColumnRef->parseNodeToStr( sColumnName, m_pImpl->m_xConnection ); + // check if the column is also a parameter + traverseSearchCondition(pColumnRef); // num_value_exp + + if ( pColumnRef->isRule() ) + { + // FIXME: the if condition is not quite right + // many expressions are rules, e.g. "5+3" + // or even: "colName + 1" + bFkt = true; + nType = getFunctionReturnType(pColumnRef); + } + } + /* + else + { + aIteratorStatus.setStatementTooComplex(); + return; + } + */ + if(aColumnAlias.isEmpty()) + aColumnAlias = sColumnName; + setSelectColumnName(sColumnName,aColumnAlias,aTableRange,bFkt,nType,SQL_ISRULE(pColumnRef,general_set_fct) || SQL_ISRULE(pColumnRef,set_fct_spec)); + } + } + } + + return !hasErrors(); +} + + +bool OSQLParseTreeIterator::traverseOrderByColumnNames(const OSQLParseNode* pSelectNode) +{ + traverseByColumnNames( pSelectNode, true ); + return !hasErrors(); +} + +void OSQLParseTreeIterator::traverseByColumnNames(const OSQLParseNode* pSelectNode, bool _bOrder) +{ + // aIteratorStatus.Clear(); + + if (pSelectNode == nullptr) + { + //aIteratorStatus.setInvalidStatement(); + return; + } + + if (m_eStatementType != OSQLStatementType::Select) + { + //aIteratorStatus.setInvalidStatement(); + return; + } + + if(SQL_ISRULE(pSelectNode,union_statement)) + { + traverseByColumnNames(pSelectNode->getChild(0),_bOrder); + return; + } + + OSL_ENSURE(pSelectNode->count() >= 4,"OSQLParseTreeIterator: error in parse tree!"); + + OSQLParseNode * pTableExp = pSelectNode->getChild(3); + OSL_ENSURE(pTableExp != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(SQL_ISRULE(pTableExp,table_exp),"OSQLParseTreeIterator:table_exp error in parse tree!"); + OSL_ENSURE(pTableExp->count() == TABLE_EXPRESSION_CHILD_COUNT,"OSQLParseTreeIterator: error in parse tree!"); + + sal_uInt32 nPos = ( _bOrder ? ORDER_BY_CHILD_POS : 2 ); + + OSQLParseNode * pOptByClause = pTableExp->getChild(nPos); + OSL_ENSURE(pOptByClause != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + if ( pOptByClause->count() == 0 ) + return; + + OSL_ENSURE(pOptByClause->count() == 3,"OSQLParseTreeIterator: error in parse tree!"); + + OSQLParseNode * pOrderingSpecCommalist = pOptByClause->getChild(2); + OSL_ENSURE(pOrderingSpecCommalist != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(!_bOrder || SQL_ISRULE(pOrderingSpecCommalist,ordering_spec_commalist),"OSQLParseTreeIterator:ordering_spec_commalist error in parse tree!"); + OSL_ENSURE(pOrderingSpecCommalist->count() > 0,"OSQLParseTreeIterator: error in parse tree!"); + + OUString sColumnName; + OUString aTableRange; + sal_uInt32 nCount = pOrderingSpecCommalist->count(); + for (sal_uInt32 i = 0; i < nCount; ++i) + { + OSQLParseNode* pColumnRef = pOrderingSpecCommalist->getChild(i); + OSL_ENSURE(pColumnRef != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + if ( _bOrder ) + { + OSL_ENSURE(SQL_ISRULE(pColumnRef,ordering_spec),"OSQLParseTreeIterator:ordering_spec error in parse tree!"); + OSL_ENSURE(pColumnRef->count() == 2,"OSQLParseTreeIterator: error in parse tree!"); + + pColumnRef = pColumnRef->getChild(0); + } + OSL_ENSURE(pColumnRef != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + aTableRange.clear(); + sColumnName.clear(); + if ( SQL_ISRULE(pColumnRef,column_ref) ) + { + // Column name (and TableRange): + getColumnRange(pColumnRef,sColumnName,aTableRange); + } + else + { // here I found a predicate + pColumnRef->parseNodeToStr( sColumnName, m_pImpl->m_xConnection, nullptr, false, false ); + } + OSL_ENSURE(!sColumnName.isEmpty(),"sColumnName must not be empty!"); + if ( _bOrder ) + { + // Ascending/Descending + OSQLParseNode * pOptAscDesc = pColumnRef->getParent()->getChild(1); + OSL_ENSURE(pOptAscDesc != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + + bool bAscending = ! (pOptAscDesc && SQL_ISTOKEN(pOptAscDesc,DESC)); + setOrderByColumnName(sColumnName, aTableRange,bAscending); + } + else + setGroupByColumnName(sColumnName, aTableRange); + } +} + +bool OSQLParseTreeIterator::traverseGroupByColumnNames(const OSQLParseNode* pSelectNode) +{ + traverseByColumnNames( pSelectNode, false ); + return !hasErrors(); +} + + +namespace +{ + OUString lcl_generateParameterName( const OSQLParseNode& _rParentNode, const OSQLParseNode& _rParamNode ) + { + OUString sColumnName( "param" ); + const sal_Int32 nCount = static_cast<sal_Int32>(_rParentNode.count()); + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + if ( _rParentNode.getChild(i) == &_rParamNode ) + { + sColumnName += OUString::number( i+1 ); + break; + } + } + return sColumnName; + } +} + + +void OSQLParseTreeIterator::traverseParameters(const OSQLParseNode* _pNode) +{ + if ( _pNode == nullptr ) + return; + + OUString sColumnName, sTableRange, aColumnAlias; + const OSQLParseNode* pParent = _pNode->getParent(); + if ( pParent != nullptr ) + { + if ( SQL_ISRULE(pParent,comparison_predicate) ) // x = X + { + sal_uInt32 nPos = 0; + if ( pParent->getChild(nPos) == _pNode ) + nPos = 2; + const OSQLParseNode* pOther = pParent->getChild(nPos); + if ( SQL_ISRULE( pOther, column_ref ) ) + getColumnRange( pOther, sColumnName, sTableRange, aColumnAlias); + else + pOther->parseNodeToStr( sColumnName, m_pImpl->m_xConnection, nullptr, false, false ); + } // if ( SQL_ISRULE(pParent,comparison_predicate) ) // x = X + else if ( SQL_ISRULE(pParent,other_like_predicate_part_2) ) + { + const OSQLParseNode* pOther = pParent->getParent()->getChild(0); + if ( SQL_ISRULE( pOther, column_ref ) ) + getColumnRange( pOther, sColumnName, sTableRange, aColumnAlias); + else + pOther->parseNodeToStr( sColumnName, m_pImpl->m_xConnection, nullptr, false, false ); + } + else if ( SQL_ISRULE(pParent,between_predicate_part_2) ) + { + const OSQLParseNode* pOther = pParent->getParent()->getChild(0); + if ( SQL_ISRULE( pOther, column_ref ) ) + getColumnRange( pOther, sColumnName, sTableRange, aColumnAlias); + else + { + pOther->parseNodeToStr( sColumnName, m_pImpl->m_xConnection, nullptr, false, false ); + lcl_generateParameterName( *pParent, *_pNode ); + } + } + else if ( pParent->getNodeType() == SQLNodeType::CommaListRule ) + { + lcl_generateParameterName( *pParent, *_pNode ); + } + } + traverseParameter( _pNode, pParent, sColumnName, sTableRange, aColumnAlias ); + const sal_uInt32 nCount = _pNode->count(); + for (sal_uInt32 i = 0; i < nCount; ++i) + { + const OSQLParseNode* pChild = _pNode->getChild(i); + traverseParameters( pChild ); + } +} + +bool OSQLParseTreeIterator::traverseSelectionCriteria(const OSQLParseNode* pSelectNode) +{ + if ( pSelectNode == nullptr ) + return false; + + + // Analyse parse tree (depending on statement type) + // and set pointer to WHERE clause: + OSQLParseNode * pWhereClause = nullptr; + + if (m_eStatementType == OSQLStatementType::Select) + { + if(SQL_ISRULE(pSelectNode,union_statement)) + { + return traverseSelectionCriteria( pSelectNode->getChild( 0 ) ) + && traverseSelectionCriteria( pSelectNode->getChild( 3 ) ); + } + OSL_ENSURE(pSelectNode->count() >= 4,"OSQLParseTreeIterator: error in parse tree!"); + + OSQLParseNode * pTableExp = pSelectNode->getChild(3); + OSL_ENSURE(pTableExp != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(SQL_ISRULE(pTableExp,table_exp),"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(pTableExp->count() == TABLE_EXPRESSION_CHILD_COUNT,"OSQLParseTreeIterator: error in parse tree!"); + + pWhereClause = pTableExp->getChild(1); + } else if (SQL_ISRULE(pSelectNode,update_statement_searched)) { + OSL_ENSURE(pSelectNode->count() == 5,"OSQLParseTreeIterator: error in parse tree!"); + pWhereClause = pSelectNode->getChild(4); + } else if (SQL_ISRULE(pSelectNode,delete_statement_searched)) { + OSL_ENSURE(pSelectNode->count() == 4,"OSQLParseTreeIterator: error in parse tree!"); + pWhereClause = pSelectNode->getChild(3); + } else if (SQL_ISRULE(pSelectNode,delete_statement_positioned)) { + // nyi + SAL_WARN( "connectivity.parse","OSQLParseTreeIterator::getSelectionCriteria: positioned nyi"); + } else { + // Other statement, no selection criteria + return false; + } + + if (!pWhereClause || !SQL_ISRULE(pWhereClause,where_clause)) + { + // The WHERE clause is optional most of the time; which means it could be a "optional_where_clause". + OSL_ENSURE(pWhereClause && SQL_ISRULE(pWhereClause,opt_where_clause),"OSQLParseTreeIterator: error in parse tree!"); + return false; + } + + // But if it's a where_clause, then it must not be empty + OSL_ENSURE(pWhereClause->count() == 2,"OSQLParseTreeIterator: error in parse tree!"); + + OSQLParseNode * pComparisonPredicate = pWhereClause->getChild(1); + OSL_ENSURE(pComparisonPredicate != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + + + // Process the comparison criteria now + + + traverseSearchCondition(pComparisonPredicate); + + return !hasErrors(); +} + + +void OSQLParseTreeIterator::traverseSearchCondition(OSQLParseNode const * pSearchCondition) +{ + if ( + SQL_ISRULE(pSearchCondition,boolean_primary) && + pSearchCondition->count() == 3 && + SQL_ISPUNCTUATION(pSearchCondition->getChild(0),"(") && + SQL_ISPUNCTUATION(pSearchCondition->getChild(2),")") + ) + { + // Round brackets + traverseSearchCondition(pSearchCondition->getChild(1)); + } + // The first element is an OR logical operation + else if ( SQL_ISRULE(pSearchCondition,search_condition) && pSearchCondition->count() == 3 ) + { + // if this assert fails, the SQL grammar has changed! + assert(SQL_ISTOKEN(pSearchCondition->getChild(1),OR)); + // Then process recursively (use the same row) ... + traverseSearchCondition(pSearchCondition->getChild(0)); +// if (! aIteratorStatus.IsSuccessful()) +// return; + + // Continue with the right child + traverseSearchCondition(pSearchCondition->getChild(2)); + } + // The first element is an AND logical operation (again) + else if ( SQL_ISRULE(pSearchCondition,boolean_term) && pSearchCondition->count() == 3 ) + { + // Then process recursively (use the same row) + traverseSearchCondition(pSearchCondition->getChild(0)); +// if (! aIteratorStatus.IsSuccessful()) +// return; + + // Continue with the right child + traverseSearchCondition(pSearchCondition->getChild(2)); + } + // Else, process single search criteria (like =, !=, ..., LIKE, IS NULL etc.) + else if (SQL_ISRULE(pSearchCondition,comparison_predicate) ) + { + OUString aValue; + pSearchCondition->getChild(2)->parseNodeToStr( aValue, m_pImpl->m_xConnection, nullptr, false, false ); + traverseOnePredicate(pSearchCondition->getChild(0),aValue,pSearchCondition->getChild(2)); + impl_fillJoinConditions(pSearchCondition); +// if (! aIteratorStatus.IsSuccessful()) +// return; + } + else if (SQL_ISRULE(pSearchCondition,like_predicate) /*&& SQL_ISRULE(pSearchCondition->getChild(0),column_ref)*/) + { + OSL_ENSURE(pSearchCondition->count() == 2,"OSQLParseTreeIterator: error in parse tree!"); + const OSQLParseNode* pPart2 = pSearchCondition->getChild(1); + + sal_Int32 nCurentPos = pPart2->count()-2; + + OSQLParseNode * pNum_value_exp = pPart2->getChild(nCurentPos); + OSQLParseNode * pOptEscape = pPart2->getChild(nCurentPos+1); + + OSL_ENSURE(pNum_value_exp != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(pOptEscape != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + + if (pOptEscape->count() != 0) + { + // aIteratorStatus.setStatementTooComplex(); + return; + } + + OUString aValue; + OSQLParseNode * pParam = nullptr; + if (SQL_ISRULE(pNum_value_exp,parameter)) + pParam = pNum_value_exp; + else if(pNum_value_exp->isToken()) + // Normal value + aValue = pNum_value_exp->getTokenValue(); + else + { + pNum_value_exp->parseNodeToStr( aValue, m_pImpl->m_xConnection, nullptr, false, false ); + pParam = pNum_value_exp; + } + + traverseOnePredicate(pSearchCondition->getChild(0),aValue,pParam); +// if (! aIteratorStatus.IsSuccessful()) +// return; + } + else if (SQL_ISRULE(pSearchCondition,in_predicate)) + { + OSL_ENSURE(pSearchCondition->count() == 2,"OSQLParseTreeIterator: error in parse tree!"); + const OSQLParseNode* pPart2 = pSearchCondition->getChild(1); + + traverseSearchCondition(pSearchCondition->getChild(0)); + // if (! aIteratorStatus.IsSuccessful()) return; + + OSQLParseNode* pChild = pPart2->getChild(2); + if ( SQL_ISRULE(pChild->getChild(0),subquery) ) + { + traverseTableNames( *m_pImpl->m_pSubTables ); + traverseSelectionCriteria(pChild->getChild(0)->getChild(1)); + } + else + { // '(' value_exp_commalist ')' + pChild = pChild->getChild(1); + sal_Int32 nCount = pChild->count(); + for (sal_Int32 i=0; i < nCount; ++i) + { + traverseSearchCondition(pChild->getChild(i)); + } + } + } + else if (SQL_ISRULE(pSearchCondition,test_for_null) /*&& SQL_ISRULE(pSearchCondition->getChild(0),column_ref)*/) + { + OSL_ENSURE(pSearchCondition->count() == 2,"OSQLParseTreeIterator: error in parse tree!"); + const OSQLParseNode* pPart2 = pSearchCondition->getChild(1); + OSL_ENSURE(SQL_ISTOKEN(pPart2->getChild(0),IS),"OSQLParseTreeIterator: error in parse tree!"); + + OUString aString; + traverseOnePredicate(pSearchCondition->getChild(0),aString,nullptr); + // if (! aIteratorStatus.IsSuccessful()) return; + } + else if (SQL_ISRULE(pSearchCondition,num_value_exp) || SQL_ISRULE(pSearchCondition,term)) + { + OUString aString; + traverseOnePredicate(pSearchCondition->getChild(0),aString,pSearchCondition->getChild(0)); + traverseOnePredicate(pSearchCondition->getChild(2),aString,pSearchCondition->getChild(2)); + } + // Just pass on the error +} + +void OSQLParseTreeIterator::traverseParameter(const OSQLParseNode* _pParseNode + ,const OSQLParseNode* _pParentNode + ,const OUString& _aColumnName + ,OUString& _aTableRange + ,const OUString& _rColumnAlias) +{ + if ( !SQL_ISRULE( _pParseNode, parameter ) ) + return; + + if ( !( m_pImpl->m_nIncludeMask & TraversalParts::Parameters ) ) + // parameters not to be included in the traversal + return; + + OSL_ENSURE(_pParseNode->count() > 0,"OSQLParseTreeIterator: error in parse tree!"); + OSQLParseNode * pMark = _pParseNode->getChild(0); + OUString sParameterName; + + if (SQL_ISPUNCTUATION(pMark,"?")) + { + sParameterName = !_rColumnAlias.isEmpty() + ? _rColumnAlias + : !_aColumnName.isEmpty() + ? _aColumnName + : OUString("?"); + } + else if (SQL_ISPUNCTUATION(pMark,":")) + { + sParameterName = _pParseNode->getChild(1)->getTokenValue(); + } + else if (SQL_ISPUNCTUATION(pMark,"[")) + { + sParameterName = _pParseNode->getChild(1)->getTokenValue(); + } + else + { + SAL_WARN( "connectivity.parse","OSQLParseTreeIterator: error in parse tree!"); + } + + // found a parameter + if ( _pParentNode && (SQL_ISRULE(_pParentNode,general_set_fct) || SQL_ISRULE(_pParentNode,set_fct_spec)) ) + {// found a function as column_ref + OUString sFunctionName; + _pParentNode->getChild(0)->parseNodeToStr( sFunctionName, m_pImpl->m_xConnection, nullptr, false, false ); + const sal_uInt32 nCount = _pParentNode->count(); + sal_uInt32 i = 0; + for(; i < nCount;++i) + { + if ( _pParentNode->getChild(i) == _pParseNode ) + break; + } + sal_Int32 nType = ::connectivity::OSQLParser::getFunctionParameterType( _pParentNode->getChild(0)->getTokenID(), i-1); + + rtl::Reference<OParseColumn> pColumn = new OParseColumn( sParameterName, + OUString(), + OUString(), + OUString(), + ColumnValue::NULLABLE_UNKNOWN, + 0, + 0, + nType, + false, + false, + isCaseSensitive(), + OUString(), + OUString(), + OUString()); + pColumn->setFunction(true); + pColumn->setAggregateFunction(true); + pColumn->setRealName(sFunctionName); + m_aParameters->push_back(pColumn); + } + else + { + bool bNotFound = true; + OSQLColumns::const_iterator aIter = ::connectivity::find( + m_aSelectColumns->begin(), + m_aSelectColumns->end(), + _aColumnName,::comphelper::UStringMixEqual( isCaseSensitive() ) + ); + if(aIter != m_aSelectColumns->end()) + { + rtl::Reference<OParseColumn> pNewColumn = new OParseColumn(*aIter,isCaseSensitive()); + pNewColumn->setName(sParameterName); + pNewColumn->setRealName(_aColumnName); + m_aParameters->push_back(pNewColumn); + bNotFound = false; + } + else if(!_aColumnName.isEmpty())// search in the tables for the right one + { + + Reference<XPropertySet> xColumn = findColumn( _aColumnName, _aTableRange, true ); + + if ( xColumn.is() ) + { + rtl::Reference<OParseColumn> pNewColumn = new OParseColumn(xColumn,isCaseSensitive()); + pNewColumn->setName(sParameterName); + pNewColumn->setRealName(_aColumnName); + m_aParameters->push_back(pNewColumn); + bNotFound = false; + } + } + if ( bNotFound ) + { + sal_Int32 nType = DataType::VARCHAR; + OSQLParseNode* pParent = _pParentNode ? _pParentNode->getParent() : nullptr; + if ( pParent && (SQL_ISRULE(pParent,general_set_fct) || SQL_ISRULE(pParent,set_fct_spec)) ) + { + const sal_uInt32 nCount = _pParentNode->count(); + sal_uInt32 i = 0; + for(; i < nCount;++i) + { + if ( _pParentNode->getChild(i) == _pParseNode ) + break; + } + nType = ::connectivity::OSQLParser::getFunctionParameterType( pParent->getChild(0)->getTokenID(), i+1); + } + + OUString aNewColName(getUniqueColumnName(getSelectColumnNames(), sParameterName)); + + rtl::Reference<OParseColumn> pColumn = new OParseColumn(aNewColName, + OUString(), + OUString(), + OUString(), + ColumnValue::NULLABLE_UNKNOWN, + 0, + 0, + nType, + false, + false, + isCaseSensitive(), + OUString(), + OUString(), + OUString()); + pColumn->setName(aNewColName); + pColumn->setRealName(sParameterName); + m_aParameters->push_back(pColumn); + } + } +} + +void OSQLParseTreeIterator::traverseOnePredicate( + OSQLParseNode const * pColumnRef, + OUString& rValue, + OSQLParseNode const * pParseNode) +{ + if ( !pParseNode ) + return; + + // Column name (and TableRange): + OUString aColumnName, aTableRange, sColumnAlias; + getColumnRange( pColumnRef, aColumnName, aTableRange, sColumnAlias); + + OUString aName; + + /*if (SQL_ISRULE(pParseNode,parameter)) + traverseParameter( pParseNode, pColumnRef, aColumnName, aTableRange, sColumnAlias ); + else */if (SQL_ISRULE(pParseNode,column_ref))// Column-Name (and TableRange): + getColumnRange(pParseNode,aName,rValue); + else + { + traverseSearchCondition(pParseNode); + // if (! aIteratorStatus.IsSuccessful()) return; + } +} + + +void OSQLParseTreeIterator::traverseAll() +{ + impl_traverse( TraversalParts::All ); +} + + +void OSQLParseTreeIterator::impl_traverse( TraversalParts _nIncludeMask ) +{ + // resets our errors + m_xErrors.reset(); + + m_pImpl->m_nIncludeMask = _nIncludeMask; + + if ( !traverseTableNames( *m_pImpl->m_pTables ) ) + return; + + switch ( m_eStatementType ) + { + case OSQLStatementType::Select: + { + const OSQLParseNode* pSelectNode = m_pParseTree; + traverseParameters( pSelectNode ); + if ( !traverseSelectColumnNames( pSelectNode ) + || !traverseOrderByColumnNames( pSelectNode ) + || !traverseGroupByColumnNames( pSelectNode ) + || !traverseSelectionCriteria( pSelectNode ) + ) + return; + } + break; + case OSQLStatementType::CreateTable: + { + //0 | 1 | 2 |3| 4 |5 + //create table sc.foo ( a char(20), b char ) + const OSQLParseNode* pCreateNode = m_pParseTree->getChild(4); + traverseCreateColumns(pCreateNode); + } + break; + case OSQLStatementType::Insert: + break; + default: + break; + } +} + +// Dummy implementations + + +OSQLTable OSQLParseTreeIterator::impl_createTableObject( const OUString& rTableName, + const OUString& rCatalogName, const OUString& rSchemaName ) +{ + OSL_PRECOND( m_eStatementType == OSQLStatementType::CreateTable, + "OSQLParseTreeIterator::impl_createTableObject: only to be called for CREATE TABLE statements!" ); + // (in all other cases, m_pTables is to contain the table objects as obtained from the tables + // container of the connection (m_xTablesContainer) + + OSQLTable aReturnTable = new OTable( + nullptr, + false, + rTableName, + "Table", + "New Created Table", + rSchemaName, + rCatalogName + ); + return aReturnTable; +} + +void OSQLParseTreeIterator::appendColumns(const OUString& _rTableAlias, const OSQLTable& _rTable) +{ + if (!_rTable.is()) + return; + + Reference<XNameAccess> xColumns = _rTable->getColumns(); + if ( !xColumns.is() ) + return; + + Sequence< OUString > aColNames = xColumns->getElementNames(); + const OUString* pBegin = aColNames.getConstArray(); + const OUString* pEnd = pBegin + aColNames.getLength(); + + ::comphelper::UStringMixLess aCompare(isCaseSensitive()); + std::vector<OUString> aSelectColumnNames = getSelectColumnNames(); + + for(;pBegin != pEnd;++pBegin) + { + OUString aName(getUniqueColumnName(aSelectColumnNames, *pBegin)); + Reference< XPropertySet > xColumn; + if(xColumns->hasByName(*pBegin) && (xColumns->getByName(*pBegin) >>= xColumn) && xColumn.is()) + { + rtl::Reference<OParseColumn> pColumn = new OParseColumn(aName + , getString(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPENAME))) + , getString(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DEFAULTVALUE))) + , getString(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DESCRIPTION))) + , getINT32(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISNULLABLE))) + , getINT32(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION))) + , getINT32(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE))) + , getINT32(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE))) + , getBOOL(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISAUTOINCREMENT))) + , getBOOL(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISCURRENCY))) + , isCaseSensitive() + , getString(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CATALOGNAME))) + , getString(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCHEMANAME))) + , getString(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TABLENAME)))); + + pColumn->setTableName(_rTableAlias); + pColumn->setRealName(*pBegin); + m_aSelectColumns->push_back(pColumn); + // update aSelectColumnNames with newly insert aName + aSelectColumnNames.insert(std::upper_bound(aSelectColumnNames.begin(), aSelectColumnNames.end(), aName, aCompare), aName); + } + else + impl_appendError( IParseContext::ErrorCode::InvalidColumn, pBegin, &_rTableAlias ); + } +} + +void OSQLParseTreeIterator::setSelectColumnName(const OUString & rColumnName,const OUString & rColumnAlias, const OUString & rTableRange, bool bFkt, sal_Int32 _nType, bool bAggFkt) +{ + if(rColumnName.toChar() == '*' && rTableRange.isEmpty()) + { // SELECT * ... + for (auto const& table : *m_pImpl->m_pTables) + appendColumns(table.first, table.second); + } + else if( rColumnName.toChar() == '*' && !rTableRange.isEmpty() ) + { // SELECT <table>.* + OSQLTables::const_iterator aFind = m_pImpl->m_pTables->find(rTableRange); + + if(aFind != m_pImpl->m_pTables->end()) + appendColumns(rTableRange, aFind->second); + } + else if ( rTableRange.isEmpty() ) + { // SELECT <something> ... + // without table specified + if ( !bFkt ) + { + Reference< XPropertySet> xNewColumn; + + for (auto const& table : *m_pImpl->m_pTables) + { + if ( !table.second.is() ) + continue; + + Reference<XNameAccess> xColumns = table.second->getColumns(); + Reference< XPropertySet > xColumn; + if ( !xColumns->hasByName( rColumnName ) + || !( xColumns->getByName( rColumnName ) >>= xColumn ) + ) + continue; + + OUString aNewColName(getUniqueColumnName(getSelectColumnNames(), rColumnAlias)); + + rtl::Reference<OParseColumn> pColumn = new OParseColumn(xColumn,isCaseSensitive()); + xNewColumn = pColumn; + pColumn->setTableName(table.first); + pColumn->setName(aNewColName); + pColumn->setRealName(rColumnName); + + break; + } + + if ( !xNewColumn.is() ) + { + // no function (due to the above !bFkt), no existing column + // => assume an expression + OUString aNewColName(getUniqueColumnName(getSelectColumnNames(), rColumnAlias)); + // did not find a column with this name in any of the tables + rtl::Reference<OParseColumn> pColumn = new OParseColumn( + aNewColName, + "VARCHAR", + // TODO: does this match with _nType? + // Or should be fill this from the getTypeInfo of the connection? + OUString(), + OUString(), + ColumnValue::NULLABLE_UNKNOWN, + 0, + 0, + _nType, + false, + false, + isCaseSensitive(), + OUString(), + OUString(), + OUString() + ); + + xNewColumn = pColumn; + pColumn->setRealName( rColumnName ); + } + + m_aSelectColumns->push_back( xNewColumn ); + } + else + { + OUString aNewColName(getUniqueColumnName(getSelectColumnNames(), rColumnAlias)); + + rtl::Reference<OParseColumn> pColumn = new OParseColumn(aNewColName,OUString(),OUString(),OUString(), + ColumnValue::NULLABLE_UNKNOWN,0,0,_nType,false,false,isCaseSensitive(), + OUString(),OUString(),OUString()); + pColumn->setFunction(true); + pColumn->setAggregateFunction(bAggFkt); + pColumn->setRealName(rColumnName); + + m_aSelectColumns->push_back(pColumn); + } + } + else // ColumnName and TableName exist + { + OSQLTables::const_iterator aFind = m_pImpl->m_pTables->find(rTableRange); + + bool bError = false; + if (aFind != m_pImpl->m_pTables->end() && aFind->second.is()) + { + if (bFkt) + { + OUString aNewColName(getUniqueColumnName(getSelectColumnNames(), rColumnAlias)); + + rtl::Reference<OParseColumn> pColumn = new OParseColumn(aNewColName,OUString(),OUString(),OUString(), + ColumnValue::NULLABLE_UNKNOWN,0,0,_nType,false,false,isCaseSensitive(), + OUString(),OUString(),OUString()); + pColumn->setFunction(true); + pColumn->setAggregateFunction(bAggFkt); + pColumn->setRealName(rColumnName); + SAL_WARN("connectivity.parse", "Trying to construct a column with Function==true and a TableName; this makes no sense."); + assert(false); + pColumn->setTableName(aFind->first); + + m_aSelectColumns->push_back(pColumn); + } + else + { + Reference< XPropertySet > xColumn; + if (aFind->second->getColumns()->hasByName(rColumnName) && (aFind->second->getColumns()->getByName(rColumnName) >>= xColumn)) + { + OUString aNewColName(getUniqueColumnName(getSelectColumnNames(), rColumnAlias)); + + rtl::Reference<OParseColumn> pColumn = new OParseColumn(xColumn,isCaseSensitive()); + pColumn->setName(aNewColName); + pColumn->setRealName(rColumnName); + pColumn->setTableName(aFind->first); + + m_aSelectColumns->push_back(pColumn); + } + else + bError = true; + } + } + else + bError = true; + + // Table does not exist or lacking field + if (bError) + { + OUString aNewColName(getUniqueColumnName(getSelectColumnNames(), rColumnAlias)); + + rtl::Reference<OParseColumn> pColumn = new OParseColumn(aNewColName,OUString(),OUString(),OUString(), + ColumnValue::NULLABLE_UNKNOWN,0,0,DataType::VARCHAR,false,false,isCaseSensitive(), + OUString(),OUString(),OUString()); + pColumn->setFunction(true); + pColumn->setAggregateFunction(bAggFkt); + + m_aSelectColumns->push_back(pColumn); + } + } +} + +std::vector<OUString> OSQLParseTreeIterator::getSelectColumnNames() const +{ + ::comphelper::UStringMixLess aCompare(isCaseSensitive()); + + std::vector<OUString> aColumnNames; + OUString sPropertyName = OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME); + for (const auto& col : *m_aSelectColumns) + aColumnNames.push_back(getString(col->getPropertyValue(sPropertyName))); + std::sort(aColumnNames.begin(), aColumnNames.end(), aCompare); + + return aColumnNames; +} + +OUString OSQLParseTreeIterator::getUniqueColumnName(const std::vector<OUString>& rColumnNames, const OUString& rColumnName) const +{ + ::comphelper::UStringMixLess aCompare(isCaseSensitive()); + if (!std::binary_search(rColumnNames.begin(), rColumnNames.end(), rColumnName, aCompare)) + return rColumnName; + + OUString aAlias; + sal_Int32 i=1; + do + { + aAlias = rColumnName + OUString::number(i++); + } + while (std::binary_search(rColumnNames.begin(), rColumnNames.end(), aAlias, aCompare)); + return aAlias; +} + +void OSQLParseTreeIterator::setOrderByColumnName(const OUString & rColumnName, OUString & rTableRange, bool bAscending) +{ + Reference<XPropertySet> xColumn = findSelectColumn( rColumnName ); + if ( !xColumn.is() ) + xColumn = findColumn ( rColumnName, rTableRange, false ); + if ( xColumn.is() ) + m_aOrderColumns->push_back(new OOrderColumn( xColumn, rTableRange, isCaseSensitive(), bAscending ) ); + else + { + sal_Int32 nId = rColumnName.toInt32(); + if ( nId > 0 && o3tl::make_unsigned(nId) < m_aSelectColumns->size() ) + m_aOrderColumns->push_back( new OOrderColumn( (*m_aSelectColumns)[nId-1], isCaseSensitive(), bAscending ) ); + } + +#ifdef SQL_TEST_PARSETREEITERATOR + cout << "OSQLParseTreeIterator::setOrderByColumnName: " + << (const char *) rColumnName << ", " + << (const char *) rTableRange << ", " + << (bAscending ? "true" : "false") + << "\n"; +#endif +} + +void OSQLParseTreeIterator::setGroupByColumnName(const OUString & rColumnName, OUString & rTableRange) +{ + Reference<XPropertySet> xColumn = findColumn( rColumnName, rTableRange, false ); + if ( xColumn.is() ) + m_aGroupColumns->push_back(new OParseColumn(xColumn,isCaseSensitive())); + else + { + sal_Int32 nId = rColumnName.toInt32(); + if ( nId > 0 && o3tl::make_unsigned(nId) < m_aSelectColumns->size() ) + m_aGroupColumns->push_back(new OParseColumn((*m_aSelectColumns)[nId-1],isCaseSensitive())); + } + +#ifdef SQL_TEST_PARSETREEITERATOR + cout << "OSQLParseTreeIterator::setGroupByColumnName: " + << (const char *) rColumnName << ", " + << (const char *) rTableRange << ", " + << (bAscending ? "true" : "false") + << "\n"; +#endif +} + + +const OSQLParseNode* OSQLParseTreeIterator::getWhereTree() const +{ + if (!m_pParseTree) + return nullptr; + + // Analyse parse tree (depending on statement type) + // and set pointer to WHERE clause: + OSQLParseNode * pWhereClause = nullptr; + if(getStatementType() == OSQLStatementType::Select) + { + OSL_ENSURE(m_pParseTree->count() >= 4,"ParseTreeIterator: error in parse tree!"); + OSQLParseNode * pTableExp = m_pParseTree->getChild(3); + OSL_ENSURE(pTableExp != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(SQL_ISRULE(pTableExp,table_exp),"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(pTableExp->count() == TABLE_EXPRESSION_CHILD_COUNT,"OSQLParseTreeIterator: error in parse tree!"); + + pWhereClause = pTableExp->getChild(1); + } + else if (SQL_ISRULE(m_pParseTree,update_statement_searched) || + SQL_ISRULE(m_pParseTree,delete_statement_searched)) + { + pWhereClause = m_pParseTree->getChild(m_pParseTree->count()-1); + } + if(pWhereClause && pWhereClause->count() != 2) + pWhereClause = nullptr; + return pWhereClause; +} + + +const OSQLParseNode* OSQLParseTreeIterator::getOrderTree() const +{ + if (!m_pParseTree || getStatementType() != OSQLStatementType::Select) + return nullptr; + + // Analyse parse tree (depending on statement type) + // and set pointer to ORDER clause: + + assert(SQL_ISRULE(m_pParseTree, select_statement) || SQL_ISRULE(m_pParseTree, union_statement)); + + auto pParseTree = m_pParseTree; + if(SQL_ISRULE(m_pParseTree, union_statement)) + { + assert(m_pParseTree->count() == 4); + pParseTree = pParseTree->getChild(3); + // since UNION is left-associative (at least in our grammar), + // possibly the left-hand (m_pParseTree->getChild(0)) is a union_statement, + // but the right hand cannot. + assert(SQL_ISRULE(pParseTree, select_statement)); + } + + OSQLParseNode * pOrderClause = nullptr; + OSL_ENSURE(pParseTree->count() == 4, "OSQLParseTreeIterator::getOrderTree: expected a SELECT, and a SELECT must have exactly four children"); + OSQLParseNode * pTableExp = pParseTree->getChild(3); + OSL_ENSURE(pTableExp != nullptr, "OSQLParseTreeIterator::getOrderTree: got NULL table_exp"); + OSL_ENSURE(SQL_ISRULE(pTableExp, table_exp), "OSQLParseTreeIterator::getOrderTree: expected table_exp but got something else"); + OSL_ENSURE(pTableExp->count() == TABLE_EXPRESSION_CHILD_COUNT,"OSQLParseTreeIterator::getOrderTree: table_exp doesn't have the expected number of children"); + // tdf#141115 upgrade the above to an assert; + // this cannot go well if there are too few children + assert(pTableExp->count() == TABLE_EXPRESSION_CHILD_COUNT); + + pOrderClause = pTableExp->getChild(ORDER_BY_CHILD_POS); + // If it is an order_by, it must not be empty + if(pOrderClause->count() != 3) + pOrderClause = nullptr; + return pOrderClause; +} + +const OSQLParseNode* OSQLParseTreeIterator::getGroupByTree() const +{ + if (!m_pParseTree || getStatementType() != OSQLStatementType::Select) + return nullptr; + + // Analyse parse tree (depending on statement type) + // and set pointer to ORDER clause: + OSQLParseNode * pGroupClause = nullptr; + OSL_ENSURE(m_pParseTree->count() >= 4,"ParseTreeIterator: error in parse tree!"); + OSQLParseNode * pTableExp = m_pParseTree->getChild(3); + OSL_ENSURE(pTableExp != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(SQL_ISRULE(pTableExp,table_exp),"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(pTableExp->count() == TABLE_EXPRESSION_CHILD_COUNT,"OSQLParseTreeIterator: error in parse tree!"); + + pGroupClause = pTableExp->getChild(2); + // If it is an order_by, it must not be empty + if(pGroupClause->count() != 3) + pGroupClause = nullptr; + return pGroupClause; +} + +const OSQLParseNode* OSQLParseTreeIterator::getHavingTree() const +{ + if (!m_pParseTree || getStatementType() != OSQLStatementType::Select) + return nullptr; + + // Analyse parse tree (depending on statement type) + // and set pointer to ORDER clause: + OSQLParseNode * pHavingClause = nullptr; + OSL_ENSURE(m_pParseTree->count() >= 4,"ParseTreeIterator: error in parse tree!"); + OSQLParseNode * pTableExp = m_pParseTree->getChild(3); + OSL_ENSURE(pTableExp != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(SQL_ISRULE(pTableExp,table_exp),"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(pTableExp->count() == TABLE_EXPRESSION_CHILD_COUNT,"OSQLParseTreeIterator: error in parse tree!"); + + pHavingClause = pTableExp->getChild(3); + // If it is an order_by, then it must not be empty + if(pHavingClause->count() < 1) + pHavingClause = nullptr; + return pHavingClause; +} + +bool OSQLParseTreeIterator::isTableNode(const OSQLParseNode* _pTableNode) +{ + return _pTableNode && (SQL_ISRULE(_pTableNode,catalog_name) || + SQL_ISRULE(_pTableNode,schema_name) || + SQL_ISRULE(_pTableNode,table_name)); +} + +const OSQLParseNode* OSQLParseTreeIterator::getSimpleWhereTree() const +{ + const OSQLParseNode* pNode = getWhereTree(); + return pNode ? pNode->getChild(1) : nullptr; +} + +const OSQLParseNode* OSQLParseTreeIterator::getSimpleOrderTree() const +{ + const OSQLParseNode* pNode = getOrderTree(); + return pNode ? pNode->getChild(2) : nullptr; +} + +const OSQLParseNode* OSQLParseTreeIterator::getSimpleGroupByTree() const +{ + const OSQLParseNode* pNode = getGroupByTree(); + return pNode ? pNode->getChild(2) : nullptr; +} + +const OSQLParseNode* OSQLParseTreeIterator::getSimpleHavingTree() const +{ + const OSQLParseNode* pNode = getHavingTree(); + return pNode ? pNode->getChild(1) : nullptr; +} + + +Reference< XPropertySet > OSQLParseTreeIterator::findSelectColumn( std::u16string_view rColumnName ) +{ + for (auto const& lookupColumn : *m_aSelectColumns) + { + Reference< XPropertySet > xColumn( lookupColumn ); + try + { + OUString sName; + xColumn->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_NAME ) ) >>= sName; + if ( sName == rColumnName ) + return xColumn; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.parse"); + } + } + return nullptr; +} + + +Reference< XPropertySet > OSQLParseTreeIterator::findColumn( const OUString & rColumnName, OUString & rTableRange, bool _bLookInSubTables ) +{ + Reference< XPropertySet > xColumn = findColumn( *m_pImpl->m_pTables, rColumnName, rTableRange ); + if ( !xColumn.is() && _bLookInSubTables ) + xColumn = findColumn( *m_pImpl->m_pSubTables, rColumnName, rTableRange ); + return xColumn; +} + + +Reference< XPropertySet > OSQLParseTreeIterator::findColumn(const OSQLTables& _rTables, const OUString & rColumnName, OUString & rTableRange) +{ + Reference< XPropertySet > xColumn; + if ( !rTableRange.isEmpty() ) + { + OSQLTables::const_iterator aFind = _rTables.find(rTableRange); + + if ( aFind != _rTables.end() + && aFind->second.is() + && aFind->second->getColumns().is() + && aFind->second->getColumns()->hasByName(rColumnName) ) + aFind->second->getColumns()->getByName(rColumnName) >>= xColumn; + } + if ( !xColumn.is() ) + { + for (auto const& table : _rTables) + { + if ( table.second.is() ) + { + Reference<XNameAccess> xColumns = table.second->getColumns(); + if( xColumns.is() && xColumns->hasByName(rColumnName) && (xColumns->getByName(rColumnName) >>= xColumn) ) + { + OSL_ENSURE(xColumn.is(),"Column isn't a propertyset!"); + // Cannot take "rTableRange = table.first" because that is the fully composed name + // that is, catalogName.schemaName.tableName + rTableRange = getString(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TABLENAME))); + break; // This column must only exits once + } + } + } + } + return xColumn; +} + + +void OSQLParseTreeIterator::impl_appendError( IParseContext::ErrorCode _eError, const OUString* _pReplaceToken1, const OUString* _pReplaceToken2 ) +{ + OUString sErrorMessage = m_rParser.getContext().getErrorMessage( _eError ); + if ( _pReplaceToken1 ) + { + bool bTwoTokens = ( _pReplaceToken2 != nullptr ); + const char* pPlaceHolder1 = bTwoTokens ? "#1" : "#"; + const OUString sPlaceHolder1 = OUString::createFromAscii( pPlaceHolder1 ); + + sErrorMessage = sErrorMessage.replaceFirst( sPlaceHolder1, *_pReplaceToken1 ); + if ( _pReplaceToken2 ) + sErrorMessage = sErrorMessage.replaceFirst( "#2" , *_pReplaceToken2 ); + } + + impl_appendError( SQLException( + sErrorMessage, nullptr, getStandardSQLState( StandardSQLState::GENERAL_ERROR ), 1000, Any() ) ); +} + + +void OSQLParseTreeIterator::impl_appendError( const SQLException& _rError ) +{ + SAL_WARN("connectivity.parse", "Adding error " << exceptionToString(Any(_rError))); + if ( m_xErrors ) + { + SQLException* pErrorChain = &*m_xErrors; + while ( pErrorChain->NextException.hasValue() ) + pErrorChain = static_cast< SQLException* >( pErrorChain->NextException.pData ); + pErrorChain->NextException <<= _rError; + } + else + m_xErrors = _rError; +} + +sal_Int32 OSQLParseTreeIterator::getFunctionReturnType(const OSQLParseNode* _pNode ) +{ + sal_Int32 nType = DataType::OTHER; + OUString sFunctionName; + if ( SQL_ISRULE(_pNode,length_exp) ) + { + _pNode->getChild(0)->getChild(0)->parseNodeToStr(sFunctionName, m_pImpl->m_xConnection, nullptr, false, false ); + nType = ::connectivity::OSQLParser::getFunctionReturnType( sFunctionName, &m_rParser.getContext() ); + } + else if ( SQL_ISRULE(_pNode,num_value_exp) || SQL_ISRULE(_pNode,term) || SQL_ISRULE(_pNode,factor) ) + { + nType = DataType::DOUBLE; + } + else + { + _pNode->getChild(0)->parseNodeToStr(sFunctionName, m_pImpl->m_xConnection, nullptr, false, false ); + + // MIN and MAX have another return type, we have to check the expression itself. + // @see http://qa.openoffice.org/issues/show_bug.cgi?id=99566 + if ( SQL_ISRULE(_pNode,general_set_fct) && (SQL_ISTOKEN(_pNode->getChild(0),MIN) || SQL_ISTOKEN(_pNode->getChild(0),MAX) )) + { + const OSQLParseNode* pValueExp = _pNode->getChild(3); + if (SQL_ISRULE(pValueExp,column_ref)) + { + OUString sColumnName; + OUString aTableRange; + getColumnRange(pValueExp,sColumnName,aTableRange); + OSL_ENSURE(!sColumnName.isEmpty(),"Columnname must not be empty!"); + Reference<XPropertySet> xColumn = findColumn( sColumnName, aTableRange, true ); + + if ( xColumn.is() ) + { + xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_TYPE)) >>= nType; + } + } + else + { + if ( SQL_ISRULE(pValueExp,num_value_exp) || SQL_ISRULE(pValueExp,term) || SQL_ISRULE(pValueExp,factor) ) + { + nType = DataType::DOUBLE; + } + else if ( SQL_ISRULE(pValueExp,datetime_primary) ) + { + switch(pValueExp->getChild(0)->getTokenID() ) + { + case SQL_TOKEN_CURRENT_DATE: + nType = DataType::DATE; + break; + case SQL_TOKEN_CURRENT_TIME: + nType = DataType::TIME; + break; + case SQL_TOKEN_CURRENT_TIMESTAMP: + nType = DataType::TIMESTAMP; + break; + } + } + else if ( SQL_ISRULE(pValueExp,value_exp_primary) ) + { + nType = getFunctionReturnType(pValueExp->getChild(1)); + } + else if ( SQL_ISRULE(pValueExp,concatenation) + || SQL_ISRULE(pValueExp,char_factor) + || SQL_ISRULE(pValueExp,bit_value_fct) + || SQL_ISRULE(pValueExp,char_value_fct) + || SQL_ISRULE(pValueExp,char_substring_fct) + || SQL_ISRULE(pValueExp,fold) + || SQL_ISTOKEN(pValueExp,STRING) ) + { + nType = DataType::VARCHAR; + } + } + if ( nType == DataType::OTHER ) + nType = DataType::DOUBLE; + } + else + nType = ::connectivity::OSQLParser::getFunctionReturnType( sFunctionName, &m_rParser.getContext() ); + } + + return nType; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |