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