diff options
Diffstat (limited to 'include/connectivity/sqlnode.hxx')
-rw-r--r-- | include/connectivity/sqlnode.hxx | 460 |
1 files changed, 460 insertions, 0 deletions
diff --git a/include/connectivity/sqlnode.hxx b/include/connectivity/sqlnode.hxx new file mode 100644 index 000000000..450040596 --- /dev/null +++ b/include/connectivity/sqlnode.hxx @@ -0,0 +1,460 @@ +/* -*- 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 <connectivity/dbtoolsdllapi.hxx> +#include <connectivity/dbmetadata.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <memory> +#include <set> +#include <vector> +#include <rtl/ustrbuf.hxx> + +namespace com::sun::star::lang { struct Locale; } +namespace com::sun::star::sdbc { class SQLException; } +namespace com::sun::star::sdbc { class XDatabaseMetaData; } + +namespace com +{ + namespace sun + { + namespace 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, + const 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<OSQLParseNode> > + 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<foo,Rule>::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(const OString& _rValue, + SQLNodeType eNewNodeType, + sal_uInt32 nNewNodeID=0); + + OSQLParseNode(const OUString& _rValue, + 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); + + OSQLParseNode* replace(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 <NULL/>, + in this case the error is not reported back, and can only be recognized by examining the + return value. + + @return + <TRUE/> if and only if the parsing was successful.<br/> + + 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 <code>SELECT * from "foo"</code>, + where <code>foo</code> is a query defined as <code>SELECT * FROM "bar"</code>, where + <code>bar</code> is defined as <code>SELECT * FROM "foo"</code>. This statement obviously + cannot be parsed to an executable statement. + + If this method returns <FALSE/>, you're encouraged to check and handle the error in + <arg>_pErrorHolder</arg>. + */ + 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, + OUString _sDec, + 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, + OUString strDec, + 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, + OUString _sDecSep, + 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 + <TRUE/> if and only if parsing was successful, <FALSE/> 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, const OUString& rString); + static OUString convertTimeString(const SQLParseNodeParameter& rParam, const OUString& 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: */ |