Coverage Report

Created: 2026-03-31 11:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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: */