Coverage Report

Created: 2025-12-31 10:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/connectivity/source/commontools/dbtools2.cxx
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
20
#include <connectivity/dbtools.hxx>
21
#include <connectivity/dbconversion.hxx>
22
#include <connectivity/dbcharset.hxx>
23
#include <connectivity/dbexception.hxx>
24
#include <SQLStatementHelper.hxx>
25
#include <unotools/confignode.hxx>
26
#include <resource/sharedresources.hxx>
27
#include <strings.hrc>
28
#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp>
29
#include <com/sun/star/sdbc/SQLException.hpp>
30
#include <com/sun/star/sdbc/XConnection.hpp>
31
#include <com/sun/star/sdbc/XDataSource.hpp>
32
#include <com/sun/star/sdbc/ColumnValue.hpp>
33
#include <com/sun/star/sdbc/DataType.hpp>
34
#include <com/sun/star/sdbc/DriverManager.hpp>
35
#include <com/sun/star/sdbc/XRow.hpp>
36
#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
37
#include <com/sun/star/sdbcx/XKeysSupplier.hpp>
38
#include <com/sun/star/sdbcx/XDataDefinitionSupplier.hpp>
39
#include <com/sun/star/sdbcx/Privilege.hpp>
40
#include <com/sun/star/container/XIndexAccess.hpp>
41
#include <com/sun/star/sdbc/KeyRule.hpp>
42
#include <com/sun/star/sdbcx/KeyType.hpp>
43
#include <TConnection.hxx>
44
#include <connectivity/sdbcx/VColumn.hxx>
45
#include <com/sun/star/frame/XModel.hpp>
46
#include <com/sun/star/container/XChild.hpp>
47
48
#include <comphelper/types.hxx>
49
#include <comphelper/diagnose_ex.hxx>
50
#include <unotools/sharedunocomponent.hxx>
51
#include <algorithm>
52
#include <string_view>
53
54
namespace dbtools
55
{
56
57
    using namespace ::com::sun::star::uno;
58
    using namespace ::com::sun::star::beans;
59
    using namespace ::com::sun::star::sdb;
60
    using namespace ::com::sun::star::sdbc;
61
    using namespace ::com::sun::star::sdbcx;
62
    using namespace ::com::sun::star::lang;
63
    using namespace ::com::sun::star::container;
64
    using namespace ::com::sun::star::frame;
65
    using namespace connectivity;
66
    using namespace comphelper;
67
68
OUString createStandardTypePart(const Reference< XPropertySet >& xColProp,const Reference< XConnection>& _xConnection,std::u16string_view _sCreatePattern)
69
0
{
70
71
0
    Reference<XDatabaseMetaData> xMetaData = _xConnection->getMetaData();
72
73
0
    ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap();
74
75
0
    OUString sTypeName;
76
0
    sal_Int32       nDataType   = 0;
77
0
    sal_Int32       nPrecision  = 0;
78
0
    sal_Int32       nScale      = 0;
79
80
0
    nDataType = nPrecision = nScale = 0;
81
82
0
    xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_TYPENAME))           >>= sTypeName;
83
0
    xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_TYPE))               >>= nDataType;
84
0
    xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_PRECISION))          >>= nPrecision;
85
0
    xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCALE))              >>= nScale;
86
87
0
    OUStringBuffer aSql;
88
89
    // check if the user enter a specific string to create autoincrement values
90
0
    OUString sAutoIncrementValue;
91
0
    Reference<XPropertySetInfo> xPropInfo = xColProp->getPropertySetInfo();
92
0
    if ( xPropInfo.is() && xPropInfo->hasPropertyByName(rPropMap.getNameByIndex(PROPERTY_ID_AUTOINCREMENTCREATION)) )
93
0
        xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_AUTOINCREMENTCREATION)) >>= sAutoIncrementValue;
94
    // look if we have to use precisions
95
0
    bool bUseLiteral = false;
96
0
    OUString sPrefix,sPostfix,sCreateParams;
97
0
    {
98
0
        Reference<XResultSet> xRes = xMetaData->getTypeInfo();
99
0
        if(xRes.is())
100
0
        {
101
0
            Reference<XRow> xRow(xRes,UNO_QUERY);
102
0
            while(xRes->next())
103
0
            {
104
0
                OUString sTypeName2Cmp = xRow->getString(1);
105
0
                sal_Int32 nType = xRow->getShort(2);
106
0
                sPrefix = xRow->getString (4);
107
0
                sPostfix = xRow->getString (5);
108
0
                sCreateParams = xRow->getString(6);
109
                // first identical type will be used if typename is empty
110
0
                if ( sTypeName.isEmpty() && nType == nDataType )
111
0
                    sTypeName = sTypeName2Cmp;
112
113
0
                if( sTypeName.equalsIgnoreAsciiCase(sTypeName2Cmp) && nType == nDataType && !sCreateParams.isEmpty() && !xRow->wasNull())
114
0
                {
115
0
                    bUseLiteral = true;
116
0
                    break;
117
0
                }
118
0
            }
119
0
        }
120
0
    }
121
122
0
    if ( !sAutoIncrementValue.isEmpty() )
123
0
    {
124
0
        sal_Int32 nIndex = sTypeName.indexOf(sAutoIncrementValue);
125
0
        if (nIndex != -1)
126
0
            sTypeName = sTypeName.replaceAt(nIndex,sTypeName.getLength() - nIndex, u"");
127
0
    }
128
129
0
    if ( (nPrecision > 0 || nScale > 0) && bUseLiteral )
