/******************************************************************************
 *
 * Project:  OpenGIS Simple Features Reference Implementation
 * Purpose:  Private definitions for OGR/SQLite driver.
 * Author:   Frank Warmerdam, warmerdam@pobox.com
 *
 ******************************************************************************
 * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
 * Copyright (c) 2009-2014, Even Rouault <even dot rouault at spatialys.com>
 *
 * SPDX-License-Identifier: MIT
 ****************************************************************************/

#ifndef OGR_SQLITE_H_INCLUDED
#define OGR_SQLITE_H_INCLUDED

#include <cstddef>
#include <map>
#include <set>
#include <utility>
#include <vector>

#include "cpl_error.h"
#include "gdal_pam.h"
#include "ogrsf_frmts.h"
#include "ogrsf_frmts.h"
#include "rasterlite2_header.h"

#ifdef SPATIALITE_AMALGAMATION
/*
/ using an AMALGAMATED version of SpatiaLite
/ a private internal copy of SQLite is included:
/ so we are required including the SpatiaLite's
/ own header
/
/ IMPORTANT NOTICE: using AMALAGATION is only
/ useful on Windows (to skip DLL hell related oddities)
/
/ You MUST NOT use AMALGAMATION on Linux or any
/ other "sane" operating system !!!!
*/
#include <spatialite/sqlite3.h>
#else
#include <sqlite3.h>
#endif

#define UNINITIALIZED_SRID -2

#if defined(DEBUG) || defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) ||     \
    defined(ALLOW_FORMAT_DUMPS)
// Enable accepting a SQL dump (starting with a "-- SQL SQLITE" or
// "-- SQL RASTERLITE" or "--SQL MBTILES" line) as a valid
// file. This makes fuzzer life easier
#define ENABLE_SQL_SQLITE_FORMAT
#endif

#include "ogrsqlitebase.h"

/************************************************************************/
/*                 SpatiaLite's own Geometry type IDs.                  */
/************************************************************************/

enum OGRSpatialiteGeomType
{
    // 2D [XY]
    OGRSplitePointXY = 1,
    OGRSpliteLineStringXY = 2,
    OGRSplitePolygonXY = 3,
    OGRSpliteMultiPointXY = 4,
    OGRSpliteMultiLineStringXY = 5,
    OGRSpliteMultiPolygonXY = 6,
    OGRSpliteGeometryCollectionXY = 7,
    // 3D [XYZ]
    OGRSplitePointXYZ = 1001,
    OGRSpliteLineStringXYZ = 1002,
    OGRSplitePolygonXYZ = 1003,
    OGRSpliteMultiPointXYZ = 1004,
    OGRSpliteMultiLineStringXYZ = 1005,
    OGRSpliteMultiPolygonXYZ = 1006,
    OGRSpliteGeometryCollectionXYZ = 1007,
    // 2D with Measure [XYM]
    OGRSplitePointXYM = 2001,
    OGRSpliteLineStringXYM = 2002,
    OGRSplitePolygonXYM = 2003,
    OGRSpliteMultiPointXYM = 2004,
    OGRSpliteMultiLineStringXYM = 2005,
    OGRSpliteMultiPolygonXYM = 2006,
    OGRSpliteGeometryCollectionXYM = 2007,
    // 3D with Measure [XYZM]
    OGRSplitePointXYZM = 3001,
    OGRSpliteLineStringXYZM = 3002,
    OGRSplitePolygonXYZM = 3003,
    OGRSpliteMultiPointXYZM = 3004,
    OGRSpliteMultiLineStringXYZM = 3005,
    OGRSpliteMultiPolygonXYZM = 3006,
    OGRSpliteGeometryCollectionXYZM = 3007,
    // COMPRESSED: 2D [XY]
    OGRSpliteComprLineStringXY = 1000002,
    OGRSpliteComprPolygonXY = 1000003,
    OGRSpliteComprMultiPointXY = 1000004,
    OGRSpliteComprMultiLineStringXY = 1000005,
    OGRSpliteComprMultiPolygonXY = 1000006,
    OGRSpliteComprGeometryCollectionXY = 1000007,
    // COMPRESSED: 3D [XYZ]
    OGRSpliteComprLineStringXYZ = 1001002,
    OGRSpliteComprPolygonXYZ = 1001003,
    OGRSpliteComprMultiPointXYZ = 1001004,
    OGRSpliteComprMultiLineStringXYZ = 1001005,
    OGRSpliteComprMultiPolygonXYZ = 1001006,
    OGRSpliteComprGeometryCollectionXYZ = 1001007,
    // COMPRESSED: 2D with Measure [XYM]
    OGRSpliteComprLineStringXYM = 1002002,
    OGRSpliteComprPolygonXYM = 1002003,
    OGRSpliteComprMultiPointXYM = 1002004,
    OGRSpliteComprMultiLineStringXYM = 1002005,
    OGRSpliteComprMultiPolygonXYM = 1002006,
    OGRSpliteComprGeometryCollectionXYM = 1002007,
    // COMPRESSED: 3D with Measure [XYZM]
    OGRSpliteComprLineStringXYZM = 1003002,
    OGRSpliteComprPolygonXYZM = 1003003,
    OGRSpliteComprMultiPointXYZM = 1003004,
    OGRSpliteComprMultiLineStringXYZM = 1003005,
    OGRSpliteComprMultiPolygonXYZM = 1003006,
    OGRSpliteComprGeometryCollectionXYZM = 1003007
};

/************************************************************************/
/*                            OGRSQLiteLayer                            */
/************************************************************************/

class OGRSQLiteDataSource;

class OGRSQLiteLayer CPL_NON_FINAL : public OGRLayer,
                                     public IOGRSQLiteGetSpatialWhere
{
  private:
    static OGRErr
    createFromSpatialiteInternal(const GByte *pabyData, OGRGeometry **ppoReturn,
                                 int nBytes, OGRwkbByteOrder eByteOrder,
                                 int *pnBytesConsumed, int nRecLevel);

    static int CanBeCompressedSpatialiteGeometry(const OGRGeometry *poGeometry);

    static int ComputeSpatiaLiteGeometrySize(const OGRGeometry *poGeometry,
                                             bool bSpatialite2D,
                                             bool bUseComprGeom);

    static int GetSpatialiteGeometryCode(const OGRGeometry *poGeometry,
                                         bool bSpatialite2D, bool bUseComprGeom,
                                         bool bAcceptMultiGeom);

    static int ExportSpatiaLiteGeometryInternal(const OGRGeometry *poGeometry,
                                                OGRwkbByteOrder eByteOrder,
                                                bool bSpatialite2D,
                                                bool bUseComprGeom,
                                                GByte *pabyData);

    CPL_DISALLOW_COPY_ASSIGN(OGRSQLiteLayer)

  protected:
    mutable OGRSQLiteFeatureDefn *m_poFeatureDefn = nullptr;

    GIntBig m_iNextShapeId = 0;

    sqlite3_stmt *m_hStmt = nullptr;
    bool m_bDoStep = true;
    bool m_bEOF = false;

    OGRSQLiteDataSource *m_poDS = nullptr;

    char *m_pszFIDColumn = nullptr;

    int *m_panFieldOrdinals = nullptr;
    int m_iFIDCol = -1;
    int m_iOGRNativeDataCol = -1;
    int m_iOGRNativeMediaTypeCol = -1;

    bool m_bIsVirtualShape = false;

    void BuildFeatureDefn(const char *pszLayerName, bool bIsSelect,
                          sqlite3_stmt *hStmt,
                          const std::set<CPLString> *paosGeomCols,
                          const std::set<CPLString> &aosIgnoredCols);

    void ClearStatement();
    virtual OGRErr ResetStatement() = 0;

    bool m_bUseComprGeom = false;

    char **m_papszCompressedColumns = nullptr;

    bool m_bAllowMultipleGeomFields = false;

    static CPLString FormatSpatialFilterFromRTree(
        OGRGeometry *poFilterGeom, const char *pszRowIDName,
        const char *pszEscapedTable, const char *pszEscapedGeomCol);

    static CPLString
    FormatSpatialFilterFromMBR(OGRGeometry *poFilterGeom,
                               const char *pszEscapedGeomColName);

    explicit OGRSQLiteLayer(OGRSQLiteDataSource *poDSIn);

  public:
    ~OGRSQLiteLayer() override;

    void Finalize();

    GDALDataset *GetDataset() override;

    void ResetReading() override;
    virtual OGRFeature *GetNextRawFeature();
    OGRFeature *GetNextFeature() override;

    OGRFeature *GetFeature(GIntBig nFeatureId) override;

    using OGRLayer::GetLayerDefn;

    const OGRFeatureDefn *GetLayerDefn() const override
    {
        return m_poFeatureDefn;
    }

    OGRSQLiteFeatureDefn *myGetLayerDefn()
    {
        return m_poFeatureDefn;
    }

    const OGRSQLiteFeatureDefn *myGetLayerDefn() const
    {
        return m_poFeatureDefn;
    }

    const char *GetFIDColumn() const override;

    int TestCapability(const char *) const override;

    OGRErr StartTransaction() override;
    OGRErr CommitTransaction() override;
    OGRErr RollbackTransaction() override;

    virtual void InvalidateCachedFeatureCountAndExtent()
    {
    }

    virtual bool IsTableLayer()
    {
        return false;
    }

    virtual bool HasSpatialIndex(int /*iGeomField*/) const
    {
        return false;
    }

    bool HasFastSpatialFilter(CPL_UNUSED int iGeomCol) override
    {
        return false;
    }

    virtual CPLString
    GetSpatialWhere(CPL_UNUSED int iGeomCol,
                    CPL_UNUSED OGRGeometry *poFilterGeom) override
    {
        return "";
    }

    static OGRErr GetSpatialiteGeometryHeader(const GByte *pabyData, int nBytes,
                                              int *pnSRID,
                                              OGRwkbGeometryType *peType,
                                              bool *pbIsEmpty, double *pdfMinX,
                                              double *pdfMinY, double *pdfMaxX,
                                              double *pdfMaxY);
    static OGRErr ImportSpatiaLiteGeometry(const GByte *, int, OGRGeometry **);
    static OGRErr ImportSpatiaLiteGeometry(const GByte *, int, OGRGeometry **,
                                           int *pnSRID);
    static OGRErr ExportSpatiaLiteGeometry(const OGRGeometry *, GInt32,
                                           OGRwkbByteOrder, bool,
                                           bool bUseComprGeom, GByte **, int *);
};

