• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 13836648005

13 Mar 2025 02:09PM UTC coverage: 70.436% (-0.01%) from 70.446%
13836648005

push

github

web-flow
New Transform type: Homography (#11949)

Add new transform type, Homography.
Add functions to compute homography from a list of GCPs.
Add functions to serialize and deserialize a homography
Automatically select homography transfrom when there are 4 or 5 GCPs present.

Fixes #11940

231 of 274 new or added lines in 2 files covered. (84.31%)

16257 existing lines in 42 files now uncovered.

553736 of 786159 relevant lines covered (70.44%)

221595.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.09
/ogr/ogrsf_frmts/hana/ogrhanadatasource.cpp
1
/******************************************************************************
2
 *
3
 * Project:  SAP HANA Spatial Driver
4
 * Purpose:  OGRHanaDataSource class implementation
5
 * Author:   Maxim Rylov
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2020, SAP SE
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12

13
#include "ogr_hana.h"
14
#include "ogrhanautils.h"
15
#include "ogrhanadrivercore.h"
16

17
#include "odbc/Connection.h"
18
#include "odbc/Environment.h"
19
#include "odbc/Exception.h"
20
#include "odbc/DatabaseMetaData.h"
21
#include "odbc/PreparedStatement.h"
22
#include "odbc/ResultSet.h"
23
#include "odbc/ResultSetMetaData.h"
24
#include "odbc/Statement.h"
25
#include "odbc/StringConverter.h"
26

27
#include <algorithm>
28
#include <iterator>
29
#include <memory>
30
#include <sstream>
31
#include <vector>
32

33
using namespace OGRHANA;
34

35
namespace
36
{
37

38
CPLString BuildConnectionString(char **openOptions)
65✔
39
{
40
    // See notes for constructing connection string for HANA
41
    // https://help.sap.com/docs/SAP_HANA_CLIENT/f1b440ded6144a54ada97ff95dac7adf/7cab593774474f2f8db335710b2f5c50.html
42

43
    std::vector<CPLString> params;
130✔
44
    bool isValid = true;
65✔
45
    const CPLString specialChars("[]{}(),;?*=!@");
130✔
46

47
    auto getOptValue = [&](const char *optionName, bool mandatory = false)
1,051✔
48
    {
49
        const char *paramValue =
50
            CSLFetchNameValueDef(openOptions, optionName, nullptr);
1,051✔
51
        if (mandatory && paramValue == nullptr)
1,051✔
52
        {
53
            isValid = false;
9✔
54
            CPLError(CE_Failure, CPLE_AppDefined,
9✔
55
                     "Mandatory connection parameter '%s' is missing.",
56
                     optionName);
57
        }
58
        return paramValue;
1,051✔
59
    };
65✔
60

61
    auto addParameter = [&](const char *paramName, const char *paramValue)
976✔
62
    {
63
        if (paramValue == nullptr)
976✔
64
            return;
421✔
65

66
        CPLString value(paramValue);
1,110✔
67
        if (value.find_first_of(specialChars) != std::string::npos)
555✔
68
        {
69
            value.replaceAll("}", "}}");
56✔
70
            params.push_back(CPLString(paramName) + "={" + value + "}");
56✔
71
        }
72
        else
73
        {
74
            params.push_back(CPLString(paramName) + "=" + value);
499✔
75
        }
76
    };
65✔
77

78
    auto addOptParameter = [&](const char *optionName, const char *paramName,
720✔
79
                               bool mandatory = false)
80
    {
81
        const char *paramValue = getOptValue(optionName, mandatory);
720✔
82
        addParameter(paramName, paramValue);
720✔
83
    };
785✔
84

85
    auto checkIgnoredOptParameter = [&](const char *optionName)
12✔
86
    {
87
        if (getOptValue(optionName))
12✔
88
            CPLError(CE_Failure, CPLE_AppDefined,
×
89
                     "Connection parameter '%s' is ignored in the current "
90
                     "combination.",
91
                     optionName);
92
    };
77✔
93

94
    if (const char *paramUserStoreKey =
65✔
95
            getOptValue(OGRHanaOpenOptionsConstants::USER_STORE_KEY))
65✔
96
    {
97
        addOptParameter(OGRHanaOpenOptionsConstants::DRIVER, "DRIVER", true);
×
98
        CPLString node = CPLString().Printf("@%s", paramUserStoreKey);
×
99
        addParameter("SERVERNODE", node.c_str());
×
100

101
        checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::DSN);
×
102
        checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::HOST);
×
103
        checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::PORT);
×
104
        checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::DATABASE);
×
105
        checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::USER);
×
106
        checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::PASSWORD);
×
107
    }
108
    else if (const char *paramDSN =
65✔
109
                 getOptValue(OGRHanaOpenOptionsConstants::DSN))
65✔
110
    {
111
        addParameter(OGRHanaOpenOptionsConstants::DSN, paramDSN);
3✔
112
        addOptParameter(OGRHanaOpenOptionsConstants::USER, "UID", true);
3✔
113
        addOptParameter(OGRHanaOpenOptionsConstants::PASSWORD, "PWD", true);
3✔
114

115
        checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::DRIVER);
3✔
116
        checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::HOST);
3✔
117
        checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::PORT);
3✔
118
        checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::DATABASE);
3✔
119
    }
120
    else
121
    {
122
        addOptParameter(OGRHanaOpenOptionsConstants::DRIVER, "DRIVER", true);
62✔
123
        const char *paramHost =
124
            getOptValue(OGRHanaOpenOptionsConstants::HOST, true);
62✔
125
        const char *paramPort =
126
            getOptValue(OGRHanaOpenOptionsConstants::PORT, true);
62✔
127
        if (paramHost != nullptr && paramPort != nullptr)
62✔
128
        {
129
            CPLString node = CPLString().Printf("%s:%s", paramHost, paramPort);
120✔
130
            addParameter("SERVERNODE", node.c_str());
60✔
131
        }
132
        addOptParameter(OGRHanaOpenOptionsConstants::USER, "UID", true);
62✔
133
        addOptParameter(OGRHanaOpenOptionsConstants::PASSWORD, "PWD", true);
62✔
134
        addOptParameter(OGRHanaOpenOptionsConstants::DATABASE, "DATABASENAME");
62✔
135
    }
136

137
    if (const char *paramSchema =
65✔
138
            getOptValue(OGRHanaOpenOptionsConstants::SCHEMA, true))
65✔
139
    {
140
        CPLString schema = CPLString().Printf("\"%s\"", paramSchema);
126✔
141
        addParameter("CURRENTSCHEMA", schema.c_str());
63✔
142
    }
143

144
    if (CPLFetchBool(openOptions, OGRHanaOpenOptionsConstants::ENCRYPT, false))
65✔
145
    {
146
        addOptParameter(OGRHanaOpenOptionsConstants::ENCRYPT, "ENCRYPT");
56✔
147
        addOptParameter(OGRHanaOpenOptionsConstants::SSL_CRYPTO_PROVIDER,
56✔
148
                        "sslCryptoProvider");
149
        addOptParameter(OGRHanaOpenOptionsConstants::SSL_KEY_STORE,
56✔
150
                        "sslKeyStore");
151
        addOptParameter(OGRHanaOpenOptionsConstants::SSL_TRUST_STORE,
56✔
152
                        "sslTrustStore");
153
        addOptParameter(OGRHanaOpenOptionsConstants::SSL_VALIDATE_CERTIFICATE,
56✔
154
                        "sslValidateCertificate");
155
        addOptParameter(OGRHanaOpenOptionsConstants::SSL_HOST_NAME_CERTIFICATE,
56✔
156
                        "sslHostNameInCertificate");
157
    }
158

159
    addOptParameter(OGRHanaOpenOptionsConstants::PACKET_SIZE, "PACKETSIZE");
65✔
160
    addOptParameter(OGRHanaOpenOptionsConstants::SPLIT_BATCH_COMMANDS,
65✔
161
                    "SPLITBATCHCOMMANDS");
162
    addParameter("CHAR_AS_UTF8", "1");
65✔
163

164
    CPLString appName;
130✔
165
    appName.Printf("GDAL %s", GDALVersionInfo("RELEASE_NAME"));
65✔
166
    addParameter("sessionVariable:APPLICATION", appName.c_str());
65✔
167

168
    return isValid ? JoinStrings(params, ";") : "";
130✔
169
}
170

171
int CPLFetchInt(CSLConstList papszStrList, const char *pszKey, int defaultValue)
54✔
172
{
173
    const char *const pszValue = CSLFetchNameValue(papszStrList, pszKey);
54✔
174
    if (pszValue == nullptr)
54✔
175
        return defaultValue;
54✔
176
    return atoi(pszValue);
×
177
}
178

179
int GetSrid(odbc::ResultSet &resultSet)
33✔
180
{
181
    int srid = UNDETERMINED_SRID;
33✔
182
    while (resultSet.next())
33✔
183
    {
184
        odbc::Int val = resultSet.getInt(1);
33✔
185
        if (!val.isNull())
33✔
186
        {
187
            srid = *val;
33✔
188
            break;
33✔
189
        }
190
    }
191
    resultSet.close();
33✔
192
    return srid;
33✔
193
}
194

195
int GetColumnSrid(odbc::Connection &conn, const CPLString &schemaName,
33✔
196
                  const CPLString &tableName, const CPLString &columnName)
197
{
198
    CPLString sql =
199
        "SELECT SRS_ID FROM SYS.ST_GEOMETRY_COLUMNS WHERE SCHEMA_NAME = ?"
200
        " AND TABLE_NAME = ? AND COLUMN_NAME = ?";
66✔
201
    odbc::PreparedStatementRef stmt = conn.prepareStatement(sql.c_str());
33✔
202
    stmt->setString(1, odbc::String(schemaName));
33✔
203
    stmt->setString(2, odbc::String(tableName));
33✔
204
    if (columnName != nullptr)
33✔
205
        stmt->setString(3, odbc::String(columnName));
33✔
206
    return GetSrid(*stmt->executeQuery());
66✔
207
}
208

UNCOV
209
int GetColumnSrid(odbc::Connection &conn, const CPLString &query,
×
210
                  const CPLString &columnName)
211
{
UNCOV
212
    CPLString clmName = QuotedIdentifier(columnName);
×
213

214
    CPLString sql =
UNCOV
215
        CPLString().Printf("SELECT %s.ST_SRID() FROM (%s) WHERE %s IS NOT NULL",
×
UNCOV
216
                           clmName.c_str(), query.c_str(), clmName.c_str());
×
217

UNCOV
218
    odbc::StatementRef stmt = conn.createStatement();
×
UNCOV
219
    return GetSrid(*stmt->executeQuery(sql.c_str()));
×
220
}
221

222
int GetSridWithFilter(odbc::Connection &conn, const CPLString &whereCondition)
13✔
223
{
224
    CPLAssert(whereCondition != nullptr);
13✔
225

226
    int ret = UNDETERMINED_SRID;
13✔
227

228
    odbc::StatementRef stmt = conn.createStatement();
26✔
229
    CPLString sql = CPLString().Printf(
13✔
230
        "SELECT SRS_ID FROM SYS.ST_SPATIAL_REFERENCE_SYSTEMS WHERE %s",
231
        whereCondition.c_str());
26✔
232
    odbc::ResultSetRef rsSrs = stmt->executeQuery(sql.c_str());
13✔
233
    while (rsSrs->next())
13✔
234
    {
235
        odbc::Int val = rsSrs->getInt(1);
13✔
236
        if (!val.isNull())
13✔
237
        {
238
            ret = *val;
13✔
239
            break;
13✔
240
        }
241
    }
242
    rsSrs->close();
13✔
243

244
    return ret;
26✔
245
}
246

247
CPLString GetSrsWktById(odbc::Connection &conn, int srid)
29✔
248
{
249
    CPLString ret;
29✔
250
    const char *sql = "SELECT DEFINITION FROM "
29✔
251
                      "SYS.ST_SPATIAL_REFERENCE_SYSTEMS WHERE SRS_ID = ?";
252
    odbc::PreparedStatementRef stmt = conn.prepareStatement(sql);
58✔
253
    stmt->setInt(1, odbc::Int(srid));
29✔
254
    odbc::ResultSetRef rs = stmt->executeQuery();
58✔
255
    while (rs->next())
29✔
256
    {
257
        odbc::String wkt = rs->getString(1);
29✔
258
        if (!wkt.isNull())
29✔
259
        {
260
            ret = *wkt;
29✔
261
            if (!ret.empty())
29✔
262
                break;
29✔
263
        }
264
    }
265
    rs->close();
29✔
266

267
    return ret;
58✔
268
}
269

270
OGRwkbGeometryType GetGeometryType(odbc::Connection &conn,
32✔
271
                                   const CPLString &query,
272
                                   const CPLString &columnName)
273
{
274
    CPLString clmName = QuotedIdentifier(columnName);
64✔
275

276
    CPLString sql = CPLString().Printf(
32✔
277
        "SELECT DISTINCT UPPER(%s.ST_GeometryType()), %s.ST_Is3D(), "
278
        "%s.ST_IsMeasured() FROM %s WHERE %s IS NOT NULL",
279
        clmName.c_str(), clmName.c_str(), clmName.c_str(), query.c_str(),
280
        clmName.c_str());
64✔
281

282
    odbc::StatementRef stmt = conn.createStatement();
64✔
283
    odbc::ResultSetRef rsGeomInfo = stmt->executeQuery(sql.c_str());
32✔
284
    OGRwkbGeometryType ret = wkbUnknown;
32✔
285
    std::size_t i = 0;
32✔
286
    while (rsGeomInfo->next())
51✔
287
    {
288
        ++i;
19✔
289
        auto typeName = rsGeomInfo->getString(1);
19✔
290
        auto hasZ = rsGeomInfo->getInt(2);
19✔
291
        auto hasM = rsGeomInfo->getInt(3);
19✔
292
        OGRwkbGeometryType geomType =
293
            ToWkbType(typeName->c_str(), *hasZ == 1, *hasM == 1);
19✔
294
        if (geomType == OGRwkbGeometryType::wkbUnknown)
19✔
295
            continue;
×
296
        if (ret == OGRwkbGeometryType::wkbUnknown)
19✔
297
            ret = geomType;
19✔
298
        else if (ret != geomType)
×
299
        {
300
            ret = OGRwkbGeometryType::wkbUnknown;
×
301
            break;
×
302
        }
303
    }
304
    rsGeomInfo->close();
32✔
305

306
    if (i == 0)
32✔
307
        ret = OGRwkbGeometryType::wkbUnknown;
13✔
308
    return ret;
64✔
309
}
310

311
GeometryColumnDescription GetGeometryColumnDescription(
33✔
312
    odbc::Connection &conn, const CPLString &schemaName,
313
    const CPLString &tableName, const CPLString &columnName,
314
    bool detectGeometryType)
315
{
316
    OGRwkbGeometryType type =
317
        detectGeometryType
318
            ? GetGeometryType(conn,
65✔
319
                              GetFullTableNameQuoted(schemaName, tableName),
64✔
320
                              columnName)
321
            : OGRwkbGeometryType::wkbUnknown;
33✔
322
    int srid = GetColumnSrid(conn, schemaName, tableName, columnName);
33✔
323

324
    return {columnName, type, srid, false};
33✔
325
}
326

327
GeometryColumnDescription
UNCOV
328
GetGeometryColumnDescription(odbc::Connection &conn, const CPLString &query,
×
329
                             const CPLString &columnName,
330
                             bool detectGeometryType)
331
{
332
    // For some queries like this SELECT ST_GeomFROMWKT('POINT(0 0)') FROM DUMMY
333
    // we need to have a proper column name.
UNCOV
334
    bool needColumnName = false;
×
UNCOV
335
    std::vector<char> specialChars = {'(', ')', '\'', ' '};
×
UNCOV
336
    for (const char c : specialChars)
×
337
    {
UNCOV
338
        if (columnName.find(c) != CPLString::npos)
×
339
        {
340
            needColumnName = true;
×
341
            break;
×
342
        }
343
    }
344

UNCOV
345
    CPLString preparedQuery = query;
×
UNCOV
346
    CPLString clmName = columnName;
×
UNCOV
347
    if (needColumnName)
×
348
    {
349
        auto it = std::search(
350
            preparedQuery.begin(), preparedQuery.end(), columnName.begin(),
351
            columnName.end(),
352
            [](char ch1, char ch2)
×
353
            {
354
                return CPLToupper(static_cast<unsigned char>(ch1)) ==
×
355
                       CPLToupper(static_cast<unsigned char>(ch2));
×
356
            });
×
357

358
        if (it != preparedQuery.end())
×
359
        {
360
            auto pos = it - preparedQuery.begin();
×
361
            CPLString newName = columnName + " AS \"tmp_geom_field\"";
×
362
            preparedQuery.replace(static_cast<std::size_t>(pos),
363
                                  columnName.length(), newName, 0,
364
                                  newName.length());
×
365
            clmName = "tmp_geom_field";
×
366
        }
367
    }
368

369
    OGRwkbGeometryType type =
370
        detectGeometryType
UNCOV
371
            ? GetGeometryType(conn, "(" + preparedQuery + ")", clmName)
×
UNCOV
372
            : OGRwkbGeometryType::wkbUnknown;
×
UNCOV
373
    int srid = GetColumnSrid(conn, preparedQuery, clmName);
×
374

UNCOV
375
    return {columnName, type, srid, false};
×
376
}
377

378
CPLString FormatDefaultValue(const char *value, short dataType)
12✔
379
{
380
    /*
381
     The values that can be set as default values are :
382
       - literal string values enclosed in single-quote characters and properly
383
     escaped like: 'Nice weather. Isn''t it ?'
384
       - numeric values (unquoted)
385
       - reserved keywords (unquoted): CURRENT_TIMESTAMP, CURRENT_DATE,
386
     CURRENT_TIME, NULL
387
       - datetime literal values enclosed in single-quote characters with the
388
     following defined format: ‘YYYY/MM/DD HH:MM:SS[.sss]’
389
       - any other driver specific expression. e.g. for SQLite:
390
     (strftime(‘%Y-%m-%dT%H:%M:%fZ’,’now’))
391
     */
392

393
    if (EQUAL(value, "NULL"))
12✔
394
        return value;
×
395

396
    switch (dataType)
12✔
397
    {
398
        case QGRHanaDataTypes::Bit:
1✔
399
        case QGRHanaDataTypes::Boolean:
400
            return value;
1✔
401
        case QGRHanaDataTypes::TinyInt:
6✔
402
        case QGRHanaDataTypes::SmallInt:
403
        case QGRHanaDataTypes::Integer:
404
        case QGRHanaDataTypes::BigInt:
405
        case QGRHanaDataTypes::Real:
406
        case QGRHanaDataTypes::Float:
407
        case QGRHanaDataTypes::Double:
408
        case QGRHanaDataTypes::Decimal:
409
        case QGRHanaDataTypes::Numeric:
410
            return value;
6✔
411
        case QGRHanaDataTypes::Char:
1✔
412
        case QGRHanaDataTypes::VarChar:
413
        case QGRHanaDataTypes::LongVarChar:
414
        case QGRHanaDataTypes::WChar:
415
        case QGRHanaDataTypes::WVarChar:
416
        case QGRHanaDataTypes::WLongVarChar:
417
            return Literal(value);
1✔
418
        case QGRHanaDataTypes::Binary:
1✔
419
        case QGRHanaDataTypes::VarBinary:
420
        case QGRHanaDataTypes::LongVarBinary:
421
        case QGRHanaDataTypes::RealVector:
422
            return value;
1✔
423
        case QGRHanaDataTypes::Date:
1✔
424
        case QGRHanaDataTypes::TypeDate:
425
            if (EQUAL(value, "CURRENT_DATE"))
1✔
426
                return value;
×
427
            return Literal(value);
1✔
428
        case QGRHanaDataTypes::Time:
1✔
429
        case QGRHanaDataTypes::TypeTime:
430
            if (EQUAL(value, "CURRENT_TIME"))
1✔
431
                return value;
×
432
            return Literal(value);
1✔
433
        case QGRHanaDataTypes::Timestamp:
1✔
434
        case QGRHanaDataTypes::TypeTimestamp:
435
            if (EQUAL(value, "CURRENT_TIMESTAMP"))
1✔
436
                return value;
×
437
            return Literal(value);
1✔
438
        default:
×
439
            return value;
×
440
    }
441
}
442

