Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: SAP HANA Spatial Driver
4 : * Purpose: OGRHanaTableLayer 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 "ogrhanafeaturereader.h"
15 : #include "ogrhanautils.h"
16 :
17 : #include <algorithm>
18 : #include <cmath>
19 : #include <cstdio>
20 : #include <cstring>
21 : #include <limits>
22 : #include <sstream>
23 :
24 : #include "odbc/Exception.h"
25 : #include "odbc/PreparedStatement.h"
26 : #include "odbc/ResultSet.h"
27 : #include "odbc/Statement.h"
28 : #include "odbc/Types.h"
29 :
30 : namespace OGRHANA
31 : {
32 : namespace
33 : {
34 :
35 : constexpr const char *UNSUPPORTED_OP_READ_ONLY =
36 : "%s : unsupported operation on a read-only datasource.";
37 :
38 12 : const char *GetColumnDefaultValue(const OGRFieldDefn &field)
39 : {
40 12 : const char *defaultValue = field.GetDefault();
41 12 : if (field.GetType() == OFTInteger && field.GetSubType() == OFSTBoolean)
42 0 : return (EQUAL(defaultValue, "1") || EQUAL(defaultValue, "'t'"))
43 1 : ? "TRUE"
44 1 : : "FALSE";
45 11 : return defaultValue;
46 : }
47 :
48 1 : CPLString FindGeomFieldName(const OGRFeatureDefn &featureDefn)
49 : {
50 1 : if (featureDefn.GetGeomFieldCount() == 0)
51 0 : return "OGR_GEOMETRY";
52 :
53 1 : int numGeomFields = featureDefn.GetGeomFieldCount();
54 1 : for (int i = 1; i <= 2 * numGeomFields; ++i)
55 : {
56 1 : CPLString name = CPLSPrintf("OGR_GEOMETRY_%d", i);
57 1 : if (featureDefn.GetGeomFieldIndex(name) < 0)
58 1 : return name;
59 : }
60 :
61 0 : return "OGR_GEOMETRY";
62 : }
63 :
64 96 : CPLString GetParameterValue(short type, const CPLString &typeName, bool isArray)
65 : {
66 96 : if (isArray)
67 : {
68 4 : CPLString arrayType = "STRING";
69 4 : switch (type)
70 : {
71 0 : case QGRHanaDataTypes::TinyInt:
72 0 : arrayType = "TINYINT";
73 0 : break;
74 0 : case QGRHanaDataTypes::SmallInt:
75 0 : arrayType = "SMALLINT";
76 0 : break;
77 1 : case QGRHanaDataTypes::Integer:
78 1 : arrayType = "INT";
79 1 : break;
80 1 : case QGRHanaDataTypes::BigInt:
81 1 : arrayType = "BIGINT";
82 1 : break;
83 0 : case QGRHanaDataTypes::Float:
84 : case QGRHanaDataTypes::Real:
85 0 : arrayType = "REAL";
86 0 : break;
87 1 : case QGRHanaDataTypes::Double:
88 1 : arrayType = "DOUBLE";
89 1 : break;
90 1 : case QGRHanaDataTypes::WVarChar:
91 1 : arrayType = "STRING";
92 1 : break;
93 : }
94 8 : return "ARRAY(SELECT * FROM OGR_PARSE_" + arrayType + "_ARRAY(?, '" +
95 8 : ARRAY_VALUES_DELIMITER + "'))";
96 : }
97 92 : else if (typeName.compare("NCLOB") == 0)
98 0 : return "TO_NCLOB(?)";
99 92 : else if (typeName.compare("CLOB") == 0)
100 0 : return "TO_CLOB(?)";
101 92 : else if (typeName.compare("BLOB") == 0)
102 0 : return "TO_BLOB(?)";
103 : else
104 92 : return "?";
105 : }
106 :
107 2 : std::vector<int> ParseIntValues(const std::string &str)
108 : {
109 2 : std::vector<int> values;
110 : try
111 : {
112 4 : std::stringstream stream(str);
113 6 : while (stream.good())
114 : {
115 4 : std::string value;
116 4 : std::getline(stream, value, ',');
117 4 : values.push_back(std::stoi(value.c_str()));
118 : }
119 : }
120 0 : catch (...)
121 : {
122 0 : values.clear();
123 : }
124 2 : return values;
125 : }
126 :
127 3 : ColumnTypeInfo ParseColumnTypeInfo(const CPLString &typeDef)
128 : {
129 0 : auto incorrectFormatErr = [&]()
130 : {
131 0 : CPLError(CE_Failure, CPLE_NotSupported,
132 0 : "Column type '%s' has incorrect format.", typeDef.c_str());
133 3 : };
134 :
135 6 : CPLString typeName;
136 6 : std::vector<int> typeSize;
137 :
138 3 : const size_t posStart = typeDef.find("(");
139 3 : if (posStart == std::string::npos)
140 : {
141 1 : typeName = typeDef;
142 : }
143 : else
144 : {
145 2 : const size_t posEnd = typeDef.rfind(")");
146 2 : if (posEnd != std::string::npos && posEnd > posStart)
147 : {
148 2 : const size_t posLast = typeDef.find_last_not_of(" \n\r\t");
149 2 : if (posLast == posEnd)
150 : {
151 2 : typeName = typeDef.substr(0, posStart);
152 : const std::string values =
153 2 : typeDef.substr(posStart + 1, posEnd - posStart - 1);
154 2 : typeSize = ParseIntValues(values);
155 : }
156 : }
157 :
158 2 : if (typeSize.empty() || typeSize.size() > 2)
159 : {
160 0 : incorrectFormatErr();
161 0 : return {"", QGRHanaDataTypes::Unknown, 0, 0};
162 : }
163 : }
164 :
165 3 : typeName.Trim();
166 :
167 3 : if (EQUAL(typeName.c_str(), "BOOLEAN"))
168 0 : return {std::move(typeName), QGRHanaDataTypes::Boolean, 0, 0};
169 3 : else if (EQUAL(typeName.c_str(), "TINYINT"))
170 0 : return {std::move(typeName), QGRHanaDataTypes::TinyInt, 0, 0};
171 3 : else if (EQUAL(typeName.c_str(), "SMALLINT"))
172 1 : return {std::move(typeName), QGRHanaDataTypes::SmallInt, 0, 0};
173 2 : else if (EQUAL(typeName.c_str(), "INTEGER"))
174 0 : return {std::move(typeName), QGRHanaDataTypes::Integer, 0, 0};
175 2 : else if (EQUAL(typeName.c_str(), "DECIMAL"))
176 : {
177 2 : switch (typeSize.size())
178 : {
179 0 : case 0:
180 0 : return {std::move(typeName), QGRHanaDataTypes::Decimal, 0, 0};
181 0 : case 1:
182 0 : return {std::move(typeName), QGRHanaDataTypes::Decimal,
183 0 : typeSize[0], 0};
184 2 : case 2:
185 2 : return {std::move(typeName), QGRHanaDataTypes::Decimal,
186 2 : typeSize[0], typeSize[1]};
187 : }
188 : }
189 0 : else if (EQUAL(typeName.c_str(), "FLOAT"))
190 : {
191 0 : switch (typeSize.size())
192 : {
193 0 : case 0:
194 0 : return {std::move(typeName), QGRHanaDataTypes::Float, 10, 0};
195 0 : case 1:
196 0 : return {std::move(typeName), QGRHanaDataTypes::Float,
197 0 : typeSize[0], 0};
198 0 : default:
199 0 : incorrectFormatErr();
200 0 : return {"", QGRHanaDataTypes::Unknown, 0, 0};
201 : }
202 : }
203 0 : else if (EQUAL(typeName.c_str(), "REAL"))
204 0 : return {std::move(typeName), QGRHanaDataTypes::Real, 0, 0};
205 0 : else if (EQUAL(typeName.c_str(), "DOUBLE"))
206 0 : return {std::move(typeName), QGRHanaDataTypes::Double, 0, 0};
207 0 : else if (EQUAL(typeName.c_str(), "VARCHAR"))
208 : {
209 0 : switch (typeSize.size())
210 : {
211 0 : case 0:
212 0 : return {std::move(typeName), QGRHanaDataTypes::VarChar, 1, 0};
213 0 : case 1:
214 0 : return {std::move(typeName), QGRHanaDataTypes::VarChar,
215 0 : typeSize[0], 0};
216 0 : default:
217 0 : incorrectFormatErr();
218 0 : return {"", QGRHanaDataTypes::Unknown, 0, 0};
219 : }
220 : }
221 0 : else if (EQUAL(typeName.c_str(), "NVARCHAR"))
222 : {
223 0 : switch (typeSize.size())
224 : {
225 0 : case 0:
226 0 : return {std::move(typeName), QGRHanaDataTypes::WVarChar, 1, 0};
227 0 : case 1:
228 0 : return {std::move(typeName), QGRHanaDataTypes::WVarChar,
229 0 : typeSize[0], 0};
230 0 : case 2:
231 0 : incorrectFormatErr();
232 0 : return {"", QGRHanaDataTypes::Unknown, 0, 0};
233 : }
234 : }
235 0 : else if (EQUAL(typeName.c_str(), "NCLOB"))
236 0 : return {std::move(typeName), QGRHanaDataTypes::WLongVarChar, 0, 0};
237 0 : else if (EQUAL(typeName.c_str(), "DATE"))
238 0 : return {std::move(typeName), QGRHanaDataTypes::Date, 0, 0};
239 0 : else if (EQUAL(typeName.c_str(), "TIME"))
240 0 : return {std::move(typeName), QGRHanaDataTypes::Time, 0, 0};
241 0 : else if (EQUAL(typeName.c_str(), "TIMESTAMP"))
242 0 : return {std::move(typeName), QGRHanaDataTypes::Timestamp, 0, 0};
243 0 : else if (EQUAL(typeName.c_str(), "VARBINARY"))
244 : {
245 0 : switch (typeSize.size())
246 : {
247 0 : case 0:
248 0 : return {std::move(typeName), QGRHanaDataTypes::VarBinary, 1, 0};
249 0 : case 1:
250 0 : return {std::move(typeName), QGRHanaDataTypes::VarBinary,
251 0 : typeSize[0], 0};
252 0 : case 2:
253 0 : incorrectFormatErr();
254 0 : return {"", QGRHanaDataTypes::Unknown, 0, 0};
255 : }
256 : }
257 0 : else if (EQUAL(typeName.c_str(), "BLOB"))
258 0 : return {std::move(typeName), QGRHanaDataTypes::LongVarBinary, 0, 0};
259 0 : else if (EQUAL(typeName.c_str(), "REAL_VECTOR"))
260 : {
261 0 : switch (typeSize.size())
262 : {
263 0 : case 0:
264 0 : return {std::move(typeName), QGRHanaDataTypes::RealVector, 1,
265 0 : 0};
266 0 : case 1:
267 0 : return {std::move(typeName), QGRHanaDataTypes::RealVector,
268 0 : typeSize[0], 0};
269 0 : case 2:
270 0 : incorrectFormatErr();
271 0 : return {"", QGRHanaDataTypes::Unknown, 0, 0};
272 : }
273 : }
274 :
275 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unknown column type '%s'.",
276 : typeName.c_str());
277 0 : return {std::move(typeName), QGRHanaDataTypes::Unknown, 0, 0};
278 : }
279 :
280 65 : CPLString GetColumnDefinition(const ColumnTypeInfo &typeInfo)
281 : {
282 65 : bool isArray = std::strstr(typeInfo.name, "ARRAY") != nullptr;
283 :
284 65 : if (isArray)
285 : {
286 4 : switch (typeInfo.type)
287 : {
288 0 : case QGRHanaDataTypes::SmallInt:
289 0 : return "SMALLINT ARRAY";
290 1 : case QGRHanaDataTypes::Integer:
291 1 : return "INTEGER ARRAY";
292 1 : case QGRHanaDataTypes::BigInt:
293 1 : return "BIGINT ARRAY";
294 0 : case QGRHanaDataTypes::Real:
295 0 : return "REAL ARRAY";
296 1 : case QGRHanaDataTypes::Double:
297 1 : return "DOUBLE ARRAY";
298 1 : case QGRHanaDataTypes::WVarChar:
299 1 : return "NVARCHAR(512) ARRAY";
300 0 : default:
301 0 : return "UNKNOWN";
302 : }
303 : }
304 :
305 61 : switch (typeInfo.type)
306 : {
307 34 : case QGRHanaDataTypes::Boolean:
308 : case QGRHanaDataTypes::TinyInt:
309 : case QGRHanaDataTypes::SmallInt:
310 : case QGRHanaDataTypes::Integer:
311 : case QGRHanaDataTypes::BigInt:
312 : case QGRHanaDataTypes::Float:
313 : case QGRHanaDataTypes::Real:
314 : case QGRHanaDataTypes::Double:
315 : case QGRHanaDataTypes::Date:
316 : case QGRHanaDataTypes::TypeDate:
317 : case QGRHanaDataTypes::Time:
318 : case QGRHanaDataTypes::TypeTime:
319 : case QGRHanaDataTypes::Timestamp:
320 : case QGRHanaDataTypes::TypeTimestamp:
321 : case QGRHanaDataTypes::Char:
322 : case QGRHanaDataTypes::WChar:
323 : case QGRHanaDataTypes::LongVarChar:
324 : case QGRHanaDataTypes::LongVarBinary:
325 34 : return typeInfo.name;
326 3 : case QGRHanaDataTypes::Decimal:
327 : case QGRHanaDataTypes::Numeric:
328 6 : return CPLString().Printf("DECIMAL(%d,%d)", typeInfo.width,
329 3 : typeInfo.precision);
330 24 : case QGRHanaDataTypes::VarChar:
331 : case QGRHanaDataTypes::WVarChar:
332 : case QGRHanaDataTypes::Binary:
333 : case QGRHanaDataTypes::VarBinary:
334 : case QGRHanaDataTypes::WLongVarChar:
335 : case QGRHanaDataTypes::RealVector:
336 24 : return (typeInfo.width == 0)
337 : ? typeInfo.name
338 48 : : CPLString().Printf("%s(%d)", typeInfo.name.c_str(),
339 48 : typeInfo.width);
340 0 : default:
341 0 : return "UNKNOWN";
342 : }
343 : }
344 :
345 64 : void SetFieldDefn(OGRFieldDefn &field, const ColumnTypeInfo &typeInfo)
346 : {
347 58 : auto isArray = [&typeInfo]()
348 58 : { return std::strstr(typeInfo.name, "ARRAY") != nullptr; };
349 :
350 64 : switch (typeInfo.type)
351 : {
352 1 : case QGRHanaDataTypes::Bit:
353 : case QGRHanaDataTypes::Boolean:
354 1 : field.SetType(OFTInteger);
355 1 : field.SetSubType(OFSTBoolean);
356 1 : break;
357 2 : case QGRHanaDataTypes::TinyInt:
358 : case QGRHanaDataTypes::SmallInt:
359 2 : field.SetType(isArray() ? OFTIntegerList : OFTInteger);
360 2 : field.SetSubType(OFSTInt16);
361 2 : break;
362 4 : case QGRHanaDataTypes::Integer:
363 4 : field.SetType(isArray() ? OFTIntegerList : OFTInteger);
364 4 : break;
365 12 : case QGRHanaDataTypes::BigInt:
366 12 : field.SetType(isArray() ? OFTInteger64List : OFTInteger64);
367 12 : break;
368 13 : case QGRHanaDataTypes::Double:
369 : case QGRHanaDataTypes::Real:
370 : case QGRHanaDataTypes::Float:
371 13 : field.SetType(isArray() ? OFTRealList : OFTReal);
372 13 : if (typeInfo.type != QGRHanaDataTypes::Double)
373 1 : field.SetSubType(OFSTFloat32);
374 13 : break;
375 3 : case QGRHanaDataTypes::Decimal:
376 : case QGRHanaDataTypes::Numeric:
377 3 : field.SetType(isArray() ? OFTRealList : OFTReal);
378 3 : break;
379 0 : case QGRHanaDataTypes::Char:
380 : case QGRHanaDataTypes::VarChar:
381 : case QGRHanaDataTypes::LongVarChar:
382 0 : field.SetType(isArray() ? OFTStringList : OFTString);
383 0 : break;
384 24 : case QGRHanaDataTypes::WChar:
385 : case QGRHanaDataTypes::WVarChar:
386 : case QGRHanaDataTypes::WLongVarChar:
387 24 : field.SetType(isArray() ? OFTStringList : OFTString);
388 24 : break;
389 1 : case QGRHanaDataTypes::Date:
390 : case QGRHanaDataTypes::TypeDate:
391 1 : field.SetType(OFTDate);
392 1 : break;
393 1 : case QGRHanaDataTypes::Time:
394 : case QGRHanaDataTypes::TypeTime:
395 1 : field.SetType(OFTTime);
396 1 : break;
397 2 : case QGRHanaDataTypes::Timestamp:
398 : case QGRHanaDataTypes::TypeTimestamp:
399 2 : field.SetType(OFTDateTime);
400 2 : break;
401 1 : case QGRHanaDataTypes::Binary:
402 : case QGRHanaDataTypes::VarBinary:
403 : case QGRHanaDataTypes::LongVarBinary:
404 : case QGRHanaDataTypes::RealVector:
405 1 : field.SetType(OFTBinary);
406 1 : break;
407 0 : default:
408 0 : break;
409 : }
410 :
411 64 : field.SetWidth(typeInfo.width);
412 64 : field.SetPrecision(typeInfo.precision);
413 64 : }
414 :
415 : } // anonymous namespace
416 :
417 : /************************************************************************/
418 : /* OGRHanaTableLayer() */
419 : /************************************************************************/
420 :
421 485 : OGRHanaTableLayer::OGRHanaTableLayer(OGRHanaDataSource *datasource,
422 : const char *schemaName,
423 485 : const char *tableName, int update)
424 : : OGRHanaLayer(datasource), schemaName_(schemaName), tableName_(tableName),
425 485 : updateMode_(update)
426 : {
427 : rawQuery_ =
428 485 : "SELECT * FROM " + GetFullTableNameQuoted(schemaName_, tableName_);
429 485 : SetDescription(tableName_.c_str());
430 485 : }
431 :
432 : /************************************************************************/
433 : /* ~OGRHanaTableLayer() */
434 : /************************************************************************/
435 :
436 970 : OGRHanaTableLayer::~OGRHanaTableLayer()
437 : {
438 485 : FlushPendingBatches(true);
439 970 : }
440 :
441 : /* -------------------------------------------------------------------- */
442 : /* Initialize() */
443 : /* -------------------------------------------------------------------- */
444 :
445 42 : OGRErr OGRHanaTableLayer::Initialize()
446 : {
447 42 : if (initialized_)
448 0 : return OGRERR_NONE;
449 :
450 : OGRErr err =
451 42 : InitFeatureDefinition(schemaName_, tableName_, rawQuery_, tableName_);
452 42 : if (err != OGRERR_NONE)
453 0 : return err;
454 :
455 42 : if (fidFieldIndex_ != OGRNullFID)
456 : {
457 38 : CPLDebug("HANA", "table %s has FID column %s.", tableName_.c_str(),
458 : fidFieldName_.c_str());
459 :
460 38 : CPLString sql = CPLString().Printf(
461 : "SELECT COUNT(*) FROM %s",
462 114 : GetFullTableNameQuoted(schemaName_, tableName_).c_str());
463 76 : odbc::StatementRef stmt = dataSource_->CreateStatement();
464 76 : odbc::ResultSetRef rs = stmt->executeQuery(sql.c_str());
465 38 : allowAutoFIDOnCreateFeature_ =
466 38 : rs->next() ? (*rs->getLong(1) == 0) : false;
467 38 : rs->close();
468 : }
469 : else
470 : {
471 4 : CPLDebug("HANA",
472 : "table %s has no FID column, FIDs will not be reliable!",
473 : tableName_.c_str());
474 :
475 4 : allowAutoFIDOnCreateFeature_ = true;
476 : }
477 :
478 42 : return OGRERR_NONE;
479 : }
480 :
481 : /* -------------------------------------------------------------------- */
482 : /* ExecuteUpdate() */
483 : /* -------------------------------------------------------------------- */
484 :
485 : std::pair<OGRErr, std::size_t>
486 144 : OGRHanaTableLayer::ExecuteUpdate(odbc::PreparedStatement &statement,
487 : bool withBatch, const char *functionName)
488 : {
489 144 : std::size_t ret = 0;
490 :
491 : try
492 : {
493 144 : if (withBatch)
494 : {
495 8 : if (statement.getBatchDataSize() >= batchSize_)
496 0 : statement.executeBatch();
497 8 : ret = 1;
498 : }
499 : else
500 : {
501 136 : ret = statement.executeUpdate();
502 : }
503 :
504 144 : if (!dataSource_->IsTransactionStarted())
505 134 : dataSource_->Commit();
506 : }
507 0 : catch (odbc::Exception &ex)
508 : {
509 0 : CPLError(CE_Failure, CPLE_AppDefined, "Failed to execute %s: %s",
510 0 : functionName, ex.what());
511 0 : return {OGRERR_FAILURE, 0};
512 : }
513 :
514 144 : return {OGRERR_NONE, ret};
515 : }
516 :
517 : /* -------------------------------------------------------------------- */
518 : /* CreateDeleteFeatureStatement() */
519 : /* -------------------------------------------------------------------- */
520 :
521 3 : odbc::PreparedStatementRef OGRHanaTableLayer::CreateDeleteFeatureStatement()
522 : {
523 3 : CPLString sql = CPLString().Printf(
524 : "DELETE FROM %s WHERE %s = ?",
525 6 : GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
526 12 : QuotedIdentifier(GetFIDColumn()).c_str());
527 6 : return dataSource_->PrepareStatement(sql.c_str());
528 : }
529 :
530 : /* -------------------------------------------------------------------- */
531 : /* CreateInsertFeatureStatement() */
532 : /* -------------------------------------------------------------------- */
533 :
534 : odbc::PreparedStatementRef
535 16 : OGRHanaTableLayer::CreateInsertFeatureStatement(bool withFID)
536 : {
537 32 : std::vector<CPLString> columns;
538 32 : std::vector<CPLString> values;
539 16 : bool hasArray = false;
540 103 : for (const AttributeColumnDescription &clmDesc : attrColumns_)
541 : {
542 87 : if (clmDesc.isFeatureID && !withFID)
543 : {
544 2 : if (clmDesc.isAutoIncrement)
545 2 : continue;
546 : }
547 :
548 85 : columns.push_back(QuotedIdentifier(clmDesc.name));
549 85 : values.push_back(
550 170 : GetParameterValue(clmDesc.type, clmDesc.typeName, clmDesc.isArray));
551 85 : if (clmDesc.isArray)
552 4 : hasArray = true;
553 : }
554 :
555 31 : for (const GeometryColumnDescription &geomClmDesc : geomColumns_)
556 : {
557 15 : columns.push_back(QuotedIdentifier(geomClmDesc.name));
558 30 : values.push_back("ST_GeomFromWKB(? , " +
559 45 : std::to_string(geomClmDesc.srid) + ")");
560 : }
561 :
562 16 : if (hasArray && !parseFunctionsChecked_)
563 : {
564 : // Create helper functions if needed.
565 1 : if (!dataSource_->ParseArrayFunctionsExist(schemaName_.c_str()))
566 1 : dataSource_->CreateParseArrayFunctions(schemaName_.c_str());
567 1 : parseFunctionsChecked_ = true;
568 : }
569 :
570 16 : const CPLString sql = CPLString().Printf(
571 : "INSERT INTO %s (%s) VALUES(%s)",
572 32 : GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
573 64 : JoinStrings(columns, ", ").c_str(), JoinStrings(values, ", ").c_str());
574 :
575 32 : return dataSource_->PrepareStatement(sql.c_str());
576 : }
577 :
578 : /* -------------------------------------------------------------------- */
579 : /* CreateUpdateFeatureStatement() */
580 : /* -------------------------------------------------------------------- */
581 :
582 4 : odbc::PreparedStatementRef OGRHanaTableLayer::CreateUpdateFeatureStatement()
583 : {
584 8 : std::vector<CPLString> values;
585 4 : values.reserve(attrColumns_.size());
586 4 : bool hasArray = false;
587 :
588 18 : for (const AttributeColumnDescription &clmDesc : attrColumns_)
589 : {
590 14 : if (clmDesc.isFeatureID)
591 : {
592 4 : if (clmDesc.isAutoIncrement)
593 3 : continue;
594 : }
595 11 : values.push_back(
596 22 : QuotedIdentifier(clmDesc.name) + " = " +
597 22 : GetParameterValue(clmDesc.type, clmDesc.typeName, clmDesc.isArray));
598 11 : if (clmDesc.isArray)
599 0 : hasArray = true;
600 : }
601 :
602 7 : for (const GeometryColumnDescription &geomClmDesc : geomColumns_)
603 : {
604 9 : values.push_back(QuotedIdentifier(geomClmDesc.name) + " = " +
605 6 : "ST_GeomFromWKB(?, " +
606 12 : std::to_string(geomClmDesc.srid) + ")");
607 : }
608 :
609 4 : if (hasArray && !parseFunctionsChecked_)
610 : {
611 : // Create helper functions if needed.
612 0 : if (!dataSource_->ParseArrayFunctionsExist(schemaName_.c_str()))
613 0 : dataSource_->CreateParseArrayFunctions(schemaName_.c_str());
614 0 : parseFunctionsChecked_ = true;
615 : }
616 :
617 4 : const CPLString sql = CPLString().Printf(
618 : "UPDATE %s SET %s WHERE %s = ?",
619 8 : GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
620 8 : JoinStrings(values, ", ").c_str(),
621 20 : QuotedIdentifier(GetFIDColumn()).c_str());
622 :
623 8 : return dataSource_->PrepareStatement(sql.c_str());
624 : }
625 :
626 : /* -------------------------------------------------------------------- */
627 : /* ResetPreparedStatements() */
628 : /* -------------------------------------------------------------------- */
629 :
630 65 : void OGRHanaTableLayer::ResetPreparedStatements()
631 : {
632 65 : if (!currentIdentityValueStmt_.isNull())
633 0 : currentIdentityValueStmt_ = nullptr;
634 65 : if (!insertFeatureStmtWithFID_.isNull())
635 1 : insertFeatureStmtWithFID_ = nullptr;
636 65 : if (!insertFeatureStmtWithoutFID_.isNull())
637 0 : insertFeatureStmtWithoutFID_ = nullptr;
638 65 : if (!deleteFeatureStmt_.isNull())
639 0 : deleteFeatureStmt_ = nullptr;
640 65 : if (!updateFeatureStmt_.isNull())
641 0 : updateFeatureStmt_ = nullptr;
642 65 : }
643 :
644 : /************************************************************************/
645 : /* SetStatementParameters() */
646 : /************************************************************************/
647 :
648 136 : OGRErr OGRHanaTableLayer::SetStatementParameters(
649 : odbc::PreparedStatement &statement, OGRFeature *feature, bool newFeature,
650 : bool withFID, const char *functionName)
651 : {
652 136 : OGRHanaFeatureReader featReader(*feature);
653 :
654 136 : unsigned short paramIndex = 0;
655 136 : int fieldIndex = -1;
656 799 : for (const AttributeColumnDescription &clmDesc : attrColumns_)
657 : {
658 663 : if (clmDesc.isFeatureID)
659 : {
660 136 : if (!withFID && clmDesc.isAutoIncrement)
661 20 : continue;
662 :
663 116 : ++paramIndex;
664 :
665 116 : switch (clmDesc.type)
666 : {
667 116 : case QGRHanaDataTypes::Integer:
668 116 : if (feature->GetFID() == OGRNullFID)
669 0 : statement.setInt(paramIndex, odbc::Int());
670 : else
671 : {
672 116 : if (std::numeric_limits<std::int32_t>::min() >
673 232 : feature->GetFID() ||
674 116 : std::numeric_limits<std::int32_t>::max() <
675 116 : feature->GetFID())
676 : {
677 0 : CPLError(CE_Failure, CPLE_AppDefined,
678 : "%s: Feature id with value %s cannot "
679 : "be stored in a column of type INTEGER",
680 : functionName,
681 0 : std::to_string(feature->GetFID()).c_str());
682 0 : return OGRERR_FAILURE;
683 : }
684 :
685 116 : statement.setInt(paramIndex,
686 232 : odbc::Int(static_cast<std::int32_t>(
687 116 : feature->GetFID())));
688 : }
689 116 : break;
690 0 : case QGRHanaDataTypes::BigInt:
691 0 : if (feature->GetFID() == OGRNullFID)
692 0 : statement.setLong(paramIndex, odbc::Long());
693 : else
694 0 : statement.setLong(paramIndex,
695 0 : odbc::Long(static_cast<std::int64_t>(
696 0 : feature->GetFID())));
697 0 : break;
698 0 : default:
699 0 : CPLError(CE_Failure, CPLE_AppDefined,
700 : "%s: Unexpected type ('%s') in the field "
701 : "'%s'",
702 0 : functionName, std::to_string(clmDesc.type).c_str(),
703 : clmDesc.name.c_str());
704 0 : return OGRERR_FAILURE;
705 : }
706 116 : continue;
707 : }
708 : else
709 527 : ++paramIndex;
710 :
711 527 : ++fieldIndex;
712 :
713 527 : switch (clmDesc.type)
714 : {
715 1 : case QGRHanaDataTypes::Bit:
716 : case QGRHanaDataTypes::Boolean:
717 1 : statement.setBoolean(paramIndex,
718 1 : featReader.GetFieldAsBoolean(fieldIndex));
719 1 : break;
720 0 : case QGRHanaDataTypes::TinyInt:
721 0 : if (clmDesc.isArray)
722 0 : statement.setString(
723 0 : paramIndex, featReader.GetFieldAsIntArray(fieldIndex));
724 : else
725 0 : statement.setByte(paramIndex,
726 0 : featReader.GetFieldAsByte(fieldIndex));
727 0 : break;
728 1 : case QGRHanaDataTypes::SmallInt:
729 1 : if (clmDesc.isArray)
730 0 : statement.setString(
731 0 : paramIndex, featReader.GetFieldAsIntArray(fieldIndex));
732 : else
733 1 : statement.setShort(paramIndex,
734 2 : featReader.GetFieldAsShort(fieldIndex));
735 1 : break;
736 3 : case QGRHanaDataTypes::Integer:
737 3 : if (clmDesc.isArray)
738 1 : statement.setString(
739 2 : paramIndex, featReader.GetFieldAsIntArray(fieldIndex));
740 : else
741 2 : statement.setInt(paramIndex,
742 4 : featReader.GetFieldAsInt(fieldIndex));
743 3 : break;
744 128 : case QGRHanaDataTypes::BigInt:
745 128 : if (clmDesc.isArray)
746 1 : statement.setString(
747 : paramIndex,
748 2 : featReader.GetFieldAsBigIntArray(fieldIndex));
749 : else
750 127 : statement.setLong(paramIndex,
751 254 : featReader.GetFieldAsLong(fieldIndex));
752 128 : break;
753 1 : case QGRHanaDataTypes::Float:
754 : case QGRHanaDataTypes::Real:
755 1 : if (clmDesc.isArray)
756 0 : statement.setString(
757 0 : paramIndex, featReader.GetFieldAsRealArray(fieldIndex));
758 : else
759 1 : statement.setFloat(paramIndex,
760 2 : featReader.GetFieldAsFloat(fieldIndex));
761 1 : break;
762 128 : case QGRHanaDataTypes::Double:
763 128 : if (clmDesc.isArray)
764 1 : statement.setString(
765 : paramIndex,
766 2 : featReader.GetFieldAsDoubleArray(fieldIndex));
767 : else
768 127 : statement.setDouble(
769 254 : paramIndex, featReader.GetFieldAsDouble(fieldIndex));
770 128 : break;
771 1 : case QGRHanaDataTypes::Decimal:
772 : case QGRHanaDataTypes::Numeric:
773 1 : if ((!feature->IsFieldSet(fieldIndex) ||
774 2 : feature->IsFieldNull(fieldIndex)) &&
775 1 : feature->GetFieldDefnRef(fieldIndex)->GetDefault() ==
776 : nullptr)
777 0 : statement.setDecimal(paramIndex, odbc::Decimal());
778 : else
779 1 : statement.setDouble(
780 2 : paramIndex, featReader.GetFieldAsDouble(fieldIndex));
781 1 : break;
782 0 : case QGRHanaDataTypes::Char:
783 : case QGRHanaDataTypes::VarChar:
784 : case QGRHanaDataTypes::LongVarChar:
785 0 : if (clmDesc.isArray)
786 0 : statement.setString(
787 : paramIndex,
788 0 : featReader.GetFieldAsStringArray(fieldIndex));
789 : else
790 0 : statement.setString(paramIndex,
791 0 : featReader.GetFieldAsString(
792 0 : fieldIndex, clmDesc.length));
793 0 : break;
794 255 : case QGRHanaDataTypes::WChar:
795 : case QGRHanaDataTypes::WVarChar:
796 : case QGRHanaDataTypes::WLongVarChar:
797 255 : if (clmDesc.isArray)
798 1 : statement.setString(
799 : paramIndex,
800 2 : featReader.GetFieldAsStringArray(fieldIndex));
801 : else
802 254 : statement.setString(paramIndex,
803 254 : featReader.GetFieldAsNString(
804 254 : fieldIndex, clmDesc.length));
805 255 : break;
806 3 : case QGRHanaDataTypes::Binary:
807 : case QGRHanaDataTypes::VarBinary:
808 : case QGRHanaDataTypes::LongVarBinary:
809 : case QGRHanaDataTypes::RealVector:
810 : {
811 3 : Binary bin = featReader.GetFieldAsBinary(fieldIndex);
812 3 : statement.setBytes(paramIndex, bin.data, bin.size);
813 : }
814 3 : break;
815 1 : case QGRHanaDataTypes::DateTime:
816 : case QGRHanaDataTypes::TypeDate:
817 1 : statement.setDate(paramIndex,
818 1 : featReader.GetFieldAsDate(fieldIndex));
819 1 : break;
820 1 : case QGRHanaDataTypes::Time:
821 : case QGRHanaDataTypes::TypeTime:
822 1 : statement.setTime(paramIndex,
823 1 : featReader.GetFieldAsTime(fieldIndex));
824 1 : break;
825 4 : case QGRHanaDataTypes::Timestamp:
826 : case QGRHanaDataTypes::TypeTimestamp:
827 4 : statement.setTimestamp(
828 4 : paramIndex, featReader.GetFieldAsTimestamp(fieldIndex));
829 4 : break;
830 : }
831 : }
832 :
833 268 : for (std::size_t i = 0; i < geomColumns_.size(); ++i)
834 : {
835 132 : ++paramIndex;
836 132 : Binary wkb{nullptr, 0};
837 132 : OGRErr err = GetGeometryWkb(feature, static_cast<int>(i), wkb);
838 132 : if (OGRERR_NONE != err)
839 0 : return err;
840 132 : statement.setBytes(paramIndex, wkb.data, wkb.size);
841 : }
842 :
843 136 : if (!newFeature)
844 : {
845 13 : ++paramIndex;
846 :
847 13 : statement.setLong(paramIndex, odbc::Long(static_cast<std::int64_t>(
848 13 : feature->GetFID())));
849 : }
850 :
851 136 : return OGRERR_NONE;
852 : }
853 :
854 : /* -------------------------------------------------------------------- */
855 : /* DropTable() */
856 : /* -------------------------------------------------------------------- */
857 :
858 0 : OGRErr OGRHanaTableLayer::DropTable()
859 : {
860 : CPLString sql =
861 0 : "DROP TABLE " + GetFullTableNameQuoted(schemaName_, tableName_);
862 : try
863 : {
864 0 : dataSource_->ExecuteSQL(sql.c_str());
865 0 : CPLDebug("HANA", "Dropped table %s.", GetName());
866 : }
867 0 : catch (const odbc::Exception &ex)
868 : {
869 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unable to delete layer '%s': %s",
870 0 : tableName_.c_str(), ex.what());
871 0 : return OGRERR_FAILURE;
872 : }
873 :
874 0 : return OGRERR_NONE;
875 : }
876 :
877 : /* -------------------------------------------------------------------- */
878 : /* ExecutePendingBatches() */
879 : /* -------------------------------------------------------------------- */
880 :
881 148 : OGRErr OGRHanaTableLayer::ExecutePendingBatches(BatchOperation op)
882 : {
883 8 : auto hasFlag = [op](BatchOperation flag) { return (op & flag) == flag; };
884 :
885 : try
886 : {
887 148 : if (!deleteFeatureStmt_.isNull() &&
888 150 : deleteFeatureStmt_->getBatchDataSize() > 0 &&
889 2 : hasFlag(BatchOperation::DELETE))
890 2 : deleteFeatureStmt_->executeBatch();
891 148 : if (!insertFeatureStmtWithFID_.isNull() &&
892 153 : insertFeatureStmtWithFID_->getBatchDataSize() > 0 &&
893 5 : hasFlag(BatchOperation::INSERT))
894 3 : insertFeatureStmtWithFID_->executeBatch();
895 148 : if (!insertFeatureStmtWithoutFID_.isNull() &&
896 148 : insertFeatureStmtWithoutFID_->getBatchDataSize() > 0 &&
897 0 : hasFlag(BatchOperation::INSERT))
898 0 : insertFeatureStmtWithoutFID_->executeBatch();
899 148 : if (!updateFeatureStmt_.isNull() &&
900 149 : updateFeatureStmt_->getBatchDataSize() > 0 &&
901 1 : hasFlag(BatchOperation::UPDATE))
902 1 : updateFeatureStmt_->executeBatch();
903 :
904 148 : return OGRERR_NONE;
905 : }
906 0 : catch (const odbc::Exception &ex)
907 : {
908 0 : ClearBatches();
909 :
910 0 : CPLError(CE_Failure, CPLE_AppDefined,
911 0 : "Failed to execute batch commands: %s", ex.what());
912 0 : return OGRERR_FAILURE;
913 : }
914 : }
915 :
916 : /* -------------------------------------------------------------------- */
917 : /* FlushPendingBatches() */
918 : /* -------------------------------------------------------------------- */
919 :
920 943 : void OGRHanaTableLayer::FlushPendingBatches(bool commit)
921 : {
922 943 : if (!HasPendingBatches())
923 939 : return;
924 :
925 4 : OGRErr err = ExecutePendingBatches(BatchOperation::ALL);
926 4 : if (OGRERR_NONE == err && commit && !dataSource_->IsTransactionStarted())
927 0 : dataSource_->Commit();
928 : }
929 :
930 : /* -------------------------------------------------------------------- */
931 : /* HasPendingBatches() */
932 : /* -------------------------------------------------------------------- */
933 :
934 950 : bool OGRHanaTableLayer::HasPendingBatches() const
935 : {
936 950 : return (!deleteFeatureStmt_.isNull() &&
937 73 : deleteFeatureStmt_->getBatchDataSize() > 0) ||
938 949 : (!insertFeatureStmtWithFID_.isNull() &&
939 123 : insertFeatureStmtWithFID_->getBatchDataSize() > 0) ||
940 947 : (!insertFeatureStmtWithoutFID_.isNull() &&
941 1927 : insertFeatureStmtWithoutFID_->getBatchDataSize() > 0) ||
942 947 : (!updateFeatureStmt_.isNull() &&
943 1021 : updateFeatureStmt_->getBatchDataSize() > 0);
944 : }
945 :
946 : /* -------------------------------------------------------------------- */
947 : /* GetColumnTypeInfo() */
948 : /* -------------------------------------------------------------------- */
949 :
950 : ColumnTypeInfo
951 65 : OGRHanaTableLayer::GetColumnTypeInfo(const OGRFieldDefn &field) const
952 : {
953 68 : for (const auto &clmType : customColumnDefs_)
954 : {
955 6 : if (EQUAL(clmType.name.c_str(), field.GetNameRef()))
956 3 : return ParseColumnTypeInfo(clmType.typeDef);
957 : }
958 :
959 62 : switch (field.GetType())
960 : {
961 6 : case OFTInteger:
962 6 : if (preservePrecision_ && field.GetWidth() > 10)
963 : {
964 0 : return {"DECIMAL", QGRHanaDataTypes::Decimal, field.GetWidth(),
965 0 : 0};
966 : }
967 : else
968 : {
969 6 : if (field.GetSubType() == OFSTBoolean)
970 : return {"BOOLEAN", QGRHanaDataTypes::Boolean,
971 1 : field.GetWidth(), 0};
972 5 : else if (field.GetSubType() == OFSTInt16)
973 : return {"SMALLINT", QGRHanaDataTypes::SmallInt,
974 1 : field.GetWidth(), 0};
975 : else
976 : return {"INTEGER", QGRHanaDataTypes::Integer,
977 4 : field.GetWidth(), 0};
978 : }
979 : break;
980 11 : case OFTInteger64:
981 11 : if (preservePrecision_ && field.GetWidth() > 20)
982 : {
983 0 : return {"DECIMAL", QGRHanaDataTypes::Decimal, field.GetWidth(),
984 0 : 0};
985 : }
986 : else
987 11 : return {"BIGINT", QGRHanaDataTypes::BigInt, field.GetWidth(),
988 11 : 0};
989 : break;
990 13 : case OFTReal:
991 13 : if (preservePrecision_ && field.GetWidth() != 0)
992 : {
993 1 : return {"DECIMAL", QGRHanaDataTypes::Decimal, field.GetWidth(),
994 1 : field.GetPrecision()};
995 : }
996 : else
997 : {
998 12 : if (field.GetSubType() == OFSTFloat32)
999 1 : return {"REAL", QGRHanaDataTypes::Real, field.GetWidth(),
1000 1 : field.GetPrecision()};
1001 : else
1002 : return {"DOUBLE", QGRHanaDataTypes::Double,
1003 11 : field.GetWidth(), field.GetPrecision()};
1004 : }
1005 23 : case OFTString:
1006 23 : if (field.GetWidth() == 0 || !preservePrecision_)
1007 : {
1008 13 : int width = static_cast<int>(defaultStringSize_);
1009 13 : return {"NVARCHAR", QGRHanaDataTypes::WLongVarChar, width, 0};
1010 : }
1011 : else
1012 : {
1013 10 : if (field.GetWidth() >= 1 && field.GetWidth() <= 5000)
1014 : return {"NVARCHAR", QGRHanaDataTypes::WLongVarChar,
1015 10 : field.GetWidth(), 0};
1016 : else
1017 0 : return {"NCLOB", QGRHanaDataTypes::WLongVarChar, 0, 0};
1018 : }
1019 1 : case OFTBinary:
1020 1 : if (field.GetWidth() >= 1 && field.GetWidth() <= 5000)
1021 : return {"VARBINARY", QGRHanaDataTypes::VarBinary,
1022 1 : field.GetWidth(), 0};
1023 : else
1024 : return {"BLOB", QGRHanaDataTypes::LongVarBinary,
1025 0 : field.GetWidth(), 0};
1026 1 : case OFTDate:
1027 1 : return {"DATE", QGRHanaDataTypes::TypeDate, field.GetWidth(), 0};
1028 1 : case OFTTime:
1029 1 : return {"TIME", QGRHanaDataTypes::TypeTime, field.GetWidth(), 0};
1030 2 : case OFTDateTime:
1031 : return {"TIMESTAMP", QGRHanaDataTypes::TypeTimestamp,
1032 2 : field.GetWidth(), 0};
1033 1 : case OFTIntegerList:
1034 1 : if (field.GetSubType() == OGRFieldSubType::OFSTInt16)
1035 0 : return {"ARRAY", QGRHanaDataTypes::SmallInt, field.GetWidth(),
1036 0 : 0};
1037 : else
1038 1 : return {"ARRAY", QGRHanaDataTypes::Integer, field.GetWidth(),
1039 1 : 0};
1040 1 : case OFTInteger64List:
1041 1 : return {"ARRAY", QGRHanaDataTypes::BigInt, field.GetWidth(), 0};
1042 1 : case OFTRealList:
1043 1 : if (field.GetSubType() == OGRFieldSubType::OFSTFloat32)
1044 0 : return {"ARRAY", QGRHanaDataTypes::Real, field.GetWidth(), 0};
1045 : else
1046 1 : return {"ARRAY", QGRHanaDataTypes::Double, field.GetWidth(), 0};
1047 : break;
1048 1 : case OFTStringList:
1049 1 : return {"ARRAY", QGRHanaDataTypes::WVarChar, 512, 0};
1050 0 : default:
1051 0 : break;
1052 : }
1053 :
1054 0 : return {"", QGRHanaDataTypes::Unknown, 0, 0};
1055 : }
1056 :
1057 : /* -------------------------------------------------------------------- */
1058 : /* GetGeometryWkb() */
1059 : /* -------------------------------------------------------------------- */
1060 132 : OGRErr OGRHanaTableLayer::GetGeometryWkb(OGRFeature *feature, int fieldIndex,
1061 : Binary &binary)
1062 : {
1063 132 : OGRGeometry *geom = feature->GetGeomFieldRef(fieldIndex);
1064 132 : if (geom == nullptr || !IsGeometryTypeSupported(geom->getIsoGeometryType()))
1065 10 : return OGRERR_NONE;
1066 :
1067 : // Rings must be closed, otherwise HANA throws an exception
1068 122 : geom->closeRings();
1069 122 : std::size_t size = static_cast<std::size_t>(geom->WkbSize());
1070 122 : EnsureBufferCapacity(size);
1071 122 : unsigned char *data = reinterpret_cast<unsigned char *>(dataBuffer_.data());
1072 122 : OGRErr err = geom->exportToWkb(OGRwkbByteOrder::wkbNDR, data,
1073 : OGRwkbVariant::wkbVariantIso);
1074 122 : if (OGRERR_NONE == err)
1075 : {
1076 122 : binary.data = data;
1077 122 : binary.size = size;
1078 : }
1079 122 : return err;
1080 : }
1081 :
1082 : /************************************************************************/
1083 : /* IGetExtent() */
1084 : /************************************************************************/
1085 :
1086 8 : OGRErr OGRHanaTableLayer::IGetExtent(int geomField, OGREnvelope *extent,
1087 : bool force)
1088 : {
1089 8 : if (geomField >= 0 && geomField < GetLayerDefn()->GetGeomFieldCount())
1090 : {
1091 8 : FlushPendingBatches(false);
1092 : }
1093 :
1094 8 : return OGRHanaLayer::IGetExtent(geomField, extent, force);
1095 : }
1096 :
1097 : /************************************************************************/
1098 : /* GetFeatureCount() */
1099 : /************************************************************************/
1100 :
1101 41 : GIntBig OGRHanaTableLayer::GetFeatureCount(int force)
1102 : {
1103 41 : FlushPendingBatches(false);
1104 :
1105 41 : return OGRHanaLayer::GetFeatureCount(force);
1106 : }
1107 :
1108 : /************************************************************************/
1109 : /* ResetReading() */
1110 : /************************************************************************/
1111 :
1112 223 : void OGRHanaTableLayer::ResetReading()
1113 : {
1114 223 : FlushPendingBatches(false);
1115 :
1116 223 : if (OGRNullFID != fidFieldIndex_ && nextFeatureId_ > 0)
1117 69 : allowAutoFIDOnCreateFeature_ = false;
1118 :
1119 223 : OGRHanaLayer::ResetReading();
1120 223 : }
1121 :
1122 : /************************************************************************/
1123 : /* TestCapability() */
1124 : /************************************************************************/
1125 :
1126 150 : int OGRHanaTableLayer::TestCapability(const char *capabilities)
1127 : {
1128 150 : if (EQUAL(capabilities, OLCRandomRead))
1129 : {
1130 2 : EnsureInitialized();
1131 2 : return fidFieldIndex_ != OGRNullFID;
1132 : }
1133 148 : if (EQUAL(capabilities, OLCFastFeatureCount))
1134 10 : return TRUE;
1135 138 : if (EQUAL(capabilities, OLCFastSpatialFilter))
1136 : {
1137 10 : EnsureInitialized();
1138 10 : return !geomColumns_.empty();
1139 : }
1140 128 : if (EQUAL(capabilities, OLCFastGetExtent))
1141 : {
1142 12 : EnsureInitialized();
1143 12 : return IsFastExtentAvailable();
1144 : }
1145 116 : if (EQUAL(capabilities, OLCCreateField))
1146 12 : return updateMode_;
1147 104 : if (EQUAL(capabilities, OLCCreateGeomField) ||
1148 94 : EQUAL(capabilities, ODsCCreateGeomFieldAfterCreateLayer))
1149 10 : return updateMode_;
1150 94 : if (EQUAL(capabilities, OLCDeleteField))
1151 2 : return updateMode_;
1152 92 : if (EQUAL(capabilities, OLCDeleteFeature))
1153 : {
1154 12 : EnsureInitialized();
1155 12 : return updateMode_ && fidFieldIndex_ != OGRNullFID;
1156 : }
1157 80 : if (EQUAL(capabilities, OLCAlterFieldDefn))
1158 12 : return updateMode_;
1159 68 : if (EQUAL(capabilities, OLCRandomWrite))
1160 13 : return updateMode_;
1161 55 : if (EQUAL(capabilities, OLCMeasuredGeometries))
1162 18 : return TRUE;
1163 37 : if (EQUAL(capabilities, OLCSequentialWrite))
1164 1 : return updateMode_;
1165 36 : if (EQUAL(capabilities, OLCTransactions))
1166 11 : return updateMode_;
1167 25 : if (EQUAL(capabilities, OLCStringsAsUTF8))
1168 1 : return TRUE;
1169 :
1170 24 : return FALSE;
1171 : }
1172 :
1173 : /************************************************************************/
1174 : /* ICreateFeature() */
1175 : /************************************************************************/
1176 :
1177 123 : OGRErr OGRHanaTableLayer::ICreateFeature(OGRFeature *feature)
1178 : {
1179 123 : if (!updateMode_)
1180 : {
1181 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1182 : "CreateFeature");
1183 0 : return OGRERR_FAILURE;
1184 : }
1185 :
1186 123 : if (nullptr == feature)
1187 : {
1188 0 : CPLError(CE_Failure, CPLE_AppDefined,
1189 : "NULL pointer to OGRFeature passed to CreateFeature().");
1190 0 : return OGRERR_FAILURE;
1191 : }
1192 :
1193 123 : EnsureInitialized();
1194 :
1195 : OGRErr err =
1196 123 : ExecutePendingBatches(BatchOperation::DELETE | BatchOperation::UPDATE);
1197 123 : if (OGRERR_NONE != err)
1198 0 : return err;
1199 :
1200 123 : bool hasFID = feature->GetFID() != OGRNullFID;
1201 123 : if (hasFID && OGRNullFID != fidFieldIndex_)
1202 7 : allowAutoFIDOnCreateFeature_ = false;
1203 :
1204 123 : if (!hasFID && allowAutoFIDOnCreateFeature_)
1205 : {
1206 108 : feature->SetFID(nextFeatureId_++);
1207 108 : hasFID = true;
1208 : }
1209 :
1210 123 : bool useBatch = hasFID && dataSource_->IsTransactionStarted();
1211 :
1212 : try
1213 : {
1214 123 : odbc::PreparedStatementRef &stmt =
1215 : hasFID ? insertFeatureStmtWithFID_ : insertFeatureStmtWithoutFID_;
1216 :
1217 123 : if (stmt.isNull())
1218 : {
1219 16 : stmt = CreateInsertFeatureStatement(hasFID);
1220 16 : if (stmt.isNull())
1221 0 : return OGRERR_FAILURE;
1222 : }
1223 :
1224 123 : err = SetStatementParameters(*stmt, feature, true, hasFID,
1225 : "CreateFeature");
1226 123 : if (OGRERR_NONE != err)
1227 0 : return err;
1228 :
1229 123 : if (useBatch)
1230 5 : stmt->addBatch();
1231 :
1232 123 : auto ret = ExecuteUpdate(*stmt, useBatch, "CreateFeature");
1233 :
1234 123 : err = ret.first;
1235 123 : if (OGRERR_NONE != err)
1236 0 : return err;
1237 :
1238 123 : if (!hasFID && OGRNullFID != fidFieldIndex_)
1239 : {
1240 8 : const CPLString sql = CPLString().Printf(
1241 : "SELECT CURRENT_IDENTITY_VALUE() \"current identity value\" "
1242 : "FROM %s",
1243 16 : GetFullTableNameQuoted(schemaName_, tableName_).c_str());
1244 :
1245 8 : if (currentIdentityValueStmt_.isNull())
1246 : {
1247 : currentIdentityValueStmt_ =
1248 2 : dataSource_->PrepareStatement(sql.c_str());
1249 2 : if (currentIdentityValueStmt_.isNull())
1250 0 : return OGRERR_FAILURE;
1251 : }
1252 :
1253 : odbc::ResultSetRef rsIdentity =
1254 16 : currentIdentityValueStmt_->executeQuery();
1255 8 : if (rsIdentity->next())
1256 : {
1257 8 : odbc::Long id = rsIdentity->getLong(1);
1258 8 : if (!id.isNull())
1259 8 : feature->SetFID(static_cast<GIntBig>(*id));
1260 : }
1261 8 : rsIdentity->close();
1262 : }
1263 :
1264 123 : return err;
1265 : }
1266 0 : catch (const std::exception &ex)
1267 : {
1268 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unable to create feature: %s",
1269 0 : ex.what());
1270 0 : return OGRERR_FAILURE;
1271 : }
1272 : return OGRERR_NONE;
1273 : }
1274 :
1275 : /************************************************************************/
1276 : /* DeleteFeature() */
1277 : /************************************************************************/
1278 :
1279 8 : OGRErr OGRHanaTableLayer::DeleteFeature(GIntBig nFID)
1280 : {
1281 8 : if (!updateMode_)
1282 : {
1283 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1284 : "DeleteFeature");
1285 0 : return OGRERR_FAILURE;
1286 : }
1287 :
1288 8 : if (nFID == OGRNullFID)
1289 : {
1290 0 : CPLError(CE_Failure, CPLE_AppDefined,
1291 : "DeleteFeature(" CPL_FRMT_GIB
1292 : ") failed. Unable to delete features "
1293 : "in tables without\n a recognised FID column.",
1294 : nFID);
1295 0 : return OGRERR_FAILURE;
1296 : }
1297 :
1298 8 : if (OGRNullFID == fidFieldIndex_)
1299 : {
1300 0 : CPLError(CE_Failure, CPLE_AppDefined,
1301 : "DeleteFeature(" CPL_FRMT_GIB
1302 : ") failed. Unable to delete features "
1303 : "in tables without\n a recognised FID column.",
1304 : nFID);
1305 0 : return OGRERR_FAILURE;
1306 : }
1307 :
1308 8 : EnsureInitialized();
1309 :
1310 8 : if (deleteFeatureStmt_.isNull())
1311 : {
1312 3 : deleteFeatureStmt_ = CreateDeleteFeatureStatement();
1313 3 : if (deleteFeatureStmt_.isNull())
1314 0 : return OGRERR_FAILURE;
1315 : }
1316 :
1317 : OGRErr err =
1318 8 : ExecutePendingBatches(BatchOperation::INSERT | BatchOperation::UPDATE);
1319 8 : if (OGRERR_NONE != err)
1320 0 : return err;
1321 :
1322 8 : deleteFeatureStmt_->setLong(1, odbc::Long(static_cast<std::int64_t>(nFID)));
1323 8 : bool withBatch = dataSource_->IsTransactionStarted();
1324 8 : if (withBatch)
1325 2 : deleteFeatureStmt_->addBatch();
1326 :
1327 8 : auto ret = ExecuteUpdate(*deleteFeatureStmt_, withBatch, "DeleteFeature");
1328 8 : return (OGRERR_NONE == ret.first && ret.second != 1)
1329 16 : ? OGRERR_NON_EXISTING_FEATURE
1330 8 : : ret.first;
1331 : }
1332 :
1333 : /************************************************************************/
1334 : /* ISetFeature() */
1335 : /************************************************************************/
1336 :
1337 13 : OGRErr OGRHanaTableLayer::ISetFeature(OGRFeature *feature)
1338 : {
1339 13 : if (!updateMode_)
1340 : {
1341 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1342 : "SetFeature");
1343 0 : return OGRERR_FAILURE;
1344 : }
1345 :
1346 13 : if (nullptr == feature)
1347 : {
1348 0 : CPLError(CE_Failure, CPLE_AppDefined,
1349 : "NULL pointer to OGRFeature passed to SetFeature().");
1350 0 : return OGRERR_FAILURE;
1351 : }
1352 :
1353 13 : if (feature->GetFID() == OGRNullFID)
1354 : {
1355 0 : CPLError(CE_Failure, CPLE_AppDefined,
1356 : "FID required on features given to SetFeature().");
1357 0 : return OGRERR_FAILURE;
1358 : }
1359 :
1360 13 : if (OGRNullFID == fidFieldIndex_)
1361 : {
1362 0 : CPLError(CE_Failure, CPLE_AppDefined,
1363 : "Unable to update features in tables without\n"
1364 : "a recognised FID column.");
1365 0 : return OGRERR_FAILURE;
1366 : }
1367 :
1368 13 : EnsureInitialized();
1369 :
1370 13 : if (updateFeatureStmt_.isNull())
1371 : {
1372 4 : updateFeatureStmt_ = CreateUpdateFeatureStatement();
1373 4 : if (updateFeatureStmt_.isNull())
1374 0 : return OGRERR_FAILURE;
1375 : }
1376 :
1377 13 : if (OGRNullFID != fidFieldIndex_)
1378 13 : allowAutoFIDOnCreateFeature_ = false;
1379 :
1380 : OGRErr err =
1381 13 : ExecutePendingBatches(BatchOperation::DELETE | BatchOperation::INSERT);
1382 13 : if (OGRERR_NONE != err)
1383 0 : return err;
1384 :
1385 : try
1386 : {
1387 13 : err = SetStatementParameters(*updateFeatureStmt_, feature, false, false,
1388 : "SetFeature");
1389 :
1390 13 : if (OGRERR_NONE != err)
1391 0 : return err;
1392 :
1393 13 : bool withBatch = dataSource_->IsTransactionStarted();
1394 13 : if (withBatch)
1395 1 : updateFeatureStmt_->addBatch();
1396 :
1397 13 : auto ret = ExecuteUpdate(*updateFeatureStmt_, withBatch, "SetFeature");
1398 13 : return (OGRERR_NONE == ret.first && ret.second != 1)
1399 26 : ? OGRERR_NON_EXISTING_FEATURE
1400 13 : : ret.first;
1401 : }
1402 0 : catch (const std::exception &ex)
1403 : {
1404 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unable to create feature: %s",
1405 0 : ex.what());
1406 0 : return OGRERR_FAILURE;
1407 : }
1408 : }
1409 :
1410 : /************************************************************************/
1411 : /* CreateField() */
1412 : /************************************************************************/
1413 :
1414 66 : OGRErr OGRHanaTableLayer::CreateField(const OGRFieldDefn *srsField,
1415 : int approxOK)
1416 : {
1417 66 : if (!updateMode_)
1418 : {
1419 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1420 : "CreateField");
1421 0 : return OGRERR_FAILURE;
1422 : }
1423 :
1424 66 : if (srsField->GetNameRef() == nullptr)
1425 : {
1426 0 : CPLError(CE_Failure, CPLE_AppDefined, "Field name cannot be NULL");
1427 0 : return OGRERR_FAILURE;
1428 : }
1429 :
1430 66 : EnsureInitialized();
1431 :
1432 132 : OGRFieldDefn dstField(srsField);
1433 :
1434 66 : if (launderColumnNames_)
1435 : {
1436 63 : auto nameRes = dataSource_->LaunderName(dstField.GetNameRef());
1437 63 : if (nameRes.first != OGRERR_NONE)
1438 0 : return nameRes.first;
1439 63 : dstField.SetName(nameRes.second.c_str());
1440 : }
1441 :
1442 197 : if (fidFieldIndex_ != OGRNullFID &&
1443 67 : EQUAL(dstField.GetNameRef(), GetFIDColumn()) &&
1444 133 : dstField.GetType() != OFTInteger && dstField.GetType() != OFTInteger64)
1445 : {
1446 1 : CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s",
1447 : dstField.GetNameRef());
1448 1 : return OGRERR_FAILURE;
1449 : }
1450 :
1451 130 : ColumnTypeInfo columnTypeInfo = GetColumnTypeInfo(dstField);
1452 130 : CPLString columnDef = GetColumnDefinition(columnTypeInfo);
1453 :
1454 65 : if (columnTypeInfo.type == QGRHanaDataTypes::Unknown)
1455 : {
1456 0 : if (columnTypeInfo.name.empty())
1457 0 : return OGRERR_FAILURE;
1458 :
1459 0 : if (approxOK)
1460 : {
1461 0 : dstField.SetDefault(nullptr);
1462 0 : CPLError(CE_Warning, CPLE_NotSupported,
1463 : "Unable to create field %s with type %s on HANA layers. "
1464 : "Creating as VARCHAR.",
1465 : dstField.GetNameRef(),
1466 : OGRFieldDefn::GetFieldTypeName(dstField.GetType()));
1467 0 : columnTypeInfo.name = "VARCHAR";
1468 0 : columnTypeInfo.width = static_cast<int>(defaultStringSize_);
1469 0 : columnDef = "VARCHAR(" + std::to_string(defaultStringSize_) + ")";
1470 : }
1471 : else
1472 : {
1473 0 : CPLError(CE_Failure, CPLE_NotSupported,
1474 : "Unable to create field %s with type %s on HANA layers.",
1475 : dstField.GetNameRef(),
1476 : OGRFieldDefn::GetFieldTypeName(dstField.GetType()));
1477 :
1478 0 : return OGRERR_FAILURE;
1479 : }
1480 : }
1481 :
1482 : CPLString clmClause =
1483 195 : QuotedIdentifier(dstField.GetNameRef()) + " " + columnDef;
1484 65 : if (!dstField.IsNullable())
1485 0 : clmClause += " NOT NULL";
1486 65 : if (dstField.GetDefault() != nullptr && !dstField.IsDefaultDriverSpecific())
1487 : {
1488 12 : if (IsArrayField(dstField.GetType()) ||
1489 24 : columnTypeInfo.type == QGRHanaDataTypes::LongVarBinary ||
1490 12 : columnTypeInfo.type == QGRHanaDataTypes::RealVector)
1491 : {
1492 0 : CPLError(
1493 : CE_Failure, CPLE_NotSupported,
1494 : "Default value cannot be created on column of data type %s: "
1495 : "%s.",
1496 : columnTypeInfo.name.c_str(), dstField.GetNameRef());
1497 :
1498 0 : return OGRERR_FAILURE;
1499 : }
1500 :
1501 : clmClause +=
1502 12 : CPLString().Printf(" DEFAULT %s", GetColumnDefaultValue(dstField));
1503 : }
1504 :
1505 65 : FlushPendingBatches(false);
1506 :
1507 65 : const CPLString sql = CPLString().Printf(
1508 : "ALTER TABLE %s ADD(%s)",
1509 130 : GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
1510 195 : clmClause.c_str());
1511 :
1512 : try
1513 : {
1514 66 : dataSource_->ExecuteSQL(sql.c_str());
1515 : }
1516 1 : catch (const odbc::Exception &ex)
1517 : {
1518 1 : CPLError(CE_Failure, CPLE_AppDefined,
1519 : "Failed to execute create attribute field %s: %s",
1520 1 : dstField.GetNameRef(), ex.what());
1521 1 : return OGRERR_FAILURE;
1522 : }
1523 :
1524 : // columnTypeInfo might contain a different definition due to custom column
1525 : // types
1526 64 : SetFieldDefn(dstField, columnTypeInfo);
1527 :
1528 64 : AttributeColumnDescription clmDesc;
1529 64 : clmDesc.name = dstField.GetNameRef();
1530 64 : clmDesc.type = columnTypeInfo.type;
1531 64 : clmDesc.typeName = columnTypeInfo.name;
1532 64 : clmDesc.isArray = IsArrayField(dstField.GetType());
1533 64 : clmDesc.length = columnTypeInfo.width;
1534 64 : clmDesc.isNullable = dstField.IsNullable();
1535 64 : clmDesc.isAutoIncrement = false; // TODO
1536 64 : clmDesc.scale = static_cast<unsigned short>(columnTypeInfo.precision);
1537 64 : clmDesc.precision = static_cast<unsigned short>(columnTypeInfo.width);
1538 64 : if (dstField.GetDefault() != nullptr)
1539 12 : clmDesc.defaultValue = dstField.GetDefault();
1540 :
1541 64 : attrColumns_.push_back(std::move(clmDesc));
1542 64 : featureDefn_->AddFieldDefn(&dstField);
1543 :
1544 64 : ColumnsChanged();
1545 :
1546 64 : return OGRERR_NONE;
1547 : }
1548 :
1549 : /************************************************************************/
1550 : /* CreateGeomField() */
1551 : /************************************************************************/
1552 :
1553 5 : OGRErr OGRHanaTableLayer::CreateGeomField(const OGRGeomFieldDefn *geomField,
1554 : int)
1555 : {
1556 5 : if (!updateMode_)
1557 : {
1558 1 : CPLError(CE_Failure, CPLE_AppDefined, UNSUPPORTED_OP_READ_ONLY,
1559 : "CreateGeomField");
1560 1 : return OGRERR_FAILURE;
1561 : }
1562 :
1563 4 : if (!IsGeometryTypeSupported(geomField->GetType()))
1564 : {
1565 1 : CPLError(CE_Failure, CPLE_NotSupported,
1566 : "Geometry field '%s' in layer '%s' has unsupported type %s",
1567 : geomField->GetNameRef(), tableName_.c_str(),
1568 : OGRGeometryTypeToName(geomField->GetType()));
1569 1 : return OGRERR_FAILURE;
1570 : }
1571 :
1572 3 : EnsureInitialized();
1573 :
1574 3 : if (featureDefn_->GetGeomFieldIndex(geomField->GetNameRef()) >= 0)
1575 : {
1576 1 : CPLError(
1577 : CE_Failure, CPLE_AppDefined,
1578 : "CreateGeomField() called with an already existing field name: %s",
1579 : geomField->GetNameRef());
1580 1 : return OGRERR_FAILURE;
1581 : }
1582 :
1583 2 : FlushPendingBatches(false);
1584 :
1585 2 : int srid = dataSource_->GetSrsId(geomField->GetSpatialRef());
1586 2 : if (srid == UNDETERMINED_SRID)
1587 : {
1588 1 : CPLError(CE_Failure, CPLE_AppDefined,
1589 : "Unable to determine the srs-id for field name: %s",
1590 : geomField->GetNameRef());
1591 1 : return OGRERR_FAILURE;
1592 : }
1593 :
1594 2 : CPLString clmName(geomField->GetNameRef());
1595 1 : if (launderColumnNames_)
1596 : {
1597 1 : auto nameRes = dataSource_->LaunderName(geomField->GetNameRef());
1598 1 : if (nameRes.first != OGRERR_NONE)
1599 0 : return nameRes.first;
1600 1 : clmName.swap(nameRes.second);
1601 : }
1602 :
1603 1 : if (clmName.empty())
1604 1 : clmName = FindGeomFieldName(*featureDefn_);
1605 :
1606 1 : CPLString sql = CPLString().Printf(
1607 : "ALTER TABLE %s ADD(%s ST_GEOMETRY(%d))",
1608 2 : GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
1609 4 : QuotedIdentifier(clmName).c_str(), srid);
1610 :
1611 : try
1612 : {
1613 1 : dataSource_->ExecuteSQL(sql.c_str());
1614 : }
1615 0 : catch (const odbc::Exception &ex)
1616 : {
1617 0 : CPLError(CE_Failure, CPLE_AppDefined,
1618 : "Failed to execute CreateGeomField() with field name '%s': %s",
1619 0 : geomField->GetNameRef(), ex.what());
1620 0 : return OGRERR_FAILURE;
1621 : }
1622 :
1623 : auto newGeomField = std::make_unique<OGRGeomFieldDefn>(
1624 1 : clmName.c_str(), geomField->GetType());
1625 1 : newGeomField->SetNullable(geomField->IsNullable());
1626 1 : newGeomField->SetSpatialRef(geomField->GetSpatialRef());
1627 2 : geomColumns_.push_back({newGeomField->GetNameRef(), newGeomField->GetType(),
1628 1 : srid, newGeomField->IsNullable() == TRUE});
1629 1 : featureDefn_->AddGeomFieldDefn(std::move(newGeomField));
1630 :
1631 1 : ColumnsChanged();
1632 :
1633 1 : return OGRERR_NONE;
1634 : }
1635 :
1636 : /************************************************************************/
1637 : /* DeleteField() */
1638 : /************************************************************************/
1639 :
1640 0 : OGRErr OGRHanaTableLayer::DeleteField(int field)
1641 : {
1642 0 : if (!updateMode_)
1643 : {
1644 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1645 : "DeleteField");
1646 0 : return OGRERR_FAILURE;
1647 : }
1648 :
1649 0 : if (field < 0 || field >= featureDefn_->GetFieldCount())
1650 : {
1651 0 : CPLError(CE_Failure, CPLE_NotSupported, "Field index is out of range");
1652 0 : return OGRERR_FAILURE;
1653 : }
1654 :
1655 0 : EnsureInitialized();
1656 :
1657 0 : FlushPendingBatches(false);
1658 :
1659 0 : CPLString clmName = featureDefn_->GetFieldDefn(field)->GetNameRef();
1660 0 : CPLString sql = CPLString().Printf(
1661 : "ALTER TABLE %s DROP (%s)",
1662 0 : GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
1663 0 : QuotedIdentifier(clmName).c_str());
1664 :
1665 : try
1666 : {
1667 0 : dataSource_->ExecuteSQL(sql.c_str());
1668 : }
1669 0 : catch (const odbc::Exception &ex)
1670 : {
1671 0 : CPLError(CE_Failure, CPLE_AppDefined, "Failed to drop column %s: %s",
1672 0 : clmName.c_str(), ex.what());
1673 0 : return OGRERR_FAILURE;
1674 : }
1675 :
1676 : auto it = std::find_if(attrColumns_.begin(), attrColumns_.end(),
1677 0 : [&](const AttributeColumnDescription &cd)
1678 0 : { return cd.name == clmName; });
1679 0 : attrColumns_.erase(it);
1680 0 : OGRErr ret = featureDefn_->DeleteFieldDefn(field);
1681 :
1682 0 : ColumnsChanged();
1683 :
1684 0 : return ret;
1685 : }
1686 :
1687 : /************************************************************************/
1688 : /* AlterFieldDefn() */
1689 : /************************************************************************/
1690 :
1691 0 : OGRErr OGRHanaTableLayer::AlterFieldDefn(int field, OGRFieldDefn *newFieldDefn,
1692 : int flagsIn)
1693 : {
1694 0 : if (!updateMode_)
1695 : {
1696 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1697 : "AlterFieldDefn");
1698 0 : return OGRERR_FAILURE;
1699 : }
1700 :
1701 0 : EnsureInitialized();
1702 :
1703 0 : if (field < 0 || field >= featureDefn_->GetFieldCount())
1704 : {
1705 0 : CPLError(CE_Failure, CPLE_NotSupported, "Field index is out of range");
1706 0 : return OGRERR_FAILURE;
1707 : }
1708 :
1709 0 : OGRFieldDefn *fieldDefn = featureDefn_->GetFieldDefn(field);
1710 :
1711 0 : int columnDescIdx = -1;
1712 0 : for (size_t i = 0; i < attrColumns_.size(); ++i)
1713 : {
1714 0 : if (EQUAL(attrColumns_[i].name.c_str(), fieldDefn->GetNameRef()))
1715 : {
1716 0 : columnDescIdx = static_cast<int>(i);
1717 0 : break;
1718 : }
1719 : }
1720 :
1721 0 : if (columnDescIdx < 0)
1722 : {
1723 0 : CPLError(CE_Failure, CPLE_NotSupported,
1724 : "Column description cannot be found");
1725 0 : return OGRERR_FAILURE;
1726 : }
1727 :
1728 0 : CPLString clmName(newFieldDefn->GetNameRef());
1729 :
1730 0 : if (launderColumnNames_)
1731 : {
1732 0 : auto nameRes = dataSource_->LaunderName(newFieldDefn->GetNameRef());
1733 0 : if (nameRes.first != OGRERR_NONE)
1734 0 : return nameRes.first;
1735 0 : clmName.swap(nameRes.second);
1736 : }
1737 :
1738 0 : FlushPendingBatches(false);
1739 :
1740 0 : ColumnTypeInfo columnTypeInfo = GetColumnTypeInfo(*newFieldDefn);
1741 :
1742 : try
1743 : {
1744 0 : if ((flagsIn & ALTER_NAME_FLAG) &&
1745 0 : (strcmp(fieldDefn->GetNameRef(), newFieldDefn->GetNameRef()) != 0))
1746 : {
1747 0 : CPLString sql = CPLString().Printf(
1748 : "RENAME COLUMN %s TO %s",
1749 0 : GetFullColumnNameQuoted(schemaName_, tableName_,
1750 : fieldDefn->GetNameRef())
1751 : .c_str(),
1752 0 : QuotedIdentifier(clmName).c_str());
1753 0 : dataSource_->ExecuteSQL(sql.c_str());
1754 : }
1755 :
1756 0 : if ((flagsIn & ALTER_TYPE_FLAG) ||
1757 0 : (flagsIn & ALTER_WIDTH_PRECISION_FLAG) ||
1758 0 : (flagsIn & ALTER_NULLABLE_FLAG) || (flagsIn & ALTER_DEFAULT_FLAG))
1759 : {
1760 0 : CPLString fieldTypeDef = GetColumnDefinition(columnTypeInfo);
1761 0 : if ((flagsIn & ALTER_NULLABLE_FLAG) &&
1762 0 : fieldDefn->IsNullable() != newFieldDefn->IsNullable())
1763 : {
1764 0 : if (fieldDefn->IsNullable())
1765 0 : fieldTypeDef += " NULL";
1766 : else
1767 0 : fieldTypeDef += " NOT NULL";
1768 : }
1769 :
1770 0 : if ((flagsIn & ALTER_DEFAULT_FLAG) &&
1771 0 : ((fieldDefn->GetDefault() == nullptr &&
1772 0 : newFieldDefn->GetDefault() != nullptr) ||
1773 0 : (fieldDefn->GetDefault() != nullptr &&
1774 0 : newFieldDefn->GetDefault() == nullptr) ||
1775 0 : (fieldDefn->GetDefault() != nullptr &&
1776 0 : newFieldDefn->GetDefault() != nullptr &&
1777 0 : strcmp(fieldDefn->GetDefault(), newFieldDefn->GetDefault()) !=
1778 : 0)))
1779 : {
1780 : fieldTypeDef +=
1781 0 : " DEFAULT " + ((fieldDefn->GetType() == OFTString)
1782 0 : ? Literal(newFieldDefn->GetDefault())
1783 0 : : CPLString(newFieldDefn->GetDefault()));
1784 : }
1785 :
1786 0 : CPLString sql = CPLString().Printf(
1787 : "ALTER TABLE %s ALTER(%s %s)",
1788 0 : GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
1789 0 : QuotedIdentifier(clmName).c_str(), fieldTypeDef.c_str());
1790 :
1791 0 : dataSource_->ExecuteSQL(sql.c_str());
1792 : }
1793 : }
1794 0 : catch (const odbc::Exception &ex)
1795 : {
1796 0 : CPLError(CE_Failure, CPLE_AppDefined, "Failed to alter field %s: %s",
1797 0 : fieldDefn->GetNameRef(), ex.what());
1798 0 : return OGRERR_FAILURE;
1799 : }
1800 :
1801 : // Update field definition and column description
1802 :
1803 0 : AttributeColumnDescription &attrClmDesc = attrColumns_[columnDescIdx];
1804 :
1805 0 : if (flagsIn & ALTER_NAME_FLAG)
1806 : {
1807 0 : fieldDefn->SetName(clmName.c_str());
1808 0 : attrClmDesc.name.assign(clmName.c_str());
1809 : }
1810 :
1811 0 : if (flagsIn & ALTER_TYPE_FLAG)
1812 : {
1813 0 : fieldDefn->SetSubType(OFSTNone);
1814 0 : fieldDefn->SetType(newFieldDefn->GetType());
1815 0 : fieldDefn->SetSubType(newFieldDefn->GetSubType());
1816 0 : attrClmDesc.isArray = IsArrayField(newFieldDefn->GetType());
1817 0 : attrClmDesc.type = columnTypeInfo.type;
1818 0 : attrClmDesc.typeName = columnTypeInfo.name;
1819 : }
1820 :
1821 0 : if (flagsIn & ALTER_WIDTH_PRECISION_FLAG)
1822 : {
1823 0 : fieldDefn->SetWidth(newFieldDefn->GetWidth());
1824 0 : fieldDefn->SetPrecision(newFieldDefn->GetPrecision());
1825 0 : attrClmDesc.length = newFieldDefn->GetWidth();
1826 0 : attrClmDesc.scale = newFieldDefn->GetWidth();
1827 0 : attrClmDesc.precision = newFieldDefn->GetPrecision();
1828 : }
1829 :
1830 0 : if (flagsIn & ALTER_NULLABLE_FLAG)
1831 : {
1832 0 : fieldDefn->SetNullable(newFieldDefn->IsNullable());
1833 0 : attrClmDesc.isNullable = newFieldDefn->IsNullable();
1834 : }
1835 :
1836 0 : if (flagsIn & ALTER_DEFAULT_FLAG)
1837 : {
1838 0 : fieldDefn->SetDefault(newFieldDefn->GetDefault());
1839 0 : attrClmDesc.name.assign(newFieldDefn->GetDefault());
1840 : }
1841 :
1842 0 : ColumnsChanged();
1843 :
1844 0 : return OGRERR_NONE;
1845 : }
1846 :
1847 : /************************************************************************/
1848 : /* ClearBatches() */
1849 : /************************************************************************/
1850 :
1851 4 : void OGRHanaTableLayer::ClearBatches()
1852 : {
1853 4 : if (!insertFeatureStmtWithFID_.isNull())
1854 4 : insertFeatureStmtWithFID_->clearBatch();
1855 4 : if (!insertFeatureStmtWithoutFID_.isNull())
1856 1 : insertFeatureStmtWithoutFID_->clearBatch();
1857 4 : if (!updateFeatureStmt_.isNull())
1858 2 : updateFeatureStmt_->clearBatch();
1859 4 : }
1860 :
1861 : /************************************************************************/
1862 : /* ColumnsChanged() */
1863 : /************************************************************************/
1864 :
1865 65 : void OGRHanaTableLayer::ColumnsChanged()
1866 : {
1867 65 : ClearQueryStatement();
1868 65 : ResetReading();
1869 65 : ResetPreparedStatements();
1870 65 : }
1871 :
1872 : /************************************************************************/
1873 : /* SetCustomColumnTypes() */
1874 : /************************************************************************/
1875 :
1876 18 : void OGRHanaTableLayer::SetCustomColumnTypes(const char *columnTypes)
1877 : {
1878 18 : if (columnTypes == nullptr)
1879 17 : return;
1880 :
1881 1 : const char *ptr = columnTypes;
1882 1 : const char *start = ptr;
1883 42 : while (*ptr != '\0')
1884 : {
1885 41 : if (*ptr == '(')
1886 : {
1887 : // Skip commas inside brackets, for example decimal(20,5)
1888 12 : while (*ptr != '\0' && *ptr != ')')
1889 : {
1890 10 : ++ptr;
1891 : }
1892 : }
1893 :
1894 41 : ++ptr;
1895 :
1896 41 : if (*ptr == ',' || *ptr == '\0')
1897 : {
1898 3 : std::size_t len = static_cast<std::size_t>(ptr - start);
1899 3 : const char *sep = std::find(start, start + len, '=');
1900 3 : if (sep != nullptr)
1901 : {
1902 3 : std::size_t pos = static_cast<std::size_t>(sep - start);
1903 6 : customColumnDefs_.push_back(
1904 : {CPLString(start, pos),
1905 3 : CPLString(start + pos + 1, len - pos - 1)});
1906 : }
1907 :
1908 3 : start = ptr + 1;
1909 : }
1910 : }
1911 : }
1912 :
1913 : /************************************************************************/
1914 : /* StartTransaction() */
1915 : /************************************************************************/
1916 :
1917 11 : OGRErr OGRHanaTableLayer::StartTransaction()
1918 : {
1919 11 : return dataSource_->StartTransaction();
1920 : }
1921 :
1922 : /************************************************************************/
1923 : /* CommitTransaction() */
1924 : /************************************************************************/
1925 :
1926 7 : OGRErr OGRHanaTableLayer::CommitTransaction()
1927 : {
1928 7 : if (HasPendingBatches())
1929 : {
1930 0 : OGRErr err = ExecutePendingBatches(BatchOperation::ALL);
1931 0 : if (OGRERR_NONE != err)
1932 0 : return err;
1933 : }
1934 :
1935 7 : return dataSource_->CommitTransaction();
1936 : }
1937 :
1938 : /************************************************************************/
1939 : /* RollbackTransaction() */
1940 : /************************************************************************/
1941 :
1942 4 : OGRErr OGRHanaTableLayer::RollbackTransaction()
1943 : {
1944 4 : ClearBatches();
1945 4 : return dataSource_->RollbackTransaction();
1946 : }
1947 :
1948 : } // namespace OGRHANA
|