Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements OGRPGDataSource class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2000, Frank Warmerdam
9 : * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include <string.h>
15 : #include "ogr_pg.h"
16 : #include "cpl_conv.h"
17 : #include "cpl_string.h"
18 : #include "cpl_hash_set.h"
19 : #include <cctype>
20 : #include <set>
21 :
22 : #include "memdataset.h"
23 :
24 : #define PQexec this_is_an_error
25 :
26 : static void OGRPGNoticeProcessor(void *arg, const char *pszMessage);
27 :
28 : /************************************************************************/
29 : /* OGRPGDataSource() */
30 : /************************************************************************/
31 :
32 : OGRPGDataSource::OGRPGDataSource() = default;
33 :
34 : /************************************************************************/
35 : /* ~OGRPGDataSource() */
36 : /************************************************************************/
37 :
38 860 : OGRPGDataSource::~OGRPGDataSource()
39 :
40 : {
41 430 : OGRPGDataSource::FlushCache(true);
42 :
43 430 : CPLFree(pszForcedTables);
44 430 : CSLDestroy(papszSchemaList);
45 :
46 908 : for (int i = 0; i < nLayers; i++)
47 478 : delete papoLayers[i];
48 :
49 430 : CPLFree(papoLayers);
50 :
51 430 : if (hPGConn != nullptr)
52 : {
53 : // If there are prelude statements, don't mess with transactions.
54 427 : if (CSLFetchNameValue(papszOpenOptions, "PRELUDE_STATEMENTS") ==
55 : nullptr)
56 419 : FlushSoftTransaction();
57 :
58 : /* --------------------------------------------------------------------
59 : */
60 : /* Send closing statements */
61 : /* --------------------------------------------------------------------
62 : */
63 : const char *pszClosingStatements =
64 427 : CSLFetchNameValue(papszOpenOptions, "CLOSING_STATEMENTS");
65 427 : if (pszClosingStatements != nullptr)
66 : {
67 : PGresult *hResult =
68 6 : OGRPG_PQexec(hPGConn, pszClosingStatements, TRUE);
69 6 : OGRPGClearResult(hResult);
70 : }
71 :
72 : /* XXX - mloskot: After the connection is closed, valgrind still
73 : * reports 36 bytes definitely lost, somewhere in the libpq.
74 : */
75 427 : PQfinish(hPGConn);
76 427 : hPGConn = nullptr;
77 : }
78 860 : }
79 :
80 : /************************************************************************/
81 : /* FlushCache() */
82 : /************************************************************************/
83 :
84 1778 : OGRErr OGRPGDataSource::FlushCacheWithRet(bool /* bAtClosing */)
85 : {
86 1778 : OGRErr eErr = EndCopy();
87 1778 : if (eErr == OGRERR_NONE)
88 : {
89 3203 : for (int iLayer = 0; iLayer < nLayers; iLayer++)
90 : {
91 1426 : papoLayers[iLayer]->RunDeferredCreationIfNecessary();
92 : }
93 : }
94 1778 : return eErr;
95 : }
96 :
97 1722 : CPLErr OGRPGDataSource::FlushCache(bool bAtClosing)
98 : {
99 1722 : return FlushCacheWithRet(bAtClosing) == OGRERR_NONE ? CE_None : CE_Failure;
100 : }
101 :
102 : /************************************************************************/
103 : /* GetCurrentSchema() */
104 : /************************************************************************/
105 :
106 425 : CPLString OGRPGDataSource::GetCurrentSchema()
107 : {
108 : /* -------------------------------------------- */
109 : /* Get the current schema */
110 : /* -------------------------------------------- */
111 425 : PGresult *hResult = OGRPG_PQexec(hPGConn, "SELECT current_schema()");
112 425 : if (hResult && PQntuples(hResult) == 1 && !PQgetisnull(hResult, 0, 0))
113 : {
114 425 : osCurrentSchema = PQgetvalue(hResult, 0, 0);
115 : }
116 425 : OGRPGClearResult(hResult);
117 :
118 850 : return osCurrentSchema;
119 : }
120 :
121 : /************************************************************************/
122 : /* OGRPGDecodeVersionString() */
123 : /************************************************************************/
124 :
125 662 : void OGRPGDataSource::OGRPGDecodeVersionString(PGver *psVersion,
126 : const char *pszVer)
127 : {
128 : // Skip leading spaces
129 662 : while (*pszVer == ' ')
130 0 : pszVer++;
131 1324 : std::string osVer(pszVer);
132 : // And truncate at the first space
133 662 : const auto nPosSpace = osVer.find(' ');
134 662 : if (nPosSpace != std::string::npos)
135 662 : osVer.resize(nPosSpace);
136 :
137 662 : memset(psVersion, 0, sizeof(*psVersion));
138 1324 : const CPLStringList aosTokens(CSLTokenizeString2(osVer.c_str(), ".", 0));
139 662 : if (aosTokens.size() >= 1)
140 662 : psVersion->nMajor = atoi(aosTokens[0]);
141 662 : if (aosTokens.size() >= 2)
142 662 : psVersion->nMinor = atoi(aosTokens[1]);
143 662 : if (aosTokens.size() >= 3)
144 0 : psVersion->nRelease = atoi(aosTokens[2]);
145 662 : }
146 :
147 : /************************************************************************/
148 : /* One entry for each PG table */
149 : /************************************************************************/
150 :
151 : struct PGTableEntry
152 : {
153 : char *pszTableName = nullptr;
154 : char *pszSchemaName = nullptr;
155 : char *pszDescription = nullptr;
156 : int nGeomColumnCount = 0;
157 : PGGeomColumnDesc *pasGeomColumns = nullptr; /* list of geometry columns */
158 : int bDerivedInfoAdded =
159 : false; /* set to TRUE if it derives from another table */
160 : };
161 :
162 1440 : static unsigned long OGRPGHashTableEntry(const void *_psTableEntry)
163 : {
164 1440 : const PGTableEntry *psTableEntry =
165 : static_cast<const PGTableEntry *>(_psTableEntry);
166 2880 : return CPLHashSetHashStr(CPLString().Printf(
167 2880 : "%s.%s", psTableEntry->pszSchemaName, psTableEntry->pszTableName));
168 : }
169 :
170 387 : static int OGRPGEqualTableEntry(const void *_psTableEntry1,
171 : const void *_psTableEntry2)
172 : {
173 387 : const PGTableEntry *psTableEntry1 =
174 : static_cast<const PGTableEntry *>(_psTableEntry1);
175 387 : const PGTableEntry *psTableEntry2 =
176 : static_cast<const PGTableEntry *>(_psTableEntry2);
177 387 : return strcmp(psTableEntry1->pszTableName, psTableEntry2->pszTableName) ==
178 768 : 0 &&
179 381 : strcmp(psTableEntry1->pszSchemaName, psTableEntry2->pszSchemaName) ==
180 387 : 0;
181 : }
182 :
183 158 : static void OGRPGTableEntryAddGeomColumn(
184 : PGTableEntry *psTableEntry, const char *pszName,
185 : const char *pszGeomType = nullptr, int GeometryTypeFlags = 0,
186 : int nSRID = UNDETERMINED_SRID, PostgisType ePostgisType = GEOM_TYPE_UNKNOWN,
187 : int bNullable = TRUE)
188 : {
189 316 : psTableEntry->pasGeomColumns = static_cast<PGGeomColumnDesc *>(CPLRealloc(
190 158 : psTableEntry->pasGeomColumns,
191 158 : sizeof(PGGeomColumnDesc) * (psTableEntry->nGeomColumnCount + 1)));
192 316 : psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].pszName =
193 158 : CPLStrdup(pszName);
194 158 : psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].pszGeomType =
195 158 : (pszGeomType) ? CPLStrdup(pszGeomType) : nullptr;
196 158 : psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount]
197 158 : .GeometryTypeFlags = GeometryTypeFlags;
198 : /* With PostGIS 2.0, querying geometry_columns can return 0, not only when
199 : */
200 : /* the SRID is truly set to 0, but also when there's no constraint */
201 158 : psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].nSRID =
202 158 : nSRID > 0 ? nSRID : UNDETERMINED_SRID;
203 158 : psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].ePostgisType =
204 : ePostgisType;
205 158 : psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].bNullable =
206 : bNullable;
207 158 : psTableEntry->nGeomColumnCount++;
208 158 : }
209 :
210 726 : static void OGRPGFreeTableEntry(void *_psTableEntry)
211 : {
212 726 : PGTableEntry *psTableEntry = static_cast<PGTableEntry *>(_psTableEntry);
213 726 : CPLFree(psTableEntry->pszTableName);
214 726 : CPLFree(psTableEntry->pszSchemaName);
215 726 : CPLFree(psTableEntry->pszDescription);
216 884 : for (int i = 0; i < psTableEntry->nGeomColumnCount; i++)
217 : {
218 158 : CPLFree(psTableEntry->pasGeomColumns[i].pszName);
219 158 : CPLFree(psTableEntry->pasGeomColumns[i].pszGeomType);
220 : }
221 726 : CPLFree(psTableEntry->pasGeomColumns);
222 726 : CPLFree(psTableEntry);
223 726 : }
224 :
225 364 : static PGTableEntry *OGRPGFindTableEntry(CPLHashSet *hSetTables,
226 : const char *pszTableName,
227 : const char *pszSchemaName)
228 : {
229 364 : PGTableEntry sEntry;
230 364 : sEntry.pszTableName = const_cast<char *>(pszTableName);
231 364 : sEntry.pszSchemaName = const_cast<char *>(pszSchemaName);
232 728 : return static_cast<PGTableEntry *>(CPLHashSetLookup(hSetTables, &sEntry));
233 : }
234 :
235 350 : static PGTableEntry *OGRPGAddTableEntry(CPLHashSet *hSetTables,
236 : const char *pszTableName,
237 : const char *pszSchemaName,
238 : const char *pszDescription)
239 : {
240 : PGTableEntry *psEntry =
241 350 : static_cast<PGTableEntry *>(CPLCalloc(1, sizeof(PGTableEntry)));
242 350 : psEntry->pszTableName = CPLStrdup(pszTableName);
243 350 : psEntry->pszSchemaName = CPLStrdup(pszSchemaName);
244 350 : psEntry->pszDescription = CPLStrdup(pszDescription ? pszDescription : "");
245 :
246 350 : CPLHashSetInsert(hSetTables, psEntry);
247 :
248 350 : return psEntry;
249 : }
250 :
251 : /************************************************************************/
252 : /* Open() */
253 : /************************************************************************/
254 :
255 430 : int OGRPGDataSource::Open(const char *pszNewName, int bUpdate, int bTestOpen,
256 : char **papszOpenOptionsIn)
257 :
258 : {
259 430 : CPLAssert(nLayers == 0);
260 430 : papszOpenOptions = CSLDuplicate(papszOpenOptionsIn);
261 :
262 : const char *pszPreludeStatements =
263 430 : CSLFetchNameValue(papszOpenOptions, "PRELUDE_STATEMENTS");
264 430 : if (pszPreludeStatements)
265 : {
266 : // If the prelude statements starts with BEGIN, then don't emit one
267 : // in our code.
268 8 : if (STARTS_WITH_CI(pszPreludeStatements, "BEGIN"))
269 6 : nSoftTransactionLevel = 1;
270 : }
271 :
272 : /* -------------------------------------------------------------------- */
273 : /* Verify postgresql prefix. */
274 : /* -------------------------------------------------------------------- */
275 430 : if (STARTS_WITH_CI(pszNewName, "PGB:"))
276 : {
277 : #if defined(BINARY_CURSOR_ENABLED)
278 : bUseBinaryCursor = TRUE;
279 : CPLDebug("PG", "BINARY cursor is used for geometry fetching");
280 : #endif
281 : }
282 430 : else if (!STARTS_WITH_CI(pszNewName, "PG:") &&
283 2 : !STARTS_WITH(pszNewName, "postgresql://"))
284 : {
285 0 : if (!bTestOpen)
286 0 : CPLError(CE_Failure, CPLE_AppDefined,
287 : "%s does not conform to PostgreSQL naming convention,"
288 : " PG:* or postgresql://\n",
289 : pszNewName);
290 0 : return FALSE;
291 : }
292 :
293 425 : const auto QuoteAndEscapeConnectionParam = [](const char *pszParam)
294 : {
295 425 : CPLString osRet("\'");
296 11050 : for (int i = 0; pszParam[i]; ++i)
297 : {
298 10625 : if (pszParam[i] == '\'')
299 0 : osRet += "\\'";
300 10625 : else if (pszParam[i] == '\\')
301 0 : osRet += "\\\\";
302 : else
303 10625 : osRet += pszParam[i];
304 : }
305 425 : osRet += '\'';
306 425 : return osRet;
307 : };
308 :
309 860 : CPLString osConnectionName(pszNewName);
310 430 : if (osConnectionName.find("PG:postgresql://") == 0)
311 1 : osConnectionName = osConnectionName.substr(3);
312 430 : const bool bIsURI = osConnectionName.find("postgresql://") == 0;
313 :
314 430 : const char *const apszOpenOptions[] = {
315 : "service", "dbname", "port", "user", "password", "host",
316 : // Non-postgreSQL options
317 : "active_schema", "schemas", "tables"};
318 860 : std::string osSchemas;
319 860 : std::string osForcedTables;
320 4300 : for (const char *pszOpenOption : apszOpenOptions)
321 : {
322 3870 : const char *pszVal = CSLFetchNameValue(papszOpenOptions, pszOpenOption);
323 3870 : if (pszVal && strcmp(pszOpenOption, "active_schema") == 0)
324 : {
325 1 : osActiveSchema = pszVal;
326 : }
327 3869 : else if (pszVal && strcmp(pszOpenOption, "schemas") == 0)
328 : {
329 0 : osSchemas = pszVal;
330 : }
331 3869 : else if (pszVal && strcmp(pszOpenOption, "tables") == 0)
332 : {
333 6 : osForcedTables = pszVal;
334 : }
335 3863 : else if (pszVal)
336 : {
337 2 : if (bIsURI)
338 : {
339 : osConnectionName +=
340 2 : osConnectionName.find('?') == std::string::npos ? '?' : '&';
341 : }
342 : else
343 : {
344 0 : if (osConnectionName.back() != ':')
345 0 : osConnectionName += ' ';
346 : }
347 2 : osConnectionName += pszOpenOption;
348 2 : osConnectionName += "=";
349 2 : if (bIsURI)
350 : {
351 2 : char *pszTmp = CPLEscapeString(pszVal, -1, CPLES_URL);
352 2 : osConnectionName += pszTmp;
353 2 : CPLFree(pszTmp);
354 : }
355 : else
356 : {
357 0 : osConnectionName += QuoteAndEscapeConnectionParam(pszVal);
358 : }
359 : }
360 : }
361 :
362 : /* -------------------------------------------------------------------- */
363 : /* Set application name if not found in connection string */
364 : /* -------------------------------------------------------------------- */
365 :
366 858 : if (strstr(pszNewName, "application_name") == nullptr &&
367 428 : getenv("PGAPPNAME") == nullptr)
368 : {
369 428 : if (bIsURI)
370 : {
371 : osConnectionName +=
372 3 : osConnectionName.find('?') == std::string::npos ? '?' : '&';
373 : }
374 : else
375 : {
376 425 : if (osConnectionName.back() != ':')
377 425 : osConnectionName += ' ';
378 : }
379 428 : osConnectionName += "application_name=";
380 856 : std::string osVal("GDAL ");
381 428 : osVal += GDALVersionInfo("RELEASE_NAME");
382 428 : if (bIsURI)
383 : {
384 3 : char *pszTmp = CPLEscapeString(osVal.c_str(), -1, CPLES_URL);
385 3 : osConnectionName += pszTmp;
386 3 : CPLFree(pszTmp);
387 : }
388 : else
389 : {
390 425 : osConnectionName += QuoteAndEscapeConnectionParam(osVal.c_str());
391 : }
392 : }
393 :
394 : const auto ParseAndRemoveParam =
395 1275 : [](char *pszStr, const char *pszParamName, std::string &osValue)
396 : {
397 1275 : const int nParamNameLen = static_cast<int>(strlen(pszParamName));
398 1275 : bool bInSingleQuotedString = false;
399 157462 : for (int i = 0; pszStr[i]; i++)
400 : {
401 156617 : if (bInSingleQuotedString)
402 : {
403 22138 : if (pszStr[i] == '\\')
404 : {
405 12 : if (pszStr[i + 1] == '\\' || pszStr[i + 1] == '\'')
406 : {
407 12 : ++i;
408 : }
409 : }
410 22126 : else if (pszStr[i] == '\'')
411 : {
412 857 : bInSingleQuotedString = false;
413 : }
414 : }
415 134479 : else if (pszStr[i] == '\'')
416 : {
417 857 : bInSingleQuotedString = true;
418 : }
419 133622 : else if (EQUALN(pszStr + i, pszParamName, nParamNameLen) &&
420 430 : (pszStr[i + nParamNameLen] == '=' ||
421 3 : pszStr[i + nParamNameLen] == ' '))
422 : {
423 430 : const int iStart = i;
424 430 : i += nParamNameLen;
425 434 : while (pszStr[i] == ' ')
426 4 : ++i;
427 430 : if (pszStr[i] == '=')
428 : {
429 430 : ++i;
430 436 : while (pszStr[i] == ' ')
431 6 : ++i;
432 430 : if (pszStr[i] == '\'')
433 : {
434 2 : ++i;
435 106 : for (; pszStr[i]; i++)
436 : {
437 106 : if (pszStr[i] == '\\')
438 : {
439 0 : if (pszStr[i + 1] == '\\' ||
440 0 : pszStr[i + 1] == '\'')
441 : {
442 0 : osValue += pszStr[i + 1];
443 0 : ++i;
444 : }
445 : }
446 106 : else if (pszStr[i] == '\'')
447 : {
448 2 : ++i;
449 2 : break;
450 : }
451 : else
452 : {
453 104 : osValue += pszStr[i];
454 : }
455 : }
456 : }
457 : else
458 : {
459 12412 : for (; pszStr[i] && pszStr[i] != ' '; i++)
460 : {
461 11984 : osValue += pszStr[i];
462 : }
463 : }
464 :
465 : // Edit pszStr to remove the parameter and its value
466 430 : if (pszStr[i] == ' ')
467 : {
468 428 : memmove(pszStr + iStart, pszStr + i,
469 428 : strlen(pszStr + i) + 1);
470 : }
471 : else
472 : {
473 2 : pszStr[iStart] = 0;
474 : }
475 : }
476 430 : return true;
477 : }
478 : }
479 845 : return false;
480 : };
481 :
482 430 : char *pszConnectionName = CPLStrdup(osConnectionName);
483 430 : char *pszConnectionNameNoPrefix =
484 860 : pszConnectionName + (STARTS_WITH_CI(pszConnectionName, "PGB:") ? 4
485 430 : : STARTS_WITH_CI(pszConnectionName, "PG:") ? 3
486 : : 0);
487 :
488 : /* -------------------------------------------------------------------- */
489 : /* Determine if the connection string contains an optional */
490 : /* ACTIVE_SCHEMA portion. If so, parse it out. */
491 : /* -------------------------------------------------------------------- */
492 857 : if (osActiveSchema.empty() && !bIsURI &&
493 427 : !ParseAndRemoveParam(pszConnectionNameNoPrefix, "active_schema",
494 : osActiveSchema))
495 : {
496 423 : osActiveSchema = "public";
497 : }
498 :
499 : /* -------------------------------------------------------------------- */
500 : /* Determine if the connection string contains an optional */
501 : /* SCHEMAS portion. If so, parse it out. */
502 : /* -------------------------------------------------------------------- */
503 860 : if (!osSchemas.empty() ||
504 430 : (!bIsURI &&
505 427 : ParseAndRemoveParam(pszConnectionNameNoPrefix, "schemas", osSchemas)))
506 : {
507 420 : papszSchemaList = CSLTokenizeString2(osSchemas.c_str(), ",", 0);
508 :
509 : /* If there is only one schema specified, make it the active schema */
510 420 : if (CSLCount(papszSchemaList) == 1)
511 : {
512 418 : osActiveSchema = papszSchemaList[0];
513 : }
514 : }
515 :
516 : /* -------------------------------------------------------------------- */
517 : /* Determine if the connection string contains an optional */
518 : /* TABLES portion. If so, parse it out. The expected */
519 : /* connection string in this case will be, e.g.: */
520 : /* */
521 : /* 'PG:dbname=warmerda user=warmerda tables=s1.t1,[s2.t2,...] */
522 : /* - where sN is schema and tN is table name */
523 : /* We must also strip this information from the connection */
524 : /* string; PQconnectdb() does not like unknown directives */
525 : /* -------------------------------------------------------------------- */
526 854 : if (!osForcedTables.empty() ||
527 424 : (!bIsURI && ParseAndRemoveParam(pszConnectionNameNoPrefix, "tables",
528 : osForcedTables)))
529 : {
530 12 : pszForcedTables = CPLStrdup(osForcedTables.c_str());
531 : }
532 :
533 : /* -------------------------------------------------------------------- */
534 : /* Try to establish connection. */
535 : /* -------------------------------------------------------------------- */
536 430 : hPGConn = PQconnectdb(pszConnectionNameNoPrefix);
537 430 : CPLFree(pszConnectionName);
538 430 : pszConnectionName = nullptr;
539 :
540 430 : if (hPGConn == nullptr || PQstatus(hPGConn) == CONNECTION_BAD)
541 : {
542 3 : CPLError(CE_Failure, CPLE_AppDefined, "PQconnectdb failed.\n%s",
543 3 : PQerrorMessage(hPGConn));
544 :
545 3 : PQfinish(hPGConn);
546 3 : hPGConn = nullptr;
547 :
548 3 : return FALSE;
549 : }
550 :
551 427 : bDSUpdate = bUpdate;
552 :
553 : /* -------------------------------------------------------------------- */
554 : /* Send prelude statements */
555 : /* -------------------------------------------------------------------- */
556 427 : if (pszPreludeStatements != nullptr)
557 : {
558 8 : PGresult *hResult = OGRPG_PQexec(hPGConn, pszPreludeStatements, TRUE);
559 8 : if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
560 : {
561 2 : OGRPGClearResult(hResult);
562 2 : return FALSE;
563 : }
564 :
565 6 : OGRPGClearResult(hResult);
566 : }
567 :
568 : /* -------------------------------------------------------------------- */
569 : /* Set the encoding to UTF8 as the driver advertises UTF8 */
570 : /* unless PGCLIENTENCODING is defined */
571 : /* -------------------------------------------------------------------- */
572 425 : if (CPLGetConfigOption("PGCLIENTENCODING", nullptr) == nullptr)
573 : {
574 423 : const char *encoding = "UTF8";
575 423 : if (PQsetClientEncoding(hPGConn, encoding) == -1)
576 : {
577 0 : CPLError(CE_Warning, CPLE_AppDefined,
578 : "PQsetClientEncoding(%s) failed.\n%s", encoding,
579 0 : PQerrorMessage(hPGConn));
580 : }
581 : }
582 :
583 : {
584 425 : PGresult *hResult = OGRPG_PQexec(hPGConn, "SHOW client_encoding");
585 850 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
586 425 : PQntuples(hResult) == 1)
587 : {
588 425 : const char *pszClientEncoding = PQgetvalue(hResult, 0, 0);
589 425 : if (pszClientEncoding)
590 : {
591 425 : CPLDebug("PG", "Client encoding: '%s'", pszClientEncoding);
592 425 : if (EQUAL(pszClientEncoding, "UTF8"))
593 : {
594 423 : m_bUTF8ClientEncoding = true;
595 : }
596 : }
597 : }
598 425 : OGRPGClearResult(hResult);
599 : }
600 :
601 : /* -------------------------------------------------------------------- */
602 : /* Install a notice processor. */
603 : /* -------------------------------------------------------------------- */
604 425 : PQsetNoticeProcessor(hPGConn, OGRPGNoticeProcessor, this);
605 :
606 : /* -------------------------------------------------------------------- */
607 : /* Detect PostGIS schema */
608 : /* -------------------------------------------------------------------- */
609 850 : CPLString osPostgisSchema;
610 : {
611 425 : PGresult *hResult = OGRPG_PQexec(
612 : hPGConn,
613 : "SELECT n.nspname FROM pg_proc p JOIN pg_namespace n "
614 425 : "ON n.oid = p.pronamespace WHERE proname = 'postgis_version'");
615 850 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
616 425 : PQntuples(hResult) > 0)
617 : {
618 425 : const char *pszPostgisSchema = PQgetvalue(hResult, 0, 0);
619 :
620 425 : CPLDebug("PG", "PostGIS schema: '%s'", pszPostgisSchema);
621 :
622 425 : osPostgisSchema = pszPostgisSchema;
623 : }
624 425 : OGRPGClearResult(hResult);
625 : }
626 :
627 : /* -------------------------------------------------------------------- */
628 : /* Get search_path */
629 : /* -------------------------------------------------------------------- */
630 850 : std::string osSearchPath = "public";
631 : {
632 425 : PGresult *hResult = OGRPG_PQexec(hPGConn, "SHOW search_path");
633 850 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
634 425 : PQntuples(hResult) > 0)
635 : {
636 425 : const char *pszVal = PQgetvalue(hResult, 0, 0);
637 425 : if (pszVal)
638 : {
639 425 : osSearchPath = pszVal;
640 : }
641 : }
642 425 : OGRPGClearResult(hResult);
643 : }
644 :
645 : /* -------------------------------------------------------------------- */
646 : /* Set active schema if different from 'public'. Also add */
647 : /* postgis schema if needed. */
648 : /* -------------------------------------------------------------------- */
649 428 : if (osActiveSchema != "public" ||
650 6 : (!osPostgisSchema.empty() &&
651 3 : osSearchPath.find(osPostgisSchema) == std::string::npos))
652 : {
653 422 : std::string osNewSearchPath;
654 422 : if (!osActiveSchema.empty() && osActiveSchema != "public")
655 : {
656 : osNewSearchPath +=
657 420 : OGRPGEscapeString(hPGConn, osActiveSchema.c_str());
658 : }
659 : // SHOW search_path reports "", but SET search_path doesn't accept it!
660 : // It must be transformed to ''
661 422 : if (STARTS_WITH(osSearchPath.c_str(), "\"\""))
662 2 : osSearchPath = "''" + osSearchPath.substr(2);
663 422 : if (!osSearchPath.empty())
664 : {
665 422 : if (!osNewSearchPath.empty())
666 420 : osNewSearchPath += ',';
667 422 : osNewSearchPath += osSearchPath;
668 : }
669 844 : if (!osPostgisSchema.empty() &&
670 422 : osSearchPath.find(osPostgisSchema) == std::string::npos)
671 : {
672 2 : if (!osNewSearchPath.empty())
673 2 : osNewSearchPath += ',';
674 : osNewSearchPath +=
675 2 : OGRPGEscapeString(hPGConn, osPostgisSchema.c_str());
676 : }
677 422 : if (osNewSearchPath != osSearchPath)
678 : {
679 420 : CPLDebug("PG", "Modifying search_path from %s to %s",
680 : osSearchPath.c_str(), osNewSearchPath.c_str());
681 :
682 420 : std::string osCommand = "SET search_path=" + osNewSearchPath;
683 420 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
684 :
685 420 : if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
686 : {
687 0 : OGRPGClearResult(hResult);
688 0 : CPLDebug("PG",
689 : "Command \"%s\" failed. Trying without 'public'.",
690 : osCommand.c_str());
691 : osCommand = CPLSPrintf(
692 : "SET search_path=%s",
693 0 : OGRPGEscapeString(hPGConn, osActiveSchema.c_str()).c_str());
694 0 : PGresult *hResult2 = OGRPG_PQexec(hPGConn, osCommand.c_str());
695 :
696 0 : if (!hResult2 || PQresultStatus(hResult2) != PGRES_COMMAND_OK)
697 : {
698 0 : OGRPGClearResult(hResult2);
699 :
700 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
701 0 : PQerrorMessage(hPGConn));
702 :
703 0 : return FALSE;
704 : }
705 : }
706 :
707 420 : OGRPGClearResult(hResult);
708 : }
709 : }
710 :
711 : /* -------------------------------------------------------------------- */
712 : /* Find out PostgreSQL version */
713 : /* -------------------------------------------------------------------- */
714 425 : sPostgreSQLVersion.nMajor = -1;
715 425 : sPostgreSQLVersion.nMinor = -1;
716 425 : sPostgreSQLVersion.nRelease = -1;
717 :
718 425 : PGresult *hResult = OGRPG_PQexec(hPGConn, "SELECT version()");
719 850 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
720 425 : PQntuples(hResult) > 0)
721 : {
722 425 : char *pszVer = PQgetvalue(hResult, 0, 0);
723 :
724 425 : CPLDebug("PG", "PostgreSQL version string : '%s'", pszVer);
725 :
726 : /* Should work with "PostgreSQL X.Y.Z ..." or "EnterpriseDB X.Y.Z ..."
727 : */
728 425 : const char *pszSpace = strchr(pszVer, ' ');
729 425 : if (pszSpace != nullptr &&
730 425 : isdigit(static_cast<unsigned char>(pszSpace[1])))
731 : {
732 425 : OGRPGDecodeVersionString(&sPostgreSQLVersion, pszSpace + 1);
733 : }
734 : }
735 425 : OGRPGClearResult(hResult);
736 425 : CPLAssert(nullptr ==
737 : hResult); /* Test if safe PQclear has not been broken */
738 :
739 : /* -------------------------------------------------------------------- */
740 : /* Set standard_conforming_strings=ON */
741 : /* -------------------------------------------------------------------- */
742 :
743 425 : hResult = OGRPG_PQexec(hPGConn, "SET standard_conforming_strings = ON");
744 425 : if (!(hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK))
745 : {
746 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
747 0 : OGRPGClearResult(hResult);
748 0 : return FALSE;
749 : }
750 425 : OGRPGClearResult(hResult);
751 :
752 : /* -------------------------------------------------------------------- */
753 : /* Test if time binary format is int8 or float8 */
754 : /* -------------------------------------------------------------------- */
755 : #if defined(BINARY_CURSOR_ENABLED)
756 : if (bUseBinaryCursor)
757 : {
758 : SoftStartTransaction();
759 :
760 : hResult =
761 : OGRPG_PQexec(hPGConn, "DECLARE gettimebinaryformat BINARY CURSOR "
762 : "FOR SELECT CAST ('00:00:01' AS time)");
763 :
764 : if (hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK)
765 : {
766 : OGRPGClearResult(hResult);
767 :
768 : hResult = OGRPG_PQexec(hPGConn, "FETCH ALL IN gettimebinaryformat");
769 :
770 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
771 : PQntuples(hResult) == 1)
772 : {
773 : if (PQfformat(hResult, 0) == 1) // Binary data representation
774 : {
775 : CPLAssert(PQgetlength(hResult, 0, 0) == 8);
776 : unsigned int nVal[2] = {0, 0};
777 : memcpy(nVal, PQgetvalue(hResult, 0, 0), 8);
778 : CPL_MSBPTR32(&nVal[0]);
779 : CPL_MSBPTR32(&nVal[1]);
780 : double dVal = 0.0;
781 : memcpy(&dVal, PQgetvalue(hResult, 0, 0), 8);
782 : CPL_MSBPTR64(&dVal);
783 : if (nVal[0] == 0 && nVal[1] == 1000000)
784 : {
785 : bBinaryTimeFormatIsInt8 = TRUE;
786 : CPLDebug("PG", "Time binary format is int8");
787 : }
788 : else if (dVal == 1.)
789 : {
790 : bBinaryTimeFormatIsInt8 = FALSE;
791 : CPLDebug("PG", "Time binary format is float8");
792 : }
793 : else
794 : {
795 : bBinaryTimeFormatIsInt8 = FALSE;
796 : CPLDebug("PG", "Time binary format is unknown");
797 : }
798 : }
799 : }
800 : }
801 :
802 : OGRPGClearResult(hResult);
803 :
804 : hResult = OGRPG_PQexec(hPGConn, "CLOSE gettimebinaryformat");
805 : OGRPGClearResult(hResult);
806 :
807 : SoftCommitTransaction();
808 : }
809 : #endif
810 :
811 : /* -------------------------------------------------------------------- */
812 : /* Test to see if this database instance has support for the */
813 : /* PostGIS Geometry type. If so, disable sequential scanning */
814 : /* so we will get the value of the gist indexes. */
815 : /* -------------------------------------------------------------------- */
816 425 : hResult =
817 425 : OGRPG_PQexec(hPGConn, "SELECT oid, typname FROM pg_type WHERE typname "
818 : "IN ('geometry', 'geography') AND typtype='b'");
819 :
820 425 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
821 1275 : PQntuples(hResult) > 0 &&
822 425 : CPLTestBool(CPLGetConfigOption("PG_USE_POSTGIS", "YES")))
823 : {
824 711 : for (int iRecord = 0; iRecord < PQntuples(hResult); iRecord++)
825 : {
826 474 : const char *pszOid = PQgetvalue(hResult, iRecord, 0);
827 474 : const char *pszTypname = PQgetvalue(hResult, iRecord, 1);
828 474 : if (EQUAL(pszTypname, "geometry"))
829 : {
830 237 : bHavePostGIS = TRUE;
831 237 : nGeometryOID = static_cast<Oid>(strtoul(pszOid, nullptr, 10));
832 : }
833 237 : else if (CPLTestBool(CPLGetConfigOption("PG_USE_GEOGRAPHY", "YES")))
834 : {
835 237 : bHaveGeography = TRUE;
836 237 : nGeographyOID = static_cast<Oid>(strtoul(pszOid, nullptr, 10));
837 : }
838 : }
839 : }
840 :
841 425 : OGRPGClearResult(hResult);
842 :
843 : /* -------------------------------------------------------------------- */
844 : /* Find out PostGIS version */
845 : /* -------------------------------------------------------------------- */
846 :
847 425 : sPostGISVersion.nMajor = -1;
848 425 : sPostGISVersion.nMinor = -1;
849 425 : sPostGISVersion.nRelease = -1;
850 :
851 425 : if (bHavePostGIS)
852 : {
853 237 : hResult = OGRPG_PQexec(hPGConn, "SELECT postgis_version()");
854 474 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
855 237 : PQntuples(hResult) > 0)
856 : {
857 237 : char *pszVer = PQgetvalue(hResult, 0, 0);
858 :
859 237 : CPLDebug("PG", "PostGIS version string : '%s'", pszVer);
860 :
861 237 : OGRPGDecodeVersionString(&sPostGISVersion, pszVer);
862 : }
863 237 : OGRPGClearResult(hResult);
864 : }
865 :
866 425 : m_bHasGeometryColumns =
867 425 : OGRPG_Check_Table_Exists(hPGConn, "geometry_columns");
868 425 : m_bHasSpatialRefSys = OGRPG_Check_Table_Exists(hPGConn, "spatial_ref_sys");
869 :
870 : /* -------------------------------------------------------------------- */
871 : /* Find out "unknown SRID" value */
872 : /* -------------------------------------------------------------------- */
873 :
874 425 : if (sPostGISVersion.nMajor >= 2)
875 : {
876 237 : hResult =
877 237 : OGRPG_PQexec(hPGConn, "SELECT ST_Srid('POINT EMPTY'::GEOMETRY)");
878 :
879 474 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
880 237 : PQntuples(hResult) > 0)
881 : {
882 237 : nUndefinedSRID = atoi(PQgetvalue(hResult, 0, 0));
883 : }
884 :
885 237 : OGRPGClearResult(hResult);
886 : }
887 : else
888 188 : nUndefinedSRID = -1;
889 :
890 425 : GetCurrentSchema();
891 :
892 850 : bListAllTables = CPLTestBool(
893 425 : CSLFetchNameValueDef(papszOpenOptions, "LIST_ALL_TABLES",
894 : CPLGetConfigOption("PG_LIST_ALL_TABLES", "NO")));
895 :
896 850 : m_bSkipViews = CPLTestBool(
897 425 : CSLFetchNameValueDef(papszOpenOptions, "SKIP_VIEWS",
898 : CPLGetConfigOption("PG_SKIP_VIEWS", "NO")));
899 :
900 425 : return TRUE;
901 : }
902 :
903 : /************************************************************************/
904 : /* LoadTables() */
905 : /************************************************************************/
906 :
907 1285 : void OGRPGDataSource::LoadTables()
908 : {
909 1285 : if (bHasLoadTables)
910 1002 : return;
911 283 : bHasLoadTables = TRUE;
912 :
913 283 : PGTableEntry **papsTables = nullptr;
914 283 : int nTableCount = 0;
915 283 : CPLHashSet *hSetTables = nullptr;
916 566 : std::set<CPLString> osRegisteredLayers;
917 :
918 317 : for (int i = 0; i < nLayers; i++)
919 : {
920 34 : osRegisteredLayers.insert(papoLayers[i]->GetName());
921 : }
922 :
923 283 : if (pszForcedTables)
924 : {
925 : const CPLStringList aosTableList(
926 20 : CSLTokenizeString2(pszForcedTables, ",", CSLT_HONOURSTRINGS));
927 :
928 22 : for (const char *pszTable : aosTableList)
929 : {
930 : // Get schema and table name
931 : const CPLStringList aosQualifiedParts(
932 24 : CSLTokenizeString2(pszTable, ".", CSLT_HONOURSTRINGS));
933 12 : const int nParts = aosQualifiedParts.size();
934 :
935 12 : if (nParts == 1 || nParts == 2)
936 : {
937 : /* Find the geometry column name if specified */
938 24 : std::string osTableName;
939 12 : std::string osGeomColumnName;
940 12 : const char *pszTablePart = aosQualifiedParts[nParts - 1];
941 12 : int j = 0;
942 210 : for (; pszTablePart[j]; ++j)
943 : {
944 200 : if (pszTablePart[j] == '\\')
945 : {
946 8 : if (pszTablePart[j + 1] == '\\' ||
947 4 : pszTablePart[j + 1] == '(')
948 : {
949 8 : ++j;
950 8 : osTableName += pszTablePart[j];
951 : }
952 : else
953 : {
954 0 : CPLError(CE_Failure, CPLE_AppDefined,
955 : "Unexpected character after backslash at "
956 : "offset %d of %s",
957 : j, pszTablePart);
958 : }
959 : }
960 192 : else if (pszTablePart[j] == '(')
961 : {
962 : // Now parse the geometry column name
963 2 : break;
964 : }
965 : else
966 : {
967 190 : osTableName += pszTablePart[j];
968 : }
969 : }
970 :
971 12 : if (pszTablePart[j] == '(')
972 : {
973 : // Now parse the geometry column name
974 2 : ++j;
975 20 : for (; pszTablePart[j]; ++j)
976 : {
977 18 : if (pszTablePart[j] == '\\')
978 : {
979 0 : if (pszTablePart[j + 1] == '\\' ||
980 0 : pszTablePart[j + 1] == '(')
981 : {
982 0 : ++j;
983 0 : osGeomColumnName += pszTablePart[j];
984 : }
985 : else
986 : {
987 0 : CPLError(CE_Failure, CPLE_AppDefined,
988 : "Unexpected character after backslash "
989 : "at offset %d of %s",
990 : j, pszTablePart);
991 : }
992 : }
993 : else
994 : {
995 18 : osGeomColumnName += pszTablePart[j];
996 : }
997 : }
998 4 : if (!osGeomColumnName.empty() &&
999 2 : osGeomColumnName.back() == ')')
1000 2 : osGeomColumnName.pop_back();
1001 : }
1002 :
1003 24 : papsTables = static_cast<PGTableEntry **>(CPLRealloc(
1004 12 : papsTables, sizeof(PGTableEntry *) * (nTableCount + 1)));
1005 24 : papsTables[nTableCount] = static_cast<PGTableEntry *>(
1006 12 : CPLCalloc(1, sizeof(PGTableEntry)));
1007 12 : if (!osGeomColumnName.empty())
1008 2 : OGRPGTableEntryAddGeomColumn(papsTables[nTableCount],
1009 : osGeomColumnName.c_str());
1010 :
1011 12 : if (nParts == 2)
1012 : {
1013 0 : papsTables[nTableCount]->pszSchemaName =
1014 0 : CPLStrdup(aosQualifiedParts[0]);
1015 0 : papsTables[nTableCount]->pszTableName =
1016 0 : CPLStrdup(osTableName.c_str());
1017 : }
1018 : else
1019 : {
1020 24 : papsTables[nTableCount]->pszSchemaName =
1021 12 : CPLStrdup(osActiveSchema.c_str());
1022 24 : papsTables[nTableCount]->pszTableName =
1023 12 : CPLStrdup(osTableName.c_str());
1024 : }
1025 12 : nTableCount++;
1026 : }
1027 : }
1028 : }
1029 :
1030 : /* -------------------------------------------------------------------- */
1031 : /* Get a list of available tables if they have not been */
1032 : /* specified through the TABLES connection string param */
1033 : /* -------------------------------------------------------------------- */
1034 283 : const char *pszAllowedRelations = m_bSkipViews ? "'r'" : "'r','v','m','f'";
1035 :
1036 283 : hSetTables = CPLHashSetNew(OGRPGHashTableEntry, OGRPGEqualTableEntry,
1037 : OGRPGFreeTableEntry);
1038 :
1039 273 : if (nTableCount == 0 && bHavePostGIS && sPostGISVersion.nMajor >= 2 &&
1040 709 : !bListAllTables &&
1041 : /* Config option mostly for comparison/debugging/etc... */
1042 153 : CPLTestBool(CPLGetConfigOption("PG_USE_POSTGIS2_OPTIM", "YES")))
1043 : {
1044 : /* --------------------------------------------------------------------
1045 : */
1046 : /* With PostGIS 2.0, the geometry_columns and geography_columns */
1047 : /* are views, based on the catalog system, that can be slow to */
1048 : /* query, so query directly the catalog system. */
1049 : /* See http://trac.osgeo.org/postgis/ticket/3092 */
1050 : /* --------------------------------------------------------------------
1051 : */
1052 152 : CPLString osCommand;
1053 304 : const char *pszConstraintDef = sPostgreSQLVersion.nMajor >= 12
1054 152 : ? "pg_get_constraintdef(s.oid)"
1055 : : "s.consrc";
1056 : osCommand.Printf(
1057 : "SELECT c.relname, n.nspname, c.relkind, a.attname, t.typname, "
1058 : "postgis_typmod_dims(a.atttypmod) dim, "
1059 : "postgis_typmod_srid(a.atttypmod) srid, "
1060 : "postgis_typmod_type(a.atttypmod)::text geomtyp, "
1061 : "array_agg(%s)::text att_constraints, a.attnotnull, "
1062 : "d.description "
1063 : "FROM pg_class c JOIN pg_attribute a ON a.attrelid=c.oid "
1064 : "JOIN pg_namespace n ON c.relnamespace = n.oid "
1065 : "AND c.relkind in (%s) AND NOT ( n.nspname = 'public' AND "
1066 : "c.relname = 'raster_columns' ) "
1067 : "JOIN pg_type t ON a.atttypid = t.oid AND (t.typname = "
1068 : "'geometry'::name OR t.typname = 'geography'::name) "
1069 : "LEFT JOIN pg_constraint s ON s.connamespace = n.oid AND "
1070 : "s.conrelid = c.oid "
1071 : "AND a.attnum = ANY (s.conkey) "
1072 : "AND (%s LIKE '%%geometrytype(%% = %%' OR %s LIKE '%%ndims(%% = "
1073 : "%%' OR %s LIKE '%%srid(%% = %%') "
1074 : "LEFT JOIN pg_description d ON d.objoid = c.oid AND d.classoid = "
1075 : "'pg_class'::regclass::oid AND d.objsubid = 0 "
1076 : "GROUP BY c.relname, n.nspname, c.relkind, a.attname, t.typname, "
1077 : "dim, srid, geomtyp, a.attnotnull, c.oid, a.attnum, d.description "
1078 : "ORDER BY c.oid, a.attnum",
1079 : pszConstraintDef, pszAllowedRelations, pszConstraintDef,
1080 152 : pszConstraintDef, pszConstraintDef);
1081 152 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
1082 :
1083 152 : if (!hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK)
1084 : {
1085 0 : OGRPGClearResult(hResult);
1086 :
1087 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
1088 0 : PQerrorMessage(hPGConn));
1089 0 : goto end;
1090 : }
1091 : /* --------------------------------------------------------------------
1092 : */
1093 : /* Parse the returned table list */
1094 : /* --------------------------------------------------------------------
1095 : */
1096 228 : for (int iRecord = 0; iRecord < PQntuples(hResult); iRecord++)
1097 : {
1098 76 : const char *pszTable = PQgetvalue(hResult, iRecord, 0);
1099 76 : const char *pszSchemaName = PQgetvalue(hResult, iRecord, 1);
1100 76 : const char *pszGeomColumnName = PQgetvalue(hResult, iRecord, 3);
1101 76 : const char *pszGeomOrGeography = PQgetvalue(hResult, iRecord, 4);
1102 76 : const char *pszDim = PQgetvalue(hResult, iRecord, 5);
1103 76 : const char *pszSRID = PQgetvalue(hResult, iRecord, 6);
1104 76 : const char *pszGeomType = PQgetvalue(hResult, iRecord, 7);
1105 76 : const char *pszConstraint = PQgetvalue(hResult, iRecord, 8);
1106 76 : const char *pszNotNull = PQgetvalue(hResult, iRecord, 9);
1107 76 : const char *pszDescription = PQgetvalue(hResult, iRecord, 10);
1108 : /*const char *pszRelkind = PQgetvalue(hResult, iRecord, 2);
1109 : CPLDebug("PG", "%s %s %s %s %s %s %s %s %s %s",
1110 : pszTable, pszSchemaName, pszRelkind,
1111 : pszGeomColumnName, pszGeomOrGeography, pszDim,
1112 : pszSRID, pszGeomType, pszConstraint, pszNotNull);*/
1113 :
1114 76 : int bNullable = EQUAL(pszNotNull, "f");
1115 :
1116 76 : PostgisType ePostgisType = GEOM_TYPE_UNKNOWN;
1117 76 : if (EQUAL(pszGeomOrGeography, "geometry"))
1118 71 : ePostgisType = GEOM_TYPE_GEOMETRY;
1119 5 : else if (EQUAL(pszGeomOrGeography, "geography"))
1120 5 : ePostgisType = GEOM_TYPE_GEOGRAPHY;
1121 :
1122 76 : int nGeomCoordDimension = atoi(pszDim);
1123 76 : bool bHasM = pszGeomType[strlen(pszGeomType) - 1] == 'M';
1124 76 : int nSRID = atoi(pszSRID);
1125 :
1126 : /* Analyze constraints that might override geometrytype, */
1127 : /* coordinate dimension and SRID */
1128 152 : CPLString osConstraint(pszConstraint);
1129 76 : osConstraint = osConstraint.tolower();
1130 76 : pszConstraint = osConstraint.c_str();
1131 76 : const char *pszNeedle = strstr(pszConstraint, "geometrytype(");
1132 152 : CPLString osGeometryType;
1133 76 : if (pszNeedle)
1134 : {
1135 4 : pszNeedle = strchr(pszNeedle, '\'');
1136 4 : if (pszNeedle)
1137 : {
1138 4 : pszNeedle++;
1139 4 : const char *pszEnd = strchr(pszNeedle, '\'');
1140 4 : if (pszEnd)
1141 : {
1142 4 : osGeometryType = pszNeedle;
1143 4 : osGeometryType.resize(pszEnd - pszNeedle);
1144 4 : pszGeomType = osGeometryType.c_str();
1145 4 : bHasM = pszGeomType[strlen(pszGeomType) - 1] == 'M';
1146 : }
1147 : }
1148 : }
1149 :
1150 76 : pszNeedle = strstr(pszConstraint, "srid(");
1151 76 : if (pszNeedle)
1152 : {
1153 4 : pszNeedle = strchr(pszNeedle, '=');
1154 4 : if (pszNeedle)
1155 : {
1156 4 : pszNeedle++;
1157 4 : nSRID = atoi(pszNeedle);
1158 : }
1159 : }
1160 :
1161 76 : pszNeedle = strstr(pszConstraint, "ndims(");
1162 76 : if (pszNeedle)
1163 : {
1164 4 : pszNeedle = strchr(pszNeedle, '=');
1165 4 : if (pszNeedle)
1166 : {
1167 4 : pszNeedle++;
1168 4 : nGeomCoordDimension = atoi(pszNeedle);
1169 : }
1170 : }
1171 :
1172 76 : int GeomTypeFlags = 0;
1173 76 : if (nGeomCoordDimension == 3)
1174 : {
1175 26 : if (bHasM)
1176 0 : GeomTypeFlags |= OGRGeometry::OGR_G_MEASURED;
1177 : else
1178 26 : GeomTypeFlags |= OGRGeometry::OGR_G_3D;
1179 : }
1180 50 : else if (nGeomCoordDimension == 4)
1181 : {
1182 3 : GeomTypeFlags |=
1183 : OGRGeometry::OGR_G_3D | OGRGeometry::OGR_G_MEASURED;
1184 : }
1185 :
1186 152 : papsTables = static_cast<PGTableEntry **>(CPLRealloc(
1187 76 : papsTables, sizeof(PGTableEntry *) * (nTableCount + 1)));
1188 152 : papsTables[nTableCount] =
1189 76 : static_cast<PGTableEntry *>(CPLCalloc(1, sizeof(PGTableEntry)));
1190 76 : papsTables[nTableCount]->pszTableName = CPLStrdup(pszTable);
1191 76 : papsTables[nTableCount]->pszSchemaName = CPLStrdup(pszSchemaName);
1192 152 : papsTables[nTableCount]->pszDescription =
1193 76 : CPLStrdup(pszDescription ? pszDescription : "");
1194 :
1195 76 : OGRPGTableEntryAddGeomColumn(
1196 76 : papsTables[nTableCount], pszGeomColumnName, pszGeomType,
1197 : GeomTypeFlags, nSRID, ePostgisType, bNullable);
1198 76 : nTableCount++;
1199 :
1200 : PGTableEntry *psEntry =
1201 76 : OGRPGFindTableEntry(hSetTables, pszTable, pszSchemaName);
1202 76 : if (psEntry == nullptr)
1203 62 : psEntry = OGRPGAddTableEntry(hSetTables, pszTable,
1204 : pszSchemaName, pszDescription);
1205 76 : OGRPGTableEntryAddGeomColumn(psEntry, pszGeomColumnName,
1206 : pszGeomType, GeomTypeFlags, nSRID,
1207 : ePostgisType, bNullable);
1208 : }
1209 :
1210 152 : OGRPGClearResult(hResult);
1211 : }
1212 131 : else if (nTableCount == 0)
1213 : {
1214 121 : CPLString osCommand;
1215 :
1216 : /* Caution : in PostGIS case, the result has 11 columns, whereas in the
1217 : */
1218 : /* non-PostGIS case it has only 3 columns */
1219 121 : if (bHavePostGIS && !bListAllTables)
1220 : {
1221 : osCommand.Printf(
1222 : "SELECT c.relname, n.nspname, g.f_geometry_column, "
1223 : "g.type, g.coord_dimension, g.srid, %d, a.attnotnull, "
1224 : "d.description, c.oid as oid, a.attnum as attnum "
1225 : "FROM pg_class c "
1226 : "JOIN pg_namespace n ON c.relnamespace=n.oid "
1227 : "JOIN geometry_columns g "
1228 : "ON c.relname::TEXT = g.f_table_name::TEXT AND n.nspname = "
1229 : "g.f_table_schema "
1230 : "JOIN pg_attribute a "
1231 : "ON a.attname = g.f_geometry_column AND a.attrelid = c.oid "
1232 : "LEFT JOIN pg_description d "
1233 : "ON d.objoid = c.oid AND d.classoid = "
1234 : "'pg_class'::regclass::oid AND d.objsubid = 0 "
1235 : "WHERE c.relkind in (%s) ",
1236 1 : GEOM_TYPE_GEOMETRY, pszAllowedRelations);
1237 :
1238 1 : if (bHaveGeography)
1239 2 : osCommand += CPLString().Printf(
1240 : "UNION SELECT c.relname, n.nspname, "
1241 : "g.f_geography_column, "
1242 : "g.type, g.coord_dimension, g.srid, %d, a.attnotnull, "
1243 : "d.description, c.oid as oid, a.attnum as attnum "
1244 : "FROM pg_class c "
1245 : "JOIN pg_namespace n ON c.relnamespace=n.oid "
1246 : "JOIN geography_columns g "
1247 : "ON c.relname::TEXT = g.f_table_name::TEXT AND n.nspname = "
1248 : "g.f_table_schema "
1249 : "JOIN pg_attribute a "
1250 : "ON a.attname = g.f_geography_column AND a.attrelid = "
1251 : "c.oid "
1252 : "LEFT JOIN pg_description d "
1253 : "ON d.objoid = c.oid AND d.classoid = "
1254 : "'pg_class'::regclass::oid AND d.objsubid = 0 "
1255 : "WHERE c.relkind in (%s)",
1256 1 : GEOM_TYPE_GEOGRAPHY, pszAllowedRelations);
1257 1 : osCommand += " ORDER BY oid, attnum";
1258 : }
1259 : else
1260 : osCommand.Printf("SELECT c.relname, n.nspname, d.description FROM "
1261 : "pg_class c "
1262 : "JOIN pg_namespace n ON c.relnamespace=n.oid "
1263 : "LEFT JOIN pg_description d "
1264 : "ON d.objoid = c.oid AND d.classoid = "
1265 : "'pg_class'::regclass::oid AND d.objsubid = 0 "
1266 : "WHERE (c.relkind in (%s) AND "
1267 : "c.relname !~ '^pg_')",
1268 120 : pszAllowedRelations);
1269 :
1270 121 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
1271 :
1272 121 : if (!hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK)
1273 : {
1274 0 : OGRPGClearResult(hResult);
1275 :
1276 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
1277 0 : PQerrorMessage(hPGConn));
1278 0 : goto end;
1279 : }
1280 :
1281 : /* --------------------------------------------------------------------
1282 : */
1283 : /* Parse the returned table list */
1284 : /* --------------------------------------------------------------------
1285 : */
1286 8554 : for (int iRecord = 0; iRecord < PQntuples(hResult); iRecord++)
1287 : {
1288 8433 : const char *pszTable = PQgetvalue(hResult, iRecord, 0);
1289 8433 : const char *pszSchemaName = PQgetvalue(hResult, iRecord, 1);
1290 8433 : const char *pszGeomColumnName = nullptr;
1291 8433 : const char *pszGeomType = nullptr;
1292 8433 : const char *pszDescription = nullptr;
1293 8433 : int nGeomCoordDimension = 0;
1294 8433 : bool bHasM = false;
1295 8433 : int nSRID = 0;
1296 8433 : int bNullable = TRUE;
1297 8433 : PostgisType ePostgisType = GEOM_TYPE_UNKNOWN;
1298 8433 : if (bHavePostGIS && !bListAllTables)
1299 : {
1300 2 : pszGeomColumnName = PQgetvalue(hResult, iRecord, 2);
1301 2 : pszGeomType = PQgetvalue(hResult, iRecord, 3);
1302 2 : bHasM = pszGeomType[strlen(pszGeomType) - 1] == 'M';
1303 2 : nGeomCoordDimension = atoi(PQgetvalue(hResult, iRecord, 4));
1304 2 : nSRID = atoi(PQgetvalue(hResult, iRecord, 5));
1305 2 : ePostgisType = static_cast<PostgisType>(
1306 2 : atoi(PQgetvalue(hResult, iRecord, 6)));
1307 2 : bNullable = EQUAL(PQgetvalue(hResult, iRecord, 7), "f");
1308 2 : pszDescription = PQgetvalue(hResult, iRecord, 8);
1309 : }
1310 : else
1311 : {
1312 8431 : pszDescription = PQgetvalue(hResult, iRecord, 2);
1313 : }
1314 :
1315 8433 : if (EQUAL(pszTable, "spatial_ref_sys") ||
1316 8313 : EQUAL(pszTable, "geometry_columns") ||
1317 8195 : EQUAL(pszTable, "geography_columns"))
1318 356 : continue;
1319 :
1320 8077 : if (EQUAL(pszSchemaName, "information_schema") ||
1321 399 : EQUAL(pszSchemaName, "ogr_system_tables"))
1322 7789 : continue;
1323 :
1324 288 : int GeomTypeFlags = 0;
1325 288 : if (nGeomCoordDimension == 3)
1326 : {
1327 1 : if (bHasM)
1328 0 : GeomTypeFlags |= OGRGeometry::OGR_G_MEASURED;
1329 : else
1330 1 : GeomTypeFlags |= OGRGeometry::OGR_G_3D;
1331 : }
1332 287 : else if (nGeomCoordDimension == 4)
1333 : {
1334 0 : GeomTypeFlags |=
1335 : OGRGeometry::OGR_G_3D | OGRGeometry::OGR_G_MEASURED;
1336 : }
1337 :
1338 576 : papsTables = static_cast<PGTableEntry **>(CPLRealloc(
1339 288 : papsTables, sizeof(PGTableEntry *) * (nTableCount + 1)));
1340 576 : papsTables[nTableCount] =
1341 288 : static_cast<PGTableEntry *>(CPLCalloc(1, sizeof(PGTableEntry)));
1342 288 : papsTables[nTableCount]->pszTableName = CPLStrdup(pszTable);
1343 288 : papsTables[nTableCount]->pszSchemaName = CPLStrdup(pszSchemaName);
1344 576 : papsTables[nTableCount]->pszDescription =
1345 288 : CPLStrdup(pszDescription ? pszDescription : "");
1346 288 : if (pszGeomColumnName)
1347 2 : OGRPGTableEntryAddGeomColumn(
1348 2 : papsTables[nTableCount], pszGeomColumnName, pszGeomType,
1349 : GeomTypeFlags, nSRID, ePostgisType, bNullable);
1350 288 : nTableCount++;
1351 :
1352 : PGTableEntry *psEntry =
1353 288 : OGRPGFindTableEntry(hSetTables, pszTable, pszSchemaName);
1354 288 : if (psEntry == nullptr)
1355 288 : psEntry = OGRPGAddTableEntry(hSetTables, pszTable,
1356 : pszSchemaName, pszDescription);
1357 288 : if (pszGeomColumnName)
1358 2 : OGRPGTableEntryAddGeomColumn(psEntry, pszGeomColumnName,
1359 : pszGeomType, GeomTypeFlags, nSRID,
1360 : ePostgisType, bNullable);
1361 : }
1362 :
1363 : /* --------------------------------------------------------------------
1364 : */
1365 : /* Cleanup */
1366 : /* --------------------------------------------------------------------
1367 : */
1368 121 : OGRPGClearResult(hResult);
1369 : }
1370 :
1371 : /* -------------------------------------------------------------------- */
1372 : /* Register the available tables. */
1373 : /* -------------------------------------------------------------------- */
1374 659 : for (int iRecord = 0; iRecord < nTableCount; iRecord++)
1375 : {
1376 : const PGTableEntry *psEntry = static_cast<PGTableEntry *>(
1377 376 : CPLHashSetLookup(hSetTables, papsTables[iRecord]));
1378 :
1379 : /* If SCHEMAS= is specified, only take into account tables inside */
1380 : /* one of the specified schemas */
1381 746 : if (papszSchemaList != nullptr &&
1382 370 : CSLFindString(papszSchemaList,
1383 370 : papsTables[iRecord]->pszSchemaName) == -1)
1384 : {
1385 282 : continue;
1386 : }
1387 :
1388 132 : CPLString osDefnName;
1389 :
1390 264 : if (papsTables[iRecord]->pszSchemaName &&
1391 132 : osCurrentSchema != papsTables[iRecord]->pszSchemaName)
1392 : {
1393 6 : osDefnName.Printf("%s.%s", papsTables[iRecord]->pszSchemaName,
1394 6 : papsTables[iRecord]->pszTableName);
1395 : }
1396 : else
1397 : {
1398 : // no prefix for current_schema in layer name, for backwards
1399 : // compatibility
1400 126 : osDefnName = papsTables[iRecord]->pszTableName;
1401 : }
1402 132 : if (osRegisteredLayers.find(osDefnName) != osRegisteredLayers.end())
1403 38 : continue;
1404 94 : osRegisteredLayers.insert(std::move(osDefnName));
1405 :
1406 188 : OGRPGTableLayer *poLayer = OpenTable(
1407 94 : osCurrentSchema, papsTables[iRecord]->pszTableName,
1408 94 : papsTables[iRecord]->pszSchemaName,
1409 94 : papsTables[iRecord]->pszDescription, nullptr, bDSUpdate, FALSE);
1410 94 : if (psEntry != nullptr)
1411 : {
1412 82 : if (psEntry->nGeomColumnCount > 0)
1413 : {
1414 44 : poLayer->SetGeometryInformation(psEntry->pasGeomColumns,
1415 44 : psEntry->nGeomColumnCount);
1416 : }
1417 : }
1418 : else
1419 : {
1420 12 : if (papsTables[iRecord]->nGeomColumnCount > 0)
1421 : {
1422 2 : poLayer->SetGeometryInformation(
1423 2 : papsTables[iRecord]->pasGeomColumns,
1424 2 : papsTables[iRecord]->nGeomColumnCount);
1425 : }
1426 : }
1427 : }
1428 :
1429 283 : end:
1430 283 : if (hSetTables)
1431 283 : CPLHashSetDestroy(hSetTables);
1432 :
1433 659 : for (int i = 0; i < nTableCount; i++)
1434 376 : OGRPGFreeTableEntry(papsTables[i]);
1435 283 : CPLFree(papsTables);
1436 : }
1437 :
1438 : /************************************************************************/
1439 : /* OpenTable() */
1440 : /************************************************************************/
1441 :
1442 510 : OGRPGTableLayer *OGRPGDataSource::OpenTable(CPLString &osCurrentSchemaIn,
1443 : const char *pszNewName,
1444 : const char *pszSchemaName,
1445 : const char *pszDescription,
1446 : const char *pszGeomColumnForced,
1447 : int bUpdate, int bTestOpen)
1448 :
1449 : {
1450 : /* -------------------------------------------------------------------- */
1451 : /* Create the layer object. */
1452 : /* -------------------------------------------------------------------- */
1453 : OGRPGTableLayer *poLayer =
1454 : new OGRPGTableLayer(this, osCurrentSchemaIn, pszNewName, pszSchemaName,
1455 510 : pszDescription, pszGeomColumnForced, bUpdate);
1456 510 : if (bTestOpen && !(poLayer->ReadTableDefinition()))
1457 : {
1458 233 : delete poLayer;
1459 233 : return nullptr;
1460 : }
1461 :
1462 : /* -------------------------------------------------------------------- */
1463 : /* Add layer to data source layer list. */
1464 : /* -------------------------------------------------------------------- */
1465 277 : papoLayers = static_cast<OGRPGTableLayer **>(
1466 277 : CPLRealloc(papoLayers, sizeof(OGRPGTableLayer *) * (nLayers + 1)));
1467 277 : papoLayers[nLayers++] = poLayer;
1468 :
1469 277 : return poLayer;
1470 : }
1471 :
1472 : /************************************************************************/
1473 : /* DeleteLayer() */
1474 : /************************************************************************/
1475 :
1476 24 : OGRErr OGRPGDataSource::DeleteLayer(int iLayer)
1477 :
1478 : {
1479 : /* Force loading of all registered tables */
1480 24 : GetLayerCount();
1481 24 : if (iLayer < 0 || iLayer >= nLayers)
1482 0 : return OGRERR_FAILURE;
1483 :
1484 24 : EndCopy();
1485 :
1486 : /* -------------------------------------------------------------------- */
1487 : /* Blow away our OGR structures related to the layer. This is */
1488 : /* pretty dangerous if anything has a reference to this layer! */
1489 : /* -------------------------------------------------------------------- */
1490 48 : CPLString osLayerName = papoLayers[iLayer]->GetLayerDefn()->GetName();
1491 48 : CPLString osTableName = papoLayers[iLayer]->GetTableName();
1492 48 : CPLString osSchemaName = papoLayers[iLayer]->GetSchemaName();
1493 :
1494 24 : CPLDebug("PG", "DeleteLayer(%s)", osLayerName.c_str());
1495 :
1496 24 : delete papoLayers[iLayer];
1497 24 : memmove(papoLayers + iLayer, papoLayers + iLayer + 1,
1498 24 : sizeof(void *) * (nLayers - iLayer - 1));
1499 24 : nLayers--;
1500 :
1501 24 : if (osLayerName.empty())
1502 0 : return OGRERR_NONE;
1503 :
1504 : /* -------------------------------------------------------------------- */
1505 : /* Remove from the database. */
1506 : /* -------------------------------------------------------------------- */
1507 24 : CPLString osCommand;
1508 :
1509 24 : SoftStartTransaction();
1510 :
1511 : osCommand.Printf("DROP TABLE %s.%s CASCADE",
1512 48 : OGRPGEscapeColumnName(osSchemaName).c_str(),
1513 72 : OGRPGEscapeColumnName(osTableName).c_str());
1514 24 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
1515 24 : OGRPGClearResult(hResult);
1516 :
1517 24 : SoftCommitTransaction();
1518 :
1519 24 : return OGRERR_NONE;
1520 : }
1521 :
1522 : /************************************************************************/
1523 : /* ICreateLayer() */
1524 : /************************************************************************/
1525 :
1526 232 : OGRLayer *OGRPGDataSource::ICreateLayer(const char *pszLayerName,
1527 : const OGRGeomFieldDefn *poGeomFieldDefn,
1528 : CSLConstList papszOptions)
1529 :
1530 : {
1531 232 : const char *pszGeomType = nullptr;
1532 232 : char *pszTableName = nullptr;
1533 232 : char *pszSchemaName = nullptr;
1534 232 : int GeometryTypeFlags = 0;
1535 :
1536 232 : auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
1537 : const auto poSRS =
1538 232 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
1539 :
1540 232 : if (pszLayerName == nullptr)
1541 0 : return nullptr;
1542 :
1543 232 : EndCopy();
1544 :
1545 : const bool bUTF8ToASCII =
1546 232 : CPLFetchBool(papszOptions, "LAUNDER_ASCII", false);
1547 : const bool bLaunder =
1548 232 : bUTF8ToASCII || CPLFetchBool(papszOptions, "LAUNDER", true);
1549 :
1550 232 : const char *pszFIDColumnNameIn = CSLFetchNameValue(papszOptions, "FID");
1551 464 : CPLString osFIDColumnName;
1552 232 : if (pszFIDColumnNameIn == nullptr)
1553 230 : osFIDColumnName = "ogc_fid";
1554 : else
1555 : {
1556 2 : if (bLaunder)
1557 : {
1558 : char *pszLaunderedFid =
1559 2 : OGRPGCommonLaunderName(pszFIDColumnNameIn, "PG", bUTF8ToASCII);
1560 2 : osFIDColumnName += pszLaunderedFid;
1561 2 : CPLFree(pszLaunderedFid);
1562 : }
1563 : else
1564 0 : osFIDColumnName += pszFIDColumnNameIn;
1565 : }
1566 464 : CPLString osFIDColumnNameEscaped = OGRPGEscapeColumnName(osFIDColumnName);
1567 :
1568 232 : if (STARTS_WITH(pszLayerName, "pg"))
1569 : {
1570 0 : CPLError(CE_Warning, CPLE_AppDefined,
1571 : "The layer name should not begin by 'pg' as it is a reserved "
1572 : "prefix");
1573 : }
1574 :
1575 232 : if (OGR_GT_HasZ(eType))
1576 5 : GeometryTypeFlags |= OGRGeometry::OGR_G_3D;
1577 232 : if (OGR_GT_HasM(eType))
1578 2 : GeometryTypeFlags |= OGRGeometry::OGR_G_MEASURED;
1579 :
1580 232 : int ForcedGeometryTypeFlags = -1;
1581 232 : const char *pszDim = CSLFetchNameValue(papszOptions, "DIM");
1582 232 : if (pszDim != nullptr)
1583 : {
1584 75 : if (EQUAL(pszDim, "XY") || EQUAL(pszDim, "2"))
1585 : {
1586 0 : GeometryTypeFlags = 0;
1587 0 : ForcedGeometryTypeFlags = GeometryTypeFlags;
1588 : }
1589 75 : else if (EQUAL(pszDim, "XYZ") || EQUAL(pszDim, "3"))
1590 : {
1591 69 : GeometryTypeFlags = OGRGeometry::OGR_G_3D;
1592 69 : ForcedGeometryTypeFlags = GeometryTypeFlags;
1593 : }
1594 6 : else if (EQUAL(pszDim, "XYM"))
1595 : {
1596 3 : GeometryTypeFlags = OGRGeometry::OGR_G_MEASURED;
1597 3 : ForcedGeometryTypeFlags = GeometryTypeFlags;
1598 : }
1599 3 : else if (EQUAL(pszDim, "XYZM") || EQUAL(pszDim, "4"))
1600 : {
1601 3 : GeometryTypeFlags =
1602 : OGRGeometry::OGR_G_3D | OGRGeometry::OGR_G_MEASURED;
1603 3 : ForcedGeometryTypeFlags = GeometryTypeFlags;
1604 : }
1605 : else
1606 : {
1607 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for DIM");
1608 : }
1609 : }
1610 :
1611 : /* Should we turn layers with None geometry type as Unknown/GEOMETRY */
1612 : /* so they are still recorded in geometry_columns table ? (#4012) */
1613 232 : int bNoneAsUnknown = CPLTestBool(
1614 232 : CSLFetchNameValueDef(papszOptions, "NONE_AS_UNKNOWN", "NO"));
1615 232 : if (bNoneAsUnknown && eType == wkbNone)
1616 0 : eType = wkbUnknown;
1617 :
1618 232 : int bExtractSchemaFromLayerName = CPLTestBool(CSLFetchNameValueDef(
1619 232 : papszOptions, "EXTRACT_SCHEMA_FROM_LAYER_NAME", "YES"));
1620 :
1621 : /* Postgres Schema handling:
1622 : Extract schema name from input layer name or passed with -lco SCHEMA.
1623 : Set layer name to "schema.table" or to "table" if schema ==
1624 : current_schema() Usage without schema name is backwards compatible
1625 : */
1626 232 : const char *pszDotPos = strstr(pszLayerName, ".");
1627 232 : if (pszDotPos != nullptr && bExtractSchemaFromLayerName)
1628 : {
1629 19 : int length = static_cast<int>(pszDotPos - pszLayerName);
1630 19 : pszSchemaName = static_cast<char *>(CPLMalloc(length + 1));
1631 19 : strncpy(pszSchemaName, pszLayerName, length);
1632 19 : pszSchemaName[length] = '\0';
1633 :
1634 19 : if (bLaunder)
1635 17 : pszTableName = OGRPGCommonLaunderName(pszDotPos + 1, "PG",
1636 : bUTF8ToASCII); // skip "."
1637 : else
1638 2 : pszTableName = CPLStrdup(
1639 4 : OGRPGCommonGenerateShortEnoughIdentifier(pszDotPos + 1)
1640 19 : .c_str()); // skip "."
1641 : }
1642 : else
1643 : {
1644 213 : pszSchemaName = nullptr;
1645 213 : if (bLaunder)
1646 213 : pszTableName = OGRPGCommonLaunderName(pszLayerName, "PG",
1647 : bUTF8ToASCII); // skip "."
1648 : else
1649 : pszTableName =
1650 0 : CPLStrdup(OGRPGCommonGenerateShortEnoughIdentifier(pszLayerName)
1651 : .c_str()); // skip "."
1652 : }
1653 :
1654 : /* -------------------------------------------------------------------- */
1655 : /* Set the default schema for the layers. */
1656 : /* -------------------------------------------------------------------- */
1657 232 : if (CSLFetchNameValue(papszOptions, "SCHEMA") != nullptr)
1658 : {
1659 3 : CPLFree(pszSchemaName);
1660 3 : pszSchemaName = CPLStrdup(CSLFetchNameValue(papszOptions, "SCHEMA"));
1661 : }
1662 :
1663 232 : const bool bTemporary = CPLFetchBool(papszOptions, "TEMPORARY", false);
1664 232 : if (bTemporary)
1665 : {
1666 1 : CPLFree(pszSchemaName);
1667 1 : pszSchemaName = CPLStrdup("pg_temp");
1668 : }
1669 232 : if (pszSchemaName == nullptr)
1670 : {
1671 211 : pszSchemaName = CPLStrdup(osCurrentSchema);
1672 : }
1673 :
1674 : // Check that the schema exist. If there is a single match in a case
1675 : // insensitive way, use it. Otherwise error out if the match is not exact.
1676 : {
1677 232 : const auto osNewSchemaName = FindSchema(pszSchemaName);
1678 232 : if (!osNewSchemaName.has_value())
1679 : {
1680 4 : CPLFree(pszTableName);
1681 4 : CPLFree(pszSchemaName);
1682 4 : return nullptr;
1683 : }
1684 228 : CPLFree(pszSchemaName);
1685 228 : pszSchemaName = CPLStrdup(osNewSchemaName->c_str());
1686 : }
1687 :
1688 : /* -------------------------------------------------------------------- */
1689 : /* Do we already have this layer? If so, should we blow it */
1690 : /* away? */
1691 : /* -------------------------------------------------------------------- */
1692 456 : CPLString osSQLLayerName;
1693 456 : if (pszSchemaName == nullptr ||
1694 228 : (!osCurrentSchema.empty() &&
1695 228 : EQUAL(pszSchemaName, osCurrentSchema.c_str())))
1696 211 : osSQLLayerName = pszTableName;
1697 : else
1698 : {
1699 17 : osSQLLayerName = pszSchemaName;
1700 17 : osSQLLayerName += ".";
1701 17 : osSQLLayerName += pszTableName;
1702 : }
1703 :
1704 : /* GetLayerByName() can instantiate layers that would have been */
1705 : /* 'hidden' otherwise, for example, non-spatial tables in a */
1706 : /* PostGIS-enabled database, so this apparently useless command is */
1707 : /* not useless. (#4012) */
1708 228 : CPLPushErrorHandler(CPLQuietErrorHandler);
1709 228 : GetLayerByName(osSQLLayerName);
1710 228 : CPLPopErrorHandler();
1711 228 : CPLErrorReset();
1712 :
1713 : /* Force loading of all registered tables */
1714 228 : GetLayerCount();
1715 :
1716 316 : for (int iLayer = 0; iLayer < nLayers; iLayer++)
1717 : {
1718 90 : if (EQUAL(osSQLLayerName.c_str(), papoLayers[iLayer]->GetName()))
1719 : {
1720 30 : if (CSLFetchNameValue(papszOptions, "OVERWRITE") != nullptr &&
1721 14 : !EQUAL(CSLFetchNameValue(papszOptions, "OVERWRITE"), "NO"))
1722 : {
1723 14 : DeleteLayer(iLayer);
1724 : }
1725 : else
1726 : {
1727 2 : CPLError(CE_Failure, CPLE_AppDefined,
1728 : "Layer %s already exists, CreateLayer failed.\n"
1729 : "Use the layer creation option OVERWRITE=YES to "
1730 : "replace it.",
1731 : osSQLLayerName.c_str());
1732 2 : CPLFree(pszTableName);
1733 2 : CPLFree(pszSchemaName);
1734 2 : return nullptr;
1735 : }
1736 : }
1737 : }
1738 :
1739 : /* -------------------------------------------------------------------- */
1740 : /* Handle the GEOM_TYPE option. */
1741 : /* -------------------------------------------------------------------- */
1742 226 : pszGeomType = CSLFetchNameValue(papszOptions, "GEOM_TYPE");
1743 226 : if (pszGeomType == nullptr)
1744 : {
1745 220 : if (bHavePostGIS)
1746 130 : pszGeomType = "geometry";
1747 : else
1748 90 : pszGeomType = "bytea";
1749 : }
1750 :
1751 226 : const char *pszGFldName = CSLFetchNameValue(papszOptions, "GEOMETRY_NAME");
1752 226 : if (eType != wkbNone && EQUAL(pszGeomType, "geography"))
1753 : {
1754 5 : if (!bHaveGeography)
1755 : {
1756 0 : CPLError(
1757 : CE_Failure, CPLE_AppDefined,
1758 : "GEOM_TYPE=geography is only supported in PostGIS >= 1.5.\n"
1759 : "Creation of layer %s has failed.",
1760 : pszLayerName);
1761 0 : CPLFree(pszTableName);
1762 0 : CPLFree(pszSchemaName);
1763 0 : return nullptr;
1764 : }
1765 :
1766 5 : if (pszGFldName == nullptr)
1767 2 : pszGFldName = "the_geog";
1768 : }
1769 221 : else if (eType != wkbNone && bHavePostGIS &&
1770 110 : !EQUAL(pszGeomType, "geography"))
1771 : {
1772 110 : if (pszGFldName == nullptr)
1773 108 : pszGFldName = "wkb_geometry";
1774 : }
1775 :
1776 226 : if (eType != wkbNone && bHavePostGIS && !EQUAL(pszGeomType, "geometry") &&
1777 5 : !EQUAL(pszGeomType, "geography"))
1778 : {
1779 0 : if (bHaveGeography)
1780 0 : CPLError(CE_Failure, CPLE_AppDefined,
1781 : "GEOM_TYPE in PostGIS enabled databases must be "
1782 : "'geometry' or 'geography'.\n"
1783 : "Creation of layer %s with GEOM_TYPE %s has failed.",
1784 : pszLayerName, pszGeomType);
1785 : else
1786 0 : CPLError(
1787 : CE_Failure, CPLE_AppDefined,
1788 : "GEOM_TYPE in PostGIS enabled databases must be 'geometry'.\n"
1789 : "Creation of layer %s with GEOM_TYPE %s has failed.",
1790 : pszLayerName, pszGeomType);
1791 :
1792 0 : CPLFree(pszTableName);
1793 0 : CPLFree(pszSchemaName);
1794 0 : return nullptr;
1795 : }
1796 :
1797 : /* -------------------------------------------------------------------- */
1798 : /* Try to get the SRS Id of this spatial reference system, */
1799 : /* adding tot the srs table if needed. */
1800 : /* -------------------------------------------------------------------- */
1801 226 : int nSRSId = nUndefinedSRID;
1802 :
1803 226 : if (poSRS != nullptr)
1804 12 : nSRSId = FetchSRSId(poSRS);
1805 :
1806 226 : if (eType != wkbNone && EQUAL(pszGeomType, "geography"))
1807 : {
1808 5 : if (poSRS != nullptr && !poSRS->IsGeographic())
1809 : {
1810 1 : CPLError(CE_Failure, CPLE_NotSupported,
1811 : "geography type only supports geographic SRS");
1812 :
1813 1 : CPLFree(pszTableName);
1814 1 : CPLFree(pszSchemaName);
1815 1 : return nullptr;
1816 : }
1817 :
1818 4 : if (!(sPostGISVersion.nMajor >= 3 ||
1819 0 : (sPostGISVersion.nMajor == 2 && sPostGISVersion.nMinor >= 2)) &&
1820 0 : nSRSId != nUndefinedSRID && nSRSId != 4326)
1821 : {
1822 0 : CPLError(CE_Failure, CPLE_NotSupported,
1823 : "geography type in PostGIS < 2.2 only supports SRS = "
1824 : "EPSG:4326");
1825 :
1826 0 : CPLFree(pszTableName);
1827 0 : CPLFree(pszSchemaName);
1828 0 : return nullptr;
1829 : }
1830 :
1831 4 : if (nSRSId == nUndefinedSRID)
1832 : {
1833 3 : CPLError(CE_Warning, CPLE_AppDefined,
1834 : "Assuming EPSG:4326 for geographic type (but no implicit "
1835 : "reprojection to it will be done)");
1836 3 : nSRSId = 4326;
1837 : }
1838 : }
1839 :
1840 225 : const char *pszGeometryType = OGRToOGCGeomType(eType);
1841 :
1842 : int bDeferredCreation =
1843 225 : CPLTestBool(CPLGetConfigOption("OGR_PG_DEFERRED_CREATION", "YES"));
1844 225 : if (!bHavePostGIS)
1845 90 : bDeferredCreation =
1846 : FALSE; /* to avoid unnecessary implementation and testing burden */
1847 :
1848 : /* -------------------------------------------------------------------- */
1849 : /* Create a basic table with the FID. Also include the */
1850 : /* geometry if this is not a PostGIS enabled table. */
1851 : /* -------------------------------------------------------------------- */
1852 225 : const bool bFID64 = CPLFetchBool(papszOptions, "FID64", false);
1853 225 : const char *pszSerialType = bFID64 ? "BIGSERIAL" : "SERIAL";
1854 :
1855 450 : CPLString osCreateTable;
1856 225 : if (bTemporary)
1857 : {
1858 : osCreateTable.Printf("CREATE TEMPORARY TABLE %s",
1859 1 : OGRPGEscapeColumnName(pszTableName).c_str());
1860 : }
1861 : else
1862 : {
1863 : osCreateTable.Printf(
1864 : "CREATE%s TABLE %s.%s",
1865 224 : CPLFetchBool(papszOptions, "UNLOGGED", false) ? " UNLOGGED" : "",
1866 448 : OGRPGEscapeColumnName(pszSchemaName).c_str(),
1867 896 : OGRPGEscapeColumnName(pszTableName).c_str());
1868 : }
1869 :
1870 225 : const char *suffix = nullptr;
1871 225 : if ((GeometryTypeFlags & OGRGeometry::OGR_G_3D) &&
1872 77 : (GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
1873 4 : suffix = "ZM";
1874 225 : else if ((GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED) &&
1875 4 : (EQUAL(pszGeomType, "geography") ||
1876 3 : wkbFlatten(eType) != wkbUnknown))
1877 2 : suffix = "M";
1878 219 : else if (GeometryTypeFlags & OGRGeometry::OGR_G_3D)
1879 73 : suffix = "Z";
1880 : else
1881 146 : suffix = "";
1882 :
1883 : {
1884 450 : CPLString osCommand;
1885 225 : if (eType != wkbNone && !bHavePostGIS)
1886 : {
1887 82 : if (pszGFldName && !EQUAL(pszGFldName, "wkb_geometry"))
1888 : {
1889 3 : CPLError(CE_Warning, CPLE_AppDefined,
1890 : "GEOMETRY_NAME=%s ignored, and set instead to "
1891 : "'wkb_geometry' as it is the only geometry column "
1892 : "name recognized for non-PostGIS enabled databases.",
1893 : pszGFldName);
1894 : }
1895 82 : pszGFldName = "wkb_geometry";
1896 82 : osCommand.Printf("%s ( "
1897 : " %s %s, "
1898 : " %s %s, "
1899 : " PRIMARY KEY (%s)",
1900 : osCreateTable.c_str(),
1901 : osFIDColumnNameEscaped.c_str(), pszSerialType,
1902 : pszGFldName, pszGeomType,
1903 82 : osFIDColumnNameEscaped.c_str());
1904 : }
1905 143 : else if (!bDeferredCreation && eType != wkbNone &&
1906 2 : EQUAL(pszGeomType, "geography"))
1907 : {
1908 1 : osCommand.Printf(
1909 : "%s ( %s %s, %s geography(%s%s%s), PRIMARY KEY (%s)",
1910 : osCreateTable.c_str(), osFIDColumnNameEscaped.c_str(),
1911 2 : pszSerialType, OGRPGEscapeColumnName(pszGFldName).c_str(),
1912 : pszGeometryType, suffix,
1913 1 : nSRSId ? CPLSPrintf(",%d", nSRSId) : "",
1914 2 : osFIDColumnNameEscaped.c_str());
1915 : }
1916 142 : else if (!bDeferredCreation && eType != wkbNone &&
1917 1 : !EQUAL(pszGeomType, "geography") &&
1918 1 : sPostGISVersion.nMajor >= 2)
1919 : {
1920 1 : osCommand.Printf(
1921 : "%s ( %s %s, %s geometry(%s%s%s), PRIMARY KEY (%s)",
1922 : osCreateTable.c_str(), osFIDColumnNameEscaped.c_str(),
1923 2 : pszSerialType, OGRPGEscapeColumnName(pszGFldName).c_str(),
1924 : pszGeometryType, suffix,
1925 0 : nSRSId ? CPLSPrintf(",%d", nSRSId) : "",
1926 2 : osFIDColumnNameEscaped.c_str());
1927 : }
1928 : else
1929 : {
1930 : osCommand.Printf("%s ( %s %s, PRIMARY KEY (%s)",
1931 : osCreateTable.c_str(),
1932 : osFIDColumnNameEscaped.c_str(), pszSerialType,
1933 141 : osFIDColumnNameEscaped.c_str());
1934 : }
1935 225 : osCreateTable = std::move(osCommand);
1936 : }
1937 :
1938 : const char *pszSI =
1939 225 : CSLFetchNameValueDef(papszOptions, "SPATIAL_INDEX", "GIST");
1940 225 : bool bCreateSpatialIndex =
1941 0 : (EQUAL(pszSI, "GIST") || EQUAL(pszSI, "SPGIST") ||
1942 225 : EQUAL(pszSI, "BRIN") || EQUAL(pszSI, "YES") || EQUAL(pszSI, "ON") ||
1943 0 : EQUAL(pszSI, "TRUE"));
1944 225 : if (!bCreateSpatialIndex && !EQUAL(pszSI, "NO") && !EQUAL(pszSI, "OFF") &&
1945 0 : !EQUAL(pszSI, "FALSE") && !EQUAL(pszSI, "NONE"))
1946 : {
1947 0 : CPLError(CE_Warning, CPLE_NotSupported,
1948 : "SPATIAL_INDEX=%s not supported", pszSI);
1949 : }
1950 450 : const char *pszSpatialIndexType = EQUAL(pszSI, "SPGIST") ? "SPGIST"
1951 225 : : EQUAL(pszSI, "BRIN") ? "BRIN"
1952 : : "GIST";
1953 196 : if (eType != wkbNone && bCreateSpatialIndex &&
1954 421 : CPLFetchBool(papszOptions, "UNLOGGED", false) &&
1955 0 : !(sPostgreSQLVersion.nMajor > 9 ||
1956 0 : (sPostgreSQLVersion.nMajor == 9 && sPostgreSQLVersion.nMinor >= 3)))
1957 : {
1958 0 : CPLError(
1959 : CE_Warning, CPLE_NotSupported,
1960 : "GiST index only supported since Postgres 9.3 on unlogged table");
1961 0 : bCreateSpatialIndex = false;
1962 : }
1963 :
1964 225 : if (!bDeferredCreation)
1965 : {
1966 92 : SoftStartTransaction();
1967 :
1968 92 : CPLString osCommand = osCreateTable;
1969 92 : osCommand += " )";
1970 :
1971 92 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
1972 92 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
1973 : {
1974 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
1975 0 : PQerrorMessage(hPGConn));
1976 0 : CPLFree(pszTableName);
1977 0 : CPLFree(pszSchemaName);
1978 :
1979 0 : OGRPGClearResult(hResult);
1980 :
1981 0 : SoftRollbackTransaction();
1982 0 : return nullptr;
1983 : }
1984 :
1985 92 : OGRPGClearResult(hResult);
1986 :
1987 92 : if (eType != wkbNone && bHavePostGIS && bCreateSpatialIndex)
1988 : {
1989 : // Create the spatial index.
1990 : // We're doing this before we add geometry and record to the
1991 : // table, so this may not be exactly the best way to do it.
1992 : const std::string osIndexName(OGRPGCommonGenerateSpatialIndexName(
1993 2 : pszTableName, pszGFldName, 0));
1994 :
1995 : osCommand.Printf("CREATE INDEX %s ON %s.%s USING %s (%s)",
1996 4 : OGRPGEscapeColumnName(osIndexName.c_str()).c_str(),
1997 4 : OGRPGEscapeColumnName(pszSchemaName).c_str(),
1998 4 : OGRPGEscapeColumnName(pszTableName).c_str(),
1999 : pszSpatialIndexType,
2000 10 : OGRPGEscapeColumnName(pszGFldName).c_str());
2001 :
2002 2 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
2003 :
2004 2 : if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
2005 : {
2006 0 : CPLError(CE_Failure, CPLE_AppDefined,
2007 : "'%s' failed for layer %s, index creation has failed.",
2008 : osCommand.c_str(), pszLayerName);
2009 :
2010 0 : CPLFree(pszTableName);
2011 0 : CPLFree(pszSchemaName);
2012 :
2013 0 : OGRPGClearResult(hResult);
2014 :
2015 0 : SoftRollbackTransaction();
2016 :
2017 0 : return nullptr;
2018 : }
2019 2 : OGRPGClearResult(hResult);
2020 : }
2021 :
2022 : /* --------------------------------------------------------------------
2023 : */
2024 : /* Complete, and commit the transaction. */
2025 : /* --------------------------------------------------------------------
2026 : */
2027 92 : SoftCommitTransaction();
2028 : }
2029 :
2030 : /* -------------------------------------------------------------------- */
2031 : /* Create the layer object. */
2032 : /* -------------------------------------------------------------------- */
2033 : OGRPGTableLayer *poLayer = new OGRPGTableLayer(
2034 225 : this, osCurrentSchema, pszTableName, pszSchemaName, "", nullptr, TRUE);
2035 225 : poLayer->SetTableDefinition(osFIDColumnName, pszGFldName, eType,
2036 : pszGeomType, nSRSId, GeometryTypeFlags);
2037 225 : poLayer->SetLaunderFlag(bLaunder);
2038 225 : poLayer->SetUTF8ToASCIIFlag(bUTF8ToASCII);
2039 225 : poLayer->SetPrecisionFlag(CPLFetchBool(papszOptions, "PRECISION", true));
2040 : // poLayer->SetForcedSRSId(nForcedSRSId);
2041 225 : poLayer->SetForcedGeometryTypeFlags(ForcedGeometryTypeFlags);
2042 225 : poLayer->SetCreateSpatialIndex(bCreateSpatialIndex, pszSpatialIndexType);
2043 225 : poLayer->SetDeferredCreation(bDeferredCreation, osCreateTable);
2044 :
2045 225 : const char *pszDescription = CSLFetchNameValue(papszOptions, "DESCRIPTION");
2046 225 : if (pszDescription != nullptr)
2047 2 : poLayer->SetForcedDescription(pszDescription);
2048 :
2049 : /* HSTORE_COLUMNS existed at a time during GDAL 1.10dev */
2050 : const char *pszHSTOREColumns =
2051 225 : CSLFetchNameValue(papszOptions, "HSTORE_COLUMNS");
2052 225 : if (pszHSTOREColumns != nullptr)
2053 0 : CPLError(CE_Warning, CPLE_AppDefined,
2054 : "HSTORE_COLUMNS not recognized. Use COLUMN_TYPES instead.");
2055 :
2056 : const char *pszOverrideColumnTypes =
2057 225 : CSLFetchNameValue(papszOptions, "COLUMN_TYPES");
2058 225 : poLayer->SetOverrideColumnTypes(pszOverrideColumnTypes);
2059 :
2060 225 : poLayer->AllowAutoFIDOnCreateViaCopy();
2061 225 : if (CPLTestBool(CPLGetConfigOption("PG_USE_COPY", "YES")))
2062 219 : poLayer->SetUseCopy();
2063 :
2064 225 : if (bFID64)
2065 2 : poLayer->SetMetadataItem(OLMD_FID64, "YES");
2066 :
2067 : /* -------------------------------------------------------------------- */
2068 : /* Add layer to data source layer list. */
2069 : /* -------------------------------------------------------------------- */
2070 225 : papoLayers = static_cast<OGRPGTableLayer **>(
2071 225 : CPLRealloc(papoLayers, sizeof(OGRPGTableLayer *) * (nLayers + 1)));
2072 :
2073 225 : papoLayers[nLayers++] = poLayer;
2074 :
2075 225 : CPLFree(pszTableName);
2076 225 : CPLFree(pszSchemaName);
2077 :
2078 225 : return poLayer;
2079 : }
2080 :
2081 : /************************************************************************/
2082 : /* TestCapability() */
2083 : /************************************************************************/
2084 :
2085 115 : int OGRPGDataSource::TestCapability(const char *pszCap)
2086 :
2087 : {
2088 115 : if (EQUAL(pszCap, ODsCCreateLayer) || EQUAL(pszCap, ODsCDeleteLayer) ||
2089 84 : EQUAL(pszCap, ODsCCreateGeomFieldAfterCreateLayer))
2090 51 : return TRUE;
2091 64 : else if (EQUAL(pszCap, ODsCCurveGeometries))
2092 12 : return TRUE;
2093 52 : else if (EQUAL(pszCap, ODsCTransactions))
2094 20 : return TRUE;
2095 32 : else if (EQUAL(pszCap, ODsCMeasuredGeometries))
2096 12 : return TRUE;
2097 20 : else if (EQUAL(pszCap, ODsCZGeometries))
2098 14 : return TRUE;
2099 6 : else if (EQUAL(pszCap, ODsCRandomLayerWrite))
2100 0 : return TRUE;
2101 : else
2102 6 : return FALSE;
2103 : }
2104 :
2105 : /************************************************************************/
2106 : /* GetLayerCount() */
2107 : /************************************************************************/
2108 :
2109 1285 : int OGRPGDataSource::GetLayerCount()
2110 : {
2111 1285 : LoadTables();
2112 1285 : return nLayers;
2113 : }
2114 :
2115 : /************************************************************************/
2116 : /* GetLayer() */
2117 : /************************************************************************/
2118 :
2119 323 : OGRLayer *OGRPGDataSource::GetLayer(int iLayer)
2120 :
2121 : {
2122 : /* Force loading of all registered tables */
2123 323 : if (iLayer < 0 || iLayer >= GetLayerCount())
2124 10 : return nullptr;
2125 : else
2126 313 : return papoLayers[iLayer];
2127 : }
2128 :
2129 : /************************************************************************/
2130 : /* FindSchema() */
2131 : /************************************************************************/
2132 :
2133 : // Check that the schema exists. If there is a single match in a case
2134 : // insensitive way, use it. Otherwise error out if the match is not exact.
2135 : // Return the schema name with its exact case from pg_catalog, or an empty
2136 : // string if an error occurs.
2137 : std::optional<std::string>
2138 288 : OGRPGDataSource::FindSchema(const char *pszSchemaNameIn)
2139 : {
2140 288 : if (strcmp(pszSchemaNameIn, "public") == 0 ||
2141 285 : strcmp(pszSchemaNameIn, "pg_temp") == 0)
2142 : {
2143 5 : return pszSchemaNameIn;
2144 : }
2145 :
2146 283 : EndCopy();
2147 :
2148 566 : std::string osSchemaName;
2149 : std::string osCommand(
2150 566 : "SELECT nspname FROM pg_catalog.pg_namespace WHERE nspname ILIKE ");
2151 283 : osCommand += OGRPGEscapeString(hPGConn, pszSchemaNameIn);
2152 283 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
2153 283 : if (hResult && PQntuples(hResult) == 1)
2154 : {
2155 275 : osSchemaName = PQgetvalue(hResult, 0, 0);
2156 : }
2157 8 : else if (hResult)
2158 : {
2159 8 : const int nTuples = PQntuples(hResult);
2160 8 : if (nTuples == 0)
2161 : {
2162 2 : CPLError(CE_Failure, CPLE_AppDefined,
2163 : "Schema \"%s\" does not exist.", pszSchemaNameIn);
2164 2 : return {};
2165 : }
2166 14 : for (int i = 0; i < nTuples; ++i)
2167 : {
2168 12 : if (strcmp(PQgetvalue(hResult, i, 0), pszSchemaNameIn) == 0)
2169 : {
2170 4 : osSchemaName = pszSchemaNameIn;
2171 4 : break;
2172 : }
2173 : }
2174 6 : if (osSchemaName.empty())
2175 : {
2176 2 : CPLError(CE_Failure, CPLE_AppDefined,
2177 : "Several schemas exist whose name matches \"%s\", but "
2178 : "not with that case. "
2179 : "Please specify the schema name with the exact case.",
2180 : pszSchemaNameIn);
2181 2 : return {};
2182 : }
2183 : }
2184 279 : OGRPGClearResult(hResult);
2185 :
2186 279 : return osSchemaName;
2187 : }
2188 :
2189 : /************************************************************************/
2190 : /* GetLayerByName() */
2191 : /************************************************************************/
2192 :
2193 534 : OGRLayer *OGRPGDataSource::GetLayerByName(const char *pszNameIn)
2194 :
2195 : {
2196 534 : char *pszTableName = nullptr;
2197 534 : char *pszGeomColumnName = nullptr;
2198 534 : char *pszSchemaName = nullptr;
2199 :
2200 534 : if (!pszNameIn)
2201 0 : return nullptr;
2202 :
2203 : /* first a case sensitive check */
2204 : /* do NOT force loading of all registered tables */
2205 754 : for (int i = 0; i < nLayers; i++)
2206 : {
2207 316 : OGRPGTableLayer *poLayer = papoLayers[i];
2208 :
2209 316 : if (strcmp(pszNameIn, poLayer->GetName()) == 0)
2210 : {
2211 96 : return poLayer;
2212 : }
2213 : }
2214 :
2215 : /* then case insensitive */
2216 608 : for (int i = 0; i < nLayers; i++)
2217 : {
2218 170 : OGRPGTableLayer *poLayer = papoLayers[i];
2219 :
2220 170 : if (EQUAL(pszNameIn, poLayer->GetName()))
2221 : {
2222 0 : return poLayer;
2223 : }
2224 : }
2225 :
2226 438 : char *pszNameWithoutBracket = CPLStrdup(pszNameIn);
2227 438 : char *pos = strchr(pszNameWithoutBracket, '(');
2228 438 : if (pos != nullptr)
2229 : {
2230 7 : *pos = '\0';
2231 7 : pszGeomColumnName = CPLStrdup(pos + 1);
2232 7 : int len = static_cast<int>(strlen(pszGeomColumnName));
2233 7 : if (len > 0)
2234 7 : pszGeomColumnName[len - 1] = '\0';
2235 : }
2236 :
2237 438 : pos = strchr(pszNameWithoutBracket, '.');
2238 438 : if (pos != nullptr)
2239 : {
2240 56 : *pos = '\0';
2241 56 : const auto osSchemaName = FindSchema(pszNameWithoutBracket);
2242 56 : if (!osSchemaName.has_value())
2243 : {
2244 0 : CPLFree(pszNameWithoutBracket);
2245 0 : CPLFree(pszGeomColumnName);
2246 0 : return nullptr;
2247 : }
2248 56 : pszSchemaName = CPLStrdup(osSchemaName->c_str());
2249 56 : pszTableName = CPLStrdup(pos + 1);
2250 : }
2251 : else
2252 : {
2253 382 : pszTableName = CPLStrdup(pszNameWithoutBracket);
2254 : }
2255 :
2256 438 : if (strlen(pszTableName) > OGR_PG_NAMEDATALEN - 1)
2257 0 : pszTableName[OGR_PG_NAMEDATALEN - 1] = 0;
2258 :
2259 438 : CPLFree(pszNameWithoutBracket);
2260 438 : pszNameWithoutBracket = nullptr;
2261 :
2262 438 : OGRPGTableLayer *poLayer = nullptr;
2263 :
2264 438 : if (pszSchemaName != nullptr && osCurrentSchema == pszSchemaName &&
2265 : pszGeomColumnName == nullptr)
2266 : {
2267 : poLayer =
2268 26 : cpl::down_cast<OGRPGTableLayer *>(GetLayerByName(pszTableName));
2269 : }
2270 : else
2271 : {
2272 412 : EndCopy();
2273 :
2274 824 : const CPLString osTableName(pszTableName);
2275 824 : const CPLString osTableNameLower = CPLString(pszTableName).tolower();
2276 412 : if (osTableName != osTableNameLower)
2277 4 : CPLPushErrorHandler(CPLQuietErrorHandler);
2278 412 : poLayer = OpenTable(osCurrentSchema, pszTableName, pszSchemaName,
2279 : nullptr, pszGeomColumnName, bDSUpdate, TRUE);
2280 412 : if (osTableName != osTableNameLower)
2281 4 : CPLPopErrorHandler();
2282 412 : if (poLayer == nullptr && osTableName != osTableNameLower)
2283 : {
2284 : poLayer =
2285 4 : OpenTable(osCurrentSchema, osTableNameLower, pszSchemaName,
2286 : nullptr, pszGeomColumnName, bDSUpdate, TRUE);
2287 : }
2288 : }
2289 :
2290 438 : CPLFree(pszTableName);
2291 438 : CPLFree(pszSchemaName);
2292 438 : CPLFree(pszGeomColumnName);
2293 :
2294 438 : return poLayer;
2295 : }
2296 :
2297 : /************************************************************************/
2298 : /* OGRPGNoticeProcessor() */
2299 : /************************************************************************/
2300 :
2301 429 : static void OGRPGNoticeProcessor(CPL_UNUSED void *arg, const char *pszMessage)
2302 : {
2303 429 : CPLDebug("OGR_PG_NOTICE", "%s", pszMessage);
2304 429 : }
2305 :
2306 : /************************************************************************/
2307 : /* InitializeMetadataTables() */
2308 : /* */
2309 : /* Create the metadata tables (SPATIAL_REF_SYS and */
2310 : /* GEOMETRY_COLUMNS). */
2311 : /************************************************************************/
2312 :
2313 0 : OGRErr OGRPGDataSource::InitializeMetadataTables()
2314 :
2315 : {
2316 : // implement later.
2317 0 : return OGRERR_FAILURE;
2318 : }
2319 :
2320 : /************************************************************************/
2321 : /* FetchSRS() */
2322 : /* */
2323 : /* Return a SRS corresponding to a particular id. Note that */
2324 : /* reference counting should be honoured on the returned */
2325 : /* OGRSpatialReference, as handles may be cached. */
2326 : /************************************************************************/
2327 :
2328 33 : const OGRSpatialReference *OGRPGDataSource::FetchSRS(int nId)
2329 :
2330 : {
2331 33 : if (nId < 0 || !m_bHasSpatialRefSys)
2332 0 : return nullptr;
2333 :
2334 : /* -------------------------------------------------------------------- */
2335 : /* First, we look through our SRID cache, is it there? */
2336 : /* -------------------------------------------------------------------- */
2337 33 : auto oIter = m_oSRSCache.find(nId);
2338 33 : if (oIter != m_oSRSCache.end())
2339 : {
2340 6 : return oIter->second.get();
2341 : }
2342 :
2343 27 : EndCopy();
2344 :
2345 : /* -------------------------------------------------------------------- */
2346 : /* Try looking up in spatial_ref_sys table. */
2347 : /* -------------------------------------------------------------------- */
2348 54 : CPLString osCommand;
2349 27 : std::unique_ptr<OGRSpatialReference, OGRSpatialReferenceReleaser> poSRS;
2350 :
2351 27 : osCommand.Printf("SELECT srtext, auth_name, auth_srid FROM spatial_ref_sys "
2352 : "WHERE srid = %d",
2353 27 : nId);
2354 27 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
2355 :
2356 54 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
2357 27 : PQntuples(hResult) == 1)
2358 : {
2359 27 : const char *pszWKT = PQgetvalue(hResult, 0, 0);
2360 27 : const char *pszAuthName = PQgetvalue(hResult, 0, 1);
2361 27 : const char *pszAuthSRID = PQgetvalue(hResult, 0, 2);
2362 27 : poSRS.reset(new OGRSpatialReference());
2363 27 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2364 :
2365 : // Try to import first from EPSG code, and then from WKT
2366 27 : if (pszAuthName && pszAuthSRID && EQUAL(pszAuthName, "EPSG") &&
2367 78 : atoi(pszAuthSRID) == nId &&
2368 24 : poSRS->importFromEPSG(nId) == OGRERR_NONE)
2369 : {
2370 : // do nothing
2371 : }
2372 3 : else if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
2373 : {
2374 0 : poSRS.reset();
2375 : }
2376 : }
2377 : else
2378 : {
2379 0 : CPLError(CE_Failure, CPLE_AppDefined, "Could not fetch SRS: %s",
2380 0 : PQerrorMessage(hPGConn));
2381 : }
2382 :
2383 27 : OGRPGClearResult(hResult);
2384 :
2385 27 : if (poSRS)
2386 27 : poSRS->StripTOWGS84IfKnownDatumAndAllowed();
2387 :
2388 : /* -------------------------------------------------------------------- */
2389 : /* Add to the cache. */
2390 : /* -------------------------------------------------------------------- */
2391 27 : oIter = m_oSRSCache.emplace(nId, std::move(poSRS)).first;
2392 27 : return oIter->second.get();
2393 : }
2394 :
2395 : /************************************************************************/
2396 : /* FetchSRSId() */
2397 : /* */
2398 : /* Fetch the id corresponding to an SRS, and if not found, add */
2399 : /* it to the table. */
2400 : /************************************************************************/
2401 :
2402 18 : int OGRPGDataSource::FetchSRSId(const OGRSpatialReference *poSRS)
2403 :
2404 : {
2405 18 : if (poSRS == nullptr || !m_bHasSpatialRefSys)
2406 0 : return nUndefinedSRID;
2407 :
2408 36 : OGRSpatialReference oSRS(*poSRS);
2409 : // cppcheck-suppress uselessAssignmentPtrArg
2410 18 : poSRS = nullptr;
2411 :
2412 18 : const char *pszAuthorityName = oSRS.GetAuthorityName(nullptr);
2413 :
2414 18 : if (pszAuthorityName == nullptr || strlen(pszAuthorityName) == 0)
2415 : {
2416 : /* --------------------------------------------------------------------
2417 : */
2418 : /* Try to identify an EPSG code */
2419 : /* --------------------------------------------------------------------
2420 : */
2421 2 : oSRS.AutoIdentifyEPSG();
2422 :
2423 2 : pszAuthorityName = oSRS.GetAuthorityName(nullptr);
2424 2 : if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
2425 : {
2426 1 : const char *pszAuthorityCode = oSRS.GetAuthorityCode(nullptr);
2427 1 : if (pszAuthorityCode != nullptr && strlen(pszAuthorityCode) > 0)
2428 : {
2429 : /* Import 'clean' SRS */
2430 1 : oSRS.importFromEPSG(atoi(pszAuthorityCode));
2431 :
2432 1 : pszAuthorityName = oSRS.GetAuthorityName(nullptr);
2433 : }
2434 : }
2435 : }
2436 : /* -------------------------------------------------------------------- */
2437 : /* Check whether the authority name/code is already mapped to a */
2438 : /* SRS ID. */
2439 : /* -------------------------------------------------------------------- */
2440 36 : CPLString osCommand;
2441 18 : int nAuthorityCode = 0;
2442 18 : if (pszAuthorityName != nullptr)
2443 : {
2444 : /* Check that the authority code is integral */
2445 17 : nAuthorityCode = atoi(oSRS.GetAuthorityCode(nullptr));
2446 17 : if (nAuthorityCode > 0)
2447 : {
2448 : osCommand.Printf("SELECT srid FROM spatial_ref_sys WHERE "
2449 : "auth_name = '%s' AND auth_srid = %d",
2450 17 : pszAuthorityName, nAuthorityCode);
2451 17 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
2452 :
2453 34 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
2454 17 : PQntuples(hResult) > 0)
2455 : {
2456 14 : int nSRSId = atoi(PQgetvalue(hResult, 0, 0));
2457 :
2458 14 : OGRPGClearResult(hResult);
2459 :
2460 14 : return nSRSId;
2461 : }
2462 :
2463 3 : OGRPGClearResult(hResult);
2464 : }
2465 : }
2466 :
2467 : /* -------------------------------------------------------------------- */
2468 : /* Translate SRS to WKT. */
2469 : /* -------------------------------------------------------------------- */
2470 4 : char *pszWKT = nullptr;
2471 4 : if (oSRS.exportToWkt(&pszWKT) != OGRERR_NONE)
2472 : {
2473 0 : CPLFree(pszWKT);
2474 0 : return nUndefinedSRID;
2475 : }
2476 :
2477 : /* -------------------------------------------------------------------- */
2478 : /* Try to find in the existing table. */
2479 : /* -------------------------------------------------------------------- */
2480 : CPLString osWKT =
2481 8 : OGRPGEscapeString(hPGConn, pszWKT, -1, "spatial_ref_sys", "srtext");
2482 : osCommand.Printf("SELECT srid FROM spatial_ref_sys WHERE srtext = %s",
2483 4 : osWKT.c_str());
2484 4 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
2485 4 : CPLFree(pszWKT); // CM: Added to prevent mem leaks
2486 4 : pszWKT = nullptr; // CM: Added
2487 :
2488 : /* -------------------------------------------------------------------- */
2489 : /* We got it! Return it. */
2490 : /* -------------------------------------------------------------------- */
2491 8 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
2492 4 : PQntuples(hResult) > 0)
2493 : {
2494 0 : int nSRSId = atoi(PQgetvalue(hResult, 0, 0));
2495 :
2496 0 : OGRPGClearResult(hResult);
2497 :
2498 0 : return nSRSId;
2499 : }
2500 :
2501 : /* -------------------------------------------------------------------- */
2502 : /* If the command actually failed, then the metadata table is */
2503 : /* likely missing. Try defining it. */
2504 : /* -------------------------------------------------------------------- */
2505 : const bool bTableMissing =
2506 4 : hResult == nullptr || PQresultStatus(hResult) == PGRES_NONFATAL_ERROR;
2507 :
2508 4 : OGRPGClearResult(hResult);
2509 :
2510 4 : if (bTableMissing)
2511 : {
2512 0 : if (InitializeMetadataTables() != OGRERR_NONE)
2513 0 : return nUndefinedSRID;
2514 : }
2515 :
2516 : /* -------------------------------------------------------------------- */
2517 : /* Get the current maximum srid in the srs table. */
2518 : /* -------------------------------------------------------------------- */
2519 4 : hResult = OGRPG_PQexec(hPGConn, "SELECT MAX(srid) FROM spatial_ref_sys");
2520 :
2521 4 : int nSRSId = 1;
2522 4 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK)
2523 : {
2524 4 : nSRSId = atoi(PQgetvalue(hResult, 0, 0)) + 1;
2525 4 : OGRPGClearResult(hResult);
2526 : }
2527 :
2528 : /* -------------------------------------------------------------------- */
2529 : /* Try adding the SRS to the SRS table. */
2530 : /* -------------------------------------------------------------------- */
2531 4 : char *pszProj4 = nullptr;
2532 4 : if (oSRS.exportToProj4(&pszProj4) != OGRERR_NONE)
2533 : {
2534 0 : CPLFree(pszProj4);
2535 0 : return nUndefinedSRID;
2536 : }
2537 :
2538 4 : CPLString osProj4 = OGRPGEscapeString(hPGConn, pszProj4, -1,
2539 4 : "spatial_ref_sys", "proj4text");
2540 :
2541 4 : if (pszAuthorityName != nullptr && nAuthorityCode > 0)
2542 : {
2543 3 : nAuthorityCode = atoi(oSRS.GetAuthorityCode(nullptr));
2544 :
2545 3 : osCommand.Printf("INSERT INTO spatial_ref_sys "
2546 : "(srid,srtext,proj4text,auth_name,auth_srid) "
2547 : "VALUES (%d, %s, %s, '%s', %d)",
2548 : nSRSId, osWKT.c_str(), osProj4.c_str(),
2549 3 : pszAuthorityName, nAuthorityCode);
2550 : }
2551 : else
2552 : {
2553 : osCommand.Printf("INSERT INTO spatial_ref_sys (srid,srtext,proj4text) "
2554 : "VALUES (%d,%s,%s)",
2555 1 : nSRSId, osWKT.c_str(), osProj4.c_str());
2556 : }
2557 :
2558 : // Free everything that was allocated.
2559 4 : CPLFree(pszProj4);
2560 4 : CPLFree(pszWKT);
2561 :
2562 4 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
2563 4 : OGRPGClearResult(hResult);
2564 :
2565 4 : return nSRSId;
2566 : }
2567 :
2568 : /************************************************************************/
2569 : /* StartTransaction() */
2570 : /* */
2571 : /* Should only be called by user code. Not driver internals. */
2572 : /************************************************************************/
2573 :
2574 74 : OGRErr OGRPGDataSource::StartTransaction(CPL_UNUSED int bForce)
2575 : {
2576 74 : if (bUserTransactionActive)
2577 : {
2578 2 : CPLError(CE_Failure, CPLE_AppDefined,
2579 : "Transaction already established");
2580 2 : return OGRERR_FAILURE;
2581 : }
2582 :
2583 72 : CPLAssert(!bSavePointActive);
2584 72 : EndCopy();
2585 :
2586 72 : if (nSoftTransactionLevel == 0)
2587 : {
2588 66 : OGRErr eErr = DoTransactionCommand("BEGIN");
2589 66 : if (eErr != OGRERR_NONE)
2590 0 : return eErr;
2591 : }
2592 : else
2593 : {
2594 6 : OGRErr eErr = DoTransactionCommand("SAVEPOINT ogr_savepoint");
2595 6 : if (eErr != OGRERR_NONE)
2596 0 : return eErr;
2597 :
2598 6 : bSavePointActive = TRUE;
2599 : }
2600 :
2601 72 : nSoftTransactionLevel++;
2602 72 : bUserTransactionActive = true;
2603 :
2604 : /*CPLDebug("PG", "poDS=%p StartTransaction() nSoftTransactionLevel=%d",
2605 : this, nSoftTransactionLevel);*/
2606 :
2607 72 : return OGRERR_NONE;
2608 : }
2609 :
2610 : /************************************************************************/
2611 : /* CommitTransaction() */
2612 : /* */
2613 : /* Should only be called by user code. Not driver internals. */
2614 : /************************************************************************/
2615 :
2616 58 : OGRErr OGRPGDataSource::CommitTransaction()
2617 : {
2618 58 : if (!bUserTransactionActive)
2619 : {
2620 2 : CPLError(CE_Failure, CPLE_AppDefined, "Transaction not established");
2621 2 : return OGRERR_FAILURE;
2622 : }
2623 :
2624 : /*CPLDebug("PG", "poDS=%p CommitTransaction() nSoftTransactionLevel=%d",
2625 : this, nSoftTransactionLevel);*/
2626 :
2627 56 : OGRErr eErr = FlushCacheWithRet(false);
2628 56 : if (eErr != OGRERR_NONE)
2629 : {
2630 1 : RollbackTransaction();
2631 1 : return eErr;
2632 : }
2633 :
2634 55 : nSoftTransactionLevel--;
2635 55 : bUserTransactionActive = false;
2636 :
2637 55 : if (bSavePointActive)
2638 : {
2639 4 : CPLAssert(nSoftTransactionLevel > 0);
2640 4 : bSavePointActive = FALSE;
2641 :
2642 4 : eErr = DoTransactionCommand("RELEASE SAVEPOINT ogr_savepoint");
2643 : }
2644 : else
2645 : {
2646 51 : if (nSoftTransactionLevel > 0)
2647 : {
2648 : // This means we have cursors still in progress
2649 7 : for (int i = 0; i < nLayers; i++)
2650 5 : papoLayers[i]->InvalidateCursor();
2651 2 : CPLAssert(nSoftTransactionLevel == 0);
2652 : }
2653 :
2654 51 : eErr = DoTransactionCommand("COMMIT");
2655 : }
2656 :
2657 55 : return eErr;
2658 : }
2659 :
2660 : /************************************************************************/
2661 : /* RollbackTransaction() */
2662 : /* */
2663 : /* Should only be called by user code. Not driver internals. */
2664 : /************************************************************************/
2665 :
2666 19 : OGRErr OGRPGDataSource::RollbackTransaction()
2667 : {
2668 19 : if (!bUserTransactionActive)
2669 : {
2670 2 : CPLError(CE_Failure, CPLE_AppDefined, "Transaction not established");
2671 2 : return OGRERR_FAILURE;
2672 : }
2673 :
2674 : /*CPLDebug("PG", "poDS=%p RollbackTransaction() nSoftTransactionLevel=%d",
2675 : this, nSoftTransactionLevel);*/
2676 :
2677 17 : FlushCache(false);
2678 :
2679 17 : nSoftTransactionLevel--;
2680 17 : bUserTransactionActive = false;
2681 :
2682 : OGRErr eErr;
2683 17 : if (bSavePointActive)
2684 : {
2685 2 : CPLAssert(nSoftTransactionLevel > 0);
2686 2 : bSavePointActive = FALSE;
2687 :
2688 2 : eErr = DoTransactionCommand("ROLLBACK TO SAVEPOINT ogr_savepoint");
2689 : }
2690 : else
2691 : {
2692 15 : if (nSoftTransactionLevel > 0)
2693 : {
2694 : // This means we have cursors still in progress
2695 0 : for (int i = 0; i < nLayers; i++)
2696 0 : papoLayers[i]->InvalidateCursor();
2697 0 : CPLAssert(nSoftTransactionLevel == 0);
2698 : }
2699 :
2700 15 : eErr = DoTransactionCommand("ROLLBACK");
2701 : }
2702 :
2703 17 : return eErr;
2704 : }
2705 :
2706 : /************************************************************************/
2707 : /* SoftStartTransaction() */
2708 : /* */
2709 : /* Create a transaction scope. If we already have a */
2710 : /* transaction active this isn't a real transaction, but just */
2711 : /* an increment to the scope count. */
2712 : /************************************************************************/
2713 :
2714 911 : OGRErr OGRPGDataSource::SoftStartTransaction()
2715 :
2716 : {
2717 911 : nSoftTransactionLevel++;
2718 : /*CPLDebug("PG", "poDS=%p SoftStartTransaction() nSoftTransactionLevel=%d",
2719 : this, nSoftTransactionLevel);*/
2720 :
2721 911 : OGRErr eErr = OGRERR_NONE;
2722 911 : if (nSoftTransactionLevel == 1)
2723 : {
2724 826 : eErr = DoTransactionCommand("BEGIN");
2725 : }
2726 :
2727 911 : return eErr;
2728 : }
2729 :
2730 : /************************************************************************/
2731 : /* SoftCommitTransaction() */
2732 : /* */
2733 : /* Commit the current transaction if we are at the outer */
2734 : /* scope. */
2735 : /************************************************************************/
2736 :
2737 909 : OGRErr OGRPGDataSource::SoftCommitTransaction()
2738 :
2739 : {
2740 909 : EndCopy();
2741 :
2742 : /*CPLDebug("PG", "poDS=%p SoftCommitTransaction() nSoftTransactionLevel=%d",
2743 : this, nSoftTransactionLevel);*/
2744 :
2745 909 : if (nSoftTransactionLevel <= 0)
2746 : {
2747 0 : CPLAssert(false);
2748 : return OGRERR_FAILURE;
2749 : }
2750 :
2751 909 : OGRErr eErr = OGRERR_NONE;
2752 909 : nSoftTransactionLevel--;
2753 909 : if (nSoftTransactionLevel == 0)
2754 : {
2755 826 : CPLAssert(!bSavePointActive);
2756 :
2757 826 : eErr = DoTransactionCommand("COMMIT");
2758 : }
2759 :
2760 909 : return eErr;
2761 : }
2762 :
2763 : /************************************************************************/
2764 : /* SoftRollbackTransaction() */
2765 : /* */
2766 : /* Do a rollback of the current transaction if we are at the 1st */
2767 : /* level */
2768 : /************************************************************************/
2769 :
2770 2 : OGRErr OGRPGDataSource::SoftRollbackTransaction()
2771 :
2772 : {
2773 2 : EndCopy();
2774 :
2775 : /*CPLDebug("PG", "poDS=%p SoftRollbackTransaction()
2776 : nSoftTransactionLevel=%d", this, nSoftTransactionLevel);*/
2777 :
2778 2 : if (nSoftTransactionLevel <= 0)
2779 : {
2780 0 : CPLAssert(false);
2781 : return OGRERR_FAILURE;
2782 : }
2783 :
2784 2 : OGRErr eErr = OGRERR_NONE;
2785 2 : nSoftTransactionLevel--;
2786 2 : if (nSoftTransactionLevel == 0)
2787 : {
2788 2 : CPLAssert(!bSavePointActive);
2789 :
2790 2 : eErr = DoTransactionCommand("ROLLBACK");
2791 : }
2792 :
2793 2 : return eErr;
2794 : }
2795 :
2796 : /************************************************************************/
2797 : /* FlushSoftTransaction() */
2798 : /* */
2799 : /* Force the unwinding of any active transaction, and its */
2800 : /* commit. Should only be used by datasource destructor */
2801 : /************************************************************************/
2802 :
2803 419 : OGRErr OGRPGDataSource::FlushSoftTransaction()
2804 :
2805 : {
2806 : /*CPLDebug("PG", "poDS=%p FlushSoftTransaction() nSoftTransactionLevel=%d",
2807 : this, nSoftTransactionLevel);*/
2808 :
2809 419 : if (nSoftTransactionLevel <= 0)
2810 419 : return OGRERR_NONE;
2811 :
2812 0 : bSavePointActive = FALSE;
2813 :
2814 0 : CPLAssert(nSoftTransactionLevel == 1);
2815 0 : nSoftTransactionLevel = 0;
2816 0 : return DoTransactionCommand("COMMIT");
2817 : }
2818 :
2819 : /************************************************************************/
2820 : /* DoTransactionCommand() */
2821 : /************************************************************************/
2822 :
2823 1798 : OGRErr OGRPGDataSource::DoTransactionCommand(const char *pszCommand)
2824 :
2825 : {
2826 1798 : OGRErr eErr = OGRERR_NONE;
2827 1798 : PGconn *l_hPGConn = GetPGConn();
2828 :
2829 1798 : PGresult *hResult = OGRPG_PQexec(l_hPGConn, pszCommand);
2830 1798 : osDebugLastTransactionCommand = pszCommand;
2831 :
2832 1798 : if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
2833 : {
2834 0 : eErr = OGRERR_FAILURE;
2835 : }
2836 :
2837 1798 : OGRPGClearResult(hResult);
2838 :
2839 1798 : return eErr;
2840 : }
2841 :
2842 : /************************************************************************/
2843 : /* OGRPGNoResetResultLayer */
2844 : /************************************************************************/
2845 :
2846 : class OGRPGNoResetResultLayer final : public OGRPGLayer
2847 : {
2848 : public:
2849 : OGRPGNoResetResultLayer(OGRPGDataSource *poDSIn, PGresult *hResultIn);
2850 :
2851 : virtual ~OGRPGNoResetResultLayer();
2852 :
2853 : virtual void ResetReading() override;
2854 :
2855 0 : virtual int TestCapability(const char *) override
2856 : {
2857 0 : return FALSE;
2858 : }
2859 :
2860 : virtual OGRFeature *GetNextFeature() override;
2861 :
2862 0 : virtual CPLString GetFromClauseForGetExtent() override
2863 : {
2864 0 : CPLAssert(false);
2865 : return "";
2866 : }
2867 :
2868 47 : virtual void ResolveSRID(const OGRPGGeomFieldDefn *poGFldDefn) override
2869 : {
2870 47 : poGFldDefn->nSRSId = -1;
2871 47 : }
2872 : };
2873 :
2874 : /************************************************************************/
2875 : /* OGRPGNoResetResultLayer() */
2876 : /************************************************************************/
2877 :
2878 96 : OGRPGNoResetResultLayer::OGRPGNoResetResultLayer(OGRPGDataSource *poDSIn,
2879 96 : PGresult *hResultIn)
2880 : {
2881 96 : poDS = poDSIn;
2882 96 : ReadResultDefinition(hResultIn);
2883 96 : hCursorResult = hResultIn;
2884 96 : CreateMapFromFieldNameToIndex(hCursorResult, poFeatureDefn,
2885 96 : m_panMapFieldNameToIndex,
2886 96 : m_panMapFieldNameToGeomIndex);
2887 96 : }
2888 :
2889 : /************************************************************************/
2890 : /* ~OGRPGNoResetResultLayer() */
2891 : /************************************************************************/
2892 :
2893 384 : OGRPGNoResetResultLayer::~OGRPGNoResetResultLayer()
2894 :
2895 : {
2896 96 : OGRPGClearResult(hCursorResult);
2897 96 : hCursorResult = nullptr;
2898 192 : }
2899 :
2900 : /************************************************************************/
2901 : /* ResetReading() */
2902 : /************************************************************************/
2903 :
2904 96 : void OGRPGNoResetResultLayer::ResetReading()
2905 : {
2906 96 : iNextShapeId = 0;
2907 96 : }
2908 :
2909 : /************************************************************************/
2910 : /* GetNextFeature() */
2911 : /************************************************************************/
2912 :
2913 192 : OGRFeature *OGRPGNoResetResultLayer::GetNextFeature()
2914 :
2915 : {
2916 192 : if (iNextShapeId == PQntuples(hCursorResult))
2917 : {
2918 96 : return nullptr;
2919 : }
2920 192 : return RecordToFeature(hCursorResult, m_panMapFieldNameToIndex,
2921 96 : m_panMapFieldNameToGeomIndex,
2922 96 : static_cast<int>(iNextShapeId++));
2923 : }
2924 :
2925 : /************************************************************************/
2926 : /* OGRPGMemLayerWrapper */
2927 : /************************************************************************/
2928 :
2929 : class OGRPGMemLayerWrapper final : public OGRLayer
2930 : {
2931 : private:
2932 : OGRPGMemLayerWrapper(const OGRPGMemLayerWrapper &) = delete;
2933 : OGRPGMemLayerWrapper &operator=(const OGRPGMemLayerWrapper &) = delete;
2934 :
2935 : GDALDataset *poMemDS = nullptr;
2936 : OGRLayer *poMemLayer = nullptr;
2937 :
2938 : public:
2939 96 : explicit OGRPGMemLayerWrapper(GDALDataset *poMemDSIn)
2940 96 : {
2941 96 : poMemDS = poMemDSIn;
2942 96 : poMemLayer = poMemDS->GetLayer(0);
2943 96 : }
2944 :
2945 190 : virtual ~OGRPGMemLayerWrapper()
2946 95 : {
2947 95 : delete poMemDS;
2948 190 : }
2949 :
2950 0 : virtual void ResetReading() override
2951 : {
2952 0 : poMemLayer->ResetReading();
2953 0 : }
2954 :
2955 80 : virtual OGRFeature *GetNextFeature() override
2956 : {
2957 80 : return poMemLayer->GetNextFeature();
2958 : }
2959 :
2960 0 : virtual OGRFeatureDefn *GetLayerDefn() override
2961 : {
2962 0 : return poMemLayer->GetLayerDefn();
2963 : }
2964 :
2965 0 : virtual int TestCapability(const char *) override
2966 : {
2967 0 : return FALSE;
2968 : }
2969 : };
2970 :
2971 : /************************************************************************/
2972 : /* GetMetadataItem() */
2973 : /************************************************************************/
2974 :
2975 515 : const char *OGRPGDataSource::GetMetadataItem(const char *pszKey,
2976 : const char *pszDomain)
2977 : {
2978 : /* Only used by ogr_pg.py to check inner working */
2979 515 : if (pszDomain != nullptr && EQUAL(pszDomain, "_debug_") &&
2980 : pszKey != nullptr)
2981 : {
2982 278 : if (EQUAL(pszKey, "bHasLoadTables"))
2983 10 : return CPLSPrintf("%d", bHasLoadTables);
2984 268 : if (EQUAL(pszKey, "nSoftTransactionLevel"))
2985 70 : return CPLSPrintf("%d", nSoftTransactionLevel);
2986 198 : if (EQUAL(pszKey, "bSavePointActive"))
2987 66 : return CPLSPrintf("%d", bSavePointActive);
2988 132 : if (EQUAL(pszKey, "bUserTransactionActive"))
2989 66 : return CPLSPrintf("%d", bUserTransactionActive);
2990 66 : if (EQUAL(pszKey, "osDebugLastTransactionCommand"))
2991 : {
2992 : const char *pszRet =
2993 66 : CPLSPrintf("%s", osDebugLastTransactionCommand.c_str());
2994 66 : osDebugLastTransactionCommand = "";
2995 66 : return pszRet;
2996 : }
2997 : }
2998 237 : return GDALDataset::GetMetadataItem(pszKey, pszDomain);
2999 : }
3000 :
3001 : /************************************************************************/
3002 : /* ExecuteSQL() */
3003 : /************************************************************************/
3004 :
3005 1254 : OGRLayer *OGRPGDataSource::ExecuteSQL(const char *pszSQLCommand,
3006 : OGRGeometry *poSpatialFilter,
3007 : const char *pszDialect)
3008 :
3009 : {
3010 : /* Skip leading whitespace characters */
3011 1254 : while (std::isspace(static_cast<unsigned char>(*pszSQLCommand)))
3012 0 : pszSQLCommand++;
3013 :
3014 1254 : FlushCache(false);
3015 :
3016 : /* -------------------------------------------------------------------- */
3017 : /* Use generic implementation for recognized dialects */
3018 : /* -------------------------------------------------------------------- */
3019 1254 : if (IsGenericSQLDialect(pszDialect))
3020 0 : return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter,
3021 0 : pszDialect);
3022 :
3023 : /* -------------------------------------------------------------------- */
3024 : /* Special case DELLAYER: command. */
3025 : /* -------------------------------------------------------------------- */
3026 1254 : if (STARTS_WITH_CI(pszSQLCommand, "DELLAYER:"))
3027 : {
3028 6 : const char *pszLayerName = pszSQLCommand + 9;
3029 :
3030 6 : while (*pszLayerName == ' ')
3031 0 : pszLayerName++;
3032 :
3033 6 : GetLayerCount();
3034 8 : for (int iLayer = 0; iLayer < nLayers; iLayer++)
3035 : {
3036 6 : if (EQUAL(papoLayers[iLayer]->GetName(), pszLayerName))
3037 : {
3038 4 : DeleteLayer(iLayer);
3039 4 : break;
3040 : }
3041 : }
3042 6 : return nullptr;
3043 : }
3044 :
3045 : /* -------------------------------------------------------------------- */
3046 : /* Execute the statement. */
3047 : /* -------------------------------------------------------------------- */
3048 1248 : PGresult *hResult = nullptr;
3049 :
3050 1248 : if (STARTS_WITH_CI(pszSQLCommand, "SELECT") == FALSE ||
3051 211 : (strstr(pszSQLCommand, "from") == nullptr &&
3052 198 : strstr(pszSQLCommand, "FROM") == nullptr))
3053 : {
3054 : /* For something that is not a select or a select without table, do not
3055 : */
3056 : /* run under transaction (CREATE DATABASE, VACUUM don't like
3057 : * transactions) */
3058 :
3059 1127 : hResult =
3060 1127 : OGRPG_PQexec(hPGConn, pszSQLCommand, TRUE /* multiple allowed */);
3061 1127 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK)
3062 : {
3063 96 : CPLDebug("PG", "Command Results Tuples = %d", PQntuples(hResult));
3064 :
3065 : OGRPGLayer *poResultLayer =
3066 96 : new OGRPGNoResetResultLayer(this, hResult);
3067 : auto poMemDS = std::unique_ptr<GDALDataset>(
3068 96 : MEMDataset::Create("", 0, 0, 0, GDT_Unknown, nullptr));
3069 96 : poMemDS->CopyLayer(poResultLayer, "sql_statement");
3070 : OGRPGMemLayerWrapper *poResLayer =
3071 96 : new OGRPGMemLayerWrapper(poMemDS.release());
3072 96 : delete poResultLayer;
3073 96 : return poResLayer;
3074 1031 : }
3075 : }
3076 : else
3077 : {
3078 121 : SoftStartTransaction();
3079 :
3080 121 : CPLString osCommand;
3081 : osCommand.Printf("DECLARE %s CURSOR for %s", "executeSQLCursor",
3082 121 : pszSQLCommand);
3083 :
3084 121 : hResult = OGRPG_PQexec(hPGConn, osCommand);
3085 :
3086 : /* --------------------------------------------------------------------
3087 : */
3088 : /* Do we have a tuple result? If so, instantiate a results */
3089 : /* layer for it. */
3090 : /* --------------------------------------------------------------------
3091 : */
3092 121 : if (hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK)
3093 : {
3094 120 : OGRPGClearResult(hResult);
3095 :
3096 120 : osCommand.Printf("FETCH 0 in %s", "executeSQLCursor");
3097 120 : hResult = OGRPG_PQexec(hPGConn, osCommand);
3098 :
3099 : OGRPGResultLayer *poLayer =
3100 120 : new OGRPGResultLayer(this, pszSQLCommand, hResult);
3101 :
3102 120 : OGRPGClearResult(hResult);
3103 :
3104 120 : osCommand.Printf("CLOSE %s", "executeSQLCursor");
3105 120 : hResult = OGRPG_PQexec(hPGConn, osCommand);
3106 120 : OGRPGClearResult(hResult);
3107 :
3108 120 : SoftCommitTransaction();
3109 :
3110 120 : if (poSpatialFilter != nullptr)
3111 5 : poLayer->SetSpatialFilter(poSpatialFilter);
3112 :
3113 120 : return poLayer;
3114 : }
3115 : else
3116 : {
3117 1 : SoftRollbackTransaction();
3118 : }
3119 : }
3120 :
3121 1032 : OGRPGClearResult(hResult);
3122 :
3123 1032 : return nullptr;
3124 : }
3125 :
3126 : /************************************************************************/
3127 : /* AbortSQL() */
3128 : /************************************************************************/
3129 :
3130 5 : OGRErr OGRPGDataSource::AbortSQL()
3131 : {
3132 5 : auto cancel = PQgetCancel(hPGConn);
3133 : int result;
3134 5 : if (cancel)
3135 : {
3136 : char errbuf[255];
3137 5 : result = PQcancel(cancel, errbuf, 255);
3138 5 : if (!result)
3139 0 : CPLDebug("PG", "Error canceling the query: %s", errbuf);
3140 5 : PQfreeCancel(cancel);
3141 5 : return result ? OGRERR_NONE : OGRERR_FAILURE;
3142 : }
3143 0 : return OGRERR_FAILURE;
3144 : }
3145 :
3146 : /************************************************************************/
3147 : /* ReleaseResultSet() */
3148 : /************************************************************************/
3149 :
3150 218 : void OGRPGDataSource::ReleaseResultSet(OGRLayer *poLayer)
3151 :
3152 : {
3153 218 : delete poLayer;
3154 218 : }
3155 :
3156 : /************************************************************************/
3157 : /* StartCopy() */
3158 : /************************************************************************/
3159 :
3160 3739 : void OGRPGDataSource::StartCopy(OGRPGTableLayer *poPGLayer)
3161 : {
3162 3739 : if (poLayerInCopyMode == poPGLayer)
3163 3554 : return;
3164 185 : EndCopy();
3165 185 : poLayerInCopyMode = poPGLayer;
3166 185 : poLayerInCopyMode->StartCopy();
3167 : }
3168 :
3169 : /************************************************************************/
3170 : /* EndCopy() */
3171 : /************************************************************************/
3172 :
3173 9644 : OGRErr OGRPGDataSource::EndCopy()
3174 : {
3175 9644 : if (poLayerInCopyMode != nullptr)
3176 : {
3177 185 : OGRErr result = poLayerInCopyMode->EndCopy();
3178 185 : poLayerInCopyMode = nullptr;
3179 :
3180 185 : return result;
3181 : }
3182 : else
3183 9459 : return OGRERR_NONE;
3184 : }
3185 :
3186 : /************************************************************************/
3187 : /* CreateMetadataTableIfNeeded() */
3188 : /************************************************************************/
3189 :
3190 4 : bool OGRPGDataSource::CreateMetadataTableIfNeeded()
3191 : {
3192 4 : if (m_bCreateMetadataTableIfNeededRun)
3193 0 : return m_bCreateMetadataTableIfNeededSuccess;
3194 :
3195 4 : m_bCreateMetadataTableIfNeededRun = true;
3196 :
3197 4 : const bool bIsSuperUser = IsSuperUser();
3198 4 : if (!bIsSuperUser && !OGRSystemTablesEventTriggerExists())
3199 : {
3200 1 : CPLError(CE_Warning, CPLE_AppDefined,
3201 : "User lacks super user privilege to be able to create event "
3202 : "trigger ogr_system_tables_event_trigger_for_metadata");
3203 1 : m_bCreateMetadataTableIfNeededSuccess = true;
3204 1 : return true;
3205 : }
3206 :
3207 : PGresult *hResult;
3208 :
3209 3 : hResult = OGRPG_PQexec(
3210 : hPGConn,
3211 : "SELECT c.oid FROM pg_class c "
3212 : "JOIN pg_namespace n ON c.relnamespace=n.oid "
3213 : "WHERE c.relname = 'metadata' AND n.nspname = 'ogr_system_tables'");
3214 : const bool bFound =
3215 3 : (hResult && PQntuples(hResult) == 1 && !PQgetisnull(hResult, 0, 0));
3216 3 : OGRPGClearResult(hResult);
3217 :
3218 3 : hResult = OGRPG_PQexec(
3219 : hPGConn,
3220 : "SELECT has_database_privilege((select current_database()), 'CREATE')");
3221 : const bool bCanCreateSchema =
3222 6 : (hResult && PQntuples(hResult) == 1 && !PQgetisnull(hResult, 0, 0) &&
3223 3 : strcmp(PQgetvalue(hResult, 0, 0), "t") == 0);
3224 3 : OGRPGClearResult(hResult);
3225 :
3226 3 : if (!bFound)
3227 : {
3228 2 : if (!bCanCreateSchema)
3229 : {
3230 0 : CPLError(CE_Warning, CPLE_AppDefined,
3231 : "User lacks CREATE SCHEMA privilege to be able to create "
3232 : "ogr_system_tables.metadata table");
3233 0 : return false;
3234 : }
3235 : }
3236 : else
3237 : {
3238 1 : if (!HasWritePermissionsOnMetadataTable())
3239 : {
3240 0 : return false;
3241 : }
3242 1 : if (!bCanCreateSchema)
3243 : {
3244 0 : CPLError(CE_Warning, CPLE_AppDefined,
3245 : "User lacks CREATE SCHEMA privilege. Assuming "
3246 : "ogr_system_tables.metadata table has correct structure");
3247 0 : m_bCreateMetadataTableIfNeededSuccess = true;
3248 0 : return true;
3249 : }
3250 : }
3251 :
3252 3 : hResult =
3253 3 : OGRPG_PQexec(hPGConn, "CREATE SCHEMA IF NOT EXISTS ogr_system_tables");
3254 3 : if (!hResult || (PQresultStatus(hResult) != PGRES_COMMAND_OK &&
3255 0 : PQresultStatus(hResult) != PGRES_TUPLES_OK))
3256 : {
3257 0 : OGRPGClearResult(hResult);
3258 0 : return false;
3259 : }
3260 3 : OGRPGClearResult(hResult);
3261 :
3262 3 : hResult = OGRPG_PQexec(
3263 : hPGConn, "CREATE TABLE IF NOT EXISTS ogr_system_tables.metadata("
3264 : "id SERIAL, "
3265 : "schema_name TEXT NOT NULL, "
3266 : "table_name TEXT NOT NULL, "
3267 : "metadata TEXT,"
3268 : "UNIQUE(schema_name, table_name))");
3269 3 : if (!hResult || (PQresultStatus(hResult) != PGRES_COMMAND_OK &&
3270 0 : PQresultStatus(hResult) != PGRES_TUPLES_OK))
3271 : {
3272 0 : OGRPGClearResult(hResult);
3273 0 : return false;
3274 : }
3275 3 : OGRPGClearResult(hResult);
3276 :
3277 3 : hResult = OGRPG_PQexec(
3278 : hPGConn,
3279 : "DROP FUNCTION IF EXISTS "
3280 : "ogr_system_tables.event_trigger_function_for_metadata() CASCADE");
3281 3 : if (!hResult || (PQresultStatus(hResult) != PGRES_COMMAND_OK &&
3282 0 : PQresultStatus(hResult) != PGRES_TUPLES_OK))
3283 : {
3284 0 : OGRPGClearResult(hResult);
3285 0 : return false;
3286 : }
3287 3 : OGRPGClearResult(hResult);
3288 :
3289 3 : hResult = OGRPG_PQexec(
3290 : hPGConn,
3291 : "CREATE FUNCTION "
3292 : "ogr_system_tables.event_trigger_function_for_metadata()\n"
3293 : "RETURNS event_trigger LANGUAGE plpgsql AS $$\n"
3294 : "DECLARE\n"
3295 : " obj record;\n"
3296 : "BEGIN\n"
3297 : " IF has_schema_privilege('ogr_system_tables', 'USAGE') THEN\n"
3298 : " IF has_table_privilege('ogr_system_tables.metadata', 'DELETE') "
3299 : "THEN\n"
3300 : " FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects()\n"
3301 : " LOOP\n"
3302 : " IF obj.object_type = 'table' THEN\n"
3303 : " DELETE FROM ogr_system_tables.metadata m WHERE "
3304 : "m.schema_name = obj.schema_name AND m.table_name = "
3305 : "obj.object_name;\n"
3306 : " END IF;\n"
3307 : " END LOOP;\n"
3308 : " END IF;\n"
3309 : " END IF;\n"
3310 : "END;\n"
3311 : "$$;");
3312 3 : if (!hResult || (PQresultStatus(hResult) != PGRES_COMMAND_OK &&
3313 0 : PQresultStatus(hResult) != PGRES_TUPLES_OK))
3314 : {
3315 0 : OGRPGClearResult(hResult);
3316 0 : return false;
3317 : }
3318 3 : OGRPGClearResult(hResult);
3319 :
3320 3 : if (bIsSuperUser)
3321 : {
3322 3 : hResult = OGRPG_PQexec(hPGConn,
3323 : "DROP EVENT TRIGGER IF EXISTS "
3324 : "ogr_system_tables_event_trigger_for_metadata");
3325 3 : if (!hResult || (PQresultStatus(hResult) != PGRES_COMMAND_OK &&
3326 0 : PQresultStatus(hResult) != PGRES_TUPLES_OK))
3327 : {
3328 0 : OGRPGClearResult(hResult);
3329 0 : return false;
3330 : }
3331 3 : OGRPGClearResult(hResult);
3332 :
3333 3 : hResult = OGRPG_PQexec(
3334 : hPGConn,
3335 : "CREATE EVENT TRIGGER ogr_system_tables_event_trigger_for_metadata "
3336 : "ON sql_drop "
3337 : "EXECUTE FUNCTION "
3338 : "ogr_system_tables.event_trigger_function_for_metadata()");
3339 3 : if (!hResult || (PQresultStatus(hResult) != PGRES_COMMAND_OK &&
3340 0 : PQresultStatus(hResult) != PGRES_TUPLES_OK))
3341 : {
3342 0 : OGRPGClearResult(hResult);
3343 0 : return false;
3344 : }
3345 3 : OGRPGClearResult(hResult);
3346 : }
3347 :
3348 3 : m_bCreateMetadataTableIfNeededSuccess = true;
3349 3 : m_bOgrSystemTablesMetadataTableExistenceTested = true;
3350 3 : m_bOgrSystemTablesMetadataTableFound = true;
3351 3 : return true;
3352 : }
3353 :
3354 : /************************************************************************/
3355 : /* IsSuperUser() */
3356 : /************************************************************************/
3357 :
3358 4 : bool OGRPGDataSource::IsSuperUser()
3359 : {
3360 4 : PGresult *hResult = OGRPG_PQexec(
3361 4 : hPGConn, "SELECT usesuper FROM pg_user WHERE usename = CURRENT_USER");
3362 : const bool bRet =
3363 7 : (hResult && PQntuples(hResult) == 1 && !PQgetisnull(hResult, 0, 0) &&
3364 3 : strcmp(PQgetvalue(hResult, 0, 0), "t") == 0);
3365 4 : OGRPGClearResult(hResult);
3366 4 : return bRet;
3367 : }
3368 :
3369 : /************************************************************************/
3370 : /* OGRSystemTablesEventTriggerExists() */
3371 : /************************************************************************/
3372 :
3373 1 : bool OGRPGDataSource::OGRSystemTablesEventTriggerExists()
3374 : {
3375 : PGresult *hResult =
3376 1 : OGRPG_PQexec(hPGConn, "SELECT 1 FROM pg_event_trigger WHERE evtname = "
3377 1 : "'ogr_system_tables_event_trigger_for_metadata'");
3378 1 : const bool bRet = (hResult && PQntuples(hResult) == 1);
3379 1 : OGRPGClearResult(hResult);
3380 1 : return bRet;
3381 : }
3382 :
3383 : /************************************************************************/
3384 : /* HasOgrSystemTablesMetadataTable() */
3385 : /************************************************************************/
3386 :
3387 60 : bool OGRPGDataSource::HasOgrSystemTablesMetadataTable()
3388 : {
3389 93 : if (!m_bOgrSystemTablesMetadataTableExistenceTested &&
3390 33 : CPLTestBool(CPLGetConfigOption("OGR_PG_ENABLE_METADATA", "YES")))
3391 : {
3392 30 : m_bOgrSystemTablesMetadataTableExistenceTested = true;
3393 : // Check that the ogr_system_tables.metadata table exists (without
3394 : // causing errors that might abort transactions)
3395 30 : PGresult *hResult = OGRPG_PQexec(
3396 : hPGConn,
3397 : "SELECT c.oid FROM pg_class c "
3398 : "JOIN pg_namespace n ON c.relnamespace=n.oid "
3399 30 : "WHERE c.relname = 'metadata' AND n.nspname = 'ogr_system_tables'");
3400 : const bool bFound =
3401 30 : (hResult && PQntuples(hResult) == 1 && !PQgetisnull(hResult, 0, 0));
3402 30 : OGRPGClearResult(hResult);
3403 30 : if (!bFound)
3404 14 : return false;
3405 :
3406 17 : hResult = OGRPG_PQexec(
3407 : hPGConn,
3408 : "SELECT has_schema_privilege('ogr_system_tables', 'USAGE')");
3409 : const bool bHasSchemaPrivilege =
3410 17 : (hResult && PQntuples(hResult) == 1 &&
3411 51 : !PQgetisnull(hResult, 0, 0) &&
3412 17 : strcmp(PQgetvalue(hResult, 0, 0), "t") == 0);
3413 17 : OGRPGClearResult(hResult);
3414 17 : if (!bHasSchemaPrivilege)
3415 : {
3416 1 : CPLError(CE_Warning, CPLE_AppDefined,
3417 : "Table ogr_system_tables.metadata exists but user lacks "
3418 : "USAGE privilege on ogr_system_tables schema");
3419 1 : return false;
3420 : }
3421 :
3422 16 : hResult = OGRPG_PQexec(
3423 : hPGConn, "SELECT has_table_privilege('ogr_system_tables.metadata', "
3424 : "'SELECT')");
3425 16 : m_bOgrSystemTablesMetadataTableFound =
3426 16 : (hResult && PQntuples(hResult) == 1 &&
3427 48 : !PQgetisnull(hResult, 0, 0) &&
3428 16 : strcmp(PQgetvalue(hResult, 0, 0), "t") == 0);
3429 16 : OGRPGClearResult(hResult);
3430 16 : if (!m_bOgrSystemTablesMetadataTableFound)
3431 : {
3432 0 : CPLError(CE_Warning, CPLE_AppDefined,
3433 : "Table ogr_system_tables.metadata exists but user lacks "
3434 : "SELECT privilege on it");
3435 : }
3436 : }
3437 46 : return m_bOgrSystemTablesMetadataTableFound;
3438 : }
3439 :
3440 : /************************************************************************/
3441 : /* HasWritePermissionsOnMetadataTable() */
3442 : /************************************************************************/
3443 :
3444 12 : bool OGRPGDataSource::HasWritePermissionsOnMetadataTable()
3445 : {
3446 12 : if (!m_bHasWritePermissionsOnMetadataTableRun)
3447 : {
3448 11 : m_bHasWritePermissionsOnMetadataTableRun = true;
3449 :
3450 11 : if (HasOgrSystemTablesMetadataTable())
3451 : {
3452 10 : PGresult *hResult = OGRPG_PQexec(
3453 : hPGConn,
3454 : "SELECT has_table_privilege('ogr_system_tables.metadata', "
3455 : "'INSERT') "
3456 : "AND has_table_privilege('ogr_system_tables.metadata', "
3457 10 : "'DELETE')");
3458 10 : m_bHasWritePermissionsOnMetadataTableSuccess =
3459 10 : (hResult && PQntuples(hResult) == 1 &&
3460 30 : !PQgetisnull(hResult, 0, 0) &&
3461 10 : strcmp(PQgetvalue(hResult, 0, 0), "t") == 0);
3462 10 : OGRPGClearResult(hResult);
3463 10 : if (!m_bHasWritePermissionsOnMetadataTableSuccess)
3464 : {
3465 0 : CPLError(CE_Warning, CPLE_AppDefined,
3466 : "User lacks INSERT and/OR DELETE privilege on "
3467 : "ogr_system_tables.metadata table");
3468 : }
3469 : }
3470 : }
3471 12 : return m_bHasWritePermissionsOnMetadataTableSuccess;
3472 : }
|