130
0
    {
131
0
        bool bTimed = (nDataType == DataType::TIME ||
132
0
                       nDataType == DataType::TIME_WITH_TIMEZONE ||
133
0
                       nDataType == DataType::TIMESTAMP ||
134
0
                       nDataType == DataType::TIMESTAMP_WITH_TIMEZONE);
135
136
0
        sal_Int32 nParenPos = (nDataType == DataType::TIME_WITH_TIMEZONE ||
137
0
                               nDataType == DataType::TIMESTAMP_WITH_TIMEZONE) ?
138
0
                               sTypeName.indexOf(' ') :
139
0
                               sTypeName.indexOf('(');
140
141
0
        if ( nParenPos == -1 )
142
0
            aSql.append(sTypeName);
143
0
        else
144
0
            aSql.append(sTypeName.subView(0, nParenPos));
145
0
        aSql.append("(");
146
147
0
        if ( nPrecision > 0 && !bTimed )
148
0
        {
149
0
            aSql.append(nPrecision);
150
0
            if ( (nScale > 0) || (!_sCreatePattern.empty() && sCreateParams.indexOf(_sCreatePattern) != -1) )
151
0
                aSql.append(",");
152
0
        }
153
0
        if ( (nScale > 0) || ( !_sCreatePattern.empty() && sCreateParams.indexOf(_sCreatePattern) != -1 ) || bTimed )
154
0
            aSql.append(nScale);
155
156
0
        if ( nParenPos == -1 )
157
0
            aSql.append(")");
158
0
        else
159
0
        {
160
0
            if ( bTimed )
161
0
                aSql.append(")");
162
0
            else
163
0
                nParenPos = sTypeName.indexOf(')',nParenPos);
164
0
            aSql.append(sTypeName.subView(nParenPos));
165
0
        }
166
0
    }
167
0
    else
168
0
        aSql.append(sTypeName); // simply add the type name
169
170
0
    OUString aDefault = ::comphelper::getString(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_DEFAULTVALUE)));
171
0
    if ( !aDefault.isEmpty() )
172
0
    {
173
0
        aSql.append(" DEFAULT " + sPrefix + aDefault + sPostfix);
174
0
    } // if ( aDefault.getLength() )
175
176
0
    return aSql.makeStringAndClear();
177
0
}
178
179
OUString createStandardColumnPart(const Reference< XPropertySet >& xColProp,const Reference< XConnection>& _xConnection,ISQLStatementHelper* _pHelper,std::u16string_view _sCreatePattern)
180
0
{
181
0
    Reference<XDatabaseMetaData> xMetaData = _xConnection->getMetaData();
182
183
0
    ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap();
184
185
0
    bool bIsAutoIncrement = false;
186
0
    xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_ISAUTOINCREMENT))    >>= bIsAutoIncrement;
187
188
0
    const OUString sQuoteString = xMetaData->getIdentifierQuoteString();
189
0
    OUStringBuffer aSql(::dbtools::quoteName(sQuoteString,::comphelper::getString(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME)))));
190
191
    // check if the user enter a specific string to create autoincrement values
192
0
    OUString sAutoIncrementValue;
193
0
    Reference<XPropertySetInfo> xPropInfo = xColProp->getPropertySetInfo();
194
0
    if ( xPropInfo.is() && xPropInfo->hasPropertyByName(rPropMap.getNameByIndex(PROPERTY_ID_AUTOINCREMENTCREATION)) )
195
0
        xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_AUTOINCREMENTCREATION)) >>= sAutoIncrementValue;
196
197
0
    aSql.append(" " + createStandardTypePart(xColProp, _xConnection, _sCreatePattern));
198
199
0
    if(::comphelper::getINT32(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_ISNULLABLE))) == ColumnValue::NO_NULLS)
200
0
        aSql.append(" NOT NULL");
201
202
0
    if ( bIsAutoIncrement && !sAutoIncrementValue.isEmpty())
203
0
    {
204
0
        aSql.append(" " + sAutoIncrementValue);
205
0
    }
206
207
0
    if ( _pHelper )
208
0
        _pHelper->addComment(xColProp,aSql);
209
210
0
    return aSql.makeStringAndClear();
211
0
}
212
213
214
OUString createStandardCreateStatement(const Reference< XPropertySet >& descriptor,const Reference< XConnection>& _xConnection,ISQLStatementHelper* _pHelper,std::u16string_view _sCreatePattern)
215
0
{
216
0
    OUStringBuffer aSql("CREATE TABLE ");
217
0
    OUString sCatalog,sSchema,sTable,sComposedName;
218
219
0
    Reference<XDatabaseMetaData> xMetaData = _xConnection->getMetaData();
220
0
    ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap();
221
222
0
    descriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME))  >>= sCatalog;
223
0
    descriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCHEMANAME))   >>= sSchema;
224
0
    descriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME))         >>= sTable;
225
226
0
    sComposedName = ::dbtools::composeTableName( xMetaData, sCatalog, sSchema, sTable, true, ::dbtools::EComposeRule::InTableDefinitions );
227
0
    if ( sComposedName.isEmpty() )
228
0
        ::dbtools::throwFunctionSequenceException(_xConnection);
229
230
0
    aSql.append(sComposedName + " (");
231
232
    // columns
233
0
    Reference<XColumnsSupplier> xColumnSup(descriptor,UNO_QUERY);
234
0
    Reference<XIndexAccess> xColumns(xColumnSup->getColumns(),UNO_QUERY);
235
    // check if there are columns
236
0
    if(!xColumns.is() || !xColumns->getCount())
237
0
        ::dbtools::throwFunctionSequenceException(_xConnection);
238
239
0
    Reference< XPropertySet > xColProp;
240
241
0
    sal_Int32 nCount = xColumns->getCount();
242
0
    for(sal_Int32 i=0;i<nCount;++i)
243
0
    {
244
0
        if ( (xColumns->getByIndex(i) >>= xColProp) && xColProp.is() )
245
0
        {
246
0
            aSql.append(
247
0
                createStandardColumnPart(xColProp,_xConnection,_pHelper,_sCreatePattern)
248
0
                + ",");
249
0
        }
250
0
    }
251
0
    return aSql.makeStringAndClear();
