Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements OGRMySQLDataSource class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : * Author: Howard Butler, hobu@hobu.net
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
10 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include <string>
16 : #include "ogr_mysql.h"
17 :
18 : #include "cpl_conv.h"
19 : #include "cpl_string.h"
20 :
21 : /************************************************************************/
22 : /* FreeResultAndNullify() */
23 : /************************************************************************/
24 :
25 1006 : inline void FreeResultAndNullify(MYSQL_RES *&hResult)
26 : {
27 1006 : if (hResult)
28 : {
29 388 : mysql_free_result(hResult);
30 388 : hResult = nullptr;
31 : }
32 1006 : }
33 :
34 : /************************************************************************/
35 : /* OGRMySQLDataSource() */
36 : /************************************************************************/
37 :
38 63 : OGRMySQLDataSource::OGRMySQLDataSource()
39 : : papoLayers(nullptr), nLayers(0), bDSUpdate(FALSE), hConn(nullptr),
40 63 : poLongResultLayer(nullptr)
41 : {
42 63 : }
43 :
44 : /************************************************************************/
45 : /* ~OGRMySQLDataSource() */
46 : /************************************************************************/
47 :
48 126 : OGRMySQLDataSource::~OGRMySQLDataSource()
49 :
50 : {
51 63 : InterruptLongResult();
52 :
53 249 : for (int i = 0; i < nLayers; i++)
54 186 : delete papoLayers[i];
55 :
56 63 : CPLFree(papoLayers);
57 :
58 63 : if (hConn != nullptr)
59 62 : mysql_close(hConn);
60 126 : }
61 :
62 : /************************************************************************/
63 : /* GetUnknownSRID() */
64 : /************************************************************************/
65 :
66 352 : int OGRMySQLDataSource::GetUnknownSRID() const
67 : {
68 352 : return m_nMajor >= 8 && !m_bIsMariaDB ? 0 : -1;
69 : }
70 :
71 : /************************************************************************/
72 : /* ReportError() */
73 : /************************************************************************/
74 :
75 10 : void OGRMySQLDataSource::ReportError(const char *pszDescription)
76 :
77 : {
78 10 : if (pszDescription)
79 10 : CPLError(CE_Failure, CPLE_AppDefined,
80 : "MySQL error message:%s Description: %s", mysql_error(hConn),
81 : pszDescription);
82 : else
83 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", mysql_error(hConn));
84 10 : }
85 :
86 : /************************************************************************/
87 : /* Open() */
88 : /************************************************************************/
89 :
90 63 : int OGRMySQLDataSource::Open(const char *pszNewName, char **papszOpenOptionsIn,
91 : int bUpdate)
92 :
93 : {
94 63 : CPLAssert(nLayers == 0);
95 :
96 : /* -------------------------------------------------------------------- */
97 : /* Use options process to get .my.cnf file contents. */
98 : /* -------------------------------------------------------------------- */
99 63 : int nPort = 0;
100 63 : char **papszTableNames = nullptr;
101 126 : std::string oHost, oPassword, oUser, oDB;
102 :
103 126 : CPLString osNewName(pszNewName);
104 63 : const char *apszOpenOptions[] = {"dbname", "port", "user",
105 : "password", "host", "tables"};
106 441 : for (int i = 0; i < (int)(sizeof(apszOpenOptions) / sizeof(char *)); i++)
107 : {
108 : const char *pszVal =
109 378 : CSLFetchNameValue(papszOpenOptionsIn, apszOpenOptions[i]);
110 378 : if (pszVal)
111 : {
112 0 : if (osNewName.back() != ':')
113 0 : osNewName += ",";
114 0 : if (i > 0)
115 : {
116 0 : osNewName += apszOpenOptions[i];
117 0 : osNewName += "=";
118 : }
119 0 : if (EQUAL(apszOpenOptions[i], "tables"))
120 : {
121 0 : for (; *pszVal; ++pszVal)
122 : {
123 0 : if (*pszVal == ',')
124 0 : osNewName += ";";
125 : else
126 0 : osNewName += *pszVal;
127 : }
128 : }
129 : else
130 0 : osNewName += pszVal;
131 : }
132 : }
133 :
134 : /* -------------------------------------------------------------------- */
135 : /* Parse out connection information. */
136 : /* -------------------------------------------------------------------- */
137 : char **papszItems =
138 63 : CSLTokenizeString2(osNewName + 6, ",", CSLT_HONOURSTRINGS);
139 :
140 63 : if (CSLCount(papszItems) < 1)
141 : {
142 0 : CSLDestroy(papszItems);
143 0 : CPLError(CE_Failure, CPLE_AppDefined,
144 : "MYSQL: request missing databasename.");
145 0 : return FALSE;
146 : }
147 :
148 63 : oDB = papszItems[0];
149 :
150 311 : for (int i = 1; papszItems[i] != nullptr; i++)
151 : {
152 248 : if (STARTS_WITH_CI(papszItems[i], "user="))
153 62 : oUser = papszItems[i] + 5;
154 186 : else if (STARTS_WITH_CI(papszItems[i], "password="))
155 62 : oPassword = papszItems[i] + 9;
156 124 : else if (STARTS_WITH_CI(papszItems[i], "host="))
157 62 : oHost = papszItems[i] + 5;
158 62 : else if (STARTS_WITH_CI(papszItems[i], "port="))
159 62 : nPort = atoi(papszItems[i] + 5);
160 0 : else if (STARTS_WITH_CI(papszItems[i], "tables="))
161 : {
162 0 : CSLDestroy(papszTableNames);
163 : papszTableNames =
164 0 : CSLTokenizeStringComplex(papszItems[i] + 7, ";", FALSE, FALSE);
165 : }
166 : else
167 0 : CPLError(CE_Warning, CPLE_AppDefined,
168 : "'%s' in MYSQL datasource definition not recognised and "
169 : "ignored.",
170 0 : papszItems[i]);
171 : }
172 :
173 63 : CSLDestroy(papszItems);
174 :
175 : /* -------------------------------------------------------------------- */
176 : /* Try to establish connection. */
177 : /* -------------------------------------------------------------------- */
178 63 : hConn = mysql_init(nullptr);
179 :
180 63 : if (hConn == nullptr)
181 : {
182 0 : CPLError(CE_Failure, CPLE_AppDefined, "mysql_init() failed.");
183 : }
184 :
185 : /* -------------------------------------------------------------------- */
186 : /* Set desired options on the connection: charset and timeout. */
187 : /* -------------------------------------------------------------------- */
188 63 : if (hConn)
189 : {
190 63 : const char *pszTimeoutLength = CPLGetConfigOption("MYSQL_TIMEOUT", "0");
191 :
192 63 : unsigned int timeout = atoi(pszTimeoutLength);
193 63 : mysql_options(hConn, MYSQL_OPT_CONNECT_TIMEOUT, (char *)&timeout);
194 :
195 63 : mysql_options(hConn, MYSQL_SET_CHARSET_NAME, "utf8");
196 : }
197 :
198 : /* -------------------------------------------------------------------- */
199 : /* Perform connection. */
200 : /* -------------------------------------------------------------------- */
201 126 : if (hConn &&
202 251 : mysql_real_connect(hConn, oHost.length() ? oHost.c_str() : nullptr,
203 125 : oUser.length() ? oUser.c_str() : nullptr,
204 125 : oPassword.length() ? oPassword.c_str() : nullptr,
205 126 : oDB.length() ? oDB.c_str() : nullptr, nPort, nullptr,
206 : CLIENT_INTERACTIVE) == nullptr)
207 : {
208 1 : CPLError(CE_Failure, CPLE_AppDefined,
209 : "MySQL connect failed for: %s\n%s", pszNewName + 6,
210 : mysql_error(hConn));
211 1 : mysql_close(hConn);
212 1 : hConn = nullptr;
213 : }
214 :
215 63 : if (hConn == nullptr)
216 : {
217 1 : CSLDestroy(papszTableNames);
218 1 : return FALSE;
219 : }
220 : else
221 : {
222 : // Enable automatic reconnection
223 : #if defined(LIBMYSQL_VERSION_ID) && (LIBMYSQL_VERSION_ID >= 80000)
224 62 : bool reconnect = 1;
225 : #else
226 : my_bool reconnect = 1;
227 : #endif
228 : // Must be called after mysql_real_connect() on MySQL < 5.0.19
229 : // and at any point on more recent versions.
230 62 : mysql_options(hConn, MYSQL_OPT_RECONNECT, &reconnect);
231 : }
232 :
233 62 : bDSUpdate = bUpdate;
234 :
235 : /* -------------------------------------------------------------------- */
236 : /* Check version. */
237 : /* -------------------------------------------------------------------- */
238 62 : auto versionLyr = ExecuteSQL("SELECT VERSION()", nullptr, nullptr);
239 62 : if (versionLyr)
240 : {
241 62 : auto versionFeat = versionLyr->GetNextFeature();
242 62 : if (versionFeat)
243 : {
244 62 : const char *pszVersion = versionFeat->GetFieldAsString(0);
245 62 : m_nMajor = atoi(pszVersion);
246 62 : const char *pszDot = strchr(pszVersion, '.');
247 62 : if (pszDot)
248 62 : m_nMinor = atoi(pszDot + 1);
249 62 : m_bIsMariaDB = strstr(pszVersion, "MariaDB") != nullptr;
250 : }
251 62 : delete versionFeat;
252 62 : ReleaseResultSet(versionLyr);
253 : }
254 :
255 : /* -------------------------------------------------------------------- */
256 : /* Get a list of available tables. */
257 : /* -------------------------------------------------------------------- */
258 62 : if (papszTableNames == nullptr)
259 : {
260 : MYSQL_RES *hResultSet;
261 : MYSQL_ROW papszRow;
262 :
263 62 : if (mysql_query(hConn, "SHOW TABLES"))
264 : {
265 0 : ReportError("SHOW TABLES Failed");
266 0 : return FALSE;
267 : }
268 :
269 62 : hResultSet = mysql_store_result(hConn);
270 62 : if (hResultSet == nullptr)
271 : {
272 0 : ReportError("mysql_store_result() failed on SHOW TABLES result.");
273 0 : return FALSE;
274 : }
275 :
276 84 : while ((papszRow = mysql_fetch_row(hResultSet)) != nullptr)
277 : {
278 22 : if (papszRow[0] == nullptr)
279 0 : continue;
280 :
281 22 : if (EQUAL(papszRow[0], "spatial_ref_sys") ||
282 17 : EQUAL(papszRow[0], "geometry_columns"))
283 10 : continue;
284 :
285 12 : papszTableNames = CSLAddString(papszTableNames, papszRow[0]);
286 : }
287 :
288 62 : FreeResultAndNullify(hResultSet);
289 : }
290 :
291 : /* -------------------------------------------------------------------- */
292 : /* Get the schema of the available tables. */
293 : /* -------------------------------------------------------------------- */
294 74 : for (int iRecord = 0;
295 74 : papszTableNames != nullptr && papszTableNames[iRecord] != nullptr;
296 : iRecord++)
297 : {
298 : // FIXME: This should be fixed to deal with tables
299 : // for which we can't open because the name is bad/
300 12 : OpenTable(papszTableNames[iRecord], bUpdate);
301 : }
302 :
303 62 : CSLDestroy(papszTableNames);
304 :
305 62 : return nLayers > 0 || bUpdate;
306 : }
307 :
308 : /************************************************************************/
309 : /* OpenTable() */
310 : /************************************************************************/
311 :
312 12 : int OGRMySQLDataSource::OpenTable(const char *pszNewName, int bUpdate)
313 :
314 : {
315 : /* -------------------------------------------------------------------- */
316 : /* Create the layer object. */
317 : /* -------------------------------------------------------------------- */
318 : OGRMySQLTableLayer *poLayer;
319 : OGRErr eErr;
320 :
321 12 : poLayer = new OGRMySQLTableLayer(this, pszNewName, bUpdate);
322 12 : eErr = poLayer->Initialize(pszNewName);
323 12 : if (eErr == OGRERR_FAILURE)
324 0 : return FALSE;
325 :
326 : /* -------------------------------------------------------------------- */
327 : /* Add layer to data source layer list. */
328 : /* -------------------------------------------------------------------- */
329 24 : papoLayers = (OGRMySQLLayer **)CPLRealloc(
330 12 : papoLayers, sizeof(OGRMySQLLayer *) * (nLayers + 1));
331 12 : papoLayers[nLayers++] = poLayer;
332 :
333 12 : return TRUE;
334 : }
335 :
336 : /************************************************************************/
337 : /* TestCapability() */
338 : /************************************************************************/
339 :
340 38 : int OGRMySQLDataSource::TestCapability(const char *pszCap)
341 :
342 : {
343 38 : if (EQUAL(pszCap, ODsCCreateLayer))
344 4 : return TRUE;
345 34 : if (EQUAL(pszCap, ODsCDeleteLayer))
346 2 : return TRUE;
347 32 : if (EQUAL(pszCap, ODsCRandomLayerWrite))
348 0 : return TRUE;
349 32 : if (EQUAL(pszCap, ODsCMeasuredGeometries))
350 4 : return TRUE;
351 28 : if (EQUAL(pszCap, ODsCZGeometries))
352 4 : return TRUE;
353 :
354 24 : return FALSE;
355 : }
356 :
357 : /************************************************************************/
358 : /* GetLayer() */
359 : /************************************************************************/
360 :
361 3985 : OGRLayer *OGRMySQLDataSource::GetLayer(int iLayer)
362 :
363 : {
364 3985 : if (iLayer < 0 || iLayer >= nLayers)
365 4 : return nullptr;
366 : else
367 3981 : return papoLayers[iLayer];
368 : }
369 :
370 : /* =====================================================================*/
371 : /* Handle spatial reference id and index for older MySQL and MariaDB */
372 : /* =====================================================================*/
373 :
374 : /************************************************************************/
375 : /* InitializeMetadataTables() */
376 : /* */
377 : /* Create the metadata tables (SPATIAL_REF_SYS and */
378 : /* GEOMETRY_COLUMNS). This method "does no harm" if the tables */
379 : /* exist and can be called at will. */
380 : /************************************************************************/
381 :
382 176 : OGRErr OGRMySQLDataSource::InitializeMetadataTables()
383 :
384 : {
385 : const char *pszCommand;
386 : MYSQL_RES *hResult;
387 176 : OGRErr eErr = OGRERR_NONE;
388 :
389 176 : if (GetMajorVersion() < 8 || IsMariaDB())
390 : {
391 54 : pszCommand = "DESCRIBE geometry_columns";
392 54 : if (mysql_query(GetConn(), pszCommand))
393 : {
394 23 : pszCommand = "CREATE TABLE geometry_columns "
395 : "( F_TABLE_CATALOG VARCHAR(256), "
396 : "F_TABLE_SCHEMA VARCHAR(256), "
397 : "F_TABLE_NAME VARCHAR(256) NOT NULL,"
398 : "F_GEOMETRY_COLUMN VARCHAR(256) NOT NULL, "
399 : "COORD_DIMENSION INT, "
400 : "SRID INT,"
401 : "TYPE VARCHAR(256) NOT NULL)";
402 23 : if (mysql_query(GetConn(), pszCommand))
403 : {
404 0 : ReportError(pszCommand);
405 0 : eErr = OGRERR_FAILURE;
406 : }
407 : else
408 23 : CPLDebug("MYSQL", "Creating geometry_columns metadata table");
409 : }
410 :
411 : // make sure to attempt to free results of successful queries
412 54 : hResult = mysql_store_result(GetConn());
413 54 : FreeResultAndNullify(hResult);
414 :
415 54 : pszCommand = "DESCRIBE spatial_ref_sys";
416 54 : if (mysql_query(GetConn(), pszCommand))
417 : {
418 23 : pszCommand = "CREATE TABLE spatial_ref_sys "
419 : "(SRID INT NOT NULL, "
420 : "AUTH_NAME VARCHAR(256), "
421 : "AUTH_SRID INT, "
422 : "SRTEXT VARCHAR(2048))";
423 23 : if (mysql_query(GetConn(), pszCommand))
424 : {
425 0 : ReportError(pszCommand);
426 0 : eErr = OGRERR_FAILURE;
427 : }
428 : else
429 23 : CPLDebug("MYSQL", "Creating spatial_ref_sys metadata table");
430 : }
431 :
432 : // make sure to attempt to free results of successful queries
433 54 : hResult = mysql_store_result(GetConn());
434 54 : FreeResultAndNullify(hResult);
435 : }
436 :
437 176 : return eErr;
438 : }
439 :
440 176 : OGRErr OGRMySQLDataSource::UpdateMetadataTables(const char *pszLayerName,
441 : OGRwkbGeometryType eType,
442 : const char *pszGeomColumnName,
443 : const int nSRSId)
444 :
445 : {
446 176 : MYSQL_RES *hResult = nullptr;
447 352 : CPLString osCommand;
448 : const char *pszGeometryType;
449 :
450 176 : if (GetMajorVersion() < 8 || IsMariaDB())
451 : {
452 : /* --------------------------------------------------------------------
453 : */
454 : /* Sometimes there is an old crufty entry in the geometry_columns
455 : */
456 : /* table if things were not properly cleaned up before. We make */
457 : /* an effort to clean out such cruft. */
458 : /* */
459 : /* --------------------------------------------------------------------
460 : */
461 : osCommand.Printf(
462 : "DELETE FROM geometry_columns WHERE f_table_name = '%s'",
463 54 : pszLayerName);
464 :
465 54 : if (mysql_query(GetConn(), osCommand))
466 : {
467 0 : ReportError(osCommand);
468 0 : return OGRERR_FAILURE;
469 : }
470 :
471 : // make sure to attempt to free results of successful queries
472 54 : hResult = mysql_store_result(GetConn());
473 54 : FreeResultAndNullify(hResult);
474 :
475 : /* --------------------------------------------------------------------
476 : */
477 : /* Attempt to add this table to the geometry_columns table, if */
478 : /* it is a spatial layer. */
479 : /* --------------------------------------------------------------------
480 : */
481 54 : if (eType != wkbNone)
482 : {
483 52 : const int nCoordDimension = eType == wkbFlatten(eType) ? 2 : 3;
484 :
485 52 : pszGeometryType = OGRToOGCGeomType(eType);
486 :
487 52 : if (nSRSId == GetUnknownSRID())
488 : osCommand.Printf("INSERT INTO geometry_columns "
489 : " (F_TABLE_NAME, "
490 : " F_GEOMETRY_COLUMN, "
491 : " COORD_DIMENSION, "
492 : " TYPE) values "
493 : " ('%s', '%s', %d, '%s')",
494 : pszLayerName, pszGeomColumnName,
495 5 : nCoordDimension, pszGeometryType);
496 : else
497 : osCommand.Printf("INSERT INTO geometry_columns "
498 : " (F_TABLE_NAME, "
499 : " F_GEOMETRY_COLUMN, "
500 : " COORD_DIMENSION, "
501 : " SRID, "
502 : " TYPE) values "
503 : " ('%s', '%s', %d, %d, '%s')",
504 : pszLayerName, pszGeomColumnName,
505 47 : nCoordDimension, nSRSId, pszGeometryType);
506 :
507 52 : if (mysql_query(GetConn(), osCommand))
508 : {
509 0 : ReportError(osCommand);
510 0 : return OGRERR_FAILURE;
511 : }
512 :
513 : // make sure to attempt to free results of successful queries
514 52 : hResult = mysql_store_result(GetConn());
515 52 : FreeResultAndNullify(hResult);
516 : }
517 : }
518 176 : return OGRERR_NONE;
519 : }
520 :
521 : /* =====================================================================*/
522 :
523 : /************************************************************************/
524 : /* FetchSRS() */
525 : /* */
526 : /* Return a SRS corresponding to a particular id. Note that */
527 : /* reference counting should be honoured on the returned */
528 : /* OGRSpatialReference, as handles may be cached. */
529 : /************************************************************************/
530 :
531 172 : const OGRSpatialReference *OGRMySQLDataSource::FetchSRS(int nId)
532 : {
533 172 : if (nId < 0)
534 0 : return nullptr;
535 :
536 : /* -------------------------------------------------------------------- */
537 : /* First, we look through our SRID cache, is it there? */
538 : /* -------------------------------------------------------------------- */
539 172 : auto oIter = m_oSRSCache.find(nId);
540 172 : if (oIter != m_oSRSCache.end())
541 : {
542 133 : return oIter->second.get();
543 : }
544 :
545 : // make sure to attempt to free any old results
546 39 : MYSQL_RES *hResult = mysql_store_result(GetConn());
547 39 : FreeResultAndNullify(hResult);
548 :
549 39 : char szCommand[128] = {};
550 39 : if (GetMajorVersion() < 8 || IsMariaDB())
551 : {
552 18 : snprintf(szCommand, sizeof(szCommand),
553 : "SELECT srtext FROM spatial_ref_sys WHERE srid = %d", nId);
554 : }
555 : else
556 : {
557 21 : snprintf(
558 : szCommand, sizeof(szCommand),
559 : "SELECT DEFINITION FROM "
560 : "INFORMATION_SCHEMA.ST_SPATIAL_REFERENCE_SYSTEMS WHERE SRS_ID = %d",
561 : nId);
562 : }
563 :
564 39 : if (!mysql_query(GetConn(), szCommand))
565 39 : hResult = mysql_store_result(GetConn());
566 :
567 39 : char *pszWKT = nullptr;
568 39 : char **papszRow = nullptr;
569 :
570 39 : if (hResult != nullptr)
571 39 : papszRow = mysql_fetch_row(hResult);
572 :
573 39 : if (papszRow != nullptr && papszRow[0] != nullptr)
574 : {
575 39 : pszWKT = CPLStrdup(papszRow[0]);
576 : }
577 :
578 39 : FreeResultAndNullify(hResult);
579 :
580 : std::unique_ptr<OGRSpatialReference, OGRSpatialReferenceReleaser> poSRS(
581 78 : new OGRSpatialReference());
582 39 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
583 39 : if (pszWKT == nullptr || poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
584 : {
585 3 : poSRS.reset();
586 : }
587 :
588 39 : CPLFree(pszWKT);
589 :
590 39 : if (poSRS)
591 : {
592 : // The WKT found in MySQL 8 ST_SPATIAL_REFERENCE_SYSTEMS is not
593 : // compatible of what GDAL understands.
594 36 : const char *pszAuthorityName = poSRS->GetAuthorityName(nullptr);
595 36 : const char *pszAuthorityCode = poSRS->GetAuthorityCode(nullptr);
596 36 : if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG") &&
597 34 : pszAuthorityCode != nullptr && strlen(pszAuthorityCode) > 0)
598 : {
599 : /* Import 'clean' SRS */
600 34 : poSRS->importFromEPSG(atoi(pszAuthorityCode));
601 : }
602 : }
603 :
604 : /* -------------------------------------------------------------------- */
605 : /* Add to the cache. */
606 : /* -------------------------------------------------------------------- */
607 39 : oIter = m_oSRSCache.emplace(nId, std::move(poSRS)).first;
608 39 : return oIter->second.get();
609 : }
610 :
611 : /************************************************************************/
612 : /* FetchSRSId() */
613 : /* */
614 : /* Fetch the id corresponding to an SRS, and if not found, add */
615 : /* it to the table. */
616 : /************************************************************************/
617 :
618 162 : int OGRMySQLDataSource::FetchSRSId(const OGRSpatialReference *poSRSIn)
619 :
620 : {
621 162 : if (poSRSIn == nullptr)
622 0 : return GetUnknownSRID();
623 :
624 324 : OGRSpatialReference oSRS(*poSRSIn);
625 : // cppcheck-suppress uselessAssignmentPtrArg
626 162 : poSRSIn = nullptr;
627 :
628 162 : const char *pszAuthorityName = oSRS.GetAuthorityName(nullptr);
629 162 : int nAuthorityCode = 0;
630 162 : if (pszAuthorityName == nullptr || strlen(pszAuthorityName) == 0)
631 : {
632 : /* --------------------------------------------------------------------
633 : */
634 : /* Try to identify an EPSG code */
635 : /* --------------------------------------------------------------------
636 : */
637 4 : oSRS.AutoIdentifyEPSG();
638 :
639 4 : pszAuthorityName = oSRS.GetAuthorityName(nullptr);
640 4 : if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
641 : {
642 0 : const char *pszAuthorityCode = oSRS.GetAuthorityCode(nullptr);
643 0 : if (pszAuthorityCode != nullptr && strlen(pszAuthorityCode) > 0)
644 : {
645 : /* Import 'clean' SRS */
646 0 : nAuthorityCode = atoi(pszAuthorityCode);
647 0 : oSRS.importFromEPSG(nAuthorityCode);
648 :
649 0 : pszAuthorityName = oSRS.GetAuthorityName(nullptr);
650 : }
651 4 : }
652 : }
653 : else
654 : {
655 158 : const char *pszAuthorityCode = oSRS.GetAuthorityCode(nullptr);
656 158 : if (pszAuthorityCode)
657 158 : nAuthorityCode = atoi(pszAuthorityCode);
658 : }
659 :
660 : /* -------------------------------------------------------------------- */
661 : /* Check whether the authority name/code is already mapped to a */
662 : /* SRS ID. */
663 : /* -------------------------------------------------------------------- */
664 324 : CPLString osCommand;
665 162 : if (pszAuthorityName != nullptr)
666 : {
667 : /* Check that the authority code is integral */
668 158 : if (nAuthorityCode > 0)
669 : {
670 158 : const char *pszTableName =
671 : "INFORMATION_SCHEMA.ST_SPATIAL_REFERENCE_SYSTEMS";
672 158 : if (GetMajorVersion() < 8 || IsMariaDB())
673 : {
674 45 : pszTableName = "spatial_ref_sys";
675 : osCommand.Printf(
676 : "SELECT srid FROM spatial_ref_sys WHERE "
677 : "auth_name = '%s' AND auth_srid = %d",
678 90 : OGRMySQLEscapeLiteral(pszAuthorityName).c_str(),
679 45 : nAuthorityCode);
680 : }
681 : else
682 : {
683 : osCommand.Printf(
684 : "SELECT SRS_ID FROM "
685 : "INFORMATION_SCHEMA.ST_SPATIAL_REFERENCE_SYSTEMS "
686 : "WHERE ORGANIZATION = '%s' AND ORGANIZATION_COORDSYS_ID = "
687 : "%d",
688 226 : OGRMySQLEscapeLiteral(pszAuthorityName).c_str(),
689 113 : nAuthorityCode);
690 : }
691 :
692 158 : MYSQL_RES *hResult = nullptr;
693 158 : if (!mysql_query(GetConn(), osCommand))
694 158 : hResult = mysql_store_result(GetConn());
695 :
696 158 : if (hResult != nullptr && !mysql_num_rows(hResult))
697 : {
698 45 : CPLDebug("MYSQL",
699 : "No rows exist currently exist in %s for %s:%d",
700 : pszTableName, pszAuthorityName, nAuthorityCode);
701 45 : FreeResultAndNullify(hResult);
702 : }
703 158 : char **papszRow = nullptr;
704 158 : if (hResult != nullptr)
705 113 : papszRow = mysql_fetch_row(hResult);
706 :
707 158 : if (papszRow != nullptr && papszRow[0] != nullptr)
708 : {
709 113 : const int nSRSId = atoi(papszRow[0]);
710 113 : FreeResultAndNullify(hResult);
711 113 : return nSRSId;
712 : }
713 :
714 : // make sure to attempt to free results of successful queries
715 45 : hResult = mysql_store_result(GetConn());
716 45 : FreeResultAndNullify(hResult);
717 : }
718 : }
719 :
720 : /* -------------------------------------------------------------------- */
721 : /* Translate SRS to WKT. */
722 : /* -------------------------------------------------------------------- */
723 49 : char *pszWKT = nullptr;
724 49 : if (oSRS.exportToWkt(&pszWKT) != OGRERR_NONE)
725 : {
726 0 : CPLFree(pszWKT);
727 0 : return GetUnknownSRID();
728 : }
729 :
730 : // MySQL 8 requires AXIS[] node in PROJCS.GEOGCS
731 49 : if (GetMajorVersion() >= 8 && !IsMariaDB() && oSRS.IsProjected())
732 : {
733 4 : OGR_SRSNode oNode;
734 2 : const char *pszWKTTmp = pszWKT;
735 2 : oNode.importFromWkt(&pszWKTTmp);
736 :
737 4 : OGRSpatialReference oSRSGeog;
738 2 : oSRSGeog.CopyGeogCSFrom(&oSRS);
739 2 : char *pszWKTGeog = nullptr;
740 2 : oSRSGeog.exportToWkt(&pszWKTGeog);
741 :
742 2 : int iChild = oNode.FindChild("GEOGCS");
743 2 : if (iChild >= 0)
744 : {
745 2 : oNode.DestroyChild(iChild);
746 2 : auto poGeogNode = new OGR_SRSNode();
747 2 : pszWKTTmp = pszWKTGeog;
748 2 : poGeogNode->importFromWkt(&pszWKTTmp);
749 2 : oNode.InsertChild(poGeogNode, iChild);
750 : }
751 2 : CPLFree(pszWKTGeog);
752 :
753 2 : CPLFree(pszWKT);
754 2 : oNode.exportToWkt(&pszWKT);
755 : }
756 :
757 : /* -------------------------------------------------------------------- */
758 : /* Try to find in the existing record. */
759 : /* -------------------------------------------------------------------- */
760 49 : const char *pszTableName =
761 : "INFORMATION_SCHEMA.ST_SPATIAL_REFERENCE_SYSTEMS";
762 49 : if (GetMajorVersion() < 8 || IsMariaDB())
763 : {
764 47 : pszTableName = "spatial_ref_sys";
765 : osCommand.Printf("SELECT srid FROM spatial_ref_sys WHERE srtext = '%s'",
766 47 : OGRMySQLEscapeLiteral(pszWKT).c_str());
767 : }
768 : else
769 : {
770 : osCommand.Printf("SELECT SRS_ID FROM "
771 : "INFORMATION_SCHEMA.ST_SPATIAL_REFERENCE_SYSTEMS "
772 : "WHERE DEFINITION = '%s'",
773 2 : OGRMySQLEscapeLiteral(pszWKT).c_str());
774 : }
775 :
776 49 : MYSQL_RES *hResult = nullptr;
777 49 : if (!mysql_query(GetConn(), osCommand))
778 49 : hResult = mysql_store_result(GetConn());
779 :
780 49 : if (hResult != nullptr && !mysql_num_rows(hResult))
781 : {
782 18 : CPLDebug("MYSQL", "No rows exist currently exist in %s with WKT = %s",
783 : pszTableName, pszWKT);
784 18 : FreeResultAndNullify(hResult);
785 : }
786 49 : char **papszRow = nullptr;
787 49 : if (hResult != nullptr)
788 31 : papszRow = mysql_fetch_row(hResult);
789 :
790 49 : if (papszRow != nullptr && papszRow[0] != nullptr)
791 : {
792 31 : const int nSRSId = atoi(papszRow[0]);
793 31 : FreeResultAndNullify(hResult);
794 31 : CPLFree(pszWKT);
795 31 : return nSRSId;
796 : }
797 :
798 : // make sure to attempt to free results of successful queries
799 18 : hResult = mysql_store_result(GetConn());
800 18 : FreeResultAndNullify(hResult);
801 :
802 18 : if (GetMajorVersion() >= 8 && !IsMariaDB())
803 : {
804 1 : int nSRSId = -1;
805 1 : if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG") &&
806 : nAuthorityCode > 0)
807 : {
808 : /* ------------------------------------------------------------ */
809 : /* If it is an EPSG code, check if we can use the entry of */
810 : /* SRS_ID equal to the EPSG code. */
811 : /* ------------------------------------------------------------ */
812 : osCommand.Printf("SELECT SRS_ID FROM INFORMATION_SCHEMA."
813 : "ST_SPATIAL_REFERENCE_SYSTEMS "
814 : "WHERE SRS_ID = %d",
815 0 : nAuthorityCode);
816 0 : if (!mysql_query(GetConn(), osCommand))
817 : {
818 0 : hResult = mysql_store_result(GetConn());
819 0 : papszRow = mysql_fetch_row(hResult);
820 0 : if (!(papszRow != nullptr && papszRow[0] != nullptr))
821 : {
822 : // No row matching SRS_ID = nAuthorityCode ? Then
823 : // we can use it
824 0 : nSRSId = nAuthorityCode;
825 : }
826 0 : FreeResultAndNullify(hResult);
827 : }
828 : }
829 1 : if (nSRSId < 0)
830 : {
831 1 : nSRSId = 1;
832 :
833 : /* ------------------------------------------------------------- */
834 : /* Get the current maximum srid in the srs table. */
835 : /* ------------------------------------------------------------- */
836 : osCommand = "SELECT MAX(SRS_ID) FROM "
837 1 : "INFORMATION_SCHEMA.ST_SPATIAL_REFERENCE_SYSTEMS";
838 1 : if (!mysql_query(GetConn(), osCommand))
839 : {
840 1 : hResult = mysql_store_result(GetConn());
841 1 : papszRow = mysql_fetch_row(hResult);
842 1 : if (papszRow != nullptr && papszRow[0] != nullptr)
843 : {
844 1 : nSRSId = atoi(papszRow[0]) + 1;
845 : }
846 1 : FreeResultAndNullify(hResult);
847 : }
848 : }
849 : else
850 : {
851 0 : nSRSId = nAuthorityCode;
852 : }
853 :
854 : /* ----------------------------------------------------------------- */
855 : /* Check if there's an existing record with same name */
856 : /* ----------------------------------------------------------------- */
857 1 : CPLString osName(oSRS.GetName());
858 :
859 : osCommand.Printf(
860 : "SELECT SRS_ID FROM "
861 : "INFORMATION_SCHEMA.ST_SPATIAL_REFERENCE_SYSTEMS WHERE NAME = '%s'",
862 1 : osName.c_str());
863 1 : if (!mysql_query(GetConn(), osCommand))
864 : {
865 0 : hResult = mysql_store_result(GetConn());
866 0 : papszRow = mysql_fetch_row(hResult);
867 0 : if (papszRow != nullptr && papszRow[0] != nullptr)
868 : {
869 0 : osName += CPLSPrintf("_srid_%d", nSRSId);
870 : }
871 0 : FreeResultAndNullify(hResult);
872 : }
873 :
874 : /* ----------------------------------------------------------------- */
875 : /* Try adding the SRS to the SRS table. */
876 : /* ----------------------------------------------------------------- */
877 1 : if (pszAuthorityName != nullptr && nAuthorityCode > 0)
878 : {
879 0 : osCommand.Printf(
880 : "CREATE SPATIAL REFERENCE SYSTEM %d NAME '%s' "
881 : "ORGANIZATION '%s' "
882 : "IDENTIFIED BY %d "
883 : "DEFINITION '%s'",
884 0 : nSRSId, OGRMySQLEscapeLiteral(osName.c_str()).c_str(),
885 0 : OGRMySQLEscapeLiteral(pszAuthorityName).c_str(), nAuthorityCode,
886 0 : OGRMySQLEscapeLiteral(pszWKT).c_str());
887 : }
888 : else
889 : {
890 : osCommand.Printf("CREATE SPATIAL REFERENCE SYSTEM %d NAME '%s' "
891 : "DEFINITION '%s'",
892 : nSRSId,
893 2 : OGRMySQLEscapeLiteral(osName.c_str()).c_str(),
894 3 : OGRMySQLEscapeLiteral(pszWKT).c_str());
895 : }
896 :
897 1 : if (mysql_query(GetConn(), osCommand))
898 : {
899 0 : ReportError((osCommand + " failed").c_str());
900 0 : nSRSId = GetUnknownSRID();
901 : }
902 :
903 1 : hResult = mysql_store_result(GetConn());
904 1 : FreeResultAndNullify(hResult);
905 :
906 1 : CPLFree(pszWKT);
907 1 : return nSRSId;
908 : }
909 :
910 : /* -------------------------------------------------------------------- */
911 : /* Get the current maximum srid in the srs table. */
912 : /* -------------------------------------------------------------------- */
913 17 : osCommand = "SELECT MAX(srid) FROM spatial_ref_sys";
914 17 : if (!mysql_query(GetConn(), osCommand))
915 : {
916 17 : hResult = mysql_store_result(GetConn());
917 17 : papszRow = mysql_fetch_row(hResult);
918 : }
919 :
920 17 : int nSRSId = papszRow != nullptr && papszRow[0] != nullptr
921 34 : ? atoi(papszRow[0]) + 1
922 : : 1;
923 :
924 17 : FreeResultAndNullify(hResult);
925 :
926 : /* -------------------------------------------------------------------- */
927 : /* Try adding the SRS to the SRS table. */
928 : /* -------------------------------------------------------------------- */
929 17 : osCommand.Printf(
930 : "INSERT INTO spatial_ref_sys (srid,srtext) VALUES (%d,'%s')", nSRSId,
931 17 : pszWKT);
932 :
933 17 : if (mysql_query(GetConn(), osCommand))
934 : {
935 0 : ReportError((osCommand + " failed").c_str());
936 0 : nSRSId = GetUnknownSRID();
937 : }
938 :
939 : // make sure to attempt to free results of successful queries
940 17 : hResult = mysql_store_result(GetConn());
941 17 : FreeResultAndNullify(hResult);
942 :
943 17 : CPLFree(pszWKT);
944 :
945 17 : return nSRSId;
946 : }
947 :
948 : /************************************************************************/
949 : /* ExecuteSQL() */
950 : /************************************************************************/
951 :
952 186 : OGRLayer *OGRMySQLDataSource::ExecuteSQL(const char *pszSQLCommand,
953 : OGRGeometry *poSpatialFilter,
954 : const char *pszDialect)
955 :
956 : {
957 186 : if (poSpatialFilter != nullptr)
958 : {
959 2 : CPLDebug("OGR_MYSQL", "Spatial filter ignored for now in "
960 : "OGRMySQLDataSource::ExecuteSQL()");
961 : }
962 :
963 : /* -------------------------------------------------------------------- */
964 : /* Use generic implementation for recognized dialects */
965 : /* -------------------------------------------------------------------- */
966 186 : if (IsGenericSQLDialect(pszDialect))
967 0 : return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter,
968 0 : pszDialect);
969 :
970 : /* -------------------------------------------------------------------- */
971 : /* Special case DELLAYER: command. */
972 : /* -------------------------------------------------------------------- */
973 : #ifdef notdef
974 : if (STARTS_WITH_CI(pszSQLCommand, "DELLAYER:"))
975 : {
976 : const char *pszLayerName = pszSQLCommand + 9;
977 :
978 : while (*pszLayerName == ' ')
979 : pszLayerName++;
980 :
981 : DeleteLayer(pszLayerName);
982 : return NULL;
983 : }
984 : #endif
985 :
986 : /* -------------------------------------------------------------------- */
987 : /* Make sure there isn't an active transaction already. */
988 : /* -------------------------------------------------------------------- */
989 186 : InterruptLongResult();
990 :
991 : /* -------------------------------------------------------------------- */
992 : /* Execute the statement. */
993 : /* -------------------------------------------------------------------- */
994 : MYSQL_RES *hResultSet;
995 :
996 186 : if (mysql_query(hConn, pszSQLCommand))
997 : {
998 2 : ReportError(pszSQLCommand);
999 2 : return nullptr;
1000 : }
1001 :
1002 184 : hResultSet = mysql_use_result(hConn);
1003 184 : if (hResultSet == nullptr)
1004 : {
1005 100 : if (mysql_field_count(hConn) == 0)
1006 : {
1007 100 : CPLDebug("MYSQL", "Command '%s' succeeded, %d rows affected.",
1008 100 : pszSQLCommand, (int)mysql_affected_rows(hConn));
1009 100 : return nullptr;
1010 : }
1011 : else
1012 : {
1013 0 : ReportError(pszSQLCommand);
1014 0 : return nullptr;
1015 : }
1016 : }
1017 :
1018 : /* -------------------------------------------------------------------- */
1019 : /* Do we have a tuple result? If so, instantiate a results */
1020 : /* layer for it. */
1021 : /* -------------------------------------------------------------------- */
1022 :
1023 : OGRMySQLResultLayer *poLayer =
1024 84 : new OGRMySQLResultLayer(this, pszSQLCommand, hResultSet);
1025 :
1026 84 : return poLayer;
1027 : }
1028 :
1029 : /************************************************************************/
1030 : /* ReleaseResultSet() */
1031 : /************************************************************************/
1032 :
1033 84 : void OGRMySQLDataSource::ReleaseResultSet(OGRLayer *poLayer)
1034 :
1035 : {
1036 84 : delete poLayer;
1037 84 : }
1038 :
1039 : /************************************************************************/
1040 : /* LaunderName() */
1041 : /************************************************************************/
1042 :
1043 336 : char *OGRMySQLDataSource::LaunderName(const char *pszSrcName)
1044 :
1045 : {
1046 336 : char *pszSafeName = CPLStrdup(pszSrcName);
1047 :
1048 3760 : for (int i = 0; pszSafeName[i] != '\0'; i++)
1049 : {
1050 3424 : pszSafeName[i] =
1051 3424 : (char)CPLTolower(static_cast<unsigned char>(pszSafeName[i]));
1052 3424 : if (pszSafeName[i] == '-' || pszSafeName[i] == '#')
1053 0 : pszSafeName[i] = '_';
1054 : }
1055 :
1056 336 : return pszSafeName;
1057 : }
1058 :
1059 : /************************************************************************/
1060 : /* RequestLongResult() */
1061 : /* */
1062 : /* Layers need to use mysql_use_result() instead of */
1063 : /* mysql_store_result() so that we won't have to load entire */
1064 : /* result sets into RAM. But only one "streamed" resultset can */
1065 : /* be active on a database connection at a time. So we need to */
1066 : /* maintain a way of closing off an active streaming resultset */
1067 : /* before any other sort of query with a resultset is */
1068 : /* executable. This method (and InterruptLongResult()) */
1069 : /* implement that exclusion. */
1070 : /************************************************************************/
1071 :
1072 256 : void OGRMySQLDataSource::RequestLongResult(OGRMySQLLayer *poNewLayer)
1073 :
1074 : {
1075 256 : InterruptLongResult();
1076 256 : poLongResultLayer = poNewLayer;
1077 256 : }
1078 :
1079 : /************************************************************************/
1080 : /* InterruptLongResult() */
1081 : /************************************************************************/
1082 :
1083 1112 : void OGRMySQLDataSource::InterruptLongResult()
1084 :
1085 : {
1086 1112 : if (poLongResultLayer != nullptr)
1087 : {
1088 409 : poLongResultLayer->ResetReading();
1089 409 : poLongResultLayer = nullptr;
1090 : }
1091 1112 : }
1092 :
1093 : /************************************************************************/
1094 : /* DeleteLayer() */
1095 : /************************************************************************/
1096 :
1097 2 : OGRErr OGRMySQLDataSource::DeleteLayer(int iLayer)
1098 :
1099 : {
1100 2 : if (iLayer < 0 || iLayer >= nLayers)
1101 0 : return OGRERR_FAILURE;
1102 :
1103 : /* -------------------------------------------------------------------- */
1104 : /* Blow away our OGR structures related to the layer. This is */
1105 : /* pretty dangerous if anything has a reference to this layer! */
1106 : /* -------------------------------------------------------------------- */
1107 4 : CPLString osLayerName = papoLayers[iLayer]->GetLayerDefn()->GetName();
1108 :
1109 2 : CPLDebug("MYSQL", "DeleteLayer(%s)", osLayerName.c_str());
1110 :
1111 2 : delete papoLayers[iLayer];
1112 2 : memmove(papoLayers + iLayer, papoLayers + iLayer + 1,
1113 2 : sizeof(void *) * (nLayers - iLayer - 1));
1114 2 : nLayers--;
1115 :
1116 : /* -------------------------------------------------------------------- */
1117 : /* Remove from the database. */
1118 : /* -------------------------------------------------------------------- */
1119 4 : CPLString osCommand;
1120 :
1121 2 : osCommand.Printf("DROP TABLE `%s` ", osLayerName.c_str());
1122 :
1123 2 : if (!mysql_query(GetConn(), osCommand))
1124 : {
1125 2 : CPLDebug("MYSQL", "Dropped table %s.", osLayerName.c_str());
1126 2 : return OGRERR_NONE;
1127 : }
1128 : else
1129 : {
1130 0 : ReportError(osCommand);
1131 0 : return OGRERR_FAILURE;
1132 : }
1133 : }
1134 :
1135 : /************************************************************************/
1136 : /* ICreateLayer() */
1137 : /************************************************************************/
1138 :
1139 : OGRLayer *
1140 176 : OGRMySQLDataSource::ICreateLayer(const char *pszLayerNameIn,
1141 : const OGRGeomFieldDefn *poGeomFieldDefn,
1142 : CSLConstList papszOptions)
1143 :
1144 : {
1145 176 : MYSQL_RES *hResult = nullptr;
1146 352 : CPLString osCommand;
1147 : const char *pszGeomColumnName;
1148 : const char *pszExpectedFIDName;
1149 : char *pszLayerName;
1150 : // int nDimension = 3; // MySQL only supports 2d currently
1151 :
1152 : /* -------------------------------------------------------------------- */
1153 : /* Make sure there isn't an active transaction already. */
1154 : /* -------------------------------------------------------------------- */
1155 176 : InterruptLongResult();
1156 :
1157 176 : const auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
1158 : const auto poSRS =
1159 176 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
1160 :
1161 176 : if (CPLFetchBool(papszOptions, "LAUNDER", true))
1162 176 : pszLayerName = LaunderName(pszLayerNameIn);
1163 : else
1164 0 : pszLayerName = CPLStrdup(pszLayerNameIn);
1165 :
1166 : // if( wkbFlatten(eType) == eType )
1167 : // nDimension = 2;
1168 :
1169 176 : CPLDebug("MYSQL", "Creating layer %s.", pszLayerName);
1170 :
1171 : /* -------------------------------------------------------------------- */
1172 : /* Do we already have this layer? If so, should we blow it */
1173 : /* away? */
1174 : /* -------------------------------------------------------------------- */
1175 :
1176 3996 : for (int iLayer = 0; iLayer < nLayers; iLayer++)
1177 : {
1178 3820 : if (EQUAL(pszLayerName, papoLayers[iLayer]->GetLayerDefn()->GetName()))
1179 : {
1180 :
1181 4 : if (CSLFetchNameValue(papszOptions, "OVERWRITE") != nullptr &&
1182 2 : !EQUAL(CSLFetchNameValue(papszOptions, "OVERWRITE"), "NO"))
1183 : {
1184 2 : DeleteLayer(iLayer);
1185 : }
1186 : else
1187 : {
1188 0 : CPLError(CE_Failure, CPLE_AppDefined,
1189 : "Layer %s already exists, CreateLayer failed.\n"
1190 : "Use the layer creation option OVERWRITE=YES to "
1191 : "replace it.",
1192 : pszLayerName);
1193 0 : CPLFree(pszLayerName);
1194 0 : return nullptr;
1195 : }
1196 : }
1197 : }
1198 :
1199 176 : pszGeomColumnName = CSLFetchNameValue(papszOptions, "GEOMETRY_NAME");
1200 176 : if (!pszGeomColumnName)
1201 176 : pszGeomColumnName = "SHAPE";
1202 :
1203 176 : pszExpectedFIDName = CSLFetchNameValue(papszOptions, "FID");
1204 176 : if (!pszExpectedFIDName)
1205 176 : pszExpectedFIDName = CSLFetchNameValue(papszOptions, "MYSQL_FID");
1206 176 : if (!pszExpectedFIDName)
1207 176 : pszExpectedFIDName = "OGR_FID";
1208 :
1209 176 : const bool bFID64 = CPLFetchBool(papszOptions, "FID64", false);
1210 176 : const char *pszFIDType = bFID64 ? "BIGINT" : "INT";
1211 :
1212 176 : CPLDebug("MYSQL", "Geometry Column Name %s.", pszGeomColumnName);
1213 176 : CPLDebug("MYSQL", "FID Column Name %s.", pszExpectedFIDName);
1214 :
1215 176 : const char *pszSI = CSLFetchNameValue(papszOptions, "SPATIAL_INDEX");
1216 : const bool bHasSI =
1217 176 : (eType != wkbNone && (pszSI == nullptr || CPLTestBool(pszSI)));
1218 :
1219 : // Calling this does no harm
1220 176 : InitializeMetadataTables();
1221 :
1222 : /* -------------------------------------------------------------------- */
1223 : /* Try to get the SRS Id of this spatial reference system, */
1224 : /* adding to the srs table if needed. */
1225 : /* -------------------------------------------------------------------- */
1226 :
1227 176 : int nSRSId = GetUnknownSRID();
1228 176 : if (poSRS != nullptr)
1229 162 : nSRSId = FetchSRSId(poSRS);
1230 :
1231 176 : if (wkbFlatten(eType) == wkbNone)
1232 : {
1233 : osCommand.Printf("CREATE TABLE `%s` ( "
1234 : " %s %s UNIQUE NOT NULL AUTO_INCREMENT )",
1235 4 : pszLayerName, pszExpectedFIDName, pszFIDType);
1236 : }
1237 : else
1238 : {
1239 : // when using mysql8 and SRS is specified, use SRID option for geometry.
1240 172 : if (GetMajorVersion() < 8 || IsMariaDB() || nSRSId == GetUnknownSRID())
1241 : osCommand.Printf("CREATE TABLE `%s` ( "
1242 : " %s %s UNIQUE NOT NULL AUTO_INCREMENT, "
1243 : " %s GEOMETRY %s)",
1244 : pszLayerName, pszExpectedFIDName, pszFIDType,
1245 57 : pszGeomColumnName, bHasSI ? "NOT NULL" : "");
1246 : else
1247 : osCommand.Printf("CREATE TABLE `%s` ( "
1248 : " %s %s UNIQUE NOT NULL AUTO_INCREMENT, "
1249 : " %s GEOMETRY %s /*!80003 SRID %d */)",
1250 : pszLayerName, pszExpectedFIDName, pszFIDType,
1251 : pszGeomColumnName, bHasSI ? "NOT NULL" : "",
1252 115 : nSRSId);
1253 : }
1254 :
1255 176 : if (CSLFetchNameValue(papszOptions, "ENGINE") != nullptr)
1256 : {
1257 0 : osCommand += " ENGINE = ";
1258 0 : osCommand += CSLFetchNameValue(papszOptions, "ENGINE");
1259 : }
1260 :
1261 176 : if (!mysql_query(GetConn(), osCommand))
1262 : {
1263 176 : if (mysql_field_count(GetConn()) == 0)
1264 176 : CPLDebug("MYSQL", "Created table %s.", pszLayerName);
1265 : else
1266 : {
1267 0 : ReportError(osCommand);
1268 0 : return nullptr;
1269 : }
1270 : }
1271 : else
1272 : {
1273 0 : ReportError(osCommand);
1274 0 : return nullptr;
1275 : }
1276 :
1277 : // make sure to attempt to free results of successful queries
1278 176 : hResult = mysql_store_result(GetConn());
1279 176 : FreeResultAndNullify(hResult);
1280 :
1281 176 : if (UpdateMetadataTables(pszLayerName, eType, pszGeomColumnName, nSRSId) !=
1282 : OGRERR_NONE)
1283 0 : return nullptr;
1284 :
1285 : /* -------------------------------------------------------------------- */
1286 : /* Create the spatial index. */
1287 : /* */
1288 : /* We're doing this before we add geometry and record to the table */
1289 : /* so this may not be exactly the best way to do it. */
1290 : /* -------------------------------------------------------------------- */
1291 176 : if (bHasSI)
1292 : {
1293 : osCommand.Printf("ALTER TABLE `%s` ADD SPATIAL INDEX(`%s`) ",
1294 170 : pszLayerName, pszGeomColumnName);
1295 :
1296 170 : if (mysql_query(GetConn(), osCommand))
1297 : {
1298 0 : ReportError(osCommand);
1299 0 : return nullptr;
1300 : }
1301 :
1302 : // make sure to attempt to free results of successful queries
1303 170 : hResult = mysql_store_result(GetConn());
1304 170 : FreeResultAndNullify(hResult);
1305 : }
1306 :
1307 : /* -------------------------------------------------------------------- */
1308 : /* Create the layer object. */
1309 : /* -------------------------------------------------------------------- */
1310 : OGRMySQLTableLayer *poLayer;
1311 : OGRErr eErr;
1312 :
1313 176 : poLayer = new OGRMySQLTableLayer(this, pszLayerName, TRUE, nSRSId);
1314 176 : eErr = poLayer->Initialize(pszLayerName);
1315 176 : if (eErr == OGRERR_FAILURE)
1316 0 : return nullptr;
1317 176 : if (eType != wkbNone)
1318 172 : poLayer->GetLayerDefn()->GetGeomFieldDefn(0)->SetNullable(FALSE);
1319 :
1320 176 : poLayer->SetLaunderFlag(CPLFetchBool(papszOptions, "LAUNDER", true));
1321 176 : poLayer->SetPrecisionFlag(CPLFetchBool(papszOptions, "PRECISION", true));
1322 :
1323 : /* -------------------------------------------------------------------- */
1324 : /* Add layer to data source layer list. */
1325 : /* -------------------------------------------------------------------- */
1326 352 : papoLayers = (OGRMySQLLayer **)CPLRealloc(
1327 176 : papoLayers, sizeof(OGRMySQLLayer *) * (nLayers + 1));
1328 :
1329 176 : papoLayers[nLayers++] = poLayer;
1330 :
1331 176 : CPLFree(pszLayerName);
1332 :
1333 176 : return poLayer;
1334 : }
1335 :
1336 : /************************************************************************/
1337 : /* OGRMySQLEscapeLiteral() */
1338 : /************************************************************************/
1339 :
1340 209 : std::string OGRMySQLEscapeLiteral(const char *pszLiteral)
1341 : {
1342 209 : std::string osVal;
1343 21258 : for (int i = 0; pszLiteral[i] != '\0'; i++)
1344 : {
1345 21049 : if (pszLiteral[i] == '\'')
1346 0 : osVal += '\'';
1347 21049 : osVal += pszLiteral[i];
1348 : }
1349 209 : return osVal;
1350 : }
|