1 /******************************************************************************
2 *
3 * Project: Oracle Spatial Driver
4 * Purpose: Implementation of the OGROCITableLayer class. This class provides
5 * layer semantics on a table, but utilizing a lot of machinery from
6 * the OGROCILayer base class.
7 * Author: Frank Warmerdam, warmerdam@pobox.com
8 *
9 ******************************************************************************
10 * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining a
13 * copy of this software and associated documentation files (the "Software"),
14 * to deal in the Software without restriction, including without limitation
15 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 * and/or sell copies of the Software, and to permit persons to whom the
17 * Software is furnished to do so, subject to the following conditions:
18 *
19 * The above copyright notice and this permission notice shall be included
20 * in all copies or substantial portions of the Software.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 * DEALINGS IN THE SOFTWARE.
29 ****************************************************************************/
30
31 #include "ogr_oci.h"
32 #include "cpl_conv.h"
33 #include "cpl_string.h"
34
35 CPL_CVSID("$Id: ogrocitablelayer.cpp fa752ad6eabafaf630a704e1892a9d837d683cb3 2021-03-06 17:04:38 +0100 Even Rouault $")
36
37 static int nDiscarded = 0;
38 static int nHits = 0;
39
40 #define HSI_UNKNOWN -2
41
42 /************************************************************************/
43 /* OGROCITableLayer() */
44 /************************************************************************/
45
OGROCITableLayer(OGROCIDataSource * poDSIn,const char * pszTableName,OGRwkbGeometryType eGType,int nSRIDIn,int bUpdate,int bNewLayerIn)46 OGROCITableLayer::OGROCITableLayer( OGROCIDataSource *poDSIn,
47 const char * pszTableName, OGRwkbGeometryType eGType,
48 int nSRIDIn, int bUpdate, int bNewLayerIn )
49
50 {
51 poDS = poDSIn;
52 bExtentUpdated = false;
53
54 pszQuery = nullptr;
55 pszWHERE = CPLStrdup( "" );
56 pszQueryStatement = nullptr;
57
58 bUpdateAccess = bUpdate;
59 bNewLayer = bNewLayerIn;
60
61 iNextShapeId = 0;
62 iNextFIDToWrite = -1;
63
64 bValidTable = FALSE;
65 if( bNewLayerIn )
66 bHaveSpatialIndex = FALSE;
67 else
68 bHaveSpatialIndex = HSI_UNKNOWN;
69
70 poFeatureDefn = ReadTableDefinition( pszTableName );
71 if( eGType != wkbUnknown && poFeatureDefn->GetGeomFieldCount() > 0 )
72 poFeatureDefn->GetGeomFieldDefn(0)->SetType(eGType);
73 SetDescription( poFeatureDefn->GetName() );
74
75 nSRID = nSRIDIn;
76 if( nSRID == -1 )
77 nSRID = LookupTableSRID();
78
79 poSRS = poDSIn->FetchSRS( nSRID );
80 if( poSRS != nullptr )
81 poSRS->Reference();
82
83 hOrdVARRAY = nullptr;
84 hElemInfoVARRAY = nullptr;
85
86 poBoundStatement = nullptr;
87
88 nWriteCacheMax = 0;
89 nWriteCacheUsed = 0;
90 pasWriteGeoms = nullptr;
91 papsWriteGeomMap = nullptr;
92 pasWriteGeomInd = nullptr;
93 papsWriteGeomIndMap = nullptr;
94
95 papWriteFields = nullptr;
96 papaeWriteFieldInd = nullptr;
97
98 panWriteFIDs = nullptr;
99
100 nDefaultStringSize = 4000;
101
102 OGROCITableLayer::ResetReading();
103 }
104
105 /************************************************************************/
106 /* ~OGROCITableLayer() */
107 /************************************************************************/
108
~OGROCITableLayer()109 OGROCITableLayer::~OGROCITableLayer()
110
111 {
112 int i;
113
114 OGROCITableLayer::SyncToDisk();
115
116 CPLFree( panWriteFIDs );
117 if( papWriteFields != nullptr )
118 {
119 for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
120 {
121 CPLFree( papWriteFields[i] );
122 CPLFree( papaeWriteFieldInd[i] );
123 }
124 }
125
126 CPLFree( papWriteFields );
127 CPLFree( papaeWriteFieldInd );
128
129 if( poBoundStatement != nullptr )
130 delete poBoundStatement;
131
132 CPLFree( pasWriteGeomInd );
133 CPLFree( papsWriteGeomIndMap );
134
135 CPLFree( papsWriteGeomMap );
136 CPLFree( pasWriteGeoms );
137
138 CPLFree( pszQuery );
139 CPLFree( pszWHERE );
140
141 if( poSRS != nullptr && poSRS->Dereference() == 0 )
142 delete poSRS;
143 }
144
145 /************************************************************************/
146 /* ReadTableDefinition() */
147 /* */
148 /* Build a schema from the named table. Done by querying the */
149 /* catalog. */
150 /************************************************************************/
151
ReadTableDefinition(const char * pszTable)152 OGRFeatureDefn *OGROCITableLayer::ReadTableDefinition( const char * pszTable )
153
154 {
155 OGROCISession *poSession = poDS->GetSession();
156 sword nStatus;
157
158 CPLString osUnquotedTableName;
159 CPLString osQuotedTableName;
160
161 /* -------------------------------------------------------------------- */
162 /* Split out the owner if available. */
163 /* -------------------------------------------------------------------- */
164 if( strstr(pszTable,".") != nullptr )
165 {
166 osTableName = strstr(pszTable,".") + 1;
167 osOwner.assign( pszTable, strlen(pszTable)-osTableName.size() - 1 );
168 osUnquotedTableName.Printf( "%s.%s", osOwner.c_str(), osTableName.c_str() );
169 osQuotedTableName.Printf( "\"%s\".\"%s\"", osOwner.c_str(), osTableName.c_str() );
170 }
171 else
172 {
173 osTableName = pszTable;
174 osOwner = "";
175 osUnquotedTableName.Printf( "%s", pszTable );
176 osQuotedTableName.Printf( "\"%s\"", pszTable );
177 }
178
179 OGRFeatureDefn *poDefn = new OGRFeatureDefn( osUnquotedTableName.c_str() );
180
181 poDefn->Reference();
182
183 /* -------------------------------------------------------------------- */
184 /* Do a DescribeAll on the table. */
185 /* -------------------------------------------------------------------- */
186 OCIParam *hAttrParam = nullptr;
187 OCIParam *hAttrList = nullptr;
188
189 // Table name unquoted
190
191 nStatus =
192 OCIDescribeAny( poSession->hSvcCtx, poSession->hError,
193 (dvoid *) osUnquotedTableName.c_str(),
194 static_cast<ub4>(osUnquotedTableName.length()), OCI_OTYPE_NAME,
195 OCI_DEFAULT, OCI_PTYPE_TABLE, poSession->hDescribe );
196
197 if( poSession->Failed( nStatus, "OCIDescribeAny" ) )
198 {
199 CPLErrorReset();
200
201 // View name unquoted
202
203 nStatus =
204 OCIDescribeAny(poSession->hSvcCtx, poSession->hError,
205 (dvoid *) osUnquotedTableName.c_str(),
206 static_cast<ub4>(osUnquotedTableName.length()), OCI_OTYPE_NAME,
207 OCI_DEFAULT, OCI_PTYPE_VIEW, poSession->hDescribe );
208
209 if( poSession->Failed( nStatus, "OCIDescribeAny" ) )
210 {
211 CPLErrorReset();
212
213 // Table name quoted
214
215 nStatus =
216 OCIDescribeAny( poSession->hSvcCtx, poSession->hError,
217 (dvoid *) osQuotedTableName.c_str(),
218 static_cast<ub4>(osQuotedTableName.length()), OCI_OTYPE_NAME,
219 OCI_DEFAULT, OCI_PTYPE_TABLE, poSession->hDescribe );
220
221 if( poSession->Failed( nStatus, "OCIDescribeAny" ) )
222 {
223 CPLErrorReset();
224
225 // View name quoted
226
227 nStatus =
228 OCIDescribeAny(poSession->hSvcCtx, poSession->hError,
229 (dvoid *) osQuotedTableName.c_str(),
230 static_cast<ub4>(osQuotedTableName.length()), OCI_OTYPE_NAME,
231 OCI_DEFAULT, OCI_PTYPE_VIEW, poSession->hDescribe );
232
233 if( poSession->Failed( nStatus, "OCIDescribeAny" ) )
234 return poDefn;
235 }
236 }
237 }
238
239 if( poSession->Failed(
240 OCIAttrGet( poSession->hDescribe, OCI_HTYPE_DESCRIBE,
241 &hAttrParam, nullptr, OCI_ATTR_PARAM, poSession->hError ),
242 "OCIAttrGet(ATTR_PARAM)") )
243 return poDefn;
244
245 if( poSession->Failed(
246 OCIAttrGet( hAttrParam, OCI_DTYPE_PARAM, &hAttrList, nullptr,
247 OCI_ATTR_LIST_COLUMNS, poSession->hError ),
248 "OCIAttrGet(ATTR_LIST_COLUMNS)" ) )
249 return poDefn;
250
251 /* -------------------------------------------------------------------- */
252 /* What is the name of the column to use as FID? This defaults */
253 /* to OGR_FID but we allow it to be overridden by a config */
254 /* variable. Ideally we would identify a column that is a */
255 /* primary key and use that, but I'm not yet sure how to */
256 /* accomplish that. */
257 /* -------------------------------------------------------------------- */
258 const char *pszExpectedFIDName =
259 CPLGetConfigOption( "OCI_FID", "OGR_FID" );
260 int bGeomFieldNullable = FALSE;
261
262 /* -------------------------------------------------------------------- */
263 /* Parse the returned table information. */
264 /* -------------------------------------------------------------------- */
265 for( int iRawFld = 0; true; iRawFld++ )
266 {
267 OGRFieldDefn oField( "", OFTString);
268 OCIParam *hParamDesc;
269 ub2 nOCIType;
270 ub4 nOCILen;
271
272 nStatus = OCIParamGet( hAttrList, OCI_DTYPE_PARAM,
273 poSession->hError, (dvoid**)&hParamDesc,
274 (ub4) iRawFld+1 );
275 if( nStatus != OCI_SUCCESS )
276 break;
277
278 if( poSession->GetParamInfo( hParamDesc, &oField, &nOCIType, &nOCILen )
279 != CE_None )
280 return poDefn;
281
282 if( oField.GetType() == OFTBinary )
283 {
284 if( nOCIType == 108 && pszGeomName == nullptr )
285 {
286 CPLFree( pszGeomName );
287 pszGeomName = CPLStrdup( oField.GetNameRef() );
288 iGeomColumn = iRawFld;
289 bGeomFieldNullable = oField.IsNullable();
290 }
291 continue;
292 }
293
294 if( EQUAL(oField.GetNameRef(),pszExpectedFIDName)
295 && (oField.GetType() == OFTInteger ||
296 oField.GetType() == OFTInteger64) )
297 {
298 pszFIDName = CPLStrdup(oField.GetNameRef());
299 continue;
300 }
301
302 poDefn->AddFieldDefn( &oField );
303 }
304
305 OGROCIStatement defaultValuesStatement( poSession );
306
307 const char* pszDefaultValueSQL =
308 "SELECT COLUMN_NAME, DATA_DEFAULT\n"
309 "FROM user_tab_columns\n"
310 "WHERE DATA_DEFAULT IS NOT NULL AND TABLE_NAME = UPPER(:table_name)";
311
312 defaultValuesStatement.Prepare(pszDefaultValueSQL);
313 defaultValuesStatement.BindString(":table_name", pszTable);
314
315 if( defaultValuesStatement.Execute( nullptr ) == CE_None )
316 {
317 char **papszRow;
318
319 while( (papszRow = defaultValuesStatement.SimpleFetchRow()) != nullptr )
320 {
321 const char* pszColName = papszRow[0];
322 const char* pszDefault = papszRow[1];
323 int nIdx = poDefn->GetFieldIndex(pszColName);
324 if( nIdx >= 0 )
325 poDefn->GetFieldDefn(nIdx)->SetDefault(pszDefault);
326 }
327 }
328
329 if( EQUAL(pszExpectedFIDName, "OGR_FID") && pszFIDName )
330 {
331 for(int i=0;i<poDefn->GetFieldCount();i++)
332 {
333 // This is presumably a Integer since we always create Integer64 with a
334 // defined precision
335 if( poDefn->GetFieldDefn(i)->GetType() == OFTInteger64 &&
336 poDefn->GetFieldDefn(i)->GetWidth() == 0 )
337 {
338 poDefn->GetFieldDefn(i)->SetType(OFTInteger);
339 }
340 }
341 }
342
343 /* -------------------------------------------------------------------- */
344 /* Identify Geometry dimension */
345 /* -------------------------------------------------------------------- */
346
347 if( pszGeomName != nullptr && strlen(pszGeomName) > 0 )
348 {
349 OGROCIStatement oDimStatement( poSession );
350 char **papszResult;
351 int iDim = -1;
352
353 if( osOwner != "" )
354 {
355 const char* pszDimCmdA =
356 "SELECT COUNT(*)\n"
357 "FROM ALL_SDO_GEOM_METADATA u, TABLE(u.diminfo) t\n"
358 "WHERE u.table_name = :table_name\n"
359 " AND u.column_name = :geometry_name\n"
360 " AND u.owner = :table_owner";
361
362 oDimStatement.Prepare( pszDimCmdA );
363 oDimStatement.BindString( ":table_name", osTableName.c_str() );
364 oDimStatement.BindString( ":geometry_name", pszGeomName );
365 oDimStatement.BindString( ":table_owner", osOwner.c_str() );
366 }
367 else
368 {
369 const char* pszDimCmdB =
370 "SELECT COUNT(*)\n"
371 "FROM USER_SDO_GEOM_METADATA u, TABLE(u.diminfo) t\n"
372 "WHERE u.table_name = :table_name\n"
373 " AND u.column_name = :geometry_name";
374
375 oDimStatement.Prepare( pszDimCmdB );
376 oDimStatement.BindString( ":table_name", osTableName.c_str() );
377 oDimStatement.BindString( ":geometry_name", pszGeomName );
378 }
379 oDimStatement.Execute( nullptr );
380
381 papszResult = oDimStatement.SimpleFetchRow();
382
383 if( CSLCount(papszResult) < 1 )
384 {
385 OGROCIStatement oDimStatement2( poSession );
386 char **papszResult2;
387
388 CPLErrorReset();
389
390 if( osOwner != "" )
391 {
392 const char* pszDimCmd2A =
393 "select m.sdo_index_dims\n"
394 "from all_sdo_index_metadata m, all_sdo_index_info i\n"
395 "where i.index_name = m.sdo_index_name\n"
396 " and i.sdo_index_owner = m.sdo_index_owner\n"
397 " and i.sdo_index_owner = upper(:table_owner)\n"
398 " and i.table_name = upper(:table_name)";
399
400 oDimStatement2.Prepare( pszDimCmd2A );
401 oDimStatement2.BindString( ":table_owner", osOwner.c_str());
402 oDimStatement2.BindString( ":table_name", osTableName.c_str());
403 }
404 else
405 {
406 const char* pszDimCmd2B =
407 "select m.sdo_index_dims\n"
408 "from user_sdo_index_metadata m, user_sdo_index_info i\n"
409 "where i.index_name = m.sdo_index_name\n"
410 " and i.table_name = upper(:table_name)";
411
412 oDimStatement2.Prepare( pszDimCmd2B );
413 oDimStatement2.BindString( ":table_name", osTableName.c_str());
414 }
415 oDimStatement2.Execute( nullptr );
416
417 papszResult2 = oDimStatement2.SimpleFetchRow();
418
419 if( CSLCount( papszResult2 ) > 0 )
420 {
421 iDim = atoi( papszResult2[0] );
422 }
423 else
424 {
425 // we want to clear any errors to avoid confusing the application.
426 CPLErrorReset();
427 }
428 }
429 else
430 {
431 iDim = atoi( papszResult[0] );
432 }
433
434 if( iDim > 0 )
435 {
436 SetDimension( iDim );
437 }
438 else
439 {
440 CPLDebug( "OCI", "get dim based of existing data or index failed." );
441 }
442
443 {
444 OGROCIStatement oDimStatement2( poSession );
445 char **papszResult2;
446
447 CPLErrorReset();
448 if( osOwner != "" )
449 {
450 const char* pszLayerTypeCmdA =
451 "select m.SDO_LAYER_GTYPE "
452 "from all_sdo_index_metadata m, all_sdo_index_info i "
453 "where i.index_name = m.sdo_index_name "
454 "and i.sdo_index_owner = m.sdo_index_owner "
455 "and i.sdo_index_owner = upper(:table_owner) "
456 "and i.table_name = upper(:table_name)";
457
458 oDimStatement2.Prepare( pszLayerTypeCmdA );
459 oDimStatement2.BindString( ":table_owner", osOwner.c_str() );
460 oDimStatement2.BindString( ":table_name", osTableName.c_str() );
461 }
462 else
463 {
464 const char* pszLayerTypeCmdB =
465 "select m.SDO_LAYER_GTYPE "
466 "from user_sdo_index_metadata m, user_sdo_index_info i "
467 "where i.index_name = m.sdo_index_name "
468 "and i.table_name = upper(:table_name)";
469 oDimStatement2.Prepare( pszLayerTypeCmdB );
470 oDimStatement2.BindString( ":table_name", osTableName.c_str() );
471 }
472
473 oDimStatement2.Execute( nullptr );
474
475 papszResult2 = oDimStatement2.SimpleFetchRow();
476
477 if( CSLCount( papszResult2 ) > 0 )
478 {
479 const char* pszLayerGType = papszResult2[0];
480 OGRwkbGeometryType eGeomType = wkbUnknown;
481 if( EQUAL(pszLayerGType, "POINT") )
482 eGeomType = wkbPoint;
483 else if( EQUAL(pszLayerGType, "LINE") )
484 eGeomType = wkbLineString;
485 else if( EQUAL(pszLayerGType, "POLYGON") )
486 eGeomType = wkbPolygon;
487 else if( EQUAL(pszLayerGType, "MULTIPOINT") )
488 eGeomType = wkbMultiPoint;
489 else if( EQUAL(pszLayerGType, "MULTILINE") )
490 eGeomType = wkbMultiLineString;
491 else if( EQUAL(pszLayerGType, "MULTIPOLYGON") )
492 eGeomType = wkbMultiPolygon;
493 else if( !EQUAL(pszLayerGType, "COLLECTION") )
494 CPLDebug("OCI", "LAYER_GTYPE = %s", pszLayerGType );
495 if( iDim == 3 )
496 eGeomType = wkbSetZ(eGeomType);
497 poDefn->GetGeomFieldDefn(0)->SetType( eGeomType );
498 poDefn->GetGeomFieldDefn(0)->SetNullable( bGeomFieldNullable );
499 }
500 else
501 {
502 // we want to clear any errors to avoid confusing the application.
503 CPLErrorReset();
504 }
505 }
506 }
507 else
508 {
509 poDefn->SetGeomType(wkbNone);
510 }
511
512 bValidTable = TRUE;
513
514 return poDefn;
515 }
516
517 /************************************************************************/
518 /* SetSpatialFilter() */
519 /************************************************************************/
520
SetSpatialFilter(OGRGeometry * poGeomIn)521 void OGROCITableLayer::SetSpatialFilter( OGRGeometry * poGeomIn )
522
523 {
524 if( !InstallFilter( poGeomIn ) )
525 return;
526
527 BuildWhere();
528
529 ResetReading();
530 }
531
532 /************************************************************************/
533 /* TestForSpatialIndex() */
534 /************************************************************************/
535
TestForSpatialIndex(const char * pszSpatWHERE)536 void OGROCITableLayer::TestForSpatialIndex( const char *pszSpatWHERE )
537
538 {
539 OGROCIStringBuf oTestCmd;
540 OGROCIStatement oTestStatement( poDS->GetSession() );
541
542 oTestCmd.Append( "SELECT COUNT(*) FROM " );
543 oTestCmd.Append( poFeatureDefn->GetName() );
544 oTestCmd.Append( pszSpatWHERE );
545
546 if( oTestStatement.Execute( oTestCmd.GetString() ) != CE_None )
547 bHaveSpatialIndex = FALSE;
548 else
549 bHaveSpatialIndex = TRUE;
550 }
551
552 /************************************************************************/
553 /* BuildWhere() */
554 /* */
555 /* Build the WHERE statement appropriate to the current set of */
556 /* criteria (spatial and attribute queries). */
557 /************************************************************************/
558
BuildWhere()559 void OGROCITableLayer::BuildWhere()
560
561 {
562 OGROCIStringBuf oWHERE;
563
564 CPLFree( pszWHERE );
565 pszWHERE = nullptr;
566
567 if( m_poFilterGeom != nullptr && bHaveSpatialIndex )
568 {
569 OGREnvelope sEnvelope;
570
571 m_poFilterGeom->getEnvelope( &sEnvelope );
572
573 oWHERE.Append( " WHERE sdo_filter(" );
574 oWHERE.Append( pszGeomName );
575 oWHERE.Append( ", MDSYS.SDO_GEOMETRY(2003," );
576 if( nSRID == -1 )
577 oWHERE.Append( "NULL" );
578 else
579 oWHERE.Appendf( 15, "%d", nSRID );
580 oWHERE.Append( ",NULL," );
581 oWHERE.Append( "MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1)," );
582 oWHERE.Append( "MDSYS.SDO_ORDINATE_ARRAY(" );
583 oWHERE.Appendf( 600,
584 "%.16g,%.16g,%.16g,%.16g,%.16g,%.16g,%.16g,%.16g,%.16g,%.16g",
585 sEnvelope.MinX, sEnvelope.MinY,
586 sEnvelope.MaxX, sEnvelope.MinY,
587 sEnvelope.MaxX, sEnvelope.MaxY,
588 sEnvelope.MinX, sEnvelope.MaxY,
589 sEnvelope.MinX, sEnvelope.MinY);
590 oWHERE.Append( ")), 'querytype=window') = 'TRUE' " );
591 }
592
593 if( bHaveSpatialIndex == HSI_UNKNOWN )
594 {
595 TestForSpatialIndex( oWHERE.GetString() );
596 if( !bHaveSpatialIndex )
597 oWHERE.Clear();
598 }
599
600 if( pszQuery != nullptr )
601 {
602 if( oWHERE.GetLast() == '\0' )
603 oWHERE.Append( "WHERE " );
604 else
605 oWHERE.Append( "AND " );
606
607 oWHERE.Append( pszQuery );
608 }
609
610 pszWHERE = oWHERE.StealString();
611 }
612
613 /************************************************************************/
614 /* BuildFullQueryStatement() */
615 /************************************************************************/
616
BuildFullQueryStatement()617 void OGROCITableLayer::BuildFullQueryStatement()
618
619 {
620 if( pszQueryStatement != nullptr )
621 {
622 CPLFree( pszQueryStatement );
623 pszQueryStatement = nullptr;
624 }
625
626 OGROCIStringBuf oCmd;
627 char *pszFields = BuildFields();
628
629 oCmd.Append( "SELECT " );
630 oCmd.Append( pszFields );
631 oCmd.Append( " FROM " );
632 oCmd.Append( poFeatureDefn->GetName() );
633 oCmd.Append( " " );
634 oCmd.Append( pszWHERE );
635
636 pszQueryStatement = oCmd.StealString();
637
638 CPLFree( pszFields );
639 }
640
641 /************************************************************************/
642 /* GetFeature() */
643 /************************************************************************/
644
GetFeature(GIntBig nFeatureId)645 OGRFeature *OGROCITableLayer::GetFeature( GIntBig nFeatureId )
646
647 {
648
649 /* -------------------------------------------------------------------- */
650 /* If we don't have an FID column scan for the desired feature. */
651 /* -------------------------------------------------------------------- */
652 if( pszFIDName == nullptr )
653 return OGROCILayer::GetFeature( nFeatureId );
654
655 /* -------------------------------------------------------------------- */
656 /* Clear any existing query. */
657 /* -------------------------------------------------------------------- */
658 ResetReading();
659
660 /* -------------------------------------------------------------------- */
661 /* Build query for this specific feature. */
662 /* -------------------------------------------------------------------- */
663 OGROCIStringBuf oCmd;
664 char *pszFields = BuildFields();
665
666 oCmd.Append( "SELECT " );
667 oCmd.Append( pszFields );
668 oCmd.Append( " FROM " );
669 oCmd.Append( poFeatureDefn->GetName() );
670 oCmd.Append( " " );
671 oCmd.Appendf( static_cast<int>(50+strlen(pszFIDName)),
672 " WHERE \"%s\" = " CPL_FRMT_GIB " ",
673 pszFIDName, nFeatureId );
674
675 CPLFree( pszFields );
676
677 /* -------------------------------------------------------------------- */
678 /* Execute the statement. */
679 /* -------------------------------------------------------------------- */
680 if( !ExecuteQuery( oCmd.GetString() ) )
681 return nullptr;
682
683 /* -------------------------------------------------------------------- */
684 /* Get the feature. */
685 /* -------------------------------------------------------------------- */
686 OGRFeature *poFeature;
687
688 poFeature = GetNextRawFeature();
689
690 if( poFeature != nullptr && poFeature->GetGeometryRef() != nullptr )
691 poFeature->GetGeometryRef()->assignSpatialReference( poSRS );
692
693 /* -------------------------------------------------------------------- */
694 /* Cleanup the statement. */
695 /* -------------------------------------------------------------------- */
696 ResetReading();
697
698 /* -------------------------------------------------------------------- */
699 /* verify the FID. */
700 /* -------------------------------------------------------------------- */
701 if( poFeature != nullptr && poFeature->GetFID() != nFeatureId )
702 {
703 CPLError( CE_Failure, CPLE_AppDefined,
704 "OGROCITableLayer::GetFeature(" CPL_FRMT_GIB ") ... query returned feature " CPL_FRMT_GIB " instead!",
705 nFeatureId, poFeature->GetFID() );
706 delete poFeature;
707 return nullptr;
708 }
709 else
710 return poFeature;
711 }
712
713 /************************************************************************/
714 /* GetNextFeature() */
715 /* */
716 /* We override the next feature method because we know that we */
717 /* implement the attribute query within the statement and so we */
718 /* don't have to test here. Eventually the spatial query will */
719 /* be fully tested within the statement as well. */
720 /************************************************************************/
721
GetNextFeature()722 OGRFeature *OGROCITableLayer::GetNextFeature()
723
724 {
725
726 while( true )
727 {
728 OGRFeature *poFeature;
729
730 poFeature = GetNextRawFeature();
731 if( poFeature == nullptr )
732 {
733 CPLDebug( "OCI", "Query complete, got %d hits, and %d discards.",
734 nHits, nDiscarded );
735 nHits = 0;
736 nDiscarded = 0;
737 return nullptr;
738 }
739
740 if( m_poFilterGeom == nullptr
741 || FilterGeometry( poFeature->GetGeometryRef() ) )
742 {
743 nHits++;
744 if( poFeature->GetGeometryRef() != nullptr )
745 poFeature->GetGeometryRef()->assignSpatialReference( poSRS );
746 return poFeature;
747 }
748
749 if( m_poFilterGeom != nullptr )
750 nDiscarded++;
751
752 delete poFeature;
753 }
754 }
755
756 /************************************************************************/
757 /* ResetReading() */
758 /************************************************************************/
759
ResetReading()760 void OGROCITableLayer::ResetReading()
761
762 {
763 nHits = 0;
764 nDiscarded = 0;
765
766 FlushPendingFeatures();
767
768 BuildFullQueryStatement();
769
770 OGROCILayer::ResetReading();
771 }
772
773 /************************************************************************/
774 /* BuildFields() */
775 /* */
776 /* Build list of fields to fetch, performing any required */
777 /* transformations (such as on geometry). */
778 /************************************************************************/
779
BuildFields()780 char *OGROCITableLayer::BuildFields()
781
782 {
783 int i;
784 OGROCIStringBuf oFldList;
785
786 if( pszGeomName )
787 {
788 oFldList.Append( "\"" );
789 oFldList.Append( pszGeomName );
790 oFldList.Append( "\"" );
791 iGeomColumn = 0;
792 }
793
794 for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
795 {
796 const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
797
798 if( oFldList.GetLast() != '\0' )
799 oFldList.Append( "," );
800
801 oFldList.Append( "\"" );
802 oFldList.Append( pszName );
803 oFldList.Append( "\"" );
804 }
805
806 if( pszFIDName != nullptr )
807 {
808 iFIDColumn = poFeatureDefn->GetFieldCount();
809 oFldList.Append( ",\"" );
810 oFldList.Append( pszFIDName );
811 oFldList.Append( "\"" );
812 }
813
814 return oFldList.StealString();
815 }
816
817 /************************************************************************/
818 /* SetAttributeFilter() */
819 /************************************************************************/
820
SetAttributeFilter(const char * pszQueryIn)821 OGRErr OGROCITableLayer::SetAttributeFilter( const char *pszQueryIn )
822
823 {
824 CPLFree(m_pszAttrQueryString);
825 m_pszAttrQueryString = (pszQueryIn) ? CPLStrdup(pszQueryIn) : nullptr;
826
827 if( (pszQueryIn == nullptr && this->pszQuery == nullptr)
828 || (pszQueryIn != nullptr && this->pszQuery != nullptr
829 && strcmp(pszQueryIn,this->pszQuery) == 0) )
830 return OGRERR_NONE;
831
832 CPLFree( this->pszQuery );
833
834 if( pszQueryIn == nullptr )
835 this->pszQuery = nullptr;
836 else
837 this->pszQuery = CPLStrdup( pszQueryIn );
838
839 BuildWhere();
840
841 ResetReading();
842
843 return OGRERR_NONE;
844 }
845
846 /************************************************************************/
847 /* ISetFeature() */
848 /* */
849 /* We implement SetFeature() by deleting the existing row (if */
850 /* it exists), and then using CreateFeature() to write it out */
851 /* tot he table normally. CreateFeature() will preserve the */
852 /* existing FID if possible. */
853 /************************************************************************/
854
ISetFeature(OGRFeature * poFeature)855 OGRErr OGROCITableLayer::ISetFeature( OGRFeature *poFeature )
856
857 {
858 /* -------------------------------------------------------------------- */
859 /* Do some validation. */
860 /* -------------------------------------------------------------------- */
861 if( pszFIDName == nullptr )
862 {
863 CPLError( CE_Failure, CPLE_AppDefined,
864 "OGROCITableLayer::ISetFeature(" CPL_FRMT_GIB ") failed because there is "
865 "no apparent FID column on table %s.",
866 poFeature->GetFID(),
867 poFeatureDefn->GetName() );
868
869 return OGRERR_FAILURE;
870 }
871
872 if( poFeature->GetFID() == OGRNullFID )
873 {
874 CPLError( CE_Failure, CPLE_AppDefined,
875 "OGROCITableLayer::ISetFeature(" CPL_FRMT_GIB ") failed because the feature "
876 "has no FID!", poFeature->GetFID() );
877
878 return OGRERR_FAILURE;
879 }
880
881 OGRErr eErr = DeleteFeature(poFeature->GetFID());
882 if( eErr != OGRERR_NONE )
883 return eErr;
884
885 return CreateFeature( poFeature );
886 }
887
888 /************************************************************************/
889 /* DeleteFeature() */
890 /************************************************************************/
891
DeleteFeature(GIntBig nFID)892 OGRErr OGROCITableLayer::DeleteFeature( GIntBig nFID )
893
894 {
895 /* -------------------------------------------------------------------- */
896 /* Do some validation. */
897 /* -------------------------------------------------------------------- */
898 if( pszFIDName == nullptr )
899 {
900 CPLError( CE_Failure, CPLE_AppDefined,
901 "OGROCITableLayer::DeleteFeature(" CPL_FRMT_GIB ") failed because there is "
902 "no apparent FID column on table %s.",
903 nFID,
904 poFeatureDefn->GetName() );
905
906 return OGRERR_FAILURE;
907 }
908
909 if( nFID == OGRNullFID )
910 {
911 CPLError( CE_Failure, CPLE_AppDefined,
912 "OGROCITableLayer::DeleteFeature(" CPL_FRMT_GIB ") failed for Null FID",
913 nFID );
914
915 return OGRERR_FAILURE;
916 }
917
918 /* -------------------------------------------------------------------- */
919 /* Prepare the delete command, and execute. We don't check the */
920 /* error result of the execute, since attempting to Set a */
921 /* non-existing feature may be OK. */
922 /* -------------------------------------------------------------------- */
923 OGROCIStringBuf oCmdText;
924 OGROCIStatement oCmdStatement( poDS->GetSession() );
925
926 oCmdText.Appendf( static_cast<int>(strlen(poFeatureDefn->GetName())+strlen(pszFIDName)+100),
927 "DELETE FROM %s WHERE \"%s\" = " CPL_FRMT_GIB,
928 poFeatureDefn->GetName(),
929 pszFIDName,
930 nFID );
931
932 if( oCmdStatement.Execute( oCmdText.GetString() ) == CE_None )
933 return (oCmdStatement.GetAffectedRows() > 0) ? OGRERR_NONE : OGRERR_NON_EXISTING_FEATURE;
934 else
935 return OGRERR_FAILURE;
936 }
937
938 /************************************************************************/
939 /* ICreateFeature() */
940 /************************************************************************/
941
ICreateFeature(OGRFeature * poFeature)942 OGRErr OGROCITableLayer::ICreateFeature( OGRFeature *poFeature )
943
944 {
945 /* -------------------------------------------------------------------- */
946 /* Add extents of this geometry to the existing layer extents. */
947 /* -------------------------------------------------------------------- */
948 if( poFeature->GetGeometryRef() != nullptr )
949 {
950 OGREnvelope sThisExtent;
951
952 poFeature->GetGeometryRef()->getEnvelope( &sThisExtent );
953
954 if( !sExtent.Contains( sThisExtent ) )
955 {
956 sExtent.Merge( sThisExtent );
957 bExtentUpdated = true;
958 }
959 }
960
961 /* -------------------------------------------------------------------- */
962 /* Get the first id value from open options */
963 /* -------------------------------------------------------------------- */
964
965 this->nFirstId = -1;
966
967 if (CSLFetchNameValue( papszOptions, "FIRST_ID" ) != nullptr)
968 {
969 this->nFirstId = atoi( CSLFetchNameValue( papszOptions, "FIRST_ID" ) );
970 }
971
972 /* -------------------------------------------------------------------- */
973 /* Get the multi load count value from open options */
974 /* -------------------------------------------------------------------- */
975
976 this->bMultiLoad = CPLFetchBool( papszOptions, "MULTI_LOAD", true );
977
978 this->nMultiLoadCount = 100;
979
980 if (CSLFetchNameValue( papszOptions, "MULTI_LOAD_COUNT" ) != nullptr)
981 {
982 this->nMultiLoadCount = atoi( CSLFetchNameValue( papszOptions,
983 "MULTI_LOAD_COUNT" ) );
984 this->bMultiLoad = true; // overwrites MULTI_LOAD=NO
985 }
986
987 /* -------------------------------------------------------------------- */
988 /* Do the actual creation. */
989 /* -------------------------------------------------------------------- */
990 if( bMultiLoad )
991 return BoundCreateFeature( poFeature );
992 else
993 return UnboundCreateFeature( poFeature );
994 }
995
996 /************************************************************************/
997 /* UnboundCreateFeature() */
998 /************************************************************************/
999
UnboundCreateFeature(OGRFeature * poFeature)1000 OGRErr OGROCITableLayer::UnboundCreateFeature( OGRFeature *poFeature )
1001
1002 {
1003 OGROCISession *poSession = poDS->GetSession();
1004 char *pszCommand;
1005 int bNeedComma = FALSE;
1006 size_t nCommandBufSize;
1007
1008 /* -------------------------------------------------------------------- */
1009 /* Prepare SQL statement buffer. */
1010 /* -------------------------------------------------------------------- */
1011 nCommandBufSize = 2000;
1012 pszCommand = (char *) CPLMalloc(nCommandBufSize);
1013
1014 /* -------------------------------------------------------------------- */
1015 /* Form the INSERT command. */
1016 /* -------------------------------------------------------------------- */
1017 snprintf( pszCommand, nCommandBufSize, "INSERT INTO \"%s\"(\"", poFeatureDefn->GetName() );
1018
1019 if( poFeature->GetGeometryRef() != nullptr )
1020 {
1021 bNeedComma = TRUE;
1022 strcat( pszCommand, pszGeomName );
1023 }
1024
1025 if( pszFIDName != nullptr )
1026 {
1027 if( bNeedComma )
1028 strcat( pszCommand, "\",\"" );
1029
1030 strcat( pszCommand, pszFIDName );
1031 bNeedComma = TRUE;
1032 }
1033
1034 for( int i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
1035 {
1036 if( !poFeature->IsFieldSetAndNotNull( i ) )
1037 continue;
1038
1039 if( !bNeedComma )
1040 bNeedComma = TRUE;
1041 else
1042 strcat( pszCommand, "\",\"" );
1043
1044 snprintf( pszCommand + strlen(pszCommand),
1045 nCommandBufSize - strlen(pszCommand), "%s",
1046 poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
1047 }
1048
1049 strcat( pszCommand, "\") VALUES (" );
1050
1051 CPLAssert( strlen(pszCommand) < nCommandBufSize );
1052
1053 /* -------------------------------------------------------------------- */
1054 /* Set the geometry */
1055 /* -------------------------------------------------------------------- */
1056 bNeedComma = poFeature->GetGeometryRef() != nullptr;
1057 if( poFeature->GetGeometryRef() != nullptr)
1058 {
1059 OGRGeometry *poGeometry = poFeature->GetGeometryRef();
1060 char szSDO_GEOMETRY[512];
1061 char szSRID[128];
1062
1063 if( nSRID == -1 )
1064 strcpy( szSRID, "NULL" );
1065 else
1066 snprintf( szSRID, sizeof(szSRID), "%d", nSRID );
1067
1068 if( wkbFlatten(poGeometry->getGeometryType()) == wkbPoint )
1069 {
1070 OGRPoint *poPoint = poGeometry->toPoint();
1071
1072 if( nDimension == 2 )
1073 CPLsnprintf( szSDO_GEOMETRY, sizeof(szSDO_GEOMETRY),
1074 "%s(%d,%s,MDSYS.SDO_POINT_TYPE(%.16g,%.16g,0),NULL,NULL)",
1075 SDO_GEOMETRY, 2001, szSRID,
1076 poPoint->getX(), poPoint->getY() );
1077 else
1078 CPLsnprintf( szSDO_GEOMETRY, sizeof(szSDO_GEOMETRY),
1079 "%s(%d,%s,MDSYS.SDO_POINT_TYPE(%.16g,%.16g,%.16g),NULL,NULL)",
1080 SDO_GEOMETRY, 3001, szSRID,
1081 poPoint->getX(), poPoint->getY(), poPoint->getZ() );
1082 }
1083 else
1084 {
1085 int nGType;
1086
1087 if( TranslateToSDOGeometry( poFeature->GetGeometryRef(), &nGType )
1088 == OGRERR_NONE )
1089 CPLsnprintf( szSDO_GEOMETRY, sizeof(szSDO_GEOMETRY),
1090 "%s(%d,%s,NULL,:elem_info,:ordinates)",
1091 SDO_GEOMETRY, nGType, szSRID );
1092 else
1093 CPLsnprintf( szSDO_GEOMETRY, sizeof(szSDO_GEOMETRY),"NULL" );
1094 }
1095
1096 if( strlen(pszCommand) + strlen(szSDO_GEOMETRY)
1097 > nCommandBufSize - 50 )
1098 {
1099 nCommandBufSize =
1100 strlen(pszCommand) + strlen(szSDO_GEOMETRY) + 10000;
1101 pszCommand = (char *) CPLRealloc(pszCommand, nCommandBufSize );
1102 }
1103
1104 strcat( pszCommand, szSDO_GEOMETRY );
1105 }
1106
1107 /* -------------------------------------------------------------------- */
1108 /* Set the FID. */
1109 /* -------------------------------------------------------------------- */
1110 size_t nOffset = strlen(pszCommand);
1111
1112 if( pszFIDName != nullptr )
1113 {
1114 GIntBig nFID;
1115
1116 if( bNeedComma )
1117 strcat( pszCommand+nOffset, ", " );
1118 bNeedComma = TRUE;
1119
1120 nOffset += strlen(pszCommand+nOffset);
1121
1122 nFID = poFeature->GetFID();
1123 if( nFID == OGRNullFID )
1124 {
1125 if( iNextFIDToWrite < 0 )
1126 {
1127 iNextFIDToWrite = GetMaxFID() + 1;
1128 }
1129 nFID = iNextFIDToWrite++;
1130 poFeature->SetFID( nFID );
1131 }
1132 snprintf( pszCommand+nOffset, nCommandBufSize - nOffset, CPL_FRMT_GIB, nFID );
1133 }
1134
1135 /* -------------------------------------------------------------------- */
1136 /* Set the other fields. */
1137 /* -------------------------------------------------------------------- */
1138 for( int i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
1139 {
1140 if( !poFeature->IsFieldSetAndNotNull( i ) )
1141 continue;
1142
1143 OGRFieldDefn *poFldDefn = poFeatureDefn->GetFieldDefn(i);
1144 const char *pszStrValue = poFeature->GetFieldAsString(i);
1145
1146 if( bNeedComma )
1147 strcat( pszCommand+nOffset, ", " );
1148 else
1149 bNeedComma = TRUE;
1150
1151 if( strlen(pszStrValue) + strlen(pszCommand+nOffset) + nOffset
1152 > nCommandBufSize-50 )
1153 {
1154 nCommandBufSize = strlen(pszCommand) + strlen(pszStrValue) + 10000;
1155 pszCommand = (char *) CPLRealloc(pszCommand, nCommandBufSize );
1156 }
1157
1158 if( poFldDefn->GetType() == OFTInteger
1159 || poFldDefn->GetType() == OFTInteger64
1160 || poFldDefn->GetType() == OFTReal )
1161 {
1162 if( poFldDefn->GetWidth() > 0 && bPreservePrecision
1163 && (int) strlen(pszStrValue) > poFldDefn->GetWidth() )
1164 {
1165 strcat( pszCommand+nOffset, "NULL" );
1166 ReportTruncation( poFldDefn );
1167 }
1168 else
1169 strcat( pszCommand+nOffset, pszStrValue );
1170 }
1171 else
1172 {
1173 int iChar;
1174
1175 /* We need to quote and escape string fields. */
1176 strcat( pszCommand+nOffset, "'" );
1177
1178 nOffset += strlen(pszCommand+nOffset);
1179
1180 for( iChar = 0; pszStrValue[iChar] != '\0'; iChar++ )
1181 {
1182 if( poFldDefn->GetWidth() != 0 && bPreservePrecision
1183 && iChar >= poFldDefn->GetWidth() )
1184 {
1185 ReportTruncation( poFldDefn );
1186 break;
1187 }
1188
1189 if( pszStrValue[iChar] == '\'' )
1190 {
1191 pszCommand[nOffset++] = '\'';
1192 pszCommand[nOffset++] = pszStrValue[iChar];
1193 }
1194 else
1195 pszCommand[nOffset++] = pszStrValue[iChar];
1196 }
1197 pszCommand[nOffset] = '\0';
1198
1199 strcat( pszCommand+nOffset, "'" );
1200 }
1201 nOffset += strlen(pszCommand+nOffset);
1202 }
1203
1204 strcat( pszCommand+nOffset, ")" );
1205
1206 /* -------------------------------------------------------------------- */
1207 /* Prepare statement. */
1208 /* -------------------------------------------------------------------- */
1209 OGROCIStatement oInsert( poSession );
1210 int bHaveOrdinates = strstr(pszCommand,":ordinates") != nullptr;
1211 int bHaveElemInfo = strstr(pszCommand,":elem_info") != nullptr;
1212
1213 if( oInsert.Prepare( pszCommand ) != CE_None )
1214 {
1215 CPLFree( pszCommand );
1216 return OGRERR_FAILURE;
1217 }
1218
1219 CPLFree( pszCommand );
1220
1221 /* -------------------------------------------------------------------- */
1222 /* Bind and translate the elem_info if we have some. */
1223 /* -------------------------------------------------------------------- */
1224 if( bHaveElemInfo )
1225 {
1226 OCIBind *hBindOrd = nullptr;
1227 int i;
1228 OCINumber oci_number;
1229
1230 // Create or clear VARRAY
1231 if( hElemInfoVARRAY == nullptr )
1232 {
1233 if( poSession->Failed(
1234 OCIObjectNew( poSession->hEnv, poSession->hError,
1235 poSession->hSvcCtx, OCI_TYPECODE_VARRAY,
1236 poSession->hElemInfoTDO, (dvoid *)nullptr,
1237 OCI_DURATION_SESSION,
1238 FALSE, (dvoid **)&hElemInfoVARRAY),
1239 "OCIObjectNew(hElemInfoVARRAY)") )
1240 return OGRERR_FAILURE;
1241 }
1242 else
1243 {
1244 sb4 nOldCount;
1245
1246 OCICollSize( poSession->hEnv, poSession->hError,
1247 hElemInfoVARRAY, &nOldCount );
1248 OCICollTrim( poSession->hEnv, poSession->hError,
1249 nOldCount, hElemInfoVARRAY );
1250 }
1251
1252 // Prepare the VARRAY of ordinate values.
1253 for (i = 0; i < nElemInfoCount; i++)
1254 {
1255 if( poSession->Failed(
1256 OCINumberFromInt( poSession->hError,
1257 (dvoid *) (panElemInfo + i),
1258 (uword)sizeof(int),
1259 OCI_NUMBER_SIGNED,
1260 &oci_number),
1261 "OCINumberFromInt") )
1262 return OGRERR_FAILURE;
1263
1264 if( poSession->Failed(
1265 OCICollAppend( poSession->hEnv, poSession->hError,
1266 (dvoid *) &oci_number,
1267 (dvoid *)nullptr, hElemInfoVARRAY),
1268 "OCICollAppend") )
1269 return OGRERR_FAILURE;
1270 }
1271
1272 // Do the binding.
1273 if( poSession->Failed(
1274 OCIBindByName( oInsert.GetStatement(), &hBindOrd,
1275 poSession->hError,
1276 (text *) ":elem_info", (sb4) -1, (dvoid *) nullptr,
1277 (sb4) 0, SQLT_NTY, (dvoid *)nullptr, (ub2 *)nullptr,
1278 (ub2 *)nullptr, (ub4)0, (ub4 *)nullptr,
1279 (ub4)OCI_DEFAULT),
1280 "OCIBindByName(:elem_info)") )
1281 return OGRERR_FAILURE;
1282
1283 if( poSession->Failed(
1284 OCIBindObject( hBindOrd, poSession->hError,
1285 poSession->hElemInfoTDO,
1286 (dvoid **)&hElemInfoVARRAY, (ub4 *)nullptr,
1287 (dvoid **)nullptr, (ub4 *)nullptr),
1288 "OCIBindObject(:elem_info)" ) )
1289 return OGRERR_FAILURE;
1290 }
1291
1292 /* -------------------------------------------------------------------- */
1293 /* Bind and translate the ordinates if we have some. */
1294 /* -------------------------------------------------------------------- */
1295 if( bHaveOrdinates )
1296 {
1297 OCIBind *hBindOrd = nullptr;
1298 int i;
1299 OCINumber oci_number;
1300
1301 // Create or clear VARRAY
1302 if( hOrdVARRAY == nullptr )
1303 {
1304 if( poSession->Failed(
1305 OCIObjectNew( poSession->hEnv, poSession->hError,
1306 poSession->hSvcCtx, OCI_TYPECODE_VARRAY,
1307 poSession->hOrdinatesTDO, (dvoid *)nullptr,
1308 OCI_DURATION_SESSION,
1309 FALSE, (dvoid **)&hOrdVARRAY),
1310 "OCIObjectNew(hOrdVARRAY)") )
1311 return OGRERR_FAILURE;
1312 }
1313 else
1314 {
1315 sb4 nOldCount;
1316
1317 OCICollSize( poSession->hEnv, poSession->hError,
1318 hOrdVARRAY, &nOldCount );
1319 OCICollTrim( poSession->hEnv, poSession->hError,
1320 nOldCount, hOrdVARRAY );
1321 }
1322
1323 // Prepare the VARRAY of ordinate values.
1324 for (i = 0; i < nOrdinalCount; i++)
1325 {
1326 if( poSession->Failed(
1327 OCINumberFromReal( poSession->hError,
1328 (dvoid *) (padfOrdinals + i),
1329 (uword)sizeof(double),
1330 &oci_number),
1331 "OCINumberFromReal") )
1332 return OGRERR_FAILURE;
1333
1334 if( poSession->Failed(
1335 OCICollAppend( poSession->hEnv, poSession->hError,
1336 (dvoid *) &oci_number,
1337 (dvoid *)nullptr, hOrdVARRAY),
1338 "OCICollAppend") )
1339 return OGRERR_FAILURE;
1340 }
1341
1342 // Do the binding.
1343 if( poSession->Failed(
1344 OCIBindByName( oInsert.GetStatement(), &hBindOrd,
1345 poSession->hError,
1346 (text *) ":ordinates", (sb4) -1, (dvoid *) nullptr,
1347 (sb4) 0, SQLT_NTY, (dvoid *)nullptr, (ub2 *)nullptr,
1348 (ub2 *)nullptr, (ub4)0, (ub4 *)nullptr,
1349 (ub4)OCI_DEFAULT),
1350 "OCIBindByName(:ordinates)") )
1351 return OGRERR_FAILURE;
1352
1353 if( poSession->Failed(
1354 OCIBindObject( hBindOrd, poSession->hError,
1355 poSession->hOrdinatesTDO,
1356 (dvoid **)&hOrdVARRAY, (ub4 *)nullptr,
1357 (dvoid **)nullptr, (ub4 *)nullptr),
1358 "OCIBindObject(:ordinates)" ) )
1359 return OGRERR_FAILURE;
1360 }
1361
1362 /* -------------------------------------------------------------------- */
1363 /* Execute the insert. */
1364 /* -------------------------------------------------------------------- */
1365 if( oInsert.Execute( nullptr ) != CE_None )
1366 return OGRERR_FAILURE;
1367 else
1368 return OGRERR_NONE;
1369 }
1370
1371 /************************************************************************/
1372 /* GetExtent() */
1373 /************************************************************************/
1374
GetExtent(OGREnvelope * psExtent,int bForce)1375 OGRErr OGROCITableLayer::GetExtent(OGREnvelope *psExtent, int bForce)
1376
1377 {
1378 CPLAssert( nullptr != psExtent );
1379
1380 OGRErr err = OGRERR_FAILURE;
1381
1382 if( EQUAL(GetGeometryColumn(),"") )
1383 {
1384 return OGRERR_NONE;
1385 }
1386
1387 /* -------------------------------------------------------------------- */
1388 /* Build query command. */
1389 /* -------------------------------------------------------------------- */
1390 CPLAssert( nullptr != pszGeomName );
1391
1392 OGROCIStringBuf oCommand;
1393 oCommand.Appendf( 1000, "SELECT "
1394 "MIN(SDO_GEOM.SDO_MIN_MBR_ORDINATE(t.%s,m.DIMINFO,1)) AS MINX,"
1395 "MIN(SDO_GEOM.SDO_MIN_MBR_ORDINATE(t.%s,m.DIMINFO,2)) AS MINY,"
1396 "MAX(SDO_GEOM.SDO_MAX_MBR_ORDINATE(t.%s,m.DIMINFO,1)) AS MAXX,"
1397 "MAX(SDO_GEOM.SDO_MAX_MBR_ORDINATE(t.%s,m.DIMINFO,2)) AS MAXY "
1398 "FROM ALL_SDO_GEOM_METADATA m, ",
1399 pszGeomName, pszGeomName, pszGeomName, pszGeomName );
1400
1401 if( osOwner != "" )
1402 {
1403 oCommand.Appendf( 500, " %s.%s t ",
1404 osOwner.c_str(), osTableName.c_str() );
1405 }
1406 else
1407 {
1408 oCommand.Appendf( 500, " %s t ",
1409 osTableName.c_str() );
1410 }
1411
1412 oCommand.Appendf( 500, "WHERE m.TABLE_NAME = UPPER('%s') AND m.COLUMN_NAME = UPPER('%s')",
1413 osTableName.c_str(), pszGeomName );
1414
1415 if( osOwner != "" )
1416 {
1417 oCommand.Appendf( 500, " AND OWNER = UPPER('%s')", osOwner.c_str() );
1418 }
1419
1420 /* -------------------------------------------------------------------- */
1421 /* Execute query command. */
1422 /* -------------------------------------------------------------------- */
1423 OGROCISession *poSession = poDS->GetSession();
1424 CPLAssert( nullptr != poSession );
1425
1426 OGROCIStatement oGetExtent( poSession );
1427
1428 if( oGetExtent.Execute( oCommand.GetString() ) == CE_None )
1429 {
1430 char **papszRow = oGetExtent.SimpleFetchRow();
1431
1432 if( papszRow != nullptr
1433 && papszRow[0] != nullptr && papszRow[1] != nullptr
1434 && papszRow[2] != nullptr && papszRow[3] != nullptr )
1435 {
1436 psExtent->MinX = CPLAtof(papszRow[0]);
1437 psExtent->MinY = CPLAtof(papszRow[1]);
1438 psExtent->MaxX = CPLAtof(papszRow[2]);
1439 psExtent->MaxY = CPLAtof(papszRow[3]);
1440
1441 err = OGRERR_NONE;
1442 }
1443 }
1444
1445 /* -------------------------------------------------------------------- */
1446 /* Query spatial extent of layer using default, */
1447 /* but not optimized implementation. */
1448 /* -------------------------------------------------------------------- */
1449 if( err != OGRERR_NONE )
1450 {
1451 err = OGRLayer::GetExtent( psExtent, bForce );
1452 CPLDebug( "OCI",
1453 "Failing to query extent of %s using default GetExtent",
1454 osTableName.c_str() );
1455 }
1456
1457 return err;
1458 }
1459
1460 /************************************************************************/
1461 /* TestCapability() */
1462 /************************************************************************/
1463
TestCapability(const char * pszCap)1464 int OGROCITableLayer::TestCapability( const char * pszCap )
1465
1466 {
1467 if( EQUAL(pszCap,OLCSequentialWrite)
1468 || EQUAL(pszCap,OLCRandomWrite) )
1469 return bUpdateAccess;
1470
1471 else if( EQUAL(pszCap,OLCCreateField) )
1472 return bUpdateAccess;
1473
1474 else
1475 return OGROCILayer::TestCapability( pszCap );
1476 }
1477
1478 /************************************************************************/
1479 /* GetFeatureCount() */
1480 /* */
1481 /* If a spatial filter is in effect, we turn control over to */
1482 /* the generic counter. Otherwise we return the total count. */
1483 /* Eventually we should consider implementing a more efficient */
1484 /* way of counting features matching a spatial query. */
1485 /************************************************************************/
1486
GetFeatureCount(int bForce)1487 GIntBig OGROCITableLayer::GetFeatureCount( int bForce )
1488
1489 {
1490 /* -------------------------------------------------------------------- */
1491 /* Use a more brute force mechanism if we have a spatial query */
1492 /* in play. */
1493 /* -------------------------------------------------------------------- */
1494 if( m_poFilterGeom != nullptr )
1495 return OGROCILayer::GetFeatureCount( bForce );
1496
1497 /* -------------------------------------------------------------------- */
1498 /* In theory it might be wise to cache this result, but it */
1499 /* won't be trivial to work out the lifetime of the value. */
1500 /* After all someone else could be adding records from another */
1501 /* application when working against a database. */
1502 /* -------------------------------------------------------------------- */
1503 OGROCISession *poSession = poDS->GetSession();
1504 OGROCIStatement oGetCount( poSession );
1505 char szCommand[1024];
1506 char **papszResult;
1507
1508 snprintf( szCommand, sizeof(szCommand), "SELECT COUNT(*) FROM %s %s",
1509 poFeatureDefn->GetName(), pszWHERE );
1510
1511 oGetCount.Execute( szCommand );
1512
1513 papszResult = oGetCount.SimpleFetchRow();
1514
1515 if( CSLCount(papszResult) < 1 )
1516 {
1517 CPLDebug( "OCI", "Fast get count failed, doing hard way." );
1518 return OGROCILayer::GetFeatureCount( bForce );
1519 }
1520
1521 return CPLAtoGIntBig(papszResult[0]);
1522 }
1523
1524 /************************************************************************/
1525 /* UpdateLayerExtents() */
1526 /************************************************************************/
1527
UpdateLayerExtents()1528 void OGROCITableLayer::UpdateLayerExtents()
1529
1530 {
1531 if( !bExtentUpdated )
1532 return;
1533
1534 bExtentUpdated = false;
1535
1536 /* -------------------------------------------------------------------- */
1537 /* Do we have existing layer extents we need to merge in to the */
1538 /* ones we collected as we created features? */
1539 /* -------------------------------------------------------------------- */
1540 bool bHaveOldExtent = false;
1541
1542 if( !bNewLayer && pszGeomName )
1543 {
1544 OGROCIStringBuf oCommand;
1545
1546 oCommand.Appendf(1000,
1547 "select min(case when r=1 then sdo_lb else null end) minx, min(case when r=2 then sdo_lb else null end) miny, "
1548 "min(case when r=1 then sdo_ub else null end) maxx, min(case when r=2 then sdo_ub else null end) maxy"
1549 " from (SELECT d.sdo_dimname, d.sdo_lb, sdo_ub, sdo_tolerance, rownum r"
1550 " FROM ALL_SDO_GEOM_METADATA m, table(m.diminfo) d"
1551 " where m.table_name = UPPER('%s') and m.COLUMN_NAME = UPPER('%s')",
1552 osTableName.c_str(), pszGeomName );
1553
1554 if( osOwner != "" )
1555 {
1556 oCommand.Appendf(500, " AND OWNER = UPPER('%s')", osOwner.c_str() );
1557 }
1558
1559 oCommand.Append(" ) ");
1560
1561 OGROCISession *poSession = poDS->GetSession();
1562 CPLAssert( nullptr != poSession );
1563
1564 OGROCIStatement oGetExtent( poSession );
1565
1566 if( oGetExtent.Execute( oCommand.GetString() ) == CE_None )
1567 {
1568 char **papszRow = oGetExtent.SimpleFetchRow();
1569
1570 if( papszRow != nullptr
1571 && papszRow[0] != nullptr && papszRow[1] != nullptr
1572 && papszRow[2] != nullptr && papszRow[3] != nullptr )
1573 {
1574 OGREnvelope sOldExtent;
1575
1576 bHaveOldExtent = true;
1577
1578 sOldExtent.MinX = CPLAtof(papszRow[0]);
1579 sOldExtent.MinY = CPLAtof(papszRow[1]);
1580 sOldExtent.MaxX = CPLAtof(papszRow[2]);
1581 sOldExtent.MaxY = CPLAtof(papszRow[3]);
1582
1583 if( sOldExtent.Contains( sExtent ) )
1584 {
1585 // nothing to do!
1586 sExtent = sOldExtent;
1587 bExtentUpdated = false;
1588 return;
1589 }
1590 else
1591 {
1592 sExtent.Merge( sOldExtent );
1593 }
1594 }
1595 }
1596 }
1597
1598 /* -------------------------------------------------------------------- */
1599 /* Establish the extents and resolution to use. */
1600 /* -------------------------------------------------------------------- */
1601 double dfResSize;
1602 double dfXMin, dfXMax, dfXRes;
1603 double dfYMin, dfYMax, dfYRes;
1604 double dfZMin, dfZMax, dfZRes;
1605
1606 if( sExtent.MaxX - sExtent.MinX > 400 )
1607 dfResSize = 0.001;
1608 else
1609 dfResSize = 0.0000001;
1610
1611 dfXMin = sExtent.MinX - dfResSize * 3;
1612 dfXMax = sExtent.MaxX + dfResSize * 3;
1613 dfXRes = dfResSize;
1614 ParseDIMINFO( "DIMINFO_X", &dfXMin, &dfXMax, &dfXRes );
1615
1616 dfYMin = sExtent.MinY - dfResSize * 3;
1617 dfYMax = sExtent.MaxY + dfResSize * 3;
1618 dfYRes = dfResSize;
1619 ParseDIMINFO( "DIMINFO_Y", &dfYMin, &dfYMax, &dfYRes );
1620
1621 dfZMin = -100000.0;
1622 dfZMax = 100000.0;
1623 dfZRes = 0.002;
1624 ParseDIMINFO( "DIMINFO_Z", &dfZMin, &dfZMax, &dfZRes );
1625
1626 /* -------------------------------------------------------------------- */
1627 /* If we already have an extent in the table, we will need to */
1628 /* update it in place. */
1629 /* -------------------------------------------------------------------- */
1630 OGROCIStringBuf sDimUpdate;
1631
1632 if( bHaveOldExtent )
1633 {
1634 sDimUpdate.Append( "UPDATE USER_SDO_GEOM_METADATA " );
1635 sDimUpdate.Append( "SET DIMINFO =" );
1636 sDimUpdate.Append( "MDSYS.SDO_DIM_ARRAY(" );
1637 sDimUpdate.Appendf(200,
1638 "MDSYS.SDO_DIM_ELEMENT('X',%.16g,%.16g,%.12g)",
1639 dfXMin, dfXMax, dfXRes );
1640 sDimUpdate.Appendf(200,
1641 ",MDSYS.SDO_DIM_ELEMENT('Y',%.16g,%.16g,%.12g)",
1642 dfYMin, dfYMax, dfYRes );
1643
1644 if( nDimension == 3 )
1645 {
1646 sDimUpdate.Appendf(200,
1647 ",MDSYS.SDO_DIM_ELEMENT('Z',%.16g,%.16g,%.12g)",
1648 dfZMin, dfZMax, dfZRes );
1649 }
1650
1651 sDimUpdate.Appendf(static_cast<int>(strlen(poFeatureDefn->GetName()) + 100),
1652 ") WHERE TABLE_NAME = '%s'", poFeatureDefn->GetName());
1653 }
1654 else
1655 {
1656 /* -------------------------------------------------------------------- */
1657 /* Prepare dimension update statement. */
1658 /* -------------------------------------------------------------------- */
1659 sDimUpdate.Append( "INSERT INTO USER_SDO_GEOM_METADATA VALUES " );
1660 sDimUpdate.Appendf( static_cast<int>(strlen(poFeatureDefn->GetName()) + 100),
1661 "('%s', '%s', ",
1662 poFeatureDefn->GetName(),
1663 pszGeomName );
1664
1665 sDimUpdate.Append( "MDSYS.SDO_DIM_ARRAY(" );
1666 sDimUpdate.Appendf(200,
1667 "MDSYS.SDO_DIM_ELEMENT('X',%.16g,%.16g,%.12g)",
1668 dfXMin, dfXMax, dfXRes );
1669 sDimUpdate.Appendf(200,
1670 ",MDSYS.SDO_DIM_ELEMENT('Y',%.16g,%.16g,%.12g)",
1671 dfYMin, dfYMax, dfYRes );
1672
1673 if( nDimension == 3 )
1674 {
1675 sDimUpdate.Appendf(200,
1676 ",MDSYS.SDO_DIM_ELEMENT('Z',%.16g,%.16g,%.12g)",
1677 dfZMin, dfZMax, dfZRes );
1678 }
1679
1680 if( nSRID == -1 )
1681 sDimUpdate.Append( "), NULL)" );
1682 else
1683 sDimUpdate.Appendf( 100, "), %d)", nSRID );
1684 }
1685
1686 /* -------------------------------------------------------------------- */
1687 /* Run the update/insert command. */
1688 /* -------------------------------------------------------------------- */
1689 OGROCIStatement oExecStatement( poDS->GetSession() );
1690
1691 oExecStatement.Execute( sDimUpdate.GetString() );
1692 }
1693
1694 /************************************************************************/
1695 /* AllocAndBindForWrite() */
1696 /************************************************************************/
1697
AllocAndBindForWrite()1698 int OGROCITableLayer::AllocAndBindForWrite()
1699
1700 {
1701 OGROCISession *poSession = poDS->GetSession();
1702 int i;
1703
1704 CPLAssert( nWriteCacheMax == 0 );
1705
1706 /* -------------------------------------------------------------------- */
1707 /* Decide on the number of rows we want to be able to cache at */
1708 /* a time. */
1709 /* -------------------------------------------------------------------- */
1710 nWriteCacheMax = nMultiLoadCount;
1711
1712 /* -------------------------------------------------------------------- */
1713 /* Collect the INSERT statement. */
1714 /* -------------------------------------------------------------------- */
1715 OGROCIStringBuf oCmdBuf;
1716
1717 oCmdBuf.Append( "INSERT /*+ APPEND */ INTO \"" );
1718 oCmdBuf.Append( poFeatureDefn->GetName() );
1719 oCmdBuf.Append( "\"(\"" );
1720 oCmdBuf.Append( pszFIDName );
1721
1722 if (GetGeomType() != wkbNone)
1723 {
1724 oCmdBuf.Append( "\",\"" );
1725 oCmdBuf.Append( pszGeomName );
1726 }
1727
1728 for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
1729 {
1730 oCmdBuf.Append( "\",\"" );
1731 oCmdBuf.Append( poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
1732 }
1733
1734 oCmdBuf.Append( "\") VALUES ( :fid " );
1735
1736 if (GetGeomType() != wkbNone)
1737 oCmdBuf.Append( ", :geometry" );
1738
1739 for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
1740 {
1741 oCmdBuf.Append( ", " );
1742 oCmdBuf.Appendf( 20, " :field_%d", i );
1743 }
1744
1745 oCmdBuf.Append( ") " );
1746
1747 /* -------------------------------------------------------------------- */
1748 /* Bind and Prepare it. */
1749 /* -------------------------------------------------------------------- */
1750 poBoundStatement = new OGROCIStatement( poSession );
1751 poBoundStatement->Prepare( oCmdBuf.GetString() );
1752
1753 /* -------------------------------------------------------------------- */
1754 /* Setup geometry indicator information. */
1755 /* -------------------------------------------------------------------- */
1756 if (GetGeomType() != wkbNone)
1757 {
1758 pasWriteGeomInd = (SDO_GEOMETRY_ind *)
1759 CPLCalloc(sizeof(SDO_GEOMETRY_ind),nWriteCacheMax);
1760
1761 papsWriteGeomIndMap = (SDO_GEOMETRY_ind **)
1762 CPLCalloc(sizeof(SDO_GEOMETRY_ind *),nWriteCacheMax);
1763
1764 for( i = 0; i < nWriteCacheMax; i++ )
1765 papsWriteGeomIndMap[i] = pasWriteGeomInd + i;
1766
1767 /* -------------------------------------------------------------------- */
1768 /* Setup all the required geometry objects, and the */
1769 /* corresponding indicator map. */
1770 /* -------------------------------------------------------------------- */
1771 pasWriteGeoms = (SDO_GEOMETRY_TYPE *)
1772 CPLCalloc( sizeof(SDO_GEOMETRY_TYPE), nWriteCacheMax);
1773 papsWriteGeomMap = (SDO_GEOMETRY_TYPE **)
1774 CPLCalloc( sizeof(SDO_GEOMETRY_TYPE *), nWriteCacheMax );
1775
1776 for( i = 0; i < nWriteCacheMax; i++ )
1777 papsWriteGeomMap[i] = pasWriteGeoms + i;
1778
1779 /* -------------------------------------------------------------------- */
1780 /* Allocate VARRAYs for the elem_info and ordinates. */
1781 /* -------------------------------------------------------------------- */
1782 for( i = 0; i < nWriteCacheMax; i++ )
1783 {
1784 if( poSession->Failed(
1785 OCIObjectNew( poSession->hEnv, poSession->hError,
1786 poSession->hSvcCtx, OCI_TYPECODE_VARRAY,
1787 poSession->hElemInfoTDO, (dvoid *)nullptr,
1788 OCI_DURATION_SESSION,
1789 FALSE,
1790 (dvoid **) &(pasWriteGeoms[i].sdo_elem_info)),
1791 "OCIObjectNew(elem_info)") )
1792 return FALSE;
1793
1794 if( poSession->Failed(
1795 OCIObjectNew( poSession->hEnv, poSession->hError,
1796 poSession->hSvcCtx, OCI_TYPECODE_VARRAY,
1797 poSession->hOrdinatesTDO, (dvoid *)nullptr,
1798 OCI_DURATION_SESSION,
1799 FALSE,
1800 (dvoid **) &(pasWriteGeoms[i].sdo_ordinates)),
1801 "OCIObjectNew(ordinates)") )
1802 return FALSE;
1803 }
1804
1805 /* -------------------------------------------------------------------- */
1806 /* Bind the geometry column. */
1807 /* -------------------------------------------------------------------- */
1808 if( poBoundStatement->BindObject(
1809 ":geometry", papsWriteGeomMap, poSession->hGeometryTDO,
1810 (void**) papsWriteGeomIndMap) != CE_None )
1811 return FALSE;
1812 }
1813
1814 /* -------------------------------------------------------------------- */
1815 /* Bind the FID column. */
1816 /* -------------------------------------------------------------------- */
1817 panWriteFIDs = (int *) CPLMalloc(sizeof(int) * nWriteCacheMax );
1818
1819 if( poBoundStatement->BindScalar( ":fid", panWriteFIDs, sizeof(int),
1820 SQLT_INT ) != CE_None )
1821 return FALSE;
1822
1823 /* -------------------------------------------------------------------- */
1824 /* Allocate each of the column data bind arrays. */
1825 /* -------------------------------------------------------------------- */
1826
1827 papWriteFields = (void **)
1828 CPLMalloc(sizeof(void*) * poFeatureDefn->GetFieldCount() );
1829 papaeWriteFieldInd = (OCIInd **)
1830 CPLCalloc(sizeof(OCIInd*),poFeatureDefn->GetFieldCount() );
1831
1832 for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
1833 {
1834 OGRFieldDefn *poFldDefn = poFeatureDefn->GetFieldDefn(i);
1835 char szFieldPlaceholderName[80];
1836
1837 snprintf( szFieldPlaceholderName, sizeof(szFieldPlaceholderName), ":field_%d", i );
1838
1839 papaeWriteFieldInd[i] = (OCIInd *)
1840 CPLCalloc(sizeof(OCIInd), nWriteCacheMax );
1841
1842 if( poFldDefn->GetType() == OFTInteger )
1843 {
1844 papWriteFields[i] =
1845 (void *) CPLCalloc( sizeof(int), nWriteCacheMax );
1846
1847 if( poBoundStatement->BindScalar(
1848 szFieldPlaceholderName, papWriteFields[i],
1849 sizeof(int), SQLT_INT, papaeWriteFieldInd[i] ) != CE_None )
1850 return FALSE;
1851 }
1852 else if( poFldDefn->GetType() == OFTInteger64 )
1853 {
1854 papWriteFields[i] =
1855 (void *) CPLCalloc( sizeof(GIntBig), nWriteCacheMax );
1856
1857 if( poBoundStatement->BindScalar(
1858 szFieldPlaceholderName, papWriteFields[i],
1859 sizeof(GIntBig), SQLT_INT, papaeWriteFieldInd[i] ) != CE_None )
1860 return FALSE;
1861 }
1862 else if( poFldDefn->GetType() == OFTReal )
1863 {
1864 papWriteFields[i] = (void *) CPLCalloc( sizeof(double),
1865 nWriteCacheMax );
1866
1867 if( poBoundStatement->BindScalar(
1868 szFieldPlaceholderName, papWriteFields[i],
1869 sizeof(double), SQLT_FLT, papaeWriteFieldInd[i] ) != CE_None )
1870 return FALSE;
1871 }
1872 else
1873 {
1874 int nEachBufSize = nDefaultStringSize + 1;
1875
1876 if( poFldDefn->GetType() == OFTString
1877 && poFldDefn->GetWidth() != 0 )
1878 nEachBufSize = poFldDefn->GetWidth() + 1;
1879
1880 papWriteFields[i] =
1881 (void *) CPLCalloc( nEachBufSize, nWriteCacheMax );
1882
1883 if( poBoundStatement->BindScalar(
1884 szFieldPlaceholderName, papWriteFields[i],
1885 nEachBufSize, SQLT_STR, papaeWriteFieldInd[i]) != CE_None )
1886 return FALSE;
1887 }
1888 }
1889
1890 return TRUE;
1891 }
1892
1893 /************************************************************************/
1894 /* BoundCreateFeature() */
1895 /************************************************************************/
1896
BoundCreateFeature(OGRFeature * poFeature)1897 OGRErr OGROCITableLayer::BoundCreateFeature( OGRFeature *poFeature )
1898
1899 {
1900 OGROCISession *poSession = poDS->GetSession();
1901 int iCache, i;
1902 OGRErr eErr;
1903 OCINumber oci_number;
1904
1905 /* If an unset field has a default value, the current implementation */
1906 /* of BoundCreateFeature() doesn't work. */
1907 for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
1908 {
1909 if( !poFeature->IsFieldSetAndNotNull( i ) &&
1910 poFeature->GetFieldDefnRef(i)->GetDefault() != nullptr )
1911 {
1912 FlushPendingFeatures();
1913 return UnboundCreateFeature(poFeature);
1914 }
1915 }
1916
1917 if( !poFeature->Validate( OGR_F_VAL_NULL | OGR_F_VAL_ALLOW_NULL_WHEN_DEFAULT, TRUE ) )
1918 return OGRERR_FAILURE;
1919
1920 iCache = nWriteCacheUsed;
1921
1922 /* -------------------------------------------------------------------- */
1923 /* Initiate the Insert */
1924 /* -------------------------------------------------------------------- */
1925 if( nWriteCacheMax == 0 )
1926 {
1927 if( !AllocAndBindForWrite() )
1928 return OGRERR_FAILURE;
1929 }
1930
1931 /* -------------------------------------------------------------------- */
1932 /* Set the geometry */
1933 /* -------------------------------------------------------------------- */
1934 if( poFeature->GetGeometryRef() != nullptr )
1935 {
1936 SDO_GEOMETRY_TYPE *psGeom = pasWriteGeoms + iCache;
1937 SDO_GEOMETRY_ind *psInd = pasWriteGeomInd + iCache;
1938 OGRGeometry *poGeometry = poFeature->GetGeometryRef();
1939 int nGType;
1940
1941 psInd->_atomic = OCI_IND_NOTNULL;
1942
1943 if( nSRID == -1 )
1944 psInd->sdo_srid = OCI_IND_NULL;
1945 else
1946 {
1947 psInd->sdo_srid = OCI_IND_NOTNULL;
1948 OCINumberFromInt( poSession->hError, &nSRID,
1949 (uword)sizeof(int), OCI_NUMBER_SIGNED,
1950 &(psGeom->sdo_srid) );
1951 }
1952
1953 /* special more efficient case for simple points */
1954 if( wkbFlatten(poGeometry->getGeometryType()) == wkbPoint )
1955 {
1956 OGRPoint *poPoint = poGeometry->toPoint();
1957 double dfValue;
1958
1959 psInd->sdo_point._atomic = OCI_IND_NOTNULL;
1960 psInd->sdo_elem_info = OCI_IND_NULL;
1961 psInd->sdo_ordinates = OCI_IND_NULL;
1962
1963 dfValue = poPoint->getX();
1964 OCINumberFromReal( poSession->hError, &dfValue,
1965 (uword)sizeof(double),
1966 &(psGeom->sdo_point.x) );
1967
1968 dfValue = poPoint->getY();
1969 OCINumberFromReal( poSession->hError, &dfValue,
1970 (uword)sizeof(double),
1971 &(psGeom->sdo_point.y) );
1972
1973 if( nDimension == 2 )
1974 {
1975 nGType = 2001;
1976 psInd->sdo_point.z = OCI_IND_NULL;
1977 }
1978 else
1979 {
1980 nGType = 3001;
1981 psInd->sdo_point.z = OCI_IND_NOTNULL;
1982
1983 dfValue = poPoint->getZ();
1984 OCINumberFromReal( poSession->hError, &dfValue,
1985 (uword)sizeof(double),
1986 &(psGeom->sdo_point.z) );
1987 }
1988 }
1989 else
1990 {
1991 psInd->sdo_point._atomic = OCI_IND_NULL;
1992 psInd->sdo_elem_info = OCI_IND_NOTNULL;
1993 psInd->sdo_ordinates = OCI_IND_NOTNULL;
1994
1995 eErr = TranslateToSDOGeometry( poFeature->GetGeometryRef(),
1996 &nGType );
1997
1998 if( eErr != OGRERR_NONE )
1999 return eErr;
2000
2001 /* Clear the existing eleminfo and ordinates arrays */
2002 sb4 nOldCount;
2003
2004 OCICollSize( poSession->hEnv, poSession->hError,
2005 psGeom->sdo_elem_info, &nOldCount );
2006 OCICollTrim( poSession->hEnv, poSession->hError,
2007 nOldCount, psGeom->sdo_elem_info );
2008
2009 OCICollSize( poSession->hEnv, poSession->hError,
2010 psGeom->sdo_ordinates, &nOldCount );
2011 OCICollTrim( poSession->hEnv, poSession->hError,
2012 nOldCount, psGeom->sdo_ordinates );
2013
2014 // Prepare the VARRAY of element values.
2015 for (i = 0; i < nElemInfoCount; i++)
2016 {
2017 OCINumberFromInt( poSession->hError,
2018 (dvoid *) (panElemInfo + i),
2019 (uword)sizeof(int), OCI_NUMBER_SIGNED,
2020 &oci_number );
2021
2022 OCICollAppend( poSession->hEnv, poSession->hError,
2023 (dvoid *) &oci_number,
2024 (dvoid *)nullptr, psGeom->sdo_elem_info );
2025 }
2026
2027 // Prepare the VARRAY of ordinate values.
2028 for (i = 0; i < nOrdinalCount; i++)
2029 {
2030 OCINumberFromReal( poSession->hError,
2031 (dvoid *) (padfOrdinals + i),
2032 (uword)sizeof(double), &oci_number );
2033 OCICollAppend( poSession->hEnv, poSession->hError,
2034 (dvoid *) &oci_number,
2035 (dvoid *)nullptr, psGeom->sdo_ordinates );
2036 }
2037 }
2038
2039 psInd->sdo_gtype = OCI_IND_NOTNULL;
2040 OCINumberFromInt( poSession->hError, &nGType,
2041 (uword)sizeof(int), OCI_NUMBER_SIGNED,
2042 &(psGeom->sdo_gtype) );
2043 }
2044 else if( pasWriteGeomInd != nullptr )
2045 {
2046 SDO_GEOMETRY_ind *psInd = pasWriteGeomInd + iCache;
2047 psInd->_atomic = OCI_IND_NULL;
2048 psInd->sdo_srid = OCI_IND_NULL;
2049 psInd->sdo_point._atomic = OCI_IND_NULL;
2050 psInd->sdo_elem_info = OCI_IND_NULL;
2051 psInd->sdo_ordinates = OCI_IND_NULL;
2052 psInd->sdo_gtype = OCI_IND_NULL;
2053 }
2054
2055 /* -------------------------------------------------------------------- */
2056 /* Set the FID. */
2057 /* -------------------------------------------------------------------- */
2058 if( poFeature->GetFID() == OGRNullFID )
2059 {
2060 if( iNextFIDToWrite < 0 )
2061 {
2062 iNextFIDToWrite = GetMaxFID() + 1;
2063 }
2064
2065 poFeature->SetFID( iNextFIDToWrite++ );
2066 }
2067
2068 panWriteFIDs[iCache] = static_cast<int>(poFeature->GetFID());
2069
2070 /* -------------------------------------------------------------------- */
2071 /* Set the other fields. */
2072 /* -------------------------------------------------------------------- */
2073 for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
2074 {
2075 if( !poFeature->IsFieldSetAndNotNull( i ) )
2076 {
2077 papaeWriteFieldInd[i][iCache] = OCI_IND_NULL;
2078 continue;
2079 }
2080
2081 papaeWriteFieldInd[i][iCache] = OCI_IND_NOTNULL;
2082
2083 OGRFieldDefn *poFldDefn = poFeatureDefn->GetFieldDefn(i);
2084
2085 if( poFldDefn->GetType() == OFTInteger )
2086 ((int *) (papWriteFields[i]))[iCache] =
2087 poFeature->GetFieldAsInteger( i );
2088
2089 else if( poFldDefn->GetType() == OFTInteger64 )
2090 ((GIntBig *) (papWriteFields[i]))[iCache] =
2091 poFeature->GetFieldAsInteger64( i );
2092
2093 else if( poFldDefn->GetType() == OFTReal )
2094 ((double *) (papWriteFields[i]))[iCache] =
2095 poFeature->GetFieldAsDouble( i );
2096
2097 else
2098 {
2099 int nLen = 1;
2100 int nEachBufSize = nDefaultStringSize + 1;
2101 const char *pszStrValue = poFeature->GetFieldAsString(i);
2102
2103 if( poFldDefn->GetType() == OFTString
2104 && poFldDefn->GetWidth() != 0 )
2105 nEachBufSize = poFldDefn->GetWidth() + 1;
2106
2107 nLen = static_cast<int>(strlen(pszStrValue));
2108 if( nLen > nEachBufSize-1 )
2109 nLen = nEachBufSize-1;
2110
2111 char *pszTarget = ((char*)papWriteFields[i]) + iCache*nEachBufSize;
2112 strncpy( pszTarget, pszStrValue, nLen );
2113 pszTarget[nLen] = '\0';
2114 }
2115 }
2116
2117 /* -------------------------------------------------------------------- */
2118 /* Do we need to flush out a full set of rows? */
2119 /* -------------------------------------------------------------------- */
2120 nWriteCacheUsed++;
2121
2122 if( nWriteCacheUsed == nWriteCacheMax )
2123 return FlushPendingFeatures();
2124 else
2125 return OGRERR_NONE;
2126 }
2127
2128 /************************************************************************/
2129 /* FlushPendingFeatures() */
2130 /************************************************************************/
2131
FlushPendingFeatures()2132 OGRErr OGROCITableLayer::FlushPendingFeatures()
2133
2134 {
2135 OGROCISession *poSession = poDS->GetSession();
2136
2137 if( nWriteCacheUsed > 0 )
2138 {
2139 CPLDebug( "OCI", "Flushing %d features on layer %s",
2140 nWriteCacheUsed, poFeatureDefn->GetName() );
2141
2142 if( poSession->Failed(
2143 OCIStmtExecute( poSession->hSvcCtx,
2144 poBoundStatement->GetStatement(),
2145 poSession->hError, (ub4) nWriteCacheUsed,
2146 (ub4) 0,
2147 (OCISnapshot *)nullptr, (OCISnapshot *)nullptr,
2148 (ub4) OCI_COMMIT_ON_SUCCESS ),
2149 "OCIStmtExecute" ) )
2150 {
2151 nWriteCacheUsed = 0;
2152 return OGRERR_FAILURE;
2153 }
2154 else
2155 {
2156 nWriteCacheUsed = 0;
2157 return OGRERR_NONE;
2158 }
2159 }
2160 else
2161 return OGRERR_NONE;
2162 }
2163
2164 /************************************************************************/
2165 /* SyncToDisk() */
2166 /* */
2167 /* Perhaps we should also be putting the metadata into a */
2168 /* usable state? */
2169 /************************************************************************/
2170
SyncToDisk()2171 OGRErr OGROCITableLayer::SyncToDisk()
2172
2173 {
2174 OGRErr eErr = FlushPendingFeatures();
2175
2176 UpdateLayerExtents();
2177
2178 CreateSpatialIndex();
2179
2180 bNewLayer = FALSE;
2181
2182 return eErr;
2183 }
2184
2185 /*************************************************************************/
2186 /* CreateSpatialIndex() */
2187 /*************************************************************************/
2188
CreateSpatialIndex()2189 void OGROCITableLayer::CreateSpatialIndex()
2190
2191 {
2192 /* -------------------------------------------------------------------- */
2193 /* For new layers we try to create a spatial index. */
2194 /* -------------------------------------------------------------------- */
2195 if( bNewLayer && sExtent.IsInit() )
2196 {
2197 /* -------------------------------------------------------------------- */
2198 /* If the user has disabled INDEX support then don't create the */
2199 /* index. */
2200 /* -------------------------------------------------------------------- */
2201 if( !CPLFetchBool( papszOptions, "SPATIAL_INDEX", true ) ||
2202 !CPLFetchBool( papszOptions, "INDEX", true ) )
2203 return;
2204
2205 /* -------------------------------------------------------------------- */
2206 /* Establish an index name. For some reason Oracle 8.1.7 does */
2207 /* not support spatial index names longer than 18 characters so */
2208 /* we magic up an index name if it would be too long. */
2209 /* -------------------------------------------------------------------- */
2210 char szIndexName[20];
2211
2212 if( strlen(poFeatureDefn->GetName()) < 15 )
2213 snprintf( szIndexName, sizeof(szIndexName), "%s_idx", poFeatureDefn->GetName() );
2214 else if( strlen(poFeatureDefn->GetName()) < 17 )
2215 snprintf( szIndexName, sizeof(szIndexName), "%si", poFeatureDefn->GetName() );
2216 else
2217 {
2218 int i, nHash = 0;
2219 const char *pszSrcName = poFeatureDefn->GetName();
2220
2221 for( i = 0; pszSrcName[i] != '\0'; i++ )
2222 nHash = (nHash + i * pszSrcName[i]) % 987651;
2223
2224 snprintf( szIndexName, sizeof(szIndexName), "OSI_%d", nHash );
2225 }
2226
2227 poDS->GetSession()->CleanName( szIndexName );
2228
2229 /* -------------------------------------------------------------------- */
2230 /* Try creating an index on the table now. Use a simple 5 */
2231 /* level quadtree based index. Would R-tree be a better default? */
2232 /* -------------------------------------------------------------------- */
2233 OGROCIStringBuf sIndexCmd;
2234 OGROCIStatement oExecStatement( poDS->GetSession() );
2235
2236 sIndexCmd.Appendf( 10000, "CREATE INDEX \"%s\" ON %s(\"%s\") "
2237 "INDEXTYPE IS MDSYS.SPATIAL_INDEX ",
2238 szIndexName,
2239 poFeatureDefn->GetName(),
2240 pszGeomName );
2241
2242 int bAddLayerGType = CPLTestBool(
2243 CSLFetchNameValueDef( papszOptions, "ADD_LAYER_GTYPE", "YES") ) &&
2244 GetGeomType() != wkbUnknown;
2245
2246 CPLString osParams(CSLFetchNameValueDef(papszOptions,"INDEX_PARAMETERS", ""));
2247 if( bAddLayerGType || !osParams.empty() )
2248 {
2249 sIndexCmd.Append( " PARAMETERS( '" );
2250 if( !osParams.empty() )
2251 sIndexCmd.Append( osParams.c_str() );
2252 if( bAddLayerGType &&
2253 osParams.ifind("LAYER_GTYPE") == std::string::npos )
2254 {
2255 if( !osParams.empty() )
2256 sIndexCmd.Append( ", " );
2257 sIndexCmd.Append( "LAYER_GTYPE=" );
2258 if( wkbFlatten(GetGeomType()) == wkbPoint )
2259 sIndexCmd.Append( "POINT" );
2260 else if( wkbFlatten(GetGeomType()) == wkbLineString )
2261 sIndexCmd.Append( "LINE" );
2262 else if( wkbFlatten(GetGeomType()) == wkbPolygon )
2263 sIndexCmd.Append( "POLYGON" );
2264 else if( wkbFlatten(GetGeomType()) == wkbMultiPoint )
2265 sIndexCmd.Append( "MULTIPOINT" );
2266 else if( wkbFlatten(GetGeomType()) == wkbMultiLineString )
2267 sIndexCmd.Append( "MULTILINE" );
2268 else if( wkbFlatten(GetGeomType()) == wkbMultiPolygon )
2269 sIndexCmd.Append( "MULTIPOLYGON" );
2270 else
2271 sIndexCmd.Append( "COLLECTION" );
2272 }
2273 sIndexCmd.Append( "' )" );
2274 }
2275
2276 if( oExecStatement.Execute( sIndexCmd.GetString() ) != CE_None )
2277 {
2278 CPLString osDropCommand;
2279 osDropCommand.Printf( "DROP INDEX \"%s\"", szIndexName );
2280 oExecStatement.Execute( osDropCommand );
2281 }
2282 }
2283 }
2284
GetMaxFID()2285 int OGROCITableLayer::GetMaxFID()
2286 {
2287 if( nFirstId > 0 )
2288 return nFirstId - 1;
2289
2290 if( pszFIDName == nullptr )
2291 return 0;
2292
2293 OGROCIStringBuf sCmd;
2294 OGROCIStatement oSelect( poDS->GetSession() );
2295
2296 sCmd.Appendf( 10000, "SELECT MAX(\"%s\") FROM \"%s\"",
2297 pszFIDName,
2298 poFeatureDefn->GetName()
2299 );
2300
2301 oSelect.Execute( sCmd.GetString() );
2302
2303 char **papszResult = oSelect.SimpleFetchRow();
2304 return CSLCount(papszResult) == 1 ? atoi( papszResult[0] ) : 0;
2305 }
2306