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