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 : }
|