1 /*
2   Copyright (c) <2007-2012> <Barbara Philippot - Olivier Courtin>
3 
4   Permission is hereby granted, free of charge, to any person obtaining a copy
5   of this software and associated documentation files (the "Software"), to deal
6   in the Software without restriction, including without limitation the rights
7   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8   copies of the Software, and to permit persons to whom the Software is
9   furnished to do so, subject to the following conditions:
10 
11   The above copyright notice and this permission notice shall be included in
12   all copies or substantial portions of the Software.
13 
14   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20   IN THE SOFTWARE.
21 */
22 
23 
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <assert.h>
27 #include <string.h>
28 #include <time.h>
29 
30 #include "ows.h"
31 
32 
33 /*
34  * Return the name of the id column from table matching layer name
35  */
ows_psql_id_column(ows * o,buffer * layer_name)36 buffer *ows_psql_id_column(ows * o, buffer * layer_name)
37 {
38   ows_layer_node *ln = NULL;
39 
40   assert(o);
41   assert(o->layers);
42   assert(layer_name);
43 
44   for (ln = o->layers->first ; ln ; ln = ln->next)
45     if (ln->layer->name && ln->layer->storage
46         && !strcmp(ln->layer->name->buf, layer_name->buf))
47       return ln->layer->storage->pkey;
48 
49   return NULL;
50 }
51 
52 
53 /*
54  * Execute an SQL request
55  */
ows_psql_exec(ows * o,const char * sql)56 PGresult * ows_psql_exec(ows *o, const char *sql)
57 {
58   PGresult* res;
59 
60   assert(o);
61   assert(sql);
62   assert(o->pg);
63 
64   ows_log(o, 8, sql);
65   res = PQexecParams(o->pg, sql, 0, NULL, NULL, NULL, NULL, 0);
66   if (strlen(PQresultErrorMessage(res)))
67     ows_log(o, 1, PQresultErrorMessage(res));
68 
69   return res;
70 }
71 
72 
73 /*
74  * Return the column number of the id column from table matching layer name
75  * (needed in wfs_get_feature only)
76  */
ows_psql_column_number_id_column(ows * o,buffer * layer_name)77 int ows_psql_column_number_id_column(ows * o, buffer * layer_name)
78 {
79   ows_layer_node *ln = NULL;
80 
81   assert(o);
82   assert(o->layers);
83   assert(layer_name);
84 
85   for (ln = o->layers->first ; ln ; ln = ln->next)
86     if (ln->layer->name && ln->layer->storage
87         && !strcmp(ln->layer->name->buf, layer_name->buf))
88       return ln->layer->storage->pkey_column_number;
89 
90   return -1;
91 }
92 
93 
94 /*
95  * Return geometry columns from the table matching layer name
96  */
ows_psql_geometry_column(ows * o,buffer * layer_name)97 list *ows_psql_geometry_column(ows * o, buffer * layer_name)
98 {
99   ows_layer_node *ln = NULL;
100 
101   assert(o);
102   assert(o->layers);
103   assert(layer_name);
104 
105   for (ln = o->layers->first ; ln ; ln = ln->next)
106     if (ln->layer->name && ln->layer->storage
107         && !strcmp(ln->layer->name->buf, layer_name->buf))
108       return ln->layer->storage->geom_columns;
109 
110   return NULL;
111 }
112 
113 
114 /*
115  * Return schema name from a given layer
116  */
ows_psql_schema_name(ows * o,buffer * layer_name)117 buffer *ows_psql_schema_name(ows * o, buffer * layer_name)
118 {
119   ows_layer_node *ln = NULL;
120 
121   assert(o);
122   assert(o->layers);
123   assert(layer_name);
124 
125   for (ln = o->layers->first ; ln ; ln = ln->next)
126     if (ln->layer->name && ln->layer->storage
127         && !strcmp(ln->layer->name->buf, layer_name->buf))
128       return ln->layer->storage->schema;
129 
130   return NULL;
131 }
132 
133 
134 /*
135  * Return table name from a given layer
136  */
ows_psql_table_name(ows * o,buffer * layer_name)137 buffer *ows_psql_table_name(ows * o, buffer * layer_name)
138 {
139   ows_layer_node *ln = NULL;
140 
141   assert(o);
142   assert(o->layers);
143   assert(layer_name);
144 
145   for (ln = o->layers->first ; ln ; ln = ln->next)
146     if (ln->layer->name && ln->layer->storage
147         && !strcmp(ln->layer->name->buf, layer_name->buf))
148       return ln->layer->storage->table;
149 
150   return NULL;
151 }
152 
153 
154 /*
155  * Check if a given WKT geometry is or not valid
156  */
ows_psql_is_geometry_valid(ows * o,buffer * geom)157 bool ows_psql_is_geometry_valid(ows * o, buffer * geom)
158 {
159   buffer *sql;
160   PGresult *res;
161   bool ret = false;
162 
163   assert(o);
164   assert(geom);
165 
166   sql = buffer_init();
167   buffer_add_str(sql, "SELECT ST_isvalid(ST_geometryfromtext('");
168   buffer_copy(sql, geom);
169   buffer_add_str(sql, "', -1));");
170 
171   res = ows_psql_exec(o, sql->buf);
172   buffer_free(sql);
173 
174   if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1
175       && (char) PQgetvalue(res, 0, 0)[0] ==  't') ret = true;
176 
177   PQclear(res);
178   return ret;
179 }
180 
181 
182 /*
183  * Check if the specified column from a layer_name is (or not) a geometry column
184  */
ows_psql_is_geometry_column(ows * o,buffer * layer_name,buffer * column)185 bool ows_psql_is_geometry_column(ows * o, buffer * layer_name, buffer * column)
186 {
187   ows_layer_node *ln;
188 
189   assert(o);
190   assert(o->layers);
191   assert(layer_name);
192   assert(column);
193 
194   for (ln = o->layers->first ; ln ; ln = ln->next)
195     if (ln->layer->name && ln->layer->storage
196         && !strcmp(ln->layer->name->buf, layer_name->buf))
197       return in_list(ln->layer->storage->geom_columns, column);
198 
199   return false;
200 }
201 
202 
203 /*
204  * Return a list of not null properties from the table matching layer name
205  */
ows_psql_not_null_properties(ows * o,buffer * layer_name)206 list *ows_psql_not_null_properties(ows * o, buffer * layer_name)
207 {
208   ows_layer_node *ln;
209 
210   assert(o);
211   assert(o->layers);
212   assert(layer_name);
213 
214   for (ln = o->layers->first ; ln ; ln = ln->next)
215     if (ln->layer->name && ln->layer->storage
216         && !strcmp(ln->layer->name->buf, layer_name->buf))
217       return ln->layer->storage->not_null_columns;
218 
219   return NULL;
220 }
221 
222 
223 /*
224  * Returns the constraint name for a table column.
225  * Used to return enumeration constraints in describe feature type request.
226  */
ows_psql_column_constraint_name(ows * o,buffer * column_name,buffer * table_name)227 buffer *ows_psql_column_constraint_name(ows * o, buffer * column_name, buffer * table_name)
228 {
229   buffer *sql;
230   PGresult *res;
231   buffer *constraint_name;
232 
233   constraint_name = buffer_init();
234 
235   assert(o);
236   assert(column_name);
237   assert(table_name);
238 
239   sql = buffer_init();
240 
241   buffer_add_str(sql, "SELECT constraint_name FROM information_schema.constraint_column_usage WHERE table_name = '");
242   buffer_add_str(sql, table_name->buf);
243   buffer_add_str(sql, "' AND column_name='");
244   buffer_add_str(sql, column_name->buf);
245   buffer_add_str(sql, "'");
246 
247   res = ows_psql_exec(o, sql->buf);
248   buffer_free(sql);
249 
250   if (PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) != 1) {
251     PQclear(res);
252     return constraint_name;
253   }
254 
255   buffer_add_str(constraint_name, PQgetvalue(res, 0, 0));
256   PQclear(res);
257 
258   return constraint_name;
259 }
260 
261 /*
262  * Returns the list of possible values for a column according to the constraint value.
263  * Used to return enumeration constraints in describe feature type request.
264  */
ows_psql_column_check_constraint(ows * o,buffer * constraint_name)265 list *ows_psql_column_check_constraint(ows * o, buffer * constraint_name)
266 {
267   buffer *sql;
268   PGresult *res;
269   list *constraints;
270   buffer *constraint_value;
271   buffer *buf;
272   size_t i;
273   size_t j;
274 
275   constraints = list_init();
276   constraint_value = buffer_init();
277 
278   assert(o);
279   assert(constraint_name);
280 
281   sql = buffer_init();
282 
283   buffer_add_str(sql, "SELECT check_clause FROM information_schema.check_constraints WHERE constraint_name = '");
284   buffer_add_str(sql, constraint_name->buf);
285   buffer_add_str(sql, "'");
286 
287   res = ows_psql_exec(o, sql->buf);
288 
289   if (PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) != 1) {
290     PQclear(res);
291     return constraints;
292   }
293 
294   buffer_add_str(constraint_value, PQgetvalue(res, 0, 0));
295   PQclear(res);
296 
297   j=0;
298   buf = buffer_init();
299   for (i = 0; constraint_value->buf[i] != '\0'; i++) {
300     if(constraint_value->buf[i] == '\'') {
301       j++;
302       if(j%2==1) {
303         buf = buffer_init();
304       } else {
305         list_add(constraints, buf);
306       }
307     } else if(j%2==1)
308       buffer_add(buf, constraint_value->buf[i]);
309   }
310 
311   return constraints;
312 }
313 
314 
315 /*
316  * Return the column's name matching the specified number from table
317  * (Only use in specific FE position function, so not directly inside
318  *  storage handle mechanism)
319  */
ows_psql_column_name(ows * o,buffer * layer_name,int number)320 buffer *ows_psql_column_name(ows * o, buffer * layer_name, int number)
321 {
322   buffer *sql;
323   PGresult *res;
324   buffer *column;
325 
326   assert(o);
327   assert(layer_name);
328 
329   sql = buffer_init();
330   column = buffer_init();
331 
332   buffer_add_str(sql, "SELECT a.attname FROM pg_class c, pg_attribute a, pg_type t WHERE c.relname ='");
333   buffer_copy(sql, layer_name);
334   buffer_add_str(sql, "' AND a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid AND a.attnum = ");
335   buffer_add_int(sql, number);
336 
337   res = ows_psql_exec(o, sql->buf);
338   buffer_free(sql);
339 
340   if (PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) != 1) {
341     PQclear(res);
342     return column;
343   }
344 
345   buffer_add_str(column, PQgetvalue(res, 0, 0));
346   PQclear(res);
347 
348   return column;
349 }
350 
351 /*
352  * Returns the column character_maximum_length value from the database
353  * information schema.
354  * Used to return maxLength constraint in describe feature type request.
355  */
ows_psql_column_character_maximum_length(ows * o,buffer * column_name,buffer * table_name)356 buffer *ows_psql_column_character_maximum_length(ows * o, buffer * column_name, buffer * table_name)
357 {
358   buffer *sql;
359   PGresult *res;
360   buffer *character_maximum_length;
361 
362   character_maximum_length = buffer_init();
363 
364   assert(o);
365   assert(column_name);
366   assert(table_name);
367 
368   sql = buffer_init();
369 
370   buffer_add_str(sql, "SELECT character_maximum_length FROM information_schema.columns WHERE table_name = '");
371   buffer_add_str(sql, table_name->buf);
372   buffer_add_str(sql, "' and column_name = '");
373   buffer_add_str(sql, column_name->buf);
374   buffer_add_str(sql, "'");
375 
376   res = ows_psql_exec(o, sql->buf);
377   buffer_free(sql);
378 
379   if (PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) != 1) {
380     PQclear(res);
381     return character_maximum_length;
382   }
383 
384   buffer_add_str(character_maximum_length, PQgetvalue(res, 0, 0));
385   PQclear(res);
386 
387   return character_maximum_length;
388 }
389 
390 
391 /*
392  * Retrieve description of a table matching a given layer name
393  */
ows_psql_describe_table(ows * o,buffer * layer_name)394 array *ows_psql_describe_table(ows * o, buffer * layer_name)
395 {
396   ows_layer_node *ln = NULL;
397 
398   assert(o);
399   assert(o->layers);
400   assert(layer_name);
401 
402   for (ln = o->layers->first ; ln ; ln = ln->next)
403     if (ln->layer->name && ln->layer->storage
404         && !strcmp(ln->layer->name->buf, layer_name->buf))
405       return ln->layer->storage->attributes;
406 
407   return NULL;
408 }
409 
410 
411 /*
412  * Retrieve an ows_version, related to current PostGIS version.
413  */
ows_psql_postgis_version(ows * o)414 ows_version * ows_psql_postgis_version(ows *o)
415 {
416   list *l;
417   PGresult * res;
418   ows_version * v = NULL;
419 
420   res = ows_psql_exec(o, "SELECT substr(postgis_full_version(), 10, 5)");
421   if (PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) != 1) {
422     PQclear(res);
423     return NULL;
424   }
425 
426   l = list_explode_str('.', (char *) PQgetvalue(res, 0, 0));
427 
428   if (    l->size == 3
429           && check_regexp(l->first->value->buf,       "^[0-9]+$")
430           && check_regexp(l->first->next->value->buf, "^[0-9]+$")
431           && check_regexp(l->last->value->buf,        "^[0-9]+$") ) {
432     v = ows_version_init();
433     v->major   = atoi(l->first->value->buf);
434     v->minor   = atoi(l->first->next->value->buf);
435     v->release = atoi(l->last->value->buf);
436   }
437 
438   list_free(l);
439   PQclear(res);
440   return v;
441 }
442 
443 
444 /*
445  * TODO
446  */
ows_psql_is_numeric(buffer * type)447 bool ows_psql_is_numeric(buffer * type)
448 {
449   assert(type);
450 
451   if (buffer_cmp(type, "int2")) return true;
452   if (buffer_cmp(type, "int4")) return true;
453   if (buffer_cmp(type, "int8")) return true;
454   if (buffer_cmp(type, "float4")) return true;
455   if (buffer_cmp(type, "float8")) return true;
456   if (buffer_ncmp(type, "numeric", 7)) return true;
457 
458   return false;
459 }
460 
461 
462 /*
463  * Convert a PostgreSql type to a valid
464  * OGC XMLSchema's type
465  */
ows_psql_to_xsd(buffer * type,enum wfs_format format)466 char *ows_psql_to_xsd(buffer * type, enum wfs_format format)
467 {
468   int gml_version = 311;
469 
470   assert(type);
471   assert(format);
472 
473   if (format == WFS_GML212) gml_version = 212;
474 
475   if (buffer_case_cmp(type, "geometry")) return "gml:GeometryPropertyType";
476   if (buffer_cmp(type, "geography")) return "gml:GeometryPropertyType";
477   if (buffer_cmp(type, "int2")) return "short";
478   if (buffer_cmp(type, "int4")) return "int";
479   if (buffer_cmp(type, "int8")) return "long";
480   if (buffer_cmp(type, "float4")) return "float";
481   if (buffer_cmp(type, "float8")) return "double";
482   if (buffer_cmp(type, "bool")) return "boolean";
483   if (buffer_cmp(type, "bytea")) return "byte";
484   if (buffer_cmp(type, "date")) return "date";
485   if (buffer_cmp(type, "time")) return "time";
486   if (buffer_ncmp(type, "numeric", 7)) return "decimal";
487   if (buffer_ncmp(type, "timestamp", 9)) return "dateTime"; /* Could be also timestamptz */
488   if (buffer_cmp(type, "POINT")) return "gml:PointPropertyType";
489   if (buffer_cmp(type, "LINESTRING") && gml_version == 212) return "gml:LineStringPropertyType";
490   if (buffer_cmp(type, "LINESTRING") && gml_version >= 311) return "gml:CurvePropertyType";
491   if (buffer_cmp(type, "POLYGON") && gml_version == 212) return "gml:PolygonPropertyType";
492   if (buffer_cmp(type, "POLYGON") && gml_version >= 311) return "gml:SurfacePropertyType";
493   if (buffer_cmp(type, "TRIANGLE")) return "gml:TrianglePropertyType";
494   if (buffer_cmp(type, "MULTIPOINT")) return "gml:MultiPointPropertyType";
495   if (buffer_cmp(type, "MULTILINESTRING") && gml_version == 212) return "gml:MultiLineStringPropertyType";
496   if (buffer_cmp(type, "MULTILINESTRING") && gml_version >= 311) return "gml:MultiCurvePropertyType";
497   if (buffer_cmp(type, "MULTIPOLYGON") && gml_version == 212) return "gml:MultiPolygonPropertyType";
498   if (buffer_cmp(type, "MULTIPOLYGON") && gml_version >= 311) return "gml:MultiSurfacePropertyType";
499   if (buffer_cmp(type, "TIN")) return "gml:TriangulatedSurfacePropertyType";
500   if (buffer_cmp(type, "POLYHEDRALSURFACE")) return "gml:PolyhedralSurfacePropertyType";
501   if (buffer_cmp(type, "GEOMETRYCOLLECTION")) return "gml:MultiGeometryPropertyType";
502 
503   return "string";
504 }
505 
506 
507 /*
508  * Convert a date from PostgreSQL to XML
509  */
ows_psql_timestamp_to_xml_time(char * timestamp)510 buffer *ows_psql_timestamp_to_xml_time(char *timestamp)
511 {
512   buffer *time;
513 
514   assert(timestamp);
515 
516   time = buffer_init();
517   buffer_add_str(time, timestamp);
518   if (!time->use) return time;
519 
520   buffer_replace(time, " ", "T");
521 
522   if (check_regexp(time->buf, "\\+"))
523     buffer_add_str(time, ":00");
524   else
525     buffer_add_str(time, "Z");
526 
527   return time;
528 }
529 
530 
531 /*
532  * Return the type of the property passed in parameter
533  */
ows_psql_type(ows * o,buffer * layer_name,buffer * property)534 buffer *ows_psql_type(ows * o, buffer * layer_name, buffer * property)
535 {
536   ows_layer_node *ln;
537 
538   assert(o);
539   assert(o->layers);
540   assert(layer_name);
541   assert(property);
542 
543   for (ln = o->layers->first ; ln ; ln = ln->next) {
544     if (ln->layer->name && ln->layer->storage
545         && !strcmp(ln->layer->name->buf, layer_name->buf))
546       return array_get(ln->layer->storage->attributes, property->buf);
547   }
548 
549   return NULL;
550 }
551 
552 
553 /*
554  * Generate a new buffer id supposed to be unique for a given layer name
555  */
ows_psql_generate_id(ows * o,buffer * layer_name)556 buffer *ows_psql_generate_id(ows * o, buffer * layer_name)
557 {
558   ows_layer_node *ln;
559   buffer * id, *sql_id;
560   FILE *fp;
561   PGresult * res;
562   int i, seed_len;
563   char * seed = NULL;
564 
565   assert(o);
566   assert(o->layers);
567   assert(layer_name);
568 
569   /* Retrieve layer node pointer */
570   for (ln = o->layers->first ; ln ; ln = ln->next) {
571     if (ln->layer->name && ln->layer->storage
572         && !strcmp(ln->layer->name->buf, layer_name->buf)) break;
573   }
574   assert(ln);
575 
576   id = buffer_init();
577 
578   /* If PK have a sequence in PostgreSQL database,
579    * retrieve next available sequence value
580    */
581   if (ln->layer->storage->pkey_sequence) {
582     sql_id = buffer_init();
583     buffer_add_str(sql_id, "SELECT nextval('");
584     buffer_copy(sql_id, ln->layer->storage->pkey_sequence);
585     buffer_add_str(sql_id, "');");
586     res = ows_psql_exec(o, sql_id->buf);
587     buffer_free(sql_id);
588 
589     if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1) {
590       buffer_add_str(id, (char *) PQgetvalue(res, 0, 0));
591       PQclear(res);
592       return id;
593     }
594 
595     /* FIXME: Shouldn't we return an error there instead ? */
596     PQclear(res);
597   }
598 
599   /* If PK have a DEFAULT in PostgreSQL database,
600    * retrieve next available DEFAULT value
601    */
602   if (ln->layer->storage->pkey_default) {
603     sql_id = buffer_init();
604     buffer_add_str(sql_id, "SELECT ");
605     buffer_copy(sql_id, ln->layer->storage->pkey_default);
606     buffer_add_str(sql_id, ";");
607     res = ows_psql_exec(o, sql_id->buf);
608     buffer_free(sql_id);
609 
610     if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1) {
611       buffer_add_str(id, (char *) PQgetvalue(res, 0, 0));
612       PQclear(res);
613       return id;
614     }
615 
616     /* FIXME: Shouldn't we return an error there instead ? */
617     PQclear(res);
618   }
619 
620   /*
621    * If we don't have a PostgreSQL Sequence, we will try to
622    * generate a pseudo random keystring using /dev/urandom
623    * Will so work only on somes/commons Unix system
624    */
625   seed_len = 6;
626   seed = malloc(sizeof(char) * (seed_len * 3 + 1));  /* multiply by 3 to be able to deal
627                                                            with hex2dec conversion */
628   assert(seed);
629   seed[0] = '\0';
630 
631   fp = fopen("/dev/urandom","r");
632   if (fp) {
633     for (i=0 ; i<seed_len ; i++)
634       sprintf(seed,"%s%03d", seed, fgetc(fp));
635     fclose(fp);
636     buffer_add_str(id, seed);
637     free(seed);
638 
639     return id;
640   }
641   free(seed);
642 
643   /* Case where we not using PostgreSQL sequence,
644    * and OS don't have a /dev/urandom support
645    * This case don't prevent to produce ID collision
646    * Don't use it unless really no others choices !!!
647    */
648   srand((int) (time(NULL) ^ rand() % 1000) + 42);
649   srand((rand() % 1000 ^ rand() % 1000) + 42);
650   buffer_add_int(id, rand());
651 
652   return id;
653 }
654 
655 
656 /*
657  * Return the number of rows returned by the specified requests
658  */
ows_psql_number_features(ows * o,list * from,list * where)659 int ows_psql_number_features(ows * o, list * from, list * where)
660 {
661   buffer *sql;
662   PGresult *res;
663   list_node *ln_from, *ln_where;
664   int nb;
665 
666   assert(o);
667   assert(from);
668   assert(where);
669 
670   nb = 0;
671 
672   /* checks if from list and where list have the same size */
673   if (from->size != where->size) return nb;
674 
675 
676   for (ln_from = from->first, ln_where = where->first;
677        ln_from;
678        ln_from = ln_from->next, ln_where = ln_where->next) {
679     sql = buffer_init();
680 
681     /* execute the request */
682     buffer_add_str(sql, "SELECT count(*) FROM \"");
683     buffer_copy(sql, ln_from->value);
684     buffer_add_str(sql, "\" ");
685     buffer_copy(sql, ln_where->value);
686     res = ows_psql_exec(o, sql->buf);
687     buffer_free(sql);
688 
689     if (PQresultStatus(res) != PGRES_TUPLES_OK) {
690       PQclear(res);
691       return -1;
692     }
693     nb = nb + atoi(PQgetvalue(res, 0, 0));
694     PQclear(res);
695   }
696 
697   return nb;
698 }
699 
700 
ows_psql_recursive_parse_gml(ows * o,xmlNodePtr n,xmlNodePtr result)701 static xmlNodePtr ows_psql_recursive_parse_gml(ows * o, xmlNodePtr n, xmlNodePtr result)
702 {
703   xmlNodePtr c;
704 
705   assert(o);
706   assert(n);
707 
708   if (result) return result;  /* avoid recursive loop */
709 
710   /* We are looking for the geometry part inside GML doc */
711   for (; n ; n = n->next) {
712 
713     if (n->type != XML_ELEMENT_NODE) continue;
714 
715     /* Check on namespace GML 3 and GML 3.2 */
716     if (    strcmp("http://www.opengis.net/gml",     (char *) n->ns->href)
717          && strcmp("http://www.opengis.net/gml/3.2", (char *) n->ns->href)) continue;
718 
719     /* GML SF Geometries Types */
720     if (   !strcmp((char *) n->name, "Point")
721         || !strcmp((char *) n->name, "LineString")
722         || !strcmp((char *) n->name, "LinearRing")
723         || !strcmp((char *) n->name, "Curve")
724         || !strcmp((char *) n->name, "Polygon")
725         || !strcmp((char *) n->name, "Triangle")
726         || !strcmp((char *) n->name, "Surface")
727         || !strcmp((char *) n->name, "MultiPoint")
728         || !strcmp((char *) n->name, "MultiLineString")
729         || !strcmp((char *) n->name, "MultiCurve")
730         || !strcmp((char *) n->name, "MultiPolygon")
731         || !strcmp((char *) n->name, "MultiSurface")
732         || !strcmp((char *) n->name, "PolyhedralSurface")
733         || !strcmp((char *) n->name, "Tin")
734         || !strcmp((char *) n->name, "TriangulatedSurface")
735         || !strcmp((char *) n->name, "MultiGeometry")) return n;
736 
737     /* Recursive exploration */
738     if (n->children)
739       for (c = n->children ; c ; c = c->next)
740         if ((result = ows_psql_recursive_parse_gml(o, c, NULL)))
741           return result;
742   }
743 
744   return NULL;
745 }
746 
747 
748 /*
749  * Transform a GML geometry to PostGIS EWKT
750  * Return NULL on error
751  */
ows_psql_gml_to_sql(ows * o,xmlNodePtr n,int srid)752 buffer * ows_psql_gml_to_sql(ows * o, xmlNodePtr n, int srid)
753 {
754   PGresult *res;
755   xmlNodePtr g;
756   buffer *result, *sql, *gml;
757 
758   assert(o);
759   assert(n);
760 
761   g = ows_psql_recursive_parse_gml(o, n, NULL);
762   if (!g) return NULL;    /* No Geometry founded in GML doc */
763 
764   /* Retrieve the sub doc and launch GML parse via PostGIS */
765   gml = buffer_init();
766   cgi_add_xml_into_buffer(gml, g);
767 
768   sql = buffer_init();
769   buffer_add_str(sql, "SELECT ST_GeomFromGML('");
770   buffer_add_str(sql, gml->buf);
771 
772   if (ows_version_get(o->postgis_version) >= 200) {
773     buffer_add_str(sql, "',");
774     buffer_add_int(sql, srid);
775     buffer_add_str(sql, ")");
776   } else {
777     /* Means PostGIS 1.5 */
778     buffer_add_str(sql, "')");
779   }
780 
781   res = ows_psql_exec(o, sql->buf);
782   buffer_free(gml);
783 
784   /* GML Parse errors cases */
785   if (PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) != 1) {
786     buffer_free(sql);
787     PQclear(res);
788     return NULL;
789   }
790 
791   result = buffer_init();
792   buffer_add_str(result, PQgetvalue(res, 0, 0));
793   PQclear(res);
794 
795   /* Check if geometry is valid */
796   if (o->check_valid_geom) {
797 
798     buffer_empty(sql);
799     buffer_add_str(sql, "SELECT ST_IsValid('");
800     buffer_add_str(sql, result->buf);
801     buffer_add_str(sql, "')");
802 
803     res = ows_psql_exec(o, sql->buf);
804 
805     if (    PQresultStatus(res) != PGRES_TUPLES_OK
806          || PQntuples(res) != 1
807          || (char) PQgetvalue(res, 0, 0)[0] !=  't') {
808       buffer_free(sql);
809       buffer_free(result);
810       PQclear(res);
811       return NULL;
812     }
813     PQclear(res);
814   }
815 
816   buffer_free(sql);
817 
818   return result;
819 }
820 
821 
822 /*
823  * Use PostgreSQL native handling to escape string
824  * string returned must be freed by the caller
825  * return NULL on error
826  */
ows_psql_escape_string(ows * o,const char * content)827 char *ows_psql_escape_string(ows *o, const char *content)
828 {
829   char *content_escaped;
830   int error = 0;
831 
832   assert(o);
833   assert(o->pg);
834   assert(content);
835 
836   content_escaped = malloc(strlen(content) * 2 + 1);
837   PQescapeStringConn(o->pg, content_escaped, content, strlen(content), &error);
838 
839   if (error != 0) return NULL;
840 
841   return content_escaped;
842 }
843 
844 
845 /*
846  * Return SRID from a given geometry
847  */
ows_psql_geometry_srid(ows * o,const char * geom)848 int ows_psql_geometry_srid(ows *o, const char *geom)
849 {
850   int srid;
851   buffer *sql;
852   PGresult *res;
853 
854   assert(o);
855   assert(o->pg);
856   assert(geom);
857 
858   sql = buffer_from_str("SELECT ST_SRID('");
859   buffer_add_str(sql, geom);
860   buffer_add_str(sql, "'::geometry)");
861 
862   res = ows_psql_exec(o, sql->buf);
863 
864   if (PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) != 1) srid = -1;
865   else srid = atoi((char *) PQgetvalue(res, 0, 0));
866 
867   buffer_free(sql);
868   PQclear(res);
869 
870   return srid;
871 }
872 
873 
874 /*
875  * vim: expandtab sw=4 ts=4
876  */
877