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