LCOV - code coverage report
Current view: directory - frmts/postgisraster - postgisrasterdataset.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 811 112 13.8 %
Date: 2012-12-26 Functions: 22 7 31.8 %

       1                 : /*************************************************************************
       2                 :  * File :    postgisrasterdataset.cpp
       3                 :  * Project:  PostGIS Raster driver
       4                 :  * Purpose:  GDAL Dataset implementation for PostGIS Raster driver
       5                 :  * Author:   Jorge Arevalo, jorge.arevalo@deimos-space.com
       6                 :  *
       7                 :  * Last changes:
       8                 :  * $Id:$
       9                 :  *
      10                 :  ************************************************************************
      11                 :  * Copyright (c) 2009 - 2011, Jorge Arevalo, jorge.arevalo@deimos-space.com
      12                 :  *
      13                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      14                 :  * copy of this software and associated documentation files (the
      15                 :  * "Software"), to deal in the Software without restriction, including
      16                 :  * without limitation the rights to use, copy, modify, merge, publish,
      17                 :  * distribute, sublicense, and/or sell copies of the Software, and to
      18                 :  * permit persons to whom the Software is furnished to do so, subject to
      19                 :  * the following conditions:
      20                 :  *
      21                 :  * The above copyright notice and this permission notice shall be included
      22                 :  * in all copies or substantial portions of the Software.
      23                 :  *
      24                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      25                 :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      26                 :  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
      27                 :  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
      28                 :  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
      29                 :  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      30                 :  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      31                 :  ************************************************************************/
      32                 : 
      33                 : #include "postgisraster.h"
      34                 : #include <stdlib.h>
      35                 : #include "gdal.h"
      36                 : #include "cpl_conv.h"
      37                 : #include "cpl_string.h"
      38                 : #include "gdal_priv.h"
      39                 : #include <math.h>
      40                 : #include "cpl_error.h"
      41                 : #include "ogr_core.h"
      42                 : 
      43                 : #ifdef OGR_ENABLED
      44                 : #include "ogr_api.h"
      45                 : #endif
      46                 : 
      47                 : #include "ogr_geometry.h"
      48                 : #include "gdal_vrt.h"
      49                 : #include "vrtdataset.h"
      50                 : #include "memdataset.h"
      51                 : 
      52                 : #ifdef _WIN32
      53                 : #define rint(x) floor((x) + 0.5)
      54                 : #endif
      55                 : 
      56                 : 
      57                 : CPL_C_START
      58                 : void GDALRegister_PostGISRaster(void);
      59                 : CPL_C_END
      60                 : 
      61                 : 
      62                 : 
      63                 : /************************
      64                 :  * \brief Constructor
      65                 :  ************************/
      66               0 : PostGISRasterDataset::PostGISRasterDataset(ResolutionStrategy inResolutionStrategy) {
      67               0 :     pszOriginalConnectionString = NULL;
      68               0 :     papszSubdatasets = NULL;
      69               0 :     nSrid = -1;
      70               0 :     poConn = NULL;
      71               0 :     bRegisteredInRasterColumns = false;
      72               0 :     pszSchema = NULL;
      73               0 :     pszTable = NULL;
      74               0 :     pszColumn = NULL;
      75               0 :     pszWhere = NULL;
      76               0 :     pszProjection = NULL;
      77               0 :     resolutionStrategy = inResolutionStrategy;
      78               0 :     nTiles = 0;
      79               0 :     nMode = NO_MODE;
      80               0 :     poDriver = NULL;
      81               0 :     adfGeoTransform[GEOTRSFRM_TOPLEFT_X] = 0.0;
      82               0 :     adfGeoTransform[GEOTRSFRM_WE_RES] = 0.0;
      83               0 :     adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] = 0.0;
      84               0 :     adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = 0.0;
      85               0 :     adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] = 0.0;
      86               0 :     adfGeoTransform[GEOTRSFRM_NS_RES] = 0.0;
      87               0 :     bBlocksCached = false;
      88               0 :     bRegularBlocking = true;// do not change! (need to be 'true' for SetRasterProperties)
      89               0 :     bAllTilesSnapToSameGrid = false;
      90                 : 
      91                 :     /**
      92                 :      * TODO: Parametrize bAllTilesSnapToSameGrid. It controls if all the
      93                 :      * raster rows, in ONE_RASTER_PER_TABLE mode, must be checked to test if
      94                 :      * they snap to the same grid and have the same srid. It can be the user
      95                 :      * decission, if he/she's sure all the rows pass the test and want more
      96                 :      * speed.
      97                 :      **/
      98                 : 
      99               0 : }
     100                 : 
     101                 : /************************
     102                 :  * \brief Constructor
     103                 :  ************************/
     104               0 : PostGISRasterDataset::~PostGISRasterDataset() {
     105                 : 
     106               0 :     if (pszSchema)
     107               0 :         CPLFree(pszSchema);
     108               0 :     if (pszTable)
     109               0 :         CPLFree(pszTable);
     110               0 :     if (pszColumn)
     111               0 :         CPLFree(pszColumn);
     112               0 :     if (pszWhere)
     113               0 :         CPLFree(pszWhere);
     114               0 :     if (pszProjection)
     115               0 :         CPLFree(pszProjection);
     116               0 :     if (pszOriginalConnectionString)
     117               0 :         CPLFree(pszOriginalConnectionString);
     118                 : 
     119               0 :     if (papszSubdatasets)
     120               0 :         CSLDestroy(papszSubdatasets);
     121               0 : }
     122                 : 
     123                 : /**************************************************************
     124                 :  * \brief Replace the single quotes by " in the input string
     125                 :  *
     126                 :  * Needed before tokenize function
     127                 :  *************************************************************/
     128                 : static
     129               1 : char * ReplaceSingleQuotes(const char * pszInput, int nLength) {
     130                 :     int i;
     131               1 :     char* pszOutput = NULL;
     132                 : 
     133               1 :     if (nLength == -1)
     134               1 :         nLength = strlen(pszInput);
     135                 : 
     136               1 :     pszOutput = (char*) CPLCalloc(nLength + 1, sizeof (char));
     137                 : 
     138              93 :     for (i = 0; i < nLength; i++) {
     139              92 :         if (pszInput[i] == '\'')
     140              12 :             pszOutput[i] = '"';
     141                 :         else
     142              80 :             pszOutput[i] = pszInput[i];
     143                 : 
     144                 :     }
     145                 : 
     146               1 :     return pszOutput;
     147                 : }
     148                 : 
     149                 : /**************************************************************
     150                 :  * \brief Replace the quotes by single quotes in the input string
     151                 :  *
     152                 :  * Needed in the 'where' part of the input string
     153                 :  *************************************************************/
     154                 : static
     155               0 : char * ReplaceQuotes(const char * pszInput, int nLength) {
     156                 :     int i;
     157               0 :     char * pszOutput = NULL;
     158                 : 
     159               0 :     if (nLength == -1)
     160               0 :         nLength = strlen(pszInput);
     161                 : 
     162               0 :     pszOutput = (char*) CPLCalloc(nLength + 1, sizeof (char));
     163                 : 
     164               0 :     for (i = 0; i < nLength; i++) {
     165               0 :         if (pszInput[i] == '"')
     166               0 :             pszOutput[i] = '\'';
     167                 :         else
     168               0 :             pszOutput[i] = pszInput[i];
     169                 :     }
     170                 : 
     171               0 :     return pszOutput;
     172                 : }
     173                 : 
     174                 : /*****************************************************************************
     175                 :  * \brief Split connection string into user, password, host, database...
     176                 :  *
     177                 :  * The parameters separated by spaces are return as a list of strings. The
     178                 :  * function accepts all the PostgreSQL recognized parameter key words.
     179                 :  *
     180                 :  * The returned list must be freed with CSLDestroy when no longer needed
     181                 :  *
     182                 :  *****************************************************************************/
     183                 : static
     184               1 : char** ParseConnectionString(const char * pszConnectionString) {
     185               1 :     char * pszEscapedConnectionString = NULL;
     186                 : 
     187                 :     /* Escape string following SQL scheme */
     188               1 :     pszEscapedConnectionString = ReplaceSingleQuotes(pszConnectionString, -1);
     189                 : 
     190                 :     /* Avoid PG: part */
     191               1 :     char* pszStartPos = (char*) strstr(pszEscapedConnectionString, ":") + 1;
     192                 : 
     193                 :     /* Tokenize */
     194                 :     char** papszParams = CSLTokenizeString2(pszStartPos, " ",
     195               1 :             CSLT_HONOURSTRINGS);
     196                 : 
     197                 :     /* Free */
     198               1 :     CPLFree(pszEscapedConnectionString);
     199                 : 
     200               1 :     return papszParams;
     201                 : 
     202                 : }
     203                 : 
     204                 : /**************************************************************************
     205                 :  * \brief Look for raster tables in database and store them as subdatasets
     206                 :  *
     207                 :  * If no table is provided in connection string, the driver looks for the
     208                 :  * existent raster tables in the schema given as argument. This argument,
     209                 :  * however, is optional. If a NULL value is provided, the driver looks for
     210                 :  * all raster tables in all schemas of the user-provided database.
     211                 :  *
     212                 :  * NOTE: Permissions are managed by libpq. The driver only returns an error
     213                 :  * if an error is returned when trying to access to tables not allowed to
     214                 :  * the current user.
     215                 :  **************************************************************************/
     216               0 : GBool PostGISRasterDataset::BrowseDatabase(const char* pszCurrentSchema,
     217                 :         char* pszValidConnectionString) {
     218                 :     /* Be careful! These 3 vars override the class ones! */
     219               0 :     char* pszSchema = NULL;
     220               0 :     char* pszTable = NULL;
     221               0 :     char* pszColumn = NULL;
     222               0 :     int i = 0;
     223               0 :     int nTuples = 0;
     224               0 :     PGresult * poResult = NULL;
     225               0 :     CPLString osCommand;
     226                 : 
     227                 : 
     228                 :     /*************************************************************
     229                 :      * Fetch all the raster tables and store them as subdatasets
     230                 :      *************************************************************/
     231               0 :     if (pszCurrentSchema == NULL) {
     232                 :         osCommand.Printf("select pg_namespace.nspname as schema, pg_class.relname as \
     233                 :                     table, pg_attribute.attname as column from pg_class, \
     234                 :                     pg_namespace,pg_attribute, pg_type where \
     235                 :                     pg_class.relnamespace = pg_namespace.oid and pg_class.oid = \
     236                 :                     pg_attribute.attrelid and pg_attribute.atttypid = pg_type.oid \
     237               0 :                     and pg_type.typname = 'raster'");
     238                 : 
     239               0 :         poResult = PQexec(poConn, osCommand.c_str());
     240               0 :         if (
     241                 :                 poResult == NULL ||
     242                 :                 PQresultStatus(poResult) != PGRES_TUPLES_OK ||
     243                 :                 PQntuples(poResult) <= 0
     244                 :                 ) {
     245                 :             CPLError(CE_Failure, CPLE_AppDefined,
     246               0 :                     "Error browsing database for PostGIS Raster tables: %s", PQerrorMessage(poConn));
     247               0 :             if (poResult != NULL)
     248               0 :                 PQclear(poResult);
     249                 : 
     250               0 :             return false;
     251                 :         }
     252                 : 
     253                 : 
     254               0 :         nTuples = PQntuples(poResult);
     255               0 :         for (i = 0; i < nTuples; i++) {
     256               0 :             pszSchema = PQgetvalue(poResult, i, 0);
     257               0 :             pszTable = PQgetvalue(poResult, i, 1);
     258               0 :             pszColumn = PQgetvalue(poResult, i, 2);
     259                 : 
     260                 :             papszSubdatasets = CSLSetNameValue(papszSubdatasets,
     261                 :                     CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)),
     262                 :                     CPLSPrintf("PG:%s schema=%s table=%s column=%s",
     263               0 :                     pszValidConnectionString, pszSchema, pszTable, pszColumn));
     264                 : 
     265                 :             papszSubdatasets = CSLSetNameValue(papszSubdatasets,
     266                 :                     CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)),
     267               0 :                     CPLSPrintf("PostGIS Raster table at %s.%s (%s)", pszSchema, pszTable, pszColumn));
     268                 :         }
     269                 : 
     270               0 :         PQclear(poResult);
     271                 : 
     272                 :     }
     273                 :         /**********************************************************************
     274                 :          * Fetch all the schema's raster tables and store them as subdatasets
     275                 :          **********************************************************************/
     276                 :     else {
     277                 :         osCommand.Printf("select pg_class.relname as table, pg_attribute.attname \
     278                 :              as column from pg_class, pg_namespace,pg_attribute, pg_type where \
     279                 :              pg_class.relnamespace = pg_namespace.oid and pg_class.oid = \
     280                 :              pg_attribute.attrelid and pg_attribute.atttypid = pg_type.oid \
     281                 :              and pg_type.typname = 'raster' and pg_namespace.nspname = '%s'",
     282               0 :                 pszCurrentSchema);
     283                 : 
     284               0 :         poResult = PQexec(poConn, osCommand.c_str());
     285               0 :         if (
     286                 :                 poResult == NULL ||
     287                 :                 PQresultStatus(poResult) != PGRES_TUPLES_OK ||
     288                 :                 PQntuples(poResult) <= 0
     289                 :                 ) {
     290                 :             CPLError(CE_Failure, CPLE_AppDefined,
     291               0 :                     "Error browsing database for PostGIS Raster tables: %s", PQerrorMessage(poConn));
     292               0 :             if (poResult != NULL)
     293               0 :                 PQclear(poResult);
     294                 : 
     295               0 :             return false;
     296                 :         }
     297                 : 
     298                 : 
     299               0 :         nTuples = PQntuples(poResult);
     300               0 :         for (i = 0; i < nTuples; i++) {
     301               0 :             pszTable = PQgetvalue(poResult, i, 0);
     302               0 :             pszColumn = PQgetvalue(poResult, i, 1);
     303                 : 
     304                 :             papszSubdatasets = CSLSetNameValue(papszSubdatasets,
     305                 :                     CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)),
     306                 :                     CPLSPrintf("PG:%s schema=%s table=%s column=%s",
     307               0 :                     pszValidConnectionString, pszCurrentSchema, pszTable, pszColumn));
     308                 : 
     309                 :             papszSubdatasets = CSLSetNameValue(papszSubdatasets,
     310                 :                     CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)),
     311                 :                     CPLSPrintf("PostGIS Raster table at %s.%s (%s)", pszCurrentSchema,
     312               0 :                     pszTable, pszColumn));
     313                 :         }
     314                 : 
     315               0 :         PQclear(poResult);
     316                 :     }
     317                 : 
     318               0 :     return true;
     319                 : }
     320                 : 
     321                 : 
     322                 : 
     323                 : 
     324                 : /*************************************************************************
     325                 :  * \brief Set the general raster properties.
     326                 :  *
     327                 :  * We must distinguish between tiled and untiled raster coverages. In
     328                 :  * PostGIS Raster, there's no real difference between 'tile' and 'raster'.
     329                 :  * There's only 'raster objects'. Each record of a raster table is a
     330                 :  * raster object, and has its own georeference information, whether if
     331                 :  * the record is a tile of a bigger raster coverage or is a complete
     332                 :  * raster. So, <b>there's no a way of knowing if the rows of a raster
     333                 :  * table are related or not</b>. It's user's responsibility. The only
     334                 :  * thing driver can do is to suppose all the rows of a table are from
     335                 :  * the same raster coverage if the user has queried for one table, without
     336                 :  * specifying a where clause.
     337                 :  *
     338                 :  * The user is responsible to ensure that the raster layer meets the minimum
     339                 :  * topological requirements for analysis. The ideal case is when all the raster
     340                 :  * tiles of a continuous layer are the same size, snap to the same grid and do
     341                 :  * not overlap. 
     342                 :  * 
     343                 :  * So, when we query for a raster table, we have 2 different cases:
     344                 :  *  - The result is only one row OR there are several rows BUT the working
     345                 :  *    mode is ONE_RASTER_PER_TABLE. The row(s) returned form one raster coverage.
     346                 :  *    We get the whole coverage extent (except rotated rasters), and its georefence, 
     347                 :  *    if possible
     348                 :  *  - The result are several rows of a table AND the working mode is
     349                 :  *    ONE_RASTER_PER_ROW. We assume each row is a different raster object,
     350                 :  *    and is reported as a subdataset. 
     351                 :  **************************************************************************/
     352               0 : GBool PostGISRasterDataset::SetRasterProperties
     353                 :     (const char * pszValidConnectionString)
     354                 : {
     355               0 :     PGresult* poResult = NULL;
     356               0 :     CPLString osCommand;
     357               0 :     int i = 0;
     358               0 :     int nTuples = 0;
     359               0 :     int nRasterID = 0;
     360               0 :     char* pszIdColumn = NULL;
     361                 :     double tileUpperLeftX;
     362                 :     double tileUpperLeftY;
     363                 :     double tileSkewX;
     364                 :     double tileSkewY;
     365                 :     double tilePixelSizeX;
     366                 :     double tilePixelSizeY;
     367               0 :     int nTileWidth = 0;
     368               0 :     int nTileHeight = 0;
     369               0 :     int nPreviousTileWidth = 0;
     370               0 :     int nPreviousTileHeight = 0;
     371               0 :     int nBlockXSize = 0, nBlockYSize = 0;
     372                 :     char szTmp[20];
     373                 : 
     374                 :     /* Incorporated variables from old SetRasterBand method */
     375               0 :     GBool bSignedByte = false;
     376               0 :     int nBitDepth = 8;
     377               0 :     char* pszDataType = NULL;
     378               0 :     int iBand = 0;
     379               0 :     double dfNodata = 0.0;
     380               0 :     GDALDataType hDataType = GDT_Byte;
     381               0 :     GBool bIsOffline = false;
     382               0 :     GBool bHasNoDataValue = false;
     383                 : 
     384                 :     /**************************************************************************
     385                 :      * Get the extent and the maximum number of bands of the requested raster
     386                 :      * TODO: The extent of rotated rasters could be a problem. We'll need a
     387                 :      * ST_RotatedExtent function in PostGIS. Without that function, we shouldn't
     388                 :      * allow rotated rasters
     389                 :      **************************************************************************/
     390                 :     // NOTE: can't use 'srid' alias in the GROUP BY. It doesn't work 
     391                 :     // with PostgreSQL 9.1
     392               0 :     if (pszWhere == NULL) {
     393                 :         osCommand.Printf(
     394                 :             "select srid, nbband, st_xmin(geom) as xmin, st_xmax(geom) as xmax, "
     395                 :             "st_ymin(geom) as ymin, st_ymax(geom) as ymax from (select st_srid(%s) srid, "
     396                 :             "st_extent(%s::geometry) geom, max(ST_NumBands(rast)) nbband from %s.%s "
     397                 :             "group by st_srid(%s)) foo", pszColumn, pszColumn, pszSchema, 
     398               0 :             pszTable, pszColumn);
     399                 :     }
     400                 : 
     401                 :     else {
     402                 :         osCommand.Printf(
     403                 :             "select srid, nbband, st_xmin(geom) as xmin, st_xmax(geom) as xmax, "
     404                 :             "st_ymin(geom) as ymin, st_ymax(geom) as ymax from (select st_srid(%s) srid, "
     405                 :             "st_extent(%s::geometry) geom, max(ST_NumBands(rast)) nbband from %s.%s "
     406                 :             "where %s group by st_srid(%s)) foo", pszColumn, pszColumn, pszSchema, 
     407               0 :             pszTable, pszWhere, pszColumn);
     408                 :     }
     409                 : 
     410                 :     CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
     411               0 :         "First query: %s", osCommand.c_str());
     412                 : 
     413                 : 
     414               0 :     poResult = PQexec(poConn, osCommand.c_str());
     415                 :     
     416                 :     // Query execution error
     417               0 :     if(poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK || 
     418                 :         PQntuples(poResult) < 0) {
     419                 : 
     420                 :         CPLError(CE_Failure, CPLE_AppDefined, "Error browsing database for "
     421               0 :             "PostGIS Raster properties");
     422                 : 
     423                 :         CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
     424               0 :             "%s", PQerrorMessage(poConn));
     425                 : 
     426               0 :         if (poResult != NULL)
     427               0 :             PQclear(poResult);
     428                 : 
     429               0 :         return false;                   
     430                 :     }
     431                 : 
     432                 :     // No tuples
     433               0 :     else if (PQntuples(poResult) == 0) {
     434                 :         CPLError(CE_Failure, CPLE_AppDefined, "Error, no data found in the table that "
     435                 :             "matches your constraints. Maybe there are no rows in the table, or maybe "
     436               0 :             "you provide a 'where' option with too much restrictions");
     437                 : 
     438               0 :         PQclear(poResult);
     439                 : 
     440               0 :         return false;
     441                 :     }
     442                 : 
     443                 :     
     444                 :     /**
     445                 :      * TODO: We could provide an extra parameter, to transform all the tiles to
     446                 :      * the same SRID
     447                 :      **/
     448               0 :     else if (PQntuples(poResult) > 1) {
     449                 :         CPLError(CE_Failure, CPLE_AppDefined,
     450                 :             "Error, the table %s.%s contains tiles with different srid. This feature "
     451                 :             "is not yet supported by the PostGIS Raster driver. Please, specify a table "
     452                 :             "that contains only tiles with the same srid or provide a 'where' constraint "
     453               0 :             "to select just the tiles with the same value for srid", pszSchema, pszTable);
     454                 : 
     455               0 :         PQclear(poResult);
     456               0 :         return false;
     457                 :     }
     458                 : 
     459                 :     // Get some information we will probably need further
     460               0 :     nSrid = atoi(PQgetvalue(poResult, 0, 0));
     461               0 :     nBands = atoi(PQgetvalue(poResult, 0, 1));
     462               0 :     xmin = atof(PQgetvalue(poResult, 0, 2));
     463               0 :     xmax = atof(PQgetvalue(poResult, 0, 3));
     464               0 :     ymin = atof(PQgetvalue(poResult, 0, 4));
     465               0 :     ymax = atof(PQgetvalue(poResult, 0, 5));
     466                 :     
     467               0 :     PQclear(poResult);
     468                 :     
     469                 :     /*****************************************************************************
     470                 :      * Now, we're going to count the number of raster tiles we will have to deal 
     471                 :      * with.To save one database server round, we get the pixel size and rotation
     472                 :      *
     473                 :      * TODO: Improve the optimization, based on MAX_TILES
     474                 :      *****************************************************************************/
     475               0 :     memset(szTmp, 0, sizeof(szTmp));
     476                 :     if (MAX_TILES > 0) {
     477               0 :         sprintf(szTmp, "limit %d", MAX_TILES);
     478                 :     }
     479                 : 
     480                 : 
     481               0 :     if (pszWhere == NULL) {
     482                 :         osCommand.Printf(
     483                 :             "select st_scalex(%s), st_scaley(%s), st_skewx(%s), "
     484                 :             "st_skewy(%s), st_width(%s), st_height(%s) from %s.%s %s", pszColumn, pszColumn, 
     485               0 :             pszColumn, pszColumn, pszColumn, pszColumn, pszSchema, pszTable, szTmp);
     486                 :     }
     487                 : 
     488                 :     else {
     489                 :         osCommand.Printf(
     490                 :             "select st_scalex(%s), st_scaley(%s), st_skewx(%s), "
     491                 :             "st_skewy(%s), st_width(%s), st_height(%s) from %s.%s where %s %s", pszColumn, 
     492                 :             pszColumn, pszColumn, pszColumn, pszColumn, pszColumn, pszSchema, pszTable, 
     493               0 :             pszWhere, szTmp);
     494                 :     }
     495                 : 
     496                 :     CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
     497               0 :         "Query: %s", osCommand.c_str());
     498                 : 
     499               0 :     poResult = PQexec(poConn, osCommand.c_str());
     500               0 :     if (poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK ||
     501                 :         PQntuples(poResult) <= 0) {
     502                 : 
     503                 : 
     504               0 :         CPLError(CE_Failure, CPLE_AppDefined, "Error retrieving raster metadata");
     505                 : 
     506                 :         CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
     507               0 :             "%s", PQerrorMessage(poConn));
     508                 : 
     509               0 :         if (poResult != NULL)
     510               0 :             PQclear(poResult);
     511                 : 
     512               0 :         return false;
     513                 :     }
     514                 : 
     515                 :     // Now we now the number of tiles that form our dataset
     516               0 :     nTiles = PQntuples(poResult);
     517                 : 
     518                 : 
     519                 :     /*****************************************************************************
     520                 :      * We are going to create a whole dataset as a mosaic with all the tiles
     521                 :      ****************************************************************************/
     522               0 :     if (nTiles == 1 || nMode == ONE_RASTER_PER_TABLE) {
     523                 :             
     524               0 :         adfGeoTransform[GEOTRSFRM_TOPLEFT_X] = xmin;    
     525                 : 
     526                 :         /**
     527                 :          * Negative tilePixelSizeY means that the coords origin is in top left corner.
     528                 :          *
     529                 :          * This is not the common situation. Most image files store data from top to 
     530                 :          * bottom, while the projected coordinate systems utilize traditional Cartesian 
     531                 :          * coordinates with the origin in the conventional lower-left corner (bottom to
     532                 :          * top). For that reason, this parameter is normally negative.
     533                 :          *
     534                 :          **/ 
     535                 : 
     536                 :         // Calculate geotransform fields
     537               0 :         for(i = 0; i < nTiles; i++) {
     538               0 :             tileSkewX = atof(PQgetvalue(poResult, i, 2));
     539               0 :             tileSkewY = atof(PQgetvalue(poResult, i, 3));
     540                 :     
     541                 :             // Rotated rasters are not allowed, so far
     542                 :             // TODO: allow them
     543               0 :             if (!CPLIsEqual(tileSkewX, 0.0) || !CPLIsEqual(tileSkewY, 0.0)) {
     544                 :                 CPLError(CE_Failure, CPLE_AppDefined, "GDAL PostGIS Raster driver can not work with "
     545               0 :                 "rotated rasters yet.");
     546                 : 
     547               0 :                 PQclear(poResult);
     548                 : 
     549               0 :                 return false;
     550                 :             }
     551               0 :             adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] = tileSkewX;
     552               0 :             adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] = tileSkewY;
     553                 :     
     554               0 :             tilePixelSizeX = atof(PQgetvalue(poResult, i, 0));
     555               0 :             tilePixelSizeY = atof(PQgetvalue(poResult, i, 1));
     556                 : 
     557               0 :             nTileWidth = atoi(PQgetvalue(poResult, i, 4));
     558               0 :             nTileHeight = atoi(PQgetvalue(poResult, i, 5));
     559                 : 
     560               0 :             if (bRegularBlocking) {
     561               0 :                 if (nPreviousTileWidth != 0 && nPreviousTileWidth != nTileWidth)
     562               0 :                     bRegularBlocking = false;
     563               0 :                 else if (nPreviousTileHeight != 0 && nPreviousTileHeight != nTileHeight)
     564               0 :                     bRegularBlocking = false;
     565                 :                 else {
     566               0 :                     nPreviousTileWidth = nTileWidth;
     567               0 :                     nPreviousTileHeight = nTileHeight;
     568                 :                 }
     569                 :             }
     570                 :             
     571                 :             // Calculate pixel size
     572               0 :             if (resolutionStrategy == AVERAGE_RESOLUTION) {
     573               0 :                 adfGeoTransform[GEOTRSFRM_WE_RES] += tilePixelSizeX;        
     574               0 :                 adfGeoTransform[GEOTRSFRM_NS_RES] += tilePixelSizeY;        
     575                 :             }
     576                 : 
     577               0 :             else if (resolutionStrategy == HIGHEST_RESOLUTION)  {
     578               0 :                 adfGeoTransform[GEOTRSFRM_WE_RES] = MIN(adfGeoTransform[GEOTRSFRM_WE_RES], 
     579               0 :                                                         atof(PQgetvalue(poResult, i, 0)));
     580                 : 
     581                 :                 /* Yes : as ns_res is negative, the highest resolution is the max value */
     582               0 :                 if (tilePixelSizeY < 0.0)
     583               0 :                     adfGeoTransform[GEOTRSFRM_NS_RES] = MAX(adfGeoTransform[GEOTRSFRM_NS_RES], 
     584               0 :                                                         tilePixelSizeY);
     585                 :                 else
     586               0 :                     adfGeoTransform[GEOTRSFRM_NS_RES] = MIN(adfGeoTransform[GEOTRSFRM_NS_RES], 
     587               0 :                                                         tilePixelSizeY);                                        
     588                 :             }
     589                 : 
     590               0 :             else if (resolutionStrategy == LOWEST_RESOLUTION) {
     591               0 :                 adfGeoTransform[GEOTRSFRM_WE_RES] = MAX(adfGeoTransform[GEOTRSFRM_WE_RES], 
     592               0 :                                                         atof(PQgetvalue(poResult, i, 0)));
     593                 : 
     594                 :                 /* Yes : as ns_res is negative, the lowest resolution is the min value */
     595               0 :                 if (tilePixelSizeY < 0.0)
     596               0 :                     adfGeoTransform[GEOTRSFRM_NS_RES] = MIN(adfGeoTransform[GEOTRSFRM_NS_RES], 
     597               0 :                                                         tilePixelSizeY);
     598                 :                 else    
     599               0 :                     adfGeoTransform[GEOTRSFRM_NS_RES] = MAX(adfGeoTransform[GEOTRSFRM_NS_RES], 
     600               0 :                                                         tilePixelSizeY);
     601                 :             }
     602                 : 
     603                 :             // USER_RESOLUTION
     604                 :             else {
     605                 :                 // It should be provided by the user. Nothing to do here...
     606                 :                 // TODO: Allow the user to provide the resolution (see gdalbuildvrt)
     607                 :             }
     608                 : 
     609                 :         } // end for    
     610                 : 
     611               0 :         if (adfGeoTransform[GEOTRSFRM_NS_RES] >= 0.0)
     612               0 :             adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = ymin;
     613                 :         else
     614               0 :             adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = ymax;
     615                 : 
     616               0 :         if (resolutionStrategy == AVERAGE_RESOLUTION) {
     617               0 :             adfGeoTransform[GEOTRSFRM_WE_RES] /= nTiles;
     618               0 :             adfGeoTransform[GEOTRSFRM_NS_RES] /= nTiles;
     619                 :         }
     620                 :         
     621               0 :         nRasterXSize = (int) fabs(rint((xmax - xmin) / adfGeoTransform[GEOTRSFRM_WE_RES]));
     622               0 :         nRasterYSize = (int) fabs(rint((ymax - ymin) / adfGeoTransform[GEOTRSFRM_NS_RES]));
     623                 :         
     624                 : 
     625               0 :         if (nRasterXSize <= 0 || nRasterYSize <= 0) {
     626                 :             CPLError(CE_Failure, CPLE_AppDefined, 
     627                 :                 "Computed PostGIS Raster dimension is invalid. You've probably specified "
     628               0 :                 "unappropriate resolution.");
     629               0 :             return CE_Failure;
     630                 :         }
     631                 : 
     632                 :         /**
     633                 :          * Regular blocking: get the last values for tile width and height as block
     634                 :          * size
     635                 :          **/
     636               0 :         if (bRegularBlocking) {
     637               0 :             nBlockXSize = nTileWidth;
     638               0 :             nBlockYSize = nTileHeight;
     639                 :         }
     640                 :         
     641               0 :         PQclear(poResult);
     642                 : 
     643                 :     
     644                 :         CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
     645                 :             "adfGeoTransform = {%f, %f, %f, %f, %f,%f}", adfGeoTransform[GEOTRSFRM_TOPLEFT_X],
     646                 :             adfGeoTransform[GEOTRSFRM_WE_RES], adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1], 
     647                 :             adfGeoTransform[GEOTRSFRM_TOPLEFT_Y],adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2], 
     648               0 :             adfGeoTransform[GEOTRSFRM_NS_RES]);
     649                 : 
     650                 :         CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
     651               0 :             "Raster size = (%d, %d)", nRasterXSize, nRasterYSize);
     652                 : 
     653                 : 
     654                 :         /****************************************************************************
     655                 :          * Dataset parameters are set. Now, let's add the raster bands
     656                 :          ***************************************************************************/
     657                 :         
     658                 :         /* Create query to fetch metadata from db */
     659               0 :         if (pszWhere == NULL) {
     660                 :             osCommand.Printf("select st_bandpixeltype(rast, band), "
     661                 :                 "st_bandnodatavalue(rast, band) is null, "
     662                 :                 "st_bandnodatavalue(rast, band) from (select %s, "
     663                 :                 "generate_series(1, st_numbands(%s)) band from (select "
     664                 :                 "rast from %s.%s limit 1) bar) foo",
     665               0 :                 pszColumn, pszColumn, pszSchema, pszTable);
     666                 :         } 
     667                 : 
     668                 :         else {
     669                 :             osCommand.Printf("select st_bandpixeltype(rast, band), "
     670                 :                 "st_bandnodatavalue(rast, band) is null, "
     671                 :                 "st_bandnodatavalue(rast, band) from (select %s, "
     672                 :                 "generate_series(1, st_numbands(%s)) band from (select "
     673                 :                 "rast from %s.%s where %s limit 1) bar) foo",
     674               0 :                 pszColumn, pszColumn, pszSchema, pszTable, pszWhere);
     675                 :         }
     676                 : 
     677                 :         CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
     678               0 :             "Query: %s", osCommand.c_str());
     679                 :         
     680               0 :         poResult = PQexec(poConn, osCommand.c_str());
     681               0 :         nTuples = PQntuples(poResult);
     682                 : 
     683                 :         /* Error getting info from database */
     684               0 :         if (poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK ||
     685                 :             nTuples <= 0) {
     686                 :             
     687                 :             CPLError(CE_Failure, CPLE_AppDefined, "Error getting band metadata "
     688               0 :                 "while creating raster bands");
     689                 :                 
     690                 :             CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): %s", 
     691               0 :                     PQerrorMessage(poConn));
     692                 :             
     693               0 :             if (poResult)
     694               0 :                 PQclear(poResult);
     695                 : 
     696               0 :             return false;
     697                 :         }
     698                 : 
     699                 :         /* Create each PostGISRasterRasterBand using the band metadata */
     700               0 :         for (iBand = 0; iBand < nTuples; iBand++) {
     701                 :             /**
     702                 :              * If we have more than one record here is because there are several
     703                 :              * rows, belonging to the same raster coverage, with different band
     704                 :              * metadata values. An error must be raised.
     705                 :              *
     706                 :              * TODO: Is there any way to fix this problem?
     707                 :              *
     708                 :              * TODO: Even when the difference between metadata values are only a
     709                 :              * few decimal numbers (for example: 3.0000000 and 3.0000001) they're
     710                 :              * different tuples. And in that case, they must be the same
     711                 :              **/
     712                 :             /*
     713                 :             if (nTuples > 1) {
     714                 :                 CPLError(CE_Failure, CPLE_AppDefined, "Error, the \
     715                 :                     ONE_RASTER_PER_TABLE mode can't be applied if the raster \
     716                 :                     rows don't have the same metadata for band %d",
     717                 :                     iBand + 1);
     718                 :                 PQclear(poResult);
     719                 :                 return false;
     720                 :             }
     721                 :             */
     722                 : 
     723                 : 
     724                 :             /* Get metadata and create raster band objects */
     725               0 :             pszDataType = CPLStrdup(PQgetvalue(poResult, iBand, 0));
     726               0 :             bHasNoDataValue = EQUALN(PQgetvalue(poResult, iBand, 1), "f", sizeof(char));
     727               0 :             dfNodata = atof(PQgetvalue(poResult, iBand, 2));
     728                 :             /** 
     729                 :              * Offline rasters are not yet supported. When offline rasters are
     730                 :              * supported, they will also requires a fast 'getter', other than 
     731                 :              * the ST_BandMetaData accessor.
     732                 :              **/
     733                 :             
     734                 :             /* bIsOffline = EQUALN(PQgetvalue(poResult, iBand, 3), "t", sizeof (char));        */
     735                 : 
     736               0 :             if (EQUALN(pszDataType, "1BB", 3 * sizeof (char))) {
     737               0 :                 hDataType = GDT_Byte;
     738               0 :                 nBitDepth = 1;
     739               0 :             } else if (EQUALN(pszDataType, "2BUI", 4 * sizeof (char))) {
     740               0 :                 hDataType = GDT_Byte;
     741               0 :                 nBitDepth = 2;
     742               0 :             } else if (EQUALN(pszDataType, "4BUI", 4 * sizeof (char))) {
     743               0 :                 hDataType = GDT_Byte;
     744               0 :                 nBitDepth = 4;
     745               0 :             } else if (EQUALN(pszDataType, "8BUI", 4 * sizeof (char))) {
     746               0 :                 hDataType = GDT_Byte;
     747               0 :                 nBitDepth = 8;
     748               0 :             } else if (EQUALN(pszDataType, "8BSI", 4 * sizeof (char))) {
     749               0 :                 hDataType = GDT_Byte;
     750                 :                 /**
     751                 :                  * To indicate the unsigned byte values between 128 and 255
     752                 :                  * should be interpreted as being values between -128 and -1 for
     753                 :                  * applications that recognise the SIGNEDBYTE type.
     754                 :                  **/
     755               0 :                 bSignedByte = true;
     756               0 :                 nBitDepth = 8;
     757               0 :             } else if (EQUALN(pszDataType, "16BSI", 5 * sizeof (char))) {
     758               0 :                 hDataType = GDT_Int16;
     759               0 :                 nBitDepth = 16;
     760               0 :             } else if (EQUALN(pszDataType, "16BUI", 5 * sizeof (char))) {
     761               0 :                 hDataType = GDT_UInt16;
     762               0 :                 nBitDepth = 16;
     763               0 :             } else if (EQUALN(pszDataType, "32BSI", 5 * sizeof (char))) {
     764               0 :                 hDataType = GDT_Int32;
     765               0 :                 nBitDepth = 32;
     766               0 :             } else if (EQUALN(pszDataType, "32BUI", 5 * sizeof (char))) {
     767               0 :                 hDataType = GDT_UInt32;
     768               0 :                 nBitDepth = 32;
     769               0 :             } else if (EQUALN(pszDataType, "32BF", 4 * sizeof (char))) {
     770               0 :                 hDataType = GDT_Float32;
     771               0 :                 nBitDepth = 32;
     772               0 :             } else if (EQUALN(pszDataType, "64BF", 4 * sizeof (char))) {
     773               0 :                 hDataType = GDT_Float64;
     774               0 :                 nBitDepth = 64;
     775                 :             } else {
     776               0 :                 hDataType = GDT_Byte;
     777               0 :                 nBitDepth = 8;
     778                 :             }
     779                 : 
     780                 :             /* Create raster band object */
     781                 :             SetBand(iBand + 1, new PostGISRasterRasterBand(this, iBand + 1, hDataType,
     782                 :                 bHasNoDataValue, dfNodata, bSignedByte, nBitDepth, 0, nBlockXSize, 
     783               0 :                 nBlockYSize, bIsOffline));
     784                 : 
     785               0 :             CPLFree(pszDataType);
     786                 :         }
     787                 : 
     788               0 :         PQclear(poResult);
     789                 :     }
     790                 : 
     791                 : 
     792                 :     /*****************************************************************************
     793                 :      * One raster per row: collect subdatasets 
     794                 :      ****************************************************************************/
     795                 :     else {
     796                 :         /* Determine the primary key/unique column on the table */
     797                 :         osCommand.Printf("select d.attname from pg_catalog.pg_constraint as a "
     798                 :             "join pg_catalog.pg_indexes as b on a.conname = b.indexname "
     799                 :             "join pg_catalog.pg_class as c on c.relname = b.tablename "
     800                 :             "join pg_catalog.pg_attribute as d on c.relfilenode = d.attrelid "
     801                 :             "where b.schemaname = '%s' and b.tablename = '%s' and "
     802               0 :             "d.attnum = a.conkey[1] and a.contype in ('p', 'u')", pszSchema, pszTable);
     803                 : 
     804                 :         CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
     805               0 :             "Query: %s", osCommand.c_str());
     806                 : 
     807                 : 
     808               0 :         poResult = PQexec(poConn, osCommand.c_str());
     809               0 :         if (poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK ||
     810                 :             PQntuples(poResult) <= 0 ) {
     811                 : 
     812               0 :             PQclear(poResult);
     813                 : 
     814                 :             /*
     815                 :               Maybe there is no primary key or unique constraint;
     816                 :               a sequence will also suffice; get the first one
     817                 :             */
     818                 : 
     819                 :             osCommand.Printf("select cols.column_name from information_schema."
     820                 :                 "columns as cols join information_schema.sequences as seqs on cols."
     821                 :                 "column_default like '%%'||seqs.sequence_name||'%%' where cols."
     822               0 :                 "table_schema = '%s' and cols.table_name = '%s'", pszSchema, pszTable);
     823                 : 
     824                 :             CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
     825               0 :                 "Query: %s", osCommand.c_str());
     826                 : 
     827               0 :             poResult = PQexec(poConn, osCommand.c_str());
     828                 : 
     829               0 :             if (poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK ||
     830                 :                 PQntuples(poResult) <= 0) {
     831                 : 
     832                 :                 CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
     833                 :                     "Could not find a primary key or unique column on the specified table; "
     834               0 :                     "using UpperLeftX and UpperLeftY.");
     835                 : 
     836                 : 
     837                 :                 /*
     838                 :                     If no primary key or unique column found, 
     839                 :                     fall back to raster upperleft x&y
     840                 :                 */
     841                 :             }
     842                 : 
     843                 :             else {
     844               0 :                 pszIdColumn = CPLStrdup(PQgetvalue(poResult, 0, 0));
     845                 :             }
     846                 : 
     847                 :         }
     848                 :         
     849                 :         // Ok, get the primary key        
     850                 :         else {
     851               0 :             pszIdColumn = CPLStrdup(PQgetvalue(poResult, 0, 0));
     852                 :         }
     853                 : 
     854               0 :         PQclear(poResult);
     855                 : 
     856                 :         /* No primary key on this table. Rely on UpperLeftX and UpperLeftY */
     857               0 :         if (pszIdColumn == NULL) {
     858               0 :             if (pszWhere == NULL) {
     859                 :                 osCommand.Printf("select ST_UpperLeftX(%s), ST_UpperLeftY(%s) from %s.%s", pszColumn, 
     860               0 :                     pszColumn, pszSchema, pszTable);
     861                 :             }
     862                 : 
     863                 :             else {
     864                 :                 osCommand.Printf("select ST_UpperLeftX(%s), ST_UpperLeftY(%s) from %s.%s where %s", 
     865               0 :                     pszColumn, pszColumn, pszSchema, pszTable, pszWhere);
     866                 :             }
     867                 :                     
     868                 : 
     869                 :             CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
     870               0 :                 "Query: %s", osCommand.c_str());
     871                 : 
     872               0 :             poResult = PQexec(poConn, osCommand.c_str());
     873               0 :             if (poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK ||
     874                 :                 PQntuples(poResult) <= 0) {
     875                 : 
     876                 :                     CPLError(CE_Failure, CPLE_AppDefined, "Error retrieving raster tile metadata "
     877               0 :                         "while creating raster subdatasets");
     878                 : 
     879                 :                     CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
     880               0 :                         "%s", PQerrorMessage(poConn));
     881                 : 
     882               0 :                     if (poResult != NULL)
     883               0 :                         PQclear(poResult);
     884                 : 
     885                 :                     // pszIdColumn is already null
     886               0 :                     return false;
     887                 :             }
     888                 : 
     889               0 :             nTuples = PQntuples(poResult);
     890                 : 
     891                 :             /* Now create the subdatasets */
     892               0 :             for (i = 0; i < nTuples; i++) {
     893               0 :                 tileUpperLeftX = atof(PQgetvalue(poResult, i, 0)); //upperleft x
     894               0 :                 tileUpperLeftY = atof(PQgetvalue(poResult, i, 1)); //upperleft y
     895                 : 
     896                 :                 papszSubdatasets = CSLSetNameValue(papszSubdatasets, 
     897                 :                     CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)),
     898                 :                     CPLSPrintf("PG:%s schema=%s table=%s column=%s "
     899                 :                         "where='ST_UpperLeftX(%s) = %f AND ST_UpperLeftY(%s) = %f'",
     900                 :                         pszValidConnectionString, pszSchema, pszTable, pszColumn, 
     901               0 :                         pszColumn, tileUpperLeftX, pszColumn, tileUpperLeftY));
     902                 : 
     903                 : 
     904                 :                 papszSubdatasets = CSLSetNameValue(papszSubdatasets,
     905                 :                     CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)),
     906                 :                     CPLSPrintf("PostGIS Raster at %s.%s (%s), UpperLeft = %f, %f", pszSchema,
     907               0 :                         pszTable, pszColumn, tileUpperLeftX, tileUpperLeftY));
     908                 : 
     909                 :             }
     910                 :     
     911               0 :             PQclear(poResult);
     912                 :         }
     913                 : 
     914                 :         /* There is a primary key */
     915                 :         else {
     916               0 :             if (pszWhere == NULL) {
     917               0 :                 osCommand.Printf("select %s from %s.%s", pszIdColumn, pszSchema, pszTable);
     918                 :             }
     919                 : 
     920                 :             else {
     921                 :                 osCommand.Printf("select %s from %s.%s where %s", pszIdColumn, 
     922               0 :                     pszSchema, pszTable, pszWhere);
     923                 :             }
     924                 :             
     925                 :             CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
     926               0 :                 "Query: %s", osCommand.c_str());
     927                 :             
     928               0 :             poResult = PQexec(poConn, osCommand.c_str());
     929               0 :             if (poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK ||
     930                 :                 PQntuples(poResult) <= 0) {
     931                 :                 
     932                 :                 CPLError(CE_Failure, CPLE_AppDefined, "Error retrieving raster row metadata "
     933               0 :                         "while creating raster subdatasets");
     934                 :                 
     935                 :                 CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): %s", 
     936               0 :                     PQerrorMessage(poConn));
     937                 :                 
     938                 :                 
     939               0 :                 if (poResult != NULL)
     940               0 :                     PQclear(poResult);
     941                 :                 
     942               0 :                 if (pszIdColumn != NULL)
     943               0 :                     CPLFree(pszIdColumn);
     944                 : 
     945               0 :                 return false;
     946                 :                 
     947                 :             }
     948                 :             
     949                 :             
     950               0 :             nTuples = PQntuples(poResult);
     951                 :             
     952                 :             /* Now, create the subdatasets */
     953               0 :             for (i = 0; i < nTuples; i++) {
     954                 :                 // this is the raster ID (or unique column)
     955               0 :                 nRasterID = atoi(PQgetvalue(poResult, i, 0));
     956                 :                 
     957                 :                 papszSubdatasets = CSLSetNameValue(papszSubdatasets,
     958                 :                     CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)),
     959                 :                     CPLSPrintf("PG:%s schema=%s table=%s column=%s where='%s = %d'",
     960                 :                         pszValidConnectionString, pszSchema, pszTable, pszColumn, 
     961               0 :                         pszIdColumn, nRasterID));
     962                 :                         
     963                 :                     
     964                 :                 papszSubdatasets = CSLSetNameValue(papszSubdatasets,
     965                 :                     CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)),
     966                 :                     CPLSPrintf("PostGIS Raster at %s.%s (%s), %s = %d", pszSchema,
     967               0 :                         pszTable, pszColumn, pszIdColumn, nRasterID));
     968                 :             }
     969                 :             
     970               0 :             PQclear(poResult);
     971                 :         }
     972                 :         
     973                 :         /**
     974                 :          * Not a single raster fetched. Not really needed. Just to keep code clean 
     975                 :          **/
     976               0 :         nRasterXSize = 0;
     977               0 :         nRasterYSize = 0;
     978               0 :         adfGeoTransform[GEOTRSFRM_TOPLEFT_X] = 0.0;
     979               0 :         adfGeoTransform[GEOTRSFRM_WE_RES] = 1.0;
     980               0 :         adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] = 0.0;
     981               0 :         adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = 0.0;
     982               0 :         adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] = 0.0;
     983               0 :         adfGeoTransform[GEOTRSFRM_NS_RES] = -1.0;
     984                 :         
     985               0 :         if (pszIdColumn != NULL)
     986               0 :             CPLFree(pszIdColumn);
     987                 :     }
     988                 :     
     989                 : 
     990               0 :     return true;
     991                 : }
     992                 : 
     993                 : /******************************************************************************
     994                 :  * \brief Get the connection information for a filename.
     995                 :  ******************************************************************************/
     996                 : static GBool
     997               1 : GetConnectionInfo(const char * pszFilename, 
     998                 :     char ** ppszConnectionString, char ** ppszSchema, char ** ppszTable, 
     999                 :     char ** ppszColumn, char ** ppszWhere, char ** ppszHost, 
    1000                 :     char ** ppszPort, char ** ppszUser, char ** ppszPassword, 
    1001                 :     int * nMode, GBool * bBrowseDatabase)
    1002                 : {
    1003               1 :     int nPos = -1, i;
    1004               1 :     char * pszTmp = NULL;
    1005               1 :     char **papszParams = ParseConnectionString(pszFilename);
    1006               1 :     if (papszParams == NULL) {
    1007               0 :         return false;
    1008                 :     }
    1009                 : 
    1010                 :     /**************************************************************************
    1011                 :      * Get mode:
    1012                 :      *  - 1. ONE_RASTER_PER_ROW: Each row is considered as a separate raster
    1013                 :      *  - 2. ONE_RASTER_PER_TABLE: All the table rows are considered as a whole
    1014                 :      *      raster coverage
    1015                 :      **************************************************************************/
    1016               1 :     nPos = CSLFindName(papszParams, "mode");
    1017               1 :     if (nPos != -1) {
    1018               0 :         *nMode = atoi(CPLParseNameValue(papszParams[nPos], NULL));
    1019                 : 
    1020               0 :         if (*nMode != ONE_RASTER_PER_ROW && *nMode != ONE_RASTER_PER_TABLE) {
    1021                 :             /* Unrecognized mode, using default one */
    1022                 :             /*
    1023                 :             CPLError(CE_Warning, CPLE_AppDefined, "Undefined working mode (%d)."
    1024                 :                     " Valid working modes are 1 (ONE_RASTER_PER_ROW) and 2"
    1025                 :                     " (ONE_RASTER_PER_TABLE). Using ONE_RASTER_PER_TABLE"
    1026                 :                     " by default", nMode);
    1027                 :              */
    1028               0 :             *nMode = ONE_RASTER_PER_ROW;
    1029                 :         }
    1030                 : 
    1031                 :         /* Remove the mode from connection string */
    1032               0 :         papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
    1033                 :     }
    1034                 :         /* Default mode */
    1035                 :     else
    1036               1 :         *nMode = ONE_RASTER_PER_ROW;
    1037                 : 
    1038                 :     /**
    1039                 :      * Case 1: There's no database name: Error, you need, at least,
    1040                 :      * specify a database name (NOTE: insensitive search)
    1041                 :      **/
    1042               1 :     nPos = CSLFindName(papszParams, "dbname");
    1043               1 :     if (nPos == -1) {
    1044                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1045               0 :                 "You must specify at least a db name");
    1046                 : 
    1047               0 :         CSLDestroy(papszParams);
    1048                 : 
    1049               0 :         return false;
    1050                 :     }
    1051                 : 
    1052                 :     /**
    1053                 :      * Case 2: There's database name, but no table name: activate a flag
    1054                 :      * for browsing the database, fetching all the schemas that contain
    1055                 :      * raster tables
    1056                 :      **/
    1057               1 :     nPos = CSLFindName(papszParams, "table");
    1058               1 :     if (nPos == -1) {
    1059               0 :         *bBrowseDatabase = true;
    1060                 : 
    1061                 :         /* Get schema name, if exist */
    1062               0 :         nPos = CSLFindName(papszParams, "schema");
    1063               0 :         if (nPos != -1) {
    1064               0 :             *ppszSchema = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
    1065                 :             /* Delete this pair from params array */
    1066               0 :             papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
    1067                 :         }
    1068                 : 
    1069                 :         /**
    1070                 :          * Remove the rest of the parameters, if exist (they mustn't be present
    1071                 :          * if we want a valid PQ connection string)
    1072                 :          **/
    1073               0 :         nPos = CSLFindName(papszParams, "column");
    1074               0 :         if (nPos != -1) {
    1075                 :             /* Delete this pair from params array */
    1076               0 :             papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
    1077                 :         }
    1078                 : 
    1079               0 :         nPos = CSLFindName(papszParams, "where");
    1080               0 :         if (nPos != -1) {
    1081                 :             /* Delete this pair from params array */
    1082               0 :             papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
    1083                 :         }
    1084                 :     } else {
    1085               1 :         *ppszTable = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
    1086                 :         /* Delete this pair from params array */
    1087               1 :         papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
    1088                 : 
    1089                 :         /**
    1090                 :          * Case 3: There's database and table name, but no column
    1091                 :          * name: Use a default column name and use the table to create the
    1092                 :          * dataset
    1093                 :          **/
    1094               1 :         nPos = CSLFindName(papszParams, "column");
    1095               1 :         if (nPos == -1) {
    1096               1 :             *ppszColumn = CPLStrdup(DEFAULT_COLUMN);
    1097                 :         }
    1098                 :         /**
    1099                 :          * Case 4: There's database, table and column name: Use the table to
    1100                 :          * create a dataset
    1101                 :          **/
    1102                 :         else {
    1103               0 :             *ppszColumn = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
    1104                 :             /* Delete this pair from params array */
    1105               0 :             papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
    1106                 :         }
    1107                 : 
    1108                 :         /* Get the rest of the parameters, if exist */
    1109               1 :         nPos = CSLFindName(papszParams, "schema");
    1110               1 :         if (nPos == -1) {
    1111               0 :             *ppszSchema = CPLStrdup(DEFAULT_SCHEMA);
    1112                 :         } else {
    1113               1 :             *ppszSchema = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
    1114                 :             /* Delete this pair from params array */
    1115               1 :             papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
    1116                 :         }
    1117                 : 
    1118               1 :         nPos = CSLFindName(papszParams, "where");
    1119               1 :         if (nPos != -1) {
    1120               0 :             *ppszWhere = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
    1121                 :             /* Delete this pair from params array */
    1122               0 :             papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
    1123                 :         }
    1124                 :     }
    1125                 : 
    1126                 :     /* Parse ppszWhere, if needed */
    1127               1 :     if (*ppszWhere) {
    1128               0 :         pszTmp = ReplaceQuotes(*ppszWhere, strlen(*ppszWhere));
    1129               0 :         CPLFree(*ppszWhere);
    1130               0 :         *ppszWhere = pszTmp;
    1131                 :     }
    1132                 : 
    1133                 :     /***************************************
    1134                 :      * Construct a valid connection string
    1135                 :      ***************************************/
    1136                 :     *ppszConnectionString = (char*) CPLCalloc(strlen(pszFilename),
    1137               1 :             sizeof (char));
    1138               5 :     for (i = 0; i < CSLCount(papszParams); i++) {
    1139               4 :         *ppszConnectionString = strncat(*ppszConnectionString, papszParams[i], strlen(papszParams[i]));
    1140               4 :         *ppszConnectionString = strncat(*ppszConnectionString, " ", strlen(" "));
    1141                 :     }
    1142                 : 
    1143               1 :     nPos = CSLFindName(papszParams, "host");
    1144               1 :     if (nPos != -1) {
    1145               1 :         *ppszHost = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
    1146                 :     }
    1147               0 :     else if (getenv("PGHOST") != NULL) {
    1148               0 :         *ppszHost = CPLStrdup(getenv("PGHOST"));
    1149                 :     }
    1150                 :     else {
    1151                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1152                 :                 "Host parameter must be provided, or PGHOST environment "
    1153               0 :                 "variable must be set. Please set the host and try again.");
    1154                 : 
    1155               0 :         CSLDestroy(papszParams);
    1156                 : 
    1157               0 :         return false;
    1158                 :     }
    1159                 : 
    1160               1 :     nPos = CSLFindName(papszParams, "port");
    1161               1 :     if (nPos != -1) {
    1162               0 :         *ppszPort = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
    1163                 :     }
    1164               1 :     else if (getenv("PGPORT") != NULL ) {
    1165               0 :         *ppszPort = CPLStrdup(getenv("PGPORT"));
    1166                 :     }
    1167                 :     else {
    1168                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1169                 :                 "Port parameter must be provided, or PGPORT environment "
    1170               1 :                 "variable must be set. Please set the port and try again.");
    1171                 : 
    1172               1 :         CSLDestroy(papszParams);
    1173                 : 
    1174               1 :         return false;
    1175                 :     }
    1176                 : 
    1177               0 :     nPos = CSLFindName(papszParams, "user");
    1178               0 :     if (nPos != -1) {
    1179               0 :         *ppszUser = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
    1180                 :     }
    1181               0 :     else if (getenv("PGUSER") != NULL ) {
    1182               0 :         *ppszUser = CPLStrdup(getenv("PGUSER"));
    1183                 :     }
    1184                 :     else {
    1185                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1186                 :                 "User parameter must be provided, or PGUSER environment "
    1187               0 :                 "variable must be set. Please set the user and try again.");
    1188                 : 
    1189               0 :         CSLDestroy(papszParams);
    1190                 : 
    1191               0 :         return false;
    1192                 :     }
    1193                 : 
    1194               0 :     nPos = CSLFindName(papszParams, "password");
    1195               0 :     if (nPos != -1) {
    1196               0 :         *ppszPassword = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
    1197                 :     }
    1198                 :     else {
    1199                 :         // if PGPASSWORD is not set, ppszPassword is set to an empty string.
    1200                 :         // this is okay, since there may be configurations in pg_hba.conf
    1201                 :         // that don't require any password to connect
    1202               0 :         *ppszPassword = CPLStrdup(getenv("PGPASSWORD"));
    1203                 :     }
    1204                 : 
    1205               0 :     CSLDestroy(papszParams);
    1206                 : 
    1207                 :     CPLDebug("PostGIS_Raster", "PostGISRasterDataset::GetConnectionInfo(): "
    1208                 :         "Mode: %d\nSchema: %s\nTable: %s\nColumn: %s\nWhere: %s\n"
    1209                 :         "Host: %s\nPort: %s\nUser: %s\nPassword: %s\nConnection String: %s", 
    1210                 :         *nMode, *ppszSchema, *ppszTable, *ppszColumn, 
    1211               0 :         *ppszWhere, *ppszHost, *ppszPort, *ppszUser, *ppszPassword, *ppszConnectionString);
    1212                 : 
    1213               0 :     return true;
    1214                 : }
    1215                 : 
    1216                 : /******************************************************************************
    1217                 :  * \brief Create a connection to a postgres database
    1218                 :  ******************************************************************************/
    1219                 : static PGconn *
    1220               1 : GetConnection(const char * pszFilename, char ** ppszConnectionString,
    1221                 :     char ** ppszSchema, char ** ppszTable, char ** ppszColumn, char ** ppszWhere, 
    1222                 :     int * nMode, GBool * bBrowseDatabase) 
    1223                 : {
    1224                 :     PostGISRasterDriver * poDriver;
    1225               1 :     PGconn * poConn = NULL;
    1226               1 :     char * pszHost = NULL;
    1227               1 :     char * pszPort = NULL;
    1228               1 :     char * pszUser = NULL;
    1229               1 :     char * pszPassword = NULL;
    1230                 : 
    1231               1 :     if (GetConnectionInfo(pszFilename, ppszConnectionString, ppszSchema, 
    1232                 :         ppszTable, ppszColumn, ppszWhere, &pszHost, &pszPort, &pszUser, 
    1233                 :         &pszPassword, nMode, bBrowseDatabase)) 
    1234                 :     {
    1235                 :         /********************************************************************
    1236                 :          * Open a new database connection
    1237                 :          ********************************************************************/
    1238               0 :         poDriver = (PostGISRasterDriver *)GDALGetDriverByName("PostGISRaster");
    1239                 :         poConn = poDriver->GetConnection(*ppszConnectionString,
    1240               0 :                 pszHost, pszPort, pszUser, pszPassword);
    1241                 : 
    1242               0 :         if (poConn == NULL) {
    1243                 :             CPLError(CE_Failure, CPLE_AppDefined,
    1244               0 :                     "Couldn't establish a database connection");
    1245                 :         }
    1246                 :     }
    1247                 : 
    1248               1 :     CPLFree(pszHost);
    1249               1 :     CPLFree(pszPort);
    1250               1 :     CPLFree(pszUser);
    1251               1 :     CPLFree(pszPassword);
    1252                 : 
    1253               1 :     return poConn;
    1254                 : }
    1255                 : 
    1256                 : 
    1257                 : /******************************************************************************
    1258                 :  * \brief Open a connection with PostgreSQL. The connection string will have
    1259                 :  * the PostgreSQL accepted format, plus the next key=value pairs:
    1260                 :  *  schema = <schema_name>
    1261                 :  *  table = <table_name>
    1262                 :  *  column = <column_name>
    1263                 :  *  where = <SQL where>
    1264                 :  *  mode = <working mode> (1 or 2)
    1265                 :  *
    1266                 :  * These pairs are used for selecting the right raster table.
    1267                 :  *****************************************************************************/
    1268           11736 : GDALDataset* PostGISRasterDataset::Open(GDALOpenInfo* poOpenInfo) {
    1269           11736 :     char* pszConnectionString = NULL;
    1270           11736 :     char* pszSchema = NULL;
    1271           11736 :     char* pszTable = NULL;
    1272           11736 :     char* pszColumn = NULL;
    1273           11736 :     char* pszWhere = NULL;
    1274           11736 :     int nMode = -1;
    1275           11736 :     PGconn * poConn = NULL;
    1276           11736 :     PostGISRasterDataset* poDS = NULL;
    1277           11736 :     GBool bBrowseDatabase = false;
    1278           11736 :     CPLString osCommand;
    1279                 :     char * pszTmp;
    1280                 : 
    1281                 :     /**************************
    1282                 :      * Check input parameter
    1283                 :      **************************/
    1284           11736 :     if (poOpenInfo->pszFilename == NULL ||
    1285                 :             poOpenInfo->fp != NULL ||
    1286                 :             !EQUALN(poOpenInfo->pszFilename, "PG:", 3))
    1287                 :     {
    1288                 :         /**
    1289                 :          * Drivers must quietly return NULL if the passed file is not of
    1290                 :          * their format. They should only produce an error if the file
    1291                 :          * does appear to be of their supported format, but for some
    1292                 :          * reason, unsupported or corrupt
    1293                 :          */
    1294           11735 :         return NULL;
    1295                 :     }
    1296                 : 
    1297               1 :     pszTmp = CPLStrdup(poOpenInfo->pszFilename);
    1298                 :     
    1299                 :     poConn = GetConnection((char *)poOpenInfo->pszFilename,
    1300                 :         &pszConnectionString, &pszSchema, &pszTable, &pszColumn, &pszWhere,
    1301               1 :         &nMode, &bBrowseDatabase);
    1302               1 :     if (poConn == NULL) {
    1303               1 :         CPLFree(pszConnectionString);
    1304               1 :         CPLFree(pszSchema);
    1305               1 :         CPLFree(pszTable);
    1306               1 :         CPLFree(pszColumn);
    1307               1 :         CPLFree(pszWhere);
    1308               1 :         return NULL;
    1309                 :     }
    1310                 :     
    1311                 : 
    1312                 :     /*************************************************************************
    1313                 :      * No table will be read. Only shows information about the existent raster
    1314                 :      * tables
    1315                 :      *************************************************************************/
    1316               0 :     if (bBrowseDatabase) {
    1317                 :         /**
    1318                 :          * Creates empty dataset object, only for subdatasets
    1319                 :          **/
    1320               0 :         poDS = new PostGISRasterDataset();
    1321               0 :         poDS->poConn = poConn;
    1322               0 :         poDS->eAccess = GA_ReadOnly;
    1323                 :         //poDS->poDriver = poDriver;
    1324               0 :         poDS->nMode = (pszSchema) ? BROWSE_SCHEMA : BROWSE_DATABASE;
    1325               0 :         poDS->nRasterXSize = 0;
    1326               0 :         poDS->nRasterYSize = 0;
    1327               0 :         poDS->adfGeoTransform[0] = 0.0;
    1328               0 :         poDS->adfGeoTransform[1] = 0.0;
    1329               0 :         poDS->adfGeoTransform[2] = 0.0;
    1330               0 :         poDS->adfGeoTransform[3] = 0.0;
    1331               0 :         poDS->adfGeoTransform[4] = 0.0;
    1332               0 :         poDS->adfGeoTransform[5] = 0.0;
    1333                 : 
    1334                 :         /**
    1335                 :          * Look for raster tables at database and
    1336                 :          * store them as subdatasets
    1337                 :          **/
    1338               0 :         if (!poDS->BrowseDatabase(pszSchema, pszConnectionString)) {
    1339               0 :             CPLFree(pszConnectionString);
    1340               0 :             delete poDS;
    1341                 : 
    1342               0 :             if (pszSchema)
    1343               0 :                 CPLFree(pszSchema);
    1344               0 :             if (pszTable)
    1345               0 :                 CPLFree(pszTable);
    1346               0 :             if (pszColumn)
    1347               0 :                 CPLFree(pszColumn);
    1348               0 :             if (pszWhere)
    1349               0 :                 CPLFree(pszWhere);
    1350                 : 
    1351               0 :             return NULL;
    1352                 :         }
    1353                 : 
    1354               0 :         if (pszSchema)
    1355               0 :             CPLFree(pszSchema);
    1356               0 :         if (pszTable)
    1357               0 :             CPLFree(pszTable);
    1358               0 :         if (pszColumn)
    1359               0 :             CPLFree(pszColumn);
    1360               0 :         if (pszWhere)
    1361               0 :             CPLFree(pszWhere);
    1362                 :     }
    1363                 :         /***********************************************************************
    1364                 :          * A table will be read: Fetch raster properties from db. Pay attention
    1365                 :          * to the block size: if the raster is blocked at database, the block
    1366                 :          * size can be fetched from each block size, if regular blocking table
    1367                 :          **********************************************************************/
    1368                 :     else {
    1369               0 :         poDS = new PostGISRasterDataset();
    1370               0 :         poDS->poConn = poConn;
    1371               0 :         poDS->eAccess = poOpenInfo->eAccess;
    1372               0 :         poDS->nMode = nMode;
    1373                 :         //poDS->poDriver = poDriver;
    1374                 : 
    1375               0 :         poDS->pszSchema = pszSchema;
    1376               0 :         poDS->pszTable = pszTable;
    1377               0 :         poDS->pszColumn = pszColumn;
    1378               0 :         poDS->pszWhere = pszWhere;
    1379                 : 
    1380                 :         /**
    1381                 :          * Fetch basic raster metadata from db
    1382                 :          **/
    1383                 : 
    1384                 :         CPLDebug("PostGIS_Raster", "Open:: connection string = %s",
    1385               0 :             pszConnectionString);
    1386                 :         
    1387               0 :         if (!poDS->SetRasterProperties(pszConnectionString)) {
    1388               0 :             CPLFree(pszConnectionString);
    1389               0 :             delete poDS;
    1390               0 :             return NULL;
    1391                 :         }
    1392                 : 
    1393               0 :         poDS->pszOriginalConnectionString = pszTmp;
    1394                 :         
    1395                 :         CPLDebug("PostGIS_Raster", "Open:: original connection string = %s",
    1396               0 :             poDS->pszOriginalConnectionString);
    1397                 : 
    1398                 :     }
    1399                 : 
    1400               0 :     CPLFree(pszConnectionString);
    1401               0 :     return poDS;
    1402                 : 
    1403                 : }
    1404                 : 
    1405                 : /*****************************************
    1406                 :  * \brief Get Metadata from raster
    1407                 :  * TODO: Add more options (the result of
    1408                 :  * calling ST_Metadata, for example)
    1409                 :  *****************************************/
    1410               0 : char** PostGISRasterDataset::GetMetadata(const char *pszDomain) {
    1411               0 :     if (pszDomain != NULL && EQUALN(pszDomain, "SUBDATASETS", 11))
    1412               0 :         return papszSubdatasets;
    1413                 :     else
    1414               0 :         return GDALDataset::GetMetadata(pszDomain);
    1415                 : }
    1416                 : 
    1417                 : /*****************************************************
    1418                 :  * \brief Fetch the projection definition string
    1419                 :  * for this dataset in OpenGIS WKT format. It should
    1420                 :  * be suitable for use with the OGRSpatialReference
    1421                 :  * class.
    1422                 :  *****************************************************/
    1423               0 : const char* PostGISRasterDataset::GetProjectionRef() {
    1424               0 :     CPLString osCommand;
    1425                 :     PGresult* poResult;
    1426                 : 
    1427               0 :     if (nSrid == -1)
    1428               0 :         return "";
    1429                 : 
    1430               0 :     if (pszProjection)
    1431               0 :         return pszProjection;
    1432                 : 
    1433                 :     /********************************************************
    1434                 :      *          Reading proj from database
    1435                 :      ********************************************************/
    1436                 :     osCommand.Printf("SELECT srtext FROM spatial_ref_sys where SRID=%d",
    1437               0 :             nSrid);
    1438               0 :     poResult = PQexec(this->poConn, osCommand.c_str());
    1439               0 :     if (poResult && PQresultStatus(poResult) == PGRES_TUPLES_OK
    1440                 :             && PQntuples(poResult) > 0) {
    1441                 :         /*
    1442                 :          * TODO: Memory leak detected with valgrind caused by allocation here.
    1443                 :          * Even when the return string is freed
    1444                 :          */
    1445               0 :         pszProjection = CPLStrdup(PQgetvalue(poResult, 0, 0));
    1446                 :     }
    1447                 : 
    1448               0 :     if (poResult)
    1449               0 :         PQclear(poResult);
    1450                 : 
    1451               0 :     return pszProjection;
    1452                 : }
    1453                 : 
    1454                 : /**********************************************************
    1455                 :  * \brief Set projection definition. The input string must
    1456                 :  * be in OGC WKT or PROJ.4 format
    1457                 :  **********************************************************/
    1458               0 : CPLErr PostGISRasterDataset::SetProjection(const char * pszProjectionRef) {
    1459               0 :     VALIDATE_POINTER1(pszProjectionRef, "SetProjection", CE_Failure);
    1460                 : 
    1461               0 :     CPLString osCommand;
    1462                 :     PGresult * poResult;
    1463               0 :     int nFetchedSrid = -1;
    1464                 : 
    1465                 : 
    1466                 :     /*****************************************************************
    1467                 :      * Check if the dataset allows updating
    1468                 :      *****************************************************************/
    1469               0 :     if (GetAccess() != GA_Update) {
    1470                 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    1471               0 :                 "This driver doesn't allow write access");
    1472               0 :         return CE_Failure;
    1473                 :     }
    1474                 : 
    1475                 :     /*****************************************************************
    1476                 :      * Look for projection with this text
    1477                 :      *****************************************************************/
    1478                 : 
    1479                 :     // First, WKT text
    1480                 :     osCommand.Printf("SELECT srid FROM spatial_ref_sys where srtext='%s'",
    1481               0 :             pszProjectionRef);
    1482               0 :     poResult = PQexec(poConn, osCommand.c_str());
    1483                 : 
    1484               0 :     if (poResult && PQresultStatus(poResult) == PGRES_TUPLES_OK
    1485                 :             && PQntuples(poResult) > 0) {
    1486                 : 
    1487               0 :         nFetchedSrid = atoi(PQgetvalue(poResult, 0, 0));
    1488                 : 
    1489                 :         // update class attribute
    1490               0 :         nSrid = nFetchedSrid;
    1491                 : 
    1492                 : 
    1493                 :         // update raster_columns table
    1494                 :         osCommand.Printf("UPDATE raster_columns SET srid=%d WHERE \
    1495                 :                     r_table_name = '%s' AND r_column = '%s'",
    1496               0 :                 nSrid, pszTable, pszColumn);
    1497               0 :         poResult = PQexec(poConn, osCommand.c_str());
    1498               0 :         if (poResult == NULL || PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    1499                 :             CPLError(CE_Failure, CPLE_AppDefined,
    1500                 :                     "Couldn't update raster_columns table: %s",
    1501               0 :                     PQerrorMessage(poConn));
    1502               0 :             return CE_Failure;
    1503                 :         }
    1504                 : 
    1505                 :         // TODO: Update ALL blocks with the new srid...
    1506                 : 
    1507               0 :         return CE_None;
    1508                 :     }
    1509                 :         // If not, proj4 text
    1510                 :     else {
    1511                 :         osCommand.Printf(
    1512                 :                 "SELECT srid FROM spatial_ref_sys where proj4text='%s'",
    1513               0 :                 pszProjectionRef);
    1514               0 :         poResult = PQexec(poConn, osCommand.c_str());
    1515                 : 
    1516               0 :         if (poResult && PQresultStatus(poResult) == PGRES_TUPLES_OK
    1517                 :                 && PQntuples(poResult) > 0) {
    1518                 : 
    1519               0 :             nFetchedSrid = atoi(PQgetvalue(poResult, 0, 0));
    1520                 : 
    1521                 :             // update class attribute
    1522               0 :             nSrid = nFetchedSrid;
    1523                 : 
    1524                 :             // update raster_columns table
    1525                 :             osCommand.Printf("UPDATE raster_columns SET srid=%d WHERE \
    1526                 :                     r_table_name = '%s' AND r_column = '%s'",
    1527               0 :                     nSrid, pszTable, pszColumn);
    1528                 : 
    1529               0 :             poResult = PQexec(poConn, osCommand.c_str());
    1530               0 :             if (poResult == NULL ||
    1531                 :                     PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    1532                 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1533                 :                         "Couldn't update raster_columns table: %s",
    1534               0 :                         PQerrorMessage(poConn));
    1535               0 :                 return CE_Failure;
    1536                 :             }
    1537                 : 
    1538                 :             // TODO: Update ALL blocks with the new srid...
    1539                 : 
    1540               0 :             return CE_None;
    1541                 :         }
    1542                 :         else {
    1543                 :             CPLError(CE_Failure, CPLE_WrongFormat,
    1544               0 :                     "Couldn't find WKT neither proj4 definition");
    1545               0 :             return CE_Failure;
    1546                 :         }
    1547               0 :     }
    1548                 : }
    1549                 : 
    1550                 : /********************************************************
    1551                 :  * \brief Set the affine transformation coefficients
    1552                 :  ********************************************************/
    1553               0 : CPLErr PostGISRasterDataset::SetGeoTransform(double* padfTransform) {
    1554               0 :     if (!padfTransform)
    1555               0 :         return CE_Failure;
    1556                 : 
    1557               0 :     adfGeoTransform[0] = padfTransform[0];
    1558               0 :     adfGeoTransform[1] = padfTransform[1];
    1559               0 :     adfGeoTransform[2] = padfTransform[2];
    1560               0 :     adfGeoTransform[3] = padfTransform[3];
    1561               0 :     adfGeoTransform[4] = padfTransform[4];
    1562               0 :     adfGeoTransform[5] = padfTransform[5];
    1563                 : 
    1564               0 :     return CE_None;
    1565                 : }
    1566                 : 
    1567                 : /********************************************************
    1568                 :  * \brief Get the affine transformation coefficients
    1569                 :  ********************************************************/
    1570               0 : CPLErr PostGISRasterDataset::GetGeoTransform(double * padfTransform) {
    1571                 :     // copy necessary values in supplied buffer
    1572               0 :     padfTransform[0] = adfGeoTransform[0];
    1573               0 :     padfTransform[1] = adfGeoTransform[1];
    1574               0 :     padfTransform[2] = adfGeoTransform[2];
    1575               0 :     padfTransform[3] = adfGeoTransform[3];
    1576               0 :     padfTransform[4] = adfGeoTransform[4];
    1577               0 :     padfTransform[5] = adfGeoTransform[5];
    1578                 : 
    1579               0 :     return CE_None;
    1580                 : }
    1581                 : 
    1582                 : /********************************************************
    1583                 :  * \brief Create a copy of a PostGIS Raster dataset.
    1584                 :  ********************************************************/
    1585                 : GDALDataset * 
    1586              18 : PostGISRasterDataset::CreateCopy( const char * pszFilename,
    1587                 :     GDALDataset *poGSrcDS, int bStrict, char ** papszOptions, 
    1588                 :     GDALProgressFunc pfnProgress, void * pProgressData ) 
    1589                 : {
    1590              18 :     char* pszSchema = NULL;
    1591              18 :     char* pszTable = NULL;
    1592              18 :     char* pszColumn = NULL;
    1593              18 :     char* pszWhere = NULL;
    1594              18 :     GBool bBrowseDatabase = false;
    1595                 :     int nMode;
    1596              18 :     char* pszConnectionString = NULL;
    1597                 :     const char* pszSubdatasetName;
    1598              18 :     PGconn * poConn = NULL;
    1599              18 :     PGresult * poResult = NULL;
    1600              18 :     CPLString osCommand;
    1601                 :     GBool bInsertSuccess;
    1602              18 :     PostGISRasterDataset *poSrcDS = (PostGISRasterDataset *)poGSrcDS;
    1603                 :     PostGISRasterDataset *poSubDS;
    1604                 : 
    1605                 :     // Check connection string
    1606              18 :     if (pszFilename == NULL ||
    1607                 :         !EQUALN(pszFilename, "PG:", 3)) {
    1608                 :         /**
    1609                 :          * The connection string provided is not a valid connection 
    1610                 :          * string.
    1611                 :          */
    1612                 :         CPLError( CE_Failure, CPLE_NotSupported, 
    1613                 :             "PostGIS Raster driver was unable to parse the provided "
    1614              18 :             "connection string." );
    1615              18 :         return NULL;
    1616                 :     }
    1617                 : 
    1618                 :     poConn = GetConnection(pszFilename, &pszConnectionString, &pszSchema, 
    1619               0 :         &pszTable, &pszColumn, &pszWhere, &nMode, &bBrowseDatabase);
    1620               0 :     if (poConn == NULL || bBrowseDatabase || pszTable == NULL) 
    1621                 :     {
    1622               0 :         CPLFree(pszConnectionString);
    1623               0 :         CPLFree(pszSchema);
    1624               0 :         CPLFree(pszTable);
    1625               0 :         CPLFree(pszColumn);
    1626               0 :         CPLFree(pszWhere);
    1627                 : 
    1628                 :         // if connection info fails, browsing mode, or no table set
    1629               0 :         return NULL;
    1630                 :     }
    1631                 : 
    1632                 :     // begin transaction
    1633               0 :     poResult = PQexec(poConn, "begin");
    1634               0 :     if (poResult == NULL ||
    1635                 :         PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    1636                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1637                 :             "Error beginning database transaction: %s",
    1638               0 :             PQerrorMessage(poConn));
    1639               0 :         if (poResult != NULL)
    1640               0 :             PQclear(poResult);
    1641               0 :         if (pszSchema)
    1642               0 :             CPLFree(pszSchema);
    1643               0 :         if (pszTable)
    1644               0 :             CPLFree(pszTable);
    1645               0 :         if (pszColumn)
    1646               0 :             CPLFree(pszColumn);
    1647               0 :         if (pszWhere)
    1648               0 :             CPLFree(pszWhere);
    1649                 : 
    1650               0 :         CPLFree(pszConnectionString);
    1651                 : 
    1652               0 :         return NULL;
    1653                 :     }
    1654                 : 
    1655               0 :     PQclear(poResult);
    1656                 : 
    1657                 :     // create table for raster (if not exists because a
    1658                 :     // dataset will not be reported for an empty table)
    1659                 : 
    1660                 :     // TODO: is 'rid' necessary?
    1661                 :     osCommand.Printf("create table if not exists %s.%s (rid serial, %s "
    1662                 :         "public.raster, constraint %s_pkey primary key (rid));",
    1663               0 :         pszSchema, pszTable, pszColumn, pszTable);
    1664               0 :     poResult = PQexec(poConn, osCommand.c_str());
    1665               0 :     if (
    1666                 :             poResult == NULL ||
    1667                 :             PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    1668                 : 
    1669                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1670                 :                 "Error creating needed tables: %s",
    1671               0 :                 PQerrorMessage(poConn));
    1672               0 :         if (poResult != NULL)
    1673               0 :             PQclear(poResult);
    1674                 : 
    1675                 :         // rollback
    1676               0 :         poResult = PQexec(poConn, "rollback");
    1677               0 :         if (poResult == NULL ||
    1678                 :             PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    1679                 : 
    1680                 :             CPLError(CE_Failure, CPLE_AppDefined,
    1681                 :                 "Error rolling back transaction: %s",
    1682               0 :                 PQerrorMessage(poConn));
    1683                 :         }
    1684               0 :         if (poResult != NULL)
    1685               0 :             PQclear(poResult);
    1686               0 :         if (pszSchema)
    1687               0 :             CPLFree(pszSchema);
    1688               0 :         if (pszTable)
    1689               0 :             CPLFree(pszTable);
    1690               0 :         if (pszColumn)
    1691               0 :             CPLFree(pszColumn);
    1692               0 :         if (pszWhere)
    1693               0 :             CPLFree(pszWhere);
    1694                 : 
    1695               0 :         CPLFree(pszConnectionString);
    1696                 : 
    1697               0 :         return NULL;
    1698                 :     }
    1699                 : 
    1700               0 :     PQclear(poResult);
    1701                 : 
    1702                 :     osCommand.Printf("create index %s_%s_gist ON %s.%s USING gist "
    1703                 :         "(public.st_convexhull(%s));", pszTable, pszColumn, 
    1704               0 :         pszSchema, pszTable, pszColumn);
    1705               0 :     poResult = PQexec(poConn, osCommand.c_str());
    1706               0 :     if (
    1707                 :             poResult == NULL ||
    1708                 :             PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    1709                 : 
    1710                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1711                 :                 "Error creating needed index: %s",
    1712               0 :                 PQerrorMessage(poConn));
    1713               0 :         if (poResult != NULL)
    1714               0 :             PQclear(poResult);
    1715                 : 
    1716                 :         // rollback
    1717               0 :         poResult = PQexec(poConn, "rollback");
    1718               0 :         if (poResult == NULL ||
    1719                 :             PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    1720                 : 
    1721                 :             CPLError(CE_Failure, CPLE_AppDefined,
    1722                 :                 "Error rolling back transaction: %s",
    1723               0 :                 PQerrorMessage(poConn));
    1724                 :         }
    1725               0 :         if (poResult != NULL)
    1726               0 :             PQclear(poResult);
    1727               0 :         if (pszSchema)
    1728               0 :             CPLFree(pszSchema);
    1729               0 :         if (pszTable)
    1730               0 :             CPLFree(pszTable);
    1731               0 :         if (pszColumn)
    1732               0 :             CPLFree(pszColumn);
    1733               0 :         if (pszWhere)
    1734               0 :             CPLFree(pszWhere);
    1735                 : 
    1736               0 :         CPLFree(pszConnectionString);
    1737                 : 
    1738               0 :         return NULL;
    1739                 :     }
    1740                 : 
    1741               0 :     PQclear(poResult);
    1742                 : 
    1743               0 :     if (poSrcDS->nMode == ONE_RASTER_PER_TABLE) {
    1744                 :         // one raster per table
    1745                 : 
    1746                 :         // insert one raster
    1747                 :         bInsertSuccess = InsertRaster(poConn, poSrcDS,
    1748               0 :             pszSchema, pszTable, pszColumn);
    1749               0 :         if (!bInsertSuccess) {
    1750                 :             // rollback
    1751               0 :             poResult = PQexec(poConn, "rollback");
    1752               0 :             if (poResult == NULL ||
    1753                 :                 PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    1754                 : 
    1755                 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1756                 :                     "Error rolling back transaction: %s",
    1757               0 :                     PQerrorMessage(poConn));
    1758                 :             }
    1759               0 :             if (poResult != NULL)
    1760               0 :                 PQclear(poResult);
    1761               0 :             if (pszSchema)
    1762               0 :                 CPLFree(pszSchema);
    1763               0 :             if (pszTable)
    1764               0 :                 CPLFree(pszTable);
    1765               0 :             if (pszColumn)
    1766               0 :                 CPLFree(pszColumn);
    1767               0 :             if (pszWhere)
    1768               0 :                 CPLFree(pszWhere);
    1769                 : 
    1770               0 :             CPLFree(pszConnectionString);
    1771                 : 
    1772               0 :             return NULL;
    1773                 :         }
    1774                 :     }
    1775               0 :     else if (poSrcDS->nMode == ONE_RASTER_PER_ROW) {
    1776                 :         // one raster per row
    1777                 : 
    1778                 :         // papszSubdatasets contains name/desc for each subdataset
    1779               0 :         for (int i = 0; i < CSLCount(poSrcDS->papszSubdatasets); i += 2) {
    1780               0 :             pszSubdatasetName = CPLParseNameValue( poSrcDS->papszSubdatasets[i], NULL);
    1781               0 :             if (pszSubdatasetName == NULL) {
    1782                 :                 CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): "
    1783                 :                 "Could not parse name/value out of subdataset list: "
    1784               0 :                 "%s", poSrcDS->papszSubdatasets[i]);
    1785               0 :                 continue;
    1786                 :             }
    1787                 : 
    1788                 :             // for each subdataset
    1789               0 :             GDALOpenInfo poOpenInfo( pszSubdatasetName, GA_ReadOnly );
    1790                 :             // open the subdataset
    1791               0 :             poSubDS = (PostGISRasterDataset *)Open(&poOpenInfo);
    1792                 : 
    1793               0 :             if (poSubDS == NULL) {
    1794                 :                 // notify!
    1795                 :                 CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): "
    1796                 :                     "Could not open a subdataset: %s", 
    1797               0 :                     pszSubdatasetName);
    1798               0 :                 continue;
    1799                 :             }
    1800                 : 
    1801                 :             // insert one raster
    1802                 :             bInsertSuccess = InsertRaster(poConn, poSubDS,
    1803               0 :                 pszSchema, pszTable, pszColumn);
    1804                 : 
    1805               0 :             if (!bInsertSuccess) {
    1806                 :                 CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): "
    1807               0 :                     "Could not copy raster subdataset to new dataset." );
    1808                 : 
    1809                 :                 // keep trying ...
    1810                 :             }
    1811                 : 
    1812                 :             // close this dataset
    1813               0 :             GDALClose((GDALDatasetH)poSubDS);
    1814                 :         }
    1815                 :     }
    1816                 : 
    1817                 :     // commit transaction
    1818               0 :     poResult = PQexec(poConn, "commit");
    1819               0 :     if (poResult == NULL ||
    1820                 :         PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    1821                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1822                 :             "Error committing database transaction: %s",
    1823               0 :             PQerrorMessage(poConn));
    1824               0 :         if (poResult != NULL)
    1825               0 :             PQclear(poResult);
    1826               0 :         if (pszSchema)
    1827               0 :             CPLFree(pszSchema);
    1828               0 :         if (pszTable)
    1829               0 :             CPLFree(pszTable);
    1830               0 :         if (pszColumn)
    1831               0 :             CPLFree(pszColumn);
    1832               0 :         if (pszWhere)
    1833               0 :             CPLFree(pszWhere);
    1834                 : 
    1835               0 :         CPLFree(pszConnectionString);
    1836                 : 
    1837               0 :         return NULL;
    1838                 :     }
    1839                 : 
    1840               0 :     PQclear(poResult);
    1841                 : 
    1842               0 :     if (pszSchema)
    1843               0 :         CPLFree(pszSchema);
    1844               0 :     if (pszTable)
    1845               0 :         CPLFree(pszTable);
    1846               0 :     if (pszColumn)
    1847               0 :         CPLFree(pszColumn);
    1848               0 :     if (pszWhere)
    1849               0 :         CPLFree(pszWhere);
    1850                 : 
    1851               0 :     CPLFree(pszConnectionString);
    1852                 : 
    1853                 :     CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): "
    1854               0 :         "Opening new dataset: %s", pszFilename);
    1855                 : 
    1856                 :     // connect to the new dataset
    1857               0 :     GDALOpenInfo poOpenInfo( pszFilename, GA_Update );
    1858                 :     // open the newdataset
    1859               0 :     poSubDS = (PostGISRasterDataset *)Open(&poOpenInfo);
    1860                 : 
    1861               0 :     if (poSubDS == NULL) {
    1862                 :         CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): "
    1863               0 :             "New dataset could not be opened.");
    1864                 :     }
    1865                 : 
    1866               0 :     return poSubDS;
    1867                 : }
    1868                 : 
    1869                 : /********************************************************
    1870                 :  * \brief Helper method to insert a new raster.
    1871                 :  ********************************************************/
    1872                 : GBool 
    1873               0 : PostGISRasterDataset::InsertRaster(PGconn * poConn, 
    1874                 :     PostGISRasterDataset * poSrcDS, const char *pszSchema, 
    1875                 :     const char * pszTable, const char * pszColumn)
    1876                 : {
    1877               0 :     CPLString osCommand;
    1878               0 :     PGresult * poResult = NULL;
    1879                 : 
    1880               0 :     if (poSrcDS->pszWhere == NULL) {
    1881                 :         osCommand.Printf("insert into %s.%s (%s) (select %s from %s.%s)",
    1882                 :             pszSchema, pszTable, pszColumn, poSrcDS->pszColumn, 
    1883               0 :             poSrcDS->pszSchema, poSrcDS->pszTable);
    1884                 :     }
    1885                 :     else {
    1886                 :         osCommand.Printf("insert into %s.%s (%s) (select %s from %s.%s where %s)",
    1887                 :             pszSchema, pszTable, pszColumn, poSrcDS->pszColumn, 
    1888               0 :             poSrcDS->pszSchema, poSrcDS->pszTable, poSrcDS->pszWhere);
    1889                 :     }
    1890                 : 
    1891                 :     CPLDebug("PostGIS_Raster", "PostGISRasterDataset::InsertRaster(): Query = %s",
    1892               0 :         osCommand.c_str());
    1893                 : 
    1894               0 :     poResult = PQexec(poConn, osCommand.c_str());
    1895               0 :     if (
    1896                 :             poResult == NULL ||
    1897                 :             PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    1898                 : 
    1899                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1900                 :                 "Error inserting raster: %s",
    1901               0 :                 PQerrorMessage(poConn));
    1902               0 :         if (poResult != NULL)
    1903               0 :             PQclear(poResult);
    1904                 : 
    1905               0 :         return false;
    1906                 :     }
    1907                 : 
    1908               0 :     PQclear(poResult);
    1909                 : 
    1910               0 :     return true;
    1911                 : }
    1912                 : 
    1913                 : /*********************************************************
    1914                 :  * \brief Delete a PostGIS Raster dataset. 
    1915                 :  *********************************************************/
    1916                 : CPLErr
    1917               0 : PostGISRasterDataset::Delete(const char* pszFilename) 
    1918                 : {
    1919               0 :     char* pszSchema = NULL;
    1920               0 :     char* pszTable = NULL;
    1921               0 :     char* pszColumn = NULL;
    1922               0 :     char* pszWhere = NULL;
    1923                 :     GBool bBrowseDatabase;
    1924               0 :     char* pszConnectionString = NULL;
    1925                 :     int nMode;
    1926               0 :     PGconn * poConn = NULL;
    1927               0 :     PGresult * poResult = NULL;
    1928               0 :     CPLString osCommand;
    1929               0 :     CPLErr nError = CE_Failure;
    1930                 : 
    1931                 :     // Check connection string
    1932               0 :     if (pszFilename == NULL ||
    1933                 :         !EQUALN(pszFilename, "PG:", 3)) { 
    1934                 :         /**
    1935                 :          * The connection string provided is not a valid connection 
    1936                 :          * string.
    1937                 :          */
    1938                 :         CPLError( CE_Failure, CPLE_NotSupported, 
    1939                 :             "PostGIS Raster driver was unable to parse the provided "
    1940               0 :             "connection string. Nothing was deleted." );
    1941               0 :         return CE_Failure;
    1942                 :     }
    1943                 : 
    1944                 :     poConn = GetConnection(pszFilename, &pszConnectionString, 
    1945                 :         &pszSchema, &pszTable, &pszColumn, &pszWhere,
    1946               0 :         &nMode, &bBrowseDatabase);
    1947               0 :     if (poConn == NULL) {
    1948               0 :         CPLFree(pszConnectionString);
    1949               0 :         CPLFree(pszSchema);
    1950               0 :         CPLFree(pszTable);
    1951               0 :         CPLFree(pszColumn);
    1952               0 :         CPLFree(pszWhere);
    1953                 : 
    1954               0 :         return CE_Failure;
    1955                 :     }
    1956                 : 
    1957                 :     // begin transaction
    1958               0 :     poResult = PQexec(poConn, "begin");
    1959               0 :     if (poResult == NULL ||
    1960                 :         PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    1961                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1962                 :             "Error beginning database transaction: %s",
    1963               0 :             PQerrorMessage(poConn));
    1964                 : 
    1965                 :         // set nMode to NO_MODE to avoid any further processing
    1966               0 :         nMode = NO_MODE;
    1967                 :     }
    1968                 : 
    1969               0 :     PQclear(poResult);
    1970                 : 
    1971               0 :     if ( nMode == ONE_RASTER_PER_TABLE || 
    1972                 :         (nMode == ONE_RASTER_PER_ROW && pszWhere == NULL)) {
    1973                 :         // without a where clause, this delete command shall delete
    1974                 :         // all subdatasets, even if the mode is ONE_RASTER_PER_ROW
    1975                 : 
    1976                 :         // drop table <schema>.<table>;
    1977               0 :         osCommand.Printf("drop table %s.%s", pszSchema, pszTable);
    1978               0 :         poResult = PQexec(poConn, osCommand.c_str());
    1979               0 :         if (poResult == NULL || 
    1980                 :             PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    1981                 :             CPLError(CE_Failure, CPLE_AppDefined,
    1982                 :                     "Couldn't drop the table %s.%s: %s",
    1983               0 :                     pszSchema, pszTable, PQerrorMessage(poConn));
    1984                 :         }
    1985                 :         else {
    1986               0 :             PQclear(poResult);
    1987               0 :             nError = CE_None;
    1988                 :         }
    1989                 :     }
    1990               0 :     else if (nMode == ONE_RASTER_PER_ROW) {
    1991                 : 
    1992                 :         // delete from <schema>.<table> where <where>
    1993                 :         osCommand.Printf("delete from %s.%s where %s", pszSchema, 
    1994               0 :             pszTable, pszWhere);
    1995               0 :         poResult = PQexec(poConn, osCommand.c_str());
    1996               0 :         if (poResult == NULL || 
    1997                 :             PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    1998                 :             CPLError(CE_Failure, CPLE_AppDefined,
    1999                 :                     "Couldn't delete records from the table %s.%s: %s",
    2000               0 :                     pszSchema, pszTable, PQerrorMessage(poConn));
    2001                 :         }
    2002                 :         else {
    2003               0 :             PQclear(poResult);
    2004               0 :             nError = CE_None;
    2005                 :         }
    2006                 :     }
    2007                 : 
    2008                 :     // if mode == NO_MODE, the begin transaction above did not complete,
    2009                 :     // so no commit is necessary
    2010               0 :     if (nMode != NO_MODE) {
    2011               0 :         poResult = PQexec(poConn, "commit");
    2012               0 :         if (poResult == NULL ||
    2013                 :             PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    2014                 :             CPLError(CE_Failure, CPLE_AppDefined,
    2015                 :                 "Error committing database transaction: %s",
    2016               0 :                 PQerrorMessage(poConn));
    2017                 : 
    2018               0 :             nError = CE_Failure;
    2019                 :         }
    2020                 :     }
    2021                 : 
    2022               0 :     if (poResult)
    2023               0 :         PQclear(poResult);
    2024               0 :     if (pszSchema)
    2025               0 :         CPLFree(pszSchema);
    2026               0 :     if (pszTable)
    2027               0 :         CPLFree(pszTable);
    2028               0 :     if (pszColumn)
    2029               0 :         CPLFree(pszColumn);
    2030               0 :     if (pszWhere)
    2031               0 :         CPLFree(pszWhere);
    2032                 : 
    2033                 :     // clean up connection string
    2034               0 :     CPLFree(pszConnectionString);
    2035                 : 
    2036               0 :     return nError;
    2037                 : }
    2038                 : 
    2039                 : /************************************************************************/
    2040                 : /*                          GDALRegister_PostGISRaster()                */
    2041                 : 
    2042                 : /************************************************************************/
    2043             582 : void GDALRegister_PostGISRaster() {
    2044                 :     GDALDriver *poDriver;
    2045                 : 
    2046             582 :     if (GDALGetDriverByName("PostGISRaster") == NULL) {
    2047             561 :         poDriver = new PostGISRasterDriver();
    2048                 : 
    2049             561 :         poDriver->SetDescription("PostGISRaster");
    2050                 :         poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
    2051             561 :                 "PostGIS Raster driver");
    2052                 : 
    2053             561 :         poDriver->pfnOpen = PostGISRasterDataset::Open;
    2054             561 :         poDriver->pfnCreateCopy = PostGISRasterDataset::CreateCopy;
    2055             561 :         poDriver->pfnDelete = PostGISRasterDataset::Delete;
    2056                 : 
    2057             561 :         GetGDALDriverManager()->RegisterDriver(poDriver);
    2058                 :     }
    2059             582 : }
    2060                 : 

Generated by: LCOV version 1.7