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