text:
#include "ogrsqlitevirtualogr.h"
#include "ogr_api.h"
#include "swq.h"
#include <map>
#include <vector>

#ifdef HAVE_SQLITE_VFS

#define VIRTUAL_OGR_DYNAMIC_EXTENSION_ENABLED
//#define DEBUG_OGR2SQLITE

#if defined(SPATIALITE_AMALGAMATION)
#include "ogrsqlite3ext.h"
#else
#include "sqlite3ext.h"
#endif

/* Declaration of sqlite3_api structure */
static SQLITE_EXTENSION_INIT1

/* The layout of fields is :
   0   : RegularField0
   ...
   n-1 : RegularField(n-1)
   n   : OGR_STYLE (may be HIDDEN)
   n+1 : GEOMETRY
*/

#define COMPILATION_ALLOWED
#include "ogrsqlitesqlfunctions.cpp" /* yes the .cpp file, to make it work on Windows with load_extension('gdalXX.dll') */
#undef COMPILATION_ALLOWED

/************************************************************************/
/*                           OGR2SQLITEModule                           */
/************************************************************************/

class OGR2SQLITEModule
{
#ifdef DEBUG
    void* pDummy; /* to track memory leaks */
#endif
    sqlite3* hDB; /* *NOT* to be freed */

    OGRDataSource* poDS; /* *NOT* to be freed */
    std::vector<OGRDataSource*> apoExtraDS; /* each datasource to be freed */

    OGRSQLiteDataSource* poSQLiteDS;  /* *NOT* to be freed, might be NULL */

    std::map< CPLString, OGRLayer* > oMapVTableToOGRLayer;

    void* hHandleSQLFunctions;

  public:
                                 OGR2SQLITEModule();
                                ~OGR2SQLITEModule();

    int                          Setup(OGRDataSource* poDS,
                                       OGRSQLiteDataSource* poSQLiteDS);
    int                          Setup(sqlite3* hDB);

    OGRDataSource*               GetDS() { return poDS; }

    int                          AddExtraDS(OGRDataSource* poDS);
    OGRDataSource               *GetExtraDS(int nIndex);

    int                          FetchSRSId(OGRSpatialReference* poSRS);

    void                         RegisterVTable(const char* pszVTableName, OGRLayer* poLayer);
    void                         UnregisterVTable(const char* pszVTableName);
    OGRLayer*                    GetLayerForVTable(const char* pszVTableName);

    void                         SetHandleSQLFunctions(void* hHandleSQLFunctionsIn);
};

/************************************************************************/
/*                        OGR2SQLITEModule()                            */
/************************************************************************/

OGR2SQLITEModule::OGR2SQLITEModule() :
    hDB(NULL), poDS(NULL), poSQLiteDS(NULL), hHandleSQLFunctions(NULL)
{
#ifdef DEBUG
    pDummy = CPLMalloc(1);
#endif
}

/************************************************************************/
/*                          ~OGR2SQLITEModule                           */
/************************************************************************/

OGR2SQLITEModule::~OGR2SQLITEModule()
{
#ifdef DEBUG
    CPLFree(pDummy);
#endif

    for(int i=0;i<(int)apoExtraDS.size();i++)
        delete apoExtraDS[i];

    OGRSQLiteUnregisterSQLFunctions(hHandleSQLFunctions);
}

/************************************************************************/
/*                        SetHandleSQLFunctions()                       */
/************************************************************************/

void OGR2SQLITEModule::SetHandleSQLFunctions(void* hHandleSQLFunctionsIn)
{
    CPLAssert(hHandleSQLFunctions == NULL);
    hHandleSQLFunctions = hHandleSQLFunctionsIn;
}

/************************************************************************/
/*                            AddExtraDS()                              */
/************************************************************************/

int OGR2SQLITEModule::AddExtraDS(OGRDataSource* poDS)
{
    int nRet = (int)apoExtraDS.size();
    apoExtraDS.push_back(poDS);
    return nRet;
}

/************************************************************************/
/*                            GetExtraDS()                              */
/************************************************************************/

OGRDataSource* OGR2SQLITEModule::GetExtraDS(int nIndex)
{
    if( nIndex < 0 || nIndex >= (int)apoExtraDS.size() )
        return NULL;
    return apoExtraDS[nIndex];
}

/************************************************************************/
/*                                Setup()                               */
/************************************************************************/

int OGR2SQLITEModule::Setup(OGRDataSource* poDSIn,
                            OGRSQLiteDataSource* poSQLiteDSIn)
{
    CPLAssert(poDS == NULL);
    CPLAssert(poSQLiteDS == NULL);
    poDS = poDSIn;
    poSQLiteDS = poSQLiteDSIn;
    return Setup(poSQLiteDS->GetDB());
}

/************************************************************************/
/*                            FetchSRSId()                              */
/************************************************************************/

int OGR2SQLITEModule::FetchSRSId(OGRSpatialReference* poSRS)
{
    int nSRSId;

    if( poSQLiteDS != NULL )
    {
        nSRSId = poSQLiteDS->GetUndefinedSRID();
        if( poSRS != NULL )
            nSRSId = poSQLiteDS->FetchSRSId(poSRS);
    }
    else
    {
        nSRSId = -1;
        if( poSRS != NULL )
        {
            const char* pszAuthorityName = poSRS->GetAuthorityName(NULL);
            if (pszAuthorityName != NULL && EQUAL(pszAuthorityName, "EPSG"))
            {
                const char* pszAuthorityCode = poSRS->GetAuthorityCode(NULL);
                if ( pszAuthorityCode != NULL && strlen(pszAuthorityCode) > 0 )
                {
                    nSRSId = atoi(pszAuthorityCode);
                }
            }
        }
    }

    return nSRSId;
}

/************************************************************************/
/*                          RegisterVTable()                            */
/************************************************************************/

void OGR2SQLITEModule::RegisterVTable(const char* pszVTableName,
                                      OGRLayer* poLayer)
{
    oMapVTableToOGRLayer[pszVTableName] = poLayer;
}

/************************************************************************/
/*                          UnregisterVTable()                          */
/************************************************************************/

void OGR2SQLITEModule::UnregisterVTable(const char* pszVTableName)
{
    oMapVTableToOGRLayer[pszVTableName] = NULL;
}

/************************************************************************/
/*                          GetLayerForVTable()                         */
/************************************************************************/

OGRLayer* OGR2SQLITEModule::GetLayerForVTable(const char* pszVTableName)
{
    std::map<CPLString, OGRLayer*>::iterator oIter =
        oMapVTableToOGRLayer.find(pszVTableName);
    if( oIter == oMapVTableToOGRLayer.end() )
        return NULL;

    OGRLayer* poLayer = oIter->second;
    if( poLayer == NULL )
    {
        /* If the associate layer is null, then try to "ping" the virtual */
        /* table since we know that we have managed to create it before */
        if( sqlite3_exec(hDB,
                     CPLSPrintf("PRAGMA table_info(\"%s\")",
                                OGRSQLiteEscapeName(pszVTableName).c_str()),
                     NULL, NULL, NULL) == SQLITE_OK )
        {
            poLayer = oMapVTableToOGRLayer[pszVTableName];
        }
    }

    return poLayer;
}

/* See http://www.sqlite.org/vtab.html for the documentation on how to
   implement a new module for the Virtual Table mechanism. */

/************************************************************************/
/*                            OGR2SQLITE_vtab                           */
/************************************************************************/

typedef struct
{
    /* Mandatory fields by sqlite3: don't change or reorder them ! */
    const sqlite3_module *pModule;
    int                   nRef;
    char                 *zErrMsg;

    /* Extension fields */
    char                 *pszVTableName;
    OGR2SQLITEModule     *poModule;
    OGRDataSource        *poDS;
    int                   bCloseDS;
    OGRLayer             *poLayer;
    int                   nMyRef;
} OGR2SQLITE_vtab;

/************************************************************************/
/*                          OGR2SQLITE_vtab_cursor                      */
/************************************************************************/

typedef struct
{
    /* Mandatory fields by sqlite3: don't change or reorder them ! */
    OGR2SQLITE_vtab *pVTab;

    /* Extension fields */
    OGRDataSource *poDupDataSource;
    OGRLayer      *poLayer;
    OGRFeature    *poFeature;

    /* nFeatureCount >= 0 if the layer has a feast feature count capability. */
    /* In which case nNextWishedIndex and nCurFeatureIndex */
    /* will be used to avoid useless GetNextFeature() */
    /* Helps in SELECT COUNT(*) FROM xxxx scenarios */
    int            nFeatureCount;
    int            nNextWishedIndex;
    int            nCurFeatureIndex;

    GByte         *pabyGeomBLOB;
    int            nGeomBLOBLen;
} OGR2SQLITE_vtab_cursor;

/************************************************************************/
/*                  OGR2SQLITE_GetNameForGeometryColumn()               */
/************************************************************************/

CPLString OGR2SQLITE_GetNameForGeometryColumn(OGRLayer* poLayer)
{
    if( poLayer->GetGeometryColumn() != NULL &&
        !EQUAL(poLayer->GetGeometryColumn(), "") )
    {
        return poLayer->GetGeometryColumn();
    }
    else
    {
        CPLString osGeomCol("GEOMETRY");
        int bTry = 2;
        while( poLayer->GetLayerDefn()->GetFieldIndex(osGeomCol) >= 0 )
        {
            osGeomCol.Printf("GEOMETRY%d", bTry++);
        }
        return osGeomCol;
    }
}

#ifdef VIRTUAL_OGR_DYNAMIC_EXTENSION_ENABLED

/************************************************************************/
/*                     OGR2SQLITEDetectSuspiciousUsage()                */
/************************************************************************/

static int OGR2SQLITEDetectSuspiciousUsage(sqlite3* hDB,
                                           const char* pszVirtualTableName,
                                           char**pzErr)
{
    char **papszResult = NULL;
    int nRowCount = 0, nColCount = 0;
    int i;

    std::vector<CPLString> aosDatabaseNames;

    /* Collect database names */
    sqlite3_get_table( hDB, "PRAGMA database_list",
                       &papszResult, &nRowCount, &nColCount, NULL );

    for(i = 1; i <= nRowCount; i++)
    {
        const char* pszUnescapedName = papszResult[i * nColCount + 1];
        aosDatabaseNames.push_back(
            CPLSPrintf("\"%s\".sqlite_master",
                       OGRSQLiteEscapeName(pszUnescapedName).c_str()));
    }

    /* Add special database (just in case, not sure it is really needed) */
    aosDatabaseNames.push_back("sqlite_temp_master");

    sqlite3_free_table(papszResult);
    papszResult = NULL;

    /* Check the triggers of each database */
    for(i = 0; i < (int)aosDatabaseNames.size(); i++ )
    {
        nRowCount = 0; nColCount = 0;

        const char* pszSQL;

        pszSQL = CPLSPrintf("SELECT name, sql FROM %s "
                            "WHERE (type = 'trigger' OR type = 'view') AND ("
                            "sql LIKE '%%%s%%' OR "
                            "sql LIKE '%%\"%s\"%%' OR "
                            "sql LIKE '%%ogr_layer_%%' )",
                            aosDatabaseNames[i].c_str(),
                            pszVirtualTableName,
                            OGRSQLiteEscapeName(pszVirtualTableName).c_str());

        sqlite3_get_table( hDB, pszSQL, &papszResult, &nRowCount, &nColCount,
                           NULL );

        sqlite3_free_table(papszResult);
        papszResult = NULL;

        if( nRowCount > 0 )
        {
            if( !CSLTestBoolean(CPLGetConfigOption("ALLOW_VIRTUAL_OGR_FROM_TRIGGER_AND_VIEW", "NO")) )
            {
                *pzErr = sqlite3_mprintf(
                    "A trigger and/or view might reference VirtualOGR table '%s'.\n"
                    "This is suspicious practice that could be used to steal data without your consent.\n"
                    "Disabling access to it unless you define the ALLOW_VIRTUAL_OGR_FROM_TRIGGER_AND_VIEW "
                    "configuration option to YES.",
                    pszVirtualTableName);
                return TRUE;
            }
        }
    }

    return FALSE;
}

#endif // VIRTUAL_OGR_DYNAMIC_EXTENSION_ENABLED

/************************************************************************/
/*                      OGR2SQLITE_ConnectCreate()                      */
/************************************************************************/

static
int OGR2SQLITE_DisconnectDestroy(sqlite3_vtab *pVTab);

