Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: Arrow Database Connectivity driver
5 : * Author: Even Rouault, <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
9 : * Copyright (c) 2024, Dewey Dunnington <dewey@voltrondata.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "ogr_adbc.h"
15 : #include "ogr_spatialref.h"
16 : #include "ogr_p.h"
17 : #include "cpl_json.h"
18 :
19 : #include <cassert>
20 : #include <cmath>
21 : #include <limits>
22 : #include <map>
23 : #include <set>
24 :
25 : #define ADBC_CALL(func, ...) m_poDS->m_driver.func(__VA_ARGS__)
26 :
27 0 : OGRArrowArrayToOGRFeatureAdapterLayer::~OGRArrowArrayToOGRFeatureAdapterLayer()
28 : {
29 0 : m_poLayerDefn->Release();
30 0 : }
31 :
32 : /************************************************************************/
33 : /* GetGeometryTypeFromString() */
34 : /************************************************************************/
35 :
36 0 : static OGRwkbGeometryType GetGeometryTypeFromString(const std::string &osType)
37 : {
38 0 : OGRwkbGeometryType eGeomType = wkbUnknown;
39 0 : OGRReadWKTGeometryType(osType.c_str(), &eGeomType);
40 0 : if (eGeomType == wkbUnknown && !osType.empty())
41 : {
42 0 : CPLDebug("ADBC", "Unknown geometry type: %s", osType.c_str());
43 : }
44 0 : return eGeomType;
45 : }
46 :
47 : /************************************************************************/
48 : /* OGRADBCLayer() */
49 : /************************************************************************/
50 :
51 0 : OGRADBCLayer::OGRADBCLayer(OGRADBCDataset *poDS, const char *pszName,
52 0 : const std::string &osStatement, bool bInternalUse)
53 : : m_poDS(poDS), m_osBaseStatement(osStatement),
54 0 : m_osModifiedBaseStatement(m_osBaseStatement), m_bInternalUse(bInternalUse)
55 : {
56 0 : SetDescription(pszName);
57 :
58 0 : memset(&m_schema, 0, sizeof(m_schema));
59 0 : }
60 :
61 : /************************************************************************/
62 : /* OGRADBCLayer() */
63 : /************************************************************************/
64 :
65 0 : OGRADBCLayer::OGRADBCLayer(OGRADBCDataset *poDS, const char *pszName,
66 : std::unique_ptr<OGRArrowArrayStream> poStream,
67 0 : ArrowSchema *schema, bool bInternalUse)
68 0 : : m_poDS(poDS), m_stream(std::move(poStream)), m_bInternalUse(bInternalUse)
69 : {
70 0 : SetDescription(pszName);
71 :
72 0 : memcpy(&m_schema, schema, sizeof(m_schema));
73 0 : schema->release = nullptr;
74 0 : }
75 :
76 : /************************************************************************/
77 : /* GetLayerDefn() */
78 : /************************************************************************/
79 :
80 0 : const OGRFeatureDefn *OGRADBCLayer::GetLayerDefn() const
81 : {
82 0 : if (!m_poAdapterLayer)
83 0 : const_cast<OGRADBCLayer *>(this)->BuildLayerDefn();
84 0 : assert(m_poAdapterLayer);
85 0 : return m_poAdapterLayer->GetLayerDefn();
86 : }
87 :
88 : /************************************************************************/
89 : /* GetFIDColumn() */
90 : /************************************************************************/
91 :
92 0 : const char *OGRADBCLayer::GetFIDColumn() const
93 : {
94 0 : if (!m_poAdapterLayer)
95 0 : const_cast<OGRADBCLayer *>(this)->BuildLayerDefn();
96 0 : return m_osFIDColName.c_str();
97 : }
98 :
99 : /************************************************************************/
100 : /* GotError() */
101 : /************************************************************************/
102 :
103 0 : bool OGRADBCLayer::GotError()
104 : {
105 0 : if (!m_poAdapterLayer)
106 0 : BuildLayerDefn();
107 0 : return m_bLayerDefinitionError;
108 : }
109 :
110 : /************************************************************************/
111 : /* ParseGeometryColumnCovering() */
112 : /************************************************************************/
113 :
114 : //! Parse bounding box column definition
115 0 : static bool ParseGeometryColumnCovering(const CPLJSONObject &oJSONDef,
116 : std::string &osBBOXColumn,
117 : std::string &osXMin,
118 : std::string &osYMin,
119 : std::string &osXMax,
120 : std::string &osYMax)
121 : {
122 0 : const auto oCovering = oJSONDef["covering"];
123 0 : if (oCovering.IsValid() &&
124 0 : oCovering.GetType() == CPLJSONObject::Type::Object)
125 : {
126 0 : const auto oBBOX = oCovering["bbox"];
127 0 : if (oBBOX.IsValid() && oBBOX.GetType() == CPLJSONObject::Type::Object)
128 : {
129 0 : const auto oXMin = oBBOX["xmin"];
130 0 : const auto oYMin = oBBOX["ymin"];
131 0 : const auto oXMax = oBBOX["xmax"];
132 0 : const auto oYMax = oBBOX["ymax"];
133 0 : if (oXMin.IsValid() && oYMin.IsValid() && oXMax.IsValid() &&
134 0 : oYMax.IsValid() &&
135 0 : oXMin.GetType() == CPLJSONObject::Type::Array &&
136 0 : oYMin.GetType() == CPLJSONObject::Type::Array &&
137 0 : oXMax.GetType() == CPLJSONObject::Type::Array &&
138 0 : oYMax.GetType() == CPLJSONObject::Type::Array)
139 : {
140 0 : const auto osXMinArray = oXMin.ToArray();
141 0 : const auto osYMinArray = oYMin.ToArray();
142 0 : const auto osXMaxArray = oXMax.ToArray();
143 0 : const auto osYMaxArray = oYMax.ToArray();
144 0 : if (osXMinArray.Size() == 2 && osYMinArray.Size() == 2 &&
145 0 : osXMaxArray.Size() == 2 && osYMaxArray.Size() == 2 &&
146 0 : osXMinArray[0].GetType() == CPLJSONObject::Type::String &&
147 0 : osXMinArray[1].GetType() == CPLJSONObject::Type::String &&
148 0 : osYMinArray[0].GetType() == CPLJSONObject::Type::String &&
149 0 : osYMinArray[1].GetType() == CPLJSONObject::Type::String &&
150 0 : osXMaxArray[0].GetType() == CPLJSONObject::Type::String &&
151 0 : osXMaxArray[1].GetType() == CPLJSONObject::Type::String &&
152 0 : osYMaxArray[0].GetType() == CPLJSONObject::Type::String &&
153 0 : osYMaxArray[1].GetType() == CPLJSONObject::Type::String &&
154 0 : osXMinArray[0].ToString() == osYMinArray[0].ToString() &&
155 0 : osXMinArray[0].ToString() == osXMaxArray[0].ToString() &&
156 0 : osXMinArray[0].ToString() == osYMaxArray[0].ToString())
157 : {
158 0 : osBBOXColumn = osXMinArray[0].ToString();
159 0 : osXMin = osXMinArray[1].ToString();
160 0 : osYMin = osYMinArray[1].ToString();
161 0 : osXMax = osXMaxArray[1].ToString();
162 0 : osYMax = osYMaxArray[1].ToString();
163 0 : return true;
164 : }
165 : }
166 : }
167 : }
168 0 : return false;
169 : }
170 :
171 : /************************************************************************/
172 : /* ParseGeoParquetColumn() */
173 : /************************************************************************/
174 :
175 0 : static void ParseGeoParquetColumn(
176 : const CPLJSONObject &oColumn,
177 : std::map<std::string, OGRwkbGeometryType> &oMapType,
178 : std::map<std::string, OGREnvelope3D> &oMapExtent,
179 : std::map<std::string, OGRADBCLayer::GeomColBBOX>
180 : &oMapGeomColumnToCoveringBBOXColumn,
181 : std::map<std::string, std::unique_ptr<OGRSpatialReference>>
182 : &oMapGeomColumnsFromGeoParquet,
183 : std::set<std::string> &oSetCoveringBBoxColumn)
184 : {
185 0 : auto oCrs = oColumn.GetObj("crs");
186 0 : if (!oCrs.IsValid())
187 : {
188 : // WGS 84 is implied if no crs member is found.
189 0 : auto poSRS = std::make_unique<OGRSpatialReference>();
190 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
191 0 : poSRS->importFromEPSG(4326);
192 0 : oMapGeomColumnsFromGeoParquet[oColumn.GetName()] = std::move(poSRS);
193 : }
194 0 : else if (oCrs.GetType() == CPLJSONObject::Type::Object)
195 : {
196 : // CRS encoded as PROJJSON (extension)
197 0 : const auto oType = oCrs["type"];
198 0 : if (oType.IsValid() && oType.GetType() == CPLJSONObject::Type::String)
199 : {
200 0 : const auto osType = oType.ToString();
201 0 : if (osType.find("CRS") != std::string::npos)
202 : {
203 0 : auto poSRS = std::make_unique<OGRSpatialReference>();
204 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
205 :
206 0 : if (poSRS->SetFromUserInput(oCrs.ToString().c_str()) ==
207 : OGRERR_NONE)
208 : {
209 0 : oMapGeomColumnsFromGeoParquet[oColumn.GetName()] =
210 0 : std::move(poSRS);
211 : }
212 : }
213 : }
214 : }
215 : else
216 : {
217 0 : oMapGeomColumnsFromGeoParquet[oColumn.GetName()] = nullptr;
218 : }
219 :
220 0 : OGRwkbGeometryType eGeomType = wkbUnknown;
221 0 : auto oType = oColumn.GetObj("geometry_types");
222 0 : if (oType.GetType() == CPLJSONObject::Type::Array)
223 : {
224 0 : const auto oTypeArray = oType.ToArray();
225 0 : if (oTypeArray.Size() == 1)
226 : {
227 0 : eGeomType = GetGeometryTypeFromString(oTypeArray[0].ToString());
228 : }
229 0 : else if (oTypeArray.Size() > 1)
230 : {
231 0 : const auto PromoteToCollection = [](OGRwkbGeometryType eType)
232 : {
233 0 : if (eType == wkbPoint)
234 0 : return wkbMultiPoint;
235 0 : if (eType == wkbLineString)
236 0 : return wkbMultiLineString;
237 0 : if (eType == wkbPolygon)
238 0 : return wkbMultiPolygon;
239 0 : return eType;
240 : };
241 0 : bool bMixed = false;
242 0 : bool bHasMulti = false;
243 0 : bool bHasZ = false;
244 0 : bool bHasM = false;
245 0 : const auto eFirstType = OGR_GT_Flatten(
246 0 : GetGeometryTypeFromString(oTypeArray[0].ToString()));
247 0 : const auto eFirstTypeCollection = PromoteToCollection(eFirstType);
248 0 : for (int i = 0; i < oTypeArray.Size(); ++i)
249 : {
250 : const auto eThisGeom =
251 0 : GetGeometryTypeFromString(oTypeArray[i].ToString());
252 0 : if (PromoteToCollection(OGR_GT_Flatten(eThisGeom)) !=
253 : eFirstTypeCollection)
254 : {
255 0 : bMixed = true;
256 0 : break;
257 : }
258 0 : bHasZ |= OGR_GT_HasZ(eThisGeom) != FALSE;
259 0 : bHasM |= OGR_GT_HasM(eThisGeom) != FALSE;
260 0 : bHasMulti |= (PromoteToCollection(OGR_GT_Flatten(eThisGeom)) ==
261 0 : OGR_GT_Flatten(eThisGeom));
262 : }
263 0 : if (!bMixed)
264 : {
265 0 : if (eFirstTypeCollection == wkbMultiPolygon ||
266 : eFirstTypeCollection == wkbMultiLineString)
267 : {
268 0 : if (bHasMulti)
269 0 : eGeomType = OGR_GT_SetModifier(eFirstTypeCollection,
270 : bHasZ, bHasM);
271 : else
272 : eGeomType =
273 0 : OGR_GT_SetModifier(eFirstType, bHasZ, bHasM);
274 : }
275 : }
276 : }
277 : }
278 :
279 0 : oMapType[oColumn.GetName()] = eGeomType;
280 :
281 0 : OGREnvelope3D sExtent;
282 0 : const auto oBBox = oColumn.GetArray("bbox");
283 0 : if (oBBox.IsValid() && oBBox.Size() == 4)
284 : {
285 0 : sExtent.MinX = oBBox[0].ToDouble();
286 0 : sExtent.MinY = oBBox[1].ToDouble();
287 0 : sExtent.MinZ = std::numeric_limits<double>::infinity();
288 0 : sExtent.MaxX = oBBox[2].ToDouble();
289 0 : sExtent.MaxY = oBBox[3].ToDouble();
290 0 : sExtent.MaxZ = -std::numeric_limits<double>::infinity();
291 0 : if (sExtent.MinX <= sExtent.MaxX)
292 : {
293 0 : oMapExtent[oColumn.GetName()] = sExtent;
294 : }
295 : }
296 0 : else if (oBBox.IsValid() && oBBox.Size() == 6)
297 : {
298 0 : sExtent.MinX = oBBox[0].ToDouble();
299 0 : sExtent.MinY = oBBox[1].ToDouble();
300 0 : sExtent.MinZ = oBBox[2].ToDouble();
301 0 : sExtent.MaxX = oBBox[3].ToDouble();
302 0 : sExtent.MaxY = oBBox[4].ToDouble();
303 0 : sExtent.MaxZ = oBBox[5].ToDouble();
304 0 : if (sExtent.MinX <= sExtent.MaxX)
305 : {
306 0 : oMapExtent[oColumn.GetName()] = sExtent;
307 : }
308 : }
309 :
310 0 : std::string osBBOXColumn;
311 0 : std::string osXMin, osYMin, osXMax, osYMax;
312 0 : if (ParseGeometryColumnCovering(oColumn, osBBOXColumn, osXMin, osYMin,
313 : osXMax, osYMax))
314 : {
315 0 : OGRADBCLayer::GeomColBBOX geomColBBOX;
316 : const std::string osPrefix =
317 0 : std::string("\"")
318 0 : .append(OGRDuplicateCharacter(osBBOXColumn, '"'))
319 0 : .append("\".\"");
320 :
321 0 : const auto BuildColName = [&osPrefix](const std::string &s)
322 : {
323 0 : return std::string(osPrefix)
324 0 : .append(OGRDuplicateCharacter(s, '"'))
325 0 : .append("\"");
326 0 : };
327 :
328 0 : geomColBBOX.osXMin = BuildColName(osXMin);
329 0 : geomColBBOX.osYMin = BuildColName(osYMin);
330 0 : geomColBBOX.osXMax = BuildColName(osXMax);
331 0 : geomColBBOX.osYMax = BuildColName(osYMax);
332 0 : oMapGeomColumnToCoveringBBOXColumn[oColumn.GetName()] =
333 0 : std::move(geomColBBOX);
334 0 : oSetCoveringBBoxColumn.insert(std::move(osBBOXColumn));
335 : }
336 0 : }
337 :
338 : /************************************************************************/
339 : /* BuildLayerDefnInit() */
340 : /************************************************************************/
341 :
342 0 : bool OGRADBCLayer::BuildLayerDefnInit(bool bCreateStream)
343 : {
344 0 : CPLAssert(!m_poAdapterLayer);
345 :
346 0 : m_bLayerDefinitionError = true;
347 0 : m_poAdapterLayer = std::make_unique<OGRArrowArrayToOGRFeatureAdapterLayer>(
348 0 : GetDescription());
349 :
350 0 : m_statement = std::make_unique<AdbcStatement>();
351 0 : if (!m_osBaseStatement.empty())
352 : {
353 0 : OGRADBCError error;
354 0 : if (ADBC_CALL(StatementNew, m_poDS->m_connection.get(),
355 0 : m_statement.get(), error) != ADBC_STATUS_OK)
356 : {
357 0 : CPLError(CE_Failure, CPLE_AppDefined,
358 : "AdbcStatementNew() failed: %s", error.message());
359 0 : return false;
360 : }
361 :
362 0 : if (ADBC_CALL(StatementSetSqlQuery, m_statement.get(),
363 0 : m_osBaseStatement.c_str(), error) != ADBC_STATUS_OK)
364 : {
365 0 : CPLError(CE_Failure, CPLE_AppDefined,
366 : "AdbcStatementSetSqlQuery() failed: %s", error.message());
367 0 : return false;
368 : }
369 : }
370 :
371 0 : if (!m_stream && bCreateStream)
372 : {
373 0 : OGRADBCError error;
374 0 : m_stream = std::make_unique<OGRArrowArrayStream>();
375 0 : int64_t rows_affected = -1;
376 0 : if (ADBC_CALL(StatementExecuteQuery, m_statement.get(), m_stream->get(),
377 0 : &rows_affected, error) != ADBC_STATUS_OK)
378 : {
379 0 : CPLError(CE_Failure, CPLE_AppDefined,
380 : "AdbcStatementExecuteQuery() failed: %s", error.message());
381 0 : return false;
382 : }
383 :
384 0 : if (m_stream->get_schema(&m_schema) != 0)
385 : {
386 0 : CPLError(CE_Failure, CPLE_AppDefined, "get_schema() failed");
387 0 : return false;
388 : }
389 : }
390 :
391 0 : m_bLayerDefinitionError = false;
392 0 : return true;
393 : }
394 :
395 : /************************************************************************/
396 : /* BuildLayerDefn() */
397 : /************************************************************************/
398 :
399 0 : void OGRADBCLayer::BuildLayerDefn()
400 : {
401 : const bool bIsUserLayerDuckDB =
402 0 : !m_bInternalUse &&
403 0 : STARTS_WITH_CI(m_osBaseStatement.c_str(), "SELECT ") &&
404 0 : m_poDS->m_bIsDuckDBDriver;
405 0 : if (!BuildLayerDefnInit(!bIsUserLayerDuckDB))
406 0 : return;
407 :
408 : // Identify geometry columns for Parquet files, and query them with
409 : // ST_AsWKB() to avoid getting duckdb_spatial own geometry encoding
410 : // (https://github.com/duckdb/duckdb_spatial/blob/a60aa3733741a99c49baaf33390c0f7c8a9598a3/spatial/src/spatial/core/geometry/geometry_serialization.cpp#L11)
411 0 : std::map<std::string, std::unique_ptr<OGRSpatialReference>> oMapGeomColumns;
412 0 : std::map<std::string, OGRwkbGeometryType> oMapType;
413 0 : std::map<std::string, OGREnvelope3D> oMapExtent;
414 0 : std::map<std::string, GeomColBBOX> oMapGeomColumnToCoveringBBOXColumn;
415 :
416 0 : if (bIsUserLayerDuckDB)
417 : {
418 : // Try to read GeoParquet 'geo' metadata
419 : std::map<std::string, std::unique_ptr<OGRSpatialReference>>
420 0 : oMapGeomColumnsFromGeoParquet;
421 0 : std::set<std::string> oSetCoveringBBoxColumn;
422 :
423 0 : std::string osGeoParquetMD;
424 0 : if (!m_poDS->m_osParquetFilename.empty())
425 : {
426 0 : auto poMetadataLayer = m_poDS->CreateInternalLayer(
427 0 : std::string("SELECT value FROM parquet_kv_metadata('")
428 0 : .append(OGRDuplicateCharacter(m_poDS->m_osParquetFilename,
429 0 : '\''))
430 0 : .append("') WHERE key = 'geo'")
431 0 : .c_str());
432 0 : if (!poMetadataLayer->GotError())
433 : {
434 : auto f = std::unique_ptr<OGRFeature>(
435 0 : poMetadataLayer->GetNextFeature());
436 0 : if (f)
437 : {
438 0 : int nBytes = 0;
439 0 : const GByte *pabyData = f->GetFieldAsBinary(0, &nBytes);
440 : osGeoParquetMD.assign(
441 0 : reinterpret_cast<const char *>(pabyData), nBytes);
442 : // CPLDebug("ADBC", "%s", osGeoParquetMD.c_str());
443 : }
444 : }
445 : }
446 0 : CPLJSONDocument oDoc;
447 0 : if (!osGeoParquetMD.empty() && oDoc.LoadMemory(osGeoParquetMD))
448 : {
449 0 : const auto oColumns = oDoc.GetRoot().GetObj("columns");
450 0 : for (const auto &oColumn : oColumns.GetChildren())
451 : {
452 0 : if (oColumn.GetString("encoding") == "WKB")
453 : {
454 0 : ParseGeoParquetColumn(oColumn, oMapType, oMapExtent,
455 : oMapGeomColumnToCoveringBBOXColumn,
456 : oMapGeomColumnsFromGeoParquet,
457 : oSetCoveringBBoxColumn);
458 : }
459 : }
460 : }
461 :
462 0 : auto poDescribeLayer = m_poDS->CreateInternalLayer(
463 0 : std::string("DESCRIBE ").append(m_osBaseStatement).c_str());
464 0 : std::string osNewStatement;
465 0 : bool bNewStatement = false;
466 0 : if (!poDescribeLayer->GotError() &&
467 0 : (m_poDS->m_bIsDuckDBDriver ||
468 : // cppcheck-suppress knownConditionTrueFalse
469 0 : !oMapGeomColumnsFromGeoParquet.empty()))
470 : {
471 0 : for (auto &&f : *poDescribeLayer)
472 : {
473 0 : const char *pszColName = f->GetFieldAsString("column_name");
474 0 : if (cpl::contains(oSetCoveringBBoxColumn, pszColName))
475 : {
476 0 : bNewStatement = true;
477 0 : continue;
478 : }
479 :
480 : // f->DumpReadable(stdout);
481 0 : if (osNewStatement.empty())
482 0 : osNewStatement = "SELECT ";
483 : else
484 0 : osNewStatement += ", ";
485 :
486 0 : auto oIter = oMapGeomColumnsFromGeoParquet.find(pszColName);
487 0 : if (oIter != oMapGeomColumnsFromGeoParquet.end())
488 : {
489 0 : oMapGeomColumns[pszColName] = std::move(oIter->second);
490 : }
491 0 : const char *pszColType = f->GetFieldAsString("column_type");
492 0 : if (STARTS_WITH_CI(pszColType, "GEOMETRY") &&
493 0 : m_poDS->m_bSpatialLoaded)
494 : {
495 0 : bNewStatement = true;
496 0 : osNewStatement += "ST_AsWKB(\"";
497 0 : osNewStatement += OGRDuplicateCharacter(pszColName, '"');
498 0 : osNewStatement += "\") AS ";
499 0 : if (oIter == oMapGeomColumnsFromGeoParquet.end())
500 : {
501 0 : oMapGeomColumns[pszColName] = nullptr;
502 : // Below is with DuckDB >= 1.5
503 0 : if (STARTS_WITH_CI(pszColType, "GEOMETRY('EPSG:"))
504 : {
505 : auto poSRS =
506 0 : std::make_unique<OGRSpatialReference>();
507 0 : poSRS->SetAxisMappingStrategy(
508 : OAMS_TRADITIONAL_GIS_ORDER);
509 0 : if (poSRS->importFromEPSG(atoi(
510 0 : pszColType + strlen("GEOMETRY('EPSG:"))) ==
511 : OGRERR_NONE)
512 : {
513 0 : oMapGeomColumns[pszColName] = std::move(poSRS);
514 : }
515 : }
516 : }
517 : }
518 0 : osNewStatement += '"';
519 0 : osNewStatement += OGRDuplicateCharacter(pszColName, '"');
520 0 : osNewStatement += '"';
521 : }
522 0 : m_osModifiedSelect = osNewStatement;
523 0 : osNewStatement += " FROM (";
524 0 : osNewStatement += m_osBaseStatement;
525 0 : osNewStatement += " )";
526 : }
527 :
528 0 : if (bNewStatement)
529 : {
530 : // CPLDebug("ADBC", "%s -> %s", m_osBaseStatement.c_str(), osNewStatement.c_str());
531 0 : if (ReplaceStatement(osNewStatement.c_str()))
532 : {
533 0 : m_osModifiedBaseStatement = std::move(osNewStatement);
534 : }
535 : else
536 : {
537 0 : m_osModifiedSelect.clear();
538 0 : oMapGeomColumns.clear();
539 : }
540 : }
541 :
542 0 : if (!m_stream)
543 : {
544 0 : auto stream = std::make_unique<OGRArrowArrayStream>();
545 0 : if (!GetArrowStreamInternal(stream->get()))
546 : {
547 0 : return;
548 : }
549 0 : m_stream = std::move(stream);
550 0 : if (m_stream->get_schema(&m_schema) != 0)
551 : {
552 0 : CPLError(CE_Failure, CPLE_AppDefined, "get_schema() failed");
553 0 : return;
554 : }
555 : }
556 : }
557 :
558 0 : for (int i = 0; i < m_schema.n_children; ++i)
559 : {
560 0 : const char *pszColName = m_schema.children[i]->name;
561 0 : auto oIter = oMapGeomColumns.find(pszColName);
562 0 : if (oIter != oMapGeomColumns.end())
563 : {
564 0 : OGRGeomFieldDefn oGeomFieldDefn(pszColName, oMapType[pszColName]);
565 0 : auto poSRS = std::move(oIter->second).release();
566 0 : if (poSRS)
567 : {
568 0 : oGeomFieldDefn.SetSpatialRef(poSRS);
569 0 : poSRS->Release();
570 : }
571 0 : m_poAdapterLayer->m_poLayerDefn->AddGeomFieldDefn(&oGeomFieldDefn);
572 :
573 0 : m_extents.push_back(oMapExtent[pszColName]);
574 0 : m_geomColBBOX.push_back(
575 0 : oMapGeomColumnToCoveringBBOXColumn[pszColName]);
576 : }
577 : else
578 : {
579 0 : m_poAdapterLayer->CreateFieldFromArrowSchema(m_schema.children[i]);
580 : }
581 : }
582 : }
583 :
584 : /************************************************************************/
585 : /* ~OGRADBCLayer() */
586 : /************************************************************************/
587 :
588 0 : OGRADBCLayer::~OGRADBCLayer()
589 : {
590 0 : OGRADBCError error;
591 0 : if (m_statement)
592 0 : ADBC_CALL(StatementRelease, m_statement.get(), error);
593 0 : if (m_schema.release)
594 0 : m_schema.release(&m_schema);
595 0 : }
596 :
597 : /************************************************************************/
598 : /* ReplaceStatement() */
599 : /************************************************************************/
600 :
601 0 : bool OGRADBCLayer::ReplaceStatement(const char *pszNewStatement)
602 : {
603 : // CPLDebug("ADBC", "%s", pszNewStatement);
604 0 : OGRADBCError error;
605 0 : auto statement = std::make_unique<AdbcStatement>();
606 0 : if (ADBC_CALL(StatementNew, m_poDS->m_connection.get(), statement.get(),
607 0 : error) != ADBC_STATUS_OK)
608 : {
609 0 : CPLError(CE_Failure, CPLE_AppDefined, "AdbcStatementNew() failed: %s",
610 : error.message());
611 0 : ADBC_CALL(StatementRelease, statement.get(), error);
612 : }
613 0 : else if (ADBC_CALL(StatementSetSqlQuery, statement.get(), pszNewStatement,
614 0 : error) != ADBC_STATUS_OK)
615 : {
616 0 : CPLError(CE_Failure, CPLE_AppDefined,
617 : "AdbcStatementSetSqlQuery() failed: %s", error.message());
618 0 : error.clear();
619 0 : ADBC_CALL(StatementRelease, statement.get(), error);
620 : }
621 : else
622 : {
623 0 : auto stream = std::make_unique<OGRArrowArrayStream>();
624 0 : int64_t rows_affected = -1;
625 : ArrowSchema newSchema;
626 0 : memset(&newSchema, 0, sizeof(newSchema));
627 0 : if (ADBC_CALL(StatementExecuteQuery, statement.get(), stream->get(),
628 0 : &rows_affected, error) != ADBC_STATUS_OK)
629 : {
630 0 : CPLError(CE_Failure, CPLE_AppDefined,
631 : "AdbcStatementExecuteQuery() failed: %s", error.message());
632 0 : error.clear();
633 0 : ADBC_CALL(StatementRelease, statement.get(), error);
634 : }
635 0 : else if (stream->get_schema(&newSchema) != 0)
636 : {
637 0 : CPLError(CE_Failure, CPLE_AppDefined, "get_schema() failed");
638 0 : ADBC_CALL(StatementRelease, statement.get(), error);
639 : }
640 : else
641 : {
642 0 : if (m_schema.release)
643 0 : m_schema.release(&m_schema);
644 0 : memcpy(&m_schema, &newSchema, sizeof(newSchema));
645 :
646 0 : if (m_statement)
647 0 : ADBC_CALL(StatementRelease, m_statement.get(), error);
648 0 : m_statement = std::move(statement);
649 :
650 0 : m_stream = std::move(stream);
651 :
652 0 : return true;
653 : }
654 : }
655 0 : return false;
656 : }
657 :
658 : /************************************************************************/
659 : /* GetNextRawFeature() */
660 : /************************************************************************/
661 :
662 0 : OGRFeature *OGRADBCLayer::GetNextRawFeature()
663 : {
664 0 : if (!m_poAdapterLayer)
665 0 : BuildLayerDefn();
666 0 : RunDeferredCreation();
667 :
668 0 : if (m_bEOF || m_bLayerDefinitionError)
669 0 : return nullptr;
670 :
671 0 : if (m_nIdx == m_poAdapterLayer->m_apoFeatures.size())
672 : {
673 0 : m_nIdx = 0;
674 0 : m_poAdapterLayer->m_apoFeatures.clear();
675 :
676 : struct ArrowArray array;
677 0 : for (int i = 0; i < 2; ++i)
678 : {
679 0 : if (!m_stream)
680 : {
681 0 : auto stream = std::make_unique<OGRArrowArrayStream>();
682 0 : if (!GetArrowStreamInternal(stream->get()))
683 : {
684 0 : m_bEOF = true;
685 0 : return nullptr;
686 : }
687 0 : m_stream = std::move(stream);
688 : }
689 :
690 0 : memset(&array, 0, sizeof(array));
691 0 : if (m_stream->get_next(&array) != 0)
692 : {
693 0 : if (m_bNextStreamUsageMaybeInvalid)
694 : {
695 0 : m_bNextStreamUsageMaybeInvalid = false;
696 0 : m_stream.reset();
697 : }
698 : else
699 : {
700 0 : m_bEOF = true;
701 0 : return nullptr;
702 : }
703 : }
704 : else
705 : {
706 0 : break;
707 : }
708 : }
709 0 : m_bGetNextArrayHasRun = true;
710 : const bool bOK =
711 0 : array.length
712 0 : ? m_poAdapterLayer->WriteArrowBatch(&m_schema, &array, nullptr)
713 0 : : false;
714 0 : if (array.release)
715 0 : array.release(&array);
716 0 : if (!bOK)
717 : {
718 0 : m_bEOF = true;
719 0 : return nullptr;
720 : }
721 : }
722 :
723 0 : auto poFeature = m_poAdapterLayer->m_apoFeatures[m_nIdx++].release();
724 : const int nGeomFieldCount =
725 0 : m_poAdapterLayer->m_poLayerDefn->GetFieldCount();
726 0 : for (int i = 0; i < nGeomFieldCount; ++i)
727 : {
728 0 : auto poGeom = poFeature->GetGeomFieldRef(i);
729 0 : if (poGeom)
730 0 : poGeom->assignSpatialReference(
731 0 : m_poAdapterLayer->m_poLayerDefn->GetGeomFieldDefn(i)
732 0 : ->GetSpatialRef());
733 : }
734 0 : if (m_osFIDColName.empty())
735 0 : poFeature->SetFID(m_nFeatureID++);
736 : else
737 0 : poFeature->SetFID(
738 0 : poFeature->GetFieldAsInteger64(m_osFIDColName.c_str()));
739 0 : return poFeature;
740 : }
741 :
742 : /************************************************************************/
743 : /* ResetReading() */
744 : /************************************************************************/
745 :
746 0 : void OGRADBCLayer::ResetReading()
747 : {
748 0 : if (m_nIdx > 0 || m_bEOF)
749 : {
750 0 : m_poAdapterLayer->m_apoFeatures.clear();
751 0 : m_stream.reset();
752 0 : m_bEOF = false;
753 0 : m_nIdx = 0;
754 0 : m_nFeatureID = 0;
755 0 : m_bGetNextArrayHasRun = false;
756 : }
757 0 : }
758 :
759 : /************************************************************************/
760 : /* IGetExtent() */
761 : /************************************************************************/
762 :
763 0 : OGRErr OGRADBCLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
764 : bool bForce)
765 : {
766 0 : if (!m_poAdapterLayer)
767 0 : BuildLayerDefn();
768 :
769 0 : *psExtent = m_extents[iGeomField];
770 0 : if (psExtent->IsInit())
771 0 : return OGRERR_NONE;
772 :
773 0 : return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
774 : }
775 :
776 : /************************************************************************/
777 : /* IGetExtent3D() */
778 : /************************************************************************/
779 :
780 0 : OGRErr OGRADBCLayer::IGetExtent3D(int iGeomField, OGREnvelope3D *psExtent,
781 : bool bForce)
782 : {
783 0 : if (!m_poAdapterLayer)
784 0 : BuildLayerDefn();
785 :
786 0 : *psExtent = m_extents[iGeomField];
787 0 : if (psExtent->IsInit())
788 0 : return OGRERR_NONE;
789 :
790 0 : return OGRLayer::IGetExtent3D(iGeomField, psExtent, bForce);
791 : }
792 :
793 : /************************************************************************/
794 : /* GetCurrentStatement() */
795 : /************************************************************************/
796 :
797 0 : std::string OGRADBCLayer::GetCurrentStatement() const
798 : {
799 0 : if (m_poDS->m_bIsDuckDBDriver && !m_osModifiedSelect.empty() &&
800 0 : STARTS_WITH_CI(m_osBaseStatement.c_str(), "SELECT * FROM ") &&
801 0 : (!m_osAttributeFilter.empty() ||
802 0 : (m_poFilterGeom &&
803 0 : (!m_geomColBBOX[m_iGeomFieldFilter].osXMin.empty() ||
804 0 : m_poDS->m_bSpatialLoaded))))
805 : {
806 0 : std::string osStatement(m_osModifiedSelect);
807 0 : osStatement.append(" FROM (")
808 0 : .append(m_osBaseStatement)
809 0 : .append(") WHERE ");
810 :
811 0 : bool bAddAnd = false;
812 0 : if (m_poFilterGeom)
813 : {
814 0 : const double dfMinX = std::isinf(m_sFilterEnvelope.MinX)
815 0 : ? -std::numeric_limits<double>::max()
816 0 : : m_sFilterEnvelope.MinX;
817 0 : const double dfMinY = std::isinf(m_sFilterEnvelope.MinY)
818 0 : ? -std::numeric_limits<double>::max()
819 0 : : m_sFilterEnvelope.MinY;
820 0 : const double dfMaxX = std::isinf(m_sFilterEnvelope.MaxX)
821 0 : ? std::numeric_limits<double>::max()
822 0 : : m_sFilterEnvelope.MaxX;
823 0 : const double dfMaxY = std::isinf(m_sFilterEnvelope.MaxY)
824 0 : ? std::numeric_limits<double>::max()
825 0 : : m_sFilterEnvelope.MaxY;
826 0 : if (!m_geomColBBOX[m_iGeomFieldFilter].osXMin.empty())
827 : {
828 0 : bAddAnd = true;
829 0 : osStatement.append(m_geomColBBOX[m_iGeomFieldFilter].osXMin)
830 0 : .append(" <= ")
831 0 : .append(CPLSPrintf("%.17g", dfMaxX))
832 0 : .append(" AND ")
833 0 : .append(m_geomColBBOX[m_iGeomFieldFilter].osXMax)
834 0 : .append(" >= ")
835 0 : .append(CPLSPrintf("%.17g", dfMinX))
836 0 : .append(" AND ")
837 0 : .append(m_geomColBBOX[m_iGeomFieldFilter].osYMin)
838 0 : .append(" <= ")
839 0 : .append(CPLSPrintf("%.17g", dfMaxY))
840 0 : .append(" AND ")
841 0 : .append(m_geomColBBOX[m_iGeomFieldFilter].osYMax)
842 0 : .append(" >= ")
843 0 : .append(CPLSPrintf("%.17g", dfMinY));
844 : }
845 0 : if (m_poDS->m_bSpatialLoaded)
846 : {
847 0 : if (bAddAnd)
848 0 : osStatement.append(" AND ");
849 0 : bAddAnd = true;
850 0 : osStatement.append("ST_Intersects(\"")
851 0 : .append(OGRDuplicateCharacter(
852 0 : m_poAdapterLayer->m_poLayerDefn
853 0 : ->GetGeomFieldDefn(m_iGeomFieldFilter)
854 : ->GetNameRef(),
855 0 : '"'))
856 : .append(CPLSPrintf(
857 : "\", ST_MakeEnvelope(%.17g,%.17g,%.17g,%.17g))", dfMinX,
858 0 : dfMinY, dfMaxX, dfMaxY));
859 : }
860 : }
861 0 : if (!m_osAttributeFilter.empty())
862 : {
863 0 : if (bAddAnd)
864 0 : osStatement.append(" AND ");
865 0 : osStatement.append("(");
866 0 : osStatement.append(m_osAttributeFilter);
867 0 : osStatement.append(")");
868 : }
869 :
870 0 : return osStatement;
871 : }
872 : else
873 : {
874 0 : return m_osModifiedBaseStatement;
875 : }
876 : }
877 :
878 : /************************************************************************/
879 : /* UpdateStatement() */
880 : /************************************************************************/
881 :
882 0 : bool OGRADBCLayer::UpdateStatement()
883 : {
884 0 : return ReplaceStatement(GetCurrentStatement().c_str());
885 : }
886 :
887 : /************************************************************************/
888 : /* SetAttributeFilter() */
889 : /************************************************************************/
890 :
891 0 : OGRErr OGRADBCLayer::SetAttributeFilter(const char *pszFilter)
892 : {
893 0 : if (!m_osModifiedSelect.empty() && m_poDS->m_bIsDuckDBDriver &&
894 0 : STARTS_WITH_CI(m_osBaseStatement.c_str(), "SELECT * FROM "))
895 : {
896 0 : m_osAttributeFilter = pszFilter ? pszFilter : "";
897 0 : return UpdateStatement() ? OGRERR_NONE : OGRERR_FAILURE;
898 : }
899 : else
900 : {
901 0 : return OGRLayer::SetAttributeFilter(pszFilter);
902 : }
903 : }
904 :
905 : /************************************************************************/
906 : /* ISetSpatialFilter() */
907 : /************************************************************************/
908 :
909 0 : OGRErr OGRADBCLayer::ISetSpatialFilter(int iGeomField,
910 : const OGRGeometry *poGeomIn)
911 :
912 : {
913 0 : if (iGeomField < GetLayerDefn()->GetGeomFieldCount())
914 : {
915 0 : m_iGeomFieldFilter = iGeomField;
916 0 : if (InstallFilter(poGeomIn))
917 0 : ResetReading();
918 0 : UpdateStatement();
919 : }
920 0 : return OGRERR_NONE;
921 : }
922 :
923 : /************************************************************************/
924 : /* TestCapability() */
925 : /************************************************************************/
926 :
927 0 : int OGRADBCLayer::TestCapability(const char *pszCap) const
928 : {
929 0 : if (!m_poAdapterLayer)
930 0 : const_cast<OGRADBCLayer *>(this)->BuildLayerDefn();
931 :
932 0 : if (EQUAL(pszCap, OLCFastGetArrowStream))
933 : {
934 0 : return !m_poFilterGeom && !m_poAttrQuery && m_osAttributeFilter.empty();
935 : }
936 0 : else if (EQUAL(pszCap, OLCFastFeatureCount))
937 : {
938 0 : return !m_poFilterGeom && !m_poAttrQuery &&
939 0 : m_osAttributeFilter.empty() && m_bIsParquetLayer;
940 : }
941 0 : else if (EQUAL(pszCap, OLCFastGetExtent))
942 : {
943 0 : return !m_extents.empty() && m_extents[0].IsInit();
944 : }
945 0 : else if (EQUAL(pszCap, OLCFastSpatialFilter) && m_iGeomFieldFilter >= 0 &&
946 0 : m_iGeomFieldFilter < GetLayerDefn()->GetGeomFieldCount())
947 : {
948 0 : if (m_poDS->m_bSpatialLoaded && m_poDS->m_bIsDuckDBDataset)
949 : {
950 : const char *pszGeomColName =
951 0 : m_poAdapterLayer->m_poLayerDefn
952 0 : ->GetGeomFieldDefn(m_iGeomFieldFilter)
953 0 : ->GetNameRef();
954 0 : auto poTmpLayer = m_poDS->CreateInternalLayer(CPLSPrintf(
955 : "SELECT 1 FROM sqlite_master WHERE tbl_name = '%s' AND type = "
956 : "'index' AND (sql LIKE '%%USING RTREE (%s)%%' OR sql LIKE "
957 : "'%%USING RTREE (\"%s\")%%')",
958 0 : OGRDuplicateCharacter(GetDescription(), '\'').c_str(),
959 : pszGeomColName,
960 0 : OGRDuplicateCharacter(pszGeomColName, '"').c_str()));
961 0 : return !poTmpLayer->GotError() &&
962 0 : std::unique_ptr<OGRFeature>(poTmpLayer->GetNextFeature());
963 : }
964 0 : else if (!m_geomColBBOX[m_iGeomFieldFilter].osXMin.empty())
965 : {
966 : // Let's assume that the presence of a geometry bounding box
967 : // column is sufficient enough to pretend to have fast spatial
968 : // filter capabilities
969 0 : return true;
970 : }
971 : }
972 0 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
973 0 : return true;
974 :
975 0 : return false;
976 : }
977 :
978 : /************************************************************************/
979 : /* GetDataset() */
980 : /************************************************************************/
981 :
982 0 : GDALDataset *OGRADBCLayer::GetDataset()
983 : {
984 0 : return m_poDS;
985 : }
986 :
987 : /************************************************************************/
988 : /* GetArrowStream() */
989 : /************************************************************************/
990 :
991 0 : bool OGRADBCLayer::GetArrowStream(struct ArrowArrayStream *out_stream,
992 : CSLConstList papszOptions)
993 : {
994 0 : if (!m_poAdapterLayer)
995 0 : BuildLayerDefn();
996 :
997 0 : if (m_poFilterGeom || m_poAttrQuery ||
998 0 : CPLFetchBool(papszOptions, GAS_OPT_DATETIME_AS_STRING, false))
999 : {
1000 0 : return OGRLayer::GetArrowStream(out_stream, papszOptions);
1001 : }
1002 :
1003 0 : if (m_stream)
1004 : {
1005 0 : memcpy(out_stream, m_stream->get(), sizeof(*out_stream));
1006 0 : memset(m_stream->get(), 0, sizeof(*out_stream));
1007 0 : m_stream.reset();
1008 : }
1009 :
1010 0 : return GetArrowStreamInternal(out_stream);
1011 : }
1012 :
1013 : /************************************************************************/
1014 : /* GetArrowStreamInternal() */
1015 : /************************************************************************/
1016 :
1017 0 : bool OGRADBCLayer::GetArrowStreamInternal(struct ArrowArrayStream *out_stream)
1018 : {
1019 0 : OGRADBCError error;
1020 0 : int64_t rows_affected = -1;
1021 0 : if (ADBC_CALL(StatementExecuteQuery, m_statement.get(), out_stream,
1022 0 : &rows_affected, error) != ADBC_STATUS_OK)
1023 : {
1024 0 : CPLError(CE_Failure, CPLE_AppDefined,
1025 : "AdbcStatementExecuteQuery() failed: %s", error.message());
1026 0 : return false;
1027 : }
1028 :
1029 0 : return true;
1030 : }
1031 :
1032 : /************************************************************************/
1033 : /* GetFeatureCountSelectCountStar() */
1034 : /************************************************************************/
1035 :
1036 0 : GIntBig OGRADBCLayer::GetFeatureCountSelectCountStar()
1037 : {
1038 0 : const std::string osCurStatement = GetCurrentStatement();
1039 :
1040 : // Cf https://github.com/duckdb/duckdb/issues/21384
1041 : // and https://github.com/apache/arrow-adbc/blob/da58c591ed89b29c9096e4ebc0fe99d369e2bc88/docs/source/cpp/concurrency.rst
1042 0 : m_bNextStreamUsageMaybeInvalid = true;
1043 :
1044 : auto poCountLayer =
1045 0 : m_poDS->CreateInternalLayer(std::string("SELECT COUNT(*) FROM (")
1046 0 : .append(osCurStatement)
1047 0 : .append(")")
1048 0 : .c_str());
1049 0 : if (!poCountLayer->GotError() &&
1050 0 : poCountLayer->GetLayerDefn()->GetFieldCount() == 1)
1051 : {
1052 : auto poFeature =
1053 0 : std::unique_ptr<OGRFeature>(poCountLayer->GetNextFeature());
1054 0 : if (poFeature)
1055 0 : return poFeature->GetFieldAsInteger64(0);
1056 : }
1057 0 : if (m_bGetNextArrayHasRun)
1058 : {
1059 0 : m_bGetNextArrayHasRun = false;
1060 0 : return GetFeatureCountSelectCountStar();
1061 : }
1062 :
1063 0 : return -1;
1064 : }
1065 :
1066 : /************************************************************************/
1067 : /* GetFeatureCountArrow() */
1068 : /************************************************************************/
1069 :
1070 0 : GIntBig OGRADBCLayer::GetFeatureCountArrow()
1071 : {
1072 0 : if (m_nIdx > 0 || m_bEOF)
1073 0 : m_stream.reset();
1074 :
1075 0 : if (!m_stream)
1076 : {
1077 0 : auto stream = std::make_unique<OGRArrowArrayStream>();
1078 0 : if (!GetArrowStreamInternal(stream->get()))
1079 : {
1080 0 : return -1;
1081 : }
1082 0 : m_stream = std::move(stream);
1083 : }
1084 :
1085 0 : GIntBig nTotal = 0;
1086 : while (true)
1087 : {
1088 : struct ArrowArray array;
1089 0 : memset(&array, 0, sizeof(array));
1090 0 : if (m_stream->get_next(&array) != 0)
1091 : {
1092 0 : if (m_bNextStreamUsageMaybeInvalid)
1093 : {
1094 0 : m_bNextStreamUsageMaybeInvalid = false;
1095 0 : auto stream = std::make_unique<OGRArrowArrayStream>();
1096 0 : if (!GetArrowStreamInternal(stream->get()))
1097 : {
1098 0 : return -1;
1099 : }
1100 0 : m_stream = std::move(stream);
1101 0 : if (m_stream->get_next(&array) != 0)
1102 : {
1103 0 : m_stream.reset();
1104 0 : return -1;
1105 : }
1106 : }
1107 : else
1108 : {
1109 0 : m_stream.reset();
1110 0 : return -1;
1111 : }
1112 : }
1113 0 : m_bGetNextArrayHasRun = true;
1114 0 : const bool bStop = array.length == 0;
1115 0 : nTotal += array.length;
1116 0 : if (array.release)
1117 0 : array.release(&array);
1118 0 : if (bStop)
1119 0 : break;
1120 0 : }
1121 0 : m_stream.reset();
1122 0 : return nTotal;
1123 : }
1124 :
1125 : /************************************************************************/
1126 : /* GetFeatureCount() */
1127 : /************************************************************************/
1128 :
1129 0 : GIntBig OGRADBCLayer::GetFeatureCount(int bForce)
1130 : {
1131 0 : if (!m_poAdapterLayer)
1132 0 : BuildLayerDefn();
1133 0 : if (m_bLayerDefinitionError)
1134 0 : return 0;
1135 :
1136 0 : if (m_poFilterGeom || m_poAttrQuery || !m_osAttributeFilter.empty())
1137 : {
1138 0 : if (!m_osModifiedSelect.empty() &&
1139 0 : STARTS_WITH_CI(m_osBaseStatement.c_str(), "SELECT * FROM ") &&
1140 0 : (!m_poFilterGeom ||
1141 0 : !m_geomColBBOX[m_iGeomFieldFilter].osXMin.empty() ||
1142 0 : m_poDS->m_bSpatialLoaded))
1143 : {
1144 0 : auto nCount = GetFeatureCountSelectCountStar();
1145 0 : if (nCount >= 0)
1146 0 : return nCount;
1147 : }
1148 :
1149 0 : return OGRLayer::GetFeatureCount(bForce);
1150 : }
1151 :
1152 0 : if (m_bIsParquetLayer)
1153 : {
1154 0 : return GetFeatureCountParquet();
1155 : }
1156 :
1157 0 : return GetFeatureCountArrow();
1158 : }
1159 :
1160 : /************************************************************************/
1161 : /* GetFeatureCountParquet() */
1162 : /************************************************************************/
1163 :
1164 0 : GIntBig OGRADBCLayer::GetFeatureCountParquet()
1165 : {
1166 : // Cf https://github.com/duckdb/duckdb/issues/21384
1167 : // and https://github.com/apache/arrow-adbc/blob/da58c591ed89b29c9096e4ebc0fe99d369e2bc88/docs/source/cpp/concurrency.rst
1168 0 : m_bNextStreamUsageMaybeInvalid = true;
1169 :
1170 : const std::string osSQL(CPLSPrintf(
1171 : "SELECT CAST(SUM(num_rows) AS BIGINT) FROM parquet_file_metadata('%s')",
1172 0 : OGRDuplicateCharacter(m_poDS->m_osParquetFilename, '\'').c_str()));
1173 0 : auto poCountLayer = m_poDS->CreateInternalLayer(osSQL.c_str());
1174 0 : if (!poCountLayer->GotError() &&
1175 0 : poCountLayer->GetLayerDefn()->GetFieldCount() == 1)
1176 : {
1177 : auto poFeature =
1178 0 : std::unique_ptr<OGRFeature>(poCountLayer->GetNextFeature());
1179 0 : if (poFeature)
1180 0 : return poFeature->GetFieldAsInteger64(0);
1181 : }
1182 0 : if (m_bGetNextArrayHasRun)
1183 : {
1184 0 : m_bGetNextArrayHasRun = false;
1185 0 : return GetFeatureCountParquet();
1186 : }
1187 :
1188 0 : return -1;
1189 : }
1190 :
1191 : #undef ADBC_CALL
|