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

Generated by: LCOV version 1.14