/src/libreoffice/include/connectivity/sqlnode.hxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
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/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
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 . |
18 | | */ |
19 | | #ifndef INCLUDED_CONNECTIVITY_SQLNODE_HXX |
20 | | #define INCLUDED_CONNECTIVITY_SQLNODE_HXX |
21 | | |
22 | | #include <connectivity/dbtoolsdllapi.hxx> |
23 | | #include <connectivity/dbmetadata.hxx> |
24 | | #include <com/sun/star/uno/Reference.hxx> |
25 | | #include <memory> |
26 | | #include <set> |
27 | | #include <string_view> |
28 | | #include <vector> |
29 | | #include <rtl/ustrbuf.hxx> |
30 | | |
31 | | namespace com::sun::star::lang { struct Locale; } |
32 | | namespace com::sun::star::sdbc { class SQLException; } |
33 | | namespace com::sun::star::sdbc { class XDatabaseMetaData; } |
34 | | |
35 | | namespace com::sun::star |
36 | | { |
37 | | namespace beans |
38 | | { |
39 | | class XPropertySet; |
40 | | } |
41 | | namespace util |
42 | | { |
43 | | class XNumberFormatter; |
44 | | } |
45 | | namespace container |
46 | | { |
47 | | class XNameAccess; |
48 | | } |
49 | | } |
50 | | |
51 | 135k | #define ORDER_BY_CHILD_POS 5 |
52 | | #define TABLE_EXPRESSION_CHILD_COUNT 9 |
53 | | |
54 | | namespace connectivity |
55 | | { |
56 | | class OSQLParser; |
57 | | class IParseContext; |
58 | | |
59 | | enum class SQLNodeType { Rule, ListRule, CommaListRule, |
60 | | Keyword, Name, |
61 | | String, IntNum, ApproxNum, |
62 | | Equal, Less, Great, LessEq, GreatEq, NotEqual, |
63 | | Punctuation, AccessDate, Concat}; |
64 | | |
65 | | typedef ::std::set< OUString > QueryNameSet; |
66 | | |
67 | | //= SQLParseNodeParameter |
68 | | |
69 | | struct SQLParseNodeParameter |
70 | | { |
71 | | const css::lang::Locale& rLocale; |
72 | | ::dbtools::DatabaseMetaData aMetaData; |
73 | | OSQLParser* pParser; |
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; |
80 | | OUString sDecSep; |
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)? |
85 | | |
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 | | OUString _sPredicateTableAlias, |
91 | | const css::lang::Locale& _rLocale, |
92 | | const IParseContext* _pContext, |
93 | | bool _bIntl, |
94 | | bool _bQuote, |
95 | | OUString _sDecSep, |
96 | | bool _bPredicate, |
97 | | bool _bParseToSDBC |
98 | | ); |
99 | | }; |
100 | | |
101 | | //= OSQLParseNode |
102 | | |
103 | | class OOO_DLLPUBLIC_DBTOOLS OSQLParseNode |
104 | | { |
105 | | friend class OSQLParser; |
106 | | |
107 | | std::vector< std::unique_ptr<OSQLParseNode> > |
108 | | m_aChildren; |
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! |
119 | | public: |
120 | | enum Rule |
121 | | { |
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 (!) |
125 | | select_statement, |
126 | | table_exp, |
127 | | table_ref_commalist, |
128 | | table_ref, |
129 | | catalog_name, |
130 | | schema_name, |
131 | | table_name, |
132 | | opt_column_commalist, |
133 | | column_commalist, |
134 | | column_ref_commalist, |
135 | | column_ref, |
136 | | opt_order_by_clause, |
137 | | ordering_spec_commalist, |
138 | | ordering_spec, |
139 | | opt_asc_desc, |
140 | | where_clause, |
141 | | opt_where_clause, |
142 | | search_condition, |
143 | | comparison, |
144 | | comparison_predicate, |
145 | | between_predicate, |
146 | | like_predicate, |
147 | | opt_escape, |
148 | | test_for_null, |
149 | | scalar_exp_commalist, |
150 | | scalar_exp, |
151 | | parameter_ref, |
152 | | parameter, |
153 | | general_set_fct, |
154 | | range_variable, |
155 | | column, |
156 | | delete_statement_positioned, |
157 | | delete_statement_searched, |
158 | | update_statement_positioned, |
159 | | update_statement_searched, |
160 | | assignment_commalist, |
161 | | assignment, |
162 | | values_or_query_spec, |
163 | | insert_statement, |
164 | | insert_atom_commalist, |
165 | | insert_atom, |
166 | | from_clause, |
167 | | qualified_join, |
168 | | cross_union, |
169 | | select_sublist, |
170 | | derived_column, |
171 | | column_val, |
172 | | set_fct_spec, |
173 | | boolean_term, |
174 | | boolean_primary, |
175 | | num_value_exp, |
176 | | join_type, |
177 | | position_exp, |
178 | | extract_exp, |
179 | | length_exp, |
180 | | char_value_fct, |
181 | | odbc_call_spec, |
182 | | in_predicate, |
183 | | existence_test, |
184 | | unique_test, |
185 | | all_or_any_predicate, |
186 | | named_columns_join, |
187 | | join_condition, |
188 | | joined_table, |
189 | | boolean_factor, |
190 | | sql_not, |
191 | | manipulative_statement, |
192 | | subquery, |
193 | | value_exp_commalist, |
194 | | odbc_fct_spec, |
195 | | union_statement, |
196 | | outer_join_type, |
197 | | char_value_exp, |
198 | | term, |
199 | | value_exp_primary, |
200 | | value_exp, |
201 | | selection, |
202 | | fold, |
203 | | char_substring_fct, |
204 | | factor, |
205 | | base_table_def, |
206 | | base_table_element_commalist, |
207 | | data_type, |
208 | | column_def, |
209 | | table_node, |
210 | | as_clause, |
211 | | opt_as, |
212 | | op_column_commalist, |
213 | | table_primary_as_range_column, |
214 | | datetime_primary, |
215 | | concatenation, |
216 | | char_factor, |
217 | | bit_value_fct, |
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, |
224 | | cast_spec, |
225 | | window_function, |
226 | | rule_count // last value |
227 | | }; |
228 | | |
229 | | // must be ascii encoding for the value |
230 | | OSQLParseNode(const char* _pValueStr, |
231 | | SQLNodeType _eNodeType, |
232 | | sal_uInt32 _nNodeID = 0); |
233 | | |
234 | | OSQLParseNode(std::string_view _rValue, |
235 | | SQLNodeType eNewNodeType, |
236 | | sal_uInt32 nNewNodeID=0); |
237 | | |
238 | | OSQLParseNode(OUString _sValue, |
239 | | SQLNodeType _eNodeType, |
240 | | sal_uInt32 _nNodeID = 0); |
241 | | |
242 | | // copies the respective ParseNode |
243 | | OSQLParseNode(const OSQLParseNode& rParseNode); |
244 | | OSQLParseNode& operator=(const OSQLParseNode& rParseNode); |
245 | | |
246 | | bool operator==(OSQLParseNode const & rParseNode) const; |
247 | | |
248 | | // destructor destructs the tree recursively |
249 | | virtual ~OSQLParseNode(); |
250 | | |
251 | 1.55M | OSQLParseNode* getParent() const {return m_pParent;}; |
252 | | |
253 | 1.47M | void setParent(OSQLParseNode* pParseNode) {m_pParent = pParseNode;}; |
254 | | |
255 | 2.62M | size_t count() const {return m_aChildren.size();}; |
256 | | inline OSQLParseNode* getChild(sal_uInt32 nPos) const; |
257 | | |
258 | | void append(OSQLParseNode* pNewSubTree); |
259 | | void insert(sal_uInt32 nPos, OSQLParseNode* pNewSubTree); |
260 | | |
261 | | void replaceAndDelete(OSQLParseNode* pOldSubTree, OSQLParseNode* pNewSubTree); |
262 | | |
263 | | OSQLParseNode* removeAt(sal_uInt32 nPos); |
264 | | |
265 | | void replaceNodeValue(const OUString& rTableAlias,const OUString& rColumnName); |
266 | | |
267 | | /** parses the node to a string which can be passed to a driver's connection for execution |
268 | | |
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. |
272 | | |
273 | | @param _out_rString |
274 | | is an output parameter taking the resulting SQL statement |
275 | | |
276 | | @param _rxConnection |
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 |
279 | | substitutions. |
280 | | |
281 | | @param _rParser |
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. |
285 | | |
286 | | @param _pErrorHolder |
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 |
289 | | return value. |
290 | | |
291 | | @return |
292 | | <TRUE/> if and only if the parsing was successful.<br/> |
293 | | |
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. |
299 | | |
300 | | If this method returns <FALSE/>, you're encouraged to check and handle the error in |
301 | | <arg>_pErrorHolder</arg>. |
302 | | */ |
303 | | bool parseNodeToExecutableStatement( OUString& _out_rString, |
304 | | const css::uno::Reference< css::sdbc::XConnection >& _rxConnection, |
305 | | OSQLParser& _rParser, |
306 | | css::sdbc::SQLException* _pErrorHolder ) const; |
307 | | |
308 | | void parseNodeToStr(OUString& rString, |
309 | | const css::uno::Reference< css::sdbc::XConnection >& _rxConnection, |
310 | | const IParseContext* pContext = nullptr, |
311 | | bool _bIntl = false, |
312 | | bool _bQuote= true) const; |
313 | | |
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, |
319 | | const OUString& rDec, |
320 | | const IParseContext* pContext = nullptr ) const; |
321 | | |
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, |
328 | | const OUString& rStrDec, |
329 | | const IParseContext* pContext = nullptr ) const; |
330 | | |
331 | | OSQLParseNode* getByRule(OSQLParseNode::Rule eRule) const; |
332 | | |
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; |
337 | | #endif |
338 | | |
339 | 1.46M | SQLNodeType getNodeType() const {return m_eNodeType;}; |
340 | | |
341 | | // RuleId returns the RuleID of the node's rule (only if IsRule()) |
342 | 7.55M | sal_uInt32 getRuleID() const {return m_nNodeID;} |
343 | | |
344 | | /** returns the ID of the rule represented by the node |
345 | | If the node does not represent a rule, UNKNOWN_RULE is returned |
346 | | */ |
347 | | Rule getKnownRuleID() const; |
348 | | |
349 | | // returns the TokenId of the node's token (only if !isRule()) |
350 | 20.8k | sal_uInt32 getTokenID() const {return m_nNodeID;} |
351 | | |
352 | | // IsRule tests whether a node is a rule (NonTerminal) |
353 | | // ATTENTION: rules can be leaves, for example empty lists |
354 | | bool isRule() const |
355 | 9.86M | { return (m_eNodeType == SQLNodeType::Rule) || (m_eNodeType == SQLNodeType::ListRule) |
356 | 1.84M | || (m_eNodeType == SQLNodeType::CommaListRule);} |
357 | | |
358 | | // IsToken tests whether a Node is a Token (Terminal but not a rule) |
359 | 1.12M | bool isToken() const {return !isRule();} |
360 | | |
361 | 187k | const OUString& getTokenValue() const {return m_aNodeValue;} |
362 | | |
363 | 52.2k | bool isLeaf() const {return m_aChildren.empty();} |
364 | | |
365 | | // negate only a searchcondition, any other rule could cause a gpf |
366 | | static void negateSearchCondition(OSQLParseNode*& pSearchCondition, bool bNegate=false); |
367 | | |
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); |
371 | | |
372 | | // Simplifies logic expressions |
373 | | // a and a = a |
374 | | // a or a = a |
375 | | // a and ( a + b) = a |
376 | | // a or a and b = a |
377 | | static void absorptions(OSQLParseNode*& pSearchCondition); |
378 | | |
379 | | // erase unnecessary braces |
380 | | static void eraseBraces(OSQLParseNode*& pSearchCondition); |
381 | | |
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, |
388 | | OUString &_rSchema, |
389 | | OUString &_rTable, |
390 | | const css::uno::Reference< css::sdbc::XDatabaseMetaData >& _xMetaData); |
391 | | |
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); |
395 | | |
396 | | /** return a table range when it exists. |
397 | | */ |
398 | | static OUString getTableRange(const OSQLParseNode* _pTableRef); |
399 | | |
400 | | protected: |
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, |
409 | | bool _bIntl, |
410 | | bool _bQuote, |
411 | | const OUString& _rDecSep, |
412 | | bool _bPredicate) const; |
413 | | |
414 | | private: |
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; |
418 | | |
419 | | /** parses a table_name node into a SQL statement particle. |
420 | | @return |
421 | | <TRUE/> if and only if parsing was successful, <FALSE/> if default handling should |
422 | | be applied. |
423 | | */ |
424 | | bool impl_parseTableNameNodeToString_throw( OUStringBuffer& rString, const SQLParseNodeParameter& rParam ) const; |
425 | | |
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, std::u16string_view rString); |
429 | | static OUString convertTimeString(const SQLParseNodeParameter& rParam, std::u16string_view rString); |
430 | | void parseLeaf(OUStringBuffer& rString, const SQLParseNodeParameter& rParam) const; |
431 | | }; |
432 | | |
433 | | inline OSQLParseNode* OSQLParseNode::getChild(sal_uInt32 nPos) const |
434 | 3.27M | { |
435 | 3.27M | return m_aChildren[nPos].get(); |
436 | 3.27M | } |
437 | | |
438 | | // utilities to query for a specific rule, token or punctuation |
439 | 7.72M | #define SQL_ISRULE(pParseNode, eRule) ((pParseNode)->isRule() && (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::eRule)) |
440 | 41.7k | #define SQL_ISRULEOR2(pParseNode, e1, e2) ((pParseNode)->isRule() && ( \ |
441 | 20.8k | (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e1) || \ |
442 | 20.8k | (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 | 124k | #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 | 281k | #define SQL_ISPUNCTUATION(pParseNode, aString) ((pParseNode)->getNodeType() == SQLNodeType::Punctuation && (pParseNode)->getTokenValue() == (aString)) |
451 | | } |
452 | | |
453 | | #endif // INCLUDED_CONNECTIVITY_SQLNODE_HXX |
454 | | |
455 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |