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 : }
|