Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: SAP HANA Spatial Driver
4 : * Purpose: OGRHanaLayer 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 "ogrhanafeaturewriter.h"
15 : #include "ogrhanautils.h"
16 :
17 : #include <algorithm>
18 : #include <cmath>
19 : #include <limits>
20 : #include <sstream>
21 : #include <memory>
22 :
23 : #include "odbc/Exception.h"
24 : #include "odbc/PreparedStatement.h"
25 : #include "odbc/ResultSet.h"
26 : #include "odbc/Statement.h"
27 : #include "odbc/Types.h"
28 :
29 : namespace OGRHANA
30 : {
31 : namespace
32 : {
33 : /************************************************************************/
34 : /* Helper methods */
35 : /************************************************************************/
36 :
37 12 : CPLString BuildQuery(const char *source, const char *columns, const char *where,
38 : const char *orderBy, int limit)
39 : {
40 12 : std::ostringstream os;
41 12 : os << "SELECT " << columns << " FROM (" << source << ")";
42 12 : if (where != nullptr && strlen(where) > 0)
43 0 : os << " WHERE " << where;
44 12 : if (orderBy != nullptr && strlen(orderBy) > 0)
45 0 : os << " ORDER BY " << orderBy;
46 12 : if (limit >= 0)
47 0 : os << " LIMIT " << std::to_string(limit);
48 36 : return os.str();
49 : }
50 :
51 12 : CPLString BuildQuery(const char *source, const char *columns)
52 : {
53 12 : return BuildQuery(source, columns, nullptr, nullptr, -1);
54 : }
55 :
56 24 : CPLString BuildSpatialFilter(int dbVersion, const OGRGeometry &geom,
57 : const CPLString &clmName, int srid)
58 : {
59 24 : OGREnvelope env;
60 24 : geom.getEnvelope(&env);
61 :
62 46 : if ((std::isinf(env.MinX) || std::isinf(env.MinY) || std::isinf(env.MaxX) ||
63 22 : std::isinf(env.MaxY)))
64 2 : return "";
65 :
66 88 : auto clampValue = [](double v)
67 : {
68 88 : constexpr double MAX_VALUE = 1e+150;
69 88 : if (v < -MAX_VALUE)
70 4 : return -MAX_VALUE;
71 84 : else if (v > MAX_VALUE)
72 4 : return MAX_VALUE;
73 80 : return v;
74 : };
75 :
76 22 : double minX = clampValue(env.MinX);
77 22 : double minY = clampValue(env.MinY);
78 22 : double maxX = clampValue(env.MaxX);
79 22 : double maxY = clampValue(env.MaxY);
80 :
81 : // TODO: add support for non-rectangular filter, see m_bFilterIsEnvelope
82 : // flag.
83 22 : if (dbVersion == 1)
84 0 : return CPLString().Printf(
85 : "\"%s\".ST_IntersectsRect(ST_GeomFromText('POINT(%.17g %.17g)', "
86 : "%d), ST_GeomFromText('POINT(%.17g %.17g)', %d)) = 1",
87 0 : clmName.c_str(), minX, minY, srid, maxX, maxY, srid);
88 : else
89 44 : return CPLString().Printf(
90 : "\"%s\".ST_IntersectsRectPlanar(ST_GeomFromText('POINT(%.17g "
91 : "%.17g)', %d), ST_GeomFromText('POINT(%.17g %.17g)', %d)) = 1",
92 22 : clmName.c_str(), minX, minY, srid, maxX, maxY, srid);
93 : }
94 :
95 : std::unique_ptr<OGRFieldDefn>
96 166 : CreateFieldDefn(const AttributeColumnDescription &columnDesc)
97 : {
98 166 : bool setFieldSize = false;
99 166 : bool setFieldPrecision = false;
100 :
101 166 : OGRFieldType ogrFieldType = OFTString;
102 166 : OGRFieldSubType ogrFieldSubType = OGRFieldSubType::OFSTNone;
103 166 : int width = columnDesc.length;
104 :
105 166 : switch (columnDesc.type)
106 : {
107 2 : case QGRHanaDataTypes::Bit:
108 : case QGRHanaDataTypes::Boolean:
109 2 : ogrFieldType = columnDesc.isArray ? OFTIntegerList : OFTInteger;
110 2 : ogrFieldSubType = OGRFieldSubType::OFSTBoolean;
111 2 : break;
112 3 : case QGRHanaDataTypes::TinyInt:
113 : case QGRHanaDataTypes::SmallInt:
114 3 : ogrFieldType = columnDesc.isArray ? OFTIntegerList : OFTInteger;
115 3 : ogrFieldSubType = OGRFieldSubType::OFSTInt16;
116 3 : break;
117 55 : case QGRHanaDataTypes::Integer:
118 55 : ogrFieldType = columnDesc.isArray ? OFTIntegerList : OFTInteger;
119 55 : break;
120 26 : case QGRHanaDataTypes::BigInt:
121 26 : ogrFieldType = columnDesc.isArray ? OFTInteger64List : OFTInteger64;
122 26 : break;
123 23 : case QGRHanaDataTypes::Double:
124 : case QGRHanaDataTypes::Real:
125 : case QGRHanaDataTypes::Float:
126 23 : ogrFieldType = columnDesc.isArray ? OFTRealList : OFTReal;
127 23 : if (columnDesc.type != QGRHanaDataTypes::Double)
128 1 : ogrFieldSubType = OGRFieldSubType::OFSTFloat32;
129 23 : break;
130 3 : case QGRHanaDataTypes::Decimal:
131 : case QGRHanaDataTypes::Numeric:
132 3 : ogrFieldType = columnDesc.isArray ? OFTRealList : OFTReal;
133 3 : setFieldPrecision = true;
134 3 : break;
135 43 : case QGRHanaDataTypes::Char:
136 : case QGRHanaDataTypes::VarChar:
137 : case QGRHanaDataTypes::LongVarChar:
138 : case QGRHanaDataTypes::WChar:
139 : case QGRHanaDataTypes::WVarChar:
140 : case QGRHanaDataTypes::WLongVarChar:
141 : // Note: OFTWideString is deprecated
142 43 : ogrFieldType = columnDesc.isArray ? OFTStringList : OFTString;
143 43 : setFieldSize = true;
144 43 : break;
145 1 : case QGRHanaDataTypes::Date:
146 : case QGRHanaDataTypes::TypeDate:
147 1 : ogrFieldType = OFTDate;
148 1 : break;
149 1 : case QGRHanaDataTypes::Time:
150 : case QGRHanaDataTypes::TypeTime:
151 1 : ogrFieldType = OFTTime;
152 1 : break;
153 2 : case QGRHanaDataTypes::Timestamp:
154 : case QGRHanaDataTypes::TypeTimestamp:
155 2 : ogrFieldType = OFTDateTime;
156 2 : break;
157 1 : case QGRHanaDataTypes::Binary:
158 : case QGRHanaDataTypes::VarBinary:
159 : case QGRHanaDataTypes::LongVarBinary:
160 1 : ogrFieldType = OFTBinary;
161 1 : setFieldSize = true;
162 1 : break;
163 6 : case QGRHanaDataTypes::RealVector:
164 6 : ogrFieldType = OFTBinary;
165 : // The .fvecs format is used for REAL_VECTOR with the dimension
166 : // lying in the range [1,65000].
167 12 : width = (columnDesc.precision == 0)
168 6 : ? 65000
169 3 : : 4 * (columnDesc.precision + 1);
170 6 : setFieldSize = true;
171 6 : break;
172 0 : default:
173 0 : break;
174 : }
175 :
176 166 : if (columnDesc.isArray && !IsArrayField(ogrFieldType))
177 0 : CPLError(CE_Failure, CPLE_AppDefined,
178 : "Array of type %s in column %s is not supported",
179 : columnDesc.typeName.c_str(), columnDesc.name.c_str());
180 :
181 : auto field =
182 166 : std::make_unique<OGRFieldDefn>(columnDesc.name.c_str(), ogrFieldType);
183 166 : field->SetSubType(ogrFieldSubType);
184 166 : field->SetNullable(columnDesc.isNullable);
185 166 : if (!columnDesc.isArray)
186 : {
187 155 : if (setFieldSize)
188 47 : field->SetWidth(width);
189 155 : if (setFieldPrecision)
190 : {
191 3 : field->SetWidth(columnDesc.precision);
192 3 : field->SetPrecision(columnDesc.scale);
193 : }
194 : }
195 166 : if (columnDesc.defaultValue.empty())
196 154 : field->SetDefault(nullptr);
197 : else
198 12 : field->SetDefault(columnDesc.defaultValue.c_str());
199 332 : return field;
200 : }
201 :
202 568 : OGRGeometry *CreateGeometryFromWkb(const void *data, std::size_t size)
203 : {
204 568 : if (size > static_cast<std::size_t>(std::numeric_limits<int>::max()))
205 0 : CPLError(CE_Failure, CPLE_AppDefined, "createFromWkb(): %s",
206 : "Geometry size is larger than maximum integer value");
207 :
208 568 : int len = static_cast<int>(size);
209 :
210 568 : OGRGeometry *geom = nullptr;
211 568 : OGRErr err = OGRGeometryFactory::createFromWkb(data, nullptr, &geom, len);
212 :
213 568 : if (OGRERR_NONE == err)
214 568 : return geom;
215 :
216 0 : auto cplError = [](const char *message)
217 0 : { CPLError(CE_Failure, CPLE_AppDefined, "ReadFeature(): %s", message); };
218 :
219 0 : switch (err)
220 : {
221 0 : case OGRERR_NOT_ENOUGH_DATA:
222 0 : cplError("Not enough data to deserialize");
223 0 : return nullptr;
224 0 : case OGRERR_UNSUPPORTED_GEOMETRY_TYPE:
225 0 : cplError("Unsupported geometry type");
226 0 : return nullptr;
227 0 : case OGRERR_CORRUPT_DATA:
228 0 : cplError("Corrupt data");
229 0 : return nullptr;
230 0 : default:
231 0 : cplError("Unrecognized error");
232 0 : return nullptr;
233 : }
234 : }
235 :
236 : } // anonymous namespace
237 :
238 : /************************************************************************/
239 : /* OGRHanaLayer() */
240 : /************************************************************************/
241 :
242 498 : OGRHanaLayer::OGRHanaLayer(OGRHanaDataSource *datasource)
243 : : dataSource_(datasource), rawQuery_(""), queryStatement_(""),
244 498 : whereClause_("")
245 : {
246 498 : }
247 :
248 : /************************************************************************/
249 : /* ~OGRHanaLayer() */
250 : /************************************************************************/
251 :
252 495 : OGRHanaLayer::~OGRHanaLayer()
253 : {
254 495 : if (featureDefn_)
255 52 : featureDefn_->Release();
256 495 : }
257 :
258 1682 : void OGRHanaLayer::EnsureInitialized()
259 : {
260 1682 : if (initialized_)
261 1627 : return;
262 :
263 55 : OGRErr err = Initialize();
264 55 : if (OGRERR_NONE != err)
265 : {
266 0 : CPLError(CE_Failure, CPLE_AppDefined, "Failed to initialize layer: %s",
267 0 : GetName());
268 : }
269 55 : initialized_ = (OGRERR_NONE == err);
270 : }
271 :
272 : /************************************************************************/
273 : /* ClearQueryStatement() */
274 : /************************************************************************/
275 :
276 204 : void OGRHanaLayer::ClearQueryStatement()
277 : {
278 204 : queryStatement_.clear();
279 204 : }
280 :
281 : /************************************************************************/
282 : /* GetQueryStatement() */
283 : /************************************************************************/
284 :
285 182 : const CPLString &OGRHanaLayer::GetQueryStatement()
286 : {
287 182 : if (!queryStatement_.empty())
288 59 : return queryStatement_;
289 :
290 123 : EnsureInitialized();
291 :
292 123 : if (!geomColumns_.empty())
293 : {
294 108 : std::vector<CPLString> columns;
295 216 : for (const GeometryColumnDescription &geometryColumnDesc : geomColumns_)
296 324 : columns.push_back(QuotedIdentifier(geometryColumnDesc.name) +
297 216 : ".ST_AsBinary() AS " +
298 216 : QuotedIdentifier(geometryColumnDesc.name));
299 :
300 550 : for (const AttributeColumnDescription &attributeColumnDesc :
301 658 : attrColumns_)
302 550 : columns.push_back(QuotedIdentifier(attributeColumnDesc.name));
303 :
304 216 : queryStatement_ = CPLString().Printf(
305 216 : "SELECT %s FROM (%s) %s", JoinStrings(columns, ", ").c_str(),
306 216 : rawQuery_.c_str(), whereClause_.c_str());
307 : }
308 : else
309 : {
310 15 : if (whereClause_.empty())
311 15 : queryStatement_ = rawQuery_;
312 : else
313 : queryStatement_ =
314 0 : CPLString().Printf("SELECT * FROM (%s) %s", rawQuery_.c_str(),
315 0 : whereClause_.c_str());
316 : }
317 :
318 123 : return queryStatement_;
319 : }
320 :
321 : /************************************************************************/
322 : /* BuildWhereClause() */
323 : /************************************************************************/
324 :
325 139 : void OGRHanaLayer::BuildWhereClause()
326 : {
327 139 : whereClause_ = "";
328 :
329 278 : CPLString spatialFilter;
330 139 : if (m_poFilterGeom != nullptr)
331 : {
332 24 : EnsureInitialized();
333 :
334 24 : OGRGeomFieldDefn *geomFieldDefn = nullptr;
335 24 : if (featureDefn_->GetGeomFieldCount() != 0)
336 24 : geomFieldDefn = featureDefn_->GetGeomFieldDefn(m_iGeomFieldFilter);
337 :
338 24 : if (geomFieldDefn != nullptr)
339 : {
340 : const GeometryColumnDescription &geomClmDesc =
341 24 : geomColumns_[static_cast<std::size_t>(m_iGeomFieldFilter)];
342 48 : spatialFilter = BuildSpatialFilter(
343 48 : dataSource_->GetHanaVersion().major(), *m_poFilterGeom,
344 48 : geomClmDesc.name, geomClmDesc.srid);
345 : }
346 : }
347 :
348 139 : if (!attrFilter_.empty())
349 : {
350 32 : whereClause_ = " WHERE " + attrFilter_;
351 32 : if (!spatialFilter.empty())
352 4 : whereClause_ += " AND " + spatialFilter;
353 : }
354 107 : else if (!spatialFilter.empty())
355 18 : whereClause_ = " WHERE " + spatialFilter;
356 139 : }
357 :
358 : /************************************************************************/
359 : /* EnsureBufferCapacity() */
360 : /************************************************************************/
361 :
362 1273 : void OGRHanaLayer::EnsureBufferCapacity(std::size_t size)
363 : {
364 1273 : if (size > dataBuffer_.size())
365 67 : dataBuffer_.resize(size);
366 1273 : }
367 :
368 : /************************************************************************/
369 : /* GetNextFeatureInternal() */
370 : /************************************************************************/
371 :
372 655 : OGRFeature *OGRHanaLayer::GetNextFeatureInternal()
373 : {
374 655 : if (resultSet_.isNull())
375 : {
376 127 : const CPLString &queryStatement = GetQueryStatement();
377 127 : CPLAssert(!queryStatement.empty());
378 :
379 : try
380 : {
381 127 : odbc::StatementRef stmt = dataSource_->CreateStatement();
382 127 : resultSet_ = stmt->executeQuery(queryStatement.c_str());
383 : }
384 0 : catch (const odbc::Exception &ex)
385 : {
386 0 : CPLError(CE_Failure, CPLE_AppDefined,
387 0 : "Failed to execute query : %s", ex.what());
388 0 : return nullptr;
389 : }
390 : }
391 :
392 655 : return ReadFeature();
393 : }
394 :
395 : /************************************************************************/
396 : /* GetGeometryColumnSrid() */
397 : /************************************************************************/
398 :
399 12 : int OGRHanaLayer::GetGeometryColumnSrid(int columnIndex) const
400 : {
401 24 : if (columnIndex < 0 ||
402 12 : static_cast<std::size_t>(columnIndex) >= geomColumns_.size())
403 0 : return -1;
404 12 : return geomColumns_[static_cast<std::size_t>(columnIndex)].srid;
405 : }
406 :
407 : /************************************************************************/
408 : /* ReadFeature() */
409 : /************************************************************************/
410 :
411 655 : OGRFeature *OGRHanaLayer::ReadFeature()
412 : {
413 655 : if (!resultSet_->next())
414 54 : return nullptr;
415 :
416 1202 : auto feature = std::make_unique<OGRFeature>(featureDefn_);
417 601 : feature->SetFID(nextFeatureId_++);
418 :
419 601 : unsigned short paramIndex = 0;
420 :
421 : // Read geometries
422 1174 : for (std::size_t i = 0; i < geomColumns_.size(); ++i)
423 : {
424 573 : ++paramIndex;
425 573 : int geomIndex = static_cast<int>(i);
426 :
427 : OGRGeomFieldDefn *geomFieldDef =
428 573 : featureDefn_->GetGeomFieldDefn(geomIndex);
429 :
430 573 : if (geomFieldDef->IsIgnored())
431 0 : continue;
432 :
433 573 : std::size_t bufLength = resultSet_->getBinaryLength(paramIndex);
434 573 : if (bufLength == 0 || bufLength == odbc::ResultSet::NULL_DATA)
435 : {
436 5 : feature->SetGeomFieldDirectly(geomIndex, nullptr);
437 5 : continue;
438 : }
439 :
440 568 : OGRGeometry *geom = nullptr;
441 568 : if (bufLength != odbc::ResultSet::UNKNOWN_LENGTH)
442 : {
443 568 : EnsureBufferCapacity(bufLength);
444 568 : resultSet_->getBinaryData(paramIndex, dataBuffer_.data(),
445 : bufLength);
446 : geom =
447 568 : CreateGeometryFromWkb(dataBuffer_.data(), dataBuffer_.size());
448 : }
449 : else
450 : {
451 0 : odbc::Binary wkb = resultSet_->getBinary(paramIndex);
452 0 : if (!wkb.isNull() && wkb->size() > 0)
453 0 : geom = CreateGeometryFromWkb(
454 0 : static_cast<const void *>(wkb->data()), wkb->size());
455 : }
456 :
457 568 : if (geom != nullptr)
458 568 : geom->assignSpatialReference(geomFieldDef->GetSpatialRef());
459 568 : feature->SetGeomFieldDirectly(geomIndex, geom);
460 : }
461 :
462 : // Read feature attributes
463 601 : OGRHanaFeatureWriter featWriter(*feature);
464 601 : int fieldIndex = -1;
465 3524 : for (const AttributeColumnDescription &clmDesc : attrColumns_)
466 : {
467 2923 : ++paramIndex;
468 :
469 2923 : if (clmDesc.isFeatureID)
470 : {
471 586 : if (clmDesc.type == QGRHanaDataTypes::Integer)
472 : {
473 586 : odbc::Int val = resultSet_->getInt(paramIndex);
474 586 : if (!val.isNull())
475 586 : feature->SetFID(static_cast<GIntBig>(*val));
476 : }
477 0 : else if (clmDesc.type == QGRHanaDataTypes::BigInt)
478 : {
479 0 : odbc::Long val = resultSet_->getLong(paramIndex);
480 0 : if (!val.isNull())
481 0 : feature->SetFID(static_cast<GIntBig>(*val));
482 : }
483 586 : continue;
484 : }
485 :
486 2337 : ++fieldIndex;
487 :
488 2337 : OGRFieldDefn *fieldDefn = featureDefn_->GetFieldDefn(fieldIndex);
489 2337 : if (fieldDefn->IsIgnored())
490 0 : continue;
491 :
492 2337 : if (clmDesc.isArray)
493 : {
494 22 : odbc::Binary val = resultSet_->getBinary(paramIndex);
495 11 : if (val.isNull())
496 : {
497 0 : feature->SetFieldNull(fieldIndex);
498 0 : continue;
499 : }
500 :
501 11 : switch (clmDesc.type)
502 : {
503 1 : case QGRHanaDataTypes::Boolean:
504 1 : featWriter.SetFieldValueAsArray<uint8_t, int32_t>(
505 : fieldIndex, val);
506 1 : break;
507 0 : case QGRHanaDataTypes::TinyInt:
508 0 : featWriter.SetFieldValueAsArray<uint8_t, int32_t>(
509 : fieldIndex, val);
510 0 : break;
511 1 : case QGRHanaDataTypes::SmallInt:
512 1 : featWriter.SetFieldValueAsArray<int16_t, int32_t>(
513 : fieldIndex, val);
514 1 : break;
515 2 : case QGRHanaDataTypes::Integer:
516 2 : featWriter.SetFieldValueAsArray<int32_t, int32_t>(
517 : fieldIndex, val);
518 2 : break;
519 2 : case QGRHanaDataTypes::BigInt:
520 2 : featWriter.SetFieldValueAsArray<GIntBig, GIntBig>(
521 : fieldIndex, val);
522 2 : break;
523 0 : case QGRHanaDataTypes::Float:
524 : case QGRHanaDataTypes::Real:
525 0 : featWriter.SetFieldValueAsArray<float, double>(fieldIndex,
526 : val);
527 0 : break;
528 2 : case QGRHanaDataTypes::Double:
529 2 : featWriter.SetFieldValueAsArray<double, double>(fieldIndex,
530 : val);
531 2 : break;
532 3 : case QGRHanaDataTypes::Char:
533 : case QGRHanaDataTypes::VarChar:
534 : case QGRHanaDataTypes::LongVarChar:
535 : case QGRHanaDataTypes::WChar:
536 : case QGRHanaDataTypes::WVarChar:
537 : case QGRHanaDataTypes::WLongVarChar:
538 3 : featWriter.SetFieldValueAsStringArray(fieldIndex, val);
539 3 : break;
540 : }
541 :
542 11 : continue;
543 : }
544 :
545 2326 : switch (clmDesc.type)
546 : {
547 1 : case QGRHanaDataTypes::Bit:
548 : case QGRHanaDataTypes::Boolean:
549 : {
550 1 : odbc::Boolean val = resultSet_->getBoolean(paramIndex);
551 1 : featWriter.SetFieldValue(fieldIndex, val);
552 : }
553 1 : break;
554 0 : case QGRHanaDataTypes::TinyInt:
555 : {
556 0 : odbc::Byte val = resultSet_->getByte(paramIndex);
557 0 : featWriter.SetFieldValue(fieldIndex, val);
558 : }
559 0 : break;
560 1 : case QGRHanaDataTypes::SmallInt:
561 : {
562 1 : odbc::Short val = resultSet_->getShort(paramIndex);
563 1 : featWriter.SetFieldValue(fieldIndex, val);
564 : }
565 1 : break;
566 2 : case QGRHanaDataTypes::Integer:
567 : {
568 2 : odbc::Int val = resultSet_->getInt(paramIndex);
569 2 : featWriter.SetFieldValue(fieldIndex, val);
570 : }
571 2 : break;
572 588 : case QGRHanaDataTypes::BigInt:
573 : {
574 588 : odbc::Long val = resultSet_->getLong(paramIndex);
575 588 : featWriter.SetFieldValue(fieldIndex, val);
576 : }
577 588 : break;
578 1 : case QGRHanaDataTypes::Real:
579 : case QGRHanaDataTypes::Float:
580 : {
581 1 : odbc::Float val = resultSet_->getFloat(paramIndex);
582 1 : featWriter.SetFieldValue(fieldIndex, val);
583 : }
584 1 : break;
585 573 : case QGRHanaDataTypes::Double:
586 : {
587 573 : odbc::Double val = resultSet_->getDouble(paramIndex);
588 573 : featWriter.SetFieldValue(fieldIndex, val);
589 : }
590 573 : break;
591 1 : case QGRHanaDataTypes::Decimal:
592 : case QGRHanaDataTypes::Numeric:
593 : {
594 2 : odbc::Decimal val = resultSet_->getDecimal(paramIndex);
595 1 : featWriter.SetFieldValue(fieldIndex, val);
596 : }
597 1 : break;
598 1146 : case QGRHanaDataTypes::Char:
599 : case QGRHanaDataTypes::VarChar:
600 : case QGRHanaDataTypes::LongVarChar:
601 : // Note: NVARCHAR data type is converted to UTF-8 on the HANA side
602 : // when using a connection setting CHAR_AS_UTF8=1.
603 : case QGRHanaDataTypes::WChar:
604 : case QGRHanaDataTypes::WVarChar:
605 : case QGRHanaDataTypes::WLongVarChar:
606 : {
607 1146 : std::size_t len = resultSet_->getStringLength(paramIndex);
608 1146 : if (len == odbc::ResultSet::NULL_DATA)
609 570 : feature->SetFieldNull(fieldIndex);
610 576 : else if (len == 0)
611 0 : feature->SetField(fieldIndex, "");
612 576 : else if (len != odbc::ResultSet::UNKNOWN_LENGTH)
613 : {
614 576 : EnsureBufferCapacity(len + 1);
615 576 : resultSet_->getStringData(paramIndex, dataBuffer_.data(),
616 : len + 1);
617 576 : featWriter.SetFieldValue(fieldIndex, dataBuffer_.data());
618 : }
619 : else
620 : {
621 0 : odbc::String data = resultSet_->getString(paramIndex);
622 0 : featWriter.SetFieldValue(fieldIndex, data);
623 : }
624 : }
625 1146 : break;
626 7 : case QGRHanaDataTypes::Binary:
627 : case QGRHanaDataTypes::VarBinary:
628 : case QGRHanaDataTypes::LongVarBinary:
629 : case QGRHanaDataTypes::RealVector:
630 : {
631 7 : std::size_t len = resultSet_->getBinaryLength(paramIndex);
632 7 : if (len == 0)
633 0 : feature->SetField(fieldIndex, 0,
634 : static_cast<GByte *>(nullptr));
635 7 : else if (len == odbc::ResultSet::NULL_DATA)
636 0 : feature->SetFieldNull(fieldIndex);
637 7 : else if (len != odbc::ResultSet::UNKNOWN_LENGTH)
638 : {
639 7 : EnsureBufferCapacity(len);
640 7 : resultSet_->getBinaryData(paramIndex, dataBuffer_.data(),
641 : len);
642 7 : featWriter.SetFieldValue(fieldIndex, dataBuffer_.data(),
643 : len);
644 : }
645 : else
646 : {
647 0 : odbc::Binary binData = resultSet_->getBinary(paramIndex);
648 0 : featWriter.SetFieldValue(fieldIndex, binData);
649 : }
650 : }
651 7 : break;
652 1 : case QGRHanaDataTypes::Date:
653 : case QGRHanaDataTypes::TypeDate:
654 : {
655 1 : odbc::Date date = resultSet_->getDate(paramIndex);
656 1 : featWriter.SetFieldValue(fieldIndex, date);
657 : }
658 1 : break;
659 1 : case QGRHanaDataTypes::Time:
660 : case QGRHanaDataTypes::TypeTime:
661 : {
662 1 : odbc::Time time = resultSet_->getTime(paramIndex);
663 1 : featWriter.SetFieldValue(fieldIndex, time);
664 : }
665 1 : break;
666 4 : case QGRHanaDataTypes::Timestamp:
667 : case QGRHanaDataTypes::TypeTimestamp:
668 : {
669 : odbc::Timestamp timestamp =
670 4 : resultSet_->getTimestamp(paramIndex);
671 4 : featWriter.SetFieldValue(fieldIndex, timestamp);
672 : }
673 4 : break;
674 0 : default:
675 0 : break;
676 : }
677 : }
678 :
679 601 : return feature.release();
680 : }
681 :
682 : /************************************************************************/
683 : /* InitFeatureDefinition() */
684 : /************************************************************************/
685 :
686 55 : OGRErr OGRHanaLayer::InitFeatureDefinition(const CPLString &schemaName,
687 : const CPLString &tableName,
688 : const CPLString &query,
689 : const CPLString &featureDefName)
690 : {
691 55 : attrColumns_.clear();
692 55 : geomColumns_.clear();
693 55 : fidFieldIndex_ = OGRNullFID;
694 55 : fidFieldName_.clear();
695 55 : featureDefn_ = new OGRFeatureDefn(featureDefName.c_str());
696 55 : featureDefn_->Reference();
697 :
698 110 : std::vector<ColumnDescription> columnDescriptions;
699 : OGRErr err =
700 55 : dataSource_->GetQueryColumns(schemaName, query, columnDescriptions);
701 55 : if (err != OGRERR_NONE)
702 0 : return err;
703 :
704 : std::vector<CPLString> primKeys =
705 55 : dataSource_->GetTablePrimaryKeys(schemaName, tableName);
706 :
707 55 : if (featureDefn_->GetGeomFieldCount() == 1)
708 55 : featureDefn_->DeleteGeomFieldDefn(0);
709 :
710 254 : for (const ColumnDescription &clmDesc : columnDescriptions)
711 : {
712 199 : if (clmDesc.isGeometry)
713 : {
714 33 : const GeometryColumnDescription &geometryColumnDesc =
715 : clmDesc.geometryDescription;
716 :
717 : auto geomFieldDefn = std::make_unique<OGRGeomFieldDefn>(
718 66 : geometryColumnDesc.name.c_str(), geometryColumnDesc.type);
719 33 : geomFieldDefn->SetNullable(geometryColumnDesc.isNullable);
720 :
721 33 : if (geometryColumnDesc.srid >= 0)
722 : {
723 : OGRSpatialReference *srs =
724 30 : dataSource_->GetSrsById(geometryColumnDesc.srid);
725 30 : geomFieldDefn->SetSpatialRef(srs);
726 30 : srs->Release();
727 : }
728 33 : geomColumns_.push_back(geometryColumnDesc);
729 33 : featureDefn_->AddGeomFieldDefn(std::move(geomFieldDefn));
730 33 : continue;
731 : }
732 :
733 : AttributeColumnDescription attributeColumnDesc =
734 332 : clmDesc.attributeDescription;
735 332 : auto field = CreateFieldDefn(attributeColumnDesc);
736 :
737 276 : if ((field->GetType() == OFTInteger ||
738 332 : field->GetType() == OFTInteger64) &&
739 80 : (fidFieldIndex_ == OGRNullFID && primKeys.size() > 0))
740 : {
741 746 : for (const CPLString &key : primKeys)
742 : {
743 743 : if (key.compare(attributeColumnDesc.name) == 0)
744 : {
745 47 : fidFieldIndex_ = static_cast<int>(attrColumns_.size());
746 47 : fidFieldName_ = field->GetNameRef();
747 47 : attributeColumnDesc.isFeatureID = true;
748 47 : break;
749 : }
750 : }
751 : }
752 :
753 166 : if (!attributeColumnDesc.isFeatureID)
754 119 : featureDefn_->AddFieldDefn(field.get());
755 166 : attrColumns_.push_back(attributeColumnDesc);
756 : }
757 :
758 55 : return OGRERR_NONE;
759 : }
760 :
761 : /************************************************************************/
762 : /* ReadGeometryExtent() */
763 : /************************************************************************/
764 :
765 14 : void OGRHanaLayer::ReadGeometryExtent(int geomField, OGREnvelope *extent,
766 : int force)
767 : {
768 14 : EnsureInitialized();
769 :
770 14 : OGRGeomFieldDefn *geomFieldDef = featureDefn_->GetGeomFieldDefn(geomField);
771 14 : const char *clmName = geomFieldDef->GetNameRef();
772 28 : odbc::PreparedStatementRef stmt;
773 14 : if (!force && IsTableLayer())
774 : {
775 2 : auto names = dataSource_->FindSchemaAndTableNames(rawQuery_.c_str());
776 4 : stmt = dataSource_->PrepareStatement(
777 : "SELECT MIN_X, MIN_Y, MAX_X, MAX_Y FROM "
778 : "SYS.M_ST_GEOMETRY_COLUMNS WHERE SCHEMA_NAME=? AND TABLE_NAME=? "
779 2 : "AND COLUMN_NAME=?");
780 4 : stmt->setString(1, names.first == ""
781 4 : ? odbc::String(dataSource_->schemaName_)
782 : : odbc::String(names.first));
783 2 : stmt->setString(2, odbc::String(names.second));
784 2 : stmt->setString(3, odbc::String(clmName));
785 : }
786 : else
787 : {
788 12 : int srid = GetGeometryColumnSrid(geomField);
789 12 : if (dataSource_->IsSrsRoundEarth(srid))
790 : {
791 4 : CPLString quotedClmName = QuotedIdentifier(clmName);
792 : bool hasSrsPlanarEquivalent =
793 2 : dataSource_->HasSrsPlanarEquivalent(srid);
794 : CPLString geomColumn =
795 2 : !hasSrsPlanarEquivalent
796 : ? quotedClmName
797 4 : : CPLString().Printf("%s.ST_SRID(%d)",
798 : quotedClmName.c_str(),
799 6 : ToPlanarSRID(srid));
800 2 : CPLString columns = CPLString().Printf(
801 : "MIN(%s.ST_XMin()), MIN(%s.ST_YMin()), MAX(%s.ST_XMax()), "
802 : "MAX(%s.ST_YMax())",
803 : geomColumn.c_str(), geomColumn.c_str(), geomColumn.c_str(),
804 2 : geomColumn.c_str());
805 4 : stmt = dataSource_->PrepareStatement(
806 6 : BuildQuery(rawQuery_.c_str(), columns.c_str()));
807 : }
808 : else
809 : {
810 : CPLString columns =
811 10 : CPLString().Printf("ST_EnvelopeAggr(%s) AS ext",
812 30 : QuotedIdentifier(clmName).c_str());
813 10 : CPLString subQuery = BuildQuery(rawQuery_.c_str(), columns);
814 30 : stmt = dataSource_->PrepareStatement(CPLString().Printf(
815 : "SELECT "
816 : "ext.ST_XMin(),ext.ST_YMin(),ext.ST_XMax(),ext.ST_YMax() "
817 : "FROM (%s)",
818 20 : subQuery.c_str()));
819 : }
820 : }
821 :
822 14 : bool set = false;
823 14 : extent->MinX = 0.0;
824 14 : extent->MaxX = 0.0;
825 14 : extent->MinY = 0.0;
826 14 : extent->MaxY = 0.0;
827 :
828 28 : odbc::ResultSetRef rsExtent = stmt->executeQuery();
829 14 : if (rsExtent->next())
830 : {
831 14 : odbc::Double val = rsExtent->getDouble(1);
832 14 : if (!val.isNull())
833 : {
834 12 : extent->MinX = *val;
835 12 : extent->MinY = *rsExtent->getDouble(2);
836 12 : extent->MaxX = *rsExtent->getDouble(3);
837 12 : extent->MaxY = *rsExtent->getDouble(4);
838 12 : set = true;
839 : }
840 : }
841 :
842 14 : rsExtent->close();
843 14 : if (!set && !force)
844 1 : ReadGeometryExtent(geomField, extent, true);
845 14 : }
846 :
847 16 : bool OGRHanaLayer::IsFastExtentAvailable()
848 : {
849 16 : if (geomColumns_.empty())
850 0 : return false;
851 :
852 16 : switch (dataSource_->GetHanaVersion().major())
853 : {
854 0 : case 2:
855 0 : return dataSource_->GetHanaVersion() >= HanaVersion(2, 0, 80);
856 16 : case 4:
857 16 : return dataSource_->GetHanaCloudVersion() >=
858 32 : HanaVersion(2024, 2, 0);
859 : }
860 :
861 0 : return false;
862 : }
863 :
864 : /************************************************************************/
865 : /* ResetReading() */
866 : /************************************************************************/
867 :
868 307 : void OGRHanaLayer::ResetReading()
869 : {
870 307 : nextFeatureId_ = 0;
871 307 : resultSet_.reset();
872 307 : }
873 :
874 : /************************************************************************/
875 : /* GetExtent() */
876 : /************************************************************************/
877 :
878 17 : OGRErr OGRHanaLayer::GetExtent(int iGeomField, OGREnvelope *extent, int force)
879 : {
880 30 : if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ||
881 13 : GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone)
882 : {
883 4 : extent->MinX = 0.0;
884 4 : extent->MaxX = 0.0;
885 4 : extent->MinY = 0.0;
886 4 : extent->MaxY = 0.0;
887 :
888 4 : if (iGeomField != 0)
889 : {
890 4 : CPLError(CE_Failure, CPLE_AppDefined,
891 : "Invalid geometry field index : %d", iGeomField);
892 : }
893 4 : return OGRERR_FAILURE;
894 : }
895 :
896 : try
897 : {
898 13 : if (!force)
899 2 : force = !IsFastExtentAvailable();
900 13 : ReadGeometryExtent(iGeomField, extent, force);
901 :
902 13 : return OGRERR_NONE;
903 : }
904 0 : catch (const std::exception &ex)
905 : {
906 : CPLString clmName =
907 0 : (iGeomField < static_cast<int>(geomColumns_.size()))
908 0 : ? geomColumns_[static_cast<std::size_t>(geomColumns_.size())]
909 0 : .name
910 0 : : "unknown column";
911 0 : CPLError(CE_Failure, CPLE_AppDefined,
912 : "Unable to query extent of '%s' using fast method: %s",
913 0 : clmName.c_str(), ex.what());
914 : }
915 :
916 0 : if (iGeomField == 0)
917 0 : return OGRLayer::GetExtent(extent, force);
918 : else
919 0 : return OGRLayer::GetExtent(iGeomField, extent, force);
920 : }
921 :
922 : /************************************************************************/
923 : /* GetFeatureCount() */
924 : /************************************************************************/
925 :
926 55 : GIntBig OGRHanaLayer::GetFeatureCount(CPL_UNUSED int force)
927 : {
928 55 : EnsureInitialized();
929 :
930 55 : GIntBig ret = 0;
931 55 : CPLString sql = CPLString().Printf("SELECT COUNT(*) FROM (%s) AS tmp",
932 110 : GetQueryStatement().c_str());
933 110 : odbc::StatementRef stmt = dataSource_->CreateStatement();
934 55 : odbc::ResultSetRef rs = stmt->executeQuery(sql.c_str());
935 55 : if (rs->next())
936 55 : ret = *rs->getLong(1);
937 55 : rs->close();
938 110 : return ret;
939 : }
940 :
941 : /************************************************************************/
942 : /* GetLayerDefn() */
943 : /************************************************************************/
944 :
945 464 : OGRFeatureDefn *OGRHanaLayer::GetLayerDefn()
946 : {
947 464 : EnsureInitialized();
948 464 : return featureDefn_;
949 : }
950 :
951 : /************************************************************************/
952 : /* GetName() */
953 : /************************************************************************/
954 :
955 2 : const char *OGRHanaLayer::GetName()
956 : {
957 2 : return GetDescription();
958 : }
959 :
960 : /************************************************************************/
961 : /* GetNextFeature() */
962 : /************************************************************************/
963 :
964 655 : OGRFeature *OGRHanaLayer::GetNextFeature()
965 : {
966 655 : EnsureInitialized();
967 :
968 : while (true)
969 : {
970 655 : OGRFeature *feature = GetNextFeatureInternal();
971 655 : if (feature == nullptr)
972 54 : return nullptr;
973 :
974 1289 : if ((m_poFilterGeom == nullptr ||
975 1202 : FilterGeometry(feature->GetGeometryRef())) &&
976 601 : (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(feature)))
977 601 : return feature;
978 :
979 0 : delete feature;
980 0 : }
981 : }
982 :
983 : /************************************************************************/
984 : /* GetFIDColumn() */
985 : /************************************************************************/
986 :
987 96 : const char *OGRHanaLayer::GetFIDColumn()
988 : {
989 96 : EnsureInitialized();
990 96 : return fidFieldName_.c_str();
991 : }
992 :
993 : /************************************************************************/
994 : /* SetAttributeFilter() */
995 : /************************************************************************/
996 :
997 119 : OGRErr OGRHanaLayer::SetAttributeFilter(const char *pszQuery)
998 : {
999 119 : CPLFree(m_pszAttrQueryString);
1000 119 : m_pszAttrQueryString = pszQuery ? CPLStrdup(pszQuery) : nullptr;
1001 :
1002 119 : if (pszQuery == nullptr || strlen(pszQuery) == 0)
1003 87 : attrFilter_ = "";
1004 : else
1005 32 : attrFilter_.assign(pszQuery, strlen(pszQuery));
1006 :
1007 119 : ClearQueryStatement();
1008 119 : BuildWhereClause();
1009 119 : ResetReading();
1010 :
1011 119 : return OGRERR_NONE;
1012 : }
1013 :
1014 : /************************************************************************/
1015 : /* SetSpatialFilter() */
1016 : /************************************************************************/
1017 :
1018 96 : void OGRHanaLayer::SetSpatialFilter(int iGeomField, OGRGeometry *poGeom)
1019 : {
1020 96 : m_iGeomFieldFilter = 0;
1021 :
1022 96 : if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount())
1023 : {
1024 4 : CPLError(CE_Failure, CPLE_AppDefined,
1025 : "Invalid geometry field index : %d", iGeomField);
1026 4 : return;
1027 : }
1028 92 : m_iGeomFieldFilter = iGeomField;
1029 :
1030 92 : if (!InstallFilter(poGeom))
1031 72 : return;
1032 :
1033 20 : ClearQueryStatement();
1034 20 : BuildWhereClause();
1035 20 : ResetReading();
1036 : }
1037 :
1038 : } // namespace OGRHANA
|