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