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, CSLConstList openOptions,
557 : int update)
558 : {
559 65 : CPLAssert(layers_.size() == 0);
560 :
561 65 : if (!STARTS_WITH_CI(newName, GetPrefix()))
562 : {
563 0 : CPLError(CE_Failure, CPLE_AppDefined,
564 : "%s does not conform to HANA driver naming convention,"
565 : " %s*\n",
566 : newName, GetPrefix());
567 0 : return FALSE;
568 : }
569 :
570 65 : updateMode_ = update;
571 65 : detectGeometryType_ = CPLFetchBool(
572 : openOptions, OGRHanaOpenOptionsConstants::DETECT_GEOMETRY_TYPE, true);
573 :
574 65 : std::size_t prefixLength = strlen(GetPrefix());
575 : char **connOptions =
576 65 : CSLTokenizeStringComplex(newName + prefixLength, ";", TRUE, FALSE);
577 :
578 65 : const char *paramSchema = CSLFetchNameValueDef(
579 : connOptions, OGRHanaOpenOptionsConstants::SCHEMA, nullptr);
580 65 : if (paramSchema != nullptr)
581 63 : schemaName_ = paramSchema;
582 :
583 65 : int ret = FALSE;
584 :
585 65 : CPLString connectionStr = BuildConnectionString(connOptions);
586 :
587 65 : if (!connectionStr.empty())
588 : {
589 56 : connEnv_ = odbc::Environment::create();
590 56 : conn_ = connEnv_->createConnection();
591 56 : conn_->setAutoCommit(false);
592 :
593 56 : const char *paramConnTimeout = CSLFetchNameValueDef(
594 : connOptions, OGRHanaOpenOptionsConstants::CONNECTION_TIMEOUT,
595 : nullptr);
596 56 : if (paramConnTimeout != nullptr)
597 0 : conn_->setConnectionTimeout(
598 0 : static_cast<unsigned long>(atoi(paramConnTimeout)));
599 :
600 : try
601 : {
602 56 : conn_->connect(connectionStr.c_str());
603 : }
604 0 : catch (const odbc::Exception &ex)
605 : {
606 0 : CPLError(CE_Failure, CPLE_AppDefined,
607 0 : "HANA connection failed: %s\n", ex.what());
608 : }
609 :
610 56 : if (conn_->connected())
611 : {
612 56 : DetermineVersions();
613 :
614 56 : const char *paramTables = CSLFetchNameValueDef(
615 : connOptions, OGRHanaOpenOptionsConstants::TABLES, "");
616 56 : InitializeLayers(paramSchema, paramTables);
617 56 : ret = TRUE;
618 : }
619 : }
620 :
621 65 : CSLDestroy(connOptions);
622 :
623 65 : return ret;
624 : }
625 :
626 : /************************************************************************/
627 : /* DeleteLayer() */
628 : /************************************************************************/
629 :
630 0 : OGRErr OGRHanaDataSource::DeleteLayer(int index)
631 : {
632 0 : if (index < 0 || static_cast<std::size_t>(index) >= layers_.size())
633 0 : return OGRERR_FAILURE;
634 :
635 : const std::unique_ptr<OGRLayer> &layer =
636 0 : layers_[static_cast<std::size_t>(index)];
637 0 : CPLDebug("HANA", "DeleteLayer(%s)", layer->GetName());
638 :
639 0 : if (auto tableLayer = dynamic_cast<OGRHanaTableLayer *>(layer.get()))
640 : {
641 0 : OGRErr err = tableLayer->DropTable();
642 0 : if (OGRERR_NONE == err)
643 0 : return err;
644 : }
645 :
646 0 : layers_.erase(layers_.begin() + index);
647 :
648 0 : return OGRERR_NONE;
649 : }
650 :
651 18 : void OGRHanaDataSource::CreateTable(
652 : const CPLString &tableName, const CPLString &fidName,
653 : const CPLString &fidType, const CPLString &geomColumnName,
654 : OGRwkbGeometryType geomType, bool geomColumnNullable,
655 : const CPLString &geomColumnIndexType, int geomSrid)
656 : {
657 36 : CPLString sql;
658 50 : if (geomType == OGRwkbGeometryType::wkbNone ||
659 32 : !(!geomColumnName.empty() && geomSrid >= 0))
660 : {
661 6 : sql = "CREATE COLUMN TABLE " +
662 18 : GetFullTableNameQuoted(schemaName_, tableName) + " (" +
663 24 : QuotedIdentifier(fidName) + " " + fidType +
664 12 : " GENERATED BY DEFAULT AS IDENTITY, PRIMARY KEY ( " +
665 18 : QuotedIdentifier(fidName) + "));";
666 : }
667 : else
668 : {
669 12 : sql = "CREATE COLUMN TABLE " +
670 36 : GetFullTableNameQuoted(schemaName_, tableName) + " (" +
671 48 : QuotedIdentifier(fidName) + " " + fidType +
672 24 : " GENERATED BY DEFAULT AS IDENTITY, " +
673 48 : QuotedIdentifier(geomColumnName) + " ST_GEOMETRY (" +
674 48 : std::to_string(geomSrid) + ")" +
675 24 : (geomColumnNullable ? "" : " NOT NULL") +
676 36 : " SPATIAL INDEX PREFERENCE " + geomColumnIndexType +
677 36 : ", PRIMARY KEY ( " + QuotedIdentifier(fidName) + "));";
678 : }
679 :
680 18 : ExecuteSQL(sql);
681 18 : }
682 :
683 56 : void OGRHanaDataSource::DetermineVersions()
684 : {
685 56 : odbc::DatabaseMetaDataRef dbmd = conn_->getDatabaseMetaData();
686 56 : CPLString dbVersion(dbmd->getDBMSVersion());
687 56 : hanaVersion_ = HanaVersion::fromString(dbVersion);
688 :
689 56 : if (hanaVersion_.major() < 4)
690 : {
691 0 : cloudVersion_ = HanaVersion(0, 0, 0);
692 0 : return;
693 : }
694 :
695 112 : odbc::StatementRef stmt = conn_->createStatement();
696 56 : const char *sql = "SELECT CLOUD_VERSION FROM SYS.M_DATABASE;";
697 :
698 112 : odbc::ResultSetRef rsVersion = stmt->executeQuery(sql);
699 56 : if (rsVersion->next())
700 : cloudVersion_ =
701 56 : HanaVersion::fromString(rsVersion->getString(1)->c_str());
702 :
703 56 : rsVersion->close();
704 : }
705 :
706 : /************************************************************************/
707 : /* FindSchemaAndTableNames() */
708 : /************************************************************************/
709 :
710 : std::pair<CPLString, CPLString>
711 15 : OGRHanaDataSource::FindSchemaAndTableNames(const char *query)
712 : {
713 30 : odbc::PreparedStatementRef stmt = PrepareStatement(query);
714 15 : if (stmt.get() == nullptr)
715 0 : return {"", ""};
716 :
717 30 : odbc::ResultSetMetaDataRef rsmd = stmt->getMetaData();
718 :
719 : // Note, getTableName returns correct table name also in the case
720 : // when the original sql query uses a view
721 30 : CPLString tableName = rsmd->getTableName(1);
722 15 : if (tableName == "M_DATABASE_")
723 0 : tableName = "M_DATABASE";
724 30 : CPLString schemaName = rsmd->getSchemaName(1);
725 15 : if (schemaName.empty() && !tableName.empty())
726 14 : schemaName = FindSchemaName(tableName.c_str());
727 15 : return {schemaName, tableName};
728 : }
729 :
730 : /************************************************************************/
731 : /* FindLayerByName() */
732 : /************************************************************************/
733 :
734 67 : int OGRHanaDataSource::FindLayerByName(const char *name)
735 : {
736 604 : for (size_t i = 0; i < layers_.size(); ++i)
737 : {
738 574 : if (EQUAL(name, layers_[i]->GetName()))
739 37 : return static_cast<int>(i);
740 : }
741 30 : return -1;
742 : }
743 :
744 : /************************************************************************/
745 : /* FindSchemaName() */
746 : /************************************************************************/
747 :
748 14 : CPLString OGRHanaDataSource::FindSchemaName(const char *objectName)
749 : {
750 26 : auto getSchemaName = [&](const char *sql)
751 : {
752 52 : odbc::PreparedStatementRef stmt = PrepareStatement(sql);
753 26 : stmt->setString(1, odbc::String(objectName));
754 52 : odbc::ResultSetRef rsEntries = stmt->executeQuery();
755 26 : CPLString ret;
756 40 : while (rsEntries->next())
757 : {
758 : // return empty string if there is more than one schema.
759 26 : if (!ret.empty())
760 : {
761 12 : ret.clear();
762 12 : break;
763 : }
764 14 : ret = *rsEntries->getString(1);
765 : }
766 26 : rsEntries->close();
767 :
768 52 : return ret;
769 14 : };
770 :
771 : CPLString ret = getSchemaName(
772 14 : "SELECT SCHEMA_NAME FROM SYS.TABLES WHERE TABLE_NAME = ?");
773 14 : if (ret.empty())
774 24 : ret = getSchemaName(
775 12 : "SELECT SCHEMA_NAME FROM SYS.VIEWS WHERE VIEW_NAME = ?");
776 :
777 28 : return ret;
778 : }
779 :
780 : /************************************************************************/
781 : /* CreateStatement() */
782 : /************************************************************************/
783 :
784 224 : odbc::StatementRef OGRHanaDataSource::CreateStatement()
785 : {
786 224 : return conn_->createStatement();
787 : }
788 :
789 : /************************************************************************/
790 : /* PrepareStatement() */
791 : /************************************************************************/
792 :
793 335 : odbc::PreparedStatementRef OGRHanaDataSource::PrepareStatement(const char *sql)
794 : {
795 335 : CPLAssert(sql != nullptr);
796 :
797 : try
798 : {
799 335 : CPLDebug("HANA", "Prepare statement %s.", sql);
800 :
801 670 : std::u16string sqlUtf16 = odbc::StringConverter::utf8ToUtf16(sql);
802 335 : return conn_->prepareStatement(sqlUtf16.c_str());
803 : }
804 2 : catch (const odbc::Exception &ex)
805 : {
806 1 : CPLError(CE_Failure, CPLE_AppDefined, "Failed to prepare statement: %s",
807 1 : ex.what());
808 : }
809 1 : return nullptr;
810 : }
811 :
812 : /************************************************************************/
813 : /* Commit() */
814 : /************************************************************************/
815 :
816 134 : void OGRHanaDataSource::Commit()
817 : {
818 134 : conn_->commit();
819 134 : }
820 :
821 : /************************************************************************/
822 : /* ExecuteSQL() */
823 : /************************************************************************/
824 :
825 96 : void OGRHanaDataSource::ExecuteSQL(const CPLString &sql)
826 : {
827 : std::u16string sqlUtf16 =
828 192 : odbc::StringConverter::utf8ToUtf16(sql.c_str(), sql.length());
829 192 : odbc::StatementRef stmt = conn_->createStatement();
830 96 : stmt->execute(sqlUtf16.c_str());
831 92 : if (!IsTransactionStarted())
832 91 : conn_->commit();
833 92 : }
834 :
835 : /************************************************************************/
836 : /* GetSrsById() */
837 : /* */
838 : /* Return a SRS corresponding to a particular id. The returned */
839 : /* object has its reference counter incremented. Consequently */
840 : /* the caller should call Release() on it (if not null) once done */
841 : /* with it. */
842 : /************************************************************************/
843 :
844 30 : OGRSpatialReference *OGRHanaDataSource::GetSrsById(int srid)
845 : {
846 30 : if (srid < 0)
847 0 : return nullptr;
848 :
849 30 : auto it = srsCache_.find(srid);
850 30 : if (it != srsCache_.end())
851 : {
852 2 : it->second->Reference();
853 2 : return it->second;
854 : }
855 :
856 28 : OGRSpatialReference *srs = nullptr;
857 :
858 28 : CPLString wkt = GetSrsWktById(*conn_, srid);
859 28 : if (!wkt.empty())
860 : {
861 28 : srs = new OGRSpatialReference();
862 28 : OGRErr err = srs->importFromWkt(wkt.c_str());
863 28 : if (OGRERR_NONE != err)
864 : {
865 0 : delete srs;
866 0 : srs = nullptr;
867 : }
868 : }
869 :
870 28 : srsCache_.insert({srid, srs});
871 :
872 28 : if (srs)
873 28 : srs->Reference();
874 28 : return srs;
875 : }
876 :
877 : /************************************************************************/
878 : /* GetSrsId() */
879 : /************************************************************************/
880 :
881 14 : int OGRHanaDataSource::GetSrsId(const OGRSpatialReference *srs)
882 : {
883 14 : if (srs == nullptr)
884 1 : return UNDETERMINED_SRID;
885 :
886 : /* -------------------------------------------------------------------- */
887 : /* Try to find srs id using authority name and code (EPSG:3857). */
888 : /* -------------------------------------------------------------------- */
889 26 : OGRSpatialReference srsLocal(*srs);
890 :
891 13 : const char *authorityName = srsLocal.GetAuthorityName(nullptr);
892 13 : if (authorityName == nullptr || strlen(authorityName) == 0)
893 : {
894 0 : srsLocal.AutoIdentifyEPSG();
895 0 : authorityName = srsLocal.GetAuthorityName(nullptr);
896 0 : if (authorityName != nullptr && EQUAL(authorityName, "EPSG"))
897 : {
898 0 : const char *authorityCode = srsLocal.GetAuthorityCode(nullptr);
899 0 : if (authorityCode != nullptr && strlen(authorityCode) > 0)
900 : {
901 0 : srsLocal.importFromEPSG(atoi(authorityCode));
902 0 : authorityName = srsLocal.GetAuthorityName(nullptr);
903 : }
904 : }
905 : }
906 :
907 13 : int authorityCode = 0;
908 13 : if (authorityName != nullptr)
909 : {
910 13 : authorityCode = atoi(srsLocal.GetAuthorityCode(nullptr));
911 13 : if (authorityCode > 0)
912 : {
913 13 : int ret = GetSridWithFilter(
914 : *conn_,
915 26 : CPLString().Printf("SRS_ID = %d AND ORGANIZATION = '%s'",
916 13 : authorityCode, authorityName));
917 13 : if (ret != UNDETERMINED_SRID)
918 13 : return ret;
919 : }
920 : }
921 :
922 : /* -------------------------------------------------------------------- */
923 : /* Try to find srs id using wkt content. */
924 : /* -------------------------------------------------------------------- */
925 :
926 0 : char *wkt = nullptr;
927 0 : OGRErr err = srsLocal.exportToWkt(&wkt);
928 0 : CPLString strWkt(wkt);
929 0 : CPLFree(wkt);
930 :
931 0 : if (OGRERR_NONE != err)
932 0 : return UNDETERMINED_SRID;
933 :
934 0 : int srid = GetSridWithFilter(
935 0 : *conn_, CPLString().Printf("DEFINITION = '%s'", strWkt.c_str()));
936 0 : if (srid != UNDETERMINED_SRID)
937 0 : return srid;
938 :
939 : /* -------------------------------------------------------------------- */
940 : /* Try to add a new spatial reference system to the database */
941 : /* -------------------------------------------------------------------- */
942 :
943 0 : char *proj4 = nullptr;
944 0 : err = srsLocal.exportToProj4(&proj4);
945 0 : CPLString strProj4(proj4);
946 0 : CPLFree(proj4);
947 :
948 0 : if (OGRERR_NONE != err)
949 0 : return srid;
950 :
951 0 : if (authorityName != nullptr && authorityCode > 0)
952 : {
953 0 : srid = authorityCode;
954 : }
955 : else
956 : {
957 0 : odbc::StatementRef stmt = conn_->createStatement();
958 0 : const char *sql =
959 : "SELECT MAX(SRS_ID) FROM SYS.ST_SPATIAL_REFERENCE_SYSTEMS WHERE "
960 : "SRS_ID >= 10000000 AND SRS_ID < 20000000";
961 0 : odbc::ResultSetRef rsSrid = stmt->executeQuery(sql);
962 0 : while (rsSrid->next())
963 : {
964 0 : odbc::Int val = rsSrid->getInt(1);
965 0 : srid = val.isNull() ? 10000000 : *val + 1;
966 : }
967 0 : rsSrid->close();
968 : }
969 :
970 : try
971 : {
972 0 : CreateSpatialReferenceSystem(srsLocal, srid, authorityName,
973 : authorityCode, strWkt, strProj4);
974 0 : return srid;
975 : }
976 0 : catch (const odbc::Exception &ex)
977 : {
978 0 : CPLError(CE_Failure, CPLE_AppDefined,
979 0 : "Unable to create an SRS in the database: %s.\n", ex.what());
980 : }
981 :
982 0 : return UNDETERMINED_SRID;
983 : }
984 :
985 : /************************************************************************/
986 : /* IsSrsRoundEarth() */
987 : /************************************************************************/
988 :
989 12 : bool OGRHanaDataSource::IsSrsRoundEarth(int srid)
990 : {
991 12 : const char *sql =
992 : "SELECT ROUND_EARTH FROM SYS.ST_SPATIAL_REFERENCE_SYSTEMS "
993 : "WHERE SRS_ID = ?";
994 24 : odbc::PreparedStatementRef stmt = PrepareStatement(sql);
995 12 : stmt->setInt(1, odbc::Int(srid));
996 12 : odbc::ResultSetRef rs = stmt->executeQuery();
997 12 : bool ret = false;
998 12 : if (rs->next())
999 12 : ret = (*rs->getString(1) == "TRUE");
1000 12 : rs->close();
1001 24 : return ret;
1002 : }
1003 :
1004 : /************************************************************************/
1005 : /* HasSrsPlanarEquivalent() */
1006 : /************************************************************************/
1007 :
1008 2 : bool OGRHanaDataSource::HasSrsPlanarEquivalent(int srid)
1009 : {
1010 2 : const char *sql = "SELECT COUNT(*) FROM SYS.ST_SPATIAL_REFERENCE_SYSTEMS "
1011 : "WHERE SRS_ID = ?";
1012 4 : odbc::PreparedStatementRef stmt = PrepareStatement(sql);
1013 2 : stmt->setInt(1, ToPlanarSRID(srid));
1014 2 : odbc::ResultSetRef rs = stmt->executeQuery();
1015 2 : std::int64_t count = 0;
1016 2 : if (rs->next())
1017 2 : count = *rs->getLong(1);
1018 2 : rs->close();
1019 4 : return count > 0;
1020 : }
1021 :
1022 : /************************************************************************/
1023 : /* GetQueryColumns() */
1024 : /************************************************************************/
1025 :
1026 55 : OGRErr OGRHanaDataSource::GetQueryColumns(
1027 : const CPLString &schemaName, const CPLString &query,
1028 : std::vector<ColumnDescription> &columnDescriptions)
1029 : {
1030 55 : columnDescriptions.clear();
1031 :
1032 110 : odbc::PreparedStatementRef stmtQuery = PrepareStatement(query);
1033 :
1034 55 : if (stmtQuery.isNull())
1035 0 : return OGRERR_FAILURE;
1036 :
1037 110 : odbc::ResultSetMetaDataRef rsmd = stmtQuery->getMetaData();
1038 55 : std::size_t numColumns = rsmd->getColumnCount();
1039 55 : if (numColumns == 0)
1040 0 : return OGRERR_NONE;
1041 :
1042 55 : columnDescriptions.reserve(numColumns);
1043 :
1044 110 : odbc::DatabaseMetaDataRef dmd = conn_->getDatabaseMetaData();
1045 : odbc::PreparedStatementRef stmtArrayTypeInfo =
1046 : PrepareStatement("SELECT DATA_TYPE_NAME FROM "
1047 : "SYS.TABLE_COLUMNS_ODBC WHERE SCHEMA_NAME = ? "
1048 : "AND TABLE_NAME = ? AND COLUMN_NAME = ? AND "
1049 110 : "DATA_TYPE_NAME LIKE '% ARRAY'");
1050 :
1051 254 : for (unsigned short clmIndex = 1; clmIndex <= numColumns; ++clmIndex)
1052 : {
1053 199 : CPLString typeName = rsmd->getColumnTypeName(clmIndex);
1054 :
1055 199 : if (typeName.empty())
1056 0 : continue;
1057 :
1058 199 : bool isArray = false;
1059 199 : CPLString tableName = rsmd->getTableName(clmIndex);
1060 199 : CPLString columnName = rsmd->getColumnName(clmIndex);
1061 199 : CPLString defaultValue;
1062 199 : short dataType = rsmd->getColumnType(clmIndex);
1063 :
1064 199 : if (!schemaName.empty() && !tableName.empty())
1065 : {
1066 : // Retrieve information about default value in column
1067 : odbc::ResultSetRef rsColumns =
1068 : dmd->getColumns(nullptr, schemaName.c_str(), tableName.c_str(),
1069 141 : columnName.c_str());
1070 141 : if (rsColumns->next())
1071 : {
1072 : odbc::String defaultValueStr =
1073 276 : rsColumns->getString(13 /*COLUMN_DEF*/);
1074 138 : if (!defaultValueStr.isNull())
1075 : defaultValue =
1076 12 : FormatDefaultValue(defaultValueStr->c_str(), dataType);
1077 : }
1078 141 : rsColumns->close();
1079 :
1080 : // Retrieve information about array type
1081 141 : stmtArrayTypeInfo->setString(1, schemaName);
1082 141 : stmtArrayTypeInfo->setString(2, tableName);
1083 141 : stmtArrayTypeInfo->setString(3, columnName);
1084 141 : odbc::ResultSetRef rsArrayTypes = stmtArrayTypeInfo->executeQuery();
1085 141 : if (rsArrayTypes->next())
1086 : {
1087 11 : typeName = *rsArrayTypes->getString(1);
1088 11 : dataType = GetArrayDataType(typeName);
1089 :
1090 11 : if (dataType == QGRHanaDataTypes::Unknown)
1091 : {
1092 0 : CPLError(
1093 : CE_Failure, CPLE_AppDefined,
1094 : "GetQueryColumns(): Unsupported type of array (%s)",
1095 : typeName.c_str());
1096 0 : return OGRERR_FAILURE;
1097 : }
1098 :
1099 11 : isArray = true;
1100 : }
1101 141 : rsArrayTypes->close();
1102 : }
1103 :
1104 199 : if (!isArray && !IsKnownDataType(dataType))
1105 : {
1106 0 : odbc::ResultSetRef rsTypeInfo = dmd->getTypeInfo(dataType);
1107 0 : if (rsTypeInfo->next())
1108 : {
1109 0 : odbc::String name = rsTypeInfo->getString(1);
1110 0 : if (name.isNull())
1111 0 : continue;
1112 0 : if (name->compare("SHORTTEXT") == 0 ||
1113 0 : name->compare("ALPHANUM") == 0)
1114 : {
1115 0 : dataType = QGRHanaDataTypes::WVarChar;
1116 : }
1117 : }
1118 0 : rsTypeInfo->close();
1119 : }
1120 :
1121 199 : if (dataType == QGRHanaDataTypes::Geometry)
1122 : {
1123 33 : GeometryColumnDescription geometryColumnDesc;
1124 33 : if (schemaName.empty() || tableName.empty())
1125 18 : geometryColumnDesc = GetGeometryColumnDescription(
1126 18 : *conn_, query, columnName, detectGeometryType_);
1127 : else
1128 48 : geometryColumnDesc = GetGeometryColumnDescription(
1129 : *conn_, schemaName, tableName, columnName,
1130 48 : detectGeometryType_);
1131 33 : geometryColumnDesc.isNullable = rsmd->isNullable(clmIndex);
1132 :
1133 66 : columnDescriptions.push_back({true, AttributeColumnDescription(),
1134 33 : std::move(geometryColumnDesc)});
1135 : }
1136 : else
1137 : {
1138 166 : AttributeColumnDescription attributeColumnDesc;
1139 166 : attributeColumnDesc.name = std::move(columnName);
1140 166 : attributeColumnDesc.type = dataType;
1141 166 : attributeColumnDesc.typeName = std::move(typeName);
1142 166 : attributeColumnDesc.isArray = isArray;
1143 166 : attributeColumnDesc.isNullable = rsmd->isNullable(clmIndex);
1144 166 : attributeColumnDesc.isAutoIncrement =
1145 166 : rsmd->isAutoIncrement(clmIndex);
1146 166 : attributeColumnDesc.length =
1147 166 : static_cast<int>(rsmd->getColumnLength(clmIndex));
1148 166 : attributeColumnDesc.precision = rsmd->getPrecision(clmIndex);
1149 166 : attributeColumnDesc.scale = rsmd->getScale(clmIndex);
1150 166 : attributeColumnDesc.defaultValue = std::move(defaultValue);
1151 :
1152 166 : columnDescriptions.push_back({false, std::move(attributeColumnDesc),
1153 : GeometryColumnDescription()});
1154 : }
1155 : }
1156 :
1157 55 : return OGRERR_NONE;
1158 : }
1159 :
1160 : /************************************************************************/
1161 : /* GetTablePrimaryKeys() */
1162 : /************************************************************************/
1163 :
1164 : std::vector<CPLString>
1165 55 : OGRHanaDataSource::GetTablePrimaryKeys(const char *schemaName,
1166 : const char *tableName)
1167 : {
1168 55 : std::vector<CPLString> ret;
1169 :
1170 110 : odbc::DatabaseMetaDataRef dmd = conn_->getDatabaseMetaData();
1171 : odbc::ResultSetRef rsPrimaryKeys =
1172 110 : dmd->getPrimaryKeys(nullptr, schemaName, tableName);
1173 129 : while (rsPrimaryKeys->next())
1174 : {
1175 74 : ret.push_back(*rsPrimaryKeys->getString(4));
1176 : }
1177 55 : rsPrimaryKeys->close();
1178 :
1179 110 : return ret;
1180 : }
1181 :
1182 : /************************************************************************/
1183 : /* InitializeLayers() */
1184 : /************************************************************************/
1185 :
1186 56 : void OGRHanaDataSource::InitializeLayers(const char *schemaName,
1187 : const char *tableNames)
1188 : {
1189 112 : std::vector<CPLString> tablesToFind = SplitStrings(tableNames, ",");
1190 56 : const bool hasTablesToFind = !tablesToFind.empty();
1191 :
1192 112 : auto addLayersFromQuery = [&](const char *query, bool updatable)
1193 : {
1194 224 : odbc::PreparedStatementRef stmt = PrepareStatement(query);
1195 112 : stmt->setString(1, odbc::String(schemaName));
1196 224 : odbc::ResultSetRef rsTables = stmt->executeQuery();
1197 579 : while (rsTables->next())
1198 : {
1199 467 : odbc::String tableName = rsTables->getString(1);
1200 467 : if (tableName.isNull())
1201 0 : continue;
1202 : auto pos =
1203 467 : std::find(tablesToFind.begin(), tablesToFind.end(), *tableName);
1204 467 : if (pos != tablesToFind.end())
1205 0 : tablesToFind.erase(pos);
1206 :
1207 : auto layer = std::make_unique<OGRHanaTableLayer>(
1208 467 : this, schemaName_.c_str(), tableName->c_str(), updatable);
1209 467 : layers_.push_back(std::move(layer));
1210 : }
1211 112 : rsTables->close();
1212 112 : };
1213 :
1214 : // Look for layers in Tables
1215 112 : std::ostringstream osTables;
1216 56 : osTables << "SELECT TABLE_NAME FROM SYS.TABLES WHERE SCHEMA_NAME = ?";
1217 56 : if (!tablesToFind.empty())
1218 : osTables << " AND TABLE_NAME IN ("
1219 0 : << JoinStrings(tablesToFind, ",", Literal) << ")";
1220 :
1221 56 : addLayersFromQuery(osTables.str().c_str(), updateMode_);
1222 :
1223 56 : if (!(hasTablesToFind && tablesToFind.empty()))
1224 : {
1225 : // Look for layers in Views
1226 56 : std::ostringstream osViews;
1227 56 : osViews << "SELECT VIEW_NAME FROM SYS.VIEWS WHERE SCHEMA_NAME = ?";
1228 : // cppcheck-suppress knownConditionTrueFalse
1229 56 : if (!tablesToFind.empty())
1230 : osViews << " AND VIEW_NAME IN ("
1231 0 : << JoinStrings(tablesToFind, ",", Literal) << ")";
1232 :
1233 56 : addLayersFromQuery(osViews.str().c_str(), false);
1234 : }
1235 :
1236 : // Report about tables that could not be found
1237 56 : for (const auto &tableName : tablesToFind)
1238 : {
1239 0 : const char *layerName = tableName.c_str();
1240 0 : if (GetLayerByName(layerName) == nullptr)
1241 0 : CPLDebug("HANA",
1242 : "Table '%s' not found or does not "
1243 : "have any geometry column.",
1244 : layerName);
1245 : }
1246 56 : }
1247 :
1248 : /************************************************************************/
1249 : /* LaunderName() */
1250 : /************************************************************************/
1251 :
1252 115 : std::pair<OGRErr, CPLString> OGRHanaDataSource::LaunderName(const char *name)
1253 : {
1254 115 : CPLAssert(name != nullptr);
1255 :
1256 115 : if (!CPLIsUTF8(name, -1))
1257 : {
1258 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s is not a valid UTF-8 string.",
1259 : name);
1260 0 : return {OGRERR_FAILURE, ""};
1261 : }
1262 :
1263 1050 : auto getUTF8SequenceLength = [](char c)
1264 : {
1265 1050 : if ((c & 0x80) == 0x00)
1266 1040 : return 1;
1267 10 : if ((c & 0xE0) == 0xC0)
1268 6 : return 2;
1269 4 : if ((c & 0xF0) == 0xE0)
1270 0 : return 3;
1271 4 : if ((c & 0xF8) == 0xF0)
1272 4 : return 4;
1273 :
1274 0 : throw std::runtime_error("Invalid UTF-8 sequence");
1275 : };
1276 :
1277 230 : CPLString newName(name);
1278 115 : bool hasNonASCII = false;
1279 115 : size_t i = 0;
1280 :
1281 1165 : while (name[i] != '\0')
1282 : {
1283 1050 : char c = name[i];
1284 1050 : int len = getUTF8SequenceLength(c);
1285 1050 : if (len == 1)
1286 : {
1287 1040 : if (c == '-' || c == '#')
1288 6 : newName[i] = '_';
1289 : else
1290 1034 : newName[i] = static_cast<char>(
1291 1034 : CPLToupper(static_cast<unsigned char>(c)));
1292 : }
1293 : else
1294 : {
1295 10 : hasNonASCII = true;
1296 : }
1297 :
1298 1050 : i += len;
1299 : }
1300 :
1301 115 : if (!hasNonASCII)
1302 222 : return {OGRERR_NONE, newName};
1303 :
1304 4 : const char *sql = "SELECT UPPER(?) FROM DUMMY";
1305 8 : odbc::PreparedStatementRef stmt = PrepareStatement(sql);
1306 4 : stmt->setString(1, odbc::String(newName.c_str()));
1307 8 : odbc::ResultSetRef rsName = stmt->executeQuery();
1308 4 : OGRErr err = OGRERR_NONE;
1309 4 : if (rsName->next())
1310 : {
1311 4 : newName.swap(*rsName->getString(1));
1312 : }
1313 : else
1314 : {
1315 0 : err = OGRERR_FAILURE;
1316 0 : newName.clear();
1317 : }
1318 4 : rsName->close();
1319 4 : return {err, newName};
1320 : }
1321 :
1322 : /************************************************************************/
1323 : /* CreateSpatialReference() */
1324 : /************************************************************************/
1325 :
1326 0 : void OGRHanaDataSource::CreateSpatialReferenceSystem(
1327 : const OGRSpatialReference &srs, int srid, const char *authorityName,
1328 : int authorityCode, const CPLString &wkt, const CPLString &proj4)
1329 : {
1330 0 : CPLString refName((srs.IsProjected()) ? srs.GetAttrValue("PROJCS")
1331 0 : : srs.GetAttrValue("GEOGCS"));
1332 0 : if (refName.empty() || EQUAL(refName.c_str(), "UNKNOWN"))
1333 0 : refName = "OGR_PROJECTION_" + std::to_string(srid);
1334 :
1335 0 : OGRErr err = OGRERR_NONE;
1336 0 : CPLString ellipsoidParams;
1337 0 : const double semiMajor = srs.GetSemiMajor(&err);
1338 0 : if (OGRERR_NONE == err)
1339 0 : ellipsoidParams += " SEMI MAJOR AXIS " + std::to_string(semiMajor);
1340 0 : const double semiMinor = srs.GetSemiMinor(&err);
1341 0 : const double invFlattening = srs.GetInvFlattening(&err);
1342 0 : if (OGRERR_NONE == err)
1343 : ellipsoidParams +=
1344 0 : " INVERSE FLATTENING " + std::to_string(invFlattening);
1345 : else
1346 0 : ellipsoidParams += " SEMI MINOR AXIS " + std::to_string(semiMinor);
1347 :
1348 0 : const char *linearUnits = nullptr;
1349 0 : srs.GetLinearUnits(&linearUnits);
1350 0 : const char *angularUnits = nullptr;
1351 0 : srs.GetAngularUnits(&angularUnits);
1352 :
1353 0 : CPLString xRange, yRange;
1354 : double dfWestLongitudeDeg, dfSouthLatitudeDeg, dfEastLongitudeDeg,
1355 : dfNorthLatitudeDeg;
1356 0 : if (srs.GetAreaOfUse(&dfWestLongitudeDeg, &dfSouthLatitudeDeg,
1357 : &dfEastLongitudeDeg, &dfNorthLatitudeDeg, nullptr))
1358 : {
1359 0 : xRange = CPLString().Printf("%s BETWEEN %f AND %f",
1360 0 : srs.IsGeographic() ? "LONGITUDE" : "X",
1361 0 : dfWestLongitudeDeg, dfEastLongitudeDeg);
1362 0 : yRange = CPLString().Printf("%s BETWEEN %f AND %f",
1363 0 : srs.IsGeographic() ? "LATITUDE" : "Y",
1364 0 : dfSouthLatitudeDeg, dfNorthLatitudeDeg);
1365 : }
1366 : else
1367 : {
1368 0 : xRange = CPLString().Printf("%s UNBOUNDED",
1369 0 : srs.IsGeographic() ? "LONGITUDE" : "X");
1370 0 : yRange = CPLString().Printf("%s UNBOUNDED ",
1371 0 : srs.IsGeographic() ? "LATITUDE" : "Y");
1372 : }
1373 :
1374 0 : CPLString organization;
1375 0 : if (authorityName != nullptr && authorityCode > 0)
1376 : {
1377 0 : organization = CPLString().Printf(
1378 : "ORGANIZATION %s IDENTIFIED BY %d",
1379 0 : QuotedIdentifier(authorityName).c_str(), authorityCode);
1380 : }
1381 :
1382 0 : CPLString sql = CPLString().Printf(
1383 : "CREATE SPATIAL REFERENCE SYSTEM %s "
1384 : "IDENTIFIED BY %d "
1385 : "TYPE %s "
1386 : "LINEAR UNIT OF MEASURE %s "
1387 : "ANGULAR UNIT OF MEASURE %s "
1388 : "%s " // ELLIPSOID
1389 : "COORDINATE %s "
1390 : "COORDINATE %s "
1391 : "%s " // ORGANIZATION
1392 : "DEFINITION %s "
1393 : "TRANSFORM DEFINITION %s",
1394 0 : QuotedIdentifier(refName).c_str(), srid,
1395 0 : srs.IsGeographic() ? "ROUND EARTH" : "PLANAR",
1396 0 : QuotedIdentifier(
1397 0 : (linearUnits == nullptr || EQUAL(linearUnits, "unknown"))
1398 : ? "metre"
1399 : : linearUnits)
1400 0 : .tolower()
1401 : .c_str(),
1402 0 : QuotedIdentifier(
1403 0 : (angularUnits == nullptr || EQUAL(angularUnits, "unknown"))
1404 : ? "degree"
1405 : : angularUnits)
1406 0 : .tolower()
1407 : .c_str(),
1408 0 : (ellipsoidParams.empty() ? ""
1409 0 : : ("ELLIPSOID" + ellipsoidParams).c_str()),
1410 : xRange.c_str(), yRange.c_str(), organization.c_str(),
1411 0 : Literal(wkt).c_str(), Literal(proj4).c_str());
1412 :
1413 0 : ExecuteSQL(sql);
1414 0 : }
1415 :
1416 : /************************************************************************/
1417 : /* CreateParseArrayFunctions() */
1418 : /************************************************************************/
1419 :
1420 1 : void OGRHanaDataSource::CreateParseArrayFunctions(const char *schemaName)
1421 : {
1422 8 : auto replaceAll = [](const CPLString &str, const CPLString &before,
1423 : const CPLString &after) -> CPLString
1424 : {
1425 16 : CPLString res = str;
1426 16 : return res.replaceAll(before, after);
1427 : };
1428 :
1429 : // clang-format off
1430 : const CPLString parseStringArrayFunc =
1431 : "CREATE OR REPLACE FUNCTION {SCHEMA}.OGR_PARSE_STRING_ARRAY(IN str NCLOB, IN delimiter NVARCHAR(10))\n"
1432 : "RETURNS TABLE(VALUE NVARCHAR(512))\n"
1433 : "LANGUAGE SQLSCRIPT\n"
1434 : "SQL SECURITY INVOKER AS\n"
1435 : "BEGIN\n"
1436 : "DECLARE arrValues NVARCHAR(512) ARRAY;\n"
1437 : "DECLARE idx INTEGER = 1;\n"
1438 : "DECLARE curPos INTEGER = 1;\n"
1439 : "DECLARE lastPos INTEGER = 1;\n"
1440 : "DECLARE delimiterLength INTEGER = LENGTH(delimiter);\n"
1441 :
1442 : "IF(NOT(:str IS NULL)) THEN\n"
1443 : "WHILE(:curPos > 0) DO\n"
1444 : "curPos = LOCATE(:str, :delimiter, :lastPos);\n"
1445 : "IF :curPos = 0 THEN\n"
1446 : "BREAK;\n"
1447 : "END IF;\n"
1448 :
1449 : "arrValues[:idx] = SUBSTRING(:str, :lastPos, :curPos - :lastPos);\n"
1450 : "lastPos = :curPos + :delimiterLength;\n"
1451 : "idx = :idx + 1;\n"
1452 : "END WHILE;\n"
1453 :
1454 : "arrValues[:idx] = SUBSTRING(:str, :lastPos, LENGTH(:str));\n"
1455 : "END IF;\n"
1456 :
1457 : "ret = UNNEST(:arrValues) AS(\"VALUE\");\n"
1458 : "RETURN SELECT * FROM :ret;\n"
1459 2 : "END;\n";
1460 : // clang-format on
1461 :
1462 : CPLString sql = replaceAll(parseStringArrayFunc, "{SCHEMA}",
1463 3 : QuotedIdentifier(schemaName));
1464 1 : ExecuteSQL(sql);
1465 :
1466 : // clang-format off
1467 : const CPLString parseTypeArrayFunc =
1468 : "CREATE OR REPLACE FUNCTION {SCHEMA}.OGR_PARSE_{TYPE}_ARRAY(IN str NCLOB, IN delimiter NVARCHAR(10))\n"
1469 : "RETURNS TABLE(VALUE {TYPE})\n"
1470 : "LANGUAGE SQLSCRIPT\n"
1471 : "SQL SECURITY INVOKER AS\n"
1472 : "BEGIN\n"
1473 : "DECLARE arrValues {TYPE} ARRAY;\n"
1474 : "DECLARE elemValue STRING;\n"
1475 : "DECLARE idx INTEGER = 1;\n"
1476 : "DECLARE CURSOR cursor_values FOR\n"
1477 : "SELECT * FROM OGR_PARSE_STRING_ARRAY(:str, :delimiter);\n"
1478 :
1479 : "FOR row_value AS cursor_values DO\n"
1480 : "elemValue = TRIM(row_value.VALUE);\n"
1481 : "IF(UPPER(elemValue) = 'NULL') THEN\n"
1482 : "arrValues[:idx] = CAST(NULL AS {TYPE});\n"
1483 : "ELSE\n"
1484 : "arrValues[:idx] = CAST(:elemValue AS {TYPE});\n"
1485 : "END IF;\n"
1486 : "idx = :idx + 1;\n"
1487 : "END FOR;\n"
1488 :
1489 : "ret = UNNEST(:arrValues) AS(\"VALUE\");\n"
1490 : "RETURN SELECT * FROM :ret;\n"
1491 2 : "END;\n";
1492 : // clang-format on
1493 :
1494 2 : sql = replaceAll(parseTypeArrayFunc, "{SCHEMA}",
1495 3 : QuotedIdentifier(schemaName));
1496 :
1497 8 : for (const CPLString &type : GetSupportedArrayTypes())
1498 : {
1499 7 : if (type == "STRING")
1500 1 : continue;
1501 6 : ExecuteSQL(replaceAll(sql, "{TYPE}", type));
1502 : }
1503 1 : }
1504 :
1505 : /************************************************************************/
1506 : /* ParseArrayFunctionsExist() */
1507 : /************************************************************************/
1508 :
1509 1 : bool OGRHanaDataSource::ParseArrayFunctionsExist(const char *schemaName)
1510 : {
1511 1 : const char *sql =
1512 : "SELECT COUNT(*) FROM FUNCTIONS WHERE SCHEMA_NAME = ? AND "
1513 : "FUNCTION_NAME LIKE 'OGR_PARSE_%_ARRAY'";
1514 2 : odbc::PreparedStatementRef stmt = PrepareStatement(sql);
1515 1 : stmt->setString(1, odbc::String(schemaName));
1516 1 : odbc::ResultSetRef rsFunctions = stmt->executeQuery();
1517 1 : auto numFunctions = rsFunctions->next() ? *rsFunctions->getLong(1) : 0;
1518 1 : rsFunctions->close();
1519 1 : return (static_cast<std::size_t>(numFunctions) ==
1520 2 : GetSupportedArrayTypes().size());
1521 : }
1522 :
1523 : /************************************************************************/
1524 : /* GetLayer() */
1525 : /************************************************************************/
1526 :
1527 43 : const OGRLayer *OGRHanaDataSource::GetLayer(int index) const
1528 : {
1529 43 : if (index < 0 || static_cast<std::size_t>(index) >= layers_.size())
1530 6 : return nullptr;
1531 37 : return layers_[static_cast<std::size_t>(index)].get();
1532 : }
1533 :
1534 : /************************************************************************/
1535 : /* GetLayerByName() */
1536 : /************************************************************************/
1537 :
1538 39 : OGRLayer *OGRHanaDataSource::GetLayerByName(const char *name)
1539 : {
1540 39 : return GetLayer(FindLayerByName(name));
1541 : }
1542 :
1543 : /************************************************************************/
1544 : /* ICreateLayer() */
1545 : /************************************************************************/
1546 :
1547 : OGRLayer *
1548 18 : OGRHanaDataSource::ICreateLayer(const char *layerNameIn,
1549 : const OGRGeomFieldDefn *poGeomFieldDefn,
1550 : CSLConstList options)
1551 : {
1552 18 : if (layerNameIn == nullptr)
1553 0 : return nullptr;
1554 :
1555 : const auto geomType =
1556 18 : poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
1557 : const auto srs =
1558 18 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
1559 :
1560 : // Check if we are allowed to create new objects in the database
1561 36 : odbc::DatabaseMetaDataRef dmd = conn_->getDatabaseMetaData();
1562 18 : if (dmd->isReadOnly())
1563 : {
1564 0 : CPLError(CE_Failure, CPLE_AppDefined,
1565 : "Unable to create Layer %s.\n"
1566 : "Database %s is read only.",
1567 0 : layerNameIn, dmd->getDatabaseName().c_str());
1568 0 : return nullptr;
1569 : }
1570 :
1571 18 : bool launderNames = CPLFetchBool(
1572 : options, OGRHanaLayerCreationOptionsConstants::LAUNDER, true);
1573 36 : CPLString layerName(layerNameIn);
1574 18 : if (launderNames)
1575 : {
1576 17 : auto nameRes = LaunderName(layerNameIn);
1577 17 : if (nameRes.first != OGRERR_NONE)
1578 0 : return nullptr;
1579 17 : layerName.swap(nameRes.second);
1580 : }
1581 :
1582 18 : CPLDebug("HANA", "Creating layer %s.", layerName.c_str());
1583 :
1584 18 : int layerIndex = FindLayerByName(layerName.c_str());
1585 18 : if (layerIndex >= 0)
1586 : {
1587 0 : bool overwriteLayer = CPLFetchBool(
1588 : options, OGRHanaLayerCreationOptionsConstants::OVERWRITE, false);
1589 0 : if (!overwriteLayer)
1590 : {
1591 0 : CPLError(CE_Failure, CPLE_AppDefined,
1592 : "Layer %s already exists, CreateLayer failed.\n"
1593 : "Use the layer creation option OVERWRITE=YES to "
1594 : "replace it.",
1595 : layerName.c_str());
1596 0 : return nullptr;
1597 : }
1598 :
1599 0 : DeleteLayer(layerIndex);
1600 : }
1601 :
1602 : int batchSize =
1603 18 : CPLFetchInt(options, OGRHanaLayerCreationOptionsConstants::BATCH_SIZE,
1604 : DEFAULT_BATCH_SIZE);
1605 18 : if (batchSize <= 0)
1606 : {
1607 0 : CPLError(CE_Failure, CPLE_AppDefined,
1608 : "Unable to create layer %s. The value of %s parameter must be "
1609 : "greater than 0.",
1610 : layerName.c_str(),
1611 : OGRHanaLayerCreationOptionsConstants::BATCH_SIZE);
1612 0 : return nullptr;
1613 : }
1614 :
1615 18 : int defaultStringSize = CPLFetchInt(
1616 : options, OGRHanaLayerCreationOptionsConstants::DEFAULT_STRING_SIZE,
1617 : DEFAULT_STRING_SIZE);
1618 18 : if (defaultStringSize <= 0)
1619 : {
1620 0 : CPLError(CE_Failure, CPLE_AppDefined,
1621 : "Unable to create layer %s. The value of %s parameter must be "
1622 : "greater than 0.",
1623 : layerName.c_str(),
1624 : OGRHanaLayerCreationOptionsConstants::DEFAULT_STRING_SIZE);
1625 0 : return nullptr;
1626 : }
1627 :
1628 : CPLString geomColumnName(CSLFetchNameValueDef(
1629 : options, OGRHanaLayerCreationOptionsConstants::GEOMETRY_NAME,
1630 36 : "OGR_GEOMETRY"));
1631 18 : if (launderNames)
1632 : {
1633 17 : auto nameRes = LaunderName(geomColumnName.c_str());
1634 17 : if (nameRes.first != OGRERR_NONE)
1635 0 : return nullptr;
1636 17 : geomColumnName.swap(nameRes.second);
1637 : }
1638 :
1639 18 : const bool geomColumnNullable = CPLFetchBool(
1640 : options, OGRHanaLayerCreationOptionsConstants::GEOMETRY_NULLABLE, true);
1641 : CPLString geomColumnIndexType(CSLFetchNameValueDef(
1642 : options, OGRHanaLayerCreationOptionsConstants::GEOMETRY_INDEX,
1643 36 : "DEFAULT"));
1644 :
1645 18 : const char *paramFidName = CSLFetchNameValueDef(
1646 : options, OGRHanaLayerCreationOptionsConstants::FID, "OGR_FID");
1647 36 : CPLString fidName(paramFidName);
1648 18 : if (launderNames)
1649 : {
1650 17 : auto nameRes = LaunderName(paramFidName);
1651 17 : if (nameRes.first != OGRERR_NONE)
1652 0 : return nullptr;
1653 17 : fidName.swap(nameRes.second);
1654 : }
1655 :
1656 : CPLString fidType =
1657 18 : CPLFetchBool(options, OGRHanaLayerCreationOptionsConstants::FID64,
1658 : false)
1659 : ? "BIGINT"
1660 36 : : "INTEGER";
1661 :
1662 18 : CPLDebug("HANA", "Geometry Column Name %s.", geomColumnName.c_str());
1663 18 : CPLDebug("HANA", "FID Column Name %s, Type %s.", fidName.c_str(),
1664 : fidType.c_str());
1665 :
1666 18 : int srid = CPLFetchInt(options, OGRHanaLayerCreationOptionsConstants::SRID,
1667 : UNDETERMINED_SRID);
1668 18 : if (srid < 0 && srs != nullptr)
1669 12 : srid = GetSrsId(srs);
1670 :
1671 : try
1672 : {
1673 18 : CreateTable(layerName, fidName, fidType, geomColumnName, geomType,
1674 : geomColumnNullable, geomColumnIndexType, srid);
1675 : }
1676 0 : catch (const odbc::Exception &ex)
1677 : {
1678 0 : CPLError(CE_Failure, CPLE_AppDefined,
1679 : "Unable to create layer %s. CreateLayer failed:%s\n",
1680 0 : layerName.c_str(), ex.what());
1681 0 : return nullptr;
1682 : }
1683 :
1684 : // Create new layer object
1685 0 : auto layer = std::make_unique<OGRHanaTableLayer>(this, schemaName_.c_str(),
1686 36 : layerName.c_str(), true);
1687 18 : if (geomType != wkbNone && layer->GetLayerDefn()->GetGeomFieldCount() > 0)
1688 12 : layer->GetLayerDefn()->GetGeomFieldDefn(0)->SetNullable(FALSE);
1689 18 : if (batchSize > 0)
1690 18 : layer->SetBatchSize(static_cast<std::size_t>(batchSize));
1691 18 : if (defaultStringSize > 0)
1692 18 : layer->SetDefaultStringSize(
1693 : static_cast<std::size_t>(defaultStringSize));
1694 18 : layer->SetLaunderFlag(launderNames);
1695 18 : layer->SetPrecisionFlag(CPLFetchBool(
1696 : options, OGRHanaLayerCreationOptionsConstants::PRECISION, true));
1697 18 : layer->SetCustomColumnTypes(CSLFetchNameValue(
1698 : options, OGRHanaLayerCreationOptionsConstants::COLUMN_TYPES));
1699 :
1700 18 : layers_.push_back(std::move(layer));
1701 :
1702 18 : return layers_.back().get();
1703 : }
1704 :
1705 : /************************************************************************/
1706 : /* TestCapability() */
1707 : /************************************************************************/
1708 :
1709 27 : int OGRHanaDataSource::TestCapability(const char *capabilities) const
1710 : {
1711 27 : if (EQUAL(capabilities, ODsCCreateLayer))
1712 4 : return updateMode_;
1713 23 : else if (EQUAL(capabilities, ODsCDeleteLayer))
1714 4 : return updateMode_;
1715 19 : else if (EQUAL(capabilities, ODsCCreateGeomFieldAfterCreateLayer))
1716 2 : return updateMode_;
1717 17 : else if (EQUAL(capabilities, ODsCMeasuredGeometries))
1718 5 : return TRUE;
1719 12 : else if (EQUAL(capabilities, ODsCRandomLayerWrite))
1720 0 : return updateMode_;
1721 12 : else if (EQUAL(capabilities, ODsCTransactions))
1722 4 : return TRUE;
1723 : else
1724 8 : return FALSE;
1725 : }
1726 :
1727 : /************************************************************************/
1728 : /* ExecuteSQL() */
1729 : /************************************************************************/
1730 :
1731 29 : OGRLayer *OGRHanaDataSource::ExecuteSQL(const char *sqlCommand,
1732 : OGRGeometry *spatialFilter,
1733 : const char *dialect)
1734 : {
1735 29 : sqlCommand = SkipLeadingSpaces(sqlCommand);
1736 :
1737 29 : if (IsGenericSQLDialect(dialect))
1738 0 : return GDALDataset::ExecuteSQL(sqlCommand, spatialFilter, dialect);
1739 :
1740 29 : if (STARTS_WITH_CI(sqlCommand, "DELLAYER:"))
1741 : {
1742 10 : const char *layerName = SkipLeadingSpaces(sqlCommand + 9);
1743 10 : int layerIndex = FindLayerByName(layerName);
1744 10 : if (layerIndex >= 0)
1745 0 : DeleteLayer(layerIndex);
1746 10 : return nullptr;
1747 : }
1748 19 : if (STARTS_WITH_CI(sqlCommand, "SELECT"))
1749 : {
1750 28 : auto stmt = PrepareStatement(sqlCommand);
1751 14 : if (stmt.isNull())
1752 1 : return nullptr;
1753 :
1754 26 : auto layer = std::make_unique<OGRHanaResultLayer>(this, sqlCommand);
1755 13 : if (spatialFilter != nullptr)
1756 1 : layer->SetSpatialFilter(spatialFilter);
1757 13 : return layer.release();
1758 : }
1759 :
1760 : try
1761 : {
1762 8 : ExecuteSQL(sqlCommand);
1763 : }
1764 6 : catch (const odbc::Exception &ex)
1765 : {
1766 3 : CPLError(CE_Failure, CPLE_AppDefined,
1767 : "Failed to execute SQL statement '%s': %s", sqlCommand,
1768 3 : ex.what());
1769 : }
1770 :
1771 5 : return nullptr;
1772 : }
1773 :
1774 : /************************************************************************/
1775 : /* StartTransaction() */
1776 : /************************************************************************/
1777 :
1778 15 : OGRErr OGRHanaDataSource::StartTransaction(CPL_UNUSED int bForce)
1779 : {
1780 15 : if (isTransactionStarted_)
1781 : {
1782 2 : CPLError(CE_Failure, CPLE_AppDefined,
1783 : "Transaction already established");
1784 2 : return OGRERR_FAILURE;
1785 : }
1786 :
1787 13 : isTransactionStarted_ = true;
1788 13 : return OGRERR_NONE;
1789 : }
1790 :
1791 : /************************************************************************/
1792 : /* CommitTransaction() */
1793 : /************************************************************************/
1794 :
1795 10 : OGRErr OGRHanaDataSource::CommitTransaction()
1796 : {
1797 10 : if (!isTransactionStarted_)
1798 : {
1799 2 : CPLError(CE_Failure, CPLE_AppDefined, "Transaction not established");
1800 2 : return OGRERR_FAILURE;
1801 : }
1802 :
1803 8 : isTransactionStarted_ = false;
1804 :
1805 : try
1806 : {
1807 127 : for (size_t i = 0; i < layers_.size(); ++i)
1808 : {
1809 119 : OGRHanaLayer *layer = static_cast<OGRHanaLayer *>(layers_[i].get());
1810 119 : if (layer->IsTableLayer())
1811 : {
1812 119 : OGRHanaTableLayer *tableLayer =
1813 : static_cast<OGRHanaTableLayer *>(layer);
1814 119 : tableLayer->FlushPendingBatches(false);
1815 : }
1816 : }
1817 :
1818 8 : conn_->commit();
1819 : }
1820 0 : catch (const odbc::Exception &ex)
1821 : {
1822 0 : CPLError(CE_Failure, CPLE_AppDefined,
1823 0 : "Failed to commit transaction: %s", ex.what());
1824 0 : return OGRERR_FAILURE;
1825 : }
1826 8 : return OGRERR_NONE;
1827 : }
1828 :
1829 : /************************************************************************/
1830 : /* RollbackTransaction() */
1831 : /************************************************************************/
1832 :
1833 6 : OGRErr OGRHanaDataSource::RollbackTransaction()
1834 : {
1835 6 : if (!isTransactionStarted_)
1836 : {
1837 2 : CPLError(CE_Failure, CPLE_AppDefined, "Transaction not established");
1838 2 : return OGRERR_FAILURE;
1839 : }
1840 :
1841 4 : isTransactionStarted_ = false;
1842 :
1843 : try
1844 : {
1845 4 : conn_->rollback();
1846 : }
1847 0 : catch (const odbc::Exception &ex)
1848 : {
1849 0 : CPLError(CE_Failure, CPLE_AppDefined,
1850 0 : "Failed to roll back transaction: %s", ex.what());
1851 0 : return OGRERR_FAILURE;
1852 : }
1853 4 : return OGRERR_NONE;
1854 : }
|