LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/adbc - ogradbcdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 40 324 12.3 %
Date: 2025-08-01 10:10:57 Functions: 5 9 55.6 %

          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

Generated by: LCOV version 1.14