1 : /******************************************************************************
2 : * $Id: ogrpgresultlayer.cpp 23565 2011-12-13 20:33:20Z rouault $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Implements OGRPGResultLayer class, access the resultset from
6 : * a particular select query done via ExecuteSQL().
7 : * Author: Frank Warmerdam, warmerdam@pobox.com
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 2002, Frank Warmerdam
11 : *
12 : * Permission is hereby granted, free of charge, to any person obtaining a
13 : * copy of this software and associated documentation files (the "Software"),
14 : * to deal in the Software without restriction, including without limitation
15 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 : * and/or sell copies of the Software, and to permit persons to whom the
17 : * Software is furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be included
20 : * in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 : * DEALINGS IN THE SOFTWARE.
29 : ****************************************************************************/
30 :
31 : #include "cpl_conv.h"
32 : #include "ogr_pg.h"
33 :
34 : CPL_CVSID("$Id: ogrpgresultlayer.cpp 23565 2011-12-13 20:33:20Z rouault $");
35 :
36 : #define PQexec this_is_an_error
37 :
38 : /************************************************************************/
39 : /* OGRPGResultLayer() */
40 : /************************************************************************/
41 :
42 34 : OGRPGResultLayer::OGRPGResultLayer( OGRPGDataSource *poDSIn,
43 : const char * pszRawQueryIn,
44 34 : PGresult *hInitialResultIn )
45 : {
46 34 : poDS = poDSIn;
47 :
48 34 : iNextShapeId = 0;
49 :
50 34 : pszRawStatement = CPLStrdup(pszRawQueryIn);
51 :
52 34 : osWHERE = "";
53 :
54 34 : BuildFullQueryStatement();
55 :
56 34 : poFeatureDefn = ReadResultDefinition(hInitialResultIn);
57 :
58 : /* We have to get the SRID of the geometry column, so to be able */
59 : /* to do spatial filtering */
60 34 : if (bHasPostGISGeometry)
61 : {
62 10 : CPLString osGetSRID;
63 10 : osGetSRID += "SELECT getsrid(";
64 10 : osGetSRID += OGRPGEscapeColumnName(pszGeomColumn);
65 10 : osGetSRID += ") FROM (";
66 10 : osGetSRID += pszRawStatement;
67 10 : osGetSRID += ") AS ogrpggetsrid LIMIT 1";
68 :
69 10 : PGresult* hSRSIdResult = OGRPG_PQexec(poDS->GetPGConn(), osGetSRID );
70 :
71 10 : if( hSRSIdResult && PQresultStatus(hSRSIdResult) == PGRES_TUPLES_OK)
72 : {
73 10 : if ( PQntuples(hSRSIdResult) > 0 )
74 10 : nSRSId = atoi(PQgetvalue(hSRSIdResult, 0, 0));
75 : }
76 : else
77 : {
78 : CPLError( CE_Failure, CPLE_AppDefined,
79 0 : "%s", PQerrorMessage(poDS->GetPGConn()) );
80 : }
81 :
82 10 : OGRPGClearResult(hSRSIdResult);
83 : }
84 24 : else if (bHasPostGISGeography)
85 : {
86 : // FIXME? But for the moment, PostGIS 1.5 only handles SRID:4326.
87 1 : nSRSId = 4326;
88 : }
89 34 : }
90 :
91 : /************************************************************************/
92 : /* ~OGRPGResultLayer() */
93 : /************************************************************************/
94 :
95 34 : OGRPGResultLayer::~OGRPGResultLayer()
96 :
97 : {
98 34 : CPLFree( pszRawStatement );
99 34 : }
100 :
101 :
102 : /************************************************************************/
103 : /* BuildFullQueryStatement() */
104 : /************************************************************************/
105 :
106 39 : void OGRPGResultLayer::BuildFullQueryStatement()
107 :
108 : {
109 39 : if( pszQueryStatement != NULL )
110 : {
111 5 : CPLFree( pszQueryStatement );
112 5 : pszQueryStatement = NULL;
113 : }
114 :
115 39 : pszQueryStatement = (char*) CPLMalloc(strlen(pszRawStatement) + strlen(osWHERE) + 40);
116 :
117 39 : if (strlen(osWHERE) == 0)
118 35 : strcpy(pszQueryStatement, pszRawStatement);
119 : else
120 : sprintf(pszQueryStatement, "SELECT * FROM (%s) AS ogrpgsubquery %s",
121 4 : pszRawStatement, osWHERE.c_str());
122 39 : }
123 :
124 : /************************************************************************/
125 : /* ResetReading() */
126 : /************************************************************************/
127 :
128 64 : void OGRPGResultLayer::ResetReading()
129 :
130 : {
131 64 : OGRPGLayer::ResetReading();
132 64 : }
133 :
134 : /************************************************************************/
135 : /* GetFeatureCount() */
136 : /************************************************************************/
137 :
138 24 : int OGRPGResultLayer::GetFeatureCount( int bForce )
139 :
140 : {
141 24 : if( TestCapability(OLCFastFeatureCount) == FALSE )
142 8 : return OGRPGLayer::GetFeatureCount( bForce );
143 :
144 16 : PGconn *hPGConn = poDS->GetPGConn();
145 16 : PGresult *hResult = NULL;
146 16 : CPLString osCommand;
147 16 : int nCount = 0;
148 :
149 : osCommand.Printf(
150 : "SELECT count(*) FROM (%s) AS ogrpgcount",
151 16 : pszQueryStatement );
152 :
153 16 : hResult = OGRPG_PQexec(hPGConn, osCommand);
154 16 : if( hResult != NULL && PQresultStatus(hResult) == PGRES_TUPLES_OK )
155 16 : nCount = atoi(PQgetvalue(hResult,0,0));
156 : else
157 0 : CPLDebug( "PG", "%s; failed.", osCommand.c_str() );
158 16 : OGRPGClearResult( hResult );
159 :
160 16 : return nCount;
161 : }
162 :
163 :
164 : /************************************************************************/
165 : /* TestCapability() */
166 : /************************************************************************/
167 :
168 48 : int OGRPGResultLayer::TestCapability( const char * pszCap )
169 :
170 : {
171 48 : if( EQUAL(pszCap,OLCFastFeatureCount) ||
172 : EQUAL(pszCap,OLCFastSetNextByIndex) )
173 : return (m_poFilterGeom == NULL ||
174 32 : ((bHasPostGISGeometry || bHasPostGISGeography) && nSRSId != UNDETERMINED_SRID)) && m_poAttrQuery == NULL;
175 :
176 16 : else if( EQUAL(pszCap,OLCFastSpatialFilter) )
177 0 : return ((bHasPostGISGeometry || bHasPostGISGeography) && nSRSId != UNDETERMINED_SRID) && m_poAttrQuery == NULL;
178 :
179 16 : else if( EQUAL(pszCap,OLCFastGetExtent) )
180 10 : return (bHasPostGISGeometry && nSRSId != UNDETERMINED_SRID) && m_poAttrQuery == NULL;
181 :
182 6 : else if( EQUAL(pszCap,OLCStringsAsUTF8) )
183 2 : return TRUE;
184 :
185 : else
186 4 : return FALSE;
187 : }
188 :
189 :
190 : /************************************************************************/
191 : /* GetNextFeature() */
192 : /************************************************************************/
193 :
194 2529 : OGRFeature *OGRPGResultLayer::GetNextFeature()
195 :
196 : {
197 :
198 98 : for( ; TRUE; )
199 : {
200 : OGRFeature *poFeature;
201 :
202 2529 : poFeature = GetNextRawFeature();
203 2529 : if( poFeature == NULL )
204 31 : return NULL;
205 :
206 2498 : if( (m_poFilterGeom == NULL
207 : || ((bHasPostGISGeometry || bHasPostGISGeography) && nSRSId != UNDETERMINED_SRID)
208 : || FilterGeometry( poFeature->GetGeometryRef() ) )
209 : && (m_poAttrQuery == NULL
210 : || m_poAttrQuery->Evaluate( poFeature )) )
211 2400 : return poFeature;
212 :
213 98 : delete poFeature;
214 : }
215 : }
216 :
217 : /************************************************************************/
218 : /* SetSpatialFilter() */
219 : /************************************************************************/
220 :
221 16 : void OGRPGResultLayer::SetSpatialFilter( OGRGeometry * poGeomIn )
222 :
223 : {
224 16 : if( InstallFilter( poGeomIn ) )
225 : {
226 10 : if ((bHasPostGISGeometry || bHasPostGISGeography) && nSRSId != UNDETERMINED_SRID)
227 : {
228 5 : if( m_poFilterGeom != NULL)
229 : {
230 : char szBox3D_1[128];
231 : char szBox3D_2[128];
232 : char* pszComma;
233 4 : OGREnvelope sEnvelope;
234 :
235 4 : m_poFilterGeom->getEnvelope( &sEnvelope );
236 4 : snprintf(szBox3D_1, sizeof(szBox3D_1), "%.12f %.12f", sEnvelope.MinX, sEnvelope.MinY);
237 8 : while((pszComma = strchr(szBox3D_1, ',')) != NULL)
238 0 : *pszComma = '.';
239 4 : snprintf(szBox3D_2, sizeof(szBox3D_2), "%.12f %.12f", sEnvelope.MaxX, sEnvelope.MaxY);
240 8 : while((pszComma = strchr(szBox3D_2, ',')) != NULL)
241 0 : *pszComma = '.';
242 : osWHERE.Printf("WHERE %s && SetSRID('BOX3D(%s, %s)'::box3d,%d) ",
243 4 : OGRPGEscapeColumnName(pszGeomColumn).c_str(), szBox3D_1, szBox3D_2, nSRSId );
244 : }
245 : else
246 : {
247 1 : osWHERE = "";
248 : }
249 :
250 5 : BuildFullQueryStatement();
251 : }
252 :
253 10 : ResetReading();
254 : }
255 :
256 16 : }
257 :
258 : /************************************************************************/
259 : /* GetExtent() */
260 : /* */
261 : /* For PostGIS use internal Extend(geometry) function */
262 : /* in other cases we use standard OGRLayer::GetExtent() */
263 : /************************************************************************/
264 :
265 5 : OGRErr OGRPGResultLayer::GetExtent( OGREnvelope *psExtent, int bForce )
266 : {
267 5 : CPLString osCommand;
268 :
269 : const char* pszExtentFct;
270 5 : if (poDS->sPostGISVersion.nMajor >= 2)
271 0 : pszExtentFct = "ST_Extent";
272 : else
273 5 : pszExtentFct = "Extent";
274 :
275 5 : if ( TestCapability(OLCFastGetExtent) )
276 : {
277 : /* Do not take the spatial filter into account */
278 : osCommand.Printf( "SELECT %s(%s) FROM (%s) AS ogrpgextent",
279 : pszExtentFct, OGRPGEscapeColumnName(pszGeomColumn).c_str(),
280 2 : pszRawStatement );
281 : }
282 3 : else if ( bHasPostGISGeography )
283 : {
284 : /* Probably not very efficient, but more efficient than client-side implementation */
285 : osCommand.Printf( "SELECT %s(ST_GeomFromWKB(ST_AsBinary(%s))) FROM (%s) AS ogrpgextent",
286 : pszExtentFct, OGRPGEscapeColumnName(pszGeomColumn).c_str(),
287 1 : pszRawStatement );
288 : }
289 :
290 5 : return RunGetExtentRequest(psExtent, bForce, osCommand);
291 : }
|