Coverage Report

Created: 2026-03-30 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteviewlayer.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  Implements OGRSQLiteViewLayer class, access to an existing
5
 *spatialite view. Author:   Even Rouault, <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "cpl_port.h"
14
#include "ogr_sqlite.h"
15
#include "ogrsqliteutility.h"
16
17
#include <cstdlib>
18
#include <cstring>
19
#include <set>
20
#include <string>
21
22
#include "cpl_conv.h"
23
#include "cpl_error.h"
24
#include "cpl_string.h"
25
#include "ogr_core.h"
26
#include "ogr_feature.h"
27
#include "ogr_geometry.h"
28
#include "sqlite3.h"
29
30
/************************************************************************/
31
/*                         OGRSQLiteViewLayer()                         */
32
/************************************************************************/
33
34
OGRSQLiteViewLayer::OGRSQLiteViewLayer(OGRSQLiteDataSource *poDSIn)
35
0
    : OGRSQLiteLayer(poDSIn)
36
0
{
37
0
}
38
39
/************************************************************************/
40
/*                        ~OGRSQLiteViewLayer()                         */
41
/************************************************************************/
42
43
OGRSQLiteViewLayer::~OGRSQLiteViewLayer()
44
45
0
{
46
0
    ClearStatement();
47
0
    CPLFree(m_pszViewName);
48
0
    CPLFree(m_pszEscapedTableName);
49
0
    CPLFree(m_pszEscapedUnderlyingTableName);
50
0
}
51
52
/************************************************************************/
53
/*                             Initialize()                             */
54
/************************************************************************/
55
56
CPLErr OGRSQLiteViewLayer::Initialize(const char *pszViewNameIn,
57
                                      const char *pszViewGeometry,
58
                                      const char *pszViewRowid,
59
                                      const char *pszUnderlyingTableName,
60
                                      const char *pszUnderlyingGeometryColumn)
61
62
0
{
63
0
    m_pszViewName = CPLStrdup(pszViewNameIn);
64
0
    SetDescription(m_pszViewName);
65
66
0
    m_osGeomColumn = pszViewGeometry;
67
0
    m_eGeomFormat = OSGF_SpatiaLite;
68
69
0
    CPLFree(m_pszFIDColumn);
70
0
    m_pszFIDColumn = CPLStrdup(pszViewRowid);
71
72
0
    m_osUnderlyingTableName = pszUnderlyingTableName;
73
0
    m_osUnderlyingGeometryColumn = pszUnderlyingGeometryColumn;
74
0
    m_poUnderlyingLayer = nullptr;
75
76
0
    m_pszEscapedTableName = CPLStrdup(SQLEscapeLiteral(m_pszViewName));
77
0
    m_pszEscapedUnderlyingTableName =
78
0
        CPLStrdup(SQLEscapeLiteral(pszUnderlyingTableName));
79
80
0
    return CE_None;
81
0
}
82
83
/************************************************************************/
84
/*                            GetLayerDefn()                            */
85
/************************************************************************/
86
87
const OGRFeatureDefn *OGRSQLiteViewLayer::GetLayerDefn() const
88
0
{
89
0
    if (!m_poFeatureDefn)
90
0
    {
91
0
        const_cast<OGRSQLiteViewLayer *>(this)->BuildLayerDefn();
92
0
    }
93
0
    return m_poFeatureDefn;
94
0
}
95
96
/************************************************************************/
97
/*                           BuildLayerDefn()                           */
98
/************************************************************************/
99
100
void OGRSQLiteViewLayer::BuildLayerDefn()
101
0
{
102
0
    EstablishFeatureDefn();
103
104
0
    if (m_poFeatureDefn == nullptr)
105
0
    {
106
0
        m_bLayerDefnError = true;
107
108
0
        m_poFeatureDefn = new OGRSQLiteFeatureDefn(m_pszViewName);
109
0
        m_poFeatureDefn->Reference();
110
0
    }
111
0
}
112
113
/************************************************************************/
114
/*                         GetUnderlyingLayer()                         */
115
/************************************************************************/
116
117
OGRSQLiteLayer *OGRSQLiteViewLayer::GetUnderlyingLayer()
118
0
{
119
0
    if (m_poUnderlyingLayer == nullptr)
120
0
    {
121
0
        if (strchr(m_osUnderlyingTableName, '(') == nullptr)
122
0
        {
123
0
            CPLString osNewUnderlyingTableName;
124
0
            osNewUnderlyingTableName.Printf(
125
0
                "%s(%s)", m_osUnderlyingTableName.c_str(),
126
0
                m_osUnderlyingGeometryColumn.c_str());
127
0
            m_poUnderlyingLayer = cpl::down_cast<OGRSQLiteLayer *>(
128
0
                m_poDS->GetLayerByNameNotVisible(osNewUnderlyingTableName));
129
0
        }
130
0
        if (m_poUnderlyingLayer == nullptr)
131
0
            m_poUnderlyingLayer = cpl::down_cast<OGRSQLiteLayer *>(
132
0
                m_poDS->GetLayerByNameNotVisible(m_osUnderlyingTableName));
133
0
    }
134
0
    return m_poUnderlyingLayer;
135
0
}
136
137
/************************************************************************/
138
/*                            GetGeomType()                             */
139
/************************************************************************/
140
141
OGRwkbGeometryType OGRSQLiteViewLayer::GetGeomType() const
142
0
{
143
0
    if (m_poFeatureDefn)
144
0
        return m_poFeatureDefn->GetGeomType();
145
146
0
    OGRSQLiteLayer *l_m_poUnderlyingLayer =
147
0
        const_cast<OGRSQLiteViewLayer *>(this)->GetUnderlyingLayer();
148
0
    if (l_m_poUnderlyingLayer)
149
0
        return l_m_poUnderlyingLayer->GetGeomType();
150
151
0
    return wkbUnknown;
152
0
}
153
154
/************************************************************************/
155
/*                        EstablishFeatureDefn()                        */
156
/************************************************************************/
157
158
CPLErr OGRSQLiteViewLayer::EstablishFeatureDefn()
159
0
{
160
0
    sqlite3 *hDB = m_poDS->GetDB();
161
0
    sqlite3_stmt *hColStmt = nullptr;
162
163
0
    OGRSQLiteLayer *l_m_poUnderlyingLayer = GetUnderlyingLayer();
164
0
    if (l_m_poUnderlyingLayer == nullptr)
165
0
    {
166
0
        CPLError(CE_Failure, CPLE_AppDefined,
167
0
                 "Cannot find underlying layer %s for view %s",
168
0
                 m_osUnderlyingTableName.c_str(), m_pszViewName);
169
0
        return CE_Failure;
170
0
    }
171
0
    if (!l_m_poUnderlyingLayer->IsTableLayer())
172
0
    {
173
0
        CPLError(CE_Failure, CPLE_AppDefined,
174
0
                 "Underlying layer %s for view %s is not a regular table",
175
0
                 m_osUnderlyingTableName.c_str(), m_pszViewName);
176
0
        return CE_Failure;
177
0
    }
178
179
0
    int nUnderlyingLayerGeomFieldIndex =
180
0
        l_m_poUnderlyingLayer->GetLayerDefn()->GetGeomFieldIndex(
181
0
            m_osUnderlyingGeometryColumn);
182
0
    if (nUnderlyingLayerGeomFieldIndex < 0)
183
0
    {
184
0
        CPLError(CE_Failure, CPLE_AppDefined,
185
0
                 "Underlying layer %s for view %s has not expected geometry "
186
0
                 "column name %s",
187
0
                 m_osUnderlyingTableName.c_str(), m_pszViewName,
188
0
                 m_osUnderlyingGeometryColumn.c_str());
189
0
        return CE_Failure;
190
0
    }
191
192
0
    m_bHasSpatialIndex =
193
0
        l_m_poUnderlyingLayer->HasSpatialIndex(nUnderlyingLayerGeomFieldIndex);
194
195
    /* -------------------------------------------------------------------- */
196
    /*      Get the column definitions for this table.                      */
197
    /* -------------------------------------------------------------------- */
198
0
    hColStmt = nullptr;
199
0
    const char *pszSQL = CPLSPrintf("SELECT \"%s\", * FROM '%s' LIMIT 1",
200
0
                                    SQLEscapeName(m_pszFIDColumn).c_str(),
201
0
                                    m_pszEscapedTableName);
202
203
0
    int rc = sqlite3_prepare_v2(hDB, pszSQL, -1, &hColStmt, nullptr);
204
0
    if (rc != SQLITE_OK)
205
0
    {
206
0
        CPLError(CE_Failure, CPLE_AppDefined,
207
0
                 "Unable to query table %s for column definitions : %s.",
208
0
                 m_pszViewName, sqlite3_errmsg(hDB));
209
210
0
        return CE_Failure;
211
0
    }
212
213
0
    rc = sqlite3_step(hColStmt);
214
0
    if (rc != SQLITE_DONE && rc != SQLITE_ROW)
215
0
    {
216
0
        CPLError(CE_Failure, CPLE_AppDefined,
217
0
                 "In Initialize(): sqlite3_step(%s):\n  %s", pszSQL,
218
0
                 sqlite3_errmsg(hDB));
219
0
        sqlite3_finalize(hColStmt);
220
0
        return CE_Failure;
221
0
    }
222
223
    /* -------------------------------------------------------------------- */
224
    /*      Collect the rest of the fields.                                 */
225
    /* -------------------------------------------------------------------- */
226
0
    std::set<CPLString> aosGeomCols;
227
0
    std::set<CPLString> aosIgnoredCols;
228
0
    aosGeomCols.insert(m_osGeomColumn);
229
0
    BuildFeatureDefn(m_pszViewName, false, hColStmt, &aosGeomCols,
230
0
                     aosIgnoredCols);
231
0
    sqlite3_finalize(hColStmt);
232
233
    /* -------------------------------------------------------------------- */
234
    /*      Set the properties of the geometry column.                      */
235
    /* -------------------------------------------------------------------- */
236
0
    if (m_poFeatureDefn->GetGeomFieldCount() != 0)
237
0
    {
238
0
        OGRSQLiteGeomFieldDefn *poSrcGeomFieldDefn =
239
0
            l_m_poUnderlyingLayer->myGetLayerDefn()->myGetGeomFieldDefn(
240
0
                nUnderlyingLayerGeomFieldIndex);
241
0
        OGRSQLiteGeomFieldDefn *poGeomFieldDefn =
242
0
            m_poFeatureDefn->myGetGeomFieldDefn(0);
243
0
        poGeomFieldDefn->SetType(poSrcGeomFieldDefn->GetType());
244
0
        poGeomFieldDefn->SetSpatialRef(poSrcGeomFieldDefn->GetSpatialRef());
245
0
        poGeomFieldDefn->m_nSRSId = poSrcGeomFieldDefn->m_nSRSId;
246
0
        poGeomFieldDefn->m_eGeomFormat = m_eGeomFormat;
247
0
    }
248
249
0
    return CE_None;
250
0
}
251
252
/************************************************************************/
253
/*                           ResetStatement()                           */
254
/************************************************************************/
255
256
OGRErr OGRSQLiteViewLayer::ResetStatement()
257
258
0
{
259
0
    CPLString osSQL;
260
261
0
    ClearStatement();
262
263
0
    m_iNextShapeId = 0;
264
265
0
    osSQL.Printf("SELECT \"%s\", * FROM '%s' %s",
266
0
                 SQLEscapeName(m_pszFIDColumn).c_str(), m_pszEscapedTableName,
267
0
                 m_osWHERE.c_str());
268
269
0
    const int rc =
270
0
        sqlite3_prepare_v2(m_poDS->GetDB(), osSQL,
271
0
                           static_cast<int>(osSQL.size()), &m_hStmt, nullptr);
272
273
0
    if (rc == SQLITE_OK)
274
0
    {
275
0
        return OGRERR_NONE;
276
0
    }
277
278
0
    CPLError(CE_Failure, CPLE_AppDefined,
279
0
             "In ResetStatement(): sqlite3_prepare_v2(%s):\n  %s",
280
0
             osSQL.c_str(), sqlite3_errmsg(m_poDS->GetDB()));
281
0
    m_hStmt = nullptr;
282
0
    return OGRERR_FAILURE;
283
0
}
284
285
/************************************************************************/
286
/*                           GetNextFeature()                           */
287
/************************************************************************/
288
289
OGRFeature *OGRSQLiteViewLayer::GetNextFeature()
290
291
0
{
292
0
    if (HasLayerDefnError())
293
0
        return nullptr;
294
295
0
    return OGRSQLiteLayer::GetNextFeature();
296
0
}
297
298
/************************************************************************/
299
/*                             GetFeature()                             */
300
/************************************************************************/
301
302
OGRFeature *OGRSQLiteViewLayer::GetFeature(GIntBig nFeatureId)
303
304
0
{
305
0
    if (HasLayerDefnError())
306
0
        return nullptr;
307
308
    /* -------------------------------------------------------------------- */
309
    /*      If we don't have an explicit FID column, just read through      */
310
    /*      the result set iteratively to find our target.                  */
311
    /* -------------------------------------------------------------------- */
312
0
    if (m_pszFIDColumn == nullptr)
313
0
        return OGRSQLiteLayer::GetFeature(nFeatureId);
314
315
    /* -------------------------------------------------------------------- */
316
    /*      Setup explicit query statement to fetch the record we want.     */
317
    /* -------------------------------------------------------------------- */
318
0
    CPLString osSQL;
319
320
0
    ClearStatement();
321
322
0
    m_iNextShapeId = nFeatureId;
323
324
0
    osSQL.Printf("SELECT \"%s\", * FROM '%s' WHERE \"%s\" = " CPL_FRMT_GIB,
325
0
                 SQLEscapeName(m_pszFIDColumn).c_str(), m_pszEscapedTableName,
326
0
                 SQLEscapeName(m_pszFIDColumn).c_str(), nFeatureId);
327
328
0
    CPLDebug("OGR_SQLITE", "exec(%s)", osSQL.c_str());
329
330
0
    const int rc =
331
0
        sqlite3_prepare_v2(m_poDS->GetDB(), osSQL,
332
0
                           static_cast<int>(osSQL.size()), &m_hStmt, nullptr);
333
0
    if (rc != SQLITE_OK)
334
0
    {
335
0
        CPLError(CE_Failure, CPLE_AppDefined,
336
0
                 "In GetFeature(): sqlite3_prepare_v2(%s):\n  %s",
337
0
                 osSQL.c_str(), sqlite3_errmsg(m_poDS->GetDB()));
338
339
0
        return nullptr;
340
0
    }
341
    /* -------------------------------------------------------------------- */
342
    /*      Get the feature if possible.                                    */
343
    /* -------------------------------------------------------------------- */
344
0
    OGRFeature *poFeature = GetNextRawFeature();
345
346
0
    ResetReading();
347
348
0
    return poFeature;
349
0
}
350
351
/************************************************************************/
352
/*                         SetAttributeFilter()                         */
353
/************************************************************************/
354
355
OGRErr OGRSQLiteViewLayer::SetAttributeFilter(const char *pszQuery)
356
357
0
{
358
0
    if (pszQuery == nullptr)
359
0
        m_osQuery = "";
360
0
    else
361
0
        m_osQuery = pszQuery;
362
363
0
    BuildWhere();
364
365
0
    ResetReading();
366
367
0
    return OGRERR_NONE;
368
0
}
369
370
/************************************************************************/
371
/*                         ISetSpatialFilter()                          */
372
/************************************************************************/
373
374
OGRErr OGRSQLiteViewLayer::ISetSpatialFilter(int, const OGRGeometry *poGeomIn)
375
376
0
{
377
0
    if (InstallFilter(poGeomIn))
378
0
    {
379
0
        BuildWhere();
380
381
0
        ResetReading();
382
0
    }
383
0
    return OGRERR_NONE;
384
0
}
385
386
/************************************************************************/
387
/*                          GetSpatialWhere()                           */
388
/************************************************************************/
389
390
CPLString OGRSQLiteViewLayer::GetSpatialWhere(int iGeomCol,
391
                                              OGRGeometry *poFilterGeom)