/************************************************************************/
/*                         OGRSQLiteTableLayer                          */
/************************************************************************/

class OGRSQLiteTableLayer final : public OGRSQLiteLayer
{
    bool m_bIsTable = true;

    bool m_bLaunderColumnNames = true;
    bool m_bSpatialite2D = false;
    bool m_bStrict = false;

    CPLString m_osWHERE{};
    CPLString m_osQuery{};
    bool m_bDeferredSpatialIndexCreation = false;

    char *m_pszTableName = nullptr;
    char *m_pszEscapedTableName = nullptr;

    mutable bool m_bLayerDefnError = false;

    sqlite3_stmt *m_hInsertStmt = nullptr;
    CPLString m_osLastInsertStmt{};

    bool m_bHasCheckedTriggers = false;
    bool m_bHasTriedDetectingFID64 = false;

    bool m_bStatisticsNeedsToBeFlushed = false;
    GIntBig m_nFeatureCount = -1; /* if -1, means not up-to-date */

    int m_bDeferredCreation = false;

    char *m_pszCreationGeomFormat = nullptr;
    int m_iFIDAsRegularColumnIndex = -1;

    void ClearInsertStmt();

    void BuildWhere();

    OGRErr ResetStatement() override;

    void BuildLayerDefn();

    OGRErr RecomputeOrdinals();

