Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements OGRPGeoTableLayer class, access to an existing table.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2005, Frank Warmerdam
9 : * Copyright (c) 2008-2011, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_conv.h"
15 : #include "ogr_pgeo.h"
16 : #include "ogrpgeogeometry.h"
17 :
18 : /************************************************************************/
19 : /* OGRPGeoTableLayer() */
20 : /************************************************************************/
21 :
22 0 : OGRPGeoTableLayer::OGRPGeoTableLayer(OGRPGeoDataSource *poDSIn,
23 0 : int nODBCStatementFlags)
24 0 : : pszQuery(nullptr)
25 : {
26 0 : m_nStatementFlags = nODBCStatementFlags;
27 0 : poDS = poDSIn;
28 0 : iNextShapeId = 0;
29 0 : nSRSId = -1;
30 0 : poFeatureDefn = nullptr;
31 0 : }
32 :
33 : /************************************************************************/
34 : /* ~OGRPGeoTableLayer() */
35 : /************************************************************************/
36 :
37 0 : OGRPGeoTableLayer::~OGRPGeoTableLayer()
38 :
39 : {
40 0 : CPLFree(pszQuery);
41 0 : ClearStatement();
42 0 : }
43 :
44 : /************************************************************************/
45 : /* Initialize() */
46 : /************************************************************************/
47 :
48 0 : CPLErr OGRPGeoTableLayer::Initialize(const char *pszTableName,
49 : const char *pszGeomCol, int nShapeType,
50 : double dfExtentLeft, double dfExtentRight,
51 : double dfExtentBottom, double dfExtentTop,
52 : int nSRID, int bHasZ, int bHasM)
53 :
54 : {
55 0 : CPLODBCSession *poSession = poDS->GetSession();
56 :
57 0 : SetDescription(pszTableName);
58 :
59 0 : CPLFree(pszGeomColumn);
60 0 : if (pszGeomCol == nullptr)
61 0 : pszGeomColumn = nullptr;
62 : else
63 0 : pszGeomColumn = CPLStrdup(pszGeomCol);
64 :
65 0 : CPLFree(pszFIDColumn);
66 0 : pszFIDColumn = nullptr;
67 :
68 0 : sExtent.MinX = dfExtentLeft;
69 0 : sExtent.MaxX = dfExtentRight;
70 0 : sExtent.MinY = dfExtentBottom;
71 0 : sExtent.MaxY = dfExtentTop;
72 :
73 0 : if (pszGeomCol)
74 0 : LookupSRID(nSRID);
75 :
76 : // Setup geometry type.
77 :
78 : // The PGeo format has a similar approach to multi-part handling as
79 : // Shapefiles, where polygon and multipolygon geometries or line and
80 : // multiline geometries will co-exist in a layer reported as just polygon or
81 : // line type respectively. To handle this in a predictable way for clients
82 : // we always promote the polygon/line types to multitypes, and
83 : // correspondingly ALWAYS return multi polygon/line geometry objects for
84 : // features (even if strictly speaking the original feature had a
85 : // polygon/line geometry object)
86 : OGRwkbGeometryType eOGRType;
87 :
88 0 : switch (nShapeType)
89 : {
90 0 : case ESRI_LAYERGEOMTYPE_NULL:
91 0 : eOGRType = wkbNone;
92 0 : break;
93 :
94 0 : case ESRI_LAYERGEOMTYPE_POINT:
95 0 : eOGRType = wkbPoint;
96 0 : break;
97 :
98 0 : case ESRI_LAYERGEOMTYPE_MULTIPOINT:
99 0 : eOGRType = wkbMultiPoint;
100 0 : break;
101 :
102 0 : case ESRI_LAYERGEOMTYPE_POLYLINE:
103 0 : eOGRType = wkbMultiLineString; // see comment above
104 0 : break;
105 :
106 0 : case ESRI_LAYERGEOMTYPE_POLYGON:
107 : case ESRI_LAYERGEOMTYPE_MULTIPATCH:
108 0 : eOGRType = wkbMultiPolygon; // see comment above
109 0 : break;
110 :
111 0 : default:
112 0 : CPLDebug("PGeo", "Unexpected value for shape type : %d",
113 : nShapeType);
114 0 : eOGRType = wkbUnknown;
115 0 : break;
116 : }
117 :
118 0 : if (eOGRType != wkbUnknown && eOGRType != wkbNone)
119 : {
120 0 : if (bHasZ)
121 0 : eOGRType = wkbSetZ(eOGRType);
122 0 : if (bHasM)
123 0 : eOGRType = wkbSetM(eOGRType);
124 : }
125 0 : CPL_IGNORE_RET_VAL(eOGRType);
126 :
127 : /* -------------------------------------------------------------------- */
128 : /* Do we have a simple primary key? */
129 : /* -------------------------------------------------------------------- */
130 0 : CPLODBCStatement oGetKey(poSession);
131 :
132 0 : if (oGetKey.GetPrimaryKeys(pszTableName) && oGetKey.Fetch())
133 : {
134 0 : pszFIDColumn = CPLStrdup(oGetKey.GetColData(3));
135 :
136 0 : if (oGetKey.Fetch()) // more than one field in key!
137 : {
138 0 : CPLFree(pszFIDColumn);
139 0 : pszFIDColumn = nullptr;
140 0 : CPLDebug("PGeo", "%s: Compound primary key, ignoring.",
141 : pszTableName);
142 : }
143 : else
144 0 : CPLDebug("PGeo", "%s: Got primary key %s.", pszTableName,
145 : pszFIDColumn);
146 : }
147 : else
148 0 : CPLDebug("PGeo", "%s: no primary key", pszTableName);
149 :
150 : /* -------------------------------------------------------------------- */
151 : /* Get the column definitions for this table. */
152 : /* -------------------------------------------------------------------- */
153 0 : CPLODBCStatement oGetCol(poSession);
154 : CPLErr eErr;
155 :
156 0 : if (!oGetCol.GetColumns(pszTableName))
157 : {
158 0 : CPLError(CE_Failure, CPLE_AppDefined, "GetColumns() failed on %s.\n%s",
159 : pszTableName, poSession->GetLastError());
160 0 : return CE_Failure;
161 : }
162 :
163 0 : eErr = BuildFeatureDefn(pszTableName, &oGetCol);
164 0 : if (eErr != CE_None)
165 0 : return eErr;
166 :
167 0 : if (poFeatureDefn->GetFieldCount() == 0)
168 : {
169 0 : CPLError(
170 : CE_Failure, CPLE_AppDefined,
171 : "No column definitions found for table '%s', layer not usable.",
172 : pszTableName);
173 0 : return CE_Failure;
174 : }
175 :
176 0 : poFeatureDefn->SetGeomType(eOGRType);
177 :
178 : // Where possible, retrieve useful metadata information from the GDB_Items
179 : // table
180 0 : if (poDS->HasGdbItemsTable())
181 : {
182 0 : CPLODBCStatement oItemsStmt(poSession);
183 0 : oItemsStmt.Append(
184 : "SELECT Definition, Documentation FROM GDB_Items WHERE Name='");
185 0 : oItemsStmt.Append(pszTableName);
186 0 : oItemsStmt.Append("'");
187 0 : if (oItemsStmt.ExecuteSQL())
188 : {
189 0 : while (oItemsStmt.Fetch())
190 : {
191 : const CPLString osDefinition =
192 0 : CPLString(oItemsStmt.GetColData(0, ""));
193 0 : if (strstr(osDefinition, "DEFeatureClassInfo") != nullptr)
194 : {
195 0 : m_osDefinition = osDefinition;
196 :
197 : // try to retrieve field domains
198 : CPLXMLTreeCloser oTree(
199 0 : CPLParseXMLString(osDefinition.c_str()));
200 0 : if (oTree.get())
201 : {
202 0 : if (const CPLXMLNode *psFieldInfoExs = CPLGetXMLNode(
203 : oTree.get(),
204 : "=DEFeatureClassInfo.GPFieldInfoExs"))
205 : {
206 0 : for (const CPLXMLNode *psFieldInfoEx =
207 0 : CPLGetXMLNode(psFieldInfoExs,
208 : "GPFieldInfoEx");
209 0 : psFieldInfoEx != nullptr;
210 0 : psFieldInfoEx = psFieldInfoEx->psNext)
211 : {
212 : const CPLString osName =
213 0 : CPLGetXMLValue(psFieldInfoEx, "Name", "");
214 : const CPLString osDomainName = CPLGetXMLValue(
215 0 : psFieldInfoEx, "DomainName", "");
216 0 : if (!osDomainName.empty())
217 : {
218 : const int fieldIndex =
219 0 : poFeatureDefn->GetFieldIndex(osName);
220 0 : if (fieldIndex != -1)
221 0 : poFeatureDefn->GetFieldDefn(fieldIndex)
222 0 : ->SetDomainName(osDomainName);
223 : }
224 : }
225 : }
226 : }
227 :
228 : // try to retrieve layer medata
229 0 : m_osDocumentation = CPLString(oItemsStmt.GetColData(1, ""));
230 0 : break;
231 : }
232 : }
233 : }
234 : }
235 :
236 0 : return CE_None;
237 : }
238 :
239 : /************************************************************************/
240 : /* ClearStatement() */
241 : /************************************************************************/
242 :
243 0 : void OGRPGeoTableLayer::ClearStatement()
244 :
245 : {
246 0 : if (poStmt != nullptr)
247 : {
248 0 : delete poStmt;
249 0 : poStmt = nullptr;
250 : }
251 0 : }
252 :
253 : /************************************************************************/
254 : /* GetStatement() */
255 : /************************************************************************/
256 :
257 0 : CPLODBCStatement *OGRPGeoTableLayer::GetStatement()
258 :
259 : {
260 0 : if (poStmt == nullptr)
261 0 : ResetStatement();
262 :
263 0 : return poStmt;
264 : }
265 :
266 : /************************************************************************/
267 : /* ResetStatement() */
268 : /************************************************************************/
269 :
270 0 : OGRErr OGRPGeoTableLayer::ResetStatement()
271 :
272 : {
273 0 : ClearStatement();
274 :
275 0 : iNextShapeId = 0;
276 :
277 0 : poStmt = new CPLODBCStatement(poDS->GetSession(), m_nStatementFlags);
278 0 : poStmt->Append("SELECT * FROM ");
279 0 : poStmt->Append(poFeatureDefn->GetName());
280 0 : if (pszQuery != nullptr)
281 0 : poStmt->Appendf(" WHERE %s", pszQuery);
282 :
283 0 : if (poStmt->ExecuteSQL())
284 0 : return OGRERR_NONE;
285 : else
286 : {
287 0 : delete poStmt;
288 0 : poStmt = nullptr;
289 0 : return OGRERR_FAILURE;
290 : }
291 : }
292 :
293 : /************************************************************************/
294 : /* ResetReading() */
295 : /************************************************************************/
296 :
297 0 : void OGRPGeoTableLayer::ResetReading()
298 :
299 : {
300 0 : ClearStatement();
301 0 : OGRPGeoLayer::ResetReading();
302 0 : }
303 :
304 : /************************************************************************/
305 : /* GetFeature() */
306 : /************************************************************************/
307 :
308 0 : OGRFeature *OGRPGeoTableLayer::GetFeature(GIntBig nFeatureId)
309 :
310 : {
311 0 : if (pszFIDColumn == nullptr)
312 0 : return OGRPGeoLayer::GetFeature(nFeatureId);
313 :
314 0 : ClearStatement();
315 :
316 0 : iNextShapeId = nFeatureId;
317 :
318 0 : poStmt = new CPLODBCStatement(poDS->GetSession(), m_nStatementFlags);
319 0 : poStmt->Append("SELECT * FROM ");
320 0 : poStmt->Append(poFeatureDefn->GetName());
321 0 : poStmt->Appendf(" WHERE %s = " CPL_FRMT_GIB, pszFIDColumn, nFeatureId);
322 :
323 0 : if (!poStmt->ExecuteSQL())
324 : {
325 0 : delete poStmt;
326 0 : poStmt = nullptr;
327 0 : return nullptr;
328 : }
329 :
330 0 : return GetNextRawFeature();
331 : }
332 :
333 : /************************************************************************/
334 : /* SetAttributeFilter() */
335 : /************************************************************************/
336 :
337 0 : OGRErr OGRPGeoTableLayer::SetAttributeFilter(const char *pszQueryIn)
338 :
339 : {
340 0 : CPLFree(m_pszAttrQueryString);
341 0 : m_pszAttrQueryString = (pszQueryIn) ? CPLStrdup(pszQueryIn) : nullptr;
342 :
343 0 : if ((pszQueryIn == nullptr && pszQuery == nullptr) ||
344 0 : (pszQueryIn != nullptr && pszQuery != nullptr &&
345 0 : EQUAL(pszQueryIn, pszQuery)))
346 0 : return OGRERR_NONE;
347 :
348 0 : CPLFree(pszQuery);
349 0 : pszQuery = pszQueryIn ? CPLStrdup(pszQueryIn) : nullptr;
350 :
351 0 : ClearStatement();
352 :
353 0 : return OGRERR_NONE;
354 : }
355 :
356 : /************************************************************************/
357 : /* TestCapability() */
358 : /************************************************************************/
359 :
360 0 : int OGRPGeoTableLayer::TestCapability(const char *pszCap)
361 :
362 : {
363 0 : if (EQUAL(pszCap, OLCRandomRead))
364 0 : return TRUE;
365 :
366 0 : else if (EQUAL(pszCap, OLCFastFeatureCount))
367 0 : return m_poFilterGeom == nullptr && poDS->CountStarWorking();
368 :
369 0 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
370 0 : return FALSE;
371 :
372 : else
373 0 : return OGRPGeoLayer::TestCapability(pszCap);
374 : }
375 :
376 : /************************************************************************/
377 : /* GetFeatureCount() */
378 : /* */
379 : /* If a spatial filter is in effect, we turn control over to */
380 : /* the generic counter. Otherwise we return the total count. */
381 : /* Eventually we should consider implementing a more efficient */
382 : /* way of counting features matching a spatial query. */
383 : /************************************************************************/
384 :
385 0 : GIntBig OGRPGeoTableLayer::GetFeatureCount(int bForce)
386 :
387 : {
388 0 : if (m_poFilterGeom != nullptr || !poDS->CountStarWorking())
389 0 : return OGRPGeoLayer::GetFeatureCount(bForce);
390 :
391 0 : CPLODBCStatement oStmt(poDS->GetSession());
392 0 : oStmt.Append("SELECT COUNT(*) FROM ");
393 0 : oStmt.Append(poFeatureDefn->GetName());
394 :
395 0 : if (pszQuery != nullptr)
396 0 : oStmt.Appendf(" WHERE %s", pszQuery);
397 :
398 0 : if (!oStmt.ExecuteSQL() || !oStmt.Fetch())
399 : {
400 0 : CPLError(CE_Failure, CPLE_AppDefined,
401 : "GetFeatureCount() failed on query %s.\n%s",
402 0 : oStmt.GetCommand(), poDS->GetSession()->GetLastError());
403 0 : return OGRPGeoLayer::GetFeatureCount(bForce);
404 : }
405 :
406 0 : return CPLAtoGIntBig(oStmt.GetColData(0));
407 : }
408 :
409 : /************************************************************************/
410 : /* GetExtent() */
411 : /************************************************************************/
412 :
413 0 : OGRErr OGRPGeoTableLayer::GetExtent(OGREnvelope *psExtent,
414 : CPL_UNUSED int bForce)
415 : {
416 0 : if (pszGeomColumn == nullptr)
417 : {
418 0 : return OGRERR_FAILURE;
419 : }
420 :
421 0 : *psExtent = sExtent;
422 0 : return OGRERR_NONE;
423 : }
|