252
0
}
253
namespace
254
{
255
    OUString generateColumnNames(const Reference<XIndexAccess>& _xColumns,const Reference<XDatabaseMetaData>& _xMetaData)
256
0
    {
257
0
        ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap();
258
259
0
        const OUString sQuote(_xMetaData->getIdentifierQuoteString());
260
0
        OUStringBuffer sSql( " (" );
261
0
        Reference< XPropertySet > xColProp;
262
263
0
        sal_Int32 nColCount  = _xColumns->getCount();
264
0
        for(sal_Int32 i=0;i<nColCount;++i)
265
0
        {
266
0
            if ( (_xColumns->getByIndex(i) >>= xColProp) && xColProp.is() )
267
0
                sSql.append( ::dbtools::quoteName(sQuote,::comphelper::getString(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME)))) +
268
0
                            ",");
269
0
        }
270
271
0
        if ( nColCount )
272
0
            sSql[sSql.getLength()-1] = ')';
273
0
        return sSql.makeStringAndClear();
274
0
    }
275
}
276
277
OUString createStandardKeyStatement(const Reference< XPropertySet >& descriptor,const Reference< XConnection>& _xConnection)
278
0
{
279
0
    Reference<XDatabaseMetaData> xMetaData = _xConnection->getMetaData();
280
0
    ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap();
281
282
0
    OUStringBuffer aSql;
283
    // keys
284
0
    Reference<XKeysSupplier> xKeySup(descriptor,UNO_QUERY);
285
0
    Reference<XIndexAccess> xKeys = xKeySup->getKeys();
286
0
    if ( xKeys.is() )
287
0
    {
288
0
        Reference< XPropertySet > xColProp;
289
0
        Reference<XIndexAccess> xColumns;
290
0
        Reference<XColumnsSupplier> xColumnSup;
291
0
        OUString sCatalog,sSchema,sTable,sComposedName;
292
0
        bool bPKey = false;
293
0
        for(sal_Int32 i=0;i<xKeys->getCount();++i)
294
0
        {
295
0
            if ( (xKeys->getByIndex(i) >>= xColProp) && xColProp.is() )
296
0
            {
297
298
0
                sal_Int32 nKeyType      = ::comphelper::getINT32(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_TYPE)));
299
300
0
                if ( nKeyType == KeyType::PRIMARY )
301
0
                {
302
0
                    if(bPKey)
303
0
                        ::dbtools::throwFunctionSequenceException(_xConnection);
304
305
0
                    bPKey = true;
306
0
                    xColumnSup.set(xColProp,UNO_QUERY);
307
0
                    xColumns.set(xColumnSup->getColumns(),UNO_QUERY);
308
0
                    if(!xColumns.is() || !xColumns->getCount())
309
0
                        ::dbtools::throwFunctionSequenceException(_xConnection);
310
311
0
                    aSql.append(" PRIMARY KEY " + generateColumnNames(xColumns,xMetaData));
312
0
                }
313
0
                else if(nKeyType == KeyType::UNIQUE)
314
0
                {
315
0
                    xColumnSup.set(xColProp,UNO_QUERY);
316
0
                    xColumns.set(xColumnSup->getColumns(),UNO_QUERY);
317
0
                    if(!xColumns.is() || !xColumns->getCount())
318
0
                        ::dbtools::throwFunctionSequenceException(_xConnection);
319
320
0
                    aSql.append(" UNIQUE " + generateColumnNames(xColumns,xMetaData));
321
0
                }
322
0
                else if(nKeyType == KeyType::FOREIGN)
323
0
                {
324
0
                    sal_Int32 nDeleteRule   = getINT32(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_DELETERULE)));
325
326
0
                    xColumnSup.set(xColProp,UNO_QUERY);
327
0
                    xColumns.set(xColumnSup->getColumns(),UNO_QUERY);
328
0
                    if(!xColumns.is() || !xColumns->getCount())
329
0
                        ::dbtools::throwFunctionSequenceException(_xConnection);
330
331
0
                    aSql.append(" FOREIGN KEY ");
332
0
                    OUString sRefTable = getString(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_REFERENCEDTABLE)));
333
0
                    ::dbtools::qualifiedNameComponents(xMetaData,
334
0
                                                        sRefTable,
335
0
                                                        sCatalog,
336
0
                                                        sSchema,
337
0
                                                        sTable,
338
0
                                                        ::dbtools::EComposeRule::InDataManipulation);
339
0
                    sComposedName = ::dbtools::composeTableName( xMetaData, sCatalog, sSchema, sTable, true, ::dbtools::EComposeRule::InTableDefinitions );
340
341
342
0
                    if ( sComposedName.isEmpty() )
343
0
                        ::dbtools::throwFunctionSequenceException(_xConnection);
344
345
0
                    aSql.append(generateColumnNames(xColumns,xMetaData));
346
347
0
                    switch(nDeleteRule)
348
0
                    {
349
0
                        case KeyRule::CASCADE:
350
0
                            aSql.append(" ON DELETE CASCADE ");
351
0
                            break;
352
0
                        case KeyRule::RESTRICT:
353
0
                            aSql.append(" ON DELETE RESTRICT ");
354
0
                            break;
355
0
                        case KeyRule::SET_NULL:
356
0
                            aSql.append(" ON DELETE SET NULL ");
357
0
                            break;
358
0
                        case KeyRule::SET_DEFAULT:
359
0
                            aSql.append(" ON DELETE SET DEFAULT ");
360
0
                            break;
361
0
                        default:
362
0
                            ;
363
0
                    }
364
0
                }
365
0
            }
366
0
        }
367
0
    }
368
369
0
    if ( !aSql.isEmpty() )
370
0
    {
371
0
        if ( aSql[aSql.getLength() - 1] == ',' )
372
0
            aSql[aSql.getLength() - 1] = ')';
373
0
        else
374
0
            aSql.append(")");
375
0
    }
376
377
0
    return aSql.makeStringAndClear();
378
379
0
}
380
381
OUString createSqlCreateTableStatement(  const Reference< XPropertySet >& descriptor,
382
                                         const Reference< XConnection>& _xConnection)
