/* -*- 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 .
*/
// here we define our functions used in the anonymous namespace to get our header file smaller // please look at the book LargeScale C++ to know why namespace
{ constchar C_AND[] = " AND "; constchar C_OR[] = " OR ";
ScopedVclPtrInstance< OQueryTableConnection > aInfo(pTableView, xInfoData); // Because OQueryTableConnection never takes ownership of the data passed to it, but only remembers the pointer, // this pointer to a local variable is not critical, as xInfoData and aInfo have the same lifetime
pTableView->NotifyTabConnection( *aInfo );
} else
{
OUString aSourceFieldName(_aDragLeft->GetField());
OUString aDestFieldName(_aDragRight->GetField()); // the connection could point on the other side if (pConn->GetSourceWin() == _aDragRight->GetTabWindow())
std::swap(aSourceFieldName, aDestFieldName);
pConn->GetData()->AppendConnLine( aSourceFieldName,aDestFieldName);
pConn->UpdateLineList(); // Modified-Flag // SetModified(); // and redraw
pConn->RecalcLines(); // for the following Invalidate, the new Connection must first be able // to determine its BoundingRect
pConn->InvalidateConnection();
}
}
OUString ParseCondition( OQueryController& rController
,const ::connectivity::OSQLParseNode* pCondition
,const OUString& _sDecimal
,const css::lang::Locale& _rLocale
,sal_uInt32 _nStartIndex)
{
OUString aCondition;
Reference< XConnection> xConnection = rController.getConnection(); if ( xConnection.is() )
{
sal_uInt32 nCount = pCondition->count(); for(sal_uInt32 i = _nStartIndex ; i < nCount ; ++i)
pCondition->getChild(i)->parseNodeToPredicateStr(aCondition,
xConnection,
rController.getNumberFormatter(),
_rLocale,
_sDecimal,
&rController.getParser().getContext());
} return aCondition;
}
SqlParseError FillOuterJoins(OQueryDesignView const * _pView, const ::connectivity::OSQLParseNode* pTableRefList)
{
SqlParseError eErrorCode = eOk;
sal_uInt32 nCount = pTableRefList->count(); bool bError = false; for (sal_uInt32 i=0; !bError && i < nCount; ++i)
{ const ::connectivity::OSQLParseNode* pParseNode = pTableRefList->getChild(i); const ::connectivity::OSQLParseNode* pJoinNode = nullptr;
return aCondition.makeStringAndClear();
} /** JoinCycle looks for a join cycle and append it to the string @param _xConnection the connection @param _pEntryConn the table connection which holds the data @param _pEntryTabTo the corresponding table window @param _rJoin the String which will contain the resulting string
*/ void JoinCycle( const Reference< XConnection>& _xConnection,
OQueryTableConnection* _pEntryConn, const OQueryTableWindow* _pEntryTabTo,
OUString& _rJoin )
{
assert(_pEntryConn && "TableConnection can not be null!");
// if we have a none numeric field, the table alias could be in the name // otherwise we are not allowed to do this (e.g. 0.1 * PRICE ) if ( !field->isOtherFunction() )
{ // we have to look if we have alias.* here but before we have to check if the column doesn't already exist
OTableFieldDescRef aInfo = new OTableFieldDesc(); for (autoconst& table : rTabList)
{
OQueryTableWindow* pTabWin = static_cast<OQueryTableWindow*>(table.second.get());
if ( field->isAggregateFunction() )
{
OSL_ENSURE(!field->GetFunction().isEmpty(),"Function name must not be empty! ;-(");
aTmpStr = field->GetFunction() + "(" + aTmpStr.makeStringAndClear() + ")";
}
OUString aFieldName,aCriteria,aWhereStr,aHavingStr,aWork/*,aOrderStr*/; // print line by line joined with AND
sal_uInt16 nMaxCriteria = 0; for (autoconst& field : _rFieldList)
{
nMaxCriteria = std::max<sal_uInt16>(nMaxCriteria,static_cast<sal_uInt16>(field->GetCriteria().size()));
} try
{ const Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData(); const OUString aQuote = xMetaData->getIdentifierQuoteString(); const IParseContext& rContext = static_cast<OQueryController&>(_pView->getController()).getParser().getContext(); // * must not contain a filter : have I already shown the correct warning ? bool bCritsOnAsteriskWarning = false; // ** TMFS **
for (sal_uInt16 i=0 ; i < nMaxCriteria ; i++)
{
aHavingStr.clear();
aWhereStr.clear();
for (autoconst& field : _rFieldList)
{
aFieldName = field->GetField();
if (aFieldName.isEmpty()) continue;
aCriteria = field->GetCriteria( i ); if ( !aCriteria.isEmpty() )
{ // * is not allowed to contain any filter, only when used in combination an aggregate function if ( aFieldName.toChar() == '*' && field->isNoneFunction() )
{ // only show the messagebox the first time if (!bCritsOnAsteriskWarning)
{
std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(_pView->GetFrameWeld(),
VclMessageType::Warning, VclButtonsType::Ok,
DBA_RES(STR_QRY_CRITERIA_ON_ASTERISK)));
xBox->run();
}
bCritsOnAsteriskWarning = true; continue;
}
aWork = quoteTableAlias(bMulti,field->GetAlias(),aQuote);
aWhereStr += " "; // aCriteria could have some German numbers so I have to be sure here
OUString aErrorMsg;
Reference<XPropertySet> xColumn;
std::unique_ptr< ::connectivity::OSQLParseNode> pParseNode( _pView->getPredicateTreeFromEntry(field,aCriteria,aErrorMsg,xColumn)); if (pParseNode)
{ if (bMulti && !(field->isOtherFunction() || (aFieldName.toChar() == '*')))
pParseNode->replaceNodeValue(field->GetAlias(),aFieldName);
OUString aWhere = aWhereStr;
pParseNode->parseNodeToStr( aWhere,
xConnection,
&rContext, false,
!field->isOtherFunction() );
aWhereStr = aWhere;
} else
{
aWhereStr += aWork + "=" + aCriteria;
}
}
} // only once for each field elseif ( !i && field->isCondition() )
{ if (aWhereStr.isEmpty()) // no more criteria
aWhereStr += "("; // bracket else
aWhereStr += C_AND;
aWhereStr += field->GetField();
}
} if (!aWhereStr.isEmpty())
{
aWhereStr += ")"; // close bracket for the AND branch if (!rRetStr.isEmpty()) // are there conditions on the field?
rRetStr.append(C_OR); else// open bracket for the OR branch
rRetStr.append('(');
rRetStr.append(aWhereStr);
} if (!aHavingStr.isEmpty())
{
aHavingStr += ")"; // close bracket for the AND branch if (!rHavingStr.isEmpty()) // are there conditions on the field?
rHavingStr.append(C_OR); else// Open bracket for the OR branch
rHavingStr.append('(');
rHavingStr.append(aHavingStr);
}
}
if (!rRetStr.isEmpty())
rRetStr.append(')'); // close bracket for the OR branch if (!rHavingStr.isEmpty())
rHavingStr.append(')'); // close bracket for the OR branch
} catch(SQLException&)
{
OSL_FAIL("Failure while building where clause!");
} returntrue;
}
SqlParseError GenerateOrder( OQueryDesignView const * _pView,
OTableFields& _rFieldList, bool bMulti,
OUString& _rsRet)
{ const OQueryController& rController = static_cast<OQueryController&>(_pView->getController()); const Reference< XConnection>& xConnection = rController.getConnection(); if ( !xConnection.is() ) return eNoConnection;
SqlParseError eErrorCode = eOk;
OUString aColumnName;
OUString aWorkStr; try
{ constbool bColumnAliasInOrderBy = rController.getSdbMetaData().supportsColumnAliasInOrderBy();
Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData();
OUString aQuote = xMetaData->getIdentifierQuoteString(); // * must not contain filter - have I already shown the warning? bool bCritsOnAsteriskWarning = false; // ** TMFS ** for (autoconst& field : _rFieldList)
{
EOrderDir eOrder = field->GetOrderDir(); // only create a sort expression when the table name and the sort criteria are defined // otherwise they will be built in GenerateCriteria if ( eOrder != ORDER_NONE )
{
aColumnName = field->GetField(); if(aColumnName.toChar() == '*')
{ // only show the MessageBox the first time if (!bCritsOnAsteriskWarning)
{
std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(_pView->GetFrameWeld(),
VclMessageType::Warning, VclButtonsType::Ok,
DBA_RES(STR_QRY_ORDERBY_ON_ASTERISK)));
xBox->run();
}
bCritsOnAsteriskWarning = true; continue;
}
if ( bColumnAliasInOrderBy && !field->GetFieldAlias().isEmpty() )
{
aWorkStr += ::dbtools::quoteName(aQuote, field->GetFieldAlias());
} elseif ( field->isNumericOrAggregateFunction() )
{
OSL_ENSURE(!field->GetFunction().isEmpty(),"Function name cannot be empty! ;-(");
aWorkStr += field->GetFunction() + "("
+ quoteTableAlias(
bMulti, field->GetAlias(), aQuote); // only quote column name when we don't have a numeric if ( field->isNumeric() )
aWorkStr += aColumnName; else
aWorkStr += ::dbtools::quoteName(aQuote, aColumnName);
// and now all inner joins // these are implemented as // "FROM tbl1, tbl2 WHERE tbl1.col1=tlb2.col2" // rather than // "FROM tbl1 INNER JOIN tbl2 ON tbl1.col1=tlb2.col2" for (autoconst& connection : rConnList)
{
OQueryTableConnection* pEntryConn = static_cast<OQueryTableConnection*>(connection.get()); if(!pEntryConn->IsVisited())
{
searchAndAppendName(_xConnection, static_cast<OQueryTableWindow*>(pEntryConn->GetSourceWin()),
aTableNames,
aTableListStr);
searchAndAppendName(_xConnection, static_cast<OQueryTableWindow*>(pEntryConn->GetDestWin()),
aTableNames,
aTableListStr);
}
}
} // all tables that haven't a connection to anyone for (autoconst& table : *pTabList)
{ const OQueryTableWindow* pEntryTab = static_cast<const OQueryTableWindow*>(table.second.get()); if(!pEntryTab->ExistsAConn())
{
aTableListStr += BuildTable(_xConnection,pEntryTab) + ",";
}
}
for (autoconst& field : _rFieldList)
{ if ( field->IsGroupBy() )
{
OSL_ENSURE(!field->GetField().isEmpty(),"No Field Name available!;-(");
OUString sGroupByPart = quoteTableAlias(bMulti,field->GetAlias(),aQuote);
// only quote the field name when it isn't calculated if ( field->isNoneFunction() )
{
sGroupByPart += ::dbtools::quoteName(aQuote, field->GetField());
} else
{
OUString aTmp = field->GetField();
OUString aErrorMsg;
Reference<XPropertySet> xColumn;
std::unique_ptr< ::connectivity::OSQLParseNode> pParseNode(_pView->getPredicateTreeFromEntry(field,aTmp,aErrorMsg,xColumn)); if (pParseNode)
{
OUString sGroupBy;
pParseNode->getChild(0)->parseNodeToStr( sGroupBy,
xConnection,
&rController.getParser().getContext(), false,
!field->isOtherFunction());
sGroupByPart += sGroupBy;
} else
sGroupByPart += field->GetField();
} if ( aGroupByNames.find(sGroupByPart) == aGroupByNames.end() )
{
aGroupByNames.emplace(sGroupByPart,true);
aGroupByStr += sGroupByPart + ",";
}
}
} if ( !aGroupByStr.isEmpty() )
{
aGroupByStr = aGroupByStr.replaceAt(aGroupByStr.getLength()-1,1, u" " );
aGroupByStr = " GROUP BY " + aGroupByStr;
}
} catch(SQLException&)
{
OSL_FAIL("Failure while building group by!");
} return aGroupByStr;
}
SqlParseError GetORCriteria(OQueryDesignView* _pView,
OSelectionBrowseBox* _pSelectionBrw, const ::connectivity::OSQLParseNode * pCondition,
sal_uInt16& nLevel , bool bHaving = false, bool bAddOrOnOneLine = false);
SqlParseError GetSelectionCriteria( OQueryDesignView* _pView,
OSelectionBrowseBox* _pSelectionBrw, const ::connectivity::OSQLParseNode* pNode,
sal_uInt16& rLevel )
{ if (!pNode || !SQL_ISRULE(pNode, select_statement)) return eNoSelectStatement;
// nyi: more checking for the correct structure!
pNode = pNode->getChild(3)->getChild(1); // no where clause found if (!pNode || pNode->isLeaf()) return eOk;
// Next free sentence...
SqlParseError eErrorCode = eOk;
::connectivity::OSQLParseNode * pCondition = pNode->getChild(1); if ( pCondition ) // no where clause
{ // now we have to check the other conditions // first make the logical easier
::connectivity::OSQLParseNode::negateSearchCondition(pCondition);
::connectivity::OSQLParseNode *pNodeTmp = pNode->getChild(1);
// first extract the inner joins conditions
GetInnerJoinCriteria(_pView,pNodeTmp); // now simplify again, join are checked in ComparisonPredicate
::connectivity::OSQLParseNode::absorptions(pNodeTmp);
pNodeTmp = pNode->getChild(1);
// round brackets around the printout if (pCondition->count() == 3 &&
SQL_ISPUNCTUATION(pCondition->getChild(0),"(") &&
SQL_ISPUNCTUATION(pCondition->getChild(2),")"))
{
eErrorCode = GetORCriteria(_pView,_pSelectionBrw,pCondition->getChild(1),nLevel,bHaving,bAddOrOnOneLine);
} // OR condition // a searchcondition can only look like this: search_condition SQL_TOKEN_OR boolean_term elseif (SQL_ISRULE(pCondition,search_condition))
{ for (int i = 0; i < 3 && eErrorCode == eOk ; i+=2)
{ const ::connectivity::OSQLParseNode* pChild = pCondition->getChild(i); if ( SQL_ISRULE(pChild,search_condition) )
eErrorCode = GetORCriteria(_pView,_pSelectionBrw,pChild,nLevel,bHaving,bAddOrOnOneLine); else
{
eErrorCode = GetANDCriteria(_pView,_pSelectionBrw,pChild, nLevel,bHaving, i != 0 && bAddOrOnOneLine); if ( !bAddOrOnOneLine)
nLevel++;
}
}
} else
eErrorCode = GetANDCriteria( _pView,_pSelectionBrw,pCondition, nLevel, bHaving,bAddOrOnOneLine );
return eErrorCode;
} bool CheckOrCriteria(const ::connectivity::OSQLParseNode* _pCondition,::connectivity::OSQLParseNode* _pFirstColumnRef)
{ bool bRet = true;
::connectivity::OSQLParseNode* pFirstColumnRef = _pFirstColumnRef; for (size_t i = 0; bRet && i < _pCondition->count(); ++i)
{ const ::connectivity::OSQLParseNode* pChild = _pCondition->getChild(i); if ( pChild->isToken() ) continue; elseif ( SQL_ISRULE(pChild,search_condition) )
bRet = CheckOrCriteria(pChild,pFirstColumnRef); else
{ // this is a simple way to test columns are the same, may be we have to adjust this algo a little bit in future. :-)
::connectivity::OSQLParseNode* pSecondColumnRef = pChild->getByRule(::connectivity::OSQLParseNode::column_ref); if ( pFirstColumnRef && pSecondColumnRef )
bRet = *pFirstColumnRef == *pSecondColumnRef; elseif ( !pFirstColumnRef )
pFirstColumnRef = pSecondColumnRef;
}
} return bRet;
}
SqlParseError GetANDCriteria( OQueryDesignView* _pView,
OSelectionBrowseBox* _pSelectionBrw, const ::connectivity::OSQLParseNode * pCondition,
sal_uInt16& nLevel, bool bHaving, bool bAddOrOnOneLine)
{ const css::lang::Locale aLocale = _pView->getLocale(); const OUString sDecimal = _pView->getDecimalSeparator();
// I will need a cast pointer to my css::sdbcx::Container
OQueryController& rController = static_cast<OQueryController&>(_pView->getController());
SqlParseError eErrorCode = eOk;
// round brackets if (SQL_ISRULE(pCondition,boolean_primary))
{ // check if we have to put the or criteria on one line. const ::connectivity::OSQLParseNode* pSearchCondition = pCondition->getChild(1); bool bMustAddOrOnOneLine = CheckOrCriteria(pSearchCondition,nullptr); if ( SQL_ISRULE( pSearchCondition, search_condition) ) // we have a or
{
_pSelectionBrw->DuplicateConditionLevel( nLevel);
eErrorCode = GetORCriteria(_pView,_pSelectionBrw,pSearchCondition->getChild(0), nLevel,bHaving,bMustAddOrOnOneLine ); if ( eErrorCode == eOk )
{
++nLevel;
eErrorCode = GetORCriteria(_pView,_pSelectionBrw,pSearchCondition->getChild(2), nLevel,bHaving,bMustAddOrOnOneLine );
}
} else
eErrorCode = GetORCriteria(_pView,_pSelectionBrw,pSearchCondition, nLevel,bHaving,bMustAddOrOnOneLine );
} // The first element is (again) an AND condition elseif ( SQL_ISRULE(pCondition,boolean_term) )
{
OSL_ENSURE(pCondition->count() == 3,"Illegal definition of boolean_term");
eErrorCode = GetANDCriteria(_pView,_pSelectionBrw,pCondition->getChild(0), nLevel,bHaving,bAddOrOnOneLine ); if ( eErrorCode == eOk )
eErrorCode = GetANDCriteria(_pView,_pSelectionBrw,pCondition->getChild(2), nLevel,bHaving,bAddOrOnOneLine );
} elseif (SQL_ISRULE( pCondition, comparison_predicate))
{
eErrorCode = ComparisonPredicate(_pView,_pSelectionBrw,pCondition,nLevel,bHaving,bAddOrOnOneLine);
} elseif( SQL_ISRULE(pCondition,like_predicate) )
{ const ::connectivity::OSQLParseNode* pValueExp = pCondition->getChild(0); if (SQL_ISRULE(pValueExp, column_ref ) )
{
OUString aCondition;
Reference< XConnection> xConnection = rController.getConnection(); if ( xConnection.is() )
{
OUString aColumnName; // the international doesn't matter I have a string
pCondition->parseNodeToPredicateStr(aCondition,
xConnection,
rController.getNumberFormatter(),
aLocale,
sDecimal,
&rController.getParser().getContext());
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.