443
short GetArrayDataType(const CPLString &typeName)
11✔
444
{
445
    if (typeName == "BOOLEAN ARRAY")
11✔
446
        return QGRHanaDataTypes::Boolean;
1✔
447
    else if (typeName == "TINYINT ARRAY")
10✔
448
        return QGRHanaDataTypes::TinyInt;
×
449
    else if (typeName == "SMALLINT ARRAY")
10✔
450
        return QGRHanaDataTypes::SmallInt;
1✔
451
    else if (typeName == "INTEGER ARRAY")
9✔
452
        return QGRHanaDataTypes::Integer;
2✔
453
    else if (typeName == "BIGINT ARRAY")
7✔
454
        return QGRHanaDataTypes::BigInt;
2✔
455
    else if (typeName == "DOUBLE ARRAY")
5✔
456
        return QGRHanaDataTypes::Double;
2✔
457
    else if (typeName == "REAL ARRAY")
3✔
458
        return QGRHanaDataTypes::Float;
×
459
    else if (typeName == "DECIMAL ARRAY" || typeName == "SMALLDECIMAL ARRAY")
3✔
460
        return QGRHanaDataTypes::Decimal;
×
461
    else if (typeName == "CHAR ARRAY")
3✔
462
        return QGRHanaDataTypes::Char;
×
463
    else if (typeName == "VARCHAR ARRAY")
3✔
464
        return QGRHanaDataTypes::VarChar;
×
465
    else if (typeName == "NCHAR ARRAY")
3✔
466
        return QGRHanaDataTypes::WChar;
×
467
    else if (typeName == "NVARCHAR ARRAY")
3✔
468
        return QGRHanaDataTypes::WVarChar;
3✔
469
    else if (typeName == "DATE ARRAY")
×
470
        return QGRHanaDataTypes::Date;
×
471
    else if (typeName == "TIME ARRAY")
×
472
        return QGRHanaDataTypes::Time;
×
473
    else if (typeName == "TIMESTAMP ARRAY" || typeName == "SECONDDATE ARRAY")
×
474
        return QGRHanaDataTypes::Timestamp;
×
475

476
    return QGRHanaDataTypes::Unknown;
×
477
}
478

