LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/mssqlspatial - ogrmssqlspatialtablelayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 0 812 0.0 %
Date: 2025-02-20 10:14:44 Functions: 0 28 0.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  MSSQL Spatial driver
       4             :  * Purpose:  Implements OGRMSSQLSpatialTableLayer class, access to an existing
       5             :  *table. Author:   Tamas Szekeres, szekerest at gmail.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010, Tamas Szekeres
       9             :  * Copyright (c) 2010-2012, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_conv.h"
      15             : #include "ogr_mssqlspatial.h"
      16             : #include "ogr_p.h"
      17             : 
      18             : #include <cmath>
      19             : #include <memory>
      20             : #include <string_view>
      21             : 
      22             : #define UNSUPPORTED_OP_READ_ONLY                                               \
      23             :     "%s : unsupported operation on a read-only datasource."
      24             : 
      25             : /************************************************************************/
      26             : /*                         OGRMSSQLAppendEscaped( )                     */
      27             : /************************************************************************/
      28             : 
      29           0 : void OGRMSSQLAppendEscaped(CPLODBCStatement *poStatement,
      30             :                            const char *pszStrValue)
      31             : {
      32           0 :     if (!pszStrValue)
      33             :     {
      34           0 :         poStatement->Append("null");
      35           0 :         return;
      36             :     }
      37             : 
      38           0 :     size_t iIn, iOut, nTextLen = strlen(pszStrValue);
      39           0 :     char *pszEscapedText = (char *)CPLMalloc(nTextLen * 2 + 3);
      40             : 
      41           0 :     pszEscapedText[0] = '\'';
      42             : 
      43           0 :     for (iIn = 0, iOut = 1; iIn < nTextLen; iIn++)
      44             :     {
      45           0 :         switch (pszStrValue[iIn])
      46             :         {
      47           0 :             case '\'':
      48           0 :                 pszEscapedText[iOut++] = '\'';  // double quote
      49           0 :                 pszEscapedText[iOut++] = pszStrValue[iIn];
      50           0 :                 break;
      51             : 
      52           0 :             default:
      53           0 :                 pszEscapedText[iOut++] = pszStrValue[iIn];
      54           0 :                 break;
      55             :         }
      56             :     }
      57             : 
      58           0 :     pszEscapedText[iOut++] = '\'';
      59             : 
      60           0 :     pszEscapedText[iOut] = '\0';
      61             : 
      62           0 :     poStatement->Append(pszEscapedText);
      63             : 
      64           0 :     CPLFree(pszEscapedText);
      65             : }
      66             : 
      67             : /************************************************************************/
      68             : /*                          OGRMSSQLSpatialTableLayer()                 */
      69             : /************************************************************************/
      70             : 
      71           0 : OGRMSSQLSpatialTableLayer::OGRMSSQLSpatialTableLayer(
      72           0 :     OGRMSSQLSpatialDataSource *poDSIn)
      73           0 :     : OGRMSSQLSpatialLayer(poDSIn)
      74             : {
      75           0 :     bUseGeometryValidation = CPLTestBool(
      76             :         CPLGetConfigOption("MSSQLSPATIAL_USE_GEOMETRY_VALIDATION", "YES"));
      77           0 : }
      78             : 
      79             : /************************************************************************/
      80             : /*                          ~OGRMSSQLSpatialTableLayer()                */
      81             : /************************************************************************/
      82             : 
      83           0 : OGRMSSQLSpatialTableLayer::~OGRMSSQLSpatialTableLayer()
      84             : 
      85             : {
      86             : #ifdef MSSQL_BCP_SUPPORTED
      87             :     CloseBCP();
      88             : #endif
      89             : 
      90           0 :     if (bNeedSpatialIndex && nLayerStatus == MSSQLLAYERSTATUS_CREATED)
      91             :     {
      92             :         /* recreate spatial index */
      93           0 :         DropSpatialIndex();
      94           0 :         CreateSpatialIndex();
      95             :     }
      96             : 
      97           0 :     CPLFree(pszTableName);
      98           0 :     CPLFree(pszLayerName);
      99           0 :     CPLFree(pszSchemaName);
     100             : 
     101           0 :     CPLFree(pszQuery);
     102           0 :     ClearStatement();
     103           0 : }
     104             : 
     105             : /************************************************************************/
     106             : /*                               GetName()                              */
     107             : /************************************************************************/
     108             : 
     109           0 : const char *OGRMSSQLSpatialTableLayer::GetName()
     110             : 
     111             : {
     112           0 :     return pszLayerName;
     113             : }
     114             : 
     115             : /************************************************************************/
     116             : /*                             GetLayerDefn()                           */
     117             : /************************************************************************/
     118           0 : OGRFeatureDefn *OGRMSSQLSpatialTableLayer::GetLayerDefn()
     119             : {
     120           0 :     if (poFeatureDefn && !bLayerDefnNeedsRefresh)
     121           0 :         return poFeatureDefn;
     122             : 
     123           0 :     CPLODBCSession *poSession = poDS->GetSession();
     124             :     /* -------------------------------------------------------------------- */
     125             :     /*      Do we have a simple primary key?                                */
     126             :     /* -------------------------------------------------------------------- */
     127           0 :     CPLODBCStatement oGetKey(poSession);
     128             : 
     129           0 :     if (oGetKey.GetPrimaryKeys(pszTableName, poDS->GetCatalog(),
     130           0 :                                pszSchemaName) &&
     131           0 :         oGetKey.Fetch())
     132             :     {
     133           0 :         CPLFree(pszFIDColumn);
     134           0 :         pszFIDColumn = CPLStrdup(oGetKey.GetColData(3));
     135             : 
     136           0 :         if (oGetKey.Fetch())  // more than one field in key!
     137             :         {
     138           0 :             oGetKey.Clear();
     139           0 :             CPLFree(pszFIDColumn);
     140           0 :             pszFIDColumn = nullptr;
     141             : 
     142           0 :             CPLDebug("OGR_MSSQLSpatial",
     143             :                      "Table %s has multiple primary key fields, "
     144             :                      "ignoring them all.",
     145             :                      pszTableName);
     146             :         }
     147             :     }
     148             : 
     149             :     /* -------------------------------------------------------------------- */
     150             :     /*      Get the column definitions for this table.                      */
     151             :     /* -------------------------------------------------------------------- */
     152           0 :     CPLODBCStatement oGetCol(poSession);
     153             : 
     154           0 :     if (!oGetCol.GetColumns(pszTableName, poDS->GetCatalog(), pszSchemaName))
     155             :     {
     156           0 :         poFeatureDefn = new OGRFeatureDefn();
     157           0 :         poFeatureDefn->Reference();
     158           0 :         return poFeatureDefn;
     159             :     }
     160             : 
     161           0 :     BuildFeatureDefn(pszLayerName, &oGetCol);
     162             : 
     163           0 :     if (eGeomType != wkbNone)
     164           0 :         poFeatureDefn->SetGeomType(eGeomType);
     165             : 
     166           0 :     if (GetSpatialRef() && poFeatureDefn->GetGeomFieldCount() == 1)
     167           0 :         poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
     168             : 
     169           0 :     if (poFeatureDefn->GetFieldCount() == 0 && pszFIDColumn == nullptr &&
     170           0 :         pszGeomColumn == nullptr)
     171             :     {
     172           0 :         CPLError(
     173             :             CE_Failure, CPLE_AppDefined,
     174             :             "No column definitions found for table '%s', layer not usable.",
     175             :             pszLayerName);
     176           0 :         return poFeatureDefn;
     177             :     }
     178             : 
     179             :     /* -------------------------------------------------------------------- */
     180             :     /*      If we got a geometry column, does it exist?  Is it binary?      */
     181             :     /* -------------------------------------------------------------------- */
     182           0 :     if (pszGeomColumn != nullptr)
     183             :     {
     184           0 :         int iColumn = oGetCol.GetColId(pszGeomColumn);
     185           0 :         if (iColumn < 0)
     186             :         {
     187           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     188             :                      "Column %s requested for geometry, but it does not exist.",
     189             :                      pszGeomColumn);
     190           0 :             CPLFree(pszGeomColumn);
     191           0 :             pszGeomColumn = nullptr;
     192             :         }
     193             :         else
     194             :         {
     195           0 :             if (nGeomColumnType < 0)
     196             :             {
     197             :                 /* last attempt to identify the geometry column type */
     198           0 :                 if (EQUAL(oGetCol.GetColTypeName(iColumn), "geometry"))
     199           0 :                     nGeomColumnType = MSSQLCOLTYPE_GEOMETRY;
     200           0 :                 else if (EQUAL(oGetCol.GetColTypeName(iColumn), "geography"))
     201           0 :                     nGeomColumnType = MSSQLCOLTYPE_GEOGRAPHY;
     202           0 :                 else if (EQUAL(oGetCol.GetColTypeName(iColumn), "varchar"))
     203           0 :                     nGeomColumnType = MSSQLCOLTYPE_TEXT;
     204           0 :                 else if (EQUAL(oGetCol.GetColTypeName(iColumn), "nvarchar"))
     205           0 :                     nGeomColumnType = MSSQLCOLTYPE_TEXT;
     206           0 :                 else if (EQUAL(oGetCol.GetColTypeName(iColumn), "text"))
     207           0 :                     nGeomColumnType = MSSQLCOLTYPE_TEXT;
     208           0 :                 else if (EQUAL(oGetCol.GetColTypeName(iColumn), "ntext"))
     209           0 :                     nGeomColumnType = MSSQLCOLTYPE_TEXT;
     210           0 :                 else if (EQUAL(oGetCol.GetColTypeName(iColumn), "image"))
     211           0 :                     nGeomColumnType = MSSQLCOLTYPE_BINARY;
     212             :                 else
     213             :                 {
     214           0 :                     CPLError(
     215             :                         CE_Failure, CPLE_AppDefined,
     216             :                         "Column type %s is not supported for geometry column.",
     217             :                         oGetCol.GetColTypeName(iColumn));
     218           0 :                     CPLFree(pszGeomColumn);
     219           0 :                     pszGeomColumn = nullptr;
     220             :                 }
     221             :             }
     222             :         }
     223             :     }
     224             : 
     225           0 :     return poFeatureDefn;
     226             : }
     227             : 
     228             : /************************************************************************/
     229             : /*                             Initialize()                             */
     230             : /************************************************************************/
     231             : 
     232           0 : CPLErr OGRMSSQLSpatialTableLayer::Initialize(const char *pszSchema,
     233             :                                              const char *pszLayerNameIn,
     234             :                                              const char *pszGeomCol,
     235             :                                              CPL_UNUSED int nCoordDimension,
     236             :                                              int nSRId, const char *pszSRText,
     237             :                                              OGRwkbGeometryType eType)
     238             : {
     239           0 :     CPLFree(pszFIDColumn);
     240           0 :     pszFIDColumn = nullptr;
     241             : 
     242             :     /* -------------------------------------------------------------------- */
     243             :     /*      Parse out schema name if present in layer.  We assume a         */
     244             :     /*      schema is provided if there is a dot in the name, and that      */
     245             :     /*      it is in the form <schema>.<tablename>                          */
     246             :     /* -------------------------------------------------------------------- */
     247           0 :     const char *pszDot = strstr(pszLayerNameIn, ".");
     248           0 :     if (pszDot != nullptr)
     249             :     {
     250           0 :         pszTableName = CPLStrdup(pszDot + 1);
     251           0 :         if (pszSchema == nullptr)
     252             :         {
     253           0 :             pszSchemaName = CPLStrdup(pszLayerNameIn);
     254           0 :             pszSchemaName[pszDot - pszLayerNameIn] = '\0';
     255             :         }
     256             :         else
     257           0 :             pszSchemaName = CPLStrdup(pszSchema);
     258             : 
     259           0 :         this->pszLayerName = CPLStrdup(pszLayerNameIn);
     260             :     }
     261             :     else
     262             :     {
     263           0 :         pszTableName = CPLStrdup(pszLayerNameIn);
     264           0 :         if (pszSchema == nullptr || EQUAL(pszSchema, "dbo"))
     265             :         {
     266           0 :             pszSchemaName = CPLStrdup("dbo");
     267           0 :             this->pszLayerName = CPLStrdup(pszLayerNameIn);
     268             :         }
     269             :         else
     270             :         {
     271           0 :             pszSchemaName = CPLStrdup(pszSchema);
     272           0 :             this->pszLayerName =
     273           0 :                 CPLStrdup(CPLSPrintf("%s.%s", pszSchemaName, pszTableName));
     274             :         }
     275             :     }
     276           0 :     SetDescription(this->pszLayerName);
     277             : 
     278             :     /* -------------------------------------------------------------------- */
     279             :     /*      Have we been provided a geometry column?                        */
     280             :     /* -------------------------------------------------------------------- */
     281           0 :     CPLFree(pszGeomColumn);
     282           0 :     if (pszGeomCol == nullptr)
     283           0 :         GetLayerDefn(); /* fetch geom column if not specified */
     284             :     else
     285           0 :         pszGeomColumn = CPLStrdup(pszGeomCol);
     286             : 
     287           0 :     if (eType != wkbNone)
     288           0 :         eGeomType = eType;
     289             : 
     290             :     /* -------------------------------------------------------------------- */
     291             :     /*             Try to find out the spatial reference                    */
     292             :     /* -------------------------------------------------------------------- */
     293             : 
     294           0 :     nSRSId = nSRId;
     295             : 
     296           0 :     if (pszSRText)
     297             :     {
     298             :         /* Process srtext directly if specified */
     299           0 :         poSRS = new OGRSpatialReference();
     300           0 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     301           0 :         if (poSRS->importFromWkt(pszSRText) != OGRERR_NONE)
     302             :         {
     303           0 :             delete poSRS;
     304           0 :             poSRS = nullptr;
     305             :         }
     306             :         else
     307             :         {
     308           0 :             const char *pszAuthorityName = poSRS->GetAuthorityName(nullptr);
     309           0 :             const char *pszAuthorityCode = poSRS->GetAuthorityCode(nullptr);
     310           0 :             if (pszAuthorityName && pszAuthorityCode &&
     311           0 :                 EQUAL(pszAuthorityName, "EPSG"))
     312             :             {
     313           0 :                 const int nCode = atoi(pszAuthorityCode);
     314           0 :                 poSRS->Clear();
     315           0 :                 poSRS->importFromEPSG(nCode);
     316             :             }
     317             :         }
     318             :     }
     319             : 
     320           0 :     if (!poSRS)
     321             :     {
     322           0 :         if (nSRSId <= 0)
     323           0 :             nSRSId = FetchSRSId();
     324             : 
     325           0 :         GetSpatialRef();
     326             :     }
     327             : 
     328           0 :     if (nSRSId < 0)
     329           0 :         nSRSId = 0;
     330             : 
     331           0 :     return CE_None;
     332             : }
     333             : 
     334             : /************************************************************************/
     335             : /*                         FetchSRSId()                                 */
     336             : /************************************************************************/
     337             : 
     338           0 : int OGRMSSQLSpatialTableLayer::FetchSRSId()
     339             : {
     340           0 :     if (poDS->UseGeometryColumns())
     341             :     {
     342           0 :         CPLODBCStatement oStatement(poDS->GetSession());
     343           0 :         oStatement.Appendf(
     344             :             "select srid from geometry_columns "
     345             :             "where f_table_schema = '%s' and f_table_name = '%s'",
     346             :             pszSchemaName, pszTableName);
     347             : 
     348           0 :         if (oStatement.ExecuteSQL() && oStatement.Fetch())
     349             :         {
     350           0 :             if (oStatement.GetColData(0))
     351           0 :                 nSRSId = atoi(oStatement.GetColData(0));
     352           0 :             if (nSRSId < 0)
     353           0 :                 nSRSId = 0;
     354             :         }
     355             :     }
     356             : 
     357           0 :     return nSRSId;
     358             : }
     359             : 
     360             : /************************************************************************/
     361             : /*                       CreateSpatialIndex()                           */
     362             : /*                                                                      */
     363             : /*      Create a spatial index on the geometry column of the layer      */
     364             : /************************************************************************/
     365             : 
     366           0 : OGRErr OGRMSSQLSpatialTableLayer::CreateSpatialIndex()
     367             : {
     368           0 :     OGRMSSQLSpatialTableLayer::GetLayerDefn();
     369             : 
     370           0 :     if (pszGeomColumn == nullptr)
     371             :     {
     372           0 :         CPLError(CE_Warning, CPLE_AppDefined, "No geometry column found.");
     373           0 :         return OGRERR_FAILURE;
     374             :     }
     375             : 
     376           0 :     CPLODBCStatement oStatement(poDS->GetSession());
     377             : 
     378           0 :     if (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY)
     379             :     {
     380           0 :         OGREnvelope oExt;
     381           0 :         if (GetExtent(&oExt, TRUE) != OGRERR_NONE)
     382             :         {
     383           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     384             :                      "Failed to get extent for spatial index.");
     385           0 :             return OGRERR_FAILURE;
     386             :         }
     387             : 
     388           0 :         if (oExt.MinX == oExt.MaxX || oExt.MinY == oExt.MaxY)
     389           0 :             return OGRERR_NONE; /* skip creating index */
     390             : 
     391           0 :         oStatement.Appendf(
     392             :             "CREATE SPATIAL INDEX [ogr_%s_%s_%s_sidx] ON [%s].[%s] ( [%s] ) "
     393             :             "USING GEOMETRY_GRID WITH (BOUNDING_BOX =(%.15g, %.15g, %.15g, "
     394             :             "%.15g))",
     395             :             pszSchemaName, pszTableName, pszGeomColumn, pszSchemaName,
     396             :             pszTableName, pszGeomColumn, oExt.MinX, oExt.MinY, oExt.MaxX,
     397             :             oExt.MaxY);
     398             :     }
     399           0 :     else if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
     400             :     {
     401           0 :         oStatement.Appendf(
     402             :             "CREATE SPATIAL INDEX [ogr_%s_%s_%s_sidx] ON [%s].[%s] ( [%s] ) "
     403             :             "USING GEOGRAPHY_GRID",
     404             :             pszSchemaName, pszTableName, pszGeomColumn, pszSchemaName,
     405             :             pszTableName, pszGeomColumn);
     406             :     }
     407             :     else
     408             :     {
     409           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     410             :                  "Spatial index is not supported on the geometry column '%s'",
     411             :                  pszGeomColumn);
     412           0 :         return OGRERR_FAILURE;
     413             :     }
     414             : 
     415           0 :     if (!oStatement.ExecuteSQL())
     416             :     {
     417           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     418             :                  "Failed to create the spatial index, %s.",
     419           0 :                  poDS->GetSession()->GetLastError());
     420           0 :         return OGRERR_FAILURE;
     421             :     }
     422             : 
     423           0 :     return OGRERR_NONE;
     424             : }
     425             : 
     426             : /************************************************************************/
     427             : /*                       DropSpatialIndex()                             */
     428             : /*                                                                      */
     429             : /*      Drop the spatial index on the geometry column of the layer      */
     430             : /************************************************************************/
     431             : 
     432           0 : void OGRMSSQLSpatialTableLayer::DropSpatialIndex()
     433             : {
     434           0 :     OGRMSSQLSpatialTableLayer::GetLayerDefn();
     435             : 
     436           0 :     CPLODBCStatement oStatement(poDS->GetSession());
     437             : 
     438           0 :     oStatement.Appendf("IF  EXISTS (SELECT * FROM sys.indexes "
     439             :                        "WHERE object_id = OBJECT_ID(N'[%s].[%s]') AND name = "
     440             :                        "N'ogr_%s_%s_%s_sidx') "
     441             :                        "DROP INDEX [ogr_%s_%s_%s_sidx] ON [%s].[%s]",
     442             :                        pszSchemaName, pszTableName, pszSchemaName, pszTableName,
     443             :                        pszGeomColumn, pszSchemaName, pszTableName,
     444             :                        pszGeomColumn, pszSchemaName, pszTableName);
     445             : 
     446           0 :     if (!oStatement.ExecuteSQL())
     447             :     {
     448           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     449             :                  "Failed to drop the spatial index, %s.",
     450           0 :                  poDS->GetSession()->GetLastError());
     451           0 :         return;
     452             :     }
     453             : }
     454             : 
     455             : /************************************************************************/
     456             : /*                     GetBracketEscapedIdentifier()                    */
     457             : /************************************************************************/
     458             : 
     459           0 : static std::string GetBracketEscapedIdentifier(const std::string_view &osStr)
     460             : {
     461           0 :     std::string osRet("[");
     462           0 :     osRet.reserve(osStr.size());
     463           0 :     for (char ch : osStr)
     464             :     {
     465           0 :         if (ch == ']')
     466             :         {
     467           0 :             osRet += ch;
     468             :         }
     469           0 :         osRet += ch;
     470             :     }
     471           0 :     osRet += ']';
     472           0 :     return osRet;
     473             : }
     474             : 
     475             : /************************************************************************/
     476             : /*                            BuildFields()                             */
     477             : /*                                                                      */
     478             : /*      Build list of fields to fetch, performing any required          */
     479             : /*      transformations (such as on geometry).                          */
     480             : /************************************************************************/
     481             : 
     482           0 : CPLString OGRMSSQLSpatialTableLayer::BuildFields()
     483             : 
     484             : {
     485           0 :     int nColumn = 0;
     486           0 :     CPLString osFieldList;
     487             : 
     488           0 :     GetLayerDefn();
     489             : 
     490           0 :     if (pszFIDColumn && poFeatureDefn->GetFieldIndex(pszFIDColumn) == -1)
     491             :     {
     492             :         /* Always get the FID column */
     493           0 :         osFieldList += GetBracketEscapedIdentifier(pszFIDColumn);
     494           0 :         ++nColumn;
     495             :     }
     496             : 
     497           0 :     if (pszGeomColumn && !poFeatureDefn->IsGeometryIgnored())
     498             :     {
     499           0 :         if (nColumn > 0)
     500           0 :             osFieldList += ", ";
     501             : 
     502           0 :         osFieldList += GetBracketEscapedIdentifier(pszGeomColumn);
     503           0 :         if (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY ||
     504           0 :             nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
     505             :         {
     506           0 :             if (poDS->GetGeometryFormat() == MSSQLGEOMETRY_WKB)
     507             :             {
     508           0 :                 osFieldList += ".STAsBinary() as ";
     509           0 :                 osFieldList += GetBracketEscapedIdentifier(pszGeomColumn);
     510             :             }
     511           0 :             else if (poDS->GetGeometryFormat() == MSSQLGEOMETRY_WKT)
     512             :             {
     513           0 :                 osFieldList += ".AsTextZM() as ";
     514           0 :                 osFieldList += GetBracketEscapedIdentifier(pszGeomColumn);
     515             :             }
     516           0 :             else if (poDS->GetGeometryFormat() == MSSQLGEOMETRY_WKBZM)
     517             :             {
     518             :                 /* SQL Server 2012 */
     519           0 :                 osFieldList += ".AsBinaryZM() as ";
     520           0 :                 osFieldList += GetBracketEscapedIdentifier(pszGeomColumn);
     521             :             }
     522             :         }
     523             : 
     524           0 :         ++nColumn;
     525             :     }
     526             : 
     527           0 :     if (poFeatureDefn->GetFieldCount() > 0)
     528             :     {
     529             :         /* need to reconstruct the field ordinals list */
     530           0 :         CPLFree(panFieldOrdinals);
     531           0 :         panFieldOrdinals =
     532           0 :             (int *)CPLMalloc(sizeof(int) * poFeatureDefn->GetFieldCount());
     533             : 
     534           0 :         for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
     535             :         {
     536           0 :             if (poFeatureDefn->GetFieldDefn(i)->IsIgnored())
     537           0 :                 continue;
     538             : 
     539           0 :             const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
     540             : 
     541           0 :             if (nColumn > 0)
     542           0 :                 osFieldList += ", ";
     543             : 
     544           0 :             osFieldList += GetBracketEscapedIdentifier(pszName);
     545             : 
     546           0 :             panFieldOrdinals[i] = nColumn;
     547             : 
     548           0 :             ++nColumn;
     549             :         }
     550             :     }
     551             : 
     552           0 :     return osFieldList;
     553             : }
     554             : 
     555             : /************************************************************************/
     556             : /*                            GetStatement()                            */
     557             : /************************************************************************/
     558             : 
     559           0 : CPLODBCStatement *OGRMSSQLSpatialTableLayer::GetStatement()
     560             : 
     561             : {
     562           0 :     if (poStmt == nullptr)
     563             :     {
     564           0 :         poStmt = BuildStatement(BuildFields());
     565             :     }
     566             : 
     567           0 :     return poStmt;
     568             : }
     569             : 
     570             : /************************************************************************/
     571             : /*                           BuildStatement()                           */
     572             : /************************************************************************/
     573             : 
     574             : CPLODBCStatement *
     575           0 : OGRMSSQLSpatialTableLayer::BuildStatement(const char *pszColumns)
     576             : 
     577             : {
     578           0 :     CPLODBCStatement *poStatement = new CPLODBCStatement(poDS->GetSession());
     579           0 :     poStatement->Append("select ");
     580           0 :     poStatement->Append(pszColumns);
     581           0 :     poStatement->Append(" from ");
     582           0 :     poStatement->Append(GetBracketEscapedIdentifier(pszSchemaName));
     583           0 :     poStatement->Append(".");
     584           0 :     poStatement->Append(GetBracketEscapedIdentifier(pszTableName));
     585             : 
     586             :     /* Append attribute query if we have it */
     587           0 :     if (pszQuery != nullptr)
     588           0 :         poStatement->Appendf(" where (%s)", pszQuery);
     589             : 
     590             :     /* If we have a spatial filter, query on it */
     591           0 :     if (m_poFilterGeom != nullptr)
     592             :     {
     593           0 :         if (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY ||
     594           0 :             nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
     595             :         {
     596           0 :             if (!std::isinf(m_sFilterEnvelope.MinX) &&
     597           0 :                 !std::isinf(m_sFilterEnvelope.MinY) &&
     598           0 :                 !std::isinf(m_sFilterEnvelope.MaxX) &&
     599           0 :                 !std::isinf(m_sFilterEnvelope.MaxY))
     600             :             {
     601           0 :                 if (pszQuery == nullptr)
     602           0 :                     poStatement->Append(" where ");
     603             :                 else
     604           0 :                     poStatement->Append(" and ");
     605             : 
     606           0 :                 poStatement->Append(GetBracketEscapedIdentifier(pszGeomColumn));
     607           0 :                 poStatement->Append(".STIntersects(");
     608             : 
     609           0 :                 if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
     610           0 :                     poStatement->Append("geography::");
     611             :                 else
     612           0 :                     poStatement->Append("geometry::");
     613             : 
     614           0 :                 if (m_sFilterEnvelope.MinX == m_sFilterEnvelope.MaxX ||
     615           0 :                     m_sFilterEnvelope.MinY == m_sFilterEnvelope.MaxY)
     616           0 :                     poStatement->Appendf(
     617             :                         "STGeomFromText('POINT(%.15g %.15g)',%d)) = 1",
     618             :                         m_sFilterEnvelope.MinX, m_sFilterEnvelope.MinY, nSRSId);
     619             :                 else
     620           0 :                     poStatement->Appendf(
     621             :                         "STGeomFromText('POLYGON((%.15g %.15g,%.15g "
     622             :                         "%.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g))',%d)) = 1",
     623             :                         m_sFilterEnvelope.MinX, m_sFilterEnvelope.MinY,
     624             :                         m_sFilterEnvelope.MaxX, m_sFilterEnvelope.MinY,
     625             :                         m_sFilterEnvelope.MaxX, m_sFilterEnvelope.MaxY,
     626             :                         m_sFilterEnvelope.MinX, m_sFilterEnvelope.MaxY,
     627             :                         m_sFilterEnvelope.MinX, m_sFilterEnvelope.MinY, nSRSId);
     628             :             }
     629             :         }
     630             :         else
     631             :         {
     632           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     633             :                      "Spatial filter is supported only on geometry and "
     634             :                      "geography column types.");
     635             : 
     636           0 :             delete poStatement;
     637           0 :             return nullptr;
     638             :         }
     639             :     }
     640             : 
     641           0 :     CPLDebug("OGR_MSSQLSpatial", "ExecuteSQL(%s)", poStatement->GetCommand());
     642           0 :     if (poStatement->ExecuteSQL())
     643           0 :         return poStatement;
     644             :     else
     645             :     {
     646           0 :         delete poStatement;
     647           0 :         return nullptr;
     648             :     }
     649             : }
     650             : 
     651             : /************************************************************************/
     652             : /*                             GetFeature()                             */
     653             : /************************************************************************/
     654             : 
     655           0 : OGRFeature *OGRMSSQLSpatialTableLayer::GetFeature(GIntBig nFeatureId)
     656             : 
     657             : {
     658           0 :     if (pszFIDColumn == nullptr)
     659           0 :         return OGRMSSQLSpatialLayer::GetFeature(nFeatureId);
     660             : 
     661           0 :     poDS->EndCopy();
     662             : 
     663           0 :     ClearStatement();
     664             : 
     665           0 :     iNextShapeId = nFeatureId;
     666             : 
     667           0 :     m_bResetNeeded = true;
     668           0 :     poStmt = new CPLODBCStatement(poDS->GetSession());
     669           0 :     CPLString osFields = BuildFields();
     670           0 :     poStmt->Appendf("select %s from %s where %s = " CPL_FRMT_GIB,
     671           0 :                     osFields.c_str(), poFeatureDefn->GetName(), pszFIDColumn,
     672             :                     nFeatureId);
     673             : 
     674           0 :     if (!poStmt->ExecuteSQL())
     675             :     {
     676           0 :         delete poStmt;
     677           0 :         poStmt = nullptr;
     678           0 :         return nullptr;
     679             :     }
     680             : 
     681           0 :     return GetNextRawFeature();
     682             : }
     683             : 
     684             : /************************************************************************/
     685             : /*                            IGetExtent()                              */
     686             : /*                                                                      */
     687             : /*      For Geometry or Geography types we can use an optimized         */
     688             : /*      statement in other cases we use standard OGRLayer::IGetExtent() */
     689             : /************************************************************************/
     690             : 
     691           0 : OGRErr OGRMSSQLSpatialTableLayer::IGetExtent(int iGeomField,
     692             :                                              OGREnvelope *psExtent, bool bForce)
     693             : {
     694           0 :     GetLayerDefn();
     695             : 
     696             :     // If we have a geometry or geography type:
     697           0 :     if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY ||
     698           0 :         nGeomColumnType == MSSQLCOLTYPE_GEOMETRY)
     699             :     {
     700             :         // Prepare statement
     701             :         auto poStatement =
     702           0 :             std::make_unique<CPLODBCStatement>(poDS->GetSession());
     703             : 
     704           0 :         if (poDS->sMSSQLVersion.nMajor >= 11)
     705             :         {
     706             :             // SQLServer 2012 or later:
     707             :             // geography is converted to geometry to obtain the rectangular
     708             :             // envelope
     709           0 :             if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
     710           0 :                 poStatement->Appendf(
     711             :                     "WITH extent(extentcol) AS (SELECT "
     712             :                     "geometry::EnvelopeAggregate(geometry::STGeomFromWKB(%s."
     713             :                     "STAsBinary(), %s.STSrid).MakeValid()) as extentcol FROM "
     714             :                     "[%s].[%s])",
     715             :                     pszGeomColumn, pszGeomColumn, pszSchemaName, pszTableName);
     716             :             else
     717           0 :                 poStatement->Appendf("WITH extent(extentcol) AS (SELECT "
     718             :                                      "geometry::EnvelopeAggregate(%s.MakeValid("
     719             :                                      ")) AS extentcol FROM [%s].[%s])",
     720             :                                      pszGeomColumn, pszSchemaName,
     721             :                                      pszTableName);
     722             : 
     723           0 :             poStatement->Appendf(
     724             :                 "SELECT extentcol.STPointN(1).STX, extentcol.STPointN(1).STY,");
     725           0 :             poStatement->Appendf("extentcol.STPointN(3).STX, "
     726             :                                  "extentcol.STPointN(3).STY FROM extent;");
     727             :         }
     728             :         else
     729             :         {
     730             :             // Before 2012 use two CTE's:
     731             :             // geography is converted to geometry to obtain the envelope
     732           0 :             if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
     733           0 :                 poStatement->Appendf("WITH ENVELOPE as (SELECT "
     734             :                                      "geometry::STGeomFromWKB(%s.STAsBinary(), "
     735             :                                      "%s.STSrid).MakeValid().STEnvelope() as "
     736             :                                      "envelope from [%s].[%s]),",
     737             :                                      pszGeomColumn, pszGeomColumn,
     738             :                                      pszSchemaName, pszTableName);
     739             :             else
     740           0 :                 poStatement->Appendf(
     741             :                     "WITH ENVELOPE as (SELECT %s.MakeValid().STEnvelope() as "
     742             :                     "envelope from [%s].[%s]),",
     743             :                     pszGeomColumn, pszSchemaName, pszTableName);
     744             : 
     745           0 :             poStatement->Appendf(" CORNERS as (SELECT envelope.STPointN(1) as "
     746             :                                  "point from ENVELOPE UNION ALL select "
     747             :                                  "envelope.STPointN(3) from ENVELOPE)");
     748           0 :             poStatement->Appendf(
     749             :                 "SELECT MIN(point.STX), MIN(point.STY), MAX(point.STX), "
     750             :                 "MAX(point.STY) FROM CORNERS;");
     751             :         }
     752             : 
     753             :         // Execute
     754           0 :         if (!poStatement->ExecuteSQL())
     755             :         {
     756           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Error getting extents, %s",
     757           0 :                      poDS->GetSession()->GetLastError());
     758             :         }
     759             :         else
     760             :         {
     761             :             // Try to update
     762           0 :             while (poStatement->Fetch())
     763             :             {
     764             : 
     765           0 :                 const char *minx = poStatement->GetColData(0);
     766           0 :                 const char *miny = poStatement->GetColData(1);
     767           0 :                 const char *maxx = poStatement->GetColData(2);
     768           0 :                 const char *maxy = poStatement->GetColData(3);
     769             : 
     770           0 :                 if (!(minx == nullptr || miny == nullptr || maxx == nullptr ||
     771             :                       maxy == nullptr))
     772             :                 {
     773           0 :                     psExtent->MinX = CPLAtof(minx);
     774           0 :                     psExtent->MinY = CPLAtof(miny);
     775           0 :                     psExtent->MaxX = CPLAtof(maxx);
     776           0 :                     psExtent->MaxY = CPLAtof(maxy);
     777           0 :                     return OGRERR_NONE;
     778             :                 }
     779             :                 else
     780             :                 {
     781           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     782             :                              "MSSQL extents query returned a NULL value");
     783             :                 }
     784             :             }
     785             :         }
     786             :     }
     787             : 
     788             :     // Fall back to generic implementation (loading all features)
     789           0 :     return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
     790             : }
     791             : 
     792             : /************************************************************************/
     793             : /*                         SetAttributeFilter()                         */
     794             : /************************************************************************/
     795             : 
     796           0 : OGRErr OGRMSSQLSpatialTableLayer::SetAttributeFilter(const char *pszQueryIn)
     797             : 
     798             : {
     799           0 :     CPLFree(m_pszAttrQueryString);
     800           0 :     m_pszAttrQueryString = (pszQueryIn) ? CPLStrdup(pszQueryIn) : nullptr;
     801             : 
     802           0 :     if ((pszQueryIn == nullptr && this->pszQuery == nullptr) ||
     803           0 :         (pszQueryIn != nullptr && this->pszQuery != nullptr &&
     804           0 :          EQUAL(pszQueryIn, this->pszQuery)))
     805           0 :         return OGRERR_NONE;
     806             : 
     807           0 :     CPLFree(this->pszQuery);
     808           0 :     this->pszQuery = (pszQueryIn) ? CPLStrdup(pszQueryIn) : nullptr;
     809             : 
     810           0 :     ClearStatement();
     811             : 
     812           0 :     return OGRERR_NONE;
     813             : }
     814             : 
     815             : /************************************************************************/
     816             : /*                           GetNextFeature()                           */
     817             : /************************************************************************/
     818             : 
     819           0 : OGRFeature *OGRMSSQLSpatialTableLayer::GetNextFeature()
     820             : {
     821           0 :     poDS->EndCopy();
     822           0 :     return OGRMSSQLSpatialLayer::GetNextFeature();
     823             : }
     824             : 
     825             : /************************************************************************/
     826             : /*                           TestCapability()                           */
     827             : /************************************************************************/
     828             : 
     829           0 : int OGRMSSQLSpatialTableLayer::TestCapability(const char *pszCap)
     830             : 
     831             : {
     832           0 :     if (bUpdateAccess)
     833             :     {
     834           0 :         if (EQUAL(pszCap, OLCSequentialWrite) ||
     835           0 :             EQUAL(pszCap, OLCCreateField) || EQUAL(pszCap, OLCDeleteFeature))
     836           0 :             return TRUE;
     837             : 
     838           0 :         else if (EQUAL(pszCap, OLCRandomWrite))
     839           0 :             return pszFIDColumn != nullptr;
     840             :     }
     841             : 
     842             : #if (ODBCVER >= 0x0300)
     843           0 :     if (EQUAL(pszCap, OLCTransactions))
     844           0 :         return TRUE;
     845             : #else
     846             :     if (EQUAL(pszCap, OLCTransactions))
     847             :         return FALSE;
     848             : #endif
     849             : 
     850           0 :     if (EQUAL(pszCap, OLCIgnoreFields))
     851           0 :         return TRUE;
     852             : 
     853           0 :     if (EQUAL(pszCap, OLCRandomRead))
     854           0 :         return pszFIDColumn != nullptr;
     855           0 :     else if (EQUAL(pszCap, OLCFastFeatureCount))
     856           0 :         return TRUE;
     857           0 :     else if (EQUAL(pszCap, OLCCurveGeometries))
     858           0 :         return TRUE;
     859           0 :     else if (EQUAL(pszCap, OLCMeasuredGeometries))
     860           0 :         return TRUE;
     861           0 :     else if (EQUAL(pszCap, OLCZGeometries))
     862           0 :         return TRUE;
     863             :     else
     864           0 :         return OGRMSSQLSpatialLayer::TestCapability(pszCap);
     865             : }
     866             : 
     867             : /************************************************************************/
     868             : /*                          GetFeatureCount()                           */
     869             : /************************************************************************/
     870             : 
     871           0 : GIntBig OGRMSSQLSpatialTableLayer::GetFeatureCount(int bForce)
     872             : 
     873             : {
     874           0 :     poDS->EndCopy();
     875             : 
     876           0 :     GetLayerDefn();
     877             : 
     878           0 :     if (TestCapability(OLCFastFeatureCount) == FALSE)
     879           0 :         return OGRMSSQLSpatialLayer::GetFeatureCount(bForce);
     880             : 
     881           0 :     CPLODBCStatement *poStatement = BuildStatement("count(*)");
     882             : 
     883           0 :     if (poStatement == nullptr || !poStatement->Fetch())
     884             :     {
     885           0 :         delete poStatement;
     886           0 :         return OGRMSSQLSpatialLayer::GetFeatureCount(bForce);
     887             :     }
     888             : 
     889           0 :     GIntBig nRet = CPLAtoGIntBig(poStatement->GetColData(0));
     890           0 :     delete poStatement;
     891           0 :     return nRet;
     892             : }
     893             : 
     894             : /************************************************************************/
     895             : /*                             StartCopy()                              */
     896             : /************************************************************************/
     897             : 
     898           0 : OGRErr OGRMSSQLSpatialTableLayer::StartCopy()
     899             : 
     900             : {
     901           0 :     return OGRERR_NONE;
     902             : }
     903             : 
     904             : /************************************************************************/
     905             : /*                              EndCopy()                               */
     906             : /************************************************************************/
     907             : 
     908           0 : OGRErr OGRMSSQLSpatialTableLayer::EndCopy()
     909             : 
     910             : {
     911             : #ifdef MSSQL_BCP_SUPPORTED
     912             :     CloseBCP();
     913             : #endif
     914           0 :     return OGRERR_NONE;
     915             : }
     916             : 
     917             : /************************************************************************/
     918             : /*                            CreateField()                             */
     919             : /************************************************************************/
     920             : 
     921           0 : OGRErr OGRMSSQLSpatialTableLayer::CreateField(const OGRFieldDefn *poFieldIn,
     922             :                                               int bApproxOK)
     923             : 
     924             : {
     925             :     char szFieldType[256];
     926           0 :     OGRFieldDefn oField(poFieldIn);
     927             : 
     928           0 :     poDS->EndCopy();
     929             : 
     930           0 :     GetLayerDefn();
     931             : 
     932             :     /* -------------------------------------------------------------------- */
     933             :     /*      Do we want to "launder" the column names into MSSQL             */
     934             :     /*      friendly format?                                                */
     935             :     /* -------------------------------------------------------------------- */
     936           0 :     if (bLaunderColumnNames)
     937             :     {
     938           0 :         char *pszSafeName = poDS->LaunderName(oField.GetNameRef());
     939             : 
     940           0 :         oField.SetName(pszSafeName);
     941           0 :         CPLFree(pszSafeName);
     942             :     }
     943             : 
     944             :     /* -------------------------------------------------------------------- */
     945             :     /*      Identify the MSSQL type.                                        */
     946             :     /* -------------------------------------------------------------------- */
     947             : 
     948           0 :     if (oField.GetType() == OFTInteger)
     949             :     {
     950           0 :         if (oField.GetWidth() > 0 && bPreservePrecision)
     951           0 :             snprintf(szFieldType, sizeof(szFieldType), "numeric(%d,0)",
     952             :                      oField.GetWidth());
     953           0 :         else if (oField.GetSubType() == OFSTInt16)
     954           0 :             strcpy(szFieldType, "smallint");
     955             :         else
     956           0 :             strcpy(szFieldType, "int");
     957             :     }
     958           0 :     else if (oField.GetType() == OFTInteger64)
     959             :     {
     960           0 :         if (oField.GetWidth() > 0 && bPreservePrecision)
     961           0 :             snprintf(szFieldType, sizeof(szFieldType), "numeric(%d,0)",
     962             :                      oField.GetWidth());
     963             :         else
     964           0 :             strcpy(szFieldType, "bigint");
     965             :     }
     966           0 :     else if (oField.GetType() == OFTReal)
     967             :     {
     968           0 :         if (oField.GetWidth() > 0 && oField.GetPrecision() >= 0 &&
     969           0 :             bPreservePrecision)
     970           0 :             snprintf(szFieldType, sizeof(szFieldType), "numeric(%d,%d)",
     971             :                      oField.GetWidth(), oField.GetPrecision());
     972           0 :         else if (oField.GetSubType() == OFSTFloat32)
     973           0 :             strcpy(szFieldType, "float(23)");
     974             :         else
     975           0 :             strcpy(szFieldType, "float(53)");
     976             :     }
     977           0 :     else if (oField.GetType() == OFTString)
     978             :     {
     979           0 :         if (oField.GetSubType() == OGRFieldSubType::OFSTUUID)
     980             :         {
     981           0 :             m_bHasUUIDColumn = true;
     982           0 :             strcpy(szFieldType, "uniqueidentifier");
     983             :         }
     984           0 :         else if (oField.GetWidth() == 0 || oField.GetWidth() > 4000 ||
     985           0 :                  !bPreservePrecision)
     986           0 :             strcpy(szFieldType, "nvarchar(MAX)");
     987             :         else
     988           0 :             snprintf(szFieldType, sizeof(szFieldType), "nvarchar(%d)",
     989             :                      oField.GetWidth());
     990             :     }
     991           0 :     else if (oField.GetType() == OFTDate)
     992             :     {
     993           0 :         strcpy(szFieldType, "date");
     994             :     }
     995           0 :     else if (oField.GetType() == OFTTime)
     996             :     {
     997           0 :         strcpy(szFieldType, "time(7)");
     998             :     }
     999           0 :     else if (oField.GetType() == OFTDateTime)
    1000             :     {
    1001           0 :         strcpy(szFieldType, "datetime");
    1002             :     }
    1003           0 :     else if (oField.GetType() == OFTBinary)
    1004             :     {
    1005           0 :         strcpy(szFieldType, "image");
    1006             :     }
    1007           0 :     else if (bApproxOK)
    1008             :     {
    1009           0 :         CPLError(CE_Warning, CPLE_NotSupported,
    1010             :                  "Can't create field %s with type %s on MSSQL layers.  "
    1011             :                  "Creating as varchar.",
    1012             :                  oField.GetNameRef(),
    1013             :                  OGRFieldDefn::GetFieldTypeName(oField.GetType()));
    1014           0 :         strcpy(szFieldType, "varchar");
    1015             :     }
    1016             :     else
    1017             :     {
    1018           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1019             :                  "Can't create field %s with type %s on MSSQL layers.",
    1020             :                  oField.GetNameRef(),
    1021             :                  OGRFieldDefn::GetFieldTypeName(oField.GetType()));
    1022             : 
    1023           0 :         return OGRERR_FAILURE;
    1024             :     }
    1025             : 
    1026             :     /* -------------------------------------------------------------------- */
    1027             :     /*      Create the new field.                                           */
    1028             :     /* -------------------------------------------------------------------- */
    1029             : 
    1030           0 :     CPLODBCStatement oStmt(poDS->GetSession());
    1031             : 
    1032           0 :     oStmt.Appendf("ALTER TABLE [%s].[%s] ADD [%s] %s", pszSchemaName,
    1033             :                   pszTableName, oField.GetNameRef(), szFieldType);
    1034             : 
    1035           0 :     if (!oField.IsNullable())
    1036             :     {
    1037           0 :         oStmt.Append(" NOT NULL");
    1038             :     }
    1039           0 :     if (oField.GetDefault() != nullptr && !oField.IsDefaultDriverSpecific())
    1040             :     {
    1041             :         /* process default value specifications */
    1042           0 :         if (EQUAL(oField.GetDefault(), "CURRENT_TIME"))
    1043           0 :             oStmt.Append(" DEFAULT(CONVERT([time],getdate()))");
    1044           0 :         else if (EQUAL(oField.GetDefault(), "CURRENT_DATE"))
    1045           0 :             oStmt.Append(" DEFAULT(CONVERT([date],getdate()))");
    1046             :         else
    1047           0 :             oStmt.Appendf(" DEFAULT(%s)", oField.GetDefault());
    1048             :     }
    1049             : 
    1050           0 :     if (!oStmt.ExecuteSQL())
    1051             :     {
    1052           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Error creating field %s, %s",
    1053           0 :                  oField.GetNameRef(), poDS->GetSession()->GetLastError());
    1054             : 
    1055           0 :         return OGRERR_FAILURE;
    1056             :     }
    1057             : 
    1058             :     /* -------------------------------------------------------------------- */
    1059             :     /*      Add the field to the OGRFeatureDefn.                            */
    1060             :     /* -------------------------------------------------------------------- */
    1061             : 
    1062           0 :     poFeatureDefn->AddFieldDefn(&oField);
    1063             : 
    1064           0 :     return OGRERR_NONE;
    1065             : }
    1066             : 
    1067             : /************************************************************************/
    1068             : /*                             ISetFeature()                             */
    1069             : /*                                                                      */
    1070             : /*      SetFeature() is implemented by an UPDATE SQL command            */
    1071             : /************************************************************************/
    1072             : 
    1073           0 : OGRErr OGRMSSQLSpatialTableLayer::ISetFeature(OGRFeature *poFeature)
    1074             : 
    1075             : {
    1076           0 :     if (!bUpdateAccess)
    1077             :     {
    1078           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1079             :                  "SetFeature");
    1080           0 :         return OGRERR_FAILURE;
    1081             :     }
    1082             : 
    1083           0 :     OGRErr eErr = OGRERR_FAILURE;
    1084             : 
    1085           0 :     poDS->EndCopy();
    1086             : 
    1087           0 :     GetLayerDefn();
    1088             : 
    1089           0 :     if (nullptr == poFeature)
    1090             :     {
    1091           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1092             :                  "NULL pointer to OGRFeature passed to SetFeature().");
    1093           0 :         return eErr;
    1094             :     }
    1095             : 
    1096           0 :     if (poFeature->GetFID() == OGRNullFID)
    1097             :     {
    1098           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1099             :                  "FID required on features given to SetFeature().");
    1100           0 :         return eErr;
    1101             :     }
    1102             : 
    1103           0 :     if (!pszFIDColumn)
    1104             :     {
    1105           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1106             :                  "Unable to update features in tables without\n"
    1107             :                  "a recognised FID column.");
    1108           0 :         return eErr;
    1109             :     }
    1110             : 
    1111           0 :     ClearStatement();
    1112             : 
    1113             :     /* -------------------------------------------------------------------- */
    1114             :     /*      Form the UPDATE command.                                        */
    1115             :     /* -------------------------------------------------------------------- */
    1116           0 :     CPLODBCStatement oStmt(poDS->GetSession());
    1117             : 
    1118           0 :     oStmt.Appendf("UPDATE [%s].[%s] SET ", pszSchemaName, pszTableName);
    1119             : 
    1120           0 :     OGRGeometry *poGeom = poFeature->GetGeometryRef();
    1121           0 :     if (bUseGeometryValidation && poGeom != nullptr)
    1122             :     {
    1123           0 :         OGRMSSQLGeometryValidator oValidator(poGeom, nGeomColumnType);
    1124           0 :         if (!oValidator.IsValid())
    1125             :         {
    1126           0 :             oValidator.MakeValid(poGeom);
    1127           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    1128             :                      "Geometry with FID = " CPL_FRMT_GIB
    1129             :                      " has been modified to valid geometry.",
    1130             :                      poFeature->GetFID());
    1131             :         }
    1132             :     }
    1133             : 
    1134           0 :     int nFieldCount = poFeatureDefn->GetFieldCount();
    1135           0 :     int bind_num = 0;
    1136           0 :     void **bind_buffer = (void **)CPLMalloc(sizeof(void *) * nFieldCount);
    1137             : 
    1138           0 :     int bNeedComma = FALSE;
    1139             :     SQLLEN nWKBLenBindParameter;
    1140           0 :     if (poGeom != nullptr && pszGeomColumn != nullptr)
    1141             :     {
    1142           0 :         oStmt.Appendf("[%s] = ", pszGeomColumn);
    1143             : 
    1144           0 :         if (nUploadGeometryFormat == MSSQLGEOMETRY_NATIVE)
    1145             :         {
    1146           0 :             OGRMSSQLGeometryWriter poWriter(poGeom, nGeomColumnType, nSRSId);
    1147           0 :             int nDataLen = poWriter.GetDataLen();
    1148           0 :             GByte *pabyData = (GByte *)CPLMalloc(nDataLen + 1);
    1149           0 :             if (poWriter.WriteSqlGeometry(pabyData, nDataLen) == OGRERR_NONE)
    1150             :             {
    1151           0 :                 char *pszBytes = GByteArrayToHexString(pabyData, nDataLen);
    1152           0 :                 SQLLEN nts = SQL_NTS;
    1153           0 :                 int nRetCode = SQLBindParameter(
    1154           0 :                     oStmt.GetStatement(), (SQLUSMALLINT)(bind_num + 1),
    1155             :                     SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR, nDataLen, 0,
    1156           0 :                     (SQLPOINTER)pszBytes, 0, &nts);
    1157           0 :                 if (nRetCode == SQL_SUCCESS ||
    1158             :                     nRetCode == SQL_SUCCESS_WITH_INFO)
    1159             :                 {
    1160           0 :                     oStmt.Append("?");
    1161           0 :                     bind_buffer[bind_num] = pszBytes;
    1162           0 :                     ++bind_num;
    1163             :                 }
    1164             :                 else
    1165             :                 {
    1166           0 :                     oStmt.Append("null");
    1167           0 :                     CPLFree(pszBytes);
    1168             :                 }
    1169             :             }
    1170             :             else
    1171             :             {
    1172           0 :                 oStmt.Append("null");
    1173             :             }
    1174           0 :             CPLFree(pabyData);
    1175             :         }
    1176           0 :         else if (nUploadGeometryFormat == MSSQLGEOMETRY_WKB)
    1177             :         {
    1178           0 :             const size_t nWKBLen = poGeom->WkbSize();
    1179           0 :             GByte *pabyWKB = (GByte *)VSI_MALLOC_VERBOSE(
    1180             :                 nWKBLen + 1);  // do we need the +1 ?
    1181           0 :             if (pabyWKB == nullptr)
    1182             :             {
    1183           0 :                 oStmt.Append("null");
    1184             :             }
    1185           0 :             else if (poGeom->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso) ==
    1186           0 :                          OGRERR_NONE &&
    1187           0 :                      (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY ||
    1188           0 :                       nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY))
    1189             :             {
    1190           0 :                 nWKBLenBindParameter = nWKBLen;
    1191           0 :                 int nRetCode = SQLBindParameter(
    1192           0 :                     oStmt.GetStatement(), (SQLUSMALLINT)(bind_num + 1),
    1193             :                     SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, nWKBLen,
    1194           0 :                     0, (SQLPOINTER)pabyWKB, nWKBLen, &nWKBLenBindParameter);
    1195           0 :                 if (nRetCode == SQL_SUCCESS ||
    1196             :                     nRetCode == SQL_SUCCESS_WITH_INFO)
    1197             :                 {
    1198           0 :                     if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
    1199             :                     {
    1200           0 :                         oStmt.Append("geography::STGeomFromWKB(?");
    1201           0 :                         oStmt.Appendf(",%d)", nSRSId);
    1202             :                     }
    1203             :                     else
    1204             :                     {
    1205           0 :                         oStmt.Append("geometry::STGeomFromWKB(?");
    1206           0 :                         oStmt.Appendf(",%d).MakeValid()", nSRSId);
    1207             :                     }
    1208           0 :                     bind_buffer[bind_num] = pabyWKB;
    1209           0 :                     ++bind_num;
    1210             :                 }
    1211             :                 else
    1212             :                 {
    1213           0 :                     oStmt.Append("null");
    1214           0 :                     CPLFree(pabyWKB);
    1215             :                 }
    1216             :             }
    1217             :             else
    1218             :             {
    1219           0 :                 oStmt.Append("null");
    1220           0 :                 CPLFree(pabyWKB);
    1221             :             }
    1222             :         }
    1223           0 :         else if (nUploadGeometryFormat == MSSQLGEOMETRY_WKT)
    1224             :         {
    1225           0 :             char *pszWKT = nullptr;
    1226           0 :             if (poGeom->exportToWkt(&pszWKT) == OGRERR_NONE &&
    1227           0 :                 (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY ||
    1228           0 :                  nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY))
    1229             :             {
    1230           0 :                 size_t nLen = 0;
    1231           0 :                 while (pszWKT[nLen] != '\0')
    1232           0 :                     nLen++;
    1233             : 
    1234           0 :                 int nRetCode = SQLBindParameter(
    1235           0 :                     oStmt.GetStatement(), (SQLUSMALLINT)(bind_num + 1),
    1236             :                     SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR, nLen, 0,
    1237           0 :                     (SQLPOINTER)pszWKT, 0, nullptr);
    1238           0 :                 if (nRetCode == SQL_SUCCESS ||
    1239             :                     nRetCode == SQL_SUCCESS_WITH_INFO)
    1240             :                 {
    1241           0 :                     if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
    1242             :                     {
    1243           0 :                         oStmt.Append("geography::STGeomFromText(?");
    1244           0 :                         oStmt.Appendf(",%d)", nSRSId);
    1245             :                     }
    1246             :                     else
    1247             :                     {
    1248           0 :                         oStmt.Append("geometry::STGeomFromText(?");
    1249           0 :                         oStmt.Appendf(",%d).MakeValid()", nSRSId);
    1250             :                     }
    1251           0 :                     bind_buffer[bind_num] = pszWKT;
    1252           0 :                     ++bind_num;
    1253             :                 }
    1254             :                 else
    1255             :                 {
    1256           0 :                     oStmt.Append("null");
    1257           0 :                     CPLFree(pszWKT);
    1258             :                 }
    1259             :             }
    1260             :             else
    1261             :             {
    1262           0 :                 oStmt.Append("null");
    1263           0 :                 CPLFree(pszWKT);
    1264             :             }
    1265             :         }
    1266             :         else
    1267           0 :             oStmt.Append("null");
    1268             : 
    1269           0 :         bNeedComma = TRUE;
    1270             :     }
    1271             : 
    1272             :     int i;
    1273           0 :     for (i = 0; i < nFieldCount; i++)
    1274             :     {
    1275           0 :         if (bNeedComma)
    1276           0 :             oStmt.Appendf(", [%s] = ",
    1277           0 :                           poFeatureDefn->GetFieldDefn(i)->GetNameRef());
    1278             :         else
    1279             :         {
    1280           0 :             oStmt.Appendf("[%s] = ",
    1281           0 :                           poFeatureDefn->GetFieldDefn(i)->GetNameRef());
    1282           0 :             bNeedComma = TRUE;
    1283             :         }
    1284             : 
    1285           0 :         if (!poFeature->IsFieldSetAndNotNull(i))
    1286           0 :             oStmt.Append("null");
    1287             :         else
    1288           0 :             AppendFieldValue(&oStmt, poFeature, i, &bind_num, bind_buffer);
    1289             :     }
    1290             : 
    1291             :     /* Add the WHERE clause */
    1292           0 :     oStmt.Appendf(" WHERE [%s] = " CPL_FRMT_GIB, pszFIDColumn,
    1293             :                   poFeature->GetFID());
    1294             : 
    1295             :     /* -------------------------------------------------------------------- */
    1296             :     /*      Execute the update.                                             */
    1297             :     /* -------------------------------------------------------------------- */
    1298             : 
    1299           0 :     if (!oStmt.ExecuteSQL())
    1300             :     {
    1301           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1302             :                  "Error updating feature with FID:" CPL_FRMT_GIB ", %s",
    1303           0 :                  poFeature->GetFID(), poDS->GetSession()->GetLastError());
    1304             : 
    1305           0 :         for (i = 0; i < bind_num; i++)
    1306           0 :             CPLFree(bind_buffer[i]);
    1307           0 :         CPLFree(bind_buffer);
    1308             : 
    1309           0 :         return OGRERR_FAILURE;
    1310             :     }
    1311             : 
    1312           0 :     for (i = 0; i < bind_num; i++)
    1313           0 :         CPLFree(bind_buffer[i]);
    1314           0 :     CPLFree(bind_buffer);
    1315             : 
    1316           0 :     if (oStmt.GetRowCountAffected() < 1)
    1317           0 :         return OGRERR_NON_EXISTING_FEATURE;
    1318             : 
    1319           0 :     return OGRERR_NONE;
    1320             : }
    1321             : 
    1322             : /************************************************************************/
    1323             : /*                          DeleteFeature()                             */
    1324             : /************************************************************************/
    1325             : 
    1326           0 : OGRErr OGRMSSQLSpatialTableLayer::DeleteFeature(GIntBig nFID)
    1327             : 
    1328             : {
    1329           0 :     if (!bUpdateAccess)
    1330             :     {
    1331           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1332             :                  "DeleteFeature");
    1333           0 :         return OGRERR_FAILURE;
    1334             :     }
    1335             : 
    1336           0 :     poDS->EndCopy();
    1337             : 
    1338           0 :     GetLayerDefn();
    1339             : 
    1340           0 :     if (pszFIDColumn == nullptr)
    1341             :     {
    1342           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1343             :                  "DeleteFeature() without any FID column.");
    1344           0 :         return OGRERR_FAILURE;
    1345             :     }
    1346             : 
    1347           0 :     if (nFID == OGRNullFID)
    1348             :     {
    1349           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1350             :                  "DeleteFeature() with unset FID fails.");
    1351           0 :         return OGRERR_FAILURE;
    1352             :     }
    1353             : 
    1354           0 :     ClearStatement();
    1355             : 
    1356             :     /* -------------------------------------------------------------------- */
    1357             :     /*      Drop the record with this FID.                                  */
    1358             :     /* -------------------------------------------------------------------- */
    1359           0 :     CPLODBCStatement oStatement(poDS->GetSession());
    1360             : 
    1361           0 :     oStatement.Appendf("DELETE FROM [%s].[%s] WHERE [%s] = " CPL_FRMT_GIB,
    1362             :                        pszSchemaName, pszTableName, pszFIDColumn, nFID);
    1363             : 
    1364           0 :     if (!oStatement.ExecuteSQL())
    1365             :     {
    1366           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1367             :                  "Attempt to delete feature with FID " CPL_FRMT_GIB
    1368             :                  " failed. %s",
    1369           0 :                  nFID, poDS->GetSession()->GetLastError());
    1370             : 
    1371           0 :         return OGRERR_FAILURE;
    1372             :     }
    1373             : 
    1374           0 :     if (oStatement.GetRowCountAffected() < 1)
    1375           0 :         return OGRERR_NON_EXISTING_FEATURE;
    1376             : 
    1377           0 :     return OGRERR_NONE;
    1378             : }
    1379             : 
    1380             : /************************************************************************/
    1381             : /*                           Failed()                                   */
    1382             : /************************************************************************/
    1383             : 
    1384           0 : int OGRMSSQLSpatialTableLayer::Failed(int nRetCode)
    1385             : 
    1386             : {
    1387           0 :     if (nRetCode == SQL_SUCCESS || nRetCode == SQL_SUCCESS_WITH_INFO)
    1388           0 :         return FALSE;
    1389             : 
    1390           0 :     char SQLState[6] = "";
    1391           0 :     char Msg[256] = "";
    1392           0 :     SQLINTEGER iNativeError = 0;
    1393           0 :     SQLSMALLINT iMsgLen = 0;
    1394             : 
    1395           0 :     int iRc = SQLGetDiagRec(SQL_HANDLE_ENV, hEnvBCP, 1, (SQLCHAR *)SQLState,
    1396           0 :                             &iNativeError, (SQLCHAR *)Msg, 256, &iMsgLen);
    1397           0 :     if (iRc != SQL_NO_DATA)
    1398             :     {
    1399           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1400             :                  "SQL Error SQLState=%s, NativeError=%d, Msg=%s\n", SQLState,
    1401             :                  static_cast<int>(iNativeError), Msg);
    1402             :     }
    1403             : 
    1404           0 :     return TRUE;
    1405             : }
    1406             : 
    1407             : /************************************************************************/
    1408             : /*                           Failed2()                                  */
    1409             : /************************************************************************/
    1410             : 
    1411             : #ifdef MSSQL_BCP_SUPPORTED
    1412             : int OGRMSSQLSpatialTableLayer::Failed2(int nRetCode)
    1413             : 
    1414             : {
    1415             :     if (nRetCode == SUCCEED)
    1416             :         return FALSE;
    1417             : 
    1418             :     char SQLState[6] = "";
    1419             :     char Msg[256] = "";
    1420             :     SQLINTEGER iNativeError = 0;
    1421             :     SQLSMALLINT iMsgLen = 0;
    1422             : 
    1423             :     int iRc = SQLGetDiagRec(SQL_HANDLE_DBC, hDBCBCP, 1, (SQLCHAR *)SQLState,
    1424             :                             &iNativeError, (SQLCHAR *)Msg, 256, &iMsgLen);
    1425             :     if (iRc != SQL_NO_DATA)
    1426             :     {
    1427             :         CPLError(CE_Failure, CPLE_AppDefined,
    1428             :                  "SQL Error SQLState=%s, NativeError=%d, Msg=%s\n", SQLState,
    1429             :                  static_cast<int>(iNativeError), Msg);
    1430             :     }
    1431             : 
    1432             :     return TRUE;
    1433             : }
    1434             : 
    1435             : /************************************************************************/
    1436             : /*                            InitBCP()                                 */
    1437             : /************************************************************************/
    1438             : 
    1439             : int OGRMSSQLSpatialTableLayer::InitBCP(const char *pszDSN)
    1440             : 
    1441             : {
    1442             :     /* Create a different connection for BCP upload */
    1443             :     if (Failed(SQLAllocHandle(SQL_HANDLE_ENV, nullptr, &hEnvBCP)))
    1444             :         return FALSE;
    1445             : 
    1446             :     /* Notify ODBC that this is an ODBC 3.0 app. */
    1447             :     if (Failed(SQLSetEnvAttr(hEnvBCP, SQL_ATTR_ODBC_VERSION,
    1448             :                              (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER)))
    1449             :     {
    1450             :         CloseBCP();
    1451             :         return FALSE;
    1452             :     }
    1453             : 
    1454             :     if (Failed(SQLAllocHandle(SQL_HANDLE_DBC, hEnvBCP, &hDBCBCP)))
    1455             :     {
    1456             :         CloseBCP();
    1457             :         return FALSE;
    1458             :     }
    1459             : 
    1460             :     /* set bulk copy mode */
    1461             :     if (Failed(SQLSetConnectAttr(hDBCBCP, SQL_COPT_SS_BCP, (void *)SQL_BCP_ON,
    1462             :                                  SQL_IS_INTEGER)))
    1463             :     {
    1464             :         CloseBCP();
    1465             :         return FALSE;
    1466             :     }
    1467             : 
    1468             :     Failed(SQLSetConnectAttr(hDBCBCP, SQL_ATTR_LOGIN_TIMEOUT, (void *)30,
    1469             :                              SQL_IS_INTEGER));
    1470             : 
    1471             :     SQLCHAR szOutConnString[1024];
    1472             :     SQLSMALLINT nOutConnStringLen = 0;
    1473             : 
    1474             :     if (Failed(SQLDriverConnect(hDBCBCP, nullptr, (SQLCHAR *)pszDSN,
    1475             :                                 (SQLSMALLINT)strlen(pszDSN), szOutConnString,
    1476             :                                 sizeof(szOutConnString), &nOutConnStringLen,
    1477             :                                 SQL_DRIVER_NOPROMPT)))
    1478             :     {
    1479             :         CloseBCP();
    1480             :         return FALSE;
    1481             :     }
    1482             : 
    1483             :     return TRUE;
    1484             : }
    1485             : 
    1486             : /************************************************************************/
    1487             : /*                            CloseBCP()                                */
    1488             : /************************************************************************/
    1489             : 
    1490             : void OGRMSSQLSpatialTableLayer::CloseBCP()
    1491             : 
    1492             : {
    1493             :     if (papstBindBuffer)
    1494             :     {
    1495             :         int iCol;
    1496             : 
    1497             :         int nRecNum = bcp_done(hDBCBCP);
    1498             :         if (nRecNum == -1)
    1499             :             Failed2(nRecNum);
    1500             : 
    1501             :         for (iCol = 0; iCol < nRawColumns; iCol++)
    1502             :             CPLFree(papstBindBuffer[iCol]);
    1503             :         CPLFree(papstBindBuffer);
    1504             :         papstBindBuffer = nullptr;
    1505             : 
    1506             :         if (bIdentityInsert)
    1507             :         {
    1508             :             bIdentityInsert = FALSE;
    1509             :         }
    1510             :     }
    1511             : 
    1512             :     if (hDBCBCP != nullptr)
    1513             :     {
    1514             :         CPLDebug("ODBC", "SQLDisconnect()");
    1515             :         SQLDisconnect(hDBCBCP);
    1516             :         SQLFreeHandle(SQL_HANDLE_DBC, hDBCBCP);
    1517             :         hDBCBCP = nullptr;
    1518             :     }
    1519             : 
    1520             :     if (hEnvBCP != nullptr)
    1521             :     {
    1522             :         SQLFreeHandle(SQL_HANDLE_ENV, hEnvBCP);
    1523             :         hEnvBCP = nullptr;
    1524             :     }
    1525             : }
    1526             : 
    1527             : /************************************************************************/
    1528             : /*                            CreateFeatureBCP()                        */
    1529             : /************************************************************************/
    1530             : 
    1531             : OGRErr OGRMSSQLSpatialTableLayer::CreateFeatureBCP(OGRFeature *poFeature)
    1532             : 
    1533             : {
    1534             :     int iCol;
    1535             :     int iField = 0;
    1536             : 
    1537             :     if (hDBCBCP == nullptr)
    1538             :     {
    1539             :         nBCPCount = 0;
    1540             : 
    1541             :         /* Tell the datasource we are now planning to copy data */
    1542             :         poDS->StartCopy(this);
    1543             : 
    1544             :         CPLODBCSession *poSession = poDS->GetSession();
    1545             : 
    1546             :         if (poSession->IsInTransaction())
    1547             :             poSession->CommitTransaction(); /* commit creating the table */
    1548             : 
    1549             :         /* Get the column definitions for this table. */
    1550             :         bLayerDefnNeedsRefresh = true;
    1551             :         GetLayerDefn();
    1552             :         bLayerDefnNeedsRefresh = false;
    1553             : 
    1554             :         if (!poFeatureDefn)
    1555             :             return OGRERR_FAILURE;
    1556             : 
    1557             :         if (poFeature->GetFID() != OGRNullFID && pszFIDColumn != nullptr &&
    1558             :             bIsIdentityFid)
    1559             :         {
    1560             :             bIdentityInsert = TRUE;
    1561             :         }
    1562             : 
    1563             :         if (!InitBCP(poDS->GetConnectionString()))
    1564             :             return OGRERR_FAILURE;
    1565             : 
    1566             :         /* Initialize the bulk copy */
    1567             :         if (Failed2(bcp_init(
    1568             :                 hDBCBCP, CPLSPrintf("[%s].[%s]", pszSchemaName, pszTableName),
    1569             :                 nullptr, nullptr, DB_IN)))
    1570             :         {
    1571             :             CloseBCP();
    1572             :             return OGRERR_FAILURE;
    1573             :         }
    1574             : 
    1575             :         if (bIdentityInsert)
    1576             :         {
    1577             :             if (Failed2(bcp_control(hDBCBCP, BCPKEEPIDENTITY, (void *)TRUE)))
    1578             :             {
    1579             :                 CPLError(CE_Failure, CPLE_AppDefined,
    1580             :                          "Failed to set identity insert bulk copy mode, %s.",
    1581             :                          poDS->GetSession()->GetLastError());
    1582             :                 return OGRERR_FAILURE;
    1583             :             }
    1584             :         }
    1585             : 
    1586             :         papstBindBuffer =
    1587             :             (BCPData **)CPLMalloc(sizeof(BCPData *) * (nRawColumns));
    1588             : 
    1589             :         for (iCol = 0; iCol < nRawColumns; iCol++)
    1590             :         {
    1591             :             papstBindBuffer[iCol] = nullptr;
    1592             : 
    1593             :             if (iCol == nGeomColumnIndex)
    1594             :             {
    1595             :                 papstBindBuffer[iCol] = (BCPData *)CPLMalloc(sizeof(BCPData));
    1596             :                 if (Failed2(bcp_bind(hDBCBCP,
    1597             :                                      nullptr /* data is provided later */, 0,
    1598             :                                      0 /*or any value < 8000*/, nullptr, 0,
    1599             :                                      SQLUDT, iCol + 1)))
    1600             :                     return OGRERR_FAILURE;
    1601             :             }
    1602             :             else if (iCol == nFIDColumnIndex)
    1603             :             {
    1604             :                 if (!bIdentityInsert)
    1605             :                     continue;
    1606             :                 /* bind fid column */
    1607             :                 papstBindBuffer[iCol] = (BCPData *)CPLMalloc(sizeof(BCPData));
    1608             :                 papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
    1609             : 
    1610             :                 if (Failed2(bcp_bind(
    1611             :                         hDBCBCP, (LPCBYTE)papstBindBuffer[iCol]->VarChar.pData,
    1612             :                         0, SQL_VARLEN_DATA, (LPCBYTE) "", 1, SQLVARCHAR,
    1613             :                         iCol + 1)))
    1614             :                     return OGRERR_FAILURE;
    1615             :             }
    1616             :             else if (iField < poFeatureDefn->GetFieldCount() &&
    1617             :                      iCol == panFieldOrdinals[iField])
    1618             :             {
    1619             :                 OGRFieldDefn *poFDefn = poFeatureDefn->GetFieldDefn(iField);
    1620             : 
    1621             :                 if (poFDefn->IsIgnored())
    1622             :                 {
    1623             :                     /* set null */
    1624             :                     ++iField;
    1625             :                     continue;
    1626             :                 }
    1627             : 
    1628             :                 int iSrcField = poFeature->GetFieldIndex(poFDefn->GetNameRef());
    1629             :                 if (iSrcField < 0)
    1630             :                 {
    1631             :                     ++iField;
    1632             :                     continue; /* no such field at the source */
    1633             :                 }
    1634             : 
    1635             :                 if (poFDefn->GetType() == OFTInteger)
    1636             :                 {
    1637             :                     /* int */
    1638             :                     papstBindBuffer[iCol] =
    1639             :                         (BCPData *)CPLMalloc(sizeof(BCPData));
    1640             :                     papstBindBuffer[iCol]->Integer.iIndicator =
    1641             :                         sizeof(papstBindBuffer[iCol]->Integer.Value);
    1642             : 
    1643             :                     if (Failed2(bcp_bind(
    1644             :                             hDBCBCP, (LPCBYTE)papstBindBuffer[iCol],
    1645             :                             sizeof(papstBindBuffer[iCol]->Integer.iIndicator),
    1646             :                             sizeof(papstBindBuffer[iCol]->Integer.Value),
    1647             :                             nullptr, 0, SQLINT4, iCol + 1)))
    1648             :                         return OGRERR_FAILURE;
    1649             :                 }
    1650             :                 else if (poFDefn->GetType() == OFTInteger64)
    1651             :                 {
    1652             :                     /* bigint */
    1653             :                     papstBindBuffer[iCol] =
    1654             :                         (BCPData *)CPLMalloc(sizeof(BCPData));
    1655             :                     papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
    1656             : 
    1657             :                     if (Failed2(bcp_bind(
    1658             :                             hDBCBCP,
    1659             :                             (LPCBYTE)papstBindBuffer[iCol]->VarChar.pData, 0,
    1660             :                             SQL_VARLEN_DATA, (LPCBYTE) "", 1, SQLVARCHAR,
    1661             :                             iCol + 1)))
    1662             :                         return OGRERR_FAILURE;
    1663             :                 }
    1664             :                 else if (poFDefn->GetType() == OFTReal)
    1665             :                 {
    1666             :                     /* float */
    1667             :                     /* TODO convert to DBNUMERIC */
    1668             :                     papstBindBuffer[iCol] =
    1669             :                         (BCPData *)CPLMalloc(sizeof(BCPData));
    1670             :                     papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
    1671             : 
    1672             :                     if (Failed2(bcp_bind(
    1673             :                             hDBCBCP,
    1674             :                             (LPCBYTE)papstBindBuffer[iCol]->VarChar.pData, 0,
    1675             :                             SQL_VARLEN_DATA, (LPCBYTE) "", 1, SQLVARCHAR,
    1676             :                             iCol + 1)))
    1677             :                         return OGRERR_FAILURE;
    1678             :                 }
    1679             :                 else if (poFDefn->GetType() == OFTString)
    1680             :                 {
    1681             :                     /* nvarchar */
    1682             :                     papstBindBuffer[iCol] =
    1683             :                         (BCPData *)CPLMalloc(sizeof(BCPData));
    1684             :                     papstBindBuffer[iCol]->VarChar.nSize = poFDefn->GetWidth();
    1685             :                     if (poFDefn->GetWidth() == 0)
    1686             :                     {
    1687             :                         if (Failed2(bcp_bind(
    1688             :                                 hDBCBCP, nullptr /* data is provided later */,
    1689             :                                 0, 0 /*or any value < 8000*/, nullptr, 0, 0,
    1690             :                                 iCol + 1)))
    1691             :                             return OGRERR_FAILURE;
    1692             :                     }
    1693             :                     else
    1694             :                     {
    1695             :                         if (Failed2(bcp_bind(
    1696             :                                 hDBCBCP, (LPCBYTE)papstBindBuffer[iCol],
    1697             :                                 sizeof(papstBindBuffer[iCol]->VarChar.nSize),
    1698             :                                 poFDefn->GetWidth(), nullptr, 0, SQLNVARCHAR,
    1699             :                                 iCol + 1)))
    1700             :                             return OGRERR_FAILURE;
    1701             :                     }
    1702             :                 }
    1703             :                 else if (poFDefn->GetType() == OFTDate)
    1704             :                 {
    1705             :                     /* date */
    1706             :                     papstBindBuffer[iCol] =
    1707             :                         (BCPData *)CPLMalloc(sizeof(BCPData));
    1708             :                     papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
    1709             : 
    1710             :                     if (Failed2(bcp_bind(
    1711             :                             hDBCBCP,
    1712             :                             (LPCBYTE)papstBindBuffer[iCol]->VarChar.pData, 0,
    1713             :                             SQL_VARLEN_DATA, (LPCBYTE) "", 1, SQLVARCHAR,
    1714             :                             iCol + 1)))
    1715             :                         return OGRERR_FAILURE;
    1716             :                 }
    1717             :                 else if (poFDefn->GetType() == OFTTime)
    1718             :                 {
    1719             :                     /* time(7) */
    1720             :                     papstBindBuffer[iCol] =
    1721             :                         (BCPData *)CPLMalloc(sizeof(BCPData));
    1722             :                     papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
    1723             : 
    1724             :                     if (Failed2(bcp_bind(
    1725             :                             hDBCBCP,
    1726             :                             (LPCBYTE)papstBindBuffer[iCol]->VarChar.pData, 0,
    1727             :                             SQL_VARLEN_DATA, (LPCBYTE) "", 1, SQLVARCHAR,
    1728             :                             iCol + 1)))
    1729             :                         return OGRERR_FAILURE;
    1730             :                 }
    1731             :                 else if (poFDefn->GetType() == OFTDateTime)
    1732             :                 {
    1733             :                     /* datetime */
    1734             :                     papstBindBuffer[iCol] =
    1735             :                         (BCPData *)CPLMalloc(sizeof(BCPData));
    1736             :                     papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
    1737             : 
    1738             :                     if (Failed2(bcp_bind(
    1739             :                             hDBCBCP,
    1740             :                             (LPCBYTE)papstBindBuffer[iCol]->VarChar.pData, 0,
    1741             :                             SQL_VARLEN_DATA, (LPCBYTE) "", 1, SQLVARCHAR,
    1742             :                             iCol + 1)))
    1743             :                         return OGRERR_FAILURE;
    1744             :                 }
    1745             :                 else if (poFDefn->GetType() == OFTBinary)
    1746             :                 {
    1747             :                     /* image */
    1748             :                     papstBindBuffer[iCol] =
    1749             :                         (BCPData *)CPLMalloc(sizeof(BCPData));
    1750             :                     if (Failed2(bcp_bind(hDBCBCP,
    1751             :                                          nullptr /* data is provided later */,
    1752             :                                          0, 0 /*or any value < 8000*/, nullptr,
    1753             :                                          0, 0, iCol + 1)))
    1754             :                         return OGRERR_FAILURE;
    1755             :                 }
    1756             :                 else
    1757             :                 {
    1758             :                     CPLError(
    1759             :                         CE_Failure, CPLE_NotSupported,
    1760             :                         "Filed %s with type %s is not supported for bulk "
    1761             :                         "insert.",
    1762             :                         poFDefn->GetNameRef(),
    1763             :                         OGRFieldDefn::GetFieldTypeName(poFDefn->GetType()));
    1764             : 
    1765             :                     return OGRERR_FAILURE;
    1766             :                 }
    1767             : 
    1768             :                 ++iField;
    1769             :             }
    1770             :         }
    1771             :     }
    1772             : 
    1773             :     /* do bulk insert here */
    1774             : 
    1775             :     /* prepare data to variables */
    1776             :     iField = 0;
    1777             :     for (iCol = 0; iCol < nRawColumns; iCol++)
    1778             :     {
    1779             :         if (iCol == nGeomColumnIndex)
    1780             :         {
    1781             :             OGRGeometry *poGeom = poFeature->GetGeometryRef();
    1782             :             if (poGeom != nullptr)
    1783             :             {
    1784             :                 /* prepare geometry */
    1785             :                 if (bUseGeometryValidation)
    1786             :                 {
    1787             :                     OGRMSSQLGeometryValidator oValidator(poGeom,
    1788             :                                                          nGeomColumnType);
    1789             :                     if (!oValidator.IsValid())
    1790             :                     {
    1791             :                         oValidator.MakeValid(poGeom);
    1792             :                         CPLError(CE_Warning, CPLE_NotSupported,
    1793             :                                  "Geometry with FID = " CPL_FRMT_GIB
    1794             :                                  " has been modified to valid geometry.",
    1795             :                                  poFeature->GetFID());
    1796             :                     }
    1797             :                 }
    1798             : 
    1799             :                 int nOutgoingSRSId = 0;
    1800             :                 // Use the SRID specified by the provided feature's geometry, if
    1801             :                 // its spatial-reference system is known; otherwise, use the
    1802             :                 // SRID associated with the table
    1803             :                 const OGRSpatialReference *poFeatureSRS =
    1804             :                     poGeom->getSpatialReference();
    1805             :                 if (poFeatureSRS)
    1806             :                     nOutgoingSRSId = poDS->FetchSRSId(poFeatureSRS);
    1807             :                 if (nOutgoingSRSId <= 0)
    1808             :                     nOutgoingSRSId = nSRSId;
    1809             : 
    1810             :                 OGRMSSQLGeometryWriter poWriter(poGeom, nGeomColumnType,
    1811             :                                                 nOutgoingSRSId);
    1812             :                 papstBindBuffer[iCol]->RawData.nSize = poWriter.GetDataLen();
    1813             :                 papstBindBuffer[iCol]->RawData.pData = (GByte *)CPLMalloc(
    1814             :                     papstBindBuffer[iCol]->RawData.nSize + 1);
    1815             : 
    1816             :                 if (poWriter.WriteSqlGeometry(
    1817             :                         papstBindBuffer[iCol]->RawData.pData,
    1818             :                         (int)papstBindBuffer[iCol]->RawData.nSize) !=
    1819             :                     OGRERR_NONE)
    1820             :                     return OGRERR_FAILURE;
    1821             : 
    1822             :                 /* set data length */
    1823             :                 if (Failed2(bcp_collen(
    1824             :                         hDBCBCP, (DBINT)papstBindBuffer[iCol]->RawData.nSize,
    1825             :                         iCol + 1)))
    1826             :                     return OGRERR_FAILURE;
    1827             :             }
    1828             :             else
    1829             :             {
    1830             :                 /* set NULL */
    1831             :                 papstBindBuffer[iCol]->RawData.nSize = SQL_NULL_DATA;
    1832             :                 if (Failed2(bcp_collen(hDBCBCP, SQL_NULL_DATA, iCol + 1)))
    1833             :                     return OGRERR_FAILURE;
    1834             :             }
    1835             :         }
    1836             :         else if (iCol == nFIDColumnIndex)
    1837             :         {
    1838             :             if (!bIdentityInsert)
    1839             :                 continue;
    1840             : 
    1841             :             GIntBig nFID = poFeature->GetFID();
    1842             :             if (nFID == OGRNullFID)
    1843             :             {
    1844             :                 papstBindBuffer[iCol]->VarChar.nSize = SQL_NULL_DATA;
    1845             :                 /* set NULL */
    1846             :                 if (Failed2(bcp_collen(hDBCBCP, SQL_NULL_DATA, iCol + 1)))
    1847             :                     return OGRERR_FAILURE;
    1848             :             }
    1849             :             else
    1850             :             {
    1851             :                 papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
    1852             :                 snprintf((char *)papstBindBuffer[iCol]->VarChar.pData, 8000,
    1853             :                          CPL_FRMT_GIB, nFID);
    1854             : 
    1855             :                 if (Failed2(bcp_collen(hDBCBCP, SQL_VARLEN_DATA, iCol + 1)))
    1856             :                     return OGRERR_FAILURE;
    1857             :             }
    1858             :         }
    1859             :         else if (iField < poFeatureDefn->GetFieldCount() &&
    1860             :                  iCol == panFieldOrdinals[iField])
    1861             :         {
    1862             :             OGRFieldDefn *poFDefn = poFeatureDefn->GetFieldDefn(iField);
    1863             : 
    1864             :             if (papstBindBuffer[iCol] == nullptr)
    1865             :             {
    1866             :                 ++iField;
    1867             :                 continue; /* column requires no data */
    1868             :             }
    1869             : 
    1870             :             if (poFDefn->GetType() == OFTInteger)
    1871             :             {
    1872             :                 /* int */
    1873             :                 if (!poFeature->IsFieldSetAndNotNull(iField))
    1874             :                     papstBindBuffer[iCol]->Integer.iIndicator = SQL_NULL_DATA;
    1875             :                 else
    1876             :                 {
    1877             :                     papstBindBuffer[iCol]->Integer.iIndicator =
    1878             :                         sizeof(papstBindBuffer[iCol]->Integer.Value);
    1879             :                     papstBindBuffer[iCol]->Integer.Value =
    1880             :                         poFeature->GetFieldAsInteger(iField);
    1881             :                 }
    1882             :             }
    1883             :             else if (poFDefn->GetType() == OFTInteger64)
    1884             :             {
    1885             :                 /* bigint */
    1886             :                 if (!poFeature->IsFieldSetAndNotNull(iField))
    1887             :                 {
    1888             :                     papstBindBuffer[iCol]->VarChar.nSize = SQL_NULL_DATA;
    1889             :                     /* set NULL */
    1890             :                     if (Failed2(bcp_collen(hDBCBCP, SQL_NULL_DATA, iCol + 1)))
    1891             :                         return OGRERR_FAILURE;
    1892             :                 }
    1893             :                 else
    1894             :                 {
    1895             :                     papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
    1896             :                     snprintf((char *)papstBindBuffer[iCol]->VarChar.pData, 8000,
    1897             :                              "%s", poFeature->GetFieldAsString(iField));
    1898             : 
    1899             :                     if (Failed2(bcp_collen(hDBCBCP, SQL_VARLEN_DATA, iCol + 1)))
    1900             :                         return OGRERR_FAILURE;
    1901             :                 }
    1902             :             }
    1903             :             else if (poFDefn->GetType() == OFTReal)
    1904             :             {
    1905             :                 /* float */
    1906             :                 if (!poFeature->IsFieldSetAndNotNull(iField))
    1907             :                 {
    1908             :                     papstBindBuffer[iCol]->VarChar.nSize = SQL_NULL_DATA;
    1909             :                     /* set NULL */
    1910             :                     if (Failed2(bcp_collen(hDBCBCP, SQL_NULL_DATA, iCol + 1)))
    1911             :                         return OGRERR_FAILURE;
    1912             :                 }
    1913             :                 else
    1914             :                 {
    1915             :                     papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
    1916             :                     snprintf((char *)papstBindBuffer[iCol]->VarChar.pData, 8000,
    1917             :                              "%s", poFeature->GetFieldAsString(iField));
    1918             : 
    1919             :                     if (Failed2(bcp_collen(hDBCBCP, SQL_VARLEN_DATA, iCol + 1)))
    1920             :                         return OGRERR_FAILURE;
    1921             :                 }
    1922             :             }
    1923             :             else if (poFDefn->GetType() == OFTString)
    1924             :             {
    1925             :                 /* nvarchar */
    1926             :                 if (poFDefn->GetWidth() != 0)
    1927             :                 {
    1928             :                     if (!poFeature->IsFieldSetAndNotNull(iField))
    1929             :                     {
    1930             :                         papstBindBuffer[iCol]->VarChar.nSize = SQL_NULL_DATA;
    1931             :                         if (Failed2(
    1932             :                                 bcp_collen(hDBCBCP, SQL_NULL_DATA, iCol + 1)))
    1933             :                             return OGRERR_FAILURE;
    1934             :                     }
    1935             :                     else
    1936             :                     {
    1937             : 
    1938             :                         wchar_t *buffer = CPLRecodeToWChar(
    1939             :                             poFeature->GetFieldAsString(iField), CPL_ENC_UTF8,
    1940             :                             CPL_ENC_UCS2);
    1941             :                         const auto nLen = wcslen(buffer);
    1942             :                         papstBindBuffer[iCol]->VarChar.nSize =
    1943             :                             (SQLLEN)nLen * sizeof(GUInt16);
    1944             : #if WCHAR_MAX > 0xFFFFu
    1945             :                         // Shorten each character to a two-byte value, as
    1946             :                         // expected by the ODBC driver
    1947             :                         GUInt16 *panBuffer =
    1948             :                             reinterpret_cast<GUInt16 *>(buffer);
    1949             :                         for (unsigned int nIndex = 1; nIndex <= nLen;
    1950             :                              nIndex += 1)
    1951             :                             panBuffer[nIndex] =
    1952             :                                 static_cast<GUInt16>(buffer[nIndex]);
    1953             : #endif
    1954             :                         memcpy(papstBindBuffer[iCol]->VarChar.pData, buffer,
    1955             :                                papstBindBuffer[iCol]->VarChar.nSize +
    1956             :                                    sizeof(GUInt16));
    1957             :                         CPLFree(buffer);
    1958             : 
    1959             :                         if (Failed2(bcp_collen(
    1960             :                                 hDBCBCP,
    1961             :                                 (DBINT)papstBindBuffer[iCol]->VarChar.nSize,
    1962             :                                 iCol + 1)))
    1963             :                             return OGRERR_FAILURE;
    1964             :                     }
    1965             :                 }
    1966             :             }
    1967             :             else if (poFDefn->GetType() == OFTDate)
    1968             :             {
    1969             :                 /* date */
    1970             :                 if (!poFeature->IsFieldSetAndNotNull(iField))
    1971             :                 {
    1972             :                     papstBindBuffer[iCol]->VarChar.nSize = SQL_NULL_DATA;
    1973             :                     /* set NULL */
    1974             :                     if (Failed2(bcp_collen(hDBCBCP, SQL_NULL_DATA, iCol + 1)))
    1975             :                         return OGRERR_FAILURE;
    1976             :                 }
    1977             :                 else
    1978             :                 {
    1979             :                     int pnYear;
    1980             :                     int pnMonth;
    1981             :                     int pnDay;
    1982             :                     int pnHour;
    1983             :                     int pnMinute;
    1984             :                     float pfSecond;
    1985             :                     int pnTZFlag;
    1986             : 
    1987             :                     poFeature->GetFieldAsDateTime(iField, &pnYear, &pnMonth,
    1988             :                                                   &pnDay, &pnHour, &pnMinute,
    1989             :                                                   &pfSecond, &pnTZFlag);
    1990             : 
    1991             :                     papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
    1992             :                     snprintf((char *)papstBindBuffer[iCol]->VarChar.pData, 8000,
    1993             :                              "%4d-%02d-%02d %02d:%02d:%06.3f", pnYear, pnMonth,
    1994             :                              pnDay, pnHour, pnMinute, pfSecond);
    1995             :                     if (Failed2(bcp_collen(hDBCBCP, SQL_VARLEN_DATA, iCol + 1)))
    1996             :                         return OGRERR_FAILURE;
    1997             :                 }
    1998             :             }
    1999             :             else if (poFDefn->GetType() == OFTTime)
    2000             :             {
    2001             :                 /* time(7) */
    2002             :                 if (!poFeature->IsFieldSetAndNotNull(iField))
    2003             :                 {
    2004             :                     papstBindBuffer[iCol]->VarChar.nSize = SQL_NULL_DATA;
    2005             :                     /* set NULL */
    2006             :                     if (Failed2(bcp_collen(hDBCBCP, SQL_NULL_DATA, iCol + 1)))
    2007             :                         return OGRERR_FAILURE;
    2008             :                 }
    2009             :                 else
    2010             :                 {
    2011             :                     int pnYear;
    2012             :                     int pnMonth;
    2013             :                     int pnDay;
    2014             :                     int pnHour;
    2015             :                     int pnMinute;
    2016             :                     float pfSecond;
    2017             :                     int pnTZFlag;
    2018             : 
    2019             :                     poFeature->GetFieldAsDateTime(iField, &pnYear, &pnMonth,
    2020             :                                                   &pnDay, &pnHour, &pnMinute,
    2021             :                                                   &pfSecond, &pnTZFlag);
    2022             : 
    2023             :                     papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
    2024             :                     snprintf((char *)papstBindBuffer[iCol]->VarChar.pData, 8000,
    2025             :                              "%4d-%02d-%02d %02d:%02d:%06.3f", pnYear, pnMonth,
    2026             :                              pnDay, pnHour, pnMinute, pfSecond);
    2027             :                     if (Failed2(bcp_collen(hDBCBCP, SQL_VARLEN_DATA, iCol + 1)))
    2028             :                         return OGRERR_FAILURE;
    2029             :                 }
    2030             :             }
    2031             :             else if (poFDefn->GetType() == OFTDateTime)
    2032             :             {
    2033             :                 /* datetime */
    2034             :                 if (!poFeature->IsFieldSetAndNotNull(iField))
    2035             :                 {
    2036             :                     papstBindBuffer[iCol]->VarChar.nSize = SQL_NULL_DATA;
    2037             :                     /* set NULL */
    2038             :                     if (Failed2(bcp_collen(hDBCBCP, SQL_NULL_DATA, iCol + 1)))
    2039             :                         return OGRERR_FAILURE;
    2040             :                 }
    2041             :                 else
    2042             :                 {
    2043             :                     int pnYear;
    2044             :                     int pnMonth;
    2045             :                     int pnDay;
    2046             :                     int pnHour;
    2047             :                     int pnMinute;
    2048             :                     float pfSecond;
    2049             :                     int pnTZFlag;
    2050             : 
    2051             :                     poFeature->GetFieldAsDateTime(iField, &pnYear, &pnMonth,
    2052             :                                                   &pnDay, &pnHour, &pnMinute,
    2053             :                                                   &pfSecond, &pnTZFlag);
    2054             : 
    2055             :                     papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
    2056             :                     snprintf((char *)papstBindBuffer[iCol]->VarChar.pData, 8000,
    2057             :                              "%4d-%02d-%02d %02d:%02d:%06.3f", pnYear, pnMonth,
    2058             :                              pnDay, pnHour, pnMinute, pfSecond);
    2059             : 
    2060             :                     if (Failed2(bcp_collen(hDBCBCP, SQL_VARLEN_DATA, iCol + 1)))
    2061             :                         return OGRERR_FAILURE;
    2062             :                 }
    2063             :             }
    2064             :             else if (poFDefn->GetType() == OFTBinary)
    2065             :             {
    2066             :                 if (!poFeature->IsFieldSetAndNotNull(iField))
    2067             :                 {
    2068             :                     papstBindBuffer[iCol]->RawData.nSize = SQL_NULL_DATA;
    2069             :                     /* set NULL */
    2070             :                     if (Failed2(bcp_collen(hDBCBCP, SQL_NULL_DATA, iCol + 1)))
    2071             :                         return OGRERR_FAILURE;
    2072             :                 }
    2073             :                 else
    2074             :                 {
    2075             :                     /* image */
    2076             :                     int nLen;
    2077             :                     papstBindBuffer[iCol]->RawData.pData =
    2078             :                         poFeature->GetFieldAsBinary(iField, &nLen);
    2079             :                     papstBindBuffer[iCol]->RawData.nSize = nLen;
    2080             : 
    2081             :                     /* set data length */
    2082             :                     if (Failed2(bcp_collen(
    2083             :                             hDBCBCP,
    2084             :                             (DBINT)papstBindBuffer[iCol]->RawData.nSize,
    2085             :                             iCol + 1)))
    2086             :                         return OGRERR_FAILURE;
    2087             :                 }
    2088             :             }
    2089             :             else
    2090             :             {
    2091             :                 CPLError(
    2092             :                     CE_Failure, CPLE_NotSupported,
    2093             :                     "Filed %s with type %s is not supported for bulk insert.",
    2094             :                     poFDefn->GetNameRef(),
    2095             :                     OGRFieldDefn::GetFieldTypeName(poFDefn->GetType()));
    2096             : 
    2097             :                 return OGRERR_FAILURE;
    2098             :             }
    2099             : 
    2100             :             ++iField;
    2101             :         }
    2102             :     }
    2103             : 
    2104             :     /* send row */
    2105             :     if (Failed2(bcp_sendrow(hDBCBCP)))
    2106             :         return OGRERR_FAILURE;
    2107             : 
    2108             :     /* send dynamic data */
    2109             :     iField = 0;
    2110             :     for (iCol = 0; iCol < nRawColumns; iCol++)
    2111             :     {
    2112             :         if (iCol == nGeomColumnIndex)
    2113             :         {
    2114             :             if (papstBindBuffer[iCol]->RawData.nSize != SQL_NULL_DATA)
    2115             :             {
    2116             :                 if (Failed2(bcp_moretext(
    2117             :                         hDBCBCP, (DBINT)papstBindBuffer[iCol]->RawData.nSize,
    2118             :                         papstBindBuffer[iCol]->RawData.pData)))
    2119             :                 {
    2120             :                 }
    2121             :                 CPLFree(papstBindBuffer[iCol]->RawData.pData);
    2122             :                 if (Failed2(bcp_moretext(hDBCBCP, 0, nullptr)))
    2123             :                 {
    2124             :                 }
    2125             :             }
    2126             :             else
    2127             :             {
    2128             :                 if (Failed2(bcp_moretext(hDBCBCP, SQL_NULL_DATA, nullptr)))
    2129             :                 {
    2130             :                 }
    2131             :             }
    2132             :         }
    2133             :         else if (iCol == nFIDColumnIndex)
    2134             :         {
    2135             :             /* TODO */
    2136             :             continue;
    2137             :         }
    2138             :         else if (iField < poFeatureDefn->GetFieldCount() &&
    2139             :                  iCol == panFieldOrdinals[iField])
    2140             :         {
    2141             :             OGRFieldDefn *poFDefn = poFeatureDefn->GetFieldDefn(iField);
    2142             : 
    2143             :             if (poFDefn->GetType() == OFTString)
    2144             :             {
    2145             :                 if (poFDefn->GetWidth() == 0)
    2146             :                 {
    2147             :                     if (poFeature->IsFieldSetAndNotNull(iField))
    2148             :                     {
    2149             :                         const char *pszStr =
    2150             :                             poFeature->GetFieldAsString(iField);
    2151             :                         if (pszStr[0] != 0)
    2152             :                         {
    2153             :                             wchar_t *buffer = CPLRecodeToWChar(
    2154             :                                 poFeature->GetFieldAsString(iField),
    2155             :                                 CPL_ENC_UTF8, CPL_ENC_UCS2);
    2156             :                             const auto nLen = wcslen(buffer);
    2157             :                             papstBindBuffer[iCol]->VarChar.nSize =
    2158             :                                 (SQLLEN)nLen * sizeof(GUInt16);
    2159             : #if WCHAR_MAX > 0xFFFFu
    2160             :                             // Shorten each character to a two-byte value, as
    2161             :                             // expected by the ODBC driver
    2162             :                             GUInt16 *panBuffer =
    2163             :                                 reinterpret_cast<GUInt16 *>(buffer);
    2164             :                             for (unsigned int nIndex = 1; nIndex <= nLen;
    2165             :                                  nIndex += 1)
    2166             :                                 panBuffer[nIndex] =
    2167             :                                     static_cast<GUInt16>(buffer[nIndex]);
    2168             : #endif
    2169             :                             if (Failed2(bcp_moretext(
    2170             :                                     hDBCBCP,
    2171             :                                     (DBINT)papstBindBuffer[iCol]->VarChar.nSize,
    2172             :                                     (LPCBYTE)buffer)))
    2173             :                             {
    2174             :                             }
    2175             : 
    2176             :                             CPLFree(buffer);
    2177             :                         }
    2178             : 
    2179             :                         if (Failed2(bcp_moretext(hDBCBCP, 0, nullptr)))
    2180             :                         {
    2181             :                         }
    2182             :                     }
    2183             :                     else
    2184             :                     {
    2185             :                         if (Failed2(
    2186             :                                 bcp_moretext(hDBCBCP, SQL_NULL_DATA, nullptr)))
    2187             :                         {
    2188             :                         }
    2189             :                     }
    2190             :                 }
    2191             :             }
    2192             :             else if (poFDefn->GetType() == OFTBinary)
    2193             :             {
    2194             :                 if (papstBindBuffer[iCol]->RawData.nSize != SQL_NULL_DATA)
    2195             :                 {
    2196             :                     if (papstBindBuffer[iCol]->RawData.nSize > 0)
    2197             :                     {
    2198             :                         if (Failed2(bcp_moretext(
    2199             :                                 hDBCBCP,
    2200             :                                 (DBINT)papstBindBuffer[iCol]->RawData.nSize,
    2201             :                                 papstBindBuffer[iCol]->RawData.pData)))
    2202             :                         {
    2203             :                         }
    2204             :                     }
    2205             :                     else
    2206             :                     {
    2207             :                         Failed2(bcp_moretext(hDBCBCP, 0, nullptr));
    2208             :                     }
    2209             :                 }
    2210             :                 else
    2211             :                 {
    2212             :                     if (Failed2(bcp_moretext(hDBCBCP, SQL_NULL_DATA, nullptr)))
    2213             :                     {
    2214             :                     }
    2215             :                 }
    2216             :             }
    2217             :             ++iField;
    2218             :         }
    2219             :     }
    2220             : 
    2221             :     if (++nBCPCount >= nBCPSize)
    2222             :     {
    2223             :         /* commit */
    2224             :         int nRecNum = bcp_batch(hDBCBCP);
    2225             :         if (nRecNum == -1)
    2226             :             Failed2(nRecNum);
    2227             : 
    2228             :         nBCPCount = 0;
    2229             :     }
    2230             : 
    2231             :     return OGRERR_NONE;
    2232             : }
    2233             : #endif /* MSSQL_BCP_SUPPORTED */
    2234             : 
    2235             : /************************************************************************/
    2236             : /*                           ICreateFeature()                            */
    2237             : /************************************************************************/
    2238             : 
    2239           0 : OGRErr OGRMSSQLSpatialTableLayer::ICreateFeature(OGRFeature *poFeature)
    2240             : 
    2241             : {
    2242           0 :     if (!bUpdateAccess)
    2243             :     {
    2244           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    2245             :                  "CreateFeature");
    2246           0 :         return OGRERR_FAILURE;
    2247             :     }
    2248             : 
    2249           0 :     GetLayerDefn();
    2250             : 
    2251           0 :     if (nullptr == poFeature)
    2252             :     {
    2253           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2254             :                  "NULL pointer to OGRFeature passed to CreateFeature().");
    2255           0 :         return OGRERR_FAILURE;
    2256             :     }
    2257             : 
    2258             : #if (ODBCVER >= 0x0300) && defined(MSSQL_BCP_SUPPORTED)
    2259             :     if (bUseCopy && !m_bHasUUIDColumn)
    2260             :     {
    2261             :         return CreateFeatureBCP(poFeature);
    2262             :     }
    2263             : #endif
    2264             : 
    2265           0 :     ClearStatement();
    2266             : 
    2267           0 :     CPLODBCSession *poSession = poDS->GetSession();
    2268             : 
    2269             :     /* the fid values are retrieved from the source layer */
    2270           0 :     CPLODBCStatement oStatement(poSession);
    2271             : 
    2272           0 :     if (poFeature->GetFID() != OGRNullFID && pszFIDColumn != nullptr &&
    2273           0 :         bIsIdentityFid)
    2274           0 :         oStatement.Appendf("SET IDENTITY_INSERT [%s].[%s] ON;", pszSchemaName,
    2275             :                            pszTableName);
    2276             : 
    2277             :     /* -------------------------------------------------------------------- */
    2278             :     /*      Form the INSERT command.                                        */
    2279             :     /* -------------------------------------------------------------------- */
    2280             : 
    2281           0 :     oStatement.Appendf("INSERT INTO [%s].[%s] ", pszSchemaName, pszTableName);
    2282             : 
    2283           0 :     OGRGeometry *poGeom = poFeature->GetGeometryRef();
    2284           0 :     GIntBig nFID = poFeature->GetFID();
    2285           0 :     if (bUseGeometryValidation && poGeom != nullptr)
    2286             :     {
    2287           0 :         OGRMSSQLGeometryValidator oValidator(poGeom, nGeomColumnType);
    2288           0 :         if (!oValidator.IsValid())
    2289             :         {
    2290           0 :             oValidator.MakeValid(poGeom);
    2291           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    2292             :                      "Geometry with FID = " CPL_FRMT_GIB
    2293             :                      " has been modified to valid geometry.",
    2294             :                      poFeature->GetFID());
    2295             :         }
    2296             :     }
    2297             : 
    2298           0 :     int bNeedComma = FALSE;
    2299             : 
    2300           0 :     if (poGeom != nullptr && pszGeomColumn != nullptr)
    2301             :     {
    2302           0 :         oStatement.Append("([");
    2303           0 :         oStatement.Append(pszGeomColumn);
    2304           0 :         oStatement.Append("]");
    2305           0 :         bNeedComma = TRUE;
    2306             :     }
    2307             : 
    2308           0 :     if (nFID != OGRNullFID && pszFIDColumn != nullptr)
    2309             :     {
    2310           0 :         if (!CPL_INT64_FITS_ON_INT32(nFID) &&
    2311           0 :             GetMetadataItem(OLMD_FID64) == nullptr)
    2312             :         {
    2313             :             /* MSSQL server doesn't support modifying pk columns without
    2314             :              * recreating the field */
    2315           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2316             :                      "Failed to create feature with large integer fid. "
    2317             :                      "The FID64 layer creation option should be used.");
    2318             : 
    2319           0 :             return OGRERR_FAILURE;
    2320             :         }
    2321             : 
    2322           0 :         if (bNeedComma)
    2323           0 :             oStatement.Appendf(", [%s]", pszFIDColumn);
    2324             :         else
    2325             :         {
    2326           0 :             oStatement.Appendf("([%s]", pszFIDColumn);
    2327           0 :             bNeedComma = TRUE;
    2328             :         }
    2329             :     }
    2330             : 
    2331           0 :     int nFieldCount = poFeatureDefn->GetFieldCount();
    2332             : 
    2333           0 :     int bind_num = 0;
    2334           0 :     void **bind_buffer = (void **)CPLMalloc(sizeof(void *) * (nFieldCount + 1));
    2335             : #ifdef SQL_SS_UDT
    2336             :     SQLLEN *bind_datalen =
    2337             :         (SQLLEN *)CPLMalloc(sizeof(SQLLEN) * (nFieldCount + 1));
    2338             : #endif
    2339             : 
    2340             :     int i;
    2341           0 :     for (i = 0; i < nFieldCount; i++)
    2342             :     {
    2343           0 :         if (!poFeature->IsFieldSetAndNotNull(i))
    2344           0 :             continue;
    2345             : 
    2346           0 :         if (bNeedComma)
    2347           0 :             oStatement.Appendf(", [%s]",
    2348           0 :                                poFeatureDefn->GetFieldDefn(i)->GetNameRef());
    2349             :         else
    2350             :         {
    2351           0 :             oStatement.Appendf("([%s]",
    2352           0 :                                poFeatureDefn->GetFieldDefn(i)->GetNameRef());
    2353           0 :             bNeedComma = TRUE;
    2354             :         }
    2355             :     }
    2356             : 
    2357             :     SQLLEN nWKBLenBindParameter;
    2358           0 :     if (oStatement.GetCommand()[strlen(oStatement.GetCommand()) - 1] != ']')
    2359             :     {
    2360             :         /* no fields were added */
    2361             : 
    2362           0 :         if (nFID == OGRNullFID && pszFIDColumn != nullptr &&
    2363           0 :             (bIsIdentityFid || poDS->AlwaysOutputFid()))
    2364           0 :             oStatement.Appendf(" OUTPUT INSERTED.[%s] DEFAULT VALUES;",
    2365           0 :                                GetFIDColumn());
    2366             :         else
    2367           0 :             oStatement.Appendf("DEFAULT VALUES;");
    2368             :     }
    2369             :     else
    2370             :     {
    2371             :         /* prepend VALUES section */
    2372           0 :         if (nFID == OGRNullFID && pszFIDColumn != nullptr &&
    2373           0 :             (bIsIdentityFid || poDS->AlwaysOutputFid()))
    2374           0 :             oStatement.Appendf(") OUTPUT INSERTED.[%s] VALUES (",
    2375           0 :                                GetFIDColumn());
    2376             :         else
    2377           0 :             oStatement.Appendf(") VALUES (");
    2378             : 
    2379             :         /* Set the geometry */
    2380           0 :         bNeedComma = FALSE;
    2381           0 :         if (poGeom != nullptr && pszGeomColumn != nullptr)
    2382             :         {
    2383           0 :             int nOutgoingSRSId = 0;
    2384             : 
    2385             :             // Use the SRID specified by the provided feature's geometry, if
    2386             :             // its spatial-reference system is known; otherwise, use the SRID
    2387             :             // associated with the table
    2388             :             const OGRSpatialReference *poFeatureSRS =
    2389           0 :                 poGeom->getSpatialReference();
    2390           0 :             if (poFeatureSRS)
    2391           0 :                 nOutgoingSRSId = poDS->FetchSRSId(poFeatureSRS);
    2392           0 :             if (nOutgoingSRSId <= 0)
    2393           0 :                 nOutgoingSRSId = nSRSId;
    2394             : 
    2395           0 :             if (nUploadGeometryFormat == MSSQLGEOMETRY_NATIVE)
    2396             :             {
    2397             : #ifdef SQL_SS_UDT
    2398             :                 OGRMSSQLGeometryWriter poWriter(poGeom, nGeomColumnType,
    2399             :                                                 nOutgoingSRSId);
    2400             :                 bind_datalen[bind_num] = poWriter.GetDataLen();
    2401             :                 GByte *pabyData =
    2402             :                     (GByte *)CPLMalloc(bind_datalen[bind_num] + 1);
    2403             :                 if (poWriter.WriteSqlGeometry(
    2404             :                         pabyData, (int)bind_datalen[bind_num]) == OGRERR_NONE)
    2405             :                 {
    2406             :                     SQLHANDLE ipd;
    2407             :                     if ((!poSession->Failed(SQLBindParameter(
    2408             :                             oStatement.GetStatement(),
    2409             :                             (SQLUSMALLINT)(bind_num + 1), SQL_PARAM_INPUT,
    2410             :                             SQL_C_BINARY, SQL_SS_UDT, SQL_SS_LENGTH_UNLIMITED,
    2411             :                             0, (SQLPOINTER)pabyData, bind_datalen[bind_num],
    2412             :                             (SQLLEN *)&bind_datalen[bind_num]))) &&
    2413             :                         (!poSession->Failed(SQLGetStmtAttr(
    2414             :                             oStatement.GetStatement(), SQL_ATTR_IMP_PARAM_DESC,
    2415             :                             &ipd, 0, nullptr))) &&
    2416             :                         (!poSession->Failed(SQLSetDescField(
    2417             :                             ipd, 1, SQL_CA_SS_UDT_TYPE_NAME,
    2418             :                             const_cast<char *>(nGeomColumnType ==
    2419             :                                                        MSSQLCOLTYPE_GEOGRAPHY
    2420             :                                                    ? "geography"
    2421             :                                                    : "geometry"),
    2422             :                             SQL_NTS))))
    2423             :                     {
    2424             :                         oStatement.Append("?");
    2425             :                         bind_buffer[bind_num] = pabyData;
    2426             :                         ++bind_num;
    2427             :                     }
    2428             :                     else
    2429             :                     {
    2430             :                         oStatement.Append("null");
    2431             :                         CPLFree(pabyData);
    2432             :                     }
    2433             :                 }
    2434             :                 else
    2435             :                 {
    2436             :                     oStatement.Append("null");
    2437             :                     CPLFree(pabyData);
    2438             :                 }
    2439             : #else
    2440           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2441             :                          "Native geometry upload is not supported");
    2442             : 
    2443             :                 // No need to free bind_buffer[i] since bind_num == 0 in that
    2444             :                 // branch
    2445           0 :                 CPLFree(bind_buffer);
    2446             : 
    2447           0 :                 return OGRERR_FAILURE;
    2448             : #endif
    2449             :                 // CPLFree(pabyData);
    2450             :             }
    2451           0 :             else if (nUploadGeometryFormat == MSSQLGEOMETRY_WKB)
    2452             :             {
    2453           0 :                 const size_t nWKBLen = poGeom->WkbSize();
    2454           0 :                 GByte *pabyWKB = (GByte *)VSI_MALLOC_VERBOSE(
    2455             :                     nWKBLen + 1);  // do we need the +1 ?
    2456           0 :                 if (pabyWKB == nullptr)
    2457             :                 {
    2458           0 :                     oStatement.Append("null");
    2459             :                 }
    2460           0 :                 else if (poGeom->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso) ==
    2461           0 :                              OGRERR_NONE &&
    2462           0 :                          (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY ||
    2463           0 :                           nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY))
    2464             :                 {
    2465           0 :                     nWKBLenBindParameter = nWKBLen;
    2466           0 :                     int nRetCode = SQLBindParameter(
    2467           0 :                         oStatement.GetStatement(), (SQLUSMALLINT)(bind_num + 1),
    2468             :                         SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY,
    2469             :                         nWKBLen, 0, (SQLPOINTER)pabyWKB, nWKBLen,
    2470           0 :                         &nWKBLenBindParameter);
    2471           0 :                     if (nRetCode == SQL_SUCCESS ||
    2472             :                         nRetCode == SQL_SUCCESS_WITH_INFO)
    2473             :                     {
    2474           0 :                         if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
    2475             :                         {
    2476           0 :                             oStatement.Append("geography::STGeomFromWKB(?");
    2477           0 :                             oStatement.Appendf(",%d)", nOutgoingSRSId);
    2478             :                         }
    2479             :                         else
    2480             :                         {
    2481           0 :                             oStatement.Append("geometry::STGeomFromWKB(?");
    2482           0 :                             oStatement.Appendf(",%d).MakeValid()",
    2483             :                                                nOutgoingSRSId);
    2484             :                         }
    2485           0 :                         bind_buffer[bind_num] = pabyWKB;
    2486           0 :                         ++bind_num;
    2487             :                     }
    2488             :                     else
    2489             :                     {
    2490           0 :                         oStatement.Append("null");
    2491           0 :                         CPLFree(pabyWKB);
    2492             :                     }
    2493             :                 }
    2494             :                 else
    2495             :                 {
    2496           0 :                     oStatement.Append("null");
    2497           0 :                     CPLFree(pabyWKB);
    2498             :                 }
    2499             :             }
    2500           0 :             else if (nUploadGeometryFormat == MSSQLGEOMETRY_WKT)
    2501             :             {
    2502           0 :                 char *pszWKT = nullptr;
    2503           0 :                 if (poGeom->exportToWkt(&pszWKT) == OGRERR_NONE &&
    2504           0 :                     (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY ||
    2505           0 :                      nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY))
    2506             :                 {
    2507           0 :                     size_t nLen = 0;
    2508           0 :                     while (pszWKT[nLen] != '\0')
    2509           0 :                         nLen++;
    2510             : 
    2511           0 :                     int nRetCode = SQLBindParameter(
    2512           0 :                         oStatement.GetStatement(), (SQLUSMALLINT)(bind_num + 1),
    2513             :                         SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR, nLen, 0,
    2514           0 :                         (SQLPOINTER)pszWKT, 0, nullptr);
    2515           0 :                     if (nRetCode == SQL_SUCCESS ||
    2516             :                         nRetCode == SQL_SUCCESS_WITH_INFO)
    2517             :                     {
    2518           0 :                         if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
    2519             :                         {
    2520           0 :                             oStatement.Append("geography::STGeomFromText(?");
    2521           0 :                             oStatement.Appendf(",%d)", nOutgoingSRSId);
    2522             :                         }
    2523             :                         else
    2524             :                         {
    2525           0 :                             oStatement.Append("geometry::STGeomFromText(?");
    2526           0 :                             oStatement.Appendf(",%d).MakeValid()",
    2527             :                                                nOutgoingSRSId);
    2528             :                         }
    2529           0 :                         bind_buffer[bind_num] = pszWKT;
    2530           0 :                         ++bind_num;
    2531             :                     }
    2532             :                     else
    2533             :                     {
    2534           0 :                         oStatement.Append("null");
    2535           0 :                         CPLFree(pszWKT);
    2536             :                     }
    2537             :                 }
    2538             :                 else
    2539             :                 {
    2540           0 :                     oStatement.Append("null");
    2541           0 :                     CPLFree(pszWKT);
    2542             :                 }
    2543             :             }
    2544             :             else
    2545           0 :                 oStatement.Append("null");
    2546             : 
    2547           0 :             bNeedComma = TRUE;
    2548             :         }
    2549             : 
    2550             :         /* Set the FID */
    2551           0 :         if (nFID != OGRNullFID && pszFIDColumn != nullptr)
    2552             :         {
    2553           0 :             if (bNeedComma)
    2554           0 :                 oStatement.Appendf(", " CPL_FRMT_GIB, nFID);
    2555             :             else
    2556             :             {
    2557           0 :                 oStatement.Appendf(CPL_FRMT_GIB, nFID);
    2558           0 :                 bNeedComma = TRUE;
    2559             :             }
    2560             :         }
    2561             : 
    2562           0 :         for (i = 0; i < nFieldCount; i++)
    2563             :         {
    2564           0 :             if (!poFeature->IsFieldSetAndNotNull(i))
    2565           0 :                 continue;
    2566             : 
    2567           0 :             if (bNeedComma)
    2568           0 :                 oStatement.Append(", ");
    2569             :             else
    2570           0 :                 bNeedComma = TRUE;
    2571             : 
    2572           0 :             AppendFieldValue(&oStatement, poFeature, i, &bind_num, bind_buffer);
    2573             :         }
    2574             : 
    2575           0 :         oStatement.Append(");");
    2576             :     }
    2577             : 
    2578           0 :     if (nFID != OGRNullFID && pszFIDColumn != nullptr && bIsIdentityFid)
    2579           0 :         oStatement.Appendf("SET IDENTITY_INSERT [%s].[%s] OFF;", pszSchemaName,
    2580             :                            pszTableName);
    2581             : 
    2582             :     /* -------------------------------------------------------------------- */
    2583             :     /*      Execute the insert.                                             */
    2584             :     /* -------------------------------------------------------------------- */
    2585             : 
    2586           0 :     if (!oStatement.ExecuteSQL())
    2587             :     {
    2588           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2589             :                  "INSERT command for new feature failed. %s",
    2590           0 :                  poDS->GetSession()->GetLastError());
    2591             : 
    2592           0 :         for (i = 0; i < bind_num; i++)
    2593           0 :             CPLFree(bind_buffer[i]);
    2594           0 :         CPLFree(bind_buffer);
    2595             : 
    2596             : #ifdef SQL_SS_UDT
    2597             :         CPLFree(bind_datalen);
    2598             : #endif
    2599             : 
    2600           0 :         return OGRERR_FAILURE;
    2601             :     }
    2602           0 :     else if (nFID == OGRNullFID && pszFIDColumn != nullptr &&
    2603           0 :              (bIsIdentityFid || poDS->AlwaysOutputFid()))
    2604             :     {
    2605             :         // fetch new ID and set it into the feature
    2606           0 :         if (oStatement.Fetch())
    2607             :         {
    2608           0 :             GIntBig newID = atoll(oStatement.GetColData(0));
    2609           0 :             poFeature->SetFID(newID);
    2610             :         }
    2611             :     }
    2612             : 
    2613           0 :     for (i = 0; i < bind_num; i++)
    2614           0 :         CPLFree(bind_buffer[i]);
    2615           0 :     CPLFree(bind_buffer);
    2616             : 
    2617             : #ifdef SQL_SS_UDT
    2618             :     CPLFree(bind_datalen);
    2619             : #endif
    2620             : 
    2621           0 :     return OGRERR_NONE;
    2622             : }
    2623             : 
    2624             : /************************************************************************/
    2625             : /*                          AppendFieldValue()                          */
    2626             : /*                                                                      */
    2627             : /* Used by CreateFeature() and SetFeature() to format a                 */
    2628             : /* non-empty field value                                                */
    2629             : /************************************************************************/
    2630             : 
    2631           0 : void OGRMSSQLSpatialTableLayer::AppendFieldValue(CPLODBCStatement *poStatement,
    2632             :                                                  OGRFeature *poFeature, int i,
    2633             :                                                  int *bind_num,
    2634             :                                                  void **bind_buffer)
    2635             : {
    2636           0 :     int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
    2637           0 :     int nOGRFieldSubType = poFeatureDefn->GetFieldDefn(i)->GetSubType();
    2638             : 
    2639             :     // We need special formatting for integer list values.
    2640           0 :     if (nOGRFieldType == OFTIntegerList)
    2641             :     {
    2642             :         // TODO
    2643           0 :         poStatement->Append("null");
    2644           0 :         return;
    2645             :     }
    2646             : 
    2647             :     // We need special formatting for real list values.
    2648           0 :     else if (nOGRFieldType == OFTRealList)
    2649             :     {
    2650             :         // TODO
    2651           0 :         poStatement->Append("null");
    2652           0 :         return;
    2653             :     }
    2654             : 
    2655             :     // We need special formatting for string list values.
    2656           0 :     else if (nOGRFieldType == OFTStringList)
    2657             :     {
    2658             :         // TODO
    2659           0 :         poStatement->Append("null");
    2660           0 :         return;
    2661             :     }
    2662             : 
    2663             :     // Binary formatting
    2664           0 :     if (nOGRFieldType == OFTBinary)
    2665             :     {
    2666           0 :         int nLen = 0;
    2667           0 :         GByte *pabyData = poFeature->GetFieldAsBinary(i, &nLen);
    2668           0 :         char *pszBytes = GByteArrayToHexString(pabyData, nLen);
    2669           0 :         poStatement->Append(pszBytes);
    2670           0 :         CPLFree(pszBytes);
    2671           0 :         return;
    2672             :     }
    2673             : 
    2674             :     // Datetime values need special handling as SQL Server's datetime type
    2675             :     // accepts values only in ISO 8601 format and only without time zone
    2676             :     // information
    2677           0 :     else if (nOGRFieldType == OFTDateTime)
    2678             :     {
    2679           0 :         char *pszStrValue = OGRGetXMLDateTime((*poFeature)[i].GetRawValue());
    2680             : 
    2681           0 :         int nRetCode = SQLBindParameter(
    2682           0 :             poStatement->GetStatement(), (SQLUSMALLINT)((*bind_num) + 1),
    2683           0 :             SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, strlen(pszStrValue) + 1,
    2684           0 :             0, (SQLPOINTER)pszStrValue, 0, nullptr);
    2685           0 :         if (nRetCode == SQL_SUCCESS || nRetCode == SQL_SUCCESS_WITH_INFO)
    2686             :         {
    2687           0 :             bind_buffer[*bind_num] = pszStrValue;
    2688           0 :             ++(*bind_num);
    2689           0 :             poStatement->Append("CAST(CAST(? AS datetimeoffset) AS datetime)");
    2690             :         }
    2691             :         else
    2692             :         {
    2693           0 :             poStatement->Append(CPLSPrintf(
    2694             :                 "CAST(CAST('%s' AS datetimeoffset) AS datetime)", pszStrValue));
    2695           0 :             CPLFree(pszStrValue);
    2696             :         }
    2697           0 :         return;
    2698             :     }
    2699             : 
    2700             :     // Flag indicating NULL or not-a-date date value
    2701             :     // e.g. 0000-00-00 - there is no year 0
    2702           0 :     OGRBoolean bIsDateNull = FALSE;
    2703             : 
    2704           0 :     const char *pszStrValue = poFeature->GetFieldAsString(i);
    2705             : 
    2706             :     // Check if date is NULL: 0000-00-00
    2707           0 :     if (nOGRFieldType == OFTDate)
    2708             :     {
    2709           0 :         if (STARTS_WITH_CI(pszStrValue, "0000"))
    2710             :         {
    2711           0 :             pszStrValue = "null";
    2712           0 :             bIsDateNull = TRUE;
    2713             :         }
    2714             :     }
    2715           0 :     else if (nOGRFieldType == OFTReal)
    2716             :     {
    2717           0 :         char *pszComma = strchr((char *)pszStrValue, ',');
    2718           0 :         if (pszComma)
    2719           0 :             *pszComma = '.';
    2720             :     }
    2721             : 
    2722           0 :     if (nOGRFieldType != OFTInteger && nOGRFieldType != OFTInteger64 &&
    2723           0 :         nOGRFieldType != OFTReal && !bIsDateNull)
    2724             :     {
    2725           0 :         if (nOGRFieldType == OFTString)
    2726             :         {
    2727           0 :             if (nOGRFieldSubType == OFSTUUID)
    2728             :             {
    2729             :                 int nRetCode =
    2730           0 :                     SQLBindParameter(poStatement->GetStatement(),
    2731           0 :                                      (SQLUSMALLINT)((*bind_num) + 1),
    2732             :                                      SQL_PARAM_INPUT, SQL_C_CHAR, SQL_GUID, 16,
    2733           0 :                                      0, (SQLPOINTER)pszStrValue, 0, nullptr);
    2734           0 :                 if (nRetCode == SQL_SUCCESS ||
    2735             :                     nRetCode == SQL_SUCCESS_WITH_INFO)
    2736             :                 {
    2737           0 :                     poStatement->Append("?");
    2738           0 :                     bind_buffer[*bind_num] = CPLStrdup(pszStrValue);
    2739           0 :                     ++(*bind_num);
    2740             :                 }
    2741             :                 else
    2742             :                 {
    2743           0 :                     OGRMSSQLAppendEscaped(poStatement, pszStrValue);
    2744             :                 }
    2745             :             }
    2746             :             else
    2747             :             {
    2748             :                 // bind UTF8 as unicode parameter
    2749             :                 wchar_t *buffer =
    2750           0 :                     CPLRecodeToWChar(pszStrValue, CPL_ENC_UTF8, CPL_ENC_UCS2);
    2751           0 :                 size_t nLen = wcslen(buffer) + 1;
    2752           0 :                 if (nLen > 4000)
    2753             :                 {
    2754             :                     /* need to handle nvarchar(max) */
    2755             : #ifdef SQL_SS_LENGTH_UNLIMITED
    2756             :                     nLen = SQL_SS_LENGTH_UNLIMITED;
    2757             : #else
    2758             :                     /* for older drivers truncate the data to 4000 chars */
    2759           0 :                     buffer[4000] = 0;
    2760           0 :                     nLen = 4000;
    2761           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    2762             :                              "String data truncation applied on field: %s. Use "
    2763             :                              "a more recent ODBC driver that supports handling "
    2764             :                              "large string values.",
    2765           0 :                              poFeatureDefn->GetFieldDefn(i)->GetNameRef());
    2766             : #endif
    2767             :                 }
    2768             : #if WCHAR_MAX > 0xFFFFu
    2769             :                 // Shorten each character to a two-byte value, as expected by
    2770             :                 // the ODBC driver
    2771           0 :                 GUInt16 *panBuffer = reinterpret_cast<GUInt16 *>(buffer);
    2772           0 :                 for (unsigned int nIndex = 1; nIndex < nLen; nIndex += 1)
    2773           0 :                     panBuffer[nIndex] = static_cast<GUInt16>(buffer[nIndex]);
    2774             : #endif
    2775             :                 int nRetCode =
    2776           0 :                     SQLBindParameter(poStatement->GetStatement(),
    2777           0 :                                      (SQLUSMALLINT)((*bind_num) + 1),
    2778             :                                      SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WVARCHAR,
    2779           0 :                                      nLen, 0, (SQLPOINTER)buffer, 0, nullptr);
    2780           0 :                 if (nRetCode == SQL_SUCCESS ||
    2781             :                     nRetCode == SQL_SUCCESS_WITH_INFO)
    2782             :                 {
    2783           0 :                     poStatement->Append("?");
    2784           0 :                     bind_buffer[*bind_num] = buffer;
    2785           0 :                     ++(*bind_num);
    2786             :                 }
    2787             :                 else
    2788             :                 {
    2789           0 :                     OGRMSSQLAppendEscaped(poStatement, pszStrValue);
    2790           0 :                     CPLFree(buffer);
    2791             :                 }
    2792             :             }
    2793             :         }
    2794             :         else
    2795           0 :             OGRMSSQLAppendEscaped(poStatement, pszStrValue);
    2796             :     }
    2797             :     else
    2798             :     {
    2799           0 :         poStatement->Append(pszStrValue);
    2800             :     }
    2801             : }

Generated by: LCOV version 1.14