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