1 : /******************************************************************************
2 : * $Id: ogrmssqlspatialtablelayer.cpp 25558 2013-01-26 16:23:11Z tamas $
3 : *
4 : * Project: MSSQL Spatial driver
5 : * Purpose: Implements OGRMSSQLSpatialTableLayer class, access to an existing table.
6 : * Author: Tamas Szekeres, szekerest at gmail.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2010, Tamas Szekeres
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 :
33 : CPL_CVSID("$Id: ogrmssqlspatialtablelayer.cpp 25558 2013-01-26 16:23:11Z tamas $");
34 :
35 : /************************************************************************/
36 : /* OGRMSSQLAppendEscaped( ) */
37 : /************************************************************************/
38 :
39 0 : void OGRMSSQLAppendEscaped( CPLODBCStatement* poStatement, const char* pszStrValue)
40 : {
41 0 : if (!pszStrValue)
42 0 : poStatement->Append("null");
43 :
44 0 : size_t iIn, iOut , nTextLen = strlen(pszStrValue);
45 0 : char *pszEscapedText = (char *) VSIMalloc(nTextLen*2 + 3);
46 :
47 0 : pszEscapedText[0] = '\'';
48 :
49 0 : for( iIn = 0, iOut = 1; iIn < nTextLen; iIn++ )
50 : {
51 0 : switch( pszStrValue[iIn] )
52 : {
53 : case '\'':
54 0 : pszEscapedText[iOut++] = '\''; // double quote
55 0 : pszEscapedText[iOut++] = pszStrValue[iIn];
56 0 : break;
57 :
58 : default:
59 0 : pszEscapedText[iOut++] = pszStrValue[iIn];
60 : break;
61 : }
62 : }
63 :
64 0 : pszEscapedText[iOut++] = '\'';
65 :
66 0 : pszEscapedText[iOut] = '\0';
67 :
68 0 : poStatement->Append(pszEscapedText);
69 :
70 0 : CPLFree( pszEscapedText );
71 0 : }
72 :
73 : /************************************************************************/
74 : /* OGRMSSQLSpatialTableLayer() */
75 : /************************************************************************/
76 :
77 0 : OGRMSSQLSpatialTableLayer::OGRMSSQLSpatialTableLayer( OGRMSSQLSpatialDataSource *poDSIn )
78 :
79 : {
80 0 : poDS = poDSIn;
81 :
82 0 : pszQuery = NULL;
83 :
84 0 : bUpdateAccess = TRUE;
85 :
86 0 : iNextShapeId = 0;
87 :
88 0 : nSRSId = -1;
89 :
90 0 : poFeatureDefn = NULL;
91 :
92 0 : pszTableName = NULL;
93 0 : pszLayerName = NULL;
94 0 : pszSchemaName = NULL;
95 0 : }
96 :
97 : /************************************************************************/
98 : /* ~OGRMSSQLSpatialTableLayer() */
99 : /************************************************************************/
100 :
101 0 : OGRMSSQLSpatialTableLayer::~OGRMSSQLSpatialTableLayer()
102 :
103 : {
104 0 : CPLFree( pszTableName );
105 0 : CPLFree( pszLayerName );
106 0 : CPLFree( pszSchemaName );
107 :
108 0 : CPLFree( pszQuery );
109 0 : ClearStatement();
110 0 : }
111 :
112 : /************************************************************************/
113 : /* GetName() */
114 : /************************************************************************/
115 :
116 0 : const char *OGRMSSQLSpatialTableLayer::GetName()
117 :
118 : {
119 0 : return pszLayerName;
120 : }
121 :
122 : /************************************************************************/
123 : /* GetLayerDefn() */
124 : /************************************************************************/
125 0 : OGRFeatureDefn* OGRMSSQLSpatialTableLayer::GetLayerDefn()
126 : {
127 0 : if (poFeatureDefn)
128 0 : return poFeatureDefn;
129 :
130 0 : CPLODBCSession *poSession = poDS->GetSession();
131 : /* -------------------------------------------------------------------- */
132 : /* Do we have a simple primary key? */
133 : /* -------------------------------------------------------------------- */
134 0 : CPLODBCStatement oGetKey( poSession );
135 :
136 0 : if( oGetKey.GetPrimaryKeys( pszTableName, poDS->GetCatalog(), pszSchemaName )
137 : && oGetKey.Fetch() )
138 : {
139 0 : pszFIDColumn = CPLStrdup(oGetKey.GetColData( 3 ));
140 :
141 0 : if( oGetKey.Fetch() ) // more than one field in key!
142 : {
143 0 : CPLFree( pszFIDColumn );
144 0 : pszFIDColumn = NULL;
145 :
146 : CPLDebug( "OGR_MSSQLSpatial", "Table %s has multiple primary key fields, "
147 0 : "ignoring them all.", pszTableName );
148 : }
149 : }
150 :
151 :
152 : /* -------------------------------------------------------------------- */
153 : /* Get the column definitions for this table. */
154 : /* -------------------------------------------------------------------- */
155 0 : CPLODBCStatement oGetCol( poSession );
156 : CPLErr eErr;
157 :
158 0 : if( !oGetCol.GetColumns( pszTableName, poDS->GetCatalog(), pszSchemaName ) )
159 0 : return NULL;
160 :
161 0 : eErr = BuildFeatureDefn( pszLayerName, &oGetCol );
162 0 : if( eErr != CE_None )
163 0 : return NULL;
164 :
165 0 : poFeatureDefn->SetGeomType(eGeomType);
166 :
167 0 : if( poFeatureDefn->GetFieldCount() == 0 &&
168 : pszFIDColumn == NULL && pszGeomColumn == NULL )
169 : {
170 : CPLError( CE_Failure, CPLE_AppDefined,
171 : "No column definitions found for table '%s', layer not usable.",
172 0 : pszLayerName );
173 0 : return NULL;
174 : }
175 :
176 : /* -------------------------------------------------------------------- */
177 : /* If we got a geometry column, does it exist? Is it binary? */
178 : /* -------------------------------------------------------------------- */
179 0 : if( pszGeomColumn != NULL )
180 : {
181 0 : int iColumn = oGetCol.GetColId( pszGeomColumn );
182 0 : if( iColumn < 0 )
183 : {
184 : CPLError( CE_Failure, CPLE_AppDefined,
185 : "Column %s requested for geometry, but it does not exist.",
186 0 : pszGeomColumn );
187 0 : CPLFree( pszGeomColumn );
188 0 : pszGeomColumn = NULL;
189 : }
190 : else
191 : {
192 0 : if ( nGeomColumnType < 0 )
193 : {
194 : /* last attempt to identify the geometry column type */
195 0 : if ( EQUAL(oGetCol.GetColTypeName( iColumn ), "geometry") )
196 0 : nGeomColumnType = MSSQLCOLTYPE_GEOMETRY;
197 0 : else if ( EQUAL(oGetCol.GetColTypeName( iColumn ), "geography") )
198 0 : nGeomColumnType = MSSQLCOLTYPE_GEOGRAPHY;
199 0 : else if ( EQUAL(oGetCol.GetColTypeName( iColumn ), "varchar") )
200 0 : nGeomColumnType = MSSQLCOLTYPE_TEXT;
201 0 : else if ( EQUAL(oGetCol.GetColTypeName( iColumn ), "nvarchar") )
202 0 : nGeomColumnType = MSSQLCOLTYPE_TEXT;
203 0 : else if ( EQUAL(oGetCol.GetColTypeName( iColumn ), "text") )
204 0 : nGeomColumnType = MSSQLCOLTYPE_TEXT;
205 0 : else if ( EQUAL(oGetCol.GetColTypeName( iColumn ), "ntext") )
206 0 : nGeomColumnType = MSSQLCOLTYPE_TEXT;
207 0 : else if ( EQUAL(oGetCol.GetColTypeName( iColumn ), "image") )
208 0 : nGeomColumnType = MSSQLCOLTYPE_BINARY;
209 : else
210 : {
211 : CPLError( CE_Failure, CPLE_AppDefined,
212 : "Column type %s is not supported for geometry column.",
213 0 : oGetCol.GetColTypeName( iColumn ) );
214 0 : CPLFree( pszGeomColumn );
215 0 : pszGeomColumn = NULL;
216 : }
217 : }
218 : }
219 : }
220 :
221 0 : return poFeatureDefn;
222 : }
223 :
224 : /************************************************************************/
225 : /* Initialize() */
226 : /************************************************************************/
227 :
228 0 : CPLErr OGRMSSQLSpatialTableLayer::Initialize( const char *pszSchema,
229 : const char *pszLayerName,
230 : const char *pszGeomCol,
231 : int nCoordDimension,
232 : int nSRId,
233 : OGRwkbGeometryType eType )
234 :
235 : {
236 0 : CPLFree( pszFIDColumn );
237 0 : pszFIDColumn = NULL;
238 :
239 : /* -------------------------------------------------------------------- */
240 : /* Parse out schema name if present in layer. We assume a */
241 : /* schema is provided if there is a dot in the name, and that */
242 : /* it is in the form <schema>.<tablename> */
243 : /* -------------------------------------------------------------------- */
244 0 : this->pszLayerName = CPLStrdup(pszLayerName);
245 0 : const char *pszDot = strstr(pszLayerName,".");
246 0 : if( pszDot != NULL )
247 : {
248 0 : pszTableName = CPLStrdup(pszDot + 1);
249 0 : pszSchemaName = CPLStrdup(pszLayerName);
250 0 : pszSchemaName[pszDot - pszLayerName] = '\0';
251 : }
252 : else
253 : {
254 0 : pszTableName = CPLStrdup(pszLayerName);
255 0 : pszSchemaName = CPLStrdup(pszSchema);
256 : }
257 :
258 : /* -------------------------------------------------------------------- */
259 : /* Have we been provided a geometry column? */
260 : /* -------------------------------------------------------------------- */
261 0 : CPLFree( pszGeomColumn );
262 0 : if( pszGeomCol == NULL )
263 0 : pszGeomColumn = NULL;
264 : else
265 0 : pszGeomColumn = CPLStrdup( pszGeomCol );
266 :
267 0 : eGeomType = eType;
268 :
269 :
270 : /* -------------------------------------------------------------------- */
271 : /* Try to find out the spatial reference */
272 : /* -------------------------------------------------------------------- */
273 :
274 0 : nSRSId = nSRId;
275 :
276 0 : if (nSRSId < 0)
277 0 : nSRSId = FetchSRSId();
278 :
279 0 : GetSpatialRef();
280 :
281 0 : return CE_None;
282 : }
283 :
284 : /************************************************************************/
285 : /* FetchSRSId() */
286 : /************************************************************************/
287 :
288 0 : int OGRMSSQLSpatialTableLayer::FetchSRSId()
289 : {
290 0 : if ( poDS->UseGeometryColumns() )
291 : {
292 0 : CPLODBCStatement oStatement = CPLODBCStatement( poDS->GetSession() );
293 : oStatement.Appendf( "select srid from geometry_columns "
294 : "where f_table_schema = '%s' and f_table_name = '%s'",
295 0 : pszSchemaName, pszTableName );
296 :
297 0 : if( oStatement.ExecuteSQL() && oStatement.Fetch() )
298 : {
299 0 : if ( oStatement.GetColData( 0 ) )
300 0 : nSRSId = atoi( oStatement.GetColData( 0 ) );
301 0 : }
302 : }
303 :
304 0 : return nSRSId;
305 : }
306 :
307 : /************************************************************************/
308 : /* CreateSpatialIndex() */
309 : /* */
310 : /* Create a spatial index on the geometry column of the layer */
311 : /************************************************************************/
312 :
313 0 : OGRErr OGRMSSQLSpatialTableLayer::CreateSpatialIndex()
314 : {
315 0 : GetLayerDefn();
316 :
317 0 : CPLODBCStatement oStatement( poDS->GetSession() );
318 :
319 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY)
320 : {
321 0 : OGREnvelope oExt;
322 0 : if (GetExtent(&oExt, TRUE) != OGRERR_NONE)
323 : {
324 : CPLError( CE_Failure, CPLE_AppDefined,
325 0 : "Failed to get extent for spatial index." );
326 0 : return OGRERR_FAILURE;
327 : }
328 :
329 : oStatement.Appendf("CREATE SPATIAL INDEX [ogr_%s_sidx] ON [dbo].[%s] ( [%s] ) "
330 : "USING GEOMETRY_GRID WITH (BOUNDING_BOX =(%.15g, %.15g, %.15g, %.15g))",
331 : pszGeomColumn, poFeatureDefn->GetName(), pszGeomColumn,
332 0 : oExt.MinX, oExt.MinY, oExt.MaxX, oExt.MaxY );
333 : }
334 0 : else if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
335 : {
336 : oStatement.Appendf("CREATE SPATIAL INDEX [ogr_%s_sidx] ON [dbo].[%s] ( [%s] ) "
337 : "USING GEOGRAPHY_GRID",
338 0 : pszGeomColumn, poFeatureDefn->GetName(), pszGeomColumn );
339 : }
340 : else
341 : {
342 : CPLError( CE_Failure, CPLE_AppDefined,
343 0 : "Spatial index is not supported on the geometry column '%s'", pszGeomColumn);
344 0 : return OGRERR_FAILURE;
345 : }
346 :
347 : //poDS->GetSession()->BeginTransaction();
348 :
349 0 : if( !oStatement.ExecuteSQL() )
350 : {
351 : CPLError( CE_Failure, CPLE_AppDefined,
352 : "Failed to create the spatial index, %s.",
353 0 : poDS->GetSession()->GetLastError());
354 0 : return OGRERR_FAILURE;
355 : }
356 :
357 : //poDS->GetSession()->CommitTransaction();
358 :
359 0 : return OGRERR_NONE;
360 : }
361 :
362 : /************************************************************************/
363 : /* DropSpatialIndex() */
364 : /* */
365 : /* Drop the spatial index on the geometry column of the layer */
366 : /************************************************************************/
367 :
368 0 : void OGRMSSQLSpatialTableLayer::DropSpatialIndex()
369 : {
370 0 : GetLayerDefn();
371 :
372 0 : CPLODBCStatement oStatement( poDS->GetSession() );
373 :
374 : oStatement.Appendf("IF EXISTS (SELECT * FROM sys.indexes "
375 : "WHERE object_id = OBJECT_ID(N'[dbo].[%s]') AND name = N'ogr_%s_sidx') "
376 : "DROP INDEX [ogr_%s_sidx] ON [dbo].[%s]",
377 : poFeatureDefn->GetName(), pszGeomColumn,
378 0 : pszGeomColumn, poFeatureDefn->GetName() );
379 :
380 : //poDS->GetSession()->BeginTransaction();
381 :
382 0 : if( !oStatement.ExecuteSQL() )
383 : {
384 : CPLError( CE_Failure, CPLE_AppDefined,
385 : "Failed to drop the spatial index, %s.",
386 0 : poDS->GetSession()->GetLastError());
387 : return;
388 0 : }
389 :
390 : //poDS->GetSession()->CommitTransaction();
391 : }
392 :
393 : /************************************************************************/
394 : /* BuildFields() */
395 : /* */
396 : /* Build list of fields to fetch, performing any required */
397 : /* transformations (such as on geometry). */
398 : /************************************************************************/
399 :
400 0 : CPLString OGRMSSQLSpatialTableLayer::BuildFields()
401 :
402 : {
403 0 : int i = 0;
404 0 : int nColumn = 0;
405 0 : CPLString osFieldList;
406 :
407 0 : if( pszFIDColumn && poFeatureDefn->GetFieldIndex( pszFIDColumn ) == -1 )
408 : {
409 : /* Always get the FID column */
410 0 : osFieldList += "[";
411 0 : osFieldList += pszFIDColumn;
412 0 : osFieldList += "]";
413 0 : ++nColumn;
414 : }
415 :
416 0 : if( pszGeomColumn )
417 : {
418 0 : if( nColumn > 0 )
419 0 : osFieldList += ", ";
420 :
421 0 : osFieldList += "[";
422 0 : osFieldList += pszGeomColumn;
423 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY ||
424 : nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
425 : {
426 0 : if ( poDS->GetGeometryFormat() == MSSQLGEOMETRY_WKB )
427 : {
428 0 : osFieldList += "].STAsBinary() as [";
429 0 : osFieldList += pszGeomColumn;
430 : }
431 0 : else if ( poDS->GetGeometryFormat() == MSSQLGEOMETRY_WKT )
432 : {
433 0 : osFieldList += "].AsTextZM() as [";
434 0 : osFieldList += pszGeomColumn;
435 : }
436 0 : else if ( poDS->GetGeometryFormat() == MSSQLGEOMETRY_WKBZM )
437 : {
438 : /* SQL Server 2012 */
439 0 : osFieldList += "].AsBinaryZM() as [";
440 0 : osFieldList += pszGeomColumn;
441 : }
442 : }
443 0 : osFieldList += "]";
444 :
445 0 : ++nColumn;
446 : }
447 :
448 0 : if (poFeatureDefn->GetFieldCount() > 0)
449 : {
450 : /* need to reconstruct the field ordinals list */
451 0 : CPLFree(panFieldOrdinals);
452 0 : panFieldOrdinals = (int *) CPLMalloc( sizeof(int) * poFeatureDefn->GetFieldCount() );
453 :
454 0 : for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
455 : {
456 0 : const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
457 :
458 0 : if( nColumn > 0 )
459 0 : osFieldList += ", ";
460 :
461 0 : osFieldList += "[";
462 0 : osFieldList += pszName;
463 0 : osFieldList += "]";
464 :
465 0 : panFieldOrdinals[i] = nColumn;
466 :
467 0 : ++nColumn;
468 : }
469 : }
470 :
471 0 : return osFieldList;
472 : }
473 :
474 : /************************************************************************/
475 : /* ClearStatement() */
476 : /************************************************************************/
477 :
478 0 : void OGRMSSQLSpatialTableLayer::ClearStatement()
479 :
480 : {
481 0 : if( poStmt != NULL )
482 : {
483 0 : delete poStmt;
484 0 : poStmt = NULL;
485 : }
486 0 : }
487 :
488 : /************************************************************************/
489 : /* GetStatement() */
490 : /************************************************************************/
491 :
492 0 : CPLODBCStatement *OGRMSSQLSpatialTableLayer::GetStatement()
493 :
494 : {
495 0 : if( poStmt == NULL )
496 : {
497 0 : poStmt = BuildStatement(BuildFields());
498 0 : iNextShapeId = 0;
499 : }
500 :
501 0 : return poStmt;
502 : }
503 :
504 :
505 : /************************************************************************/
506 : /* BuildStatement() */
507 : /************************************************************************/
508 :
509 0 : CPLODBCStatement* OGRMSSQLSpatialTableLayer::BuildStatement(const char* pszColumns)
510 :
511 : {
512 0 : CPLODBCStatement* poStatement = new CPLODBCStatement( poDS->GetSession() );
513 0 : poStatement->Append( "select " );
514 0 : poStatement->Append( pszColumns );
515 0 : poStatement->Append( " from " );
516 0 : poStatement->Append( pszSchemaName );
517 0 : poStatement->Append( "." );
518 0 : poStatement->Append( pszTableName );
519 :
520 : /* Append attribute query if we have it */
521 0 : if( pszQuery != NULL )
522 0 : poStatement->Appendf( " where (%s)", pszQuery );
523 :
524 : /* If we have a spatial filter, query on it */
525 0 : if ( m_poFilterGeom != NULL )
526 : {
527 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY
528 : || nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
529 : {
530 0 : if( pszQuery == NULL )
531 0 : poStatement->Append( " where" );
532 : else
533 0 : poStatement->Append( " and" );
534 :
535 0 : poStatement->Appendf(" [%s].STIntersects(", pszGeomColumn );
536 :
537 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
538 0 : poStatement->Append( "geography::" );
539 : else
540 0 : poStatement->Append( "geometry::" );
541 :
542 0 : if ( m_sFilterEnvelope.MinX == m_sFilterEnvelope.MaxX ||
543 : m_sFilterEnvelope.MinY == m_sFilterEnvelope.MaxY)
544 : poStatement->Appendf("STGeomFromText('POINT(%.15g %.15g)',%d)) = 1",
545 0 : m_sFilterEnvelope.MinX, m_sFilterEnvelope.MinY, nSRSId >= 0? nSRSId : 0);
546 : else
547 : poStatement->Appendf( "STGeomFromText('POLYGON((%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g))',%d)) = 1",
548 : m_sFilterEnvelope.MinX, m_sFilterEnvelope.MinY,
549 : m_sFilterEnvelope.MaxX, m_sFilterEnvelope.MinY,
550 : m_sFilterEnvelope.MaxX, m_sFilterEnvelope.MaxY,
551 : m_sFilterEnvelope.MinX, m_sFilterEnvelope.MaxY,
552 : m_sFilterEnvelope.MinX, m_sFilterEnvelope.MinY,
553 0 : nSRSId >= 0? nSRSId : 0 );
554 : }
555 : else
556 : {
557 : CPLError( CE_Failure, CPLE_AppDefined,
558 0 : "Spatial filter is supported only on geometry and geography column types." );
559 :
560 0 : delete poStatement;
561 0 : return NULL;
562 : }
563 : }
564 :
565 0 : CPLDebug( "OGR_MSSQLSpatial", "ExecuteSQL(%s)", poStatement->GetCommand() );
566 0 : if( poStatement->ExecuteSQL() )
567 0 : return poStatement;
568 : else
569 : {
570 0 : delete poStatement;
571 0 : return NULL;
572 : }
573 : }
574 :
575 : /************************************************************************/
576 : /* ResetReading() */
577 : /************************************************************************/
578 :
579 0 : void OGRMSSQLSpatialTableLayer::ResetReading()
580 :
581 : {
582 0 : ClearStatement();
583 0 : OGRMSSQLSpatialLayer::ResetReading();
584 0 : }
585 :
586 : /************************************************************************/
587 : /* GetFeature() */
588 : /************************************************************************/
589 :
590 0 : OGRFeature *OGRMSSQLSpatialTableLayer::GetFeature( long nFeatureId )
591 :
592 : {
593 0 : if( pszFIDColumn == NULL )
594 0 : return OGRMSSQLSpatialLayer::GetFeature( nFeatureId );
595 :
596 0 : ClearStatement();
597 :
598 0 : iNextShapeId = nFeatureId;
599 :
600 0 : poStmt = new CPLODBCStatement( poDS->GetSession() );
601 0 : CPLString osFields = BuildFields();
602 : poStmt->Appendf( "select %s from %s where %s = %ld", osFields.c_str(),
603 0 : poFeatureDefn->GetName(), pszFIDColumn, nFeatureId );
604 :
605 0 : if( !poStmt->ExecuteSQL() )
606 : {
607 0 : delete poStmt;
608 0 : poStmt = NULL;
609 0 : return NULL;
610 : }
611 :
612 0 : return GetNextRawFeature();
613 : }
614 :
615 : /************************************************************************/
616 : /* SetAttributeFilter() */
617 : /************************************************************************/
618 :
619 0 : OGRErr OGRMSSQLSpatialTableLayer::SetAttributeFilter( const char *pszQuery )
620 :
621 : {
622 0 : if( (pszQuery == NULL && this->pszQuery == NULL)
623 : || (pszQuery != NULL && this->pszQuery != NULL
624 : && EQUAL(pszQuery,this->pszQuery)) )
625 0 : return OGRERR_NONE;
626 :
627 0 : CPLFree( this->pszQuery );
628 0 : this->pszQuery = (pszQuery) ? CPLStrdup( pszQuery ) : NULL;
629 :
630 0 : ClearStatement();
631 :
632 0 : return OGRERR_NONE;
633 : }
634 :
635 :
636 : /************************************************************************/
637 : /* TestCapability() */
638 : /************************************************************************/
639 :
640 0 : int OGRMSSQLSpatialTableLayer::TestCapability( const char * pszCap )
641 :
642 : {
643 0 : if ( bUpdateAccess )
644 : {
645 0 : if( EQUAL(pszCap,OLCSequentialWrite) || EQUAL(pszCap,OLCCreateField)
646 : || EQUAL(pszCap,OLCDeleteFeature) )
647 0 : return TRUE;
648 :
649 0 : else if( EQUAL(pszCap,OLCRandomWrite) )
650 0 : return (pszFIDColumn != NULL);
651 : }
652 :
653 : #if (ODBCVER >= 0x0300)
654 0 : if( EQUAL(pszCap,OLCTransactions) )
655 0 : return TRUE;
656 : #else
657 : if( EQUAL(pszCap,OLCTransactions) )
658 : return FALSE;
659 : #endif
660 :
661 0 : if( EQUAL(pszCap,OLCRandomRead) )
662 0 : return (pszFIDColumn != NULL);
663 0 : else if( EQUAL(pszCap,OLCFastFeatureCount) )
664 0 : return TRUE;
665 : else
666 0 : return OGRMSSQLSpatialLayer::TestCapability( pszCap );
667 : }
668 :
669 : /************************************************************************/
670 : /* GetFeatureCount() */
671 : /************************************************************************/
672 :
673 0 : int OGRMSSQLSpatialTableLayer::GetFeatureCount( int bForce )
674 :
675 : {
676 0 : GetLayerDefn();
677 :
678 0 : if( TestCapability(OLCFastFeatureCount) == FALSE )
679 0 : return OGRMSSQLSpatialLayer::GetFeatureCount( bForce );
680 :
681 0 : ClearStatement();
682 :
683 0 : CPLODBCStatement* poStatement = BuildStatement( "count(*)" );
684 :
685 0 : if (poStatement == NULL || !poStatement->Fetch())
686 : {
687 0 : delete poStatement;
688 0 : return OGRMSSQLSpatialLayer::GetFeatureCount( bForce );
689 : }
690 :
691 0 : int nRet = atoi(poStatement->GetColData( 0 ));
692 0 : delete poStatement;
693 0 : return nRet;
694 : }
695 :
696 :
697 : /************************************************************************/
698 : /* CreateField() */
699 : /************************************************************************/
700 :
701 0 : OGRErr OGRMSSQLSpatialTableLayer::CreateField( OGRFieldDefn *poFieldIn,
702 : int bApproxOK )
703 :
704 : {
705 : char szFieldType[256];
706 0 : OGRFieldDefn oField( poFieldIn );
707 :
708 0 : GetLayerDefn();
709 :
710 : /* -------------------------------------------------------------------- */
711 : /* Do we want to "launder" the column names into MSSQL */
712 : /* friendly format? */
713 : /* -------------------------------------------------------------------- */
714 0 : if( bLaunderColumnNames )
715 : {
716 0 : char *pszSafeName = poDS->LaunderName( oField.GetNameRef() );
717 :
718 0 : oField.SetName( pszSafeName );
719 0 : CPLFree( pszSafeName );
720 : }
721 :
722 : /* -------------------------------------------------------------------- */
723 : /* Identify the MSSQL type. */
724 : /* -------------------------------------------------------------------- */
725 :
726 0 : if( oField.GetType() == OFTInteger )
727 : {
728 0 : if( oField.GetWidth() > 0 && bPreservePrecision )
729 0 : sprintf( szFieldType, "numeric(%d,0)", oField.GetWidth() );
730 : else
731 0 : strcpy( szFieldType, "int" );
732 : }
733 0 : else if( oField.GetType() == OFTReal )
734 : {
735 0 : if( oField.GetWidth() > 0 && oField.GetPrecision() > 0
736 : && bPreservePrecision )
737 : sprintf( szFieldType, "numeric(%d,%d)",
738 0 : oField.GetWidth(), oField.GetPrecision() );
739 : else
740 0 : strcpy( szFieldType, "float" );
741 : }
742 0 : else if( oField.GetType() == OFTString )
743 : {
744 0 : if( oField.GetWidth() == 0 || !bPreservePrecision )
745 0 : strcpy( szFieldType, "varchar(MAX)" );
746 : else
747 0 : sprintf( szFieldType, "varchar(%d)", oField.GetWidth() );
748 : }
749 0 : else if( oField.GetType() == OFTDate )
750 : {
751 0 : strcpy( szFieldType, "date" );
752 : }
753 0 : else if( oField.GetType() == OFTTime )
754 : {
755 0 : strcpy( szFieldType, "time(7)" );
756 : }
757 0 : else if( oField.GetType() == OFTDateTime )
758 : {
759 0 : strcpy( szFieldType, "datetime" );
760 : }
761 0 : else if( oField.GetType() == OFTBinary )
762 : {
763 0 : strcpy( szFieldType, "image" );
764 : }
765 0 : else if( bApproxOK )
766 : {
767 : CPLError( CE_Warning, CPLE_NotSupported,
768 : "Can't create field %s with type %s on MSSQL layers. Creating as varchar.",
769 : oField.GetNameRef(),
770 0 : OGRFieldDefn::GetFieldTypeName(oField.GetType()) );
771 0 : strcpy( szFieldType, "varchar" );
772 : }
773 : else
774 : {
775 : CPLError( CE_Failure, CPLE_NotSupported,
776 : "Can't create field %s with type %s on MSSQL layers.",
777 : oField.GetNameRef(),
778 0 : OGRFieldDefn::GetFieldTypeName(oField.GetType()) );
779 :
780 0 : return OGRERR_FAILURE;
781 : }
782 :
783 : /* -------------------------------------------------------------------- */
784 : /* Create the new field. */
785 : /* -------------------------------------------------------------------- */
786 :
787 0 : CPLODBCStatement oStmt( poDS->GetSession() );
788 :
789 : oStmt.Appendf( "ALTER TABLE [%s].[%s] ADD [%s] %s",
790 0 : pszSchemaName, pszTableName, oField.GetNameRef(), szFieldType);
791 :
792 0 : if( !oStmt.ExecuteSQL() )
793 : {
794 : CPLError( CE_Failure, CPLE_AppDefined,
795 : "Error creating field %s, %s", oField.GetNameRef(),
796 0 : poDS->GetSession()->GetLastError() );
797 :
798 0 : return OGRERR_FAILURE;
799 : }
800 :
801 : /* -------------------------------------------------------------------- */
802 : /* Add the field to the OGRFeatureDefn. */
803 : /* -------------------------------------------------------------------- */
804 :
805 0 : poFeatureDefn->AddFieldDefn( &oField );
806 :
807 0 : return OGRERR_NONE;
808 : }
809 :
810 : /************************************************************************/
811 : /* SetFeature() */
812 : /* */
813 : /* SetFeature() is implemented by an UPDATE SQL command */
814 : /************************************************************************/
815 :
816 0 : OGRErr OGRMSSQLSpatialTableLayer::SetFeature( OGRFeature *poFeature )
817 :
818 : {
819 0 : OGRErr eErr = OGRERR_FAILURE;
820 :
821 0 : GetLayerDefn();
822 :
823 0 : if( NULL == poFeature )
824 : {
825 : CPLError( CE_Failure, CPLE_AppDefined,
826 0 : "NULL pointer to OGRFeature passed to SetFeature()." );
827 0 : return eErr;
828 : }
829 :
830 0 : if( poFeature->GetFID() == OGRNullFID )
831 : {
832 : CPLError( CE_Failure, CPLE_AppDefined,
833 0 : "FID required on features given to SetFeature()." );
834 0 : return eErr;
835 : }
836 :
837 0 : if( !pszFIDColumn )
838 : {
839 : CPLError( CE_Failure, CPLE_AppDefined,
840 : "Unable to update features in tables without\n"
841 0 : "a recognised FID column.");
842 0 : return eErr;
843 :
844 : }
845 :
846 0 : ClearStatement();
847 :
848 : /* -------------------------------------------------------------------- */
849 : /* Form the UPDATE command. */
850 : /* -------------------------------------------------------------------- */
851 0 : CPLODBCStatement oStmt( poDS->GetSession() );
852 :
853 0 : oStmt.Appendf( "UPDATE [%s].[%s] SET ", pszSchemaName, pszTableName);
854 :
855 0 : OGRMSSQLGeometryValidator oValidator(poFeature->GetGeometryRef());
856 0 : OGRGeometry *poGeom = oValidator.GetValidGeometryRef();
857 :
858 0 : if (poFeature->GetGeometryRef() != poGeom)
859 : {
860 : CPLError( CE_Warning, CPLE_NotSupported,
861 0 : "Geometry with FID = %ld has been modified.", poFeature->GetFID() );
862 : }
863 :
864 0 : int bNeedComma = FALSE;
865 0 : if(pszGeomColumn != NULL)
866 : {
867 0 : char *pszWKT = NULL;
868 :
869 0 : if (poGeom != NULL)
870 0 : poGeom->exportToWkt( &pszWKT );
871 :
872 0 : oStmt.Appendf( "[%s] = ", pszGeomColumn );
873 :
874 0 : if( pszWKT != NULL && (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY
875 : || nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY))
876 : {
877 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
878 : {
879 0 : oStmt.Append( "geography::STGeomFromText(" );
880 0 : OGRMSSQLAppendEscaped(&oStmt, pszWKT);
881 0 : oStmt.Appendf(",%d)", nSRSId );
882 : }
883 : else
884 : {
885 0 : oStmt.Append( "geometry::STGeomFromText(" );
886 0 : OGRMSSQLAppendEscaped(&oStmt, pszWKT);
887 0 : oStmt.Appendf(",%d).MakeValid()", nSRSId );
888 : }
889 : }
890 : else
891 0 : oStmt.Append( "null" );
892 :
893 0 : bNeedComma = TRUE;
894 0 : CPLFree(pszWKT);
895 : }
896 :
897 0 : int nFieldCount = poFeatureDefn->GetFieldCount();
898 : int i;
899 0 : for( i = 0; i < nFieldCount; i++ )
900 : {
901 0 : if (bNeedComma)
902 0 : oStmt.Appendf( ", [%s] = ", poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
903 : else
904 : {
905 0 : oStmt.Appendf( "[%s] = ", poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
906 0 : bNeedComma = TRUE;
907 : }
908 :
909 0 : if( !poFeature->IsFieldSet( i ) )
910 0 : oStmt.Append( "null" );
911 : else
912 0 : AppendFieldValue(&oStmt, poFeature, i);
913 : }
914 :
915 : /* Add the WHERE clause */
916 0 : oStmt.Appendf( " WHERE [%s] = %ld" , pszFIDColumn, poFeature->GetFID());
917 :
918 : /* -------------------------------------------------------------------- */
919 : /* Execute the update. */
920 : /* -------------------------------------------------------------------- */
921 :
922 0 : if( !oStmt.ExecuteSQL() )
923 : {
924 : CPLError( CE_Failure, CPLE_AppDefined,
925 : "Error updating feature with FID:%ld, %s", poFeature->GetFID(),
926 0 : poDS->GetSession()->GetLastError() );
927 :
928 0 : return OGRERR_FAILURE;
929 : }
930 :
931 0 : return OGRERR_NONE;
932 : }
933 :
934 : /************************************************************************/
935 : /* DeleteFeature() */
936 : /************************************************************************/
937 :
938 0 : OGRErr OGRMSSQLSpatialTableLayer::DeleteFeature( long nFID )
939 :
940 : {
941 0 : GetLayerDefn();
942 :
943 0 : if( pszFIDColumn == NULL )
944 : {
945 : CPLError( CE_Failure, CPLE_AppDefined,
946 0 : "DeleteFeature() without any FID column." );
947 0 : return OGRERR_FAILURE;
948 : }
949 :
950 0 : if( nFID == OGRNullFID )
951 : {
952 : CPLError( CE_Failure, CPLE_AppDefined,
953 0 : "DeleteFeature() with unset FID fails." );
954 0 : return OGRERR_FAILURE;
955 : }
956 :
957 0 : ClearStatement();
958 :
959 : /* -------------------------------------------------------------------- */
960 : /* Drop the record with this FID. */
961 : /* -------------------------------------------------------------------- */
962 0 : CPLODBCStatement oStatement( poDS->GetSession() );
963 :
964 : oStatement.Appendf("DELETE FROM [%s] WHERE [%s] = %ld",
965 0 : poFeatureDefn->GetName(), pszFIDColumn, nFID);
966 :
967 0 : if( !oStatement.ExecuteSQL() )
968 : {
969 : CPLError( CE_Failure, CPLE_AppDefined,
970 : "Attempt to delete feature with FID %ld failed. %s",
971 0 : nFID, poDS->GetSession()->GetLastError() );
972 :
973 0 : return OGRERR_FAILURE;
974 : }
975 :
976 0 : return OGRERR_NONE;
977 : }
978 :
979 : /************************************************************************/
980 : /* CreateFeature() */
981 : /************************************************************************/
982 :
983 0 : OGRErr OGRMSSQLSpatialTableLayer::CreateFeature( OGRFeature *poFeature )
984 :
985 : {
986 0 : GetLayerDefn();
987 :
988 0 : if( NULL == poFeature )
989 : {
990 : CPLError( CE_Failure, CPLE_AppDefined,
991 0 : "NULL pointer to OGRFeature passed to CreateFeature()." );
992 0 : return OGRERR_FAILURE;
993 : }
994 :
995 0 : ClearStatement();
996 :
997 0 : CPLODBCStatement oStatement( poDS->GetSession() );
998 :
999 : /* the fid values are retieved from the source layer */
1000 0 : if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL )
1001 0 : oStatement.Appendf("SET IDENTITY_INSERT [%s].[%s] ON;", pszSchemaName, pszTableName );
1002 :
1003 : /* -------------------------------------------------------------------- */
1004 : /* Form the INSERT command. */
1005 : /* -------------------------------------------------------------------- */
1006 :
1007 0 : oStatement.Appendf( "INSERT INTO [%s].[%s] (", pszSchemaName, pszTableName );
1008 :
1009 0 : OGRMSSQLGeometryValidator oValidator(poFeature->GetGeometryRef());
1010 0 : OGRGeometry *poGeom = oValidator.GetValidGeometryRef();
1011 :
1012 0 : if (poFeature->GetGeometryRef() != poGeom)
1013 : {
1014 : CPLError( CE_Warning, CPLE_NotSupported,
1015 0 : "Geometry with FID = %ld has been modified.", poFeature->GetFID() );
1016 : }
1017 :
1018 0 : int bNeedComma = FALSE;
1019 :
1020 0 : if (poGeom != NULL && pszGeomColumn != NULL)
1021 : {
1022 0 : oStatement.Append( pszGeomColumn );
1023 0 : bNeedComma = TRUE;
1024 : }
1025 :
1026 0 : if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL )
1027 : {
1028 0 : if (bNeedComma)
1029 0 : oStatement.Appendf( ", [%s]", pszFIDColumn );
1030 : else
1031 : {
1032 0 : oStatement.Appendf( "[%s]", pszFIDColumn );
1033 0 : bNeedComma = TRUE;
1034 : }
1035 : }
1036 :
1037 0 : int nFieldCount = poFeatureDefn->GetFieldCount();
1038 : int i;
1039 0 : for( i = 0; i < nFieldCount; i++ )
1040 : {
1041 0 : if( !poFeature->IsFieldSet( i ) )
1042 0 : continue;
1043 :
1044 0 : if (bNeedComma)
1045 0 : oStatement.Appendf( ", [%s]", poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
1046 : else
1047 : {
1048 0 : oStatement.Appendf( "[%s]", poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
1049 0 : bNeedComma = TRUE;
1050 : }
1051 : }
1052 :
1053 0 : oStatement.Appendf( ") VALUES (" );
1054 :
1055 : /* Set the geometry */
1056 0 : bNeedComma = FALSE;
1057 0 : if(poGeom != NULL && pszGeomColumn != NULL)
1058 : {
1059 0 : char *pszWKT = NULL;
1060 :
1061 : //poGeom->setCoordinateDimension( nCoordDimension );
1062 :
1063 0 : poGeom->exportToWkt( &pszWKT );
1064 :
1065 0 : if( pszWKT != NULL && (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY
1066 : || nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY))
1067 : {
1068 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
1069 : {
1070 0 : oStatement.Append( "geography::STGeomFromText(" );
1071 0 : OGRMSSQLAppendEscaped(&oStatement, pszWKT);
1072 0 : oStatement.Appendf(",%d)", nSRSId );
1073 : }
1074 : else
1075 : {
1076 0 : oStatement.Append( "geometry::STGeomFromText(" );
1077 0 : OGRMSSQLAppendEscaped(&oStatement, pszWKT);
1078 0 : oStatement.Appendf(",%d).MakeValid()", nSRSId );
1079 : }
1080 : }
1081 : else
1082 0 : oStatement.Append( "null" );
1083 :
1084 0 : bNeedComma = TRUE;
1085 0 : CPLFree(pszWKT);
1086 : }
1087 :
1088 : /* Set the FID */
1089 0 : if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL )
1090 : {
1091 0 : if (bNeedComma)
1092 0 : oStatement.Appendf( ", %ld", poFeature->GetFID() );
1093 : else
1094 : {
1095 0 : oStatement.Appendf( "%ld", poFeature->GetFID() );
1096 0 : bNeedComma = TRUE;
1097 : }
1098 : }
1099 :
1100 0 : for( i = 0; i < nFieldCount; i++ )
1101 : {
1102 0 : if( !poFeature->IsFieldSet( i ) )
1103 0 : continue;
1104 :
1105 0 : if (bNeedComma)
1106 0 : oStatement.Append( ", " );
1107 : else
1108 0 : bNeedComma = TRUE;
1109 :
1110 0 : AppendFieldValue(&oStatement, poFeature, i);
1111 : }
1112 :
1113 0 : oStatement.Append( ");" );
1114 :
1115 0 : if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL )
1116 0 : oStatement.Appendf("SET IDENTITY_INSERT [%s].[%s] OFF;", pszSchemaName, pszTableName );
1117 :
1118 : /* -------------------------------------------------------------------- */
1119 : /* Execute the insert. */
1120 : /* -------------------------------------------------------------------- */
1121 :
1122 0 : if( !oStatement.ExecuteSQL() )
1123 : {
1124 : CPLError( CE_Failure, CPLE_AppDefined,
1125 : "INSERT command for new feature failed. %s",
1126 0 : poDS->GetSession()->GetLastError() );
1127 :
1128 0 : return OGRERR_FAILURE;
1129 : }
1130 :
1131 0 : return OGRERR_NONE;
1132 : }
1133 :
1134 : /************************************************************************/
1135 : /* AppendFieldValue() */
1136 : /* */
1137 : /* Used by CreateFeature() and SetFeature() to format a */
1138 : /* non-empty field value */
1139 : /************************************************************************/
1140 :
1141 0 : void OGRMSSQLSpatialTableLayer::AppendFieldValue(CPLODBCStatement *poStatement,
1142 : OGRFeature* poFeature, int i)
1143 : {
1144 0 : int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
1145 :
1146 : // We need special formatting for integer list values.
1147 0 : if( nOGRFieldType == OFTIntegerList )
1148 : {
1149 : //TODO
1150 0 : poStatement->Append( "null" );
1151 0 : return;
1152 : }
1153 :
1154 : // We need special formatting for real list values.
1155 0 : else if( nOGRFieldType == OFTRealList )
1156 : {
1157 : //TODO
1158 0 : poStatement->Append( "null" );
1159 0 : return;
1160 : }
1161 :
1162 : // We need special formatting for string list values.
1163 0 : else if( nOGRFieldType == OFTStringList )
1164 : {
1165 : //TODO
1166 0 : poStatement->Append( "null" );
1167 0 : return;
1168 : }
1169 :
1170 : // Binary formatting
1171 0 : if( nOGRFieldType == OFTBinary )
1172 : {
1173 0 : int nLen = 0;
1174 0 : GByte* pabyData = poFeature->GetFieldAsBinary( i, &nLen );
1175 0 : char* pszBytes = GByteArrayToHexString( pabyData, nLen);
1176 0 : poStatement->Append( pszBytes );
1177 0 : CPLFree(pszBytes);
1178 0 : return;
1179 : }
1180 :
1181 : // Flag indicating NULL or not-a-date date value
1182 : // e.g. 0000-00-00 - there is no year 0
1183 0 : OGRBoolean bIsDateNull = FALSE;
1184 :
1185 0 : const char *pszStrValue = poFeature->GetFieldAsString(i);
1186 :
1187 : // Check if date is NULL: 0000-00-00
1188 0 : if( nOGRFieldType == OFTDate )
1189 : {
1190 0 : if( EQUALN( pszStrValue, "0000", 4 ) )
1191 : {
1192 0 : pszStrValue = "null";
1193 0 : bIsDateNull = TRUE;
1194 : }
1195 : }
1196 0 : else if ( nOGRFieldType == OFTReal )
1197 : {
1198 0 : char* pszComma = strchr((char*)pszStrValue, ',');
1199 0 : if (pszComma)
1200 0 : *pszComma = '.';
1201 : }
1202 :
1203 0 : if( nOGRFieldType != OFTInteger && nOGRFieldType != OFTReal
1204 : && !bIsDateNull )
1205 : {
1206 0 : OGRMSSQLAppendEscaped(poStatement, pszStrValue);
1207 : }
1208 : else
1209 : {
1210 0 : poStatement->Append( pszStrValue );
1211 : }
1212 : }
1213 :
|