1 : /******************************************************************************
2 : * $Id: ogrpgtablelayer.cpp 17989 2009-11-11 12:03:31Z chaitanya $
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 "ogrpgutility.h"
32 : #include "cpl_conv.h"
33 : #include "cpl_string.h"
34 : #include "cpl_error.h"
35 :
36 : CPL_CVSID("$Id: ogrpgtablelayer.cpp 17989 2009-11-11 12:03:31Z chaitanya $");
37 :
38 : #define USE_COPY_UNSET -10
39 : static CPLString OGRPGEscapeStringList(PGconn *hPGConn,
40 : char** papszItems, int bForInsertOrUpdate);
41 : static CPLString OGRPGEscapeString(PGconn *hPGConn,
42 : const char* pszStrValue, int nMaxLength,
43 : const char* pszFieldName);
44 :
45 : /************************************************************************/
46 : /* OGRPGTableLayer() */
47 : /************************************************************************/
48 :
49 412 : 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 412 : int nSRSIdIn )
57 :
58 : {
59 412 : poDS = poDSIn;
60 :
61 412 : pszQueryStatement = NULL;
62 :
63 412 : bUpdateAccess = bUpdate;
64 :
65 412 : iNextShapeId = 0;
66 :
67 412 : nSRSId = nSRSIdIn;
68 :
69 412 : bLaunderColumnNames = TRUE;
70 412 : bCopyActive = FALSE;
71 412 : bUseCopy = USE_COPY_UNSET; // unknown
72 :
73 412 : pszTableName = CPLStrdup( pszTableNameIn );
74 412 : pszSchemaName = NULL; // set in ReadTableDefinition
75 412 : pszSqlTableName = NULL; //set in ReadTableDefinition
76 412 : pszSqlGeomParentTableName = NULL;
77 :
78 : poFeatureDefn = ReadTableDefinition( osCurrentSchema,
79 : pszTableName,
80 : pszSchemaNameIn,
81 : pszGeomColumnIn,
82 412 : bAdvertizeGeomColumn );
83 :
84 412 : if( poFeatureDefn )
85 : {
86 409 : ResetReading();
87 :
88 : // check SRID if it's necessary
89 409 : if( nSRSId == -2 )
90 136 : GetSpatialRef();
91 : }
92 :
93 412 : bHasWarnedIncompatibleGeom = FALSE;
94 412 : }
95 :
96 : //************************************************************************/
97 : /* ~OGRPGTableLayer() */
98 : /************************************************************************/
99 :
100 824 : OGRPGTableLayer::~OGRPGTableLayer()
101 :
102 : {
103 412 : EndCopy();
104 412 : CPLFree( pszSqlTableName );
105 412 : CPLFree( pszTableName );
106 412 : CPLFree( pszSqlGeomParentTableName );
107 412 : CPLFree( pszSchemaName );
108 824 : }
109 :
110 : /************************************************************************/
111 : /* ReadTableDefinition() */
112 : /* */
113 : /* Build a schema from the named table. Done by querying the */
114 : /* catalog. */
115 : /************************************************************************/
116 :
117 412 : OGRFeatureDefn *OGRPGTableLayer::ReadTableDefinition( CPLString& osCurrentSchema,
118 : const char * pszTableIn,
119 : const char * pszSchemaNameIn,
120 : const char * pszGeomColumnIn,
121 : int bAdvertizeGeomColumn)
122 :
123 : {
124 : PGresult *hResult;
125 412 : CPLString osCommand;
126 412 : CPLString osPrimaryKey;
127 412 : PGconn *hPGConn = poDS->GetPGConn();
128 :
129 412 : poDS->FlushSoftTransaction();
130 :
131 : /* -------------------------------------------- */
132 : /* Detect table primary key */
133 : /* -------------------------------------------- */
134 :
135 : /* -------------------------------------------- */
136 : /* Check config options */
137 : /* -------------------------------------------- */
138 412 : osPrimaryKey = CPLGetConfigOption( "PGSQL_OGR_FID", "ogc_fid" );
139 :
140 412 : if (pszSchemaNameIn)
141 405 : pszSchemaName = CPLStrdup( pszSchemaNameIn );
142 7 : else if (strlen(osCurrentSchema))
143 7 : pszSchemaName = CPLStrdup( osCurrentSchema );
144 :
145 412 : CPLString osSchemaClause;
146 412 : if( pszSchemaName )
147 412 : osSchemaClause.Printf("AND n.nspname='%s'", pszSchemaName);
148 :
149 : const char* pszTypnameEqualsAnyClause;
150 412 : if (poDS->sPostgreSQLVersion.nMajor == 7 && poDS->sPostgreSQLVersion.nMinor <= 3)
151 0 : pszTypnameEqualsAnyClause = "ANY(SELECT '{int2, int4, serial}')";
152 : else
153 412 : pszTypnameEqualsAnyClause = "ANY(ARRAY['int2','int4','serial'])";
154 :
155 : /* See #1889 for why we don't use 'AND a.attnum = ANY(i.indkey)' */
156 : osCommand.Printf("SELECT a.attname, a.attnum, t.typname, "
157 : "t.typname = %s AS isfid "
158 : "FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n, pg_index i "
159 : "WHERE a.attnum > 0 AND a.attrelid = c.oid "
160 : "AND a.atttypid = t.oid AND c.relnamespace = n.oid "
161 : "AND c.oid = i.indrelid AND i.indisprimary = 't' "
162 : "AND t.typname !~ '^geom' AND c.relname = '%s' "
163 : "AND (i.indkey[0]=a.attnum OR i.indkey[1]=a.attnum OR i.indkey[2]=a.attnum "
164 : "OR i.indkey[3]=a.attnum OR i.indkey[4]=a.attnum OR i.indkey[5]=a.attnum "
165 : "OR i.indkey[6]=a.attnum OR i.indkey[7]=a.attnum OR i.indkey[8]=a.attnum "
166 : "OR i.indkey[9]=a.attnum) %s ORDER BY a.attnum",
167 412 : pszTypnameEqualsAnyClause, pszTableIn, osSchemaClause.c_str() );
168 :
169 412 : hResult = PQexec(hPGConn, osCommand.c_str() );
170 :
171 412 : if ( hResult && PGRES_TUPLES_OK == PQresultStatus(hResult) )
172 : {
173 412 : if ( PQntuples( hResult ) == 1 && PQgetisnull( hResult,0,0 ) == false )
174 : {
175 : /* Check if single-field PK can be represented as 32-bit integer. */
176 305 : CPLString osValue(PQgetvalue(hResult, 0, 3));
177 305 : if( osValue == "t" )
178 : {
179 305 : osPrimaryKey.Printf( "%s", PQgetvalue(hResult,0,0) );
180 305 : CPLDebug( "PG", "Primary key name (FID): %s", osPrimaryKey.c_str() );
181 305 : }
182 : }
183 107 : else if ( PQntuples( hResult ) > 1 )
184 : {
185 : CPLError( CE_Warning, CPLE_AppDefined,
186 : "Multi-column primary key in \'%s\' detected but not supported.",
187 1 : pszTableIn );
188 : }
189 :
190 412 : OGRPGClearResult( hResult );
191 : /* Zero tuples means no PK is defined, perfectly valid case. */
192 : }
193 : else
194 : {
195 : CPLError( CE_Failure, CPLE_AppDefined,
196 0 : "%s", PQerrorMessage(hPGConn) );
197 : }
198 :
199 : /* -------------------------------------------------------------------- */
200 : /* Fire off commands to get back the columns of the table. */
201 : /* -------------------------------------------------------------------- */
202 412 : hResult = PQexec(hPGConn, "BEGIN");
203 :
204 412 : if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK )
205 : {
206 412 : OGRPGClearResult( hResult );
207 :
208 : osCommand.Printf(
209 : "DECLARE mycursor CURSOR for "
210 : "SELECT DISTINCT a.attname, t.typname, a.attlen,"
211 : " format_type(a.atttypid,a.atttypmod) "
212 : "FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n "
213 : "WHERE c.relname = '%s' "
214 : "AND a.attnum > 0 AND a.attrelid = c.oid "
215 : "AND a.atttypid = t.oid "
216 : "AND c.relnamespace=n.oid "
217 : "%s",
218 412 : pszTableIn, osSchemaClause.c_str());
219 :
220 412 : hResult = PQexec(hPGConn, osCommand.c_str() );
221 : }
222 :
223 412 : if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK )
224 : {
225 412 : OGRPGClearResult( hResult );
226 412 : hResult = PQexec(hPGConn, "FETCH ALL in mycursor" );
227 : }
228 :
229 412 : if( !hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK )
230 : {
231 0 : OGRPGClearResult( hResult );
232 :
233 : CPLError( CE_Failure, CPLE_AppDefined,
234 0 : "%s", PQerrorMessage(hPGConn) );
235 0 : return NULL;
236 : }
237 :
238 412 : if( PQntuples(hResult) == 0 )
239 : {
240 3 : OGRPGClearResult( hResult );
241 :
242 3 : hResult = PQexec(hPGConn, "CLOSE mycursor");
243 3 : OGRPGClearResult( hResult );
244 :
245 3 : hResult = PQexec(hPGConn, "COMMIT");
246 3 : OGRPGClearResult( hResult );
247 :
248 : CPLError( CE_Failure, CPLE_AppDefined,
249 : "No field definitions found for '%s', is it a table?",
250 3 : pszTableIn );
251 3 : return NULL;
252 : }
253 :
254 : /* -------------------------------------------------------------------- */
255 : /* Parse the returned table information. */
256 : /* -------------------------------------------------------------------- */
257 409 : CPLString osDefnName;
258 409 : if ( pszSchemaNameIn && osCurrentSchema != pszSchemaNameIn )
259 : {
260 : /* For backwards compatibility, don't report the geometry column name */
261 : /* if it's wkb_geometry */
262 71 : if (bAdvertizeGeomColumn && pszGeomColumnIn)
263 3 : osDefnName.Printf( "%s.%s(%s)", pszSchemaNameIn, pszTableIn, pszGeomColumnIn );
264 : else
265 65 : osDefnName.Printf("%s.%s", pszSchemaNameIn, pszTableIn );
266 68 : pszSqlTableName = CPLStrdup(CPLString().Printf("\"%s\".\"%s\"", pszSchemaNameIn, pszTableIn ));
267 : }
268 : else
269 : {
270 : //no prefix for current_schema in layer name, for backwards compatibility
271 : /* For backwards compatibility, don't report the geometry column name */
272 : /* if it's wkb_geometry */
273 361 : if (bAdvertizeGeomColumn && pszGeomColumnIn)
274 20 : osDefnName.Printf( "%s(%s)", pszTableIn, pszGeomColumnIn );
275 : else
276 321 : osDefnName = pszTableIn;
277 341 : pszSqlTableName = CPLStrdup(CPLString().Printf("\"%s\"", pszTableIn ));
278 : }
279 :
280 409 : OGRFeatureDefn *poDefn = new OGRFeatureDefn( osDefnName );
281 : int iRecord;
282 :
283 409 : poDefn->Reference();
284 409 : poDefn->SetGeomType( wkbNone );
285 409 : if (pszGeomColumnIn)
286 233 : pszGeomColumn = CPLStrdup(pszGeomColumnIn);
287 :
288 409 : for( iRecord = 0; iRecord < PQntuples(hResult); iRecord++ )
289 : {
290 3006 : const char *pszType = NULL;
291 3006 : const char *pszFormatType = NULL;
292 3006 : OGRFieldDefn oField( PQgetvalue( hResult, iRecord, 0 ), OFTString);
293 :
294 3006 : pszType = PQgetvalue(hResult, iRecord, 1 );
295 3006 : pszFormatType = PQgetvalue(hResult,iRecord,3);
296 :
297 : /* TODO: Add detection of other primary key to use as FID */
298 3006 : if( EQUAL(oField.GetNameRef(),osPrimaryKey) )
299 : {
300 361 : bHasFid = TRUE;
301 361 : pszFIDColumn = CPLStrdup(oField.GetNameRef());
302 361 : CPLDebug("PG","Using column '%s' as FID for table '%s'", pszFIDColumn, pszTableIn );
303 361 : continue;
304 : }
305 2645 : else if( EQUAL(pszType,"geometry") )
306 : {
307 330 : bHasPostGISGeometry = TRUE;
308 330 : if (!pszGeomColumn)
309 28 : pszGeomColumn = CPLStrdup(oField.GetNameRef());
310 330 : poDefn->SetGeomType( wkbUnknown );
311 330 : continue;
312 : }
313 2315 : else if( EQUAL(pszType,"geography") )
314 : {
315 0 : bHasPostGISGeography = TRUE;
316 0 : if (!pszGeomColumn)
317 0 : pszGeomColumn = CPLStrdup(oField.GetNameRef());
318 0 : continue;
319 : }
320 2315 : else if( EQUAL(oField.GetNameRef(),"WKB_GEOMETRY") )
321 : {
322 147 : if (!pszGeomColumn)
323 : {
324 147 : bHasWkb = TRUE;
325 147 : pszGeomColumn = CPLStrdup(oField.GetNameRef());
326 147 : if( EQUAL(pszType,"OID") )
327 0 : bWkbAsOid = TRUE;
328 : }
329 147 : poDefn->SetGeomType( wkbUnknown );
330 147 : continue;
331 : }
332 :
333 2168 : if( EQUAL(pszType,"text") )
334 : {
335 33 : oField.SetType( OFTString );
336 : }
337 2303 : else if( EQUAL(pszType,"_bpchar") ||
338 : EQUAL(pszType,"_varchar") ||
339 : EQUAL(pszType,"_text"))
340 : {
341 168 : oField.SetType( OFTStringList );
342 : }
343 2478 : else if( EQUAL(pszType,"bpchar") || EQUAL(pszType,"varchar") )
344 : {
345 : int nWidth;
346 :
347 511 : nWidth = atoi(PQgetvalue(hResult,iRecord,2));
348 511 : if( nWidth == -1 )
349 : {
350 511 : if( EQUALN(pszFormatType,"character(",10) )
351 310 : nWidth = atoi(pszFormatType+10);
352 201 : else if( EQUALN(pszFormatType,"character varying(",18) )
353 5 : nWidth = atoi(pszFormatType+18);
354 : else
355 196 : nWidth = 0;
356 : }
357 511 : oField.SetType( OFTString );
358 511 : oField.SetWidth( nWidth );
359 : }
360 1456 : else if( EQUAL(pszType,"bool") )
361 : {
362 33 : oField.SetType( OFTInteger );
363 33 : oField.SetWidth( 1 );
364 : }
365 1423 : else if( EQUAL(pszType,"numeric") )
366 : {
367 163 : const char *pszFormatName = PQgetvalue(hResult,iRecord,3);
368 163 : const char *pszPrecision = strstr(pszFormatName,",");
369 163 : int nWidth, nPrecision = 0;
370 :
371 163 : nWidth = atoi(pszFormatName + 8);
372 163 : if( pszPrecision != NULL )
373 163 : nPrecision = atoi(pszPrecision+1);
374 :
375 163 : if( nPrecision == 0 )
376 : {
377 : // FIXME : If nWidth > 10, OFTInteger may not be large enough */
378 102 : oField.SetType( OFTInteger );
379 : }
380 : else
381 61 : oField.SetType( OFTReal );
382 :
383 163 : oField.SetWidth( nWidth );
384 163 : oField.SetPrecision( nPrecision );
385 : }
386 1260 : else if( EQUAL(pszFormatType,"integer[]") )
387 : {
388 56 : oField.SetType( OFTIntegerList );
389 : }
390 1316 : else if( EQUAL(pszFormatType, "float[]") ||
391 : EQUAL(pszFormatType, "real[]") ||
392 : EQUAL(pszFormatType, "double precision[]") )
393 : {
394 112 : oField.SetType( OFTRealList );
395 : }
396 1092 : else if( EQUAL(pszType,"int2") )
397 : {
398 33 : oField.SetType( OFTInteger );
399 33 : oField.SetWidth( 5 );
400 : }
401 1059 : else if( EQUAL(pszType,"int8") )
402 : {
403 : /* FIXME: OFTInteger can not handle 64bit integers */
404 33 : oField.SetType( OFTInteger );
405 : }
406 1026 : else if( EQUALN(pszType,"int",3) )
407 : {
408 200 : oField.SetType( OFTInteger );
409 : }
410 1120 : else if( EQUALN(pszType,"float",5) ||
411 : EQUALN(pszType,"double",6) ||
412 : EQUAL(pszType,"real") )
413 : {
414 294 : oField.SetType( OFTReal );
415 : }
416 532 : else if( EQUALN(pszType, "timestamp",9) )
417 : {
418 220 : oField.SetType( OFTDateTime );
419 : }
420 312 : else if( EQUALN(pszType, "date",4) )
421 : {
422 128 : oField.SetType( OFTDate );
423 : }
424 184 : else if( EQUALN(pszType, "time",4) )
425 : {
426 128 : oField.SetType( OFTTime );
427 : }
428 56 : else if( EQUAL(pszType,"bytea") )
429 : {
430 56 : oField.SetType( OFTBinary );
431 : }
432 :
433 : else
434 : {
435 : CPLDebug( "PG", "Field %s is of unknown format type %s (type=%s).",
436 0 : oField.GetNameRef(), pszFormatType, pszType );
437 : }
438 :
439 2168 : poDefn->AddFieldDefn( &oField );
440 : }
441 :
442 409 : OGRPGClearResult( hResult );
443 :
444 409 : hResult = PQexec(hPGConn, "CLOSE mycursor");
445 409 : OGRPGClearResult( hResult );
446 :
447 409 : hResult = PQexec(hPGConn, "COMMIT");
448 409 : OGRPGClearResult( hResult );
449 :
450 : // get layer geometry type (for PostGIS dataset)
451 409 : if ( bHasPostGISGeometry || bHasPostGISGeography )
452 : {
453 : /* Get the geometry type and dimensions from the table, or */
454 : /* from its parents if it is a derived table, or from the parent of the parent, etc.. */
455 260 : int bGoOn = TRUE;
456 817 : while(bGoOn)
457 : {
458 : osCommand.Printf(
459 : "SELECT type, coord_dimension%s FROM %s WHERE f_table_name='%s'",
460 : (nSRSId == -2) ? ", srid" : "",
461 : (bHasPostGISGeometry) ? "geometry_columns" : "geography_columns",
462 297 : (pszSqlGeomParentTableName) ? pszSqlGeomParentTableName : pszTableIn);
463 297 : if (pszGeomColumn)
464 : {
465 : osCommand += CPLString().Printf(" AND %s='%s'",
466 : (bHasPostGISGeometry) ? "f_geometry_column" : "f_geography_column",
467 297 : pszGeomColumn);
468 : }
469 297 : if (pszSchemaName)
470 : {
471 297 : osCommand += CPLString().Printf(" AND f_table_schema='%s'", pszSchemaName);
472 : }
473 :
474 297 : hResult = PQexec(hPGConn,osCommand);
475 :
476 297 : if ( hResult && PQntuples(hResult) == 1 && !PQgetisnull(hResult,0,0) )
477 : {
478 260 : char * pszType = PQgetvalue(hResult,0,0);
479 260 : OGRwkbGeometryType nGeomType = wkbUnknown;
480 :
481 260 : nCoordDimension = MAX(2,MIN(3,atoi(PQgetvalue(hResult,0,1))));
482 :
483 260 : if (nSRSId == -2)
484 243 : nSRSId = atoi(PQgetvalue(hResult,0,2));
485 :
486 : // check only standard OGC geometry types
487 260 : if ( EQUAL(pszType, "POINT") )
488 76 : nGeomType = wkbPoint;
489 184 : else if ( EQUAL(pszType,"LINESTRING"))
490 0 : nGeomType = wkbLineString;
491 184 : else if ( EQUAL(pszType,"POLYGON"))
492 8 : nGeomType = wkbPolygon;
493 176 : else if ( EQUAL(pszType,"MULTIPOINT"))
494 0 : nGeomType = wkbMultiPoint;
495 176 : else if ( EQUAL(pszType,"MULTILINESTRING"))
496 0 : nGeomType = wkbMultiLineString;
497 176 : else if ( EQUAL(pszType,"MULTIPOLYGON"))
498 0 : nGeomType = wkbMultiPolygon;
499 176 : else if ( EQUAL(pszType,"GEOMETRYCOLLECTION"))
500 0 : nGeomType = wkbGeometryCollection;
501 :
502 260 : if( nCoordDimension == 3 && nGeomType != wkbUnknown )
503 14 : nGeomType = (OGRwkbGeometryType) (nGeomType | wkb25DBit);
504 :
505 : CPLDebug("PG","Layer '%s' geometry type: %s:%s, Dim=%d",
506 : pszTableIn, pszType, OGRGeometryTypeToName(nGeomType),
507 260 : nCoordDimension );
508 :
509 260 : poDefn->SetGeomType( nGeomType );
510 :
511 260 : bGoOn = FALSE;
512 : }
513 : else
514 : {
515 : /* Fetch the name of the parent table */
516 : osCommand.Printf("SELECT pg_class.relname FROM pg_class WHERE oid = "
517 : "(SELECT pg_inherits.inhparent FROM pg_inherits WHERE inhrelid = "
518 : "(SELECT pg_class.oid FROM pg_class WHERE relname = '%s'))",
519 37 : (pszSqlGeomParentTableName) ? pszSqlGeomParentTableName : pszTableIn );
520 :
521 37 : OGRPGClearResult( hResult );
522 37 : hResult = PQexec(hPGConn, osCommand.c_str() );
523 :
524 37 : if ( hResult && PQntuples( hResult ) == 1 && !PQgetisnull( hResult,0,0 ) )
525 : {
526 37 : CPLFree(pszSqlGeomParentTableName);
527 37 : pszSqlGeomParentTableName = CPLStrdup( PQgetvalue(hResult,0,0) );
528 : }
529 : else
530 : {
531 : /* No more parent : stop recursion */
532 0 : bGoOn = FALSE;
533 : }
534 : }
535 :
536 297 : OGRPGClearResult( hResult );
537 : }
538 : }
539 :
540 409 : return poDefn;
541 : }
542 :
543 : /************************************************************************/
544 : /* SetSpatialFilter() */
545 : /************************************************************************/
546 :
547 6 : void OGRPGTableLayer::SetSpatialFilter( OGRGeometry * poGeomIn )
548 :
549 : {
550 6 : if( InstallFilter( poGeomIn ) )
551 : {
552 6 : BuildWhere();
553 :
554 6 : ResetReading();
555 : }
556 6 : }
557 :
558 : /************************************************************************/
559 : /* BuildWhere() */
560 : /* */
561 : /* Build the WHERE statement appropriate to the current set of */
562 : /* criteria (spatial and attribute queries). */
563 : /************************************************************************/
564 :
565 50 : void OGRPGTableLayer::BuildWhere()
566 :
567 : {
568 50 : osWHERE = "";
569 :
570 58 : if( m_poFilterGeom != NULL && (bHasPostGISGeometry || bHasPostGISGeography) )
571 : {
572 4 : OGREnvelope sEnvelope;
573 :
574 4 : m_poFilterGeom->getEnvelope( &sEnvelope );
575 : osWHERE.Printf("WHERE \"%s\" && SetSRID('BOX3D(%.12f %.12f, %.12f %.12f)'::box3d,%d) ",
576 : pszGeomColumn,
577 : sEnvelope.MinX, sEnvelope.MinY,
578 : sEnvelope.MaxX, sEnvelope.MaxY,
579 4 : nSRSId );
580 : }
581 :
582 50 : if( strlen(osQuery) > 0 )
583 : {
584 28 : if( strlen(osWHERE) == 0 )
585 : {
586 27 : osWHERE.Printf( "WHERE %s ", osQuery.c_str() );
587 : }
588 : else
589 : {
590 1 : osWHERE += "AND ";
591 1 : osWHERE += osQuery;
592 : }
593 : }
594 50 : }
595 :
596 : /************************************************************************/
597 : /* BuildFullQueryStatement() */
598 : /************************************************************************/
599 :
600 492 : void OGRPGTableLayer::BuildFullQueryStatement()
601 :
602 : {
603 492 : if( pszQueryStatement != NULL )
604 : {
605 83 : CPLFree( pszQueryStatement );
606 83 : pszQueryStatement = NULL;
607 : }
608 :
609 492 : CPLString osFields = BuildFields();
610 :
611 : pszQueryStatement = (char *)
612 : CPLMalloc(strlen(osFields)+strlen(osWHERE)
613 492 : +strlen(pszSqlTableName) + 40);
614 : sprintf( pszQueryStatement,
615 : "SELECT %s FROM %s %s",
616 492 : osFields.c_str(), pszSqlTableName, osWHERE.c_str() );
617 492 : }
618 :
619 : /************************************************************************/
620 : /* ResetReading() */
621 : /************************************************************************/
622 :
623 492 : void OGRPGTableLayer::ResetReading()
624 :
625 : {
626 492 : bUseCopy = USE_COPY_UNSET;
627 :
628 492 : BuildFullQueryStatement();
629 :
630 492 : OGRPGLayer::ResetReading();
631 492 : }
632 :
633 : /************************************************************************/
634 : /* GetNextFeature() */
635 : /************************************************************************/
636 :
637 195 : OGRFeature *OGRPGTableLayer::GetNextFeature()
638 :
639 : {
640 32 : for( ; TRUE; )
641 : {
642 : OGRFeature *poFeature;
643 :
644 195 : poFeature = GetNextRawFeature();
645 195 : if( poFeature == NULL )
646 12 : return NULL;
647 :
648 : /* We just have to look if there is a geometry filter */
649 : /* If there's a PostGIS geometry column, the spatial filter */
650 : /* is already taken into account in the select request */
651 : /* The attribute filter is always taken into account by the select request */
652 183 : if( m_poFilterGeom == NULL
653 : || bHasPostGISGeometry
654 : || bHasPostGISGeography
655 : || FilterGeometry( poFeature->GetGeometryRef() ) )
656 151 : return poFeature;
657 :
658 32 : delete poFeature;
659 : }
660 : }
661 :
662 : /************************************************************************/
663 : /* BuildFields() */
664 : /* */
665 : /* Build list of fields to fetch, performing any required */
666 : /* transformations (such as on geometry). */
667 : /************************************************************************/
668 :
669 507 : CPLString OGRPGTableLayer::BuildFields()
670 :
671 : {
672 507 : int i = 0;
673 507 : CPLString osFieldList;
674 :
675 507 : if( bHasFid && poFeatureDefn->GetFieldIndex( pszFIDColumn ) == -1 )
676 : {
677 459 : osFieldList += "\"";
678 459 : osFieldList += pszFIDColumn;
679 459 : osFieldList += "\"";
680 : }
681 :
682 507 : if( pszGeomColumn )
683 : {
684 506 : if( strlen(osFieldList) > 0 )
685 459 : osFieldList += ", ";
686 :
687 506 : if( bHasPostGISGeometry )
688 : {
689 312 : if ( poDS->bUseBinaryCursor )
690 : {
691 12 : osFieldList += "AsEWKB(\"";
692 12 : osFieldList += pszGeomColumn;
693 12 : osFieldList += "\")";
694 : }
695 300 : else if ( poDS->sPostGISVersion.nMajor >= 1 )
696 : {
697 298 : osFieldList += "AsEWKT(\"";
698 298 : osFieldList += pszGeomColumn;
699 298 : osFieldList += "\")";
700 : }
701 : else
702 : {
703 2 : osFieldList += "AsText(\"";
704 2 : osFieldList += pszGeomColumn;
705 2 : osFieldList += "\")";
706 : }
707 : }
708 194 : else if ( bHasPostGISGeography )
709 : {
710 0 : if ( poDS->bUseBinaryCursor )
711 : {
712 0 : osFieldList += "ST_AsBinary(\"";
713 0 : osFieldList += pszGeomColumn;
714 0 : osFieldList += "\")";
715 : }
716 : else
717 : {
718 0 : osFieldList += "ST_AsText(\"";
719 0 : osFieldList += pszGeomColumn;
720 0 : osFieldList += "\")";
721 : }
722 : }
723 : else
724 : {
725 194 : osFieldList += "\"";
726 194 : osFieldList += pszGeomColumn;
727 194 : osFieldList += "\"";
728 : }
729 : }
730 :
731 3075 : for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
732 : {
733 2568 : const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
734 :
735 2568 : if( strlen(osFieldList) > 0 )
736 2567 : osFieldList += ", ";
737 :
738 : /* With a binary cursor, it is not possible to get the time zone */
739 : /* of a timestamptz column. So we fallback to asking it in text mode */
740 2568 : if ( poDS->bUseBinaryCursor &&
741 : poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDateTime)
742 : {
743 20 : osFieldList += "CAST (\"";
744 20 : osFieldList += pszName ;
745 20 : osFieldList += "\" AS text)";
746 : }
747 : else
748 : {
749 2548 : osFieldList += "\"";
750 2548 : osFieldList += pszName;
751 2548 : osFieldList += "\"" ;
752 : }
753 : }
754 :
755 0 : return osFieldList;
756 : }
757 :
758 : /************************************************************************/
759 : /* SetAttributeFilter() */
760 : /************************************************************************/
761 :
762 44 : OGRErr OGRPGTableLayer::SetAttributeFilter( const char *pszQuery )
763 :
764 : {
765 44 : if( pszQuery == NULL )
766 16 : osQuery = "";
767 : else
768 28 : osQuery = pszQuery;
769 :
770 44 : BuildWhere();
771 :
772 44 : ResetReading();
773 :
774 44 : return OGRERR_NONE;
775 : }
776 :
777 : /************************************************************************/
778 : /* DeleteFeature() */
779 : /************************************************************************/
780 :
781 2 : OGRErr OGRPGTableLayer::DeleteFeature( long nFID )
782 :
783 : {
784 2 : PGconn *hPGConn = poDS->GetPGConn();
785 2 : PGresult *hResult = NULL;
786 2 : CPLString osCommand;
787 :
788 : /* -------------------------------------------------------------------- */
789 : /* We can only delete features if we have a well defined FID */
790 : /* column to target. */
791 : /* -------------------------------------------------------------------- */
792 2 : if( !bHasFid )
793 : {
794 : CPLError( CE_Failure, CPLE_AppDefined,
795 : "DeleteFeature(%ld) failed. Unable to delete features in tables without\n"
796 : "a recognised FID column.",
797 0 : nFID );
798 0 : return OGRERR_FAILURE;
799 :
800 : }
801 :
802 : /* -------------------------------------------------------------------- */
803 : /* Form the statement to drop the record. */
804 : /* -------------------------------------------------------------------- */
805 : osCommand.Printf( "DELETE FROM %s WHERE \"%s\" = %ld",
806 2 : pszSqlTableName, pszFIDColumn, nFID );
807 :
808 : /* -------------------------------------------------------------------- */
809 : /* Execute the delete. */
810 : /* -------------------------------------------------------------------- */
811 : OGRErr eErr;
812 :
813 2 : eErr = poDS->SoftStartTransaction();
814 2 : if( eErr != OGRERR_NONE )
815 0 : return eErr;
816 :
817 2 : hResult = PQexec(hPGConn, osCommand);
818 :
819 2 : if( PQresultStatus(hResult) != PGRES_COMMAND_OK )
820 : {
821 : CPLError( CE_Failure, CPLE_AppDefined,
822 : "DeleteFeature() DELETE statement failed.\n%s",
823 0 : PQerrorMessage(hPGConn) );
824 :
825 0 : OGRPGClearResult( hResult );
826 :
827 0 : poDS->SoftRollback();
828 0 : eErr = OGRERR_FAILURE;
829 : }
830 : else
831 : {
832 2 : OGRPGClearResult( hResult );
833 :
834 2 : eErr = poDS->SoftCommit();
835 : }
836 :
837 2 : return eErr;
838 : }
839 :
840 : /************************************************************************/
841 : /* AppendFieldValue() */
842 : /* */
843 : /* Used by CreateFeatureViaInsert() and SetFeature() to format a */
844 : /* non-empty field value */
845 : /************************************************************************/
846 :
847 4244 : void OGRPGTableLayer::AppendFieldValue(PGconn *hPGConn, CPLString& osCommand,
848 : OGRFeature* poFeature, int i)
849 : {
850 4244 : int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
851 :
852 : // We need special formatting for integer list values.
853 4244 : if( nOGRFieldType == OFTIntegerList )
854 : {
855 2 : int nCount, nOff = 0, j;
856 2 : const int *panItems = poFeature->GetFieldAsIntegerList(i,&nCount);
857 2 : char *pszNeedToFree = NULL;
858 :
859 2 : pszNeedToFree = (char *) CPLMalloc(nCount * 13 + 10);
860 2 : strcpy( pszNeedToFree, "'{" );
861 6 : for( j = 0; j < nCount; j++ )
862 : {
863 4 : if( j != 0 )
864 2 : strcat( pszNeedToFree+nOff, "," );
865 :
866 4 : nOff += strlen(pszNeedToFree+nOff);
867 4 : sprintf( pszNeedToFree+nOff, "%d", panItems[j] );
868 : }
869 2 : strcat( pszNeedToFree+nOff, "}'" );
870 :
871 2 : osCommand += pszNeedToFree;
872 2 : CPLFree(pszNeedToFree);
873 :
874 2 : return;
875 : }
876 :
877 : // We need special formatting for real list values.
878 4242 : else if( nOGRFieldType == OFTRealList )
879 : {
880 4 : int nCount, nOff = 0, j;
881 4 : const double *padfItems =poFeature->GetFieldAsDoubleList(i,&nCount);
882 4 : char *pszNeedToFree = NULL;
883 :
884 4 : pszNeedToFree = (char *) CPLMalloc(nCount * 40 + 10);
885 4 : strcpy( pszNeedToFree, "'{" );
886 12 : for( j = 0; j < nCount; j++ )
887 : {
888 8 : if( j != 0 )
889 4 : strcat( pszNeedToFree+nOff, "," );
890 :
891 8 : nOff += strlen(pszNeedToFree+nOff);
892 8 : sprintf( pszNeedToFree+nOff, "%.16g", padfItems[j] );
893 : }
894 4 : strcat( pszNeedToFree+nOff, "}'" );
895 :
896 4 : osCommand += pszNeedToFree;
897 4 : CPLFree(pszNeedToFree);
898 :
899 4 : return;
900 : }
901 :
902 : // We need special formatting for string list values.
903 4238 : else if( nOGRFieldType == OFTStringList )
904 : {
905 6 : char **papszItems = poFeature->GetFieldAsStringList(i);
906 :
907 6 : osCommand += OGRPGEscapeStringList(hPGConn, papszItems, TRUE);
908 :
909 6 : return;
910 : }
911 :
912 : // Binary formatting
913 4232 : else if( nOGRFieldType == OFTBinary )
914 : {
915 2 : osCommand += "'";
916 :
917 2 : int nLen = 0;
918 2 : GByte* pabyData = poFeature->GetFieldAsBinary( i, &nLen );
919 2 : char* pszBytea = GByteArrayToBYTEA( pabyData, nLen);
920 :
921 2 : osCommand += pszBytea;
922 :
923 2 : CPLFree(pszBytea);
924 2 : osCommand += "'";
925 :
926 2 : return;
927 : }
928 :
929 : // Flag indicating NULL or not-a-date date value
930 : // e.g. 0000-00-00 - there is no year 0
931 4230 : OGRBoolean bIsDateNull = FALSE;
932 :
933 4230 : const char *pszStrValue = poFeature->GetFieldAsString(i);
934 :
935 : // Check if date is NULL: 0000-00-00
936 4230 : if( nOGRFieldType == OFTDate )
937 : {
938 2 : if( EQUALN( pszStrValue, "0000", 4 ) )
939 : {
940 0 : pszStrValue = "NULL";
941 0 : bIsDateNull = TRUE;
942 : }
943 : }
944 :
945 4330 : if( nOGRFieldType != OFTInteger && nOGRFieldType != OFTReal
946 : && !bIsDateNull )
947 : {
948 : osCommand += OGRPGEscapeString(hPGConn, pszStrValue,
949 : poFeatureDefn->GetFieldDefn(i)->GetWidth(),
950 100 : poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
951 : }
952 : else
953 : {
954 4130 : osCommand += pszStrValue;
955 : }
956 : }
957 :
958 : /************************************************************************/
959 : /* SetFeature() */
960 : /* */
961 : /* SetFeature() is implemented by an UPDATE SQL command */
962 : /************************************************************************/
963 :
964 2004 : OGRErr OGRPGTableLayer::SetFeature( OGRFeature *poFeature )
965 :
966 : {
967 2004 : PGconn *hPGConn = poDS->GetPGConn();
968 2004 : PGresult *hResult = NULL;
969 2004 : CPLString osCommand;
970 2004 : int i = 0;
971 2004 : int bNeedComma = FALSE;
972 2004 : OGRErr eErr = OGRERR_FAILURE;
973 :
974 2004 : if( NULL == poFeature )
975 : {
976 : CPLError( CE_Failure, CPLE_AppDefined,
977 0 : "NULL pointer to OGRFeature passed to SetFeature()." );
978 0 : return eErr;
979 : }
980 :
981 2004 : if( poFeature->GetFID() == OGRNullFID )
982 : {
983 : CPLError( CE_Failure, CPLE_AppDefined,
984 0 : "FID required on features given to SetFeature()." );
985 0 : return eErr;
986 : }
987 :
988 2004 : if( !bHasFid )
989 : {
990 : CPLError( CE_Failure, CPLE_AppDefined,
991 : "Unable to update features in tables without\n"
992 0 : "a recognised FID column.");
993 0 : return eErr;
994 :
995 : }
996 :
997 2004 : eErr = poDS->SoftStartTransaction();
998 2004 : if( eErr != OGRERR_NONE )
999 : {
1000 0 : return eErr;
1001 : }
1002 :
1003 : /* -------------------------------------------------------------------- */
1004 : /* Form the UPDATE command. */
1005 : /* -------------------------------------------------------------------- */
1006 2004 : osCommand.Printf( "UPDATE %s SET ", pszSqlTableName );
1007 :
1008 : /* Set the geometry */
1009 2004 : if( bHasWkb )
1010 : {
1011 1002 : osCommand += "WKB_GEOMETRY = ";
1012 1002 : if ( poFeature->GetGeometryRef() != NULL )
1013 : {
1014 1 : if( !bWkbAsOid )
1015 : {
1016 1 : char *pszBytea = GeometryToBYTEA( poFeature->GetGeometryRef() );
1017 :
1018 1 : if( pszBytea != NULL )
1019 : {
1020 1 : osCommand = osCommand + "'" + pszBytea + "'";
1021 1 : CPLFree( pszBytea );
1022 : }
1023 : else
1024 0 : osCommand += "NULL";
1025 : }
1026 : else
1027 : {
1028 0 : Oid oid = GeometryToOID( poFeature->GetGeometryRef() );
1029 :
1030 0 : if( oid != 0 )
1031 : {
1032 0 : osCommand += CPLString().Printf( "'%d' ", oid );
1033 : }
1034 : else
1035 0 : osCommand += "NULL";
1036 : }
1037 : }
1038 : else
1039 1001 : osCommand += "NULL";
1040 1002 : bNeedComma = TRUE;
1041 : }
1042 1002 : else if( bHasPostGISGeometry || bHasPostGISGeography )
1043 : {
1044 1002 : osCommand = osCommand + "\"" + pszGeomColumn + "\" = ";
1045 1002 : char *pszWKT = NULL;
1046 :
1047 1002 : if( poFeature->GetGeometryRef() != NULL )
1048 : {
1049 1 : OGRGeometry *poGeom = (OGRGeometry *) poFeature->GetGeometryRef();
1050 :
1051 1 : poGeom->closeRings();
1052 1 : poGeom->setCoordinateDimension( nCoordDimension );
1053 :
1054 1 : poGeom->exportToWkt( &pszWKT );
1055 : }
1056 :
1057 1002 : if( pszWKT != NULL )
1058 : {
1059 1 : if( bHasPostGISGeography )
1060 : osCommand +=
1061 : CPLString().Printf(
1062 0 : "ST_GeographyFromText('SRID=%d;%s'::TEXT) ", nSRSId, pszWKT );
1063 1 : else if( poDS->sPostGISVersion.nMajor >= 1 )
1064 : osCommand +=
1065 : CPLString().Printf(
1066 1 : "GeomFromEWKT('SRID=%d;%s'::TEXT) ", nSRSId, pszWKT );
1067 : else
1068 : osCommand +=
1069 : CPLString().Printf(
1070 0 : "GeometryFromText('%s'::TEXT,%d) ", pszWKT, nSRSId );
1071 1 : OGRFree( pszWKT );
1072 : }
1073 : else
1074 1001 : osCommand += "NULL";
1075 :
1076 1002 : bNeedComma = TRUE;
1077 : }
1078 :
1079 4020 : for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
1080 : {
1081 2016 : if( bNeedComma )
1082 2016 : osCommand += ", ";
1083 : else
1084 0 : bNeedComma = TRUE;
1085 :
1086 : osCommand = osCommand
1087 2016 : + "\"" + poFeatureDefn->GetFieldDefn(i)->GetNameRef() + "\" = ";
1088 :
1089 2016 : if( !poFeature->IsFieldSet( i ) )
1090 : {
1091 8 : osCommand += "NULL";
1092 : }
1093 : else
1094 : {
1095 2008 : AppendFieldValue(hPGConn, osCommand, poFeature, i);
1096 : }
1097 : }
1098 :
1099 : /* Add the WHERE clause */
1100 2004 : osCommand += " WHERE ";
1101 2004 : osCommand = osCommand + "\"" + pszFIDColumn + "\" = ";
1102 2004 : osCommand += CPLString().Printf( "%ld ", poFeature->GetFID() );
1103 :
1104 : /* -------------------------------------------------------------------- */
1105 : /* Execute the update. */
1106 : /* -------------------------------------------------------------------- */
1107 2004 : hResult = PQexec(hPGConn, osCommand);
1108 2004 : if( PQresultStatus(hResult) != PGRES_COMMAND_OK )
1109 : {
1110 : CPLError( CE_Failure, CPLE_AppDefined,
1111 : "UPDATE command for feature %ld failed.\n%s\nCommand: %s",
1112 0 : poFeature->GetFID(), PQerrorMessage(hPGConn), osCommand.c_str() );
1113 :
1114 0 : OGRPGClearResult( hResult );
1115 :
1116 0 : poDS->SoftRollback();
1117 :
1118 0 : return OGRERR_FAILURE;
1119 : }
1120 :
1121 2004 : OGRPGClearResult( hResult );
1122 :
1123 2004 : return poDS->SoftCommit();
1124 : }
1125 :
1126 : /************************************************************************/
1127 : /* CreateFeature() */
1128 : /************************************************************************/
1129 :
1130 2102 : OGRErr OGRPGTableLayer::CreateFeature( OGRFeature *poFeature )
1131 : {
1132 2102 : if( NULL == poFeature )
1133 : {
1134 : CPLError( CE_Failure, CPLE_AppDefined,
1135 0 : "NULL pointer to OGRFeature passed to CreateFeature()." );
1136 0 : return OGRERR_FAILURE;
1137 : }
1138 :
1139 : // We avoid testing the config option too often.
1140 2102 : if( bUseCopy == USE_COPY_UNSET )
1141 37 : bUseCopy = CSLTestBoolean( CPLGetConfigOption( "PG_USE_COPY", "NO") );
1142 :
1143 2102 : if( !bUseCopy )
1144 : {
1145 2080 : return CreateFeatureViaInsert( poFeature );
1146 : }
1147 : else
1148 : {
1149 22 : if ( !bCopyActive )
1150 4 : StartCopy();
1151 :
1152 22 : return CreateFeatureViaCopy( poFeature );
1153 : }
1154 : }
1155 :
1156 : /************************************************************************/
1157 : /* EscapeString( ) */
1158 : /************************************************************************/
1159 :
1160 112 : static CPLString OGRPGEscapeString(PGconn *hPGConn,
1161 : const char* pszStrValue, int nMaxLength,
1162 : const char* pszFieldName)
1163 : {
1164 112 : CPLString osCommand;
1165 :
1166 : /* We need to quote and escape string fields. */
1167 112 : osCommand += "'";
1168 :
1169 112 : int nSrcLen = strlen(pszStrValue);
1170 112 : if (nMaxLength > 0 && nSrcLen > nMaxLength)
1171 : {
1172 : CPLDebug( "PG",
1173 : "Truncated %s field value, it was too long.",
1174 2 : pszFieldName );
1175 2 : nSrcLen = nMaxLength;
1176 :
1177 4 : while( nSrcLen > 0 && ((unsigned char *) pszStrValue)[nSrcLen-1] > 127 )
1178 : {
1179 0 : CPLDebug( "PG", "Backup to start of multi-byte character." );
1180 0 : nSrcLen--;
1181 : }
1182 : }
1183 :
1184 112 : char* pszDestStr = (char*)CPLMalloc(2 * nSrcLen + 1);
1185 :
1186 : /* -------------------------------------------------------------------- */
1187 : /* PQescapeStringConn was introduced in PostgreSQL security releases */
1188 : /* 8.1.4, 8.0.8, 7.4.13, 7.3.15 */
1189 : /* PG_HAS_PQESCAPESTRINGCONN is added by a test in 'configure' */
1190 : /* so it is not set by default when building OGR for Win32 */
1191 : /* -------------------------------------------------------------------- */
1192 : #if defined(PG_HAS_PQESCAPESTRINGCONN)
1193 : int nError;
1194 112 : PQescapeStringConn (hPGConn, pszDestStr, pszStrValue, nSrcLen, &nError);
1195 112 : if (nError == 0)
1196 112 : osCommand += pszDestStr;
1197 : else
1198 : CPLError(CE_Warning, CPLE_AppDefined,
1199 : "PQescapeString(): %s\n"
1200 : " input: '%s'\n"
1201 : " got: '%s'\n",
1202 : PQerrorMessage( hPGConn ),
1203 0 : pszStrValue, pszDestStr );
1204 : #else
1205 : PQescapeString(pszDestStr, pszStrValue, nSrcLen);
1206 : osCommand += pszDestStr;
1207 : #endif
1208 112 : CPLFree(pszDestStr);
1209 :
1210 112 : osCommand += "'";
1211 :
1212 0 : return osCommand;
1213 : }
1214 :
1215 :
1216 : /************************************************************************/
1217 : /* OGRPGEscapeStringList( ) */
1218 : /************************************************************************/
1219 :
1220 12 : static CPLString OGRPGEscapeStringList(PGconn *hPGConn,
1221 : char** papszItems, int bForInsertOrUpdate)
1222 : {
1223 12 : int bFirstItem = TRUE;
1224 12 : CPLString osStr;
1225 12 : if (bForInsertOrUpdate)
1226 6 : osStr += "ARRAY[";
1227 : else
1228 6 : osStr += "{";
1229 48 : while(*papszItems)
1230 : {
1231 24 : if (!bFirstItem)
1232 : {
1233 12 : osStr += ',';
1234 : }
1235 :
1236 24 : char* pszStr = *papszItems;
1237 24 : if (*pszStr != '\0')
1238 : {
1239 24 : if (bForInsertOrUpdate)
1240 12 : osStr += OGRPGEscapeString(hPGConn, pszStr, -1, "");
1241 : else
1242 : {
1243 12 : osStr += '"';
1244 :
1245 44 : while(*pszStr)
1246 : {
1247 20 : if (*pszStr == '"' )
1248 0 : osStr += "\\";
1249 20 : osStr += *pszStr;
1250 20 : pszStr++;
1251 : }
1252 :
1253 12 : osStr += '"';
1254 : }
1255 : }
1256 : else
1257 0 : osStr += "NULL";
1258 :
1259 24 : bFirstItem = FALSE;
1260 :
1261 24 : papszItems++;
1262 : }
1263 12 : if (bForInsertOrUpdate)
1264 6 : osStr += "]";
1265 : else
1266 6 : osStr += "}";
1267 0 : return osStr;
1268 : }
1269 :
1270 : /************************************************************************/
1271 : /* CreateFeatureViaInsert() */
1272 : /************************************************************************/
1273 :
1274 2080 : OGRErr OGRPGTableLayer::CreateFeatureViaInsert( OGRFeature *poFeature )
1275 :
1276 : {
1277 2080 : PGconn *hPGConn = poDS->GetPGConn();
1278 : PGresult *hResult;
1279 2080 : CPLString osCommand;
1280 : int i;
1281 2080 : int bNeedComma = FALSE;
1282 : OGRErr eErr;
1283 :
1284 2080 : eErr = poDS->SoftStartTransaction();
1285 2080 : if( eErr != OGRERR_NONE )
1286 : {
1287 0 : return eErr;
1288 : }
1289 :
1290 : /* -------------------------------------------------------------------- */
1291 : /* Form the INSERT command. */
1292 : /* -------------------------------------------------------------------- */
1293 2080 : osCommand.Printf( "INSERT INTO %s (", pszSqlTableName );
1294 :
1295 2080 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1296 :
1297 2102 : if( bHasWkb && poGeom != NULL )
1298 : {
1299 22 : osCommand += "WKB_GEOMETRY ";
1300 22 : bNeedComma = TRUE;
1301 : }
1302 2058 : else if( (bHasPostGISGeometry || bHasPostGISGeography) && poGeom != NULL )
1303 : {
1304 52 : osCommand = osCommand + "\"" + pszGeomColumn + "\" ";
1305 52 : bNeedComma = TRUE;
1306 : }
1307 :
1308 2080 : if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL )
1309 : {
1310 0 : if( bNeedComma )
1311 0 : osCommand += ", ";
1312 :
1313 0 : osCommand = osCommand + "\"" + pszFIDColumn + "\" ";
1314 0 : bNeedComma = TRUE;
1315 : }
1316 :
1317 2080 : int nFieldCount = poFeatureDefn->GetFieldCount();
1318 4402 : for( i = 0; i < nFieldCount; i++ )
1319 : {
1320 2322 : if( !poFeature->IsFieldSet( i ) )
1321 86 : continue;
1322 :
1323 2236 : if( !bNeedComma )
1324 2006 : bNeedComma = TRUE;
1325 : else
1326 230 : osCommand += ", ";
1327 :
1328 : osCommand = osCommand
1329 2236 : + "\"" + poFeatureDefn->GetFieldDefn(i)->GetNameRef() + "\"";
1330 : }
1331 :
1332 2080 : osCommand += ") VALUES (";
1333 :
1334 : /* Set the geometry */
1335 2080 : bNeedComma = poGeom != NULL;
1336 2132 : if( (bHasPostGISGeometry || bHasPostGISGeography) && poGeom != NULL)
1337 : {
1338 52 : char *pszWKT = NULL;
1339 :
1340 52 : CheckGeomTypeCompatibility(poGeom);
1341 :
1342 52 : poGeom->closeRings();
1343 52 : poGeom->setCoordinateDimension( nCoordDimension );
1344 :
1345 52 : poGeom->exportToWkt( &pszWKT );
1346 :
1347 52 : if( pszWKT != NULL )
1348 : {
1349 52 : if( bHasPostGISGeography )
1350 : osCommand +=
1351 : CPLString().Printf(
1352 0 : "ST_GeographyFromText('SRID=%d;%s'::TEXT) ", nSRSId, pszWKT );
1353 52 : else if( poDS->sPostGISVersion.nMajor >= 1 )
1354 : osCommand +=
1355 : CPLString().Printf(
1356 52 : "GeomFromEWKT('SRID=%d;%s'::TEXT) ", nSRSId, pszWKT );
1357 : else
1358 : osCommand +=
1359 : CPLString().Printf(
1360 0 : "GeometryFromText('%s'::TEXT,%d) ", pszWKT, nSRSId );
1361 52 : OGRFree( pszWKT );
1362 : }
1363 : else
1364 0 : osCommand += "''";
1365 : }
1366 2050 : else if( bHasWkb && !bWkbAsOid && poGeom != NULL )
1367 : {
1368 22 : char *pszBytea = GeometryToBYTEA( poGeom );
1369 :
1370 22 : if( pszBytea != NULL )
1371 : {
1372 22 : osCommand = osCommand + "'" + pszBytea + "'";
1373 22 : CPLFree( pszBytea );
1374 : }
1375 : else
1376 0 : osCommand += "''";
1377 : }
1378 2006 : else if( bHasWkb && bWkbAsOid && poGeom != NULL )
1379 : {
1380 0 : Oid oid = GeometryToOID( poGeom );
1381 :
1382 0 : if( oid != 0 )
1383 : {
1384 0 : osCommand += CPLString().Printf( "'%d' ", oid );
1385 : }
1386 : else
1387 0 : osCommand += "''";
1388 : }
1389 :
1390 : /* Set the FID */
1391 2080 : if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL )
1392 : {
1393 0 : if( bNeedComma )
1394 0 : osCommand += ", ";
1395 0 : osCommand += CPLString().Printf( "%ld ", poFeature->GetFID() );
1396 0 : bNeedComma = TRUE;
1397 : }
1398 :
1399 :
1400 4402 : for( i = 0; i < nFieldCount; i++ )
1401 : {
1402 2322 : if( !poFeature->IsFieldSet( i ) )
1403 86 : continue;
1404 :
1405 2236 : if( bNeedComma )
1406 230 : osCommand += ", ";
1407 : else
1408 2006 : bNeedComma = TRUE;
1409 :
1410 2236 : AppendFieldValue(hPGConn, osCommand, poFeature, i);
1411 : }
1412 :
1413 2080 : osCommand += ")";
1414 :
1415 : /* -------------------------------------------------------------------- */
1416 : /* Execute the insert. */
1417 : /* -------------------------------------------------------------------- */
1418 2080 : hResult = PQexec(hPGConn, osCommand);
1419 2080 : if( PQresultStatus(hResult) != PGRES_COMMAND_OK )
1420 : {
1421 : CPLError( CE_Failure, CPLE_AppDefined,
1422 : "INSERT command for new feature failed.\n%s\nCommand: %s",
1423 0 : PQerrorMessage(hPGConn), osCommand.c_str() );
1424 :
1425 0 : OGRPGClearResult( hResult );
1426 :
1427 0 : poDS->SoftRollback();
1428 :
1429 0 : return OGRERR_FAILURE;
1430 : }
1431 :
1432 : #ifdef notdef
1433 : /* Should we use this oid to get back the FID and assign back to the
1434 : feature? I think we are supposed to. */
1435 : Oid nNewOID = PQoidValue( hResult );
1436 : printf( "nNewOID = %d\n", (int) nNewOID );
1437 : #endif
1438 :
1439 2080 : OGRPGClearResult( hResult );
1440 :
1441 2080 : return poDS->SoftCommit();
1442 : }
1443 :
1444 : /************************************************************************/
1445 : /* CreateFeatureViaCopy() */
1446 : /************************************************************************/
1447 :
1448 22 : OGRErr OGRPGTableLayer::CreateFeatureViaCopy( OGRFeature *poFeature )
1449 : {
1450 22 : PGconn *hPGConn = poDS->GetPGConn();
1451 22 : CPLString osCommand;
1452 :
1453 : /* First process geometry */
1454 22 : OGRGeometry *poGeometry = (OGRGeometry *) poFeature->GetGeometryRef();
1455 :
1456 22 : char *pszGeom = NULL;
1457 22 : if ( NULL != poGeometry && (bHasWkb || bHasPostGISGeometry || bHasPostGISGeography))
1458 : {
1459 22 : poGeometry->closeRings();
1460 22 : poGeometry->setCoordinateDimension( nCoordDimension );
1461 :
1462 22 : CheckGeomTypeCompatibility(poGeometry);
1463 :
1464 22 : if (bHasWkb)
1465 11 : pszGeom = GeometryToBYTEA( poGeometry );
1466 : else
1467 11 : pszGeom = GeometryToHex( poGeometry, nSRSId );
1468 : }
1469 :
1470 22 : if ( pszGeom )
1471 : {
1472 : osCommand += pszGeom,
1473 22 : CPLFree( pszGeom );
1474 : }
1475 : else
1476 : {
1477 0 : osCommand = "\\N";
1478 : }
1479 :
1480 : /* Next process the field id column */
1481 22 : if( bHasFid && poFeatureDefn->GetFieldIndex( pszFIDColumn ) != -1 )
1482 : {
1483 0 : if (osCommand.size() > 0)
1484 0 : osCommand += "\t";
1485 :
1486 : /* Set the FID */
1487 0 : if( poFeature->GetFID() != OGRNullFID )
1488 : {
1489 0 : osCommand += CPLString().Printf("%ld ", poFeature->GetFID());
1490 : }
1491 : else
1492 : {
1493 0 : osCommand += "\\N" ;
1494 : }
1495 : }
1496 :
1497 :
1498 : /* Now process the remaining fields */
1499 :
1500 22 : int nFieldCount = poFeatureDefn->GetFieldCount();
1501 148 : for( int i = 0; i < nFieldCount; i++ )
1502 : {
1503 126 : const char *pszStrValue = poFeature->GetFieldAsString(i);
1504 126 : char *pszNeedToFree = NULL;
1505 :
1506 126 : if (osCommand.size() > 0)
1507 126 : osCommand += "\t";
1508 :
1509 126 : if( !poFeature->IsFieldSet( i ) )
1510 : {
1511 20 : osCommand += "\\N" ;
1512 :
1513 20 : continue;
1514 : }
1515 :
1516 106 : int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
1517 :
1518 : // We need special formatting for integer list values.
1519 106 : if( nOGRFieldType == OFTIntegerList )
1520 : {
1521 2 : int nCount, nOff = 0, j;
1522 2 : const int *panItems = poFeature->GetFieldAsIntegerList(i,&nCount);
1523 :
1524 2 : pszNeedToFree = (char *) CPLMalloc(nCount * 13 + 10);
1525 2 : strcpy( pszNeedToFree, "{" );
1526 6 : for( j = 0; j < nCount; j++ )
1527 : {
1528 4 : if( j != 0 )
1529 2 : strcat( pszNeedToFree+nOff, "," );
1530 :
1531 4 : nOff += strlen(pszNeedToFree+nOff);
1532 4 : sprintf( pszNeedToFree+nOff, "%d", panItems[j] );
1533 : }
1534 2 : strcat( pszNeedToFree+nOff, "}" );
1535 2 : pszStrValue = pszNeedToFree;
1536 : }
1537 :
1538 : // We need special formatting for real list values.
1539 104 : else if( nOGRFieldType == OFTRealList )
1540 : {
1541 4 : int nCount, nOff = 0, j;
1542 4 : const double *padfItems =poFeature->GetFieldAsDoubleList(i,&nCount);
1543 :
1544 4 : pszNeedToFree = (char *) CPLMalloc(nCount * 40 + 10);
1545 4 : strcpy( pszNeedToFree, "{" );
1546 12 : for( j = 0; j < nCount; j++ )
1547 : {
1548 8 : if( j != 0 )
1549 4 : strcat( pszNeedToFree+nOff, "," );
1550 :
1551 8 : nOff += strlen(pszNeedToFree+nOff);
1552 8 : sprintf( pszNeedToFree+nOff, "%.16g", padfItems[j] );
1553 : }
1554 4 : strcat( pszNeedToFree+nOff, "}" );
1555 4 : pszStrValue = pszNeedToFree;
1556 : }
1557 :
1558 :
1559 : // We need special formatting for string list values.
1560 100 : else if( nOGRFieldType == OFTStringList )
1561 : {
1562 6 : CPLString osStr;
1563 6 : char **papszItems = poFeature->GetFieldAsStringList(i);
1564 :
1565 6 : pszStrValue = pszNeedToFree = CPLStrdup(OGRPGEscapeStringList(hPGConn, papszItems, FALSE));
1566 : }
1567 :
1568 : // Binary formatting
1569 94 : else if( nOGRFieldType == OFTBinary )
1570 : {
1571 2 : int nLen = 0;
1572 2 : GByte* pabyData = poFeature->GetFieldAsBinary( i, &nLen );
1573 2 : char* pszBytea = GByteArrayToBYTEA( pabyData, nLen);
1574 :
1575 2 : pszStrValue = pszNeedToFree = pszBytea;
1576 : }
1577 :
1578 146 : if( nOGRFieldType != OFTIntegerList &&
1579 : nOGRFieldType != OFTRealList &&
1580 : nOGRFieldType != OFTInteger &&
1581 : nOGRFieldType != OFTReal &&
1582 : nOGRFieldType != OFTBinary )
1583 : {
1584 : int iChar;
1585 :
1586 392 : for( iChar = 0; pszStrValue[iChar] != '\0'; iChar++ )
1587 : {
1588 352 : if( poFeatureDefn->GetFieldDefn(i)->GetWidth() > 0
1589 : && iChar == poFeatureDefn->GetFieldDefn(i)->GetWidth() )
1590 : {
1591 : CPLDebug( "PG",
1592 : "Truncated %s field value, it was too long.",
1593 0 : poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
1594 0 : break;
1595 : }
1596 :
1597 : /* Escape embedded \, \t, \n, \r since they will cause COPY
1598 : to misinterpret a line of text and thus abort */
1599 1408 : if( pszStrValue[iChar] == '\\' ||
1600 352 : pszStrValue[iChar] == '\t' ||
1601 352 : pszStrValue[iChar] == '\r' ||
1602 352 : pszStrValue[iChar] == '\n' )
1603 : {
1604 0 : osCommand += '\\';
1605 : }
1606 :
1607 352 : osCommand += pszStrValue[iChar];
1608 : }
1609 : }
1610 : else
1611 : {
1612 66 : osCommand += pszStrValue;
1613 : }
1614 :
1615 106 : if( pszNeedToFree )
1616 14 : CPLFree( pszNeedToFree );
1617 : }
1618 :
1619 : /* Add end of line marker */
1620 22 : osCommand += "\n";
1621 :
1622 :
1623 : /* ------------------------------------------------------------ */
1624 : /* Execute the copy. */
1625 : /* ------------------------------------------------------------ */
1626 :
1627 22 : OGRErr result = OGRERR_NONE;
1628 :
1629 : /* This is for postgresql 7.4 and higher */
1630 : #if !defined(PG_PRE74)
1631 22 : int copyResult = PQputCopyData(hPGConn, osCommand.c_str(), strlen(osCommand.c_str()));
1632 : //CPLDebug("PG", "PQputCopyData(%s)", osCommand.c_str());
1633 :
1634 22 : switch (copyResult)
1635 : {
1636 : case 0:
1637 0 : CPLError( CE_Failure, CPLE_AppDefined, "Writing COPY data blocked.");
1638 0 : result = OGRERR_FAILURE;
1639 0 : break;
1640 : case -1:
1641 0 : CPLError( CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn) );
1642 0 : result = OGRERR_FAILURE;
1643 : break;
1644 : }
1645 : #else /* else defined(PG_PRE74) */
1646 : int copyResult = PQputline(hPGConn, osCommand.c_str());
1647 :
1648 : if (copyResult == EOF)
1649 : {
1650 : CPLError( CE_Failure, CPLE_AppDefined, "Writing COPY data blocked.");
1651 : result = OGRERR_FAILURE;
1652 : }
1653 : #endif /* end of defined(PG_PRE74) */
1654 :
1655 22 : return result;
1656 : }
1657 :
1658 :
1659 : /************************************************************************/
1660 : /* TestCapability() */
1661 : /************************************************************************/
1662 :
1663 66 : int OGRPGTableLayer::TestCapability( const char * pszCap )
1664 :
1665 : {
1666 66 : if ( bUpdateAccess )
1667 : {
1668 64 : if( EQUAL(pszCap,OLCSequentialWrite) || EQUAL(pszCap,OLCCreateField) )
1669 4 : return TRUE;
1670 :
1671 60 : else if( EQUAL(pszCap,OLCRandomWrite) )
1672 2 : return bHasFid;
1673 : }
1674 :
1675 60 : if( EQUAL(pszCap,OLCRandomRead) )
1676 2 : return bHasFid;
1677 :
1678 58 : else if( EQUAL(pszCap,OLCFastFeatureCount) ||
1679 : EQUAL(pszCap,OLCFastSetNextByIndex) )
1680 46 : return m_poFilterGeom == NULL || bHasPostGISGeometry || bHasPostGISGeography;
1681 :
1682 12 : else if( EQUAL(pszCap,OLCFastSpatialFilter) )
1683 2 : return bHasPostGISGeometry || bHasPostGISGeography;
1684 :
1685 10 : else if( EQUAL(pszCap,OLCTransactions) )
1686 2 : return TRUE;
1687 :
1688 8 : else if( EQUAL(pszCap,OLCFastGetExtent) )
1689 6 : return bHasPostGISGeometry;
1690 :
1691 2 : else if( EQUAL(pszCap,OLCStringsAsUTF8) )
1692 2 : return TRUE;
1693 :
1694 : else
1695 0 : return FALSE;
1696 : }
1697 :
1698 : /************************************************************************/
1699 : /* CreateField() */
1700 : /************************************************************************/
1701 :
1702 137 : OGRErr OGRPGTableLayer::CreateField( OGRFieldDefn *poFieldIn, int bApproxOK )
1703 :
1704 : {
1705 137 : PGconn *hPGConn = poDS->GetPGConn();
1706 137 : PGresult *hResult = NULL;
1707 137 : CPLString osCommand;
1708 : char szFieldType[256];
1709 137 : OGRFieldDefn oField( poFieldIn );
1710 :
1711 : /* -------------------------------------------------------------------- */
1712 : /* Do we want to "launder" the column names into Postgres */
1713 : /* friendly format? */
1714 : /* -------------------------------------------------------------------- */
1715 137 : if( bLaunderColumnNames )
1716 : {
1717 137 : char *pszSafeName = poDS->LaunderName( oField.GetNameRef() );
1718 :
1719 137 : oField.SetName( pszSafeName );
1720 137 : CPLFree( pszSafeName );
1721 :
1722 137 : if( EQUAL(oField.GetNameRef(),"oid") )
1723 : {
1724 : CPLError( CE_Warning, CPLE_AppDefined,
1725 0 : "Renaming field 'oid' to 'oid_' to avoid conflict with internal oid field." );
1726 0 : oField.SetName( "oid_" );
1727 : }
1728 : }
1729 :
1730 : /* -------------------------------------------------------------------- */
1731 : /* Work out the PostgreSQL type. */
1732 : /* -------------------------------------------------------------------- */
1733 137 : if( oField.GetType() == OFTInteger )
1734 : {
1735 28 : if( oField.GetWidth() > 0 && bPreservePrecision )
1736 12 : sprintf( szFieldType, "NUMERIC(%d,0)", oField.GetWidth() );
1737 : else
1738 16 : strcpy( szFieldType, "INTEGER" );
1739 : }
1740 109 : else if( oField.GetType() == OFTReal )
1741 : {
1742 32 : if( oField.GetWidth() > 0 && oField.GetPrecision() > 0
1743 : && bPreservePrecision )
1744 : sprintf( szFieldType, "NUMERIC(%d,%d)",
1745 7 : oField.GetWidth(), oField.GetPrecision() );
1746 : else
1747 25 : strcpy( szFieldType, "FLOAT8" );
1748 : }
1749 77 : else if( oField.GetType() == OFTString )
1750 : {
1751 27 : if( oField.GetWidth() == 0 || !bPreservePrecision )
1752 14 : strcpy( szFieldType, "VARCHAR" );
1753 : else
1754 13 : sprintf( szFieldType, "CHAR(%d)", oField.GetWidth() );
1755 : }
1756 50 : else if( oField.GetType() == OFTIntegerList )
1757 : {
1758 4 : strcpy( szFieldType, "INTEGER[]" );
1759 : }
1760 46 : else if( oField.GetType() == OFTRealList )
1761 : {
1762 8 : strcpy( szFieldType, "FLOAT8[]" );
1763 : }
1764 38 : else if( oField.GetType() == OFTStringList )
1765 : {
1766 12 : strcpy( szFieldType, "varchar[]" );
1767 : }
1768 26 : else if( oField.GetType() == OFTDate )
1769 : {
1770 6 : strcpy( szFieldType, "date" );
1771 : }
1772 20 : else if( oField.GetType() == OFTTime )
1773 : {
1774 6 : strcpy( szFieldType, "time" );
1775 : }
1776 14 : else if( oField.GetType() == OFTDateTime )
1777 : {
1778 10 : strcpy( szFieldType, "timestamp with time zone" );
1779 : }
1780 4 : else if( oField.GetType() == OFTBinary )
1781 : {
1782 4 : strcpy( szFieldType, "bytea" );
1783 : }
1784 0 : else if( bApproxOK )
1785 : {
1786 : CPLError( CE_Warning, CPLE_NotSupported,
1787 : "Can't create field %s with type %s on PostgreSQL layers. Creating as VARCHAR.",
1788 : oField.GetNameRef(),
1789 0 : OGRFieldDefn::GetFieldTypeName(oField.GetType()) );
1790 0 : strcpy( szFieldType, "VARCHAR" );
1791 : }
1792 : else
1793 : {
1794 : CPLError( CE_Failure, CPLE_NotSupported,
1795 : "Can't create field %s with type %s on PostgreSQL layers.",
1796 : oField.GetNameRef(),
1797 0 : OGRFieldDefn::GetFieldTypeName(oField.GetType()) );
1798 :
1799 0 : return OGRERR_FAILURE;
1800 : }
1801 :
1802 : /* -------------------------------------------------------------------- */
1803 : /* Create the new field. */
1804 : /* -------------------------------------------------------------------- */
1805 137 : poDS->FlushSoftTransaction();
1806 137 : hResult = PQexec(hPGConn, "BEGIN");
1807 137 : OGRPGClearResult( hResult );
1808 :
1809 : osCommand.Printf( "ALTER TABLE %s ADD COLUMN \"%s\" %s",
1810 137 : pszSqlTableName, oField.GetNameRef(), szFieldType );
1811 137 : hResult = PQexec(hPGConn, osCommand);
1812 137 : if( PQresultStatus(hResult) != PGRES_COMMAND_OK )
1813 : {
1814 : CPLError( CE_Failure, CPLE_AppDefined,
1815 : "%s\n%s",
1816 : osCommand.c_str(),
1817 0 : PQerrorMessage(hPGConn) );
1818 :
1819 0 : OGRPGClearResult( hResult );
1820 :
1821 0 : hResult = PQexec( hPGConn, "ROLLBACK" );
1822 0 : OGRPGClearResult( hResult );
1823 :
1824 0 : return OGRERR_FAILURE;
1825 : }
1826 :
1827 137 : OGRPGClearResult( hResult );
1828 :
1829 137 : hResult = PQexec(hPGConn, "COMMIT");
1830 137 : OGRPGClearResult( hResult );
1831 :
1832 137 : poFeatureDefn->AddFieldDefn( &oField );
1833 :
1834 137 : return OGRERR_NONE;
1835 : }
1836 :
1837 : /************************************************************************/
1838 : /* GetFeature() */
1839 : /************************************************************************/
1840 :
1841 15 : OGRFeature *OGRPGTableLayer::GetFeature( long nFeatureId )
1842 :
1843 : {
1844 15 : if( pszFIDColumn == NULL )
1845 0 : return OGRLayer::GetFeature( nFeatureId );
1846 :
1847 : /* -------------------------------------------------------------------- */
1848 : /* Discard any existing resultset. */
1849 : /* -------------------------------------------------------------------- */
1850 15 : ResetReading();
1851 :
1852 : /* -------------------------------------------------------------------- */
1853 : /* Issue query for a single record. */
1854 : /* -------------------------------------------------------------------- */
1855 15 : OGRFeature *poFeature = NULL;
1856 15 : PGresult *hResult = NULL;
1857 15 : PGconn *hPGConn = poDS->GetPGConn();
1858 15 : CPLString osFieldList = BuildFields();
1859 15 : CPLString osCommand;
1860 :
1861 15 : poDS->FlushSoftTransaction();
1862 15 : poDS->SoftStartTransaction();
1863 :
1864 : osCommand.Printf(
1865 : "DECLARE getfeaturecursor %s for "
1866 : "SELECT %s FROM %s WHERE \"%s\" = %ld",
1867 : ( poDS->bUseBinaryCursor ) ? "BINARY CURSOR" : "CURSOR",
1868 : osFieldList.c_str(), pszSqlTableName, pszFIDColumn,
1869 15 : nFeatureId );
1870 :
1871 15 : hResult = PQexec(hPGConn, osCommand.c_str() );
1872 :
1873 15 : if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK )
1874 : {
1875 15 : OGRPGClearResult( hResult );
1876 :
1877 15 : hResult = PQexec(hPGConn, "FETCH ALL in getfeaturecursor" );
1878 :
1879 15 : if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK )
1880 : {
1881 15 : int nRows = PQntuples(hResult);
1882 15 : if (nRows > 0)
1883 : {
1884 13 : hCursorResult = hResult;
1885 13 : CreateMapFromFieldNameToIndex();
1886 13 : poFeature = RecordToFeature( 0 );
1887 13 : hCursorResult = NULL;
1888 :
1889 13 : if (nRows > 1)
1890 : {
1891 : CPLError(CE_Warning, CPLE_AppDefined,
1892 : "%d rows in response to the WHERE %s = %ld clause !",
1893 0 : nRows, pszFIDColumn, nFeatureId );
1894 : }
1895 : }
1896 : else
1897 : {
1898 : CPLError( CE_Failure, CPLE_AppDefined,
1899 2 : "Attempt to read feature with unknown feature id (%ld).", nFeatureId );
1900 : }
1901 : }
1902 : }
1903 :
1904 : /* -------------------------------------------------------------------- */
1905 : /* Cleanup */
1906 : /* -------------------------------------------------------------------- */
1907 15 : OGRPGClearResult( hResult );
1908 :
1909 15 : hResult = PQexec(hPGConn, "CLOSE getfeaturecursor");
1910 15 : OGRPGClearResult( hResult );
1911 :
1912 15 : poDS->FlushSoftTransaction();
1913 :
1914 15 : return poFeature;
1915 : }
1916 :
1917 : /************************************************************************/
1918 : /* GetFeatureCount() */
1919 : /************************************************************************/
1920 :
1921 38 : int OGRPGTableLayer::GetFeatureCount( int bForce )
1922 :
1923 : {
1924 38 : if( TestCapability(OLCFastFeatureCount) == FALSE )
1925 3 : return OGRPGLayer::GetFeatureCount( bForce );
1926 :
1927 : /* -------------------------------------------------------------------- */
1928 : /* In theory it might be wise to cache this result, but it */
1929 : /* won't be trivial to work out the lifetime of the value. */
1930 : /* After all someone else could be adding records from another */
1931 : /* application when working against a database. */
1932 : /* -------------------------------------------------------------------- */
1933 35 : PGconn *hPGConn = poDS->GetPGConn();
1934 35 : PGresult *hResult = NULL;
1935 35 : CPLString osCommand;
1936 35 : int nCount = 0;
1937 :
1938 : osCommand.Printf(
1939 : "SELECT count(*) FROM %s %s",
1940 35 : pszSqlTableName, osWHERE.c_str() );
1941 :
1942 35 : hResult = PQexec(hPGConn, osCommand);
1943 35 : if( hResult != NULL && PQresultStatus(hResult) == PGRES_TUPLES_OK )
1944 35 : nCount = atoi(PQgetvalue(hResult,0,0));
1945 : else
1946 0 : CPLDebug( "PG", "%s; failed.", osCommand.c_str() );
1947 35 : OGRPGClearResult( hResult );
1948 :
1949 35 : return nCount;
1950 : }
1951 :
1952 : /************************************************************************/
1953 : /* GetSpatialRef() */
1954 : /* */
1955 : /* We override this to try and fetch the table SRID from the */
1956 : /* geometry_columns table if the srsid is -2 (meaning we */
1957 : /* haven't yet even looked for it). */
1958 : /************************************************************************/
1959 :
1960 136 : OGRSpatialReference *OGRPGTableLayer::GetSpatialRef()
1961 :
1962 : {
1963 136 : if( nSRSId == -2 )
1964 : {
1965 136 : PGconn *hPGConn = poDS->GetPGConn();
1966 136 : PGresult *hResult = NULL;
1967 136 : CPLString osCommand;
1968 :
1969 136 : nSRSId = -1;
1970 :
1971 136 : poDS->SoftStartTransaction();
1972 :
1973 136 : if (bHasPostGISGeography)
1974 : {
1975 : osCommand.Printf(
1976 : "SELECT srid FROM geography_columns "
1977 : "WHERE f_table_name = '%s'",
1978 0 : (pszSqlGeomParentTableName) ? pszSqlGeomParentTableName : pszTableName);
1979 :
1980 0 : if (pszGeomColumn)
1981 : {
1982 0 : osCommand += CPLString().Printf(" AND f_geography_column = '%s'", pszGeomColumn);
1983 : }
1984 : }
1985 : else
1986 : {
1987 : osCommand.Printf(
1988 : "SELECT srid FROM geometry_columns "
1989 : "WHERE f_table_name = '%s'",
1990 136 : (pszSqlGeomParentTableName) ? pszSqlGeomParentTableName : pszTableName);
1991 :
1992 136 : if (pszGeomColumn)
1993 : {
1994 135 : osCommand += CPLString().Printf(" AND f_geometry_column = '%s'", pszGeomColumn);
1995 : }
1996 : }
1997 :
1998 136 : if (pszSchemaName)
1999 : {
2000 136 : osCommand += CPLString().Printf(" AND f_table_schema = '%s'", pszSchemaName);
2001 : }
2002 :
2003 136 : hResult = PQexec(hPGConn, osCommand.c_str() );
2004 :
2005 136 : if( hResult
2006 : && PQresultStatus(hResult) == PGRES_TUPLES_OK
2007 : && PQntuples(hResult) == 1 )
2008 : {
2009 1 : nSRSId = atoi(PQgetvalue(hResult,0,0));
2010 : }
2011 :
2012 136 : OGRPGClearResult( hResult );
2013 :
2014 136 : poDS->SoftCommit();
2015 : }
2016 :
2017 136 : return OGRPGLayer::GetSpatialRef();
2018 : }
2019 :
2020 : /************************************************************************/
2021 : /* GetExtent() */
2022 : /* */
2023 : /* For PostGIS use internal Extend(geometry) function */
2024 : /* in other cases we use standard OGRLayer::GetExtent() */
2025 : /************************************************************************/
2026 :
2027 2 : OGRErr OGRPGTableLayer::GetExtent( OGREnvelope *psExtent, int bForce )
2028 : {
2029 2 : CPLString osCommand;
2030 :
2031 2 : if ( TestCapability(OLCFastGetExtent) )
2032 : {
2033 : osCommand.Printf( "SELECT Extent(\"%s\") FROM %s",
2034 1 : pszGeomColumn, pszSqlTableName );
2035 : }
2036 1 : else if ( bHasPostGISGeography )
2037 : {
2038 : /* Probably not very efficient, but more efficient than client-side implementation */
2039 : osCommand.Printf( "SELECT Extent(ST_GeomFromWKB(ST_AsBinary(\"%s\"))) FROM %s",
2040 0 : pszGeomColumn, pszSqlTableName );
2041 : }
2042 :
2043 2 : return RunGetExtentRequest(psExtent, bForce, osCommand);
2044 : }
2045 :
2046 : /************************************************************************/
2047 : /* StartCopy() */
2048 : /************************************************************************/
2049 :
2050 4 : OGRErr OGRPGTableLayer::StartCopy()
2051 :
2052 : {
2053 4 : OGRErr result = OGRERR_NONE;
2054 :
2055 : /* Tell the datasource we are now planning to copy data */
2056 4 : poDS->StartCopy( this );
2057 :
2058 4 : CPLString osFields = BuildCopyFields();
2059 :
2060 4 : int size = strlen(osFields) + strlen(pszSqlTableName) + 100;
2061 4 : char *pszCommand = (char *) CPLMalloc(size);
2062 :
2063 : sprintf( pszCommand,
2064 : "COPY %s (%s) FROM STDIN;",
2065 4 : pszSqlTableName, osFields.c_str() );
2066 :
2067 4 : PGconn *hPGConn = poDS->GetPGConn();
2068 4 : PGresult *hResult = PQexec(hPGConn, pszCommand);
2069 :
2070 4 : if ( !hResult || (PQresultStatus(hResult) != PGRES_COPY_IN))
2071 : {
2072 : CPLError( CE_Failure, CPLE_AppDefined,
2073 0 : "%s", PQerrorMessage(hPGConn) );
2074 0 : result = OGRERR_FAILURE;
2075 : }
2076 : else
2077 4 : bCopyActive = TRUE;
2078 :
2079 4 : OGRPGClearResult( hResult );
2080 4 : CPLFree( pszCommand );
2081 :
2082 4 : return OGRERR_NONE;
2083 : }
2084 :
2085 : /************************************************************************/
2086 : /* EndCopy() */
2087 : /************************************************************************/
2088 :
2089 416 : OGRErr OGRPGTableLayer::EndCopy()
2090 :
2091 : {
2092 416 : if( !bCopyActive )
2093 412 : return OGRERR_NONE;
2094 :
2095 : /* This method is called from the datasource when
2096 : a COPY operation is ended */
2097 4 : OGRErr result = OGRERR_NONE;
2098 :
2099 4 : PGconn *hPGConn = poDS->GetPGConn();
2100 4 : CPLDebug( "PG", "PQputCopyEnd()" );
2101 :
2102 4 : bCopyActive = FALSE;
2103 :
2104 : /* This is for postgresql 7.4 and higher */
2105 : #if !defined(PG_PRE74)
2106 4 : int copyResult = PQputCopyEnd(hPGConn, NULL);
2107 :
2108 4 : switch (copyResult)
2109 : {
2110 : case 0:
2111 0 : CPLError( CE_Failure, CPLE_AppDefined, "Writing COPY data blocked.");
2112 0 : result = OGRERR_FAILURE;
2113 0 : break;
2114 : case -1:
2115 0 : CPLError( CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn) );
2116 0 : result = OGRERR_FAILURE;
2117 : break;
2118 : }
2119 :
2120 : #else /* defined(PG_PRE74) */
2121 : PQputline(hPGConn, "\\.\n");
2122 : int copyResult = PQendcopy(hPGConn);
2123 :
2124 : if (copyResult != 0)
2125 : {
2126 : CPLError( CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn) );
2127 : result = OGRERR_FAILURE;
2128 : }
2129 : #endif /* defined(PG_PRE74) */
2130 :
2131 : /* Now check the results of the copy */
2132 4 : PGresult * hResult = PQgetResult( hPGConn );
2133 :
2134 4 : if( hResult && PQresultStatus(hResult) != PGRES_COMMAND_OK )
2135 : {
2136 : CPLError( CE_Failure, CPLE_AppDefined,
2137 : "COPY statement failed.\n%s",
2138 0 : PQerrorMessage(hPGConn) );
2139 :
2140 0 : result = OGRERR_FAILURE;
2141 : }
2142 :
2143 4 : OGRPGClearResult( hResult );
2144 :
2145 4 : bUseCopy = USE_COPY_UNSET;
2146 :
2147 4 : return result;
2148 : }
2149 :
2150 : /************************************************************************/
2151 : /* BuildCopyFields() */
2152 : /************************************************************************/
2153 :
2154 4 : CPLString OGRPGTableLayer::BuildCopyFields()
2155 : {
2156 4 : int i = 0;
2157 4 : CPLString osFieldList;
2158 :
2159 4 : if( bHasFid && poFeatureDefn->GetFieldIndex( pszFIDColumn ) != -1 )
2160 : {
2161 0 : osFieldList += "\"";
2162 0 : osFieldList += pszFIDColumn;
2163 0 : osFieldList += "\"";
2164 : }
2165 :
2166 4 : if( pszGeomColumn )
2167 : {
2168 4 : if( strlen(osFieldList) > 0 )
2169 0 : osFieldList += ", ";
2170 :
2171 4 : osFieldList += "\"";
2172 4 : osFieldList += pszGeomColumn;
2173 4 : osFieldList += "\"";
2174 : }
2175 :
2176 58 : for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
2177 : {
2178 54 : const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
2179 :
2180 54 : if( strlen(osFieldList) > 0 )
2181 54 : osFieldList += ", ";
2182 :
2183 54 : osFieldList += "\"";
2184 54 : osFieldList += pszName;
2185 54 : osFieldList += "\"" ;
2186 : }
2187 :
2188 0 : return osFieldList;
2189 : }
2190 :
2191 : /************************************************************************/
2192 : /* CheckGeomTypeCompatibility() */
2193 : /************************************************************************/
2194 :
2195 74 : void OGRPGTableLayer::CheckGeomTypeCompatibility(OGRGeometry* poGeom)
2196 : {
2197 74 : if (bHasWarnedIncompatibleGeom)
2198 0 : return;
2199 :
2200 74 : OGRwkbGeometryType eFlatLayerGeomType = wkbFlatten(poFeatureDefn->GetGeomType());
2201 74 : OGRwkbGeometryType eFlatGeomType = wkbFlatten(poGeom->getGeometryType());
2202 74 : if (eFlatLayerGeomType == wkbUnknown)
2203 44 : return;
2204 :
2205 30 : if (eFlatLayerGeomType == wkbGeometryCollection)
2206 : bHasWarnedIncompatibleGeom = eFlatGeomType != wkbMultiPoint &&
2207 : eFlatGeomType != wkbMultiLineString &&
2208 : eFlatGeomType != wkbMultiPolygon &&
2209 0 : eFlatGeomType != wkbGeometryCollection;
2210 : else
2211 30 : bHasWarnedIncompatibleGeom = (eFlatGeomType != eFlatLayerGeomType);
2212 :
2213 30 : if (bHasWarnedIncompatibleGeom)
2214 : {
2215 : CPLError(CE_Warning, CPLE_AppDefined,
2216 : "Geometry to be inserted is of type %s, whereas the layer geometry type is %s.\n"
2217 : "Insertion is likely to fail",
2218 0 : OGRGeometryTypeToName(poGeom->getGeometryType()),
2219 0 : OGRGeometryTypeToName(poFeatureDefn->GetGeomType()));
2220 : }
2221 : }
|