1 : /******************************************************************************
2 : * $Id: ogrmssqlspatialtablelayer.cpp 21943 2011-03-12 18:25:22Z 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 21943 2011-03-12 18:25:22Z 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 += "].STAsText() as [";
400 0 : osFieldList += pszGeomColumn;
401 : }
402 : }
403 0 : osFieldList += "]";
404 :
405 0 : ++nColumn;
406 : }
407 :
408 0 : if (poFeatureDefn->GetFieldCount() > 0)
409 : {
410 : /* need to reconstruct the field ordinals list */
411 0 : CPLFree(panFieldOrdinals);
412 0 : panFieldOrdinals = (int *) CPLMalloc( sizeof(int) * poFeatureDefn->GetFieldCount() );
413 :
414 0 : for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
415 : {
416 0 : const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
417 :
418 0 : if( nColumn > 0 )
419 0 : osFieldList += ", ";
420 :
421 0 : osFieldList += "[";
422 0 : osFieldList += pszName;
423 0 : osFieldList += "]";
424 :
425 0 : panFieldOrdinals[i] = nColumn;
426 :
427 0 : ++nColumn;
428 : }
429 : }
430 :
431 0 : return osFieldList;
432 : }
433 :
434 : /************************************************************************/
435 : /* ClearStatement() */
436 : /************************************************************************/
437 :
438 0 : void OGRMSSQLSpatialTableLayer::ClearStatement()
439 :
440 : {
441 0 : if( poStmt != NULL )
442 : {
443 0 : delete poStmt;
444 0 : poStmt = NULL;
445 : }
446 0 : }
447 :
448 : /************************************************************************/
449 : /* GetStatement() */
450 : /************************************************************************/
451 :
452 0 : CPLODBCStatement *OGRMSSQLSpatialTableLayer::GetStatement()
453 :
454 : {
455 0 : if( poStmt == NULL )
456 : {
457 0 : poStmt = BuildStatement(BuildFields());
458 0 : iNextShapeId = 0;
459 : }
460 :
461 0 : return poStmt;
462 : }
463 :
464 :
465 : /************************************************************************/
466 : /* BuildStatement() */
467 : /************************************************************************/
468 :
469 0 : CPLODBCStatement* OGRMSSQLSpatialTableLayer::BuildStatement(const char* pszColumns)
470 :
471 : {
472 0 : CPLODBCStatement* poStatement = new CPLODBCStatement( poDS->GetSession() );
473 0 : poStatement->Append( "select " );
474 0 : poStatement->Append( pszColumns );
475 0 : poStatement->Append( " from " );
476 0 : poStatement->Append( pszSchemaName );
477 0 : poStatement->Append( "." );
478 0 : poStatement->Append( pszTableName );
479 :
480 : /* Append attribute query if we have it */
481 0 : if( pszQuery != NULL )
482 0 : poStatement->Appendf( " where %s", pszQuery );
483 :
484 : /* If we have a spatial filter, query on it */
485 0 : if ( m_poFilterGeom != NULL )
486 : {
487 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY
488 : || nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
489 : {
490 0 : if( pszQuery == NULL )
491 0 : poStatement->Append( " where" );
492 : else
493 0 : poStatement->Append( " and" );
494 :
495 0 : poStatement->Appendf(" [%s].STIntersects(", pszGeomColumn );
496 :
497 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
498 0 : poStatement->Append( "geography::" );
499 : else
500 0 : poStatement->Append( "geometry::" );
501 :
502 0 : if ( m_sFilterEnvelope.MinX == m_sFilterEnvelope.MaxX ||
503 : m_sFilterEnvelope.MinY == m_sFilterEnvelope.MaxY)
504 : poStatement->Appendf("STGeomFromText('POINT(%.15g %.15g)',%d)) = 1",
505 0 : m_sFilterEnvelope.MinX, m_sFilterEnvelope.MinY, nSRSId >= 0? nSRSId : 0);
506 : else
507 : poStatement->Appendf( "STGeomFromText('POLYGON((%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g))',%d)) = 1",
508 : m_sFilterEnvelope.MinX, m_sFilterEnvelope.MinY,
509 : m_sFilterEnvelope.MaxX, m_sFilterEnvelope.MinY,
510 : m_sFilterEnvelope.MaxX, m_sFilterEnvelope.MaxY,
511 : m_sFilterEnvelope.MinX, m_sFilterEnvelope.MaxY,
512 : m_sFilterEnvelope.MinX, m_sFilterEnvelope.MinY,
513 0 : nSRSId >= 0? nSRSId : 0 );
514 : }
515 : else
516 : {
517 : CPLError( CE_Failure, CPLE_AppDefined,
518 0 : "Spatial filter is supported only on geometry and geography column types." );
519 :
520 0 : delete poStatement;
521 0 : return NULL;
522 : }
523 : }
524 :
525 0 : CPLDebug( "OGR_MSSQLSpatial", "ExecuteSQL(%s)", poStatement->GetCommand() );
526 0 : if( poStatement->ExecuteSQL() )
527 0 : return poStatement;
528 : else
529 : {
530 0 : delete poStatement;
531 0 : return NULL;
532 : }
533 : }
534 :
535 : /************************************************************************/
536 : /* ResetReading() */
537 : /************************************************************************/
538 :
539 0 : void OGRMSSQLSpatialTableLayer::ResetReading()
540 :
541 : {
542 0 : ClearStatement();
543 0 : OGRMSSQLSpatialLayer::ResetReading();
544 0 : }
545 :
546 : /************************************************************************/
547 : /* GetFeature() */
548 : /************************************************************************/
549 :
550 0 : OGRFeature *OGRMSSQLSpatialTableLayer::GetFeature( long nFeatureId )
551 :
552 : {
553 0 : if( pszFIDColumn == NULL )
554 0 : return OGRMSSQLSpatialLayer::GetFeature( nFeatureId );
555 :
556 0 : ClearStatement();
557 :
558 0 : iNextShapeId = nFeatureId;
559 :
560 0 : poStmt = new CPLODBCStatement( poDS->GetSession() );
561 0 : CPLString osFields = BuildFields();
562 : poStmt->Appendf( "select %s from %s where %s = %ld", osFields.c_str(),
563 0 : poFeatureDefn->GetName(), pszFIDColumn, nFeatureId );
564 :
565 0 : if( !poStmt->ExecuteSQL() )
566 : {
567 0 : delete poStmt;
568 0 : poStmt = NULL;
569 0 : return NULL;
570 : }
571 :
572 0 : return GetNextRawFeature();
573 : }
574 :
575 : /************************************************************************/
576 : /* SetAttributeFilter() */
577 : /************************************************************************/
578 :
579 0 : OGRErr OGRMSSQLSpatialTableLayer::SetAttributeFilter( const char *pszQuery )
580 :
581 : {
582 0 : if( (pszQuery == NULL && this->pszQuery == NULL)
583 : || (pszQuery != NULL && this->pszQuery != NULL
584 : && EQUAL(pszQuery,this->pszQuery)) )
585 0 : return OGRERR_NONE;
586 :
587 0 : CPLFree( this->pszQuery );
588 0 : this->pszQuery = (pszQuery) ? CPLStrdup( pszQuery ) : NULL;
589 :
590 0 : ClearStatement();
591 :
592 0 : return OGRERR_NONE;
593 : }
594 :
595 :
596 : /************************************************************************/
597 : /* TestCapability() */
598 : /************************************************************************/
599 :
600 0 : int OGRMSSQLSpatialTableLayer::TestCapability( const char * pszCap )
601 :
602 : {
603 0 : if ( bUpdateAccess )
604 : {
605 0 : if( EQUAL(pszCap,OLCSequentialWrite) || EQUAL(pszCap,OLCCreateField) )
606 0 : return TRUE;
607 :
608 0 : else if( EQUAL(pszCap,OLCRandomWrite) )
609 0 : return (pszFIDColumn != NULL);
610 : }
611 :
612 : #if (ODBCVER >= 0x0300)
613 0 : if( EQUAL(pszCap,OLCTransactions) )
614 0 : return TRUE;
615 : #else
616 : if( EQUAL(pszCap,OLCTransactions) )
617 : return FALSE;
618 : #endif
619 :
620 0 : if( EQUAL(pszCap,OLCRandomRead) )
621 0 : return (pszFIDColumn != NULL);
622 0 : else if( EQUAL(pszCap,OLCFastFeatureCount) )
623 0 : return TRUE;
624 : else
625 0 : return OGRMSSQLSpatialLayer::TestCapability( pszCap );
626 : }
627 :
628 : /************************************************************************/
629 : /* GetFeatureCount() */
630 : /************************************************************************/
631 :
632 0 : int OGRMSSQLSpatialTableLayer::GetFeatureCount( int bForce )
633 :
634 : {
635 0 : GetLayerDefn();
636 :
637 0 : if( TestCapability(OLCFastFeatureCount) == FALSE )
638 0 : return OGRMSSQLSpatialLayer::GetFeatureCount( bForce );
639 :
640 0 : ClearStatement();
641 :
642 0 : CPLODBCStatement* poStatement = BuildStatement( "count(*)" );
643 :
644 0 : if (poStatement == NULL || !poStatement->Fetch())
645 : {
646 0 : delete poStatement;
647 0 : return OGRMSSQLSpatialLayer::GetFeatureCount( bForce );
648 : }
649 :
650 0 : int nRet = atoi(poStatement->GetColData( 0 ));
651 0 : delete poStatement;
652 0 : return nRet;
653 : }
654 :
655 :
656 : /************************************************************************/
657 : /* CreateField() */
658 : /************************************************************************/
659 :
660 0 : OGRErr OGRMSSQLSpatialTableLayer::CreateField( OGRFieldDefn *poFieldIn,
661 : int bApproxOK )
662 :
663 : {
664 : char szFieldType[256];
665 0 : OGRFieldDefn oField( poFieldIn );
666 :
667 0 : GetLayerDefn();
668 :
669 : /* -------------------------------------------------------------------- */
670 : /* Do we want to "launder" the column names into MSSQL */
671 : /* friendly format? */
672 : /* -------------------------------------------------------------------- */
673 0 : if( bLaunderColumnNames )
674 : {
675 0 : char *pszSafeName = poDS->LaunderName( oField.GetNameRef() );
676 :
677 0 : oField.SetName( pszSafeName );
678 0 : CPLFree( pszSafeName );
679 : }
680 :
681 : /* -------------------------------------------------------------------- */
682 : /* Identify the MSSQL type. */
683 : /* -------------------------------------------------------------------- */
684 :
685 0 : if( oField.GetType() == OFTInteger )
686 : {
687 0 : if( oField.GetWidth() > 0 && bPreservePrecision )
688 0 : sprintf( szFieldType, "numeric(%d,0)", oField.GetWidth() );
689 : else
690 0 : strcpy( szFieldType, "int" );
691 : }
692 0 : else if( oField.GetType() == OFTReal )
693 : {
694 0 : if( oField.GetWidth() > 0 && oField.GetPrecision() > 0
695 : && bPreservePrecision )
696 : sprintf( szFieldType, "numeric(%d,%d)",
697 0 : oField.GetWidth(), oField.GetPrecision() );
698 : else
699 0 : strcpy( szFieldType, "float" );
700 : }
701 0 : else if( oField.GetType() == OFTString )
702 : {
703 0 : if( oField.GetWidth() == 0 || !bPreservePrecision )
704 0 : strcpy( szFieldType, "varchar(MAX)" );
705 : else
706 0 : sprintf( szFieldType, "varchar(%d)", oField.GetWidth() );
707 : }
708 0 : else if( oField.GetType() == OFTDate )
709 : {
710 0 : strcpy( szFieldType, "date" );
711 : }
712 0 : else if( oField.GetType() == OFTTime )
713 : {
714 0 : strcpy( szFieldType, "time(7)" );
715 : }
716 0 : else if( oField.GetType() == OFTDateTime )
717 : {
718 0 : strcpy( szFieldType, "datetime" );
719 : }
720 0 : else if( oField.GetType() == OFTBinary )
721 : {
722 0 : strcpy( szFieldType, "image" );
723 : }
724 0 : else if( bApproxOK )
725 : {
726 : CPLError( CE_Warning, CPLE_NotSupported,
727 : "Can't create field %s with type %s on MSSQL layers. Creating as varchar.",
728 : oField.GetNameRef(),
729 0 : OGRFieldDefn::GetFieldTypeName(oField.GetType()) );
730 0 : strcpy( szFieldType, "varchar" );
731 : }
732 : else
733 : {
734 : CPLError( CE_Failure, CPLE_NotSupported,
735 : "Can't create field %s with type %s on MSSQL layers.",
736 : oField.GetNameRef(),
737 0 : OGRFieldDefn::GetFieldTypeName(oField.GetType()) );
738 :
739 0 : return OGRERR_FAILURE;
740 : }
741 :
742 : /* -------------------------------------------------------------------- */
743 : /* Create the new field. */
744 : /* -------------------------------------------------------------------- */
745 :
746 0 : CPLODBCStatement oStmt( poDS->GetSession() );
747 :
748 : oStmt.Appendf( "ALTER TABLE [%s].[%s] ADD [%s] %s",
749 0 : pszSchemaName, pszTableName, oField.GetNameRef(), szFieldType);
750 :
751 0 : if( !oStmt.ExecuteSQL() )
752 : {
753 : CPLError( CE_Failure, CPLE_AppDefined,
754 : "Error creating field %s, %s", oField.GetNameRef(),
755 0 : poDS->GetSession()->GetLastError() );
756 :
757 0 : return OGRERR_FAILURE;
758 : }
759 :
760 : /* -------------------------------------------------------------------- */
761 : /* Add the field to the OGRFeatureDefn. */
762 : /* -------------------------------------------------------------------- */
763 :
764 0 : poFeatureDefn->AddFieldDefn( &oField );
765 :
766 0 : return OGRERR_NONE;
767 : }
768 :
769 : /************************************************************************/
770 : /* SetFeature() */
771 : /* */
772 : /* SetFeature() is implemented by an UPDATE SQL command */
773 : /************************************************************************/
774 :
775 0 : OGRErr OGRMSSQLSpatialTableLayer::SetFeature( OGRFeature *poFeature )
776 :
777 : {
778 0 : OGRErr eErr = OGRERR_FAILURE;
779 :
780 0 : GetLayerDefn();
781 :
782 0 : if( NULL == poFeature )
783 : {
784 : CPLError( CE_Failure, CPLE_AppDefined,
785 0 : "NULL pointer to OGRFeature passed to SetFeature()." );
786 0 : return eErr;
787 : }
788 :
789 0 : if( poFeature->GetFID() == OGRNullFID )
790 : {
791 : CPLError( CE_Failure, CPLE_AppDefined,
792 0 : "FID required on features given to SetFeature()." );
793 0 : return eErr;
794 : }
795 :
796 0 : if( !pszFIDColumn )
797 : {
798 : CPLError( CE_Failure, CPLE_AppDefined,
799 : "Unable to update features in tables without\n"
800 0 : "a recognised FID column.");
801 0 : return eErr;
802 :
803 : }
804 :
805 0 : ClearStatement();
806 :
807 : /* -------------------------------------------------------------------- */
808 : /* Form the UPDATE command. */
809 : /* -------------------------------------------------------------------- */
810 0 : CPLODBCStatement oStmt( poDS->GetSession() );
811 :
812 0 : oStmt.Appendf( "UPDATE [%s].[%s] SET ", pszSchemaName, pszTableName);
813 :
814 0 : OGRMSSQLGeometryValidator oValidator(poFeature->GetGeometryRef());
815 0 : OGRGeometry *poGeom = oValidator.GetValidGeometryRef();
816 :
817 0 : if (poFeature->GetGeometryRef() != poGeom)
818 : {
819 : CPLError( CE_Warning, CPLE_NotSupported,
820 0 : "Geometry with FID = %ld has been modified.", poFeature->GetFID() );
821 : }
822 :
823 0 : int bNeedComma = FALSE;
824 0 : if(pszGeomColumn != NULL)
825 : {
826 0 : char *pszWKT = NULL;
827 :
828 0 : if (poGeom != NULL)
829 0 : poGeom->exportToWkt( &pszWKT );
830 :
831 0 : oStmt.Appendf( "[%s] = ", pszGeomColumn );
832 :
833 0 : if( pszWKT != NULL && (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY
834 : || nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY))
835 : {
836 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
837 : {
838 0 : oStmt.Append( "geography::STGeomFromText(" );
839 0 : OGRMSSQLAppendEscaped(&oStmt, pszWKT);
840 0 : oStmt.Appendf(",%d)", nSRSId );
841 : }
842 : else
843 : {
844 0 : oStmt.Append( "geometry::STGeomFromText(" );
845 0 : OGRMSSQLAppendEscaped(&oStmt, pszWKT);
846 0 : oStmt.Appendf(",%d).MakeValid()", nSRSId );
847 : }
848 : }
849 : else
850 0 : oStmt.Append( "null" );
851 :
852 0 : bNeedComma = TRUE;
853 0 : CPLFree(pszWKT);
854 : }
855 :
856 0 : int nFieldCount = poFeatureDefn->GetFieldCount();
857 : int i;
858 0 : for( i = 0; i < nFieldCount; i++ )
859 : {
860 0 : if (bNeedComma)
861 0 : oStmt.Appendf( ", [%s] = ", poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
862 : else
863 : {
864 0 : oStmt.Appendf( "[%s] = ", poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
865 0 : bNeedComma = TRUE;
866 : }
867 :
868 0 : if( !poFeature->IsFieldSet( i ) )
869 0 : oStmt.Append( "null" );
870 : else
871 0 : AppendFieldValue(&oStmt, poFeature, i);
872 : }
873 :
874 : /* Add the WHERE clause */
875 0 : oStmt.Appendf( " WHERE [%s] = %ld" , pszFIDColumn, poFeature->GetFID());
876 :
877 : /* -------------------------------------------------------------------- */
878 : /* Execute the update. */
879 : /* -------------------------------------------------------------------- */
880 :
881 0 : if( !oStmt.ExecuteSQL() )
882 : {
883 : CPLError( CE_Failure, CPLE_AppDefined,
884 : "Error updating feature with FID:%ld, %s", poFeature->GetFID(),
885 0 : poDS->GetSession()->GetLastError() );
886 :
887 0 : return OGRERR_FAILURE;
888 : }
889 :
890 0 : return OGRERR_NONE;
891 : }
892 :
893 : /************************************************************************/
894 : /* DeleteFeature() */
895 : /************************************************************************/
896 :
897 0 : OGRErr OGRMSSQLSpatialTableLayer::DeleteFeature( long nFID )
898 :
899 : {
900 0 : GetLayerDefn();
901 :
902 0 : if( pszFIDColumn == NULL )
903 : {
904 : CPLError( CE_Failure, CPLE_AppDefined,
905 0 : "DeleteFeature() without any FID column." );
906 0 : return OGRERR_FAILURE;
907 : }
908 :
909 0 : if( nFID == OGRNullFID )
910 : {
911 : CPLError( CE_Failure, CPLE_AppDefined,
912 0 : "DeleteFeature() with unset FID fails." );
913 0 : return OGRERR_FAILURE;
914 : }
915 :
916 0 : ClearStatement();
917 :
918 : /* -------------------------------------------------------------------- */
919 : /* Drop the record with this FID. */
920 : /* -------------------------------------------------------------------- */
921 0 : CPLODBCStatement oStatement( poDS->GetSession() );
922 :
923 : oStatement.Appendf("DELETE FROM [%s] WHERE [%s] = %ld",
924 0 : poFeatureDefn->GetName(), pszFIDColumn, nFID);
925 :
926 0 : if( !oStatement.ExecuteSQL() )
927 : {
928 : CPLError( CE_Failure, CPLE_AppDefined,
929 : "Attempt to delete feature with FID %ld failed. %s",
930 0 : nFID, poDS->GetSession()->GetLastError() );
931 :
932 0 : return OGRERR_FAILURE;
933 : }
934 :
935 0 : return OGRERR_NONE;
936 : }
937 :
938 : /************************************************************************/
939 : /* CreateFeature() */
940 : /************************************************************************/
941 :
942 0 : OGRErr OGRMSSQLSpatialTableLayer::CreateFeature( OGRFeature *poFeature )
943 :
944 : {
945 0 : GetLayerDefn();
946 :
947 0 : if( NULL == poFeature )
948 : {
949 : CPLError( CE_Failure, CPLE_AppDefined,
950 0 : "NULL pointer to OGRFeature passed to CreateFeature()." );
951 0 : return OGRERR_FAILURE;
952 : }
953 :
954 0 : ClearStatement();
955 :
956 0 : CPLODBCStatement oStatement( poDS->GetSession() );
957 :
958 : /* the fid values are retieved from the source layer */
959 0 : if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL )
960 0 : oStatement.Appendf("SET IDENTITY_INSERT [%s].[%s] ON;", pszSchemaName, pszTableName );
961 :
962 : /* -------------------------------------------------------------------- */
963 : /* Form the INSERT command. */
964 : /* -------------------------------------------------------------------- */
965 :
966 0 : oStatement.Appendf( "INSERT INTO [%s].[%s] (", pszSchemaName, pszTableName );
967 :
968 0 : OGRMSSQLGeometryValidator oValidator(poFeature->GetGeometryRef());
969 0 : OGRGeometry *poGeom = oValidator.GetValidGeometryRef();
970 :
971 0 : if (poFeature->GetGeometryRef() != poGeom)
972 : {
973 : CPLError( CE_Warning, CPLE_NotSupported,
974 0 : "Geometry with FID = %ld has been modified.", poFeature->GetFID() );
975 : }
976 :
977 0 : int bNeedComma = FALSE;
978 :
979 0 : if (poGeom != NULL && pszGeomColumn != NULL)
980 : {
981 0 : oStatement.Append( pszGeomColumn );
982 0 : bNeedComma = TRUE;
983 : }
984 :
985 0 : if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL )
986 : {
987 0 : if (bNeedComma)
988 0 : oStatement.Appendf( ", [%s]", pszFIDColumn );
989 : else
990 : {
991 0 : oStatement.Appendf( "[%s]", pszFIDColumn );
992 0 : bNeedComma = TRUE;
993 : }
994 : }
995 :
996 0 : int nFieldCount = poFeatureDefn->GetFieldCount();
997 : int i;
998 0 : for( i = 0; i < nFieldCount; i++ )
999 : {
1000 0 : if( !poFeature->IsFieldSet( i ) )
1001 0 : continue;
1002 :
1003 0 : if (bNeedComma)
1004 0 : oStatement.Appendf( ", [%s]", poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
1005 : else
1006 : {
1007 0 : oStatement.Appendf( "[%s]", poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
1008 0 : bNeedComma = TRUE;
1009 : }
1010 : }
1011 :
1012 0 : oStatement.Appendf( ") VALUES (" );
1013 :
1014 : /* Set the geometry */
1015 0 : bNeedComma = FALSE;
1016 0 : if(poGeom != NULL && pszGeomColumn != NULL)
1017 : {
1018 0 : char *pszWKT = NULL;
1019 :
1020 : //poGeom->setCoordinateDimension( nCoordDimension );
1021 :
1022 0 : poGeom->exportToWkt( &pszWKT );
1023 :
1024 0 : if( pszWKT != NULL && (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY
1025 : || nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY))
1026 : {
1027 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
1028 : {
1029 0 : oStatement.Append( "geography::STGeomFromText(" );
1030 0 : OGRMSSQLAppendEscaped(&oStatement, pszWKT);
1031 0 : oStatement.Appendf(",%d)", nSRSId );
1032 : }
1033 : else
1034 : {
1035 0 : oStatement.Append( "geometry::STGeomFromText(" );
1036 0 : OGRMSSQLAppendEscaped(&oStatement, pszWKT);
1037 0 : oStatement.Appendf(",%d).MakeValid()", nSRSId );
1038 : }
1039 : }
1040 : else
1041 0 : oStatement.Append( "null" );
1042 :
1043 0 : bNeedComma = TRUE;
1044 0 : CPLFree(pszWKT);
1045 : }
1046 :
1047 : /* Set the FID */
1048 0 : if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL )
1049 : {
1050 0 : if (bNeedComma)
1051 0 : oStatement.Appendf( ", %ld", poFeature->GetFID() );
1052 : else
1053 : {
1054 0 : oStatement.Appendf( "%ld", poFeature->GetFID() );
1055 0 : bNeedComma = TRUE;
1056 : }
1057 : }
1058 :
1059 0 : for( i = 0; i < nFieldCount; i++ )
1060 : {
1061 0 : if( !poFeature->IsFieldSet( i ) )
1062 0 : continue;
1063 :
1064 0 : if (bNeedComma)
1065 0 : oStatement.Append( ", " );
1066 : else
1067 0 : bNeedComma = TRUE;
1068 :
1069 0 : AppendFieldValue(&oStatement, poFeature, i);
1070 : }
1071 :
1072 0 : oStatement.Append( ");" );
1073 :
1074 0 : if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL )
1075 0 : oStatement.Appendf("SET IDENTITY_INSERT [%s].[%s] OFF;", pszSchemaName, pszTableName );
1076 :
1077 : /* -------------------------------------------------------------------- */
1078 : /* Execute the insert. */
1079 : /* -------------------------------------------------------------------- */
1080 :
1081 0 : if( !oStatement.ExecuteSQL() )
1082 : {
1083 : CPLError( CE_Failure, CPLE_AppDefined,
1084 : "INSERT command for new feature failed. %s",
1085 0 : poDS->GetSession()->GetLastError() );
1086 :
1087 0 : return OGRERR_FAILURE;
1088 : }
1089 :
1090 0 : return OGRERR_NONE;
1091 : }
1092 :
1093 : /************************************************************************/
1094 : /* AppendFieldValue() */
1095 : /* */
1096 : /* Used by CreateFeature() and SetFeature() to format a */
1097 : /* non-empty field value */
1098 : /************************************************************************/
1099 :
1100 0 : void OGRMSSQLSpatialTableLayer::AppendFieldValue(CPLODBCStatement *poStatement,
1101 : OGRFeature* poFeature, int i)
1102 : {
1103 0 : int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
1104 :
1105 : // We need special formatting for integer list values.
1106 0 : if( nOGRFieldType == OFTIntegerList )
1107 : {
1108 : //TODO
1109 0 : poStatement->Append( "null" );
1110 0 : return;
1111 : }
1112 :
1113 : // We need special formatting for real list values.
1114 0 : else if( nOGRFieldType == OFTRealList )
1115 : {
1116 : //TODO
1117 0 : poStatement->Append( "null" );
1118 0 : return;
1119 : }
1120 :
1121 : // We need special formatting for string list values.
1122 0 : else if( nOGRFieldType == OFTStringList )
1123 : {
1124 : //TODO
1125 0 : poStatement->Append( "null" );
1126 0 : return;
1127 : }
1128 :
1129 : // Binary formatting
1130 0 : if( nOGRFieldType == OFTBinary )
1131 : {
1132 0 : int nLen = 0;
1133 0 : GByte* pabyData = poFeature->GetFieldAsBinary( i, &nLen );
1134 0 : char* pszBytes = GByteArrayToHexString( pabyData, nLen);
1135 0 : poStatement->Append( pszBytes );
1136 0 : CPLFree(pszBytes);
1137 0 : return;
1138 : }
1139 :
1140 : // Flag indicating NULL or not-a-date date value
1141 : // e.g. 0000-00-00 - there is no year 0
1142 0 : OGRBoolean bIsDateNull = FALSE;
1143 :
1144 0 : const char *pszStrValue = poFeature->GetFieldAsString(i);
1145 :
1146 : // Check if date is NULL: 0000-00-00
1147 0 : if( nOGRFieldType == OFTDate )
1148 : {
1149 0 : if( EQUALN( pszStrValue, "0000", 4 ) )
1150 : {
1151 0 : pszStrValue = "null";
1152 0 : bIsDateNull = TRUE;
1153 : }
1154 : }
1155 0 : else if ( nOGRFieldType == OFTReal )
1156 : {
1157 0 : char* pszComma = strchr((char*)pszStrValue, ',');
1158 0 : if (pszComma)
1159 0 : *pszComma = '.';
1160 : }
1161 :
1162 0 : if( nOGRFieldType != OFTInteger && nOGRFieldType != OFTReal
1163 : && !bIsDateNull )
1164 : {
1165 0 : OGRMSSQLAppendEscaped(poStatement, pszStrValue);
1166 : }
1167 : else
1168 : {
1169 0 : poStatement->Append( pszStrValue );
1170 : }
1171 : }
1172 :
|