383
0
{
384
0
    OUString aSql = ::dbtools::createStandardCreateStatement(descriptor,_xConnection,nullptr,{});
385
0
    const OUString sKeyStmt = ::dbtools::createStandardKeyStatement(descriptor,_xConnection);
386
0
    if ( !sKeyStmt.isEmpty() )
387
0
        aSql += sKeyStmt;
388
0
    else
389
0
    {
390
0
        if ( aSql.endsWith(",") )
391
0
            aSql = aSql.replaceAt(aSql.getLength()-1, 1, u")");
392
0
        else
393
0
            aSql += ")";
394
0
    }
395
0
    return aSql;
396
0
}
397
namespace
398
{
399
    Reference<XPropertySet> lcl_createSDBCXColumn(const Reference<XNameAccess>& _xPrimaryKeyColumns,
400
                                          const Reference<XConnection>& _xConnection,
401
                                          const Any& _aCatalog,
402
                                          const OUString& _aSchema,
403
                                          const OUString& _aTable,
404
                                          const OUString& _rQueryName,
405
                                          const OUString& _rName,
406
                                          bool _bCase,
407
                                          bool _bQueryForInfo,
408
                                          bool _bIsAutoIncrement,
409
                                          bool _bIsCurrency,
410
                                          sal_Int32 _nDataType)
411
0
    {
412
0
        Reference<XDatabaseMetaData> xMetaData = _xConnection->getMetaData();
413
0
        Reference< XResultSet > xResult = xMetaData->getColumns(_aCatalog, _aSchema, _aTable, _rQueryName);
414
0
        OUString sCatalog;
415
0
        _aCatalog >>= sCatalog;
416
417
0
        if ( !xResult.is() )
418
0
            return nullptr;
419
420
0
        rtl::Reference<connectivity::sdbcx::OColumn> xProp;
421
0
        UStringMixEqual aMixCompare(_bCase);
422
0
        Reference< XRow > xRow(xResult,UNO_QUERY);
423
0
        while( xResult->next() )
424
0
        {
425
0
            if ( aMixCompare(xRow->getString(4),_rName) )
426
0
            {
427
0
                sal_Int32       nField5 = xRow->getInt(5);
428
0
                OUString aField6 = xRow->getString(6);
429
0
                sal_Int32       nField7 = xRow->getInt(7)
430
0
                            ,   nField9 = xRow->getInt(9)
431
0
                            ,   nField11= xRow->getInt(11);
432
0
                OUString sField12 = xRow->getString(12),
433
0
                                sField13 = xRow->getString(13);
434
0
                ::comphelper::disposeComponent(xRow);
435
436
0
                bool bAutoIncrement = _bIsAutoIncrement
437
0
                        ,bIsCurrency    = _bIsCurrency;
438
0
                if ( _bQueryForInfo )
439
0
                {
440
0
                    const OUString sQuote = xMetaData->getIdentifierQuoteString();
441
0
                    OUString sQuotedName  = ::dbtools::quoteName(sQuote,_rName);
442
0
                    OUString sComposedName = composeTableNameForSelect(_xConnection, getString( _aCatalog ), _aSchema, _aTable );
443
444
0
                    ColumnInformationMap aInfo((UStringMixLess(_bCase)));
445
0
                    collectColumnInformation(_xConnection,sComposedName,sQuotedName,aInfo);
446
0
                    ColumnInformationMap::const_iterator aIter = aInfo.begin();
447
0
                    if ( aIter != aInfo.end() )
448
0
                    {
449
0
                        bAutoIncrement  = aIter->second.first.first;
450
0
                        bIsCurrency     = aIter->second.first.second;
451
0
                        if ( DataType::OTHER == nField5 )
452
0
                            nField5     = aIter->second.second;
453
0
                    }
454
0
                }
455
0
                else if ( DataType::OTHER == nField5 )
456
0
                    nField5 = _nDataType;
457
458
0
                if ( nField11 != ColumnValue::NO_NULLS )
459
0
                {
460
0
                    try
461
0
                    {
462
0
                        if ( _xPrimaryKeyColumns.is() )
463
0
                        {
464
0
                            if ( _xPrimaryKeyColumns->hasByName(_rName) )
465
0
                                nField11 = ColumnValue::NO_NULLS;
466
467
0
                        }
468
0
                        else
469
0
                        {
470
0
                            Reference< XResultSet > xPKeys = xMetaData->getPrimaryKeys( _aCatalog, _aSchema, _aTable );
471
0
                            Reference< XRow > xPKeyRow( xPKeys, UNO_QUERY_THROW );
472
0
                            while( xPKeys->next() ) // there can be only one primary key
473
0
                            {
474
0
                                OUString sKeyColumn = xPKeyRow->getString(4);
475
0
                                if ( aMixCompare(_rName,sKeyColumn) )
476
0
                                {
477
0
                                    nField11 = ColumnValue::NO_NULLS;
478
0
                                    break;
479
0
                                }
480
0
                            }
481
0
                        }
482
0
                    }
483
0
                    catch(SQLException&)
484
0
                    {
485
0
                        TOOLS_WARN_EXCEPTION( "connectivity.commontools", "lcl_createSDBCXColumn" );
486
0
                    }
487
0
                }
488
489
0
                xProp = new connectivity::sdbcx::OColumn(_rName,
490
0
                                            aField6,
491
0
                                            sField13,
492
0
                                            sField12,
493
0
                                            nField11,
494
0
                                            nField7,
495
0
                                            nField9,
496
0
                                            nField5,
497
0
                                            bAutoIncrement,
498
0
                                            false,
499
0
                                            bIsCurrency,
500
0
                                            _bCase,
501
0
                                            sCatalog,
502
0
                                            _aSchema,
503
0
                                            _aTable);
504
505
0
                break;
506
0
            }
507
0
        }
508
509
0
        return xProp;
510
0
    }
511
512
    Reference< XModel> lcl_getXModel(const Reference< XInterface>& _xIface)
513
0
    {
514
0
        Reference< XInterface > xParent = _xIface;
515
0
        Reference< XModel > xModel(xParent,UNO_QUERY);
516
0
        while( xParent.is() && !xModel.is() )
517
0
        {
518
0
            Reference<XChild> xChild(xParent,UNO_QUERY);
519
0
            xParent.set(xChild.is() ? xChild->getParent() : Reference< XInterface >(),UNO_QUERY);
520
0
            xModel.set(xParent,UNO_QUERY);
521
0
        }
522
0
        return xModel;
523
0
    }
524
}
525
526
Reference<XPropertySet> createSDBCXColumn(const Reference<XPropertySet>& _xTable,
527
                                          const Reference<XConnection>& _xConnection,
528
                                          const OUString& _rName,
529
                                          bool _bCase,
530
                                          bool _bQueryForInfo,
531
                                          bool _bIsAutoIncrement,
532
                                          bool _bIsCurrency,
533
                                          sal_Int32 _nDataType)
