1 : /******************************************************************************
2 : * $Id: ogrpgtablelayer.cpp 24257 2012-04-18 18:19:22Z rouault $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Implements OGRPGTableLayer class, access to an existing table.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2000, Frank Warmerdam
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_pg.h"
31 : #include "cpl_conv.h"
32 : #include "cpl_string.h"
33 : #include "cpl_error.h"
34 :
35 : #define PQexec this_is_an_error
36 :
37 : CPL_CVSID("$Id: ogrpgtablelayer.cpp 24257 2012-04-18 18:19:22Z rouault $");
38 :
39 : #define USE_COPY_UNSET -10
40 : static CPLString OGRPGEscapeStringList(PGconn *hPGConn,
41 : char** papszItems, int bForInsertOrUpdate);
42 :
43 : #define UNSUPPORTED_OP_READ_ONLY "%s : unsupported operation on a read-only datasource."
44 :
45 : /************************************************************************/
46 : /* OGRPGTableLayer() */
47 : /************************************************************************/
48 :
49 15872 : OGRPGTableLayer::OGRPGTableLayer( OGRPGDataSource *poDSIn,
50 : CPLString& osCurrentSchema,
51 : const char * pszTableNameIn,
52 : const char * pszSchemaNameIn,
53 : const char * pszGeomColumnIn,
54 : int bUpdate,
55 : int bAdvertizeGeomColumn,
56 15872 : int nSRSIdIn )
57 :
58 : {
59 15872 : poDS = poDSIn;
60 :
61 15872 : pszQueryStatement = NULL;
62 :
63 15872 : bUpdateAccess = bUpdate;
64 :
65 15872 : iNextShapeId = 0;
66 :
67 15872 : nSRSId = nSRSIdIn;
68 15872 : nGeomType = wkbUnknown;
69 15872 : bGeometryInformationSet = FALSE;
70 :
71 15872 : bLaunderColumnNames = TRUE;
72 15872 : bPreservePrecision = TRUE;
73 15872 : bCopyActive = FALSE;
74 15872 : bUseCopy = USE_COPY_UNSET; // unknown
75 15872 : bFIDColumnInCopyFields = FALSE;
76 :
77 15872 : pszTableName = CPLStrdup( pszTableNameIn );
78 15872 : if (pszGeomColumnIn)
79 10496 : pszGeomColumn = CPLStrdup(pszGeomColumnIn);
80 15872 : if (pszSchemaNameIn)
81 15718 : pszSchemaName = CPLStrdup( pszSchemaNameIn );
82 154 : else if (strlen(osCurrentSchema))
83 154 : pszSchemaName = CPLStrdup( osCurrentSchema );
84 : else
85 0 : pszSchemaName = NULL;
86 :
87 15872 : pszSqlGeomParentTableName = NULL;
88 :
89 15872 : bHasWarnedIncompatibleGeom = FALSE;
90 15872 : bHasWarnedAlreadySetFID = FALSE;
91 :
92 : /* Just in provision for people yelling about broken backward compatibility ... */
93 15872 : bRetrieveFID = CSLTestBoolean(CPLGetConfigOption("OGR_PG_RETRIEVE_FID", "TRUE"));
94 :
95 : /* -------------------------------------------------------------------- */
96 : /* Build the layer defn name. */
97 : /* -------------------------------------------------------------------- */
98 15872 : if ( pszSchemaNameIn && osCurrentSchema != pszSchemaNameIn )
99 : {
100 : /* For backwards compatibility, don't report the geometry column name */
101 : /* if it's wkb_geometry */
102 5078 : if (bAdvertizeGeomColumn && pszGeomColumnIn)
103 6 : osDefnName.Printf( "%s.%s(%s)", pszSchemaNameIn, pszTableName, pszGeomColumnIn );
104 : else
105 5066 : osDefnName.Printf("%s.%s", pszSchemaNameIn, pszTableName );
106 : pszSqlTableName = CPLStrdup(CPLString().Printf("%s.%s",
107 : OGRPGEscapeColumnName(pszSchemaNameIn).c_str(),
108 5072 : OGRPGEscapeColumnName(pszTableName).c_str() ));
109 : }
110 : else
111 : {
112 : //no prefix for current_schema in layer name, for backwards compatibility
113 : /* For backwards compatibility, don't report the geometry column name */
114 : /* if it's wkb_geometry */
115 10966 : if (bAdvertizeGeomColumn && pszGeomColumnIn)
116 166 : osDefnName.Printf( "%s(%s)", pszTableName, pszGeomColumnIn );
117 : else
118 10634 : osDefnName = pszTableName;
119 10800 : pszSqlTableName = CPLStrdup(OGRPGEscapeColumnName(pszTableName));
120 : }
121 :
122 15872 : osPrimaryKey = CPLGetConfigOption( "PGSQL_OGR_FID", "ogc_fid" );
123 15872 : }
124 :
125 : //************************************************************************/
126 : /* ~OGRPGTableLayer() */
127 : /************************************************************************/
128 :
129 15872 : OGRPGTableLayer::~OGRPGTableLayer()
130 :
131 : {
132 15872 : EndCopy();
133 15872 : CPLFree( pszSqlTableName );
134 15872 : CPLFree( pszTableName );
135 15872 : CPLFree( pszSqlGeomParentTableName );
136 15872 : CPLFree( pszSchemaName );
137 15872 : }
138 :
139 : /************************************************************************/
140 : /* SetGeometryInformation() */
141 : /************************************************************************/
142 :
143 10572 : void OGRPGTableLayer::SetGeometryInformation(const char* pszType,
144 : int nCoordDimension,
145 : int nSRID,
146 : PostgisType ePostgisType)
147 : {
148 10572 : if (pszType == NULL || nCoordDimension == 0 || nSRID == UNDETERMINED_SRID ||
149 : ePostgisType == GEOM_TYPE_UNKNOWN)
150 2 : return;
151 :
152 10570 : bGeometryInformationSet = TRUE;
153 :
154 10570 : nGeomType = OGRFromOGCGeomType(pszType);
155 :
156 10570 : this->nCoordDimension = nCoordDimension;
157 10570 : this->nSRSId = nSRID;
158 :
159 10570 : if( nCoordDimension == 3 && nGeomType != wkbUnknown )
160 264 : nGeomType = (OGRwkbGeometryType) (nGeomType | wkb25DBit);
161 :
162 10570 : if( ePostgisType == GEOM_TYPE_GEOMETRY)
163 10374 : bHasPostGISGeometry = TRUE;
164 196 : else if( ePostgisType == GEOM_TYPE_GEOGRAPHY)
165 196 : bHasPostGISGeography = TRUE;
166 :
167 : CPLDebug("PG","Layer '%s' geometry type: %s:%s, Dim=%d",
168 : pszTableName, pszType, OGRGeometryTypeToName(nGeomType),
169 10570 : nCoordDimension );
170 : }
171 :
172 : /************************************************************************/
173 : /* GetGeomType() */
174 : /************************************************************************/
175 :
176 24 : OGRwkbGeometryType OGRPGTableLayer::GetGeomType()
177 : {
178 24 : if (bGeometryInformationSet)
179 22 : return nGeomType;
180 :
181 2 : return GetLayerDefn()->GetGeomType();
182 : }
183 :
184 : /************************************************************************/
185 : /* ReadTableDefinition() */
186 : /* */
187 : /* Build a schema from the named table. Done by querying the */
188 : /* catalog. */
189 : /************************************************************************/
190 :
191 558 : OGRFeatureDefn *OGRPGTableLayer::ReadTableDefinition()
192 :
193 : {
194 : PGresult *hResult;
195 558 : CPLString osCommand;
196 558 : PGconn *hPGConn = poDS->GetPGConn();
197 :
198 558 : poDS->FlushSoftTransaction();
199 :
200 558 : CPLString osSchemaClause;
201 558 : if( pszSchemaName )
202 558 : osSchemaClause.Printf("AND n.nspname='%s'", pszSchemaName);
203 :
204 : const char* pszTypnameEqualsAnyClause;
205 558 : if (poDS->sPostgreSQLVersion.nMajor == 7 && poDS->sPostgreSQLVersion.nMinor <= 3)
206 0 : pszTypnameEqualsAnyClause = "ANY(SELECT '{int2, int4, serial}')";
207 : else
208 558 : pszTypnameEqualsAnyClause = "ANY(ARRAY['int2','int4','serial'])";
209 :
210 558 : CPLString osEscapedTableNameSingleQuote = OGRPGEscapeString(hPGConn, pszTableName, -1, "");
211 558 : const char* pszEscapedTableNameSingleQuote = osEscapedTableNameSingleQuote.c_str();
212 :
213 : /* See #1889 for why we don't use 'AND a.attnum = ANY(i.indkey)' */
214 : osCommand.Printf("SELECT a.attname, a.attnum, t.typname, "
215 : "t.typname = %s AS isfid "
216 : "FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n, pg_index i "
217 : "WHERE a.attnum > 0 AND a.attrelid = c.oid "
218 : "AND a.atttypid = t.oid AND c.relnamespace = n.oid "
219 : "AND c.oid = i.indrelid AND i.indisprimary = 't' "
220 : "AND t.typname !~ '^geom' AND c.relname = %s "
221 : "AND (i.indkey[0]=a.attnum OR i.indkey[1]=a.attnum OR i.indkey[2]=a.attnum "
222 : "OR i.indkey[3]=a.attnum OR i.indkey[4]=a.attnum OR i.indkey[5]=a.attnum "
223 : "OR i.indkey[6]=a.attnum OR i.indkey[7]=a.attnum OR i.indkey[8]=a.attnum "
224 : "OR i.indkey[9]=a.attnum) %s ORDER BY a.attnum",
225 558 : pszTypnameEqualsAnyClause, pszEscapedTableNameSingleQuote, osSchemaClause.c_str() );
226 :
227 558 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str() );
228 :
229 558 : if ( hResult && PGRES_TUPLES_OK == PQresultStatus(hResult) )
230 : {
231 558 : if ( PQntuples( hResult ) == 1 && PQgetisnull( hResult,0,0 ) == false )
232 : {
233 : /* Check if single-field PK can be represented as 32-bit integer. */
234 368 : CPLString osValue(PQgetvalue(hResult, 0, 3));
235 368 : if( osValue == "t" )
236 : {
237 368 : osPrimaryKey.Printf( "%s", PQgetvalue(hResult,0,0) );
238 368 : CPLDebug( "PG", "Primary key name (FID): %s", osPrimaryKey.c_str() );
239 368 : }
240 : }
241 190 : else if ( PQntuples( hResult ) > 1 )
242 : {
243 : CPLError( CE_Warning, CPLE_AppDefined,
244 : "Multi-column primary key in \'%s\' detected but not supported.",
245 2 : pszTableName );
246 : }
247 :
248 558 : OGRPGClearResult( hResult );
249 : /* Zero tuples means no PK is defined, perfectly valid case. */
250 : }
251 : else
252 : {
253 : CPLError( CE_Failure, CPLE_AppDefined,
254 0 : "%s", PQerrorMessage(hPGConn) );
255 : }
256 :
257 : /* -------------------------------------------------------------------- */
258 : /* Fire off commands to get back the columns of the table. */
259 : /* -------------------------------------------------------------------- */
260 558 : hResult = OGRPG_PQexec(hPGConn, "BEGIN");
261 :
262 558 : if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK )
263 : {
264 558 : OGRPGClearResult( hResult );
265 :
266 : osCommand.Printf(
267 : "DECLARE mycursor CURSOR for "
268 : "SELECT DISTINCT a.attname, t.typname, a.attlen,"
269 : " format_type(a.atttypid,a.atttypmod), a.attnum "
270 : "FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n "
271 : "WHERE c.relname = %s "
272 : "AND a.attnum > 0 AND a.attrelid = c.oid "
273 : "AND a.atttypid = t.oid "
274 : "AND c.relnamespace=n.oid "
275 : "%s"
276 : "ORDER BY a.attnum",
277 558 : pszEscapedTableNameSingleQuote, osSchemaClause.c_str());
278 :
279 558 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str() );
280 : }
281 :
282 558 : if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK )
283 : {
284 558 : OGRPGClearResult( hResult );
285 558 : hResult = OGRPG_PQexec(hPGConn, "FETCH ALL in mycursor" );
286 : }
287 :
288 558 : if( !hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK )
289 : {
290 0 : OGRPGClearResult( hResult );
291 :
292 : CPLError( CE_Failure, CPLE_AppDefined,
293 0 : "%s", PQerrorMessage(hPGConn) );
294 0 : return NULL;
295 : }
296 :
297 558 : if( PQntuples(hResult) == 0 )
298 : {
299 120 : OGRPGClearResult( hResult );
300 :
301 120 : hResult = OGRPG_PQexec(hPGConn, "CLOSE mycursor");
302 120 : OGRPGClearResult( hResult );
303 :
304 120 : hResult = OGRPG_PQexec(hPGConn, "COMMIT");
305 120 : OGRPGClearResult( hResult );
306 :
307 : CPLError( CE_Failure, CPLE_AppDefined,
308 : "No field definitions found for '%s', is it a table?",
309 120 : pszTableName );
310 120 : return NULL;
311 : }
312 :
313 : /* -------------------------------------------------------------------- */
314 : /* Parse the returned table information. */
315 : /* -------------------------------------------------------------------- */
316 438 : OGRFeatureDefn *poDefn = new OGRFeatureDefn( osDefnName );
317 : int iRecord;
318 :
319 438 : poDefn->Reference();
320 :
321 438 : for( iRecord = 0; iRecord < PQntuples(hResult); iRecord++ )
322 : {
323 2328 : const char *pszType = NULL;
324 2328 : const char *pszFormatType = NULL;
325 2328 : OGRFieldDefn oField( PQgetvalue( hResult, iRecord, 0 ), OFTString);
326 :
327 2328 : pszType = PQgetvalue(hResult, iRecord, 1 );
328 2328 : pszFormatType = PQgetvalue(hResult,iRecord,3);
329 :
330 2328 : if( EQUAL(oField.GetNameRef(),osPrimaryKey) )
331 : {
332 392 : bHasFid = TRUE;
333 392 : pszFIDColumn = CPLStrdup(oField.GetNameRef());
334 392 : CPLDebug("PG","Using column '%s' as FID for table '%s'", pszFIDColumn, pszTableName );
335 392 : continue;
336 : }
337 1936 : else if( EQUAL(pszType,"geometry") )
338 : {
339 256 : bHasPostGISGeometry = TRUE;
340 256 : if (!pszGeomColumn)
341 72 : pszGeomColumn = CPLStrdup(oField.GetNameRef());
342 256 : continue;
343 : }
344 1680 : else if( EQUAL(pszType,"geography") )
345 : {
346 6 : bHasPostGISGeography = TRUE;
347 6 : if (!pszGeomColumn)
348 2 : pszGeomColumn = CPLStrdup(oField.GetNameRef());
349 6 : continue;
350 : }
351 1674 : else if( EQUAL(oField.GetNameRef(),"WKB_GEOMETRY") )
352 : {
353 126 : if (!pszGeomColumn)
354 : {
355 126 : bHasWkb = TRUE;
356 126 : pszGeomColumn = CPLStrdup(oField.GetNameRef());
357 126 : if( EQUAL(pszType,"OID") )
358 0 : bWkbAsOid = TRUE;
359 : }
360 126 : continue;
361 : }
362 :
363 1548 : if( EQUAL(pszType,"text") )
364 : {
365 22 : oField.SetType( OFTString );
366 : }
367 1634 : else if( EQUAL(pszType,"_bpchar") ||
368 : EQUAL(pszType,"_varchar") ||
369 : EQUAL(pszType,"_text"))
370 : {
371 108 : oField.SetType( OFTStringList );
372 : }
373 1886 : else if( EQUAL(pszType,"bpchar") || EQUAL(pszType,"varchar") )
374 : {
375 : int nWidth;
376 :
377 468 : nWidth = atoi(PQgetvalue(hResult,iRecord,2));
378 468 : if( nWidth == -1 )
379 : {
380 468 : if( EQUALN(pszFormatType,"character(",10) )
381 80 : nWidth = atoi(pszFormatType+10);
382 388 : else if( EQUALN(pszFormatType,"character varying(",18) )
383 164 : nWidth = atoi(pszFormatType+18);
384 : else
385 224 : nWidth = 0;
386 : }
387 468 : oField.SetType( OFTString );
388 468 : oField.SetWidth( nWidth );
389 : }
390 950 : else if( EQUAL(pszType,"bool") )
391 : {
392 20 : oField.SetType( OFTInteger );
393 20 : oField.SetWidth( 1 );
394 : }
395 930 : else if( EQUAL(pszType,"numeric") )
396 : {
397 128 : const char *pszFormatName = PQgetvalue(hResult,iRecord,3);
398 128 : const char *pszPrecision = strstr(pszFormatName,",");
399 128 : int nWidth, nPrecision = 0;
400 :
401 128 : nWidth = atoi(pszFormatName + 8);
402 128 : if( pszPrecision != NULL )
403 128 : nPrecision = atoi(pszPrecision+1);
404 :
405 128 : if( nPrecision == 0 )
406 : {
407 : // FIXME : If nWidth > 10, OFTInteger may not be large enough */
408 68 : oField.SetType( OFTInteger );
409 : }
410 : else
411 60 : oField.SetType( OFTReal );
412 :
413 128 : oField.SetWidth( nWidth );
414 128 : oField.SetPrecision( nPrecision );
415 : }
416 802 : else if( EQUAL(pszFormatType,"integer[]") )
417 : {
418 36 : oField.SetType( OFTIntegerList );
419 : }
420 880 : else if( EQUAL(pszFormatType, "float[]") ||
421 : EQUAL(pszFormatType, "real[]") ||
422 : EQUAL(pszFormatType, "double precision[]") )
423 : {
424 114 : oField.SetType( OFTRealList );
425 : }
426 652 : else if( EQUAL(pszType,"int2") )
427 : {
428 20 : oField.SetType( OFTInteger );
429 20 : oField.SetWidth( 5 );
430 : }
431 632 : else if( EQUAL(pszType,"int8") )
432 : {
433 : /* FIXME: OFTInteger can not handle 64bit integers */
434 20 : oField.SetType( OFTInteger );
435 : }
436 612 : else if( EQUALN(pszType,"int",3) )
437 : {
438 158 : oField.SetType( OFTInteger );
439 : }
440 664 : else if( EQUALN(pszType,"float",5) ||
441 : EQUALN(pszType,"double",6) ||
442 : EQUAL(pszType,"real") )
443 : {
444 210 : oField.SetType( OFTReal );
445 : }
446 244 : else if( EQUALN(pszType, "timestamp",9) )
447 : {
448 96 : oField.SetType( OFTDateTime );
449 : }
450 148 : else if( EQUALN(pszType, "date",4) )
451 : {
452 52 : oField.SetType( OFTDate );
453 : }
454 96 : else if( EQUALN(pszType, "time",4) )
455 : {
456 52 : oField.SetType( OFTTime );
457 : }
458 44 : else if( EQUAL(pszType,"bytea") )
459 : {
460 36 : oField.SetType( OFTBinary );
461 : }
462 :
463 : else
464 : {
465 : CPLDebug( "PG", "Field %s is of unknown format type %s (type=%s).",
466 8 : oField.GetNameRef(), pszFormatType, pszType );
467 : }
468 :
469 1548 : poDefn->AddFieldDefn( &oField );
470 : }
471 :
472 438 : OGRPGClearResult( hResult );
473 :
474 438 : hResult = OGRPG_PQexec(hPGConn, "CLOSE mycursor");
475 438 : OGRPGClearResult( hResult );
476 :
477 438 : hResult = OGRPG_PQexec(hPGConn, "COMMIT");
478 438 : OGRPGClearResult( hResult );
479 :
480 : /* If geometry type, SRID, etc... have always been set by SetGeometryInformation() */
481 : /* no need to issue a new SQL query. Just record the geom type in the layer definition */
482 438 : if (bGeometryInformationSet)
483 : {
484 : ;
485 : }
486 : // get layer geometry type (for PostGIS dataset)
487 372 : else if ( bHasPostGISGeometry || bHasPostGISGeography )
488 : {
489 : /* Get the geometry type and dimensions from the table, or */
490 : /* from its parents if it is a derived table, or from the parent of the parent, etc.. */
491 78 : int bGoOn = TRUE;
492 :
493 236 : while(bGoOn)
494 : {
495 : osCommand.Printf(
496 : "SELECT type, coord_dimension%s FROM %s WHERE f_table_name='%s'",
497 : (nSRSId == UNDETERMINED_SRID) ? ", srid" : "",
498 : (bHasPostGISGeometry) ? "geometry_columns" : "geography_columns",
499 80 : (pszSqlGeomParentTableName) ? pszSqlGeomParentTableName : pszTableName);
500 80 : if (pszGeomColumn)
501 : {
502 : osCommand += CPLString().Printf(" AND %s='%s'",
503 : (bHasPostGISGeometry) ? "f_geometry_column" : "f_geography_column",
504 80 : pszGeomColumn);
505 : }
506 80 : if (pszSchemaName)
507 : {
508 80 : osCommand += CPLString().Printf(" AND f_table_schema='%s'", pszSchemaName);
509 : }
510 :
511 80 : hResult = OGRPG_PQexec(hPGConn,osCommand);
512 :
513 80 : if ( hResult && PQntuples(hResult) == 1 && !PQgetisnull(hResult,0,0) )
514 : {
515 78 : char * pszType = PQgetvalue(hResult,0,0);
516 :
517 78 : nCoordDimension = MAX(2,MIN(3,atoi(PQgetvalue(hResult,0,1))));
518 :
519 78 : if (nSRSId == UNDETERMINED_SRID)
520 18 : nSRSId = atoi(PQgetvalue(hResult,0,2));
521 :
522 : SetGeometryInformation(pszType, nCoordDimension, nSRSId,
523 78 : (bHasPostGISGeometry) ? GEOM_TYPE_GEOMETRY : GEOM_TYPE_GEOGRAPHY);
524 :
525 78 : bGoOn = FALSE;
526 : }
527 : else
528 : {
529 : CPLString osEscapedTableNameSingleQuote = OGRPGEscapeString(hPGConn,
530 2 : (pszSqlGeomParentTableName) ? pszSqlGeomParentTableName : pszTableName, -1, "");
531 2 : const char* pszEscapedTableNameSingleQuote = osEscapedTableNameSingleQuote.c_str();
532 :
533 : /* Fetch the name of the parent table */
534 2 : if (pszSchemaName)
535 : {
536 : osCommand.Printf("SELECT pg_class.relname FROM pg_class WHERE oid = "
537 : "(SELECT pg_inherits.inhparent FROM pg_inherits WHERE inhrelid = "
538 : "(SELECT c.oid FROM pg_class c, pg_namespace n WHERE c.relname = %s AND c.relnamespace=n.oid AND n.nspname = '%s'))",
539 2 : pszEscapedTableNameSingleQuote, pszSchemaName );
540 : }
541 : else
542 : {
543 : osCommand.Printf("SELECT pg_class.relname FROM pg_class WHERE oid = "
544 : "(SELECT pg_inherits.inhparent FROM pg_inherits WHERE inhrelid = "
545 : "(SELECT pg_class.oid FROM pg_class WHERE relname = %s))",
546 0 : pszEscapedTableNameSingleQuote );
547 : }
548 :
549 2 : OGRPGClearResult( hResult );
550 2 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str() );
551 :
552 2 : if ( hResult && PQntuples( hResult ) == 1 && !PQgetisnull( hResult,0,0 ) )
553 : {
554 2 : CPLFree(pszSqlGeomParentTableName);
555 2 : pszSqlGeomParentTableName = CPLStrdup( PQgetvalue(hResult,0,0) );
556 : }
557 : else
558 : {
559 : /* No more parent : stop recursion */
560 0 : bGoOn = FALSE;
561 2 : }
562 : }
563 :
564 80 : OGRPGClearResult( hResult );
565 : }
566 :
567 78 : if (nSRSId == UNDETERMINED_SRID)
568 0 : nSRSId = poDS->GetUndefinedSRID();
569 : }
570 216 : else if (pszGeomColumn == NULL)
571 : {
572 90 : nGeomType = wkbNone;
573 : }
574 :
575 438 : poDefn->SetGeomType( nGeomType );
576 :
577 438 : return poDefn;
578 : }
579 :
580 : /************************************************************************/
581 : /* SetSpatialFilter() */
582 : /************************************************************************/
583 :
584 72 : void OGRPGTableLayer::SetSpatialFilter( OGRGeometry * poGeomIn )
585 :
586 : {
587 72 : GetLayerDefn();
588 :
589 72 : if( InstallFilter( poGeomIn ) )
590 : {
591 30 : BuildWhere();
592 :
593 30 : ResetReading();
594 : }
595 72 : }
596 :
597 : /************************************************************************/
598 : /* BuildWhere() */
599 : /* */
600 : /* Build the WHERE statement appropriate to the current set of */
601 : /* criteria (spatial and attribute queries). */
602 : /************************************************************************/
603 :
604 176 : void OGRPGTableLayer::BuildWhere()
605 :
606 : {
607 176 : osWHERE = "";
608 :
609 204 : if( m_poFilterGeom != NULL && (bHasPostGISGeometry || bHasPostGISGeography) )
610 : {
611 : char szBox3D_1[128];
612 : char szBox3D_2[128];
613 : char* pszComma;
614 16 : OGREnvelope sEnvelope;
615 :
616 16 : m_poFilterGeom->getEnvelope( &sEnvelope );
617 16 : snprintf(szBox3D_1, sizeof(szBox3D_1), "%.12f %.12f", sEnvelope.MinX, sEnvelope.MinY);
618 32 : while((pszComma = strchr(szBox3D_1, ',')) != NULL)
619 0 : *pszComma = '.';
620 16 : snprintf(szBox3D_2, sizeof(szBox3D_2), "%.12f %.12f", sEnvelope.MaxX, sEnvelope.MaxY);
621 32 : while((pszComma = strchr(szBox3D_2, ',')) != NULL)
622 0 : *pszComma = '.';
623 : osWHERE.Printf("WHERE %s && %s('BOX3D(%s, %s)'::box3d,%d) ",
624 : OGRPGEscapeColumnName(pszGeomColumn).c_str(),
625 : (poDS->sPostGISVersion.nMajor >= 2) ? "ST_SetSRID" : "SetSRID",
626 16 : szBox3D_1, szBox3D_2, nSRSId );
627 : }
628 :
629 176 : if( strlen(osQuery) > 0 )
630 : {
631 92 : if( strlen(osWHERE) == 0 )
632 : {
633 90 : osWHERE.Printf( "WHERE %s ", osQuery.c_str() );
634 : }
635 : else
636 : {
637 2 : osWHERE += "AND (";
638 2 : osWHERE += osQuery;
639 2 : osWHERE += ")";
640 : }
641 : }
642 176 : }
643 :
644 : /************************************************************************/
645 : /* BuildFullQueryStatement() */
646 : /************************************************************************/
647 :
648 852 : void OGRPGTableLayer::BuildFullQueryStatement()
649 :
650 : {
651 852 : if( pszQueryStatement != NULL )
652 : {
653 414 : CPLFree( pszQueryStatement );
654 414 : pszQueryStatement = NULL;
655 : }
656 :
657 852 : CPLString osFields = BuildFields();
658 :
659 : pszQueryStatement = (char *)
660 : CPLMalloc(strlen(osFields)+strlen(osWHERE)
661 852 : +strlen(pszSqlTableName) + 40);
662 : sprintf( pszQueryStatement,
663 : "SELECT %s FROM %s %s",
664 852 : osFields.c_str(), pszSqlTableName, osWHERE.c_str() );
665 852 : }
666 :
667 : /************************************************************************/
668 : /* ResetReading() */
669 : /************************************************************************/
670 :
671 852 : void OGRPGTableLayer::ResetReading()
672 :
673 : {
674 852 : GetLayerDefn();
675 :
676 852 : bUseCopy = USE_COPY_UNSET;
677 :
678 852 : BuildFullQueryStatement();
679 :
680 852 : OGRPGLayer::ResetReading();
681 852 : }
682 :
683 : /************************************************************************/
684 : /* GetNextFeature() */
685 : /************************************************************************/
686 :
687 2044 : OGRFeature *OGRPGTableLayer::GetNextFeature()
688 :
689 : {
690 2044 : GetLayerDefn();
691 :
692 152 : for( ; TRUE; )
693 : {
694 : OGRFeature *poFeature;
695 :
696 2196 : poFeature = GetNextRawFeature();
697 2196 : if( poFeature == NULL )
698 74 : return NULL;
699 :
700 : /* We just have to look if there is a geometry filter */
701 : /* If there's a PostGIS geometry column, the spatial filter */
702 : /* is already taken into account in the select request */
703 : /* The attribute filter is always taken into account by the select request */
704 2122 : if( m_poFilterGeom == NULL
705 : || bHasPostGISGeometry
706 : || bHasPostGISGeography
707 : || FilterGeometry( poFeature->GetGeometryRef() ) )
708 1970 : return poFeature;
709 :
710 152 : delete poFeature;
711 : }
712 : }
713 :
714 : /************************************************************************/
715 : /* BuildFields() */
716 : /* */
717 : /* Build list of fields to fetch, performing any required */
718 : /* transformations (such as on geometry). */
719 : /************************************************************************/
720 :
721 922 : CPLString OGRPGTableLayer::BuildFields()
722 :
723 : {
724 922 : int i = 0;
725 922 : CPLString osFieldList;
726 :
727 922 : if( bHasFid && poFeatureDefn->GetFieldIndex( pszFIDColumn ) == -1 )
728 : {
729 838 : osFieldList += OGRPGEscapeColumnName(pszFIDColumn);
730 : }
731 :
732 922 : if( pszGeomColumn )
733 : {
734 808 : if( strlen(osFieldList) > 0 )
735 750 : osFieldList += ", ";
736 :
737 808 : if( bHasPostGISGeometry )
738 : {
739 462 : if ( poDS->bUseBinaryCursor )
740 : {
741 2 : osFieldList += OGRPGEscapeColumnName(pszGeomColumn);
742 : }
743 460 : else if (CSLTestBoolean(CPLGetConfigOption("PG_USE_BASE64", "NO")) &&
744 : nCoordDimension != 4 /* we don't know how to decode 4-dim EWKB for now */)
745 : {
746 2 : if (poDS->sPostGISVersion.nMajor >= 2)
747 0 : osFieldList += "encode(ST_AsEWKB(";
748 : else
749 2 : osFieldList += "encode(AsEWKB(";
750 2 : osFieldList += OGRPGEscapeColumnName(pszGeomColumn);
751 2 : osFieldList += "), 'base64') AS EWKBBase64";
752 : }
753 458 : else if ( !CSLTestBoolean(CPLGetConfigOption("PG_USE_TEXT", "NO")) &&
754 : nCoordDimension != 4 && /* we don't know how to decode 4-dim EWKB for now */
755 : /* perhaps works also for older version, but I didn't check */
756 : (poDS->sPostGISVersion.nMajor > 1 ||
757 : (poDS->sPostGISVersion.nMajor == 1 && poDS->sPostGISVersion.nMinor >= 1)) )
758 : {
759 : /* This will return EWKB in an hex encoded form */
760 426 : osFieldList += OGRPGEscapeColumnName(pszGeomColumn);
761 : }
762 32 : else if ( poDS->sPostGISVersion.nMajor >= 1 )
763 : {
764 32 : if (poDS->sPostGISVersion.nMajor >= 2)
765 0 : osFieldList += "ST_AsEWKT(";
766 : else
767 32 : osFieldList += "AsEWKT(";
768 32 : osFieldList += OGRPGEscapeColumnName(pszGeomColumn);
769 32 : osFieldList += ")";
770 : }
771 : else
772 : {
773 0 : osFieldList += "AsText(";
774 0 : osFieldList += OGRPGEscapeColumnName(pszGeomColumn);
775 0 : osFieldList += ")";
776 : }
777 : }
778 346 : else if ( bHasPostGISGeography )
779 : {
780 8 : if ( poDS->bUseBinaryCursor )
781 : {
782 0 : osFieldList += "ST_AsBinary(";
783 0 : osFieldList += OGRPGEscapeColumnName(pszGeomColumn);
784 0 : osFieldList += ")";
785 : }
786 8 : else if (CSLTestBoolean(CPLGetConfigOption("PG_USE_BASE64", "NO")))
787 : {
788 0 : osFieldList += "encode(ST_AsBinary(";
789 0 : osFieldList += OGRPGEscapeColumnName(pszGeomColumn);
790 0 : osFieldList += "), 'base64') AS BinaryBase64";
791 : }
792 8 : else if ( !CSLTestBoolean(CPLGetConfigOption("PG_USE_TEXT", "NO")) )
793 : {
794 8 : osFieldList += OGRPGEscapeColumnName(pszGeomColumn);
795 : }
796 : else
797 : {
798 0 : osFieldList += "ST_AsText(";
799 0 : osFieldList += OGRPGEscapeColumnName(pszGeomColumn);
800 0 : osFieldList += ")";
801 : }
802 : }
803 : else
804 : {
805 338 : osFieldList += OGRPGEscapeColumnName(pszGeomColumn);
806 : }
807 : }
808 :
809 4454 : for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
810 : {
811 3532 : const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
812 :
813 3532 : if( strlen(osFieldList) > 0 )
814 3506 : osFieldList += ", ";
815 :
816 : /* With a binary cursor, it is not possible to get the time zone */
817 : /* of a timestamptz column. So we fallback to asking it in text mode */
818 3532 : if ( poDS->bUseBinaryCursor &&
819 : poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDateTime)
820 : {
821 8 : osFieldList += "CAST (";
822 8 : osFieldList += OGRPGEscapeColumnName(pszName);
823 8 : osFieldList += " AS text)";
824 : }
825 : else
826 : {
827 3524 : osFieldList += OGRPGEscapeColumnName(pszName);
828 : }
829 : }
830 :
831 0 : return osFieldList;
832 : }
833 :
834 : /************************************************************************/
835 : /* SetAttributeFilter() */
836 : /************************************************************************/
837 :
838 146 : OGRErr OGRPGTableLayer::SetAttributeFilter( const char *pszQuery )
839 :
840 : {
841 146 : GetLayerDefn();
842 :
843 146 : if( pszQuery == NULL )
844 54 : osQuery = "";
845 : else
846 92 : osQuery = pszQuery;
847 :
848 146 : BuildWhere();
849 :
850 146 : ResetReading();
851 :
852 146 : return OGRERR_NONE;
853 : }
854 :
855 : /************************************************************************/
856 : /* DeleteFeature() */
857 : /************************************************************************/
858 :
859 12 : OGRErr OGRPGTableLayer::DeleteFeature( long nFID )
860 :
861 : {
862 12 : PGconn *hPGConn = poDS->GetPGConn();
863 12 : PGresult *hResult = NULL;
864 12 : CPLString osCommand;
865 :
866 12 : GetLayerDefn();
867 :
868 12 : if( !bUpdateAccess )
869 : {
870 : CPLError( CE_Failure, CPLE_NotSupported,
871 : UNSUPPORTED_OP_READ_ONLY,
872 0 : "DeleteFeature");
873 0 : return OGRERR_FAILURE;
874 : }
875 :
876 : /* -------------------------------------------------------------------- */
877 : /* We can only delete features if we have a well defined FID */
878 : /* column to target. */
879 : /* -------------------------------------------------------------------- */
880 12 : if( !bHasFid )
881 : {
882 : CPLError( CE_Failure, CPLE_AppDefined,
883 : "DeleteFeature(%ld) failed. Unable to delete features in tables without\n"
884 : "a recognised FID column.",
885 0 : nFID );
886 0 : return OGRERR_FAILURE;
887 :
888 : }
889 :
890 : /* -------------------------------------------------------------------- */
891 : /* Form the statement to drop the record. */
892 : /* -------------------------------------------------------------------- */
893 : osCommand.Printf( "DELETE FROM %s WHERE %s = %ld",
894 12 : pszSqlTableName, OGRPGEscapeColumnName(pszFIDColumn).c_str(), nFID );
895 :
896 : /* -------------------------------------------------------------------- */
897 : /* Execute the delete. */
898 : /* -------------------------------------------------------------------- */
899 : OGRErr eErr;
900 :
901 12 : eErr = poDS->SoftStartTransaction();
902 12 : if( eErr != OGRERR_NONE )
903 0 : return eErr;
904 :
905 12 : hResult = OGRPG_PQexec(hPGConn, osCommand);
906 :
907 12 : if( PQresultStatus(hResult) != PGRES_COMMAND_OK )
908 : {
909 : CPLError( CE_Failure, CPLE_AppDefined,
910 : "DeleteFeature() DELETE statement failed.\n%s",
911 0 : PQerrorMessage(hPGConn) );
912 :
913 0 : OGRPGClearResult( hResult );
914 :
915 0 : poDS->SoftRollback();
916 0 : eErr = OGRERR_FAILURE;
917 : }
918 : else
919 : {
920 12 : OGRPGClearResult( hResult );
921 :
922 12 : eErr = poDS->SoftCommit();
923 : }
924 :
925 12 : return eErr;
926 : }
927 :
928 : /************************************************************************/
929 : /* AppendFieldValue() */
930 : /* */
931 : /* Used by CreateFeatureViaInsert() and SetFeature() to format a */
932 : /* non-empty field value */
933 : /************************************************************************/
934 :
935 10942 : void OGRPGTableLayer::AppendFieldValue(PGconn *hPGConn, CPLString& osCommand,
936 : OGRFeature* poFeature, int i)
937 : {
938 10942 : int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
939 :
940 : // We need special formatting for integer list values.
941 10942 : if( nOGRFieldType == OFTIntegerList )
942 : {
943 4 : int nCount, nOff = 0, j;
944 4 : const int *panItems = poFeature->GetFieldAsIntegerList(i,&nCount);
945 4 : char *pszNeedToFree = NULL;
946 :
947 4 : pszNeedToFree = (char *) CPLMalloc(nCount * 13 + 10);
948 4 : strcpy( pszNeedToFree, "'{" );
949 12 : for( j = 0; j < nCount; j++ )
950 : {
951 8 : if( j != 0 )
952 4 : strcat( pszNeedToFree+nOff, "," );
953 :
954 8 : nOff += strlen(pszNeedToFree+nOff);
955 8 : sprintf( pszNeedToFree+nOff, "%d", panItems[j] );
956 : }
957 4 : strcat( pszNeedToFree+nOff, "}'" );
958 :
959 4 : osCommand += pszNeedToFree;
960 4 : CPLFree(pszNeedToFree);
961 :
962 4 : return;
963 : }
964 :
965 : // We need special formatting for real list values.
966 10938 : else if( nOGRFieldType == OFTRealList )
967 : {
968 24 : int nCount, nOff = 0, j;
969 24 : const double *padfItems =poFeature->GetFieldAsDoubleList(i,&nCount);
970 24 : char *pszNeedToFree = NULL;
971 :
972 24 : pszNeedToFree = (char *) CPLMalloc(nCount * 40 + 10);
973 24 : strcpy( pszNeedToFree, "'{" );
974 72 : for( j = 0; j < nCount; j++ )
975 : {
976 48 : if( j != 0 )
977 24 : strcat( pszNeedToFree+nOff, "," );
978 :
979 48 : nOff += strlen(pszNeedToFree+nOff);
980 : //Check for special values. They need to be quoted.
981 48 : if( CPLIsNan(padfItems[j]) )
982 8 : sprintf( pszNeedToFree+nOff, "NaN" );
983 40 : else if( CPLIsInf(padfItems[j]) )
984 24 : sprintf( pszNeedToFree+nOff, (padfItems[j] > 0) ? "Infinity" : "-Infinity" );
985 : else
986 16 : sprintf( pszNeedToFree+nOff, "%.16g", padfItems[j] );
987 :
988 48 : char* pszComma = strchr(pszNeedToFree+nOff, ',');
989 48 : if (pszComma)
990 0 : *pszComma = '.';
991 : }
992 24 : strcat( pszNeedToFree+nOff, "}'" );
993 :
994 24 : osCommand += pszNeedToFree;
995 24 : CPLFree(pszNeedToFree);
996 :
997 24 : return;
998 : }
999 :
1000 : // We need special formatting for string list values.
1001 10914 : else if( nOGRFieldType == OFTStringList )
1002 : {
1003 12 : char **papszItems = poFeature->GetFieldAsStringList(i);
1004 :
1005 12 : osCommand += OGRPGEscapeStringList(hPGConn, papszItems, TRUE);
1006 :
1007 12 : return;
1008 : }
1009 :
1010 : // Binary formatting
1011 10902 : else if( nOGRFieldType == OFTBinary )
1012 : {
1013 4 : if (poDS->bUseEscapeStringSyntax)
1014 4 : osCommand += "E";
1015 :
1016 4 : osCommand += "'";
1017 :
1018 4 : int nLen = 0;
1019 4 : GByte* pabyData = poFeature->GetFieldAsBinary( i, &nLen );
1020 4 : char* pszBytea = GByteArrayToBYTEA( pabyData, nLen);
1021 :
1022 4 : osCommand += pszBytea;
1023 :
1024 4 : CPLFree(pszBytea);
1025 4 : osCommand += "'";
1026 :
1027 4 : return;
1028 : }
1029 :
1030 : // Flag indicating NULL or not-a-date date value
1031 : // e.g. 0000-00-00 - there is no year 0
1032 10898 : OGRBoolean bIsDateNull = FALSE;
1033 :
1034 10898 : const char *pszStrValue = poFeature->GetFieldAsString(i);
1035 :
1036 : // Check if date is NULL: 0000-00-00
1037 10898 : if( nOGRFieldType == OFTDate )
1038 : {
1039 4 : if( EQUALN( pszStrValue, "0000", 4 ) )
1040 : {
1041 0 : pszStrValue = "NULL";
1042 0 : bIsDateNull = TRUE;
1043 : }
1044 : }
1045 10894 : else if ( nOGRFieldType == OFTReal )
1046 : {
1047 388 : char* pszComma = strchr((char*)pszStrValue, ',');
1048 388 : if (pszComma)
1049 0 : *pszComma = '.';
1050 : //Check for special values. They need to be quoted.
1051 388 : double dfVal = poFeature->GetFieldAsDouble(i);
1052 388 : if( CPLIsNan(dfVal) )
1053 4 : pszStrValue = "'NaN'";
1054 384 : else if( CPLIsInf(dfVal) )
1055 12 : pszStrValue = (dfVal > 0) ? "'Infinity'" : "'-Infinity'";
1056 : }
1057 :
1058 13276 : if( nOGRFieldType != OFTInteger && nOGRFieldType != OFTReal
1059 : && !bIsDateNull )
1060 : {
1061 : osCommand += OGRPGEscapeString(hPGConn, pszStrValue,
1062 : poFeatureDefn->GetFieldDefn(i)->GetWidth(),
1063 2378 : poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
1064 : }
1065 : else
1066 : {
1067 8520 : osCommand += pszStrValue;
1068 : }
1069 : }
1070 :
1071 : /************************************************************************/
1072 : /* SetFeature() */
1073 : /* */
1074 : /* SetFeature() is implemented by an UPDATE SQL command */
1075 : /************************************************************************/
1076 :
1077 4026 : OGRErr OGRPGTableLayer::SetFeature( OGRFeature *poFeature )
1078 :
1079 : {
1080 4026 : PGconn *hPGConn = poDS->GetPGConn();
1081 4026 : PGresult *hResult = NULL;
1082 4026 : CPLString osCommand;
1083 4026 : int i = 0;
1084 4026 : int bNeedComma = FALSE;
1085 4026 : OGRErr eErr = OGRERR_FAILURE;
1086 :
1087 4026 : GetLayerDefn();
1088 :
1089 4026 : if( !bUpdateAccess )
1090 : {
1091 : CPLError( CE_Failure, CPLE_NotSupported,
1092 : UNSUPPORTED_OP_READ_ONLY,
1093 0 : "SetFeature");
1094 0 : return OGRERR_FAILURE;
1095 : }
1096 :
1097 4026 : if( NULL == poFeature )
1098 : {
1099 : CPLError( CE_Failure, CPLE_AppDefined,
1100 0 : "NULL pointer to OGRFeature passed to SetFeature()." );
1101 0 : return eErr;
1102 : }
1103 :
1104 4026 : if( poFeature->GetFID() == OGRNullFID )
1105 : {
1106 : CPLError( CE_Failure, CPLE_AppDefined,
1107 0 : "FID required on features given to SetFeature()." );
1108 0 : return eErr;
1109 : }
1110 :
1111 4026 : if( !bHasFid )
1112 : {
1113 : CPLError( CE_Failure, CPLE_AppDefined,
1114 : "Unable to update features in tables without\n"
1115 0 : "a recognised FID column.");
1116 0 : return eErr;
1117 :
1118 : }
1119 :
1120 4026 : eErr = poDS->SoftStartTransaction();
1121 4026 : if( eErr != OGRERR_NONE )
1122 : {
1123 0 : return eErr;
1124 : }
1125 :
1126 : /* -------------------------------------------------------------------- */
1127 : /* Form the UPDATE command. */
1128 : /* -------------------------------------------------------------------- */
1129 4026 : osCommand.Printf( "UPDATE %s SET ", pszSqlTableName );
1130 :
1131 : /* Set the geometry */
1132 4026 : if( bHasWkb )
1133 : {
1134 2012 : osCommand += "WKB_GEOMETRY = ";
1135 2012 : if ( poFeature->GetGeometryRef() != NULL )
1136 : {
1137 10 : if( !bWkbAsOid )
1138 : {
1139 10 : char *pszBytea = GeometryToBYTEA( poFeature->GetGeometryRef() );
1140 :
1141 10 : if( pszBytea != NULL )
1142 : {
1143 10 : if (poDS->bUseEscapeStringSyntax)
1144 10 : osCommand += "E";
1145 10 : osCommand = osCommand + "'" + pszBytea + "'";
1146 10 : CPLFree( pszBytea );
1147 : }
1148 : else
1149 0 : osCommand += "NULL";
1150 : }
1151 : else
1152 : {
1153 0 : Oid oid = GeometryToOID( poFeature->GetGeometryRef() );
1154 :
1155 0 : if( oid != 0 )
1156 : {
1157 0 : osCommand += CPLString().Printf( "'%d' ", oid );
1158 : }
1159 : else
1160 0 : osCommand += "NULL";
1161 : }
1162 : }
1163 : else
1164 2002 : osCommand += "NULL";
1165 2012 : bNeedComma = TRUE;
1166 : }
1167 2014 : else if( bHasPostGISGeometry || bHasPostGISGeography )
1168 : {
1169 2014 : osCommand = osCommand + OGRPGEscapeColumnName(pszGeomColumn) + " = ";
1170 2014 : OGRGeometry *poGeom = NULL;
1171 :
1172 2014 : if( poFeature->GetGeometryRef() != NULL )
1173 : {
1174 12 : poGeom = (OGRGeometry *) poFeature->GetGeometryRef();
1175 :
1176 12 : poGeom->closeRings();
1177 12 : poGeom->setCoordinateDimension( nCoordDimension );
1178 :
1179 : }
1180 :
1181 2014 : if ( !CSLTestBoolean(CPLGetConfigOption("PG_USE_TEXT", "NO")) )
1182 : {
1183 2014 : if ( poGeom != NULL )
1184 : {
1185 12 : char* pszHexEWKB = GeometryToHex( poGeom, nSRSId );
1186 12 : if ( bHasPostGISGeography )
1187 2 : osCommand += CPLString().Printf("'%s'::GEOGRAPHY", pszHexEWKB);
1188 : else
1189 10 : osCommand += CPLString().Printf("'%s'::GEOMETRY", pszHexEWKB);
1190 12 : OGRFree( pszHexEWKB );
1191 : }
1192 : else
1193 2002 : osCommand += "NULL";
1194 : }
1195 : else
1196 : {
1197 0 : char *pszWKT = NULL;
1198 :
1199 0 : if (poGeom != NULL)
1200 0 : poGeom->exportToWkt( &pszWKT );
1201 :
1202 0 : if( pszWKT != NULL )
1203 : {
1204 0 : if( bHasPostGISGeography )
1205 : osCommand +=
1206 : CPLString().Printf(
1207 0 : "ST_GeographyFromText('SRID=%d;%s'::TEXT) ", nSRSId, pszWKT );
1208 0 : else if( poDS->sPostGISVersion.nMajor >= 1 )
1209 : osCommand +=
1210 : CPLString().Printf(
1211 0 : "GeomFromEWKT('SRID=%d;%s'::TEXT) ", nSRSId, pszWKT );
1212 : else
1213 : osCommand +=
1214 : CPLString().Printf(
1215 0 : "GeometryFromText('%s'::TEXT,%d) ", pszWKT, nSRSId );
1216 0 : OGRFree( pszWKT );
1217 : }
1218 : else
1219 0 : osCommand += "NULL";
1220 :
1221 : }
1222 2014 : bNeedComma = TRUE;
1223 : }
1224 :
1225 8148 : for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
1226 : {
1227 4122 : if( bNeedComma )
1228 4122 : osCommand += ", ";
1229 : else
1230 0 : bNeedComma = TRUE;
1231 :
1232 : osCommand = osCommand
1233 4122 : + OGRPGEscapeColumnName(poFeatureDefn->GetFieldDefn(i)->GetNameRef()) + " = ";
1234 :
1235 4122 : if( !poFeature->IsFieldSet( i ) )
1236 : {
1237 56 : osCommand += "NULL";
1238 : }
1239 : else
1240 : {
1241 4066 : AppendFieldValue(hPGConn, osCommand, poFeature, i);
1242 : }
1243 : }
1244 :
1245 : /* Add the WHERE clause */
1246 4026 : osCommand += " WHERE ";
1247 4026 : osCommand = osCommand + OGRPGEscapeColumnName(pszFIDColumn) + " = ";
1248 4026 : osCommand += CPLString().Printf( "%ld ", poFeature->GetFID() );
1249 :
1250 : /* -------------------------------------------------------------------- */
1251 : /* Execute the update. */
1252 : /* -------------------------------------------------------------------- */
1253 4026 : hResult = OGRPG_PQexec(hPGConn, osCommand);
1254 4026 : if( PQresultStatus(hResult) != PGRES_COMMAND_OK )
1255 : {
1256 : CPLError( CE_Failure, CPLE_AppDefined,
1257 : "UPDATE command for feature %ld failed.\n%s\nCommand: %s",
1258 0 : poFeature->GetFID(), PQerrorMessage(hPGConn), osCommand.c_str() );
1259 :
1260 0 : OGRPGClearResult( hResult );
1261 :
1262 0 : poDS->SoftRollback();
1263 :
1264 0 : return OGRERR_FAILURE;
1265 : }
1266 :
1267 4026 : OGRPGClearResult( hResult );
1268 :
1269 4026 : return poDS->SoftCommit();
1270 : }
1271 :
1272 : /************************************************************************/
1273 : /* CreateFeature() */
1274 : /************************************************************************/
1275 :
1276 6406 : OGRErr OGRPGTableLayer::CreateFeature( OGRFeature *poFeature )
1277 : {
1278 6406 : GetLayerDefn();
1279 :
1280 6406 : if( !bUpdateAccess )
1281 : {
1282 : CPLError( CE_Failure, CPLE_NotSupported,
1283 : UNSUPPORTED_OP_READ_ONLY,
1284 0 : "CreateFeature");
1285 0 : return OGRERR_FAILURE;
1286 : }
1287 :
1288 6406 : if( NULL == poFeature )
1289 : {
1290 : CPLError( CE_Failure, CPLE_AppDefined,
1291 0 : "NULL pointer to OGRFeature passed to CreateFeature()." );
1292 0 : return OGRERR_FAILURE;
1293 : }
1294 :
1295 : // We avoid testing the config option too often.
1296 6406 : if( bUseCopy == USE_COPY_UNSET )
1297 142 : bUseCopy = CSLTestBoolean( CPLGetConfigOption( "PG_USE_COPY", "NO") );
1298 :
1299 6406 : if( !bUseCopy )
1300 : {
1301 6318 : return CreateFeatureViaInsert( poFeature );
1302 : }
1303 : else
1304 : {
1305 88 : if ( !bCopyActive )
1306 : {
1307 : /* This is a heuristics. If the first feature to be copied has a */
1308 : /* FID set (and that a FID column has been identified), then we will */
1309 : /* try to copy FID values from features. Otherwise, we will not */
1310 : /* do and assume that the FID column is an autoincremented column. */
1311 20 : StartCopy(poFeature->GetFID() != OGRNullFID);
1312 : }
1313 :
1314 88 : return CreateFeatureViaCopy( poFeature );
1315 : }
1316 : }
1317 :
1318 : /************************************************************************/
1319 : /* OGRPGEscapeColumnName( ) */
1320 : /************************************************************************/
1321 :
1322 50368 : CPLString OGRPGEscapeColumnName(const char* pszColumnName)
1323 : {
1324 50368 : CPLString osStr;
1325 :
1326 50368 : osStr += "\"";
1327 :
1328 : char ch;
1329 464202 : for(int i=0; (ch = pszColumnName[i]) != '\0'; i++)
1330 : {
1331 413834 : if (ch == '"')
1332 0 : osStr.append(1, ch);
1333 413834 : osStr.append(1, ch);
1334 : }
1335 :
1336 50368 : osStr += "\"";
1337 :
1338 0 : return osStr;
1339 : }
1340 :
1341 : /************************************************************************/
1342 : /* OGRPGEscapeString( ) */
1343 : /************************************************************************/
1344 :
1345 3100 : CPLString OGRPGEscapeString(PGconn *hPGConn,
1346 : const char* pszStrValue, int nMaxLength,
1347 : const char* pszFieldName)
1348 : {
1349 3100 : CPLString osCommand;
1350 :
1351 : /* We need to quote and escape string fields. */
1352 3100 : osCommand += "'";
1353 :
1354 3100 : int nSrcLen = strlen(pszStrValue);
1355 3100 : if (nMaxLength > 0 && nSrcLen > nMaxLength)
1356 : {
1357 : CPLDebug( "PG",
1358 : "Truncated %s field value, it was too long.",
1359 4 : pszFieldName );
1360 4 : nSrcLen = nMaxLength;
1361 :
1362 8 : while( nSrcLen > 0 && ((unsigned char *) pszStrValue)[nSrcLen-1] > 127 )
1363 : {
1364 0 : CPLDebug( "PG", "Backup to start of multi-byte character." );
1365 0 : nSrcLen--;
1366 : }
1367 : }
1368 :
1369 3100 : char* pszDestStr = (char*)CPLMalloc(2 * nSrcLen + 1);
1370 :
1371 : /* -------------------------------------------------------------------- */
1372 : /* PQescapeStringConn was introduced in PostgreSQL security releases */
1373 : /* 8.1.4, 8.0.8, 7.4.13, 7.3.15 */
1374 : /* PG_HAS_PQESCAPESTRINGCONN is added by a test in 'configure' */
1375 : /* so it is not set by default when building OGR for Win32 */
1376 : /* -------------------------------------------------------------------- */
1377 : #if defined(PG_HAS_PQESCAPESTRINGCONN)
1378 : int nError;
1379 3100 : PQescapeStringConn (hPGConn, pszDestStr, pszStrValue, nSrcLen, &nError);
1380 3100 : if (nError == 0)
1381 3100 : osCommand += pszDestStr;
1382 : else
1383 : CPLError(CE_Warning, CPLE_AppDefined,
1384 : "PQescapeString(): %s\n"
1385 : " input: '%s'\n"
1386 : " got: '%s'\n",
1387 : PQerrorMessage( hPGConn ),
1388 0 : pszStrValue, pszDestStr );
1389 : #else
1390 : PQescapeString(pszDestStr, pszStrValue, nSrcLen);
1391 : osCommand += pszDestStr;
1392 : #endif
1393 3100 : CPLFree(pszDestStr);
1394 :
1395 3100 : osCommand += "'";
1396 :
1397 0 : return osCommand;
1398 : }
1399 :
1400 :
1401 : /************************************************************************/
1402 : /* OGRPGEscapeStringList( ) */
1403 : /************************************************************************/
1404 :
1405 24 : static CPLString OGRPGEscapeStringList(PGconn *hPGConn,
1406 : char** papszItems, int bForInsertOrUpdate)
1407 : {
1408 24 : int bFirstItem = TRUE;
1409 24 : CPLString osStr;
1410 24 : if (bForInsertOrUpdate)
1411 12 : osStr += "ARRAY[";
1412 : else
1413 12 : osStr += "{";
1414 96 : while(*papszItems)
1415 : {
1416 48 : if (!bFirstItem)
1417 : {
1418 24 : osStr += ',';
1419 : }
1420 :
1421 48 : char* pszStr = *papszItems;
1422 48 : if (*pszStr != '\0')
1423 : {
1424 48 : if (bForInsertOrUpdate)
1425 24 : osStr += OGRPGEscapeString(hPGConn, pszStr, -1, "");
1426 : else
1427 : {
1428 24 : osStr += '"';
1429 :
1430 88 : while(*pszStr)
1431 : {
1432 40 : if (*pszStr == '"' )
1433 0 : osStr += "\\";
1434 40 : osStr += *pszStr;
1435 40 : pszStr++;
1436 : }
1437 :
1438 24 : osStr += '"';
1439 : }
1440 : }
1441 : else
1442 0 : osStr += "NULL";
1443 :
1444 48 : bFirstItem = FALSE;
1445 :
1446 48 : papszItems++;
1447 : }
1448 24 : if (bForInsertOrUpdate)
1449 12 : osStr += "]";
1450 : else
1451 12 : osStr += "}";
1452 0 : return osStr;
1453 : }
1454 :
1455 : /************************************************************************/
1456 : /* CreateFeatureViaInsert() */
1457 : /************************************************************************/
1458 :
1459 6318 : OGRErr OGRPGTableLayer::CreateFeatureViaInsert( OGRFeature *poFeature )
1460 :
1461 : {
1462 6318 : PGconn *hPGConn = poDS->GetPGConn();
1463 : PGresult *hResult;
1464 6318 : CPLString osCommand;
1465 : int i;
1466 6318 : int bNeedComma = FALSE;
1467 : OGRErr eErr;
1468 :
1469 6318 : eErr = poDS->SoftStartTransaction();
1470 6318 : if( eErr != OGRERR_NONE )
1471 : {
1472 0 : return eErr;
1473 : }
1474 :
1475 6318 : int bEmptyInsert = FALSE;
1476 :
1477 : /* -------------------------------------------------------------------- */
1478 : /* Form the INSERT command. */
1479 : /* -------------------------------------------------------------------- */
1480 6318 : osCommand.Printf( "INSERT INTO %s (", pszSqlTableName );
1481 :
1482 6318 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1483 :
1484 6382 : if( bHasWkb && poGeom != NULL )
1485 : {
1486 64 : osCommand += "WKB_GEOMETRY ";
1487 64 : bNeedComma = TRUE;
1488 : }
1489 6254 : else if( (bHasPostGISGeometry || bHasPostGISGeography) && poGeom != NULL )
1490 : {
1491 190 : osCommand = osCommand + OGRPGEscapeColumnName(pszGeomColumn) + " ";
1492 190 : bNeedComma = TRUE;
1493 : }
1494 :
1495 : /* Use case of ogr_pg_60 test */
1496 6318 : if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL )
1497 : {
1498 8 : if( bNeedComma )
1499 0 : osCommand += ", ";
1500 :
1501 8 : osCommand = osCommand + OGRPGEscapeColumnName(pszFIDColumn) + " ";
1502 8 : bNeedComma = TRUE;
1503 : }
1504 :
1505 6318 : int nFieldCount = poFeatureDefn->GetFieldCount();
1506 13584 : for( i = 0; i < nFieldCount; i++ )
1507 : {
1508 7266 : if( !poFeature->IsFieldSet( i ) )
1509 390 : continue;
1510 :
1511 6876 : if( !bNeedComma )
1512 6050 : bNeedComma = TRUE;
1513 : else
1514 826 : osCommand += ", ";
1515 :
1516 : osCommand = osCommand
1517 6876 : + OGRPGEscapeColumnName(poFeatureDefn->GetFieldDefn(i)->GetNameRef());
1518 : }
1519 :
1520 6318 : if (!bNeedComma)
1521 6 : bEmptyInsert = TRUE;
1522 :
1523 6318 : osCommand += ") VALUES (";
1524 :
1525 : /* Set the geometry */
1526 6318 : bNeedComma = FALSE;
1527 6508 : if( (bHasPostGISGeometry || bHasPostGISGeography) && poGeom != NULL)
1528 : {
1529 :
1530 190 : CheckGeomTypeCompatibility(poGeom);
1531 :
1532 190 : poGeom->closeRings();
1533 190 : poGeom->setCoordinateDimension( nCoordDimension );
1534 :
1535 :
1536 190 : if ( !CSLTestBoolean(CPLGetConfigOption("PG_USE_TEXT", "NO")) )
1537 : {
1538 190 : char *pszHexEWKB = GeometryToHex( poGeom, nSRSId );
1539 190 : if ( bHasPostGISGeography )
1540 2 : osCommand += CPLString().Printf("'%s'::GEOGRAPHY", pszHexEWKB);
1541 : else
1542 188 : osCommand += CPLString().Printf("'%s'::GEOMETRY", pszHexEWKB);
1543 190 : OGRFree( pszHexEWKB );
1544 : }
1545 : else
1546 : {
1547 0 : char *pszWKT = NULL;
1548 0 : poGeom->exportToWkt( &pszWKT );
1549 :
1550 0 : if( pszWKT != NULL )
1551 : {
1552 0 : if( bHasPostGISGeography )
1553 : osCommand +=
1554 : CPLString().Printf(
1555 0 : "ST_GeographyFromText('SRID=%d;%s'::TEXT) ", nSRSId, pszWKT );
1556 0 : else if( poDS->sPostGISVersion.nMajor >= 1 )
1557 : osCommand +=
1558 : CPLString().Printf(
1559 0 : "GeomFromEWKT('SRID=%d;%s'::TEXT) ", nSRSId, pszWKT );
1560 : else
1561 : osCommand +=
1562 : CPLString().Printf(
1563 0 : "GeometryFromText('%s'::TEXT,%d) ", pszWKT, nSRSId );
1564 0 : OGRFree( pszWKT );
1565 : }
1566 : else
1567 0 : osCommand += "''";
1568 :
1569 : }
1570 190 : bNeedComma = TRUE;
1571 : }
1572 6192 : else if( bHasWkb && !bWkbAsOid && poGeom != NULL )
1573 : {
1574 64 : char *pszBytea = GeometryToBYTEA( poGeom );
1575 :
1576 64 : if( pszBytea != NULL )
1577 : {
1578 64 : if (poDS->bUseEscapeStringSyntax)
1579 64 : osCommand += "E";
1580 64 : osCommand = osCommand + "'" + pszBytea + "'";
1581 64 : CPLFree( pszBytea );
1582 : }
1583 : else
1584 0 : osCommand += "''";
1585 :
1586 64 : bNeedComma = TRUE;
1587 : }
1588 6064 : else if( bHasWkb && bWkbAsOid && poGeom != NULL )
1589 : {
1590 0 : Oid oid = GeometryToOID( poGeom );
1591 :
1592 0 : if( oid != 0 )
1593 : {
1594 0 : osCommand += CPLString().Printf( "'%d' ", oid );
1595 : }
1596 : else
1597 0 : osCommand += "''";
1598 :
1599 0 : bNeedComma = TRUE;
1600 : }
1601 :
1602 6318 : if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL )
1603 : {
1604 8 : if( bNeedComma )
1605 0 : osCommand += ", ";
1606 8 : osCommand += CPLString().Printf( "%ld ", poFeature->GetFID() );
1607 8 : bNeedComma = TRUE;
1608 : }
1609 :
1610 :
1611 13584 : for( i = 0; i < nFieldCount; i++ )
1612 : {
1613 7266 : if( !poFeature->IsFieldSet( i ) )
1614 390 : continue;
1615 :
1616 6876 : if( bNeedComma )
1617 826 : osCommand += ", ";
1618 : else
1619 6050 : bNeedComma = TRUE;
1620 :
1621 6876 : AppendFieldValue(hPGConn, osCommand, poFeature, i);
1622 : }
1623 :
1624 6318 : osCommand += ")";
1625 :
1626 6318 : if (bEmptyInsert)
1627 6 : osCommand.Printf( "INSERT INTO %s DEFAULT VALUES", pszSqlTableName );
1628 :
1629 6318 : int bReturnRequested = FALSE;
1630 : /* RETURNING is only available since Postgres 8.2 */
1631 : /* We only get the FID, but we also could add the unset fields to get */
1632 : /* the default values */
1633 6318 : if (bRetrieveFID && pszFIDColumn != NULL && poFeature->GetFID() == OGRNullFID &&
1634 : (poDS->sPostgreSQLVersion.nMajor >= 9 ||
1635 : (poDS->sPostgreSQLVersion.nMajor == 8 && poDS->sPostgreSQLVersion.nMinor >= 2)))
1636 : {
1637 6308 : bReturnRequested = TRUE;
1638 6308 : osCommand += " RETURNING ";
1639 6308 : osCommand += OGRPGEscapeColumnName(pszFIDColumn);
1640 : }
1641 :
1642 : /* -------------------------------------------------------------------- */
1643 : /* Execute the insert. */
1644 : /* -------------------------------------------------------------------- */
1645 6318 : hResult = OGRPG_PQexec(hPGConn, osCommand);
1646 6318 : if (bReturnRequested && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
1647 : PQntuples(hResult) == 1 && PQnfields(hResult) == 1 )
1648 : {
1649 6306 : const char* pszFID = PQgetvalue(hResult, 0, 0 );
1650 6306 : long nFID = atol(pszFID);
1651 6306 : poFeature->SetFID(nFID);
1652 : }
1653 12 : else if( bReturnRequested || PQresultStatus(hResult) != PGRES_COMMAND_OK )
1654 : {
1655 : CPLError( CE_Failure, CPLE_AppDefined,
1656 : "INSERT command for new feature failed.\n%s\nCommand: %s",
1657 4 : PQerrorMessage(hPGConn), osCommand.c_str() );
1658 :
1659 4 : if( !bHasWarnedAlreadySetFID && poFeature->GetFID() != OGRNullFID &&
1660 : pszFIDColumn != NULL )
1661 : {
1662 0 : bHasWarnedAlreadySetFID = TRUE;
1663 : CPLError(CE_Warning, CPLE_AppDefined,
1664 : "You've inserted feature with an already set FID and that's perhaps the reason for the failure. "
1665 : "If so, this can happen if you reuse the same feature object for sequential insertions. "
1666 : "Indeed, since GDAL 1.8.0, the FID of an inserted feature is got from the server, so it is not a good idea"
1667 0 : "to reuse it afterwards... All in all, try unsetting the FID with SetFID(-1) before calling CreateFeature()");
1668 : }
1669 :
1670 4 : OGRPGClearResult( hResult );
1671 :
1672 4 : poDS->SoftRollback();
1673 :
1674 4 : return OGRERR_FAILURE;
1675 : }
1676 :
1677 6314 : OGRPGClearResult( hResult );
1678 :
1679 6314 : return poDS->SoftCommit();
1680 : }
1681 :
1682 : /************************************************************************/
1683 : /* CreateFeatureViaCopy() */
1684 : /************************************************************************/
1685 :
1686 88 : OGRErr OGRPGTableLayer::CreateFeatureViaCopy( OGRFeature *poFeature )
1687 : {
1688 88 : PGconn *hPGConn = poDS->GetPGConn();
1689 88 : CPLString osCommand;
1690 :
1691 : /* First process geometry */
1692 88 : OGRGeometry *poGeometry = (OGRGeometry *) poFeature->GetGeometryRef();
1693 :
1694 88 : char *pszGeom = NULL;
1695 88 : if ( NULL != poGeometry && (bHasWkb || bHasPostGISGeometry || bHasPostGISGeography))
1696 : {
1697 44 : poGeometry->closeRings();
1698 44 : poGeometry->setCoordinateDimension( nCoordDimension );
1699 :
1700 44 : CheckGeomTypeCompatibility(poGeometry);
1701 :
1702 44 : if (bHasWkb)
1703 22 : pszGeom = GeometryToBYTEA( poGeometry );
1704 : else
1705 22 : pszGeom = GeometryToHex( poGeometry, nSRSId );
1706 : }
1707 :
1708 88 : if ( pszGeom )
1709 : {
1710 : osCommand += pszGeom,
1711 44 : CPLFree( pszGeom );
1712 : }
1713 44 : else if (nGeomType != wkbNone)
1714 : {
1715 12 : osCommand = "\\N";
1716 : }
1717 :
1718 : /* Next process the field id column */
1719 88 : int nFIDIndex = -1;
1720 88 : if( bFIDColumnInCopyFields )
1721 : {
1722 8 : if (osCommand.size() > 0)
1723 0 : osCommand += "\t";
1724 :
1725 8 : nFIDIndex = poFeatureDefn->GetFieldIndex( pszFIDColumn );
1726 :
1727 : /* Set the FID */
1728 8 : if( poFeature->GetFID() != OGRNullFID )
1729 : {
1730 8 : osCommand += CPLString().Printf("%ld ", poFeature->GetFID());
1731 : }
1732 : else
1733 : {
1734 0 : osCommand += "\\N" ;
1735 : }
1736 : }
1737 :
1738 :
1739 : /* Now process the remaining fields */
1740 :
1741 88 : int nFieldCount = poFeatureDefn->GetFieldCount();
1742 88 : int bAddTab = osCommand.size() > 0;
1743 :
1744 460 : for( int i = 0; i < nFieldCount; i++ )
1745 : {
1746 372 : if (i == nFIDIndex)
1747 0 : continue;
1748 :
1749 372 : const char *pszStrValue = poFeature->GetFieldAsString(i);
1750 372 : char *pszNeedToFree = NULL;
1751 :
1752 372 : if (bAddTab)
1753 348 : osCommand += "\t";
1754 372 : bAddTab = TRUE;
1755 :
1756 372 : if( !poFeature->IsFieldSet( i ) )
1757 : {
1758 72 : osCommand += "\\N" ;
1759 :
1760 72 : continue;
1761 : }
1762 :
1763 300 : int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
1764 :
1765 : // We need special formatting for integer list values.
1766 300 : if( nOGRFieldType == OFTIntegerList )
1767 : {
1768 4 : int nCount, nOff = 0, j;
1769 4 : const int *panItems = poFeature->GetFieldAsIntegerList(i,&nCount);
1770 :
1771 4 : pszNeedToFree = (char *) CPLMalloc(nCount * 13 + 10);
1772 4 : strcpy( pszNeedToFree, "{" );
1773 12 : for( j = 0; j < nCount; j++ )
1774 : {
1775 8 : if( j != 0 )
1776 4 : strcat( pszNeedToFree+nOff, "," );
1777 :
1778 8 : nOff += strlen(pszNeedToFree+nOff);
1779 8 : sprintf( pszNeedToFree+nOff, "%d", panItems[j] );
1780 : }
1781 4 : strcat( pszNeedToFree+nOff, "}" );
1782 4 : pszStrValue = pszNeedToFree;
1783 : }
1784 :
1785 : // We need special formatting for real list values.
1786 296 : else if( nOGRFieldType == OFTRealList )
1787 : {
1788 20 : int nCount, nOff = 0, j;
1789 20 : const double *padfItems =poFeature->GetFieldAsDoubleList(i,&nCount);
1790 :
1791 20 : pszNeedToFree = (char *) CPLMalloc(nCount * 40 + 10);
1792 20 : strcpy( pszNeedToFree, "{" );
1793 60 : for( j = 0; j < nCount; j++ )
1794 : {
1795 40 : if( j != 0 )
1796 20 : strcat( pszNeedToFree+nOff, "," );
1797 :
1798 40 : nOff += strlen(pszNeedToFree+nOff);
1799 : //Check for special values. They need to be quoted.
1800 40 : if( CPLIsNan(padfItems[j]) )
1801 8 : sprintf( pszNeedToFree+nOff, "NaN" );
1802 32 : else if( CPLIsInf(padfItems[j]) )
1803 16 : sprintf( pszNeedToFree+nOff, (padfItems[j] > 0) ? "Infinity" : "-Infinity" );
1804 : else
1805 16 : sprintf( pszNeedToFree+nOff, "%.16g", padfItems[j] );
1806 :
1807 40 : char* pszComma = strchr(pszNeedToFree+nOff, ',');
1808 40 : if (pszComma)
1809 0 : *pszComma = '.';
1810 : }
1811 20 : strcat( pszNeedToFree+nOff, "}" );
1812 20 : pszStrValue = pszNeedToFree;
1813 : }
1814 :
1815 :
1816 : // We need special formatting for string list values.
1817 276 : else if( nOGRFieldType == OFTStringList )
1818 : {
1819 12 : CPLString osStr;
1820 12 : char **papszItems = poFeature->GetFieldAsStringList(i);
1821 :
1822 12 : pszStrValue = pszNeedToFree = CPLStrdup(OGRPGEscapeStringList(hPGConn, papszItems, FALSE));
1823 : }
1824 :
1825 : // Binary formatting
1826 264 : else if( nOGRFieldType == OFTBinary )
1827 : {
1828 4 : int nLen = 0;
1829 4 : GByte* pabyData = poFeature->GetFieldAsBinary( i, &nLen );
1830 4 : char* pszBytea = GByteArrayToBYTEA( pabyData, nLen);
1831 :
1832 4 : pszStrValue = pszNeedToFree = pszBytea;
1833 : }
1834 :
1835 260 : else if( nOGRFieldType == OFTReal )
1836 : {
1837 68 : char* pszComma = strchr((char*)pszStrValue, ',');
1838 68 : if (pszComma)
1839 0 : *pszComma = '.';
1840 : //Check for special values. They need to be quoted.
1841 68 : double dfVal = poFeature->GetFieldAsDouble(i);
1842 68 : if( CPLIsNan(dfVal) )
1843 4 : pszStrValue = "NaN";
1844 64 : else if( CPLIsInf(dfVal) )
1845 8 : pszStrValue = (dfVal > 0) ? "Infinity" : "-Infinity";
1846 : }
1847 :
1848 444 : if( nOGRFieldType != OFTIntegerList &&
1849 : nOGRFieldType != OFTRealList &&
1850 : nOGRFieldType != OFTInteger &&
1851 : nOGRFieldType != OFTReal &&
1852 : nOGRFieldType != OFTBinary )
1853 : {
1854 : int iChar;
1855 :
1856 1000 : for( iChar = 0; pszStrValue[iChar] != '\0'; iChar++ )
1857 : {
1858 856 : if( poFeatureDefn->GetFieldDefn(i)->GetWidth() > 0
1859 : && iChar == poFeatureDefn->GetFieldDefn(i)->GetWidth() )
1860 : {
1861 : CPLDebug( "PG",
1862 : "Truncated %s field value, it was too long.",
1863 0 : poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
1864 0 : break;
1865 : }
1866 :
1867 : /* Escape embedded \, \t, \n, \r since they will cause COPY
1868 : to misinterpret a line of text and thus abort */
1869 3424 : if( pszStrValue[iChar] == '\\' ||
1870 856 : pszStrValue[iChar] == '\t' ||
1871 856 : pszStrValue[iChar] == '\r' ||
1872 856 : pszStrValue[iChar] == '\n' )
1873 : {
1874 0 : osCommand += '\\';
1875 : }
1876 :
1877 856 : osCommand += pszStrValue[iChar];
1878 : }
1879 : }
1880 : else
1881 : {
1882 156 : osCommand += pszStrValue;
1883 : }
1884 :
1885 300 : if( pszNeedToFree )
1886 40 : CPLFree( pszNeedToFree );
1887 : }
1888 :
1889 : /* Add end of line marker */
1890 88 : osCommand += "\n";
1891 :
1892 :
1893 : /* ------------------------------------------------------------ */
1894 : /* Execute the copy. */
1895 : /* ------------------------------------------------------------ */
1896 :
1897 88 : OGRErr result = OGRERR_NONE;
1898 :
1899 : /* This is for postgresql 7.4 and higher */
1900 : #if !defined(PG_PRE74)
1901 88 : int copyResult = PQputCopyData(hPGConn, osCommand.c_str(), strlen(osCommand.c_str()));
1902 : //CPLDebug("PG", "PQputCopyData(%s)", osCommand.c_str());
1903 :
1904 88 : switch (copyResult)
1905 : {
1906 : case 0:
1907 0 : CPLError( CE_Failure, CPLE_AppDefined, "Writing COPY data blocked.");
1908 0 : result = OGRERR_FAILURE;
1909 0 : break;
1910 : case -1:
1911 0 : CPLError( CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn) );
1912 0 : result = OGRERR_FAILURE;
1913 : break;
1914 : }
1915 : #else /* else defined(PG_PRE74) */
1916 : int copyResult = PQputline(hPGConn, osCommand.c_str());
1917 :
1918 : if (copyResult == EOF)
1919 : {
1920 : CPLError( CE_Failure, CPLE_AppDefined, "Writing COPY data blocked.");
1921 : result = OGRERR_FAILURE;
1922 : }
1923 : #endif /* end of defined(PG_PRE74) */
1924 :
1925 88 : return result;
1926 : }
1927 :
1928 :
1929 : /************************************************************************/
1930 : /* TestCapability() */
1931 : /************************************************************************/
1932 :
1933 380 : int OGRPGTableLayer::TestCapability( const char * pszCap )
1934 :
1935 : {
1936 380 : GetLayerDefn();
1937 :
1938 380 : if ( bUpdateAccess )
1939 : {
1940 358 : if( EQUAL(pszCap,OLCSequentialWrite) ||
1941 : EQUAL(pszCap,OLCCreateField) ||
1942 : EQUAL(pszCap,OLCDeleteField) ||
1943 : EQUAL(pszCap,OLCAlterFieldDefn) )
1944 24 : return TRUE;
1945 :
1946 334 : else if( EQUAL(pszCap,OLCRandomWrite) ||
1947 : EQUAL(pszCap,OLCDeleteFeature) )
1948 24 : return bHasFid;
1949 : }
1950 :
1951 332 : if( EQUAL(pszCap,OLCRandomRead) )
1952 22 : return bHasFid;
1953 :
1954 310 : else if( EQUAL(pszCap,OLCFastFeatureCount) ||
1955 : EQUAL(pszCap,OLCFastSetNextByIndex) )
1956 242 : return m_poFilterGeom == NULL || bHasPostGISGeometry || bHasPostGISGeography;
1957 :
1958 68 : else if( EQUAL(pszCap,OLCFastSpatialFilter) )
1959 4 : return bHasPostGISGeometry || bHasPostGISGeography;
1960 :
1961 64 : else if( EQUAL(pszCap,OLCTransactions) )
1962 12 : return TRUE;
1963 :
1964 52 : else if( EQUAL(pszCap,OLCFastGetExtent) )
1965 32 : return bHasPostGISGeometry;
1966 :
1967 20 : else if( EQUAL(pszCap,OLCStringsAsUTF8) )
1968 12 : return TRUE;
1969 :
1970 : else
1971 8 : return FALSE;
1972 : }
1973 :
1974 : /************************************************************************/
1975 : /* OGRPGTableLayerGetType() */
1976 : /************************************************************************/
1977 :
1978 350 : static CPLString OGRPGTableLayerGetType(OGRFieldDefn& oField,
1979 : int bPreservePrecision,
1980 : int bApproxOK)
1981 : {
1982 : char szFieldType[256];
1983 :
1984 : /* -------------------------------------------------------------------- */
1985 : /* Work out the PostgreSQL type. */
1986 : /* -------------------------------------------------------------------- */
1987 350 : if( oField.GetType() == OFTInteger )
1988 : {
1989 60 : if( oField.GetWidth() > 0 && bPreservePrecision )
1990 24 : sprintf( szFieldType, "NUMERIC(%d,0)", oField.GetWidth() );
1991 : else
1992 36 : strcpy( szFieldType, "INTEGER" );
1993 : }
1994 290 : else if( oField.GetType() == OFTReal )
1995 : {
1996 76 : if( oField.GetWidth() > 0 && oField.GetPrecision() > 0
1997 : && bPreservePrecision )
1998 : sprintf( szFieldType, "NUMERIC(%d,%d)",
1999 20 : oField.GetWidth(), oField.GetPrecision() );
2000 : else
2001 56 : strcpy( szFieldType, "FLOAT8" );
2002 : }
2003 214 : else if( oField.GetType() == OFTString )
2004 : {
2005 110 : if (oField.GetWidth() > 0 && bPreservePrecision )
2006 44 : sprintf( szFieldType, "VARCHAR(%d)", oField.GetWidth() );
2007 : else
2008 66 : strcpy( szFieldType, "VARCHAR");
2009 : }
2010 104 : else if( oField.GetType() == OFTIntegerList )
2011 : {
2012 8 : strcpy( szFieldType, "INTEGER[]" );
2013 : }
2014 96 : else if( oField.GetType() == OFTRealList )
2015 : {
2016 20 : strcpy( szFieldType, "FLOAT8[]" );
2017 : }
2018 76 : else if( oField.GetType() == OFTStringList )
2019 : {
2020 24 : strcpy( szFieldType, "varchar[]" );
2021 : }
2022 52 : else if( oField.GetType() == OFTDate )
2023 : {
2024 12 : strcpy( szFieldType, "date" );
2025 : }
2026 40 : else if( oField.GetType() == OFTTime )
2027 : {
2028 12 : strcpy( szFieldType, "time" );
2029 : }
2030 28 : else if( oField.GetType() == OFTDateTime )
2031 : {
2032 20 : strcpy( szFieldType, "timestamp with time zone" );
2033 : }
2034 8 : else if( oField.GetType() == OFTBinary )
2035 : {
2036 8 : strcpy( szFieldType, "bytea" );
2037 : }
2038 0 : else if( bApproxOK )
2039 : {
2040 : CPLError( CE_Warning, CPLE_NotSupported,
2041 : "Can't create field %s with type %s on PostgreSQL layers. Creating as VARCHAR.",
2042 : oField.GetNameRef(),
2043 0 : OGRFieldDefn::GetFieldTypeName(oField.GetType()) );
2044 0 : strcpy( szFieldType, "VARCHAR" );
2045 : }
2046 : else
2047 : {
2048 : CPLError( CE_Failure, CPLE_NotSupported,
2049 : "Can't create field %s with type %s on PostgreSQL layers.",
2050 : oField.GetNameRef(),
2051 0 : OGRFieldDefn::GetFieldTypeName(oField.GetType()) );
2052 0 : strcpy( szFieldType, "");
2053 : }
2054 :
2055 350 : return szFieldType;
2056 : }
2057 :
2058 : /************************************************************************/
2059 : /* CreateField() */
2060 : /************************************************************************/
2061 :
2062 346 : OGRErr OGRPGTableLayer::CreateField( OGRFieldDefn *poFieldIn, int bApproxOK )
2063 :
2064 : {
2065 346 : PGconn *hPGConn = poDS->GetPGConn();
2066 346 : PGresult *hResult = NULL;
2067 346 : CPLString osCommand;
2068 346 : CPLString osFieldType;
2069 346 : OGRFieldDefn oField( poFieldIn );
2070 :
2071 346 : GetLayerDefn();
2072 :
2073 346 : if( !bUpdateAccess )
2074 : {
2075 : CPLError( CE_Failure, CPLE_NotSupported,
2076 : UNSUPPORTED_OP_READ_ONLY,
2077 0 : "CreateField");
2078 0 : return OGRERR_FAILURE;
2079 : }
2080 :
2081 : /* -------------------------------------------------------------------- */
2082 : /* Do we want to "launder" the column names into Postgres */
2083 : /* friendly format? */
2084 : /* -------------------------------------------------------------------- */
2085 346 : if( bLaunderColumnNames )
2086 : {
2087 346 : char *pszSafeName = poDS->LaunderName( oField.GetNameRef() );
2088 :
2089 346 : oField.SetName( pszSafeName );
2090 346 : CPLFree( pszSafeName );
2091 :
2092 346 : if( EQUAL(oField.GetNameRef(),"oid") )
2093 : {
2094 : CPLError( CE_Warning, CPLE_AppDefined,
2095 0 : "Renaming field 'oid' to 'oid_' to avoid conflict with internal oid field." );
2096 0 : oField.SetName( "oid_" );
2097 : }
2098 : }
2099 :
2100 346 : osFieldType = OGRPGTableLayerGetType(oField, bPreservePrecision, bApproxOK);
2101 346 : if (osFieldType.size() == 0)
2102 0 : return OGRERR_FAILURE;
2103 :
2104 : /* -------------------------------------------------------------------- */
2105 : /* Create the new field. */
2106 : /* -------------------------------------------------------------------- */
2107 346 : poDS->FlushSoftTransaction();
2108 346 : hResult = OGRPG_PQexec(hPGConn, "BEGIN");
2109 346 : OGRPGClearResult( hResult );
2110 :
2111 : osCommand.Printf( "ALTER TABLE %s ADD COLUMN %s %s",
2112 : pszSqlTableName, OGRPGEscapeColumnName(oField.GetNameRef()).c_str(),
2113 346 : osFieldType.c_str() );
2114 346 : hResult = OGRPG_PQexec(hPGConn, osCommand);
2115 346 : if( PQresultStatus(hResult) != PGRES_COMMAND_OK )
2116 : {
2117 : CPLError( CE_Failure, CPLE_AppDefined,
2118 : "%s\n%s",
2119 : osCommand.c_str(),
2120 0 : PQerrorMessage(hPGConn) );
2121 :
2122 0 : OGRPGClearResult( hResult );
2123 :
2124 0 : hResult = OGRPG_PQexec( hPGConn, "ROLLBACK" );
2125 0 : OGRPGClearResult( hResult );
2126 :
2127 0 : return OGRERR_FAILURE;
2128 : }
2129 :
2130 346 : OGRPGClearResult( hResult );
2131 :
2132 346 : hResult = OGRPG_PQexec(hPGConn, "COMMIT");
2133 346 : OGRPGClearResult( hResult );
2134 :
2135 346 : poFeatureDefn->AddFieldDefn( &oField );
2136 :
2137 346 : return OGRERR_NONE;
2138 : }
2139 :
2140 : /************************************************************************/
2141 : /* DeleteField() */
2142 : /************************************************************************/
2143 :
2144 4 : OGRErr OGRPGTableLayer::DeleteField( int iField )
2145 : {
2146 4 : PGconn *hPGConn = poDS->GetPGConn();
2147 4 : PGresult *hResult = NULL;
2148 4 : CPLString osCommand;
2149 :
2150 4 : GetLayerDefn();
2151 :
2152 4 : if( !bUpdateAccess )
2153 : {
2154 : CPLError( CE_Failure, CPLE_NotSupported,
2155 : UNSUPPORTED_OP_READ_ONLY,
2156 0 : "DeleteField");
2157 0 : return OGRERR_FAILURE;
2158 : }
2159 :
2160 4 : if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
2161 : {
2162 : CPLError( CE_Failure, CPLE_NotSupported,
2163 0 : "Invalid field index");
2164 0 : return OGRERR_FAILURE;
2165 : }
2166 :
2167 4 : poDS->FlushSoftTransaction();
2168 :
2169 4 : hResult = OGRPG_PQexec(hPGConn, "BEGIN");
2170 4 : OGRPGClearResult( hResult );
2171 :
2172 : osCommand.Printf( "ALTER TABLE %s DROP COLUMN %s",
2173 : pszSqlTableName,
2174 4 : OGRPGEscapeColumnName(poFeatureDefn->GetFieldDefn(iField)->GetNameRef()).c_str() );
2175 4 : hResult = OGRPG_PQexec(hPGConn, osCommand);
2176 4 : if( PQresultStatus(hResult) != PGRES_COMMAND_OK )
2177 : {
2178 : CPLError( CE_Failure, CPLE_AppDefined,
2179 : "%s\n%s",
2180 : osCommand.c_str(),
2181 0 : PQerrorMessage(hPGConn) );
2182 :
2183 0 : OGRPGClearResult( hResult );
2184 :
2185 0 : hResult = OGRPG_PQexec( hPGConn, "ROLLBACK" );
2186 0 : OGRPGClearResult( hResult );
2187 :
2188 0 : return OGRERR_FAILURE;
2189 : }
2190 :
2191 4 : OGRPGClearResult( hResult );
2192 :
2193 4 : hResult = OGRPG_PQexec(hPGConn, "COMMIT");
2194 4 : OGRPGClearResult( hResult );
2195 :
2196 4 : return poFeatureDefn->DeleteFieldDefn( iField );
2197 : }
2198 :
2199 : /************************************************************************/
2200 : /* AlterFieldDefn() */
2201 : /************************************************************************/
2202 :
2203 4 : OGRErr OGRPGTableLayer::AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags )
2204 : {
2205 4 : PGconn *hPGConn = poDS->GetPGConn();
2206 4 : PGresult *hResult = NULL;
2207 4 : CPLString osCommand;
2208 :
2209 4 : GetLayerDefn();
2210 :
2211 4 : if( !bUpdateAccess )
2212 : {
2213 : CPLError( CE_Failure, CPLE_NotSupported,
2214 : UNSUPPORTED_OP_READ_ONLY,
2215 0 : "AlterFieldDefn");
2216 0 : return OGRERR_FAILURE;
2217 : }
2218 :
2219 4 : if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
2220 : {
2221 : CPLError( CE_Failure, CPLE_NotSupported,
2222 0 : "Invalid field index");
2223 0 : return OGRERR_FAILURE;
2224 : }
2225 :
2226 4 : OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
2227 4 : OGRFieldDefn oField( poNewFieldDefn );
2228 :
2229 4 : poDS->FlushSoftTransaction();
2230 :
2231 4 : hResult = OGRPG_PQexec(hPGConn, "BEGIN");
2232 4 : OGRPGClearResult( hResult );
2233 :
2234 4 : if (!(nFlags & ALTER_TYPE_FLAG))
2235 0 : oField.SetType(poFieldDefn->GetType());
2236 :
2237 4 : if (!(nFlags & ALTER_WIDTH_PRECISION_FLAG))
2238 : {
2239 0 : oField.SetWidth(poFieldDefn->GetWidth());
2240 0 : oField.SetPrecision(poFieldDefn->GetPrecision());
2241 : }
2242 :
2243 4 : if ((nFlags & ALTER_TYPE_FLAG) ||
2244 : (nFlags & ALTER_WIDTH_PRECISION_FLAG))
2245 : {
2246 : CPLString osFieldType = OGRPGTableLayerGetType(oField,
2247 : bPreservePrecision,
2248 4 : TRUE);
2249 4 : if (osFieldType.size() == 0)
2250 : {
2251 0 : hResult = OGRPG_PQexec( hPGConn, "ROLLBACK" );
2252 0 : OGRPGClearResult( hResult );
2253 :
2254 0 : return OGRERR_FAILURE;
2255 : }
2256 :
2257 : osCommand.Printf( "ALTER TABLE %s ALTER COLUMN %s TYPE %s",
2258 : pszSqlTableName,
2259 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
2260 4 : osFieldType.c_str() );
2261 4 : hResult = OGRPG_PQexec(hPGConn, osCommand);
2262 4 : if( PQresultStatus(hResult) != PGRES_COMMAND_OK )
2263 : {
2264 : CPLError( CE_Failure, CPLE_AppDefined,
2265 : "%s\n%s",
2266 : osCommand.c_str(),
2267 0 : PQerrorMessage(hPGConn) );
2268 :
2269 0 : OGRPGClearResult( hResult );
2270 :
2271 0 : hResult = OGRPG_PQexec( hPGConn, "ROLLBACK" );
2272 0 : OGRPGClearResult( hResult );
2273 :
2274 0 : return OGRERR_FAILURE;
2275 : }
2276 4 : OGRPGClearResult( hResult );
2277 : }
2278 :
2279 :
2280 4 : if( (nFlags & ALTER_NAME_FLAG) )
2281 : {
2282 4 : if (bLaunderColumnNames)
2283 : {
2284 4 : char *pszSafeName = poDS->LaunderName( oField.GetNameRef() );
2285 4 : oField.SetName( pszSafeName );
2286 4 : CPLFree( pszSafeName );
2287 : }
2288 :
2289 4 : if( EQUAL(oField.GetNameRef(),"oid") )
2290 : {
2291 : CPLError( CE_Warning, CPLE_AppDefined,
2292 0 : "Renaming field 'oid' to 'oid_' to avoid conflict with internal oid field." );
2293 0 : oField.SetName( "oid_" );
2294 : }
2295 :
2296 4 : if ( strcmp(poFieldDefn->GetNameRef(), oField.GetNameRef()) != 0 )
2297 : {
2298 : osCommand.Printf( "ALTER TABLE %s RENAME COLUMN %s TO %s",
2299 : pszSqlTableName,
2300 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
2301 4 : OGRPGEscapeColumnName(oField.GetNameRef()).c_str() );
2302 4 : hResult = OGRPG_PQexec(hPGConn, osCommand);
2303 4 : if( PQresultStatus(hResult) != PGRES_COMMAND_OK )
2304 : {
2305 : CPLError( CE_Failure, CPLE_AppDefined,
2306 : "%s\n%s",
2307 : osCommand.c_str(),
2308 0 : PQerrorMessage(hPGConn) );
2309 :
2310 0 : OGRPGClearResult( hResult );
2311 :
2312 0 : hResult = OGRPG_PQexec( hPGConn, "ROLLBACK" );
2313 0 : OGRPGClearResult( hResult );
2314 :
2315 0 : return OGRERR_FAILURE;
2316 : }
2317 4 : OGRPGClearResult( hResult );
2318 : }
2319 : }
2320 :
2321 4 : hResult = OGRPG_PQexec(hPGConn, "COMMIT");
2322 4 : OGRPGClearResult( hResult );
2323 :
2324 4 : if (nFlags & ALTER_NAME_FLAG)
2325 4 : poFieldDefn->SetName(oField.GetNameRef());
2326 4 : if (nFlags & ALTER_TYPE_FLAG)
2327 4 : poFieldDefn->SetType(oField.GetType());
2328 4 : if (nFlags & ALTER_WIDTH_PRECISION_FLAG)
2329 : {
2330 4 : poFieldDefn->SetWidth(oField.GetWidth());
2331 4 : poFieldDefn->SetPrecision(oField.GetPrecision());
2332 : }
2333 :
2334 4 : return OGRERR_NONE;
2335 :
2336 : }
2337 :
2338 : /************************************************************************/
2339 : /* GetFeature() */
2340 : /************************************************************************/
2341 :
2342 70 : OGRFeature *OGRPGTableLayer::GetFeature( long nFeatureId )
2343 :
2344 : {
2345 70 : GetLayerDefn();
2346 :
2347 70 : if( pszFIDColumn == NULL )
2348 0 : return OGRLayer::GetFeature( nFeatureId );
2349 :
2350 : /* -------------------------------------------------------------------- */
2351 : /* Discard any existing resultset. */
2352 : /* -------------------------------------------------------------------- */
2353 70 : ResetReading();
2354 :
2355 : /* -------------------------------------------------------------------- */
2356 : /* Issue query for a single record. */
2357 : /* -------------------------------------------------------------------- */
2358 70 : OGRFeature *poFeature = NULL;
2359 70 : PGresult *hResult = NULL;
2360 70 : PGconn *hPGConn = poDS->GetPGConn();
2361 70 : CPLString osFieldList = BuildFields();
2362 70 : CPLString osCommand;
2363 :
2364 70 : poDS->FlushSoftTransaction();
2365 70 : poDS->SoftStartTransaction();
2366 :
2367 : osCommand.Printf(
2368 : "DECLARE getfeaturecursor %s for "
2369 : "SELECT %s FROM %s WHERE %s = %ld",
2370 : ( poDS->bUseBinaryCursor ) ? "BINARY CURSOR" : "CURSOR",
2371 : osFieldList.c_str(), pszSqlTableName, OGRPGEscapeColumnName(pszFIDColumn).c_str(),
2372 70 : nFeatureId );
2373 :
2374 70 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str() );
2375 :
2376 70 : if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK )
2377 : {
2378 70 : OGRPGClearResult( hResult );
2379 :
2380 70 : hResult = OGRPG_PQexec(hPGConn, "FETCH ALL in getfeaturecursor" );
2381 :
2382 70 : if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK )
2383 : {
2384 70 : int nRows = PQntuples(hResult);
2385 70 : if (nRows > 0)
2386 : {
2387 54 : hCursorResult = hResult;
2388 54 : CreateMapFromFieldNameToIndex();
2389 54 : poFeature = RecordToFeature( 0 );
2390 54 : hCursorResult = NULL;
2391 :
2392 54 : if (nRows > 1)
2393 : {
2394 : CPLError(CE_Warning, CPLE_AppDefined,
2395 : "%d rows in response to the WHERE %s = %ld clause !",
2396 0 : nRows, pszFIDColumn, nFeatureId );
2397 : }
2398 : }
2399 : else
2400 : {
2401 : CPLError( CE_Failure, CPLE_AppDefined,
2402 16 : "Attempt to read feature with unknown feature id (%ld).", nFeatureId );
2403 : }
2404 : }
2405 : }
2406 0 : else if ( hResult && PQresultStatus(hResult) == PGRES_FATAL_ERROR )
2407 : {
2408 : CPLError( CE_Failure, CPLE_AppDefined,
2409 0 : "%s", PQresultErrorMessage( hResult ) );
2410 : }
2411 :
2412 : /* -------------------------------------------------------------------- */
2413 : /* Cleanup */
2414 : /* -------------------------------------------------------------------- */
2415 70 : OGRPGClearResult( hResult );
2416 :
2417 70 : hResult = OGRPG_PQexec(hPGConn, "CLOSE getfeaturecursor");
2418 70 : OGRPGClearResult( hResult );
2419 :
2420 70 : poDS->FlushSoftTransaction();
2421 :
2422 70 : return poFeature;
2423 : }
2424 :
2425 : /************************************************************************/
2426 : /* GetFeatureCount() */
2427 : /************************************************************************/
2428 :
2429 198 : int OGRPGTableLayer::GetFeatureCount( int bForce )
2430 :
2431 : {
2432 198 : GetLayerDefn();
2433 :
2434 198 : if( TestCapability(OLCFastFeatureCount) == FALSE )
2435 10 : return OGRPGLayer::GetFeatureCount( bForce );
2436 :
2437 : /* -------------------------------------------------------------------- */
2438 : /* In theory it might be wise to cache this result, but it */
2439 : /* won't be trivial to work out the lifetime of the value. */
2440 : /* After all someone else could be adding records from another */
2441 : /* application when working against a database. */
2442 : /* -------------------------------------------------------------------- */
2443 188 : PGconn *hPGConn = poDS->GetPGConn();
2444 188 : PGresult *hResult = NULL;
2445 188 : CPLString osCommand;
2446 188 : int nCount = 0;
2447 :
2448 : osCommand.Printf(
2449 : "SELECT count(*) FROM %s %s",
2450 188 : pszSqlTableName, osWHERE.c_str() );
2451 :
2452 188 : hResult = OGRPG_PQexec(hPGConn, osCommand);
2453 188 : if( hResult != NULL && PQresultStatus(hResult) == PGRES_TUPLES_OK )
2454 188 : nCount = atoi(PQgetvalue(hResult,0,0));
2455 : else
2456 0 : CPLDebug( "PG", "%s; failed.", osCommand.c_str() );
2457 188 : OGRPGClearResult( hResult );
2458 :
2459 188 : return nCount;
2460 : }
2461 :
2462 : /************************************************************************/
2463 : /* GetSpatialRef() */
2464 : /* */
2465 : /* We override this to try and fetch the table SRID from the */
2466 : /* geometry_columns table if the srsid is UNDETERMINED_SRID */
2467 : /* (meaning we haven't yet even looked for it). */
2468 : /************************************************************************/
2469 :
2470 20 : OGRSpatialReference *OGRPGTableLayer::GetSpatialRef()
2471 :
2472 : {
2473 20 : if( nSRSId == UNDETERMINED_SRID )
2474 : {
2475 8 : PGconn *hPGConn = poDS->GetPGConn();
2476 8 : PGresult *hResult = NULL;
2477 8 : CPLString osCommand;
2478 :
2479 8 : nSRSId = poDS->GetUndefinedSRID();
2480 :
2481 8 : poDS->SoftStartTransaction();
2482 :
2483 : osCommand.Printf(
2484 : "SELECT srid FROM geometry_columns "
2485 : "WHERE f_table_name = '%s'",
2486 8 : pszTableName);
2487 :
2488 8 : if (pszGeomColumn)
2489 : {
2490 4 : osCommand += CPLString().Printf(" AND f_geometry_column = '%s'", pszGeomColumn);
2491 : }
2492 :
2493 8 : if (pszSchemaName)
2494 : {
2495 8 : osCommand += CPLString().Printf(" AND f_table_schema = '%s'", pszSchemaName);
2496 : }
2497 :
2498 8 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str() );
2499 :
2500 8 : if( hResult
2501 : && PQresultStatus(hResult) == PGRES_TUPLES_OK
2502 : && PQntuples(hResult) == 1 )
2503 : {
2504 0 : nSRSId = atoi(PQgetvalue(hResult,0,0));
2505 : }
2506 :
2507 8 : OGRPGClearResult( hResult );
2508 :
2509 8 : poDS->SoftCommit();
2510 : }
2511 :
2512 20 : return OGRPGLayer::GetSpatialRef();
2513 : }
2514 :
2515 : /************************************************************************/
2516 : /* GetExtent() */
2517 : /* */
2518 : /* For PostGIS use internal Extend(geometry) function */
2519 : /* in other cases we use standard OGRLayer::GetExtent() */
2520 : /************************************************************************/
2521 :
2522 14 : OGRErr OGRPGTableLayer::GetExtent( OGREnvelope *psExtent, int bForce )
2523 : {
2524 14 : CPLString osCommand;
2525 :
2526 14 : GetLayerDefn();
2527 :
2528 : const char* pszExtentFct;
2529 14 : if (poDS->sPostGISVersion.nMajor >= 2)
2530 0 : pszExtentFct = "ST_Extent";
2531 : else
2532 14 : pszExtentFct = "Extent";
2533 :
2534 14 : if ( TestCapability(OLCFastGetExtent) )
2535 : {
2536 : osCommand.Printf( "SELECT %s(%s) FROM %s", pszExtentFct,
2537 6 : OGRPGEscapeColumnName(pszGeomColumn).c_str(), pszSqlTableName );
2538 : }
2539 8 : else if ( bHasPostGISGeography )
2540 : {
2541 : /* Probably not very efficient, but more efficient than client-side implementation */
2542 : osCommand.Printf( "SELECT %s(ST_GeomFromWKB(ST_AsBinary(%s))) FROM %s",
2543 : pszExtentFct,
2544 2 : OGRPGEscapeColumnName(pszGeomColumn).c_str(), pszSqlTableName );
2545 : }
2546 :
2547 14 : return RunGetExtentRequest(psExtent, bForce, osCommand);
2548 : }
2549 :
2550 : /************************************************************************/
2551 : /* StartCopy() */
2552 : /************************************************************************/
2553 :
2554 20 : OGRErr OGRPGTableLayer::StartCopy(int bSetFID)
2555 :
2556 : {
2557 20 : OGRErr result = OGRERR_NONE;
2558 :
2559 : /* Tell the datasource we are now planning to copy data */
2560 20 : poDS->StartCopy( this );
2561 :
2562 20 : CPLString osFields = BuildCopyFields(bSetFID);
2563 :
2564 20 : int size = strlen(osFields) + strlen(pszSqlTableName) + 100;
2565 20 : char *pszCommand = (char *) CPLMalloc(size);
2566 :
2567 : sprintf( pszCommand,
2568 : "COPY %s (%s) FROM STDIN;",
2569 20 : pszSqlTableName, osFields.c_str() );
2570 :
2571 20 : PGconn *hPGConn = poDS->GetPGConn();
2572 20 : PGresult *hResult = OGRPG_PQexec(hPGConn, pszCommand);
2573 :
2574 20 : if ( !hResult || (PQresultStatus(hResult) != PGRES_COPY_IN))
2575 : {
2576 : CPLError( CE_Failure, CPLE_AppDefined,
2577 0 : "%s", PQerrorMessage(hPGConn) );
2578 0 : result = OGRERR_FAILURE;
2579 : }
2580 : else
2581 20 : bCopyActive = TRUE;
2582 :
2583 20 : OGRPGClearResult( hResult );
2584 20 : CPLFree( pszCommand );
2585 :
2586 20 : return OGRERR_NONE;
2587 : }
2588 :
2589 : /************************************************************************/
2590 : /* EndCopy() */
2591 : /************************************************************************/
2592 :
2593 15892 : OGRErr OGRPGTableLayer::EndCopy()
2594 :
2595 : {
2596 15892 : if( !bCopyActive )
2597 15872 : return OGRERR_NONE;
2598 :
2599 : /* This method is called from the datasource when
2600 : a COPY operation is ended */
2601 20 : OGRErr result = OGRERR_NONE;
2602 :
2603 20 : PGconn *hPGConn = poDS->GetPGConn();
2604 20 : CPLDebug( "PG", "PQputCopyEnd()" );
2605 :
2606 20 : bCopyActive = FALSE;
2607 :
2608 : /* This is for postgresql 7.4 and higher */
2609 : #if !defined(PG_PRE74)
2610 20 : int copyResult = PQputCopyEnd(hPGConn, NULL);
2611 :
2612 20 : switch (copyResult)
2613 : {
2614 : case 0:
2615 0 : CPLError( CE_Failure, CPLE_AppDefined, "Writing COPY data blocked.");
2616 0 : result = OGRERR_FAILURE;
2617 0 : break;
2618 : case -1:
2619 0 : CPLError( CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn) );
2620 0 : result = OGRERR_FAILURE;
2621 : break;
2622 : }
2623 :
2624 : #else /* defined(PG_PRE74) */
2625 : PQputline(hPGConn, "\\.\n");
2626 : int copyResult = PQendcopy(hPGConn);
2627 :
2628 : if (copyResult != 0)
2629 : {
2630 : CPLError( CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn) );
2631 : result = OGRERR_FAILURE;
2632 : }
2633 : #endif /* defined(PG_PRE74) */
2634 :
2635 : /* Now check the results of the copy */
2636 20 : PGresult * hResult = PQgetResult( hPGConn );
2637 :
2638 20 : if( hResult && PQresultStatus(hResult) != PGRES_COMMAND_OK )
2639 : {
2640 : CPLError( CE_Failure, CPLE_AppDefined,
2641 : "COPY statement failed.\n%s",
2642 0 : PQerrorMessage(hPGConn) );
2643 :
2644 0 : result = OGRERR_FAILURE;
2645 : }
2646 :
2647 20 : OGRPGClearResult( hResult );
2648 :
2649 20 : bUseCopy = USE_COPY_UNSET;
2650 :
2651 20 : return result;
2652 : }
2653 :
2654 : /************************************************************************/
2655 : /* BuildCopyFields() */
2656 : /************************************************************************/
2657 :
2658 20 : CPLString OGRPGTableLayer::BuildCopyFields(int bSetFID)
2659 : {
2660 20 : int i = 0;
2661 20 : int nFIDIndex = -1;
2662 20 : CPLString osFieldList;
2663 :
2664 20 : if( pszGeomColumn != NULL )
2665 : {
2666 12 : osFieldList = OGRPGEscapeColumnName(pszGeomColumn);
2667 : }
2668 :
2669 20 : bFIDColumnInCopyFields = (pszFIDColumn != NULL && bSetFID);
2670 20 : if( bFIDColumnInCopyFields )
2671 : {
2672 4 : if( osFieldList.size() > 0 )
2673 0 : osFieldList += ", ";
2674 :
2675 4 : nFIDIndex = poFeatureDefn->GetFieldIndex( pszFIDColumn );
2676 :
2677 4 : osFieldList += OGRPGEscapeColumnName(pszFIDColumn);
2678 : }
2679 :
2680 164 : for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
2681 : {
2682 144 : if (i == nFIDIndex)
2683 0 : continue;
2684 :
2685 144 : const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
2686 :
2687 144 : if( osFieldList.size() > 0 )
2688 140 : osFieldList += ", ";
2689 :
2690 144 : osFieldList += OGRPGEscapeColumnName(pszName);
2691 : }
2692 :
2693 0 : return osFieldList;
2694 : }
2695 :
2696 : /************************************************************************/
2697 : /* CheckGeomTypeCompatibility() */
2698 : /************************************************************************/
2699 :
2700 234 : void OGRPGTableLayer::CheckGeomTypeCompatibility(OGRGeometry* poGeom)
2701 : {
2702 234 : if (bHasWarnedIncompatibleGeom)
2703 0 : return;
2704 :
2705 234 : OGRwkbGeometryType eFlatLayerGeomType = wkbFlatten(poFeatureDefn->GetGeomType());
2706 234 : OGRwkbGeometryType eFlatGeomType = wkbFlatten(poGeom->getGeometryType());
2707 234 : if (eFlatLayerGeomType == wkbUnknown)
2708 112 : return;
2709 :
2710 122 : if (eFlatLayerGeomType == wkbGeometryCollection)
2711 : bHasWarnedIncompatibleGeom = eFlatGeomType != wkbMultiPoint &&
2712 : eFlatGeomType != wkbMultiLineString &&
2713 : eFlatGeomType != wkbMultiPolygon &&
2714 0 : eFlatGeomType != wkbGeometryCollection;
2715 : else
2716 122 : bHasWarnedIncompatibleGeom = (eFlatGeomType != eFlatLayerGeomType);
2717 :
2718 122 : if (bHasWarnedIncompatibleGeom)
2719 : {
2720 : CPLError(CE_Warning, CPLE_AppDefined,
2721 : "Geometry to be inserted is of type %s, whereas the layer geometry type is %s.\n"
2722 : "Insertion is likely to fail",
2723 0 : OGRGeometryTypeToName(poGeom->getGeometryType()),
2724 0 : OGRGeometryTypeToName(poFeatureDefn->GetGeomType()));
2725 : }
2726 : }
2727 :
2728 : /************************************************************************/
2729 : /* GetLayerDefnCanReturnNULL() */
2730 : /************************************************************************/
2731 :
2732 558 : OGRFeatureDefn * OGRPGTableLayer::GetLayerDefnCanReturnNULL()
2733 : {
2734 558 : if (poFeatureDefn)
2735 0 : return poFeatureDefn;
2736 :
2737 558 : poFeatureDefn = ReadTableDefinition();
2738 :
2739 558 : if( poFeatureDefn )
2740 : {
2741 438 : ResetReading();
2742 : }
2743 :
2744 558 : return poFeatureDefn;
2745 : }
2746 :
2747 : /************************************************************************/
2748 : /* GetLayerDefn() */
2749 : /************************************************************************/
2750 :
2751 18208 : OGRFeatureDefn * OGRPGTableLayer::GetLayerDefn()
2752 : {
2753 18208 : if (poFeatureDefn)
2754 17944 : return poFeatureDefn;
2755 :
2756 264 : GetLayerDefnCanReturnNULL();
2757 264 : if (poFeatureDefn == NULL)
2758 : {
2759 4 : poFeatureDefn = new OGRFeatureDefn(pszTableName);
2760 4 : poFeatureDefn->Reference();
2761 : }
2762 264 : return poFeatureDefn;
2763 : }
|