static
int OGR2SQLITE_ConnectCreate(sqlite3* hDB, void *pAux,
                             int argc, const char *const*argv,
                             sqlite3_vtab **ppVTab, char**pzErr)
{
    OGR2SQLITEModule* poModule = (OGR2SQLITEModule*) pAux;
    OGRLayer* poLayer = NULL;
    OGRDataSource* poDS = NULL;
    int bExposeOGR_STYLE = FALSE;
    int bCloseDS = FALSE;
    int bInternalUse = FALSE;
    int i;

#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "ConnectCreate(%s)", argv[2]);
#endif

    /*for(i=0;i<argc;i++)
        printf("[%d] %s\n", i, argv[i]);*/

/* -------------------------------------------------------------------- */
/*      If called from ogrexecutesql.cpp                                */
/* -------------------------------------------------------------------- */
    poDS = poModule->GetDS();
    if( poDS != NULL && argc == 6 &&
        CPLGetValueType(argv[3]) == CPL_VALUE_INTEGER )
    {
        bInternalUse = TRUE;
        if( argc != 6 )
        {
            *pzErr = sqlite3_mprintf(
                "Expected syntax: CREATE VIRTUAL TABLE xxx USING "
                "VirtualOGR(ds_idx, layer_name, expose_ogr_style)");
            return SQLITE_ERROR;
        }

        int nDSIndex = atoi(argv[3]);
        if( nDSIndex >= 0 )
        {
            poDS = poModule->GetExtraDS(nDSIndex);
            if( poDS == NULL )
            {
                *pzErr = sqlite3_mprintf("Invalid dataset index : %d", nDSIndex);
                return SQLITE_ERROR;
            }
        }
        CPLString osLayerName(OGRSQLiteParamsUnquote(argv[4]));

        poLayer = poDS->GetLayerByName(osLayerName);
        if( poLayer == NULL )
        {
            *pzErr = sqlite3_mprintf( "Cannot find layer '%s' in '%s'",
                                      osLayerName.c_str(), poDS->GetName() );
            return SQLITE_ERROR;
        }

        bExposeOGR_STYLE = atoi(OGRSQLiteParamsUnquote(argv[5]));
    }
#ifdef VIRTUAL_OGR_DYNAMIC_EXTENSION_ENABLED
/* -------------------------------------------------------------------- */
/*      If called from outside (OGR loaded as a sqlite3 extension)      */
/* -------------------------------------------------------------------- */
    else
    {
        if( argc < 4 || argc > 7 )
        {
            *pzErr = sqlite3_mprintf(
                "Expected syntax: CREATE VIRTUAL TABLE xxx USING "
                "VirtualOGR(datasource_name[, update_mode, [layer_name[, expose_ogr_style]]])");
            return SQLITE_ERROR;
        }

        if( OGR2SQLITEDetectSuspiciousUsage(hDB, argv[2], pzErr) )
        {
            return SQLITE_ERROR;
        }

        CPLString osDSName(OGRSQLiteParamsUnquote(argv[3]));
        CPLString osUpdate(OGRSQLiteParamsUnquote((argc >= 5) ? argv[4] : "0"));

        if( !EQUAL(osUpdate, "1") && !EQUAL(osUpdate, "0") )
        {
            *pzErr = sqlite3_mprintf(
                "update_mode parameter should be 0 or 1");
            return SQLITE_ERROR;
        }

        int bUpdate = atoi(osUpdate);

        poDS = (OGRDataSource* )OGROpenShared(osDSName, bUpdate, NULL);
        if( poDS == NULL )
        {
            *pzErr = sqlite3_mprintf( "Cannot open datasource '%s'", osDSName.c_str() );
            return SQLITE_ERROR;
        }

        CPLString osLayerName;
        if( argc >= 6 )
        {
            osLayerName = OGRSQLiteParamsUnquote(argv[5]);
            poLayer = poDS->GetLayerByName(osLayerName);
        }
        else
        {
            if( poDS->GetLayerCount() == 0 )
            {
                *pzErr = sqlite3_mprintf( "Datasource '%s' has no layers",
                                          osDSName.c_str() );
                poDS->Release();
                return SQLITE_ERROR;
            }

            if( poDS->GetLayerCount() > 1 )
            {
                *pzErr = sqlite3_mprintf( "Datasource '%s' has more than one layers, and none was explicitely selected.",
                                          osDSName.c_str() );
                poDS->Release();
                return SQLITE_ERROR;
            }

            poLayer = poDS->GetLayer(0);
        }

        if( poLayer == NULL )
        {
            *pzErr = sqlite3_mprintf( "Cannot find layer '%s' in '%s'", osLayerName.c_str(), osDSName.c_str() );
            poDS->Release();
            return SQLITE_ERROR;
        }

        if( argc == 7 )
        {
            bExposeOGR_STYLE = atoi(OGRSQLiteParamsUnquote(argv[6]));
        }

        bCloseDS = TRUE;
    }
#endif // VIRTUAL_OGR_DYNAMIC_EXTENSION_ENABLED
    OGR2SQLITE_vtab* vtab =
                (OGR2SQLITE_vtab*) CPLCalloc(1, sizeof(OGR2SQLITE_vtab));
    /* We dont need to fill the non-extended fields */
    vtab->pszVTableName = CPLStrdup(OGRSQLiteEscapeName(argv[2]));
    vtab->poModule = poModule;
    vtab->poDS = poDS;
    vtab->bCloseDS = bCloseDS;
    vtab->poLayer = poLayer;
    vtab->nMyRef = 0;

    poModule->RegisterVTable(vtab->pszVTableName, poLayer);

    *ppVTab = (sqlite3_vtab*) vtab;

    CPLString osSQL;
    osSQL = "CREATE TABLE ";
    osSQL += "\"";
    osSQL += OGRSQLiteEscapeName(argv[2]);
    osSQL += "\"";
    osSQL += "(";

    int bAddComma = FALSE;

    OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn();
    for(i=0;i<poFDefn->GetFieldCount();i++)
    {
        if( bAddComma )
            osSQL += ",";
        bAddComma = TRUE;

        OGRFieldDefn* poFieldDefn = poFDefn->GetFieldDefn(i);

        osSQL += "\"";
        osSQL += OGRSQLiteEscapeName(poFieldDefn->GetNameRef());
        osSQL += "\"";
        osSQL += " ";
        osSQL += OGRSQLiteFieldDefnToSQliteFieldDefn(poFieldDefn);
    }

    if( bAddComma )
        osSQL += ",";
    bAddComma = TRUE;
    osSQL += "OGR_STYLE VARCHAR";
    if( !bExposeOGR_STYLE )
     osSQL += " HIDDEN";

    for(i=0;i<poFDefn->GetGeomFieldCount();i++)
    {
        if( bAddComma )
            osSQL += ",";
        bAddComma = TRUE;

        OGRGeomFieldDefn* poFieldDefn = poFDefn->GetGeomFieldDefn(i);

        osSQL += "\"";
        if( i == 0 )
            osSQL += OGRSQLiteEscapeName(OGR2SQLITE_GetNameForGeometryColumn(poLayer));
        else
            osSQL += OGRSQLiteEscapeName(poFieldDefn->GetNameRef());
        osSQL += "\"";
        osSQL += " BLOB";

        /* We use a special column type, e.g. BLOB_POINT_25D_4326 */
        /* when the virtual table is created by OGRSQLiteExecuteSQL() */
        /* and thus for interal use only. */
        if( bInternalUse )
        {
            osSQL += "_";
            osSQL += OGRToOGCGeomType(poFieldDefn->GetType());
            osSQL += "_";
            osSQL += (poFieldDefn->GetType() & wkb25DBit) ? "25D" : "2D";
            OGRSpatialReference* poSRS = poFieldDefn->GetSpatialRef();
            if( poSRS == NULL && i == 0 )
                poSRS = poLayer->GetSpatialRef();
            int nSRID = poModule->FetchSRSId(poSRS);
            if( nSRID >= 0 )
            {
                osSQL += "_";
                osSQL += CPLSPrintf("%d", nSRID);
            }
        }
    }

    osSQL += ")";

    CPLDebug("OGR2SQLITE", "sqlite3_declare_vtab(%s)", osSQL.c_str());
    if (sqlite3_declare_vtab (hDB, osSQL.c_str()) != SQLITE_OK)
    {
        *pzErr = sqlite3_mprintf("CREATE VIRTUAL: invalid SQL statement : %s",
                                 osSQL.c_str());
        OGR2SQLITE_DisconnectDestroy((sqlite3_vtab*) vtab);
        return SQLITE_ERROR;
    }

    return SQLITE_OK;
}

/************************************************************************/
/*                        OGR2SQLITE_BestIndex()                        */
/************************************************************************/

static
int OGR2SQLITE_BestIndex(sqlite3_vtab *pVTab, sqlite3_index_info* pIndex)
{
    int i;
    OGR2SQLITE_vtab* pMyVTab = (OGR2SQLITE_vtab*) pVTab;
    OGRFeatureDefn* poFDefn = pMyVTab->poLayer->GetLayerDefn();

#ifdef DEBUG_OGR2SQLITE
    CPLString osQueryPatternUsable, osQueryPatternNotUsable;
    for (i = 0; i < pIndex->nConstraint; i++)
    {
        int iCol = pIndex->aConstraint[i].iColumn;
        const char* pszFieldName;
        if( iCol == -1 )
            pszFieldName = "FID";
        else if( iCol >= 0 && iCol < poFDefn->GetFieldCount() )
            pszFieldName = poFDefn->GetFieldDefn(iCol)->GetNameRef();
        else
            pszFieldName = "unknown_field";

        const char* pszOp;
        switch(pIndex->aConstraint[i].op)
        {
            case SQLITE_INDEX_CONSTRAINT_EQ: pszOp = " = "; break;
            case SQLITE_INDEX_CONSTRAINT_GT: pszOp = " > "; break;
            case SQLITE_INDEX_CONSTRAINT_LE: pszOp = " <= "; break;
            case SQLITE_INDEX_CONSTRAINT_LT: pszOp = " < "; break;
            case SQLITE_INDEX_CONSTRAINT_GE: pszOp = " >= "; break;
            case SQLITE_INDEX_CONSTRAINT_MATCH: pszOp = " MATCH "; break;
            default: pszOp = " (unknown op) "; break;
        }

        if (pIndex->aConstraint[i].usable)
        {
            if (osQueryPatternUsable.size()) osQueryPatternUsable += " AND ";
            osQueryPatternUsable += pszFieldName;
            osQueryPatternUsable += pszOp;
            osQueryPatternUsable += "?";
        }
        else
        {
            if (osQueryPatternNotUsable.size()) osQueryPatternNotUsable += " AND ";
            osQueryPatternNotUsable += pszFieldName;
            osQueryPatternNotUsable += pszOp;
            osQueryPatternNotUsable += "?";
        }
    }
    CPLDebug("OGR2SQLITE", "BestIndex, usable ( %s ), not usable ( %s )",
             osQueryPatternUsable.c_str(), osQueryPatternNotUsable.c_str());
#endif

    int nConstraints = 0;
    for (i = 0; i < pIndex->nConstraint; i++)
    {
        int iCol = pIndex->aConstraint[i].iColumn;
        if (pIndex->aConstraint[i].usable &&
            pIndex->aConstraint[i].op != SQLITE_INDEX_CONSTRAINT_MATCH &&
            iCol < poFDefn->GetFieldCount() &&
            (iCol < 0 || poFDefn->GetFieldDefn(iCol)->GetType() != OFTBinary))
        {
            pIndex->aConstraintUsage[i].argvIndex = nConstraints + 1;
            pIndex->aConstraintUsage[i].omit = TRUE;

            nConstraints ++;
        }
        else
        {
            pIndex->aConstraintUsage[i].argvIndex = 0;
            pIndex->aConstraintUsage[i].omit = FALSE;
        }
    }

    int* panConstraints = NULL;

    if( nConstraints )
    {
        panConstraints = (int*)
                    sqlite3_malloc( sizeof(int) * (1 + 2 * nConstraints) );
        panConstraints[0] = nConstraints;

        nConstraints = 0;

        for (i = 0; i < pIndex->nConstraint; i++)
        {
            if (pIndex->aConstraintUsage[i].omit)
            {
                panConstraints[2 * nConstraints + 1] =
                                            pIndex->aConstraint[i].iColumn;
                panConstraints[2 * nConstraints + 2] =
                                            pIndex->aConstraint[i].op;

                nConstraints ++;
            }
        }
    }

    pIndex->orderByConsumed = FALSE;
    pIndex->idxNum = 0;

    if (nConstraints != 0)
    {
        pIndex->idxStr = (char *) panConstraints;
        pIndex->needToFreeIdxStr = TRUE;
    }
    else
    {
        pIndex->idxStr = NULL;
        pIndex->needToFreeIdxStr = FALSE;
    }

    return SQLITE_OK;
}

