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>
28 #include <rtl/ustrbuf.hxx>
30 namespace com::sun::star::lang
{ struct Locale
; }
31 namespace com::sun::star::sdbc
{ class SQLException
; }
32 namespace com::sun::star::sdbc
{ class XDatabaseMetaData
; }
46 class XNumberFormatter
;
56 #define ORDER_BY_CHILD_POS 5
57 #define TABLE_EXPRESSION_CHILD_COUNT 9
59 namespace connectivity
64 enum class SQLNodeType
{ Rule
, ListRule
, CommaListRule
,
66 String
, IntNum
, ApproxNum
,
67 Equal
, Less
, Great
, LessEq
, GreatEq
, NotEqual
,
68 Punctuation
, AccessDate
, Concat
};
70 typedef ::std::set
< OUString
> QueryNameSet
;
72 //= SQLParseNodeParameter
74 struct OOO_DLLPUBLIC_DBTOOLS SQLParseNodeParameter
76 const css::lang::Locale
& rLocale
;
77 ::dbtools::DatabaseMetaData aMetaData
;
79 std::shared_ptr
< QueryNameSet
> pSubQueryHistory
;
80 css::uno::Reference
< css::util::XNumberFormatter
> xFormatter
;
81 css::uno::Reference
< css::beans::XPropertySet
> xField
;
82 OUString sPredicateTableAlias
;
83 css::uno::Reference
< css::container::XNameAccess
> xQueries
; // see bParseToSDBCLevel
84 const IParseContext
& m_rContext
;
86 bool bQuote
: 1; /// should we quote identifiers?
87 bool bInternational
: 1; /// should we internationalize keywords and placeholders?
88 bool bPredicate
: 1; /// are we going to parse a mere predicate?
89 bool bParseToSDBCLevel
: 1; /// should we create an SDBC-level statement (e.g. with substituted sub queries)?
91 SQLParseNodeParameter(
92 const css::uno::Reference
< css::sdbc::XConnection
>& _rxConnection
,
93 const css::uno::Reference
< css::util::XNumberFormatter
>& _xFormatter
,
94 const css::uno::Reference
< css::beans::XPropertySet
>& _xField
,
95 const OUString
&_sPredicateTableAlias
,
96 const css::lang::Locale
& _rLocale
,
97 const IParseContext
* _pContext
,
108 class OOO_DLLPUBLIC_DBTOOLS OSQLParseNode
110 friend class OSQLParser
;
112 std::vector
< std::unique_ptr
<OSQLParseNode
> >
114 OSQLParseNode
* m_pParent
; // pParent for reverse linkage in the tree
115 OUString m_aNodeValue
; // token name, or empty in case of rules,
116 // or OUString in case of
117 // OUString, INT, etc.
118 SQLNodeType m_eNodeType
; // see above
119 sal_uInt32 m_nNodeID
; // Rule ID (if IsRule())
120 // or Token ID (if !IsRule())
121 // Rule IDs and Token IDs can't
122 // be distinguished by their values,
123 // IsRule has to be used for that!
127 UNKNOWN_RULE
= 0, // ID indicating that a node is no rule with a matching Rule-enum value (see getKnownRuleID)
128 // we make sure it is 0 so that it is the default-constructor value of this enum
129 // and std::map<foo,Rule>::operator[](bar) default-inserts UNKNOWN_RULE rather than select_statement (!)
137 opt_column_commalist
,
139 column_ref_commalist
,
142 ordering_spec_commalist
,
149 comparison_predicate
,
154 scalar_exp_commalist
,
161 delete_statement_positioned
,
162 delete_statement_searched
,
163 update_statement_positioned
,
164 update_statement_searched
,
165 assignment_commalist
,
167 values_or_query_spec
,
169 insert_atom_commalist
,
190 all_or_any_predicate
,
196 manipulative_statement
,
211 base_table_element_commalist
,
218 table_primary_as_range_column
,
223 comparison_predicate_part_2
,
224 parenthesized_boolean_value_expression
,
225 character_string_type
,
226 other_like_predicate_part_2
,
227 between_predicate_part_2
,
228 null_predicate_part_2
,
231 rule_count
// last value
234 // must be ascii encoding for the value
235 OSQLParseNode(const sal_Char
* _pValueStr
,
236 SQLNodeType _eNodeType
,
237 sal_uInt32 _nNodeID
= 0);
239 OSQLParseNode(const OString
& _rValue
,
240 SQLNodeType eNewNodeType
,
241 sal_uInt32 nNewNodeID
=0);
243 OSQLParseNode(const OUString
& _rValue
,
244 SQLNodeType _eNodeType
,
245 sal_uInt32 _nNodeID
= 0);
247 // copies the respective ParseNode
248 OSQLParseNode(const OSQLParseNode
& rParseNode
);
249 OSQLParseNode
& operator=(const OSQLParseNode
& rParseNode
);
251 bool operator==(OSQLParseNode
const & rParseNode
) const;
253 // destructor destructs the tree recursively
254 virtual ~OSQLParseNode();
256 OSQLParseNode
* getParent() const {return m_pParent
;};
258 void setParent(OSQLParseNode
* pParseNode
) {m_pParent
= pParseNode
;};
260 size_t count() const {return m_aChildren
.size();};
261 inline OSQLParseNode
* getChild(sal_uInt32 nPos
) const;
263 void append(OSQLParseNode
* pNewSubTree
);
264 void insert(sal_uInt32 nPos
, OSQLParseNode
* pNewSubTree
);
266 OSQLParseNode
* replace(OSQLParseNode
* pOldSubTree
, OSQLParseNode
* pNewSubTree
);
268 OSQLParseNode
* removeAt(sal_uInt32 nPos
);
270 void replaceNodeValue(const OUString
& rTableAlias
,const OUString
& rColumnName
);
272 /** parses the node to a string which can be passed to a driver's connection for execution
274 Any particles of the parse tree which represent application-level features - such
275 as queries appearing in the FROM part - are substituted, so that the resulting statement can
276 be executed at an SDBC-level connection.
279 is an output parameter taking the resulting SQL statement
282 the connection relative to which to parse. This must be an SDB-level connection (e.g.
283 support the XQueriesSupplier interface) for the method to be able to do all necessary
287 the SQLParser used to create the node. This is needed in case we need to parse
288 sub queries which are present in the SQL statement - those sub queries need to be parsed,
289 too, to check whether they contain nested sub queries.
292 takes the error which occurred while generating the statement, if any. Might be <NULL/>,
293 in this case the error is not reported back, and can only be recognized by examining the
297 <TRUE/> if and only if the parsing was successful.<br/>
299 Currently, there's only one condition how this method can fail: If it contains a nested
300 query which causes a cycle. E.g., consider a statement <code>SELECT * from "foo"</code>,
301 where <code>foo</code> is a query defined as <code>SELECT * FROM "bar"</code>, where
302 <code>bar</code> is defined as <code>SELECT * FROM "foo"</code>. This statement obviously
303 cannot be parsed to an executable statement.
305 If this method returns <FALSE/>, you're encouraged to check and handle the error in
306 <arg>_pErrorHolder</arg>.
308 bool parseNodeToExecutableStatement( OUString
& _out_rString
,
309 const css::uno::Reference
< css::sdbc::XConnection
>& _rxConnection
,
310 OSQLParser
& _rParser
,
311 css::sdbc::SQLException
* _pErrorHolder
) const;
313 void parseNodeToStr(OUString
& rString
,
314 const css::uno::Reference
< css::sdbc::XConnection
>& _rxConnection
,
315 const IParseContext
* pContext
= nullptr,
317 bool _bQuote
= true) const;
319 // quoted and internationalised
320 void parseNodeToPredicateStr(OUString
& rString
,
321 const css::uno::Reference
< css::sdbc::XConnection
>& _rxConnection
,
322 const css::uno::Reference
< css::util::XNumberFormatter
> & xFormatter
,
323 const css::lang::Locale
& rIntl
,
325 const IParseContext
* pContext
= nullptr ) const;
327 void parseNodeToPredicateStr(OUString
& rString
,
328 const css::uno::Reference
< css::sdbc::XConnection
>& _rxConnection
,
329 const css::uno::Reference
< css::util::XNumberFormatter
> & xFormatter
,
330 const css::uno::Reference
< css::beans::XPropertySet
> & _xField
,
331 const OUString
&_sTableAlias
,
332 const css::lang::Locale
& rIntl
,
334 const IParseContext
* pContext
= nullptr ) const;
336 OSQLParseNode
* getByRule(OSQLParseNode::Rule eRule
) const;
338 #if OSL_DEBUG_LEVEL > 1
339 // shows the ParseTree with tabs and linefeeds
340 void showParseTree( OUString
& rString
) const;
341 void showParseTree( OUStringBuffer
& _inout_rBuf
, sal_uInt32 nLevel
) const;
344 SQLNodeType
getNodeType() const {return m_eNodeType
;};
346 // RuleId returns the RuleID of the node's rule (only if IsRule())
347 sal_uInt32
getRuleID() const {return m_nNodeID
;}
349 /** returns the ID of the rule represented by the node
350 If the node does not represent a rule, UNKNOWN_RULE is returned
352 Rule
getKnownRuleID() const;
354 // returns the TokenId of the node's token (only if !isRule())
355 sal_uInt32
getTokenID() const {return m_nNodeID
;}
357 // IsRule tests whether a node is a rule (NonTerminal)
358 // ATTENTION: rules can be leaves, for example empty lists
360 { return (m_eNodeType
== SQLNodeType::Rule
) || (m_eNodeType
== SQLNodeType::ListRule
)
361 || (m_eNodeType
== SQLNodeType::CommaListRule
);}
363 // IsToken tests whether a Node is a Token (Terminal but not a rule)
364 bool isToken() const {return !isRule();}
366 const OUString
& getTokenValue() const {return m_aNodeValue
;}
368 bool isLeaf() const {return m_aChildren
.empty();}
370 // negate only a searchcondition, any other rule could cause a gpf
371 static void negateSearchCondition(OSQLParseNode
*& pSearchCondition
, bool bNegate
=false);
373 // normalize a logic form
374 // e.q. (a or b) and (c or d) <=> a and c or a and d or b and c or b and d
375 static void disjunctiveNormalForm(OSQLParseNode
*& pSearchCondition
);
377 // Simplifies logic expressions
380 // a and ( a + b) = a
382 static void absorptions(OSQLParseNode
*& pSearchCondition
);
384 // erase unnecessary braces
385 static void eraseBraces(OSQLParseNode
*& pSearchCondition
);
387 // makes the logic formula a little smaller
388 static void compress(OSQLParseNode
*& pSearchCondition
);
389 // return the catalog, schema and tablename from this node
390 // _pTableNode must be a rule of that above or a SQL_TOKEN_NAME
391 static bool getTableComponents(const OSQLParseNode
* _pTableNode
,
392 css::uno::Any
&_rCatalog
,
395 const css::uno::Reference
< css::sdbc::XDatabaseMetaData
>& _xMetaData
);
397 // substitute all occurrences of :var or [name] into the dynamic parameter ?
398 // _pNode will be modified if parameters exists
399 static void substituteParameterNames(OSQLParseNode
const * _pNode
);
401 /** return a table range when it exists.
403 static OUString
getTableRange(const OSQLParseNode
* _pTableRef
);
406 // ParseNodeToStr concatenates all Tokens (leaves) of the ParseNodes.
407 void parseNodeToStr(OUString
& rString
,
408 const css::uno::Reference
< css::sdbc::XConnection
>& _rxConnection
,
409 const css::uno::Reference
< css::util::XNumberFormatter
> & xFormatter
,
410 const css::uno::Reference
< css::beans::XPropertySet
> & _xField
,
411 const OUString
&_sPredicateTableAlias
,
412 const css::lang::Locale
& rIntl
,
413 const IParseContext
* pContext
,
417 bool _bPredicate
) const;
420 void impl_parseNodeToString_throw( OUStringBuffer
& rString
, const SQLParseNodeParameter
& rParam
, bool bSimple
=true ) const;
421 void impl_parseLikeNodeToString_throw( OUStringBuffer
& rString
, const SQLParseNodeParameter
& rParam
, bool bSimple
=true ) const;
422 void impl_parseTableRangeNodeToString_throw( OUStringBuffer
& rString
, const SQLParseNodeParameter
& rParam
) const;
424 /** parses a table_name node into a SQL statement particle.
426 <TRUE/> if and only if parsing was successful, <FALSE/> if default handling should
429 bool impl_parseTableNameNodeToString_throw( OUStringBuffer
& rString
, const SQLParseNodeParameter
& rParam
) const;
431 bool addDateValue(OUStringBuffer
& rString
, const SQLParseNodeParameter
& rParam
) const;
432 static OUString
convertDateTimeString(const SQLParseNodeParameter
& rParam
, const OUString
& rString
);
433 static OUString
convertDateString(const SQLParseNodeParameter
& rParam
, const OUString
& rString
);
434 static OUString
convertTimeString(const SQLParseNodeParameter
& rParam
, const OUString
& rString
);
435 void parseLeaf(OUStringBuffer
& rString
, const SQLParseNodeParameter
& rParam
) const;
438 inline OSQLParseNode
* OSQLParseNode::getChild(sal_uInt32 nPos
) const
440 return m_aChildren
[nPos
].get();
443 // utilities to query for a specific rule, token or punctuation
444 #define SQL_ISRULE(pParseNode, eRule) ((pParseNode)->isRule() && (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::eRule))
445 #define SQL_ISRULEOR2(pParseNode, e1, e2) ((pParseNode)->isRule() && ( \
446 (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e1) || \
447 (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e2)))
448 #define SQL_ISRULEOR3(pParseNode, e1, e2, e3) ((pParseNode)->isRule() && ( \
449 (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e1) || \
450 (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e2) || \
451 (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e3)))
452 #define SQL_ISTOKEN(pParseNode, token) ((pParseNode)->isToken() && (pParseNode)->getTokenID() == SQL_TOKEN_##token)
453 #define SQL_ISTOKENOR2(pParseNode, tok0, tok1) ((pParseNode)->isToken() && ( (pParseNode)->getTokenID() == SQL_TOKEN_##tok0 || (pParseNode)->getTokenID() == SQL_TOKEN_##tok1 ))
454 #define SQL_ISTOKENOR3(pParseNode, tok0, tok1, tok2) ((pParseNode)->isToken() && ( (pParseNode)->getTokenID() == SQL_TOKEN_##tok0 || (pParseNode)->getTokenID() == SQL_TOKEN_##tok1 || (pParseNode)->getTokenID() == SQL_TOKEN_##tok2 ))
455 #define SQL_ISPUNCTUATION(pParseNode, aString) ((pParseNode)->getNodeType() == SQLNodeType::Punctuation && (pParseNode)->getTokenValue() == (aString))
458 #endif // INCLUDED_CONNECTIVITY_SQLNODE_HXX
460 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */