Coverage Report

Created: 2026-08-14 09:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/adbc/ogradbcbigquerylayer.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  Arrow Database Connectivity driver
5
 * Author:   Even Rouault, <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
9
  *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "ogr_adbc.h"
14
#include "ogr_p.h"
15
16
#include <algorithm>
17
18
/************************************************************************/
19
/*                        OGRADBCBigQueryLayer()                        */
20
/************************************************************************/
21
22
OGRADBCBigQueryLayer::OGRADBCBigQueryLayer(OGRADBCDataset *poDS,
23
                                           const char *pszName,
24
                                           const std::string &osStatement,
25
                                           bool bInternalUse)
26
0
    : OGRADBCLayer(poDS, pszName, osStatement, bInternalUse)
27
0
{
28
0
}
29
30
/************************************************************************/
31
/*                    GetBigQueryDatasetAndTableId()                    */
32
/************************************************************************/
33
34
bool OGRADBCBigQueryLayer::GetBigQueryDatasetAndTableId(
35
    std::string &osDatasetId, std::string &osTableId) const
36
0
{
37
0
    auto nPos = CPLString(m_osBaseStatement).ifind(" FROM ");
38
0
    if (nPos != std::string::npos)
39
0
    {
40
0
        nPos += strlen(" FROM ");
41
0
        const auto nPos2 = m_osBaseStatement.find(' ', nPos);
42
0
        const std::string osTableName =
43
0
            (nPos2 != std::string::npos)
44
0
                ? m_osBaseStatement.substr(nPos, nPos2 - nPos)
45
0
                : m_osBaseStatement.substr(nPos);
46
47
0
        const auto nPosDot = osTableName.find('.');
48
0
        if (nPosDot != std::string::npos)
49
0
        {
50
0
            osDatasetId = osTableName.substr(0, nPosDot);
51
0
            osTableId = osTableName.substr(nPosDot + 1);
52
0
            if (osDatasetId.size() > 2 && osDatasetId[0] == '`' &&
53
0
                osDatasetId.back() == '`')
54
0
                osDatasetId = osDatasetId.substr(1, osDatasetId.size() - 2);
55
0
            if (osTableId.size() > 2 && osTableId[0] == '`' &&
56
0
                osTableId.back() == '`')
57
0
                osTableId = osTableId.substr(1, osTableId.size() - 2);
58
0
            return true;
59
0
        }
60
0
    }
61
0
    return false;
62
0
}
63
64
/************************************************************************/
65
/*                           BuildLayerDefn()                           */
66
/************************************************************************/
67
68
void OGRADBCBigQueryLayer::BuildLayerDefn()
69
0
{
70
0
    if (!BuildLayerDefnInit(true))
71
0
        return;
72
73
0
    std::map<std::string, std::unique_ptr<OGRSpatialReference>> oMapGeomColumns;
74
0
    std::map<std::string, bool> oMapIsNullable;
75
76
0
    const bool bIsLikelyTableExtract =
77
0
        !m_bInternalUse &&
78
0
        STARTS_WITH_CI(m_osBaseStatement.c_str(), "SELECT ") &&
79
0
        !STARTS_WITH_CI(m_osBaseStatement.c_str(), "SELECT COUNT(");
80
0
    if (bIsLikelyTableExtract)
81
0
    {
82
0
        std::string osDatasetId;
83
0
        std::string osTableId;
84
0
        if (GetBigQueryDatasetAndTableId(osDatasetId, osTableId))
85
0
        {
86
0
            auto poColumnList = m_poDS->CreateInternalLayer(CPLSPrintf(
87
0
                "SELECT c.column_name, c.data_type, c.is_nullable, "
88
0
                "keys.ordinal_position AS key_ordinal_position, "
89
0
                "keys.position_in_unique_constraint FROM "
90
0
                "`%s`.INFORMATION_SCHEMA.COLUMNS c "
91
0
                "LEFT JOIN `%s`.INFORMATION_SCHEMA.KEY_COLUMN_USAGE keys ON "
92
0
                "c.table_schema = keys.table_schema AND "
93
0
                "c.table_name = keys.table_name AND "
94
0
                "c.column_name = keys.column_name "
95
0
                "WHERE c.table_name='%s' AND c.is_hidden = 'NO' "
96
0
                "ORDER BY c.ordinal_position",
97
0
                OGRDuplicateCharacter(osDatasetId.c_str(), '`').c_str(),
98
0
                OGRDuplicateCharacter(osDatasetId.c_str(), '`').c_str(),
99
0
                OGRDuplicateCharacter(osTableId.c_str(), '\'').c_str()));
100
0
            if (poColumnList->GetLayerDefn()->GetFieldCount() == 5)
101
0
            {
102
0
                for (auto &&f : *poColumnList)
103
0
                {
104
0
                    constexpr int IDX_COL_NAME = 0;
105
0
                    constexpr int IDX_DATA_TYPE = 1;
106
0
                    constexpr int IDX_IS_NULLABLE = 2;
107
0
                    constexpr int IDX_KEY_ORDINAL_POSITION = 3;
108
0
                    constexpr int IDX_POSITION_IN_UNIQUE_CONSTRAINT = 4;
109
0
                    const char *pszColName = f->GetFieldAsString(IDX_COL_NAME);
110
0
                    const char *pszColType = f->GetFieldAsString(IDX_DATA_TYPE);
111
0
                    if (EQUAL(pszColType, "GEOGRAPHY"))
112
0
                    {
113
0
                        auto poSRS = std::make_unique<OGRSpatialReference>();
114
0
                        poSRS->SetAxisMappingStrategy(
115
0
                            OAMS_TRADITIONAL_GIS_ORDER);
116
0
                        poSRS->importFromEPSG(4326);
117
0
                        oMapGeomColumns[pszColName] = std::move(poSRS);
118
0
                    }
119
0
                    oMapIsNullable[pszColName] =
120
0
                        EQUAL(f->GetFieldAsString(IDX_IS_NULLABLE), "YES");
121
0
                    if (f->IsFieldNull(IDX_POSITION_IN_UNIQUE_CONSTRAINT) &&
122
0
                        !f->IsFieldNull(IDX_KEY_ORDINAL_POSITION))
123
0
                    {
124
0
                        if (EQUAL(pszColType, "INT64") &&
125
0
                            f->GetFieldAsInteger64(IDX_KEY_ORDINAL_POSITION) ==
126
0
                                1 &&
127
0
                            m_osFIDColName.empty())
128
0
                        {
129
0
                            m_osFIDColName = pszColName;
130
0
                        }
131
0
                        else
132
0
                        {
133
0
                            m_osFIDColName.clear();
134
0
                        }
135
0
                    }
136
0
                }
137
138
0
                if (!oMapGeomColumns.empty())
139
0
                {
140
0
                    std::string osNewStatement = "SELECT ";
141
0
                    for (int i = 0; i < m_schema.n_children; ++i)
142
0
                    {
143
0
                        if (i > 0)
144
0
                            osNewStatement += ", ";
145
0
                        const char *pszColName = m_schema.children[i]->name;
146
0
                        auto oIter = oMapGeomColumns.find(pszColName);
147
0
                        if (oIter != oMapGeomColumns.end())
148
0
                        {
149
0
                            osNewStatement += "ST_AsBinary(`";
150
0
                            osNewStatement +=
151
0
                                OGRDuplicateCharacter(pszColName, '`');
152
0
                            osNewStatement += "`) AS ";
153
0
                        }
154
0
                        osNewStatement += '`';
155
0
                        osNewStatement +=
156
0
                            OGRDuplicateCharacter(pszColName, '`');
157
0
                        osNewStatement += '`';
158
0
                    }
159
0
                    m_osModifiedSelect = osNewStatement;
160
0
                    osNewStatement += " FROM (";
161
0
                    osNewStatement += m_osBaseStatement;
162
0
                    osNewStatement += " )";
163
164
#ifdef DEBUG_VEBOSE
165
                    CPLDebug("ADBC", "%s -> %s", m_osBaseStatement.c_str(),
166
                             osNewStatement.c_str());
167
#endif
168
169
0
                    CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
170
0
                    if (ReplaceStatement(osNewStatement.c_str()))
171
0
                    {
172
0
                        m_osModifiedBaseStatement = std::move(osNewStatement);
173
0
                    }
174
0
                    else
175
0
                    {
176
0
                        m_osModifiedSelect.clear();
177
0
                        oMapGeomColumns.clear();
178
0
                    }
179
0
                }
180
0
            }
181
0
        }
182
0
    }
183
184
0
    auto poLayerDefn = m_poAdapterLayer->GetLayerDefn();
185
186
0
    for (int i = 0; i < m_schema.n_children; ++i)
187
0
    {
188
0
        const char *pszColName = m_schema.children[i]->name;
189
0
        auto oIter = oMapGeomColumns.find(pszColName);
190
0
        if (oIter != oMapGeomColumns.end())
191
0
        {
192
0
            OGRGeomFieldDefn oGeomFieldDefn(pszColName, wkbUnknown);
193
0
            auto poSRS = std::move(oIter->second).release();
194
0
            if (poSRS)
195
0
            {
196
0
                oGeomFieldDefn.SetSpatialRef(poSRS);
197
0
                poSRS->Release();
198
0
            }
199
0
            poLayerDefn->AddGeomFieldDefn(&oGeomFieldDefn);
200
0
        }
201
0
        else
202
0
        {
203
0
            m_poAdapterLayer->CreateFieldFromArrowSchema(m_schema.children[i]);
204
0
        }
205
0
    }
206
207
0
    if (bIsLikelyTableExtract)
208
0
    {
209
0
        for (int i = 0; i < poLayerDefn->GetFieldCount(); ++i)
210
0
        {
211
0
            auto poFldDefn = poLayerDefn->GetFieldDefn(i);
212
0
            auto oIter = oMapIsNullable.find(poFldDefn->GetNameRef());
213
0
            if (oIter != oMapIsNullable.end())
214
0
                poFldDefn->SetNullable(oIter->second);
215
0
        }
216
0
        for (int i = 0; i < poLayerDefn->GetGeomFieldCount(); ++i)
217
0
        {
218
0
            auto poGFldDefn = poLayerDefn->GetGeomFieldDefn(i);
219
0
            std::string osSQL = "SELECT DISTINCT ST_GeometryType(`";
220
0
            osSQL += OGRDuplicateCharacter(poGFldDefn->GetNameRef(), '`');
221
0
            osSQL += "`) FROM (";
222
0
            osSQL += m_osBaseStatement;
223
0
            osSQL += ')';
224
0
            auto poGeomTypeList = m_poDS->CreateInternalLayer(osSQL.c_str());
225
0
            if (poGeomTypeList->GetLayerDefn()->GetFieldCount() == 1)
226
0
            {
227
0
                std::string osType;
228
0
                for (auto &&f : *poGeomTypeList)
229
0
                {
230
0
                    if (osType.empty())
231
0
                    {
232
0
                        osType = f->GetFieldAsString(0);
233
0
                    }
234
0
                    else
235
0
                    {
236
0
                        osType.clear();
237
0
                        break;
238
0
                    }
239
0
                }
240
0
                if (STARTS_WITH_CI(osType.c_str(), "ST_"))
241
0
                {
242
0
                    poGFldDefn->SetType(
243
0
                        OGRFromOGCGeomType(osType.c_str() + strlen("ST_")));
244
0
                }
245
0
            }
246
0
            auto oIter = oMapIsNullable.find(poGFldDefn->GetNameRef());
247
0
            if (oIter != oMapIsNullable.end())
248
0
                poGFldDefn->SetNullable(oIter->second);
249
0
        }
250
0
    }
251
0
}
252
253
/************************************************************************/
254
/*                         SetAttributeFilter()                         */
255
/************************************************************************/
256
257
OGRErr OGRADBCBigQueryLayer::SetAttributeFilter(const char *pszFilter)
258
0
{
259
0
    if (!m_osModifiedSelect.empty())
260
0
    {
261
0
        m_osAttributeFilter = pszFilter ? pszFilter : "";
262
0
        return UpdateStatement() ? OGRERR_NONE : OGRERR_FAILURE;
263
0
    }
264
0
    else
265
0
    {
266
0
        return OGRLayer::SetAttributeFilter(pszFilter);
267
0
    }
268
0
}
269
270
/************************************************************************/
271
/*                          GetFeatureCount()                           */
272
/************************************************************************/
273
274
GIntBig OGRADBCBigQueryLayer::GetFeatureCount(int /*bForce*/)
275
0
{
276
0
    if (!m_poAdapterLayer)
277
0
        BuildLayerDefn();
278
0
    if (m_bLayerDefinitionError)
279
0
        return 0;
280
281
0
    auto nCount = GetFeatureCountSelectCountStar();
282
0
    if (nCount >= 0)
283
0
        return nCount;
284
285
0
    return GetFeatureCountArrow();
286
0
}
287
288
/************************************************************************/
289
/*                           TestCapability()                           */
290
/************************************************************************/
291
292
int OGRADBCBigQueryLayer::TestCapability(const char *pszCap) const
293
0
{
294
0
    if (!m_poAdapterLayer)
295
0
        const_cast<OGRADBCBigQueryLayer *>(this)->BuildLayerDefn();
296
297
0
    if (EQUAL(pszCap, OLCSequentialWrite) || EQUAL(pszCap, OLCCreateField))
298
0
        return m_poDS->GetAccess() == GA_Update;
299
300
0
    if (EQUAL(pszCap, OLCRandomWrite) || EQUAL(pszCap, OLCDeleteFeature))
301
0
        return m_poDS->GetAccess() == GA_Update && !m_osFIDColName.empty();
302
303
0
    return OGRADBCLayer::TestCapability(pszCap);
304
0
}
305
306
/************************************************************************/
307
/*                             IGetExtent()                             */
308
/************************************************************************/
309
310
OGRErr OGRADBCBigQueryLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
311
                                        bool bForce)
312
0
{
313
0
    if (!m_poAdapterLayer)
314
0
        BuildLayerDefn();
315
316
0
    const char *pszGeomColName =
317
0
        GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetNameRef();
318
0
    std::string osSQL = "SELECT ST_Extent(`";
319
0
    osSQL += OGRDuplicateCharacter(pszGeomColName, '`');
320
0
    osSQL += "`) FROM (";
321
0
    osSQL += m_osBaseStatement;
322
0
    osSQL += ")";
323
0
    auto poExtentLayer = m_poDS->CreateInternalLayer(osSQL.c_str());
324
0
    if (poExtentLayer->GetLayerDefn()->GetFieldCount() == 4)
325
0
    {
326
0
        auto f = std::unique_ptr<OGRFeature>(poExtentLayer->GetNextFeature());
327
0
        if (f && f->IsFieldSetAndNotNull(0))
328
0
        {
329
0
            psExtent->MinX = f->GetFieldAsDouble(0);
330
0
            psExtent->MinY = f->GetFieldAsDouble(1);
331
0
            psExtent->MaxX = f->GetFieldAsDouble(2);
332
0
            psExtent->MaxY = f->GetFieldAsDouble(3);
333
0
            return OGRERR_NONE;
334
0
        }
335
0
        else
336
0
            return OGRERR_FAILURE;
337
0
    }
338
339
0
    return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
340
0
}
341
342
/************************************************************************/
343
/*                        GetCurrentStatement()                         */
344
/************************************************************************/
345
346
std::string OGRADBCBigQueryLayer::GetCurrentStatement() const
347
0
{
348
0
    if (!m_osAttributeFilter.empty() || m_poFilterGeom)
349
0
    {
350
0
        std::string osStatement(m_osModifiedSelect);
351
0
        osStatement.append(" FROM (")
352
0
            .append(m_osBaseStatement)
353
0
            .append(") WHERE ");
354
0
        if (m_poFilterGeom)
355
0
        {
356
0
            if (m_sFilterEnvelope.MinX > 180 || m_sFilterEnvelope.MinY > 90 ||
357
0
                m_sFilterEnvelope.MaxX < -180 || m_sFilterEnvelope.MaxY < -90)
358
0
            {
359
0
                osStatement.append(" FALSE");
360
0
                return osStatement;
361
0
            }
362
0
            constexpr double EPSILON = 1e-8;
363
0
            const double dfMinX =
364
0
                std::max(-180.0, m_sFilterEnvelope.MinX - EPSILON);
365
0
            const double dfMinY =
366
0
                std::max(-90.0, m_sFilterEnvelope.MinY - EPSILON);
367
0
            const double dfMaxX =
368
0
                std::min(180.0, m_sFilterEnvelope.MaxX + EPSILON);
369
0
            const double dfMaxY =
370
0
                std::min(90.0, m_sFilterEnvelope.MaxY + EPSILON);
371
0
            const char *pszGeomColName =
372
0
                m_poAdapterLayer->GetLayerDefn()
373
0
                    ->GetGeomFieldDefn(m_iGeomFieldFilter)
374
0
                    ->GetNameRef();
375
0
            osStatement +=
376
0
                CPLSPrintf("ST_IntersectsBox(`%s`,%.17g,%.17g,%.17g,%.17g)",
377
0
                           OGRDuplicateCharacter(pszGeomColName, '`').c_str(),
378
0
                           dfMinX, dfMinY, dfMaxX, dfMaxY);
379
0
        }
380
0
        if (!m_osAttributeFilter.empty())
381
0
        {
382
0
            if (m_poFilterGeom)
383
0
                osStatement.append(" AND ");
384
0
            osStatement.append("(");
385
0
            osStatement.append(m_osAttributeFilter);
386
0
            osStatement.append(")");
387
0
        }
388
389
#ifdef DEBUG_VEBOSE
390
        CPLDebug("ADBC", "%s", osStatement.c_str());
391
#endif
392
393
0
        return osStatement;
394
0
    }
395
0
    else
396
0
    {
397
0
        return m_osModifiedBaseStatement;
398
0
    }
399
0
}
400
401
/************************************************************************/
402
/*                             GetSQLType()                             */
403
/************************************************************************/
404
405
static std::string GetSQLType(const OGRFieldDefn *poField)
406
0
{
407
0
    switch (poField->GetType())
408
0
    {
409
0
        case OFTInteger:
410
0
            return poField->GetSubType() == OFSTBoolean ? "BOOLEAN" : "INTEGER";
411
0
        case OFTInteger64:
412
0
            return "INT64";
413
0
        case OFTReal:
414
0
            return "FLOAT64";
415
0
        case OFTDate:
416
0
            return "DATE";
417
0
        case OFTTime:
418
0
            return "TIME";
419
0
        case OFTDateTime:
420
0
            return "TIMESTAMP";
421
0
        case OFTString:
422
0
            return poField->GetSubType() == OFSTJSON ? "JSON" : "STRING";
423
0
        case OFTBinary:
424
0
            return "BYTES";
425
0
        case OFTStringList:
426
0
            return "ARRAY<STRING>";
427
0
        case OFTRealList:
428
0
            return "ARRAY<FLOAT64>";
429
0
        case OFTIntegerList:
430
0
            return "ARRAY<INTEGER>";
431
0
        case OFTInteger64List:
432
0
            return "ARRAY<INT64>";
433
0
        case OFTWideString:
434
0
        case OFTWideStringList:
435
0
            CPLError(CE_Failure, CPLE_NotSupported, "Unsupported type");
436
0
            break;
437
0
    }
438
0
    return std::string();
439
0
}
440
441
/************************************************************************/
442
/*                            CreateField()                             */
443
/************************************************************************/
444
445
OGRErr OGRADBCBigQueryLayer::CreateField(const OGRFieldDefn *poField,
446
                                         int /*bApproxOK*/)
447
0
{
448
0
    if (m_poDS->GetAccess() != GA_Update)
449
0
    {
450
0
        CPLError(
451
0
            CE_Failure, CPLE_NotSupported,
452
0
            "CreateField() only supported on datasets opened in update mode");
453
0
        return OGRERR_FAILURE;
454
0
    }
455
0
    if (!m_poAdapterLayer)
456
0
        BuildLayerDefn();
457
0
    if (m_bLayerDefinitionError)
458
0
        return OGRERR_FAILURE;
459
460
0
    if (GetLayerDefn()->GetFieldIndex(poField->GetNameRef()) >= 0)
461
0
    {
462
0
        CPLError(CE_Failure, CPLE_AppDefined, "Field '%s' already exists.",
463
0
                 poField->GetNameRef());
464
0
        return OGRERR_FAILURE;
465
0
    }
466
467
0
    const auto osSQLType = GetSQLType(poField);
468
0
    if (osSQLType.empty())
469
0
        return OGRERR_FAILURE;
470
471
0
    if (!m_bDeferredCreation)
472
0
    {
473
0
        std::string osDatasetId, osTableId;
474
0
        if (!GetBigQueryDatasetAndTableId(osDatasetId, osTableId))
475
0
        {
476
0
            CPLError(CE_Failure, CPLE_NotSupported,
477
0
                     "CreateField(): cannot get dataset and table ID");
478
0
            return OGRERR_FAILURE;
479
0
        }
480
481
0
        std::string osSQL = "ALTER TABLE `";
482
0
        osSQL += OGRDuplicateCharacter(osDatasetId.c_str(), '`');
483
0
        osSQL += "`.`";
484
0
        osSQL += OGRDuplicateCharacter(osTableId.c_str(), '`');
485
0
        osSQL += "` ADD COLUMN `";
486
0
        osSQL += OGRDuplicateCharacter(poField->GetNameRef(), '`');
487
0
        osSQL += "` ";
488
0
        osSQL += osSQLType;
489
0
        if (m_poDS->CreateInternalLayer(osSQL.c_str())->GotError())
490
0
            return OGRERR_FAILURE;
491
0
    }
492
493
0
    return m_poAdapterLayer->CreateField(poField, false);
494
0
}
495
496
/************************************************************************/
497
/*                           GetFieldValue()                            */
498
/************************************************************************/
499
500
static std::string GetFieldValue(const OGRFieldDefn *poFldDefn,
501
                                 OGRFeature *poFeature, int iField)
502
0
{
503
0
    std::string osVal;
504
505
0
    if (poFeature->IsFieldNull(iField))
506
0
        osVal = "NULL";
507
508
0
    else if (poFldDefn->GetType() == OFTInteger ||
509
0
             poFldDefn->GetType() == OFTInteger64)
510
0
    {
511
0
        const auto nVal = poFeature->GetFieldAsInteger64(iField);
512
0
        if (poFldDefn->GetSubType() == OFSTBoolean)
513
0
            osVal = nVal ? "TRUE" : "FALSE";
514
0
        else
515
0
            osVal = std::to_string(nVal);
516
0
    }
517
0
    else if (poFldDefn->GetType() == OFTReal)
518
0
    {
519
0
        osVal = CPLSPrintf("%.17g", poFeature->GetFieldAsDouble(iField));
520
0
    }
521
0
    else if (poFldDefn->GetType() == OFTDate)
522
0
    {
523
0
        char szTmpFieldValue[OGR_SIZEOF_ISO8601_DATETIME_BUFFER];
524
0
        constexpr bool bAlwaysMillisecond = false;
525
0
        OGRGetISO8601DateTime(poFeature->GetRawFieldRef(iField),
526
0
                              bAlwaysMillisecond, szTmpFieldValue);
527
0
        szTmpFieldValue[strlen("YYYY-MM-DD")] = 0;
528
0
        osVal += "DATE \'";
529
0
        osVal += szTmpFieldValue;
530
0
        osVal += '\'';
531
0
    }
532
0
    else if (poFldDefn->GetType() == OFTDateTime)
533
0
    {
534
0
        osVal += '\'';
535
0
        osVal += poFeature->GetFieldAsISO8601DateTime(iField, nullptr);
536
0
        osVal += '\'';
537
0
    }
538
0
    else if (poFldDefn->GetType() == OFTBinary)
539
0
    {
540
0
        osVal += "b'";
541
0
        int nCount = 0;
542
0
        GByte *pabyVal = poFeature->GetFieldAsBinary(iField, &nCount);
543
0
        osVal.reserve(nCount * 4 + 4);
544
0
        for (int i = 0; i < nCount; ++i)
545
0
        {
546
0
            osVal += CPLSPrintf("\\x%02X", pabyVal[i]);
547
0
        }
548
0
        osVal += '\'';
549
0
    }
550
0
    else if (poFldDefn->GetType() == OFTStringList)
551
0
    {
552
0
        CSLConstList papszStr = poFeature->GetFieldAsStringList(iField);
553
0
        osVal += '[';
554
0
        for (int i = 0; papszStr && papszStr[i]; ++i)
555
0
        {
556
0
            if (i > 0)
557
0
                osVal += ',';
558
0
            osVal += '\'';
559
0
            osVal += OGRDuplicateCharacter(papszStr[i], '\'');
560
0
            osVal += '\'';
561
0
        }
562
0
        osVal += ']';
563
0
    }
564
0
    else if (poFldDefn->GetType() == OFTIntegerList)
565
0
    {
566
0
        int nCount = 0;
567
0
        const int *panVals = poFeature->GetFieldAsIntegerList(iField, &nCount);
568
0
        osVal += '[';
569
0
        for (int i = 0; i < nCount; ++i)
570
0
        {
571
0
            if (i > 0)
572
0
                osVal += ',';
573
0
            osVal += std::to_string(panVals[i]);
574
0
        }
575
0
        osVal += ']';
576
0
    }
577
0
    else if (poFldDefn->GetType() == OFTInteger64List)
578
0
    {
579
0
        int nCount = 0;
580
0
        const auto *panVals =
581
0
            poFeature->GetFieldAsInteger64List(iField, &nCount);
582
0
        osVal += '[';
583
0
        for (int i = 0; i < nCount; ++i)
584
0
        {
585
0
            if (i > 0)
586
0
                osVal += ',';
587
0
            osVal += std::to_string(panVals[i]);
588
0
        }
589
0
        osVal += ']';
590
0
    }
591
0
    else if (poFldDefn->GetType() == OFTRealList)
592
0
    {
593
0
        int nCount = 0;
594
0
        const double *padfVals =
595
0
            poFeature->GetFieldAsDoubleList(iField, &nCount);
596
0
        osVal += '[';
597
0
        for (int i = 0; i < nCount; ++i)
598
0
        {
599
0
            if (i > 0)
600
0
                osVal += ',';
601
0
            osVal += CPLSPrintf("%.17g", padfVals[i]);
602
0
        }
603
0
        osVal += ']';
604
0
    }
605
0
    else
606
0
    {
607
        // Cf https://cloud.google.com/bigquery/docs/json-data?hl=en#create_a_json_value
608
0
        if (poFldDefn->GetSubType() == OFSTJSON)
609
0
            osVal += "JSON ";
610
0
        osVal += '\'';
611
0
        osVal +=
612
0
            OGRDuplicateCharacter(poFeature->GetFieldAsString(iField), '\'');
613
0
        osVal += '\'';
614
0
    }
615
0
    return osVal;
616
0
}
617
618
/************************************************************************/
619
/*                           ICreateFeature()                           */
620
/************************************************************************/
621
622
OGRErr OGRADBCBigQueryLayer::ICreateFeature(OGRFeature *poFeature)
623
0
{
624
0
    if (m_poDS->GetAccess() != GA_Update)
625
0
    {
626
0
        CPLError(
627
0
            CE_Failure, CPLE_NotSupported,
628
0
            "CreateFeature() only supported on datasets opened in update mode");
629
0
        return OGRERR_FAILURE;
630
0
    }
631
0
    if (!m_poAdapterLayer)
632
0
        BuildLayerDefn();
633
0
    if (m_bDeferredCreation)
634
0
        RunDeferredCreation();
635
0
    if (m_bLayerDefinitionError)
636
0
        return OGRERR_FAILURE;
637
638
0
    std::string osDatasetId;
639
0
    std::string osTableId;
640
0
    if (!STARTS_WITH_CI(m_osBaseStatement.c_str(), "SELECT * FROM ") ||
641
0
        CPLString(m_osBaseStatement).ifind(" WHERE ") != std::string::npos ||
642
0
        !GetBigQueryDatasetAndTableId(osDatasetId, osTableId))
643
0
    {
644
0
        CPLError(CE_Failure, CPLE_NotSupported,
645
0
                 "CreateFeature(): cannot get dataset and table ID");
646
0
        return OGRERR_FAILURE;
647
0
    }
648
649
0
    std::string osFieldNames;
650
0
    std::string osFieldValues;
651
652
0
    if (!m_osFIDColName.empty())
653
0
    {
654
0
        if (poFeature->GetFID() < 0)
655
0
        {
656
0
            if (m_nMaxFeatureID < 0)
657
0
            {
658
0
                std::string osSQL = "SELECT MAX(`";
659
0
                osSQL += OGRDuplicateCharacter(m_osFIDColName.c_str(), '`');
660
0
                osSQL += "`) FROM (";
661
0
                osSQL += m_osBaseStatement;
662
0
                osSQL += ')';
663
664
0
                auto poMaxFIDLayer = m_poDS->CreateInternalLayer(osSQL.c_str());
665
0
                if (poMaxFIDLayer->GetLayerDefn()->GetFieldCount() != 1)
666
0
                    return OGRERR_FAILURE;
667
0
                auto f = std::unique_ptr<OGRFeature>(
668
0
                    poMaxFIDLayer->GetNextFeature());
669
0
                if (f)
670
0
                    m_nMaxFeatureID = f->GetFieldAsInteger64(0);
671
0
                else
672
0
                    m_nMaxFeatureID = 0;
673
0
            }
674
0
            poFeature->SetFID(++m_nMaxFeatureID);
675
0
        }
676
0
        osFieldNames = m_osFIDColName;
677
0
        osFieldValues = std::to_string(poFeature->GetFID());
678
0
    }
679
680
0
    auto poFeatureDefn = GetLayerDefn();
681
0
    for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); ++i)
682
0
    {
683
0
        const char *pszName = poFeatureDefn->GetGeomFieldDefn(i)->GetNameRef();
684
0
        if (!osFieldNames.empty())
685
0
            osFieldNames += ", ";
686
0
        osFieldNames += '`';
687
0
        osFieldNames += OGRDuplicateCharacter(pszName, '`');
688
0
        osFieldNames += '`';
689
690
0
        if (!osFieldValues.empty())
691
0
            osFieldValues += ", ";
692
693
0
        const auto poGeom = poFeature->GetGeomFieldRef(i);
694
0
        if (poGeom)
695
0
        {
696
0
            osFieldValues += "ST_GeogFromText('";
697
0
            char *pszWKT = nullptr;
698
0
            poGeom->exportToWkt(&pszWKT, wkbVariantIso);
699
0
            osFieldValues += pszWKT;
700
0
            CPLFree(pszWKT);
701
0
            osFieldValues += "')";
702
0
        }
703
0
        else
704
0
        {
705
0
            osFieldValues += "NULL";
706
0
        }
707
0
    }
708
0
    for (int i = 0; i < poFeatureDefn->GetFieldCount(); ++i)
709
0
    {
710
0
        const auto poFieldDefn = poFeatureDefn->GetFieldDefn(i);
711
0
        const char *pszName = poFieldDefn->GetNameRef();
712
0
        if (!EQUAL(pszName, m_osFIDColName.c_str()) && poFeature->IsFieldSet(i))
713
0
        {
714
0
            if (!osFieldNames.empty())
715
0
                osFieldNames += ", ";
716
0
            osFieldNames += '`';
717
0
            osFieldNames += OGRDuplicateCharacter(pszName, '`');
718
0
            osFieldNames += '`';
719
720
0
            if (!osFieldValues.empty())
721
0
                osFieldValues += ", ";
722
723
0
            osFieldValues += GetFieldValue(poFieldDefn, poFeature, i);
724
0
        }
725
0
    }
726
727
0
    std::string osSQL = "INSERT INTO `";
728
0
    osSQL += OGRDuplicateCharacter(osDatasetId, '`');
729
0
    osSQL += "`.`";
730
0
    osSQL += OGRDuplicateCharacter(osTableId, '`');
731
0
    osSQL += "` ";
732
0
    if (osFieldNames.empty())
733
0
    {
734
0
        osSQL += "DEFAULT VALUES";
735
0
    }
736
0
    else
737
0
    {
738
0
        osSQL += '(';
739
0
        osSQL += osFieldNames;
740
0
        osSQL += ") VALUES (";
741
0
        osSQL += osFieldValues;
742
0
        osSQL += ')';
743
0
    }
744
0
    return m_poDS->CreateInternalLayer(osSQL.c_str())->GotError()
745
0
               ? OGRERR_FAILURE
746
0
               : OGRERR_NONE;
747
0
}
748
749
/************************************************************************/
750
/*                            ISetFeature()                             */
751
/************************************************************************/
752
753
OGRErr OGRADBCBigQueryLayer::ISetFeature(OGRFeature *poFeature)
754
0
{
755
0
    if (m_poDS->GetAccess() != GA_Update)
756
0
    {
757
0
        CPLError(
758
0
            CE_Failure, CPLE_NotSupported,
759
0
            "SetFeature() only supported on datasets opened in update mode");
760
0
        return OGRERR_FAILURE;
761
0
    }
762
0
    if (m_osFIDColName.empty())
763
0
    {
764
0
        CPLError(CE_Failure, CPLE_NotSupported,
765
0
                 "SetFeature() only supported on tables with a INT64 single "
766
0
                 "column primary key");
767
0
        return OGRERR_FAILURE;
768
0
    }
769
0
    if (poFeature->GetFID() < 0)
770
0
    {
771
0
        return OGRERR_NON_EXISTING_FEATURE;
772
0
    }
773
774
0
    if (!m_poAdapterLayer)
775
0
        BuildLayerDefn();
776
0
    if (m_bDeferredCreation)
777
0
        RunDeferredCreation();
778
0
    if (m_bLayerDefinitionError)
779
0
        return OGRERR_FAILURE;
780
781
0
    std::string osDatasetId;
782
0
    std::string osTableId;
783
0
    if (!STARTS_WITH_CI(m_osBaseStatement.c_str(), "SELECT * FROM ") ||
784
0
        CPLString(m_osBaseStatement).ifind(" WHERE ") != std::string::npos ||
785
0
        !GetBigQueryDatasetAndTableId(osDatasetId, osTableId))
786
0
    {
787
0
        CPLError(CE_Failure, CPLE_NotSupported,
788
0
                 "SetFeature(): cannot get dataset and table ID");
789
0
        return OGRERR_FAILURE;
790
0
    }
791
792
0
    std::string osSQL = "UPDATE `";
793
0
    osSQL += OGRDuplicateCharacter(osDatasetId, '`');
794
0
    osSQL += "`.`";
795
0
    osSQL += OGRDuplicateCharacter(osTableId, '`');
796
0
    osSQL += "` SET ";
797
798
0
    bool bAddComma = false;
799
0
    const auto poFeatureDefn = GetLayerDefn();
800
801
0
    for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); ++i)
802
0
    {
803
0
        const char *pszName = poFeatureDefn->GetGeomFieldDefn(i)->GetNameRef();
804
0
        if (bAddComma)
805
0
            osSQL += ", ";
806
0
        bAddComma = true;
807
0
        osSQL += '`';
808
0
        osSQL += OGRDuplicateCharacter(pszName, '`');
809
0
        osSQL += "` = ";
810
0
        const auto poGeom = poFeature->GetGeomFieldRef(i);
811
0
        if (poGeom)
812
0
        {
813
0
            osSQL += "ST_GeogFromText('";
814
0
            char *pszWKT = nullptr;
815
0
            poGeom->exportToWkt(&pszWKT, wkbVariantIso);
816
0
            osSQL += pszWKT;
817
0
            CPLFree(pszWKT);
818
0
            osSQL += "')";
819
0
        }
820
0
        else
821
0
        {
822
0
            osSQL += "NULL";
823
0
        }
824
0
    }
825
826
0
    for (int i = 0; i < poFeatureDefn->GetFieldCount(); ++i)
827
0
    {
828
0
        const auto poFieldDefn = poFeatureDefn->GetFieldDefn(i);
829
0
        const char *pszName = poFieldDefn->GetNameRef();
830
0
        if (!EQUAL(pszName, m_osFIDColName.c_str()) && poFeature->IsFieldSet(i))
831
0
        {
832
0
            if (bAddComma)
833
0
                osSQL += ", ";
834
0
            bAddComma = true;
835
0
            osSQL += '`';
836
0
            osSQL += OGRDuplicateCharacter(pszName, '`');
837
0
            osSQL += "` = ";
838
0
            osSQL += GetFieldValue(poFieldDefn, poFeature, i);
839
0
        }
840
0
    }
841
842
0
    osSQL += " WHERE `";
843
0
    osSQL += OGRDuplicateCharacter(m_osFIDColName, '`');
844
0
    osSQL += "` = ";
845
0
    osSQL += std::to_string(poFeature->GetFID());
846
847
0
    return bAddComma && m_poDS->CreateInternalLayer(osSQL.c_str())->GotError()
848
0
               ? OGRERR_FAILURE
849
0
               : OGRERR_NONE;