392
0
{
393
0
    if (HasLayerDefnError() || m_poFeatureDefn == nullptr || iGeomCol < 0 ||
394
0
        iGeomCol >= m_poFeatureDefn->GetGeomFieldCount())
395
0
        return "";
396
397
0
    if (poFilterGeom != nullptr && m_bHasSpatialIndex)
398
0
    {
399
0
        OGREnvelope sEnvelope;
400
401
0
        poFilterGeom->getEnvelope(&sEnvelope);
402
403
        /* We first check that the spatial index table exists */
404
0
        if (!m_bHasCheckedSpatialIndexTable)
405
0
        {
406
0
            m_bHasCheckedSpatialIndexTable = true;
407
0
            char **papszResult = nullptr;
408
0
            int nRowCount = 0;
409
0
            int nColCount = 0;
410
0
            char *pszErrMsg = nullptr;
411
412
0
            CPLString osSQL;
413
0
            osSQL.Printf(
414
0
                "SELECT name FROM sqlite_master "
415
0
                "WHERE name='idx_%s_%s'",
416
0
                m_pszEscapedUnderlyingTableName,
417
0
                SQLEscapeLiteral(m_osUnderlyingGeometryColumn).c_str());
418
419
0
            int rc =
420
0
                sqlite3_get_table(m_poDS->GetDB(), osSQL.c_str(), &papszResult,
421
0
                                  &nRowCount, &nColCount, &pszErrMsg);
422
423
0
            if (rc != SQLITE_OK)
424
0
            {
425
0
                CPLError(CE_Failure, CPLE_AppDefined, "Error: %s", pszErrMsg);
426
0
                sqlite3_free(pszErrMsg);
427
0
                m_bHasSpatialIndex = false;
428
0
            }
429
0
            else
430
0
            {
431
0
                if (nRowCount != 1)
432
0
                {
433
0
                    m_bHasSpatialIndex = false;
434
0
                }
435
436
0
                sqlite3_free_table(papszResult);
437
0
            }
438
0
        }
439
440
0
        if (m_bHasSpatialIndex)
441
0
        {
442
0
            return FormatSpatialFilterFromRTree(
443
0
                poFilterGeom,
444
0
                CPLSPrintf("\"%s\"", SQLEscapeName(m_pszFIDColumn).c_str()),
445
0
                m_pszEscapedUnderlyingTableName,
446
0
                SQLEscapeLiteral(m_osUnderlyingGeometryColumn).c_str());
447
0
        }
448
0
        else
449
0
        {
450
0
            CPLDebug("SQLITE",
451
0
                     "Count not find idx_%s_%s layer. Disabling spatial index",
452
0
                     m_pszEscapedUnderlyingTableName,
453
0
                     m_osUnderlyingGeometryColumn.c_str());
454
0
        }
455
0
    }
456
457
0
    if (poFilterGeom != nullptr && m_poDS->IsSpatialiteLoaded())
458
0
    {
459
0
        return FormatSpatialFilterFromMBR(
460
0
            poFilterGeom,
461
0
            SQLEscapeName(
462
0
                m_poFeatureDefn->GetGeomFieldDefn(iGeomCol)->GetNameRef())
463
0
                .c_str());
464
0
    }
465
466
0
    return "";
467
0
}
468
469
/************************************************************************/
470
/*                             BuildWhere()                             */
471
/*                                                                      */
472
/*      Build the WHERE statement appropriate to the current set of     */
473
/*      criteria (spatial and attribute queries).                       */
474
/************************************************************************/
475
476
void OGRSQLiteViewLayer::BuildWhere()
477
478
0
{
479
0
    m_osWHERE = "";
480
481
0
    CPLString osSpatialWHERE =
482
0
        GetSpatialWhere(m_iGeomFieldFilter, m_poFilterGeom);
483
0
    if (!osSpatialWHERE.empty())
484
0
    {
485
0
        m_osWHERE = "WHERE ";
486
0
        m_osWHERE += osSpatialWHERE;
487
0
    }
488
489
0
    if (!m_osQuery.empty())
490
0
    {
491
0
        if (m_osWHERE.empty())
492
0
        {
493
0
            m_osWHERE = "WHERE ";
494
0
            m_osWHERE += m_osQuery;
495
0
        }
496
0
        else
497
0
        {
498
0
            m_osWHERE += " AND (";
499
0
            m_osWHERE += m_osQuery;
500
0
            m_osWHERE += ")";
501
0
        }
502
0
    }
503
0
}
504
505
/************************************************************************/
506
/*                           TestCapability()                           */
507
/************************************************************************/
508
509
int OGRSQLiteViewLayer::TestCapability(const char *pszCap) const
510
511
0
{
512
0
    if (HasLayerDefnError())
513
0
        return FALSE;
514
515
0
    if (EQUAL(pszCap, OLCFastFeatureCount))
516
0
        return m_poFilterGeom == nullptr || m_osGeomColumn.empty() ||
517
0
               m_bHasSpatialIndex;
518
519
0
    else if (EQUAL(pszCap, OLCFastSpatialFilter))
520
0
        return m_bHasSpatialIndex;
521
522
0
    else
523
0
        return OGRSQLiteLayer::TestCapability(pszCap);
524
0
}
525
526
/************************************************************************/
527
/*                          GetFeatureCount()                           */
528
/*                                                                      */
529
/*      If a spatial filter is in effect, we turn control over to       */
530
/*      the generic counter.  Otherwise we return the total count.      */
531
/*      Eventually we should consider implementing a more efficient     */
532
/*      way of counting features matching a spatial query.              */
533
/************************************************************************/
534
535
GIntBig OGRSQLiteViewLayer::GetFeatureCount(int bForce)
536
537
0
{
538
0
    if (HasLayerDefnError())
539
0
        return 0;
540
541
0
    if (!TestCapability(OLCFastFeatureCount))
542
0
        return OGRSQLiteLayer::GetFeatureCount(bForce);
543
544
    /* -------------------------------------------------------------------- */
545
    /*      Form count SQL.                                                 */
546
    /* -------------------------------------------------------------------- */
547
0
    const char *pszSQL = CPLSPrintf("SELECT count(*) FROM '%s' %s",
548
0
                                    m_pszEscapedTableName, m_osWHERE.c_str());
549
550
    /* -------------------------------------------------------------------- */
551
    /*      Execute.                                                        */
552
    /* -------------------------------------------------------------------- */
553
0
    char **papszResult, *pszErrMsg;
554
0
    int nRowCount, nColCount;
555
0
    int nResult = -1;
556
557
0
    if (sqlite3_get_table(m_poDS->GetDB(), pszSQL, &papszResult, &nColCount,
558
0
                          &nRowCount, &pszErrMsg) != SQLITE_OK)
559
0
        return -1;
560
561
0
    if (nRowCount == 1 && nColCount == 1)
562
0
        nResult = atoi(papszResult[1]);
563
564
0
    sqlite3_free_table(papszResult);
565
566
0
    return nResult;
567
0
}