Line data Source code
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 65 : CPLString BuildConnectionString(char **openOptions)
39 : {
40 : // See notes for constructing connection string for HANA
41 : // https://help.sap.com/docs/SAP_HANA_CLIENT/f1b440ded6144a54ada97ff95dac7adf/7cab593774474f2f8db335710b2f5c50.html
42 :
43 130 : std::vector<CPLString> params;
44 65 : bool isValid = true;
45 130 : const CPLString specialChars("[]{}(),;?*=!@");
46 :
47 1051 : auto getOptValue = [&](const char *optionName, bool mandatory = false)
48 : {
49 : const char *paramValue =
50 1051 : CSLFetchNameValueDef(openOptions, optionName, nullptr);
51 1051 : if (mandatory && paramValue == nullptr)
52 : {
53 9 : isValid = false;
54 9 : CPLError(CE_Failure, CPLE_AppDefined,
55 : "Mandatory connection parameter '%s' is missing.",
56 : optionName);
57 : }
58 1051 : return paramValue;
59 65 : };
60 :
61 976 : auto addParameter = [&](const char *paramName, const char *paramValue)
62 : {
63 976 : if (paramValue == nullptr)
64 421 : return;
65 :
66 1110 : CPLString value(paramValue);
67 555 : if (value.find_first_of(specialChars) != std::string::npos)
68 : {
69 56 : value.replaceAll("}", "}}");
70 56 : params.push_back(CPLString(paramName) + "={" + value + "}");
71 : }
72 : else
73 : {
74 499 : params.push_back(CPLString(paramName) + "=" + value);
75 : }
76 65 : };
77 :
78 720 : auto addOptParameter = [&](const char *optionName, const char *paramName,
79 : bool mandatory = false)
80 : {
81 720 : const char *paramValue = getOptValue(optionName, mandatory);
82 720 : addParameter(paramName, paramValue);
83 785 : };
84 :
85 12 : auto checkIgnoredOptParameter = [&](const char *optionName)
86 : {
87 12 : if (getOptValue(optionName))
88 0 : CPLError(CE_Failure, CPLE_AppDefined,
89 : "Connection parameter '%s' is ignored in the current "
90 : "combination.",
91 : optionName);
92 77 : };
93 :
94 65 : if (const char *paramUserStoreKey =
95 65 : getOptValue(OGRHanaOpenOptionsConstants::USER_STORE_KEY))
96 : {
97 0 : addOptParameter(OGRHanaOpenOptionsConstants::DRIVER, "DRIVER", true);
98 0 : CPLString node = CPLString().Printf("@%s", paramUserStoreKey);
99 0 : addParameter("SERVERNODE", node.c_str());
100 :
101 0 : checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::DSN);
102 0 : checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::HOST);
103 0 : checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::PORT);
104 0 : checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::DATABASE);
105 0 : checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::USER);
106 0 : checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::PASSWORD);
107 : }
108 65 : else if (const char *paramDSN =
109 65 : getOptValue(OGRHanaOpenOptionsConstants::DSN))
110 : {
111 3 : addParameter(OGRHanaOpenOptionsConstants::DSN, paramDSN);
112 3 : addOptParameter(OGRHanaOpenOptionsConstants::USER, "UID", true);
113 3 : addOptParameter(OGRHanaOpenOptionsConstants::PASSWORD, "PWD", true);
114 :
115 3 : checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::DRIVER);
116 3 : checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::HOST);
117 3 : checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::PORT);
118 3 : checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::DATABASE);
119 : }
120 : else
121 : {
122 62 : addOptParameter(OGRHanaOpenOptionsConstants::DRIVER, "DRIVER", true);
123 : const char *paramHost =
124 62 : getOptValue(OGRHanaOpenOptionsConstants::HOST, true);
125 : const char *paramPort =
126 62 : getOptValue(OGRHanaOpenOptionsConstants::PORT, true);
127 62 : if (paramHost != nullptr && paramPort != nullptr)
128 : {
129 120 : CPLString node = CPLString().Printf("%s:%s", paramHost, paramPort);
130 60 : addParameter("SERVERNODE", node.c_str());
131 : }
132 62 : addOptParameter(OGRHanaOpenOptionsConstants::USER, "UID", true);
133 62 : addOptParameter(OGRHanaOpenOptionsConstants::PASSWORD, "PWD", true);
134 62 : addOptParameter(OGRHanaOpenOptionsConstants::DATABASE, "DATABASENAME");
135 : }
136 :
137 65 : if (const char *paramSchema =
138 65 : getOptValue(OGRHanaOpenOptionsConstants::SCHEMA, true))
139 : {
140 126 : CPLString schema = CPLString().Printf("\"%s\"", paramSchema);
141 63 : addParameter("CURRENTSCHEMA", schema.c_str());
142 : }
143 :
144 65 : if (CPLFetchBool(openOptions, OGRHanaOpenOptionsConstants::ENCRYPT, false))
145 : {
146 56 : addOptParameter(OGRHanaOpenOptionsConstants::ENCRYPT, "ENCRYPT");
147 56 : addOptParameter(OGRHanaOpenOptionsConstants::SSL_CRYPTO_PROVIDER,
148 : "sslCryptoProvider");
149 56 : addOptParameter(OGRHanaOpenOptionsConstants::SSL_KEY_STORE,
150 : "sslKeyStore");
151 56 : addOptParameter(OGRHanaOpenOptionsConstants::SSL_TRUST_STORE,
152 : "sslTrustStore");
153 56 : addOptParameter(OGRHanaOpenOptionsConstants::SSL_VALIDATE_CERTIFICATE,
154 : "sslValidateCertificate");
155 56 : addOptParameter(OGRHanaOpenOptionsConstants::SSL_HOST_NAME_CERTIFICATE,
156 : "sslHostNameInCertificate");
157 : }
158 :
159 65 : addOptParameter(OGRHanaOpenOptionsConstants::PACKET_SIZE, "PACKETSIZE");
160 65 : addOptParameter(OGRHanaOpenOptionsConstants::SPLIT_BATCH_COMMANDS,
161 : "SPLITBATCHCOMMANDS");
162 65 : addParameter("CHAR_AS_UTF8", "1");
163 :
164 130 : CPLString appName;
165 65 : appName.Printf("GDAL %s", GDALVersionInfo("RELEASE_NAME"));
166 65 : addParameter("sessionVariable:APPLICATION", appName.c_str());
167 :
168 130 : return isValid ? JoinStrings(params, ";") : "";
169 : }
170 :
171 54 : int CPLFetchInt(CSLConstList papszStrList, const char *pszKey, int defaultValue)
172 : {
173 54 : const char *const pszValue = CSLFetchNameValue(papszStrList, pszKey);
174 54 : if (pszValue == nullptr)
175 54 : return defaultValue;
176 0 : return atoi(pszValue);
177 : }
178 :
179 33 : int GetSrid(odbc::ResultSet &resultSet)
180 : {
181 33 : int srid = UNDETERMINED_SRID;
182 33 : while (resultSet.next())
183 : {
184 30 : odbc::Int val = resultSet.getInt(1);
185 30 : if (!val.isNull())
186 : {
187 30 : srid = *val;
188 30 : break;
189 : }
190 : }
191 33 : resultSet.close();
192 33 : return srid;
193 : }
194 :
195 24 : int GetColumnSrid(odbc::Connection &conn, const CPLString &schemaName,
196 : const CPLString &tableName, const CPLString &columnName)
197 : {
198 : CPLString sql =
199 : "SELECT SRS_ID FROM SYS.ST_GEOMETRY_COLUMNS WHERE SCHEMA_NAME = ?"
200 48 : " AND TABLE_NAME = ? AND COLUMN_NAME = ?";
201 24 : odbc::PreparedStatementRef stmt = conn.prepareStatement(sql.c_str());
202 24 : stmt->setString(1, odbc::String(schemaName));
203 24 : stmt->setString(2, odbc::String(tableName));
204 24 : if (columnName != nullptr)
205 24 : stmt->setString(3, odbc::String(columnName));
206 48 : return GetSrid(*stmt->executeQuery());
207 : }
208 :
209 9 : int GetColumnSrid(odbc::Connection &conn, const CPLString &query,
210 : const CPLString &columnName)
211 : {
212 18 : CPLString clmName = QuotedIdentifier(columnName);
213 :
214 : CPLString sql =
215 9 : CPLString().Printf("SELECT %s.ST_SRID() FROM (%s) WHERE %s IS NOT NULL",
216 18 : clmName.c_str(), query.c_str(), clmName.c_str());
217 :
218 9 : odbc::StatementRef stmt = conn.createStatement();
219 18 : return GetSrid(*stmt->executeQuery(sql.c_str()));
220 : }
221 :
222 13 : int GetSridWithFilter(odbc::Connection &conn, const CPLString &whereCondition)
223 : {
224 13 : CPLAssert(whereCondition != nullptr);
225 :
226 13 : int ret = UNDETERMINED_SRID;
227 :
228 26 : odbc::StatementRef stmt = conn.createStatement();
229 13 : CPLString sql = CPLString().Printf(
230 : "SELECT SRS_ID FROM SYS.ST_SPATIAL_REFERENCE_SYSTEMS WHERE %s",
231 26 : whereCondition.c_str());
232 13 : odbc::ResultSetRef rsSrs = stmt->executeQuery(sql.c_str());
233 13 : while (rsSrs->next())
234 : {
235 13 : odbc::Int val = rsSrs->getInt(1);
236 13 : if (!val.isNull())
237 : {
238 13 : ret = *val;
239 13 : break;
240 : }
241 : }
242 13 : rsSrs->close();
243 :
244 26 : return ret;
245 : }
246 :
247 28 : CPLString GetSrsWktById(odbc::Connection &conn, int srid)
248 : {
249 28 : CPLString ret;
250 28 : const char *sql = "SELECT DEFINITION FROM "
251 : "SYS.ST_SPATIAL_REFERENCE_SYSTEMS WHERE SRS_ID = ?";
252 56 : odbc::PreparedStatementRef stmt = conn.prepareStatement(sql);
253 28 : stmt->setInt(1, odbc::Int(srid));
254 56 : odbc::ResultSetRef rs = stmt->executeQuery();
255 28 : while (rs->next())
256 : {
257 28 : odbc::String wkt = rs->getString(1);
258 28 : if (!wkt.isNull())
259 : {
260 28 : ret = *wkt;
261 28 : if (!ret.empty())
262 28 : break;
263 : }
264 : }
265 28 : rs->close();
266 :
267 56 : return ret;
268 : }
269 :
270 32 : OGRwkbGeometryType GetGeometryType(odbc::Connection &conn,
271 : const CPLString &query,
272 : const CPLString &columnName)
273 : {
274 64 : CPLString clmName = QuotedIdentifier(columnName);
275 :
276 32 : CPLString sql = CPLString().Printf(
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 64 : clmName.c_str());
281 :
282 64 : odbc::StatementRef stmt = conn.createStatement();
283 32 : odbc::ResultSetRef rsGeomInfo = stmt->executeQuery(sql.c_str());
284 32 : OGRwkbGeometryType ret = wkbUnknown;
285 32 : std::size_t i = 0;
286 48 : while (rsGeomInfo->next())
287 : {
288 16 : ++i;
289 16 : auto typeName = rsGeomInfo->getString(1);
290 16 : auto hasZ = rsGeomInfo->getInt(2);
291 16 : auto hasM = rsGeomInfo->getInt(3);
292 : OGRwkbGeometryType geomType =
293 16 : ToWkbType(typeName->c_str(), *hasZ == 1, *hasM == 1);
294 16 : if (geomType == OGRwkbGeometryType::wkbUnknown)
295 0 : continue;
296 16 : if (ret == OGRwkbGeometryType::wkbUnknown)
297 16 : ret = geomType;
298 0 : else if (ret != geomType)
299 : {
300 0 : ret = OGRwkbGeometryType::wkbUnknown;
301 0 : break;
302 : }
303 : }
304 32 : rsGeomInfo->close();
305 :
306 32 : if (i == 0)
307 16 : ret = OGRwkbGeometryType::wkbUnknown;
308 64 : return ret;
309 : }
310 :
311 24 : GeometryColumnDescription GetGeometryColumnDescription(
312 : odbc::Connection &conn, const CPLString &schemaName,
313 : const CPLString &tableName, const CPLString &columnName,
314 : bool detectGeometryType)
315 : {
316 : OGRwkbGeometryType type =
317 : detectGeometryType
318 47 : ? GetGeometryType(conn,
319 46 : GetFullTableNameQuoted(schemaName, tableName),
320 : columnName)
321 24 : : OGRwkbGeometryType::wkbUnknown;
322 24 : int srid = GetColumnSrid(conn, schemaName, tableName, columnName);
323 :
324 24 : return {columnName, type, srid, false};
325 : }
326 :
327 : GeometryColumnDescription
328 9 : 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.
334 9 : bool needColumnName = false;
335 18 : std::vector<char> specialChars = {'(', ')', '\'', ' '};
336 45 : for (const char c : specialChars)
337 : {
338 36 : if (columnName.find(c) != CPLString::npos)
339 : {
340 0 : needColumnName = true;
341 0 : break;
342 : }
343 : }
344 :
345 18 : CPLString preparedQuery = query;
346 9 : CPLString clmName = columnName;
347 9 : if (needColumnName)
348 : {
349 : auto it = std::search(
350 : preparedQuery.begin(), preparedQuery.end(), columnName.begin(),
351 : columnName.end(),
352 0 : [](char ch1, char ch2)
353 : {
354 0 : return CPLToupper(static_cast<unsigned char>(ch1)) ==
355 0 : CPLToupper(static_cast<unsigned char>(ch2));
356 0 : });
357 :
358 0 : if (it != preparedQuery.end())
359 : {
360 0 : auto pos = it - preparedQuery.begin();
361 0 : CPLString newName = columnName + " AS \"tmp_geom_field\"";
362 : preparedQuery.replace(static_cast<std::size_t>(pos),
363 : columnName.length(), newName, 0,
364 0 : newName.length());
365 0 : clmName = "tmp_geom_field";
366 : }
367 : }
368 :
369 : OGRwkbGeometryType type =
370 : detectGeometryType
371 18 : ? GetGeometryType(conn, "(" + preparedQuery + ")", clmName)
372 9 : : OGRwkbGeometryType::wkbUnknown;
373 9 : int srid = GetColumnSrid(conn, preparedQuery, clmName);
374 :
375 18 : return {columnName, type, srid, false};
376 : }
377 :
378 12 : CPLString FormatDefaultValue(const char *value, short dataType)
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 12 : if (EQUAL(value, "NULL"))
394 0 : return value;
395 :
396 12 : switch (dataType)
397 : {
398 1 : case QGRHanaDataTypes::Bit:
399 : case QGRHanaDataTypes::Boolean:
400 1 : return value;
401 6 : case QGRHanaDataTypes::TinyInt:
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 6 : return value;
411 1 : case QGRHanaDataTypes::Char:
412 : case QGRHanaDataTypes::VarChar:
413 : case QGRHanaDataTypes::LongVarChar:
414 : case QGRHanaDataTypes::WChar:
415 : case QGRHanaDataTypes::WVarChar:
416 : case QGRHanaDataTypes::WLongVarChar:
417 1 : return Literal(value);
418 1 : case QGRHanaDataTypes::Binary:
419 : case QGRHanaDataTypes::VarBinary:
420 : case QGRHanaDataTypes::LongVarBinary:
421 : case QGRHanaDataTypes::RealVector:
422 1 : return value;
423 1 : case QGRHanaDataTypes::Date:
424 : case QGRHanaDataTypes::TypeDate:
425 1 : if (EQUAL(value, "CURRENT_DATE"))
426 0 : return value;
427 1 : return Literal(value);
428 1 : case QGRHanaDataTypes::Time:
429 : case QGRHanaDataTypes::TypeTime:
430 1 : if (EQUAL(value, "CURRENT_TIME"))
431 0 : return value;
432 1 : return Literal(value);
433 1 : case QGRHanaDataTypes::Timestamp:
434 : case QGRHanaDataTypes::TypeTimestamp:
435 1 : if (EQUAL(value, "CURRENT_TIMESTAMP"))
436 0 : return value;
437 1 : return Literal(value);
438 0 : default:
439 0 : return value;
440 : }
441 : }
442 :
443 11 : short GetArrayDataType(const CPLString &typeName)
444 : {
445 11 : if (typeName == "BOOLEAN ARRAY")
446 1 : return QGRHanaDataTypes::Boolean;
447 10 : else if (typeName == "TINYINT ARRAY")
448 0 : return QGRHanaDataTypes::TinyInt;
449 10 : else if (typeName == "SMALLINT ARRAY")
450 1 : return QGRHanaDataTypes::SmallInt;
451 9 : else if (typeName == "INTEGER ARRAY")
452 2 : return QGRHanaDataTypes::Integer;
453 7 : else if (typeName == "BIGINT ARRAY")
454 2 : return QGRHanaDataTypes::BigInt;
455 5 : else if (typeName == "DOUBLE ARRAY")
456 2 : return QGRHanaDataTypes::Double;
457 3 : else if (typeName == "REAL ARRAY")
458 0 : return QGRHanaDataTypes::Float;
459 3 : else if (typeName == "DECIMAL ARRAY" || typeName == "SMALLDECIMAL ARRAY")
460 0 : return QGRHanaDataTypes::Decimal;
461 3 : else if (typeName == "CHAR ARRAY")
462 0 : return QGRHanaDataTypes::Char;
463 3 : else if (typeName == "VARCHAR ARRAY")
464 0 : return QGRHanaDataTypes::VarChar;
465 3 : else if (typeName == "NCHAR ARRAY")
466 0 : return QGRHanaDataTypes::WChar;
467 3 : else if (typeName == "NVARCHAR ARRAY")
468 3 : return QGRHanaDataTypes::WVarChar;
469 0 : else if (typeName == "DATE ARRAY")
470 0 : return QGRHanaDataTypes::Date;
471 0 : else if (typeName == "TIME ARRAY")
472 0 : return QGRHanaDataTypes::Time;
473 0 : else if (typeName == "TIMESTAMP ARRAY" || typeName == "SECONDDATE ARRAY")
474 0 : return QGRHanaDataTypes::Timestamp;
475 :
476 0 : return QGRHanaDataTypes::Unknown;
477 : }
478 :
479 2 : std::vector<CPLString> GetSupportedArrayTypes()
480 : {
481 16 : return {"TINYINT", "SMALLINT", "INT", "BIGINT", "REAL", "DOUBLE", "STRING"};
482 : }
483 :
484 188 : bool IsKnownDataType(short dataType)
485 : {
486 187 : return dataType == QGRHanaDataTypes::Bit ||
487 187 : dataType == QGRHanaDataTypes::Boolean ||
488 187 : dataType == QGRHanaDataTypes::TinyInt ||
489 185 : dataType == QGRHanaDataTypes::SmallInt ||
490 132 : dataType == QGRHanaDataTypes::Integer ||
491 108 : dataType == QGRHanaDataTypes::BigInt ||
492 88 : dataType == QGRHanaDataTypes::Double ||
493 87 : dataType == QGRHanaDataTypes::Real ||
494 87 : dataType == QGRHanaDataTypes::Float ||
495 84 : dataType == QGRHanaDataTypes::Decimal ||
496 84 : dataType == QGRHanaDataTypes::Numeric ||
497 84 : dataType == QGRHanaDataTypes::Char ||
498 84 : dataType == QGRHanaDataTypes::VarChar ||
499 84 : dataType == QGRHanaDataTypes::LongVarChar ||
500 84 : dataType == QGRHanaDataTypes::WChar ||
501 44 : dataType == QGRHanaDataTypes::WVarChar ||
502 44 : dataType == QGRHanaDataTypes::WLongVarChar ||
503 44 : dataType == QGRHanaDataTypes::Date ||
504 43 : dataType == QGRHanaDataTypes::TypeDate ||
505 43 : dataType == QGRHanaDataTypes::Time ||
506 42 : dataType == QGRHanaDataTypes::TypeTime ||
507 42 : dataType == QGRHanaDataTypes::Timestamp ||
508 40 : dataType == QGRHanaDataTypes::TypeTimestamp ||
509 40 : dataType == QGRHanaDataTypes::Binary ||
510 39 : dataType == QGRHanaDataTypes::VarBinary ||
511 39 : dataType == QGRHanaDataTypes::LongVarBinary ||
512 375 : dataType == QGRHanaDataTypes::Geometry ||
513 188 : dataType == QGRHanaDataTypes::RealVector;
514 : }
515 :
516 : } // anonymous namespace
517 :
518 : /************************************************************************/
519 : /* GetPrefix() */
520 : /************************************************************************/
521 :
522 195 : const char *OGRHanaDataSource::GetPrefix()
523 : {
524 195 : return HANA_PREFIX;
525 : }
526 :
527 : /************************************************************************/
528 : /* OGRHanaDataSource() */
529 : /************************************************************************/
530 :
531 65 : OGRHanaDataSource::OGRHanaDataSource()
532 : {
533 65 : }
534 :
535 : /************************************************************************/
536 : /* ~OGRHanaDataSource() */
537 : /************************************************************************/
538 :
539 130 : OGRHanaDataSource::~OGRHanaDataSource()
540 : {
541 65 : layers_.clear();
542 :
543 93 : for (const auto &kv : srsCache_)
544 : {
545 28 : OGRSpatialReference *srs = kv.second;
546 28 : if (srs != nullptr)
547 28 : srs->Release();
548 : }
549 65 : srsCache_.clear();
550 130 : }
551 :
552 : /************************************************************************/
553 : /* Open() */
554 : /************************************************************************/
555 :
556 65 : int OGRHanaDataSource::Open(const char *newName, char **openOptions, int update)
557 : {
558 65 : CPLAssert(layers_.size() == 0);
559 :
560 65 : if (!STARTS_WITH_CI(newName, GetPrefix()))
561 : {
562 0 : CPLError(CE_Failure, CPLE_AppDefined,
563 : "%s does not conform to HANA driver naming convention,"
564 : " %s*\n",
565 : newName, GetPrefix());
566 0 : return FALSE;
567 : }
568 :
569 65 : updateMode_ = update;
570 65 : detectGeometryType_ = CPLFetchBool(
571 : openOptions, OGRHanaOpenOptionsConstants::DETECT_GEOMETRY_TYPE, true);
572 :
573 65 : std::size_t prefixLength = strlen(GetPrefix());
574 : char **connOptions =
575 65 : CSLTokenizeStringComplex(newName + prefixLength, ";", TRUE, FALSE);
576 :
577 65 : const char *paramSchema = CSLFetchNameValueDef(
578 : connOptions, OGRHanaOpenOptionsConstants::SCHEMA, nullptr);
579 65 : if (paramSchema != nullptr)
580 63 : schemaName_ = paramSchema;
581 :
582 65 : int ret = FALSE;
583 :
584 65 : CPLString connectionStr = BuildConnectionString(connOptions);
585 :
586 65 : if (!connectionStr.empty())
587 : {
588 56 : connEnv_ = odbc::Environment::create();
589 56 : conn_ = connEnv_->createConnection();
590 56 : conn_->setAutoCommit(false);
591 :
592 56 : const char *paramConnTimeout = CSLFetchNameValueDef(
593 : connOptions, OGRHanaOpenOptionsConstants::CONNECTION_TIMEOUT,
594 : nullptr);
595 56 : if (paramConnTimeout != nullptr)
596 0 : conn_->setConnectionTimeout(
597 0 : static_cast<unsigned long>(atoi(paramConnTimeout)));
598 :
599 : try
600 : {
601 56 : conn_->connect(connectionStr.c_str());
602 : }
603 0 : catch (const odbc::Exception &ex)
604 : {
605 0 : CPLError(CE_Failure, CPLE_AppDefined,
606 0 : "HANA connection failed: %s\n", ex.what());
607 : }
608 :
609 56 : if (conn_->connected())
610 : {
611 56 : DetermineVersions();
612 :
613 56 : const char *paramTables = CSLFetchNameValueDef(
614 : connOptions, OGRHanaOpenOptionsConstants::TABLES, "");
615 56 : InitializeLayers(paramSchema, paramTables);
616 56 : ret = TRUE;
617 : }
618 : }
619 :
620 65 : CSLDestroy(connOptions);
621 :
622 65 : return ret;
623 : }
624 :
625 : /************************************************************************/
626 : /* DeleteLayer() */
627 : /************************************************************************/
628 :
629 0 : OGRErr OGRHanaDataSource::DeleteLayer(int index)
630 : {
631 0 : if (index < 0 || static_cast<std::size_t>(index) >= layers_.size())
632 0 : return OGRERR_FAILURE;
633 :
634 : const std::unique_ptr<OGRLayer> &layer =
635 0 : layers_[static_cast<std::size_t>(index)];
636 0 : CPLDebug("HANA", "DeleteLayer(%s)", layer->GetName());
637 :
638 0 : if (auto tableLayer = dynamic_cast<OGRHanaTableLayer *>(layer.get()))
639 : {
640 0 : OGRErr err = tableLayer->DropTable();
641 0 : if (OGRERR_NONE == err)
642 0 : return err;
643 : }
644 :
645 0 : layers_.erase(layers_.begin() + index);
646 :
647 0 : return OGRERR_NONE;
648 : }
649 :
650 18 : void OGRHanaDataSource::CreateTable(
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 36 : CPLString sql;
657 50 : if (geomType == OGRwkbGeometryType::wkbNone ||
658 32 : !(!geomColumnName.empty() && geomSrid >= 0))
659 : {
660 6 : sql = "CREATE COLUMN TABLE " +
661 18 : GetFullTableNameQuoted(schemaName_, tableName) + " (" +
662 24 : QuotedIdentifier(fidName) + " " + fidType +
663 12 : " GENERATED BY DEFAULT AS IDENTITY, PRIMARY KEY ( " +
664 18 : QuotedIdentifier(fidName) + "));";
665 : }
666 : else
667 : {
668 12 : sql = "CREATE COLUMN TABLE " +
669 36 : GetFullTableNameQuoted(schemaName_, tableName) + " (" +
670 48 : QuotedIdentifier(fidName) + " " + fidType +
671 24 : " GENERATED BY DEFAULT AS IDENTITY, " +
672 48 : QuotedIdentifier(geomColumnName) + " ST_GEOMETRY (" +
673 48 : std::to_string(geomSrid) + ")" +
674 24 : (geomColumnNullable ? "" : " NOT NULL") +
675 36 : " SPATIAL INDEX PREFERENCE " + geomColumnIndexType +
676 36 : ", PRIMARY KEY ( " + QuotedIdentifier(fidName) + "));";
677 : }
678 :
679 18 : ExecuteSQL(sql);
680 18 : }
681 :
682 56 : void OGRHanaDataSource::DetermineVersions()
683 : {
684 56 : odbc::DatabaseMetaDataRef dbmd = conn_->getDatabaseMetaData();
685 56 : CPLString dbVersion(dbmd->getDBMSVersion());
686 56 : hanaVersion_ = HanaVersion::fromString(dbVersion);
687 :
688 56 : if (hanaVersion_.major() < 4)
689 : {
690 0 : cloudVersion_ = HanaVersion(0, 0, 0);
691 0 : return;
692 : }
693 :
694 112 : odbc::StatementRef stmt = conn_->createStatement();
695 56 : const char *sql = "SELECT CLOUD_VERSION FROM SYS.M_DATABASE;";
696 :
697 112 : odbc::ResultSetRef rsVersion = stmt->executeQuery(sql);
698 56 : if (rsVersion->next())
699 : cloudVersion_ =
700 56 : HanaVersion::fromString(rsVersion->getString(1)->c_str());
701 :
702 56 : rsVersion->close();
703 : }
704 :
705 : /************************************************************************/
706 : /* FindSchemaAndTableNames() */
707 : /************************************************************************/
708 :
709 : std::pair<CPLString, CPLString>
710 15 : OGRHanaDataSource::FindSchemaAndTableNames(const char *query)
711 : {
712 30 : odbc::PreparedStatementRef stmt = PrepareStatement(query);
713 15 : if (stmt.get() == nullptr)
714 0 : return {"", ""};
715 :
716 30 : odbc::ResultSetMetaDataRef rsmd = stmt->getMetaData();
717 :
718 : // Note, getTableName returns correct table name also in the case
719 : // when the original sql query uses a view
720 30 : CPLString tableName = rsmd->getTableName(1);
721 15 : if (tableName == "M_DATABASE_")
722 0 : tableName = "M_DATABASE";
723 30 : CPLString schemaName = rsmd->getSchemaName(1);
724 15 : if (schemaName.empty() && !tableName.empty())
725 14 : schemaName = FindSchemaName(tableName.c_str());
726 15 : return {schemaName, tableName};
727 : }
728 :
729 : /************************************************************************/
730 : /* FindLayerByName() */
731 : /************************************************************************/
732 :
733 67 : int OGRHanaDataSource::FindLayerByName(const char *name)
734 : {
735 604 : for (size_t i = 0; i < layers_.size(); ++i)
736 : {
737 574 : if (EQUAL(name, layers_[i]->GetName()))
738 37 : return static_cast<int>(i);
739 : }
740 30 : return -1;
741 : }
742 :
743 : /************************************************************************/
744 : /* FindSchemaName() */
745 : /************************************************************************/
746 :
747 14 : CPLString OGRHanaDataSource::FindSchemaName(const char *objectName)
748 : {
749 28 : auto getSchemaName = [&](const char *sql)
750 : {
751 56 : odbc::PreparedStatementRef stmt = PrepareStatement(sql);
752 28 : stmt->setString(1, odbc::String(objectName));
753 56 : odbc::ResultSetRef rsEntries = stmt->executeQuery();
754 28 : CPLString ret;
755 42 : while (rsEntries->next())
756 : {
757 : // return empty string if there is more than one schema.
758 28 : if (!ret.empty())
759 : {
760 14 : ret.clear();
761 14 : break;
762 : }
763 14 : ret = *rsEntries->getString(1);
764 : }
765 28 : rsEntries->close();
766 :
767 56 : return ret;
768 14 : };
769 :
770 : CPLString ret = getSchemaName(
771 14 : "SELECT SCHEMA_NAME FROM SYS.TABLES WHERE TABLE_NAME = ?");
772 14 : if (ret.empty())
773 28 : ret = getSchemaName(
774 14 : "SELECT SCHEMA_NAME FROM SYS.VIEWS WHERE VIEW_NAME = ?");
775 :
776 28 : return ret;
777 : }
778 :
779 : /************************************************************************/
780 : /* CreateStatement() */
781 : /************************************************************************/
782 :
783 220 : odbc::StatementRef OGRHanaDataSource::CreateStatement()
784 : {
785 220 : return conn_->createStatement();
786 : }
787 :
788 : /************************************************************************/
789 : /* PrepareStatement() */
790 : /************************************************************************/
791 :
792 337 : odbc::PreparedStatementRef OGRHanaDataSource::PrepareStatement(const char *sql)
793 : {
794 337 : CPLAssert(sql != nullptr);
795 :
796 : try
797 : {
798 337 : CPLDebug("HANA", "Prepare statement %s.", sql);
799 :
800 674 : std::u16string sqlUtf16 = odbc::StringConverter::utf8ToUtf16(sql);
801 337 : return conn_->prepareStatement(sqlUtf16.c_str());
802 : }
803 2 : catch (const odbc::Exception &ex)
804 : {
805 1 : CPLError(CE_Failure, CPLE_AppDefined, "Failed to prepare statement: %s",
806 1 : ex.what());
807 : }
808 1 : return nullptr;
809 : }
810 :
811 : /************************************************************************/
812 : /* Commit() */
813 : /************************************************************************/
814 :
815 134 : void OGRHanaDataSource::Commit()
816 : {
817 134 : conn_->commit();
818 134 : }
819 :
820 : /************************************************************************/
821 : /* ExecuteSQL() */
822 : /************************************************************************/
823 :
824 96 : void OGRHanaDataSource::ExecuteSQL(const CPLString &sql)
825 : {
826 : std::u16string sqlUtf16 =
827 192 : odbc::StringConverter::utf8ToUtf16(sql.c_str(), sql.length());
828 192 : odbc::StatementRef stmt = conn_->createStatement();
829 96 : stmt->execute(sqlUtf16.c_str());
830 92 : if (!IsTransactionStarted())
831 91 : conn_->commit();
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 30 : OGRSpatialReference *OGRHanaDataSource::GetSrsById(int srid)
844 : {
845 30 : if (srid < 0)
846 0 : return nullptr;
847 :
848 30 : auto it = srsCache_.find(srid);
849 30 : if (it != srsCache_.end())
850 : {
851 2 : it->second->Reference();
852 2 : return it->second;
853 : }
854 :
855 28 : OGRSpatialReference *srs = nullptr;
856 :
857 28 : CPLString wkt = GetSrsWktById(*conn_, srid);
858 28 : if (!wkt.empty())
859 : {
860 28 : srs = new OGRSpatialReference();
861 28 : OGRErr err = srs->importFromWkt(wkt.c_str());
862 28 : if (OGRERR_NONE != err)
863 : {
864 0 : delete srs;
865 0 : srs = nullptr;
866 : }
867 : }
868 :
869 28 : srsCache_.insert({srid, srs});
870 :
871 28 : if (srs)
872 28 : srs->Reference();
873 28 : return srs;
874 : }
875 :
876 : /************************************************************************/
877 : /* GetSrsId() */
878 : /************************************************************************/
879 :
880 14 : int OGRHanaDataSource::GetSrsId(const OGRSpatialReference *srs)
881 : {
882 14 : if (srs == nullptr)
883 1 : return UNDETERMINED_SRID;
884 :
885 : /* -------------------------------------------------------------------- */
886 : /* Try to find srs id using authority name and code (EPSG:3857). */
887 : /* -------------------------------------------------------------------- */
888 26 : OGRSpatialReference srsLocal(*srs);
889 :
890 13 : const char *authorityName = srsLocal.GetAuthorityName(nullptr);
891 13 : if (authorityName == nullptr || strlen(authorityName) == 0)
892 : {
893 0 : srsLocal.AutoIdentifyEPSG();
894 0 : authorityName = srsLocal.GetAuthorityName(nullptr);
895 0 : if (authorityName != nullptr && EQUAL(authorityName, "EPSG"))
896 : {
897 0 : const char *authorityCode = srsLocal.GetAuthorityCode(nullptr);
898 0 : if (authorityCode != nullptr && strlen(authorityCode) > 0)
899 : {
900 0 : srsLocal.importFromEPSG(atoi(authorityCode));
901 0 : authorityName = srsLocal.GetAuthorityName(nullptr);
902 : }
903 : }
904 : }
905 :
906 13 : int authorityCode = 0;
907 13 : if (authorityName != nullptr)
908 : {
909 13 : authorityCode = atoi(srsLocal.GetAuthorityCode(nullptr));
910 13 : if (authorityCode > 0)
911 : {
912 13 : int ret = GetSridWithFilter(
913 : *conn_,
914 26 : CPLString().Printf("SRS_ID = %d AND ORGANIZATION = '%s'",
915 13 : authorityCode, authorityName));
916 13 : if (ret != UNDETERMINED_SRID)
917 13 : return ret;
918 : }
919 : }
920 :
921 : /* -------------------------------------------------------------------- */
922 : /* Try to find srs id using wkt content. */
923 : /* -------------------------------------------------------------------- */
924 :
925 0 : char *wkt = nullptr;
926 0 : OGRErr err = srsLocal.exportToWkt(&wkt);
927 0 : CPLString strWkt(wkt);
928 0 : CPLFree(wkt);
929 :
930 0 : if (OGRERR_NONE != err)
931 0 : return UNDETERMINED_SRID;
932 :
933 0 : int srid = GetSridWithFilter(
934 0 : *conn_, CPLString().Printf("DEFINITION = '%s'", strWkt.c_str()));
935 0 : if (srid != UNDETERMINED_SRID)
936 0 : return srid;
937 :
938 : /* -------------------------------------------------------------------- */
939 : /* Try to add a new spatial reference system to the database */
940 : /* -------------------------------------------------------------------- */
941 :
942 0 : char *proj4 = nullptr;
943 0 : err = srsLocal.exportToProj4(&proj4);
944 0 : CPLString strProj4(proj4);
945 0 : CPLFree(proj4);
946 :
947 0 : if (OGRERR_NONE != err)
948 0 : return srid;
949 :
950 0 : if (authorityName != nullptr && authorityCode > 0)
951 : {
952 0 : srid = authorityCode;
953 : }
954 : else
955 : {
956 0 : odbc::StatementRef stmt = conn_->createStatement();
957 0 : const char *sql =
958 : "SELECT MAX(SRS_ID) FROM SYS.ST_SPATIAL_REFERENCE_SYSTEMS WHERE "
959 : "SRS_ID >= 10000000 AND SRS_ID < 20000000";
960 0 : odbc::ResultSetRef rsSrid = stmt->executeQuery(sql);
961 0 : while (rsSrid->next())
962 : {
963 0 : odbc::Int val = rsSrid->getInt(1);
964 0 : srid = val.isNull() ? 10000000 : *val + 1;
965 : }
966 0 : rsSrid->close();
967 : }
968 :
969 : try
970 : {
971 0 : CreateSpatialReferenceSystem(srsLocal, srid, authorityName,
972 : authorityCode, strWkt, strProj4);
973 0 : return srid;
974 : }
975 0 : catch (const odbc::Exception &ex)
976 : {
977 0 : CPLError(CE_Failure, CPLE_AppDefined,
978 0 : "Unable to create an SRS in the database: %s.\n", ex.what());
979 : }
980 :
981 0 : return UNDETERMINED_SRID;
982 : }
983 :
984 : /************************************************************************/
985 : /* IsSrsRoundEarth() */
986 : /************************************************************************/
987 :
988 12 : bool OGRHanaDataSource::IsSrsRoundEarth(int srid)
989 : {
990 12 : const char *sql =
991 : "SELECT ROUND_EARTH FROM SYS.ST_SPATIAL_REFERENCE_SYSTEMS "
992 : "WHERE SRS_ID = ?";
993 24 : odbc::PreparedStatementRef stmt = PrepareStatement(sql);
994 12 : stmt->setInt(1, odbc::Int(srid));
995 12 : odbc::ResultSetRef rs = stmt->executeQuery();
996 12 : bool ret = false;
997 12 : if (rs->next())
998 12 : ret = (*rs->getString(1) == "TRUE");
999 12 : rs->close();
1000 24 : return ret;
1001 : }
1002 :
1003 : /************************************************************************/
1004 : /* HasSrsPlanarEquivalent() */
1005 : /************************************************************************/
1006 :
1007 2 : bool OGRHanaDataSource::HasSrsPlanarEquivalent(int srid)
1008 : {
1009 2 : const char *sql = "SELECT COUNT(*) FROM SYS.ST_SPATIAL_REFERENCE_SYSTEMS "
1010 : "WHERE SRS_ID = ?";
1011 4 : odbc::PreparedStatementRef stmt = PrepareStatement(sql);
1012 2 : stmt->setInt(1, ToPlanarSRID(srid));
1013 2 : odbc::ResultSetRef rs = stmt->executeQuery();
1014 2 : std::int64_t count = 0;
1015 2 : if (rs->next())
1016 2 : count = *rs->getLong(1);
1017 2 : rs->close();
1018 4 : return count > 0;
1019 : }
1020 :
1021 : /************************************************************************/
1022 : /* GetQueryColumns() */
1023 : /************************************************************************/
1024 :
1025 55 : OGRErr OGRHanaDataSource::GetQueryColumns(
1026 : const CPLString &schemaName, const CPLString &query,
1027 : std::vector<ColumnDescription> &columnDescriptions)
1028 : {
1029 55 : columnDescriptions.clear();
1030 :
1031 110 : odbc::PreparedStatementRef stmtQuery = PrepareStatement(query);
1032 :
1033 55 : if (stmtQuery.isNull())
1034 0 : return OGRERR_FAILURE;
1035 :
1036 110 : odbc::ResultSetMetaDataRef rsmd = stmtQuery->getMetaData();
1037 55 : std::size_t numColumns = rsmd->getColumnCount();
1038 55 : if (numColumns == 0)
1039 0 : return OGRERR_NONE;
1040 :
1041 55 : columnDescriptions.reserve(numColumns);
1042 :
1043 110 : odbc::DatabaseMetaDataRef dmd = conn_->getDatabaseMetaData();
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 110 : "DATA_TYPE_NAME LIKE '% ARRAY'");
1049 :
1050 254 : for (unsigned short clmIndex = 1; clmIndex <= numColumns; ++clmIndex)
1051 : {
1052 199 : CPLString typeName = rsmd->getColumnTypeName(clmIndex);
1053 :
1054 199 : if (typeName.empty())
1055 0 : continue;
1056 :
1057 199 : bool isArray = false;
1058 199 : CPLString tableName = rsmd->getTableName(clmIndex);
1059 199 : CPLString columnName = rsmd->getColumnName(clmIndex);
1060 199 : CPLString defaultValue;
1061 199 : short dataType = rsmd->getColumnType(clmIndex);
1062 :
1063 199 : if (!schemaName.empty() && !tableName.empty())
1064 : {
1065 : // Retrieve information about default value in column
1066 : odbc::ResultSetRef rsColumns =
1067 : dmd->getColumns(nullptr, schemaName.c_str(), tableName.c_str(),
1068 141 : columnName.c_str());
1069 141 : if (rsColumns->next())
1070 : {
1071 : odbc::String defaultValueStr =
1072 276 : rsColumns->getString(13 /*COLUMN_DEF*/);
1073 138 : if (!defaultValueStr.isNull())
1074 : defaultValue =
1075 12 : FormatDefaultValue(defaultValueStr->c_str(), dataType);
1076 : }
1077 141 : rsColumns->close();
1078 :
1079 : // Retrieve information about array type
1080 141 : stmtArrayTypeInfo->setString(1, schemaName);
1081 141 : stmtArrayTypeInfo->setString(2, tableName);
1082 141 : stmtArrayTypeInfo->setString(3, columnName);
1083 141 : odbc::ResultSetRef rsArrayTypes = stmtArrayTypeInfo->executeQuery();
1084 141 : if (rsArrayTypes->next())
1085 : {
1086 11 : typeName = *rsArrayTypes->getString(1);
1087 11 : dataType = GetArrayDataType(typeName);
1088 :
1089 11 : if (dataType == QGRHanaDataTypes::Unknown)
1090 : {
1091 0 : CPLError(
1092 : CE_Failure, CPLE_AppDefined,
1093 : "GetQueryColumns(): Unsupported type of array (%s)",
1094 : typeName.c_str());
1095 0 : return OGRERR_FAILURE;
1096 : }
1097 :
1098 11 : isArray = true;
1099 : }
1100 141 : rsArrayTypes->close();
1101 : }
1102 :
1103 199 : if (!isArray && !IsKnownDataType(dataType))
1104 : {
1105 0 : odbc::ResultSetRef rsTypeInfo = dmd->getTypeInfo(dataType);
1106 0 : if (rsTypeInfo->next())
1107 : {
1108 0 : odbc::String name = rsTypeInfo->getString(1);
1109 0 : if (name.isNull())
1110 0 : continue;
1111 0 : if (name->compare("SHORTTEXT") == 0 ||
1112 0 : name->compare("ALPHANUM") == 0)
1113 : {
1114 0 : dataType = QGRHanaDataTypes::WVarChar;
1115 : }
1116 : }
1117 0 : rsTypeInfo->close();
1118 : }
1119 :
1120 199 : if (dataType == QGRHanaDataTypes::Geometry)
1121 : {
1122 33 : GeometryColumnDescription geometryColumnDesc;
1123 33 : if (schemaName.empty() || tableName.empty())
1124 18 : geometryColumnDesc = GetGeometryColumnDescription(
1125 18 : *conn_, query, columnName, detectGeometryType_);
1126 : else
1127 48 : geometryColumnDesc = GetGeometryColumnDescription(
1128 : *conn_, schemaName, tableName, columnName,
1129 48 : detectGeometryType_);
1130 33 : geometryColumnDesc.isNullable = rsmd->isNullable(clmIndex);
1131 :
1132 66 : columnDescriptions.push_back({true, AttributeColumnDescription(),
1133 33 : std::move(geometryColumnDesc)});
1134 : }
1135 : else
1136 : {
1137 166 : AttributeColumnDescription attributeColumnDesc;
1138 166 : attributeColumnDesc.name = std::move(columnName);
1139 166 : attributeColumnDesc.type = dataType;
1140 166 : attributeColumnDesc.typeName = std::move(typeName);
1141 166 : attributeColumnDesc.isArray = isArray;
1142 166 : attributeColumnDesc.isNullable = rsmd->isNullable(clmIndex);
1143 166 : attributeColumnDesc.isAutoIncrement =
1144 166 : rsmd->isAutoIncrement(clmIndex);
1145 166 : attributeColumnDesc.length =
1146 166 : static_cast<int>(rsmd->getColumnLength(clmIndex));
1147 166 : attributeColumnDesc.precision = rsmd->getPrecision(clmIndex);
1148 166 : attributeColumnDesc.scale = rsmd->getScale(clmIndex);
1149 166 : attributeColumnDesc.defaultValue = std::move(defaultValue);
1150 :
1151 166 : columnDescriptions.push_back({false, std::move(attributeColumnDesc),
1152 : GeometryColumnDescription()});
1153 : }
1154 : }
1155 :
1156 55 : return OGRERR_NONE;
1157 : }
1158 :
1159 : /************************************************************************/
1160 : /* GetTablePrimaryKeys() */
1161 : /************************************************************************/
1162 :
1163 : std::vector<CPLString>
1164 55 : OGRHanaDataSource::GetTablePrimaryKeys(const char *schemaName,
1165 : const char *tableName)
1166 : {
1167 55 : std::vector<CPLString> ret;
1168 :
1169 110 : odbc::DatabaseMetaDataRef dmd = conn_->getDatabaseMetaData();
1170 : odbc::ResultSetRef rsPrimaryKeys =
1171 110 : dmd->getPrimaryKeys(nullptr, schemaName, tableName);
1172 2877 : while (rsPrimaryKeys->next())
1173 : {
1174 2822 : ret.push_back(*rsPrimaryKeys->getString(4));
1175 : }
1176 55 : rsPrimaryKeys->close();
1177 :
1178 110 : return ret;
1179 : }
1180 :
1181 : /************************************************************************/
1182 : /* InitializeLayers() */
1183 : /************************************************************************/
1184 :
1185 56 : void OGRHanaDataSource::InitializeLayers(const char *schemaName,
1186 : const char *tableNames)
1187 : {
1188 112 : std::vector<CPLString> tablesToFind = SplitStrings(tableNames, ",");
1189 56 : const bool hasTablesToFind = !tablesToFind.empty();
1190 :
1191 112 : auto addLayersFromQuery = [&](const char *query, bool updatable)
1192 : {
1193 224 : odbc::PreparedStatementRef stmt = PrepareStatement(query);
1194 112 : stmt->setString(1, odbc::String(schemaName));
1195 224 : odbc::ResultSetRef rsTables = stmt->executeQuery();
1196 579 : while (rsTables->next())
1197 : {
1198 467 : odbc::String tableName = rsTables->getString(1);
1199 467 : if (tableName.isNull())
1200 0 : continue;
1201 : auto pos =
1202 467 : std::find(tablesToFind.begin(), tablesToFind.end(), *tableName);
1203 467 : if (pos != tablesToFind.end())
1204 0 : tablesToFind.erase(pos);
1205 :
1206 : auto layer = std::make_unique<OGRHanaTableLayer>(
1207 467 : this, schemaName_.c_str(), tableName->c_str(), updatable);
1208 467 : layers_.push_back(std::move(layer));
1209 : }
1210 112 : rsTables->close();
1211 112 : };
1212 :
1213 : // Look for layers in Tables
1214 112 : std::ostringstream osTables;
1215 56 : osTables << "SELECT TABLE_NAME FROM SYS.TABLES WHERE SCHEMA_NAME = ?";
1216 56 : if (!tablesToFind.empty())
1217 : osTables << " AND TABLE_NAME IN ("
1218 0 : << JoinStrings(tablesToFind, ",", Literal) << ")";
1219 :
1220 56 : addLayersFromQuery(osTables.str().c_str(), updateMode_);
1221 :
1222 56 : if (!(hasTablesToFind && tablesToFind.empty()))
1223 : {
1224 : // Look for layers in Views
1225 56 : std::ostringstream osViews;
1226 56 : osViews << "SELECT VIEW_NAME FROM SYS.VIEWS WHERE SCHEMA_NAME = ?";
1227 : // cppcheck-suppress knownConditionTrueFalse
1228 56 : if (!tablesToFind.empty())
1229 : osViews << " AND VIEW_NAME IN ("
1230 0 : << JoinStrings(tablesToFind, ",", Literal) << ")";
1231 :
1232 56 : addLayersFromQuery(osViews.str().c_str(), false);
1233 : }
1234 :
1235 : // Report about tables that could not be found
1236 56 : for (const auto &tableName : tablesToFind)
1237 : {
1238 0 : const char *layerName = tableName.c_str();
1239 0 : if (GetLayerByName(layerName) == nullptr)
1240 0 : 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 115 : std::pair<OGRErr, CPLString> OGRHanaDataSource::LaunderName(const char *name)
1252 : {
1253 115 : CPLAssert(name != nullptr);
1254 :
1255 115 : if (!CPLIsUTF8(name, -1))
1256 : {
1257 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s is not a valid UTF-8 string.",
1258 : name);
1259 0 : return {OGRERR_FAILURE, ""};
1260 : }
1261 :
1262 1050 : auto getUTF8SequenceLength = [](char c)
1263 : {
1264 1050 : if ((c & 0x80) == 0x00)
1265 1040 : return 1;
1266 10 : if ((c & 0xE0) == 0xC0)
1267 6 : return 2;
1268 4 : if ((c & 0xF0) == 0xE0)
1269 0 : return 3;
1270 4 : if ((c & 0xF8) == 0xF0)
1271 4 : return 4;
1272 :
1273 0 : throw std::runtime_error("Invalid UTF-8 sequence");
1274 : };
1275 :
1276 230 : CPLString newName(name);
1277 115 : bool hasNonASCII = false;
1278 115 : size_t i = 0;
1279 :
1280 1165 : while (name[i] != '\0')
1281 : {
1282 1050 : char c = name[i];
1283 1050 : int len = getUTF8SequenceLength(c);
1284 1050 : if (len == 1)
1285 : {
1286 1040 : if (c == '-' || c == '#')
1287 6 : newName[i] = '_';
1288 : else
1289 1034 : newName[i] = static_cast<char>(
1290 1034 : CPLToupper(static_cast<unsigned char>(c)));
1291 : }
1292 : else
1293 : {
1294 10 : hasNonASCII = true;
1295 : }
1296 :
1297 1050 : i += len;
1298 : }
1299 :
1300 115 : if (!hasNonASCII)
1301 222 : return {OGRERR_NONE, newName};
1302 :
1303 4 : const char *sql = "SELECT UPPER(?) FROM DUMMY";
1304 8 : odbc::PreparedStatementRef stmt = PrepareStatement(sql);
1305 4 : stmt->setString(1, odbc::String(newName.c_str()));
1306 8 : odbc::ResultSetRef rsName = stmt->executeQuery();
1307 4 : OGRErr err = OGRERR_NONE;
1308 4 : if (rsName->next())
1309 : {
1310 4 : newName.swap(*rsName->getString(1));
1311 : }
1312 : else
1313 : {
1314 0 : err = OGRERR_FAILURE;
1315 0 : newName.clear();
1316 : }
1317 4 : rsName->close();
1318 4 : return {err, newName};
1319 : }
1320 :
1321 : /************************************************************************/
1322 : /* CreateSpatialReference() */
1323 : /************************************************************************/
1324 :
1325 0 : void OGRHanaDataSource::CreateSpatialReferenceSystem(
1326 : const OGRSpatialReference &srs, int srid, const char *authorityName,
1327 : int authorityCode, const CPLString &wkt, const CPLString &proj4)
1328 : {
1329 0 : CPLString refName((srs.IsProjected()) ? srs.GetAttrValue("PROJCS")
1330 0 : : srs.GetAttrValue("GEOGCS"));
1331 0 : if (refName.empty() || EQUAL(refName.c_str(), "UNKNOWN"))
1332 0 : refName = "OGR_PROJECTION_" + std::to_string(srid);
1333 :
1334 0 : OGRErr err = OGRERR_NONE;
1335 0 : CPLString ellipsoidParams;
1336 0 : const double semiMajor = srs.GetSemiMajor(&err);
1337 0 : if (OGRERR_NONE == err)
1338 0 : ellipsoidParams += " SEMI MAJOR AXIS " + std::to_string(semiMajor);
1339 0 : const double semiMinor = srs.GetSemiMinor(&err);
1340 0 : const double invFlattening = srs.GetInvFlattening(&err);
1341 0 : if (OGRERR_NONE == err)
1342 : ellipsoidParams +=
1343 0 : " INVERSE FLATTENING " + std::to_string(invFlattening);
1344 : else
1345 0 : ellipsoidParams += " SEMI MINOR AXIS " + std::to_string(semiMinor);
1346 :
1347 0 : const char *linearUnits = nullptr;
1348 0 : srs.GetLinearUnits(&linearUnits);
1349 0 : const char *angularUnits = nullptr;
1350 0 : srs.GetAngularUnits(&angularUnits);
1351 :
1352 0 : CPLString xRange, yRange;
1353 : double dfWestLongitudeDeg, dfSouthLatitudeDeg, dfEastLongitudeDeg,
1354 : dfNorthLatitudeDeg;
1355 0 : if (srs.GetAreaOfUse(&dfWestLongitudeDeg, &dfSouthLatitudeDeg,
1356 : &dfEastLongitudeDeg, &dfNorthLatitudeDeg, nullptr))
1357 : {
1358 0 : xRange = CPLString().Printf("%s BETWEEN %f AND %f",
1359 0 : srs.IsGeographic() ? "LONGITUDE" : "X",
1360 0 : dfWestLongitudeDeg, dfEastLongitudeDeg);
1361 0 : yRange = CPLString().Printf("%s BETWEEN %f AND %f",
1362 0 : srs.IsGeographic() ? "LATITUDE" : "Y",
1363 0 : dfSouthLatitudeDeg, dfNorthLatitudeDeg);
1364 : }
1365 : else
1366 : {
1367 0 : xRange = CPLString().Printf("%s UNBOUNDED",
1368 0 : srs.IsGeographic() ? "LONGITUDE" : "X");
1369 0 : yRange = CPLString().Printf("%s UNBOUNDED ",
1370 0 : srs.IsGeographic() ? "LATITUDE" : "Y");
1371 : }
1372 :
1373 0 : CPLString organization;
1374 0 : if (authorityName != nullptr && authorityCode > 0)
1375 : {
1376 0 : organization = CPLString().Printf(
1377 : "ORGANIZATION %s IDENTIFIED BY %d",
1378 0 : QuotedIdentifier(authorityName).c_str(), authorityCode);
1379 : }
1380 :
1381 0 : 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 0 : QuotedIdentifier(refName).c_str(), srid,
1394 0 : srs.IsGeographic() ? "ROUND EARTH" : "PLANAR",
1395 0 : QuotedIdentifier(
1396 0 : (linearUnits == nullptr || EQUAL(linearUnits, "unknown"))
1397 : ? "metre"
1398 : : linearUnits)
1399 0 : .tolower()
1400 : .c_str(),
1401 0 : QuotedIdentifier(
1402 0 : (angularUnits == nullptr || EQUAL(angularUnits, "unknown"))
1403 : ? "degree"
1404 : : angularUnits)
1405 0 : .tolower()
1406 : .c_str(),
1407 0 : (ellipsoidParams.empty() ? ""
1408 0 : : ("ELLIPSOID" + ellipsoidParams).c_str()),
1409 : xRange.c_str(), yRange.c_str(), organization.c_str(),
1410 0 : Literal(wkt).c_str(), Literal(proj4).c_str());
1411 :
1412 0 : ExecuteSQL(sql);
1413 0 : }
1414 :
1415 : /************************************************************************/
1416 : /* CreateParseArrayFunctions() */
1417 : /************************************************************************/
1418 :
1419 1 : void OGRHanaDataSource::CreateParseArrayFunctions(const char *schemaName)
1420 : {
1421 8 : auto replaceAll = [](const CPLString &str, const CPLString &before,
1422 : const CPLString &after) -> CPLString
1423 : {
1424 16 : CPLString res = str;
1425 16 : return res.replaceAll(before, after);
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 2 : "END;\n";
1459 : // clang-format on
1460 :
1461 : CPLString sql = replaceAll(parseStringArrayFunc, "{SCHEMA}",
1462 3 : QuotedIdentifier(schemaName));
1463 1 : ExecuteSQL(sql);
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 2 : "END;\n";
1491 : // clang-format on
1492 :
1493 2 : sql = replaceAll(parseTypeArrayFunc, "{SCHEMA}",
1494 3 : QuotedIdentifier(schemaName));
1495 :
1496 8 : for (const CPLString &type : GetSupportedArrayTypes())
1497 : {
1498 7 : if (type == "STRING")
1499 1 : continue;
1500 6 : ExecuteSQL(replaceAll(sql, "{TYPE}", type));
1501 : }
1502 1 : }
1503 :
1504 : /************************************************************************/
1505 : /* ParseArrayFunctionsExist() */
1506 : /************************************************************************/
1507 :
1508 1 : bool OGRHanaDataSource::ParseArrayFunctionsExist(const char *schemaName)
1509 : {
1510 1 : const char *sql =
1511 : "SELECT COUNT(*) FROM FUNCTIONS WHERE SCHEMA_NAME = ? AND "
1512 : "FUNCTION_NAME LIKE 'OGR_PARSE_%_ARRAY'";
1513 2 : odbc::PreparedStatementRef stmt = PrepareStatement(sql);
1514 1 : stmt->setString(1, odbc::String(schemaName));
1515 1 : odbc::ResultSetRef rsFunctions = stmt->executeQuery();
1516 1 : auto numFunctions = rsFunctions->next() ? *rsFunctions->getLong(1) : 0;
1517 1 : rsFunctions->close();
1518 1 : return (static_cast<std::size_t>(numFunctions) ==
1519 2 : GetSupportedArrayTypes().size());
1520 : }
1521 :
1522 : /************************************************************************/
1523 : /* GetLayer() */
1524 : /************************************************************************/
1525 :
1526 43 : OGRLayer *OGRHanaDataSource::GetLayer(int index)
1527 : {
1528 43 : if (index < 0 || static_cast<std::size_t>(index) >= layers_.size())
1529 6 : return nullptr;
1530 37 : return layers_[static_cast<std::size_t>(index)].get();
1531 : }
1532 :
1533 : /************************************************************************/
1534 : /* GetLayerByName() */
1535 : /************************************************************************/
1536 :
1537 39 : OGRLayer *OGRHanaDataSource::GetLayerByName(const char *name)
1538 : {
1539 39 : return GetLayer(FindLayerByName(name));
1540 : }
1541 :
1542 : /************************************************************************/
1543 : /* ICreateLayer() */
1544 : /************************************************************************/
1545 :
1546 : OGRLayer *
1547 18 : OGRHanaDataSource::ICreateLayer(const char *layerNameIn,
1548 : const OGRGeomFieldDefn *poGeomFieldDefn,
1549 : CSLConstList options)
1550 : {
1551 18 : if (layerNameIn == nullptr)
1552 0 : return nullptr;
1553 :
1554 : const auto geomType =
1555 18 : poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
1556 : const auto srs =
1557 18 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
1558 :
1559 : // Check if we are allowed to create new objects in the database
1560 36 : odbc::DatabaseMetaDataRef dmd = conn_->getDatabaseMetaData();
1561 18 : if (dmd->isReadOnly())
1562 : {
1563 0 : CPLError(CE_Failure, CPLE_AppDefined,
1564 : "Unable to create Layer %s.\n"
1565 : "Database %s is read only.",
1566 0 : layerNameIn, dmd->getDatabaseName().c_str());
1567 0 : return nullptr;
1568 : }
1569 :
1570 18 : bool launderNames = CPLFetchBool(
1571 : options, OGRHanaLayerCreationOptionsConstants::LAUNDER, true);
1572 36 : CPLString layerName(layerNameIn);
1573 18 : if (launderNames)
1574 : {
1575 17 : auto nameRes = LaunderName(layerNameIn);
1576 17 : if (nameRes.first != OGRERR_NONE)
1577 0 : return nullptr;
1578 17 : layerName.swap(nameRes.second);
1579 : }
1580 :
1581 18 : CPLDebug("HANA", "Creating layer %s.", layerName.c_str());
1582 :
1583 18 : int layerIndex = FindLayerByName(layerName.c_str());
1584 18 : if (layerIndex >= 0)
1585 : {
1586 0 : bool overwriteLayer = CPLFetchBool(
1587 : options, OGRHanaLayerCreationOptionsConstants::OVERWRITE, false);
1588 0 : if (!overwriteLayer)
1589 : {
1590 0 : 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 0 : return nullptr;
1596 : }
1597 :
1598 0 : DeleteLayer(layerIndex);
1599 : }
1600 :
1601 : int batchSize =
1602 18 : CPLFetchInt(options, OGRHanaLayerCreationOptionsConstants::BATCH_SIZE,
1603 : DEFAULT_BATCH_SIZE);
1604 18 : if (batchSize <= 0)
1605 : {
1606 0 : 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 0 : return nullptr;
1612 : }
1613 :
1614 18 : int defaultStringSize = CPLFetchInt(
1615 : options, OGRHanaLayerCreationOptionsConstants::DEFAULT_STRING_SIZE,
1616 : DEFAULT_STRING_SIZE);
1617 18 : if (defaultStringSize <= 0)
1618 : {
1619 0 : 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 0 : return nullptr;
1625 : }
1626 :
1627 : CPLString geomColumnName(CSLFetchNameValueDef(
1628 : options, OGRHanaLayerCreationOptionsConstants::GEOMETRY_NAME,
1629 36 : "OGR_GEOMETRY"));
1630 18 : if (launderNames)
1631 : {
1632 17 : auto nameRes = LaunderName(geomColumnName.c_str());
1633 17 : if (nameRes.first != OGRERR_NONE)
1634 0 : return nullptr;
1635 17 : geomColumnName.swap(nameRes.second);
1636 : }
1637 :
1638 18 : const bool geomColumnNullable = CPLFetchBool(
1639 : options, OGRHanaLayerCreationOptionsConstants::GEOMETRY_NULLABLE, true);
1640 : CPLString geomColumnIndexType(CSLFetchNameValueDef(
1641 : options, OGRHanaLayerCreationOptionsConstants::GEOMETRY_INDEX,
1642 36 : "DEFAULT"));
1643 :
1644 18 : const char *paramFidName = CSLFetchNameValueDef(
1645 : options, OGRHanaLayerCreationOptionsConstants::FID, "OGR_FID");
1646 36 : CPLString fidName(paramFidName);
1647 18 : if (launderNames)
1648 : {
1649 17 : auto nameRes = LaunderName(paramFidName);
1650 17 : if (nameRes.first != OGRERR_NONE)
1651 0 : return nullptr;
1652 17 : fidName.swap(nameRes.second);
1653 : }
1654 :
1655 : CPLString fidType =
1656 18 : CPLFetchBool(options, OGRHanaLayerCreationOptionsConstants::FID64,
1657 : false)
1658 : ? "BIGINT"
1659 36 : : "INTEGER";
1660 :
1661 18 : CPLDebug("HANA", "Geometry Column Name %s.", geomColumnName.c_str());
1662 18 : CPLDebug("HANA", "FID Column Name %s, Type %s.", fidName.c_str(),
1663 : fidType.c_str());
1664 :
1665 18 : int srid = CPLFetchInt(options, OGRHanaLayerCreationOptionsConstants::SRID,
1666 : UNDETERMINED_SRID);
1667 18 : if (srid < 0 && srs != nullptr)
1668 12 : srid = GetSrsId(srs);
1669 :
1670 : try
1671 : {
1672 18 : CreateTable(layerName, fidName, fidType, geomColumnName, geomType,
1673 : geomColumnNullable, geomColumnIndexType, srid);
1674 : }
1675 0 : catch (const odbc::Exception &ex)
1676 : {
1677 0 : CPLError(CE_Failure, CPLE_AppDefined,
1678 : "Unable to create layer %s. CreateLayer failed:%s\n",
1679 0 : layerName.c_str(), ex.what());
1680 0 : return nullptr;
1681 : }
1682 :
1683 : // Create new layer object
1684 0 : auto layer = std::make_unique<OGRHanaTableLayer>(this, schemaName_.c_str(),
1685 36 : layerName.c_str(), true);
1686 18 : if (geomType != wkbNone && layer->GetLayerDefn()->GetGeomFieldCount() > 0)
1687 12 : layer->GetLayerDefn()->GetGeomFieldDefn(0)->SetNullable(FALSE);
1688 18 : if (batchSize > 0)
1689 18 : layer->SetBatchSize(static_cast<std::size_t>(batchSize));
1690 18 : if (defaultStringSize > 0)
1691 18 : layer->SetDefaultStringSize(
1692 : static_cast<std::size_t>(defaultStringSize));
1693 18 : layer->SetLaunderFlag(launderNames);
1694 18 : layer->SetPrecisionFlag(CPLFetchBool(
1695 : options, OGRHanaLayerCreationOptionsConstants::PRECISION, true));
1696 18 : layer->SetCustomColumnTypes(CSLFetchNameValue(
1697 : options, OGRHanaLayerCreationOptionsConstants::COLUMN_TYPES));
1698 :
1699 18 : layers_.push_back(std::move(layer));
1700 :
1701 18 : return layers_.back().get();
1702 : }
1703 :
1704 : /************************************************************************/
1705 : /* TestCapability() */
1706 : /************************************************************************/
1707 :
1708 27 : int OGRHanaDataSource::TestCapability(const char *capabilities)
1709 : {
1710 27 : if (EQUAL(capabilities, ODsCCreateLayer))
1711 4 : return updateMode_;
1712 23 : else if (EQUAL(capabilities, ODsCDeleteLayer))
1713 4 : return updateMode_;
1714 19 : else if (EQUAL(capabilities, ODsCCreateGeomFieldAfterCreateLayer))
1715 2 : return updateMode_;
1716 17 : else if (EQUAL(capabilities, ODsCMeasuredGeometries))
1717 5 : return TRUE;
1718 12 : else if (EQUAL(capabilities, ODsCRandomLayerWrite))
1719 0 : return updateMode_;
1720 12 : else if (EQUAL(capabilities, ODsCTransactions))
1721 4 : return TRUE;
1722 : else
1723 8 : return FALSE;
1724 : }
1725 :
1726 : /************************************************************************/
1727 : /* ExecuteSQL() */
1728 : /************************************************************************/
1729 :
1730 29 : OGRLayer *OGRHanaDataSource::ExecuteSQL(const char *sqlCommand,
1731 : OGRGeometry *spatialFilter,
1732 : const char *dialect)
1733 : {
1734 29 : sqlCommand = SkipLeadingSpaces(sqlCommand);
1735 :
1736 29 : if (IsGenericSQLDialect(dialect))
1737 0 : return GDALDataset::ExecuteSQL(sqlCommand, spatialFilter, dialect);
1738 :
1739 29 : if (STARTS_WITH_CI(sqlCommand, "DELLAYER:"))
1740 : {
1741 10 : const char *layerName = SkipLeadingSpaces(sqlCommand + 9);
1742 10 : int layerIndex = FindLayerByName(layerName);
1743 10 : if (layerIndex >= 0)
1744 0 : DeleteLayer(layerIndex);
1745 10 : return nullptr;
1746 : }
1747 19 : if (STARTS_WITH_CI(sqlCommand, "SELECT"))
1748 : {
1749 28 : auto stmt = PrepareStatement(sqlCommand);
1750 14 : if (stmt.isNull())
1751 1 : return nullptr;
1752 :
1753 26 : auto layer = std::make_unique<OGRHanaResultLayer>(this, sqlCommand);
1754 13 : if (spatialFilter != nullptr)
1755 1 : layer->SetSpatialFilter(spatialFilter);
1756 13 : return layer.release();
1757 : }
1758 :
1759 : try
1760 : {
1761 8 : ExecuteSQL(sqlCommand);
1762 : }
1763 6 : catch (const odbc::Exception &ex)
1764 : {
1765 3 : CPLError(CE_Failure, CPLE_AppDefined,
1766 : "Failed to execute SQL statement '%s': %s", sqlCommand,
1767 3 : ex.what());
1768 : }
1769 :
1770 5 : return nullptr;
1771 : }
1772 :
1773 : /************************************************************************/
1774 : /* StartTransaction() */
1775 : /************************************************************************/
1776 :
1777 15 : OGRErr OGRHanaDataSource::StartTransaction(CPL_UNUSED int bForce)
1778 : {
1779 15 : if (isTransactionStarted_)
1780 : {
1781 2 : CPLError(CE_Failure, CPLE_AppDefined,
1782 : "Transaction already established");
1783 2 : return OGRERR_FAILURE;
1784 : }
1785 :
1786 13 : isTransactionStarted_ = true;
1787 13 : return OGRERR_NONE;
1788 : }
1789 :
1790 : /************************************************************************/
1791 : /* CommitTransaction() */
1792 : /************************************************************************/
1793 :
1794 10 : OGRErr OGRHanaDataSource::CommitTransaction()
1795 : {
1796 10 : if (!isTransactionStarted_)
1797 : {
1798 2 : CPLError(CE_Failure, CPLE_AppDefined, "Transaction not established");
1799 2 : return OGRERR_FAILURE;
1800 : }
1801 :
1802 8 : isTransactionStarted_ = false;
1803 :
1804 : try
1805 : {
1806 127 : for (size_t i = 0; i < layers_.size(); ++i)
1807 : {
1808 119 : OGRHanaLayer *layer = static_cast<OGRHanaLayer *>(layers_[i].get());
1809 119 : if (layer->IsTableLayer())
1810 : {
1811 119 : OGRHanaTableLayer *tableLayer =
1812 : static_cast<OGRHanaTableLayer *>(layer);
1813 119 : tableLayer->FlushPendingBatches(false);
1814 : }
1815 : }
1816 :
1817 8 : conn_->commit();
1818 : }
1819 0 : catch (const odbc::Exception &ex)
1820 : {
1821 0 : CPLError(CE_Failure, CPLE_AppDefined,
1822 0 : "Failed to commit transaction: %s", ex.what());
1823 0 : return OGRERR_FAILURE;
1824 : }
1825 8 : return OGRERR_NONE;
1826 : }
1827 :
1828 : /************************************************************************/
1829 : /* RollbackTransaction() */
1830 : /************************************************************************/
1831 :
1832 6 : OGRErr OGRHanaDataSource::RollbackTransaction()
1833 : {
1834 6 : if (!isTransactionStarted_)
1835 : {
1836 2 : CPLError(CE_Failure, CPLE_AppDefined, "Transaction not established");
1837 2 : return OGRERR_FAILURE;
1838 : }
1839 :
1840 4 : isTransactionStarted_ = false;
1841 :
1842 : try
1843 : {
1844 4 : conn_->rollback();
1845 : }
1846 0 : catch (const odbc::Exception &ex)
1847 : {
1848 0 : CPLError(CE_Failure, CPLE_AppDefined,
1849 0 : "Failed to roll back transaction: %s", ex.what());
1850 0 : return OGRERR_FAILURE;
1851 : }
1852 4 : return OGRERR_NONE;
1853 : }
|