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();
310 0 : const char *pszAuthorityCode = poSRS->GetAuthorityCode();
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 : CPLODBCSession *poSession = poDS->GetSession();
1118 0 : CPLODBCStatement oStmt(poSession);
1119 :
1120 0 : oStmt.Appendf("UPDATE [%s].[%s] SET ", pszSchemaName, pszTableName);
1121 :
1122 0 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1123 0 : if (bUseGeometryValidation && poGeom != nullptr)
1124 : {
1125 0 : OGRMSSQLGeometryValidator oValidator(poGeom, nGeomColumnType);
1126 0 : if (!oValidator.IsValid())
1127 : {
1128 0 : oValidator.MakeValid(poGeom);
1129 0 : CPLError(CE_Warning, CPLE_NotSupported,
1130 : "Geometry with FID = " CPL_FRMT_GIB
1131 : " has been modified to valid geometry.",
1132 : poFeature->GetFID());
1133 : }
1134 : }
1135 :
1136 0 : int nFieldCount = poFeatureDefn->GetFieldCount();
1137 0 : int bind_num = 0;
1138 : void **bind_buffer =
1139 0 : static_cast<void **>(CPLMalloc(sizeof(void *) * (nFieldCount + 1)));
1140 : #ifdef SQL_SS_UDT
1141 : SQLLEN *bind_datalen =
1142 : static_cast<SQLLEN *>(CPLMalloc(sizeof(SQLLEN) * (nFieldCount + 1)));
1143 : #endif
1144 :
1145 0 : int bNeedComma = FALSE;
1146 : SQLLEN nWKBLenBindParameter;
1147 0 : if (poGeom != nullptr && pszGeomColumn != nullptr)
1148 : {
1149 0 : oStmt.Appendf("[%s] = ", pszGeomColumn);
1150 :
1151 0 : if (nUploadGeometryFormat == MSSQLGEOMETRY_NATIVE)
1152 : {
1153 : #ifdef SQL_SS_UDT
1154 : OGRMSSQLGeometryWriter poWriter(poGeom, nGeomColumnType, nSRSId);
1155 : bind_datalen[bind_num] = poWriter.GetDataLen();
1156 : GByte *pabyData =
1157 : static_cast<GByte *>(CPLMalloc(bind_datalen[bind_num] + 1));
1158 : if (poWriter.WriteSqlGeometry(
1159 : pabyData, static_cast<int>(bind_datalen[bind_num])) ==
1160 : OGRERR_NONE)
1161 : {
1162 : SQLHANDLE ipd;
1163 : if ((!poSession->Failed(SQLBindParameter(
1164 : oStmt.GetStatement(),
1165 : static_cast<SQLUSMALLINT>(bind_num + 1),
1166 : SQL_PARAM_INPUT, SQL_C_BINARY, SQL_SS_UDT,
1167 : SQL_SS_LENGTH_UNLIMITED, 0,
1168 : static_cast<SQLPOINTER>(pabyData),
1169 : bind_datalen[bind_num],
1170 : reinterpret_cast<SQLLEN *>(
1171 : &bind_datalen[bind_num])))) &&
1172 : (!poSession->Failed(SQLGetStmtAttr(oStmt.GetStatement(),
1173 : SQL_ATTR_IMP_PARAM_DESC,
1174 : &ipd, 0, nullptr))) &&
1175 : (!poSession->Failed(SQLSetDescField(
1176 : ipd, 1, SQL_CA_SS_UDT_TYPE_NAME,
1177 : const_cast<char *>(nGeomColumnType ==
1178 : MSSQLCOLTYPE_GEOGRAPHY
1179 : ? "geography"
1180 : : "geometry"),
1181 : SQL_NTS))))
1182 : {
1183 : oStmt.Append("?");
1184 : bind_buffer[bind_num] = pabyData;
1185 : ++bind_num;
1186 : }
1187 : else
1188 : {
1189 : oStmt.Append("null");
1190 : CPLFree(pabyData);
1191 : }
1192 : }
1193 : else
1194 : {
1195 : oStmt.Append("null");
1196 : CPLFree(pabyData);
1197 : }
1198 : #else
1199 0 : CPLError(CE_Failure, CPLE_AppDefined,
1200 : "Native geometry upload is not supported");
1201 0 : CPLFree(bind_buffer);
1202 :
1203 0 : return OGRERR_FAILURE;
1204 : #endif
1205 : }
1206 0 : else if (nUploadGeometryFormat == MSSQLGEOMETRY_WKB)
1207 : {
1208 0 : const size_t nWKBLen = poGeom->WkbSize();
1209 : GByte *pabyWKB = static_cast<GByte *>(
1210 0 : VSI_MALLOC_VERBOSE(nWKBLen + 1)); // do we need the +1 ?
1211 0 : if (pabyWKB == nullptr)
1212 : {
1213 0 : oStmt.Append("null");
1214 : }
1215 0 : else if (poGeom->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso) ==
1216 0 : OGRERR_NONE &&
1217 0 : (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY ||
1218 0 : nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY))
1219 : {
1220 0 : nWKBLenBindParameter = nWKBLen;
1221 0 : int nRetCode = SQLBindParameter(
1222 : oStmt.GetStatement(),
1223 0 : static_cast<SQLUSMALLINT>(bind_num + 1), SQL_PARAM_INPUT,
1224 : SQL_C_BINARY, SQL_LONGVARBINARY, nWKBLen, 0,
1225 : static_cast<SQLPOINTER>(pabyWKB), nWKBLen,
1226 0 : &nWKBLenBindParameter);
1227 0 : if (nRetCode == SQL_SUCCESS ||
1228 : nRetCode == SQL_SUCCESS_WITH_INFO)
1229 : {
1230 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
1231 : {
1232 0 : oStmt.Append("geography::STGeomFromWKB(?");
1233 0 : oStmt.Appendf(",%d)", nSRSId);
1234 : }
1235 : else
1236 : {
1237 0 : oStmt.Append("geometry::STGeomFromWKB(?");
1238 0 : oStmt.Appendf(",%d).MakeValid()", nSRSId);
1239 : }
1240 0 : bind_buffer[bind_num] = pabyWKB;
1241 0 : ++bind_num;
1242 : }
1243 : else
1244 : {
1245 0 : oStmt.Append("null");
1246 0 : CPLFree(pabyWKB);
1247 : }
1248 : }
1249 : else
1250 : {
1251 0 : oStmt.Append("null");
1252 0 : CPLFree(pabyWKB);
1253 : }
1254 : }
1255 0 : else if (nUploadGeometryFormat == MSSQLGEOMETRY_WKT)
1256 : {
1257 0 : char *pszWKT = nullptr;
1258 0 : if (poGeom->exportToWkt(&pszWKT) == OGRERR_NONE &&
1259 0 : (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY ||
1260 0 : nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY))
1261 : {
1262 0 : size_t nLen = 0;
1263 0 : while (pszWKT[nLen] != '\0')
1264 0 : nLen++;
1265 :
1266 0 : int nRetCode = SQLBindParameter(
1267 : oStmt.GetStatement(),
1268 0 : static_cast<SQLUSMALLINT>(bind_num + 1), SQL_PARAM_INPUT,
1269 : SQL_C_CHAR, SQL_LONGVARCHAR, nLen, 0,
1270 0 : static_cast<SQLPOINTER>(pszWKT), 0, nullptr);
1271 0 : if (nRetCode == SQL_SUCCESS ||
1272 : nRetCode == SQL_SUCCESS_WITH_INFO)
1273 : {
1274 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
1275 : {
1276 0 : oStmt.Append("geography::STGeomFromText(?");
1277 0 : oStmt.Appendf(",%d)", nSRSId);
1278 : }
1279 : else
1280 : {
1281 0 : oStmt.Append("geometry::STGeomFromText(?");
1282 0 : oStmt.Appendf(",%d).MakeValid()", nSRSId);
1283 : }
1284 0 : bind_buffer[bind_num] = pszWKT;
1285 0 : ++bind_num;
1286 : }
1287 : else
1288 : {
1289 0 : oStmt.Append("null");
1290 0 : CPLFree(pszWKT);
1291 : }
1292 : }
1293 : else
1294 : {
1295 0 : oStmt.Append("null");
1296 0 : CPLFree(pszWKT);
1297 : }
1298 : }
1299 : else
1300 0 : oStmt.Append("null");
1301 :
1302 0 : bNeedComma = TRUE;
1303 : }
1304 :
1305 : int i;
1306 0 : for (i = 0; i < nFieldCount; i++)
1307 : {
1308 0 : if (bNeedComma)
1309 0 : oStmt.Appendf(", [%s] = ",
1310 0 : poFeatureDefn->GetFieldDefn(i)->GetNameRef());
1311 : else
1312 : {
1313 0 : oStmt.Appendf("[%s] = ",
1314 0 : poFeatureDefn->GetFieldDefn(i)->GetNameRef());
1315 0 : bNeedComma = TRUE;
1316 : }
1317 :
1318 0 : if (!poFeature->IsFieldSetAndNotNull(i))
1319 0 : oStmt.Append("null");
1320 : else
1321 0 : AppendFieldValue(&oStmt, poFeature, i, &bind_num, bind_buffer);
1322 : }
1323 :
1324 : /* Add the WHERE clause */
1325 0 : oStmt.Appendf(" WHERE [%s] = " CPL_FRMT_GIB, pszFIDColumn,
1326 : poFeature->GetFID());
1327 :
1328 : /* -------------------------------------------------------------------- */
1329 : /* Execute the update. */
1330 : /* -------------------------------------------------------------------- */
1331 :
1332 0 : if (!oStmt.ExecuteSQL())
1333 : {
1334 0 : CPLError(CE_Failure, CPLE_AppDefined,
1335 : "Error updating feature with FID:" CPL_FRMT_GIB ", %s",
1336 0 : poFeature->GetFID(), poDS->GetSession()->GetLastError());
1337 :
1338 0 : for (i = 0; i < bind_num; i++)
1339 0 : CPLFree(bind_buffer[i]);
1340 0 : CPLFree(bind_buffer);
1341 :
1342 : #ifdef SQL_SS_UDT
1343 : CPLFree(bind_datalen);
1344 : #endif
1345 0 : return OGRERR_FAILURE;
1346 : }
1347 :
1348 0 : for (i = 0; i < bind_num; i++)
1349 0 : CPLFree(bind_buffer[i]);
1350 0 : CPLFree(bind_buffer);
1351 :
1352 : #ifdef SQL_SS_UDT
1353 : CPLFree(bind_datalen);
1354 : #endif
1355 :
1356 0 : if (oStmt.GetRowCountAffected() < 1)
1357 0 : return OGRERR_NON_EXISTING_FEATURE;
1358 :
1359 0 : return OGRERR_NONE;
1360 : }
1361 :
1362 : /************************************************************************/
1363 : /* DeleteFeature() */
1364 : /************************************************************************/
1365 :
1366 0 : OGRErr OGRMSSQLSpatialTableLayer::DeleteFeature(GIntBig nFID)
1367 :
1368 : {
1369 0 : if (!bUpdateAccess)
1370 : {
1371 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1372 : "DeleteFeature");
1373 0 : return OGRERR_FAILURE;
1374 : }
1375 :
1376 0 : poDS->EndCopy();
1377 :
1378 0 : GetLayerDefn();
1379 :
1380 0 : if (pszFIDColumn == nullptr)
1381 : {
1382 0 : CPLError(CE_Failure, CPLE_AppDefined,
1383 : "DeleteFeature() without any FID column.");
1384 0 : return OGRERR_FAILURE;
1385 : }
1386 :
1387 0 : if (nFID == OGRNullFID)
1388 : {
1389 0 : CPLError(CE_Failure, CPLE_AppDefined,
1390 : "DeleteFeature() with unset FID fails.");
1391 0 : return OGRERR_FAILURE;
1392 : }
1393 :
1394 0 : ClearStatement();
1395 :
1396 : /* -------------------------------------------------------------------- */
1397 : /* Drop the record with this FID. */
1398 : /* -------------------------------------------------------------------- */
1399 0 : CPLODBCStatement oStatement(poDS->GetSession());
1400 :
1401 0 : oStatement.Appendf("DELETE FROM [%s].[%s] WHERE [%s] = " CPL_FRMT_GIB,
1402 : pszSchemaName, pszTableName, pszFIDColumn, nFID);
1403 :
1404 0 : if (!oStatement.ExecuteSQL())
1405 : {
1406 0 : CPLError(CE_Failure, CPLE_AppDefined,
1407 : "Attempt to delete feature with FID " CPL_FRMT_GIB
1408 : " failed. %s",
1409 0 : nFID, poDS->GetSession()->GetLastError());
1410 :
1411 0 : return OGRERR_FAILURE;
1412 : }
1413 :
1414 0 : if (oStatement.GetRowCountAffected() < 1)
1415 0 : return OGRERR_NON_EXISTING_FEATURE;
1416 :
1417 0 : return OGRERR_NONE;
1418 : }
1419 :
1420 : /************************************************************************/
1421 : /* Failed() */
1422 : /************************************************************************/
1423 :
1424 0 : int OGRMSSQLSpatialTableLayer::Failed(int nRetCode)
1425 :
1426 : {
1427 0 : if (nRetCode == SQL_SUCCESS || nRetCode == SQL_SUCCESS_WITH_INFO)
1428 0 : return FALSE;
1429 :
1430 0 : char SQLState[6] = "";
1431 0 : char Msg[256] = "";
1432 0 : SQLINTEGER iNativeError = 0;
1433 0 : SQLSMALLINT iMsgLen = 0;
1434 :
1435 0 : int iRc = SQLGetDiagRec(
1436 : SQL_HANDLE_ENV, hEnvBCP, 1, reinterpret_cast<SQLCHAR *>(SQLState),
1437 0 : &iNativeError, reinterpret_cast<SQLCHAR *>(Msg), 256, &iMsgLen);
1438 0 : if (iRc != SQL_NO_DATA)
1439 : {
1440 0 : CPLError(CE_Failure, CPLE_AppDefined,
1441 : "SQL Error SQLState=%s, NativeError=%d, Msg=%s\n", SQLState,
1442 : static_cast<int>(iNativeError), Msg);
1443 : }
1444 :
1445 0 : return TRUE;
1446 : }
1447 :
1448 : /************************************************************************/
1449 : /* Failed2() */
1450 : /************************************************************************/
1451 :
1452 : #ifdef MSSQL_BCP_SUPPORTED
1453 : int OGRMSSQLSpatialTableLayer::Failed2(int nRetCode)
1454 :
1455 : {
1456 : if (nRetCode == SUCCEED)
1457 : return FALSE;
1458 :
1459 : char SQLState[6] = "";
1460 : char Msg[256] = "";
1461 : SQLINTEGER iNativeError = 0;
1462 : SQLSMALLINT iMsgLen = 0;
1463 :
1464 : int iRc = SQLGetDiagRec(
1465 : SQL_HANDLE_DBC, hDBCBCP, 1, reinterpret_cast<SQLCHAR *>(SQLState),
1466 : &iNativeError, reinterpret_cast<SQLCHAR *>(Msg), 256, &iMsgLen);
1467 : if (iRc != SQL_NO_DATA)
1468 : {
1469 : CPLError(CE_Failure, CPLE_AppDefined,
1470 : "SQL Error SQLState=%s, NativeError=%d, Msg=%s\n", SQLState,
1471 : static_cast<int>(iNativeError), Msg);
1472 : }
1473 :
1474 : return TRUE;
1475 : }
1476 :
1477 : /************************************************************************/
1478 : /* InitBCP() */
1479 : /************************************************************************/
1480 :
1481 : int OGRMSSQLSpatialTableLayer::InitBCP(const char *pszDSN)
1482 :
1483 : {
1484 : /* Create a different connection for BCP upload */
1485 : if (Failed(SQLAllocHandle(SQL_HANDLE_ENV, nullptr, &hEnvBCP)))
1486 : return FALSE;
1487 :
1488 : /* Notify ODBC that this is an ODBC 3.0 app. */
1489 : if (Failed(SQLSetEnvAttr(hEnvBCP, SQL_ATTR_ODBC_VERSION,
1490 : (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER)))
1491 : {
1492 : CloseBCP();
1493 : return FALSE;
1494 : }
1495 :
1496 : if (Failed(SQLAllocHandle(SQL_HANDLE_DBC, hEnvBCP, &hDBCBCP)))
1497 : {
1498 : CloseBCP();
1499 : return FALSE;
1500 : }
1501 :
1502 : /* set bulk copy mode */
1503 : if (Failed(SQLSetConnectAttr(hDBCBCP, SQL_COPT_SS_BCP,
1504 : reinterpret_cast<void *>(SQL_BCP_ON),
1505 : SQL_IS_INTEGER)))
1506 : {
1507 : CloseBCP();
1508 : return FALSE;
1509 : }
1510 :
1511 : Failed(SQLSetConnectAttr(hDBCBCP, SQL_ATTR_LOGIN_TIMEOUT,
1512 : reinterpret_cast<void *>(30), SQL_IS_INTEGER));
1513 :
1514 : SQLCHAR szOutConnString[1024];
1515 : SQLSMALLINT nOutConnStringLen = 0;
1516 :
1517 : if (Failed(SQLDriverConnect(
1518 : hDBCBCP, nullptr,
1519 : reinterpret_cast<SQLCHAR *>(const_cast<char *>(pszDSN)),
1520 : static_cast<SQLSMALLINT>(strlen(pszDSN)), szOutConnString,
1521 : sizeof(szOutConnString), &nOutConnStringLen, SQL_DRIVER_NOPROMPT)))
1522 : {
1523 : CloseBCP();
1524 : return FALSE;
1525 : }
1526 :
1527 : return TRUE;
1528 : }
1529 :
1530 : /************************************************************************/
1531 : /* CloseBCP() */
1532 : /************************************************************************/
1533 :
1534 : void OGRMSSQLSpatialTableLayer::CloseBCP()
1535 :
1536 : {
1537 : if (papstBindBuffer)
1538 : {
1539 : int iCol;
1540 :
1541 : int nRecNum = bcp_done(hDBCBCP);
1542 : if (nRecNum == -1)
1543 : Failed2(nRecNum);
1544 :
1545 : for (iCol = 0; iCol < nRawColumns; iCol++)
1546 : CPLFree(papstBindBuffer[iCol]);
1547 : CPLFree(papstBindBuffer);
1548 : papstBindBuffer = nullptr;
1549 :
1550 : if (bIdentityInsert)
1551 : {
1552 : bIdentityInsert = FALSE;
1553 : }
1554 : }
1555 :
1556 : if (hDBCBCP != nullptr)
1557 : {
1558 : CPLDebug("ODBC", "SQLDisconnect()");
1559 : SQLDisconnect(hDBCBCP);
1560 : SQLFreeHandle(SQL_HANDLE_DBC, hDBCBCP);
1561 : hDBCBCP = nullptr;
1562 : }
1563 :
1564 : if (hEnvBCP != nullptr)
1565 : {
1566 : SQLFreeHandle(SQL_HANDLE_ENV, hEnvBCP);
1567 : hEnvBCP = nullptr;
1568 : }
1569 : }
1570 :
1571 : /************************************************************************/
1572 : /* CreateFeatureBCP() */
1573 : /************************************************************************/
1574 :
1575 : OGRErr OGRMSSQLSpatialTableLayer::CreateFeatureBCP(OGRFeature *poFeature)
1576 :
1577 : {
1578 : int iCol;
1579 : int iField = 0;
1580 :
1581 : if (hDBCBCP == nullptr)
1582 : {
1583 : nBCPCount = 0;
1584 :
1585 : /* Tell the datasource we are now planning to copy data */
1586 : poDS->StartCopy(this);
1587 :
1588 : CPLODBCSession *poSession = poDS->GetSession();
1589 :
1590 : if (poSession->IsInTransaction())
1591 : poSession->CommitTransaction(); /* commit creating the table */
1592 :
1593 : /* Get the column definitions for this table. */
1594 : bLayerDefnNeedsRefresh = true;
1595 : GetLayerDefn();
1596 : bLayerDefnNeedsRefresh = false;
1597 :
1598 : if (!poFeatureDefn)
1599 : return OGRERR_FAILURE;
1600 :
1601 : if (poFeature->GetFID() != OGRNullFID && pszFIDColumn != nullptr &&
1602 : bIsIdentityFid)
1603 : {
1604 : bIdentityInsert = TRUE;
1605 : }
1606 :
1607 : if (!InitBCP(poDS->GetConnectionString()))
1608 : return OGRERR_FAILURE;
1609 :
1610 : /* Initialize the bulk copy */
1611 : if (Failed2(bcp_init(
1612 : hDBCBCP, CPLSPrintf("[%s].[%s]", pszSchemaName, pszTableName),
1613 : nullptr, nullptr, DB_IN)))
1614 : {
1615 : CloseBCP();
1616 : return OGRERR_FAILURE;
1617 : }
1618 :
1619 : if (bIdentityInsert)
1620 : {
1621 : if (Failed2(bcp_control(hDBCBCP, BCPKEEPIDENTITY,
1622 : reinterpret_cast<void *>(TRUE))))
1623 : {
1624 : CPLError(CE_Failure, CPLE_AppDefined,
1625 : "Failed to set identity insert bulk copy mode, %s.",
1626 : poDS->GetSession()->GetLastError());
1627 : return OGRERR_FAILURE;
1628 : }
1629 : }
1630 :
1631 : papstBindBuffer = static_cast<BCPData **>(
1632 : CPLMalloc(sizeof(BCPData *) * (nRawColumns)));
1633 :
1634 : for (iCol = 0; iCol < nRawColumns; iCol++)
1635 : {
1636 : papstBindBuffer[iCol] = nullptr;
1637 :
1638 : if (iCol == nGeomColumnIndex)
1639 : {
1640 : papstBindBuffer[iCol] =
1641 : static_cast<BCPData *>(CPLMalloc(sizeof(BCPData)));
1642 : if (Failed2(bcp_bind(hDBCBCP,
1643 : nullptr /* data is provided later */, 0,
1644 : 0 /*or any value < 8000*/, nullptr, 0,
1645 : SQLUDT, iCol + 1)))
1646 : return OGRERR_FAILURE;
1647 : }
1648 : else if (iCol == nFIDColumnIndex)
1649 : {
1650 : if (!bIdentityInsert)
1651 : continue;
1652 : /* bind fid column */
1653 : papstBindBuffer[iCol] =
1654 : static_cast<BCPData *>(CPLMalloc(sizeof(BCPData)));
1655 : papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
1656 :
1657 : if (Failed2(bcp_bind(
1658 : hDBCBCP,
1659 : reinterpret_cast<LPCBYTE>(const_cast<char **>(
1660 : papstBindBuffer[iCol]->VarChar.pData)),
1661 : 0, SQL_VARLEN_DATA, reinterpret_cast<LPCBYTE>(""), 1,
1662 : SQLVARCHAR, iCol + 1)))
1663 : return OGRERR_FAILURE;
1664 : }
1665 : else if (iField < poFeatureDefn->GetFieldCount() &&
1666 : iCol == panFieldOrdinals[iField])
1667 : {
1668 : OGRFieldDefn *poFDefn = poFeatureDefn->GetFieldDefn(iField);
1669 :
1670 : if (poFDefn->IsIgnored())
1671 : {
1672 : /* set null */
1673 : ++iField;
1674 : continue;
1675 : }
1676 :
1677 : int iSrcField = poFeature->GetFieldIndex(poFDefn->GetNameRef());
1678 : if (iSrcField < 0)
1679 : {
1680 : ++iField;
1681 : continue; /* no such field at the source */
1682 : }
1683 :
1684 : if (poFDefn->GetType() == OFTInteger)
1685 : {
1686 : /* int */
1687 : papstBindBuffer[iCol] =
1688 : static_cast<BCPData *>(CPLMalloc(sizeof(BCPData)));
1689 : papstBindBuffer[iCol]->Integer.iIndicator =
1690 : sizeof(papstBindBuffer[iCol]->Integer.Value);
1691 :
1692 : if (Failed2(bcp_bind(
1693 : hDBCBCP,
1694 : reinterpret_cast<LPCBYTE>(papstBindBuffer[iCol]),
1695 : sizeof(papstBindBuffer[iCol]->Integer.iIndicator),
1696 : sizeof(papstBindBuffer[iCol]->Integer.Value),
1697 : nullptr, 0, SQLINT4, iCol + 1)))
1698 : return OGRERR_FAILURE;
1699 : }
1700 : else if (poFDefn->GetType() == OFTInteger64)
1701 : {
1702 : /* bigint */
1703 : papstBindBuffer[iCol] =
1704 : static_cast<BCPData *>(CPLMalloc(sizeof(BCPData)));
1705 : papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
1706 :
1707 : if (Failed2(bcp_bind(
1708 : hDBCBCP,
1709 : reinterpret_cast<LPCBYTE>(const_cast<char **>(
1710 : papstBindBuffer[iCol]->VarChar.pData)),
1711 : 0, SQL_VARLEN_DATA, reinterpret_cast<LPCBYTE>(""),
1712 : 1, SQLVARCHAR, iCol + 1)))
1713 : return OGRERR_FAILURE;
1714 : }
1715 : else if (poFDefn->GetType() == OFTReal)
1716 : {
1717 : /* float */
1718 : /* TODO convert to DBNUMERIC */
1719 : papstBindBuffer[iCol] =
1720 : static_cast<BCPData *>(CPLMalloc(sizeof(BCPData)));
1721 : papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
1722 :
1723 : if (Failed2(bcp_bind(
1724 : hDBCBCP,
1725 : reinterpret_cast<LPCBYTE>(const_cast<char **>(
1726 : papstBindBuffer[iCol]->VarChar.pData)),
1727 : 0, SQL_VARLEN_DATA, reinterpret_cast<LPCBYTE>(""),
1728 : 1, SQLVARCHAR, iCol + 1)))
1729 : return OGRERR_FAILURE;
1730 : }
1731 : else if (poFDefn->GetType() == OFTString)
1732 : {
1733 : /* nvarchar */
1734 : papstBindBuffer[iCol] =
1735 : static_cast<BCPData *>(CPLMalloc(sizeof(BCPData)));
1736 : papstBindBuffer[iCol]->VarChar.nSize = poFDefn->GetWidth();
1737 : if (poFDefn->GetWidth() == 0)
1738 : {
1739 : if (Failed2(bcp_bind(
1740 : hDBCBCP, nullptr /* data is provided later */,
1741 : 0, 0 /*or any value < 8000*/, nullptr, 0, 0,
1742 : iCol + 1)))
1743 : return OGRERR_FAILURE;
1744 : }
1745 : else
1746 : {
1747 : if (Failed2(bcp_bind(
1748 : hDBCBCP,
1749 : reinterpret_cast<LPCBYTE>(
1750 : papstBindBuffer[iCol]),
1751 : sizeof(papstBindBuffer[iCol]->VarChar.nSize),
1752 : poFDefn->GetWidth(), nullptr, 0, SQLNVARCHAR,
1753 : iCol + 1)))
1754 : return OGRERR_FAILURE;
1755 : }
1756 : }
1757 : else if (poFDefn->GetType() == OFTDate)
1758 : {
1759 : /* date */
1760 : papstBindBuffer[iCol] =
1761 : static_cast<BCPData *>(CPLMalloc(sizeof(BCPData)));
1762 : papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
1763 :
1764 : if (Failed2(bcp_bind(
1765 : hDBCBCP,
1766 : reinterpret_cast<LPCBYTE>(const_cast<char **>(
1767 : papstBindBuffer[iCol]->VarChar.pData)),
1768 : 0, SQL_VARLEN_DATA, reinterpret_cast<LPCBYTE>(""),
1769 : 1, SQLVARCHAR, iCol + 1)))
1770 : return OGRERR_FAILURE;
1771 : }
1772 : else if (poFDefn->GetType() == OFTTime)
1773 : {
1774 : /* time(7) */
1775 : papstBindBuffer[iCol] =
1776 : static_cast<BCPData *>(CPLMalloc(sizeof(BCPData)));
1777 : papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
1778 :
1779 : if (Failed2(bcp_bind(
1780 : hDBCBCP,
1781 : reinterpret_cast<LPCBYTE>(const_cast<char **>(
1782 : papstBindBuffer[iCol]->VarChar.pData)),
1783 : 0, SQL_VARLEN_DATA, reinterpret_cast<LPCBYTE>(""),
1784 : 1, SQLVARCHAR, iCol + 1)))
1785 : return OGRERR_FAILURE;
1786 : }
1787 : else if (poFDefn->GetType() == OFTDateTime)
1788 : {
1789 : /* datetime */
1790 : papstBindBuffer[iCol] =
1791 : static_cast<BCPData *>(CPLMalloc(sizeof(BCPData)));
1792 : papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
1793 :
1794 : if (Failed2(bcp_bind(
1795 : hDBCBCP,
1796 : reinterpret_cast<LPCBYTE>(const_cast<char **>(
1797 : papstBindBuffer[iCol]->VarChar.pData)),
1798 : 0, SQL_VARLEN_DATA, reinterpret_cast<LPCBYTE>(""),
1799 : 1, SQLVARCHAR, iCol + 1)))
1800 : return OGRERR_FAILURE;
1801 : }
1802 : else if (poFDefn->GetType() == OFTBinary)
1803 : {
1804 : /* image */
1805 : papstBindBuffer[iCol] =
1806 : static_cast<BCPData *>(CPLMalloc(sizeof(BCPData)));
1807 : if (Failed2(bcp_bind(hDBCBCP,
1808 : nullptr /* data is provided later */,
1809 : 0, 0 /*or any value < 8000*/, nullptr,
1810 : 0, 0, iCol + 1)))
1811 : return OGRERR_FAILURE;
1812 : }
1813 : else
1814 : {
1815 : CPLError(
1816 : CE_Failure, CPLE_NotSupported,
1817 : "Filed %s with type %s is not supported for bulk "
1818 : "insert.",
1819 : poFDefn->GetNameRef(),
1820 : OGRFieldDefn::GetFieldTypeName(poFDefn->GetType()));
1821 :
1822 : return OGRERR_FAILURE;
1823 : }
1824 :
1825 : ++iField;
1826 : }
1827 : }
1828 : }
1829 :
1830 : /* do bulk insert here */
1831 :
1832 : /* prepare data to variables */
1833 : iField = 0;
1834 : for (iCol = 0; iCol < nRawColumns; iCol++)
1835 : {
1836 : if (iCol == nGeomColumnIndex)
1837 : {
1838 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1839 : if (poGeom != nullptr)
1840 : {
1841 : /* prepare geometry */
1842 : if (bUseGeometryValidation)
1843 : {
1844 : OGRMSSQLGeometryValidator oValidator(poGeom,
1845 : nGeomColumnType);
1846 : if (!oValidator.IsValid())
1847 : {
1848 : oValidator.MakeValid(poGeom);
1849 : CPLError(CE_Warning, CPLE_NotSupported,
1850 : "Geometry with FID = " CPL_FRMT_GIB
1851 : " has been modified to valid geometry.",
1852 : poFeature->GetFID());
1853 : }
1854 : }
1855 :
1856 : int nOutgoingSRSId = 0;
1857 : // Use the SRID specified by the provided feature's geometry, if
1858 : // its spatial-reference system is known; otherwise, use the
1859 : // SRID associated with the table
1860 : const OGRSpatialReference *poFeatureSRS =
1861 : poGeom->getSpatialReference();
1862 : if (poFeatureSRS)
1863 : nOutgoingSRSId = poDS->FetchSRSId(poFeatureSRS);
1864 : if (nOutgoingSRSId <= 0)
1865 : nOutgoingSRSId = nSRSId;
1866 :
1867 : OGRMSSQLGeometryWriter poWriter(poGeom, nGeomColumnType,
1868 : nOutgoingSRSId);
1869 : papstBindBuffer[iCol]->RawData.nSize = poWriter.GetDataLen();
1870 : papstBindBuffer[iCol]->RawData.pData = static_cast<GByte *>(
1871 : CPLMalloc(papstBindBuffer[iCol]->RawData.nSize + 1));
1872 :
1873 : if (poWriter.WriteSqlGeometry(
1874 : papstBindBuffer[iCol]->RawData.pData,
1875 : static_cast<int>(
1876 : papstBindBuffer[iCol]->RawData.nSize)) !=
1877 : OGRERR_NONE)
1878 : return OGRERR_FAILURE;
1879 :
1880 : /* set data length */
1881 : if (Failed2(
1882 : bcp_collen(hDBCBCP,
1883 : static_cast<DBINT>(
1884 : papstBindBuffer[iCol]->RawData.nSize),
1885 : iCol + 1)))
1886 : return OGRERR_FAILURE;
1887 : }
1888 : else
1889 : {
1890 : /* set NULL */
1891 : papstBindBuffer[iCol]->RawData.nSize = SQL_NULL_DATA;
1892 : if (Failed2(bcp_collen(hDBCBCP, SQL_NULL_DATA, iCol + 1)))
1893 : return OGRERR_FAILURE;
1894 : }
1895 : }
1896 : else if (iCol == nFIDColumnIndex)
1897 : {
1898 : if (!bIdentityInsert)
1899 : continue;
1900 :
1901 : GIntBig nFID = poFeature->GetFID();
1902 : if (nFID == OGRNullFID)
1903 : {
1904 : papstBindBuffer[iCol]->VarChar.nSize = SQL_NULL_DATA;
1905 : /* set NULL */
1906 : if (Failed2(bcp_collen(hDBCBCP, SQL_NULL_DATA, iCol + 1)))
1907 : return OGRERR_FAILURE;
1908 : }
1909 : else
1910 : {
1911 : papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
1912 : snprintf(reinterpret_cast<char *>(
1913 : papstBindBuffer[iCol]->VarChar.pData),
1914 : 8000, CPL_FRMT_GIB, nFID);
1915 :
1916 : if (Failed2(bcp_collen(hDBCBCP, SQL_VARLEN_DATA, iCol + 1)))
1917 : return OGRERR_FAILURE;
1918 : }
1919 : }
1920 : else if (iField < poFeatureDefn->GetFieldCount() &&
1921 : iCol == panFieldOrdinals[iField])
1922 : {
1923 : OGRFieldDefn *poFDefn = poFeatureDefn->GetFieldDefn(iField);
1924 :
1925 : if (papstBindBuffer[iCol] == nullptr)
1926 : {
1927 : ++iField;
1928 : continue; /* column requires no data */
1929 : }
1930 :
1931 : if (poFDefn->GetType() == OFTInteger)
1932 : {
1933 : /* int */
1934 : if (!poFeature->IsFieldSetAndNotNull(iField))
1935 : papstBindBuffer[iCol]->Integer.iIndicator = SQL_NULL_DATA;
1936 : else
1937 : {
1938 : papstBindBuffer[iCol]->Integer.iIndicator =
1939 : sizeof(papstBindBuffer[iCol]->Integer.Value);
1940 : papstBindBuffer[iCol]->Integer.Value =
1941 : poFeature->GetFieldAsInteger(iField);
1942 : }
1943 : }
1944 : else if (poFDefn->GetType() == OFTInteger64)
1945 : {
1946 : /* bigint */
1947 : if (!poFeature->IsFieldSetAndNotNull(iField))
1948 : {
1949 : papstBindBuffer[iCol]->VarChar.nSize = SQL_NULL_DATA;
1950 : /* set NULL */
1951 : if (Failed2(bcp_collen(hDBCBCP, SQL_NULL_DATA, iCol + 1)))
1952 : return OGRERR_FAILURE;
1953 : }
1954 : else
1955 : {
1956 : papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
1957 : snprintf(reinterpret_cast<char *>(
1958 : papstBindBuffer[iCol]->VarChar.pData),
1959 : 8000, "%s", poFeature->GetFieldAsString(iField));
1960 :
1961 : if (Failed2(bcp_collen(hDBCBCP, SQL_VARLEN_DATA, iCol + 1)))
1962 : return OGRERR_FAILURE;
1963 : }
1964 : }
1965 : else if (poFDefn->GetType() == OFTReal)
1966 : {
1967 : /* float */
1968 : if (!poFeature->IsFieldSetAndNotNull(iField))
1969 : {
1970 : papstBindBuffer[iCol]->VarChar.nSize = SQL_NULL_DATA;
1971 : /* set NULL */
1972 : if (Failed2(bcp_collen(hDBCBCP, SQL_NULL_DATA, iCol + 1)))
1973 : return OGRERR_FAILURE;
1974 : }
1975 : else
1976 : {
1977 : papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
1978 : snprintf(reinterpret_cast<char *>(
1979 : papstBindBuffer[iCol]->VarChar.pData),
1980 : 8000, "%s", poFeature->GetFieldAsString(iField));
1981 :
1982 : if (Failed2(bcp_collen(hDBCBCP, SQL_VARLEN_DATA, iCol + 1)))
1983 : return OGRERR_FAILURE;
1984 : }
1985 : }
1986 : else if (poFDefn->GetType() == OFTString)
1987 : {
1988 : /* nvarchar */
1989 : if (poFDefn->GetWidth() != 0)
1990 : {
1991 : if (!poFeature->IsFieldSetAndNotNull(iField))
1992 : {
1993 : papstBindBuffer[iCol]->VarChar.nSize = SQL_NULL_DATA;
1994 : if (Failed2(
1995 : bcp_collen(hDBCBCP, SQL_NULL_DATA, iCol + 1)))
1996 : return OGRERR_FAILURE;
1997 : }
1998 : else
1999 : {
2000 :
2001 : wchar_t *buffer = CPLRecodeToWChar(
2002 : poFeature->GetFieldAsString(iField), CPL_ENC_UTF8,
2003 : CPL_ENC_UCS2);
2004 : const auto nLen = wcslen(buffer);
2005 : papstBindBuffer[iCol]->VarChar.nSize =
2006 : static_cast<SQLLEN>(nLen * sizeof(GUInt16));
2007 : #if WCHAR_MAX > 0xFFFFu
2008 : // Shorten each character to a two-byte value, as
2009 : // expected by the ODBC driver
2010 : GUInt16 *panBuffer =
2011 : reinterpret_cast<GUInt16 *>(buffer);
2012 : for (unsigned int nIndex = 1; nIndex <= nLen;
2013 : nIndex += 1)
2014 : panBuffer[nIndex] =
2015 : static_cast<GUInt16>(buffer[nIndex]);
2016 : #endif
2017 : memcpy(papstBindBuffer[iCol]->VarChar.pData, buffer,
2018 : papstBindBuffer[iCol]->VarChar.nSize +
2019 : sizeof(GUInt16));
2020 : CPLFree(buffer);
2021 :
2022 : if (Failed2(bcp_collen(
2023 : hDBCBCP,
2024 : static_cast<DBINT>(
2025 : papstBindBuffer[iCol]->VarChar.nSize),
2026 : iCol + 1)))
2027 : return OGRERR_FAILURE;
2028 : }
2029 : }
2030 : }
2031 : else if (poFDefn->GetType() == OFTDate)
2032 : {
2033 : /* date */
2034 : if (!poFeature->IsFieldSetAndNotNull(iField))
2035 : {
2036 : papstBindBuffer[iCol]->VarChar.nSize = SQL_NULL_DATA;
2037 : /* set NULL */
2038 : if (Failed2(bcp_collen(hDBCBCP, SQL_NULL_DATA, iCol + 1)))
2039 : return OGRERR_FAILURE;
2040 : }
2041 : else
2042 : {
2043 : int pnYear;
2044 : int pnMonth;
2045 : int pnDay;
2046 : int pnHour;
2047 : int pnMinute;
2048 : float pfSecond;
2049 : int pnTZFlag;
2050 :
2051 : poFeature->GetFieldAsDateTime(iField, &pnYear, &pnMonth,
2052 : &pnDay, &pnHour, &pnMinute,
2053 : &pfSecond, &pnTZFlag);
2054 :
2055 : papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
2056 : snprintf(reinterpret_cast<char *>(
2057 : papstBindBuffer[iCol]->VarChar.pData),
2058 : 8000, "%4d-%02d-%02d %02d:%02d:%06.3f", pnYear,
2059 : pnMonth, pnDay, pnHour, pnMinute, pfSecond);
2060 : if (Failed2(bcp_collen(hDBCBCP, SQL_VARLEN_DATA, iCol + 1)))
2061 : return OGRERR_FAILURE;
2062 : }
2063 : }
2064 : else if (poFDefn->GetType() == OFTTime)
2065 : {
2066 : /* time(7) */
2067 : if (!poFeature->IsFieldSetAndNotNull(iField))
2068 : {
2069 : papstBindBuffer[iCol]->VarChar.nSize = SQL_NULL_DATA;
2070 : /* set NULL */
2071 : if (Failed2(bcp_collen(hDBCBCP, SQL_NULL_DATA, iCol + 1)))
2072 : return OGRERR_FAILURE;
2073 : }
2074 : else
2075 : {
2076 : int pnYear;
2077 : int pnMonth;
2078 : int pnDay;
2079 : int pnHour;
2080 : int pnMinute;
2081 : float pfSecond;
2082 : int pnTZFlag;
2083 :
2084 : poFeature->GetFieldAsDateTime(iField, &pnYear, &pnMonth,
2085 : &pnDay, &pnHour, &pnMinute,
2086 : &pfSecond, &pnTZFlag);
2087 :
2088 : papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
2089 : snprintf(reinterpret_cast<char *>(
2090 : papstBindBuffer[iCol]->VarChar.pData),
2091 : 8000, "%4d-%02d-%02d %02d:%02d:%06.3f", pnYear,
2092 : pnMonth, pnDay, pnHour, pnMinute, pfSecond);
2093 : if (Failed2(bcp_collen(hDBCBCP, SQL_VARLEN_DATA, iCol + 1)))
2094 : return OGRERR_FAILURE;
2095 : }
2096 : }
2097 : else if (poFDefn->GetType() == OFTDateTime)
2098 : {
2099 : /* datetime */
2100 : if (!poFeature->IsFieldSetAndNotNull(iField))
2101 : {
2102 : papstBindBuffer[iCol]->VarChar.nSize = SQL_NULL_DATA;
2103 : /* set NULL */
2104 : if (Failed2(bcp_collen(hDBCBCP, SQL_NULL_DATA, iCol + 1)))
2105 : return OGRERR_FAILURE;
2106 : }
2107 : else
2108 : {
2109 : int pnYear;
2110 : int pnMonth;
2111 : int pnDay;
2112 : int pnHour;
2113 : int pnMinute;
2114 : float pfSecond;
2115 : int pnTZFlag;
2116 :
2117 : poFeature->GetFieldAsDateTime(iField, &pnYear, &pnMonth,
2118 : &pnDay, &pnHour, &pnMinute,
2119 : &pfSecond, &pnTZFlag);
2120 :
2121 : papstBindBuffer[iCol]->VarChar.nSize = SQL_VARLEN_DATA;
2122 : snprintf(reinterpret_cast<char *>(
2123 : papstBindBuffer[iCol]->VarChar.pData),
2124 : 8000, "%4d-%02d-%02d %02d:%02d:%06.3f", pnYear,
2125 : pnMonth, pnDay, pnHour, pnMinute, pfSecond);
2126 :
2127 : if (Failed2(bcp_collen(hDBCBCP, SQL_VARLEN_DATA, iCol + 1)))
2128 : return OGRERR_FAILURE;
2129 : }
2130 : }
2131 : else if (poFDefn->GetType() == OFTBinary)
2132 : {
2133 : if (!poFeature->IsFieldSetAndNotNull(iField))
2134 : {
2135 : papstBindBuffer[iCol]->RawData.nSize = SQL_NULL_DATA;
2136 : /* set NULL */
2137 : if (Failed2(bcp_collen(hDBCBCP, SQL_NULL_DATA, iCol + 1)))
2138 : return OGRERR_FAILURE;
2139 : }
2140 : else
2141 : {
2142 : /* image */
2143 : int nLen;
2144 : papstBindBuffer[iCol]->RawData.pData =
2145 : poFeature->GetFieldAsBinary(iField, &nLen);
2146 : papstBindBuffer[iCol]->RawData.nSize = nLen;
2147 :
2148 : /* set data length */
2149 : if (Failed2(bcp_collen(
2150 : hDBCBCP,
2151 : static_cast<DBINT>(
2152 : papstBindBuffer[iCol]->RawData.nSize),
2153 : iCol + 1)))
2154 : return OGRERR_FAILURE;
2155 : }
2156 : }
2157 : else
2158 : {
2159 : CPLError(
2160 : CE_Failure, CPLE_NotSupported,
2161 : "Field %s with type %s is not supported for bulk insert.",
2162 : poFDefn->GetNameRef(),
2163 : OGRFieldDefn::GetFieldTypeName(poFDefn->GetType()));
2164 :
2165 : return OGRERR_FAILURE;
2166 : }
2167 :
2168 : ++iField;
2169 : }
2170 : }
2171 :
2172 : /* send row */
2173 : if (Failed2(bcp_sendrow(hDBCBCP)))
2174 : return OGRERR_FAILURE;
2175 :
2176 : /* send dynamic data */
2177 : iField = 0;
2178 : for (iCol = 0; iCol < nRawColumns; iCol++)
2179 : {
2180 : if (iCol == nGeomColumnIndex)
2181 : {
2182 : if (papstBindBuffer[iCol]->RawData.nSize != SQL_NULL_DATA)
2183 : {
2184 : if (Failed2(
2185 : bcp_moretext(hDBCBCP,
2186 : static_cast<DBINT>(
2187 : papstBindBuffer[iCol]->RawData.nSize),
2188 : papstBindBuffer[iCol]->RawData.pData)))
2189 : {
2190 : }
2191 : CPLFree(papstBindBuffer[iCol]->RawData.pData);
2192 : if (Failed2(bcp_moretext(hDBCBCP, 0, nullptr)))
2193 : {
2194 : }
2195 : }
2196 : else
2197 : {
2198 : if (Failed2(bcp_moretext(hDBCBCP, SQL_NULL_DATA, nullptr)))
2199 : {
2200 : }
2201 : }
2202 : }
2203 : else if (iCol == nFIDColumnIndex)
2204 : {
2205 : /* TODO */
2206 : continue;
2207 : }
2208 : else if (iField < poFeatureDefn->GetFieldCount() &&
2209 : iCol == panFieldOrdinals[iField])
2210 : {
2211 : OGRFieldDefn *poFDefn = poFeatureDefn->GetFieldDefn(iField);
2212 :
2213 : if (poFDefn->GetType() == OFTString)
2214 : {
2215 : if (poFDefn->GetWidth() == 0)
2216 : {
2217 : if (poFeature->IsFieldSetAndNotNull(iField))
2218 : {
2219 : const char *pszStr =
2220 : poFeature->GetFieldAsString(iField);
2221 : if (pszStr[0] != 0)
2222 : {
2223 : wchar_t *buffer = CPLRecodeToWChar(
2224 : poFeature->GetFieldAsString(iField),
2225 : CPL_ENC_UTF8, CPL_ENC_UCS2);
2226 : const auto nLen = wcslen(buffer);
2227 : papstBindBuffer[iCol]->VarChar.nSize =
2228 : static_cast<SQLLEN>(nLen * sizeof(GUInt16));
2229 : #if WCHAR_MAX > 0xFFFFu
2230 : // Shorten each character to a two-byte value, as
2231 : // expected by the ODBC driver
2232 : GUInt16 *panBuffer =
2233 : reinterpret_cast<GUInt16 *>(buffer);
2234 : for (unsigned int nIndex = 1; nIndex <= nLen;
2235 : nIndex += 1)
2236 : panBuffer[nIndex] =
2237 : static_cast<GUInt16>(buffer[nIndex]);
2238 : #endif
2239 : if (Failed2(bcp_moretext(
2240 : hDBCBCP,
2241 : static_cast<DBINT>(
2242 : papstBindBuffer[iCol]->VarChar.nSize),
2243 : reinterpret_cast<LPCBYTE>(buffer))))
2244 : {
2245 : }
2246 :
2247 : CPLFree(buffer);
2248 : }
2249 :
2250 : if (Failed2(bcp_moretext(hDBCBCP, 0, nullptr)))
2251 : {
2252 : }
2253 : }
2254 : else
2255 : {
2256 : if (Failed2(
2257 : bcp_moretext(hDBCBCP, SQL_NULL_DATA, nullptr)))
2258 : {
2259 : }
2260 : }
2261 : }
2262 : }
2263 : else if (poFDefn->GetType() == OFTBinary)
2264 : {
2265 : if (papstBindBuffer[iCol]->RawData.nSize != SQL_NULL_DATA)
2266 : {
2267 : if (papstBindBuffer[iCol]->RawData.nSize > 0)
2268 : {
2269 : if (Failed2(bcp_moretext(
2270 : hDBCBCP,
2271 : static_cast<DBINT>(
2272 : papstBindBuffer[iCol]->RawData.nSize),
2273 : papstBindBuffer[iCol]->RawData.pData)))
2274 : {
2275 : }
2276 : }
2277 : else
2278 : {
2279 : Failed2(bcp_moretext(hDBCBCP, 0, nullptr));
2280 : }
2281 : }
2282 : else
2283 : {
2284 : if (Failed2(bcp_moretext(hDBCBCP, SQL_NULL_DATA, nullptr)))
2285 : {
2286 : }
2287 : }
2288 : }
2289 : ++iField;
2290 : }
2291 : }
2292 :
2293 : if (++nBCPCount >= nBCPSize)
2294 : {
2295 : /* commit */
2296 : int nRecNum = bcp_batch(hDBCBCP);
2297 : if (nRecNum == -1)
2298 : Failed2(nRecNum);
2299 :
2300 : nBCPCount = 0;
2301 : }
2302 :
2303 : return OGRERR_NONE;
2304 : }
2305 : #endif /* MSSQL_BCP_SUPPORTED */
2306 :
2307 : /************************************************************************/
2308 : /* ICreateFeature() */
2309 : /************************************************************************/
2310 :
2311 0 : OGRErr OGRMSSQLSpatialTableLayer::ICreateFeature(OGRFeature *poFeature)
2312 :
2313 : {
2314 0 : if (!bUpdateAccess)
2315 : {
2316 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
2317 : "CreateFeature");
2318 0 : return OGRERR_FAILURE;
2319 : }
2320 :
2321 0 : GetLayerDefn();
2322 :
2323 0 : if (nullptr == poFeature)
2324 : {
2325 0 : CPLError(CE_Failure, CPLE_AppDefined,
2326 : "NULL pointer to OGRFeature passed to CreateFeature().");
2327 0 : return OGRERR_FAILURE;
2328 : }
2329 :
2330 : #if (ODBCVER >= 0x0300) && defined(MSSQL_BCP_SUPPORTED)
2331 : if (bUseCopy && !m_bHasUUIDColumn)
2332 : {
2333 : return CreateFeatureBCP(poFeature);
2334 : }
2335 : #endif
2336 :
2337 0 : ClearStatement();
2338 :
2339 0 : CPLODBCSession *poSession = poDS->GetSession();
2340 :
2341 : /* the fid values are retrieved from the source layer */
2342 0 : CPLODBCStatement oStatement(poSession);
2343 :
2344 0 : if (poFeature->GetFID() != OGRNullFID && pszFIDColumn != nullptr &&
2345 0 : bIsIdentityFid)
2346 0 : oStatement.Appendf("SET IDENTITY_INSERT [%s].[%s] ON;", pszSchemaName,
2347 : pszTableName);
2348 :
2349 : /* -------------------------------------------------------------------- */
2350 : /* Form the INSERT command. */
2351 : /* -------------------------------------------------------------------- */
2352 :
2353 0 : oStatement.Appendf("INSERT INTO [%s].[%s] ", pszSchemaName, pszTableName);
2354 :
2355 0 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
2356 0 : GIntBig nFID = poFeature->GetFID();
2357 0 : if (bUseGeometryValidation && poGeom != nullptr)
2358 : {
2359 0 : OGRMSSQLGeometryValidator oValidator(poGeom, nGeomColumnType);
2360 0 : if (!oValidator.IsValid())
2361 : {
2362 0 : oValidator.MakeValid(poGeom);
2363 0 : CPLError(CE_Warning, CPLE_NotSupported,
2364 : "Geometry with FID = " CPL_FRMT_GIB
2365 : " has been modified to valid geometry.",
2366 : poFeature->GetFID());
2367 : }
2368 : }
2369 :
2370 0 : int bNeedComma = FALSE;
2371 :
2372 0 : if (poGeom != nullptr && pszGeomColumn != nullptr)
2373 : {
2374 0 : oStatement.Append("([");
2375 0 : oStatement.Append(pszGeomColumn);
2376 0 : oStatement.Append("]");
2377 0 : bNeedComma = TRUE;
2378 : }
2379 :
2380 0 : if (nFID != OGRNullFID && pszFIDColumn != nullptr)
2381 : {
2382 0 : if (!CPL_INT64_FITS_ON_INT32(nFID) &&
2383 0 : GetMetadataItem(OLMD_FID64) == nullptr)
2384 : {
2385 : /* MSSQL server doesn't support modifying pk columns without
2386 : * recreating the field */
2387 0 : CPLError(CE_Failure, CPLE_AppDefined,
2388 : "Failed to create feature with large integer fid. "
2389 : "The FID64 layer creation option should be used.");
2390 :
2391 0 : return OGRERR_FAILURE;
2392 : }
2393 :
2394 0 : if (bNeedComma)
2395 0 : oStatement.Appendf(", [%s]", pszFIDColumn);
2396 : else
2397 : {
2398 0 : oStatement.Appendf("([%s]", pszFIDColumn);
2399 0 : bNeedComma = TRUE;
2400 : }
2401 : }
2402 :
2403 0 : int nFieldCount = poFeatureDefn->GetFieldCount();
2404 :
2405 0 : int bind_num = 0;
2406 : void **bind_buffer =
2407 0 : static_cast<void **>(CPLMalloc(sizeof(void *) * (nFieldCount + 1)));
2408 : #ifdef SQL_SS_UDT
2409 : SQLLEN *bind_datalen =
2410 : static_cast<SQLLEN *>(CPLMalloc(sizeof(SQLLEN) * (nFieldCount + 1)));
2411 : #endif
2412 :
2413 : int i;
2414 0 : for (i = 0; i < nFieldCount; i++)
2415 : {
2416 0 : if (!poFeature->IsFieldSetAndNotNull(i))
2417 0 : continue;
2418 :
2419 0 : if (bNeedComma)
2420 0 : oStatement.Appendf(", [%s]",
2421 0 : poFeatureDefn->GetFieldDefn(i)->GetNameRef());
2422 : else
2423 : {
2424 0 : oStatement.Appendf("([%s]",
2425 0 : poFeatureDefn->GetFieldDefn(i)->GetNameRef());
2426 0 : bNeedComma = TRUE;
2427 : }
2428 : }
2429 :
2430 : SQLLEN nWKBLenBindParameter;
2431 0 : if (oStatement.GetCommand()[strlen(oStatement.GetCommand()) - 1] != ']')
2432 : {
2433 : /* no fields were added */
2434 :
2435 0 : if (nFID == OGRNullFID && pszFIDColumn != nullptr &&
2436 0 : (bIsIdentityFid || poDS->AlwaysOutputFid()))
2437 0 : oStatement.Appendf(" OUTPUT INSERTED.[%s] DEFAULT VALUES;",
2438 0 : GetFIDColumn());
2439 : else
2440 0 : oStatement.Appendf("DEFAULT VALUES;");
2441 : }
2442 : else
2443 : {
2444 : /* prepend VALUES section */
2445 0 : if (nFID == OGRNullFID && pszFIDColumn != nullptr &&
2446 0 : (bIsIdentityFid || poDS->AlwaysOutputFid()))
2447 0 : oStatement.Appendf(") OUTPUT INSERTED.[%s] VALUES (",
2448 0 : GetFIDColumn());
2449 : else
2450 0 : oStatement.Appendf(") VALUES (");
2451 :
2452 : /* Set the geometry */
2453 0 : bNeedComma = FALSE;
2454 0 : if (poGeom != nullptr && pszGeomColumn != nullptr)
2455 : {
2456 0 : int nOutgoingSRSId = 0;
2457 :
2458 : // Use the SRID specified by the provided feature's geometry, if
2459 : // its spatial-reference system is known; otherwise, use the SRID
2460 : // associated with the table
2461 : const OGRSpatialReference *poFeatureSRS =
2462 0 : poGeom->getSpatialReference();
2463 0 : if (poFeatureSRS)
2464 0 : nOutgoingSRSId = poDS->FetchSRSId(poFeatureSRS);
2465 0 : if (nOutgoingSRSId <= 0)
2466 0 : nOutgoingSRSId = nSRSId;
2467 :
2468 0 : if (nUploadGeometryFormat == MSSQLGEOMETRY_NATIVE)
2469 : {
2470 : #ifdef SQL_SS_UDT
2471 : OGRMSSQLGeometryWriter poWriter(poGeom, nGeomColumnType,
2472 : nOutgoingSRSId);
2473 : bind_datalen[bind_num] = poWriter.GetDataLen();
2474 : GByte *pabyData =
2475 : static_cast<GByte *>(CPLMalloc(bind_datalen[bind_num] + 1));
2476 : if (poWriter.WriteSqlGeometry(
2477 : pabyData, static_cast<int>(bind_datalen[bind_num])) ==
2478 : OGRERR_NONE)
2479 : {
2480 : SQLHANDLE ipd;
2481 : if ((!poSession->Failed(SQLBindParameter(
2482 : oStatement.GetStatement(),
2483 : static_cast<SQLUSMALLINT>(bind_num + 1),
2484 : SQL_PARAM_INPUT, SQL_C_BINARY, SQL_SS_UDT,
2485 : SQL_SS_LENGTH_UNLIMITED, 0,
2486 : static_cast<SQLPOINTER>(pabyData),
2487 : bind_datalen[bind_num],
2488 : reinterpret_cast<SQLLEN *>(
2489 : &bind_datalen[bind_num])))) &&
2490 : (!poSession->Failed(SQLGetStmtAttr(
2491 : oStatement.GetStatement(), SQL_ATTR_IMP_PARAM_DESC,
2492 : &ipd, 0, nullptr))) &&
2493 : (!poSession->Failed(SQLSetDescField(
2494 : ipd, 1, SQL_CA_SS_UDT_TYPE_NAME,
2495 : const_cast<char *>(nGeomColumnType ==
2496 : MSSQLCOLTYPE_GEOGRAPHY
2497 : ? "geography"
2498 : : "geometry"),
2499 : SQL_NTS))))
2500 : {
2501 : oStatement.Append("?");
2502 : bind_buffer[bind_num] = pabyData;
2503 : ++bind_num;
2504 : }
2505 : else
2506 : {
2507 : oStatement.Append("null");
2508 : CPLFree(pabyData);
2509 : }
2510 : }
2511 : else
2512 : {
2513 : oStatement.Append("null");
2514 : CPLFree(pabyData);
2515 : }
2516 : #else
2517 0 : CPLError(CE_Failure, CPLE_AppDefined,
2518 : "Native geometry upload is not supported");
2519 :
2520 : // No need to free bind_buffer[i] since bind_num == 0 in that
2521 : // branch
2522 0 : CPLFree(bind_buffer);
2523 :
2524 0 : return OGRERR_FAILURE;
2525 : #endif
2526 : // CPLFree(pabyData);
2527 : }
2528 0 : else if (nUploadGeometryFormat == MSSQLGEOMETRY_WKB)
2529 : {
2530 0 : const size_t nWKBLen = poGeom->WkbSize();
2531 : GByte *pabyWKB = static_cast<GByte *>(
2532 0 : VSI_MALLOC_VERBOSE(nWKBLen + 1)); // do we need the +1 ?
2533 0 : if (pabyWKB == nullptr)
2534 : {
2535 0 : oStatement.Append("null");
2536 : }
2537 0 : else if (poGeom->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso) ==
2538 0 : OGRERR_NONE &&
2539 0 : (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY ||
2540 0 : nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY))
2541 : {
2542 0 : nWKBLenBindParameter = nWKBLen;
2543 0 : int nRetCode = SQLBindParameter(
2544 : oStatement.GetStatement(),
2545 0 : static_cast<SQLUSMALLINT>(bind_num + 1),
2546 : SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY,
2547 : nWKBLen, 0, static_cast<SQLPOINTER>(pabyWKB), nWKBLen,
2548 0 : &nWKBLenBindParameter);
2549 0 : if (nRetCode == SQL_SUCCESS ||
2550 : nRetCode == SQL_SUCCESS_WITH_INFO)
2551 : {
2552 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
2553 : {
2554 0 : oStatement.Append("geography::STGeomFromWKB(?");
2555 0 : oStatement.Appendf(",%d)", nOutgoingSRSId);
2556 : }
2557 : else
2558 : {
2559 0 : oStatement.Append("geometry::STGeomFromWKB(?");
2560 0 : oStatement.Appendf(",%d).MakeValid()",
2561 : nOutgoingSRSId);
2562 : }
2563 0 : bind_buffer[bind_num] = pabyWKB;
2564 0 : ++bind_num;
2565 : }
2566 : else
2567 : {
2568 0 : oStatement.Append("null");
2569 0 : CPLFree(pabyWKB);
2570 : }
2571 : }
2572 : else
2573 : {
2574 0 : oStatement.Append("null");
2575 0 : CPLFree(pabyWKB);
2576 : }
2577 : }
2578 0 : else if (nUploadGeometryFormat == MSSQLGEOMETRY_WKT)
2579 : {
2580 0 : char *pszWKT = nullptr;
2581 0 : if (poGeom->exportToWkt(&pszWKT) == OGRERR_NONE &&
2582 0 : (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY ||
2583 0 : nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY))
2584 : {
2585 0 : size_t nLen = 0;
2586 0 : while (pszWKT[nLen] != '\0')
2587 0 : nLen++;
2588 :
2589 0 : int nRetCode = SQLBindParameter(
2590 : oStatement.GetStatement(),
2591 0 : static_cast<SQLUSMALLINT>(bind_num + 1),
2592 : SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR, nLen, 0,
2593 0 : static_cast<SQLPOINTER>(pszWKT), 0, nullptr);
2594 0 : if (nRetCode == SQL_SUCCESS ||
2595 : nRetCode == SQL_SUCCESS_WITH_INFO)
2596 : {
2597 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
2598 : {
2599 0 : oStatement.Append("geography::STGeomFromText(?");
2600 0 : oStatement.Appendf(",%d)", nOutgoingSRSId);
2601 : }
2602 : else
2603 : {
2604 0 : oStatement.Append("geometry::STGeomFromText(?");
2605 0 : oStatement.Appendf(",%d).MakeValid()",
2606 : nOutgoingSRSId);
2607 : }
2608 0 : bind_buffer[bind_num] = pszWKT;
2609 0 : ++bind_num;
2610 : }
2611 : else
2612 : {
2613 0 : oStatement.Append("null");
2614 0 : CPLFree(pszWKT);
2615 : }
2616 : }
2617 : else
2618 : {
2619 0 : oStatement.Append("null");
2620 0 : CPLFree(pszWKT);
2621 : }
2622 : }
2623 : else
2624 0 : oStatement.Append("null");
2625 :
2626 0 : bNeedComma = TRUE;
2627 : }
2628 :
2629 : /* Set the FID */
2630 0 : if (nFID != OGRNullFID && pszFIDColumn != nullptr)
2631 : {
2632 0 : if (bNeedComma)
2633 0 : oStatement.Appendf(", " CPL_FRMT_GIB, nFID);
2634 : else
2635 : {
2636 0 : oStatement.Appendf(CPL_FRMT_GIB, nFID);
2637 0 : bNeedComma = TRUE;
2638 : }
2639 : }
2640 :
2641 0 : for (i = 0; i < nFieldCount; i++)
2642 : {
2643 0 : if (!poFeature->IsFieldSetAndNotNull(i))
2644 0 : continue;
2645 :
2646 0 : if (bNeedComma)
2647 0 : oStatement.Append(", ");
2648 : else
2649 0 : bNeedComma = TRUE;
2650 :
2651 0 : AppendFieldValue(&oStatement, poFeature, i, &bind_num, bind_buffer);
2652 : }
2653 :
2654 0 : oStatement.Append(");");
2655 : }
2656 :
2657 0 : if (nFID != OGRNullFID && pszFIDColumn != nullptr && bIsIdentityFid)
2658 0 : oStatement.Appendf("SET IDENTITY_INSERT [%s].[%s] OFF;", pszSchemaName,
2659 : pszTableName);
2660 :
2661 : /* -------------------------------------------------------------------- */
2662 : /* Execute the insert. */
2663 : /* -------------------------------------------------------------------- */
2664 :
2665 0 : if (!oStatement.ExecuteSQL())
2666 : {
2667 0 : CPLError(CE_Failure, CPLE_AppDefined,
2668 : "INSERT command for new feature failed. %s",
2669 0 : poDS->GetSession()->GetLastError());
2670 :
2671 0 : for (i = 0; i < bind_num; i++)
2672 0 : CPLFree(bind_buffer[i]);
2673 0 : CPLFree(bind_buffer);
2674 :
2675 : #ifdef SQL_SS_UDT
2676 : CPLFree(bind_datalen);
2677 : #endif
2678 :
2679 0 : return OGRERR_FAILURE;
2680 : }
2681 0 : else if (nFID == OGRNullFID && pszFIDColumn != nullptr &&
2682 0 : (bIsIdentityFid || poDS->AlwaysOutputFid()))
2683 : {
2684 : // fetch new ID and set it into the feature
2685 0 : if (oStatement.Fetch())
2686 : {
2687 0 : GIntBig newID = atoll(oStatement.GetColData(0));
2688 0 : poFeature->SetFID(newID);
2689 : }
2690 : }
2691 :
2692 0 : for (i = 0; i < bind_num; i++)
2693 0 : CPLFree(bind_buffer[i]);
2694 0 : CPLFree(bind_buffer);
2695 :
2696 : #ifdef SQL_SS_UDT
2697 : CPLFree(bind_datalen);
2698 : #endif
2699 :
2700 0 : return OGRERR_NONE;
2701 : }
2702 :
2703 : /************************************************************************/
2704 : /* AppendFieldValue() */
2705 : /* */
2706 : /* Used by CreateFeature() and SetFeature() to format a */
2707 : /* non-empty field value */
2708 : /************************************************************************/
2709 :
2710 0 : void OGRMSSQLSpatialTableLayer::AppendFieldValue(CPLODBCStatement *poStatement,
2711 : OGRFeature *poFeature, int i,
2712 : int *bind_num,
2713 : void **bind_buffer)
2714 : {
2715 0 : int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
2716 0 : int nOGRFieldSubType = poFeatureDefn->GetFieldDefn(i)->GetSubType();
2717 :
2718 : // We need special formatting for integer list values.
2719 0 : if (nOGRFieldType == OFTIntegerList)
2720 : {
2721 : // TODO
2722 0 : poStatement->Append("null");
2723 0 : return;
2724 : }
2725 :
2726 : // We need special formatting for real list values.
2727 0 : else if (nOGRFieldType == OFTRealList)
2728 : {
2729 : // TODO
2730 0 : poStatement->Append("null");
2731 0 : return;
2732 : }
2733 :
2734 : // We need special formatting for string list values.
2735 0 : else if (nOGRFieldType == OFTStringList)
2736 : {
2737 : // TODO
2738 0 : poStatement->Append("null");
2739 0 : return;
2740 : }
2741 :
2742 : // Binary formatting
2743 0 : if (nOGRFieldType == OFTBinary)
2744 : {
2745 0 : int nLen = 0;
2746 0 : GByte *pabyData = poFeature->GetFieldAsBinary(i, &nLen);
2747 0 : char *pszBytes = GByteArrayToHexString(pabyData, nLen);
2748 0 : poStatement->Append(pszBytes);
2749 0 : CPLFree(pszBytes);
2750 0 : return;
2751 : }
2752 :
2753 : // Datetime values need special handling as SQL Server's datetime type
2754 : // accepts values only in ISO 8601 format and only without time zone
2755 : // information
2756 0 : else if (nOGRFieldType == OFTDateTime)
2757 : {
2758 0 : char *pszStrValue = OGRGetXMLDateTime((*poFeature)[i].GetRawValue());
2759 :
2760 0 : int nRetCode = SQLBindParameter(
2761 : poStatement->GetStatement(),
2762 0 : static_cast<SQLUSMALLINT>((*bind_num) + 1), SQL_PARAM_INPUT,
2763 0 : SQL_C_CHAR, SQL_VARCHAR, strlen(pszStrValue) + 1, 0,
2764 0 : static_cast<SQLPOINTER>(pszStrValue), 0, nullptr);
2765 0 : if (nRetCode == SQL_SUCCESS || nRetCode == SQL_SUCCESS_WITH_INFO)
2766 : {
2767 0 : bind_buffer[*bind_num] = pszStrValue;
2768 0 : ++(*bind_num);
2769 0 : poStatement->Append("CAST(CAST(? AS datetimeoffset) AS datetime)");
2770 : }
2771 : else
2772 : {
2773 0 : poStatement->Append(CPLSPrintf(
2774 : "CAST(CAST('%s' AS datetimeoffset) AS datetime)", pszStrValue));
2775 0 : CPLFree(pszStrValue);
2776 : }
2777 0 : return;
2778 : }
2779 :
2780 : // Flag indicating NULL or not-a-date date value
2781 : // e.g. 0000-00-00 - there is no year 0
2782 0 : bool bIsDateNull = FALSE;
2783 :
2784 0 : const char *pszStrValue = poFeature->GetFieldAsString(i);
2785 :
2786 : // Check if date is NULL: 0000-00-00
2787 0 : if (nOGRFieldType == OFTDate)
2788 : {
2789 0 : if (STARTS_WITH_CI(pszStrValue, "0000"))
2790 : {
2791 0 : pszStrValue = "null";
2792 0 : bIsDateNull = TRUE;
2793 : }
2794 : }
2795 0 : else if (nOGRFieldType == OFTReal)
2796 : {
2797 0 : char *pszComma = strchr(const_cast<char *>(pszStrValue), ',');
2798 0 : if (pszComma)
2799 0 : *pszComma = '.';
2800 : }
2801 :
2802 0 : if (nOGRFieldType != OFTInteger && nOGRFieldType != OFTInteger64 &&
2803 0 : nOGRFieldType != OFTReal && !bIsDateNull)
2804 : {
2805 0 : if (nOGRFieldType == OFTString)
2806 : {
2807 0 : if (nOGRFieldSubType == OFSTUUID)
2808 : {
2809 0 : int nRetCode = SQLBindParameter(
2810 : poStatement->GetStatement(),
2811 0 : static_cast<SQLUSMALLINT>((*bind_num) + 1), SQL_PARAM_INPUT,
2812 : SQL_C_CHAR, SQL_GUID, 16, 0,
2813 : const_cast<SQLPOINTER>(
2814 : static_cast<const void *>(pszStrValue)),
2815 0 : 0, nullptr);
2816 0 : if (nRetCode == SQL_SUCCESS ||
2817 : nRetCode == SQL_SUCCESS_WITH_INFO)
2818 : {
2819 0 : poStatement->Append("?");
2820 0 : bind_buffer[*bind_num] = CPLStrdup(pszStrValue);
2821 0 : ++(*bind_num);
2822 : }
2823 : else
2824 : {
2825 0 : OGRMSSQLAppendEscaped(poStatement, pszStrValue);
2826 : }
2827 : }
2828 : else
2829 : {
2830 : // bind UTF8 as unicode parameter
2831 : wchar_t *buffer =
2832 0 : CPLRecodeToWChar(pszStrValue, CPL_ENC_UTF8, CPL_ENC_UCS2);
2833 0 : size_t nLen = wcslen(buffer) + 1;
2834 0 : if (nLen > 4000)
2835 : {
2836 : /* need to handle nvarchar(max) */
2837 : #ifdef SQL_SS_LENGTH_UNLIMITED
2838 : nLen = SQL_SS_LENGTH_UNLIMITED;
2839 : #else
2840 : /* for older drivers truncate the data to 4000 chars */
2841 0 : buffer[4000] = 0;
2842 0 : nLen = 4000;
2843 0 : CPLError(CE_Warning, CPLE_AppDefined,
2844 : "String data truncation applied on field: %s. Use "
2845 : "a more recent ODBC driver that supports handling "
2846 : "large string values.",
2847 0 : poFeatureDefn->GetFieldDefn(i)->GetNameRef());
2848 : #endif
2849 : }
2850 : #if WCHAR_MAX > 0xFFFFu
2851 : // Shorten each character to a two-byte value, as expected by
2852 : // the ODBC driver
2853 0 : GUInt16 *panBuffer = reinterpret_cast<GUInt16 *>(buffer);
2854 0 : for (unsigned int nIndex = 1; nIndex < nLen; nIndex += 1)
2855 0 : panBuffer[nIndex] = static_cast<GUInt16>(buffer[nIndex]);
2856 : #endif
2857 0 : int nRetCode = SQLBindParameter(
2858 : poStatement->GetStatement(),
2859 0 : static_cast<SQLUSMALLINT>((*bind_num) + 1), SQL_PARAM_INPUT,
2860 : SQL_C_WCHAR, SQL_WVARCHAR, nLen, 0,
2861 0 : static_cast<SQLPOINTER>(buffer), 0, nullptr);
2862 0 : if (nRetCode == SQL_SUCCESS ||
2863 : nRetCode == SQL_SUCCESS_WITH_INFO)
2864 : {
2865 0 : poStatement->Append("?");
2866 0 : bind_buffer[*bind_num] = buffer;
2867 0 : ++(*bind_num);
2868 : }
2869 : else
2870 : {
2871 0 : OGRMSSQLAppendEscaped(poStatement, pszStrValue);
2872 0 : CPLFree(buffer);
2873 : }
2874 : }
2875 : }
2876 : else
2877 0 : OGRMSSQLAppendEscaped(poStatement, pszStrValue);
2878 : }
2879 : else
2880 : {
2881 0 : poStatement->Append(pszStrValue);
2882 : }
2883 : }
|