/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
/** quotes a string and search for quotes inside the string and replace them with the new quote @param rValue The value to be quoted. @param rQuote The quote @param rQuoteToReplace The quote to replace with @return The quoted string.
*/
OUString SetQuotation(const OUString& rValue, std::u16string_view rQuote, std::u16string_view rQuoteToReplace)
{ // Replace quotes with double quotes or the parser gets into problems if (!rQuote.empty()) return rQuote + rValue.replaceAll(rQuote, rQuoteToReplace) + rQuote; return rValue;
}
OUStringBuffer sBuffer(rString); try
{
OSQLParseNode::impl_parseNodeToString_throw( sBuffer,
SQLParseNodeParameter(
_rxConnection, xFormatter, _xField, _sPredicateTableAlias, rIntl, pContext,
_bIntl, _bQuote, _rDecSep, _bPredicate, false
) );
} catch( const SQLException& )
{
SAL_WARN( "connectivity.parse", "OSQLParseNode::parseNodeToStr: this should not throw!" ); // our callers don't expect this method to throw anything. The only known situation // where impl_parseNodeToString_throw can throw is when there is a cyclic reference // in the sub queries, but this cannot be the case here, as we do not parse to // SDBC level.
}
rString = sBuffer.makeStringAndClear();
}
// table name - might be a query name case table_name:
bSimple=false;
bHandled = impl_parseTableNameNodeToString_throw( rString, rParam ); break;
case as_clause:
bSimple=false;
assert(nCount == 0 || nCount == 2); if (nCount == 2)
{ if ( rParam.aMetaData.generateASBeforeCorrelationName() )
rString.append(" AS ");
m_aChildren[1]->impl_parseNodeToString_throw( rString, rParam, false );
}
bHandled = true; break;
case opt_as:
assert(nCount == 0);
bHandled = true; break;
case like_predicate: // Depending on whether international is given, LIKE is treated differently // international: *, ? are placeholders // else SQL92 conform: %, _
impl_parseLikeNodeToString_throw( rString, rParam, bSimple );
bHandled = true; break;
case general_set_fct: case set_fct_spec: case position_exp: case extract_exp: case length_exp: case char_value_fct:
bSimple=false; if (!addDateValue(rString, rParam))
{ // Do not quote function name
SQLParseNodeParameter aNewParam(rParam);
aNewParam.bQuote = ( SQL_ISRULE(this,length_exp) || SQL_ISRULE(this,char_value_fct) );
// In the comma lists, put commas in-between all subtrees if ((m_eNodeType == SQLNodeType::CommaListRule) && (i < (nCount - 1)))
aStringPara.append(",");
} else
i++;
}
rString.append(aStringPara);
}
bHandled = true; break;
case factor:
bSimple = false; if (nCount == 2 && m_aChildren[0] && m_aChildren[1]
&& (SQL_ISPUNCTUATION(m_aChildren[0], "-") || SQL_ISPUNCTUATION(m_aChildren[0], "+"))
&& (m_aChildren[1]->getNodeType() == SQLNodeType::IntNum
|| m_aChildren[1]->getNodeType() == SQLNodeType::ApproxNum))
{ // A signed number ("+" or "-" plus either IntNum or ApproxNum) // The default processing would first add the sign, then process the number, which // would see that rString is not empty already, and insert a space between the sign // and the digits. Avoid that unneeded space.
OUStringBuffer aFactorPara;
m_aChildren[1]->impl_parseNodeToString_throw(aFactorPara, rParam, bSimple); // Insert a space before the signed number, similar to parseLeaf for IntNum / ApproxNum if (!rString.isEmpty())
rString.append(" ");
rString.append(m_aChildren[0]->getTokenValue() + aFactorPara);
bHandled = true;
} break;
case odbc_call_spec: case subquery: case term: case window_function: case cast_spec: case num_value_exp:
bSimple = false; break; default: break;
} // switch ( getKnownRuleID() )
if ( bHandled ) return;
for (auto i = m_aChildren.begin(); i != m_aChildren.end();)
{ const OSQLParseNode* pSubTree = i->get(); if ( !pSubTree )
{
++i; continue;
}
SQLParseNodeParameter aNewParam(rParam);
// don't replace the field for subqueries if (rParam.xField.is() && SQL_ISRULE(pSubTree,subquery))
aNewParam.xField = nullptr;
// When we are building a criterion inside a query view, // simplify criterion display by removing: // "currentFieldName" // "currentFieldName" = // but only in simple expressions. // This means anything that is made of: // (see the rules conditionalised by inPredicateCheck() in sqlbison.y). // - parentheses // - logical operators (and, or, not) // - comparison operators (IS, =, >, <, BETWEEN, LIKE, ...) // but *not* e.g. in function arguments if (bSimple && rParam.bPredicate && rParam.xField.is() && SQL_ISRULE(pSubTree,column_ref))
{ if (columnMatchP(pSubTree, rParam))
{ // skip field
++i; // if the following node is the comparison operator'=', // we filter it as well if (SQL_ISRULE(this, comparison_predicate))
{ if(i != m_aChildren.end())
{
pSubTree = i->get(); if (pSubTree && pSubTree->getNodeType() == SQLNodeType::Equal)
++i;
}
}
} else
{
pSubTree->impl_parseNodeToString_throw( rString, aNewParam, bSimple );
++i;
// In the comma lists, put commas in-between all subtrees if ((m_eNodeType == SQLNodeType::CommaListRule) && (i != m_aChildren.end()))
rString.append(",");
}
} else
{
pSubTree->impl_parseNodeToString_throw( rString, aNewParam, bSimple );
++i;
// In the comma lists, put commas in-between all subtrees if ((m_eNodeType == SQLNodeType::CommaListRule) && (i != m_aChildren.end()))
{ if (SQL_ISRULE(this,value_exp_commalist) && rParam.bPredicate)
rString.append(";"); else
rString.append(",");
}
} // The right hand-side of these operators is not simple switch ( getKnownRuleID() )
{ case general_set_fct: case set_fct_spec: case position_exp: case extract_exp: case length_exp: case char_value_fct: case odbc_call_spec: case subquery: case comparison_predicate: case between_predicate: case like_predicate: case test_for_null: case in_predicate: case existence_test: case unique_test: case all_or_any_predicate: case join_condition: case comparison_predicate_part_2: case parenthesized_boolean_value_expression: case other_like_predicate_part_2: case between_predicate_part_2:
bSimple=false; break; default: break;
}
}
}
bool OSQLParseNode::impl_parseTableNameNodeToString_throw( OUStringBuffer& rString, const SQLParseNodeParameter& rParam ) const
{ // is the table_name part of a table_ref?
OSL_ENSURE( getParent(), "OSQLParseNode::impl_parseTableNameNodeToString_throw: table_name without parent?" ); if ( !getParent() || ( getParent()->getKnownRuleID() != table_ref ) ) returnfalse;
// if it's a query, maybe we need to substitute the SQL statement ... if ( !rParam.bParseToSDBCLevel ) returnfalse;
if ( !rParam.xQueries.is() ) // connection does not support queries in queries, or was no query supplier returnfalse;
// substitute the query name with the constituting command
OUString sCommand;
OSL_VERIFY( xQuery->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_COMMAND ) ) >>= sCommand );
// the query we found here might itself be based on another query, so parse it recursively
OSL_ENSURE( rParam.pParser, "OSQLParseNode::impl_parseTableNameNodeToString_throw: cannot analyze sub queries without a parser!" ); if ( bEscapeProcessing && rParam.pParser )
{
OUString sError;
std::unique_ptr< OSQLParseNode > pSubQueryNode( rParam.pParser->parseTree( sError, sCommand ) ); if (pSubQueryNode)
{ // parse the sub-select to SDBC level, too
OUStringBuffer sSubSelect;
pSubQueryNode->impl_parseNodeToString_throw( sSubSelect, rParam, false ); if ( !sSubSelect.isEmpty() )
sCommand = sSubSelect.makeStringAndClear();
}
}
// append the query name as table alias, since it might be referenced in other // parts of the statement - but only if there's no other alias name present if ( !lcl_isAliasNamePresent( *this ) )
{
rString.append( " AS " ); if ( rParam.bQuote )
rString.append(SetQuotation( sTableOrQueryName,
rParam.aMetaData.getIdentifierQuoteString(), rParam.aMetaData.getIdentifierQuoteString() ));
}
// don't forget to remove the query name from the history, else multiple inclusions // won't work // #i69227# / 2006-10-10 / frank.schoenheit@sun.com
rParam.pSubQueryHistory->erase( sTableOrQueryName );
if ( ( pLiteral->isRule() && !SQL_ISRULE(pLiteral,value_exp) ) || SQL_ISTOKEN(pLiteral,FALSE) || SQL_ISTOKEN(pLiteral,TRUE) )
{ switch(nType)
{ case DataType::CHAR: case DataType::VARCHAR: case DataType::LONGVARCHAR: case DataType::CLOB: if ( !SQL_ISRULE(pReturn,char_value_exp) && !buildStringNodes(pReturn) )
pReturn = nullptr; break; default: break;
}
} else
{ switch(pLiteral->getNodeType())
{ case SQLNodeType::String: switch(nType)
{ case DataType::CHAR: case DataType::VARCHAR: case DataType::LONGVARCHAR: case DataType::CLOB: break; case DataType::DATE: case DataType::TIME: case DataType::TIMESTAMP: if (m_xFormatter.is())
pReturn = buildDate( nType, pReturn); break; default:
m_sErrorMessage = m_pContext->getErrorMessage(IParseContext::ErrorCode::InvalidCompare); break;
} break; case SQLNodeType::AccessDate: switch(nType)
{ case DataType::DATE: case DataType::TIME: case DataType::TIMESTAMP: if ( m_xFormatter.is() )
pReturn = buildDate( nType, pReturn); else
m_sErrorMessage = m_pContext->getErrorMessage(IParseContext::ErrorCode::InvalidDateCompare); break; default:
m_sErrorMessage = m_pContext->getErrorMessage(IParseContext::ErrorCode::InvalidCompare); break;
} break; case SQLNodeType::IntNum: switch(nType)
{ case DataType::BIT: case DataType::BOOLEAN: case DataType::DECIMAL: case DataType::NUMERIC: case DataType::TINYINT: case DataType::SMALLINT: case DataType::INTEGER: case DataType::BIGINT: case DataType::FLOAT: case DataType::REAL: case DataType::DOUBLE: // kill thousand separators if any
killThousandSeparator(pReturn); break; case DataType::CHAR: case DataType::VARCHAR: case DataType::LONGVARCHAR: case DataType::CLOB:
pReturn = buildNode_STR_NUM(pReturn); break; default:
m_sErrorMessage = m_pContext->getErrorMessage(IParseContext::ErrorCode::InvalidIntCompare); break;
} break; case SQLNodeType::ApproxNum: switch(nType)
{ case DataType::DECIMAL: case DataType::NUMERIC: case DataType::FLOAT: case DataType::REAL: case DataType::DOUBLE: // kill thousand separators if any
killThousandSeparator(pReturn); break; case DataType::CHAR: case DataType::VARCHAR: case DataType::LONGVARCHAR: case DataType::CLOB:
pReturn = buildNode_STR_NUM(pReturn); break; case DataType::INTEGER: default:
m_sErrorMessage = m_pContext->getErrorMessage(IParseContext::ErrorCode::InvalidRealCompare); break;
} break; default:
;
}
} return pReturn;
}
OSQLParseNode* pNode1 = convertNode(nType,pLiteral); if ( pNode1 )
{
OSQLParseNode* pNode2 = convertNode(nType,pLiteral2); if ( m_sErrorMessage.isEmpty() )
nErg = buildNode(pAppend,pCompare,pNode1,pNode2);
}
} if (!pCompare->getParent()) // I have no parent so I was not used and I must die :-) delete pCompare; return nErg;
}
pReturn = new OSQLInternalNode(stringToDouble(_pLiteral->getTokenValue(),nScale),SQLNodeType::String);
} else
pReturn = new OSQLInternalNode(_pLiteral->getTokenValue(),SQLNodeType::String);
// reset the parser
m_xField = xField;
m_xFormatter = xFormatter;
if (m_xField.is())
{
sal_Int32 nType=0; try
{ // get the field name
OUString aString;
// retrieve the fields name // #75243# use the RealName of the column if there is any otherwise the name which could be the alias // of the field
Reference< XPropertySetInfo> xInfo = m_xField->getPropertySetInfo(); if ( bUseRealName && xInfo->hasPropertyByName(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_REALNAME)))
m_xField->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_REALNAME)) >>= aString; else
m_xField->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= aString;
m_sFieldName = aString;
// get the field format key if ( xInfo->hasPropertyByName(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FORMATKEY)))
m_xField->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FORMATKEY)) >>= m_nFormatKey; else
m_nFormatKey = 0;
// get the field type
m_xField->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)) >>= nType;
} catch ( Exception& )
{
OSL_ASSERT(false);
}
if (m_nFormatKey && m_xFormatter.is())
{
Any aValue = getNumberFormatProperty( m_xFormatter, m_nFormatKey, OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_LOCALE) );
OSL_ENSURE(aValue.getValueType() == cppu::UnoType<css::lang::Locale>::get(), "OSQLParser::PredicateTree : invalid language property !");
switch (nType)
{ case DataType::DATE: case DataType::TIME: case DataType::TIMESTAMP:
s_pScanner->SetRule(OSQLScanner::GetDATERule()); break; case DataType::CHAR: case DataType::VARCHAR: case DataType::LONGVARCHAR: case DataType::CLOB:
s_pScanner->SetRule(OSQLScanner::GetSTRINGRule()); break; default:
{ auto& s_xLocaleData = getLocaleData(); if ( s_xLocaleData.get()->get()->getLocaleItem( m_pData->aLocale ).decimalSeparator.toChar() == ',' )
s_pScanner->SetRule(OSQLScanner::GetGERRule()); else
s_pScanner->SetRule(OSQLScanner::GetENGRule());
}
}
// Start the parser if (SQLyyparse() != 0)
{
m_sFieldName.clear();
m_xField.clear();
m_xFormatter.clear();
m_nFormatKey = 0;
m_nDateFormatKey = 0;
if (m_sErrorMessage.isEmpty())
m_sErrorMessage = s_pScanner->getErrorMessage(); if (m_sErrorMessage.isEmpty())
m_sErrorMessage = m_pContext->getErrorMessage(IParseContext::ErrorCode::General);
rErrorMessage = m_sErrorMessage;
// clear the garbage collector
(*s_pGarbageCollector)->clearAndDelete(); // coverity[leaked_storage : FALSE] - because the garbage collector deleted it
m_pParseTree.release(); return nullptr;
} else
{
(*s_pGarbageCollector)->clear();
// Instead, the parse method sets the member pParseTree and simply returns that
OSL_ENSURE(m_pParseTree != nullptr,"OSQLParser: Parser did not return a ParseTree!"); return std::move(m_pParseTree);
}
}
std::unique_lock aGuard(getMutex()); // Do we have to initialize the data? if (s_nRefCount == 0)
{
s_pScanner = new OSQLScanner();
s_pScanner->setScanner();
s_pGarbageCollector = new OSQLParseNodesGarbageCollector();
// reset to UNKNOWN_RULE
static_assert(OSQLParseNode::UNKNOWN_RULE==0, "UNKNOWN_RULE must be 0 for memset to 0 to work");
memset(OSQLParser::s_nRuleIDs,0,sizeof(OSQLParser::s_nRuleIDs));
for (constauto & aRuleDescription : aRuleDescriptions)
{ // look up the rule description in the our identifier map
sal_uInt32 nParserRuleID = StrToRuleID( aRuleDescription.sRuleName ); // map the parser's rule ID to the OSQLParseNode::Rule
s_aReverseRuleIDLookup[ nParserRuleID ] = aRuleDescription.eRule; // and map the OSQLParseNode::Rule to the parser's rule ID
s_nRuleIDs[ aRuleDescription.eRule ] = nParserRuleID;
}
}
++s_nRefCount;
if (m_pContext == nullptr) // take the default context
m_pContext = &s_aDefaultContext;
// if there is no format key, yet, make sure we have a feasible one for our locale try
{ if ( !m_nFormatKey && xFormatTypes.is() )
m_nFormatKey = ::dbtools::getDefaultNumberFormat( m_xField, xFormatTypes, m_pData->aLocale );
} catch( Exception& ) { } const OUString& sValue = pLiteral->getTokenValue();
sal_Int32 nTryFormat = m_nFormatKey; bool bSuccess = lcl_saveConvertToNumber( m_xFormatter, nTryFormat, sValue, _rfValue );
// If our format key didn't do, try the default date format for our locale. if ( !bSuccess && xFormatTypes.is() )
{ try
{
nTryFormat = xFormatTypes->getStandardFormat( NumberFormat::DATE, m_pData->aLocale );
} catch( Exception& ) { }
bSuccess = lcl_saveConvertToNumber( m_xFormatter, nTryFormat, sValue, _rfValue );
}
// if this also didn't do, try ISO format if ( !bSuccess && xFormatTypes.is() )
{ try
{
nTryFormat = xFormatTypes->getFormatIndex( NumberFormatIndex::DATE_DIN_YYYYMMDD, m_pData->aLocale );
} catch( Exception& ) { }
bSuccess = lcl_saveConvertToNumber( m_xFormatter, nTryFormat, sValue, _rfValue );
}
// if this also didn't do, try fallback date format (en-US) if ( !bSuccess )
{
nTryFormat = m_nDateFormatKey;
bSuccess = lcl_saveConvertToNumber( m_xFormatter, nTryFormat, sValue, _rfValue );
} return bSuccess;
}
OSQLParseNode* OSQLParser::buildDate(sal_Int32 _nType,OSQLParseNode*& pLiteral)
{ // try converting the string into a date, according to our format key double fValue = 0.0;
OSQLParseNode* pFCTNode = nullptr;
if ( extractDate(pLiteral,fValue) )
pFCTNode = buildNode_Date( fValue, _nType);
delete pLiteral;
pLiteral = nullptr;
if ( !pFCTNode )
m_sErrorMessage = m_pContext->getErrorMessage(IParseContext::ErrorCode::InvalidDateCompare);
OSQLParseNode::OSQLParseNode(const OSQLParseNode& rParseNode)
{ // Set the getParent to NULL
m_pParent = nullptr;
// Copy the members
m_aNodeValue = rParseNode.m_aNodeValue;
m_eNodeType = rParseNode.m_eNodeType;
m_nNodeID = rParseNode.m_nNodeID;
// Remember that we derived from Container. According to SV-Help the Container's // copy ctor creates a new Container with the same pointers for content. // This means after copying the Container, for all non-NULL pointers a copy is // created and reattached instead of the old pointer.
// If not a leaf, then process SubTrees for (autoconst& child : rParseNode.m_aChildren)
append(new OSQLParseNode(*child));
}
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.24 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.