android: Update app icon to new startcenter icon
[LibreOffice.git] / dbaccess / source / ui / querydesign / QueryDesignView.cxx
blob95c8e82c267dfb340062e2a485dc7152ba88aa4f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <QueryDesignView.hxx>
21 #include <QueryTableView.hxx>
22 #include "QTableWindow.hxx"
23 #include <querycontroller.hxx>
24 #include <sqlbison.hxx>
25 #include <vcl/split.hxx>
26 #include <comphelper/diagnose_ex.hxx>
27 #include <o3tl/safeint.hxx>
28 #include <o3tl/string_view.hxx>
29 #include <osl/diagnose.h>
30 #include <vcl/svapp.hxx>
31 #include <vcl/weld.hxx>
32 #include <browserids.hxx>
33 #include "SelectionBrowseBox.hxx"
34 #include <strings.hrc>
35 #include <strings.hxx>
36 #include <comphelper/string.hxx>
37 #include <connectivity/dbtools.hxx>
38 #include <connectivity/dbexception.hxx>
39 #include <com/sun/star/sdbc/DataType.hpp>
40 #include <com/sun/star/container/XNameAccess.hpp>
41 #include <com/sun/star/sdbc/ColumnValue.hpp>
42 #include <connectivity/PColumn.hxx>
43 #include "QTableConnection.hxx"
44 #include <ConnectionLineData.hxx>
45 #include "QTableConnectionData.hxx"
46 #include <core_resource.hxx>
47 #include <UITools.hxx>
48 #include <querycontainerwindow.hxx>
49 #include <unotools/localedatawrapper.hxx>
50 #include <unotools/syslocale.hxx>
51 #include <memory>
52 #include <set>
53 #include <string_view>
55 using namespace ::dbaui;
56 using namespace ::utl;
57 using namespace ::connectivity;
58 using namespace ::dbtools;
59 using namespace ::com::sun::star::uno;
60 using namespace ::com::sun::star::lang;
61 using namespace ::com::sun::star::i18n;
62 using namespace ::com::sun::star::sdbc;
63 using namespace ::com::sun::star::beans;
64 using namespace ::com::sun::star::container;
66 // here we define our functions used in the anonymous namespace to get our header file smaller
67 // please look at the book LargeScale C++ to know why
68 namespace
70 const char C_AND[] = " AND ";
71 const char C_OR[] = " OR ";
73 bool InsertJoin( const OQueryDesignView* _pView,
74 const ::connectivity::OSQLParseNode *pNode);
76 SqlParseError InstallFields(OQueryDesignView* _pView,
77 const ::connectivity::OSQLParseNode* pNode,
78 OJoinTableView::OTableWindowMap* pTabList );
80 SqlParseError GetGroupCriteria( OQueryDesignView* _pView,
81 OSelectionBrowseBox* _pSelectionBrw,
82 const ::connectivity::OSQLParseNode* pSelectRoot );
84 SqlParseError GetHavingCriteria(OQueryDesignView* _pView,
85 OSelectionBrowseBox* _pSelectionBrw,
86 const ::connectivity::OSQLParseNode* pSelectRoot,
87 sal_uInt16& rLevel );
89 SqlParseError GetOrderCriteria( OQueryDesignView* _pView,
90 OSelectionBrowseBox* _pSelectionBrw,
91 const ::connectivity::OSQLParseNode* pParseRoot );
93 SqlParseError AddFunctionCondition(OQueryDesignView const * _pView,
94 OSelectionBrowseBox* _pSelectionBrw,
95 const ::connectivity::OSQLParseNode * pCondition,
96 const sal_uInt16 nLevel,
97 bool bHaving,
98 bool _bAddOrOnOneLine);
100 OUString quoteTableAlias(bool _bQuote, const OUString& _sAliasName, std::u16string_view _sQuote)
102 OUString sRet;
103 if ( _bQuote && !_sAliasName.isEmpty() )
105 sRet = ::dbtools::quoteName(_sQuote,_sAliasName) + ".";
107 return sRet;
109 OUString getTableRange(const OQueryDesignView* _pView,const ::connectivity::OSQLParseNode* _pTableRef)
111 Reference< XConnection> xConnection = static_cast<OQueryController&>(_pView->getController()).getConnection();
112 OUString sTableRange;
113 if ( _pTableRef )
115 sTableRange = ::connectivity::OSQLParseNode::getTableRange(_pTableRef);
116 if ( sTableRange.isEmpty() )
117 _pTableRef->parseNodeToStr(sTableRange,xConnection,nullptr,false,false);
119 return sTableRange;
121 void insertConnection(const OQueryDesignView* _pView,const EJoinType& _eJoinType, const OTableFieldDescRef& _aDragLeft, const OTableFieldDescRef& _aDragRight, bool _bNatural = false)
123 OQueryTableView* pTableView = static_cast<OQueryTableView*>(_pView->getTableView());
124 OQueryTableConnection* pConn = static_cast<OQueryTableConnection*>( pTableView->GetTabConn(static_cast<OTableWindow*>(_aDragLeft->GetTabWindow()),static_cast<OTableWindow*>(_aDragRight->GetTabWindow()),true));
126 if ( !pConn )
128 auto xInfoData = std::make_shared<OQueryTableConnectionData>();
129 xInfoData->InitFromDrag(_aDragLeft, _aDragRight);
130 xInfoData->SetJoinType(_eJoinType);
132 if ( _bNatural )
134 xInfoData->ResetConnLines();
135 xInfoData->setNatural(_bNatural);
138 Reference<XNameAccess> xReferencedTableColumns(xInfoData->getReferencedTable()->getColumns());
139 Sequence< OUString> aSeq = xInfoData->getReferencingTable()->getColumns()->getElementNames();
140 const OUString* pIter = aSeq.getConstArray();
141 const OUString* pEnd = pIter + aSeq.getLength();
142 for(;pIter != pEnd;++pIter)
144 if ( xReferencedTableColumns->hasByName(*pIter) )
145 xInfoData->AppendConnLine(*pIter,*pIter);
148 catch( const Exception& )
150 DBG_UNHANDLED_EXCEPTION("dbaccess");
154 ScopedVclPtrInstance< OQueryTableConnection > aInfo(pTableView, xInfoData);
155 // Because OQueryTableConnection never takes ownership of the data passed to it, but only remembers the pointer,
156 // this pointer to a local variable is not critical, as xInfoData and aInfo have the same lifetime
157 pTableView->NotifyTabConnection( *aInfo );
159 else
161 OUString aSourceFieldName(_aDragLeft->GetField());
162 OUString aDestFieldName(_aDragRight->GetField());
163 // the connection could point on the other side
164 if(pConn->GetSourceWin() == _aDragRight->GetTabWindow())
166 OUString aTmp(aSourceFieldName);
167 aSourceFieldName = aDestFieldName;
168 aDestFieldName = aTmp;
170 pConn->GetData()->AppendConnLine( aSourceFieldName,aDestFieldName);
171 pConn->UpdateLineList();
172 // Modified-Flag
173 // SetModified();
174 // and redraw
175 pConn->RecalcLines();
176 // for the following Invalidate, the new Connection must first be able
177 // to determine its BoundingRect
178 pConn->InvalidateConnection();
181 OUString ParseCondition( OQueryController& rController
182 ,const ::connectivity::OSQLParseNode* pCondition
183 ,const OUString& _sDecimal
184 ,const css::lang::Locale& _rLocale
185 ,sal_uInt32 _nStartIndex)
187 OUString aCondition;
188 Reference< XConnection> xConnection = rController.getConnection();
189 if ( xConnection.is() )
191 sal_uInt32 nCount = pCondition->count();
192 for(sal_uInt32 i = _nStartIndex ; i < nCount ; ++i)
193 pCondition->getChild(i)->parseNodeToPredicateStr(aCondition,
194 xConnection,
195 rController.getNumberFormatter(),
196 _rLocale,
197 _sDecimal,
198 &rController.getParser().getContext());
200 return aCondition;
202 SqlParseError FillOuterJoins(OQueryDesignView const * _pView,
203 const ::connectivity::OSQLParseNode* pTableRefList)
205 SqlParseError eErrorCode = eOk;
206 sal_uInt32 nCount = pTableRefList->count();
207 bool bError = false;
208 for (sal_uInt32 i=0; !bError && i < nCount; ++i)
210 const ::connectivity::OSQLParseNode* pParseNode = pTableRefList->getChild(i);
211 const ::connectivity::OSQLParseNode* pJoinNode = nullptr;
213 if ( SQL_ISRULE( pParseNode, qualified_join ) || SQL_ISRULE( pParseNode, joined_table ) || SQL_ISRULE( pParseNode, cross_union ) )
214 pJoinNode = pParseNode;
215 else if( SQL_ISRULE(pParseNode,table_ref)
216 && pParseNode->count() == 4 ) // '{' SQL_TOKEN_OJ joined_table '}'
217 pJoinNode = pParseNode->getChild(2);
219 if ( pJoinNode )
221 if ( !InsertJoin(_pView,pJoinNode) )
222 bError = true;
225 // check if error occurred
226 if ( bError )
227 eErrorCode = eIllegalJoin;
229 return eErrorCode;
232 /** FillDragInfo fills the field description out of the table
234 SqlParseError FillDragInfo( const OQueryDesignView* _pView,
235 const ::connectivity::OSQLParseNode* pColumnRef,
236 OTableFieldDescRef const & _rDragInfo)
238 SqlParseError eErrorCode = eOk;
240 bool bErg = false;
242 OUString aTableRange,aColumnName;
243 ::connectivity::OSQLParseTreeIterator& rParseIter = static_cast<OQueryController&>(_pView->getController()).getParseIterator();
244 rParseIter.getColumnRange( pColumnRef, aColumnName, aTableRange );
246 if ( !aTableRange.isEmpty() )
248 OQueryTableWindow* pSTW = static_cast<OQueryTableView*>(_pView->getTableView())->FindTable( aTableRange );
249 bErg = (pSTW && pSTW->ExistsField( aColumnName, _rDragInfo ) );
251 if ( !bErg )
253 sal_uInt16 nCntAccount;
254 bErg = static_cast<OQueryTableView*>(_pView->getTableView())->FindTableFromField(aColumnName, _rDragInfo, nCntAccount);
255 if ( !bErg )
256 bErg = _pView->HasFieldByAliasName(aColumnName, _rDragInfo);
258 if ( !bErg )
260 eErrorCode = eColumnNotFound;
261 OUString sError(DBA_RES(STR_QRY_COLUMN_NOT_FOUND));
262 sError = sError.replaceFirst("$name$",aColumnName);
263 _pView->getController().appendError( sError );
267 Reference<XDatabaseMetaData> xMeta = _pView->getController().getConnection()->getMetaData();
268 if ( xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers() )
269 _pView->getController().appendError(DBA_RES(STR_QRY_CHECK_CASESENSITIVE));
271 catch(Exception&)
276 return eErrorCode;
278 OUString BuildJoinCriteria( const Reference< XConnection>& _xConnection,
279 const OConnectionLineDataVec* pLineDataList,
280 const OQueryTableConnectionData* pData)
282 OUStringBuffer aCondition;
283 if ( _xConnection.is() )
287 const Reference< XDatabaseMetaData > xMetaData = _xConnection->getMetaData();
288 const OUString aQuote = xMetaData->getIdentifierQuoteString();
290 for (auto const& lineData : *pLineDataList)
292 if(!aCondition.isEmpty())
293 aCondition.append(C_AND);
294 aCondition.append(
295 quoteTableAlias(true,pData->GetAliasName(JTCS_FROM),aQuote)
296 + ::dbtools::quoteName(aQuote, lineData->GetFieldName(JTCS_FROM) )
297 + " = "
298 + quoteTableAlias(true,pData->GetAliasName(JTCS_TO),aQuote)
299 + ::dbtools::quoteName(aQuote, lineData->GetFieldName(JTCS_TO) ));
302 catch(SQLException&)
304 OSL_FAIL("Failure while building Join criteria!");
308 return aCondition.makeStringAndClear();
310 /** JoinCycle looks for a join cycle and append it to the string
311 @param _xConnection the connection
312 @param _pEntryConn the table connection which holds the data
313 @param _pEntryTabTo the corresponding table window
314 @param _rJoin the String which will contain the resulting string
316 void JoinCycle( const Reference< XConnection>& _xConnection,
317 OQueryTableConnection* _pEntryConn,
318 const OQueryTableWindow* _pEntryTabTo,
319 OUString& _rJoin )
321 OSL_ENSURE(_pEntryConn,"TableConnection can not be null!");
323 OQueryTableConnectionData* pData = static_cast< OQueryTableConnectionData*>(_pEntryConn->GetData().get());
324 if ( !(pData->GetJoinType() != INNER_JOIN && _pEntryTabTo->ExistsAVisitedConn()) )
325 return;
327 bool bBrace = false;
328 if(_rJoin.endsWith(")"))
330 bBrace = true;
331 _rJoin = _rJoin.replaceAt(_rJoin.getLength()-1,1, u" ");
333 _rJoin += C_AND + BuildJoinCriteria(_xConnection,&pData->GetConnLineDataList(),pData);
334 if(bBrace)
335 _rJoin += ")";
336 _pEntryConn->SetVisited(true);
338 OUString BuildTable( const Reference< XConnection>& _xConnection,
339 const OQueryTableWindow* pEntryTab,
340 bool _bForce = false
343 OUString aDBName(pEntryTab->GetComposedName());
345 if( _xConnection.is() )
349 Reference< XDatabaseMetaData > xMetaData = _xConnection->getMetaData();
351 OUString sCatalog, sSchema, sTable;
352 ::dbtools::qualifiedNameComponents( xMetaData, aDBName, sCatalog, sSchema, sTable, ::dbtools::EComposeRule::InDataManipulation );
353 OUString aTableListStr = ::dbtools::composeTableNameForSelect( _xConnection, sCatalog, sSchema, sTable );
355 OUString aQuote = xMetaData->getIdentifierQuoteString();
356 if ( _bForce || isAppendTableAliasEnabled( _xConnection ) || pEntryTab->GetAliasName() != aDBName )
358 aTableListStr += " ";
359 if ( generateAsBeforeTableAlias( _xConnection ) )
360 aTableListStr += "AS ";
361 aTableListStr += ::dbtools::quoteName( aQuote, pEntryTab->GetAliasName() );
363 aDBName = aTableListStr;
365 catch(const SQLException&)
367 DBG_UNHANDLED_EXCEPTION("dbaccess");
370 return aDBName;
372 OUString BuildJoin( const Reference< XConnection>& _xConnection,
373 const OUString& rLh,
374 std::u16string_view rRh,
375 const OQueryTableConnectionData* pData)
378 OUString aErg(rLh);
379 if ( pData->isNatural() && pData->GetJoinType() != CROSS_JOIN )
380 aErg += " NATURAL ";
381 switch(pData->GetJoinType())
383 case LEFT_JOIN:
384 aErg += " LEFT OUTER ";
385 break;
386 case RIGHT_JOIN:
387 aErg += " RIGHT OUTER ";
388 break;
389 case CROSS_JOIN:
390 OSL_ENSURE(!pData->isNatural(),"OQueryDesignView::BuildJoin: This should not happen!");
391 aErg += " CROSS ";
392 break;
393 case INNER_JOIN:
394 OSL_ENSURE(pData->isNatural(),"OQueryDesignView::BuildJoin: This should not happen!");
395 aErg += " INNER ";
396 break;
397 default:
398 aErg += " FULL OUTER ";
399 break;
401 aErg += OUString::Concat("JOIN ") + rRh;
402 if ( CROSS_JOIN != pData->GetJoinType() && !pData->isNatural() )
404 aErg += " ON " + BuildJoinCriteria(_xConnection,&pData->GetConnLineDataList(),pData);
407 return aErg;
409 OUString BuildJoin( const Reference< XConnection>& _xConnection,
410 const OQueryTableWindow* pLh,
411 const OQueryTableWindow* pRh,
412 const OQueryTableConnectionData* pData
415 bool bForce = pData->GetJoinType() == CROSS_JOIN || pData->isNatural();
416 return BuildJoin(_xConnection,BuildTable(_xConnection,pLh,bForce),BuildTable(_xConnection,pRh,bForce),pData);
418 OUString BuildJoin( const Reference< XConnection>& _xConnection,
419 const OUString &rLh,
420 const OQueryTableWindow* pRh,
421 const OQueryTableConnectionData* pData
424 return BuildJoin(_xConnection,rLh,BuildTable(_xConnection,pRh),pData);
426 OUString BuildJoin( const Reference< XConnection>& _xConnection,
427 const OQueryTableWindow* pLh,
428 const OUString &rRh,
429 const OQueryTableConnectionData* pData
432 // strict ANSI SQL:
433 // - does not support any bracketing of JOINS
434 // - supports nested joins only in the LEFT HAND SIDE
435 // In this case, we are trying to build a join with a nested join
436 // in the right hand side.
437 // So switch the direction of the join and both hand sides.
438 OQueryTableConnectionData data(*pData);
439 switch (data.GetJoinType())
441 case LEFT_JOIN:
442 data.SetJoinType(RIGHT_JOIN);
443 break;
444 case RIGHT_JOIN:
445 data.SetJoinType(LEFT_JOIN);
446 break;
447 default:
448 // the other join types are symmetric, so nothing to change
449 break;
451 return BuildJoin(_xConnection, rRh, BuildTable(_xConnection,pLh), &data);
453 void addConnectionTableNames( const Reference< XConnection>& _xConnection,
454 const OQueryTableConnection* const pEntryConn,
455 std::set<OUString> &_rTableNames )
457 // insert tables into table list to avoid double entries
458 const OQueryTableWindow* const pEntryTabFrom = static_cast<OQueryTableWindow*>(pEntryConn->GetSourceWin());
459 const OQueryTableWindow* const pEntryTabTo = static_cast<OQueryTableWindow*>(pEntryConn->GetDestWin());
460 _rTableNames.insert(BuildTable(_xConnection,pEntryTabFrom));
461 _rTableNames.insert(BuildTable(_xConnection,pEntryTabTo));
463 void GetNextJoin( const Reference< XConnection>& _xConnection,
464 OQueryTableConnection* pEntryConn,
465 OQueryTableWindow const * pEntryTabTo,
466 OUString &aJoin,
467 std::set<OUString> &_rTableNames)
469 OQueryTableConnectionData* pEntryConnData = static_cast<OQueryTableConnectionData*>(pEntryConn->GetData().get());
470 if ( pEntryConnData->GetJoinType() == INNER_JOIN && !pEntryConnData->isNatural() )
471 return;
473 if(aJoin.isEmpty())
475 addConnectionTableNames(_xConnection, pEntryConn, _rTableNames);
476 OQueryTableWindow* pEntryTabFrom = static_cast<OQueryTableWindow*>(pEntryConn->GetSourceWin());
477 aJoin = BuildJoin(_xConnection,pEntryTabFrom,pEntryTabTo,pEntryConnData);
479 else if(pEntryTabTo == pEntryConn->GetDestWin())
481 addConnectionTableNames(_xConnection, pEntryConn, _rTableNames);
482 aJoin = BuildJoin(_xConnection,aJoin,pEntryTabTo,pEntryConnData);
484 else if(pEntryTabTo == pEntryConn->GetSourceWin())
486 addConnectionTableNames(_xConnection, pEntryConn, _rTableNames);
487 aJoin = BuildJoin(_xConnection,pEntryTabTo,aJoin,pEntryConnData);
490 pEntryConn->SetVisited(true);
492 // first search for the "to" window
493 const auto& rConnections = pEntryConn->GetParent()->getTableConnections();
494 bool bFound = false;
495 for (auto const& connection : rConnections)
497 OQueryTableConnection* pNext = static_cast<OQueryTableConnection*>(connection.get());
498 if(!pNext->IsVisited() && (pNext->GetSourceWin() == pEntryTabTo || pNext->GetDestWin() == pEntryTabTo))
500 OQueryTableWindow* pEntryTab = pNext->GetSourceWin() == pEntryTabTo ? static_cast<OQueryTableWindow*>(pNext->GetDestWin()) : static_cast<OQueryTableWindow*>(pNext->GetSourceWin());
501 // exists there a connection to a OQueryTableWindow that holds a connection that has been already visited
502 JoinCycle(_xConnection,pNext,pEntryTab,aJoin);
503 if(!pNext->IsVisited())
504 GetNextJoin(_xConnection, pNext, pEntryTab, aJoin, _rTableNames);
505 bFound = true;
509 // when nothing found look for the "from" window
510 if(bFound)
511 return;
513 OQueryTableWindow* pEntryTabFrom = static_cast<OQueryTableWindow*>(pEntryConn->GetSourceWin());
514 for (auto const& connection : rConnections)
516 OQueryTableConnection* pNext = static_cast<OQueryTableConnection*>(connection.get());
517 if(!pNext->IsVisited() && (pNext->GetSourceWin() == pEntryTabFrom || pNext->GetDestWin() == pEntryTabFrom))
519 OQueryTableWindow* pEntryTab = pNext->GetSourceWin() == pEntryTabFrom ? static_cast<OQueryTableWindow*>(pNext->GetDestWin()) : static_cast<OQueryTableWindow*>(pNext->GetSourceWin());
520 // exists there a connection to a OQueryTableWindow that holds a connection that has been already visited
521 JoinCycle(_xConnection,pNext,pEntryTab,aJoin);
522 if(!pNext->IsVisited())
523 GetNextJoin(_xConnection, pNext, pEntryTab, aJoin, _rTableNames);
527 SqlParseError InsertJoinConnection( const OQueryDesignView* _pView,
528 const ::connectivity::OSQLParseNode *pNode,
529 const EJoinType& _eJoinType,
530 const ::connectivity::OSQLParseNode *pLeftTable,
531 const ::connectivity::OSQLParseNode *pRightTable)
533 SqlParseError eErrorCode = eOk;
534 if (pNode->count() == 3 && // statement between brackets
535 SQL_ISPUNCTUATION(pNode->getChild(0),"(") &&
536 SQL_ISPUNCTUATION(pNode->getChild(2),")"))
538 eErrorCode = InsertJoinConnection(_pView,pNode->getChild(1), _eJoinType,pLeftTable,pRightTable);
540 else if (SQL_ISRULEOR2(pNode,search_condition,boolean_term) && // AND/OR-joints:
541 pNode->count() == 3)
543 // only allow AND joints
544 if (!SQL_ISTOKEN(pNode->getChild(1),AND))
545 eErrorCode = eIllegalJoinCondition;
546 else if ( eOk == (eErrorCode = InsertJoinConnection(_pView,pNode->getChild(0), _eJoinType,pLeftTable,pRightTable)) )
547 eErrorCode = InsertJoinConnection(_pView,pNode->getChild(2), _eJoinType,pLeftTable,pRightTable);
549 else if (SQL_ISRULE(pNode,comparison_predicate))
551 // only the comparison of columns is allowed
552 OSL_ENSURE(pNode->count() == 3,"OQueryDesignView::InsertJoinConnection: Error in Parse Tree");
553 if (!(SQL_ISRULE(pNode->getChild(0),column_ref) &&
554 SQL_ISRULE(pNode->getChild(2),column_ref) &&
555 pNode->getChild(1)->getNodeType() == SQLNodeType::Equal))
557 OUString sError(DBA_RES(STR_QRY_JOIN_COLUMN_COMPARE));
558 _pView->getController().appendError( sError );
559 return eIllegalJoin;
562 OTableFieldDescRef aDragLeft = new OTableFieldDesc();
563 OTableFieldDescRef aDragRight = new OTableFieldDesc();
564 eErrorCode = FillDragInfo(_pView,pNode->getChild(0),aDragLeft);
565 if ( eOk != eErrorCode )
566 return eErrorCode;
567 eErrorCode = FillDragInfo(_pView,pNode->getChild(2),aDragRight);
568 if ( eOk != eErrorCode )
569 return eErrorCode;
571 if ( pLeftTable )
573 OQueryTableWindow* pLeftWindow = static_cast<OQueryTableView*>(_pView->getTableView())->FindTable( getTableRange(_pView,pLeftTable->getByRule(OSQLParseNode::table_ref) ));
574 if ( pLeftWindow == aDragLeft->GetTabWindow() )
575 insertConnection(_pView,_eJoinType,aDragLeft,aDragRight);
576 else
577 insertConnection(_pView,_eJoinType,aDragRight,aDragLeft);
579 else
580 insertConnection(_pView,_eJoinType,aDragLeft,aDragRight);
582 else
583 eErrorCode = eIllegalJoin;
584 return eErrorCode;
586 bool GetInnerJoinCriteria( const OQueryDesignView* _pView,
587 const ::connectivity::OSQLParseNode *pCondition)
589 return InsertJoinConnection(_pView,pCondition, INNER_JOIN,nullptr,nullptr) != eOk;
591 OUString GenerateSelectList( const OQueryDesignView* _pView,
592 OTableFields& _rFieldList,
593 bool bAlias)
595 Reference< XConnection> xConnection = static_cast<OQueryController&>(_pView->getController()).getConnection();
596 if ( !xConnection.is() )
597 return OUString();
599 OUStringBuffer aTmpStr,aFieldListStr;
601 bool bAsterisk = false;
602 int nVis = 0;
603 for (auto const& field : _rFieldList)
605 if ( field->IsVisible() )
607 if ( field->GetField().toChar() == '*' )
608 bAsterisk = true;
609 ++nVis;
612 if(nVis == 1)
613 bAsterisk = false;
617 const Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData();
618 const OUString aQuote = xMetaData->getIdentifierQuoteString();
620 OJoinTableView::OTableWindowMap& rTabList = _pView->getTableView()->GetTabWinMap();
622 for (auto const& field : _rFieldList)
624 OUString rFieldName = field->GetField();
625 if ( !rFieldName.isEmpty() && field->IsVisible() )
627 aTmpStr = "";
628 const OUString rAlias = field->GetAlias();
629 const OUString rFieldAlias = field->GetFieldAlias();
631 aTmpStr.append(quoteTableAlias((bAlias || bAsterisk),rAlias,aQuote));
633 // if we have a none numeric field, the table alias could be in the name
634 // otherwise we are not allowed to do this (e.g. 0.1 * PRICE )
635 if ( !field->isOtherFunction() )
637 // we have to look if we have alias.* here but before we have to check if the column doesn't already exist
638 OTableFieldDescRef aInfo = new OTableFieldDesc();
639 for (auto const& table : rTabList)
641 OQueryTableWindow* pTabWin = static_cast<OQueryTableWindow*>(table.second.get());
643 if ( pTabWin->ExistsField( rFieldName, aInfo ) )
645 rFieldName = aInfo->GetField();
646 break;
649 if ( ( rFieldName.toChar() != '*' ) && ( rFieldName.indexOf( aQuote ) == -1 ) )
651 OSL_ENSURE(!field->GetTable().isEmpty(),"No table field name!");
652 aTmpStr.append(::dbtools::quoteName(aQuote, rFieldName));
654 else
655 aTmpStr.append(rFieldName);
657 else
658 aTmpStr.append(rFieldName);
660 if ( field->isAggregateFunction() )
662 OSL_ENSURE(!field->GetFunction().isEmpty(),"Function name must not be empty! ;-(");
663 OUStringBuffer aTmpStr2( field->GetFunction() + "(" + aTmpStr + ")");
664 aTmpStr = aTmpStr2;
667 if (!rFieldAlias.isEmpty() &&
668 (rFieldName.toChar() != '*' ||
669 field->isNumericOrAggregateFunction() ||
670 field->isOtherFunction()))
672 aTmpStr.append(" AS " + ::dbtools::quoteName(aQuote, rFieldAlias));
674 aFieldListStr.append(aTmpStr);
675 aTmpStr.setLength(0);
676 aFieldListStr.append(", ");
679 if(!aFieldListStr.isEmpty())
680 aFieldListStr.setLength(aFieldListStr.getLength()-2);
682 catch(SQLException&)
684 OSL_FAIL("Failure while building select list!");
686 return aFieldListStr.makeStringAndClear();
688 bool GenerateCriterias( OQueryDesignView const * _pView,
689 OUStringBuffer& rRetStr,
690 OUStringBuffer& rHavingStr,
691 OTableFields& _rFieldList,
692 bool bMulti )
694 Reference< XConnection> xConnection = static_cast<OQueryController&>(_pView->getController()).getConnection();
695 if(!xConnection.is())
696 return false;
698 OUString aFieldName,aCriteria,aWhereStr,aHavingStr,aWork/*,aOrderStr*/;
699 // print line by line joined with AND
700 sal_uInt16 nMaxCriteria = 0;
701 for (auto const& field : _rFieldList)
703 nMaxCriteria = std::max<sal_uInt16>(nMaxCriteria,static_cast<sal_uInt16>(field->GetCriteria().size()));
707 const Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData();
708 const OUString aQuote = xMetaData->getIdentifierQuoteString();
709 const IParseContext& rContext = static_cast<OQueryController&>(_pView->getController()).getParser().getContext();
710 // * must not contain a filter : have I already shown the correct warning ?
711 bool bCritsOnAsteriskWarning = false; // ** TMFS **
713 for (sal_uInt16 i=0 ; i < nMaxCriteria ; i++)
715 aHavingStr.clear();
716 aWhereStr.clear();
718 for (auto const& field : _rFieldList)
720 aFieldName = field->GetField();
722 if (aFieldName.isEmpty())
723 continue;
724 aCriteria = field->GetCriteria( i );
725 if ( !aCriteria.isEmpty() )
727 // * is not allowed to contain any filter, only when used in combination an aggregate function
728 if ( aFieldName.toChar() == '*' && field->isNoneFunction() )
730 // only show the messagebox the first time
731 if (!bCritsOnAsteriskWarning)
733 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(_pView->GetFrameWeld(),
734 VclMessageType::Warning, VclButtonsType::Ok,
735 DBA_RES(STR_QRY_CRITERIA_ON_ASTERISK)));
736 xBox->run();
738 bCritsOnAsteriskWarning = true;
739 continue;
741 aWork = quoteTableAlias(bMulti,field->GetAlias(),aQuote);
743 if ( (field->GetFunctionType() & (FKT_OTHER|FKT_NUMERIC)) || (aFieldName.toChar() == '*') )
744 aWork += aFieldName;
745 else
746 aWork += ::dbtools::quoteName(aQuote, aFieldName);
748 if ( field->isAggregateFunction() || field->IsGroupBy() )
750 if (aHavingStr.isEmpty()) // no more criteria
751 aHavingStr += "("; // bracket
752 else
753 aHavingStr += C_AND;
755 if ( field->isAggregateFunction() )
757 OSL_ENSURE(!field->GetFunction().isEmpty(),"No function name for aggregate given!");
758 aHavingStr += field->GetFunction() + "(" + aWork + ")"; // bracket
760 else
761 aHavingStr += aWork;
763 OUString aErrorMsg;
764 Reference<XPropertySet> xColumn;
765 std::unique_ptr< ::connectivity::OSQLParseNode> pParseNode(_pView->getPredicateTreeFromEntry(field,aCriteria,aErrorMsg,xColumn));
766 if (pParseNode)
768 if (bMulti && !(field->isOtherFunction() || (aFieldName.toChar() == '*')))
769 pParseNode->replaceNodeValue(field->GetAlias(),aFieldName);
770 OUString sHavingStr = aHavingStr;
772 sal_uInt32 nCount = pParseNode->count();
773 for( sal_uInt32 node = 1 ; node < nCount ; ++node)
774 pParseNode->getChild(node)->parseNodeToStr( sHavingStr,
775 xConnection,
776 &rContext,
777 false,
778 !field->isOtherFunction());
779 aHavingStr = sHavingStr;
781 else
782 aHavingStr += aCriteria;
784 else
786 if ( aWhereStr.isEmpty() ) // no more criteria
787 aWhereStr += "("; // bracket
788 else
789 aWhereStr += C_AND;
791 aWhereStr += " ";
792 // aCriteria could have some German numbers so I have to be sure here
793 OUString aErrorMsg;
794 Reference<XPropertySet> xColumn;
795 std::unique_ptr< ::connectivity::OSQLParseNode> pParseNode( _pView->getPredicateTreeFromEntry(field,aCriteria,aErrorMsg,xColumn));
796 if (pParseNode)
798 if (bMulti && !(field->isOtherFunction() || (aFieldName.toChar() == '*')))
799 pParseNode->replaceNodeValue(field->GetAlias(),aFieldName);
800 OUString aWhere = aWhereStr;
801 pParseNode->parseNodeToStr( aWhere,
802 xConnection,
803 &rContext,
804 false,
805 !field->isOtherFunction() );
806 aWhereStr = aWhere;
808 else
810 aWhereStr += aWork + "=" + aCriteria;
814 // only once for each field
815 else if ( !i && field->isCondition() )
817 if (aWhereStr.isEmpty()) // no more criteria
818 aWhereStr += "("; // bracket
819 else
820 aWhereStr += C_AND;
821 aWhereStr += field->GetField();
824 if (!aWhereStr.isEmpty())
826 aWhereStr += ")"; // close bracket for the AND branch
827 if (!rRetStr.isEmpty()) // are there conditions on the field?
828 rRetStr.append(C_OR);
829 else // open bracket for the OR branch
830 rRetStr.append('(');
831 rRetStr.append(aWhereStr);
833 if (!aHavingStr.isEmpty())
835 aHavingStr += ")"; // close bracket for the AND branch
836 if (!rHavingStr.isEmpty()) // are there conditions on the field?
837 rHavingStr.append(C_OR);
838 else // Open bracket for the OR branch
839 rHavingStr.append('(');
840 rHavingStr.append(aHavingStr);
844 if (!rRetStr.isEmpty())
845 rRetStr.append(')'); // close bracket for the OR branch
846 if (!rHavingStr.isEmpty())
847 rHavingStr.append(')'); // close bracket for the OR branch
849 catch(SQLException&)
851 OSL_FAIL("Failure while building where clause!");
853 return true;
855 SqlParseError GenerateOrder( OQueryDesignView const * _pView,
856 OTableFields& _rFieldList,
857 bool bMulti,
858 OUString& _rsRet)
860 const OQueryController& rController = static_cast<OQueryController&>(_pView->getController());
861 const Reference< XConnection>& xConnection = rController.getConnection();
862 if ( !xConnection.is() )
863 return eNoConnection;
865 SqlParseError eErrorCode = eOk;
867 OUString aColumnName;
868 OUString aWorkStr;
871 const bool bColumnAliasInOrderBy = rController.getSdbMetaData().supportsColumnAliasInOrderBy();
872 Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData();
873 OUString aQuote = xMetaData->getIdentifierQuoteString();
874 // * must not contain filter - have I already shown the warning?
875 bool bCritsOnAsteriskWarning = false; // ** TMFS **
876 for (auto const& field : _rFieldList)
878 EOrderDir eOrder = field->GetOrderDir();
879 // only create a sort expression when the table name and the sort criteria are defined
880 // otherwise they will be built in GenerateCriteria
881 if ( eOrder != ORDER_NONE )
883 aColumnName = field->GetField();
884 if(aColumnName.toChar() == '*')
886 // only show the MessageBox the first time
887 if (!bCritsOnAsteriskWarning)
889 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(_pView->GetFrameWeld(),
890 VclMessageType::Warning, VclButtonsType::Ok,
891 DBA_RES(STR_QRY_ORDERBY_ON_ASTERISK)));
892 xBox->run();
894 bCritsOnAsteriskWarning = true;
895 continue;
898 if ( bColumnAliasInOrderBy && !field->GetFieldAlias().isEmpty() )
900 aWorkStr += ::dbtools::quoteName(aQuote, field->GetFieldAlias());
902 else if ( field->isNumericOrAggregateFunction() )
904 OSL_ENSURE(!field->GetFunction().isEmpty(),"Function name cannot be empty! ;-(");
905 aWorkStr += field->GetFunction() + "("
906 + quoteTableAlias(
907 bMulti, field->GetAlias(), aQuote);
908 // only quote column name when we don't have a numeric
909 if ( field->isNumeric() )
910 aWorkStr += aColumnName;
911 else
912 aWorkStr += ::dbtools::quoteName(aQuote, aColumnName);
914 aWorkStr += ")";
916 else if ( field->isOtherFunction() )
918 aWorkStr += aColumnName;
920 else
922 aWorkStr += quoteTableAlias(bMulti,field->GetAlias(),aQuote) + ::dbtools::quoteName(aQuote, aColumnName);
924 aWorkStr += OUString::Concat(" ") + o3tl::getToken( u";ASC;DESC", static_cast<sal_uInt16>(eOrder), ';' ) + ",";
929 OUString sTemp(comphelper::string::stripEnd(aWorkStr, ','));
930 aWorkStr = sTemp;
933 if ( !aWorkStr.isEmpty() )
935 const sal_Int32 nMaxOrder = xMetaData->getMaxColumnsInOrderBy();
936 if ( nMaxOrder && nMaxOrder < comphelper::string::getTokenCount(aWorkStr, ',') )
937 eErrorCode = eStatementTooLong;
938 else
940 _rsRet = " ORDER BY " + aWorkStr;
944 catch(SQLException&)
946 OSL_FAIL("Failure while building group by!");
949 return eErrorCode;
952 void GenerateInnerJoinCriterias(const Reference< XConnection>& _xConnection,
953 OUString& _rJoinCrit,
954 const std::vector<VclPtr<OTableConnection> >& _rConnList)
956 for (auto const& connection : _rConnList)
958 const OQueryTableConnection* pEntryConn = static_cast<const OQueryTableConnection*>(connection.get());
959 OQueryTableConnectionData* pEntryConnData = static_cast<OQueryTableConnectionData*>(pEntryConn->GetData().get());
960 if ( pEntryConnData->GetJoinType() == INNER_JOIN && !pEntryConnData->isNatural() )
962 if(!_rJoinCrit.isEmpty())
963 _rJoinCrit += C_AND;
964 _rJoinCrit += BuildJoinCriteria(_xConnection,&pEntryConnData->GetConnLineDataList(),pEntryConnData);
968 void searchAndAppendName(const Reference< XConnection>& _xConnection,
969 const OQueryTableWindow* _pTableWindow,
970 std::set<OUString>& _rTableNames,
971 OUString& _rsTableListStr
974 OUString sTabName(BuildTable(_xConnection,_pTableWindow));
976 if(_rTableNames.insert(sTabName).second)
978 _rsTableListStr += sTabName + ",";
981 OUString GenerateFromClause( const Reference< XConnection>& _xConnection,
982 const OQueryTableView::OTableWindowMap* pTabList,
983 const std::vector<VclPtr<OTableConnection> >& rConnList
987 OUString aTableListStr;
988 // used to avoid putting a table twice in FROM clause
989 std::set<OUString> aTableNames;
991 // generate outer join clause in from
992 if(!rConnList.empty())
994 std::map<OTableWindow*,sal_Int32> aConnectionCount;
995 auto aEnd = rConnList.end();
996 for (auto const& connection : rConnList)
998 static_cast<OQueryTableConnection*>(connection.get())->SetVisited(false);
999 ++aConnectionCount[connection->GetSourceWin()];
1000 ++aConnectionCount[connection->GetDestWin()];
1002 std::multimap<sal_Int32 , OTableWindow*> aMulti;
1003 for (auto const& elem : aConnectionCount)
1005 aMulti.emplace(elem.second,elem.first);
1008 const bool bUseEscape = ::dbtools::getBooleanDataSourceSetting( _xConnection, PROPERTY_OUTERJOINESCAPE );
1009 std::multimap<sal_Int32 , OTableWindow*>::const_reverse_iterator aRIter = aMulti.rbegin();
1010 std::multimap<sal_Int32 , OTableWindow*>::const_reverse_iterator aREnd = aMulti.rend();
1011 for(;aRIter != aREnd;++aRIter)
1013 auto aConIter = aRIter->second->getTableView()->getTableConnections(aRIter->second);
1014 for(;aConIter != aEnd;++aConIter)
1016 OQueryTableConnection* pEntryConn = static_cast<OQueryTableConnection*>((*aConIter).get());
1017 if(!pEntryConn->IsVisited() && pEntryConn->GetSourceWin() == aRIter->second )
1019 OUString aJoin;
1020 GetNextJoin(_xConnection,
1021 pEntryConn,
1022 static_cast<OQueryTableWindow*>(pEntryConn->GetDestWin()),
1023 aJoin,
1024 aTableNames);
1026 if(!aJoin.isEmpty())
1028 OUString aStr;
1029 switch(static_cast<OQueryTableConnectionData*>(pEntryConn->GetData().get())->GetJoinType())
1031 case LEFT_JOIN:
1032 case RIGHT_JOIN:
1033 case FULL_JOIN:
1035 // create outer join
1036 if ( bUseEscape )
1037 aStr += "{ oj ";
1038 aStr += aJoin;
1039 if ( bUseEscape )
1040 aStr += " }";
1042 break;
1043 default:
1044 aStr += aJoin;
1045 break;
1047 aStr += ",";
1048 aTableListStr += aStr;
1054 // and now all inner joins
1055 // these are implemented as
1056 // "FROM tbl1, tbl2 WHERE tbl1.col1=tlb2.col2"
1057 // rather than
1058 // "FROM tbl1 INNER JOIN tbl2 ON tbl1.col1=tlb2.col2"
1059 for (auto const& connection : rConnList)
1061 OQueryTableConnection* pEntryConn = static_cast<OQueryTableConnection*>(connection.get());
1062 if(!pEntryConn->IsVisited())
1064 searchAndAppendName(_xConnection,
1065 static_cast<OQueryTableWindow*>(pEntryConn->GetSourceWin()),
1066 aTableNames,
1067 aTableListStr);
1069 searchAndAppendName(_xConnection,
1070 static_cast<OQueryTableWindow*>(pEntryConn->GetDestWin()),
1071 aTableNames,
1072 aTableListStr);
1076 // all tables that haven't a connection to anyone
1077 for (auto const& table : *pTabList)
1079 const OQueryTableWindow* pEntryTab = static_cast<const OQueryTableWindow*>(table.second.get());
1080 if(!pEntryTab->ExistsAConn())
1082 aTableListStr += BuildTable(_xConnection,pEntryTab) + ",";
1086 if(!aTableListStr.isEmpty())
1087 aTableListStr = aTableListStr.replaceAt(aTableListStr.getLength()-1,1, u"" );
1088 return aTableListStr;
1090 OUString GenerateGroupBy(const OQueryDesignView* _pView,OTableFields& _rFieldList, bool bMulti )
1092 OQueryController& rController = static_cast<OQueryController&>(_pView->getController());
1093 const Reference< XConnection> xConnection = rController.getConnection();
1094 if(!xConnection.is())
1095 return OUString();
1097 std::map< OUString,bool> aGroupByNames;
1099 OUString aGroupByStr;
1102 const Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData();
1103 const OUString aQuote = xMetaData->getIdentifierQuoteString();
1105 for (auto const& field : _rFieldList)
1107 if ( field->IsGroupBy() )
1109 OSL_ENSURE(!field->GetField().isEmpty(),"No Field Name available!;-(");
1110 OUString sGroupByPart = quoteTableAlias(bMulti,field->GetAlias(),aQuote);
1112 // only quote the field name when it isn't calculated
1113 if ( field->isNoneFunction() )
1115 sGroupByPart += ::dbtools::quoteName(aQuote, field->GetField());
1117 else
1119 OUString aTmp = field->GetField();
1120 OUString aErrorMsg;
1121 Reference<XPropertySet> xColumn;
1122 std::unique_ptr< ::connectivity::OSQLParseNode> pParseNode(_pView->getPredicateTreeFromEntry(field,aTmp,aErrorMsg,xColumn));
1123 if (pParseNode)
1125 OUString sGroupBy;
1126 pParseNode->getChild(0)->parseNodeToStr( sGroupBy,
1127 xConnection,
1128 &rController.getParser().getContext(),
1129 false,
1130 !field->isOtherFunction());
1131 sGroupByPart += sGroupBy;
1133 else
1134 sGroupByPart += field->GetField();
1136 if ( aGroupByNames.find(sGroupByPart) == aGroupByNames.end() )
1138 aGroupByNames.emplace(sGroupByPart,true);
1139 aGroupByStr += sGroupByPart + ",";
1143 if ( !aGroupByStr.isEmpty() )
1145 aGroupByStr = aGroupByStr.replaceAt(aGroupByStr.getLength()-1,1, u" " );
1146 OUString aGroupByStr2 = " GROUP BY " + aGroupByStr;
1147 aGroupByStr = aGroupByStr2;
1150 catch(SQLException&)
1152 OSL_FAIL("Failure while building group by!");
1154 return aGroupByStr;
1156 SqlParseError GetORCriteria(OQueryDesignView* _pView,
1157 OSelectionBrowseBox* _pSelectionBrw,
1158 const ::connectivity::OSQLParseNode * pCondition,
1159 sal_uInt16& nLevel ,
1160 bool bHaving = false,
1161 bool bAddOrOnOneLine = false);
1162 SqlParseError GetSelectionCriteria( OQueryDesignView* _pView,
1163 OSelectionBrowseBox* _pSelectionBrw,
1164 const ::connectivity::OSQLParseNode* pNode,
1165 sal_uInt16& rLevel )
1167 if (!pNode || !SQL_ISRULE(pNode, select_statement))
1168 return eNoSelectStatement;
1170 // nyi: more checking for the correct structure!
1171 pNode = pNode->getChild(3)->getChild(1);
1172 // no where clause found
1173 if (!pNode || pNode->isLeaf())
1174 return eOk;
1176 // Next free sentence...
1177 SqlParseError eErrorCode = eOk;
1178 ::connectivity::OSQLParseNode * pCondition = pNode->getChild(1);
1179 if ( pCondition ) // no where clause
1181 // now we have to check the other conditions
1182 // first make the logical easier
1183 ::connectivity::OSQLParseNode::negateSearchCondition(pCondition);
1184 ::connectivity::OSQLParseNode *pNodeTmp = pNode->getChild(1);
1186 ::connectivity::OSQLParseNode::disjunctiveNormalForm(pNodeTmp);
1187 pNodeTmp = pNode->getChild(1);
1188 ::connectivity::OSQLParseNode::absorptions(pNodeTmp);
1189 pNodeTmp = pNode->getChild(1);
1190 // compress sort the criteria @see https://bz.apache.org/ooo/show_bug.cgi?id=24079
1191 OSQLParseNode::compress(pNodeTmp);
1192 pNodeTmp = pNode->getChild(1);
1194 // first extract the inner joins conditions
1195 GetInnerJoinCriteria(_pView,pNodeTmp);
1196 // now simplify again, join are checked in ComparisonPredicate
1197 ::connectivity::OSQLParseNode::absorptions(pNodeTmp);
1198 pNodeTmp = pNode->getChild(1);
1200 // it could happen that pCondition is not more valid
1201 eErrorCode = GetORCriteria(_pView,_pSelectionBrw,pNodeTmp, rLevel);
1203 return eErrorCode;
1205 SqlParseError GetANDCriteria( OQueryDesignView* _pView,
1206 OSelectionBrowseBox* _pSelectionBrw,
1207 const ::connectivity::OSQLParseNode * pCondition,
1208 sal_uInt16& nLevel,
1209 bool bHaving,
1210 bool bAddOrOnOneLine);
1211 SqlParseError ComparisonPredicate(OQueryDesignView const * _pView,
1212 OSelectionBrowseBox* _pSelectionBrw,
1213 const ::connectivity::OSQLParseNode * pCondition,
1214 const sal_uInt16 nLevel,
1215 bool bHaving,
1216 bool bAddOrOnOneLine);
1217 SqlParseError GetORCriteria(OQueryDesignView* _pView,
1218 OSelectionBrowseBox* _pSelectionBrw,
1219 const ::connectivity::OSQLParseNode * pCondition,
1220 sal_uInt16& nLevel ,
1221 bool bHaving,
1222 bool bAddOrOnOneLine)
1224 SqlParseError eErrorCode = eOk;
1226 // round brackets around the printout
1227 if (pCondition->count() == 3 &&
1228 SQL_ISPUNCTUATION(pCondition->getChild(0),"(") &&
1229 SQL_ISPUNCTUATION(pCondition->getChild(2),")"))
1231 eErrorCode = GetORCriteria(_pView,_pSelectionBrw,pCondition->getChild(1),nLevel,bHaving,bAddOrOnOneLine);
1233 // OR condition
1234 // a searchcondition can only look like this: search_condition SQL_TOKEN_OR boolean_term
1235 else if (SQL_ISRULE(pCondition,search_condition))
1237 for (int i = 0; i < 3 && eErrorCode == eOk ; i+=2)
1239 const ::connectivity::OSQLParseNode* pChild = pCondition->getChild(i);
1240 if ( SQL_ISRULE(pChild,search_condition) )
1241 eErrorCode = GetORCriteria(_pView,_pSelectionBrw,pChild,nLevel,bHaving,bAddOrOnOneLine);
1242 else
1244 eErrorCode = GetANDCriteria(_pView,_pSelectionBrw,pChild, nLevel,bHaving, i != 0 && bAddOrOnOneLine);
1245 if ( !bAddOrOnOneLine)
1246 nLevel++;
1250 else
1251 eErrorCode = GetANDCriteria( _pView,_pSelectionBrw,pCondition, nLevel, bHaving,bAddOrOnOneLine );
1253 return eErrorCode;
1255 bool CheckOrCriteria(const ::connectivity::OSQLParseNode* _pCondition,::connectivity::OSQLParseNode* _pFirstColumnRef)
1257 bool bRet = true;
1258 ::connectivity::OSQLParseNode* pFirstColumnRef = _pFirstColumnRef;
1259 for (size_t i = 0; bRet && i < _pCondition->count(); ++i)
1261 const ::connectivity::OSQLParseNode* pChild = _pCondition->getChild(i);
1262 if ( pChild->isToken() )
1263 continue;
1264 else if ( SQL_ISRULE(pChild,search_condition) )
1265 bRet = CheckOrCriteria(pChild,pFirstColumnRef);
1266 else
1268 // this is a simple way to test columns are the same, may be we have to adjust this algo a little bit in future. :-)
1269 ::connectivity::OSQLParseNode* pSecondColumnRef = pChild->getByRule(::connectivity::OSQLParseNode::column_ref);
1270 if ( pFirstColumnRef && pSecondColumnRef )
1271 bRet = *pFirstColumnRef == *pSecondColumnRef;
1272 else if ( !pFirstColumnRef )
1273 pFirstColumnRef = pSecondColumnRef;
1276 return bRet;
1278 SqlParseError GetANDCriteria( OQueryDesignView* _pView,
1279 OSelectionBrowseBox* _pSelectionBrw,
1280 const ::connectivity::OSQLParseNode * pCondition,
1281 sal_uInt16& nLevel,
1282 bool bHaving,
1283 bool bAddOrOnOneLine)
1285 const css::lang::Locale aLocale = _pView->getLocale();
1286 const OUString sDecimal = _pView->getDecimalSeparator();
1288 // I will need a cast pointer to my css::sdbcx::Container
1289 OQueryController& rController = static_cast<OQueryController&>(_pView->getController());
1290 SqlParseError eErrorCode = eOk;
1292 // round brackets
1293 if (SQL_ISRULE(pCondition,boolean_primary))
1295 // check if we have to put the or criteria on one line.
1296 const ::connectivity::OSQLParseNode* pSearchCondition = pCondition->getChild(1);
1297 bool bMustAddOrOnOneLine = CheckOrCriteria(pSearchCondition,nullptr);
1298 if ( SQL_ISRULE( pSearchCondition, search_condition) ) // we have a or
1300 _pSelectionBrw->DuplicateConditionLevel( nLevel);
1301 eErrorCode = GetORCriteria(_pView,_pSelectionBrw,pSearchCondition->getChild(0), nLevel,bHaving,bMustAddOrOnOneLine );
1302 if ( eErrorCode == eOk )
1304 ++nLevel;
1305 eErrorCode = GetORCriteria(_pView,_pSelectionBrw,pSearchCondition->getChild(2), nLevel,bHaving,bMustAddOrOnOneLine );
1308 else
1309 eErrorCode = GetORCriteria(_pView,_pSelectionBrw,pSearchCondition, nLevel,bHaving,bMustAddOrOnOneLine );
1311 // The first element is (again) an AND condition
1312 else if ( SQL_ISRULE(pCondition,boolean_term) )
1314 OSL_ENSURE(pCondition->count() == 3,"Illegal definition of boolean_term");
1315 eErrorCode = GetANDCriteria(_pView,_pSelectionBrw,pCondition->getChild(0), nLevel,bHaving,bAddOrOnOneLine );
1316 if ( eErrorCode == eOk )
1317 eErrorCode = GetANDCriteria(_pView,_pSelectionBrw,pCondition->getChild(2), nLevel,bHaving,bAddOrOnOneLine );
1319 else if (SQL_ISRULE( pCondition, comparison_predicate))
1321 eErrorCode = ComparisonPredicate(_pView,_pSelectionBrw,pCondition,nLevel,bHaving,bAddOrOnOneLine);
1323 else if( SQL_ISRULE(pCondition,like_predicate) )
1325 const ::connectivity::OSQLParseNode* pValueExp = pCondition->getChild(0);
1326 if (SQL_ISRULE(pValueExp, column_ref ) )
1328 OUString aCondition;
1329 Reference< XConnection> xConnection = rController.getConnection();
1330 if ( xConnection.is() )
1332 OUString aColumnName;
1333 // the international doesn't matter I have a string
1334 pCondition->parseNodeToPredicateStr(aCondition,
1335 xConnection,
1336 rController.getNumberFormatter(),
1337 aLocale,
1338 sDecimal,
1339 &rController.getParser().getContext());
1341 pValueExp->parseNodeToPredicateStr( aColumnName,
1342 xConnection,
1343 rController.getNumberFormatter(),
1344 aLocale,
1345 sDecimal,
1346 &rController.getParser().getContext());
1348 // don't display the column name
1349 aCondition = aCondition.copy(aColumnName.getLength());
1350 aCondition = aCondition.trim();
1353 OTableFieldDescRef aDragLeft = new OTableFieldDesc();
1354 if ( eOk == ( eErrorCode = FillDragInfo(_pView,pValueExp,aDragLeft) ))
1356 if ( bHaving )
1357 aDragLeft->SetGroupBy(true);
1358 _pSelectionBrw->AddCondition(aDragLeft, aCondition, nLevel,bAddOrOnOneLine);
1361 else if(SQL_ISRULEOR3(pValueExp, general_set_fct, set_fct_spec, position_exp) ||
1362 SQL_ISRULEOR3(pValueExp, extract_exp, fold, char_substring_fct) ||
1363 SQL_ISRULEOR2(pValueExp, length_exp, char_value_fct))
1365 AddFunctionCondition( _pView,
1366 _pSelectionBrw,
1367 pCondition,
1368 nLevel,
1369 bHaving,
1370 bAddOrOnOneLine);
1372 else
1374 eErrorCode = eNoColumnInLike;
1375 OUString sError(DBA_RES(STR_QRY_LIKE_LEFT_NO_COLUMN));
1376 _pView->getController().appendError( sError );
1379 else if( SQL_ISRULEOR2(pCondition,test_for_null,in_predicate)
1380 || SQL_ISRULEOR2(pCondition,all_or_any_predicate,between_predicate))
1382 if ( SQL_ISRULEOR2(pCondition->getChild(0), set_fct_spec , general_set_fct ) )
1384 AddFunctionCondition( _pView,
1385 _pSelectionBrw,
1386 pCondition,
1387 nLevel,
1388 bHaving,
1389 bAddOrOnOneLine);
1391 else if ( SQL_ISRULE(pCondition->getChild(0), column_ref ) )
1393 // parse condition
1394 OUString sCondition = ParseCondition(rController,pCondition,sDecimal,aLocale,1);
1395 OTableFieldDescRef aDragLeft = new OTableFieldDesc();
1396 if ( eOk == ( eErrorCode = FillDragInfo(_pView,pCondition->getChild(0),aDragLeft)) )
1398 if ( bHaving )
1399 aDragLeft->SetGroupBy(true);
1400 _pSelectionBrw->AddCondition(aDragLeft, sCondition, nLevel,bAddOrOnOneLine);
1403 else
1405 // Parse the function condition
1406 OUString sCondition = ParseCondition(rController,pCondition,sDecimal,aLocale,1);
1407 Reference< XConnection> xConnection = rController.getConnection();
1408 // the international doesn't matter I have a string
1409 OUString sName;
1410 pCondition->getChild(0)->parseNodeToPredicateStr(sName,
1411 xConnection,
1412 rController.getNumberFormatter(),
1413 aLocale,
1414 sDecimal,
1415 &rController.getParser().getContext());
1417 OTableFieldDescRef aDragLeft = new OTableFieldDesc();
1418 aDragLeft->SetField(sName);
1419 aDragLeft->SetFunctionType(FKT_OTHER);
1421 if ( bHaving )
1422 aDragLeft->SetGroupBy(true);
1423 _pSelectionBrw->AddCondition(aDragLeft, sCondition, nLevel,bAddOrOnOneLine);
1426 else if( SQL_ISRULEOR2(pCondition,existence_test,unique_test) )
1428 // Parse the function condition
1429 OUString aCondition = ParseCondition(rController,pCondition,sDecimal,aLocale,0);
1431 OTableFieldDescRef aDragLeft = new OTableFieldDesc();
1432 aDragLeft->SetField(aCondition);
1433 aDragLeft->SetFunctionType(FKT_CONDITION);
1435 eErrorCode = _pSelectionBrw->InsertField(aDragLeft,BROWSER_INVALIDID,false).is() ? eOk : eTooManyColumns;
1437 else //! TODO not supported yet
1438 eErrorCode = eStatementTooComplex;
1439 // Pass on the error code
1440 return eErrorCode;
1442 SqlParseError AddFunctionCondition(OQueryDesignView const * _pView,
1443 OSelectionBrowseBox* _pSelectionBrw,
1444 const ::connectivity::OSQLParseNode * pCondition,
1445 const sal_uInt16 nLevel,
1446 bool bHaving,
1447 bool bAddOrOnOneLine)
1449 SqlParseError eErrorCode = eOk;
1450 OQueryController& rController = static_cast<OQueryController&>(_pView->getController());
1452 OSQLParseNode* pFunction = pCondition->getChild(0);
1454 OSL_ENSURE(SQL_ISRULEOR3(pFunction, general_set_fct, set_fct_spec, position_exp) ||
1455 SQL_ISRULEOR3(pFunction, extract_exp, fold, char_substring_fct) ||
1456 SQL_ISRULEOR2(pFunction,length_exp,char_value_fct),
1457 "Illegal call!");
1459 Reference< XConnection> xConnection = rController.getConnection();
1460 if(xConnection.is())
1462 OUString aCondition;
1463 OUString aColumnName;
1464 OTableFieldDescRef aDragLeft = new OTableFieldDesc();
1465 pCondition->parseNodeToPredicateStr(aCondition,
1466 xConnection,
1467 rController.getNumberFormatter(),
1468 _pView->getLocale(),
1469 _pView->getDecimalSeparator(),
1470 &rController.getParser().getContext());
1472 pFunction->parseNodeToStr( aColumnName,
1473 xConnection,
1474 &rController.getParser().getContext(),
1475 true); // quote is to true because we need quoted elements inside the function
1476 // don't display the column name
1477 aCondition = aCondition.copy(aColumnName.getLength());
1478 aCondition = aCondition.trim();
1479 if ( aCondition.startsWith("=") ) // ignore the equal sign
1480 aCondition = aCondition.copy(1);
1482 if ( SQL_ISRULE(pFunction, general_set_fct ) )
1484 sal_Int32 nFunctionType = FKT_AGGREGATE;
1485 OSQLParseNode* pParamNode = pFunction->getChild(pFunction->count()-2);
1486 if ( pParamNode && pParamNode->getTokenValue().toChar() == '*' )
1488 OJoinTableView::OTableWindowMap& rTabList = _pView->getTableView()->GetTabWinMap();
1489 for (auto const& table : rTabList)
1491 OQueryTableWindow* pTabWin = static_cast<OQueryTableWindow*>(table.second.get());
1492 if (pTabWin->ExistsField( "*", aDragLeft ))
1494 aDragLeft->SetAlias(OUString());
1495 aDragLeft->SetTable(OUString());
1496 break;
1500 else if (pParamNode)
1502 eErrorCode = FillDragInfo(_pView,pParamNode,aDragLeft);
1503 if ( eOk != eErrorCode && SQL_ISRULE(pParamNode,num_value_exp))
1505 OUString sParameterValue;
1506 pParamNode->parseNodeToStr( sParameterValue,
1507 xConnection,
1508 &rController.getParser().getContext());
1509 nFunctionType |= FKT_NUMERIC;
1510 aDragLeft->SetField(sParameterValue);
1511 eErrorCode = eOk;
1514 aDragLeft->SetFunctionType(nFunctionType);
1515 if ( bHaving )
1516 aDragLeft->SetGroupBy(true);
1517 aDragLeft->SetFunction(aColumnName.getToken(0, '('));
1519 else
1521 // for an unknown function we write the whole text in the field
1522 aDragLeft->SetField(aColumnName);
1523 if(bHaving)
1524 aDragLeft->SetGroupBy(true);
1525 aDragLeft->SetFunctionType(FKT_OTHER|FKT_NUMERIC);
1527 _pSelectionBrw->AddCondition(aDragLeft, aCondition, nLevel,bAddOrOnOneLine);
1530 return eErrorCode;
1532 SqlParseError ComparisonPredicate(OQueryDesignView const * _pView,
1533 OSelectionBrowseBox* _pSelectionBrw,
1534 const ::connectivity::OSQLParseNode * pCondition,
1535 const sal_uInt16 nLevel,
1536 bool bHaving
1537 ,bool bAddOrOnOneLine)
1539 SqlParseError eErrorCode = eOk;
1540 OQueryController& rController = static_cast<OQueryController&>(_pView->getController());
1542 OSL_ENSURE(SQL_ISRULE( pCondition, comparison_predicate),"ComparisonPredicate: pCondition is not a Comparison Predicate");
1543 if ( SQL_ISRULE(pCondition->getChild(0), column_ref )
1544 || SQL_ISRULE(pCondition->getChild(pCondition->count()-1), column_ref) )
1546 OUString aCondition;
1547 OTableFieldDescRef aDragLeft = new OTableFieldDesc();
1549 if ( SQL_ISRULE(pCondition->getChild(0), column_ref ) && SQL_ISRULE(pCondition->getChild(pCondition->count()-1), column_ref ) )
1551 OTableFieldDescRef aDragRight = new OTableFieldDesc();
1552 eErrorCode = FillDragInfo(_pView,pCondition->getChild(0),aDragLeft);
1553 if (eOk != eErrorCode)
1554 return eErrorCode;
1555 eErrorCode = FillDragInfo(_pView,pCondition->getChild(2),aDragRight);
1556 if (eOk != eErrorCode)
1557 return eErrorCode;
1559 OQueryTableConnection* pConn = static_cast<OQueryTableConnection*>(
1560 _pView->getTableView()->GetTabConn(static_cast<OQueryTableWindow*>(aDragLeft->GetTabWindow()),
1561 static_cast<OQueryTableWindow*>(aDragRight->GetTabWindow()),
1562 true));
1563 if ( pConn )
1565 OConnectionLineDataVec& rLineDataList = pConn->GetData()->GetConnLineDataList();
1566 for (auto const& lineData : rLineDataList)
1568 if(lineData->GetSourceFieldName() == aDragLeft->GetField() ||
1569 lineData->GetDestFieldName() == aDragLeft->GetField() )
1570 return eOk;
1575 sal_uInt32 nPos = 0;
1576 if(SQL_ISRULE(pCondition->getChild(0), column_ref ))
1578 nPos = 0;
1579 sal_uInt32 i=1;
1581 // don't display the equal
1582 if (pCondition->getChild(i)->getNodeType() == SQLNodeType::Equal)
1583 i++;
1585 // parse the condition
1586 aCondition = ParseCondition(rController
1587 ,pCondition
1588 ,_pView->getDecimalSeparator()
1589 ,_pView->getLocale()
1590 ,i);
1592 else if( SQL_ISRULE(pCondition->getChild(pCondition->count()-1), column_ref ) )
1594 nPos = pCondition->count()-1;
1596 sal_Int32 i = static_cast<sal_Int32>(pCondition->count() - 2);
1597 switch (pCondition->getChild(i)->getNodeType())
1599 case SQLNodeType::Equal:
1600 // don't display the equal
1601 i--;
1602 break;
1603 case SQLNodeType::Less:
1604 // take the opposite as we change the order
1605 i--;
1606 aCondition += ">";
1607 break;
1608 case SQLNodeType::LessEq:
1609 // take the opposite as we change the order
1610 i--;
1611 aCondition += ">=";
1612 break;
1613 case SQLNodeType::Great:
1614 // take the opposite as we change the order
1615 i--;
1616 aCondition += "<";
1617 break;
1618 case SQLNodeType::GreatEq:
1619 // take the opposite as we change the order
1620 i--;
1621 aCondition += "<=";
1622 break;
1623 default:
1624 break;
1627 // go backward
1628 Reference< XConnection> xConnection = rController.getConnection();
1629 if(xConnection.is())
1631 for (; i >= 0; i--)
1632 pCondition->getChild(i)->parseNodeToPredicateStr(aCondition,
1633 xConnection,
1634 rController.getNumberFormatter(),
1635 _pView->getLocale(),
1636 _pView->getDecimalSeparator(),
1637 &rController.getParser().getContext());
1640 // else ???
1642 if( eOk == ( eErrorCode = FillDragInfo(_pView,pCondition->getChild(nPos),aDragLeft)))
1644 if(bHaving)
1645 aDragLeft->SetGroupBy(true);
1646 _pSelectionBrw->AddCondition(aDragLeft, aCondition, nLevel,bAddOrOnOneLine);
1649 else if( SQL_ISRULEOR2(pCondition->getChild(0), set_fct_spec , general_set_fct ) )
1651 AddFunctionCondition( _pView,
1652 _pSelectionBrw,
1653 pCondition,
1654 nLevel,
1655 bHaving,
1656 bAddOrOnOneLine);
1658 else // it can only be an Expr
1660 OUString aName,aCondition;
1662 // Field name
1663 Reference< XConnection> xConnection = rController.getConnection();
1664 if(xConnection.is())
1666 ::connectivity::OSQLParseNode *pLhs = pCondition->getChild(0);
1667 ::connectivity::OSQLParseNode *pRhs = pCondition->getChild(2);
1668 pLhs->parseNodeToStr(aName,
1669 xConnection,
1670 &rController.getParser().getContext(),
1671 true);
1672 // Criteria
1673 aCondition = pCondition->getChild(1)->getTokenValue();
1674 pRhs->parseNodeToPredicateStr(aCondition,
1675 xConnection,
1676 rController.getNumberFormatter(),
1677 _pView->getLocale(),
1678 _pView->getDecimalSeparator(),
1679 &rController.getParser().getContext());
1682 OTableFieldDescRef aDragLeft = new OTableFieldDesc();
1683 aDragLeft->SetField(aName);
1684 aDragLeft->SetFunctionType(FKT_OTHER|FKT_NUMERIC);
1685 // and add it on
1686 _pSelectionBrw->AddCondition(aDragLeft, aCondition, nLevel,bAddOrOnOneLine);
1688 return eErrorCode;
1691 OQueryTableWindow* lcl_findColumnInTables( const OUString& _rColumName, const OJoinTableView::OTableWindowMap& _rTabList, OTableFieldDescRef const & _rInfo )
1693 for (auto const& table : _rTabList)
1695 OQueryTableWindow* pTabWin = static_cast< OQueryTableWindow* >( table.second.get() );
1696 if ( pTabWin && pTabWin->ExistsField( _rColumName, _rInfo ) )
1697 return pTabWin;
1699 return nullptr;
1702 void InsertColumnRef(const OQueryDesignView* _pView,
1703 const ::connectivity::OSQLParseNode * pColumnRef,
1704 OUString& aColumnName,
1705 const OUString& aColumnAlias,
1706 OUString& aTableRange,
1707 OTableFieldDescRef const & _raInfo,
1708 OJoinTableView::OTableWindowMap const * pTabList)
1711 // Put the table names together
1712 ::connectivity::OSQLParseTreeIterator& rParseIter = static_cast<OQueryController&>(_pView->getController()).getParseIterator();
1713 rParseIter.getColumnRange( pColumnRef, aColumnName, aTableRange );
1715 bool bFound(false);
1716 OSL_ENSURE(!aColumnName.isEmpty(),"Column name must not be empty");
1717 if (aTableRange.isEmpty())
1719 // SELECT column, ...
1720 bFound = nullptr != lcl_findColumnInTables( aColumnName, *pTabList, _raInfo );
1721 if ( bFound && ( aColumnName.toChar() != '*' ) )
1722 _raInfo->SetFieldAlias(aColumnAlias);
1724 else
1726 // SELECT range.column, ...
1727 OQueryTableWindow* pTabWin = static_cast<OQueryTableView*>(_pView->getTableView())->FindTable(aTableRange);
1729 if (pTabWin && pTabWin->ExistsField(aColumnName, _raInfo))
1731 if(aColumnName.toChar() != '*')
1732 _raInfo->SetFieldAlias(aColumnAlias);
1733 bFound = true;
1736 if (!bFound)
1738 _raInfo->SetTable(OUString());
1739 _raInfo->SetAlias(OUString());
1740 _raInfo->SetField(aColumnName);
1741 _raInfo->SetFieldAlias(aColumnAlias); // nyi : here it continues Expr_1, Expr_2 ...
1742 _raInfo->SetFunctionType(FKT_OTHER);
1745 bool checkJoinConditions( const OQueryDesignView* _pView,
1746 const ::connectivity::OSQLParseNode* _pNode )
1748 const ::connectivity::OSQLParseNode* pJoinNode = nullptr;
1749 bool bRet = true;
1750 if (SQL_ISRULE(_pNode,qualified_join))
1751 pJoinNode = _pNode;
1752 else if (SQL_ISRULE(_pNode,table_ref)
1753 && _pNode->count() == 3
1754 && SQL_ISPUNCTUATION(_pNode->getChild(0),"(")
1755 && SQL_ISPUNCTUATION(_pNode->getChild(2),")") ) // '(' joined_table ')'
1756 pJoinNode = _pNode->getChild(1);
1757 else if (! ( SQL_ISRULE(_pNode, table_ref) && _pNode->count() == 2) ) // table_node table_primary_as_range_column
1758 bRet = false;
1760 if (pJoinNode && !InsertJoin(_pView,pJoinNode))
1761 bRet = false;
1762 return bRet;
1764 bool InsertJoin(const OQueryDesignView* _pView,
1765 const ::connectivity::OSQLParseNode *pNode)
1767 OSL_ENSURE( SQL_ISRULE( pNode, qualified_join ) || SQL_ISRULE( pNode, joined_table ) || SQL_ISRULE( pNode, cross_union ),
1768 "OQueryDesignView::InsertJoin: Error in the Parse Tree");
1770 if (SQL_ISRULE(pNode,joined_table))
1771 return InsertJoin(_pView,pNode->getChild(1));
1773 // first check the left and right side
1774 const ::connectivity::OSQLParseNode* pRightTableRef = pNode->getChild(3); // table_ref
1775 if ( SQL_ISRULE(pNode, qualified_join) && SQL_ISTOKEN(pNode->getChild(1),NATURAL) )
1776 pRightTableRef = pNode->getChild(4); // table_ref
1778 if ( !checkJoinConditions(_pView,pNode->getChild(0)) || !checkJoinConditions(_pView,pRightTableRef))
1779 return false;
1781 // named column join may be implemented later
1782 // SQL_ISRULE(pNode->getChild(4),named_columns_join)
1783 EJoinType eJoinType = INNER_JOIN;
1784 bool bNatural = false;
1785 if ( SQL_ISRULE(pNode, qualified_join) )
1787 ::connectivity::OSQLParseNode* pJoinType = pNode->getChild(1); // join_type
1788 if ( SQL_ISTOKEN(pJoinType,NATURAL) )
1790 bNatural = true;
1791 pJoinType = pNode->getChild(2);
1794 if (SQL_ISRULE(pJoinType,join_type) && (!pJoinType->count() || SQL_ISTOKEN(pJoinType->getChild(0),INNER)))
1796 eJoinType = INNER_JOIN;
1798 else
1800 if (SQL_ISRULE(pJoinType,join_type)) // one level deeper
1801 pJoinType = pJoinType->getChild(0);
1803 if (SQL_ISTOKEN(pJoinType->getChild(0),LEFT))
1804 eJoinType = LEFT_JOIN;
1805 else if(SQL_ISTOKEN(pJoinType->getChild(0),RIGHT))
1806 eJoinType = RIGHT_JOIN;
1807 else
1808 eJoinType = FULL_JOIN;
1810 if ( SQL_ISRULE(pNode->getChild(4),join_condition) )
1812 if ( InsertJoinConnection(_pView,pNode->getChild(4)->getChild(1), eJoinType,pNode->getChild(0),pRightTableRef) != eOk )
1813 return false;
1816 else if ( SQL_ISRULE(pNode, cross_union) )
1818 eJoinType = CROSS_JOIN;
1819 pRightTableRef = pNode->getChild(pNode->count() - 1);
1821 else
1822 return false;
1824 if ( eJoinType != CROSS_JOIN && !bNatural )
1825 return true;
1827 OQueryTableWindow* pLeftWindow = static_cast<OQueryTableView*>(_pView->getTableView())->FindTable( getTableRange(_pView,pNode->getChild(0)) );
1828 OQueryTableWindow* pRightWindow = static_cast<OQueryTableView*>(_pView->getTableView())->FindTable( getTableRange(_pView,pRightTableRef) );
1829 OSL_ENSURE(pLeftWindow && pRightWindow,"Table Windows could not be found!");
1830 if ( !pLeftWindow || !pRightWindow )
1831 return false;
1833 OTableFieldDescRef aDragLeft = new OTableFieldDesc();
1834 aDragLeft->SetTabWindow(pLeftWindow);
1835 aDragLeft->SetTable(pLeftWindow->GetTableName());
1836 aDragLeft->SetAlias(pLeftWindow->GetAliasName());
1838 OTableFieldDescRef aDragRight = new OTableFieldDesc();
1839 aDragRight->SetTabWindow(pRightWindow);
1840 aDragRight->SetTable(pRightWindow->GetTableName());
1841 aDragRight->SetAlias(pRightWindow->GetAliasName());
1843 insertConnection(_pView,eJoinType,aDragLeft,aDragRight,bNatural);
1845 return true;
1847 void insertUnUsedFields(OQueryDesignView const * _pView,OSelectionBrowseBox* _pSelectionBrw)
1849 // now we have to insert the fields which aren't in the statement
1850 OQueryController& rController = static_cast<OQueryController&>(_pView->getController());
1851 OTableFields& rUnUsedFields = rController.getUnUsedFields();
1852 for (auto & unusedField : rUnUsedFields)
1853 if(_pSelectionBrw->InsertField(unusedField,BROWSER_INVALIDID,false,false).is())
1854 unusedField = nullptr;
1855 OTableFields().swap( rUnUsedFields );
1858 SqlParseError InitFromParseNodeImpl(OQueryDesignView* _pView,OSelectionBrowseBox* _pSelectionBrw)
1860 SqlParseError eErrorCode = eOk;
1862 OQueryController& rController = static_cast<OQueryController&>(_pView->getController());
1864 _pSelectionBrw->PreFill();
1865 _pSelectionBrw->SetReadOnly(rController.isReadOnly());
1866 _pSelectionBrw->Fill();
1868 ::connectivity::OSQLParseTreeIterator& aIterator = rController.getParseIterator();
1869 const ::connectivity::OSQLParseNode* pParseTree = aIterator.getParseTree();
1873 if ( !pParseTree )
1875 // now we have to insert the fields which aren't in the statement
1876 insertUnUsedFields(_pView,_pSelectionBrw);
1877 break;
1880 if ( !rController.isEscapeProcessing() ) // not allowed in this mode
1882 eErrorCode = eNativeMode;
1883 break;
1886 if ( !( SQL_ISRULE( pParseTree, select_statement ) ) )
1888 eErrorCode = eNoSelectStatement;
1889 break;
1892 const OSQLParseNode* pTableExp = pParseTree->getChild(3);
1893 if ( pTableExp->getChild(7)->count() > 0 || pTableExp->getChild(8)->count() > 0)
1895 eErrorCode = eStatementTooComplex;
1896 break;
1899 Reference< XConnection> xConnection = rController.getConnection();
1900 if ( !xConnection.is() )
1902 OSL_FAIL( "InitFromParseNodeImpl: no connection? no connection!" );
1903 break;
1906 const OSQLTables& aMap = aIterator.getTables();
1907 ::comphelper::UStringMixLess aTmp(aMap.key_comp());
1908 ::comphelper::UStringMixEqual aKeyComp( aTmp.isCaseSensitive() );
1910 Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData();
1913 sal_Int32 nMax = xMetaData->getMaxTablesInSelect();
1914 if ( nMax && nMax < static_cast<sal_Int32>(aMap.size()) )
1916 eErrorCode = eTooManyTables;
1917 break;
1920 OUString sComposedName;
1921 OUString sAlias;
1923 OQueryTableView* pTableView = static_cast<OQueryTableView*>(_pView->getTableView());
1924 pTableView->clearLayoutInformation();
1925 for (auto const& elem : aMap)
1927 OSQLTable xTable = elem.second;
1928 Reference< XPropertySet > xTableProps( xTable, UNO_QUERY_THROW );
1930 sAlias = elem.first;
1932 // check whether this is a query
1933 Reference< XPropertySetInfo > xPSI = xTableProps->getPropertySetInfo();
1934 bool bIsQuery = xPSI.is() && xPSI->hasPropertyByName( PROPERTY_COMMAND );
1936 if ( bIsQuery )
1937 OSL_VERIFY( xTableProps->getPropertyValue( PROPERTY_NAME ) >>= sComposedName );
1938 else
1940 sComposedName = ::dbtools::composeTableName( xMetaData, xTableProps, ::dbtools::EComposeRule::InDataManipulation, false );
1942 // if the alias is the complete (composed) table, then shorten it
1943 if ( aKeyComp( sComposedName, elem.first ) )
1945 OUString sCatalog, sSchema, sTable;
1946 ::dbtools::qualifiedNameComponents( xMetaData, sComposedName, sCatalog, sSchema, sTable, ::dbtools::EComposeRule::InDataManipulation );
1947 sAlias = sTable;
1951 // find the existent window for this alias
1952 OQueryTableWindow* pExistentWin = pTableView->FindTable( sAlias );
1953 if ( !pExistentWin )
1955 pTableView->AddTabWin( sComposedName, sAlias ); // don't create data here
1957 else
1959 // there already exists a window for this alias...
1960 if ( !aKeyComp( pExistentWin->GetData()->GetComposedName(), sComposedName ) )
1961 // ... but for another complete table name -> new window
1962 pTableView->AddTabWin(sComposedName, sAlias);
1966 // now delete the data for which we haven't any tablewindow
1967 OJoinTableView::OTableWindowMap aTableMap(pTableView->GetTabWinMap());
1968 for (auto const& table : aTableMap)
1970 if(aMap.find(table.second->GetComposedName()) == aMap.end() &&
1971 aMap.find(table.first) == aMap.end())
1972 pTableView->RemoveTabWin(table.second);
1975 if ( eOk == (eErrorCode = FillOuterJoins(_pView,pTableExp->getChild(0)->getChild(1))) )
1977 // check if we have a distinct statement
1978 if(SQL_ISTOKEN(pParseTree->getChild(1),DISTINCT))
1980 rController.setDistinct(true);
1981 rController.InvalidateFeature(SID_QUERY_DISTINCT_VALUES);
1983 else
1985 rController.setDistinct(false);
1988 ///check if query has a limit
1989 if( pTableExp->getChild(6)->count() >= 2 && pTableExp->getChild(6)->getChild(1) )
1991 rController.setLimit(
1992 pTableExp->getChild(6)->getChild(1)->getTokenValue().toInt64() );
1994 else
1996 rController.setLimit(-1);
1999 if ( (eErrorCode = InstallFields(_pView, pParseTree, &pTableView->GetTabWinMap())) == eOk )
2001 // GetSelectionCriteria must be called before GetHavingCriteria
2002 sal_uInt16 nLevel=0;
2004 if ( eOk == (eErrorCode = GetSelectionCriteria(_pView,_pSelectionBrw,pParseTree,nLevel)) )
2006 if ( eOk == (eErrorCode = GetGroupCriteria(_pView,_pSelectionBrw,pParseTree)) )
2008 if ( eOk == (eErrorCode = GetHavingCriteria(_pView,_pSelectionBrw,pParseTree,nLevel)) )
2010 if ( eOk == (eErrorCode = GetOrderCriteria(_pView,_pSelectionBrw,pParseTree)) )
2011 insertUnUsedFields(_pView,_pSelectionBrw);
2018 catch(SQLException&)
2020 OSL_FAIL("getMaxTablesInSelect!");
2023 while ( false );
2025 // New Undo-Actions were created in the Manager by the regeneration
2026 rController.ClearUndoManager();
2027 _pSelectionBrw->Invalidate();
2028 return eErrorCode;
2030 /** fillSelectSubList
2031 @return
2032 <TRUE/> when columns could be inserted otherwise <FALSE/>
2034 SqlParseError fillSelectSubList( OQueryDesignView* _pView,
2035 OJoinTableView::OTableWindowMap* _pTabList)
2037 SqlParseError eErrorCode = eOk;
2038 bool bFirstField = true;
2039 for (auto const& table : *_pTabList)
2041 OQueryTableWindow* pTabWin = static_cast<OQueryTableWindow*>(table.second.get());
2042 OTableFieldDescRef aInfo = new OTableFieldDesc();
2043 if (pTabWin->ExistsField( "*", aInfo ))
2045 eErrorCode = _pView->InsertField(aInfo, bFirstField);
2046 bFirstField = false;
2047 if (eErrorCode != eOk)
2048 break;
2051 return eErrorCode;
2053 SqlParseError InstallFields(OQueryDesignView* _pView,
2054 const ::connectivity::OSQLParseNode* pNode,
2055 OJoinTableView::OTableWindowMap* pTabList )
2057 if( pNode==nullptr || !SQL_ISRULE(pNode,select_statement))
2058 return eNoSelectStatement;
2060 ::connectivity::OSQLParseNode* pParseTree = pNode->getChild(2); // selection
2061 bool bFirstField = true; // When initializing, the first field must be reactivated
2063 SqlParseError eErrorCode = eOk;
2065 if ( pParseTree->isRule() && SQL_ISPUNCTUATION(pParseTree->getChild(0),"*") )
2067 // SELECT * ...
2068 eErrorCode = fillSelectSubList(_pView,pTabList);
2070 else if (SQL_ISRULE(pParseTree,scalar_exp_commalist) )
2072 // SELECT column, ...
2073 OQueryController& rController = static_cast<OQueryController&>(_pView->getController());
2074 Reference< XConnection> xConnection = rController.getConnection();
2076 OUString aColumnName,aTableRange;
2077 for (size_t i = 0; i < pParseTree->count() && eOk == eErrorCode ; ++i)
2079 ::connectivity::OSQLParseNode * pColumnRef = pParseTree->getChild(i);
2081 do {
2083 if ( SQL_ISRULE(pColumnRef,select_sublist) )
2085 eErrorCode = fillSelectSubList(_pView,pTabList);
2086 break;
2089 if ( SQL_ISRULE(pColumnRef,derived_column) )
2091 OUString aColumnAlias(connectivity::OSQLParseTreeIterator::getColumnAlias(pColumnRef)); // might be empty
2092 pColumnRef = pColumnRef->getChild(0);
2093 OTableFieldDescRef aInfo = new OTableFieldDesc();
2095 if ( pColumnRef->getKnownRuleID() != OSQLParseNode::subquery &&
2096 pColumnRef->count() == 3 &&
2097 SQL_ISPUNCTUATION(pColumnRef->getChild(0),"(") &&
2098 SQL_ISPUNCTUATION(pColumnRef->getChild(2),")")
2100 pColumnRef = pColumnRef->getChild(1);
2102 if (SQL_ISRULE(pColumnRef,column_ref))
2104 InsertColumnRef(_pView,pColumnRef,aColumnName,aColumnAlias,aTableRange,aInfo,pTabList);
2105 eErrorCode = _pView->InsertField(aInfo, bFirstField);
2106 bFirstField = false;
2108 else if(SQL_ISRULEOR3(pColumnRef, general_set_fct, set_fct_spec, position_exp) ||
2109 SQL_ISRULEOR3(pColumnRef, extract_exp, fold, char_substring_fct) ||
2110 SQL_ISRULEOR2(pColumnRef,length_exp,char_value_fct))
2112 OUString aColumns;
2113 pColumnRef->parseNodeToPredicateStr(aColumns,
2114 xConnection,
2115 rController.getNumberFormatter(),
2116 _pView->getLocale(),
2117 _pView->getDecimalSeparator(),
2118 &rController.getParser().getContext());
2120 sal_Int32 nFunctionType = FKT_NONE;
2121 ::connectivity::OSQLParseNode* pParamRef = nullptr;
2122 sal_Int32 nColumnRefPos = pColumnRef->count() - 2;
2123 if ( nColumnRefPos >= 0 && o3tl::make_unsigned(nColumnRefPos) < pColumnRef->count() )
2124 pParamRef = pColumnRef->getChild(nColumnRefPos);
2126 if ( SQL_ISRULE(pColumnRef,general_set_fct)
2127 && pParamRef && SQL_ISRULE(pParamRef,column_ref) )
2129 // Check the parameters for Column references
2130 InsertColumnRef(_pView,pParamRef,aColumnName,aColumnAlias,aTableRange,aInfo,pTabList);
2132 else if ( SQL_ISRULE(pColumnRef,general_set_fct) )
2134 if ( pParamRef && pParamRef->getTokenValue().toChar() == '*' )
2136 for (auto const& table : *pTabList)
2138 OQueryTableWindow* pTabWin = static_cast<OQueryTableWindow*>(table.second.get());
2139 if (pTabWin->ExistsField( "*", aInfo ))
2141 aInfo->SetAlias(OUString());
2142 aInfo->SetTable(OUString());
2143 break;
2147 else
2149 OUString sFieldName = aColumns;
2150 if ( pParamRef )
2151 { // we got an aggregate function but without column name inside
2152 // so we set the whole argument of the function as field name
2153 nFunctionType |= FKT_NUMERIC;
2154 sFieldName.clear();
2155 pParamRef->parseNodeToStr( sFieldName,
2156 xConnection,
2157 &rController.getParser().getContext(),
2158 true); // quote is to true because we need quoted elements inside the function
2160 aInfo->SetDataType(DataType::DOUBLE);
2161 aInfo->SetFieldType(TAB_NORMAL_FIELD);
2162 aInfo->SetField(sFieldName);
2164 aInfo->SetTabWindow(nullptr);
2165 aInfo->SetFieldAlias(aColumnAlias);
2167 else
2169 _pView->fillFunctionInfo(pColumnRef,aColumns,aInfo);
2170 aInfo->SetFieldAlias(aColumnAlias);
2173 if ( SQL_ISRULE(pColumnRef,general_set_fct) )
2175 aInfo->SetFunctionType(nFunctionType|FKT_AGGREGATE);
2176 aInfo->SetFunction(OUString(comphelper::string::stripEnd(o3tl::getToken(aColumns,0,'('), ' ')));
2178 else
2179 aInfo->SetFunctionType(nFunctionType|FKT_OTHER);
2181 eErrorCode = _pView->InsertField(aInfo, bFirstField);
2182 bFirstField = false;
2184 else
2186 OUString aColumns;
2187 pColumnRef->parseNodeToStr( aColumns,
2188 xConnection,
2189 &rController.getParser().getContext(),
2190 true); // quote is to true because we need quoted elements inside the function
2192 aInfo->SetTabWindow( nullptr );
2194 // since we support queries in queries, the thingie might belong to an existing "table"
2195 OQueryTableWindow* pExistingTable = lcl_findColumnInTables( aColumns, *pTabList, aInfo );
2196 if ( pExistingTable )
2198 aInfo->SetTabWindow( pExistingTable );
2199 aInfo->SetTable( pExistingTable->GetTableName() );
2200 aInfo->SetAlias( pExistingTable->GetAliasName() );
2203 aInfo->SetDataType(DataType::DOUBLE);
2204 aInfo->SetFieldType(TAB_NORMAL_FIELD);
2205 aInfo->SetField(aColumns);
2206 aInfo->SetFieldAlias(aColumnAlias);
2207 aInfo->SetFunctionType(FKT_NUMERIC | FKT_OTHER);
2209 eErrorCode = _pView->InsertField(aInfo, bFirstField);
2210 bFirstField = false;
2213 break;
2216 OSL_FAIL( "InstallFields: don't know how to interpret this parse node!" );
2218 } while ( false );
2221 else
2222 eErrorCode = eStatementTooComplex;
2224 return eErrorCode;
2226 SqlParseError GetOrderCriteria( OQueryDesignView* _pView,
2227 OSelectionBrowseBox* _pSelectionBrw,
2228 const ::connectivity::OSQLParseNode* pParseRoot )
2230 SqlParseError eErrorCode = eOk;
2231 if (!pParseRoot->getChild(3)->getChild(ORDER_BY_CHILD_POS)->isLeaf())
2233 ::connectivity::OSQLParseNode* pNode = pParseRoot->getChild(3)->getChild(ORDER_BY_CHILD_POS)->getChild(2);
2234 ::connectivity::OSQLParseNode* pParamRef = nullptr;
2236 OQueryController& rController = static_cast<OQueryController&>(_pView->getController());
2237 EOrderDir eOrderDir;
2238 for( size_t i=0 ; i<pNode->count() ; i++ )
2240 OTableFieldDescRef aDragLeft = new OTableFieldDesc();
2241 eOrderDir = ORDER_ASC;
2242 ::connectivity::OSQLParseNode* pChild = pNode->getChild( i );
2244 if (SQL_ISTOKEN( pChild->getChild(1), DESC ) )
2245 eOrderDir = ORDER_DESC;
2247 ::connectivity::OSQLParseNode* pArgument = pChild->getChild(0);
2249 if(SQL_ISRULE(pArgument,column_ref))
2251 if( eOk == FillDragInfo(_pView,pArgument,aDragLeft))
2252 _pSelectionBrw->AddOrder( aDragLeft, eOrderDir, i);
2253 else // it could be an alias name for a field
2255 OUString aTableRange,aColumnName;
2256 ::connectivity::OSQLParseTreeIterator& rParseIter = rController.getParseIterator();
2257 rParseIter.getColumnRange( pArgument, aColumnName, aTableRange );
2259 OTableFields& aList = rController.getTableFieldDesc();
2260 for (auto const& elem : aList)
2262 if(elem.is() && elem->GetFieldAlias() == aColumnName)
2263 elem->SetOrderDir( eOrderDir );
2267 else if(SQL_ISRULE(pArgument, general_set_fct ) &&
2268 SQL_ISRULE(pParamRef = pArgument->getChild(pArgument->count()-2),column_ref) &&
2269 eOk == FillDragInfo(_pView,pParamRef,aDragLeft))
2270 _pSelectionBrw->AddOrder( aDragLeft, eOrderDir, i );
2271 else if( SQL_ISRULE(pArgument, set_fct_spec ) )
2274 Reference< XConnection> xConnection = rController.getConnection();
2275 if(xConnection.is())
2277 OUString sCondition;
2278 pArgument->parseNodeToPredicateStr(sCondition,
2279 xConnection,
2280 rController.getNumberFormatter(),
2281 _pView->getLocale(),
2282 _pView->getDecimalSeparator(),
2283 &rController.getParser().getContext());
2284 _pView->fillFunctionInfo(pArgument,sCondition,aDragLeft);
2285 aDragLeft->SetFunctionType(FKT_OTHER);
2286 aDragLeft->SetOrderDir(eOrderDir);
2287 aDragLeft->SetVisible(false);
2288 _pSelectionBrw->AddOrder( aDragLeft, eOrderDir, i );
2290 else
2291 eErrorCode = eColumnNotFound;
2293 else
2294 eErrorCode = eColumnNotFound;
2297 return eErrorCode;
2299 SqlParseError GetHavingCriteria( OQueryDesignView* _pView,
2300 OSelectionBrowseBox* _pSelectionBrw,
2301 const ::connectivity::OSQLParseNode* pSelectRoot,
2302 sal_uInt16& rLevel )
2304 SqlParseError eErrorCode = eOk;
2305 if (!pSelectRoot->getChild(3)->getChild(3)->isLeaf())
2306 eErrorCode = GetORCriteria(_pView,_pSelectionBrw,pSelectRoot->getChild(3)->getChild(3)->getChild(1),rLevel, true);
2307 return eErrorCode;
2309 SqlParseError GetGroupCriteria( OQueryDesignView* _pView,
2310 OSelectionBrowseBox* _pSelectionBrw,
2311 const ::connectivity::OSQLParseNode* pSelectRoot )
2313 SqlParseError eErrorCode = eOk;
2314 if (!pSelectRoot->getChild(3)->getChild(2)->isLeaf()) // opt_group_by_clause
2316 OQueryController& rController = static_cast<OQueryController&>(_pView->getController());
2317 ::connectivity::OSQLParseNode* pGroupBy = pSelectRoot->getChild(3)->getChild(2)->getChild(2);
2319 for( size_t i=0 ; i < pGroupBy->count() && eOk == eErrorCode; ++i )
2321 OTableFieldDescRef aDragInfo = new OTableFieldDesc();
2322 ::connectivity::OSQLParseNode* pParamRef = nullptr;
2323 ::connectivity::OSQLParseNode* pArgument = pGroupBy->getChild( i );
2324 if(SQL_ISRULE(pArgument,column_ref))
2326 if ( eOk == (eErrorCode = FillDragInfo(_pView,pArgument,aDragInfo)) )
2328 aDragInfo->SetGroupBy(true);
2329 _pSelectionBrw->AddGroupBy(aDragInfo);
2332 else if(SQL_ISRULE(pArgument, general_set_fct ) &&
2333 SQL_ISRULE(pParamRef = pArgument->getChild(pArgument->count()-2),column_ref) &&
2334 eOk == FillDragInfo(_pView,pParamRef,aDragInfo))
2336 aDragInfo->SetGroupBy(true);
2337 _pSelectionBrw->AddGroupBy( aDragInfo );
2339 else if( SQL_ISRULE(pArgument, set_fct_spec ) )
2341 Reference< XConnection> xConnection = rController.getConnection();
2342 if(xConnection.is())
2344 OUString sGroupByExpression;
2345 pArgument->parseNodeToStr( sGroupByExpression,
2346 xConnection,
2347 &rController.getParser().getContext(),
2348 true); // quote is to true because we need quoted elements inside the function
2349 _pView->fillFunctionInfo(pArgument,sGroupByExpression,aDragInfo);
2350 aDragInfo->SetFunctionType(FKT_OTHER);
2351 aDragInfo->SetGroupBy(true);
2352 aDragInfo->SetVisible(false);
2353 _pSelectionBrw->AddGroupBy( aDragInfo );
2355 else
2356 eErrorCode = eColumnNotFound;
2360 return eErrorCode;
2363 OUString getParseErrorMessage( SqlParseError _eErrorCode )
2365 TranslateId pResId;
2366 switch (_eErrorCode)
2368 case eIllegalJoin:
2369 pResId = STR_QRY_ILLEGAL_JOIN;
2370 break;
2371 case eStatementTooLong:
2372 pResId = STR_QRY_TOO_LONG_STATEMENT;
2373 break;
2374 case eNoConnection:
2375 pResId = STR_QRY_SYNTAX;
2376 break;
2377 case eNoSelectStatement:
2378 pResId = STR_QRY_NOSELECT;
2379 break;
2380 case eNoColumnInLike:
2381 pResId = STR_QRY_SYNTAX;
2382 break;
2383 case eColumnNotFound:
2384 pResId = STR_QRY_SYNTAX;
2385 break;
2386 case eNativeMode:
2387 pResId = STR_QRY_NATIVE;
2388 break;
2389 case eTooManyTables:
2390 pResId = STR_QRY_TOO_MANY_TABLES;
2391 break;
2392 case eTooManyColumns:
2393 pResId = STR_QRY_TOO_MANY_COLUMNS;
2394 break;
2395 case eStatementTooComplex:
2396 pResId = STR_QRY_TOOCOMPLEX;
2397 break;
2398 default:
2399 pResId = STR_QRY_SYNTAX;
2400 break;
2402 return DBA_RES(pResId);
2406 // end of anonymous namespace
2408 OQueryDesignView::OQueryDesignView( OQueryContainerWindow* _pParent,
2409 OQueryController& _rController,
2410 const Reference< XComponentContext >& _rxContext)
2411 :OJoinDesignView( _pParent, _rController, _rxContext )
2412 ,m_aSplitter( VclPtr<Splitter>::Create(this) )
2413 ,m_eChildFocus(NONE)
2414 ,m_bInSplitHandler( false )
2419 SvtSysLocale aSysLocale;
2420 m_aLocale = aSysLocale.GetLanguageTag().getLocale();
2421 m_sDecimalSep = aSysLocale.GetLocaleData().getNumDecimalSep();
2423 catch(Exception&)
2427 m_pSelectionBox = VclPtr<OSelectionBrowseBox>::Create(this);
2429 setNoneVisibleRow(static_cast<OQueryController&>(getController()).getVisibleRows());
2430 m_pSelectionBox->Show();
2431 // setup Splitter
2432 m_aSplitter->SetSplitHdl(LINK(this, OQueryDesignView,SplitHdl));
2433 m_aSplitter->Show();
2437 OQueryDesignView::~OQueryDesignView()
2439 disposeOnce();
2442 void OQueryDesignView::dispose()
2444 if ( m_pTableView )
2445 ::dbaui::notifySystemWindow(this,m_pTableView,::comphelper::mem_fun(&TaskPaneList::RemoveWindow));
2446 m_pSelectionBox.disposeAndClear();
2447 m_aSplitter.disposeAndClear();
2448 OJoinDesignView::dispose();
2451 IMPL_LINK_NOARG( OQueryDesignView, SplitHdl, Splitter*, void )
2453 if (!getController().isReadOnly())
2455 m_bInSplitHandler = true;
2456 m_aSplitter->SetPosPixel( Point( m_aSplitter->GetPosPixel().X(),m_aSplitter->GetSplitPosPixel() ) );
2457 static_cast<OQueryController&>(getController()).setSplitPos(m_aSplitter->GetSplitPosPixel());
2458 static_cast<OQueryController&>(getController()).setModified( true );
2459 Resize();
2460 m_bInSplitHandler = true;
2464 void OQueryDesignView::Construct()
2466 m_pTableView = VclPtr<OQueryTableView>::Create(m_pScrollWindow,this);
2467 ::dbaui::notifySystemWindow(this,m_pTableView,::comphelper::mem_fun(&TaskPaneList::AddWindow));
2468 OJoinDesignView::Construct();
2471 void OQueryDesignView::initialize()
2473 if(static_cast<OQueryController&>(getController()).getSplitPos() != -1)
2475 m_aSplitter->SetPosPixel( Point( m_aSplitter->GetPosPixel().X(),static_cast<OQueryController&>(getController()).getSplitPos() ) );
2476 m_aSplitter->SetSplitPosPixel(static_cast<OQueryController&>(getController()).getSplitPos());
2478 m_pSelectionBox->initialize();
2479 reset();
2482 void OQueryDesignView::resizeDocumentView(tools::Rectangle& _rPlayground)
2484 Point aPlaygroundPos( _rPlayground.TopLeft() );
2485 Size aPlaygroundSize( _rPlayground.GetSize() );
2487 // calc the split pos, and forward it to the controller
2488 sal_Int32 nSplitPos = static_cast<OQueryController&>(getController()).getSplitPos();
2489 if ( 0 != aPlaygroundSize.Height() )
2491 if ( ( -1 == nSplitPos )
2492 || ( nSplitPos >= aPlaygroundSize.Height() )
2495 // let the selection browse box determine an optimal size
2496 Size aSelectionBoxSize = m_pSelectionBox->CalcOptimalSize( aPlaygroundSize );
2497 nSplitPos = aPlaygroundSize.Height() - aSelectionBoxSize.Height() - m_aSplitter->GetSizePixel().Height();
2498 // still an invalid size?
2499 if ( nSplitPos == -1 || nSplitPos >= aPlaygroundSize.Height() )
2500 nSplitPos = sal_Int32(aPlaygroundSize.Height()*0.6);
2502 static_cast<OQueryController&>(getController()).setSplitPos(nSplitPos);
2505 if ( !m_bInSplitHandler )
2506 { // the resize is triggered by something else than the split handler
2507 // our main focus is to try to preserve the size of the selectionbrowse box
2508 Size aSelBoxSize = m_pSelectionBox->GetSizePixel();
2509 if ( aSelBoxSize.Height() )
2511 // keep the size of the sel box constant
2512 nSplitPos = aPlaygroundSize.Height() - m_aSplitter->GetSizePixel().Height() - aSelBoxSize.Height();
2514 // and if the box is smaller than the optimal size, try to do something about it
2515 Size aSelBoxOptSize = m_pSelectionBox->CalcOptimalSize( aPlaygroundSize );
2516 if ( aSelBoxOptSize.Height() > aSelBoxSize.Height() )
2518 nSplitPos = aPlaygroundSize.Height() - m_aSplitter->GetSizePixel().Height() - aSelBoxOptSize.Height();
2521 static_cast< OQueryController& >(getController()).setSplitPos( nSplitPos );
2526 // normalize the split pos
2527 Point aSplitPos( _rPlayground.Left(), nSplitPos );
2528 Size aSplitSize( _rPlayground.GetSize().Width(), m_aSplitter->GetSizePixel().Height() );
2530 if( ( aSplitPos.Y() + aSplitSize.Height() ) > ( aPlaygroundSize.Height() ))
2531 aSplitPos.setY( aPlaygroundSize.Height() - aSplitSize.Height() );
2533 if( aSplitPos.Y() <= aPlaygroundPos.Y() )
2534 aSplitPos.setY( aPlaygroundPos.Y() + sal_Int32(aPlaygroundSize.Height() * 0.2) );
2536 // position the table
2537 Size aTableViewSize(aPlaygroundSize.Width(), aSplitPos.Y() - aPlaygroundPos.Y());
2538 m_pScrollWindow->SetPosSizePixel(aPlaygroundPos, aTableViewSize);
2540 // position the selection browse box
2541 Point aPos( aPlaygroundPos.X(), aSplitPos.Y() + aSplitSize.Height() );
2542 m_pSelectionBox->SetPosSizePixel( aPos, Size( aPlaygroundSize.Width(), aPlaygroundSize.Height() - aSplitSize.Height() - aTableViewSize.Height() ));
2544 // set the size of the splitter
2545 m_aSplitter->SetPosSizePixel( aSplitPos, aSplitSize );
2546 m_aSplitter->SetDragRectPixel( _rPlayground );
2548 // just for completeness: there is no space left, we occupied it all ...
2549 _rPlayground.SetPos( _rPlayground.BottomRight() );
2550 _rPlayground.SetSize( Size( 0, 0 ) );
2553 void OQueryDesignView::setReadOnly(bool _bReadOnly)
2555 m_pSelectionBox->SetReadOnly(_bReadOnly);
2558 void OQueryDesignView::clear()
2560 m_pSelectionBox->ClearAll(); // clear the whole selection
2561 m_pTableView->ClearAll();
2564 void OQueryDesignView::copy()
2566 if( m_eChildFocus == SELECTION)
2567 m_pSelectionBox->copy();
2570 bool OQueryDesignView::isCutAllowed() const
2572 bool bAllowed = false;
2573 if ( SELECTION == m_eChildFocus )
2574 bAllowed = m_pSelectionBox->isCutAllowed();
2575 return bAllowed;
2578 bool OQueryDesignView::isPasteAllowed() const
2580 bool bAllowed = false;
2581 if ( SELECTION == m_eChildFocus )
2582 bAllowed = m_pSelectionBox->isPasteAllowed();
2583 return bAllowed;
2586 bool OQueryDesignView::isCopyAllowed() const
2588 bool bAllowed = false;
2589 if ( SELECTION == m_eChildFocus )
2590 bAllowed = m_pSelectionBox->isCopyAllowed();
2591 return bAllowed;
2594 void OQueryDesignView::stopTimer()
2596 m_pSelectionBox->stopTimer();
2599 void OQueryDesignView::startTimer()
2601 m_pSelectionBox->startTimer();
2604 void OQueryDesignView::cut()
2606 if( m_eChildFocus == SELECTION)
2608 m_pSelectionBox->cut();
2609 static_cast<OQueryController&>(getController()).setModified(true);
2613 void OQueryDesignView::paste()
2615 if( m_eChildFocus == SELECTION)
2617 m_pSelectionBox->paste();
2618 static_cast<OQueryController&>(getController()).setModified(true);
2622 void OQueryDesignView::TableDeleted(const OUString& rAliasName)
2624 // message that the table was removed from the window
2625 m_pSelectionBox->DeleteFields( rAliasName );
2626 static_cast<OQueryController&>(getController()).InvalidateFeature(ID_BROWSER_ADDTABLE); // inform the view again
2629 bool OQueryDesignView::HasFieldByAliasName(std::u16string_view rFieldName, OTableFieldDescRef const & rInfo) const
2631 return m_pSelectionBox->HasFieldByAliasName( rFieldName, rInfo);
2634 SqlParseError OQueryDesignView::InsertField( const OTableFieldDescRef& rInfo, bool bActivate)
2636 return m_pSelectionBox->InsertField( rInfo, BROWSER_INVALIDID, true/*bVis*/, bActivate ).is() ? eOk : eTooManyColumns;
2639 sal_Int32 OQueryDesignView::getColWidth(sal_uInt16 _nColPos) const
2641 static sal_Int32 s_nDefaultWidth = GetTextWidth("0") * 15;
2642 sal_Int32 nWidth = static_cast<OQueryController&>(getController()).getColWidth(_nColPos);
2643 if ( !nWidth )
2644 nWidth = s_nDefaultWidth;
2645 return nWidth;
2648 void OQueryDesignView::fillValidFields(std::u16string_view sAliasName, weld::ComboBox& rFieldList)
2650 rFieldList.clear();
2652 bool bAllTables = sAliasName.empty();
2654 OJoinTableView::OTableWindowMap& rTabWins = m_pTableView->GetTabWinMap();
2655 OUString strCurrentPrefix;
2656 std::vector< OUString> aFields;
2657 for (auto const& tabWin : rTabWins)
2659 OQueryTableWindow* pCurrentWin = static_cast<OQueryTableWindow*>(tabWin.second.get());
2660 if (bAllTables || (pCurrentWin->GetAliasName() == sAliasName))
2662 strCurrentPrefix = pCurrentWin->GetAliasName() + ".";
2664 pCurrentWin->EnumValidFields(aFields);
2666 for (auto const& field : aFields)
2668 if (bAllTables || field.toChar() == '*')
2669 rFieldList.append_text(strCurrentPrefix + field);
2670 else
2671 rFieldList.append_text(field);
2674 if (!bAllTables)
2675 // this means that I came into this block because the table name was exactly what I was looking for so I can end here
2676 // (and I prevent that fields get added more than once, if a table is repeated in TabWin)
2677 break;
2682 bool OQueryDesignView::PreNotify(NotifyEvent& rNEvt)
2684 if (rNEvt.GetType() == NotifyEventType::GETFOCUS)
2686 if ( m_pSelectionBox && m_pSelectionBox->HasChildPathFocus() )
2687 m_eChildFocus = SELECTION;
2688 else
2689 m_eChildFocus = TABLEVIEW;
2692 return OJoinDesignView::PreNotify(rNEvt);
2695 // check if the statement is correct when not returning false
2696 bool OQueryDesignView::checkStatement()
2698 bool bRet = true;
2699 if ( m_pSelectionBox )
2700 bRet = m_pSelectionBox->Save(); // an error occurred so we return no
2701 return bRet;
2704 OUString OQueryDesignView::getStatement()
2706 OQueryController& rController = static_cast<OQueryController&>(getController());
2707 m_rController.clearError();
2708 // used for fields which aren't any longer in the statement
2709 OTableFields& rUnUsedFields = rController.getUnUsedFields();
2710 OTableFields().swap( rUnUsedFields );
2712 // create the select columns
2713 sal_uInt32 nFieldcount = 0;
2714 OTableFields& rFieldList = rController.getTableFieldDesc();
2715 for (auto const& field : rFieldList)
2717 if (!field->GetField().isEmpty() && field->IsVisible() )
2718 ++nFieldcount;
2719 else if (!field->GetField().isEmpty() &&
2720 !field->HasCriteria() &&
2721 field->isNoneFunction() &&
2722 field->GetOrderDir() == ORDER_NONE &&
2723 !field->IsGroupBy() &&
2724 field->GetFunction().isEmpty() )
2725 rUnUsedFields.push_back(field);
2727 if ( !nFieldcount ) // no visible fields so return
2729 rUnUsedFields = rFieldList;
2730 return OUString();
2733 OQueryTableView::OTableWindowMap& rTabList = m_pTableView->GetTabWinMap();
2734 sal_uInt32 nTabcount = rTabList.size();
2736 OUString aFieldListStr(GenerateSelectList(this,rFieldList,nTabcount>1));
2737 if( aFieldListStr.isEmpty() )
2738 return OUString();
2740 // Exception handling, if no fields have been passed we should not
2741 // change the tab page
2742 // TabBarSelectHdl will query the SQL-OUString for STATEMENT_NOFIELDS
2743 // and trigger an error message
2744 // ----------------- Build table list ----------------------
2746 const auto& rConnList = m_pTableView->getTableConnections();
2747 Reference< XConnection> xConnection = rController.getConnection();
2748 OUString aTableListStr(GenerateFromClause(xConnection,&rTabList,rConnList));
2749 OSL_ENSURE(!aTableListStr.isEmpty(), "OQueryDesignView::getStatement() : unexpected : have Fields, but no Tables !");
2750 // if fields exist now, these only can be created by inserting from an already existing table; if on the other hand
2751 // a table is deleted, also the belonging fields will be deleted -> therefore it CANNOT occur that fields
2752 // exist but no tables exist (and aFieldListStr has its length, I secure this above)
2753 OUStringBuffer aHavingStr,aCriteriaListStr;
2755 // ----------------- build the criteria ----------------------
2756 if (!GenerateCriterias(this,aCriteriaListStr,aHavingStr,rFieldList, nTabcount > 1))
2757 return OUString();
2759 OUString aJoinCrit;
2760 GenerateInnerJoinCriterias(xConnection,aJoinCrit,rConnList);
2761 if(!aJoinCrit.isEmpty())
2763 OUString aTmp = "( " + aJoinCrit + " )";
2764 if(!aCriteriaListStr.isEmpty())
2766 aTmp += C_AND;
2768 aCriteriaListStr.insert(0, aTmp);
2770 // ----------------- construct statement ----------------------
2771 OUStringBuffer aSqlCmd("SELECT ");
2772 if(rController.isDistinct())
2773 aSqlCmd.append(" DISTINCT ");
2774 aSqlCmd.append(aFieldListStr + " FROM " + aTableListStr);
2776 if (!aCriteriaListStr.isEmpty())
2778 aSqlCmd.append(" WHERE " + aCriteriaListStr);
2780 Reference<XDatabaseMetaData> xMeta;
2781 if ( xConnection.is() )
2782 xMeta = xConnection->getMetaData();
2783 bool bUseAlias = nTabcount > 1;
2784 if ( xMeta.is() )
2785 bUseAlias = bUseAlias || !xMeta->supportsGroupByUnrelated();
2787 aSqlCmd.append(GenerateGroupBy(this,rFieldList,bUseAlias));
2788 // ----------------- construct GroupBy and attach ------------
2789 if(!aHavingStr.isEmpty())
2791 aSqlCmd.append(" HAVING " + aHavingStr);
2793 // ----------------- construct sorting and attach ------------
2794 OUString sOrder;
2795 SqlParseError eErrorCode = eOk;
2796 if ( (eErrorCode = GenerateOrder(this,rFieldList,nTabcount > 1,sOrder)) == eOk)
2797 aSqlCmd.append(sOrder);
2798 else
2800 if ( !m_rController.hasError() )
2801 m_rController.appendError( getParseErrorMessage( eErrorCode ) );
2803 m_rController.displayError();
2805 // --------------------- Limit Clause -------------------
2807 const sal_Int64 nLimit = rController.getLimit();
2808 if( nLimit != -1 )
2810 aSqlCmd.append( " LIMIT " + OUString::number(nLimit) );
2814 OUString sSQL = aSqlCmd.makeStringAndClear();
2815 if ( xConnection.is() )
2817 ::connectivity::OSQLParser& rParser( rController.getParser() );
2818 OUString sErrorMessage;
2819 std::unique_ptr<OSQLParseNode> pParseNode( rParser.parseTree( sErrorMessage, sSQL, true ) );
2820 if (pParseNode)
2822 OSQLParseNode* pNode = pParseNode->getChild(3)->getChild(1);
2823 if ( pNode->count() > 1 )
2825 ::connectivity::OSQLParseNode * pCondition = pNode->getChild(1);
2826 if ( pCondition ) // no where clause
2828 OSQLParseNode::compress(pCondition);
2829 OUString sTemp;
2830 pParseNode->parseNodeToStr(sTemp,xConnection);
2831 sSQL = sTemp;
2836 return sSQL;
2839 void OQueryDesignView::setSlotEnabled(sal_Int32 _nSlotId, bool _bEnable)
2841 sal_uInt16 nRow;
2842 switch (_nSlotId)
2844 case SID_QUERY_VIEW_FUNCTIONS:
2845 nRow = BROW_FUNCTION_ROW;
2846 break;
2847 case SID_QUERY_VIEW_TABLES:
2848 nRow = BROW_TABLE_ROW;
2849 break;
2850 case SID_QUERY_VIEW_ALIASES:
2851 nRow = BROW_COLUMNALIAS_ROW;
2852 break;
2853 default:
2854 // ????????????
2855 nRow = 0;
2856 break;
2858 m_pSelectionBox->SetRowVisible(nRow,_bEnable);
2859 m_pSelectionBox->Invalidate();
2862 bool OQueryDesignView::isSlotEnabled(sal_Int32 _nSlotId)
2864 sal_uInt16 nRow;
2865 switch (_nSlotId)
2867 case SID_QUERY_VIEW_FUNCTIONS:
2868 nRow = BROW_FUNCTION_ROW;
2869 break;
2870 case SID_QUERY_VIEW_TABLES:
2871 nRow = BROW_TABLE_ROW;
2872 break;
2873 case SID_QUERY_VIEW_ALIASES:
2874 nRow = BROW_COLUMNALIAS_ROW;
2875 break;
2876 default:
2877 // ?????????
2878 nRow = 0;
2879 break;
2881 return m_pSelectionBox->IsRowVisible(nRow);
2884 void OQueryDesignView::SaveUIConfig()
2886 OQueryController& rCtrl = static_cast<OQueryController&>(getController());
2887 rCtrl.SaveTabWinsPosSize( &m_pTableView->GetTabWinMap(), m_pScrollWindow->GetHScrollBar().GetThumbPos(), m_pScrollWindow->GetVScrollBar().GetThumbPos() );
2888 rCtrl.setVisibleRows( m_pSelectionBox->GetNoneVisibleRows() );
2889 if ( m_aSplitter->GetSplitPosPixel() != 0 )
2890 rCtrl.setSplitPos( m_aSplitter->GetSplitPosPixel() );
2893 std::unique_ptr<OSQLParseNode> OQueryDesignView::getPredicateTreeFromEntry(const OTableFieldDescRef& pEntry,
2894 const OUString& _sCriteria,
2895 OUString& _rsErrorMessage,
2896 Reference<XPropertySet>& _rxColumn) const
2898 OSL_ENSURE(pEntry.is(),"Entry is null!");
2899 if(!pEntry.is())
2900 return nullptr;
2901 Reference< XConnection> xConnection = static_cast<OQueryController&>(getController()).getConnection();
2902 if(!xConnection.is())
2903 return nullptr;
2905 ::connectivity::OSQLParser& rParser( static_cast<OQueryController&>(getController()).getParser() );
2906 OQueryTableWindow* pWin = static_cast<OQueryTableWindow*>(pEntry->GetTabWindow());
2908 // special handling for functions
2909 if ( pEntry->GetFunctionType() & (FKT_OTHER | FKT_AGGREGATE | FKT_NUMERIC) )
2911 // we have a function here so we have to distinguish the type of return value
2912 OUString sFunction;
2913 if ( pEntry->isNumericOrAggregateFunction() )
2914 sFunction = pEntry->GetFunction().getToken(0, '(');
2916 if ( sFunction.isEmpty() )
2917 sFunction = pEntry->GetField().getToken(0, '(');
2919 sal_Int32 nType = ::connectivity::OSQLParser::getFunctionReturnType(sFunction,&rParser.getContext());
2920 if ( nType == DataType::OTHER || (sFunction.isEmpty() && pEntry->isNumericOrAggregateFunction()) )
2922 // first try the international version
2923 OUString sSql = "SELECT * FROM x WHERE " + pEntry->GetField() + _sCriteria;
2924 std::unique_ptr<OSQLParseNode> pParseNode( rParser.parseTree( _rsErrorMessage, sSql, true ) );
2925 nType = DataType::DOUBLE;
2926 if (pParseNode)
2928 OSQLParseNode* pColumnRef = pParseNode->getByRule(OSQLParseNode::column_ref);
2929 if ( pColumnRef )
2931 OTableFieldDescRef aField = new OTableFieldDesc();
2932 if ( eOk == FillDragInfo(this,pColumnRef,aField) )
2934 nType = aField->GetDataType();
2940 Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
2941 rtl::Reference<parse::OParseColumn> pColumn = new parse::OParseColumn( pEntry->GetField(),
2942 OUString(),
2943 OUString(),
2944 OUString(),
2945 ColumnValue::NULLABLE_UNKNOWN,
2948 nType,
2949 false,
2950 false,
2951 xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers(),
2952 OUString(),
2953 OUString(),
2954 OUString());
2955 _rxColumn = pColumn;
2956 pColumn->setFunction(true);
2957 pColumn->setRealName(pEntry->GetField());
2959 else
2961 if (pWin)
2963 Reference<XNameAccess> xColumns = pWin->GetOriginalColumns();
2964 if (xColumns.is() && xColumns->hasByName(pEntry->GetField()))
2965 xColumns->getByName(pEntry->GetField()) >>= _rxColumn;
2969 // _rxColumn, if it is a "lookup" column, not a computed column,
2970 // is guaranteed to be the column taken from the *source* of the column,
2971 // that is either a table or another query.
2972 // _rxColumn is *not* taken from the columns of the query we are constructing
2973 // (and rightfully so, since it may not be part of these columns; SELECT A FROM t WHERE B = foo)
2974 // If it is a computed column, we just constructed it above, with same Name and RealName.
2975 // In all cases, we should use the "external" name of the column, not the "RealName";
2976 // the latter is the name that the column had in the source of the source query.
2977 // An example: we are designing "SELECT A, B FROM q WHERE C='foo'"
2978 // q itself is query "SELECT aye AS A, bee as B, cee as C FROM t"
2979 // We are currently treating the entry "C='foo'"
2980 // Then _rxColumn has Name "C" and RealName "cee". We should *obviously* use "C", not "cee".
2981 std::unique_ptr<OSQLParseNode> pParseNode = rParser.predicateTree( _rsErrorMessage,
2982 _sCriteria,
2983 static_cast<OQueryController&>(getController()).getNumberFormatter(),
2984 _rxColumn,
2985 false);
2986 return pParseNode;
2989 void OQueryDesignView::GetFocus()
2991 OJoinDesignView::GetFocus();
2992 if ( m_pSelectionBox && !m_pSelectionBox->HasChildPathFocus() )
2994 // first we have to deactivate the current cell to refill when necessary
2995 m_pSelectionBox->DeactivateCell();
2996 m_pSelectionBox->ActivateCell(m_pSelectionBox->GetCurRow(), m_pSelectionBox->GetCurColumnId());
2997 m_pSelectionBox->GrabFocus();
3001 void OQueryDesignView::reset()
3003 m_pTableView->ClearAll();
3004 m_pTableView->ReSync();
3007 void OQueryDesignView::setNoneVisibleRow(sal_Int32 _nRows)
3009 m_pSelectionBox->SetNoneVisibleRow(_nRows);
3012 void OQueryDesignView::initByFieldDescriptions( const Sequence< PropertyValue >& i_rFieldDescriptions )
3014 OQueryController& rController = static_cast< OQueryController& >( getController() );
3016 m_pSelectionBox->PreFill();
3017 m_pSelectionBox->SetReadOnly( rController.isReadOnly() );
3018 m_pSelectionBox->Fill();
3020 for ( auto const & field : i_rFieldDescriptions )
3022 ::rtl::Reference< OTableFieldDesc > pField( new OTableFieldDesc() );
3023 pField->Load( field, true );
3024 InsertField( pField, false );
3027 rController.ClearUndoManager();
3028 m_pSelectionBox->Invalidate();
3031 bool OQueryDesignView::initByParseIterator( ::dbtools::SQLExceptionInfo* _pErrorInfo )
3033 SqlParseError eErrorCode = eNativeMode;
3034 m_rController.clearError();
3038 eErrorCode = InitFromParseNodeImpl( this, m_pSelectionBox );
3040 if ( eErrorCode != eOk )
3042 if ( !m_rController.hasError() )
3043 m_rController.appendError( getParseErrorMessage( eErrorCode ) );
3045 if ( _pErrorInfo )
3047 *_pErrorInfo = m_rController.getError();
3049 else
3051 m_rController.displayError();
3055 catch ( const Exception& )
3057 DBG_UNHANDLED_EXCEPTION("dbaccess");
3059 return eErrorCode == eOk;
3062 // Utility function for fillFunctionInfo
3063 namespace {
3064 sal_Int32 char_datatype(const::connectivity::OSQLParseNode* pDataType, const unsigned int offset) {
3065 int cnt = pDataType->count() - offset;
3066 if ( cnt < 0 )
3068 OSL_FAIL("internal error in decoding character datatype specification");
3069 return DataType::VARCHAR;
3071 else if ( cnt == 0 )
3073 if ( offset == 0 )
3075 // The datatype is the node itself
3076 if ( SQL_ISTOKENOR2 (pDataType, CHARACTER, CHAR) )
3077 return DataType::CHAR;
3078 else if ( SQL_ISTOKEN (pDataType, VARCHAR) )
3079 return DataType::VARCHAR;
3080 else if ( SQL_ISTOKEN (pDataType, CLOB) )
3081 return DataType::CLOB;
3082 else
3084 OSL_FAIL("unknown/unexpected token in decoding character datatype specification");
3085 return DataType::VARCHAR;
3088 else
3090 // No child left to read!
3091 OSL_FAIL("incomplete datatype in decoding character datatype specification");
3092 return DataType::VARCHAR;
3096 if ( SQL_ISTOKEN(pDataType->getChild(offset), NATIONAL) )
3097 return char_datatype(pDataType, offset+1);
3098 else if ( SQL_ISTOKENOR3(pDataType->getChild(offset), CHARACTER, CHAR, NCHAR) )
3100 if ( cnt > 2 && SQL_ISTOKEN(pDataType->getChild(offset+1), LARGE) && SQL_ISTOKEN(pDataType->getChild(offset+2), OBJECT) )
3101 return DataType::CLOB;
3102 else if ( cnt > 1 && SQL_ISTOKEN(pDataType->getChild(offset+1), VARYING) )
3103 return DataType::VARCHAR;
3104 else
3105 return DataType::CHAR;
3107 else if ( SQL_ISTOKEN (pDataType->getChild(offset), VARCHAR) )
3108 return DataType::VARCHAR;
3109 else if ( SQL_ISTOKENOR2 (pDataType->getChild(offset), CLOB, NCLOB) )
3110 return DataType::CLOB;
3112 OSL_FAIL("unrecognised character datatype");
3113 return DataType::VARCHAR;
3117 // Try to guess the type of an expression in simple cases.
3118 // Originally meant to be called only on a function call (hence the misnomer),
3119 // but now tries to do the best it can also in other cases.
3120 // Don't completely rely on fillFunctionInfo,
3121 // it won't look at the function's arguments to find the return type
3122 // (in particular, in the case of general_set_fct,
3123 // the return type is the type of the argument;
3124 // if that is (as is typical) a column reference,
3125 // it is the type of the column).
3126 // TODO: There is similar "guess the expression's type" code in several places:
3127 // SelectionBrowseBox.cxx: OSelectionBrowseBox::saveField
3128 // QueryDesignView.cxx: InstallFields, GetOrderCriteria, GetGroupCriteria
3129 // If possible, they should be factorised into this function
3130 // (which should then be renamed...)
3132 void OQueryDesignView::fillFunctionInfo( const ::connectivity::OSQLParseNode* pNode
3133 ,const OUString& sFunctionTerm
3134 ,OTableFieldDescRef& aInfo)
3136 // get the type of the expression, as far as easily possible
3137 OQueryController& rController = static_cast<OQueryController&>(getController());
3138 sal_Int32 nDataType = DataType::DOUBLE;
3139 switch(pNode->getNodeType())
3141 case SQLNodeType::Concat:
3142 case SQLNodeType::String:
3143 nDataType = DataType::VARCHAR;
3144 break;
3145 case SQLNodeType::IntNum:
3146 nDataType = DataType::INTEGER;
3147 break;
3148 case SQLNodeType::ApproxNum:
3149 nDataType = DataType::DOUBLE;
3150 break;
3151 case SQLNodeType::AccessDate:
3152 nDataType = DataType::TIMESTAMP;
3153 break;
3154 case SQLNodeType::Equal:
3155 case SQLNodeType::Less:
3156 case SQLNodeType::Great:
3157 case SQLNodeType::LessEq:
3158 case SQLNodeType::GreatEq:
3159 case SQLNodeType::NotEqual:
3160 nDataType = DataType::BOOLEAN;
3161 break;
3162 case SQLNodeType::Name:
3163 case SQLNodeType::ListRule:
3164 case SQLNodeType::CommaListRule:
3165 case SQLNodeType::Keyword:
3166 case SQLNodeType::Punctuation:
3167 OSL_FAIL("Unexpected SQL Node Type");
3168 break;
3169 case SQLNodeType::Rule:
3170 switch(pNode->getKnownRuleID())
3172 case OSQLParseNode::select_statement:
3173 case OSQLParseNode::table_exp:
3174 case OSQLParseNode::table_ref_commalist:
3175 case OSQLParseNode::table_ref:
3176 case OSQLParseNode::catalog_name:
3177 case OSQLParseNode::schema_name:
3178 case OSQLParseNode::table_name:
3179 case OSQLParseNode::opt_column_commalist:
3180 case OSQLParseNode::column_commalist:
3181 case OSQLParseNode::column_ref_commalist:
3182 case OSQLParseNode::column_ref:
3183 case OSQLParseNode::opt_order_by_clause:
3184 case OSQLParseNode::ordering_spec_commalist:
3185 case OSQLParseNode::ordering_spec:
3186 case OSQLParseNode::opt_asc_desc:
3187 case OSQLParseNode::where_clause:
3188 case OSQLParseNode::opt_where_clause:
3189 case OSQLParseNode::opt_escape:
3190 case OSQLParseNode::scalar_exp_commalist:
3191 case OSQLParseNode::scalar_exp: // Seems to never be generated?
3192 case OSQLParseNode::parameter_ref:
3193 case OSQLParseNode::parameter:
3194 case OSQLParseNode::range_variable:
3195 case OSQLParseNode::delete_statement_positioned:
3196 case OSQLParseNode::delete_statement_searched:
3197 case OSQLParseNode::update_statement_positioned:
3198 case OSQLParseNode::update_statement_searched:
3199 case OSQLParseNode::assignment_commalist:
3200 case OSQLParseNode::assignment:
3201 case OSQLParseNode::insert_statement:
3202 case OSQLParseNode::insert_atom_commalist:
3203 case OSQLParseNode::insert_atom:
3204 case OSQLParseNode::from_clause:
3205 case OSQLParseNode::qualified_join:
3206 case OSQLParseNode::cross_union:
3207 case OSQLParseNode::select_sublist:
3208 case OSQLParseNode::join_type:
3209 case OSQLParseNode::named_columns_join:
3210 case OSQLParseNode::joined_table:
3211 case OSQLParseNode::sql_not:
3212 case OSQLParseNode::manipulative_statement:
3213 case OSQLParseNode::value_exp_commalist:
3214 case OSQLParseNode::union_statement:
3215 case OSQLParseNode::outer_join_type:
3216 case OSQLParseNode::selection:
3217 case OSQLParseNode::base_table_def:
3218 case OSQLParseNode::base_table_element_commalist:
3219 case OSQLParseNode::data_type:
3220 case OSQLParseNode::column_def:
3221 case OSQLParseNode::table_node:
3222 case OSQLParseNode::as_clause:
3223 case OSQLParseNode::opt_as:
3224 case OSQLParseNode::op_column_commalist:
3225 case OSQLParseNode::table_primary_as_range_column:
3226 case OSQLParseNode::character_string_type:
3227 case OSQLParseNode::comparison:
3228 OSL_FAIL("Unexpected SQL RuleID");
3229 break;
3230 case OSQLParseNode::column:
3231 case OSQLParseNode::column_val:
3232 OSL_FAIL("Cannot guess column type");
3233 break;
3234 case OSQLParseNode::values_or_query_spec:
3235 OSL_FAIL("Cannot guess VALUES type");
3236 break;
3237 case OSQLParseNode::derived_column:
3238 OSL_FAIL("Cannot guess computed column type");
3239 break;
3240 case OSQLParseNode::subquery:
3241 OSL_FAIL("Cannot guess subquery return type");
3242 break;
3243 case OSQLParseNode::search_condition:
3244 case OSQLParseNode::comparison_predicate:
3245 case OSQLParseNode::between_predicate:
3246 case OSQLParseNode::like_predicate:
3247 case OSQLParseNode::test_for_null:
3248 case OSQLParseNode::boolean_term:
3249 case OSQLParseNode::boolean_primary:
3250 case OSQLParseNode::in_predicate:
3251 case OSQLParseNode::existence_test:
3252 case OSQLParseNode::unique_test:
3253 case OSQLParseNode::all_or_any_predicate:
3254 case OSQLParseNode::join_condition:
3255 case OSQLParseNode::boolean_factor:
3256 case OSQLParseNode::comparison_predicate_part_2:
3257 case OSQLParseNode::parenthesized_boolean_value_expression:
3258 case OSQLParseNode::other_like_predicate_part_2:
3259 case OSQLParseNode::between_predicate_part_2:
3260 nDataType = DataType::BOOLEAN;
3261 break;
3262 case OSQLParseNode::num_value_exp:
3263 case OSQLParseNode::extract_exp:
3264 case OSQLParseNode::term:
3265 case OSQLParseNode::factor:
3266 // Might by an integer or a float; take the most generic
3267 nDataType = DataType::DOUBLE;
3268 break;
3269 case OSQLParseNode::value_exp_primary:
3270 case OSQLParseNode::value_exp:
3271 case OSQLParseNode::odbc_call_spec:
3272 // Really, we don't know. Let the default.
3273 break;
3274 case OSQLParseNode::position_exp:
3275 case OSQLParseNode::length_exp:
3276 nDataType = DataType::INTEGER;
3277 break;
3278 case OSQLParseNode::char_value_exp:
3279 case OSQLParseNode::char_value_fct:
3280 case OSQLParseNode::fold:
3281 case OSQLParseNode::char_substring_fct:
3282 case OSQLParseNode::char_factor:
3283 case OSQLParseNode::concatenation:
3284 nDataType = DataType::VARCHAR;
3285 break;
3286 case OSQLParseNode::datetime_primary:
3287 nDataType = DataType::TIMESTAMP;
3288 break;
3289 case OSQLParseNode::bit_value_fct:
3290 nDataType = DataType::BINARY;
3291 break;
3292 case OSQLParseNode::general_set_fct: // May depend on argument; ignore that for now
3293 case OSQLParseNode::set_fct_spec:
3295 if (pNode->count() == 0)
3297 // This is not a function call, no sense to continue with a function return type lookup
3298 OSL_FAIL("Got leaf SQL node where non-leaf expected");
3299 break;
3301 const OSQLParseNode* pFunctionName = pNode->getChild(0);
3302 if ( SQL_ISPUNCTUATION(pFunctionName,"{") )
3304 if ( pNode->count() == 3 )
3305 return fillFunctionInfo( pNode->getChild(1), sFunctionTerm, aInfo );
3306 else
3307 OSL_FAIL("ODBC escape not in recognised form");
3308 break;
3310 else
3312 if ( SQL_ISRULEOR2(pNode,length_exp,char_value_fct) )
3313 pFunctionName = pFunctionName->getChild(0);
3315 OUString sFunctionName = pFunctionName->getTokenValue();
3316 if ( sFunctionName.isEmpty() )
3317 sFunctionName = OStringToOUString(OSQLParser::TokenIDToStr(pFunctionName->getTokenID()),RTL_TEXTENCODING_UTF8);
3319 nDataType = OSQLParser::getFunctionReturnType(
3320 sFunctionName
3321 ,&rController.getParser().getContext());
3323 break;
3325 case OSQLParseNode::odbc_fct_spec:
3327 if (pNode->count() != 2)
3329 OSL_FAIL("interior of ODBC escape not in recognised shape");
3330 break;
3333 const OSQLParseNode* const pEscapeType = pNode->getChild(0);
3334 if (SQL_ISTOKEN(pEscapeType, TS))
3335 nDataType = DataType::TIMESTAMP;
3336 else if (SQL_ISTOKEN(pEscapeType, D))
3337 nDataType = DataType::DATE;
3338 else if (SQL_ISTOKEN(pEscapeType, T))
3339 nDataType = DataType::TIME;
3340 else if (SQL_ISTOKEN(pEscapeType, FN))
3341 return fillFunctionInfo( pNode->getChild(1), sFunctionTerm, aInfo );
3342 else
3343 OSL_FAIL("Unknown ODBC escape");
3344 break;
3346 case OSQLParseNode::cast_spec:
3348 if ( pNode->count() != 6 || !SQL_ISTOKEN(pNode->getChild(3), AS) )
3350 OSL_FAIL("CAST not in recognised shape");
3351 break;
3353 const OSQLParseNode *pCastTarget = pNode->getChild(4);
3354 if ( SQL_ISTOKENOR2(pCastTarget, INTEGER, INT) )
3355 nDataType = DataType::INTEGER;
3356 else if ( SQL_ISTOKEN(pCastTarget, SMALLINT) )
3357 nDataType = DataType::SMALLINT;
3358 else if ( SQL_ISTOKEN(pCastTarget, BIGINT) )
3359 nDataType = DataType::BIGINT;
3360 else if ( SQL_ISTOKEN(pCastTarget, FLOAT) )
3361 nDataType = DataType::FLOAT;
3362 else if ( SQL_ISTOKEN(pCastTarget, REAL) )
3363 nDataType = DataType::REAL;
3364 else if ( SQL_ISTOKEN(pCastTarget, DOUBLE) )
3365 nDataType = DataType::DOUBLE;
3366 else if ( SQL_ISTOKEN(pCastTarget, BOOLEAN) )
3367 nDataType = DataType::BOOLEAN;
3368 else if ( SQL_ISTOKEN(pCastTarget, DATE) )
3369 nDataType = DataType::DATE;
3370 else if ( pCastTarget->count() > 0 )
3372 const OSQLParseNode *pDataType = pCastTarget->getChild(0);
3373 while (pDataType->count() > 0)
3375 pCastTarget = pDataType;
3376 pDataType = pDataType->getChild(0);
3378 if ( SQL_ISTOKEN (pDataType, TIME) )
3379 nDataType = DataType::TIME;
3380 else if ( SQL_ISTOKEN (pDataType, TIMESTAMP) )
3381 nDataType = DataType::TIMESTAMP;
3382 else if ( SQL_ISTOKENOR3 (pDataType, CHARACTER, CHAR, NCHAR) )
3383 nDataType = char_datatype(pCastTarget, 0);
3384 else if ( SQL_ISTOKEN (pDataType, VARCHAR) )
3385 nDataType = DataType::VARCHAR;
3386 else if ( SQL_ISTOKEN (pDataType, CLOB) )
3387 nDataType = DataType::CLOB;
3388 else if ( SQL_ISTOKEN (pDataType, NATIONAL) )
3389 nDataType = char_datatype(pCastTarget, 1);
3390 else if ( SQL_ISTOKEN (pDataType, BINARY) )
3392 if ( pCastTarget->count() > 2 && SQL_ISTOKEN(pCastTarget->getChild(1), LARGE) && SQL_ISTOKEN(pCastTarget->getChild(2), OBJECT) )
3393 nDataType = DataType::BLOB;
3394 else if ( pCastTarget->count() > 1 && SQL_ISTOKEN(pCastTarget->getChild(1), VARYING) )
3395 nDataType = DataType::VARBINARY;
3396 else
3397 nDataType = DataType::BINARY;
3399 else if ( SQL_ISTOKEN (pDataType, VARBINARY) )
3400 nDataType = DataType::VARBINARY;
3401 else if ( SQL_ISTOKEN (pDataType, BLOB) )
3402 nDataType = DataType::BLOB;
3403 else if ( SQL_ISTOKEN (pDataType, NUMERIC) )
3404 nDataType = DataType::NUMERIC;
3405 else if ( SQL_ISTOKENOR2 (pDataType, DECIMAL, DEC) )
3406 nDataType = DataType::DECIMAL;
3407 else if ( SQL_ISTOKEN (pDataType, FLOAT) )
3408 nDataType = DataType::FLOAT;
3409 else if ( SQL_ISTOKEN (pDataType, DOUBLE) )
3410 nDataType = DataType::DOUBLE;
3411 else if ( SQL_ISTOKEN (pDataType, INTERVAL) )
3412 // Not in DataType published constant (because not in JDBC...)
3413 nDataType = DataType::VARCHAR;
3414 else
3415 OSL_FAIL("Failed to decode CAST target");
3417 else
3418 OSL_FAIL("Could not decipher CAST target");
3419 break;
3421 default:
3422 OSL_FAIL("Unknown SQL RuleID");
3423 break;
3425 break;
3426 default:
3427 OSL_FAIL("Unknown SQL Node Type");
3428 break;
3431 aInfo->SetDataType(nDataType);
3432 aInfo->SetFieldType(TAB_NORMAL_FIELD);
3433 aInfo->SetField(sFunctionTerm);
3434 aInfo->SetTabWindow(nullptr);
3437 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */