Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Arrow generic code
4 : * Purpose: Arrow generic code
5 : * Author: Even Rouault, <even.rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2022, Planet Labs
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #ifndef OGARROWLAYER_HPP_INCLUDED
14 : #define OGARROWLAYER_HPP_INCLUDED
15 :
16 : #include "ogr_arrow.h"
17 :
18 : #include "cpl_float.h"
19 : #include "cpl_json.h"
20 : #include "cpl_time.h"
21 : #include "ogrlayerarrow.h"
22 : #include "ogr_p.h"
23 : #include "ogr_swq.h"
24 : #include "ogr_wkb.h"
25 :
26 : #include <algorithm>
27 : #include <cassert>
28 : #include <cinttypes>
29 : #include <limits>
30 : #include <string_view>
31 :
32 : #define SWQ_ISNOTNULL (-SWQ_ISNULL)
33 :
34 : #if defined(__clang__)
35 : #pragma clang diagnostic push
36 : #pragma clang diagnostic ignored "-Wweak-vtables"
37 : #endif
38 :
39 : inline IOGRArrowLayer::~IOGRArrowLayer() = default;
40 :
41 : /************************************************************************/
42 : /* OGRArrowLayer() */
43 : /************************************************************************/
44 :
45 1712 : inline OGRArrowLayer::OGRArrowLayer(OGRArrowDataset *poDS,
46 1712 : const char *pszLayerName)
47 1712 : : m_poArrowDS(poDS), m_poMemoryPool(poDS->GetMemoryPool())
48 : {
49 1712 : m_poFeatureDefn = new OGRFeatureDefn(pszLayerName);
50 1712 : m_poFeatureDefn->SetGeomType(wkbNone);
51 1712 : m_poFeatureDefn->Reference();
52 1712 : SetDescription(pszLayerName);
53 1712 : }
54 :
55 : /************************************************************************/
56 : /* ~OGRFeatherLayer() */
57 : /************************************************************************/
58 :
59 1709 : inline OGRArrowLayer::~OGRArrowLayer()
60 : {
61 1709 : if (m_sCachedSchema.release)
62 59 : m_sCachedSchema.release(&m_sCachedSchema);
63 :
64 1709 : CPLDebug("ARROW", "Memory pool: bytes_allocated = %" PRId64,
65 1709 : m_poMemoryPool->bytes_allocated());
66 1709 : CPLDebug("ARROW", "Memory pool: max_memory = %" PRId64,
67 1709 : m_poMemoryPool->max_memory());
68 1709 : m_poFeatureDefn->Release();
69 1709 : }
70 :
71 : /************************************************************************/
72 : /* LoadGDALSchema() */
73 : /************************************************************************/
74 :
75 : inline std::map<std::string, std::unique_ptr<OGRFieldDefn>>
76 1712 : OGRArrowLayer::LoadGDALSchema(const arrow::KeyValueMetadata *kv_metadata)
77 : {
78 : std::map<std::string, std::unique_ptr<OGRFieldDefn>>
79 1712 : oMapFieldNameToGDALSchemaFieldDefn;
80 1753 : if (kv_metadata && kv_metadata->Contains("gdal:schema") &&
81 41 : CPLTestBool(CPLGetConfigOption(
82 1753 : ("OGR_" + GetDriverUCName() + "_READ_GDAL_SCHEMA").c_str(), "YES")))
83 : {
84 82 : auto gdalSchema = kv_metadata->Get("gdal:schema");
85 41 : if (gdalSchema.ok())
86 : {
87 41 : CPLDebug(GetDriverUCName().c_str(), "gdal:schema = %s",
88 : gdalSchema->c_str());
89 82 : CPLJSONDocument oDoc;
90 41 : if (oDoc.LoadMemory(*gdalSchema))
91 : {
92 82 : auto oRoot = oDoc.GetRoot();
93 :
94 41 : m_osFIDColumn = oRoot.GetString("fid");
95 :
96 123 : auto oColumns = oRoot.GetObj("columns");
97 41 : if (oColumns.IsValid())
98 : {
99 427 : for (const auto &oColumn : oColumns.GetChildren())
100 : {
101 772 : const auto osName = oColumn.GetName();
102 1158 : const auto osType = oColumn.GetString("type");
103 1158 : const auto osSubType = oColumn.GetString("subtype");
104 : auto poFieldDefn = std::make_unique<OGRFieldDefn>(
105 772 : osName.c_str(), OFTString);
106 2306 : for (int iType = 0;
107 2306 : iType <= static_cast<int>(OFTMaxType); iType++)
108 : {
109 2306 : if (EQUAL(osType.c_str(),
110 : OGRFieldDefn::GetFieldTypeName(
111 : static_cast<OGRFieldType>(iType))))
112 : {
113 386 : poFieldDefn->SetType(
114 : static_cast<OGRFieldType>(iType));
115 386 : break;
116 : }
117 : }
118 386 : if (!osSubType.empty())
119 : {
120 500 : for (int iSubType = 0;
121 500 : iSubType <= static_cast<int>(OFSTMaxSubType);
122 : iSubType++)
123 : {
124 500 : if (EQUAL(osSubType.c_str(),
125 : OGRFieldDefn::GetFieldSubTypeName(
126 : static_cast<OGRFieldSubType>(
127 : iSubType))))
128 : {
129 112 : poFieldDefn->SetSubType(
130 : static_cast<OGRFieldSubType>(iSubType));
131 112 : break;
132 : }
133 : }
134 : }
135 386 : poFieldDefn->SetWidth(oColumn.GetInteger("width"));
136 386 : poFieldDefn->SetPrecision(
137 : oColumn.GetInteger("precision"));
138 :
139 : const auto osAlternativeName =
140 1158 : oColumn.GetString("alternative_name");
141 386 : if (!osAlternativeName.empty())
142 2 : poFieldDefn->SetAlternativeName(
143 : osAlternativeName.c_str());
144 :
145 1158 : const auto osComment = oColumn.GetString("comment");
146 386 : if (!osComment.empty())
147 3 : poFieldDefn->SetComment(osComment);
148 :
149 386 : oMapFieldNameToGDALSchemaFieldDefn[osName] =
150 772 : std::move(poFieldDefn);
151 : }
152 : }
153 : }
154 : }
155 : }
156 1712 : return oMapFieldNameToGDALSchemaFieldDefn;
157 : }
158 :
159 : /************************************************************************/
160 : /* LoadGDALMetadata() */
161 : /************************************************************************/
162 :
163 : inline void
164 1167 : OGRArrowLayer::LoadGDALMetadata(const arrow::KeyValueMetadata *kv_metadata)
165 : {
166 1167 : if (kv_metadata && kv_metadata->Contains("gdal:metadata"))
167 : {
168 16 : auto gdalMetadata = kv_metadata->Get("gdal:metadata");
169 8 : if (gdalMetadata.ok())
170 : {
171 16 : CPLJSONDocument oDoc;
172 8 : if (oDoc.LoadMemory(*gdalMetadata))
173 : {
174 16 : auto oRoot = oDoc.GetRoot();
175 18 : for (const auto &oDomain : oRoot.GetChildren())
176 : {
177 11 : if (STARTS_WITH(oDomain.GetName().c_str(), "json:") &&
178 1 : oDomain.GetType() == CPLJSONObject::Type::Object)
179 : {
180 1 : char **papszMD = nullptr;
181 1 : papszMD = CSLAddString(
182 : papszMD,
183 2 : oDomain.Format(CPLJSONObject::PrettyFormat::Plain)
184 : .c_str());
185 1 : SetMetadata(papszMD, oDomain.GetName().c_str());
186 1 : CSLDestroy(papszMD);
187 : }
188 10 : else if (STARTS_WITH(oDomain.GetName().c_str(), "xml:") &&
189 1 : oDomain.GetType() == CPLJSONObject::Type::String)
190 : {
191 1 : char **papszMD = nullptr;
192 : papszMD =
193 1 : CSLAddString(papszMD, oDomain.ToString().c_str());
194 1 : SetMetadata(papszMD, oDomain.GetName().c_str());
195 1 : CSLDestroy(papszMD);
196 : }
197 : else
198 : {
199 16 : for (const auto &oItem : oDomain.GetChildren())
200 : {
201 8 : if (oItem.GetType() == CPLJSONObject::Type::String)
202 : {
203 16 : SetMetadataItem(oItem.GetName().c_str(),
204 16 : oItem.ToString().c_str(),
205 16 : oDomain.GetName().c_str());
206 : }
207 : }
208 : }
209 : }
210 : }
211 : }
212 : }
213 1167 : }
214 :
215 : /************************************************************************/
216 : /* IsIntegerArrowType() */
217 : /************************************************************************/
218 :
219 8948 : inline bool OGRArrowLayer::IsIntegerArrowType(arrow::Type::type typeId)
220 : {
221 8547 : return typeId == arrow::Type::INT8 || typeId == arrow::Type::UINT8 ||
222 7745 : typeId == arrow::Type::INT16 || typeId == arrow::Type::UINT16 ||
223 6536 : typeId == arrow::Type::INT32 || typeId == arrow::Type::UINT32 ||
224 17495 : typeId == arrow::Type::INT64 || typeId == arrow::Type::UINT64;
225 : }
226 :
227 : /************************************************************************/
228 : /* IsHandledListOrMapType() */
229 : /************************************************************************/
230 :
231 8942 : inline bool OGRArrowLayer::IsHandledListOrMapType(
232 : const std::shared_ptr<arrow::DataType> &valueType)
233 : {
234 8942 : const auto itemTypeId = valueType->id();
235 8541 : return itemTypeId == arrow::Type::BOOL || IsIntegerArrowType(itemTypeId) ||
236 5287 : itemTypeId == arrow::Type::HALF_FLOAT ||
237 4886 : itemTypeId == arrow::Type::FLOAT ||
238 4485 : itemTypeId == arrow::Type::DOUBLE ||
239 : #if ARROW_VERSION_MAJOR >= 18
240 4485 : itemTypeId == arrow::Type::DECIMAL32 ||
241 4485 : itemTypeId == arrow::Type::DECIMAL64 ||
242 : #endif
243 3711 : itemTypeId == arrow::Type::DECIMAL128 ||
244 3687 : itemTypeId == arrow::Type::DECIMAL256 ||
245 1712 : itemTypeId == arrow::Type::STRING ||
246 1688 : itemTypeId == arrow::Type::LARGE_STRING ||
247 : #if ARROW_VERSION_MAJOR >= 15
248 1684 : itemTypeId == arrow::Type::STRING_VIEW ||
249 : #endif
250 1683 : itemTypeId == arrow::Type::BINARY ||
251 : #if ARROW_VERSION_MAJOR >= 15
252 1683 : itemTypeId == arrow::Type::BINARY_VIEW ||
253 : #endif
254 1261 : itemTypeId == arrow::Type::STRUCT ||
255 26 : (itemTypeId == arrow::Type::MAP &&
256 13 : IsHandledMapType(
257 27686 : std::static_pointer_cast<arrow::MapType>(valueType))) ||
258 48 : ((itemTypeId == arrow::Type::LIST ||
259 24 : itemTypeId == arrow::Type::LARGE_LIST ||
260 2496 : itemTypeId == arrow::Type::FIXED_SIZE_LIST) &&
261 1248 : IsHandledListType(
262 19132 : std::static_pointer_cast<arrow::BaseListType>(valueType)));
263 : }
264 :
265 : /************************************************************************/
266 : /* IsHandledListType() */
267 : /************************************************************************/
268 :
269 1714 : inline bool OGRArrowLayer::IsHandledListType(
270 : const std::shared_ptr<arrow::BaseListType> &listType)
271 : {
272 1714 : return IsHandledListOrMapType(listType->value_type());
273 : }
274 :
275 : /************************************************************************/
276 : /* IsHandledMapType() */
277 : /************************************************************************/
278 :
279 : inline bool
280 7228 : OGRArrowLayer::IsHandledMapType(const std::shared_ptr<arrow::MapType> &mapType)
281 : {
282 7228 : const auto typeId = mapType->key_type()->id();
283 : return (typeId == arrow::Type::STRING
284 : #if ARROW_VERSION_MAJOR >= 15
285 2 : || typeId == arrow::Type::STRING_VIEW
286 : #endif
287 14456 : ) &&
288 14456 : IsHandledListOrMapType(mapType->item_type());
289 : }
290 :
291 : /************************************************************************/
292 : /* MapArrowTypeToOGR() */
293 : /************************************************************************/
294 :
295 35990 : inline bool OGRArrowLayer::MapArrowTypeToOGR(
296 : const std::shared_ptr<arrow::DataType> &typeIn,
297 : const std::shared_ptr<arrow::Field> &field, OGRFieldDefn &oField,
298 : OGRFieldType &eType, OGRFieldSubType &eSubType,
299 : const std::vector<int> &path,
300 : const std::map<std::string, std::unique_ptr<OGRFieldDefn>>
301 : &oMapFieldNameToGDALSchemaFieldDefn)
302 : {
303 35990 : bool bTypeOK = false;
304 :
305 71980 : std::string osExtensionName;
306 35990 : std::shared_ptr<arrow::DataType> type(typeIn);
307 35990 : if (type->id() == arrow::Type::EXTENSION)
308 : {
309 167 : auto extensionType = cpl::down_cast<arrow::ExtensionType *>(type.get());
310 167 : osExtensionName = extensionType->extension_name();
311 167 : type = extensionType->storage_type();
312 : }
313 35828 : else if (const auto &field_kv_metadata = field->metadata())
314 : {
315 10 : auto extension_name = field_kv_metadata->Get(ARROW_EXTENSION_NAME_KEY);
316 5 : if (extension_name.ok())
317 : {
318 3 : osExtensionName = *extension_name;
319 : }
320 : }
321 :
322 36160 : if (!osExtensionName.empty() &&
323 170 : osExtensionName != EXTENSION_NAME_ARROW_JSON)
324 : {
325 6 : CPLDebug(GetDriverUCName().c_str(),
326 : "Dealing with field %s of extension type %s as %s",
327 3 : field->name().c_str(), osExtensionName.c_str(),
328 6 : type->ToString().c_str());
329 : }
330 :
331 35990 : switch (type->id())
332 : {
333 382 : case arrow::Type::NA:
334 382 : break;
335 :
336 414 : case arrow::Type::BOOL:
337 414 : bTypeOK = true;
338 414 : eType = OFTInteger;
339 414 : eSubType = OFSTBoolean;
340 414 : break;
341 1194 : case arrow::Type::UINT8:
342 : case arrow::Type::INT8:
343 : case arrow::Type::UINT16:
344 1194 : bTypeOK = true;
345 1194 : eType = OFTInteger;
346 1194 : break;
347 406 : case arrow::Type::INT16:
348 406 : bTypeOK = true;
349 406 : eType = OFTInteger;
350 406 : eSubType = OFSTInt16;
351 406 : break;
352 24 : case arrow::Type::UINT32:
353 24 : bTypeOK = true;
354 24 : eType = OFTInteger64;
355 24 : break;
356 1415 : case arrow::Type::INT32:
357 1415 : bTypeOK = true;
358 1415 : eType = OFTInteger;
359 1415 : break;
360 398 : case arrow::Type::UINT64:
361 398 : bTypeOK = true;
362 398 : eType = OFTReal; // potential loss
363 398 : break;
364 1723 : case arrow::Type::INT64:
365 1723 : bTypeOK = true;
366 1723 : eType = OFTInteger64;
367 1723 : break;
368 519 : case arrow::Type::HALF_FLOAT: // should use OFSTFloat16 if we had it
369 : case arrow::Type::FLOAT:
370 519 : bTypeOK = true;
371 519 : eType = OFTReal;
372 519 : eSubType = OFSTFloat32;
373 519 : break;
374 894 : case arrow::Type::DOUBLE:
375 894 : bTypeOK = true;
376 894 : eType = OFTReal;
377 894 : break;
378 2507 : case arrow::Type::STRING:
379 : case arrow::Type::LARGE_STRING:
380 : #if ARROW_VERSION_MAJOR >= 15
381 : case arrow::Type::STRING_VIEW:
382 : #endif
383 2507 : bTypeOK = true;
384 2507 : eType = OFTString;
385 2507 : if (osExtensionName == EXTENSION_NAME_ARROW_JSON)
386 167 : eSubType = OFSTJSON;
387 2507 : break;
388 836 : case arrow::Type::BINARY:
389 : case arrow::Type::LARGE_BINARY:
390 : #if ARROW_VERSION_MAJOR >= 15
391 : case arrow::Type::BINARY_VIEW:
392 : #endif
393 836 : bTypeOK = true;
394 836 : eType = OFTBinary;
395 836 : break;
396 406 : case arrow::Type::FIXED_SIZE_BINARY:
397 406 : bTypeOK = true;
398 406 : eType = OFTBinary;
399 406 : oField.SetWidth(
400 812 : std::static_pointer_cast<arrow::FixedSizeBinaryType>(type)
401 406 : ->byte_width());
402 406 : break;
403 :
404 857 : case arrow::Type::DATE32:
405 : case arrow::Type::DATE64:
406 857 : bTypeOK = true;
407 857 : eType = OFTDate;
408 857 : break;
409 :
410 2482 : case arrow::Type::TIMESTAMP:
411 : {
412 2482 : bTypeOK = true;
413 : const auto timestampType =
414 2482 : static_cast<arrow::TimestampType *>(type.get());
415 2482 : eType = OFTDateTime;
416 2482 : const auto &osTZ = timestampType->timezone();
417 2482 : int nTZFlag = OGRTimezoneToTZFlag(osTZ.c_str(), false);
418 2482 : if (nTZFlag == OGR_TZFLAG_UNKNOWN && !osTZ.empty())
419 : {
420 0 : CPLDebug(GetDriverUCName().c_str(),
421 : "Field %s has unrecognized timezone %s. "
422 : "UTC datetime will be used instead.",
423 0 : field->name().c_str(), osTZ.c_str());
424 0 : nTZFlag = OGR_TZFLAG_UTC;
425 : }
426 2482 : oField.SetTZFlag(nTZFlag);
427 2482 : break;
428 : }
429 :
430 812 : case arrow::Type::TIME32:
431 812 : bTypeOK = true;
432 812 : eType = OFTTime;
433 812 : break;
434 :
435 796 : case arrow::Type::TIME64:
436 796 : bTypeOK = true;
437 796 : eType = OFTInteger64; // our OFTTime doesn't have micro or
438 : // nanosecond accuracy
439 796 : break;
440 :
441 : #if ARROW_VERSION_MAJOR >= 18
442 823 : case arrow::Type::DECIMAL32:
443 : case arrow::Type::DECIMAL64:
444 : #endif
445 : case arrow::Type::DECIMAL128:
446 : case arrow::Type::DECIMAL256:
447 : {
448 823 : bTypeOK = true;
449 : const auto decimalType =
450 1646 : std::static_pointer_cast<arrow::DecimalType>(type);
451 823 : eType = OFTReal;
452 823 : oField.SetWidth(decimalType->precision());
453 823 : oField.SetPrecision(decimalType->scale());
454 823 : break;
455 : }
456 :
457 11887 : case arrow::Type::LIST:
458 : case arrow::Type::FIXED_SIZE_LIST:
459 : {
460 11887 : bTypeOK = true;
461 23774 : auto listType = std::static_pointer_cast<arrow::BaseListType>(type);
462 11887 : switch (listType->value_type()->id())
463 : {
464 812 : case arrow::Type::BOOL:
465 812 : eType = OFTIntegerList;
466 812 : eSubType = OFSTBoolean;
467 812 : break;
468 4060 : case arrow::Type::UINT8:
469 : case arrow::Type::INT8:
470 : case arrow::Type::UINT16:
471 : case arrow::Type::INT16:
472 : case arrow::Type::INT32:
473 4060 : eType = OFTIntegerList;
474 4060 : break;
475 46 : case arrow::Type::UINT32:
476 46 : eType = OFTInteger64List;
477 46 : break;
478 796 : case arrow::Type::UINT64:
479 796 : eType = OFTRealList; // potential loss
480 796 : break;
481 1985 : case arrow::Type::INT64:
482 1985 : eType = OFTInteger64List;
483 1985 : break;
484 841 : case arrow::Type::HALF_FLOAT: // should use OFSTFloat16 if we
485 : // had it
486 : case arrow::Type::FLOAT:
487 841 : eType = OFTRealList;
488 841 : eSubType = OFSTFloat32;
489 841 : break;
490 1643 : case arrow::Type::DOUBLE:
491 : #if ARROW_VERSION_MAJOR >= 18
492 : case arrow::Type::DECIMAL32:
493 : case arrow::Type::DECIMAL64:
494 : #endif
495 : case arrow::Type::DECIMAL128:
496 : case arrow::Type::DECIMAL256:
497 1643 : eType = OFTRealList;
498 1643 : break;
499 1238 : case arrow::Type::STRING:
500 : case arrow::Type::LARGE_STRING:
501 : case arrow::Type::BINARY:
502 : #if ARROW_VERSION_MAJOR >= 15
503 : case arrow::Type::STRING_VIEW:
504 : #endif
505 1238 : eType = OFTStringList;
506 1238 : break;
507 466 : default:
508 : {
509 466 : if (IsHandledListType(listType))
510 : {
511 466 : eType = OFTString;
512 466 : eSubType = OFSTJSON;
513 : }
514 : else
515 : {
516 0 : bTypeOK = false;
517 0 : CPLError(CE_Warning, CPLE_AppDefined,
518 : "Field %s of unhandled type %s ignored",
519 0 : field->name().c_str(),
520 0 : type->ToString().c_str());
521 : }
522 466 : break;
523 : }
524 : }
525 11887 : break;
526 : }
527 :
528 7215 : case arrow::Type::MAP:
529 : {
530 7215 : bTypeOK = true;
531 14430 : auto mapType = std::static_pointer_cast<arrow::MapType>(type);
532 7215 : if (IsHandledMapType(mapType))
533 : {
534 7215 : eType = OFTString;
535 7215 : eSubType = OFSTJSON;
536 : }
537 : else
538 : {
539 0 : bTypeOK = false;
540 0 : CPLError(CE_Warning, CPLE_AppDefined,
541 : "Field %s of unhandled type %s ignored",
542 0 : field->name().c_str(), type->ToString().c_str());
543 : }
544 7215 : break;
545 : }
546 :
547 0 : case arrow::Type::STRUCT:
548 : // should be handled by specialized code
549 0 : CPLAssert(false);
550 : break;
551 :
552 : // unhandled types
553 :
554 0 : case arrow::Type::INTERVAL_MONTHS:
555 : case arrow::Type::INTERVAL_DAY_TIME:
556 : case arrow::Type::SPARSE_UNION:
557 : case arrow::Type::DENSE_UNION:
558 : case arrow::Type::DICTIONARY:
559 : case arrow::Type::EXTENSION:
560 : case arrow::Type::DURATION:
561 : case arrow::Type::LARGE_LIST:
562 : case arrow::Type::INTERVAL_MONTH_DAY_NANO:
563 : #if ARROW_VERSION_MAJOR >= 12
564 : case arrow::Type::RUN_END_ENCODED:
565 : #endif
566 : #if ARROW_VERSION_MAJOR >= 15
567 : case arrow::Type::LIST_VIEW:
568 : case arrow::Type::LARGE_LIST_VIEW:
569 : #endif
570 : case arrow::Type::MAX_ID:
571 : {
572 0 : CPLError(CE_Warning, CPLE_AppDefined,
573 : "Field %s of unhandled type %s ignored",
574 0 : field->name().c_str(), type->ToString().c_str());
575 0 : break;
576 : }
577 : }
578 :
579 35990 : if (bTypeOK)
580 : {
581 : const auto oIter =
582 35608 : oMapFieldNameToGDALSchemaFieldDefn.find(field->name());
583 35608 : oField.SetType(eType);
584 35608 : if (oIter != oMapFieldNameToGDALSchemaFieldDefn.end())
585 : {
586 386 : const auto &poGDALFieldDefn = oIter->second;
587 386 : if (poGDALFieldDefn->GetType() == eType)
588 : {
589 386 : if (eSubType == OFSTNone)
590 : {
591 274 : eSubType = poGDALFieldDefn->GetSubType();
592 : }
593 112 : else if (eSubType != poGDALFieldDefn->GetSubType())
594 : {
595 0 : CPLDebug(
596 0 : GetDriverUCName().c_str(),
597 : "Field subtype inferred from Parquet/Arrow schema is "
598 : "%s, "
599 : "whereas the one in gdal:schema is %s. "
600 : "Using the former one.",
601 : OGR_GetFieldSubTypeName(eSubType),
602 : OGR_GetFieldSubTypeName(poGDALFieldDefn->GetSubType()));
603 : }
604 : }
605 : else
606 : {
607 0 : CPLDebug(GetDriverUCName().c_str(),
608 : "Field type inferred from Parquet/Arrow schema is %s, "
609 : "whereas the one in gdal:schema is %s. "
610 : "Using the former one.",
611 : OGR_GetFieldTypeName(eType),
612 : OGR_GetFieldTypeName(poGDALFieldDefn->GetType()));
613 : }
614 386 : if (poGDALFieldDefn->GetWidth() > 0)
615 39 : oField.SetWidth(poGDALFieldDefn->GetWidth());
616 386 : if (poGDALFieldDefn->GetPrecision() > 0)
617 17 : oField.SetPrecision(poGDALFieldDefn->GetPrecision());
618 386 : if (poGDALFieldDefn->GetAlternativeNameRef()[0])
619 2 : oField.SetAlternativeName(
620 : poGDALFieldDefn->GetAlternativeNameRef());
621 386 : if (!poGDALFieldDefn->GetComment().empty())
622 3 : oField.SetComment(poGDALFieldDefn->GetComment());
623 : }
624 35608 : oField.SetSubType(eSubType);
625 35608 : oField.SetNullable(field->nullable());
626 35608 : m_poFeatureDefn->AddFieldDefn(&oField);
627 35608 : m_anMapFieldIndexToArrowColumn.push_back(path);
628 : }
629 :
630 71980 : return bTypeOK;
631 : }
632 :
633 : /************************************************************************/
634 : /* CreateFieldFromSchema() */
635 : /************************************************************************/
636 :
637 10281 : inline void OGRArrowLayer::CreateFieldFromSchema(
638 : const std::shared_ptr<arrow::Field> &field, const std::vector<int> &path,
639 : const std::map<std::string, std::unique_ptr<OGRFieldDefn>>
640 : &oMapFieldNameToGDALSchemaFieldDefn)
641 : {
642 20562 : OGRFieldDefn oField(field->name().c_str(), OFTString);
643 10281 : OGRFieldType eType = OFTString;
644 10281 : OGRFieldSubType eSubType = OFSTNone;
645 10281 : bool bTypeOK = true;
646 :
647 20562 : auto type = field->type();
648 10281 : if (type->id() == arrow::Type::DICTIONARY && path.size() == 1)
649 : {
650 : const auto dictionaryType =
651 218 : std::static_pointer_cast<arrow::DictionaryType>(field->type());
652 218 : auto indexType = dictionaryType->index_type();
653 218 : if (dictionaryType->value_type()->id() == arrow::Type::STRING &&
654 109 : IsIntegerArrowType(indexType->id()))
655 : {
656 218 : std::string osDomainName(field->name() + "Domain");
657 109 : m_poArrowDS->RegisterDomainName(osDomainName,
658 109 : m_poFeatureDefn->GetFieldCount());
659 109 : oField.SetDomainName(osDomainName);
660 109 : type = std::move(indexType);
661 : }
662 : else
663 : {
664 0 : bTypeOK = false;
665 : }
666 : }
667 :
668 10281 : if (type->id() == arrow::Type::STRUCT)
669 : {
670 416 : const auto subfields = field->Flatten();
671 416 : auto newpath = path;
672 208 : newpath.push_back(0);
673 936 : for (int j = 0; j < static_cast<int>(subfields.size()); j++)
674 : {
675 728 : const auto &subfield = subfields[j];
676 728 : newpath.back() = j;
677 728 : CreateFieldFromSchema(subfield, newpath,
678 : oMapFieldNameToGDALSchemaFieldDefn);
679 : }
680 : }
681 10073 : else if (bTypeOK)
682 : {
683 10073 : MapArrowTypeToOGR(type, field, oField, eType, eSubType, path,
684 : oMapFieldNameToGDALSchemaFieldDefn);
685 : }
686 10281 : }
687 :
688 : /************************************************************************/
689 : /* BuildDomainFromBatch() */
690 : /************************************************************************/
691 :
692 35 : inline std::unique_ptr<OGRFieldDomain> OGRArrowLayer::BuildDomainFromBatch(
693 : const std::string &osDomainName,
694 : const std::shared_ptr<arrow::RecordBatch> &poBatch, int iCol) const
695 : {
696 70 : const auto array = poBatch->column(iCol);
697 70 : auto castArray = std::static_pointer_cast<arrow::DictionaryArray>(array);
698 70 : auto dict = castArray->dictionary();
699 35 : CPLAssert(dict->type_id() == arrow::Type::STRING);
700 35 : OGRFieldType eType = OFTInteger;
701 35 : const auto indexTypeId = castArray->dict_type()->index_type()->id();
702 35 : if (indexTypeId == arrow::Type::UINT32 ||
703 35 : indexTypeId == arrow::Type::UINT64 || indexTypeId == arrow::Type::INT64)
704 0 : eType = OFTInteger64;
705 70 : auto values = std::static_pointer_cast<arrow::StringArray>(dict);
706 70 : std::vector<OGRCodedValue> asValues;
707 35 : if (values->length() > INT_MAX)
708 : {
709 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
710 : "BuildDomainFromBatch(): too many values");
711 0 : return nullptr;
712 : }
713 : try
714 : {
715 35 : asValues.reserve(static_cast<size_t>(values->length()));
716 : }
717 0 : catch (const std::bad_alloc &)
718 : {
719 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
720 : "BuildDomainFromBatch(): out of memory");
721 0 : return nullptr;
722 : }
723 140 : for (int i = 0; i < static_cast<int>(values->length()); ++i)
724 : {
725 105 : if (!values->IsNull(i))
726 : {
727 : OGRCodedValue val;
728 105 : val.pszCode = CPLStrdup(CPLSPrintf("%d", i));
729 105 : val.pszValue = CPLStrdup(values->GetString(i).c_str());
730 105 : asValues.emplace_back(val);
731 : }
732 : }
733 70 : return std::make_unique<OGRCodedFieldDomain>(
734 105 : osDomainName, std::string(), eType, OFSTNone, std::move(asValues));
735 : }
736 :
737 : /************************************************************************/
738 : /* GetStorageArray() */
739 : /************************************************************************/
740 :
741 188275 : static const arrow::Array *GetStorageArray(const arrow::Array *array)
742 : {
743 188275 : if (array->type_id() == arrow::Type::EXTENSION)
744 : {
745 : auto extensionArray =
746 1896 : cpl::down_cast<const arrow::ExtensionArray *>(array);
747 1896 : array = extensionArray->storage().get();
748 : }
749 188275 : return array;
750 : }
751 :
752 : /************************************************************************/
753 : /* ComputeGeometryColumnTypeProcessBatch() */
754 : /************************************************************************/
755 :
756 535 : inline OGRwkbGeometryType OGRArrowLayer::ComputeGeometryColumnTypeProcessBatch(
757 : const std::shared_ptr<arrow::RecordBatch> &poBatch, int iGeomCol,
758 : int iBatchCol, OGRwkbGeometryType eGeomType) const
759 : {
760 1070 : const auto array = poBatch->column(iBatchCol);
761 535 : const auto storageArray = GetStorageArray(array.get());
762 : const auto castBinaryArray =
763 535 : (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKB)
764 535 : ? dynamic_cast<const arrow::BinaryArray *>(storageArray)
765 535 : : nullptr;
766 : const auto castLargeBinaryArray =
767 535 : (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKB)
768 535 : ? dynamic_cast<const arrow::LargeBinaryArray *>(storageArray)
769 535 : : nullptr;
770 : const auto castStringArray =
771 535 : (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKT)
772 535 : ? dynamic_cast<const arrow::StringArray *>(storageArray)
773 535 : : nullptr;
774 : const auto castLargeStringArray =
775 535 : (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKT)
776 535 : ? dynamic_cast<const arrow::LargeStringArray *>(storageArray)
777 535 : : nullptr;
778 3208 : for (int64_t i = 0; i < poBatch->num_rows(); i++)
779 : {
780 2675 : if (!storageArray->IsNull(i))
781 : {
782 2152 : OGRwkbGeometryType eThisGeomType = wkbNone;
783 2152 : if (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKB &&
784 : castBinaryArray)
785 : {
786 2040 : arrow::BinaryArray::offset_type out_length = 0;
787 2040 : const uint8_t *data = castBinaryArray->GetValue(i, &out_length);
788 2040 : if (out_length >= 5)
789 : {
790 2040 : OGRReadWKBGeometryType(data, wkbVariantIso, &eThisGeomType);
791 : }
792 : }
793 112 : else if (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKB &&
794 : castLargeBinaryArray)
795 : {
796 0 : arrow::LargeBinaryArray::offset_type out_length = 0;
797 : const uint8_t *data =
798 0 : castLargeBinaryArray->GetValue(i, &out_length);
799 0 : if (out_length >= 5)
800 : {
801 0 : OGRReadWKBGeometryType(data, wkbVariantIso, &eThisGeomType);
802 : }
803 : }
804 112 : else if (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKT &&
805 : castStringArray)
806 : {
807 224 : const auto osWKT = castStringArray->GetString(i);
808 112 : if (!osWKT.empty())
809 : {
810 112 : OGRReadWKTGeometryType(osWKT.c_str(), &eThisGeomType);
811 : }
812 : }
813 0 : else if (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKT &&
814 : castLargeStringArray)
815 : {
816 0 : const auto osWKT = castLargeStringArray->GetString(i);
817 0 : if (!osWKT.empty())
818 : {
819 0 : OGRReadWKTGeometryType(osWKT.c_str(), &eThisGeomType);
820 : }
821 : }
822 :
823 2152 : if (eThisGeomType != wkbNone)
824 : {
825 2152 : if (eGeomType == wkbNone)
826 531 : eGeomType = eThisGeomType;
827 1621 : else if (wkbFlatten(eThisGeomType) == wkbFlatten(eGeomType))
828 : ;
829 6 : else if (wkbFlatten(eThisGeomType) == wkbMultiLineString &&
830 0 : wkbFlatten(eGeomType) == wkbLineString)
831 : {
832 0 : eGeomType = OGR_GT_SetModifier(
833 : wkbMultiLineString,
834 0 : OGR_GT_HasZ(eThisGeomType) || OGR_GT_HasZ(eGeomType),
835 0 : OGR_GT_HasM(eThisGeomType) || OGR_GT_HasM(eGeomType));
836 : }
837 8 : else if (wkbFlatten(eThisGeomType) == wkbLineString &&
838 2 : wkbFlatten(eGeomType) == wkbMultiLineString)
839 : ;
840 6 : else if (wkbFlatten(eThisGeomType) == wkbMultiPolygon &&
841 0 : wkbFlatten(eGeomType) == wkbPolygon)
842 : {
843 0 : eGeomType = OGR_GT_SetModifier(
844 : wkbMultiPolygon,
845 0 : OGR_GT_HasZ(eThisGeomType) || OGR_GT_HasZ(eGeomType),
846 0 : OGR_GT_HasM(eThisGeomType) || OGR_GT_HasM(eGeomType));
847 : }
848 10 : else if (wkbFlatten(eThisGeomType) == wkbPolygon &&
849 4 : wkbFlatten(eGeomType) == wkbMultiPolygon)
850 : ;
851 : else
852 2 : return wkbUnknown;
853 :
854 2150 : eGeomType = OGR_GT_SetModifier(
855 : eGeomType,
856 2150 : OGR_GT_HasZ(eThisGeomType) || OGR_GT_HasZ(eGeomType),
857 2150 : OGR_GT_HasM(eThisGeomType) || OGR_GT_HasM(eGeomType));
858 : }
859 : }
860 : }
861 533 : return eGeomType;
862 : }
863 :
864 : /************************************************************************/
865 : /* IsPointType() */
866 : /************************************************************************/
867 :
868 828 : static bool IsPointType(const std::shared_ptr<arrow::DataType> &type,
869 : bool &bHasZOut, bool &bHasMOut)
870 : {
871 828 : if (type->id() != arrow::Type::FIXED_SIZE_LIST)
872 498 : return false;
873 660 : auto poListType = std::static_pointer_cast<arrow::FixedSizeListType>(type);
874 330 : const int nOutDimensionality = poListType->list_size();
875 660 : const std::string osValueFieldName(poListType->value_field()->name());
876 330 : if (nOutDimensionality == 2)
877 : {
878 114 : bHasZOut = false;
879 114 : bHasMOut = false;
880 : }
881 216 : else if (nOutDimensionality == 3)
882 : {
883 156 : if (osValueFieldName == "xym")
884 : {
885 60 : bHasZOut = false;
886 60 : bHasMOut = true;
887 : }
888 : else /* if (osValueFieldName == "xyz" || osValueFieldName == "element") */
889 : {
890 96 : bHasMOut = false;
891 96 : bHasZOut = true;
892 : }
893 : }
894 60 : else if (nOutDimensionality == 4)
895 : {
896 60 : bHasMOut = true;
897 60 : bHasZOut = true;
898 : }
899 : else
900 : {
901 0 : return false;
902 : }
903 330 : return poListType->value_type()->id() == arrow::Type::DOUBLE;
904 : }
905 :
906 : /************************************************************************/
907 : /* IsListOfPointType() */
908 : /************************************************************************/
909 :
910 1298 : static bool IsListOfPointType(const std::shared_ptr<arrow::DataType> &type,
911 : int nDepth, bool &bHasZOut, bool &bHasMOut)
912 : {
913 1298 : if (type->id() != arrow::Type::LIST)
914 0 : return false;
915 1298 : auto poListType = std::static_pointer_cast<arrow::ListType>(type);
916 : return nDepth == 1
917 1298 : ? IsPointType(poListType->value_type(), bHasZOut, bHasMOut)
918 607 : : IsListOfPointType(poListType->value_type(), nDepth - 1,
919 1298 : bHasZOut, bHasMOut);
920 : }
921 :
922 : /************************************************************************/
923 : /* IsPointStructType() */
924 : /************************************************************************/
925 :
926 498 : static bool IsPointStructType(const std::shared_ptr<arrow::DataType> &type,
927 : bool &bHasZOut, bool &bHasMOut)
928 : {
929 498 : if (type->id() != arrow::Type::STRUCT)
930 0 : return false;
931 996 : auto poStructType = std::static_pointer_cast<arrow::StructType>(type);
932 498 : const int nNumFields = poStructType->num_fields();
933 498 : if (nNumFields < 2 || nNumFields > 4)
934 0 : return false;
935 498 : bHasZOut = false;
936 498 : bHasMOut = false;
937 996 : const auto poFieldX = poStructType->field(0);
938 996 : if (poFieldX->name() != "x" ||
939 498 : poFieldX->type()->id() != arrow::Type::DOUBLE)
940 0 : return false;
941 996 : const auto poFieldY = poStructType->field(1);
942 996 : if (poFieldY->name() != "y" ||
943 498 : poFieldY->type()->id() != arrow::Type::DOUBLE)
944 0 : return false;
945 498 : if (nNumFields == 2)
946 288 : return true;
947 420 : const auto poField2 = poStructType->field(2);
948 210 : if (poField2->type()->id() != arrow::Type::DOUBLE)
949 0 : return false;
950 210 : if (poField2->name() == "z")
951 : {
952 204 : bHasZOut = true;
953 204 : if (nNumFields == 4)
954 : {
955 6 : const auto poField3 = poStructType->field(3);
956 12 : if (poField3->name() != "m" ||
957 6 : poField3->type()->id() != arrow::Type::DOUBLE)
958 0 : return false;
959 6 : bHasMOut = true;
960 : }
961 : }
962 6 : else if (poField2->name() == "m")
963 : {
964 6 : bHasMOut = true;
965 : }
966 : else
967 : {
968 0 : return false;
969 : }
970 210 : return true;
971 : }
972 :
973 : /************************************************************************/
974 : /* IsListOfPointStructType() */
975 : /************************************************************************/
976 :
977 : static bool
978 790 : IsListOfPointStructType(const std::shared_ptr<arrow::DataType> &type,
979 : int nDepth, bool &bHasZOut, bool &bHasMOut)
980 : {
981 790 : if (type->id() != arrow::Type::LIST)
982 0 : return false;
983 790 : auto poListType = std::static_pointer_cast<arrow::ListType>(type);
984 : return nDepth == 1
985 790 : ? IsPointStructType(poListType->value_type(), bHasZOut, bHasMOut)
986 376 : : IsListOfPointStructType(poListType->value_type(), nDepth - 1,
987 790 : bHasZOut, bHasMOut);
988 : }
989 :
990 : /************************************************************************/
991 : /* IsValidGeometryEncoding() */
992 : /************************************************************************/
993 :
994 1780 : inline bool OGRArrowLayer::IsValidGeometryEncoding(
995 : const std::shared_ptr<arrow::Field> &field, const std::string &osEncoding,
996 : bool bWarnIfUnknownEncoding, OGRwkbGeometryType &eGeomTypeOut,
997 : OGRArrowGeomEncoding &eOGRArrowGeomEncodingOut)
998 : {
999 1780 : const auto &fieldName = field->name();
1000 3560 : std::shared_ptr<arrow::DataType> fieldType = field->type();
1001 1780 : auto fieldTypeId = fieldType->id();
1002 :
1003 1780 : if (fieldTypeId == arrow::Type::EXTENSION)
1004 : {
1005 : auto extensionType =
1006 127 : cpl::down_cast<arrow::ExtensionType *>(fieldType.get());
1007 127 : fieldType = extensionType->storage_type();
1008 127 : fieldTypeId = fieldType->id();
1009 : }
1010 :
1011 1780 : eGeomTypeOut = wkbUnknown;
1012 :
1013 3505 : if (osEncoding == "WKT" || // As used in Parquet geo metadata
1014 1725 : osEncoding ==
1015 3505 : "ogc.wkt" || // As used in ARROW:extension:name field metadata
1016 1725 : osEncoding ==
1017 : "geoarrow.wkt" // As used in ARROW:extension:name field metadata
1018 : )
1019 : {
1020 55 : if (fieldTypeId != arrow::Type::LARGE_STRING &&
1021 : fieldTypeId != arrow::Type::STRING)
1022 : {
1023 0 : CPLError(CE_Warning, CPLE_AppDefined,
1024 : "Geometry column %s has a non String type: %s. "
1025 : "Handling it as a regular field",
1026 0 : fieldName.c_str(), fieldType->ToString().c_str());
1027 0 : return false;
1028 : }
1029 55 : eOGRArrowGeomEncodingOut = OGRArrowGeomEncoding::WKT;
1030 55 : return true;
1031 : }
1032 :
1033 2683 : if (osEncoding == "WKB" || // As used in Parquet geo metadata
1034 958 : osEncoding ==
1035 2683 : "ogc.wkb" || // As used in ARROW:extension:name field metadata
1036 958 : osEncoding ==
1037 : "geoarrow.wkb" // As used in ARROW:extension:name field metadata
1038 : )
1039 : {
1040 768 : if (fieldTypeId != arrow::Type::LARGE_BINARY &&
1041 : fieldTypeId != arrow::Type::BINARY)
1042 : {
1043 0 : CPLError(CE_Warning, CPLE_AppDefined,
1044 : "Geometry column %s has a non Binary type: %s. "
1045 : "Handling it as a regular field",
1046 0 : fieldName.c_str(), fieldType->ToString().c_str());
1047 0 : return false;
1048 : }
1049 768 : eOGRArrowGeomEncodingOut = OGRArrowGeomEncoding::WKB;
1050 768 : return true;
1051 : }
1052 :
1053 957 : bool bHasZ = false;
1054 957 : bool bHasM = false;
1055 957 : if (osEncoding == "geoarrow.point" || osEncoding == "point")
1056 : {
1057 137 : if (IsPointType(fieldType, bHasZ, bHasM))
1058 : {
1059 53 : eOGRArrowGeomEncodingOut = OGRArrowGeomEncoding::GEOARROW_FSL_POINT;
1060 : }
1061 84 : else if (IsPointStructType(fieldType, bHasZ, bHasM))
1062 : {
1063 84 : eOGRArrowGeomEncodingOut =
1064 : OGRArrowGeomEncoding::GEOARROW_STRUCT_POINT;
1065 : }
1066 : else
1067 : {
1068 0 : CPLError(CE_Warning, CPLE_AppDefined,
1069 : "Geometry column %s has a type != fixed_size_list<xy: "
1070 : "double>[2]> and != struct<x: double, y: double>: %s. "
1071 : "Handling it as a regular field",
1072 0 : fieldName.c_str(), fieldType->name().c_str());
1073 0 : return false;
1074 : }
1075 137 : eGeomTypeOut = OGR_GT_SetModifier(wkbPoint, static_cast<int>(bHasZ),
1076 : static_cast<int>(bHasM));
1077 137 : return true;
1078 : }
1079 :
1080 820 : else if (osEncoding == "geoarrow.linestring" || osEncoding == "linestring")
1081 : {
1082 122 : if (IsListOfPointType(fieldType, 1, bHasZ, bHasM))
1083 : {
1084 52 : eOGRArrowGeomEncodingOut =
1085 : OGRArrowGeomEncoding::GEOARROW_FSL_LINESTRING;
1086 : }
1087 70 : else if (IsListOfPointStructType(fieldType, 1, bHasZ, bHasM))
1088 : {
1089 70 : eOGRArrowGeomEncodingOut =
1090 : OGRArrowGeomEncoding::GEOARROW_STRUCT_LINESTRING;
1091 : }
1092 : else
1093 : {
1094 0 : CPLError(CE_Warning, CPLE_AppDefined,
1095 : "Geometry column %s has a type != fixed_size_list<xy: "
1096 : "double>[2]> and != list<element: struct<x: double, y: "
1097 : "double>>: %s. "
1098 : "Handling it as a regular field",
1099 0 : fieldName.c_str(), fieldType->ToString().c_str());
1100 0 : return false;
1101 : }
1102 122 : eGeomTypeOut = OGR_GT_SetModifier(
1103 : wkbLineString, static_cast<int>(bHasZ), static_cast<int>(bHasM));
1104 122 : return true;
1105 : }
1106 :
1107 698 : else if (osEncoding == "geoarrow.polygon" || osEncoding == "polygon")
1108 : {
1109 165 : if (IsListOfPointType(fieldType, 2, bHasZ, bHasM))
1110 : {
1111 63 : eOGRArrowGeomEncodingOut =
1112 : OGRArrowGeomEncoding::GEOARROW_FSL_POLYGON;
1113 : }
1114 102 : else if (IsListOfPointStructType(fieldType, 2, bHasZ, bHasM))
1115 : {
1116 102 : eOGRArrowGeomEncodingOut =
1117 : OGRArrowGeomEncoding::GEOARROW_STRUCT_POLYGON;
1118 : }
1119 : else
1120 : {
1121 0 : CPLError(CE_Warning, CPLE_AppDefined,
1122 : "Geometry column %s has a type != list<vertices: "
1123 : "fixed_size_list<xy: double>[2]>> and != list<element: "
1124 : "list<element: struct<x: double, y: double>>>: %s. "
1125 : "Handling it as a regular field",
1126 0 : fieldName.c_str(), fieldType->ToString().c_str());
1127 0 : return false;
1128 : }
1129 165 : eGeomTypeOut = OGR_GT_SetModifier(wkbPolygon, static_cast<int>(bHasZ),
1130 : static_cast<int>(bHasM));
1131 165 : return true;
1132 : }
1133 :
1134 533 : else if (osEncoding == "geoarrow.multipoint" || osEncoding == "multipoint")
1135 : {
1136 122 : if (IsListOfPointType(fieldType, 1, bHasZ, bHasM))
1137 : {
1138 52 : eOGRArrowGeomEncodingOut =
1139 : OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOINT;
1140 : }
1141 70 : else if (IsListOfPointStructType(fieldType, 1, bHasZ, bHasM))
1142 : {
1143 70 : eOGRArrowGeomEncodingOut =
1144 : OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOINT;
1145 : }
1146 : else
1147 : {
1148 0 : CPLError(CE_Warning, CPLE_AppDefined,
1149 : "Geometry column %s has a type != fixed_size_list<xy: "
1150 : "double>[2]> and != list<element: struct<x: double, y: "
1151 : "double>>: %s. "
1152 : "Handling it as a regular field",
1153 0 : fieldName.c_str(), fieldType->ToString().c_str());
1154 0 : return false;
1155 : }
1156 122 : eGeomTypeOut = OGR_GT_SetModifier(
1157 : wkbMultiPoint, static_cast<int>(bHasZ), static_cast<int>(bHasM));
1158 122 : return true;
1159 : }
1160 :
1161 770 : else if (osEncoding == "geoarrow.multilinestring" ||
1162 359 : osEncoding == "multilinestring")
1163 : {
1164 122 : if (IsListOfPointType(fieldType, 2, bHasZ, bHasM))
1165 : {
1166 52 : eOGRArrowGeomEncodingOut =
1167 : OGRArrowGeomEncoding::GEOARROW_FSL_MULTILINESTRING;
1168 : }
1169 70 : else if (IsListOfPointStructType(fieldType, 2, bHasZ, bHasM))
1170 : {
1171 70 : eOGRArrowGeomEncodingOut =
1172 : OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTILINESTRING;
1173 : }
1174 : else
1175 : {
1176 0 : CPLError(CE_Warning, CPLE_AppDefined,
1177 : "Geometry column %s has a type != list<vertices: "
1178 : "fixed_size_list<xy: double>[2]>> and != list<element: "
1179 : "list<element: struct<x: double, y: double>>>: %s. "
1180 : "Handling it as a regular field",
1181 0 : fieldName.c_str(), fieldType->ToString().c_str());
1182 0 : return false;
1183 : }
1184 122 : eGeomTypeOut =
1185 122 : OGR_GT_SetModifier(wkbMultiLineString, static_cast<int>(bHasZ),
1186 : static_cast<int>(bHasM));
1187 122 : return true;
1188 : }
1189 :
1190 520 : else if (osEncoding == "geoarrow.multipolygon" ||
1191 231 : osEncoding == "multipolygon")
1192 : {
1193 160 : if (IsListOfPointType(fieldType, 3, bHasZ, bHasM))
1194 : {
1195 58 : eOGRArrowGeomEncodingOut =
1196 : OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOLYGON;
1197 : }
1198 102 : else if (IsListOfPointStructType(fieldType, 3, bHasZ, bHasM))
1199 : {
1200 102 : eOGRArrowGeomEncodingOut =
1201 : OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOLYGON;
1202 : }
1203 : else
1204 : {
1205 0 : CPLError(
1206 : CE_Warning, CPLE_AppDefined,
1207 : "Geometry column %s has a type != list<polygons: list<rings: "
1208 : "list<vertices: fixed_size_list<xy: double>[2]>>> and != "
1209 : "list<element: list<element: list<element: struct<x: double, "
1210 : "y: double>>>>: %s. "
1211 : "Handling it as a regular field",
1212 0 : fieldName.c_str(), fieldType->ToString().c_str());
1213 0 : return false;
1214 : }
1215 160 : eGeomTypeOut = OGR_GT_SetModifier(
1216 : wkbMultiPolygon, static_cast<int>(bHasZ), static_cast<int>(bHasM));
1217 160 : return true;
1218 : }
1219 :
1220 129 : if (bWarnIfUnknownEncoding)
1221 : {
1222 0 : CPLError(CE_Warning, CPLE_AppDefined,
1223 : "Geometry column %s uses a unhandled encoding: %s. "
1224 : "Handling it as a regular field",
1225 : fieldName.c_str(), osEncoding.c_str());
1226 : }
1227 129 : return false;
1228 : }
1229 :
1230 : /************************************************************************/
1231 : /* GetGeometryTypeFromString() */
1232 : /************************************************************************/
1233 :
1234 : inline OGRwkbGeometryType
1235 283 : OGRArrowLayer::GetGeometryTypeFromString(const std::string &osType)
1236 : {
1237 283 : OGRwkbGeometryType eGeomType = wkbUnknown;
1238 283 : OGRReadWKTGeometryType(osType.c_str(), &eGeomType);
1239 283 : if (eGeomType == wkbUnknown && !osType.empty())
1240 : {
1241 0 : CPLDebug("ARROW", "Unknown geometry type: %s", osType.c_str());
1242 : }
1243 283 : return eGeomType;
1244 : }
1245 :
1246 : static CPLJSONObject GetObjectAsJSON(const arrow::Array *array,
1247 : const size_t nIdx);
1248 :
1249 : /************************************************************************/
1250 : /* AddToArray() */
1251 : /************************************************************************/
1252 :
1253 : template <class Container>
1254 26978 : void AddToContainer(Container &oContainer, const arrow::Array *array,
1255 : const size_t nIdx)
1256 : {
1257 26978 : switch (array->type()->id())
1258 : {
1259 734 : case arrow::Type::BOOL:
1260 : {
1261 734 : oContainer.Add(
1262 734 : static_cast<const arrow::BooleanArray *>(array)->Value(nIdx));
1263 734 : break;
1264 : }
1265 741 : case arrow::Type::UINT8:
1266 : {
1267 741 : oContainer.Add(
1268 741 : static_cast<const arrow::UInt8Array *>(array)->Value(nIdx));
1269 741 : break;
1270 : }
1271 741 : case arrow::Type::INT8:
1272 : {
1273 741 : oContainer.Add(
1274 741 : static_cast<const arrow::Int8Array *>(array)->Value(nIdx));
1275 741 : break;
1276 : }
1277 741 : case arrow::Type::UINT16:
1278 : {
1279 741 : oContainer.Add(
1280 741 : static_cast<const arrow::UInt16Array *>(array)->Value(nIdx));
1281 741 : break;
1282 : }
1283 741 : case arrow::Type::INT16:
1284 : {
1285 741 : oContainer.Add(
1286 741 : static_cast<const arrow::Int16Array *>(array)->Value(nIdx));
1287 741 : break;
1288 : }
1289 741 : case arrow::Type::INT32:
1290 : {
1291 741 : oContainer.Add(
1292 732 : static_cast<const arrow::Int32Array *>(array)->Value(nIdx));
1293 741 : break;
1294 : }
1295 120 : case arrow::Type::UINT32:
1296 : {
1297 120 : oContainer.Add(static_cast<GInt64>(
1298 120 : static_cast<const arrow::UInt32Array *>(array)->Value(nIdx)));
1299 120 : break;
1300 : }
1301 4910 : case arrow::Type::INT64:
1302 : {
1303 4910 : oContainer.Add(static_cast<GInt64>(
1304 4910 : static_cast<const arrow::Int64Array *>(array)->Value(nIdx)));
1305 4910 : break;
1306 : }
1307 741 : case arrow::Type::UINT64:
1308 : {
1309 741 : oContainer.Add(static_cast<uint64_t>(
1310 741 : static_cast<const arrow::UInt64Array *>(array)->Value(nIdx)));
1311 741 : break;
1312 : }
1313 338 : case arrow::Type::HALF_FLOAT:
1314 : {
1315 : const uint16_t nFloat16 =
1316 338 : static_cast<const arrow::HalfFloatArray *>(array)->Value(nIdx);
1317 338 : uint32_t nFloat32 = CPLHalfToFloat(nFloat16);
1318 : float f;
1319 338 : memcpy(&f, &nFloat32, sizeof(nFloat32));
1320 338 : oContainer.Add(f);
1321 338 : break;
1322 : }
1323 741 : case arrow::Type::FLOAT:
1324 : {
1325 741 : oContainer.Add(
1326 741 : static_cast<const arrow::FloatArray *>(array)->Value(nIdx));
1327 741 : break;
1328 : }
1329 3981 : case arrow::Type::DOUBLE:
1330 : {
1331 3981 : oContainer.Add(
1332 3972 : static_cast<const arrow::DoubleArray *>(array)->Value(nIdx));
1333 3981 : break;
1334 : }
1335 :
1336 : #if ARROW_VERSION_MAJOR >= 18
1337 0 : case arrow::Type::DECIMAL32:
1338 : {
1339 0 : oContainer.Add(
1340 0 : CPLAtof(static_cast<const arrow::Decimal32Array *>(array)
1341 : ->FormatValue(nIdx)
1342 : .c_str()));
1343 0 : break;
1344 : }
1345 0 : case arrow::Type::DECIMAL64:
1346 : {
1347 0 : oContainer.Add(
1348 0 : CPLAtof(static_cast<const arrow::Decimal64Array *>(array)
1349 : ->FormatValue(nIdx)
1350 : .c_str()));
1351 0 : break;
1352 : }
1353 : #endif
1354 1354 : case arrow::Type::DECIMAL128:
1355 : {
1356 1354 : oContainer.Add(
1357 1350 : CPLAtof(static_cast<const arrow::Decimal128Array *>(array)
1358 : ->FormatValue(nIdx)
1359 : .c_str()));
1360 1354 : break;
1361 : }
1362 112 : case arrow::Type::DECIMAL256:
1363 : {
1364 112 : oContainer.Add(
1365 108 : CPLAtof(static_cast<const arrow::Decimal256Array *>(array)
1366 : ->FormatValue(nIdx)
1367 : .c_str()));
1368 112 : break;
1369 : }
1370 3611 : case arrow::Type::STRING:
1371 : {
1372 3611 : oContainer.Add(
1373 : static_cast<const arrow::StringArray *>(array)->GetString(
1374 : nIdx));
1375 3611 : break;
1376 : }
1377 112 : case arrow::Type::LARGE_STRING:
1378 : {
1379 112 : oContainer.Add(
1380 : static_cast<const arrow::LargeStringArray *>(array)->GetString(
1381 : nIdx));
1382 112 : break;
1383 : }
1384 : #if ARROW_VERSION_MAJOR >= 15
1385 4 : case arrow::Type::STRING_VIEW:
1386 : {
1387 4 : oContainer.Add(
1388 : static_cast<const arrow::StringViewArray *>(array)->GetString(
1389 : nIdx));
1390 4 : break;
1391 : }
1392 : #endif
1393 6511 : case arrow::Type::LIST:
1394 : case arrow::Type::LARGE_LIST:
1395 : case arrow::Type::FIXED_SIZE_LIST:
1396 : case arrow::Type::MAP:
1397 : case arrow::Type::STRUCT:
1398 : {
1399 6511 : oContainer.Add(GetObjectAsJSON(array, nIdx));
1400 6511 : break;
1401 : }
1402 :
1403 4 : case arrow::Type::BINARY:
1404 : {
1405 4 : const auto castArray =
1406 : static_cast<const arrow::BinaryArray *>(array);
1407 4 : int length = 0;
1408 4 : const uint8_t *data = castArray->GetValue(nIdx, &length);
1409 4 : bool bIsASCII = true;
1410 9 : for (int i = 0; i < length && bIsASCII; ++i)
1411 5 : bIsASCII = data[i] >= 32 && data[i] <= 127;
1412 4 : if (bIsASCII)
1413 : {
1414 3 : oContainer.Add(
1415 : std::string(reinterpret_cast<const char *>(data), length));
1416 : }
1417 : else
1418 : {
1419 1 : char *pszBase64 = CPLBase64Encode(length, data);
1420 1 : oContainer.Add(std::string("base64:").append(pszBase64));
1421 1 : CPLFree(pszBase64);
1422 : }
1423 4 : break;
1424 : }
1425 :
1426 : #if ARROW_VERSION_MAJOR >= 15
1427 0 : case arrow::Type::BINARY_VIEW:
1428 : {
1429 0 : const auto castArray =
1430 : static_cast<const arrow::BinaryViewArray *>(array);
1431 0 : const auto view = castArray->GetView(nIdx);
1432 0 : if (view.size() <= INT_MAX)
1433 : {
1434 0 : const int length = static_cast<int>(view.size());
1435 0 : const char *data = view.data();
1436 0 : bool bIsASCII = true;
1437 0 : for (int i = 0; i < length && bIsASCII; ++i)
1438 0 : bIsASCII =
1439 0 : data[i] >= 32 && static_cast<unsigned>(data[i]) <= 127;
1440 0 : if (bIsASCII)
1441 : {
1442 0 : oContainer.Add(std::string(view));
1443 : }
1444 : else
1445 : {
1446 0 : char *pszBase64 = CPLBase64Encode(
1447 : length, reinterpret_cast<const GByte *>(data));
1448 0 : oContainer.Add(std::string("base64:").append(pszBase64));
1449 0 : CPLFree(pszBase64);
1450 : }
1451 : }
1452 : else
1453 : {
1454 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too large binary view");
1455 : }
1456 0 : break;
1457 : }
1458 : #endif
1459 :
1460 0 : default:
1461 : {
1462 0 : CPLDebug("ARROW", "AddToContainer(): unexpected data type %s",
1463 0 : array->type()->ToString().c_str());
1464 0 : break;
1465 : }
1466 : }
1467 26978 : }
1468 :
1469 : /************************************************************************/
1470 : /* AddToArray() */
1471 : /************************************************************************/
1472 :
1473 6406 : static void AddToArray(CPLJSONArray &oArray, const arrow::Array *array,
1474 : const size_t nIdx)
1475 : {
1476 6406 : AddToContainer(oArray, array, nIdx);
1477 6406 : }
1478 :
1479 : /************************************************************************/
1480 : /* GetListAsJSON() */
1481 : /************************************************************************/
1482 :
1483 : template <class ArrowType>
1484 4978 : static CPLJSONArray GetListAsJSON(const ArrowType *array,
1485 : const size_t nIdxInArray)
1486 : {
1487 9956 : const auto values = array->values();
1488 4978 : const auto nIdxStart = array->value_offset(nIdxInArray);
1489 4978 : const auto nCount = array->value_length(nIdxInArray);
1490 4978 : CPLJSONArray oArray;
1491 13738 : for (auto k = decltype(nCount){0}; k < nCount; k++)
1492 : {
1493 8760 : if (values->IsNull(nIdxStart + k))
1494 2354 : oArray.AddNull();
1495 : else
1496 6406 : AddToArray(oArray, values.get(),
1497 6406 : static_cast<size_t>(nIdxStart + k));
1498 : }
1499 9956 : return oArray;
1500 : }
1501 :
1502 : /************************************************************************/
1503 : /* AddToDict() */
1504 : /************************************************************************/
1505 :
1506 : namespace
1507 : {
1508 : struct ContainerAdapter
1509 : {
1510 : CPLJSONObject &m_oDict;
1511 : const std::string m_osKey;
1512 :
1513 20572 : ContainerAdapter(CPLJSONObject &oDictIn, const std::string &osKeyIn)
1514 20572 : : m_oDict(oDictIn), m_osKey(osKeyIn)
1515 : {
1516 20572 : }
1517 :
1518 20572 : template <class T> void Add(const T &v)
1519 : {
1520 20572 : m_oDict.Add(m_osKey, v);
1521 20572 : }
1522 : };
1523 : } // namespace
1524 :
1525 20572 : static void AddToDict(CPLJSONObject &oDict, const std::string &osKey,
1526 : const arrow::Array *array, const size_t nIdx)
1527 : {
1528 41144 : ContainerAdapter dictAdapter(oDict, osKey);
1529 20572 : AddToContainer(dictAdapter, array, nIdx);
1530 20572 : }
1531 :
1532 : /************************************************************************/
1533 : /* GetMapAsJSON() */
1534 : /************************************************************************/
1535 :
1536 : template <class KeyArrayType>
1537 25129 : static CPLJSONObject GetMapAsJSON(const arrow::Array *array,
1538 : const size_t nIdxInArray)
1539 : {
1540 25129 : const auto mapArray = static_cast<const arrow::MapArray *>(array);
1541 50258 : const auto keys = std::static_pointer_cast<KeyArrayType>(mapArray->keys());
1542 50258 : const auto values = mapArray->items();
1543 25129 : const auto nIdxStart = mapArray->value_offset(nIdxInArray);
1544 25129 : const int nCount = mapArray->value_length(nIdxInArray);
1545 25129 : CPLJSONObject oRoot;
1546 45766 : for (int k = 0; k < nCount; k++)
1547 : {
1548 20637 : if (!keys->IsNull(nIdxStart + k))
1549 : {
1550 41274 : const auto osKey = keys->GetString(nIdxStart + k);
1551 20637 : if (!values->IsNull(nIdxStart + k))
1552 13731 : AddToDict(oRoot, osKey, values.get(), nIdxStart + k);
1553 : else
1554 6906 : oRoot.AddNull(osKey);
1555 : }
1556 : }
1557 50258 : return oRoot;
1558 : }
1559 :
1560 25129 : static CPLJSONObject GetMapAsJSON(const arrow::Array *array,
1561 : const size_t nIdxInArray)
1562 : {
1563 25129 : const auto mapArray = static_cast<const arrow::MapArray *>(array);
1564 25129 : const auto eKeyType = mapArray->keys()->type()->id();
1565 25129 : if (eKeyType == arrow::Type::STRING)
1566 25127 : return GetMapAsJSON<arrow::StringArray>(array, nIdxInArray);
1567 : #if ARROW_VERSION_MAJOR >= 15
1568 2 : else if (eKeyType == arrow::Type::STRING_VIEW)
1569 2 : return GetMapAsJSON<arrow::StringViewArray>(array, nIdxInArray);
1570 : #endif
1571 : else
1572 : {
1573 0 : CPLAssert(false);
1574 : return CPLJSONObject();
1575 : }
1576 : }
1577 :
1578 : /************************************************************************/
1579 : /* GetStructureAsJSON() */
1580 : /************************************************************************/
1581 :
1582 3567 : static CPLJSONObject GetStructureAsJSON(const arrow::Array *array,
1583 : const size_t nIdxInArray)
1584 : {
1585 3567 : CPLJSONObject oRoot;
1586 3567 : const auto structArray = static_cast<const arrow::StructArray *>(array);
1587 7134 : const auto structArrayType = structArray->type();
1588 13663 : for (int i = 0; i < structArrayType->num_fields(); ++i)
1589 : {
1590 20192 : const auto field = structArray->field(i);
1591 10096 : if (!field->IsNull(nIdxInArray))
1592 : {
1593 6841 : AddToDict(oRoot, structArrayType->field(i)->name(), field.get(),
1594 : nIdxInArray);
1595 : }
1596 : else
1597 3255 : oRoot.AddNull(structArrayType->field(i)->name());
1598 : }
1599 :
1600 7134 : return oRoot;
1601 : }
1602 :
1603 : /************************************************************************/
1604 : /* GetObjectAsJSON() */
1605 : /************************************************************************/
1606 :
1607 6511 : static CPLJSONObject GetObjectAsJSON(const arrow::Array *array,
1608 : const size_t nIdxInArray)
1609 : {
1610 6511 : switch (array->type()->id())
1611 : {
1612 26 : case arrow::Type::MAP:
1613 26 : return GetMapAsJSON(array, nIdxInArray);
1614 2627 : case arrow::Type::LIST:
1615 5254 : return GetListAsJSON(static_cast<const arrow::ListArray *>(array),
1616 2627 : nIdxInArray);
1617 111 : case arrow::Type::LARGE_LIST:
1618 222 : return GetListAsJSON(
1619 111 : static_cast<const arrow::LargeListArray *>(array), nIdxInArray);
1620 180 : case arrow::Type::FIXED_SIZE_LIST:
1621 360 : return GetListAsJSON(
1622 : static_cast<const arrow::FixedSizeListArray *>(array),
1623 180 : nIdxInArray);
1624 3567 : case arrow::Type::STRUCT:
1625 3567 : return GetStructureAsJSON(array, nIdxInArray);
1626 0 : default:
1627 : {
1628 0 : CPLError(CE_Failure, CPLE_AppDefined,
1629 : "GetObjectAsJSON(): unhandled value format: %s",
1630 0 : array->type()->ToString().c_str());
1631 0 : return CPLJSONObject();
1632 : }
1633 : }
1634 : }
1635 :
1636 : template <class OGRType, class ArrowType, class ArrayType>
1637 32552 : static void ReadList(OGRFeature *poFeature, int i, int64_t nIdxInArray,
1638 : const ArrayType *array)
1639 : {
1640 65104 : const auto values = std::static_pointer_cast<ArrowType>(array->values());
1641 32552 : const auto nIdxStart = array->value_offset(nIdxInArray);
1642 32552 : const int nCount = array->value_length(nIdxInArray);
1643 65104 : std::vector<OGRType> aValues;
1644 32552 : aValues.reserve(nCount);
1645 97422 : for (int k = 0; k < nCount; k++)
1646 : {
1647 64870 : aValues.push_back(static_cast<OGRType>(values->Value(nIdxStart + k)));
1648 : }
1649 32552 : poFeature->SetField(i, nCount, aValues.data());
1650 32552 : }
1651 :
1652 : template <class ArrowType, class ArrayType>
1653 7251 : static void ReadListDouble(OGRFeature *poFeature, int i, int64_t nIdxInArray,
1654 : const ArrayType *array)
1655 : {
1656 14502 : const auto values = std::static_pointer_cast<ArrowType>(array->values());
1657 7251 : const auto rawValues = values->raw_values();
1658 7251 : const auto nIdxStart = array->value_offset(nIdxInArray);
1659 7251 : const int nCount = array->value_length(nIdxInArray);
1660 14502 : std::vector<double> aValues;
1661 7251 : aValues.reserve(nCount);
1662 21231 : for (int k = 0; k < nCount; k++)
1663 : {
1664 13980 : if (values->IsNull(nIdxStart + k))
1665 2970 : aValues.push_back(std::numeric_limits<double>::quiet_NaN());
1666 : else
1667 11010 : aValues.push_back(rawValues[nIdxStart + k]);
1668 : }
1669 7251 : poFeature->SetField(i, nCount, aValues.data());
1670 7251 : }
1671 :
1672 : template <class ArrayType>
1673 50038 : static void ReadList(OGRFeature *poFeature, int i, int64_t nIdxInArray,
1674 : const ArrayType *array, arrow::Type::type valueTypeId)
1675 : {
1676 50038 : switch (valueTypeId)
1677 : {
1678 3429 : case arrow::Type::BOOL:
1679 : {
1680 3429 : ReadList<int, arrow::BooleanArray>(poFeature, i, nIdxInArray,
1681 : array);
1682 3429 : break;
1683 : }
1684 3259 : case arrow::Type::UINT8:
1685 : {
1686 3259 : ReadList<int, arrow::UInt8Array>(poFeature, i, nIdxInArray, array);
1687 3259 : break;
1688 : }
1689 3259 : case arrow::Type::INT8:
1690 : {
1691 3259 : ReadList<int, arrow::Int8Array>(poFeature, i, nIdxInArray, array);
1692 3259 : break;
1693 : }
1694 3259 : case arrow::Type::UINT16:
1695 : {
1696 3259 : ReadList<int, arrow::UInt16Array>(poFeature, i, nIdxInArray, array);
1697 3259 : break;
1698 : }
1699 3259 : case arrow::Type::INT16:
1700 : {
1701 3259 : ReadList<int, arrow::Int16Array>(poFeature, i, nIdxInArray, array);
1702 3259 : break;
1703 : }
1704 4109 : case arrow::Type::INT32:
1705 : {
1706 4109 : ReadList<int, arrow::Int32Array>(poFeature, i, nIdxInArray, array);
1707 4109 : break;
1708 : }
1709 440 : case arrow::Type::UINT32:
1710 : {
1711 440 : ReadList<GIntBig, arrow::UInt32Array>(poFeature, i, nIdxInArray,
1712 : array);
1713 440 : break;
1714 : }
1715 8279 : case arrow::Type::INT64:
1716 : {
1717 8279 : ReadList<GIntBig, arrow::Int64Array>(poFeature, i, nIdxInArray,
1718 : array);
1719 8279 : break;
1720 : }
1721 3259 : case arrow::Type::UINT64:
1722 : {
1723 3259 : ReadList<double, arrow::UInt64Array>(poFeature, i, nIdxInArray,
1724 : array);
1725 3259 : break;
1726 : }
1727 199 : case arrow::Type::HALF_FLOAT:
1728 : {
1729 398 : const auto values = std::static_pointer_cast<arrow::HalfFloatArray>(
1730 : array->values());
1731 199 : const auto nIdxStart = array->value_offset(nIdxInArray);
1732 199 : const int nCount = array->value_length(nIdxInArray);
1733 398 : std::vector<double> aValues;
1734 199 : aValues.reserve(nCount);
1735 560 : for (int k = 0; k < nCount; k++)
1736 : {
1737 361 : if (values->IsNull(nIdxStart + k))
1738 131 : aValues.push_back(std::numeric_limits<double>::quiet_NaN());
1739 : else
1740 : {
1741 230 : const uint16_t nFloat16 = values->Value(nIdxStart + k);
1742 230 : uint32_t nFloat32 = CPLHalfToFloat(nFloat16);
1743 : float f;
1744 230 : memcpy(&f, &nFloat32, sizeof(nFloat32));
1745 230 : aValues.push_back(f);
1746 : }
1747 : }
1748 199 : poFeature->SetField(i, nCount, aValues.data());
1749 199 : break;
1750 : }
1751 3490 : case arrow::Type::FLOAT:
1752 : {
1753 3490 : ReadListDouble<arrow::FloatArray>(poFeature, i, nIdxInArray, array);
1754 3490 : break;
1755 : }
1756 3761 : case arrow::Type::DOUBLE:
1757 : {
1758 3761 : ReadListDouble<arrow::DoubleArray>(poFeature, i, nIdxInArray,
1759 : array);
1760 3761 : break;
1761 : }
1762 :
1763 : #if ARROW_VERSION_MAJOR >= 18
1764 0 : case arrow::Type::DECIMAL32:
1765 : {
1766 0 : const auto values = std::static_pointer_cast<arrow::Decimal32Array>(
1767 : array->values());
1768 0 : const auto nIdxStart = array->value_offset(nIdxInArray);
1769 0 : const int nCount = array->value_length(nIdxInArray);
1770 0 : std::vector<double> aValues;
1771 0 : aValues.reserve(nCount);
1772 0 : for (int k = 0; k < nCount; k++)
1773 : {
1774 0 : if (values->IsNull(nIdxStart + k))
1775 0 : aValues.push_back(std::numeric_limits<double>::quiet_NaN());
1776 : else
1777 0 : aValues.push_back(
1778 0 : CPLAtof(values->FormatValue(nIdxStart + k).c_str()));
1779 : }
1780 0 : poFeature->SetField(i, nCount, aValues.data());
1781 0 : break;
1782 : }
1783 :
1784 0 : case arrow::Type::DECIMAL64:
1785 : {
1786 0 : const auto values = std::static_pointer_cast<arrow::Decimal64Array>(
1787 : array->values());
1788 0 : const auto nIdxStart = array->value_offset(nIdxInArray);
1789 0 : const int nCount = array->value_length(nIdxInArray);
1790 0 : std::vector<double> aValues;
1791 0 : aValues.reserve(nCount);
1792 0 : for (int k = 0; k < nCount; k++)
1793 : {
1794 0 : if (values->IsNull(nIdxStart + k))
1795 0 : aValues.push_back(std::numeric_limits<double>::quiet_NaN());
1796 : else
1797 0 : aValues.push_back(
1798 0 : CPLAtof(values->FormatValue(nIdxStart + k).c_str()));
1799 : }
1800 0 : poFeature->SetField(i, nCount, aValues.data());
1801 0 : break;
1802 : }
1803 : #endif
1804 :
1805 1485 : case arrow::Type::DECIMAL128:
1806 : {
1807 2970 : const auto values =
1808 : std::static_pointer_cast<arrow::Decimal128Array>(
1809 : array->values());
1810 1485 : const auto nIdxStart = array->value_offset(nIdxInArray);
1811 1485 : const int nCount = array->value_length(nIdxInArray);
1812 2970 : std::vector<double> aValues;
1813 1485 : aValues.reserve(nCount);
1814 2970 : for (int k = 0; k < nCount; k++)
1815 : {
1816 1485 : if (values->IsNull(nIdxStart + k))
1817 346 : aValues.push_back(std::numeric_limits<double>::quiet_NaN());
1818 : else
1819 1139 : aValues.push_back(
1820 1139 : CPLAtof(values->FormatValue(nIdxStart + k).c_str()));
1821 : }
1822 1485 : poFeature->SetField(i, nCount, aValues.data());
1823 1485 : break;
1824 : }
1825 :
1826 1485 : case arrow::Type::DECIMAL256:
1827 : {
1828 2970 : const auto values =
1829 : std::static_pointer_cast<arrow::Decimal256Array>(
1830 : array->values());
1831 1485 : const auto nIdxStart = array->value_offset(nIdxInArray);
1832 1485 : const int nCount = array->value_length(nIdxInArray);
1833 2970 : std::vector<double> aValues;
1834 1485 : aValues.reserve(nCount);
1835 2970 : for (int k = 0; k < nCount; k++)
1836 : {
1837 1485 : if (values->IsNull(nIdxStart + k))
1838 346 : aValues.push_back(std::numeric_limits<double>::quiet_NaN());
1839 : else
1840 1139 : aValues.push_back(
1841 1139 : CPLAtof(values->FormatValue(nIdxStart + k).c_str()));
1842 : }
1843 1485 : poFeature->SetField(i, nCount, aValues.data());
1844 1485 : break;
1845 : }
1846 :
1847 3517 : case arrow::Type::STRING:
1848 : {
1849 7034 : const auto values =
1850 : std::static_pointer_cast<arrow::StringArray>(array->values());
1851 3517 : const auto nIdxStart = array->value_offset(nIdxInArray);
1852 3517 : const int nCount = array->value_length(nIdxInArray);
1853 7034 : CPLStringList aosList;
1854 9164 : for (int k = 0; k < nCount; k++)
1855 : {
1856 5647 : if (values->IsNull(nIdxStart + k))
1857 354 : aosList.AddString(
1858 : ""); // we cannot have null strings in a list
1859 : else
1860 5293 : aosList.AddString(values->GetString(nIdxStart + k).c_str());
1861 : }
1862 3517 : poFeature->SetField(i, aosList.List());
1863 3517 : break;
1864 : }
1865 :
1866 1485 : case arrow::Type::LARGE_STRING:
1867 : {
1868 2970 : const auto values =
1869 : std::static_pointer_cast<arrow::LargeStringArray>(
1870 : array->values());
1871 1485 : const auto nIdxStart = array->value_offset(nIdxInArray);
1872 1485 : const auto nCount = array->value_length(nIdxInArray);
1873 2970 : CPLStringList aosList;
1874 3236 : for (auto k = decltype(nCount){0}; k < nCount; k++)
1875 : {
1876 1751 : if (values->IsNull(nIdxStart + k))
1877 354 : aosList.AddString(
1878 : ""); // we cannot have null strings in a list
1879 : else
1880 1397 : aosList.AddString(values->GetString(nIdxStart + k).c_str());
1881 : }
1882 1485 : poFeature->SetField(i, aosList.List());
1883 1485 : break;
1884 : }
1885 :
1886 : #if ARROW_VERSION_MAJOR >= 15
1887 2 : case arrow::Type::STRING_VIEW:
1888 : {
1889 4 : const auto values =
1890 : std::static_pointer_cast<arrow::StringViewArray>(
1891 : array->values());
1892 2 : const auto nIdxStart = array->value_offset(nIdxInArray);
1893 2 : const int nCount = array->value_length(nIdxInArray);
1894 4 : CPLStringList aosList;
1895 6 : for (int k = 0; k < nCount; k++)
1896 : {
1897 4 : if (values->IsNull(nIdxStart + k))
1898 1 : aosList.AddString(
1899 : ""); // we cannot have null strings in a list
1900 : else
1901 3 : aosList.AddString(values->GetString(nIdxStart + k).c_str());
1902 : }
1903 2 : poFeature->SetField(i, aosList.List());
1904 2 : break;
1905 : }
1906 : #endif
1907 :
1908 2 : case arrow::Type::BINARY:
1909 : {
1910 4 : const auto values =
1911 : std::static_pointer_cast<arrow::BinaryArray>(array->values());
1912 2 : const auto nIdxStart = array->value_offset(nIdxInArray);
1913 2 : const auto nCount = array->value_length(nIdxInArray);
1914 4 : CPLStringList aosList;
1915 6 : for (auto k = decltype(nCount){0}; k < nCount; k++)
1916 : {
1917 4 : if (values->IsNull(nIdxStart + k))
1918 : {
1919 1 : aosList.AddString(
1920 : ""); // we cannot have null strings in a list
1921 : }
1922 : else
1923 : {
1924 3 : int length = 0;
1925 3 : const uint8_t *data =
1926 3 : values->GetValue(nIdxStart + k, &length);
1927 3 : bool bIsASCII = true;
1928 10 : for (int j = 0; j < length && bIsASCII; ++j)
1929 7 : bIsASCII = data[j] >= 32 && data[j] <= 127;
1930 3 : if (bIsASCII)
1931 : {
1932 2 : aosList.AddString(
1933 : std::string(reinterpret_cast<const char *>(data),
1934 : length)
1935 : .c_str());
1936 : }
1937 : else
1938 : {
1939 1 : char *pszBase64 = CPLBase64Encode(length, data);
1940 1 : aosList.AddString(
1941 2 : std::string("base64:").append(pszBase64).c_str());
1942 1 : CPLFree(pszBase64);
1943 : }
1944 : }
1945 : }
1946 2 : poFeature->SetField(i, aosList.List());
1947 2 : break;
1948 : }
1949 :
1950 2060 : case arrow::Type::LIST:
1951 : case arrow::Type::LARGE_LIST:
1952 : case arrow::Type::FIXED_SIZE_LIST:
1953 : case arrow::Type::MAP:
1954 : case arrow::Type::STRUCT:
1955 : {
1956 2060 : poFeature->SetField(
1957 : i, GetListAsJSON(array, static_cast<size_t>(nIdxInArray))
1958 : .Format(CPLJSONObject::PrettyFormat::Plain)
1959 : .c_str());
1960 2060 : break;
1961 : }
1962 :
1963 0 : default:
1964 : {
1965 0 : CPLDebug("ARROW", "ReadList(): unexpected data type %s",
1966 0 : array->values()->type()->ToString().c_str());
1967 0 : break;
1968 : }
1969 : }
1970 50038 : }
1971 :
1972 : /************************************************************************/
1973 : /* SetPointsOfLine() */
1974 : /************************************************************************/
1975 :
1976 : template <bool bHasZ, bool bHasM, int nDim>
1977 960 : void SetPointsOfLine(OGRLineString *poLS, const arrow::DoubleArray *pointValues,
1978 : size_t pointOffset, int numPoints)
1979 : {
1980 : if (!bHasZ && !bHasM)
1981 : {
1982 : static_assert(sizeof(OGRRawPoint) == 2 * sizeof(double),
1983 : "sizeof(OGRRawPoint) == 2 * sizeof(double)");
1984 1240 : poLS->setPoints(numPoints,
1985 : reinterpret_cast<const OGRRawPoint *>(
1986 620 : pointValues->raw_values() + pointOffset));
1987 620 : return;
1988 : }
1989 :
1990 340 : poLS->setNumPoints(numPoints, FALSE);
1991 1634 : for (int k = 0; k < numPoints; k++)
1992 : {
1993 : if constexpr (bHasZ)
1994 : {
1995 : if constexpr (bHasM)
1996 : {
1997 330 : poLS->setPoint(k, pointValues->Value(pointOffset + nDim * k),
1998 330 : pointValues->Value(pointOffset + nDim * k + 1),
1999 330 : pointValues->Value(pointOffset + nDim * k + 2),
2000 330 : pointValues->Value(pointOffset + nDim * k + 3));
2001 : }
2002 : else
2003 : {
2004 634 : poLS->setPoint(k, pointValues->Value(pointOffset + nDim * k),
2005 634 : pointValues->Value(pointOffset + nDim * k + 1),
2006 634 : pointValues->Value(pointOffset + nDim * k + 2));
2007 : }
2008 : }
2009 : else /* if( bHasM ) */
2010 : {
2011 330 : poLS->setPointM(k, pointValues->Value(pointOffset + nDim * k),
2012 330 : pointValues->Value(pointOffset + nDim * k + 1),
2013 330 : pointValues->Value(pointOffset + nDim * k + 2));
2014 : }
2015 : }
2016 : }
2017 :
2018 : typedef void (*SetPointsOfLineType)(OGRLineString *, const arrow::DoubleArray *,
2019 : size_t, int);
2020 :
2021 836 : static SetPointsOfLineType GetSetPointsOfLine(bool bHasZ, bool bHasM)
2022 : {
2023 836 : if (bHasZ && bHasM)
2024 66 : return SetPointsOfLine<true, true, 4>;
2025 770 : if (bHasZ)
2026 168 : return SetPointsOfLine<true, false, 3>;
2027 602 : if (bHasM)
2028 66 : return SetPointsOfLine<false, true, 3>;
2029 536 : return SetPointsOfLine<false, false, 2>;
2030 : }
2031 :
2032 : /************************************************************************/
2033 : /* SetPointsOfLineStruct() */
2034 : /************************************************************************/
2035 :
2036 : template <bool bHasZ, bool bHasM, int nDim>
2037 1428 : void SetPointsOfLineStruct(OGRLineString *poLS,
2038 : const arrow::StructArray *structArray,
2039 : size_t pointOffset, int numPoints)
2040 : {
2041 1428 : CPLAssert(structArray->num_fields() == nDim);
2042 1428 : const auto &fields = structArray->fields();
2043 1428 : const auto &fieldX = fields[0];
2044 1428 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
2045 1428 : const auto fieldXDouble = static_cast<arrow::DoubleArray *>(fieldX.get());
2046 1428 : const auto &fieldY = fields[1];
2047 1428 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
2048 1428 : const auto fieldYDouble = static_cast<arrow::DoubleArray *>(fieldY.get());
2049 1428 : const arrow::DoubleArray *fieldZDouble = nullptr;
2050 1428 : const arrow::DoubleArray *fieldMDouble = nullptr;
2051 1428 : int iField = 2;
2052 : if constexpr (bHasZ)
2053 : {
2054 466 : const auto &field = fields[iField];
2055 466 : ++iField;
2056 466 : CPLAssert(field->type_id() == arrow::Type::DOUBLE);
2057 466 : fieldZDouble = static_cast<arrow::DoubleArray *>(field.get());
2058 : }
2059 : if constexpr (bHasM)
2060 : {
2061 26 : const auto &field = fields[iField];
2062 26 : CPLAssert(field->type_id() == arrow::Type::DOUBLE);
2063 26 : fieldMDouble = static_cast<arrow::DoubleArray *>(field.get());
2064 : }
2065 :
2066 1428 : poLS->setNumPoints(numPoints, FALSE);
2067 6544 : for (int k = 0; k < numPoints; k++)
2068 : {
2069 : if constexpr (bHasZ)
2070 : {
2071 : if constexpr (bHasM)
2072 : {
2073 55 : poLS->setPoint(k, fieldXDouble->Value(pointOffset + k),
2074 55 : fieldYDouble->Value(pointOffset + k),
2075 55 : fieldZDouble->Value(pointOffset + k),
2076 55 : fieldMDouble->Value(pointOffset + k));
2077 : }
2078 : else
2079 : {
2080 1335 : poLS->setPoint(k, fieldXDouble->Value(pointOffset + k),
2081 1335 : fieldYDouble->Value(pointOffset + k),
2082 1335 : fieldZDouble->Value(pointOffset + k));
2083 : }
2084 : }
2085 : else if constexpr (bHasM)
2086 : {
2087 55 : poLS->setPointM(k, fieldXDouble->Value(pointOffset + k),
2088 55 : fieldYDouble->Value(pointOffset + k),
2089 55 : fieldMDouble->Value(pointOffset + k));
2090 : }
2091 : else
2092 : {
2093 3671 : poLS->setPoint(k, fieldXDouble->Value(pointOffset + k),
2094 3671 : fieldYDouble->Value(pointOffset + k));
2095 : }
2096 : }
2097 1428 : }
2098 :
2099 : typedef void (*SetPointsOfLineStructType)(OGRLineString *,
2100 : const arrow::StructArray *, size_t,
2101 : int);
2102 :
2103 1100 : static SetPointsOfLineStructType GetSetPointsOfLineStruct(bool bHasZ,
2104 : bool bHasM)
2105 : {
2106 1100 : if (bHasZ && bHasM)
2107 11 : return SetPointsOfLineStruct<true, true, 4>;
2108 1089 : if (bHasZ)
2109 419 : return SetPointsOfLineStruct<true, false, 3>;
2110 670 : if (bHasM)
2111 11 : return SetPointsOfLineStruct<false, true, 3>;
2112 659 : return SetPointsOfLineStruct<false, false, 2>;
2113 : }
2114 :
2115 : /************************************************************************/
2116 : /* TimestampToOGR() */
2117 : /************************************************************************/
2118 :
2119 : inline void
2120 11200 : OGRArrowLayer::TimestampToOGR(int64_t timestamp,
2121 : const arrow::TimestampType *timestampType,
2122 : int nTZFlag, OGRField *psField)
2123 : {
2124 11200 : const auto unit = timestampType->unit();
2125 11200 : double floatingPart = 0;
2126 11200 : if (unit == arrow::TimeUnit::MILLI)
2127 : {
2128 7409 : floatingPart = (timestamp % 1000) / 1e3;
2129 7409 : timestamp /= 1000;
2130 : }
2131 3791 : else if (unit == arrow::TimeUnit::MICRO)
2132 : {
2133 3309 : floatingPart = (timestamp % (1000 * 1000)) / 1e6;
2134 3309 : timestamp /= 1000 * 1000;
2135 : }
2136 482 : else if (unit == arrow::TimeUnit::NANO)
2137 : {
2138 241 : floatingPart = (timestamp % (1000 * 1000 * 1000)) / 1e9;
2139 241 : timestamp /= 1000 * 1000 * 1000;
2140 : }
2141 11200 : if (nTZFlag > OGR_TZFLAG_MIXED_TZ)
2142 : {
2143 5602 : const int TZOffset = (nTZFlag - OGR_TZFLAG_UTC) * 15;
2144 5602 : timestamp += TZOffset * 60;
2145 : }
2146 : struct tm dt;
2147 11200 : CPLUnixTimeToYMDHMS(timestamp, &dt);
2148 11200 : psField->Date.Year = static_cast<GInt16>(dt.tm_year + 1900);
2149 11200 : psField->Date.Month = static_cast<GByte>(dt.tm_mon + 1);
2150 11200 : psField->Date.Day = static_cast<GByte>(dt.tm_mday);
2151 11200 : psField->Date.Hour = static_cast<GByte>(dt.tm_hour);
2152 11200 : psField->Date.Minute = static_cast<GByte>(dt.tm_min);
2153 11200 : psField->Date.TZFlag = static_cast<GByte>(nTZFlag);
2154 11200 : psField->Date.Second = static_cast<float>(dt.tm_sec + floatingPart);
2155 11200 : }
2156 :
2157 : /************************************************************************/
2158 : /* ReadFeature() */
2159 : /************************************************************************/
2160 :
2161 9971 : inline OGRFeature *OGRArrowLayer::ReadFeature(
2162 : int64_t nIdxInBatch,
2163 : const std::vector<std::shared_ptr<arrow::Array>> &poColumnArrays) const
2164 : {
2165 9971 : OGRFeature *poFeature = new OGRFeature(m_poFeatureDefn);
2166 :
2167 9971 : if (m_iFIDArrowColumn >= 0)
2168 : {
2169 3400 : const int iCol =
2170 3400 : m_bIgnoredFields ? m_nRequestedFIDColumn : m_iFIDArrowColumn;
2171 3400 : const arrow::Array *array = poColumnArrays[iCol].get();
2172 3400 : if (!array->IsNull(nIdxInBatch))
2173 : {
2174 3400 : if (array->type_id() == arrow::Type::INT64)
2175 : {
2176 3400 : const auto castArray =
2177 : static_cast<const arrow::Int64Array *>(array);
2178 3400 : poFeature->SetFID(
2179 3400 : static_cast<GIntBig>(castArray->Value(nIdxInBatch)));
2180 : }
2181 0 : else if (array->type_id() == arrow::Type::INT32)
2182 : {
2183 0 : const auto castArray =
2184 : static_cast<const arrow::Int32Array *>(array);
2185 0 : poFeature->SetFID(castArray->Value(nIdxInBatch));
2186 : }
2187 : }
2188 : }
2189 :
2190 9971 : const int nFieldCount = m_poFeatureDefn->GetFieldCount();
2191 176740 : for (int i = 0; i < nFieldCount; ++i)
2192 : {
2193 : int iCol;
2194 166769 : if (m_bIgnoredFields)
2195 : {
2196 16640 : iCol = m_anMapFieldIndexToArrayIndex[i];
2197 16640 : if (iCol < 0)
2198 5042 : continue;
2199 : }
2200 : else
2201 : {
2202 150129 : iCol = m_anMapFieldIndexToArrowColumn[i][0];
2203 : }
2204 :
2205 161727 : const arrow::Array *array = GetStorageArray(poColumnArrays[iCol].get());
2206 161727 : if (array->IsNull(nIdxInBatch))
2207 : {
2208 16826 : poFeature->SetFieldNull(i);
2209 16826 : continue;
2210 : }
2211 :
2212 144901 : int j = 1;
2213 144901 : bool bSkipToNextField = false;
2214 159125 : while (array->type_id() == arrow::Type::STRUCT)
2215 : {
2216 14224 : const auto castArray =
2217 : static_cast<const arrow::StructArray *>(array);
2218 14224 : const auto &subArrays = castArray->fields();
2219 14224 : CPLAssert(
2220 : j < static_cast<int>(m_anMapFieldIndexToArrowColumn[i].size()));
2221 14224 : const int iArrowSubcol = m_anMapFieldIndexToArrowColumn[i][j];
2222 14224 : j++;
2223 14224 : CPLAssert(iArrowSubcol < static_cast<int>(subArrays.size()));
2224 14224 : array = GetStorageArray(subArrays[iArrowSubcol].get());
2225 14224 : if (array->IsNull(nIdxInBatch))
2226 : {
2227 0 : poFeature->SetFieldNull(i);
2228 0 : bSkipToNextField = true;
2229 0 : break;
2230 : }
2231 : }
2232 144901 : if (bSkipToNextField)
2233 0 : continue;
2234 :
2235 144901 : if (array->type_id() == arrow::Type::DICTIONARY)
2236 : {
2237 1506 : const auto castArray =
2238 : static_cast<const arrow::DictionaryArray *>(array);
2239 : m_poReadFeatureTmpArray =
2240 1506 : castArray->indices(); // does not return a const reference
2241 1506 : array = GetStorageArray(m_poReadFeatureTmpArray.get());
2242 1506 : if (array->IsNull(nIdxInBatch))
2243 : {
2244 0 : poFeature->SetFieldNull(i);
2245 0 : continue;
2246 : }
2247 : }
2248 :
2249 144901 : switch (array->type_id())
2250 : {
2251 0 : case arrow::Type::NA:
2252 0 : break;
2253 :
2254 1554 : case arrow::Type::BOOL:
2255 : {
2256 1554 : const auto castArray =
2257 : static_cast<const arrow::BooleanArray *>(array);
2258 1554 : poFeature->SetFieldSameTypeUnsafe(
2259 1554 : i, castArray->Value(nIdxInBatch));
2260 1554 : break;
2261 : }
2262 1489 : case arrow::Type::UINT8:
2263 : {
2264 1489 : const auto castArray =
2265 : static_cast<const arrow::UInt8Array *>(array);
2266 1489 : poFeature->SetFieldSameTypeUnsafe(
2267 1489 : i, castArray->Value(nIdxInBatch));
2268 1489 : break;
2269 : }
2270 1485 : case arrow::Type::INT8:
2271 : {
2272 1485 : const auto castArray =
2273 : static_cast<const arrow::Int8Array *>(array);
2274 1485 : poFeature->SetFieldSameTypeUnsafe(
2275 1485 : i, castArray->Value(nIdxInBatch));
2276 1485 : break;
2277 : }
2278 1485 : case arrow::Type::UINT16:
2279 : {
2280 1485 : const auto castArray =
2281 : static_cast<const arrow::UInt16Array *>(array);
2282 1485 : poFeature->SetFieldSameTypeUnsafe(
2283 1485 : i, castArray->Value(nIdxInBatch));
2284 1485 : break;
2285 : }
2286 1563 : case arrow::Type::INT16:
2287 : {
2288 1563 : const auto castArray =
2289 : static_cast<const arrow::Int16Array *>(array);
2290 1563 : poFeature->SetFieldSameTypeUnsafe(
2291 1563 : i, castArray->Value(nIdxInBatch));
2292 1563 : break;
2293 : }
2294 199 : case arrow::Type::UINT32:
2295 : {
2296 199 : const auto castArray =
2297 : static_cast<const arrow::UInt32Array *>(array);
2298 199 : poFeature->SetFieldSameTypeUnsafe(
2299 199 : i, static_cast<GIntBig>(castArray->Value(nIdxInBatch)));
2300 199 : break;
2301 : }
2302 8513 : case arrow::Type::INT32:
2303 : {
2304 8513 : const auto castArray =
2305 : static_cast<const arrow::Int32Array *>(array);
2306 8513 : poFeature->SetFieldSameTypeUnsafe(
2307 : i, castArray->Value(nIdxInBatch));
2308 8513 : break;
2309 : }
2310 1485 : case arrow::Type::UINT64:
2311 : {
2312 1485 : const auto castArray =
2313 : static_cast<const arrow::UInt64Array *>(array);
2314 1485 : poFeature->SetFieldSameTypeUnsafe(
2315 1485 : i, static_cast<double>(castArray->Value(nIdxInBatch)));
2316 1485 : break;
2317 : }
2318 7552 : case arrow::Type::INT64:
2319 : {
2320 7552 : const auto castArray =
2321 : static_cast<const arrow::Int64Array *>(array);
2322 7552 : poFeature->SetFieldSameTypeUnsafe(
2323 7552 : i, static_cast<GIntBig>(castArray->Value(nIdxInBatch)));
2324 7552 : break;
2325 : }
2326 199 : case arrow::Type::HALF_FLOAT:
2327 : {
2328 199 : const auto castArray =
2329 : static_cast<const arrow::HalfFloatArray *>(array);
2330 199 : const uint16_t nFloat16 = castArray->Value(nIdxInBatch);
2331 199 : uint32_t nFloat32 = CPLHalfToFloat(nFloat16);
2332 : float f;
2333 199 : memcpy(&f, &nFloat32, sizeof(nFloat32));
2334 199 : poFeature->SetFieldSameTypeUnsafe(i, f);
2335 199 : break;
2336 : }
2337 1644 : case arrow::Type::FLOAT:
2338 : {
2339 1644 : const auto castArray =
2340 : static_cast<const arrow::FloatArray *>(array);
2341 1644 : poFeature->SetFieldSameTypeUnsafe(
2342 1644 : i, castArray->Value(nIdxInBatch));
2343 1644 : break;
2344 : }
2345 3655 : case arrow::Type::DOUBLE:
2346 : {
2347 3655 : const auto castArray =
2348 : static_cast<const arrow::DoubleArray *>(array);
2349 3655 : poFeature->SetFieldSameTypeUnsafe(
2350 : i, castArray->Value(nIdxInBatch));
2351 3655 : break;
2352 : }
2353 7548 : case arrow::Type::STRING:
2354 : {
2355 7548 : const auto castArray =
2356 : static_cast<const arrow::StringArray *>(array);
2357 7548 : int out_length = 0;
2358 : const uint8_t *data =
2359 7548 : castArray->GetValue(nIdxInBatch, &out_length);
2360 : char *pszString =
2361 7548 : static_cast<char *>(CPLMalloc(out_length + 1));
2362 7548 : memcpy(pszString, data, out_length);
2363 7548 : pszString[out_length] = 0;
2364 7548 : poFeature->SetFieldSameTypeUnsafe(i, pszString);
2365 7548 : break;
2366 : }
2367 1962 : case arrow::Type::BINARY:
2368 : {
2369 1962 : const auto castArray =
2370 : static_cast<const arrow::BinaryArray *>(array);
2371 1962 : int out_length = 0;
2372 : const uint8_t *data =
2373 1962 : castArray->GetValue(nIdxInBatch, &out_length);
2374 1962 : poFeature->SetField(i, out_length, data);
2375 1962 : break;
2376 : }
2377 : #if ARROW_VERSION_MAJOR >= 15
2378 3 : case arrow::Type::BINARY_VIEW:
2379 : {
2380 3 : const auto castArray =
2381 : static_cast<const arrow::BinaryViewArray *>(array);
2382 3 : const auto view = castArray->GetView(nIdxInBatch);
2383 3 : poFeature->SetField(i, static_cast<int>(view.size()),
2384 3 : view.data());
2385 3 : break;
2386 : }
2387 : #endif
2388 : #if ARROW_VERSION_MAJOR >= 15
2389 3 : case arrow::Type::STRING_VIEW:
2390 : {
2391 3 : const auto castArray =
2392 : static_cast<const arrow::StringViewArray *>(array);
2393 3 : const auto strView = castArray->GetView(nIdxInBatch);
2394 : char *pszString =
2395 3 : static_cast<char *>(CPLMalloc(strView.length() + 1));
2396 3 : memcpy(pszString, strView.data(), strView.length());
2397 3 : pszString[strView.length()] = 0;
2398 3 : poFeature->SetFieldSameTypeUnsafe(i, pszString);
2399 3 : break;
2400 : }
2401 : #endif
2402 1866 : case arrow::Type::FIXED_SIZE_BINARY:
2403 : {
2404 1866 : const auto castArray =
2405 : static_cast<const arrow::FixedSizeBinaryArray *>(array);
2406 1866 : const uint8_t *data = castArray->GetValue(nIdxInBatch);
2407 1866 : poFeature->SetField(i, castArray->byte_width(), data);
2408 1866 : break;
2409 : }
2410 3491 : case arrow::Type::DATE32:
2411 : {
2412 : // number of days since Epoch
2413 3491 : const auto castArray =
2414 : static_cast<const arrow::Date32Array *>(array);
2415 : int64_t timestamp =
2416 3491 : static_cast<int64_t>(castArray->Value(nIdxInBatch)) * 3600 *
2417 3491 : 24;
2418 : struct tm dt;
2419 3491 : CPLUnixTimeToYMDHMS(timestamp, &dt);
2420 3491 : poFeature->SetField(i, dt.tm_year + 1900, dt.tm_mon + 1,
2421 : dt.tm_mday, 0, 0, 0);
2422 3491 : break;
2423 : }
2424 241 : case arrow::Type::DATE64:
2425 : {
2426 : // number of milliseconds since Epoch
2427 241 : const auto castArray =
2428 : static_cast<const arrow::Date64Array *>(array);
2429 : int64_t timestamp =
2430 241 : static_cast<int64_t>(castArray->Value(nIdxInBatch)) / 1000;
2431 : struct tm dt;
2432 241 : CPLUnixTimeToYMDHMS(timestamp, &dt);
2433 241 : poFeature->SetField(i, dt.tm_year + 1900, dt.tm_mon + 1,
2434 : dt.tm_mday, 0, 0, 0);
2435 241 : break;
2436 : }
2437 11198 : case arrow::Type::TIMESTAMP:
2438 : {
2439 : const auto timestampType = static_cast<arrow::TimestampType *>(
2440 11198 : array->data()->type.get());
2441 11198 : const auto castArray =
2442 : static_cast<const arrow::TimestampArray *>(array);
2443 11198 : const int64_t timestamp = castArray->Value(nIdxInBatch);
2444 : OGRField sField;
2445 11198 : sField.Set.nMarker1 = OGRUnsetMarker;
2446 11198 : sField.Set.nMarker2 = OGRUnsetMarker;
2447 11198 : sField.Set.nMarker3 = OGRUnsetMarker;
2448 11198 : TimestampToOGR(timestamp, timestampType,
2449 11198 : m_poFeatureDefn->GetFieldDefn(i)->GetTZFlag(),
2450 : &sField);
2451 11198 : poFeature->SetField(i, &sField);
2452 11198 : break;
2453 : }
2454 3359 : case arrow::Type::TIME32:
2455 : {
2456 : const auto timestampType =
2457 3359 : static_cast<arrow::Time32Type *>(array->data()->type.get());
2458 3359 : const auto castArray =
2459 : static_cast<const arrow::Time32Array *>(array);
2460 3359 : const auto unit = timestampType->unit();
2461 3359 : int value = castArray->Value(nIdxInBatch);
2462 3359 : double floatingPart = 0;
2463 3359 : if (unit == arrow::TimeUnit::MILLI)
2464 : {
2465 3158 : floatingPart = (value % 1000) / 1e3;
2466 3158 : value /= 1000;
2467 : }
2468 3359 : const int nHour = value / 3600;
2469 3359 : const int nMinute = (value / 60) % 60;
2470 3359 : const int nSecond = value % 60;
2471 3359 : poFeature->SetField(i, 0, 0, 0, nHour, nMinute,
2472 3359 : static_cast<float>(nSecond + floatingPart));
2473 3359 : break;
2474 : }
2475 3063 : case arrow::Type::TIME64:
2476 : {
2477 3063 : const auto castArray =
2478 : static_cast<const arrow::Time64Array *>(array);
2479 3063 : poFeature->SetField(
2480 3063 : i, static_cast<GIntBig>(castArray->Value(nIdxInBatch)));
2481 3063 : break;
2482 : }
2483 :
2484 : #if ARROW_VERSION_MAJOR >= 18
2485 0 : case arrow::Type::DECIMAL32:
2486 : {
2487 0 : const auto castArray =
2488 : static_cast<const arrow::Decimal32Array *>(array);
2489 0 : poFeature->SetField(
2490 0 : i, CPLAtof(castArray->FormatValue(nIdxInBatch).c_str()));
2491 0 : break;
2492 : }
2493 :
2494 0 : case arrow::Type::DECIMAL64:
2495 : {
2496 0 : const auto castArray =
2497 : static_cast<const arrow::Decimal64Array *>(array);
2498 0 : poFeature->SetField(
2499 0 : i, CPLAtof(castArray->FormatValue(nIdxInBatch).c_str()));
2500 0 : break;
2501 : }
2502 : #endif
2503 :
2504 1564 : case arrow::Type::DECIMAL128:
2505 : {
2506 1564 : const auto castArray =
2507 : static_cast<const arrow::Decimal128Array *>(array);
2508 1564 : poFeature->SetField(
2509 3128 : i, CPLAtof(castArray->FormatValue(nIdxInBatch).c_str()));
2510 1564 : break;
2511 : }
2512 :
2513 1380 : case arrow::Type::DECIMAL256:
2514 : {
2515 1380 : const auto castArray =
2516 : static_cast<const arrow::Decimal256Array *>(array);
2517 1380 : poFeature->SetField(
2518 2760 : i, CPLAtof(castArray->FormatValue(nIdxInBatch).c_str()));
2519 1380 : break;
2520 : }
2521 :
2522 28750 : case arrow::Type::LIST:
2523 : {
2524 28750 : const auto castArray =
2525 : static_cast<const arrow::ListArray *>(array);
2526 : const auto listType = static_cast<const arrow::ListType *>(
2527 28750 : array->data()->type.get());
2528 28750 : ReadList(poFeature, i, nIdxInBatch, castArray,
2529 28750 : listType->value_field()->type()->id());
2530 28750 : break;
2531 : }
2532 :
2533 21288 : case arrow::Type::FIXED_SIZE_LIST:
2534 : {
2535 21288 : const auto castArray =
2536 : static_cast<const arrow::FixedSizeListArray *>(array);
2537 : const auto listType =
2538 : static_cast<const arrow::FixedSizeListType *>(
2539 21288 : array->data()->type.get());
2540 21288 : ReadList(poFeature, i, nIdxInBatch, castArray,
2541 21288 : listType->value_field()->type()->id());
2542 21288 : break;
2543 : }
2544 :
2545 1485 : case arrow::Type::LARGE_STRING:
2546 : {
2547 1485 : const auto castArray =
2548 : static_cast<const arrow::LargeStringArray *>(array);
2549 1485 : poFeature->SetField(i,
2550 2970 : castArray->GetString(nIdxInBatch).c_str());
2551 1485 : break;
2552 : }
2553 1774 : case arrow::Type::LARGE_BINARY:
2554 : {
2555 1774 : const auto castArray =
2556 : static_cast<const arrow::LargeBinaryArray *>(array);
2557 1774 : arrow::LargeBinaryArray::offset_type out_length = 0;
2558 : const uint8_t *data =
2559 1774 : castArray->GetValue(nIdxInBatch, &out_length);
2560 1774 : if (out_length >= 0 && out_length <= INT_MAX - 1)
2561 : {
2562 : // coverity[overflow_sink]
2563 1774 : poFeature->SetField(i, static_cast<int>(out_length), data);
2564 : }
2565 : else
2566 : {
2567 : // this is probably the most likely code path if people use
2568 : // LargeBinary...
2569 0 : CPLError(CE_Warning, CPLE_AppDefined,
2570 : "Too large binary: " CPL_FRMT_GUIB " bytes",
2571 : static_cast<GUIntBig>(out_length));
2572 : }
2573 1774 : break;
2574 : }
2575 :
2576 25103 : case arrow::Type::MAP:
2577 : {
2578 25103 : const auto castArray =
2579 : static_cast<const arrow::MapArray *>(array);
2580 25103 : poFeature->SetField(
2581 25103 : i, GetMapAsJSON(castArray, static_cast<size_t>(nIdxInBatch))
2582 50206 : .Format(CPLJSONObject::PrettyFormat::Plain)
2583 : .c_str());
2584 25103 : break;
2585 : }
2586 :
2587 : // unhandled types
2588 0 : case arrow::Type::STRUCT: // should not happen
2589 : case arrow::Type::INTERVAL_MONTHS:
2590 : case arrow::Type::INTERVAL_DAY_TIME:
2591 : case arrow::Type::SPARSE_UNION:
2592 : case arrow::Type::DENSE_UNION:
2593 : case arrow::Type::DICTIONARY:
2594 : case arrow::Type::EXTENSION:
2595 : case arrow::Type::DURATION:
2596 : case arrow::Type::LARGE_LIST:
2597 : case arrow::Type::INTERVAL_MONTH_DAY_NANO:
2598 : #if ARROW_VERSION_MAJOR >= 12
2599 : case arrow::Type::RUN_END_ENCODED:
2600 : #endif
2601 : #if ARROW_VERSION_MAJOR >= 15
2602 : case arrow::Type::LIST_VIEW:
2603 : case arrow::Type::LARGE_LIST_VIEW:
2604 : #endif
2605 : case arrow::Type::MAX_ID:
2606 : {
2607 : // Shouldn't happen normally as we should have discarded those
2608 : // fields when creating OGR field definitions
2609 0 : CPLError(CE_Warning, CPLE_AppDefined,
2610 : "Cannot read content for field %s",
2611 0 : m_poFeatureDefn->GetFieldDefn(i)->GetNameRef());
2612 0 : break;
2613 : }
2614 : }
2615 : }
2616 :
2617 9971 : const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
2618 19601 : for (int i = 0; i < nGeomFieldCount; ++i)
2619 : {
2620 : int iCol;
2621 9630 : if (m_bIgnoredFields)
2622 : {
2623 1121 : iCol = m_anMapGeomFieldIndexToArrayIndex[i];
2624 1121 : if (iCol < 0)
2625 54 : continue;
2626 : }
2627 : else
2628 : {
2629 8509 : iCol = m_anMapGeomFieldIndexToArrowColumn[i];
2630 : }
2631 :
2632 9576 : const auto array = GetStorageArray(poColumnArrays[iCol].get());
2633 9576 : auto poGeometry = ReadGeometry(i, array, nIdxInBatch);
2634 9576 : if (poGeometry)
2635 : {
2636 7017 : const auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(i);
2637 7418 : if (wkbFlatten(poGeometry->getGeometryType()) == wkbLineString &&
2638 401 : wkbFlatten(poGeomFieldDefn->GetType()) == wkbMultiLineString)
2639 : {
2640 : poGeometry =
2641 3 : OGRGeometryFactory::forceToMultiLineString(poGeometry);
2642 : }
2643 7903 : else if (wkbFlatten(poGeometry->getGeometryType()) == wkbPolygon &&
2644 889 : wkbFlatten(poGeomFieldDefn->GetType()) == wkbMultiPolygon)
2645 : {
2646 : poGeometry =
2647 33 : OGRGeometryFactory::forceToMultiPolygon(poGeometry);
2648 : }
2649 7017 : if (OGR_GT_HasZ(poGeomFieldDefn->GetType()) && !poGeometry->Is3D())
2650 : {
2651 63 : poGeometry->set3D(true);
2652 : }
2653 7017 : poFeature->SetGeomFieldDirectly(i, poGeometry);
2654 : }
2655 : }
2656 :
2657 9971 : return poFeature;
2658 : }
2659 :
2660 : /************************************************************************/
2661 : /* ReadGeometry() */
2662 : /************************************************************************/
2663 :
2664 9796 : inline OGRGeometry *OGRArrowLayer::ReadGeometry(int iGeomField,
2665 : const arrow::Array *array,
2666 : int64_t nIdxInBatch) const
2667 : {
2668 9796 : if (array->IsNull(nIdxInBatch))
2669 : {
2670 2608 : return nullptr;
2671 : }
2672 7188 : OGRGeometry *poGeometry = nullptr;
2673 7188 : const auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(iGeomField);
2674 7188 : const auto eGeomType = poGeomFieldDefn->GetType();
2675 7188 : const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
2676 7188 : const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
2677 7188 : const int nDim = 2 + (bHasZ ? 1 : 0) + (bHasM ? 1 : 0);
2678 :
2679 : const auto CreatePoint =
2680 798 : [bHasZ, bHasM](const arrow::DoubleArray *pointValues, int pointOffset)
2681 : {
2682 399 : if (bHasZ)
2683 : {
2684 199 : if (bHasM)
2685 : {
2686 66 : return new OGRPoint(pointValues->Value(pointOffset),
2687 66 : pointValues->Value(pointOffset + 1),
2688 66 : pointValues->Value(pointOffset + 2),
2689 66 : pointValues->Value(pointOffset + 3));
2690 : }
2691 : else
2692 : {
2693 133 : return new OGRPoint(pointValues->Value(pointOffset),
2694 133 : pointValues->Value(pointOffset + 1),
2695 133 : pointValues->Value(pointOffset + 2));
2696 : }
2697 : }
2698 200 : else if (bHasM)
2699 : {
2700 66 : return OGRPoint::createXYM(pointValues->Value(pointOffset),
2701 66 : pointValues->Value(pointOffset + 1),
2702 132 : pointValues->Value(pointOffset + 2));
2703 : }
2704 : else
2705 : {
2706 134 : return new OGRPoint(pointValues->Value(pointOffset),
2707 134 : pointValues->Value(pointOffset + 1));
2708 : }
2709 7188 : };
2710 :
2711 : const auto CreateStructPoint =
2712 667 : [nDim, bHasZ, bHasM](const arrow::StructArray *structArray,
2713 2001 : int64_t pointOffset)
2714 : {
2715 667 : CPL_IGNORE_RET_VAL(nDim);
2716 667 : CPLAssert(structArray->num_fields() == nDim);
2717 667 : const auto &fieldX = structArray->field(0);
2718 667 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
2719 : const auto fieldXDouble =
2720 667 : static_cast<arrow::DoubleArray *>(fieldX.get());
2721 667 : const auto &fieldY = structArray->field(1);
2722 667 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
2723 : const auto fieldYDouble =
2724 667 : static_cast<arrow::DoubleArray *>(fieldY.get());
2725 667 : if (bHasZ)
2726 : {
2727 278 : const auto &fieldZ = structArray->field(2);
2728 278 : CPLAssert(fieldZ->type_id() == arrow::Type::DOUBLE);
2729 : const auto fieldZDouble =
2730 278 : static_cast<arrow::DoubleArray *>(fieldZ.get());
2731 278 : if (bHasM)
2732 : {
2733 11 : const auto &fieldM = structArray->field(3);
2734 11 : CPLAssert(fieldM->type_id() == arrow::Type::DOUBLE);
2735 : const auto fieldMDouble =
2736 11 : static_cast<arrow::DoubleArray *>(fieldM.get());
2737 11 : return new OGRPoint(fieldXDouble->Value(pointOffset),
2738 11 : fieldYDouble->Value(pointOffset),
2739 11 : fieldZDouble->Value(pointOffset),
2740 11 : fieldMDouble->Value(pointOffset));
2741 : }
2742 : else
2743 : {
2744 267 : return new OGRPoint(fieldXDouble->Value(pointOffset),
2745 267 : fieldYDouble->Value(pointOffset),
2746 267 : fieldZDouble->Value(pointOffset));
2747 : }
2748 : }
2749 389 : else if (bHasM)
2750 : {
2751 11 : const auto &fieldM = structArray->field(2);
2752 11 : CPLAssert(fieldM->type_id() == arrow::Type::DOUBLE);
2753 : const auto fieldMDouble =
2754 11 : static_cast<arrow::DoubleArray *>(fieldM.get());
2755 11 : return OGRPoint::createXYM(fieldXDouble->Value(pointOffset),
2756 : fieldYDouble->Value(pointOffset),
2757 11 : fieldMDouble->Value(pointOffset));
2758 : }
2759 : else
2760 : {
2761 378 : return new OGRPoint(fieldXDouble->Value(pointOffset),
2762 378 : fieldYDouble->Value(pointOffset));
2763 : }
2764 7188 : };
2765 :
2766 : // Arrow 14 since https://github.com/apache/arrow/commit/95a8bfb319b2729c8f6daa069433caba3b4ddddd
2767 : // returns reference to shared pointers, so we can safely take the raw pointer
2768 : // and cast it.
2769 : // Earlier versions returned a non-reference shared pointer, so formally it
2770 : // is safer to use static_pointer_cast (although in practice given that
2771 : // "values" is a member variable), the Arrow >= 14 path might work...
2772 : #if ARROW_VERSION_MAJOR >= 14
2773 : #define GET_PTR_FROM_VALUES(var, type, values) \
2774 : const auto var = static_cast<const type *>((values).get())
2775 : #else
2776 : #define GET_PTR_FROM_VALUES(var, type, values) \
2777 : const auto var##tmp = std::static_pointer_cast<type>(values); \
2778 : const auto var = var##tmp.get()
2779 : #endif
2780 :
2781 7188 : switch (m_aeGeomEncoding[iGeomField])
2782 : {
2783 4248 : case OGRArrowGeomEncoding::WKB:
2784 : {
2785 4248 : int out_length = 0;
2786 : const uint8_t *data;
2787 4248 : if (array->type_id() == arrow::Type::BINARY)
2788 : {
2789 4237 : const auto castArray =
2790 : static_cast<const arrow::BinaryArray *>(array);
2791 4237 : data = castArray->GetValue(nIdxInBatch, &out_length);
2792 : }
2793 : else
2794 : {
2795 11 : CPLAssert(array->type_id() == arrow::Type::LARGE_BINARY);
2796 11 : const auto castArray =
2797 : static_cast<const arrow::LargeBinaryArray *>(array);
2798 11 : int64_t out_length64 = 0;
2799 11 : data = castArray->GetValue(nIdxInBatch, &out_length64);
2800 11 : if (out_length64 > INT_MAX)
2801 : {
2802 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too large geometry");
2803 0 : return nullptr;
2804 : }
2805 11 : out_length = static_cast<int>(out_length64);
2806 : }
2807 4248 : if (OGRGeometryFactory::createFromWkb(
2808 4248 : data, poGeomFieldDefn->GetSpatialRef(), &poGeometry,
2809 4248 : out_length) == OGRERR_NONE)
2810 : {
2811 : #ifdef DEBUG_ReadWKBBoundingBox
2812 : OGREnvelope sEnvelopeFromWKB;
2813 : bool bRet =
2814 : OGRWKBGetBoundingBox(data, out_length, sEnvelopeFromWKB);
2815 : CPLAssert(bRet);
2816 : OGREnvelope sEnvelopeFromGeom;
2817 : poGeometry->getEnvelope(&sEnvelopeFromGeom);
2818 : CPLAssert(sEnvelopeFromWKB == sEnvelopeFromGeom);
2819 : #endif
2820 : }
2821 4248 : break;
2822 : }
2823 :
2824 120 : case OGRArrowGeomEncoding::WKT:
2825 : {
2826 120 : if (array->type_id() == arrow::Type::STRING)
2827 : {
2828 119 : const auto castArray =
2829 : static_cast<const arrow::StringArray *>(array);
2830 238 : const auto osWKT = castArray->GetString(nIdxInBatch);
2831 119 : OGRGeometryFactory::createFromWkt(
2832 119 : osWKT.c_str(), poGeomFieldDefn->GetSpatialRef(),
2833 : &poGeometry);
2834 : }
2835 : else
2836 : {
2837 1 : CPLAssert(array->type_id() == arrow::Type::LARGE_STRING);
2838 1 : const auto castArray =
2839 : static_cast<const arrow::LargeStringArray *>(array);
2840 2 : const auto osWKT = castArray->GetString(nIdxInBatch);
2841 1 : OGRGeometryFactory::createFromWkt(
2842 1 : osWKT.c_str(), poGeomFieldDefn->GetSpatialRef(),
2843 : &poGeometry);
2844 : }
2845 120 : break;
2846 : }
2847 :
2848 0 : case OGRArrowGeomEncoding::GEOARROW_FSL_GENERIC:
2849 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_GENERIC:
2850 : {
2851 0 : CPLAssert(false);
2852 : break;
2853 : }
2854 :
2855 113 : case OGRArrowGeomEncoding::GEOARROW_FSL_POINT:
2856 : {
2857 113 : CPLAssert(array->type_id() == arrow::Type::FIXED_SIZE_LIST);
2858 113 : const auto listArray =
2859 : static_cast<const arrow::FixedSizeListArray *>(array);
2860 113 : CPLAssert(listArray->values()->type_id() == arrow::Type::DOUBLE);
2861 113 : GET_PTR_FROM_VALUES(pointValues, arrow::DoubleArray,
2862 : listArray->values());
2863 113 : if (!pointValues->IsNull(nDim * nIdxInBatch))
2864 : {
2865 113 : poGeometry = CreatePoint(pointValues,
2866 : static_cast<int>(nDim * nIdxInBatch));
2867 113 : poGeometry->assignSpatialReference(
2868 113 : poGeomFieldDefn->GetSpatialRef());
2869 : }
2870 113 : break;
2871 : }
2872 :
2873 100 : case OGRArrowGeomEncoding::GEOARROW_FSL_LINESTRING:
2874 : {
2875 100 : CPLAssert(array->type_id() == arrow::Type::LIST);
2876 100 : const auto listArray = static_cast<const arrow::ListArray *>(array);
2877 100 : CPLAssert(listArray->values()->type_id() ==
2878 : arrow::Type::FIXED_SIZE_LIST);
2879 100 : GET_PTR_FROM_VALUES(listOfPointsValues, arrow::FixedSizeListArray,
2880 : listArray->values());
2881 100 : CPLAssert(listOfPointsValues->values()->type_id() ==
2882 : arrow::Type::DOUBLE);
2883 100 : GET_PTR_FROM_VALUES(pointValues, arrow::DoubleArray,
2884 : listOfPointsValues->values());
2885 100 : const auto nPoints = listArray->value_length(nIdxInBatch);
2886 : const auto nPointOffset =
2887 100 : listArray->value_offset(nIdxInBatch) * nDim;
2888 100 : auto poLineString = new OGRLineString();
2889 100 : poGeometry = poLineString;
2890 100 : poGeometry->assignSpatialReference(
2891 100 : poGeomFieldDefn->GetSpatialRef());
2892 100 : if (nPoints)
2893 : {
2894 64 : GetSetPointsOfLine(bHasZ, bHasM)(poLineString, pointValues,
2895 : nPointOffset, nPoints);
2896 : }
2897 : else
2898 : {
2899 36 : poGeometry->set3D(bHasZ);
2900 36 : poGeometry->setMeasured(bHasM);
2901 : }
2902 100 : break;
2903 : }
2904 :
2905 468 : case OGRArrowGeomEncoding::GEOARROW_FSL_POLYGON:
2906 : {
2907 468 : CPLAssert(array->type_id() == arrow::Type::LIST);
2908 468 : const auto listOfRingsArray =
2909 : static_cast<const arrow::ListArray *>(array);
2910 468 : CPLAssert(listOfRingsArray->values()->type_id() ==
2911 : arrow::Type::LIST);
2912 468 : GET_PTR_FROM_VALUES(listOfRingsValues, arrow::ListArray,
2913 : listOfRingsArray->values());
2914 468 : CPLAssert(listOfRingsValues->values()->type_id() ==
2915 : arrow::Type::FIXED_SIZE_LIST);
2916 468 : GET_PTR_FROM_VALUES(listOfPointsValues, arrow::FixedSizeListArray,
2917 : listOfRingsValues->values());
2918 468 : CPLAssert(listOfPointsValues->values()->type_id() ==
2919 : arrow::Type::DOUBLE);
2920 468 : GET_PTR_FROM_VALUES(pointValues, arrow::DoubleArray,
2921 : listOfPointsValues->values());
2922 468 : const auto setPointsFun = GetSetPointsOfLine(bHasZ, bHasM);
2923 468 : const auto nRings = listOfRingsArray->value_length(nIdxInBatch);
2924 : const auto nRingOffset =
2925 468 : listOfRingsArray->value_offset(nIdxInBatch);
2926 468 : auto poPoly = new OGRPolygon();
2927 468 : poGeometry = poPoly;
2928 468 : poGeometry->assignSpatialReference(
2929 468 : poGeomFieldDefn->GetSpatialRef());
2930 954 : for (auto k = decltype(nRings){0}; k < nRings; k++)
2931 : {
2932 : const auto nPoints =
2933 486 : listOfRingsValues->value_length(nRingOffset + k);
2934 : const auto nPointOffset =
2935 486 : listOfRingsValues->value_offset(nRingOffset + k) * nDim;
2936 486 : auto poRing = new OGRLinearRing();
2937 486 : if (nPoints)
2938 : {
2939 486 : setPointsFun(poRing, pointValues, nPointOffset, nPoints);
2940 : }
2941 486 : poPoly->addRingDirectly(poRing);
2942 : }
2943 468 : if (poGeometry->IsEmpty())
2944 : {
2945 122 : poGeometry->set3D(bHasZ);
2946 122 : poGeometry->setMeasured(bHasM);
2947 : }
2948 468 : break;
2949 : }
2950 :
2951 148 : case OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOINT:
2952 : {
2953 148 : CPLAssert(array->type_id() == arrow::Type::LIST);
2954 148 : const auto listArray = static_cast<const arrow::ListArray *>(array);
2955 148 : CPLAssert(listArray->values()->type_id() ==
2956 : arrow::Type::FIXED_SIZE_LIST);
2957 148 : GET_PTR_FROM_VALUES(listOfPointsValues, arrow::FixedSizeListArray,
2958 : listArray->values());
2959 148 : CPLAssert(listOfPointsValues->values()->type_id() ==
2960 : arrow::Type::DOUBLE);
2961 148 : GET_PTR_FROM_VALUES(pointValues, arrow::DoubleArray,
2962 : listOfPointsValues->values());
2963 148 : const auto nPoints = listArray->value_length(nIdxInBatch);
2964 : const auto nPointOffset =
2965 148 : listArray->value_offset(nIdxInBatch) * nDim;
2966 148 : auto poMultiPoint = new OGRMultiPoint();
2967 148 : poGeometry = poMultiPoint;
2968 148 : poGeometry->assignSpatialReference(
2969 148 : poGeomFieldDefn->GetSpatialRef());
2970 434 : for (auto k = decltype(nPoints){0}; k < nPoints; k++)
2971 : {
2972 572 : poMultiPoint->addGeometryDirectly(
2973 286 : CreatePoint(pointValues, nPointOffset + k * nDim));
2974 : }
2975 148 : if (poGeometry->IsEmpty())
2976 : {
2977 34 : poGeometry->set3D(bHasZ);
2978 34 : poGeometry->setMeasured(bHasM);
2979 : }
2980 148 : break;
2981 : }
2982 :
2983 144 : case OGRArrowGeomEncoding::GEOARROW_FSL_MULTILINESTRING:
2984 : {
2985 144 : CPLAssert(array->type_id() == arrow::Type::LIST);
2986 144 : const auto listOfStringsArray =
2987 : static_cast<const arrow::ListArray *>(array);
2988 144 : CPLAssert(listOfStringsArray->values()->type_id() ==
2989 : arrow::Type::LIST);
2990 144 : GET_PTR_FROM_VALUES(listOfStringsValues, arrow::ListArray,
2991 : listOfStringsArray->values());
2992 144 : CPLAssert(listOfStringsValues->values()->type_id() ==
2993 : arrow::Type::FIXED_SIZE_LIST);
2994 144 : GET_PTR_FROM_VALUES(listOfPointsValues, arrow::FixedSizeListArray,
2995 : listOfStringsValues->values());
2996 144 : CPLAssert(listOfPointsValues->values()->type_id() ==
2997 : arrow::Type::DOUBLE);
2998 144 : GET_PTR_FROM_VALUES(pointValues, arrow::DoubleArray,
2999 : listOfPointsValues->values());
3000 144 : const auto setPointsFun = GetSetPointsOfLine(bHasZ, bHasM);
3001 144 : const auto nStrings = listOfStringsArray->value_length(nIdxInBatch);
3002 : const auto nRingOffset =
3003 144 : listOfStringsArray->value_offset(nIdxInBatch);
3004 144 : auto poMLS = new OGRMultiLineString();
3005 144 : poGeometry = poMLS;
3006 144 : poGeometry->assignSpatialReference(
3007 144 : poGeomFieldDefn->GetSpatialRef());
3008 316 : for (auto k = decltype(nStrings){0}; k < nStrings; k++)
3009 : {
3010 : const auto nPoints =
3011 172 : listOfStringsValues->value_length(nRingOffset + k);
3012 : const auto nPointOffset =
3013 172 : listOfStringsValues->value_offset(nRingOffset + k) * nDim;
3014 172 : auto poLS = new OGRLineString();
3015 172 : if (nPoints)
3016 : {
3017 172 : setPointsFun(poLS, pointValues, nPointOffset, nPoints);
3018 : }
3019 172 : poMLS->addGeometryDirectly(poLS);
3020 : }
3021 144 : if (poGeometry->IsEmpty())
3022 : {
3023 36 : poGeometry->set3D(bHasZ);
3024 36 : poGeometry->setMeasured(bHasM);
3025 : }
3026 144 : break;
3027 : }
3028 :
3029 160 : case OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOLYGON:
3030 : {
3031 160 : CPLAssert(array->type_id() == arrow::Type::LIST);
3032 160 : const auto listOfPartsArray =
3033 : static_cast<const arrow::ListArray *>(array);
3034 160 : CPLAssert(listOfPartsArray->values()->type_id() ==
3035 : arrow::Type::LIST);
3036 160 : GET_PTR_FROM_VALUES(listOfPartsValues, arrow::ListArray,
3037 : listOfPartsArray->values());
3038 160 : CPLAssert(listOfPartsValues->values()->type_id() ==
3039 : arrow::Type::LIST);
3040 160 : GET_PTR_FROM_VALUES(listOfRingsValues, arrow::ListArray,
3041 : listOfPartsValues->values());
3042 160 : CPLAssert(listOfRingsValues->values()->type_id() ==
3043 : arrow::Type::FIXED_SIZE_LIST);
3044 160 : GET_PTR_FROM_VALUES(listOfPointsValues, arrow::FixedSizeListArray,
3045 : listOfRingsValues->values());
3046 160 : CPLAssert(listOfPointsValues->values()->type_id() ==
3047 : arrow::Type::DOUBLE);
3048 160 : GET_PTR_FROM_VALUES(pointValues, arrow::DoubleArray,
3049 : listOfPointsValues->values());
3050 160 : auto poMP = new OGRMultiPolygon();
3051 160 : poGeometry = poMP;
3052 160 : poGeometry->assignSpatialReference(
3053 160 : poGeomFieldDefn->GetSpatialRef());
3054 160 : const auto setPointsFun = GetSetPointsOfLine(bHasZ, bHasM);
3055 160 : const auto nParts = listOfPartsArray->value_length(nIdxInBatch);
3056 : const auto nPartOffset =
3057 160 : listOfPartsArray->value_offset(nIdxInBatch);
3058 356 : for (auto j = decltype(nParts){0}; j < nParts; j++)
3059 : {
3060 : const auto nRings =
3061 196 : listOfPartsValues->value_length(nPartOffset + j);
3062 : const auto nRingOffset =
3063 196 : listOfPartsValues->value_offset(nPartOffset + j);
3064 196 : auto poPoly = new OGRPolygon();
3065 434 : for (auto k = decltype(nRings){0}; k < nRings; k++)
3066 : {
3067 : const auto nPoints =
3068 238 : listOfRingsValues->value_length(nRingOffset + k);
3069 : const auto nPointOffset =
3070 238 : listOfRingsValues->value_offset(nRingOffset + k) * nDim;
3071 238 : auto poRing = new OGRLinearRing();
3072 238 : if (nPoints)
3073 : {
3074 238 : setPointsFun(poRing, pointValues, nPointOffset,
3075 : nPoints);
3076 : }
3077 238 : poPoly->addRingDirectly(poRing);
3078 : }
3079 196 : poMP->addGeometryDirectly(poPoly);
3080 : }
3081 160 : if (poGeometry->IsEmpty())
3082 : {
3083 36 : poGeometry->set3D(bHasZ);
3084 36 : poGeometry->setMeasured(bHasM);
3085 : }
3086 160 : break;
3087 : }
3088 :
3089 311 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_POINT:
3090 : {
3091 311 : CPLAssert(array->type_id() == arrow::Type::STRUCT);
3092 311 : const auto structArray =
3093 : static_cast<const arrow::StructArray *>(array);
3094 311 : if (!structArray->IsNull(nIdxInBatch))
3095 : {
3096 311 : poGeometry = CreateStructPoint(structArray, nIdxInBatch);
3097 311 : poGeometry->assignSpatialReference(
3098 311 : poGeomFieldDefn->GetSpatialRef());
3099 : }
3100 311 : break;
3101 : }
3102 :
3103 200 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_LINESTRING:
3104 : {
3105 200 : CPLAssert(array->type_id() == arrow::Type::LIST);
3106 200 : const auto listArray = static_cast<const arrow::ListArray *>(array);
3107 200 : CPLAssert(listArray->values()->type_id() == arrow::Type::STRUCT);
3108 200 : GET_PTR_FROM_VALUES(pointValues, arrow::StructArray,
3109 : listArray->values());
3110 200 : const auto nPoints = listArray->value_length(nIdxInBatch);
3111 200 : const auto nPointOffset = listArray->value_offset(nIdxInBatch);
3112 200 : auto poLineString = new OGRLineString();
3113 200 : poGeometry = poLineString;
3114 200 : poGeometry->assignSpatialReference(
3115 200 : poGeomFieldDefn->GetSpatialRef());
3116 200 : if (nPoints)
3117 : {
3118 164 : GetSetPointsOfLineStruct(bHasZ, bHasM)(
3119 : poLineString, pointValues, nPointOffset, nPoints);
3120 : }
3121 : else
3122 : {
3123 36 : poGeometry->set3D(bHasZ);
3124 36 : poGeometry->setMeasured(bHasM);
3125 : }
3126 200 : break;
3127 : }
3128 :
3129 316 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_POLYGON:
3130 : {
3131 316 : CPLAssert(array->type_id() == arrow::Type::LIST);
3132 316 : const auto listOfRingsArray =
3133 : static_cast<const arrow::ListArray *>(array);
3134 316 : CPLAssert(listOfRingsArray->values()->type_id() ==
3135 : arrow::Type::LIST);
3136 316 : GET_PTR_FROM_VALUES(listOfRingsValues, arrow::ListArray,
3137 : listOfRingsArray->values());
3138 316 : CPLAssert(listOfRingsValues->values()->type_id() ==
3139 : arrow::Type::STRUCT);
3140 316 : GET_PTR_FROM_VALUES(pointValues, arrow::StructArray,
3141 : listOfRingsValues->values());
3142 316 : const auto setPointsFun = GetSetPointsOfLineStruct(bHasZ, bHasM);
3143 316 : const auto nRings = listOfRingsArray->value_length(nIdxInBatch);
3144 : const auto nRingOffset =
3145 316 : listOfRingsArray->value_offset(nIdxInBatch);
3146 316 : auto poPoly = new OGRPolygon();
3147 316 : poGeometry = poPoly;
3148 316 : poGeometry->assignSpatialReference(
3149 316 : poGeomFieldDefn->GetSpatialRef());
3150 648 : for (auto k = decltype(nRings){0}; k < nRings; k++)
3151 : {
3152 : const auto nPoints =
3153 332 : listOfRingsValues->value_length(nRingOffset + k);
3154 : const auto nPointOffset =
3155 332 : listOfRingsValues->value_offset(nRingOffset + k);
3156 332 : auto poRing = new OGRLinearRing();
3157 332 : if (nPoints)
3158 : {
3159 332 : setPointsFun(poRing, pointValues, nPointOffset, nPoints);
3160 : }
3161 332 : poPoly->addRingDirectly(poRing);
3162 : }
3163 316 : if (poGeometry->IsEmpty())
3164 : {
3165 52 : poGeometry->set3D(bHasZ);
3166 52 : poGeometry->setMeasured(bHasM);
3167 : }
3168 316 : break;
3169 : }
3170 :
3171 240 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOINT:
3172 : {
3173 240 : CPLAssert(array->type_id() == arrow::Type::LIST);
3174 240 : const auto listArray = static_cast<const arrow::ListArray *>(array);
3175 240 : CPLAssert(listArray->values()->type_id() == arrow::Type::STRUCT);
3176 240 : GET_PTR_FROM_VALUES(pointValues, arrow::StructArray,
3177 : listArray->values());
3178 240 : const auto nPoints = listArray->value_length(nIdxInBatch);
3179 240 : const auto nPointOffset = listArray->value_offset(nIdxInBatch);
3180 240 : auto poMultiPoint = new OGRMultiPoint();
3181 240 : poGeometry = poMultiPoint;
3182 240 : poGeometry->assignSpatialReference(
3183 240 : poGeomFieldDefn->GetSpatialRef());
3184 596 : for (auto k = decltype(nPoints){0}; k < nPoints; k++)
3185 : {
3186 712 : poMultiPoint->addGeometryDirectly(
3187 356 : CreateStructPoint(pointValues, nPointOffset + k));
3188 : }
3189 240 : if (poGeometry->IsEmpty())
3190 : {
3191 36 : poGeometry->set3D(bHasZ);
3192 36 : poGeometry->setMeasured(bHasM);
3193 : }
3194 240 : break;
3195 : }
3196 :
3197 284 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTILINESTRING:
3198 : {
3199 284 : CPLAssert(array->type_id() == arrow::Type::LIST);
3200 284 : const auto listOfStringsArray =
3201 : static_cast<const arrow::ListArray *>(array);
3202 284 : CPLAssert(listOfStringsArray->values()->type_id() ==
3203 : arrow::Type::LIST);
3204 284 : GET_PTR_FROM_VALUES(listOfStringsValues, arrow::ListArray,
3205 : listOfStringsArray->values());
3206 284 : CPLAssert(listOfStringsValues->values()->type_id() ==
3207 : arrow::Type::STRUCT);
3208 284 : GET_PTR_FROM_VALUES(pointValues, arrow::StructArray,
3209 : listOfStringsValues->values());
3210 284 : const auto setPointsFun = GetSetPointsOfLineStruct(bHasZ, bHasM);
3211 284 : const auto nStrings = listOfStringsArray->value_length(nIdxInBatch);
3212 : const auto nRingOffset =
3213 284 : listOfStringsArray->value_offset(nIdxInBatch);
3214 284 : auto poMLS = new OGRMultiLineString();
3215 284 : poGeometry = poMLS;
3216 284 : poGeometry->assignSpatialReference(
3217 284 : poGeomFieldDefn->GetSpatialRef());
3218 696 : for (auto k = decltype(nStrings){0}; k < nStrings; k++)
3219 : {
3220 : const auto nPoints =
3221 412 : listOfStringsValues->value_length(nRingOffset + k);
3222 : const auto nPointOffset =
3223 412 : listOfStringsValues->value_offset(nRingOffset + k);
3224 412 : auto poLS = new OGRLineString();
3225 412 : if (nPoints)
3226 : {
3227 412 : setPointsFun(poLS, pointValues, nPointOffset, nPoints);
3228 : }
3229 412 : poMLS->addGeometryDirectly(poLS);
3230 : }
3231 284 : if (poGeometry->IsEmpty())
3232 : {
3233 36 : poGeometry->set3D(bHasZ);
3234 36 : poGeometry->setMeasured(bHasM);
3235 : }
3236 284 : break;
3237 : }
3238 :
3239 336 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOLYGON:
3240 : {
3241 336 : CPLAssert(array->type_id() == arrow::Type::LIST);
3242 336 : const auto listOfPartsArray =
3243 : static_cast<const arrow::ListArray *>(array);
3244 336 : CPLAssert(listOfPartsArray->values()->type_id() ==
3245 : arrow::Type::LIST);
3246 336 : GET_PTR_FROM_VALUES(listOfPartsValues, arrow::ListArray,
3247 : listOfPartsArray->values());
3248 336 : CPLAssert(listOfPartsValues->values()->type_id() ==
3249 : arrow::Type::LIST);
3250 336 : GET_PTR_FROM_VALUES(listOfRingsValues, arrow::ListArray,
3251 : listOfPartsValues->values());
3252 336 : CPLAssert(listOfRingsValues->values()->type_id() ==
3253 : arrow::Type::STRUCT);
3254 336 : GET_PTR_FROM_VALUES(pointValues, arrow::StructArray,
3255 : listOfRingsValues->values());
3256 336 : auto poMP = new OGRMultiPolygon();
3257 336 : poGeometry = poMP;
3258 336 : poGeometry->assignSpatialReference(
3259 336 : poGeomFieldDefn->GetSpatialRef());
3260 336 : const auto setPointsFun = GetSetPointsOfLineStruct(bHasZ, bHasM);
3261 336 : const auto nParts = listOfPartsArray->value_length(nIdxInBatch);
3262 : const auto nPartOffset =
3263 336 : listOfPartsArray->value_offset(nIdxInBatch);
3264 756 : for (auto j = decltype(nParts){0}; j < nParts; j++)
3265 : {
3266 : const auto nRings =
3267 420 : listOfPartsValues->value_length(nPartOffset + j);
3268 : const auto nRingOffset =
3269 420 : listOfPartsValues->value_offset(nPartOffset + j);
3270 420 : auto poPoly = new OGRPolygon();
3271 940 : for (auto k = decltype(nRings){0}; k < nRings; k++)
3272 : {
3273 : const auto nPoints =
3274 520 : listOfRingsValues->value_length(nRingOffset + k);
3275 : const auto nPointOffset =
3276 520 : listOfRingsValues->value_offset(nRingOffset + k);
3277 520 : auto poRing = new OGRLinearRing();
3278 520 : if (nPoints)
3279 : {
3280 520 : setPointsFun(poRing, pointValues, nPointOffset,
3281 : nPoints);
3282 : }
3283 520 : poPoly->addRingDirectly(poRing);
3284 : }
3285 420 : poMP->addGeometryDirectly(poPoly);
3286 : }
3287 336 : if (poGeometry->IsEmpty())
3288 : {
3289 52 : poGeometry->set3D(bHasZ);
3290 52 : poGeometry->setMeasured(bHasM);
3291 : }
3292 336 : break;
3293 : }
3294 : }
3295 7188 : return poGeometry;
3296 : }
3297 :
3298 : /************************************************************************/
3299 : /* ResetReading() */
3300 : /************************************************************************/
3301 :
3302 7260 : inline void OGRArrowLayer::ResetReading()
3303 : {
3304 7260 : m_bEOF = false;
3305 7260 : m_nFeatureIdx = 0;
3306 7260 : m_nIdxInBatch = 0;
3307 7260 : m_poReadFeatureTmpArray.reset();
3308 7260 : if (m_iRecordBatch != 0)
3309 : {
3310 6377 : m_iRecordBatch = -1;
3311 6377 : m_poBatch.reset();
3312 6377 : m_poBatchColumns.clear();
3313 : }
3314 7260 : }
3315 :
3316 : /***********************************************************************/
3317 : /* GetColumnSubNode() */
3318 : /***********************************************************************/
3319 :
3320 : /* static*/
3321 : inline const swq_expr_node *
3322 284 : OGRArrowLayer::GetColumnSubNode(const swq_expr_node *poNode)
3323 : {
3324 284 : if (poNode->eNodeType == SNT_OPERATION && poNode->nSubExprCount == 2)
3325 : {
3326 284 : if (poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN)
3327 202 : return poNode->papoSubExpr[0];
3328 82 : if (poNode->papoSubExpr[1]->eNodeType == SNT_COLUMN)
3329 12 : return poNode->papoSubExpr[1];
3330 : }
3331 70 : return nullptr;
3332 : }
3333 :
3334 : /***********************************************************************/
3335 : /* GetConstantSubNode() */
3336 : /***********************************************************************/
3337 :
3338 : /* static */
3339 : inline const swq_expr_node *
3340 284 : OGRArrowLayer::GetConstantSubNode(const swq_expr_node *poNode)
3341 : {
3342 284 : if (poNode->eNodeType == SNT_OPERATION && poNode->nSubExprCount == 2)
3343 : {
3344 284 : if (poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT)
3345 270 : return poNode->papoSubExpr[1];
3346 14 : if (poNode->papoSubExpr[0]->eNodeType == SNT_CONSTANT)
3347 12 : return poNode->papoSubExpr[0];
3348 : }
3349 2 : return nullptr;
3350 : }
3351 :
3352 : /***********************************************************************/
3353 : /* IsComparisonOp() */
3354 : /***********************************************************************/
3355 :
3356 : /* static*/
3357 428 : inline bool OGRArrowLayer::IsComparisonOp(int op)
3358 : {
3359 166 : return (op == SWQ_EQ || op == SWQ_NE || op == SWQ_LT || op == SWQ_LE ||
3360 594 : op == SWQ_GT || op == SWQ_GE);
3361 : }
3362 :
3363 : /***********************************************************************/
3364 : /* FillTargetValueFromSrcExpr() */
3365 : /***********************************************************************/
3366 :
3367 212 : static bool FillTargetValueFromSrcExpr(const OGRFieldDefn *poFieldDefn,
3368 : OGRArrowLayer::Constraint *psConstraint,
3369 : const swq_expr_node *poSrcValue)
3370 : {
3371 212 : switch (poFieldDefn->GetType())
3372 : {
3373 111 : case OFTInteger:
3374 111 : psConstraint->eType = OGRArrowLayer::Constraint::Type::Integer;
3375 111 : if (poSrcValue->field_type == SWQ_FLOAT)
3376 2 : psConstraint->sValue.Integer =
3377 2 : static_cast<int>(poSrcValue->float_value);
3378 : else
3379 109 : psConstraint->sValue.Integer =
3380 109 : static_cast<int>(poSrcValue->int_value);
3381 : psConstraint->osValue =
3382 111 : std::to_string(psConstraint->sValue.Integer);
3383 111 : break;
3384 :
3385 29 : case OFTInteger64:
3386 29 : psConstraint->eType = OGRArrowLayer::Constraint::Type::Integer64;
3387 29 : if (poSrcValue->field_type == SWQ_FLOAT)
3388 0 : psConstraint->sValue.Integer64 =
3389 0 : static_cast<GIntBig>(poSrcValue->float_value);
3390 : else
3391 29 : psConstraint->sValue.Integer64 = poSrcValue->int_value;
3392 : psConstraint->osValue =
3393 29 : std::to_string(psConstraint->sValue.Integer64);
3394 29 : break;
3395 :
3396 25 : case OFTReal:
3397 25 : psConstraint->eType = OGRArrowLayer::Constraint::Type::Real;
3398 25 : psConstraint->sValue.Real = poSrcValue->float_value;
3399 25 : psConstraint->osValue = std::to_string(psConstraint->sValue.Real);
3400 25 : break;
3401 :
3402 22 : case OFTString:
3403 22 : psConstraint->eType = OGRArrowLayer::Constraint::Type::String;
3404 22 : psConstraint->sValue.String = poSrcValue->string_value;
3405 22 : psConstraint->osValue = psConstraint->sValue.String;
3406 22 : break;
3407 : #ifdef not_yet_handled
3408 : case OFTDate:
3409 : case OFTTime:
3410 : case OFTDateTime:
3411 : if (poSrcValue->field_type == SWQ_TIMESTAMP ||
3412 : poSrcValue->field_type == SWQ_DATE ||
3413 : poSrcValue->field_type == SWQ_TIME)
3414 : {
3415 : int nYear = 0, nMonth = 0, nDay = 0, nHour = 0, nMin = 0,
3416 : nSec = 0;
3417 : if (sscanf(poSrcValue->string_value,
3418 : "%04d/%02d/%02d %02d:%02d:%02d", &nYear, &nMonth,
3419 : &nDay, &nHour, &nMin, &nSec) == 6 ||
3420 : sscanf(poSrcValue->string_value, "%04d/%02d/%02d", &nYear,
3421 : &nMonth, &nDay) == 3 ||
3422 : sscanf(poSrcValue->string_value, "%02d:%02d:%02d", &nHour,
3423 : &nMin, &nSec) == 3)
3424 : {
3425 : psConstraint->eType =
3426 : OGRArrowLayer::Constraint::Type::DateTime;
3427 : psConstraint->sValue.Date.Year = (GInt16)nYear;
3428 : psConstraint->sValue.Date.Month = (GByte)nMonth;
3429 : psConstraint->sValue.Date.Day = (GByte)nDay;
3430 : psConstraint->sValue.Date.Hour = (GByte)nHour;
3431 : psConstraint->sValue.Date.Minute = (GByte)nMin;
3432 : psConstraint->sValue.Date.Second = (GByte)nSec;
3433 : psConstraint->sValue.Date.TZFlag = 0;
3434 : psConstraint->sValue.Date.Reserved = 0;
3435 : }
3436 : else
3437 : return false;
3438 : }
3439 : else
3440 : return false;
3441 : break;
3442 : #endif
3443 25 : default:
3444 25 : return false;
3445 : }
3446 187 : return true;
3447 : }
3448 :
3449 : /***********************************************************************/
3450 : /* ComputeConstraintsArrayIdx() */
3451 : /***********************************************************************/
3452 :
3453 542 : inline void OGRArrowLayer::ComputeConstraintsArrayIdx()
3454 : {
3455 743 : for (auto &constraint : m_asAttributeFilterConstraints)
3456 : {
3457 202 : if (m_bIgnoredFields)
3458 : {
3459 25 : if (constraint.iField == m_poFeatureDefn->GetFieldCount() + SPF_FID)
3460 : {
3461 1 : constraint.iArrayIdx = m_nRequestedFIDColumn;
3462 1 : if (constraint.iArrayIdx < 0 && m_osFIDColumn.empty())
3463 1 : return;
3464 : }
3465 : else
3466 : {
3467 24 : constraint.iArrayIdx =
3468 24 : m_anMapFieldIndexToArrayIndex[constraint.iField];
3469 : }
3470 24 : if (constraint.iArrayIdx < 0)
3471 : {
3472 1 : CPLError(CE_Failure, CPLE_AppDefined,
3473 : "Constraint on field %s cannot be applied due to "
3474 : "it being ignored",
3475 1 : constraint.iField ==
3476 1 : m_poFeatureDefn->GetFieldCount() + SPF_FID
3477 0 : ? m_osFIDColumn.c_str()
3478 1 : : m_poFeatureDefn->GetFieldDefn(constraint.iField)
3479 1 : ->GetNameRef());
3480 : }
3481 : }
3482 : else
3483 : {
3484 177 : if (constraint.iField == m_poFeatureDefn->GetFieldCount() + SPF_FID)
3485 : {
3486 8 : constraint.iArrayIdx = m_iFIDArrowColumn;
3487 8 : if (constraint.iArrayIdx < 0 && !m_osFIDColumn.empty())
3488 : {
3489 0 : CPLDebug(GetDriverUCName().c_str(),
3490 : "Constraint on field %s cannot be applied",
3491 : m_osFIDColumn.c_str());
3492 : }
3493 : }
3494 : else
3495 : {
3496 169 : constraint.iArrayIdx =
3497 169 : m_anMapFieldIndexToArrowColumn[constraint.iField][0];
3498 : }
3499 : }
3500 : }
3501 : }
3502 :
3503 : /***********************************************************************/
3504 : /* ExploreExprNode() */
3505 : /***********************************************************************/
3506 :
3507 347 : inline void OGRArrowLayer::ExploreExprNode(const swq_expr_node *poNode)
3508 : {
3509 199 : const auto AddConstraint = [this](Constraint &constraint)
3510 199 : { m_asAttributeFilterConstraints.emplace_back(constraint); };
3511 :
3512 347 : if (poNode->eNodeType == SNT_OPERATION && poNode->nOperation == SWQ_AND &&
3513 18 : poNode->nSubExprCount == 2)
3514 : {
3515 18 : ExploreExprNode(poNode->papoSubExpr[0]);
3516 18 : ExploreExprNode(poNode->papoSubExpr[1]);
3517 : }
3518 :
3519 329 : else if (poNode->eNodeType == SNT_OPERATION &&
3520 658 : IsComparisonOp(poNode->nOperation) && poNode->nSubExprCount == 2)
3521 : {
3522 284 : const swq_expr_node *poColumn = GetColumnSubNode(poNode);
3523 284 : const swq_expr_node *poValue = GetConstantSubNode(poNode);
3524 496 : if (poColumn != nullptr && poValue != nullptr &&
3525 212 : (poColumn->field_index < m_poFeatureDefn->GetFieldCount() ||
3526 8 : poColumn->field_index ==
3527 8 : m_poFeatureDefn->GetFieldCount() + SPF_FID))
3528 : {
3529 : const OGRFieldDefn oDummyFIDFieldDefn(m_osFIDColumn.c_str(),
3530 424 : OFTInteger64);
3531 : const OGRFieldDefn *poFieldDefn =
3532 212 : (poColumn->field_index ==
3533 212 : m_poFeatureDefn->GetFieldCount() + SPF_FID)
3534 212 : ? &oDummyFIDFieldDefn
3535 204 : : m_poFeatureDefn->GetFieldDefn(poColumn->field_index);
3536 :
3537 424 : Constraint constraint;
3538 212 : constraint.iField = poColumn->field_index;
3539 212 : constraint.nOperation = poNode->nOperation;
3540 :
3541 212 : if (FillTargetValueFromSrcExpr(poFieldDefn, &constraint, poValue))
3542 : {
3543 187 : if (poColumn == poNode->papoSubExpr[0])
3544 : {
3545 : // nothing to do
3546 : }
3547 : else
3548 : {
3549 : /* If "constant op column", then we must reverse */
3550 : /* the operator for LE, LT, GE, GT */
3551 12 : switch (poNode->nOperation)
3552 : {
3553 2 : case SWQ_LE:
3554 2 : constraint.nOperation = SWQ_GE;
3555 2 : break;
3556 2 : case SWQ_LT:
3557 2 : constraint.nOperation = SWQ_GT;
3558 2 : break;
3559 2 : case SWQ_NE: /* do nothing */;
3560 2 : break;
3561 2 : case SWQ_EQ: /* do nothing */;
3562 2 : break;
3563 2 : case SWQ_GE:
3564 2 : constraint.nOperation = SWQ_LE;
3565 2 : break;
3566 2 : case SWQ_GT:
3567 2 : constraint.nOperation = SWQ_LT;
3568 2 : break;
3569 0 : default:
3570 0 : CPLAssert(false);
3571 : break;
3572 : }
3573 : }
3574 :
3575 187 : AddConstraint(constraint);
3576 : }
3577 : }
3578 : }
3579 :
3580 45 : else if (poNode->eNodeType == SNT_OPERATION &&
3581 45 : poNode->nOperation == SWQ_ISNULL && poNode->nSubExprCount == 1)
3582 : {
3583 4 : const swq_expr_node *poColumn = poNode->papoSubExpr[0];
3584 8 : if (poColumn->eNodeType == SNT_COLUMN &&
3585 4 : poColumn->field_index < m_poFeatureDefn->GetFieldCount())
3586 : {
3587 8 : Constraint constraint;
3588 4 : constraint.iField = poColumn->field_index;
3589 4 : constraint.nOperation = poNode->nOperation;
3590 4 : AddConstraint(constraint);
3591 4 : }
3592 : }
3593 :
3594 41 : else if (poNode->eNodeType == SNT_OPERATION &&
3595 41 : poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1 &&
3596 20 : poNode->papoSubExpr[0]->eNodeType == SNT_OPERATION &&
3597 20 : poNode->papoSubExpr[0]->nOperation == SWQ_ISNULL &&
3598 16 : poNode->papoSubExpr[0]->nSubExprCount == 1)
3599 : {
3600 16 : const swq_expr_node *poColumn = poNode->papoSubExpr[0]->papoSubExpr[0];
3601 32 : if (poColumn->eNodeType == SNT_COLUMN &&
3602 16 : poColumn->field_index < m_poFeatureDefn->GetFieldCount())
3603 : {
3604 16 : Constraint constraint;
3605 8 : constraint.iField = poColumn->field_index;
3606 8 : constraint.nOperation = SWQ_ISNOTNULL;
3607 8 : AddConstraint(constraint);
3608 : }
3609 : }
3610 347 : }
3611 :
3612 : /***********************************************************************/
3613 : /* SetAttributeFilter() */
3614 : /***********************************************************************/
3615 :
3616 748 : inline OGRErr OGRArrowLayer::SetAttributeFilter(const char *pszFilter)
3617 : {
3618 748 : m_asAttributeFilterConstraints.clear();
3619 :
3620 : // When changing filters, we need to invalidate cached batches, as
3621 : // PostFilterArrowArray() has potentially modified array contents
3622 748 : if (m_poAttrQuery)
3623 110 : InvalidateCachedBatches();
3624 :
3625 748 : OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter);
3626 748 : if (eErr != OGRERR_NONE)
3627 0 : return eErr;
3628 :
3629 748 : if (m_poAttrQuery != nullptr)
3630 : {
3631 461 : if (m_nUseOptimizedAttributeFilter < 0)
3632 : {
3633 364 : m_nUseOptimizedAttributeFilter = CPLTestBool(CPLGetConfigOption(
3634 728 : ("OGR_" + GetDriverUCName() + "_OPTIMIZED_ATTRIBUTE_FILTER")
3635 : .c_str(),
3636 : "YES"));
3637 : }
3638 461 : if (m_nUseOptimizedAttributeFilter)
3639 : {
3640 : swq_expr_node *poNode =
3641 311 : static_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
3642 311 : poNode->ReplaceBetweenByGEAndLERecurse();
3643 311 : ExploreExprNode(poNode);
3644 311 : ComputeConstraintsArrayIdx();
3645 : }
3646 : }
3647 :
3648 748 : return OGRERR_NONE;
3649 : }
3650 :
3651 : /************************************************************************/
3652 : /* ConstraintEvaluator() */
3653 : /************************************************************************/
3654 :
3655 : namespace
3656 : {
3657 : template <class T, class U> struct CompareGeneric
3658 : {
3659 289 : static inline bool get(int op, const T &val1, const U &val2)
3660 : {
3661 289 : switch (op)
3662 : {
3663 8 : case SWQ_LE:
3664 8 : return val1 <= val2;
3665 8 : case SWQ_LT:
3666 8 : return val1 < val2;
3667 90 : case SWQ_NE:
3668 90 : return val1 != val2;
3669 163 : case SWQ_EQ:
3670 163 : return val1 == val2;
3671 12 : case SWQ_GE:
3672 12 : return val1 >= val2;
3673 8 : case SWQ_GT:
3674 8 : return val1 > val2;
3675 : break;
3676 0 : default:
3677 0 : CPLAssert(false);
3678 : }
3679 : return true;
3680 : }
3681 : };
3682 :
3683 : template <class T, class U> struct Compare
3684 : {
3685 : };
3686 :
3687 : template <class T> struct Compare<T, T> : public CompareGeneric<T, T>
3688 : {
3689 : };
3690 :
3691 : template <> struct Compare<int, GIntBig> : public CompareGeneric<int, GIntBig>
3692 : {
3693 : };
3694 :
3695 : template <> struct Compare<double, GIntBig>
3696 : {
3697 0 : static inline bool get(int op, double val1, GIntBig val2)
3698 : {
3699 0 : return CompareGeneric<double, double>::get(op, val1,
3700 0 : static_cast<double>(val2));
3701 : }
3702 : };
3703 :
3704 : template <> struct Compare<GIntBig, int> : public CompareGeneric<GIntBig, int>
3705 : {
3706 : };
3707 :
3708 : template <> struct Compare<double, int> : public CompareGeneric<double, int>
3709 : {
3710 : };
3711 :
3712 : template <class T>
3713 289 : static bool ConstraintEvaluator(const OGRArrowLayer::Constraint &constraint,
3714 : const T value)
3715 : {
3716 289 : bool b = false;
3717 289 : switch (constraint.eType)
3718 : {
3719 204 : case OGRArrowLayer::Constraint::Type::Integer:
3720 408 : b = Compare<T, int>::get(constraint.nOperation, value,
3721 204 : constraint.sValue.Integer);
3722 204 : break;
3723 43 : case OGRArrowLayer::Constraint::Type::Integer64:
3724 86 : b = Compare<T, GIntBig>::get(constraint.nOperation, value,
3725 43 : constraint.sValue.Integer64);
3726 43 : break;
3727 42 : case OGRArrowLayer::Constraint::Type::Real:
3728 84 : b = Compare<double, double>::get(constraint.nOperation,
3729 0 : static_cast<double>(value),
3730 42 : constraint.sValue.Real);
3731 42 : break;
3732 0 : case OGRArrowLayer::Constraint::Type::String:
3733 0 : b = Compare<std::string, std::string>::get(constraint.nOperation,
3734 : std::to_string(value),
3735 0 : constraint.osValue);
3736 0 : break;
3737 : }
3738 289 : return b;
3739 : }
3740 :
3741 45 : inline bool CompareStr(int op, const std::string_view &val1,
3742 : const std::string &val2)
3743 : {
3744 45 : if (op == SWQ_EQ)
3745 : {
3746 14 : return val1 == val2;
3747 : }
3748 31 : const int cmpRes = val2.compare(val1);
3749 31 : switch (op)
3750 : {
3751 4 : case SWQ_LE:
3752 4 : return cmpRes >= 0;
3753 4 : case SWQ_LT:
3754 4 : return cmpRes > 0;
3755 23 : case SWQ_NE:
3756 23 : return cmpRes != 0;
3757 : // case SWQ_EQ: return cmpRes == 0;
3758 0 : case SWQ_GE:
3759 0 : return cmpRes <= 0;
3760 0 : case SWQ_GT:
3761 0 : return cmpRes < 0;
3762 : break;
3763 0 : default:
3764 0 : CPLAssert(false);
3765 : }
3766 : return true;
3767 : }
3768 :
3769 45 : inline bool ConstraintEvaluator(const OGRArrowLayer::Constraint &constraint,
3770 : const std::string_view &value)
3771 : {
3772 45 : return CompareStr(constraint.nOperation, value, constraint.osValue);
3773 : }
3774 :
3775 : } // namespace
3776 :
3777 : /************************************************************************/
3778 : /* SkipToNextFeatureDueToAttributeFilter() */
3779 : /************************************************************************/
3780 :
3781 441 : inline bool OGRArrowLayer::SkipToNextFeatureDueToAttributeFilter() const
3782 : {
3783 679 : for (const auto &constraint : m_asAttributeFilterConstraints)
3784 : {
3785 441 : if (constraint.iArrayIdx < 0)
3786 : {
3787 42 : if (constraint.iField ==
3788 34 : m_poFeatureDefn->GetFieldCount() + SPF_FID &&
3789 16 : m_osFIDColumn.empty())
3790 : {
3791 16 : if (!ConstraintEvaluator(constraint,
3792 16 : static_cast<GIntBig>(m_nFeatureIdx)))
3793 : {
3794 203 : return true;
3795 : }
3796 4 : continue;
3797 : }
3798 : else
3799 : {
3800 : // can happen if ignoring a field that is needed by the
3801 : // attribute filter. ComputeConstraintsArrayIdx() will have
3802 : // warned about that
3803 2 : continue;
3804 : }
3805 : }
3806 :
3807 : const arrow::Array *array =
3808 423 : m_poBatchColumns[constraint.iArrayIdx].get();
3809 :
3810 423 : const bool bIsNull = array->IsNull(m_nIdxInBatch);
3811 423 : if (constraint.nOperation == SWQ_ISNULL)
3812 : {
3813 7 : if (bIsNull)
3814 : {
3815 3 : continue;
3816 : }
3817 4 : return true;
3818 : }
3819 416 : else if (constraint.nOperation == SWQ_ISNOTNULL)
3820 : {
3821 22 : if (!bIsNull)
3822 : {
3823 18 : continue;
3824 : }
3825 4 : return true;
3826 : }
3827 394 : else if (bIsNull)
3828 : {
3829 65 : return true;
3830 : }
3831 :
3832 329 : switch (array->type_id())
3833 : {
3834 0 : case arrow::Type::NA:
3835 0 : break;
3836 :
3837 92 : case arrow::Type::BOOL:
3838 : {
3839 92 : const auto castArray =
3840 : static_cast<const arrow::BooleanArray *>(array);
3841 92 : if (!ConstraintEvaluator(
3842 : constraint,
3843 92 : static_cast<int>(castArray->Value(m_nIdxInBatch))))
3844 : {
3845 42 : return true;
3846 : }
3847 50 : break;
3848 : }
3849 7 : case arrow::Type::UINT8:
3850 : {
3851 7 : const auto castArray =
3852 : static_cast<const arrow::UInt8Array *>(array);
3853 7 : if (!ConstraintEvaluator(
3854 : constraint,
3855 7 : static_cast<int>(castArray->Value(m_nIdxInBatch))))
3856 : {
3857 3 : return true;
3858 : }
3859 4 : break;
3860 : }
3861 30 : case arrow::Type::INT8:
3862 : {
3863 30 : const auto castArray =
3864 : static_cast<const arrow::Int8Array *>(array);
3865 30 : if (!ConstraintEvaluator(
3866 : constraint,
3867 30 : static_cast<int>(castArray->Value(m_nIdxInBatch))))
3868 : {
3869 2 : return true;
3870 : }
3871 28 : break;
3872 : }
3873 4 : case arrow::Type::UINT16:
3874 : {
3875 4 : const auto castArray =
3876 : static_cast<const arrow::UInt16Array *>(array);
3877 4 : if (!ConstraintEvaluator(
3878 : constraint,
3879 4 : static_cast<int>(castArray->Value(m_nIdxInBatch))))
3880 : {
3881 2 : return true;
3882 : }
3883 2 : break;
3884 : }
3885 0 : case arrow::Type::INT16:
3886 : {
3887 0 : const auto castArray =
3888 : static_cast<const arrow::Int16Array *>(array);
3889 0 : if (!ConstraintEvaluator(
3890 : constraint,
3891 0 : static_cast<int>(castArray->Value(m_nIdxInBatch))))
3892 : {
3893 0 : return true;
3894 : }
3895 0 : break;
3896 : }
3897 0 : case arrow::Type::UINT32:
3898 : {
3899 0 : const auto castArray =
3900 : static_cast<const arrow::UInt32Array *>(array);
3901 0 : if (!ConstraintEvaluator(
3902 : constraint,
3903 0 : static_cast<GIntBig>(castArray->Value(m_nIdxInBatch))))
3904 : {
3905 0 : return true;
3906 : }
3907 0 : break;
3908 : }
3909 71 : case arrow::Type::INT32:
3910 : {
3911 71 : const auto castArray =
3912 : static_cast<const arrow::Int32Array *>(array);
3913 71 : if (!ConstraintEvaluator(constraint,
3914 71 : castArray->Value(m_nIdxInBatch)))
3915 : {
3916 28 : return true;
3917 : }
3918 43 : break;
3919 : }
3920 8 : case arrow::Type::UINT64:
3921 : {
3922 8 : const auto castArray =
3923 : static_cast<const arrow::UInt64Array *>(array);
3924 8 : if (!ConstraintEvaluator(
3925 : constraint,
3926 8 : static_cast<double>(castArray->Value(m_nIdxInBatch))))
3927 : {
3928 6 : return true;
3929 : }
3930 2 : break;
3931 : }
3932 27 : case arrow::Type::INT64:
3933 : {
3934 27 : const auto castArray =
3935 : static_cast<const arrow::Int64Array *>(array);
3936 27 : if (!ConstraintEvaluator(
3937 : constraint,
3938 27 : static_cast<GIntBig>(castArray->Value(m_nIdxInBatch))))
3939 : {
3940 10 : return true;
3941 : }
3942 17 : break;
3943 : }
3944 0 : case arrow::Type::HALF_FLOAT:
3945 : {
3946 0 : const auto castArray =
3947 : static_cast<const arrow::HalfFloatArray *>(array);
3948 0 : const uint16_t nFloat16 = castArray->Value(m_nIdxInBatch);
3949 0 : uint32_t nFloat32 = CPLHalfToFloat(nFloat16);
3950 : float f;
3951 0 : memcpy(&f, &nFloat32, sizeof(nFloat32));
3952 0 : if (!ConstraintEvaluator(constraint, static_cast<double>(f)))
3953 : {
3954 0 : return true;
3955 : }
3956 0 : break;
3957 : }
3958 4 : case arrow::Type::FLOAT:
3959 : {
3960 4 : const auto castArray =
3961 : static_cast<const arrow::FloatArray *>(array);
3962 4 : if (!ConstraintEvaluator(
3963 : constraint,
3964 4 : static_cast<double>(castArray->Value(m_nIdxInBatch))))
3965 : {
3966 2 : return true;
3967 : }
3968 2 : break;
3969 : }
3970 22 : case arrow::Type::DOUBLE:
3971 : {
3972 22 : const auto castArray =
3973 : static_cast<const arrow::DoubleArray *>(array);
3974 22 : if (!ConstraintEvaluator(constraint,
3975 22 : castArray->Value(m_nIdxInBatch)))
3976 : {
3977 6 : return true;
3978 : }
3979 16 : break;
3980 : }
3981 45 : case arrow::Type::STRING:
3982 : {
3983 45 : const auto castArray =
3984 : static_cast<const arrow::StringArray *>(array);
3985 45 : int out_length = 0;
3986 : const uint8_t *data =
3987 45 : castArray->GetValue(m_nIdxInBatch, &out_length);
3988 45 : if (!ConstraintEvaluator(
3989 : constraint,
3990 90 : std::string_view(reinterpret_cast<const char *>(data),
3991 : out_length)))
3992 : {
3993 13 : return true;
3994 : }
3995 32 : break;
3996 : }
3997 :
3998 : #if ARROW_VERSION_MAJOR >= 18
3999 0 : case arrow::Type::DECIMAL32:
4000 : {
4001 0 : const auto castArray =
4002 : static_cast<const arrow::Decimal32Array *>(array);
4003 0 : if (!ConstraintEvaluator(
4004 : constraint,
4005 0 : CPLAtof(castArray->FormatValue(m_nIdxInBatch).c_str())))
4006 : {
4007 0 : return true;
4008 : }
4009 0 : break;
4010 : }
4011 :
4012 0 : case arrow::Type::DECIMAL64:
4013 : {
4014 0 : const auto castArray =
4015 : static_cast<const arrow::Decimal64Array *>(array);
4016 0 : if (!ConstraintEvaluator(
4017 : constraint,
4018 0 : CPLAtof(castArray->FormatValue(m_nIdxInBatch).c_str())))
4019 : {
4020 0 : return true;
4021 : }
4022 0 : break;
4023 : }
4024 : #endif
4025 :
4026 4 : case arrow::Type::DECIMAL128:
4027 : {
4028 4 : const auto castArray =
4029 : static_cast<const arrow::Decimal128Array *>(array);
4030 4 : if (!ConstraintEvaluator(
4031 : constraint,
4032 8 : CPLAtof(castArray->FormatValue(m_nIdxInBatch).c_str())))
4033 : {
4034 2 : return true;
4035 : }
4036 2 : break;
4037 : }
4038 :
4039 4 : case arrow::Type::DECIMAL256:
4040 : {
4041 4 : const auto castArray =
4042 : static_cast<const arrow::Decimal256Array *>(array);
4043 4 : if (!ConstraintEvaluator(
4044 : constraint,
4045 8 : CPLAtof(castArray->FormatValue(m_nIdxInBatch).c_str())))
4046 : {
4047 2 : return true;
4048 : }
4049 2 : break;
4050 : }
4051 :
4052 11 : default:
4053 11 : break;
4054 : }
4055 : }
4056 238 : return false;
4057 : }
4058 :
4059 : /************************************************************************/
4060 : /* SetBatch() */
4061 : /************************************************************************/
4062 :
4063 : inline void
4064 3732 : OGRArrowLayer::SetBatch(const std::shared_ptr<arrow::RecordBatch> &poBatch)
4065 : {
4066 3732 : m_poBatch = poBatch;
4067 3732 : m_poBatchColumns.clear();
4068 3732 : m_poArrayWKB = nullptr;
4069 3732 : m_poArrayWKBLarge = nullptr;
4070 3732 : m_poArrayBBOX = nullptr;
4071 3732 : m_poArrayXMinDouble = nullptr;
4072 3732 : m_poArrayYMinDouble = nullptr;
4073 3732 : m_poArrayXMaxDouble = nullptr;
4074 3732 : m_poArrayYMaxDouble = nullptr;
4075 3732 : m_poArrayXMinFloat = nullptr;
4076 3732 : m_poArrayYMinFloat = nullptr;
4077 3732 : m_poArrayXMaxFloat = nullptr;
4078 3732 : m_poArrayYMaxFloat = nullptr;
4079 :
4080 3732 : if (m_poBatch)
4081 : {
4082 2686 : m_poBatchColumns = m_poBatch->columns();
4083 2686 : SanityCheckOfSetBatch();
4084 : }
4085 :
4086 3732 : if (m_poBatch && m_poFilterGeom && !m_bBaseArrowIgnoreSpatialFilterRect)
4087 : {
4088 : int iCol;
4089 570 : if (m_bIgnoredFields)
4090 : {
4091 148 : iCol = m_anMapGeomFieldIndexToArrayIndex[m_iGeomFieldFilter];
4092 : }
4093 : else
4094 : {
4095 422 : iCol = m_anMapGeomFieldIndexToArrowColumn[m_iGeomFieldFilter];
4096 : }
4097 1140 : if (iCol >= 0 &&
4098 570 : m_aeGeomEncoding[m_iGeomFieldFilter] == OGRArrowGeomEncoding::WKB)
4099 : {
4100 : const arrow::Array *poArrayWKB =
4101 117 : GetStorageArray(m_poBatchColumns[iCol].get());
4102 117 : if (poArrayWKB->type_id() == arrow::Type::BINARY)
4103 117 : m_poArrayWKB =
4104 : static_cast<const arrow::BinaryArray *>(poArrayWKB);
4105 : else
4106 : {
4107 0 : CPLAssert(poArrayWKB->type_id() == arrow::Type::LARGE_BINARY);
4108 0 : m_poArrayWKBLarge =
4109 : static_cast<const arrow::LargeBinaryArray *>(poArrayWKB);
4110 : }
4111 : }
4112 :
4113 1140 : if (iCol >= 0 &&
4114 570 : CPLTestBool(CPLGetConfigOption(
4115 1140 : ("OGR_" + GetDriverUCName() + "_USE_BBOX").c_str(), "YES")))
4116 : {
4117 : const auto oIter =
4118 570 : m_oMapGeomFieldIndexToGeomColBBOX.find(m_iGeomFieldFilter);
4119 570 : if (oIter != m_oMapGeomFieldIndexToGeomColBBOX.end())
4120 : {
4121 274 : const int idx = m_bIgnoredFields ? oIter->second.iArrayIdx
4122 109 : : oIter->second.iArrowCol;
4123 165 : if (idx >= 0)
4124 : {
4125 137 : CPLAssert(static_cast<size_t>(idx) <
4126 : m_poBatchColumns.size());
4127 137 : m_poArrayBBOX = m_poBatchColumns[idx].get();
4128 137 : CPLAssert(m_poArrayBBOX->type_id() == arrow::Type::STRUCT);
4129 137 : const auto castArray =
4130 : static_cast<const arrow::StructArray *>(m_poArrayBBOX);
4131 137 : const auto &subArrays = castArray->fields();
4132 137 : CPLAssert(
4133 : static_cast<size_t>(oIter->second.iArrowSubfieldXMin) <
4134 : subArrays.size());
4135 : const auto xminArray =
4136 137 : subArrays[oIter->second.iArrowSubfieldXMin].get();
4137 137 : CPLAssert(
4138 : static_cast<size_t>(oIter->second.iArrowSubfieldYMin) <
4139 : subArrays.size());
4140 : const auto yminArray =
4141 137 : subArrays[oIter->second.iArrowSubfieldYMin].get();
4142 137 : CPLAssert(
4143 : static_cast<size_t>(oIter->second.iArrowSubfieldXMax) <
4144 : subArrays.size());
4145 : const auto xmaxArray =
4146 137 : subArrays[oIter->second.iArrowSubfieldXMax].get();
4147 137 : CPLAssert(
4148 : static_cast<size_t>(oIter->second.iArrowSubfieldYMax) <
4149 : subArrays.size());
4150 : const auto ymaxArray =
4151 137 : subArrays[oIter->second.iArrowSubfieldYMax].get();
4152 137 : if (oIter->second.bIsFloat)
4153 : {
4154 136 : CPLAssert(xminArray->type_id() == arrow::Type::FLOAT);
4155 136 : m_poArrayXMinFloat =
4156 : static_cast<const arrow::FloatArray *>(xminArray);
4157 136 : CPLAssert(yminArray->type_id() == arrow::Type::FLOAT);
4158 136 : m_poArrayYMinFloat =
4159 : static_cast<const arrow::FloatArray *>(yminArray);
4160 136 : CPLAssert(xmaxArray->type_id() == arrow::Type::FLOAT);
4161 136 : m_poArrayXMaxFloat =
4162 : static_cast<const arrow::FloatArray *>(xmaxArray);
4163 136 : CPLAssert(ymaxArray->type_id() == arrow::Type::FLOAT);
4164 136 : m_poArrayYMaxFloat =
4165 : static_cast<const arrow::FloatArray *>(ymaxArray);
4166 : }
4167 : else
4168 : {
4169 1 : CPLAssert(xminArray->type_id() == arrow::Type::DOUBLE);
4170 1 : m_poArrayXMinDouble =
4171 : static_cast<const arrow::DoubleArray *>(xminArray);
4172 1 : CPLAssert(yminArray->type_id() == arrow::Type::DOUBLE);
4173 1 : m_poArrayYMinDouble =
4174 : static_cast<const arrow::DoubleArray *>(yminArray);
4175 1 : CPLAssert(xmaxArray->type_id() == arrow::Type::DOUBLE);
4176 1 : m_poArrayXMaxDouble =
4177 : static_cast<const arrow::DoubleArray *>(xmaxArray);
4178 1 : CPLAssert(ymaxArray->type_id() == arrow::Type::DOUBLE);
4179 1 : m_poArrayYMaxDouble =
4180 : static_cast<const arrow::DoubleArray *>(ymaxArray);
4181 : }
4182 : }
4183 : }
4184 : }
4185 : }
4186 3732 : }
4187 :
4188 : /************************************************************************/
4189 : /* SanityCheckOfSetBatch() */
4190 : /************************************************************************/
4191 :
4192 2686 : inline void OGRArrowLayer::SanityCheckOfSetBatch() const
4193 : {
4194 : #ifdef DEBUG
4195 2686 : CPLAssert(m_poBatch);
4196 :
4197 2686 : const auto &poColumns = m_poBatch->columns();
4198 :
4199 : // Sanity checks
4200 2686 : CPLAssert(m_poBatch->num_columns() == (m_bIgnoredFields
4201 : ? m_nExpectedBatchColumns
4202 : : m_poSchema->num_fields()));
4203 2686 : const auto &fields = m_poSchema->fields();
4204 :
4205 95156 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i)
4206 : {
4207 : int iCol;
4208 92470 : if (m_bIgnoredFields)
4209 : {
4210 15277 : iCol = m_anMapFieldIndexToArrayIndex[i];
4211 15277 : if (iCol < 0)
4212 3918 : continue;
4213 : }
4214 : else
4215 : {
4216 77193 : iCol = m_anMapFieldIndexToArrowColumn[i][0];
4217 : }
4218 88552 : CPL_IGNORE_RET_VAL(iCol); // to make cppcheck happy
4219 :
4220 88552 : CPLAssert(iCol < static_cast<int>(poColumns.size()));
4221 88552 : CPLAssert(fields[m_anMapFieldIndexToArrowColumn[i][0]]->type()->id() ==
4222 : poColumns[iCol]->type_id());
4223 : }
4224 :
4225 5264 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
4226 : {
4227 : int iCol;
4228 2578 : if (m_bIgnoredFields)
4229 : {
4230 514 : iCol = m_anMapGeomFieldIndexToArrayIndex[i];
4231 514 : if (iCol < 0)
4232 31 : continue;
4233 : }
4234 : else
4235 : {
4236 2064 : iCol = m_anMapGeomFieldIndexToArrowColumn[i];
4237 : }
4238 2547 : CPL_IGNORE_RET_VAL(iCol); // to make cppcheck happy
4239 :
4240 2547 : CPLAssert(iCol < static_cast<int>(poColumns.size()));
4241 2547 : CPLAssert(fields[m_anMapGeomFieldIndexToArrowColumn[i]]->type()->id() ==
4242 : poColumns[iCol]->type_id());
4243 : }
4244 : #else
4245 : CPL_IGNORE_RET_VAL(m_nExpectedBatchColumns);
4246 : #endif
4247 2686 : }
4248 :
4249 : /************************************************************************/
4250 : /* GetNextRawFeature() */
4251 : /************************************************************************/
4252 :
4253 11976 : inline OGRFeature *OGRArrowLayer::GetNextRawFeature()
4254 : {
4255 11976 : if (m_bEOF || !m_bSpatialFilterIntersectsLayerExtent)
4256 421 : return nullptr;
4257 :
4258 11555 : if (m_poBatch == nullptr || m_nIdxInBatch == m_poBatch->num_rows())
4259 : {
4260 3503 : m_bEOF = !ReadNextBatch();
4261 3503 : if (m_bEOF)
4262 1421 : return nullptr;
4263 : }
4264 :
4265 : // Evaluate spatial filter by computing the bounding box of each geometry
4266 : // but without creating a OGRGeometry
4267 10134 : if (m_poFilterGeom && !m_bBaseArrowIgnoreSpatialFilterRect)
4268 : {
4269 : int iCol;
4270 987 : if (m_bIgnoredFields)
4271 : {
4272 206 : iCol = m_anMapGeomFieldIndexToArrayIndex[m_iGeomFieldFilter];
4273 : }
4274 : else
4275 : {
4276 781 : iCol = m_anMapGeomFieldIndexToArrowColumn[m_iGeomFieldFilter];
4277 : }
4278 :
4279 987 : if (iCol >= 0 && (m_poArrayXMinFloat || m_poArrayXMinDouble))
4280 : {
4281 283 : OGREnvelope sEnvelopeSkipToNextFeatureDueToBBOX;
4282 : const auto IntersectsBBOX =
4283 2199 : [this, &sEnvelopeSkipToNextFeatureDueToBBOX]()
4284 : {
4285 627 : if (m_poArrayXMinFloat &&
4286 313 : !m_poArrayXMinFloat->IsNull(m_nIdxInBatch))
4287 : {
4288 313 : sEnvelopeSkipToNextFeatureDueToBBOX.MinX =
4289 313 : m_poArrayXMinFloat->Value(m_nIdxInBatch);
4290 313 : sEnvelopeSkipToNextFeatureDueToBBOX.MinY =
4291 313 : m_poArrayYMinFloat->Value(m_nIdxInBatch);
4292 313 : sEnvelopeSkipToNextFeatureDueToBBOX.MaxX =
4293 313 : m_poArrayXMaxFloat->Value(m_nIdxInBatch);
4294 313 : sEnvelopeSkipToNextFeatureDueToBBOX.MaxY =
4295 313 : m_poArrayYMaxFloat->Value(m_nIdxInBatch);
4296 313 : if (m_sFilterEnvelope.Intersects(
4297 313 : sEnvelopeSkipToNextFeatureDueToBBOX))
4298 : {
4299 270 : return true;
4300 : }
4301 : }
4302 2 : else if (m_poArrayXMinDouble &&
4303 1 : !m_poArrayXMinDouble->IsNull(m_nIdxInBatch))
4304 : {
4305 1 : sEnvelopeSkipToNextFeatureDueToBBOX.MinX =
4306 1 : m_poArrayXMinDouble->Value(m_nIdxInBatch);
4307 1 : sEnvelopeSkipToNextFeatureDueToBBOX.MinY =
4308 1 : m_poArrayYMinDouble->Value(m_nIdxInBatch);
4309 1 : sEnvelopeSkipToNextFeatureDueToBBOX.MaxX =
4310 1 : m_poArrayXMaxDouble->Value(m_nIdxInBatch);
4311 1 : sEnvelopeSkipToNextFeatureDueToBBOX.MaxY =
4312 1 : m_poArrayYMaxDouble->Value(m_nIdxInBatch);
4313 1 : if (m_sFilterEnvelope.Intersects(
4314 1 : sEnvelopeSkipToNextFeatureDueToBBOX))
4315 : {
4316 1 : return true;
4317 : }
4318 : }
4319 43 : return false;
4320 283 : };
4321 :
4322 : while (true)
4323 : {
4324 813 : if (!m_poArrayBBOX->IsNull(m_nIdxInBatch) && IntersectsBBOX() &&
4325 271 : (m_asAttributeFilterConstraints.empty() ||
4326 2 : !SkipToNextFeatureDueToAttributeFilter()))
4327 : {
4328 270 : break;
4329 : }
4330 :
4331 270 : IncrFeatureIdx();
4332 270 : m_nIdxInBatch++;
4333 270 : if (m_nIdxInBatch == m_poBatch->num_rows())
4334 : {
4335 22 : m_bEOF = !ReadNextBatch();
4336 22 : if (m_bEOF)
4337 13 : return nullptr;
4338 : }
4339 270 : }
4340 : }
4341 704 : else if (iCol >= 0 && m_aeGeomEncoding[m_iGeomFieldFilter] ==
4342 : OGRArrowGeomEncoding::WKB)
4343 : {
4344 131 : CPLAssert(m_poArrayWKB || m_poArrayWKBLarge);
4345 131 : OGREnvelope sEnvelope;
4346 :
4347 : while (true)
4348 : {
4349 220 : bool bMatchBBOX = false;
4350 417 : if ((m_poArrayWKB && m_poArrayWKB->IsNull(m_nIdxInBatch)) ||
4351 197 : (m_poArrayWKBLarge &&
4352 0 : m_poArrayWKBLarge->IsNull(m_nIdxInBatch)))
4353 : {
4354 : // nothing to do
4355 : }
4356 : else
4357 : {
4358 197 : if (m_poArrayWKB)
4359 : {
4360 197 : int out_length = 0;
4361 : const uint8_t *data =
4362 197 : m_poArrayWKB->GetValue(m_nIdxInBatch, &out_length);
4363 394 : if (OGRWKBGetBoundingBox(data, out_length, sEnvelope) &&
4364 197 : m_sFilterEnvelope.Intersects(sEnvelope))
4365 : {
4366 115 : bMatchBBOX = true;
4367 : }
4368 : }
4369 : else
4370 : {
4371 0 : CPLAssert(m_poArrayWKBLarge);
4372 0 : int64_t out_length64 = 0;
4373 0 : const uint8_t *data = m_poArrayWKBLarge->GetValue(
4374 : m_nIdxInBatch, &out_length64);
4375 0 : if (out_length64 < INT_MAX &&
4376 0 : OGRWKBGetBoundingBox(data,
4377 : static_cast<int>(out_length64),
4378 0 : sEnvelope) &&
4379 0 : m_sFilterEnvelope.Intersects(sEnvelope))
4380 : {
4381 0 : bMatchBBOX = true;
4382 : }
4383 : }
4384 : }
4385 221 : if (bMatchBBOX && (m_asAttributeFilterConstraints.empty() ||
4386 1 : !SkipToNextFeatureDueToAttributeFilter()))
4387 : {
4388 115 : break;
4389 : }
4390 :
4391 105 : IncrFeatureIdx();
4392 105 : m_nIdxInBatch++;
4393 105 : if (m_nIdxInBatch == m_poBatch->num_rows())
4394 : {
4395 25 : m_bEOF = !ReadNextBatch();
4396 25 : if (m_bEOF)
4397 16 : return nullptr;
4398 : }
4399 89 : }
4400 : }
4401 1146 : else if (iCol >= 0 &&
4402 573 : m_aeGeomEncoding[m_iGeomFieldFilter] ==
4403 : OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOLYGON)
4404 : {
4405 : const auto poGeomFieldDefn =
4406 10 : m_poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
4407 10 : const auto eGeomType = poGeomFieldDefn->GetType();
4408 10 : const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
4409 10 : const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
4410 10 : const int nDim = 2 + (bHasZ ? 1 : 0) + (bHasM ? 1 : 0);
4411 :
4412 : bool bReturnFeature;
4413 0 : do
4414 : {
4415 10 : bReturnFeature = false;
4416 10 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
4417 10 : CPLAssert(array->type_id() == arrow::Type::LIST);
4418 10 : auto listOfPartsArray =
4419 : static_cast<const arrow::ListArray *>(array);
4420 10 : CPLAssert(listOfPartsArray->values()->type_id() ==
4421 : arrow::Type::LIST);
4422 : auto listOfPartsValues =
4423 : std::static_pointer_cast<arrow::ListArray>(
4424 10 : listOfPartsArray->values());
4425 10 : CPLAssert(listOfPartsValues->values()->type_id() ==
4426 : arrow::Type::LIST);
4427 : auto listOfRingsValues =
4428 : std::static_pointer_cast<arrow::ListArray>(
4429 10 : listOfPartsValues->values());
4430 10 : CPLAssert(listOfRingsValues->values()->type_id() ==
4431 : arrow::Type::FIXED_SIZE_LIST);
4432 : auto listOfPointsValues =
4433 : std::static_pointer_cast<arrow::FixedSizeListArray>(
4434 10 : listOfRingsValues->values());
4435 10 : CPLAssert(listOfPointsValues->values()->type_id() ==
4436 : arrow::Type::DOUBLE);
4437 : auto pointValues = std::static_pointer_cast<arrow::DoubleArray>(
4438 10 : listOfPointsValues->values());
4439 :
4440 : while (true)
4441 : {
4442 18 : bool bMatchBBOX = false;
4443 18 : if (!listOfPartsArray->IsNull(m_nIdxInBatch))
4444 : {
4445 14 : OGREnvelope sEnvelope;
4446 : const auto nParts =
4447 14 : listOfPartsArray->value_length(m_nIdxInBatch);
4448 : const auto nPartOffset =
4449 14 : listOfPartsArray->value_offset(m_nIdxInBatch);
4450 28 : for (auto j = decltype(nParts){0}; j < nParts; j++)
4451 : {
4452 14 : const auto nRings = listOfPartsValues->value_length(
4453 14 : nPartOffset + j);
4454 : const auto nRingOffset =
4455 14 : listOfPartsValues->value_offset(nPartOffset +
4456 : j);
4457 14 : if (nRings >= 1)
4458 : {
4459 : const auto nPoints =
4460 14 : listOfRingsValues->value_length(
4461 : nRingOffset);
4462 : const auto nPointOffset =
4463 14 : listOfRingsValues->value_offset(
4464 : nRingOffset) *
4465 14 : nDim;
4466 : const double *padfRawValue =
4467 14 : pointValues->raw_values() + nPointOffset;
4468 73 : for (auto l = decltype(nPoints){0}; l < nPoints;
4469 : ++l)
4470 : {
4471 59 : sEnvelope.Merge(padfRawValue[nDim * l],
4472 59 : padfRawValue[nDim * l + 1]);
4473 : }
4474 : // for bounding box, only the first ring matters
4475 : }
4476 : }
4477 :
4478 24 : if (nParts != 0 &&
4479 10 : m_sFilterEnvelope.Intersects(sEnvelope))
4480 : {
4481 10 : bMatchBBOX = true;
4482 : }
4483 : }
4484 28 : if (bMatchBBOX &&
4485 10 : (m_asAttributeFilterConstraints.empty() ||
4486 0 : !SkipToNextFeatureDueToAttributeFilter()))
4487 : {
4488 10 : bReturnFeature = true;
4489 10 : break;
4490 : }
4491 :
4492 8 : IncrFeatureIdx();
4493 8 : m_nIdxInBatch++;
4494 8 : if (m_nIdxInBatch == m_poBatch->num_rows())
4495 : {
4496 0 : m_bEOF = !ReadNextBatch();
4497 0 : if (m_bEOF)
4498 0 : return nullptr;
4499 0 : break;
4500 : }
4501 8 : }
4502 10 : } while (!bReturnFeature);
4503 : }
4504 563 : else if (iCol >= 0 && m_aeGeomEncoding[m_iGeomFieldFilter] ==
4505 : OGRArrowGeomEncoding::GEOARROW_STRUCT_POINT)
4506 : {
4507 : bool bReturnFeature;
4508 10 : do
4509 : {
4510 44 : bReturnFeature = false;
4511 44 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
4512 44 : CPLAssert(array->type_id() == arrow::Type::STRUCT);
4513 44 : auto pointValues =
4514 : static_cast<const arrow::StructArray *>(array);
4515 44 : const auto &fields = pointValues->fields();
4516 44 : const auto &fieldX = fields[0];
4517 44 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
4518 : const auto fieldXDouble =
4519 44 : static_cast<arrow::DoubleArray *>(fieldX.get());
4520 44 : const auto &fieldY = fields[1];
4521 44 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
4522 : const auto fieldYDouble =
4523 44 : static_cast<arrow::DoubleArray *>(fieldY.get());
4524 :
4525 : while (true)
4526 : {
4527 98 : bool bMatchBBOX = false;
4528 98 : if (!array->IsNull(m_nIdxInBatch))
4529 : {
4530 76 : const double dfX = fieldXDouble->Value(m_nIdxInBatch);
4531 76 : const double dfY = fieldYDouble->Value(m_nIdxInBatch);
4532 76 : if (dfX >= m_sFilterEnvelope.MinX &&
4533 46 : dfY >= m_sFilterEnvelope.MinY &&
4534 46 : dfX <= m_sFilterEnvelope.MaxX &&
4535 30 : dfY <= m_sFilterEnvelope.MaxY)
4536 : {
4537 30 : bMatchBBOX = true;
4538 : }
4539 : }
4540 128 : if (bMatchBBOX &&
4541 30 : (m_asAttributeFilterConstraints.empty() ||
4542 0 : !SkipToNextFeatureDueToAttributeFilter()))
4543 : {
4544 30 : bReturnFeature = true;
4545 30 : break;
4546 : }
4547 :
4548 68 : IncrFeatureIdx();
4549 68 : m_nIdxInBatch++;
4550 68 : if (m_nIdxInBatch == m_poBatch->num_rows())
4551 : {
4552 14 : m_bEOF = !ReadNextBatch();
4553 14 : if (m_bEOF)
4554 4 : return nullptr;
4555 10 : break;
4556 : }
4557 54 : }
4558 40 : } while (!bReturnFeature);
4559 : }
4560 1058 : else if (iCol >= 0 &&
4561 529 : m_aeGeomEncoding[m_iGeomFieldFilter] ==
4562 : OGRArrowGeomEncoding::GEOARROW_STRUCT_LINESTRING)
4563 : {
4564 : bool bReturnFeature;
4565 0 : do
4566 : {
4567 64 : bReturnFeature = false;
4568 64 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
4569 64 : CPLAssert(array->type_id() == arrow::Type::LIST);
4570 64 : const auto listArray =
4571 : static_cast<const arrow::ListArray *>(array);
4572 64 : CPLAssert(listArray->values()->type_id() ==
4573 : arrow::Type::STRUCT);
4574 : auto pointValues = std::static_pointer_cast<arrow::StructArray>(
4575 64 : listArray->values());
4576 64 : const auto &fields = pointValues->fields();
4577 64 : const auto &fieldX = fields[0];
4578 64 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
4579 : const auto fieldXDouble =
4580 64 : static_cast<arrow::DoubleArray *>(fieldX.get());
4581 64 : const auto &fieldY = fields[1];
4582 64 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
4583 : const auto fieldYDouble =
4584 64 : static_cast<arrow::DoubleArray *>(fieldY.get());
4585 :
4586 : while (true)
4587 : {
4588 160 : bool bMatchBBOX = false;
4589 160 : if (!listArray->IsNull(m_nIdxInBatch))
4590 : {
4591 120 : OGREnvelope sEnvelope;
4592 : const auto nPoints =
4593 120 : listArray->value_length(m_nIdxInBatch);
4594 : const auto nPointOffset =
4595 120 : listArray->value_offset(m_nIdxInBatch);
4596 120 : if (nPoints > 0)
4597 : {
4598 : const double *padfRawXValue =
4599 80 : fieldXDouble->raw_values() + nPointOffset;
4600 : const double *padfRawYValue =
4601 80 : fieldYDouble->raw_values() + nPointOffset;
4602 240 : for (auto l = decltype(nPoints){0}; l < nPoints;
4603 : ++l)
4604 : {
4605 160 : sEnvelope.Merge(padfRawXValue[l],
4606 160 : padfRawYValue[l]);
4607 : }
4608 80 : if (m_sFilterEnvelope.Intersects(sEnvelope))
4609 : {
4610 48 : bMatchBBOX = true;
4611 : }
4612 : }
4613 : }
4614 208 : if (bMatchBBOX &&
4615 48 : (m_asAttributeFilterConstraints.empty() ||
4616 0 : !SkipToNextFeatureDueToAttributeFilter()))
4617 : {
4618 48 : bReturnFeature = true;
4619 48 : break;
4620 : }
4621 :
4622 112 : IncrFeatureIdx();
4623 112 : m_nIdxInBatch++;
4624 112 : if (m_nIdxInBatch == m_poBatch->num_rows())
4625 : {
4626 16 : m_bEOF = !ReadNextBatch();
4627 16 : if (m_bEOF)
4628 16 : return nullptr;
4629 0 : break;
4630 : }
4631 96 : }
4632 48 : } while (!bReturnFeature);
4633 : }
4634 465 : else if (iCol >= 0 && m_aeGeomEncoding[m_iGeomFieldFilter] ==
4635 : OGRArrowGeomEncoding::GEOARROW_STRUCT_POLYGON)
4636 : {
4637 : bool bReturnFeature;
4638 0 : do
4639 : {
4640 96 : bReturnFeature = false;
4641 96 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
4642 96 : CPLAssert(array->type_id() == arrow::Type::LIST);
4643 96 : const auto listOfRingsArray =
4644 : static_cast<const arrow::ListArray *>(array);
4645 96 : CPLAssert(listOfRingsArray->values()->type_id() ==
4646 : arrow::Type::LIST);
4647 : const auto listOfRingsValues =
4648 : std::static_pointer_cast<arrow::ListArray>(
4649 96 : listOfRingsArray->values());
4650 96 : CPLAssert(listOfRingsValues->values()->type_id() ==
4651 : arrow::Type::STRUCT);
4652 : auto pointValues = std::static_pointer_cast<arrow::StructArray>(
4653 96 : listOfRingsValues->values());
4654 96 : const auto &fields = pointValues->fields();
4655 96 : const auto &fieldX = fields[0];
4656 96 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
4657 : const auto fieldXDouble =
4658 96 : static_cast<arrow::DoubleArray *>(fieldX.get());
4659 96 : const auto &fieldY = fields[1];
4660 96 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
4661 : const auto fieldYDouble =
4662 96 : static_cast<arrow::DoubleArray *>(fieldY.get());
4663 :
4664 : while (true)
4665 : {
4666 240 : bool bMatchBBOX = false;
4667 240 : if (!listOfRingsArray->IsNull(m_nIdxInBatch))
4668 : {
4669 184 : OGREnvelope sEnvelope;
4670 : const auto nRings =
4671 184 : listOfRingsArray->value_length(m_nIdxInBatch);
4672 : const auto nRingOffset =
4673 184 : listOfRingsArray->value_offset(m_nIdxInBatch);
4674 184 : if (nRings >= 1)
4675 : {
4676 : const auto nPoints =
4677 128 : listOfRingsValues->value_length(nRingOffset);
4678 : const auto nPointOffset =
4679 128 : listOfRingsValues->value_offset(nRingOffset);
4680 : const double *padfRawXValue =
4681 128 : fieldXDouble->raw_values() + nPointOffset;
4682 : const double *padfRawYValue =
4683 128 : fieldYDouble->raw_values() + nPointOffset;
4684 688 : for (auto l = decltype(nPoints){0}; l < nPoints;
4685 : ++l)
4686 : {
4687 560 : sEnvelope.Merge(padfRawXValue[l],
4688 560 : padfRawYValue[l]);
4689 : }
4690 : // for bounding box, only the first ring matters
4691 :
4692 128 : if (m_sFilterEnvelope.Intersects(sEnvelope))
4693 : {
4694 72 : bMatchBBOX = true;
4695 : }
4696 : }
4697 : }
4698 312 : if (bMatchBBOX &&
4699 72 : (m_asAttributeFilterConstraints.empty() ||
4700 0 : !SkipToNextFeatureDueToAttributeFilter()))
4701 : {
4702 72 : bReturnFeature = true;
4703 72 : break;
4704 : }
4705 :
4706 168 : IncrFeatureIdx();
4707 168 : m_nIdxInBatch++;
4708 168 : if (m_nIdxInBatch == m_poBatch->num_rows())
4709 : {
4710 24 : m_bEOF = !ReadNextBatch();
4711 24 : if (m_bEOF)
4712 24 : return nullptr;
4713 0 : break;
4714 : }
4715 144 : }
4716 72 : } while (!bReturnFeature);
4717 : }
4718 738 : else if (iCol >= 0 &&
4719 369 : m_aeGeomEncoding[m_iGeomFieldFilter] ==
4720 : OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOINT)
4721 : {
4722 : bool bReturnFeature;
4723 0 : do
4724 : {
4725 64 : bReturnFeature = false;
4726 64 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
4727 64 : CPLAssert(array->type_id() == arrow::Type::LIST);
4728 64 : const auto listArray =
4729 : static_cast<const arrow::ListArray *>(array);
4730 64 : CPLAssert(listArray->values()->type_id() ==
4731 : arrow::Type::STRUCT);
4732 : auto pointValues = std::static_pointer_cast<arrow::StructArray>(
4733 64 : listArray->values());
4734 64 : const auto &fields = pointValues->fields();
4735 64 : const auto &fieldX = fields[0];
4736 64 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
4737 : const auto fieldXDouble =
4738 64 : static_cast<arrow::DoubleArray *>(fieldX.get());
4739 64 : const auto &fieldY = fields[1];
4740 64 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
4741 : const auto fieldYDouble =
4742 64 : static_cast<arrow::DoubleArray *>(fieldY.get());
4743 :
4744 : while (true)
4745 : {
4746 160 : bool bMatchBBOX = false;
4747 160 : if (!listArray->IsNull(m_nIdxInBatch))
4748 : {
4749 : const auto nPoints =
4750 128 : listArray->value_length(m_nIdxInBatch);
4751 : const auto nPointOffset =
4752 128 : listArray->value_offset(m_nIdxInBatch);
4753 128 : if (nPoints > 0)
4754 : {
4755 : const double *padfRawXValue =
4756 96 : fieldXDouble->raw_values() + nPointOffset;
4757 : const double *padfRawYValue =
4758 96 : fieldYDouble->raw_values() + nPointOffset;
4759 176 : for (auto l = decltype(nPoints){0}; l < nPoints;
4760 : ++l)
4761 : {
4762 128 : if (padfRawXValue[l] >=
4763 128 : m_sFilterEnvelope.MinX &&
4764 108 : padfRawYValue[l] >=
4765 108 : m_sFilterEnvelope.MinY &&
4766 88 : padfRawXValue[l] <=
4767 88 : m_sFilterEnvelope.MaxX &&
4768 68 : padfRawYValue[l] <= m_sFilterEnvelope.MaxY)
4769 : {
4770 48 : bMatchBBOX = true;
4771 48 : break;
4772 : }
4773 : }
4774 : }
4775 : }
4776 208 : if (bMatchBBOX &&
4777 48 : (m_asAttributeFilterConstraints.empty() ||
4778 0 : !SkipToNextFeatureDueToAttributeFilter()))
4779 : {
4780 48 : bReturnFeature = true;
4781 48 : break;
4782 : }
4783 :
4784 112 : IncrFeatureIdx();
4785 112 : m_nIdxInBatch++;
4786 112 : if (m_nIdxInBatch == m_poBatch->num_rows())
4787 : {
4788 16 : m_bEOF = !ReadNextBatch();
4789 16 : if (m_bEOF)
4790 16 : return nullptr;
4791 0 : break;
4792 : }
4793 96 : }
4794 48 : } while (!bReturnFeature);
4795 : }
4796 610 : else if (iCol >= 0 &&
4797 305 : m_aeGeomEncoding[m_iGeomFieldFilter] ==
4798 : OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTILINESTRING)
4799 : {
4800 : bool bReturnFeature;
4801 0 : do
4802 : {
4803 88 : bReturnFeature = false;
4804 88 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
4805 88 : CPLAssert(array->type_id() == arrow::Type::LIST);
4806 88 : auto listOfPartsArray =
4807 : static_cast<const arrow::ListArray *>(array);
4808 88 : CPLAssert(listOfPartsArray->values()->type_id() ==
4809 : arrow::Type::LIST);
4810 : auto listOfPartsValues =
4811 : std::static_pointer_cast<arrow::ListArray>(
4812 88 : listOfPartsArray->values());
4813 88 : CPLAssert(listOfPartsValues->values()->type_id() ==
4814 : arrow::Type::STRUCT);
4815 : auto pointValues = std::static_pointer_cast<arrow::StructArray>(
4816 88 : listOfPartsValues->values());
4817 88 : const auto &fields = pointValues->fields();
4818 88 : const auto &fieldX = fields[0];
4819 88 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
4820 : const auto fieldXDouble =
4821 88 : static_cast<arrow::DoubleArray *>(fieldX.get());
4822 88 : const auto &fieldY = fields[1];
4823 88 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
4824 : const auto fieldYDouble =
4825 88 : static_cast<arrow::DoubleArray *>(fieldY.get());
4826 :
4827 : while (true)
4828 : {
4829 200 : bool bMatchBBOX = false;
4830 200 : if (!listOfPartsArray->IsNull(m_nIdxInBatch))
4831 : {
4832 : const auto nParts =
4833 160 : listOfPartsArray->value_length(m_nIdxInBatch);
4834 : const auto nPartOffset =
4835 160 : listOfPartsArray->value_offset(m_nIdxInBatch);
4836 312 : for (auto j = decltype(nParts){0};
4837 312 : j < nParts && !bMatchBBOX; j++)
4838 : {
4839 152 : OGREnvelope sEnvelope;
4840 : const auto nPoints =
4841 152 : listOfPartsValues->value_length(nPartOffset +
4842 : j);
4843 : const auto nPointOffset =
4844 152 : listOfPartsValues->value_offset(nPartOffset +
4845 : j);
4846 : const double *padfRawXValue =
4847 152 : fieldXDouble->raw_values() + nPointOffset;
4848 : const double *padfRawYValue =
4849 152 : fieldYDouble->raw_values() + nPointOffset;
4850 488 : for (auto l = decltype(nPoints){0}; l < nPoints;
4851 : ++l)
4852 : {
4853 336 : sEnvelope.Merge(padfRawXValue[l],
4854 336 : padfRawYValue[l]);
4855 : }
4856 :
4857 152 : if (m_sFilterEnvelope.Intersects(sEnvelope))
4858 : {
4859 72 : bMatchBBOX = true;
4860 : }
4861 : }
4862 : }
4863 272 : if (bMatchBBOX &&
4864 72 : (m_asAttributeFilterConstraints.empty() ||
4865 0 : !SkipToNextFeatureDueToAttributeFilter()))
4866 : {
4867 72 : bReturnFeature = true;
4868 72 : break;
4869 : }
4870 :
4871 128 : IncrFeatureIdx();
4872 128 : m_nIdxInBatch++;
4873 128 : if (m_nIdxInBatch == m_poBatch->num_rows())
4874 : {
4875 16 : m_bEOF = !ReadNextBatch();
4876 16 : if (m_bEOF)
4877 16 : return nullptr;
4878 0 : break;
4879 : }
4880 112 : }
4881 72 : } while (!bReturnFeature);
4882 : }
4883 434 : else if (iCol >= 0 &&
4884 217 : m_aeGeomEncoding[m_iGeomFieldFilter] ==
4885 : OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOLYGON)
4886 : {
4887 : bool bReturnFeature;
4888 0 : do
4889 : {
4890 96 : bReturnFeature = false;
4891 96 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
4892 96 : CPLAssert(array->type_id() == arrow::Type::LIST);
4893 96 : auto listOfPartsArray =
4894 : static_cast<const arrow::ListArray *>(array);
4895 96 : CPLAssert(listOfPartsArray->values()->type_id() ==
4896 : arrow::Type::LIST);
4897 : auto listOfPartsValues =
4898 : std::static_pointer_cast<arrow::ListArray>(
4899 96 : listOfPartsArray->values());
4900 96 : CPLAssert(listOfPartsValues->values()->type_id() ==
4901 : arrow::Type::LIST);
4902 : auto listOfRingsValues =
4903 : std::static_pointer_cast<arrow::ListArray>(
4904 96 : listOfPartsValues->values());
4905 96 : CPLAssert(listOfRingsValues->values()->type_id() ==
4906 : arrow::Type::STRUCT);
4907 : auto pointValues = std::static_pointer_cast<arrow::StructArray>(
4908 96 : listOfRingsValues->values());
4909 96 : const auto &fields = pointValues->fields();
4910 96 : const auto &fieldX = fields[0];
4911 96 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
4912 : const auto fieldXDouble =
4913 96 : static_cast<arrow::DoubleArray *>(fieldX.get());
4914 96 : const auto &fieldY = fields[1];
4915 96 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
4916 : const auto fieldYDouble =
4917 96 : static_cast<arrow::DoubleArray *>(fieldY.get());
4918 :
4919 : while (true)
4920 : {
4921 240 : bool bMatchBBOX = false;
4922 240 : if (!listOfPartsArray->IsNull(m_nIdxInBatch))
4923 : {
4924 : const auto nParts =
4925 188 : listOfPartsArray->value_length(m_nIdxInBatch);
4926 : const auto nPartOffset =
4927 188 : listOfPartsArray->value_offset(m_nIdxInBatch);
4928 356 : for (auto j = decltype(nParts){0};
4929 356 : j < nParts && !bMatchBBOX; j++)
4930 : {
4931 168 : OGREnvelope sEnvelope;
4932 168 : const auto nRings = listOfPartsValues->value_length(
4933 168 : nPartOffset + j);
4934 : const auto nRingOffset =
4935 168 : listOfPartsValues->value_offset(nPartOffset +
4936 : j);
4937 168 : if (nRings >= 1)
4938 : {
4939 : const auto nPoints =
4940 168 : listOfRingsValues->value_length(
4941 : nRingOffset);
4942 : const auto nPointOffset =
4943 168 : listOfRingsValues->value_offset(
4944 : nRingOffset);
4945 : const double *padfRawXValue =
4946 168 : fieldXDouble->raw_values() + nPointOffset;
4947 : const double *padfRawYValue =
4948 168 : fieldYDouble->raw_values() + nPointOffset;
4949 888 : for (auto l = decltype(nPoints){0}; l < nPoints;
4950 : ++l)
4951 : {
4952 720 : sEnvelope.Merge(padfRawXValue[l],
4953 720 : padfRawYValue[l]);
4954 : }
4955 :
4956 168 : if (m_sFilterEnvelope.Intersects(sEnvelope))
4957 : {
4958 72 : bMatchBBOX = true;
4959 : }
4960 : // for bounding box, only the first ring matters
4961 : }
4962 : }
4963 : }
4964 312 : if (bMatchBBOX &&
4965 72 : (m_asAttributeFilterConstraints.empty() ||
4966 0 : !SkipToNextFeatureDueToAttributeFilter()))
4967 : {
4968 72 : bReturnFeature = true;
4969 72 : break;
4970 : }
4971 :
4972 168 : IncrFeatureIdx();
4973 168 : m_nIdxInBatch++;
4974 168 : if (m_nIdxInBatch == m_poBatch->num_rows())
4975 : {
4976 24 : m_bEOF = !ReadNextBatch();
4977 24 : if (m_bEOF)
4978 24 : return nullptr;
4979 0 : break;
4980 : }
4981 144 : }
4982 72 : } while (!bReturnFeature);
4983 : }
4984 121 : else if (iCol >= 0)
4985 : {
4986 121 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
4987 : while (true)
4988 : {
4989 220 : bool bMatchBBOX = false;
4990 :
4991 : auto poGeometry = std::unique_ptr<OGRGeometry>(
4992 220 : ReadGeometry(m_iGeomFieldFilter, array, m_nIdxInBatch));
4993 220 : if (poGeometry && !poGeometry->IsEmpty())
4994 : {
4995 115 : OGREnvelope sEnvelope;
4996 115 : poGeometry->getEnvelope(&sEnvelope);
4997 115 : if (m_sFilterEnvelope.Intersects(sEnvelope))
4998 : {
4999 88 : bMatchBBOX = true;
5000 : }
5001 : }
5002 220 : if (bMatchBBOX && (m_asAttributeFilterConstraints.empty() ||
5003 0 : !SkipToNextFeatureDueToAttributeFilter()))
5004 : {
5005 88 : break;
5006 : }
5007 :
5008 132 : IncrFeatureIdx();
5009 132 : m_nIdxInBatch++;
5010 132 : if (m_nIdxInBatch == m_poBatch->num_rows())
5011 : {
5012 33 : m_bEOF = !ReadNextBatch();
5013 33 : if (m_bEOF)
5014 33 : return nullptr;
5015 0 : array = GetStorageArray(m_poBatchColumns[iCol].get());
5016 : }
5017 99 : }
5018 825 : }
5019 : }
5020 :
5021 9147 : else if (!m_asAttributeFilterConstraints.empty())
5022 : {
5023 : while (true)
5024 : {
5025 438 : if (!SkipToNextFeatureDueToAttributeFilter())
5026 : {
5027 236 : break;
5028 : }
5029 :
5030 202 : IncrFeatureIdx();
5031 202 : m_nIdxInBatch++;
5032 202 : if (m_nIdxInBatch == m_poBatch->num_rows())
5033 : {
5034 90 : m_bEOF = !ReadNextBatch();
5035 90 : if (m_bEOF)
5036 52 : return nullptr;
5037 : }
5038 : }
5039 : }
5040 :
5041 9920 : auto poFeature = ReadFeature(m_nIdxInBatch, m_poBatchColumns);
5042 :
5043 9920 : if (m_iFIDArrowColumn < 0)
5044 6522 : poFeature->SetFID(m_nFeatureIdx);
5045 :
5046 9920 : IncrFeatureIdx();
5047 9920 : m_nIdxInBatch++;
5048 :
5049 9920 : return poFeature;
5050 : }
5051 :
5052 : /************************************************************************/
5053 : /* GetExtentFromMetadata() */
5054 : /************************************************************************/
5055 :
5056 : inline OGRErr
5057 768 : OGRArrowLayer::GetExtentFromMetadata(const CPLJSONObject &oJSONDef,
5058 : OGREnvelope3D *psExtent)
5059 : {
5060 2304 : const auto oBBox = oJSONDef.GetArray("bbox");
5061 768 : if (oBBox.IsValid() && oBBox.Size() == 4)
5062 : {
5063 463 : psExtent->MinX = oBBox[0].ToDouble();
5064 463 : psExtent->MinY = oBBox[1].ToDouble();
5065 463 : psExtent->MinZ = std::numeric_limits<double>::infinity();
5066 463 : psExtent->MaxX = oBBox[2].ToDouble();
5067 463 : psExtent->MaxY = oBBox[3].ToDouble();
5068 463 : psExtent->MaxZ = -std::numeric_limits<double>::infinity();
5069 463 : if (psExtent->MinX <= psExtent->MaxX)
5070 463 : return OGRERR_NONE;
5071 : }
5072 305 : else if (oBBox.IsValid() && oBBox.Size() == 6)
5073 : {
5074 236 : psExtent->MinX = oBBox[0].ToDouble();
5075 236 : psExtent->MinY = oBBox[1].ToDouble();
5076 236 : psExtent->MinZ = oBBox[2].ToDouble();
5077 236 : psExtent->MaxX = oBBox[3].ToDouble();
5078 236 : psExtent->MaxY = oBBox[4].ToDouble();
5079 236 : psExtent->MaxZ = oBBox[5].ToDouble();
5080 236 : if (psExtent->MinX <= psExtent->MaxX)
5081 236 : return OGRERR_NONE;
5082 : }
5083 69 : return OGRERR_FAILURE;
5084 : }
5085 :
5086 : /************************************************************************/
5087 : /* ISetSpatialFilter() */
5088 : /************************************************************************/
5089 :
5090 1362 : inline OGRErr OGRArrowLayer::ISetSpatialFilter(int iGeomField,
5091 : const OGRGeometry *poGeomIn)
5092 :
5093 : {
5094 : // When changing filters, we need to invalidate cached batches, as
5095 : // PostFilterArrowArray() has potentially modified array contents
5096 1362 : if (m_poFilterGeom)
5097 784 : InvalidateCachedBatches();
5098 :
5099 1362 : m_bSpatialFilterIntersectsLayerExtent = true;
5100 1362 : if (iGeomField < GetLayerDefn()->GetGeomFieldCount())
5101 : {
5102 1340 : m_iGeomFieldFilter = iGeomField;
5103 1340 : if (InstallFilter(poGeomIn))
5104 1115 : ResetReading();
5105 1340 : if (m_poFilterGeom != nullptr)
5106 : {
5107 1048 : OGREnvelope sLayerExtent;
5108 1048 : if (FastGetExtent(iGeomField, &sLayerExtent))
5109 : {
5110 626 : m_bSpatialFilterIntersectsLayerExtent =
5111 626 : m_sFilterEnvelope.Intersects(sLayerExtent);
5112 : }
5113 : }
5114 : }
5115 :
5116 1362 : SetBatch(m_poBatch);
5117 1362 : return OGRERR_NONE;
5118 : }
5119 :
5120 : /************************************************************************/
5121 : /* FastGetExtent() */
5122 : /************************************************************************/
5123 :
5124 735 : inline bool OGRArrowLayer::FastGetExtent(int iGeomField,
5125 : OGREnvelope *psExtent) const
5126 : {
5127 : {
5128 735 : const auto oIter = m_oMapExtents.find(iGeomField);
5129 735 : if (oIter != m_oMapExtents.end())
5130 : {
5131 5 : *psExtent = oIter->second;
5132 5 : return true;
5133 : }
5134 : }
5135 :
5136 : const char *pszGeomFieldName =
5137 730 : m_poFeatureDefn->GetGeomFieldDefn(iGeomField)->GetNameRef();
5138 730 : const auto oIter = m_oMapGeometryColumns.find(pszGeomFieldName);
5139 1458 : if (oIter != m_oMapGeometryColumns.end() &&
5140 728 : CPLTestBool(CPLGetConfigOption(
5141 1458 : ("OGR_" + GetDriverUCName() + "_USE_BBOX").c_str(), "YES")))
5142 : {
5143 722 : const auto &oJSONDef = oIter->second;
5144 722 : OGREnvelope3D sEnvelope3D;
5145 722 : if (GetExtentFromMetadata(oJSONDef, &sEnvelope3D) == OGRERR_NONE)
5146 : {
5147 663 : *psExtent = sEnvelope3D;
5148 663 : return true;
5149 : }
5150 : }
5151 67 : return false;
5152 : }
5153 :
5154 : /************************************************************************/
5155 : /* IGetExtent() */
5156 : /************************************************************************/
5157 :
5158 65 : inline OGRErr OGRArrowLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
5159 : bool bForce)
5160 : {
5161 65 : if (FastGetExtent(iGeomField, psExtent))
5162 : {
5163 44 : return OGRERR_NONE;
5164 : }
5165 :
5166 21 : if (!bForce && !CanRunNonForcedGetExtent())
5167 : {
5168 0 : return OGRERR_FAILURE;
5169 : }
5170 :
5171 : int iCol;
5172 21 : if (m_bIgnoredFields)
5173 : {
5174 0 : iCol = m_anMapGeomFieldIndexToArrayIndex[iGeomField];
5175 : }
5176 : else
5177 : {
5178 21 : iCol = m_anMapGeomFieldIndexToArrowColumn[iGeomField];
5179 : }
5180 21 : if (iCol < 0)
5181 : {
5182 0 : return OGRERR_FAILURE;
5183 : }
5184 :
5185 21 : if (m_aeGeomEncoding[iGeomField] == OGRArrowGeomEncoding::WKB)
5186 : {
5187 7 : ResetReading();
5188 7 : if (m_poBatch == nullptr)
5189 : {
5190 6 : m_bEOF = !ReadNextBatch();
5191 6 : if (m_bEOF)
5192 0 : return OGRERR_FAILURE;
5193 : }
5194 7 : *psExtent = OGREnvelope();
5195 :
5196 7 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
5197 7 : const arrow::BinaryArray *smallArray = nullptr;
5198 7 : const arrow::LargeBinaryArray *largeArray = nullptr;
5199 7 : if (array->type_id() == arrow::Type::BINARY)
5200 7 : smallArray = static_cast<const arrow::BinaryArray *>(array);
5201 : else
5202 : {
5203 0 : CPLAssert(array->type_id() == arrow::Type::LARGE_BINARY);
5204 0 : largeArray = static_cast<const arrow::LargeBinaryArray *>(array);
5205 : }
5206 7 : OGREnvelope sEnvelope;
5207 : while (true)
5208 : {
5209 32 : if (!array->IsNull(m_nIdxInBatch))
5210 : {
5211 30 : if (smallArray)
5212 : {
5213 30 : int out_length = 0;
5214 : const uint8_t *data =
5215 30 : smallArray->GetValue(m_nIdxInBatch, &out_length);
5216 30 : if (OGRWKBGetBoundingBox(data, out_length, sEnvelope))
5217 : {
5218 30 : psExtent->Merge(sEnvelope);
5219 : }
5220 : }
5221 : else
5222 : {
5223 0 : assert(largeArray);
5224 0 : int64_t out_length = 0;
5225 : const uint8_t *data =
5226 0 : largeArray->GetValue(m_nIdxInBatch, &out_length);
5227 0 : if (out_length < INT_MAX &&
5228 0 : OGRWKBGetBoundingBox(data, static_cast<int>(out_length),
5229 : sEnvelope))
5230 : {
5231 0 : psExtent->Merge(sEnvelope);
5232 : }
5233 : }
5234 : }
5235 :
5236 32 : m_nIdxInBatch++;
5237 32 : if (m_nIdxInBatch == m_poBatch->num_rows())
5238 : {
5239 7 : m_bEOF = !ReadNextBatch();
5240 7 : if (m_bEOF)
5241 : {
5242 7 : ResetReading();
5243 7 : if (psExtent->IsInit())
5244 : {
5245 7 : m_oMapExtents[iGeomField] = *psExtent;
5246 7 : return OGRERR_NONE;
5247 : }
5248 0 : return OGRERR_FAILURE;
5249 : }
5250 0 : array = GetStorageArray(m_poBatchColumns[iCol].get());
5251 0 : if (array->type_id() == arrow::Type::BINARY)
5252 0 : smallArray = static_cast<const arrow::BinaryArray *>(array);
5253 : else
5254 : {
5255 0 : CPLAssert(array->type_id() == arrow::Type::LARGE_BINARY);
5256 0 : largeArray =
5257 : static_cast<const arrow::LargeBinaryArray *>(array);
5258 : }
5259 : }
5260 25 : }
5261 : }
5262 14 : else if (m_aeGeomEncoding[iGeomField] ==
5263 : OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOLYGON)
5264 : {
5265 0 : ResetReading();
5266 0 : if (m_poBatch == nullptr)
5267 : {
5268 0 : m_bEOF = !ReadNextBatch();
5269 0 : if (m_bEOF)
5270 0 : return OGRERR_FAILURE;
5271 : }
5272 0 : *psExtent = OGREnvelope();
5273 :
5274 : const auto poGeomFieldDefn =
5275 0 : m_poFeatureDefn->GetGeomFieldDefn(iGeomField);
5276 0 : const auto eGeomType = poGeomFieldDefn->GetType();
5277 0 : const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
5278 0 : const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
5279 0 : const int nDim = 2 + (bHasZ ? 1 : 0) + (bHasM ? 1 : 0);
5280 :
5281 0 : begin_multipolygon:
5282 0 : auto array = m_poBatchColumns[iCol].get();
5283 0 : CPLAssert(array->type_id() == arrow::Type::LIST);
5284 0 : auto listOfPartsArray = static_cast<const arrow::ListArray *>(array);
5285 0 : CPLAssert(listOfPartsArray->values()->type_id() == arrow::Type::LIST);
5286 : auto listOfPartsValues = std::static_pointer_cast<arrow::ListArray>(
5287 0 : listOfPartsArray->values());
5288 0 : CPLAssert(listOfPartsValues->values()->type_id() == arrow::Type::LIST);
5289 : auto listOfRingsValues = std::static_pointer_cast<arrow::ListArray>(
5290 0 : listOfPartsValues->values());
5291 0 : CPLAssert(listOfRingsValues->values()->type_id() ==
5292 : arrow::Type::FIXED_SIZE_LIST);
5293 : auto listOfPointsValues =
5294 : std::static_pointer_cast<arrow::FixedSizeListArray>(
5295 0 : listOfRingsValues->values());
5296 0 : CPLAssert(listOfPointsValues->values()->type_id() ==
5297 : arrow::Type::DOUBLE);
5298 : auto pointValues = std::static_pointer_cast<arrow::DoubleArray>(
5299 0 : listOfPointsValues->values());
5300 :
5301 : while (true)
5302 : {
5303 0 : if (!listOfPartsArray->IsNull(m_nIdxInBatch))
5304 : {
5305 : const auto nParts =
5306 0 : listOfPartsArray->value_length(m_nIdxInBatch);
5307 : const auto nPartOffset =
5308 0 : listOfPartsArray->value_offset(m_nIdxInBatch);
5309 0 : for (auto j = decltype(nParts){0}; j < nParts; j++)
5310 : {
5311 : const auto nRings =
5312 0 : listOfPartsValues->value_length(nPartOffset + j);
5313 : const auto nRingOffset =
5314 0 : listOfPartsValues->value_offset(nPartOffset + j);
5315 0 : if (nRings >= 1)
5316 : {
5317 : const auto nPoints =
5318 0 : listOfRingsValues->value_length(nRingOffset);
5319 : const auto nPointOffset =
5320 0 : listOfRingsValues->value_offset(nRingOffset) * nDim;
5321 : const double *padfRawValue =
5322 0 : pointValues->raw_values() + nPointOffset;
5323 0 : for (auto l = decltype(nPoints){0}; l < nPoints; ++l)
5324 : {
5325 0 : psExtent->Merge(padfRawValue[nDim * l],
5326 0 : padfRawValue[nDim * l + 1]);
5327 : }
5328 : // for bounding box, only the first ring matters
5329 : }
5330 : }
5331 : }
5332 :
5333 0 : m_nIdxInBatch++;
5334 0 : if (m_nIdxInBatch == m_poBatch->num_rows())
5335 : {
5336 0 : m_bEOF = !ReadNextBatch();
5337 0 : if (m_bEOF)
5338 : {
5339 0 : ResetReading();
5340 0 : if (psExtent->IsInit())
5341 : {
5342 0 : m_oMapExtents[iGeomField] = *psExtent;
5343 0 : return OGRERR_NONE;
5344 : }
5345 0 : return OGRERR_FAILURE;
5346 : }
5347 0 : goto begin_multipolygon;
5348 : }
5349 0 : }
5350 : }
5351 :
5352 14 : return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
5353 : }
5354 :
5355 : /************************************************************************/
5356 : /* FastGetExtent3D() */
5357 : /************************************************************************/
5358 :
5359 38 : inline bool OGRArrowLayer::FastGetExtent3D(int iGeomField,
5360 : OGREnvelope3D *psExtent) const
5361 : {
5362 : {
5363 38 : const auto oIter = m_oMapExtents3D.find(iGeomField);
5364 38 : if (oIter != m_oMapExtents3D.end())
5365 : {
5366 0 : *psExtent = oIter->second;
5367 0 : return true;
5368 : }
5369 : }
5370 :
5371 : const char *pszGeomFieldName =
5372 38 : m_poFeatureDefn->GetGeomFieldDefn(iGeomField)->GetNameRef();
5373 38 : const auto oIter = m_oMapGeometryColumns.find(pszGeomFieldName);
5374 76 : if (oIter != m_oMapGeometryColumns.end() &&
5375 38 : CPLTestBool(CPLGetConfigOption(
5376 76 : ("OGR_" + GetDriverUCName() + "_USE_BBOX").c_str(), "YES")))
5377 : {
5378 36 : const auto &oJSONDef = oIter->second;
5379 64 : if (GetExtentFromMetadata(oJSONDef, psExtent) == OGRERR_NONE &&
5380 28 : psExtent->Is3D())
5381 : {
5382 2 : return true;
5383 : }
5384 : }
5385 36 : return false;
5386 : }
5387 :
5388 : /************************************************************************/
5389 : /* IGetExtent3D() */
5390 : /************************************************************************/
5391 :
5392 19 : inline OGRErr OGRArrowLayer::IGetExtent3D(int iGeomField,
5393 : OGREnvelope3D *psExtent, bool bForce)
5394 : {
5395 19 : if (FastGetExtent3D(iGeomField, psExtent))
5396 : {
5397 1 : return OGRERR_NONE;
5398 : }
5399 :
5400 18 : return OGRLayer::IGetExtent3D(iGeomField, psExtent, bForce);
5401 : }
5402 :
5403 : /************************************************************************/
5404 : /* OverrideArrowSchemaRelease() */
5405 : /************************************************************************/
5406 :
5407 : template <class T>
5408 609 : static void OverrideArrowRelease(OGRArrowDataset *poDS, T *obj)
5409 : {
5410 : // We override the release callback, since it can use the memory pool,
5411 : // and we need to make sure it is still alive when the object (ArrowArray
5412 : // or ArrowSchema) is deleted
5413 609 : struct OverriddenPrivate
5414 : {
5415 : OverriddenPrivate() = default;
5416 : OverriddenPrivate(const OverriddenPrivate &) = delete;
5417 : OverriddenPrivate &operator=(const OverriddenPrivate &) = delete;
5418 :
5419 : std::shared_ptr<arrow::MemoryPool> poMemoryPool{};
5420 : void (*pfnPreviousRelease)(T *) = nullptr;
5421 : void *pPreviousPrivateData = nullptr;
5422 :
5423 609 : static void release(T *l_obj)
5424 : {
5425 609 : OverriddenPrivate *myPrivate =
5426 : static_cast<OverriddenPrivate *>(l_obj->private_data);
5427 609 : l_obj->private_data = myPrivate->pPreviousPrivateData;
5428 609 : l_obj->release = myPrivate->pfnPreviousRelease;
5429 609 : l_obj->release(l_obj);
5430 609 : delete myPrivate;
5431 609 : }
5432 : };
5433 :
5434 609 : auto overriddenPrivate = new OverriddenPrivate();
5435 609 : overriddenPrivate->poMemoryPool = poDS->GetSharedMemoryPool();
5436 609 : overriddenPrivate->pPreviousPrivateData = obj->private_data;
5437 609 : overriddenPrivate->pfnPreviousRelease = obj->release;
5438 :
5439 609 : obj->release = OverriddenPrivate::release;
5440 609 : obj->private_data = overriddenPrivate;
5441 609 : }
5442 :
5443 : /************************************************************************/
5444 : /* UseRecordBatchBaseImplementation() */
5445 : /************************************************************************/
5446 :
5447 474 : inline bool OGRArrowLayer::UseRecordBatchBaseImplementation() const
5448 : {
5449 474 : if (CPLTestBool(CPLGetConfigOption("OGR_ARROW_STREAM_BASE_IMPL", "NO")))
5450 : {
5451 4 : return true;
5452 : }
5453 :
5454 470 : if (m_aosArrowArrayStreamOptions.FetchBool(GAS_OPT_DATETIME_AS_STRING,
5455 : false))
5456 : {
5457 1 : const int nFieldCount = m_poFeatureDefn->GetFieldCount();
5458 14 : for (int i = 0; i < nFieldCount; ++i)
5459 : {
5460 14 : const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
5461 28 : if (!poFieldDefn->IsIgnored() &&
5462 14 : poFieldDefn->GetType() == OFTDateTime)
5463 : {
5464 1 : CPLDebug("ARROW",
5465 : "DATETIME_AS_STRING=YES not compatible of fast "
5466 : "Arrow implementation");
5467 1 : return true;
5468 : }
5469 : }
5470 : }
5471 :
5472 469 : if (EQUAL(m_aosArrowArrayStreamOptions.FetchNameValueDef(
5473 : "GEOMETRY_ENCODING", ""),
5474 : "WKB"))
5475 : {
5476 131 : const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
5477 181 : for (int i = 0; i < nGeomFieldCount; i++)
5478 : {
5479 146 : if (!m_poFeatureDefn->GetGeomFieldDefn(i)->IsIgnored() &&
5480 259 : m_aeGeomEncoding[i] != OGRArrowGeomEncoding::WKB &&
5481 113 : m_aeGeomEncoding[i] != OGRArrowGeomEncoding::WKT)
5482 : {
5483 96 : CPLDebug("ARROW", "Geometry encoding not compatible of fast "
5484 : "Arrow implementation");
5485 96 : return true;
5486 : }
5487 : }
5488 : }
5489 :
5490 373 : if (m_bIgnoredFields)
5491 : {
5492 : std::vector<int> ignoredState(m_anMapFieldIndexToArrowColumn.size(),
5493 106 : -1);
5494 7658 : for (size_t i = 0; i < m_anMapFieldIndexToArrowColumn.size(); i++)
5495 : {
5496 7553 : const int nArrowCol = m_anMapFieldIndexToArrowColumn[i][0];
5497 7553 : if (nArrowCol >= static_cast<int>(ignoredState.size()))
5498 1 : ignoredState.resize(nArrowCol + 1, -1);
5499 : const auto bIsIgnored =
5500 7553 : m_poFeatureDefn->GetFieldDefn(static_cast<int>(i))->IsIgnored();
5501 7553 : if (ignoredState[nArrowCol] < 0)
5502 : {
5503 7095 : ignoredState[nArrowCol] = static_cast<int>(bIsIgnored);
5504 : }
5505 : else
5506 : {
5507 : // struct fields will point to the same arrow column
5508 458 : if (ignoredState[nArrowCol] != static_cast<int>(bIsIgnored))
5509 : {
5510 1 : CPLDebug("ARROW",
5511 : "Inconsistent ignore state for Arrow Columns");
5512 1 : return true;
5513 : }
5514 : }
5515 : }
5516 : }
5517 :
5518 372 : if (m_poAttrQuery || m_poFilterGeom)
5519 : {
5520 163 : struct ArrowSchema *psSchema = &m_sCachedSchema;
5521 163 : if (psSchema->release)
5522 104 : psSchema->release(psSchema);
5523 163 : memset(psSchema, 0, sizeof(*psSchema));
5524 :
5525 326 : const bool bCanPostFilter = GetArrowSchemaInternal(psSchema) == 0 &&
5526 163 : CanPostFilterArrowArray(psSchema);
5527 163 : if (!bCanPostFilter)
5528 11 : return true;
5529 : }
5530 :
5531 361 : return false;
5532 : }
5533 :
5534 : /************************************************************************/
5535 : /* GetArrowStream() */
5536 : /************************************************************************/
5537 :
5538 293 : inline bool OGRArrowLayer::GetArrowStream(struct ArrowArrayStream *out_stream,
5539 : CSLConstList papszOptions)
5540 : {
5541 293 : if (!OGRLayer::GetArrowStream(out_stream, papszOptions))
5542 0 : return false;
5543 :
5544 293 : m_bUseRecordBatchBaseImplementation = UseRecordBatchBaseImplementation();
5545 293 : return true;
5546 : }
5547 :
5548 : /************************************************************************/
5549 : /* GetArrowSchema() */
5550 : /************************************************************************/
5551 :
5552 433 : inline int OGRArrowLayer::GetArrowSchema(struct ArrowArrayStream *stream,
5553 : struct ArrowSchema *out_schema)
5554 : {
5555 433 : if (m_bUseRecordBatchBaseImplementation)
5556 209 : return OGRLayer::GetArrowSchema(stream, out_schema);
5557 :
5558 224 : return GetArrowSchemaInternal(out_schema);
5559 : }
5560 :
5561 : /************************************************************************/
5562 : /* GetArrowSchemaInternal() */
5563 : /************************************************************************/
5564 :
5565 10800 : static bool IsSilentlyIgnoredFormatForGetArrowSchemaArray(const char *format)
5566 : {
5567 : // n: null
5568 10800 : return strcmp(format, "n") == 0;
5569 : }
5570 :
5571 : inline int
5572 387 : OGRArrowLayer::GetArrowSchemaInternal(struct ArrowSchema *out_schema) const
5573 : {
5574 774 : auto status = arrow::ExportSchema(*m_poSchema, out_schema);
5575 387 : if (!status.ok())
5576 : {
5577 0 : CPLError(CE_Failure, CPLE_AppDefined, "ExportSchema() failed with %s",
5578 0 : status.message().c_str());
5579 0 : return EIO;
5580 : }
5581 :
5582 387 : CPLAssert(out_schema->n_children == m_poSchema->num_fields());
5583 :
5584 : // Remove ignored fields from the ArrowSchema.
5585 :
5586 : struct FieldDesc
5587 : {
5588 : bool bIsRegularField =
5589 : false; // true = attribute field, false = geometry field
5590 : int nIdx = -1;
5591 : };
5592 :
5593 : // cppcheck-suppress unreadVariable
5594 : std::vector<FieldDesc> fieldDesc(
5595 774 : static_cast<size_t>(out_schema->n_children));
5596 21816 : for (size_t i = 0; i < m_anMapFieldIndexToArrowColumn.size(); i++)
5597 : {
5598 21429 : const int nArrowCol = m_anMapFieldIndexToArrowColumn[i][0];
5599 21429 : if (fieldDesc[nArrowCol].nIdx < 0)
5600 : {
5601 20148 : fieldDesc[nArrowCol].bIsRegularField = true;
5602 20148 : fieldDesc[nArrowCol].nIdx = static_cast<int>(i);
5603 : }
5604 : }
5605 788 : for (size_t i = 0; i < m_anMapGeomFieldIndexToArrowColumn.size(); i++)
5606 : {
5607 401 : const int nArrowCol = m_anMapGeomFieldIndexToArrowColumn[i];
5608 401 : CPLAssert(fieldDesc[nArrowCol].nIdx < 0);
5609 401 : fieldDesc[nArrowCol].bIsRegularField = false;
5610 401 : fieldDesc[nArrowCol].nIdx = static_cast<int>(i);
5611 : }
5612 :
5613 387 : int j = 0;
5614 : const char *pszReqGeomEncoding =
5615 387 : m_aosArrowArrayStreamOptions.FetchNameValueDef("GEOMETRY_ENCODING", "");
5616 :
5617 387 : const char *pszExtensionName = EXTENSION_NAME_OGC_WKB;
5618 387 : if (EQUAL(pszReqGeomEncoding, "WKB") || EQUAL(pszReqGeomEncoding, ""))
5619 : {
5620 : const char *const pszGeometryMetadataEncoding =
5621 387 : m_aosArrowArrayStreamOptions.FetchNameValue(
5622 : "GEOMETRY_METADATA_ENCODING");
5623 387 : if (pszGeometryMetadataEncoding)
5624 : {
5625 0 : if (EQUAL(pszGeometryMetadataEncoding, "OGC"))
5626 0 : pszExtensionName = EXTENSION_NAME_OGC_WKB;
5627 0 : else if (EQUAL(pszGeometryMetadataEncoding, "GEOARROW"))
5628 0 : pszExtensionName = EXTENSION_NAME_GEOARROW_WKB;
5629 : else
5630 0 : CPLError(CE_Warning, CPLE_NotSupported,
5631 : "Unsupported GEOMETRY_METADATA_ENCODING value: %s",
5632 : pszGeometryMetadataEncoding);
5633 : }
5634 : }
5635 :
5636 21294 : for (int i = 0; i < out_schema->n_children; ++i)
5637 : {
5638 20907 : if (fieldDesc[i].nIdx < 0)
5639 : {
5640 358 : if (m_iFIDArrowColumn == i)
5641 : {
5642 21 : out_schema->children[j] = out_schema->children[i];
5643 21 : ++j;
5644 : }
5645 337 : else if (cpl::contains(m_oSetBBoxArrowColumns, i))
5646 : {
5647 : // Remove bounding box columns from exported schema
5648 82 : out_schema->children[i]->release(out_schema->children[i]);
5649 82 : out_schema->children[i] = nullptr;
5650 : }
5651 255 : else if (IsSilentlyIgnoredFormatForGetArrowSchemaArray(
5652 255 : out_schema->children[i]->format))
5653 : {
5654 : // Silently ignore columns with null data type...
5655 255 : out_schema->children[i]->release(out_schema->children[i]);
5656 : }
5657 : else
5658 : {
5659 : // can happen with data types we don't support
5660 0 : if (m_aosArrowArrayStreamOptions.FetchBool(
5661 : "SILENCE_GET_SCHEMA_ERROR", false))
5662 : {
5663 0 : CPLDebug(GetDriverUCName().c_str(),
5664 : "GetArrowSchema() error: fieldDesc[%d].nIdx < 0 "
5665 : "not expected: name=%s, format=%s",
5666 0 : i, out_schema->children[i]->name,
5667 0 : out_schema->children[i]->format);
5668 : }
5669 : else
5670 : {
5671 0 : CPLError(CE_Failure, CPLE_NotSupported,
5672 : "GetArrowSchema() error: fieldDesc[%d].nIdx < 0 "
5673 : "not expected: name=%s, format=%s",
5674 0 : i, out_schema->children[i]->name,
5675 0 : out_schema->children[i]->format);
5676 : }
5677 0 : for (; i < out_schema->n_children; ++i, ++j)
5678 0 : out_schema->children[j] = out_schema->children[i];
5679 0 : out_schema->n_children = j;
5680 :
5681 0 : out_schema->release(out_schema);
5682 :
5683 0 : return EIO;
5684 : }
5685 358 : continue;
5686 : }
5687 :
5688 : const auto bIsIgnored =
5689 20549 : fieldDesc[i].bIsRegularField
5690 20549 : ? m_poFeatureDefn->GetFieldDefn(fieldDesc[i].nIdx)->IsIgnored()
5691 401 : : m_poFeatureDefn->GetGeomFieldDefn(fieldDesc[i].nIdx)
5692 401 : ->IsIgnored();
5693 20549 : if (bIsIgnored)
5694 : {
5695 2911 : out_schema->children[i]->release(out_schema->children[i]);
5696 : }
5697 : else
5698 : {
5699 18021 : if (!fieldDesc[i].bIsRegularField &&
5700 383 : EQUAL(pszReqGeomEncoding, "WKB"))
5701 : {
5702 64 : const int iGeomField = fieldDesc[i].nIdx;
5703 64 : if (m_aeGeomEncoding[iGeomField] == OGRArrowGeomEncoding::WKT)
5704 : {
5705 : const auto poGeomFieldDefn =
5706 17 : m_poFeatureDefn->GetGeomFieldDefn(iGeomField);
5707 17 : CPLAssert(strcmp(out_schema->children[i]->name,
5708 : poGeomFieldDefn->GetNameRef()) == 0);
5709 17 : auto poSchema = CreateSchemaForWKBGeometryColumn(
5710 : poGeomFieldDefn, "z", pszExtensionName);
5711 17 : out_schema->children[i]->release(out_schema->children[i]);
5712 17 : *(out_schema->children[j]) = *poSchema;
5713 17 : CPLFree(poSchema);
5714 : }
5715 47 : else if (m_aeGeomEncoding[iGeomField] !=
5716 : OGRArrowGeomEncoding::WKB)
5717 : {
5718 : // Shouldn't happen if UseRecordBatchBaseImplementation()
5719 : // is up to date
5720 0 : CPLAssert(false);
5721 : }
5722 : else
5723 : {
5724 47 : out_schema->children[j] = out_schema->children[i];
5725 : }
5726 : }
5727 : else
5728 : {
5729 17574 : out_schema->children[j] = out_schema->children[i];
5730 : }
5731 :
5732 18021 : if (!fieldDesc[i].bIsRegularField &&
5733 383 : (EQUAL(pszReqGeomEncoding, "WKB") ||
5734 319 : EQUAL(pszReqGeomEncoding, "")))
5735 : {
5736 383 : const int iGeomField = fieldDesc[i].nIdx;
5737 383 : const char *pszFormat = out_schema->children[j]->format;
5738 383 : if (m_aeGeomEncoding[iGeomField] == OGRArrowGeomEncoding::WKB &&
5739 695 : !out_schema->children[j]->metadata &&
5740 312 : (strcmp(pszFormat, "z") == 0 ||
5741 1 : strcmp(pszFormat, "Z") == 0))
5742 : {
5743 : const auto poGeomFieldDefn =
5744 312 : m_poFeatureDefn->GetGeomFieldDefn(iGeomField);
5745 : // Set ARROW:extension:name = ogc:wkb
5746 312 : auto poSchema = CreateSchemaForWKBGeometryColumn(
5747 : poGeomFieldDefn, pszFormat, pszExtensionName);
5748 312 : out_schema->children[i]->release(out_schema->children[i]);
5749 312 : *(out_schema->children[j]) = *poSchema;
5750 312 : CPLFree(poSchema);
5751 : }
5752 : }
5753 :
5754 17638 : ++j;
5755 : }
5756 : }
5757 :
5758 387 : out_schema->n_children = j;
5759 :
5760 387 : OverrideArrowRelease(m_poArrowDS, out_schema);
5761 :
5762 387 : return 0;
5763 : }
5764 :
5765 : /************************************************************************/
5766 : /* GetNextArrowArray() */
5767 : /************************************************************************/
5768 :
5769 604 : inline int OGRArrowLayer::GetNextArrowArray(struct ArrowArrayStream *stream,
5770 : struct ArrowArray *out_array)
5771 : {
5772 604 : if (m_bUseRecordBatchBaseImplementation)
5773 224 : return OGRLayer::GetNextArrowArray(stream, out_array);
5774 :
5775 : while (true)
5776 : {
5777 398 : if (m_bEOF)
5778 : {
5779 10 : memset(out_array, 0, sizeof(*out_array));
5780 176 : return 0;
5781 : }
5782 :
5783 388 : if (m_poBatch == nullptr || m_nIdxInBatch == m_poBatch->num_rows())
5784 : {
5785 363 : if (!ReadNextBatch())
5786 : {
5787 166 : if (m_poAttrQuery || m_poFilterGeom)
5788 : {
5789 97 : InvalidateCachedBatches();
5790 : }
5791 166 : m_bEOF = true;
5792 166 : memset(out_array, 0, sizeof(*out_array));
5793 166 : return 0;
5794 : }
5795 : }
5796 :
5797 222 : const bool bNeedsPostFilter =
5798 333 : (m_poAttrQuery && !m_bBaseArrowIgnoreAttributeFilter) ||
5799 111 : (m_poFilterGeom && !m_bBaseArrowIgnoreSpatialFilter);
5800 :
5801 : struct ArrowSchema schema;
5802 222 : memset(&schema, 0, sizeof(schema));
5803 222 : auto status = arrow::ExportRecordBatch(*m_poBatch, out_array, &schema);
5804 222 : m_nIdxInBatch = m_poBatch->num_rows();
5805 222 : if (!status.ok())
5806 : {
5807 0 : CPLError(CE_Failure, CPLE_AppDefined,
5808 : "ExportRecordBatch() failed with %s",
5809 0 : status.message().c_str());
5810 0 : return EIO;
5811 : }
5812 :
5813 : // Remove bounding box columns from exported array, or columns
5814 : // of unsupported data types that we voluntarily strip off.
5815 : const auto RemoveBBoxOrUnsupportedColumns =
5816 32653 : [out_array, &schema](const std::set<int> &oSetBBoxArrayIndex)
5817 : {
5818 222 : int j = 0;
5819 10817 : for (int i = 0; i < static_cast<int>(schema.n_children); ++i)
5820 : {
5821 21140 : if (cpl::contains(oSetBBoxArrayIndex, i) ||
5822 10545 : IsSilentlyIgnoredFormatForGetArrowSchemaArray(
5823 10545 : schema.children[i]->format))
5824 : {
5825 126 : out_array->children[i]->release(out_array->children[i]);
5826 126 : out_array->children[i] = nullptr;
5827 :
5828 126 : schema.children[i]->release(schema.children[i]);
5829 126 : schema.children[i] = nullptr;
5830 : }
5831 : else
5832 : {
5833 10469 : out_array->children[j] = out_array->children[i];
5834 10469 : schema.children[j] = schema.children[i];
5835 10469 : ++j;
5836 : }
5837 : }
5838 222 : out_array->n_children = j;
5839 222 : schema.n_children = j;
5840 222 : };
5841 :
5842 222 : if (m_bIgnoredFields)
5843 : {
5844 178 : std::set<int> oSetBBoxArrayIndex;
5845 126 : for (const auto &iter : m_oMapGeomFieldIndexToGeomColBBOX)
5846 : {
5847 37 : if (iter.second.iArrayIdx >= 0)
5848 20 : oSetBBoxArrayIndex.insert(iter.second.iArrayIdx);
5849 : }
5850 89 : RemoveBBoxOrUnsupportedColumns(oSetBBoxArrayIndex);
5851 : }
5852 : else
5853 : {
5854 133 : RemoveBBoxOrUnsupportedColumns(m_oSetBBoxArrowColumns);
5855 : }
5856 :
5857 222 : if (EQUAL(m_aosArrowArrayStreamOptions.FetchNameValueDef(
5858 : "GEOMETRY_ENCODING", ""),
5859 : "WKB"))
5860 : {
5861 40 : const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
5862 95 : for (int i = 0; i < nGeomFieldCount; i++)
5863 : {
5864 : const auto poGeomFieldDefn =
5865 55 : m_poFeatureDefn->GetGeomFieldDefn(i);
5866 55 : if (!poGeomFieldDefn->IsIgnored())
5867 : {
5868 47 : if (m_aeGeomEncoding[i] == OGRArrowGeomEncoding::WKT)
5869 : {
5870 : const int nArrayIdx =
5871 18 : m_bIgnoredFields
5872 26 : ? m_anMapGeomFieldIndexToArrayIndex[i]
5873 8 : : m_anMapGeomFieldIndexToArrowColumn[i];
5874 18 : auto sourceArray = out_array->children[nArrayIdx];
5875 : auto targetArray =
5876 18 : strcmp(schema.children[nArrayIdx]->format, "u") == 0
5877 18 : ? CreateWKBArrayFromWKTArray<uint32_t>(
5878 : sourceArray)
5879 0 : : CreateWKBArrayFromWKTArray<uint64_t>(
5880 18 : sourceArray);
5881 18 : if (targetArray)
5882 : {
5883 18 : sourceArray->release(sourceArray);
5884 18 : *(out_array->children[nArrayIdx]) = *targetArray;
5885 18 : CPLFree(targetArray);
5886 : }
5887 : else
5888 : {
5889 0 : out_array->release(out_array);
5890 0 : memset(out_array, 0, sizeof(*out_array));
5891 0 : if (schema.release)
5892 0 : schema.release(&schema);
5893 0 : return ENOMEM;
5894 : }
5895 : }
5896 29 : else if (m_aeGeomEncoding[i] != OGRArrowGeomEncoding::WKB)
5897 : {
5898 : // Shouldn't happen if UseRecordBatchBaseImplementation()
5899 : // is up to date
5900 0 : CPLAssert(false);
5901 : }
5902 : }
5903 : }
5904 : }
5905 :
5906 222 : if (schema.release)
5907 222 : schema.release(&schema);
5908 :
5909 222 : OverrideArrowRelease(m_poArrowDS, out_array);
5910 :
5911 222 : const auto nFeatureIdxCur = m_nFeatureIdx;
5912 : // TODO: We likely have an issue regarding FIDs based on m_nFeatureIdx
5913 : // when m_iFIDArrowColumn < 0, only a subset of row groups is
5914 : // selected, and this batch goes across non consecutive row groups.
5915 1119 : for (int64_t i = 0; i < m_nIdxInBatch; ++i)
5916 897 : IncrFeatureIdx();
5917 :
5918 222 : if (bNeedsPostFilter)
5919 : {
5920 130 : CPLStringList aosOptions;
5921 130 : if (m_iFIDArrowColumn < 0)
5922 : aosOptions.SetNameValue(
5923 : "BASE_SEQUENTIAL_FID",
5924 : CPLSPrintf(CPL_FRMT_GIB,
5925 122 : static_cast<GIntBig>(nFeatureIdxCur)));
5926 :
5927 : // If there might be more than one record batch, it is more
5928 : // prudent to clone the array before modifying it.
5929 130 : if (nFeatureIdxCur > 0 || !TestCapability(OLCFastFeatureCount) ||
5930 0 : out_array->length < GetFeatureCount(false))
5931 : {
5932 : struct ArrowArray new_array;
5933 130 : if (!OGRCloneArrowArray(&m_sCachedSchema, out_array,
5934 : &new_array))
5935 : {
5936 0 : if (out_array->release)
5937 0 : out_array->release(out_array);
5938 0 : memset(out_array, 0, sizeof(*out_array));
5939 0 : return ENOMEM;
5940 : }
5941 130 : if (out_array->release)
5942 130 : out_array->release(out_array);
5943 130 : memcpy(out_array, &new_array, sizeof(new_array));
5944 : }
5945 :
5946 130 : PostFilterArrowArray(&m_sCachedSchema, out_array,
5947 130 : aosOptions.List());
5948 130 : if (out_array->length == 0)
5949 : {
5950 18 : if (out_array->release)
5951 18 : out_array->release(out_array);
5952 18 : memset(out_array, 0, sizeof(*out_array));
5953 : // If there are no records after filtering, start again
5954 : // with a new batch
5955 18 : continue;
5956 : }
5957 : }
5958 :
5959 204 : break;
5960 18 : }
5961 :
5962 204 : return 0;
5963 : }
5964 :
5965 : /************************************************************************/
5966 : /* OGRArrowLayerAppendBuffer */
5967 : /************************************************************************/
5968 :
5969 : class OGRArrowLayerAppendBuffer : public OGRAppendBuffer
5970 : {
5971 : public:
5972 18 : OGRArrowLayerAppendBuffer(struct ArrowArray *targetArrayIn,
5973 : size_t nInitialCapacityIn)
5974 18 : : m_psTargetArray(targetArrayIn)
5975 : {
5976 18 : m_nCapacity = nInitialCapacityIn;
5977 18 : m_pRawBuffer = const_cast<void *>(m_psTargetArray->buffers[2]);
5978 18 : }
5979 :
5980 : protected:
5981 4 : bool Grow(size_t nItemSize) override
5982 : {
5983 4 : constexpr uint32_t MAX_SIZE_SINT32 =
5984 : static_cast<uint32_t>(std::numeric_limits<int32_t>::max());
5985 4 : if (nItemSize > MAX_SIZE_SINT32 - m_nSize)
5986 : {
5987 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too large WKT content");
5988 0 : return false;
5989 : }
5990 4 : size_t nNewCapacity = m_nSize + nItemSize;
5991 4 : CPLAssert(m_nCapacity <= MAX_SIZE_SINT32);
5992 : const size_t nDoubleCapacity =
5993 4 : std::min<size_t>(MAX_SIZE_SINT32, 2 * m_nCapacity);
5994 4 : if (nNewCapacity < nDoubleCapacity)
5995 4 : nNewCapacity = nDoubleCapacity;
5996 4 : CPLAssert(nNewCapacity <= MAX_SIZE_SINT32);
5997 4 : void *newBuffer = VSI_MALLOC_ALIGNED_AUTO_VERBOSE(nNewCapacity);
5998 4 : if (newBuffer == nullptr)
5999 : {
6000 0 : return false;
6001 : }
6002 4 : m_nCapacity = nNewCapacity;
6003 4 : memcpy(newBuffer, m_pRawBuffer, m_nSize);
6004 4 : VSIFreeAligned(m_pRawBuffer);
6005 4 : m_pRawBuffer = newBuffer;
6006 4 : m_psTargetArray->buffers[2] = m_pRawBuffer;
6007 4 : return true;
6008 : }
6009 :
6010 : private:
6011 : struct ArrowArray *m_psTargetArray;
6012 :
6013 : OGRArrowLayerAppendBuffer(const OGRArrowLayerAppendBuffer &) = delete;
6014 : OGRArrowLayerAppendBuffer &
6015 : operator=(const OGRArrowLayerAppendBuffer &) = delete;
6016 : };
6017 :
6018 : /************************************************************************/
6019 : /* CreateWKBArrayFromWKTArray() */
6020 : /************************************************************************/
6021 :
6022 : template <typename SourceOffset>
6023 : inline struct ArrowArray *
6024 18 : OGRArrowLayer::CreateWKBArrayFromWKTArray(const struct ArrowArray *sourceArray)
6025 : {
6026 18 : CPLAssert(sourceArray->n_buffers == 3);
6027 18 : CPLAssert(sourceArray->buffers[1] != nullptr);
6028 18 : CPLAssert(sourceArray->buffers[2] != nullptr);
6029 :
6030 18 : const size_t nLength = static_cast<size_t>(sourceArray->length);
6031 : auto targetArray = static_cast<struct ArrowArray *>(
6032 18 : CPLCalloc(1, sizeof(struct ArrowArray)));
6033 18 : targetArray->release = OGRLayer::ReleaseArray;
6034 18 : targetArray->length = nLength;
6035 :
6036 18 : targetArray->n_buffers = 3;
6037 18 : targetArray->buffers =
6038 18 : static_cast<const void **>(CPLCalloc(3, sizeof(void *)));
6039 :
6040 : // Allocate validity map buffer if needed
6041 18 : const auto sourceNull =
6042 18 : static_cast<const uint8_t *>(sourceArray->buffers[0]);
6043 18 : const size_t nOffset = static_cast<size_t>(sourceArray->offset);
6044 18 : uint8_t *targetNull = nullptr;
6045 18 : if (sourceArray->null_count && sourceNull)
6046 : {
6047 6 : targetArray->buffers[0] =
6048 3 : VSI_MALLOC_ALIGNED_AUTO_VERBOSE((nLength + 7) / 8);
6049 3 : if (targetArray->buffers[0])
6050 : {
6051 3 : targetArray->null_count = sourceArray->null_count;
6052 3 : targetNull = static_cast<uint8_t *>(
6053 3 : const_cast<void *>(targetArray->buffers[0]));
6054 3 : if (nOffset == 0)
6055 : {
6056 3 : memcpy(targetNull, sourceNull, (nLength + 7) / 8);
6057 : }
6058 : else
6059 : {
6060 0 : memset(targetNull, 0, (nLength + 7) / 8);
6061 0 : for (size_t i = 0; i < nLength; ++i)
6062 : {
6063 0 : if ((sourceNull[(i + nOffset) / 8] >> ((i + nOffset) % 8)) &
6064 : 1)
6065 0 : targetNull[i / 8] |= (1 << (i % 8));
6066 : }
6067 : }
6068 : }
6069 : }
6070 :
6071 : // Allocate offset buffer
6072 36 : targetArray->buffers[1] =
6073 18 : VSI_MALLOC_ALIGNED_AUTO_VERBOSE(sizeof(uint32_t) * (1 + nLength));
6074 :
6075 : // Allocate data (WKB) buffer
6076 18 : constexpr size_t DEFAULT_WKB_SIZE = 100;
6077 18 : uint32_t nInitialCapacity = static_cast<uint32_t>(std::min<size_t>(
6078 18 : std::numeric_limits<int32_t>::max(), DEFAULT_WKB_SIZE * nLength));
6079 18 : targetArray->buffers[2] = VSI_MALLOC_ALIGNED_AUTO_VERBOSE(nInitialCapacity);
6080 :
6081 : // Check buffers have been allocated
6082 18 : if ((sourceArray->null_count && sourceNull && !targetNull) ||
6083 18 : targetArray->buffers[1] == nullptr ||
6084 18 : targetArray->buffers[2] == nullptr)
6085 : {
6086 0 : targetArray->release(targetArray);
6087 0 : return nullptr;
6088 : }
6089 :
6090 36 : OGRArrowLayerAppendBuffer oOGRAppendBuffer(targetArray, nInitialCapacity);
6091 18 : OGRWKTToWKBTranslator oTranslator(oOGRAppendBuffer);
6092 :
6093 18 : const auto sourceOffsets =
6094 18 : static_cast<const SourceOffset *>(sourceArray->buffers[1]) + nOffset;
6095 18 : auto sourceBytes =
6096 18 : static_cast<char *>(const_cast<void *>(sourceArray->buffers[2]));
6097 18 : auto targetOffsets =
6098 18 : static_cast<uint32_t *>(const_cast<void *>(targetArray->buffers[1]));
6099 159 : for (size_t i = 0; i < nLength; ++i)
6100 : {
6101 141 : targetOffsets[i] = static_cast<uint32_t>(oOGRAppendBuffer.GetSize());
6102 :
6103 141 : if (targetNull && ((targetNull[i / 8] >> (i % 8)) & 1) == 0)
6104 : {
6105 3 : continue;
6106 : }
6107 :
6108 276 : const size_t nWKBSize = oTranslator.TranslateWKT(
6109 138 : sourceBytes + sourceOffsets[i],
6110 138 : static_cast<size_t>(sourceOffsets[i + 1] - sourceOffsets[i]),
6111 138 : sourceOffsets[i + 1] < sourceOffsets[nLength]);
6112 138 : if (nWKBSize == static_cast<size_t>(-1))
6113 : {
6114 0 : targetArray->release(targetArray);
6115 0 : return nullptr;
6116 : }
6117 : }
6118 18 : targetOffsets[nLength] = static_cast<uint32_t>(oOGRAppendBuffer.GetSize());
6119 :
6120 18 : return targetArray;
6121 : }
6122 :
6123 : /************************************************************************/
6124 : /* TestCapability() */
6125 : /************************************************************************/
6126 :
6127 908 : inline int OGRArrowLayer::TestCapability(const char *pszCap) const
6128 : {
6129 :
6130 908 : if (EQUAL(pszCap, OLCStringsAsUTF8))
6131 500 : return true;
6132 :
6133 589 : else if (EQUAL(pszCap, OLCFastGetArrowStream) &&
6134 181 : !UseRecordBatchBaseImplementation())
6135 : {
6136 181 : return true;
6137 : }
6138 :
6139 227 : if (EQUAL(pszCap, OLCFastGetExtent))
6140 : {
6141 31 : OGREnvelope sEnvelope;
6142 48 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
6143 : {
6144 31 : if (!FastGetExtent(i, &sEnvelope))
6145 14 : return false;
6146 : }
6147 17 : return true;
6148 : }
6149 :
6150 196 : if (EQUAL(pszCap, OLCFastGetExtent3D))
6151 : {
6152 19 : OGREnvelope3D sEnvelope;
6153 20 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
6154 : {
6155 19 : if (!FastGetExtent3D(i, &sEnvelope))
6156 18 : return false;
6157 : }
6158 1 : return true;
6159 : }
6160 :
6161 177 : return false;
6162 : }
6163 :
6164 : #if defined(__clang__)
6165 : #pragma clang diagnostic pop
6166 : #endif
6167 :
6168 : #endif /* OGARROWLAYER_HPP_INCLUDED */
|