Line data Source code
1 : /****************************************************************************** 2 : * File : PostGISRasterDriver.cpp 3 : * Project: PostGIS Raster driver 4 : * Purpose: Implements PostGIS Raster driver class methods 5 : * Author: Jorge Arevalo, jorge.arevalo@deimos-space.com 6 : * 7 : * 8 : ****************************************************************************** 9 : * Copyright (c) 2010, Jorge Arevalo, jorge.arevalo@deimos-space.com 10 : * Copyright (c) 2013, Even Rouault 11 : * 12 : * SPDX-License-Identifier: MIT 13 : ******************************************************************************/ 14 : 15 : #include "gdal_frmts.h" 16 : #include "gdalplugindriverproxy.h" 17 : 18 : #include <stdexcept> 19 : 20 : #include "postgisrasterdrivercore.h" 21 : 22 : #include "gdalsubdatasetinfo.h" 23 : 24 : /************************************************************************/ 25 : /* PostGISRasterDriverIdentify() */ 26 : /************************************************************************/ 27 : 28 57922 : int PostGISRasterDriverIdentify(GDALOpenInfo *poOpenInfo) 29 : 30 : { 31 57922 : if (poOpenInfo->pszFilename == nullptr || poOpenInfo->fpL != nullptr || 32 54464 : !STARTS_WITH_CI(poOpenInfo->pszFilename, "PG:")) 33 : { 34 57918 : return FALSE; 35 : } 36 : 37 : // Will avoid a OGR PostgreSQL connection string to be recognized as a 38 : // PostgisRaster one and later fail (#6034) 39 4 : if (strstr(poOpenInfo->pszFilename, " schemas=") || 40 4 : strstr(poOpenInfo->pszFilename, " SCHEMAS=")) 41 : { 42 0 : return FALSE; 43 : } 44 : 45 4 : return TRUE; 46 : } 47 : 48 : /************************************************************** 49 : * \brief Replace the single quotes by " in the input string 50 : * 51 : * Needed before tokenize function 52 : *************************************************************/ 53 2 : static char *ReplaceSingleQuotes(const char *pszInput, int nLength) 54 : { 55 : int i; 56 2 : char *pszOutput = nullptr; 57 : 58 2 : if (nLength == -1) 59 2 : nLength = static_cast<int>(strlen(pszInput)); 60 : 61 2 : pszOutput = static_cast<char *>(CPLCalloc(nLength + 1, sizeof(char))); 62 : 63 106 : for (i = 0; i < nLength; i++) 64 : { 65 104 : if (pszInput[i] == '\'') 66 12 : pszOutput[i] = '"'; 67 : else 68 92 : pszOutput[i] = pszInput[i]; 69 : } 70 : 71 2 : return pszOutput; 72 : } 73 : 74 : /*********************************************************************** 75 : * \brief Split connection string into user, password, host, database... 76 : * 77 : * The parameters separated by spaces are return as a list of strings. 78 : * The function accepts all the PostgreSQL recognized parameter keywords. 79 : * 80 : * The returned list must be freed with CSLDestroy when no longer needed 81 : **********************************************************************/ 82 2 : char **PostGISRasterParseConnectionString(const char *pszConnectionString) 83 : { 84 : 85 : /* Escape string following SQL scheme */ 86 : char *pszEscapedConnectionString = 87 2 : ReplaceSingleQuotes(pszConnectionString, -1); 88 : 89 : /* Avoid PG: part */ 90 2 : char *pszStartPos = strstr(pszEscapedConnectionString, ":") + 1; 91 : 92 : /* Tokenize */ 93 : char **papszParams = 94 2 : CSLTokenizeString2(pszStartPos, " ", CSLT_HONOURSTRINGS); 95 : 96 : /* Free */ 97 2 : CPLFree(pszEscapedConnectionString); 98 : 99 2 : return papszParams; 100 : } 101 : 102 : /************************************************************************/ 103 : /* PostGISRasterDriverGetSubdatasetInfo() */ 104 : /************************************************************************/ 105 : 106 : struct PostGISRasterDriverSubdatasetInfo final : public GDALSubdatasetInfo 107 : { 108 : public: 109 0 : explicit PostGISRasterDriverSubdatasetInfo(const std::string &fileName) 110 0 : : GDALSubdatasetInfo(fileName) 111 : { 112 0 : } 113 : 114 : // GDALSubdatasetInfo interface 115 : private: 116 : void parseFileName() override; 117 : }; 118 : 119 0 : void PostGISRasterDriverSubdatasetInfo::parseFileName() 120 : { 121 0 : if (!STARTS_WITH_CI(m_fileName.c_str(), "PG:")) 122 : { 123 0 : return; 124 : } 125 : 126 0 : char **papszParams = PostGISRasterParseConnectionString(m_fileName.c_str()); 127 : 128 0 : const int nTableIdx = CSLFindName(papszParams, "table"); 129 0 : if (nTableIdx != -1) 130 : { 131 0 : size_t nTableStart = m_fileName.find("table="); 132 0 : bool bHasQuotes{false}; 133 : try 134 : { 135 0 : bHasQuotes = m_fileName.at(nTableStart + 6) == '\''; 136 : } 137 0 : catch (const std::out_of_range &) 138 : { 139 : // ignore error 140 : } 141 : 142 0 : m_subdatasetComponent = papszParams[nTableIdx]; 143 : 144 0 : if (bHasQuotes) 145 : { 146 0 : m_subdatasetComponent.insert(6, "'"); 147 0 : m_subdatasetComponent.push_back('\''); 148 : } 149 : 150 0 : m_driverPrefixComponent = "PG"; 151 : 152 0 : size_t nPathLength = m_subdatasetComponent.length(); 153 0 : if (nTableStart != 0) 154 : { 155 0 : nPathLength++; 156 0 : nTableStart--; 157 : } 158 : 159 0 : m_pathComponent = m_fileName; 160 0 : m_pathComponent.erase(nTableStart, nPathLength); 161 0 : m_pathComponent.erase(0, 3); 162 : } 163 : 164 0 : CSLDestroy(papszParams); 165 : } 166 : 167 : static GDALSubdatasetInfo * 168 2687 : PostGISRasterDriverGetSubdatasetInfo(const char *pszFileName) 169 : { 170 2687 : if (STARTS_WITH_CI(pszFileName, "PG:")) 171 : { 172 : std::unique_ptr<GDALSubdatasetInfo> info = 173 0 : std::make_unique<PostGISRasterDriverSubdatasetInfo>(pszFileName); 174 0 : if (!info->GetSubdatasetComponent().empty() && 175 0 : !info->GetPathComponent().empty()) 176 : { 177 0 : return info.release(); 178 : } 179 : } 180 2687 : return nullptr; 181 : } 182 : 183 : /************************************************************************/ 184 : /* PostGISRasterDriverSetCommonMetadata() */ 185 : /************************************************************************/ 186 : 187 1760 : void PostGISRasterDriverSetCommonMetadata(GDALDriver *poDriver) 188 : { 189 1760 : poDriver->SetDescription(DRIVER_NAME); 190 1760 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); 191 1760 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "PostGIS Raster driver"); 192 1760 : poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES"); 193 1760 : poDriver->SetMetadataItem(GDAL_DMD_CONNECTION_PREFIX, "PG:"); 194 : 195 1760 : poDriver->pfnIdentify = PostGISRasterDriverIdentify; 196 1760 : poDriver->pfnGetSubdatasetInfoFunc = PostGISRasterDriverGetSubdatasetInfo; 197 : 198 1760 : poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES"); 199 1760 : poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES"); 200 1760 : } 201 : 202 : /************************************************************************/ 203 : /* DeclareDeferredPostGISRasterPlugin() */ 204 : /************************************************************************/ 205 : 206 : #ifdef PLUGIN_FILENAME 207 2033 : void DeclareDeferredPostGISRasterPlugin() 208 : { 209 2033 : if (GDALGetDriverByName(DRIVER_NAME) != nullptr) 210 : { 211 283 : return; 212 : } 213 1750 : auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME); 214 : #ifdef PLUGIN_INSTALLATION_MESSAGE 215 : poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE, 216 : PLUGIN_INSTALLATION_MESSAGE); 217 : #endif 218 1750 : PostGISRasterDriverSetCommonMetadata(poDriver); 219 1750 : GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver); 220 : } 221 : #endif