/************************************************************************/
/*                      OGR2SQLITE_DisconnectDestroy()                  */
/************************************************************************/

static
int OGR2SQLITE_DisconnectDestroy(sqlite3_vtab *pVTab)
{
    OGR2SQLITE_vtab* pMyVTab = (OGR2SQLITE_vtab*) pVTab;

#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "DisconnectDestroy(%s)",pMyVTab->pszVTableName);
#endif

    sqlite3_free(pMyVTab->zErrMsg);
    if( pMyVTab->bCloseDS )
        pMyVTab->poDS->Release();
    pMyVTab->poModule->UnregisterVTable(pMyVTab->pszVTableName);
    CPLFree(pMyVTab->pszVTableName);
    CPLFree(pMyVTab);

    return SQLITE_OK;
}

/************************************************************************/
/*                           OGR2SQLITE_Open()                          */
/************************************************************************/

static
int OGR2SQLITE_Open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor)
{
    OGR2SQLITE_vtab* pMyVTab = (OGR2SQLITE_vtab*) pVTab;
#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "Open(%s, %s)",
             pMyVTab->poDS->GetName(), pMyVTab->poLayer->GetName());
#endif

    OGRDataSource* poDupDataSource = NULL;
    OGRLayer* poLayer = NULL;

    if( pMyVTab->nMyRef == 0 )
    {
        poLayer = pMyVTab->poLayer;
    }
    else
    {
        poDupDataSource =
            (OGRDataSource*) OGROpen(pMyVTab->poDS->GetName(), FALSE, NULL);
        if( poDupDataSource == NULL )
            return SQLITE_ERROR;
        poLayer = poDupDataSource->GetLayerByName(
                                                pMyVTab->poLayer->GetName());
        if( poLayer == NULL )
        {
            delete poDupDataSource;
            return SQLITE_ERROR;
        }
        if( !poLayer->GetLayerDefn()->
                IsSame(pMyVTab->poLayer->GetLayerDefn()) )
        {
            delete poDupDataSource;
            return SQLITE_ERROR;
        }
    }
    pMyVTab->nMyRef ++;

    OGR2SQLITE_vtab_cursor* pCursor = (OGR2SQLITE_vtab_cursor*)
                                CPLCalloc(1, sizeof(OGR2SQLITE_vtab_cursor));
    /* We dont need to fill the non-extended fields */
    *ppCursor = (sqlite3_vtab_cursor *)pCursor;

    pCursor->poDupDataSource = poDupDataSource;
    pCursor->poLayer = poLayer;
    pCursor->poLayer->ResetReading();
    pCursor->poFeature = NULL;
    pCursor->nNextWishedIndex = 0;
    pCursor->nCurFeatureIndex = -1;
    pCursor->nFeatureCount = -1;

    pCursor->pabyGeomBLOB = NULL;
    pCursor->nGeomBLOBLen = -1;

    return SQLITE_OK;
}

/************************************************************************/
/*                           OGR2SQLITE_Close()                         */
/************************************************************************/

static
int OGR2SQLITE_Close(sqlite3_vtab_cursor* pCursor)
{
    OGR2SQLITE_vtab_cursor* pMyCursor = (OGR2SQLITE_vtab_cursor*) pCursor;
    OGR2SQLITE_vtab* pMyVTab = pMyCursor->pVTab;
#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "Close(%s, %s)",
             pMyVTab->poDS->GetName(), pMyVTab->poLayer->GetName());
#endif
    pMyVTab->nMyRef --;

    delete pMyCursor->poFeature;
    delete pMyCursor->poDupDataSource;

    CPLFree(pMyCursor->pabyGeomBLOB);

    CPLFree(pCursor);

    return SQLITE_OK;
}

/************************************************************************/
/*                          OGR2SQLITE_Filter()                         */
/************************************************************************/

static
int OGR2SQLITE_Filter(sqlite3_vtab_cursor* pCursor,
                      int idxNum, const char *idxStr,
                      int argc, sqlite3_value **argv)
{
    OGR2SQLITE_vtab_cursor* pMyCursor = (OGR2SQLITE_vtab_cursor*) pCursor;
#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "Filter");
#endif

    int* panConstraints = (int*) idxStr;
    int nConstraints = panConstraints ? panConstraints[0] : 0;

    if( nConstraints != argc )
        return SQLITE_ERROR;

    CPLString osAttributeFilter;

    OGRFeatureDefn* poFDefn = pMyCursor->poLayer->GetLayerDefn();

    int i;
    for (i = 0; i < argc; i++)
    {
        int nCol = panConstraints[2 * i + 1];
        OGRFieldDefn* poFieldDefn = NULL;
        if( nCol >= 0 )
        {
            poFieldDefn = poFDefn->GetFieldDefn(nCol);
            if( poFieldDefn == NULL )
                return SQLITE_ERROR;
        }

        if( i != 0 )
            osAttributeFilter += " AND ";

        if( poFieldDefn != NULL )
        {
            const char* pszFieldName = poFieldDefn->GetNameRef();
            char ch;
            int bNeedsQuoting = swq_is_reserved_keyword(pszFieldName);
            for(int j = 0; !bNeedsQuoting &&
                           (ch = pszFieldName[j]) != '\0'; j++ )
            {
                if (!(isalnum((int)ch) || ch == '_'))
                    bNeedsQuoting = FALSE;
            }

            if( bNeedsQuoting )
            {
                /* FIXME: we would need some virtual method */
                OGRSFDriver* poDriver = pMyCursor->pVTab->poDS->GetDriver();
                char chQuote;

                if (poDriver && (
                    EQUAL(poDriver->GetName(), "PostgreSQL") ||
                    EQUAL(poDriver->GetName(), "SQLite") ||
                    EQUAL(poDriver->GetName(), "FileGDB" )) )
                    chQuote = '"';
                else
                    chQuote = '\'';

                osAttributeFilter += chQuote;
                if( chQuote == '"' )
                    osAttributeFilter += OGRSQLiteEscapeName(pszFieldName);
                else
                    osAttributeFilter += OGRSQLiteEscape(pszFieldName);
                osAttributeFilter += chQuote;
            }
            else
            {
                osAttributeFilter += pszFieldName;
            }
        }
        else
            osAttributeFilter += "FID";

        switch(panConstraints[2 * i + 2])
        {
            case SQLITE_INDEX_CONSTRAINT_EQ: osAttributeFilter += " = "; break;
            case SQLITE_INDEX_CONSTRAINT_GT: osAttributeFilter += " > "; break;
            case SQLITE_INDEX_CONSTRAINT_LE: osAttributeFilter += " <= "; break;
            case SQLITE_INDEX_CONSTRAINT_LT: osAttributeFilter += " < "; break;
            case SQLITE_INDEX_CONSTRAINT_GE: osAttributeFilter += " >= "; break;
            default:
            {
                sqlite3_free(pMyCursor->pVTab->zErrMsg);
                pMyCursor->pVTab->zErrMsg = sqlite3_mprintf(
                                        "Unhandled constraint operator : %d",
                                        panConstraints[2 * i + 2]);
                return SQLITE_ERROR;
            }
        }

        if (sqlite3_value_type (argv[i]) == SQLITE_INTEGER)
        {
            osAttributeFilter +=
                CPLSPrintf(CPL_FRMT_GIB, sqlite3_value_int64 (argv[i]));
        }
        else if (sqlite3_value_type (argv[i]) == SQLITE_FLOAT)
        {
            osAttributeFilter +=
                CPLSPrintf("%.18g", sqlite3_value_double (argv[i]));
        }
        else if (sqlite3_value_type (argv[i]) == SQLITE_TEXT)
        {
            osAttributeFilter += "'";
            osAttributeFilter += OGRSQLiteEscape((const char*) sqlite3_value_text (argv[i]));
            osAttributeFilter += "'";
        }
        else
        {
            sqlite3_free(pMyCursor->pVTab->zErrMsg);
            pMyCursor->pVTab->zErrMsg = sqlite3_mprintf(
                                    "Unhandled constraint data type : %d",
                                    sqlite3_value_type (argv[i]));
            return SQLITE_ERROR;
        }
    }

#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "Attribute filter : %s",
             osAttributeFilter.c_str());
#endif

    if( pMyCursor->poLayer->SetAttributeFilter( osAttributeFilter.size() ?
                            osAttributeFilter.c_str() : NULL) != OGRERR_NONE )
    {
        sqlite3_free(pMyCursor->pVTab->zErrMsg);
        pMyCursor->pVTab->zErrMsg = sqlite3_mprintf(
                "Cannot apply attribute filter : %s",
                osAttributeFilter.c_str());
        return SQLITE_ERROR;
    }

    if( pMyCursor->poLayer->TestCapability(OLCFastFeatureCount) )
    {
        pMyCursor->nFeatureCount = pMyCursor->poLayer->GetFeatureCount();
        pMyCursor->poLayer->ResetReading();
    }
    else
        pMyCursor->nFeatureCount = -1;

    if( pMyCursor->nFeatureCount < 0 )
    {
        pMyCursor->poFeature = pMyCursor->poLayer->GetNextFeature();
#ifdef DEBUG_OGR2SQLITE
        CPLDebug("OGR2SQLITE", "GetNextFeature() --> %d",
            pMyCursor->poFeature ? (int)pMyCursor->poFeature->GetFID() : -1);
#endif
    }

    pMyCursor->nNextWishedIndex = 0;
    pMyCursor->nCurFeatureIndex = -1;

    return SQLITE_OK;
}

/************************************************************************/
/*                          OGR2SQLITE_Next()                           */
/************************************************************************/

static
int OGR2SQLITE_Next(sqlite3_vtab_cursor* pCursor)
{
    OGR2SQLITE_vtab_cursor* pMyCursor = (OGR2SQLITE_vtab_cursor*) pCursor;
#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "Next");
#endif

    pMyCursor->nNextWishedIndex ++;
    if( pMyCursor->nFeatureCount < 0 )
    {
        delete pMyCursor->poFeature;
        pMyCursor->poFeature = pMyCursor->poLayer->GetNextFeature();

        CPLFree(pMyCursor->pabyGeomBLOB);
        pMyCursor->pabyGeomBLOB = NULL;
        pMyCursor->nGeomBLOBLen = -1;

#ifdef DEBUG_OGR2SQLITE
        CPLDebug("OGR2SQLITE", "GetNextFeature() --> %d",
            pMyCursor->poFeature ? (int)pMyCursor->poFeature->GetFID() : -1);
#endif
    }
    return SQLITE_OK;
}

/************************************************************************/
/*                          OGR2SQLITE_Eof()                            */
/************************************************************************/

static
int OGR2SQLITE_Eof(sqlite3_vtab_cursor* pCursor)
{
    OGR2SQLITE_vtab_cursor* pMyCursor = (OGR2SQLITE_vtab_cursor*) pCursor;
#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "Eof");
#endif

    if( pMyCursor->nFeatureCount < 0 )
    {
        return (pMyCursor->poFeature == NULL);
    }
    else
    {
        return ( pMyCursor->nNextWishedIndex >= pMyCursor->nFeatureCount );
    }
}

/************************************************************************/
/*                      OGR2SQLITE_GoToWishedIndex()                    */
/************************************************************************/

static void OGR2SQLITE_GoToWishedIndex(OGR2SQLITE_vtab_cursor* pMyCursor)
{
    if( pMyCursor->nFeatureCount >= 0 )
    {
        if( pMyCursor->nCurFeatureIndex < pMyCursor->nNextWishedIndex )
        {
            do
            {
                pMyCursor->nCurFeatureIndex ++;

                delete pMyCursor->poFeature;
                pMyCursor->poFeature = pMyCursor->poLayer->GetNextFeature();
#ifdef DEBUG_OGR2SQLITE
                CPLDebug("OGR2SQLITE", "GetNextFeature() --> %d",
                    pMyCursor->poFeature ? (int)pMyCursor->poFeature->GetFID() : -1);
#endif
            }
            while( pMyCursor->nCurFeatureIndex < pMyCursor->nNextWishedIndex );

            CPLFree(pMyCursor->pabyGeomBLOB);
            pMyCursor->pabyGeomBLOB = NULL;
            pMyCursor->nGeomBLOBLen = -1;
        }
    }
}

