LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/mssqlspatial - ogrmssqlspatialdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 113 785 14.4 %
Date: 2024-05-04 12:52:34 Functions: 5 25 20.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  MSSQL Spatial driver
       4             :  * Purpose:  Implements OGRMSSQLSpatialDataSource class..
       5             :  * Author:   Tamas Szekeres, szekerest at gmail.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010, Tamas Szekeres
       9             :  * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice shall be included
      19             :  * in all copies or substantial portions of the Software.
      20             :  *
      21             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27             :  * DEALINGS IN THE SOFTWARE.
      28             :  ****************************************************************************/
      29             : 
      30             : #include "ogr_mssqlspatial.h"
      31             : 
      32             : /************************************************************************/
      33             : /*                          OGRMSSQLSpatialDataSource()                 */
      34             : /************************************************************************/
      35             : 
      36           1 : OGRMSSQLSpatialDataSource::OGRMSSQLSpatialDataSource() : bDSUpdate(false)
      37             : {
      38           1 :     pszName = nullptr;
      39           1 :     pszCatalog = nullptr;
      40           1 :     papoLayers = nullptr;
      41           1 :     nLayers = 0;
      42             : 
      43           1 :     poLayerInCopyMode = nullptr;
      44             : 
      45           1 :     nGeometryFormat = MSSQLGEOMETRY_NATIVE;
      46           1 :     pszConnection = nullptr;
      47             : 
      48           1 :     sMSSQLVersion.nMajor = 0;
      49           1 :     sMSSQLVersion.nMinor = 0;
      50           1 :     sMSSQLVersion.nBuild = 0;
      51           1 :     sMSSQLVersion.nRevision = 0;
      52             : 
      53           1 :     bUseGeometryColumns = CPLTestBool(
      54             :         CPLGetConfigOption("MSSQLSPATIAL_USE_GEOMETRY_COLUMNS", "YES"));
      55           1 :     bAlwaysOutputFid =
      56           1 :         CPLTestBool(CPLGetConfigOption("MSSQLSPATIAL_ALWAYS_OUTPUT_FID", "NO"));
      57           1 :     bListAllTables =
      58           1 :         CPLTestBool(CPLGetConfigOption("MSSQLSPATIAL_LIST_ALL_TABLES", "NO"));
      59             : 
      60             :     const char *nBCPSizeParam =
      61           1 :         CPLGetConfigOption("MSSQLSPATIAL_BCP_SIZE", nullptr);
      62           1 :     if (nBCPSizeParam != nullptr)
      63           0 :         nBCPSize = atoi(nBCPSizeParam);
      64             :     else
      65           1 :         nBCPSize = 1000;
      66             : #ifdef MSSQL_BCP_SUPPORTED
      67             :     bUseCopy = CPLTestBool(CPLGetConfigOption("MSSQLSPATIAL_USE_BCP", "TRUE"));
      68             : #else
      69           1 :     bUseCopy = FALSE;
      70             : #endif
      71           1 :     CPLDebug("MSSQLSpatial", "Use COPY/BCP: %d", bUseCopy);
      72           1 : }
      73             : 
      74             : /************************************************************************/
      75             : /*                         ~OGRMSSQLSpatialDataSource()                 */
      76             : /************************************************************************/
      77             : 
      78           2 : OGRMSSQLSpatialDataSource::~OGRMSSQLSpatialDataSource()
      79             : 
      80             : {
      81           1 :     for (int i = 0; i < nLayers; i++)
      82           0 :         delete papoLayers[i];
      83             : 
      84           1 :     CPLFree(papoLayers);
      85             : 
      86           1 :     CPLFree(pszName);
      87           1 :     CPLFree(pszCatalog);
      88             : 
      89           1 :     CPLFree(pszConnection);
      90           2 : }
      91             : 
      92             : /************************************************************************/
      93             : /*                      OGRMSSQLDecodeVersionString()                   */
      94             : /************************************************************************/
      95             : 
      96           0 : void OGRMSSQLSpatialDataSource::OGRMSSQLDecodeVersionString(MSSQLVer *psVersion,
      97             :                                                             const char *pszVer)
      98             : {
      99           0 :     while (*pszVer == ' ')
     100           0 :         pszVer++;
     101             : 
     102           0 :     const char *ptr = pszVer;
     103             :     // get Version string
     104           0 :     while (*ptr && *ptr != ' ')
     105           0 :         ptr++;
     106           0 :     GUInt32 iLen = static_cast<int>(ptr - pszVer);
     107           0 :     char szVer[20] = {};
     108           0 :     if (iLen > sizeof(szVer) - 1)
     109           0 :         iLen = sizeof(szVer) - 1;
     110           0 :     strncpy(szVer, pszVer, iLen);
     111           0 :     szVer[iLen] = '\0';
     112             : 
     113           0 :     ptr = pszVer = szVer;
     114             : 
     115             :     // get Major number
     116           0 :     while (*ptr && *ptr != '.')
     117           0 :         ptr++;
     118           0 :     iLen = static_cast<int>(ptr - pszVer);
     119           0 :     char szNum[20] = {};
     120           0 :     if (iLen > sizeof(szNum) - 1)
     121           0 :         iLen = sizeof(szNum) - 1;
     122           0 :     strncpy(szNum, pszVer, iLen);
     123           0 :     szNum[iLen] = '\0';
     124           0 :     psVersion->nMajor = atoi(szNum);
     125             : 
     126           0 :     if (*ptr == 0)
     127           0 :         return;
     128           0 :     pszVer = ++ptr;
     129             : 
     130             :     // get Minor number
     131           0 :     while (*ptr && *ptr != '.')
     132           0 :         ptr++;
     133           0 :     iLen = static_cast<int>(ptr - pszVer);
     134           0 :     if (iLen > sizeof(szNum) - 1)
     135           0 :         iLen = sizeof(szNum) - 1;
     136           0 :     strncpy(szNum, pszVer, iLen);
     137           0 :     szNum[iLen] = '\0';
     138           0 :     psVersion->nMinor = atoi(szNum);
     139             : 
     140           0 :     if (*ptr == 0)
     141           0 :         return;
     142           0 :     pszVer = ++ptr;
     143             : 
     144             :     // get Build number
     145           0 :     while (*ptr && *ptr != '.')
     146           0 :         ptr++;
     147           0 :     iLen = static_cast<int>(ptr - pszVer);
     148           0 :     if (iLen > sizeof(szNum) - 1)
     149           0 :         iLen = sizeof(szNum) - 1;
     150           0 :     strncpy(szNum, pszVer, iLen);
     151           0 :     szNum[iLen] = '\0';
     152           0 :     psVersion->nBuild = atoi(szNum);
     153             : 
     154           0 :     if (*ptr == 0)
     155           0 :         return;
     156           0 :     pszVer = ++ptr;
     157             : 
     158             :     // get Revision number
     159           0 :     while (*ptr && *ptr != '.')
     160           0 :         ptr++;
     161           0 :     iLen = static_cast<int>(ptr - pszVer);
     162           0 :     if (iLen > sizeof(szNum) - 1)
     163           0 :         iLen = sizeof(szNum) - 1;
     164           0 :     strncpy(szNum, pszVer, iLen);
     165           0 :     szNum[iLen] = '\0';
     166           0 :     psVersion->nRevision = atoi(szNum);
     167             : }
     168             : 
     169             : /************************************************************************/
     170             : /*                           TestCapability()                           */
     171             : /************************************************************************/
     172             : 
     173           0 : int OGRMSSQLSpatialDataSource::TestCapability(const char *pszCap)
     174             : 
     175             : {
     176             : #if (ODBCVER >= 0x0300)
     177           0 :     if (EQUAL(pszCap, ODsCTransactions))
     178           0 :         return TRUE;
     179             : #endif
     180           0 :     if (EQUAL(pszCap, ODsCCreateLayer) || EQUAL(pszCap, ODsCDeleteLayer))
     181           0 :         return TRUE;
     182           0 :     if (EQUAL(pszCap, ODsCRandomLayerWrite))
     183           0 :         return TRUE;
     184           0 :     if (EQUAL(pszCap, OLCFastGetExtent))
     185           0 :         return TRUE;
     186           0 :     else if (EQUAL(pszCap, ODsCCurveGeometries))
     187           0 :         return TRUE;
     188           0 :     else if (EQUAL(pszCap, ODsCMeasuredGeometries))
     189           0 :         return TRUE;
     190           0 :     else if (EQUAL(pszCap, ODsCZGeometries))
     191           0 :         return TRUE;
     192             :     else
     193           0 :         return FALSE;
     194             : }
     195             : 
     196             : /************************************************************************/
     197             : /*                              GetLayer()                              */
     198             : /************************************************************************/
     199             : 
     200           0 : OGRLayer *OGRMSSQLSpatialDataSource::GetLayer(int iLayer)
     201             : 
     202             : {
     203           0 :     if (iLayer < 0 || iLayer >= nLayers)
     204           0 :         return nullptr;
     205             :     else
     206           0 :         return papoLayers[iLayer];
     207             : }
     208             : 
     209             : /************************************************************************/
     210             : /*                           GetLayerByName()                           */
     211             : /************************************************************************/
     212             : 
     213           0 : OGRLayer *OGRMSSQLSpatialDataSource::GetLayerByName(const char *pszLayerName)
     214             : 
     215             : {
     216           0 :     if (!pszLayerName)
     217           0 :         return nullptr;
     218             : 
     219           0 :     char *pszTableName = nullptr;
     220           0 :     char *pszSchemaName = nullptr;
     221             : 
     222           0 :     const char *pszDotPos = strstr(pszLayerName, ".");
     223           0 :     if (pszDotPos != nullptr)
     224             :     {
     225           0 :         int length = static_cast<int>(pszDotPos - pszLayerName);
     226           0 :         pszSchemaName = (char *)CPLMalloc(length + 1);
     227           0 :         strncpy(pszSchemaName, pszLayerName, length);
     228           0 :         pszSchemaName[length] = '\0';
     229           0 :         pszTableName = CPLStrdup(pszDotPos + 1);  // skip "."
     230             :     }
     231             :     else
     232             :     {
     233           0 :         pszSchemaName = CPLStrdup("dbo");
     234           0 :         pszTableName = CPLStrdup(pszLayerName);
     235             :     }
     236             : 
     237           0 :     for (int iLayer = 0; iLayer < nLayers; iLayer++)
     238             :     {
     239           0 :         if (EQUAL(pszTableName, papoLayers[iLayer]->GetTableName()) &&
     240           0 :             EQUAL(pszSchemaName, papoLayers[iLayer]->GetSchemaName()))
     241             :         {
     242           0 :             CPLFree(pszSchemaName);
     243           0 :             CPLFree(pszTableName);
     244           0 :             return papoLayers[iLayer];
     245             :         }
     246             :     }
     247             : 
     248           0 :     CPLFree(pszSchemaName);
     249           0 :     CPLFree(pszTableName);
     250             : 
     251           0 :     return nullptr;
     252             : }
     253             : 
     254             : /************************************************************************/
     255             : /*                            DeleteLayer()                             */
     256             : /************************************************************************/
     257             : 
     258           0 : OGRErr OGRMSSQLSpatialDataSource::DeleteLayer(int iLayer)
     259             : 
     260             : {
     261           0 :     if (iLayer < 0 || iLayer >= nLayers)
     262           0 :         return OGRERR_FAILURE;
     263             : 
     264           0 :     EndCopy();
     265             : 
     266             :     /* -------------------------------------------------------------------- */
     267             :     /*      Blow away our OGR structures related to the layer.  This is     */
     268             :     /*      pretty dangerous if anything has a reference to this layer!     */
     269             :     /* -------------------------------------------------------------------- */
     270           0 :     const char *pszTableName = papoLayers[iLayer]->GetTableName();
     271           0 :     const char *pszSchemaName = papoLayers[iLayer]->GetSchemaName();
     272             : 
     273           0 :     CPLODBCStatement oStmt(&oSession);
     274           0 :     if (bUseGeometryColumns)
     275           0 :         oStmt.Appendf("DELETE FROM geometry_columns WHERE f_table_schema = "
     276             :                       "'%s' AND f_table_name = '%s'\n",
     277             :                       pszSchemaName, pszTableName);
     278           0 :     oStmt.Appendf("DROP TABLE [%s].[%s]", pszSchemaName, pszTableName);
     279             : 
     280           0 :     CPLDebug("MSSQLSpatial", "DeleteLayer(%s)", pszTableName);
     281             : 
     282           0 :     papoLayers[iLayer]->SetSpatialIndexFlag(FALSE);
     283             : 
     284           0 :     delete papoLayers[iLayer];
     285           0 :     memmove(papoLayers + iLayer, papoLayers + iLayer + 1,
     286           0 :             sizeof(void *) * (nLayers - iLayer - 1));
     287           0 :     nLayers--;
     288             : 
     289             :     /* -------------------------------------------------------------------- */
     290             :     /*      Remove from the database.                                       */
     291             :     /* -------------------------------------------------------------------- */
     292             : 
     293           0 :     int bInTransaction = oSession.IsInTransaction();
     294           0 :     if (!bInTransaction)
     295           0 :         oSession.BeginTransaction();
     296             : 
     297           0 :     if (!oStmt.ExecuteSQL())
     298             :     {
     299           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Error deleting layer: %s",
     300             :                  GetSession()->GetLastError());
     301             : 
     302           0 :         if (!bInTransaction)
     303           0 :             oSession.RollbackTransaction();
     304             : 
     305           0 :         return OGRERR_FAILURE;
     306             :     }
     307             : 
     308           0 :     if (!bInTransaction)
     309           0 :         oSession.CommitTransaction();
     310             : 
     311           0 :     return OGRERR_NONE;
     312             : }
     313             : 
     314             : /************************************************************************/
     315             : /*                            CreateLayer()                             */
     316             : /************************************************************************/
     317             : 
     318             : OGRLayer *
     319           0 : OGRMSSQLSpatialDataSource::ICreateLayer(const char *pszLayerName,
     320             :                                         const OGRGeomFieldDefn *poGeomFieldDefn,
     321             :                                         CSLConstList papszOptions)
     322             : 
     323             : {
     324           0 :     char *pszTableName = nullptr;
     325           0 :     char *pszSchemaName = nullptr;
     326           0 :     const char *pszGeomType = nullptr;
     327           0 :     const char *pszGeomColumn = nullptr;
     328           0 :     int nCoordDimension = 3;
     329           0 :     char *pszFIDColumnName = nullptr;
     330             : 
     331           0 :     const auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
     332             :     const auto poSRS =
     333           0 :         poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
     334             : 
     335           0 :     EndCopy();
     336             : 
     337             :     /* determine the dimension */
     338           0 :     if (eType == wkbFlatten(eType))
     339           0 :         nCoordDimension = 2;
     340             : 
     341           0 :     if (CSLFetchNameValue(papszOptions, "DIM") != nullptr)
     342           0 :         nCoordDimension = atoi(CSLFetchNameValue(papszOptions, "DIM"));
     343             : 
     344           0 :     int bExtractSchemaFromLayerName = CPLTestBool(CSLFetchNameValueDef(
     345           0 :         papszOptions, "EXTRACT_SCHEMA_FROM_LAYER_NAME", "YES"));
     346             : 
     347             :     /* MSSQL Schema handling:
     348             :        Extract schema name from input layer name or passed with -lco SCHEMA.
     349             :        Set layer name to "schema.table" or to "table" if schema is not
     350             :        specified
     351             :     */
     352           0 :     const char *pszDotPos = strstr(pszLayerName, ".");
     353           0 :     if (pszDotPos != nullptr && bExtractSchemaFromLayerName)
     354             :     {
     355           0 :         int length = static_cast<int>(pszDotPos - pszLayerName);
     356           0 :         pszSchemaName = (char *)CPLMalloc(length + 1);
     357           0 :         CPLAssert(pszSchemaName !=
     358             :                   nullptr); /* to make Coverity happy and not believe a
     359             :                                REVERSE_INULL is possible */
     360           0 :         strncpy(pszSchemaName, pszLayerName, length);
     361           0 :         pszSchemaName[length] = '\0';
     362             : 
     363           0 :         if (CPLFetchBool(papszOptions, "LAUNDER", true))
     364           0 :             pszTableName = LaunderName(pszDotPos + 1);  // skip "."
     365             :         else
     366           0 :             pszTableName = CPLStrdup(pszDotPos + 1);  // skip "."
     367             :     }
     368             :     else
     369             :     {
     370           0 :         if (CPLFetchBool(papszOptions, "LAUNDER", TRUE))
     371           0 :             pszTableName = LaunderName(pszLayerName);  // skip "."
     372             :         else
     373           0 :             pszTableName = CPLStrdup(pszLayerName);  // skip "."
     374             :     }
     375             : 
     376           0 :     if (CSLFetchNameValue(papszOptions, "SCHEMA") != nullptr)
     377             :     {
     378           0 :         CPLFree(pszSchemaName);
     379           0 :         pszSchemaName = CPLStrdup(CSLFetchNameValue(papszOptions, "SCHEMA"));
     380             :     }
     381             : 
     382           0 :     if (pszSchemaName == nullptr)
     383           0 :         pszSchemaName = CPLStrdup("dbo");
     384             : 
     385             :     /* -------------------------------------------------------------------- */
     386             :     /*      Do we already have this layer?  If so, should we blow it        */
     387             :     /*      away?                                                           */
     388             :     /* -------------------------------------------------------------------- */
     389             :     int iLayer;
     390             : 
     391           0 :     for (iLayer = 0; iLayer < nLayers; iLayer++)
     392             :     {
     393           0 :         if (EQUAL(pszTableName, papoLayers[iLayer]->GetTableName()) &&
     394           0 :             EQUAL(pszSchemaName, papoLayers[iLayer]->GetSchemaName()))
     395             :         {
     396           0 :             if (CSLFetchNameValue(papszOptions, "OVERWRITE") != nullptr &&
     397           0 :                 !EQUAL(CSLFetchNameValue(papszOptions, "OVERWRITE"), "NO"))
     398             :             {
     399           0 :                 CPLFree(pszSchemaName);
     400           0 :                 pszSchemaName = CPLStrdup(papoLayers[iLayer]->GetSchemaName());
     401             : 
     402           0 :                 DeleteLayer(iLayer);
     403             :             }
     404             :             else
     405             :             {
     406           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     407             :                          "Layer %s already exists, CreateLayer failed.\n"
     408             :                          "Use the layer creation option OVERWRITE=YES to "
     409             :                          "replace it.",
     410             :                          pszLayerName);
     411             : 
     412           0 :                 CPLFree(pszSchemaName);
     413           0 :                 CPLFree(pszTableName);
     414           0 :                 return nullptr;
     415             :             }
     416             :         }
     417             :     }
     418             : 
     419             :     /* -------------------------------------------------------------------- */
     420             :     /*      Handle the GEOM_TYPE option.                                    */
     421             :     /* -------------------------------------------------------------------- */
     422           0 :     if (eType != wkbNone)
     423             :     {
     424           0 :         pszGeomType = CSLFetchNameValue(papszOptions, "GEOM_TYPE");
     425             : 
     426           0 :         if (!pszGeomType)
     427           0 :             pszGeomType = "geometry";
     428             : 
     429           0 :         if (!EQUAL(pszGeomType, "geometry") && !EQUAL(pszGeomType, "geography"))
     430             :         {
     431           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     432             :                      "FORMAT=%s not recognised or supported.", pszGeomType);
     433             : 
     434           0 :             CPLFree(pszSchemaName);
     435           0 :             CPLFree(pszTableName);
     436           0 :             return nullptr;
     437             :         }
     438             : 
     439             :         /* determine the geometry column name */
     440           0 :         pszGeomColumn = CSLFetchNameValue(papszOptions, "GEOMETRY_NAME");
     441           0 :         if (!pszGeomColumn)
     442           0 :             pszGeomColumn = CSLFetchNameValue(papszOptions, "GEOM_NAME");
     443           0 :         if (!pszGeomColumn)
     444           0 :             pszGeomColumn = "ogr_geometry";
     445             :     }
     446             :     const bool bGeomNullable =
     447           0 :         CPLFetchBool(papszOptions, "GEOMETRY_NULLABLE", true);
     448             : 
     449             :     /* -------------------------------------------------------------------- */
     450             :     /*      Initialize the metadata tables                                  */
     451             :     /* -------------------------------------------------------------------- */
     452             : 
     453           0 :     if (InitializeMetadataTables() != OGRERR_NONE)
     454             :     {
     455           0 :         CPLFree(pszSchemaName);
     456           0 :         CPLFree(pszTableName);
     457           0 :         return nullptr;
     458             :     }
     459             : 
     460             :     /* -------------------------------------------------------------------- */
     461             :     /*      Try to get the SRS Id of this spatial reference system,         */
     462             :     /*      adding to the srs table if needed.                              */
     463             :     /* -------------------------------------------------------------------- */
     464           0 :     int nSRSId = 0;
     465             : 
     466           0 :     if (CSLFetchNameValue(papszOptions, "SRID") != nullptr)
     467           0 :         nSRSId = atoi(CSLFetchNameValue(papszOptions, "SRID"));
     468             : 
     469           0 :     if (nSRSId == 0 && poSRS != nullptr)
     470           0 :         nSRSId = FetchSRSId(poSRS);
     471             : 
     472             :     /* -------------------------------------------------------------------- */
     473             :     /*      Create a new table and create a new entry in the geometry,      */
     474             :     /*      geometry_columns metadata table.                                */
     475             :     /* -------------------------------------------------------------------- */
     476             : 
     477           0 :     CPLODBCStatement oStmt(&oSession);
     478             : 
     479           0 :     if (eType != wkbNone && bUseGeometryColumns)
     480             :     {
     481           0 :         const char *pszGeometryType = OGRToOGCGeomType(eType);
     482             : 
     483           0 :         oStmt.Appendf(
     484             :             "DELETE FROM geometry_columns WHERE f_table_schema = '%s' "
     485             :             "AND f_table_name = '%s'\n",
     486             :             pszSchemaName, pszTableName);
     487             : 
     488           0 :         oStmt.Appendf("INSERT INTO [geometry_columns] ([f_table_catalog], "
     489             :                       "[f_table_schema] ,[f_table_name], "
     490             :                       "[f_geometry_column],[coord_dimension],[srid],[geometry_"
     491             :                       "type]) VALUES ('%s', '%s', '%s', '%s', %d, %d, '%s')\n",
     492             :                       pszCatalog, pszSchemaName, pszTableName, pszGeomColumn,
     493             :                       nCoordDimension, nSRSId, pszGeometryType);
     494             :     }
     495             : 
     496           0 :     if (!EQUAL(pszSchemaName, "dbo"))
     497             :     {
     498             :         // creating the schema if not exists
     499           0 :         oStmt.Appendf("IF NOT EXISTS (SELECT name from sys.schemas WHERE name "
     500             :                       "= '%s') EXEC sp_executesql N'CREATE SCHEMA [%s]'\n",
     501             :                       pszSchemaName, pszSchemaName);
     502             :     }
     503             : 
     504             :     /* determine the FID column name */
     505             :     const char *pszFIDColumnNameIn =
     506           0 :         CSLFetchNameValueDef(papszOptions, "FID", "ogr_fid");
     507           0 :     if (CPLFetchBool(papszOptions, "LAUNDER", TRUE))
     508           0 :         pszFIDColumnName = LaunderName(pszFIDColumnNameIn);
     509             :     else
     510           0 :         pszFIDColumnName = CPLStrdup(pszFIDColumnNameIn);
     511             : 
     512           0 :     const bool bFID64 = CPLFetchBool(papszOptions, "FID64", FALSE);
     513           0 :     const char *pszFIDType = bFID64 ? "bigint" : "int";
     514             : 
     515           0 :     if (eType == wkbNone)
     516             :     {
     517           0 :         oStmt.Appendf(
     518             :             "CREATE TABLE [%s].[%s] ([%s] [%s] IDENTITY(1,1) NOT NULL, "
     519             :             "CONSTRAINT [PK_%s] PRIMARY KEY CLUSTERED ([%s] ASC))",
     520             :             pszSchemaName, pszTableName, pszFIDColumnName, pszFIDType,
     521             :             pszTableName, pszFIDColumnName);
     522             :     }
     523             :     else
     524             :     {
     525           0 :         oStmt.Appendf(
     526             :             "CREATE TABLE [%s].[%s] ([%s] [%s] IDENTITY(1,1) NOT NULL, "
     527             :             "[%s] [%s] %s, CONSTRAINT [PK_%s] PRIMARY KEY CLUSTERED ([%s] "
     528             :             "ASC))",
     529             :             pszSchemaName, pszTableName, pszFIDColumnName, pszFIDType,
     530             :             pszGeomColumn, pszGeomType, bGeomNullable ? "NULL" : "NOT NULL",
     531             :             pszTableName, pszFIDColumnName);
     532             :     }
     533             : 
     534           0 :     CPLFree(pszFIDColumnName);
     535             : 
     536           0 :     int bInTransaction = oSession.IsInTransaction();
     537           0 :     if (!bInTransaction)
     538           0 :         oSession.BeginTransaction();
     539             : 
     540           0 :     if (!oStmt.ExecuteSQL())
     541             :     {
     542           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     543             :                  "Error creating layer: %s When using the overwrite option and "
     544             :                  "the layer doesn't contain geometry column, you might require "
     545             :                  "to use the MSSQLSPATIAL_LIST_ALL_TABLES config option to get "
     546             :                  "the previous layer deleted before creating the new one.",
     547             :                  GetSession()->GetLastError());
     548             : 
     549           0 :         if (!bInTransaction)
     550           0 :             oSession.RollbackTransaction();
     551             : 
     552           0 :         return nullptr;
     553             :     }
     554             : 
     555           0 :     if (!bInTransaction)
     556           0 :         oSession.CommitTransaction();
     557             : 
     558             :     /* -------------------------------------------------------------------- */
     559             :     /*      Create the layer object.                                        */
     560             :     /* -------------------------------------------------------------------- */
     561             :     OGRMSSQLSpatialTableLayer *poLayer;
     562             : 
     563           0 :     poLayer = new OGRMSSQLSpatialTableLayer(this);
     564             : 
     565           0 :     if (bInTransaction)
     566           0 :         poLayer->SetLayerStatus(MSSQLLAYERSTATUS_INITIAL);
     567             :     else
     568           0 :         poLayer->SetLayerStatus(MSSQLLAYERSTATUS_CREATED);
     569             : 
     570           0 :     poLayer->SetLaunderFlag(CPLFetchBool(papszOptions, "LAUNDER", true));
     571           0 :     poLayer->SetPrecisionFlag(CPLFetchBool(papszOptions, "PRECISION", true));
     572             : 
     573           0 :     if (bUseCopy)
     574           0 :         poLayer->SetUseCopy(nBCPSize);
     575             : 
     576           0 :     const char *pszSI = CSLFetchNameValue(papszOptions, "SPATIAL_INDEX");
     577           0 :     int bCreateSpatialIndex = (pszSI == nullptr || CPLTestBool(pszSI));
     578           0 :     if (pszGeomColumn == nullptr)
     579           0 :         bCreateSpatialIndex = FALSE;
     580             : 
     581           0 :     poLayer->SetSpatialIndexFlag(bCreateSpatialIndex);
     582             : 
     583             :     const char *pszUploadGeometryFormat =
     584           0 :         CSLFetchNameValue(papszOptions, "UPLOAD_GEOM_FORMAT");
     585           0 :     if (pszUploadGeometryFormat)
     586             :     {
     587           0 :         if (STARTS_WITH_CI(pszUploadGeometryFormat, "wkb"))
     588           0 :             poLayer->SetUploadGeometryFormat(MSSQLGEOMETRY_WKB);
     589           0 :         else if (STARTS_WITH_CI(pszUploadGeometryFormat, "wkt"))
     590           0 :             poLayer->SetUploadGeometryFormat(MSSQLGEOMETRY_WKT);
     591             :     }
     592             : 
     593           0 :     char *pszWKT = nullptr;
     594           0 :     if (poSRS && poSRS->exportToWkt(&pszWKT) != OGRERR_NONE)
     595             :     {
     596           0 :         CPLFree(pszWKT);
     597           0 :         pszWKT = nullptr;
     598             :     }
     599             : 
     600           0 :     if (bFID64)
     601           0 :         poLayer->SetMetadataItem(OLMD_FID64, "YES");
     602             : 
     603           0 :     if (poLayer->Initialize(pszSchemaName, pszTableName, pszGeomColumn,
     604             :                             nCoordDimension, nSRSId, pszWKT,
     605           0 :                             eType) == CE_Failure)
     606             :     {
     607           0 :         CPLFree(pszSchemaName);
     608           0 :         CPLFree(pszTableName);
     609           0 :         CPLFree(pszWKT);
     610           0 :         return nullptr;
     611             :     }
     612             : 
     613           0 :     CPLFree(pszSchemaName);
     614           0 :     CPLFree(pszTableName);
     615           0 :     CPLFree(pszWKT);
     616             : 
     617             :     /* -------------------------------------------------------------------- */
     618             :     /*      Add layer to data source layer list.                            */
     619             :     /* -------------------------------------------------------------------- */
     620           0 :     papoLayers = (OGRMSSQLSpatialTableLayer **)CPLRealloc(
     621           0 :         papoLayers, sizeof(OGRMSSQLSpatialTableLayer *) * (nLayers + 1));
     622             : 
     623           0 :     papoLayers[nLayers++] = poLayer;
     624             : 
     625           0 :     return poLayer;
     626             : }
     627             : 
     628             : /************************************************************************/
     629             : /*                             OpenTable()                              */
     630             : /************************************************************************/
     631             : 
     632           0 : int OGRMSSQLSpatialDataSource::OpenTable(const char *pszSchemaName,
     633             :                                          const char *pszTableName,
     634             :                                          const char *pszGeomCol,
     635             :                                          int nCoordDimension, int nSRID,
     636             :                                          const char *pszSRText,
     637             :                                          OGRwkbGeometryType eType, bool bUpdate)
     638             : {
     639             :     /* -------------------------------------------------------------------- */
     640             :     /*      Create the layer object.                                        */
     641             :     /* -------------------------------------------------------------------- */
     642           0 :     OGRMSSQLSpatialTableLayer *poLayer = new OGRMSSQLSpatialTableLayer(this);
     643             : 
     644           0 :     if (poLayer->Initialize(pszSchemaName, pszTableName, pszGeomCol,
     645           0 :                             nCoordDimension, nSRID, pszSRText, eType))
     646             :     {
     647           0 :         delete poLayer;
     648           0 :         return FALSE;
     649             :     }
     650           0 :     poLayer->SetUpdate(bUpdate);
     651             : 
     652           0 :     if (bUseCopy)
     653           0 :         poLayer->SetUseCopy(nBCPSize);
     654             : 
     655             :     /* -------------------------------------------------------------------- */
     656             :     /*      Add layer to data source layer list.                            */
     657             :     /* -------------------------------------------------------------------- */
     658           0 :     papoLayers = (OGRMSSQLSpatialTableLayer **)CPLRealloc(
     659           0 :         papoLayers, sizeof(OGRMSSQLSpatialTableLayer *) * (nLayers + 1));
     660           0 :     papoLayers[nLayers++] = poLayer;
     661             : 
     662           0 :     return TRUE;
     663             : }
     664             : 
     665             : /************************************************************************/
     666             : /*                       GetLayerCount()                                */
     667             : /************************************************************************/
     668             : 
     669           0 : int OGRMSSQLSpatialDataSource::GetLayerCount()
     670             : {
     671           0 :     return nLayers;
     672             : }
     673             : 
     674             : /************************************************************************/
     675             : /*                       ParseValue()                                   */
     676             : /************************************************************************/
     677             : 
     678         529 : int OGRMSSQLSpatialDataSource::ParseValue(char **pszValue, char *pszSource,
     679             :                                           const char *pszKey, int nStart,
     680             :                                           int nNext, int nTerm, int bRemove)
     681             : {
     682         529 :     int nLen = static_cast<int>(strlen(pszKey));
     683         529 :     if ((*pszValue) == nullptr && nStart + nLen < nNext &&
     684         185 :         EQUALN(pszSource + nStart, pszKey, nLen))
     685             :     {
     686           4 :         *pszValue =
     687           4 :             (char *)CPLMalloc(sizeof(char) * (nNext - nStart - nLen + 1));
     688           4 :         strncpy(*pszValue, pszSource + nStart + nLen, nNext - nStart - nLen);
     689           4 :         (*pszValue)[nNext - nStart - nLen] = 0;
     690             : 
     691           4 :         if (bRemove)
     692             :         {
     693             :             // remove the value from the source string
     694           0 :             if (pszSource[nNext] == ';')
     695           0 :                 memmove(pszSource + nStart, pszSource + nNext + 1,
     696           0 :                         nTerm - nNext);
     697             :             else
     698           0 :                 memmove(pszSource + nStart, pszSource + nNext,
     699           0 :                         nTerm - nNext + 1);
     700             :         }
     701           4 :         return TRUE;
     702             :     }
     703         525 :     return FALSE;
     704             : }
     705             : 
     706             : /************************************************************************/
     707             : /*                                Open()                                */
     708             : /************************************************************************/
     709             : 
     710           1 : int OGRMSSQLSpatialDataSource::Open(const char *pszNewName, bool bUpdate,
     711             :                                     int bTestOpen)
     712             : 
     713             : {
     714           1 :     CPLAssert(nLayers == 0);
     715             : 
     716           1 :     if (!STARTS_WITH_CI(pszNewName, "MSSQL:"))
     717             :     {
     718           0 :         if (!bTestOpen)
     719           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     720             :                      "%s does not conform to MSSSQLSpatial naming convention,"
     721             :                      " MSSQL:*\n",
     722             :                      pszNewName);
     723           0 :         return FALSE;
     724             :     }
     725             : 
     726             :     /* Determine if the connection string contains specific values */
     727           1 :     char *pszTableSpec = nullptr;
     728           1 :     char *pszGeometryFormat = nullptr;
     729           1 :     char *pszConnectionName = CPLStrdup(pszNewName + 6);
     730           1 :     char *pszDriver = nullptr;
     731           1 :     char *pszUID = nullptr;
     732           1 :     char *pszPWD = nullptr;
     733             :     int nCurrent, nNext, nTerm;
     734           1 :     nCurrent = nNext = nTerm = static_cast<int>(strlen(pszConnectionName));
     735             : 
     736          95 :     while (nCurrent > 0)
     737             :     {
     738          94 :         --nCurrent;
     739          94 :         if (pszConnectionName[nCurrent] == ';')
     740             :         {
     741           4 :             nNext = nCurrent;
     742           4 :             continue;
     743             :         }
     744             : 
     745          90 :         if (ParseValue(&pszCatalog, pszConnectionName, "database=", nCurrent,
     746          90 :                        nNext, nTerm, FALSE))
     747           1 :             continue;
     748             : 
     749          89 :         if (ParseValue(&pszTableSpec, pszConnectionName, "tables=", nCurrent,
     750          89 :                        nNext, nTerm, TRUE))
     751           0 :             continue;
     752             : 
     753          89 :         if (ParseValue(&pszDriver, pszConnectionName, "driver=", nCurrent,
     754          89 :                        nNext, nTerm, FALSE))
     755           1 :             continue;
     756             : 
     757          88 :         if (ParseValue(&pszUID, pszConnectionName, "uid=", nCurrent, nNext,
     758          88 :                        nTerm, FALSE))
     759           1 :             continue;
     760             : 
     761          87 :         if (ParseValue(&pszPWD, pszConnectionName, "pwd=", nCurrent, nNext,
     762          87 :                        nTerm, FALSE))
     763           1 :             continue;
     764             : 
     765          86 :         if (ParseValue(&pszGeometryFormat, pszConnectionName,
     766          86 :                        "geometryformat=", nCurrent, nNext, nTerm, TRUE))
     767             :         {
     768           0 :             if (STARTS_WITH_CI(pszGeometryFormat, "wkbzm"))
     769           0 :                 nGeometryFormat = MSSQLGEOMETRY_WKBZM;
     770           0 :             else if (STARTS_WITH_CI(pszGeometryFormat, "wkb"))
     771           0 :                 nGeometryFormat = MSSQLGEOMETRY_WKB;
     772           0 :             else if (STARTS_WITH_CI(pszGeometryFormat, "wkt"))
     773           0 :                 nGeometryFormat = MSSQLGEOMETRY_WKT;
     774           0 :             else if (STARTS_WITH_CI(pszGeometryFormat, "native"))
     775           0 :                 nGeometryFormat = MSSQLGEOMETRY_NATIVE;
     776             :             else
     777             :             {
     778           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     779             :                          "Invalid geometry type specified: %s,"
     780             :                          " MSSQL:*\n",
     781             :                          pszGeometryFormat);
     782             : 
     783           0 :                 CPLFree(pszTableSpec);
     784           0 :                 CPLFree(pszGeometryFormat);
     785           0 :                 CPLFree(pszConnectionName);
     786           0 :                 CPLFree(pszDriver);
     787           0 :                 CPLFree(pszUID);
     788           0 :                 CPLFree(pszPWD);
     789           0 :                 return FALSE;
     790             :             }
     791             : 
     792           0 :             CPLFree(pszGeometryFormat);
     793           0 :             pszGeometryFormat = nullptr;
     794           0 :             continue;
     795             :         }
     796             :     }
     797             : 
     798             :     /* Determine if the connection string contains the catalog portion */
     799           1 :     if (pszCatalog == nullptr)
     800             :     {
     801           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     802             :                  "'%s' does not contain the 'database' portion\n", pszNewName);
     803             : 
     804           0 :         CPLFree(pszTableSpec);
     805           0 :         CPLFree(pszGeometryFormat);
     806           0 :         CPLFree(pszConnectionName);
     807           0 :         CPLFree(pszDriver);
     808           0 :         CPLFree(pszUID);
     809           0 :         CPLFree(pszPWD);
     810           0 :         return FALSE;
     811             :     }
     812             : 
     813           1 :     pszName = CPLStrdup(pszNewName);
     814             : 
     815           1 :     char **papszTableNames = nullptr;
     816           1 :     char **papszSchemaNames = nullptr;
     817           1 :     char **papszGeomColumnNames = nullptr;
     818           1 :     char **papszCoordDimensions = nullptr;
     819           1 :     char **papszSRIds = nullptr;
     820           1 :     char **papszSRTexts = nullptr;
     821             : 
     822             :     /* Determine if the connection string contains the TABLES portion */
     823           1 :     if (pszTableSpec != nullptr)
     824             :     {
     825             :         char **papszTableList;
     826             :         int i;
     827             : 
     828           0 :         papszTableList = CSLTokenizeString2(pszTableSpec, ",", 0);
     829             : 
     830           0 :         for (i = 0; i < CSLCount(papszTableList); i++)
     831             :         {
     832             :             char **papszQualifiedParts;
     833             : 
     834             :             // Get schema and table name
     835           0 :             papszQualifiedParts = CSLTokenizeString2(papszTableList[i], ".", 0);
     836             : 
     837             :             /* Find the geometry column name if specified */
     838           0 :             if (CSLCount(papszQualifiedParts) >= 1)
     839             :             {
     840           0 :                 char *pszGeomColumnName = nullptr;
     841           0 :                 char *pos = strchr(
     842           0 :                     papszQualifiedParts[CSLCount(papszQualifiedParts) - 1],
     843             :                     '(');
     844           0 :                 if (pos != nullptr)
     845             :                 {
     846           0 :                     *pos = '\0';
     847           0 :                     pszGeomColumnName = pos + 1;
     848           0 :                     int len = static_cast<int>(strlen(pszGeomColumnName));
     849           0 :                     if (len > 0)
     850           0 :                         pszGeomColumnName[len - 1] = '\0';
     851             :                 }
     852             :                 papszGeomColumnNames =
     853           0 :                     CSLAddString(papszGeomColumnNames,
     854             :                                  pszGeomColumnName ? pszGeomColumnName : "");
     855             :             }
     856             : 
     857           0 :             if (CSLCount(papszQualifiedParts) == 2)
     858             :             {
     859             :                 papszSchemaNames =
     860           0 :                     CSLAddString(papszSchemaNames, papszQualifiedParts[0]);
     861             :                 papszTableNames =
     862           0 :                     CSLAddString(papszTableNames, papszQualifiedParts[1]);
     863             :             }
     864           0 :             else if (CSLCount(papszQualifiedParts) == 1)
     865             :             {
     866           0 :                 papszSchemaNames = CSLAddString(papszSchemaNames, "dbo");
     867             :                 papszTableNames =
     868           0 :                     CSLAddString(papszTableNames, papszQualifiedParts[0]);
     869             :             }
     870             : 
     871           0 :             CSLDestroy(papszQualifiedParts);
     872             :         }
     873             : 
     874           0 :         CSLDestroy(papszTableList);
     875             :     }
     876             : 
     877           1 :     CPLFree(pszTableSpec);
     878             : 
     879           1 :     if (pszDriver == nullptr)
     880             :     {
     881           0 :         char *pszConnectionName2 = pszConnectionName;
     882             : #if SQLNCLI_VERSION == 11
     883             :         pszDriver = CPLStrdup("{SQL Server Native Client 11.0}");
     884             : #elif SQLNCLI_VERSION == 10
     885             :         pszDriver = CPLStrdup("{SQL Server Native Client 10.0}");
     886             : #elif MSODBCSQL_VERSION == 13
     887             :         pszDriver = CPLStrdup("{ODBC Driver 13 for SQL Server}");
     888             : #elif MSODBCSQL_VERSION == 17
     889             :         pszDriver = CPLStrdup("{ODBC Driver 17 for SQL Server}");
     890             : #elif MSODBCSQL_VERSION == 18
     891             :         pszDriver = CPLStrdup("{ODBC Driver 18 for SQL Server}");
     892             : #else
     893           0 :         pszDriver = CPLStrdup("{SQL Server}");
     894             : #endif
     895           0 :         pszConnectionName = CPLStrdup(
     896             :             CPLSPrintf("DRIVER=%s;%s", pszDriver, pszConnectionName2));
     897           0 :         CPLFree(pszConnectionName2);
     898             :     }
     899             : 
     900           1 :     CPLFree(pszDriver);
     901             : 
     902           1 :     if (pszUID == nullptr)
     903             :     {
     904             :         const char *pszUIDConst =
     905           0 :             CPLGetConfigOption("MSSQLSPATIAL_UID", nullptr);
     906           0 :         if (pszUIDConst)
     907           0 :             pszUID = CPLStrdup(pszUIDConst);
     908             :     }
     909           1 :     if (pszUID != nullptr)
     910             :     {
     911           1 :         char *pszConnectionName2 = pszConnectionName;
     912             :         pszConnectionName =
     913           1 :             CPLStrdup(CPLSPrintf("%s;UID=%s", pszConnectionName2, pszUID));
     914           1 :         CPLFree(pszConnectionName2);
     915             :     }
     916           1 :     if (pszPWD == nullptr)
     917             :     {
     918             :         const char *pszPWDConst =
     919           0 :             CPLGetConfigOption("MSSQLSPATIAL_PWD", nullptr);
     920           0 :         if (pszPWDConst)
     921           0 :             pszPWD = CPLStrdup(pszPWDConst);
     922             :     }
     923           1 :     if (pszPWD != nullptr)
     924             :     {
     925           1 :         char *pszConnectionName2 = pszConnectionName;
     926             :         pszConnectionName =
     927           1 :             CPLStrdup(CPLSPrintf("%s;PWD=%s", pszConnectionName2, pszPWD));
     928           1 :         CPLFree(pszConnectionName2);
     929             :     }
     930             : 
     931           1 :     CPLFree(pszUID);
     932           1 :     CPLFree(pszPWD);
     933             : 
     934             :     /* Initialize the SQL Server connection. */
     935           1 :     if (!oSession.EstablishSession(pszConnectionName, "", ""))
     936             :     {
     937             :         /* Get a list of the available drivers */
     938             :         HENV hEnv;
     939           1 :         if (SQL_SUCCEEDED(SQLAllocEnv(&hEnv)))
     940             :         {
     941           2 :             CPLString osDriverList;
     942           1 :             SQLUSMALLINT direction = SQL_FETCH_FIRST;
     943             :             SQLSMALLINT driver_ret;
     944             :             SQLSMALLINT attr_ret;
     945             :             SQLCHAR attr[256];
     946             :             SQLCHAR driver[256];
     947           1 :             while (SQL_SUCCEEDED(SQLDrivers(hEnv, direction, driver,
     948             :                                             sizeof(driver), &driver_ret, attr,
     949             :                                             sizeof(attr), &attr_ret)))
     950             :             {
     951           0 :                 direction = SQL_FETCH_NEXT;
     952           0 :                 osDriverList += CPLSPrintf("%s\n", driver);
     953             :             }
     954             : 
     955           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     956             :                      "Unable to initialize connection to the server for %s,\n"
     957             :                      "%s\n"
     958             :                      "Try specifying the driver in the connection string from "
     959             :                      "the list of available drivers:\n"
     960             :                      "%s",
     961             :                      pszNewName, oSession.GetLastError(), osDriverList.c_str());
     962             :         }
     963             :         else
     964             :         {
     965           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     966             :                      "Unable to initialize connection to the server for %s,\n"
     967             :                      "%s\n",
     968             :                      pszNewName, oSession.GetLastError());
     969             :         }
     970             : 
     971           1 :         if (hEnv != nullptr)
     972           1 :             SQLFreeEnv(hEnv);
     973             : 
     974           1 :         CSLDestroy(papszTableNames);
     975           1 :         CSLDestroy(papszSchemaNames);
     976           1 :         CSLDestroy(papszGeomColumnNames);
     977           1 :         CSLDestroy(papszCoordDimensions);
     978           1 :         CSLDestroy(papszSRIds);
     979           1 :         CSLDestroy(papszSRTexts);
     980           1 :         CPLFree(pszGeometryFormat);
     981           1 :         CPLFree(pszConnectionName);
     982           1 :         return FALSE;
     983             :     }
     984             : 
     985             :     /* -------------------------------------------------------------------- */
     986             :     /*      Find out SQLServer version                                      */
     987             :     /* -------------------------------------------------------------------- */
     988             :     if (true)
     989             :     {
     990           0 :         sMSSQLVersion.nMajor = -1;
     991           0 :         sMSSQLVersion.nMinor = -1;
     992           0 :         sMSSQLVersion.nBuild = -1;
     993           0 :         sMSSQLVersion.nRevision = -1;
     994             : 
     995           0 :         CPLODBCStatement oStmt(&oSession);
     996             : 
     997             :         /* Use join to make sure the existence of the referred column/table */
     998           0 :         oStmt.Append(
     999             :             "SELECT SERVERPROPERTY('ProductVersion') AS ProductVersion;");
    1000             : 
    1001           0 :         if (oStmt.ExecuteSQL())
    1002             :         {
    1003           0 :             while (oStmt.Fetch())
    1004             :             {
    1005           0 :                 OGRMSSQLDecodeVersionString(&sMSSQLVersion,
    1006             :                                             oStmt.GetColData(0));
    1007             :             }
    1008             :         }
    1009             :     }
    1010             : 
    1011           0 :     char **papszTypes = nullptr;
    1012             : 
    1013             :     /* read metadata for the specified tables */
    1014           0 :     if (papszTableNames != nullptr && bUseGeometryColumns)
    1015             :     {
    1016           0 :         for (int iTable = 0; papszTableNames[iTable] != nullptr; iTable++)
    1017             :         {
    1018           0 :             CPLODBCStatement oStmt(&oSession);
    1019             : 
    1020             :             /* Use join to make sure the existence of the referred column/table
    1021             :              */
    1022           0 :             oStmt.Appendf(
    1023             :                 "SELECT f_geometry_column, coord_dimension, g.srid, srtext, "
    1024             :                 "geometry_type FROM dbo.geometry_columns g JOIN "
    1025             :                 "INFORMATION_SCHEMA.COLUMNS ON f_table_schema = TABLE_SCHEMA "
    1026             :                 "and f_table_name = TABLE_NAME and f_geometry_column = "
    1027             :                 "COLUMN_NAME left outer join dbo.spatial_ref_sys s on g.srid = "
    1028             :                 "s.srid WHERE f_table_schema = '%s' AND f_table_name = '%s'",
    1029           0 :                 papszSchemaNames[iTable], papszTableNames[iTable]);
    1030             : 
    1031           0 :             if (oStmt.ExecuteSQL())
    1032             :             {
    1033           0 :                 while (oStmt.Fetch())
    1034             :                 {
    1035           0 :                     if (papszGeomColumnNames == nullptr)
    1036           0 :                         papszGeomColumnNames = CSLAddString(
    1037             :                             papszGeomColumnNames, oStmt.GetColData(0));
    1038           0 :                     else if (*papszGeomColumnNames[iTable] == 0)
    1039             :                     {
    1040           0 :                         CPLFree(papszGeomColumnNames[iTable]);
    1041           0 :                         papszGeomColumnNames[iTable] =
    1042           0 :                             CPLStrdup(oStmt.GetColData(0));
    1043             :                     }
    1044             : 
    1045           0 :                     papszCoordDimensions = CSLAddString(
    1046             :                         papszCoordDimensions, oStmt.GetColData(1, "2"));
    1047             :                     papszSRIds =
    1048           0 :                         CSLAddString(papszSRIds, oStmt.GetColData(2, "0"));
    1049             :                     papszSRTexts =
    1050           0 :                         CSLAddString(papszSRTexts, oStmt.GetColData(3, ""));
    1051           0 :                     papszTypes = CSLAddString(papszTypes,
    1052             :                                               oStmt.GetColData(4, "GEOMETRY"));
    1053             :                 }
    1054             :             }
    1055             :             else
    1056             :             {
    1057             :                 /* probably the table is missing at all */
    1058           0 :                 InitializeMetadataTables();
    1059             :             }
    1060             :         }
    1061             :     }
    1062             : 
    1063             :     /* if requesting all user database table then this takes priority */
    1064           0 :     if (papszTableNames == nullptr && bListAllTables)
    1065             :     {
    1066           0 :         CPLODBCStatement oStmt(&oSession);
    1067             : 
    1068           0 :         oStmt.Append(
    1069             :             "select sys.schemas.name, sys.schemas.name + '.' + "
    1070             :             "sys.objects.name, sys.columns.name from sys.columns join "
    1071             :             "sys.types on sys.columns.system_type_id = "
    1072             :             "sys.types.system_type_id and sys.columns.user_type_id = "
    1073             :             "sys.types.user_type_id join sys.objects on sys.objects.object_id "
    1074             :             "= sys.columns.object_id join sys.schemas on sys.objects.schema_id "
    1075             :             "= sys.schemas.schema_id where (sys.types.name = 'geometry' or "
    1076             :             "sys.types.name = 'geography') and (sys.objects.type = 'U' or "
    1077             :             "sys.objects.type = 'V') union all select sys.schemas.name, "
    1078             :             "sys.schemas.name + '.' + sys.objects.name, '' from sys.objects "
    1079             :             "join sys.schemas on sys.objects.schema_id = sys.schemas.schema_id "
    1080             :             "where not exists (select * from sys.columns sc1 join sys.types on "
    1081             :             "sc1.system_type_id = sys.types.system_type_id where "
    1082             :             "(sys.types.name = 'geometry' or sys.types.name = 'geography') and "
    1083             :             "sys.objects.object_id = sc1.object_id) and (sys.objects.type = "
    1084             :             "'U' or sys.objects.type = 'V')");
    1085             : 
    1086           0 :         if (oStmt.ExecuteSQL())
    1087             :         {
    1088           0 :             while (oStmt.Fetch())
    1089             :             {
    1090             :                 papszSchemaNames =
    1091           0 :                     CSLAddString(papszSchemaNames, oStmt.GetColData(0));
    1092             :                 papszTableNames =
    1093           0 :                     CSLAddString(papszTableNames, oStmt.GetColData(1));
    1094             :                 papszGeomColumnNames =
    1095           0 :                     CSLAddString(papszGeomColumnNames, oStmt.GetColData(2));
    1096             :             }
    1097             :         }
    1098             :     }
    1099             : 
    1100             :     /* Determine the available tables if not specified. */
    1101           0 :     if (papszTableNames == nullptr && bUseGeometryColumns)
    1102             :     {
    1103           0 :         CPLODBCStatement oStmt(&oSession);
    1104             : 
    1105             :         /* Use join to make sure the existence of the referred column/table */
    1106           0 :         oStmt.Append("SELECT f_table_schema, f_table_name, f_geometry_column, "
    1107             :                      "coord_dimension, g.srid, srtext, geometry_type FROM "
    1108             :                      "dbo.geometry_columns g JOIN INFORMATION_SCHEMA.COLUMNS "
    1109             :                      "ON f_table_schema = TABLE_SCHEMA and f_table_name = "
    1110             :                      "TABLE_NAME and f_geometry_column = COLUMN_NAME left "
    1111             :                      "outer join dbo.spatial_ref_sys s on g.srid = s.srid");
    1112             : 
    1113           0 :         if (oStmt.ExecuteSQL())
    1114             :         {
    1115           0 :             while (oStmt.Fetch())
    1116             :             {
    1117             :                 papszSchemaNames =
    1118           0 :                     CSLAddString(papszSchemaNames, oStmt.GetColData(0, "dbo"));
    1119             :                 papszTableNames =
    1120           0 :                     CSLAddString(papszTableNames, oStmt.GetColData(1));
    1121             :                 papszGeomColumnNames =
    1122           0 :                     CSLAddString(papszGeomColumnNames, oStmt.GetColData(2));
    1123           0 :                 papszCoordDimensions = CSLAddString(papszCoordDimensions,
    1124             :                                                     oStmt.GetColData(3, "2"));
    1125           0 :                 papszSRIds = CSLAddString(papszSRIds, oStmt.GetColData(4, "0"));
    1126             :                 papszSRTexts =
    1127           0 :                     CSLAddString(papszSRTexts, oStmt.GetColData(5, ""));
    1128             :                 papszTypes =
    1129           0 :                     CSLAddString(papszTypes, oStmt.GetColData(6, "GEOMETRY"));
    1130             :             }
    1131             :         }
    1132             :         else
    1133             :         {
    1134             :             /* probably the table is missing at all */
    1135           0 :             InitializeMetadataTables();
    1136             :         }
    1137             :     }
    1138             : 
    1139             :     /* Query catalog for tables having geometry columns */
    1140           0 :     if (papszTableNames == nullptr)
    1141             :     {
    1142           0 :         CPLODBCStatement oStmt(&oSession);
    1143             : 
    1144           0 :         oStmt.Append(
    1145             :             "SELECT sys.schemas.name, sys.schemas.name + '.' + "
    1146             :             "sys.objects.name, sys.columns.name from sys.columns join "
    1147             :             "sys.types on sys.columns.system_type_id = "
    1148             :             "sys.types.system_type_id and sys.columns.user_type_id = "
    1149             :             "sys.types.user_type_id join sys.objects on sys.objects.object_id "
    1150             :             "= sys.columns.object_id join sys.schemas on sys.objects.schema_id "
    1151             :             "= sys.schemas.schema_id where (sys.types.name = 'geometry' or "
    1152             :             "sys.types.name = 'geography') and (sys.objects.type = 'U' or "
    1153             :             "sys.objects.type = 'V')");
    1154             : 
    1155           0 :         if (oStmt.ExecuteSQL())
    1156             :         {
    1157           0 :             while (oStmt.Fetch())
    1158             :             {
    1159             :                 papszSchemaNames =
    1160           0 :                     CSLAddString(papszSchemaNames, oStmt.GetColData(0));
    1161             :                 papszTableNames =
    1162           0 :                     CSLAddString(papszTableNames, oStmt.GetColData(1));
    1163             :                 papszGeomColumnNames =
    1164           0 :                     CSLAddString(papszGeomColumnNames, oStmt.GetColData(2));
    1165             :             }
    1166             :         }
    1167             :     }
    1168             : 
    1169             :     int nSRId, nCoordDimension;
    1170             :     OGRwkbGeometryType eType;
    1171             : 
    1172           0 :     for (int iTable = 0;
    1173           0 :          papszTableNames != nullptr && papszTableNames[iTable] != nullptr;
    1174             :          iTable++)
    1175             :     {
    1176           0 :         if (papszSRIds != nullptr)
    1177           0 :             nSRId = atoi(papszSRIds[iTable]);
    1178             :         else
    1179           0 :             nSRId = 0;
    1180             : 
    1181           0 :         if (papszCoordDimensions != nullptr)
    1182           0 :             nCoordDimension = atoi(papszCoordDimensions[iTable]);
    1183             :         else
    1184           0 :             nCoordDimension = 2;
    1185             : 
    1186           0 :         if (papszTypes != nullptr)
    1187           0 :             eType = OGRFromOGCGeomType(papszTypes[iTable]);
    1188             :         else
    1189           0 :             eType = wkbUnknown;
    1190             : 
    1191           0 :         CPLAssert(papszGeomColumnNames && papszGeomColumnNames[iTable]);
    1192           0 :         if (strlen(papszGeomColumnNames[iTable]) > 0)
    1193           0 :             OpenTable(papszSchemaNames[iTable], papszTableNames[iTable],
    1194           0 :                       papszGeomColumnNames[iTable], nCoordDimension, nSRId,
    1195           0 :                       papszSRTexts ? papszSRTexts[iTable] : nullptr, eType,
    1196             :                       bUpdate);
    1197             :         else
    1198           0 :             OpenTable(papszSchemaNames[iTable], papszTableNames[iTable],
    1199             :                       nullptr, nCoordDimension, nSRId,
    1200           0 :                       papszSRTexts ? papszSRTexts[iTable] : nullptr, wkbNone,
    1201             :                       bUpdate);
    1202             :     }
    1203             : 
    1204           0 :     CSLDestroy(papszTableNames);
    1205           0 :     CSLDestroy(papszSchemaNames);
    1206           0 :     CSLDestroy(papszGeomColumnNames);
    1207           0 :     CSLDestroy(papszCoordDimensions);
    1208           0 :     CSLDestroy(papszSRIds);
    1209           0 :     CSLDestroy(papszSRTexts);
    1210           0 :     CSLDestroy(papszTypes);
    1211             : 
    1212           0 :     CPLFree(pszGeometryFormat);
    1213             : 
    1214           0 :     CPLFree(pszConnection);
    1215           0 :     pszConnection = pszConnectionName;
    1216             : 
    1217           0 :     bDSUpdate = bUpdate;
    1218             : 
    1219           0 :     return TRUE;
    1220             : }
    1221             : 
    1222             : /************************************************************************/
    1223             : /*                             ExecuteSQL()                             */
    1224             : /************************************************************************/
    1225             : 
    1226           0 : OGRLayer *OGRMSSQLSpatialDataSource::ExecuteSQL(const char *pszSQLCommand,
    1227             :                                                 OGRGeometry *poSpatialFilter,
    1228             :                                                 const char *pszDialect)
    1229             : 
    1230             : {
    1231             :     /* -------------------------------------------------------------------- */
    1232             :     /*      Use generic implementation for recognized dialects              */
    1233             :     /* -------------------------------------------------------------------- */
    1234           0 :     if (IsGenericSQLDialect(pszDialect))
    1235           0 :         return OGRDataSource::ExecuteSQL(pszSQLCommand, poSpatialFilter,
    1236           0 :                                          pszDialect);
    1237             : 
    1238             :     /* -------------------------------------------------------------------- */
    1239             :     /*      Special case DELLAYER: command.                                 */
    1240             :     /* -------------------------------------------------------------------- */
    1241           0 :     if (STARTS_WITH_CI(pszSQLCommand, "DELLAYER:"))
    1242             :     {
    1243           0 :         const char *pszLayerName = pszSQLCommand + 9;
    1244             : 
    1245           0 :         while (*pszLayerName == ' ')
    1246           0 :             pszLayerName++;
    1247             : 
    1248           0 :         OGRLayer *poLayer = GetLayerByName(pszLayerName);
    1249             : 
    1250           0 :         for (int iLayer = 0; iLayer < nLayers; iLayer++)
    1251             :         {
    1252           0 :             if (papoLayers[iLayer] == poLayer)
    1253             :             {
    1254           0 :                 DeleteLayer(iLayer);
    1255           0 :                 break;
    1256             :             }
    1257             :         }
    1258           0 :         return nullptr;
    1259             :     }
    1260             : 
    1261           0 :     CPLDebug("MSSQLSpatial", "ExecuteSQL(%s) called.", pszSQLCommand);
    1262             : 
    1263           0 :     if (STARTS_WITH_CI(pszSQLCommand, "DROP SPATIAL INDEX ON "))
    1264             :     {
    1265             :         /* Handle command to drop a spatial index. */
    1266             :         OGRMSSQLSpatialTableLayer *poLayer =
    1267           0 :             new OGRMSSQLSpatialTableLayer(this);
    1268             : 
    1269           0 :         if (poLayer)
    1270             :         {
    1271           0 :             if (poLayer->Initialize(nullptr, pszSQLCommand + 22, nullptr, 0, 0,
    1272           0 :                                     nullptr, wkbUnknown) != CE_None)
    1273             :             {
    1274           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1275             :                          "Failed to initialize layer '%s'", pszSQLCommand + 22);
    1276             :             }
    1277           0 :             poLayer->DropSpatialIndex();
    1278           0 :             delete poLayer;
    1279             :         }
    1280           0 :         return nullptr;
    1281             :     }
    1282           0 :     else if (STARTS_WITH_CI(pszSQLCommand, "CREATE SPATIAL INDEX ON "))
    1283             :     {
    1284             :         /* Handle command to create a spatial index. */
    1285             :         OGRMSSQLSpatialTableLayer *poLayer =
    1286           0 :             new OGRMSSQLSpatialTableLayer(this);
    1287             : 
    1288           0 :         if (poLayer)
    1289             :         {
    1290           0 :             if (poLayer->Initialize(nullptr, pszSQLCommand + 24, nullptr, 0, 0,
    1291           0 :                                     nullptr, wkbUnknown) != CE_None)
    1292             :             {
    1293           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1294             :                          "Failed to initialize layer '%s'", pszSQLCommand + 24);
    1295             :             }
    1296           0 :             poLayer->CreateSpatialIndex();
    1297           0 :             delete poLayer;
    1298             :         }
    1299           0 :         return nullptr;
    1300             :     }
    1301             : 
    1302             :     /* Execute the command natively */
    1303           0 :     CPLODBCStatement *poStmt = new CPLODBCStatement(&oSession);
    1304           0 :     poStmt->Append(pszSQLCommand);
    1305             : 
    1306           0 :     if (!poStmt->ExecuteSQL())
    1307             :     {
    1308           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", oSession.GetLastError());
    1309           0 :         delete poStmt;
    1310           0 :         return nullptr;
    1311             :     }
    1312             : 
    1313             :     /* -------------------------------------------------------------------- */
    1314             :     /*      Are there result columns for this statement?                    */
    1315             :     /* -------------------------------------------------------------------- */
    1316           0 :     if (poStmt->GetColCount() == 0)
    1317             :     {
    1318           0 :         delete poStmt;
    1319           0 :         CPLErrorReset();
    1320           0 :         return nullptr;
    1321             :     }
    1322             : 
    1323             :     /* -------------------------------------------------------------------- */
    1324             :     /*      Create a results layer.  It will take ownership of the          */
    1325             :     /*      statement.                                                      */
    1326             :     /* -------------------------------------------------------------------- */
    1327             : 
    1328             :     OGRMSSQLSpatialSelectLayer *poLayer =
    1329           0 :         new OGRMSSQLSpatialSelectLayer(this, poStmt);
    1330             : 
    1331           0 :     if (poSpatialFilter != nullptr)
    1332           0 :         poLayer->SetSpatialFilter(poSpatialFilter);
    1333             : 
    1334           0 :     return poLayer;
    1335             : }
    1336             : 
    1337             : /************************************************************************/
    1338             : /*                          ReleaseResultSet()                          */
    1339             : /************************************************************************/
    1340             : 
    1341           0 : void OGRMSSQLSpatialDataSource::ReleaseResultSet(OGRLayer *poLayer)
    1342             : 
    1343             : {
    1344           0 :     delete poLayer;
    1345           0 : }
    1346             : 
    1347             : /************************************************************************/
    1348             : /*                            LaunderName()                             */
    1349             : /************************************************************************/
    1350             : 
    1351           0 : char *OGRMSSQLSpatialDataSource::LaunderName(const char *pszSrcName)
    1352             : 
    1353             : {
    1354           0 :     char *pszSafeName = CPLStrdup(pszSrcName);
    1355             :     int i;
    1356             : 
    1357           0 :     for (i = 0; pszSafeName[i] != '\0'; i++)
    1358             :     {
    1359           0 :         pszSafeName[i] =
    1360           0 :             (char)CPLTolower(static_cast<unsigned char>(pszSafeName[i]));
    1361           0 :         if (pszSafeName[i] == '-' || pszSafeName[i] == '#')
    1362           0 :             pszSafeName[i] = '_';
    1363             :     }
    1364             : 
    1365           0 :     return pszSafeName;
    1366             : }
    1367             : 
    1368             : /************************************************************************/
    1369             : /*                      InitializeMetadataTables()                      */
    1370             : /*                                                                      */
    1371             : /*      Create the metadata tables (SPATIAL_REF_SYS and                 */
    1372             : /*      GEOMETRY_COLUMNS).                                              */
    1373             : /************************************************************************/
    1374             : 
    1375           0 : OGRErr OGRMSSQLSpatialDataSource::InitializeMetadataTables()
    1376             : 
    1377             : {
    1378           0 :     if (bUseGeometryColumns)
    1379             :     {
    1380           0 :         CPLODBCStatement oStmt(&oSession);
    1381             : 
    1382           0 :         oStmt.Append(
    1383             :             "IF NOT EXISTS (SELECT * FROM sys.objects WHERE "
    1384             :             "object_id = OBJECT_ID(N'[dbo].[geometry_columns]') AND type in "
    1385             :             "(N'U')) "
    1386             :             "CREATE TABLE geometry_columns (f_table_catalog varchar(128) not "
    1387             :             "null, "
    1388             :             "f_table_schema varchar(128) not null, f_table_name varchar(256) "
    1389             :             "not null, "
    1390             :             "f_geometry_column varchar(256) not null, coord_dimension integer "
    1391             :             "not null, "
    1392             :             "srid integer not null, geometry_type varchar(30) not null, "
    1393             :             "CONSTRAINT geometry_columns_pk PRIMARY KEY (f_table_catalog, "
    1394             :             "f_table_schema, f_table_name, f_geometry_column));\n");
    1395             : 
    1396           0 :         oStmt.Append("IF NOT EXISTS (SELECT * FROM sys.objects "
    1397             :                      "WHERE object_id = OBJECT_ID(N'[dbo].[spatial_ref_sys]') "
    1398             :                      "AND type in (N'U')) "
    1399             :                      "CREATE TABLE spatial_ref_sys (srid integer not null "
    1400             :                      "PRIMARY KEY, auth_name varchar(256), auth_srid integer, "
    1401             :                      "srtext varchar(2048), proj4text varchar(2048))");
    1402             : 
    1403           0 :         int bInTransaction = oSession.IsInTransaction();
    1404           0 :         if (!bInTransaction)
    1405           0 :             oSession.BeginTransaction();
    1406             : 
    1407           0 :         if (!oStmt.ExecuteSQL())
    1408             :         {
    1409           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1410             :                      "Error initializing the metadata tables : %s",
    1411             :                      GetSession()->GetLastError());
    1412             : 
    1413           0 :             if (!bInTransaction)
    1414           0 :                 oSession.RollbackTransaction();
    1415             : 
    1416           0 :             return OGRERR_FAILURE;
    1417             :         }
    1418             : 
    1419           0 :         if (!bInTransaction)
    1420           0 :             oSession.CommitTransaction();
    1421             :     }
    1422             : 
    1423           0 :     return OGRERR_NONE;
    1424             : }
    1425             : 
    1426             : /************************************************************************/
    1427             : /*                              FetchSRS()                              */
    1428             : /*                                                                      */
    1429             : /*      Return a SRS corresponding to a particular id.  Note that       */
    1430             : /*      reference counting should be honoured on the returned           */
    1431             : /*      OGRSpatialReference, as handles may be cached.                  */
    1432             : /************************************************************************/
    1433             : 
    1434           0 : OGRSpatialReference *OGRMSSQLSpatialDataSource::FetchSRS(int nId)
    1435             : 
    1436             : {
    1437           0 :     if (nId <= 0)
    1438           0 :         return nullptr;
    1439             : 
    1440             :     /* -------------------------------------------------------------------- */
    1441             :     /*      First, we look through our SRID cache, is it there?             */
    1442             :     /* -------------------------------------------------------------------- */
    1443           0 :     auto oIter = m_oSRSCache.find(nId);
    1444           0 :     if (oIter != m_oSRSCache.end())
    1445             :     {
    1446           0 :         return oIter->second.get();
    1447             :     }
    1448             : 
    1449           0 :     EndCopy();
    1450             : 
    1451             :     /* -------------------------------------------------------------------- */
    1452             :     /*      Try looking up in spatial_ref_sys table                         */
    1453             :     /* -------------------------------------------------------------------- */
    1454           0 :     if (bUseGeometryColumns)
    1455             :     {
    1456           0 :         CPLODBCStatement oStmt(GetSession());
    1457           0 :         oStmt.Appendf("SELECT srtext FROM spatial_ref_sys WHERE srid = %d",
    1458             :                       nId);
    1459             : 
    1460           0 :         if (oStmt.ExecuteSQL() && oStmt.Fetch())
    1461             :         {
    1462           0 :             if (oStmt.GetColData(0))
    1463             :             {
    1464             :                 auto poSRS = std::unique_ptr<OGRSpatialReference,
    1465             :                                              OGRSpatialReferenceReleaser>(
    1466           0 :                     new OGRSpatialReference());
    1467           0 :                 poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1468           0 :                 const char *pszWKT = oStmt.GetColData(0);
    1469           0 :                 if (poSRS->importFromWkt(pszWKT) == OGRERR_NONE)
    1470             :                 {
    1471             :                     const char *pszAuthorityName =
    1472           0 :                         poSRS->GetAuthorityName(nullptr);
    1473             :                     const char *pszAuthorityCode =
    1474           0 :                         poSRS->GetAuthorityCode(nullptr);
    1475           0 :                     if (pszAuthorityName && pszAuthorityCode &&
    1476           0 :                         EQUAL(pszAuthorityName, "EPSG"))
    1477             :                     {
    1478           0 :                         const int nCode = atoi(pszAuthorityCode);
    1479           0 :                         poSRS->Clear();
    1480           0 :                         poSRS->importFromEPSG(nCode);
    1481             :                     }
    1482             : 
    1483           0 :                     return AddSRIDToCache(nId, std::move(poSRS));
    1484             :                 }
    1485             :             }
    1486             :         }
    1487             :     }
    1488             : 
    1489             :     /* -------------------------------------------------------------------- */
    1490             :     /*      Try looking up the EPSG list                                    */
    1491             :     /* -------------------------------------------------------------------- */
    1492             :     auto poSRS =
    1493             :         std::unique_ptr<OGRSpatialReference, OGRSpatialReferenceReleaser>(
    1494           0 :             new OGRSpatialReference());
    1495           0 :     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1496           0 :     if (poSRS->importFromEPSG(nId) == OGRERR_NONE)
    1497             :     {
    1498           0 :         return AddSRIDToCache(nId, std::move(poSRS));
    1499             :     }
    1500             : 
    1501           0 :     return nullptr;
    1502             : }
    1503             : 
    1504             : /************************************************************************/
    1505             : /*                         AddSRIDToCache()                             */
    1506             : /*                                                                      */
    1507             : /*      Note: this will not add a reference on the poSRS object. Make   */
    1508             : /*      sure it is freshly created, or add a reference yourself if not. */
    1509             : /************************************************************************/
    1510             : 
    1511           0 : OGRSpatialReference *OGRMSSQLSpatialDataSource::AddSRIDToCache(
    1512             :     int nId,
    1513             :     std::unique_ptr<OGRSpatialReference, OGRSpatialReferenceReleaser> &&poSRS)
    1514             : {
    1515             :     /* -------------------------------------------------------------------- */
    1516             :     /*      Add to the cache.                                               */
    1517             :     /* -------------------------------------------------------------------- */
    1518           0 :     auto oIter = m_oSRSCache.emplace(nId, std::move(poSRS)).first;
    1519           0 :     return oIter->second.get();
    1520             : }
    1521             : 
    1522             : /************************************************************************/
    1523             : /*                             FetchSRSId()                             */
    1524             : /*                                                                      */
    1525             : /*      Fetch the id corresponding to an SRS, and if not found, add     */
    1526             : /*      it to the table.                                                */
    1527             : /************************************************************************/
    1528             : 
    1529           0 : int OGRMSSQLSpatialDataSource::FetchSRSId(const OGRSpatialReference *poSRS)
    1530             : 
    1531             : {
    1532           0 :     char *pszWKT = nullptr;
    1533           0 :     int nSRSId = 0;
    1534             :     const char *pszAuthorityName;
    1535             : 
    1536           0 :     if (poSRS == nullptr)
    1537           0 :         return 0;
    1538             :     /* -------------------------------------------------------------------- */
    1539             :     /*      First, we look through our SRID cache, is it there?             */
    1540             :     /* -------------------------------------------------------------------- */
    1541           0 :     for (const auto &pair : m_oSRSCache)
    1542             :     {
    1543           0 :         if (pair.second.get() == poSRS)
    1544           0 :             return pair.first;
    1545             :     }
    1546           0 :     for (const auto &pair : m_oSRSCache)
    1547             :     {
    1548           0 :         if (pair.second != nullptr && pair.second->IsSame(poSRS))
    1549           0 :             return pair.first;
    1550             :     }
    1551             : 
    1552           0 :     OGRSpatialReference oSRS(*poSRS);
    1553             :     // cppcheck-suppress uselessAssignmentPtrArg
    1554           0 :     poSRS = nullptr;
    1555             : 
    1556           0 :     pszAuthorityName = oSRS.GetAuthorityName(nullptr);
    1557             : 
    1558           0 :     if (pszAuthorityName == nullptr || strlen(pszAuthorityName) == 0)
    1559             :     {
    1560             :         /* --------------------------------------------------------------------
    1561             :          */
    1562             :         /*      Try to identify an EPSG code */
    1563             :         /* --------------------------------------------------------------------
    1564             :          */
    1565           0 :         oSRS.AutoIdentifyEPSG();
    1566             : 
    1567           0 :         pszAuthorityName = oSRS.GetAuthorityName(nullptr);
    1568           0 :         if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
    1569             :         {
    1570           0 :             const char *pszAuthorityCode = oSRS.GetAuthorityCode(nullptr);
    1571           0 :             if (pszAuthorityCode != nullptr && strlen(pszAuthorityCode) > 0)
    1572             :             {
    1573             :                 /* Import 'clean' SRS */
    1574           0 :                 oSRS.importFromEPSG(atoi(pszAuthorityCode));
    1575             : 
    1576           0 :                 pszAuthorityName = oSRS.GetAuthorityName(nullptr);
    1577             :             }
    1578             :         }
    1579             :     }
    1580             :     /* -------------------------------------------------------------------- */
    1581             :     /*      Check whether the EPSG authority code is already mapped to a    */
    1582             :     /*      SRS ID.                                                         */
    1583             :     /* -------------------------------------------------------------------- */
    1584           0 :     int nAuthorityCode = 0;
    1585           0 :     if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
    1586             :     {
    1587             :         /* For the root authority name 'EPSG', the authority code
    1588             :          * should always be integral
    1589             :          */
    1590           0 :         nAuthorityCode = atoi(oSRS.GetAuthorityCode(nullptr));
    1591             : 
    1592           0 :         CPLODBCStatement oStmt(&oSession);
    1593           0 :         oStmt.Appendf("SELECT srid FROM spatial_ref_sys WHERE "
    1594             :                       "auth_name = '%s' AND auth_srid = %d",
    1595             :                       pszAuthorityName, nAuthorityCode);
    1596             : 
    1597           0 :         if (oStmt.ExecuteSQL() && oStmt.Fetch() && oStmt.GetColData(0))
    1598             :         {
    1599           0 :             nSRSId = atoi(oStmt.GetColData(0));
    1600           0 :             if (nSRSId != 0)
    1601             :             {
    1602             :                 std::unique_ptr<OGRSpatialReference,
    1603             :                                 OGRSpatialReferenceReleaser>
    1604           0 :                     poCachedSRS(new OGRSpatialReference(oSRS));
    1605           0 :                 poCachedSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1606           0 :                 AddSRIDToCache(nSRSId, std::move(poCachedSRS));
    1607             :             }
    1608           0 :             return nSRSId;
    1609             :         }
    1610             :     }
    1611             : 
    1612             :     /* -------------------------------------------------------------------- */
    1613             :     /*      Translate SRS to WKT.                                           */
    1614             :     /* -------------------------------------------------------------------- */
    1615           0 :     if (oSRS.exportToWkt(&pszWKT) != OGRERR_NONE)
    1616             :     {
    1617           0 :         CPLFree(pszWKT);
    1618           0 :         return 0;
    1619             :     }
    1620             : 
    1621             :     /* -------------------------------------------------------------------- */
    1622             :     /*      Try to find in the existing table.                              */
    1623             :     /* -------------------------------------------------------------------- */
    1624           0 :     CPLODBCStatement oStmt(&oSession);
    1625             : 
    1626           0 :     oStmt.Append("SELECT srid FROM spatial_ref_sys WHERE srtext = ");
    1627           0 :     OGRMSSQLAppendEscaped(&oStmt, pszWKT);
    1628             : 
    1629             :     /* -------------------------------------------------------------------- */
    1630             :     /*      We got it!  Return it.                                          */
    1631             :     /* -------------------------------------------------------------------- */
    1632           0 :     if (oStmt.ExecuteSQL())
    1633             :     {
    1634           0 :         if (oStmt.Fetch() && oStmt.GetColData(0))
    1635             :         {
    1636           0 :             nSRSId = atoi(oStmt.GetColData(0));
    1637           0 :             CPLFree(pszWKT);
    1638           0 :             return nSRSId;
    1639             :         }
    1640             :     }
    1641             :     else
    1642             :     {
    1643             :         /* probably the table is missing at all */
    1644           0 :         if (InitializeMetadataTables() != OGRERR_NONE)
    1645             :         {
    1646           0 :             CPLFree(pszWKT);
    1647           0 :             return 0;
    1648             :         }
    1649             :     }
    1650             : 
    1651             :     /* -------------------------------------------------------------------- */
    1652             :     /*      Try adding the SRS to the SRS table.                            */
    1653             :     /* -------------------------------------------------------------------- */
    1654           0 :     char *pszProj4 = nullptr;
    1655           0 :     if (oSRS.exportToProj4(&pszProj4) != OGRERR_NONE)
    1656             :     {
    1657           0 :         CPLFree(pszProj4);
    1658           0 :         CPLFree(pszWKT);
    1659           0 :         return 0;
    1660             :     }
    1661             : 
    1662             :     /* -------------------------------------------------------------------- */
    1663             :     /*      Check whether the auth_code can be used as srid.                */
    1664             :     /* -------------------------------------------------------------------- */
    1665           0 :     nSRSId = nAuthorityCode;
    1666             : 
    1667           0 :     oStmt.Clear();
    1668             : 
    1669           0 :     int bInTransaction = oSession.IsInTransaction();
    1670           0 :     if (!bInTransaction)
    1671           0 :         oSession.BeginTransaction();
    1672             : 
    1673           0 :     if (nAuthorityCode > 0)
    1674             :     {
    1675           0 :         oStmt.Appendf("SELECT srid FROM spatial_ref_sys where srid = %d",
    1676             :                       nAuthorityCode);
    1677           0 :         if (oStmt.ExecuteSQL() && oStmt.Fetch())
    1678             :         {
    1679           0 :             nSRSId = 0;
    1680             :         }
    1681             :     }
    1682             : 
    1683             :     /* -------------------------------------------------------------------- */
    1684             :     /*      Get the current maximum srid in the srs table.                  */
    1685             :     /* -------------------------------------------------------------------- */
    1686             : 
    1687           0 :     if (nSRSId == 0)
    1688             :     {
    1689           0 :         oStmt.Clear();
    1690           0 :         oStmt.Append("SELECT COALESCE(MAX(srid) + 1, 32768) FROM "
    1691             :                      "spatial_ref_sys where srid between 32768 and 65536");
    1692             : 
    1693           0 :         if (oStmt.ExecuteSQL() && oStmt.Fetch() && oStmt.GetColData(0))
    1694             :         {
    1695           0 :             nSRSId = atoi(oStmt.GetColData(0));
    1696             :         }
    1697             :     }
    1698             : 
    1699           0 :     if (nSRSId == 0)
    1700             :     {
    1701             :         /* unable to allocate srid */
    1702           0 :         if (!bInTransaction)
    1703           0 :             oSession.RollbackTransaction();
    1704           0 :         CPLFree(pszProj4);
    1705           0 :         CPLFree(pszWKT);
    1706           0 :         return 0;
    1707             :     }
    1708             : 
    1709           0 :     oStmt.Clear();
    1710           0 :     if (nAuthorityCode > 0)
    1711             :     {
    1712           0 :         oStmt.Appendf("INSERT INTO spatial_ref_sys (srid, auth_srid, "
    1713             :                       "auth_name, srtext, proj4text) "
    1714             :                       "VALUES (%d, %d, ",
    1715             :                       nSRSId, nAuthorityCode);
    1716           0 :         OGRMSSQLAppendEscaped(&oStmt, pszAuthorityName);
    1717           0 :         oStmt.Append(", ");
    1718           0 :         OGRMSSQLAppendEscaped(&oStmt, pszWKT);
    1719           0 :         oStmt.Append(", ");
    1720           0 :         OGRMSSQLAppendEscaped(&oStmt, pszProj4);
    1721           0 :         oStmt.Append(")");
    1722             :     }
    1723             :     else
    1724             :     {
    1725           0 :         oStmt.Appendf(
    1726             :             "INSERT INTO spatial_ref_sys (srid,srtext,proj4text) VALUES (%d, ",
    1727             :             nSRSId);
    1728           0 :         OGRMSSQLAppendEscaped(&oStmt, pszWKT);
    1729           0 :         oStmt.Append(", ");
    1730           0 :         OGRMSSQLAppendEscaped(&oStmt, pszProj4);
    1731           0 :         oStmt.Append(")");
    1732             :     }
    1733             : 
    1734             :     /* Free everything that was allocated. */
    1735           0 :     CPLFree(pszProj4);
    1736           0 :     CPLFree(pszWKT);
    1737             : 
    1738           0 :     if (oStmt.ExecuteSQL())
    1739             :     {
    1740           0 :         if (!bInTransaction)
    1741           0 :             oSession.CommitTransaction();
    1742             :     }
    1743             :     else
    1744             :     {
    1745           0 :         if (!bInTransaction)
    1746           0 :             oSession.RollbackTransaction();
    1747             :     }
    1748             : 
    1749           0 :     return nSRSId;
    1750             : }
    1751             : 
    1752             : /************************************************************************/
    1753             : /*                         StartTransaction()                           */
    1754             : /*                                                                      */
    1755             : /* Should only be called by user code. Not driver internals.            */
    1756             : /************************************************************************/
    1757             : 
    1758           0 : OGRErr OGRMSSQLSpatialDataSource::StartTransaction(CPL_UNUSED int bForce)
    1759             : {
    1760           0 :     if (!oSession.BeginTransaction())
    1761             :     {
    1762           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Failed to start transaction: %s",
    1763             :                  oSession.GetLastError());
    1764           0 :         return OGRERR_FAILURE;
    1765             :     }
    1766             : 
    1767           0 :     return OGRERR_NONE;
    1768             : }
    1769             : 
    1770             : /************************************************************************/
    1771             : /*                         CommitTransaction()                          */
    1772             : /*                                                                      */
    1773             : /* Should only be called by user code. Not driver internals.            */
    1774             : /************************************************************************/
    1775             : 
    1776           0 : OGRErr OGRMSSQLSpatialDataSource::CommitTransaction()
    1777             : {
    1778           0 :     if (!oSession.CommitTransaction())
    1779             :     {
    1780           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1781             :                  "Failed to commit transaction: %s", oSession.GetLastError());
    1782             : 
    1783           0 :         for (int iLayer = 0; iLayer < nLayers; iLayer++)
    1784             :         {
    1785           0 :             if (papoLayers[iLayer]->GetLayerStatus() ==
    1786             :                 MSSQLLAYERSTATUS_INITIAL)
    1787           0 :                 papoLayers[iLayer]->SetLayerStatus(MSSQLLAYERSTATUS_DISABLED);
    1788             :         }
    1789           0 :         return OGRERR_FAILURE;
    1790             :     }
    1791             : 
    1792             :     /* set the status for the newly created layers */
    1793           0 :     for (int iLayer = 0; iLayer < nLayers; iLayer++)
    1794             :     {
    1795           0 :         if (papoLayers[iLayer]->GetLayerStatus() == MSSQLLAYERSTATUS_INITIAL)
    1796           0 :             papoLayers[iLayer]->SetLayerStatus(MSSQLLAYERSTATUS_CREATED);
    1797             :     }
    1798             : 
    1799           0 :     return OGRERR_NONE;
    1800             : }
    1801             : 
    1802             : /************************************************************************/
    1803             : /*                        RollbackTransaction()                         */
    1804             : /*                                                                      */
    1805             : /* Should only be called by user code. Not driver internals.            */
    1806             : /************************************************************************/
    1807             : 
    1808           0 : OGRErr OGRMSSQLSpatialDataSource::RollbackTransaction()
    1809             : {
    1810             :     /* set the status for the newly created layers */
    1811           0 :     for (int iLayer = 0; iLayer < nLayers; iLayer++)
    1812             :     {
    1813           0 :         if (papoLayers[iLayer]->GetLayerStatus() == MSSQLLAYERSTATUS_INITIAL)
    1814           0 :             papoLayers[iLayer]->SetLayerStatus(MSSQLLAYERSTATUS_DISABLED);
    1815             :     }
    1816             : 
    1817           0 :     if (!oSession.RollbackTransaction())
    1818             :     {
    1819           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1820             :                  "Failed to roll back transaction: %s",
    1821             :                  oSession.GetLastError());
    1822           0 :         return OGRERR_FAILURE;
    1823             :     }
    1824             : 
    1825           0 :     return OGRERR_NONE;
    1826             : }
    1827             : 
    1828             : /************************************************************************/
    1829             : /*                             StartCopy()                              */
    1830             : /************************************************************************/
    1831             : 
    1832           0 : void OGRMSSQLSpatialDataSource::StartCopy(
    1833             :     OGRMSSQLSpatialTableLayer *poMSSQLSpatialLayer)
    1834             : {
    1835           0 :     if (poLayerInCopyMode == poMSSQLSpatialLayer)
    1836           0 :         return;
    1837           0 :     EndCopy();
    1838           0 :     poLayerInCopyMode = poMSSQLSpatialLayer;
    1839           0 :     poLayerInCopyMode->StartCopy();
    1840             : }
    1841             : 
    1842             : /************************************************************************/
    1843             : /*                              EndCopy()                               */
    1844             : /************************************************************************/
    1845             : 
    1846           0 : OGRErr OGRMSSQLSpatialDataSource::EndCopy()
    1847             : {
    1848           0 :     if (poLayerInCopyMode != nullptr)
    1849             :     {
    1850           0 :         OGRErr result = poLayerInCopyMode->EndCopy();
    1851           0 :         poLayerInCopyMode = nullptr;
    1852             : 
    1853           0 :         return result;
    1854             :     }
    1855             :     else
    1856           0 :         return OGRERR_NONE;
    1857             : }

Generated by: LCOV version 1.14