LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/pgdump - ogrpgdumplayer.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 451 248 55.0 %
Date: 2012-12-26 Functions: 20 13 65.0 %

       1                 : /******************************************************************************
       2                 :  * $Id: ogrpgdumplayer.cpp 24950 2012-09-22 13:54:36Z rouault $
       3                 :  *
       4                 :  * Project:  OpenGIS Simple Features Reference Implementation
       5                 :  * Purpose:  Implements OGRPGDumpLayer class
       6                 :  * Author:   Even Rouault, <even dot rouault at mines dash paris dot org>
       7                 :  *
       8                 :  ******************************************************************************
       9                 :  * Copyright (c) 2010, Even Rouault
      10                 :  *
      11                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      12                 :  * copy of this software and associated documentation files (the "Software"),
      13                 :  * to deal in the Software without restriction, including without limitation
      14                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15                 :  * and/or sell copies of the Software, and to permit persons to whom the
      16                 :  * Software is furnished to do so, subject to the following conditions:
      17                 :  *
      18                 :  * The above copyright notice and this permission notice shall be included
      19                 :  * in all copies or substantial portions of the Software.
      20                 :  *
      21                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22                 :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26                 :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27                 :  * DEALINGS IN THE SOFTWARE.
      28                 :  ****************************************************************************/
      29                 : 
      30                 : #include "ogr_pgdump.h"
      31                 : #include "cpl_conv.h"
      32                 : #include "cpl_string.h"
      33                 : 
      34                 : CPL_CVSID("$Id: ogrpgdumplayer.cpp 24950 2012-09-22 13:54:36Z rouault $");
      35                 : 
      36                 : #define USE_COPY_UNSET -1
      37                 : 
      38                 : /* Flags for creating WKB format for PostGIS */
      39                 : #define WKBZOFFSET 0x80000000
      40                 : #define WKBMOFFSET 0x40000000
      41                 : #define WKBSRIDFLAG 0x20000000
      42                 : #define WKBBBOXFLAG 0x10000000
      43                 : 
      44                 : static CPLString OGRPGDumpEscapeStringList(
      45                 :                                        char** papszItems, int bForInsertOrUpdate);
      46                 : 
      47                 : /************************************************************************/
      48                 : /*                        OGRPGDumpLayer()                              */
      49                 : /************************************************************************/
      50                 : 
      51               3 : OGRPGDumpLayer::OGRPGDumpLayer(OGRPGDumpDataSource* poDS,
      52                 :                                const char* pszSchemaName,
      53                 :                                const char* pszTableName,
      54                 :                                const char* pszGeomColumn,
      55                 :                                const char *pszFIDColumn,
      56                 :                                int         nCoordDimension,
      57                 :                                int         nSRSId,
      58                 :                                int         bWriteAsHexIn,
      59               3 :                                int         bCreateTable)
      60                 : {
      61               3 :     this->poDS = poDS;
      62               3 :     poFeatureDefn = new OGRFeatureDefn( pszTableName );
      63               3 :     poFeatureDefn->Reference();
      64               3 :     nFeatures = 0;
      65                 :     pszSqlTableName = CPLStrdup(CPLString().Printf("%s.%s",
      66                 :                                OGRPGDumpEscapeColumnName(pszSchemaName).c_str(),
      67               3 :                                OGRPGDumpEscapeColumnName(pszTableName).c_str() ));
      68               3 :     this->pszGeomColumn = (pszGeomColumn) ? CPLStrdup(pszGeomColumn) : NULL;
      69               3 :     this->pszFIDColumn = CPLStrdup(pszFIDColumn);
      70               3 :     this->nCoordDimension = nCoordDimension;
      71               3 :     this->nSRSId = nSRSId;
      72               3 :     this->bCreateTable = bCreateTable;
      73               3 :     bLaunderColumnNames = TRUE;
      74               3 :     bPreservePrecision = TRUE;
      75               3 :     bUseCopy = USE_COPY_UNSET;
      76               3 :     bWriteAsHex = bWriteAsHexIn;
      77               3 :     bCopyActive = FALSE;
      78               3 : }
      79                 : 
      80                 : /************************************************************************/
      81                 : /*                          ~OGRPGDumpLayer()                           */
      82                 : /************************************************************************/
      83                 : 
      84               3 : OGRPGDumpLayer::~OGRPGDumpLayer()
      85                 : {
      86               3 :     EndCopy();
      87                 : 
      88               3 :     poFeatureDefn->Release();
      89               3 :     CPLFree(pszSqlTableName);
      90               3 :     CPLFree(pszGeomColumn);
      91               3 :     CPLFree(pszFIDColumn);
      92               3 : }
      93                 : 
      94                 : /************************************************************************/
      95                 : /*                           GetNextFeature()                           */
      96                 : /************************************************************************/
      97                 : 
      98               0 : OGRFeature *OGRPGDumpLayer::GetNextFeature()
      99                 : {
     100               0 :     CPLError(CE_Failure, CPLE_NotSupported, "PGDump driver is write only");
     101               0 :     return NULL;
     102                 : }
     103                 : 
     104                 : /************************************************************************/
     105                 : /*                           GetNextFeature()                           */
     106                 : /************************************************************************/
     107                 : 
     108               0 : int OGRPGDumpLayer::TestCapability( const char * pszCap )
     109                 : {
     110               0 :     if( EQUAL(pszCap,OLCSequentialWrite) ||
     111                 :         EQUAL(pszCap,OLCCreateField) )
     112               0 :         return TRUE;
     113                 :     else
     114               0 :         return FALSE;
     115                 : }
     116                 : 
     117                 : /************************************************************************/
     118                 : /*                           GeometryToHex()                            */
     119                 : /************************************************************************/
     120                 : 
     121              20 : char *OGRPGDumpLayer::GeometryToHex( OGRGeometry * poGeometry, int nSRSId )
     122                 : {
     123                 :     GByte       *pabyWKB;
     124                 :     char        *pszTextBuf;
     125                 :     char        *pszTextBufCurrent;
     126                 :     char        *pszHex;
     127                 : 
     128              20 :     int nWkbSize = poGeometry->WkbSize();
     129              20 :     pabyWKB = (GByte *) CPLMalloc(nWkbSize);
     130                 : 
     131              20 :     if( poGeometry->exportToWkb( wkbNDR, pabyWKB ) != OGRERR_NONE )
     132                 :     {
     133               0 :         CPLFree( pabyWKB );
     134               0 :         return CPLStrdup("");
     135                 :     }
     136                 : 
     137                 :     /* When converting to hex, each byte takes 2 hex characters.  In addition
     138                 :        we add in 8 characters to represent the SRID integer in hex, and
     139                 :        one for a null terminator */
     140                 : 
     141              20 :     int pszSize = nWkbSize*2 + 8 + 1;
     142              20 :     pszTextBuf = (char *) CPLMalloc(pszSize);
     143              20 :     pszTextBufCurrent = pszTextBuf;
     144                 : 
     145                 :     /* Convert the 1st byte, which is the endianess flag, to hex. */
     146              20 :     pszHex = CPLBinaryToHex( 1, pabyWKB );
     147              20 :     strcpy(pszTextBufCurrent, pszHex );
     148              20 :     CPLFree ( pszHex );
     149              20 :     pszTextBufCurrent += 2;
     150                 : 
     151                 :     /* Next, get the geom type which is bytes 2 through 5 */
     152                 :     GUInt32 geomType;
     153              20 :     memcpy( &geomType, pabyWKB+1, 4 );
     154                 : 
     155                 :     /* Now add the SRID flag if an SRID is provided */
     156              20 :     if (nSRSId != -1)
     157                 :     {
     158                 :         /* Change the flag to wkbNDR (little) endianess */
     159              10 :         GUInt32 nGSrsFlag = CPL_LSBWORD32( WKBSRIDFLAG );
     160                 :         /* Apply the flag */
     161              10 :         geomType = geomType | nGSrsFlag;
     162                 :     }
     163                 : 
     164                 :     /* Now write the geom type which is 4 bytes */
     165              20 :     pszHex = CPLBinaryToHex( 4, (GByte*) &geomType );
     166              20 :     strcpy(pszTextBufCurrent, pszHex );
     167              20 :     CPLFree ( pszHex );
     168              20 :     pszTextBufCurrent += 8;
     169                 : 
     170                 :     /* Now include SRID if provided */
     171              20 :     if (nSRSId != -1)
     172                 :     {
     173                 :         /* Force the srsid to wkbNDR (little) endianess */
     174              10 :         GUInt32 nGSRSId = CPL_LSBWORD32( nSRSId );
     175              10 :         pszHex = CPLBinaryToHex( sizeof(nGSRSId),(GByte*) &nGSRSId );
     176              10 :         strcpy(pszTextBufCurrent, pszHex );
     177              10 :         CPLFree ( pszHex );
     178              10 :         pszTextBufCurrent += 8;
     179                 :     }
     180                 : 
     181                 :     /* Copy the rest of the data over - subtract
     182                 :        5 since we already copied 5 bytes above */
     183              20 :     pszHex = CPLBinaryToHex( nWkbSize - 5, pabyWKB + 5 );
     184              20 :     strcpy(pszTextBufCurrent, pszHex );
     185              20 :     CPLFree ( pszHex );
     186                 : 
     187              20 :     CPLFree( pabyWKB );
     188                 : 
     189              20 :     return pszTextBuf;
     190                 : }
     191                 : 
     192                 : /************************************************************************/
     193                 : /*                           GetNextFeature()                           */
     194                 : /************************************************************************/
     195                 : 
     196              30 : OGRErr OGRPGDumpLayer::CreateFeature( OGRFeature *poFeature )
     197                 : {
     198              30 :     if( NULL == poFeature )
     199                 :     {
     200                 :         CPLError( CE_Failure, CPLE_AppDefined,
     201               0 :                   "NULL pointer to OGRFeature passed to CreateFeature()." );
     202               0 :         return OGRERR_FAILURE;
     203                 :     }
     204                 : 
     205              30 :     nFeatures ++;
     206                 : 
     207                 :     // We avoid testing the config option too often. 
     208              30 :     if( bUseCopy == USE_COPY_UNSET )
     209               3 :         bUseCopy = CSLTestBoolean( CPLGetConfigOption( "PG_USE_COPY", "NO") );
     210                 : 
     211              30 :     if( !bUseCopy )
     212                 :     {
     213              10 :         return CreateFeatureViaInsert( poFeature );
     214                 :     }
     215                 :     else
     216                 :     {
     217              20 :         if ( !bCopyActive )
     218               2 :             StartCopy();
     219                 : 
     220              20 :         return CreateFeatureViaCopy( poFeature );
     221                 :     }
     222                 : }
     223                 : 
     224                 : /************************************************************************/
     225                 : /*                       CreateFeatureViaInsert()                       */
     226                 : /************************************************************************/
     227                 : 
     228              10 : OGRErr OGRPGDumpLayer::CreateFeatureViaInsert( OGRFeature *poFeature )
     229                 : 
     230                 : {
     231              10 :     CPLString           osCommand;
     232              10 :     int                 i = 0;
     233              10 :     int                 bNeedComma = FALSE;
     234              10 :     OGRErr              eErr = OGRERR_FAILURE;
     235              10 :     int bEmptyInsert = FALSE;
     236                 :     
     237              10 :     if( NULL == poFeature )
     238                 :     {
     239                 :         CPLError( CE_Failure, CPLE_AppDefined,
     240               0 :                   "NULL pointer to OGRFeature passed to CreateFeatureViaInsert()." );
     241               0 :         return eErr;
     242                 :     }
     243                 : 
     244                 : /* -------------------------------------------------------------------- */
     245                 : /*      Form the INSERT command.                                        */
     246                 : /* -------------------------------------------------------------------- */
     247              10 :     osCommand.Printf( "INSERT INTO %s (", pszSqlTableName );
     248                 : 
     249              10 :     OGRGeometry *poGeom = poFeature->GetGeometryRef();
     250              10 :     if( poGeom != NULL && pszGeomColumn != NULL )
     251                 :     {
     252              10 :         osCommand = osCommand + OGRPGDumpEscapeColumnName(pszGeomColumn) + " ";
     253              10 :         bNeedComma = TRUE;
     254                 :     }
     255                 : 
     256              10 :     if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL )
     257                 :     {
     258               0 :         if( bNeedComma )
     259               0 :             osCommand += ", ";
     260                 :         
     261               0 :         osCommand = osCommand + OGRPGDumpEscapeColumnName(pszFIDColumn) + " ";
     262               0 :         bNeedComma = TRUE;
     263                 :     }
     264                 : 
     265              50 :     for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
     266                 :     {
     267              40 :         if( !poFeature->IsFieldSet( i ) )
     268              10 :             continue;
     269                 : 
     270              30 :         if( !bNeedComma )
     271               0 :             bNeedComma = TRUE;
     272                 :         else
     273              30 :             osCommand += ", ";
     274                 : 
     275                 :         osCommand = osCommand 
     276              30 :             + OGRPGDumpEscapeColumnName(poFeatureDefn->GetFieldDefn(i)->GetNameRef());
     277                 :     }
     278                 : 
     279              10 :     if (!bNeedComma)
     280               0 :         bEmptyInsert = TRUE;
     281                 : 
     282              10 :     osCommand += ") VALUES (";
     283                 : 
     284                 :     /* Set the geometry */
     285              10 :     bNeedComma = FALSE;
     286              10 :     if( poGeom != NULL && pszGeomColumn != NULL )
     287                 :     {
     288              10 :         char    *pszWKT = NULL;
     289                 : 
     290              10 :         poGeom->closeRings();
     291              10 :         poGeom->setCoordinateDimension( nCoordDimension );
     292                 : 
     293              10 :         if( bWriteAsHex )
     294                 :         {
     295              10 :             char* pszHex = GeometryToHex( poGeom, nSRSId );
     296              10 :             osCommand += "'";
     297              10 :             if (pszHex)
     298              10 :                 osCommand += pszHex;
     299              10 :             osCommand += "'";
     300              10 :             CPLFree(pszHex);
     301                 :         }
     302                 :         else
     303                 :         {
     304               0 :             poGeom->exportToWkt( &pszWKT );
     305                 : 
     306               0 :             if( pszWKT != NULL )
     307                 :             {
     308                 :                 osCommand +=
     309                 :                     CPLString().Printf(
     310               0 :                         "GeomFromEWKT('SRID=%d;%s'::TEXT) ", nSRSId, pszWKT );
     311               0 :                 OGRFree( pszWKT );
     312                 :             }
     313                 :             else
     314               0 :                 osCommand += "''";
     315                 :         }
     316                 : 
     317              10 :         bNeedComma = TRUE;
     318                 :     }
     319                 : 
     320                 :     /* Set the FID */
     321              10 :     if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL )
     322                 :     {
     323               0 :         if( bNeedComma )
     324               0 :             osCommand += ", ";
     325               0 :         osCommand += CPLString().Printf( "%ld ", poFeature->GetFID() );
     326               0 :         bNeedComma = TRUE;
     327                 :     }
     328                 : 
     329                 : 
     330              50 :     for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
     331                 :     {
     332              40 :         if( !poFeature->IsFieldSet( i ) )
     333              10 :             continue;
     334                 : 
     335              30 :         if( bNeedComma )
     336              30 :             osCommand += ", ";
     337                 :         else
     338               0 :             bNeedComma = TRUE;
     339                 : 
     340              30 :         AppendFieldValue(osCommand, poFeature, i);
     341                 :     }
     342                 : 
     343              10 :     osCommand += ")";
     344                 : 
     345              10 :     if (bEmptyInsert)
     346               0 :         osCommand.Printf( "INSERT INTO %s DEFAULT VALUES", pszSqlTableName );
     347                 : 
     348                 : /* -------------------------------------------------------------------- */
     349                 : /*      Execute the insert.                                             */
     350                 : /* -------------------------------------------------------------------- */
     351              10 :     poDS->Log(osCommand);
     352                 : 
     353              10 :     return OGRERR_NONE;
     354                 : }
     355                 : 
     356                 : 
     357                 : /************************************************************************/
     358                 : /*                        CreateFeatureViaCopy()                        */
     359                 : /************************************************************************/
     360                 : 
     361              20 : OGRErr OGRPGDumpLayer::CreateFeatureViaCopy( OGRFeature *poFeature )
     362                 : {
     363              20 :     CPLString            osCommand;
     364                 : 
     365                 :     /* First process geometry */
     366              20 :     OGRGeometry *poGeometry = (OGRGeometry *) poFeature->GetGeometryRef();
     367                 :     
     368              20 :     if (pszGeomColumn)
     369                 :     {
     370              10 :         char *pszGeom = NULL;
     371              10 :         if ( NULL != poGeometry /* && (bHasWkb || bHasPostGISGeometry || bHasPostGISGeography) */)
     372                 :         {
     373              10 :             poGeometry->closeRings();
     374              10 :             poGeometry->setCoordinateDimension( nCoordDimension );
     375                 :             
     376                 :             //CheckGeomTypeCompatibility(poGeometry);
     377                 :     
     378                 :             /*if (bHasWkb)
     379                 :                 pszGeom = GeometryToBYTEA( poGeometry );
     380                 :             else*/
     381              10 :                 pszGeom = GeometryToHex( poGeometry, nSRSId );
     382                 :         }
     383                 :     
     384              10 :         if ( pszGeom )
     385                 :         {
     386                 :             osCommand += pszGeom,
     387              10 :             CPLFree( pszGeom );
     388                 :         }
     389                 :         else
     390                 :         {
     391               0 :             osCommand = "\\N";
     392                 :         }
     393                 :     }
     394                 : 
     395                 :     /* Next process the field id column */
     396              20 :     if( /*bHasFid &&*/ poFeatureDefn->GetFieldIndex( pszFIDColumn ) != -1 )
     397                 :     {
     398               0 :         if (osCommand.size() > 0)
     399               0 :             osCommand += "\t";
     400                 :             
     401                 :         /* Set the FID */
     402               0 :         if( poFeature->GetFID() != OGRNullFID )
     403                 :         {
     404               0 :             osCommand += CPLString().Printf("%ld ", poFeature->GetFID());
     405                 :         }
     406                 :         else
     407                 :         {
     408               0 :             osCommand += "\\N" ;
     409                 :         }
     410                 :     }
     411                 : 
     412                 : 
     413                 :     /* Now process the remaining fields */
     414                 : 
     415              20 :     int nFieldCount = poFeatureDefn->GetFieldCount();
     416             110 :     for( int i = 0; i < nFieldCount;  i++ )
     417                 :     {
     418              90 :         const char *pszStrValue = poFeature->GetFieldAsString(i);
     419              90 :         char *pszNeedToFree = NULL;
     420                 : 
     421              90 :         if (i > 0 || osCommand.size() > 0)
     422              80 :             osCommand += "\t";
     423                 :             
     424              90 :         if( !poFeature->IsFieldSet( i ) )
     425                 :         {
     426              21 :             osCommand += "\\N" ;
     427                 : 
     428              21 :             continue;
     429                 :         }
     430                 : 
     431              69 :         int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
     432                 : 
     433                 :         // We need special formatting for integer list values.
     434              69 :         if( nOGRFieldType == OFTIntegerList )
     435                 :         {
     436               0 :             int nCount, nOff = 0, j;
     437               0 :             const int *panItems = poFeature->GetFieldAsIntegerList(i,&nCount);
     438                 : 
     439               0 :             pszNeedToFree = (char *) CPLMalloc(nCount * 13 + 10);
     440               0 :             strcpy( pszNeedToFree, "{" );
     441               0 :             for( j = 0; j < nCount; j++ )
     442                 :             {
     443               0 :                 if( j != 0 )
     444               0 :                     strcat( pszNeedToFree+nOff, "," );
     445                 : 
     446               0 :                 nOff += strlen(pszNeedToFree+nOff);
     447               0 :                 sprintf( pszNeedToFree+nOff, "%d", panItems[j] );
     448                 :             }
     449               0 :             strcat( pszNeedToFree+nOff, "}" );
     450               0 :             pszStrValue = pszNeedToFree;
     451                 :         }
     452                 : 
     453                 :         // We need special formatting for real list values.
     454              69 :         else if( nOGRFieldType == OFTRealList )
     455                 :         {
     456               0 :             int nCount, nOff = 0, j;
     457               0 :             const double *padfItems =poFeature->GetFieldAsDoubleList(i,&nCount);
     458                 : 
     459               0 :             pszNeedToFree = (char *) CPLMalloc(nCount * 40 + 10);
     460               0 :             strcpy( pszNeedToFree, "{" );
     461               0 :             for( j = 0; j < nCount; j++ )
     462                 :             {
     463               0 :                 if( j != 0 )
     464               0 :                     strcat( pszNeedToFree+nOff, "," );
     465                 : 
     466               0 :                 nOff += strlen(pszNeedToFree+nOff);
     467                 :                 //Check for special values. They need to be quoted.
     468               0 :                 if( CPLIsNan(padfItems[j]) )
     469               0 :                     sprintf( pszNeedToFree+nOff, "NaN" );
     470               0 :                 else if( CPLIsInf(padfItems[j]) )
     471               0 :                     sprintf( pszNeedToFree+nOff, (padfItems[j] > 0) ? "Infinity" : "-Infinity" );
     472                 :                 else
     473               0 :                     sprintf( pszNeedToFree+nOff, "%.16g", padfItems[j] );
     474                 : 
     475                 :             }
     476               0 :             strcat( pszNeedToFree+nOff, "}" );
     477               0 :             pszStrValue = pszNeedToFree;
     478                 :         }
     479                 : 
     480                 : 
     481                 :         // We need special formatting for string list values.
     482              69 :         else if( nOGRFieldType == OFTStringList )
     483                 :         {
     484               0 :             CPLString osStr;
     485               0 :             char **papszItems = poFeature->GetFieldAsStringList(i);
     486                 : 
     487               0 :             pszStrValue = pszNeedToFree = CPLStrdup(OGRPGDumpEscapeStringList(papszItems, FALSE));
     488                 :         }
     489                 : 
     490                 :         // Binary formatting
     491              69 :         else if( nOGRFieldType == OFTBinary )
     492                 :         {
     493               0 :             int nLen = 0;
     494               0 :             GByte* pabyData = poFeature->GetFieldAsBinary( i, &nLen );
     495               0 :             char* pszBytea = GByteArrayToBYTEA( pabyData, nLen);
     496                 : 
     497               0 :             pszStrValue = pszNeedToFree = pszBytea;
     498                 :         }
     499                 : 
     500              69 :         else if( nOGRFieldType == OFTReal )
     501                 :         {
     502              20 :             char* pszComma = strchr((char*)pszStrValue, ',');
     503              20 :             if (pszComma)
     504               0 :                 *pszComma = '.';
     505                 :             //Check for special values. They need to be quoted.
     506              20 :             double dfVal = poFeature->GetFieldAsDouble(i);
     507              20 :             if( CPLIsNan(dfVal) )
     508               0 :                 pszStrValue = "NaN";
     509              20 :             else if( CPLIsInf(dfVal) )
     510               0 :                 pszStrValue = (dfVal > 0) ? "Infinity" : "-Infinity";
     511                 :         }
     512                 : 
     513              98 :         if( nOGRFieldType != OFTIntegerList &&
     514                 :             nOGRFieldType != OFTRealList &&
     515                 :             nOGRFieldType != OFTInteger &&
     516                 :             nOGRFieldType != OFTReal &&
     517                 :             nOGRFieldType != OFTBinary )
     518                 :         {
     519                 :             int         iChar;
     520                 : 
     521             189 :             for( iChar = 0; pszStrValue[iChar] != '\0'; iChar++ )
     522                 :             {
     523             160 :                 if( poFeatureDefn->GetFieldDefn(i)->GetWidth() > 0
     524                 :                     && iChar == poFeatureDefn->GetFieldDefn(i)->GetWidth() )
     525                 :                 {
     526                 :                     CPLDebug( "PG",
     527                 :                               "Truncated %s field value, it was too long.",
     528               0 :                               poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
     529               0 :                     break;
     530                 :                 }
     531                 : 
     532                 :                 /* Escape embedded \, \t, \n, \r since they will cause COPY
     533                 :                    to misinterpret a line of text and thus abort */
     534             640 :                 if( pszStrValue[iChar] == '\\' || 
     535             160 :                     pszStrValue[iChar] == '\t' || 
     536             160 :                     pszStrValue[iChar] == '\r' || 
     537             160 :                     pszStrValue[iChar] == '\n'   )
     538                 :                 {
     539               0 :                     osCommand += '\\';
     540                 :                 }
     541                 : 
     542             160 :                 osCommand += pszStrValue[iChar];
     543                 :             }
     544                 :         }
     545                 :         else
     546                 :         {
     547              40 :             osCommand += pszStrValue;
     548                 :         }
     549                 : 
     550              69 :         if( pszNeedToFree )
     551               0 :             CPLFree( pszNeedToFree );
     552                 :     }
     553                 : 
     554                 :     /* Add end of line marker */
     555                 :     //osCommand += "\n";
     556                 : 
     557                 : 
     558                 :     /* ------------------------------------------------------------ */
     559                 :     /*      Execute the copy.                                       */
     560                 :     /* ------------------------------------------------------------ */
     561                 : 
     562              20 :     OGRErr result = OGRERR_NONE;
     563                 : 
     564              20 :     poDS->Log(osCommand, FALSE);
     565                 : 
     566              20 :     return result;
     567                 : }
     568                 : 
     569                 : /************************************************************************/
     570                 : /*                             StartCopy()                              */
     571                 : /************************************************************************/
     572                 : 
     573               2 : OGRErr OGRPGDumpLayer::StartCopy()
     574                 : 
     575                 : {
     576                 :     /* Tell the datasource we are now planning to copy data */
     577               2 :     poDS->StartCopy( this ); 
     578                 : 
     579               2 :     CPLString osFields = BuildCopyFields();
     580                 : 
     581               2 :     int size = strlen(osFields) +  strlen(pszSqlTableName) + 100;
     582               2 :     char *pszCommand = (char *) CPLMalloc(size);
     583                 : 
     584                 :     sprintf( pszCommand,
     585                 :              "COPY %s (%s) FROM STDIN",
     586               2 :              pszSqlTableName, osFields.c_str() );
     587                 : 
     588               2 :     poDS->Log(pszCommand);
     589               2 :     bCopyActive = TRUE;
     590                 : 
     591               2 :     CPLFree( pszCommand );
     592                 : 
     593               2 :     return OGRERR_NONE;
     594                 : }
     595                 : 
     596                 : /************************************************************************/
     597                 : /*                              EndCopy()                               */
     598                 : /************************************************************************/
     599                 : 
     600               5 : OGRErr OGRPGDumpLayer::EndCopy()
     601                 : 
     602                 : {
     603               5 :     if( !bCopyActive )
     604               3 :         return OGRERR_NONE;
     605                 : 
     606               2 :     bCopyActive = FALSE;
     607                 : 
     608               2 :     poDS->Log("\\.", FALSE);
     609               2 :     poDS->Log("END");
     610                 : 
     611               2 :     bUseCopy = USE_COPY_UNSET;
     612                 : 
     613               2 :     return OGRERR_NONE;
     614                 : }
     615                 : 
     616                 : /************************************************************************/
     617                 : /*                          BuildCopyFields()                           */
     618                 : /************************************************************************/
     619                 : 
     620               2 : CPLString OGRPGDumpLayer::BuildCopyFields()
     621                 : {
     622               2 :     int     i = 0;
     623               2 :     CPLString osFieldList;
     624                 : 
     625               2 :     if( /*bHasFid &&*/ poFeatureDefn->GetFieldIndex( pszFIDColumn ) != -1 )
     626                 :     {
     627               0 :         osFieldList += OGRPGDumpEscapeColumnName(pszFIDColumn);
     628                 :     }
     629                 : 
     630               2 :     if( pszGeomColumn )
     631                 :     {
     632               1 :         if( strlen(osFieldList) > 0 )
     633               0 :             osFieldList += ", ";
     634                 : 
     635               1 :         osFieldList += OGRPGDumpEscapeColumnName(pszGeomColumn);
     636                 :     }
     637                 : 
     638              11 :     for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
     639                 :     {
     640               9 :         const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
     641                 : 
     642               9 :         if( strlen(osFieldList) > 0 )
     643               8 :             osFieldList += ", ";
     644                 : 
     645               9 :         osFieldList += OGRPGDumpEscapeColumnName(pszName);
     646                 :     }
     647                 : 
     648               0 :     return osFieldList;
     649                 : }
     650                 : 
     651                 : /************************************************************************/
     652                 : /*                       OGRPGDumpEscapeColumnName( )                   */
     653                 : /************************************************************************/
     654                 : 
     655              69 : CPLString OGRPGDumpEscapeColumnName(const char* pszColumnName)
     656                 : {
     657              69 :     CPLString osStr;
     658                 : 
     659              69 :     osStr += "\"";
     660                 : 
     661                 :     char ch;
     662             564 :     for(int i=0; (ch = pszColumnName[i]) != '\0'; i++)
     663                 :     {
     664             495 :         if (ch == '"')
     665               0 :             osStr.append(1, ch);
     666             495 :         osStr.append(1, ch);
     667                 :     }
     668                 : 
     669              69 :     osStr += "\"";
     670                 : 
     671               0 :     return osStr;
     672                 : }
     673                 : 
     674                 : /************************************************************************/
     675                 : /*                             EscapeString( )                          */
     676                 : /************************************************************************/
     677                 : 
     678              13 : CPLString OGRPGDumpEscapeString(
     679                 :                                    const char* pszStrValue, int nMaxLength,
     680                 :                                    const char* pszFieldName)
     681                 : {
     682              13 :     CPLString osCommand;
     683                 : 
     684                 :     /* We need to quote and escape string fields. */
     685              13 :     osCommand += "'";
     686                 : 
     687              13 :     int nSrcLen = strlen(pszStrValue);
     688              13 :     if (nMaxLength > 0 && nSrcLen > nMaxLength)
     689                 :     {
     690                 :         CPLDebug( "PG",
     691                 :                   "Truncated %s field value, it was too long.",
     692               0 :                   pszFieldName );
     693               0 :         nSrcLen = nMaxLength;
     694                 :         
     695               0 :         while( nSrcLen > 0 && ((unsigned char *) pszStrValue)[nSrcLen-1] > 127 )
     696                 :         {
     697               0 :             CPLDebug( "PG", "Backup to start of multi-byte character." );
     698               0 :             nSrcLen--;
     699                 :         }
     700                 :     }
     701                 : 
     702              13 :     char* pszDestStr = (char*)CPLMalloc(2 * nSrcLen + 1);
     703                 : 
     704                 :     /* -------------------------------------------------------------------- */
     705                 :     /*  PQescapeStringConn was introduced in PostgreSQL security releases   */
     706                 :     /*  8.1.4, 8.0.8, 7.4.13, 7.3.15                                        */
     707                 :     /*  PG_HAS_PQESCAPESTRINGCONN is added by a test in 'configure'         */
     708                 :     /*  so it is not set by default when building OGR for Win32             */
     709                 :     /* -------------------------------------------------------------------- */
     710                 : #if defined(PG_HAS_PQESCAPESTRINGCONN)
     711                 :     int nError;
     712                 :     PQescapeStringConn (hPGConn, pszDestStr, pszStrValue, nSrcLen, &nError);
     713                 :     if (nError == 0)
     714                 :         osCommand += pszDestStr;
     715                 :     else
     716                 :         CPLError(CE_Warning, CPLE_AppDefined, 
     717                 :                  "PQescapeString(): %s\n"
     718                 :                  "  input: '%s'\n"
     719                 :                  "    got: '%s'\n",
     720                 :                  PQerrorMessage( hPGConn ),
     721                 :                  pszStrValue, pszDestStr );
     722                 : #else
     723                 :     //PQescapeString(pszDestStr, pszStrValue, nSrcLen);
     724                 :     
     725                 :     int i, j;
     726             108 :     for(i=0,j=0; i < nSrcLen; i++)
     727                 :     {
     728              95 :         if (pszStrValue[i] == '\'')
     729                 :         {
     730               0 :             pszDestStr[j++] = '\'';
     731               0 :             pszDestStr[j++] = '\'';
     732                 :         }
     733              95 :         else if (pszStrValue[i] == '\\')
     734                 :         {
     735               0 :             pszDestStr[j++] = '\\';
     736               0 :             pszDestStr[j++] = '\\';
     737                 :         }
     738                 :         else
     739              95 :             pszDestStr[j++] = pszStrValue[i];
     740                 :     }
     741              13 :     pszDestStr[j] = 0;
     742                 : 
     743              13 :     osCommand += pszDestStr;
     744                 : #endif
     745              13 :     CPLFree(pszDestStr);
     746                 : 
     747              13 :     osCommand += "'";
     748                 : 
     749               0 :     return osCommand;
     750                 : }
     751                 : 
     752                 : 
     753                 : /************************************************************************/
     754                 : /*                    OGRPGDumpEscapeStringList( )                      */
     755                 : /************************************************************************/
     756                 : 
     757               0 : static CPLString OGRPGDumpEscapeStringList(
     758                 :                                        char** papszItems, int bForInsertOrUpdate)
     759                 : {
     760               0 :     int bFirstItem = TRUE;
     761               0 :     CPLString osStr;
     762               0 :     if (bForInsertOrUpdate)
     763               0 :         osStr += "ARRAY[";
     764                 :     else
     765               0 :         osStr += "{";
     766               0 :     while(*papszItems)
     767                 :     {
     768               0 :         if (!bFirstItem)
     769                 :         {
     770               0 :             osStr += ',';
     771                 :         }
     772                 : 
     773               0 :         char* pszStr = *papszItems;
     774               0 :         if (*pszStr != '\0')
     775                 :         {
     776               0 :             if (bForInsertOrUpdate)
     777               0 :                 osStr += OGRPGDumpEscapeString(pszStr, -1, "");
     778                 :             else
     779                 :             {
     780               0 :                 osStr += '"';
     781                 : 
     782               0 :                 while(*pszStr)
     783                 :                 {
     784               0 :                     if (*pszStr == '"' )
     785               0 :                         osStr += "\\";
     786               0 :                     osStr += *pszStr;
     787               0 :                     pszStr++;
     788                 :                 }
     789                 : 
     790               0 :                 osStr += '"';
     791                 :             }
     792                 :         }
     793                 :         else
     794               0 :             osStr += "NULL";
     795                 : 
     796               0 :         bFirstItem = FALSE;
     797                 : 
     798               0 :         papszItems++;
     799                 :     }
     800               0 :     if (bForInsertOrUpdate)
     801               0 :         osStr += "]";
     802                 :     else
     803               0 :         osStr += "}";
     804               0 :     return osStr;
     805                 : }
     806                 : 
     807                 : /************************************************************************/
     808                 : /*                          AppendFieldValue()                          */
     809                 : /*                                                                      */
     810                 : /* Used by CreateFeatureViaInsert() and SetFeature() to format a        */
     811                 : /* non-empty field value                                                */
     812                 : /************************************************************************/
     813                 : 
     814              30 : void OGRPGDumpLayer::AppendFieldValue(CPLString& osCommand,
     815                 :                                        OGRFeature* poFeature, int i)
     816                 : {
     817              30 :     int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
     818                 : 
     819                 :     // We need special formatting for integer list values.
     820              30 :     if(  nOGRFieldType == OFTIntegerList )
     821                 :     {
     822               0 :         int nCount, nOff = 0, j;
     823               0 :         const int *panItems = poFeature->GetFieldAsIntegerList(i,&nCount);
     824               0 :         char *pszNeedToFree = NULL;
     825                 : 
     826               0 :         pszNeedToFree = (char *) CPLMalloc(nCount * 13 + 10);
     827               0 :         strcpy( pszNeedToFree, "'{" );
     828               0 :         for( j = 0; j < nCount; j++ )
     829                 :         {
     830               0 :             if( j != 0 )
     831               0 :                 strcat( pszNeedToFree+nOff, "," );
     832                 : 
     833               0 :             nOff += strlen(pszNeedToFree+nOff);
     834               0 :             sprintf( pszNeedToFree+nOff, "%d", panItems[j] );
     835                 :         }
     836               0 :         strcat( pszNeedToFree+nOff, "}'" );
     837                 : 
     838               0 :         osCommand += pszNeedToFree;
     839               0 :         CPLFree(pszNeedToFree);
     840                 : 
     841               0 :         return;
     842                 :     }
     843                 : 
     844                 :     // We need special formatting for real list values.
     845              30 :     else if( nOGRFieldType == OFTRealList )
     846                 :     {
     847               0 :         int nCount, nOff = 0, j;
     848               0 :         const double *padfItems =poFeature->GetFieldAsDoubleList(i,&nCount);
     849               0 :         char *pszNeedToFree = NULL;
     850                 : 
     851               0 :         pszNeedToFree = (char *) CPLMalloc(nCount * 40 + 10);
     852               0 :         strcpy( pszNeedToFree, "'{" );
     853               0 :         for( j = 0; j < nCount; j++ )
     854                 :         {
     855               0 :             if( j != 0 )
     856               0 :                 strcat( pszNeedToFree+nOff, "," );
     857                 : 
     858               0 :             nOff += strlen(pszNeedToFree+nOff);
     859                 :             //Check for special values. They need to be quoted.
     860               0 :             if( CPLIsNan(padfItems[j]) )
     861               0 :                 sprintf( pszNeedToFree+nOff, "NaN" );
     862               0 :             else if( CPLIsInf(padfItems[j]) )
     863               0 :                 sprintf( pszNeedToFree+nOff, (padfItems[j] > 0) ? "Infinity" : "-Infinity" );
     864                 :             else
     865               0 :                 sprintf( pszNeedToFree+nOff, "%.16g", padfItems[j] );
     866                 : 
     867                 :         }
     868               0 :         strcat( pszNeedToFree+nOff, "}'" );
     869                 : 
     870               0 :         osCommand += pszNeedToFree;
     871               0 :         CPLFree(pszNeedToFree);
     872                 : 
     873               0 :         return;
     874                 :     }
     875                 : 
     876                 :     // We need special formatting for string list values.
     877              30 :     else if( nOGRFieldType == OFTStringList )
     878                 :     {
     879               0 :         char **papszItems = poFeature->GetFieldAsStringList(i);
     880                 : 
     881               0 :         osCommand += OGRPGDumpEscapeStringList(papszItems, TRUE);
     882                 : 
     883               0 :         return;
     884                 :     }
     885                 : 
     886                 :     // Binary formatting
     887              30 :     else if( nOGRFieldType == OFTBinary )
     888                 :     {
     889               0 :         osCommand += "'";
     890                 : 
     891               0 :         int nLen = 0;
     892               0 :         GByte* pabyData = poFeature->GetFieldAsBinary( i, &nLen );
     893               0 :         char* pszBytea = GByteArrayToBYTEA( pabyData, nLen);
     894                 : 
     895               0 :         osCommand += pszBytea;
     896                 : 
     897               0 :         CPLFree(pszBytea);
     898               0 :         osCommand += "'";
     899                 : 
     900               0 :         return;
     901                 :     }
     902                 : 
     903                 :     // Flag indicating NULL or not-a-date date value
     904                 :     // e.g. 0000-00-00 - there is no year 0
     905              30 :     OGRBoolean bIsDateNull = FALSE;
     906                 : 
     907              30 :     const char *pszStrValue = poFeature->GetFieldAsString(i);
     908                 : 
     909                 :     // Check if date is NULL: 0000-00-00
     910              30 :     if( nOGRFieldType == OFTDate )
     911                 :     {
     912               0 :         if( EQUALN( pszStrValue, "0000", 4 ) )
     913                 :         {
     914               0 :             pszStrValue = "NULL";
     915               0 :             bIsDateNull = TRUE;
     916                 :         }
     917                 :     }
     918              30 :     else if ( nOGRFieldType == OFTReal )
     919                 :     {
     920              10 :         char* pszComma = strchr((char*)pszStrValue, ',');
     921              10 :         if (pszComma)
     922               0 :             *pszComma = '.';
     923                 :         //Check for special values. They need to be quoted.
     924              10 :         double dfVal = poFeature->GetFieldAsDouble(i);
     925              10 :         if( CPLIsNan(dfVal) )
     926               0 :             pszStrValue = "'NaN'";
     927              10 :         else if( CPLIsInf(dfVal) )
     928               0 :             pszStrValue = (dfVal > 0) ? "'Infinity'" : "'-Infinity'";
     929                 :     }
     930                 : 
     931              40 :     if( nOGRFieldType != OFTInteger && nOGRFieldType != OFTReal
     932                 :         && !bIsDateNull )
     933                 :     {
     934                 :         osCommand += OGRPGDumpEscapeString( pszStrValue,
     935                 :                                         poFeatureDefn->GetFieldDefn(i)->GetWidth(),
     936              10 :                                         poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
     937                 :     }
     938                 :     else
     939                 :     {
     940              20 :         osCommand += pszStrValue;
     941                 :     }
     942                 : }
     943                 : 
     944                 : 
     945                 : /************************************************************************/
     946                 : /*                        GByteArrayToBYTEA()                           */
     947                 : /************************************************************************/
     948                 : 
     949               0 : char* OGRPGDumpLayer::GByteArrayToBYTEA( const GByte* pabyData, int nLen)
     950                 : {
     951                 :     char* pszTextBuf;
     952                 : 
     953               0 :     pszTextBuf = (char *) CPLMalloc(nLen*5+1);
     954                 : 
     955               0 :     int  iSrc, iDst=0;
     956                 : 
     957               0 :     for( iSrc = 0; iSrc < nLen; iSrc++ )
     958                 :     {
     959               0 :         if( pabyData[iSrc] < 40 || pabyData[iSrc] > 126
     960               0 :             || pabyData[iSrc] == '\\' )
     961                 :         {
     962               0 :             sprintf( pszTextBuf+iDst, "\\\\%03o", pabyData[iSrc] );
     963               0 :             iDst += 5;
     964                 :         }
     965                 :         else
     966               0 :             pszTextBuf[iDst++] = pabyData[iSrc];
     967                 :     }
     968               0 :     pszTextBuf[iDst] = '\0';
     969                 : 
     970               0 :     return pszTextBuf;
     971                 : }
     972                 : 
     973                 : /************************************************************************/
     974                 : /*                           GetNextFeature()                           */
     975                 : /************************************************************************/
     976                 : 
     977              13 : OGRErr OGRPGDumpLayer::CreateField( OGRFieldDefn *poFieldIn,
     978                 :                                      int bApproxOK )
     979                 : {
     980              13 :     if (nFeatures != 0)
     981                 :     {
     982                 :         CPLError(CE_Failure, CPLE_NotSupported,
     983               0 :                  "Cannot create field after first feature has been written");
     984               0 :         return OGRERR_FAILURE;
     985                 :     }
     986                 :     
     987              13 :     CPLString           osCommand;
     988                 :     char                szFieldType[256];
     989              13 :     OGRFieldDefn        oField( poFieldIn );
     990                 : 
     991                 : /* -------------------------------------------------------------------- */
     992                 : /*      Do we want to "launder" the column names into Postgres          */
     993                 : /*      friendly format?                                                */
     994                 : /* -------------------------------------------------------------------- */
     995              13 :     if( bLaunderColumnNames )
     996                 :     {
     997              13 :         char    *pszSafeName = poDS->LaunderName( oField.GetNameRef() );
     998                 : 
     999              13 :         oField.SetName( pszSafeName );
    1000              13 :         CPLFree( pszSafeName );
    1001                 : 
    1002              13 :         if( EQUAL(oField.GetNameRef(),"oid") )
    1003                 :         {
    1004                 :             CPLError( CE_Warning, CPLE_AppDefined,
    1005               0 :                       "Renaming field 'oid' to 'oid_' to avoid conflict with internal oid field." );
    1006               0 :             oField.SetName( "oid_" );
    1007                 :         }
    1008                 :     }
    1009                 : 
    1010                 : /* -------------------------------------------------------------------- */
    1011                 : /*      Work out the PostgreSQL type.                                   */
    1012                 : /* -------------------------------------------------------------------- */
    1013              13 :     if( oField.GetType() == OFTInteger )
    1014                 :     {
    1015               3 :         if( oField.GetWidth() > 0 && bPreservePrecision )
    1016               0 :             sprintf( szFieldType, "NUMERIC(%d,0)", oField.GetWidth() );
    1017                 :         else
    1018               3 :             strcpy( szFieldType, "INTEGER" );
    1019                 :     }
    1020              10 :     else if( oField.GetType() == OFTReal )
    1021                 :     {
    1022               3 :         if( oField.GetWidth() > 0 && oField.GetPrecision() > 0
    1023                 :             && bPreservePrecision )
    1024                 :             sprintf( szFieldType, "NUMERIC(%d,%d)",
    1025               0 :                      oField.GetWidth(), oField.GetPrecision() );
    1026                 :         else
    1027               3 :             strcpy( szFieldType, "FLOAT8" );
    1028                 :     }
    1029               7 :     else if( oField.GetType() == OFTString )
    1030                 :     {
    1031               7 :         if (oField.GetWidth() > 0 &&  bPreservePrecision )
    1032               3 :             sprintf( szFieldType, "VARCHAR(%d)",  oField.GetWidth() );
    1033                 :         else
    1034               4 :             strcpy( szFieldType, "VARCHAR");
    1035                 :     }
    1036               0 :     else if( oField.GetType() == OFTIntegerList )
    1037                 :     {
    1038               0 :         strcpy( szFieldType, "INTEGER[]" );
    1039                 :     }
    1040               0 :     else if( oField.GetType() == OFTRealList )
    1041                 :     {
    1042               0 :         strcpy( szFieldType, "FLOAT8[]" );
    1043                 :     }
    1044               0 :     else if( oField.GetType() == OFTStringList )
    1045                 :     {
    1046               0 :         strcpy( szFieldType, "varchar[]" );
    1047                 :     }
    1048               0 :     else if( oField.GetType() == OFTDate )
    1049                 :     {
    1050               0 :         strcpy( szFieldType, "date" );
    1051                 :     }
    1052               0 :     else if( oField.GetType() == OFTTime )
    1053                 :     {
    1054               0 :         strcpy( szFieldType, "time" );
    1055                 :     }
    1056               0 :     else if( oField.GetType() == OFTDateTime )
    1057                 :     {
    1058               0 :         strcpy( szFieldType, "timestamp with time zone" );
    1059                 :     }
    1060               0 :     else if( oField.GetType() == OFTBinary )
    1061                 :     {
    1062               0 :         strcpy( szFieldType, "bytea" );
    1063                 :     }
    1064               0 :     else if( bApproxOK )
    1065                 :     {
    1066                 :         CPLError( CE_Warning, CPLE_NotSupported,
    1067                 :                   "Can't create field %s with type %s on PostgreSQL layers.  Creating as VARCHAR.",
    1068                 :                   oField.GetNameRef(),
    1069               0 :                   OGRFieldDefn::GetFieldTypeName(oField.GetType()) );
    1070               0 :         strcpy( szFieldType, "VARCHAR" );
    1071                 :     }
    1072                 :     else
    1073                 :     {
    1074                 :         CPLError( CE_Failure, CPLE_NotSupported,
    1075                 :                   "Can't create field %s with type %s on PostgreSQL layers.",
    1076                 :                   oField.GetNameRef(),
    1077               0 :                   OGRFieldDefn::GetFieldTypeName(oField.GetType()) );
    1078                 : 
    1079               0 :         return OGRERR_FAILURE;
    1080                 :     }
    1081                 : 
    1082                 : /* -------------------------------------------------------------------- */
    1083                 : /*      Create the new field.                                           */
    1084                 : /* -------------------------------------------------------------------- */
    1085                 :     osCommand.Printf( "ALTER TABLE %s ADD COLUMN %s %s",
    1086                 :                       pszSqlTableName, OGRPGDumpEscapeColumnName(oField.GetNameRef()).c_str(),
    1087              13 :                       szFieldType );
    1088              13 :     if (bCreateTable)
    1089              13 :         poDS->Log(osCommand);
    1090                 : 
    1091              13 :     poFeatureDefn->AddFieldDefn( &oField );
    1092                 :     
    1093              13 :     return OGRERR_NONE;
    1094                 : }

Generated by: LCOV version 1.7