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 945 : void OGRHanaTableLayer::FlushPendingBatches(bool commit)
921 : {
922 945 : if (!HasPendingBatches())
923 941 : 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 952 : bool OGRHanaTableLayer::HasPendingBatches() const
935 : {
936 952 : return (!deleteFeatureStmt_.isNull() &&
937 75 : deleteFeatureStmt_->getBatchDataSize() > 0) ||
938 951 : (!insertFeatureStmtWithFID_.isNull() &&
939 125 : insertFeatureStmtWithFID_->getBatchDataSize() > 0) ||
940 949 : (!insertFeatureStmtWithoutFID_.isNull() &&
941 1933 : insertFeatureStmtWithoutFID_->getBatchDataSize() > 0) ||
942 949 : (!updateFeatureStmt_.isNull() &&
943 1025 : 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 : }
976 4 : return {"INTEGER", QGRHanaDataTypes::Integer, field.GetWidth(), 0};
977 :
978 11 : case OFTInteger64:
979 11 : if (preservePrecision_ && field.GetWidth() > 20)
980 : {
981 0 : return {"DECIMAL", QGRHanaDataTypes::Decimal, field.GetWidth(),
982 0 : 0};
983 : }
984 11 : return {"BIGINT", QGRHanaDataTypes::BigInt, field.GetWidth(), 0};
985 :
986 13 : case OFTReal:
987 13 : if (preservePrecision_ && field.GetWidth() != 0)
988 : {
989 1 : return {"DECIMAL", QGRHanaDataTypes::Decimal, field.GetWidth(),
990 1 : field.GetPrecision()};
991 : }
992 : else
993 : {
994 12 : if (field.GetSubType() == OFSTFloat32)
995 1 : return {"REAL", QGRHanaDataTypes::Real, field.GetWidth(),
996 1 : field.GetPrecision()};
997 : }
998 11 : return {"DOUBLE", QGRHanaDataTypes::Double, field.GetWidth(),
999 11 : field.GetPrecision()};
1000 23 : case OFTString:
1001 23 : if (field.GetWidth() == 0 || !preservePrecision_)
1002 : {
1003 13 : int width = static_cast<int>(defaultStringSize_);
1004 13 : return {"NVARCHAR", QGRHanaDataTypes::WLongVarChar, width, 0};
1005 : }
1006 : else
1007 : {
1008 10 : if (field.GetWidth() >= 1 && field.GetWidth() <= 5000)
1009 : return {"NVARCHAR", QGRHanaDataTypes::WLongVarChar,
1010 10 : field.GetWidth(), 0};
1011 : }
1012 0 : return {"NCLOB", QGRHanaDataTypes::WLongVarChar, 0, 0};
1013 1 : case OFTBinary:
1014 1 : if (field.GetWidth() >= 1 && field.GetWidth() <= 5000)
1015 : return {"VARBINARY", QGRHanaDataTypes::VarBinary,
1016 1 : field.GetWidth(), 0};
1017 0 : return {"BLOB", QGRHanaDataTypes::LongVarBinary, field.GetWidth(),
1018 0 : 0};
1019 1 : case OFTDate:
1020 1 : return {"DATE", QGRHanaDataTypes::TypeDate, field.GetWidth(), 0};
1021 1 : case OFTTime:
1022 1 : return {"TIME", QGRHanaDataTypes::TypeTime, field.GetWidth(), 0};
1023 2 : case OFTDateTime:
1024 : return {"TIMESTAMP", QGRHanaDataTypes::TypeTimestamp,
1025 2 : field.GetWidth(), 0};
1026 1 : case OFTIntegerList:
1027 1 : if (field.GetSubType() == OGRFieldSubType::OFSTInt16)
1028 0 : return {"ARRAY", QGRHanaDataTypes::SmallInt, field.GetWidth(),
1029 0 : 0};
1030 1 : return {"ARRAY", QGRHanaDataTypes::Integer, field.GetWidth(), 0};
1031 1 : case OFTInteger64List:
1032 1 : return {"ARRAY", QGRHanaDataTypes::BigInt, field.GetWidth(), 0};
1033 1 : case OFTRealList:
1034 1 : if (field.GetSubType() == OGRFieldSubType::OFSTFloat32)
1035 0 : return {"ARRAY", QGRHanaDataTypes::Real, field.GetWidth(), 0};
1036 1 : return {"ARRAY", QGRHanaDataTypes::Double, field.GetWidth(), 0};
1037 1 : case OFTStringList:
1038 1 : return {"ARRAY", QGRHanaDataTypes::WVarChar, 512, 0};
1039 0 : default:
1040 0 : break;
1041 : }
1042 :
1043 0 : return {"", QGRHanaDataTypes::Unknown, 0, 0};
1044 : }
1045 :
1046 : /* -------------------------------------------------------------------- */
1047 : /* GetGeometryWkb() */
1048 : /* -------------------------------------------------------------------- */
1049 132 : OGRErr OGRHanaTableLayer::GetGeometryWkb(OGRFeature *feature, int fieldIndex,
1050 : Binary &binary)
1051 : {
1052 132 : OGRGeometry *geom = feature->GetGeomFieldRef(fieldIndex);
1053 132 : if (geom == nullptr || !IsGeometryTypeSupported(geom->getIsoGeometryType()))
1054 10 : return OGRERR_NONE;
1055 :
1056 : // Rings must be closed, otherwise HANA throws an exception
1057 122 : geom->closeRings();
1058 122 : std::size_t size = static_cast<std::size_t>(geom->WkbSize());
1059 122 : EnsureBufferCapacity(size);
1060 122 : unsigned char *data = reinterpret_cast<unsigned char *>(dataBuffer_.data());
1061 122 : OGRErr err = geom->exportToWkb(OGRwkbByteOrder::wkbNDR, data,
1062 : OGRwkbVariant::wkbVariantIso);
1063 122 : if (OGRERR_NONE == err)
1064 : {
1065 122 : binary.data = data;
1066 122 : binary.size = size;
1067 : }
1068 122 : return err;
1069 : }
1070 :
1071 : /************************************************************************/
1072 : /* IGetExtent() */
1073 : /************************************************************************/
1074 :
1075 8 : OGRErr OGRHanaTableLayer::IGetExtent(int geomField, OGREnvelope *extent,
1076 : bool force)
1077 : {
1078 8 : if (geomField >= 0 && geomField < GetLayerDefn()->GetGeomFieldCount())
1079 : {
1080 8 : FlushPendingBatches(false);
1081 : }
1082 :
1083 8 : return OGRHanaLayer::IGetExtent(geomField, extent, force);
1084 : }
1085 :
1086 : /************************************************************************/
1087 : /* GetFeatureCount() */
1088 : /************************************************************************/
1089 :
1090 41 : GIntBig OGRHanaTableLayer::GetFeatureCount(int force)
1091 : {
1092 41 : FlushPendingBatches(false);
1093 :
1094 41 : return OGRHanaLayer::GetFeatureCount(force);
1095 : }
1096 :
1097 : /************************************************************************/
1098 : /* ResetReading() */
1099 : /************************************************************************/
1100 :
1101 225 : void OGRHanaTableLayer::ResetReading()
1102 : {
1103 225 : FlushPendingBatches(false);
1104 :
1105 225 : if (OGRNullFID != fidFieldIndex_ && nextFeatureId_ > 0)
1106 71 : allowAutoFIDOnCreateFeature_ = false;
1107 :
1108 225 : OGRHanaLayer::ResetReading();
1109 225 : }
1110 :
1111 : /************************************************************************/
1112 : /* TestCapability() */
1113 : /************************************************************************/
1114 :
1115 151 : int OGRHanaTableLayer::TestCapability(const char *capabilities) const
1116 : {
1117 151 : if (EQUAL(capabilities, OLCRandomRead))
1118 : {
1119 2 : const_cast<OGRHanaTableLayer *>(this)->EnsureInitialized();
1120 2 : return fidFieldIndex_ != OGRNullFID;
1121 : }
1122 149 : if (EQUAL(capabilities, OLCFastFeatureCount))
1123 10 : return TRUE;
1124 139 : if (EQUAL(capabilities, OLCFastSpatialFilter))
1125 : {
1126 10 : const_cast<OGRHanaTableLayer *>(this)->EnsureInitialized();
1127 10 : return !geomColumns_.empty();
1128 : }
1129 129 : if (EQUAL(capabilities, OLCFastGetExtent))
1130 : {
1131 12 : const_cast<OGRHanaTableLayer *>(this)->EnsureInitialized();
1132 12 : return IsFastExtentAvailable();
1133 : }
1134 117 : if (EQUAL(capabilities, OLCCreateField))
1135 12 : return updateMode_;
1136 105 : if (EQUAL(capabilities, OLCCreateGeomField) ||
1137 95 : EQUAL(capabilities, ODsCCreateGeomFieldAfterCreateLayer))
1138 10 : return updateMode_;
1139 95 : if (EQUAL(capabilities, OLCDeleteField))
1140 2 : return updateMode_;
1141 93 : if (EQUAL(capabilities, OLCDeleteFeature))
1142 : {
1143 12 : const_cast<OGRHanaTableLayer *>(this)->EnsureInitialized();
1144 12 : return updateMode_ && fidFieldIndex_ != OGRNullFID;
1145 : }
1146 81 : if (EQUAL(capabilities, OLCAlterFieldDefn))
1147 12 : return updateMode_;
1148 69 : if (EQUAL(capabilities, OLCRandomWrite))
1149 13 : return updateMode_;
1150 56 : if (EQUAL(capabilities, OLCMeasuredGeometries))
1151 18 : return TRUE;
1152 38 : if (EQUAL(capabilities, OLCSequentialWrite))
1153 1 : return updateMode_;
1154 37 : if (EQUAL(capabilities, OLCTransactions))
1155 11 : return updateMode_;
1156 26 : if (EQUAL(capabilities, OLCStringsAsUTF8))
1157 1 : return TRUE;
1158 :
1159 25 : return FALSE;
1160 : }
1161 :
1162 : /************************************************************************/
1163 : /* ICreateFeature() */
1164 : /************************************************************************/
1165 :
1166 123 : OGRErr OGRHanaTableLayer::ICreateFeature(OGRFeature *feature)
1167 : {
1168 123 : if (!updateMode_)
1169 : {
1170 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1171 : "CreateFeature");
1172 0 : return OGRERR_FAILURE;
1173 : }
1174 :
1175 123 : if (nullptr == feature)
1176 : {
1177 0 : CPLError(CE_Failure, CPLE_AppDefined,
1178 : "NULL pointer to OGRFeature passed to CreateFeature().");
1179 0 : return OGRERR_FAILURE;
1180 : }
1181 :
1182 123 : EnsureInitialized();
1183 :
1184 : OGRErr err =
1185 123 : ExecutePendingBatches(BatchOperation::DELETE | BatchOperation::UPDATE);
1186 123 : if (OGRERR_NONE != err)
1187 0 : return err;
1188 :
1189 123 : bool hasFID = feature->GetFID() != OGRNullFID;
1190 123 : if (hasFID && OGRNullFID != fidFieldIndex_)
1191 7 : allowAutoFIDOnCreateFeature_ = false;
1192 :
1193 123 : if (!hasFID && allowAutoFIDOnCreateFeature_)
1194 : {
1195 108 : feature->SetFID(nextFeatureId_++);
1196 108 : hasFID = true;
1197 : }
1198 :
1199 123 : bool useBatch = hasFID && dataSource_->IsTransactionStarted();
1200 :
1201 : try
1202 : {
1203 123 : odbc::PreparedStatementRef &stmt =
1204 : hasFID ? insertFeatureStmtWithFID_ : insertFeatureStmtWithoutFID_;
1205 :
1206 123 : if (stmt.isNull())
1207 : {
1208 16 : stmt = CreateInsertFeatureStatement(hasFID);
1209 16 : if (stmt.isNull())
1210 0 : return OGRERR_FAILURE;
1211 : }
1212 :
1213 123 : err = SetStatementParameters(*stmt, feature, true, hasFID,
1214 : "CreateFeature");
1215 123 : if (OGRERR_NONE != err)
1216 0 : return err;
1217 :
1218 123 : if (useBatch)
1219 5 : stmt->addBatch();
1220 :
1221 123 : auto ret = ExecuteUpdate(*stmt, useBatch, "CreateFeature");
1222 :
1223 123 : err = ret.first;
1224 123 : if (OGRERR_NONE != err)
1225 0 : return err;
1226 :
1227 123 : if (!hasFID && OGRNullFID != fidFieldIndex_)
1228 : {
1229 8 : const CPLString sql = CPLString().Printf(
1230 : "SELECT CURRENT_IDENTITY_VALUE() \"current identity value\" "
1231 : "FROM %s",
1232 16 : GetFullTableNameQuoted(schemaName_, tableName_).c_str());
1233 :
1234 8 : if (currentIdentityValueStmt_.isNull())
1235 : {
1236 : currentIdentityValueStmt_ =
1237 2 : dataSource_->PrepareStatement(sql.c_str());
1238 2 : if (currentIdentityValueStmt_.isNull())
1239 0 : return OGRERR_FAILURE;
1240 : }
1241 :
1242 : odbc::ResultSetRef rsIdentity =
1243 16 : currentIdentityValueStmt_->executeQuery();
1244 8 : if (rsIdentity->next())
1245 : {
1246 8 : odbc::Long id = rsIdentity->getLong(1);
1247 8 : if (!id.isNull())
1248 8 : feature->SetFID(static_cast<GIntBig>(*id));
1249 : }
1250 8 : rsIdentity->close();
1251 : }
1252 :
1253 123 : return err;
1254 : }
1255 0 : catch (const std::exception &ex)
1256 : {
1257 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unable to create feature: %s",
1258 0 : ex.what());
1259 0 : return OGRERR_FAILURE;
1260 : }
1261 : return OGRERR_NONE;
1262 : }
1263 :
1264 : /************************************************************************/
1265 : /* DeleteFeature() */
1266 : /************************************************************************/
1267 :
1268 8 : OGRErr OGRHanaTableLayer::DeleteFeature(GIntBig nFID)
1269 : {
1270 8 : if (!updateMode_)
1271 : {
1272 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1273 : "DeleteFeature");
1274 0 : return OGRERR_FAILURE;
1275 : }
1276 :
1277 8 : if (nFID == OGRNullFID)
1278 : {
1279 0 : CPLError(CE_Failure, CPLE_AppDefined,
1280 : "DeleteFeature(" CPL_FRMT_GIB
1281 : ") failed. Unable to delete features "
1282 : "in tables without\n a recognised FID column.",
1283 : nFID);
1284 0 : return OGRERR_FAILURE;
1285 : }
1286 :
1287 8 : if (OGRNullFID == fidFieldIndex_)
1288 : {
1289 0 : CPLError(CE_Failure, CPLE_AppDefined,
1290 : "DeleteFeature(" CPL_FRMT_GIB
1291 : ") failed. Unable to delete features "
1292 : "in tables without\n a recognised FID column.",
1293 : nFID);
1294 0 : return OGRERR_FAILURE;
1295 : }
1296 :
1297 8 : EnsureInitialized();
1298 :
1299 8 : if (deleteFeatureStmt_.isNull())
1300 : {
1301 3 : deleteFeatureStmt_ = CreateDeleteFeatureStatement();
1302 3 : if (deleteFeatureStmt_.isNull())
1303 0 : return OGRERR_FAILURE;
1304 : }
1305 :
1306 : OGRErr err =
1307 8 : ExecutePendingBatches(BatchOperation::INSERT | BatchOperation::UPDATE);
1308 8 : if (OGRERR_NONE != err)
1309 0 : return err;
1310 :
1311 8 : deleteFeatureStmt_->setLong(1, odbc::Long(static_cast<std::int64_t>(nFID)));
1312 8 : bool withBatch = dataSource_->IsTransactionStarted();
1313 8 : if (withBatch)
1314 2 : deleteFeatureStmt_->addBatch();
1315 :
1316 8 : auto ret = ExecuteUpdate(*deleteFeatureStmt_, withBatch, "DeleteFeature");
1317 8 : return (OGRERR_NONE == ret.first && ret.second != 1)
1318 16 : ? OGRERR_NON_EXISTING_FEATURE
1319 8 : : ret.first;
1320 : }
1321 :
1322 : /************************************************************************/
1323 : /* ISetFeature() */
1324 : /************************************************************************/
1325 :
1326 13 : OGRErr OGRHanaTableLayer::ISetFeature(OGRFeature *feature)
1327 : {
1328 13 : if (!updateMode_)
1329 : {
1330 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1331 : "SetFeature");
1332 0 : return OGRERR_FAILURE;
1333 : }
1334 :
1335 13 : if (nullptr == feature)
1336 : {
1337 0 : CPLError(CE_Failure, CPLE_AppDefined,
1338 : "NULL pointer to OGRFeature passed to SetFeature().");
1339 0 : return OGRERR_FAILURE;
1340 : }
1341 :
1342 13 : if (feature->GetFID() == OGRNullFID)
1343 : {
1344 0 : CPLError(CE_Failure, CPLE_AppDefined,
1345 : "FID required on features given to SetFeature().");
1346 0 : return OGRERR_FAILURE;
1347 : }
1348 :
1349 13 : if (OGRNullFID == fidFieldIndex_)
1350 : {
1351 0 : CPLError(CE_Failure, CPLE_AppDefined,
1352 : "Unable to update features in tables without\n"
1353 : "a recognised FID column.");
1354 0 : return OGRERR_FAILURE;
1355 : }
1356 :
1357 13 : EnsureInitialized();
1358 :
1359 13 : if (updateFeatureStmt_.isNull())
1360 : {
1361 4 : updateFeatureStmt_ = CreateUpdateFeatureStatement();
1362 4 : if (updateFeatureStmt_.isNull())
1363 0 : return OGRERR_FAILURE;
1364 : }
1365 :
1366 13 : if (OGRNullFID != fidFieldIndex_)
1367 13 : allowAutoFIDOnCreateFeature_ = false;
1368 :
1369 : OGRErr err =
1370 13 : ExecutePendingBatches(BatchOperation::DELETE | BatchOperation::INSERT);
1371 13 : if (OGRERR_NONE != err)
1372 0 : return err;
1373 :
1374 : try
1375 : {
1376 13 : err = SetStatementParameters(*updateFeatureStmt_, feature, false, false,
1377 : "SetFeature");
1378 :
1379 13 : if (OGRERR_NONE != err)
1380 0 : return err;
1381 :
1382 13 : bool withBatch = dataSource_->IsTransactionStarted();
1383 13 : if (withBatch)
1384 1 : updateFeatureStmt_->addBatch();
1385 :
1386 13 : auto ret = ExecuteUpdate(*updateFeatureStmt_, withBatch, "SetFeature");
1387 13 : return (OGRERR_NONE == ret.first && ret.second != 1)
1388 26 : ? OGRERR_NON_EXISTING_FEATURE
1389 13 : : ret.first;
1390 : }
1391 0 : catch (const std::exception &ex)
1392 : {
1393 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unable to create feature: %s",
1394 0 : ex.what());
1395 0 : return OGRERR_FAILURE;
1396 : }
1397 : }
1398 :
1399 : /************************************************************************/
1400 : /* CreateField() */
1401 : /************************************************************************/
1402 :
1403 66 : OGRErr OGRHanaTableLayer::CreateField(const OGRFieldDefn *srsField,
1404 : int approxOK)
1405 : {
1406 66 : if (!updateMode_)
1407 : {
1408 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1409 : "CreateField");
1410 0 : return OGRERR_FAILURE;
1411 : }
1412 :
1413 66 : if (srsField->GetNameRef() == nullptr)
1414 : {
1415 0 : CPLError(CE_Failure, CPLE_AppDefined, "Field name cannot be NULL");
1416 0 : return OGRERR_FAILURE;
1417 : }
1418 :
1419 66 : EnsureInitialized();
1420 :
1421 132 : OGRFieldDefn dstField(srsField);
1422 :
1423 66 : if (launderColumnNames_)
1424 : {
1425 63 : auto nameRes = dataSource_->LaunderName(dstField.GetNameRef());
1426 63 : if (nameRes.first != OGRERR_NONE)
1427 0 : return nameRes.first;
1428 63 : dstField.SetName(nameRes.second.c_str());
1429 : }
1430 :
1431 197 : if (fidFieldIndex_ != OGRNullFID &&
1432 67 : EQUAL(dstField.GetNameRef(), GetFIDColumn()) &&
1433 133 : dstField.GetType() != OFTInteger && dstField.GetType() != OFTInteger64)
1434 : {
1435 1 : CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s",
1436 : dstField.GetNameRef());
1437 1 : return OGRERR_FAILURE;
1438 : }
1439 :
1440 130 : ColumnTypeInfo columnTypeInfo = GetColumnTypeInfo(dstField);
1441 130 : CPLString columnDef = GetColumnDefinition(columnTypeInfo);
1442 :
1443 65 : if (columnTypeInfo.type == QGRHanaDataTypes::Unknown)
1444 : {
1445 0 : if (columnTypeInfo.name.empty())
1446 0 : return OGRERR_FAILURE;
1447 :
1448 0 : if (approxOK)
1449 : {
1450 0 : dstField.SetDefault(nullptr);
1451 0 : CPLError(CE_Warning, CPLE_NotSupported,
1452 : "Unable to create field %s with type %s on HANA layers. "
1453 : "Creating as VARCHAR.",
1454 : dstField.GetNameRef(),
1455 : OGRFieldDefn::GetFieldTypeName(dstField.GetType()));
1456 0 : columnTypeInfo.name = "VARCHAR";
1457 0 : columnTypeInfo.width = static_cast<int>(defaultStringSize_);
1458 0 : columnDef = "VARCHAR(" + std::to_string(defaultStringSize_) + ")";
1459 : }
1460 : else
1461 : {
1462 0 : CPLError(CE_Failure, CPLE_NotSupported,
1463 : "Unable to create field %s with type %s on HANA layers.",
1464 : dstField.GetNameRef(),
1465 : OGRFieldDefn::GetFieldTypeName(dstField.GetType()));
1466 :
1467 0 : return OGRERR_FAILURE;
1468 : }
1469 : }
1470 :
1471 : CPLString clmClause =
1472 195 : QuotedIdentifier(dstField.GetNameRef()) + " " + columnDef;
1473 65 : if (!dstField.IsNullable())
1474 0 : clmClause += " NOT NULL";
1475 65 : if (dstField.GetDefault() != nullptr && !dstField.IsDefaultDriverSpecific())
1476 : {
1477 12 : if (IsArrayField(dstField.GetType()) ||
1478 24 : columnTypeInfo.type == QGRHanaDataTypes::LongVarBinary ||
1479 12 : columnTypeInfo.type == QGRHanaDataTypes::RealVector)
1480 : {
1481 0 : CPLError(
1482 : CE_Failure, CPLE_NotSupported,
1483 : "Default value cannot be created on column of data type %s: "
1484 : "%s.",
1485 : columnTypeInfo.name.c_str(), dstField.GetNameRef());
1486 :
1487 0 : return OGRERR_FAILURE;
1488 : }
1489 :
1490 : clmClause +=
1491 12 : CPLString().Printf(" DEFAULT %s", GetColumnDefaultValue(dstField));
1492 : }
1493 :
1494 65 : FlushPendingBatches(false);
1495 :
1496 65 : const CPLString sql = CPLString().Printf(
1497 : "ALTER TABLE %s ADD(%s)",
1498 130 : GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
1499 195 : clmClause.c_str());
1500 :
1501 : try
1502 : {
1503 66 : dataSource_->ExecuteSQL(sql.c_str());
1504 : }
1505 1 : catch (const odbc::Exception &ex)
1506 : {
1507 1 : CPLError(CE_Failure, CPLE_AppDefined,
1508 : "Failed to execute create attribute field %s: %s",
1509 1 : dstField.GetNameRef(), ex.what());
1510 1 : return OGRERR_FAILURE;
1511 : }
1512 :
1513 : // columnTypeInfo might contain a different definition due to custom column
1514 : // types
1515 64 : SetFieldDefn(dstField, columnTypeInfo);
1516 :
1517 64 : AttributeColumnDescription clmDesc;
1518 64 : clmDesc.name = dstField.GetNameRef();
1519 64 : clmDesc.type = columnTypeInfo.type;
1520 64 : clmDesc.typeName = columnTypeInfo.name;
1521 64 : clmDesc.isArray = IsArrayField(dstField.GetType());
1522 64 : clmDesc.length = columnTypeInfo.width;
1523 64 : clmDesc.isNullable = dstField.IsNullable();
1524 64 : clmDesc.isAutoIncrement = false; // TODO
1525 64 : clmDesc.scale = static_cast<unsigned short>(columnTypeInfo.precision);
1526 64 : clmDesc.precision = static_cast<unsigned short>(columnTypeInfo.width);
1527 64 : if (dstField.GetDefault() != nullptr)
1528 12 : clmDesc.defaultValue = dstField.GetDefault();
1529 :
1530 64 : attrColumns_.push_back(std::move(clmDesc));
1531 64 : featureDefn_->AddFieldDefn(&dstField);
1532 :
1533 64 : ColumnsChanged();
1534 :
1535 64 : return OGRERR_NONE;
1536 : }
1537 :
1538 : /************************************************************************/
1539 : /* CreateGeomField() */
1540 : /************************************************************************/
1541 :
1542 5 : OGRErr OGRHanaTableLayer::CreateGeomField(const OGRGeomFieldDefn *geomField,
1543 : int)
1544 : {
1545 5 : if (!updateMode_)
1546 : {
1547 1 : CPLError(CE_Failure, CPLE_AppDefined, UNSUPPORTED_OP_READ_ONLY,
1548 : "CreateGeomField");
1549 1 : return OGRERR_FAILURE;
1550 : }
1551 :
1552 4 : if (!IsGeometryTypeSupported(geomField->GetType()))
1553 : {
1554 1 : CPLError(CE_Failure, CPLE_NotSupported,
1555 : "Geometry field '%s' in layer '%s' has unsupported type %s",
1556 : geomField->GetNameRef(), tableName_.c_str(),
1557 : OGRGeometryTypeToName(geomField->GetType()));
1558 1 : return OGRERR_FAILURE;
1559 : }
1560 :
1561 3 : EnsureInitialized();
1562 :
1563 3 : if (featureDefn_->GetGeomFieldIndex(geomField->GetNameRef()) >= 0)
1564 : {
1565 1 : CPLError(
1566 : CE_Failure, CPLE_AppDefined,
1567 : "CreateGeomField() called with an already existing field name: %s",
1568 : geomField->GetNameRef());
1569 1 : return OGRERR_FAILURE;
1570 : }
1571 :
1572 2 : FlushPendingBatches(false);
1573 :
1574 2 : int srid = dataSource_->GetSrsId(geomField->GetSpatialRef());
1575 2 : if (srid == UNDETERMINED_SRID)
1576 : {
1577 1 : CPLError(CE_Failure, CPLE_AppDefined,
1578 : "Unable to determine the srs-id for field name: %s",
1579 : geomField->GetNameRef());
1580 1 : return OGRERR_FAILURE;
1581 : }
1582 :
1583 2 : CPLString clmName(geomField->GetNameRef());
1584 1 : if (launderColumnNames_)
1585 : {
1586 1 : auto nameRes = dataSource_->LaunderName(geomField->GetNameRef());
1587 1 : if (nameRes.first != OGRERR_NONE)
1588 0 : return nameRes.first;
1589 1 : clmName.swap(nameRes.second);
1590 : }
1591 :
1592 1 : if (clmName.empty())
1593 1 : clmName = FindGeomFieldName(*featureDefn_);
1594 :
1595 1 : CPLString sql = CPLString().Printf(
1596 : "ALTER TABLE %s ADD(%s ST_GEOMETRY(%d))",
1597 2 : GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
1598 4 : QuotedIdentifier(clmName).c_str(), srid);
1599 :
1600 : try
1601 : {
1602 1 : dataSource_->ExecuteSQL(sql.c_str());
1603 : }
1604 0 : catch (const odbc::Exception &ex)
1605 : {
1606 0 : CPLError(CE_Failure, CPLE_AppDefined,
1607 : "Failed to execute CreateGeomField() with field name '%s': %s",
1608 0 : geomField->GetNameRef(), ex.what());
1609 0 : return OGRERR_FAILURE;
1610 : }
1611 :
1612 : auto newGeomField = std::make_unique<OGRGeomFieldDefn>(
1613 1 : clmName.c_str(), geomField->GetType());
1614 1 : newGeomField->SetNullable(geomField->IsNullable());
1615 1 : newGeomField->SetSpatialRef(geomField->GetSpatialRef());
1616 2 : geomColumns_.push_back({newGeomField->GetNameRef(), newGeomField->GetType(),
1617 1 : srid, newGeomField->IsNullable() == TRUE});
1618 1 : featureDefn_->AddGeomFieldDefn(std::move(newGeomField));
1619 :
1620 1 : ColumnsChanged();
1621 :
1622 1 : return OGRERR_NONE;
1623 : }
1624 :
1625 : /************************************************************************/
1626 : /* DeleteField() */
1627 : /************************************************************************/
1628 :
1629 0 : OGRErr OGRHanaTableLayer::DeleteField(int field)
1630 : {
1631 0 : if (!updateMode_)
1632 : {
1633 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1634 : "DeleteField");
1635 0 : return OGRERR_FAILURE;
1636 : }
1637 :
1638 0 : if (field < 0 || field >= featureDefn_->GetFieldCount())
1639 : {
1640 0 : CPLError(CE_Failure, CPLE_NotSupported, "Field index is out of range");
1641 0 : return OGRERR_FAILURE;
1642 : }
1643 :
1644 0 : EnsureInitialized();
1645 :
1646 0 : FlushPendingBatches(false);
1647 :
1648 0 : CPLString clmName = featureDefn_->GetFieldDefn(field)->GetNameRef();
1649 0 : CPLString sql = CPLString().Printf(
1650 : "ALTER TABLE %s DROP (%s)",
1651 0 : GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
1652 0 : QuotedIdentifier(clmName).c_str());
1653 :
1654 : try
1655 : {
1656 0 : dataSource_->ExecuteSQL(sql.c_str());
1657 : }
1658 0 : catch (const odbc::Exception &ex)
1659 : {
1660 0 : CPLError(CE_Failure, CPLE_AppDefined, "Failed to drop column %s: %s",
1661 0 : clmName.c_str(), ex.what());
1662 0 : return OGRERR_FAILURE;
1663 : }
1664 :
1665 : auto it = std::find_if(attrColumns_.begin(), attrColumns_.end(),
1666 0 : [&](const AttributeColumnDescription &cd)
1667 0 : { return cd.name == clmName; });
1668 0 : attrColumns_.erase(it);
1669 0 : OGRErr ret = featureDefn_->DeleteFieldDefn(field);
1670 :
1671 0 : ColumnsChanged();
1672 :
1673 0 : return ret;
1674 : }
1675 :
1676 : /************************************************************************/
1677 : /* AlterFieldDefn() */
1678 : /************************************************************************/
1679 :
1680 0 : OGRErr OGRHanaTableLayer::AlterFieldDefn(int field, OGRFieldDefn *newFieldDefn,
1681 : int flagsIn)
1682 : {
1683 0 : if (!updateMode_)
1684 : {
1685 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1686 : "AlterFieldDefn");
1687 0 : return OGRERR_FAILURE;
1688 : }
1689 :
1690 0 : EnsureInitialized();
1691 :
1692 0 : if (field < 0 || field >= featureDefn_->GetFieldCount())
1693 : {
1694 0 : CPLError(CE_Failure, CPLE_NotSupported, "Field index is out of range");
1695 0 : return OGRERR_FAILURE;
1696 : }
1697 :
1698 0 : OGRFieldDefn *fieldDefn = featureDefn_->GetFieldDefn(field);
1699 :
1700 0 : int columnDescIdx = -1;
1701 0 : for (size_t i = 0; i < attrColumns_.size(); ++i)
1702 : {
1703 0 : if (EQUAL(attrColumns_[i].name.c_str(), fieldDefn->GetNameRef()))
1704 : {
1705 0 : columnDescIdx = static_cast<int>(i);
1706 0 : break;
1707 : }
1708 : }
1709 :
1710 0 : if (columnDescIdx < 0)
1711 : {
1712 0 : CPLError(CE_Failure, CPLE_NotSupported,
1713 : "Column description cannot be found");
1714 0 : return OGRERR_FAILURE;
1715 : }
1716 :
1717 0 : CPLString clmName(newFieldDefn->GetNameRef());
1718 :
1719 0 : if (launderColumnNames_)
1720 : {
1721 0 : auto nameRes = dataSource_->LaunderName(newFieldDefn->GetNameRef());
1722 0 : if (nameRes.first != OGRERR_NONE)
1723 0 : return nameRes.first;
1724 0 : clmName.swap(nameRes.second);
1725 : }
1726 :
1727 0 : FlushPendingBatches(false);
1728 :
1729 0 : ColumnTypeInfo columnTypeInfo = GetColumnTypeInfo(*newFieldDefn);
1730 :
1731 : try
1732 : {
1733 0 : if ((flagsIn & ALTER_NAME_FLAG) &&
1734 0 : (strcmp(fieldDefn->GetNameRef(), newFieldDefn->GetNameRef()) != 0))
1735 : {
1736 0 : CPLString sql = CPLString().Printf(
1737 : "RENAME COLUMN %s TO %s",
1738 0 : GetFullColumnNameQuoted(schemaName_, tableName_,
1739 : fieldDefn->GetNameRef())
1740 : .c_str(),
1741 0 : QuotedIdentifier(clmName).c_str());
1742 0 : dataSource_->ExecuteSQL(sql.c_str());
1743 : }
1744 :
1745 0 : if ((flagsIn & ALTER_TYPE_FLAG) ||
1746 0 : (flagsIn & ALTER_WIDTH_PRECISION_FLAG) ||
1747 0 : (flagsIn & ALTER_NULLABLE_FLAG) || (flagsIn & ALTER_DEFAULT_FLAG))
1748 : {
1749 0 : CPLString fieldTypeDef = GetColumnDefinition(columnTypeInfo);
1750 0 : if ((flagsIn & ALTER_NULLABLE_FLAG) &&
1751 0 : fieldDefn->IsNullable() != newFieldDefn->IsNullable())
1752 : {
1753 0 : if (fieldDefn->IsNullable())
1754 0 : fieldTypeDef += " NULL";
1755 : else
1756 0 : fieldTypeDef += " NOT NULL";
1757 : }
1758 :
1759 0 : if ((flagsIn & ALTER_DEFAULT_FLAG) &&
1760 0 : ((fieldDefn->GetDefault() == nullptr &&
1761 0 : newFieldDefn->GetDefault() != nullptr) ||
1762 0 : (fieldDefn->GetDefault() != nullptr &&
1763 0 : newFieldDefn->GetDefault() == nullptr) ||
1764 0 : (fieldDefn->GetDefault() != nullptr &&
1765 0 : newFieldDefn->GetDefault() != nullptr &&
1766 0 : strcmp(fieldDefn->GetDefault(), newFieldDefn->GetDefault()) !=
1767 : 0)))
1768 : {
1769 : fieldTypeDef +=
1770 0 : " DEFAULT " + ((fieldDefn->GetType() == OFTString)
1771 0 : ? Literal(newFieldDefn->GetDefault())
1772 0 : : CPLString(newFieldDefn->GetDefault()));
1773 : }
1774 :
1775 0 : CPLString sql = CPLString().Printf(
1776 : "ALTER TABLE %s ALTER(%s %s)",
1777 0 : GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
1778 0 : QuotedIdentifier(clmName).c_str(), fieldTypeDef.c_str());
1779 :
1780 0 : dataSource_->ExecuteSQL(sql.c_str());
1781 : }
1782 : }
1783 0 : catch (const odbc::Exception &ex)
1784 : {
1785 0 : CPLError(CE_Failure, CPLE_AppDefined, "Failed to alter field %s: %s",
1786 0 : fieldDefn->GetNameRef(), ex.what());
1787 0 : return OGRERR_FAILURE;
1788 : }
1789 :
1790 : // Update field definition and column description
1791 :
1792 0 : AttributeColumnDescription &attrClmDesc = attrColumns_[columnDescIdx];
1793 :
1794 0 : if (flagsIn & ALTER_NAME_FLAG)
1795 : {
1796 0 : fieldDefn->SetName(clmName.c_str());
1797 0 : attrClmDesc.name.assign(clmName.c_str());
1798 : }
1799 :
1800 0 : if (flagsIn & ALTER_TYPE_FLAG)
1801 : {
1802 0 : fieldDefn->SetSubType(OFSTNone);
1803 0 : fieldDefn->SetType(newFieldDefn->GetType());
1804 0 : fieldDefn->SetSubType(newFieldDefn->GetSubType());
1805 0 : attrClmDesc.isArray = IsArrayField(newFieldDefn->GetType());
1806 0 : attrClmDesc.type = columnTypeInfo.type;
1807 0 : attrClmDesc.typeName = columnTypeInfo.name;
1808 : }
1809 :
1810 0 : if (flagsIn & ALTER_WIDTH_PRECISION_FLAG)
1811 : {
1812 0 : fieldDefn->SetWidth(newFieldDefn->GetWidth());
1813 0 : fieldDefn->SetPrecision(newFieldDefn->GetPrecision());
1814 0 : attrClmDesc.length = newFieldDefn->GetWidth();
1815 0 : attrClmDesc.scale = newFieldDefn->GetWidth();
1816 0 : attrClmDesc.precision = newFieldDefn->GetPrecision();
1817 : }
1818 :
1819 0 : if (flagsIn & ALTER_NULLABLE_FLAG)
1820 : {
1821 0 : fieldDefn->SetNullable(newFieldDefn->IsNullable());
1822 0 : attrClmDesc.isNullable = newFieldDefn->IsNullable();
1823 : }
1824 :
1825 0 : if (flagsIn & ALTER_DEFAULT_FLAG)
1826 : {
1827 0 : fieldDefn->SetDefault(newFieldDefn->GetDefault());
1828 0 : attrClmDesc.name.assign(newFieldDefn->GetDefault());
1829 : }
1830 :
1831 0 : ColumnsChanged();
1832 :
1833 0 : return OGRERR_NONE;
1834 : }
1835 :
1836 : /************************************************************************/
1837 : /* ClearBatches() */
1838 : /************************************************************************/
1839 :
1840 4 : void OGRHanaTableLayer::ClearBatches()
1841 : {
1842 4 : if (!insertFeatureStmtWithFID_.isNull())
1843 4 : insertFeatureStmtWithFID_->clearBatch();
1844 4 : if (!insertFeatureStmtWithoutFID_.isNull())
1845 1 : insertFeatureStmtWithoutFID_->clearBatch();
1846 4 : if (!updateFeatureStmt_.isNull())
1847 2 : updateFeatureStmt_->clearBatch();
1848 4 : }
1849 :
1850 : /************************************************************************/
1851 : /* ColumnsChanged() */
1852 : /************************************************************************/
1853 :
1854 65 : void OGRHanaTableLayer::ColumnsChanged()
1855 : {
1856 65 : ClearQueryStatement();
1857 65 : ResetReading();
1858 65 : ResetPreparedStatements();
1859 65 : }
1860 :
1861 : /************************************************************************/
1862 : /* SetCustomColumnTypes() */
1863 : /************************************************************************/
1864 :
1865 18 : void OGRHanaTableLayer::SetCustomColumnTypes(const char *columnTypes)
1866 : {
1867 18 : if (columnTypes == nullptr)
1868 17 : return;
1869 :
1870 1 : const char *ptr = columnTypes;
1871 1 : const char *start = ptr;
1872 42 : while (*ptr != '\0')
1873 : {
1874 41 : if (*ptr == '(')
1875 : {
1876 : // Skip commas inside brackets, for example decimal(20,5)
1877 12 : while (*ptr != '\0' && *ptr != ')')
1878 : {
1879 10 : ++ptr;
1880 : }
1881 : }
1882 :
1883 41 : ++ptr;
1884 :
1885 41 : if (*ptr == ',' || *ptr == '\0')
1886 : {
1887 3 : std::size_t len = static_cast<std::size_t>(ptr - start);
1888 3 : const char *sep = std::find(start, start + len, '=');
1889 3 : if (sep != nullptr)
1890 : {
1891 3 : std::size_t pos = static_cast<std::size_t>(sep - start);
1892 6 : customColumnDefs_.push_back(
1893 : {CPLString(start, pos),
1894 3 : CPLString(start + pos + 1, len - pos - 1)});
1895 : }
1896 :
1897 3 : start = ptr + 1;
1898 : }
1899 : }
1900 : }
1901 :
1902 : /************************************************************************/
1903 : /* StartTransaction() */
1904 : /************************************************************************/
1905 :
1906 11 : OGRErr OGRHanaTableLayer::StartTransaction()
1907 : {
1908 11 : return dataSource_->StartTransaction();
1909 : }
1910 :
1911 : /************************************************************************/
1912 : /* CommitTransaction() */
1913 : /************************************************************************/
1914 :
1915 7 : OGRErr OGRHanaTableLayer::CommitTransaction()
1916 : {
1917 7 : if (HasPendingBatches())
1918 : {
1919 0 : OGRErr err = ExecutePendingBatches(BatchOperation::ALL);
1920 0 : if (OGRERR_NONE != err)
1921 0 : return err;
1922 : }
1923 :
1924 7 : return dataSource_->CommitTransaction();
1925 : }
1926 :
1927 : /************************************************************************/
1928 : /* RollbackTransaction() */
1929 : /************************************************************************/
1930 :
1931 4 : OGRErr OGRHanaTableLayer::RollbackTransaction()
1932 : {
1933 4 : ClearBatches();
1934 4 : return dataSource_->RollbackTransaction();
1935 : }
1936 :
1937 : } // namespace OGRHANA
|