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