Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements OGRMySQLTableLayer 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 "cpl_conv.h"
16 : #include "cpl_string.h"
17 : #include "ogr_mysql.h"
18 :
19 : /************************************************************************/
20 : /* OGRMySQLTableLayer() */
21 : /************************************************************************/
22 :
23 188 : OGRMySQLTableLayer::OGRMySQLTableLayer(OGRMySQLDataSource *poDSIn,
24 : CPL_UNUSED const char *pszTableName,
25 188 : int bUpdate, int nSRSIdIn)
26 : : OGRMySQLLayer(poDSIn), bUpdateAccess(bUpdate), pszQuery(nullptr),
27 376 : pszWHERE(CPLStrdup("")), bLaunderColumnNames(TRUE),
28 188 : bPreservePrecision(FALSE)
29 : {
30 188 : pszQueryStatement = nullptr;
31 :
32 188 : iNextShapeId = 0;
33 :
34 188 : nSRSId = nSRSIdIn;
35 :
36 188 : poFeatureDefn = nullptr;
37 :
38 188 : SetDescription(pszTableName);
39 188 : }
40 :
41 : /************************************************************************/
42 : /* ~OGRMySQLTableLayer() */
43 : /************************************************************************/
44 :
45 376 : OGRMySQLTableLayer::~OGRMySQLTableLayer()
46 :
47 : {
48 188 : CPLFree(pszQuery);
49 188 : CPLFree(pszWHERE);
50 376 : }
51 :
52 : /************************************************************************/
53 : /* Initialize() */
54 : /* */
55 : /* Make sure we only do a ResetReading once we really have a */
56 : /* FieldDefn. Otherwise, we'll segfault. After you construct */
57 : /* the MySQLTableLayer, make sure to do pLayer->Initialize() */
58 : /************************************************************************/
59 :
60 188 : OGRErr OGRMySQLTableLayer::Initialize(const char *pszTableName)
61 : {
62 188 : poFeatureDefn = ReadTableDefinition(pszTableName);
63 188 : if (poFeatureDefn)
64 : {
65 188 : ResetReading();
66 188 : return OGRERR_NONE;
67 : }
68 : else
69 : {
70 0 : return OGRERR_FAILURE;
71 : }
72 : }
73 :
74 : /************************************************************************/
75 : /* ReadTableDefinition() */
76 : /* */
77 : /* Build a schema from the named table. Done by querying the */
78 : /* catalog. */
79 : /************************************************************************/
80 :
81 188 : OGRFeatureDefn *OGRMySQLTableLayer::ReadTableDefinition(const char *pszTable)
82 :
83 : {
84 : MYSQL_RES *hResult;
85 376 : CPLString osCommand;
86 :
87 : /* -------------------------------------------------------------------- */
88 : /* Fire off commands to get back the schema of the table. */
89 : /* -------------------------------------------------------------------- */
90 188 : osCommand.Printf("DESCRIBE `%s`", pszTable);
91 188 : pszGeomColumnTable = CPLStrdup(pszTable);
92 188 : if (mysql_query(poDS->GetConn(), osCommand))
93 : {
94 0 : poDS->ReportError("DESCRIBE Failed");
95 0 : return nullptr;
96 : }
97 :
98 188 : hResult = mysql_store_result(poDS->GetConn());
99 188 : if (hResult == nullptr)
100 : {
101 0 : poDS->ReportError("mysql_store_result() failed on DESCRIBE result.");
102 0 : return nullptr;
103 : }
104 :
105 : /* -------------------------------------------------------------------- */
106 : /* Parse the returned table information. */
107 : /* -------------------------------------------------------------------- */
108 188 : OGRFeatureDefn *poDefn = new OGRFeatureDefn(pszTable);
109 : char **papszRow;
110 188 : OGRwkbGeometryType eForcedGeomType = wkbUnknown;
111 188 : int bGeomColumnNotNullable = FALSE;
112 :
113 188 : poDefn->Reference();
114 :
115 588 : while ((papszRow = mysql_fetch_row(hResult)) != nullptr)
116 : {
117 400 : OGRFieldDefn oField(papszRow[0], OFTString);
118 :
119 400 : const char *pszType = papszRow[1];
120 400 : if (pszType == nullptr)
121 0 : continue;
122 :
123 400 : int nLenType = (int)strlen(pszType);
124 :
125 400 : if (EQUAL(pszType, "varbinary") ||
126 400 : (nLenType >= 4 && EQUAL(pszType + nLenType - 4, "blob")))
127 : {
128 0 : oField.SetType(OFTBinary);
129 : }
130 400 : else if (EQUAL(pszType, "varchar") ||
131 400 : (nLenType >= 4 && EQUAL(pszType + nLenType - 4, "enum")) ||
132 400 : (nLenType >= 3 && EQUAL(pszType + nLenType - 3, "set")))
133 : {
134 0 : oField.SetType(OFTString);
135 : }
136 400 : else if (STARTS_WITH_CI(pszType, "char"))
137 : {
138 0 : oField.SetType(OFTString);
139 : char **papszTokens;
140 :
141 0 : papszTokens = CSLTokenizeString2(pszType, "(),", 0);
142 0 : if (CSLCount(papszTokens) >= 2)
143 : {
144 : /* width is the second */
145 0 : oField.SetWidth(atoi(papszTokens[1]));
146 : }
147 :
148 0 : CSLDestroy(papszTokens);
149 0 : oField.SetType(OFTString);
150 : }
151 :
152 400 : if (nLenType >= 4 && EQUAL(pszType + nLenType - 4, "text"))
153 : {
154 8 : oField.SetType(OFTString);
155 : }
156 392 : else if (STARTS_WITH_CI(pszType, "varchar"))
157 : {
158 : /*
159 : pszType is usually in the form "varchar(15)"
160 : so we'll split it up and get the width and precision
161 : */
162 :
163 6 : oField.SetType(OFTString);
164 : char **papszTokens;
165 :
166 6 : papszTokens = CSLTokenizeString2(pszType, "(),", 0);
167 6 : if (CSLCount(papszTokens) >= 2)
168 : {
169 : /* width is the second */
170 6 : oField.SetWidth(atoi(papszTokens[1]));
171 : }
172 :
173 6 : CSLDestroy(papszTokens);
174 6 : oField.SetType(OFTString);
175 : }
176 386 : else if (STARTS_WITH_CI(pszType, "int"))
177 : {
178 190 : oField.SetType(OFTInteger);
179 : }
180 196 : else if (STARTS_WITH_CI(pszType, "tinyint"))
181 : {
182 0 : oField.SetType(OFTInteger);
183 : }
184 196 : else if (STARTS_WITH_CI(pszType, "smallint"))
185 : {
186 0 : oField.SetType(OFTInteger);
187 : }
188 196 : else if (STARTS_WITH_CI(pszType, "mediumint"))
189 : {
190 0 : oField.SetType(OFTInteger);
191 : }
192 196 : else if (STARTS_WITH_CI(pszType, "bigint"))
193 : {
194 6 : oField.SetType(OFTInteger64);
195 : }
196 190 : else if (STARTS_WITH_CI(pszType, "decimal"))
197 : {
198 : /*
199 : pszType is usually in the form "decimal(15,2)"
200 : so we'll split it up and get the width and precision
201 : */
202 0 : oField.SetType(OFTReal);
203 : char **papszTokens;
204 :
205 0 : papszTokens = CSLTokenizeString2(pszType, "(),", 0);
206 0 : if (CSLCount(papszTokens) >= 3)
207 : {
208 : /* width is the second and precision is the third */
209 0 : oField.SetWidth(atoi(papszTokens[1]));
210 0 : oField.SetPrecision(atoi(papszTokens[2]));
211 : }
212 0 : CSLDestroy(papszTokens);
213 : }
214 190 : else if (STARTS_WITH_CI(pszType, "float"))
215 : {
216 0 : oField.SetType(OFTReal);
217 : }
218 190 : else if (EQUAL(pszType, "double"))
219 : {
220 4 : oField.SetType(OFTReal);
221 : }
222 186 : else if (STARTS_WITH_CI(pszType, "double"))
223 : {
224 : // double can also be double(15,2)
225 : // so we'll handle this case here after
226 : // we check for just a regular double
227 : // without a width and precision specified
228 :
229 0 : char **papszTokens = CSLTokenizeString2(pszType, "(),", 0);
230 0 : if (CSLCount(papszTokens) >= 3)
231 : {
232 : /* width is the second and precision is the third */
233 0 : oField.SetWidth(atoi(papszTokens[1]));
234 0 : oField.SetPrecision(atoi(papszTokens[2]));
235 : }
236 0 : CSLDestroy(papszTokens);
237 :
238 0 : oField.SetType(OFTReal);
239 : }
240 186 : else if (EQUAL(pszType, "decimal"))
241 : {
242 0 : oField.SetType(OFTReal);
243 : }
244 186 : else if (EQUAL(pszType, "date"))
245 : {
246 0 : oField.SetType(OFTDate);
247 : }
248 186 : else if (EQUAL(pszType, "time"))
249 : {
250 0 : oField.SetType(OFTTime);
251 : }
252 186 : else if (EQUAL(pszType, "datetime") || EQUAL(pszType, "timestamp"))
253 : {
254 4 : oField.SetType(OFTDateTime);
255 : }
256 182 : else if (EQUAL(pszType, "year"))
257 : {
258 0 : oField.SetType(OFTString);
259 0 : oField.SetWidth(10);
260 : }
261 182 : else if (EQUAL(pszType, "geometry") ||
262 0 : OGRFromOGCGeomType(pszType) != wkbUnknown)
263 : {
264 182 : if (pszGeomColumn == nullptr)
265 : {
266 182 : pszGeomColumn = CPLStrdup(papszRow[0]);
267 182 : eForcedGeomType = OGRFromOGCGeomType(pszType);
268 182 : bGeomColumnNotNullable =
269 182 : (papszRow[2] != nullptr && EQUAL(papszRow[2], "NO"));
270 : }
271 : else
272 : {
273 0 : CPLDebug("MYSQL",
274 : "Ignoring %s as geometry column. Another one(%s) has "
275 : "already been found before",
276 : papszRow[0], pszGeomColumn);
277 : }
278 182 : continue;
279 : }
280 : // Is this an integer primary key field?
281 410 : if (!bHasFid && papszRow[3] != nullptr && EQUAL(papszRow[3], "PRI") &&
282 192 : (oField.GetType() == OFTInteger ||
283 4 : oField.GetType() == OFTInteger64))
284 : {
285 188 : bHasFid = TRUE;
286 188 : pszFIDColumn = CPLStrdup(oField.GetNameRef());
287 188 : if (oField.GetType() == OFTInteger64)
288 4 : SetMetadataItem(OLMD_FID64, "YES");
289 188 : continue;
290 : }
291 :
292 : // Is not nullable ?
293 30 : if (papszRow[2] != nullptr && EQUAL(papszRow[2], "NO"))
294 3 : oField.SetNullable(FALSE);
295 :
296 : // Has default ?
297 30 : const char *pszDefault = papszRow[4];
298 30 : if (pszDefault != nullptr)
299 : {
300 36 : if (!EQUAL(pszDefault, "NULL") &&
301 12 : !STARTS_WITH_CI(pszDefault, "CURRENT_") &&
302 34 : pszDefault[0] != '(' && pszDefault[0] != '\'' &&
303 10 : CPLGetValueType(pszDefault) == CPL_VALUE_STRING)
304 : {
305 6 : int nYear = 0;
306 6 : int nMonth = 0;
307 6 : int nDay = 0;
308 6 : int nHour = 0;
309 6 : int nMinute = 0;
310 6 : float fSecond = 0.0f;
311 8 : if (oField.GetType() == OFTDateTime &&
312 2 : sscanf(pszDefault, "%d-%d-%d %d:%d:%f", &nYear, &nMonth,
313 : &nDay, &nHour, &nMinute, &fSecond) == 6)
314 : {
315 2 : oField.SetDefault(CPLSPrintf(
316 : "'%04d/%02d/%02d %02d:%02d:%02d'", nYear, nMonth, nDay,
317 2 : nHour, nMinute, (int)(fSecond + 0.5)));
318 : }
319 : else
320 : {
321 8 : CPLString osDefault("'");
322 4 : char *pszTmp = CPLEscapeString(pszDefault, -1, CPLES_SQL);
323 4 : osDefault += pszTmp;
324 4 : CPLFree(pszTmp);
325 4 : osDefault += "'";
326 4 : oField.SetDefault(osDefault);
327 : }
328 : }
329 : else
330 : {
331 6 : if (EQUAL(pszDefault, "CURRENT_TIMESTAMP()"))
332 1 : oField.SetDefault("CURRENT_TIMESTAMP");
333 : else
334 5 : oField.SetDefault(pszDefault);
335 : }
336 : }
337 :
338 30 : poDefn->AddFieldDefn(&oField);
339 : }
340 :
341 : // set to none for now... if we have a geometry column it will be set layer.
342 188 : poDefn->SetGeomType(wkbNone);
343 :
344 188 : mysql_free_result(hResult);
345 188 : hResult = nullptr;
346 :
347 188 : if (bHasFid)
348 188 : CPLDebug("MySQL", "table %s has FID column %s.", pszTable,
349 : pszFIDColumn);
350 : else
351 0 : CPLDebug("MySQL",
352 : "table %s has no FID column, FIDs will not be reliable!",
353 : pszTable);
354 :
355 188 : if (pszGeomColumn)
356 : {
357 182 : char *pszType = nullptr;
358 :
359 : auto poGeomFieldDefn =
360 182 : std::make_unique<OGRMySQLGeomFieldDefn>(poDS, pszGeomColumn);
361 :
362 182 : if (poDS->GetMajorVersion() < 8 || poDS->IsMariaDB())
363 : osCommand.Printf("SELECT type, coord_dimension FROM "
364 : "geometry_columns WHERE f_table_name='%s'",
365 57 : pszTable);
366 :
367 : else
368 : osCommand.Printf("SELECT GEOMETRY_TYPE_NAME FROM "
369 : "INFORMATION_SCHEMA.ST_GEOMETRY_COLUMNS "
370 : "WHERE TABLE_NAME = '%s'",
371 125 : pszTable);
372 :
373 182 : if (!mysql_query(poDS->GetConn(), osCommand))
374 182 : hResult = mysql_store_result(poDS->GetConn());
375 :
376 182 : papszRow = nullptr;
377 182 : if (hResult != nullptr)
378 182 : papszRow = mysql_fetch_row(hResult);
379 :
380 182 : if (papszRow != nullptr && papszRow[0] != nullptr)
381 : {
382 :
383 182 : pszType = papszRow[0];
384 :
385 182 : OGRwkbGeometryType l_nGeomType = OGRFromOGCGeomType(pszType);
386 :
387 182 : if (poDS->GetMajorVersion() < 8 || poDS->IsMariaDB())
388 57 : if (papszRow[1] != nullptr && atoi(papszRow[1]) == 3)
389 1 : l_nGeomType = wkbSetZ(l_nGeomType);
390 :
391 182 : poGeomFieldDefn->SetType(l_nGeomType);
392 : }
393 0 : else if (eForcedGeomType != wkbUnknown)
394 0 : poGeomFieldDefn->SetType(eForcedGeomType);
395 :
396 182 : if (bGeomColumnNotNullable)
397 180 : poGeomFieldDefn->SetNullable(FALSE);
398 :
399 : // Fetch the SRID for this table now
400 182 : nSRSId = FetchSRSId();
401 :
402 182 : poGeomFieldDefn->nSRSId = nSRSId;
403 182 : poDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
404 :
405 182 : if (hResult != nullptr)
406 182 : mysql_free_result(
407 : hResult); // Free our query results for finding type.
408 182 : hResult = nullptr;
409 : }
410 :
411 188 : return poDefn;
412 : }
413 :
414 : /************************************************************************/
415 : /* SetSpatialFilter() */
416 : /************************************************************************/
417 :
418 36 : void OGRMySQLTableLayer::SetSpatialFilter(OGRGeometry *poGeomIn)
419 :
420 : {
421 36 : if (!InstallFilter(poGeomIn))
422 14 : return;
423 :
424 22 : BuildWhere();
425 :
426 22 : ResetReading();
427 : }
428 :
429 : /************************************************************************/
430 : /* BuildWhere() */
431 : /* */
432 : /* Build the WHERE statement appropriate to the current set of */
433 : /* criteria (spatial and attribute queries). */
434 : /************************************************************************/
435 :
436 98 : void OGRMySQLTableLayer::BuildWhere()
437 :
438 : {
439 98 : CPLFree(pszWHERE);
440 98 : const size_t nWHERELen = 500 + ((pszQuery) ? strlen(pszQuery) : 0);
441 98 : pszWHERE = (char *)CPLMalloc(nWHERELen);
442 98 : pszWHERE[0] = '\0';
443 :
444 98 : if (m_poFilterGeom != nullptr && pszGeomColumn)
445 : {
446 : char szEnvelope[400];
447 28 : OGREnvelope sEnvelope;
448 28 : szEnvelope[0] = '\0';
449 :
450 : // POLYGON((MINX MINY, MAXX MINY, MAXX MAXY, MINX MAXY, MINX MINY))
451 28 : m_poFilterGeom->getEnvelope(&sEnvelope);
452 :
453 28 : const OGRSpatialReference *l_poSRS = GetSpatialRef();
454 : const bool bIsGeography =
455 42 : (poDS->GetMajorVersion() >= 8 && !poDS->IsMariaDB() && l_poSRS &&
456 14 : l_poSRS->IsGeographic());
457 :
458 28 : const double dfMinX = sEnvelope.MinX;
459 28 : const double dfMinY = sEnvelope.MinY;
460 28 : const double dfMaxX = sEnvelope.MaxX;
461 28 : const double dfMaxY = sEnvelope.MaxY;
462 :
463 28 : CPLsnprintf(szEnvelope, sizeof(szEnvelope),
464 : "POLYGON((%.17g %.17g, %.17g %.17g, %.17g %.17g, %.17g "
465 : "%.17g, %.17g %.17g))",
466 : dfMinX, dfMinY, dfMaxX, dfMinY, dfMaxX, dfMaxY, dfMinX,
467 : dfMaxY, dfMinX, dfMinY);
468 :
469 28 : if (bIsGeography)
470 : {
471 : // Force a "random" projected CRS so that the spatial filter works as
472 : // we expect.
473 : // This is due to the following returning false
474 : // select MBRIntersects(ST_GeomFromText('POLYGON((-179 -89, 179 -89, 179 89, -179 89, -179 -89))', 4326, 'axis-order=long-lat'), ST_GeomFromText('POINT(0 0)', 4326));
475 2 : snprintf(pszWHERE, nWHERELen,
476 : "WHERE MBRIntersects(ST_GeomFromText('%s', 32631), "
477 : "ST_SRID(`%s`,32631))",
478 : szEnvelope, pszGeomColumn);
479 : }
480 : else
481 : {
482 26 : snprintf(pszWHERE, nWHERELen,
483 : "WHERE MBRIntersects(%s('%s', %d), `%s`)",
484 26 : poDS->GetMajorVersion() >= 8 ? "ST_GeomFromText"
485 : : "GeomFromText",
486 : szEnvelope, nSRSId, pszGeomColumn);
487 : }
488 : }
489 :
490 98 : if (pszQuery != nullptr)
491 : {
492 42 : if (strlen(pszWHERE) == 0)
493 36 : snprintf(pszWHERE, nWHERELen, "WHERE %s ", pszQuery);
494 : else
495 6 : snprintf(pszWHERE + strlen(pszWHERE), nWHERELen - strlen(pszWHERE),
496 : "&& (%s) ", pszQuery);
497 : }
498 :
499 98 : if (pszWHERE[0])
500 64 : CPLDebug("MYSQL", "Filter: %s", pszWHERE);
501 98 : }
502 :
503 : /************************************************************************/
504 : /* BuildFullQueryStatement() */
505 : /************************************************************************/
506 :
507 857 : void OGRMySQLTableLayer::BuildFullQueryStatement()
508 :
509 : {
510 857 : if (pszQueryStatement != nullptr)
511 : {
512 669 : CPLFree(pszQueryStatement);
513 669 : pszQueryStatement = nullptr;
514 : }
515 :
516 857 : char *pszFields = BuildFields();
517 :
518 857 : pszQueryStatement =
519 2571 : (char *)CPLMalloc(strlen(pszFields) + strlen(pszWHERE) +
520 857 : strlen(poFeatureDefn->GetName()) + 40);
521 2571 : snprintf(pszQueryStatement,
522 1714 : strlen(pszFields) + strlen(pszWHERE) +
523 857 : strlen(poFeatureDefn->GetName()) + 40,
524 857 : "SELECT %s FROM `%s` %s", pszFields, poFeatureDefn->GetName(),
525 : pszWHERE);
526 :
527 857 : CPLFree(pszFields);
528 857 : }
529 :
530 : /************************************************************************/
531 : /* ResetReading() */
532 : /************************************************************************/
533 :
534 857 : void OGRMySQLTableLayer::ResetReading()
535 :
536 : {
537 857 : BuildFullQueryStatement();
538 :
539 857 : OGRMySQLLayer::ResetReading();
540 857 : }
541 :
542 : /************************************************************************/
543 : /* BuildFields() */
544 : /* */
545 : /* Build list of fields to fetch, performing any required */
546 : /* transformations (such as on geometry). */
547 : /************************************************************************/
548 :
549 901 : char *OGRMySQLTableLayer::BuildFields()
550 :
551 : {
552 901 : size_t nSize = 25;
553 901 : if (pszGeomColumn)
554 887 : nSize += strlen(pszGeomColumn);
555 :
556 901 : if (bHasFid)
557 901 : nSize += strlen(pszFIDColumn);
558 :
559 3036 : for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
560 2135 : nSize += strlen(poFeatureDefn->GetFieldDefn(i)->GetNameRef()) + 6;
561 :
562 901 : char *pszFieldList = (char *)CPLMalloc(nSize);
563 901 : pszFieldList[0] = '\0';
564 :
565 901 : if (bHasFid && poFeatureDefn->GetFieldIndex(pszFIDColumn) == -1)
566 901 : snprintf(pszFieldList, nSize, "`%s`", pszFIDColumn);
567 :
568 901 : if (pszGeomColumn)
569 : {
570 887 : if (strlen(pszFieldList) > 0)
571 887 : strcat(pszFieldList, ", ");
572 :
573 : /* ------------------------------------------------------------ */
574 : /* Geometry returned from MySQL is as WKB, with the */
575 : /* first 4 bytes being an int that defines the SRID */
576 : /* and the rest being the WKB. */
577 : /* ------------------------------------------------------------ */
578 887 : snprintf(pszFieldList + strlen(pszFieldList),
579 887 : nSize - strlen(pszFieldList), "`%s` `%s`", pszGeomColumn,
580 : pszGeomColumn);
581 : }
582 :
583 3036 : for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
584 : {
585 2135 : const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
586 :
587 2135 : if (strlen(pszFieldList) > 0)
588 2135 : strcat(pszFieldList, ", ");
589 :
590 2135 : strcat(pszFieldList, "`");
591 2135 : strcat(pszFieldList, pszName);
592 2135 : strcat(pszFieldList, "`");
593 : }
594 :
595 901 : CPLAssert(strlen(pszFieldList) < nSize);
596 :
597 901 : return pszFieldList;
598 : }
599 :
600 : /************************************************************************/
601 : /* SetAttributeFilter() */
602 : /************************************************************************/
603 :
604 76 : OGRErr OGRMySQLTableLayer::SetAttributeFilter(const char *pszQueryIn)
605 :
606 : {
607 76 : CPLFree(m_pszAttrQueryString);
608 76 : m_pszAttrQueryString = pszQueryIn ? CPLStrdup(pszQueryIn) : nullptr;
609 :
610 76 : CPLFree(pszQuery);
611 :
612 76 : if (pszQueryIn == nullptr || strlen(pszQueryIn) == 0)
613 34 : pszQuery = nullptr;
614 : else
615 42 : pszQuery = CPLStrdup(pszQueryIn);
616 :
617 76 : BuildWhere();
618 :
619 76 : ResetReading();
620 :
621 76 : return OGRERR_NONE;
622 : }
623 :
624 : /************************************************************************/
625 : /* TestCapability() */
626 : /************************************************************************/
627 :
628 404 : int OGRMySQLTableLayer::TestCapability(const char *pszCap)
629 :
630 : {
631 404 : if (EQUAL(pszCap, OLCRandomRead))
632 4 : return bHasFid;
633 :
634 400 : else if (EQUAL(pszCap, OLCFastFeatureCount))
635 0 : return TRUE;
636 :
637 400 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
638 0 : return TRUE;
639 :
640 400 : else if (EQUAL(pszCap, OLCFastGetExtent))
641 4 : return TRUE;
642 :
643 396 : else if (EQUAL(pszCap, OLCCreateField))
644 4 : return bUpdateAccess;
645 :
646 392 : else if (EQUAL(pszCap, OLCDeleteFeature))
647 2 : return bUpdateAccess;
648 :
649 390 : else if (EQUAL(pszCap, OLCRandomWrite))
650 6 : return bUpdateAccess;
651 :
652 384 : else if (EQUAL(pszCap, OLCSequentialWrite))
653 2 : return bUpdateAccess;
654 :
655 382 : else if (EQUAL(pszCap, OLCMeasuredGeometries))
656 180 : return TRUE;
657 :
658 202 : else if (EQUAL(pszCap, OLCZGeometries))
659 6 : return TRUE;
660 :
661 : else
662 196 : return FALSE;
663 : }
664 :
665 : /************************************************************************/
666 : /* ISetFeature() */
667 : /* */
668 : /* SetFeature() is implemented by dropping the old copy of the */
669 : /* feature in question (if there is one) and then creating a */
670 : /* new one with the provided feature id. */
671 : /************************************************************************/
672 :
673 20 : OGRErr OGRMySQLTableLayer::ISetFeature(OGRFeature *poFeature)
674 :
675 : {
676 : OGRErr eErr;
677 :
678 20 : if (poFeature->GetFID() == OGRNullFID)
679 : {
680 0 : CPLError(CE_Failure, CPLE_AppDefined,
681 : "FID required on features given to SetFeature().");
682 0 : return OGRERR_FAILURE;
683 : }
684 :
685 20 : eErr = DeleteFeature(poFeature->GetFID());
686 20 : if (eErr != OGRERR_NONE)
687 4 : return eErr;
688 :
689 16 : return CreateFeature(poFeature);
690 : }
691 :
692 : /************************************************************************/
693 : /* DeleteFeature() */
694 : /************************************************************************/
695 :
696 30 : OGRErr OGRMySQLTableLayer::DeleteFeature(GIntBig nFID)
697 :
698 : {
699 30 : MYSQL_RES *hResult = nullptr;
700 60 : CPLString osCommand;
701 :
702 : /* -------------------------------------------------------------------- */
703 : /* We can only delete features if we have a well defined FID */
704 : /* column to target. */
705 : /* -------------------------------------------------------------------- */
706 30 : if (!bHasFid)
707 : {
708 0 : CPLError(CE_Failure, CPLE_AppDefined,
709 : "DeleteFeature(" CPL_FRMT_GIB
710 : ") failed. Unable to delete features "
711 : "in tables without\n a recognised FID column.",
712 : nFID);
713 0 : return OGRERR_FAILURE;
714 : }
715 :
716 : /* -------------------------------------------------------------------- */
717 : /* Form the statement to drop the record. */
718 : /* -------------------------------------------------------------------- */
719 : osCommand.Printf("DELETE FROM `%s` WHERE `%s` = " CPL_FRMT_GIB,
720 30 : poFeatureDefn->GetName(), pszFIDColumn, nFID);
721 :
722 : /* -------------------------------------------------------------------- */
723 : /* Execute the delete. */
724 : /* -------------------------------------------------------------------- */
725 30 : poDS->InterruptLongResult();
726 30 : if (mysql_query(poDS->GetConn(), osCommand.c_str()))
727 : {
728 0 : poDS->ReportError(osCommand.c_str());
729 0 : return OGRERR_FAILURE;
730 : }
731 :
732 : // make sure to attempt to free results of successful queries
733 30 : hResult = mysql_store_result(poDS->GetConn());
734 30 : if (hResult != nullptr)
735 0 : mysql_free_result(hResult);
736 30 : hResult = nullptr;
737 :
738 30 : return mysql_affected_rows(poDS->GetConn()) > 0
739 30 : ? OGRERR_NONE
740 30 : : OGRERR_NON_EXISTING_FEATURE;
741 : }
742 :
743 : /************************************************************************/
744 : /* ICreateFeature() */
745 : /************************************************************************/
746 :
747 444 : OGRErr OGRMySQLTableLayer::ICreateFeature(OGRFeature *poFeature)
748 :
749 : {
750 444 : int bNeedComma = FALSE;
751 888 : CPLString osCommand;
752 :
753 : /* -------------------------------------------------------------------- */
754 : /* Form the INSERT command. */
755 : /* -------------------------------------------------------------------- */
756 444 : osCommand.Printf("INSERT INTO `%s` (", poFeatureDefn->GetName());
757 :
758 444 : if (poFeature->GetGeometryRef() != nullptr)
759 : {
760 432 : osCommand = osCommand + "`" + pszGeomColumn + "` ";
761 432 : bNeedComma = TRUE;
762 : }
763 :
764 444 : if (poFeature->GetFID() != OGRNullFID && pszFIDColumn != nullptr)
765 : {
766 22 : if (bNeedComma)
767 16 : osCommand += ", ";
768 :
769 22 : osCommand = osCommand + "`" + pszFIDColumn + "` ";
770 22 : bNeedComma = TRUE;
771 : }
772 :
773 1950 : for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
774 : {
775 1506 : if (!poFeature->IsFieldSet(i))
776 350 : continue;
777 :
778 1156 : if (!bNeedComma)
779 6 : bNeedComma = TRUE;
780 : else
781 1150 : osCommand += ", ";
782 :
783 2312 : osCommand = osCommand + "`" +
784 2312 : poFeatureDefn->GetFieldDefn(i)->GetNameRef() + "`";
785 : }
786 :
787 444 : osCommand += ") VALUES (";
788 :
789 : // Set the geometry
790 444 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
791 444 : bNeedComma = poGeom != nullptr;
792 444 : if (poGeom != nullptr)
793 : {
794 432 : char *pszWKT = nullptr;
795 432 : poGeom->closeRings();
796 432 : poGeom->flattenTo2D();
797 432 : poGeom->exportToWkt(&pszWKT);
798 :
799 432 : if (pszWKT != nullptr)
800 : {
801 432 : const char *pszAxisOrder = "";
802 432 : OGRSpatialReference *l_poSRS = GetSpatialRef();
803 679 : if (poDS->GetMajorVersion() >= 8 && !poDS->IsMariaDB() && l_poSRS &&
804 247 : l_poSRS->IsGeographic())
805 : {
806 100 : pszAxisOrder = ", 'axis-order=long-lat'";
807 : }
808 :
809 864 : osCommand += CPLString().Printf("%s('%s',%d%s) ",
810 432 : poDS->GetMajorVersion() >= 8
811 : ? "ST_GeomFromText"
812 : : "GeometryFromText",
813 432 : pszWKT, nSRSId, pszAxisOrder);
814 :
815 432 : CPLFree(pszWKT);
816 : }
817 : else
818 0 : osCommand += "''";
819 : }
820 :
821 : // Set the FID
822 444 : if (poFeature->GetFID() != OGRNullFID && pszFIDColumn != nullptr)
823 : {
824 22 : GIntBig nFID = poFeature->GetFID();
825 28 : if (!CPL_INT64_FITS_ON_INT32(nFID) &&
826 6 : GetMetadataItem(OLMD_FID64) == nullptr)
827 : {
828 2 : CPLString osCommand2;
829 : osCommand2.Printf("ALTER TABLE `%s` MODIFY COLUMN `%s` BIGINT "
830 : "UNIQUE NOT NULL AUTO_INCREMENT",
831 2 : poFeatureDefn->GetName(), pszFIDColumn);
832 :
833 2 : if (mysql_query(poDS->GetConn(), osCommand2))
834 : {
835 0 : poDS->ReportError(osCommand2);
836 0 : return OGRERR_FAILURE;
837 : }
838 :
839 : // make sure to attempt to free results of successful queries
840 2 : MYSQL_RES *hResult = mysql_store_result(poDS->GetConn());
841 2 : if (hResult != nullptr)
842 0 : mysql_free_result(hResult);
843 2 : hResult = nullptr;
844 :
845 2 : SetMetadataItem(OLMD_FID64, "YES");
846 : }
847 :
848 22 : if (bNeedComma)
849 16 : osCommand += ", ";
850 22 : osCommand += CPLString().Printf(CPL_FRMT_GIB, nFID);
851 22 : bNeedComma = TRUE;
852 : }
853 :
854 1950 : for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
855 : {
856 1506 : if (!poFeature->IsFieldSet(i))
857 350 : continue;
858 :
859 1156 : if (bNeedComma)
860 1150 : osCommand += ", ";
861 : else
862 6 : bNeedComma = TRUE;
863 :
864 1156 : const char *pszStrValue = poFeature->GetFieldAsString(i);
865 :
866 1156 : if (poFeature->IsFieldNull(i))
867 : {
868 16 : osCommand += "NULL";
869 : }
870 2004 : else if (poFeatureDefn->GetFieldDefn(i)->GetType() != OFTInteger &&
871 1452 : poFeatureDefn->GetFieldDefn(i)->GetType() != OFTInteger64 &&
872 2592 : poFeatureDefn->GetFieldDefn(i)->GetType() != OFTReal &&
873 312 : poFeatureDefn->GetFieldDefn(i)->GetType() != OFTBinary)
874 : {
875 : // We need to quote and escape string fields.
876 312 : osCommand += "'";
877 :
878 2662 : for (int iChar = 0; pszStrValue[iChar] != '\0'; iChar++)
879 : {
880 2352 : if (poFeatureDefn->GetFieldDefn(i)->GetType() !=
881 2352 : OFTIntegerList &&
882 2352 : poFeatureDefn->GetFieldDefn(i)->GetType() !=
883 2352 : OFTInteger64List &&
884 4704 : poFeatureDefn->GetFieldDefn(i)->GetType() != OFTRealList &&
885 7056 : poFeatureDefn->GetFieldDefn(i)->GetWidth() > 0 &&
886 28 : iChar == poFeatureDefn->GetFieldDefn(i)->GetWidth())
887 : {
888 2 : CPLDebug("MYSQL",
889 : "Truncated %s field value, it was too long.",
890 2 : poFeatureDefn->GetFieldDefn(i)->GetNameRef());
891 2 : break;
892 : }
893 :
894 2350 : if (pszStrValue[iChar] == '\\' || pszStrValue[iChar] == '\'')
895 : {
896 2 : osCommand += '\\';
897 2 : osCommand += pszStrValue[iChar];
898 : }
899 : else
900 2348 : osCommand += pszStrValue[iChar];
901 : }
902 :
903 312 : osCommand += "'";
904 : }
905 828 : else if (poFeatureDefn->GetFieldDefn(i)->GetType() == OFTBinary)
906 : {
907 0 : int binaryCount = 0;
908 0 : GByte *binaryData = poFeature->GetFieldAsBinary(i, &binaryCount);
909 0 : char *pszHexValue = CPLBinaryToHex(binaryCount, binaryData);
910 :
911 0 : osCommand += "x'";
912 0 : osCommand += pszHexValue;
913 0 : osCommand += "'";
914 :
915 0 : CPLFree(pszHexValue);
916 : }
917 : else
918 : {
919 828 : osCommand += pszStrValue;
920 : }
921 : }
922 :
923 444 : osCommand += ")";
924 :
925 : // CPLDebug("MYSQL", "%s", osCommand.c_str());
926 444 : int nQueryResult = mysql_query(poDS->GetConn(), osCommand.c_str());
927 444 : const my_ulonglong nFID = mysql_insert_id(poDS->GetConn());
928 :
929 444 : if (nQueryResult)
930 : {
931 7 : int eErrorCode = mysql_errno(poDS->GetConn());
932 7 : if (eErrorCode == 1153)
933 : { // ER_NET_PACKET_TOO_LARGE)
934 0 : poDS->ReportError(
935 : "CreateFeature failed because the MySQL server "
936 : "cannot read the entire query statement. Increase "
937 : "the size of statements your server will allow by "
938 : "altering the 'max_allowed_packet' parameter in "
939 : "your MySQL server configuration.");
940 : }
941 : else
942 : {
943 7 : CPLDebug("MYSQL", "Error number %d", eErrorCode);
944 7 : poDS->ReportError(osCommand.c_str());
945 : }
946 :
947 : // make sure to attempt to free results
948 7 : MYSQL_RES *hResult = mysql_store_result(poDS->GetConn());
949 7 : if (hResult != nullptr)
950 0 : mysql_free_result(hResult);
951 7 : hResult = nullptr;
952 :
953 7 : return OGRERR_FAILURE;
954 : }
955 :
956 437 : if (nFID > 0)
957 : {
958 437 : poFeature->SetFID(nFID);
959 : }
960 :
961 : // make sure to attempt to free results of successful queries
962 437 : MYSQL_RES *hResult = mysql_store_result(poDS->GetConn());
963 437 : if (hResult != nullptr)
964 0 : mysql_free_result(hResult);
965 437 : hResult = nullptr;
966 :
967 437 : return OGRERR_NONE;
968 : }
969 :
970 : /************************************************************************/
971 : /* CreateField() */
972 : /************************************************************************/
973 :
974 160 : OGRErr OGRMySQLTableLayer::CreateField(const OGRFieldDefn *poFieldIn,
975 : int bApproxOK)
976 :
977 : {
978 :
979 160 : MYSQL_RES *hResult = nullptr;
980 320 : CPLString osCommand;
981 :
982 : char szFieldType[256];
983 320 : OGRFieldDefn oField(poFieldIn);
984 :
985 : /* -------------------------------------------------------------------- */
986 : /* Do we want to "launder" the column names into Postgres */
987 : /* friendly format? */
988 : /* -------------------------------------------------------------------- */
989 160 : if (bLaunderColumnNames)
990 : {
991 160 : char *pszSafeName = poDS->LaunderName(oField.GetNameRef());
992 :
993 160 : oField.SetName(pszSafeName);
994 160 : CPLFree(pszSafeName);
995 : }
996 :
997 : /* -------------------------------------------------------------------- */
998 : /* Work out the MySQL type. */
999 : /* -------------------------------------------------------------------- */
1000 160 : if (oField.GetType() == OFTInteger)
1001 : {
1002 30 : if (oField.GetWidth() > 0 && bPreservePrecision)
1003 0 : snprintf(szFieldType, sizeof(szFieldType), "DECIMAL(%d,0)",
1004 : oField.GetWidth());
1005 : else
1006 30 : strcpy(szFieldType, "INTEGER");
1007 : }
1008 130 : else if (oField.GetType() == OFTInteger64)
1009 : {
1010 26 : if (oField.GetWidth() > 0 && bPreservePrecision)
1011 0 : snprintf(szFieldType, sizeof(szFieldType), "DECIMAL(%d,0)",
1012 : oField.GetWidth());
1013 : else
1014 26 : strcpy(szFieldType, "BIGINT");
1015 : }
1016 104 : else if (oField.GetType() == OFTReal)
1017 : {
1018 28 : if (oField.GetWidth() > 0 && oField.GetPrecision() > 0 &&
1019 0 : bPreservePrecision)
1020 0 : snprintf(szFieldType, sizeof(szFieldType), "DOUBLE(%d,%d)",
1021 : oField.GetWidth(), oField.GetPrecision());
1022 : else
1023 28 : strcpy(szFieldType, "DOUBLE");
1024 : }
1025 :
1026 76 : else if (oField.GetType() == OFTDate)
1027 : {
1028 0 : oField.SetDefault(nullptr);
1029 0 : snprintf(szFieldType, sizeof(szFieldType), "DATE");
1030 : }
1031 :
1032 76 : else if (oField.GetType() == OFTDateTime)
1033 : {
1034 8 : if (oField.GetDefault() != nullptr &&
1035 4 : STARTS_WITH_CI(oField.GetDefault(), "CURRENT_TIMESTAMP"))
1036 2 : snprintf(szFieldType, sizeof(szFieldType), "TIMESTAMP");
1037 : else
1038 2 : snprintf(szFieldType, sizeof(szFieldType), "DATETIME");
1039 : }
1040 :
1041 72 : else if (oField.GetType() == OFTTime)
1042 : {
1043 0 : oField.SetDefault(nullptr);
1044 0 : snprintf(szFieldType, sizeof(szFieldType), "TIME");
1045 : }
1046 :
1047 72 : else if (oField.GetType() == OFTBinary)
1048 : {
1049 0 : snprintf(szFieldType, sizeof(szFieldType), "LONGBLOB");
1050 : }
1051 :
1052 72 : else if (oField.GetType() == OFTString)
1053 : {
1054 72 : if (oField.GetWidth() == 0 || !bPreservePrecision)
1055 : {
1056 46 : if (oField.GetDefault() != nullptr)
1057 4 : strcpy(szFieldType, "VARCHAR(256)");
1058 : else
1059 42 : strcpy(szFieldType, "TEXT");
1060 : }
1061 : else
1062 26 : snprintf(szFieldType, sizeof(szFieldType), "VARCHAR(%d)",
1063 : oField.GetWidth());
1064 : }
1065 0 : else if (bApproxOK)
1066 : {
1067 0 : CPLError(CE_Warning, CPLE_NotSupported,
1068 : "Can't create field %s with type %s on MySQL layers. "
1069 : "Creating as TEXT.",
1070 : oField.GetNameRef(),
1071 : OGRFieldDefn::GetFieldTypeName(oField.GetType()));
1072 0 : strcpy(szFieldType, "TEXT");
1073 0 : oField.SetWidth(0);
1074 0 : oField.SetPrecision(0);
1075 : }
1076 : else
1077 : {
1078 0 : CPLError(CE_Failure, CPLE_NotSupported,
1079 : "Can't create field %s with type %s on MySQL layers.",
1080 : oField.GetNameRef(),
1081 : OGRFieldDefn::GetFieldTypeName(oField.GetType()));
1082 :
1083 0 : return OGRERR_FAILURE;
1084 : }
1085 :
1086 : osCommand.Printf("ALTER TABLE `%s` ADD COLUMN `%s` %s%s",
1087 160 : poFeatureDefn->GetName(), oField.GetNameRef(), szFieldType,
1088 160 : (!oField.IsNullable()) ? " NOT NULL" : "");
1089 160 : if (oField.GetDefault() != nullptr && !oField.IsDefaultDriverSpecific())
1090 : {
1091 12 : osCommand += " DEFAULT ";
1092 12 : osCommand += oField.GetDefault();
1093 : }
1094 :
1095 160 : if (mysql_query(poDS->GetConn(), osCommand))
1096 : {
1097 0 : poDS->ReportError(osCommand);
1098 0 : return OGRERR_FAILURE;
1099 : }
1100 :
1101 : // make sure to attempt to free results of successful queries
1102 160 : hResult = mysql_store_result(poDS->GetConn());
1103 160 : if (hResult != nullptr)
1104 0 : mysql_free_result(hResult);
1105 160 : hResult = nullptr;
1106 :
1107 160 : poFeatureDefn->AddFieldDefn(&oField);
1108 :
1109 160 : return OGRERR_NONE;
1110 : }
1111 :
1112 : /************************************************************************/
1113 : /* GetFeature() */
1114 : /************************************************************************/
1115 :
1116 44 : OGRFeature *OGRMySQLTableLayer::GetFeature(GIntBig nFeatureId)
1117 :
1118 : {
1119 44 : if (pszFIDColumn == nullptr)
1120 0 : return OGRMySQLLayer::GetFeature(nFeatureId);
1121 :
1122 : /* -------------------------------------------------------------------- */
1123 : /* Discard any existing resultset. */
1124 : /* -------------------------------------------------------------------- */
1125 44 : ResetReading();
1126 :
1127 : /* -------------------------------------------------------------------- */
1128 : /* Prepare query command that will just fetch the one record of */
1129 : /* interest. */
1130 : /* -------------------------------------------------------------------- */
1131 44 : char *pszFieldList = BuildFields();
1132 88 : CPLString osCommand;
1133 :
1134 : osCommand.Printf("SELECT %s FROM `%s` WHERE `%s` = " CPL_FRMT_GIB,
1135 44 : pszFieldList, poFeatureDefn->GetName(), pszFIDColumn,
1136 44 : nFeatureId);
1137 44 : CPLFree(pszFieldList);
1138 :
1139 : /* -------------------------------------------------------------------- */
1140 : /* Issue the command. */
1141 : /* -------------------------------------------------------------------- */
1142 44 : if (mysql_query(poDS->GetConn(), osCommand))
1143 : {
1144 0 : poDS->ReportError(osCommand);
1145 0 : return nullptr;
1146 : }
1147 :
1148 44 : hResultSet = mysql_store_result(poDS->GetConn());
1149 44 : if (hResultSet == nullptr)
1150 : {
1151 0 : poDS->ReportError("mysql_store_result() failed on query.");
1152 0 : return nullptr;
1153 : }
1154 :
1155 : /* -------------------------------------------------------------------- */
1156 : /* Fetch the result record. */
1157 : /* -------------------------------------------------------------------- */
1158 : char **papszRow;
1159 : unsigned long *panLengths;
1160 :
1161 44 : papszRow = mysql_fetch_row(hResultSet);
1162 44 : if (papszRow == nullptr)
1163 8 : return nullptr;
1164 :
1165 36 : panLengths = mysql_fetch_lengths(hResultSet);
1166 :
1167 : /* -------------------------------------------------------------------- */
1168 : /* Transform into a feature. */
1169 : /* -------------------------------------------------------------------- */
1170 36 : iNextShapeId = nFeatureId;
1171 :
1172 36 : OGRFeature *poFeature = RecordToFeature(papszRow, panLengths);
1173 :
1174 36 : iNextShapeId = 0;
1175 :
1176 : /* -------------------------------------------------------------------- */
1177 : /* Cleanup */
1178 : /* -------------------------------------------------------------------- */
1179 36 : if (hResultSet != nullptr)
1180 36 : mysql_free_result(hResultSet);
1181 36 : hResultSet = nullptr;
1182 :
1183 36 : return poFeature;
1184 : }
1185 :
1186 : /************************************************************************/
1187 : /* GetFeatureCount() */
1188 : /* */
1189 : /* If a spatial filter is in effect, we turn control over to */
1190 : /* the generic counter. Otherwise we return the total count. */
1191 : /* Eventually we should consider implementing a more efficient */
1192 : /* way of counting features matching a spatial query. */
1193 : /************************************************************************/
1194 :
1195 66 : GIntBig OGRMySQLTableLayer::GetFeatureCount(CPL_UNUSED int bForce)
1196 : {
1197 : /* -------------------------------------------------------------------- */
1198 : /* Ensure any active long result is interrupted. */
1199 : /* -------------------------------------------------------------------- */
1200 66 : poDS->InterruptLongResult();
1201 :
1202 : /* -------------------------------------------------------------------- */
1203 : /* Issue the appropriate select command. */
1204 : /* -------------------------------------------------------------------- */
1205 : MYSQL_RES *hResult;
1206 : const char *pszCommand;
1207 :
1208 66 : pszCommand = CPLSPrintf("SELECT COUNT(*) FROM `%s` %s",
1209 66 : poFeatureDefn->GetName(), pszWHERE);
1210 :
1211 66 : if (mysql_query(poDS->GetConn(), pszCommand))
1212 : {
1213 0 : poDS->ReportError(pszCommand);
1214 0 : return FALSE;
1215 : }
1216 :
1217 66 : hResult = mysql_store_result(poDS->GetConn());
1218 66 : if (hResult == nullptr)
1219 : {
1220 0 : poDS->ReportError("mysql_store_result() failed on SELECT COUNT(*).");
1221 0 : return FALSE;
1222 : }
1223 :
1224 : /* -------------------------------------------------------------------- */
1225 : /* Capture the result. */
1226 : /* -------------------------------------------------------------------- */
1227 66 : char **papszRow = mysql_fetch_row(hResult);
1228 66 : GIntBig nCount = 0;
1229 :
1230 66 : if (papszRow != nullptr && papszRow[0] != nullptr)
1231 66 : nCount = CPLAtoGIntBig(papszRow[0]);
1232 :
1233 66 : mysql_free_result(hResult);
1234 :
1235 66 : return nCount;
1236 : }
1237 :
1238 : /************************************************************************/
1239 : /* GetExtent() */
1240 : /* */
1241 : /* Retrieve the MBR of the MySQL table. This should be made more */
1242 : /* in the future when MySQL adds support for a single MBR query */
1243 : /* like PostgreSQL. */
1244 : /************************************************************************/
1245 :
1246 12 : OGRErr OGRMySQLTableLayer::GetExtent(OGREnvelope *psExtent,
1247 : CPL_UNUSED int bForce)
1248 : {
1249 12 : if (GetLayerDefn()->GetGeomType() == wkbNone)
1250 : {
1251 0 : psExtent->MinX = 0.0;
1252 0 : psExtent->MaxX = 0.0;
1253 0 : psExtent->MinY = 0.0;
1254 0 : psExtent->MaxY = 0.0;
1255 :
1256 0 : return OGRERR_FAILURE;
1257 : }
1258 :
1259 12 : ResetReading();
1260 :
1261 12 : OGREnvelope oEnv;
1262 24 : CPLString osCommand;
1263 12 : GBool bExtentSet = FALSE;
1264 :
1265 12 : if (poDS->GetMajorVersion() >= 8 && !poDS->IsMariaDB())
1266 : {
1267 : // ST_Envelope() does not work on geographic SRS, so force to 0
1268 : osCommand.Printf("SELECT ST_Envelope(ST_SRID(`%s`,0)) FROM `%s`;",
1269 6 : pszGeomColumn, pszGeomColumnTable);
1270 : }
1271 : else
1272 : {
1273 : osCommand.Printf("SELECT Envelope(`%s`) FROM `%s`;", pszGeomColumn,
1274 6 : pszGeomColumnTable);
1275 : }
1276 :
1277 12 : if (mysql_query(poDS->GetConn(), osCommand) == 0)
1278 : {
1279 12 : MYSQL_RES *result = mysql_use_result(poDS->GetConn());
1280 12 : if (result == nullptr)
1281 : {
1282 0 : poDS->ReportError("mysql_use_result() failed on extents query.");
1283 0 : return OGRERR_FAILURE;
1284 : }
1285 :
1286 : MYSQL_ROW row;
1287 12 : unsigned long *panLengths = nullptr;
1288 114 : while ((row = mysql_fetch_row(result)) != nullptr)
1289 : {
1290 102 : if (panLengths == nullptr)
1291 : {
1292 12 : panLengths = mysql_fetch_lengths(result);
1293 12 : if (panLengths == nullptr)
1294 : {
1295 0 : poDS->ReportError(
1296 : "mysql_fetch_lengths() failed on extents query.");
1297 0 : return OGRERR_FAILURE;
1298 : }
1299 : }
1300 :
1301 102 : OGRGeometry *poGeometry = nullptr;
1302 : // Geometry columns will have the first 4 bytes contain the SRID.
1303 102 : OGRGeometryFactory::createFromWkb(
1304 102 : row[0] + 4, nullptr, &poGeometry,
1305 102 : static_cast<int>(panLengths[0] - 4));
1306 :
1307 102 : if (poGeometry != nullptr)
1308 : {
1309 102 : if (!bExtentSet)
1310 : {
1311 12 : poGeometry->getEnvelope(psExtent);
1312 12 : bExtentSet = TRUE;
1313 : }
1314 : else
1315 : {
1316 90 : poGeometry->getEnvelope(&oEnv);
1317 90 : if (oEnv.MinX < psExtent->MinX)
1318 20 : psExtent->MinX = oEnv.MinX;
1319 90 : if (oEnv.MinY < psExtent->MinY)
1320 30 : psExtent->MinY = oEnv.MinY;
1321 90 : if (oEnv.MaxX > psExtent->MaxX)
1322 20 : psExtent->MaxX = oEnv.MaxX;
1323 90 : if (oEnv.MaxY > psExtent->MaxY)
1324 0 : psExtent->MaxY = oEnv.MaxY;
1325 : }
1326 102 : delete poGeometry;
1327 : }
1328 : }
1329 :
1330 12 : mysql_free_result(result);
1331 : }
1332 : else
1333 : {
1334 0 : poDS->ReportError(osCommand.c_str());
1335 : }
1336 :
1337 12 : return bExtentSet ? OGRERR_NONE : OGRERR_FAILURE;
1338 : }
|