LCOV - code coverage report
Current view: top level - frmts/tiledb - tiledbsparse.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2472 2822 87.6 %
Date: 2025-01-18 12:42:00 Functions: 85 90 94.4 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL TileDB Driver
       4             :  * Purpose:  Implement GDAL TileDB Support based on https://www.tiledb.io
       5             :  * Author:   TileDB, Inc
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2023, TileDB, Inc
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "tiledbheaders.h"
      14             : 
      15             : #include "cpl_json.h"
      16             : #include "cpl_time.h"
      17             : #include "ogr_p.h"
      18             : #include "ogr_recordbatch.h"
      19             : #include "ogr_swq.h"
      20             : 
      21             : #include <algorithm>
      22             : #include <limits>
      23             : 
      24             : constexpr int SECONDS_PER_DAY = 3600 * 24;
      25             : 
      26             : /************************************************************************/
      27             : /* ==================================================================== */
      28             : /*                            ProcessField                              */
      29             : /* ==================================================================== */
      30             : /************************************************************************/
      31             : 
      32             : namespace
      33             : {
      34             : 
      35             : template <tiledb_datatype_t> struct GetType
      36             : {
      37             : };
      38             : 
      39             : template <> struct GetType<TILEDB_INT32>
      40             : {
      41             :     using EltType = std::vector<int32_t>;
      42             : };
      43             : 
      44             : template <> struct GetType<TILEDB_INT16>
      45             : {
      46             :     using EltType = std::vector<int16_t>;
      47             : };
      48             : 
      49             : template <> struct GetType<TILEDB_UINT8>
      50             : {
      51             :     using EltType = std::vector<uint8_t>;
      52             : };
      53             : 
      54             : template <> struct GetType<TILEDB_UINT16>
      55             : {
      56             :     using EltType = std::vector<uint16_t>;
      57             : };
      58             : 
      59             : template <> struct GetType<TILEDB_BOOL>
      60             : {
      61             :     using EltType = VECTOR_OF_BOOL;
      62             : };
      63             : 
      64             : template <> struct GetType<TILEDB_INT64>
      65             : {
      66             :     using EltType = std::vector<int64_t>;
      67             : };
      68             : 
      69             : template <> struct GetType<TILEDB_FLOAT64>
      70             : {
      71             :     using EltType = std::vector<double>;
      72             : };
      73             : 
      74             : template <> struct GetType<TILEDB_FLOAT32>
      75             : {
      76             :     using EltType = std::vector<float>;
      77             : };
      78             : 
      79             : template <> struct GetType<TILEDB_STRING_ASCII>
      80             : {
      81             :     using EltType = std::string;
      82             : };
      83             : 
      84             : template <> struct GetType<TILEDB_STRING_UTF8>
      85             : {
      86             :     using EltType = std::string;
      87             : };
      88             : 
      89             : template <> struct GetType<TILEDB_BLOB>
      90             : {
      91             :     using EltType = std::vector<uint8_t>;
      92             : };
      93             : 
      94             : template <> struct GetType<TILEDB_DATETIME_DAY>
      95             : {
      96             :     using EltType = std::vector<int64_t>;
      97             : };
      98             : 
      99             : template <> struct GetType<TILEDB_DATETIME_MS>
     100             : {
     101             :     using EltType = std::vector<int64_t>;
     102             : };
     103             : 
     104             : template <> struct GetType<TILEDB_TIME_MS>
     105             : {
     106             :     using EltType = std::vector<int64_t>;
     107             : };
     108             : 
     109             : template <template <class> class Func> struct ProcessField
     110             : {
     111        9428 :     static void exec(tiledb_datatype_t eType, OGRTileDBLayer::ArrayType &array)
     112             :     {
     113        9428 :         switch (eType)
     114             :         {
     115        1446 :             case TILEDB_INT32:
     116        1446 :                 Func<GetType<TILEDB_INT32>::EltType>::exec(array);
     117        1446 :                 break;
     118         958 :             case TILEDB_INT16:
     119         958 :                 Func<GetType<TILEDB_INT16>::EltType>::exec(array);
     120         958 :                 break;
     121         479 :             case TILEDB_UINT8:
     122         479 :                 Func<GetType<TILEDB_UINT8>::EltType>::exec(array);
     123         479 :                 break;
     124         479 :             case TILEDB_UINT16:
     125         479 :                 Func<GetType<TILEDB_UINT16>::EltType>::exec(array);
     126         479 :                 break;
     127         958 :             case TILEDB_BOOL:
     128         958 :                 Func<GetType<TILEDB_BOOL>::EltType>::exec(array);
     129         958 :                 break;
     130         583 :             case TILEDB_INT64:
     131         583 :                 Func<GetType<TILEDB_INT64>::EltType>::exec(array);
     132         583 :                 break;
     133         958 :             case TILEDB_FLOAT32:
     134         958 :                 Func<GetType<TILEDB_FLOAT32>::EltType>::exec(array);
     135         958 :                 break;
     136        1062 :             case TILEDB_FLOAT64:
     137        1062 :                 Func<GetType<TILEDB_FLOAT64>::EltType>::exec(array);
     138        1062 :                 break;
     139           0 :             case TILEDB_STRING_ASCII:
     140           0 :                 Func<GetType<TILEDB_STRING_ASCII>::EltType>::exec(array);
     141           0 :                 break;
     142         589 :             case TILEDB_STRING_UTF8:
     143         589 :                 Func<GetType<TILEDB_STRING_UTF8>::EltType>::exec(array);
     144         589 :                 break;
     145         479 :             case TILEDB_BLOB:
     146         479 :                 Func<GetType<TILEDB_BLOB>::EltType>::exec(array);
     147         479 :                 break;
     148         479 :             case TILEDB_DATETIME_DAY:
     149         479 :                 Func<GetType<TILEDB_DATETIME_DAY>::EltType>::exec(array);
     150         479 :                 break;
     151         479 :             case TILEDB_DATETIME_MS:
     152         479 :                 Func<GetType<TILEDB_DATETIME_MS>::EltType>::exec(array);
     153         479 :                 break;
     154         479 :             case TILEDB_TIME_MS:
     155         479 :                 Func<GetType<TILEDB_TIME_MS>::EltType>::exec(array);
     156         479 :                 break;
     157             : 
     158           0 :             default:
     159             :             {
     160           0 :                 CPLAssert(false);
     161             :                 break;
     162             :             }
     163             :         }
     164        9428 :     }
     165             : };
     166             : 
     167             : }  // namespace
     168             : 
     169             : /************************************************************************/
     170             : /* ==================================================================== */
     171             : /*                            OGRTileDBDataset                          */
     172             : /* ==================================================================== */
     173             : /************************************************************************/
     174             : 
     175             : /************************************************************************/
     176             : /*                         OGRTileDBDataset()                           */
     177             : /************************************************************************/
     178             : 
     179          88 : OGRTileDBDataset::OGRTileDBDataset()
     180             : {
     181          88 : }
     182             : 
     183             : /************************************************************************/
     184             : /*                         ~OGRTileDBDataset()                          */
     185             : /************************************************************************/
     186             : 
     187         176 : OGRTileDBDataset::~OGRTileDBDataset()
     188             : {
     189         176 : }
     190             : 
     191             : /************************************************************************/
     192             : /*                                Open()                                */
     193             : /************************************************************************/
     194             : 
     195          38 : GDALDataset *OGRTileDBDataset::Open(GDALOpenInfo *poOpenInfo,
     196             :                                     tiledb::Object::Type objectType)
     197             : 
     198             : {
     199          76 :     auto poDS = std::make_unique<OGRTileDBDataset>();
     200          38 :     poDS->eAccess = poOpenInfo->eAccess;
     201             :     const char *pszConfig =
     202          38 :         CSLFetchNameValue(poOpenInfo->papszOpenOptions, "TILEDB_CONFIG");
     203             : 
     204          76 :     const char *pszTimestamp = CSLFetchNameValueDef(
     205          38 :         poOpenInfo->papszOpenOptions, "TILEDB_TIMESTAMP", "0");
     206          38 :     const uint64_t nTimestamp = std::strtoull(pszTimestamp, nullptr, 10);
     207             : 
     208          38 :     if (pszConfig != nullptr)
     209             :     {
     210           0 :         tiledb::Config cfg(pszConfig);
     211           0 :         poDS->m_ctx.reset(new tiledb::Context(cfg));
     212             :     }
     213             :     else
     214             :     {
     215          76 :         tiledb::Config cfg;
     216          38 :         cfg["sm.enable_signal_handlers"] = "false";
     217          38 :         poDS->m_ctx.reset(new tiledb::Context(cfg));
     218             :     }
     219             : 
     220             :     std::string osFilename(
     221          76 :         TileDBDataset::VSI_to_tiledb_uri(poOpenInfo->pszFilename));
     222          38 :     if (osFilename.back() == '/')
     223           0 :         osFilename.pop_back();
     224             : 
     225          42 :     const auto AddLayer = [&poDS, nTimestamp, poOpenInfo](
     226             :                               const std::string &osLayerFilename,
     227             :                               const std::optional<std::string> &osLayerName =
     228         246 :                                   std::optional<std::string>())
     229             :     {
     230             :         auto poLayer = std::make_unique<OGRTileDBLayer>(
     231          42 :             poDS.get(), osLayerFilename.c_str(),
     232          42 :             osLayerName.has_value()
     233          77 :                 ? (*osLayerName).c_str()
     234          77 :                 : CPLGetBasenameSafe(osLayerFilename.c_str()).c_str(),
     235         126 :             wkbUnknown, nullptr);
     236          42 :         poLayer->m_bUpdatable = poOpenInfo->eAccess == GA_Update;
     237          84 :         if (!poLayer->InitFromStorage(poDS->m_ctx.get(), nTimestamp,
     238          42 :                                       poOpenInfo->papszOpenOptions))
     239             :         {
     240           2 :             poLayer->m_array.reset();
     241           2 :             return false;
     242             :         }
     243             : 
     244          40 :         int nBatchSize = atoi(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     245             :                                                    "BATCH_SIZE", "0"));
     246          40 :         poLayer->m_nBatchSize =
     247          40 :             nBatchSize <= 0 ? DEFAULT_BATCH_SIZE : nBatchSize;
     248             : 
     249          80 :         poLayer->m_bStats =
     250          40 :             CPLFetchBool(poOpenInfo->papszOpenOptions, "STATS", false);
     251             : 
     252          40 :         poDS->m_apoLayers.emplace_back(std::move(poLayer));
     253          40 :         return true;
     254          38 :     };
     255             : 
     256          38 :     CPL_IGNORE_RET_VAL(objectType);
     257          38 :     if (objectType == tiledb::Object::Type::Group)
     258             :     {
     259           3 :         poDS->m_osGroupName = osFilename;
     260           9 :         tiledb::Group group(*(poDS->m_ctx), osFilename.c_str(), TILEDB_READ);
     261          10 :         for (uint64_t i = 0; i < group.member_count(); ++i)
     262             :         {
     263          14 :             auto obj = group.member(i);
     264           7 :             if (obj.type() == tiledb::Object::Type::Array)
     265             :             {
     266          14 :                 tiledb::ArraySchema schema(*(poDS->m_ctx), obj.uri());
     267           7 :                 if (schema.array_type() == TILEDB_SPARSE)
     268             :                 {
     269           7 :                     AddLayer(obj.uri(), obj.name());
     270             :                 }
     271             :             }
     272             :         }
     273             :     }
     274             :     else
     275             :     {
     276          35 :         if (!AddLayer(osFilename))
     277           2 :             return nullptr;
     278             :     }
     279             : 
     280          36 :     return poDS.release();
     281             : }
     282             : 
     283             : /************************************************************************/
     284             : /*                           TestCapability()                           */
     285             : /************************************************************************/
     286             : 
     287          36 : int OGRTileDBDataset::TestCapability(const char *pszCap)
     288             : {
     289          36 :     if (EQUAL(pszCap, ODsCCreateLayer))
     290             :     {
     291          46 :         return eAccess == GA_Update &&
     292          46 :                (m_apoLayers.empty() || !m_osGroupName.empty());
     293             :     }
     294          14 :     if (EQUAL(pszCap, ODsCCurveGeometries) ||
     295           7 :         EQUAL(pszCap, ODsCMeasuredGeometries) || EQUAL(pszCap, ODsCZGeometries))
     296             :     {
     297          10 :         return TRUE;
     298             :     }
     299           4 :     return FALSE;
     300             : }
     301             : 
     302             : /***********************************************************************/
     303             : /*                            ExecuteSQL()                             */
     304             : /***********************************************************************/
     305             : 
     306           6 : OGRLayer *OGRTileDBDataset::ExecuteSQL(const char *pszSQLCommand,
     307             :                                        OGRGeometry *poSpatialFilter,
     308             :                                        const char *pszDialect)
     309             : {
     310           6 :     return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter, pszDialect);
     311             : }
     312             : 
     313             : /***********************************************************************/
     314             : /*                           ICreateLayer()                            */
     315             : /***********************************************************************/
     316             : OGRLayer *
     317          56 : OGRTileDBDataset::ICreateLayer(const char *pszName,
     318             :                                const OGRGeomFieldDefn *poGeomFieldDefn,
     319             :                                CSLConstList papszOptions)
     320             : {
     321          56 :     if (eAccess != GA_Update)
     322             :     {
     323           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     324             :                  "CreateLayer() failed: dataset in read-only mode");
     325           1 :         return nullptr;
     326             :     }
     327             : 
     328          55 :     if (!m_osGroupName.empty() && strchr(pszName, '/'))
     329             :     {
     330             :         // Otherwise a layer name wit ha slash when groups are enabled causes
     331             :         // a "[TileDB::Array] Error: FragmentID: input URI is invalid. Provided URI does not contain a fragment name."
     332             :         // exception on re-opening starting with TileDB 2.21
     333           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     334             :                  "Slash is not supported in layer name");
     335           0 :         return nullptr;
     336             :     }
     337             : 
     338          55 :     const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
     339             :     const auto poSpatialRef =
     340          55 :         poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
     341             : 
     342          55 :     if (m_osGroupName.empty() && !m_apoLayers.empty())
     343             :     {
     344           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     345             :                  "CreateLayer() failed: no more than one layer per dataset "
     346             :                  "supported on a array object. Create a dataset with the "
     347             :                  "CREATE_GROUP=YES creation option or open such group "
     348             :                  "to enable multiple layer creation.");
     349           1 :         return nullptr;
     350             :     }
     351             : 
     352          54 :     if (eGType == wkbNone)
     353             :     {
     354           2 :         CPLError(CE_Failure, CPLE_NotSupported,
     355             :                  "CreateLayer() failed: non-spatial layer not supported");
     356           2 :         return nullptr;
     357             :     }
     358             : 
     359         104 :     std::string osFilename = GetDescription();
     360          52 :     if (!m_osGroupName.empty())
     361             :     {
     362             :         osFilename =
     363           3 :             CPLFormFilenameSafe(m_osGroupName.c_str(), "layers", nullptr);
     364           6 :         if (!STARTS_WITH(m_osGroupName.c_str(), "s3://") &&
     365           3 :             !STARTS_WITH(m_osGroupName.c_str(), "gcs://"))
     366             :         {
     367             :             VSIStatBufL sStat;
     368           3 :             if (VSIStatL(osFilename.c_str(), &sStat) != 0)
     369           1 :                 VSIMkdir(osFilename.c_str(), 0755);
     370             :         }
     371           3 :         osFilename = CPLFormFilenameSafe(osFilename.c_str(), pszName, nullptr);
     372             :     }
     373             :     auto poLayer = std::make_unique<OGRTileDBLayer>(
     374         104 :         this, osFilename.c_str(), pszName, eGType, poSpatialRef);
     375          52 :     poLayer->m_bUpdatable = true;
     376          52 :     poLayer->m_ctx.reset(new tiledb::Context(*m_ctx));
     377          52 :     poLayer->m_osGroupName = m_osGroupName;
     378             : 
     379          52 :     const char *pszBounds = CSLFetchNameValue(papszOptions, "BOUNDS");
     380          52 :     if (pszBounds)
     381             :     {
     382          25 :         CPLStringList aosBounds(CSLTokenizeString2(pszBounds, ",", 0));
     383          25 :         if (aosBounds.Count() == 4)
     384             :         {
     385          23 :             poLayer->m_dfXStart = CPLAtof(aosBounds[0]);
     386          23 :             poLayer->m_dfYStart = CPLAtof(aosBounds[1]);
     387          23 :             poLayer->m_dfXEnd = CPLAtof(aosBounds[2]);
     388          23 :             poLayer->m_dfYEnd = CPLAtof(aosBounds[3]);
     389             :         }
     390           2 :         else if (aosBounds.Count() == 6)
     391             :         {
     392           1 :             poLayer->m_dfXStart = CPLAtof(aosBounds[0]);
     393           1 :             poLayer->m_dfYStart = CPLAtof(aosBounds[1]);
     394           1 :             poLayer->m_dfZStart = CPLAtof(aosBounds[2]);
     395           1 :             poLayer->m_dfXEnd = CPLAtof(aosBounds[3]);
     396           1 :             poLayer->m_dfYEnd = CPLAtof(aosBounds[4]);
     397           1 :             poLayer->m_dfZEnd = CPLAtof(aosBounds[5]);
     398             :         }
     399             :         else
     400             :         {
     401           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     402             :                      "Domain bounds specified as minx,miny,maxx,maxy or "
     403             :                      "minx,miny,minx,maxx,maxy,maxz are "
     404             :                      "required for array creation.");
     405           1 :             return nullptr;
     406             :         }
     407             :     }
     408          27 :     else if (poSpatialRef && poSpatialRef->IsGeographic())
     409             :     {
     410           8 :         poLayer->m_dfXStart = -360;
     411           8 :         poLayer->m_dfXEnd = 360;
     412           8 :         poLayer->m_dfYStart = -90;
     413           8 :         poLayer->m_dfYEnd = 90;
     414             :     }
     415          19 :     else if (poSpatialRef && poSpatialRef->IsProjected())
     416             :     {
     417             :         // Should hopefully be sufficiently large for most projections...
     418             :         // For example the eastings of Mercator go between [-PI * a, PI * a]
     419             :         // so we take a 2x margin here.
     420           2 :         const double dfBounds = 2 * M_PI * poSpatialRef->GetSemiMajor();
     421           2 :         poLayer->m_dfXStart = -dfBounds;
     422           2 :         poLayer->m_dfXEnd = dfBounds;
     423           2 :         poLayer->m_dfYStart = -dfBounds;
     424           2 :         poLayer->m_dfYEnd = dfBounds;
     425             :     }
     426             :     else
     427             :     {
     428          17 :         CPLError(CE_Failure, CPLE_AppDefined,
     429             :                  "Domain bounds must be specified with the BOUNDS layer "
     430             :                  "creation option.");
     431          17 :         return nullptr;
     432             :     }
     433             : 
     434             :     int nBatchSize =
     435          34 :         atoi(CSLFetchNameValueDef(papszOptions, "BATCH_SIZE", "0"));
     436          34 :     poLayer->m_nBatchSize = nBatchSize <= 0 ? DEFAULT_BATCH_SIZE : nBatchSize;
     437             : 
     438             :     int nTileCapacity =
     439          34 :         atoi(CSLFetchNameValueDef(papszOptions, "TILE_CAPACITY", "0"));
     440          34 :     poLayer->m_nTileCapacity =
     441          34 :         nTileCapacity <= 0 ? DEFAULT_TILE_CAPACITY : nTileCapacity;
     442             : 
     443          34 :     poLayer->m_bStats = CPLFetchBool(papszOptions, "STATS", false);
     444             : 
     445          68 :     poLayer->m_dfTileExtent =
     446          34 :         std::min(poLayer->m_dfYEnd - poLayer->m_dfYStart,
     447          68 :                  poLayer->m_dfXEnd - poLayer->m_dfXStart) /
     448             :         10;
     449             : 
     450          34 :     const char *pszTileExtent = CSLFetchNameValue(papszOptions, "TILE_EXTENT");
     451          34 :     if (pszTileExtent)
     452           0 :         poLayer->m_dfTileExtent = CPLAtof(pszTileExtent);
     453             : 
     454          34 :     if (wkbHasZ(eGType) || eGType == wkbUnknown)
     455             :     {
     456          16 :         poLayer->m_osZDim = "_Z";
     457          32 :         poLayer->m_dfZTileExtent =
     458          16 :             (poLayer->m_dfZEnd - poLayer->m_dfZStart) / 2;
     459             : 
     460             :         const char *pszZTileExtent =
     461          16 :             CSLFetchNameValue(papszOptions, "TILE_Z_EXTENT");
     462          16 :         if (pszZTileExtent)
     463           0 :             poLayer->m_dfZTileExtent = CPLAtof(pszZTileExtent);
     464             :     }
     465             : 
     466          34 :     const char *pszAddZDim = CSLFetchNameValue(papszOptions, "ADD_Z_DIM");
     467          34 :     if (pszAddZDim && !EQUAL(pszAddZDim, "AUTO") && !CPLTestBool(pszAddZDim))
     468           0 :         poLayer->m_osZDim.clear();
     469             : 
     470             :     const char *pszTimestamp =
     471          34 :         CSLFetchNameValueDef(papszOptions, "TILEDB_TIMESTAMP", "0");
     472          34 :     poLayer->m_nTimestamp = std::strtoull(pszTimestamp, nullptr, 10);
     473             : 
     474          34 :     const char *pszCompression = CSLFetchNameValue(papszOptions, "COMPRESSION");
     475             :     const char *pszCompressionLevel =
     476          34 :         CSLFetchNameValue(papszOptions, "COMPRESSION_LEVEL");
     477             : 
     478          34 :     poLayer->m_filterList.reset(new tiledb::FilterList(*poLayer->m_ctx));
     479          34 :     if (pszCompression != nullptr)
     480             :     {
     481           1 :         int nLevel = (pszCompressionLevel) ? atoi(pszCompressionLevel) : -1;
     482           1 :         TileDBDataset::AddFilter(*(poLayer->m_ctx.get()),
     483           1 :                                  *(poLayer->m_filterList.get()), pszCompression,
     484             :                                  nLevel);
     485             :     }
     486             : 
     487          34 :     poLayer->m_osFIDColumn = CSLFetchNameValueDef(papszOptions, "FID", "FID");
     488             : 
     489             :     const char *pszGeomColName =
     490          34 :         CSLFetchNameValueDef(papszOptions, "GEOMETRY_NAME", "wkb_geometry");
     491          34 :     if (EQUAL(pszGeomColName, "") && wkbFlatten(eGType) != wkbPoint)
     492             :     {
     493           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     494             :                  "GEOMETRY_NAME must be defined to a non-empty string "
     495             :                  "for layers whose geometry type is not Point.");
     496           0 :         return nullptr;
     497             :     }
     498          34 :     poLayer->m_poFeatureDefn->GetGeomFieldDefn(0)->SetName(pszGeomColName);
     499             : 
     500          34 :     poLayer->m_eCurrentMode = OGRTileDBLayer::CurrentMode::WriteInProgress;
     501             : 
     502             :     const char *pszTileDBStringType =
     503          34 :         CSLFetchNameValue(papszOptions, "TILEDB_STRING_TYPE");
     504          34 :     if (pszTileDBStringType)
     505             :     {
     506           0 :         if (EQUAL(pszTileDBStringType, "ASCII"))
     507           0 :             poLayer->m_eTileDBStringType = TILEDB_STRING_ASCII;
     508           0 :         else if (EQUAL(pszTileDBStringType, "UTF8"))
     509           0 :             poLayer->m_eTileDBStringType = TILEDB_STRING_UTF8;
     510             :     }
     511             : 
     512          34 :     m_apoLayers.emplace_back(std::move(poLayer));
     513             : 
     514          34 :     return m_apoLayers.back().get();
     515             : }
     516             : 
     517             : /************************************************************************/
     518             : /*                          Create()                                    */
     519             : /************************************************************************/
     520             : 
     521          50 : GDALDataset *OGRTileDBDataset::Create(const char *pszFilename,
     522             :                                       CSLConstList papszOptions)
     523             : {
     524         100 :     auto poDS = std::make_unique<OGRTileDBDataset>();
     525          50 :     poDS->SetDescription(TileDBDataset::VSI_to_tiledb_uri(pszFilename));
     526          50 :     poDS->eAccess = GA_Update;
     527             : 
     528          50 :     const char *pszConfig = CSLFetchNameValue(papszOptions, "TILEDB_CONFIG");
     529          50 :     if (pszConfig != nullptr)
     530             :     {
     531           0 :         tiledb::Config cfg(pszConfig);
     532           0 :         poDS->m_ctx.reset(new tiledb::Context(cfg));
     533             :     }
     534             :     else
     535             :     {
     536          50 :         poDS->m_ctx.reset(new tiledb::Context());
     537             :     }
     538             : 
     539          50 :     if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "CREATE_GROUP", "NO")))
     540             :     {
     541             :         try
     542             :         {
     543           1 :             tiledb::create_group(*(poDS->m_ctx.get()), poDS->GetDescription());
     544             :         }
     545           0 :         catch (const std::exception &e)
     546             :         {
     547           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     548           0 :             return nullptr;
     549             :         }
     550           1 :         poDS->m_osGroupName = poDS->GetDescription();
     551             :     }
     552             : 
     553          50 :     return poDS.release();
     554             : }
     555             : 
     556             : /************************************************************************/
     557             : /* ==================================================================== */
     558             : /*                            OGRTileDBLayer                            */
     559             : /* ==================================================================== */
     560             : /************************************************************************/
     561             : 
     562             : /************************************************************************/
     563             : /*                         OGRTileDBLayer()                             */
     564             : /************************************************************************/
     565             : 
     566          94 : OGRTileDBLayer::OGRTileDBLayer(GDALDataset *poDS, const char *pszFilename,
     567             :                                const char *pszLayerName,
     568             :                                const OGRwkbGeometryType eGType,
     569          94 :                                const OGRSpatialReference *poSRS)
     570             :     : m_poDS(poDS), m_osFilename(pszFilename),
     571          94 :       m_poFeatureDefn(new OGRFeatureDefn(pszLayerName)),
     572           0 :       m_pbLayerStillAlive(std::make_shared<bool>(true)),
     573             :       m_anFIDs(std::make_shared<std::vector<int64_t>>()),
     574             :       m_adfXs(std::make_shared<std::vector<double>>()),
     575             :       m_adfYs(std::make_shared<std::vector<double>>()),
     576             :       m_adfZs(std::make_shared<std::vector<double>>()),
     577             :       m_abyGeometries(std::make_shared<std::vector<unsigned char>>()),
     578         188 :       m_anGeometryOffsets(std::make_shared<std::vector<uint64_t>>())
     579             : {
     580          94 :     m_poFeatureDefn->SetGeomType(eGType);
     581             : 
     582          94 :     if (poSRS)
     583             :     {
     584          10 :         auto poSRSClone = poSRS->Clone();
     585          10 :         m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRSClone);
     586          10 :         poSRSClone->Release();
     587             :     }
     588             : 
     589          94 :     m_poFeatureDefn->Reference();
     590             : 
     591          94 :     SetDescription(pszLayerName);
     592          94 : }
     593             : 
     594             : /************************************************************************/
     595             : /*                         ~OGRTileDBLayer()                            */
     596             : /************************************************************************/
     597             : 
     598         188 : OGRTileDBLayer::~OGRTileDBLayer()
     599             : {
     600          94 :     *m_pbLayerStillAlive = false;
     601             : 
     602             :     try
     603             :     {
     604          94 :         if (m_bUpdatable && !m_bInitializationAttempted && m_filterList)
     605             :         {
     606           4 :             InitializeSchemaAndArray();
     607             :         }
     608          94 :         if (m_array && m_bUpdatable)
     609             :         {
     610          38 :             SwitchToWritingMode();
     611             :         }
     612          94 :         if (m_array)
     613             :         {
     614          74 :             if (m_bUpdatable)
     615             :             {
     616          38 :                 if (m_bInitialized && !m_adfXs->empty())
     617             :                 {
     618          27 :                     FlushArrays();
     619             :                 }
     620             : 
     621             :                 // write the pad metadata
     622          38 :                 m_array->put_metadata("PAD_X", TILEDB_FLOAT64, 1, &m_dfPadX);
     623          38 :                 m_array->put_metadata("PAD_Y", TILEDB_FLOAT64, 1, &m_dfPadY);
     624          38 :                 if (m_dfPadZ != 0)
     625           0 :                     m_array->put_metadata("PAD_Z", TILEDB_FLOAT64, 1,
     626           0 :                                           &m_dfPadZ);
     627             : 
     628          38 :                 if (m_nTotalFeatureCount >= 0)
     629          66 :                     m_array->put_metadata("FEATURE_COUNT", TILEDB_INT64, 1,
     630          33 :                                           &m_nTotalFeatureCount);
     631             : 
     632          38 :                 if (m_oLayerExtent.IsInit())
     633             :                 {
     634          62 :                     m_array->put_metadata("LAYER_EXTENT_MINX", TILEDB_FLOAT64,
     635          31 :                                           1, &m_oLayerExtent.MinX);
     636          62 :                     m_array->put_metadata("LAYER_EXTENT_MINY", TILEDB_FLOAT64,
     637          31 :                                           1, &m_oLayerExtent.MinY);
     638          62 :                     m_array->put_metadata("LAYER_EXTENT_MAXX", TILEDB_FLOAT64,
     639          31 :                                           1, &m_oLayerExtent.MaxX);
     640          62 :                     m_array->put_metadata("LAYER_EXTENT_MAXY", TILEDB_FLOAT64,
     641          31 :                                           1, &m_oLayerExtent.MaxY);
     642             :                 }
     643             :             }
     644             : 
     645          74 :             m_array->close();
     646             :         }
     647             :     }
     648           0 :     catch (const std::exception &e)
     649             :     {
     650           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     651             :     }
     652             : 
     653          94 :     if (m_poFeatureDefn)
     654          94 :         m_poFeatureDefn->Release();
     655         188 : }
     656             : 
     657             : /************************************************************************/
     658             : /*                         InitFromStorage()                            */
     659             : /************************************************************************/
     660             : 
     661          42 : bool OGRTileDBLayer::InitFromStorage(tiledb::Context *poCtx,
     662             :                                      uint64_t nTimestamp,
     663             :                                      CSLConstList papszOpenOptions)
     664             : {
     665          42 :     m_bInitialized = true;
     666          42 :     m_bInitializationAttempted = true;
     667          42 :     m_ctx.reset(new tiledb::Context(*poCtx));
     668          42 :     m_schema.reset(new tiledb::ArraySchema(*m_ctx, m_osFilename));
     669          42 :     m_nTimestamp = nTimestamp;
     670             : 
     671          42 :     m_filterList.reset(new tiledb::FilterList(*m_ctx));
     672             : 
     673          84 :     CPLJSONObject oJson;
     674          84 :     CPLJSONObject oSchema;
     675          42 :     oJson.Add("schema", oSchema);
     676             : 
     677             :     {
     678          84 :         const auto filters = m_schema->coords_filter_list();
     679          42 :         CPLJSONArray oCoordsFilterList;
     680          43 :         for (uint32_t j = 0; j < filters.nfilters(); ++j)
     681             :         {
     682           1 :             const auto filter = filters.filter(j);
     683           1 :             oCoordsFilterList.Add(tiledb::Filter::to_str(filter.filter_type()));
     684             :         }
     685          42 :         oSchema.Add("coords_filter_list", oCoordsFilterList);
     686             :     }
     687             : 
     688          42 :     if (m_nTimestamp)
     689           0 :         m_array.reset(new tiledb::Array(
     690           0 :             *m_ctx, m_osFilename, TILEDB_READ,
     691           0 :             tiledb::TemporalPolicy(tiledb::TimeTravel, m_nTimestamp)));
     692             :     else
     693          42 :         m_array.reset(new tiledb::Array(*m_ctx, m_osFilename, TILEDB_READ));
     694             : 
     695          84 :     const auto domain = m_schema->domain();
     696          42 :     if (domain.ndim() < 2)
     697             :     {
     698           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     699             :                  "Domain should have at least 2 dimensions");
     700           0 :         return false;
     701             :     }
     702             : 
     703         181 :     const auto CreateField = [this](const std::string &osName,
     704             :                                     tiledb_datatype_t type, bool bIsSingle,
     705         543 :                                     bool bIsNullable)
     706             :     {
     707         181 :         bool bOK = true;
     708         181 :         OGRFieldType eType = OFTString;
     709         181 :         OGRFieldSubType eSubType = OFSTNone;
     710         181 :         auto &fieldValues = m_aFieldValues;
     711         181 :         switch (type)
     712             :         {
     713           8 :             case TILEDB_UINT16:
     714           8 :                 eType = bIsSingle ? OFTInteger : OFTIntegerList;
     715           8 :                 fieldValues.push_back(
     716          16 :                     std::make_shared<std::vector<uint16_t>>());
     717           8 :                 break;
     718          27 :             case TILEDB_INT32:
     719          27 :                 eType = bIsSingle ? OFTInteger : OFTIntegerList;
     720          27 :                 fieldValues.push_back(std::make_shared<std::vector<int32_t>>());
     721          27 :                 break;
     722          11 :             case TILEDB_INT64:
     723          11 :                 eType = bIsSingle ? OFTInteger64 : OFTInteger64List;
     724          11 :                 fieldValues.push_back(std::make_shared<std::vector<int64_t>>());
     725          11 :                 break;
     726          18 :             case TILEDB_FLOAT32:
     727          18 :                 eType = bIsSingle ? OFTReal : OFTRealList;
     728          18 :                 eSubType = OFSTFloat32;
     729          18 :                 fieldValues.push_back(std::make_shared<std::vector<float>>());
     730          18 :                 break;
     731          21 :             case TILEDB_FLOAT64:
     732          21 :                 eType = bIsSingle ? OFTReal : OFTRealList;
     733          21 :                 fieldValues.push_back(std::make_shared<std::vector<double>>());
     734          21 :                 break;
     735          18 :             case TILEDB_INT16:
     736          18 :                 eType = bIsSingle ? OFTInteger : OFTIntegerList;
     737          18 :                 eSubType = OFSTInt16;
     738          18 :                 fieldValues.push_back(std::make_shared<std::vector<int16_t>>());
     739          18 :                 break;
     740          16 :             case TILEDB_STRING_ASCII:
     741             :             case TILEDB_STRING_UTF8:
     742          16 :                 eType = OFTString;
     743          16 :                 fieldValues.push_back(std::make_shared<std::string>());
     744          16 :                 break;
     745          18 :             case TILEDB_BOOL:
     746          18 :                 eType = bIsSingle ? OFTInteger : OFTIntegerList;
     747          18 :                 eSubType = OFSTBoolean;
     748          18 :                 fieldValues.push_back(std::make_shared<VECTOR_OF_BOOL>());
     749          18 :                 break;
     750           9 :             case TILEDB_DATETIME_DAY:
     751           9 :                 eType = OFTDate;
     752           9 :                 fieldValues.push_back(std::make_shared<std::vector<int64_t>>());
     753           9 :                 break;
     754           9 :             case TILEDB_DATETIME_MS:
     755           9 :                 eType = OFTDateTime;
     756           9 :                 fieldValues.push_back(std::make_shared<std::vector<int64_t>>());
     757           9 :                 break;
     758           9 :             case TILEDB_TIME_MS:
     759           9 :                 eType = OFTTime;
     760           9 :                 fieldValues.push_back(std::make_shared<std::vector<int64_t>>());
     761           9 :                 break;
     762           8 :             case TILEDB_UINT8:
     763           8 :                 eType = bIsSingle ? OFTInteger : OFTBinary;
     764           8 :                 fieldValues.push_back(std::make_shared<std::vector<uint8_t>>());
     765           8 :                 break;
     766           9 :             case TILEDB_BLOB:
     767             :             {
     768           9 :                 if (bIsSingle)
     769             :                 {
     770           0 :                     bOK = false;
     771           0 :                     const char *pszTypeName = "";
     772           0 :                     tiledb_datatype_to_str(type, &pszTypeName);
     773           0 :                     CPLError(
     774             :                         CE_Warning, CPLE_AppDefined,
     775             :                         "Ignoring attribute %s of type %s, as only "
     776             :                         "variable length is supported, but it has a fixed size",
     777             :                         osName.c_str(), pszTypeName);
     778             :                 }
     779             :                 else
     780             :                 {
     781           9 :                     eType = OFTBinary;
     782           9 :                     fieldValues.push_back(
     783          18 :                         std::make_shared<std::vector<uint8_t>>());
     784             :                 }
     785           9 :                 break;
     786             :             }
     787             : 
     788             : #ifdef HAS_TILEDB_GEOM_WKB_WKT
     789             :             case TILEDB_GEOM_WKT:
     790             :             {
     791             :                 eType = OFTString;
     792             :                 fieldValues.push_back(std::make_shared<std::string>());
     793             :                 break;
     794             :             }
     795             : 
     796             :             case TILEDB_GEOM_WKB:
     797             :             {
     798             :                 eType = OFTBinary;
     799             :                 fieldValues.push_back(std::make_shared<std::vector<uint8_t>>());
     800             :                 break;
     801             :             }
     802             : #endif
     803             : 
     804           0 :             case TILEDB_CHAR:
     805             :             case TILEDB_INT8:
     806             :             case TILEDB_UINT32:
     807             :             case TILEDB_UINT64:
     808             :             case TILEDB_STRING_UTF16:
     809             :             case TILEDB_STRING_UTF32:
     810             :             case TILEDB_STRING_UCS2:
     811             :             case TILEDB_STRING_UCS4:
     812             :             case TILEDB_DATETIME_YEAR:
     813             :             case TILEDB_DATETIME_MONTH:
     814             :             case TILEDB_DATETIME_WEEK:
     815             :             case TILEDB_DATETIME_HR:
     816             :             case TILEDB_DATETIME_MIN:
     817             :             case TILEDB_DATETIME_SEC:
     818             :             case TILEDB_DATETIME_US:
     819             :             case TILEDB_DATETIME_NS:
     820             :             case TILEDB_DATETIME_PS:
     821             :             case TILEDB_DATETIME_FS:
     822             :             case TILEDB_DATETIME_AS:
     823             :             case TILEDB_TIME_HR:
     824             :             case TILEDB_TIME_MIN:
     825             :             case TILEDB_TIME_SEC:
     826             :             case TILEDB_TIME_US:
     827             :             case TILEDB_TIME_NS:
     828             :             case TILEDB_TIME_PS:
     829             :             case TILEDB_TIME_FS:
     830             :             case TILEDB_TIME_AS:
     831             :             case TILEDB_ANY:
     832             :             {
     833             :                 // TODO ?
     834           0 :                 const char *pszTypeName = "";
     835           0 :                 tiledb_datatype_to_str(type, &pszTypeName);
     836           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     837             :                          "Ignoring attribute %s as its type %s is unsupported",
     838             :                          osName.c_str(), pszTypeName);
     839           0 :                 bOK = false;
     840           0 :                 break;
     841             :             }
     842             :         }
     843         181 :         if (bOK)
     844             :         {
     845         181 :             m_aeFieldTypes.push_back(type);
     846         362 :             OGRFieldDefn oFieldDefn(osName.c_str(), eType);
     847         181 :             oFieldDefn.SetSubType(eSubType);
     848         181 :             oFieldDefn.SetNullable(bIsNullable);
     849         181 :             m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     850             :         }
     851         181 :     };
     852             : 
     853             :     // Figure out dimensions
     854          42 :     m_osXDim.clear();
     855          42 :     m_osYDim.clear();
     856             : 
     857             :     // to improve interoperability with PDAL generated datasets
     858             :     const bool bDefaultDimNameWithoutUnderscore =
     859         123 :         !CSLFetchNameValue(papszOpenOptions, "DIM_X") &&
     860          39 :         !CSLFetchNameValue(papszOpenOptions, "DIM_Y") &&
     861          39 :         !CSLFetchNameValue(papszOpenOptions, "DIM_Z") &&
     862          81 :         !domain.has_dimension("_X") && !domain.has_dimension("_Y") &&
     863          81 :         domain.has_dimension("X") && domain.has_dimension("Y");
     864             : 
     865             :     const std::string osXDim =
     866             :         CSLFetchNameValueDef(papszOpenOptions, "DIM_X",
     867          84 :                              bDefaultDimNameWithoutUnderscore ? "X" : "_X");
     868             :     const std::string osYDim =
     869             :         CSLFetchNameValueDef(papszOpenOptions, "DIM_Y",
     870          84 :                              bDefaultDimNameWithoutUnderscore ? "Y" : "_Y");
     871             :     const std::string osZDim =
     872             :         CSLFetchNameValueDef(papszOpenOptions, "DIM_Z",
     873          84 :                              bDefaultDimNameWithoutUnderscore ? "Z" : "_Z");
     874         147 :     for (unsigned int i = 0; i < domain.ndim(); ++i)
     875             :     {
     876         105 :         auto dim = domain.dimension(i);
     877         105 :         if (dim.name() == osXDim)
     878             :         {
     879          40 :             m_osXDim = dim.name();
     880          40 :             if (dim.type() != TILEDB_FLOAT64)
     881             :             {
     882           0 :                 const char *pszTypeName = "";
     883           0 :                 tiledb_datatype_to_str(dim.type(), &pszTypeName);
     884           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     885             :                          "Did not get expected type for %s dimension of "
     886             :                          "domain. Got %s, expected FLOAT64",
     887           0 :                          dim.name().c_str(), pszTypeName);
     888           0 :                 return false;
     889             :             }
     890          40 :             const auto dimdomain = dim.domain<double>();
     891          40 :             m_dfXStart = dimdomain.first;
     892          40 :             m_dfXEnd = dimdomain.second;
     893             :         }
     894          65 :         else if (dim.name() == osYDim)
     895             :         {
     896          42 :             m_osYDim = dim.name();
     897          42 :             if (dim.type() != TILEDB_FLOAT64)
     898             :             {
     899           0 :                 const char *pszTypeName = "";
     900           0 :                 tiledb_datatype_to_str(dim.type(), &pszTypeName);
     901           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     902             :                          "Did not get expected type for %s dimension of "
     903             :                          "domain. Got %s, expected FLOAT64",
     904           0 :                          dim.name().c_str(), pszTypeName);
     905           0 :                 return false;
     906             :             }
     907          42 :             const auto dimdomain = dim.domain<double>();
     908          42 :             m_dfYStart = dimdomain.first;
     909          42 :             m_dfYEnd = dimdomain.second;
     910             :         }
     911          23 :         else if (dim.name() == osZDim)
     912             :         {
     913          21 :             m_osZDim = dim.name();
     914          21 :             if (dim.type() != TILEDB_FLOAT64)
     915             :             {
     916           0 :                 const char *pszTypeName = "";
     917           0 :                 tiledb_datatype_to_str(dim.type(), &pszTypeName);
     918           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     919             :                          "Did not get expected type for %s dimension of "
     920             :                          "domain. Got %s, expected FLOAT64",
     921           0 :                          dim.name().c_str(), pszTypeName);
     922           0 :                 return false;
     923             :             }
     924          21 :             const auto dimdomain = dim.domain<double>();
     925          21 :             m_dfZStart = dimdomain.first;
     926          21 :             m_dfZEnd = dimdomain.second;
     927             :         }
     928             :         else
     929             :         {
     930           2 :             CreateField(dim.name(), dim.type(), /*bIsSingle=*/true,
     931             :                         /*bIsNullable=*/false);
     932             :         }
     933             :     }
     934          42 :     if (m_osXDim.empty())
     935             :     {
     936           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     937             :                  "Did not get expected _X dimension of domain");
     938           2 :         return false;
     939             :     }
     940          40 :     if (m_osYDim.empty())
     941             :     {
     942           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     943             :                  "Did not get expected _Y dimension of domain");
     944           0 :         return false;
     945             :     }
     946             : 
     947          40 :     tiledb_datatype_t v_type = TILEDB_FLOAT64;
     948          40 :     const void *v_r = nullptr;
     949          40 :     uint32_t v_num = 0;
     950          80 :     std::string osFIDColumn = "FID";
     951          40 :     m_array->get_metadata("FID_ATTRIBUTE_NAME", &v_type, &v_num, &v_r);
     952          37 :     if (v_r && (v_type == TILEDB_UINT8 || v_type == TILEDB_CHAR ||
     953          77 :                 v_type == TILEDB_STRING_ASCII || v_type == TILEDB_STRING_UTF8))
     954             :     {
     955          37 :         osFIDColumn.assign(static_cast<const char *>(v_r), v_num);
     956             :     }
     957             : 
     958          80 :     std::string osGeomColumn = "wkb_geometry";
     959          40 :     m_array->get_metadata("GEOMETRY_ATTRIBUTE_NAME", &v_type, &v_num, &v_r);
     960          34 :     if (v_r && (v_type == TILEDB_UINT8 || v_type == TILEDB_CHAR ||
     961          74 :                 v_type == TILEDB_STRING_ASCII || v_type == TILEDB_STRING_UTF8))
     962             :     {
     963          34 :         osGeomColumn.assign(static_cast<const char *>(v_r), v_num);
     964             :     }
     965             : #ifdef HAS_TILEDB_GEOM_WKB_WKT
     966             :     else
     967             :     {
     968             :         // If GEOMETRY_ATTRIBUTE_NAME isn't defined, identify the first attribute
     969             :         // of type TILEDB_GEOM_WKB
     970             :         osGeomColumn.clear();
     971             :         for (unsigned i = 0; i < m_schema->attribute_num(); ++i)
     972             :         {
     973             :             auto attr = m_schema->attribute(i);
     974             :             if (attr.type() == TILEDB_GEOM_WKB &&
     975             :                 attr.cell_val_num() == TILEDB_VAR_NUM)
     976             :             {
     977             :                 if (osGeomColumn.empty())
     978             :                     osGeomColumn = attr.name();
     979             :             }
     980             :         }
     981             :     }
     982             : #endif
     983             : 
     984          40 :     bool bFoundWkbGeometry = false;
     985          80 :     CPLJSONArray oAttributes;
     986          40 :     oSchema.Add("attributes", oAttributes);
     987         290 :     for (unsigned i = 0; i < m_schema->attribute_num(); ++i)
     988             :     {
     989         250 :         auto attr = m_schema->attribute(i);
     990             : 
     991             :         // Export attribute in json:TILEDB metadata domain, mostly for unit
     992             :         // testing purposes
     993             :         {
     994         500 :             CPLJSONObject oAttribute;
     995         250 :             oAttributes.Add(oAttribute);
     996         250 :             oAttribute.Set("name", attr.name());
     997         250 :             const char *pszTypeName = "";
     998         250 :             tiledb_datatype_to_str(attr.type(), &pszTypeName);
     999         250 :             oAttribute.Set("type", pszTypeName);
    1000         250 :             if (attr.cell_val_num() == TILEDB_VAR_NUM)
    1001         105 :                 oAttribute.Set("cell_val_num", "variable");
    1002             :             else
    1003         145 :                 oAttribute.Set("cell_val_num",
    1004         145 :                                static_cast<GIntBig>(attr.cell_val_num()));
    1005         250 :             oAttribute.Set("nullable", attr.nullable());
    1006             : 
    1007         500 :             const auto filters = attr.filter_list();
    1008         250 :             CPLJSONArray oFilterList;
    1009         269 :             for (uint32_t j = 0; j < filters.nfilters(); ++j)
    1010             :             {
    1011          19 :                 const auto filter = filters.filter(j);
    1012          19 :                 oFilterList.Add(tiledb::Filter::to_str(filter.filter_type()));
    1013             :             }
    1014         250 :             oAttribute.Add("filter_list", oFilterList);
    1015             :         }
    1016             : 
    1017         250 :         if (attr.name() == osFIDColumn && attr.type() == TILEDB_INT64)
    1018             :         {
    1019          37 :             m_osFIDColumn = attr.name();
    1020          37 :             continue;
    1021             :         }
    1022         213 :         if (attr.name() == osGeomColumn &&
    1023          34 :             (attr.type() == TILEDB_UINT8 || attr.type() == TILEDB_BLOB
    1024             : #ifdef HAS_TILEDB_GEOM_WKB_WKT
    1025             :              || attr.type() == TILEDB_GEOM_WKB
    1026             : #endif
    1027         247 :              ) &&
    1028          34 :             attr.cell_val_num() == TILEDB_VAR_NUM)
    1029             :         {
    1030          34 :             bFoundWkbGeometry = true;
    1031          34 :             continue;
    1032             :         }
    1033             : #ifdef HAS_TILEDB_GEOM_WKB_WKT
    1034             :         else if (attr.type() == TILEDB_GEOM_WKB &&
    1035             :                  attr.cell_val_num() == TILEDB_VAR_NUM)
    1036             :         {
    1037             :             CPLError(CE_Warning, CPLE_AppDefined,
    1038             :                      "Attribute %s has type GeomWKB, but another one (%s) is "
    1039             :                      "already used as the OGR geometry column. Dealing with %s "
    1040             :                      "has a Binary field",
    1041             :                      attr.name().c_str(), osGeomColumn.c_str(),
    1042             :                      attr.name().c_str());
    1043             :         }
    1044             : #endif
    1045             : 
    1046         179 :         const bool bIsSingle = attr.cell_val_num() == 1;
    1047         179 :         if (attr.cell_val_num() > 1 && attr.cell_val_num() != TILEDB_VAR_NUM)
    1048             :         {
    1049           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    1050             :                      "Ignoring attribute %s as it has a number of values per "
    1051             :                      "cell that is not 1 neither variable size",
    1052           0 :                      attr.name().c_str());
    1053           0 :             continue;
    1054             :         }
    1055         179 :         CreateField(attr.name(), attr.type(), bIsSingle, attr.nullable());
    1056             :     }
    1057             : 
    1058          40 :     if (bFoundWkbGeometry)
    1059          34 :         m_poFeatureDefn->GetGeomFieldDefn(0)->SetName(osGeomColumn.c_str());
    1060             : 
    1061         219 :     for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i)
    1062         179 :         m_aFieldValueOffsets.push_back(
    1063         358 :             std::make_shared<std::vector<uint64_t>>());
    1064          40 :     m_aFieldValidity.resize(m_poFeatureDefn->GetFieldCount());
    1065             : 
    1066          40 :     m_array->get_metadata("PAD_X", &v_type, &v_num, &v_r);
    1067          40 :     if (v_r && v_type == TILEDB_FLOAT64 && v_num == 1)
    1068             :     {
    1069          40 :         m_dfPadX = *static_cast<const double *>(v_r);
    1070             :     }
    1071             : 
    1072          40 :     m_array->get_metadata("PAD_Y", &v_type, &v_num, &v_r);
    1073          40 :     if (v_r && v_type == TILEDB_FLOAT64 && v_num == 1)
    1074             :     {
    1075          40 :         m_dfPadY = *static_cast<const double *>(v_r);
    1076             :     }
    1077             : 
    1078          40 :     m_array->get_metadata("PAD_Z", &v_type, &v_num, &v_r);
    1079          40 :     if (v_r && v_type == TILEDB_FLOAT64 && v_num == 1)
    1080             :     {
    1081           0 :         m_dfPadZ = *static_cast<const double *>(v_r);
    1082             :     }
    1083             : 
    1084          40 :     m_array->get_metadata("FEATURE_COUNT", &v_type, &v_num, &v_r);
    1085          40 :     if (v_r && v_type == TILEDB_INT64 && v_num == 1)
    1086             :     {
    1087          34 :         m_nTotalFeatureCount = *static_cast<const int64_t *>(v_r);
    1088             :     }
    1089             : 
    1090          40 :     m_array->get_metadata("LAYER_EXTENT_MINX", &v_type, &v_num, &v_r);
    1091          40 :     if (v_r && v_type == TILEDB_FLOAT64 && v_num == 1)
    1092             :     {
    1093          32 :         m_oLayerExtent.MinX = *static_cast<const double *>(v_r);
    1094             :     }
    1095             : 
    1096          40 :     m_array->get_metadata("LAYER_EXTENT_MINY", &v_type, &v_num, &v_r);
    1097          40 :     if (v_r && v_type == TILEDB_FLOAT64 && v_num == 1)
    1098             :     {
    1099          32 :         m_oLayerExtent.MinY = *static_cast<const double *>(v_r);
    1100             :     }
    1101             : 
    1102          40 :     m_array->get_metadata("LAYER_EXTENT_MAXX", &v_type, &v_num, &v_r);
    1103          40 :     if (v_r && v_type == TILEDB_FLOAT64 && v_num == 1)
    1104             :     {
    1105          32 :         m_oLayerExtent.MaxX = *static_cast<const double *>(v_r);
    1106             :     }
    1107             : 
    1108          40 :     m_array->get_metadata("LAYER_EXTENT_MAXY", &v_type, &v_num, &v_r);
    1109          40 :     if (v_r && v_type == TILEDB_FLOAT64 && v_num == 1)
    1110             :     {
    1111          32 :         m_oLayerExtent.MaxY = *static_cast<const double *>(v_r);
    1112             :     }
    1113             : 
    1114          40 :     m_array->get_metadata("CRS", &v_type, &v_num, &v_r);
    1115          12 :     if (v_r && (v_type == TILEDB_UINT8 || v_type == TILEDB_CHAR ||
    1116          52 :                 v_type == TILEDB_STRING_ASCII || v_type == TILEDB_STRING_UTF8))
    1117             :     {
    1118          24 :         std::string osStr;
    1119          12 :         osStr.assign(static_cast<const char *>(v_r), v_num);
    1120          12 :         OGRSpatialReference *poSRS = new OGRSpatialReference();
    1121          12 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1122          12 :         if (poSRS->SetFromUserInput(
    1123             :                 osStr.c_str(),
    1124          12 :                 OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
    1125             :             OGRERR_NONE)
    1126             :         {
    1127           0 :             poSRS->Release();
    1128           0 :             poSRS = nullptr;
    1129             :         }
    1130          12 :         if (poSRS)
    1131             :         {
    1132          12 :             m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
    1133          12 :             poSRS->Release();
    1134             :         }
    1135             :     }
    1136             : 
    1137          40 :     m_array->get_metadata("GeometryType", &v_type, &v_num, &v_r);
    1138          40 :     if (v_r && (v_type == TILEDB_UINT8 || v_type == TILEDB_CHAR ||
    1139          80 :                 v_type == TILEDB_STRING_ASCII || v_type == TILEDB_STRING_UTF8))
    1140             :     {
    1141          80 :         std::string osStr;
    1142          40 :         osStr.assign(static_cast<const char *>(v_r), v_num);
    1143          40 :         OGRwkbGeometryType eGeomType = wkbUnknown;
    1144          40 :         OGRReadWKTGeometryType(osStr.c_str(), &eGeomType);
    1145          80 :         m_poFeatureDefn->GetGeomFieldDefn(0)->SetType(eGeomType);
    1146             :     }
    1147           0 :     else if (!bFoundWkbGeometry)
    1148             :     {
    1149           0 :         m_poFeatureDefn->GetGeomFieldDefn(0)->SetType(
    1150           0 :             m_osZDim.empty() ? wkbPoint : wkbPoint25D);
    1151             :     }
    1152             : 
    1153             :     // Export array metadata in json:TILEDB metadata domain, mostly for
    1154             :     // unit testing purposes
    1155          80 :     CPLJSONObject oArray;
    1156          40 :     oJson.Add("array", oArray);
    1157          80 :     CPLJSONObject oMetadata;
    1158          40 :     oArray.Add("metadata", oMetadata);
    1159         445 :     for (uint64_t i = 0; i < m_array->metadata_num(); ++i)
    1160             :     {
    1161         810 :         std::string osKey;
    1162         405 :         m_array->get_metadata_from_index(i, &osKey, &v_type, &v_num, &v_r);
    1163         810 :         CPLJSONObject oMDItem;
    1164         405 :         oMetadata.Add(osKey, oMDItem);
    1165             : 
    1166         405 :         const char *pszTypeName = "";
    1167         405 :         tiledb_datatype_to_str(v_type, &pszTypeName);
    1168         405 :         oMDItem.Set("type", pszTypeName);
    1169             : 
    1170         405 :         switch (v_type)
    1171             :         {
    1172           0 :             case TILEDB_INT32:
    1173           0 :                 if (v_num == 1)
    1174           0 :                     oMDItem.Set("value", *static_cast<const int32_t *>(v_r));
    1175           0 :                 break;
    1176          34 :             case TILEDB_INT64:
    1177          34 :                 if (v_num == 1)
    1178          34 :                     oMDItem.Set("value",
    1179             :                                 static_cast<GIntBig>(
    1180          34 :                                     *static_cast<const int64_t *>(v_r)));
    1181          34 :                 break;
    1182         208 :             case TILEDB_FLOAT64:
    1183         208 :                 if (v_num == 1)
    1184         208 :                     oMDItem.Set("value", *static_cast<const double *>(v_r));
    1185         208 :                 break;
    1186         163 :             case TILEDB_STRING_ASCII:
    1187             :             case TILEDB_STRING_UTF8:
    1188             :             {
    1189         326 :                 std::string osStr;
    1190         163 :                 osStr.append(static_cast<const char *>(v_r), v_num);
    1191         163 :                 if (osStr.find("$schema") != std::string::npos)
    1192             :                 {
    1193             :                     // PROJJSON typically
    1194          24 :                     CPLJSONDocument oDoc;
    1195          12 :                     if (oDoc.LoadMemory(osStr))
    1196             :                     {
    1197          12 :                         oMDItem.Add("value", oDoc.GetRoot());
    1198             :                     }
    1199             :                     else
    1200             :                     {
    1201           0 :                         oMDItem.Set("value", osStr);
    1202             :                     }
    1203             :                 }
    1204             :                 else
    1205             :                 {
    1206         151 :                     oMDItem.Set("value", osStr);
    1207             :                 }
    1208         163 :                 break;
    1209             :             }
    1210           0 :             default:
    1211             :                 // other types unhandled for now
    1212           0 :                 break;
    1213             :         }
    1214             :     }
    1215             : 
    1216          40 :     char *apszMD[] = {nullptr, nullptr};
    1217          40 :     std::string osJsonMD = oJson.Format(CPLJSONObject::PrettyFormat::Plain);
    1218          40 :     apszMD[0] = osJsonMD.data();
    1219          40 :     SetMetadata(apszMD, "json:TILEDB");
    1220             : 
    1221          40 :     return true;
    1222             : }
    1223             : 
    1224             : /************************************************************************/
    1225             : /*                     GetDatabaseGeomColName()                         */
    1226             : /************************************************************************/
    1227             : 
    1228        1282 : const char *OGRTileDBLayer::GetDatabaseGeomColName()
    1229             : {
    1230        1282 :     const char *pszGeomColName = GetGeometryColumn();
    1231        1282 :     if (pszGeomColName && pszGeomColName[0] == 0)
    1232          33 :         pszGeomColName = nullptr;
    1233        1282 :     return pszGeomColName;
    1234             : }
    1235             : 
    1236             : /************************************************************************/
    1237             : /*                        SetReadBuffers()                              */
    1238             : /************************************************************************/
    1239             : 
    1240         329 : void OGRTileDBLayer::SetReadBuffers(bool bGrowVariableSizeArrays)
    1241             : {
    1242             :     const auto GetValueSize =
    1243        2063 :         [this, bGrowVariableSizeArrays](const std::string & /*osColName*/,
    1244        4110 :                                         size_t nCapacity, size_t nMulFactor = 1)
    1245             :     {
    1246        2063 :         if (bGrowVariableSizeArrays)
    1247             :         {
    1248          16 :             CPLAssert(nCapacity > 0);
    1249          16 :             return 2 * nCapacity;
    1250             :         }
    1251        2047 :         return std::max(m_nBatchSize * nMulFactor, nCapacity);
    1252         329 :     };
    1253             : 
    1254         329 :     m_anFIDs->resize(m_nBatchSize);
    1255         329 :     if (!m_osFIDColumn.empty())
    1256             :     {
    1257         326 :         m_query->set_data_buffer(m_osFIDColumn, *(m_anFIDs));
    1258             :     }
    1259             : 
    1260         329 :     if (!m_poFeatureDefn->GetGeomFieldDefn(0)->IsIgnored())
    1261             :     {
    1262         326 :         const char *pszGeomColName = GetDatabaseGeomColName();
    1263         326 :         if (pszGeomColName)
    1264             :         {
    1265         320 :             m_anGeometryOffsets->resize(m_nBatchSize);
    1266         320 :             m_abyGeometries->resize(GetValueSize(pszGeomColName,
    1267             :                                                  m_nGeometriesCapacity,
    1268             :                                                  m_nEstimatedWkbSizePerRow));
    1269         320 :             m_nGeometriesCapacity = m_abyGeometries->capacity();
    1270         320 :             const auto colType = m_schema->attribute(pszGeomColName).type();
    1271         320 :             if (colType == TILEDB_UINT8)
    1272             :             {
    1273           0 :                 m_query->set_data_buffer(pszGeomColName, *m_abyGeometries);
    1274             :                 m_query->set_offsets_buffer(pszGeomColName,
    1275           0 :                                             *m_anGeometryOffsets);
    1276             :             }
    1277         320 :             else if (colType == TILEDB_BLOB)
    1278             :             {
    1279             :                 m_query->set_data_buffer(
    1280             :                     pszGeomColName,
    1281         320 :                     reinterpret_cast<std::byte *>(m_abyGeometries->data()),
    1282         640 :                     m_abyGeometries->size());
    1283             :                 m_query->set_offsets_buffer(pszGeomColName,
    1284         320 :                                             m_anGeometryOffsets->data(),
    1285         320 :                                             m_anGeometryOffsets->size());
    1286             :             }
    1287             : #ifdef HAS_TILEDB_GEOM_WKB_WKT
    1288             :             else if (colType == TILEDB_GEOM_WKB)
    1289             :             {
    1290             :                 m_query->set_offsets_buffer(pszGeomColName,
    1291             :                                             *m_anGeometryOffsets);
    1292             :                 // We could use API expected std::byte, but this requires
    1293             :                 // TileDB 2.22 because of https://github.com/TileDB-Inc/TileDB/pull/4826
    1294             :                 m_query->set_data_buffer(
    1295             :                     pszGeomColName,
    1296             :                     static_cast<void *>(m_abyGeometries->data()),
    1297             :                     m_abyGeometries->size());
    1298             :             }
    1299             : #endif
    1300             :             else
    1301             :             {
    1302           0 :                 CPLAssert(false);
    1303             :             }
    1304             :         }
    1305             :         else
    1306             :         {
    1307           6 :             m_adfXs->resize(m_nBatchSize);
    1308           6 :             m_query->set_data_buffer(m_osXDim, *m_adfXs);
    1309             : 
    1310           6 :             m_adfYs->resize(m_nBatchSize);
    1311           6 :             m_query->set_data_buffer(m_osYDim, *m_adfYs);
    1312             : 
    1313           6 :             if (!m_osZDim.empty())
    1314             :             {
    1315           2 :                 m_adfZs->resize(m_nBatchSize);
    1316           2 :                 m_query->set_data_buffer(m_osZDim, *m_adfZs);
    1317             :             }
    1318             :         }
    1319             :     }
    1320             : 
    1321         329 :     if (m_anFieldValuesCapacity.empty())
    1322          46 :         m_anFieldValuesCapacity.resize(m_poFeatureDefn->GetFieldCount());
    1323             : 
    1324        5079 :     for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i)
    1325             :     {
    1326        4750 :         const OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
    1327        4750 :         if (poFieldDefn->IsIgnored())
    1328           3 :             continue;
    1329        4747 :         const char *pszFieldName = poFieldDefn->GetNameRef();
    1330        4747 :         auto &anOffsets = *(m_aFieldValueOffsets[i]);
    1331        4747 :         if (poFieldDefn->IsNullable())
    1332             :         {
    1333        2145 :             m_aFieldValidity[i].resize(m_nBatchSize);
    1334        2145 :             m_query->set_validity_buffer(pszFieldName, m_aFieldValidity[i]);
    1335             :         }
    1336        4747 :         auto &fieldValues = m_aFieldValues[i];
    1337        4747 :         switch (poFieldDefn->GetType())
    1338             :         {
    1339        1455 :             case OFTInteger:
    1340             :             {
    1341        1455 :                 if (m_aeFieldTypes[i] == TILEDB_BOOL)
    1342             :                 {
    1343             :                     auto &v = *(
    1344         242 :                         std::get<std::shared_ptr<VECTOR_OF_BOOL>>(fieldValues));
    1345         242 :                     v.resize(m_nBatchSize);
    1346             : #ifdef VECTOR_OF_BOOL_IS_NOT_UINT8_T
    1347             :                     m_query->set_data_buffer(pszFieldName, v.data(), v.size());
    1348             : #else
    1349         242 :                     m_query->set_data_buffer(pszFieldName, v);
    1350             : #endif
    1351             :                 }
    1352        1213 :                 else if (m_aeFieldTypes[i] == TILEDB_INT16)
    1353             :                 {
    1354             :                     auto &v = *(std::get<std::shared_ptr<std::vector<int16_t>>>(
    1355         242 :                         fieldValues));
    1356         242 :                     v.resize(m_nBatchSize);
    1357         242 :                     m_query->set_data_buffer(pszFieldName, v);
    1358             :                 }
    1359         971 :                 else if (m_aeFieldTypes[i] == TILEDB_INT32)
    1360             :                 {
    1361             :                     auto &v = *(std::get<std::shared_ptr<std::vector<int32_t>>>(
    1362         487 :                         fieldValues));
    1363         487 :                     v.resize(m_nBatchSize);
    1364         487 :                     m_query->set_data_buffer(pszFieldName, v);
    1365             :                 }
    1366         484 :                 else if (m_aeFieldTypes[i] == TILEDB_UINT8)
    1367             :                 {
    1368             :                     auto &v = *(std::get<std::shared_ptr<std::vector<uint8_t>>>(
    1369         242 :                         fieldValues));
    1370         242 :                     v.resize(m_nBatchSize);
    1371         242 :                     m_query->set_data_buffer(pszFieldName, v);
    1372             :                 }
    1373         242 :                 else if (m_aeFieldTypes[i] == TILEDB_UINT16)
    1374             :                 {
    1375             :                     auto &v =
    1376             :                         *(std::get<std::shared_ptr<std::vector<uint16_t>>>(
    1377         242 :                             fieldValues));
    1378         242 :                     v.resize(m_nBatchSize);
    1379         242 :                     m_query->set_data_buffer(pszFieldName, v);
    1380             :                 }
    1381             :                 else
    1382             :                 {
    1383           0 :                     CPLAssert(false);
    1384             :                 }
    1385        1455 :                 break;
    1386             :             }
    1387             : 
    1388         726 :             case OFTIntegerList:
    1389             :             {
    1390         726 :                 auto iter = m_oMapEstimatedSizePerRow.find(pszFieldName);
    1391             :                 const int nMulFactor =
    1392         726 :                     iter != m_oMapEstimatedSizePerRow.end()
    1393         726 :                         ? static_cast<int>(
    1394         726 :                               std::min<uint64_t>(1000, iter->second))
    1395         726 :                         : 8;
    1396         726 :                 if (m_aeFieldTypes[i] == TILEDB_BOOL)
    1397             :                 {
    1398             :                     auto &v = *(
    1399         242 :                         std::get<std::shared_ptr<VECTOR_OF_BOOL>>(fieldValues));
    1400         242 :                     v.resize(GetValueSize(
    1401         242 :                         pszFieldName, m_anFieldValuesCapacity[i], nMulFactor));
    1402         242 :                     m_anFieldValuesCapacity[i] = v.capacity();
    1403         242 :                     anOffsets.resize(m_nBatchSize);
    1404         242 :                     m_query->set_offsets_buffer(pszFieldName, anOffsets);
    1405             : #ifdef VECTOR_OF_BOOL_IS_NOT_UINT8_T
    1406             :                     m_query->set_data_buffer(pszFieldName, v.data(), v.size());
    1407             : #else
    1408         242 :                     m_query->set_data_buffer(pszFieldName, v);
    1409             : #endif
    1410             :                 }
    1411         484 :                 else if (m_aeFieldTypes[i] == TILEDB_INT16)
    1412             :                 {
    1413             :                     auto &v = *(std::get<std::shared_ptr<std::vector<int16_t>>>(
    1414         242 :                         fieldValues));
    1415         242 :                     v.resize(GetValueSize(
    1416         242 :                         pszFieldName, m_anFieldValuesCapacity[i], nMulFactor));
    1417         242 :                     m_anFieldValuesCapacity[i] = v.capacity();
    1418         242 :                     anOffsets.resize(m_nBatchSize);
    1419         242 :                     m_query->set_data_buffer(pszFieldName, v);
    1420         242 :                     m_query->set_offsets_buffer(pszFieldName, anOffsets);
    1421             :                 }
    1422         242 :                 else if (m_aeFieldTypes[i] == TILEDB_INT32)
    1423             :                 {
    1424             :                     auto &v = *(std::get<std::shared_ptr<std::vector<int32_t>>>(
    1425         242 :                         fieldValues));
    1426         242 :                     v.resize(GetValueSize(
    1427         242 :                         pszFieldName, m_anFieldValuesCapacity[i], nMulFactor));
    1428         242 :                     m_anFieldValuesCapacity[i] = v.capacity();
    1429         242 :                     anOffsets.resize(m_nBatchSize);
    1430         242 :                     m_query->set_data_buffer(pszFieldName, v);
    1431         242 :                     m_query->set_offsets_buffer(pszFieldName, anOffsets);
    1432             :                 }
    1433           0 :                 else if (m_aeFieldTypes[i] == TILEDB_UINT8)
    1434             :                 {
    1435             :                     auto &v = *(std::get<std::shared_ptr<std::vector<uint8_t>>>(
    1436           0 :                         fieldValues));
    1437           0 :                     v.resize(GetValueSize(
    1438           0 :                         pszFieldName, m_anFieldValuesCapacity[i], nMulFactor));
    1439           0 :                     m_anFieldValuesCapacity[i] = v.capacity();
    1440           0 :                     anOffsets.resize(m_nBatchSize);
    1441           0 :                     m_query->set_data_buffer(pszFieldName, v);
    1442           0 :                     m_query->set_offsets_buffer(pszFieldName, anOffsets);
    1443             :                 }
    1444           0 :                 else if (m_aeFieldTypes[i] == TILEDB_UINT16)
    1445             :                 {
    1446             :                     auto &v =
    1447             :                         *(std::get<std::shared_ptr<std::vector<uint16_t>>>(
    1448           0 :                             fieldValues));
    1449           0 :                     v.resize(GetValueSize(
    1450           0 :                         pszFieldName, m_anFieldValuesCapacity[i], nMulFactor));
    1451           0 :                     m_anFieldValuesCapacity[i] = v.capacity();
    1452           0 :                     anOffsets.resize(m_nBatchSize);
    1453           0 :                     m_query->set_data_buffer(pszFieldName, v);
    1454           0 :                     m_query->set_offsets_buffer(pszFieldName, anOffsets);
    1455             :                 }
    1456             :                 else
    1457             :                 {
    1458           0 :                     CPLAssert(false);
    1459             :                 }
    1460         726 :                 break;
    1461             :             }
    1462             : 
    1463        1017 :             case OFTInteger64:
    1464             :             case OFTDate:
    1465             :             case OFTDateTime:
    1466             :             case OFTTime:
    1467             :             {
    1468             :                 auto &v = *(std::get<std::shared_ptr<std::vector<int64_t>>>(
    1469        1017 :                     fieldValues));
    1470        1017 :                 v.resize(m_nBatchSize);
    1471        1017 :                 m_query->set_data_buffer(pszFieldName, v);
    1472        1017 :                 break;
    1473             :             }
    1474             : 
    1475           0 :             case OFTInteger64List:
    1476             :             {
    1477           0 :                 auto iter = m_oMapEstimatedSizePerRow.find(pszFieldName);
    1478             :                 const int nMulFactor =
    1479           0 :                     iter != m_oMapEstimatedSizePerRow.end()
    1480           0 :                         ? static_cast<int>(
    1481           0 :                               std::min<uint64_t>(1000, iter->second))
    1482           0 :                         : 8;
    1483             :                 auto &v = *(std::get<std::shared_ptr<std::vector<int64_t>>>(
    1484           0 :                     fieldValues));
    1485           0 :                 v.resize(GetValueSize(pszFieldName, m_anFieldValuesCapacity[i],
    1486             :                                       nMulFactor));
    1487           0 :                 m_anFieldValuesCapacity[i] = v.capacity();
    1488           0 :                 anOffsets.resize(m_nBatchSize);
    1489           0 :                 m_query->set_data_buffer(pszFieldName, v);
    1490           0 :                 m_query->set_offsets_buffer(pszFieldName, anOffsets);
    1491           0 :                 break;
    1492             :             }
    1493             : 
    1494         532 :             case OFTReal:
    1495             :             {
    1496         532 :                 if (poFieldDefn->GetSubType() == OFSTFloat32)
    1497             :                 {
    1498             :                     auto &v = *(std::get<std::shared_ptr<std::vector<float>>>(
    1499         242 :                         fieldValues));
    1500         242 :                     v.resize(m_nBatchSize);
    1501         242 :                     m_query->set_data_buffer(pszFieldName, v);
    1502             :                 }
    1503             :                 else
    1504             :                 {
    1505             :                     auto &v = *(std::get<std::shared_ptr<std::vector<double>>>(
    1506         290 :                         fieldValues));
    1507         290 :                     v.resize(m_nBatchSize);
    1508         290 :                     m_query->set_data_buffer(pszFieldName, v);
    1509             :                 }
    1510         532 :                 break;
    1511             :             }
    1512             : 
    1513         484 :             case OFTRealList:
    1514             :             {
    1515         484 :                 auto iter = m_oMapEstimatedSizePerRow.find(pszFieldName);
    1516             :                 const int nMulFactor =
    1517         484 :                     iter != m_oMapEstimatedSizePerRow.end()
    1518         484 :                         ? static_cast<int>(
    1519         484 :                               std::min<uint64_t>(1000, iter->second))
    1520         484 :                         : 8;
    1521         484 :                 anOffsets.resize(m_nBatchSize);
    1522         484 :                 if (poFieldDefn->GetSubType() == OFSTFloat32)
    1523             :                 {
    1524             :                     auto &v = *(std::get<std::shared_ptr<std::vector<float>>>(
    1525         242 :                         fieldValues));
    1526         242 :                     v.resize(GetValueSize(
    1527         242 :                         pszFieldName, m_anFieldValuesCapacity[i], nMulFactor));
    1528         242 :                     m_anFieldValuesCapacity[i] = v.capacity();
    1529         242 :                     m_query->set_data_buffer(pszFieldName, v);
    1530         242 :                     m_query->set_offsets_buffer(pszFieldName, anOffsets);
    1531             :                 }
    1532             :                 else
    1533             :                 {
    1534             :                     auto &v = *(std::get<std::shared_ptr<std::vector<double>>>(
    1535         242 :                         fieldValues));
    1536         242 :                     v.resize(GetValueSize(
    1537         242 :                         pszFieldName, m_anFieldValuesCapacity[i], nMulFactor));
    1538         242 :                     m_anFieldValuesCapacity[i] = v.capacity();
    1539         242 :                     m_query->set_data_buffer(pszFieldName, v);
    1540         242 :                     m_query->set_offsets_buffer(pszFieldName, anOffsets);
    1541             :                 }
    1542         484 :                 break;
    1543             :             }
    1544             : 
    1545         291 :             case OFTString:
    1546             :             {
    1547             :                 auto &v =
    1548         291 :                     *(std::get<std::shared_ptr<std::string>>(fieldValues));
    1549         291 :                 auto iter = m_oMapEstimatedSizePerRow.find(pszFieldName);
    1550         580 :                 v.resize(GetValueSize(pszFieldName, m_anFieldValuesCapacity[i],
    1551         291 :                                       iter != m_oMapEstimatedSizePerRow.end()
    1552         289 :                                           ? iter->second
    1553             :                                           : 8));
    1554         291 :                 m_anFieldValuesCapacity[i] = v.capacity();
    1555         291 :                 anOffsets.resize(m_nBatchSize);
    1556         291 :                 m_query->set_data_buffer(pszFieldName, v);
    1557         291 :                 m_query->set_offsets_buffer(pszFieldName, anOffsets);
    1558         291 :                 break;
    1559             :             }
    1560             : 
    1561         242 :             case OFTBinary:
    1562             :             {
    1563         242 :                 const auto eType = m_schema->attribute(pszFieldName).type();
    1564             :                 auto &v = *(std::get<std::shared_ptr<std::vector<uint8_t>>>(
    1565         242 :                     fieldValues));
    1566         242 :                 auto iter = m_oMapEstimatedSizePerRow.find(pszFieldName);
    1567         484 :                 v.resize(GetValueSize(pszFieldName, m_anFieldValuesCapacity[i],
    1568         242 :                                       iter != m_oMapEstimatedSizePerRow.end()
    1569         242 :                                           ? iter->second
    1570             :                                           : 8));
    1571         242 :                 m_anFieldValuesCapacity[i] = v.capacity();
    1572         242 :                 anOffsets.resize(m_nBatchSize);
    1573         242 :                 if (eType == TILEDB_UINT8)
    1574             :                 {
    1575           0 :                     m_query->set_data_buffer(pszFieldName, v);
    1576           0 :                     m_query->set_offsets_buffer(pszFieldName, anOffsets);
    1577             :                 }
    1578         242 :                 else if (eType == TILEDB_BLOB)
    1579             :                 {
    1580             :                     m_query->set_data_buffer(
    1581         242 :                         pszFieldName, reinterpret_cast<std::byte *>(v.data()),
    1582         484 :                         v.size());
    1583             :                     m_query->set_offsets_buffer(pszFieldName, anOffsets.data(),
    1584         242 :                                                 anOffsets.size());
    1585             :                 }
    1586             :                 else
    1587             :                 {
    1588           0 :                     CPLAssert(false);
    1589             :                 }
    1590         242 :                 break;
    1591             :             }
    1592             : 
    1593           0 :             default:
    1594             :             {
    1595           0 :                 CPLAssert(false);
    1596             :                 break;
    1597             :             }
    1598             :         }
    1599             :     }
    1600         329 : }
    1601             : 
    1602             : /************************************************************************/
    1603             : /*                           SetupQuery()                               */
    1604             : /************************************************************************/
    1605             : 
    1606             : namespace
    1607             : {
    1608             : template <class T> struct ResetArray
    1609             : {
    1610         703 :     static void exec(OGRTileDBLayer::ArrayType &array)
    1611             :     {
    1612         703 :         array = std::make_shared<T>();
    1613         703 :     }
    1614             : };
    1615             : }  // namespace
    1616             : 
    1617          60 : void OGRTileDBLayer::AllocateNewBuffers()
    1618             : {
    1619          60 :     m_anFIDs = std::make_shared<std::vector<int64_t>>();
    1620          60 :     m_adfXs = std::make_shared<std::vector<double>>();
    1621          60 :     m_adfYs = std::make_shared<std::vector<double>>();
    1622          60 :     m_adfZs = std::make_shared<std::vector<double>>();
    1623          60 :     m_abyGeometries = std::make_shared<std::vector<unsigned char>>();
    1624          60 :     m_anGeometryOffsets = std::make_shared<std::vector<uint64_t>>();
    1625             : 
    1626         763 :     for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
    1627             :     {
    1628         703 :         ProcessField<ResetArray>::exec(m_aeFieldTypes[i], m_aFieldValues[i]);
    1629             : 
    1630         703 :         m_aFieldValueOffsets[i] = std::make_shared<std::vector<uint64_t>>();
    1631             :     }
    1632          60 : }
    1633             : 
    1634         355 : bool OGRTileDBLayer::SetupQuery(tiledb::QueryCondition *queryCondition)
    1635             : {
    1636         355 :     if (!m_bArrowBatchReleased)
    1637             :     {
    1638          21 :         AllocateNewBuffers();
    1639             :     }
    1640             : 
    1641         355 :     m_anFIDs->clear();
    1642         355 :     m_adfXs->clear();
    1643         355 :     m_anGeometryOffsets->clear();
    1644         355 :     m_nOffsetInResultSet = 0;
    1645         355 :     m_nRowCountInResultSet = 0;
    1646         355 :     if (m_bAttributeFilterAlwaysFalse)
    1647          22 :         return false;
    1648             : 
    1649         333 :     const char *pszGeomColName = GetDatabaseGeomColName();
    1650             : 
    1651             :     // FIXME: remove this
    1652         333 :     const bool bHitBug = CPLTestBool(CPLGetConfigOption("TILEDB_BUG", "NO"));
    1653         333 :     if (bHitBug)
    1654             :     {
    1655           0 :         m_nBatchSize = 1;
    1656           0 :         m_nEstimatedWkbSizePerRow = 10;
    1657             :     }
    1658             : 
    1659             :     try
    1660             :     {
    1661         333 :         if (!m_query)
    1662             :         {
    1663         293 :             m_query = std::make_unique<tiledb::Query>(*m_ctx, *m_array);
    1664         293 :             m_query->set_layout(TILEDB_UNORDERED);
    1665         293 :             if (queryCondition)
    1666          14 :                 m_query->set_condition(*queryCondition);
    1667         279 :             else if (m_poQueryCondition)
    1668         133 :                 m_query->set_condition(*(m_poQueryCondition.get()));
    1669             : 
    1670         293 :             if (m_nEstimatedWkbSizePerRow == 0)
    1671             :             {
    1672         194 :                 for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i)
    1673             :                 {
    1674             :                     const OGRFieldDefn *poFieldDefn =
    1675         159 :                         m_poFeatureDefn->GetFieldDefn(i);
    1676         159 :                     const char *pszFieldName = poFieldDefn->GetNameRef();
    1677         159 :                     switch (poFieldDefn->GetType())
    1678             :                     {
    1679          59 :                         case OFTString:
    1680             :                         case OFTBinary:
    1681             :                         case OFTIntegerList:
    1682             :                         case OFTInteger64List:
    1683             :                         case OFTRealList:
    1684             :                         {
    1685             :                             uint64_t nEstRows;
    1686             :                             uint64_t nEstBytes;
    1687          59 :                             if (poFieldDefn->IsNullable())
    1688             :                             {
    1689             :                                 const auto estimation =
    1690             :                                     m_query->est_result_size_var_nullable(
    1691          31 :                                         pszFieldName);
    1692          31 :                                 nEstRows = estimation[0] / sizeof(uint64_t);
    1693          31 :                                 nEstBytes = estimation[1];
    1694             :                             }
    1695             :                             else
    1696             :                             {
    1697             :                                 const auto estimation =
    1698          28 :                                     m_query->est_result_size_var(pszFieldName);
    1699          28 :                                 nEstRows = estimation[0] / sizeof(uint64_t);
    1700          28 :                                 nEstBytes = estimation[1];
    1701             :                             }
    1702          59 :                             if (nEstRows)
    1703             :                             {
    1704          57 :                                 m_oMapEstimatedSizePerRow[pszFieldName] =
    1705          57 :                                     std::max<size_t>(1,
    1706             :                                                      static_cast<size_t>(
    1707          57 :                                                          nEstBytes / nEstRows) *
    1708          57 :                                                          4 / 3);
    1709          57 :                                 CPLDebug("TILEDB", "Average %s size: %u bytes",
    1710             :                                          pszFieldName,
    1711             :                                          static_cast<unsigned>(
    1712             :                                              m_oMapEstimatedSizePerRow
    1713          57 :                                                  [pszFieldName]));
    1714             :                             }
    1715          59 :                             break;
    1716             :                         }
    1717             : 
    1718         100 :                         default:
    1719         100 :                             break;
    1720             :                     }
    1721             :                 }
    1722             : 
    1723          35 :                 m_nEstimatedWkbSizePerRow = 9;  // Size of 2D point WKB
    1724          35 :                 if (pszGeomColName)
    1725             :                 {
    1726             :                     const auto estimation =
    1727          29 :                         m_query->est_result_size_var(pszGeomColName);
    1728          29 :                     const uint64_t nEstRows = estimation[0] / sizeof(uint64_t);
    1729          29 :                     const uint64_t nEstBytes = estimation[1];
    1730          29 :                     if (nEstRows)
    1731             :                     {
    1732          27 :                         m_nEstimatedWkbSizePerRow = std::max(
    1733          27 :                             m_nEstimatedWkbSizePerRow,
    1734          27 :                             static_cast<size_t>(nEstBytes / nEstRows) * 4 / 3);
    1735          27 :                         CPLDebug(
    1736             :                             "TILEDB", "Average WKB size: %u bytes",
    1737          27 :                             static_cast<unsigned>(m_nEstimatedWkbSizePerRow));
    1738             :                     }
    1739             :                 }
    1740             :             }
    1741             : 
    1742         293 :             if (m_poFilterGeom && queryCondition == nullptr)
    1743             :             {
    1744          47 :                 tiledb::Subarray subarray(*m_ctx, *m_array);
    1745             : 
    1746             :                 const double dfMinX =
    1747          47 :                     std::max(m_dfXStart, m_sFilterEnvelope.MinX - m_dfPadX);
    1748             :                 const double dfMaxX =
    1749          47 :                     std::min(m_dfXEnd, m_sFilterEnvelope.MaxX + m_dfPadX);
    1750             :                 const double dfMinY =
    1751          47 :                     std::max(m_dfYStart, m_sFilterEnvelope.MinY - m_dfPadY);
    1752             :                 const double dfMaxY =
    1753          47 :                     std::min(m_dfYEnd, m_sFilterEnvelope.MaxY + m_dfPadY);
    1754             : 
    1755          47 :                 if (dfMaxX < dfMinX || dfMaxY < dfMinY)
    1756             :                 {
    1757           4 :                     m_bQueryComplete = true;
    1758           4 :                     return false;
    1759             :                 }
    1760             : 
    1761          43 :                 subarray.add_range(m_osXDim, dfMinX, dfMaxX);
    1762          43 :                 subarray.add_range(m_osYDim, dfMinY, dfMaxY);
    1763          43 :                 m_query->set_subarray(subarray);
    1764             :             }
    1765             :         }
    1766             : 
    1767         329 :         SetReadBuffers(m_bGrowBuffers);
    1768         329 :         m_bGrowBuffers = false;
    1769             : 
    1770             :         // Create a loop
    1771             :         tiledb::Query::Status status;
    1772         329 :         uint64_t nRowCount = 0;
    1773             :         while (true)
    1774             :         {
    1775             :             // Submit query and get status
    1776         329 :             if (m_bStats)
    1777           0 :                 tiledb::Stats::enable();
    1778             : 
    1779         329 :             m_query->submit();
    1780             : 
    1781         329 :             if (m_bStats)
    1782             :             {
    1783           0 :                 tiledb::Stats::dump(stdout);
    1784           0 :                 tiledb::Stats::disable();
    1785             :             }
    1786             : 
    1787         329 :             status = m_query->query_status();
    1788         329 :             if (status == tiledb::Query::Status::FAILED)
    1789             :             {
    1790           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Query failed");
    1791           0 :                 m_bQueryComplete = true;
    1792           0 :                 return false;
    1793             :             }
    1794             : 
    1795             :             const auto result_buffer_elements =
    1796         329 :                 m_query->result_buffer_elements();
    1797         329 :             if (m_osFIDColumn.empty())
    1798             :             {
    1799           3 :                 auto oIter = result_buffer_elements.begin();
    1800           3 :                 if (oIter != result_buffer_elements.end())
    1801           3 :                     nRowCount = oIter->second.second;
    1802             :             }
    1803             :             else
    1804             :             {
    1805         326 :                 auto oIter = result_buffer_elements.find(m_osFIDColumn);
    1806         326 :                 if (oIter != result_buffer_elements.end())
    1807         326 :                     nRowCount = oIter->second.second;
    1808             :             }
    1809         329 :             if (!m_poFeatureDefn->GetGeomFieldDefn(0)->IsIgnored() &&
    1810             :                 pszGeomColName)
    1811             :             {
    1812         320 :                 auto oIter = result_buffer_elements.find(pszGeomColName);
    1813         320 :                 if (oIter != result_buffer_elements.end())
    1814             :                 {
    1815         320 :                     const auto &result = oIter->second;
    1816         320 :                     nRowCount = std::min(nRowCount, result.first);
    1817             :                     // For some reason, result.first can be 1, and result.second 0
    1818         320 :                     if (!bHitBug && result.second == 0)
    1819          46 :                         nRowCount = 0;
    1820             :                 }
    1821             :                 else
    1822             :                 {
    1823           0 :                     CPLAssert(false);
    1824             :                 }
    1825             :             }
    1826        5079 :             for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i)
    1827             :             {
    1828             :                 const OGRFieldDefn *poFieldDefn =
    1829        4750 :                     m_poFeatureDefn->GetFieldDefn(i);
    1830        4750 :                 if (!poFieldDefn->IsIgnored())
    1831             :                 {
    1832        4747 :                     const char *pszFieldName = poFieldDefn->GetNameRef();
    1833        4747 :                     auto oIter = result_buffer_elements.find(pszFieldName);
    1834        4747 :                     if (oIter != result_buffer_elements.end())
    1835             :                     {
    1836        4747 :                         const auto &result = oIter->second;
    1837        4747 :                         if (result.first == 0)
    1838             :                         {
    1839        3290 :                             nRowCount = std::min(nRowCount, result.second);
    1840             :                         }
    1841             :                         else
    1842        1457 :                             nRowCount = std::min(nRowCount, result.first);
    1843             :                     }
    1844             :                     else
    1845             :                     {
    1846           0 :                         CPLAssert(false);
    1847             :                     }
    1848             :                 }
    1849             :             }
    1850             : 
    1851         329 :             if (status != tiledb::Query::Status::INCOMPLETE)
    1852         289 :                 break;
    1853             : 
    1854          40 :             if (bHitBug)
    1855             :             {
    1856           0 :                 if (nRowCount > 0)
    1857           0 :                     break;
    1858           0 :                 SetReadBuffers(true);
    1859             :             }
    1860          40 :             else if (nRowCount < m_nBatchSize)
    1861             :             {
    1862           2 :                 if (nRowCount > 0)
    1863             :                 {
    1864           2 :                     m_bGrowBuffers = true;
    1865           2 :                     break;
    1866             :                 }
    1867           0 :                 CPLDebug("TILEDB", "Got 0 rows. Grow buffers");
    1868           0 :                 SetReadBuffers(true);
    1869             :             }
    1870             :             else
    1871          38 :                 break;
    1872           0 :         }
    1873             : 
    1874         329 :         m_bQueryComplete = (status == tiledb::Query::Status::COMPLETE);
    1875         329 :         m_nRowCountInResultSet = nRowCount;
    1876             : 
    1877         329 :         if (nRowCount == 0)
    1878             :         {
    1879          46 :             m_bQueryComplete = true;
    1880          46 :             return false;
    1881             :         }
    1882             :         //CPLDebug("TILEDB", "Read %d rows", int(nRowCount));
    1883             : 
    1884         566 :         const auto result_buffer_elements = m_query->result_buffer_elements();
    1885         283 :         m_anFIDs->resize(nRowCount);
    1886         283 :         if (m_osFIDColumn.empty())
    1887             :         {
    1888           7 :             for (uint64_t i = 0; i < nRowCount; ++i)
    1889             :             {
    1890           4 :                 (*m_anFIDs)[i] = m_nNextFID;
    1891           4 :                 m_nNextFID++;
    1892             :             }
    1893             :         }
    1894             : 
    1895         283 :         if (!m_poFeatureDefn->GetGeomFieldDefn(0)->IsIgnored())
    1896             :         {
    1897         280 :             if (pszGeomColName)
    1898             :             {
    1899         274 :                 auto oIter = result_buffer_elements.find(pszGeomColName);
    1900         274 :                 if (oIter != result_buffer_elements.end())
    1901             :                 {
    1902         274 :                     const auto &result = oIter->second;
    1903         274 :                     if (nRowCount < result.first)
    1904             :                     {
    1905           0 :                         m_abyGeometries->resize(
    1906           0 :                             (*m_anGeometryOffsets)[nRowCount]);
    1907             :                     }
    1908             :                     else
    1909             :                     {
    1910         274 :                         m_abyGeometries->resize(
    1911         274 :                             static_cast<size_t>(result.second));
    1912             :                     }
    1913         274 :                     m_anGeometryOffsets->resize(nRowCount);
    1914             :                 }
    1915             :             }
    1916             :             else
    1917             :             {
    1918           6 :                 m_adfXs->resize(nRowCount);
    1919           6 :                 m_adfYs->resize(nRowCount);
    1920           6 :                 if (!m_osZDim.empty())
    1921           2 :                     m_adfZs->resize(nRowCount);
    1922             :             }
    1923             :         }
    1924             : 
    1925        4259 :         for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i)
    1926             :         {
    1927        3976 :             const OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
    1928        3976 :             if (poFieldDefn->IsIgnored())
    1929           3 :                 continue;
    1930        3973 :             const char *pszFieldName = poFieldDefn->GetNameRef();
    1931        3973 :             auto &anOffsets = *(m_aFieldValueOffsets[i]);
    1932        3973 :             auto oIter = result_buffer_elements.find(pszFieldName);
    1933        3973 :             if (oIter == result_buffer_elements.end())
    1934             :             {
    1935           0 :                 CPLAssert(false);
    1936             :                 continue;
    1937             :             }
    1938        3973 :             const auto &result = oIter->second;
    1939        3973 :             if (poFieldDefn->IsNullable())
    1940        1751 :                 m_aFieldValidity[i].resize(nRowCount);
    1941        3973 :             auto &fieldValues = m_aFieldValues[i];
    1942        3973 :             switch (poFieldDefn->GetType())
    1943             :             {
    1944        1215 :                 case OFTInteger:
    1945             :                 {
    1946        1215 :                     if (m_aeFieldTypes[i] == TILEDB_BOOL)
    1947             :                     {
    1948             :                         auto &v = *(std::get<std::shared_ptr<VECTOR_OF_BOOL>>(
    1949         202 :                             fieldValues));
    1950         202 :                         v.resize(result.second);
    1951             :                     }
    1952        1013 :                     else if (m_aeFieldTypes[i] == TILEDB_INT16)
    1953             :                     {
    1954             :                         auto &v =
    1955             :                             *(std::get<std::shared_ptr<std::vector<int16_t>>>(
    1956         202 :                                 fieldValues));
    1957         202 :                         v.resize(result.second);
    1958             :                     }
    1959         811 :                     else if (m_aeFieldTypes[i] == TILEDB_INT32)
    1960             :                     {
    1961             :                         auto &v =
    1962             :                             *(std::get<std::shared_ptr<std::vector<int32_t>>>(
    1963         407 :                                 fieldValues));
    1964         407 :                         v.resize(result.second);
    1965             :                     }
    1966         404 :                     else if (m_aeFieldTypes[i] == TILEDB_UINT8)
    1967             :                     {
    1968             :                         auto &v =
    1969             :                             *(std::get<std::shared_ptr<std::vector<uint8_t>>>(
    1970         202 :                                 fieldValues));
    1971         202 :                         v.resize(result.second);
    1972             :                     }
    1973         202 :                     else if (m_aeFieldTypes[i] == TILEDB_UINT16)
    1974             :                     {
    1975             :                         auto &v =
    1976             :                             *(std::get<std::shared_ptr<std::vector<uint16_t>>>(
    1977         202 :                                 fieldValues));
    1978         202 :                         v.resize(result.second);
    1979             :                     }
    1980             :                     else
    1981             :                     {
    1982           0 :                         CPLAssert(false);
    1983             :                     }
    1984        1215 :                     break;
    1985             :                 }
    1986             : 
    1987         606 :                 case OFTIntegerList:
    1988             :                 {
    1989         606 :                     if (m_aeFieldTypes[i] == TILEDB_BOOL)
    1990             :                     {
    1991             :                         auto &v = *(std::get<std::shared_ptr<VECTOR_OF_BOOL>>(
    1992         202 :                             fieldValues));
    1993         202 :                         if (nRowCount < result.first)
    1994             :                         {
    1995           0 :                             v.resize(anOffsets[nRowCount] / sizeof(v[0]));
    1996             :                         }
    1997             :                         else
    1998             :                         {
    1999         202 :                             v.resize(static_cast<size_t>(result.second));
    2000             :                         }
    2001             :                     }
    2002         404 :                     else if (m_aeFieldTypes[i] == TILEDB_INT16)
    2003             :                     {
    2004             :                         auto &v =
    2005             :                             *(std::get<std::shared_ptr<std::vector<int16_t>>>(
    2006         202 :                                 fieldValues));
    2007         202 :                         if (nRowCount < result.first)
    2008             :                         {
    2009           0 :                             v.resize(anOffsets[nRowCount] / sizeof(v[0]));
    2010             :                         }
    2011             :                         else
    2012             :                         {
    2013         202 :                             v.resize(static_cast<size_t>(result.second));
    2014             :                         }
    2015             :                     }
    2016         202 :                     else if (m_aeFieldTypes[i] == TILEDB_INT32)
    2017             :                     {
    2018             :                         auto &v =
    2019             :                             *(std::get<std::shared_ptr<std::vector<int32_t>>>(
    2020         202 :                                 fieldValues));
    2021         202 :                         if (nRowCount < result.first)
    2022             :                         {
    2023           0 :                             v.resize(anOffsets[nRowCount] / sizeof(v[0]));
    2024             :                         }
    2025             :                         else
    2026             :                         {
    2027         202 :                             v.resize(static_cast<size_t>(result.second));
    2028             :                         }
    2029             :                     }
    2030           0 :                     else if (m_aeFieldTypes[i] == TILEDB_UINT8)
    2031             :                     {
    2032             :                         auto &v =
    2033             :                             *(std::get<std::shared_ptr<std::vector<uint8_t>>>(
    2034           0 :                                 fieldValues));
    2035           0 :                         if (nRowCount < result.first)
    2036             :                         {
    2037           0 :                             v.resize(anOffsets[nRowCount] / sizeof(v[0]));
    2038             :                         }
    2039             :                         else
    2040             :                         {
    2041           0 :                             v.resize(static_cast<size_t>(result.second));
    2042             :                         }
    2043             :                     }
    2044           0 :                     else if (m_aeFieldTypes[i] == TILEDB_UINT16)
    2045             :                     {
    2046             :                         auto &v =
    2047             :                             *(std::get<std::shared_ptr<std::vector<uint16_t>>>(
    2048           0 :                                 fieldValues));
    2049           0 :                         if (nRowCount < result.first)
    2050             :                         {
    2051           0 :                             v.resize(anOffsets[nRowCount] / sizeof(v[0]));
    2052             :                         }
    2053             :                         else
    2054             :                         {
    2055           0 :                             v.resize(static_cast<size_t>(result.second));
    2056             :                         }
    2057             :                     }
    2058             :                     else
    2059             :                     {
    2060           0 :                         CPLAssert(false);
    2061             :                     }
    2062         606 :                     anOffsets.resize(nRowCount);
    2063         606 :                     break;
    2064             :                 }
    2065             : 
    2066         853 :                 case OFTInteger64:
    2067             :                 case OFTDate:
    2068             :                 case OFTDateTime:
    2069             :                 case OFTTime:
    2070             :                 {
    2071             :                     auto &v = *(std::get<std::shared_ptr<std::vector<int64_t>>>(
    2072         853 :                         fieldValues));
    2073         853 :                     v.resize(result.second);
    2074         853 :                     break;
    2075             :                 }
    2076             : 
    2077           0 :                 case OFTInteger64List:
    2078             :                 {
    2079             :                     auto &v = *(std::get<std::shared_ptr<std::vector<int64_t>>>(
    2080           0 :                         fieldValues));
    2081           0 :                     if (nRowCount < result.first)
    2082             :                     {
    2083           0 :                         v.resize(anOffsets[nRowCount] / sizeof(v[0]));
    2084             :                     }
    2085             :                     else
    2086             :                     {
    2087           0 :                         v.resize(static_cast<size_t>(result.second));
    2088             :                     }
    2089           0 :                     anOffsets.resize(nRowCount);
    2090           0 :                     break;
    2091             :                 }
    2092             : 
    2093         448 :                 case OFTReal:
    2094             :                 {
    2095         448 :                     if (poFieldDefn->GetSubType() == OFSTFloat32)
    2096             :                     {
    2097             :                         auto &v =
    2098             :                             *(std::get<std::shared_ptr<std::vector<float>>>(
    2099         202 :                                 fieldValues));
    2100         202 :                         v.resize(result.second);
    2101             :                     }
    2102             :                     else
    2103             :                     {
    2104             :                         auto &v =
    2105             :                             *(std::get<std::shared_ptr<std::vector<double>>>(
    2106         246 :                                 fieldValues));
    2107         246 :                         v.resize(result.second);
    2108             :                     }
    2109         448 :                     break;
    2110             :                 }
    2111             : 
    2112         404 :                 case OFTRealList:
    2113             :                 {
    2114         404 :                     if (poFieldDefn->GetSubType() == OFSTFloat32)
    2115             :                     {
    2116             :                         auto &v =
    2117             :                             *(std::get<std::shared_ptr<std::vector<float>>>(
    2118         202 :                                 fieldValues));
    2119         202 :                         if (nRowCount < result.first)
    2120             :                         {
    2121           0 :                             v.resize(anOffsets[nRowCount] / sizeof(v[0]));
    2122             :                         }
    2123             :                         else
    2124             :                         {
    2125         202 :                             v.resize(static_cast<size_t>(result.second));
    2126             :                         }
    2127             :                     }
    2128             :                     else
    2129             :                     {
    2130             :                         auto &v =
    2131             :                             *(std::get<std::shared_ptr<std::vector<double>>>(
    2132         202 :                                 fieldValues));
    2133         202 :                         if (nRowCount < result.first)
    2134             :                         {
    2135           0 :                             v.resize(anOffsets[nRowCount] / sizeof(v[0]));
    2136             :                         }
    2137             :                         else
    2138             :                         {
    2139         202 :                             v.resize(static_cast<size_t>(result.second));
    2140             :                         }
    2141             :                     }
    2142         404 :                     anOffsets.resize(nRowCount);
    2143         404 :                     break;
    2144             :                 }
    2145             : 
    2146         245 :                 case OFTString:
    2147             :                 {
    2148             :                     auto &v =
    2149         245 :                         *(std::get<std::shared_ptr<std::string>>(fieldValues));
    2150         245 :                     if (nRowCount < result.first)
    2151             :                     {
    2152           0 :                         v.resize(anOffsets[nRowCount] / sizeof(v[0]));
    2153             :                     }
    2154             :                     else
    2155             :                     {
    2156         245 :                         v.resize(static_cast<size_t>(result.second));
    2157             :                     }
    2158         245 :                     anOffsets.resize(nRowCount);
    2159         245 :                     break;
    2160             :                 }
    2161             : 
    2162         202 :                 case OFTBinary:
    2163             :                 {
    2164             :                     auto &v = *(std::get<std::shared_ptr<std::vector<uint8_t>>>(
    2165         202 :                         fieldValues));
    2166         202 :                     if (nRowCount < result.first)
    2167             :                     {
    2168           0 :                         v.resize(anOffsets[nRowCount] / sizeof(v[0]));
    2169             :                     }
    2170             :                     else
    2171             :                     {
    2172         202 :                         v.resize(static_cast<size_t>(result.second));
    2173             :                     }
    2174         202 :                     anOffsets.resize(nRowCount);
    2175         202 :                     break;
    2176             :                 }
    2177             : 
    2178           0 :                 default:
    2179             :                 {
    2180           0 :                     CPLAssert(false);
    2181             :                     break;
    2182             :                 }
    2183             :             }
    2184             :         }
    2185             :     }
    2186           0 :     catch (const std::exception &e)
    2187             :     {
    2188           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    2189           0 :         m_bQueryComplete = true;
    2190           0 :         return false;
    2191             :     }
    2192             : 
    2193         283 :     return true;
    2194             : }
    2195             : 
    2196             : /************************************************************************/
    2197             : /*                       SwitchToReadingMode()                          */
    2198             : /************************************************************************/
    2199             : 
    2200         591 : void OGRTileDBLayer::SwitchToReadingMode()
    2201             : {
    2202         591 :     if (m_eCurrentMode == CurrentMode::WriteInProgress)
    2203             :     {
    2204           3 :         m_eCurrentMode = CurrentMode::None;
    2205             :         try
    2206             :         {
    2207           3 :             if (m_array)
    2208             :             {
    2209           3 :                 if (!m_adfXs->empty())
    2210             :                 {
    2211           3 :                     FlushArrays();
    2212             :                 }
    2213           3 :                 m_array->close();
    2214           3 :                 m_array.reset();
    2215             :             }
    2216             :         }
    2217           0 :         catch (const std::exception &e)
    2218             :         {
    2219           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    2220           0 :             m_array.reset();
    2221           0 :             return;
    2222             :         }
    2223             : 
    2224             :         try
    2225             :         {
    2226           3 :             if (m_nTimestamp)
    2227           0 :                 m_array.reset(new tiledb::Array(
    2228           0 :                     *m_ctx, m_osFilename, TILEDB_READ,
    2229           0 :                     tiledb::TemporalPolicy(tiledb::TimeTravel, m_nTimestamp)));
    2230             :             else
    2231           6 :                 m_array.reset(
    2232           6 :                     new tiledb::Array(*m_ctx, m_osFilename, TILEDB_READ));
    2233             :         }
    2234           0 :         catch (const std::exception &e)
    2235             :         {
    2236           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    2237           0 :             return;
    2238             :         }
    2239             :     }
    2240         591 :     m_eCurrentMode = CurrentMode::ReadInProgress;
    2241             : }
    2242             : 
    2243             : /************************************************************************/
    2244             : /*                       GetNextRawFeature()                            */
    2245             : /************************************************************************/
    2246             : 
    2247         723 : OGRFeature *OGRTileDBLayer::GetNextRawFeature()
    2248             : {
    2249         723 :     if (m_eCurrentMode == CurrentMode::WriteInProgress)
    2250             :     {
    2251           2 :         ResetReading();
    2252             :     }
    2253         723 :     if (!m_array)
    2254           0 :         return nullptr;
    2255             : 
    2256         723 :     if (m_nOffsetInResultSet >= m_nRowCountInResultSet)
    2257             :     {
    2258         454 :         if (m_bQueryComplete)
    2259         162 :             return nullptr;
    2260             : 
    2261         292 :         if (!SetupQuery(nullptr))
    2262          67 :             return nullptr;
    2263             :     }
    2264             : 
    2265         494 :     return TranslateCurrentFeature();
    2266             : }
    2267             : 
    2268             : /***********************************************************************/
    2269             : /*                        GetColumnSubNode()                           */
    2270             : /***********************************************************************/
    2271             : 
    2272         199 : static const swq_expr_node *GetColumnSubNode(const swq_expr_node *poNode)
    2273             : {
    2274         199 :     if (poNode->eNodeType == SNT_OPERATION && poNode->nSubExprCount == 2)
    2275             :     {
    2276         199 :         if (poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN)
    2277         158 :             return poNode->papoSubExpr[0];
    2278          41 :         if (poNode->papoSubExpr[1]->eNodeType == SNT_COLUMN)
    2279          22 :             return poNode->papoSubExpr[1];
    2280             :     }
    2281          19 :     return nullptr;
    2282             : }
    2283             : 
    2284             : /***********************************************************************/
    2285             : /*                        GetConstantSubNode()                         */
    2286             : /***********************************************************************/
    2287             : 
    2288         199 : static const swq_expr_node *GetConstantSubNode(const swq_expr_node *poNode)
    2289             : {
    2290         199 :     if (poNode->eNodeType == SNT_OPERATION && poNode->nSubExprCount == 2)
    2291             :     {
    2292         199 :         if (poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT)
    2293         177 :             return poNode->papoSubExpr[1];
    2294          22 :         if (poNode->papoSubExpr[0]->eNodeType == SNT_CONSTANT)
    2295          22 :             return poNode->papoSubExpr[0];
    2296             :     }
    2297           0 :     return nullptr;
    2298             : }
    2299             : 
    2300             : /***********************************************************************/
    2301             : /*                           IsComparisonOp()                          */
    2302             : /***********************************************************************/
    2303             : 
    2304         255 : static bool IsComparisonOp(int op)
    2305             : {
    2306         138 :     return (op == SWQ_EQ || op == SWQ_NE || op == SWQ_LT || op == SWQ_LE ||
    2307         393 :             op == SWQ_GT || op == SWQ_GE);
    2308             : }
    2309             : 
    2310             : /***********************************************************************/
    2311             : /*                       OGRFieldToTimeMS()                            */
    2312             : /***********************************************************************/
    2313             : 
    2314          14 : static int64_t OGRFieldToTimeMS(const OGRField &sField)
    2315             : {
    2316          14 :     GIntBig nVal = sField.Date.Hour * 3600 + sField.Date.Minute * 60;
    2317             :     return static_cast<int64_t>(
    2318          14 :         (static_cast<double>(nVal) + sField.Date.Second) * 1000 + 0.5);
    2319             : }
    2320             : 
    2321             : /***********************************************************************/
    2322             : /*                       OGRFieldToDateDay()                           */
    2323             : /***********************************************************************/
    2324             : 
    2325          16 : static int64_t OGRFieldToDateDay(const OGRField &sField)
    2326             : {
    2327             :     struct tm brokenDown;
    2328          16 :     memset(&brokenDown, 0, sizeof(brokenDown));
    2329          16 :     brokenDown.tm_year = sField.Date.Year - 1900;
    2330          16 :     brokenDown.tm_mon = sField.Date.Month - 1;
    2331          16 :     brokenDown.tm_mday = sField.Date.Day;
    2332          16 :     brokenDown.tm_hour = 0;
    2333          16 :     brokenDown.tm_min = 0;
    2334          16 :     brokenDown.tm_sec = 0;
    2335          16 :     GIntBig nVal = CPLYMDHMSToUnixTime(&brokenDown);
    2336          16 :     return static_cast<int64_t>(nVal / SECONDS_PER_DAY);
    2337             : }
    2338             : 
    2339             : /***********************************************************************/
    2340             : /*                       OGRFieldToDateTimeMS()                        */
    2341             : /***********************************************************************/
    2342             : 
    2343          16 : static int64_t OGRFieldToDateTimeMS(const OGRField &sField)
    2344             : {
    2345             :     struct tm brokenDown;
    2346          16 :     memset(&brokenDown, 0, sizeof(brokenDown));
    2347          16 :     brokenDown.tm_year = sField.Date.Year - 1900;
    2348          16 :     brokenDown.tm_mon = sField.Date.Month - 1;
    2349          16 :     brokenDown.tm_mday = sField.Date.Day;
    2350          16 :     brokenDown.tm_hour = sField.Date.Hour;
    2351          16 :     brokenDown.tm_min = sField.Date.Minute;
    2352          16 :     brokenDown.tm_sec = 0;
    2353          16 :     GIntBig nVal = CPLYMDHMSToUnixTime(&brokenDown);
    2354          16 :     if (sField.Date.TZFlag != 0 && sField.Date.TZFlag != 1)
    2355             :     {
    2356          16 :         nVal -= (sField.Date.TZFlag - 100) * 15 * 60;
    2357             :     }
    2358             :     return static_cast<int64_t>(
    2359          16 :         (static_cast<double>(nVal) + sField.Date.Second) * 1000 + 0.5);
    2360             : }
    2361             : 
    2362             : /***********************************************************************/
    2363             : /*                  CreateQueryConditionForIntType()                   */
    2364             : /***********************************************************************/
    2365             : 
    2366             : template <typename T>
    2367             : static std::unique_ptr<tiledb::QueryCondition>
    2368          30 : CreateQueryConditionForIntType(tiledb::Context &ctx,
    2369             :                                const OGRFieldDefn *poFieldDefn, int nVal,
    2370             :                                tiledb_query_condition_op_t tiledb_op,
    2371             :                                bool &bAlwaysTrue, bool &bAlwaysFalse)
    2372             : {
    2373          48 :     if (nVal >= static_cast<int>(std::numeric_limits<T>::min()) &&
    2374          18 :         nVal <= static_cast<int>(std::numeric_limits<T>::max()))
    2375             :     {
    2376             :         return std::make_unique<tiledb::QueryCondition>(
    2377             :             tiledb::QueryCondition::create(ctx, poFieldDefn->GetNameRef(),
    2378           6 :                                            static_cast<T>(nVal), tiledb_op));
    2379             :     }
    2380          24 :     else if (tiledb_op == TILEDB_EQ)
    2381             :     {
    2382           4 :         bAlwaysFalse = true;
    2383             :     }
    2384          20 :     else if (tiledb_op == TILEDB_NE)
    2385             :     {
    2386           4 :         bAlwaysTrue = true;
    2387             :     }
    2388          16 :     else if (nVal > static_cast<int>(std::numeric_limits<T>::max()))
    2389             :     {
    2390           8 :         bAlwaysTrue = (tiledb_op == TILEDB_LE || tiledb_op == TILEDB_LT);
    2391           8 :         bAlwaysFalse = (tiledb_op == TILEDB_GE || tiledb_op == TILEDB_GT);
    2392             :     }
    2393           8 :     else if (nVal < static_cast<int>(std::numeric_limits<T>::min()))
    2394             :     {
    2395           8 :         bAlwaysTrue = (tiledb_op == TILEDB_GE || tiledb_op == TILEDB_GT);
    2396           8 :         bAlwaysFalse = (tiledb_op == TILEDB_LE || tiledb_op == TILEDB_LT);
    2397             :     }
    2398          24 :     return nullptr;
    2399             : }
    2400             : 
    2401             : /***********************************************************************/
    2402             : /*                     CreateQueryCondition()                          */
    2403             : /***********************************************************************/
    2404             : 
    2405         207 : std::unique_ptr<tiledb::QueryCondition> OGRTileDBLayer::CreateQueryCondition(
    2406             :     int nOperation, bool bColumnIsLeft, const swq_expr_node *poColumn,
    2407             :     const swq_expr_node *poValue, bool &bAlwaysTrue, bool &bAlwaysFalse)
    2408             : {
    2409         207 :     bAlwaysTrue = false;
    2410         207 :     bAlwaysFalse = false;
    2411             : 
    2412         395 :     if (poColumn != nullptr && poValue != nullptr &&
    2413         188 :         poColumn->field_index < m_poFeatureDefn->GetFieldCount())
    2414             :     {
    2415             :         const OGRFieldDefn *poFieldDefn =
    2416         188 :             m_poFeatureDefn->GetFieldDefn(poColumn->field_index);
    2417             : 
    2418         188 :         if (!bColumnIsLeft)
    2419             :         {
    2420             :             /* If "constant op column", then we must reverse */
    2421             :             /* the operator for LE, LT, GE, GT */
    2422          22 :             switch (nOperation)
    2423             :             {
    2424           4 :                 case SWQ_LE:
    2425           4 :                     nOperation = SWQ_GE;
    2426           4 :                     break;
    2427           4 :                 case SWQ_LT:
    2428           4 :                     nOperation = SWQ_GT;
    2429           4 :                     break;
    2430           2 :                 case SWQ_NE: /* do nothing */;
    2431           2 :                     break;
    2432           2 :                 case SWQ_EQ: /* do nothing */;
    2433           2 :                     break;
    2434           4 :                 case SWQ_GE:
    2435           4 :                     nOperation = SWQ_LE;
    2436           4 :                     break;
    2437           6 :                 case SWQ_GT:
    2438           6 :                     nOperation = SWQ_LT;
    2439           6 :                     break;
    2440           0 :                 default:
    2441           0 :                     CPLAssert(false);
    2442             :                     break;
    2443             :             }
    2444             :         }
    2445             : 
    2446         188 :         tiledb_query_condition_op_t tiledb_op = TILEDB_EQ;
    2447         188 :         switch (nOperation)
    2448             :         {
    2449          12 :             case SWQ_LE:
    2450          12 :                 tiledb_op = TILEDB_LE;
    2451          12 :                 break;
    2452          14 :             case SWQ_LT:
    2453          14 :                 tiledb_op = TILEDB_LT;
    2454          14 :                 break;
    2455          30 :             case SWQ_NE:
    2456          30 :                 tiledb_op = TILEDB_NE;
    2457          30 :                 break;
    2458         106 :             case SWQ_EQ:
    2459         106 :                 tiledb_op = TILEDB_EQ;
    2460         106 :                 break;
    2461          12 :             case SWQ_GE:
    2462          12 :                 tiledb_op = TILEDB_GE;
    2463          12 :                 break;
    2464          14 :             case SWQ_GT:
    2465          14 :                 tiledb_op = TILEDB_GT;
    2466          14 :                 break;
    2467           0 :             default:
    2468           0 :                 CPLAssert(false);
    2469             :                 break;
    2470             :         }
    2471             : 
    2472         188 :         switch (poFieldDefn->GetType())
    2473             :         {
    2474         150 :             case OFTInteger:
    2475             :             {
    2476             :                 int nVal;
    2477         150 :                 if (poValue->field_type == SWQ_FLOAT)
    2478           2 :                     nVal = static_cast<int>(poValue->float_value);
    2479         148 :                 else if (SWQ_IS_INTEGER(poValue->field_type))
    2480         148 :                     nVal = static_cast<int>(poValue->int_value);
    2481             :                 else
    2482             :                 {
    2483           0 :                     CPLDebug("TILEDB",
    2484             :                              "Unexpected field_type in SQL expression");
    2485           0 :                     CPLAssert(false);
    2486             :                     return nullptr;
    2487             :                 }
    2488             : 
    2489         150 :                 if (m_aeFieldTypes[poColumn->field_index] == TILEDB_BOOL)
    2490             :                 {
    2491           8 :                     if (nVal == 0 || nVal == 1)
    2492             :                     {
    2493             :                         return std::make_unique<tiledb::QueryCondition>(
    2494          12 :                             tiledb::QueryCondition::create(
    2495           4 :                                 *(m_ctx.get()), poFieldDefn->GetNameRef(),
    2496           4 :                                 static_cast<uint8_t>(nVal), tiledb_op));
    2497             :                     }
    2498           4 :                     else if (tiledb_op == TILEDB_EQ)
    2499             :                     {
    2500           2 :                         bAlwaysFalse = true;
    2501           2 :                         return nullptr;
    2502             :                     }
    2503           2 :                     else if (tiledb_op == TILEDB_NE)
    2504             :                     {
    2505           2 :                         bAlwaysTrue = true;
    2506           2 :                         return nullptr;
    2507             :                     }
    2508             :                 }
    2509         142 :                 else if (m_aeFieldTypes[poColumn->field_index] == TILEDB_INT16)
    2510             :                 {
    2511             :                     return CreateQueryConditionForIntType<int16_t>(
    2512          30 :                         *(m_ctx.get()), poFieldDefn, nVal, tiledb_op,
    2513          30 :                         bAlwaysTrue, bAlwaysFalse);
    2514             :                 }
    2515         112 :                 else if (m_aeFieldTypes[poColumn->field_index] == TILEDB_UINT8)
    2516             :                 {
    2517             :                     return CreateQueryConditionForIntType<uint8_t>(
    2518           0 :                         *(m_ctx.get()), poFieldDefn, nVal, tiledb_op,
    2519           0 :                         bAlwaysTrue, bAlwaysFalse);
    2520             :                 }
    2521         112 :                 else if (m_aeFieldTypes[poColumn->field_index] == TILEDB_UINT16)
    2522             :                 {
    2523             :                     return CreateQueryConditionForIntType<uint16_t>(
    2524           0 :                         *(m_ctx.get()), poFieldDefn, nVal, tiledb_op,
    2525           0 :                         bAlwaysTrue, bAlwaysFalse);
    2526             :                 }
    2527             :                 else
    2528             :                 {
    2529             :                     return std::make_unique<tiledb::QueryCondition>(
    2530         336 :                         tiledb::QueryCondition::create(
    2531         112 :                             *(m_ctx.get()), poFieldDefn->GetNameRef(), nVal,
    2532         112 :                             tiledb_op));
    2533             :                 }
    2534           0 :                 break;
    2535             :             }
    2536             : 
    2537           6 :             case OFTInteger64:
    2538             :             {
    2539             :                 int64_t nVal;
    2540           6 :                 if (poValue->field_type == SWQ_FLOAT)
    2541           2 :                     nVal = static_cast<int64_t>(poValue->float_value);
    2542           4 :                 else if (SWQ_IS_INTEGER(poValue->field_type))
    2543           4 :                     nVal = static_cast<int64_t>(poValue->int_value);
    2544             :                 else
    2545             :                 {
    2546           0 :                     CPLDebug("TILEDB",
    2547             :                              "Unexpected field_type in SQL expression");
    2548           0 :                     CPLAssert(false);
    2549             :                     return nullptr;
    2550             :                 }
    2551             :                 return std::make_unique<tiledb::QueryCondition>(
    2552          12 :                     tiledb::QueryCondition::create(*(m_ctx.get()),
    2553             :                                                    poFieldDefn->GetNameRef(),
    2554           6 :                                                    nVal, tiledb_op));
    2555             :             }
    2556             : 
    2557          12 :             case OFTReal:
    2558             :             {
    2559          12 :                 if (poValue->field_type != SWQ_FLOAT)
    2560             :                 {
    2561           0 :                     CPLDebug("TILEDB",
    2562             :                              "Unexpected field_type in SQL expression");
    2563           0 :                     CPLAssert(false);
    2564             :                     return nullptr;
    2565             :                 }
    2566          12 :                 if (poFieldDefn->GetSubType() == OFSTFloat32)
    2567             :                 {
    2568             :                     return std::make_unique<tiledb::QueryCondition>(
    2569           6 :                         tiledb::QueryCondition::create(
    2570           2 :                             *(m_ctx.get()), poFieldDefn->GetNameRef(),
    2571           2 :                             static_cast<float>(poValue->float_value),
    2572           2 :                             tiledb_op));
    2573             :                 }
    2574             :                 return std::make_unique<tiledb::QueryCondition>(
    2575          30 :                     tiledb::QueryCondition::create(
    2576          10 :                         *(m_ctx.get()), poFieldDefn->GetNameRef(),
    2577          20 :                         poValue->float_value, tiledb_op));
    2578             :             }
    2579             : 
    2580          10 :             case OFTString:
    2581             :             {
    2582          10 :                 if (poValue->field_type != SWQ_STRING)
    2583             :                 {
    2584           0 :                     CPLDebug("TILEDB",
    2585             :                              "Unexpected field_type in SQL expression");
    2586           0 :                     CPLAssert(false);
    2587             :                     return nullptr;
    2588             :                 }
    2589             :                 return std::make_unique<tiledb::QueryCondition>(
    2590          30 :                     tiledb::QueryCondition::create(
    2591          10 :                         *(m_ctx.get()), poFieldDefn->GetNameRef(),
    2592          30 :                         std::string(poValue->string_value), tiledb_op));
    2593             :             }
    2594             : 
    2595           4 :             case OFTDateTime:
    2596             :             {
    2597           4 :                 if (poValue->field_type == SWQ_TIMESTAMP ||
    2598           0 :                     poValue->field_type == SWQ_DATE ||
    2599           0 :                     poValue->field_type == SWQ_TIME)
    2600             :                 {
    2601             :                     OGRField sField;
    2602           4 :                     if (OGRParseDate(poValue->string_value, &sField, 0))
    2603             :                     {
    2604             :                         return std::make_unique<tiledb::QueryCondition>(
    2605           6 :                             tiledb::QueryCondition::create(
    2606           2 :                                 *(m_ctx.get()), poFieldDefn->GetNameRef(),
    2607           2 :                                 OGRFieldToDateTimeMS(sField), tiledb_op));
    2608             :                     }
    2609             :                     else
    2610             :                     {
    2611           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
    2612             :                                  "Failed to parse %s as a date time",
    2613           2 :                                  poValue->string_value);
    2614             :                     }
    2615             :                 }
    2616           2 :                 break;
    2617             :             }
    2618             : 
    2619           4 :             case OFTDate:
    2620             :             {
    2621           4 :                 if (poValue->field_type == SWQ_TIMESTAMP ||
    2622           0 :                     poValue->field_type == SWQ_DATE ||
    2623           0 :                     poValue->field_type == SWQ_TIME)
    2624             :                 {
    2625             :                     OGRField sField;
    2626           4 :                     if (OGRParseDate(poValue->string_value, &sField, 0))
    2627             :                     {
    2628             :                         return std::make_unique<tiledb::QueryCondition>(
    2629           6 :                             tiledb::QueryCondition::create(
    2630           2 :                                 *(m_ctx.get()), poFieldDefn->GetNameRef(),
    2631           2 :                                 OGRFieldToDateDay(sField), tiledb_op));
    2632             :                     }
    2633             :                     else
    2634             :                     {
    2635           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
    2636             :                                  "Failed to parse %s as a date",
    2637           2 :                                  poValue->string_value);
    2638             :                     }
    2639             :                 }
    2640           2 :                 break;
    2641             :             }
    2642             : 
    2643             : #ifdef not_supported_by_tiledb
    2644             :             // throws the following error:
    2645             :             // C API: TileDB Internal, std::exception; Cannot perform query comparison; Unsupported query conditional type on
    2646             :             case OFTTime:
    2647             :             {
    2648             :                 if (poValue->field_type == SWQ_TIMESTAMP ||
    2649             :                     poValue->field_type == SWQ_DATE ||
    2650             :                     poValue->field_type == SWQ_TIME)
    2651             :                 {
    2652             :                     OGRField sField;
    2653             :                     if (OGRParseDate(poValue->string_value, &sField, 0))
    2654             :                     {
    2655             :                         return std::make_unique<tiledb::QueryCondition>(
    2656             :                             tiledb::QueryCondition::create(
    2657             :                                 *(m_ctx.get()), poFieldDefn->GetNameRef(),
    2658             :                                 OGRFieldToTimeMS(sField), tiledb_op));
    2659             :                     }
    2660             :                 }
    2661             :                 break;
    2662             :             }
    2663             : #endif
    2664             : 
    2665           2 :             default:
    2666           2 :                 break;
    2667             :         }
    2668             :     }
    2669          25 :     return nullptr;
    2670             : }
    2671             : 
    2672             : /***********************************************************************/
    2673             : /*                     CreateQueryCondition()                          */
    2674             : /***********************************************************************/
    2675             : 
    2676             : std::unique_ptr<tiledb::QueryCondition>
    2677         323 : OGRTileDBLayer::CreateQueryCondition(const swq_expr_node *poNode,
    2678             :                                      bool &bAlwaysTrue, bool &bAlwaysFalse)
    2679             : {
    2680         323 :     bAlwaysTrue = false;
    2681         323 :     bAlwaysFalse = false;
    2682             : 
    2683             :     // A AND B
    2684         323 :     if (poNode->eNodeType == SNT_OPERATION && poNode->nOperation == SWQ_AND &&
    2685          30 :         poNode->nSubExprCount == 2)
    2686             :     {
    2687             :         bool bAlwaysTrueLeft, bAlwaysFalseLeft, bAlwaysTrueRight,
    2688             :             bAlwaysFalseRight;
    2689          30 :         auto left = CreateQueryCondition(poNode->papoSubExpr[0],
    2690          60 :                                          bAlwaysTrueLeft, bAlwaysFalseLeft);
    2691          30 :         auto right = CreateQueryCondition(poNode->papoSubExpr[1],
    2692          60 :                                           bAlwaysTrueRight, bAlwaysFalseRight);
    2693          30 :         if (bAlwaysFalseLeft || bAlwaysFalseRight)
    2694             :         {
    2695           5 :             bAlwaysFalse = true;
    2696           5 :             return nullptr;
    2697             :         }
    2698          25 :         if (bAlwaysTrueLeft)
    2699             :         {
    2700           3 :             if (bAlwaysTrueRight)
    2701             :             {
    2702           1 :                 bAlwaysTrue = true;
    2703           1 :                 return nullptr;
    2704             :             }
    2705           2 :             return right;
    2706             :         }
    2707          22 :         if (bAlwaysTrueRight)
    2708             :         {
    2709           2 :             return left;
    2710             :         }
    2711          20 :         if (left && right)
    2712             :         {
    2713             :             return std::make_unique<tiledb::QueryCondition>(
    2714          32 :                 left->combine(*(right.get()), TILEDB_AND));
    2715             :         }
    2716             :         // Returning only left or right member is OK for a AND
    2717           4 :         m_bAttributeFilterPartiallyTranslated = true;
    2718           4 :         if (left)
    2719           2 :             return left;
    2720           2 :         return right;
    2721             :     }
    2722             : 
    2723             :     // A OR B
    2724         293 :     else if (poNode->eNodeType == SNT_OPERATION &&
    2725         293 :              poNode->nOperation == SWQ_OR && poNode->nSubExprCount == 2)
    2726             :     {
    2727             :         bool bAlwaysTrueLeft, bAlwaysFalseLeft, bAlwaysTrueRight,
    2728             :             bAlwaysFalseRight;
    2729          34 :         auto left = CreateQueryCondition(poNode->papoSubExpr[0],
    2730          68 :                                          bAlwaysTrueLeft, bAlwaysFalseLeft);
    2731          34 :         auto right = CreateQueryCondition(poNode->papoSubExpr[1],
    2732          68 :                                           bAlwaysTrueRight, bAlwaysFalseRight);
    2733          34 :         if (bAlwaysTrueLeft || bAlwaysTrueRight)
    2734             :         {
    2735           5 :             bAlwaysTrue = true;
    2736           5 :             return nullptr;
    2737             :         }
    2738          29 :         if (bAlwaysFalseLeft)
    2739             :         {
    2740           3 :             if (bAlwaysFalseRight)
    2741             :             {
    2742           1 :                 bAlwaysFalse = true;
    2743           1 :                 return nullptr;
    2744             :             }
    2745           2 :             return right;
    2746             :         }
    2747          26 :         if (bAlwaysFalseRight)
    2748             :         {
    2749           2 :             return left;
    2750             :         }
    2751          24 :         if (left && right)
    2752             :         {
    2753             :             return std::make_unique<tiledb::QueryCondition>(
    2754          32 :                 left->combine(*(right.get()), TILEDB_OR));
    2755             :         }
    2756           8 :         m_bAttributeFilterPartiallyTranslated = true;
    2757           8 :         return nullptr;
    2758             :     }
    2759             : 
    2760             :     // field_name IN (constant, ..., constant)
    2761         777 :     else if (poNode->eNodeType == SNT_OPERATION &&
    2762         259 :              poNode->nOperation == SWQ_IN && poNode->nSubExprCount >= 2 &&
    2763         522 :              poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
    2764           4 :              poNode->papoSubExpr[0]->field_index <
    2765           4 :                  m_poFeatureDefn->GetFieldCount())
    2766             :     {
    2767           4 :         std::unique_ptr<tiledb::QueryCondition> cond;
    2768          12 :         for (int i = 1; i < poNode->nSubExprCount; ++i)
    2769             :         {
    2770           8 :             if (poNode->papoSubExpr[i]->eNodeType == SNT_CONSTANT)
    2771             :             {
    2772             :                 bool bAlwaysTrueTmp;
    2773             :                 bool bAlwaysFalseTmp;
    2774             :                 auto newCond = CreateQueryCondition(
    2775           8 :                     SWQ_EQ, true, poNode->papoSubExpr[0],
    2776           8 :                     poNode->papoSubExpr[i], bAlwaysTrueTmp, bAlwaysFalseTmp);
    2777           8 :                 if (bAlwaysFalseTmp)
    2778           0 :                     continue;
    2779           8 :                 if (!newCond)
    2780             :                 {
    2781           0 :                     m_bAttributeFilterPartiallyTranslated = true;
    2782           0 :                     return nullptr;
    2783             :                 }
    2784           8 :                 if (!cond)
    2785             :                 {
    2786           4 :                     cond = std::move(newCond);
    2787             :                 }
    2788             :                 else
    2789             :                 {
    2790           8 :                     cond = std::make_unique<tiledb::QueryCondition>(
    2791          12 :                         cond->combine(*(newCond.get()), TILEDB_OR));
    2792             :                 }
    2793             :             }
    2794             :             else
    2795             :             {
    2796           0 :                 m_bAttributeFilterPartiallyTranslated = true;
    2797           0 :                 return nullptr;
    2798             :             }
    2799             :         }
    2800           4 :         if (!cond)
    2801           0 :             bAlwaysFalse = true;
    2802           4 :         return cond;
    2803             :     }
    2804             : 
    2805             :     // field_name =/<>/</>/<=/>= constant (or the reverse)
    2806         255 :     else if (poNode->eNodeType == SNT_OPERATION &&
    2807         510 :              IsComparisonOp(poNode->nOperation) && poNode->nSubExprCount == 2)
    2808             :     {
    2809         199 :         const swq_expr_node *poColumn = GetColumnSubNode(poNode);
    2810         199 :         const swq_expr_node *poValue = GetConstantSubNode(poNode);
    2811             :         return CreateQueryCondition(
    2812         199 :             poNode->nOperation, poColumn == poNode->papoSubExpr[0], poColumn,
    2813         199 :             poValue, bAlwaysTrue, bAlwaysFalse);
    2814             :     }
    2815             : 
    2816             :     // field_name IS NULL
    2817         168 :     else if (poNode->eNodeType == SNT_OPERATION &&
    2818          56 :              poNode->nOperation == SWQ_ISNULL && poNode->nSubExprCount == 1 &&
    2819         140 :              poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
    2820          28 :              poNode->papoSubExpr[0]->field_index <
    2821          28 :                  m_poFeatureDefn->GetFieldCount())
    2822             :     {
    2823             :         const OGRFieldDefn *poFieldDefn =
    2824          28 :             m_poFeatureDefn->GetFieldDefn(poNode->papoSubExpr[0]->field_index);
    2825          28 :         if (!poFieldDefn->IsNullable())
    2826             :         {
    2827          14 :             bAlwaysFalse = true;
    2828          14 :             return nullptr;
    2829             :         }
    2830          28 :         auto qc = std::make_unique<tiledb::QueryCondition>(*(m_ctx.get()));
    2831          14 :         qc->init(poFieldDefn->GetNameRef(), nullptr, 0, TILEDB_EQ);
    2832          14 :         return qc;
    2833             :     }
    2834             : 
    2835             :     // field_name IS NOT NULL
    2836          84 :     else if (poNode->eNodeType == SNT_OPERATION &&
    2837          28 :              poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1 &&
    2838          28 :              poNode->papoSubExpr[0]->nOperation == SWQ_ISNULL &&
    2839          28 :              poNode->papoSubExpr[0]->nSubExprCount == 1 &&
    2840          84 :              poNode->papoSubExpr[0]->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
    2841          28 :              poNode->papoSubExpr[0]->papoSubExpr[0]->field_index <
    2842          28 :                  m_poFeatureDefn->GetFieldCount())
    2843             :     {
    2844          56 :         const OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(
    2845          28 :             poNode->papoSubExpr[0]->papoSubExpr[0]->field_index);
    2846          28 :         if (!poFieldDefn->IsNullable())
    2847             :         {
    2848          14 :             bAlwaysTrue = true;
    2849          14 :             return nullptr;
    2850             :         }
    2851          28 :         auto qc = std::make_unique<tiledb::QueryCondition>(*(m_ctx.get()));
    2852          14 :         qc->init(poFieldDefn->GetNameRef(), nullptr, 0, TILEDB_NE);
    2853          14 :         return qc;
    2854             :     }
    2855             : 
    2856           0 :     m_bAttributeFilterPartiallyTranslated = true;
    2857           0 :     return nullptr;
    2858             : }
    2859             : 
    2860             : /***********************************************************************/
    2861             : /*                         SetAttributeFilter()                        */
    2862             : /***********************************************************************/
    2863             : 
    2864         215 : OGRErr OGRTileDBLayer::SetAttributeFilter(const char *pszFilter)
    2865             : {
    2866         215 :     m_bAttributeFilterPartiallyTranslated = false;
    2867         215 :     m_poQueryCondition.reset();
    2868         215 :     m_bAttributeFilterAlwaysFalse = false;
    2869         215 :     m_bAttributeFilterAlwaysTrue = false;
    2870         215 :     OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter);
    2871         215 :     if (eErr != OGRERR_NONE)
    2872           0 :         return eErr;
    2873             : 
    2874         215 :     if (m_poAttrQuery != nullptr)
    2875             :     {
    2876         195 :         if (m_nUseOptimizedAttributeFilter < 0)
    2877             :         {
    2878           3 :             m_nUseOptimizedAttributeFilter = CPLTestBool(CPLGetConfigOption(
    2879             :                 "OGR_TILEDB_OPTIMIZED_ATTRIBUTE_FILTER", "YES"));
    2880             :         }
    2881         195 :         if (m_nUseOptimizedAttributeFilter)
    2882             :         {
    2883             :             swq_expr_node *poNode =
    2884         195 :                 static_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
    2885         195 :             poNode->ReplaceBetweenByGEAndLERecurse();
    2886         195 :             poNode->PushNotOperationDownToStack();
    2887             :             bool bAlwaysTrue, bAlwaysFalse;
    2888         195 :             CPLErrorReset();
    2889             :             try
    2890             :             {
    2891             :                 m_poQueryCondition =
    2892         195 :                     CreateQueryCondition(poNode, bAlwaysTrue, bAlwaysFalse);
    2893             :             }
    2894           0 :             catch (const std::exception &e)
    2895             :             {
    2896           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    2897           0 :                 return OGRERR_FAILURE;
    2898             :             }
    2899         195 :             if (CPLGetLastErrorType() == CE_Failure)
    2900           4 :                 return OGRERR_FAILURE;
    2901         191 :             if (m_poQueryCondition && m_bAttributeFilterPartiallyTranslated)
    2902             :             {
    2903           4 :                 CPLDebug("TILEDB", "Attribute filter partially translated to "
    2904             :                                    "libtiledb query condition");
    2905             :             }
    2906         187 :             else if (!m_poQueryCondition)
    2907             :             {
    2908          61 :                 CPLDebug("TILEDB", "Attribute filter could not be translated "
    2909             :                                    "to libtiledb query condition");
    2910             :             }
    2911         191 :             m_bAttributeFilterAlwaysTrue = bAlwaysTrue;
    2912         191 :             m_bAttributeFilterAlwaysFalse = bAlwaysFalse;
    2913             :         }
    2914             :     }
    2915             : 
    2916         211 :     return OGRERR_NONE;
    2917             : }
    2918             : 
    2919             : /************************************************************************/
    2920             : /*                        GetMetadataItem()                             */
    2921             : /************************************************************************/
    2922             : 
    2923         186 : const char *OGRTileDBLayer::GetMetadataItem(const char *pszName,
    2924             :                                             const char *pszDomain)
    2925             : {
    2926         186 :     if (pszDomain && EQUAL(pszDomain, "_DEBUG_"))
    2927             :     {
    2928         180 :         if (EQUAL(pszName, "ATTRIBUTE_FILTER_TRANSLATION"))
    2929             :         {
    2930         212 :             if (!m_poQueryCondition && !m_bAttributeFilterAlwaysFalse &&
    2931          32 :                 !m_bAttributeFilterAlwaysTrue)
    2932          10 :                 return "NONE";
    2933         170 :             if (m_bAttributeFilterPartiallyTranslated)
    2934           4 :                 return "PARTIAL";
    2935         166 :             return "WHOLE";
    2936             :         }
    2937             :     }
    2938           6 :     return OGRLayer::GetMetadataItem(pszName, pszDomain);
    2939             : }
    2940             : 
    2941             : /************************************************************************/
    2942             : /*                    TranslateCurrentFeature()                         */
    2943             : /************************************************************************/
    2944             : 
    2945         503 : OGRFeature *OGRTileDBLayer::TranslateCurrentFeature()
    2946             : {
    2947         503 :     auto poFeature = new OGRFeature(m_poFeatureDefn);
    2948             : 
    2949         503 :     poFeature->SetFID((*m_anFIDs)[m_nOffsetInResultSet]);
    2950             : 
    2951             :     // For a variable size attribute (list type), return the number of elements
    2952             :     // for the feature of index m_nOffsetInResultSet.
    2953        2258 :     const auto GetEltCount = [this](const std::vector<uint64_t> &anOffsets,
    2954             :                                     size_t nEltSizeInBytes,
    2955        5420 :                                     size_t nTotalSizeInBytes)
    2956             :     {
    2957             :         uint64_t nSize;
    2958        2258 :         if (static_cast<size_t>(m_nOffsetInResultSet + 1) < anOffsets.size())
    2959             :         {
    2960        1808 :             nSize = anOffsets[m_nOffsetInResultSet + 1] -
    2961         904 :                     anOffsets[m_nOffsetInResultSet];
    2962             :         }
    2963             :         else
    2964             :         {
    2965        1354 :             nSize = nTotalSizeInBytes - anOffsets[m_nOffsetInResultSet];
    2966             :         }
    2967        2258 :         return static_cast<size_t>(nSize / nEltSizeInBytes);
    2968         503 :     };
    2969             : 
    2970         503 :     if (!m_poFeatureDefn->GetGeomFieldDefn(0)->IsIgnored())
    2971             :     {
    2972         493 :         const char *pszGeomColName = GetDatabaseGeomColName();
    2973         493 :         if (pszGeomColName)
    2974             :         {
    2975             :             const size_t nWKBSize =
    2976         489 :                 GetEltCount(*m_anGeometryOffsets, 1, m_abyGeometries->size());
    2977         489 :             OGRGeometry *poGeom = nullptr;
    2978         978 :             OGRGeometryFactory::createFromWkb(
    2979         978 :                 m_abyGeometries->data() +
    2980             :                     static_cast<size_t>(
    2981         489 :                         (*m_anGeometryOffsets)[m_nOffsetInResultSet]),
    2982         489 :                 GetSpatialRef(), &poGeom, nWKBSize);
    2983         489 :             poFeature->SetGeometryDirectly(poGeom);
    2984             :         }
    2985             :         else
    2986             :         {
    2987             :             OGRPoint *poPoint =
    2988           4 :                 m_adfZs->empty()
    2989           8 :                     ? new OGRPoint((*m_adfXs)[m_nOffsetInResultSet],
    2990           3 :                                    (*m_adfYs)[m_nOffsetInResultSet])
    2991           1 :                     : new OGRPoint((*m_adfXs)[m_nOffsetInResultSet],
    2992           1 :                                    (*m_adfYs)[m_nOffsetInResultSet],
    2993           1 :                                    (*m_adfZs)[m_nOffsetInResultSet]);
    2994           4 :             poPoint->assignSpatialReference(GetSpatialRef());
    2995           4 :             poFeature->SetGeometryDirectly(poPoint);
    2996             :         }
    2997             :     }
    2998             : 
    2999         503 :     const int nFieldCount = m_poFeatureDefn->GetFieldCount();
    3000        5957 :     for (int i = 0; i < nFieldCount; ++i)
    3001             :     {
    3002             :         const OGRFieldDefn *poFieldDefn =
    3003        5454 :             m_poFeatureDefn->GetFieldDefnUnsafe(i);
    3004        5454 :         if (poFieldDefn->IsIgnored())
    3005             :         {
    3006          10 :             continue;
    3007             :         }
    3008        5444 :         if (poFieldDefn->IsNullable())
    3009             :         {
    3010        2974 :             if (!m_aFieldValidity[i][m_nOffsetInResultSet])
    3011             :             {
    3012         558 :                 poFeature->SetFieldNull(i);
    3013         558 :                 continue;
    3014             :             }
    3015             :         }
    3016             : 
    3017        4886 :         const auto &anOffsets = *(m_aFieldValueOffsets[i]);
    3018        4886 :         auto &fieldValues = m_aFieldValues[i];
    3019        4886 :         switch (poFieldDefn->GetType())
    3020             :         {
    3021        1389 :             case OFTInteger:
    3022             :             {
    3023        1389 :                 if (m_aeFieldTypes[i] == TILEDB_BOOL)
    3024             :                 {
    3025             :                     const auto &v = *(
    3026         225 :                         std::get<std::shared_ptr<VECTOR_OF_BOOL>>(fieldValues));
    3027         225 :                     poFeature->SetFieldSameTypeUnsafe(i,
    3028         225 :                                                       v[m_nOffsetInResultSet]);
    3029             :                 }
    3030        1164 :                 else if (m_aeFieldTypes[i] == TILEDB_INT16)
    3031             :                 {
    3032             :                     const auto &v =
    3033             :                         *(std::get<std::shared_ptr<std::vector<int16_t>>>(
    3034         225 :                             fieldValues));
    3035         225 :                     poFeature->SetFieldSameTypeUnsafe(i,
    3036         225 :                                                       v[m_nOffsetInResultSet]);
    3037             :                 }
    3038         939 :                 else if (m_aeFieldTypes[i] == TILEDB_INT32)
    3039             :                 {
    3040             :                     const auto &v =
    3041             :                         *(std::get<std::shared_ptr<std::vector<int32_t>>>(
    3042         489 :                             fieldValues));
    3043         489 :                     poFeature->SetFieldSameTypeUnsafe(i,
    3044         489 :                                                       v[m_nOffsetInResultSet]);
    3045             :                 }
    3046         450 :                 else if (m_aeFieldTypes[i] == TILEDB_UINT8)
    3047             :                 {
    3048             :                     const auto &v =
    3049             :                         *(std::get<std::shared_ptr<std::vector<uint8_t>>>(
    3050         225 :                             fieldValues));
    3051         225 :                     poFeature->SetFieldSameTypeUnsafe(i,
    3052         225 :                                                       v[m_nOffsetInResultSet]);
    3053             :                 }
    3054         225 :                 else if (m_aeFieldTypes[i] == TILEDB_UINT16)
    3055             :                 {
    3056             :                     const auto &v =
    3057             :                         *(std::get<std::shared_ptr<std::vector<uint16_t>>>(
    3058         225 :                             fieldValues));
    3059         225 :                     poFeature->SetFieldSameTypeUnsafe(i,
    3060         225 :                                                       v[m_nOffsetInResultSet]);
    3061             :                 }
    3062             :                 else
    3063             :                 {
    3064           0 :                     CPLAssert(false);
    3065             :                 }
    3066        1389 :                 break;
    3067             :             }
    3068             : 
    3069         675 :             case OFTIntegerList:
    3070             :             {
    3071         675 :                 if (m_aeFieldTypes[i] == TILEDB_BOOL)
    3072             :                 {
    3073             :                     const auto &v = *(
    3074         225 :                         std::get<std::shared_ptr<VECTOR_OF_BOOL>>(fieldValues));
    3075         225 :                     const size_t nEltCount = GetEltCount(
    3076             :                         anOffsets, sizeof(v[0]), v.size() * sizeof(v[0]));
    3077         450 :                     std::vector<int32_t> tmp;
    3078             :                     const auto *inPtr =
    3079         225 :                         v.data() +
    3080         225 :                         static_cast<size_t>(anOffsets[m_nOffsetInResultSet] /
    3081         225 :                                             sizeof(v[0]));
    3082         605 :                     for (size_t j = 0; j < nEltCount; ++j)
    3083         380 :                         tmp.push_back(inPtr[j]);
    3084         225 :                     poFeature->SetField(i, static_cast<int>(nEltCount),
    3085         225 :                                         tmp.data());
    3086             :                 }
    3087         450 :                 else if (m_aeFieldTypes[i] == TILEDB_INT16)
    3088             :                 {
    3089             :                     const auto &v =
    3090             :                         *(std::get<std::shared_ptr<std::vector<int16_t>>>(
    3091         225 :                             fieldValues));
    3092         225 :                     const size_t nEltCount = GetEltCount(
    3093         225 :                         anOffsets, sizeof(v[0]), v.size() * sizeof(v[0]));
    3094         450 :                     std::vector<int32_t> tmp;
    3095             :                     const auto *inPtr =
    3096         225 :                         v.data() +
    3097         225 :                         static_cast<size_t>(anOffsets[m_nOffsetInResultSet] /
    3098         225 :                                             sizeof(v[0]));
    3099         605 :                     for (size_t j = 0; j < nEltCount; ++j)
    3100         380 :                         tmp.push_back(inPtr[j]);
    3101         225 :                     poFeature->SetField(i, static_cast<int>(nEltCount),
    3102         225 :                                         tmp.data());
    3103             :                 }
    3104         225 :                 else if (m_aeFieldTypes[i] == TILEDB_INT32)
    3105             :                 {
    3106             :                     const auto &v =
    3107             :                         *(std::get<std::shared_ptr<std::vector<int32_t>>>(
    3108         225 :                             fieldValues));
    3109         225 :                     const size_t nEltCount = GetEltCount(
    3110         225 :                         anOffsets, sizeof(v[0]), v.size() * sizeof(v[0]));
    3111         225 :                     poFeature->SetField(
    3112             :                         i, static_cast<int>(nEltCount),
    3113         225 :                         v.data() + static_cast<size_t>(
    3114         225 :                                        anOffsets[m_nOffsetInResultSet] /
    3115             :                                        sizeof(v[0])));
    3116             :                 }
    3117           0 :                 else if (m_aeFieldTypes[i] == TILEDB_UINT8)
    3118             :                 {
    3119             :                     const auto &v =
    3120             :                         *(std::get<std::shared_ptr<std::vector<uint8_t>>>(
    3121           0 :                             fieldValues));
    3122           0 :                     const size_t nEltCount = GetEltCount(
    3123             :                         anOffsets, sizeof(v[0]), v.size() * sizeof(v[0]));
    3124           0 :                     std::vector<int32_t> tmp;
    3125             :                     const auto *inPtr =
    3126           0 :                         v.data() +
    3127           0 :                         static_cast<size_t>(anOffsets[m_nOffsetInResultSet] /
    3128           0 :                                             sizeof(v[0]));
    3129           0 :                     for (size_t j = 0; j < nEltCount; ++j)
    3130           0 :                         tmp.push_back(inPtr[j]);
    3131           0 :                     poFeature->SetField(i, static_cast<int>(nEltCount),
    3132           0 :                                         tmp.data());
    3133             :                 }
    3134           0 :                 else if (m_aeFieldTypes[i] == TILEDB_UINT16)
    3135             :                 {
    3136             :                     const auto &v =
    3137             :                         *(std::get<std::shared_ptr<std::vector<uint16_t>>>(
    3138           0 :                             fieldValues));
    3139           0 :                     const size_t nEltCount = GetEltCount(
    3140           0 :                         anOffsets, sizeof(v[0]), v.size() * sizeof(v[0]));
    3141           0 :                     poFeature->SetField(
    3142             :                         i, static_cast<int>(nEltCount),
    3143           0 :                         v.data() + static_cast<size_t>(
    3144           0 :                                        anOffsets[m_nOffsetInResultSet] /
    3145             :                                        sizeof(v[0])));
    3146             :                 }
    3147             :                 else
    3148             :                 {
    3149           0 :                     CPLAssert(false);
    3150             :                 }
    3151         675 :                 break;
    3152             :             }
    3153             : 
    3154         419 :             case OFTInteger64:
    3155             :             {
    3156             :                 const auto &v =
    3157             :                     *(std::get<std::shared_ptr<std::vector<int64_t>>>(
    3158         419 :                         fieldValues));
    3159         419 :                 poFeature->SetFieldSameTypeUnsafe(
    3160         419 :                     i, static_cast<GIntBig>(v[m_nOffsetInResultSet]));
    3161         419 :                 break;
    3162             :             }
    3163             : 
    3164           0 :             case OFTInteger64List:
    3165             :             {
    3166             :                 const auto &v =
    3167             :                     *(std::get<std::shared_ptr<std::vector<int64_t>>>(
    3168           0 :                         fieldValues));
    3169           0 :                 const size_t nEltCount = GetEltCount(anOffsets, sizeof(v[0]),
    3170           0 :                                                      v.size() * sizeof(v[0]));
    3171           0 :                 poFeature->SetField(
    3172             :                     i, static_cast<int>(nEltCount),
    3173           0 :                     v.data() +
    3174           0 :                         static_cast<size_t>(anOffsets[m_nOffsetInResultSet] /
    3175             :                                             sizeof(v[0])));
    3176           0 :                 break;
    3177             :             }
    3178             : 
    3179         634 :             case OFTReal:
    3180             :             {
    3181         634 :                 if (poFieldDefn->GetSubType() == OFSTFloat32)
    3182             :                 {
    3183             :                     const auto &v =
    3184             :                         *(std::get<std::shared_ptr<std::vector<float>>>(
    3185         225 :                             fieldValues));
    3186         225 :                     poFeature->SetFieldSameTypeUnsafe(i,
    3187         225 :                                                       v[m_nOffsetInResultSet]);
    3188             :                 }
    3189             :                 else
    3190             :                 {
    3191             :                     const auto &v =
    3192             :                         *(std::get<std::shared_ptr<std::vector<double>>>(
    3193         409 :                             fieldValues));
    3194         409 :                     poFeature->SetFieldSameTypeUnsafe(i,
    3195         409 :                                                       v[m_nOffsetInResultSet]);
    3196             :                 }
    3197         634 :                 break;
    3198             :             }
    3199             : 
    3200         450 :             case OFTRealList:
    3201             :             {
    3202         450 :                 if (poFieldDefn->GetSubType() == OFSTFloat32)
    3203             :                 {
    3204             :                     const auto &v =
    3205             :                         *(std::get<std::shared_ptr<std::vector<float>>>(
    3206         225 :                             fieldValues));
    3207         225 :                     const size_t nEltCount = GetEltCount(
    3208         225 :                         anOffsets, sizeof(v[0]), v.size() * sizeof(v[0]));
    3209         450 :                     std::vector<double> tmp;
    3210             :                     const auto *inPtr =
    3211         225 :                         v.data() +
    3212         225 :                         static_cast<size_t>(anOffsets[m_nOffsetInResultSet] /
    3213         225 :                                             sizeof(v[0]));
    3214         795 :                     for (size_t j = 0; j < nEltCount; ++j)
    3215         570 :                         tmp.push_back(inPtr[j]);
    3216         225 :                     poFeature->SetField(i, static_cast<int>(nEltCount),
    3217         225 :                                         tmp.data());
    3218             :                 }
    3219             :                 else
    3220             :                 {
    3221             :                     const auto &v =
    3222             :                         *(std::get<std::shared_ptr<std::vector<double>>>(
    3223         225 :                             fieldValues));
    3224         225 :                     const size_t nEltCount = GetEltCount(
    3225         225 :                         anOffsets, sizeof(v[0]), v.size() * sizeof(v[0]));
    3226         225 :                     poFeature->SetField(
    3227             :                         i, static_cast<int>(nEltCount),
    3228         225 :                         v.data() + static_cast<size_t>(
    3229         225 :                                        anOffsets[m_nOffsetInResultSet] /
    3230             :                                        sizeof(v[0])));
    3231             :                 }
    3232         450 :                 break;
    3233             :             }
    3234             : 
    3235         419 :             case OFTString:
    3236             :             {
    3237             :                 auto &v =
    3238         419 :                     *(std::get<std::shared_ptr<std::string>>(fieldValues));
    3239         419 :                 const size_t nEltCount = GetEltCount(anOffsets, 1, v.size());
    3240         419 :                 if (static_cast<size_t>(m_nOffsetInResultSet + 1) <
    3241         419 :                     anOffsets.size())
    3242             :                 {
    3243             :                     char &chSavedRef =
    3244         234 :                         v[static_cast<size_t>(anOffsets[m_nOffsetInResultSet]) +
    3245         234 :                           nEltCount];
    3246         234 :                     const char chSavedBackup = chSavedRef;
    3247         234 :                     chSavedRef = 0;
    3248         234 :                     poFeature->SetField(
    3249         468 :                         i, v.data() + static_cast<size_t>(
    3250         234 :                                           anOffsets[m_nOffsetInResultSet]));
    3251         234 :                     chSavedRef = chSavedBackup;
    3252             :                 }
    3253             :                 else
    3254             :                 {
    3255         185 :                     poFeature->SetField(
    3256         370 :                         i, v.data() + static_cast<size_t>(
    3257         185 :                                           anOffsets[m_nOffsetInResultSet]));
    3258             :                 }
    3259         419 :                 break;
    3260             :             }
    3261             : 
    3262         225 :             case OFTBinary:
    3263             :             {
    3264             :                 auto &v = *(std::get<std::shared_ptr<std::vector<uint8_t>>>(
    3265         225 :                     fieldValues));
    3266         225 :                 const size_t nEltCount = GetEltCount(anOffsets, 1, v.size());
    3267         225 :                 poFeature->SetField(
    3268             :                     i, static_cast<int>(nEltCount),
    3269             :                     static_cast<const void *>(
    3270         225 :                         v.data() +
    3271         225 :                         static_cast<size_t>(anOffsets[m_nOffsetInResultSet])));
    3272         225 :                 break;
    3273             :             }
    3274             : 
    3275         225 :             case OFTDate:
    3276             :             {
    3277             :                 const auto &v =
    3278             :                     *(std::get<std::shared_ptr<std::vector<int64_t>>>(
    3279         225 :                         fieldValues));
    3280         225 :                 auto psField = poFeature->GetRawFieldRef(i);
    3281         225 :                 psField->Set.nMarker1 = OGRUnsetMarker;
    3282         225 :                 psField->Set.nMarker2 = OGRUnsetMarker;
    3283         225 :                 psField->Set.nMarker3 = OGRUnsetMarker;
    3284         225 :                 constexpr int DAYS_IN_YEAR_APPROX = 365;
    3285             :                 // Avoid overflow in the x SECONDS_PER_DAY muliplication
    3286         450 :                 if (v[m_nOffsetInResultSet] > DAYS_IN_YEAR_APPROX * 100000 ||
    3287         225 :                     v[m_nOffsetInResultSet] < -DAYS_IN_YEAR_APPROX * 100000)
    3288             :                 {
    3289           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Invalid date value");
    3290             :                 }
    3291             :                 else
    3292             :                 {
    3293             :                     GIntBig timestamp =
    3294         225 :                         static_cast<GIntBig>(v[m_nOffsetInResultSet]) *
    3295         225 :                         SECONDS_PER_DAY;
    3296             :                     struct tm dt;
    3297         225 :                     CPLUnixTimeToYMDHMS(timestamp, &dt);
    3298             : 
    3299         225 :                     psField->Date.Year = static_cast<GInt16>(dt.tm_year + 1900);
    3300         225 :                     psField->Date.Month = static_cast<GByte>(dt.tm_mon + 1);
    3301         225 :                     psField->Date.Day = static_cast<GByte>(dt.tm_mday);
    3302         225 :                     psField->Date.Hour = 0;
    3303         225 :                     psField->Date.Minute = 0;
    3304         225 :                     psField->Date.Second = 0;
    3305         225 :                     psField->Date.TZFlag = 0;
    3306             :                 }
    3307         225 :                 break;
    3308             :             }
    3309             : 
    3310         225 :             case OFTDateTime:
    3311             :             {
    3312             :                 const auto &v =
    3313             :                     *(std::get<std::shared_ptr<std::vector<int64_t>>>(
    3314         225 :                         fieldValues));
    3315             :                 GIntBig timestamp =
    3316         225 :                     static_cast<GIntBig>(v[m_nOffsetInResultSet]);
    3317         225 :                 double floatingPart = (timestamp % 1000) / 1e3;
    3318         225 :                 timestamp /= 1000;
    3319             :                 struct tm dt;
    3320         225 :                 CPLUnixTimeToYMDHMS(timestamp, &dt);
    3321         225 :                 auto psField = poFeature->GetRawFieldRef(i);
    3322         225 :                 psField->Set.nMarker1 = OGRUnsetMarker;
    3323         225 :                 psField->Set.nMarker2 = OGRUnsetMarker;
    3324         225 :                 psField->Set.nMarker3 = OGRUnsetMarker;
    3325         225 :                 psField->Date.Year = static_cast<GInt16>(dt.tm_year + 1900);
    3326         225 :                 psField->Date.Month = static_cast<GByte>(dt.tm_mon + 1);
    3327         225 :                 psField->Date.Day = static_cast<GByte>(dt.tm_mday);
    3328         225 :                 psField->Date.Hour = static_cast<GByte>(dt.tm_hour);
    3329         225 :                 psField->Date.Minute = static_cast<GByte>(dt.tm_min);
    3330         225 :                 psField->Date.Second =
    3331         225 :                     static_cast<float>(dt.tm_sec + floatingPart);
    3332         225 :                 psField->Date.TZFlag = static_cast<GByte>(100);
    3333         225 :                 break;
    3334             :             }
    3335             : 
    3336         225 :             case OFTTime:
    3337             :             {
    3338             :                 const auto &v =
    3339             :                     *(std::get<std::shared_ptr<std::vector<int64_t>>>(
    3340         225 :                         fieldValues));
    3341         225 :                 GIntBig value = static_cast<GIntBig>(v[m_nOffsetInResultSet]);
    3342         225 :                 double floatingPart = (value % 1000) / 1e3;
    3343         225 :                 value /= 1000;
    3344         225 :                 auto psField = poFeature->GetRawFieldRef(i);
    3345         225 :                 psField->Set.nMarker1 = OGRUnsetMarker;
    3346         225 :                 psField->Set.nMarker2 = OGRUnsetMarker;
    3347         225 :                 psField->Set.nMarker3 = OGRUnsetMarker;
    3348         225 :                 psField->Date.Year = 0;
    3349         225 :                 psField->Date.Month = 0;
    3350         225 :                 psField->Date.Day = 0;
    3351         225 :                 const int nHour = static_cast<int>(value / 3600);
    3352         225 :                 const int nMinute = static_cast<int>((value / 60) % 60);
    3353         225 :                 const int nSecond = static_cast<int>(value % 60);
    3354         225 :                 psField->Date.Hour = static_cast<GByte>(nHour);
    3355         225 :                 psField->Date.Minute = static_cast<GByte>(nMinute);
    3356         225 :                 psField->Date.Second =
    3357         225 :                     static_cast<float>(nSecond + floatingPart);
    3358         225 :                 psField->Date.TZFlag = 0;
    3359         225 :                 break;
    3360             :             }
    3361             : 
    3362           0 :             default:
    3363             :             {
    3364           0 :                 CPLAssert(false);
    3365             :                 break;
    3366             :             }
    3367             :         }
    3368             :     }
    3369         503 :     m_nOffsetInResultSet++;
    3370             : 
    3371         503 :     return poFeature;
    3372             : }
    3373             : 
    3374             : /************************************************************************/
    3375             : /*                         GetFeature()                                 */
    3376             : /************************************************************************/
    3377             : 
    3378          14 : OGRFeature *OGRTileDBLayer::GetFeature(GIntBig nFID)
    3379             : {
    3380          14 :     if (m_osFIDColumn.empty())
    3381           0 :         return OGRLayer::GetFeature(nFID);
    3382             : 
    3383          28 :     tiledb::QueryCondition qc(*(m_ctx.get()));
    3384          14 :     qc.init(m_osFIDColumn, &nFID, sizeof(nFID), TILEDB_EQ);
    3385          14 :     ResetReading();
    3386          14 :     if (!SetupQuery(&qc))
    3387           5 :         return nullptr;
    3388           9 :     auto poFeat = TranslateCurrentFeature();
    3389           9 :     ResetReading();
    3390           9 :     return poFeat;
    3391             : }
    3392             : 
    3393             : /************************************************************************/
    3394             : /*                      GetFeatureCount()                               */
    3395             : /************************************************************************/
    3396             : 
    3397          30 : GIntBig OGRTileDBLayer::GetFeatureCount(int bForce)
    3398             : {
    3399          30 :     if (!m_poAttrQuery && !m_poFilterGeom && m_nTotalFeatureCount >= 0)
    3400          16 :         return m_nTotalFeatureCount;
    3401          14 :     GIntBig nRet = OGRLayer::GetFeatureCount(bForce);
    3402          14 :     if (nRet >= 0 && !m_poAttrQuery && !m_poFilterGeom)
    3403           2 :         m_nTotalFeatureCount = nRet;
    3404          14 :     return nRet;
    3405             : }
    3406             : 
    3407             : /************************************************************************/
    3408             : /*                          GetExtent()                                 */
    3409             : /************************************************************************/
    3410             : 
    3411           8 : OGRErr OGRTileDBLayer::GetExtent(OGREnvelope *psExtent, int bForce)
    3412             : {
    3413           8 :     if (m_oLayerExtent.IsInit())
    3414             :     {
    3415           8 :         *psExtent = m_oLayerExtent;
    3416           8 :         return OGRERR_NONE;
    3417             :     }
    3418           0 :     return OGRLayer::GetExtent(psExtent, bForce);
    3419             : }
    3420             : 
    3421             : /************************************************************************/
    3422             : /*                         ResetReading()                               */
    3423             : /************************************************************************/
    3424             : 
    3425         592 : void OGRTileDBLayer::ResetReading()
    3426             : {
    3427         592 :     if (m_eCurrentMode == CurrentMode::WriteInProgress && !m_array)
    3428           1 :         return;
    3429             : 
    3430         591 :     SwitchToReadingMode();
    3431         591 :     ResetBuffers();
    3432         591 :     m_nNextFID = 1;
    3433         591 :     m_nOffsetInResultSet = 0;
    3434         591 :     m_nRowCountInResultSet = 0;
    3435         591 :     m_query.reset();
    3436         591 :     m_bQueryComplete = false;
    3437             : }
    3438             : 
    3439             : /************************************************************************/
    3440             : /*                         CreateField()                                */
    3441             : /************************************************************************/
    3442             : 
    3443         145 : OGRErr OGRTileDBLayer::CreateField(const OGRFieldDefn *poField,
    3444             :                                    int /* bApproxOK*/)
    3445             : {
    3446         145 :     if (!m_bUpdatable)
    3447           0 :         return OGRERR_FAILURE;
    3448         145 :     if (m_schema)
    3449             :     {
    3450           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    3451             :                  "Cannot add field after schema has been initialized");
    3452           1 :         return OGRERR_FAILURE;
    3453             :     }
    3454         144 :     if (poField->GetType() == OFTStringList)
    3455             :     {
    3456           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported field type");
    3457           0 :         return OGRERR_FAILURE;
    3458             :     }
    3459         144 :     const char *pszFieldName = poField->GetNameRef();
    3460         288 :     if (m_poFeatureDefn->GetFieldIndex(pszFieldName) >= 0 ||
    3461         144 :         pszFieldName == m_osFIDColumn ||
    3462         276 :         strcmp(pszFieldName, GetGeometryColumn()) == 0 ||
    3463         564 :         pszFieldName == m_osXDim || pszFieldName == m_osYDim ||
    3464         138 :         pszFieldName == m_osZDim)
    3465             :     {
    3466           6 :         CPLError(CE_Failure, CPLE_AppDefined,
    3467             :                  "A field or dimension of same name (%s) already exists",
    3468             :                  pszFieldName);
    3469           6 :         return OGRERR_FAILURE;
    3470             :     }
    3471         138 :     OGRFieldDefn oFieldDefn(poField);
    3472         138 :     m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
    3473         138 :     m_aeFieldTypesInCreateField.push_back(-1);
    3474         236 :     if (poField->GetType() == OFTInteger ||
    3475          98 :         poField->GetType() == OFTIntegerList)
    3476             :     {
    3477             :         const char *pszTileDBIntType =
    3478          61 :             CPLGetConfigOption("TILEDB_INT_TYPE", "INT32");
    3479          61 :         if (EQUAL(pszTileDBIntType, "UINT8"))
    3480             :         {
    3481           6 :             m_aeFieldTypesInCreateField.back() = TILEDB_UINT8;
    3482             :         }
    3483          55 :         else if (EQUAL(pszTileDBIntType, "UINT16"))
    3484             :         {
    3485           6 :             m_aeFieldTypesInCreateField.back() = TILEDB_UINT16;
    3486             :         }
    3487             :     }
    3488         138 :     return OGRERR_NONE;
    3489             : }
    3490             : 
    3491             : /************************************************************************/
    3492             : /*                      InitializeSchemaAndArray()                      */
    3493             : /************************************************************************/
    3494             : 
    3495          34 : void OGRTileDBLayer::InitializeSchemaAndArray()
    3496             : {
    3497          34 :     m_bInitializationAttempted = true;
    3498             : 
    3499             :     try
    3500             :     {
    3501             :         // create the tiledb schema
    3502             :         // dimensions will be _x and _y, we can also add _z (2.5d)
    3503             :         // set dimensions and attribute type for schema
    3504             :         // we will use row-major for now but we could use hilbert indexing
    3505          34 :         m_schema.reset(new tiledb::ArraySchema(*m_ctx, TILEDB_SPARSE));
    3506          34 :         m_schema->set_tile_order(TILEDB_ROW_MAJOR);
    3507          34 :         m_schema->set_cell_order(TILEDB_ROW_MAJOR);
    3508             : 
    3509          34 :         m_schema->set_coords_filter_list(*m_filterList);
    3510          34 :         m_schema->set_offsets_filter_list(*m_filterList);
    3511             : 
    3512          34 :         tiledb::Domain domain(*m_ctx);
    3513             : 
    3514             :         auto xdim = tiledb::Dimension::create<double>(
    3515          34 :             *m_ctx, m_osXDim, {m_dfXStart, m_dfXEnd}, m_dfTileExtent);
    3516             :         auto ydim = tiledb::Dimension::create<double>(
    3517          34 :             *m_ctx, m_osYDim, {m_dfYStart, m_dfYEnd}, m_dfTileExtent);
    3518          34 :         if (!m_osZDim.empty())
    3519             :         {
    3520             :             auto zdim = tiledb::Dimension::create<double>(
    3521          16 :                 *m_ctx, m_osZDim, {m_dfZStart, m_dfZEnd}, m_dfZTileExtent);
    3522          32 :             domain.add_dimensions(std::move(xdim), std::move(ydim),
    3523          48 :                                   std::move(zdim));
    3524             :         }
    3525             :         else
    3526             :         {
    3527          18 :             domain.add_dimensions(std::move(xdim), std::move(ydim));
    3528             :         }
    3529             : 
    3530          34 :         m_schema->set_domain(domain);
    3531             : 
    3532          34 :         m_schema->set_capacity(m_nTileCapacity);
    3533             : 
    3534             :         // allow geometries with same _X, _Y
    3535          34 :         m_schema->set_allows_dups(true);
    3536             : 
    3537             :         // add FID attribute
    3538          34 :         if (!m_osFIDColumn.empty())
    3539             :         {
    3540          96 :             m_schema->add_attribute(tiledb::Attribute::create<int64_t>(
    3541          64 :                 *m_ctx, m_osFIDColumn, *m_filterList));
    3542             :         }
    3543             : 
    3544             :         // add geometry attribute
    3545          34 :         const char *pszGeomColName = GetDatabaseGeomColName();
    3546          34 :         if (pszGeomColName)
    3547             :         {
    3548             :             const char *pszWkbBlobType =
    3549          29 :                 CPLGetConfigOption("TILEDB_WKB_GEOMETRY_TYPE",
    3550             : #ifdef HAS_TILEDB_GEOM_WKB_WKT
    3551             :                                    "GEOM_WKB"
    3552             : #else
    3553             :                                    "BLOB"
    3554             : #endif
    3555             :                 );
    3556             :             auto wkbGeometryAttr = tiledb::Attribute::create(
    3557          29 :                 *m_ctx, pszGeomColName,
    3558             : #ifdef HAS_TILEDB_GEOM_WKB_WKT
    3559             :                 EQUAL(pszWkbBlobType, "GEOM_WKB") ? TILEDB_GEOM_WKB :
    3560             : #endif
    3561          29 :                 EQUAL(pszWkbBlobType, "UINT8") ? TILEDB_UINT8
    3562         116 :                                                : TILEDB_BLOB);
    3563             : 
    3564          29 :             wkbGeometryAttr.set_filter_list(*m_filterList);
    3565          29 :             wkbGeometryAttr.set_cell_val_num(TILEDB_VAR_NUM);
    3566          29 :             m_schema->add_attribute(wkbGeometryAttr);
    3567             :         }
    3568             : 
    3569          34 :         auto &aFieldValues = m_aFieldValues;
    3570          34 :         CPLAssert(static_cast<int>(m_aeFieldTypesInCreateField.size()) ==
    3571             :                   m_poFeatureDefn->GetFieldCount());
    3572         172 :         for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
    3573             :         {
    3574         138 :             const OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
    3575         138 :             const bool bIsNullable = poFieldDefn->IsNullable();
    3576             : 
    3577             :             const auto CreateAttr =
    3578         138 :                 [this, poFieldDefn, bIsNullable](tiledb_datatype_t type,
    3579         966 :                                                  bool bIsVariableSize = false)
    3580             :             {
    3581         138 :                 m_aeFieldTypes.push_back(type);
    3582             :                 auto attr = tiledb::Attribute::create(
    3583         414 :                     *m_ctx, poFieldDefn->GetNameRef(), m_aeFieldTypes.back());
    3584         138 :                 attr.set_filter_list(*m_filterList);
    3585         138 :                 attr.set_nullable(bIsNullable);
    3586         138 :                 if (bIsVariableSize)
    3587          54 :                     attr.set_cell_val_num(TILEDB_VAR_NUM);
    3588         138 :                 m_schema->add_attribute(attr);
    3589         138 :             };
    3590             : 
    3591         138 :             const auto eType = poFieldDefn->GetType();
    3592         138 :             switch (eType)
    3593             :             {
    3594          61 :                 case OFTInteger:
    3595             :                 case OFTIntegerList:
    3596             :                 {
    3597          61 :                     if (poFieldDefn->GetSubType() == OFSTBoolean)
    3598             :                     {
    3599          14 :                         CreateAttr(TILEDB_BOOL, eType == OFTIntegerList);
    3600          14 :                         aFieldValues.push_back(
    3601          28 :                             std::make_shared<VECTOR_OF_BOOL>());
    3602             :                     }
    3603          47 :                     else if (poFieldDefn->GetSubType() == OFSTInt16)
    3604             :                     {
    3605          14 :                         CreateAttr(TILEDB_INT16, eType == OFTIntegerList);
    3606          14 :                         aFieldValues.push_back(
    3607          28 :                             std::make_shared<std::vector<int16_t>>());
    3608             :                     }
    3609          33 :                     else if (m_aeFieldTypesInCreateField[i] >= 0)
    3610             :                     {
    3611          12 :                         if (m_aeFieldTypesInCreateField[i] == TILEDB_UINT8)
    3612             :                         {
    3613           6 :                             CreateAttr(TILEDB_UINT8, eType == OFTIntegerList);
    3614           6 :                             aFieldValues.push_back(
    3615          12 :                                 std::make_shared<std::vector<uint8_t>>());
    3616             :                         }
    3617           6 :                         else if (m_aeFieldTypesInCreateField[i] ==
    3618             :                                  TILEDB_UINT16)
    3619             :                         {
    3620           6 :                             CreateAttr(TILEDB_UINT16, eType == OFTIntegerList);
    3621           6 :                             aFieldValues.push_back(
    3622          12 :                                 std::make_shared<std::vector<uint16_t>>());
    3623             :                         }
    3624             :                         else
    3625             :                         {
    3626           0 :                             CPLAssert(false);
    3627             :                         }
    3628             :                     }
    3629             :                     else
    3630             :                     {
    3631             :                         const char *pszTileDBIntType =
    3632          21 :                             CPLGetConfigOption("TILEDB_INT_TYPE", "INT32");
    3633          21 :                         if (EQUAL(pszTileDBIntType, "UINT8"))
    3634             :                         {
    3635           0 :                             CreateAttr(TILEDB_UINT8, eType == OFTIntegerList);
    3636           0 :                             aFieldValues.push_back(
    3637           0 :                                 std::make_shared<std::vector<uint8_t>>());
    3638             :                         }
    3639          21 :                         else if (EQUAL(pszTileDBIntType, "UINT16"))
    3640             :                         {
    3641           0 :                             CreateAttr(TILEDB_UINT16, eType == OFTIntegerList);
    3642           0 :                             aFieldValues.push_back(
    3643           0 :                                 std::make_shared<std::vector<uint16_t>>());
    3644             :                         }
    3645             :                         else
    3646             :                         {
    3647          21 :                             CreateAttr(TILEDB_INT32, eType == OFTIntegerList);
    3648          21 :                             aFieldValues.push_back(
    3649          42 :                                 std::make_shared<std::vector<int32_t>>());
    3650             :                         }
    3651             :                     }
    3652          61 :                     break;
    3653             :                 }
    3654             : 
    3655           8 :                 case OFTInteger64:
    3656             :                 {
    3657           8 :                     CreateAttr(TILEDB_INT64);
    3658           8 :                     aFieldValues.push_back(
    3659          16 :                         std::make_shared<std::vector<int64_t>>());
    3660           8 :                     break;
    3661             :                 }
    3662             : 
    3663           1 :                 case OFTInteger64List:
    3664             :                 {
    3665           1 :                     CreateAttr(TILEDB_INT64, true);
    3666           1 :                     aFieldValues.push_back(
    3667           2 :                         std::make_shared<std::vector<int64_t>>());
    3668           1 :                     break;
    3669             :                 }
    3670             : 
    3671          29 :                 case OFTReal:
    3672             :                 case OFTRealList:
    3673             :                 {
    3674          29 :                     if (poFieldDefn->GetSubType() == OFSTFloat32)
    3675             :                     {
    3676          14 :                         CreateAttr(TILEDB_FLOAT32, eType == OFTRealList);
    3677          14 :                         aFieldValues.push_back(
    3678          28 :                             std::make_shared<std::vector<float>>());
    3679             :                     }
    3680             :                     else
    3681             :                     {
    3682          15 :                         CreateAttr(TILEDB_FLOAT64, eType == OFTRealList);
    3683          15 :                         aFieldValues.push_back(
    3684          30 :                             std::make_shared<std::vector<double>>());
    3685             :                     }
    3686          29 :                     break;
    3687             :                 }
    3688             : 
    3689          11 :                 case OFTString:
    3690             :                 {
    3691          11 :                     CreateAttr(m_eTileDBStringType, true);
    3692          11 :                     aFieldValues.push_back(std::make_shared<std::string>());
    3693          11 :                     break;
    3694             :                 }
    3695             : 
    3696           7 :                 case OFTDate:
    3697             :                 {
    3698           7 :                     CreateAttr(TILEDB_DATETIME_DAY);
    3699           7 :                     aFieldValues.push_back(
    3700          14 :                         std::make_shared<std::vector<int64_t>>());
    3701           7 :                     break;
    3702             :                 }
    3703             : 
    3704           7 :                 case OFTDateTime:
    3705             :                 {
    3706           7 :                     CreateAttr(TILEDB_DATETIME_MS);
    3707           7 :                     aFieldValues.push_back(
    3708          14 :                         std::make_shared<std::vector<int64_t>>());
    3709           7 :                     break;
    3710             :                 }
    3711             : 
    3712           7 :                 case OFTTime:
    3713             :                 {
    3714           7 :                     CreateAttr(TILEDB_TIME_MS);
    3715           7 :                     aFieldValues.push_back(
    3716          14 :                         std::make_shared<std::vector<int64_t>>());
    3717           7 :                     break;
    3718             :                 }
    3719             : 
    3720           7 :                 case OFTBinary:
    3721             :                 {
    3722             :                     const char *pszBlobType =
    3723           7 :                         CPLGetConfigOption("TILEDB_BINARY_TYPE", "BLOB");
    3724           7 :                     CreateAttr(EQUAL(pszBlobType, "UINT8") ? TILEDB_UINT8
    3725             :                                                            : TILEDB_BLOB,
    3726             :                                true);
    3727           7 :                     aFieldValues.push_back(
    3728          14 :                         std::make_shared<std::vector<uint8_t>>());
    3729           7 :                     break;
    3730             :                 }
    3731             : 
    3732           0 :                 default:
    3733             :                 {
    3734           0 :                     CPLError(CE_Failure, CPLE_NoWriteAccess,
    3735             :                              "Unsupported attribute definition.\n");
    3736           0 :                     return;
    3737             :                 }
    3738             :             }
    3739             :         }
    3740             : 
    3741         172 :         for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
    3742             :         {
    3743         138 :             m_aFieldValueOffsets.push_back(
    3744         276 :                 std::make_shared<std::vector<uint64_t>>());
    3745             :         }
    3746          34 :         m_aFieldValidity.resize(m_poFeatureDefn->GetFieldCount());
    3747             : 
    3748          34 :         tiledb::Array::create(m_osFilename, *m_schema);
    3749             : 
    3750          34 :         if (!m_osGroupName.empty())
    3751             :         {
    3752           3 :             tiledb::Group group(*m_ctx, m_osGroupName, TILEDB_WRITE);
    3753           3 :             group.add_member(m_osFilename, false, GetDescription());
    3754             :         }
    3755             : 
    3756          34 :         if (m_nTimestamp)
    3757           0 :             m_array.reset(new tiledb::Array(
    3758           0 :                 *m_ctx, m_osFilename, TILEDB_WRITE,
    3759           0 :                 tiledb::TemporalPolicy(tiledb::TimeTravel, m_nTimestamp)));
    3760             :         else
    3761          68 :             m_array.reset(
    3762          68 :                 new tiledb::Array(*m_ctx, m_osFilename, TILEDB_WRITE));
    3763             : 
    3764          34 :         if (!m_osFIDColumn.empty())
    3765             :         {
    3766          64 :             m_array->put_metadata("FID_ATTRIBUTE_NAME", TILEDB_STRING_UTF8,
    3767          32 :                                   static_cast<int>(m_osFIDColumn.size()),
    3768          32 :                                   m_osFIDColumn.c_str());
    3769             :         }
    3770             : 
    3771          63 :         if (pszGeomColName &&
    3772          29 :             CPLTestBool(CPLGetConfigOption(
    3773             :                 "OGR_TILEDB_WRITE_GEOMETRY_ATTRIBUTE_NAME", "YES")))
    3774             :         {
    3775          58 :             m_array->put_metadata("GEOMETRY_ATTRIBUTE_NAME", TILEDB_STRING_UTF8,
    3776          29 :                                   static_cast<int>(strlen(pszGeomColName)),
    3777             :                                   pszGeomColName);
    3778             :         }
    3779             : 
    3780          34 :         m_array->put_metadata(DATASET_TYPE_ATTRIBUTE_NAME, TILEDB_STRING_UTF8,
    3781             :                               static_cast<int>(strlen(GEOMETRY_DATASET_TYPE)),
    3782             :                               GEOMETRY_DATASET_TYPE);
    3783             : 
    3784          34 :         auto poSRS = GetSpatialRef();
    3785          34 :         if (poSRS)
    3786             :         {
    3787          10 :             char *pszStr = nullptr;
    3788          10 :             poSRS->exportToPROJJSON(&pszStr, nullptr);
    3789          10 :             if (!pszStr)
    3790           0 :                 poSRS->exportToWkt(&pszStr, nullptr);
    3791          10 :             if (pszStr)
    3792             :             {
    3793          20 :                 m_array->put_metadata("CRS", TILEDB_STRING_UTF8,
    3794          10 :                                       static_cast<int>(strlen(pszStr)), pszStr);
    3795             :             }
    3796          10 :             CPLFree(pszStr);
    3797             :         }
    3798             : 
    3799          34 :         const auto GetStringGeometryType = [](OGRwkbGeometryType eType)
    3800             :         {
    3801          34 :             const auto eFlattenType = wkbFlatten(eType);
    3802          34 :             std::string osType = "Unknown";
    3803          34 :             if (wkbPoint == eFlattenType)
    3804           7 :                 osType = "Point";
    3805          27 :             else if (wkbLineString == eFlattenType)
    3806           1 :                 osType = "LineString";
    3807          26 :             else if (wkbPolygon == eFlattenType)
    3808           2 :                 osType = "Polygon";
    3809          24 :             else if (wkbMultiPoint == eFlattenType)
    3810           1 :                 osType = "MultiPoint";
    3811          23 :             else if (wkbMultiLineString == eFlattenType)
    3812           1 :                 osType = "MultiLineString";
    3813          22 :             else if (wkbMultiPolygon == eFlattenType)
    3814           1 :                 osType = "MultiPolygon";
    3815          21 :             else if (wkbGeometryCollection == eFlattenType)
    3816           1 :                 osType = "GeometryCollection";
    3817          20 :             else if (wkbCircularString == eFlattenType)
    3818           1 :                 osType = "CircularString";
    3819          19 :             else if (wkbCompoundCurve == eFlattenType)
    3820           1 :                 osType = "CompoundCurve";
    3821          18 :             else if (wkbCurvePolygon == eFlattenType)
    3822           1 :                 osType = "CurvePolygon";
    3823          17 :             else if (wkbMultiCurve == eFlattenType)
    3824           1 :                 osType = "MultiCurve";
    3825          16 :             else if (wkbMultiSurface == eFlattenType)
    3826           1 :                 osType = "MultiSurface";
    3827          15 :             else if (wkbPolyhedralSurface == eFlattenType)
    3828           1 :                 osType = "PolyhedralSurface";
    3829          14 :             else if (wkbTIN == eFlattenType)
    3830           1 :                 osType = "TIN";
    3831             : 
    3832          34 :             if (OGR_GT_HasZ(eType) && OGR_GT_HasM(eType))
    3833           1 :                 osType += " ZM";
    3834          33 :             else if (OGR_GT_HasZ(eType))
    3835           2 :                 osType += " Z";
    3836          31 :             else if (OGR_GT_HasM(eType))
    3837           1 :                 osType += " M";
    3838             : 
    3839          34 :             return osType;
    3840             :         };
    3841          34 :         const auto eGeomType = GetGeomType();
    3842          34 :         const std::string osGeometryType = GetStringGeometryType(eGeomType);
    3843          68 :         m_array->put_metadata("GeometryType", TILEDB_STRING_ASCII,
    3844          34 :                               static_cast<int>(osGeometryType.size()),
    3845          34 :                               osGeometryType.data());
    3846             : 
    3847          34 :         m_bInitialized = true;
    3848             :     }
    3849           0 :     catch (const tiledb::TileDBError &e)
    3850             :     {
    3851           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3852           0 :                  "InitializeSchemaAndArray() failed: %s", e.what());
    3853             :     }
    3854             : }
    3855             : 
    3856             : /************************************************************************/
    3857             : /*                       SwitchToWritingMode()                          */
    3858             : /************************************************************************/
    3859             : 
    3860         102 : void OGRTileDBLayer::SwitchToWritingMode()
    3861             : {
    3862         102 :     if (m_eCurrentMode != CurrentMode::WriteInProgress)
    3863             :     {
    3864           7 :         m_nNextFID = GetFeatureCount(true) + 1;
    3865           7 :         if (m_eCurrentMode == CurrentMode::ReadInProgress)
    3866             :         {
    3867           6 :             m_eCurrentMode = CurrentMode::None;
    3868           6 :             ResetBuffers();
    3869             :         }
    3870             : 
    3871           7 :         m_query.reset();
    3872           7 :         m_array.reset();
    3873             : 
    3874             :         try
    3875             :         {
    3876           7 :             if (m_nTimestamp)
    3877           0 :                 m_array.reset(new tiledb::Array(
    3878           0 :                     *m_ctx, m_osFilename, TILEDB_WRITE,
    3879           0 :                     tiledb::TemporalPolicy(tiledb::TimeTravel, m_nTimestamp)));
    3880             :             else
    3881          14 :                 m_array.reset(
    3882          14 :                     new tiledb::Array(*m_ctx, m_osFilename, TILEDB_WRITE));
    3883             :         }
    3884           0 :         catch (const std::exception &e)
    3885             :         {
    3886           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    3887           0 :             return;
    3888             :         }
    3889             :     }
    3890         102 :     m_eCurrentMode = CurrentMode::WriteInProgress;
    3891             : }
    3892             : 
    3893             : /************************************************************************/
    3894             : /*                         ICreateFeature()                             */
    3895             : /************************************************************************/
    3896             : 
    3897          64 : OGRErr OGRTileDBLayer::ICreateFeature(OGRFeature *poFeature)
    3898             : {
    3899          64 :     if (!m_bUpdatable)
    3900           0 :         return OGRERR_FAILURE;
    3901             : 
    3902          64 :     SwitchToWritingMode();
    3903             : 
    3904          64 :     if (!m_bInitializationAttempted)
    3905             :     {
    3906          30 :         InitializeSchemaAndArray();
    3907             :     }
    3908          64 :     if (!m_bInitialized)
    3909           0 :         return OGRERR_FAILURE;
    3910             : 
    3911          64 :     if (!m_array)
    3912           0 :         return OGRERR_FAILURE;
    3913             : 
    3914          64 :     const OGRGeometry *poGeom = poFeature->GetGeometryRef();
    3915          64 :     if (!poGeom || poGeom->IsEmpty())
    3916             :     {
    3917           2 :         CPLError(CE_Failure, CPLE_NotSupported,
    3918             :                  "Features without geometry (or with empty geometry) are not "
    3919             :                  "supported");
    3920           2 :         return OGRERR_FAILURE;
    3921             :     }
    3922             : 
    3923          62 :     if (GetDatabaseGeomColName())
    3924             :     {
    3925          55 :         const size_t nWkbSize = poGeom->WkbSize();
    3926          55 :         std::vector<unsigned char> aGeometry(nWkbSize);
    3927          55 :         poGeom->exportToWkb(wkbNDR, aGeometry.data(), wkbVariantIso);
    3928          55 :         m_abyGeometries->insert(m_abyGeometries->end(), aGeometry.begin(),
    3929         110 :                                 aGeometry.end());
    3930          55 :         if (m_anGeometryOffsets->empty())
    3931          29 :             m_anGeometryOffsets->push_back(0);
    3932          55 :         m_anGeometryOffsets->push_back(m_anGeometryOffsets->back() + nWkbSize);
    3933             :     }
    3934           7 :     else if (wkbFlatten(poGeom->getGeometryType()) != wkbPoint)
    3935             :     {
    3936           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3937             :                  "Cannot write non-Point geometry in a layer without a "
    3938             :                  "geometry attribute");
    3939           0 :         return OGRERR_FAILURE;
    3940             :     }
    3941             : 
    3942          62 :     int64_t nFID = poFeature->GetFID();
    3943          62 :     if (nFID < 0)
    3944             :     {
    3945          56 :         nFID = m_nNextFID++;
    3946          56 :         poFeature->SetFID(nFID);
    3947             :     }
    3948          62 :     if (!m_osFIDColumn.empty())
    3949          59 :         m_anFIDs->push_back(nFID);
    3950             : 
    3951          62 :     const int nFieldCount = m_poFeatureDefn->GetFieldCountUnsafe();
    3952         476 :     for (int i = 0; i < nFieldCount; i++)
    3953             :     {
    3954         414 :         const OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
    3955         414 :         const bool bFieldIsValid = poFeature->IsFieldSetAndNotNull(i);
    3956         414 :         auto &anOffsets = *(m_aFieldValueOffsets[i]);
    3957         414 :         if (poFieldDefn->IsNullable())
    3958             :         {
    3959         224 :             m_aFieldValidity[i].push_back(bFieldIsValid);
    3960             :         }
    3961             :         else
    3962             :         {
    3963         190 :             if (!bFieldIsValid)
    3964             :             {
    3965          54 :                 CPLError(CE_Warning, CPLE_AppDefined,
    3966             :                          "Field %d of feature " CPL_FRMT_GIB
    3967             :                          " is null or unset, "
    3968             :                          "but field is declared as not nullable. Readers "
    3969             :                          "will see an incorrect value",
    3970             :                          i, static_cast<GIntBig>(nFID));
    3971             :             }
    3972             :         }
    3973         414 :         auto &fieldValues = m_aFieldValues[i];
    3974             : 
    3975         414 :         switch (poFieldDefn->GetType())
    3976             :         {
    3977         124 :             case OFTInteger:
    3978             :             {
    3979             :                 const int nVal =
    3980         124 :                     bFieldIsValid ? poFeature->GetFieldAsIntegerUnsafe(i) : 0;
    3981         124 :                 if (m_aeFieldTypes[i] == TILEDB_BOOL)
    3982          20 :                     std::get<std::shared_ptr<VECTOR_OF_BOOL>>(fieldValues)
    3983          20 :                         ->push_back(static_cast<uint8_t>(nVal));
    3984         104 :                 else if (m_aeFieldTypes[i] == TILEDB_INT16)
    3985          20 :                     std::get<std::shared_ptr<std::vector<int16_t>>>(fieldValues)
    3986          20 :                         ->push_back(static_cast<int16_t>(nVal));
    3987          84 :                 else if (m_aeFieldTypes[i] == TILEDB_INT32)
    3988          44 :                     std::get<std::shared_ptr<std::vector<int32_t>>>(fieldValues)
    3989          44 :                         ->push_back(nVal);
    3990          40 :                 else if (m_aeFieldTypes[i] == TILEDB_UINT8)
    3991          20 :                     std::get<std::shared_ptr<std::vector<uint8_t>>>(fieldValues)
    3992          20 :                         ->push_back(static_cast<uint8_t>(nVal));
    3993          20 :                 else if (m_aeFieldTypes[i] == TILEDB_UINT16)
    3994             :                     std::get<std::shared_ptr<std::vector<uint16_t>>>(
    3995          20 :                         fieldValues)
    3996          20 :                         ->push_back(static_cast<uint16_t>(nVal));
    3997             :                 else
    3998             :                 {
    3999           0 :                     CPLAssert(false);
    4000             :                 }
    4001         124 :                 break;
    4002             :             }
    4003             : 
    4004          60 :             case OFTIntegerList:
    4005             :             {
    4006          60 :                 int nCount = 0;
    4007             :                 const int *panVal =
    4008          60 :                     poFeature->GetFieldAsIntegerList(i, &nCount);
    4009          60 :                 if (anOffsets.empty())
    4010          27 :                     anOffsets.push_back(0);
    4011          60 :                 if (m_aeFieldTypes[i] == TILEDB_BOOL)
    4012             :                 {
    4013             :                     auto &v = *(
    4014          20 :                         std::get<std::shared_ptr<VECTOR_OF_BOOL>>(fieldValues));
    4015          48 :                     for (int j = 0; j < nCount; ++j)
    4016          28 :                         v.push_back(static_cast<uint8_t>(panVal[j]));
    4017          40 :                     anOffsets.push_back(anOffsets.back() +
    4018          20 :                                         nCount * sizeof(v[0]));
    4019             :                 }
    4020          40 :                 else if (m_aeFieldTypes[i] == TILEDB_INT16)
    4021             :                 {
    4022             :                     auto &v = *(std::get<std::shared_ptr<std::vector<int16_t>>>(
    4023          20 :                         fieldValues));
    4024          48 :                     for (int j = 0; j < nCount; ++j)
    4025          28 :                         v.push_back(static_cast<int16_t>(panVal[j]));
    4026          40 :                     anOffsets.push_back(anOffsets.back() +
    4027          20 :                                         nCount * sizeof(v[0]));
    4028             :                 }
    4029          20 :                 else if (m_aeFieldTypes[i] == TILEDB_INT32)
    4030             :                 {
    4031             :                     auto &v = *(std::get<std::shared_ptr<std::vector<int32_t>>>(
    4032          20 :                         fieldValues));
    4033          20 :                     v.insert(v.end(), panVal, panVal + nCount);
    4034          40 :                     anOffsets.push_back(anOffsets.back() +
    4035          20 :                                         nCount * sizeof(v[0]));
    4036             :                 }
    4037           0 :                 else if (m_aeFieldTypes[i] == TILEDB_UINT8)
    4038             :                 {
    4039             :                     auto &v = *(std::get<std::shared_ptr<std::vector<uint8_t>>>(
    4040           0 :                         fieldValues));
    4041           0 :                     for (int j = 0; j < nCount; ++j)
    4042           0 :                         v.push_back(static_cast<uint8_t>(panVal[j]));
    4043           0 :                     anOffsets.push_back(anOffsets.back() +
    4044           0 :                                         nCount * sizeof(v[0]));
    4045             :                 }
    4046           0 :                 else if (m_aeFieldTypes[i] == TILEDB_UINT16)
    4047             :                 {
    4048             :                     auto &v =
    4049             :                         *(std::get<std::shared_ptr<std::vector<uint16_t>>>(
    4050           0 :                             fieldValues));
    4051           0 :                     for (int j = 0; j < nCount; ++j)
    4052           0 :                         v.push_back(static_cast<uint16_t>(panVal[j]));
    4053           0 :                     anOffsets.push_back(anOffsets.back() +
    4054           0 :                                         nCount * sizeof(v[0]));
    4055             :                 }
    4056             :                 else
    4057             :                 {
    4058           0 :                     CPLAssert(false);
    4059             :                 }
    4060          60 :                 break;
    4061             :             }
    4062             : 
    4063          30 :             case OFTInteger64:
    4064             :             {
    4065          30 :                 std::get<std::shared_ptr<std::vector<int64_t>>>(fieldValues)
    4066          30 :                     ->push_back(bFieldIsValid
    4067          30 :                                     ? poFeature->GetFieldAsInteger64Unsafe(i)
    4068             :                                     : 0);
    4069          30 :                 break;
    4070             :             }
    4071             : 
    4072           0 :             case OFTInteger64List:
    4073             :             {
    4074           0 :                 int nCount = 0;
    4075             :                 const int64_t *panVal = reinterpret_cast<const int64_t *>(
    4076           0 :                     poFeature->GetFieldAsInteger64List(i, &nCount));
    4077           0 :                 if (anOffsets.empty())
    4078           0 :                     anOffsets.push_back(0);
    4079             :                 auto &v = *(std::get<std::shared_ptr<std::vector<int64_t>>>(
    4080           0 :                     fieldValues));
    4081           0 :                 v.insert(v.end(), panVal, panVal + nCount);
    4082           0 :                 anOffsets.push_back(anOffsets.back() + nCount * sizeof(v[0]));
    4083           0 :                 break;
    4084             :             }
    4085             : 
    4086          50 :             case OFTReal:
    4087             :             {
    4088             :                 const double dfVal =
    4089          50 :                     bFieldIsValid ? poFeature->GetFieldAsDoubleUnsafe(i)
    4090          12 :                                   : std::numeric_limits<double>::quiet_NaN();
    4091          50 :                 if (poFieldDefn->GetSubType() == OFSTFloat32)
    4092          20 :                     std::get<std::shared_ptr<std::vector<float>>>(fieldValues)
    4093          20 :                         ->push_back(static_cast<float>(dfVal));
    4094             :                 else
    4095          30 :                     std::get<std::shared_ptr<std::vector<double>>>(fieldValues)
    4096          30 :                         ->push_back(dfVal);
    4097          50 :                 break;
    4098             :             }
    4099             : 
    4100          40 :             case OFTRealList:
    4101             :             {
    4102          40 :                 int nCount = 0;
    4103             :                 const double *padfVal =
    4104          40 :                     poFeature->GetFieldAsDoubleList(i, &nCount);
    4105          40 :                 if (anOffsets.empty())
    4106          18 :                     anOffsets.push_back(0);
    4107          40 :                 if (poFieldDefn->GetSubType() == OFSTFloat32)
    4108             :                 {
    4109             :                     auto &v = *(std::get<std::shared_ptr<std::vector<float>>>(
    4110          20 :                         fieldValues));
    4111          62 :                     for (int j = 0; j < nCount; ++j)
    4112          42 :                         v.push_back(static_cast<float>(padfVal[j]));
    4113          40 :                     anOffsets.push_back(anOffsets.back() +
    4114          20 :                                         nCount * sizeof(v[0]));
    4115             :                 }
    4116             :                 else
    4117             :                 {
    4118             :                     auto &v = *(std::get<std::shared_ptr<std::vector<double>>>(
    4119          20 :                         fieldValues));
    4120          20 :                     v.insert(v.end(), padfVal, padfVal + nCount);
    4121          40 :                     anOffsets.push_back(anOffsets.back() +
    4122          20 :                                         nCount * sizeof(v[0]));
    4123             :                 }
    4124          40 :                 break;
    4125             :             }
    4126             : 
    4127          30 :             case OFTString:
    4128             :             {
    4129             :                 const char *pszValue =
    4130          30 :                     bFieldIsValid ? poFeature->GetFieldAsStringUnsafe(i)
    4131          30 :                                   : nullptr;
    4132          30 :                 const size_t nValueLen = pszValue ? strlen(pszValue) : 0;
    4133          30 :                 if (pszValue)
    4134             :                 {
    4135             :                     auto &v =
    4136          24 :                         *(std::get<std::shared_ptr<std::string>>(fieldValues));
    4137          24 :                     v.insert(v.end(), pszValue, pszValue + nValueLen);
    4138             :                 }
    4139          30 :                 if (anOffsets.empty())
    4140          10 :                     anOffsets.push_back(0);
    4141          30 :                 anOffsets.push_back(anOffsets.back() + nValueLen);
    4142          30 :                 break;
    4143             :             }
    4144             : 
    4145          20 :             case OFTBinary:
    4146             :             {
    4147          20 :                 int nCount = 0;
    4148             :                 const GByte *pabyValue =
    4149          20 :                     poFeature->GetFieldAsBinary(i, &nCount);
    4150             :                 auto &v = *(std::get<std::shared_ptr<std::vector<uint8_t>>>(
    4151          20 :                     fieldValues));
    4152          20 :                 v.insert(v.end(), pabyValue, pabyValue + nCount);
    4153          20 :                 if (anOffsets.empty())
    4154           9 :                     anOffsets.push_back(0);
    4155          20 :                 anOffsets.push_back(anOffsets.back() + nCount);
    4156          20 :                 break;
    4157             :             }
    4158             : 
    4159          20 :             case OFTDate:
    4160             :             {
    4161             :                 auto &v = *(std::get<std::shared_ptr<std::vector<int64_t>>>(
    4162          20 :                     fieldValues));
    4163          20 :                 if (bFieldIsValid)
    4164             :                 {
    4165          14 :                     const auto poRawField = poFeature->GetRawFieldRef(i);
    4166          14 :                     v.push_back(OGRFieldToDateDay(*poRawField));
    4167             :                 }
    4168             :                 else
    4169             :                 {
    4170           6 :                     v.push_back(0);
    4171             :                 }
    4172          20 :                 break;
    4173             :             }
    4174             : 
    4175          20 :             case OFTDateTime:
    4176             :             {
    4177             :                 auto &v = *(std::get<std::shared_ptr<std::vector<int64_t>>>(
    4178          20 :                     fieldValues));
    4179          20 :                 if (bFieldIsValid)
    4180             :                 {
    4181          14 :                     const auto poRawField = poFeature->GetRawFieldRef(i);
    4182          14 :                     v.push_back(OGRFieldToDateTimeMS(*poRawField));
    4183             :                 }
    4184             :                 else
    4185             :                 {
    4186           6 :                     v.push_back(0);
    4187             :                 }
    4188          20 :                 break;
    4189             :             }
    4190             : 
    4191          20 :             case OFTTime:
    4192             :             {
    4193             :                 auto &v = *(std::get<std::shared_ptr<std::vector<int64_t>>>(
    4194          20 :                     fieldValues));
    4195          20 :                 if (bFieldIsValid)
    4196             :                 {
    4197          14 :                     const auto poRawField = poFeature->GetRawFieldRef(i);
    4198          14 :                     v.push_back(OGRFieldToTimeMS(*poRawField));
    4199             :                 }
    4200             :                 else
    4201             :                 {
    4202           6 :                     v.push_back(0);
    4203             :                 }
    4204          20 :                 break;
    4205             :             }
    4206             : 
    4207           0 :             default:
    4208             :             {
    4209           0 :                 CPLError(CE_Failure, CPLE_NoWriteAccess,
    4210             :                          "Unsupported attribute definition.\n");
    4211           0 :                 return OGRERR_FAILURE;
    4212             :             }
    4213             :         }
    4214             :     }
    4215             : 
    4216          62 :     OGREnvelope sEnvelope;
    4217          62 :     OGREnvelope3D sEnvelope3D;
    4218          62 :     if (!m_osZDim.empty())
    4219             :     {
    4220          34 :         poGeom->getEnvelope(&sEnvelope3D);
    4221          34 :         sEnvelope = sEnvelope3D;
    4222             :     }
    4223             :     else
    4224             :     {
    4225          28 :         poGeom->getEnvelope(&sEnvelope);
    4226             :     }
    4227             : 
    4228          62 :     m_oLayerExtent.Merge(sEnvelope);
    4229             : 
    4230             :     // use mid point of envelope
    4231          62 :     m_adfXs->push_back(sEnvelope.MinX +
    4232          62 :                        ((sEnvelope.MaxX - sEnvelope.MinX) / 2.0));
    4233          62 :     m_adfYs->push_back(sEnvelope.MinY +
    4234          62 :                        ((sEnvelope.MaxY - sEnvelope.MinY) / 2.0));
    4235             : 
    4236             :     // Compute maximum "radius" of a geometry around its mid point,
    4237             :     // for later spatial requests
    4238          62 :     m_dfPadX = std::max(m_dfPadX, (sEnvelope.MaxX - sEnvelope.MinX) / 2);
    4239          62 :     m_dfPadY = std::max(m_dfPadY, (sEnvelope.MaxY - sEnvelope.MinY) / 2);
    4240             : 
    4241          62 :     if (!m_osZDim.empty())
    4242             :     {
    4243          34 :         m_adfZs->push_back(sEnvelope3D.MinZ +
    4244          34 :                            ((sEnvelope3D.MaxZ - sEnvelope3D.MinZ) / 2.0));
    4245          34 :         m_dfPadZ =
    4246          34 :             std::max(m_dfPadZ, (sEnvelope3D.MaxZ - sEnvelope3D.MinZ) / 2);
    4247             :     }
    4248             : 
    4249          62 :     if (m_nTotalFeatureCount < 0)
    4250          29 :         m_nTotalFeatureCount = 1;
    4251             :     else
    4252          33 :         ++m_nTotalFeatureCount;
    4253             : 
    4254          62 :     if (m_adfXs->size() == m_nBatchSize)
    4255             :     {
    4256             :         try
    4257             :         {
    4258           4 :             FlushArrays();
    4259             :         }
    4260           0 :         catch (const std::exception &e)
    4261             :         {
    4262           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    4263           0 :             return OGRERR_FAILURE;
    4264             :         }
    4265             :     }
    4266             : 
    4267          62 :     return OGRERR_NONE;
    4268             : }
    4269             : 
    4270             : /************************************************************************/
    4271             : /*                          FlushArrays()                               */
    4272             : /************************************************************************/
    4273             : 
    4274          34 : void OGRTileDBLayer::FlushArrays()
    4275             : {
    4276          34 :     CPLDebug("TILEDB", "Flush %d records", static_cast<int>(m_adfXs->size()));
    4277             : 
    4278             :     try
    4279             :     {
    4280          68 :         tiledb::Query query(*m_ctx, *m_array);
    4281          34 :         query.set_layout(TILEDB_UNORDERED);
    4282          34 :         if (!m_osFIDColumn.empty())
    4283          32 :             query.set_data_buffer(m_osFIDColumn, *m_anFIDs);
    4284          34 :         query.set_data_buffer(m_osXDim, *m_adfXs);
    4285          34 :         query.set_data_buffer(m_osYDim, *m_adfYs);
    4286          34 :         if (!m_osZDim.empty())
    4287          16 :             query.set_data_buffer(m_osZDim, *m_adfZs);
    4288             : 
    4289          34 :         const char *pszGeomColName = GetDatabaseGeomColName();
    4290          34 :         if (pszGeomColName)
    4291             :         {
    4292          29 :             m_anGeometryOffsets->pop_back();
    4293          29 :             const auto eTileDBType = m_schema->attribute(pszGeomColName).type();
    4294          29 :             if (eTileDBType == TILEDB_UINT8)
    4295             :             {
    4296           0 :                 query.set_data_buffer(pszGeomColName, *m_abyGeometries);
    4297           0 :                 query.set_offsets_buffer(pszGeomColName, *m_anGeometryOffsets);
    4298             :             }
    4299          29 :             else if (eTileDBType == TILEDB_BLOB)
    4300             :             {
    4301             :                 query.set_data_buffer(
    4302             :                     pszGeomColName,
    4303          29 :                     reinterpret_cast<std::byte *>(m_abyGeometries->data()),
    4304          58 :                     m_abyGeometries->size());
    4305             :                 query.set_offsets_buffer(pszGeomColName,
    4306          29 :                                          m_anGeometryOffsets->data(),
    4307          29 :                                          m_anGeometryOffsets->size());
    4308             :             }
    4309             : #ifdef HAS_TILEDB_GEOM_WKB_WKT
    4310             :             else if (eTileDBType == TILEDB_GEOM_WKB)
    4311             :             {
    4312             :                 query.set_offsets_buffer(pszGeomColName, *m_anGeometryOffsets);
    4313             :                 // We could use API expected std::byte, but this requires
    4314             :                 // TileDB 2.22 because of https://github.com/TileDB-Inc/TileDB/pull/4826
    4315             :                 query.set_data_buffer(
    4316             :                     pszGeomColName,
    4317             :                     static_cast<void *>(m_abyGeometries->data()),
    4318             :                     m_abyGeometries->size());
    4319             :             }
    4320             : #endif
    4321             :             else
    4322             :             {
    4323           0 :                 CPLAssert(false);
    4324             :             }
    4325             :         }
    4326             : 
    4327         211 :         for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
    4328             :         {
    4329         177 :             const OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
    4330         177 :             const char *pszFieldName = poFieldDefn->GetNameRef();
    4331         177 :             auto &anOffsets = *(m_aFieldValueOffsets[i]);
    4332         177 :             auto &fieldValues = m_aFieldValues[i];
    4333             : 
    4334         177 :             if (poFieldDefn->IsNullable())
    4335          63 :                 query.set_validity_buffer(pszFieldName, m_aFieldValidity[i]);
    4336             : 
    4337         177 :             const auto eType = poFieldDefn->GetType();
    4338         177 :             switch (eType)
    4339             :             {
    4340          84 :                 case OFTInteger:
    4341             :                 case OFTIntegerList:
    4342             :                 {
    4343          84 :                     if (eType == OFTIntegerList)
    4344             :                     {
    4345          27 :                         anOffsets.pop_back();
    4346          27 :                         query.set_offsets_buffer(pszFieldName, anOffsets);
    4347             :                     }
    4348             : 
    4349          84 :                     if (m_aeFieldTypes[i] == TILEDB_BOOL)
    4350             :                     {
    4351             :                         auto &v = *(std::get<std::shared_ptr<VECTOR_OF_BOOL>>(
    4352          18 :                             fieldValues));
    4353             : #ifdef VECTOR_OF_BOOL_IS_NOT_UINT8_T
    4354             :                         query.set_data_buffer(pszFieldName, v.data(), v.size());
    4355             : #else
    4356          18 :                         query.set_data_buffer(pszFieldName, v);
    4357             : #endif
    4358             :                     }
    4359          66 :                     else if (m_aeFieldTypes[i] == TILEDB_INT16)
    4360             :                     {
    4361             :                         auto &v =
    4362             :                             *(std::get<std::shared_ptr<std::vector<int16_t>>>(
    4363          18 :                                 fieldValues));
    4364          18 :                         query.set_data_buffer(pszFieldName, v);
    4365             :                     }
    4366          48 :                     else if (m_aeFieldTypes[i] == TILEDB_INT32)
    4367             :                     {
    4368             :                         auto &v =
    4369             :                             *(std::get<std::shared_ptr<std::vector<int32_t>>>(
    4370          30 :                                 fieldValues));
    4371          30 :                         query.set_data_buffer(pszFieldName, v);
    4372             :                     }
    4373          18 :                     else if (m_aeFieldTypes[i] == TILEDB_UINT8)
    4374             :                     {
    4375             :                         auto &v =
    4376             :                             *(std::get<std::shared_ptr<std::vector<uint8_t>>>(
    4377           9 :                                 fieldValues));
    4378           9 :                         query.set_data_buffer(pszFieldName, v);
    4379             :                     }
    4380           9 :                     else if (m_aeFieldTypes[i] == TILEDB_UINT16)
    4381             :                     {
    4382             :                         auto &v =
    4383             :                             *(std::get<std::shared_ptr<std::vector<uint16_t>>>(
    4384           9 :                                 fieldValues));
    4385           9 :                         query.set_data_buffer(pszFieldName, v);
    4386             :                     }
    4387          84 :                     break;
    4388             :                 }
    4389             : 
    4390          10 :                 case OFTInteger64:
    4391             :                 {
    4392             :                     query.set_data_buffer(
    4393             :                         pszFieldName,
    4394             :                         *std::get<std::shared_ptr<std::vector<int64_t>>>(
    4395          10 :                             fieldValues));
    4396          10 :                     break;
    4397             :                 }
    4398             : 
    4399           0 :                 case OFTInteger64List:
    4400             :                 {
    4401           0 :                     anOffsets.pop_back();
    4402             :                     query.set_data_buffer(
    4403             :                         pszFieldName,
    4404             :                         *std::get<std::shared_ptr<std::vector<int64_t>>>(
    4405           0 :                             fieldValues));
    4406           0 :                     query.set_offsets_buffer(pszFieldName, anOffsets);
    4407           0 :                     break;
    4408             :                 }
    4409             : 
    4410          19 :                 case OFTReal:
    4411             :                 {
    4412          19 :                     if (poFieldDefn->GetSubType() == OFSTFloat32)
    4413             :                     {
    4414             :                         query.set_data_buffer(
    4415             :                             pszFieldName,
    4416             :                             *std::get<std::shared_ptr<std::vector<float>>>(
    4417           9 :                                 fieldValues));
    4418             :                     }
    4419             :                     else
    4420             :                     {
    4421             :                         query.set_data_buffer(
    4422             :                             pszFieldName,
    4423             :                             *std::get<std::shared_ptr<std::vector<double>>>(
    4424          10 :                                 fieldValues));
    4425             :                     }
    4426          19 :                     break;
    4427             :                 }
    4428             : 
    4429          18 :                 case OFTRealList:
    4430             :                 {
    4431          18 :                     anOffsets.pop_back();
    4432          18 :                     if (poFieldDefn->GetSubType() == OFSTFloat32)
    4433             :                     {
    4434             :                         query.set_data_buffer(
    4435             :                             pszFieldName,
    4436             :                             *std::get<std::shared_ptr<std::vector<float>>>(
    4437           9 :                                 fieldValues));
    4438           9 :                         query.set_offsets_buffer(pszFieldName, anOffsets);
    4439             :                     }
    4440             :                     else
    4441             :                     {
    4442             :                         query.set_data_buffer(
    4443             :                             pszFieldName,
    4444             :                             *std::get<std::shared_ptr<std::vector<double>>>(
    4445           9 :                                 fieldValues));
    4446           9 :                         query.set_offsets_buffer(pszFieldName, anOffsets);
    4447             :                     }
    4448          18 :                     break;
    4449             :                 }
    4450             : 
    4451          10 :                 case OFTString:
    4452             :                 {
    4453          10 :                     anOffsets.pop_back();
    4454             :                     query.set_data_buffer(
    4455             :                         pszFieldName,
    4456          10 :                         *std::get<std::shared_ptr<std::string>>(fieldValues));
    4457          10 :                     query.set_offsets_buffer(pszFieldName, anOffsets);
    4458          10 :                     break;
    4459             :                 }
    4460             : 
    4461           9 :                 case OFTBinary:
    4462             :                 {
    4463           9 :                     anOffsets.pop_back();
    4464             :                     auto &v = *(std::get<std::shared_ptr<std::vector<uint8_t>>>(
    4465           9 :                         fieldValues));
    4466           9 :                     if (m_aeFieldTypes[i] == TILEDB_UINT8)
    4467             :                     {
    4468           0 :                         query.set_data_buffer(pszFieldName, v);
    4469           0 :                         query.set_offsets_buffer(pszFieldName, anOffsets);
    4470             :                     }
    4471           9 :                     else if (m_aeFieldTypes[i] == TILEDB_BLOB)
    4472             :                     {
    4473             :                         query.set_data_buffer(
    4474             :                             pszFieldName,
    4475           9 :                             reinterpret_cast<std::byte *>(v.data()), v.size());
    4476             :                         query.set_offsets_buffer(pszFieldName, anOffsets.data(),
    4477           9 :                                                  anOffsets.size());
    4478             :                     }
    4479             :                     else
    4480             :                     {
    4481           0 :                         CPLAssert(false);
    4482             :                     }
    4483           9 :                     break;
    4484             :                 }
    4485             : 
    4486          27 :                 case OFTDate:
    4487             :                 case OFTDateTime:
    4488             :                 case OFTTime:
    4489             :                 {
    4490             :                     query.set_data_buffer(
    4491             :                         pszFieldName,
    4492             :                         *std::get<std::shared_ptr<std::vector<int64_t>>>(
    4493          27 :                             fieldValues));
    4494          27 :                     break;
    4495             :                 }
    4496             : 
    4497           0 :                 default:
    4498             :                 {
    4499           0 :                     CPLAssert(false);
    4500             :                     break;
    4501             :                 }
    4502             :             }
    4503             :         }
    4504             : 
    4505          34 :         if (m_bStats)
    4506           0 :             tiledb::Stats::enable();
    4507             : 
    4508          34 :         query.submit();
    4509             : 
    4510          34 :         if (m_bStats)
    4511             :         {
    4512           0 :             tiledb::Stats::dump(stdout);
    4513           0 :             tiledb::Stats::disable();
    4514             :         }
    4515             :     }
    4516           0 :     catch (const std::exception &)
    4517             :     {
    4518           0 :         ResetBuffers();
    4519           0 :         throw;
    4520             :     }
    4521          34 :     ResetBuffers();
    4522          34 : }
    4523             : 
    4524             : /************************************************************************/
    4525             : /*                          ResetBuffers()                              */
    4526             : /************************************************************************/
    4527             : 
    4528             : namespace
    4529             : {
    4530             : template <class T> struct ClearArray
    4531             : {
    4532        8725 :     static void exec(OGRTileDBLayer::ArrayType &array)
    4533             :     {
    4534        8725 :         std::get<std::shared_ptr<T>>(array)->clear();
    4535        8725 :     }
    4536             : };
    4537             : }  // namespace
    4538             : 
    4539         631 : void OGRTileDBLayer::ResetBuffers()
    4540             : {
    4541         631 :     if (!m_bArrowBatchReleased)
    4542             :     {
    4543          39 :         AllocateNewBuffers();
    4544             :     }
    4545             :     else
    4546             :     {
    4547             :         // Reset buffers
    4548         592 :         m_anFIDs->clear();
    4549         592 :         m_adfXs->clear();
    4550         592 :         m_adfYs->clear();
    4551         592 :         m_adfZs->clear();
    4552         592 :         m_abyGeometries->clear();
    4553         592 :         m_anGeometryOffsets->clear();
    4554        9317 :         for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
    4555             :         {
    4556        8725 :             m_aFieldValueOffsets[i]->clear();
    4557        8725 :             m_aFieldValidity[i].clear();
    4558        8725 :             ProcessField<ClearArray>::exec(m_aeFieldTypes[i],
    4559        8725 :                                            m_aFieldValues[i]);
    4560             :         }
    4561             :     }
    4562         631 : }
    4563             : 
    4564             : /************************************************************************/
    4565             : /*                         TestCapability()                             */
    4566             : /************************************************************************/
    4567             : 
    4568         293 : int OGRTileDBLayer::TestCapability(const char *pszCap)
    4569             : {
    4570         293 :     if (EQUAL(pszCap, OLCCreateField))
    4571           4 :         return m_bUpdatable && m_schema == nullptr;
    4572             : 
    4573         289 :     if (EQUAL(pszCap, OLCSequentialWrite))
    4574           3 :         return m_bUpdatable;
    4575             : 
    4576         286 :     if (EQUAL(pszCap, OLCFastFeatureCount))
    4577           0 :         return (!m_poAttrQuery && !m_poFilterGeom && m_nTotalFeatureCount >= 0);
    4578             : 
    4579         286 :     if (EQUAL(pszCap, OLCFastGetExtent))
    4580           2 :         return m_oLayerExtent.IsInit();
    4581             : 
    4582         284 :     if (EQUAL(pszCap, OLCStringsAsUTF8))
    4583         196 :         return true;
    4584             : 
    4585          88 :     if (EQUAL(pszCap, OLCCurveGeometries))
    4586          41 :         return true;
    4587             : 
    4588          47 :     if (EQUAL(pszCap, OLCMeasuredGeometries))
    4589          35 :         return true;
    4590             : 
    4591          12 :     if (EQUAL(pszCap, OLCFastSpatialFilter))
    4592           0 :         return true;
    4593             : 
    4594          12 :     if (EQUAL(pszCap, OLCIgnoreFields))
    4595           1 :         return true;
    4596             : 
    4597          11 :     if (EQUAL(pszCap, OLCFastGetArrowStream))
    4598           0 :         return true;
    4599             : 
    4600          11 :     return false;
    4601             : }
    4602             : 
    4603             : /************************************************************************/
    4604             : /*                         GetArrowSchema()                             */
    4605             : /************************************************************************/
    4606             : 
    4607          44 : int OGRTileDBLayer::GetArrowSchema(struct ArrowArrayStream *out_stream,
    4608             :                                    struct ArrowSchema *out_schema)
    4609             : {
    4610          44 :     int ret = OGRLayer::GetArrowSchema(out_stream, out_schema);
    4611          44 :     if (ret != 0)
    4612           0 :         return ret;
    4613             : 
    4614             :     // Patch integer fields
    4615          44 :     const bool bIncludeFID = CPLTestBool(
    4616             :         m_aosArrowArrayStreamOptions.FetchNameValueDef("INCLUDE_FID", "YES"));
    4617          44 :     const int nFieldCount = m_poFeatureDefn->GetFieldCount();
    4618          44 :     int iSchemaChild = bIncludeFID ? 1 : 0;
    4619         581 :     for (int i = 0; i < nFieldCount; ++i)
    4620             :     {
    4621         537 :         const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
    4622         537 :         if (poFieldDefn->IsIgnored())
    4623             :         {
    4624           2 :             continue;
    4625             :         }
    4626         535 :         const auto eType = poFieldDefn->GetType();
    4627         535 :         if (eType == OFTInteger || eType == OFTIntegerList)
    4628             :         {
    4629         243 :             const char *&format =
    4630             :                 eType == OFTInteger
    4631         162 :                     ? out_schema->children[iSchemaChild]->format
    4632          81 :                     : out_schema->children[iSchemaChild]->children[0]->format;
    4633         243 :             if (m_aeFieldTypes[i] == TILEDB_BOOL)
    4634          54 :                 format = "b";
    4635         189 :             else if (m_aeFieldTypes[i] == TILEDB_INT16)
    4636          54 :                 format = "s";
    4637         135 :             else if (m_aeFieldTypes[i] == TILEDB_INT32)
    4638          81 :                 format = "i";
    4639          54 :             else if (m_aeFieldTypes[i] == TILEDB_UINT8)
    4640          27 :                 format = "C";
    4641          27 :             else if (m_aeFieldTypes[i] == TILEDB_UINT16)
    4642          27 :                 format = "S";
    4643             :             else
    4644             :             {
    4645           0 :                 CPLAssert(false);
    4646             :             }
    4647             :         }
    4648         535 :         ++iSchemaChild;
    4649             :     }
    4650             : 
    4651             :     // Patch other fields
    4652         663 :     for (int64_t i = 0; i < out_schema->n_children; ++i)
    4653             :     {
    4654         619 :         const char *&format = out_schema->children[i]->format;
    4655         619 :         if (strcmp(format, "+l") == 0)
    4656             :         {
    4657             :             // 32-bit list to 64-bit list
    4658         135 :             format = "+L";
    4659             :         }
    4660         484 :         else if (strcmp(format, "u") == 0)
    4661             :         {
    4662             :             // 32-bit string to 64-bit string
    4663          33 :             format = "U";
    4664             :         }
    4665         451 :         else if (strcmp(format, "z") == 0)
    4666             :         {
    4667             :             // 32-bit binary to 64-bit binary
    4668          69 :             format = "Z";
    4669             :         }
    4670             :     }
    4671          44 :     return 0;
    4672             : }
    4673             : 
    4674             : /************************************************************************/
    4675             : /*                        ReleaseArrowArray()                           */
    4676             : /************************************************************************/
    4677             : 
    4678         975 : void OGRTileDBLayer::ReleaseArrowArray(struct ArrowArray *array)
    4679             : {
    4680        1901 :     for (int i = 0; i < static_cast<int>(array->n_children); ++i)
    4681             :     {
    4682         926 :         if (array->children[i] && array->children[i]->release)
    4683             :         {
    4684         926 :             array->children[i]->release(array->children[i]);
    4685         926 :             CPLFree(array->children[i]);
    4686             :         }
    4687             :     }
    4688         975 :     CPLFree(array->children);
    4689         975 :     CPLFree(array->buffers);
    4690             : 
    4691         975 :     OGRTileDBArrowArrayPrivateData *psPrivateData =
    4692             :         static_cast<OGRTileDBArrowArrayPrivateData *>(array->private_data);
    4693        1005 :     if (psPrivateData->m_pbLayerStillAlive &&
    4694        1005 :         *psPrivateData->m_pbLayerStillAlive)
    4695             :     {
    4696          23 :         psPrivateData->m_poLayer->m_bArrowBatchReleased = true;
    4697             :     }
    4698         975 :     delete psPrivateData;
    4699         975 :     array->private_data = nullptr;
    4700         975 :     array->release = nullptr;
    4701         975 : }
    4702             : 
    4703             : /************************************************************************/
    4704             : /*                            SetNullBuffer()                           */
    4705             : /************************************************************************/
    4706             : 
    4707         662 : void OGRTileDBLayer::SetNullBuffer(
    4708             :     struct ArrowArray *psChild, int iField,
    4709             :     const std::vector<bool> &abyValidityFromFilters)
    4710             : {
    4711         662 :     if (m_poFeatureDefn->GetFieldDefn(iField)->IsNullable())
    4712             :     {
    4713             :         // TileDB used a std::vector<uint8_t> with 1 element per byte
    4714             :         // whereas Arrow uses a ~ std::vector<bool> with 8 elements per byte
    4715         245 :         OGRTileDBArrowArrayPrivateData *psPrivateData =
    4716             :             static_cast<OGRTileDBArrowArrayPrivateData *>(
    4717             :                 psChild->private_data);
    4718         245 :         const auto &v_validity = m_aFieldValidity[iField];
    4719         245 :         uint8_t *pabyNull = nullptr;
    4720         245 :         const size_t nSrcSize = static_cast<size_t>(m_nRowCountInResultSet);
    4721         245 :         if (abyValidityFromFilters.empty())
    4722             :         {
    4723         852 :             for (size_t i = 0; i < nSrcSize; ++i)
    4724             :             {
    4725         686 :                 if (!v_validity[i])
    4726             :                 {
    4727         143 :                     ++psChild->null_count;
    4728         143 :                     if (pabyNull == nullptr)
    4729             :                     {
    4730             :                         psPrivateData->nullHolder =
    4731         143 :                             std::make_shared<std::vector<uint8_t>>(
    4732         143 :                                 (nSrcSize + 7) / 8, static_cast<uint8_t>(0xFF));
    4733         143 :                         pabyNull = psPrivateData->nullHolder->data();
    4734         143 :                         psChild->buffers[0] = pabyNull;
    4735             :                     }
    4736         143 :                     pabyNull[i / 8] &= static_cast<uint8_t>(~(1 << (i % 8)));
    4737             :                 }
    4738             :             }
    4739             :         }
    4740             :         else
    4741             :         {
    4742         356 :             for (size_t i = 0, j = 0; i < nSrcSize; ++i)
    4743             :             {
    4744         277 :                 if (abyValidityFromFilters[i])
    4745             :                 {
    4746          97 :                     if (!v_validity[i])
    4747             :                     {
    4748          18 :                         ++psChild->null_count;
    4749          18 :                         if (pabyNull == nullptr)
    4750             :                         {
    4751          18 :                             const size_t nDstSize =
    4752          18 :                                 static_cast<size_t>(psChild->length);
    4753             :                             psPrivateData->nullHolder =
    4754          18 :                                 std::make_shared<std::vector<uint8_t>>(
    4755           0 :                                     (nDstSize + 7) / 8,
    4756          18 :                                     static_cast<uint8_t>(0xFF));
    4757          18 :                             pabyNull = psPrivateData->nullHolder->data();
    4758          18 :                             psChild->buffers[0] = pabyNull;
    4759             :                         }
    4760          18 :                         pabyNull[j / 8] &=
    4761          18 :                             static_cast<uint8_t>(~(1 << (j % 8)));
    4762             :                     }
    4763          97 :                     ++j;
    4764             :                 }
    4765             :             }
    4766             :         }
    4767             :     }
    4768         662 : }
    4769             : 
    4770             : /************************************************************************/
    4771             : /*                           FillBoolArray()                            */
    4772             : /************************************************************************/
    4773             : 
    4774          34 : void OGRTileDBLayer::FillBoolArray(
    4775             :     struct ArrowArray *psChild, int iField,
    4776             :     const std::vector<bool> &abyValidityFromFilters)
    4777             : {
    4778             :     OGRTileDBArrowArrayPrivateData *psPrivateData =
    4779          34 :         new OGRTileDBArrowArrayPrivateData;
    4780          34 :     psChild->private_data = psPrivateData;
    4781             : 
    4782          34 :     psChild->n_buffers = 2;
    4783          34 :     psChild->buffers = static_cast<const void **>(CPLCalloc(2, sizeof(void *)));
    4784             :     // TileDB used a std::vector<uint8_t> with 1 element per byte
    4785             :     // whereas Arrow uses a ~ std::vector<bool> with 8 elements per byte
    4786             :     const auto &v_source =
    4787          34 :         *(std::get<std::shared_ptr<VECTOR_OF_BOOL>>(m_aFieldValues[iField]));
    4788          34 :     const size_t nDstSize = abyValidityFromFilters.empty()
    4789          34 :                                 ? v_source.size()
    4790          10 :                                 : static_cast<size_t>(psChild->length);
    4791             :     auto arrayValues =
    4792          68 :         std::make_shared<std::vector<uint8_t>>((nDstSize + 7) / 8);
    4793          34 :     psPrivateData->valueHolder = arrayValues;
    4794          34 :     auto panValues = arrayValues->data();
    4795          34 :     psChild->buffers[1] = panValues;
    4796          34 :     if (abyValidityFromFilters.empty())
    4797             :     {
    4798          87 :         for (size_t i = 0; i < v_source.size(); ++i)
    4799             :         {
    4800          63 :             if (v_source[i])
    4801             :             {
    4802          17 :                 panValues[i / 8] |= static_cast<uint8_t>(1 << (i % 8));
    4803             :             }
    4804             :         }
    4805             :     }
    4806             :     else
    4807             :     {
    4808          33 :         for (size_t i = 0, j = 0; i < v_source.size(); ++i)
    4809             :         {
    4810          23 :             if (abyValidityFromFilters[i])
    4811             :             {
    4812           5 :                 if (v_source[i])
    4813             :                 {
    4814           1 :                     panValues[j / 8] |= static_cast<uint8_t>(1 << (j % 8));
    4815             :                 }
    4816           5 :                 ++j;
    4817             :             }
    4818             :         }
    4819             :     }
    4820             : 
    4821          34 :     SetNullBuffer(psChild, iField, abyValidityFromFilters);
    4822          34 : }
    4823             : 
    4824             : /************************************************************************/
    4825             : /*                        FillPrimitiveArray()                          */
    4826             : /************************************************************************/
    4827             : 
    4828             : template <typename T>
    4829         318 : void OGRTileDBLayer::FillPrimitiveArray(
    4830             :     struct ArrowArray *psChild, int iField,
    4831             :     const std::vector<bool> &abyValidityFromFilters)
    4832             : {
    4833         318 :     OGRTileDBArrowArrayPrivateData *psPrivateData =
    4834         318 :         new OGRTileDBArrowArrayPrivateData;
    4835         318 :     psChild->private_data = psPrivateData;
    4836             : 
    4837         318 :     psChild->n_buffers = 2;
    4838         318 :     psChild->buffers = static_cast<const void **>(CPLCalloc(2, sizeof(void *)));
    4839             :     auto &v_source =
    4840         318 :         std::get<std::shared_ptr<std::vector<T>>>(m_aFieldValues[iField]);
    4841         318 :     psPrivateData->valueHolder = v_source;
    4842         318 :     psChild->buffers[1] = v_source->data();
    4843             : 
    4844         318 :     if (!abyValidityFromFilters.empty())
    4845             :     {
    4846          92 :         const size_t nSrcSize = static_cast<size_t>(m_nRowCountInResultSet);
    4847         319 :         for (size_t i = 0, j = 0; i < nSrcSize; ++i)
    4848             :         {
    4849         227 :             if (abyValidityFromFilters[i])
    4850             :             {
    4851          59 :                 (*v_source)[j] = (*v_source)[i];
    4852          59 :                 ++j;
    4853             :             }
    4854             :         }
    4855             :     }
    4856             : 
    4857         318 :     SetNullBuffer(psChild, iField, abyValidityFromFilters);
    4858         318 : }
    4859             : 
    4860             : /************************************************************************/
    4861             : /*                      FillStringOrBinaryArray()                       */
    4862             : /************************************************************************/
    4863             : 
    4864             : template <typename T>
    4865          72 : void OGRTileDBLayer::FillStringOrBinaryArray(
    4866             :     struct ArrowArray *psChild, int iField,
    4867             :     const std::vector<bool> &abyValidityFromFilters)
    4868             : {
    4869          72 :     OGRTileDBArrowArrayPrivateData *psPrivateData =
    4870          72 :         new OGRTileDBArrowArrayPrivateData;
    4871          72 :     psChild->private_data = psPrivateData;
    4872             : 
    4873          72 :     psChild->n_buffers = 3;
    4874          72 :     psChild->buffers = static_cast<const void **>(CPLCalloc(3, sizeof(void *)));
    4875             : 
    4876          72 :     auto &v_source = std::get<std::shared_ptr<T>>(m_aFieldValues[iField]);
    4877             : 
    4878          72 :     psPrivateData->offsetHolder = m_aFieldValueOffsets[iField];
    4879             :     // Add back extra offset
    4880          72 :     if (!psPrivateData->offsetHolder->empty())
    4881          72 :         psPrivateData->offsetHolder->push_back(v_source->size());
    4882          72 :     psChild->buffers[1] = psPrivateData->offsetHolder->data();
    4883             : 
    4884          72 :     psPrivateData->valueHolder = v_source;
    4885          72 :     psChild->buffers[2] = v_source->data();
    4886             : 
    4887          72 :     if (!abyValidityFromFilters.empty())
    4888             :     {
    4889          21 :         const size_t nSrcSize = static_cast<size_t>(m_nRowCountInResultSet);
    4890          21 :         size_t nAccLen = 0;
    4891          77 :         for (size_t i = 0, j = 0; i < nSrcSize; ++i)
    4892             :         {
    4893          56 :             if (abyValidityFromFilters[i])
    4894             :             {
    4895          17 :                 const size_t nSrcOffset =
    4896          17 :                     static_cast<size_t>((*psPrivateData->offsetHolder)[i]);
    4897          17 :                 const size_t nNextOffset =
    4898          17 :                     static_cast<size_t>((*psPrivateData->offsetHolder)[i + 1]);
    4899          17 :                 const size_t nItemLen = nNextOffset - nSrcOffset;
    4900          17 :                 (*psPrivateData->offsetHolder)[j] = nAccLen;
    4901          17 :                 if (nItemLen && nAccLen < nSrcOffset)
    4902             :                 {
    4903           5 :                     memmove(v_source->data() + nAccLen,
    4904           5 :                             v_source->data() + nSrcOffset, nItemLen);
    4905             :                 }
    4906          17 :                 nAccLen += nItemLen;
    4907          17 :                 ++j;
    4908             :             }
    4909             :         }
    4910          21 :         (*psPrivateData->offsetHolder)[static_cast<size_t>(psChild->length)] =
    4911             :             nAccLen;
    4912             :     }
    4913             : 
    4914          72 :     SetNullBuffer(psChild, iField, abyValidityFromFilters);
    4915          72 : }
    4916             : 
    4917             : /************************************************************************/
    4918             : /*                      FillTimeOrDateArray()                           */
    4919             : /************************************************************************/
    4920             : 
    4921          68 : void OGRTileDBLayer::FillTimeOrDateArray(
    4922             :     struct ArrowArray *psChild, int iField,
    4923             :     const std::vector<bool> &abyValidityFromFilters)
    4924             : {
    4925             :     // TileDB uses 64-bit for time[ms], whereas Arrow uses 32-bit
    4926             :     // Idem for date[day]
    4927             :     OGRTileDBArrowArrayPrivateData *psPrivateData =
    4928          68 :         new OGRTileDBArrowArrayPrivateData;
    4929          68 :     psChild->private_data = psPrivateData;
    4930             : 
    4931          68 :     psChild->n_buffers = 2;
    4932          68 :     psChild->buffers = static_cast<const void **>(CPLCalloc(2, sizeof(void *)));
    4933             : 
    4934             :     const auto &v_source = *(std::get<std::shared_ptr<std::vector<int64_t>>>(
    4935          68 :         m_aFieldValues[iField]));
    4936          68 :     const size_t nDstSize = abyValidityFromFilters.empty()
    4937          68 :                                 ? v_source.size()
    4938          20 :                                 : static_cast<size_t>(psChild->length);
    4939         136 :     auto newValuesPtr = std::make_shared<std::vector<int32_t>>(nDstSize);
    4940          68 :     psPrivateData->valueHolder = newValuesPtr;
    4941          68 :     auto &newValues = *newValuesPtr;
    4942             : 
    4943          68 :     if (abyValidityFromFilters.empty())
    4944             :     {
    4945         174 :         for (size_t i = 0; i < v_source.size(); ++i)
    4946             :         {
    4947         126 :             newValues[i] = static_cast<int32_t>(v_source[i]);
    4948             :         }
    4949             :     }
    4950             :     else
    4951             :     {
    4952          66 :         for (size_t i = 0, j = 0; i < v_source.size(); ++i)
    4953             :         {
    4954          46 :             if (abyValidityFromFilters[i])
    4955             :             {
    4956          10 :                 newValues[j] = static_cast<int32_t>(v_source[i]);
    4957          10 :                 ++j;
    4958             :             }
    4959             :         }
    4960             :     }
    4961          68 :     psChild->buffers[1] = newValuesPtr->data();
    4962             : 
    4963          68 :     SetNullBuffer(psChild, iField, abyValidityFromFilters);
    4964          68 : }
    4965             : 
    4966             : /************************************************************************/
    4967             : /*                      FillPrimitiveListArray()                        */
    4968             : /************************************************************************/
    4969             : 
    4970             : template <typename T>
    4971         136 : void OGRTileDBLayer::FillPrimitiveListArray(
    4972             :     struct ArrowArray *psChild, int iField,
    4973             :     const std::vector<bool> &abyValidityFromFilters)
    4974             : {
    4975         136 :     OGRTileDBArrowArrayPrivateData *psPrivateData =
    4976         136 :         new OGRTileDBArrowArrayPrivateData;
    4977         136 :     psChild->private_data = psPrivateData;
    4978             : 
    4979             :     // We cannot direct use m_aFieldValueOffsets as it uses offsets in
    4980             :     // bytes whereas Arrow uses offsets in number of elements
    4981         136 :     psChild->n_buffers = 2;
    4982         136 :     psChild->buffers = static_cast<const void **>(CPLCalloc(2, sizeof(void *)));
    4983         136 :     auto offsetsPtr = std::make_shared<std::vector<uint64_t>>();
    4984         136 :     const auto &offsetsSrc = *(m_aFieldValueOffsets[iField]);
    4985         136 :     const size_t nSrcVals = offsetsSrc.size();
    4986         136 :     if (abyValidityFromFilters.empty())
    4987             :     {
    4988          96 :         offsetsPtr->reserve(nSrcVals + 1);
    4989             :     }
    4990             :     else
    4991             :     {
    4992          40 :         offsetsPtr->reserve(static_cast<size_t>(psChild->length) + 1);
    4993             :     }
    4994         136 :     psPrivateData->offsetHolder = offsetsPtr;
    4995         136 :     auto &offsets = *offsetsPtr;
    4996             :     auto &v_source =
    4997         136 :         std::get<std::shared_ptr<std::vector<T>>>(m_aFieldValues[iField]);
    4998             : 
    4999         136 :     if (abyValidityFromFilters.empty())
    5000             :     {
    5001         348 :         for (size_t i = 0; i < nSrcVals; ++i)
    5002         252 :             offsets.push_back(offsetsSrc[i] / sizeof(T));
    5003          96 :         offsets.push_back(v_source->size());
    5004             :     }
    5005             :     else
    5006             :     {
    5007          40 :         size_t nAccLen = 0;
    5008         132 :         for (size_t i = 0; i < nSrcVals; ++i)
    5009             :         {
    5010          92 :             if (abyValidityFromFilters[i])
    5011             :             {
    5012          20 :                 const auto nSrcOffset =
    5013          20 :                     static_cast<size_t>(offsetsSrc[i] / sizeof(T));
    5014          40 :                 const auto nNextOffset = i + 1 < nSrcVals
    5015          20 :                                              ? offsetsSrc[i + 1] / sizeof(T)
    5016             :                                              : v_source->size();
    5017          20 :                 const size_t nItemLen =
    5018             :                     static_cast<size_t>(nNextOffset - nSrcOffset);
    5019          20 :                 offsets.push_back(nAccLen);
    5020          20 :                 if (nItemLen && nAccLen < nSrcOffset)
    5021             :                 {
    5022           4 :                     memmove(v_source->data() + nAccLen,
    5023           4 :                             v_source->data() + nSrcOffset,
    5024             :                             nItemLen * sizeof(T));
    5025             :                 }
    5026          20 :                 nAccLen += nItemLen;
    5027             :             }
    5028             :         }
    5029          40 :         offsets.push_back(nAccLen);
    5030             :     }
    5031             : 
    5032         136 :     psChild->buffers[1] = offsetsPtr->data();
    5033             : 
    5034         136 :     SetNullBuffer(psChild, iField, abyValidityFromFilters);
    5035             : 
    5036         136 :     psChild->n_children = 1;
    5037         136 :     psChild->children = static_cast<struct ArrowArray **>(
    5038         136 :         CPLCalloc(1, sizeof(struct ArrowArray *)));
    5039         272 :     psChild->children[0] = static_cast<struct ArrowArray *>(
    5040         136 :         CPLCalloc(1, sizeof(struct ArrowArray)));
    5041         136 :     auto psValueChild = psChild->children[0];
    5042             : 
    5043         136 :     psValueChild->release = psChild->release;
    5044             : 
    5045         136 :     psValueChild->n_buffers = 2;
    5046         136 :     psValueChild->buffers =
    5047         136 :         static_cast<const void **>(CPLCalloc(2, sizeof(void *)));
    5048         136 :     psValueChild->length = offsets.back();
    5049             : 
    5050         136 :     psPrivateData = new OGRTileDBArrowArrayPrivateData;
    5051         136 :     psValueChild->private_data = psPrivateData;
    5052         136 :     psPrivateData->valueHolder = v_source;
    5053         136 :     psValueChild->buffers[1] = v_source->data();
    5054         136 : }
    5055             : 
    5056             : /************************************************************************/
    5057             : /*                        FillBoolListArray()                           */
    5058             : /************************************************************************/
    5059             : 
    5060          34 : void OGRTileDBLayer::FillBoolListArray(
    5061             :     struct ArrowArray *psChild, int iField,
    5062             :     const std::vector<bool> &abyValidityFromFilters)
    5063             : {
    5064             :     OGRTileDBArrowArrayPrivateData *psPrivateData =
    5065          34 :         new OGRTileDBArrowArrayPrivateData;
    5066          34 :     psChild->private_data = psPrivateData;
    5067             : 
    5068          34 :     psChild->n_buffers = 2;
    5069          34 :     psChild->buffers = static_cast<const void **>(CPLCalloc(2, sizeof(void *)));
    5070          34 :     auto &offsetsPtr = m_aFieldValueOffsets[iField];
    5071          34 :     psPrivateData->offsetHolder = offsetsPtr;
    5072             :     auto &v_source =
    5073          34 :         *(std::get<std::shared_ptr<VECTOR_OF_BOOL>>(m_aFieldValues[iField]));
    5074             : 
    5075          34 :     psChild->n_children = 1;
    5076          34 :     psChild->children = static_cast<struct ArrowArray **>(
    5077          34 :         CPLCalloc(1, sizeof(struct ArrowArray *)));
    5078          68 :     psChild->children[0] = static_cast<struct ArrowArray *>(
    5079          34 :         CPLCalloc(1, sizeof(struct ArrowArray)));
    5080          34 :     auto psValueChild = psChild->children[0];
    5081             : 
    5082          34 :     psValueChild->release = psChild->release;
    5083             : 
    5084          34 :     psValueChild->n_buffers = 2;
    5085          34 :     psValueChild->buffers =
    5086          34 :         static_cast<const void **>(CPLCalloc(2, sizeof(void *)));
    5087             : 
    5088          34 :     psPrivateData = new OGRTileDBArrowArrayPrivateData;
    5089          34 :     psValueChild->private_data = psPrivateData;
    5090             : 
    5091             :     // TileDB used a std::vector<uint8_t> with 1 element per byte
    5092             :     // whereas Arrow uses a ~ std::vector<bool> with 8 elements per byte
    5093             :     auto arrayValues =
    5094          68 :         std::make_shared<std::vector<uint8_t>>((v_source.size() + 7) / 8);
    5095          34 :     psPrivateData->valueHolder = arrayValues;
    5096          34 :     auto panValues = arrayValues->data();
    5097          34 :     psValueChild->buffers[1] = panValues;
    5098             : 
    5099          34 :     if (abyValidityFromFilters.empty())
    5100             :     {
    5101          24 :         offsetsPtr->push_back(v_source.size());
    5102             : 
    5103         116 :         for (size_t iFeat = 0; iFeat < v_source.size(); ++iFeat)
    5104             :         {
    5105          92 :             if (v_source[iFeat])
    5106          46 :                 panValues[iFeat / 8] |= static_cast<uint8_t>(1 << (iFeat % 8));
    5107             :         }
    5108             : 
    5109          24 :         psValueChild->length = v_source.size();
    5110             :     }
    5111             :     else
    5112             :     {
    5113          10 :         CPLAssert(offsetsPtr->size() > static_cast<size_t>(psChild->length));
    5114             : 
    5115          10 :         auto &offsets = *offsetsPtr;
    5116          10 :         const size_t nSrcVals = offsets.size();
    5117          10 :         size_t nAccLen = 0;
    5118          33 :         for (size_t i = 0, j = 0; i < nSrcVals; ++i)
    5119             :         {
    5120          23 :             if (abyValidityFromFilters[i])
    5121             :             {
    5122           5 :                 const auto nSrcOffset = static_cast<size_t>(offsets[i]);
    5123             :                 const auto nNextOffset =
    5124           5 :                     i + 1 < nSrcVals ? offsets[i + 1] : v_source.size();
    5125           5 :                 const size_t nItemLen =
    5126             :                     static_cast<size_t>(nNextOffset - nSrcOffset);
    5127           5 :                 offsets[j] = nAccLen;
    5128          13 :                 for (size_t k = 0; k < nItemLen; ++k)
    5129             :                 {
    5130           8 :                     if (v_source[nSrcOffset + k])
    5131             :                     {
    5132           4 :                         panValues[(nAccLen + k) / 8] |=
    5133           4 :                             static_cast<uint8_t>(1 << ((nAccLen + k) % 8));
    5134             :                     }
    5135             :                 }
    5136           5 :                 ++j;
    5137           5 :                 nAccLen += nItemLen;
    5138             :             }
    5139             :         }
    5140          10 :         offsets[static_cast<size_t>(psChild->length)] = nAccLen;
    5141             : 
    5142          10 :         psValueChild->length = nAccLen;
    5143             :     }
    5144             : 
    5145          34 :     psChild->buffers[1] = offsetsPtr->data();
    5146             : 
    5147          34 :     SetNullBuffer(psChild, iField, abyValidityFromFilters);
    5148          34 : }
    5149             : 
    5150             : /************************************************************************/
    5151             : /*                        GetNextArrowArray()                           */
    5152             : /************************************************************************/
    5153             : 
    5154          91 : int OGRTileDBLayer::GetNextArrowArray(struct ArrowArrayStream *stream,
    5155             :                                       struct ArrowArray *out_array)
    5156             : {
    5157          91 :     memset(out_array, 0, sizeof(*out_array));
    5158             : 
    5159          91 :     if (m_eCurrentMode == CurrentMode::WriteInProgress)
    5160             :     {
    5161           0 :         ResetReading();
    5162             :     }
    5163          91 :     if (!m_array)
    5164           0 :         return 0;
    5165          91 :     if (m_bQueryComplete)
    5166          42 :         return 0;
    5167             : 
    5168          49 :     const size_t nBatchSizeBackup = m_nBatchSize;
    5169             :     const char *pszBatchSize =
    5170          49 :         m_aosArrowArrayStreamOptions.FetchNameValue("MAX_FEATURES_IN_BATCH");
    5171          49 :     if (pszBatchSize)
    5172           6 :         m_nBatchSize = atoi(pszBatchSize);
    5173          49 :     if (m_nBatchSize > INT_MAX - 1)
    5174           0 :         m_nBatchSize = INT_MAX - 1;
    5175          49 :     const bool bSetupOK = SetupQuery(nullptr);
    5176          49 :     m_nBatchSize = nBatchSizeBackup;
    5177          49 :     if (!bSetupOK)
    5178           0 :         return 0;
    5179             : 
    5180          49 :     const bool bIncludeFID = CPLTestBool(
    5181             :         m_aosArrowArrayStreamOptions.FetchNameValueDef("INCLUDE_FID", "YES"));
    5182             : 
    5183          49 :     int nChildren = 0;
    5184          49 :     if (bIncludeFID)
    5185             :     {
    5186          47 :         nChildren++;
    5187             :     }
    5188          49 :     const int nFieldCount = m_poFeatureDefn->GetFieldCount();
    5189         713 :     for (int i = 0; i < nFieldCount; i++)
    5190             :     {
    5191         664 :         const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
    5192         664 :         if (!poFieldDefn->IsIgnored())
    5193             :         {
    5194         662 :             nChildren++;
    5195             :         }
    5196             :     }
    5197          98 :     for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
    5198             :     {
    5199          49 :         if (!m_poFeatureDefn->GetGeomFieldDefn(i)->IsIgnored())
    5200             :         {
    5201          47 :             nChildren++;
    5202             :         }
    5203             :     }
    5204          49 :     out_array->length = m_nRowCountInResultSet;
    5205          49 :     out_array->n_children = nChildren;
    5206          49 :     out_array->children =
    5207          49 :         (struct ArrowArray **)CPLCalloc(sizeof(struct ArrowArray *), nChildren);
    5208             : 
    5209             :     // Allocate list of parent buffers: no nulls, null bitmap can be omitted
    5210          49 :     out_array->n_buffers = 1;
    5211          49 :     out_array->buffers =
    5212          49 :         static_cast<const void **>(CPLCalloc(1, sizeof(void *)));
    5213             : 
    5214             :     {
    5215             :         OGRTileDBArrowArrayPrivateData *psPrivateData =
    5216          49 :             new OGRTileDBArrowArrayPrivateData;
    5217          49 :         if (m_bArrowBatchReleased)
    5218             :         {
    5219          30 :             psPrivateData->m_poLayer = this;
    5220          30 :             psPrivateData->m_pbLayerStillAlive = m_pbLayerStillAlive;
    5221             :         }
    5222          49 :         out_array->private_data = psPrivateData;
    5223             :     }
    5224          49 :     out_array->release = OGRTileDBLayer::ReleaseArrowArray;
    5225             : 
    5226          98 :     std::vector<bool> abyValidityFromFilters;
    5227          49 :     size_t nCountIntersecting = 0;
    5228          49 :     if (!m_anGeometryOffsets->empty())
    5229             :     {
    5230             :         // Add back extra offset
    5231          45 :         m_anGeometryOffsets->push_back(m_abyGeometries->size());
    5232             : 
    5233             :         // Given that the TileDB filtering is based only on the center point
    5234             :         // of geometries, we need to refine it a bit from the actual WKB we get
    5235          45 :         if (m_poFilterGeom && (m_dfPadX > 0 || m_dfPadY > 0))
    5236             :         {
    5237          26 :             const size_t nSrcVals = static_cast<size_t>(m_nRowCountInResultSet);
    5238          26 :             abyValidityFromFilters.resize(nSrcVals);
    5239          26 :             OGREnvelope sEnvelope;
    5240          26 :             size_t nAccLen = 0;
    5241         113 :             for (size_t i = 0; i < nSrcVals; ++i)
    5242             :             {
    5243             :                 const auto nSrcOffset =
    5244          87 :                     static_cast<size_t>((*m_anGeometryOffsets)[i]);
    5245             :                 const auto nNextOffset =
    5246          87 :                     static_cast<size_t>((*m_anGeometryOffsets)[i + 1]);
    5247          87 :                 const auto nItemLen = nNextOffset - nSrcOffset;
    5248          87 :                 const GByte *pabyWKB = m_abyGeometries->data() + nSrcOffset;
    5249          87 :                 const size_t nWKBSize = nItemLen;
    5250          87 :                 if (FilterWKBGeometry(pabyWKB, nWKBSize,
    5251             :                                       /* bEnvelopeAlreadySet=*/false,
    5252             :                                       sEnvelope))
    5253             :                 {
    5254          39 :                     abyValidityFromFilters[i] = true;
    5255          39 :                     (*m_anGeometryOffsets)[nCountIntersecting] = nAccLen;
    5256          39 :                     if (nItemLen && nAccLen < nSrcOffset)
    5257             :                     {
    5258          13 :                         memmove(m_abyGeometries->data() + nAccLen,
    5259          13 :                                 m_abyGeometries->data() + nSrcOffset, nItemLen);
    5260             :                     }
    5261          39 :                     nAccLen += nItemLen;
    5262          39 :                     nCountIntersecting++;
    5263             :                 }
    5264             :             }
    5265          26 :             (*m_anGeometryOffsets)[nCountIntersecting] = nAccLen;
    5266             : 
    5267          26 :             if (nCountIntersecting == m_nRowCountInResultSet)
    5268             :             {
    5269           8 :                 abyValidityFromFilters.clear();
    5270             :             }
    5271             :             else
    5272             :             {
    5273          18 :                 out_array->length = nCountIntersecting;
    5274             :             }
    5275             :         }
    5276             :     }
    5277             : 
    5278          49 :     int iSchemaChild = 0;
    5279          49 :     if (bIncludeFID)
    5280             :     {
    5281          94 :         out_array->children[iSchemaChild] = static_cast<struct ArrowArray *>(
    5282          47 :             CPLCalloc(1, sizeof(struct ArrowArray)));
    5283          47 :         auto psChild = out_array->children[iSchemaChild];
    5284          47 :         ++iSchemaChild;
    5285             :         OGRTileDBArrowArrayPrivateData *psPrivateData =
    5286          47 :             new OGRTileDBArrowArrayPrivateData;
    5287          47 :         psPrivateData->valueHolder = m_anFIDs;
    5288          47 :         psChild->private_data = psPrivateData;
    5289          47 :         psChild->release = OGRTileDBLayer::ReleaseArrowArray;
    5290          47 :         psChild->length = out_array->length;
    5291          47 :         psChild->n_buffers = 2;
    5292          47 :         psChild->buffers =
    5293          47 :             static_cast<const void **>(CPLCalloc(2, sizeof(void *)));
    5294          47 :         if (!abyValidityFromFilters.empty())
    5295             :         {
    5296          88 :             for (size_t i = 0, j = 0; i < m_nRowCountInResultSet; ++i)
    5297             :             {
    5298          70 :                 if (abyValidityFromFilters[i])
    5299             :                 {
    5300          22 :                     (*m_anFIDs)[j] = (*m_anFIDs)[i];
    5301          22 :                     ++j;
    5302             :                 }
    5303             :             }
    5304             :         }
    5305          47 :         psChild->buffers[1] = m_anFIDs->data();
    5306             :     }
    5307             : 
    5308             :     try
    5309             :     {
    5310         713 :         for (int i = 0; i < nFieldCount; ++i)
    5311             :         {
    5312         664 :             const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
    5313         664 :             if (poFieldDefn->IsIgnored())
    5314             :             {
    5315           2 :                 continue;
    5316             :             }
    5317             : 
    5318        1324 :             out_array->children[iSchemaChild] =
    5319             :                 static_cast<struct ArrowArray *>(
    5320         662 :                     CPLCalloc(1, sizeof(struct ArrowArray)));
    5321         662 :             auto psChild = out_array->children[iSchemaChild];
    5322         662 :             ++iSchemaChild;
    5323         662 :             psChild->release = OGRTileDBLayer::ReleaseArrowArray;
    5324         662 :             psChild->length = out_array->length;
    5325         662 :             const auto eSubType = poFieldDefn->GetSubType();
    5326         662 :             switch (poFieldDefn->GetType())
    5327             :             {
    5328         204 :                 case OFTInteger:
    5329             :                 {
    5330         204 :                     if (m_aeFieldTypes[i] == TILEDB_BOOL)
    5331             :                     {
    5332          34 :                         FillBoolArray(psChild, i, abyValidityFromFilters);
    5333             :                     }
    5334         170 :                     else if (m_aeFieldTypes[i] == TILEDB_INT16)
    5335             :                     {
    5336          34 :                         FillPrimitiveArray<int16_t>(psChild, i,
    5337             :                                                     abyValidityFromFilters);
    5338             :                     }
    5339         136 :                     else if (m_aeFieldTypes[i] == TILEDB_INT32)
    5340             :                     {
    5341          68 :                         FillPrimitiveArray<int32_t>(psChild, i,
    5342             :                                                     abyValidityFromFilters);
    5343             :                     }
    5344          68 :                     else if (m_aeFieldTypes[i] == TILEDB_UINT8)
    5345             :                     {
    5346          34 :                         FillPrimitiveArray<uint8_t>(psChild, i,
    5347             :                                                     abyValidityFromFilters);
    5348             :                     }
    5349          34 :                     else if (m_aeFieldTypes[i] == TILEDB_UINT16)
    5350             :                     {
    5351          34 :                         FillPrimitiveArray<uint16_t>(psChild, i,
    5352             :                                                      abyValidityFromFilters);
    5353             :                     }
    5354             :                     else
    5355             :                     {
    5356           0 :                         CPLAssert(false);
    5357             :                     }
    5358         204 :                     break;
    5359             :                 }
    5360             : 
    5361         102 :                 case OFTIntegerList:
    5362             :                 {
    5363         102 :                     if (m_aeFieldTypes[i] == TILEDB_BOOL)
    5364             :                     {
    5365          34 :                         FillBoolListArray(psChild, i, abyValidityFromFilters);
    5366             :                     }
    5367          68 :                     else if (m_aeFieldTypes[i] == TILEDB_INT16)
    5368             :                     {
    5369          34 :                         FillPrimitiveListArray<int16_t>(psChild, i,
    5370             :                                                         abyValidityFromFilters);
    5371             :                     }
    5372          34 :                     else if (m_aeFieldTypes[i] == TILEDB_INT32)
    5373             :                     {
    5374          34 :                         FillPrimitiveListArray<int32_t>(psChild, i,
    5375             :                                                         abyValidityFromFilters);
    5376             :                     }
    5377           0 :                     else if (m_aeFieldTypes[i] == TILEDB_UINT8)
    5378             :                     {
    5379           0 :                         FillPrimitiveListArray<uint8_t>(psChild, i,
    5380             :                                                         abyValidityFromFilters);
    5381             :                     }
    5382           0 :                     else if (m_aeFieldTypes[i] == TILEDB_UINT16)
    5383             :                     {
    5384           0 :                         FillPrimitiveListArray<uint16_t>(
    5385             :                             psChild, i, abyValidityFromFilters);
    5386             :                     }
    5387             :                     else
    5388             :                     {
    5389           0 :                         CPLAssert(false);
    5390             :                     }
    5391         102 :                     break;
    5392             :                 }
    5393             : 
    5394          74 :                 case OFTInteger64:
    5395             :                 case OFTDateTime:
    5396             :                 {
    5397          74 :                     FillPrimitiveArray<int64_t>(psChild, i,
    5398             :                                                 abyValidityFromFilters);
    5399          74 :                     break;
    5400             :                 }
    5401             : 
    5402           0 :                 case OFTInteger64List:
    5403             :                 {
    5404           0 :                     FillPrimitiveListArray<int64_t>(psChild, i,
    5405             :                                                     abyValidityFromFilters);
    5406           0 :                     break;
    5407             :                 }
    5408             : 
    5409          74 :                 case OFTReal:
    5410             :                 {
    5411          74 :                     if (eSubType == OFSTFloat32)
    5412             :                     {
    5413          34 :                         FillPrimitiveArray<float>(psChild, i,
    5414             :                                                   abyValidityFromFilters);
    5415             :                     }
    5416             :                     else
    5417             :                     {
    5418          40 :                         FillPrimitiveArray<double>(psChild, i,
    5419             :                                                    abyValidityFromFilters);
    5420             :                     }
    5421          74 :                     break;
    5422             :                 }
    5423             : 
    5424          68 :                 case OFTRealList:
    5425             :                 {
    5426          68 :                     if (eSubType == OFSTFloat32)
    5427             :                     {
    5428          34 :                         FillPrimitiveListArray<float>(psChild, i,
    5429             :                                                       abyValidityFromFilters);
    5430             :                     }
    5431             :                     else
    5432             :                     {
    5433          34 :                         FillPrimitiveListArray<double>(psChild, i,
    5434             :                                                        abyValidityFromFilters);
    5435             :                     }
    5436          68 :                     break;
    5437             :                 }
    5438             : 
    5439          38 :                 case OFTString:
    5440             :                 {
    5441          38 :                     FillStringOrBinaryArray<std::string>(
    5442             :                         psChild, i, abyValidityFromFilters);
    5443          38 :                     break;
    5444             :                 }
    5445             : 
    5446          34 :                 case OFTBinary:
    5447             :                 {
    5448          34 :                     FillStringOrBinaryArray<std::vector<uint8_t>>(
    5449             :                         psChild, i, abyValidityFromFilters);
    5450          34 :                     break;
    5451             :                 }
    5452             : 
    5453          68 :                 case OFTTime:
    5454             :                 case OFTDate:
    5455             :                 {
    5456          68 :                     FillTimeOrDateArray(psChild, i, abyValidityFromFilters);
    5457          68 :                     break;
    5458             :                 }
    5459             : 
    5460           0 :                 case OFTStringList:
    5461             :                 case OFTWideString:
    5462             :                 case OFTWideStringList:
    5463           0 :                     break;
    5464             :             }
    5465             :         }
    5466             : 
    5467          49 :         if (!m_poFeatureDefn->GetGeomFieldDefn(0)->IsIgnored())
    5468             :         {
    5469          94 :             out_array->children[iSchemaChild] =
    5470             :                 static_cast<struct ArrowArray *>(
    5471          47 :                     CPLCalloc(1, sizeof(struct ArrowArray)));
    5472          47 :             auto psChild = out_array->children[iSchemaChild];
    5473          47 :             ++iSchemaChild;
    5474          47 :             psChild->release = OGRTileDBLayer::ReleaseArrowArray;
    5475          47 :             psChild->length = out_array->length;
    5476             : 
    5477             :             OGRTileDBArrowArrayPrivateData *psPrivateData =
    5478          47 :                 new OGRTileDBArrowArrayPrivateData;
    5479          47 :             psChild->private_data = psPrivateData;
    5480             : 
    5481          47 :             psChild->n_buffers = 3;
    5482          47 :             psChild->buffers =
    5483          47 :                 static_cast<const void **>(CPLCalloc(3, sizeof(void *)));
    5484             : 
    5485          47 :             if (!m_anGeometryOffsets->empty() || m_adfXs->empty())
    5486             :             {
    5487          45 :                 psPrivateData->offsetHolder = m_anGeometryOffsets;
    5488          45 :                 psChild->buffers[1] = m_anGeometryOffsets->data();
    5489             : 
    5490          45 :                 psPrivateData->valueHolder = m_abyGeometries;
    5491          45 :                 psChild->buffers[2] = m_abyGeometries->data();
    5492             :             }
    5493             :             else
    5494             :             {
    5495             :                 // Build Point WKB from X/Y/Z arrays
    5496             : 
    5497           2 :                 const int nDims = m_osZDim.empty() ? 2 : 3;
    5498           2 :                 const size_t nPointWKBSize = 5 + nDims * sizeof(double);
    5499             : 
    5500           4 :                 auto offsets = std::make_shared<std::vector<uint64_t>>();
    5501           2 :                 psPrivateData->offsetHolder = offsets;
    5502           2 :                 offsets->reserve(m_adfXs->size());
    5503             : 
    5504           2 :                 auto pabyWKB = std::make_shared<std::vector<unsigned char>>();
    5505           2 :                 pabyWKB->reserve(nPointWKBSize * m_adfXs->size());
    5506           2 :                 psPrivateData->valueHolder = pabyWKB;
    5507             : 
    5508             :                 unsigned char wkbHeader[5];
    5509           2 :                 wkbHeader[0] = static_cast<unsigned char>(wkbNDR);
    5510           2 :                 uint32_t wkbType = wkbPoint + ((nDims == 3) ? 1000 : 0);
    5511           2 :                 CPL_LSBPTR32(&wkbType);
    5512           2 :                 memcpy(wkbHeader + 1, &wkbType, sizeof(uint32_t));
    5513           2 :                 double *padfX = m_adfXs->data();
    5514           2 :                 double *padfY = m_adfYs->data();
    5515           2 :                 double *padfZ = m_adfZs->data();
    5516           2 :                 uint64_t nOffset = 0;
    5517           6 :                 for (size_t i = 0; i < m_adfXs->size(); ++i)
    5518             :                 {
    5519           4 :                     pabyWKB->insert(pabyWKB->end(), wkbHeader,
    5520           8 :                                     wkbHeader + sizeof(wkbHeader));
    5521           4 :                     unsigned char *x =
    5522           4 :                         reinterpret_cast<unsigned char *>(&padfX[i]);
    5523           4 :                     CPL_LSBPTR64(x);
    5524           4 :                     pabyWKB->insert(pabyWKB->end(), x, x + sizeof(double));
    5525           4 :                     CPL_LSBPTR64(x);
    5526           4 :                     unsigned char *y =
    5527           4 :                         reinterpret_cast<unsigned char *>(&padfY[i]);
    5528           4 :                     CPL_LSBPTR64(y);
    5529           4 :                     pabyWKB->insert(pabyWKB->end(), y, y + sizeof(double));
    5530           4 :                     CPL_LSBPTR64(y);
    5531           4 :                     if (nDims == 3)
    5532             :                     {
    5533           2 :                         unsigned char *z =
    5534           2 :                             reinterpret_cast<unsigned char *>(&padfZ[i]);
    5535           2 :                         CPL_LSBPTR64(z);
    5536           2 :                         pabyWKB->insert(pabyWKB->end(), z, z + sizeof(double));
    5537           2 :                         CPL_LSBPTR64(z);
    5538             :                     }
    5539           4 :                     offsets->push_back(nOffset);
    5540           4 :                     nOffset += nPointWKBSize;
    5541             :                 }
    5542           2 :                 offsets->push_back(nOffset);
    5543             : 
    5544           2 :                 psChild->buffers[1] = offsets->data();
    5545           2 :                 psChild->buffers[2] = pabyWKB->data();
    5546             :             }
    5547             :         }
    5548          49 :         CPL_IGNORE_RET_VAL(iSchemaChild);
    5549             : 
    5550          53 :         if (m_poAttrQuery &&
    5551           4 :             (!m_poQueryCondition || m_bAttributeFilterPartiallyTranslated))
    5552             :         {
    5553             :             struct ArrowSchema schema;
    5554           2 :             stream->get_schema(stream, &schema);
    5555           2 :             CPLAssert(schema.release != nullptr);
    5556           2 :             CPLAssert(schema.n_children == out_array->n_children);
    5557             :             // Spatial filter already evaluated
    5558           2 :             auto poFilterGeomBackup = m_poFilterGeom;
    5559           2 :             m_poFilterGeom = nullptr;
    5560           2 :             if (CanPostFilterArrowArray(&schema))
    5561           2 :                 PostFilterArrowArray(&schema, out_array, nullptr);
    5562           2 :             schema.release(&schema);
    5563           2 :             m_poFilterGeom = poFilterGeomBackup;
    5564             :         }
    5565             :     }
    5566           0 :     catch (const std::exception &e)
    5567             :     {
    5568           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    5569           0 :         out_array->release(out_array);
    5570           0 :         memset(out_array, 0, sizeof(*out_array));
    5571           0 :         return ENOMEM;
    5572             :     }
    5573             : 
    5574          49 :     m_bArrowBatchReleased = false;
    5575             : 
    5576          49 :     return 0;
    5577             : }

Generated by: LCOV version 1.14