Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/connectivity/source/parse/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 83 kB image not shown  

Quelle  sqliterator.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <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 <comphelper/diagnose_ex.hxx>
#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>
#include <utility>

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>( UStringMixLess(m_bIsCaseSensitive) );
            m_pSubTables = std::make_shared<OSQLTables>( UStringMixLess(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, OUString _aForbiddenQueryName )
            :m_rpAllForbiddenNames( _rIteratorImpl.m_pForbiddenQueryNames )
            ,m_sForbiddenQueryName(std::move( _aForbiddenQueryName ))
        {
            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 OUString s_sWildcard = u"%"_ustr ;

        // we want all catalogues, all schemas, all tables
        Sequence< OUString > sTableTypes { u"VIEW"_ustr, u"TABLE"_ustr, 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 );
    }
    catchconst 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 ] = std::move(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.emplace_back(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);

    assert(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 >&&nbsp;_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, falsefalse );
                _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;
                        }
                    }
                    catchconst 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,&nbsp;_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(u"*"_ustr, u""_ustr, u""_ustr);
    }
    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, falsefalse );
                setSelectColumnName(u"*"_ustr, u""_ustr, 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);
    assert(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);
    assert(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);
    assert(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);
        assert(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);
        }
        assert(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, falsefalse );
        }
        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(  u"param"_ustr  );
        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, falsefalse );
        } // 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, falsefalse );
        }
        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, falsefalse );
                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);
        assert(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, nullptrfalsefalse );
        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 nCurrentPos = pPart2->count()-2;

        OSQLParseNode * pNum_value_exp  = pPart2->getChild(nCurrentPos);
        OSQLParseNode * pOptEscape      = pPart2->getChild(nCurrentPos+1);

        assert(pNum_value_exp != nullptr && "OSQLParseTreeIterator: error in parse tree!");
        assert(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, falsefalse );
            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
                        :   u"?"_ustr;
    }
    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, falsefalse );
        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 */

        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,
        u"Table"_ustr,
        u"New Created Table"_ustr,
        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;

    ::comphelper::UStringMixLess aCompare(isCaseSensitive());
    std::vector<OUString> aSelectColumnNames = getSelectColumnNames();

    for (auto& colName : xColumns->getElementNames())
    {
        OUString aName(getUniqueColumnName(aSelectColumnNames, colName));
        Reference< XPropertySet > xColumn;
        if(xColumns->hasByName(colName) && (xColumns->getByName(colName) >>= 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(colName);
            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, &colName, &_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 )
        {
            rtl::Reference< OParseColumn> 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,
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=90 H=96 G=93

¤ Dauer der Verarbeitung: 0.22 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.