479
std::vector<CPLString> GetSupportedArrayTypes()
2✔
480
{
481
    return {"TINYINT", "SMALLINT", "INT", "BIGINT", "REAL", "DOUBLE", "STRING"};
16✔
482
}
483

484
bool IsKnownDataType(short dataType)
188✔
485
{
486
    return dataType == QGRHanaDataTypes::Bit ||
187✔
487
           dataType == QGRHanaDataTypes::Boolean ||
187✔
488
           dataType == QGRHanaDataTypes::TinyInt ||
187✔
489
           dataType == QGRHanaDataTypes::SmallInt ||
185✔
490
           dataType == QGRHanaDataTypes::Integer ||
132✔
491
           dataType == QGRHanaDataTypes::BigInt ||
108✔
492
           dataType == QGRHanaDataTypes::Double ||
88✔
493
           dataType == QGRHanaDataTypes::Real ||
87✔
494
           dataType == QGRHanaDataTypes::Float ||
87✔
495
           dataType == QGRHanaDataTypes::Decimal ||
84✔
496
           dataType == QGRHanaDataTypes::Numeric ||
84✔
497
           dataType == QGRHanaDataTypes::Char ||
84✔
498
           dataType == QGRHanaDataTypes::VarChar ||
84✔
499
           dataType == QGRHanaDataTypes::LongVarChar ||
84✔
500
           dataType == QGRHanaDataTypes::WChar ||
84✔
501
           dataType == QGRHanaDataTypes::WVarChar ||
44✔
502
           dataType == QGRHanaDataTypes::WLongVarChar ||
44✔
503
           dataType == QGRHanaDataTypes::Date ||
44✔
504
           dataType == QGRHanaDataTypes::TypeDate ||
43✔
505
           dataType == QGRHanaDataTypes::Time ||
43✔
506
           dataType == QGRHanaDataTypes::TypeTime ||
42✔
507
           dataType == QGRHanaDataTypes::Timestamp ||
42✔
508
           dataType == QGRHanaDataTypes::TypeTimestamp ||
40✔
509
           dataType == QGRHanaDataTypes::Binary ||
40✔
510
           dataType == QGRHanaDataTypes::VarBinary ||
39✔
511
           dataType == QGRHanaDataTypes::LongVarBinary ||
39✔
512
           dataType == QGRHanaDataTypes::Geometry ||
375✔
513
           dataType == QGRHanaDataTypes::RealVector;
188✔
514
}
515

516
}  // anonymous namespace
517

518
/************************************************************************/
519
/*                               GetPrefix()                            */
520
/************************************************************************/
521

522
const char *OGRHanaDataSource::GetPrefix()
195✔
523
{
524
    return HANA_PREFIX;
195✔
525
}
526

527
/************************************************************************/
528
/*                         OGRHanaDataSource()                          */
529
/************************************************************************/
530

531
OGRHanaDataSource::OGRHanaDataSource()
65✔
532
{
533
}
65✔
534

535
/************************************************************************/
536
/*                        ~OGRHanaDataSource()                          */
537
/************************************************************************/
538

539
OGRHanaDataSource::~OGRHanaDataSource()
130✔
540
{
541
    layers_.clear();
65✔
542

543
    for (const auto &kv : srsCache_)
94✔
544
    {
545
        OGRSpatialReference *srs = kv.second;
29✔
546
        if (srs != nullptr)
29✔
547
            srs->Release();
29✔
548
    }
549
    srsCache_.clear();
65✔
550
}
130✔
551

552
/************************************************************************/
553
/*                                 Open()                               */
554
/************************************************************************/
555

556
int OGRHanaDataSource::Open(const char *newName, char **openOptions, int update)
65✔
557
{
558
    CPLAssert(layers_.size() == 0);
65✔
559

560
    if (!STARTS_WITH_CI(newName, GetPrefix()))
65✔
561
    {
562
        CPLError(CE_Failure, CPLE_AppDefined,
×
563
                 "%s does not conform to HANA driver naming convention,"
564
                 " %s*\n",
565
                 newName, GetPrefix());
566
        return FALSE;
×
567
    }
568

569
    updateMode_ = update;
65✔
570
    detectGeometryType_ = CPLFetchBool(
65✔
571
        openOptions, OGRHanaOpenOptionsConstants::DETECT_GEOMETRY_TYPE, true);
572

573
    std::size_t prefixLength = strlen(GetPrefix());
65✔
574
    char **connOptions =
575
        CSLTokenizeStringComplex(newName + prefixLength, ";", TRUE, FALSE);
65✔
576

577
    const char *paramSchema = CSLFetchNameValueDef(
65✔
578
        connOptions, OGRHanaOpenOptionsConstants::SCHEMA, nullptr);
579
    if (paramSchema != nullptr)
65✔
580
        schemaName_ = paramSchema;
63✔
581

582
    int ret = FALSE;
65✔
583

584
    CPLString connectionStr = BuildConnectionString(connOptions);
65✔
585

586
    if (!connectionStr.empty())
65✔
587
    {
588
        connEnv_ = odbc::Environment::create();
56✔
589
        conn_ = connEnv_->createConnection();
56✔
590
        conn_->setAutoCommit(false);
56✔
591

592
        const char *paramConnTimeout = CSLFetchNameValueDef(
56✔
593
            connOptions, OGRHanaOpenOptionsConstants::CONNECTION_TIMEOUT,
594
            nullptr);
595
        if (paramConnTimeout != nullptr)
56✔
596
            conn_->setConnectionTimeout(
×
597
                static_cast<unsigned long>(atoi(paramConnTimeout)));
×
598

599
        try
600
        {
601
            conn_->connect(connectionStr.c_str());
56✔
602
        }
603
        catch (const odbc::Exception &ex)
×
604
        {
605
            CPLError(CE_Failure, CPLE_AppDefined,
×
606
                     "HANA connection failed: %s\n", ex.what());
×
607
        }
608

609
        if (conn_->connected())
56✔
610
        {
611
            DetermineVersions();
56✔
612

613
            const char *paramTables = CSLFetchNameValueDef(
56✔
614
                connOptions, OGRHanaOpenOptionsConstants::TABLES, "");
615
            InitializeLayers(paramSchema, paramTables);
56✔
616
            ret = TRUE;
56✔
617
        }
618
    }
619

620
    CSLDestroy(connOptions);
65✔
621

622
    return ret;
65✔
623
}
624

625
/************************************************************************/
626
/*                            DeleteLayer()                             */
627
/************************************************************************/
628

629
OGRErr OGRHanaDataSource::DeleteLayer(int index)
×
630
{
631
    if (index < 0 || static_cast<std::size_t>(index) >= layers_.size())
×
632
        return OGRERR_FAILURE;
×
633

634
    const std::unique_ptr<OGRLayer> &layer =
635
        layers_[static_cast<std::size_t>(index)];
×
636
    CPLDebug("HANA", "DeleteLayer(%s)", layer->GetName());
×
637

638
    if (auto tableLayer = dynamic_cast<OGRHanaTableLayer *>(layer.get()))
×
639
    {
640
        OGRErr err = tableLayer->DropTable();
×
641
        if (OGRERR_NONE == err)
×
642
            return err;
×
643
    }
644

645
    layers_.erase(layers_.begin() + index);
×
646

647
    return OGRERR_NONE;
×
648
}
649

650
void OGRHanaDataSource::CreateTable(
18✔
651
    const CPLString &tableName, const CPLString &fidName,
652
    const CPLString &fidType, const CPLString &geomColumnName,
653
    OGRwkbGeometryType geomType, bool geomColumnNullable,
654
    const CPLString &geomColumnIndexType, int geomSrid)
655
{
656
    CPLString sql;
36✔
657
    if (geomType == OGRwkbGeometryType::wkbNone ||
50✔
658
        !(!geomColumnName.empty() && geomSrid >= 0))
32✔
659
    {
660
        sql = "CREATE COLUMN TABLE " +
6✔
661
              GetFullTableNameQuoted(schemaName_, tableName) + " (" +
18✔
662
              QuotedIdentifier(fidName) + " " + fidType +
24✔
663
              " GENERATED BY DEFAULT AS IDENTITY, PRIMARY KEY ( " +
12✔
664
              QuotedIdentifier(fidName) + "));";
18✔
665
    }
666
    else
667
    {
668
        sql = "CREATE COLUMN TABLE " +
12✔
669
              GetFullTableNameQuoted(schemaName_, tableName) + " (" +
36✔
670
              QuotedIdentifier(fidName) + " " + fidType +
48✔
671
              " GENERATED BY DEFAULT AS IDENTITY, " +
24✔
672
              QuotedIdentifier(geomColumnName) + " ST_GEOMETRY (" +
48✔
673
              std::to_string(geomSrid) + ")" +
48✔
674
              (geomColumnNullable ? "" : " NOT NULL") +
24✔
675
              " SPATIAL INDEX PREFERENCE " + geomColumnIndexType +
36✔
676
              ", PRIMARY KEY ( " + QuotedIdentifier(fidName) + "));";
36✔
677
    }
678

679
    ExecuteSQL(sql);
18✔
680
}
18✔
681

682
void OGRHanaDataSource::DetermineVersions()
56✔
683
{
684
    odbc::DatabaseMetaDataRef dbmd = conn_->getDatabaseMetaData();
56✔
685
    CPLString dbVersion(dbmd->getDBMSVersion());
56✔
686
    hanaVersion_ = HanaVersion::fromString(dbVersion);
56✔
687

688
    if (hanaVersion_.major() < 4)
56✔
689
    {
690
        cloudVersion_ = HanaVersion(0, 0, 0);
×
691
        return;
×
692
    }
693

694
    odbc::StatementRef stmt = conn_->createStatement();
112✔
695
    const char *sql = "SELECT CLOUD_VERSION FROM SYS.M_DATABASE;";
56✔
696

697
    odbc::ResultSetRef rsVersion = stmt->executeQuery(sql);
112✔
698
    if (rsVersion->next())
56✔
699
        cloudVersion_ =
700
            HanaVersion::fromString(rsVersion->getString(1)->c_str());
56✔
701

702
    rsVersion->close();
56✔
703
}
704

705
/************************************************************************/
706
/*                            FindSchemaAndTableNames()                 */
707
/************************************************************************/
708

709
std::pair<CPLString, CPLString>
710
OGRHanaDataSource::FindSchemaAndTableNames(const char *query)
15✔
711
{
712
    odbc::PreparedStatementRef stmt = PrepareStatement(query);
30✔
713
    if (stmt.get() == nullptr)
15✔
714
        return {"", ""};
×
715

716
    odbc::ResultSetMetaDataRef rsmd = stmt->getMetaData();
30✔
717

718
    // Note, getTableName returns correct table name also in the case
719
    // when the original sql query uses a view
720
    CPLString tableName = rsmd->getTableName(1);
30✔
721
    if (tableName == "M_DATABASE_")
15✔
722
        tableName = "M_DATABASE";
×
723
    CPLString schemaName = rsmd->getSchemaName(1);
30✔
724
    if (schemaName.empty() && !tableName.empty())
15✔
725
        schemaName = FindSchemaName(tableName.c_str());
14✔
726
    return {schemaName, tableName};
15✔
727
}
728

729
/************************************************************************/
730
/*                            FindLayerByName()                         */
731
/************************************************************************/
732

733
int OGRHanaDataSource::FindLayerByName(const char *name)
67✔
734
{
735
    for (size_t i = 0; i < layers_.size(); ++i)
604✔
736
    {
737
        if (EQUAL(name, layers_[i]->GetName()))
574✔
738
            return static_cast<int>(i);
37✔
739
    }
740
    return -1;
30✔
741
}
742

743
/************************************************************************/
744
/*                            FindSchemaName()                          */
745
/************************************************************************/
746

747
CPLString OGRHanaDataSource::FindSchemaName(const char *objectName)
14✔
748
{
749
    auto getSchemaName = [&](const char *sql)
14✔
750
    {
751
        odbc::PreparedStatementRef stmt = PrepareStatement(sql);
28✔
752
        stmt->setString(1, odbc::String(objectName));
14✔
753
        odbc::ResultSetRef rsEntries = stmt->executeQuery();
28✔
754
        CPLString ret;
14✔
755
        while (rsEntries->next())
28✔
756
        {
757
            // return empty string if there is more than one schema.
758
            if (!ret.empty())
14✔
759
            {
UNCOV
760
                ret.clear();
×
UNCOV
761
                break;
×
762
            }
763
            ret = *rsEntries->getString(1);
14✔
764
        }
765
        rsEntries->close();
14✔
766

767
        return ret;
28✔
768
    };
14✔
769

770
    CPLString ret = getSchemaName(
771
        "SELECT SCHEMA_NAME FROM SYS.TABLES WHERE TABLE_NAME = ?");
14✔
772
    if (ret.empty())
14✔
UNCOV
773
        ret = getSchemaName(
×
UNCOV
774
            "SELECT SCHEMA_NAME FROM SYS.VIEWS WHERE VIEW_NAME = ?");
×
775

776
    return ret;
28✔
777
}
778

779
/************************************************************************/
780
/*                              CreateStatement()                       */
781
/************************************************************************/
782

783
odbc::StatementRef OGRHanaDataSource::CreateStatement()
220✔
784
{
785
    return conn_->createStatement();
220✔
786
}
787

788
/************************************************************************/
789
/*                              PrepareStatement()                      */
790
/************************************************************************/
791

792
odbc::PreparedStatementRef OGRHanaDataSource::PrepareStatement(const char *sql)
323✔
793
{
794
    CPLAssert(sql != nullptr);
323✔
795

796
    try
797
    {
798
        CPLDebug("HANA", "Prepare statement %s.", sql);
323✔
799

800
        std::u16string sqlUtf16 = odbc::StringConverter::utf8ToUtf16(sql);
646✔
801
        return conn_->prepareStatement(sqlUtf16.c_str());
323✔
802
    }
803
    catch (const odbc::Exception &ex)
2✔
804
    {
805
        CPLError(CE_Failure, CPLE_AppDefined, "Failed to prepare statement: %s",
1✔
806
                 ex.what());
1✔
807
    }
808
    return nullptr;
1✔
809
}
810

811
/************************************************************************/
812
/*                              Commit()                                */
813
/************************************************************************/
814

815
void OGRHanaDataSource::Commit()
134✔
816
{
817
    conn_->commit();
134✔
818
}
134✔
819

820
/************************************************************************/
821
/*                            ExecuteSQL()                              */
822
/************************************************************************/
823

824
void OGRHanaDataSource::ExecuteSQL(const CPLString &sql)
96✔
825
{
826
    std::u16string sqlUtf16 =
827
        odbc::StringConverter::utf8ToUtf16(sql.c_str(), sql.length());
192✔
828
    odbc::StatementRef stmt = conn_->createStatement();
192✔
829
    stmt->execute(sqlUtf16.c_str());
96✔
830
    if (!IsTransactionStarted())
92✔
831
        conn_->commit();
91✔
832
}
92✔
833

834
/************************************************************************/
835
/*                            GetSrsById()                              */
836
/*                                                                      */
837
/*      Return a SRS corresponding to a particular id.  The returned    */
838
/*      object has its reference counter incremented. Consequently      */
839
/*      the caller should call Release() on it (if not null) once done  */
840
/*      with it.                                                        */
841
/************************************************************************/
842

843
OGRSpatialReference *OGRHanaDataSource::GetSrsById(int srid)
33✔
844
{
845
    if (srid < 0)
33✔
846
        return nullptr;
×
847

848
    auto it = srsCache_.find(srid);
33✔
849
    if (it != srsCache_.end())
33✔
850
    {
851
        it->second->Reference();
4✔
852
        return it->second;
4✔
853
    }
854

855
    OGRSpatialReference *srs = nullptr;
29✔
856

857
    CPLString wkt = GetSrsWktById(*conn_, srid);
29✔
858
    if (!wkt.empty())
29✔
859
    {
860
        srs = new OGRSpatialReference();
29✔
861
        OGRErr err = srs->importFromWkt(wkt.c_str());
29✔
862
        if (OGRERR_NONE != err)
29✔
863
        {
864
            delete srs;
×
865
            srs = nullptr;
×
866
        }
867
    }
868

869
    srsCache_.insert({srid, srs});
29✔
870

871
    if (srs)
29✔
872
        srs->Reference();
29✔
873
    return srs;
29✔
874
}
875

876
/************************************************************************/
877
/*                               GetSrsId()                             */
878
/************************************************************************/
879

880
int OGRHanaDataSource::GetSrsId(const OGRSpatialReference *srs)
14✔
881
{
882
    if (srs == nullptr)
14✔
883
        return UNDETERMINED_SRID;
1✔
884

885
    /* -------------------------------------------------------------------- */
886
    /*      Try to find srs id using authority name and code (EPSG:3857).   */
887
    /* -------------------------------------------------------------------- */
888
    OGRSpatialReference srsLocal(*srs);
26✔
889

890
    const char *authorityName = srsLocal.GetAuthorityName(nullptr);
13✔
891
    if (authorityName == nullptr || strlen(authorityName) == 0)
13✔
892
    {
893
        srsLocal.AutoIdentifyEPSG();
×
894
        authorityName = srsLocal.GetAuthorityName(nullptr);
×
895
        if (authorityName != nullptr && EQUAL(authorityName, "EPSG"))
×
896
        {
897
            const char *authorityCode = srsLocal.GetAuthorityCode(nullptr);
×
898
            if (authorityCode != nullptr && strlen(authorityCode) > 0)
×
899
            {
900
                srsLocal.importFromEPSG(atoi(authorityCode));
×
901
                authorityName = srsLocal.GetAuthorityName(nullptr);
×
902
            }
903
        }
904
    }
905

906
    int authorityCode = 0;
13✔
907
    if (authorityName != nullptr)
13✔
908
    {
909
        authorityCode = atoi(srsLocal.GetAuthorityCode(nullptr));
13✔
910
        if (authorityCode > 0)
13✔
911
        {
912
            int ret = GetSridWithFilter(
13✔
913
                *conn_,
914
                CPLString().Printf("SRS_ID = %d AND ORGANIZATION = '%s'",
26✔
915
                                   authorityCode, authorityName));
13✔
916
            if (ret != UNDETERMINED_SRID)
13✔
917
                return ret;
13✔
918
        }
919
    }
920

921
    /* -------------------------------------------------------------------- */
922
    /*      Try to find srs id using wkt content.                           */
923
    /* -------------------------------------------------------------------- */
924

925
    char *wkt = nullptr;
×
926
    OGRErr err = srsLocal.exportToWkt(&wkt);
×
927
    CPLString strWkt(wkt);
×
928
    CPLFree(wkt);
×
929

930
    if (OGRERR_NONE != err)
×
931
        return UNDETERMINED_SRID;
×
932

933
    int srid = GetSridWithFilter(
×
934
        *conn_, CPLString().Printf("DEFINITION = '%s'", strWkt.c_str()));
×
935
    if (srid != UNDETERMINED_SRID)
×
936
        return srid;
×
937

938
    /* -------------------------------------------------------------------- */
939
    /*      Try to add a new spatial reference system to the database       */
940
    /* -------------------------------------------------------------------- */
941

942
    char *proj4 = nullptr;
×
943
    err = srsLocal.exportToProj4(&proj4);
×
944
    CPLString strProj4(proj4);
×
945
    CPLFree(proj4);
×
946

947
    if (OGRERR_NONE != err)
×
948
        return srid;
×
949

950
    if (authorityName != nullptr && authorityCode > 0)
×
951
    {
952
        srid = authorityCode;
×
953
    }
954
    else
955
    {
956
        odbc::StatementRef stmt = conn_->createStatement();
×
957
        const char *sql =
×
958
            "SELECT MAX(SRS_ID) FROM SYS.ST_SPATIAL_REFERENCE_SYSTEMS WHERE "
959
            "SRS_ID >= 10000000 AND SRS_ID < 20000000";
960
        odbc::ResultSetRef rsSrid = stmt->executeQuery(sql);
×
961
        while (rsSrid->next())
×
962
        {
963
            odbc::Int val = rsSrid->getInt(1);
×
964
            srid = val.isNull() ? 10000000 : *val + 1;
×
965
        }
966
        rsSrid->close();
×
967
    }
968

969
    try
970
    {
971
        CreateSpatialReferenceSystem(srsLocal, srid, authorityName,
×
972
                                     authorityCode, strWkt, strProj4);
973
        return srid;
×
974
    }
975
    catch (const odbc::Exception &ex)
×
976
    {
977
        CPLError(CE_Failure, CPLE_AppDefined,
×
978
                 "Unable to create an SRS in the database: %s.\n", ex.what());
×
979
    }
980

981
    return UNDETERMINED_SRID;
×
982
}
983

984
/************************************************************************/
985
/*                           IsSrsRoundEarth()                          */
986
/************************************************************************/
987

988
bool OGRHanaDataSource::IsSrsRoundEarth(int srid)
12✔
989
{
990
    const char *sql =
12✔
991
        "SELECT ROUND_EARTH FROM SYS.ST_SPATIAL_REFERENCE_SYSTEMS "
992
        "WHERE SRS_ID = ?";
993
    odbc::PreparedStatementRef stmt = PrepareStatement(sql);
24✔
994
    stmt->setInt(1, odbc::Int(srid));
12✔
995
    odbc::ResultSetRef rs = stmt->executeQuery();
12✔
996
    bool ret = false;
12✔
997
    if (rs->next())
12✔
998
        ret = (*rs->getString(1) == "TRUE");
12✔
999
    rs->close();
12✔
1000
    return ret;
24✔
1001
}
1002

1003
/************************************************************************/
1004
/*                        HasSrsPlanarEquivalent()                      */
1005
/************************************************************************/
1006

1007
bool OGRHanaDataSource::HasSrsPlanarEquivalent(int srid)
2✔
1008
{
1009
    const char *sql = "SELECT COUNT(*) FROM SYS.ST_SPATIAL_REFERENCE_SYSTEMS "
2✔
1010
                      "WHERE SRS_ID = ?";
1011
    odbc::PreparedStatementRef stmt = PrepareStatement(sql);
4✔
1012
    stmt->setInt(1, ToPlanarSRID(srid));
2✔
1013
    odbc::ResultSetRef rs = stmt->executeQuery();
2✔
1014
    std::int64_t count = 0;
2✔
1015
    if (rs->next())
2✔
1016
        count = *rs->getLong(1);
2✔
1017
    rs->close();
2✔
1018
    return count > 0;
4✔
1019
}
1020

1021
/************************************************************************/
1022
/*                           GetQueryColumns()                          */
1023
/************************************************************************/
1024

1025
OGRErr OGRHanaDataSource::GetQueryColumns(
55✔
1026
    const CPLString &schemaName, const CPLString &query,
1027
    std::vector<ColumnDescription> &columnDescriptions)
1028
{
1029
    columnDescriptions.clear();
55✔
1030

1031
    odbc::PreparedStatementRef stmtQuery = PrepareStatement(query);
110✔
1032

1033
    if (stmtQuery.isNull())
55✔
1034
        return OGRERR_FAILURE;
×
1035

1036
    odbc::ResultSetMetaDataRef rsmd = stmtQuery->getMetaData();
110✔
1037
    std::size_t numColumns = rsmd->getColumnCount();
55✔
1038
    if (numColumns == 0)
55✔
1039
        return OGRERR_NONE;
×
1040

1041
    columnDescriptions.reserve(numColumns);
55✔
1042

1043
    odbc::DatabaseMetaDataRef dmd = conn_->getDatabaseMetaData();
110✔
1044
    odbc::PreparedStatementRef stmtArrayTypeInfo =
1045
        PrepareStatement("SELECT DATA_TYPE_NAME FROM "
1046
                         "SYS.TABLE_COLUMNS_ODBC WHERE SCHEMA_NAME = ? "
1047
                         "AND TABLE_NAME = ? AND COLUMN_NAME = ? AND "
1048
                         "DATA_TYPE_NAME LIKE '% ARRAY'");
110✔
1049

1050
    for (unsigned short clmIndex = 1; clmIndex <= numColumns; ++clmIndex)
254✔
1051
    {
1052
        CPLString typeName = rsmd->getColumnTypeName(clmIndex);
199✔
1053

1054
        if (typeName.empty())
199✔
1055
            continue;
×
1056

1057
        bool isArray = false;
199✔
1058
        CPLString tableName = rsmd->getTableName(clmIndex);
199✔
1059
        CPLString columnName = rsmd->getColumnName(clmIndex);
199✔
1060
        CPLString defaultValue;
199✔
1061
        short dataType = rsmd->getColumnType(clmIndex);
199✔
1062

1063
        if (!schemaName.empty() && !tableName.empty())
199✔
1064
        {
1065
            // Retrieve information about default value in column
1066
            odbc::ResultSetRef rsColumns =
1067
                dmd->getColumns(nullptr, schemaName.c_str(), tableName.c_str(),
1068
                                columnName.c_str());
198✔
1069
            if (rsColumns->next())
198✔
1070
            {
1071
                odbc::String defaultValueStr =
1072
                    rsColumns->getString(13 /*COLUMN_DEF*/);
390✔
1073
                if (!defaultValueStr.isNull())
195✔
1074
                    defaultValue =
1075
                        FormatDefaultValue(defaultValueStr->c_str(), dataType);
12✔
1076
            }
1077
            rsColumns->close();
198✔
1078

1079
            // Retrieve information about array type
1080
            stmtArrayTypeInfo->setString(1, schemaName);
198✔
1081
            stmtArrayTypeInfo->setString(2, tableName);
198✔
1082
            stmtArrayTypeInfo->setString(3, columnName);
198✔
1083
            odbc::ResultSetRef rsArrayTypes = stmtArrayTypeInfo->executeQuery();
198✔
1084
            if (rsArrayTypes->next())
198✔
1085
            {
1086
                typeName = *rsArrayTypes->getString(1);
11✔
1087
                dataType = GetArrayDataType(typeName);
11✔
1088

1089
                if (dataType == QGRHanaDataTypes::Unknown)
11✔
1090
                {
1091
                    CPLError(
×
1092
                        CE_Failure, CPLE_AppDefined,
1093
                        "GetQueryColumns(): Unsupported type of array (%s)",
1094
                        typeName.c_str());
1095
                    return OGRERR_FAILURE;
×
1096
                }
1097

1098
                isArray = true;
11✔
1099
            }
1100
            rsArrayTypes->close();
198✔
1101
        }
1102

1103
        if (!isArray && !IsKnownDataType(dataType))
199✔
1104
        {
1105
            odbc::ResultSetRef rsTypeInfo = dmd->getTypeInfo(dataType);
×
1106
            if (rsTypeInfo->next())
×
1107
            {
1108
                odbc::String name = rsTypeInfo->getString(1);
×
1109
                if (name.isNull())
×
1110
                    continue;
×
1111
                if (name->compare("SHORTTEXT") == 0 ||
×
1112
                    name->compare("ALPHANUM") == 0)
×
1113
                {
1114
                    dataType = QGRHanaDataTypes::WVarChar;
×
1115
                }
1116
            }
1117
            rsTypeInfo->close();
×
1118
        }
1119

1120
        if (dataType == QGRHanaDataTypes::Geometry)
199✔
1121
        {
1122
            GeometryColumnDescription geometryColumnDesc;
33✔
1123
            if (schemaName.empty() || tableName.empty())
33✔
UNCOV
1124
                geometryColumnDesc = GetGeometryColumnDescription(
×
UNCOV
1125
                    *conn_, query, columnName, detectGeometryType_);
×
1126
            else
1127
                geometryColumnDesc = GetGeometryColumnDescription(
66✔
1128
                    *conn_, schemaName, tableName, columnName,
1129
                    detectGeometryType_);
66✔
1130
            geometryColumnDesc.isNullable = rsmd->isNullable(clmIndex);
33✔
1131

1132
            columnDescriptions.push_back({true, AttributeColumnDescription(),
66✔
1133
                                          std::move(geometryColumnDesc)});
33✔
1134
        }
1135
        else
1136
        {
1137
            AttributeColumnDescription attributeColumnDesc;
166✔
1138
            attributeColumnDesc.name = std::move(columnName);
166✔
1139
            attributeColumnDesc.type = dataType;
166✔
1140
            attributeColumnDesc.typeName = std::move(typeName);
166✔
1141
            attributeColumnDesc.isArray = isArray;
166✔
1142
            attributeColumnDesc.isNullable = rsmd->isNullable(clmIndex);
166✔
1143
            attributeColumnDesc.isAutoIncrement =
166✔
1144
                rsmd->isAutoIncrement(clmIndex);
166✔
1145
            attributeColumnDesc.length =
166✔
1146
                static_cast<int>(rsmd->getColumnLength(clmIndex));
166✔
1147
            attributeColumnDesc.precision = rsmd->getPrecision(clmIndex);
166✔
1148
            attributeColumnDesc.scale = rsmd->getScale(clmIndex);
166✔
1149
            attributeColumnDesc.defaultValue = std::move(defaultValue);
166✔
1150

1151
            columnDescriptions.push_back({false, std::move(attributeColumnDesc),
166✔
1152
                                          GeometryColumnDescription()});
1153
        }
1154
    }
1155

1156
    return OGRERR_NONE;
55✔
1157
}
1158

1159
/************************************************************************/
1160
/*                          GetTablePrimaryKeys()                       */
1161
/************************************************************************/
1162

1163
std::vector<CPLString>
1164
OGRHanaDataSource::GetTablePrimaryKeys(const char *schemaName,
55✔
1165
                                       const char *tableName)
1166
{
1167
    std::vector<CPLString> ret;
55✔
1168

1169
    odbc::DatabaseMetaDataRef dmd = conn_->getDatabaseMetaData();
110✔
1170
    odbc::ResultSetRef rsPrimaryKeys =
1171
        dmd->getPrimaryKeys(nullptr, schemaName, tableName);
110✔
1172
    while (rsPrimaryKeys->next())
105✔
1173
    {
1174
        ret.push_back(*rsPrimaryKeys->getString(4));
50✔
1175
    }
1176
    rsPrimaryKeys->close();
55✔
1177

1178
    return ret;
110✔
1179
}
1180

1181
/************************************************************************/
1182
/*                          InitializeLayers()                          */
1183
/************************************************************************/
1184

1185
void OGRHanaDataSource::InitializeLayers(const char *schemaName,
56✔
1186
                                         const char *tableNames)
1187
{
1188
    std::vector<CPLString> tablesToFind = SplitStrings(tableNames, ",");
112✔
1189
    const bool hasTablesToFind = !tablesToFind.empty();
56✔
1190

1191
    auto addLayersFromQuery = [&](const char *query, bool updatable)
112✔
1192
    {
1193
        odbc::PreparedStatementRef stmt = PrepareStatement(query);
224✔
1194
        stmt->setString(1, odbc::String(schemaName));
112✔
1195
        odbc::ResultSetRef rsTables = stmt->executeQuery();
224✔
1196
        while (rsTables->next())
579✔
1197
        {
1198
            odbc::String tableName = rsTables->getString(1);
467✔
1199
            if (tableName.isNull())
467✔
1200
                continue;
×
1201
            auto pos =
1202
                std::find(tablesToFind.begin(), tablesToFind.end(), *tableName);
467✔
1203
            if (pos != tablesToFind.end())
467✔
1204
                tablesToFind.erase(pos);
×
1205

1206
            auto layer = std::make_unique<OGRHanaTableLayer>(
1207
                this, schemaName_.c_str(), tableName->c_str(), updatable);
467✔
1208
            layers_.push_back(std::move(layer));
467✔
1209
        }
1210
        rsTables->close();
112✔
1211
    };
112✔
1212

1213
    // Look for layers in Tables
1214
    std::ostringstream osTables;
112✔
1215
    osTables << "SELECT TABLE_NAME FROM SYS.TABLES WHERE SCHEMA_NAME = ?";
56✔
1216
    if (!tablesToFind.empty())
56✔
1217
        osTables << " AND TABLE_NAME IN ("
1218
                 << JoinStrings(tablesToFind, ",", Literal) << ")";
×
1219

1220
    addLayersFromQuery(osTables.str().c_str(), updateMode_);
56✔
1221

1222
    if (!(hasTablesToFind && tablesToFind.empty()))
56✔
1223
    {
1224
        // Look for layers in Views
1225
        std::ostringstream osViews;
56✔
1226
        osViews << "SELECT VIEW_NAME FROM SYS.VIEWS WHERE SCHEMA_NAME = ?";
56✔
1227
        // cppcheck-suppress knownConditionTrueFalse
1228
        if (!tablesToFind.empty())
56✔
1229
            osViews << " AND VIEW_NAME IN ("
1230
                    << JoinStrings(tablesToFind, ",", Literal) << ")";
×
1231

1232
        addLayersFromQuery(osViews.str().c_str(), false);
56✔
1233
    }
1234

1235
    // Report about tables that could not be found
1236
    for (const auto &tableName : tablesToFind)
56✔
1237
    {
1238
        const char *layerName = tableName.c_str();
×
1239
        if (GetLayerByName(layerName) == nullptr)
×
1240
            CPLDebug("HANA",
×
1241
                     "Table '%s' not found or does not "
1242
                     "have any geometry column.",
1243
                     layerName);
1244
    }
1245
}
56✔
1246

1247
/************************************************************************/
1248
/*                          LaunderName()                               */
1249
/************************************************************************/
1250

1251
std::pair<OGRErr, CPLString> OGRHanaDataSource::LaunderName(const char *name)
115✔
1252
{
1253
    CPLAssert(name != nullptr);
115✔
1254

1255
    if (!CPLIsUTF8(name, -1))
115✔
1256
    {
1257
        CPLError(CE_Failure, CPLE_AppDefined, "%s is not a valid UTF-8 string.",
×
1258
                 name);
1259
        return {OGRERR_FAILURE, ""};
×
1260
    }
1261

1262
    auto getUTF8SequenceLength = [](char c)
1,050✔
1263
    {
1264
        if ((c & 0x80) == 0x00)
1,050✔
1265
            return 1;
1,040✔
1266
        if ((c & 0xE0) == 0xC0)
10✔
1267
            return 2;
6✔
1268
        if ((c & 0xF0) == 0xE0)
4✔
1269
            return 3;
×
1270
        if ((c & 0xF8) == 0xF0)
4✔
1271
            return 4;
4✔
1272

1273
        throw std::runtime_error("Invalid UTF-8 sequence");
×
1274
    };
1275

1276
    CPLString newName(name);
230✔
1277
    bool hasNonASCII = false;
115✔
1278
    size_t i = 0;
115✔
1279

1280
    while (name[i] != '\0')
1,165✔
1281
    {
1282
        char c = name[i];
1,050✔
1283
        int len = getUTF8SequenceLength(c);
1,050✔
1284
        if (len == 1)
1,050✔
1285
        {
1286
            if (c == '-' || c == '#')
1,040✔
1287
                newName[i] = '_';
6✔
1288
            else
1289
                newName[i] = static_cast<char>(
1,034✔
1290
                    CPLToupper(static_cast<unsigned char>(c)));
1,034✔
1291
        }
1292
        else
1293
        {
1294
            hasNonASCII = true;
10✔
1295
        }
1296

1297
        i += len;
1,050✔
1298
    }
1299

1300
    if (!hasNonASCII)
115✔
1301
        return {OGRERR_NONE, newName};
222✔
1302

1303
    const char *sql = "SELECT UPPER(?) FROM DUMMY";
4✔
1304
    odbc::PreparedStatementRef stmt = PrepareStatement(sql);
8✔
1305
    stmt->setString(1, odbc::String(newName.c_str()));
4✔
1306
    odbc::ResultSetRef rsName = stmt->executeQuery();
8✔
1307
    OGRErr err = OGRERR_NONE;
4✔
1308
    if (rsName->next())
4✔
1309
    {
1310
        newName.swap(*rsName->getString(1));
4✔
1311
    }
1312
    else
1313
    {
1314
        err = OGRERR_FAILURE;
×
1315
        newName.clear();
×
1316
    }
1317
    rsName->close();
4✔
1318
    return {err, newName};
4✔
1319
}
1320

1321
/************************************************************************/
1322
/*                       CreateSpatialReference()                       */
1323
/************************************************************************/
1324

1325
void OGRHanaDataSource::CreateSpatialReferenceSystem(
×
1326
    const OGRSpatialReference &srs, int srid, const char *authorityName,
1327
    int authorityCode, const CPLString &wkt, const CPLString &proj4)
1328
{
1329
    CPLString refName((srs.IsProjected()) ? srs.GetAttrValue("PROJCS")
×
1330
                                          : srs.GetAttrValue("GEOGCS"));
×
1331
    if (refName.empty() || EQUAL(refName.c_str(), "UNKNOWN"))
×
1332
        refName = "OGR_PROJECTION_" + std::to_string(srid);
×
1333

1334
    OGRErr err = OGRERR_NONE;
×
1335
    CPLString ellipsoidParams;
×
1336
    const double semiMajor = srs.GetSemiMajor(&err);
×
1337
    if (OGRERR_NONE == err)
×
1338
        ellipsoidParams += " SEMI MAJOR AXIS " + std::to_string(semiMajor);
×
1339
    const double semiMinor = srs.GetSemiMinor(&err);
×
1340
    const double invFlattening = srs.GetInvFlattening(&err);
×
1341
    if (OGRERR_NONE == err)
×
1342
        ellipsoidParams +=
1343
            " INVERSE FLATTENING " + std::to_string(invFlattening);
×
1344
    else
1345
        ellipsoidParams += " SEMI MINOR AXIS " + std::to_string(semiMinor);
×
1346

1347
    const char *linearUnits = nullptr;
×
1348
    srs.GetLinearUnits(&linearUnits);
×
1349
    const char *angularUnits = nullptr;
×
1350
    srs.GetAngularUnits(&angularUnits);
×
1351

1352
    CPLString xRange, yRange;
×
1353
    double dfWestLongitudeDeg, dfSouthLatitudeDeg, dfEastLongitudeDeg,
1354
        dfNorthLatitudeDeg;
1355
    if (srs.GetAreaOfUse(&dfWestLongitudeDeg, &dfSouthLatitudeDeg,
×
1356
                         &dfEastLongitudeDeg, &dfNorthLatitudeDeg, nullptr))
1357
    {
1358
        xRange = CPLString().Printf("%s BETWEEN %f AND %f",
×
1359
                                    srs.IsGeographic() ? "LONGITUDE" : "X",
×
1360
                                    dfWestLongitudeDeg, dfEastLongitudeDeg);
×
1361
        yRange = CPLString().Printf("%s BETWEEN %f AND %f",
×
1362
                                    srs.IsGeographic() ? "LATITUDE" : "Y",
×
1363
                                    dfSouthLatitudeDeg, dfNorthLatitudeDeg);
×
1364
    }
1365
    else
1366
    {
1367
        xRange = CPLString().Printf("%s UNBOUNDED",
×
1368
                                    srs.IsGeographic() ? "LONGITUDE" : "X");
×
1369
        yRange = CPLString().Printf("%s UNBOUNDED ",
×
1370
                                    srs.IsGeographic() ? "LATITUDE" : "Y");
×
1371
    }
1372

1373
    CPLString organization;
×
1374
    if (authorityName != nullptr && authorityCode > 0)
×
1375
    {
1376
        organization = CPLString().Printf(
×
1377
            "ORGANIZATION %s IDENTIFIED BY %d",
1378
            QuotedIdentifier(authorityName).c_str(), authorityCode);
×
1379
    }
1380

1381
    CPLString sql = CPLString().Printf(
×
1382
        "CREATE SPATIAL REFERENCE SYSTEM %s "
1383
        "IDENTIFIED BY %d "
1384
        "TYPE %s "
1385
        "LINEAR UNIT OF MEASURE %s "
1386
        "ANGULAR UNIT OF MEASURE %s "
1387
        "%s "  // ELLIPSOID
1388
        "COORDINATE %s "
1389
        "COORDINATE %s "
1390
        "%s "  // ORGANIZATION
1391
        "DEFINITION %s "
1392
        "TRANSFORM DEFINITION %s",
1393
        QuotedIdentifier(refName).c_str(), srid,
×
1394
        srs.IsGeographic() ? "ROUND EARTH" : "PLANAR",
×
1395
        QuotedIdentifier(
×
1396
            (linearUnits == nullptr || EQUAL(linearUnits, "unknown"))
×
1397
                ? "metre"
1398
                : linearUnits)
1399
            .tolower()
×
1400
            .c_str(),
1401
        QuotedIdentifier(
×
1402
            (angularUnits == nullptr || EQUAL(angularUnits, "unknown"))
×
1403
                ? "degree"
1404
                : angularUnits)
1405
            .tolower()
×
1406
            .c_str(),
1407
        (ellipsoidParams.empty() ? ""
×
1408
                                 : ("ELLIPSOID" + ellipsoidParams).c_str()),
×
1409
        xRange.c_str(), yRange.c_str(), organization.c_str(),
1410
        Literal(wkt).c_str(), Literal(proj4).c_str());
×
1411

1412
    ExecuteSQL(sql);
×
1413
}
×
1414

1415
/************************************************************************/
1416
/*                       CreateParseArrayFunctions()                    */
1417
/************************************************************************/
1418

1419
void OGRHanaDataSource::CreateParseArrayFunctions(const char *schemaName)
1✔
1420
{
1421
    auto replaceAll = [](const CPLString &str, const CPLString &before,
8✔
1422
                         const CPLString &after) -> CPLString
1423
    {
1424
        CPLString res = str;
16✔
1425
        return res.replaceAll(before, after);
16✔
1426
    };
1427

1428
    // clang-format off
1429
    const CPLString parseStringArrayFunc =
1430
        "CREATE OR REPLACE FUNCTION {SCHEMA}.OGR_PARSE_STRING_ARRAY(IN str NCLOB, IN delimiter NVARCHAR(10))\n"
1431
          "RETURNS TABLE(VALUE NVARCHAR(512))\n"
1432
          "LANGUAGE SQLSCRIPT\n"
1433
          "SQL SECURITY INVOKER AS\n"
1434
        "BEGIN\n"
1435
            "DECLARE arrValues NVARCHAR(512) ARRAY;\n"
1436
            "DECLARE idx INTEGER = 1;\n"
1437
            "DECLARE curPos INTEGER = 1;\n"
1438
            "DECLARE lastPos INTEGER = 1;\n"
1439
            "DECLARE delimiterLength INTEGER = LENGTH(delimiter);\n"
1440

1441
            "IF(NOT(:str IS NULL)) THEN\n"
1442
               "WHILE(:curPos > 0) DO\n"
1443
                   "curPos = LOCATE(:str, :delimiter, :lastPos);\n"
1444
                   "IF :curPos = 0 THEN\n"
1445
                        "BREAK;\n"
1446
                    "END IF;\n"
1447

1448
                    "arrValues[:idx] = SUBSTRING(:str, :lastPos, :curPos - :lastPos);\n"
1449
                    "lastPos = :curPos + :delimiterLength;\n"
1450
                    "idx = :idx + 1;\n"
1451
                "END WHILE;\n"
1452

1453
                "arrValues[:idx] = SUBSTRING(:str, :lastPos, LENGTH(:str));\n"
1454
            "END IF;\n"
1455

1456
            "ret = UNNEST(:arrValues) AS(\"VALUE\");\n"
1457
            "RETURN SELECT * FROM :ret;\n"
1458
        "END;\n";
2✔
1459
    // clang-format on
1460

1461
    CPLString sql = replaceAll(parseStringArrayFunc, "{SCHEMA}",
1462
                               QuotedIdentifier(schemaName));
3✔
1463
    ExecuteSQL(sql);
1✔
1464

1465
    // clang-format off
1466
    const CPLString parseTypeArrayFunc =
1467
        "CREATE OR REPLACE FUNCTION {SCHEMA}.OGR_PARSE_{TYPE}_ARRAY(IN str NCLOB, IN delimiter NVARCHAR(10))\n"
1468
           "RETURNS TABLE(VALUE {TYPE})\n"
1469
           "LANGUAGE SQLSCRIPT\n"
1470
           "SQL SECURITY INVOKER AS\n"
1471
        "BEGIN\n"
1472
            "DECLARE arrValues {TYPE} ARRAY;\n"
1473
            "DECLARE elemValue STRING;\n"
1474
            "DECLARE idx INTEGER = 1;\n"
1475
            "DECLARE CURSOR cursor_values FOR\n"
1476
                  "SELECT * FROM OGR_PARSE_STRING_ARRAY(:str, :delimiter);\n"
1477

1478
            "FOR row_value AS cursor_values DO\n"
1479
                "elemValue = TRIM(row_value.VALUE);\n"
1480
                "IF(UPPER(elemValue) = 'NULL') THEN\n"
1481
                    "arrValues[:idx] = CAST(NULL AS {TYPE});\n"
1482
                "ELSE\n"
1483
                    "arrValues[:idx] = CAST(:elemValue AS {TYPE});\n"
1484
                "END IF;\n"
1485
                "idx = :idx + 1;\n"
1486
            "END FOR;\n"
1487

1488
            "ret = UNNEST(:arrValues) AS(\"VALUE\");\n"
1489
            "RETURN SELECT * FROM :ret;\n"
1490
        "END;\n";
2✔
1491
    // clang-format on
1492

1493
    sql = replaceAll(parseTypeArrayFunc, "{SCHEMA}",
2✔
1494
                     QuotedIdentifier(schemaName));
3✔
1495

1496
    for (const CPLString &type : GetSupportedArrayTypes())
8✔
1497
    {
1498
        if (type == "STRING")
7✔
1499
            continue;
1✔
1500
        ExecuteSQL(replaceAll(sql, "{TYPE}", type));
6✔
1501
    }
1502
}
1✔
1503

1504
/************************************************************************/
1505
/*                       ParseArrayFunctionsExist()                     */
1506
/************************************************************************/
1507

1508
bool OGRHanaDataSource::ParseArrayFunctionsExist(const char *schemaName)
1✔
1509
{
1510
    const char *sql =
1✔
1511
        "SELECT COUNT(*) FROM FUNCTIONS WHERE SCHEMA_NAME = ? AND "
1512
        "FUNCTION_NAME LIKE 'OGR_PARSE_%_ARRAY'";
1513
    odbc::PreparedStatementRef stmt = PrepareStatement(sql);
2✔
1514
    stmt->setString(1, odbc::String(schemaName));
1✔
1515
    odbc::ResultSetRef rsFunctions = stmt->executeQuery();
1✔
1516
    auto numFunctions = rsFunctions->next() ? *rsFunctions->getLong(1) : 0;
1✔
1517
    rsFunctions->close();
1✔
1518
    return (static_cast<std::size_t>(numFunctions) ==
1✔
1519
            GetSupportedArrayTypes().size());
2✔
1520
}
1521

1522
/************************************************************************/
1523
/*                               GetLayer()                             */
1524
/************************************************************************/
1525

1526
OGRLayer *OGRHanaDataSource::GetLayer(int index)
43✔
1527
{
1528
    if (index < 0 || static_cast<std::size_t>(index) >= layers_.size())
43✔
1529
        return nullptr;
6✔
1530
    return layers_[static_cast<std::size_t>(index)].get();
37✔
1531
}
1532

1533
/************************************************************************/
1534
/*                           GetLayerByName()                           */
1535
/************************************************************************/
1536

1537
OGRLayer *OGRHanaDataSource::GetLayerByName(const char *name)
39✔
1538
{
1539
    return GetLayer(FindLayerByName(name));
39✔
1540
}
1541

1542
/************************************************************************/
1543
/*                              ICreateLayer()                          */
1544
/************************************************************************/
1545

1546
OGRLayer *
1547
OGRHanaDataSource::ICreateLayer(const char *layerNameIn,
18✔
1548
                                const OGRGeomFieldDefn *poGeomFieldDefn,
1549
                                CSLConstList options)
1550
{
1551
    if (layerNameIn == nullptr)
18✔
1552
        return nullptr;
×
1553

1554
    const auto geomType =
1555
        poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
18✔
1556
    const auto srs =
1557
        poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
18✔
1558

1559
    // Check if we are allowed to create new objects in the database
1560
    odbc::DatabaseMetaDataRef dmd = conn_->getDatabaseMetaData();
36✔
1561
    if (dmd->isReadOnly())
18✔
1562
    {
1563
        CPLError(CE_Failure, CPLE_AppDefined,
×
1564
                 "Unable to create Layer %s.\n"
1565
                 "Database %s is read only.",
1566
                 layerNameIn, dmd->getDatabaseName().c_str());
×
1567
        return nullptr;
×
1568
    }
1569

1570
    bool launderNames = CPLFetchBool(
18✔
1571
        options, OGRHanaLayerCreationOptionsConstants::LAUNDER, true);
1572
    CPLString layerName(layerNameIn);
36✔
1573
    if (launderNames)
18✔
1574
    {
1575
        auto nameRes = LaunderName(layerNameIn);
17✔
1576
        if (nameRes.first != OGRERR_NONE)
17✔
1577
            return nullptr;
×
1578
        layerName.swap(nameRes.second);
17✔
1579
    }
1580

1581
    CPLDebug("HANA", "Creating layer %s.", layerName.c_str());
18✔
1582

1583
    int layerIndex = FindLayerByName(layerName.c_str());
18✔
1584
    if (layerIndex >= 0)
18✔
1585
    {
1586
        bool overwriteLayer = CPLFetchBool(
×
1587
            options, OGRHanaLayerCreationOptionsConstants::OVERWRITE, false);
1588
        if (!overwriteLayer)
×
1589
        {
1590
            CPLError(CE_Failure, CPLE_AppDefined,
×
1591
                     "Layer %s already exists, CreateLayer failed.\n"
1592
                     "Use the layer creation option OVERWRITE=YES to "
1593
                     "replace it.",
1594
                     layerName.c_str());
1595
            return nullptr;
×
1596
        }
1597

1598
        DeleteLayer(layerIndex);
×
1599
    }
1600

1601
    int batchSize =
1602
        CPLFetchInt(options, OGRHanaLayerCreationOptionsConstants::BATCH_SIZE,
18✔
1603
                    DEFAULT_BATCH_SIZE);
1604
    if (batchSize <= 0)
18✔
1605
    {
1606
        CPLError(CE_Failure, CPLE_AppDefined,
×
1607
                 "Unable to create layer %s. The value of %s parameter must be "
1608
                 "greater than 0.",
1609
                 layerName.c_str(),
1610
                 OGRHanaLayerCreationOptionsConstants::BATCH_SIZE);
1611
        return nullptr;
×
1612
    }
1613

1614
    int defaultStringSize = CPLFetchInt(
18✔
1615
        options, OGRHanaLayerCreationOptionsConstants::DEFAULT_STRING_SIZE,
1616
        DEFAULT_STRING_SIZE);
1617
    if (defaultStringSize <= 0)
18✔
1618
    {
1619
        CPLError(CE_Failure, CPLE_AppDefined,
×
1620
                 "Unable to create layer %s. The value of %s parameter must be "
1621
                 "greater than 0.",
1622
                 layerName.c_str(),
1623
                 OGRHanaLayerCreationOptionsConstants::DEFAULT_STRING_SIZE);
1624
        return nullptr;
×
1625
    }
1626

1627
    CPLString geomColumnName(CSLFetchNameValueDef(
1628
        options, OGRHanaLayerCreationOptionsConstants::GEOMETRY_NAME,
1629
        "OGR_GEOMETRY"));
36✔
1630
    if (launderNames)
18✔
1631
    {
1632
        auto nameRes = LaunderName(geomColumnName.c_str());
17✔
1633
        if (nameRes.first != OGRERR_NONE)
17✔
1634
            return nullptr;
×
1635
        geomColumnName.swap(nameRes.second);
17✔
1636
    }
1637

1638
    const bool geomColumnNullable = CPLFetchBool(
18✔
1639
        options, OGRHanaLayerCreationOptionsConstants::GEOMETRY_NULLABLE, true);
1640
    CPLString geomColumnIndexType(CSLFetchNameValueDef(
1641
        options, OGRHanaLayerCreationOptionsConstants::GEOMETRY_INDEX,
1642
        "DEFAULT"));
36✔
1643

1644
    const char *paramFidName = CSLFetchNameValueDef(
18✔
1645
        options, OGRHanaLayerCreationOptionsConstants::FID, "OGR_FID");
1646
    CPLString fidName(paramFidName);
36✔
1647
    if (launderNames)
18✔
1648
    {
1649
        auto nameRes = LaunderName(paramFidName);
17✔
1650
        if (nameRes.first != OGRERR_NONE)
17✔
1651
            return nullptr;
×
1652
        fidName.swap(nameRes.second);
17✔
1653
    }
1654

1655
    CPLString fidType =
1656
        CPLFetchBool(options, OGRHanaLayerCreationOptionsConstants::FID64,
18✔
1657
                     false)
1658
            ? "BIGINT"
1659
            : "INTEGER";
36✔
1660

1661
    CPLDebug("HANA", "Geometry Column Name %s.", geomColumnName.c_str());
18✔
1662
    CPLDebug("HANA", "FID Column Name %s, Type %s.", fidName.c_str(),
18✔
1663
             fidType.c_str());
1664

1665
    int srid = CPLFetchInt(options, OGRHanaLayerCreationOptionsConstants::SRID,
18✔
1666
                           UNDETERMINED_SRID);
1667
    if (srid < 0 && srs != nullptr)
18✔
1668
        srid = GetSrsId(srs);
12✔
1669

1670
    try
1671
    {
1672
        CreateTable(layerName, fidName, fidType, geomColumnName, geomType,
18✔
1673
                    geomColumnNullable, geomColumnIndexType, srid);
1674
    }
1675
    catch (const odbc::Exception &ex)
×
1676
    {
1677
        CPLError(CE_Failure, CPLE_AppDefined,
×
1678
                 "Unable to create layer %s. CreateLayer failed:%s\n",
1679
                 layerName.c_str(), ex.what());
×
1680
        return nullptr;
×
1681
    }
1682

1683
    // Create new layer object
1684
    auto layer = std::make_unique<OGRHanaTableLayer>(this, schemaName_.c_str(),
×
1685
                                                     layerName.c_str(), true);
36✔
1686
    if (geomType != wkbNone && layer->GetLayerDefn()->GetGeomFieldCount() > 0)
18✔
1687
        layer->GetLayerDefn()->GetGeomFieldDefn(0)->SetNullable(FALSE);
12✔
1688
    if (batchSize > 0)
18✔
1689
        layer->SetBatchSize(static_cast<std::size_t>(batchSize));
18✔
1690
    if (defaultStringSize > 0)
18✔
1691
        layer->SetDefaultStringSize(
18✔
1692
            static_cast<std::size_t>(defaultStringSize));
1693
    layer->SetLaunderFlag(launderNames);
18✔
1694
    layer->SetPrecisionFlag(CPLFetchBool(
18✔
1695
        options, OGRHanaLayerCreationOptionsConstants::PRECISION, true));
1696
    layer->SetCustomColumnTypes(CSLFetchNameValue(
18✔
1697
        options, OGRHanaLayerCreationOptionsConstants::COLUMN_TYPES));
1698

1699
    layers_.push_back(std::move(layer));
18✔
1700

1701
    return layers_.back().get();
18✔
1702
}
1703

1704
/************************************************************************/
1705
/*                           TestCapability()                           */
1706
/************************************************************************/
1707

1708
int OGRHanaDataSource::TestCapability(const char *capabilities)
27✔
1709
{
1710
    if (EQUAL(capabilities, ODsCCreateLayer))
27✔
1711
        return updateMode_;
4✔
1712
    else if (EQUAL(capabilities, ODsCDeleteLayer))
23✔
1713
        return updateMode_;
4✔
1714
    else if (EQUAL(capabilities, ODsCCreateGeomFieldAfterCreateLayer))
19✔
1715
        return updateMode_;
2✔
1716
    else if (EQUAL(capabilities, ODsCMeasuredGeometries))
17✔
1717
        return TRUE;
5✔
1718
    else if (EQUAL(capabilities, ODsCRandomLayerWrite))
12✔
1719
        return updateMode_;
×
1720
    else if (EQUAL(capabilities, ODsCTransactions))
12✔
1721
        return TRUE;
4✔
1722
    else
1723
        return FALSE;
8✔
1724
}
1725

1726
/************************************************************************/
1727
/*                             ExecuteSQL()                             */
1728
/************************************************************************/
1729

1730
OGRLayer *OGRHanaDataSource::ExecuteSQL(const char *sqlCommand,
29✔
1731
                                        OGRGeometry *spatialFilter,
1732
                                        const char *dialect)
1733
{
1734
    sqlCommand = SkipLeadingSpaces(sqlCommand);
29✔
1735

1736
    if (IsGenericSQLDialect(dialect))
29✔
1737
        return GDALDataset::ExecuteSQL(sqlCommand, spatialFilter, dialect);
×
1738

1739
    if (STARTS_WITH_CI(sqlCommand, "DELLAYER:"))
29✔
1740
    {
1741
        const char *layerName = SkipLeadingSpaces(sqlCommand + 9);
10✔
1742
        int layerIndex = FindLayerByName(layerName);
10✔
1743
        if (layerIndex >= 0)
10✔
1744
            DeleteLayer(layerIndex);
×
1745
        return nullptr;
10✔
1746
    }
1747
    if (STARTS_WITH_CI(sqlCommand, "SELECT"))
19✔
1748
    {
1749
        auto stmt = PrepareStatement(sqlCommand);
28✔
1750
        if (stmt.isNull())
14✔
1751
            return nullptr;
1✔
1752

1753
        auto layer = std::make_unique<OGRHanaResultLayer>(this, sqlCommand);
26✔
1754
        if (spatialFilter != nullptr)
13✔
1755
            layer->SetSpatialFilter(spatialFilter);
1✔
1756
        return layer.release();
13✔
1757
    }
1758

1759
    try
1760
    {
1761
        ExecuteSQL(sqlCommand);
8✔
1762
    }
1763
    catch (const odbc::Exception &ex)
6✔
1764
    {
1765
        CPLError(CE_Failure, CPLE_AppDefined,
3✔
1766
                 "Failed to execute SQL statement '%s': %s", sqlCommand,
1767
                 ex.what());
3✔
1768
    }
1769

1770
    return nullptr;
5✔
1771
}
1772

1773
/************************************************************************/
1774
/*                           StartTransaction()                         */
1775
/************************************************************************/
1776

1777
OGRErr OGRHanaDataSource::StartTransaction(CPL_UNUSED int bForce)
15✔
1778
{
1779
    if (isTransactionStarted_)
15✔
1780
    {
1781
        CPLError(CE_Failure, CPLE_AppDefined,
2✔
1782
                 "Transaction already established");
1783
        return OGRERR_FAILURE;
2✔
1784
    }
1785

1786
    isTransactionStarted_ = true;
13✔
1787
    return OGRERR_NONE;
13✔
1788
}
1789

1790
/************************************************************************/
1791
/*                           CommitTransaction()                        */
1792
/************************************************************************/
1793

1794
OGRErr OGRHanaDataSource::CommitTransaction()
10✔
1795
{
1796
    if (!isTransactionStarted_)
10✔
1797
    {
1798
        CPLError(CE_Failure, CPLE_AppDefined, "Transaction not established");
2✔
1799
        return OGRERR_FAILURE;
2✔
1800
    }
1801

1802
    isTransactionStarted_ = false;
8✔
1803

1804
    try
1805
    {
1806
        for (size_t i = 0; i < layers_.size(); ++i)
127✔
1807
        {
1808
            OGRHanaLayer *layer = static_cast<OGRHanaLayer *>(layers_[i].get());
119✔
1809
            if (layer->IsTableLayer())
119✔
1810
            {
1811
                OGRHanaTableLayer *tableLayer =
119✔
1812
                    static_cast<OGRHanaTableLayer *>(layer);
1813
                tableLayer->FlushPendingBatches(false);
119✔
1814
            }
1815
        }
1816

1817
        conn_->commit();
8✔
1818
    }
1819
    catch (const odbc::Exception &ex)
×
1820
    {
1821
        CPLError(CE_Failure, CPLE_AppDefined,
×
1822
                 "Failed to commit transaction: %s", ex.what());
×
1823
        return OGRERR_FAILURE;
×
1824
    }
1825
    return OGRERR_NONE;
8✔
1826
}
1827

1828
/************************************************************************/
1829
/*                           RollbackTransaction()                      */
1830
/************************************************************************/
1831

1832
OGRErr OGRHanaDataSource::RollbackTransaction()
6✔
1833
{
1834
    if (!isTransactionStarted_)
6✔
1835
    {
1836
        CPLError(CE_Failure, CPLE_AppDefined, "Transaction not established");
2✔
1837
        return OGRERR_FAILURE;
2✔
1838
    }
1839

1840
    isTransactionStarted_ = false;
4✔
1841

1842
    try
1843
    {
1844
        conn_->rollback();
4✔
1845
    }
1846
    catch (const odbc::Exception &ex)
×
1847
    {
1848
        CPLError(CE_Failure, CPLE_AppDefined,
×
1849
                 "Failed to roll back transaction: %s", ex.what());
×
1850
        return OGRERR_FAILURE;
×
1851
    }
1852
    return OGRERR_NONE;
4✔
1853
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc