Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements OGRPGDumpDataSource class.
5 : * Author: Even Rouault, <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include <algorithm>
14 : #include <cstring>
15 : #include "ogr_pgdump.h"
16 : #include "cpl_conv.h"
17 : #include "cpl_md5.h"
18 : #include "cpl_string.h"
19 :
20 : /************************************************************************/
21 : /* OGRPGDumpDataSource() */
22 : /************************************************************************/
23 :
24 100 : OGRPGDumpDataSource::OGRPGDumpDataSource(const char *pszNameIn,
25 100 : char **papszOptions)
26 : {
27 100 : SetDescription(pszNameIn);
28 :
29 100 : const char *pszCRLFFormat = CSLFetchNameValue(papszOptions, "LINEFORMAT");
30 :
31 100 : bool bUseCRLF = false;
32 100 : if (pszCRLFFormat == nullptr)
33 : {
34 : #ifdef _WIN32
35 : bUseCRLF = true;
36 : #endif
37 : }
38 56 : else if (EQUAL(pszCRLFFormat, "CRLF"))
39 : {
40 1 : bUseCRLF = true;
41 : }
42 55 : else if (EQUAL(pszCRLFFormat, "LF"))
43 : {
44 55 : bUseCRLF = false;
45 : }
46 : else
47 : {
48 0 : CPLError(CE_Warning, CPLE_AppDefined,
49 : "LINEFORMAT=%s not understood, use one of CRLF or LF.",
50 : pszCRLFFormat);
51 : #ifdef _WIN32
52 : bUseCRLF = true;
53 : #endif
54 : }
55 :
56 100 : if (bUseCRLF)
57 1 : m_pszEOL = "\r\n";
58 :
59 100 : m_fp = VSIFOpenL(pszNameIn, "wb");
60 100 : if (m_fp == nullptr)
61 : {
62 1 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszNameIn);
63 1 : return;
64 : }
65 : }
66 :
67 : /************************************************************************/
68 : /* ~OGRPGDumpDataSource() */
69 : /************************************************************************/
70 :
71 200 : OGRPGDumpDataSource::~OGRPGDumpDataSource()
72 :
73 : {
74 100 : EndCopy();
75 100 : m_apoLayers.clear();
76 :
77 100 : if (m_fp)
78 : {
79 99 : LogCommit();
80 99 : VSIFCloseL(m_fp);
81 99 : m_fp = nullptr;
82 : }
83 200 : }
84 :
85 : /************************************************************************/
86 : /* LogStartTransaction() */
87 : /************************************************************************/
88 :
89 115 : void OGRPGDumpDataSource::LogStartTransaction()
90 : {
91 115 : if (m_bInTransaction)
92 0 : return;
93 115 : m_bInTransaction = true;
94 115 : Log("BEGIN");
95 : }
96 :
97 : /************************************************************************/
98 : /* LogCommit() */
99 : /************************************************************************/
100 :
101 214 : void OGRPGDumpDataSource::LogCommit()
102 : {
103 214 : EndCopy();
104 :
105 214 : if (!m_bInTransaction)
106 99 : return;
107 115 : m_bInTransaction = false;
108 115 : Log("COMMIT");
109 : }
110 :
111 : /************************************************************************/
112 : /* OGRPGCommonLaunderName() */
113 : /************************************************************************/
114 :
115 198443 : char *OGRPGCommonLaunderName(const char *pszSrcName, const char *pszDebugPrefix,
116 : bool bUTF8ToASCII)
117 :
118 : {
119 198443 : char *pszSafeName = bUTF8ToASCII ? CPLUTF8ForceToASCII(pszSrcName, '_')
120 198437 : : CPLStrdup(pszSrcName);
121 :
122 198443 : int i = 0; // needed after loop
123 8501410 : for (; i < OGR_PG_NAMEDATALEN - 1 && pszSafeName[i] != '\0'; i++)
124 : {
125 8302960 : if (static_cast<unsigned char>(pszSafeName[i]) <= 127)
126 : {
127 8302950 : pszSafeName[i] =
128 8302950 : (char)CPLTolower(static_cast<unsigned char>(pszSafeName[i]));
129 8302950 : if (pszSafeName[i] == '\'' || pszSafeName[i] == '-' ||
130 8302950 : pszSafeName[i] == '#')
131 : {
132 6 : pszSafeName[i] = '_';
133 : }
134 : }
135 : }
136 :
137 198443 : if (i == OGR_PG_NAMEDATALEN - 1 && pszSafeName[i] != '\0')
138 : {
139 55625 : constexpr int FIRST_8_CHARS_OF_MD5 = 8;
140 55625 : pszSafeName[i - FIRST_8_CHARS_OF_MD5 - 1] = '_';
141 55625 : memcpy(pszSafeName + i - FIRST_8_CHARS_OF_MD5, CPLMD5String(pszSrcName),
142 : FIRST_8_CHARS_OF_MD5);
143 : }
144 :
145 198443 : pszSafeName[i] = '\0';
146 :
147 198443 : if (strcmp(pszSrcName, pszSafeName) != 0)
148 : {
149 176833 : if (CPLStrlenUTF8(pszSafeName) < CPLStrlenUTF8(pszSrcName))
150 : {
151 55625 : CPLError(CE_Warning, CPLE_AppDefined,
152 : "%s identifier truncated to %s", pszSrcName, pszSafeName);
153 : }
154 : else
155 : {
156 121208 : CPLDebug(pszDebugPrefix, "LaunderName('%s') -> '%s'", pszSrcName,
157 : pszSafeName);
158 : }
159 : }
160 :
161 198443 : return pszSafeName;
162 : }
163 :
164 : /************************************************************************/
165 : /* ICreateLayer() */
166 : /************************************************************************/
167 :
168 : OGRLayer *
169 115 : OGRPGDumpDataSource::ICreateLayer(const char *pszLayerName,
170 : const OGRGeomFieldDefn *poGeomFieldDefn,
171 : CSLConstList papszOptions)
172 :
173 : {
174 115 : if (STARTS_WITH(pszLayerName, "pg"))
175 : {
176 0 : CPLError(CE_Warning, CPLE_AppDefined,
177 : "The layer name should not begin by 'pg' as it is a reserved "
178 : "prefix");
179 : }
180 :
181 115 : auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
182 : const auto poSRS =
183 115 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
184 :
185 115 : const bool bCreateTable = CPLFetchBool(papszOptions, "CREATE_TABLE", true);
186 : const bool bCreateSchema =
187 115 : CPLFetchBool(papszOptions, "CREATE_SCHEMA", true);
188 : const char *pszDropTable =
189 115 : CSLFetchNameValueDef(papszOptions, "DROP_TABLE", "IF_EXISTS");
190 115 : int nGeometryTypeFlags = 0;
191 :
192 115 : if (OGR_GT_HasZ(eType))
193 23 : nGeometryTypeFlags |= OGRGeometry::OGR_G_3D;
194 115 : if (OGR_GT_HasM(eType))
195 2 : nGeometryTypeFlags |= OGRGeometry::OGR_G_MEASURED;
196 :
197 115 : int nForcedGeometryTypeFlags = -1;
198 115 : const char *pszDim = CSLFetchNameValue(papszOptions, "DIM");
199 115 : if (pszDim != nullptr)
200 : {
201 19 : if (EQUAL(pszDim, "XY") || EQUAL(pszDim, "2"))
202 : {
203 0 : nGeometryTypeFlags = 0;
204 0 : nForcedGeometryTypeFlags = nGeometryTypeFlags;
205 : }
206 19 : else if (EQUAL(pszDim, "XYZ") || EQUAL(pszDim, "3"))
207 : {
208 7 : nGeometryTypeFlags = OGRGeometry::OGR_G_3D;
209 7 : nForcedGeometryTypeFlags = nGeometryTypeFlags;
210 : }
211 12 : else if (EQUAL(pszDim, "XYM"))
212 : {
213 6 : nGeometryTypeFlags = OGRGeometry::OGR_G_MEASURED;
214 6 : nForcedGeometryTypeFlags = nGeometryTypeFlags;
215 : }
216 6 : else if (EQUAL(pszDim, "XYZM") || EQUAL(pszDim, "4"))
217 : {
218 6 : nGeometryTypeFlags =
219 : OGRGeometry::OGR_G_3D | OGRGeometry::OGR_G_MEASURED;
220 6 : nForcedGeometryTypeFlags = nGeometryTypeFlags;
221 : }
222 : else
223 : {
224 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for DIM");
225 : }
226 : }
227 :
228 115 : const int nDimension =
229 115 : 2 + ((nGeometryTypeFlags & OGRGeometry::OGR_G_3D) ? 1 : 0) +
230 115 : ((nGeometryTypeFlags & OGRGeometry::OGR_G_MEASURED) ? 1 : 0);
231 :
232 : /* Should we turn layers with None geometry type as Unknown/GEOMETRY */
233 : /* so they are still recorded in geometry_columns table ? (#4012) */
234 115 : const bool bNoneAsUnknown = CPLTestBool(
235 : CSLFetchNameValueDef(papszOptions, "NONE_AS_UNKNOWN", "NO"));
236 :
237 115 : if (bNoneAsUnknown && eType == wkbNone)
238 0 : eType = wkbUnknown;
239 :
240 115 : const bool bExtractSchemaFromLayerName = CPLTestBool(CSLFetchNameValueDef(
241 : papszOptions, "EXTRACT_SCHEMA_FROM_LAYER_NAME", "YES"));
242 :
243 : // Postgres Schema handling:
244 :
245 : // Extract schema name from input layer name or passed with -lco SCHEMA.
246 : // Set layer name to "schema.table" or to "table" if schema ==
247 : // current_schema() Usage without schema name is backwards compatible
248 :
249 115 : const char *pszDotPos = strstr(pszLayerName, ".");
250 230 : std::string osTable;
251 230 : std::string osSchema;
252 : const bool bUTF8ToASCII =
253 115 : CPLFetchBool(papszOptions, "LAUNDER_ASCII", false);
254 : const bool bLaunder =
255 115 : bUTF8ToASCII || CPLFetchBool(papszOptions, "LAUNDER", true);
256 :
257 115 : if (pszDotPos != nullptr && bExtractSchemaFromLayerName)
258 : {
259 48 : const size_t length = static_cast<size_t>(pszDotPos - pszLayerName);
260 48 : osSchema = pszLayerName;
261 48 : osSchema.resize(length);
262 :
263 48 : if (bLaunder)
264 : {
265 48 : char *pszTmp = OGRPGCommonLaunderName(pszDotPos + 1, "PGDump",
266 : bUTF8ToASCII); // skip "."
267 48 : osTable = pszTmp;
268 48 : CPLFree(pszTmp);
269 : }
270 : else
271 0 : osTable = OGRPGCommonGenerateShortEnoughIdentifier(pszDotPos +
272 48 : 1); // skip "."
273 : }
274 : else
275 : {
276 67 : if (bLaunder)
277 : {
278 : char *pszTmp =
279 65 : OGRPGCommonLaunderName(pszLayerName, "PGDump", bUTF8ToASCII);
280 65 : osTable = pszTmp;
281 65 : CPLFree(pszTmp);
282 : }
283 : else
284 2 : osTable = OGRPGCommonGenerateShortEnoughIdentifier(pszLayerName);
285 : }
286 :
287 : const std::string osTableEscaped =
288 230 : OGRPGDumpEscapeColumnName(osTable.c_str());
289 115 : const char *pszTableEscaped = osTableEscaped.c_str();
290 :
291 115 : LogCommit();
292 :
293 : /* -------------------------------------------------------------------- */
294 : /* Set the default schema for the layers. */
295 : /* -------------------------------------------------------------------- */
296 230 : CPLString osCommand;
297 :
298 115 : const char *pszSchemaOption = CSLFetchNameValue(papszOptions, "SCHEMA");
299 115 : if (pszSchemaOption)
300 : {
301 2 : osSchema = pszSchemaOption;
302 2 : if (bCreateSchema)
303 : {
304 : osCommand.Printf(
305 : "CREATE SCHEMA %s",
306 2 : OGRPGDumpEscapeColumnName(osSchema.c_str()).c_str());
307 2 : Log(osCommand);
308 : }
309 : }
310 :
311 115 : const bool bTemporary = CPLFetchBool(papszOptions, "TEMPORARY", false);
312 115 : if (bTemporary)
313 : {
314 1 : osSchema = "pg_temp";
315 : }
316 :
317 115 : if (osSchema.empty())
318 : {
319 64 : osSchema = "public";
320 : }
321 : const std::string osSchemaEscaped =
322 230 : OGRPGDumpEscapeColumnName(osSchema.c_str());
323 115 : const char *pszSchemaEscaped = osSchemaEscaped.c_str();
324 :
325 : /* -------------------------------------------------------------------- */
326 : /* Do we already have this layer? */
327 : /* -------------------------------------------------------------------- */
328 131 : for (const auto &poLayer : m_apoLayers)
329 : {
330 16 : if (EQUAL(pszLayerName, poLayer->GetDescription()))
331 : {
332 0 : CPLError(CE_Failure, CPLE_AppDefined,
333 : "Layer %s already exists, CreateLayer failed.\n",
334 : pszLayerName);
335 0 : return nullptr;
336 : }
337 : }
338 :
339 115 : if (bCreateTable &&
340 114 : (EQUAL(pszDropTable, "YES") || EQUAL(pszDropTable, "ON") ||
341 114 : EQUAL(pszDropTable, "TRUE") || EQUAL(pszDropTable, "IF_EXISTS")))
342 : {
343 114 : if (EQUAL(pszDropTable, "IF_EXISTS"))
344 : osCommand.Printf("DROP TABLE IF EXISTS %s.%s CASCADE",
345 114 : pszSchemaEscaped, pszTableEscaped);
346 : else
347 : osCommand.Printf("DROP TABLE %s.%s CASCADE", pszSchemaEscaped,
348 0 : pszTableEscaped);
349 114 : Log(osCommand);
350 : }
351 :
352 : /* -------------------------------------------------------------------- */
353 : /* Handle the GEOM_TYPE option. */
354 : /* -------------------------------------------------------------------- */
355 115 : const char *pszGeomType = CSLFetchNameValue(papszOptions, "GEOM_TYPE");
356 115 : if (pszGeomType == nullptr)
357 : {
358 108 : pszGeomType = "geometry";
359 : }
360 :
361 115 : if (!EQUAL(pszGeomType, "geometry") && !EQUAL(pszGeomType, "geography"))
362 : {
363 0 : CPLError(
364 : CE_Failure, CPLE_AppDefined,
365 : "GEOM_TYPE in PostGIS enabled databases must be 'geometry' or "
366 : "'geography'. Creation of layer %s with GEOM_TYPE %s has failed.",
367 : pszLayerName, pszGeomType);
368 0 : return nullptr;
369 : }
370 :
371 : /* -------------------------------------------------------------------- */
372 : /* Try to get the SRS Id of this spatial reference system, */
373 : /* adding tot the srs table if needed. */
374 : /* -------------------------------------------------------------------- */
375 : const char *pszPostgisVersion =
376 115 : CSLFetchNameValueDef(papszOptions, "POSTGIS_VERSION", "2.2");
377 115 : const int nPostGISMajor = atoi(pszPostgisVersion);
378 115 : const char *pszPostgisVersionDot = strchr(pszPostgisVersion, '.');
379 115 : const int nPostGISMinor =
380 115 : pszPostgisVersionDot ? atoi(pszPostgisVersionDot + 1) : 0;
381 115 : const int nUnknownSRSId = nPostGISMajor >= 2 ? 0 : -1;
382 :
383 115 : int nSRSId = nUnknownSRSId;
384 115 : int nForcedSRSId = -2;
385 115 : const char *pszSRID = CSLFetchNameValue(papszOptions, "SRID");
386 115 : if (pszSRID)
387 : {
388 1 : nSRSId = atoi(pszSRID);
389 1 : nForcedSRSId = nSRSId;
390 : }
391 : else
392 : {
393 114 : if (poSRS)
394 : {
395 0 : const char *pszAuthorityName = poSRS->GetAuthorityName(nullptr);
396 0 : if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
397 : {
398 : /* Assume the EPSG Id is the SRS ID. Might be a wrong guess ! */
399 0 : nSRSId = atoi(poSRS->GetAuthorityCode(nullptr));
400 : }
401 : else
402 : {
403 0 : const char *pszGeogCSName = poSRS->GetAttrValue("GEOGCS");
404 0 : if (pszGeogCSName != nullptr &&
405 0 : EQUAL(pszGeogCSName, "GCS_WGS_1984"))
406 : {
407 0 : nSRSId = 4326;
408 : }
409 : }
410 : }
411 : }
412 :
413 : const std::string osEscapedTableNameSingleQuote =
414 230 : OGRPGDumpEscapeString(osTable.c_str());
415 : const char *pszEscapedTableNameSingleQuote =
416 115 : osEscapedTableNameSingleQuote.c_str();
417 :
418 115 : const char *pszGeometryType = OGRToOGCGeomType(eType);
419 :
420 115 : const char *pszGFldName = CSLFetchNameValue(papszOptions, "GEOMETRY_NAME");
421 115 : if (eType != wkbNone && !EQUAL(pszGeomType, "geography"))
422 : {
423 78 : if (pszGFldName == nullptr)
424 72 : pszGFldName = "wkb_geometry";
425 :
426 78 : if (nPostGISMajor < 2)
427 : {
428 : // Sometimes there is an old cruft entry in the geometry_columns
429 : // table if things were not properly cleaned up before. We make
430 : // an effort to clean out such cruft.
431 : //
432 : // Note: PostGIS 2.0 defines geometry_columns as a view (no clean up
433 : // is needed).
434 :
435 : osCommand.Printf("DELETE FROM geometry_columns "
436 : "WHERE f_table_name = %s AND f_table_schema = %s",
437 : pszEscapedTableNameSingleQuote,
438 1 : OGRPGDumpEscapeString(osSchema.c_str()).c_str());
439 1 : if (bCreateTable)
440 1 : Log(osCommand);
441 : }
442 : }
443 :
444 115 : LogStartTransaction();
445 :
446 : /* -------------------------------------------------------------------- */
447 : /* Create an empty table first. */
448 : /* -------------------------------------------------------------------- */
449 115 : if (bCreateTable)
450 : {
451 114 : if (bTemporary)
452 : {
453 1 : osCommand.Printf("CREATE TEMPORARY TABLE %s()", pszTableEscaped);
454 : }
455 : else
456 : {
457 : osCommand.Printf("CREATE%s TABLE %s.%s()",
458 113 : CPLFetchBool(papszOptions, "UNLOGGED", false)
459 : ? " UNLOGGED"
460 : : "",
461 113 : pszSchemaEscaped, pszTableEscaped);
462 : }
463 114 : Log(osCommand);
464 : }
465 :
466 : /* -------------------------------------------------------------------- */
467 : /* Add FID if needed. */
468 : /* -------------------------------------------------------------------- */
469 115 : const char *pszFIDColumnNameIn = CSLFetchNameValue(papszOptions, "FID");
470 230 : CPLString osFIDColumnName;
471 115 : if (pszFIDColumnNameIn == nullptr)
472 109 : osFIDColumnName = "ogc_fid";
473 : else
474 : {
475 6 : if (bLaunder)
476 : {
477 6 : char *pszLaunderedFid = OGRPGCommonLaunderName(
478 : pszFIDColumnNameIn, "PGDump", bUTF8ToASCII);
479 6 : osFIDColumnName = pszLaunderedFid;
480 6 : CPLFree(pszLaunderedFid);
481 : }
482 : else
483 : {
484 0 : osFIDColumnName = pszFIDColumnNameIn;
485 : }
486 : }
487 : const CPLString osFIDColumnNameEscaped =
488 230 : OGRPGDumpEscapeColumnName(osFIDColumnName);
489 :
490 115 : const bool bFID64 = CPLFetchBool(papszOptions, "FID64", false);
491 115 : const char *pszSerialType = bFID64 ? "BIGSERIAL" : "SERIAL";
492 :
493 115 : if (bCreateTable && !osFIDColumnName.empty())
494 : {
495 224 : std::string osConstraintName(osTable);
496 112 : if (osConstraintName.size() + strlen("_pk") >
497 : static_cast<size_t>(OGR_PG_NAMEDATALEN - 1))
498 : {
499 4 : osConstraintName.resize(OGR_PG_NAMEDATALEN - 1 - strlen("_pk"));
500 : }
501 112 : osConstraintName += "_pk";
502 : osCommand.Printf(
503 : "ALTER TABLE %s.%s ADD COLUMN %s %s "
504 : "CONSTRAINT %s PRIMARY KEY",
505 : pszSchemaEscaped, pszTableEscaped, osFIDColumnNameEscaped.c_str(),
506 : pszSerialType,
507 112 : OGRPGDumpEscapeColumnName(osConstraintName.c_str()).c_str());
508 112 : Log(osCommand);
509 : }
510 :
511 : /* -------------------------------------------------------------------- */
512 : /* Create geometry/geography column (actual creation possibly */
513 : /* deferred). */
514 : /* -------------------------------------------------------------------- */
515 230 : std::vector<std::string> aosGeomCommands;
516 115 : if (bCreateTable && eType != wkbNone && EQUAL(pszGeomType, "geography"))
517 : {
518 7 : if (CSLFetchNameValue(papszOptions, "GEOMETRY_NAME") != nullptr)
519 0 : pszGFldName = CSLFetchNameValue(papszOptions, "GEOMETRY_NAME");
520 : else
521 7 : pszGFldName = "the_geog";
522 :
523 7 : const char *suffix = "";
524 7 : if ((nGeometryTypeFlags & OGRGeometry::OGR_G_MEASURED) &&
525 4 : (nGeometryTypeFlags & OGRGeometry::OGR_G_3D))
526 : {
527 2 : suffix = "ZM";
528 : }
529 5 : else if ((nGeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
530 : {
531 2 : suffix = "M";
532 : }
533 3 : else if ((nGeometryTypeFlags & OGRGeometry::OGR_G_3D))
534 : {
535 2 : suffix = "Z";
536 : }
537 :
538 7 : if (nSRSId)
539 : osCommand.Printf("ALTER TABLE %s.%s "
540 : "ADD COLUMN %s geography(%s%s,%d)",
541 : pszSchemaEscaped, pszTableEscaped,
542 0 : OGRPGDumpEscapeColumnName(pszGFldName).c_str(),
543 0 : pszGeometryType, suffix, nSRSId);
544 : else
545 : osCommand.Printf("ALTER TABLE %s.%s "
546 : "ADD COLUMN %s geography(%s%s)",
547 : pszSchemaEscaped, pszTableEscaped,
548 14 : OGRPGDumpEscapeColumnName(pszGFldName).c_str(),
549 7 : pszGeometryType, suffix);
550 7 : aosGeomCommands.push_back(osCommand);
551 : }
552 108 : else if (bCreateTable && eType != wkbNone)
553 : {
554 77 : const char *suffix = "";
555 77 : if (nGeometryTypeFlags ==
556 80 : static_cast<int>(OGRGeometry::OGR_G_MEASURED) &&
557 3 : wkbFlatten(eType) != wkbUnknown)
558 : {
559 2 : suffix = "M";
560 : }
561 :
562 : osCommand.Printf(
563 : "SELECT AddGeometryColumn(%s,%s,%s,%d,'%s%s',%d)",
564 154 : OGRPGDumpEscapeString(bTemporary ? "" : osSchema.c_str()).c_str(),
565 : pszEscapedTableNameSingleQuote,
566 154 : OGRPGDumpEscapeString(pszGFldName).c_str(), nSRSId, pszGeometryType,
567 154 : suffix, nDimension);
568 77 : aosGeomCommands.push_back(osCommand);
569 : }
570 :
571 : const char *pszSI =
572 115 : CSLFetchNameValueDef(papszOptions, "SPATIAL_INDEX", "GIST");
573 115 : const bool bCreateSpatialIndex =
574 0 : (EQUAL(pszSI, "GIST") || EQUAL(pszSI, "SPGIST") ||
575 115 : EQUAL(pszSI, "BRIN") || EQUAL(pszSI, "YES") || EQUAL(pszSI, "ON") ||
576 0 : EQUAL(pszSI, "TRUE"));
577 115 : if (!bCreateSpatialIndex && !EQUAL(pszSI, "NO") && !EQUAL(pszSI, "OFF") &&
578 0 : !EQUAL(pszSI, "FALSE") && !EQUAL(pszSI, "NONE"))
579 : {
580 0 : CPLError(CE_Warning, CPLE_NotSupported,
581 : "SPATIAL_INDEX=%s not supported", pszSI);
582 : }
583 230 : const char *pszSpatialIndexType = EQUAL(pszSI, "SPGIST") ? "SPGIST"
584 115 : : EQUAL(pszSI, "BRIN") ? "BRIN"
585 : : "GIST";
586 :
587 230 : std::vector<std::string> aosSpatialIndexCreationCommands;
588 115 : if (bCreateTable && bCreateSpatialIndex && pszGFldName && eType != wkbNone)
589 : {
590 : const std::string osIndexName(OGRPGCommonGenerateSpatialIndexName(
591 168 : osTable.c_str(), pszGFldName, 0));
592 :
593 : /* --------------------------------------------------------------- */
594 : /* Create the spatial index. */
595 : /* --------------------------------------------------------------- */
596 : osCommand.Printf("CREATE INDEX %s "
597 : "ON %s.%s "
598 : "USING %s (%s)",
599 168 : OGRPGDumpEscapeColumnName(osIndexName.c_str()).c_str(),
600 : pszSchemaEscaped, pszTableEscaped, pszSpatialIndexType,
601 252 : OGRPGDumpEscapeColumnName(pszGFldName).c_str());
602 84 : aosSpatialIndexCreationCommands.push_back(osCommand);
603 : }
604 :
605 : /* -------------------------------------------------------------------- */
606 : /* Create the layer object. */
607 : /* -------------------------------------------------------------------- */
608 : const bool bWriteAsHex =
609 115 : !CPLFetchBool(papszOptions, "WRITE_EWKT_GEOM", false);
610 :
611 : auto poLayer = std::make_unique<OGRPGDumpLayer>(
612 115 : this, osSchema.c_str(), osTable.c_str(),
613 115 : !osFIDColumnName.empty() ? osFIDColumnName.c_str() : nullptr,
614 230 : bWriteAsHex, bCreateTable);
615 115 : poLayer->SetLaunderFlag(bLaunder);
616 115 : poLayer->SetUTF8ToASCIIFlag(bUTF8ToASCII);
617 115 : poLayer->SetPrecisionFlag(CPLFetchBool(papszOptions, "PRECISION", true));
618 :
619 : const char *pszOverrideColumnTypes =
620 115 : CSLFetchNameValue(papszOptions, "COLUMN_TYPES");
621 115 : poLayer->SetOverrideColumnTypes(pszOverrideColumnTypes);
622 115 : poLayer->SetUnknownSRSId(nUnknownSRSId);
623 115 : poLayer->SetForcedSRSId(nForcedSRSId);
624 115 : poLayer->SetCreateSpatialIndex(bCreateSpatialIndex, pszSpatialIndexType);
625 115 : poLayer->SetPostGISVersion(nPostGISMajor, nPostGISMinor);
626 115 : poLayer->SetForcedGeometryTypeFlags(nForcedGeometryTypeFlags);
627 :
628 : // Log geometry field creation immediately or defer it, according to
629 : // GEOM_COLUMN_POSITION
630 115 : const bool bGeomColumnPositionImmediate = EQUAL(
631 : CSLFetchNameValueDef(papszOptions, "GEOM_COLUMN_POSITION", "IMMEDIATE"),
632 : "IMMEDIATE");
633 115 : poLayer->SetGeomColumnPositionImmediate(bGeomColumnPositionImmediate);
634 115 : if (bGeomColumnPositionImmediate)
635 : {
636 188 : for (const auto &osSQL : aosGeomCommands)
637 79 : Log(osSQL.c_str());
638 : }
639 : else
640 : {
641 6 : poLayer->SetDeferredGeomFieldCreationCommands(aosGeomCommands);
642 : }
643 115 : poLayer->SetSpatialIndexCreationCommands(aosSpatialIndexCreationCommands);
644 :
645 115 : const char *pszDescription = CSLFetchNameValue(papszOptions, "DESCRIPTION");
646 115 : if (pszDescription != nullptr)
647 1 : poLayer->SetForcedDescription(pszDescription);
648 :
649 115 : if (eType != wkbNone)
650 : {
651 170 : OGRGeomFieldDefn oTmp(pszGFldName, eType);
652 85 : auto poGeomField = std::make_unique<OGRPGDumpGeomFieldDefn>(&oTmp);
653 85 : poGeomField->m_nSRSId = nSRSId;
654 85 : poGeomField->m_nGeometryTypeFlags = nGeometryTypeFlags;
655 85 : poLayer->GetLayerDefn()->AddGeomFieldDefn(std::move(poGeomField));
656 : }
657 30 : else if (pszGFldName)
658 6 : poLayer->SetGeometryFieldName(pszGFldName);
659 :
660 : /* -------------------------------------------------------------------- */
661 : /* Add layer to data source layer list. */
662 : /* -------------------------------------------------------------------- */
663 115 : m_apoLayers.emplace_back(std::move(poLayer));
664 :
665 115 : return m_apoLayers.back().get();
666 : }
667 :
668 : /************************************************************************/
669 : /* TestCapability() */
670 : /************************************************************************/
671 :
672 75 : int OGRPGDumpDataSource::TestCapability(const char *pszCap)
673 :
674 : {
675 75 : if (EQUAL(pszCap, ODsCCreateLayer))
676 39 : return TRUE;
677 36 : else if (EQUAL(pszCap, ODsCCreateGeomFieldAfterCreateLayer))
678 13 : return TRUE;
679 23 : else if (EQUAL(pszCap, ODsCCurveGeometries))
680 0 : return TRUE;
681 23 : else if (EQUAL(pszCap, ODsCMeasuredGeometries))
682 0 : return TRUE;
683 23 : else if (EQUAL(pszCap, ODsCZGeometries))
684 0 : return TRUE;
685 23 : else if (EQUAL(pszCap, ODsCRandomLayerWrite))
686 0 : return TRUE;
687 : else
688 23 : return FALSE;
689 : }
690 :
691 : /************************************************************************/
692 : /* GetLayer() */
693 : /************************************************************************/
694 :
695 7 : OGRLayer *OGRPGDumpDataSource::GetLayer(int iLayer)
696 :
697 : {
698 7 : if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
699 0 : return nullptr;
700 : else
701 7 : return m_apoLayers[iLayer].get();
702 : }
703 :
704 : /************************************************************************/
705 : /* Log() */
706 : /************************************************************************/
707 :
708 1244 : bool OGRPGDumpDataSource::Log(const char *pszStr, bool bAddSemiColumn)
709 : {
710 1244 : if (m_fp == nullptr)
711 : {
712 1 : return false;
713 : }
714 :
715 1243 : VSIFWriteL(pszStr, strlen(pszStr), 1, m_fp);
716 1243 : if (bAddSemiColumn)
717 : {
718 1202 : const char chSemiColumn = ';';
719 1202 : VSIFWriteL(&chSemiColumn, 1, 1, m_fp);
720 : }
721 1243 : VSIFWriteL(m_pszEOL, strlen(m_pszEOL), 1, m_fp);
722 1243 : return true;
723 : }
724 :
725 : /************************************************************************/
726 : /* StartCopy() */
727 : /************************************************************************/
728 10 : void OGRPGDumpDataSource::StartCopy(OGRPGDumpLayer *poPGLayer)
729 : {
730 10 : EndCopy();
731 10 : m_poLayerInCopyMode = poPGLayer;
732 10 : }
733 :
734 : /************************************************************************/
735 : /* EndCopy() */
736 : /************************************************************************/
737 324 : OGRErr OGRPGDumpDataSource::EndCopy()
738 : {
739 324 : if (m_poLayerInCopyMode != nullptr)
740 : {
741 10 : OGRErr result = m_poLayerInCopyMode->EndCopy();
742 10 : m_poLayerInCopyMode = nullptr;
743 :
744 10 : return result;
745 : }
746 :
747 314 : return OGRERR_NONE;
748 : }
|