/* -*- 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 . */ #ifndef INCLUDED_CONNECTIVITY_SQLNODE_HXX #define INCLUDED_CONNECTIVITY_SQLNODE_HXX #include #include #include #include #include #include #include #include namespace com::sun::star::lang { struct Locale; } namespace com::sun::star::sdbc { class SQLException; } namespace com::sun::star::sdbc { class XDatabaseMetaData; } namespace com::sun::star { namespace beans { class XPropertySet; } namespace util { class XNumberFormatter; } namespace container { class XNameAccess; } } #define ORDER_BY_CHILD_POS 5 #define TABLE_EXPRESSION_CHILD_COUNT 9 namespace connectivity { class OSQLParser; class IParseContext; enum class SQLNodeType { Rule, ListRule, CommaListRule, Keyword, Name, String, IntNum, ApproxNum, Equal, Less, Great, LessEq, GreatEq, NotEqual, Punctuation, AccessDate, Concat}; typedef ::std::set< OUString > QueryNameSet; //= SQLParseNodeParameter struct SQLParseNodeParameter { const css::lang::Locale& rLocale; ::dbtools::DatabaseMetaData aMetaData; OSQLParser* pParser; std::shared_ptr< QueryNameSet > pSubQueryHistory; css::uno::Reference< css::util::XNumberFormatter > xFormatter; css::uno::Reference< css::beans::XPropertySet > xField; OUString sPredicateTableAlias; css::uno::Reference< css::container::XNameAccess > xQueries; // see bParseToSDBCLevel const IParseContext& m_rContext; OUString sDecSep; bool bQuote : 1; /// should we quote identifiers? bool bInternational : 1; /// should we internationalize keywords and placeholders? bool bPredicate : 1; /// are we going to parse a mere predicate? bool bParseToSDBCLevel : 1; /// should we create an SDBC-level statement (e.g. with substituted sub queries)? SQLParseNodeParameter( const css::uno::Reference< css::sdbc::XConnection >& _rxConnection, const css::uno::Reference< css::util::XNumberFormatter >& _xFormatter, const css::uno::Reference< css::beans::XPropertySet >& _xField, OUString _sPredicateTableAlias, const css::lang::Locale& _rLocale, const IParseContext* _pContext, bool _bIntl, bool _bQuote, OUString _sDecSep, bool _bPredicate, bool _bParseToSDBC ); }; //= OSQLParseNode class OOO_DLLPUBLIC_DBTOOLS OSQLParseNode { friend class OSQLParser; std::vector< std::unique_ptr > m_aChildren; OSQLParseNode* m_pParent; // pParent for reverse linkage in the tree OUString m_aNodeValue; // token name, or empty in case of rules, // or OUString in case of // OUString, INT, etc. SQLNodeType m_eNodeType; // see above sal_uInt32 m_nNodeID; // Rule ID (if IsRule()) // or Token ID (if !IsRule()) // Rule IDs and Token IDs can't // be distinguished by their values, // IsRule has to be used for that! public: enum Rule { UNKNOWN_RULE = 0, // ID indicating that a node is no rule with a matching Rule-enum value (see getKnownRuleID) // we make sure it is 0 so that it is the default-constructor value of this enum // and std::map::operator[](bar) default-inserts UNKNOWN_RULE rather than select_statement (!) select_statement, table_exp, table_ref_commalist, table_ref, catalog_name, schema_name, table_name, opt_column_commalist, column_commalist, column_ref_commalist, column_ref, opt_order_by_clause, ordering_spec_commalist, ordering_spec, opt_asc_desc, where_clause, opt_where_clause, search_condition, comparison, comparison_predicate, between_predicate, like_predicate, opt_escape, test_for_null, scalar_exp_commalist, scalar_exp, parameter_ref, parameter, general_set_fct, range_variable, column, delete_statement_positioned, delete_statement_searched, update_statement_positioned, update_statement_searched, assignment_commalist, assignment, values_or_query_spec, insert_statement, insert_atom_commalist, insert_atom, from_clause, qualified_join, cross_union, select_sublist, derived_column, column_val, set_fct_spec, boolean_term, boolean_primary, num_value_exp, join_type, position_exp, extract_exp, length_exp, char_value_fct, odbc_call_spec, in_predicate, existence_test, unique_test, all_or_any_predicate, named_columns_join, join_condition, joined_table, boolean_factor, sql_not, manipulative_statement, subquery, value_exp_commalist, odbc_fct_spec, union_statement, outer_join_type, char_value_exp, term, value_exp_primary, value_exp, selection, fold, char_substring_fct, factor, base_table_def, base_table_element_commalist, data_type, column_def, table_node, as_clause, opt_as, op_column_commalist, table_primary_as_range_column, datetime_primary, concatenation, char_factor, bit_value_fct, comparison_predicate_part_2, parenthesized_boolean_value_expression, character_string_type, other_like_predicate_part_2, between_predicate_part_2, null_predicate_part_2, cast_spec, window_function, rule_count // last value }; // must be ascii encoding for the value OSQLParseNode(const char* _pValueStr, SQLNodeType _eNodeType, sal_uInt32 _nNodeID = 0); OSQLParseNode(std::string_view _rValue, SQLNodeType eNewNodeType, sal_uInt32 nNewNodeID=0); OSQLParseNode(OUString _sValue, SQLNodeType _eNodeType, sal_uInt32 _nNodeID = 0); // copies the respective ParseNode OSQLParseNode(const OSQLParseNode& rParseNode); OSQLParseNode& operator=(const OSQLParseNode& rParseNode); bool operator==(OSQLParseNode const & rParseNode) const; // destructor destructs the tree recursively virtual ~OSQLParseNode(); OSQLParseNode* getParent() const {return m_pParent;}; void setParent(OSQLParseNode* pParseNode) {m_pParent = pParseNode;}; size_t count() const {return m_aChildren.size();}; inline OSQLParseNode* getChild(sal_uInt32 nPos) const; void append(OSQLParseNode* pNewSubTree); void insert(sal_uInt32 nPos, OSQLParseNode* pNewSubTree); void replaceAndDelete(OSQLParseNode* pOldSubTree, OSQLParseNode* pNewSubTree); OSQLParseNode* removeAt(sal_uInt32 nPos); void replaceNodeValue(const OUString& rTableAlias,const OUString& rColumnName); /** parses the node to a string which can be passed to a driver's connection for execution Any particles of the parse tree which represent application-level features - such as queries appearing in the FROM part - are substituted, so that the resulting statement can be executed at an SDBC-level connection. @param _out_rString is an output parameter taking the resulting SQL statement @param _rxConnection the connection relative to which to parse. This must be an SDB-level connection (e.g. support the XQueriesSupplier interface) for the method to be able to do all necessary substitutions. @param _rParser the SQLParser used to create the node. This is needed in case we need to parse sub queries which are present in the SQL statement - those sub queries need to be parsed, too, to check whether they contain nested sub queries. @param _pErrorHolder takes the error which occurred while generating the statement, if any. Might be , in this case the error is not reported back, and can only be recognized by examining the return value. @return if and only if the parsing was successful.
Currently, there's only one condition how this method can fail: If it contains a nested query which causes a cycle. E.g., consider a statement SELECT * from "foo", where foo is a query defined as SELECT * FROM "bar", where bar is defined as SELECT * FROM "foo". This statement obviously cannot be parsed to an executable statement. If this method returns , you're encouraged to check and handle the error in _pErrorHolder. */ bool parseNodeToExecutableStatement( OUString& _out_rString, const css::uno::Reference< css::sdbc::XConnection >& _rxConnection, OSQLParser& _rParser, css::sdbc::SQLException* _pErrorHolder ) const; void parseNodeToStr(OUString& rString, const css::uno::Reference< css::sdbc::XConnection >& _rxConnection, const IParseContext* pContext = nullptr, bool _bIntl = false, bool _bQuote= true) const; // quoted and internationalised void parseNodeToPredicateStr(OUString& rString, const css::uno::Reference< css::sdbc::XConnection >& _rxConnection, const css::uno::Reference< css::util::XNumberFormatter > & xFormatter, const css::lang::Locale& rIntl, const OUString& rDec, const IParseContext* pContext = nullptr ) const; void parseNodeToPredicateStr(OUString& rString, const css::uno::Reference< css::sdbc::XConnection >& _rxConnection, const css::uno::Reference< css::util::XNumberFormatter > & xFormatter, const css::uno::Reference< css::beans::XPropertySet > & _xField, const OUString &_sTableAlias, const css::lang::Locale& rIntl, const OUString& rStrDec, const IParseContext* pContext = nullptr ) const; OSQLParseNode* getByRule(OSQLParseNode::Rule eRule) const; #if OSL_DEBUG_LEVEL > 1 // shows the ParseTree with tabs and linefeeds void showParseTree( OUString& rString ) const; void showParseTree( OUStringBuffer& _inout_rBuf, sal_uInt32 nLevel ) const; #endif SQLNodeType getNodeType() const {return m_eNodeType;}; // RuleId returns the RuleID of the node's rule (only if IsRule()) sal_uInt32 getRuleID() const {return m_nNodeID;} /** returns the ID of the rule represented by the node If the node does not represent a rule, UNKNOWN_RULE is returned */ Rule getKnownRuleID() const; // returns the TokenId of the node's token (only if !isRule()) sal_uInt32 getTokenID() const {return m_nNodeID;} // IsRule tests whether a node is a rule (NonTerminal) // ATTENTION: rules can be leaves, for example empty lists bool isRule() const { return (m_eNodeType == SQLNodeType::Rule) || (m_eNodeType == SQLNodeType::ListRule) || (m_eNodeType == SQLNodeType::CommaListRule);} // IsToken tests whether a Node is a Token (Terminal but not a rule) bool isToken() const {return !isRule();} const OUString& getTokenValue() const {return m_aNodeValue;} bool isLeaf() const {return m_aChildren.empty();} // negate only a searchcondition, any other rule could cause a gpf static void negateSearchCondition(OSQLParseNode*& pSearchCondition, bool bNegate=false); // normalize a logic form // e.q. (a or b) and (c or d) <=> a and c or a and d or b and c or b and d static void disjunctiveNormalForm(OSQLParseNode*& pSearchCondition); // Simplifies logic expressions // a and a = a // a or a = a // a and ( a + b) = a // a or a and b = a static void absorptions(OSQLParseNode*& pSearchCondition); // erase unnecessary braces static void eraseBraces(OSQLParseNode*& pSearchCondition); // makes the logic formula a little smaller static void compress(OSQLParseNode*& pSearchCondition); // return the catalog, schema and tablename from this node // _pTableNode must be a rule of that above or a SQL_TOKEN_NAME static bool getTableComponents(const OSQLParseNode* _pTableNode, css::uno::Any &_rCatalog, OUString &_rSchema, OUString &_rTable, const css::uno::Reference< css::sdbc::XDatabaseMetaData >& _xMetaData); // substitute all occurrences of :var or [name] into the dynamic parameter ? // _pNode will be modified if parameters exists static void substituteParameterNames(OSQLParseNode const * _pNode); /** return a table range when it exists. */ static OUString getTableRange(const OSQLParseNode* _pTableRef); protected: // ParseNodeToStr concatenates all Tokens (leaves) of the ParseNodes. void parseNodeToStr(OUString& rString, const css::uno::Reference< css::sdbc::XConnection >& _rxConnection, const css::uno::Reference< css::util::XNumberFormatter > & xFormatter, const css::uno::Reference< css::beans::XPropertySet > & _xField, const OUString &_sPredicateTableAlias, const css::lang::Locale& rIntl, const IParseContext* pContext, bool _bIntl, bool _bQuote, const OUString& _rDecSep, bool _bPredicate) const; private: void impl_parseNodeToString_throw( OUStringBuffer& rString, const SQLParseNodeParameter& rParam, bool bSimple=true ) const; void impl_parseLikeNodeToString_throw( OUStringBuffer& rString, const SQLParseNodeParameter& rParam, bool bSimple=true ) const; void impl_parseTableRangeNodeToString_throw( OUStringBuffer& rString, const SQLParseNodeParameter& rParam ) const; /** parses a table_name node into a SQL statement particle. @return if and only if parsing was successful, if default handling should be applied. */ bool impl_parseTableNameNodeToString_throw( OUStringBuffer& rString, const SQLParseNodeParameter& rParam ) const; bool addDateValue(OUStringBuffer& rString, const SQLParseNodeParameter& rParam) const; static OUString convertDateTimeString(const SQLParseNodeParameter& rParam, const OUString& rString); static OUString convertDateString(const SQLParseNodeParameter& rParam, std::u16string_view rString); static OUString convertTimeString(const SQLParseNodeParameter& rParam, std::u16string_view rString); void parseLeaf(OUStringBuffer& rString, const SQLParseNodeParameter& rParam) const; }; inline OSQLParseNode* OSQLParseNode::getChild(sal_uInt32 nPos) const { return m_aChildren[nPos].get(); } // utilities to query for a specific rule, token or punctuation #define SQL_ISRULE(pParseNode, eRule) ((pParseNode)->isRule() && (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::eRule)) #define SQL_ISRULEOR2(pParseNode, e1, e2) ((pParseNode)->isRule() && ( \ (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e1) || \ (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e2))) #define SQL_ISRULEOR3(pParseNode, e1, e2, e3) ((pParseNode)->isRule() && ( \ (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e1) || \ (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e2) || \ (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e3))) #define SQL_ISTOKEN(pParseNode, token) ((pParseNode)->isToken() && (pParseNode)->getTokenID() == SQL_TOKEN_##token) #define SQL_ISTOKENOR2(pParseNode, tok0, tok1) ((pParseNode)->isToken() && ( (pParseNode)->getTokenID() == SQL_TOKEN_##tok0 || (pParseNode)->getTokenID() == SQL_TOKEN_##tok1 )) #define SQL_ISTOKENOR3(pParseNode, tok0, tok1, tok2) ((pParseNode)->isToken() && ( (pParseNode)->getTokenID() == SQL_TOKEN_##tok0 || (pParseNode)->getTokenID() == SQL_TOKEN_##tok1 || (pParseNode)->getTokenID() == SQL_TOKEN_##tok2 )) #define SQL_ISPUNCTUATION(pParseNode, aString) ((pParseNode)->getNodeType() == SQLNodeType::Punctuation && (pParseNode)->getTokenValue() == (aString)) } #endif // INCLUDED_CONNECTIVITY_SQLNODE_HXX /* vim:set shiftwidth=4 softtabstop=4 expandtab: */