LCOV - code coverage report
Current view: directory - frmts/postgisraster - postgisrasterdataset.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 972 112 11.5 %
Date: 2012-04-28 Functions: 24 7 29.2 %

       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 "ogr_api.h"
      36                 : #include "ogr_geometry.h"
      37                 : #include "gdal.h"
      38                 : #include "cpl_conv.h"
      39                 : #include "cpl_string.h"
      40                 : #include "gdal_priv.h"
      41                 : #include <math.h>
      42                 : #include "cpl_error.h"
      43                 : #include "ogr_core.h"
      44                 : 
      45                 : #ifdef _WIN32
      46                 : #define rint(x) floor((x) + 0.5)
      47                 : #endif
      48                 : 
      49                 : CPL_C_START
      50                 : void GDALRegister_PostGISRaster(void);
      51                 : 
      52                 : CPL_C_END
      53                 : 
      54                 : 
      55                 : 
      56                 : /************************
      57                 :  * \brief Constructor
      58                 :  ************************/
      59               0 : PostGISRasterDataset::PostGISRasterDataset() {
      60               0 :     papszSubdatasets = NULL;
      61               0 :     nSrid = -1;
      62               0 :     poConn = NULL;
      63               0 :     bRegularBlocking = false;
      64               0 :     bRegisteredInRasterColumns = false;
      65               0 :     pszSchema = NULL;
      66               0 :     pszTable = NULL;
      67               0 :     pszColumn = NULL;
      68               0 :     pszWhere = NULL;
      69               0 :     pszProjection = NULL;
      70               0 :     nMode = NO_MODE;
      71               0 :     poDriver = NULL;
      72               0 :     nBlockXSize = 0;
      73               0 :     nBlockYSize = 0;
      74               0 :     adfGeoTransform[0] = 0.0; /* X Origin (top left corner) */
      75               0 :     adfGeoTransform[1] = 1.0; /* X Pixel size */
      76               0 :     adfGeoTransform[2] = 0.0;
      77               0 :     adfGeoTransform[3] = 0.0; /* Y Origin (top left corner) */
      78               0 :     adfGeoTransform[4] = 0.0;
      79               0 :     adfGeoTransform[5] = 1.0; /* Y Pixel Size */
      80               0 :     bBlocksCached = false;
      81               0 :     bRegularBlocking = false;
      82               0 :     bAllTilesSnapToSameGrid = false;
      83                 : 
      84                 :     /**
      85                 :      * TODO: Parametrize bAllTilesSnapToSameGrid. It controls if all the
      86                 :      * raster rows, in ONE_RASTER_PER_TABLE mode, must be checked to test if
      87                 :      * they snap to the same grid and have the same srid. It can be the user
      88                 :      * decission, if he/she's sure all the rows pass the test and want more
      89                 :      * speed.
      90                 :      **/
      91                 : 
      92               0 : }
      93                 : 
      94                 : /************************
      95                 :  * \brief Constructor
      96                 :  ************************/
      97               0 : PostGISRasterDataset::~PostGISRasterDataset() {
      98                 : 
      99               0 :     if (pszSchema)
     100               0 :         CPLFree(pszSchema);
     101               0 :     if (pszTable)
     102               0 :         CPLFree(pszTable);
     103               0 :     if (pszColumn)
     104               0 :         CPLFree(pszColumn);
     105               0 :     if (pszWhere)
     106               0 :         CPLFree(pszWhere);
     107               0 :     if (pszProjection)
     108               0 :         CPLFree(pszProjection);
     109                 : 
     110               0 :     if (papszSubdatasets)
     111               0 :         CSLDestroy(papszSubdatasets);
     112               0 : }
     113                 : 
     114                 : /**************************************************************
     115                 :  * \brief Replace the single quotes by " in the input string
     116                 :  *
     117                 :  * Needed before tokenize function
     118                 :  *************************************************************/
     119                 : static
     120               2 : char * ReplaceSingleQuotes(const char * pszInput, int nLength) {
     121                 :     int i;
     122               2 :     char* pszOutput = NULL;
     123                 : 
     124               2 :     if (nLength == -1)
     125               2 :         nLength = strlen(pszInput);
     126                 : 
     127               2 :     pszOutput = (char*) CPLCalloc(nLength + 1, sizeof (char));
     128                 : 
     129             186 :     for (i = 0; i < nLength; i++) {
     130             184 :         if (pszInput[i] == '\'')
     131              24 :             pszOutput[i] = '"';
     132                 :         else
     133             160 :             pszOutput[i] = pszInput[i];
     134                 : 
     135                 :     }
     136                 : 
     137               2 :     return pszOutput;
     138                 : }
     139                 : 
     140                 : /**************************************************************
     141                 :  * \brief Replace the quotes by single quotes in the input string
     142                 :  *
     143                 :  * Needed in the 'where' part of the input string
     144                 :  *************************************************************/
     145                 : static
     146               0 : char * ReplaceQuotes(const char * pszInput, int nLength) {
     147                 :     int i;
     148               0 :     char * pszOutput = NULL;
     149                 : 
     150               0 :     if (nLength == -1)
     151               0 :         nLength = strlen(pszInput);
     152                 : 
     153               0 :     pszOutput = (char*) CPLCalloc(nLength + 1, sizeof (char));
     154                 : 
     155               0 :     for (i = 0; i < nLength; i++) {
     156               0 :         if (pszInput[i] == '"')
     157               0 :             pszOutput[i] = '\'';
     158                 :         else
     159               0 :             pszOutput[i] = pszInput[i];
     160                 :     }
     161                 : 
     162               0 :     return pszOutput;
     163                 : }
     164                 : 
     165                 : /*****************************************************************************
     166                 :  * \brief Split connection string into user, password, host, database...
     167                 :  *
     168                 :  * The parameters separated by spaces are return as a list of strings. The
     169                 :  * function accepts all the PostgreSQL recognized parameter key words.
     170                 :  *
     171                 :  * The returned list must be freed with CSLDestroy when no longer needed
     172                 :  *
     173                 :  *****************************************************************************/
     174                 : static
     175               2 : char** ParseConnectionString(const char * pszConnectionString) {
     176               2 :     char * pszEscapedConnectionString = NULL;
     177                 : 
     178                 :     /* Escape string following SQL scheme */
     179               2 :     pszEscapedConnectionString = ReplaceSingleQuotes(pszConnectionString, -1);
     180                 : 
     181                 :     /* Avoid PG: part */
     182               2 :     char* pszStartPos = (char*) strstr(pszEscapedConnectionString, ":") + 1;
     183                 : 
     184                 :     /* Tokenize */
     185                 :     char** papszParams = CSLTokenizeString2(pszStartPos, " ",
     186               2 :             CSLT_HONOURSTRINGS);
     187                 : 
     188                 :     /* Free */
     189               2 :     CPLFree(pszEscapedConnectionString);
     190                 : 
     191               2 :     return papszParams;
     192                 : 
     193                 : }
     194                 : 
     195                 : /**************************************************************************
     196                 :  * \brief Look for raster tables in database and store them as subdatasets
     197                 :  *
     198                 :  * If no table is provided in connection string, the driver looks for the
     199                 :  * existent raster tables in the schema given as argument. This argument,
     200                 :  * however, is optional. If a NULL value is provided, the driver looks for
     201                 :  * all raster tables in all schemas of the user-provided database.
     202                 :  *
     203                 :  * NOTE: Permissions are managed by libpq. The driver only returns an error
     204                 :  * if an error is returned when trying to access to tables not allowed to
     205                 :  * the current user.
     206                 :  **************************************************************************/
     207               0 : GBool PostGISRasterDataset::BrowseDatabase(const char* pszCurrentSchema,
     208                 :         char* pszValidConnectionString) {
     209                 :     /* Be careful! These 3 vars override the class ones! */
     210               0 :     char* pszSchema = NULL;
     211               0 :     char* pszTable = NULL;
     212               0 :     char* pszColumn = NULL;
     213               0 :     int i = 0;
     214               0 :     int nTuples = 0;
     215               0 :     PGresult * poResult = NULL;
     216               0 :     CPLString osCommand;
     217                 : 
     218                 : 
     219                 :     /*************************************************************
     220                 :      * Fetch all the raster tables and store them as subdatasets
     221                 :      *************************************************************/
     222               0 :     if (pszCurrentSchema == NULL) {
     223                 :         osCommand.Printf("select pg_namespace.nspname as schema, pg_class.relname as \
     224                 :           table, pg_attribute.attname as column from pg_class, \
     225                 :           pg_namespace,pg_attribute, pg_type where \
     226                 :           pg_class.relnamespace = pg_namespace.oid and pg_class.oid = \
     227                 :           pg_attribute.attrelid and pg_attribute.atttypid = pg_type.oid \
     228               0 :           and pg_type.typname = 'raster'");
     229                 : 
     230               0 :         poResult = PQexec(poConn, osCommand.c_str());
     231               0 :         if (
     232                 :                 poResult == NULL ||
     233                 :                 PQresultStatus(poResult) != PGRES_TUPLES_OK ||
     234                 :                 PQntuples(poResult) <= 0
     235                 :                 ) {
     236                 :             CPLError(CE_Failure, CPLE_AppDefined,
     237               0 :                     "Error browsing database for PostGIS Raster tables: %s", PQerrorMessage(poConn));
     238               0 :             if (poResult != NULL)
     239               0 :                 PQclear(poResult);
     240                 : 
     241               0 :             return false;
     242                 :         }
     243                 : 
     244                 : 
     245               0 :         nTuples = PQntuples(poResult);
     246               0 :         for (i = 0; i < nTuples; i++) {
     247               0 :             pszSchema = PQgetvalue(poResult, i, 0);
     248               0 :             pszTable = PQgetvalue(poResult, i, 1);
     249               0 :             pszColumn = PQgetvalue(poResult, i, 2);
     250                 : 
     251                 :             papszSubdatasets = CSLSetNameValue(papszSubdatasets,
     252                 :                     CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)),
     253                 :                     CPLSPrintf("PG:%s schema=%s table=%s column=%s",
     254               0 :                     pszValidConnectionString, pszSchema, pszTable, pszColumn));
     255                 : 
     256                 :             papszSubdatasets = CSLSetNameValue(papszSubdatasets,
     257                 :                     CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)),
     258               0 :                     CPLSPrintf("PostGIS Raster table at %s.%s (%s)", pszSchema, pszTable, pszColumn));
     259                 :         }
     260                 : 
     261               0 :         PQclear(poResult);
     262                 : 
     263                 :     }
     264                 :         /**********************************************************************
     265                 :          * Fetch all the schema's raster tables and store them as subdatasets
     266                 :          **********************************************************************/
     267                 :     else {
     268                 :         osCommand.Printf("select pg_class.relname as table, pg_attribute.attname \
     269                 :        as column from pg_class, pg_namespace,pg_attribute, pg_type where \
     270                 :        pg_class.relnamespace = pg_namespace.oid and pg_class.oid = \
     271                 :        pg_attribute.attrelid and pg_attribute.atttypid = pg_type.oid \
     272                 :        and pg_type.typname = 'raster' and pg_namespace.nspname = '%s'",
     273               0 :                 pszCurrentSchema);
     274                 : 
     275               0 :         poResult = PQexec(poConn, osCommand.c_str());
     276               0 :         if (
     277                 :                 poResult == NULL ||
     278                 :                 PQresultStatus(poResult) != PGRES_TUPLES_OK ||
     279                 :                 PQntuples(poResult) <= 0
     280                 :                 ) {
     281                 :             CPLError(CE_Failure, CPLE_AppDefined,
     282               0 :                     "Error browsing database for PostGIS Raster tables: %s", PQerrorMessage(poConn));
     283               0 :             if (poResult != NULL)
     284               0 :                 PQclear(poResult);
     285                 : 
     286               0 :             return false;
     287                 :         }
     288                 : 
     289                 : 
     290               0 :         nTuples = PQntuples(poResult);
     291               0 :         for (i = 0; i < nTuples; i++) {
     292               0 :             pszTable = PQgetvalue(poResult, i, 0);
     293               0 :             pszColumn = PQgetvalue(poResult, i, 1);
     294                 : 
     295                 :             papszSubdatasets = CSLSetNameValue(papszSubdatasets,
     296                 :                     CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)),
     297                 :                     CPLSPrintf("PG:%s schema=%s table=%s column=%s",
     298               0 :                     pszValidConnectionString, pszCurrentSchema, pszTable, pszColumn));
     299                 : 
     300                 :             papszSubdatasets = CSLSetNameValue(papszSubdatasets,
     301                 :                     CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)),
     302                 :                     CPLSPrintf("PostGIS Raster table at %s.%s (%s)", pszCurrentSchema,
     303               0 :                     pszTable, pszColumn));
     304                 :         }
     305                 : 
     306               0 :         PQclear(poResult);
     307                 :     }
     308                 : 
     309               0 :     return true;
     310                 : }
     311                 : 
     312                 : /*************************************************************************
     313                 :  * \brief Set the general raster properties.
     314                 :  *
     315                 :  * We must distinguish between tiled and untiled raster coverages. In
     316                 :  * PostGIS Raster, there's no real difference between 'tile' and 'raster'.
     317                 :  * There's only 'raster objects'. Each record of a raster table is a
     318                 :  * raster object, and has its own georeference information, whether if
     319                 :  * the record is a tile of a bigger raster coverage or is a complete
     320                 :  * raster. So, <b>there's no a way of knowing if the rows of a raster
     321                 :  * table are related or not</b>. It's user's responsibility. The only
     322                 :  * thing driver can do is to suppose all the rows of a table are from
     323                 :  * the same raster coverage if the user has queried for one table, without
     324                 :  * specifying a where clause.
     325                 :  *
     326                 :  * The user is responsible to ensure that the raster layer meets the minimum
     327                 :  * topological requirements for analysis. The ideal case is when all the raster
     328                 :  * tiles of a continuous layer are the same size, snap to the same grid and do
     329                 :  * not overlap.
     330                 :  *
     331                 :  * So, when we query for a raster table, we have 3 different cases:
     332                 :  *  - If the result is only one row, we can gather the raster properties
     333                 :  *    from the returned object, regardless is a tile or a whole raster
     334                 :  *  - If the result are several rows of a table, and the working mode is
     335                 :  *    ONE_RASTER_PER_TABLE, we assume all the rows are from the same raster
     336                 :  *    coverage. The rows are ordered by upper left y, upper left x, growing way,
     337                 :  *    and we can get raster size from the first and last elements.
     338                 :  *  - If the result are several rows of a table, and the working mode is
     339                 :  *    ONE_RASTER_PER_ROW, we assume each row is a different raster object,
     340                 :  *    and is reported as a subdataset. If you want only one of the raster rows,
     341                 :  *    you must specify a where clause to restrict the number of rows returned.
     342                 :  **************************************************************************/
     343               0 : GBool PostGISRasterDataset::SetRasterProperties
     344                 :     (const char * pszValidConnectionString)
     345                 : {
     346               0 :     PGresult* poResult = NULL;
     347               0 :     CPLString osCommand;
     348               0 :     CPLString osCommand2;
     349               0 :     PGresult* poResult2 = NULL;
     350               0 :     int i = 0;
     351               0 :     int nTuples = 0;
     352               0 :     GBool bRetValue = false;
     353               0 :     OGRSpatialReference * poSR = NULL;
     354               0 :     OGRGeometry* poGeom = NULL;
     355               0 :     OGRErr OgrErr = OGRERR_NONE;
     356               0 :     OGREnvelope* poE = NULL;
     357                 :     char* pszExtent;
     358                 :     char* pszProjectionRef;
     359               0 :     char* pszIdColumn = NULL;
     360               0 :     int nTmpSrid = -1;
     361               0 :     double dfTmpScaleX = 0.0;
     362               0 :     double dfTmpScaleY = 0.0;
     363               0 :     double dfTmpSkewX = 0.0;
     364               0 :     double dfTmpSkewY = 0.0;
     365               0 :     int nWidth = 0;
     366               0 :     int nHeight = 0;
     367               0 :     int nTmpWidth = 0;
     368               0 :     int nTmpHeight = 0;
     369                 : 
     370                 :     /* Determine the primary key/unique column on the raster table */
     371                 :     osCommand.Printf("select cols.column_name from information_schema."
     372                 :         "constraint_column_usage as cols join information_schema."
     373                 :         "table_constraints as constr on constr.constraint_name = cols."
     374                 :         "constraint_name where cols.table_schema = '%s' and cols.table_name "
     375               0 :         "= '%s'", pszSchema, pszTable);
     376                 : 
     377                 :     CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
     378               0 :             "Query: %s", osCommand.c_str());
     379               0 :     poResult = PQexec(poConn, osCommand.c_str());
     380               0 :     if (poResult == NULL ||
     381                 :         PQresultStatus(poResult) != PGRES_TUPLES_OK ||
     382                 :         PQntuples(poResult) <= 0 ) {
     383                 : 
     384               0 :         PQclear(poResult);
     385                 : 
     386                 :         // maybe there is no primary key or unique constraint;
     387                 :         // a sequence will also suffice, so look for the first sequence
     388                 : 
     389                 :         osCommand.Printf("select cols.column_name from information_schema."
     390                 :             "columns as cols join information_schema.sequences as seqs on cols."
     391                 :             "column_default like '%%'||seqs.sequence_name||'%%' where cols."
     392                 :             "table_schema = '%s' and cols.table_name = '%s'", pszSchema, 
     393               0 :             pszTable);
     394                 : 
     395                 :         CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
     396               0 :                 "Query: %s", osCommand.c_str());
     397               0 :         poResult = PQexec(poConn, osCommand.c_str());
     398                 : 
     399               0 :         if (poResult == NULL || 
     400                 :             PQresultStatus(poResult) != PGRES_TUPLES_OK ||
     401                 :             PQntuples(poResult) <= 0) {
     402                 : 
     403                 :             CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
     404               0 :                 "Could not find a primary key or unique column on the specified table; using UpperLeftX and UpperLeftY.");
     405                 : 
     406                 :             /* If no primary key or unique column found, fall back to raster upperleft x&y */
     407                 :         }
     408                 :         else {
     409               0 :             pszIdColumn = CPLStrdup(PQgetvalue(poResult, 0, 0));
     410                 :         }
     411                 :     }
     412                 :     else {
     413               0 :         pszIdColumn = CPLStrdup(PQgetvalue(poResult, 0, 0));
     414                 :     }
     415                 : 
     416               0 :     PQclear(poResult);
     417                 : 
     418                 :     /* Execute the query to fetch raster data from db */
     419               0 :     if (pszWhere == NULL) {
     420               0 :         if (pszIdColumn == NULL) {
     421                 :             osCommand.Printf("select (foo.md).* from (select st_metadata(%s) "
     422               0 :                 "as md from %s.%s) as foo", pszColumn, pszSchema, pszTable);
     423                 :         }
     424                 :         else {
     425                 :             osCommand.Printf("select (foo.md).*, foo.%s from (select %s, "
     426                 :                 "st_metadata(%s) as md from %s.%s) as foo", pszIdColumn, 
     427               0 :                 pszIdColumn, pszColumn, pszSchema, pszTable);
     428                 :         }
     429                 :     } else {
     430               0 :         if (pszIdColumn == NULL) {
     431                 :             osCommand.Printf("select (foo.md).* from (select st_metadata(%s) "
     432                 :             "as md from %s.%s where %s) as foo", pszColumn, pszSchema, 
     433               0 :             pszTable, pszWhere);
     434                 :         }
     435                 :         else {
     436                 :             osCommand.Printf("select (foo.md).*, foo.%s from (select %s, "
     437                 :             "st_metadata(%s) as md from %s.%s where %s) as foo", pszIdColumn, 
     438               0 :             pszIdColumn, pszColumn, pszSchema, pszTable, pszWhere);
     439                 :         }
     440                 :     }
     441                 : 
     442                 :     CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
     443               0 :             "Query: %s", osCommand.c_str());
     444               0 :     poResult = PQexec(poConn, osCommand.c_str());
     445               0 :     if (
     446                 :             poResult == NULL ||
     447                 :             PQresultStatus(poResult) != PGRES_TUPLES_OK ||
     448                 :             PQntuples(poResult) <= 0
     449                 :             ) {
     450                 :         CPLError(CE_Failure, CPLE_AppDefined,
     451               0 :                 "Error browsing database for PostGIS Raster properties");
     452                 : 
     453               0 :         if (poResult != NULL)
     454               0 :             PQclear(poResult);
     455                 : 
     456               0 :         if (pszIdColumn != NULL)
     457               0 :             CPLFree(pszIdColumn);
     458                 : 
     459               0 :         return false;
     460                 :     }
     461                 : 
     462               0 :     nTuples = PQntuples(poResult);
     463                 : 
     464                 : 
     465                 :     /******************************************
     466                 :      * Easier case. Only one raster to fetch
     467                 :      ******************************************/
     468               0 :     if (nTuples == 1) {
     469               0 :         nSrid = atoi(PQgetvalue(poResult, 0, 8));
     470               0 :         nBands = atoi(PQgetvalue(poResult, 0, 9));
     471               0 :         adfGeoTransform[0] = atof(PQgetvalue(poResult, 0, 0)); //upperleft x
     472               0 :         adfGeoTransform[1] = atof(PQgetvalue(poResult, 0, 4)); //pixelsize x
     473               0 :         adfGeoTransform[2] = atof(PQgetvalue(poResult, 0, 6)); //skew x
     474               0 :         adfGeoTransform[3] = atof(PQgetvalue(poResult, 0, 1)); //upperleft y
     475               0 :         adfGeoTransform[4] = atof(PQgetvalue(poResult, 0, 7)); //skew y
     476               0 :         adfGeoTransform[5] = atof(PQgetvalue(poResult, 0, 5)); //pixelsize y
     477                 : 
     478               0 :         nRasterXSize = atoi(PQgetvalue(poResult, 0, 2));
     479               0 :         nRasterYSize = atoi(PQgetvalue(poResult, 0, 3));
     480                 : 
     481                 :         /**
     482                 :          * Not tiled dataset: The whole raster.
     483                 :          * TODO: 'invent' a good block size.
     484                 :          */
     485               0 :         nBlockXSize = nRasterXSize;
     486               0 :         nBlockYSize = nRasterYSize;
     487                 : 
     488               0 :         bRetValue = true;
     489                 :     } else {
     490               0 :         switch (nMode) {
     491                 :                 /**
     492                 :                  * Each row is a different raster. Create subdatasets, one per row
     493                 :                  **/
     494                 : 
     495                 :             case ONE_RASTER_PER_ROW:
     496                 :                 {
     497                 : 
     498               0 :                 if (pszIdColumn == NULL) {
     499               0 :                     for (i = 0; i < nTuples; i++) {
     500               0 :                         adfGeoTransform[0] = atof(PQgetvalue(poResult, i, 0)); //upperleft x
     501               0 :                         adfGeoTransform[3] = atof(PQgetvalue(poResult, i, 1)); //upperleft y
     502                 :                         papszSubdatasets = CSLSetNameValue(papszSubdatasets,
     503                 :                                 CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)),
     504                 :                                 CPLSPrintf("PG:%s schema=%s table=%s column=%s "
     505                 :                                     "where='ST_UpperLeftX(%s) = %f AND ST_UpperLeftY(%s) = %f'",
     506                 :                                     pszValidConnectionString, pszSchema, pszTable, pszColumn, 
     507               0 :                                     pszColumn, adfGeoTransform[0], pszColumn, adfGeoTransform[3]));
     508                 : 
     509                 :                         papszSubdatasets = CSLSetNameValue(papszSubdatasets,
     510                 :                                 CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)),
     511                 :                                 CPLSPrintf("PostGIS Raster at %s.%s (%s), UpperLeft = %f, %f", pszSchema,
     512               0 :                                     pszTable, pszColumn, adfGeoTransform[0], adfGeoTransform[3]));
     513                 :                     }
     514                 :                 }
     515                 :                 else {
     516               0 :                     for (i = 0; i < nTuples; i++) {
     517                 :                         // this is the raster ID (or unique column)
     518               0 :                         nSrid = atoi(PQgetvalue(poResult, i, 10));
     519                 : 
     520                 :                         papszSubdatasets = CSLSetNameValue(papszSubdatasets,
     521                 :                                 CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)),
     522                 :                                 CPLSPrintf("PG:%s schema=%s table=%s column=%s where='%s = %d'",
     523                 :                                 pszValidConnectionString, pszSchema, pszTable, pszColumn, 
     524               0 :                                 pszIdColumn, nSrid));
     525                 : 
     526                 :                         papszSubdatasets = CSLSetNameValue(papszSubdatasets,
     527                 :                                 CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)),
     528                 :                                 CPLSPrintf("PostGIS Raster at %s.%s (%s), %s = %d", pszSchema,
     529               0 :                                 pszTable, pszColumn, pszIdColumn, nSrid));
     530                 :                     }
     531                 : 
     532               0 :                     CPLFree(pszIdColumn);
     533                 :                 }
     534                 : 
     535                 : 
     536                 :                 /* Not a single raster fetched */
     537               0 :                 nRasterXSize = 0;
     538               0 :                 nRasterYSize = 0;
     539                 : 
     540               0 :                 bRetValue = true;
     541                 : 
     542                 :                 }
     543               0 :                 break;
     544                 : 
     545                 :                 /************************************************************
     546                 :                  * All rows form a whole raster coverage
     547                 :                 ************************************************************/
     548                 :             case ONE_RASTER_PER_TABLE:
     549                 :                 {
     550                 :                 /* This column is not used in this mode. */
     551               0 :                 if (pszIdColumn != NULL)
     552               0 :                     CPLFree(pszIdColumn);
     553                 : 
     554                 :                 /**
     555                 :                  * Get the rest of raster properties from this object
     556                 :                  */
     557               0 :                 nSrid = atoi(PQgetvalue(poResult, 0, 8));
     558                 : 
     559               0 :                 nBands = atoi(PQgetvalue(poResult, 0, 9));
     560               0 :                 adfGeoTransform[0] = atof(PQgetvalue(poResult, 0, 0)); //upperleft x
     561               0 :                 adfGeoTransform[1] = atof(PQgetvalue(poResult, 0, 4)); //pixelsize x
     562               0 :                 adfGeoTransform[2] = atof(PQgetvalue(poResult, 0, 6)); //skew x
     563               0 :                 adfGeoTransform[3] = atof(PQgetvalue(poResult, 0, 1)); //upperleft y
     564               0 :                 adfGeoTransform[4] = atof(PQgetvalue(poResult, 0, 7)); //skew y
     565               0 :                 adfGeoTransform[5] = atof(PQgetvalue(poResult, 0, 5)); //pixelsize y
     566               0 :                 nWidth = atoi(PQgetvalue(poResult, 0, 2));
     567               0 :                 nHeight = atoi(PQgetvalue(poResult, 0, 3));
     568                 : 
     569                 :                 /**
     570                 :                  * Now check if all tiles have the same dimensions.
     571                 :                  *
     572                 :                  * NOTE: If bRegularBlocking is 'true', this is not checked.
     573                 :                  * It's user responsibility
     574                 :                  *
     575                 :                  * TODO: Find a good block size, that works in any situation.
     576                 :                  **/
     577               0 :                 if (!bRegularBlocking) 
     578                 :                 {
     579               0 :                     for(i = 1; i < nTuples; i++)
     580                 :                     {
     581               0 :                         nTmpWidth = atoi(PQgetvalue(poResult, i, 2));
     582               0 :                         nTmpHeight = atoi(PQgetvalue(poResult, i, 3));
     583                 : 
     584               0 :                         if (nWidth != nTmpWidth || nHeight != nTmpHeight)
     585                 :                         {
     586                 :                             // Not supported until the above TODO is implemented
     587                 :                             CPLError(CE_Failure, CPLE_AppDefined,
     588                 :                                 "Error, the table %s.%s contains tiles with "
     589                 :                                 "different size, and irregular blocking is "
     590               0 :                                 "not supported yet", pszSchema, pszTable);
     591                 : 
     592               0 :                             PQclear(poResult);
     593                 : 
     594               0 :                             return false;                             
     595                 :                         }
     596                 :                     }
     597                 : 
     598                 :                     // Now, we can ensure this
     599               0 :                     bRegularBlocking = true;
     600               0 :                     nBlockXSize = nWidth;
     601               0 :                     nBlockYSize = nHeight;
     602                 :                 }
     603                 : 
     604                 :                 // The user ensures this...
     605                 :                 else
     606                 :                 {                                        
     607               0 :                     nBlockXSize = nWidth;
     608               0 :                     nBlockYSize = nHeight;
     609                 :                 }
     610                 : 
     611                 :                 /**
     612                 :                  * Check all the raster tiles have the same srid and snap to the
     613                 :                  * same grid. If not, return an error
     614                 :                  *
     615                 :                  * NOTE: If bAllTilesSnapToSameGrid is 'true', this is not
     616                 :                  * checked. It's user responsibility.
     617                 :                  *
     618                 :                  * TODO: Work even if this requisites are  not complained. For
     619                 :                  * example, by:
     620                 :                  *  - Resampling all the rows to the grid of the first one
     621                 :                  *  - Providing a new grid alignment for all the rows, with a
     622                 :                  *    maximum of 6 parameters: ulx, uly, pixelsizex, pixelsizey,
     623                 :                  *    skewx, skewy or a minimum of 3 parameters: ulx, uly,
     624                 :                  *    pixelsize (x and y pixel sizes are equal and both skew are
     625                 :                  *    0).
     626                 :                  **/
     627               0 :                 if (!bAllTilesSnapToSameGrid)
     628                 :                 {
     629               0 :                     for(i = 1; i < nTuples; i++)
     630                 :                     {
     631               0 :                         nTmpSrid = atoi(PQgetvalue(poResult, i, 8));
     632               0 :                         dfTmpScaleX = atof(PQgetvalue(poResult, i, 4));
     633               0 :                         dfTmpScaleY = atof(PQgetvalue(poResult, i, 5));
     634               0 :                         dfTmpSkewX = atof(PQgetvalue(poResult, i, 6));
     635               0 :                         dfTmpSkewY = atof(PQgetvalue(poResult, i, 7));
     636                 : 
     637               0 :                         if (nTmpSrid != nSrid ||
     638               0 :                                 FLT_NEQ(dfTmpScaleX, adfGeoTransform[1]) ||
     639               0 :                                 FLT_NEQ(dfTmpScaleY, adfGeoTransform[5]) ||
     640               0 :                                 FLT_NEQ(dfTmpSkewX, adfGeoTransform[2]) ||
     641               0 :                                 FLT_NEQ(dfTmpSkewY, adfGeoTransform[4]))
     642                 :                         {
     643                 :                             /**
     644                 :                              * In this mode, it is not allowed this situation,
     645                 :                              * unless while the above TODO is not implemented
     646                 :                              **/
     647                 :                             CPLError(CE_Failure, CPLE_AppDefined,
     648                 :                                 "Error, the table %s.%s contains tiles with "
     649                 :                                 "different SRID or snapping to different grids",
     650               0 :                                 pszSchema, pszTable);
     651                 : 
     652               0 :                             PQclear(poResult);
     653                 : 
     654               0 :                             return false;
     655                 :                         }
     656                 :                     }
     657                 : 
     658                 :                     // Now, we can ensure this
     659               0 :                     bAllTilesSnapToSameGrid = true;
     660                 :                 }
     661                 : 
     662                 :                 /**
     663                 :                  * Now, if there's irregular blocking and/or the blocks don't
     664                 :                  * snap to the same grid or don't have the same srid, we should
     665                 :                  * fix these situations. Assuming that we don't return an error
     666                 :                  * in that cases, of course.
     667                 :                  **/
     668                 : 
     669                 : 
     670                 : 
     671                 :                 /**
     672                 :                  * Get whole raster extent
     673                 :                  **/
     674               0 :                 if (pszWhere == NULL)
     675                 :                     osCommand2.Printf("select st_astext(st_setsrid(st_extent(%s::geometry),%d)) from %s.%s",
     676               0 :                         pszColumn, nSrid, pszSchema, pszTable);
     677                 :                 else
     678                 :                     osCommand2.Printf("select st_astext(st_setsrid(st_extent(%s::geometry),%d)) from %s.%s where %s",
     679               0 :                         pszColumn, nSrid, pszSchema, pszTable, pszWhere);
     680                 : 
     681                 : 
     682               0 :                 poResult2 = PQexec(poConn, osCommand2.c_str());
     683               0 :                 if (poResult2 == NULL ||
     684                 :                         PQresultStatus(poResult2) != PGRES_TUPLES_OK ||
     685                 :                         PQntuples(poResult2) <= 0) {
     686                 :                     CPLError(CE_Failure, CPLE_AppDefined,
     687                 :                             "Error calculating whole raster extent: %s",
     688               0 :                             PQerrorMessage(poConn));
     689                 : 
     690               0 :                     if (poResult2 != NULL)
     691               0 :                         PQclear(poResult2);
     692                 : 
     693               0 :                     if (poResult != NULL)
     694               0 :                         PQclear(poResult);
     695                 : 
     696               0 :                     return false;
     697                 :                 }
     698                 : 
     699                 :                 /* Construct an OGR object with the raster extent */
     700               0 :                 pszExtent = PQgetvalue(poResult2, 0, 0);
     701                 : 
     702               0 :                 pszProjectionRef = (char*) GetProjectionRef();
     703               0 :                 poSR = new OGRSpatialReference(pszProjectionRef);
     704                 :                 OgrErr = OGRGeometryFactory::createFromWkt(&pszExtent,
     705               0 :                         poSR, &poGeom);
     706               0 :                 if (OgrErr != OGRERR_NONE) {
     707                 :                     CPLError(CE_Failure, CPLE_AppDefined,
     708               0 :                             "Couldn't calculate raster extent");
     709                 : 
     710               0 :                     if (poResult2)
     711               0 :                         PQclear(poResult2);
     712                 : 
     713               0 :                     if (poResult != NULL)
     714               0 :                         PQclear(poResult);
     715                 : 
     716               0 :                     return false;
     717                 :                 }
     718                 : 
     719               0 :                 poE = new OGREnvelope();
     720               0 :                 poGeom->getEnvelope(poE);
     721                 : 
     722                 :                 /* Correction for upper left y coord*/
     723                 : 
     724                 :                 /**
     725                 :                  * TODO: Review this. Is a good algorithm?
     726                 :                  * If the pixel size Y is negative, we can assume the raster's
     727                 :                  * reference system uses cartesian coordinates, in which the
     728                 :                  * origin is in lower-left corner, while the origin in an image
     729                 :                  * is un upper-left corner. In this case, the upper left Y value
     730                 :                  * will be MaxY from the envelope. Otherwise, it will be MinY.
     731                 :                  **/
     732                 :                 /*
     733                 :                 adfGeoTransform[0] = poE->MinX;
     734                 :                 if (adfGeoTransform[5] >= 0.0)
     735                 :                     adfGeoTransform[3] = poE->MinY;
     736                 :                 else
     737                 :                     adfGeoTransform[3] = poE->MaxY;
     738                 :                 */
     739                 : 
     740                 :                 /**
     741                 :                  * The raster size is the extent covered for all the raster's
     742                 :                  * columns
     743                 :                  **/
     744                 :                 nRasterXSize = (int)
     745               0 :                         fabs(rint((poE->MaxX - poE->MinX) / adfGeoTransform[1]));
     746                 :                 nRasterYSize = (int)
     747               0 :                         fabs(rint((poE->MaxY - poE->MinY) / adfGeoTransform[5]));
     748                 : 
     749                 : 
     750                 :                 /* Free resources */
     751               0 :                 OGRGeometryFactory::destroyGeometry(poGeom);
     752               0 :                 delete poE;
     753               0 :                 delete poSR;
     754               0 :                 PQclear(poResult2);
     755                 : 
     756               0 :                 bRetValue = true;
     757                 : 
     758                 :                 }
     759               0 :                 break;
     760                 : 
     761                 :                 /* TODO: take into account more cases, if applies */
     762                 :             default:
     763                 :                 {
     764                 :                 CPLError(CE_Failure, CPLE_AppDefined,
     765               0 :                         "Error, incorrect working mode");
     766                 : 
     767                 :                 /* This column is not used in this mode. */
     768               0 :                 if (pszIdColumn != NULL)
     769               0 :                     CPLFree(pszIdColumn);
     770                 : 
     771               0 :                 bRetValue = false;
     772                 :                 }
     773                 :         }
     774                 :     }
     775                 : 
     776                 :     CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
     777                 :             "adfGeoTransform = {%f, %f, %f, %f, %f,%f}", adfGeoTransform[0],
     778                 :             adfGeoTransform[1], adfGeoTransform[2], adfGeoTransform[3],
     779               0 :             adfGeoTransform[4], adfGeoTransform[5]);
     780                 : 
     781                 :     CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
     782               0 :             "Raster size = (%d, %d)", nRasterXSize, nRasterYSize);
     783                 : 
     784                 : 
     785                 :     CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
     786               0 :             "Block dimensions = (%d x %d)", nBlockXSize, nBlockYSize);
     787                 : 
     788               0 :     PQclear(poResult);
     789               0 :     return bRetValue;
     790                 : }
     791                 : 
     792                 : /*********************************************
     793                 :  * \brief Set raster bands for this dataset
     794                 :  *********************************************/
     795               0 : GBool PostGISRasterDataset::SetRasterBands() {
     796               0 :     GBool bSignedByte = false;
     797               0 :     int nBitDepth = 8;
     798               0 :     char* pszDataType = NULL;
     799               0 :     int iBand = 0;
     800               0 :     PGresult * poResult = NULL;
     801               0 :     CPLString osCommand;
     802               0 :     double dfNodata = 0.0;
     803               0 :     GDALDataType hDataType = GDT_Byte;
     804               0 :     int nTuples = 0;
     805               0 :     GBool bIsOffline = false;
     806               0 :     GBool bHasNoDataValue = false;
     807                 : 
     808                 :     /* Create each PostGISRasterRasterBand using the band metadata */
     809               0 :     for (iBand = 0; iBand < nBands; iBand++) {
     810                 :         /* Create query to fetch metadata from db */
     811               0 :         if (pszWhere == NULL) {
     812                 :             osCommand.Printf("select (foo.md).* from (select"
     813                 :                     " distinct st_bandmetadata( %s, %d) as md from %s. %s) as foo",
     814               0 :                     pszColumn, iBand + 1, pszSchema, pszTable);
     815                 :         } else {
     816                 : 
     817                 :             osCommand.Printf("select (foo.md).* from (select"
     818                 :                     " distinct st_bandmetadata( %s, %d) as md from %s. %s where %s) as foo",
     819               0 :                     pszColumn, iBand + 1, pszSchema, pszTable, pszWhere);
     820                 :         }
     821                 : 
     822               0 :         poResult = PQexec(poConn, osCommand.c_str());
     823               0 :         nTuples = PQntuples(poResult);
     824                 : 
     825                 :         /* Error getting info from database */
     826               0 :         if (poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK ||
     827                 :                 nTuples <= 0) {
     828                 :             CPLError(CE_Failure, CPLE_AppDefined,
     829                 :                     "Error getting band metadata: %s",
     830               0 :                     PQerrorMessage(poConn));
     831               0 :             if (poResult)
     832               0 :                 PQclear(poResult);
     833                 : 
     834               0 :             return false;
     835                 :         }
     836                 : 
     837                 :         /**
     838                 :          * If we have more than one record here is because there are several
     839                 :          * rows, belonging to the same raster coverage, with different band
     840                 :          * metadata values. An error must be raised.
     841                 :          *
     842                 :          * TODO: Is there any way to fix this problem?
     843                 :          *
     844                 :          * TODO: Even when the difference between metadata values are only a
     845                 :          * few decimal numbers (for example: 3.0000000 and 3.0000001) they're
     846                 :          * different tuples. And in that case, they must be the same
     847                 :          **/
     848                 :         /*
     849                 :         if (nTuples > 1) {
     850                 :             CPLError(CE_Failure, CPLE_AppDefined, "Error, the \
     851                 :                     ONE_RASTER_PER_TABLE mode can't be applied if the raster \
     852                 :                     rows don't have the same metadata for band %d",
     853                 :                     iBand + 1);
     854                 :             PQclear(poResult);
     855                 :             return false;
     856                 :         }
     857                 :          */
     858                 : 
     859                 : 
     860                 :         /* Get metadata and create raster band objects */
     861               0 :         pszDataType = CPLStrdup(PQgetvalue(poResult, 0, 0));
     862               0 :         bHasNoDataValue = EQUALN(PQgetvalue(poResult, 0, 1), "t", sizeof(char));
     863               0 :         dfNodata = atof(PQgetvalue(poResult, 0, 2));
     864               0 :         bIsOffline = EQUALN(PQgetvalue(poResult, 0, 3), "t", sizeof (char));
     865                 : 
     866               0 :         if (EQUALN(pszDataType, "1BB", 3 * sizeof (char))) {
     867               0 :             hDataType = GDT_Byte;
     868               0 :             nBitDepth = 1;
     869               0 :         } else if (EQUALN(pszDataType, "2BUI", 4 * sizeof (char))) {
     870               0 :             hDataType = GDT_Byte;
     871               0 :             nBitDepth = 2;
     872               0 :         } else if (EQUALN(pszDataType, "4BUI", 4 * sizeof (char))) {
     873               0 :             hDataType = GDT_Byte;
     874               0 :             nBitDepth = 4;
     875               0 :         } else if (EQUALN(pszDataType, "8BUI", 4 * sizeof (char))) {
     876               0 :             hDataType = GDT_Byte;
     877               0 :             nBitDepth = 8;
     878               0 :         } else if (EQUALN(pszDataType, "8BSI", 4 * sizeof (char))) {
     879               0 :             hDataType = GDT_Byte;
     880                 :             /**
     881                 :              * To indicate the unsigned byte values between 128 and 255
     882                 :              * should be interpreted as being values between -128 and -1 for
     883                 :              * applications that recognise the SIGNEDBYTE type.
     884                 :              **/
     885               0 :             bSignedByte = true;
     886               0 :             nBitDepth = 8;
     887               0 :         } else if (EQUALN(pszDataType, "16BSI", 5 * sizeof (char))) {
     888               0 :             hDataType = GDT_Int16;
     889               0 :             nBitDepth = 16;
     890               0 :         } else if (EQUALN(pszDataType, "16BUI", 5 * sizeof (char))) {
     891               0 :             hDataType = GDT_UInt16;
     892               0 :             nBitDepth = 16;
     893               0 :         } else if (EQUALN(pszDataType, "32BSI", 5 * sizeof (char))) {
     894               0 :             hDataType = GDT_Int32;
     895               0 :             nBitDepth = 32;
     896               0 :         } else if (EQUALN(pszDataType, "32BUI", 5 * sizeof (char))) {
     897               0 :             hDataType = GDT_UInt32;
     898               0 :             nBitDepth = 32;
     899               0 :         } else if (EQUALN(pszDataType, "32BF", 4 * sizeof (char))) {
     900               0 :             hDataType = GDT_Float32;
     901               0 :             nBitDepth = 32;
     902               0 :         } else if (EQUALN(pszDataType, "64BF", 4 * sizeof (char))) {
     903               0 :             hDataType = GDT_Float64;
     904               0 :             nBitDepth = 64;
     905                 :         } else {
     906               0 :             hDataType = GDT_Byte;
     907               0 :             nBitDepth = 8;
     908                 :         }
     909                 : 
     910                 :         /* Create raster band object */
     911                 :         SetBand(iBand + 1, new PostGISRasterRasterBand(this, iBand + 1, hDataType,
     912               0 :                 bHasNoDataValue, dfNodata, bSignedByte, nBitDepth, 0, bIsOffline));
     913                 : 
     914               0 :         CPLFree(pszDataType);
     915               0 :         PQclear(poResult);
     916                 :     }
     917                 : 
     918               0 :     return true;
     919                 : }
     920                 : 
     921                 : /**
     922                 :  * Read/write a region of image data from multiple bands.
     923                 :  *
     924                 :  * This method allows reading a region of one or more PostGISRasterBands from
     925                 :  * this dataset into a buffer. The write support is still under development
     926                 :  *
     927                 :  * The function fetches all the raster data that intersects with the region
     928                 :  * provided, and store the data in the GDAL cache.
     929                 :  *
     930                 :  * TODO: This only works in case of regular blocking rasters. A more
     931                 :  * general approach to allow non-regular blocking rasters is under development.
     932                 :  *
     933                 :  * It automatically takes care of data type translation if the data type
     934                 :  * (eBufType) of the buffer is different than that of the
     935                 :  * PostGISRasterRasterBand.
     936                 :  *
     937                 :  * TODO: The method should take care of image decimation / replication if the
     938                 :  * buffer size (nBufXSize x nBufYSize) is different than the size of the region
     939                 :  * being accessed (nXSize x nYSize).
     940                 :  *
     941                 :  * The nPixelSpace, nLineSpace and nBandSpace parameters allow reading into or
     942                 :  * writing from various organization of buffers.
     943                 :  *
     944                 :  * @param eRWFlag Either GF_Read to read a region of data, or GF_Write to write
     945                 :  * a region of data.
     946                 :  *
     947                 :  * @param nXOff The pixel offset to the top left corner of the region of the
     948                 :  * band to be accessed. This would be zero to start from the left side.
     949                 :  *
     950                 :  * @param nYOff The line offset to the top left corner of the region of the band
     951                 :  * to be accessed. This would be zero to start from the top.
     952                 :  *
     953                 :  * @param nXSize The width of the region of the band to be accessed in pixels.
     954                 :  *
     955                 :  * @param nYSize The height of the region of the band to be accessed in lines.
     956                 :  *
     957                 :  * @param pData The buffer into which the data should be read, or from which it
     958                 :  * should be written. This buffer must contain at least
     959                 :  * nBufXSize * nBufYSize * nBandCount words of type eBufType. It is organized in
     960                 :  * left to right,top to bottom pixel order. Spacing is controlled by the
     961                 :  * nPixelSpace, and nLineSpace parameters.
     962                 :  *
     963                 :  * @param nBufXSize the width of the buffer image into which the desired region
     964                 :  * is to be read, or from which it is to be written.
     965                 :  *
     966                 :  * @param nBufYSize the height of the buffer image into which the desired region
     967                 :  * is to be read, or from which it is to be written.
     968                 :  *
     969                 :  * @param eBufType the type of the pixel values in the pData data buffer. The
     970                 :  * pixel values will automatically be translated to/from the
     971                 :  * PostGISRasterRasterBand data type as needed.
     972                 :  *
     973                 :  * @param nBandCount the number of bands being read or written.
     974                 :  *
     975                 :  * @param panBandMap the list of nBandCount band numbers being read/written.
     976                 :  * Note band numbers are 1 based. This may be NULL to select the first
     977                 :  * nBandCount bands.
     978                 :  *
     979                 :  * @param nPixelSpace The byte offset from the start of one pixel value in pData
     980                 :  * to the start of the next pixel value within a scanline. If defaulted (0) the
     981                 :  * size of the datatype eBufType is used.
     982                 :  *
     983                 :  * @param nLineSpace The byte offset from the start of one scanline in pData to
     984                 :  * the start of the next. If defaulted (0) the size of the datatype
     985                 :  * eBufType * nBufXSize is used.
     986                 :  *
     987                 :  * @param nBandSpace the byte offset from the start of one bands data to the
     988                 :  * start of the next. If defaulted (0) the value will be nLineSpace * nBufYSize
     989                 :  * implying band sequential organization of the data buffer.
     990                 :  *
     991                 :  * @return CE_Failure if the access fails, otherwise CE_None.
     992                 :  */
     993               0 : CPLErr PostGISRasterDataset::IRasterIO(GDALRWFlag eRWFlag,
     994                 :         int nXOff, int nYOff, int nXSize, int nYSize,
     995                 :         void * pData, int nBufXSize, int nBufYSize,
     996                 :         GDALDataType eBufType,
     997                 :         int nBandCount, int *panBandMap,
     998                 :         int nPixelSpace, int nLineSpace, int nBandSpace)
     999                 : {
    1000                 :     double adfTransform[6];
    1001                 :     double adfProjWin[8];
    1002                 :     int ulx, uly, lrx, lry;
    1003               0 :     CPLString osCommand;
    1004               0 :     PGresult* poResult = NULL;
    1005               0 :     int nTuples = 0;
    1006               0 :     int iTuplesIndex = 0;
    1007               0 :     GByte* pbyData = NULL;
    1008               0 :     int nWKBLength = 0;
    1009                 :     int iBandIndex;
    1010               0 :     GDALRasterBlock * poBlock = NULL;
    1011                 :     int iBlockXOff, iBlockYOff;
    1012                 :     int nBandDataSize, nBandDataLength;
    1013               0 :     char * pBandData = NULL;
    1014               0 :     PostGISRasterRasterBand * poBand = NULL;
    1015               0 :     GByte * pabySrcBlock = NULL;
    1016                 :     int nBlocksPerRow, nBlocksPerColumn;
    1017                 :     char orderByY[4];
    1018                 :     char orderByX[3];
    1019                 : 
    1020                 : 
    1021                 :     /**
    1022                 :      * TODO: Write support not implemented yet
    1023                 :      **/
    1024               0 :     if (eRWFlag == GF_Write)
    1025                 :     {
    1026                 :         CPLError(CE_Failure, CPLE_NotSupported,
    1027               0 :                 "PostGIS Raster does not support writing");
    1028               0 :         return CE_Failure;
    1029                 :     }
    1030                 : 
    1031                 :     /**
    1032                 :      * TODO: Data decimation / replication needed
    1033                 :      */
    1034               0 :     if (nBufXSize != nXSize || nBufYSize != nYSize)
    1035                 :     {
    1036                 :         /**
    1037                 :          * This will cause individual IReadBlock calls
    1038                 :          *
    1039                 :          */
    1040                 :         return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    1041                 :                 pData, nBufXSize, nBufYSize, eBufType, nBandCount,
    1042               0 :                 panBandMap, nPixelSpace, nLineSpace, nBandSpace);
    1043                 :     }
    1044                 :         
    1045                 :     CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO: "
    1046                 :             "nBandSpace = %d, nLineSpace = %d, nPixelSpace = %d",
    1047               0 :             nBandSpace, nLineSpace, nPixelSpace);
    1048                 : 
    1049                 :     /**************************************************************************
    1050                 :      * In the first call, we fetch the data from database and store it as
    1051                 :      * 'blocks' in the cache.
    1052                 :      *
    1053                 :      * TODO: If the data is not cached, we must 'invent' a good block size, and
    1054                 :      * divide the data in blocks. To get a proper block size, we should rewrite
    1055                 :      * the GetBlockSize function at band level.
    1056                 :      ***************************************************************************/
    1057               0 :     if (!bBlocksCached)
    1058                 :     {
    1059                 :         CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO: "
    1060                 :                 "Buffer size = (%d, %d), Region size = (%d, %d)",
    1061               0 :                 nBufXSize, nBufYSize, nXSize, nYSize);
    1062                 : 
    1063                 :         /*******************************************************************
    1064                 :          * Construct a projected window to intersect the band data
    1065                 :          *******************************************************************/
    1066               0 :         GetGeoTransform(adfTransform);
    1067               0 :         ulx = nXOff;
    1068               0 :         uly = nYOff;
    1069               0 :         lrx = nXOff + nXSize;
    1070               0 :         lry = nYOff + nYSize;
    1071                 : 
    1072                 :         /* Calculate the intersection polygon */
    1073               0 :         adfProjWin[0] = adfTransform[0] +
    1074               0 :             ulx * adfTransform[1] +
    1075               0 :             uly * adfTransform[2];
    1076                 : 
    1077               0 :         adfProjWin[1] = adfTransform[3] +
    1078               0 :             ulx * adfTransform[4] +
    1079               0 :             uly * adfTransform[5];
    1080                 : 
    1081               0 :         adfProjWin[2] = adfTransform[0] +
    1082               0 :             lrx * adfTransform[1] +
    1083               0 :             uly * adfTransform[2];
    1084                 : 
    1085               0 :         adfProjWin[3] = adfTransform[3] +
    1086               0 :             lrx * adfTransform[4] +
    1087               0 :             uly * adfTransform[5];
    1088                 : 
    1089               0 :         adfProjWin[4] = adfTransform[0] +
    1090               0 :             lrx * adfTransform[1] +
    1091               0 :             lry * adfTransform[2];
    1092                 : 
    1093               0 :         adfProjWin[5] = adfTransform[3] +
    1094               0 :             lrx * adfTransform[4] +
    1095               0 :             lry * adfTransform[5];
    1096                 : 
    1097               0 :         adfProjWin[6] = adfTransform[0] +
    1098               0 :             ulx * adfTransform[1] +
    1099               0 :             lry * adfTransform[2];
    1100                 : 
    1101               0 :         adfProjWin[7] = adfTransform[3] +
    1102               0 :             ulx * adfTransform[4] +
    1103               0 :             lry * adfTransform[5];
    1104                 : 
    1105                 : 
    1106                 :         /* Construct order by for the query */
    1107               0 :         memset(orderByX, 0, 3);
    1108               0 :         memset(orderByY, 0, 4);
    1109                 : 
    1110               0 :         strcpy(orderByX, "asc");
    1111               0 :         if (nSrid == -1)
    1112               0 :             strcpy(orderByY, "asc"); // Y starts at 0 and grows
    1113                 :         else
    1114               0 :             strcpy(orderByY, "desc");// Y starts at max and decreases
    1115                 :  
    1116                 : 
    1117                 :         /*********************************************************************
    1118                 :         * We first get the data from database (ordered from upper left pixel
    1119                 :         * to lower right one)
    1120                 :         *********************************************************************/
    1121               0 :         if (pszWhere == NULL)
    1122                 :         {
    1123                 :             osCommand.Printf("SELECT %s, ST_ScaleX(%s), ST_SkewY(%s), "
    1124                 :                 "ST_SkewX(%s), ST_ScaleY(%s), ST_UpperLeftX(%s), "
    1125                 :                 "ST_UpperLeftY(%s), ST_Width(%s), ST_Height(%s) FROM %s.%s WHERE "
    1126                 :                 "ST_Intersects(%s, ST_PolygonFromText('POLYGON((%f %f, %f %f, %f %f"
    1127                 :                 ", %f %f, %f %f))', %d)) ORDER BY ST_UpperLeftY(%s) %s, "
    1128                 :                 "ST_UpperLeftX(%s) %s", pszColumn, pszColumn,
    1129                 :                 pszColumn, pszColumn, pszColumn, pszColumn, pszColumn, pszColumn,
    1130                 :                 pszColumn, pszSchema, pszTable, pszColumn, adfProjWin[0],
    1131                 :                 adfProjWin[1], adfProjWin[2], adfProjWin[3], adfProjWin[4],
    1132                 :                 adfProjWin[5], adfProjWin[6], adfProjWin[7], adfProjWin[0],
    1133               0 :                 adfProjWin[1], nSrid, pszColumn, orderByY, pszColumn, orderByX);
    1134                 :         }
    1135                 : 
    1136                 : 
    1137                 :         else
    1138                 :         {
    1139                 :             osCommand.Printf("SELECT %s ST_ScaleX(%s), ST_SkewY(%s), "
    1140                 :                 "ST_SkewX(%s), ST_ScaleY(%s), ST_UpperLeftX(%s), "
    1141                 :                 "ST_UpperLeftY(%s), ST_Width(%s), ST_Height(%s) FROM %s.%s WHERE %s AND "
    1142                 :                 "ST_Intersects(%s, ST_PolygonFromText('POLYGON((%f %f, %f %f, %f %f"
    1143                 :                 ", %f %f, %f %f))', %d)) ORDER BY ST_UpperLeftY(%s) %s, "
    1144                 :                 "ST_UpperLeftX(%s) %s", pszColumn, pszColumn,
    1145                 :                 pszColumn, pszColumn, pszColumn, pszColumn, pszColumn, pszColumn,
    1146                 :                 pszColumn,pszSchema, pszTable, pszWhere, pszColumn, adfProjWin[0],
    1147                 :                 adfProjWin[1], adfProjWin[2], adfProjWin[3], adfProjWin[4],
    1148                 :                 adfProjWin[5], adfProjWin[6], adfProjWin[7], adfProjWin[0],
    1149               0 :                 adfProjWin[1], nSrid, pszColumn, orderByY, pszColumn, orderByX);
    1150                 :         }
    1151                 : 
    1152                 :         CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): Query = %s",
    1153               0 :             osCommand.c_str());
    1154                 : 
    1155               0 :         poResult = PQexec(poConn, osCommand.c_str());
    1156               0 :         if (poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK ||
    1157                 :             PQntuples(poResult) <= 0)
    1158                 :         {
    1159               0 :             if (poResult)
    1160               0 :                 PQclear(poResult);
    1161                 : 
    1162                 :             /**
    1163                 :              * This will cause individual IReadBlock calls
    1164                 :              *
    1165                 :              */
    1166                 :             return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    1167                 :                     pData, nBufXSize, nBufYSize, eBufType, nBandCount,
    1168               0 :                     panBandMap, nPixelSpace, nLineSpace, nBandSpace);
    1169                 :         }
    1170                 : 
    1171                 :         /**
    1172                 :          * NOTE: In case of any of the raster columns have different SRID, the
    1173                 :          * query will fail. So, if we don't fail, we can assume all the rows
    1174                 :          * have the same SRID. We don't need to check it
    1175                 :          **/
    1176                 : 
    1177                 : 
    1178               0 :         nTuples = PQntuples(poResult);
    1179                 :         CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): nTuples = %d",
    1180               0 :             nTuples);
    1181                 : 
    1182                 :         CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): "
    1183               0 :             "Raster size = (%d, %d)", nRasterXSize, nRasterYSize);
    1184                 : 
    1185                 : 
    1186                 : 
    1187                 :         /**************************************************************************
    1188                 :          * This is the simplest case: all the rows have the same dimensions
    1189                 :          * (regularly blocked raster)
    1190                 :          *
    1191                 :          * Now we'll cache each tuple as a data block. More accurately, we must
    1192                 :          * access each tuple, get the band data, and store this data as a block. So,
    1193                 :          * each tuple contains the data for nBands blocks (and nBandCount <=
    1194                 :          * nBands)
    1195                 :          *************************************************************************/
    1196               0 :         for(iBandIndex = 0; iBandIndex < nBandCount; iBandIndex++)
    1197                 :         {
    1198               0 :             poBand = (PostGISRasterRasterBand *)GetRasterBand(iBandIndex + 1);
    1199                 : 
    1200               0 :             nBandDataSize = GDALGetDataTypeSize(poBand->eDataType) / 8;
    1201                 : 
    1202                 :             nBandDataLength = poBand->nBlockXSize * poBand->nBlockYSize *
    1203               0 :                 nBandDataSize;
    1204                 : 
    1205                 :             CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): "
    1206                 :                 "Block size (%d, %d) for band %d", poBand->nBlockXSize,
    1207               0 :                 poBand->nBlockYSize, poBand->nBand);
    1208                 : 
    1209                 :             /* Enables block caching, if it wasn't enabled */
    1210               0 :             if (!poBand->InitBlockInfo())
    1211               0 :                 continue;
    1212                 : 
    1213                 :             /**
    1214                 :              * This can be different from poBand->nBlocksPerRow and
    1215                 :              * poBand->nBlocksPerColumn, if the region size is different than
    1216                 :              * the raster size. So, we calculate these values for this case.
    1217                 :              */
    1218                 :             nBlocksPerRow =
    1219               0 :                     (nXSize + poBand->nBlockXSize - 1) / poBand->nBlockXSize;
    1220                 : 
    1221                 :             nBlocksPerColumn =
    1222               0 :                     (nYSize + poBand->nBlockYSize - 1) / poBand->nBlockYSize;
    1223                 : 
    1224                 :             CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): "
    1225               0 :                 "Number of blocks: %dx%d", nBlocksPerRow, nBlocksPerColumn);
    1226                 : 
    1227               0 :             for(iBlockYOff = 0; iBlockYOff < nBlocksPerColumn;
    1228                 :                     iBlockYOff++)
    1229                 :             {
    1230               0 :                 for(iBlockXOff = 0; iBlockXOff < nBlocksPerRow;
    1231                 :                         iBlockXOff++)
    1232                 :                 {
    1233                 :                     iTuplesIndex = (iBlockYOff * nBlocksPerRow) +
    1234               0 :                         iBlockXOff;
    1235                 : 
    1236                 :                     CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): "
    1237                 :                             "iBlockXOff = %d, iBlockYOff = %d, "
    1238                 :                             "iTuplesIndex = %d", iBlockXOff, iBlockYOff,
    1239               0 :                             iTuplesIndex);
    1240                 : 
    1241                 :                     pbyData = CPLHexToBinary(PQgetvalue(poResult, iTuplesIndex,
    1242               0 :                             1), &nWKBLength);
    1243                 : 
    1244                 :                     pBandData = (char *)GET_BAND_DATA(pbyData, poBand->nBand,
    1245               0 :                             nBandDataSize, nBandDataLength);
    1246                 : 
    1247                 :                     CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): "
    1248                 :                         "Block data length for band %d: %d", poBand->nBand,
    1249               0 :                         nBandDataLength);
    1250                 : 
    1251                 :                     CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): "
    1252               0 :                         "Block (%d, %d)", iBlockXOff, iBlockYOff);
    1253                 : 
    1254                 :                     /* Create a new block */
    1255                 :                     poBlock = new GDALRasterBlock(poBand, iBlockXOff,
    1256               0 :                             iBlockYOff);
    1257                 : 
    1258               0 :                     poBlock->AddLock();
    1259                 : 
    1260                 :                     /* Allocate data space */
    1261               0 :                     if (poBlock->Internalize() != CE_None)
    1262                 :                     {
    1263               0 :                         poBlock->DropLock();
    1264               0 :                         delete poBlock;
    1265               0 :                         continue;
    1266                 :                     }
    1267                 : 
    1268                 :                     /* Add the block to the block matrix */
    1269               0 :                     if (poBand->AdoptBlock(iBlockXOff, iBlockYOff, poBlock) !=
    1270                 :                             CE_None)
    1271                 :                     {
    1272               0 :                         poBlock->DropLock();
    1273               0 :                         delete poBlock;
    1274               0 :                         continue;
    1275                 :                     }
    1276                 : 
    1277                 :                     /**
    1278                 :                      * Copy data to block
    1279                 :                      *
    1280                 :                      * TODO: Enable write mode too (mark the block as dirty and
    1281                 :                      * create IWriteBlock in PostGISRasterRasterBand)
    1282                 :                      */
    1283               0 :                     pabySrcBlock = (GByte *)poBlock->GetDataRef();
    1284                 : 
    1285               0 :                     if (poBand->eDataType == eBufType)
    1286                 :                     {
    1287               0 :                         memcpy(pabySrcBlock, pBandData, nBandDataLength);
    1288                 :                     }
    1289                 : 
    1290                 :                     /**
    1291                 :                      * As in GDALDataset class... expensive way of handling
    1292                 :                      * single words
    1293                 :                      */
    1294                 :                     else
    1295                 :                     {
    1296                 :                         GDALCopyWords(pBandData, poBand->eDataType, 0,
    1297               0 :                                 pabySrcBlock, eBufType, 0, 1);
    1298                 :                     }
    1299                 : 
    1300               0 :                     poBlock->DropLock();
    1301                 : 
    1302               0 :                     CPLFree(pbyData);
    1303               0 :                     pbyData = NULL;
    1304                 :                 }
    1305                 : 
    1306                 :             }
    1307                 : 
    1308                 :         }
    1309                 : 
    1310               0 :         PQclear(poResult);
    1311               0 :         bBlocksCached = true;
    1312                 :     }
    1313                 : 
    1314                 :     /* Once the blocks are cached, we delegate in GDAL I/O system */
    1315                 :     return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
    1316                 :             nBufXSize, nBufYSize, eBufType, nBandCount, panBandMap, nPixelSpace,
    1317               0 :             nLineSpace, nBandSpace);
    1318                 : }
    1319                 : 
    1320                 : /******************************************************************************
    1321                 :  * \brief Get the connection information for a filename.
    1322                 :  ******************************************************************************/
    1323                 : static GBool
    1324               2 : GetConnectionInfo(const char * pszFilename, 
    1325                 :     char ** ppszConnectionString, char ** ppszSchema, char ** ppszTable, 
    1326                 :     char ** ppszColumn, char ** ppszWhere, char ** ppszHost, 
    1327                 :     char ** ppszPort, char ** ppszUser, char ** ppszPassword, 
    1328                 :     int * nMode, GBool * bBrowseDatabase)
    1329                 : {
    1330               2 :     int nPos = -1, i;
    1331               2 :     char * pszTmp = NULL;
    1332               2 :     char **papszParams = ParseConnectionString(pszFilename);
    1333               2 :     if (papszParams == NULL) {
    1334               0 :         return false;
    1335                 :     }
    1336                 : 
    1337                 :     /**************************************************************************
    1338                 :      * Get mode:
    1339                 :      *  - 1. ONE_RASTER_PER_ROW: Each row is considered as a separate raster
    1340                 :      *  - 2. ONE_RASTER_PER_TABLE: All the table rows are considered as a whole
    1341                 :      *      raster coverage
    1342                 :      **************************************************************************/
    1343               2 :     nPos = CSLFindName(papszParams, "mode");
    1344               2 :     if (nPos != -1) {
    1345               0 :         *nMode = atoi(CPLParseNameValue(papszParams[nPos], NULL));
    1346                 : 
    1347               0 :         if (*nMode != ONE_RASTER_PER_ROW && *nMode != ONE_RASTER_PER_TABLE) {
    1348                 :             /* Unrecognized mode, using default one */
    1349                 :             /*
    1350                 :             CPLError(CE_Warning, CPLE_AppDefined, "Undefined working mode (%d)."
    1351                 :                     " Valid working modes are 1 (ONE_RASTER_PER_ROW) and 2"
    1352                 :                     " (ONE_RASTER_PER_TABLE). Using ONE_RASTER_PER_TABLE"
    1353                 :                     " by default", nMode);
    1354                 :              */
    1355               0 :             *nMode = ONE_RASTER_PER_ROW;
    1356                 :         }
    1357                 : 
    1358                 :         /* Remove the mode from connection string */
    1359               0 :         papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
    1360                 :     }
    1361                 :         /* Default mode */
    1362                 :     else
    1363               2 :         *nMode = ONE_RASTER_PER_ROW;
    1364                 : 
    1365                 :     /**
    1366                 :      * Case 1: There's no database name: Error, you need, at least,
    1367                 :      * specify a database name (NOTE: insensitive search)
    1368                 :      **/
    1369               2 :     nPos = CSLFindName(papszParams, "dbname");
    1370               2 :     if (nPos == -1) {
    1371                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1372               0 :                 "You must specify at least a db name");
    1373                 : 
    1374               0 :         CSLDestroy(papszParams);
    1375                 : 
    1376               0 :         return false;
    1377                 :     }
    1378                 : 
    1379                 :     /**
    1380                 :      * Case 2: There's database name, but no table name: activate a flag
    1381                 :      * for browsing the database, fetching all the schemas that contain
    1382                 :      * raster tables
    1383                 :      **/
    1384               2 :     nPos = CSLFindName(papszParams, "table");
    1385               2 :     if (nPos == -1) {
    1386               0 :         *bBrowseDatabase = true;
    1387                 : 
    1388                 :         /* Get schema name, if exist */
    1389               0 :         nPos = CSLFindName(papszParams, "schema");
    1390               0 :         if (nPos != -1) {
    1391               0 :             *ppszSchema = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
    1392                 :             /* Delete this pair from params array */
    1393               0 :             papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
    1394                 :         }
    1395                 : 
    1396                 :         /**
    1397                 :          * Remove the rest of the parameters, if exist (they mustn't be present
    1398                 :          * if we want a valid PQ connection string)
    1399                 :          **/
    1400               0 :         nPos = CSLFindName(papszParams, "column");
    1401               0 :         if (nPos != -1) {
    1402                 :             /* Delete this pair from params array */
    1403               0 :             papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
    1404                 :         }
    1405                 : 
    1406               0 :         nPos = CSLFindName(papszParams, "where");
    1407               0 :         if (nPos != -1) {
    1408                 :             /* Delete this pair from params array */
    1409               0 :             papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
    1410                 :         }
    1411                 :     } else {
    1412               2 :         *ppszTable = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
    1413                 :         /* Delete this pair from params array */
    1414               2 :         papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
    1415                 : 
    1416                 :         /**
    1417                 :          * Case 3: There's database and table name, but no column
    1418                 :          * name: Use a default column name and use the table to create the
    1419                 :          * dataset
    1420                 :          **/
    1421               2 :         nPos = CSLFindName(papszParams, "column");
    1422               2 :         if (nPos == -1) {
    1423               2 :             *ppszColumn = CPLStrdup(DEFAULT_COLUMN);
    1424                 :         }
    1425                 :         /**
    1426                 :          * Case 4: There's database, table and column name: Use the table to
    1427                 :          * create a dataset
    1428                 :          **/
    1429                 :         else {
    1430               0 :             *ppszColumn = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
    1431                 :             /* Delete this pair from params array */
    1432               0 :             papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
    1433                 :         }
    1434                 : 
    1435                 :         /* Get the rest of the parameters, if exist */
    1436               2 :         nPos = CSLFindName(papszParams, "schema");
    1437               2 :         if (nPos == -1) {
    1438               0 :             *ppszSchema = CPLStrdup(DEFAULT_SCHEMA);
    1439                 :         } else {
    1440               2 :             *ppszSchema = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
    1441                 :             /* Delete this pair from params array */
    1442               2 :             papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
    1443                 :         }
    1444                 : 
    1445               2 :         nPos = CSLFindName(papszParams, "where");
    1446               2 :         if (nPos != -1) {
    1447               0 :             *ppszWhere = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
    1448                 :             /* Delete this pair from params array */
    1449               0 :             papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
    1450                 :         }
    1451                 :     }
    1452                 : 
    1453                 :     /* Parse ppszWhere, if needed */
    1454               2 :     if (*ppszWhere) {
    1455               0 :         pszTmp = ReplaceQuotes(*ppszWhere, strlen(*ppszWhere));
    1456               0 :         CPLFree(*ppszWhere);
    1457               0 :         *ppszWhere = pszTmp;
    1458                 :     }
    1459                 : 
    1460                 :     /***************************************
    1461                 :      * Construct a valid connection string
    1462                 :      ***************************************/
    1463                 :     *ppszConnectionString = (char*) CPLCalloc(strlen(pszFilename),
    1464               2 :             sizeof (char));
    1465              10 :     for (i = 0; i < CSLCount(papszParams); i++) {
    1466               8 :         *ppszConnectionString = strncat(*ppszConnectionString, papszParams[i], strlen(papszParams[i]));
    1467               8 :         *ppszConnectionString = strncat(*ppszConnectionString, " ", strlen(" "));
    1468                 :     }
    1469                 : 
    1470               2 :     nPos = CSLFindName(papszParams, "host");
    1471               2 :     if (nPos != -1) {
    1472               2 :         *ppszHost = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
    1473                 :     }
    1474               0 :     else if (getenv("PGHOST") != NULL) {
    1475               0 :         *ppszHost = CPLStrdup(getenv("PGHOST"));
    1476                 :     }
    1477                 :     else {
    1478                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1479                 :                 "Host parameter must be provided, or PGHOST environment "
    1480               0 :                 "variable must be set. Please set the host and try again.");
    1481                 : 
    1482               0 :         CSLDestroy(papszParams);
    1483                 : 
    1484               0 :         return false;
    1485                 :     }
    1486                 : 
    1487               2 :     nPos = CSLFindName(papszParams, "port");
    1488               2 :     if (nPos != -1) {
    1489               0 :         *ppszPort = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
    1490                 :     }
    1491               2 :     else if (getenv("PGPORT") != NULL ) {
    1492               0 :         *ppszPort = CPLStrdup(getenv("PGPORT"));
    1493                 :     }
    1494                 :     else {
    1495                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1496                 :                 "Port parameter must be provided, or PGPORT environment "
    1497               2 :                 "variable must be set. Please set the port and try again.");
    1498                 : 
    1499               2 :         CSLDestroy(papszParams);
    1500                 : 
    1501               2 :         return false;
    1502                 :     }
    1503                 : 
    1504               0 :     nPos = CSLFindName(papszParams, "user");
    1505               0 :     if (nPos != -1) {
    1506               0 :         *ppszUser = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
    1507                 :     }
    1508               0 :     else if (getenv("PGUSER") != NULL ) {
    1509               0 :         *ppszUser = CPLStrdup(getenv("PGUSER"));
    1510                 :     }
    1511                 :     else {
    1512                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1513                 :                 "User parameter must be provided, or PGUSER environment "
    1514               0 :                 "variable must be set. Please set the user and try again.");
    1515                 : 
    1516               0 :         CSLDestroy(papszParams);
    1517                 : 
    1518               0 :         return false;
    1519                 :     }
    1520                 : 
    1521               0 :     nPos = CSLFindName(papszParams, "password");
    1522               0 :     if (nPos != -1) {
    1523               0 :         *ppszPassword = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
    1524                 :     }
    1525                 :     else {
    1526                 :         // if PGPASSWORD is not set, ppszPassword is set to an empty string.
    1527                 :         // this is okay, since there may be configurations in pg_hba.conf
    1528                 :         // that don't require any password to connect
    1529               0 :         *ppszPassword = CPLStrdup(getenv("PGPASSWORD"));
    1530                 :     }
    1531                 : 
    1532               0 :     CSLDestroy(papszParams);
    1533                 : 
    1534                 :     CPLDebug("PostGIS_Raster", "PostGISRasterDataset::GetConnectionInfo(): "
    1535                 :         "Mode: %d\nSchema: %s\nTable: %s\nColumn: %s\nWhere: %s\n"
    1536                 :         "Host: %s\nPort: %s\nUser: %s\nPassword: %s\nConnection String: %s", 
    1537                 :         *nMode, *ppszSchema, *ppszTable, *ppszColumn, 
    1538               0 :         *ppszWhere, *ppszHost, *ppszPort, *ppszUser, *ppszPassword, *ppszConnectionString);
    1539                 : 
    1540               0 :     return true;
    1541                 : }
    1542                 : 
    1543                 : /******************************************************************************
    1544                 :  * \brief Create a connection to a postgres database
    1545                 :  ******************************************************************************/
    1546                 : static PGconn *
    1547               2 : GetConnection(const char * pszFilename, char ** ppszConnectionString,
    1548                 :     char ** ppszSchema, char ** ppszTable, char ** ppszColumn, char ** ppszWhere, 
    1549                 :     int * nMode, GBool * bBrowseDatabase) 
    1550                 : {
    1551                 :     PostGISRasterDriver * poDriver;
    1552               2 :     PGconn * poConn = NULL;
    1553               2 :     char * pszHost = NULL;
    1554               2 :     char * pszPort = NULL;
    1555               2 :     char * pszUser = NULL;
    1556               2 :     char * pszPassword = NULL;
    1557                 : 
    1558               2 :     if (GetConnectionInfo(pszFilename, ppszConnectionString, ppszSchema, 
    1559                 :         ppszTable, ppszColumn, ppszWhere, &pszHost, &pszPort, &pszUser, 
    1560                 :         &pszPassword, nMode, bBrowseDatabase)) 
    1561                 :     {
    1562                 :         /********************************************************************
    1563                 :          * Open a new database connection
    1564                 :          ********************************************************************/
    1565               0 :         poDriver = (PostGISRasterDriver *)GDALGetDriverByName("PostGISRaster");
    1566                 :         poConn = poDriver->GetConnection(*ppszConnectionString,
    1567               0 :                 pszHost, pszPort, pszUser, pszPassword);
    1568                 : 
    1569               0 :         if (poConn == NULL) {
    1570                 :             CPLError(CE_Failure, CPLE_AppDefined,
    1571               0 :                     "Couldn't establish a database connection");
    1572                 :         }
    1573                 :     }
    1574                 : 
    1575               2 :     CPLFree(pszHost);
    1576               2 :     CPLFree(pszPort);
    1577               2 :     CPLFree(pszUser);
    1578               2 :     CPLFree(pszPassword);
    1579                 : 
    1580               2 :     return poConn;
    1581                 : }
    1582                 : 
    1583                 : 
    1584                 : /******************************************************************************
    1585                 :  * \brief Open a connection with PostgreSQL. The connection string will have
    1586                 :  * the PostgreSQL accepted format, plus the next key=value pairs:
    1587                 :  *  schema = <schema_name>
    1588                 :  *  table = <table_name>
    1589                 :  *  column = <column_name>
    1590                 :  *  where = <SQL where>
    1591                 :  *  mode = <working mode> (1 or 2)
    1592                 :  *
    1593                 :  * These pairs are used for selecting the right raster table.
    1594                 :  *****************************************************************************/
    1595           22110 : GDALDataset* PostGISRasterDataset::Open(GDALOpenInfo* poOpenInfo) {
    1596           22110 :     char* pszConnectionString = NULL;
    1597           22110 :     char* pszSchema = NULL;
    1598           22110 :     char* pszTable = NULL;
    1599           22110 :     char* pszColumn = NULL;
    1600           22110 :     char* pszWhere = NULL;
    1601           22110 :     int nMode = -1;
    1602           22110 :     PGconn * poConn = NULL;
    1603           22110 :     PostGISRasterDataset* poDS = NULL;
    1604           22110 :     GBool bBrowseDatabase = false;
    1605           22110 :     PGresult * poResult = NULL;
    1606           22110 :     CPLString osCommand;
    1607                 : 
    1608                 :     /**************************
    1609                 :      * Check input parameter
    1610                 :      **************************/
    1611           22110 :     if (poOpenInfo->pszFilename == NULL ||
    1612                 :             poOpenInfo->fp != NULL ||
    1613                 :             !EQUALN(poOpenInfo->pszFilename, "PG:", 3))
    1614                 :     {
    1615                 :         /**
    1616                 :          * Drivers must quietly return NULL if the passed file is not of
    1617                 :          * their format. They should only produce an error if the file
    1618                 :          * does appear to be of their supported format, but for some
    1619                 :          * reason, unsupported or corrupt
    1620                 :          */
    1621           22108 :         return NULL;
    1622                 :     }
    1623                 : 
    1624                 :     poConn = GetConnection((char *)poOpenInfo->pszFilename,
    1625                 :         &pszConnectionString, &pszSchema, &pszTable, &pszColumn, &pszWhere,
    1626               2 :         &nMode, &bBrowseDatabase);
    1627               2 :     if (poConn == NULL) {
    1628               2 :         CPLFree(pszConnectionString);
    1629               2 :         CPLFree(pszSchema);
    1630               2 :         CPLFree(pszTable);
    1631               2 :         CPLFree(pszColumn);
    1632               2 :         CPLFree(pszWhere);
    1633               2 :         return NULL;
    1634                 :     }
    1635                 : 
    1636                 :     /* Check geometry type existence */
    1637               0 :     poResult = PQexec(poConn, "SELECT oid FROM pg_type WHERE typname = 'geometry'");
    1638               0 :     if (
    1639                 :             poResult == NULL ||
    1640                 :             PQresultStatus(poResult) != PGRES_TUPLES_OK ||
    1641                 :             PQntuples(poResult) <= 0
    1642                 :             ) {
    1643                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1644                 :                 "Error checking geometry type existence. Is PostGIS correctly "
    1645               0 :                 "installed?: %s", PQerrorMessage(poConn));
    1646               0 :         if (poResult != NULL)
    1647               0 :             PQclear(poResult);
    1648               0 :         if (pszSchema)
    1649               0 :             CPLFree(pszSchema);
    1650               0 :         if (pszTable)
    1651               0 :             CPLFree(pszTable);
    1652               0 :         if (pszColumn)
    1653               0 :             CPLFree(pszColumn);
    1654               0 :         if (pszWhere)
    1655               0 :             CPLFree(pszWhere);
    1656                 : 
    1657               0 :         return NULL;
    1658                 :     }
    1659                 : 
    1660               0 :     PQclear(poResult);
    1661                 : 
    1662                 : 
    1663                 :     /* Check spatial tables existence */
    1664                 :     poResult = PQexec(poConn, "select pg_namespace.nspname as schemaname, "
    1665                 :                     "pg_class.relname as tablename from pg_class, "
    1666                 :                     "pg_namespace where pg_class.relnamespace = pg_namespace.oid "
    1667                 :                     "and (pg_class.relname='raster_columns' or "
    1668                 :                     "pg_class.relname='raster_overviews' or "
    1669                 :                     "pg_class.relname='geometry_columns' or "
    1670               0 :                     "pg_class.relname='spatial_ref_sys')");
    1671               0 :     if (
    1672                 :             poResult == NULL ||
    1673                 :             PQresultStatus(poResult) != PGRES_TUPLES_OK ||
    1674                 :             PQntuples(poResult) <= 0
    1675                 :             ) {
    1676                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1677                 :                 "Error checking needed tables existence: %s",
    1678               0 :                 PQerrorMessage(poConn));
    1679               0 :         if (poResult != NULL)
    1680               0 :             PQclear(poResult);
    1681               0 :         if (pszSchema)
    1682               0 :             CPLFree(pszSchema);
    1683               0 :         if (pszTable)
    1684               0 :             CPLFree(pszTable);
    1685               0 :         if (pszColumn)
    1686               0 :             CPLFree(pszColumn);
    1687               0 :         if (pszWhere)
    1688               0 :             CPLFree(pszWhere);
    1689                 : 
    1690               0 :         return NULL;
    1691                 : 
    1692                 :     }
    1693                 : 
    1694               0 :     PQclear(poResult);
    1695                 : 
    1696                 :     /*************************************************************************
    1697                 :      * No table will be read. Only shows information about the existent raster
    1698                 :      * tables
    1699                 :      *************************************************************************/
    1700               0 :     if (bBrowseDatabase) {
    1701                 :         /**
    1702                 :          * Creates empty dataset object, only for subdatasets
    1703                 :          **/
    1704               0 :         poDS = new PostGISRasterDataset();
    1705               0 :         poDS->poConn = poConn;
    1706               0 :         poDS->eAccess = GA_ReadOnly;
    1707                 :         //poDS->poDriver = poDriver;
    1708               0 :         poDS->nMode = (pszSchema) ? BROWSE_SCHEMA : BROWSE_DATABASE;
    1709               0 :         poDS->nRasterXSize = 0;
    1710               0 :         poDS->nRasterYSize = 0;
    1711               0 :         poDS->adfGeoTransform[0] = 0.0;
    1712               0 :         poDS->adfGeoTransform[1] = 0.0;
    1713               0 :         poDS->adfGeoTransform[2] = 0.0;
    1714               0 :         poDS->adfGeoTransform[3] = 0.0;
    1715               0 :         poDS->adfGeoTransform[4] = 0.0;
    1716               0 :         poDS->adfGeoTransform[5] = 0.0;
    1717                 : 
    1718                 :         /**
    1719                 :          * Look for raster tables at database and
    1720                 :          * store them as subdatasets
    1721                 :          **/
    1722               0 :         if (!poDS->BrowseDatabase(pszSchema, pszConnectionString)) {
    1723               0 :             CPLFree(pszConnectionString);
    1724               0 :             delete poDS;
    1725                 : 
    1726               0 :             if (pszSchema)
    1727               0 :                 CPLFree(pszSchema);
    1728               0 :             if (pszTable)
    1729               0 :                 CPLFree(pszTable);
    1730               0 :             if (pszColumn)
    1731               0 :                 CPLFree(pszColumn);
    1732               0 :             if (pszWhere)
    1733               0 :                 CPLFree(pszWhere);
    1734                 : 
    1735               0 :             return NULL;
    1736                 :         }
    1737                 : 
    1738               0 :         if (pszSchema)
    1739               0 :             CPLFree(pszSchema);
    1740               0 :         if (pszTable)
    1741               0 :             CPLFree(pszTable);
    1742               0 :         if (pszColumn)
    1743               0 :             CPLFree(pszColumn);
    1744               0 :         if (pszWhere)
    1745               0 :             CPLFree(pszWhere);
    1746                 :     }
    1747                 :         /***********************************************************************
    1748                 :          * A table will be read: Fetch raster properties from db. Pay attention
    1749                 :          * to the block size: if the raster is blocked at database, the block
    1750                 :          * size can be fetched from each block size, if regular blocking table
    1751                 :          **********************************************************************/
    1752                 :     else {
    1753               0 :         poDS = new PostGISRasterDataset();
    1754               0 :         poDS->poConn = poConn;
    1755               0 :         poDS->eAccess = poOpenInfo->eAccess;
    1756               0 :         poDS->nMode = nMode;
    1757                 :         //poDS->poDriver = poDriver;
    1758                 : 
    1759               0 :         poDS->pszSchema = pszSchema;
    1760               0 :         poDS->pszTable = pszTable;
    1761               0 :         poDS->pszColumn = pszColumn;
    1762               0 :         poDS->pszWhere = pszWhere;
    1763                 : 
    1764                 :         /**
    1765                 :          * Fetch basic raster metadata from db
    1766                 :          **/
    1767                 : 
    1768               0 :         if (!poDS->SetRasterProperties(pszConnectionString)) {
    1769               0 :             CPLFree(pszConnectionString);
    1770               0 :             delete poDS;
    1771               0 :             return NULL;
    1772                 :         }
    1773                 : 
    1774                 :         /* Set raster bands */
    1775               0 :         if (!poDS->SetRasterBands()) {
    1776               0 :             CPLFree(pszConnectionString);
    1777               0 :             delete poDS;
    1778               0 :             return NULL;
    1779                 :         }
    1780                 : 
    1781                 :     }
    1782                 : 
    1783               0 :     CPLFree(pszConnectionString);
    1784               0 :     return poDS;
    1785                 : 
    1786                 : }
    1787                 : 
    1788                 : /*****************************************
    1789                 :  * \brief Get Metadata from raster
    1790                 :  * TODO: Add more options (the result of
    1791                 :  * calling ST_Metadata, for example)
    1792                 :  *****************************************/
    1793               0 : char** PostGISRasterDataset::GetMetadata(const char *pszDomain) {
    1794               0 :     if (pszDomain != NULL && EQUALN(pszDomain, "SUBDATASETS", 11))
    1795               0 :         return papszSubdatasets;
    1796                 :     else
    1797               0 :         return GDALDataset::GetMetadata(pszDomain);
    1798                 : }
    1799                 : 
    1800                 : /*****************************************************
    1801                 :  * \brief Fetch the projection definition string
    1802                 :  * for this dataset in OpenGIS WKT format. It should
    1803                 :  * be suitable for use with the OGRSpatialReference
    1804                 :  * class.
    1805                 :  *****************************************************/
    1806               0 : const char* PostGISRasterDataset::GetProjectionRef() {
    1807               0 :     CPLString osCommand;
    1808                 :     PGresult* poResult;
    1809                 : 
    1810               0 :     if (nSrid == -1)
    1811               0 :         return "";
    1812                 : 
    1813               0 :     if (pszProjection)
    1814               0 :         return pszProjection;
    1815                 : 
    1816                 :     /********************************************************
    1817                 :      *          Reading proj from database
    1818                 :      ********************************************************/
    1819                 :     osCommand.Printf("SELECT srtext FROM spatial_ref_sys where SRID=%d",
    1820               0 :             nSrid);
    1821               0 :     poResult = PQexec(this->poConn, osCommand.c_str());
    1822               0 :     if (poResult && PQresultStatus(poResult) == PGRES_TUPLES_OK
    1823                 :             && PQntuples(poResult) > 0) {
    1824                 :         /*
    1825                 :          * TODO: Memory leak detected with valgrind caused by allocation here.
    1826                 :          * Even when the return string is freed
    1827                 :          */
    1828               0 :         pszProjection = CPLStrdup(PQgetvalue(poResult, 0, 0));
    1829                 :     }
    1830                 : 
    1831               0 :     if (poResult)
    1832               0 :         PQclear(poResult);
    1833                 : 
    1834               0 :     return pszProjection;
    1835                 : }
    1836                 : 
    1837                 : /**********************************************************
    1838                 :  * \brief Set projection definition. The input string must
    1839                 :  * be in OGC WKT or PROJ.4 format
    1840                 :  **********************************************************/
    1841               0 : CPLErr PostGISRasterDataset::SetProjection(const char * pszProjectionRef) {
    1842               0 :     VALIDATE_POINTER1(pszProjectionRef, "SetProjection", CE_Failure);
    1843                 : 
    1844               0 :     CPLString osCommand;
    1845                 :     PGresult * poResult;
    1846               0 :     int nFetchedSrid = -1;
    1847                 : 
    1848                 : 
    1849                 :     /*****************************************************************
    1850                 :      * Check if the dataset allows updating
    1851                 :      *****************************************************************/
    1852               0 :     if (GetAccess() != GA_Update) {
    1853                 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    1854               0 :                 "This driver doesn't allow write access");
    1855               0 :         return CE_Failure;
    1856                 :     }
    1857                 : 
    1858                 :     /*****************************************************************
    1859                 :      * Look for projection with this text
    1860                 :      *****************************************************************/
    1861                 : 
    1862                 :     // First, WKT text
    1863                 :     osCommand.Printf("SELECT srid FROM spatial_ref_sys where srtext='%s'",
    1864               0 :             pszProjectionRef);
    1865               0 :     poResult = PQexec(poConn, osCommand.c_str());
    1866                 : 
    1867               0 :     if (poResult && PQresultStatus(poResult) == PGRES_TUPLES_OK
    1868                 :             && PQntuples(poResult) > 0) {
    1869                 : 
    1870               0 :         nFetchedSrid = atoi(PQgetvalue(poResult, 0, 0));
    1871                 : 
    1872                 :         // update class attribute
    1873               0 :         nSrid = nFetchedSrid;
    1874                 : 
    1875                 : 
    1876                 :         // update raster_columns table
    1877                 :         osCommand.Printf("UPDATE raster_columns SET srid=%d WHERE \
    1878                 :                     r_table_name = '%s' AND r_column = '%s'",
    1879               0 :                 nSrid, pszTable, pszColumn);
    1880               0 :         poResult = PQexec(poConn, osCommand.c_str());
    1881               0 :         if (poResult == NULL || PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    1882                 :             CPLError(CE_Failure, CPLE_AppDefined,
    1883                 :                     "Couldn't update raster_columns table: %s",
    1884               0 :                     PQerrorMessage(poConn));
    1885               0 :             return CE_Failure;
    1886                 :         }
    1887                 : 
    1888                 :         // TODO: Update ALL blocks with the new srid...
    1889                 : 
    1890               0 :         return CE_None;
    1891                 :     }
    1892                 :         // If not, proj4 text
    1893                 :     else {
    1894                 :         osCommand.Printf(
    1895                 :                 "SELECT srid FROM spatial_ref_sys where proj4text='%s'",
    1896               0 :                 pszProjectionRef);
    1897               0 :         poResult = PQexec(poConn, osCommand.c_str());
    1898                 : 
    1899               0 :         if (poResult && PQresultStatus(poResult) == PGRES_TUPLES_OK
    1900                 :                 && PQntuples(poResult) > 0) {
    1901                 : 
    1902               0 :             nFetchedSrid = atoi(PQgetvalue(poResult, 0, 0));
    1903                 : 
    1904                 :             // update class attribute
    1905               0 :             nSrid = nFetchedSrid;
    1906                 : 
    1907                 :             // update raster_columns table
    1908                 :             osCommand.Printf("UPDATE raster_columns SET srid=%d WHERE \
    1909                 :                     r_table_name = '%s' AND r_column = '%s'",
    1910               0 :                     nSrid, pszTable, pszColumn);
    1911                 : 
    1912               0 :             poResult = PQexec(poConn, osCommand.c_str());
    1913               0 :             if (poResult == NULL ||
    1914                 :                     PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    1915                 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1916                 :                         "Couldn't update raster_columns table: %s",
    1917               0 :                         PQerrorMessage(poConn));
    1918               0 :                 return CE_Failure;
    1919                 :             }
    1920                 : 
    1921                 :             // TODO: Update ALL blocks with the new srid...
    1922                 : 
    1923               0 :             return CE_None;
    1924                 :         }
    1925                 :         else {
    1926                 :             CPLError(CE_Failure, CPLE_WrongFormat,
    1927               0 :                     "Couldn't find WKT neither proj4 definition");
    1928               0 :             return CE_Failure;
    1929                 :         }
    1930               0 :     }
    1931                 : }
    1932                 : 
    1933                 : /********************************************************
    1934                 :  * \brief Set the affine transformation coefficients
    1935                 :  ********************************************************/
    1936               0 : CPLErr PostGISRasterDataset::SetGeoTransform(double* padfTransform) {
    1937               0 :     if (!padfTransform)
    1938               0 :         return CE_Failure;
    1939                 : 
    1940               0 :     adfGeoTransform[0] = padfTransform[0];
    1941               0 :     adfGeoTransform[1] = padfTransform[1];
    1942               0 :     adfGeoTransform[2] = padfTransform[2];
    1943               0 :     adfGeoTransform[3] = padfTransform[3];
    1944               0 :     adfGeoTransform[4] = padfTransform[4];
    1945               0 :     adfGeoTransform[5] = padfTransform[5];
    1946                 : 
    1947               0 :     return CE_None;
    1948                 : }
    1949                 : 
    1950                 : /********************************************************
    1951                 :  * \brief Get the affine transformation coefficients
    1952                 :  ********************************************************/
    1953               0 : CPLErr PostGISRasterDataset::GetGeoTransform(double * padfTransform) {
    1954                 :     // copy necessary values in supplied buffer
    1955               0 :     padfTransform[0] = adfGeoTransform[0];
    1956               0 :     padfTransform[1] = adfGeoTransform[1];
    1957               0 :     padfTransform[2] = adfGeoTransform[2];
    1958               0 :     padfTransform[3] = adfGeoTransform[3];
    1959               0 :     padfTransform[4] = adfGeoTransform[4];
    1960               0 :     padfTransform[5] = adfGeoTransform[5];
    1961                 : 
    1962               0 :     return CE_None;
    1963                 : }
    1964                 : 
    1965                 : /********************************************************
    1966                 :  * \brief Create a copy of a PostGIS Raster dataset.
    1967                 :  ********************************************************/
    1968                 : GDALDataset * 
    1969              36 : PostGISRasterDataset::CreateCopy( const char * pszFilename,
    1970                 :     GDALDataset *poGSrcDS, int bStrict, char ** papszOptions, 
    1971                 :     GDALProgressFunc pfnProgress, void * pProgressData ) 
    1972                 : {
    1973              36 :     char* pszSchema = NULL;
    1974              36 :     char* pszTable = NULL;
    1975              36 :     char* pszColumn = NULL;
    1976              36 :     char* pszWhere = NULL;
    1977              36 :     GBool bBrowseDatabase = false;
    1978                 :     int nMode;
    1979              36 :     char* pszConnectionString = NULL;
    1980                 :     const char* pszSubdatasetName;
    1981              36 :     PGconn * poConn = NULL;
    1982              36 :     PGresult * poResult = NULL;
    1983              36 :     CPLString osCommand;
    1984                 :     GBool bInsertSuccess;
    1985              36 :     PostGISRasterDataset *poSrcDS = (PostGISRasterDataset *)poGSrcDS;
    1986                 :     PostGISRasterDataset *poSubDS;
    1987                 : 
    1988                 :     // Check connection string
    1989              36 :     if (pszFilename == NULL ||
    1990                 :         !EQUALN(pszFilename, "PG:", 3)) {
    1991                 :         /**
    1992                 :          * The connection string provided is not a valid connection 
    1993                 :          * string.
    1994                 :          */
    1995                 :         CPLError( CE_Failure, CPLE_NotSupported, 
    1996                 :             "PostGIS Raster driver was unable to parse the provided "
    1997              36 :             "connection string." );
    1998              36 :         return NULL;
    1999                 :     }
    2000                 : 
    2001                 :     poConn = GetConnection(pszFilename, &pszConnectionString, &pszSchema, 
    2002               0 :         &pszTable, &pszColumn, &pszWhere, &nMode, &bBrowseDatabase);
    2003               0 :     if (poConn == NULL || bBrowseDatabase || pszTable == NULL) 
    2004                 :     {
    2005               0 :         CPLFree(pszConnectionString);
    2006               0 :         CPLFree(pszSchema);
    2007               0 :         CPLFree(pszTable);
    2008               0 :         CPLFree(pszColumn);
    2009               0 :         CPLFree(pszWhere);
    2010                 : 
    2011                 :         // if connection info fails, browsing mode, or no table set
    2012               0 :         return NULL;
    2013                 :     }
    2014                 : 
    2015                 :     /* Check geometry type existence */
    2016               0 :     poResult = PQexec(poConn, "SELECT oid FROM pg_type WHERE typname = 'geometry'");
    2017               0 :     if (
    2018                 :             poResult == NULL ||
    2019                 :             PQresultStatus(poResult) != PGRES_TUPLES_OK ||
    2020                 :             PQntuples(poResult) <= 0
    2021                 :             ) {
    2022                 :         CPLError(CE_Failure, CPLE_AppDefined,
    2023                 :                 "Error checking geometry type existence. Is PostGIS correctly \
    2024               0 :                 installed?: %s", PQerrorMessage(poConn));
    2025               0 :         if (poResult != NULL)
    2026               0 :             PQclear(poResult);
    2027               0 :         if (pszSchema)
    2028               0 :             CPLFree(pszSchema);
    2029               0 :         if (pszTable)
    2030               0 :             CPLFree(pszTable);
    2031               0 :         if (pszColumn)
    2032               0 :             CPLFree(pszColumn);
    2033               0 :         if (pszWhere)
    2034               0 :             CPLFree(pszWhere);
    2035                 : 
    2036               0 :         CPLFree(pszConnectionString);
    2037                 : 
    2038               0 :         return NULL;
    2039                 :     }
    2040                 : 
    2041               0 :     PQclear(poResult);
    2042                 : 
    2043                 :     /* Check spatial tables existence */
    2044                 :     poResult = PQexec(poConn, "select pg_namespace.nspname as schemaname, \
    2045                 :                     pg_class.relname as tablename from pg_class, \
    2046                 :                     pg_namespace where pg_class.relnamespace = pg_namespace.oid \
    2047                 :                     and (pg_class.relname='raster_columns' or \
    2048                 :                     pg_class.relname='raster_overviews' or \
    2049                 :                     pg_class.relname='geometry_columns' or \
    2050               0 :                     pg_class.relname='spatial_ref_sys')");
    2051               0 :     if (
    2052                 :             poResult == NULL ||
    2053                 :             PQresultStatus(poResult) != PGRES_TUPLES_OK ||
    2054                 :             PQntuples(poResult) <= 0
    2055                 :             ) {
    2056                 :         CPLError(CE_Failure, CPLE_AppDefined,
    2057                 :                 "Error checking needed tables existence: %s",
    2058               0 :                 PQerrorMessage(poConn));
    2059               0 :         if (poResult != NULL)
    2060               0 :             PQclear(poResult);
    2061               0 :         if (pszSchema)
    2062               0 :             CPLFree(pszSchema);
    2063               0 :         if (pszTable)
    2064               0 :             CPLFree(pszTable);
    2065               0 :         if (pszColumn)
    2066               0 :             CPLFree(pszColumn);
    2067               0 :         if (pszWhere)
    2068               0 :             CPLFree(pszWhere);
    2069                 : 
    2070               0 :         CPLFree(pszConnectionString);
    2071                 : 
    2072               0 :         return NULL;
    2073                 :     }
    2074                 : 
    2075               0 :     PQclear(poResult);
    2076                 : 
    2077                 :     // begin transaction
    2078               0 :     poResult = PQexec(poConn, "begin");
    2079               0 :     if (poResult == NULL ||
    2080                 :         PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    2081                 :         CPLError(CE_Failure, CPLE_AppDefined,
    2082                 :             "Error beginning database transaction: %s",
    2083               0 :             PQerrorMessage(poConn));
    2084               0 :         if (poResult != NULL)
    2085               0 :             PQclear(poResult);
    2086               0 :         if (pszSchema)
    2087               0 :             CPLFree(pszSchema);
    2088               0 :         if (pszTable)
    2089               0 :             CPLFree(pszTable);
    2090               0 :         if (pszColumn)
    2091               0 :             CPLFree(pszColumn);
    2092               0 :         if (pszWhere)
    2093               0 :             CPLFree(pszWhere);
    2094                 : 
    2095               0 :         CPLFree(pszConnectionString);
    2096                 : 
    2097               0 :         return NULL;
    2098                 :     }
    2099                 : 
    2100               0 :     PQclear(poResult);
    2101                 : 
    2102                 :     // create table for raster (if not exists because a
    2103                 :     // dataset will not be reported for an empty table)
    2104                 : 
    2105                 :     // TODO: is 'rid' necessary?
    2106                 :     osCommand.Printf("create table if not exists %s.%s (rid serial, %s "
    2107                 :         "public.raster, constraint %s_pkey primary key (rid));",
    2108               0 :         pszSchema, pszTable, pszColumn, pszTable);
    2109               0 :     poResult = PQexec(poConn, osCommand.c_str());
    2110               0 :     if (
    2111                 :             poResult == NULL ||
    2112                 :             PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    2113                 : 
    2114                 :         CPLError(CE_Failure, CPLE_AppDefined,
    2115                 :                 "Error creating needed tables: %s",
    2116               0 :                 PQerrorMessage(poConn));
    2117               0 :         if (poResult != NULL)
    2118               0 :             PQclear(poResult);
    2119                 : 
    2120                 :         // rollback
    2121               0 :         poResult = PQexec(poConn, "rollback");
    2122               0 :         if (poResult == NULL ||
    2123                 :             PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    2124                 : 
    2125                 :             CPLError(CE_Failure, CPLE_AppDefined,
    2126                 :                 "Error rolling back transaction: %s",
    2127               0 :                 PQerrorMessage(poConn));
    2128                 :         }
    2129               0 :         if (poResult != NULL)
    2130               0 :             PQclear(poResult);
    2131               0 :         if (pszSchema)
    2132               0 :             CPLFree(pszSchema);
    2133               0 :         if (pszTable)
    2134               0 :             CPLFree(pszTable);
    2135               0 :         if (pszColumn)
    2136               0 :             CPLFree(pszColumn);
    2137               0 :         if (pszWhere)
    2138               0 :             CPLFree(pszWhere);
    2139                 : 
    2140               0 :         CPLFree(pszConnectionString);
    2141                 : 
    2142               0 :         return NULL;
    2143                 :     }
    2144                 : 
    2145               0 :     PQclear(poResult);
    2146                 : 
    2147                 :     osCommand.Printf("create index %s_%s_gist ON %s.%s USING gist "
    2148                 :         "(public.st_convexhull(%s));", pszTable, pszColumn, 
    2149               0 :         pszSchema, pszTable, pszColumn);
    2150               0 :     poResult = PQexec(poConn, osCommand.c_str());
    2151               0 :     if (
    2152                 :             poResult == NULL ||
    2153                 :             PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    2154                 : 
    2155                 :         CPLError(CE_Failure, CPLE_AppDefined,
    2156                 :                 "Error creating needed index: %s",
    2157               0 :                 PQerrorMessage(poConn));
    2158               0 :         if (poResult != NULL)
    2159               0 :             PQclear(poResult);
    2160                 : 
    2161                 :         // rollback
    2162               0 :         poResult = PQexec(poConn, "rollback");
    2163               0 :         if (poResult == NULL ||
    2164                 :             PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    2165                 : 
    2166                 :             CPLError(CE_Failure, CPLE_AppDefined,
    2167                 :                 "Error rolling back transaction: %s",
    2168               0 :                 PQerrorMessage(poConn));
    2169                 :         }
    2170               0 :         if (poResult != NULL)
    2171               0 :             PQclear(poResult);
    2172               0 :         if (pszSchema)
    2173               0 :             CPLFree(pszSchema);
    2174               0 :         if (pszTable)
    2175               0 :             CPLFree(pszTable);
    2176               0 :         if (pszColumn)
    2177               0 :             CPLFree(pszColumn);
    2178               0 :         if (pszWhere)
    2179               0 :             CPLFree(pszWhere);
    2180                 : 
    2181               0 :         CPLFree(pszConnectionString);
    2182                 : 
    2183               0 :         return NULL;
    2184                 :     }
    2185                 : 
    2186               0 :     PQclear(poResult);
    2187                 : 
    2188               0 :     if (poSrcDS->nMode == ONE_RASTER_PER_TABLE) {
    2189                 :         // one raster per table
    2190                 : 
    2191                 :         // insert one raster
    2192                 :         bInsertSuccess = InsertRaster(poConn, poSrcDS,
    2193               0 :             pszSchema, pszTable, pszColumn);
    2194               0 :         if (!bInsertSuccess) {
    2195                 :             // rollback
    2196               0 :             poResult = PQexec(poConn, "rollback");
    2197               0 :             if (poResult == NULL ||
    2198                 :                 PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    2199                 : 
    2200                 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2201                 :                     "Error rolling back transaction: %s",
    2202               0 :                     PQerrorMessage(poConn));
    2203                 :             }
    2204               0 :             if (poResult != NULL)
    2205               0 :                 PQclear(poResult);
    2206               0 :             if (pszSchema)
    2207               0 :                 CPLFree(pszSchema);
    2208               0 :             if (pszTable)
    2209               0 :                 CPLFree(pszTable);
    2210               0 :             if (pszColumn)
    2211               0 :                 CPLFree(pszColumn);
    2212               0 :             if (pszWhere)
    2213               0 :                 CPLFree(pszWhere);
    2214                 : 
    2215               0 :             CPLFree(pszConnectionString);
    2216                 : 
    2217               0 :             return NULL;
    2218                 :         }
    2219                 :     }
    2220               0 :     else if (poSrcDS->nMode == ONE_RASTER_PER_ROW) {
    2221                 :         // one raster per row
    2222                 : 
    2223                 :         // papszSubdatasets contains name/desc for each subdataset
    2224               0 :         for (int i = 0; i < CSLCount(poSrcDS->papszSubdatasets); i += 2) {
    2225               0 :             pszSubdatasetName = CPLParseNameValue( poSrcDS->papszSubdatasets[i], NULL);
    2226               0 :             if (pszSubdatasetName == NULL) {
    2227                 :                 CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): "
    2228                 :                 "Could not parse name/value out of subdataset list: "
    2229               0 :                 "%s", poSrcDS->papszSubdatasets[i]);
    2230               0 :                 continue;
    2231                 :             }
    2232                 : 
    2233                 :             // for each subdataset
    2234               0 :             GDALOpenInfo poOpenInfo( pszSubdatasetName, GA_ReadOnly );
    2235                 :             // open the subdataset
    2236               0 :             poSubDS = (PostGISRasterDataset *)Open(&poOpenInfo);
    2237                 : 
    2238               0 :             if (poSubDS == NULL) {
    2239                 :                 // notify!
    2240                 :                 CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): "
    2241                 :                     "Could not open a subdataset: %s", 
    2242               0 :                     pszSubdatasetName);
    2243               0 :                 continue;
    2244                 :             }
    2245                 : 
    2246                 :             // insert one raster
    2247                 :             bInsertSuccess = InsertRaster(poConn, poSubDS,
    2248               0 :                 pszSchema, pszTable, pszColumn);
    2249                 : 
    2250               0 :             if (!bInsertSuccess) {
    2251                 :                 CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): "
    2252               0 :                     "Could not copy raster subdataset to new dataset." );
    2253                 : 
    2254                 :                 // keep trying ...
    2255                 :             }
    2256                 : 
    2257                 :             // close this dataset
    2258               0 :             GDALClose((GDALDatasetH)poSubDS);
    2259                 :         }
    2260                 :     }
    2261                 : 
    2262                 :     // commit transaction
    2263               0 :     poResult = PQexec(poConn, "commit");
    2264               0 :     if (poResult == NULL ||
    2265                 :         PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    2266                 :         CPLError(CE_Failure, CPLE_AppDefined,
    2267                 :             "Error committing database transaction: %s",
    2268               0 :             PQerrorMessage(poConn));
    2269               0 :         if (poResult != NULL)
    2270               0 :             PQclear(poResult);
    2271               0 :         if (pszSchema)
    2272               0 :             CPLFree(pszSchema);
    2273               0 :         if (pszTable)
    2274               0 :             CPLFree(pszTable);
    2275               0 :         if (pszColumn)
    2276               0 :             CPLFree(pszColumn);
    2277               0 :         if (pszWhere)
    2278               0 :             CPLFree(pszWhere);
    2279                 : 
    2280               0 :         CPLFree(pszConnectionString);
    2281                 : 
    2282               0 :         return NULL;
    2283                 :     }
    2284                 : 
    2285               0 :     PQclear(poResult);
    2286                 : 
    2287               0 :     if (pszSchema)
    2288               0 :         CPLFree(pszSchema);
    2289               0 :     if (pszTable)
    2290               0 :         CPLFree(pszTable);
    2291               0 :     if (pszColumn)
    2292               0 :         CPLFree(pszColumn);
    2293               0 :     if (pszWhere)
    2294               0 :         CPLFree(pszWhere);
    2295                 : 
    2296               0 :     CPLFree(pszConnectionString);
    2297                 : 
    2298                 :     CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): "
    2299               0 :         "Opening new dataset: %s", pszFilename);
    2300                 : 
    2301                 :     // connect to the new dataset
    2302               0 :     GDALOpenInfo poOpenInfo( pszFilename, GA_Update );
    2303                 :     // open the newdataset
    2304               0 :     poSubDS = (PostGISRasterDataset *)Open(&poOpenInfo);
    2305                 : 
    2306               0 :     if (poSubDS == NULL) {
    2307                 :         CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): "
    2308               0 :             "New dataset could not be opened.");
    2309                 :     }
    2310                 : 
    2311               0 :     return poSubDS;
    2312                 : }
    2313                 : 
    2314                 : /********************************************************
    2315                 :  * \brief Helper method to insert a new raster.
    2316                 :  ********************************************************/
    2317                 : GBool 
    2318               0 : PostGISRasterDataset::InsertRaster(PGconn * poConn, 
    2319                 :     PostGISRasterDataset * poSrcDS, const char *pszSchema, 
    2320                 :     const char * pszTable, const char * pszColumn)
    2321                 : {
    2322               0 :     CPLString osCommand;
    2323               0 :     PGresult * poResult = NULL;
    2324                 : 
    2325               0 :     if (poSrcDS->pszWhere == NULL) {
    2326                 :         osCommand.Printf("insert into %s.%s (%s) (select %s from %s.%s)",
    2327                 :             pszSchema, pszTable, pszColumn, poSrcDS->pszColumn, 
    2328               0 :             poSrcDS->pszSchema, poSrcDS->pszTable);
    2329                 :     }
    2330                 :     else {
    2331                 :         osCommand.Printf("insert into %s.%s (%s) (select %s from %s.%s where %s)",
    2332                 :             pszSchema, pszTable, pszColumn, poSrcDS->pszColumn, 
    2333               0 :             poSrcDS->pszSchema, poSrcDS->pszTable, poSrcDS->pszWhere);
    2334                 :     }
    2335                 : 
    2336                 :     CPLDebug("PostGIS_Raster", "PostGISRasterDataset::InsertRaster(): Query = %s",
    2337               0 :         osCommand.c_str());
    2338                 : 
    2339               0 :     poResult = PQexec(poConn, osCommand.c_str());
    2340               0 :     if (
    2341                 :             poResult == NULL ||
    2342                 :             PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    2343                 : 
    2344                 :         CPLError(CE_Failure, CPLE_AppDefined,
    2345                 :                 "Error inserting raster: %s",
    2346               0 :                 PQerrorMessage(poConn));
    2347               0 :         if (poResult != NULL)
    2348               0 :             PQclear(poResult);
    2349                 : 
    2350               0 :         return false;
    2351                 :     }
    2352                 : 
    2353               0 :     PQclear(poResult);
    2354                 : 
    2355               0 :     return true;
    2356                 : }
    2357                 : 
    2358                 : /*********************************************************
    2359                 :  * \brief Delete a PostGIS Raster dataset. 
    2360                 :  *********************************************************/
    2361                 : CPLErr
    2362               0 : PostGISRasterDataset::Delete(const char* pszFilename) 
    2363                 : {
    2364               0 :     char* pszSchema = NULL;
    2365               0 :     char* pszTable = NULL;
    2366               0 :     char* pszColumn = NULL;
    2367               0 :     char* pszWhere = NULL;
    2368                 :     GBool bBrowseDatabase;
    2369               0 :     char* pszConnectionString = NULL;
    2370                 :     int nMode;
    2371               0 :     PGconn * poConn = NULL;
    2372               0 :     PGresult * poResult = NULL;
    2373               0 :     CPLString osCommand;
    2374               0 :     CPLErr nError = CE_Failure;
    2375                 : 
    2376                 :     // Check connection string
    2377               0 :     if (pszFilename == NULL ||
    2378                 :         !EQUALN(pszFilename, "PG:", 3)) { 
    2379                 :         /**
    2380                 :          * The connection string provided is not a valid connection 
    2381                 :          * string.
    2382                 :          */
    2383                 :         CPLError( CE_Failure, CPLE_NotSupported, 
    2384                 :             "PostGIS Raster driver was unable to parse the provided "
    2385               0 :             "connection string. Nothing was deleted." );
    2386               0 :         return CE_Failure;
    2387                 :     }
    2388                 : 
    2389                 :     poConn = GetConnection(pszFilename, &pszConnectionString, 
    2390                 :         &pszSchema, &pszTable, &pszColumn, &pszWhere,
    2391               0 :         &nMode, &bBrowseDatabase);
    2392               0 :     if (poConn == NULL) {
    2393               0 :         CPLFree(pszConnectionString);
    2394               0 :         CPLFree(pszSchema);
    2395               0 :         CPLFree(pszTable);
    2396               0 :         CPLFree(pszColumn);
    2397               0 :         CPLFree(pszWhere);
    2398                 : 
    2399               0 :         return CE_Failure;
    2400                 :     }
    2401                 : 
    2402                 :     // begin transaction
    2403               0 :     poResult = PQexec(poConn, "begin");
    2404               0 :     if (poResult == NULL ||
    2405                 :         PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    2406                 :         CPLError(CE_Failure, CPLE_AppDefined,
    2407                 :             "Error beginning database transaction: %s",
    2408               0 :             PQerrorMessage(poConn));
    2409                 : 
    2410                 :         // set nMode to NO_MODE to avoid any further processing
    2411               0 :         nMode = NO_MODE;
    2412                 :     }
    2413                 : 
    2414               0 :     PQclear(poResult);
    2415                 : 
    2416               0 :     if ( nMode == ONE_RASTER_PER_TABLE || 
    2417                 :         (nMode == ONE_RASTER_PER_ROW && pszWhere == NULL)) {
    2418                 :         // without a where clause, this delete command shall delete
    2419                 :         // all subdatasets, even if the mode is ONE_RASTER_PER_ROW
    2420                 : 
    2421                 :         // drop table <schema>.<table>;
    2422               0 :         osCommand.Printf("drop table %s.%s", pszSchema, pszTable);
    2423               0 :         poResult = PQexec(poConn, osCommand.c_str());
    2424               0 :         if (poResult == NULL || 
    2425                 :             PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    2426                 :             CPLError(CE_Failure, CPLE_AppDefined,
    2427                 :                     "Couldn't drop the table %s.%s: %s",
    2428               0 :                     pszSchema, pszTable, PQerrorMessage(poConn));
    2429                 :         }
    2430                 :         else {
    2431               0 :             PQclear(poResult);
    2432               0 :             nError = CE_None;
    2433                 :         }
    2434                 :     }
    2435               0 :     else if (nMode == ONE_RASTER_PER_ROW) {
    2436                 : 
    2437                 :         // delete from <schema>.<table> where <where>
    2438                 :         osCommand.Printf("delete from %s.%s where %s", pszSchema, 
    2439               0 :             pszTable, pszWhere);
    2440               0 :         poResult = PQexec(poConn, osCommand.c_str());
    2441               0 :         if (poResult == NULL || 
    2442                 :             PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    2443                 :             CPLError(CE_Failure, CPLE_AppDefined,
    2444                 :                     "Couldn't delete records from the table %s.%s: %s",
    2445               0 :                     pszSchema, pszTable, PQerrorMessage(poConn));
    2446                 :         }
    2447                 :         else {
    2448               0 :             PQclear(poResult);
    2449               0 :             nError = CE_None;
    2450                 :         }
    2451                 :     }
    2452                 : 
    2453                 :     // if mode == NO_MODE, the begin transaction above did not complete,
    2454                 :     // so no commit is necessary
    2455               0 :     if (nMode != NO_MODE) {
    2456               0 :         poResult = PQexec(poConn, "commit");
    2457               0 :         if (poResult == NULL ||
    2458                 :             PQresultStatus(poResult) != PGRES_COMMAND_OK) {
    2459                 :             CPLError(CE_Failure, CPLE_AppDefined,
    2460                 :                 "Error committing database transaction: %s",
    2461               0 :                 PQerrorMessage(poConn));
    2462                 : 
    2463               0 :             nError = CE_Failure;
    2464                 :         }
    2465                 :     }
    2466                 : 
    2467               0 :     if (poResult)
    2468               0 :         PQclear(poResult);
    2469               0 :     if (pszSchema)
    2470               0 :         CPLFree(pszSchema);
    2471               0 :     if (pszTable)
    2472               0 :         CPLFree(pszTable);
    2473               0 :     if (pszColumn)
    2474               0 :         CPLFree(pszColumn);
    2475               0 :     if (pszWhere)
    2476               0 :         CPLFree(pszWhere);
    2477                 : 
    2478                 :     // clean up connection string
    2479               0 :     CPLFree(pszConnectionString);
    2480                 : 
    2481               0 :     return nError;
    2482                 : }
    2483                 : 
    2484                 : /************************************************************************/
    2485                 : /*                          GDALRegister_PostGISRaster()                */
    2486                 : 
    2487                 : /************************************************************************/
    2488            1135 : void GDALRegister_PostGISRaster() {
    2489                 :     GDALDriver *poDriver;
    2490                 : 
    2491            1135 :     if (GDALGetDriverByName("PostGISRaster") == NULL) {
    2492            1093 :         poDriver = new PostGISRasterDriver();
    2493                 : 
    2494            1093 :         poDriver->SetDescription("PostGISRaster");
    2495                 :         poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
    2496            1093 :                 "PostGIS Raster driver");
    2497                 : 
    2498            1093 :         poDriver->pfnOpen = PostGISRasterDataset::Open;
    2499            1093 :         poDriver->pfnCreateCopy = PostGISRasterDataset::CreateCopy;
    2500            1093 :         poDriver->pfnDelete = PostGISRasterDataset::Delete;
    2501                 : 
    2502            1093 :         GetGDALDriverManager()->RegisterDriver(poDriver);
    2503                 :     }
    2504            1135 : }
    2505                 : 

Generated by: LCOV version 1.7