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 : #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)
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 : OGRLayer *OGRMySQLDataSource::GetLayer(int iLayer)
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 : const OGRSpatialReference *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.get();
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 : std::unique_ptr<OGRSpatialReference, OGRSpatialReferenceReleaser> poSRS(
583 78 : new OGRSpatialReference());
584 39 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
585 39 : if (pszWKT == nullptr || poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
586 : {
587 3 : poSRS.reset();
588 : }
589 :
590 39 : CPLFree(pszWKT);
591 :
592 39 : if (poSRS)
593 : {
594 : // The WKT found in MySQL 8 ST_SPATIAL_REFERENCE_SYSTEMS is not
595 : // compatible of what GDAL understands.
596 36 : const char *pszAuthorityName = poSRS->GetAuthorityName(nullptr);
597 36 : const char *pszAuthorityCode = poSRS->GetAuthorityCode(nullptr);
598 36 : if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG") &&
599 34 : pszAuthorityCode != nullptr && strlen(pszAuthorityCode) > 0)
600 : {
601 : /* Import 'clean' SRS */
602 34 : poSRS->importFromEPSG(atoi(pszAuthorityCode));
603 : }
604 : }
605 :
606 : /* -------------------------------------------------------------------- */
607 : /* Add to the cache. */
608 : /* -------------------------------------------------------------------- */
609 39 : oIter = m_oSRSCache.emplace(nId, std::move(poSRS)).first;
610 39 : return oIter->second.get();
611 : }
612 :
613 : /************************************************************************/
614 : /* FetchSRSId() */
615 : /* */
616 : /* Fetch the id corresponding to an SRS, and if not found, add */
617 : /* it to the table. */
618 : /************************************************************************/
619 :
620 162 : int OGRMySQLDataSource::FetchSRSId(const OGRSpatialReference *poSRSIn)
621 :
622 : {
623 162 : if (poSRSIn == nullptr)
624 0 : return GetUnknownSRID();
625 :
626 324 : OGRSpatialReference oSRS(*poSRSIn);
627 : // cppcheck-suppress uselessAssignmentPtrArg
628 162 : poSRSIn = nullptr;
629 :
630 162 : const char *pszAuthorityName = oSRS.GetAuthorityName(nullptr);
631 162 : int nAuthorityCode = 0;
632 162 : if (pszAuthorityName == nullptr || strlen(pszAuthorityName) == 0)
633 : {
634 : /* --------------------------------------------------------------------
635 : */
636 : /* Try to identify an EPSG code */
637 : /* --------------------------------------------------------------------
638 : */
639 4 : oSRS.AutoIdentifyEPSG();
640 :
641 4 : pszAuthorityName = oSRS.GetAuthorityName(nullptr);
642 4 : if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
643 : {
644 0 : const char *pszAuthorityCode = oSRS.GetAuthorityCode(nullptr);
645 0 : if (pszAuthorityCode != nullptr && strlen(pszAuthorityCode) > 0)
646 : {
647 : /* Import 'clean' SRS */
648 0 : nAuthorityCode = atoi(pszAuthorityCode);
649 0 : oSRS.importFromEPSG(nAuthorityCode);
650 :
651 0 : pszAuthorityName = oSRS.GetAuthorityName(nullptr);
652 : }
653 4 : }
654 : }
655 : else
656 : {
657 158 : const char *pszAuthorityCode = oSRS.GetAuthorityCode(nullptr);
658 158 : if (pszAuthorityCode)
659 158 : nAuthorityCode = atoi(pszAuthorityCode);
660 : }
661 :
662 : /* -------------------------------------------------------------------- */
663 : /* Check whether the authority name/code is already mapped to a */
664 : /* SRS ID. */
665 : /* -------------------------------------------------------------------- */
666 324 : CPLString osCommand;
667 162 : if (pszAuthorityName != nullptr)
668 : {
669 : /* Check that the authority code is integral */
670 158 : if (nAuthorityCode > 0)
671 : {
672 158 : const char *pszTableName =
673 : "INFORMATION_SCHEMA.ST_SPATIAL_REFERENCE_SYSTEMS";
674 158 : if (GetMajorVersion() < 8 || IsMariaDB())
675 : {
676 45 : pszTableName = "spatial_ref_sys";
677 : osCommand.Printf(
678 : "SELECT srid FROM spatial_ref_sys WHERE "
679 : "auth_name = '%s' AND auth_srid = %d",
680 90 : OGRMySQLEscapeLiteral(pszAuthorityName).c_str(),
681 45 : nAuthorityCode);
682 : }
683 : else
684 : {
685 : osCommand.Printf(
686 : "SELECT SRS_ID FROM "
687 : "INFORMATION_SCHEMA.ST_SPATIAL_REFERENCE_SYSTEMS "
688 : "WHERE ORGANIZATION = '%s' AND ORGANIZATION_COORDSYS_ID = "
689 : "%d",
690 226 : OGRMySQLEscapeLiteral(pszAuthorityName).c_str(),
691 113 : nAuthorityCode);
692 : }
693 :
694 158 : MYSQL_RES *hResult = nullptr;
695 158 : if (!mysql_query(GetConn(), osCommand))
696 158 : hResult = mysql_store_result(GetConn());
697 :
698 158 : if (hResult != nullptr && !mysql_num_rows(hResult))
699 : {
700 45 : CPLDebug("MYSQL",
701 : "No rows exist currently exist in %s for %s:%d",
702 : pszTableName, pszAuthorityName, nAuthorityCode);
703 45 : FreeResultAndNullify(hResult);
704 : }
705 158 : char **papszRow = nullptr;
706 158 : if (hResult != nullptr)
707 113 : papszRow = mysql_fetch_row(hResult);
708 :
709 158 : if (papszRow != nullptr && papszRow[0] != nullptr)
710 : {
711 113 : const int nSRSId = atoi(papszRow[0]);
712 113 : FreeResultAndNullify(hResult);
713 113 : return nSRSId;
714 : }
715 :
716 : // make sure to attempt to free results of successful queries
717 45 : hResult = mysql_store_result(GetConn());
718 45 : FreeResultAndNullify(hResult);
719 : }
720 : }
721 :
722 : /* -------------------------------------------------------------------- */
723 : /* Translate SRS to WKT. */
724 : /* -------------------------------------------------------------------- */
725 49 : char *pszWKT = nullptr;
726 49 : if (oSRS.exportToWkt(&pszWKT) != OGRERR_NONE)
727 : {
728 0 : CPLFree(pszWKT);
729 0 : return GetUnknownSRID();
730 : }
731 :
732 : // MySQL 8 requires AXIS[] node in PROJCS.GEOGCS
733 49 : if (GetMajorVersion() >= 8 && !IsMariaDB() && oSRS.IsProjected())
734 : {
735 4 : OGR_SRSNode oNode;
736 2 : const char *pszWKTTmp = pszWKT;
737 2 : oNode.importFromWkt(&pszWKTTmp);
738 :
739 4 : OGRSpatialReference oSRSGeog;
740 2 : oSRSGeog.CopyGeogCSFrom(&oSRS);
741 2 : char *pszWKTGeog = nullptr;
742 2 : oSRSGeog.exportToWkt(&pszWKTGeog);
743 :
744 2 : int iChild = oNode.FindChild("GEOGCS");
745 2 : if (iChild >= 0)
746 : {
747 2 : oNode.DestroyChild(iChild);
748 2 : auto poGeogNode = new OGR_SRSNode();
749 2 : pszWKTTmp = pszWKTGeog;
750 2 : poGeogNode->importFromWkt(&pszWKTTmp);
751 2 : oNode.InsertChild(poGeogNode, iChild);
752 : }
753 2 : CPLFree(pszWKTGeog);
754 :
755 2 : CPLFree(pszWKT);
756 2 : oNode.exportToWkt(&pszWKT);
757 : }
758 :
759 : /* -------------------------------------------------------------------- */
760 : /* Try to find in the existing record. */
761 : /* -------------------------------------------------------------------- */
762 49 : const char *pszTableName =
763 : "INFORMATION_SCHEMA.ST_SPATIAL_REFERENCE_SYSTEMS";
764 49 : if (GetMajorVersion() < 8 || IsMariaDB())
765 : {
766 47 : pszTableName = "spatial_ref_sys";
767 : osCommand.Printf("SELECT srid FROM spatial_ref_sys WHERE srtext = '%s'",
768 47 : OGRMySQLEscapeLiteral(pszWKT).c_str());
769 : }
770 : else
771 : {
772 : osCommand.Printf("SELECT SRS_ID FROM "
773 : "INFORMATION_SCHEMA.ST_SPATIAL_REFERENCE_SYSTEMS "
774 : "WHERE DEFINITION = '%s'",
775 2 : OGRMySQLEscapeLiteral(pszWKT).c_str());
776 : }
777 :
778 49 : MYSQL_RES *hResult = nullptr;
779 49 : if (!mysql_query(GetConn(), osCommand))
780 49 : hResult = mysql_store_result(GetConn());
781 :
782 49 : if (hResult != nullptr && !mysql_num_rows(hResult))
783 : {
784 18 : CPLDebug("MYSQL", "No rows exist currently exist in %s with WKT = %s",
785 : pszTableName, pszWKT);
786 18 : FreeResultAndNullify(hResult);
787 : }
788 49 : char **papszRow = nullptr;
789 49 : if (hResult != nullptr)
790 31 : papszRow = mysql_fetch_row(hResult);
791 :
792 49 : if (papszRow != nullptr && papszRow[0] != nullptr)
793 : {
794 31 : const int nSRSId = atoi(papszRow[0]);
795 31 : FreeResultAndNullify(hResult);
796 31 : CPLFree(pszWKT);
797 31 : return nSRSId;
798 : }
799 :
800 : // make sure to attempt to free results of successful queries
801 18 : hResult = mysql_store_result(GetConn());
802 18 : FreeResultAndNullify(hResult);
803 :
804 18 : if (GetMajorVersion() >= 8 && !IsMariaDB())
805 : {
806 1 : int nSRSId = -1;
807 1 : if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG") &&
808 : nAuthorityCode > 0)
809 : {
810 : /* ------------------------------------------------------------ */
811 : /* If it is an EPSG code, check if we can use the entry of */
812 : /* SRS_ID equal to the EPSG code. */
813 : /* ------------------------------------------------------------ */
814 : osCommand.Printf("SELECT SRS_ID FROM INFORMATION_SCHEMA."
815 : "ST_SPATIAL_REFERENCE_SYSTEMS "
816 : "WHERE SRS_ID = %d",
817 0 : nAuthorityCode);
818 0 : if (!mysql_query(GetConn(), osCommand))
819 : {
820 0 : hResult = mysql_store_result(GetConn());
821 0 : papszRow = mysql_fetch_row(hResult);
822 0 : if (!(papszRow != nullptr && papszRow[0] != nullptr))
823 : {
824 : // No row matching SRS_ID = nAuthorityCode ? Then
825 : // we can use it
826 0 : nSRSId = nAuthorityCode;
827 : }
828 0 : FreeResultAndNullify(hResult);
829 : }
830 : }
831 1 : if (nSRSId < 0)
832 : {
833 1 : nSRSId = 1;
834 :
835 : /* ------------------------------------------------------------- */
836 : /* Get the current maximum srid in the srs table. */
837 : /* ------------------------------------------------------------- */
838 : osCommand = "SELECT MAX(SRS_ID) FROM "
839 1 : "INFORMATION_SCHEMA.ST_SPATIAL_REFERENCE_SYSTEMS";
840 1 : if (!mysql_query(GetConn(), osCommand))
841 : {
842 1 : hResult = mysql_store_result(GetConn());
843 1 : papszRow = mysql_fetch_row(hResult);
844 1 : if (papszRow != nullptr && papszRow[0] != nullptr)
845 : {
846 1 : nSRSId = atoi(papszRow[0]) + 1;
847 : }
848 1 : FreeResultAndNullify(hResult);
849 : }
850 : }
851 : else
852 : {
853 0 : nSRSId = nAuthorityCode;
854 : }
855 :
856 : /* ----------------------------------------------------------------- */
857 : /* Check if there's an existing record with same name */
858 : /* ----------------------------------------------------------------- */
859 1 : CPLString osName(oSRS.GetName());
860 :
861 : osCommand.Printf(
862 : "SELECT SRS_ID FROM "
863 : "INFORMATION_SCHEMA.ST_SPATIAL_REFERENCE_SYSTEMS WHERE NAME = '%s'",
864 1 : osName.c_str());
865 1 : if (!mysql_query(GetConn(), osCommand))
866 : {
867 0 : hResult = mysql_store_result(GetConn());
868 0 : papszRow = mysql_fetch_row(hResult);
869 0 : if (papszRow != nullptr && papszRow[0] != nullptr)
870 : {
871 0 : osName += CPLSPrintf("_srid_%d", nSRSId);
872 : }
873 0 : FreeResultAndNullify(hResult);
874 : }
875 :
876 : /* ----------------------------------------------------------------- */
877 : /* Try adding the SRS to the SRS table. */
878 : /* ----------------------------------------------------------------- */
879 1 : if (pszAuthorityName != nullptr && nAuthorityCode > 0)
880 : {
881 0 : osCommand.Printf(
882 : "CREATE SPATIAL REFERENCE SYSTEM %d NAME '%s' "
883 : "ORGANIZATION '%s' "
884 : "IDENTIFIED BY %d "
885 : "DEFINITION '%s'",
886 0 : nSRSId, OGRMySQLEscapeLiteral(osName.c_str()).c_str(),
887 0 : OGRMySQLEscapeLiteral(pszAuthorityName).c_str(), nAuthorityCode,
888 0 : OGRMySQLEscapeLiteral(pszWKT).c_str());
889 : }
890 : else
891 : {
892 : osCommand.Printf("CREATE SPATIAL REFERENCE SYSTEM %d NAME '%s' "
893 : "DEFINITION '%s'",
894 : nSRSId,
895 2 : OGRMySQLEscapeLiteral(osName.c_str()).c_str(),
896 3 : OGRMySQLEscapeLiteral(pszWKT).c_str());
897 : }
898 :
899 1 : if (mysql_query(GetConn(), osCommand))
900 : {
901 0 : ReportError((osCommand + " failed").c_str());
902 0 : nSRSId = GetUnknownSRID();
903 : }
904 :
905 1 : hResult = mysql_store_result(GetConn());
906 1 : FreeResultAndNullify(hResult);
907 :
908 1 : CPLFree(pszWKT);
909 1 : return nSRSId;
910 : }
911 :
912 : /* -------------------------------------------------------------------- */
913 : /* Get the current maximum srid in the srs table. */
914 : /* -------------------------------------------------------------------- */
915 17 : osCommand = "SELECT MAX(srid) FROM spatial_ref_sys";
916 17 : if (!mysql_query(GetConn(), osCommand))
917 : {
918 17 : hResult = mysql_store_result(GetConn());
919 17 : papszRow = mysql_fetch_row(hResult);
920 : }
921 :
922 17 : int nSRSId = papszRow != nullptr && papszRow[0] != nullptr
923 34 : ? atoi(papszRow[0]) + 1
924 : : 1;
925 :
926 17 : FreeResultAndNullify(hResult);
927 :
928 : /* -------------------------------------------------------------------- */
929 : /* Try adding the SRS to the SRS table. */
930 : /* -------------------------------------------------------------------- */
931 17 : osCommand.Printf(
932 : "INSERT INTO spatial_ref_sys (srid,srtext) VALUES (%d,'%s')", nSRSId,
933 17 : pszWKT);
934 :
935 17 : if (mysql_query(GetConn(), osCommand))
936 : {
937 0 : ReportError((osCommand + " failed").c_str());
938 0 : nSRSId = GetUnknownSRID();
939 : }
940 :
941 : // make sure to attempt to free results of successful queries
942 17 : hResult = mysql_store_result(GetConn());
943 17 : FreeResultAndNullify(hResult);
944 :
945 17 : CPLFree(pszWKT);
946 :
947 17 : return nSRSId;
948 : }
949 :
950 : /************************************************************************/
951 : /* ExecuteSQL() */
952 : /************************************************************************/
953 :
954 186 : OGRLayer *OGRMySQLDataSource::ExecuteSQL(const char *pszSQLCommand,
955 : OGRGeometry *poSpatialFilter,
956 : const char *pszDialect)
957 :
958 : {
959 186 : if (poSpatialFilter != nullptr)
960 : {
961 2 : CPLDebug("OGR_MYSQL", "Spatial filter ignored for now in "
962 : "OGRMySQLDataSource::ExecuteSQL()");
963 : }
964 :
965 : /* -------------------------------------------------------------------- */
966 : /* Use generic implementation for recognized dialects */
967 : /* -------------------------------------------------------------------- */
968 186 : if (IsGenericSQLDialect(pszDialect))
969 0 : return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter,
970 0 : pszDialect);
971 :
972 : /* -------------------------------------------------------------------- */
973 : /* Special case DELLAYER: command. */
974 : /* -------------------------------------------------------------------- */
975 : #ifdef notdef
976 : if (STARTS_WITH_CI(pszSQLCommand, "DELLAYER:"))
977 : {
978 : const char *pszLayerName = pszSQLCommand + 9;
979 :
980 : while (*pszLayerName == ' ')
981 : pszLayerName++;
982 :
983 : DeleteLayer(pszLayerName);
984 : return NULL;
985 : }
986 : #endif
987 :
988 : /* -------------------------------------------------------------------- */
989 : /* Make sure there isn't an active transaction already. */
990 : /* -------------------------------------------------------------------- */
991 186 : InterruptLongResult();
992 :
993 : /* -------------------------------------------------------------------- */
994 : /* Execute the statement. */
995 : /* -------------------------------------------------------------------- */
996 : MYSQL_RES *hResultSet;
997 :
998 186 : if (mysql_query(hConn, pszSQLCommand))
999 : {
1000 2 : ReportError(pszSQLCommand);
1001 2 : return nullptr;
1002 : }
1003 :
1004 184 : hResultSet = mysql_use_result(hConn);
1005 184 : if (hResultSet == nullptr)
1006 : {
1007 100 : if (mysql_field_count(hConn) == 0)
1008 : {
1009 100 : CPLDebug("MYSQL", "Command '%s' succeeded, %d rows affected.",
1010 100 : pszSQLCommand, (int)mysql_affected_rows(hConn));
1011 100 : return nullptr;
1012 : }
1013 : else
1014 : {
1015 0 : ReportError(pszSQLCommand);
1016 0 : return nullptr;
1017 : }
1018 : }
1019 :
1020 : /* -------------------------------------------------------------------- */
1021 : /* Do we have a tuple result? If so, instantiate a results */
1022 : /* layer for it. */
1023 : /* -------------------------------------------------------------------- */
1024 :
1025 : OGRMySQLResultLayer *poLayer =
1026 84 : new OGRMySQLResultLayer(this, pszSQLCommand, hResultSet);
1027 :
1028 84 : return poLayer;
1029 : }
1030 :
1031 : /************************************************************************/
1032 : /* ReleaseResultSet() */
1033 : /************************************************************************/
1034 :
1035 84 : void OGRMySQLDataSource::ReleaseResultSet(OGRLayer *poLayer)
1036 :
1037 : {
1038 84 : delete poLayer;
1039 84 : }
1040 :
1041 : /************************************************************************/
1042 : /* LaunderName() */
1043 : /************************************************************************/
1044 :
1045 336 : char *OGRMySQLDataSource::LaunderName(const char *pszSrcName)
1046 :
1047 : {
1048 336 : char *pszSafeName = CPLStrdup(pszSrcName);
1049 :
1050 3760 : for (int i = 0; pszSafeName[i] != '\0'; i++)
1051 : {
1052 3424 : pszSafeName[i] =
1053 3424 : (char)CPLTolower(static_cast<unsigned char>(pszSafeName[i]));
1054 3424 : if (pszSafeName[i] == '-' || pszSafeName[i] == '#')
1055 0 : pszSafeName[i] = '_';
1056 : }
1057 :
1058 336 : return pszSafeName;
1059 : }
1060 :
1061 : /************************************************************************/
1062 : /* RequestLongResult() */
1063 : /* */
1064 : /* Layers need to use mysql_use_result() instead of */
1065 : /* mysql_store_result() so that we won't have to load entire */
1066 : /* result sets into RAM. But only one "streamed" resultset can */
1067 : /* be active on a database connection at a time. So we need to */
1068 : /* maintain a way of closing off an active streaming resultset */
1069 : /* before any other sort of query with a resultset is */
1070 : /* executable. This method (and InterruptLongResult()) */
1071 : /* implement that exclusion. */
1072 : /************************************************************************/
1073 :
1074 256 : void OGRMySQLDataSource::RequestLongResult(OGRMySQLLayer *poNewLayer)
1075 :
1076 : {
1077 256 : InterruptLongResult();
1078 256 : poLongResultLayer = poNewLayer;
1079 256 : }
1080 :
1081 : /************************************************************************/
1082 : /* InterruptLongResult() */
1083 : /************************************************************************/
1084 :
1085 1112 : void OGRMySQLDataSource::InterruptLongResult()
1086 :
1087 : {
1088 1112 : if (poLongResultLayer != nullptr)
1089 : {
1090 409 : poLongResultLayer->ResetReading();
1091 409 : poLongResultLayer = nullptr;
1092 : }
1093 1112 : }
1094 :
1095 : /************************************************************************/
1096 : /* DeleteLayer() */
1097 : /************************************************************************/
1098 :
1099 2 : OGRErr OGRMySQLDataSource::DeleteLayer(int iLayer)
1100 :
1101 : {
1102 2 : if (iLayer < 0 || iLayer >= nLayers)
1103 0 : return OGRERR_FAILURE;
1104 :
1105 : /* -------------------------------------------------------------------- */
1106 : /* Blow away our OGR structures related to the layer. This is */
1107 : /* pretty dangerous if anything has a reference to this layer! */
1108 : /* -------------------------------------------------------------------- */
1109 4 : CPLString osLayerName = papoLayers[iLayer]->GetLayerDefn()->GetName();
1110 :
1111 2 : CPLDebug("MYSQL", "DeleteLayer(%s)", osLayerName.c_str());
1112 :
1113 2 : delete papoLayers[iLayer];
1114 2 : memmove(papoLayers + iLayer, papoLayers + iLayer + 1,
1115 2 : sizeof(void *) * (nLayers - iLayer - 1));
1116 2 : nLayers--;
1117 :
1118 : /* -------------------------------------------------------------------- */
1119 : /* Remove from the database. */
1120 : /* -------------------------------------------------------------------- */
1121 4 : CPLString osCommand;
1122 :
1123 2 : osCommand.Printf("DROP TABLE `%s` ", osLayerName.c_str());
1124 :
1125 2 : if (!mysql_query(GetConn(), osCommand))
1126 : {
1127 2 : CPLDebug("MYSQL", "Dropped table %s.", osLayerName.c_str());
1128 2 : return OGRERR_NONE;
1129 : }
1130 : else
1131 : {
1132 0 : ReportError(osCommand);
1133 0 : return OGRERR_FAILURE;
1134 : }
1135 : }
1136 :
1137 : /************************************************************************/
1138 : /* ICreateLayer() */
1139 : /************************************************************************/
1140 :
1141 : OGRLayer *
1142 176 : OGRMySQLDataSource::ICreateLayer(const char *pszLayerNameIn,
1143 : const OGRGeomFieldDefn *poGeomFieldDefn,
1144 : CSLConstList papszOptions)
1145 :
1146 : {
1147 176 : MYSQL_RES *hResult = nullptr;
1148 352 : CPLString osCommand;
1149 : const char *pszGeomColumnName;
1150 : const char *pszExpectedFIDName;
1151 : char *pszLayerName;
1152 : // int nDimension = 3; // MySQL only supports 2d currently
1153 :
1154 : /* -------------------------------------------------------------------- */
1155 : /* Make sure there isn't an active transaction already. */
1156 : /* -------------------------------------------------------------------- */
1157 176 : InterruptLongResult();
1158 :
1159 176 : const auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
1160 : const auto poSRS =
1161 176 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
1162 :
1163 176 : if (CPLFetchBool(papszOptions, "LAUNDER", true))
1164 176 : pszLayerName = LaunderName(pszLayerNameIn);
1165 : else
1166 0 : pszLayerName = CPLStrdup(pszLayerNameIn);
1167 :
1168 : // if( wkbFlatten(eType) == eType )
1169 : // nDimension = 2;
1170 :
1171 176 : CPLDebug("MYSQL", "Creating layer %s.", pszLayerName);
1172 :
1173 : /* -------------------------------------------------------------------- */
1174 : /* Do we already have this layer? If so, should we blow it */
1175 : /* away? */
1176 : /* -------------------------------------------------------------------- */
1177 :
1178 3996 : for (int iLayer = 0; iLayer < nLayers; iLayer++)
1179 : {
1180 3820 : if (EQUAL(pszLayerName, papoLayers[iLayer]->GetLayerDefn()->GetName()))
1181 : {
1182 :
1183 4 : if (CSLFetchNameValue(papszOptions, "OVERWRITE") != nullptr &&
1184 2 : !EQUAL(CSLFetchNameValue(papszOptions, "OVERWRITE"), "NO"))
1185 : {
1186 2 : DeleteLayer(iLayer);
1187 : }
1188 : else
1189 : {
1190 0 : CPLError(CE_Failure, CPLE_AppDefined,
1191 : "Layer %s already exists, CreateLayer failed.\n"
1192 : "Use the layer creation option OVERWRITE=YES to "
1193 : "replace it.",
1194 : pszLayerName);
1195 0 : CPLFree(pszLayerName);
1196 0 : return nullptr;
1197 : }
1198 : }
1199 : }
1200 :
1201 176 : pszGeomColumnName = CSLFetchNameValue(papszOptions, "GEOMETRY_NAME");
1202 176 : if (!pszGeomColumnName)
1203 176 : pszGeomColumnName = "SHAPE";
1204 :
1205 176 : pszExpectedFIDName = CSLFetchNameValue(papszOptions, "FID");
1206 176 : if (!pszExpectedFIDName)
1207 176 : pszExpectedFIDName = CSLFetchNameValue(papszOptions, "MYSQL_FID");
1208 176 : if (!pszExpectedFIDName)
1209 176 : pszExpectedFIDName = "OGR_FID";
1210 :
1211 176 : const bool bFID64 = CPLFetchBool(papszOptions, "FID64", false);
1212 176 : const char *pszFIDType = bFID64 ? "BIGINT" : "INT";
1213 :
1214 176 : CPLDebug("MYSQL", "Geometry Column Name %s.", pszGeomColumnName);
1215 176 : CPLDebug("MYSQL", "FID Column Name %s.", pszExpectedFIDName);
1216 :
1217 176 : const char *pszSI = CSLFetchNameValue(papszOptions, "SPATIAL_INDEX");
1218 : const bool bHasSI =
1219 176 : (eType != wkbNone && (pszSI == nullptr || CPLTestBool(pszSI)));
1220 :
1221 : // Calling this does no harm
1222 176 : InitializeMetadataTables();
1223 :
1224 : /* -------------------------------------------------------------------- */
1225 : /* Try to get the SRS Id of this spatial reference system, */
1226 : /* adding to the srs table if needed. */
1227 : /* -------------------------------------------------------------------- */
1228 :
1229 176 : int nSRSId = GetUnknownSRID();
1230 176 : if (poSRS != nullptr)
1231 162 : nSRSId = FetchSRSId(poSRS);
1232 :
1233 176 : if (wkbFlatten(eType) == wkbNone)
1234 : {
1235 : osCommand.Printf("CREATE TABLE `%s` ( "
1236 : " %s %s UNIQUE NOT NULL AUTO_INCREMENT )",
1237 4 : pszLayerName, pszExpectedFIDName, pszFIDType);
1238 : }
1239 : else
1240 : {
1241 : // when using mysql8 and SRS is specified, use SRID option for geometry.
1242 172 : if (GetMajorVersion() < 8 || IsMariaDB() || nSRSId == GetUnknownSRID())
1243 : osCommand.Printf("CREATE TABLE `%s` ( "
1244 : " %s %s UNIQUE NOT NULL AUTO_INCREMENT, "
1245 : " %s GEOMETRY %s)",
1246 : pszLayerName, pszExpectedFIDName, pszFIDType,
1247 57 : pszGeomColumnName, bHasSI ? "NOT NULL" : "");
1248 : else
1249 : osCommand.Printf("CREATE TABLE `%s` ( "
1250 : " %s %s UNIQUE NOT NULL AUTO_INCREMENT, "
1251 : " %s GEOMETRY %s /*!80003 SRID %d */)",
1252 : pszLayerName, pszExpectedFIDName, pszFIDType,
1253 : pszGeomColumnName, bHasSI ? "NOT NULL" : "",
1254 115 : nSRSId);
1255 : }
1256 :
1257 176 : if (CSLFetchNameValue(papszOptions, "ENGINE") != nullptr)
1258 : {
1259 0 : osCommand += " ENGINE = ";
1260 0 : osCommand += CSLFetchNameValue(papszOptions, "ENGINE");
1261 : }
1262 :
1263 176 : if (!mysql_query(GetConn(), osCommand))
1264 : {
1265 176 : if (mysql_field_count(GetConn()) == 0)
1266 176 : CPLDebug("MYSQL", "Created table %s.", pszLayerName);
1267 : else
1268 : {
1269 0 : ReportError(osCommand);
1270 0 : return nullptr;
1271 : }
1272 : }
1273 : else
1274 : {
1275 0 : ReportError(osCommand);
1276 0 : return nullptr;
1277 : }
1278 :
1279 : // make sure to attempt to free results of successful queries
1280 176 : hResult = mysql_store_result(GetConn());
1281 176 : FreeResultAndNullify(hResult);
1282 :
1283 176 : if (UpdateMetadataTables(pszLayerName, eType, pszGeomColumnName, nSRSId) !=
1284 : OGRERR_NONE)
1285 0 : return nullptr;
1286 :
1287 : /* -------------------------------------------------------------------- */
1288 : /* Create the spatial index. */
1289 : /* */
1290 : /* We're doing this before we add geometry and record to the table */
1291 : /* so this may not be exactly the best way to do it. */
1292 : /* -------------------------------------------------------------------- */
1293 176 : if (bHasSI)
1294 : {
1295 : osCommand.Printf("ALTER TABLE `%s` ADD SPATIAL INDEX(`%s`) ",
1296 170 : pszLayerName, pszGeomColumnName);
1297 :
1298 170 : if (mysql_query(GetConn(), osCommand))
1299 : {
1300 0 : ReportError(osCommand);
1301 0 : return nullptr;
1302 : }
1303 :
1304 : // make sure to attempt to free results of successful queries
1305 170 : hResult = mysql_store_result(GetConn());
1306 170 : FreeResultAndNullify(hResult);
1307 : }
1308 :
1309 : /* -------------------------------------------------------------------- */
1310 : /* Create the layer object. */
1311 : /* -------------------------------------------------------------------- */
1312 : OGRMySQLTableLayer *poLayer;
1313 : OGRErr eErr;
1314 :
1315 176 : poLayer = new OGRMySQLTableLayer(this, pszLayerName, TRUE, nSRSId);
1316 176 : eErr = poLayer->Initialize(pszLayerName);
1317 176 : if (eErr == OGRERR_FAILURE)
1318 0 : return nullptr;
1319 176 : if (eType != wkbNone)
1320 172 : poLayer->GetLayerDefn()->GetGeomFieldDefn(0)->SetNullable(FALSE);
1321 :
1322 176 : poLayer->SetLaunderFlag(CPLFetchBool(papszOptions, "LAUNDER", true));
1323 176 : poLayer->SetPrecisionFlag(CPLFetchBool(papszOptions, "PRECISION", true));
1324 :
1325 : /* -------------------------------------------------------------------- */
1326 : /* Add layer to data source layer list. */
1327 : /* -------------------------------------------------------------------- */
1328 352 : papoLayers = (OGRMySQLLayer **)CPLRealloc(
1329 176 : papoLayers, sizeof(OGRMySQLLayer *) * (nLayers + 1));
1330 :
1331 176 : papoLayers[nLayers++] = poLayer;
1332 :
1333 176 : CPLFree(pszLayerName);
1334 :
1335 176 : return poLayer;
1336 : }
1337 :
1338 : /************************************************************************/
1339 : /* OGRMySQLEscapeLiteral() */
1340 : /************************************************************************/
1341 :
1342 209 : std::string OGRMySQLEscapeLiteral(const char *pszLiteral)
1343 : {
1344 209 : std::string osVal;
1345 21258 : for (int i = 0; pszLiteral[i] != '\0'; i++)
1346 : {
1347 21049 : if (pszLiteral[i] == '\'')
1348 0 : osVal += '\'';
1349 21049 : osVal += pszLiteral[i];
1350 : }
1351 209 : return osVal;
1352 : }
|