Line data Source code
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 _CONNECTIVITY_SQLNODE_HXX
20 : #define _CONNECTIVITY_SQLNODE_HXX
21 :
22 : #include "connectivity/dbtoolsdllapi.hxx"
23 : #include "connectivity/dbmetadata.hxx"
24 : #include <com/sun/star/uno/Reference.hxx>
25 : #include <com/sun/star/util/XNumberFormatTypes.hpp>
26 : #include <com/sun/star/beans/XPropertySet.hpp>
27 : #include <vector>
28 : #include <functional>
29 : #include <set>
30 : #include <boost/shared_ptr.hpp>
31 : #include <rtl/ustrbuf.hxx>
32 :
33 : // forward declarations
34 : namespace com
35 : {
36 : namespace sun
37 : {
38 : namespace star
39 : {
40 : namespace beans
41 : {
42 : class XPropertySet;
43 : }
44 : namespace util
45 : {
46 : class XNumberFormatter;
47 : }
48 : namespace container
49 : {
50 : class XNameAccess;
51 : }
52 : }
53 : }
54 : }
55 :
56 : namespace rtl
57 : {
58 : class OUStringBuffer;
59 : }
60 : #define ORDER_BY_CHILD_POS 5
61 : #define TABLE_EXPRESSION_CHILD_COUNT 9
62 :
63 : namespace connectivity
64 : {
65 : class OSQLParser;
66 : class OSQLParseNode;
67 : class IParseContext;
68 :
69 : typedef ::std::vector< OSQLParseNode* > OSQLParseNodes;
70 :
71 : enum SQLNodeType {SQL_NODE_RULE, SQL_NODE_LISTRULE, SQL_NODE_COMMALISTRULE,
72 : SQL_NODE_KEYWORD, SQL_NODE_COMPARISON, SQL_NODE_NAME,
73 : SQL_NODE_STRING, SQL_NODE_INTNUM, SQL_NODE_APPROXNUM,
74 : SQL_NODE_EQUAL,SQL_NODE_LESS,SQL_NODE_GREAT,SQL_NODE_LESSEQ,SQL_NODE_GREATEQ,SQL_NODE_NOTEQUAL,
75 : SQL_NODE_PUNCTUATION, SQL_NODE_AMMSC, SQL_NODE_ACCESS_DATE,SQL_NODE_DATE,SQL_NODE_CONCAT};
76 :
77 : typedef ::std::set< ::rtl::OUString > QueryNameSet;
78 : //==================================================================
79 : //= SQLParseNodeParameter
80 : //==================================================================
81 0 : struct OOO_DLLPUBLIC_DBTOOLS SQLParseNodeParameter
82 : {
83 : const ::com::sun::star::lang::Locale& rLocale;
84 : ::dbtools::DatabaseMetaData aMetaData;
85 : OSQLParser* pParser;
86 : ::boost::shared_ptr< QueryNameSet > pSubQueryHistory;
87 : ::com::sun::star::uno::Reference< ::com::sun::star::util::XNumberFormatter > xFormatter;
88 : ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet > xField;
89 : ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameAccess > xQueries; // see bParseToSDBCLevel
90 : const IParseContext& m_rContext;
91 : sal_Char cDecSep;
92 : bool bQuote : 1; /// should we quote identifiers?
93 : bool bInternational : 1; /// should we internationalize keywords and placeholders?
94 : bool bPredicate : 1; /// are we going to parse a mere predicate?
95 : bool bParseToSDBCLevel : 1; /// should we create an SDBC-level statement (e.g. with substituted sub queries)?
96 :
97 : SQLParseNodeParameter(
98 : const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConnection,
99 : const ::com::sun::star::uno::Reference< ::com::sun::star::util::XNumberFormatter >& _xFormatter,
100 : const ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet >& _xField,
101 : const ::com::sun::star::lang::Locale& _rLocale,
102 : const IParseContext* _pContext,
103 : bool _bIntl,
104 : bool _bQuote,
105 : sal_Char _cDecSep,
106 : bool _bPredicate,
107 : bool _bParseToSDBC
108 : );
109 : ~SQLParseNodeParameter();
110 : };
111 :
112 : //==========================================================================
113 : //= OSQLParseNode
114 : //==========================================================================
115 : class OOO_DLLPUBLIC_DBTOOLS OSQLParseNode
116 : {
117 : friend class OSQLParser;
118 :
119 : OSQLParseNodes m_aChildren;
120 : OSQLParseNode* m_pParent; // pParent for reverse linkage in the tree
121 : ::rtl::OUString m_aNodeValue; // token name, or empty in case of rules,
122 : // or ::rtl::OUString in case of
123 : // ::rtl::OUString, INT, etc.
124 : SQLNodeType m_eNodeType; // see above
125 : sal_uInt32 m_nNodeID; // ::com::sun::star::chaos::Rule ID (if IsRule())
126 : // or Token ID (if !IsRule())
127 : // ::com::sun::star::chaos::Rule IDs and Token IDs can't
128 : // be distinguished by their values,
129 : // IsRule has to be used for that!
130 : public:
131 : enum Rule
132 : {
133 : select_statement = 0,
134 : table_exp,
135 : table_ref_commalist,
136 : table_ref,
137 : catalog_name,
138 : schema_name,
139 : table_name,
140 : opt_column_commalist,
141 : column_commalist,
142 : column_ref_commalist,
143 : column_ref,
144 : opt_order_by_clause,
145 : ordering_spec_commalist,
146 : ordering_spec,
147 : opt_asc_desc,
148 : where_clause,
149 : opt_where_clause,
150 : search_condition,
151 : comparison_predicate,
152 : between_predicate,
153 : like_predicate,
154 : opt_escape,
155 : test_for_null,
156 : scalar_exp_commalist,
157 : scalar_exp,
158 : parameter_ref,
159 : parameter,
160 : general_set_fct,
161 : range_variable,
162 : column,
163 : delete_statement_positioned,
164 : delete_statement_searched,
165 : update_statement_positioned,
166 : update_statement_searched,
167 : assignment_commalist,
168 : assignment,
169 : values_or_query_spec,
170 : insert_statement,
171 : insert_atom_commalist,
172 : insert_atom,
173 : predicate_check,
174 : from_clause,
175 : qualified_join,
176 : cross_union,
177 : select_sublist,
178 : derived_column,
179 : column_val,
180 : set_fct_spec,
181 : boolean_term,
182 : boolean_primary,
183 : num_value_exp,
184 : join_type,
185 : position_exp,
186 : extract_exp,
187 : length_exp,
188 : char_value_fct,
189 : odbc_call_spec,
190 : in_predicate,
191 : existence_test,
192 : unique_test,
193 : all_or_any_predicate,
194 : named_columns_join,
195 : join_condition,
196 : joined_table,
197 : boolean_factor,
198 : sql_not,
199 : boolean_test,
200 : manipulative_statement,
201 : subquery,
202 : value_exp_commalist,
203 : odbc_fct_spec,
204 : union_statement,
205 : outer_join_type,
206 : char_value_exp,
207 : term,
208 : value_exp_primary,
209 : value_exp,
210 : selection,
211 : fold,
212 : char_substring_fct,
213 : factor,
214 : base_table_def,
215 : base_table_element_commalist,
216 : data_type,
217 : column_def,
218 : table_node,
219 : as,
220 : op_column_commalist,
221 : table_primary_as_range_column,
222 : datetime_primary,
223 : concatenation,
224 : char_factor,
225 : bit_value_fct,
226 : comparison_predicate_part_2,
227 : parenthesized_boolean_value_expression,
228 : character_string_type,
229 : other_like_predicate_part_2,
230 : between_predicate_part_2,
231 : cast_spec,
232 : rule_count, // last value
233 : UNKNOWN_RULE = -1 // ID indicating that a node is no rule with a matching Rule-enum value (see getKnownRuleID)
234 : };
235 :
236 : // must be ascii encoding for the value
237 : OSQLParseNode(const sal_Char* _pValueStr,
238 : SQLNodeType _eNodeType,
239 : sal_uInt32 _nNodeID = 0);
240 :
241 : OSQLParseNode(const ::rtl::OString& _rValue,
242 : SQLNodeType eNewNodeType,
243 : sal_uInt32 nNewNodeID=0);
244 :
245 : OSQLParseNode(const ::rtl::OUString& _rValue,
246 : SQLNodeType _eNodeType,
247 : sal_uInt32 _nNodeID = 0);
248 :
249 : // copies the respective ParseNode
250 : OSQLParseNode(const OSQLParseNode& rParseNode);
251 : OSQLParseNode& operator=(const OSQLParseNode& rParseNode);
252 :
253 : sal_Bool operator==(OSQLParseNode& rParseNode) const;
254 :
255 : // destructor destructs the tree recursively
256 : virtual ~OSQLParseNode();
257 :
258 65 : OSQLParseNode* getParent() const {return m_pParent;};
259 :
260 62 : void setParent(OSQLParseNode* pParseNode) {m_pParent = pParseNode;};
261 :
262 235 : size_t count() const {return m_aChildren.size();};
263 : inline OSQLParseNode* getChild(sal_uInt32 nPos) const;
264 :
265 : void append(OSQLParseNode* pNewSubTree);
266 : void insert(sal_uInt32 nPos, OSQLParseNode* pNewSubTree);
267 :
268 : OSQLParseNode* replace(OSQLParseNode* pOldSubTree, OSQLParseNode* pNewSubTree);
269 :
270 : OSQLParseNode* removeAt(sal_uInt32 nPos);
271 :
272 : void replaceNodeValue(const ::rtl::OUString& rTableAlias,const ::rtl::OUString& rColumnName);
273 :
274 : /** parses the node to a string which can be passed to a driver's connection for execution
275 :
276 : Any particles of the parse tree which represent application-level features - such
277 : as queries appearing in the FROM part - are substituted, so that the resulting statement can
278 : be executed at an SDBC-level connection.
279 :
280 : @param _out_rString
281 : is an output parameter taking the resulting SQL statement
282 :
283 : @param _rxConnection
284 : the connection relative to which to parse. This must be an SDB-level connection (e.g.
285 : support the XQueriesSupplier interface) for the method to be able to do all necessary
286 : substitutions.
287 :
288 : @param _rParser
289 : the SQLParser used to create the node. This is needed in case we need to parse
290 : sub queries which are present in the SQL statement - those sub queries need to be parsed,
291 : too, to check whether they contain nested sub queries.
292 :
293 : @param _pErrorHolder
294 : takes the error which occurred while generating the statement, if any. Might be <NULL/>,
295 : in this case the error is not reported back, and can only be recognized by examing the
296 : return value.
297 :
298 : @return
299 : <TRUE/> if and only if the parsing was successful.<br/>
300 :
301 : Currently, there's only one condition how this method can fail: If it contains a nested
302 : query which causes a cycle. E.g., consider a statement <code>SELECT * from "foo"</code>,
303 : where <code>foo</code> is a query defined as <code>SELECT * FROM "bar"</code>, where
304 : <code>bar</code> is defined as <code>SELECT * FROM "foo"</code>. This statement obviously
305 : cannot be parsed to an executable statement.
306 :
307 : If this method returns <FALSE/>, you're encouraged to check and handle the error in
308 : <arg>_pErrorHolder</arg>.
309 : */
310 : bool parseNodeToExecutableStatement( ::rtl::OUString& _out_rString,
311 : const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConnection,
312 : OSQLParser& _rParser,
313 : ::com::sun::star::sdbc::SQLException* _pErrorHolder ) const;
314 :
315 : void parseNodeToStr(::rtl::OUString& rString,
316 : const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConnection,
317 : const IParseContext* pContext = NULL,
318 : sal_Bool _bIntl = sal_False,
319 : sal_Bool _bQuote= sal_True) const;
320 :
321 : // quoted and internationalised
322 : void parseNodeToPredicateStr(::rtl::OUString& rString,
323 : const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConnection,
324 : const ::com::sun::star::uno::Reference< ::com::sun::star::util::XNumberFormatter > & xFormatter,
325 : const ::com::sun::star::lang::Locale& rIntl,
326 : sal_Char _cDec,
327 : const IParseContext* pContext = NULL ) const;
328 :
329 : void parseNodeToPredicateStr(::rtl::OUString& rString,
330 : const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConnection,
331 : const ::com::sun::star::uno::Reference< ::com::sun::star::util::XNumberFormatter > & xFormatter,
332 : const ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet > & _xField,
333 : const ::com::sun::star::lang::Locale& rIntl,
334 : sal_Char _cDec,
335 : const IParseContext* pContext = NULL ) const;
336 :
337 : OSQLParseNode* getByRule(OSQLParseNode::Rule eRule) const;
338 :
339 : #if OSL_DEBUG_LEVEL > 1
340 : // shows the ParseTree with tabs and linefeeds
341 : void showParseTree( ::rtl::OUString& rString ) const;
342 : void showParseTree( ::rtl::OUStringBuffer& _inout_rBuf, sal_uInt32 nLevel ) const;
343 : #endif
344 :
345 65 : SQLNodeType getNodeType() const {return m_eNodeType;};
346 :
347 : // RuleId returns the RuleID of the node's rule (only if IsRule())
348 359 : sal_uInt32 getRuleID() const {return m_nNodeID;}
349 :
350 : /** returns the ID of the rule represented by the node
351 : If the node does not represent a rule, UNKNOWN_RULE is returned
352 : */
353 : Rule getKnownRuleID() const;
354 :
355 : // returns the TokenId of the node's token (only if !isRule())
356 2 : sal_uInt32 getTokenID() const {return m_nNodeID;}
357 :
358 : // IsRule tests whether a node is a rule (NonTerminal)
359 : // ATTENTION: rules can be leaves, for example empty lists
360 399 : sal_Bool isRule() const
361 : { return (m_eNodeType == SQL_NODE_RULE) || (m_eNodeType == SQL_NODE_LISTRULE)
362 399 : || (m_eNodeType == SQL_NODE_COMMALISTRULE);}
363 :
364 : // IsToken tests whether a Node is a Token (Terminal but not a rule)
365 4 : sal_Bool isToken() const {return !isRule();}
366 :
367 9 : const ::rtl::OUString& getTokenValue() const {return m_aNodeValue;}
368 :
369 : void setTokenValue(const ::rtl::OUString& rString) { if (isToken()) m_aNodeValue = rString;}
370 :
371 2 : sal_Bool isLeaf() const {return m_aChildren.empty();}
372 :
373 : // negate only a searchcondition, any other rule could cause a gpf
374 : static void negateSearchCondition(OSQLParseNode*& pSearchCondition,sal_Bool bNegate=sal_False);
375 :
376 : // normalize a logic form
377 : // e.q. (a or b) and (c or d) <=> a and c or a and d or b and c or b and d
378 : static void disjunctiveNormalForm(OSQLParseNode*& pSearchCondition);
379 :
380 : // Simplifies logic expressions
381 : // a and a = a
382 : // a or a = a
383 : // a and ( a + b) = a
384 : // a or a and b = a
385 : static void absorptions(OSQLParseNode*& pSearchCondition);
386 :
387 : // erase unnecessary braces
388 : static void eraseBraces(OSQLParseNode*& pSearchCondition);
389 :
390 : // makes the logic formula a little smaller
391 : static void compress(OSQLParseNode*& pSearchCondition);
392 : // return the catalog, schema and tablename form this node
393 : // _pTableNode must be a rule of that above or a SQL_TOKEN_NAME
394 : static sal_Bool getTableComponents(const OSQLParseNode* _pTableNode,
395 : ::com::sun::star::uno::Any &_rCatalog,
396 : ::rtl::OUString &_rSchema,
397 : ::rtl::OUString &_rTable
398 : ,const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XDatabaseMetaData >& _xMetaData);
399 :
400 : // substitute all occurrences of :var or [name] into the dynamic parameter ?
401 : // _pNode will be modified if parameters exists
402 : static void substituteParameterNames(OSQLParseNode* _pNode);
403 :
404 : /** return a table range when it exists.
405 : */
406 : static ::rtl::OUString getTableRange(const OSQLParseNode* _pTableRef);
407 :
408 : protected:
409 : // ParseNodeToStr concatenates all Tokens (leaves) of the ParseNodes.
410 : void parseNodeToStr(::rtl::OUString& rString,
411 : const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConnection,
412 : const ::com::sun::star::uno::Reference< ::com::sun::star::util::XNumberFormatter > & xFormatter,
413 : const ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet > & _xField,
414 : const ::com::sun::star::lang::Locale& rIntl,
415 : const IParseContext* pContext,
416 : bool _bIntl,
417 : bool _bQuote,
418 : sal_Char _cDecSep,
419 : bool _bPredicate,
420 : bool _bSubstitute) const;
421 :
422 : private:
423 : void impl_parseNodeToString_throw( ::rtl::OUStringBuffer& rString, const SQLParseNodeParameter& rParam) const;
424 : void impl_parseLikeNodeToString_throw( ::rtl::OUStringBuffer& rString, const SQLParseNodeParameter& rParam ) const;
425 : void impl_parseTableRangeNodeToString_throw( ::rtl::OUStringBuffer& rString, const SQLParseNodeParameter& rParam ) const;
426 :
427 : /** parses a table_name node into a SQL statement particle.
428 : @return
429 : <TRUE/> if and only if parsing was successful, <FALSE/> if default handling should
430 : be applied.
431 : */
432 : bool impl_parseTableNameNodeToString_throw( ::rtl::OUStringBuffer& rString, const SQLParseNodeParameter& rParam ) const;
433 :
434 : sal_Bool addDateValue(::rtl::OUStringBuffer& rString, const SQLParseNodeParameter& rParam) const;
435 : ::rtl::OUString convertDateTimeString(const SQLParseNodeParameter& rParam, const ::rtl::OUString& rString) const;
436 : ::rtl::OUString convertDateString(const SQLParseNodeParameter& rParam, const ::rtl::OUString& rString) const;
437 : ::rtl::OUString convertTimeString(const SQLParseNodeParameter& rParam, const ::rtl::OUString& rString) const;
438 : void parseLeaf(::rtl::OUStringBuffer& rString, const SQLParseNodeParameter& rParam) const;
439 : };
440 :
441 : //-----------------------------------------------------------------------------
442 241 : inline OSQLParseNode* OSQLParseNode::getChild(sal_uInt32 nPos) const
443 : {
444 : OSL_ENSURE(nPos < m_aChildren.size(), "Invalid Position");
445 :
446 : // return m_aChildren[nPos];
447 241 : return m_aChildren.at(nPos);
448 : }
449 :
450 : // utilities to query for a specific rule, token or punctuation
451 : #define SQL_ISRULE(pParseNode, eRule) ((pParseNode)->isRule() && (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::eRule))
452 : #define SQL_ISRULEOR2(pParseNode, e1, e2) ((pParseNode)->isRule() && ( \
453 : (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e1) || \
454 : (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e2)))
455 : #define SQL_ISRULEOR3(pParseNode, e1, e2, e3) ((pParseNode)->isRule() && ( \
456 : (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e1) || \
457 : (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e2) || \
458 : (pParseNode)->getRuleID() == OSQLParser::RuleID(OSQLParseNode::e3)))
459 : #define SQL_ISTOKEN(pParseNode, token) ((pParseNode)->isToken() && (pParseNode)->getTokenID() == SQL_TOKEN_##token)
460 : #define SQL_ISTOKENOR2(pParseNode, tok0, tok1) ((pParseNode)->isToken() && ( (pParseNode)->getTokenID() == SQL_TOKEN_##tok0 || (pParseNode)->getTokenID() == SQL_TOKEN_##tok1 ))
461 : #define SQL_ISTOKENOR3(pParseNode, tok0, tok1, tok2) ((pParseNode)->isToken() && ( (pParseNode)->getTokenID() == SQL_TOKEN_##tok0 || (pParseNode)->getTokenID() == SQL_TOKEN_##tok1 || (pParseNode)->getTokenID() == SQL_TOKEN_##tok2 ))
462 : #define SQL_ISPUNCTUATION(pParseNode, aString) ((pParseNode)->getNodeType() == SQL_NODE_PUNCTUATION && !(pParseNode)->getTokenValue().compareToAscii(aString))
463 : }
464 :
465 : #endif //_CONNECTIVITY_SQLNODE_HXX
466 :
467 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|