summaryrefslogtreecommitdiffstats
path: root/include/connectivity/sqlnode.hxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--include/connectivity/sqlnode.hxx455
1 files changed, 455 insertions, 0 deletions
diff --git a/include/connectivity/sqlnode.hxx b/include/connectivity/sqlnode.hxx
new file mode 100644
index 0000000000..6724090392
--- /dev/null
+++ b/include/connectivity/sqlnode.hxx
@@ -0,0 +1,455 @@
+/* -*- 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 <string_view>
+#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::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<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(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 <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,
+ 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,
+ 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, 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: */