Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements OGRODBCTableLayer class, access to an existing table.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2003, Frank Warmerdam
9 : * Copyright (c) 2009-2012, Even Rouault <even dot rouault at spatialys.com>
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 "cpl_conv.h"
31 : #include "ogr_odbc.h"
32 :
33 : /************************************************************************/
34 : /* OGRODBCTableLayer() */
35 : /************************************************************************/
36 :
37 0 : OGRODBCTableLayer::OGRODBCTableLayer(OGRODBCDataSource *poDSIn,
38 0 : int nODBCStatementFlags)
39 : : pszQuery(nullptr), bHaveSpatialExtents(FALSE), pszTableName(nullptr),
40 0 : pszSchemaName(nullptr)
41 : {
42 0 : m_nStatementFlags = nODBCStatementFlags;
43 0 : poDS = poDSIn;
44 0 : iNextShapeId = 0;
45 :
46 0 : nSRSId = -1;
47 :
48 0 : poFeatureDefn = nullptr;
49 0 : }
50 :
51 : /************************************************************************/
52 : /* ~OGRODBCTableLayer() */
53 : /************************************************************************/
54 :
55 0 : OGRODBCTableLayer::~OGRODBCTableLayer()
56 :
57 : {
58 0 : CPLFree(pszTableName);
59 0 : CPLFree(pszSchemaName);
60 :
61 0 : CPLFree(pszQuery);
62 0 : ClearStatement();
63 0 : }
64 :
65 : /************************************************************************/
66 : /* Initialize() */
67 : /************************************************************************/
68 :
69 0 : CPLErr OGRODBCTableLayer::Initialize(const char *pszLayerName,
70 : const char *pszGeomCol)
71 :
72 : {
73 0 : CPLODBCSession *poSession = poDS->GetSession();
74 :
75 0 : CPLFree(pszFIDColumn);
76 0 : pszFIDColumn = nullptr;
77 :
78 0 : SetDescription(pszLayerName);
79 :
80 : /* -------------------------------------------------------------------- */
81 : /* Parse out schema name if present in layer. We assume a */
82 : /* schema is provided if there is a dot in the name, and that */
83 : /* it is in the form <schema>.<tablename> */
84 : /* -------------------------------------------------------------------- */
85 0 : const char *pszDot = strstr(pszLayerName, ".");
86 0 : if (pszDot != nullptr)
87 : {
88 0 : pszTableName = CPLStrdup(pszDot + 1);
89 0 : pszSchemaName = CPLStrdup(pszLayerName);
90 0 : pszSchemaName[pszDot - pszLayerName] = '\0';
91 : }
92 : else
93 : {
94 0 : pszTableName = CPLStrdup(pszLayerName);
95 : }
96 :
97 : /* -------------------------------------------------------------------- */
98 : /* Do we have a simple primary key? */
99 : /* -------------------------------------------------------------------- */
100 0 : CPLODBCStatement oGetKey(poSession);
101 :
102 0 : if (oGetKey.GetPrimaryKeys(pszTableName, nullptr, pszSchemaName) &&
103 0 : oGetKey.Fetch())
104 : {
105 0 : pszFIDColumn = CPLStrdup(oGetKey.GetColData(3));
106 :
107 0 : if (oGetKey.Fetch()) // more than one field in key!
108 : {
109 0 : CPLFree(pszFIDColumn);
110 0 : pszFIDColumn = nullptr;
111 :
112 0 : CPLDebug("OGR_ODBC",
113 : "Table %s has multiple primary key fields, "
114 : "ignoring them all.",
115 : pszTableName);
116 : }
117 : }
118 :
119 : /* -------------------------------------------------------------------- */
120 : /* Have we been provided a geometry column? */
121 : /* -------------------------------------------------------------------- */
122 0 : CPLFree(pszGeomColumn);
123 0 : if (pszGeomCol == nullptr)
124 0 : pszGeomColumn = nullptr;
125 : else
126 0 : pszGeomColumn = CPLStrdup(pszGeomCol);
127 :
128 : /* -------------------------------------------------------------------- */
129 : /* Get the column definitions for this table. */
130 : /* -------------------------------------------------------------------- */
131 0 : CPLODBCStatement oGetCol(poSession);
132 : CPLErr eErr;
133 :
134 0 : if (!oGetCol.GetColumns(pszTableName, nullptr, pszSchemaName))
135 0 : return CE_Failure;
136 :
137 0 : eErr = BuildFeatureDefn(pszLayerName, &oGetCol);
138 0 : if (eErr != CE_None)
139 0 : return eErr;
140 :
141 0 : if (poFeatureDefn->GetFieldCount() == 0)
142 : {
143 0 : CPLError(
144 : CE_Warning, CPLE_AppDefined,
145 : "No column definitions found for table '%s', layer not usable.",
146 : pszLayerName);
147 0 : return CE_Failure;
148 : }
149 :
150 : /* -------------------------------------------------------------------- */
151 : /* Do we have XMIN, YMIN, XMAX, YMAX extent fields? */
152 : /* -------------------------------------------------------------------- */
153 0 : if (poFeatureDefn->GetFieldIndex("XMIN") != -1 &&
154 0 : poFeatureDefn->GetFieldIndex("XMAX") != -1 &&
155 0 : poFeatureDefn->GetFieldIndex("YMIN") != -1 &&
156 0 : poFeatureDefn->GetFieldIndex("YMAX") != -1)
157 : {
158 0 : bHaveSpatialExtents = TRUE;
159 0 : CPLDebug("OGR_ODBC", "Table %s has geometry extent fields.",
160 : pszLayerName);
161 : }
162 :
163 : /* -------------------------------------------------------------------- */
164 : /* If we got a geometry column, does it exist? Is it binary? */
165 : /* -------------------------------------------------------------------- */
166 0 : if (pszGeomColumn != nullptr)
167 : {
168 0 : int iColumn = oGetCol.GetColId(pszGeomColumn);
169 0 : if (iColumn < 0)
170 : {
171 0 : CPLError(CE_Failure, CPLE_AppDefined,
172 : "Column %s requested for geometry, but it does not exist.",
173 : pszGeomColumn);
174 0 : CPLFree(pszGeomColumn);
175 0 : pszGeomColumn = nullptr;
176 : }
177 : else
178 : {
179 0 : if (CPLODBCStatement::GetTypeMapping(oGetCol.GetColType(iColumn)) ==
180 : SQL_C_BINARY)
181 0 : bGeomColumnWKB = TRUE;
182 : }
183 : }
184 :
185 0 : return CE_None;
186 : }
187 :
188 : /************************************************************************/
189 : /* ClearStatement() */
190 : /************************************************************************/
191 :
192 0 : void OGRODBCTableLayer::ClearStatement()
193 :
194 : {
195 0 : if (poStmt != nullptr)
196 : {
197 0 : delete poStmt;
198 0 : poStmt = nullptr;
199 : }
200 0 : }
201 :
202 : /************************************************************************/
203 : /* GetStatement() */
204 : /************************************************************************/
205 :
206 0 : CPLODBCStatement *OGRODBCTableLayer::GetStatement()
207 :
208 : {
209 0 : if (poStmt == nullptr)
210 0 : ResetStatement();
211 :
212 0 : return poStmt;
213 : }
214 :
215 : /************************************************************************/
216 : /* EscapeAndQuoteIdentifier() */
217 : /************************************************************************/
218 :
219 0 : static CPLString EscapeAndQuoteIdentifier(const CPLString &osStr)
220 : {
221 0 : CPLString osRet;
222 0 : int num_dots = 0;
223 0 : for (size_t i = 0; i < osStr.size(); i++)
224 : {
225 0 : if (osStr[i] == '"')
226 : {
227 0 : osRet += "\\\"";
228 : }
229 0 : else if (osStr[i] == '.' && num_dots == 0)
230 : {
231 : /* It's schema qualified, so first segment we assume is the schema
232 : * and should be quoted separately */
233 0 : osRet += "\".\"";
234 0 : num_dots += 1;
235 : }
236 : else
237 : {
238 0 : osRet += osStr[i];
239 : }
240 : }
241 0 : return '"' + osRet + '"';
242 : }
243 :
244 : /************************************************************************/
245 : /* ResetStatement() */
246 : /************************************************************************/
247 :
248 0 : OGRErr OGRODBCTableLayer::ResetStatement()
249 :
250 : {
251 0 : ClearStatement();
252 :
253 0 : iNextShapeId = 0;
254 :
255 0 : poStmt = new CPLODBCStatement(poDS->GetSession(), m_nStatementFlags);
256 0 : poStmt->Append("SELECT * FROM ");
257 0 : poStmt->Append(EscapeAndQuoteIdentifier(poFeatureDefn->GetName()));
258 :
259 : /* Append attribute query if we have it */
260 0 : if (pszQuery != nullptr)
261 0 : poStmt->Appendf(" WHERE %s", pszQuery);
262 :
263 : /* If we have a spatial filter, and per record extents, query on it */
264 0 : if (m_poFilterGeom != nullptr && bHaveSpatialExtents)
265 : {
266 0 : if (pszQuery == nullptr)
267 0 : poStmt->Append(" WHERE");
268 : else
269 0 : poStmt->Append(" AND");
270 :
271 0 : poStmt->Appendf(" XMAX > %.8f AND XMIN < %.8f"
272 : " AND YMAX > %.8f AND YMIN < %.8f",
273 : m_sFilterEnvelope.MinX, m_sFilterEnvelope.MaxX,
274 : m_sFilterEnvelope.MinY, m_sFilterEnvelope.MaxY);
275 : }
276 :
277 0 : CPLDebug("OGR_ODBC", "ExecuteSQL(%s)", poStmt->GetCommand());
278 0 : if (poStmt->ExecuteSQL())
279 0 : return OGRERR_NONE;
280 : else
281 : {
282 0 : delete poStmt;
283 0 : poStmt = nullptr;
284 0 : return OGRERR_FAILURE;
285 : }
286 : }
287 :
288 : /************************************************************************/
289 : /* ResetReading() */
290 : /************************************************************************/
291 :
292 0 : void OGRODBCTableLayer::ResetReading()
293 :
294 : {
295 0 : ClearStatement();
296 0 : OGRODBCLayer::ResetReading();
297 0 : }
298 :
299 : /************************************************************************/
300 : /* GetFeature() */
301 : /************************************************************************/
302 :
303 0 : OGRFeature *OGRODBCTableLayer::GetFeature(GIntBig nFeatureId)
304 :
305 : {
306 0 : if (pszFIDColumn == nullptr)
307 0 : return OGRODBCLayer::GetFeature(nFeatureId);
308 :
309 0 : ClearStatement();
310 :
311 0 : iNextShapeId = nFeatureId;
312 :
313 0 : poStmt = new CPLODBCStatement(poDS->GetSession(), m_nStatementFlags);
314 0 : poStmt->Append("SELECT * FROM ");
315 0 : poStmt->Append(EscapeAndQuoteIdentifier(poFeatureDefn->GetName()));
316 0 : poStmt->Appendf(" WHERE %s = " CPL_FRMT_GIB,
317 0 : EscapeAndQuoteIdentifier(pszFIDColumn).c_str(), nFeatureId);
318 :
319 0 : if (!poStmt->ExecuteSQL())
320 : {
321 0 : delete poStmt;
322 0 : poStmt = nullptr;
323 0 : return nullptr;
324 : }
325 :
326 0 : return GetNextRawFeature();
327 : }
328 :
329 : /************************************************************************/
330 : /* SetAttributeFilter() */
331 : /************************************************************************/
332 :
333 0 : OGRErr OGRODBCTableLayer::SetAttributeFilter(const char *pszQueryIn)
334 :
335 : {
336 0 : CPLFree(m_pszAttrQueryString);
337 0 : m_pszAttrQueryString = (pszQueryIn) ? CPLStrdup(pszQueryIn) : nullptr;
338 :
339 0 : if ((pszQueryIn == nullptr && pszQuery == nullptr) ||
340 0 : (pszQueryIn != nullptr && pszQuery != nullptr &&
341 0 : EQUAL(pszQueryIn, pszQuery)))
342 0 : return OGRERR_NONE;
343 :
344 0 : CPLFree(pszQuery);
345 0 : pszQuery = pszQueryIn != nullptr ? CPLStrdup(pszQueryIn) : nullptr;
346 :
347 0 : ClearStatement();
348 :
349 0 : return OGRERR_NONE;
350 : }
351 :
352 : /************************************************************************/
353 : /* TestCapability() */
354 : /************************************************************************/
355 :
356 0 : int OGRODBCTableLayer::TestCapability(const char *pszCap)
357 :
358 : {
359 0 : if (EQUAL(pszCap, OLCRandomRead))
360 0 : return TRUE;
361 :
362 : else
363 0 : return OGRODBCLayer::TestCapability(pszCap);
364 : }
365 :
366 : /************************************************************************/
367 : /* GetFeatureCount() */
368 : /* */
369 : /* If a spatial filter is in effect, we turn control over to */
370 : /* the generic counter. Otherwise we return the total count. */
371 : /* Eventually we should consider implementing a more efficient */
372 : /* way of counting features matching a spatial query. */
373 : /************************************************************************/
374 :
375 0 : GIntBig OGRODBCTableLayer::GetFeatureCount(int bForce)
376 :
377 : {
378 0 : if (m_poFilterGeom != nullptr)
379 0 : return OGRODBCLayer::GetFeatureCount(bForce);
380 :
381 0 : CPLODBCStatement oStmt(poDS->GetSession());
382 0 : oStmt.Append("SELECT COUNT(*) FROM ");
383 0 : oStmt.Append(EscapeAndQuoteIdentifier(poFeatureDefn->GetName()));
384 :
385 0 : if (pszQuery != nullptr)
386 0 : oStmt.Appendf(" WHERE %s", pszQuery);
387 :
388 0 : if (!oStmt.ExecuteSQL() || !oStmt.Fetch())
389 : {
390 0 : CPLError(CE_Failure, CPLE_AppDefined,
391 : "GetFeatureCount() failed on query %s.\n%s",
392 0 : oStmt.GetCommand(), poDS->GetSession()->GetLastError());
393 0 : return OGRODBCLayer::GetFeatureCount(bForce);
394 : }
395 :
396 0 : return CPLAtoGIntBig(oStmt.GetColData(0));
397 : }
398 :
399 : /************************************************************************/
400 : /* GetSpatialRef() */
401 : /* */
402 : /* We override this to try and fetch the table SRID from the */
403 : /* geometry_columns table if the srsid is -2 (meaning we */
404 : /* haven't yet even looked for it). */
405 : /************************************************************************/
406 :
407 0 : OGRSpatialReference *OGRODBCTableLayer::GetSpatialRef()
408 :
409 : {
410 : #ifdef notdef
411 : if (nSRSId == -2)
412 : {
413 : PGconn *hPGConn = poDS->GetPGConn();
414 :
415 : nSRSId = -1;
416 :
417 : poDS->SoftStartTransaction();
418 :
419 : char szCommand[1024] = {};
420 : sprintf(szCommand,
421 : "SELECT srid FROM geometry_columns "
422 : "WHERE f_table_name = '%s'",
423 : poFeatureDefn->GetName());
424 : PGresult *hResult = PQexec(hPGConn, szCommand);
425 :
426 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
427 : PQntuples(hResult) == 1)
428 : {
429 : nSRSId = atoi(PQgetvalue(hResult, 0, 0));
430 : }
431 :
432 : poDS->SoftCommit();
433 : }
434 : #endif
435 :
436 0 : return OGRODBCLayer::GetSpatialRef();
437 : }
|