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