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 "ogradbcdrivercore.h"
16 : #include "ogr_mem.h"
17 : #include "ogr_p.h"
18 : #include "cpl_error.h"
19 : #include "cpl_json.h"
20 : #include "gdal_adbc.h"
21 :
22 : #if defined(OGR_ADBC_HAS_DRIVER_MANAGER)
23 : #include <arrow-adbc/adbc_driver_manager.h>
24 : #endif
25 :
26 : #define OGR_ADBC_VERSION ADBC_VERSION_1_1_0
27 : static_assert(sizeof(AdbcDriver) == ADBC_DRIVER_1_1_0_SIZE);
28 :
29 : namespace
30 : {
31 :
32 0 : AdbcStatusCode OGRADBCLoadDriver(const char *driver_name,
33 : const char *entrypoint, void *driver,
34 : struct AdbcError *error)
35 : {
36 : GDALAdbcLoadDriverFunc load_driver_override =
37 0 : GDALGetAdbcLoadDriverOverride();
38 0 : if (load_driver_override)
39 : {
40 0 : return load_driver_override(driver_name, entrypoint, OGR_ADBC_VERSION,
41 0 : driver, error);
42 : }
43 : else
44 : {
45 : #if defined(OGR_ADBC_HAS_DRIVER_MANAGER)
46 : return AdbcLoadDriver(driver_name, entrypoint, OGR_ADBC_VERSION, driver,
47 : error);
48 : #else
49 0 : return ADBC_STATUS_NOT_IMPLEMENTED;
50 : #endif
51 : }
52 : }
53 :
54 : } // namespace
55 :
56 : // Helper to wrap driver callbacks
57 : #define ADBC_CALL(func, ...) m_driver.func(__VA_ARGS__)
58 :
59 : /************************************************************************/
60 : /* ~OGRADBCDataset() */
61 : /************************************************************************/
62 :
63 0 : OGRADBCDataset::~OGRADBCDataset()
64 : {
65 : // Layers must be closed before the connection
66 0 : m_apoLayers.clear();
67 0 : OGRADBCError error;
68 0 : if (m_connection)
69 0 : ADBC_CALL(ConnectionRelease, m_connection.get(), error);
70 0 : error.clear();
71 0 : if (m_driver.release)
72 : {
73 0 : ADBC_CALL(DatabaseRelease, &m_database, error);
74 0 : m_driver.release(&m_driver, error);
75 : }
76 0 : }
77 :
78 : /************************************************************************/
79 : /* CreateLayer() */
80 : /************************************************************************/
81 :
82 : std::unique_ptr<OGRADBCLayer>
83 0 : OGRADBCDataset::CreateLayer(const char *pszStatement, const char *pszLayerName,
84 : bool bInternalUse)
85 : {
86 :
87 0 : OGRADBCError error;
88 :
89 0 : CPLString osStatement(pszStatement);
90 0 : if (!m_osParquetFilename.empty())
91 : {
92 0 : const char *pszSrcLayerName = m_apoLayers.size() == 1
93 0 : ? m_apoLayers[0]->GetDescription()
94 0 : : pszLayerName;
95 : // Substitute the OGR layer name with the DuckDB expected filename,
96 : // single-quoted
97 : const std::string osFrom =
98 0 : std::string(" FROM ").append(pszSrcLayerName);
99 0 : const auto nPos = osStatement.ifind(osFrom);
100 0 : if (nPos != std::string::npos)
101 : {
102 : osStatement =
103 0 : osStatement.substr(0, nPos)
104 0 : .append(" FROM '")
105 0 : .append(OGRDuplicateCharacter(m_osParquetFilename, '\''))
106 0 : .append("'")
107 0 : .append(osStatement.substr(nPos + osFrom.size()));
108 : }
109 : else
110 : {
111 : const std::string osFrom2 =
112 0 : std::string(" FROM \"")
113 0 : .append(OGRDuplicateCharacter(pszSrcLayerName, '"'))
114 0 : .append("\"");
115 0 : const auto nPos2 = osStatement.ifind(osFrom2);
116 0 : if (nPos2 != std::string::npos)
117 : {
118 : osStatement =
119 0 : osStatement.substr(0, nPos2)
120 0 : .append(" FROM '")
121 : .append(
122 0 : OGRDuplicateCharacter(m_osParquetFilename, '\''))
123 0 : .append("'")
124 0 : .append(osStatement.substr(nPos2 + osFrom2.size()));
125 : }
126 : }
127 : }
128 :
129 0 : auto statement = std::make_unique<AdbcStatement>();
130 0 : if (ADBC_CALL(StatementNew, m_connection.get(), statement.get(), error) !=
131 : ADBC_STATUS_OK)
132 : {
133 0 : CPLError(CE_Failure, CPLE_AppDefined, "AdbcStatementNew() failed: %s",
134 : error.message());
135 0 : return nullptr;
136 : }
137 :
138 0 : if (ADBC_CALL(StatementSetSqlQuery, statement.get(), osStatement.c_str(),
139 0 : error) != ADBC_STATUS_OK)
140 : {
141 0 : CPLError(CE_Failure, CPLE_AppDefined,
142 : "AdbcStatementSetSqlQuery() failed: %s", error.message());
143 0 : error.clear();
144 0 : ADBC_CALL(StatementRelease, statement.get(), error);
145 0 : return nullptr;
146 : }
147 :
148 0 : auto stream = std::make_unique<OGRArrowArrayStream>();
149 0 : int64_t rows_affected = -1;
150 0 : if (ADBC_CALL(StatementExecuteQuery, statement.get(), stream->get(),
151 0 : &rows_affected, error) != ADBC_STATUS_OK)
152 : {
153 0 : CPLError(CE_Failure, CPLE_AppDefined,
154 : "AdbcStatementExecuteQuery() failed: %s", error.message());
155 0 : error.clear();
156 0 : ADBC_CALL(StatementRelease, statement.get(), error);
157 0 : return nullptr;
158 : }
159 :
160 0 : ArrowSchema schema = {};
161 0 : if (stream->get_schema(&schema) != 0)
162 : {
163 0 : CPLError(CE_Failure, CPLE_AppDefined, "get_schema() failed");
164 0 : ADBC_CALL(StatementRelease, statement.get(), error);
165 0 : return nullptr;
166 : }
167 :
168 : return std::make_unique<OGRADBCLayer>(
169 0 : this, pszLayerName, osStatement.c_str(), std::move(statement),
170 0 : std::move(stream), &schema, bInternalUse);
171 : }
172 :
173 : /************************************************************************/
174 : /* ExecuteSQL() */
175 : /************************************************************************/
176 :
177 0 : OGRLayer *OGRADBCDataset::ExecuteSQL(const char *pszStatement,
178 : OGRGeometry *poSpatialFilter,
179 : const char *pszDialect)
180 : {
181 0 : if (pszDialect && pszDialect[0] != 0 && !EQUAL(pszDialect, "NATIVE"))
182 : {
183 0 : return GDALDataset::ExecuteSQL(pszStatement, poSpatialFilter,
184 0 : pszDialect);
185 : }
186 :
187 0 : auto poLayer = CreateLayer(pszStatement, "RESULTSET", false);
188 0 : if (poLayer && poSpatialFilter)
189 : {
190 0 : if (poLayer->GetGeomType() == wkbNone)
191 0 : return nullptr;
192 0 : poLayer->SetSpatialFilter(poSpatialFilter);
193 : }
194 0 : return poLayer.release();
195 : }
196 :
197 : /************************************************************************/
198 : /* Open() */
199 : /************************************************************************/
200 :
201 0 : bool OGRADBCDataset::Open(const GDALOpenInfo *poOpenInfo)
202 : {
203 0 : OGRADBCError error;
204 :
205 0 : const char *pszFilename = poOpenInfo->pszFilename;
206 0 : std::unique_ptr<GDALOpenInfo> poTmpOpenInfo;
207 0 : if (STARTS_WITH(pszFilename, "ADBC:"))
208 : {
209 0 : pszFilename += strlen("ADBC:");
210 : poTmpOpenInfo =
211 0 : std::make_unique<GDALOpenInfo>(pszFilename, GA_ReadOnly);
212 0 : poTmpOpenInfo->papszOpenOptions = poOpenInfo->papszOpenOptions;
213 0 : poOpenInfo = poTmpOpenInfo.get();
214 : }
215 : const char *pszADBCDriverName =
216 0 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "ADBC_DRIVER");
217 0 : m_bIsDuckDBDataset = OGRADBCDriverIsDuckDB(poOpenInfo);
218 : const bool bIsSQLite3 =
219 0 : (pszADBCDriverName && EQUAL(pszADBCDriverName, "adbc_driver_sqlite")) ||
220 0 : OGRADBCDriverIsSQLite3(poOpenInfo);
221 : bool bIsParquet =
222 0 : OGRADBCDriverIsParquet(poOpenInfo) ||
223 0 : EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "parquet");
224 0 : const char *pszSQL = CSLFetchNameValue(poOpenInfo->papszOpenOptions, "SQL");
225 0 : if (!bIsParquet && pszSQL)
226 : {
227 0 : CPLString osSQL(pszSQL);
228 0 : auto iPos = osSQL.find("FROM '");
229 0 : if (iPos != std::string::npos)
230 : {
231 0 : iPos += strlen("FROM '");
232 0 : const auto iPos2 = osSQL.find("'", iPos);
233 0 : if (iPos2 != std::string::npos)
234 : {
235 0 : const std::string osFilename = osSQL.substr(iPos, iPos2 - iPos);
236 0 : if (EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(),
237 : "parquet"))
238 : {
239 0 : m_osParquetFilename = osFilename;
240 0 : bIsParquet = true;
241 : }
242 : }
243 : }
244 : }
245 0 : const bool bIsPostgreSQL = STARTS_WITH(pszFilename, "postgresql://");
246 :
247 0 : if (!pszADBCDriverName)
248 : {
249 0 : if (m_bIsDuckDBDataset || bIsParquet)
250 : {
251 0 : pszADBCDriverName =
252 : #ifdef _WIN32
253 : "duckdb.dll"
254 : #elif defined(__MACH__) && defined(__APPLE__)
255 : "libduckdb.dylib"
256 : #else
257 : "libduckdb.so"
258 : #endif
259 : ;
260 : }
261 0 : else if (bIsPostgreSQL)
262 0 : pszADBCDriverName = "adbc_driver_postgresql";
263 0 : else if (bIsSQLite3)
264 : {
265 0 : pszADBCDriverName = "adbc_driver_sqlite";
266 : }
267 : else
268 : {
269 0 : CPLError(CE_Failure, CPLE_AppDefined,
270 : "Open option ADBC_DRIVER must be specified");
271 0 : return false;
272 : }
273 : }
274 :
275 0 : m_bIsDuckDBDriver =
276 0 : m_bIsDuckDBDataset || bIsParquet ||
277 0 : (pszADBCDriverName && strstr(pszADBCDriverName, "duckdb"));
278 :
279 : // Load the driver
280 0 : if (m_bIsDuckDBDriver)
281 : {
282 0 : if (OGRADBCLoadDriver(pszADBCDriverName, "duckdb_adbc_init", &m_driver,
283 0 : error) != ADBC_STATUS_OK)
284 : {
285 0 : CPLError(CE_Failure, CPLE_AppDefined, "AdbcLoadDriver() failed: %s",
286 : error.message());
287 0 : return false;
288 : }
289 : }
290 : else
291 : {
292 0 : if (OGRADBCLoadDriver(pszADBCDriverName, nullptr, &m_driver, error) !=
293 : ADBC_STATUS_OK)
294 : {
295 0 : CPLError(CE_Failure, CPLE_AppDefined, "AdbcLoadDriver() failed: %s",
296 : error.message());
297 0 : return false;
298 : }
299 : }
300 :
301 : // Allocate the database
302 0 : if (ADBC_CALL(DatabaseNew, &m_database, error) != ADBC_STATUS_OK)
303 : {
304 0 : CPLError(CE_Failure, CPLE_AppDefined, "AdbcDatabaseNew() failed: %s",
305 : error.message());
306 0 : return false;
307 : }
308 :
309 : // Set options
310 0 : if (m_bIsDuckDBDriver)
311 : {
312 0 : if (ADBC_CALL(DatabaseSetOption, &m_database, "path",
313 : bIsParquet ? ":memory:" : pszFilename,
314 0 : error) != ADBC_STATUS_OK)
315 : {
316 0 : CPLError(CE_Failure, CPLE_AppDefined,
317 : "AdbcDatabaseSetOption() failed: %s", error.message());
318 0 : return false;
319 : }
320 : }
321 0 : else if (pszFilename[0])
322 : {
323 0 : if (ADBC_CALL(DatabaseSetOption, &m_database, "uri", pszFilename,
324 0 : error) != ADBC_STATUS_OK)
325 : {
326 0 : CPLError(CE_Failure, CPLE_AppDefined,
327 : "AdbcDatabaseSetOption() failed: %s", error.message());
328 0 : return false;
329 : }
330 : }
331 :
332 0 : for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(
333 0 : static_cast<CSLConstList>(poOpenInfo->papszOpenOptions)))
334 : {
335 0 : if (STARTS_WITH_CI(pszKey, "ADBC_OPTION_"))
336 : {
337 0 : if (ADBC_CALL(DatabaseSetOption, &m_database,
338 : pszKey + strlen("ADBC_OPTION_"), pszValue,
339 0 : error) != ADBC_STATUS_OK)
340 : {
341 0 : CPLError(CE_Failure, CPLE_AppDefined,
342 : "AdbcDatabaseSetOption() failed: %s", error.message());
343 0 : return false;
344 : }
345 : }
346 : }
347 :
348 0 : if (ADBC_CALL(DatabaseInit, &m_database, error) != ADBC_STATUS_OK)
349 : {
350 0 : CPLError(CE_Failure, CPLE_AppDefined, "AdbcDatabaseInit() failed: %s",
351 : error.message());
352 0 : return false;
353 : }
354 :
355 0 : m_connection = std::make_unique<AdbcConnection>();
356 0 : if (ADBC_CALL(ConnectionNew, m_connection.get(), error) != ADBC_STATUS_OK)
357 : {
358 0 : CPLError(CE_Failure, CPLE_AppDefined, "AdbcConnectionNew() failed: %s",
359 : error.message());
360 0 : return false;
361 : }
362 :
363 0 : if (ADBC_CALL(ConnectionInit, m_connection.get(), &m_database, error) !=
364 : ADBC_STATUS_OK)
365 : {
366 0 : CPLError(CE_Failure, CPLE_AppDefined, "AdbcConnectionInit() failed: %s",
367 : error.message());
368 0 : return false;
369 : }
370 :
371 0 : char **papszPreludeStatements = CSLFetchNameValueMultiple(
372 0 : poOpenInfo->papszOpenOptions, "PRELUDE_STATEMENTS");
373 0 : for (const char *pszStatement :
374 0 : cpl::Iterate(CSLConstList(papszPreludeStatements)))
375 : {
376 0 : CreateInternalLayer(pszStatement);
377 : }
378 0 : CSLDestroy(papszPreludeStatements);
379 0 : if (m_bIsDuckDBDriver && CPLTestBool(CPLGetConfigOption(
380 : "OGR_ADBC_AUTO_LOAD_DUCKDB_SPATIAL", "ON")))
381 : {
382 : auto poTmpLayer =
383 : CreateInternalLayer("SELECT 1 FROM duckdb_extensions() WHERE "
384 0 : "extension_name='spatial' AND loaded = false");
385 0 : if (poTmpLayer && std::unique_ptr<OGRFeature>(
386 0 : poTmpLayer->GetNextFeature()) != nullptr)
387 : {
388 0 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
389 0 : CreateInternalLayer("LOAD spatial");
390 : }
391 :
392 : poTmpLayer =
393 0 : CreateInternalLayer("SELECT 1 FROM duckdb_extensions() WHERE "
394 0 : "extension_name='spatial' AND loaded = true");
395 0 : m_bSpatialLoaded =
396 0 : poTmpLayer && std::unique_ptr<OGRFeature>(
397 0 : poTmpLayer->GetNextFeature()) != nullptr;
398 : }
399 :
400 0 : std::string osLayerName = "RESULTSET";
401 0 : std::string osSQL;
402 0 : bool bIsParquetLayer = false;
403 0 : if (bIsParquet)
404 : {
405 0 : if (m_osParquetFilename.empty())
406 0 : m_osParquetFilename = pszFilename;
407 0 : osLayerName = CPLGetBasenameSafe(m_osParquetFilename.c_str());
408 0 : if (osLayerName == "*")
409 0 : osLayerName = CPLGetBasenameSafe(
410 0 : CPLGetDirnameSafe(m_osParquetFilename.c_str()).c_str());
411 0 : if (!pszSQL)
412 : {
413 : osSQL =
414 : CPLSPrintf("SELECT * FROM '%s'",
415 0 : OGRDuplicateCharacter(pszFilename, '\'').c_str());
416 0 : pszSQL = osSQL.c_str();
417 0 : bIsParquetLayer = true;
418 : }
419 : }
420 :
421 0 : if (pszSQL)
422 : {
423 0 : if (pszSQL[0])
424 : {
425 0 : std::unique_ptr<OGRADBCLayer> poLayer;
426 0 : if ((bIsParquet || m_bIsDuckDBDataset) && m_bSpatialLoaded)
427 : {
428 0 : std::string osErrorMsg;
429 : {
430 0 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
431 0 : poLayer = CreateLayer(pszSQL, osLayerName.c_str(), false);
432 0 : if (!poLayer)
433 0 : osErrorMsg = CPLGetLastErrorMsg();
434 : }
435 0 : if (!poLayer)
436 : {
437 0 : CPLDebug("ADBC",
438 : "Connecting with 'LOAD spatial' did not work "
439 : "(%s). Retrying without it",
440 : osErrorMsg.c_str());
441 0 : ADBC_CALL(ConnectionRelease, m_connection.get(), error);
442 0 : m_connection.reset();
443 :
444 0 : ADBC_CALL(DatabaseRelease, &m_database, error);
445 0 : memset(&m_database, 0, sizeof(m_database));
446 :
447 0 : if (ADBC_CALL(DatabaseNew, &m_database, error) !=
448 : ADBC_STATUS_OK)
449 : {
450 0 : CPLError(CE_Failure, CPLE_AppDefined,
451 : "AdbcDatabaseNew() failed: %s",
452 : error.message());
453 0 : return false;
454 : }
455 0 : if (ADBC_CALL(DatabaseSetOption, &m_database, "path",
456 0 : ":memory:", error) != ADBC_STATUS_OK)
457 : {
458 0 : CPLError(CE_Failure, CPLE_AppDefined,
459 : "AdbcDatabaseSetOption() failed: %s",
460 : error.message());
461 0 : return false;
462 : }
463 :
464 0 : if (ADBC_CALL(DatabaseInit, &m_database, error) !=
465 : ADBC_STATUS_OK)
466 : {
467 0 : CPLError(CE_Failure, CPLE_AppDefined,
468 : "AdbcDatabaseInit() failed: %s",
469 : error.message());
470 0 : return false;
471 : }
472 :
473 0 : m_connection = std::make_unique<AdbcConnection>();
474 0 : if (ADBC_CALL(ConnectionNew, m_connection.get(), error) !=
475 : ADBC_STATUS_OK)
476 : {
477 0 : CPLError(CE_Failure, CPLE_AppDefined,
478 : "AdbcConnectionNew() failed: %s",
479 : error.message());
480 0 : return false;
481 : }
482 :
483 0 : if (ADBC_CALL(ConnectionInit, m_connection.get(),
484 0 : &m_database, error) != ADBC_STATUS_OK)
485 : {
486 0 : CPLError(CE_Failure, CPLE_AppDefined,
487 : "AdbcConnectionInit() failed: %s",
488 : error.message());
489 0 : return false;
490 : }
491 : }
492 : }
493 0 : if (!poLayer)
494 : {
495 0 : poLayer = CreateLayer(pszSQL, osLayerName.c_str(), false);
496 0 : if (!poLayer)
497 0 : return false;
498 : }
499 :
500 0 : poLayer->m_bIsParquetLayer = bIsParquetLayer;
501 0 : m_apoLayers.emplace_back(std::move(poLayer));
502 : }
503 : }
504 0 : else if (m_bIsDuckDBDataset || bIsSQLite3)
505 : {
506 : auto poLayerList = CreateInternalLayer(
507 0 : "SELECT name FROM sqlite_master WHERE type IN ('table', 'view')");
508 0 : if (!poLayerList || poLayerList->GetLayerDefn()->GetFieldCount() != 1)
509 : {
510 0 : return false;
511 : }
512 :
513 0 : for (const auto &poFeature : poLayerList.get())
514 : {
515 0 : const char *pszLayerName = poFeature->GetFieldAsString(0);
516 0 : if (bIsSQLite3 && EQUAL(pszLayerName, "SpatialIndex"))
517 0 : continue;
518 : const std::string osStatement =
519 : CPLSPrintf("SELECT * FROM \"%s\"",
520 0 : OGRDuplicateCharacter(pszLayerName, '"').c_str());
521 0 : CPLTurnFailureIntoWarning(true);
522 : auto poLayer =
523 0 : CreateLayer(osStatement.c_str(), pszLayerName, false);
524 0 : CPLTurnFailureIntoWarning(false);
525 0 : if (poLayer)
526 : {
527 0 : m_apoLayers.emplace_back(std::move(poLayer));
528 : }
529 0 : }
530 : }
531 0 : else if (bIsPostgreSQL)
532 : {
533 : auto poLayerList = CreateInternalLayer(
534 : "SELECT n.nspname, c.relname FROM pg_class c "
535 : "JOIN pg_namespace n ON c.relnamespace = n.oid "
536 : "AND c.relkind in ('r','v','m','f') "
537 : "AND n.nspname NOT IN ('pg_catalog', 'information_schema') "
538 0 : "ORDER BY c.oid");
539 0 : if (!poLayerList || poLayerList->GetLayerDefn()->GetFieldCount() != 2)
540 : {
541 0 : return false;
542 : }
543 :
544 0 : for (const auto &poFeature : poLayerList.get())
545 : {
546 0 : const char *pszNamespace = poFeature->GetFieldAsString(0);
547 0 : const char *pszTableName = poFeature->GetFieldAsString(1);
548 : const std::string osStatement =
549 : CPLSPrintf("SELECT * FROM \"%s\".\"%s\"",
550 0 : OGRDuplicateCharacter(pszNamespace, '"').c_str(),
551 0 : OGRDuplicateCharacter(pszTableName, '"').c_str());
552 :
553 0 : CPLTurnFailureIntoWarning(true);
554 : auto poLayer = CreateLayer(
555 : osStatement.c_str(),
556 0 : CPLSPrintf("%s.%s", pszNamespace, pszTableName), false);
557 0 : CPLTurnFailureIntoWarning(false);
558 0 : if (poLayer)
559 : {
560 0 : m_apoLayers.emplace_back(std::move(poLayer));
561 : }
562 : }
563 : }
564 :
565 0 : return true;
566 : }
567 :
568 : /************************************************************************/
569 : /* GetLayerByName() */
570 : /************************************************************************/
571 :
572 0 : OGRLayer *OGRADBCDataset::GetLayerByName(const char *pszName)
573 : {
574 0 : OGRLayer *poLayer = GDALDataset::GetLayerByName(pszName);
575 0 : if (poLayer || !EQUAL(pszName, "table_list"))
576 0 : return poLayer;
577 :
578 0 : OGRADBCError error;
579 0 : auto objectsStream = std::make_unique<OGRArrowArrayStream>();
580 0 : ADBC_CALL(ConnectionGetObjects, m_connection.get(),
581 : ADBC_OBJECT_DEPTH_TABLES,
582 : /* catalog = */ nullptr,
583 : /* db_schema = */ nullptr,
584 : /* table_name = */ nullptr,
585 : /* table_type = */ nullptr,
586 : /* column_name = */ nullptr, objectsStream->get(), error);
587 :
588 0 : ArrowSchema schema = {};
589 0 : if (objectsStream->get_schema(&schema) != 0)
590 : {
591 0 : CPLError(CE_Failure, CPLE_AppDefined, "get_schema() failed");
592 0 : return nullptr;
593 : }
594 :
595 0 : auto statement = std::make_unique<AdbcStatement>();
596 0 : OGRADBCLayer tmpLayer(this, "", "", std::move(statement),
597 0 : std::move(objectsStream), &schema,
598 0 : /* bInternalUse = */ true);
599 0 : const auto tmpLayerDefn = tmpLayer.GetLayerDefn();
600 0 : if (tmpLayerDefn->GetFieldIndex("catalog_name") < 0 ||
601 0 : tmpLayerDefn->GetFieldIndex("catalog_db_schemas") < 0)
602 : {
603 0 : return nullptr;
604 : }
605 :
606 : auto poTableListLayer =
607 0 : std::make_unique<OGRMemLayer>("table_list", nullptr, wkbNone);
608 : {
609 0 : OGRFieldDefn oField("catalog_name", OFTString);
610 0 : poTableListLayer->CreateField(&oField);
611 : }
612 : {
613 0 : OGRFieldDefn oField("schema_name", OFTString);
614 0 : poTableListLayer->CreateField(&oField);
615 : }
616 : {
617 0 : OGRFieldDefn oField("table_name", OFTString);
618 0 : poTableListLayer->CreateField(&oField);
619 : }
620 : {
621 0 : OGRFieldDefn oField("table_type", OFTString);
622 0 : poTableListLayer->CreateField(&oField);
623 : }
624 :
625 0 : for (const auto &poFeature : tmpLayer)
626 : {
627 : const char *pszCatalogName =
628 0 : poFeature->GetFieldAsString("catalog_name");
629 : const char *pszCatalogDBSchemas =
630 0 : poFeature->GetFieldAsString("catalog_db_schemas");
631 0 : if (pszCatalogDBSchemas)
632 : {
633 0 : CPLJSONDocument oDoc;
634 0 : if (oDoc.LoadMemory(pszCatalogDBSchemas))
635 : {
636 0 : auto oRoot = oDoc.GetRoot();
637 0 : if (oRoot.GetType() == CPLJSONObject::Type::Array)
638 : {
639 0 : for (const auto &oSchema : oRoot.ToArray())
640 : {
641 0 : if (oSchema.GetType() == CPLJSONObject::Type::Object)
642 : {
643 : const std::string osSchemaName =
644 0 : oSchema.GetString("schema_name");
645 : const auto oTables =
646 0 : oSchema.GetArray("db_schema_tables");
647 0 : if (oTables.IsValid())
648 : {
649 0 : for (const auto &oTable : oTables)
650 : {
651 0 : if (oTable.GetType() ==
652 : CPLJSONObject::Type::Object)
653 : {
654 : const std::string osTableName =
655 0 : oTable.GetString("table_name");
656 : const std::string osTableType =
657 0 : oTable.GetString("table_type");
658 0 : if (!osTableName.empty() &&
659 0 : osTableType != "index" &&
660 0 : osTableType != "trigger")
661 : {
662 : OGRFeature oFeat(
663 : poTableListLayer
664 0 : ->GetLayerDefn());
665 0 : if (pszCatalogName)
666 0 : oFeat.SetField("catalog_name",
667 : pszCatalogName);
668 0 : if (oSchema.GetObj("schema_name")
669 0 : .IsValid())
670 0 : oFeat.SetField(
671 : "schema_name",
672 : osSchemaName.c_str());
673 0 : oFeat.SetField("table_name",
674 : osTableName.c_str());
675 0 : if (oTable.GetObj("table_type")
676 0 : .IsValid())
677 0 : oFeat.SetField(
678 : "table_type",
679 : osTableType.c_str());
680 0 : CPL_IGNORE_RET_VAL(
681 0 : poTableListLayer->CreateFeature(
682 : &oFeat));
683 : }
684 : }
685 : }
686 : }
687 : }
688 : }
689 : }
690 : }
691 : }
692 : }
693 :
694 0 : m_apoLayers.emplace_back(std::move(poTableListLayer));
695 0 : return m_apoLayers.back().get();
696 : }
697 :
698 : #undef ADBC_CALL
|