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