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

Generated by: LCOV version 1.14