LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/mysql - ogrmysqltablelayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 485 587 82.6 %
Date: 2024-05-03 15:49:35 Functions: 19 19 100.0 %

          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             : }

Generated by: LCOV version 1.14