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

Generated by: LCOV version 1.14