/src/gdal/ogr/ogrsf_frmts/adbc/ogradbclayer.cpp
Line | Count | Source |
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 | 0 | #define ADBC_CALL(func, ...) m_poDS->m_driver.func(__VA_ARGS__) |
26 | | |
27 | | OGRArrowArrayToOGRFeatureAdapterLayer::~OGRArrowArrayToOGRFeatureAdapterLayer() |
28 | 0 | { |
29 | 0 | m_poLayerDefn->Release(); |
30 | 0 | } |
31 | | |
32 | | /************************************************************************/ |
33 | | /* GetGeometryTypeFromString() */ |
34 | | /************************************************************************/ |
35 | | |
36 | | static OGRwkbGeometryType GetGeometryTypeFromString(const std::string &osType) |
37 | 0 | { |
38 | 0 | OGRwkbGeometryType eGeomType = wkbUnknown; |
39 | 0 | OGRReadWKTGeometryType(osType.c_str(), &eGeomType); |
40 | 0 | if (eGeomType == wkbUnknown && !osType.empty()) |
41 | 0 | { |
42 | 0 | CPLDebug("ADBC", "Unknown geometry type: %s", osType.c_str()); |
43 | 0 | } |
44 | 0 | return eGeomType; |
45 | 0 | } |
46 | | |
47 | | /************************************************************************/ |
48 | | /* OGRADBCLayer() */ |
49 | | /************************************************************************/ |
50 | | |
51 | | OGRADBCLayer::OGRADBCLayer(OGRADBCDataset *poDS, const char *pszName, |
52 | | const std::string &osStatement, bool bInternalUse) |
53 | 0 | : m_poDS(poDS), m_osBaseStatement(osStatement), |
54 | 0 | m_osModifiedBaseStatement(m_osBaseStatement), m_bInternalUse(bInternalUse) |
55 | 0 | { |
56 | 0 | SetDescription(pszName); |
57 | |
|
58 | 0 | memset(&m_schema, 0, sizeof(m_schema)); |
59 | 0 | } |
60 | | |
61 | | /************************************************************************/ |
62 | | /* OGRADBCLayer() */ |
63 | | /************************************************************************/ |
64 | | |
65 | | OGRADBCLayer::OGRADBCLayer(OGRADBCDataset *poDS, const char *pszName, |
66 | | std::unique_ptr<OGRArrowArrayStream> poStream, |
67 | | ArrowSchema *schema, bool bInternalUse) |
68 | 0 | : m_poDS(poDS), m_stream(std::move(poStream)), m_bInternalUse(bInternalUse) |
69 | 0 | { |
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 | | const OGRFeatureDefn *OGRADBCLayer::GetLayerDefn() const |
81 | 0 | { |
82 | 0 | if (!m_poAdapterLayer) |
83 | 0 | const_cast<OGRADBCLayer *>(this)->BuildLayerDefn(); |
84 | 0 | assert(m_poAdapterLayer); |
85 | 0 | return m_poAdapterLayer->GetLayerDefn(); |
86 | 0 | } |
87 | | |
88 | | /************************************************************************/ |
89 | | /* GetFIDColumn() */ |
90 | | /************************************************************************/ |
91 | | |
92 | | const char *OGRADBCLayer::GetFIDColumn() const |
93 | 0 | { |
94 | 0 | if (!m_poAdapterLayer) |
95 | 0 | const_cast<OGRADBCLayer *>(this)->BuildLayerDefn(); |
96 | 0 | return m_osFIDColName.c_str(); |
97 | 0 | } |
98 | | |
99 | | /************************************************************************/ |
100 | | /* GotError() */ |
101 | | /************************************************************************/ |
102 | | |
103 | | bool OGRADBCLayer::GotError() |
104 | 0 | { |
105 | 0 | if (!m_poAdapterLayer) |
106 | 0 | BuildLayerDefn(); |
107 | 0 | return m_bLayerDefinitionError; |
108 | 0 | } |
109 | | |
110 | | /************************************************************************/ |
111 | | /* ParseGeometryColumnCovering() */ |
112 | | /************************************************************************/ |
113 | | |
114 | | //! Parse bounding box column definition |
115 | | 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 | 0 | { |
122 | 0 | const auto oCovering = oJSONDef["covering"]; |
123 | 0 | if (oCovering.IsValid() && |
124 | 0 | oCovering.GetType() == CPLJSONObject::Type::Object) |
125 | 0 | { |
126 | 0 | const auto oBBOX = oCovering["bbox"]; |
127 | 0 | if (oBBOX.IsValid() && oBBOX.GetType() == CPLJSONObject::Type::Object) |
128 | 0 | { |
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 | 0 | { |
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 | 0 | { |
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 | 0 | } |
165 | 0 | } |
166 | 0 | } |
167 | 0 | } |
168 | 0 | return false; |
169 | 0 | } |
170 | | |
171 | | /************************************************************************/ |
172 | | /* ParseGeoParquetColumn() */ |
173 | | /************************************************************************/ |
174 | | |
175 | | 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 | 0 | { |
185 | 0 | auto oCrs = oColumn.GetObj("crs"); |
186 | 0 | if (!oCrs.IsValid()) |
187 | 0 | { |
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 | 0 | } |
194 | 0 | else if (oCrs.GetType() == CPLJSONObject::Type::Object) |
195 | 0 | { |
196 | | // CRS encoded as PROJJSON (extension) |
197 | 0 | const auto oType = oCrs["type"]; |
198 | 0 | if (oType.IsValid() && oType.GetType() == CPLJSONObject::Type::String) |
199 | 0 | { |
200 | 0 | const auto osType = oType.ToString(); |
201 | 0 | if (osType.find("CRS") != std::string::npos) |
202 | 0 | { |
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 | 0 | OGRERR_NONE) |
208 | 0 | { |
209 | 0 | oMapGeomColumnsFromGeoParquet[oColumn.GetName()] = |
210 | 0 | std::move(poSRS); |
211 | 0 | } |
212 | 0 | } |
213 | 0 | } |
214 | 0 | } |
215 | 0 | else |
216 | 0 | { |
217 | 0 | oMapGeomColumnsFromGeoParquet[oColumn.GetName()] = nullptr; |
218 | 0 | } |
219 | |
|
220 | 0 | OGRwkbGeometryType eGeomType = wkbUnknown; |
221 | 0 | auto oType = oColumn.GetObj("geometry_types"); |
222 | 0 | if (oType.GetType() == CPLJSONObject::Type::Array) |
223 | 0 | { |
224 | 0 | const auto oTypeArray = oType.ToArray(); |
225 | 0 | if (oTypeArray.Size() == 1) |
226 | 0 | { |
227 | 0 | eGeomType = GetGeometryTypeFromString(oTypeArray[0].ToString()); |
228 | 0 | } |
229 | 0 | else if (oTypeArray.Size() > 1) |
230 | 0 | { |
231 | 0 | const auto PromoteToCollection = [](OGRwkbGeometryType eType) |
232 | 0 | { |
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 | 0 | }; |
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 | 0 | { |
250 | 0 | const auto eThisGeom = |
251 | 0 | GetGeometryTypeFromString(oTypeArray[i].ToString()); |
252 | 0 | if (PromoteToCollection(OGR_GT_Flatten(eThisGeom)) != |
253 | 0 | eFirstTypeCollection) |
254 | 0 | { |
255 | 0 | bMixed = true; |
256 | 0 | break; |
257 | 0 | } |
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 | 0 | } |
263 | 0 | if (!bMixed) |
264 | 0 | { |
265 | 0 | if (eFirstTypeCollection == wkbMultiPolygon || |
266 | 0 | eFirstTypeCollection == wkbMultiLineString) |
267 | 0 | { |
268 | 0 | if (bHasMulti) |
269 | 0 | eGeomType = OGR_GT_SetModifier(eFirstTypeCollection, |
270 | 0 | bHasZ, bHasM); |
271 | 0 | else |
272 | 0 | eGeomType = |
273 | 0 | OGR_GT_SetModifier(eFirstType, bHasZ, bHasM); |
274 | 0 | } |
275 | 0 | } |
276 | 0 | } |
277 | 0 | } |
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 | 0 | { |
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 | 0 | { |
293 | 0 | oMapExtent[oColumn.GetName()] = sExtent; |
294 | 0 | } |
295 | 0 | } |
296 | 0 | else if (oBBox.IsValid() && oBBox.Size() == 6) |
297 | 0 | { |
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 | 0 | { |
306 | 0 | oMapExtent[oColumn.GetName()] = sExtent; |
307 | 0 | } |
308 | 0 | } |
309 | |
|
310 | 0 | std::string osBBOXColumn; |
311 | 0 | std::string osXMin, osYMin, osXMax, osYMax; |
312 | 0 | if (ParseGeometryColumnCovering(oColumn, osBBOXColumn, osXMin, osYMin, |
313 | 0 | osXMax, osYMax)) |
314 | 0 | { |
315 | 0 | OGRADBCLayer::GeomColBBOX geomColBBOX; |
316 | 0 | 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 | 0 | { |
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 | 0 | } |
336 | 0 | } |
337 | | |
338 | | /************************************************************************/ |
339 | | /* BuildLayerDefnInit() */ |
340 | | /************************************************************************/ |
341 | | |
342 | | bool OGRADBCLayer::BuildLayerDefnInit(bool bCreateStream) |
343 | 0 | { |
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 | 0 | { |
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 | 0 | { |
357 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
358 | 0 | "AdbcStatementNew() failed: %s", error.message()); |
359 | 0 | return false; |
360 | 0 | } |
361 | | |
362 | 0 | if (ADBC_CALL(StatementSetSqlQuery, m_statement.get(), |
363 | 0 | m_osBaseStatement.c_str(), error) != ADBC_STATUS_OK) |
364 | 0 | { |
365 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
366 | 0 | "AdbcStatementSetSqlQuery() failed: %s", error.message()); |
367 | 0 | return false; |
368 | 0 | } |
369 | 0 | } |
370 | | |
371 | 0 | if (!m_stream && bCreateStream) |
372 | 0 | { |
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 | 0 | { |
379 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
380 | 0 | "AdbcStatementExecuteQuery() failed: %s", error.message()); |
381 | 0 | return false; |
382 | 0 | } |
383 | | |
384 | 0 | if (m_stream->get_schema(&m_schema) != 0) |
385 | 0 | { |
386 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "get_schema() failed"); |
387 | 0 | return false; |
388 | 0 | } |
389 | 0 | } |
390 | | |
391 | 0 | m_bLayerDefinitionError = false; |
392 | 0 | return true; |
393 | 0 | } |
394 | | |
395 | | /************************************************************************/ |
396 | | /* BuildLayerDefn() */ |
397 | | /************************************************************************/ |
398 | | |
399 | | void OGRADBCLayer::BuildLayerDefn() |
400 | 0 | { |
401 | 0 | 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 | 0 | { |
418 | | // Try to read GeoParquet 'geo' metadata |
419 | 0 | 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 | 0 | { |
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 | 0 | { |
434 | 0 | auto f = std::unique_ptr<OGRFeature>( |
435 | 0 | poMetadataLayer->GetNextFeature()); |
436 | 0 | if (f) |
437 | 0 | { |
438 | 0 | int nBytes = 0; |
439 | 0 | const GByte *pabyData = f->GetFieldAsBinary(0, &nBytes); |
440 | 0 | osGeoParquetMD.assign( |
441 | 0 | reinterpret_cast<const char *>(pabyData), nBytes); |
442 | | // CPLDebug("ADBC", "%s", osGeoParquetMD.c_str()); |
443 | 0 | } |
444 | 0 | } |
445 | 0 | } |
446 | 0 | CPLJSONDocument oDoc; |
447 | 0 | if (!osGeoParquetMD.empty() && oDoc.LoadMemory(osGeoParquetMD)) |
448 | 0 | { |
449 | 0 | const auto oColumns = oDoc.GetRoot().GetObj("columns"); |
450 | 0 | for (const auto &oColumn : oColumns.GetChildren()) |
451 | 0 | { |
452 | 0 | if (oColumn.GetString("encoding") == "WKB") |
453 | 0 | { |
454 | 0 | ParseGeoParquetColumn(oColumn, oMapType, oMapExtent, |
455 | 0 | oMapGeomColumnToCoveringBBOXColumn, |
456 | 0 | oMapGeomColumnsFromGeoParquet, |
457 | 0 | oSetCoveringBBoxColumn); |
458 | 0 | } |
459 | 0 | } |
460 | 0 | } |
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 | 0 | { |
471 | 0 | for (auto &&f : *poDescribeLayer) |
472 | 0 | { |
473 | 0 | const char *pszColName = f->GetFieldAsString("column_name"); |
474 | 0 | if (cpl::contains(oSetCoveringBBoxColumn, pszColName)) |
475 | 0 | { |
476 | 0 | bNewStatement = true; |
477 | 0 | continue; |
478 | 0 | } |
479 | | |
480 | | // f->DumpReadable(stdout); |
481 | 0 | if (osNewStatement.empty()) |
482 | 0 | osNewStatement = "SELECT "; |
483 | 0 | else |
484 | 0 | osNewStatement += ", "; |
485 | |
|
486 | 0 | auto oIter = oMapGeomColumnsFromGeoParquet.find(pszColName); |
487 | 0 | if (oIter != oMapGeomColumnsFromGeoParquet.end()) |
488 | 0 | { |
489 | 0 | oMapGeomColumns[pszColName] = std::move(oIter->second); |
490 | 0 | } |
491 | 0 | const char *pszColType = f->GetFieldAsString("column_type"); |
492 | 0 | if (STARTS_WITH_CI(pszColType, "GEOMETRY") && |
493 | 0 | m_poDS->m_bSpatialLoaded) |
494 | 0 | { |
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 | 0 | { |
501 | 0 | oMapGeomColumns[pszColName] = nullptr; |
502 | | // Below is with DuckDB >= 1.5 |
503 | 0 | if (STARTS_WITH_CI(pszColType, "GEOMETRY('EPSG:")) |
504 | 0 | { |
505 | 0 | auto poSRS = |
506 | 0 | std::make_unique<OGRSpatialReference>(); |
507 | 0 | poSRS->SetAxisMappingStrategy( |
508 | 0 | OAMS_TRADITIONAL_GIS_ORDER); |
509 | 0 | if (poSRS->importFromEPSG(atoi( |
510 | 0 | pszColType + strlen("GEOMETRY('EPSG:"))) == |
511 | 0 | OGRERR_NONE) |
512 | 0 | { |
513 | 0 | oMapGeomColumns[pszColName] = std::move(poSRS); |
514 | 0 | } |
515 | 0 | } |
516 | 0 | } |
517 | 0 | } |
518 | 0 | osNewStatement += '"'; |
519 | 0 | osNewStatement += OGRDuplicateCharacter(pszColName, '"'); |
520 | 0 | osNewStatement += '"'; |
521 | 0 | } |
522 | 0 | m_osModifiedSelect = osNewStatement; |
523 | 0 | osNewStatement += " FROM ("; |
524 | 0 | osNewStatement += m_osBaseStatement; |
525 | 0 | osNewStatement += " )"; |
526 | 0 | } |
527 | |
|
528 | 0 | if (bNewStatement) |
529 | 0 | { |
530 | | // CPLDebug("ADBC", "%s -> %s", m_osBaseStatement.c_str(), osNewStatement.c_str()); |
531 | 0 | if (ReplaceStatement(osNewStatement.c_str())) |
532 | 0 | { |
533 | 0 | m_osModifiedBaseStatement = std::move(osNewStatement); |
534 | 0 | } |
535 | 0 | else |
536 | 0 | { |
537 | 0 | m_osModifiedSelect.clear(); |
538 | 0 | oMapGeomColumns.clear(); |
539 | 0 | } |
540 | 0 | } |
541 | |
|
542 | 0 | if (!m_stream) |
543 | 0 | { |
544 | 0 | auto stream = std::make_unique<OGRArrowArrayStream>(); |
545 | 0 | if (!GetArrowStreamInternal(stream->get())) |
546 | 0 | { |
547 | 0 | return; |
548 | 0 | } |
549 | 0 | m_stream = std::move(stream); |
550 | 0 | if (m_stream->get_schema(&m_schema) != 0) |
551 | 0 | { |
552 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "get_schema() failed"); |
553 | 0 | return; |
554 | 0 | } |
555 | 0 | } |
556 | 0 | } |
557 | | |
558 | 0 | for (int i = 0; i < m_schema.n_children; ++i) |
559 | 0 | { |
560 | 0 | const char *pszColName = m_schema.children[i]->name; |
561 | 0 | auto oIter = oMapGeomColumns.find(pszColName); |
562 | 0 | if (oIter != oMapGeomColumns.end()) |
563 | 0 | { |
564 | 0 | OGRGeomFieldDefn oGeomFieldDefn(pszColName, oMapType[pszColName]); |
565 | 0 | auto poSRS = std::move(oIter->second).release(); |
566 | 0 | if (poSRS) |
567 | 0 | { |
568 | 0 | oGeomFieldDefn.SetSpatialRef(poSRS); |
569 | 0 | poSRS->Release(); |
570 | 0 | } |
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 | 0 | } |
577 | 0 | else |
578 | 0 | { |
579 | 0 | m_poAdapterLayer->CreateFieldFromArrowSchema(m_schema.children[i]); |
580 | 0 | } |
581 | 0 | } |
582 | 0 | } |
583 | | |
584 | | /************************************************************************/ |
585 | | /* ~OGRADBCLayer() */ |
586 | | /************************************************************************/ |
587 | | |
588 | | OGRADBCLayer::~OGRADBCLayer() |
589 | 0 | { |
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 | | bool OGRADBCLayer::ReplaceStatement(const char *pszNewStatement) |
602 | 0 | { |
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 | 0 | { |
609 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "AdbcStatementNew() failed: %s", |
610 | 0 | error.message()); |
611 | 0 | ADBC_CALL(StatementRelease, statement.get(), error); |
612 | 0 | } |
613 | 0 | else if (ADBC_CALL(StatementSetSqlQuery, statement.get(), pszNewStatement, |
614 | 0 | error) != ADBC_STATUS_OK) |
615 | 0 | { |
616 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
617 | 0 | "AdbcStatementSetSqlQuery() failed: %s", error.message()); |
618 | 0 | error.clear(); |
619 | 0 | ADBC_CALL(StatementRelease, statement.get(), error); |
620 | 0 | } |
621 | 0 | else |
622 | 0 | { |
623 | 0 | auto stream = std::make_unique<OGRArrowArrayStream>(); |
624 | 0 | int64_t rows_affected = -1; |
625 | 0 | 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 | 0 | { |
630 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
631 | 0 | "AdbcStatementExecuteQuery() failed: %s", error.message()); |
632 | 0 | error.clear(); |
633 | 0 | ADBC_CALL(StatementRelease, statement.get(), error); |
634 | 0 | } |
635 | 0 | else if (stream->get_schema(&newSchema) != 0) |
636 | 0 | { |
637 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "get_schema() failed"); |
638 | 0 | ADBC_CALL(StatementRelease, statement.get(), error); |
639 | 0 | } |
640 | 0 | else |
641 | 0 | { |
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 | 0 | } |
654 | 0 | } |
655 | 0 | return false; |
656 | 0 | } |
657 | | |
658 | | /************************************************************************/ |
659 | | /* GetNextRawFeature() */ |
660 | | /************************************************************************/ |
661 | | |
662 | | OGRFeature *OGRADBCLayer::GetNextRawFeature() |
663 | 0 | { |
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 | 0 | { |
673 | 0 | m_nIdx = 0; |
674 | 0 | m_poAdapterLayer->m_apoFeatures.clear(); |
675 | |
|
676 | 0 | struct ArrowArray array; |
677 | 0 | for (int i = 0; i < 2; ++i) |
678 | 0 | { |
679 | 0 | if (!m_stream) |
680 | 0 | { |
681 | 0 | auto stream = std::make_unique<OGRArrowArrayStream>(); |
682 | 0 | if (!GetArrowStreamInternal(stream->get())) |
683 | 0 | { |
684 | 0 | m_bEOF = true; |
685 | 0 | return nullptr; |
686 | 0 | } |
687 | 0 | m_stream = std::move(stream); |
688 | 0 | } |
689 | | |
690 | 0 | memset(&array, 0, sizeof(array)); |
691 | 0 | if (m_stream->get_next(&array) != 0) |
692 | 0 | { |
693 | 0 | if (m_bNextStreamUsageMaybeInvalid) |
694 | 0 | { |
695 | 0 | m_bNextStreamUsageMaybeInvalid = false; |
696 | 0 | m_stream.reset(); |
697 | 0 | } |
698 | 0 | else |
699 | 0 | { |
700 | 0 | m_bEOF = true; |
701 | 0 | return nullptr; |
702 | 0 | } |
703 | 0 | } |
704 | 0 | else |
705 | 0 | { |
706 | 0 | break; |
707 | 0 | } |
708 | 0 | } |
709 | 0 | m_bGetNextArrayHasRun = true; |
710 | 0 | 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 | 0 | { |
718 | 0 | m_bEOF = true; |
719 | 0 | return nullptr; |
720 | 0 | } |
721 | 0 | } |
722 | | |
723 | 0 | auto poFeature = m_poAdapterLayer->m_apoFeatures[m_nIdx++].release(); |
724 | 0 | const int nGeomFieldCount = |
725 | 0 | m_poAdapterLayer->m_poLayerDefn->GetFieldCount(); |
726 | 0 | for (int i = 0; i < nGeomFieldCount; ++i) |
727 | 0 | { |
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 | 0 | } |
734 | 0 | if (m_osFIDColName.empty()) |
735 | 0 | poFeature->SetFID(m_nFeatureID++); |
736 | 0 | else |
737 | 0 | poFeature->SetFID( |
738 | 0 | poFeature->GetFieldAsInteger64(m_osFIDColName.c_str())); |
739 | 0 | return poFeature; |
740 | 0 | } |
741 | | |
742 | | /************************************************************************/ |
743 | | /* ResetReading() */ |
744 | | /************************************************************************/ |
745 | | |
746 | | void OGRADBCLayer::ResetReading() |
747 | 0 | { |
748 | 0 | if (m_nIdx > 0 || m_bEOF) |
749 | 0 | { |
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 | 0 | } |
757 | 0 | } |
758 | | |
759 | | /************************************************************************/ |
760 | | /* IGetExtent() */ |
761 | | /************************************************************************/ |
762 | | |
763 | | OGRErr OGRADBCLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent, |
764 | | bool bForce) |
765 | 0 | { |
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 | 0 | } |
775 | | |
776 | | /************************************************************************/ |
777 | | /* IGetExtent3D() */ |
778 | | /************************************************************************/ |
779 | | |
780 | | OGRErr OGRADBCLayer::IGetExtent3D(int iGeomField, OGREnvelope3D *psExtent, |
781 | | bool bForce) |
782 | 0 | { |
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 | 0 | } |
792 | | |
793 | | /************************************************************************/ |
794 | | /* GetCurrentStatement() */ |
795 | | /************************************************************************/ |
796 | | |
797 | | std::string OGRADBCLayer::GetCurrentStatement() const |
798 | 0 | { |
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 | 0 | { |
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 | 0 | { |
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 | 0 | { |
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 | 0 | } |
845 | 0 | if (m_poDS->m_bSpatialLoaded) |
846 | 0 | { |
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 | 0 | ->GetNameRef(), |
855 | 0 | '"')) |
856 | 0 | .append(CPLSPrintf( |
857 | 0 | "\", ST_MakeEnvelope(%.17g,%.17g,%.17g,%.17g))", dfMinX, |
858 | 0 | dfMinY, dfMaxX, dfMaxY)); |
859 | 0 | } |
860 | 0 | } |
861 | 0 | if (!m_osAttributeFilter.empty()) |
862 | 0 | { |
863 | 0 | if (bAddAnd) |
864 | 0 | osStatement.append(" AND "); |
865 | 0 | osStatement.append("("); |
866 | 0 | osStatement.append(m_osAttributeFilter); |
867 | 0 | osStatement.append(")"); |
868 | 0 | } |
869 | |
|
870 | 0 | return osStatement; |
871 | 0 | } |
872 | 0 | else |
873 | 0 | { |
874 | 0 | return m_osModifiedBaseStatement; |
875 | 0 | } |
876 | 0 | } |
877 | | |
878 | | /************************************************************************/ |
879 | | /* UpdateStatement() */ |
880 | | /************************************************************************/ |
881 | | |
882 | | bool OGRADBCLayer::UpdateStatement() |
883 | 0 | { |
884 | 0 | return ReplaceStatement(GetCurrentStatement().c_str()); |
885 | 0 | } |
886 | | |
887 | | /************************************************************************/ |
888 | | /* SetAttributeFilter() */ |
889 | | /************************************************************************/ |
890 | | |
891 | | OGRErr OGRADBCLayer::SetAttributeFilter(const char *pszFilter) |
892 | 0 | { |
893 | 0 | if (!m_osModifiedSelect.empty() && m_poDS->m_bIsDuckDBDriver && |
894 | 0 | STARTS_WITH_CI(m_osBaseStatement.c_str(), "SELECT * FROM ")) |
895 | 0 | { |
896 | 0 | m_osAttributeFilter = pszFilter ? pszFilter : ""; |
897 | 0 | return UpdateStatement() ? OGRERR_NONE : OGRERR_FAILURE; |
898 | 0 | } |
899 | 0 | else |
900 | 0 | { |
901 | 0 | return OGRLayer::SetAttributeFilter(pszFilter); |
902 | 0 | } |
903 | 0 | } |
904 | | |
905 | | /************************************************************************/ |
906 | | /* ISetSpatialFilter() */ |
907 | | /************************************************************************/ |
908 | | |
909 | | OGRErr OGRADBCLayer::ISetSpatialFilter(int iGeomField, |
910 | | const OGRGeometry *poGeomIn) |
911 | | |
912 | 0 | { |
913 | 0 | if (iGeomField < GetLayerDefn()->GetGeomFieldCount()) |
914 | 0 | { |
915 | 0 | m_iGeomFieldFilter = iGeomField; |
916 | 0 | if (InstallFilter(poGeomIn)) |
917 | 0 | ResetReading(); |
918 | 0 | UpdateStatement(); |
919 | 0 | } |
920 | 0 | return OGRERR_NONE; |
921 | 0 | } |
922 | | |
923 | | /************************************************************************/ |
924 | | /* TestCapability() */ |
925 | | /************************************************************************/ |
926 | | |
927 | | int OGRADBCLayer::TestCapability(const char *pszCap) const |
928 | 0 | { |
929 | 0 | if (!m_poAdapterLayer) |
930 | 0 | const_cast<OGRADBCLayer *>(this)->BuildLayerDefn(); |
931 | |
|
932 | 0 | if (EQUAL(pszCap, OLCFastGetArrowStream)) |
933 | 0 | { |
934 | 0 | return !m_poFilterGeom && !m_poAttrQuery && m_osAttributeFilter.empty(); |
935 | 0 | } |
936 | 0 | else if (EQUAL(pszCap, OLCFastFeatureCount)) |
937 | 0 | { |
938 | 0 | return !m_poFilterGeom && !m_poAttrQuery && |
939 | 0 | m_osAttributeFilter.empty() && m_bIsParquetLayer; |
940 | 0 | } |
941 | 0 | else if (EQUAL(pszCap, OLCFastGetExtent)) |
942 | 0 | { |
943 | 0 | return !m_extents.empty() && m_extents[0].IsInit(); |
944 | 0 | } |
945 | 0 | else if (EQUAL(pszCap, OLCFastSpatialFilter) && m_iGeomFieldFilter >= 0 && |
946 | 0 | m_iGeomFieldFilter < GetLayerDefn()->GetGeomFieldCount()) |
947 | 0 | { |
948 | 0 | if (m_poDS->m_bSpatialLoaded && m_poDS->m_bIsDuckDBDataset) |
949 | 0 | { |
950 | 0 | 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 | 0 | "SELECT 1 FROM sqlite_master WHERE tbl_name = '%s' AND type = " |
956 | 0 | "'index' AND (sql LIKE '%%USING RTREE (%s)%%' OR sql LIKE " |
957 | 0 | "'%%USING RTREE (\"%s\")%%')", |
958 | 0 | OGRDuplicateCharacter(GetDescription(), '\'').c_str(), |
959 | 0 | pszGeomColName, |
960 | 0 | OGRDuplicateCharacter(pszGeomColName, '"').c_str())); |
961 | 0 | return !poTmpLayer->GotError() && |
962 | 0 | std::unique_ptr<OGRFeature>(poTmpLayer->GetNextFeature()); |
963 | 0 | } |
964 | 0 | else if (!m_geomColBBOX[m_iGeomFieldFilter].osXMin.empty()) |
965 | 0 | { |
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 | 0 | } |
971 | 0 | } |
972 | 0 | else if (EQUAL(pszCap, OLCStringsAsUTF8)) |
973 | 0 | return true; |
974 | | |
975 | 0 | return false; |
976 | 0 | } |
977 | | |
978 | | /************************************************************************/ |
979 | | /* GetDataset() */ |
980 | | /************************************************************************/ |
981 | | |
982 | | GDALDataset *OGRADBCLayer::GetDataset() |
983 | 0 | { |
984 | 0 | return m_poDS; |
985 | 0 | } |
986 | | |
987 | | /************************************************************************/ |
988 | | /* GetArrowStream() */ |
989 | | /************************************************************************/ |
990 | | |
991 | | bool OGRADBCLayer::GetArrowStream(struct ArrowArrayStream *out_stream, |
992 | | CSLConstList papszOptions) |
993 | 0 | { |
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 | 0 | { |
1000 | 0 | return OGRLayer::GetArrowStream(out_stream, papszOptions); |
1001 | 0 | } |
1002 | | |
1003 | 0 | if (m_stream) |
1004 | 0 | { |
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 | 0 | } |
1009 | |
|
1010 | 0 | return GetArrowStreamInternal(out_stream); |
1011 | 0 | } |
1012 | | |
1013 | | /************************************************************************/ |
1014 | | /* GetArrowStreamInternal() */ |
1015 | | /************************************************************************/ |
1016 | | |
1017 | | bool OGRADBCLayer::GetArrowStreamInternal(struct ArrowArrayStream *out_stream) |
1018 | 0 | { |
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 | 0 | { |
1024 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1025 | 0 | "AdbcStatementExecuteQuery() failed: %s", error.message()); |
1026 | 0 | return false; |
1027 | 0 | } |
1028 | | |
1029 | 0 | return true; |
1030 | 0 | } |
1031 | | |
1032 | | /************************************************************************/ |
1033 | | /* GetFeatureCountSelectCountStar() */ |
1034 | | /************************************************************************/ |
1035 | | |
1036 | | GIntBig OGRADBCLayer::GetFeatureCountSelectCountStar() |
1037 | 0 | { |
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 | 0 | 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 | 0 | { |
1052 | 0 | auto poFeature = |
1053 | 0 | std::unique_ptr<OGRFeature>(poCountLayer->GetNextFeature()); |
1054 | 0 | if (poFeature) |
1055 | 0 | return poFeature->GetFieldAsInteger64(0); |
1056 | 0 | } |
1057 | 0 | if (m_bGetNextArrayHasRun) |
1058 | 0 | { |
1059 | 0 | m_bGetNextArrayHasRun = false; |
1060 | 0 | return GetFeatureCountSelectCountStar(); |
1061 | 0 | } |
1062 | | |
1063 | 0 | return -1; |
1064 | 0 | } |
1065 | | |
1066 | | /************************************************************************/ |
1067 | | /* GetFeatureCountArrow() */ |
1068 | | /************************************************************************/ |
1069 | | |
1070 | | GIntBig OGRADBCLayer::GetFeatureCountArrow() |
1071 | 0 | { |
1072 | 0 | if (m_nIdx > 0 || m_bEOF) |
1073 | 0 | m_stream.reset(); |
1074 | |
|
1075 | 0 | if (!m_stream) |
1076 | 0 | { |
1077 | 0 | auto stream = std::make_unique<OGRArrowArrayStream>(); |
1078 | 0 | if (!GetArrowStreamInternal(stream->get())) |
1079 | 0 | { |
1080 | 0 | return -1; |
1081 | 0 | } |
1082 | 0 | m_stream = std::move(stream); |
1083 | 0 | } |
1084 | | |
1085 | 0 | GIntBig nTotal = 0; |
1086 | 0 | while (true) |
1087 | 0 | { |
1088 | 0 | struct ArrowArray array; |
1089 | 0 | memset(&array, 0, sizeof(array)); |
1090 | 0 | if (m_stream->get_next(&array) != 0) |
1091 | 0 | { |
1092 | 0 | if (m_bNextStreamUsageMaybeInvalid) |
1093 | 0 | { |
1094 | 0 | m_bNextStreamUsageMaybeInvalid = false; |
1095 | 0 | auto stream = std::make_unique<OGRArrowArrayStream>(); |
1096 | 0 | if (!GetArrowStreamInternal(stream->get())) |
1097 | 0 | { |
1098 | 0 | return -1; |
1099 | 0 | } |
1100 | 0 | m_stream = std::move(stream); |
1101 | 0 | if (m_stream->get_next(&array) != 0) |
1102 | 0 | { |
1103 | 0 | m_stream.reset(); |
1104 | 0 | return -1; |
1105 | 0 | } |
1106 | 0 | } |
1107 | 0 | else |
1108 | 0 | { |
1109 | 0 | m_stream.reset(); |
1110 | 0 | return -1; |
1111 | 0 | } |
1112 | 0 | } |
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 | 0 | } |
1124 | | |
1125 | | /************************************************************************/ |
1126 | | /* GetFeatureCount() */ |
1127 | | /************************************************************************/ |
1128 | | |
1129 | | GIntBig OGRADBCLayer::GetFeatureCount(int bForce) |
1130 | 0 | { |
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 | 0 | { |
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 | 0 | { |
1144 | 0 | auto nCount = GetFeatureCountSelectCountStar(); |
1145 | 0 | if (nCount >= 0) |
1146 | 0 | return nCount; |
1147 | 0 | } |
1148 | | |
1149 | 0 | return OGRLayer::GetFeatureCount(bForce); |
1150 | 0 | } |
1151 | | |
1152 | 0 | if (m_bIsParquetLayer) |
1153 | 0 | { |
1154 | 0 | return GetFeatureCountParquet(); |
1155 | 0 | } |
1156 | | |
1157 | 0 | return GetFeatureCountArrow(); |
1158 | 0 | } |
1159 | | |
1160 | | /************************************************************************/ |
1161 | | /* GetFeatureCountParquet() */ |
1162 | | /************************************************************************/ |
1163 | | |
1164 | | GIntBig OGRADBCLayer::GetFeatureCountParquet() |
1165 | 0 | { |
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 | 0 | const std::string osSQL(CPLSPrintf( |
1171 | 0 | "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 | 0 | { |
1177 | 0 | auto poFeature = |
1178 | 0 | std::unique_ptr<OGRFeature>(poCountLayer->GetNextFeature()); |
1179 | 0 | if (poFeature) |
1180 | 0 | return poFeature->GetFieldAsInteger64(0); |
1181 | 0 | } |
1182 | 0 | if (m_bGetNextArrayHasRun) |
1183 | 0 | { |
1184 | 0 | m_bGetNextArrayHasRun = false; |
1185 | 0 | return GetFeatureCountParquet(); |
1186 | 0 | } |
1187 | | |
1188 | 0 | return -1; |
1189 | 0 | } |
1190 | | |
1191 | | #undef ADBC_CALL |