    void AddColumnDef(char *pszNewFieldList, size_t nBufLen,
                      OGRFieldDefn *poFldDefn);

    void InitFieldListForRecrerate(char *&pszNewFieldList,
                                   char *&pszFieldListForSelect,
                                   size_t &nBufLenOut, int nExtraSpace = 0);
    OGRErr RecreateTable(const char *pszFieldListForSelect,
                         const char *pszNewFieldList,
                         const char *pszGenericErrorMessage,
                         const char *pszAdditionalDef = nullptr);
    OGRErr BindValues(OGRFeature *poFeature, sqlite3_stmt *hStmt,
                      bool bBindUnsetAsNull);

    bool CheckSpatialIndexTable(int iGeomCol);

    CPLErr EstablishFeatureDefn(const char *pszGeomCol, bool bMayEmitError);

    void LoadStatistics();
    void LoadStatisticsSpatialite4DB();

    CPLString FieldDefnToSQliteFieldDefn(OGRFieldDefn *poFieldDefn);

    OGRErr RunAddGeometryColumn(const OGRSQLiteGeomFieldDefn *poGeomField,
                                bool bAddColumnsForNonSpatialite);

    CPL_DISALLOW_COPY_ASSIGN(OGRSQLiteTableLayer)

  public:
    explicit OGRSQLiteTableLayer(OGRSQLiteDataSource *);
    ~OGRSQLiteTableLayer() override;

    CPLErr Initialize(const char *pszTableName, bool bIsTable,
                      bool bIsVirtualShapeIn, bool bDeferredCreation,
                      bool bMayEmitError);
    void SetCreationParameters(const char *pszFIDColumnName,
                               OGRwkbGeometryType eGeomType,
                               const char *pszGeomFormat,
                               const char *pszGeometryName,
                               OGRSpatialReference *poSRS, int nSRSId);
    const char *GetName() const override;

    GIntBig GetFeatureCount(int) override;

    OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
                      bool bForce) override;

    const OGRFeatureDefn *GetLayerDefn() const override;

    bool HasLayerDefnError() const
    {
        GetLayerDefn();
        return m_bLayerDefnError;
    }

    virtual OGRErr ISetSpatialFilter(int iGeomField,
                                     const OGRGeometry *) override;
    OGRErr SetAttributeFilter(const char *) override;
    OGRErr ISetFeature(OGRFeature *poFeature) override;
    OGRErr DeleteFeature(GIntBig nFID) override;
    OGRErr ICreateFeature(OGRFeature *poFeature) override;

    virtual OGRErr CreateField(const OGRFieldDefn *poField,
                               int bApproxOK = TRUE) override;
    virtual OGRErr CreateGeomField(const OGRGeomFieldDefn *poGeomFieldIn,
                                   int bApproxOK = TRUE) override;
    OGRErr DeleteField(int iField) override;
    OGRErr ReorderFields(int *panMap) override;
    virtual OGRErr AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
                                  int nFlags) override;
    OGRErr AddForeignKeysToTable(const char *pszKeys);

    OGRFeature *GetNextFeature() override;
    OGRFeature *GetFeature(GIntBig nFeatureId) override;

    int TestCapability(const char *) const override;

    CSLConstList GetMetadata(const char *pszDomain = "") override;
    virtual const char *GetMetadataItem(const char *pszName,
                                        const char *pszDomain = "") override;

    // follow methods are not base class overrides
    void SetLaunderFlag(bool bFlag)
    {
        m_bLaunderColumnNames = bFlag;
    }

    void SetUseCompressGeom(bool bFlag)
    {
        m_bUseComprGeom = bFlag;
    }

    void SetDeferredSpatialIndexCreation(bool bFlag)
    {
        m_bDeferredSpatialIndexCreation = bFlag;
    }

    void SetCompressedColumns(const char *pszCompressedColumns);

    void SetStrictFlag(bool bFlag)
    {
        m_bStrict = bFlag;
    }

    int CreateSpatialIndex(int iGeomCol);

    void CreateSpatialIndexIfNecessary();

    void InitFeatureCount();
    bool DoStatisticsNeedToBeFlushed();
    void ForceStatisticsToBeFlushed();
    bool AreStatisticsValid();
    int SaveStatistics();

    void InvalidateCachedFeatureCountAndExtent() override;

    bool IsTableLayer() override
    {
        return true;
    }

    bool HasSpatialIndex(int iGeomField) const override;
    bool HasFastSpatialFilter(int iGeomCol) override;
    virtual CPLString GetSpatialWhere(int iGeomCol,
                                      OGRGeometry *poFilterGeom) override;

    OGRErr RunDeferredCreationIfNecessary();
};