534
0
{
535
0
    Reference<XPropertySet> xProp;
536
0
    OSL_ENSURE(_xTable.is(),"Table is NULL!");
537
0
    if ( !_xTable.is() )
538
0
        return xProp;
539
540
0
    ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap();
541
0
    Any aCatalog = _xTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME));
542
0
    OUString sCatalog;
543
0
    aCatalog >>= sCatalog;
544
545
0
    OUString aSchema, aTable;
546
0
    _xTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCHEMANAME))  >>= aSchema;
547
0
    _xTable->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME))        >>= aTable;
548
549
0
    Reference<XNameAccess> xPrimaryKeyColumns = getPrimaryKeyColumns_throw(_xTable);
550
551
0
    xProp = lcl_createSDBCXColumn(xPrimaryKeyColumns,_xConnection,aCatalog, aSchema, aTable, _rName,_rName,_bCase,_bQueryForInfo,_bIsAutoIncrement,_bIsCurrency,_nDataType);
552
0
    if ( !xProp.is() )
553
0
    {
554
0
        xProp = lcl_createSDBCXColumn(xPrimaryKeyColumns,_xConnection,aCatalog, aSchema, aTable, u"%"_ustr,_rName,_bCase,_bQueryForInfo,_bIsAutoIncrement,_bIsCurrency,_nDataType);
555
0
        if ( !xProp.is() )
556
0
            xProp = new connectivity::sdbcx::OColumn(_rName,
557
0
                                                OUString(),OUString(),OUString(),
558
0
                                                ColumnValue::NULLABLE_UNKNOWN,
559
0
                                                0,
560
0
                                                0,
561
0
                                                DataType::VARCHAR,
562
0
                                                _bIsAutoIncrement,
563
0
                                                false,
564
0
                                                _bIsCurrency,
565
0
                                                _bCase,
566
0
                                                sCatalog,
567
0
                                                aSchema,
568
0
                                                aTable);
569
570
0
    }
571
572
0
    return xProp;
573
0
}
574
575
576
bool getBooleanDataSourceSetting( const Reference< XConnection >& _rxConnection, const char* _pAsciiSettingName )
577
0
{
578
0
    return getBooleanDataSourceSetting(_rxConnection, OUString::createFromAscii( _pAsciiSettingName ));
579
0
}
580
581
bool getBooleanDataSourceSetting( const Reference< XConnection >& _rxConnection, const OUString & rSettingName )
582
0
{
583
0
    bool bValue( false );
584
0
    try
585
0
    {
586
0
        Reference< XPropertySet> xDataSourceProperties( findDataSource( _rxConnection ), UNO_QUERY );
587
0
        OSL_ENSURE( xDataSourceProperties.is(), "::dbtools::getBooleanDataSourceSetting: somebody is using this with a non-SDB-level connection!" );
588
0
        if ( xDataSourceProperties.is() )
589
0
        {
590
0
            Reference< XPropertySet > xSettings(
591
0
                xDataSourceProperties->getPropertyValue(u"Settings"_ustr),
592
0
                UNO_QUERY_THROW
593
0
            );
594
0
            OSL_VERIFY( xSettings->getPropertyValue( rSettingName ) >>= bValue );
595
0
        }
596
0
    }
597
0
    catch( const Exception& )
598
0
    {
599
0
        DBG_UNHANDLED_EXCEPTION("connectivity.commontools");
600
0
    }
601
0
    return bValue;
602
0
}
603
604
bool getDataSourceSetting( const Reference< XInterface >& _xChild, const OUString& _sAsciiSettingsName,
605
    Any& /* [out] */ _rSettingsValue )
606
35.3k
{
607
35.3k
    bool bIsPresent = false;
608
35.3k
    try
609
35.3k
    {
610
35.3k
        const Reference< XPropertySet> xDataSourceProperties( findDataSource( _xChild ), UNO_QUERY );
611
35.3k
        if ( !xDataSourceProperties.is() )
612
35.3k
            return false;
613
614
0
        const Reference< XPropertySet > xSettings(
615
0
                xDataSourceProperties->getPropertyValue(u"Settings"_ustr),
616
0
                UNO_QUERY_THROW
617
0
            );
618
619
0
        _rSettingsValue = xSettings->getPropertyValue( _sAsciiSettingsName );
620
0
        bIsPresent = true;
621
0
    }
622
35.3k
    catch( const Exception& )
623
35.3k
    {
624
0
        bIsPresent = false;
625
0
    }
626
0
    return bIsPresent;
627
35.3k
}
628
629
bool isDataSourcePropertyEnabled(const Reference<XInterface>& _xProp, const OUString& _sProperty, bool _bDefault)
630
46.0k
{
631
46.0k
    bool bEnabled = _bDefault;
632
46.0k
    try
633
46.0k
    {
634
46.0k
        Reference< XPropertySet> xProp(findDataSource(_xProp),UNO_QUERY);
635
46.0k
        if ( xProp.is() )
636
0
        {
637
0
            Sequence< PropertyValue > aInfo;
638
0
            xProp->getPropertyValue(u"Info"_ustr) >>= aInfo;
639
0
            const PropertyValue* pValue =std::find_if(std::cbegin(aInfo),
640
0
                                                std::cend(aInfo),
641
0
                                                [&_sProperty](const PropertyValue& lhs)
642
0
                                                { return lhs.Name == _sProperty; });
643
0
            if ( pValue != std::cend(aInfo) )
644
0
                pValue->Value >>= bEnabled;
645
0
        }
646
46.0k
    }
647
46.0k
    catch(SQLException&)
648
46.0k
    {
649
0
        DBG_UNHANDLED_EXCEPTION("connectivity.commontools");
650
0
    }
651
46.0k
    return bEnabled;
652
46.0k
}
653
654
Reference< XTablesSupplier> getDataDefinitionByURLAndConnection(
655
            const OUString& _rsUrl,
656
            const Reference< XConnection>& _xConnection,
657
            const Reference< XComponentContext >& _rxContext)
