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