/************************************************************************/
/*                          OGRSQLiteViewLayer                          */
/************************************************************************/

class OGRSQLiteViewLayer final : public OGRSQLiteLayer
{
    CPLString m_osWHERE{};
    CPLString m_osQuery{};
    bool m_bHasCheckedSpatialIndexTable = false;

    OGRSQLiteGeomFormat m_eGeomFormat = OSGF_Unknown;
    CPLString m_osGeomColumn{};
    bool m_bHasSpatialIndex = false;

    char *m_pszViewName = nullptr;
    char *m_pszEscapedTableName = nullptr;
    char *m_pszEscapedUnderlyingTableName = nullptr;

    mutable bool m_bLayerDefnError = false;

    CPLString m_osUnderlyingTableName{};
    CPLString m_osUnderlyingGeometryColumn{};

    OGRSQLiteLayer *m_poUnderlyingLayer = nullptr;

    OGRSQLiteLayer *GetUnderlyingLayer();

    void BuildLayerDefn();

    void BuildWhere();

    OGRErr ResetStatement() override;

    CPLErr EstablishFeatureDefn();

    CPL_DISALLOW_COPY_ASSIGN(OGRSQLiteViewLayer)

  public:
    explicit OGRSQLiteViewLayer(OGRSQLiteDataSource *);
    ~OGRSQLiteViewLayer() override;

    const char *GetName() const override
    {
        return m_pszViewName;
    }

    OGRwkbGeometryType GetGeomType() const override;

    CPLErr Initialize(const char *pszViewName, const char *pszViewGeometry,
                      const char *pszViewRowid, const char *pszTableName,
                      const char *pszGeometryColumn);

    const OGRFeatureDefn *GetLayerDefn() const override;

    bool HasLayerDefnError() const
    {
        GetLayerDefn();
        return m_bLayerDefnError;
    }

    OGRFeature *GetNextFeature() override;
    GIntBig GetFeatureCount(int) override;

    virtual OGRErr ISetSpatialFilter(int iGeomField,
                                     const OGRGeometry *poGeom) override;

    OGRErr SetAttributeFilter(const char *) override;

    OGRFeature *GetFeature(GIntBig nFeatureId) override;

    int TestCapability(const char *) const override;

    bool HasSpatialIndex(int) const override
    {
        return m_bHasSpatialIndex;
    }

    virtual CPLString GetSpatialWhere(int iGeomCol,
                                      OGRGeometry *poFilterGeom) override;
};

/************************************************************************/
/*                         OGRSQLiteSelectLayer                         */
/************************************************************************/

class OGRSQLiteSelectLayer CPL_NON_FINAL : public OGRSQLiteLayer,
                                           public IOGRSQLiteSelectLayer
{
    OGRSQLiteSelectLayerCommonBehaviour *m_poBehavior = nullptr;
    bool m_bCanReopenBaseDS = false;

    OGRErr ResetStatement() override;

    CPL_DISALLOW_COPY_ASSIGN(OGRSQLiteSelectLayer)

  public:
    OGRSQLiteSelectLayer(OGRSQLiteDataSource *, const CPLString &osSQL,
                         sqlite3_stmt *, bool bUseStatementForGetNextFeature,
                         bool bEmptyLayer, bool bAllowMultipleGeomFields,
                         bool bCanReopenBaseDS);
    ~OGRSQLiteSelectLayer() override;

    void ResetReading() override;

    OGRFeature *GetNextFeature() override;
    GIntBig GetFeatureCount(int) override;

    virtual OGRErr ISetSpatialFilter(int iGeomField,
                                     const OGRGeometry *) override;
    OGRErr SetAttributeFilter(const char *) override;

    int TestCapability(const char *) const override;

    OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
                      bool bForce) override;

    const OGRFeatureDefn *GetLayerDefn() const override
    {
        return OGRSQLiteLayer::GetLayerDefn();
    }

    char *&GetAttrQueryString() override
    {
        return m_pszAttrQueryString;
    }

    OGRFeatureQuery *&GetFeatureQuery() override
    {
        return m_poAttrQuery;
    }

    OGRGeometry *&GetFilterGeom() override
    {
        return m_poFilterGeom;
    }

    int &GetIGeomFieldFilter() override
    {
        return m_iGeomFieldFilter;
    }

    const OGRSpatialReference *GetSpatialRef() const override
    {
        return OGRSQLiteLayer::GetSpatialRef();
    }

    int InstallFilter(const OGRGeometry *poGeomIn) override
    {
        return OGRSQLiteLayer::InstallFilter(poGeomIn);
    }

    int HasReadFeature() override
    {
        return m_iNextShapeId > 0;
    }

    void BaseResetReading() override
    {
        OGRSQLiteLayer::ResetReading();
    }

    OGRFeature *BaseGetNextFeature() override
    {
        return OGRSQLiteLayer::GetNextFeature();
    }

    OGRErr BaseSetAttributeFilter(const char *pszQuery) override
    {
        return OGRSQLiteLayer::SetAttributeFilter(pszQuery);
    }

    GIntBig BaseGetFeatureCount(int bForce) override
    {
        return OGRSQLiteLayer::GetFeatureCount(bForce);
    }

    int BaseTestCapability(const char *pszCap) const override
    {
        return OGRSQLiteLayer::TestCapability(pszCap);
    }

    virtual OGRErr BaseGetExtent(int iGeomField, OGREnvelope *psExtent,
                                 bool bForce) override
    {
        return OGRSQLiteLayer::IGetExtent(iGeomField, psExtent, bForce);
    }

    bool
    ValidateGeometryFieldIndexForSetSpatialFilter(int iGeomField,
                                                  const OGRGeometry *poGeomIn,
                                                  bool bIsSelectLayer) override
    {
        return OGRSQLiteLayer::ValidateGeometryFieldIndexForSetSpatialFilter(
            iGeomField, poGeomIn, bIsSelectLayer);
    }
};

/************************************************************************/
/*                         OGRSQLiteDataSource                          */
/************************************************************************/

class OGR2SQLITEModule;

class OGRSQLiteDataSource final : public OGRSQLiteBaseDataSource
{
    std::vector<std::unique_ptr<OGRSQLiteLayer>> m_apoLayers{};

    // We maintain a list of known SRID to reduce the number of trips to
    // the database to get SRSes.
    std::map<int, OGRSpatialReferenceRefCountedPtr> m_oSRSCache{};

    OGRSpatialReference *AddSRIDToCache(int nId,
                                        OGRSpatialReferenceRefCountedPtr poSRS);

    bool m_bHaveGeometryColumns = false;
    bool m_bIsSpatiaLiteDB = false;
    bool m_bSpatialite4Layout = false;

    int m_nUndefinedSRID = -1;

    void DeleteLayer(const char *pszLayer);

    const char *GetSRTEXTColName();

    bool InitWithEPSG();

    bool OpenVirtualTable(const char *pszName, const char *pszSQL);

    GIntBig m_nFileTimestamp = 0;
    bool m_bLastSQLCommandIsUpdateLayerStatistics = false;

    std::map<CPLString, std::set<CPLString>> m_aoMapTableToSetOfGeomCols{};

    void SaveStatistics();

    std::vector<std::unique_ptr<OGRLayer>> m_apoInvisibleLayers{};

#ifdef HAVE_RASTERLITE2
    void *m_hRL2Ctxt = nullptr;
    bool InitRasterLite2();
    void FinishRasterLite2();

    CPLString m_osCoverageName{};
    GIntBig m_nSectionId = -1;
    rl2CoveragePtr m_pRL2Coverage = nullptr;
    bool m_bRL2MixedResolutions = false;
#endif
    CPLStringList m_aosSubDatasets{};
    bool m_bGeoTransformValid = false;
    GDALGeoTransform m_gt{};
    OGRSpatialReference m_oSRS{};
    bool m_bPromote1BitAs8Bit = false;
    bool OpenRaster();
    bool OpenRasterSubDataset(const char *pszConnectionId);
    OGRSQLiteDataSource *m_poParentDS = nullptr;
    std::vector<OGRSQLiteDataSource *> m_apoOverviewDS{};

    OGR2SQLITEModule *m_poSQLiteModule = nullptr;

#ifdef HAVE_RASTERLITE2
    void ListOverviews();
    void CreateRL2OverviewDatasetIfNeeded(double dfXRes, double dfYRes);
#endif

    bool OpenOrCreateDB(int flags, bool bRegisterOGR2SQLiteExtensions);

    CPLErr Close(GDALProgressFunc = nullptr, void * = nullptr) override;

    void PostInitSpatialite();

    CPL_DISALLOW_COPY_ASSIGN(OGRSQLiteDataSource)

  public:
    OGRSQLiteDataSource();
    ~OGRSQLiteDataSource() override;

    bool Open(GDALOpenInfo *poOpenInfo);
    bool Create(const char *, CSLConstList papszOptions);

    bool OpenTable(const char *pszTableName, bool IsTable, bool bIsVirtualShape,
                   bool bMayEmitError);
    bool OpenView(const char *pszViewName, const char *pszViewGeometry,
                  const char *pszViewRowid, const char *pszTableName,
                  const char *pszGeometryColumn);

    int GetLayerCount() const override
    {
        return static_cast<int>(m_apoLayers.size());
    }

    using GDALDataset::GetLayer;
    const OGRLayer *GetLayer(int) const override;
    OGRLayer *GetLayerByName(const char *) override;
    bool IsLayerPrivate(int) const override;
    OGRLayer *GetLayerByNameNotVisible(const char *);
    virtual std::pair<OGRLayer *, IOGRSQLiteGetSpatialWhere *>
    GetLayerWithGetSpatialWhereByName(const char *pszName) override;

    virtual OGRLayer *ICreateLayer(const char *pszName,
                                   const OGRGeomFieldDefn *poGeomFieldDefn,
                                   CSLConstList papszOptions) override;
    OGRErr DeleteLayer(int) override;

    int TestCapability(const char *) const override;

    OGRLayer *ExecuteSQL(const char *pszSQLCommand,
                         OGRGeometry *poSpatialFilter,
                         const char *pszDialect) override;
    void ReleaseResultSet(OGRLayer *poLayer) override;

    CPLErr FlushCache(bool bAtClosing) override;

    OGRErr StartTransaction(int bForce = FALSE) override;
    OGRErr CommitTransaction() override;
    OGRErr RollbackTransaction() override;

    CSLConstList GetMetadata(const char *pszDomain = "") override;

    CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
    const OGRSpatialReference *GetSpatialRef() const override;

    static char *LaunderName(const char *);
    int FetchSRSId(const OGRSpatialReference *poSRS);
    OGRSpatialReference *FetchSRS(int nSRID);

    void DisableUpdate()
    {
        eAccess = GA_ReadOnly;
    }

    void SetName(const char *pszNameIn);

    const std::set<CPLString> &GetGeomColsForTable(const char *pszTableName)
    {
        return m_aoMapTableToSetOfGeomCols[pszTableName];
    }

    GIntBig GetFileTimestamp() const
    {
        return m_nFileTimestamp;
    }

    bool IsSpatialiteDB() const
    {
        return m_bIsSpatiaLiteDB;
    }

    bool HasSpatialite4Layout() const
    {
        return m_bSpatialite4Layout;
    }

    int GetUndefinedSRID() const
    {
        return m_nUndefinedSRID;
    }

    bool HasGeometryColumns() const
    {
        return m_bHaveGeometryColumns;
    }

    bool AddRelationship(std::unique_ptr<GDALRelationship> &&relationship,
                         std::string &failureReason) override;
    bool ValidateRelationship(const GDALRelationship *poRelationship,
                              std::string &failureReason);

    void ReloadLayers();

#ifdef HAVE_RASTERLITE2
    void *GetRL2Context() const
    {
        return m_hRL2Ctxt;
    }

    rl2CoveragePtr GetRL2CoveragePtr() const
    {
        return m_pRL2Coverage;
    }

    GIntBig GetSectionId() const
    {
        return m_nSectionId;
    }

    const GDALGeoTransform &GetGeoTransform() const
    {
        return m_gt;
    }

    bool IsRL2MixedResolutions() const
    {
        return m_bRL2MixedResolutions;
    }

    CPLErr IBuildOverviews(const char *, int, const int *, int, const int *,
                           GDALProgressFunc, void *,
                           CSLConstList papszOptions) override;

#endif
    OGRSQLiteDataSource *GetParentDS() const
    {
        return m_poParentDS;
    }

    const std::vector<OGRSQLiteDataSource *> &GetOverviews() const
    {
        return m_apoOverviewDS;
    }

    bool HasPromote1BitAS8Bit() const
    {
        return m_bPromote1BitAs8Bit;
    }
};

#ifdef HAVE_RASTERLITE2
/************************************************************************/
/*                            RL2RasterBand                             */
/************************************************************************/

class RL2RasterBand final : public GDALPamRasterBand
{
    bool m_bHasNoData = false;
    double m_dfNoDataValue = 0;
    GDALColorInterp m_eColorInterp = GCI_Undefined;
    GDALColorTable *m_poCT = nullptr;

    CPL_DISALLOW_COPY_ASSIGN(RL2RasterBand)

  public:
    RL2RasterBand(int nBandIn, int nPixelType, GDALDataType eDT, int nBits,
                  bool bPromote1BitAs8Bit, int nBlockXSizeIn, int nBlockYSizeIn,
                  bool bHasNoDataIn, double dfNoDataValueIn);
    explicit RL2RasterBand(const RL2RasterBand *poOther);

    ~RL2RasterBand() override;

  protected:
    CPLErr IReadBlock(int, int, void *) override;

    GDALColorInterp GetColorInterpretation() override
    {
        return m_eColorInterp;
    }

    double GetNoDataValue(int *pbSuccess = nullptr) override;
    GDALColorTable *GetColorTable() override;
    int GetOverviewCount() override;
    GDALRasterBand *GetOverview(int) override;
};
#endif  // HAVE_RASTERLITE2

CPLString OGRSQLiteFieldDefnToSQliteFieldDefn(const OGRFieldDefn *poFieldDefn,
                                              bool bSQLiteDialectInternalUse,
                                              bool bStrict);

void OGRSQLiteRegisterInflateDeflate(sqlite3 *hDB);

void OGRSQLiteDriverUnload(GDALDriver *);

#ifdef HAVE_RASTERLITE2
GDALDataset *OGRSQLiteDriverCreateCopy(const char *, GDALDataset *, int,
                                       CSLConstList,
                                       GDALProgressFunc pfnProgress,
                                       void *pProgressData);
#endif

#endif /* ndef OGR_SQLITE_H_INCLUDED */
