Line data Source code
1 : /******************************************************************************
2 :
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 : * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
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 "ogr_pg.h"
32 : #include "cpl_conv.h"
33 : #include "cpl_string.h"
34 : #include "cpl_error.h"
35 : #include "ogr_p.h"
36 :
37 : #include <chrono>
38 : #include <condition_variable>
39 : #include <mutex>
40 : #include <thread>
41 :
42 : #define PQexec this_is_an_error
43 :
44 : #define UNSUPPORTED_OP_READ_ONLY \
45 : "%s : unsupported operation on a read-only datasource."
46 :
47 : /************************************************************************/
48 : /* OGRPGTableFeatureDefn */
49 : /************************************************************************/
50 :
51 : class OGRPGTableFeatureDefn final : public OGRPGFeatureDefn
52 : {
53 : private:
54 : OGRPGTableFeatureDefn(const OGRPGTableFeatureDefn &) = delete;
55 : OGRPGTableFeatureDefn &operator=(const OGRPGTableFeatureDefn &) = delete;
56 :
57 : OGRPGTableLayer *poLayer = nullptr;
58 :
59 : void SolveFields() const;
60 :
61 : public:
62 656 : explicit OGRPGTableFeatureDefn(OGRPGTableLayer *poLayerIn,
63 : const char *pszName = nullptr)
64 656 : : OGRPGFeatureDefn(pszName), poLayer(poLayerIn)
65 : {
66 656 : }
67 :
68 656 : virtual void UnsetLayer() override
69 : {
70 656 : poLayer = nullptr;
71 656 : OGRPGFeatureDefn::UnsetLayer();
72 656 : }
73 :
74 160029 : virtual int GetFieldCount() const override
75 : {
76 160029 : SolveFields();
77 160029 : return OGRPGFeatureDefn::GetFieldCount();
78 : }
79 :
80 52203 : virtual OGRFieldDefn *GetFieldDefn(int i) override
81 : {
82 52203 : SolveFields();
83 52203 : return OGRPGFeatureDefn::GetFieldDefn(i);
84 : }
85 :
86 35453 : virtual const OGRFieldDefn *GetFieldDefn(int i) const override
87 : {
88 35453 : SolveFields();
89 35453 : return OGRPGFeatureDefn::GetFieldDefn(i);
90 : }
91 :
92 7521 : virtual int GetFieldIndex(const char *pszName) const override
93 : {
94 7521 : SolveFields();
95 7521 : return OGRPGFeatureDefn::GetFieldIndex(pszName);
96 : }
97 :
98 54198 : virtual int GetGeomFieldCount() const override
99 : {
100 54198 : if (poLayer != nullptr && !poLayer->HasGeometryInformation())
101 1039 : SolveFields();
102 54198 : return OGRPGFeatureDefn::GetGeomFieldCount();
103 : }
104 :
105 14551 : virtual OGRPGGeomFieldDefn *GetGeomFieldDefn(int i) override
106 : {
107 14551 : if (poLayer != nullptr && !poLayer->HasGeometryInformation())
108 141 : SolveFields();
109 14551 : return OGRPGFeatureDefn::GetGeomFieldDefn(i);
110 : }
111 :
112 1023 : virtual const OGRPGGeomFieldDefn *GetGeomFieldDefn(int i) const override
113 : {
114 1023 : if (poLayer != nullptr && !poLayer->HasGeometryInformation())
115 0 : SolveFields();
116 1023 : return OGRPGFeatureDefn::GetGeomFieldDefn(i);
117 : }
118 :
119 822 : virtual int GetGeomFieldIndex(const char *pszName) const override
120 : {
121 822 : if (poLayer != nullptr && !poLayer->HasGeometryInformation())
122 0 : SolveFields();
123 822 : return OGRPGFeatureDefn::GetGeomFieldIndex(pszName);
124 : }
125 : };
126 :
127 : /************************************************************************/
128 : /* SolveFields() */
129 : /************************************************************************/
130 :
131 256386 : void OGRPGTableFeatureDefn::SolveFields() const
132 : {
133 256386 : if (poLayer == nullptr)
134 1153 : return;
135 :
136 255233 : poLayer->ReadTableDefinition();
137 : }
138 :
139 : /************************************************************************/
140 : /* GetFIDColumn() */
141 : /************************************************************************/
142 :
143 83 : const char *OGRPGTableLayer::GetFIDColumn()
144 :
145 : {
146 83 : ReadTableDefinition();
147 :
148 83 : if (pszFIDColumn != nullptr)
149 81 : return pszFIDColumn;
150 : else
151 2 : return "";
152 : }
153 :
154 : /************************************************************************/
155 : /* OGRPGTableLayer() */
156 : /************************************************************************/
157 :
158 656 : OGRPGTableLayer::OGRPGTableLayer(OGRPGDataSource *poDSIn,
159 : CPLString &osCurrentSchema,
160 : const char *pszTableNameIn,
161 : const char *pszSchemaNameIn,
162 : const char *pszDescriptionIn,
163 656 : const char *pszGeomColForcedIn, int bUpdate)
164 1312 : : bUpdateAccess(bUpdate), pszTableName(CPLStrdup(pszTableNameIn)),
165 656 : pszSchemaName(CPLStrdup(pszSchemaNameIn ? pszSchemaNameIn
166 347 : : osCurrentSchema.c_str())),
167 656 : m_pszTableDescription(pszDescriptionIn ? CPLStrdup(pszDescriptionIn)
168 : : nullptr),
169 : osPrimaryKey(CPLGetConfigOption("PGSQL_OGR_FID", "ogc_fid")),
170 656 : pszGeomColForced(pszGeomColForcedIn ? CPLStrdup(pszGeomColForcedIn)
171 : : nullptr),
172 : // Just in provision for people yelling about broken backward
173 : // compatibility.
174 : bRetrieveFID(
175 2624 : CPLTestBool(CPLGetConfigOption("OGR_PG_RETRIEVE_FID", "TRUE")))
176 : {
177 656 : poDS = poDSIn;
178 656 : pszQueryStatement = nullptr;
179 :
180 : /* -------------------------------------------------------------------- */
181 : /* Build the layer defn name. */
182 : /* -------------------------------------------------------------------- */
183 1312 : CPLString osDefnName;
184 656 : if (pszSchemaNameIn && osCurrentSchema != pszSchemaNameIn)
185 : {
186 46 : osDefnName.Printf("%s.%s", pszSchemaNameIn, pszTableName);
187 138 : pszSqlTableName = CPLStrdup(CPLString().Printf(
188 92 : "%s.%s", OGRPGEscapeColumnName(pszSchemaNameIn).c_str(),
189 138 : OGRPGEscapeColumnName(pszTableName).c_str()));
190 : }
191 : else
192 : {
193 : // no prefix for current_schema in layer name, for backwards
194 : // compatibility.
195 610 : osDefnName = pszTableName;
196 610 : pszSqlTableName = CPLStrdup(OGRPGEscapeColumnName(pszTableName));
197 : }
198 656 : if (pszGeomColForced != nullptr)
199 : {
200 5 : osDefnName += "(";
201 5 : osDefnName += pszGeomColForced;
202 5 : osDefnName += ")";
203 : }
204 :
205 656 : poFeatureDefn = new OGRPGTableFeatureDefn(this, osDefnName);
206 656 : SetDescription(poFeatureDefn->GetName());
207 656 : poFeatureDefn->Reference();
208 :
209 : // bSealFields = false because we do lazy resolution of fields
210 656 : poFeatureDefn->Seal(/* bSealFields = */ false);
211 :
212 656 : if (pszDescriptionIn != nullptr && !EQUAL(pszDescriptionIn, ""))
213 : {
214 1 : OGRLayer::SetMetadataItem("DESCRIPTION", pszDescriptionIn);
215 : }
216 656 : }
217 :
218 : //************************************************************************/
219 : /* ~OGRPGTableLayer() */
220 : /************************************************************************/
221 :
222 1312 : OGRPGTableLayer::~OGRPGTableLayer()
223 :
224 : {
225 656 : if (bDeferredCreation)
226 0 : RunDeferredCreationIfNecessary();
227 656 : if (bCopyActive)
228 0 : EndCopy();
229 656 : UpdateSequenceIfNeeded();
230 656 : SerializeMetadata();
231 :
232 656 : CPLFree(pszSqlTableName);
233 656 : CPLFree(pszTableName);
234 656 : CPLFree(pszSqlGeomParentTableName);
235 656 : CPLFree(pszSchemaName);
236 656 : CPLFree(m_pszTableDescription);
237 656 : CPLFree(pszGeomColForced);
238 656 : CSLDestroy(papszOverrideColumnTypes);
239 1312 : }
240 :
241 : /************************************************************************/
242 : /* LoadMetadata() */
243 : /************************************************************************/
244 :
245 73 : void OGRPGTableLayer::LoadMetadata()
246 : {
247 73 : if (m_bMetadataLoaded)
248 52 : return;
249 21 : m_bMetadataLoaded = true;
250 :
251 21 : PGconn *hPGConn = poDS->GetPGConn();
252 : const std::string osSQL(
253 : CPLSPrintf("SELECT metadata FROM ogr_system_tables.metadata WHERE "
254 : "schema_name = %s AND table_name = %s",
255 42 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
256 84 : OGRPGEscapeString(hPGConn, pszTableName).c_str()));
257 42 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
258 21 : auto poSqlLyr = poDS->ExecuteSQL(osSQL.c_str(), nullptr, nullptr);
259 21 : if (poSqlLyr)
260 : {
261 : auto poFeature =
262 14 : std::unique_ptr<OGRFeature>(poSqlLyr->GetNextFeature());
263 7 : if (poFeature)
264 : {
265 1 : if (poFeature->IsFieldSetAndNotNull(0))
266 : {
267 1 : const char *pszXML = poFeature->GetFieldAsString(0);
268 1 : if (pszXML)
269 : {
270 1 : auto psRoot = CPLParseXMLString(pszXML);
271 1 : if (psRoot)
272 : {
273 1 : oMDMD.XMLInit(psRoot, true);
274 1 : CPLDestroyXMLNode(psRoot);
275 : }
276 : }
277 : }
278 : }
279 7 : poDS->ReleaseResultSet(poSqlLyr);
280 : }
281 : }
282 :
283 : /************************************************************************/
284 : /* SerializeMetadata() */
285 : /************************************************************************/
286 :
287 656 : void OGRPGTableLayer::SerializeMetadata()
288 : {
289 656 : if (!m_bMetadataModified)
290 648 : return;
291 :
292 8 : PGconn *hPGConn = poDS->GetPGConn();
293 8 : CPLXMLNode *psMD = oMDMD.Serialize();
294 :
295 8 : if (psMD)
296 : {
297 : // Remove DESCRIPTION and OLMD_FID64 items from metadata
298 :
299 6 : CPLXMLNode *psPrev = nullptr;
300 12 : for (CPLXMLNode *psIter = psMD; psIter;)
301 : {
302 6 : CPLXMLNode *psNext = psIter->psNext;
303 18 : if (psIter->eType == CXT_Element &&
304 12 : strcmp(psIter->pszValue, "Metadata") == 0 &&
305 6 : CPLGetXMLNode(psIter, "domain") == nullptr)
306 : {
307 6 : bool bFoundInterestingItems = false;
308 14 : for (CPLXMLNode *psIter2 = psIter->psChild; psIter2;)
309 : {
310 8 : CPLXMLNode *psNext2 = psIter2->psNext;
311 24 : if (psIter2->eType == CXT_Element &&
312 16 : strcmp(psIter2->pszValue, "MDI") == 0 &&
313 8 : (EQUAL(CPLGetXMLValue(psIter2, "key", ""),
314 6 : OLMD_FID64) ||
315 6 : EQUAL(CPLGetXMLValue(psIter2, "key", ""),
316 : "DESCRIPTION")))
317 : {
318 6 : CPLRemoveXMLChild(psIter, psIter2);
319 : }
320 : else
321 : {
322 2 : bFoundInterestingItems = true;
323 : }
324 8 : psIter2 = psNext2;
325 : }
326 6 : if (!bFoundInterestingItems)
327 : {
328 5 : if (psPrev)
329 0 : psPrev->psNext = psNext;
330 : else
331 5 : psMD = psNext;
332 5 : psIter->psNext = nullptr;
333 5 : CPLDestroyXMLNode(psIter);
334 : }
335 : }
336 6 : psIter = psNext;
337 6 : psPrev = psIter;
338 : }
339 : }
340 :
341 8 : if (psMD)
342 : {
343 1 : PGresult *hResult = OGRPG_PQexec(
344 1 : hPGConn, "CREATE SCHEMA IF NOT EXISTS ogr_system_tables");
345 1 : OGRPGClearResult(hResult);
346 :
347 1 : hResult = OGRPG_PQexec(
348 : hPGConn, "CREATE TABLE IF NOT EXISTS ogr_system_tables.metadata("
349 : "id SERIAL, "
350 : "schema_name TEXT NOT NULL, "
351 : "table_name TEXT NOT NULL, "
352 : "metadata TEXT,"
353 : "UNIQUE(schema_name, table_name))");
354 1 : OGRPGClearResult(hResult);
355 :
356 1 : hResult = OGRPG_PQexec(
357 : hPGConn,
358 : "DROP FUNCTION IF EXISTS "
359 : "ogr_system_tables.event_trigger_function_for_metadata() CASCADE");
360 1 : OGRPGClearResult(hResult);
361 :
362 1 : hResult = OGRPG_PQexec(
363 : hPGConn,
364 : "CREATE FUNCTION "
365 : "ogr_system_tables.event_trigger_function_for_metadata()\n"
366 : "RETURNS event_trigger LANGUAGE plpgsql AS $$\n"
367 : "DECLARE\n"
368 : " obj record;\n"
369 : "BEGIN\n"
370 : " FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects()\n"
371 : " LOOP\n"
372 : " IF obj.object_type = 'table' THEN\n"
373 : " DELETE FROM ogr_system_tables.metadata m WHERE "
374 : "m.schema_name = obj.schema_name AND m.table_name = "
375 : "obj.object_name;\n"
376 : " END IF;\n"
377 : " END LOOP;\n"
378 : "END;\n"
379 : "$$;");
380 1 : OGRPGClearResult(hResult);
381 :
382 1 : hResult = OGRPG_PQexec(hPGConn,
383 : "DROP EVENT TRIGGER IF EXISTS "
384 : "ogr_system_tables_event_trigger_for_metadata");
385 1 : OGRPGClearResult(hResult);
386 :
387 1 : hResult = OGRPG_PQexec(
388 : hPGConn,
389 : "CREATE EVENT TRIGGER ogr_system_tables_event_trigger_for_metadata "
390 : "ON sql_drop "
391 : "EXECUTE FUNCTION "
392 : "ogr_system_tables.event_trigger_function_for_metadata()");
393 1 : OGRPGClearResult(hResult);
394 :
395 2 : CPLString osCommand;
396 : osCommand.Printf("DELETE FROM ogr_system_tables.metadata WHERE "
397 : "schema_name = %s AND table_name = %s",
398 2 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
399 3 : OGRPGEscapeString(hPGConn, pszTableName).c_str());
400 1 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
401 1 : OGRPGClearResult(hResult);
402 :
403 : CPLXMLNode *psRoot =
404 1 : CPLCreateXMLNode(nullptr, CXT_Element, "GDALMetadata");
405 1 : CPLAddXMLChild(psRoot, psMD);
406 1 : char *pszXML = CPLSerializeXMLTree(psRoot);
407 : // CPLDebug("PG", "Serializing %s", pszXML);
408 :
409 : osCommand.Printf("INSERT INTO ogr_system_tables.metadata (schema_name, "
410 : "table_name, metadata) VALUES (%s, %s, %s)",
411 2 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
412 2 : OGRPGEscapeString(hPGConn, pszTableName).c_str(),
413 4 : OGRPGEscapeString(hPGConn, pszXML).c_str());
414 1 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
415 1 : OGRPGClearResult(hResult);
416 :
417 1 : CPLDestroyXMLNode(psRoot);
418 1 : CPLFree(pszXML);
419 : }
420 : else
421 : {
422 14 : CPLString osCommand;
423 : osCommand.Printf("DELETE FROM ogr_system_tables.metadata WHERE "
424 : "schema_name = %s AND table_name = %s",
425 14 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
426 21 : OGRPGEscapeString(hPGConn, pszTableName).c_str());
427 : PGresult *hResult =
428 7 : OGRPG_PQexec(hPGConn, osCommand.c_str(), false, true);
429 7 : OGRPGClearResult(hResult);
430 : }
431 : }
432 :
433 : /************************************************************************/
434 : /* GetMetadataDomainList() */
435 : /************************************************************************/
436 :
437 4 : char **OGRPGTableLayer::GetMetadataDomainList()
438 : {
439 4 : LoadMetadata();
440 :
441 4 : if (m_pszTableDescription == nullptr)
442 2 : GetMetadata();
443 4 : if (m_pszTableDescription != nullptr && m_pszTableDescription[0] != '\0')
444 2 : return CSLAddString(nullptr, "");
445 2 : return nullptr;
446 : }
447 :
448 : /************************************************************************/
449 : /* GetMetadata() */
450 : /************************************************************************/
451 :
452 33 : char **OGRPGTableLayer::GetMetadata(const char *pszDomain)
453 : {
454 33 : LoadMetadata();
455 :
456 33 : if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
457 32 : m_pszTableDescription == nullptr)
458 : {
459 13 : PGconn *hPGConn = poDS->GetPGConn();
460 26 : CPLString osCommand;
461 : osCommand.Printf("SELECT d.description FROM pg_class c "
462 : "JOIN pg_namespace n ON c.relnamespace=n.oid "
463 : "JOIN pg_description d "
464 : "ON d.objoid = c.oid AND d.classoid = "
465 : "'pg_class'::regclass::oid AND d.objsubid = 0 "
466 : "WHERE c.relname = %s AND n.nspname = %s AND "
467 : "c.relkind in ('r', 'v') ",
468 26 : OGRPGEscapeString(hPGConn, pszTableName).c_str(),
469 39 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str());
470 13 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
471 :
472 13 : const char *pszDesc = nullptr;
473 26 : if (hResult && PGRES_TUPLES_OK == PQresultStatus(hResult) &&
474 13 : PQntuples(hResult) == 1)
475 : {
476 3 : pszDesc = PQgetvalue(hResult, 0, 0);
477 3 : if (pszDesc)
478 3 : OGRLayer::SetMetadataItem("DESCRIPTION", pszDesc);
479 : }
480 13 : m_pszTableDescription = CPLStrdup(pszDesc ? pszDesc : "");
481 :
482 13 : OGRPGClearResult(hResult);
483 : }
484 :
485 33 : return OGRLayer::GetMetadata(pszDomain);
486 : }
487 :
488 : /************************************************************************/
489 : /* GetMetadataItem() */
490 : /************************************************************************/
491 :
492 19 : const char *OGRPGTableLayer::GetMetadataItem(const char *pszName,
493 : const char *pszDomain)
494 : {
495 19 : LoadMetadata();
496 :
497 19 : GetMetadata(pszDomain);
498 19 : return OGRLayer::GetMetadataItem(pszName, pszDomain);
499 : }
500 :
501 : /************************************************************************/
502 : /* SetMetadata() */
503 : /************************************************************************/
504 :
505 9 : CPLErr OGRPGTableLayer::SetMetadata(char **papszMD, const char *pszDomain)
506 : {
507 9 : LoadMetadata();
508 :
509 9 : OGRLayer::SetMetadata(papszMD, pszDomain);
510 9 : m_bMetadataModified = true;
511 :
512 11 : if (!osForcedDescription.empty() &&
513 2 : (pszDomain == nullptr || EQUAL(pszDomain, "")))
514 : {
515 2 : OGRLayer::SetMetadataItem("DESCRIPTION", osForcedDescription);
516 : }
517 :
518 9 : if (!bDeferredCreation && (pszDomain == nullptr || EQUAL(pszDomain, "")))
519 : {
520 8 : const char *pszDescription = OGRLayer::GetMetadataItem("DESCRIPTION");
521 8 : if (pszDescription == nullptr)
522 4 : pszDescription = "";
523 8 : PGconn *hPGConn = poDS->GetPGConn();
524 8 : CPLString osCommand;
525 :
526 : osCommand.Printf(
527 : "COMMENT ON TABLE %s IS %s", pszSqlTableName,
528 8 : pszDescription[0] != '\0'
529 12 : ? OGRPGEscapeString(hPGConn, pszDescription).c_str()
530 12 : : "NULL");
531 8 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
532 8 : OGRPGClearResult(hResult);
533 :
534 8 : CPLFree(m_pszTableDescription);
535 8 : m_pszTableDescription = CPLStrdup(pszDescription);
536 : }
537 :
538 9 : return CE_None;
539 : }
540 :
541 : /************************************************************************/
542 : /* SetMetadataItem() */
543 : /************************************************************************/
544 :
545 8 : CPLErr OGRPGTableLayer::SetMetadataItem(const char *pszName,
546 : const char *pszValue,
547 : const char *pszDomain)
548 : {
549 8 : LoadMetadata();
550 :
551 8 : if ((pszDomain == nullptr || EQUAL(pszDomain, "")) && pszName != nullptr &&
552 16 : EQUAL(pszName, "DESCRIPTION") && !osForcedDescription.empty())
553 : {
554 2 : pszValue = osForcedDescription;
555 : }
556 :
557 8 : OGRLayer::SetMetadataItem(pszName, pszValue, pszDomain);
558 8 : m_bMetadataModified = true;
559 :
560 8 : if (!bDeferredCreation && (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
561 5 : pszName != nullptr && EQUAL(pszName, "DESCRIPTION"))
562 : {
563 3 : SetMetadata(GetMetadata());
564 : }
565 :
566 8 : return CE_None;
567 : }
568 :
569 : /************************************************************************/
570 : /* SetForcedDescription() */
571 : /************************************************************************/
572 :
573 1 : void OGRPGTableLayer::SetForcedDescription(const char *pszDescriptionIn)
574 : {
575 1 : osForcedDescription = pszDescriptionIn;
576 1 : CPLFree(m_pszTableDescription);
577 1 : m_pszTableDescription = CPLStrdup(pszDescriptionIn);
578 1 : SetMetadataItem("DESCRIPTION", osForcedDescription);
579 1 : }
580 :
581 : /************************************************************************/
582 : /* SetGeometryInformation() */
583 : /************************************************************************/
584 :
585 52 : void OGRPGTableLayer::SetGeometryInformation(PGGeomColumnDesc *pasDesc,
586 : int nGeomFieldCount)
587 : {
588 : // Flag must be set before instantiating geometry fields.
589 52 : bGeometryInformationSet = TRUE;
590 104 : auto oTemporaryUnsealer(poFeatureDefn->GetTemporaryUnsealer(false));
591 :
592 118 : for (int i = 0; i < nGeomFieldCount; i++)
593 : {
594 : auto poGeomFieldDefn =
595 66 : std::make_unique<OGRPGGeomFieldDefn>(this, pasDesc[i].pszName);
596 66 : poGeomFieldDefn->SetNullable(pasDesc[i].bNullable);
597 66 : poGeomFieldDefn->nSRSId = pasDesc[i].nSRID;
598 66 : poGeomFieldDefn->GeometryTypeFlags = pasDesc[i].GeometryTypeFlags;
599 66 : poGeomFieldDefn->ePostgisType = pasDesc[i].ePostgisType;
600 66 : if (pasDesc[i].pszGeomType != nullptr)
601 : {
602 : OGRwkbGeometryType eGeomType =
603 66 : OGRFromOGCGeomType(pasDesc[i].pszGeomType);
604 66 : if ((poGeomFieldDefn->GeometryTypeFlags & OGRGeometry::OGR_G_3D) &&
605 : (eGeomType != wkbUnknown))
606 23 : eGeomType = wkbSetZ(eGeomType);
607 66 : if ((poGeomFieldDefn->GeometryTypeFlags &
608 66 : OGRGeometry::OGR_G_MEASURED) &&
609 : (eGeomType != wkbUnknown))
610 1 : eGeomType = wkbSetM(eGeomType);
611 66 : poGeomFieldDefn->SetType(eGeomType);
612 : }
613 66 : poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
614 : }
615 52 : }
616 :
617 : /************************************************************************/
618 : /* ReadTableDefinition() */
619 : /* */
620 : /* Build a schema from the named table. Done by querying the */
621 : /* catalog. */
622 : /************************************************************************/
623 :
624 255687 : int OGRPGTableLayer::ReadTableDefinition()
625 :
626 : {
627 255687 : PGconn *hPGConn = poDS->GetPGConn();
628 :
629 255687 : if (bTableDefinitionValid >= 0)
630 255281 : return bTableDefinitionValid;
631 406 : bTableDefinitionValid = FALSE;
632 :
633 406 : poDS->EndCopy();
634 :
635 812 : auto oTemporaryUnsealer(poFeatureDefn->GetTemporaryUnsealer());
636 :
637 : /* -------------------------------------------------------------------- */
638 : /* Get the OID of the table. */
639 : /* -------------------------------------------------------------------- */
640 :
641 812 : CPLString osCommand;
642 : osCommand.Printf("SELECT c.oid FROM pg_class c "
643 : "JOIN pg_namespace n ON c.relnamespace=n.oid "
644 : "WHERE c.relname = %s AND n.nspname = %s",
645 812 : OGRPGEscapeString(hPGConn, pszTableName).c_str(),
646 1218 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str());
647 406 : unsigned int nTableOID = 0;
648 : {
649 406 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
650 406 : if (hResult && PGRES_TUPLES_OK == PQresultStatus(hResult))
651 : {
652 406 : if (PQntuples(hResult) == 1 && PQgetisnull(hResult, 0, 0) == false)
653 : {
654 195 : nTableOID = static_cast<unsigned>(
655 195 : CPLAtoGIntBig(PQgetvalue(hResult, 0, 0)));
656 195 : OGRPGClearResult(hResult);
657 : }
658 : else
659 : {
660 211 : CPLDebug("PG", "Could not retrieve table oid for %s",
661 : pszTableName);
662 211 : OGRPGClearResult(hResult);
663 211 : return FALSE;
664 : }
665 : }
666 : else
667 : {
668 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
669 : PQerrorMessage(hPGConn));
670 0 : return FALSE;
671 : }
672 : }
673 :
674 : /* -------------------------------------------------------------------- */
675 : /* Identify the integer primary key. */
676 : /* -------------------------------------------------------------------- */
677 :
678 : osCommand.Printf(
679 : "SELECT a.attname, a.attnum, t.typname, "
680 : "t.typname = ANY(ARRAY['int2','int4','int8','serial','bigserial']) AS "
681 : "isfid "
682 : "FROM pg_attribute a "
683 : "JOIN pg_type t ON t.oid = a.atttypid "
684 : "JOIN pg_index i ON i.indrelid = a.attrelid "
685 : "WHERE a.attnum > 0 AND a.attrelid = %u "
686 : "AND i.indisprimary = 't' "
687 : "AND t.typname !~ '^geom' "
688 : "AND a.attnum = ANY(i.indkey) ORDER BY a.attnum",
689 195 : nTableOID);
690 :
691 195 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
692 :
693 195 : if (hResult && PGRES_TUPLES_OK == PQresultStatus(hResult))
694 : {
695 195 : if (PQntuples(hResult) == 1 && PQgetisnull(hResult, 0, 0) == false)
696 : {
697 : /* Check if single-field PK can be represented as integer. */
698 292 : CPLString osValue(PQgetvalue(hResult, 0, 3));
699 146 : if (osValue == "t")
700 : {
701 146 : osPrimaryKey.Printf("%s", PQgetvalue(hResult, 0, 0));
702 146 : const char *pszFIDType = PQgetvalue(hResult, 0, 2);
703 146 : CPLDebug("PG", "Primary key name (FID): %s, type : %s",
704 : osPrimaryKey.c_str(), pszFIDType);
705 146 : if (EQUAL(pszFIDType, "int8"))
706 2 : OGRLayer::SetMetadataItem(OLMD_FID64, "YES");
707 : }
708 : }
709 49 : else if (PQntuples(hResult) > 1)
710 : {
711 0 : CPLError(CE_Warning, CPLE_AppDefined,
712 : "Multi-column primary key in \'%s\' detected but not "
713 : "supported.",
714 : pszTableName);
715 : }
716 :
717 195 : OGRPGClearResult(hResult);
718 : /* Zero tuples means no PK is defined, perfectly valid case. */
719 : }
720 : else
721 : {
722 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
723 : }
724 :
725 : /* -------------------------------------------------------------------- */
726 : /* Fire off commands to get back the columns of the table. */
727 : /* -------------------------------------------------------------------- */
728 : osCommand.Printf(
729 : "SELECT a.attname, t.typname, a.attlen,"
730 : " format_type(a.atttypid,a.atttypmod), a.attnotnull, def.def, "
731 : "i.indisunique, descr.description%s "
732 : "FROM pg_attribute a "
733 : "JOIN pg_type t ON t.oid = a.atttypid "
734 : "LEFT JOIN "
735 : "(SELECT adrelid, adnum, pg_get_expr(adbin, adrelid) AS def FROM "
736 : "pg_attrdef) def "
737 : "ON def.adrelid = a.attrelid AND def.adnum = a.attnum "
738 : // Find unique constraints that are on a single column only
739 : "LEFT JOIN "
740 : "(SELECT DISTINCT indrelid, indkey, indisunique FROM pg_index WHERE "
741 : "indisunique) i "
742 : "ON i.indrelid = a.attrelid AND i.indkey[0] = a.attnum AND i.indkey[1] "
743 : "IS NULL "
744 : "LEFT JOIN pg_description descr "
745 : "ON descr.objoid = a.attrelid "
746 : "AND descr.classoid = 'pg_class'::regclass::oid "
747 : "AND descr.objsubid = a.attnum "
748 : "WHERE a.attnum > 0 AND a.attrelid = %u "
749 : "ORDER BY a.attnum",
750 195 : (poDS->sPostgreSQLVersion.nMajor >= 12 ? ", a.attgenerated" : ""),
751 195 : nTableOID);
752 :
753 195 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
754 :
755 195 : if (!hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK)
756 : {
757 0 : OGRPGClearResult(hResult);
758 :
759 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
760 0 : return bTableDefinitionValid;
761 : }
762 :
763 195 : if (PQntuples(hResult) == 0)
764 : {
765 0 : OGRPGClearResult(hResult);
766 :
767 0 : CPLDebug("PG", "No field definitions found for '%s', is it a table?",
768 : pszTableName);
769 0 : return bTableDefinitionValid;
770 : }
771 :
772 : /* -------------------------------------------------------------------- */
773 : /* Parse the returned table information. */
774 : /* -------------------------------------------------------------------- */
775 1283 : for (int iRecord = 0; iRecord < PQntuples(hResult); iRecord++)
776 : {
777 1088 : OGRFieldDefn oField(PQgetvalue(hResult, iRecord, 0), OFTString);
778 :
779 1088 : const char *pszType = PQgetvalue(hResult, iRecord, 1);
780 1088 : int nWidth = atoi(PQgetvalue(hResult, iRecord, 2));
781 1088 : const char *pszFormatType = PQgetvalue(hResult, iRecord, 3);
782 1088 : const char *pszNotNull = PQgetvalue(hResult, iRecord, 4);
783 1088 : const char *pszDefault = PQgetisnull(hResult, iRecord, 5)
784 1088 : ? nullptr
785 167 : : PQgetvalue(hResult, iRecord, 5);
786 1088 : const char *pszIsUnique = PQgetvalue(hResult, iRecord, 6);
787 1088 : const char *pszDescription = PQgetvalue(hResult, iRecord, 7);
788 1088 : const char *pszGenerated = poDS->sPostgreSQLVersion.nMajor >= 12
789 1088 : ? PQgetvalue(hResult, iRecord, 8)
790 1088 : : "";
791 :
792 1088 : if (pszNotNull && EQUAL(pszNotNull, "t"))
793 160 : oField.SetNullable(FALSE);
794 1088 : if (pszIsUnique && EQUAL(pszIsUnique, "t"))
795 162 : oField.SetUnique(TRUE);
796 :
797 1088 : if (EQUAL(oField.GetNameRef(), osPrimaryKey))
798 : {
799 166 : pszFIDColumn = CPLStrdup(oField.GetNameRef());
800 166 : CPLDebug("PG", "Using column '%s' as FID for table '%s'",
801 : pszFIDColumn, pszTableName);
802 166 : continue;
803 : }
804 1698 : else if (EQUAL(pszType, "geometry") || EQUAL(pszType, "geography") ||
805 776 : EQUAL(oField.GetNameRef(), "WKB_GEOMETRY"))
806 : {
807 : const auto InitGeomField =
808 436 : [this, &pszType, &oField](OGRPGGeomFieldDefn *poGeomFieldDefn)
809 : {
810 175 : if (EQUAL(pszType, "geometry"))
811 132 : poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOMETRY;
812 43 : else if (EQUAL(pszType, "geography"))
813 : {
814 5 : poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOGRAPHY;
815 5 : if (!(poDS->sPostGISVersion.nMajor >= 3 ||
816 0 : (poDS->sPostGISVersion.nMajor == 2 &&
817 0 : poDS->sPostGISVersion.nMinor >= 2)))
818 : {
819 : // EPSG:4326 was a requirement for geography before
820 : // PostGIS 2.2
821 0 : poGeomFieldDefn->nSRSId = 4326;
822 : }
823 : }
824 : else
825 : {
826 38 : poGeomFieldDefn->ePostgisType = GEOM_TYPE_WKB;
827 38 : if (EQUAL(pszType, "OID"))
828 0 : bWkbAsOid = TRUE;
829 : }
830 175 : poGeomFieldDefn->SetNullable(oField.IsNullable());
831 359 : };
832 :
833 184 : if (!bGeometryInformationSet)
834 : {
835 167 : if (pszGeomColForced == nullptr ||
836 14 : EQUAL(pszGeomColForced, oField.GetNameRef()))
837 : {
838 : auto poGeomFieldDefn = std::make_unique<OGRPGGeomFieldDefn>(
839 144 : this, oField.GetNameRef());
840 144 : InitGeomField(poGeomFieldDefn.get());
841 144 : poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
842 : }
843 : }
844 : else
845 : {
846 31 : int idx = poFeatureDefn->GetGeomFieldIndex(oField.GetNameRef());
847 31 : if (idx >= 0)
848 : {
849 31 : auto poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(idx);
850 31 : InitGeomField(poGeomFieldDefn);
851 : }
852 : }
853 :
854 184 : continue;
855 : }
856 :
857 738 : OGRPGCommonLayerSetType(oField, pszType, pszFormatType, nWidth);
858 :
859 738 : if (pszDefault)
860 : {
861 27 : OGRPGCommonLayerNormalizeDefault(&oField, pszDefault);
862 : }
863 738 : if (pszDescription)
864 738 : oField.SetComment(pszDescription);
865 :
866 : // CPLDebug("PG", "name=%s, type=%s", oField.GetNameRef(), pszType);
867 738 : poFeatureDefn->AddFieldDefn(&oField);
868 1476 : m_abGeneratedColumns.push_back(pszGenerated != nullptr &&
869 738 : pszGenerated[0] != '\0');
870 : }
871 :
872 195 : OGRPGClearResult(hResult);
873 :
874 195 : bTableDefinitionValid = TRUE;
875 :
876 195 : ResetReading();
877 :
878 : /* If geometry type, SRID, etc... have always been set by
879 : * SetGeometryInformation() */
880 : /* no need to issue a new SQL query. Just record the geom type in the layer
881 : * definition */
882 195 : if (bGeometryInformationSet)
883 : {
884 24 : return TRUE;
885 : }
886 171 : bGeometryInformationSet = TRUE;
887 :
888 : // get layer geometry type (for PostGIS dataset)
889 315 : for (int iField = 0; iField < poFeatureDefn->GetGeomFieldCount(); iField++)
890 : {
891 : OGRPGGeomFieldDefn *poGeomFieldDefn =
892 144 : poFeatureDefn->GetGeomFieldDefn(iField);
893 :
894 : /* Get the geometry type and dimensions from the table, or */
895 : /* from its parents if it is a derived table, or from the parent of the
896 : * parent, etc.. */
897 144 : int bGoOn = poDS->m_bHasGeometryColumns;
898 144 : const bool bHasPostGISGeometry =
899 144 : (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY);
900 :
901 291 : while (bGoOn)
902 : {
903 : const CPLString osEscapedThisOrParentTableName(OGRPGEscapeString(
904 147 : hPGConn, (pszSqlGeomParentTableName) ? pszSqlGeomParentTableName
905 294 : : pszTableName));
906 : osCommand.Printf("SELECT type, coord_dimension, srid FROM %s WHERE "
907 : "f_table_name = %s",
908 : (bHasPostGISGeometry) ? "geometry_columns"
909 : : "geography_columns",
910 147 : osEscapedThisOrParentTableName.c_str());
911 :
912 147 : osCommand += CPLString().Printf(
913 : " AND %s=%s",
914 : (bHasPostGISGeometry) ? "f_geometry_column"
915 : : "f_geography_column",
916 294 : OGRPGEscapeString(hPGConn, poGeomFieldDefn->GetNameRef())
917 147 : .c_str());
918 :
919 147 : osCommand += CPLString().Printf(
920 : " AND f_table_schema = %s",
921 147 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str());
922 :
923 147 : hResult = OGRPG_PQexec(hPGConn, osCommand);
924 :
925 253 : if (hResult && PQntuples(hResult) == 1 &&
926 106 : !PQgetisnull(hResult, 0, 0))
927 : {
928 106 : const char *pszType = PQgetvalue(hResult, 0, 0);
929 :
930 106 : int dim = atoi(PQgetvalue(hResult, 0, 1));
931 106 : bool bHasM = pszType[strlen(pszType) - 1] == 'M';
932 106 : int GeometryTypeFlags = 0;
933 106 : if (dim == 3)
934 : {
935 29 : if (bHasM)
936 0 : GeometryTypeFlags |= OGRGeometry::OGR_G_MEASURED;
937 : else
938 29 : GeometryTypeFlags |= OGRGeometry::OGR_G_3D;
939 : }
940 77 : else if (dim == 4)
941 3 : GeometryTypeFlags |=
942 : OGRGeometry::OGR_G_3D | OGRGeometry::OGR_G_MEASURED;
943 :
944 106 : int nSRSId = atoi(PQgetvalue(hResult, 0, 2));
945 :
946 106 : poGeomFieldDefn->GeometryTypeFlags = GeometryTypeFlags;
947 106 : if (nSRSId > 0)
948 18 : poGeomFieldDefn->nSRSId = nSRSId;
949 106 : OGRwkbGeometryType eGeomType = OGRFromOGCGeomType(pszType);
950 106 : if (poGeomFieldDefn->GeometryTypeFlags &
951 32 : OGRGeometry::OGR_G_3D &&
952 : eGeomType != wkbUnknown)
953 8 : eGeomType = wkbSetZ(eGeomType);
954 106 : if (poGeomFieldDefn->GeometryTypeFlags &
955 3 : OGRGeometry::OGR_G_MEASURED &&
956 : eGeomType != wkbUnknown)
957 0 : eGeomType = wkbSetM(eGeomType);
958 106 : poGeomFieldDefn->SetType(eGeomType);
959 :
960 106 : bGoOn = FALSE;
961 : }
962 : else
963 : {
964 : /* Fetch the name of the parent table */
965 : osCommand.Printf(
966 : "SELECT pg_class.relname FROM pg_class WHERE oid = "
967 : "(SELECT pg_inherits.inhparent FROM pg_inherits WHERE "
968 : "inhrelid = "
969 : "(SELECT c.oid FROM pg_class c, pg_namespace n "
970 : "WHERE c.relname = %s AND c.relnamespace=n.oid AND "
971 : "n.nspname = %s))",
972 : osEscapedThisOrParentTableName.c_str(),
973 41 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str());
974 :
975 41 : OGRPGClearResult(hResult);
976 41 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
977 :
978 44 : if (hResult && PQntuples(hResult) == 1 &&
979 3 : !PQgetisnull(hResult, 0, 0))
980 : {
981 3 : CPLFree(pszSqlGeomParentTableName);
982 3 : pszSqlGeomParentTableName =
983 3 : CPLStrdup(PQgetvalue(hResult, 0, 0));
984 : }
985 : else
986 : {
987 : /* No more parent : stop recursion */
988 38 : bGoOn = FALSE;
989 : }
990 : }
991 :
992 147 : OGRPGClearResult(hResult);
993 : }
994 : }
995 :
996 171 : return bTableDefinitionValid;
997 : }
998 :
999 : /************************************************************************/
1000 : /* SetTableDefinition() */
1001 : /************************************************************************/
1002 :
1003 204 : void OGRPGTableLayer::SetTableDefinition(const char *pszFIDColumnName,
1004 : const char *pszGFldName,
1005 : OGRwkbGeometryType eType,
1006 : const char *pszGeomType, int nSRSId,
1007 : int GeometryTypeFlags)
1008 : {
1009 204 : bTableDefinitionValid = TRUE;
1010 204 : bGeometryInformationSet = TRUE;
1011 204 : pszFIDColumn = CPLStrdup(pszFIDColumnName);
1012 408 : auto oTemporaryUnsealer(poFeatureDefn->GetTemporaryUnsealer());
1013 204 : poFeatureDefn->SetGeomType(wkbNone);
1014 204 : if (eType != wkbNone)
1015 : {
1016 : auto poGeomFieldDefn =
1017 177 : std::make_unique<OGRPGGeomFieldDefn>(this, pszGFldName);
1018 177 : poGeomFieldDefn->SetType(eType);
1019 177 : poGeomFieldDefn->GeometryTypeFlags = GeometryTypeFlags;
1020 :
1021 177 : if (EQUAL(pszGeomType, "geometry"))
1022 : {
1023 112 : poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOMETRY;
1024 112 : poGeomFieldDefn->nSRSId = nSRSId;
1025 : }
1026 65 : else if (EQUAL(pszGeomType, "geography"))
1027 : {
1028 4 : poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOGRAPHY;
1029 4 : poGeomFieldDefn->nSRSId = nSRSId;
1030 : }
1031 : else
1032 : {
1033 61 : poGeomFieldDefn->ePostgisType = GEOM_TYPE_WKB;
1034 61 : if (EQUAL(pszGeomType, "OID"))
1035 0 : bWkbAsOid = TRUE;
1036 : }
1037 177 : poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
1038 : }
1039 27 : else if (pszGFldName != nullptr)
1040 : {
1041 2 : m_osFirstGeometryFieldName = pszGFldName;
1042 : }
1043 204 : m_osLCOGeomType = pszGeomType;
1044 204 : }
1045 :
1046 : /************************************************************************/
1047 : /* SetSpatialFilter() */
1048 : /************************************************************************/
1049 :
1050 92 : void OGRPGTableLayer::SetSpatialFilter(int iGeomField, OGRGeometry *poGeomIn)
1051 :
1052 : {
1053 174 : if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ||
1054 82 : GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone)
1055 : {
1056 10 : if (iGeomField != 0)
1057 : {
1058 10 : CPLError(CE_Failure, CPLE_AppDefined,
1059 : "Invalid geometry field index : %d", iGeomField);
1060 : }
1061 10 : return;
1062 : }
1063 82 : m_iGeomFieldFilter = iGeomField;
1064 :
1065 82 : if (InstallFilter(poGeomIn))
1066 : {
1067 41 : BuildWhere();
1068 :
1069 41 : ResetReading();
1070 : }
1071 : }
1072 :
1073 : /************************************************************************/
1074 : /* BuildWhere() */
1075 : /* */
1076 : /* Build the WHERE statement appropriate to the current set of */
1077 : /* criteria (spatial and attribute queries). */
1078 : /************************************************************************/
1079 :
1080 207 : void OGRPGTableLayer::BuildWhere()
1081 :
1082 : {
1083 207 : osWHERE = "";
1084 207 : OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
1085 207 : if (poFeatureDefn->GetGeomFieldCount() != 0)
1086 203 : poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
1087 :
1088 207 : if (m_poFilterGeom != nullptr && poGeomFieldDefn != nullptr &&
1089 53 : poDS->sPostGISVersion.nMajor >= 0 &&
1090 49 : (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ||
1091 18 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY))
1092 : {
1093 : char szBox3D_1[128];
1094 : char szBox3D_2[128];
1095 31 : OGREnvelope sEnvelope;
1096 :
1097 31 : m_poFilterGeom->getEnvelope(&sEnvelope);
1098 31 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)
1099 : {
1100 0 : if (sEnvelope.MinX < -180.0)
1101 0 : sEnvelope.MinX = -180.0;
1102 0 : if (sEnvelope.MinY < -90.0)
1103 0 : sEnvelope.MinY = -90.0;
1104 0 : if (sEnvelope.MaxX > 180.0)
1105 0 : sEnvelope.MaxX = 180.0;
1106 0 : if (sEnvelope.MaxY > 90.0)
1107 0 : sEnvelope.MaxY = 90.0;
1108 : }
1109 31 : CPLsnprintf(szBox3D_1, sizeof(szBox3D_1), "%.18g %.18g", sEnvelope.MinX,
1110 : sEnvelope.MinY);
1111 31 : CPLsnprintf(szBox3D_2, sizeof(szBox3D_2), "%.18g %.18g", sEnvelope.MaxX,
1112 : sEnvelope.MaxY);
1113 : osWHERE.Printf(
1114 : "WHERE %s && ST_SetSRID('BOX3D(%s, %s)'::box3d,%d) ",
1115 62 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(),
1116 62 : szBox3D_1, szBox3D_2, poGeomFieldDefn->nSRSId);
1117 : }
1118 :
1119 207 : if (!osQuery.empty())
1120 : {
1121 89 : if (osWHERE.empty())
1122 : {
1123 82 : osWHERE.Printf("WHERE %s ", osQuery.c_str());
1124 : }
1125 : else
1126 : {
1127 7 : osWHERE += "AND (";
1128 7 : osWHERE += osQuery;
1129 7 : osWHERE += ")";
1130 : }
1131 : }
1132 207 : }
1133 :
1134 : /************************************************************************/
1135 : /* BuildFullQueryStatement() */
1136 : /************************************************************************/
1137 :
1138 946 : void OGRPGTableLayer::BuildFullQueryStatement()
1139 :
1140 : {
1141 946 : CPLString osFields = BuildFields();
1142 946 : if (pszQueryStatement != nullptr)
1143 : {
1144 577 : CPLFree(pszQueryStatement);
1145 577 : pszQueryStatement = nullptr;
1146 : }
1147 1892 : pszQueryStatement = static_cast<char *>(CPLMalloc(
1148 946 : osFields.size() + osWHERE.size() + strlen(pszSqlTableName) + 40));
1149 946 : snprintf(pszQueryStatement,
1150 946 : osFields.size() + osWHERE.size() + strlen(pszSqlTableName) + 40,
1151 : "SELECT %s FROM %s %s", osFields.c_str(), pszSqlTableName,
1152 : osWHERE.c_str());
1153 946 : }
1154 :
1155 : /************************************************************************/
1156 : /* ResetReading() */
1157 : /************************************************************************/
1158 :
1159 974 : void OGRPGTableLayer::ResetReading()
1160 :
1161 : {
1162 974 : if (bInResetReading)
1163 28 : return;
1164 946 : bInResetReading = TRUE;
1165 :
1166 946 : if (bDeferredCreation)
1167 15 : RunDeferredCreationIfNecessary();
1168 946 : poDS->EndCopy();
1169 946 : bUseCopyByDefault = FALSE;
1170 :
1171 946 : BuildFullQueryStatement();
1172 :
1173 946 : OGRPGLayer::ResetReading();
1174 :
1175 946 : bInResetReading = FALSE;
1176 : }
1177 :
1178 : /************************************************************************/
1179 : /* GetNextFeature() */
1180 : /************************************************************************/
1181 :
1182 1192 : OGRFeature *OGRPGTableLayer::GetNextFeature()
1183 :
1184 : {
1185 1192 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
1186 0 : return nullptr;
1187 1192 : poDS->EndCopy();
1188 :
1189 1192 : if (pszQueryStatement == nullptr)
1190 6 : ResetReading();
1191 :
1192 1192 : OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
1193 1192 : if (poFeatureDefn->GetGeomFieldCount() != 0)
1194 1120 : poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
1195 1192 : poFeatureDefn->GetFieldCount();
1196 :
1197 : while (true)
1198 : {
1199 1304 : OGRFeature *poFeature = GetNextRawFeature();
1200 1304 : if (poFeature == nullptr)
1201 120 : return nullptr;
1202 :
1203 : /* We just have to look if there is a geometry filter */
1204 : /* If there's a PostGIS geometry column, the spatial filter */
1205 : /* is already taken into account in the select request */
1206 : /* The attribute filter is always taken into account by the select
1207 : * request */
1208 345 : if (m_poFilterGeom == nullptr || poGeomFieldDefn == nullptr ||
1209 345 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ||
1210 1733 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ||
1211 204 : FilterGeometry(poFeature->GetGeomFieldRef(m_iGeomFieldFilter)))
1212 : {
1213 1072 : if (iFIDAsRegularColumnIndex >= 0)
1214 : {
1215 1 : poFeature->SetField(iFIDAsRegularColumnIndex,
1216 : poFeature->GetFID());
1217 : }
1218 1072 : return poFeature;
1219 : }
1220 :
1221 112 : delete poFeature;
1222 112 : }
1223 : }
1224 :
1225 : /************************************************************************/
1226 : /* BuildFields() */
1227 : /* */
1228 : /* Build list of fields to fetch, performing any required */
1229 : /* transformations (such as on geometry). */
1230 : /************************************************************************/
1231 :
1232 1054 : CPLString OGRPGTableLayer::BuildFields()
1233 :
1234 : {
1235 1054 : int i = 0;
1236 1054 : CPLString osFieldList;
1237 :
1238 1054 : poFeatureDefn->GetFieldCount();
1239 :
1240 2065 : if (pszFIDColumn != nullptr &&
1241 1011 : poFeatureDefn->GetFieldIndex(pszFIDColumn) == -1)
1242 : {
1243 1007 : osFieldList += OGRPGEscapeColumnName(pszFIDColumn);
1244 : }
1245 :
1246 2024 : for (i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
1247 : {
1248 : OGRPGGeomFieldDefn *poGeomFieldDefn =
1249 970 : poFeatureDefn->GetGeomFieldDefn(i);
1250 : CPLString osEscapedGeom =
1251 1940 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
1252 :
1253 970 : if (!osFieldList.empty())
1254 943 : osFieldList += ", ";
1255 :
1256 970 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY)
1257 : {
1258 612 : if (!poDS->HavePostGIS() || poDS->bUseBinaryCursor)
1259 : {
1260 7 : osFieldList += osEscapedGeom;
1261 : }
1262 605 : else if (CPLTestBool(CPLGetConfigOption("PG_USE_BASE64", "NO")))
1263 : {
1264 1 : osFieldList += "encode(ST_AsEWKB(";
1265 1 : osFieldList += osEscapedGeom;
1266 1 : osFieldList += "), 'base64') AS ";
1267 2 : osFieldList += OGRPGEscapeColumnName(
1268 1 : CPLSPrintf("EWKBBase64_%s", poGeomFieldDefn->GetNameRef()));
1269 : }
1270 : else
1271 : {
1272 : /* This will return EWKB in an hex encoded form */
1273 604 : osFieldList += osEscapedGeom;
1274 : }
1275 : }
1276 358 : else if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)
1277 : {
1278 : #if defined(BINARY_CURSOR_ENABLED)
1279 : if (poDS->bUseBinaryCursor)
1280 : {
1281 : osFieldList += "ST_AsBinary(";
1282 : osFieldList += osEscapedGeom;
1283 : osFieldList += ") AS";
1284 : osFieldList += OGRPGEscapeColumnName(
1285 : CPLSPrintf("AsBinary_%s", poGeomFieldDefn->GetNameRef()));
1286 : }
1287 : else
1288 : #endif
1289 15 : if (CPLTestBool(CPLGetConfigOption("PG_USE_BASE64", "NO")))
1290 : {
1291 0 : osFieldList += "encode(ST_AsEWKB(";
1292 0 : osFieldList += osEscapedGeom;
1293 0 : osFieldList += "::geometry), 'base64') AS ";
1294 0 : osFieldList += OGRPGEscapeColumnName(
1295 0 : CPLSPrintf("EWKBBase64_%s", poGeomFieldDefn->GetNameRef()));
1296 : }
1297 : else
1298 : {
1299 15 : osFieldList += osEscapedGeom;
1300 : }
1301 : }
1302 : else
1303 : {
1304 343 : osFieldList += osEscapedGeom;
1305 : }
1306 : }
1307 :
1308 5032 : for (i = 0; i < poFeatureDefn->GetFieldCount(); i++)
1309 : {
1310 3978 : const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
1311 :
1312 3978 : if (!osFieldList.empty())
1313 3958 : osFieldList += ", ";
1314 :
1315 : #if defined(BINARY_CURSOR_ENABLED)
1316 : /* With a binary cursor, it is not possible to get the time zone */
1317 : /* of a timestamptz column. So we fallback to asking it in text mode */
1318 : if (poDS->bUseBinaryCursor &&
1319 : poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDateTime)
1320 : {
1321 : osFieldList += "CAST (";
1322 : osFieldList += OGRPGEscapeColumnName(pszName);
1323 : osFieldList += " AS text)";
1324 : }
1325 : else
1326 : #endif
1327 : {
1328 3978 : osFieldList += OGRPGEscapeColumnName(pszName);
1329 : }
1330 : }
1331 :
1332 1054 : return osFieldList;
1333 : }
1334 :
1335 : /************************************************************************/
1336 : /* SetAttributeFilter() */
1337 : /************************************************************************/
1338 :
1339 166 : OGRErr OGRPGTableLayer::SetAttributeFilter(const char *pszQuery)
1340 :
1341 : {
1342 166 : CPLFree(m_pszAttrQueryString);
1343 166 : m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : nullptr;
1344 :
1345 166 : if (pszQuery == nullptr)
1346 77 : osQuery = "";
1347 : else
1348 89 : osQuery = pszQuery;
1349 :
1350 166 : BuildWhere();
1351 :
1352 166 : ResetReading();
1353 :
1354 166 : return OGRERR_NONE;
1355 : }
1356 :
1357 : /************************************************************************/
1358 : /* DeleteFeature() */
1359 : /************************************************************************/
1360 :
1361 26 : OGRErr OGRPGTableLayer::DeleteFeature(GIntBig nFID)
1362 :
1363 : {
1364 26 : PGconn *hPGConn = poDS->GetPGConn();
1365 52 : CPLString osCommand;
1366 :
1367 26 : GetLayerDefn()->GetFieldCount();
1368 :
1369 26 : if (!bUpdateAccess)
1370 : {
1371 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1372 : "DeleteFeature");
1373 0 : return OGRERR_FAILURE;
1374 : }
1375 :
1376 26 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
1377 0 : return OGRERR_FAILURE;
1378 26 : poDS->EndCopy();
1379 26 : bAutoFIDOnCreateViaCopy = FALSE;
1380 :
1381 : /* -------------------------------------------------------------------- */
1382 : /* We can only delete features if we have a well defined FID */
1383 : /* column to target. */
1384 : /* -------------------------------------------------------------------- */
1385 26 : if (pszFIDColumn == nullptr)
1386 : {
1387 0 : CPLError(CE_Failure, CPLE_AppDefined,
1388 : "DeleteFeature(" CPL_FRMT_GIB
1389 : ") failed. Unable to delete features in tables without\n"
1390 : "a recognised FID column.",
1391 : nFID);
1392 0 : return OGRERR_FAILURE;
1393 : }
1394 :
1395 : /* -------------------------------------------------------------------- */
1396 : /* Form the statement to drop the record. */
1397 : /* -------------------------------------------------------------------- */
1398 : osCommand.Printf("DELETE FROM %s WHERE %s = " CPL_FRMT_GIB, pszSqlTableName,
1399 26 : OGRPGEscapeColumnName(pszFIDColumn).c_str(), nFID);
1400 :
1401 : /* -------------------------------------------------------------------- */
1402 : /* Execute the delete. */
1403 : /* -------------------------------------------------------------------- */
1404 : OGRErr eErr;
1405 :
1406 26 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
1407 :
1408 26 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
1409 : {
1410 0 : CPLError(CE_Failure, CPLE_AppDefined,
1411 : "DeleteFeature() DELETE statement failed.\n%s",
1412 : PQerrorMessage(hPGConn));
1413 :
1414 0 : eErr = OGRERR_FAILURE;
1415 : }
1416 : else
1417 : {
1418 26 : if (EQUAL(PQcmdStatus(hResult), "DELETE 0"))
1419 12 : eErr = OGRERR_NON_EXISTING_FEATURE;
1420 : else
1421 14 : eErr = OGRERR_NONE;
1422 : }
1423 :
1424 26 : OGRPGClearResult(hResult);
1425 :
1426 26 : return eErr;
1427 : }
1428 :
1429 : /************************************************************************/
1430 : /* ISetFeature() */
1431 : /* */
1432 : /* SetFeature() is implemented by an UPDATE SQL command */
1433 : /************************************************************************/
1434 :
1435 2053 : OGRErr OGRPGTableLayer::ISetFeature(OGRFeature *poFeature)
1436 :
1437 : {
1438 2053 : return IUpdateFeature(poFeature, -1, nullptr, -1, nullptr, false);
1439 : }
1440 :
1441 : /************************************************************************/
1442 : /* UpdateFeature() */
1443 : /************************************************************************/
1444 :
1445 2061 : OGRErr OGRPGTableLayer::IUpdateFeature(OGRFeature *poFeature,
1446 : int nUpdatedFieldsCount,
1447 : const int *panUpdatedFieldsIdx,
1448 : int nUpdatedGeomFieldsCount,
1449 : const int *panUpdatedGeomFieldsIdx,
1450 : bool /* bUpdateStyleString*/)
1451 :
1452 : {
1453 2061 : PGconn *hPGConn = poDS->GetPGConn();
1454 4122 : CPLString osCommand;
1455 2061 : bool bNeedComma = false;
1456 2061 : OGRErr eErr = OGRERR_FAILURE;
1457 :
1458 2061 : GetLayerDefn()->GetFieldCount();
1459 :
1460 2061 : const char *pszMethodName =
1461 2061 : nUpdatedFieldsCount >= 0 ? "UpdateFeature" : "SetFeature";
1462 2061 : if (!bUpdateAccess)
1463 : {
1464 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1465 : pszMethodName);
1466 0 : return OGRERR_FAILURE;
1467 : }
1468 :
1469 2061 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
1470 0 : return OGRERR_FAILURE;
1471 2061 : poDS->EndCopy();
1472 :
1473 2061 : if (nullptr == poFeature)
1474 : {
1475 0 : CPLError(CE_Failure, CPLE_AppDefined,
1476 : "NULL pointer to OGRFeature passed to %s().", pszMethodName);
1477 0 : return eErr;
1478 : }
1479 :
1480 2061 : if (poFeature->GetFID() == OGRNullFID)
1481 : {
1482 2 : CPLError(CE_Failure, CPLE_AppDefined,
1483 : "FID required on features given to %s().", pszMethodName);
1484 2 : return eErr;
1485 : }
1486 :
1487 2059 : if (pszFIDColumn == nullptr)
1488 : {
1489 0 : CPLError(CE_Failure, CPLE_AppDefined,
1490 : "Unable to update features in tables without\n"
1491 : "a recognised FID column.");
1492 0 : return eErr;
1493 : }
1494 :
1495 : /* In case the FID column has also been created as a regular field */
1496 2059 : if (iFIDAsRegularColumnIndex >= 0)
1497 : {
1498 5 : if (!poFeature->IsFieldSetAndNotNull(iFIDAsRegularColumnIndex) ||
1499 2 : poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex) !=
1500 2 : poFeature->GetFID())
1501 : {
1502 2 : CPLError(CE_Failure, CPLE_AppDefined,
1503 : "Inconsistent values of FID and field of same name");
1504 2 : return OGRERR_FAILURE;
1505 : }
1506 : }
1507 :
1508 : /* -------------------------------------------------------------------- */
1509 : /* Form the UPDATE command. */
1510 : /* -------------------------------------------------------------------- */
1511 2057 : osCommand.Printf("UPDATE %s SET ", pszSqlTableName);
1512 :
1513 : /* Set the geometry */
1514 : const int nGeomStop = nUpdatedGeomFieldsCount >= 0
1515 2057 : ? nUpdatedGeomFieldsCount
1516 2051 : : poFeatureDefn->GetGeomFieldCount();
1517 4109 : for (int i = 0; i < nGeomStop; i++)
1518 : {
1519 2052 : const int iField =
1520 2052 : nUpdatedGeomFieldsCount >= 0 ? panUpdatedGeomFieldsIdx[i] : i;
1521 : OGRPGGeomFieldDefn *poGeomFieldDefn =
1522 2052 : poFeatureDefn->GetGeomFieldDefn(iField);
1523 2052 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(iField);
1524 2052 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_WKB)
1525 : {
1526 1017 : if (bNeedComma)
1527 0 : osCommand += ", ";
1528 : else
1529 1017 : bNeedComma = true;
1530 :
1531 1017 : osCommand += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
1532 1017 : osCommand += " = ";
1533 1017 : if (poGeom != nullptr)
1534 : {
1535 11 : if (!bWkbAsOid)
1536 : {
1537 : char *pszBytea =
1538 22 : GeometryToBYTEA(poGeom, poDS->sPostGISVersion.nMajor,
1539 11 : poDS->sPostGISVersion.nMinor);
1540 :
1541 11 : if (pszBytea != nullptr)
1542 : {
1543 11 : osCommand += "E'";
1544 11 : osCommand += pszBytea;
1545 11 : osCommand += '\'';
1546 11 : CPLFree(pszBytea);
1547 : }
1548 : else
1549 0 : osCommand += "NULL";
1550 : }
1551 : else
1552 : {
1553 0 : Oid oid = GeometryToOID(poGeom);
1554 :
1555 0 : if (oid != 0)
1556 : {
1557 0 : osCommand += CPLString().Printf("'%d' ", oid);
1558 : }
1559 : else
1560 0 : osCommand += "NULL";
1561 : }
1562 : }
1563 : else
1564 1006 : osCommand += "NULL";
1565 : }
1566 1035 : else if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ||
1567 1034 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY)
1568 : {
1569 1035 : if (bNeedComma)
1570 0 : osCommand += ", ";
1571 : else
1572 1035 : bNeedComma = true;
1573 :
1574 1035 : osCommand += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
1575 1035 : osCommand += " = ";
1576 1035 : if (poGeom != nullptr)
1577 : {
1578 29 : poGeom->closeRings();
1579 29 : poGeom->set3D(poGeomFieldDefn->GeometryTypeFlags &
1580 29 : OGRGeometry::OGR_G_3D);
1581 29 : poGeom->setMeasured(poGeomFieldDefn->GeometryTypeFlags &
1582 29 : OGRGeometry::OGR_G_MEASURED);
1583 : }
1584 :
1585 1035 : if (poGeom != nullptr)
1586 : {
1587 58 : char *pszHexEWKB = OGRGeometryToHexEWKB(
1588 : poGeom, poGeomFieldDefn->nSRSId,
1589 29 : poDS->sPostGISVersion.nMajor, poDS->sPostGISVersion.nMinor);
1590 29 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)
1591 : osCommand +=
1592 1 : CPLString().Printf("'%s'::GEOGRAPHY", pszHexEWKB);
1593 : else
1594 : osCommand +=
1595 28 : CPLString().Printf("'%s'::GEOMETRY", pszHexEWKB);
1596 29 : CPLFree(pszHexEWKB);
1597 : }
1598 : else
1599 1006 : osCommand += "NULL";
1600 : }
1601 : }
1602 :
1603 2057 : const int nStop = nUpdatedFieldsCount >= 0 ? nUpdatedFieldsCount
1604 2051 : : poFeatureDefn->GetFieldCount();
1605 4240 : for (int i = 0; i < nStop; i++)
1606 : {
1607 2183 : const int iField =
1608 2183 : nUpdatedFieldsCount >= 0 ? panUpdatedFieldsIdx[i] : i;
1609 2183 : if (iFIDAsRegularColumnIndex == iField)
1610 1 : continue;
1611 2182 : if (!poFeature->IsFieldSet(iField))
1612 0 : continue;
1613 2182 : if (m_abGeneratedColumns[iField])
1614 2 : continue;
1615 :
1616 2180 : if (bNeedComma)
1617 2175 : osCommand += ", ";
1618 : else
1619 5 : bNeedComma = true;
1620 :
1621 4360 : osCommand += OGRPGEscapeColumnName(
1622 4360 : poFeatureDefn->GetFieldDefn(iField)->GetNameRef());
1623 2180 : osCommand += " = ";
1624 :
1625 2180 : if (poFeature->IsFieldNull(iField))
1626 : {
1627 64 : osCommand += "NULL";
1628 : }
1629 : else
1630 : {
1631 2116 : OGRPGCommonAppendFieldValue(osCommand, poFeature, iField,
1632 : OGRPGEscapeString, hPGConn);
1633 : }
1634 : }
1635 2057 : if (!bNeedComma) // nothing to do
1636 0 : return OGRERR_NONE;
1637 :
1638 : /* Add the WHERE clause */
1639 2057 : osCommand += " WHERE ";
1640 2057 : osCommand += OGRPGEscapeColumnName(pszFIDColumn);
1641 2057 : osCommand += +" = ";
1642 2057 : osCommand += CPLString().Printf(CPL_FRMT_GIB, poFeature->GetFID());
1643 :
1644 : /* -------------------------------------------------------------------- */
1645 : /* Execute the update. */
1646 : /* -------------------------------------------------------------------- */
1647 2057 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
1648 2057 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
1649 : {
1650 0 : CPLError(CE_Failure, CPLE_AppDefined,
1651 : "UPDATE command for feature " CPL_FRMT_GIB
1652 : " failed.\n%s\nCommand: %s",
1653 : poFeature->GetFID(), PQerrorMessage(hPGConn),
1654 : osCommand.c_str());
1655 :
1656 0 : OGRPGClearResult(hResult);
1657 :
1658 0 : return OGRERR_FAILURE;
1659 : }
1660 :
1661 2057 : if (EQUAL(PQcmdStatus(hResult), "UPDATE 0"))
1662 9 : eErr = OGRERR_NON_EXISTING_FEATURE;
1663 : else
1664 2048 : eErr = OGRERR_NONE;
1665 :
1666 2057 : OGRPGClearResult(hResult);
1667 :
1668 2057 : return eErr;
1669 : }
1670 :
1671 : /************************************************************************/
1672 : /* ICreateFeature() */
1673 : /************************************************************************/
1674 :
1675 3851 : OGRErr OGRPGTableLayer::ICreateFeature(OGRFeature *poFeature)
1676 : {
1677 3851 : GetLayerDefn()->GetFieldCount();
1678 :
1679 3851 : if (!bUpdateAccess)
1680 : {
1681 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1682 : "CreateFeature");
1683 0 : return OGRERR_FAILURE;
1684 : }
1685 :
1686 3851 : if (nullptr == poFeature)
1687 : {
1688 0 : CPLError(CE_Failure, CPLE_AppDefined,
1689 : "NULL pointer to OGRFeature passed to CreateFeature().");
1690 0 : return OGRERR_FAILURE;
1691 : }
1692 :
1693 3851 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
1694 0 : return OGRERR_FAILURE;
1695 :
1696 : /* In case the FID column has also been created as a regular field */
1697 3851 : GIntBig nFID = poFeature->GetFID();
1698 3851 : if (iFIDAsRegularColumnIndex >= 0)
1699 : {
1700 4 : if (nFID == OGRNullFID)
1701 : {
1702 3 : if (poFeature->IsFieldSetAndNotNull(iFIDAsRegularColumnIndex))
1703 : {
1704 2 : poFeature->SetFID(
1705 2 : poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex));
1706 : }
1707 : }
1708 : else
1709 : {
1710 2 : if (!poFeature->IsFieldSetAndNotNull(iFIDAsRegularColumnIndex) ||
1711 1 : poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex) !=
1712 : nFID)
1713 : {
1714 1 : CPLError(CE_Failure, CPLE_AppDefined,
1715 : "Inconsistent values of FID and field of same name");
1716 1 : return OGRERR_FAILURE;
1717 : }
1718 : }
1719 : }
1720 :
1721 : /* Auto-promote FID column to 64bit if necessary */
1722 3854 : if (pszFIDColumn != nullptr && !CPL_INT64_FITS_ON_INT32(nFID) &&
1723 4 : OGRLayer::GetMetadataItem(OLMD_FID64) == nullptr)
1724 : {
1725 2 : poDS->EndCopy();
1726 :
1727 2 : CPLString osCommand;
1728 : osCommand.Printf("ALTER TABLE %s ALTER COLUMN %s TYPE INT8",
1729 : pszSqlTableName,
1730 2 : OGRPGEscapeColumnName(pszFIDColumn).c_str());
1731 2 : PGconn *hPGConn = poDS->GetPGConn();
1732 2 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
1733 2 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
1734 : {
1735 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
1736 : PQerrorMessage(hPGConn));
1737 :
1738 0 : OGRPGClearResult(hResult);
1739 :
1740 0 : return OGRERR_FAILURE;
1741 : }
1742 2 : OGRPGClearResult(hResult);
1743 :
1744 2 : OGRLayer::SetMetadataItem(OLMD_FID64, "YES");
1745 : }
1746 :
1747 3850 : if (bFirstInsertion)
1748 : {
1749 181 : bFirstInsertion = FALSE;
1750 181 : if (CPLTestBool(CPLGetConfigOption("OGR_TRUNCATE", "NO")))
1751 : {
1752 1 : PGconn *hPGConn = poDS->GetPGConn();
1753 2 : CPLString osCommand;
1754 :
1755 1 : osCommand.Printf("TRUNCATE TABLE %s", pszSqlTableName);
1756 1 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
1757 1 : OGRPGClearResult(hResult);
1758 : }
1759 : }
1760 :
1761 : // We avoid testing the config option too often.
1762 3850 : if (bUseCopy == USE_COPY_UNSET)
1763 48 : bUseCopy = CPLTestBool(CPLGetConfigOption("PG_USE_COPY", "NO"));
1764 :
1765 : OGRErr eErr;
1766 3850 : if (!bUseCopy)
1767 : {
1768 114 : eErr = CreateFeatureViaInsert(poFeature);
1769 : }
1770 : else
1771 : {
1772 : /* If there's a unset field with a default value, then we must use */
1773 : /* a specific INSERT statement to avoid unset fields to be bound to NULL
1774 : */
1775 3736 : bool bHasDefaultValue = false;
1776 3736 : const int nFieldCount = poFeatureDefn->GetFieldCount();
1777 8960 : for (int iField = 0; iField < nFieldCount; iField++)
1778 : {
1779 6484 : if (!poFeature->IsFieldSet(iField) &&
1780 1259 : poFeature->GetFieldDefnRef(iField)->GetDefault() != nullptr)
1781 : {
1782 1 : bHasDefaultValue = true;
1783 1 : break;
1784 : }
1785 : }
1786 3736 : if (bHasDefaultValue)
1787 : {
1788 1 : eErr = CreateFeatureViaInsert(poFeature);
1789 : }
1790 : else
1791 : {
1792 : bool bFIDSet =
1793 3735 : (pszFIDColumn != nullptr && poFeature->GetFID() != OGRNullFID);
1794 3735 : if (bCopyActive && bFIDSet != bFIDColumnInCopyFields)
1795 : {
1796 1 : eErr = CreateFeatureViaInsert(poFeature);
1797 : }
1798 180 : else if (!bCopyActive && poFeatureDefn->GetFieldCount() == 0 &&
1799 3914 : poFeatureDefn->GetGeomFieldCount() == 0 && !bFIDSet)
1800 : {
1801 4 : eErr = CreateFeatureViaInsert(poFeature);
1802 : }
1803 : else
1804 : {
1805 3730 : if (!bCopyActive)
1806 : {
1807 : /* This is a heuristics. If the first feature to be copied
1808 : * has a */
1809 : /* FID set (and that a FID column has been identified), then
1810 : * we will */
1811 : /* try to copy FID values from features. Otherwise, we will
1812 : * not */
1813 : /* do and assume that the FID column is an autoincremented
1814 : * column. */
1815 176 : bFIDColumnInCopyFields = bFIDSet;
1816 176 : bNeedToUpdateSequence = bFIDSet;
1817 : }
1818 :
1819 3730 : eErr = CreateFeatureViaCopy(poFeature);
1820 3730 : if (bFIDSet)
1821 14 : bAutoFIDOnCreateViaCopy = FALSE;
1822 3730 : if (eErr == OGRERR_NONE && bAutoFIDOnCreateViaCopy)
1823 : {
1824 3695 : poFeature->SetFID(++iNextShapeId);
1825 : }
1826 : }
1827 : }
1828 : }
1829 :
1830 3850 : if (eErr == OGRERR_NONE && iFIDAsRegularColumnIndex >= 0)
1831 : {
1832 3 : poFeature->SetField(iFIDAsRegularColumnIndex, poFeature->GetFID());
1833 : }
1834 :
1835 3850 : return eErr;
1836 : }
1837 :
1838 : /************************************************************************/
1839 : /* OGRPGEscapeColumnName( ) */
1840 : /************************************************************************/
1841 :
1842 16189 : CPLString OGRPGEscapeColumnName(const char *pszColumnName)
1843 : {
1844 16189 : CPLString osStr = "\"";
1845 :
1846 16189 : char ch = '\0';
1847 201234 : for (int i = 0; (ch = pszColumnName[i]) != '\0'; i++)
1848 : {
1849 185045 : if (ch == '"')
1850 0 : osStr.append(1, ch);
1851 185045 : osStr.append(1, ch);
1852 : }
1853 :
1854 16189 : osStr += "\"";
1855 :
1856 16189 : return osStr;
1857 : }
1858 :
1859 : /************************************************************************/
1860 : /* OGRPGEscapeString( ) */
1861 : /************************************************************************/
1862 :
1863 3254 : CPLString OGRPGEscapeString(void *hPGConnIn, const char *pszStrValue,
1864 : int nMaxLength, const char *pszTableName,
1865 : const char *pszFieldName)
1866 : {
1867 3254 : PGconn *hPGConn = reinterpret_cast<PGconn *>(hPGConnIn);
1868 3254 : CPLString osCommand;
1869 :
1870 : /* We need to quote and escape string fields. */
1871 3254 : osCommand += "'";
1872 :
1873 3254 : int nSrcLen = static_cast<int>(strlen(pszStrValue));
1874 3254 : int nSrcLenUTF = CPLStrlenUTF8(pszStrValue);
1875 :
1876 3254 : if (nMaxLength > 0 && nSrcLenUTF > nMaxLength)
1877 : {
1878 0 : CPLDebug("PG", "Truncated %s.%s field value '%s' to %d characters.",
1879 : pszTableName, pszFieldName, pszStrValue, nMaxLength);
1880 :
1881 0 : int iUTF8Char = 0;
1882 0 : for (int iChar = 0; iChar < nSrcLen; iChar++)
1883 : {
1884 0 : if (((reinterpret_cast<const unsigned char *>(pszStrValue))[iChar] &
1885 : 0xc0) != 0x80)
1886 : {
1887 0 : if (iUTF8Char == nMaxLength)
1888 : {
1889 0 : nSrcLen = iChar;
1890 0 : break;
1891 : }
1892 0 : iUTF8Char++;
1893 : }
1894 : }
1895 : }
1896 :
1897 3254 : char *pszDestStr = static_cast<char *>(CPLMalloc(2 * nSrcLen + 1));
1898 :
1899 3254 : int nError = 0;
1900 3254 : PQescapeStringConn(hPGConn, pszDestStr, pszStrValue, nSrcLen, &nError);
1901 3254 : if (nError == 0)
1902 3254 : osCommand += pszDestStr;
1903 : else
1904 0 : CPLError(CE_Warning, CPLE_AppDefined,
1905 : "PQescapeString(): %s\n"
1906 : " input: '%s'\n"
1907 : " got: '%s'\n",
1908 : PQerrorMessage(hPGConn), pszStrValue, pszDestStr);
1909 :
1910 3254 : CPLFree(pszDestStr);
1911 :
1912 3254 : osCommand += "'";
1913 :
1914 6508 : return osCommand;
1915 : }
1916 :
1917 : /************************************************************************/
1918 : /* CreateFeatureViaInsert() */
1919 : /************************************************************************/
1920 :
1921 120 : OGRErr OGRPGTableLayer::CreateFeatureViaInsert(OGRFeature *poFeature)
1922 :
1923 : {
1924 120 : PGconn *hPGConn = poDS->GetPGConn();
1925 240 : CPLString osCommand;
1926 120 : int bNeedComma = FALSE;
1927 :
1928 120 : int bEmptyInsert = FALSE;
1929 :
1930 120 : poDS->EndCopy();
1931 :
1932 : /* -------------------------------------------------------------------- */
1933 : /* Form the INSERT command. */
1934 : /* -------------------------------------------------------------------- */
1935 120 : osCommand.Printf("INSERT INTO %s (", pszSqlTableName);
1936 :
1937 229 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
1938 : {
1939 109 : OGRGeomFieldDefn *poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(i);
1940 109 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
1941 109 : if (poGeom == nullptr)
1942 26 : continue;
1943 83 : if (!bNeedComma)
1944 83 : bNeedComma = TRUE;
1945 : else
1946 0 : osCommand += ", ";
1947 83 : osCommand += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()) + " ";
1948 : }
1949 :
1950 : /* Use case of ogr_pg_60 test */
1951 120 : if (poFeature->GetFID() != OGRNullFID && pszFIDColumn != nullptr)
1952 : {
1953 7 : bNeedToUpdateSequence = true;
1954 :
1955 7 : if (bNeedComma)
1956 5 : osCommand += ", ";
1957 :
1958 7 : osCommand = osCommand + OGRPGEscapeColumnName(pszFIDColumn) + " ";
1959 7 : bNeedComma = TRUE;
1960 : }
1961 : else
1962 : {
1963 113 : UpdateSequenceIfNeeded();
1964 : }
1965 :
1966 120 : const int nFieldCount = poFeatureDefn->GetFieldCount();
1967 556 : for (int i = 0; i < nFieldCount; i++)
1968 : {
1969 436 : if (iFIDAsRegularColumnIndex == i)
1970 1 : continue;
1971 435 : if (!poFeature->IsFieldSet(i))
1972 184 : continue;
1973 251 : if (m_abGeneratedColumns[i])
1974 2 : continue;
1975 :
1976 249 : if (!bNeedComma)
1977 23 : bNeedComma = TRUE;
1978 : else
1979 226 : osCommand += ", ";
1980 :
1981 : osCommand =
1982 249 : osCommand +
1983 498 : OGRPGEscapeColumnName(poFeatureDefn->GetFieldDefn(i)->GetNameRef());
1984 : }
1985 :
1986 120 : if (!bNeedComma)
1987 12 : bEmptyInsert = TRUE;
1988 :
1989 120 : osCommand += ") VALUES (";
1990 :
1991 : /* Set the geometry */
1992 120 : bNeedComma = FALSE;
1993 229 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
1994 : {
1995 : OGRPGGeomFieldDefn *poGeomFieldDefn =
1996 109 : poFeatureDefn->GetGeomFieldDefn(i);
1997 109 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
1998 109 : if (poGeom == nullptr)
1999 26 : continue;
2000 83 : if (bNeedComma)
2001 0 : osCommand += ", ";
2002 : else
2003 83 : bNeedComma = TRUE;
2004 :
2005 83 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ||
2006 83 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY)
2007 : {
2008 54 : CheckGeomTypeCompatibility(i, poGeom);
2009 :
2010 54 : poGeom->closeRings();
2011 54 : poGeom->set3D(poGeomFieldDefn->GeometryTypeFlags &
2012 54 : OGRGeometry::OGR_G_3D);
2013 54 : poGeom->setMeasured(poGeomFieldDefn->GeometryTypeFlags &
2014 54 : OGRGeometry::OGR_G_MEASURED);
2015 :
2016 54 : int nSRSId = poGeomFieldDefn->nSRSId;
2017 :
2018 108 : char *pszHexEWKB = OGRGeometryToHexEWKB(
2019 54 : poGeom, nSRSId, poDS->sPostGISVersion.nMajor,
2020 54 : poDS->sPostGISVersion.nMinor);
2021 54 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)
2022 0 : osCommand += CPLString().Printf("'%s'::GEOGRAPHY", pszHexEWKB);
2023 : else
2024 54 : osCommand += CPLString().Printf("'%s'::GEOMETRY", pszHexEWKB);
2025 54 : CPLFree(pszHexEWKB);
2026 : }
2027 29 : else if (!bWkbAsOid)
2028 : {
2029 : char *pszBytea =
2030 58 : GeometryToBYTEA(poGeom, poDS->sPostGISVersion.nMajor,
2031 29 : poDS->sPostGISVersion.nMinor);
2032 :
2033 29 : if (pszBytea != nullptr)
2034 : {
2035 29 : osCommand += "E'";
2036 29 : osCommand += pszBytea;
2037 29 : osCommand += '\'';
2038 29 : CPLFree(pszBytea);
2039 : }
2040 : else
2041 0 : osCommand += "''";
2042 : }
2043 0 : else if (poGeomFieldDefn->ePostgisType ==
2044 : GEOM_TYPE_WKB /* && bWkbAsOid */)
2045 : {
2046 0 : Oid oid = GeometryToOID(poGeom);
2047 :
2048 0 : if (oid != 0)
2049 : {
2050 0 : osCommand += CPLString().Printf("'%d' ", oid);
2051 : }
2052 : else
2053 0 : osCommand += "''";
2054 : }
2055 : }
2056 :
2057 120 : if (poFeature->GetFID() != OGRNullFID && pszFIDColumn != nullptr)
2058 : {
2059 7 : if (bNeedComma)
2060 5 : osCommand += ", ";
2061 7 : osCommand += CPLString().Printf(CPL_FRMT_GIB " ", poFeature->GetFID());
2062 7 : bNeedComma = TRUE;
2063 : }
2064 :
2065 556 : for (int i = 0; i < nFieldCount; i++)
2066 : {
2067 436 : if (iFIDAsRegularColumnIndex == i)
2068 1 : continue;
2069 435 : if (!poFeature->IsFieldSet(i))
2070 184 : continue;
2071 251 : if (m_abGeneratedColumns[i])
2072 2 : continue;
2073 :
2074 249 : if (bNeedComma)
2075 226 : osCommand += ", ";
2076 : else
2077 23 : bNeedComma = TRUE;
2078 :
2079 249 : OGRPGCommonAppendFieldValue(osCommand, poFeature, i, OGRPGEscapeString,
2080 : hPGConn);
2081 : }
2082 :
2083 120 : osCommand += ")";
2084 :
2085 120 : if (bEmptyInsert)
2086 12 : osCommand.Printf("INSERT INTO %s DEFAULT VALUES", pszSqlTableName);
2087 :
2088 120 : int bReturnRequested = FALSE;
2089 : /* We only get the FID, but we also could add the unset fields to get */
2090 : /* the default values */
2091 236 : if (bRetrieveFID && pszFIDColumn != nullptr &&
2092 116 : poFeature->GetFID() == OGRNullFID)
2093 : {
2094 109 : bReturnRequested = TRUE;
2095 109 : osCommand += " RETURNING ";
2096 109 : osCommand += OGRPGEscapeColumnName(pszFIDColumn);
2097 : }
2098 :
2099 : /* -------------------------------------------------------------------- */
2100 : /* Execute the insert. */
2101 : /* -------------------------------------------------------------------- */
2102 120 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2103 109 : if (bReturnRequested && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
2104 229 : PQntuples(hResult) == 1 && PQnfields(hResult) == 1)
2105 : {
2106 107 : const char *pszFID = PQgetvalue(hResult, 0, 0);
2107 107 : poFeature->SetFID(CPLAtoGIntBig(pszFID));
2108 : }
2109 13 : else if (bReturnRequested || PQresultStatus(hResult) != PGRES_COMMAND_OK)
2110 : {
2111 3 : CPLError(CE_Failure, CPLE_AppDefined,
2112 : "INSERT command for new feature failed.\n%s\nCommand: %s",
2113 : PQerrorMessage(hPGConn), osCommand.c_str());
2114 :
2115 3 : if (!bHasWarnedAlreadySetFID && poFeature->GetFID() != OGRNullFID &&
2116 0 : pszFIDColumn != nullptr)
2117 : {
2118 0 : bHasWarnedAlreadySetFID = TRUE;
2119 0 : CPLError(CE_Warning, CPLE_AppDefined,
2120 : "You've inserted feature with an already set FID and "
2121 : "that's perhaps the reason for the failure. "
2122 : "If so, this can happen if you reuse the same feature "
2123 : "object for sequential insertions. "
2124 : "Indeed, since GDAL 1.8.0, the FID of an inserted feature "
2125 : "is got from the server, so it is not a good idea"
2126 : "to reuse it afterwards... All in all, try unsetting the "
2127 : "FID with SetFID(-1) before calling CreateFeature()");
2128 : }
2129 :
2130 3 : OGRPGClearResult(hResult);
2131 :
2132 3 : return OGRERR_FAILURE;
2133 : }
2134 :
2135 117 : OGRPGClearResult(hResult);
2136 :
2137 117 : return OGRERR_NONE;
2138 : }
2139 :
2140 : /************************************************************************/
2141 : /* CreateFeatureViaCopy() */
2142 : /************************************************************************/
2143 :
2144 3730 : OGRErr OGRPGTableLayer::CreateFeatureViaCopy(OGRFeature *poFeature)
2145 : {
2146 3730 : PGconn *hPGConn = poDS->GetPGConn();
2147 7460 : CPLString osCommand;
2148 :
2149 : /* Tell the datasource we are now planning to copy data */
2150 3730 : poDS->StartCopy(this);
2151 :
2152 : /* First process geometry */
2153 7438 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
2154 : {
2155 : OGRPGGeomFieldDefn *poGeomFieldDefn =
2156 3708 : poFeatureDefn->GetGeomFieldDefn(i);
2157 3708 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
2158 :
2159 3708 : char *pszGeom = nullptr;
2160 3708 : if (nullptr != poGeom)
2161 : {
2162 1648 : CheckGeomTypeCompatibility(i, poGeom);
2163 :
2164 1648 : poGeom->closeRings();
2165 1648 : poGeom->set3D(poGeomFieldDefn->GeometryTypeFlags &
2166 1648 : OGRGeometry::OGR_G_3D);
2167 1648 : poGeom->setMeasured(poGeomFieldDefn->GeometryTypeFlags &
2168 1648 : OGRGeometry::OGR_G_MEASURED);
2169 :
2170 1648 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_WKB)
2171 778 : pszGeom = GeometryToBYTEA(poGeom, poDS->sPostGISVersion.nMajor,
2172 778 : poDS->sPostGISVersion.nMinor);
2173 : else
2174 870 : pszGeom = OGRGeometryToHexEWKB(poGeom, poGeomFieldDefn->nSRSId,
2175 870 : poDS->sPostGISVersion.nMajor,
2176 870 : poDS->sPostGISVersion.nMinor);
2177 : }
2178 :
2179 3708 : if (!osCommand.empty())
2180 12 : osCommand += "\t";
2181 :
2182 3708 : if (pszGeom)
2183 : {
2184 1648 : osCommand += pszGeom;
2185 1648 : CPLFree(pszGeom);
2186 : }
2187 : else
2188 : {
2189 2060 : osCommand += "\\N";
2190 : }
2191 : }
2192 :
2193 7460 : std::vector<bool> abFieldsToInclude(m_abGeneratedColumns.size(), true);
2194 8951 : for (size_t i = 0; i < abFieldsToInclude.size(); i++)
2195 5221 : abFieldsToInclude[i] = !m_abGeneratedColumns[i];
2196 :
2197 3730 : if (bFIDColumnInCopyFields)
2198 : {
2199 14 : OGRPGCommonAppendCopyFID(osCommand, poFeature);
2200 : }
2201 3730 : OGRPGCommonAppendCopyRegularFields(osCommand, poFeature, pszFIDColumn,
2202 : abFieldsToInclude, OGRPGEscapeString,
2203 : hPGConn);
2204 :
2205 : /* Add end of line marker */
2206 3730 : osCommand += "\n";
2207 :
2208 : // PostgreSQL doesn't provide very helpful reporting of invalid UTF-8
2209 : // content in COPY mode.
2210 7460 : if (poDS->IsUTF8ClientEncoding() &&
2211 3730 : !CPLIsUTF8(osCommand.c_str(), static_cast<int>(osCommand.size())))
2212 : {
2213 0 : CPLError(CE_Failure, CPLE_AppDefined,
2214 : "Non UTF-8 content found when writing feature " CPL_FRMT_GIB
2215 : " of layer %s: %s",
2216 0 : poFeature->GetFID(), poFeatureDefn->GetName(),
2217 : osCommand.c_str());
2218 0 : return OGRERR_FAILURE;
2219 : }
2220 :
2221 : /* ------------------------------------------------------------ */
2222 : /* Execute the copy. */
2223 : /* ------------------------------------------------------------ */
2224 :
2225 3730 : OGRErr result = OGRERR_NONE;
2226 :
2227 3730 : int copyResult = PQputCopyData(hPGConn, osCommand.c_str(),
2228 3730 : static_cast<int>(osCommand.size()));
2229 : #ifdef DEBUG_VERBOSE
2230 : CPLDebug("PG", "PQputCopyData(%s)", osCommand.c_str());
2231 : #endif
2232 :
2233 3730 : switch (copyResult)
2234 : {
2235 0 : case 0:
2236 0 : CPLError(CE_Failure, CPLE_AppDefined, "Writing COPY data blocked.");
2237 0 : result = OGRERR_FAILURE;
2238 0 : break;
2239 0 : case -1:
2240 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
2241 : PQerrorMessage(hPGConn));
2242 0 : result = OGRERR_FAILURE;
2243 0 : break;
2244 : }
2245 :
2246 3730 : return result;
2247 : }
2248 :
2249 : /************************************************************************/
2250 : /* TestCapability() */
2251 : /************************************************************************/
2252 :
2253 767 : int OGRPGTableLayer::TestCapability(const char *pszCap)
2254 :
2255 : {
2256 767 : if (bUpdateAccess)
2257 : {
2258 762 : if (EQUAL(pszCap, OLCSequentialWrite) ||
2259 755 : EQUAL(pszCap, OLCCreateField) ||
2260 743 : EQUAL(pszCap, OLCCreateGeomField) ||
2261 742 : EQUAL(pszCap, OLCDeleteField) || EQUAL(pszCap, OLCAlterFieldDefn) ||
2262 718 : EQUAL(pszCap, OLCAlterGeomFieldDefn) || EQUAL(pszCap, OLCRename))
2263 53 : return TRUE;
2264 :
2265 709 : else if (EQUAL(pszCap, OLCRandomWrite) ||
2266 702 : EQUAL(pszCap, OLCUpdateFeature) ||
2267 700 : EQUAL(pszCap, OLCDeleteFeature))
2268 : {
2269 19 : GetLayerDefn()->GetFieldCount();
2270 19 : return pszFIDColumn != nullptr;
2271 : }
2272 : }
2273 :
2274 695 : if (EQUAL(pszCap, OLCRandomRead))
2275 : {
2276 12 : GetLayerDefn()->GetFieldCount();
2277 12 : return pszFIDColumn != nullptr;
2278 : }
2279 :
2280 683 : else if (EQUAL(pszCap, OLCFastFeatureCount) ||
2281 536 : EQUAL(pszCap, OLCFastSetNextByIndex))
2282 : {
2283 178 : if (m_poFilterGeom == nullptr)
2284 152 : return TRUE;
2285 26 : OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
2286 26 : if (poFeatureDefn->GetGeomFieldCount() > 0)
2287 : poGeomFieldDefn =
2288 26 : poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
2289 52 : return poGeomFieldDefn == nullptr ||
2290 26 : (poDS->sPostGISVersion.nMajor >= 0 &&
2291 23 : (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ||
2292 34 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY));
2293 : }
2294 :
2295 505 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
2296 : {
2297 2 : OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
2298 2 : if (poFeatureDefn->GetGeomFieldCount() > 0)
2299 : poGeomFieldDefn =
2300 2 : poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
2301 4 : return poGeomFieldDefn == nullptr ||
2302 2 : (poDS->sPostGISVersion.nMajor >= 0 &&
2303 1 : (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ||
2304 2 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY));
2305 : }
2306 :
2307 503 : else if (EQUAL(pszCap, OLCTransactions))
2308 7 : return TRUE;
2309 :
2310 496 : else if (EQUAL(pszCap, OLCFastGetExtent) ||
2311 461 : EQUAL(pszCap, OLCFastGetExtent3D))
2312 : {
2313 37 : OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
2314 37 : if (poFeatureDefn->GetGeomFieldCount() > 0)
2315 37 : poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(0);
2316 37 : return poGeomFieldDefn != nullptr &&
2317 70 : poDS->sPostGISVersion.nMajor >= 0 &&
2318 70 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY;
2319 : }
2320 :
2321 459 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
2322 7 : return TRUE;
2323 :
2324 452 : else if (EQUAL(pszCap, OLCCurveGeometries))
2325 220 : return TRUE;
2326 :
2327 232 : else if (EQUAL(pszCap, OLCMeasuredGeometries))
2328 199 : return TRUE;
2329 :
2330 33 : else if (EQUAL(pszCap, OLCZGeometries))
2331 15 : return TRUE;
2332 :
2333 : else
2334 18 : return FALSE;
2335 : }
2336 :
2337 : /************************************************************************/
2338 : /* CreateField() */
2339 : /************************************************************************/
2340 :
2341 604 : OGRErr OGRPGTableLayer::CreateField(const OGRFieldDefn *poFieldIn,
2342 : int bApproxOK)
2343 :
2344 : {
2345 604 : PGconn *hPGConn = poDS->GetPGConn();
2346 1208 : CPLString osCommand;
2347 1208 : CPLString osFieldType;
2348 1208 : OGRFieldDefn oField(poFieldIn);
2349 :
2350 604 : GetLayerDefn()->GetFieldCount();
2351 :
2352 604 : if (!bUpdateAccess)
2353 : {
2354 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
2355 : "CreateField");
2356 0 : return OGRERR_FAILURE;
2357 : }
2358 :
2359 606 : if (pszFIDColumn != nullptr && EQUAL(oField.GetNameRef(), pszFIDColumn) &&
2360 1210 : oField.GetType() != OFTInteger && oField.GetType() != OFTInteger64)
2361 : {
2362 1 : CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s",
2363 : oField.GetNameRef());
2364 1 : return OGRERR_FAILURE;
2365 : }
2366 :
2367 : /* -------------------------------------------------------------------- */
2368 : /* Do we want to "launder" the column names into Postgres */
2369 : /* friendly format? */
2370 : /* -------------------------------------------------------------------- */
2371 603 : if (bLaunderColumnNames)
2372 : {
2373 : char *pszSafeName =
2374 601 : OGRPGCommonLaunderName(oField.GetNameRef(), "PG", m_bUTF8ToASCII);
2375 :
2376 601 : oField.SetName(pszSafeName);
2377 601 : CPLFree(pszSafeName);
2378 :
2379 601 : if (EQUAL(oField.GetNameRef(), "oid"))
2380 : {
2381 0 : CPLError(CE_Warning, CPLE_AppDefined,
2382 : "Renaming field 'oid' to 'oid_' to avoid conflict with "
2383 : "internal oid field.");
2384 0 : oField.SetName("oid_");
2385 : }
2386 : }
2387 :
2388 : const char *pszOverrideType =
2389 603 : CSLFetchNameValue(papszOverrideColumnTypes, oField.GetNameRef());
2390 603 : if (pszOverrideType != nullptr)
2391 3 : osFieldType = pszOverrideType;
2392 : else
2393 : {
2394 1200 : osFieldType = OGRPGCommonLayerGetType(
2395 1200 : oField, CPL_TO_BOOL(bPreservePrecision), CPL_TO_BOOL(bApproxOK));
2396 600 : if (osFieldType.empty())
2397 0 : return OGRERR_FAILURE;
2398 : }
2399 :
2400 1206 : CPLString osConstraints;
2401 603 : if (!oField.IsNullable())
2402 1 : osConstraints += " NOT NULL";
2403 603 : if (oField.IsUnique())
2404 4 : osConstraints += " UNIQUE";
2405 603 : if (oField.GetDefault() != nullptr && !oField.IsDefaultDriverSpecific())
2406 : {
2407 8 : osConstraints += " DEFAULT ";
2408 8 : osConstraints += OGRPGCommonLayerGetPGDefault(&oField);
2409 : }
2410 :
2411 1206 : std::string osCommentON;
2412 603 : if (!oField.GetComment().empty())
2413 : {
2414 4 : osCommentON = "COMMENT ON COLUMN ";
2415 4 : osCommentON += pszSqlTableName;
2416 4 : osCommentON += '.';
2417 4 : osCommentON += OGRPGEscapeColumnName(oField.GetNameRef());
2418 4 : osCommentON += " IS ";
2419 4 : osCommentON += OGRPGEscapeString(hPGConn, oField.GetComment().c_str());
2420 : }
2421 :
2422 : /* -------------------------------------------------------------------- */
2423 : /* Create the new field. */
2424 : /* -------------------------------------------------------------------- */
2425 603 : if (bDeferredCreation)
2426 : {
2427 600 : if (!(pszFIDColumn != nullptr &&
2428 300 : EQUAL(pszFIDColumn, oField.GetNameRef())))
2429 : {
2430 299 : osCreateTable += ", ";
2431 299 : osCreateTable += OGRPGEscapeColumnName(oField.GetNameRef());
2432 299 : osCreateTable += " ";
2433 299 : osCreateTable += osFieldType;
2434 299 : osCreateTable += osConstraints;
2435 :
2436 299 : if (!osCommentON.empty())
2437 1 : m_aosDeferredCommentOnColumns.push_back(osCommentON);
2438 : }
2439 : }
2440 : else
2441 : {
2442 303 : poDS->EndCopy();
2443 :
2444 : osCommand.Printf("ALTER TABLE %s ADD COLUMN %s %s", pszSqlTableName,
2445 606 : OGRPGEscapeColumnName(oField.GetNameRef()).c_str(),
2446 606 : osFieldType.c_str());
2447 303 : osCommand += osConstraints;
2448 :
2449 303 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2450 303 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2451 : {
2452 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2453 : PQerrorMessage(hPGConn));
2454 :
2455 0 : OGRPGClearResult(hResult);
2456 :
2457 0 : return OGRERR_FAILURE;
2458 : }
2459 :
2460 303 : OGRPGClearResult(hResult);
2461 :
2462 303 : if (!osCommentON.empty())
2463 : {
2464 3 : hResult = OGRPG_PQexec(hPGConn, osCommentON.c_str());
2465 3 : OGRPGClearResult(hResult);
2466 : }
2467 : }
2468 :
2469 603 : whileUnsealing(poFeatureDefn)->AddFieldDefn(&oField);
2470 603 : m_abGeneratedColumns.resize(poFeatureDefn->GetFieldCount());
2471 :
2472 603 : if (pszFIDColumn != nullptr && EQUAL(oField.GetNameRef(), pszFIDColumn))
2473 : {
2474 1 : iFIDAsRegularColumnIndex = poFeatureDefn->GetFieldCount() - 1;
2475 : }
2476 :
2477 603 : return OGRERR_NONE;
2478 : }
2479 :
2480 : /************************************************************************/
2481 : /* RunAddGeometryColumn() */
2482 : /************************************************************************/
2483 :
2484 : OGRErr
2485 8 : OGRPGTableLayer::RunAddGeometryColumn(const OGRPGGeomFieldDefn *poGeomField)
2486 : {
2487 8 : PGconn *hPGConn = poDS->GetPGConn();
2488 :
2489 8 : const char *pszGeometryType = OGRToOGCGeomType(poGeomField->GetType());
2490 8 : const char *suffix = "";
2491 8 : int dim = 2;
2492 8 : if ((poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_3D) &&
2493 4 : (poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
2494 2 : dim = 4;
2495 6 : else if ((poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
2496 : {
2497 1 : if (!(wkbFlatten(poGeomField->GetType()) == wkbUnknown))
2498 1 : suffix = "M";
2499 1 : dim = 3;
2500 : }
2501 5 : else if (poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_3D)
2502 2 : dim = 3;
2503 :
2504 16 : CPLString osCommand;
2505 : osCommand.Printf(
2506 : "SELECT AddGeometryColumn(%s,%s,%s,%d,'%s%s',%d)",
2507 16 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
2508 16 : OGRPGEscapeString(hPGConn, pszTableName).c_str(),
2509 16 : OGRPGEscapeString(hPGConn, poGeomField->GetNameRef()).c_str(),
2510 32 : poGeomField->nSRSId, pszGeometryType, suffix, dim);
2511 :
2512 8 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
2513 :
2514 8 : if (!hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK)
2515 : {
2516 0 : CPLError(CE_Failure, CPLE_AppDefined,
2517 0 : "AddGeometryColumn failed for layer %s.", GetName());
2518 :
2519 0 : OGRPGClearResult(hResult);
2520 :
2521 0 : return OGRERR_FAILURE;
2522 : }
2523 :
2524 8 : OGRPGClearResult(hResult);
2525 :
2526 8 : if (!poGeomField->IsNullable())
2527 : {
2528 : osCommand.Printf(
2529 : "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL", pszSqlTableName,
2530 0 : OGRPGEscapeColumnName(poGeomField->GetNameRef()).c_str());
2531 :
2532 0 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
2533 0 : OGRPGClearResult(hResult);
2534 : }
2535 :
2536 8 : return OGRERR_NONE;
2537 : }
2538 :
2539 : /************************************************************************/
2540 : /* RunCreateSpatialIndex() */
2541 : /************************************************************************/
2542 :
2543 : OGRErr
2544 132 : OGRPGTableLayer::RunCreateSpatialIndex(const OGRPGGeomFieldDefn *poGeomField,
2545 : int nIdx)
2546 : {
2547 132 : PGconn *hPGConn = poDS->GetPGConn();
2548 264 : CPLString osCommand;
2549 :
2550 264 : std::string osIndexName(pszTableName);
2551 264 : std::string osSuffix("_");
2552 132 : osSuffix += poGeomField->GetNameRef();
2553 132 : osSuffix += "_geom_idx";
2554 132 : if (bLaunderColumnNames)
2555 : {
2556 131 : if (osSuffix.size() >= static_cast<size_t>(OGR_PG_NAMEDATALEN - 1))
2557 : {
2558 1 : osSuffix = "_";
2559 1 : osSuffix += CPLSPrintf("%d", nIdx);
2560 1 : osSuffix += "_geom_idx";
2561 : }
2562 131 : if (osIndexName.size() + osSuffix.size() >
2563 : static_cast<size_t>(OGR_PG_NAMEDATALEN - 1))
2564 2 : osIndexName.resize(OGR_PG_NAMEDATALEN - 1 - osSuffix.size());
2565 : }
2566 132 : osIndexName += osSuffix;
2567 :
2568 : osCommand.Printf("CREATE INDEX %s ON %s USING %s (%s)",
2569 264 : OGRPGEscapeColumnName(osIndexName.c_str()).c_str(),
2570 : pszSqlTableName, osSpatialIndexType.c_str(),
2571 396 : OGRPGEscapeColumnName(poGeomField->GetNameRef()).c_str());
2572 :
2573 132 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
2574 :
2575 132 : if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
2576 : {
2577 0 : CPLError(CE_Failure, CPLE_AppDefined,
2578 0 : "CREATE INDEX failed for layer %s.", GetName());
2579 :
2580 0 : OGRPGClearResult(hResult);
2581 :
2582 0 : return OGRERR_FAILURE;
2583 : }
2584 :
2585 132 : OGRPGClearResult(hResult);
2586 :
2587 132 : return OGRERR_NONE;
2588 : }
2589 :
2590 : /************************************************************************/
2591 : /* CreateGeomField() */
2592 : /************************************************************************/
2593 :
2594 18 : OGRErr OGRPGTableLayer::CreateGeomField(const OGRGeomFieldDefn *poGeomFieldIn,
2595 : CPL_UNUSED int bApproxOK)
2596 : {
2597 18 : OGRwkbGeometryType eType = poGeomFieldIn->GetType();
2598 18 : if (eType == wkbNone)
2599 : {
2600 0 : CPLError(CE_Failure, CPLE_AppDefined,
2601 : "Cannot create geometry field of type wkbNone");
2602 0 : return OGRERR_FAILURE;
2603 : }
2604 :
2605 : // Check if GEOMETRY_NAME layer creation option was set, but no initial
2606 : // column was created in ICreateLayer()
2607 18 : CPLString osGeomFieldName = (m_osFirstGeometryFieldName.size())
2608 2 : ? m_osFirstGeometryFieldName
2609 36 : : CPLString(poGeomFieldIn->GetNameRef());
2610 18 : m_osFirstGeometryFieldName = ""; // reset for potential next geom columns
2611 :
2612 : auto poGeomField =
2613 36 : std::make_unique<OGRPGGeomFieldDefn>(this, osGeomFieldName);
2614 18 : if (EQUAL(poGeomField->GetNameRef(), ""))
2615 : {
2616 0 : if (poFeatureDefn->GetGeomFieldCount() == 0)
2617 0 : poGeomField->SetName(EQUAL(m_osLCOGeomType.c_str(), "geography")
2618 : ? "the_geog"
2619 : : "wkb_geometry");
2620 : else
2621 0 : poGeomField->SetName(CPLSPrintf(
2622 0 : "wkb_geometry%d", poFeatureDefn->GetGeomFieldCount() + 1));
2623 : }
2624 18 : const auto poSRSIn = poGeomFieldIn->GetSpatialRef();
2625 18 : if (poSRSIn)
2626 : {
2627 4 : auto l_poSRS = poSRSIn->Clone();
2628 4 : l_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2629 4 : poGeomField->SetSpatialRef(l_poSRS);
2630 4 : l_poSRS->Release();
2631 : }
2632 : /* -------------------------------------------------------------------- */
2633 : /* Do we want to "launder" the column names into Postgres */
2634 : /* friendly format? */
2635 : /* -------------------------------------------------------------------- */
2636 18 : if (bLaunderColumnNames)
2637 : {
2638 18 : char *pszSafeName = OGRPGCommonLaunderName(poGeomField->GetNameRef(),
2639 18 : "PG", m_bUTF8ToASCII);
2640 :
2641 18 : poGeomField->SetName(pszSafeName);
2642 18 : CPLFree(pszSafeName);
2643 : }
2644 :
2645 18 : const OGRSpatialReference *poSRS = poGeomField->GetSpatialRef();
2646 18 : int nSRSId = poDS->GetUndefinedSRID();
2647 18 : if (nForcedSRSId != UNDETERMINED_SRID)
2648 0 : nSRSId = nForcedSRSId;
2649 18 : else if (poSRS != nullptr)
2650 4 : nSRSId = poDS->FetchSRSId(poSRS);
2651 :
2652 18 : int GeometryTypeFlags = 0;
2653 18 : if (OGR_GT_HasZ(eType))
2654 4 : GeometryTypeFlags |= OGRGeometry::OGR_G_3D;
2655 18 : if (OGR_GT_HasM(eType))
2656 2 : GeometryTypeFlags |= OGRGeometry::OGR_G_MEASURED;
2657 18 : if (nForcedGeometryTypeFlags >= 0)
2658 : {
2659 2 : GeometryTypeFlags = nForcedGeometryTypeFlags;
2660 : eType =
2661 2 : OGR_GT_SetModifier(eType, GeometryTypeFlags & OGRGeometry::OGR_G_3D,
2662 : GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED);
2663 : }
2664 18 : poGeomField->SetType(eType);
2665 18 : poGeomField->SetNullable(poGeomFieldIn->IsNullable());
2666 18 : poGeomField->nSRSId = nSRSId;
2667 18 : poGeomField->GeometryTypeFlags = GeometryTypeFlags;
2668 36 : poGeomField->ePostgisType = EQUAL(m_osLCOGeomType.c_str(), "geography")
2669 18 : ? GEOM_TYPE_GEOGRAPHY
2670 : : GEOM_TYPE_GEOMETRY;
2671 :
2672 : /* -------------------------------------------------------------------- */
2673 : /* Create the new field. */
2674 : /* -------------------------------------------------------------------- */
2675 18 : if (!bDeferredCreation)
2676 : {
2677 8 : poDS->EndCopy();
2678 :
2679 8 : if (RunAddGeometryColumn(poGeomField.get()) != OGRERR_NONE)
2680 : {
2681 0 : return OGRERR_FAILURE;
2682 : }
2683 :
2684 8 : if (bCreateSpatialIndexFlag)
2685 : {
2686 8 : if (RunCreateSpatialIndex(poGeomField.get(), 0) != OGRERR_NONE)
2687 : {
2688 0 : return OGRERR_FAILURE;
2689 : }
2690 : }
2691 : }
2692 :
2693 18 : whileUnsealing(poFeatureDefn)->AddGeomFieldDefn(std::move(poGeomField));
2694 :
2695 18 : return OGRERR_NONE;
2696 : }
2697 :
2698 : /************************************************************************/
2699 : /* DeleteField() */
2700 : /************************************************************************/
2701 :
2702 4 : OGRErr OGRPGTableLayer::DeleteField(int iField)
2703 : {
2704 4 : PGconn *hPGConn = poDS->GetPGConn();
2705 8 : CPLString osCommand;
2706 :
2707 4 : GetLayerDefn()->GetFieldCount();
2708 :
2709 4 : if (!bUpdateAccess)
2710 : {
2711 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
2712 : "DeleteField");
2713 0 : return OGRERR_FAILURE;
2714 : }
2715 :
2716 4 : if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
2717 : {
2718 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
2719 0 : return OGRERR_FAILURE;
2720 : }
2721 :
2722 4 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
2723 0 : return OGRERR_FAILURE;
2724 4 : poDS->EndCopy();
2725 :
2726 : osCommand.Printf(
2727 : "ALTER TABLE %s DROP COLUMN %s", pszSqlTableName,
2728 8 : OGRPGEscapeColumnName(poFeatureDefn->GetFieldDefn(iField)->GetNameRef())
2729 4 : .c_str());
2730 4 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2731 4 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2732 : {
2733 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2734 : PQerrorMessage(hPGConn));
2735 :
2736 0 : OGRPGClearResult(hResult);
2737 :
2738 0 : return OGRERR_FAILURE;
2739 : }
2740 :
2741 4 : OGRPGClearResult(hResult);
2742 :
2743 4 : m_abGeneratedColumns.erase(m_abGeneratedColumns.begin() + iField);
2744 :
2745 4 : return whileUnsealing(poFeatureDefn)->DeleteFieldDefn(iField);
2746 : }
2747 :
2748 : /************************************************************************/
2749 : /* AlterFieldDefn() */
2750 : /************************************************************************/
2751 :
2752 20 : OGRErr OGRPGTableLayer::AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
2753 : int nFlagsIn)
2754 : {
2755 20 : PGconn *hPGConn = poDS->GetPGConn();
2756 40 : CPLString osCommand;
2757 :
2758 20 : GetLayerDefn()->GetFieldCount();
2759 :
2760 20 : if (!bUpdateAccess)
2761 : {
2762 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
2763 : "AlterFieldDefn");
2764 0 : return OGRERR_FAILURE;
2765 : }
2766 :
2767 20 : if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
2768 : {
2769 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
2770 0 : return OGRERR_FAILURE;
2771 : }
2772 :
2773 20 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
2774 0 : return OGRERR_FAILURE;
2775 20 : poDS->EndCopy();
2776 :
2777 20 : OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
2778 40 : auto oTemporaryUnsealer(poFieldDefn->GetTemporaryUnsealer());
2779 40 : OGRFieldDefn oField(poNewFieldDefn);
2780 :
2781 20 : poDS->SoftStartTransaction();
2782 :
2783 20 : if (!(nFlagsIn & ALTER_TYPE_FLAG))
2784 : {
2785 14 : oField.SetSubType(OFSTNone);
2786 14 : oField.SetType(poFieldDefn->GetType());
2787 14 : oField.SetSubType(poFieldDefn->GetSubType());
2788 : }
2789 :
2790 20 : if (!(nFlagsIn & ALTER_WIDTH_PRECISION_FLAG))
2791 : {
2792 14 : oField.SetWidth(poFieldDefn->GetWidth());
2793 14 : oField.SetPrecision(poFieldDefn->GetPrecision());
2794 : }
2795 :
2796 20 : if ((nFlagsIn & ALTER_TYPE_FLAG) || (nFlagsIn & ALTER_WIDTH_PRECISION_FLAG))
2797 : {
2798 : CPLString osFieldType = OGRPGCommonLayerGetType(
2799 6 : oField, CPL_TO_BOOL(bPreservePrecision), true);
2800 6 : if (osFieldType.empty())
2801 : {
2802 0 : poDS->SoftRollbackTransaction();
2803 :
2804 0 : return OGRERR_FAILURE;
2805 : }
2806 :
2807 : osCommand.Printf(
2808 : "ALTER TABLE %s ALTER COLUMN %s TYPE %s", pszSqlTableName,
2809 12 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
2810 12 : osFieldType.c_str());
2811 :
2812 6 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2813 6 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2814 : {
2815 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2816 : PQerrorMessage(hPGConn));
2817 :
2818 0 : OGRPGClearResult(hResult);
2819 :
2820 0 : poDS->SoftRollbackTransaction();
2821 :
2822 0 : return OGRERR_FAILURE;
2823 : }
2824 6 : OGRPGClearResult(hResult);
2825 : }
2826 :
2827 26 : if ((nFlagsIn & ALTER_NULLABLE_FLAG) &&
2828 6 : poFieldDefn->IsNullable() != poNewFieldDefn->IsNullable())
2829 : {
2830 2 : oField.SetNullable(poNewFieldDefn->IsNullable());
2831 :
2832 2 : if (poNewFieldDefn->IsNullable())
2833 : osCommand.Printf(
2834 : "ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL", pszSqlTableName,
2835 1 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
2836 : else
2837 : osCommand.Printf(
2838 : "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL", pszSqlTableName,
2839 1 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
2840 :
2841 2 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2842 2 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2843 : {
2844 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2845 : PQerrorMessage(hPGConn));
2846 :
2847 0 : OGRPGClearResult(hResult);
2848 :
2849 0 : poDS->SoftRollbackTransaction();
2850 :
2851 0 : return OGRERR_FAILURE;
2852 : }
2853 2 : OGRPGClearResult(hResult);
2854 : }
2855 :
2856 : // Only supports adding a unique constraint
2857 30 : if ((nFlagsIn & ALTER_UNIQUE_FLAG) && !poFieldDefn->IsUnique() &&
2858 10 : poNewFieldDefn->IsUnique())
2859 : {
2860 2 : oField.SetUnique(poNewFieldDefn->IsUnique());
2861 :
2862 : osCommand.Printf(
2863 : "ALTER TABLE %s ADD UNIQUE (%s)", pszSqlTableName,
2864 2 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
2865 :
2866 2 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2867 2 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2868 : {
2869 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2870 : PQerrorMessage(hPGConn));
2871 :
2872 0 : OGRPGClearResult(hResult);
2873 :
2874 0 : poDS->SoftRollbackTransaction();
2875 :
2876 0 : return OGRERR_FAILURE;
2877 : }
2878 2 : OGRPGClearResult(hResult);
2879 : }
2880 22 : else if ((nFlagsIn & ALTER_UNIQUE_FLAG) && poFieldDefn->IsUnique() &&
2881 4 : !poNewFieldDefn->IsUnique())
2882 : {
2883 2 : oField.SetUnique(TRUE);
2884 2 : CPLError(CE_Warning, CPLE_NotSupported,
2885 : "Dropping a UNIQUE constraint is not supported currently");
2886 : }
2887 :
2888 28 : if ((nFlagsIn & ALTER_DEFAULT_FLAG) &&
2889 8 : ((poFieldDefn->GetDefault() == nullptr &&
2890 6 : poNewFieldDefn->GetDefault() != nullptr) ||
2891 8 : (poFieldDefn->GetDefault() != nullptr &&
2892 2 : poNewFieldDefn->GetDefault() == nullptr) ||
2893 7 : (poFieldDefn->GetDefault() != nullptr &&
2894 1 : poNewFieldDefn->GetDefault() != nullptr &&
2895 1 : strcmp(poFieldDefn->GetDefault(), poNewFieldDefn->GetDefault()) !=
2896 : 0)))
2897 : {
2898 2 : oField.SetDefault(poNewFieldDefn->GetDefault());
2899 :
2900 2 : if (poNewFieldDefn->GetDefault() == nullptr)
2901 : osCommand.Printf(
2902 : "ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT", pszSqlTableName,
2903 1 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
2904 : else
2905 : osCommand.Printf(
2906 : "ALTER TABLE %s ALTER COLUMN %s SET DEFAULT %s",
2907 : pszSqlTableName,
2908 2 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
2909 3 : OGRPGCommonLayerGetPGDefault(poNewFieldDefn).c_str());
2910 :
2911 2 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2912 2 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2913 : {
2914 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2915 : PQerrorMessage(hPGConn));
2916 :
2917 0 : OGRPGClearResult(hResult);
2918 :
2919 0 : poDS->SoftRollbackTransaction();
2920 :
2921 0 : return OGRERR_FAILURE;
2922 : }
2923 2 : OGRPGClearResult(hResult);
2924 : }
2925 :
2926 30 : if ((nFlagsIn & ALTER_COMMENT_FLAG) &&
2927 10 : poFieldDefn->GetComment() != poNewFieldDefn->GetComment())
2928 : {
2929 6 : oField.SetComment(poNewFieldDefn->GetComment());
2930 :
2931 6 : if (!poNewFieldDefn->GetComment().empty())
2932 : {
2933 : osCommand.Printf(
2934 : "COMMENT ON COLUMN %s.%s IS %s", pszSqlTableName,
2935 8 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
2936 8 : OGRPGEscapeString(hPGConn, poNewFieldDefn->GetComment().c_str())
2937 8 : .c_str());
2938 : }
2939 : else
2940 : {
2941 : osCommand.Printf(
2942 : "COMMENT ON COLUMN %s.%s IS NULL", pszSqlTableName,
2943 2 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
2944 : }
2945 :
2946 6 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2947 6 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2948 : {
2949 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2950 : PQerrorMessage(hPGConn));
2951 :
2952 0 : OGRPGClearResult(hResult);
2953 :
2954 0 : poDS->SoftRollbackTransaction();
2955 :
2956 0 : return OGRERR_FAILURE;
2957 : }
2958 6 : OGRPGClearResult(hResult);
2959 : }
2960 :
2961 20 : if ((nFlagsIn & ALTER_NAME_FLAG))
2962 : {
2963 6 : if (bLaunderColumnNames)
2964 : {
2965 6 : char *pszSafeName = OGRPGCommonLaunderName(oField.GetNameRef(),
2966 6 : "PG", m_bUTF8ToASCII);
2967 6 : oField.SetName(pszSafeName);
2968 6 : CPLFree(pszSafeName);
2969 : }
2970 :
2971 6 : if (EQUAL(oField.GetNameRef(), "oid"))
2972 : {
2973 0 : CPLError(CE_Warning, CPLE_AppDefined,
2974 : "Renaming field 'oid' to 'oid_' to avoid conflict with "
2975 : "internal oid field.");
2976 0 : oField.SetName("oid_");
2977 : }
2978 :
2979 6 : if (strcmp(poFieldDefn->GetNameRef(), oField.GetNameRef()) != 0)
2980 : {
2981 : osCommand.Printf(
2982 : "ALTER TABLE %s RENAME COLUMN %s TO %s", pszSqlTableName,
2983 12 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
2984 18 : OGRPGEscapeColumnName(oField.GetNameRef()).c_str());
2985 6 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2986 6 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2987 : {
2988 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s",
2989 : osCommand.c_str(), PQerrorMessage(hPGConn));
2990 :
2991 0 : OGRPGClearResult(hResult);
2992 :
2993 0 : poDS->SoftRollbackTransaction();
2994 :
2995 0 : return OGRERR_FAILURE;
2996 : }
2997 6 : OGRPGClearResult(hResult);
2998 : }
2999 : }
3000 :
3001 20 : poDS->SoftCommitTransaction();
3002 :
3003 20 : if (nFlagsIn & ALTER_NAME_FLAG)
3004 6 : poFieldDefn->SetName(oField.GetNameRef());
3005 20 : if (nFlagsIn & ALTER_TYPE_FLAG)
3006 : {
3007 6 : poFieldDefn->SetSubType(OFSTNone);
3008 6 : poFieldDefn->SetType(oField.GetType());
3009 6 : poFieldDefn->SetSubType(oField.GetSubType());
3010 : }
3011 20 : if (nFlagsIn & ALTER_WIDTH_PRECISION_FLAG)
3012 : {
3013 6 : poFieldDefn->SetWidth(oField.GetWidth());
3014 6 : poFieldDefn->SetPrecision(oField.GetPrecision());
3015 : }
3016 20 : if (nFlagsIn & ALTER_NULLABLE_FLAG)
3017 6 : poFieldDefn->SetNullable(oField.IsNullable());
3018 20 : if (nFlagsIn & ALTER_DEFAULT_FLAG)
3019 8 : poFieldDefn->SetDefault(oField.GetDefault());
3020 20 : if (nFlagsIn & ALTER_UNIQUE_FLAG)
3021 14 : poFieldDefn->SetUnique(oField.IsUnique());
3022 20 : if (nFlagsIn & ALTER_COMMENT_FLAG)
3023 10 : poFieldDefn->SetComment(oField.GetComment());
3024 :
3025 20 : return OGRERR_NONE;
3026 : }
3027 :
3028 : /************************************************************************/
3029 : /* AlterGeomFieldDefn() */
3030 : /************************************************************************/
3031 :
3032 : OGRErr
3033 6 : OGRPGTableLayer::AlterGeomFieldDefn(int iGeomFieldToAlter,
3034 : const OGRGeomFieldDefn *poNewGeomFieldDefn,
3035 : int nFlagsIn)
3036 : {
3037 6 : PGconn *hPGConn = poDS->GetPGConn();
3038 12 : CPLString osCommand;
3039 :
3040 6 : if (!bUpdateAccess)
3041 : {
3042 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
3043 : "AlterGeomFieldDefn");
3044 0 : return OGRERR_FAILURE;
3045 : }
3046 :
3047 11 : if (iGeomFieldToAlter < 0 ||
3048 5 : iGeomFieldToAlter >= GetLayerDefn()->GetGeomFieldCount())
3049 : {
3050 1 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
3051 1 : return OGRERR_FAILURE;
3052 : }
3053 :
3054 5 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3055 0 : return OGRERR_FAILURE;
3056 5 : poDS->EndCopy();
3057 :
3058 5 : auto poGeomFieldDefn = cpl::down_cast<OGRPGGeomFieldDefn *>(
3059 5 : poFeatureDefn->GetGeomFieldDefn(iGeomFieldToAlter));
3060 10 : auto oTemporaryUnsealer(poGeomFieldDefn->GetTemporaryUnsealer());
3061 :
3062 5 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG)
3063 : {
3064 5 : const auto poNewSRSRef = poNewGeomFieldDefn->GetSpatialRef();
3065 5 : if (poNewSRSRef && poNewSRSRef->GetCoordinateEpoch() > 0)
3066 : {
3067 1 : CPLError(CE_Failure, CPLE_NotSupported,
3068 : "Setting a coordinate epoch is not supported for "
3069 : "PostGIS");
3070 1 : return OGRERR_FAILURE;
3071 : }
3072 : }
3073 :
3074 8 : const OGRGeomFieldDefn oGeomField(poNewGeomFieldDefn);
3075 4 : poDS->SoftStartTransaction();
3076 :
3077 4 : int nGeomTypeFlags = poGeomFieldDefn->GeometryTypeFlags;
3078 :
3079 8 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_TYPE_FLAG) &&
3080 4 : poGeomFieldDefn->GetType() != poNewGeomFieldDefn->GetType())
3081 : {
3082 : const char *pszGeometryType =
3083 2 : OGRToOGCGeomType(poNewGeomFieldDefn->GetType());
3084 2 : std::string osType;
3085 2 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY)
3086 2 : osType += "geometry(";
3087 : else
3088 0 : osType += "geography(";
3089 2 : osType += pszGeometryType;
3090 2 : nGeomTypeFlags = 0;
3091 2 : if (OGR_GT_HasZ(poNewGeomFieldDefn->GetType()))
3092 0 : nGeomTypeFlags |= OGRGeometry::OGR_G_3D;
3093 2 : if (OGR_GT_HasM(poNewGeomFieldDefn->GetType()))
3094 0 : nGeomTypeFlags |= OGRGeometry::OGR_G_MEASURED;
3095 2 : if (nGeomTypeFlags & OGRGeometry::OGR_G_3D)
3096 0 : osType += "Z";
3097 2 : else if (nGeomTypeFlags & OGRGeometry::OGR_G_MEASURED)
3098 0 : osType += "M";
3099 2 : if (poGeomFieldDefn->nSRSId > 0)
3100 2 : osType += CPLSPrintf(",%d", poGeomFieldDefn->nSRSId);
3101 2 : osType += ")";
3102 :
3103 : osCommand.Printf(
3104 : "ALTER TABLE %s ALTER COLUMN %s TYPE %s", pszSqlTableName,
3105 4 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(),
3106 4 : osType.c_str());
3107 :
3108 2 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3109 2 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
3110 : {
3111 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
3112 : PQerrorMessage(hPGConn));
3113 :
3114 1 : OGRPGClearResult(hResult);
3115 :
3116 1 : poDS->SoftRollbackTransaction();
3117 :
3118 1 : return OGRERR_FAILURE;
3119 : }
3120 1 : OGRPGClearResult(hResult);
3121 : }
3122 :
3123 3 : const auto poOldSRS = poGeomFieldDefn->GetSpatialRef();
3124 3 : int nSRID = poGeomFieldDefn->nSRSId;
3125 :
3126 3 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG))
3127 : {
3128 3 : const auto poNewSRS = poNewGeomFieldDefn->GetSpatialRef();
3129 3 : const char *const apszOptions[] = {
3130 : "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES", nullptr};
3131 3 : if ((poOldSRS == nullptr && poNewSRS != nullptr) ||
3132 7 : (poOldSRS != nullptr && poNewSRS == nullptr) ||
3133 1 : (poOldSRS != nullptr && poNewSRS != nullptr &&
3134 1 : !poOldSRS->IsSame(poNewSRS, apszOptions)))
3135 : {
3136 3 : if (poNewSRS)
3137 2 : nSRID = poDS->FetchSRSId(poNewSRS);
3138 : else
3139 1 : nSRID = 0;
3140 :
3141 : osCommand.Printf(
3142 : "SELECT UpdateGeometrySRID(%s,%s,%s,%d)",
3143 6 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
3144 6 : OGRPGEscapeString(hPGConn, pszTableName).c_str(),
3145 6 : OGRPGEscapeString(hPGConn, poGeomFieldDefn->GetNameRef())
3146 : .c_str(),
3147 9 : nSRID);
3148 :
3149 3 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3150 3 : if (PQresultStatus(hResult) != PGRES_TUPLES_OK)
3151 : {
3152 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s",
3153 : osCommand.c_str(), PQerrorMessage(hPGConn));
3154 :
3155 0 : OGRPGClearResult(hResult);
3156 :
3157 0 : poDS->SoftRollbackTransaction();
3158 :
3159 0 : return OGRERR_FAILURE;
3160 : }
3161 3 : OGRPGClearResult(hResult);
3162 : }
3163 : }
3164 :
3165 6 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_NULLABLE_FLAG) &&
3166 3 : poGeomFieldDefn->IsNullable() != poNewGeomFieldDefn->IsNullable())
3167 : {
3168 2 : if (poNewGeomFieldDefn->IsNullable())
3169 : osCommand.Printf(
3170 : "ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL", pszSqlTableName,
3171 1 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str());
3172 : else
3173 : osCommand.Printf(
3174 : "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL", pszSqlTableName,
3175 1 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str());
3176 :
3177 2 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3178 2 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
3179 : {
3180 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
3181 : PQerrorMessage(hPGConn));
3182 :
3183 0 : OGRPGClearResult(hResult);
3184 :
3185 0 : poDS->SoftRollbackTransaction();
3186 :
3187 0 : return OGRERR_FAILURE;
3188 : }
3189 2 : OGRPGClearResult(hResult);
3190 : }
3191 :
3192 6 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG) &&
3193 3 : strcmp(poGeomFieldDefn->GetNameRef(),
3194 : poNewGeomFieldDefn->GetNameRef()) != 0)
3195 : {
3196 : osCommand.Printf(
3197 : "ALTER TABLE %s RENAME COLUMN %s TO %s", pszSqlTableName,
3198 2 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(),
3199 3 : OGRPGEscapeColumnName(oGeomField.GetNameRef()).c_str());
3200 1 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3201 1 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
3202 : {
3203 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
3204 : PQerrorMessage(hPGConn));
3205 :
3206 0 : OGRPGClearResult(hResult);
3207 :
3208 0 : poDS->SoftRollbackTransaction();
3209 :
3210 0 : return OGRERR_FAILURE;
3211 : }
3212 1 : OGRPGClearResult(hResult);
3213 : }
3214 :
3215 3 : poDS->SoftCommitTransaction();
3216 :
3217 3 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG)
3218 3 : poGeomFieldDefn->SetName(oGeomField.GetNameRef());
3219 3 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_TYPE_FLAG)
3220 : {
3221 3 : poGeomFieldDefn->GeometryTypeFlags = nGeomTypeFlags;
3222 3 : poGeomFieldDefn->SetType(oGeomField.GetType());
3223 : }
3224 3 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NULLABLE_FLAG)
3225 3 : poGeomFieldDefn->SetNullable(oGeomField.IsNullable());
3226 3 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG)
3227 : {
3228 3 : const auto poSRSRef = oGeomField.GetSpatialRef();
3229 3 : if (poSRSRef)
3230 : {
3231 2 : auto poSRSNew = poSRSRef->Clone();
3232 2 : poGeomFieldDefn->SetSpatialRef(poSRSNew);
3233 2 : poSRSNew->Release();
3234 : }
3235 : else
3236 : {
3237 1 : poGeomFieldDefn->SetSpatialRef(nullptr);
3238 : }
3239 3 : poGeomFieldDefn->nSRSId = nSRID;
3240 : }
3241 :
3242 3 : return OGRERR_NONE;
3243 : }
3244 :
3245 : /************************************************************************/
3246 : /* GetFeature() */
3247 : /************************************************************************/
3248 :
3249 111 : OGRFeature *OGRPGTableLayer::GetFeature(GIntBig nFeatureId)
3250 :
3251 : {
3252 111 : GetLayerDefn()->GetFieldCount();
3253 :
3254 111 : if (pszFIDColumn == nullptr)
3255 3 : return OGRLayer::GetFeature(nFeatureId);
3256 :
3257 : /* -------------------------------------------------------------------- */
3258 : /* Issue query for a single record. */
3259 : /* -------------------------------------------------------------------- */
3260 108 : OGRFeature *poFeature = nullptr;
3261 108 : PGconn *hPGConn = poDS->GetPGConn();
3262 216 : CPLString osFieldList = BuildFields();
3263 108 : CPLString osCommand;
3264 :
3265 108 : poDS->EndCopy();
3266 108 : poDS->SoftStartTransaction();
3267 :
3268 : osCommand.Printf("DECLARE getfeaturecursor %s for "
3269 : "SELECT %s FROM %s WHERE %s = " CPL_FRMT_GIB,
3270 108 : (poDS->bUseBinaryCursor) ? "BINARY CURSOR" : "CURSOR",
3271 : osFieldList.c_str(), pszSqlTableName,
3272 108 : OGRPGEscapeColumnName(pszFIDColumn).c_str(), nFeatureId);
3273 :
3274 108 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
3275 :
3276 108 : if (hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK)
3277 : {
3278 108 : OGRPGClearResult(hResult);
3279 :
3280 108 : hResult = OGRPG_PQexec(hPGConn, "FETCH ALL in getfeaturecursor");
3281 :
3282 108 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK)
3283 : {
3284 108 : int nRows = PQntuples(hResult);
3285 108 : if (nRows > 0)
3286 : {
3287 86 : int *panTempMapFieldNameToIndex = nullptr;
3288 86 : int *panTempMapFieldNameToGeomIndex = nullptr;
3289 86 : CreateMapFromFieldNameToIndex(hResult, poFeatureDefn,
3290 : panTempMapFieldNameToIndex,
3291 : panTempMapFieldNameToGeomIndex);
3292 86 : poFeature = RecordToFeature(hResult, panTempMapFieldNameToIndex,
3293 : panTempMapFieldNameToGeomIndex, 0);
3294 86 : CPLFree(panTempMapFieldNameToIndex);
3295 86 : CPLFree(panTempMapFieldNameToGeomIndex);
3296 86 : if (poFeature && iFIDAsRegularColumnIndex >= 0)
3297 : {
3298 1 : poFeature->SetField(iFIDAsRegularColumnIndex,
3299 : poFeature->GetFID());
3300 : }
3301 :
3302 86 : if (nRows > 1)
3303 : {
3304 0 : CPLError(
3305 : CE_Warning, CPLE_AppDefined,
3306 : "%d rows in response to the WHERE %s = " CPL_FRMT_GIB
3307 : " clause !",
3308 : nRows, pszFIDColumn, nFeatureId);
3309 : }
3310 : }
3311 : else
3312 : {
3313 22 : CPLError(CE_Failure, CPLE_AppDefined,
3314 : "Attempt to read feature with unknown feature id "
3315 : "(" CPL_FRMT_GIB ").",
3316 : nFeatureId);
3317 : }
3318 : }
3319 : }
3320 0 : else if (hResult && PQresultStatus(hResult) == PGRES_FATAL_ERROR)
3321 : {
3322 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
3323 : PQresultErrorMessage(hResult));
3324 : }
3325 :
3326 : /* -------------------------------------------------------------------- */
3327 : /* Cleanup */
3328 : /* -------------------------------------------------------------------- */
3329 108 : OGRPGClearResult(hResult);
3330 :
3331 108 : hResult = OGRPG_PQexec(hPGConn, "CLOSE getfeaturecursor");
3332 108 : OGRPGClearResult(hResult);
3333 :
3334 108 : poDS->SoftCommitTransaction();
3335 :
3336 108 : return poFeature;
3337 : }
3338 :
3339 : /************************************************************************/
3340 : /* GetFeatureCount() */
3341 : /************************************************************************/
3342 :
3343 145 : GIntBig OGRPGTableLayer::GetFeatureCount(int bForce)
3344 :
3345 : {
3346 145 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3347 0 : return 0;
3348 145 : poDS->EndCopy();
3349 :
3350 145 : if (TestCapability(OLCFastFeatureCount) == FALSE)
3351 11 : return OGRPGLayer::GetFeatureCount(bForce);
3352 :
3353 : /* -------------------------------------------------------------------- */
3354 : /* In theory it might be wise to cache this result, but it */
3355 : /* won't be trivial to work out the lifetime of the value. */
3356 : /* After all someone else could be adding records from another */
3357 : /* application when working against a database. */
3358 : /* -------------------------------------------------------------------- */
3359 134 : PGconn *hPGConn = poDS->GetPGConn();
3360 134 : CPLString osCommand;
3361 134 : GIntBig nCount = 0;
3362 :
3363 : osCommand.Printf("SELECT count(*) FROM %s %s", pszSqlTableName,
3364 134 : osWHERE.c_str());
3365 :
3366 134 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3367 134 : if (hResult != nullptr && PQresultStatus(hResult) == PGRES_TUPLES_OK)
3368 134 : nCount = CPLAtoGIntBig(PQgetvalue(hResult, 0, 0));
3369 : else
3370 0 : CPLDebug("PG", "%s; failed.", osCommand.c_str());
3371 134 : OGRPGClearResult(hResult);
3372 :
3373 134 : return nCount;
3374 : }
3375 :
3376 : /************************************************************************/
3377 : /* ResolveSRID() */
3378 : /************************************************************************/
3379 :
3380 80 : void OGRPGTableLayer::ResolveSRID(const OGRPGGeomFieldDefn *poGFldDefn)
3381 :
3382 : {
3383 80 : PGconn *hPGConn = poDS->GetPGConn();
3384 80 : CPLString osCommand;
3385 :
3386 80 : int nSRSId = poDS->GetUndefinedSRID();
3387 80 : if (!poDS->m_bHasGeometryColumns)
3388 : {
3389 0 : poGFldDefn->nSRSId = nSRSId;
3390 0 : return;
3391 : }
3392 :
3393 : osCommand.Printf(
3394 : "SELECT srid FROM geometry_columns "
3395 : "WHERE f_table_name = %s AND "
3396 : "f_geometry_column = %s",
3397 160 : OGRPGEscapeString(hPGConn, pszTableName).c_str(),
3398 240 : OGRPGEscapeString(hPGConn, poGFldDefn->GetNameRef()).c_str());
3399 :
3400 : osCommand +=
3401 80 : CPLString().Printf(" AND f_table_schema = %s",
3402 80 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str());
3403 :
3404 80 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
3405 :
3406 160 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
3407 80 : PQntuples(hResult) == 1)
3408 : {
3409 39 : nSRSId = atoi(PQgetvalue(hResult, 0, 0));
3410 : }
3411 :
3412 80 : OGRPGClearResult(hResult);
3413 :
3414 : /* With PostGIS 2.0, SRID = 0 can also mean that there's no constraint */
3415 : /* so we need to fetch from values */
3416 : /* We assume that all geometry of this column have identical SRID */
3417 80 : if (nSRSId <= 0 && poGFldDefn->ePostgisType == GEOM_TYPE_GEOMETRY &&
3418 39 : poDS->sPostGISVersion.nMajor >= 0)
3419 : {
3420 72 : CPLString osGetSRID;
3421 36 : osGetSRID += "SELECT ST_SRID(";
3422 36 : osGetSRID += OGRPGEscapeColumnName(poGFldDefn->GetNameRef());
3423 36 : osGetSRID += ") FROM ";
3424 36 : osGetSRID += pszSqlTableName;
3425 36 : osGetSRID += " WHERE (";
3426 36 : osGetSRID += OGRPGEscapeColumnName(poGFldDefn->GetNameRef());
3427 36 : osGetSRID += " IS NOT NULL) LIMIT 1";
3428 :
3429 36 : hResult = OGRPG_PQexec(poDS->GetPGConn(), osGetSRID);
3430 72 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
3431 36 : PQntuples(hResult) == 1)
3432 : {
3433 34 : nSRSId = atoi(PQgetvalue(hResult, 0, 0));
3434 : }
3435 :
3436 36 : OGRPGClearResult(hResult);
3437 : }
3438 :
3439 80 : poGFldDefn->nSRSId = nSRSId;
3440 : }
3441 :
3442 : /************************************************************************/
3443 : /* StartCopy() */
3444 : /************************************************************************/
3445 :
3446 176 : OGRErr OGRPGTableLayer::StartCopy()
3447 :
3448 : {
3449 : /*CPLDebug("PG", "OGRPGDataSource(%p)::StartCopy(%p)", poDS, this);*/
3450 :
3451 176 : CPLString osFields = BuildCopyFields();
3452 :
3453 176 : size_t size = osFields.size() + strlen(pszSqlTableName) + 100;
3454 176 : char *pszCommand = static_cast<char *>(CPLMalloc(size));
3455 :
3456 176 : snprintf(pszCommand, size, "COPY %s (%s) FROM STDIN;", pszSqlTableName,
3457 : osFields.c_str());
3458 :
3459 176 : PGconn *hPGConn = poDS->GetPGConn();
3460 176 : PGresult *hResult = OGRPG_PQexec(hPGConn, pszCommand);
3461 :
3462 176 : if (!hResult || (PQresultStatus(hResult) != PGRES_COPY_IN))
3463 : {
3464 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
3465 : }
3466 : else
3467 176 : bCopyActive = TRUE;
3468 :
3469 176 : OGRPGClearResult(hResult);
3470 176 : CPLFree(pszCommand);
3471 :
3472 352 : return OGRERR_NONE;
3473 : }
3474 :
3475 : /************************************************************************/
3476 : /* EndCopy() */
3477 : /************************************************************************/
3478 :
3479 176 : OGRErr OGRPGTableLayer::EndCopy()
3480 :
3481 : {
3482 176 : if (!bCopyActive)
3483 0 : return OGRERR_NONE;
3484 : /*CPLDebug("PG", "OGRPGDataSource(%p)::EndCopy(%p)", poDS, this);*/
3485 :
3486 : /* This method is called from the datasource when
3487 : a COPY operation is ended */
3488 176 : OGRErr result = OGRERR_NONE;
3489 :
3490 176 : PGconn *hPGConn = poDS->GetPGConn();
3491 176 : CPLDebug("PG", "PQputCopyEnd()");
3492 :
3493 176 : bCopyActive = FALSE;
3494 :
3495 176 : int copyResult = PQputCopyEnd(hPGConn, nullptr);
3496 :
3497 176 : switch (copyResult)
3498 : {
3499 0 : case 0:
3500 0 : CPLError(CE_Failure, CPLE_AppDefined, "Writing COPY data blocked.");
3501 0 : result = OGRERR_FAILURE;
3502 0 : break;
3503 0 : case -1:
3504 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
3505 : PQerrorMessage(hPGConn));
3506 0 : result = OGRERR_FAILURE;
3507 0 : break;
3508 : }
3509 :
3510 : /* Now check the results of the copy */
3511 176 : PGresult *hResult = PQgetResult(hPGConn);
3512 :
3513 176 : if (hResult && PQresultStatus(hResult) != PGRES_COMMAND_OK)
3514 : {
3515 1 : CPLError(CE_Failure, CPLE_AppDefined, "COPY statement failed.\n%s",
3516 : PQerrorMessage(hPGConn));
3517 :
3518 1 : result = OGRERR_FAILURE;
3519 : }
3520 :
3521 176 : OGRPGClearResult(hResult);
3522 :
3523 176 : if (!bUseCopyByDefault)
3524 34 : bUseCopy = USE_COPY_UNSET;
3525 :
3526 176 : UpdateSequenceIfNeeded();
3527 :
3528 176 : return result;
3529 : }
3530 :
3531 : /************************************************************************/
3532 : /* UpdateSequenceIfNeeded() */
3533 : /************************************************************************/
3534 :
3535 945 : void OGRPGTableLayer::UpdateSequenceIfNeeded()
3536 : {
3537 945 : if (bNeedToUpdateSequence && pszFIDColumn != nullptr)
3538 : {
3539 18 : PGconn *hPGConn = poDS->GetPGConn();
3540 18 : CPLString osCommand;
3541 : // setval() only works if the value is in [1,INT_MAX] range
3542 : // so do not update it if MAX(fid) <= 0
3543 : osCommand.Printf(
3544 : "SELECT setval(pg_get_serial_sequence(%s, %s), MAX(%s)) FROM %s "
3545 : "WHERE EXISTS (SELECT 1 FROM %s WHERE %s > 0 LIMIT 1)",
3546 36 : OGRPGEscapeString(hPGConn, pszSqlTableName).c_str(),
3547 36 : OGRPGEscapeString(hPGConn, pszFIDColumn).c_str(),
3548 18 : OGRPGEscapeColumnName(pszFIDColumn).c_str(), pszSqlTableName,
3549 72 : pszSqlTableName, OGRPGEscapeColumnName(pszFIDColumn).c_str());
3550 18 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3551 18 : OGRPGClearResult(hResult);
3552 18 : bNeedToUpdateSequence = false;
3553 : }
3554 945 : }
3555 :
3556 : /************************************************************************/
3557 : /* BuildCopyFields() */
3558 : /************************************************************************/
3559 :
3560 176 : CPLString OGRPGTableLayer::BuildCopyFields()
3561 : {
3562 176 : int i = 0;
3563 176 : int nFIDIndex = -1;
3564 176 : CPLString osFieldList;
3565 :
3566 338 : for (i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
3567 : {
3568 162 : OGRGeomFieldDefn *poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(i);
3569 162 : if (!osFieldList.empty())
3570 6 : osFieldList += ", ";
3571 162 : osFieldList += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
3572 : }
3573 :
3574 176 : if (bFIDColumnInCopyFields)
3575 : {
3576 11 : if (!osFieldList.empty())
3577 7 : osFieldList += ", ";
3578 :
3579 11 : nFIDIndex = poFeatureDefn->GetFieldIndex(pszFIDColumn);
3580 :
3581 11 : osFieldList += OGRPGEscapeColumnName(pszFIDColumn);
3582 : }
3583 :
3584 696 : for (i = 0; i < poFeatureDefn->GetFieldCount(); i++)
3585 : {
3586 520 : if (i == nFIDIndex)
3587 2 : continue;
3588 518 : if (m_abGeneratedColumns[i])
3589 2 : continue;
3590 :
3591 516 : const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
3592 :
3593 516 : if (!osFieldList.empty())
3594 500 : osFieldList += ", ";
3595 :
3596 516 : osFieldList += OGRPGEscapeColumnName(pszName);
3597 : }
3598 :
3599 176 : return osFieldList;
3600 : }
3601 :
3602 : /************************************************************************/
3603 : /* CheckGeomTypeCompatibility() */
3604 : /************************************************************************/
3605 :
3606 1702 : void OGRPGTableLayer::CheckGeomTypeCompatibility(int iGeomField,
3607 : OGRGeometry *poGeom)
3608 : {
3609 1702 : if (bHasWarnedIncompatibleGeom)
3610 0 : return;
3611 :
3612 : OGRwkbGeometryType eExpectedGeomType =
3613 1702 : poFeatureDefn->GetGeomFieldDefn(iGeomField)->GetType();
3614 1702 : OGRwkbGeometryType eFlatLayerGeomType = wkbFlatten(eExpectedGeomType);
3615 1702 : OGRwkbGeometryType eFlatGeomType = wkbFlatten(poGeom->getGeometryType());
3616 1702 : if (eFlatLayerGeomType == wkbUnknown)
3617 676 : return;
3618 :
3619 1026 : if (eFlatLayerGeomType == wkbGeometryCollection)
3620 0 : bHasWarnedIncompatibleGeom = eFlatGeomType != wkbMultiPoint &&
3621 0 : eFlatGeomType != wkbMultiLineString &&
3622 0 : eFlatGeomType != wkbMultiPolygon &&
3623 : eFlatGeomType != wkbGeometryCollection;
3624 : else
3625 1026 : bHasWarnedIncompatibleGeom = (eFlatGeomType != eFlatLayerGeomType);
3626 :
3627 1026 : if (bHasWarnedIncompatibleGeom)
3628 : {
3629 2 : CPLError(CE_Warning, CPLE_AppDefined,
3630 : "Geometry to be inserted is of type %s, whereas the layer "
3631 : "geometry type is %s.\n"
3632 : "Insertion is likely to fail",
3633 1 : OGRGeometryTypeToName(poGeom->getGeometryType()),
3634 : OGRGeometryTypeToName(eExpectedGeomType));
3635 : }
3636 : }
3637 :
3638 : /************************************************************************/
3639 : /* SetOverrideColumnTypes() */
3640 : /************************************************************************/
3641 :
3642 204 : void OGRPGTableLayer::SetOverrideColumnTypes(const char *pszOverrideColumnTypes)
3643 : {
3644 204 : if (pszOverrideColumnTypes == nullptr)
3645 203 : return;
3646 :
3647 1 : const char *pszIter = pszOverrideColumnTypes;
3648 2 : CPLString osCur;
3649 44 : while (*pszIter != '\0')
3650 : {
3651 44 : if (*pszIter == '(')
3652 : {
3653 : /* Ignore commas inside ( ) pair */
3654 12 : while (*pszIter != '\0')
3655 : {
3656 12 : if (*pszIter == ')')
3657 : {
3658 2 : osCur += *pszIter;
3659 2 : pszIter++;
3660 2 : break;
3661 : }
3662 10 : osCur += *pszIter;
3663 10 : pszIter++;
3664 : }
3665 2 : if (*pszIter == '\0')
3666 1 : break;
3667 : }
3668 :
3669 43 : if (*pszIter == ',')
3670 : {
3671 3 : papszOverrideColumnTypes =
3672 3 : CSLAddString(papszOverrideColumnTypes, osCur);
3673 3 : osCur = "";
3674 : }
3675 : else
3676 40 : osCur += *pszIter;
3677 43 : pszIter++;
3678 : }
3679 1 : if (!osCur.empty())
3680 1 : papszOverrideColumnTypes =
3681 1 : CSLAddString(papszOverrideColumnTypes, osCur);
3682 : }
3683 :
3684 : /************************************************************************/
3685 : /* GetExtent() */
3686 : /* */
3687 : /* For PostGIS use internal ST_EstimatedExtent(geometry) function */
3688 : /* if bForce == 0 */
3689 : /************************************************************************/
3690 :
3691 32 : OGRErr OGRPGTableLayer::GetExtent(int iGeomField, OGREnvelope *psExtent,
3692 : int bForce)
3693 : {
3694 64 : CPLString osCommand;
3695 :
3696 54 : if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ||
3697 22 : GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone)
3698 : {
3699 10 : if (iGeomField != 0)
3700 : {
3701 10 : CPLError(CE_Failure, CPLE_AppDefined,
3702 : "Invalid geometry field index : %d", iGeomField);
3703 : }
3704 10 : return OGRERR_FAILURE;
3705 : }
3706 :
3707 22 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3708 0 : return OGRERR_FAILURE;
3709 22 : poDS->EndCopy();
3710 :
3711 : OGRPGGeomFieldDefn *poGeomFieldDefn =
3712 22 : poFeatureDefn->GetGeomFieldDefn(iGeomField);
3713 :
3714 : // if bForce is 0 and ePostgisType is not GEOM_TYPE_GEOGRAPHY we can use
3715 : // the ST_EstimatedExtent function which is quicker
3716 : // ST_EstimatedExtent was called ST_Estimated_Extent up to PostGIS 2.0.x
3717 : // ST_EstimatedExtent returns NULL in absence of statistics (an exception
3718 : // before
3719 : // PostGIS 1.5.4)
3720 22 : if (bForce == 0 && TestCapability(OLCFastGetExtent))
3721 : {
3722 1 : PGconn *hPGConn = poDS->GetPGConn();
3723 :
3724 2 : const char *pszExtentFct = poDS->sPostGISVersion.nMajor > 2 ||
3725 0 : (poDS->sPostGISVersion.nMajor == 2 &&
3726 0 : poDS->sPostGISVersion.nMinor >= 1)
3727 1 : ? "ST_EstimatedExtent"
3728 : : "ST_Estimated_Extent";
3729 :
3730 : osCommand.Printf(
3731 : "SELECT %s(%s, %s, %s)", pszExtentFct,
3732 2 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
3733 2 : OGRPGEscapeString(hPGConn, pszTableName).c_str(),
3734 4 : OGRPGEscapeString(hPGConn, poGeomFieldDefn->GetNameRef()).c_str());
3735 :
3736 : /* Quiet error: ST_Estimated_Extent may return an error if statistics */
3737 : /* have not been computed */
3738 1 : if (RunGetExtentRequest(*psExtent, bForce, osCommand, TRUE) ==
3739 : OGRERR_NONE)
3740 1 : return OGRERR_NONE;
3741 :
3742 0 : CPLDebug(
3743 : "PG",
3744 : "Unable to get estimated extent by PostGIS. Trying real extent.");
3745 : }
3746 :
3747 21 : return OGRPGLayer::GetExtent(iGeomField, psExtent, bForce);
3748 : }
3749 :
3750 : /************************************************************************/
3751 : /* Rename() */
3752 : /************************************************************************/
3753 :
3754 6 : OGRErr OGRPGTableLayer::Rename(const char *pszNewName)
3755 : {
3756 6 : if (!TestCapability(OLCRename))
3757 0 : return OGRERR_FAILURE;
3758 :
3759 6 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3760 0 : return OGRERR_FAILURE;
3761 6 : poDS->EndCopy();
3762 6 : ResetReading();
3763 :
3764 6 : char *pszNewSqlTableName = CPLStrdup(OGRPGEscapeColumnName(pszNewName));
3765 6 : PGconn *hPGConn = poDS->GetPGConn();
3766 6 : CPLString osCommand;
3767 : osCommand.Printf("ALTER TABLE %s RENAME TO %s", pszSqlTableName,
3768 6 : pszNewSqlTableName);
3769 6 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3770 :
3771 6 : OGRErr eRet = OGRERR_NONE;
3772 6 : if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
3773 : {
3774 2 : eRet = OGRERR_FAILURE;
3775 2 : CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
3776 :
3777 2 : CPLFree(pszNewSqlTableName);
3778 : }
3779 : else
3780 : {
3781 4 : CPLFree(pszTableName);
3782 4 : pszTableName = CPLStrdup(pszNewName);
3783 :
3784 4 : CPLFree(pszSqlTableName);
3785 4 : pszSqlTableName = pszNewSqlTableName;
3786 :
3787 4 : SetDescription(pszNewName);
3788 4 : whileUnsealing(poFeatureDefn)->SetName(pszNewName);
3789 : }
3790 :
3791 6 : OGRPGClearResult(hResult);
3792 :
3793 6 : return eRet;
3794 : }
3795 :
3796 : /************************************************************************/
3797 : /* SetDeferredCreation() */
3798 : /************************************************************************/
3799 :
3800 204 : void OGRPGTableLayer::SetDeferredCreation(int bDeferredCreationIn,
3801 : const std::string &osCreateTableIn)
3802 : {
3803 204 : bDeferredCreation = bDeferredCreationIn;
3804 204 : osCreateTable = osCreateTableIn;
3805 204 : }
3806 :
3807 : /************************************************************************/
3808 : /* RunDeferredCreationIfNecessary() */
3809 : /************************************************************************/
3810 :
3811 1439 : OGRErr OGRPGTableLayer::RunDeferredCreationIfNecessary()
3812 : {
3813 1439 : if (!bDeferredCreation)
3814 1304 : return OGRERR_NONE;
3815 135 : bDeferredCreation = FALSE;
3816 :
3817 135 : poDS->EndCopy();
3818 :
3819 259 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
3820 : {
3821 124 : OGRPGGeomFieldDefn *poGeomField = poFeatureDefn->GetGeomFieldDefn(i);
3822 :
3823 124 : if (poDS->HavePostGIS() ||
3824 0 : poGeomField->ePostgisType == GEOM_TYPE_GEOGRAPHY)
3825 : {
3826 : const char *pszGeometryType =
3827 124 : OGRToOGCGeomType(poGeomField->GetType());
3828 :
3829 124 : osCreateTable += ", ";
3830 124 : osCreateTable += OGRPGEscapeColumnName(poGeomField->GetNameRef());
3831 124 : osCreateTable += " ";
3832 124 : if (poGeomField->ePostgisType == GEOM_TYPE_GEOMETRY)
3833 120 : osCreateTable += "geometry(";
3834 : else
3835 4 : osCreateTable += "geography(";
3836 124 : osCreateTable += pszGeometryType;
3837 124 : if ((poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_3D) &&
3838 47 : (poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
3839 3 : osCreateTable += "ZM";
3840 121 : else if (poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_3D)
3841 44 : osCreateTable += "Z";
3842 77 : else if (poGeomField->GeometryTypeFlags &
3843 : OGRGeometry::OGR_G_MEASURED)
3844 4 : osCreateTable += "M";
3845 124 : if (poGeomField->nSRSId > 0)
3846 16 : osCreateTable += CPLSPrintf(",%d", poGeomField->nSRSId);
3847 124 : osCreateTable += ")";
3848 124 : if (!poGeomField->IsNullable())
3849 1 : osCreateTable += " NOT NULL";
3850 : }
3851 : }
3852 :
3853 135 : osCreateTable += " )";
3854 270 : CPLString osCommand(osCreateTable);
3855 :
3856 135 : PGconn *hPGConn = poDS->GetPGConn();
3857 :
3858 135 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
3859 135 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
3860 : {
3861 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
3862 : PQerrorMessage(hPGConn));
3863 :
3864 0 : OGRPGClearResult(hResult);
3865 0 : return OGRERR_FAILURE;
3866 : }
3867 :
3868 135 : OGRPGClearResult(hResult);
3869 :
3870 136 : for (const auto &osSQL : m_aosDeferredCommentOnColumns)
3871 : {
3872 1 : hResult = OGRPG_PQexec(hPGConn, osSQL.c_str());
3873 1 : OGRPGClearResult(hResult);
3874 : }
3875 135 : m_aosDeferredCommentOnColumns.clear();
3876 :
3877 135 : if (bCreateSpatialIndexFlag)
3878 : {
3879 259 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
3880 : {
3881 : OGRPGGeomFieldDefn *poGeomField =
3882 124 : poFeatureDefn->GetGeomFieldDefn(i);
3883 124 : if (RunCreateSpatialIndex(poGeomField, i) != OGRERR_NONE)
3884 : {
3885 0 : return OGRERR_FAILURE;
3886 : }
3887 : }
3888 : }
3889 :
3890 135 : char **papszMD = OGRLayer::GetMetadata();
3891 135 : if (papszMD != nullptr)
3892 2 : SetMetadata(papszMD);
3893 :
3894 135 : return OGRERR_NONE;
3895 : }
3896 :
3897 : /************************************************************************/
3898 : /* GetGeometryTypes() */
3899 : /************************************************************************/
3900 :
3901 27 : OGRGeometryTypeCounter *OGRPGTableLayer::GetGeometryTypes(
3902 : int iGeomField, int nFlagsGGT, int &nEntryCountOut,
3903 : GDALProgressFunc pfnProgress, void *pProgressData)
3904 : {
3905 27 : if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount())
3906 : {
3907 1 : CPLError(CE_Failure, CPLE_AppDefined,
3908 : "Invalid geometry field index : %d", iGeomField);
3909 1 : nEntryCountOut = 0;
3910 1 : return nullptr;
3911 : }
3912 :
3913 26 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3914 : {
3915 0 : nEntryCountOut = 0;
3916 0 : return nullptr;
3917 : }
3918 26 : poDS->EndCopy();
3919 :
3920 : const OGRPGGeomFieldDefn *poGeomFieldDefn =
3921 26 : GetLayerDefn()->GetGeomFieldDefn(iGeomField);
3922 : const auto osEscapedGeom =
3923 52 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
3924 52 : CPLString osSQL;
3925 26 : if ((nFlagsGGT & OGR_GGT_GEOMCOLLECTIONZ_TINZ) != 0)
3926 : {
3927 18 : CPLString osFilter;
3928 : osFilter.Printf("(ST_Zmflag(%s) = 2 AND "
3929 : "((GeometryType(%s) = 'GEOMETRYCOLLECTION' AND "
3930 : "ST_NumGeometries(%s) >= 1 AND "
3931 : "geometrytype(ST_GeometryN(%s, 1)) = 'TIN') OR "
3932 : "GeometryType(%s) = 'TIN'))",
3933 : osEscapedGeom.c_str(), osEscapedGeom.c_str(),
3934 : osEscapedGeom.c_str(), osEscapedGeom.c_str(),
3935 9 : osEscapedGeom.c_str());
3936 :
3937 18 : std::string l_osWHERE(osWHERE);
3938 9 : if (l_osWHERE.empty())
3939 6 : l_osWHERE = " WHERE ";
3940 : else
3941 3 : l_osWHERE += " AND ";
3942 9 : l_osWHERE += "(NOT (";
3943 9 : l_osWHERE += osFilter;
3944 9 : l_osWHERE += ") OR ";
3945 9 : l_osWHERE += osEscapedGeom;
3946 9 : l_osWHERE += " IS NULL)";
3947 :
3948 18 : std::string l_osWHEREFilter(osWHERE);
3949 9 : if (l_osWHEREFilter.empty())
3950 6 : l_osWHEREFilter = " WHERE ";
3951 : else
3952 3 : l_osWHEREFilter += " AND ";
3953 9 : l_osWHEREFilter += osFilter;
3954 :
3955 : osSQL.Printf(
3956 : "(SELECT GeometryType(%s), ST_Zmflag(%s), COUNT(*) FROM %s %s "
3957 : "GROUP BY GeometryType(%s), ST_Zmflag(%s)) UNION ALL "
3958 : "(SELECT * FROM (SELECT 'TIN', 2, COUNT(*) AS count FROM %s %s) "
3959 : "tinsubselect WHERE tinsubselect.count != 0)",
3960 : osEscapedGeom.c_str(), osEscapedGeom.c_str(), pszSqlTableName,
3961 : l_osWHERE.c_str(), osEscapedGeom.c_str(), osEscapedGeom.c_str(),
3962 9 : pszSqlTableName, l_osWHEREFilter.c_str());
3963 : }
3964 17 : else if ((nFlagsGGT & OGR_GGT_STOP_IF_MIXED) != 0)
3965 : {
3966 8 : std::string l_osWHERE(osWHERE);
3967 4 : if (l_osWHERE.empty())
3968 2 : l_osWHERE = " WHERE ";
3969 : else
3970 2 : l_osWHERE += " AND ";
3971 4 : l_osWHERE += osEscapedGeom;
3972 4 : l_osWHERE += " IS NOT NULL";
3973 :
3974 8 : std::string l_osWHERE_NULL(osWHERE);
3975 4 : if (l_osWHERE_NULL.empty())
3976 2 : l_osWHERE_NULL = " WHERE ";
3977 : else
3978 2 : l_osWHERE_NULL += " AND ";
3979 4 : l_osWHERE_NULL += osEscapedGeom;
3980 4 : l_osWHERE_NULL += " IS NULL";
3981 :
3982 : osSQL.Printf("(SELECT DISTINCT GeometryType(%s), ST_Zmflag(%s), 0 FROM "
3983 : "%s %s LIMIT 2) "
3984 : "UNION ALL (SELECT NULL, NULL, 0 FROM %s %s LIMIT 1)",
3985 : osEscapedGeom.c_str(), osEscapedGeom.c_str(),
3986 : pszSqlTableName, l_osWHERE.c_str(), pszSqlTableName,
3987 4 : l_osWHERE_NULL.c_str());
3988 : }
3989 : else
3990 : {
3991 : const bool bDebug =
3992 13 : CPLTestBool(CPLGetConfigOption("OGR_PG_DEBUG_GGT_CANCEL", "NO"));
3993 : osSQL.Printf(
3994 : "SELECT GeometryType(%s), ST_Zmflag(%s), COUNT(*)%s FROM %s %s "
3995 : "GROUP BY GeometryType(%s), ST_Zmflag(%s)",
3996 : osEscapedGeom.c_str(), osEscapedGeom.c_str(),
3997 : bDebug ? ", pg_sleep(1)" : "", pszSqlTableName, osWHERE.c_str(),
3998 13 : osEscapedGeom.c_str(), osEscapedGeom.c_str());
3999 : }
4000 :
4001 52 : std::thread thread;
4002 26 : std::mutex mutex;
4003 26 : std::condition_variable cv;
4004 26 : bool stopThread = false;
4005 26 : if (pfnProgress && pfnProgress != GDALDummyProgress)
4006 : {
4007 4 : thread = std::thread(
4008 2 : [&]()
4009 : {
4010 4 : std::unique_lock<std::mutex> lock(mutex);
4011 4 : while (!stopThread)
4012 : {
4013 2 : if (!pfnProgress(0.0, "", pProgressData))
4014 1 : poDS->AbortSQL();
4015 2 : cv.wait_for(lock, std::chrono::milliseconds(100));
4016 : }
4017 4 : });
4018 : }
4019 :
4020 26 : PGconn *hPGConn = poDS->GetPGConn();
4021 26 : PGresult *hResult = OGRPG_PQexec(hPGConn, osSQL.c_str());
4022 :
4023 26 : if (pfnProgress && pfnProgress != GDALDummyProgress)
4024 : {
4025 : {
4026 4 : std::unique_lock<std::mutex> lock(mutex);
4027 2 : stopThread = true;
4028 2 : cv.notify_one();
4029 : }
4030 2 : thread.join();
4031 : }
4032 :
4033 26 : nEntryCountOut = 0;
4034 26 : OGRGeometryTypeCounter *pasRet = nullptr;
4035 26 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK)
4036 : {
4037 25 : const int nTuples = PQntuples(hResult);
4038 25 : nEntryCountOut = nTuples;
4039 : pasRet = static_cast<OGRGeometryTypeCounter *>(
4040 25 : CPLCalloc(1 + nEntryCountOut, sizeof(OGRGeometryTypeCounter)));
4041 69 : for (int i = 0; i < nTuples; ++i)
4042 : {
4043 44 : const char *pszGeomType = PQgetvalue(hResult, i, 0);
4044 44 : const char *pszZMFlag = PQgetvalue(hResult, i, 1);
4045 44 : const char *pszCount = PQgetvalue(hResult, i, 2);
4046 44 : if (pszCount)
4047 : {
4048 44 : if (pszGeomType == nullptr || pszGeomType[0] == '\0')
4049 : {
4050 16 : pasRet[i].eGeomType = wkbNone;
4051 : }
4052 28 : else if (pszZMFlag != nullptr)
4053 : {
4054 28 : const int nZMFlag = atoi(pszZMFlag);
4055 28 : pasRet[i].eGeomType = OGRFromOGCGeomType(pszGeomType);
4056 28 : int nModifier = 0;
4057 28 : if (nZMFlag == 1)
4058 1 : nModifier = OGRGeometry::OGR_G_MEASURED;
4059 27 : else if (nZMFlag == 2)
4060 9 : nModifier = OGRGeometry::OGR_G_3D;
4061 18 : else if (nZMFlag == 3)
4062 1 : nModifier =
4063 : OGRGeometry::OGR_G_MEASURED | OGRGeometry::OGR_G_3D;
4064 28 : pasRet[i].eGeomType = OGR_GT_SetModifier(
4065 28 : pasRet[i].eGeomType, nModifier & OGRGeometry::OGR_G_3D,
4066 : nModifier & OGRGeometry::OGR_G_MEASURED);
4067 : }
4068 44 : pasRet[i].nCount =
4069 44 : static_cast<int64_t>(std::strtoll(pszCount, nullptr, 10));
4070 : }
4071 : }
4072 : }
4073 :
4074 26 : OGRPGClearResult(hResult);
4075 :
4076 26 : return pasRet;
4077 : }
4078 :
4079 : #undef PQexec
|