LCOV - code coverage report
Current view: top level - frmts/tiledb - tiledbsparse.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2471 2821 87.6 %
Date: 2024-11-21 22:18:42 Functions: 85 90 94.4 %

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

Generated by: LCOV version 1.14