LCOV - code coverage report
Current view: top level - frmts/tiledb - tiledbsparse.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2473 2823 87.6 %
Date: 2026-06-19 21:24:00 Functions: 85 90 94.4 %

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

Generated by: LCOV version 1.14