1 : /******************************************************************************
2 : * $Id: ogrpglayer.cpp 17987 2009-11-10 15:39:16Z rouault $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Implements OGRPGLayer class which implements shared handling
6 : * of feature geometry and so forth needed by OGRPGResultLayer and
7 : * OGRPGTableLayer.
8 : * Author: Frank Warmerdam, warmerdam@pobox.com
9 : *
10 : ******************************************************************************
11 : * Copyright (c) 2000, Frank Warmerdam
12 : *
13 : * Permission is hereby granted, free of charge, to any person obtaining a
14 : * copy of this software and associated documentation files (the "Software"),
15 : * to deal in the Software without restriction, including without limitation
16 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 : * and/or sell copies of the Software, and to permit persons to whom the
18 : * Software is furnished to do so, subject to the following conditions:
19 : *
20 : * The above copyright notice and this permission notice shall be included
21 : * in all copies or substantial portions of the Software.
22 : *
23 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 : * DEALINGS IN THE SOFTWARE.
30 : ****************************************************************************/
31 :
32 : /* Some functions have been extracted from PostgreSQL code base */
33 : /* The applicable copyright & licence notice is the following one : */
34 : /*
35 : PostgreSQL Database Management System
36 : (formerly known as Postgres, then as Postgres95)
37 :
38 : Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
39 :
40 : Portions Copyright (c) 1994, The Regents of the University of California
41 :
42 : Permission to use, copy, modify, and distribute this software and its
43 : documentation for any purpose, without fee, and without a written agreement
44 : is hereby granted, provided that the above copyright notice and this
45 : paragraph and the following two paragraphs appear in all copies.
46 :
47 : IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
48 : DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
49 : LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
50 : DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
51 : POSSIBILITY OF SUCH DAMAGE.
52 :
53 : THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
54 : INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
55 : AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
56 : ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
57 : PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
58 : */
59 :
60 : #include "ogr_pg.h"
61 : #include "ogrpgutility.h"
62 : #include "ogr_p.h"
63 : #include "cpl_conv.h"
64 : #include "cpl_string.h"
65 :
66 : CPL_CVSID("$Id: ogrpglayer.cpp 17987 2009-11-10 15:39:16Z rouault $");
67 :
68 : #define CURSOR_PAGE 500
69 :
70 : // These originally are defined in libpq-fs.h.
71 :
72 : #ifndef INV_WRITE
73 : #define INV_WRITE 0x00020000
74 : #define INV_READ 0x00040000
75 : #endif
76 :
77 : /* Flags for creating WKB format for PostGIS */
78 : #define WKBZOFFSET 0x80000000
79 : #define WKBMOFFSET 0x40000000
80 : #define WKBSRIDFLAG 0x20000000
81 : #define WKBBBOXFLAG 0x10000000
82 :
83 : /************************************************************************/
84 : /* OGRPGLayer() */
85 : /************************************************************************/
86 :
87 441 : OGRPGLayer::OGRPGLayer()
88 :
89 : {
90 441 : poDS = NULL;
91 :
92 441 : bHasWkb = FALSE;
93 441 : bWkbAsOid = FALSE;
94 441 : bHasPostGISGeometry = FALSE;
95 441 : bHasPostGISGeography = FALSE;
96 441 : pszGeomColumn = NULL;
97 441 : pszQueryStatement = NULL;
98 :
99 441 : bHasFid = FALSE;
100 441 : pszFIDColumn = NULL;
101 :
102 441 : iNextShapeId = 0;
103 441 : nResultOffset = 0;
104 :
105 441 : nCoordDimension = 2; // initialize in case PostGIS is not available
106 :
107 441 : poSRS = NULL;
108 441 : nSRSId = -2; // we haven't even queried the database for it yet.
109 :
110 441 : pszCursorName = CPLStrdup(CPLSPrintf("OGRPGLayerReader%p", this));
111 :
112 441 : hCursorResult = NULL;
113 441 : bCursorActive = FALSE;
114 :
115 441 : bCanUseBinaryCursor = TRUE;
116 :
117 441 : poFeatureDefn = NULL;
118 441 : panMapFieldNameToIndex = NULL;
119 441 : }
120 :
121 : /************************************************************************/
122 : /* ~OGRPGLayer() */
123 : /************************************************************************/
124 :
125 441 : OGRPGLayer::~OGRPGLayer()
126 :
127 : {
128 441 : if( m_nFeaturesRead > 0 && poFeatureDefn != NULL )
129 : {
130 : CPLDebug( "PG", "%d features read on layer '%s'.",
131 : (int) m_nFeaturesRead,
132 43 : poFeatureDefn->GetName() );
133 : }
134 :
135 441 : ResetReading();
136 :
137 441 : CPLFree( pszGeomColumn );
138 441 : CPLFree( pszFIDColumn );
139 441 : CPLFree( pszQueryStatement );
140 441 : CPLFree( panMapFieldNameToIndex );
141 441 : CPLFree( pszCursorName );
142 :
143 441 : if( poSRS != NULL )
144 0 : poSRS->Release();
145 :
146 441 : if( poFeatureDefn )
147 438 : poFeatureDefn->Release();
148 441 : }
149 :
150 : /************************************************************************/
151 : /* ResetReading() */
152 : /************************************************************************/
153 :
154 947 : void OGRPGLayer::ResetReading()
155 :
156 : {
157 947 : PGconn *hPGConn = poDS->GetPGConn();
158 947 : CPLString osCommand;
159 :
160 947 : iNextShapeId = 0;
161 :
162 947 : if( hCursorResult != NULL )
163 : {
164 74 : OGRPGClearResult( hCursorResult );
165 :
166 74 : if( bCursorActive )
167 : {
168 74 : osCommand.Printf("CLOSE %s", pszCursorName );
169 :
170 74 : hCursorResult = PQexec(hPGConn, osCommand.c_str());
171 74 : OGRPGClearResult( hCursorResult );
172 : }
173 :
174 74 : poDS->FlushSoftTransaction();
175 :
176 74 : hCursorResult = NULL;
177 947 : }
178 947 : }
179 :
180 : /************************************************************************/
181 : /* OGRPGGetStrFromBinaryNumeric() */
182 : /************************************************************************/
183 :
184 : /* Adaptation of get_str_from_var() from pgsql/src/backend/utils/adt/numeric.c */
185 :
186 : typedef short NumericDigit;
187 :
188 : typedef struct NumericVar
189 : {
190 : int ndigits; /* # of digits in digits[] - can be 0! */
191 : int weight; /* weight of first digit */
192 : int sign; /* NUMERIC_POS, NUMERIC_NEG, or NUMERIC_NAN */
193 : int dscale; /* display scale */
194 : NumericDigit *digits; /* base-NBASE digits */
195 : } NumericVar;
196 :
197 : #define NUMERIC_POS 0x0000
198 : #define NUMERIC_NEG 0x4000
199 : #define NUMERIC_NAN 0xC000
200 :
201 : #define DEC_DIGITS 4
202 : /*
203 : * get_str_from_var() -
204 : *
205 : * Convert a var to text representation (guts of numeric_out).
206 : * CAUTION: var's contents may be modified by rounding!
207 : * Returns a palloc'd string.
208 : */
209 : static char *
210 4 : OGRPGGetStrFromBinaryNumeric(NumericVar *var)
211 : {
212 : char *str;
213 : char *cp;
214 : char *endcp;
215 : int i;
216 : int d;
217 : NumericDigit dig;
218 : NumericDigit d1;
219 :
220 4 : int dscale = var->dscale;
221 :
222 : /*
223 : * Allocate space for the result.
224 : *
225 : * i is set to to # of decimal digits before decimal point. dscale is the
226 : * # of decimal digits we will print after decimal point. We may generate
227 : * as many as DEC_DIGITS-1 excess digits at the end, and in addition we
228 : * need room for sign, decimal point, null terminator.
229 : */
230 4 : i = (var->weight + 1) * DEC_DIGITS;
231 4 : if (i <= 0)
232 2 : i = 1;
233 :
234 4 : str = (char*)CPLMalloc(i + dscale + DEC_DIGITS + 2);
235 4 : cp = str;
236 :
237 : /*
238 : * Output a dash for negative values
239 : */
240 4 : if (var->sign == NUMERIC_NEG)
241 0 : *cp++ = '-';
242 :
243 : /*
244 : * Output all digits before the decimal point
245 : */
246 4 : if (var->weight < 0)
247 : {
248 2 : d = var->weight + 1;
249 2 : *cp++ = '0';
250 : }
251 : else
252 : {
253 6 : for (d = 0; d <= var->weight; d++)
254 : {
255 4 : dig = (d < var->ndigits) ? var->digits[d] : 0;
256 4 : CPL_MSBPTR16(&dig);
257 : /* In the first digit, suppress extra leading decimal zeroes */
258 : {
259 4 : bool putit = (d > 0);
260 :
261 4 : d1 = dig / 1000;
262 4 : dig -= d1 * 1000;
263 4 : putit |= (d1 > 0);
264 4 : if (putit)
265 2 : *cp++ = d1 + '0';
266 4 : d1 = dig / 100;
267 4 : dig -= d1 * 100;
268 4 : putit |= (d1 > 0);
269 4 : if (putit)
270 2 : *cp++ = d1 + '0';
271 4 : d1 = dig / 10;
272 4 : dig -= d1 * 10;
273 4 : putit |= (d1 > 0);
274 4 : if (putit)
275 2 : *cp++ = d1 + '0';
276 4 : *cp++ = dig + '0';
277 : }
278 : }
279 : }
280 :
281 : /*
282 : * If requested, output a decimal point and all the digits that follow it.
283 : * We initially put out a multiple of DEC_DIGITS digits, then truncate if
284 : * needed.
285 : */
286 4 : if (dscale > 0)
287 : {
288 2 : *cp++ = '.';
289 2 : endcp = cp + dscale;
290 4 : for (i = 0; i < dscale; d++, i += DEC_DIGITS)
291 : {
292 2 : dig = (d >= 0 && d < var->ndigits) ? var->digits[d] : 0;
293 2 : CPL_MSBPTR16(&dig);
294 2 : d1 = dig / 1000;
295 2 : dig -= d1 * 1000;
296 2 : *cp++ = d1 + '0';
297 2 : d1 = dig / 100;
298 2 : dig -= d1 * 100;
299 2 : *cp++ = d1 + '0';
300 2 : d1 = dig / 10;
301 2 : dig -= d1 * 10;
302 2 : *cp++ = d1 + '0';
303 2 : *cp++ = dig + '0';
304 : }
305 2 : cp = endcp;
306 : }
307 :
308 : /*
309 : * terminate the string and return it
310 : */
311 4 : *cp = '\0';
312 4 : return str;
313 : }
314 :
315 : /************************************************************************/
316 : /* OGRPGj2date() */
317 : /************************************************************************/
318 :
319 : /* Coming from j2date() in pgsql/src/backend/utils/adt/datetime.c */
320 :
321 : #define POSTGRES_EPOCH_JDATE 2451545 /* == date2j(2000, 1, 1) */
322 :
323 : static
324 2 : void OGRPGj2date(int jd, int *year, int *month, int *day)
325 : {
326 : unsigned int julian;
327 : unsigned int quad;
328 : unsigned int extra;
329 : int y;
330 :
331 2 : julian = jd;
332 2 : julian += 32044;
333 2 : quad = julian / 146097;
334 2 : extra = (julian - quad * 146097) * 4 + 3;
335 2 : julian += 60 + quad * 3 + extra / 146097;
336 2 : quad = julian / 1461;
337 2 : julian -= quad * 1461;
338 2 : y = julian * 4 / 1461;
339 : julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366))
340 2 : + 123;
341 2 : y += quad * 4;
342 2 : *year = y - 4800;
343 2 : quad = julian * 2141 / 65536;
344 2 : *day = julian - 7834 * quad / 256;
345 2 : *month = (quad + 10) % 12 + 1;
346 :
347 : return;
348 : } /* j2date() */
349 :
350 :
351 : /************************************************************************/
352 : /* OGRPGdt2time() */
353 : /************************************************************************/
354 :
355 : #define USECS_PER_SEC 1000000
356 : #define USECS_PER_MIN ((GIntBig) 60 * USECS_PER_SEC)
357 : #define USECS_PER_HOUR ((GIntBig) 3600 * USECS_PER_SEC)
358 : #define USECS_PER_DAY ((GIntBig) 3600 * 24 * USECS_PER_SEC)
359 :
360 : /* Coming from dt2time() in pgsql/src/backend/utils/adt/timestamp.c */
361 :
362 : static
363 : void
364 2 : OGRPGdt2timeInt8(GIntBig jd, int *hour, int *min, int *sec, double *fsec)
365 : {
366 : GIntBig time;
367 :
368 2 : time = jd;
369 :
370 2 : *hour = (int) (time / USECS_PER_HOUR);
371 2 : time -= (GIntBig) (*hour) * USECS_PER_HOUR;
372 2 : *min = (int) (time / USECS_PER_MIN);
373 2 : time -= (GIntBig) (*min) * USECS_PER_MIN;
374 2 : *sec = (int)time / USECS_PER_SEC;
375 2 : *fsec = time - *sec * USECS_PER_SEC;
376 2 : } /* dt2time() */
377 :
378 : static
379 : void
380 0 : OGRPGdt2timeFloat8(double jd, int *hour, int *min, int *sec, double *fsec)
381 : {
382 : double time;
383 :
384 0 : time = jd;
385 :
386 0 : *hour = (int) (time / 3600.);
387 0 : time -= (*hour) * 3600.;
388 0 : *min = (int) (time / 60.);
389 0 : time -= (*min) * 60.;
390 0 : *sec = (int)time;
391 0 : *fsec = time - *sec;
392 0 : }
393 :
394 : /************************************************************************/
395 : /* OGRPGTimeStamp2DMYHMS() */
396 : /************************************************************************/
397 :
398 : #define TMODULO(t,q,u) \
399 : do { \
400 : (q) = ((t) / (u)); \
401 : if ((q) != 0) (t) -= ((q) * (u)); \
402 : } while(0)
403 :
404 : /* Coming from timestamp2tm() in pgsql/src/backend/utils/adt/timestamp.c */
405 :
406 : static
407 0 : int OGRPGTimeStamp2DMYHMS(GIntBig dt, int *year, int *month, int *day,
408 : int* hour, int* min, int* sec)
409 : {
410 : GIntBig date;
411 : GIntBig time;
412 : double fsec;
413 :
414 0 : time = dt;
415 0 : TMODULO(time, date, USECS_PER_DAY);
416 :
417 0 : if (time < 0)
418 : {
419 0 : time += USECS_PER_DAY;
420 0 : date -= 1;
421 : }
422 :
423 : /* add offset to go from J2000 back to standard Julian date */
424 0 : date += POSTGRES_EPOCH_JDATE;
425 :
426 : /* Julian day routine does not work for negative Julian days */
427 0 : if (date < 0 || date > (double) INT_MAX)
428 0 : return -1;
429 :
430 0 : OGRPGj2date((int) date, year, month, day);
431 0 : OGRPGdt2timeInt8(time, hour, min, sec, &fsec);
432 :
433 0 : return 0;
434 : }
435 :
436 :
437 : /************************************************************************/
438 : /* TokenizeStringListFromText() */
439 : /* */
440 : /* Tokenize a varchar[] returned as a text */
441 : /************************************************************************/
442 :
443 84 : static void OGRPGTokenizeStringListUnescapeToken(char* pszToken)
444 : {
445 84 : if (EQUAL(pszToken, "NULL"))
446 : {
447 0 : pszToken[0] = '\0';
448 0 : return;
449 : }
450 :
451 84 : int iSrc = 0, iDst = 0;
452 224 : for(iSrc = 0; pszToken[iSrc] != '\0'; iSrc++)
453 : {
454 140 : pszToken[iDst] = pszToken[iSrc];
455 140 : if (pszToken[iSrc] != '\\')
456 140 : iDst ++;
457 : }
458 84 : pszToken[iDst] = '\0';
459 : }
460 :
461 : /* {"a\",b",d,NULL,e} should be tokenized into 3 pieces : a",b d empty_string e */
462 42 : static char ** OGRPGTokenizeStringListFromText(const char* pszText)
463 : {
464 42 : char** papszTokens = NULL;
465 42 : const char* pszCur = strchr(pszText, '{');
466 42 : if (pszCur == NULL)
467 : {
468 0 : CPLError(CE_Warning, CPLE_AppDefined, "Incorrect string list : %s", pszText);
469 0 : return papszTokens;
470 : }
471 :
472 42 : const char* pszNewTokenStart = NULL;
473 42 : int bInDoubleQuotes = FALSE;
474 42 : pszCur ++;
475 266 : while(*pszCur)
476 : {
477 224 : if (*pszCur == '\\')
478 : {
479 0 : pszCur ++;
480 0 : if (*pszCur == 0)
481 0 : break;
482 0 : pszCur ++;
483 0 : continue;
484 : }
485 :
486 224 : if (*pszCur == '"')
487 : {
488 0 : bInDoubleQuotes = !bInDoubleQuotes;
489 0 : if (bInDoubleQuotes)
490 0 : pszNewTokenStart = pszCur + 1;
491 : else
492 : {
493 0 : if (pszCur[1] == ',' || pszCur[1] == '}')
494 : {
495 0 : if (pszNewTokenStart != NULL && pszCur > pszNewTokenStart)
496 : {
497 0 : char* pszNewToken = (char*) CPLMalloc(pszCur - pszNewTokenStart + 1);
498 0 : memcpy(pszNewToken, pszNewTokenStart, pszCur - pszNewTokenStart);
499 0 : pszNewToken[pszCur - pszNewTokenStart] = 0;
500 0 : OGRPGTokenizeStringListUnescapeToken(pszNewToken);
501 0 : papszTokens = CSLAddString(papszTokens, pszNewToken);
502 0 : CPLFree(pszNewToken);
503 : }
504 0 : pszNewTokenStart = NULL;
505 0 : if (pszCur[1] == ',')
506 0 : pszCur ++;
507 : else
508 0 : return papszTokens;
509 : }
510 : else
511 : {
512 : /* error */
513 0 : break;
514 : }
515 : }
516 : }
517 224 : if (!bInDoubleQuotes)
518 : {
519 224 : if (*pszCur == '{')
520 : {
521 : /* error */
522 0 : break;
523 : }
524 224 : else if (*pszCur == '}')
525 : {
526 42 : if (pszNewTokenStart != NULL && pszCur > pszNewTokenStart)
527 : {
528 42 : char* pszNewToken = (char*) CPLMalloc(pszCur - pszNewTokenStart + 1);
529 42 : memcpy(pszNewToken, pszNewTokenStart, pszCur - pszNewTokenStart);
530 42 : pszNewToken[pszCur - pszNewTokenStart] = 0;
531 42 : OGRPGTokenizeStringListUnescapeToken(pszNewToken);
532 42 : papszTokens = CSLAddString(papszTokens, pszNewToken);
533 42 : CPLFree(pszNewToken);
534 : }
535 42 : return papszTokens;
536 : }
537 182 : else if (*pszCur == ',')
538 : {
539 42 : if (pszNewTokenStart != NULL && pszCur > pszNewTokenStart)
540 : {
541 42 : char* pszNewToken = (char*) CPLMalloc(pszCur - pszNewTokenStart + 1);
542 42 : memcpy(pszNewToken, pszNewTokenStart, pszCur - pszNewTokenStart);
543 42 : pszNewToken[pszCur - pszNewTokenStart] = 0;
544 42 : OGRPGTokenizeStringListUnescapeToken(pszNewToken);
545 42 : papszTokens = CSLAddString(papszTokens, pszNewToken);
546 42 : CPLFree(pszNewToken);
547 : }
548 42 : pszNewTokenStart = pszCur + 1;
549 : }
550 140 : else if (pszNewTokenStart == NULL)
551 42 : pszNewTokenStart = pszCur;
552 : }
553 182 : pszCur++;
554 : }
555 :
556 0 : CPLError(CE_Warning, CPLE_AppDefined, "Incorrect string list : %s", pszText);
557 0 : return papszTokens;
558 : }
559 :
560 : /************************************************************************/
561 : /* RecordToFeature() */
562 : /* */
563 : /* Convert the indicated record of the current result set into */
564 : /* a feature. */
565 : /************************************************************************/
566 :
567 2254 : OGRFeature *OGRPGLayer::RecordToFeature( int iRecord )
568 :
569 : {
570 : /* -------------------------------------------------------------------- */
571 : /* Create a feature from the current result. */
572 : /* -------------------------------------------------------------------- */
573 : int iField;
574 2254 : OGRFeature *poFeature = new OGRFeature( poFeatureDefn );
575 :
576 2254 : poFeature->SetFID( iNextShapeId );
577 2254 : m_nFeaturesRead++;
578 :
579 : /* ==================================================================== */
580 : /* Transfer all result fields we can. */
581 : /* ==================================================================== */
582 9871 : for( iField = 0;
583 : iField < PQnfields(hCursorResult);
584 : iField++ )
585 : {
586 : int iOGRField;
587 :
588 : #if !defined(PG_PRE74)
589 7617 : int nTypeOID = PQftype(hCursorResult, iField);
590 : #endif
591 :
592 : /* -------------------------------------------------------------------- */
593 : /* Handle FID. */
594 : /* -------------------------------------------------------------------- */
595 7617 : if( bHasFid && EQUAL(PQfname(hCursorResult,iField),pszFIDColumn) )
596 : {
597 : #if !defined(PG_PRE74)
598 2216 : if ( PQfformat( hCursorResult, iField ) == 1 ) // Binary data representation
599 : {
600 2 : if ( nTypeOID == INT4OID)
601 : {
602 : int nVal;
603 : CPLAssert(PQgetlength(hCursorResult, iRecord, iField) == sizeof(int));
604 2 : memcpy( &nVal, PQgetvalue( hCursorResult, iRecord, iField ), sizeof(int) );
605 2 : CPL_MSBPTR32(&nVal);
606 2 : poFeature->SetFID( nVal );
607 : }
608 : else
609 : {
610 0 : CPLDebug("PG", "FID. Unhandled OID %d.", nTypeOID );
611 0 : continue;
612 : }
613 : }
614 : else
615 : #endif /* notdef PG_PRE74 */
616 : {
617 2214 : char* pabyData = PQgetvalue(hCursorResult,iRecord,iField);
618 : /* ogr_pg_20 may crash if PostGIS is unavailable and we don't test pabyData */
619 2214 : if (pabyData)
620 2214 : poFeature->SetFID( atoi(pabyData) );
621 : else
622 0 : continue;
623 : }
624 : }
625 :
626 : /* -------------------------------------------------------------------- */
627 : /* Handle PostGIS geometry */
628 : /* -------------------------------------------------------------------- */
629 :
630 10162 : if( bHasPostGISGeometry || bHasPostGISGeography )
631 : {
632 3638 : if ( EQUAL(PQfname(hCursorResult,iField),"ST_AsBinary") ||
633 : EQUAL(PQfname(hCursorResult,iField),"AsBinary") )
634 : {
635 : GByte * pabyWKB = (GByte*) PQgetvalue( hCursorResult,
636 0 : iRecord, iField);
637 :
638 0 : int nLength = PQgetlength(hCursorResult, iRecord, iField);
639 :
640 : /* No geometry */
641 0 : if (nLength == 0)
642 0 : continue;
643 :
644 0 : OGRGeometry * poGeom = NULL;
645 0 : if( !poDS->bUseBinaryCursor && nLength >= 4 &&
646 : (EQUALN((const char*)pabyWKB,"\\000",4) || EQUALN((const char*)pabyWKB,"\\001",4)) )
647 : {
648 0 : const char* pszBYTEA = (const char*)pabyWKB;
649 0 : pabyWKB = BYTEAToGByteArray(pszBYTEA, &nLength);
650 0 : OGRGeometryFactory::createFromWkb( pabyWKB, NULL, &poGeom, nLength );
651 0 : CPLFree(pabyWKB);
652 : }
653 : else
654 0 : OGRGeometryFactory::createFromWkb( pabyWKB, NULL, &poGeom, nLength );
655 :
656 0 : if( poGeom != NULL )
657 : {
658 0 : poGeom->assignSpatialReference( poSRS );
659 0 : poFeature->SetGeometryDirectly( poGeom );
660 : }
661 :
662 0 : continue;
663 : }
664 3638 : else if ( poDS->bUseBinaryCursor &&
665 : (EQUAL(PQfname(hCursorResult,iField),pszGeomColumn) ||
666 : EQUAL(PQfname(hCursorResult,iField),"AsEWKB")) )
667 : {
668 : /* Handle HEX result or EWKB binary cursor result */
669 : char * pabyData = PQgetvalue( hCursorResult,
670 2 : iRecord, iField);
671 :
672 2 : int nLength = PQgetlength(hCursorResult, iRecord, iField);
673 :
674 : /* No geometry */
675 2 : if (nLength == 0)
676 0 : continue;
677 :
678 : OGRGeometry * poGeom;
679 3 : if( nLength >= 2 && (EQUALN(pabyData,"00",2) || EQUALN(pabyData,"01",2)) )
680 : {
681 1 : poGeom = HEXToGeometry(pabyData);
682 : }
683 : else
684 : {
685 1 : poGeom = EWKBToGeometry((GByte*)pabyData, nLength);
686 : }
687 :
688 2 : if( poGeom != NULL )
689 : {
690 2 : poGeom->assignSpatialReference( poSRS );
691 2 : poFeature->SetGeometryDirectly( poGeom );
692 : }
693 :
694 2 : continue;
695 : }
696 3636 : else if (EQUAL(PQfname(hCursorResult,iField),pszGeomColumn) ||
697 : EQUAL(PQfname(hCursorResult,iField),"asEWKT") ||
698 : EQUAL(PQfname(hCursorResult,iField),"asText") ||
699 : EQUAL(PQfname(hCursorResult,iField),"ST_AsText") )
700 : {
701 : /* Handle WKT */
702 : char *pszWKT;
703 : char *pszPostSRID;
704 1091 : OGRGeometry *poGeometry = NULL;
705 :
706 1091 : pszWKT = PQgetvalue( hCursorResult, iRecord, iField );
707 1091 : pszPostSRID = pszWKT;
708 :
709 : // optionally strip off PostGIS SRID identifier. This
710 : // happens if we got a raw geometry field.
711 1091 : if( EQUALN(pszPostSRID,"SRID=",5) )
712 : {
713 0 : while( *pszPostSRID != '\0' && *pszPostSRID != ';' )
714 0 : pszPostSRID++;
715 0 : if( *pszPostSRID == ';' )
716 0 : pszPostSRID++;
717 : }
718 :
719 1102 : if( EQUALN(pszPostSRID,"00",2) || EQUALN(pszPostSRID,"01",2) )
720 : {
721 : poGeometry =
722 : HEXToGeometry(
723 11 : PQgetvalue( hCursorResult, iRecord, iField ) );
724 : }
725 : else
726 : OGRGeometryFactory::createFromWkt( &pszPostSRID, NULL,
727 1080 : &poGeometry );
728 1091 : if( poGeometry != NULL )
729 : {
730 74 : poGeometry->assignSpatialReference( poSRS );
731 74 : poFeature->SetGeometryDirectly( poGeometry );
732 : }
733 :
734 1091 : continue;
735 : }
736 : }
737 : /* -------------------------------------------------------------------- */
738 : /* Handle raw binary geometry ... this hasn't been tested in a */
739 : /* while. */
740 : /* -------------------------------------------------------------------- */
741 3979 : else if( EQUAL(PQfname(hCursorResult,iField),"WKB_GEOMETRY") )
742 : {
743 1135 : OGRGeometry *poGeometry = NULL;
744 1135 : char * pabyData = PQgetvalue( hCursorResult, iRecord, iField);
745 :
746 1135 : if( bWkbAsOid )
747 : {
748 : poGeometry =
749 0 : OIDToGeometry( (Oid) atoi(pabyData) );
750 : }
751 : else
752 : {
753 1135 : if (poDS->bUseBinaryCursor
754 : #if !defined(PG_PRE74)
755 : && PQfformat( hCursorResult, iField ) == 1
756 : #endif
757 : )
758 : {
759 1 : int nLength = PQgetlength(hCursorResult, iRecord, iField);
760 1 : poGeometry = EWKBToGeometry((GByte*)pabyData, nLength);
761 : }
762 1135 : if (poGeometry == NULL)
763 : {
764 1134 : poGeometry = BYTEAToGeometry( pabyData );
765 : }
766 : }
767 :
768 1135 : if( poGeometry != NULL )
769 : {
770 125 : poGeometry->assignSpatialReference( poSRS );
771 125 : poFeature->SetGeometryDirectly( poGeometry );
772 : }
773 :
774 1135 : continue;
775 : }
776 :
777 : /* -------------------------------------------------------------------- */
778 : /* Transfer regular data fields. */
779 : /* -------------------------------------------------------------------- */
780 5389 : iOGRField = panMapFieldNameToIndex[iField];
781 :
782 5389 : if( iOGRField < 0 )
783 2216 : continue;
784 :
785 3173 : if( PQgetisnull( hCursorResult, iRecord, iField ) )
786 304 : continue;
787 :
788 : OGRFieldType eOGRType =
789 2869 : poFeatureDefn->GetFieldDefn(iOGRField)->GetType();
790 :
791 2869 : if( eOGRType == OFTIntegerList)
792 : {
793 : int *panList, nCount, i;
794 :
795 : #if !defined(PG_PRE74)
796 16 : if ( PQfformat( hCursorResult, iField ) == 1 ) // Binary data representation
797 : {
798 2 : if (nTypeOID == INT4ARRAYOID)
799 : {
800 2 : char * pData = PQgetvalue( hCursorResult, iRecord, iField );
801 :
802 : // goto number of array elements
803 2 : pData += 3 * sizeof(int);
804 2 : memcpy( &nCount, pData, sizeof(int) );
805 2 : CPL_MSBPTR32( &nCount );
806 :
807 2 : panList = (int *) CPLCalloc(sizeof(int),nCount);
808 :
809 : // goto first array element
810 2 : pData += 2 * sizeof(int);
811 :
812 6 : for( i = 0; i < nCount; i++ )
813 : {
814 : // get element size
815 4 : int nSize = *(int *)(pData);
816 4 : CPL_MSBPTR32( &nSize );
817 :
818 : CPLAssert( nSize == sizeof(int) );
819 :
820 4 : pData += sizeof(int);
821 :
822 4 : memcpy( &panList[i], pData, nSize );
823 4 : CPL_MSBPTR32(&panList[i]);
824 :
825 4 : pData += nSize;
826 : }
827 : }
828 : else
829 : {
830 0 : CPLDebug("PG", "Field %d: Incompatible OID (%d) with OFTIntegerList.", iOGRField, nTypeOID );
831 0 : continue;
832 : }
833 : }
834 : else
835 : #endif /* notdef PG_PRE74 */
836 : {
837 : char **papszTokens;
838 : papszTokens = CSLTokenizeStringComplex(
839 : PQgetvalue( hCursorResult, iRecord, iField ),
840 14 : "{,}", FALSE, FALSE );
841 :
842 14 : nCount = CSLCount(papszTokens);
843 14 : panList = (int *) CPLCalloc(sizeof(int),nCount);
844 :
845 42 : for( i = 0; i < nCount; i++ )
846 28 : panList[i] = atoi(papszTokens[i]);
847 14 : CSLDestroy( papszTokens );
848 : }
849 16 : poFeature->SetField( iOGRField, nCount, panList );
850 16 : CPLFree( panList );
851 : }
852 :
853 2853 : else if( eOGRType == OFTRealList )
854 : {
855 : int nCount, i;
856 : double *padfList;
857 :
858 : #if !defined(PG_PRE74)
859 32 : if ( PQfformat( hCursorResult, iField ) == 1 ) // Binary data representation
860 : {
861 4 : if (nTypeOID == FLOAT8ARRAYOID || nTypeOID == FLOAT4ARRAYOID)
862 : {
863 4 : char * pData = PQgetvalue( hCursorResult, iRecord, iField );
864 :
865 : // goto number of array elements
866 4 : pData += 3 * sizeof(int);
867 4 : memcpy( &nCount, pData, sizeof(int) );
868 4 : CPL_MSBPTR32( &nCount );
869 :
870 4 : padfList = (double *) CPLCalloc(sizeof(double),nCount);
871 :
872 : // goto first array element
873 4 : pData += 2 * sizeof(int);
874 :
875 12 : for( i = 0; i < nCount; i++ )
876 : {
877 : // get element size
878 8 : int nSize = *(int *)(pData);
879 8 : CPL_MSBPTR32( &nSize );
880 :
881 8 : pData += sizeof(int);
882 :
883 8 : if (nTypeOID == FLOAT8ARRAYOID)
884 : {
885 : CPLAssert( nSize == sizeof(double) );
886 :
887 4 : memcpy( &padfList[i], pData, nSize );
888 4 : CPL_MSBPTR64(&padfList[i]);
889 : }
890 : else
891 : {
892 : float fVal;
893 : CPLAssert( nSize == sizeof(float) );
894 :
895 4 : memcpy( &fVal, pData, nSize );
896 4 : CPL_MSBPTR32(&fVal);
897 :
898 4 : padfList[i] = fVal;
899 : }
900 :
901 8 : pData += nSize;
902 : }
903 : }
904 : else
905 : {
906 0 : CPLDebug("PG", "Field %d: Incompatible OID (%d) with OFTRealList.", iOGRField, nTypeOID );
907 0 : continue;
908 : }
909 : }
910 : else
911 : #endif /* notdef PG_PRE74 */
912 : {
913 : char **papszTokens;
914 : papszTokens = CSLTokenizeStringComplex(
915 : PQgetvalue( hCursorResult, iRecord, iField ),
916 28 : "{,}", FALSE, FALSE );
917 :
918 28 : nCount = CSLCount(papszTokens);
919 28 : padfList = (double *) CPLCalloc(sizeof(double),nCount);
920 :
921 84 : for( i = 0; i < nCount; i++ )
922 56 : padfList[i] = atof(papszTokens[i]);
923 28 : CSLDestroy( papszTokens );
924 : }
925 :
926 32 : poFeature->SetField( iOGRField, nCount, padfList );
927 32 : CPLFree( padfList );
928 : }
929 :
930 2821 : else if( eOGRType == OFTStringList )
931 : {
932 48 : char **papszTokens = 0;
933 :
934 : #if !defined(PG_PRE74)
935 48 : if ( PQfformat( hCursorResult, iField ) == 1 ) // Binary data representation
936 : {
937 6 : char * pData = PQgetvalue( hCursorResult, iRecord, iField );
938 : int nCount, i;
939 :
940 : // goto number of array elements
941 6 : pData += 3 * sizeof(int);
942 6 : memcpy( &nCount, pData, sizeof(int) );
943 6 : CPL_MSBPTR32( &nCount );
944 :
945 : // goto first array element
946 6 : pData += 2 * sizeof(int);
947 :
948 18 : for( i = 0; i < nCount; i++ )
949 : {
950 : // get element size
951 12 : int nSize = *(int *)(pData);
952 12 : CPL_MSBPTR32( &nSize );
953 :
954 12 : pData += sizeof(int);
955 :
956 12 : if (nSize <= 0)
957 0 : papszTokens = CSLAddString(papszTokens, "");
958 : else
959 : {
960 12 : if (pData[nSize] == '\0')
961 12 : papszTokens = CSLAddString(papszTokens, pData);
962 : else
963 : {
964 0 : char* pszToken = (char*) CPLMalloc(nSize + 1);
965 0 : memcpy(pszToken, pData, nSize);
966 0 : pszToken[nSize] = '\0';
967 0 : papszTokens = CSLAddString(papszTokens, pszToken);
968 0 : CPLFree(pszToken);
969 : }
970 :
971 12 : pData += nSize;
972 : }
973 : }
974 : }
975 : else
976 : #endif /* notdef PG_PRE74 */
977 : {
978 : papszTokens =
979 42 : OGRPGTokenizeStringListFromText(PQgetvalue(hCursorResult, iRecord, iField ));
980 : }
981 :
982 48 : if ( papszTokens )
983 : {
984 48 : poFeature->SetField( iOGRField, papszTokens );
985 48 : CSLDestroy( papszTokens );
986 : }
987 : }
988 :
989 2879 : else if( eOGRType == OFTDate
990 : || eOGRType == OFTTime
991 : || eOGRType == OFTDateTime )
992 : {
993 : #if !defined(PG_PRE74)
994 106 : if ( PQfformat( hCursorResult, iField ) == 1 ) // Binary data
995 : {
996 8 : if ( nTypeOID == DATEOID )
997 : {
998 : int nVal, nYear, nMonth, nDay;
999 : CPLAssert(PQgetlength(hCursorResult, iRecord, iField) == sizeof(int));
1000 2 : memcpy( &nVal, PQgetvalue( hCursorResult, iRecord, iField ), sizeof(int) );
1001 2 : CPL_MSBPTR32(&nVal);
1002 2 : OGRPGj2date(nVal + POSTGRES_EPOCH_JDATE, &nYear, &nMonth, &nDay);
1003 2 : poFeature->SetField( iOGRField, nYear, nMonth, nDay);
1004 : }
1005 6 : else if ( nTypeOID == TIMEOID )
1006 : {
1007 : int nHour, nMinute, nSecond;
1008 : char szTime[32];
1009 : double dfsec;
1010 : CPLAssert(PQgetlength(hCursorResult, iRecord, iField) == 8);
1011 2 : if (poDS->bBinaryTimeFormatIsInt8)
1012 : {
1013 : unsigned int nVal[2];
1014 : GIntBig llVal;
1015 2 : memcpy( nVal, PQgetvalue( hCursorResult, iRecord, iField ), 8 );
1016 2 : CPL_MSBPTR32(&nVal[0]);
1017 2 : CPL_MSBPTR32(&nVal[1]);
1018 2 : llVal = (GIntBig) ((((GUIntBig)nVal[0]) << 32) | nVal[1]);
1019 2 : OGRPGdt2timeInt8(llVal, &nHour, &nMinute, &nSecond, &dfsec);
1020 : }
1021 : else
1022 : {
1023 : double dfVal;
1024 0 : memcpy( &dfVal, PQgetvalue( hCursorResult, iRecord, iField ), 8 );
1025 0 : CPL_MSBPTR64(&dfVal);
1026 0 : OGRPGdt2timeFloat8(dfVal, &nHour, &nMinute, &nSecond, &dfsec);
1027 : }
1028 2 : sprintf(szTime, "%02d:%02d:%02d", nHour, nMinute, nSecond);
1029 2 : poFeature->SetField( iOGRField, szTime);
1030 : }
1031 4 : else if ( nTypeOID == TIMESTAMPOID || nTypeOID == TIMESTAMPTZOID )
1032 : {
1033 : unsigned int nVal[2];
1034 : GIntBig llVal;
1035 : int nYear, nMonth, nDay, nHour, nMinute, nSecond;
1036 : CPLAssert(PQgetlength(hCursorResult, iRecord, iField) == 8);
1037 0 : memcpy( nVal, PQgetvalue( hCursorResult, iRecord, iField ), 8 );
1038 0 : CPL_MSBPTR32(&nVal[0]);
1039 0 : CPL_MSBPTR32(&nVal[1]);
1040 0 : llVal = (GIntBig) ((((GUIntBig)nVal[0]) << 32) | nVal[1]);
1041 0 : if (OGRPGTimeStamp2DMYHMS(llVal, &nYear, &nMonth, &nDay, &nHour, &nMinute, &nSecond) == 0)
1042 0 : poFeature->SetField( iOGRField, nYear, nMonth, nDay, nHour, nMinute, nSecond);
1043 : }
1044 4 : else if ( nTypeOID == TEXTOID )
1045 : {
1046 : OGRField sFieldValue;
1047 :
1048 4 : if( OGRParseDate( PQgetvalue( hCursorResult, iRecord, iField ),
1049 : &sFieldValue, 0 ) )
1050 : {
1051 4 : poFeature->SetField( iOGRField, &sFieldValue );
1052 : }
1053 : }
1054 : else
1055 : {
1056 0 : CPLDebug( "PG", "Binary DATE format not yet implemented. OID = %d", nTypeOID );
1057 : }
1058 : }
1059 : else
1060 : #endif /* notdef PG_PRE74 */
1061 : {
1062 : OGRField sFieldValue;
1063 :
1064 98 : if( OGRParseDate( PQgetvalue( hCursorResult, iRecord, iField ),
1065 : &sFieldValue, 0 ) )
1066 : {
1067 98 : poFeature->SetField( iOGRField, &sFieldValue );
1068 : }
1069 : }
1070 : }
1071 2667 : else if( eOGRType == OFTBinary )
1072 : {
1073 : #if !defined(PG_PRE74)
1074 16 : if ( PQfformat( hCursorResult, iField ) == 1)
1075 : {
1076 2 : int nLength = PQgetlength(hCursorResult, iRecord, iField);
1077 2 : GByte* pabyData = (GByte*) PQgetvalue( hCursorResult, iRecord, iField );
1078 2 : poFeature->SetField( iOGRField, nLength, pabyData );
1079 : }
1080 : else
1081 : #endif /* notdef PG_PRE74 */
1082 : {
1083 14 : int nLength = PQgetlength(hCursorResult, iRecord, iField);
1084 14 : const char* pszBytea = (const char*) PQgetvalue( hCursorResult, iRecord, iField );
1085 14 : GByte* pabyData = BYTEAToGByteArray( pszBytea, &nLength );
1086 14 : poFeature->SetField( iOGRField, nLength, pabyData );
1087 14 : CPLFree(pabyData);
1088 : }
1089 : }
1090 : else
1091 : {
1092 : #if !defined(PG_PRE74)
1093 2651 : if ( PQfformat( hCursorResult, iField ) == 1 &&
1094 : eOGRType != OFTString ) // Binary data
1095 : {
1096 18 : if ( nTypeOID == BOOLOID )
1097 : {
1098 : char cVal;
1099 : CPLAssert(PQgetlength(hCursorResult, iRecord, iField) == sizeof(char));
1100 2 : cVal = *PQgetvalue( hCursorResult, iRecord, iField );
1101 2 : poFeature->SetField( iOGRField, cVal );
1102 : }
1103 16 : else if ( nTypeOID == NUMERICOID )
1104 : {
1105 : unsigned short sLen, sSign, sDscale;
1106 : short sWeight;
1107 4 : char* pabyData = PQgetvalue( hCursorResult, iRecord, iField );
1108 4 : memcpy( &sLen, pabyData, sizeof(short));
1109 4 : pabyData += sizeof(short);
1110 4 : CPL_MSBPTR16(&sLen);
1111 4 : memcpy( &sWeight, pabyData, sizeof(short));
1112 4 : pabyData += sizeof(short);
1113 4 : CPL_MSBPTR16(&sWeight);
1114 4 : memcpy( &sSign, pabyData, sizeof(short));
1115 4 : pabyData += sizeof(short);
1116 4 : CPL_MSBPTR16(&sSign);
1117 4 : memcpy( &sDscale, pabyData, sizeof(short));
1118 4 : pabyData += sizeof(short);
1119 4 : CPL_MSBPTR16(&sDscale);
1120 : CPLAssert(PQgetlength(hCursorResult, iRecord, iField) == (int)((4 + sLen) * sizeof(short)));
1121 :
1122 : NumericVar var;
1123 4 : var.ndigits = sLen;
1124 4 : var.weight = sWeight;
1125 4 : var.sign = sSign;
1126 4 : var.dscale = sDscale;
1127 4 : var.digits = (NumericDigit*)pabyData;
1128 4 : char* str = OGRPGGetStrFromBinaryNumeric(&var);
1129 4 : poFeature->SetField( iOGRField, str);
1130 4 : CPLFree(str);
1131 : }
1132 12 : else if ( nTypeOID == INT2OID )
1133 : {
1134 : short sVal;
1135 : CPLAssert(PQgetlength(hCursorResult, iRecord, iField) == sizeof(short));
1136 2 : memcpy( &sVal, PQgetvalue( hCursorResult, iRecord, iField ), sizeof(short) );
1137 2 : CPL_MSBPTR16(&sVal);
1138 2 : poFeature->SetField( iOGRField, sVal );
1139 : }
1140 10 : else if ( nTypeOID == INT4OID )
1141 : {
1142 : int nVal;
1143 : CPLAssert(PQgetlength(hCursorResult, iRecord, iField) == sizeof(int));
1144 2 : memcpy( &nVal, PQgetvalue( hCursorResult, iRecord, iField ), sizeof(int) );
1145 2 : CPL_MSBPTR32(&nVal);
1146 2 : poFeature->SetField( iOGRField, nVal );
1147 : }
1148 8 : else if ( nTypeOID == INT8OID )
1149 : {
1150 : unsigned int nVal[2];
1151 : GIntBig llVal;
1152 : CPLAssert(PQgetlength(hCursorResult, iRecord, iField) == 8);
1153 2 : memcpy( nVal, PQgetvalue( hCursorResult, iRecord, iField ), 8 );
1154 2 : CPL_MSBPTR32(&nVal[0]);
1155 2 : CPL_MSBPTR32(&nVal[1]);
1156 2 : llVal = (GIntBig) ((((GUIntBig)nVal[0]) << 32) | nVal[1]);
1157 : /* FIXME : 64bit -> 32bit conversion */
1158 2 : poFeature->SetField( iOGRField, (int)llVal );
1159 : }
1160 6 : else if ( nTypeOID == FLOAT4OID )
1161 : {
1162 : float fVal;
1163 : CPLAssert(PQgetlength(hCursorResult, iRecord, iField) == sizeof(float));
1164 4 : memcpy( &fVal, PQgetvalue( hCursorResult, iRecord, iField ), sizeof(float) );
1165 4 : CPL_MSBPTR32(&fVal);
1166 4 : poFeature->SetField( iOGRField, fVal );
1167 : }
1168 2 : else if ( nTypeOID == FLOAT8OID )
1169 : {
1170 : double dfVal;
1171 : CPLAssert(PQgetlength(hCursorResult, iRecord, iField) == sizeof(double));
1172 2 : memcpy( &dfVal, PQgetvalue( hCursorResult, iRecord, iField ), sizeof(double) );
1173 2 : CPL_MSBPTR64(&dfVal);
1174 2 : poFeature->SetField( iOGRField, dfVal );
1175 : }
1176 : else
1177 : {
1178 : CPLDebug("PG", "Field %d: Incompatible OID (%d) with %s.", iOGRField, nTypeOID,
1179 0 : OGRFieldDefn::GetFieldTypeName( eOGRType ));
1180 0 : continue;
1181 : }
1182 : }
1183 : else
1184 : #endif /* notdef PG_PRE74 */
1185 : {
1186 2633 : if ( eOGRType == OFTInteger &&
1187 : poFeatureDefn->GetFieldDefn(iOGRField)->GetWidth() == 1)
1188 : {
1189 14 : char* pabyData = PQgetvalue( hCursorResult, iRecord, iField );
1190 14 : if (EQUALN(pabyData, "T", 1))
1191 10 : poFeature->SetField( iOGRField, 1);
1192 4 : else if (EQUALN(pabyData, "F", 1))
1193 0 : poFeature->SetField( iOGRField, 0);
1194 : else
1195 4 : poFeature->SetField( iOGRField, pabyData);
1196 : }
1197 : else
1198 : {
1199 : poFeature->SetField( iOGRField,
1200 2619 : PQgetvalue( hCursorResult, iRecord, iField ) );
1201 : }
1202 : }
1203 : }
1204 : }
1205 :
1206 2254 : return poFeature;
1207 : }
1208 :
1209 : /************************************************************************/
1210 : /* CreateMapFromFieldNameToIndex() */
1211 : /************************************************************************/
1212 :
1213 : /* Evaluating GetFieldIndex() on each field of each feature can be very */
1214 : /* expensive if the layer has many fields (total complexity of O(n^2) where */
1215 : /* n is the number of fields), so it is valuable to compute the map from */
1216 : /* the fetched fields to the OGR field index */
1217 113 : void OGRPGLayer::CreateMapFromFieldNameToIndex()
1218 : {
1219 113 : CPLFree(panMapFieldNameToIndex);
1220 113 : panMapFieldNameToIndex = NULL;
1221 113 : if ( PQresultStatus(hCursorResult) == PGRES_TUPLES_OK )
1222 : {
1223 : panMapFieldNameToIndex =
1224 113 : (int*)CPLMalloc(sizeof(int) * PQnfields(hCursorResult));
1225 952 : for( int iField = 0;
1226 : iField < PQnfields(hCursorResult);
1227 : iField++ )
1228 : {
1229 839 : panMapFieldNameToIndex[iField] =
1230 839 : poFeatureDefn->GetFieldIndex(PQfname(hCursorResult,iField));
1231 : }
1232 : }
1233 113 : }
1234 :
1235 :
1236 : /************************************************************************/
1237 : /* SetInitialQueryCursor() */
1238 : /************************************************************************/
1239 :
1240 100 : void OGRPGLayer::SetInitialQueryCursor()
1241 : {
1242 100 : PGconn *hPGConn = poDS->GetPGConn();
1243 100 : CPLString osCommand;
1244 :
1245 : CPLAssert( pszQueryStatement != NULL );
1246 :
1247 100 : poDS->FlushSoftTransaction();
1248 100 : poDS->SoftStartTransaction();
1249 :
1250 102 : if ( poDS->bUseBinaryCursor && bCanUseBinaryCursor )
1251 : osCommand.Printf( "DECLARE %s BINARY CURSOR for %s",
1252 2 : pszCursorName, pszQueryStatement );
1253 : else
1254 : osCommand.Printf( "DECLARE %s CURSOR for %s",
1255 98 : pszCursorName, pszQueryStatement );
1256 :
1257 100 : hCursorResult = PQexec(hPGConn, osCommand );
1258 100 : OGRPGClearResult( hCursorResult );
1259 :
1260 100 : osCommand.Printf( "FETCH %d in %s", CURSOR_PAGE, pszCursorName );
1261 100 : hCursorResult = PQexec(hPGConn, osCommand );
1262 :
1263 100 : bCursorActive = TRUE;
1264 :
1265 100 : CreateMapFromFieldNameToIndex();
1266 :
1267 100 : nResultOffset = 0;
1268 100 : }
1269 :
1270 : /************************************************************************/
1271 : /* GetNextRawFeature() */
1272 : /************************************************************************/
1273 :
1274 2267 : OGRFeature *OGRPGLayer::GetNextRawFeature()
1275 :
1276 : {
1277 2267 : PGconn *hPGConn = poDS->GetPGConn();
1278 2267 : CPLString osCommand;
1279 :
1280 : /* -------------------------------------------------------------------- */
1281 : /* Do we need to establish an initial query? */
1282 : /* -------------------------------------------------------------------- */
1283 2267 : if( iNextShapeId == 0 && hCursorResult == NULL )
1284 : {
1285 69 : SetInitialQueryCursor();
1286 : }
1287 :
1288 : /* -------------------------------------------------------------------- */
1289 : /* Are we in some sort of error condition? */
1290 : /* -------------------------------------------------------------------- */
1291 2267 : if( hCursorResult == NULL
1292 : || PQresultStatus(hCursorResult) != PGRES_TUPLES_OK )
1293 : {
1294 0 : CPLDebug( "PG", "PQclear() on an error condition");
1295 :
1296 0 : OGRPGClearResult( hCursorResult );
1297 :
1298 0 : iNextShapeId = MAX(1,iNextShapeId);
1299 0 : return NULL;
1300 : }
1301 :
1302 : /* -------------------------------------------------------------------- */
1303 : /* Do we need to fetch more records? */
1304 : /* -------------------------------------------------------------------- */
1305 2267 : if( nResultOffset >= PQntuples(hCursorResult)
1306 : && bCursorActive )
1307 : {
1308 30 : OGRPGClearResult( hCursorResult );
1309 :
1310 30 : osCommand.Printf( "FETCH %d in %s", CURSOR_PAGE, pszCursorName );
1311 30 : hCursorResult = PQexec(hPGConn, osCommand );
1312 :
1313 30 : nResultOffset = 0;
1314 : }
1315 :
1316 : /* -------------------------------------------------------------------- */
1317 : /* Are we out of results? If so complete the transaction, and */
1318 : /* cleanup, but don't reset the next shapeid. */
1319 : /* -------------------------------------------------------------------- */
1320 2267 : if( nResultOffset >= PQntuples(hCursorResult) )
1321 : {
1322 26 : OGRPGClearResult( hCursorResult );
1323 :
1324 26 : if( bCursorActive )
1325 : {
1326 26 : osCommand.Printf( "CLOSE %s", pszCursorName );
1327 :
1328 26 : hCursorResult = PQexec(hPGConn, osCommand);
1329 26 : OGRPGClearResult( hCursorResult );
1330 : }
1331 :
1332 26 : poDS->FlushSoftTransaction();
1333 :
1334 26 : hCursorResult = NULL;
1335 26 : bCursorActive = FALSE;
1336 :
1337 26 : iNextShapeId = MAX(1,iNextShapeId);
1338 :
1339 26 : return NULL;
1340 : }
1341 :
1342 :
1343 : /* -------------------------------------------------------------------- */
1344 : /* Create a feature from the current result. */
1345 : /* -------------------------------------------------------------------- */
1346 2241 : OGRFeature *poFeature = RecordToFeature( nResultOffset );
1347 :
1348 2241 : nResultOffset++;
1349 2241 : iNextShapeId++;
1350 :
1351 2241 : return poFeature;
1352 : }
1353 :
1354 : /************************************************************************/
1355 : /* SetNextByIndex() */
1356 : /************************************************************************/
1357 :
1358 4 : OGRErr OGRPGLayer::SetNextByIndex( long nIndex )
1359 :
1360 : {
1361 4 : if( !TestCapability(OLCFastSetNextByIndex) )
1362 0 : return OGRLayer::SetNextByIndex(nIndex);
1363 :
1364 4 : if( nIndex == iNextShapeId)
1365 : {
1366 0 : return OGRERR_NONE;
1367 : }
1368 :
1369 4 : if( nIndex < 0 )
1370 : {
1371 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid index");
1372 0 : return OGRERR_FAILURE;
1373 : }
1374 :
1375 4 : if( nIndex == 0 )
1376 : {
1377 0 : ResetReading();
1378 0 : return OGRERR_NONE;
1379 : }
1380 :
1381 4 : PGconn *hPGConn = poDS->GetPGConn();
1382 4 : CPLString osCommand;
1383 :
1384 4 : if (hCursorResult == NULL )
1385 : {
1386 2 : SetInitialQueryCursor();
1387 : }
1388 :
1389 4 : OGRPGClearResult( hCursorResult );
1390 :
1391 4 : osCommand.Printf( "FETCH ABSOLUTE %ld in %s", nIndex+1, pszCursorName );
1392 4 : hCursorResult = PQexec(hPGConn, osCommand );
1393 :
1394 4 : if (PQresultStatus(hCursorResult) != PGRES_TUPLES_OK ||
1395 : PQntuples(hCursorResult) != 1)
1396 : {
1397 : CPLError( CE_Failure, CPLE_AppDefined,
1398 0 : "Attempt to read feature at invalid index (%ld).", nIndex );
1399 :
1400 0 : OGRPGClearResult( hCursorResult );
1401 :
1402 0 : if( bCursorActive )
1403 : {
1404 0 : osCommand.Printf( "CLOSE %s", pszCursorName );
1405 :
1406 0 : hCursorResult = PQexec(hPGConn, osCommand);
1407 0 : OGRPGClearResult( hCursorResult );
1408 : }
1409 :
1410 0 : poDS->FlushSoftTransaction();
1411 :
1412 0 : hCursorResult = NULL;
1413 0 : bCursorActive = FALSE;
1414 :
1415 0 : iNextShapeId = 0;
1416 :
1417 0 : return OGRERR_FAILURE;
1418 : }
1419 :
1420 4 : nResultOffset = 0;
1421 4 : iNextShapeId = nIndex;
1422 :
1423 4 : return OGRERR_NONE;
1424 : }
1425 :
1426 : /************************************************************************/
1427 : /* HEXToGeometry() */
1428 : /************************************************************************/
1429 :
1430 12 : OGRGeometry *OGRPGLayer::HEXToGeometry( const char *pszBytea )
1431 :
1432 : {
1433 12 : GByte *pabyWKB = NULL;
1434 12 : int iSrc=0;
1435 12 : int iDst=0;
1436 12 : OGRGeometry *poGeometry = NULL;
1437 :
1438 12 : if( pszBytea == NULL )
1439 0 : return NULL;
1440 :
1441 : /* -------------------------------------------------------------------- */
1442 : /* Convert hex to binary. */
1443 : /* -------------------------------------------------------------------- */
1444 12 : pabyWKB = (GByte *) CPLMalloc(strlen(pszBytea)+1);
1445 1784 : while( pszBytea[iSrc] != '\0' )
1446 : {
1447 3375 : if( pszBytea[iSrc] >= '0' && pszBytea[iSrc] <= '9' )
1448 1615 : pabyWKB[iDst] = pszBytea[iSrc] - '0';
1449 290 : else if( pszBytea[iSrc] >= 'A' && pszBytea[iSrc] <= 'F' )
1450 145 : pabyWKB[iDst] = pszBytea[iSrc] - 'A' + 10;
1451 0 : else if( pszBytea[iSrc] >= 'a' && pszBytea[iSrc] <= 'f' )
1452 0 : pabyWKB[iDst] = pszBytea[iSrc] - 'a' + 10;
1453 : else
1454 0 : pabyWKB[iDst] = 0;
1455 :
1456 1760 : pabyWKB[iDst] *= 16;
1457 :
1458 1760 : iSrc++;
1459 :
1460 3375 : if( pszBytea[iSrc] >= '0' && pszBytea[iSrc] <= '9' )
1461 1615 : pabyWKB[iDst] += pszBytea[iSrc] - '0';
1462 290 : else if( pszBytea[iSrc] >= 'A' && pszBytea[iSrc] <= 'F' )
1463 145 : pabyWKB[iDst] += pszBytea[iSrc] - 'A' + 10;
1464 0 : else if( pszBytea[iSrc] >= 'a' && pszBytea[iSrc] <= 'f' )
1465 0 : pabyWKB[iDst] += pszBytea[iSrc] - 'a' + 10;
1466 : else
1467 0 : pabyWKB[iDst] += 0;
1468 :
1469 1760 : iSrc++;
1470 1760 : iDst++;
1471 : }
1472 :
1473 12 : poGeometry = EWKBToGeometry(pabyWKB, iDst);
1474 :
1475 12 : CPLFree(pabyWKB);
1476 :
1477 12 : return poGeometry;
1478 : }
1479 :
1480 :
1481 : /************************************************************************/
1482 : /* EWKBToGeometry() */
1483 : /************************************************************************/
1484 :
1485 14 : OGRGeometry *OGRPGLayer::EWKBToGeometry( GByte *pabyWKB, int nLength )
1486 :
1487 : {
1488 14 : OGRGeometry *poGeometry = NULL;
1489 14 : unsigned int ewkbFlags = 0;
1490 :
1491 : /* -------------------------------------------------------------------- */
1492 : /* Detect XYZM variant of PostGIS EWKB */
1493 : /* */
1494 : /* OGR does not support parsing M coordinate, */
1495 : /* so we return NULL geometry. */
1496 : /* -------------------------------------------------------------------- */
1497 14 : memcpy(&ewkbFlags, pabyWKB+1, 4);
1498 14 : OGRwkbByteOrder eByteOrder = (pabyWKB[0] == 0 ? wkbXDR : wkbNDR);
1499 14 : if( OGR_SWAP( eByteOrder ) )
1500 0 : ewkbFlags= CPL_SWAP32(ewkbFlags);
1501 :
1502 14 : if (ewkbFlags & 0x40000000)
1503 : {
1504 : CPLError( CE_Failure, CPLE_AppDefined,
1505 7 : "Reading EWKB with 4-dimensional coordinates (XYZM) is not supported" );
1506 :
1507 7 : return NULL;
1508 : }
1509 :
1510 : /* -------------------------------------------------------------------- */
1511 : /* PostGIS EWKB format includes an SRID, but this won't be */
1512 : /* understood by OGR, so if the SRID flag is set, we remove the */
1513 : /* SRID (bytes at offset 5 to 8). */
1514 : /* -------------------------------------------------------------------- */
1515 21 : if( (pabyWKB[0] == 0 /* big endian */ && (pabyWKB[1] & 0x20) )
1516 14 : || (pabyWKB[0] != 0 /* little endian */ && (pabyWKB[4] & 0x20)) )
1517 : {
1518 0 : memmove( pabyWKB+5, pabyWKB+9, nLength-9 );
1519 0 : nLength -= 4;
1520 0 : if( pabyWKB[0] == 0 )
1521 0 : pabyWKB[1] &= (~0x20);
1522 : else
1523 0 : pabyWKB[4] &= (~0x20);
1524 : }
1525 :
1526 : /* -------------------------------------------------------------------- */
1527 : /* Try to ingest the geometry. */
1528 : /* -------------------------------------------------------------------- */
1529 7 : OGRGeometryFactory::createFromWkb( pabyWKB, NULL, &poGeometry, nLength );
1530 :
1531 7 : return poGeometry;
1532 : }
1533 :
1534 : /************************************************************************/
1535 : /* GeometryToHex() */
1536 : /************************************************************************/
1537 11 : char *OGRPGLayer::GeometryToHex( OGRGeometry * poGeometry, int nSRSId )
1538 : {
1539 : GByte *pabyWKB;
1540 : char *pszTextBuf;
1541 : char *pszTextBufCurrent;
1542 : char *pszHex;
1543 :
1544 11 : int nWkbSize = poGeometry->WkbSize();
1545 11 : pabyWKB = (GByte *) CPLMalloc(nWkbSize);
1546 :
1547 11 : if( poGeometry->exportToWkb( wkbNDR, pabyWKB ) != OGRERR_NONE )
1548 : {
1549 0 : CPLFree( pabyWKB );
1550 0 : return CPLStrdup("");
1551 : }
1552 :
1553 : /* When converting to hex, each byte takes 2 hex characters. In addition
1554 : we add in 8 characters to represent the SRID integer in hex, and
1555 : one for a null terminator */
1556 :
1557 11 : int pszSize = nWkbSize*2 + 8 + 1;
1558 11 : pszTextBuf = (char *) CPLMalloc(pszSize);
1559 11 : pszTextBufCurrent = pszTextBuf;
1560 :
1561 : /* Convert the 1st byte, which is the endianess flag, to hex. */
1562 11 : pszHex = CPLBinaryToHex( 1, pabyWKB );
1563 11 : strcpy(pszTextBufCurrent, pszHex );
1564 11 : CPLFree ( pszHex );
1565 11 : pszTextBufCurrent += 2;
1566 :
1567 : /* Next, get the geom type which is bytes 2 through 5 */
1568 : GUInt32 geomType;
1569 11 : memcpy( &geomType, pabyWKB+1, 4 );
1570 :
1571 : /* Now add the SRID flag if an SRID is provided */
1572 11 : if (nSRSId != -1)
1573 : {
1574 : /* Change the flag to wkbNDR (little) endianess */
1575 0 : GUInt32 nGSrsFlag = CPL_LSBWORD32( WKBSRIDFLAG );
1576 : /* Apply the flag */
1577 0 : geomType = geomType | nGSrsFlag;
1578 : }
1579 :
1580 : /* Now write the geom type which is 4 bytes */
1581 11 : pszHex = CPLBinaryToHex( 4, (GByte*) &geomType );
1582 11 : strcpy(pszTextBufCurrent, pszHex );
1583 11 : CPLFree ( pszHex );
1584 11 : pszTextBufCurrent += 8;
1585 :
1586 : /* Now include SRID if provided */
1587 11 : if (nSRSId != -1)
1588 : {
1589 : /* Force the srsid to wkbNDR (little) endianess */
1590 0 : GUInt32 nGSRSId = CPL_LSBWORD32( nSRSId );
1591 0 : pszHex = CPLBinaryToHex( sizeof(nGSRSId),(GByte*) &nGSRSId );
1592 0 : strcpy(pszTextBufCurrent, pszHex );
1593 0 : CPLFree ( pszHex );
1594 0 : pszTextBufCurrent += 8;
1595 : }
1596 :
1597 : /* Copy the rest of the data over - subtract
1598 : 5 since we already copied 5 bytes above */
1599 11 : pszHex = CPLBinaryToHex( nWkbSize - 5, pabyWKB + 5 );
1600 11 : strcpy(pszTextBufCurrent, pszHex );
1601 11 : CPLFree ( pszHex );
1602 :
1603 11 : CPLFree( pabyWKB );
1604 :
1605 11 : return pszTextBuf;
1606 : }
1607 :
1608 :
1609 : /************************************************************************/
1610 : /* BYTEAToGByteArray() */
1611 : /************************************************************************/
1612 :
1613 1148 : GByte* OGRPGLayer::BYTEAToGByteArray( const char *pszBytea, int* pnLength )
1614 : {
1615 : GByte* pabyData;
1616 1148 : int iSrc=0, iDst=0;
1617 :
1618 1148 : if( pszBytea == NULL )
1619 : {
1620 0 : if (pnLength) *pnLength = 0;
1621 0 : return NULL;
1622 : }
1623 :
1624 1148 : pabyData = (GByte *) CPLMalloc(strlen(pszBytea));
1625 :
1626 39970 : while( pszBytea[iSrc] != '\0' )
1627 : {
1628 37674 : if( pszBytea[iSrc] == '\\' )
1629 : {
1630 47347 : if( pszBytea[iSrc+1] >= '0' && pszBytea[iSrc+1] <= '9' )
1631 : {
1632 47304 : pabyData[iDst++] =
1633 23652 : (pszBytea[iSrc+1] - 48) * 64
1634 23652 : + (pszBytea[iSrc+2] - 48) * 8
1635 47304 : + (pszBytea[iSrc+3] - 48) * 1;
1636 23652 : iSrc += 4;
1637 : }
1638 : else
1639 : {
1640 43 : pabyData[iDst++] = pszBytea[iSrc+1];
1641 43 : iSrc += 2;
1642 : }
1643 : }
1644 : else
1645 : {
1646 13979 : pabyData[iDst++] = pszBytea[iSrc++];
1647 : }
1648 : }
1649 1148 : if (pnLength) *pnLength = iDst;
1650 :
1651 1148 : return pabyData;
1652 : }
1653 :
1654 :
1655 : /************************************************************************/
1656 : /* BYTEAToGeometry() */
1657 : /************************************************************************/
1658 :
1659 1134 : OGRGeometry *OGRPGLayer::BYTEAToGeometry( const char *pszBytea )
1660 :
1661 : {
1662 : GByte *pabyWKB;
1663 1134 : int nLen=0;
1664 : OGRGeometry *poGeometry;
1665 :
1666 1134 : if( pszBytea == NULL )
1667 0 : return NULL;
1668 :
1669 1134 : pabyWKB = BYTEAToGByteArray(pszBytea, &nLen);
1670 :
1671 1134 : poGeometry = NULL;
1672 1134 : OGRGeometryFactory::createFromWkb( pabyWKB, NULL, &poGeometry, nLen );
1673 :
1674 1134 : CPLFree( pabyWKB );
1675 1134 : return poGeometry;
1676 : }
1677 :
1678 :
1679 : /************************************************************************/
1680 : /* GByteArrayToBYTEA() */
1681 : /************************************************************************/
1682 :
1683 38 : char* OGRPGLayer::GByteArrayToBYTEA( const GByte* pabyData, int nLen)
1684 : {
1685 : char* pszTextBuf;
1686 :
1687 38 : pszTextBuf = (char *) CPLMalloc(nLen*5+1);
1688 :
1689 38 : int iSrc, iDst=0;
1690 :
1691 10253 : for( iSrc = 0; iSrc < nLen; iSrc++ )
1692 : {
1693 20442 : if( pabyData[iSrc] < 40 || pabyData[iSrc] > 126
1694 3661 : || pabyData[iSrc] == '\\' )
1695 : {
1696 6566 : sprintf( pszTextBuf+iDst, "\\\\%03o", pabyData[iSrc] );
1697 6566 : iDst += 5;
1698 : }
1699 : else
1700 3649 : pszTextBuf[iDst++] = pabyData[iSrc];
1701 : }
1702 38 : pszTextBuf[iDst] = '\0';
1703 :
1704 38 : return pszTextBuf;
1705 : }
1706 :
1707 : /************************************************************************/
1708 : /* GeometryToBYTEA() */
1709 : /************************************************************************/
1710 :
1711 34 : char *OGRPGLayer::GeometryToBYTEA( OGRGeometry * poGeometry )
1712 :
1713 : {
1714 34 : int nWkbSize = poGeometry->WkbSize();
1715 : GByte *pabyWKB;
1716 : char *pszTextBuf;
1717 :
1718 34 : pabyWKB = (GByte *) CPLMalloc(nWkbSize);
1719 34 : if( poGeometry->exportToWkb( wkbNDR, pabyWKB ) != OGRERR_NONE )
1720 : {
1721 0 : CPLFree(pabyWKB);
1722 0 : return CPLStrdup("");
1723 : }
1724 :
1725 34 : pszTextBuf = GByteArrayToBYTEA( pabyWKB, nWkbSize );
1726 34 : CPLFree(pabyWKB);
1727 :
1728 34 : return pszTextBuf;
1729 : }
1730 :
1731 : /************************************************************************/
1732 : /* OIDToGeometry() */
1733 : /************************************************************************/
1734 :
1735 0 : OGRGeometry *OGRPGLayer::OIDToGeometry( Oid oid )
1736 :
1737 : {
1738 0 : PGconn *hPGConn = poDS->GetPGConn();
1739 : GByte *pabyWKB;
1740 : int fd, nBytes;
1741 : OGRGeometry *poGeometry;
1742 :
1743 : #define MAX_WKB 500000
1744 :
1745 0 : if( oid == 0 )
1746 0 : return NULL;
1747 :
1748 0 : fd = lo_open( hPGConn, oid, INV_READ );
1749 0 : if( fd < 0 )
1750 0 : return NULL;
1751 :
1752 0 : pabyWKB = (GByte *) CPLMalloc(MAX_WKB);
1753 0 : nBytes = lo_read( hPGConn, fd, (char *) pabyWKB, MAX_WKB );
1754 0 : lo_close( hPGConn, fd );
1755 :
1756 0 : poGeometry = NULL;
1757 0 : OGRGeometryFactory::createFromWkb( pabyWKB, NULL, &poGeometry, nBytes );
1758 :
1759 0 : CPLFree( pabyWKB );
1760 :
1761 0 : return poGeometry;
1762 : }
1763 :
1764 : /************************************************************************/
1765 : /* GeometryToOID() */
1766 : /************************************************************************/
1767 :
1768 0 : Oid OGRPGLayer::GeometryToOID( OGRGeometry * poGeometry )
1769 :
1770 : {
1771 0 : PGconn *hPGConn = poDS->GetPGConn();
1772 0 : int nWkbSize = poGeometry->WkbSize();
1773 : GByte *pabyWKB;
1774 : Oid oid;
1775 : int fd, nBytesWritten;
1776 :
1777 0 : pabyWKB = (GByte *) CPLMalloc(nWkbSize);
1778 0 : if( poGeometry->exportToWkb( wkbNDR, pabyWKB ) != OGRERR_NONE )
1779 0 : return 0;
1780 :
1781 0 : oid = lo_creat( hPGConn, INV_READ|INV_WRITE );
1782 :
1783 0 : fd = lo_open( hPGConn, oid, INV_WRITE );
1784 0 : nBytesWritten = lo_write( hPGConn, fd, (char *) pabyWKB, nWkbSize );
1785 0 : lo_close( hPGConn, fd );
1786 :
1787 0 : if( nBytesWritten != nWkbSize )
1788 : {
1789 : CPLDebug( "PG",
1790 : "Only wrote %d bytes of %d intended for (fd=%d,oid=%d).\n",
1791 0 : nBytesWritten, nWkbSize, fd, oid );
1792 : }
1793 :
1794 0 : CPLFree( pabyWKB );
1795 :
1796 0 : return oid;
1797 : }
1798 :
1799 : /************************************************************************/
1800 : /* StartTransaction() */
1801 : /************************************************************************/
1802 :
1803 15 : OGRErr OGRPGLayer::StartTransaction()
1804 :
1805 : {
1806 15 : return poDS->SoftStartTransaction();
1807 : }
1808 :
1809 : /************************************************************************/
1810 : /* CommitTransaction() */
1811 : /************************************************************************/
1812 :
1813 15 : OGRErr OGRPGLayer::CommitTransaction()
1814 :
1815 : {
1816 15 : return poDS->SoftCommit();
1817 : }
1818 :
1819 : /************************************************************************/
1820 : /* RollbackTransaction() */
1821 : /************************************************************************/
1822 :
1823 0 : OGRErr OGRPGLayer::RollbackTransaction()
1824 :
1825 : {
1826 0 : return poDS->SoftRollback();
1827 : }
1828 :
1829 : /************************************************************************/
1830 : /* GetSpatialRef() */
1831 : /************************************************************************/
1832 :
1833 136 : OGRSpatialReference *OGRPGLayer::GetSpatialRef()
1834 :
1835 : {
1836 136 : if( poSRS == NULL && nSRSId > -1 )
1837 : {
1838 0 : poSRS = poDS->FetchSRS( nSRSId );
1839 0 : if( poSRS != NULL )
1840 0 : poSRS->Reference();
1841 : else
1842 0 : nSRSId = -1;
1843 : }
1844 :
1845 136 : return poSRS;
1846 : }
1847 :
1848 : /************************************************************************/
1849 : /* GetFIDColumn() */
1850 : /************************************************************************/
1851 :
1852 0 : const char *OGRPGLayer::GetFIDColumn()
1853 :
1854 : {
1855 0 : if( pszFIDColumn != NULL )
1856 0 : return pszFIDColumn;
1857 : else
1858 0 : return "";
1859 : }
1860 :
1861 : /************************************************************************/
1862 : /* GetGeometryColumn() */
1863 : /************************************************************************/
1864 :
1865 6 : const char *OGRPGLayer::GetGeometryColumn()
1866 :
1867 : {
1868 6 : if( pszGeomColumn != NULL )
1869 6 : return pszGeomColumn;
1870 : else
1871 0 : return "";
1872 : }
1873 :
1874 : /************************************************************************/
1875 : /* GetExtent() */
1876 : /************************************************************************/
1877 :
1878 4 : OGRErr OGRPGLayer::RunGetExtentRequest( OGREnvelope *psExtent, int bForce,
1879 : CPLString osCommand)
1880 : {
1881 4 : if ( psExtent == NULL )
1882 0 : return OGRERR_FAILURE;
1883 :
1884 4 : if ( TestCapability(OLCFastGetExtent) || bHasPostGISGeography )
1885 : {
1886 2 : PGconn *hPGConn = poDS->GetPGConn();
1887 2 : PGresult *hResult = NULL;
1888 :
1889 2 : hResult = PQexec( hPGConn, osCommand );
1890 2 : if( ! hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK || PQgetisnull(hResult,0,0) )
1891 : {
1892 0 : OGRPGClearResult( hResult );
1893 0 : CPLDebug("PG","Unable to get extent by PostGIS. Using standard OGRLayer method.");
1894 0 : return OGRPGLayer::GetExtent( psExtent, bForce );
1895 : }
1896 :
1897 2 : char * pszBox = PQgetvalue(hResult,0,0);
1898 : char * ptr, *ptrEndParenthesis;
1899 : char szVals[64*6+6];
1900 :
1901 2 : ptr = strchr(pszBox, '(');
1902 2 : if (ptr)
1903 2 : ptr ++;
1904 2 : if (ptr == NULL ||
1905 : (ptrEndParenthesis = strchr(ptr, ')')) == NULL ||
1906 : ptrEndParenthesis - ptr > (int)(sizeof(szVals) - 1))
1907 : {
1908 : CPLError( CE_Failure, CPLE_IllegalArg,
1909 0 : "Bad extent representation: '%s'", pszBox);
1910 :
1911 0 : OGRPGClearResult( hResult );
1912 0 : return OGRERR_FAILURE;
1913 : }
1914 :
1915 2 : strncpy(szVals,ptr,ptrEndParenthesis - ptr);
1916 2 : szVals[ptrEndParenthesis - ptr] = '\0';
1917 :
1918 2 : char ** papszTokens = CSLTokenizeString2(szVals," ,",CSLT_HONOURSTRINGS);
1919 2 : int nTokenCnt = poDS->sPostGISVersion.nMajor >= 1 ? 4 : 6;
1920 :
1921 2 : if ( CSLCount(papszTokens) != nTokenCnt )
1922 : {
1923 : CPLError( CE_Failure, CPLE_IllegalArg,
1924 0 : "Bad extent representation: '%s'", pszBox);
1925 0 : CSLDestroy(papszTokens);
1926 :
1927 0 : OGRPGClearResult( hResult );
1928 0 : return OGRERR_FAILURE;
1929 : }
1930 :
1931 : // Take X,Y coords
1932 : // For PostGis ver >= 1.0.0 -> Tokens: X1 Y1 X2 Y2 (nTokenCnt = 4)
1933 : // For PostGIS ver < 1.0.0 -> Tokens: X1 Y1 Z1 X2 Y2 Z2 (nTokenCnt = 6)
1934 : // => X2 index calculated as nTokenCnt/2
1935 : // Y2 index caluclated as nTokenCnt/2+1
1936 :
1937 2 : psExtent->MinX = CPLAtof( papszTokens[0] );
1938 2 : psExtent->MinY = CPLAtof( papszTokens[1] );
1939 2 : psExtent->MaxX = CPLAtof( papszTokens[nTokenCnt/2] );
1940 2 : psExtent->MaxY = CPLAtof( papszTokens[nTokenCnt/2+1] );
1941 :
1942 2 : CSLDestroy(papszTokens);
1943 2 : OGRPGClearResult( hResult );
1944 :
1945 2 : return OGRERR_NONE;
1946 : }
1947 :
1948 2 : return OGRLayer::GetExtent( psExtent, bForce );
1949 : }
|