/************************************************************************/
/*                         OGR2SQLITE_Column()                          */
/************************************************************************/

static
int OGR2SQLITE_Column(sqlite3_vtab_cursor* pCursor,
                      sqlite3_context* pContext, int nCol)
{
    OGR2SQLITE_vtab_cursor* pMyCursor = (OGR2SQLITE_vtab_cursor*) pCursor;
    OGRFeature* poFeature;
#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "Column %d", nCol);
#endif

    OGR2SQLITE_GoToWishedIndex(pMyCursor);

    poFeature = pMyCursor->poFeature;
    if( poFeature == NULL)
        return SQLITE_ERROR;

    OGRFeatureDefn* poFDefn = pMyCursor->poLayer->GetLayerDefn();
    int nFieldCount = poFDefn->GetFieldCount();

    if( nCol == nFieldCount )
    {
        sqlite3_result_text(pContext,
                            poFeature->GetStyleString(),
                            -1, SQLITE_TRANSIENT);
        return SQLITE_OK;
    }
    else if( nCol == (nFieldCount + 1) &&
             poFDefn->GetGeomType() != wkbNone )
    {
        if( pMyCursor->nGeomBLOBLen < 0 )
        {
            OGRGeometry* poGeom = poFeature->GetGeometryRef();
            if( poGeom == NULL )
            {
                pMyCursor->nGeomBLOBLen = 0;
            }
            else
            {
                CPLAssert(pMyCursor->pabyGeomBLOB == NULL);

                OGRSpatialReference* poSRS = poGeom->getSpatialReference();
                int nSRSId = pMyCursor->pVTab->poModule->FetchSRSId(poSRS);

                if( OGRSQLiteLayer::ExportSpatiaLiteGeometry(
                        poGeom, nSRSId, wkbNDR, FALSE, FALSE, FALSE,
                        &pMyCursor->pabyGeomBLOB,
                        &pMyCursor->nGeomBLOBLen ) != CE_None )
                {
                    pMyCursor->nGeomBLOBLen = 0;
                }
            }
        }

        if( pMyCursor->nGeomBLOBLen == 0 )
        {
            sqlite3_result_null(pContext);
        }
        else
        {
            GByte *pabyGeomBLOBDup = (GByte*)
                                CPLMalloc(pMyCursor->nGeomBLOBLen);
            memcpy(pabyGeomBLOBDup,
                   pMyCursor->pabyGeomBLOB, pMyCursor->nGeomBLOBLen);
            sqlite3_result_blob(pContext, pabyGeomBLOBDup,
                                pMyCursor->nGeomBLOBLen, CPLFree);
        }

        return SQLITE_OK;
    }
    else if( nCol > (nFieldCount + 1) &&
             nCol - (nFieldCount + 1) < poFDefn->GetGeomFieldCount() )
    {
        OGRGeometry* poGeom = poFeature->GetGeomFieldRef(nCol - (nFieldCount + 1));
        if( poGeom == NULL )
        {
            sqlite3_result_null(pContext);
        }
        else
        {
            OGRSpatialReference* poSRS = poGeom->getSpatialReference();
            int nSRSId = pMyCursor->pVTab->poModule->FetchSRSId(poSRS);

            GByte* pabyGeomBLOB = NULL;
            int nGeomBLOBLen = 0;
            if( OGRSQLiteLayer::ExportSpatiaLiteGeometry(
                    poGeom, nSRSId, wkbNDR, FALSE, FALSE, FALSE,
                    &pabyGeomBLOB, &nGeomBLOBLen ) != CE_None )
            {
                nGeomBLOBLen = 0;
            }

            if( nGeomBLOBLen == 0 )
            {
                sqlite3_result_null(pContext);
            }
            else
            {
                sqlite3_result_blob(pContext, pabyGeomBLOB,
                                    nGeomBLOBLen, CPLFree);
            }
        }
        return SQLITE_OK;
    }
    else if( nCol < 0 || nCol >= nFieldCount )
    {
        return SQLITE_ERROR;
    }
    else if( !poFeature->IsFieldSet(nCol) )
    {
        sqlite3_result_null(pContext);
        return SQLITE_OK;
    }

    switch( poFDefn->GetFieldDefn(nCol)->GetType() )
    {
        case OFTInteger:
            sqlite3_result_int(pContext,
                               poFeature->GetFieldAsInteger(nCol));
            break;

        case OFTReal:
            sqlite3_result_double(pContext,
                                  poFeature->GetFieldAsDouble(nCol));
            break;

        case OFTBinary:
        {
            int nSize;
            GByte* pBlob = poFeature->GetFieldAsBinary(nCol, &nSize);
            sqlite3_result_blob(pContext, pBlob, nSize, SQLITE_TRANSIENT);
            break;
        }

        case OFTDateTime:
        {
            int nYear, nMonth, nDay, nHour, nMinute, nSecond, nTZ;
            poFeature->GetFieldAsDateTime(nCol, &nYear, &nMonth, &nDay,
                                          &nHour, &nMinute, &nSecond, &nTZ);
            char szBuffer[64];
            sprintf(szBuffer, "%04d-%02d-%02dT%02d:%02d:%02d",
                    nYear, nMonth, nDay, nHour, nMinute, nSecond);
            sqlite3_result_text(pContext,
                                szBuffer,
                                -1, SQLITE_TRANSIENT);
            break;
        }

        case OFTDate:
        {
            int nYear, nMonth, nDay, nHour, nMinute, nSecond, nTZ;
            poFeature->GetFieldAsDateTime(nCol, &nYear, &nMonth, &nDay,
                                          &nHour, &nMinute, &nSecond, &nTZ);
            char szBuffer[64];
            sprintf(szBuffer, "%04d-%02d-%02dT", nYear, nMonth, nDay);
            sqlite3_result_text(pContext,
                                szBuffer,
                                -1, SQLITE_TRANSIENT);
            break;
        }

        case OFTTime:
        {
            int nYear, nMonth, nDay, nHour, nMinute, nSecond, nTZ;
            poFeature->GetFieldAsDateTime(nCol, &nYear, &nMonth, &nDay,
                                          &nHour, &nMinute, &nSecond, &nTZ);
            char szBuffer[64];
            sprintf(szBuffer, "%02d:%02d:%02d", nHour, nMinute, nSecond);
            sqlite3_result_text(pContext,
                                szBuffer,
                                -1, SQLITE_TRANSIENT);
            break;
        }

        default:
            sqlite3_result_text(pContext,
                                poFeature->GetFieldAsString(nCol),
                                -1, SQLITE_TRANSIENT);
            break;
    }

    return SQLITE_OK;
}

/************************************************************************/
/*                         OGR2SQLITE_Rowid()                           */
/************************************************************************/

static
int OGR2SQLITE_Rowid(sqlite3_vtab_cursor* pCursor, sqlite3_int64 *pRowid)
{
    OGR2SQLITE_vtab_cursor* pMyCursor = (OGR2SQLITE_vtab_cursor*) pCursor;
#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "Rowid");
#endif

    OGR2SQLITE_GoToWishedIndex(pMyCursor);

    if( pMyCursor->poFeature == NULL)
        return SQLITE_ERROR;

    *pRowid = pMyCursor->poFeature->GetFID();

    return SQLITE_OK;
}

/************************************************************************/
/*                         OGR2SQLITE_Rename()                          */
/************************************************************************/

static
int OGR2SQLITE_Rename(sqlite3_vtab *pVtab, const char *zNew)
{
    //CPLDebug("OGR2SQLITE", "Rename");
    return SQLITE_ERROR;
}

#if 0
/************************************************************************/
/*                        OGR2SQLITE_FindFunction()                     */
/************************************************************************/

static
int OGR2SQLITE_FindFunction(sqlite3_vtab *pVtab,
                            int nArg,
                            const char *zName,
                            void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
                            void **ppArg)
{
    CPLDebug("OGR2SQLITE", "FindFunction %s", zName);

    return 0;
}
#endif

/************************************************************************/
/*                     OGR2SQLITE_FeatureFromArgs()                     */
/************************************************************************/

static OGRFeature* OGR2SQLITE_FeatureFromArgs(OGRLayer* poLayer,
                                              int argc,
                                              sqlite3_value **argv)
{
    OGRFeatureDefn* poLayerDefn = poLayer->GetLayerDefn();
    int nFieldCount = poLayerDefn->GetFieldCount();
    int nGeomFieldCount = poLayerDefn->GetGeomFieldCount();
    if( argc != 2 + nFieldCount + 1 + nGeomFieldCount)
    {
        CPLDebug("OGR2SQLITE", "Did not get expect argument count : %d, %d", argc,
                    2 + nFieldCount + 1 + nGeomFieldCount);
        return NULL;
    }

    OGRFeature* poFeature = new OGRFeature(poLayerDefn);
    int i;
    for(i = 0; i < nFieldCount; i++)
    {
        switch( sqlite3_value_type(argv[2 + i]) )
        {
            case SQLITE_INTEGER:
                //FIXME use int64 when OGR has 64bit integer support
                poFeature->SetField(i, sqlite3_value_int(argv[2 + i]));
                break;
            case SQLITE_FLOAT:
                poFeature->SetField(i, sqlite3_value_double(argv[2 + i]));
                break;
            case SQLITE_TEXT:
            {
                const char* pszValue = (const char*) sqlite3_value_text(argv[2 + i]);
                switch( poLayerDefn->GetFieldDefn(i)->GetType() )
                {
                    case OFTDate:
                    case OFTTime:
                    case OFTDateTime:
                    {
                        if( !OGRSQLITEStringToDateTimeField( poFeature, i, pszValue ) )
                            poFeature->SetField(i, pszValue);
                        break;
                    }

                    default:
                        poFeature->SetField(i, pszValue);
                        break;
                }
                break;
            }
            case SQLITE_BLOB:
            {
                GByte* paby = (GByte *) sqlite3_value_blob (argv[2 + i]);
                int nLen = sqlite3_value_bytes (argv[2 + i]);
                poFeature->SetField(i, nLen, paby);
                break;
            }
            default:
                break;
        }
    }

    int nStyleIdx = 2 + nFieldCount;
    if( sqlite3_value_type(argv[nStyleIdx]) == SQLITE_TEXT )
    {
        poFeature->SetStyleString((const char*) sqlite3_value_text(argv[nStyleIdx]));
    }

    for(i = 0; i < nGeomFieldCount; i++)
    {
        int nGeomFieldIdx = 2 + nFieldCount + 1 + i;
        if( sqlite3_value_type(argv[nGeomFieldIdx]) == SQLITE_BLOB )
        {
            GByte* pabyBlob = (GByte *) sqlite3_value_blob (argv[nGeomFieldIdx]);
            int nLen = sqlite3_value_bytes (argv[nGeomFieldIdx]);
            OGRGeometry* poGeom = NULL;
            if( OGRSQLiteLayer::ImportSpatiaLiteGeometry(
                            pabyBlob, nLen, &poGeom ) == CE_None )
            {
                poFeature->SetGeomFieldDirectly(i, poGeom);
            }
        }
    }

    if( sqlite3_value_type(argv[1]) == SQLITE_INTEGER )
        poFeature->SetFID( sqlite3_value_int64(argv[1]) );

    return poFeature;
}

/************************************************************************/
/*                            OGR2SQLITE_Update()                       */
/************************************************************************/

static
int OGR2SQLITE_Update(sqlite3_vtab *pVTab,
                      int argc,
                      sqlite3_value **argv,
                      sqlite_int64 *pRowid)
{
    CPLDebug("OGR2SQLITE", "OGR2SQLITE_Update");

    OGR2SQLITE_vtab* pMyVTab = (OGR2SQLITE_vtab*) pVTab;
    OGRLayer* poLayer = pMyVTab->poLayer;

    if( argc == 1 )
    {
         /* DELETE */

        OGRErr eErr = poLayer->DeleteFeature(sqlite3_value_int64(argv[0]));

        return ( eErr == OGRERR_NONE ) ? SQLITE_OK : SQLITE_ERROR;
    }
    else if( argc > 1 && sqlite3_value_type(argv[0]) == SQLITE_NULL )
    {
         /* INSERT */

        OGRFeature* poFeature = OGR2SQLITE_FeatureFromArgs(poLayer, argc, argv);
        if( poFeature == NULL )
            return SQLITE_ERROR;

        OGRErr eErr = poLayer->CreateFeature(poFeature);
        if( eErr == OGRERR_NONE )
            *pRowid = poFeature->GetFID();

        delete poFeature;

        return ( eErr == OGRERR_NONE ) ? SQLITE_OK : SQLITE_ERROR;
    }
    else if( argc > 1 && sqlite3_value_type(argv[0]) == SQLITE_INTEGER &&
             sqlite3_value_type(argv[1]) == SQLITE_INTEGER &&
             sqlite3_value_int64(argv[0]) == sqlite3_value_int64(argv[1]) )
    {
        /* UPDATE */

        OGRFeature* poFeature = OGR2SQLITE_FeatureFromArgs(poLayer, argc, argv);
        if( poFeature == NULL )
            return SQLITE_ERROR;

        OGRErr eErr = poLayer->SetFeature(poFeature);

        delete poFeature;

        return ( eErr == OGRERR_NONE ) ? SQLITE_OK : SQLITE_ERROR;
    }

    // UPDATE table SET rowid=rowid+1 WHERE ... unsupported

    return SQLITE_ERROR;
}

/************************************************************************/
/*                        sOGR2SQLITEModule                             */
/************************************************************************/

static const struct sqlite3_module sOGR2SQLITEModule =
{
    1, /* iVersion */
    OGR2SQLITE_ConnectCreate, /* xCreate */
    OGR2SQLITE_ConnectCreate, /* xConnect */
    OGR2SQLITE_BestIndex,
    OGR2SQLITE_DisconnectDestroy, /* xDisconnect */
    OGR2SQLITE_DisconnectDestroy, /* xDestroy */
    OGR2SQLITE_Open,
    OGR2SQLITE_Close,
    OGR2SQLITE_Filter,
    OGR2SQLITE_Next,
    OGR2SQLITE_Eof,
    OGR2SQLITE_Column,
    OGR2SQLITE_Rowid,
    OGR2SQLITE_Update,
    NULL, /* xBegin */
    NULL, /* xSync */
    NULL, /* xCommit */
    NULL, /* xFindFunctionRollback */
    NULL, /* xFindFunction */  // OGR2SQLITE_FindFunction;
    OGR2SQLITE_Rename
};

/************************************************************************/
/*                           OGR2SQLITE_GetLayer()                      */
/************************************************************************/

static
OGRLayer* OGR2SQLITE_GetLayer(const char* pszFuncName,
                              sqlite3_context* pContext,
                              int argc, sqlite3_value** argv)
{
    if( argc != 1 )
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "%s: %s(): %s",
                 "VirtualOGR",
                 pszFuncName,
                 "Invalid number of arguments");
        sqlite3_result_null (pContext);
        return NULL;
    }

    if( sqlite3_value_type (argv[0]) != SQLITE_TEXT )
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "%s: %s(): %s",
                 "VirtualOGR",
                 pszFuncName,
                 "Invalid argument type");
        sqlite3_result_null (pContext);
        return NULL;
    }

    const char* pszVTableName = (const char*)sqlite3_value_text(argv[0]);

    OGR2SQLITEModule* poModule =
                    (OGR2SQLITEModule*) sqlite3_user_data(pContext);

    OGRLayer* poLayer = poModule->GetLayerForVTable(OGRSQLiteParamsUnquote(pszVTableName));
    if( poLayer == NULL )
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "%s: %s(): %s",
                 "VirtualOGR",
                 pszFuncName,
                 "Unknown virtual table");
        sqlite3_result_null (pContext);
        return NULL;
    }

    return poLayer;
}

/************************************************************************/
/*                       OGR2SQLITE_ogr_layer_Extent()                  */
/************************************************************************/

static
void OGR2SQLITE_ogr_layer_Extent(sqlite3_context* pContext,
                                 int argc, sqlite3_value** argv)
{
    OGRLayer* poLayer = OGR2SQLITE_GetLayer("ogr_layer_Extent",
                                            pContext, argc, argv);
    if( poLayer == NULL )
        return;

    OGR2SQLITEModule* poModule =
                    (OGR2SQLITEModule*) sqlite3_user_data(pContext);

    if( poLayer->GetGeomType() == wkbNone )
    {
        sqlite3_result_null (pContext);
        return;
    }

    OGREnvelope sExtent;
    if( poLayer->GetExtent(&sExtent) != OGRERR_NONE )
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "%s: %s(): %s",
                 "VirtualOGR",
                 "ogr_layer_Extent",
                 "Cannot fetch layer extent");
        sqlite3_result_null (pContext);
        return;
    }

    OGRPolygon oPoly;
    OGRLinearRing* poRing = new OGRLinearRing();
    oPoly.addRingDirectly(poRing);
    poRing->addPoint(sExtent.MinX, sExtent.MinY);
    poRing->addPoint(sExtent.MaxX, sExtent.MinY);
    poRing->addPoint(sExtent.MaxX, sExtent.MaxY);
    poRing->addPoint(sExtent.MinX, sExtent.MaxY);
    poRing->addPoint(sExtent.MinX, sExtent.MinY);

    GByte* pabySLBLOB = NULL;
    int nBLOBLen = 0;
    int nSRID = poModule->FetchSRSId(poLayer->GetSpatialRef());
    if( OGRSQLiteLayer::ExportSpatiaLiteGeometry(
                    &oPoly, nSRID, wkbNDR, FALSE,
                    FALSE, FALSE, &pabySLBLOB, &nBLOBLen ) == CE_None )
    {
        sqlite3_result_blob(pContext, pabySLBLOB, nBLOBLen, CPLFree);
    }
    else
    {
        sqlite3_result_null (pContext);
    }
}

/************************************************************************/
/*                       OGR2SQLITE_ogr_layer_SRID()                    */
/************************************************************************/

static
void OGR2SQLITE_ogr_layer_SRID(sqlite3_context* pContext,
                                 int argc, sqlite3_value** argv)
{
    OGRLayer* poLayer = OGR2SQLITE_GetLayer("OGR2SQLITE_ogr_layer_SRID",
                                            pContext, argc, argv);
    if( poLayer == NULL )
        return;

    OGR2SQLITEModule* poModule =
                    (OGR2SQLITEModule*) sqlite3_user_data(pContext);

    if( poLayer->GetGeomType() == wkbNone )
    {
        sqlite3_result_null (pContext);
        return;
    }

    int nSRID = poModule->FetchSRSId(poLayer->GetSpatialRef());
    sqlite3_result_int(pContext, nSRID);
}

/************************************************************************/
/*                 OGR2SQLITE_ogr_layer_GeometryType()                  */
/************************************************************************/

static
void OGR2SQLITE_ogr_layer_GeometryType(sqlite3_context* pContext,
                                 int argc, sqlite3_value** argv)
{
    OGRLayer* poLayer = OGR2SQLITE_GetLayer("OGR2SQLITE_ogr_layer_GeometryType",
                                            pContext, argc, argv);
    if( poLayer == NULL )
        return;

    OGRwkbGeometryType eType = poLayer->GetGeomType();

    if( eType == wkbNone )
    {
        sqlite3_result_null (pContext);
        return;
    }

    const char* psz2DName = OGRToOGCGeomType(eType);
    if( eType & wkb25DBit )
        sqlite3_result_text( pContext, CPLSPrintf("%s Z", psz2DName), -1, SQLITE_TRANSIENT );
    else
        sqlite3_result_text( pContext, psz2DName, -1, SQLITE_TRANSIENT );
}

/************************************************************************/
/*                OGR2SQLITE_ogr_layer_FeatureCount()                   */
/************************************************************************/

static
void OGR2SQLITE_ogr_layer_FeatureCount(sqlite3_context* pContext,
                                       int argc, sqlite3_value** argv)
{
    OGRLayer* poLayer = OGR2SQLITE_GetLayer("OGR2SQLITE_ogr_layer_FeatureCount",
                                            pContext, argc, argv);
    if( poLayer == NULL )
        return;

    sqlite3_result_int64( pContext, poLayer->GetFeatureCount() );
}

/************************************************************************/
/*                      OGR2SQLITEDestroyModule()                       */
/************************************************************************/

static void OGR2SQLITEDestroyModule(void* pData)
{
    CPLDebug("OGR", "Unloading VirtualOGR module");
    delete (OGR2SQLITEModule*) pData;
}

/* ENABLE_VIRTUAL_OGR_SPATIAL_INDEX is not defined */
#ifdef ENABLE_VIRTUAL_OGR_SPATIAL_INDEX

/************************************************************************/
/*                    OGR2SQLITESpatialIndex_vtab                       */
/************************************************************************/

typedef struct
{
    /* Mandatory fields by sqlite3: don't change or reorder them ! */
    const sqlite3_module *pModule;
    int                   nRef;
    char                 *zErrMsg;

    /* Extension fields */
    char                 *pszVTableName;
    OGR2SQLITEModule     *poModule;
    OGRDataSource        *poDS;
    int                   bCloseDS;
    OGRLayer             *poLayer;
    int                   nMyRef;
} OGR2SQLITESpatialIndex_vtab;

/************************************************************************/
/*                  OGR2SQLITESpatialIndex_vtab_cursor                  */
/************************************************************************/

typedef struct
{
    /* Mandatory fields by sqlite3: don't change or reorder them ! */
    OGR2SQLITESpatialIndex_vtab *pVTab;

    /* Extension fields */
    OGRDataSource *poDupDataSource;
    OGRLayer      *poLayer;
    OGRFeature    *poFeature;
    int            bHasSetBounds;
    double         dfMinX, dfMinY, dfMaxX, dfMaxY;
} OGR2SQLITESpatialIndex_vtab_cursor;

/************************************************************************/
/*                   OGR2SQLITESpatialIndex_ConnectCreate()             */
/************************************************************************/

static
int OGR2SQLITESpatialIndex_ConnectCreate(sqlite3* hDB, void *pAux,
                             int argc, const char *const*argv,
                             sqlite3_vtab **ppVTab, char**pzErr)
{
    OGR2SQLITEModule* poModule = (OGR2SQLITEModule*) pAux;
    OGRLayer* poLayer = NULL;
    OGRDataSource* poDS = NULL;
    int bCloseDS = FALSE;
    int i;

#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "ConnectCreate(%s)", argv[2]);
#endif

    /*for(i=0;i<argc;i++)
        printf("[%d] %s\n", i, argv[i]);*/

/* -------------------------------------------------------------------- */
/*      If called from ogrexecutesql.cpp                                */
/* -------------------------------------------------------------------- */
    poDS = poModule->GetDS();
    if( poDS == NULL )
        return SQLITE_ERROR;

    if( argc != 10 )
    {
        *pzErr = sqlite3_mprintf(
            "Expected syntax: CREATE VIRTUAL TABLE xxx USING "
            "VirtualOGRSpatialIndex(ds_idx, layer_name, pkid, xmin, xmax, ymin, ymax)");
        return SQLITE_ERROR;
    }

    int nDSIndex = atoi(argv[3]);
    if( nDSIndex >= 0 )
    {
        poDS = poModule->GetExtraDS(nDSIndex);
        if( poDS == NULL )
        {
            *pzErr = sqlite3_mprintf("Invalid dataset index : %d", nDSIndex);
            return SQLITE_ERROR;
        }
    }

    poDS = (OGRDataSource*) OGROpen( poDS->GetName(), FALSE, NULL);
    if( poDS == NULL )
    {
        return SQLITE_ERROR;
    }
    bCloseDS = TRUE;

    CPLString osLayerName(OGRSQLiteParamsUnquote(argv[4]));

    poLayer = poDS->GetLayerByName(osLayerName);
    if( poLayer == NULL )
    {
        *pzErr = sqlite3_mprintf( "Cannot find layer '%s' in '%s'",
                                    osLayerName.c_str(), poDS->GetName() );
        return SQLITE_ERROR;
    }

    OGR2SQLITESpatialIndex_vtab* vtab =
                (OGR2SQLITESpatialIndex_vtab*) CPLCalloc(1, sizeof(OGR2SQLITESpatialIndex_vtab));
    /* We dont need to fill the non-extended fields */
    vtab->pszVTableName = CPLStrdup(OGRSQLiteEscapeName(argv[2]));
    vtab->poModule = poModule;
    vtab->poDS = poDS;
    vtab->bCloseDS = bCloseDS;
    vtab->poLayer = poLayer;
    vtab->nMyRef = 0;

    *ppVTab = (sqlite3_vtab*) vtab;

    CPLString osSQL;
    osSQL = "CREATE TABLE ";
    osSQL += "\"";
    osSQL += OGRSQLiteEscapeName(argv[2]);
    osSQL += "\"";
    osSQL += "(";

    int bAddComma = FALSE;

    for(i=0;i<5;i++)
    {
        if( bAddComma )
            osSQL += ",";
        bAddComma = TRUE;

        osSQL += "\"";
        osSQL += OGRSQLiteEscapeName(OGRSQLiteParamsUnquote(argv[5+i]));
        osSQL += "\"";
        osSQL += " ";
        osSQL += (i == 0) ? "INTEGER" : "FLOAT";
    }

    osSQL += ")";

    CPLDebug("OGR2SQLITE", "sqlite3_declare_vtab(%s)", osSQL.c_str());
    if (sqlite3_declare_vtab (hDB, osSQL.c_str()) != SQLITE_OK)
    {
        *pzErr = sqlite3_mprintf("CREATE VIRTUAL: invalid SQL statement : %s",
                                 osSQL.c_str());
        return SQLITE_ERROR;
    }

    return SQLITE_OK;
}

/************************************************************************/
/*                      OGR2SQLITESpatialIndex_BestIndex()              */
/************************************************************************/

static
int OGR2SQLITESpatialIndex_BestIndex(sqlite3_vtab *pVTab, sqlite3_index_info* pIndex)
{
#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "BestIndex");
#endif

    int i;

    int bMinX = FALSE, bMinY = FALSE, bMaxX = FALSE, bMaxY = FALSE;

    for (i = 0; i < pIndex->nConstraint; i++)
    {
        int iCol = pIndex->aConstraint[i].iColumn;
        /* MinX */
        if( !bMinX && iCol == 1 && pIndex->aConstraint[i].usable &&
            (pIndex->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_LE ||
                pIndex->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_LT) )
            bMinX = TRUE;
        /* MaxX */
        else if( !bMaxX && iCol == 2 && pIndex->aConstraint[i].usable &&
            (pIndex->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_GE ||
                pIndex->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_GT) )
            bMaxX = TRUE;
        /* MinY */
        else if( !bMinY && iCol == 3 && pIndex->aConstraint[i].usable &&
            (pIndex->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_LE ||
                pIndex->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_LT) )
            bMinY = TRUE;
        /* MaxY */
        else if( !bMaxY && iCol == 4 && pIndex->aConstraint[i].usable &&
            (pIndex->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_GE ||
                pIndex->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_GT) )
            bMaxY = TRUE;
        else
            break;
    }

    if( bMinX && bMinY && bMaxX && bMaxY )
    {
        CPLAssert( pIndex->nConstraint == 4 );

        int nConstraints = 0;
        for (i = 0; i < pIndex->nConstraint; i++)
        {
            pIndex->aConstraintUsage[i].argvIndex = nConstraints + 1;
            pIndex->aConstraintUsage[i].omit = TRUE;

            nConstraints ++;
        }

        int* panConstraints = (int*)
                    sqlite3_malloc( sizeof(int) * (1 + 2 * nConstraints) );
        panConstraints[0] = nConstraints;

        nConstraints = 0;

        for (i = 0; i < pIndex->nConstraint; i++)
        {
            if (pIndex->aConstraintUsage[i].omit)
            {
                panConstraints[2 * nConstraints + 1] =
                                            pIndex->aConstraint[i].iColumn;
                panConstraints[2 * nConstraints + 2] =
                                            pIndex->aConstraint[i].op;

                nConstraints ++;
            }
        }

        pIndex->idxStr = (char *) panConstraints;
        pIndex->needToFreeIdxStr = TRUE;

        pIndex->orderByConsumed = FALSE;
        pIndex->idxNum = 0;

        return SQLITE_OK;
    }
    else
    {
        CPLDebug("OGR2SQLITE", "OGR2SQLITESpatialIndex_BestIndex: unhandled request");
        return SQLITE_ERROR;
/*
        for (i = 0; i < pIndex->nConstraint; i++)
        {
            pIndex->aConstraintUsage[i].argvIndex = 0;
            pIndex->aConstraintUsage[i].omit = FALSE;
        }

        pIndex->idxStr = NULL;
        pIndex->needToFreeIdxStr = FALSE;
*/
    }
}

/************************************************************************/
/*                  OGR2SQLITESpatialIndex_DisconnectDestroy()          */
/************************************************************************/

static
int OGR2SQLITESpatialIndex_DisconnectDestroy(sqlite3_vtab *pVTab)
{
    OGR2SQLITESpatialIndex_vtab* pMyVTab = (OGR2SQLITESpatialIndex_vtab*) pVTab;

#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "DisconnectDestroy(%s)",pMyVTab->pszVTableName);
#endif

    sqlite3_free(pMyVTab->zErrMsg);
    if( pMyVTab->bCloseDS )
        delete pMyVTab->poDS;
    CPLFree(pMyVTab->pszVTableName);
    CPLFree(pMyVTab);

    return SQLITE_OK;
}

/************************************************************************/
/*                    OGR2SQLITESpatialIndex_Open()                     */
/************************************************************************/

static
int OGR2SQLITESpatialIndex_Open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor)
{
    OGR2SQLITESpatialIndex_vtab* pMyVTab = (OGR2SQLITESpatialIndex_vtab*) pVTab;
#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "Open(%s, %s)",
             pMyVTab->poDS->GetName(), pMyVTab->poLayer->GetName());
#endif

    OGRDataSource* poDupDataSource = NULL;
    OGRLayer* poLayer = NULL;

    if( pMyVTab->nMyRef == 0 )
    {
        poLayer = pMyVTab->poLayer;
    }
    else
    {
        poDupDataSource =
            (OGRDataSource*) OGROpen(pMyVTab->poDS->GetName(), FALSE, NULL);
        if( poDupDataSource == NULL )
            return SQLITE_ERROR;
        poLayer = poDupDataSource->GetLayerByName(
                                                pMyVTab->poLayer->GetName());
        if( poLayer == NULL )
        {
            delete poDupDataSource;
            return SQLITE_ERROR;
        }
        if( !poLayer->GetLayerDefn()->
                IsSame(pMyVTab->poLayer->GetLayerDefn()) )
        {
            delete poDupDataSource;
            return SQLITE_ERROR;
        }
    }
    pMyVTab->nMyRef ++;

    OGR2SQLITESpatialIndex_vtab_cursor* pCursor = (OGR2SQLITESpatialIndex_vtab_cursor*)
                                CPLCalloc(1, sizeof(OGR2SQLITESpatialIndex_vtab_cursor));
    /* We dont need to fill the non-extended fields */
    *ppCursor = (sqlite3_vtab_cursor *)pCursor;

    pCursor->poDupDataSource = poDupDataSource;
    pCursor->poLayer = poLayer;
    pCursor->poLayer->ResetReading();
    pCursor->poFeature = NULL;

    return SQLITE_OK;
}

/************************************************************************/
/*                      OGR2SQLITESpatialIndex_Close()                  */
/************************************************************************/

static
int OGR2SQLITESpatialIndex_Close(sqlite3_vtab_cursor* pCursor)
{
    OGR2SQLITESpatialIndex_vtab_cursor* pMyCursor = (OGR2SQLITESpatialIndex_vtab_cursor*) pCursor;
    OGR2SQLITESpatialIndex_vtab* pMyVTab = pMyCursor->pVTab;
#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "Close(%s, %s)",
             pMyVTab->poDS->GetName(), pMyVTab->poLayer->GetName());
#endif
    pMyVTab->nMyRef --;

    delete pMyCursor->poFeature;
    delete pMyCursor->poDupDataSource;

    CPLFree(pCursor);

    return SQLITE_OK;
}

/************************************************************************/
/*                     OGR2SQLITESpatialIndex_Filter()                  */
/************************************************************************/

static
int OGR2SQLITESpatialIndex_Filter(sqlite3_vtab_cursor* pCursor,
                      int idxNum, const char *idxStr,
                      int argc, sqlite3_value **argv)
{
    OGR2SQLITESpatialIndex_vtab_cursor* pMyCursor = (OGR2SQLITESpatialIndex_vtab_cursor*) pCursor;
#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "Filter");
#endif

    int* panConstraints = (int*) idxStr;
    int nConstraints = panConstraints ? panConstraints[0] : 0;

    if( nConstraints != argc )
        return SQLITE_ERROR;

    int i;
    double dfMinX = 0, dfMaxX = 0, dfMinY = 0, dfMaxY = 0;
    for (i = 0; i < argc; i++)
    {
        int nCol = panConstraints[2 * i + 1];
        if( nCol < 0 )
            return SQLITE_ERROR;

        double dfVal;
        if (sqlite3_value_type (argv[i]) == SQLITE_INTEGER)
            dfVal = sqlite3_value_int64 (argv[i]);
        else if (sqlite3_value_type (argv[i]) == SQLITE_FLOAT)
            dfVal = sqlite3_value_double (argv[i]);
        else
            return SQLITE_ERROR;

        if( nCol == 1 )
            dfMaxX = dfVal;
        else if( nCol == 2 )
            dfMinX = dfVal;
        else if( nCol == 3 )
            dfMaxY = dfVal;
        else if( nCol == 4 )
            dfMinY = dfVal;
        else
            return SQLITE_ERROR;
    }

#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "Spatial filter : %.18g, %.18g, %.18g, %.18g",
              dfMinX, dfMinY, dfMaxX, dfMaxY);
#endif

    pMyCursor->poLayer->SetSpatialFilterRect(dfMinX, dfMinY, dfMaxX, dfMaxY);
    pMyCursor->poLayer->ResetReading();

    pMyCursor->poFeature = pMyCursor->poLayer->GetNextFeature();
    pMyCursor->bHasSetBounds = FALSE;

    return SQLITE_OK;
}

/************************************************************************/
/*                    OGR2SQLITESpatialIndex_Next()                     */
/************************************************************************/

static
int OGR2SQLITESpatialIndex_Next(sqlite3_vtab_cursor* pCursor)
{
    OGR2SQLITESpatialIndex_vtab_cursor* pMyCursor = (OGR2SQLITESpatialIndex_vtab_cursor*) pCursor;
#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "Next");
#endif

    delete pMyCursor->poFeature;
    pMyCursor->poFeature = pMyCursor->poLayer->GetNextFeature();
    pMyCursor->bHasSetBounds = FALSE;

    return SQLITE_OK;
}

/************************************************************************/
/*                      OGR2SQLITESpatialIndex_Eof()                    */
/************************************************************************/

static
int OGR2SQLITESpatialIndex_Eof(sqlite3_vtab_cursor* pCursor)
{
    OGR2SQLITESpatialIndex_vtab_cursor* pMyCursor = (OGR2SQLITESpatialIndex_vtab_cursor*) pCursor;
#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "Eof");
#endif

    return (pMyCursor->poFeature == NULL);
}

/************************************************************************/
/*                    OGR2SQLITESpatialIndex_Column()                   */
/************************************************************************/

static
int OGR2SQLITESpatialIndex_Column(sqlite3_vtab_cursor* pCursor,
                      sqlite3_context* pContext, int nCol)
{
    OGR2SQLITESpatialIndex_vtab_cursor* pMyCursor = (OGR2SQLITESpatialIndex_vtab_cursor*) pCursor;
    OGRFeature* poFeature;
#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "Column %d", nCol);
#endif

    poFeature = pMyCursor->poFeature;
    if( poFeature == NULL)
        return SQLITE_ERROR;

    if( nCol == 0 )
    {
        CPLDebug("OGR2SQLITE", "--> FID = %ld", poFeature->GetFID());
        sqlite3_result_int64(pContext, poFeature->GetFID());
        return SQLITE_OK;
    }

    if( !pMyCursor->bHasSetBounds )
    {
        OGRGeometry* poGeom = poFeature->GetGeometryRef();
        if( poGeom != NULL && !poGeom->IsEmpty() )
        {
            OGREnvelope sEnvelope;
            poGeom->getEnvelope(&sEnvelope);
            pMyCursor->bHasSetBounds = TRUE;
            pMyCursor->dfMinX = sEnvelope.MinX;
            pMyCursor->dfMinY = sEnvelope.MinY;
            pMyCursor->dfMaxX = sEnvelope.MaxX;
            pMyCursor->dfMaxY = sEnvelope.MaxY;
        }
    }
    if( !pMyCursor->bHasSetBounds )
    {
        sqlite3_result_null(pContext);
        return SQLITE_OK;
    }

    if( nCol == 1 )
    {
        sqlite3_result_double(pContext, pMyCursor->dfMinX);
        return SQLITE_OK;
    }
    if( nCol == 2 )
    {
        sqlite3_result_double(pContext, pMyCursor->dfMaxX);
        return SQLITE_OK;
    }
    if( nCol == 3 )
    {
        sqlite3_result_double(pContext, pMyCursor->dfMinY);
        return SQLITE_OK;
    }
    if( nCol == 4 )
    {
        sqlite3_result_double(pContext, pMyCursor->dfMaxY);
        return SQLITE_OK;
    }

    return SQLITE_ERROR;
}

/************************************************************************/
/*                    OGR2SQLITESpatialIndex_Rowid()                    */
/************************************************************************/

static
int OGR2SQLITESpatialIndex_Rowid(sqlite3_vtab_cursor* pCursor, sqlite3_int64 *pRowid)
{
#ifdef DEBUG_OGR2SQLITE
    CPLDebug("OGR2SQLITE", "Rowid");
#endif

    return SQLITE_ERROR;
}

/************************************************************************/
/*                   OGR2SQLITESpatialIndex_Rename()                    */
/************************************************************************/

static
int OGR2SQLITESpatialIndex_Rename(sqlite3_vtab *pVtab, const char *zNew)
{
    //CPLDebug("OGR2SQLITE", "Rename");
    return SQLITE_ERROR;
}

/************************************************************************/
/*                       sOGR2SQLITESpatialIndex                        */
/************************************************************************/

static const struct sqlite3_module sOGR2SQLITESpatialIndex =
{
    1, /* iVersion */
    OGR2SQLITESpatialIndex_ConnectCreate, /* xCreate */
    OGR2SQLITESpatialIndex_ConnectCreate, /* xConnect */
    OGR2SQLITESpatialIndex_BestIndex,
    OGR2SQLITESpatialIndex_DisconnectDestroy, /* xDisconnect */
    OGR2SQLITESpatialIndex_DisconnectDestroy, /* xDestroy */
    OGR2SQLITESpatialIndex_Open,
    OGR2SQLITESpatialIndex_Close,
    OGR2SQLITESpatialIndex_Filter,
    OGR2SQLITESpatialIndex_Next,
    OGR2SQLITESpatialIndex_Eof,
    OGR2SQLITESpatialIndex_Column,
    OGR2SQLITESpatialIndex_Rowid,
    NULL, /* xUpdate */
    NULL, /* xBegin */
    NULL, /* xSync */
    NULL, /* xCommit */
    NULL, /* xFindFunctionRollback */
    NULL, /* xFindFunction */
    OGR2SQLITESpatialIndex_Rename
};
#endif // ENABLE_VIRTUAL_OGR_SPATIAL_INDEX

/************************************************************************/
/*                              Setup()                                 */
/************************************************************************/

int OGR2SQLITEModule::Setup(sqlite3* hDB)
{
    int rc;

    this->hDB = hDB;

    rc = sqlite3_create_module_v2(hDB, "VirtualOGR", &sOGR2SQLITEModule, this,
                                  OGR2SQLITEDestroyModule);
    if( rc != SQLITE_OK )
        return FALSE;

#ifdef ENABLE_VIRTUAL_OGR_SPATIAL_INDEX
    rc = sqlite3_create_module(hDB, "VirtualOGRSpatialIndex",
                                &sOGR2SQLITESpatialIndex, this);
    if( rc != SQLITE_OK )
        return FALSE;
#endif // ENABLE_VIRTUAL_OGR_SPATIAL_INDEX

    rc= sqlite3_create_function(hDB, "ogr_layer_Extent", 1, SQLITE_ANY, this,
                                OGR2SQLITE_ogr_layer_Extent, NULL, NULL);
    if( rc != SQLITE_OK )
        return FALSE;

    rc= sqlite3_create_function(hDB, "ogr_layer_SRID", 1, SQLITE_ANY, this,
                                OGR2SQLITE_ogr_layer_SRID, NULL, NULL);
    if( rc != SQLITE_OK )
        return FALSE;

    rc= sqlite3_create_function(hDB, "ogr_layer_GeometryType", 1, SQLITE_ANY, this,
                                OGR2SQLITE_ogr_layer_GeometryType, NULL, NULL);
    if( rc != SQLITE_OK )
        return FALSE;

    rc= sqlite3_create_function(hDB, "ogr_layer_FeatureCount", 1, SQLITE_ANY, this,
                                OGR2SQLITE_ogr_layer_FeatureCount, NULL, NULL);
    if( rc != SQLITE_OK )
        return FALSE;

    SetHandleSQLFunctions(OGRSQLiteRegisterSQLFunctions(hDB));

    return TRUE;
}

/************************************************************************/
/*                        OGR2SQLITE_Setup()                            */
/************************************************************************/

OGR2SQLITEModule* OGR2SQLITE_Setup(OGRDataSource* poDS,
                                   OGRSQLiteDataSource* poSQLiteDS)
{
    OGR2SQLITEModule* poModule = new OGR2SQLITEModule();
    poModule->Setup(poDS, poSQLiteDS);
    return poModule;
}

/************************************************************************/
/*                       OGR2SQLITE_AddExtraDS()                        */
/************************************************************************/

int OGR2SQLITE_AddExtraDS(OGR2SQLITEModule* poModule, OGRDataSource* poDS)
{
    return poModule->AddExtraDS(poDS);
}

#ifdef VIRTUAL_OGR_DYNAMIC_EXTENSION_ENABLED

/************************************************************************/
/*                        sqlite3_extension_init()                      */
/************************************************************************/

CPL_C_START
int CPL_DLL sqlite3_extension_init (sqlite3 * hDB, char **pzErrMsg,
                                    const sqlite3_api_routines * pApi);
CPL_C_END

/* Entry point for dynamically loaded extension (typically called by load_extension()) */
int sqlite3_extension_init (sqlite3 * hDB, char **pzErrMsg,
                            const sqlite3_api_routines * pApi)
{
    CPLDebug("OGR", "OGR SQLite extension loading...");

    SQLITE_EXTENSION_INIT2(pApi);

    *pzErrMsg = NULL;

    OGRRegisterAll();

    OGR2SQLITEModule* poModule = new OGR2SQLITEModule();
    if( poModule->Setup(hDB) )
    {
        CPLDebug("OGR", "OGR SQLite extension loaded");
        return SQLITE_OK;
    }
    else
        return SQLITE_ERROR;
}

#endif // VIRTUAL_OGR_DYNAMIC_EXTENSION_ENABLED

/************************************************************************/
/*                        OGR2SQLITE_static_register()                  */
/************************************************************************/

CPL_C_START
int CPL_DLL OGR2SQLITE_static_register (sqlite3 * hDB, char **pzErrMsg,
                                        const sqlite3_api_routines * pApi);
CPL_C_END

#ifndef WIN32
extern const struct sqlite3_api_routines OGRSQLITE_static_routines;
#endif

int OGR2SQLITE_static_register (sqlite3 * hDB, char **pzErrMsg,
                                const sqlite3_api_routines * pApi)
{
#ifndef WIN32
    if( pApi->create_module == NULL )
    {
        pApi = &OGRSQLITE_static_routines;
    }
#endif
    SQLITE_EXTENSION_INIT2 (pApi);

    *pzErrMsg = NULL;

    /* The config option is turned off by ogrsqliteexecutesql.cpp that needs */
    /* to create a custom module */
    if( CSLTestBoolean(CPLGetConfigOption("OGR_SQLITE_STATIC_VIRTUAL_OGR", "YES")) )
    {
        /* Can happen if sqlite is compiled with SQLITE_OMIT_LOAD_EXTENSION (with sqlite 3.6.10 for example) */
        /* We return here OK since it is not vital for regular SQLite dababases */
        /* to load the OGR SQL functions */
        if( pApi->create_module == NULL )
            return SQLITE_OK;

        OGR2SQLITEModule* poModule = new OGR2SQLITEModule();
        return poModule->Setup(hDB) ? SQLITE_OK : SQLITE_ERROR;
    }
    else
    {
        /* Can happen if sqlite is compiled with SQLITE_OMIT_LOAD_EXTENSION (with sqlite 3.6.10 for example) */
        /* We return fail since Setup() will later be called, and crash */
        /* if create_module isn't available */
        if( pApi->create_module == NULL )
            return SQLITE_ERROR;
    }

    return SQLITE_OK;
}

/************************************************************************/
/*                           OGR2SQLITE_Register()                      */
/************************************************************************/

/* We call this function so that each time a db is created, */
/* OGR2SQLITE_static_register is called, to initialize the sqlite3_api */
/* structure with the right pointers. */

void OGR2SQLITE_Register()
{
    sqlite3_auto_extension ((void (*)(void)) OGR2SQLITE_static_register);
}

#endif // HAVE_SQLITE_VFS

redpajama_set_name: RedPajamaGithub


Be ready for the any adventure with both the longsleeve and t-shirt in organic wool and silk, whether it be in the city or in the mountains. Choose your preferred color and size for both products.
Wool because it is the golden standard in most situations. Organic because it makes a difference. Silk because it softens, strengthens, and improves wool's temperature regulating qualities.
Our merino wool fibres are only 16.3 microns thick, less than half as thick as a human hair. Some of the finest merino wool is only found in our products.
Produced in Europe: We produce our products in Lithuania. We do this to ensure quality and reduce transportation emission. European production comes at a price, but that means that in return you get a product that lasts longer with fewer kilometers travelled. Please use our garments until they are worn out.


text:
Holland pitches plan to revitalize Portland neighborhood
Updated: 10:39 PM EST Jan 6, 2016
Mark Vanderhoff
Gill Holland is known for his work turning East Market Street into the lively NuLu neighborhood. But can he do the same for Louisville's Portland neighborhood?Holland said it is coming along, slowly but surely. He said the historic neighborhood is full of potential and points to new businesses in the area."And we're basically trying to find the worst building in each block to buy," Holland said.Holland's vision for boarded-up homes and vacant warehouses in the Portland neighborhood is buy them cheap, fix them up and sell them.On Wednesday, he pitched his 10-year plan to Venture Connections, a networking group for investors and entrepreneurs.Holland helped revitalize Louisville's NuLu neighborhood.His latest project, the Portland investment initiative, focuses on the neighborhood north of Market Street and west of downtown.He has bought 35 properties so far and his plan calls for $24 million in investments. That's attracted newcomers like Joey Eaton, who bought a home on 32nd Street."I love the people, I love the history, I love the potential," Eaton said.Eaton was working in Quixote Coffee Shop, which opened a little more than one year ago. It's next to an art gallery and down the street from a new restaurant.Many residents, like Barbara Horton, support Holland's efforts."Have nice homes, you know, build nice homes, be in a nice neighborhood. We don't have to be around all these drugs and people, 'cause there's a whole lot of them around here," Horton said.But others, like Leta Cottrell, are worried Holland's plans will push longtime residents out of the neighborhood."Instead of him wanting to build restaurants and build different buildings, he should build some stuff to maybe help get all these teens off the street, too," Cottrell said.Holland said his renovated vacant homes will be sold to clients of affordable-housing programs. The first deal closes in two weeks."We are working with affordable-housing advocates. We are excited about all the people living in the area. We want people to stay in the area," Holland said. "The reality is, there's 1,400 vacant and abandoned properties, so we need to repopulate the area."Holland said there are a lot of other promising developments on the horizon. The city has a plan for a new park along Portland's Riverfront. And they just closed on a historic building that will be home to nonprofits trying to help Portland residents.
Gill Holland is known for his work turning East Market Street into the lively NuLu neighborhood. But can he do the same for Louisville's Portland neighborhood?
Holland said it is coming along, slowly but surely. He said the historic neighborhood is full of potential and points to new businesses in the area.
"And we're basically trying to find the worst building in each block to buy," Holland said.
Holland's vision for boarded-up homes and vacant warehouses in the Portland neighborhood is buy them cheap, fix them up and sell them.
On Wednesday, he pitched his 10-year plan to Venture Connections, a networking group for investors and entrepreneurs.
Holland helped revitalize Louisville's NuLu neighborhood.
His latest project, the Portland investment initiative, focuses on the neighborhood north of Market Street and west of downtown.
He has bought 35 properties so far and his plan calls for $24 million in investments. That's attracted newcomers like Joey Eaton, who bought a home on 32nd Street.
"I love the people, I love the history, I love the potential," Eaton said.
Eaton was working in Quixote Coffee Shop, which opened a little more than one year ago. It's next to an art gallery and down the street from a new restaurant.
Many residents, like Barbara Horton, support Holland's efforts.
"Have nice homes, you know, build nice homes, be in a nice neighborhood. We don't have to be around all these drugs and people, 'cause there's a whole lot of them around here," Horton said.
But others, like Leta Cottrell, are worried Holland's plans will push longtime residents out of the neighborhood.
"Instead of him wanting to build restaurants and build different buildings, he should build some stuff to maybe help get all these teens off the street, too," Cottrell said.
Holland said his renovated vacant homes will be sold to clients of affordable-housing programs. The first deal closes in two weeks.
"We are working with affordable-housing advocates. We are excited about all the people living in the area. We want people to stay in the area," Holland said. "The reality is, there's 1,400 vacant and abandoned properties, so we need to repopulate the area."
Holland said there are a lot of other promising developments on the horizon. The city has a plan for a new park along Portland's Riverfront. And they just closed on a historic building that will be home to nonprofits trying to help Portland residents.

redpajama_set_name: RedPajamaCommonCrawl


text:
The Race Against Resistance
Inside small pharma's battle to save us from superbugs
by Lisa Scanlon Mogolov
Science-Biomedicine
Health-Health Care and Treatment
illustration by Marcos Chin
It's hard to overstate the impact that antibiotics had on medicine after they became widely available in the 1940s. In the United States, the leading causes of death shifted from infectious to noncommunicable diseases such as heart conditions and cancer. Antibiotics helped lift the average life expectancy at birth from 54 years in 1920 to 78.6 today. But almost as soon as the treatments were made available, physicians had to cope with the specter of antibiotic resistance, a fight that has become increasingly desperate in recent years with the rise of antibiotic-resistant superbugs.
"We are approaching a cliff," says Anna Diaz Triola (MBA 2000), vice president of marketing at Summit Therapeutics in Cambridge, Massachusetts, which is part of a cohort of small pharma companies that, against the odds, are trying to turn the superbug tide. "If we don't take steps to slow down or stop drug resistance, we will fall back to a time when simple infections killed people. Can you imagine? That's a bit of a doomsday perspective, but it's a call to action."
"We are approaching a cliff. If we don't take steps to slow down or stop drug resistance, we will fall back to a time when simple infections killed people."
The World Health Organization echoes Triola's sense of urgency, warning that antibiotic resistance presents one of the greatest threats to health, food security, and development worldwide. And according to the Centers for Disease Control and Prevention, each year in the United States at least 2 million people get an antibiotic-resistant infection, and at least 23,000 people die as a result. And yet, there are only 42 antibiotics currently in clinical development, and typically only 20 percent of infectious disease drugs that enter phase 1 clinical trials will receive FDA approval.
Eric Kimble (MBA 1994), chief commercial officer at Entasis Therapeutics in Waltham, Massachusetts, suggests two main reasons why so few new antibiotics are in the pipeline. First, antibiotics are used for relatively short courses and, as a result, aren't as profitable as chronic-care medicines. Second, in the United States, hospitals budget for all the drugs used in a facility, and new antibiotics—which are priced higher than their older counterparts to recoup development costs—have to compete for a share of that finite budget.
In recent years, many large pharma companies have stopped their antibiotic research altogether, with small pharma trying to fill the void—often unsuccessfully. Kimble points to San Francisco–based Achaogen, which filed for bankruptcy last April despite securing regulatory approval for one of its antibiotic treatments in 2018.
Given the catch-22s of making a profit on antibiotics, what's a pharma startup to do? Begin with the pharmacoeconomic data, Kimble says: "We need to justify the higher prices of our drugs, and we need to do a better job of helping hospitals understand that by using a more extensive antibiotic up front compared to the older generic version that has some resistance, we are saving those institutions money [by ensuring], for example, fewer days in the hospital." Triola echoes this argument. "Without any economic data to justify the use of premium-priced treatments over cheaper generics, how are you going to make the case that a hospital system should spend dollars on a branded agent against pennies of a generic agent?"
Small pharma can also focus on one bug at a time instead of broad-spectrum drugs that attack whole swaths of them, potentially fostering resistance. For example, one drug in Entasis's pipeline fights Acinetobacter baumannii, a lethal bug that tends to travel down endotracheal tubes and often causes pneumonia in ICU patients. When the drug goes to market, the company plans to have data to back up its price. "If we say, for instance, that we can wean a patient off a ventilator three days sooner…and we get a patient out of the ICU four days sooner...then that's a justified cost savings to the hospital, which we can then use to price our drug appropriately," Kimble says.
Kimble and Triola also stress the importance of developing antibiotics that can be used both in a hospital setting and at home, reflecting a dramatic change in the industry in recent years. "The business model essentially has moved into the outpatient setting," Triola explains. "So while antibiotics will largely be initiated in a hospital setting, how do we set up transitions of care? How do we create pathways to ensure that our patients who have the most urgent needs receive the right antibiotics, wherever they might need them?"
Apart from everything that small pharma is doing to get drugs to patients, there are a number of initiatives to support their efforts. One such effort is CARB-X (Combating Antibiotic-Resistant Bacteria), a Boston University–based public-private partnership launched in 2016 by the US Department of Health and Human Services. The initiative is investing $500 million over the next few years in the research and development of products to tackle drug-resistant bacteria, including the work of Kimble's and Triola's companies.
The pendulum is starting to swing. Awareness is spreading that these superbugs are changing more rapidly than we are, and that the existing model is broken, Kimble says. It's going to take some combination of private and public funds, working together with industry, "to make sure that the bugs don't win."
Anna Triola
MBA 2000, Section A
Eric Kimble
MBA 1994, Section F
BostInno
Inside the Nonprofit Aiming to Disrupt Big Pharma, One Case Study at a Time
Re: Richard Horgan (MBA 2018)
Vertex Appoints First Female CEO
Re: Reshmak Kewalramani (GMP 18)


To check whether or not provided VAST is correct and contains video.Paste the VAST URL in below webpage's VAST URL section.
Google have added new functionality checks like IMA SDK (Interactive Media Ads Software Development Kit which is used for requesting video ads.
Use the video ad tag URL. As described in other articles, you can pick up the URL in DFP or create the URL manually.The URL includes all of the parameters for the video ad tag.
Create objects for ad requests and specify the appropriate parameters.
With this option, you construct a video ad tag using ActionScript 3. The ActionScript code calls on the IMA SDK.
If there is %n present in the VAST URL make sure to replace it with [timestamp] because for Google Inspector will show an error if %n is not replaced.
Test JW Player's video ad capabilities using our VAST MP4, Liverail VPAID, and Google IMA sample tags or with your own ad tag.
The JW Player Ad Tester logs ad events as they fire.
SpringServe VAST Tag Tester, Share the result with the client.
How To Identify VAST and VPAID ?


text:
Sterre Laurien Kalis (born 30 August 1999) is a Dutch cricketer who plays for the national cricket team as a right-handed batter. At domestic level, she plays for the English teams Northern Diamonds, Birmingham Phoenix and North East Warriors, and has previously played for Essex and Northern Superchargers. She has also taken the field for other domestic teams in the Netherlands and Australia.

Career
Kalis played for the Netherlands in the 2015 ICC Women's World Twenty20 Qualifier in November 2015.

In June 2018, Kalis was named in the Netherlands' squad for the 2018 ICC Women's World Twenty20 Qualifier tournament. She made her Women's Twenty20 International (WT20I) for the Netherlands against United Arab Emirates in the World Twenty20 Qualifier on 7 July 2018. She was the leading run-scorer for the tournament, with 231 runs in five matches. Following the conclusion of the tournament, she was named as the rising star of the Netherlands' squad by the International Cricket Council (ICC).

In July 2018, she was named in the ICC Women's Global Development Squad. In November 2018, she was again named in the Women's Global Development Squad, to play fixtures against Women's Big Bash League (WBBL) clubs.

In May 2019, she was named in Netherlands' squad for the 2019 ICC Women's Qualifier Europe tournament in Spain. On 27 June 2019, in the match against Germany, Kalis scored her first century in WT20Is, and equalled the record for the highest score in a WT20I, with 126 not out. She was the leading run-scorer in the tournament, with 158 runs in four matches.

In August 2019, she was named in the Dutch squad for the 2019 ICC Women's World Twenty20 Qualifier tournament in Scotland. She was the leading run-scorer for the Netherlands in the tournament, with 117 runs in five matches.

In November 2020, Kalis was nominated for the ICC Women's Associate Cricketer of the Decade award. In October 2021, she was named in the Dutch team for the 2021 Women's Cricket World Cup Qualifier tournament in Zimbabwe.

In April 2022, she was bought by the Birmingham Phoenix for the 2022 season of The Hundred in England. Later that month, she was announced in the North East Warriors squad for the 2022 Women's Twenty20 Cup.

In May 2022, Kalis played seven matches for the tournament-winning Tornadoes team at the 2022 FairBreak Invitational T20 in Dubai, United Arab Emirates. During the Invitational, she scored a total of 225 runs, the fifth-highest of any player, including two fifties, and two other scores in excess of 40. She was also awarded player of the match for top scoring with 32 in her team's first match, against the Sapphires team.

References

External links

1999 births
Living people
Dutch women cricketers
Netherlands women Twenty20 International cricketers
Sportspeople from The Hague
Essex women cricketers
Northern Diamonds cricketers
Northern Superchargers cricketers
North East Warriors cricketers

redpajama_set_name: RedPajamaWikipedia


Life Insurance – directly to you.
Premium Life Direct offers the comprehensive Life Insurance you would normally access through a financial adviser.When you choose NobleOak, you're choosing a trusted Australian insurer with over 140 years of experience and customer client service that's second to none.
Please refer to the Premium Life Direct Product Disclosure Statement for more information.
more certainty at claim time.
Our Life cover premiums are on average 20%* lower than comparable cover. This is because we don't pay adviser fees and pass the savings back to you.
You speak to a local person and we guide you through your application. That's why we received a satisfaction score of 98.5%# from our clients.
Feel secure knowing you're covered by the winner of the Canstar Outstanding Value, Strategic Insight Overall Excellence and FEEFO Gold Trusted Services awards.
Life Insurance tailored to you.
the cover you need in one product.
Protect the financial security of your loved ones if you die or are diagnosed with a terminal illness. Up to $15 million of cover available.
Protect your income if you become ill or injured and can't work. Provides up to 75% of your income (up to $25,000 per month).
Protect yourself and your family from financial stress if you're permanently disabled and unable to work. Up to $5 million cover available.
Focus on your recovery if you suffer from a critical illness. 38 conditions are covered, including cancer, heart attack and stroke.
Keep your business running if you can't work due to illness or injury. Provides protection for fixed business expenses up to $25,000 per month.
Our Life and TPD cover can be written within an SMSF. These covers meet all Superannuation Industry Supervision Act (SIS) requirements.
Get covered in 3 simple steps.
Our friendly insurance specialists will help you get your free Life Insurance quote over the phone or online.
We'll assess your application. If you need medical tests we'll pay for them and update throughout the process.
The 20% savings quoted are the average savings when comparing NobleOak's premiums for its Term Life cover under NobleOak's Premium Life Direct product to the average cost of Term Life insurance products offered by other Life Insurance companies, including products available directly from the insurer (8 products included in this comparison) and those available for purchase through a financial adviser or broker (7 products included in this comparison).Based on premium rates available at 31 October 2018, Strategic Insight has calculated that for Term Life, Noble Oak's Premium Life Direct product is on average 45% lower than other comparable Direct Offerings (using an analysis of 8 products, Male and Female non-smoker White Collar workers with a Sum Insured of $500,000 and across 5 year age bands from age 30 to age 50).Strategic Insight has also calculated that for Term Life, Noble Oak's Premium Life Direct product is on average 12% lower than Advised Offerings (using an analysis of 7 products, Male and Female non-smoker White Collar workers with a Sum Insured of $500,000 and across 5 year age bands from age 30 to age 60 for Advised products).Life Insurance rates for insurers, including NobleOak, may change in the future and this could change the outcome.Strategic Insight (Actuaries and Researchers) is the leading independent supplier of Australian Life Insurance and Managed Funds market information, relied upon for over 20 years by the leading life offices, analysts, dealer groups and government bodies.If you wish to see the comparison tables and how our premiums compare to Direct and Advised products, please click here.
2017 client survey by Pureprofile.