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