1 : /******************************************************************************
2 : * $Id: ogrpglayer.cpp 19703 2010-05-14 16:46:57Z 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 19703 2010-05-14 16:46:57Z 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 1569 : OGRPGLayer::OGRPGLayer()
88 :
89 : {
90 1569 : poDS = NULL;
91 :
92 1569 : bHasWkb = FALSE;
93 1569 : bWkbAsOid = FALSE;
94 1569 : bHasPostGISGeometry = FALSE;
95 1569 : bHasPostGISGeography = FALSE;
96 1569 : pszGeomColumn = NULL;
97 1569 : pszQueryStatement = NULL;
98 :
99 1569 : bHasFid = FALSE;
100 1569 : pszFIDColumn = NULL;
101 :
102 1569 : iNextShapeId = 0;
103 1569 : nResultOffset = 0;
104 :
105 1569 : nCoordDimension = 2; // initialize in case PostGIS is not available
106 :
107 1569 : poSRS = NULL;
108 1569 : nSRSId = -2; // we haven't even queried the database for it yet.
109 :
110 1569 : pszCursorName = CPLStrdup(CPLSPrintf("OGRPGLayerReader%p", this));
111 :
112 1569 : hCursorResult = NULL;
113 1569 : bCursorActive = FALSE;
114 :
115 1569 : bCanUseBinaryCursor = TRUE;
116 :
117 1569 : poFeatureDefn = NULL;
118 1569 : panMapFieldNameToIndex = NULL;
119 1569 : }
120 :
121 : /************************************************************************/
122 : /* ~OGRPGLayer() */
123 : /************************************************************************/
124 :
125 1569 : OGRPGLayer::~OGRPGLayer()
126 :
127 : {
128 1569 : if( m_nFeaturesRead > 0 && poFeatureDefn != NULL )
129 : {
130 : CPLDebug( "PG", "%d features read on layer '%s'.",
131 : (int) m_nFeaturesRead,
132 52 : poFeatureDefn->GetName() );
133 : }
134 :
135 1569 : ResetReading();
136 :
137 1569 : CPLFree( pszGeomColumn );
138 1569 : CPLFree( pszFIDColumn );
139 1569 : CPLFree( pszQueryStatement );
140 1569 : CPLFree( panMapFieldNameToIndex );
141 1569 : CPLFree( pszCursorName );
142 :
143 1569 : if( poSRS != NULL )
144 0 : poSRS->Release();
145 :
146 1569 : if( poFeatureDefn )
147 1566 : poFeatureDefn->Release();
148 1569 : }
149 :
150 : /************************************************************************/
151 : /* ResetReading() */
152 : /************************************************************************/
153 :
154 3212 : void OGRPGLayer::ResetReading()
155 :
156 : {
157 3212 : PGconn *hPGConn = poDS->GetPGConn();
158 3212 : CPLString osCommand;
159 :
160 3212 : iNextShapeId = 0;
161 :
162 3212 : if( hCursorResult != NULL )
163 : {
164 93 : OGRPGClearResult( hCursorResult );
165 :
166 93 : if( bCursorActive )
167 : {
168 93 : osCommand.Printf("CLOSE %s", pszCursorName );
169 :
170 93 : hCursorResult = PQexec(hPGConn, osCommand.c_str());
171 93 : OGRPGClearResult( hCursorResult );
172 : }
173 :
174 93 : poDS->FlushSoftTransaction();
175 :
176 93 : hCursorResult = NULL;
177 3212 : }
178 3212 : }
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 : int OGRPGTimeStamp2DMYHMS(GIntBig dt, int *year, int *month, int *day,
408 0 : 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 2273 : OGRFeature *OGRPGLayer::RecordToFeature( int iRecord )
568 :
569 : {
570 : /* -------------------------------------------------------------------- */
571 : /* Create a feature from the current result. */
572 : /* -------------------------------------------------------------------- */
573 : int iField;
574 2273 : OGRFeature *poFeature = new OGRFeature( poFeatureDefn );
575 :
576 2273 : poFeature->SetFID( iNextShapeId );
577 2273 : m_nFeaturesRead++;
578 :
579 : /* ==================================================================== */
580 : /* Transfer all result fields we can. */
581 : /* ==================================================================== */
582 10161 : for( iField = 0;
583 : iField < PQnfields(hCursorResult);
584 : iField++ )
585 : {
586 : int iOGRField;
587 :
588 : #if !defined(PG_PRE74)
589 7888 : int nTypeOID = PQftype(hCursorResult, iField);
590 : #endif
591 :
592 : /* -------------------------------------------------------------------- */
593 : /* Handle FID. */
594 : /* -------------------------------------------------------------------- */
595 7888 : if( bHasFid && EQUAL(PQfname(hCursorResult,iField),pszFIDColumn) )
596 : {
597 : #if !defined(PG_PRE74)
598 2230 : if ( PQfformat( hCursorResult, iField ) == 1 ) // Binary data representation
599 : {
600 2 : if ( nTypeOID == INT4OID)
601 : {
602 : int nVal;
603 2 : 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 2228 : char* pabyData = PQgetvalue(hCursorResult,iRecord,iField);
618 : /* ogr_pg_20 may crash if PostGIS is unavailable and we don't test pabyData */
619 2228 : if (pabyData)
620 2228 : poFeature->SetFID( atoi(pabyData) );
621 : else
622 0 : continue;
623 : }
624 : }
625 :
626 : /* -------------------------------------------------------------------- */
627 : /* Handle PostGIS geometry */
628 : /* -------------------------------------------------------------------- */
629 :
630 10529 : if( bHasPostGISGeometry || bHasPostGISGeography )
631 : {
632 3740 : 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 3740 : 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 3738 : 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 1097 : OGRGeometry *poGeometry = NULL;
705 :
706 1097 : pszWKT = PQgetvalue( hCursorResult, iRecord, iField );
707 1097 : pszPostSRID = pszWKT;
708 :
709 : // optionally strip off PostGIS SRID identifier. This
710 : // happens if we got a raw geometry field.
711 1097 : 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 1108 : 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 1086 : &poGeometry );
728 1097 : if( poGeometry != NULL )
729 : {
730 74 : poGeometry->assignSpatialReference( poSRS );
731 74 : poFeature->SetGeometryDirectly( poGeometry );
732 : }
733 :
734 1097 : continue;
735 : }
736 : }
737 : /* -------------------------------------------------------------------- */
738 : /* Handle raw binary geometry ... this hasn't been tested in a */
739 : /* while. */
740 : /* -------------------------------------------------------------------- */
741 4148 : else if( EQUAL(PQfname(hCursorResult,iField),"WKB_GEOMETRY") )
742 : {
743 1141 : OGRGeometry *poGeometry = NULL;
744 1141 : char * pabyData = PQgetvalue( hCursorResult, iRecord, iField);
745 :
746 1141 : if( bWkbAsOid )
747 : {
748 : poGeometry =
749 0 : OIDToGeometry( (Oid) atoi(pabyData) );
750 : }
751 : else
752 : {
753 1141 : 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 1141 : if (poGeometry == NULL)
763 : {
764 1140 : poGeometry = BYTEAToGeometry( pabyData );
765 : }
766 : }
767 :
768 1141 : if( poGeometry != NULL )
769 : {
770 125 : poGeometry->assignSpatialReference( poSRS );
771 125 : poFeature->SetGeometryDirectly( poGeometry );
772 : }
773 :
774 1141 : continue;
775 : }
776 :
777 : /* -------------------------------------------------------------------- */
778 : /* Transfer regular data fields. */
779 : /* -------------------------------------------------------------------- */
780 5648 : iOGRField = panMapFieldNameToIndex[iField];
781 :
782 5648 : if( iOGRField < 0 )
783 2230 : continue;
784 :
785 3418 : if( PQgetisnull( hCursorResult, iRecord, iField ) )
786 476 : continue;
787 :
788 : OGRFieldType eOGRType =
789 2942 : poFeatureDefn->GetFieldDefn(iOGRField)->GetType();
790 :
791 2942 : 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 4 : 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 2926 : else if( eOGRType == OFTRealList )
854 : {
855 : int nCount, i;
856 : double *padfList;
857 :
858 : #if !defined(PG_PRE74)
859 44 : 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 4 : 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 4 : 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 40 : "{,}", FALSE, FALSE );
917 :
918 40 : nCount = CSLCount(papszTokens);
919 40 : padfList = (double *) CPLCalloc(sizeof(double),nCount);
920 :
921 120 : for( i = 0; i < nCount; i++ )
922 80 : padfList[i] = CPLAtof(papszTokens[i]);
923 40 : CSLDestroy( papszTokens );
924 : }
925 :
926 44 : poFeature->SetField( iOGRField, nCount, padfList );
927 44 : CPLFree( padfList );
928 : }
929 :
930 2882 : 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 2940 : 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 2 : 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 2 : 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 0 : 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 2728 : 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 2712 : if ( PQfformat( hCursorResult, iField ) == 1 &&
1094 : eOGRType != OFTString ) // Binary data
1095 : {
1096 18 : if ( nTypeOID == BOOLOID )
1097 : {
1098 : char cVal;
1099 2 : 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 4 : 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, CPLAtof(str));
1130 4 : CPLFree(str);
1131 : }
1132 12 : else if ( nTypeOID == INT2OID )
1133 : {
1134 : short sVal;
1135 2 : 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 2 : 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 2 : 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 4 : 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 2 : 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 2694 : 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 2680 : else if ( eOGRType == OFTReal )
1198 : {
1199 : poFeature->SetField( iOGRField,
1200 186 : CPLAtof(PQgetvalue( hCursorResult, iRecord, iField )) );
1201 : }
1202 : else
1203 : {
1204 : poFeature->SetField( iOGRField,
1205 2494 : PQgetvalue( hCursorResult, iRecord, iField ) );
1206 : }
1207 : }
1208 : }
1209 : }
1210 :
1211 2273 : return poFeature;
1212 : }
1213 :
1214 : /************************************************************************/
1215 : /* CreateMapFromFieldNameToIndex() */
1216 : /************************************************************************/
1217 :
1218 : /* Evaluating GetFieldIndex() on each field of each feature can be very */
1219 : /* expensive if the layer has many fields (total complexity of O(n^2) where */
1220 : /* n is the number of fields), so it is valuable to compute the map from */
1221 : /* the fetched fields to the OGR field index */
1222 132 : void OGRPGLayer::CreateMapFromFieldNameToIndex()
1223 : {
1224 132 : CPLFree(panMapFieldNameToIndex);
1225 132 : panMapFieldNameToIndex = NULL;
1226 132 : if ( PQresultStatus(hCursorResult) == PGRES_TUPLES_OK )
1227 : {
1228 : panMapFieldNameToIndex =
1229 132 : (int*)CPLMalloc(sizeof(int) * PQnfields(hCursorResult));
1230 1131 : for( int iField = 0;
1231 : iField < PQnfields(hCursorResult);
1232 : iField++ )
1233 : {
1234 : panMapFieldNameToIndex[iField] =
1235 999 : poFeatureDefn->GetFieldIndex(PQfname(hCursorResult,iField));
1236 : }
1237 : }
1238 132 : }
1239 :
1240 :
1241 : /************************************************************************/
1242 : /* SetInitialQueryCursor() */
1243 : /************************************************************************/
1244 :
1245 119 : void OGRPGLayer::SetInitialQueryCursor()
1246 : {
1247 119 : PGconn *hPGConn = poDS->GetPGConn();
1248 119 : CPLString osCommand;
1249 :
1250 119 : CPLAssert( pszQueryStatement != NULL );
1251 :
1252 119 : poDS->FlushSoftTransaction();
1253 119 : poDS->SoftStartTransaction();
1254 :
1255 121 : if ( poDS->bUseBinaryCursor && bCanUseBinaryCursor )
1256 : osCommand.Printf( "DECLARE %s BINARY CURSOR for %s",
1257 2 : pszCursorName, pszQueryStatement );
1258 : else
1259 : osCommand.Printf( "DECLARE %s CURSOR for %s",
1260 117 : pszCursorName, pszQueryStatement );
1261 :
1262 119 : hCursorResult = PQexec(hPGConn, osCommand );
1263 119 : OGRPGClearResult( hCursorResult );
1264 :
1265 119 : osCommand.Printf( "FETCH %d in %s", CURSOR_PAGE, pszCursorName );
1266 119 : hCursorResult = PQexec(hPGConn, osCommand );
1267 :
1268 119 : bCursorActive = TRUE;
1269 :
1270 119 : CreateMapFromFieldNameToIndex();
1271 :
1272 119 : nResultOffset = 0;
1273 119 : }
1274 :
1275 : /************************************************************************/
1276 : /* GetNextRawFeature() */
1277 : /************************************************************************/
1278 :
1279 2286 : OGRFeature *OGRPGLayer::GetNextRawFeature()
1280 :
1281 : {
1282 2286 : PGconn *hPGConn = poDS->GetPGConn();
1283 2286 : CPLString osCommand;
1284 :
1285 : /* -------------------------------------------------------------------- */
1286 : /* Do we need to establish an initial query? */
1287 : /* -------------------------------------------------------------------- */
1288 2286 : if( iNextShapeId == 0 && hCursorResult == NULL )
1289 : {
1290 85 : SetInitialQueryCursor();
1291 : }
1292 :
1293 : /* -------------------------------------------------------------------- */
1294 : /* Are we in some sort of error condition? */
1295 : /* -------------------------------------------------------------------- */
1296 2286 : if( hCursorResult == NULL
1297 : || PQresultStatus(hCursorResult) != PGRES_TUPLES_OK )
1298 : {
1299 0 : CPLDebug( "PG", "PQclear() on an error condition");
1300 :
1301 0 : OGRPGClearResult( hCursorResult );
1302 :
1303 0 : iNextShapeId = MAX(1,iNextShapeId);
1304 0 : return NULL;
1305 : }
1306 :
1307 : /* -------------------------------------------------------------------- */
1308 : /* Do we need to fetch more records? */
1309 : /* -------------------------------------------------------------------- */
1310 2286 : if( PQntuples(hCursorResult) > 0 &&
1311 : nResultOffset >= PQntuples(hCursorResult)
1312 : && bCursorActive )
1313 : {
1314 27 : OGRPGClearResult( hCursorResult );
1315 :
1316 27 : osCommand.Printf( "FETCH %d in %s", CURSOR_PAGE, pszCursorName );
1317 27 : hCursorResult = PQexec(hPGConn, osCommand );
1318 :
1319 27 : nResultOffset = 0;
1320 : }
1321 :
1322 : /* -------------------------------------------------------------------- */
1323 : /* Are we out of results? If so complete the transaction, and */
1324 : /* cleanup, but don't reset the next shapeid. */
1325 : /* -------------------------------------------------------------------- */
1326 2286 : if( nResultOffset >= PQntuples(hCursorResult) )
1327 : {
1328 26 : OGRPGClearResult( hCursorResult );
1329 :
1330 26 : if( bCursorActive )
1331 : {
1332 26 : osCommand.Printf( "CLOSE %s", pszCursorName );
1333 :
1334 26 : hCursorResult = PQexec(hPGConn, osCommand);
1335 26 : OGRPGClearResult( hCursorResult );
1336 : }
1337 :
1338 26 : poDS->FlushSoftTransaction();
1339 :
1340 26 : hCursorResult = NULL;
1341 26 : bCursorActive = FALSE;
1342 :
1343 26 : iNextShapeId = MAX(1,iNextShapeId);
1344 :
1345 26 : return NULL;
1346 : }
1347 :
1348 :
1349 : /* -------------------------------------------------------------------- */
1350 : /* Create a feature from the current result. */
1351 : /* -------------------------------------------------------------------- */
1352 2260 : OGRFeature *poFeature = RecordToFeature( nResultOffset );
1353 :
1354 2260 : nResultOffset++;
1355 2260 : iNextShapeId++;
1356 :
1357 2260 : return poFeature;
1358 : }
1359 :
1360 : /************************************************************************/
1361 : /* SetNextByIndex() */
1362 : /************************************************************************/
1363 :
1364 4 : OGRErr OGRPGLayer::SetNextByIndex( long nIndex )
1365 :
1366 : {
1367 4 : if( !TestCapability(OLCFastSetNextByIndex) )
1368 0 : return OGRLayer::SetNextByIndex(nIndex);
1369 :
1370 4 : if( nIndex == iNextShapeId)
1371 : {
1372 0 : return OGRERR_NONE;
1373 : }
1374 :
1375 4 : if( nIndex < 0 )
1376 : {
1377 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid index");
1378 0 : return OGRERR_FAILURE;
1379 : }
1380 :
1381 4 : if( nIndex == 0 )
1382 : {
1383 0 : ResetReading();
1384 0 : return OGRERR_NONE;
1385 : }
1386 :
1387 4 : PGconn *hPGConn = poDS->GetPGConn();
1388 4 : CPLString osCommand;
1389 :
1390 4 : if (hCursorResult == NULL )
1391 : {
1392 2 : SetInitialQueryCursor();
1393 : }
1394 :
1395 4 : OGRPGClearResult( hCursorResult );
1396 :
1397 4 : osCommand.Printf( "FETCH ABSOLUTE %ld in %s", nIndex+1, pszCursorName );
1398 4 : hCursorResult = PQexec(hPGConn, osCommand );
1399 :
1400 4 : if (PQresultStatus(hCursorResult) != PGRES_TUPLES_OK ||
1401 : PQntuples(hCursorResult) != 1)
1402 : {
1403 : CPLError( CE_Failure, CPLE_AppDefined,
1404 0 : "Attempt to read feature at invalid index (%ld).", nIndex );
1405 :
1406 0 : OGRPGClearResult( hCursorResult );
1407 :
1408 0 : if( bCursorActive )
1409 : {
1410 0 : osCommand.Printf( "CLOSE %s", pszCursorName );
1411 :
1412 0 : hCursorResult = PQexec(hPGConn, osCommand);
1413 0 : OGRPGClearResult( hCursorResult );
1414 : }
1415 :
1416 0 : poDS->FlushSoftTransaction();
1417 :
1418 0 : hCursorResult = NULL;
1419 0 : bCursorActive = FALSE;
1420 :
1421 0 : iNextShapeId = 0;
1422 :
1423 4 : return OGRERR_FAILURE;
1424 : }
1425 :
1426 4 : nResultOffset = 0;
1427 4 : iNextShapeId = nIndex;
1428 :
1429 4 : return OGRERR_NONE;
1430 : }
1431 :
1432 : /************************************************************************/
1433 : /* HEXToGeometry() */
1434 : /************************************************************************/
1435 :
1436 12 : OGRGeometry *OGRPGLayer::HEXToGeometry( const char *pszBytea )
1437 :
1438 : {
1439 : GByte *pabyWKB;
1440 12 : int nWKBLength=0;
1441 : OGRGeometry *poGeometry;
1442 :
1443 12 : if( pszBytea == NULL )
1444 0 : return NULL;
1445 :
1446 12 : pabyWKB = CPLHexToBinary(pszBytea, &nWKBLength);
1447 :
1448 12 : poGeometry = EWKBToGeometry(pabyWKB, nWKBLength);
1449 :
1450 12 : CPLFree(pabyWKB);
1451 :
1452 12 : return poGeometry;
1453 : }
1454 :
1455 :
1456 : /************************************************************************/
1457 : /* EWKBToGeometry() */
1458 : /************************************************************************/
1459 :
1460 14 : OGRGeometry *OGRPGLayer::EWKBToGeometry( GByte *pabyWKB, int nLength )
1461 :
1462 : {
1463 14 : OGRGeometry *poGeometry = NULL;
1464 14 : unsigned int ewkbFlags = 0;
1465 :
1466 14 : if (nLength < 5)
1467 : {
1468 : CPLError( CE_Failure, CPLE_AppDefined,
1469 0 : "Invalid EWKB content : %d bytes", nLength );
1470 0 : return NULL;
1471 : }
1472 :
1473 : /* -------------------------------------------------------------------- */
1474 : /* Detect XYZM variant of PostGIS EWKB */
1475 : /* */
1476 : /* OGR does not support parsing M coordinate, */
1477 : /* so we return NULL geometry. */
1478 : /* -------------------------------------------------------------------- */
1479 14 : memcpy(&ewkbFlags, pabyWKB+1, 4);
1480 14 : OGRwkbByteOrder eByteOrder = (pabyWKB[0] == 0 ? wkbXDR : wkbNDR);
1481 14 : if( OGR_SWAP( eByteOrder ) )
1482 0 : ewkbFlags= CPL_SWAP32(ewkbFlags);
1483 :
1484 14 : if (ewkbFlags & 0x40000000)
1485 : {
1486 : CPLError( CE_Failure, CPLE_AppDefined,
1487 7 : "Reading EWKB with 4-dimensional coordinates (XYZM) is not supported" );
1488 :
1489 7 : return NULL;
1490 : }
1491 :
1492 : /* -------------------------------------------------------------------- */
1493 : /* PostGIS EWKB format includes an SRID, but this won't be */
1494 : /* understood by OGR, so if the SRID flag is set, we remove the */
1495 : /* SRID (bytes at offset 5 to 8). */
1496 : /* -------------------------------------------------------------------- */
1497 7 : if( nLength > 9 &&
1498 : ((pabyWKB[0] == 0 /* big endian */ && (pabyWKB[1] & 0x20) )
1499 : || (pabyWKB[0] != 0 /* little endian */ && (pabyWKB[4] & 0x20))) )
1500 : {
1501 0 : memmove( pabyWKB+5, pabyWKB+9, nLength-9 );
1502 0 : nLength -= 4;
1503 0 : if( pabyWKB[0] == 0 )
1504 0 : pabyWKB[1] &= (~0x20);
1505 : else
1506 0 : pabyWKB[4] &= (~0x20);
1507 : }
1508 :
1509 : /* -------------------------------------------------------------------- */
1510 : /* Try to ingest the geometry. */
1511 : /* -------------------------------------------------------------------- */
1512 7 : OGRGeometryFactory::createFromWkb( pabyWKB, NULL, &poGeometry, nLength );
1513 :
1514 7 : return poGeometry;
1515 : }
1516 :
1517 : /************************************************************************/
1518 : /* GeometryToHex() */
1519 : /************************************************************************/
1520 11 : char *OGRPGLayer::GeometryToHex( OGRGeometry * poGeometry, int nSRSId )
1521 : {
1522 : GByte *pabyWKB;
1523 : char *pszTextBuf;
1524 : char *pszTextBufCurrent;
1525 : char *pszHex;
1526 :
1527 11 : int nWkbSize = poGeometry->WkbSize();
1528 11 : pabyWKB = (GByte *) CPLMalloc(nWkbSize);
1529 :
1530 11 : if( poGeometry->exportToWkb( wkbNDR, pabyWKB ) != OGRERR_NONE )
1531 : {
1532 0 : CPLFree( pabyWKB );
1533 0 : return CPLStrdup("");
1534 : }
1535 :
1536 : /* When converting to hex, each byte takes 2 hex characters. In addition
1537 : we add in 8 characters to represent the SRID integer in hex, and
1538 : one for a null terminator */
1539 :
1540 11 : int pszSize = nWkbSize*2 + 8 + 1;
1541 11 : pszTextBuf = (char *) CPLMalloc(pszSize);
1542 11 : pszTextBufCurrent = pszTextBuf;
1543 :
1544 : /* Convert the 1st byte, which is the endianess flag, to hex. */
1545 11 : pszHex = CPLBinaryToHex( 1, pabyWKB );
1546 11 : strcpy(pszTextBufCurrent, pszHex );
1547 11 : CPLFree ( pszHex );
1548 11 : pszTextBufCurrent += 2;
1549 :
1550 : /* Next, get the geom type which is bytes 2 through 5 */
1551 : GUInt32 geomType;
1552 11 : memcpy( &geomType, pabyWKB+1, 4 );
1553 :
1554 : /* Now add the SRID flag if an SRID is provided */
1555 11 : if (nSRSId != -1)
1556 : {
1557 : /* Change the flag to wkbNDR (little) endianess */
1558 0 : GUInt32 nGSrsFlag = CPL_LSBWORD32( WKBSRIDFLAG );
1559 : /* Apply the flag */
1560 0 : geomType = geomType | nGSrsFlag;
1561 : }
1562 :
1563 : /* Now write the geom type which is 4 bytes */
1564 11 : pszHex = CPLBinaryToHex( 4, (GByte*) &geomType );
1565 11 : strcpy(pszTextBufCurrent, pszHex );
1566 11 : CPLFree ( pszHex );
1567 11 : pszTextBufCurrent += 8;
1568 :
1569 : /* Now include SRID if provided */
1570 11 : if (nSRSId != -1)
1571 : {
1572 : /* Force the srsid to wkbNDR (little) endianess */
1573 0 : GUInt32 nGSRSId = CPL_LSBWORD32( nSRSId );
1574 0 : pszHex = CPLBinaryToHex( sizeof(nGSRSId),(GByte*) &nGSRSId );
1575 0 : strcpy(pszTextBufCurrent, pszHex );
1576 0 : CPLFree ( pszHex );
1577 0 : pszTextBufCurrent += 8;
1578 : }
1579 :
1580 : /* Copy the rest of the data over - subtract
1581 : 5 since we already copied 5 bytes above */
1582 11 : pszHex = CPLBinaryToHex( nWkbSize - 5, pabyWKB + 5 );
1583 11 : strcpy(pszTextBufCurrent, pszHex );
1584 11 : CPLFree ( pszHex );
1585 :
1586 11 : CPLFree( pabyWKB );
1587 :
1588 11 : return pszTextBuf;
1589 : }
1590 :
1591 :
1592 : /************************************************************************/
1593 : /* BYTEAToGByteArray() */
1594 : /************************************************************************/
1595 :
1596 1154 : GByte* OGRPGLayer::BYTEAToGByteArray( const char *pszBytea, int* pnLength )
1597 : {
1598 : GByte* pabyData;
1599 1154 : int iSrc=0, iDst=0;
1600 :
1601 1154 : if( pszBytea == NULL )
1602 : {
1603 0 : if (pnLength) *pnLength = 0;
1604 0 : return NULL;
1605 : }
1606 :
1607 1154 : pabyData = (GByte *) CPLMalloc(strlen(pszBytea));
1608 :
1609 39982 : while( pszBytea[iSrc] != '\0' )
1610 : {
1611 37674 : if( pszBytea[iSrc] == '\\' )
1612 : {
1613 47347 : if( pszBytea[iSrc+1] >= '0' && pszBytea[iSrc+1] <= '9' )
1614 : {
1615 23652 : if (pszBytea[iSrc+2] == '\0' ||
1616 : pszBytea[iSrc+3] == '\0')
1617 0 : break;
1618 :
1619 : pabyData[iDst++] =
1620 : (pszBytea[iSrc+1] - 48) * 64
1621 : + (pszBytea[iSrc+2] - 48) * 8
1622 23652 : + (pszBytea[iSrc+3] - 48) * 1;
1623 23652 : iSrc += 4;
1624 : }
1625 : else
1626 : {
1627 43 : if (pszBytea[iSrc+1] == '\0')
1628 0 : break;
1629 :
1630 43 : pabyData[iDst++] = pszBytea[iSrc+1];
1631 43 : iSrc += 2;
1632 : }
1633 : }
1634 : else
1635 : {
1636 13979 : pabyData[iDst++] = pszBytea[iSrc++];
1637 : }
1638 : }
1639 1154 : if (pnLength) *pnLength = iDst;
1640 :
1641 1154 : return pabyData;
1642 : }
1643 :
1644 :
1645 : /************************************************************************/
1646 : /* BYTEAToGeometry() */
1647 : /************************************************************************/
1648 :
1649 1140 : OGRGeometry *OGRPGLayer::BYTEAToGeometry( const char *pszBytea )
1650 :
1651 : {
1652 : GByte *pabyWKB;
1653 1140 : int nLen=0;
1654 : OGRGeometry *poGeometry;
1655 :
1656 1140 : if( pszBytea == NULL )
1657 0 : return NULL;
1658 :
1659 1140 : pabyWKB = BYTEAToGByteArray(pszBytea, &nLen);
1660 :
1661 1140 : poGeometry = NULL;
1662 1140 : OGRGeometryFactory::createFromWkb( pabyWKB, NULL, &poGeometry, nLen );
1663 :
1664 1140 : CPLFree( pabyWKB );
1665 1140 : return poGeometry;
1666 : }
1667 :
1668 :
1669 : /************************************************************************/
1670 : /* GByteArrayToBYTEA() */
1671 : /************************************************************************/
1672 :
1673 38 : char* OGRPGLayer::GByteArrayToBYTEA( const GByte* pabyData, int nLen)
1674 : {
1675 : char* pszTextBuf;
1676 :
1677 38 : pszTextBuf = (char *) CPLMalloc(nLen*5+1);
1678 :
1679 38 : int iSrc, iDst=0;
1680 :
1681 10253 : for( iSrc = 0; iSrc < nLen; iSrc++ )
1682 : {
1683 16781 : if( pabyData[iSrc] < 40 || pabyData[iSrc] > 126
1684 : || pabyData[iSrc] == '\\' )
1685 : {
1686 6566 : sprintf( pszTextBuf+iDst, "\\\\%03o", pabyData[iSrc] );
1687 6566 : iDst += 5;
1688 : }
1689 : else
1690 3649 : pszTextBuf[iDst++] = pabyData[iSrc];
1691 : }
1692 38 : pszTextBuf[iDst] = '\0';
1693 :
1694 38 : return pszTextBuf;
1695 : }
1696 :
1697 : /************************************************************************/
1698 : /* GeometryToBYTEA() */
1699 : /************************************************************************/
1700 :
1701 34 : char *OGRPGLayer::GeometryToBYTEA( OGRGeometry * poGeometry )
1702 :
1703 : {
1704 34 : int nWkbSize = poGeometry->WkbSize();
1705 : GByte *pabyWKB;
1706 : char *pszTextBuf;
1707 :
1708 34 : pabyWKB = (GByte *) CPLMalloc(nWkbSize);
1709 34 : if( poGeometry->exportToWkb( wkbNDR, pabyWKB ) != OGRERR_NONE )
1710 : {
1711 0 : CPLFree(pabyWKB);
1712 0 : return CPLStrdup("");
1713 : }
1714 :
1715 34 : pszTextBuf = GByteArrayToBYTEA( pabyWKB, nWkbSize );
1716 34 : CPLFree(pabyWKB);
1717 :
1718 34 : return pszTextBuf;
1719 : }
1720 :
1721 : /************************************************************************/
1722 : /* OIDToGeometry() */
1723 : /************************************************************************/
1724 :
1725 0 : OGRGeometry *OGRPGLayer::OIDToGeometry( Oid oid )
1726 :
1727 : {
1728 0 : PGconn *hPGConn = poDS->GetPGConn();
1729 : GByte *pabyWKB;
1730 : int fd, nBytes;
1731 : OGRGeometry *poGeometry;
1732 :
1733 : #define MAX_WKB 500000
1734 :
1735 0 : if( oid == 0 )
1736 0 : return NULL;
1737 :
1738 0 : fd = lo_open( hPGConn, oid, INV_READ );
1739 0 : if( fd < 0 )
1740 0 : return NULL;
1741 :
1742 0 : pabyWKB = (GByte *) CPLMalloc(MAX_WKB);
1743 0 : nBytes = lo_read( hPGConn, fd, (char *) pabyWKB, MAX_WKB );
1744 0 : lo_close( hPGConn, fd );
1745 :
1746 0 : poGeometry = NULL;
1747 0 : OGRGeometryFactory::createFromWkb( pabyWKB, NULL, &poGeometry, nBytes );
1748 :
1749 0 : CPLFree( pabyWKB );
1750 :
1751 0 : return poGeometry;
1752 : }
1753 :
1754 : /************************************************************************/
1755 : /* GeometryToOID() */
1756 : /************************************************************************/
1757 :
1758 0 : Oid OGRPGLayer::GeometryToOID( OGRGeometry * poGeometry )
1759 :
1760 : {
1761 0 : PGconn *hPGConn = poDS->GetPGConn();
1762 0 : int nWkbSize = poGeometry->WkbSize();
1763 : GByte *pabyWKB;
1764 : Oid oid;
1765 : int fd, nBytesWritten;
1766 :
1767 0 : pabyWKB = (GByte *) CPLMalloc(nWkbSize);
1768 0 : if( poGeometry->exportToWkb( wkbNDR, pabyWKB ) != OGRERR_NONE )
1769 0 : return 0;
1770 :
1771 0 : oid = lo_creat( hPGConn, INV_READ|INV_WRITE );
1772 :
1773 0 : fd = lo_open( hPGConn, oid, INV_WRITE );
1774 0 : nBytesWritten = lo_write( hPGConn, fd, (char *) pabyWKB, nWkbSize );
1775 0 : lo_close( hPGConn, fd );
1776 :
1777 0 : if( nBytesWritten != nWkbSize )
1778 : {
1779 : CPLDebug( "PG",
1780 : "Only wrote %d bytes of %d intended for (fd=%d,oid=%d).\n",
1781 0 : nBytesWritten, nWkbSize, fd, oid );
1782 : }
1783 :
1784 0 : CPLFree( pabyWKB );
1785 :
1786 0 : return oid;
1787 : }
1788 :
1789 : /************************************************************************/
1790 : /* StartTransaction() */
1791 : /************************************************************************/
1792 :
1793 15 : OGRErr OGRPGLayer::StartTransaction()
1794 :
1795 : {
1796 15 : return poDS->SoftStartTransaction();
1797 : }
1798 :
1799 : /************************************************************************/
1800 : /* CommitTransaction() */
1801 : /************************************************************************/
1802 :
1803 15 : OGRErr OGRPGLayer::CommitTransaction()
1804 :
1805 : {
1806 15 : return poDS->SoftCommit();
1807 : }
1808 :
1809 : /************************************************************************/
1810 : /* RollbackTransaction() */
1811 : /************************************************************************/
1812 :
1813 0 : OGRErr OGRPGLayer::RollbackTransaction()
1814 :
1815 : {
1816 0 : return poDS->SoftRollback();
1817 : }
1818 :
1819 : /************************************************************************/
1820 : /* GetSpatialRef() */
1821 : /************************************************************************/
1822 :
1823 218 : OGRSpatialReference *OGRPGLayer::GetSpatialRef()
1824 :
1825 : {
1826 218 : if( poSRS == NULL && nSRSId > -1 )
1827 : {
1828 0 : poSRS = poDS->FetchSRS( nSRSId );
1829 0 : if( poSRS != NULL )
1830 0 : poSRS->Reference();
1831 : else
1832 0 : nSRSId = -1;
1833 : }
1834 :
1835 218 : return poSRS;
1836 : }
1837 :
1838 : /************************************************************************/
1839 : /* GetFIDColumn() */
1840 : /************************************************************************/
1841 :
1842 4 : const char *OGRPGLayer::GetFIDColumn()
1843 :
1844 : {
1845 4 : if( pszFIDColumn != NULL )
1846 2 : return pszFIDColumn;
1847 : else
1848 2 : return "";
1849 : }
1850 :
1851 : /************************************************************************/
1852 : /* GetGeometryColumn() */
1853 : /************************************************************************/
1854 :
1855 6 : const char *OGRPGLayer::GetGeometryColumn()
1856 :
1857 : {
1858 6 : if( pszGeomColumn != NULL )
1859 6 : return pszGeomColumn;
1860 : else
1861 0 : return "";
1862 : }
1863 :
1864 : /************************************************************************/
1865 : /* GetExtent() */
1866 : /************************************************************************/
1867 :
1868 : OGRErr OGRPGLayer::RunGetExtentRequest( OGREnvelope *psExtent, int bForce,
1869 4 : CPLString osCommand)
1870 : {
1871 4 : if ( psExtent == NULL )
1872 0 : return OGRERR_FAILURE;
1873 :
1874 4 : if ( TestCapability(OLCFastGetExtent) || bHasPostGISGeography )
1875 : {
1876 2 : PGconn *hPGConn = poDS->GetPGConn();
1877 2 : PGresult *hResult = NULL;
1878 :
1879 2 : hResult = PQexec( hPGConn, osCommand );
1880 2 : if( ! hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK || PQgetisnull(hResult,0,0) )
1881 : {
1882 0 : OGRPGClearResult( hResult );
1883 0 : CPLDebug("PG","Unable to get extent by PostGIS. Using standard OGRLayer method.");
1884 0 : return OGRPGLayer::GetExtent( psExtent, bForce );
1885 : }
1886 :
1887 2 : char * pszBox = PQgetvalue(hResult,0,0);
1888 : char * ptr, *ptrEndParenthesis;
1889 : char szVals[64*6+6];
1890 :
1891 2 : ptr = strchr(pszBox, '(');
1892 2 : if (ptr)
1893 2 : ptr ++;
1894 2 : if (ptr == NULL ||
1895 : (ptrEndParenthesis = strchr(ptr, ')')) == NULL ||
1896 : ptrEndParenthesis - ptr > (int)(sizeof(szVals) - 1))
1897 : {
1898 : CPLError( CE_Failure, CPLE_IllegalArg,
1899 0 : "Bad extent representation: '%s'", pszBox);
1900 :
1901 0 : OGRPGClearResult( hResult );
1902 0 : return OGRERR_FAILURE;
1903 : }
1904 :
1905 2 : strncpy(szVals,ptr,ptrEndParenthesis - ptr);
1906 2 : szVals[ptrEndParenthesis - ptr] = '\0';
1907 :
1908 2 : char ** papszTokens = CSLTokenizeString2(szVals," ,",CSLT_HONOURSTRINGS);
1909 2 : int nTokenCnt = poDS->sPostGISVersion.nMajor >= 1 ? 4 : 6;
1910 :
1911 2 : if ( CSLCount(papszTokens) != nTokenCnt )
1912 : {
1913 : CPLError( CE_Failure, CPLE_IllegalArg,
1914 0 : "Bad extent representation: '%s'", pszBox);
1915 0 : CSLDestroy(papszTokens);
1916 :
1917 0 : OGRPGClearResult( hResult );
1918 0 : return OGRERR_FAILURE;
1919 : }
1920 :
1921 : // Take X,Y coords
1922 : // For PostGis ver >= 1.0.0 -> Tokens: X1 Y1 X2 Y2 (nTokenCnt = 4)
1923 : // For PostGIS ver < 1.0.0 -> Tokens: X1 Y1 Z1 X2 Y2 Z2 (nTokenCnt = 6)
1924 : // => X2 index calculated as nTokenCnt/2
1925 : // Y2 index caluclated as nTokenCnt/2+1
1926 :
1927 2 : psExtent->MinX = CPLAtof( papszTokens[0] );
1928 2 : psExtent->MinY = CPLAtof( papszTokens[1] );
1929 2 : psExtent->MaxX = CPLAtof( papszTokens[nTokenCnt/2] );
1930 2 : psExtent->MaxY = CPLAtof( papszTokens[nTokenCnt/2+1] );
1931 :
1932 2 : CSLDestroy(papszTokens);
1933 2 : OGRPGClearResult( hResult );
1934 :
1935 2 : return OGRERR_NONE;
1936 : }
1937 :
1938 2 : return OGRLayer::GetExtent( psExtent, bForce );
1939 : }
|