658
0
{
659
0
    Reference< XTablesSupplier> xTablesSup;
660
0
    try
661
0
    {
662
0
        Reference< XDriverManager2 > xManager = DriverManager::create( _rxContext );
663
0
        Reference< XDataDefinitionSupplier > xSupp( xManager->getDriverByURL( _rsUrl ), UNO_QUERY );
664
665
0
        if ( xSupp.is() )
666
0
        {
667
0
            xTablesSup = xSupp->getDataDefinitionByConnection( _xConnection );
668
0
            OSL_ENSURE(xTablesSup.is(),"No table supplier!");
669
0
        }
670
0
    }
671
0
    catch( const Exception& )
672
0
    {
673
0
        DBG_UNHANDLED_EXCEPTION("connectivity.commontools");
674
0
    }
675
0
    return xTablesSup;
676
0
}
677
678
679
sal_Int32 getTablePrivileges(const Reference< XDatabaseMetaData>& _xMetaData,
680
                             const OUString& _sCatalog,
681
                             const OUString& _sSchema,
682
                             const OUString& _sTable)
683
0
{
684
0
    OSL_ENSURE(_xMetaData.is(),"Invalid metadata!");
685
0
    sal_Int32 nPrivileges = 0;
686
0
    try
687
0
    {
688
0
        Any aVal;
689
0
        if(!_sCatalog.isEmpty())
690
0
            aVal <<= _sCatalog;
691
0
        Reference< XResultSet > xPrivileges = _xMetaData->getTablePrivileges(aVal, _sSchema, _sTable);
692
0
        Reference< XRow > xCurrentRow(xPrivileges, UNO_QUERY);
693
694
0
        const OUString sUserWorkingFor = _xMetaData->getUserName();
695
0
        static const char sSELECT[] = "SELECT";
696
0
        static const char sINSERT[] = "INSERT";
697
0
        static const char sUPDATE[] = "UPDATE";
698
0
        static const char sDELETE[] = "DELETE";
699
0
        static const char sREAD[] = "READ";
700
0
        static const char sCREATE[] = "CREATE";
701
0
        static const char sALTER[] = "ALTER";
702
0
        static const char sREFERENCE[] = "REFERENCE";
703
0
        static const char sDROP[] = "DROP";
704
705
0
        if ( xCurrentRow.is() )
706
0
        {
707
            // after creation the set is positioned before the first record, per definition
708
0
            OUString sPrivilege, sGrantee;
709
0
            while ( xPrivileges->next() )
710
0
            {
711
0
                sGrantee    = xCurrentRow->getString(5);
712
0
                sPrivilege  = xCurrentRow->getString(6);
713
714
0
                if (!sUserWorkingFor.equalsIgnoreAsciiCase(sGrantee))
715
0
                    continue;
716
717
0
                if (sPrivilege.equalsIgnoreAsciiCase(sSELECT))
718
0
                    nPrivileges |= Privilege::SELECT;
719
0
                else if (sPrivilege.equalsIgnoreAsciiCase(sINSERT))
720
0
                    nPrivileges |= Privilege::INSERT;
721
0
                else if (sPrivilege.equalsIgnoreAsciiCase(sUPDATE))
722
0
                    nPrivileges |= Privilege::UPDATE;
723
0
                else if (sPrivilege.equalsIgnoreAsciiCase(sDELETE))
724
0
                    nPrivileges |= Privilege::DELETE;
725
0
                else if (sPrivilege.equalsIgnoreAsciiCase(sREAD))
726
0
                    nPrivileges |= Privilege::READ;
727
0
                else if (sPrivilege.equalsIgnoreAsciiCase(sCREATE))
728
0
                    nPrivileges |= Privilege::CREATE;
729
0
                else if (sPrivilege.equalsIgnoreAsciiCase(sALTER))
730
0
                    nPrivileges |= Privilege::ALTER;
731
0
                else if (sPrivilege.equalsIgnoreAsciiCase(sREFERENCE))
732
0
                    nPrivileges |= Privilege::REFERENCE;
733
0
                else if (sPrivilege.equalsIgnoreAsciiCase(sDROP))
734
0
                    nPrivileges |= Privilege::DROP;
735
0
            }
736
0
        }
737
0
        disposeComponent(xPrivileges);
738
739
        // Some drivers put a table privilege as soon as any column has the privilege,
740
        // some drivers only if all columns have the privilege.
741
        // To unify the situation, collect column privileges here, too.
742
0
        Reference< XResultSet > xColumnPrivileges = _xMetaData->getColumnPrivileges(aVal, _sSchema, _sTable, u"%"_ustr);
743
0
        Reference< XRow > xColumnCurrentRow(xColumnPrivileges, UNO_QUERY);
744
0
        if ( xColumnCurrentRow.is() )
745
0
        {
746
            // after creation the set is positioned before the first record, per definition
747
0
            OUString sPrivilege, sGrantee;
748
0
            while ( xColumnPrivileges->next() )
749
0
            {
750
0
                sGrantee    = xColumnCurrentRow->getString(6);
751
0
                sPrivilege  = xColumnCurrentRow->getString(7);
752
753
0
                if (!sUserWorkingFor.equalsIgnoreAsciiCase(sGrantee))
754
0
                    continue;
755
756
0
                if (sPrivilege.equalsIgnoreAsciiCase(sSELECT))
757
0
                    nPrivileges |= Privilege::SELECT;
758
0
                else if (sPrivilege.equalsIgnoreAsciiCase(sINSERT))
759
0
                    nPrivileges |= Privilege::INSERT;
760
0
                else if (sPrivilege.equalsIgnoreAsciiCase(sUPDATE))
761
0
                    nPrivileges |= Privilege::UPDATE;
762
0
                else if (sPrivilege.equalsIgnoreAsciiCase(sDELETE))
763
0
                    nPrivileges |= Privilege::DELETE;
764
0
                else if (sPrivilege.equalsIgnoreAsciiCase(sREAD))
765
0
                    nPrivileges |= Privilege::READ;
766
0
                else if (sPrivilege.equalsIgnoreAsciiCase(sCREATE))
767
0
                    nPrivileges |= Privilege::CREATE;
768
0
                else if (sPrivilege.equalsIgnoreAsciiCase(sALTER))
769
0
                    nPrivileges |= Privilege::ALTER;
770
0
                else if (sPrivilege.equalsIgnoreAsciiCase(sREFERENCE))
771
0
                    nPrivileges |= Privilege::REFERENCE;
772
0
                else if (sPrivilege.equalsIgnoreAsciiCase(sDROP))
773
0
                    nPrivileges |= Privilege::DROP;
774
0
            }
775
0
        }
776
0
        disposeComponent(xColumnPrivileges);
777
0
    }
778
0
    catch(const SQLException& e)
779
0
    {
780
        // some drivers don't support any privileges so we assume that we are allowed to do all we want :-)
781
0
        if(e.SQLState == "IM001")
782
0
            nPrivileges |=  Privilege::DROP         |
783
0
                            Privilege::REFERENCE    |
784
0
                            Privilege::ALTER        |
785
0
                            Privilege::CREATE       |
786
0
                            Privilege::READ         |
787
0
                            Privilege::DELETE       |
788
0
                            Privilege::UPDATE       |
789
0
                            Privilege::INSERT       |
790
0
                            Privilege::SELECT;
791
0
        else
792
0
            OSL_FAIL("Could not collect the privileges !");
793
0
    }
794
0
    return nPrivileges;
795
0
}
796
797
// we need some more information about the column
798
void collectColumnInformation(const Reference< XConnection>& _xConnection,
799
                              std::u16string_view _sComposedName,
800
                              std::u16string_view _rName,
801
                              ColumnInformationMap& _rInfo)
