1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
19 #ifndef INCLUDED_CONNECTIVITY_SQLNODE_HXX
20 #define INCLUDED_CONNECTIVITY_SQLNODE_HXX
22 #include <connectivity/dbtoolsdllapi.hxx>
23 #include <connectivity/dbmetadata.hxx>
24 #include <com/sun/star/uno/Reference.hxx>
27 #include <string_view>
29 #include <rtl/ustrbuf.hxx>
31 namespace com::sun::star::lang
{ struct Locale
; }
32 namespace com::sun::star::sdbc
{ class SQLException
; }
33 namespace com::sun::star::sdbc
{ class XDatabaseMetaData
; }
35 namespace com::sun::star
43 class XNumberFormatter
;
51 #define ORDER_BY_CHILD_POS 5
52 #define TABLE_EXPRESSION_CHILD_COUNT 9
54 namespace connectivity
59 enum class SQLNodeType
{ Rule
, ListRule
, CommaListRule
,
61 String
, IntNum
, ApproxNum
,
62 Equal
, Less
, Great
, LessEq
, GreatEq
, NotEqual
,
63 Punctuation
, AccessDate
, Concat
};
65 typedef ::std::set
< OUString
> QueryNameSet
;
67 //= SQLParseNodeParameter
69 struct SQLParseNodeParameter
71 const css::lang::Locale
& rLocale
;
72 ::dbtools::DatabaseMetaData aMetaData
;
74 std::shared_ptr
< QueryNameSet
> pSubQueryHistory
;
75 css::uno::Reference
< css::util::XNumberFormatter
> xFormatter
;
76 css::uno::Reference
< css::beans::XPropertySet
> xField
;
77 OUString sPredicateTableAlias
;
78 css::uno::Reference
< css::container::XNameAccess
> xQueries
; // see bParseToSDBCLevel
79 const IParseContext
& m_rContext
;
81 bool bQuote
: 1; /// should we quote identifiers?
82 bool bInternational
: 1; /// should we internationalize keywords and placeholders?
83 bool bPredicate
: 1; /// are we going to parse a mere predicate?
84 bool bParseToSDBCLevel
: 1; /// should we create an SDBC-level statement (e.g. with substituted sub queries)?
86 SQLParseNodeParameter(
87 const css::uno::Reference
< css::sdbc::XConnection
>& _rxConnection
,
88 const css::uno::Reference
< css::util::XNumberFormatter
>& _xFormatter
,
89 const css::uno::Reference
< css::beans::XPropertySet
>& _xField
,
90 const OUString
&_sPredicateTableAlias
,
91 const css::lang::Locale
& _rLocale
,
92 const IParseContext
* _pContext
,
103 class OOO_DLLPUBLIC_DBTOOLS OSQLParseNode
105 friend class OSQLParser
;
107 std::vector
< std::unique_ptr
<OSQLParseNode
> >
109 OSQLParseNode
* m_pParent
; // pParent for reverse linkage in the tree
110 OUString m_aNodeValue
; // token name, or empty in case of rules,
111 // or OUString in case of
112 // OUString, INT, etc.
113 SQLNodeType m_eNodeType
; // see above
114 sal_uInt32 m_nNodeID
; // Rule ID (if IsRule())
115 // or Token ID (if !IsRule())
116 // Rule IDs and Token IDs can't
117 // be distinguished by their values,
118 // IsRule has to be used for that!
122 UNKNOWN_RULE
= 0, // ID indicating that a node is no rule with a matching Rule-enum value (see getKnownRuleID)
123 // we make sure it is 0 so that it is the default-constructor value of this enum
124 // and std::map<foo,Rule>::operator[](bar) default-inserts UNKNOWN_RULE rather than select_statement (!)
132 opt_column_commalist
,
134 column_ref_commalist
,
137 ordering_spec_commalist
,
144 comparison_predicate
,
149 scalar_exp_commalist
,
156 delete_statement_positioned
,
157 delete_statement_searched
,
158 update_statement_positioned
,
159 update_statement_searched
,
160 assignment_commalist
,
162 values_or_query_spec
,
164 insert_atom_commalist
,
185 all_or_any_predicate
,
191 manipulative_statement
,
206 base_table_element_commalist
,
213 table_primary_as_range_column
,
218 comparison_predicate_part_2
,
219 parenthesized_boolean_value_expression
,
220 character_string_type
,
221 other_like_predicate_part_2
,
222 between_predicate_part_2
,
223 null_predicate_part_2
,
226 rule_count
// last value
229 // must be ascii encoding for the value
230 OSQLParseNode(const char* _pValueStr
,
231 SQLNodeType _eNodeType
,
232 sal_uInt32 _nNodeID
= 0);
234 OSQLParseNode(std::string_view _rValue
,
235 SQLNodeType eNewNodeType
,
236 sal_uInt32 nNewNodeID
=0);
238 OSQLParseNode(const OUString
& _rValue
,
239 SQLNodeType _eNodeType
,
240 sal_uInt32 _nNodeID
= 0);
242 // copies the respective ParseNode
243 OSQLParseNode(const OSQLParseNode
& rParseNode
);
244 OSQLParseNode
& operator=(const OSQLParseNode
& rParseNode
);
246 bool operator==(OSQLParseNode
const & rParseNode
) const;
248 // destructor destructs the tree recursively
249 virtual ~OSQLParseNode();
251 OSQLParseNode
* getParent() const {return m_pParent
;};
253 void setParent(OSQLParseNode
* pParseNode
) {m_pParent
= pParseNode
;};
255 size_t count() const {return m_aChildren
.size();};
256 inline OSQLParseNode
* getChild(sal_uInt32 nPos
) const;
258 void append(OSQLParseNode
* pNewSubTree
);
259 void insert(sal_uInt32 nPos
, OSQLParseNode
* pNewSubTree
);
261 OSQLParseNode
* replace(OSQLParseNode
* pOldSubTree
, OSQLParseNode
* pNewSubTree
);
263 OSQLParseNode
* removeAt(sal_uInt32 nPos
);
265 void replaceNodeValue(const OUString
& rTableAlias
,const OUString
& rColumnName
);
267 /** parses the node to a string which can be passed to a driver's connection for execution
269 Any particles of the parse tree which represent application-level features - such
270 as queries appearing in the FROM part - are substituted, so that the resulting statement can
271 be executed at an SDBC-level connection.
274 is an output parameter taking the resulting SQL statement
277 the connection relative to which to parse. This must be an SDB-level connection (e.g.
278 support the XQueriesSupplier interface) for the method to be able to do all necessary
282 the SQLParser used to create the node. This is needed in case we need to parse
283 sub queries which are present in the SQL statement - those sub queries need to be parsed,
284 too, to check whether they contain nested sub queries.
287 takes the error which occurred while generating the statement, if any. Might be <NULL/>,
288 in this case the error is not reported back, and can only be recognized by examining the
292 <TRUE/> if and only if the parsing was successful.<br/>
294 Currently, there's only one condition how this method can fail: If it contains a nested
295 query which causes a cycle. E.g., consider a statement <code>SELECT * from "foo"</code>,
296 where <code>foo</code> is a query defined as <code>SELECT * FROM "bar"</code>, where
297 <code>bar</code> is defined as <code>SELECT * FROM "foo"</code>. This statement obviously
298 cannot be parsed to an executable statement.
300 If this method returns <FALSE/>, you're encouraged to check and handle the error in
301 <arg>_pErrorHolder</arg>.
303 bool parseNodeToExecutableStatement( OUString
& _out_rString
,
304 const css::uno::Reference
< css::sdbc::XConnection
>& _rxConnection
,
305 OSQLParser
& _rParser
,
306 css::sdbc::SQLException
* _pErrorHolder
) const;
308 void parseNodeToStr(OUString
& rString
,
309 const css::uno::Reference
< css::sdbc::XConnection
>& _rxConnection
,
310 const IParseContext
* pContext
= nullptr,
312 bool _bQuote
= true) const;
314 // quoted and internationalised
315 void parseNodeToPredicateStr(OUString
& rString
,
316 const css::uno::Reference
< css::sdbc::XConnection
>& _rxConnection
,
317 const css::uno::Reference
< css::util::XNumberFormatter
> & xFormatter
,
318 const css::lang::Locale
& rIntl
,
320 const IParseContext
* pContext
= nullptr ) const;
322 void parseNodeToPredicateStr(OUString
& rString
,
323 const css::uno::Reference
< css::sdbc::XConnection
>& _rxConnection
,
324 const css::uno::Reference
< css::util::XNumberFormatter
> & xFormatter
,
325 const css::uno::Reference
< css::beans::XPropertySet
> & _xField
,
326 const OUString
&_sTableAlias
,
327 const css::lang::Locale
& rIntl
,
329 const IParseContext
* pContext
= nullptr ) const;
331 OSQLParseNode
* getByRule(OSQLParseNode::Rule eRule
) const;
333 #if OSL_DEBUG_LEVEL > 1
334 // shows the ParseTree with tabs and linefeeds
335 void showParseTree( OUString
& rString
) const;
336 void showParseTree( OUStringBuffer
& _inout_rBuf
, sal_uInt32 nLevel
) const;
339 SQLNodeType
getNodeType() const {return m_eNodeType
;};
341 // RuleId returns the RuleID of the node's rule (only if IsRule())
342 sal_uInt32
getRuleID() const {return m_nNodeID
;}
344 /** returns the ID of the rule represented by the node
345 If the node does not represent a rule, UNKNOWN_RULE is returned
347 Rule
getKnownRuleID() const;
349 // returns the TokenId of the node's token (only if !isRule())
350 sal_uInt32
getTokenID() const {return m_nNodeID
;}
352 // IsRule tests whether a node is a rule (NonTerminal)
353 // ATTENTION: rules can be leaves, for example empty lists
355 { return (m_eNodeType
== SQLNodeType::Rule
) || (m_eNodeType
== SQLNodeType::ListRule
)
356 || (m_eNodeType
== SQLNodeType::CommaListRule
);}
358 // IsToken tests whether a Node is a Token (Terminal but not a rule)
359 bool isToken() const {return !isRule();}
361 const OUString
& getTokenValue() const {return m_aNodeValue
;}
363 bool isLeaf() const {return m_aChildren
.empty();}
365 // negate only a searchcondition, any other rule could cause a gpf
366 static void negateSearchCondition(OSQLParseNode
*& pSearchCondition
, bool bNegate
=false);
368 // normalize a logic form
369 // e.q. (a or b) and (c or d) <=> a and c or a and d or b and c or b and d
370 static void disjunctiveNormalForm(OSQLParseNode
*& pSearchCondition
);
372 // Simplifies logic expressions
375 // a and ( a + b) = a
377 static void absorptions(OSQLParseNode
*& pSearchCondition
);
379 // erase unnecessary braces
380 static void eraseBraces(OSQLParseNode
*& pSearchCondition
);
382 // makes the logic formula a little smaller
383 static void compress(OSQLParseNode
*& pSearchCondition
);
384 // return the catalog, schema and tablename from this node
385 // _pTableNode must be a rule of that above or a SQL_TOKEN_NAME
386 static bool getTableComponents(const OSQLParseNode
* _pTableNode
,
387 css::uno::Any
&_rCatalog
,
390 const css::uno::Reference
< css::sdbc::XDatabaseMetaData
>& _xMetaData
);
392 // substitute all occurrences of :var or [name] into the dynamic parameter ?
393 // _pNode will be modified if parameters exists
394 static void substituteParameterNames(OSQLParseNode
const * _pNode
);
396 /** return a table range when it exists.
398 static OUString
getTableRange(const OSQLParseNode
* _pTableRef
);
401 // ParseNodeToStr concatenates all Tokens (leaves) of the ParseNodes.
402 void parseNodeToStr(OUString
& rString
,
403 const css::uno::Reference
< css::sdbc::XConnection
>& _rxConnection
,
404 const css::uno::Reference
< css::util::XNumberFormatter
> & xFormatter
,
405 const css::uno::Reference
< css::beans::XPropertySet
> & _xField
,
406 const OUString
&_sPredicateTableAlias
,
407 const css::lang::Locale
& rIntl
,
408 const IParseContext
* pContext
,
412 bool _bPredicate
) const;
415 void impl_parseNodeToString_throw( OUStringBuffer
& rString
, const SQLParseNodeParameter
& rParam
, bool bSimple
=true ) const;
416 void impl_parseLikeNodeToString_throw( OUStringBuffer
& rString
, const SQLParseNodeParameter
& rParam
, bool bSimple
=true ) const;
417 void impl_parseTableRangeNodeToString_throw( OUStringBuffer
& rString
, const SQLParseNodeParameter
& rParam
) const;
419 /** parses a table_name node into a SQL statement particle.
421 <TRUE/> if and only if parsing was successful, <FALSE/> if default handling should
424 bool impl_parseTableNameNodeToString_throw( OUStringBuffer
& rString
, const SQLParseNodeParameter
& rParam
) const;
426 bool addDateValue(OUStringBuffer
& rString
, const SQLParseNodeParameter
& rParam
) const;
427 static OUString
convertDateTimeString(const SQLParseNodeParameter
& rParam
, const OUString
& rString
);
428 static OUString
convertDateString(const SQLParseNodeParameter
& rParam
, const OUString
& rString
);
429 static OUString
convertTimeString(const SQLParseNodeParameter
& rParam
, const OUString
& rString
);
430 void parseLeaf(OUStringBuffer
& rString
, const SQLParseNodeParameter
& rParam
) const;
433 inline OSQLParseNode
* OSQLParseNode::getChild(sal_uInt32 nPos
) const
435 return m_aChildren
[nPos
].get();
438 // utilities to query for a specific rule, token or punctuation
439 #define SQL_ISRULE(pParseNode, eRule) ((pParseNode)->isRule() && (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::eRule))
440 #define SQL_ISRULEOR2(pParseNode, e1, e2) ((pParseNode)->isRule() && ( \
441 (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e1) || \
442 (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e2)))
443 #define SQL_ISRULEOR3(pParseNode, e1, e2, e3) ((pParseNode)->isRule() && ( \
444 (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e1) || \
445 (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e2) || \
446 (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e3)))
447 #define SQL_ISTOKEN(pParseNode, token) ((pParseNode)->isToken() && (pParseNode)->getTokenID() == SQL_TOKEN_##token)
448 #define SQL_ISTOKENOR2(pParseNode, tok0, tok1) ((pParseNode)->isToken() && ( (pParseNode)->getTokenID() == SQL_TOKEN_##tok0 || (pParseNode)->getTokenID() == SQL_TOKEN_##tok1 ))
449 #define SQL_ISTOKENOR3(pParseNode, tok0, tok1, tok2) ((pParseNode)->isToken() && ( (pParseNode)->getTokenID() == SQL_TOKEN_##tok0 || (pParseNode)->getTokenID() == SQL_TOKEN_##tok1 || (pParseNode)->getTokenID() == SQL_TOKEN_##tok2 ))
450 #define SQL_ISPUNCTUATION(pParseNode, aString) ((pParseNode)->getNodeType() == SQLNodeType::Punctuation && (pParseNode)->getTokenValue() == (aString))
453 #endif // INCLUDED_CONNECTIVITY_SQLNODE_HXX
455 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */