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