850
0
}
851
852
/************************************************************************/
853
/*                           DeleteFeature()                            */
854
/************************************************************************/
855
856
OGRErr OGRADBCBigQueryLayer::DeleteFeature(GIntBig nFID)
857
0
{
858
0
    if (m_poDS->GetAccess() != GA_Update)
859
0
    {
860
0
        CPLError(
861
0
            CE_Failure, CPLE_NotSupported,
862
0
            "DeleteFeature() only supported on datasets opened in update mode");
863
0
        return OGRERR_FAILURE;
864
0
    }
865
0
    if (m_osFIDColName.empty())
866
0
    {
867
0
        CPLError(CE_Failure, CPLE_NotSupported,
868
0
                 "DeleteFeature() only supported on tables with a INT64 single "
869
0
                 "column primary key");
870
0
        return OGRERR_FAILURE;
871
0
    }
872
0
    if (!m_poAdapterLayer)
873
0
        BuildLayerDefn();
874
0
    if (m_bDeferredCreation)
875
0
        RunDeferredCreation();
876
0
    if (m_bLayerDefinitionError)
877
0
        return OGRERR_FAILURE;
878
0
    if (nFID < 0)
879
0
        return OGRERR_NON_EXISTING_FEATURE;
880
881
0
    std::string osDatasetId;
882
0
    std::string osTableId;
883
0
    if (!GetBigQueryDatasetAndTableId(osDatasetId, osTableId))
884
0
    {
885
0
        CPLError(CE_Failure, CPLE_NotSupported,
886
0
                 "DeleteFeature(): cannot get dataset and table ID");
887
0
        return OGRERR_FAILURE;
888
0
    }
889
890
0
    std::string osSQL = "DELETE FROM `";
891
0
    osSQL += OGRDuplicateCharacter(osDatasetId, '`');
892
0
    osSQL += "`.`";
893
0
    osSQL += OGRDuplicateCharacter(osTableId, '`');
894
0
    osSQL += "` WHERE `";
895
0
    osSQL += OGRDuplicateCharacter(m_osFIDColName, '`');
896
0
    osSQL += "` = ";
897
0
    osSQL += std::to_string(nFID);
898
899
0
    return m_poDS->CreateInternalLayer(osSQL.c_str())->GotError()
900
0
               ? OGRERR_FAILURE
901
0
               : OGRERR_NONE;
902
0
}
903
904
/************************************************************************/
905
/*                        SetDeferredCreation()                         */
906
/************************************************************************/
907
908
void OGRADBCBigQueryLayer::SetDeferredCreation(
909
    const char *pszFIDColName, const OGRGeomFieldDefn *poGeomFieldDefn)
910
0
{
911
0
    m_bDeferredCreation = true;
912
0
    m_osFIDColName = pszFIDColName;
913
0
    m_poAdapterLayer = std::make_unique<OGRArrowArrayToOGRFeatureAdapterLayer>(
914
0
        GetDescription());
915
0
    if (poGeomFieldDefn && poGeomFieldDefn->GetType() != wkbNone)
916
0
    {
917
0
        OGRGeomFieldDefn oFieldDefn(poGeomFieldDefn);
918
0
        if (oFieldDefn.GetNameRef()[0] == '\0')
919
0
            oFieldDefn.SetName("geog");
920
0
        m_poAdapterLayer->CreateGeomField(&oFieldDefn, false);
921
0
    }
922
0
}
923
924
/************************************************************************/
925
/*                        RunDeferredCreation()                         */
926
/************************************************************************/
927
928
bool OGRADBCBigQueryLayer::RunDeferredCreation()
929
0
{
930
0
    if (m_bDeferredCreation)
931
0
    {
932
0
        m_bDeferredCreation = false;
933
934
0
        auto poFeatureDefn = m_poAdapterLayer->GetLayerDefn();
935
0
        std::string osDatasetId;
936
0
        std::string osTableId;
937
0
        CPL_IGNORE_RET_VAL(
938
0
            GetBigQueryDatasetAndTableId(osDatasetId, osTableId));
939
940
0
        std::string osSQL = "CREATE TABLE `";
941
0
        osSQL += OGRDuplicateCharacter(osDatasetId.c_str(), '`');
942
0
        osSQL += "`.`";
943
0
        osSQL += OGRDuplicateCharacter(osTableId.c_str(), '`');
944
0
        osSQL += "` (";
945
0
        bool bAddComma = false;
946
0
        if (!m_osFIDColName.empty())
947
0
        {
948
0
            osSQL += '`';
949
0
            osSQL += OGRDuplicateCharacter(m_osFIDColName.c_str(), '`');
950
0
            osSQL += "` INT64 PRIMARY KEY NOT ENFORCED";
951
0
            bAddComma = true;
952
0
        }
953
0
        for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); ++i)
954
0
        {
955
0
            const auto poFieldDefn = poFeatureDefn->GetGeomFieldDefn(i);
956
0
            if (bAddComma)
957
0
                osSQL += ", ";
958
0
            bAddComma = true;
959
0
            osSQL += '`';
960
0
            osSQL += OGRDuplicateCharacter(poFieldDefn->GetNameRef(), '`');
961
0
            osSQL += "` GEOGRAPHY";
962
0
            if (!poFieldDefn->IsNullable())
963
0
                osSQL += " NOT NULL";
964
0
        }
965
0
        for (int i = 0; i < poFeatureDefn->GetFieldCount(); ++i)
966
0
        {
967
0
            const auto poFieldDefn = poFeatureDefn->GetFieldDefn(i);
968
0
            if (bAddComma)
969
0
                osSQL += ", ";
970
0
            bAddComma = true;
971
0
            osSQL += '`';
972
0
            osSQL += OGRDuplicateCharacter(poFieldDefn->GetNameRef(), '`');
973
0
            osSQL += "` ";
974
0
            osSQL += GetSQLType(poFieldDefn);
975
0
            if (!poFieldDefn->IsNullable())
976
0
                osSQL += " NOT NULL";
977
0
        }
978
0
        osSQL += ')';
979
980
0
        m_bLayerDefinitionError =
981
0
            m_poDS->CreateInternalLayer(osSQL.c_str())->GotError();
982
0
    }
983
0
    return !m_bLayerDefinitionError;
984
0
}