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

Generated by: LCOV version 1.14