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

Generated by: LCOV version 1.14