802
11.4k
{
803
11.4k
    OUString sSelect = OUString::Concat("SELECT ") + _rName +
804
11.4k
        " FROM " + _sComposedName +
805
11.4k
        " WHERE 0 = 1";
806
807
11.4k
    try
808
11.4k
    {
809
11.4k
        ::utl::SharedUNOComponent< XStatement > xStmt( _xConnection->createStatement() );
810
11.4k
        Reference< XPropertySet > xStatementProps( xStmt, UNO_QUERY_THROW );
811
11.4k
        xStatementProps->setPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_ESCAPEPROCESSING ), Any( false ) );
812
11.4k
        Reference< XResultSet > xResult( xStmt->executeQuery( sSelect ), UNO_SET_THROW );
813
11.4k
        Reference< XResultSetMetaDataSupplier > xSuppMeta( xResult, UNO_QUERY_THROW );
814
11.4k
        Reference< XResultSetMetaData > xMeta( xSuppMeta->getMetaData(), UNO_SET_THROW );
815
816
11.4k
        sal_Int32 nCount = xMeta->getColumnCount();
817
11.4k
        OSL_ENSURE( nCount != 0, "::dbtools::collectColumnInformation: result set has empty (column-less) meta data!" );
818
362k
        for (sal_Int32 i=1; i <= nCount ; ++i)
819
350k
        {
820
350k
            _rInfo.emplace( xMeta->getColumnName(i),
821
350k
                ColumnInformation(TBoolPair(xMeta->isAutoIncrement(i),xMeta->isCurrency(i)),xMeta->getColumnType(i)));
822
350k
        }
823
11.4k
    }
824
11.4k
    catch( const Exception& )
825
11.4k
    {
826
0
        DBG_UNHANDLED_EXCEPTION("connectivity.commontools");
827
0
    }
