LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/amigocloud - ogramigoclouddatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 0 428 0.0 %
Date: 2025-10-25 23:36:32 Functions: 0 26 0.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  AmigoCloud Translator
       4             :  * Purpose:  Implements OGRAmigoCloudDataSource class
       5             :  * Author:   Victor Chernetsky, <victor at amigocloud dot com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2015, Victor Chernetsky, <victor at amigocloud dot com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_multiproc.h"  // CPLSleep()
      14             : 
      15             : #include "ogr_amigocloud.h"
      16             : #include "ogr_pgdump.h"
      17             : #include "ogrlibjsonutils.h"
      18             : #include <sstream>
      19             : 
      20             : CPLString OGRAMIGOCLOUDGetOptionValue(const char *pszFilename,
      21             :                                       const char *pszOptionName);
      22             : 
      23             : /************************************************************************/
      24             : /*                        OGRAmigoCloudDataSource()                        */
      25             : /************************************************************************/
      26             : 
      27           0 : OGRAmigoCloudDataSource::OGRAmigoCloudDataSource()
      28             :     : pszProjectId(nullptr), papoLayers(nullptr), nLayers(0), bReadWrite(false),
      29           0 :       bUseHTTPS(true), bMustCleanPersistent(false), bHasOGRMetadataFunction(-1)
      30             : {
      31           0 : }
      32             : 
      33             : /************************************************************************/
      34             : /*                       ~OGRAmigoCloudDataSource()                        */
      35             : /************************************************************************/
      36             : 
      37           0 : OGRAmigoCloudDataSource::~OGRAmigoCloudDataSource()
      38             : 
      39             : {
      40           0 :     for (int i = 0; i < nLayers; i++)
      41           0 :         delete papoLayers[i];
      42           0 :     CPLFree(papoLayers);
      43             : 
      44           0 :     if (bMustCleanPersistent)
      45             :     {
      46           0 :         char **papszOptions = nullptr;
      47           0 :         papszOptions = CSLSetNameValue(papszOptions, "CLOSE_PERSISTENT",
      48             :                                        CPLSPrintf("AMIGOCLOUD:%p", this));
      49           0 :         papszOptions = CSLAddString(papszOptions, GetUserAgentOption().c_str());
      50           0 :         CPLHTTPDestroyResult(CPLHTTPFetch(GetAPIURL(), papszOptions));
      51           0 :         CSLDestroy(papszOptions);
      52             :     }
      53             : 
      54           0 :     CPLFree(pszProjectId);
      55           0 : }
      56             : 
      57           0 : std::string OGRAmigoCloudDataSource::GetUserAgentOption()
      58             : {
      59           0 :     std::stringstream userAgent;
      60             :     userAgent << "USERAGENT=gdal/AmigoCloud build:"
      61           0 :               << GDALVersionInfo("RELEASE_NAME");
      62           0 :     return userAgent.str();
      63             : }
      64             : 
      65             : /************************************************************************/
      66             : /*                           TestCapability()                           */
      67             : /************************************************************************/
      68             : 
      69           0 : int OGRAmigoCloudDataSource::TestCapability(const char *pszCap) const
      70             : 
      71             : {
      72           0 :     if (bReadWrite && EQUAL(pszCap, ODsCCreateLayer) && nLayers == 0)
      73           0 :         return TRUE;
      74           0 :     else if (bReadWrite && EQUAL(pszCap, ODsCDeleteLayer))
      75           0 :         return TRUE;
      76           0 :     else if (EQUAL(pszCap, ODsCRandomLayerWrite))
      77           0 :         return bReadWrite;
      78           0 :     else if (EQUAL(pszCap, ODsCZGeometries))
      79           0 :         return TRUE;
      80             :     else
      81           0 :         return FALSE;
      82             : }
      83             : 
      84             : /************************************************************************/
      85             : /*                              GetLayer()                              */
      86             : /************************************************************************/
      87             : 
      88           0 : const OGRLayer *OGRAmigoCloudDataSource::GetLayer(int iLayer) const
      89             : {
      90           0 :     if (iLayer < 0 || iLayer >= nLayers)
      91           0 :         return nullptr;
      92             :     else
      93           0 :         return papoLayers[iLayer];
      94             : }
      95             : 
      96             : /************************************************************************/
      97             : /*                          GetLayerByName()                            */
      98             : /************************************************************************/
      99             : 
     100           0 : OGRLayer *OGRAmigoCloudDataSource::GetLayerByName(const char *pszLayerName)
     101             : {
     102           0 :     if (nLayers > 1)
     103             :     {
     104           0 :         return GDALDataset::GetLayerByName(pszLayerName);
     105             :     }
     106           0 :     else if (nLayers == 1)
     107             :     {
     108           0 :         return papoLayers[0];
     109             :     }
     110           0 :     return nullptr;
     111             : }
     112             : 
     113             : /************************************************************************/
     114             : /*                     OGRAMIGOCLOUDGetOptionValue()                       */
     115             : /************************************************************************/
     116             : 
     117           0 : CPLString OGRAMIGOCLOUDGetOptionValue(const char *pszFilename,
     118             :                                       const char *pszOptionName)
     119             : {
     120           0 :     CPLString osOptionName(pszOptionName);
     121           0 :     osOptionName += "=";
     122           0 :     const char *pszOptionValue = strstr(pszFilename, osOptionName);
     123           0 :     if (!pszOptionValue)
     124           0 :         return "";
     125             : 
     126           0 :     CPLString osOptionValue(pszOptionValue + osOptionName.size());
     127           0 :     const char *pszSpace = strchr(osOptionValue.c_str(), ' ');
     128           0 :     if (pszSpace)
     129           0 :         osOptionValue.resize(pszSpace - osOptionValue.c_str());
     130           0 :     return osOptionValue;
     131             : }
     132             : 
     133           0 : bool OGRAmigoCloudDataSource::ListDatasets()
     134             : {
     135           0 :     std::stringstream url;
     136           0 :     url << std::string(GetAPIURL()) << "/users/0/projects/"
     137           0 :         << std::string(GetProjectId()) << "/datasets/?summary";
     138           0 :     json_object *result = RunGET(url.str().c_str());
     139           0 :     if (result == nullptr)
     140             :     {
     141           0 :         CPLError(CE_Failure, CPLE_AppDefined, "AmigoCloud:get failed.");
     142           0 :         return false;
     143             :     }
     144             : 
     145             :     {
     146           0 :         auto type = json_object_get_type(result);
     147           0 :         if (type == json_type_object)
     148             :         {
     149             :             json_object *poResults =
     150           0 :                 CPL_json_object_object_get(result, "results");
     151           0 :             if (poResults != nullptr &&
     152           0 :                 json_object_get_type(poResults) == json_type_array)
     153             :             {
     154           0 :                 CPLprintf("List of available datasets for project id: %s\n",
     155             :                           GetProjectId());
     156           0 :                 CPLprintf("| id \t | name\n");
     157           0 :                 CPLprintf("|--------|-------------------\n");
     158           0 :                 const auto nSize = json_object_array_length(poResults);
     159           0 :                 for (auto i = decltype(nSize){0}; i < nSize; ++i)
     160             :                 {
     161           0 :                     json_object *ds = json_object_array_get_idx(poResults, i);
     162           0 :                     if (ds != nullptr)
     163             :                     {
     164           0 :                         const char *name = nullptr;
     165           0 :                         int64_t dataset_id = 0;
     166             :                         json_object *poName =
     167           0 :                             CPL_json_object_object_get(ds, "name");
     168           0 :                         if (poName != nullptr)
     169             :                         {
     170           0 :                             name = json_object_get_string(poName);
     171             :                         }
     172             :                         json_object *poId =
     173           0 :                             CPL_json_object_object_get(ds, "id");
     174           0 :                         if (poId != nullptr)
     175             :                         {
     176           0 :                             dataset_id = json_object_get_int64(poId);
     177             :                         }
     178           0 :                         if (name != nullptr)
     179             :                         {
     180           0 :                             std::stringstream str;
     181           0 :                             str << "| " << dataset_id << "\t | " << name;
     182           0 :                             CPLprintf("%s\n", str.str().c_str());
     183             :                         }
     184             :                     }
     185             :                 }
     186             :             }
     187             :         }
     188           0 :         json_object_put(result);
     189             :     }
     190           0 :     return true;
     191             : }
     192             : 
     193             : /************************************************************************/
     194             : /*                                Open()                                */
     195             : /************************************************************************/
     196             : 
     197           0 : int OGRAmigoCloudDataSource::Open(const char *pszFilename,
     198             :                                   char **papszOpenOptionsIn, int bUpdateIn)
     199             : 
     200             : {
     201             : 
     202           0 :     bReadWrite = CPL_TO_BOOL(bUpdateIn);
     203             : 
     204           0 :     pszProjectId = CPLStrdup(pszFilename + strlen("AMIGOCLOUD:"));
     205           0 :     char *pchSpace = strchr(pszProjectId, ' ');
     206           0 :     if (pchSpace)
     207           0 :         *pchSpace = '\0';
     208           0 :     if (pszProjectId[0] == 0)
     209             :     {
     210           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Missing project id");
     211           0 :         return FALSE;
     212             :     }
     213             : 
     214             :     osAPIKey =
     215             :         CSLFetchNameValueDef(papszOpenOptionsIn, "AMIGOCLOUD_API_KEY",
     216           0 :                              CPLGetConfigOption("AMIGOCLOUD_API_KEY", ""));
     217             : 
     218           0 :     if (osAPIKey.empty())
     219             :     {
     220             :         osAPIKey =
     221           0 :             OGRAMIGOCLOUDGetOptionValue(pszFilename, "AMIGOCLOUD_API_KEY");
     222             :     }
     223           0 :     if (osAPIKey.empty())
     224             :     {
     225           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     226             :                  "AMIGOCLOUD_API_KEY is not defined.\n");
     227           0 :         return FALSE;
     228             :     }
     229             : 
     230           0 :     OGRLayer *poSchemaLayer = ExecuteSQLInternal("SELECT current_schema()");
     231           0 :     if (poSchemaLayer)
     232             :     {
     233           0 :         OGRFeature *poFeat = poSchemaLayer->GetNextFeature();
     234           0 :         if (poFeat)
     235             :         {
     236           0 :             if (poFeat->GetFieldCount() == 1)
     237             :             {
     238           0 :                 osCurrentSchema = poFeat->GetFieldAsString(0);
     239             :             }
     240           0 :             delete poFeat;
     241             :         }
     242           0 :         ReleaseResultSet(poSchemaLayer);
     243             :     }
     244           0 :     if (osCurrentSchema.empty())
     245           0 :         return FALSE;
     246             : 
     247           0 :     CPLString osDatasets = OGRAMIGOCLOUDGetOptionValue(pszFilename, "datasets");
     248           0 :     if (!osDatasets.empty())
     249             :     {
     250           0 :         char **papszTables = CSLTokenizeString2(osDatasets, ",", 0);
     251           0 :         for (int i = 0; papszTables && papszTables[i]; i++)
     252             :         {
     253             : 
     254           0 :             papoLayers = (OGRAmigoCloudTableLayer **)CPLRealloc(
     255           0 :                 papoLayers, (nLayers + 1) * sizeof(OGRAmigoCloudTableLayer *));
     256             : 
     257           0 :             papoLayers[nLayers++] =
     258           0 :                 new OGRAmigoCloudTableLayer(this, papszTables[i]);
     259             :         }
     260           0 :         CSLDestroy(papszTables);
     261             : 
     262             :         // If OVERWRITE: YES, truncate the layer.
     263           0 :         if (nLayers == 1 &&
     264           0 :             CPLFetchBool(papszOpenOptionsIn, "OVERWRITE", false))
     265             :         {
     266           0 :             TruncateDataset(papoLayers[0]->GetTableName());
     267             :         }
     268           0 :         return TRUE;
     269             :     }
     270             :     else
     271             :     {
     272             :         // If 'datasets' word is in the filename, but no dataset id specified,
     273             :         // print the list of available datasets
     274           0 :         if (std::string(pszFilename).find("datasets") != std::string::npos)
     275           0 :             ListDatasets();
     276             :     }
     277             : 
     278           0 :     return TRUE;
     279             : }
     280             : 
     281             : /************************************************************************/
     282             : /*                            GetAPIURL()                               */
     283             : /************************************************************************/
     284             : 
     285           0 : const char *OGRAmigoCloudDataSource::GetAPIURL() const
     286             : {
     287           0 :     const char *pszAPIURL = CPLGetConfigOption("AMIGOCLOUD_API_URL", nullptr);
     288           0 :     if (pszAPIURL)
     289           0 :         return pszAPIURL;
     290             : 
     291           0 :     else if (bUseHTTPS)
     292           0 :         return CPLSPrintf("https://app.amigocloud.com/api/v1");
     293             :     else
     294           0 :         return CPLSPrintf("http://app.amigocloud.com/api/v1");
     295             : }
     296             : 
     297             : /************************************************************************/
     298             : /*                             FetchSRSId()                             */
     299             : /************************************************************************/
     300             : 
     301           0 : int OGRAmigoCloudDataSource::FetchSRSId(OGRSpatialReference *poSRS)
     302             : 
     303             : {
     304           0 :     if (poSRS == nullptr)
     305           0 :         return 0;
     306             : 
     307           0 :     OGRSpatialReference oSRS(*poSRS);
     308             :     // cppcheck-suppress uselessAssignmentPtrArg
     309           0 :     poSRS = nullptr;
     310             : 
     311           0 :     const char *pszAuthorityName = oSRS.GetAuthorityName(nullptr);
     312             : 
     313           0 :     if (pszAuthorityName == nullptr || strlen(pszAuthorityName) == 0)
     314             :     {
     315             :         /* --------------------------------------------------------------------
     316             :          */
     317             :         /*      Try to identify an EPSG code */
     318             :         /* --------------------------------------------------------------------
     319             :          */
     320           0 :         oSRS.AutoIdentifyEPSG();
     321             : 
     322           0 :         pszAuthorityName = oSRS.GetAuthorityName(nullptr);
     323           0 :         if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
     324             :         {
     325           0 :             const char *pszAuthorityCode = oSRS.GetAuthorityCode(nullptr);
     326           0 :             if (pszAuthorityCode != nullptr && strlen(pszAuthorityCode) > 0)
     327             :             {
     328             :                 /* Import 'clean' SRS */
     329           0 :                 oSRS.importFromEPSG(atoi(pszAuthorityCode));
     330             : 
     331           0 :                 pszAuthorityName = oSRS.GetAuthorityName(nullptr);
     332             :             }
     333             :         }
     334             :     }
     335             :     /* -------------------------------------------------------------------- */
     336             :     /*      Check whether the EPSG authority code is already mapped to a    */
     337             :     /*      SRS ID.                                                         */
     338             :     /* -------------------------------------------------------------------- */
     339           0 :     if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
     340             :     {
     341             :         /* For the root authority name 'EPSG', the authority code
     342             :          * should always be integral
     343             :          */
     344           0 :         const int nAuthorityCode = atoi(oSRS.GetAuthorityCode(nullptr));
     345             : 
     346           0 :         return nAuthorityCode;
     347             :     }
     348             : 
     349           0 :     return 0;
     350             : }
     351             : 
     352             : /************************************************************************/
     353             : /*                          ICreateLayer()                              */
     354             : /************************************************************************/
     355             : 
     356             : OGRLayer *
     357           0 : OGRAmigoCloudDataSource::ICreateLayer(const char *pszNameIn,
     358             :                                       const OGRGeomFieldDefn *poGeomFieldDefn,
     359             :                                       CSLConstList papszOptions)
     360             : {
     361           0 :     if (!bReadWrite)
     362             :     {
     363           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     364             :                  "Operation not available in read-only mode");
     365           0 :         return nullptr;
     366             :     }
     367             : 
     368           0 :     const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
     369             :     const auto poSpatialRef =
     370           0 :         poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
     371             : 
     372           0 :     CPLString osName(pszNameIn);
     373             :     OGRAmigoCloudTableLayer *poLayer =
     374           0 :         new OGRAmigoCloudTableLayer(this, osName);
     375             :     const bool bGeomNullable =
     376           0 :         CPLFetchBool(papszOptions, "GEOMETRY_NULLABLE", true);
     377           0 :     OGRSpatialReference *poSRSClone = nullptr;
     378           0 :     if (poSpatialRef)
     379             :     {
     380           0 :         poSRSClone = poSpatialRef->Clone();
     381           0 :         poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     382             :     }
     383           0 :     poLayer->SetDeferredCreation(eGType, poSRSClone, bGeomNullable);
     384           0 :     if (poSRSClone)
     385           0 :         poSRSClone->Release();
     386           0 :     papoLayers = (OGRAmigoCloudTableLayer **)CPLRealloc(
     387           0 :         papoLayers, (nLayers + 1) * sizeof(OGRAmigoCloudTableLayer *));
     388           0 :     papoLayers[nLayers++] = poLayer;
     389             : 
     390           0 :     return poLayer;
     391             : }
     392             : 
     393             : /************************************************************************/
     394             : /*                            DeleteLayer()                             */
     395             : /************************************************************************/
     396             : 
     397           0 : OGRErr OGRAmigoCloudDataSource::DeleteLayer(int iLayer)
     398             : {
     399           0 :     if (!bReadWrite)
     400             :     {
     401           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     402             :                  "Operation not available in read-only mode");
     403           0 :         return OGRERR_FAILURE;
     404             :     }
     405             : 
     406           0 :     if (iLayer < 0 || iLayer >= nLayers)
     407             :     {
     408           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     409             :                  "Layer %d not in legal range of 0 to %d.", iLayer,
     410           0 :                  nLayers - 1);
     411           0 :         return OGRERR_FAILURE;
     412             :     }
     413             : 
     414             :     /* -------------------------------------------------------------------- */
     415             :     /*      Blow away our OGR structures related to the layer.  This is     */
     416             :     /*      pretty dangerous if anything has a reference to this layer!     */
     417             :     /* -------------------------------------------------------------------- */
     418           0 :     CPLString osDatasetId = papoLayers[iLayer]->GetDatasetId();
     419             : 
     420           0 :     CPLDebug("AMIGOCLOUD", "DeleteLayer(%s)", osDatasetId.c_str());
     421             : 
     422           0 :     int bDeferredCreation = papoLayers[iLayer]->GetDeferredCreation();
     423           0 :     papoLayers[iLayer]->CancelDeferredCreation();
     424           0 :     delete papoLayers[iLayer];
     425           0 :     memmove(papoLayers + iLayer, papoLayers + iLayer + 1,
     426           0 :             sizeof(void *) * (nLayers - iLayer - 1));
     427           0 :     nLayers--;
     428             : 
     429           0 :     if (osDatasetId.empty())
     430           0 :         return OGRERR_NONE;
     431             : 
     432           0 :     if (!bDeferredCreation)
     433             :     {
     434           0 :         std::stringstream url;
     435           0 :         url << std::string(GetAPIURL())
     436           0 :             << "/users/0/projects/" + std::string(GetProjectId()) +
     437           0 :                    "/datasets/" + osDatasetId.c_str();
     438           0 :         if (!RunDELETE(url.str().c_str()))
     439             :         {
     440           0 :             return OGRERR_FAILURE;
     441             :         }
     442             :     }
     443             : 
     444           0 :     return OGRERR_NONE;
     445             : }
     446             : 
     447             : /************************************************************************/
     448             : /*                          AddHTTPOptions()                            */
     449             : /************************************************************************/
     450             : 
     451           0 : char **OGRAmigoCloudDataSource::AddHTTPOptions()
     452             : {
     453           0 :     bMustCleanPersistent = true;
     454             : 
     455           0 :     return CSLAddString(nullptr, CPLSPrintf("PERSISTENT=AMIGOCLOUD:%p", this));
     456             : }
     457             : 
     458             : /************************************************************************/
     459             : /*                               RunPOST()                               */
     460             : /************************************************************************/
     461             : 
     462           0 : json_object *OGRAmigoCloudDataSource::RunPOST(const char *pszURL,
     463             :                                               const char *pszPostData,
     464             :                                               const char *pszHeaders)
     465             : {
     466           0 :     CPLString osURL(pszURL);
     467             : 
     468             :     /* -------------------------------------------------------------------- */
     469             :     /*      Provide the API Key                                             */
     470             :     /* -------------------------------------------------------------------- */
     471           0 :     if (!osAPIKey.empty())
     472             :     {
     473           0 :         if (osURL.find("?") == std::string::npos)
     474           0 :             osURL += "?token=";
     475             :         else
     476           0 :             osURL += "&token=";
     477           0 :         osURL += osAPIKey;
     478             :     }
     479             : 
     480           0 :     char **papszOptions = nullptr;
     481           0 :     CPLString osPOSTFIELDS("POSTFIELDS=");
     482           0 :     if (pszPostData)
     483           0 :         osPOSTFIELDS += pszPostData;
     484           0 :     papszOptions = CSLAddString(papszOptions, osPOSTFIELDS);
     485           0 :     papszOptions = CSLAddString(papszOptions, pszHeaders);
     486           0 :     papszOptions = CSLAddString(papszOptions, GetUserAgentOption().c_str());
     487             : 
     488           0 :     CPLHTTPResult *psResult = CPLHTTPFetch(osURL.c_str(), papszOptions);
     489           0 :     CSLDestroy(papszOptions);
     490           0 :     if (psResult == nullptr)
     491           0 :         return nullptr;
     492             : 
     493           0 :     if (psResult->pszContentType &&
     494           0 :         strncmp(psResult->pszContentType, "text/html", 9) == 0)
     495             :     {
     496           0 :         CPLDebug("AMIGOCLOUD", "RunPOST HTML Response: %s", psResult->pabyData);
     497           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     498             :                  "HTML error page returned by server: %s", psResult->pabyData);
     499           0 :         CPLHTTPDestroyResult(psResult);
     500           0 :         return nullptr;
     501             :     }
     502           0 :     if (psResult->pszErrBuf != nullptr && psResult->pabyData != nullptr)
     503             :     {
     504           0 :         CPLError(CE_Failure, CPLE_AppDefined, "POST Response: %s",
     505             :                  psResult->pabyData);
     506             :     }
     507           0 :     else if (psResult->nStatus != 0)
     508             :     {
     509           0 :         CPLDebug("AMIGOCLOUD", "RunPOST Error Status:%d", psResult->nStatus);
     510             :     }
     511             : 
     512           0 :     if (psResult->pabyData == nullptr)
     513             :     {
     514           0 :         CPLHTTPDestroyResult(psResult);
     515           0 :         return nullptr;
     516             :     }
     517             : 
     518           0 :     json_object *poObj = nullptr;
     519           0 :     const char *pszText = reinterpret_cast<const char *>(psResult->pabyData);
     520           0 :     if (!OGRJSonParse(pszText, &poObj, true))
     521             :     {
     522           0 :         CPLHTTPDestroyResult(psResult);
     523           0 :         return nullptr;
     524             :     }
     525             : 
     526           0 :     CPLHTTPDestroyResult(psResult);
     527             : 
     528           0 :     if (poObj != nullptr)
     529             :     {
     530           0 :         if (json_object_get_type(poObj) == json_type_object)
     531             :         {
     532           0 :             json_object *poError = CPL_json_object_object_get(poObj, "error");
     533           0 :             if (poError != nullptr &&
     534           0 :                 json_object_get_type(poError) == json_type_array &&
     535           0 :                 json_object_array_length(poError) > 0)
     536             :             {
     537           0 :                 poError = json_object_array_get_idx(poError, 0);
     538           0 :                 if (poError != nullptr &&
     539           0 :                     json_object_get_type(poError) == json_type_string)
     540             :                 {
     541           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     542             :                              "Error returned by server : %s",
     543             :                              json_object_get_string(poError));
     544           0 :                     json_object_put(poObj);
     545           0 :                     return nullptr;
     546             :                 }
     547             :             }
     548           0 :             json_object *poJob = CPL_json_object_object_get(poObj, "job");
     549           0 :             if (poJob != nullptr)
     550             :             {
     551           0 :                 const char *job = json_object_get_string(poJob);
     552           0 :                 if (job != nullptr)
     553             :                 {
     554           0 :                     waitForJobToFinish(job);
     555             :                 }
     556             :             }
     557             :         }
     558             :         else
     559             :         {
     560           0 :             json_object_put(poObj);
     561           0 :             return nullptr;
     562             :         }
     563             :     }
     564             : 
     565           0 :     return poObj;
     566             : }
     567             : 
     568           0 : bool OGRAmigoCloudDataSource::waitForJobToFinish(const char *jobId)
     569             : {
     570           0 :     std::stringstream url;
     571           0 :     url << std::string(GetAPIURL()) << "/me/jobs/" << std::string(jobId);
     572           0 :     int count = 0;
     573           0 :     while (count < 5)
     574             :     {
     575           0 :         count++;
     576           0 :         json_object *result = RunGET(url.str().c_str());
     577           0 :         if (result == nullptr)
     578             :         {
     579           0 :             CPLError(CE_Failure, CPLE_AppDefined, "waitForJobToFinish failed.");
     580           0 :             return false;
     581             :         }
     582             : 
     583             :         {
     584           0 :             int type = json_object_get_type(result);
     585           0 :             if (type == json_type_object)
     586             :             {
     587             :                 json_object *poStatus =
     588           0 :                     CPL_json_object_object_get(result, "status");
     589           0 :                 const char *status = json_object_get_string(poStatus);
     590           0 :                 if (status != nullptr)
     591             :                 {
     592           0 :                     if (std::string(status) == "SUCCESS")
     593             :                     {
     594           0 :                         return true;
     595             :                     }
     596           0 :                     else if (std::string(status) == "FAILURE")
     597             :                     {
     598           0 :                         CPLError(CE_Failure, CPLE_AppDefined, "Job failed : %s",
     599             :                                  json_object_get_string(result));
     600           0 :                         return false;
     601             :                     }
     602             :                 }
     603             :             }
     604             :         }
     605           0 :         CPLSleep(1.0);  // Sleep 1 sec.
     606             :     }
     607           0 :     return false;
     608             : }
     609             : 
     610           0 : bool OGRAmigoCloudDataSource::TruncateDataset(const CPLString &tableName)
     611             : {
     612           0 :     std::stringstream changeset;
     613           0 :     changeset << "[{\"type\":\"DML\",\"entity\":\"" << tableName << "\",";
     614           0 :     changeset << "\"parent\":null,\"action\":\"TRUNCATE\",\"data\":null}]";
     615           0 :     SubmitChangeset(changeset.str());
     616           0 :     return true;
     617             : }
     618             : 
     619           0 : void OGRAmigoCloudDataSource::SubmitChangeset(const CPLString &json)
     620             : {
     621           0 :     std::stringstream url;
     622           0 :     url << std::string(GetAPIURL())
     623           0 :         << "/users/0/projects/" + std::string(GetProjectId()) +
     624           0 :                "/submit_changeset";
     625           0 :     std::stringstream changeset;
     626           0 :     changeset << "{\"changeset\":\"" << OGRAMIGOCLOUDJsonEncode(json) << "\"}";
     627           0 :     json_object *poObj = RunPOST(url.str().c_str(), changeset.str().c_str());
     628           0 :     if (poObj != nullptr)
     629           0 :         json_object_put(poObj);
     630           0 : }
     631             : 
     632             : /************************************************************************/
     633             : /*                               RunDELETE()                               */
     634             : /************************************************************************/
     635             : 
     636           0 : bool OGRAmigoCloudDataSource::RunDELETE(const char *pszURL)
     637             : {
     638           0 :     CPLString osURL(pszURL);
     639             : 
     640             :     /* -------------------------------------------------------------------- */
     641             :     /*      Provide the API Key                                             */
     642             :     /* -------------------------------------------------------------------- */
     643           0 :     if (!osAPIKey.empty())
     644             :     {
     645           0 :         if (osURL.find("?") == std::string::npos)
     646           0 :             osURL += "?token=";
     647             :         else
     648           0 :             osURL += "&token=";
     649           0 :         osURL += osAPIKey;
     650             :     }
     651             : 
     652           0 :     char **papszOptions = nullptr;
     653           0 :     CPLString osPOSTFIELDS("CUSTOMREQUEST=DELETE");
     654           0 :     papszOptions = CSLAddString(papszOptions, osPOSTFIELDS);
     655           0 :     papszOptions = CSLAddString(papszOptions, GetUserAgentOption().c_str());
     656             : 
     657           0 :     CPLHTTPResult *psResult = CPLHTTPFetch(osURL.c_str(), papszOptions);
     658           0 :     CSLDestroy(papszOptions);
     659           0 :     if (psResult == nullptr)
     660           0 :         return false;
     661             : 
     662           0 :     if (psResult->pszContentType &&
     663           0 :         strncmp(psResult->pszContentType, "text/html", 9) == 0)
     664             :     {
     665           0 :         CPLDebug("AMIGOCLOUD", "RunDELETE HTML Response:%s",
     666             :                  psResult->pabyData);
     667           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     668             :                  "HTML error page returned by server:%s", psResult->pabyData);
     669           0 :         CPLHTTPDestroyResult(psResult);
     670           0 :         return false;
     671             :     }
     672           0 :     if (psResult->pszErrBuf != nullptr && psResult->pabyData != nullptr)
     673             :     {
     674           0 :         CPLError(CE_Failure, CPLE_AppDefined, "DELETE Response: %s",
     675             :                  psResult->pabyData);
     676             :     }
     677           0 :     else if (psResult->nStatus != 0)
     678             :     {
     679           0 :         CPLDebug("AMIGOCLOUD", "DELETE Error Status:%d", psResult->nStatus);
     680             :     }
     681           0 :     CPLHTTPDestroyResult(psResult);
     682             : 
     683           0 :     return true;
     684             : }
     685             : 
     686             : /************************************************************************/
     687             : /*                               RunGET()                               */
     688             : /************************************************************************/
     689             : 
     690           0 : json_object *OGRAmigoCloudDataSource::RunGET(const char *pszURL)
     691             : {
     692           0 :     CPLString osURL(pszURL);
     693             : 
     694             :     /* -------------------------------------------------------------------- */
     695             :     /*      Provide the API Key                                             */
     696             :     /* -------------------------------------------------------------------- */
     697           0 :     if (!osAPIKey.empty())
     698             :     {
     699           0 :         if (osURL.find("?") == std::string::npos)
     700           0 :             osURL += "?token=";
     701             :         else
     702           0 :             osURL += "&token=";
     703           0 :         osURL += osAPIKey;
     704             :     }
     705           0 :     char **papszOptions = nullptr;
     706           0 :     papszOptions = CSLAddString(papszOptions, GetUserAgentOption().c_str());
     707             : 
     708           0 :     CPLHTTPResult *psResult = CPLHTTPFetch(osURL.c_str(), papszOptions);
     709           0 :     CSLDestroy(papszOptions);
     710           0 :     if (psResult == nullptr)
     711             :     {
     712           0 :         return nullptr;
     713             :     }
     714             : 
     715           0 :     if (psResult->pszContentType &&
     716           0 :         strncmp(psResult->pszContentType, "text/html", 9) == 0)
     717             :     {
     718           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     719             :                  "HTML error page returned by server:%s", psResult->pabyData);
     720           0 :         CPLHTTPDestroyResult(psResult);
     721           0 :         return nullptr;
     722             :     }
     723           0 :     if (psResult->pszErrBuf != nullptr && psResult->pabyData != nullptr)
     724             :     {
     725           0 :         CPLError(CE_Failure, CPLE_AppDefined, "GET Response: %s",
     726             :                  psResult->pabyData);
     727             :     }
     728           0 :     else if (psResult->nStatus != 0)
     729             :     {
     730           0 :         CPLDebug("AMIGOCLOUD", "RunGET Error Status:%d", psResult->nStatus);
     731             :     }
     732             : 
     733           0 :     if (psResult->pabyData == nullptr)
     734             :     {
     735           0 :         CPLHTTPDestroyResult(psResult);
     736           0 :         return nullptr;
     737             :     }
     738             : 
     739           0 :     CPLDebug("AMIGOCLOUD", "RunGET Response:%s", psResult->pabyData);
     740             : 
     741           0 :     json_object *poObj = nullptr;
     742           0 :     const char *pszText = reinterpret_cast<const char *>(psResult->pabyData);
     743           0 :     if (!OGRJSonParse(pszText, &poObj, true))
     744             :     {
     745           0 :         CPLHTTPDestroyResult(psResult);
     746           0 :         return nullptr;
     747             :     }
     748             : 
     749           0 :     CPLHTTPDestroyResult(psResult);
     750             : 
     751           0 :     if (poObj != nullptr)
     752             :     {
     753           0 :         if (json_object_get_type(poObj) == json_type_object)
     754             :         {
     755           0 :             json_object *poError = CPL_json_object_object_get(poObj, "error");
     756           0 :             if (poError != nullptr &&
     757           0 :                 json_object_get_type(poError) == json_type_array &&
     758           0 :                 json_object_array_length(poError) > 0)
     759             :             {
     760           0 :                 poError = json_object_array_get_idx(poError, 0);
     761           0 :                 if (poError != nullptr &&
     762           0 :                     json_object_get_type(poError) == json_type_string)
     763             :                 {
     764           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     765             :                              "Error returned by server : %s",
     766             :                              json_object_get_string(poError));
     767           0 :                     json_object_put(poObj);
     768           0 :                     return nullptr;
     769             :                 }
     770             :             }
     771             :         }
     772             :         else
     773             :         {
     774           0 :             json_object_put(poObj);
     775           0 :             return nullptr;
     776             :         }
     777             :     }
     778             : 
     779           0 :     return poObj;
     780             : }
     781             : 
     782             : /************************************************************************/
     783             : /*                               RunSQL()                               */
     784             : /************************************************************************/
     785             : 
     786           0 : json_object *OGRAmigoCloudDataSource::RunSQL(const char *pszUnescapedSQL)
     787             : {
     788           0 :     CPLString osSQL;
     789           0 :     std::string pszAPIURL = GetAPIURL();
     790           0 :     osSQL = pszAPIURL + "/users/0/projects/" + CPLString(pszProjectId) + "/sql";
     791           0 :     std::string sql = pszUnescapedSQL;
     792           0 :     if (sql.find("DELETE") != std::string::npos ||
     793           0 :         sql.find("delete") != std::string::npos ||
     794           0 :         sql.find("INSERT") != std::string::npos ||
     795           0 :         sql.find("insert") != std::string::npos ||
     796           0 :         sql.find("UPDATE") != std::string::npos ||
     797           0 :         sql.find("update") != std::string::npos)
     798             :     {
     799           0 :         std::stringstream query;
     800           0 :         query << "{\"query\": \"" << OGRAMIGOCLOUDJsonEncode(pszUnescapedSQL)
     801           0 :               << "\"}";
     802           0 :         return RunPOST(osSQL.c_str(), query.str().c_str());
     803             :     }
     804             :     else
     805             :     {
     806           0 :         osSQL += "?query=";
     807           0 :         char *pszEscaped = CPLEscapeString(pszUnescapedSQL, -1, CPLES_URL);
     808           0 :         osSQL += pszEscaped;
     809           0 :         CPLFree(pszEscaped);
     810           0 :         return RunGET(osSQL.c_str());
     811             :     }
     812             : }
     813             : 
     814             : /************************************************************************/
     815             : /*                        OGRAMIGOCLOUDGetSingleRow()                      */
     816             : /************************************************************************/
     817             : 
     818           0 : json_object *OGRAMIGOCLOUDGetSingleRow(json_object *poObj)
     819             : {
     820           0 :     if (poObj == nullptr)
     821             :     {
     822           0 :         return nullptr;
     823             :     }
     824             : 
     825           0 :     json_object *poRows = CPL_json_object_object_get(poObj, "data");
     826           0 :     if (poRows == nullptr || json_object_get_type(poRows) != json_type_array ||
     827           0 :         json_object_array_length(poRows) != 1)
     828             :     {
     829           0 :         return nullptr;
     830             :     }
     831             : 
     832           0 :     json_object *poRowObj = json_object_array_get_idx(poRows, 0);
     833           0 :     if (poRowObj == nullptr ||
     834           0 :         json_object_get_type(poRowObj) != json_type_object)
     835             :     {
     836           0 :         return nullptr;
     837             :     }
     838             : 
     839           0 :     return poRowObj;
     840             : }
     841             : 
     842             : /************************************************************************/
     843             : /*                             ExecuteSQL()                             */
     844             : /************************************************************************/
     845             : 
     846           0 : OGRLayer *OGRAmigoCloudDataSource::ExecuteSQL(const char *pszSQLCommand,
     847             :                                               OGRGeometry *poSpatialFilter,
     848             :                                               const char *pszDialect)
     849             : 
     850             : {
     851             :     /* -------------------------------------------------------------------- */
     852             :     /*      Use generic implementation for recognized dialects              */
     853             :     /* -------------------------------------------------------------------- */
     854           0 :     if (IsGenericSQLDialect(pszDialect))
     855           0 :         return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter,
     856           0 :                                        pszDialect);
     857             : 
     858           0 :     return ExecuteSQLInternal(pszSQLCommand, poSpatialFilter, pszDialect, true);
     859             : }
     860             : 
     861           0 : OGRLayer *OGRAmigoCloudDataSource::ExecuteSQLInternal(
     862             :     const char *pszSQLCommand, OGRGeometry *poSpatialFilter, const char *,
     863             :     bool bRunDeferredActions)
     864             : 
     865             : {
     866           0 :     if (bRunDeferredActions)
     867             :     {
     868           0 :         for (int iLayer = 0; iLayer < nLayers; iLayer++)
     869             :         {
     870           0 :             papoLayers[iLayer]->RunDeferredCreationIfNecessary();
     871           0 :             papoLayers[iLayer]->FlushDeferredInsert();
     872             :         }
     873             :     }
     874             : 
     875             :     /* Skip leading spaces */
     876           0 :     while (*pszSQLCommand == ' ')
     877           0 :         pszSQLCommand++;
     878             : 
     879           0 :     if (!EQUALN(pszSQLCommand, "SELECT", strlen("SELECT")) &&
     880           0 :         !EQUALN(pszSQLCommand, "EXPLAIN", strlen("EXPLAIN")) &&
     881           0 :         !EQUALN(pszSQLCommand, "WITH", strlen("WITH")))
     882             :     {
     883           0 :         RunSQL(pszSQLCommand);
     884           0 :         return nullptr;
     885             :     }
     886             : 
     887             :     OGRAmigoCloudResultLayer *poLayer =
     888           0 :         new OGRAmigoCloudResultLayer(this, pszSQLCommand);
     889             : 
     890           0 :     if (poSpatialFilter != nullptr)
     891           0 :         poLayer->SetSpatialFilter(poSpatialFilter);
     892             : 
     893           0 :     if (!poLayer->IsOK())
     894             :     {
     895           0 :         delete poLayer;
     896           0 :         return nullptr;
     897             :     }
     898             : 
     899           0 :     return poLayer;
     900             : }
     901             : 
     902             : /************************************************************************/
     903             : /*                          ReleaseResultSet()                          */
     904             : /************************************************************************/
     905             : 
     906           0 : void OGRAmigoCloudDataSource::ReleaseResultSet(OGRLayer *poLayer)
     907             : 
     908             : {
     909           0 :     delete poLayer;
     910           0 : }

Generated by: LCOV version 1.14