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