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