/* -*- 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 #include #include #include #include #include #include #include #include #include #include #ifdef SQL_TEST_PARSETREEITERATOR #include #endif #include #include #include #include #include #include #include #include #include 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( m_bIsCaseSensitive ); m_pSubTables = std::make_shared( 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(); 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 const char s_sWildcard[] = "%" ; // we want all catalogues, all schemas, all tables Sequence< OUString > sTableTypes(3); sTableTypes[0] = "VIEW"; sTableTypes[1] = "TABLE"; sTableTypes[2] = s_sWildcard; // just to be sure to include anything else... if ( _rxDBMeta.is() ) { sComposedName.clear(); Reference< XResultSet> xRes = _rxDBMeta->getTables( !_rCatalog.isEmpty() ? makeAny( _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(_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 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); Reference< XPropertySet> xCol = pColumn; m_aCreateColumns->push_back(xCol); } } } } 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(m_aSelectColumns, "*", "", ""); } 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(m_aSelectColumns, "*", "", 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(m_aSelectColumns,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(_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); 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()) { 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 xColumn = findColumn( _aColumnName, _aTableRange, true ); if ( xColumn.is() ) { 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( sParameterName ) ); 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(::rtl::Reference const & _rColumns,const OUString& _rTableAlias,const OSQLTable& _rTable) { if (!_rTable.is()) return; Reference xColumns = _rTable->getColumns(); if ( !xColumns.is() ) return; Sequence< OUString > aColNames = xColumns->getElementNames(); const OUString* pBegin = aColNames.getConstArray(); const OUString* pEnd = pBegin + aColNames.getLength(); for(;pBegin != pEnd;++pBegin) { OUString aName(getUniqueColumnName(*pBegin)); Reference< XPropertySet > xColumn; if(xColumns->hasByName(*pBegin) && (xColumns->getByName(*pBegin) >>= xColumn) && xColumn.is()) { 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); Reference< XPropertySet> xCol = pColumn; _rColumns->push_back(xCol); } else impl_appendError( IParseContext::ErrorCode::InvalidColumn, pBegin, &_rTableAlias ); } } void OSQLParseTreeIterator::setSelectColumnName(::rtl::Reference const & _rColumns,const OUString & rColumnName,const OUString & rColumnAlias, const OUString & rTableRange, bool bFkt, sal_Int32 _nType, bool bAggFkt) { if(rColumnName.toChar() == '*' && rTableRange.isEmpty()) { // SELECT * ... OSL_ENSURE(_rColumns == m_aSelectColumns,"Invalid columns used here!"); for (auto const& table : *m_pImpl->m_pTables) appendColumns(_rColumns,table.first,table.second); } else if( rColumnName.toChar() == '*' && !rTableRange.isEmpty() ) { // SELECT .* OSL_ENSURE(_rColumns == m_aSelectColumns,"Invalid columns used here!"); OSQLTables::const_iterator aFind = m_pImpl->m_pTables->find(rTableRange); if(aFind != m_pImpl->m_pTables->end()) appendColumns(_rColumns,rTableRange,aFind->second); } else if ( rTableRange.isEmpty() ) { // SELECT ... // without table specified if ( !bFkt ) { Reference< XPropertySet> xNewColumn; for (auto const& table : *m_pImpl->m_pTables) { if ( !table.second.is() ) continue; Reference xColumns = table.second->getColumns(); Reference< XPropertySet > xColumn; if ( !xColumns->hasByName( rColumnName ) || !( xColumns->getByName( rColumnName ) >>= xColumn ) ) continue; OUString aNewColName(getUniqueColumnName(rColumnAlias)); 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( rColumnAlias ) ); // did not find a column with this name in any of the tables 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 ); } _rColumns->push_back( xNewColumn ); } else { OUString aNewColName(getUniqueColumnName(rColumnAlias)); 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); Reference< XPropertySet> xCol = pColumn; _rColumns->push_back(xCol); } } 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(rColumnAlias)); 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); Reference< XPropertySet> xCol = pColumn; _rColumns->push_back(xCol); } else { Reference< XPropertySet > xColumn; if (aFind->second->getColumns()->hasByName(rColumnName) && (aFind->second->getColumns()->getByName(rColumnName) >>= xColumn)) { OUString aNewColName(getUniqueColumnName(rColumnAlias)); OParseColumn* pColumn = new OParseColumn(xColumn,isCaseSensitive()); pColumn->setName(aNewColName); pColumn->setRealName(rColumnName); pColumn->setTableName(aFind->first); Reference< XPropertySet> xCol = pColumn; _rColumns->push_back(xCol); } else bError = true; } } else bError = true; // Table does not exist or lacking field if (bError) { OUString aNewColName(getUniqueColumnName(rColumnAlias)); 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); Reference< XPropertySet> xCol = pColumn; _rColumns->push_back(xCol); } } } OUString OSQLParseTreeIterator::getUniqueColumnName(const OUString & rColumnName) const { OUString aAlias(rColumnName); OSQLColumns::const_iterator aIter = find( m_aSelectColumns->begin(), m_aSelectColumns->end(), aAlias, ::comphelper::UStringMixEqual( isCaseSensitive() ) ); sal_Int32 i=1; while(aIter != m_aSelectColumns->end()) { aAlias = rColumnName + OUString::number(i++); aIter = find( m_aSelectColumns->begin(), m_aSelectColumns->end(), aAlias, ::comphelper::UStringMixEqual( isCaseSensitive() ) ); } return aAlias; } void OSQLParseTreeIterator::setOrderByColumnName(const OUString & rColumnName, OUString & rTableRange, bool bAscending) { Reference 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 && nId < static_cast(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 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 && nId < static_cast(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: OSQLParseNode * pOrderClause = 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!"); 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( const OUString & 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 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 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: */