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 OGR_ARROW_H
14 : #define OGR_ARROW_H
15 :
16 : #include "gdal_pam.h"
17 : #include "ogrsf_frmts.h"
18 :
19 : #include <map>
20 : #include <set>
21 :
22 : #include "ogr_include_arrow.h"
23 :
24 : enum class OGRArrowGeomEncoding
25 : {
26 : WKB,
27 : WKT,
28 :
29 : // F(ixed) S(ize) L(ist) of (x,y[,z][,m]) values / Interleaved layout
30 : GEOARROW_FSL_GENERIC, // only used by OGRArrowWriterLayer::m_eGeomEncoding
31 : GEOARROW_FSL_POINT,
32 : GEOARROW_FSL_LINESTRING,
33 : GEOARROW_FSL_POLYGON,
34 : GEOARROW_FSL_MULTIPOINT,
35 : GEOARROW_FSL_MULTILINESTRING,
36 : GEOARROW_FSL_MULTIPOLYGON,
37 :
38 : // Struct of (x,y,[,z][,m])
39 : GEOARROW_STRUCT_GENERIC, // only used by OGRArrowWriterLayer::m_eGeomEncoding
40 : GEOARROW_STRUCT_POINT,
41 : GEOARROW_STRUCT_LINESTRING,
42 : GEOARROW_STRUCT_POLYGON,
43 : GEOARROW_STRUCT_MULTIPOINT,
44 : GEOARROW_STRUCT_MULTILINESTRING,
45 : GEOARROW_STRUCT_MULTIPOLYGON,
46 : };
47 :
48 : /************************************************************************/
49 : /* OGRArrowIsGeoArrowStruct() */
50 : /************************************************************************/
51 :
52 260 : inline bool OGRArrowIsGeoArrowStruct(OGRArrowGeomEncoding eEncoding)
53 : {
54 260 : switch (eEncoding)
55 : {
56 260 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_GENERIC:
57 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_POINT:
58 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_LINESTRING:
59 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_POLYGON:
60 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOINT:
61 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTILINESTRING:
62 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOLYGON:
63 260 : return true;
64 :
65 0 : default:
66 0 : return false;
67 : }
68 : }
69 :
70 : /************************************************************************/
71 : /* OGRArrowLayer */
72 : /************************************************************************/
73 :
74 : class OGRArrowDataset;
75 :
76 : class OGRArrowLayer CPL_NON_FINAL
77 : : public OGRLayer,
78 : public OGRGetNextFeatureThroughRaw<OGRArrowLayer>
79 : {
80 : public:
81 : struct Constraint
82 : {
83 : enum class Type
84 : {
85 : Integer,
86 : Integer64,
87 : Real,
88 : String,
89 : };
90 : int iField = -1; // index to a OGRFeatureDefn OGRField
91 : int iArrayIdx = -1; // index to m_poBatchColumns
92 : int nOperation = -1; // SWQ_xxxx
93 : Type eType{};
94 : OGRField sValue{};
95 : std::string osValue{};
96 : };
97 :
98 : private:
99 : OGRArrowLayer(const OGRArrowLayer &) = delete;
100 : OGRArrowLayer &operator=(const OGRArrowLayer &) = delete;
101 :
102 : int m_nUseOptimizedAttributeFilter = -1;
103 : bool m_bSpatialFilterIntersectsLayerExtent = true;
104 : bool m_bUseRecordBatchBaseImplementation = false;
105 :
106 : // Modified by UseRecordBatchBaseImplementation()
107 : mutable struct ArrowSchema m_sCachedSchema = {};
108 :
109 : bool SkipToNextFeatureDueToAttributeFilter() const;
110 : void ExploreExprNode(const swq_expr_node *poNode);
111 : bool UseRecordBatchBaseImplementation() const;
112 :
113 : template <typename SourceOffset>
114 : static struct ArrowArray *
115 : CreateWKBArrayFromWKTArray(const struct ArrowArray *sourceArray);
116 :
117 : int GetArrowSchemaInternal(struct ArrowSchema *out) const;
118 :
119 : protected:
120 : OGRArrowDataset *m_poArrowDS = nullptr;
121 : arrow::MemoryPool *m_poMemoryPool = nullptr;
122 : OGRFeatureDefn *m_poFeatureDefn = nullptr;
123 : std::shared_ptr<arrow::Schema> m_poSchema{};
124 : std::string m_osFIDColumn{};
125 : int m_iFIDArrowColumn = -1;
126 : std::vector<std::vector<int>> m_anMapFieldIndexToArrowColumn{};
127 : std::vector<int> m_anMapGeomFieldIndexToArrowColumn{};
128 : std::vector<OGRArrowGeomEncoding> m_aeGeomEncoding{};
129 :
130 : //! Whether bounding box based spatial filter should be skipped.
131 : // This is set to true by OGRParquetDatasetLayer when there is a bounding
132 : // box field, as an optimization.
133 : bool m_bBaseArrowIgnoreSpatialFilterRect = false;
134 :
135 : //! Whether spatial filter should be skipped (by GetNextArrowArray())
136 : // This is set to true by OGRParquetDatasetLayer when filtering points in
137 : // a rectangle.
138 : bool m_bBaseArrowIgnoreSpatialFilter = false;
139 :
140 : //! Describe the bbox column of a geometry column
141 : struct GeomColBBOX
142 : {
143 : bool bIsFloat = false;
144 : int iArrowCol = -1;
145 : int iArrayIdx = -1; // only valid when m_bIgnoredFields == true
146 : int iArrowSubfieldXMin = -1;
147 : int iArrowSubfieldYMin = -1;
148 : int iArrowSubfieldXMax = -1;
149 : int iArrowSubfieldYMax = -1;
150 : };
151 :
152 : //! Map from OGR geometry field index to GeomColBBOX
153 : std::map<int, GeomColBBOX> m_oMapGeomFieldIndexToGeomColBBOX{};
154 :
155 : const arrow::BinaryArray *m_poArrayWKB = nullptr;
156 : const arrow::LargeBinaryArray *m_poArrayWKBLarge = nullptr;
157 : const arrow::Array *m_poArrayBBOX = nullptr;
158 : const arrow::DoubleArray *m_poArrayXMinDouble = nullptr;
159 : const arrow::DoubleArray *m_poArrayYMinDouble = nullptr;
160 : const arrow::DoubleArray *m_poArrayXMaxDouble = nullptr;
161 : const arrow::DoubleArray *m_poArrayYMaxDouble = nullptr;
162 : const arrow::FloatArray *m_poArrayXMinFloat = nullptr;
163 : const arrow::FloatArray *m_poArrayYMinFloat = nullptr;
164 : const arrow::FloatArray *m_poArrayXMaxFloat = nullptr;
165 : const arrow::FloatArray *m_poArrayYMaxFloat = nullptr;
166 :
167 : //! References values in range [0, m_poSchema->field_count()-1]
168 : std::set<int> m_oSetBBoxArrowColumns{};
169 :
170 : bool m_bIgnoredFields = false;
171 : std::vector<int>
172 : m_anMapFieldIndexToArrayIndex{}; // only valid when m_bIgnoredFields is
173 : // set
174 : std::vector<int> m_anMapGeomFieldIndexToArrayIndex{}; // only valid when
175 : // m_bIgnoredFields is set
176 : int m_nRequestedFIDColumn = -1; // only valid when m_bIgnoredFields is set
177 :
178 : int m_nExpectedBatchColumns =
179 : -1; // Should be equal to m_poBatch->num_columns() (when
180 : // m_bIgnoredFields is set)
181 :
182 : bool m_bEOF = false;
183 : int64_t m_nFeatureIdx = 0;
184 : int64_t m_nIdxInBatch = 0;
185 : std::map<std::string, CPLJSONObject> m_oMapGeometryColumns{};
186 : mutable std::map<int, OGREnvelope> m_oMapExtents{};
187 : int m_iRecordBatch = -1;
188 : std::shared_ptr<arrow::RecordBatch> m_poBatch{};
189 : // m_poBatch->columns() is a relatively costly operation, so cache its
190 : // result
191 : std::vector<std::shared_ptr<arrow::Array>>
192 : m_poBatchColumns{}; // must always be == m_poBatch->columns()
193 : mutable std::shared_ptr<arrow::Array> m_poReadFeatureTmpArray{};
194 :
195 : std::vector<Constraint> m_asAttributeFilterConstraints{};
196 :
197 : //! Whether attribute filter should be skipped.
198 : // This is set to true by OGRParquetDatasetLayer when it can fully translate
199 : // a filter, as an optimization.
200 : bool m_bBaseArrowIgnoreAttributeFilter = false;
201 :
202 : std::map<std::string, std::unique_ptr<OGRFieldDefn>>
203 : LoadGDALSchema(const arrow::KeyValueMetadata *kv_metadata);
204 :
205 : void LoadGDALMetadata(const arrow::KeyValueMetadata *kv_metadata);
206 :
207 : OGRArrowLayer(OGRArrowDataset *poDS, const char *pszLayerName);
208 :
209 : virtual std::string GetDriverUCName() const = 0;
210 : static bool IsIntegerArrowType(arrow::Type::type typeId);
211 : static bool
212 : IsHandledListOrMapType(const std::shared_ptr<arrow::DataType> &valueType);
213 : static bool
214 : IsHandledListType(const std::shared_ptr<arrow::BaseListType> &listType);
215 : static bool
216 : IsHandledMapType(const std::shared_ptr<arrow::MapType> &mapType);
217 : static bool
218 : IsValidGeometryEncoding(const std::shared_ptr<arrow::Field> &field,
219 : const std::string &osEncoding,
220 : bool bWarnIfUnknownEncoding,
221 : OGRwkbGeometryType &eGeomTypeOut,
222 : OGRArrowGeomEncoding &eGeomEncodingOut);
223 : static OGRwkbGeometryType
224 : GetGeometryTypeFromString(const std::string &osType);
225 : bool
226 : MapArrowTypeToOGR(const std::shared_ptr<arrow::DataType> &type,
227 : const std::shared_ptr<arrow::Field> &field,
228 : OGRFieldDefn &oField, OGRFieldType &eType,
229 : OGRFieldSubType &eSubType, const std::vector<int> &path,
230 : const std::map<std::string, std::unique_ptr<OGRFieldDefn>>
231 : &oMapFieldNameToGDALSchemaFieldDefn);
232 : void CreateFieldFromSchema(
233 : const std::shared_ptr<arrow::Field> &field,
234 : const std::vector<int> &path,
235 : const std::map<std::string, std::unique_ptr<OGRFieldDefn>>
236 : &oMapFieldNameToGDALSchemaFieldDefn);
237 : std::unique_ptr<OGRFieldDomain>
238 : BuildDomainFromBatch(const std::string &osDomainName,
239 : const std::shared_ptr<arrow::RecordBatch> &poBatch,
240 : int iCol) const;
241 : OGRwkbGeometryType ComputeGeometryColumnTypeProcessBatch(
242 : const std::shared_ptr<arrow::RecordBatch> &poBatch, int iGeomCol,
243 : int iBatchCol, OGRwkbGeometryType eGeomType) const;
244 : static bool ReadWKBBoundingBox(const uint8_t *data, size_t size,
245 : OGREnvelope &sEnvelope);
246 : OGRFeature *ReadFeature(
247 : int64_t nIdxInBatch,
248 : const std::vector<std::shared_ptr<arrow::Array>> &poColumnArrays) const;
249 : OGRGeometry *ReadGeometry(int iGeomField, const arrow::Array *array,
250 : int64_t nIdxInBatch) const;
251 : virtual bool ReadNextBatch() = 0;
252 : virtual void InvalidateCachedBatches() = 0;
253 : OGRFeature *GetNextRawFeature();
254 :
255 0 : virtual bool CanRunNonForcedGetExtent()
256 : {
257 0 : return true;
258 : }
259 :
260 : void SetBatch(const std::shared_ptr<arrow::RecordBatch> &poBatch);
261 :
262 : // Refreshes Constraint.iArrayIdx from iField. To be called by SetIgnoredFields()
263 : void ComputeConstraintsArrayIdx();
264 :
265 : static const swq_expr_node *GetColumnSubNode(const swq_expr_node *poNode);
266 : static const swq_expr_node *GetConstantSubNode(const swq_expr_node *poNode);
267 : static bool IsComparisonOp(int op);
268 :
269 : virtual bool FastGetExtent(int iGeomField, OGREnvelope *psExtent) const;
270 : bool FastGetExtent3D(int iGeomField, OGREnvelope3D *psExtent) const;
271 : static OGRErr GetExtentFromMetadata(const CPLJSONObject &oJSONDef,
272 : OGREnvelope3D *psExtent);
273 :
274 : int GetArrowSchema(struct ArrowArrayStream *,
275 : struct ArrowSchema *out) override;
276 : int GetNextArrowArray(struct ArrowArrayStream *,
277 : struct ArrowArray *out) override;
278 :
279 4695 : virtual void IncrFeatureIdx()
280 : {
281 4695 : ++m_nFeatureIdx;
282 4695 : }
283 :
284 : void SanityCheckOfSetBatch() const;
285 :
286 : public:
287 : virtual ~OGRArrowLayer() override;
288 :
289 13525 : OGRFeatureDefn *GetLayerDefn() override
290 : {
291 13525 : return m_poFeatureDefn;
292 : }
293 :
294 : void ResetReading() override;
295 :
296 1875 : const char *GetFIDColumn() override
297 : {
298 1875 : return m_osFIDColumn.c_str();
299 : }
300 9053 : DEFINE_GET_NEXT_FEATURE_THROUGH_RAW(OGRArrowLayer)
301 : OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
302 : bool bForce) override;
303 : OGRErr IGetExtent3D(int iGeomField, OGREnvelope3D *psExtent,
304 : bool bForce) override;
305 : OGRErr SetAttributeFilter(const char *pszFilter) override;
306 :
307 : OGRErr ISetSpatialFilter(int iGeomField,
308 : const OGRGeometry *poGeom) override;
309 :
310 : int TestCapability(const char *pszCap) override;
311 :
312 : bool GetArrowStream(struct ArrowArrayStream *out_stream,
313 : CSLConstList papszOptions = nullptr) override;
314 :
315 : virtual std::unique_ptr<OGRFieldDomain>
316 : BuildDomain(const std::string &osDomainName, int iFieldIndex) const = 0;
317 :
318 : static void TimestampToOGR(int64_t timestamp,
319 : const arrow::TimestampType *timestampType,
320 : int nTZFlag, OGRField *psField);
321 : };
322 :
323 : /************************************************************************/
324 : /* OGRArrowDataset */
325 : /************************************************************************/
326 :
327 : class OGRArrowDataset CPL_NON_FINAL : public GDALPamDataset
328 : {
329 : std::shared_ptr<arrow::MemoryPool> m_poMemoryPool{};
330 : std::unique_ptr<OGRArrowLayer> m_poLayer{};
331 : std::vector<std::string> m_aosDomainNames{};
332 : std::map<std::string, int> m_oMapDomainNameToCol{};
333 :
334 : protected:
335 1120 : void close()
336 : {
337 1120 : m_poLayer.reset();
338 1120 : m_poMemoryPool.reset();
339 1120 : }
340 :
341 : public:
342 : explicit OGRArrowDataset(
343 : const std::shared_ptr<arrow::MemoryPool> &poMemoryPool);
344 :
345 1665 : inline arrow::MemoryPool *GetMemoryPool() const
346 : {
347 1665 : return m_poMemoryPool.get();
348 : }
349 :
350 609 : inline const std::shared_ptr<arrow::MemoryPool> &GetSharedMemoryPool() const
351 : {
352 609 : return m_poMemoryPool;
353 : }
354 :
355 : void SetLayer(std::unique_ptr<OGRArrowLayer> &&poLayer);
356 :
357 : void RegisterDomainName(const std::string &osDomainName, int iFieldIndex);
358 :
359 : std::vector<std::string> GetFieldDomainNames(
360 : CSLConstList /*papszOptions*/ = nullptr) const override;
361 : const OGRFieldDomain *
362 : GetFieldDomain(const std::string &name) const override;
363 :
364 : int GetLayerCount() override;
365 : OGRLayer *GetLayer(int idx) override;
366 : };
367 :
368 : /************************************************************************/
369 : /* OGRArrowWriterLayer */
370 : /************************************************************************/
371 :
372 : class OGRArrowWriterLayer CPL_NON_FINAL : public OGRLayer
373 :
374 : {
375 : protected:
376 : OGRArrowWriterLayer(const OGRArrowWriterLayer &) = delete;
377 : OGRArrowWriterLayer &operator=(const OGRArrowWriterLayer &) = delete;
378 :
379 : arrow::MemoryPool *m_poMemoryPool = nullptr;
380 : bool m_bInitializationOK = false;
381 : std::shared_ptr<arrow::io::OutputStream> m_poOutputStream{};
382 : std::shared_ptr<arrow::Schema> m_poSchema{};
383 : OGRFeatureDefn *m_poFeatureDefn = nullptr;
384 : std::map<std::string, std::unique_ptr<OGRFieldDomain>> m_oMapFieldDomains{};
385 : std::map<std::string, std::shared_ptr<arrow::Array>>
386 : m_oMapFieldDomainToStringArray{};
387 :
388 : bool m_bWriteFieldArrowExtensionName = false;
389 : OGRArrowGeomEncoding m_eGeomEncoding = OGRArrowGeomEncoding::WKB;
390 : std::vector<OGRArrowGeomEncoding> m_aeGeomEncoding{};
391 : int m_nWKTCoordinatePrecision = -1;
392 :
393 : //! Base struct data type for GeoArrow struct geometry columns.
394 : // Constraint: if not empty, m_apoBaseStructGeomType.size() == m_poFeatureDefn->GetGeomFieldCount()
395 : std::vector<std::shared_ptr<arrow::DataType>> m_apoBaseStructGeomType{};
396 :
397 : //! Whether to use a struct field with the values of the bounding box
398 : // of the geometries. Used by Parquet.
399 : bool m_bWriteBBoxStruct = false;
400 :
401 : //! Schema fields for bounding box of geometry columns.
402 : // Constraint: if not empty, m_apoFieldsBBOX.size() == m_poFeatureDefn->GetGeomFieldCount()
403 : std::vector<std::shared_ptr<arrow::Field>> m_apoFieldsBBOX{};
404 :
405 : //! Array builers for bounding box of geometry columns.
406 : // m_apoBuildersBBOXStruct is for the top-level field of type struct.
407 : // m_apoBuildersBBOX{XMin|YMin|XMax|YMax} are for the floating-point values
408 : // Constraint: if not empty, m_apoBuildersBBOX{Struct|XMin|YMin|XMax|YMax}.size() == m_poFeatureDefn->GetGeomFieldCount()
409 : std::vector<std::shared_ptr<arrow::StructBuilder>>
410 : m_apoBuildersBBOXStruct{};
411 : std::vector<std::shared_ptr<arrow::FloatBuilder>> m_apoBuildersBBOXXMin{};
412 : std::vector<std::shared_ptr<arrow::FloatBuilder>> m_apoBuildersBBOXYMin{};
413 : std::vector<std::shared_ptr<arrow::FloatBuilder>> m_apoBuildersBBOXXMax{};
414 : std::vector<std::shared_ptr<arrow::FloatBuilder>> m_apoBuildersBBOXYMax{};
415 :
416 : std::string m_osFIDColumn{};
417 : int64_t m_nFeatureCount = 0;
418 :
419 : int64_t m_nRowGroupSize = 64 * 1024;
420 : arrow::Compression::type m_eCompression = arrow::Compression::UNCOMPRESSED;
421 :
422 : std::vector<std::shared_ptr<arrow::Field>> m_apoFieldsFromArrowSchema{};
423 : std::vector<std::shared_ptr<arrow::ArrayBuilder>> m_apoBuilders{};
424 :
425 : std::vector<uint8_t> m_abyBuffer{};
426 :
427 : std::vector<int> m_anTZFlag{}; // size: GetFieldCount()
428 : std::vector<OGREnvelope3D> m_aoEnvelopes{}; // size: GetGeomFieldCount()
429 : std::vector<std::set<OGRwkbGeometryType>>
430 : m_oSetWrittenGeometryTypes{}; // size: GetGeomFieldCount()
431 :
432 : static OGRArrowGeomEncoding
433 : GetPreciseArrowGeomEncoding(OGRArrowGeomEncoding eEncodingType,
434 : OGRwkbGeometryType eGType);
435 : static const char *
436 : GetGeomEncodingAsString(OGRArrowGeomEncoding eGeomEncoding,
437 : bool bForParquetGeo);
438 :
439 : virtual bool IsSupportedGeometryType(OGRwkbGeometryType eGType) const = 0;
440 :
441 : virtual std::string GetDriverUCName() const = 0;
442 :
443 : virtual bool IsFileWriterCreated() const = 0;
444 : virtual void CreateWriter() = 0;
445 : virtual bool CloseFileWriter() = 0;
446 :
447 : void CreateSchemaCommon();
448 : void FinalizeSchema();
449 : virtual void CreateSchema() = 0;
450 :
451 0 : virtual void PerformStepsBeforeFinalFlushGroup()
452 : {
453 0 : }
454 :
455 : void CreateArrayBuilders();
456 :
457 : //! Clear array builders
458 : void ClearArrayBuilers();
459 :
460 : virtual bool FlushGroup() = 0;
461 : bool FinalizeWriting();
462 : bool WriteArrays(std::function<bool(const std::shared_ptr<arrow::Field> &,
463 : const std::shared_ptr<arrow::Array> &)>
464 : postProcessArray);
465 :
466 128 : virtual void FixupWKBGeometryBeforeWriting(GByte * /*pabyWKB*/,
467 : size_t /*nLen*/)
468 : {
469 128 : }
470 :
471 0 : virtual void FixupGeometryBeforeWriting(OGRGeometry * /* poGeom */)
472 : {
473 0 : }
474 :
475 : virtual bool IsSRSRequired() const = 0;
476 : bool WriteArrowBatchInternal(
477 : const struct ArrowSchema *schema, struct ArrowArray *array,
478 : CSLConstList papszOptions,
479 : std::function<bool(const std::shared_ptr<arrow::RecordBatch> &)>
480 : writeBatch);
481 :
482 : OGRErr BuildGeometry(OGRGeometry *poGeom, int iGeomField,
483 : arrow::ArrayBuilder *poBuilder);
484 :
485 : public:
486 : OGRArrowWriterLayer(
487 : arrow::MemoryPool *poMemoryPool,
488 : const std::shared_ptr<arrow::io::OutputStream> &poOutputStream,
489 : const char *pszLayerName);
490 :
491 : ~OGRArrowWriterLayer() override;
492 :
493 : bool AddFieldDomain(std::unique_ptr<OGRFieldDomain> &&domain,
494 : std::string &failureReason);
495 : std::vector<std::string> GetFieldDomainNames() const;
496 : const OGRFieldDomain *GetFieldDomain(const std::string &name) const;
497 :
498 7 : const char *GetFIDColumn() override
499 : {
500 7 : return m_osFIDColumn.c_str();
501 : }
502 :
503 6056 : OGRFeatureDefn *GetLayerDefn() override
504 : {
505 6056 : return m_poFeatureDefn;
506 : }
507 :
508 23 : void ResetReading() override
509 : {
510 23 : }
511 :
512 23 : OGRFeature *GetNextFeature() override
513 : {
514 23 : return nullptr;
515 : }
516 :
517 : int TestCapability(const char *pszCap) override;
518 : OGRErr CreateField(const OGRFieldDefn *poField,
519 : int bApproxOK = TRUE) override;
520 : OGRErr CreateGeomField(const OGRGeomFieldDefn *poField,
521 : int bApproxOK = TRUE) override;
522 : GIntBig GetFeatureCount(int bForce) override;
523 :
524 112 : bool IsArrowSchemaSupported(const struct ArrowSchema * /*schema*/,
525 : CSLConstList /* papszOptions */,
526 : std::string & /*osErrorMsg */) const override
527 : {
528 112 : return true;
529 : }
530 :
531 : bool
532 : CreateFieldFromArrowSchema(const struct ArrowSchema *schema,
533 : CSLConstList papszOptions = nullptr) override;
534 : bool WriteArrowBatch(const struct ArrowSchema *schema,
535 : struct ArrowArray *array,
536 : CSLConstList papszOptions = nullptr) override = 0;
537 :
538 : protected:
539 : OGRErr ICreateFeature(OGRFeature *poFeature) override;
540 :
541 : bool FlushFeatures();
542 : };
543 :
544 : #endif // OGR_ARROW_H
|