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