1 : /******************************************************************************
2 : * $Id: ogrmssqlspatialtablelayer.cpp 23942 2012-02-11 13:55:23Z rouault $
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 23942 2012-02-11 13:55:23Z rouault $");
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 : || EQUAL(pszCap,OLCDeleteFeature) )
607 0 : return TRUE;
608 :
609 0 : else if( EQUAL(pszCap,OLCRandomWrite) )
610 0 : return (pszFIDColumn != NULL);
611 : }
612 :
613 : #if (ODBCVER >= 0x0300)
614 0 : if( EQUAL(pszCap,OLCTransactions) )
615 0 : return TRUE;
616 : #else
617 : if( EQUAL(pszCap,OLCTransactions) )
618 : return FALSE;
619 : #endif
620 :
621 0 : if( EQUAL(pszCap,OLCRandomRead) )
622 0 : return (pszFIDColumn != NULL);
623 0 : else if( EQUAL(pszCap,OLCFastFeatureCount) )
624 0 : return TRUE;
625 : else
626 0 : return OGRMSSQLSpatialLayer::TestCapability( pszCap );
627 : }
628 :
629 : /************************************************************************/
630 : /* GetFeatureCount() */
631 : /************************************************************************/
632 :
633 0 : int OGRMSSQLSpatialTableLayer::GetFeatureCount( int bForce )
634 :
635 : {
636 0 : GetLayerDefn();
637 :
638 0 : if( TestCapability(OLCFastFeatureCount) == FALSE )
639 0 : return OGRMSSQLSpatialLayer::GetFeatureCount( bForce );
640 :
641 0 : ClearStatement();
642 :
643 0 : CPLODBCStatement* poStatement = BuildStatement( "count(*)" );
644 :
645 0 : if (poStatement == NULL || !poStatement->Fetch())
646 : {
647 0 : delete poStatement;
648 0 : return OGRMSSQLSpatialLayer::GetFeatureCount( bForce );
649 : }
650 :
651 0 : int nRet = atoi(poStatement->GetColData( 0 ));
652 0 : delete poStatement;
653 0 : return nRet;
654 : }
655 :
656 :
657 : /************************************************************************/
658 : /* CreateField() */
659 : /************************************************************************/
660 :
661 0 : OGRErr OGRMSSQLSpatialTableLayer::CreateField( OGRFieldDefn *poFieldIn,
662 : int bApproxOK )
663 :
664 : {
665 : char szFieldType[256];
666 0 : OGRFieldDefn oField( poFieldIn );
667 :
668 0 : GetLayerDefn();
669 :
670 : /* -------------------------------------------------------------------- */
671 : /* Do we want to "launder" the column names into MSSQL */
672 : /* friendly format? */
673 : /* -------------------------------------------------------------------- */
674 0 : if( bLaunderColumnNames )
675 : {
676 0 : char *pszSafeName = poDS->LaunderName( oField.GetNameRef() );
677 :
678 0 : oField.SetName( pszSafeName );
679 0 : CPLFree( pszSafeName );
680 : }
681 :
682 : /* -------------------------------------------------------------------- */
683 : /* Identify the MSSQL type. */
684 : /* -------------------------------------------------------------------- */
685 :
686 0 : if( oField.GetType() == OFTInteger )
687 : {
688 0 : if( oField.GetWidth() > 0 && bPreservePrecision )
689 0 : sprintf( szFieldType, "numeric(%d,0)", oField.GetWidth() );
690 : else
691 0 : strcpy( szFieldType, "int" );
692 : }
693 0 : else if( oField.GetType() == OFTReal )
694 : {
695 0 : if( oField.GetWidth() > 0 && oField.GetPrecision() > 0
696 : && bPreservePrecision )
697 : sprintf( szFieldType, "numeric(%d,%d)",
698 0 : oField.GetWidth(), oField.GetPrecision() );
699 : else
700 0 : strcpy( szFieldType, "float" );
701 : }
702 0 : else if( oField.GetType() == OFTString )
703 : {
704 0 : if( oField.GetWidth() == 0 || !bPreservePrecision )
705 0 : strcpy( szFieldType, "varchar(MAX)" );
706 : else
707 0 : sprintf( szFieldType, "varchar(%d)", oField.GetWidth() );
708 : }
709 0 : else if( oField.GetType() == OFTDate )
710 : {
711 0 : strcpy( szFieldType, "date" );
712 : }
713 0 : else if( oField.GetType() == OFTTime )
714 : {
715 0 : strcpy( szFieldType, "time(7)" );
716 : }
717 0 : else if( oField.GetType() == OFTDateTime )
718 : {
719 0 : strcpy( szFieldType, "datetime" );
720 : }
721 0 : else if( oField.GetType() == OFTBinary )
722 : {
723 0 : strcpy( szFieldType, "image" );
724 : }
725 0 : else if( bApproxOK )
726 : {
727 : CPLError( CE_Warning, CPLE_NotSupported,
728 : "Can't create field %s with type %s on MSSQL layers. Creating as varchar.",
729 : oField.GetNameRef(),
730 0 : OGRFieldDefn::GetFieldTypeName(oField.GetType()) );
731 0 : strcpy( szFieldType, "varchar" );
732 : }
733 : else
734 : {
735 : CPLError( CE_Failure, CPLE_NotSupported,
736 : "Can't create field %s with type %s on MSSQL layers.",
737 : oField.GetNameRef(),
738 0 : OGRFieldDefn::GetFieldTypeName(oField.GetType()) );
739 :
740 0 : return OGRERR_FAILURE;
741 : }
742 :
743 : /* -------------------------------------------------------------------- */
744 : /* Create the new field. */
745 : /* -------------------------------------------------------------------- */
746 :
747 0 : CPLODBCStatement oStmt( poDS->GetSession() );
748 :
749 : oStmt.Appendf( "ALTER TABLE [%s].[%s] ADD [%s] %s",
750 0 : pszSchemaName, pszTableName, oField.GetNameRef(), szFieldType);
751 :
752 0 : if( !oStmt.ExecuteSQL() )
753 : {
754 : CPLError( CE_Failure, CPLE_AppDefined,
755 : "Error creating field %s, %s", oField.GetNameRef(),
756 0 : poDS->GetSession()->GetLastError() );
757 :
758 0 : return OGRERR_FAILURE;
759 : }
760 :
761 : /* -------------------------------------------------------------------- */
762 : /* Add the field to the OGRFeatureDefn. */
763 : /* -------------------------------------------------------------------- */
764 :
765 0 : poFeatureDefn->AddFieldDefn( &oField );
766 :
767 0 : return OGRERR_NONE;
768 : }
769 :
770 : /************************************************************************/
771 : /* SetFeature() */
772 : /* */
773 : /* SetFeature() is implemented by an UPDATE SQL command */
774 : /************************************************************************/
775 :
776 0 : OGRErr OGRMSSQLSpatialTableLayer::SetFeature( OGRFeature *poFeature )
777 :
778 : {
779 0 : OGRErr eErr = OGRERR_FAILURE;
780 :
781 0 : GetLayerDefn();
782 :
783 0 : if( NULL == poFeature )
784 : {
785 : CPLError( CE_Failure, CPLE_AppDefined,
786 0 : "NULL pointer to OGRFeature passed to SetFeature()." );
787 0 : return eErr;
788 : }
789 :
790 0 : if( poFeature->GetFID() == OGRNullFID )
791 : {
792 : CPLError( CE_Failure, CPLE_AppDefined,
793 0 : "FID required on features given to SetFeature()." );
794 0 : return eErr;
795 : }
796 :
797 0 : if( !pszFIDColumn )
798 : {
799 : CPLError( CE_Failure, CPLE_AppDefined,
800 : "Unable to update features in tables without\n"
801 0 : "a recognised FID column.");
802 0 : return eErr;
803 :
804 : }
805 :
806 0 : ClearStatement();
807 :
808 : /* -------------------------------------------------------------------- */
809 : /* Form the UPDATE command. */
810 : /* -------------------------------------------------------------------- */
811 0 : CPLODBCStatement oStmt( poDS->GetSession() );
812 :
813 0 : oStmt.Appendf( "UPDATE [%s].[%s] SET ", pszSchemaName, pszTableName);
814 :
815 0 : OGRMSSQLGeometryValidator oValidator(poFeature->GetGeometryRef());
816 0 : OGRGeometry *poGeom = oValidator.GetValidGeometryRef();
817 :
818 0 : if (poFeature->GetGeometryRef() != poGeom)
819 : {
820 : CPLError( CE_Warning, CPLE_NotSupported,
821 0 : "Geometry with FID = %ld has been modified.", poFeature->GetFID() );
822 : }
823 :
824 0 : int bNeedComma = FALSE;
825 0 : if(pszGeomColumn != NULL)
826 : {
827 0 : char *pszWKT = NULL;
828 :
829 0 : if (poGeom != NULL)
830 0 : poGeom->exportToWkt( &pszWKT );
831 :
832 0 : oStmt.Appendf( "[%s] = ", pszGeomColumn );
833 :
834 0 : if( pszWKT != NULL && (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY
835 : || nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY))
836 : {
837 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
838 : {
839 0 : oStmt.Append( "geography::STGeomFromText(" );
840 0 : OGRMSSQLAppendEscaped(&oStmt, pszWKT);
841 0 : oStmt.Appendf(",%d)", nSRSId );
842 : }
843 : else
844 : {
845 0 : oStmt.Append( "geometry::STGeomFromText(" );
846 0 : OGRMSSQLAppendEscaped(&oStmt, pszWKT);
847 0 : oStmt.Appendf(",%d).MakeValid()", nSRSId );
848 : }
849 : }
850 : else
851 0 : oStmt.Append( "null" );
852 :
853 0 : bNeedComma = TRUE;
854 0 : CPLFree(pszWKT);
855 : }
856 :
857 0 : int nFieldCount = poFeatureDefn->GetFieldCount();
858 : int i;
859 0 : for( i = 0; i < nFieldCount; i++ )
860 : {
861 0 : if (bNeedComma)
862 0 : oStmt.Appendf( ", [%s] = ", poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
863 : else
864 : {
865 0 : oStmt.Appendf( "[%s] = ", poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
866 0 : bNeedComma = TRUE;
867 : }
868 :
869 0 : if( !poFeature->IsFieldSet( i ) )
870 0 : oStmt.Append( "null" );
871 : else
872 0 : AppendFieldValue(&oStmt, poFeature, i);
873 : }
874 :
875 : /* Add the WHERE clause */
876 0 : oStmt.Appendf( " WHERE [%s] = %ld" , pszFIDColumn, poFeature->GetFID());
877 :
878 : /* -------------------------------------------------------------------- */
879 : /* Execute the update. */
880 : /* -------------------------------------------------------------------- */
881 :
882 0 : if( !oStmt.ExecuteSQL() )
883 : {
884 : CPLError( CE_Failure, CPLE_AppDefined,
885 : "Error updating feature with FID:%ld, %s", poFeature->GetFID(),
886 0 : poDS->GetSession()->GetLastError() );
887 :
888 0 : return OGRERR_FAILURE;
889 : }
890 :
891 0 : return OGRERR_NONE;
892 : }
893 :
894 : /************************************************************************/
895 : /* DeleteFeature() */
896 : /************************************************************************/
897 :
898 0 : OGRErr OGRMSSQLSpatialTableLayer::DeleteFeature( long nFID )
899 :
900 : {
901 0 : GetLayerDefn();
902 :
903 0 : if( pszFIDColumn == NULL )
904 : {
905 : CPLError( CE_Failure, CPLE_AppDefined,
906 0 : "DeleteFeature() without any FID column." );
907 0 : return OGRERR_FAILURE;
908 : }
909 :
910 0 : if( nFID == OGRNullFID )
911 : {
912 : CPLError( CE_Failure, CPLE_AppDefined,
913 0 : "DeleteFeature() with unset FID fails." );
914 0 : return OGRERR_FAILURE;
915 : }
916 :
917 0 : ClearStatement();
918 :
919 : /* -------------------------------------------------------------------- */
920 : /* Drop the record with this FID. */
921 : /* -------------------------------------------------------------------- */
922 0 : CPLODBCStatement oStatement( poDS->GetSession() );
923 :
924 : oStatement.Appendf("DELETE FROM [%s] WHERE [%s] = %ld",
925 0 : poFeatureDefn->GetName(), pszFIDColumn, nFID);
926 :
927 0 : if( !oStatement.ExecuteSQL() )
928 : {
929 : CPLError( CE_Failure, CPLE_AppDefined,
930 : "Attempt to delete feature with FID %ld failed. %s",
931 0 : nFID, poDS->GetSession()->GetLastError() );
932 :
933 0 : return OGRERR_FAILURE;
934 : }
935 :
936 0 : return OGRERR_NONE;
937 : }
938 :
939 : /************************************************************************/
940 : /* CreateFeature() */
941 : /************************************************************************/
942 :
943 0 : OGRErr OGRMSSQLSpatialTableLayer::CreateFeature( OGRFeature *poFeature )
944 :
945 : {
946 0 : GetLayerDefn();
947 :
948 0 : if( NULL == poFeature )
949 : {
950 : CPLError( CE_Failure, CPLE_AppDefined,
951 0 : "NULL pointer to OGRFeature passed to CreateFeature()." );
952 0 : return OGRERR_FAILURE;
953 : }
954 :
955 0 : ClearStatement();
956 :
957 0 : CPLODBCStatement oStatement( poDS->GetSession() );
958 :
959 : /* the fid values are retieved from the source layer */
960 0 : if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL )
961 0 : oStatement.Appendf("SET IDENTITY_INSERT [%s].[%s] ON;", pszSchemaName, pszTableName );
962 :
963 : /* -------------------------------------------------------------------- */
964 : /* Form the INSERT command. */
965 : /* -------------------------------------------------------------------- */
966 :
967 0 : oStatement.Appendf( "INSERT INTO [%s].[%s] (", pszSchemaName, pszTableName );
968 :
969 0 : OGRMSSQLGeometryValidator oValidator(poFeature->GetGeometryRef());
970 0 : OGRGeometry *poGeom = oValidator.GetValidGeometryRef();
971 :
972 0 : if (poFeature->GetGeometryRef() != poGeom)
973 : {
974 : CPLError( CE_Warning, CPLE_NotSupported,
975 0 : "Geometry with FID = %ld has been modified.", poFeature->GetFID() );
976 : }
977 :
978 0 : int bNeedComma = FALSE;
979 :
980 0 : if (poGeom != NULL && pszGeomColumn != NULL)
981 : {
982 0 : oStatement.Append( pszGeomColumn );
983 0 : bNeedComma = TRUE;
984 : }
985 :
986 0 : if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL )
987 : {
988 0 : if (bNeedComma)
989 0 : oStatement.Appendf( ", [%s]", pszFIDColumn );
990 : else
991 : {
992 0 : oStatement.Appendf( "[%s]", pszFIDColumn );
993 0 : bNeedComma = TRUE;
994 : }
995 : }
996 :
997 0 : int nFieldCount = poFeatureDefn->GetFieldCount();
998 : int i;
999 0 : for( i = 0; i < nFieldCount; i++ )
1000 : {
1001 0 : if( !poFeature->IsFieldSet( i ) )
1002 0 : continue;
1003 :
1004 0 : if (bNeedComma)
1005 0 : oStatement.Appendf( ", [%s]", poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
1006 : else
1007 : {
1008 0 : oStatement.Appendf( "[%s]", poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
1009 0 : bNeedComma = TRUE;
1010 : }
1011 : }
1012 :
1013 0 : oStatement.Appendf( ") VALUES (" );
1014 :
1015 : /* Set the geometry */
1016 0 : bNeedComma = FALSE;
1017 0 : if(poGeom != NULL && pszGeomColumn != NULL)
1018 : {
1019 0 : char *pszWKT = NULL;
1020 :
1021 : //poGeom->setCoordinateDimension( nCoordDimension );
1022 :
1023 0 : poGeom->exportToWkt( &pszWKT );
1024 :
1025 0 : if( pszWKT != NULL && (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY
1026 : || nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY))
1027 : {
1028 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
1029 : {
1030 0 : oStatement.Append( "geography::STGeomFromText(" );
1031 0 : OGRMSSQLAppendEscaped(&oStatement, pszWKT);
1032 0 : oStatement.Appendf(",%d)", nSRSId );
1033 : }
1034 : else
1035 : {
1036 0 : oStatement.Append( "geometry::STGeomFromText(" );
1037 0 : OGRMSSQLAppendEscaped(&oStatement, pszWKT);
1038 0 : oStatement.Appendf(",%d).MakeValid()", nSRSId );
1039 : }
1040 : }
1041 : else
1042 0 : oStatement.Append( "null" );
1043 :
1044 0 : bNeedComma = TRUE;
1045 0 : CPLFree(pszWKT);
1046 : }
1047 :
1048 : /* Set the FID */
1049 0 : if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL )
1050 : {
1051 0 : if (bNeedComma)
1052 0 : oStatement.Appendf( ", %ld", poFeature->GetFID() );
1053 : else
1054 : {
1055 0 : oStatement.Appendf( "%ld", poFeature->GetFID() );
1056 0 : bNeedComma = TRUE;
1057 : }
1058 : }
1059 :
1060 0 : for( i = 0; i < nFieldCount; i++ )
1061 : {
1062 0 : if( !poFeature->IsFieldSet( i ) )
1063 0 : continue;
1064 :
1065 0 : if (bNeedComma)
1066 0 : oStatement.Append( ", " );
1067 : else
1068 0 : bNeedComma = TRUE;
1069 :
1070 0 : AppendFieldValue(&oStatement, poFeature, i);
1071 : }
1072 :
1073 0 : oStatement.Append( ");" );
1074 :
1075 0 : if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL )
1076 0 : oStatement.Appendf("SET IDENTITY_INSERT [%s].[%s] OFF;", pszSchemaName, pszTableName );
1077 :
1078 : /* -------------------------------------------------------------------- */
1079 : /* Execute the insert. */
1080 : /* -------------------------------------------------------------------- */
1081 :
1082 0 : if( !oStatement.ExecuteSQL() )
1083 : {
1084 : CPLError( CE_Failure, CPLE_AppDefined,
1085 : "INSERT command for new feature failed. %s",
1086 0 : poDS->GetSession()->GetLastError() );
1087 :
1088 0 : return OGRERR_FAILURE;
1089 : }
1090 :
1091 0 : return OGRERR_NONE;
1092 : }
1093 :
1094 : /************************************************************************/
1095 : /* AppendFieldValue() */
1096 : /* */
1097 : /* Used by CreateFeature() and SetFeature() to format a */
1098 : /* non-empty field value */
1099 : /************************************************************************/
1100 :
1101 0 : void OGRMSSQLSpatialTableLayer::AppendFieldValue(CPLODBCStatement *poStatement,
1102 : OGRFeature* poFeature, int i)
1103 : {
1104 0 : int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
1105 :
1106 : // We need special formatting for integer list values.
1107 0 : if( nOGRFieldType == OFTIntegerList )
1108 : {
1109 : //TODO
1110 0 : poStatement->Append( "null" );
1111 0 : return;
1112 : }
1113 :
1114 : // We need special formatting for real list values.
1115 0 : else if( nOGRFieldType == OFTRealList )
1116 : {
1117 : //TODO
1118 0 : poStatement->Append( "null" );
1119 0 : return;
1120 : }
1121 :
1122 : // We need special formatting for string list values.
1123 0 : else if( nOGRFieldType == OFTStringList )
1124 : {
1125 : //TODO
1126 0 : poStatement->Append( "null" );
1127 0 : return;
1128 : }
1129 :
1130 : // Binary formatting
1131 0 : if( nOGRFieldType == OFTBinary )
1132 : {
1133 0 : int nLen = 0;
1134 0 : GByte* pabyData = poFeature->GetFieldAsBinary( i, &nLen );
1135 0 : char* pszBytes = GByteArrayToHexString( pabyData, nLen);
1136 0 : poStatement->Append( pszBytes );
1137 0 : CPLFree(pszBytes);
1138 0 : return;
1139 : }
1140 :
1141 : // Flag indicating NULL or not-a-date date value
1142 : // e.g. 0000-00-00 - there is no year 0
1143 0 : OGRBoolean bIsDateNull = FALSE;
1144 :
1145 0 : const char *pszStrValue = poFeature->GetFieldAsString(i);
1146 :
1147 : // Check if date is NULL: 0000-00-00
1148 0 : if( nOGRFieldType == OFTDate )
1149 : {
1150 0 : if( EQUALN( pszStrValue, "0000", 4 ) )
1151 : {
1152 0 : pszStrValue = "null";
1153 0 : bIsDateNull = TRUE;
1154 : }
1155 : }
1156 0 : else if ( nOGRFieldType == OFTReal )
1157 : {
1158 0 : char* pszComma = strchr((char*)pszStrValue, ',');
1159 0 : if (pszComma)
1160 0 : *pszComma = '.';
1161 : }
1162 :
1163 0 : if( nOGRFieldType != OFTInteger && nOGRFieldType != OFTReal
1164 : && !bIsDateNull )
1165 : {
1166 0 : OGRMSSQLAppendEscaped(poStatement, pszStrValue);
1167 : }
1168 : else
1169 : {
1170 0 : poStatement->Append( pszStrValue );
1171 : }
1172 : }
1173 :
|