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