| /************************************************************** |
| * |
| * 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 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| #ifndef _CONNECTIVITY_SQLNODE_HXX |
| #define _CONNECTIVITY_SQLNODE_HXX |
| |
| #include "connectivity/dbtoolsdllapi.hxx" |
| #include "connectivity/dbmetadata.hxx" |
| #include <com/sun/star/uno/Reference.hxx> |
| #include <com/sun/star/util/XNumberFormatTypes.hpp> |
| #include <com/sun/star/beans/XPropertySet.hpp> |
| #include <vector> |
| #include <functional> |
| #include <set> |
| #include <boost/shared_ptr.hpp> |
| #include <rtl/ustrbuf.hxx> |
| |
| // forward declarations |
| namespace com |
| { |
| namespace sun |
| { |
| namespace star |
| { |
| namespace beans |
| { |
| class XPropertySet; |
| } |
| namespace util |
| { |
| class XNumberFormatter; |
| } |
| namespace container |
| { |
| class XNameAccess; |
| } |
| } |
| } |
| } |
| |
| namespace rtl |
| { |
| class OUStringBuffer; |
| } |
| #define ORDER_BY_CHILD_POS 5 |
| #define TABLE_EXPRESSION_CHILD_COUNT 9 |
| |
| namespace connectivity |
| { |
| class OSQLParser; |
| class OSQLParseNode; |
| class IParseContext; |
| |
| typedef ::std::vector< OSQLParseNode* > OSQLParseNodes; |
| |
| enum SQLNodeType {SQL_NODE_RULE, SQL_NODE_LISTRULE, SQL_NODE_COMMALISTRULE, |
| SQL_NODE_KEYWORD, SQL_NODE_COMPARISON, SQL_NODE_NAME, |
| SQL_NODE_STRING, SQL_NODE_INTNUM, SQL_NODE_APPROXNUM, |
| SQL_NODE_EQUAL,SQL_NODE_LESS,SQL_NODE_GREAT,SQL_NODE_LESSEQ,SQL_NODE_GREATEQ,SQL_NODE_NOTEQUAL, |
| SQL_NODE_PUNCTUATION, SQL_NODE_AMMSC, SQL_NODE_ACCESS_DATE,SQL_NODE_DATE,SQL_NODE_CONCAT}; |
| |
| typedef ::std::set< ::rtl::OUString > QueryNameSet; |
| //================================================================== |
| //= SQLParseNodeParameter |
| //================================================================== |
| struct OOO_DLLPUBLIC_DBTOOLS SQLParseNodeParameter |
| { |
| const ::com::sun::star::lang::Locale& rLocale; |
| ::dbtools::DatabaseMetaData aMetaData; |
| OSQLParser* pParser; |
| ::boost::shared_ptr< QueryNameSet > pSubQueryHistory; |
| ::com::sun::star::uno::Reference< ::com::sun::star::util::XNumberFormatter > xFormatter; |
| ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet > xField; |
| ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameAccess > xQueries; // see bParseToSDBCLevel |
| const IParseContext& m_rContext; |
| sal_Char cDecSep; |
| 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 ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConnection, |
| const ::com::sun::star::uno::Reference< ::com::sun::star::util::XNumberFormatter >& _xFormatter, |
| const ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet >& _xField, |
| const ::com::sun::star::lang::Locale& _rLocale, |
| const IParseContext* _pContext, |
| bool _bIntl, |
| bool _bQuote, |
| sal_Char _cDecSep, |
| bool _bPredicate, |
| bool _bParseToSDBC |
| ); |
| ~SQLParseNodeParameter(); |
| }; |
| |
| //========================================================================== |
| //= OSQLParseNode |
| //========================================================================== |
| class OOO_DLLPUBLIC_DBTOOLS OSQLParseNode |
| { |
| friend class OSQLParser; |
| |
| OSQLParseNodes m_aChildren; |
| OSQLParseNode* m_pParent; // pParent fuer Reuckverkettung im Baum |
| ::rtl::OUString m_aNodeValue; // Token-Name oder leer bei Regeln oder ::rtl::OUString bei |
| // ::rtl::OUString, INT, usw. -Werten |
| SQLNodeType m_eNodeType; // s. o. |
| sal_uInt32 m_nNodeID; // ::com::sun::star::chaos::Rule ID (bei IsRule()) oder Token ID (bei !IsRule()) |
| // ::com::sun::star::chaos::Rule IDs und Token IDs koennen nicht anhand des Wertes |
| // unterschieden werden, dafuer ist IsRule() abzufragen! |
| public: |
| enum Rule |
| { |
| select_statement = 0, |
| 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_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, |
| predicate_check, |
| 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, |
| boolean_test, |
| 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, |
| 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, |
| cast_spec, |
| rule_count, // letzter_wert |
| UNKNOWN_RULE // ID indicating that a node is no rule with a matching Rule-enum value (see getKnownRuleID) |
| }; |
| |
| // must be ascii encoding for the value |
| OSQLParseNode(const sal_Char* _pValueStr, |
| SQLNodeType _eNodeType, |
| sal_uInt32 _nNodeID = 0); |
| |
| OSQLParseNode(const ::rtl::OString& _rValue, |
| SQLNodeType eNewNodeType, |
| sal_uInt32 nNewNodeID=0); |
| |
| OSQLParseNode(const sal_Unicode* _pValue, |
| SQLNodeType _eNodeType, |
| sal_uInt32 _nNodeID = 0); |
| |
| OSQLParseNode(const ::rtl::OUString& _rValue, |
| SQLNodeType _eNodeType, |
| sal_uInt32 _nNodeID = 0); |
| |
| // Kopiert den entsprechenden ParseNode |
| OSQLParseNode(const OSQLParseNode& rParseNode); |
| OSQLParseNode& operator=(const OSQLParseNode& rParseNode); |
| |
| sal_Bool operator==(OSQLParseNode& rParseNode) const; |
| |
| // Destruktor raeumt rekursiv den Baum ab |
| virtual ~OSQLParseNode(); |
| |
| // Parent gibt den Zeiger auf den Parent zurueck |
| OSQLParseNode* getParent() const {return m_pParent;}; |
| |
| // SetParent setzt den Parent-Zeiger eines ParseNodes |
| void setParent(OSQLParseNode* pParseNode) {m_pParent = pParseNode;}; |
| |
| // ChildCount liefert die Anzahl der Kinder eines Knotens |
| sal_uInt32 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* replaceAt(sal_uInt32 nPos, OSQLParseNode* pNewSubTree); |
| OSQLParseNode* replace(OSQLParseNode* pOldSubTree, OSQLParseNode* pNewSubTree); |
| |
| OSQLParseNode* removeAt(sal_uInt32 nPos); |
| OSQLParseNode* remove(OSQLParseNode* pSubTree); |
| |
| void replaceNodeValue(const ::rtl::OUString& rTableAlias,const ::rtl::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 subsituted, 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 occured while generating the statement, if any. Might be <NULL/>, |
| in this case the error is not reported back, and can only be recognized by examing 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>bar </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( ::rtl::OUString& _out_rString, |
| const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConnection, |
| OSQLParser& _rParser, |
| ::com::sun::star::sdbc::SQLException* _pErrorHolder ) const; |
| |
| void parseNodeToStr(::rtl::OUString& rString, |
| const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConnection, |
| const IParseContext* pContext = NULL, |
| sal_Bool _bIntl = sal_False, |
| sal_Bool _bQuote= sal_True) const; |
| |
| // quoted und internationalisert |
| void parseNodeToPredicateStr(::rtl::OUString& rString, |
| const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConnection, |
| const ::com::sun::star::uno::Reference< ::com::sun::star::util::XNumberFormatter > & xFormatter, |
| const ::com::sun::star::lang::Locale& rIntl, |
| sal_Char _cDec, |
| const IParseContext* pContext = NULL ) const; |
| |
| void parseNodeToPredicateStr(::rtl::OUString& rString, |
| const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConnection, |
| const ::com::sun::star::uno::Reference< ::com::sun::star::util::XNumberFormatter > & xFormatter, |
| const ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet > & _xField, |
| const ::com::sun::star::lang::Locale& rIntl, |
| sal_Char _cDec, |
| const IParseContext* pContext = NULL ) const; |
| |
| OSQLParseNode* getByRule(OSQLParseNode::Rule eRule) const; |
| |
| #if OSL_DEBUG_LEVEL > 0 |
| // zeigt den ParseTree mit tabs und linefeeds |
| void showParseTree( ::rtl::OUString& rString ) const; |
| void showParseTree( ::rtl::OUStringBuffer& _inout_rBuf, sal_uInt32 nLevel ) const; |
| #endif |
| |
| // GetNodeType gibt den Knotentyp zurueck |
| SQLNodeType getNodeType() const {return m_eNodeType;}; |
| |
| // RuleId liefert die RuleId der Regel des Knotens (nur bei 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; |
| |
| // RuleId liefert die TokenId des Tokens des Knotens (nur bei ! IsRule()) |
| sal_uInt32 getTokenID() const {return m_nNodeID;} |
| |
| // IsRule testet ob ein Node eine Regel (NonTerminal) ist |
| // Achtung : Regeln koenne auch Blaetter sein, z.B. leere Listen |
| sal_Bool isRule() const |
| { return (m_eNodeType == SQL_NODE_RULE) || (m_eNodeType == SQL_NODE_LISTRULE) |
| || (m_eNodeType == SQL_NODE_COMMALISTRULE);} |
| |
| // IsToken testet ob ein Node ein Token (Terminal) ist |
| sal_Bool isToken() const {return !isRule();} // ein Token ist keine Regel |
| |
| // TokenValue liefert den NodeValue eines Tokens |
| const ::rtl::OUString& getTokenValue() const {return m_aNodeValue;} |
| |
| // SetTokenValue setzt den NodeValue |
| void setTokenValue(const ::rtl::OUString& rString) { if (isToken()) m_aNodeValue = rString;} |
| |
| // IsLeaf testet ob ein Node ein Blatt ist |
| sal_Bool isLeaf() const {return m_aChildren.empty();} |
| |
| // negate only a searchcondition, any other rule could cause a gpf |
| static void negateSearchCondition(OSQLParseNode*& pSearchCondition,sal_Bool bNegate=sal_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); |
| |
| // Simplies logic expressions |
| // a * a = a |
| // a + a = a |
| // a * ( a + b) = a |
| // a + a * b = a |
| static void absorptions(OSQLParseNode*& pSearchCondition); |
| |
| // erase not nessary braces |
| static void eraseBraces(OSQLParseNode*& pSearchCondition); |
| |
| // makes the logic formula a little more smaller |
| static void compress(OSQLParseNode*& pSearchCondition); |
| // return the catalog, schema and tablename form this node |
| // _pTableNode must be a rule of that above or a SQL_TOKEN_NAME |
| static sal_Bool getTableComponents(const OSQLParseNode* _pTableNode, |
| ::com::sun::star::uno::Any &_rCatalog, |
| ::rtl::OUString &_rSchema, |
| ::rtl::OUString &_rTable |
| ,const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XDatabaseMetaData >& _xMetaData); |
| |
| // susbtitute all occurences of :var or [name] into the dynamic parameter ? |
| // _pNode will be modified if parameters exists |
| static void substituteParameterNames(OSQLParseNode* _pNode); |
| |
| /** return a table range when it exists. |
| */ |
| static ::rtl::OUString getTableRange(const OSQLParseNode* _pTableRef); |
| |
| protected: |
| // ParseNodeToStr konkateniert alle Token (Blaetter) des ParseNodes |
| void parseNodeToStr(::rtl::OUString& rString, |
| const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConnection, |
| const ::com::sun::star::uno::Reference< ::com::sun::star::util::XNumberFormatter > & xFormatter, |
| const ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet > & _xField, |
| const ::com::sun::star::lang::Locale& rIntl, |
| const IParseContext* pContext, |
| bool _bIntl, |
| bool _bQuote, |
| sal_Char _cDecSep, |
| bool _bPredicate, |
| bool _bSubstitute) const; |
| |
| private: |
| void impl_parseNodeToString_throw( ::rtl::OUStringBuffer& rString, const SQLParseNodeParameter& rParam) const; |
| void impl_parseLikeNodeToString_throw( ::rtl::OUStringBuffer& rString, const SQLParseNodeParameter& rParam ) const; |
| void impl_parseTableRangeNodeToString_throw( ::rtl::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( ::rtl::OUStringBuffer& rString, const SQLParseNodeParameter& rParam ) const; |
| |
| sal_Bool addDateValue(::rtl::OUStringBuffer& rString, const SQLParseNodeParameter& rParam) const; |
| ::rtl::OUString convertDateTimeString(const SQLParseNodeParameter& rParam, const ::rtl::OUString& rString) const; |
| ::rtl::OUString convertDateString(const SQLParseNodeParameter& rParam, const ::rtl::OUString& rString) const; |
| ::rtl::OUString convertTimeString(const SQLParseNodeParameter& rParam, const ::rtl::OUString& rString) const; |
| void parseLeaf(::rtl::OUStringBuffer& rString, const SQLParseNodeParameter& rParam) const; |
| }; |
| |
| //----------------------------------------------------------------------------- |
| inline OSQLParseNode* OSQLParseNode::getChild(sal_uInt32 nPos) const |
| { |
| OSL_ENSURE(nPos < m_aChildren.size(), "Invalid Position"); |
| |
| // return m_aChildren[nPos]; |
| return m_aChildren.at(nPos); |
| } |
| |
| // Utility-Methoden zum Abfragen auf bestimmte Rules, Token oder Punctuation: |
| #define SQL_ISRULE(pParseNode, eRule) ((pParseNode)->isRule() && (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::eRule)) |
| #define SQL_ISTOKEN(pParseNode, token) ((pParseNode)->isToken() && (pParseNode)->getTokenID() == SQL_TOKEN_##token) |
| #define SQL_ISPUNCTUATION(pParseNode, aString) ((pParseNode)->getNodeType() == SQL_NODE_PUNCTUATION && !(pParseNode)->getTokenValue().compareToAscii(aString)) |
| } |
| |
| #endif //_CONNECTIVITY_SQLNODE_HXX |