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