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