828
11.4k
}
829
830
831
bool isEmbeddedInDatabase( const Reference< XInterface >& _rxComponent, Reference< XConnection >& _rxActualConnection )
832
0
{
833
0
    bool bIsEmbedded = false;
834
0
    try
835
0
    {
836
0
        Reference< XModel > xModel = lcl_getXModel( _rxComponent );
837
838
0
        if ( xModel.is() )
839
0
        {
840
0
            for (auto& arg : xModel->getArgs())
841
0
            {
842
0
                if (arg.Name == "ComponentData")
843
0
                {
844
0
                    Sequence<PropertyValue> aDocumentContext;
845
0
                    arg.Value >>= aDocumentContext;
846
0
                    for (auto& item : aDocumentContext)
847
0
                    {
848
0
                        if (item.Name == "ActiveConnection" && (item.Value >>= _rxActualConnection))
849
0
                        {
850
0
                            bIsEmbedded = true;
851
0
                            break;
852
0
                        }
853
0
                    }
854
0
                    break;
855
0
                }
856
0
            }
857
0
        }
858
0
    }
859
0
    catch(Exception&)
860
0
    {
861
        // not interested in
862
0
    }
863
0
    return bIsEmbedded;
864
0
}
865
866
namespace
867
{
868
    OUString lcl_getEncodingName( rtl_TextEncoding _eEncoding )
869
0
    {
870
0
        OUString sEncodingName;
871
872
0
        OCharsetMap aCharsets;
873
0
        OCharsetMap::CharsetIterator aEncodingPos = aCharsets.find( _eEncoding );
874
0
        OSL_ENSURE( aEncodingPos != aCharsets.end(), "lcl_getEncodingName: *which* encoding?" );
875
0
        if ( aEncodingPos != aCharsets.end() )
876
0
            sEncodingName = (*aEncodingPos).getIanaName();
877
878
0
        return sEncodingName;
879
0
    }
880
}
881
882
883
sal_Int32 DBTypeConversion::convertUnicodeString( const OUString& _rSource, OString& _rDest, rtl_TextEncoding _eEncoding )
884
0
{
885
0
    if ( !rtl_convertUStringToString( &_rDest.pData, _rSource.getStr(), _rSource.getLength(),
886
0
            _eEncoding,
887
0
            RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
888
0
            RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE |
889
0
            RTL_UNICODETOTEXT_FLAGS_PRIVATE_MAPTO0 )
890
0
        )
891
0
    {
892
0
        SharedResources aResources;
893
0
        OUString sMessage = aResources.getResourceStringWithSubstitution( STR_CANNOT_CONVERT_STRING,
894
0
            "$string$", _rSource,
895
0
            "$charset$",  lcl_getEncodingName( _eEncoding )
896
0
        );
897
898
0
        throw SQLException(
899
0
            sMessage,
900
0
            nullptr,
901
0
            u"22018"_ustr,
902
0
            22018,
903
0
            Any()
904
0
        );
905
0
    }
906
907
0
    return _rDest.getLength();
908
0
}
909
910
911
sal_Int32 DBTypeConversion::convertUnicodeStringToLength( const OUString& _rSource, OString&  _rDest,
912
   sal_Int32 _nMaxLen, rtl_TextEncoding _eEncoding )
913
0
{
914
0
    sal_Int32 nLen = convertUnicodeString( _rSource, _rDest, _eEncoding );
915
0
    if ( nLen > _nMaxLen )
916
0
    {
917
0
        SharedResources aResources;
918
0
        OUString sMessage = aResources.getResourceStringWithSubstitution( STR_STRING_LENGTH_EXCEEDED,
919
0
            "$string$", _rSource,
920
0
            "$maxlen$", OUString::number( _nMaxLen ),
921
0
            "$charset$", lcl_getEncodingName( _eEncoding )
922
0
        );
923
924
0
        throw SQLException(
925
0
            sMessage,
926
0
            nullptr,
927
0
            u"22001"_ustr,
928
0
            22001,
929
0
            Any()
930
0
        );
931
0
    }
932
933
0
    return nLen;
934
0
}
935
936
OUString getDefaultReportEngineServiceName(const Reference< XComponentContext >& _rxORB)
937
0
{
938
0
    ::utl::OConfigurationTreeRoot aReportEngines = ::utl::OConfigurationTreeRoot::createWithComponentContext(
939
0
        _rxORB, u"org.openoffice.Office.DataAccess/ReportEngines"_ustr, -1, ::utl::OConfigurationTreeRoot::CM_READONLY);
940
941
0
    if ( aReportEngines.isValid() )
942
0
    {
943
0
        OUString sDefaultReportEngineName;
944
0
        aReportEngines.getNodeValue(u"DefaultReportEngine"_ustr) >>= sDefaultReportEngineName;
945
0
        if ( !sDefaultReportEngineName.isEmpty() )
946
0
        {
947
0
            ::utl::OConfigurationNode aReportEngineNames = aReportEngines.openNode(u"ReportEngineNames"_ustr);
948
0
            if ( aReportEngineNames.isValid() )
949
0
            {
950
0
                ::utl::OConfigurationNode aReportEngine = aReportEngineNames.openNode(sDefaultReportEngineName);
951
0
                if ( aReportEngine.isValid() )
952
0
                {
953
0
                    OUString sRet;
954
0
                    aReportEngine.getNodeValue(u"ServiceName"_ustr) >>= sRet;
955
0
                    return sRet;
956
0
                }
957
0
            }
958
0
        }
959
0
        else
960
0
            return u"org.libreoffice.report.pentaho.SOReportJobFactory"_ustr;
961
0
    }
962
0
    else
963
0
        return u"org.libreoffice.report.pentaho.SOReportJobFactory"_ustr;
964
0
    return OUString();
965
0
}
966
967
bool isAggregateColumn(const Reference< XSingleSelectQueryComposer > &_xParser, const Reference< XPropertySet > &_xField)
968
0
{
969
0
    OUString sName;
970
0
    _xField->getPropertyValue(u"Name"_ustr) >>= sName;
971
0
    Reference< XColumnsSupplier > xColumnsSupplier(_xParser, UNO_QUERY);
972
0
    Reference< css::container::XNameAccess >  xCols;
973
0
    if (xColumnsSupplier.is())
974
0
        xCols = xColumnsSupplier->getColumns();
975
976
0
    return isAggregateColumn(xCols, sName);
977
0
}
978
979
bool isAggregateColumn(const Reference< XNameAccess > &_xColumns, const OUString &_sName)
980
0
{
981
0
    if ( _xColumns.is() && _xColumns->hasByName(_sName) )
982
0
    {
983
0
        Reference<XPropertySet> xProp(_xColumns->getByName(_sName),UNO_QUERY);
984
0
        assert(xProp.is());
985
0
        return isAggregateColumn( xProp );
986
0
    }
987
0
    return false;
988
0
}
989
990
bool isAggregateColumn( const Reference< XPropertySet > &_xColumn )
991
0
{
992
0
    bool bAgg(false);
993
994
0
    static constexpr OUString sAgg = u"AggregateFunction"_ustr;
995
0
    if ( _xColumn->getPropertySetInfo()->hasPropertyByName(sAgg) )
996
0
        _xColumn->getPropertyValue(sAgg) >>= bAgg;
997
998
0
    return bAgg;
999
0
}
1000
1001
1002
}   // namespace dbtools
1003
1004
1005
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */