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